-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
1350 lines (1259 loc) · 60.6 KB
/
script.js
File metadata and controls
1350 lines (1259 loc) · 60.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
const tasks = {
prompting: {
room: "Prompt Terminal",
title: "Broken Prompt At Terminal",
body:
"The terminal found a vague Codex request. Choose the prompt that gives Codex enough context to make a scoped, testable change.",
primer:
"Prompting is how you tell Codex what engineering job to do. A weak prompt like 'Fix the app' forces Codex to guess the goal, the relevant files, how much it should change, and how to prove the fix worked. A strong prompt gives Codex a clear task boundary: what problem to solve, where to inspect, what not to break, how to verify the result, and what summary to return.",
code: "Fix the app.",
mission: "Choose the strongest Codex prompt.",
missionBody:
"Prompting is the instruction layer for Codex. Before you ask Codex to act, give it the goal, relevant context, boundaries, verification step, and the kind of final summary you expect.",
takeawayTitle: "Prompting Mechanic Unlocked",
takeaway:
"Prompting is how you turn a vague intention into a task Codex can actually execute. A strong prompt gives Codex the goal, the relevant context, the files or area to inspect, the constraints to preserve, and the verification you expect. This matters because Codex is not just answering a question; it may explore a codebase, edit files, run commands, and summarize tradeoffs. The clearer the prompt, the less Codex has to guess. A production-quality Codex prompt usually answers: what should change, where should Codex look, what should stay untouched, how should the work be tested, and what should the final handoff include.",
formula: ["Goal", "Context", "Scope", "Constraints", "Verify", "Summarize"],
choices: [
{
label: "A. Too broad",
text: "Fix everything and make the app better. Change whatever you need.",
correct: false,
feedback: "Still too open-ended. Codex needs a bounded task, context, and verification.",
},
{
label: "B. Scoped and testable",
text:
"Find why the login button does not submit on mobile. Inspect the auth form and related tests. " +
"Make the smallest safe fix, preserve existing UI behavior, run the relevant test command, and summarize the changed files.",
correct: true,
feedback:
"Correct. This answer is strongest because it gives Codex a specific user-visible bug, tells it where to look, limits the change to the smallest safe fix, protects existing behavior, requires verification, and asks for a useful handoff. That is exactly how you turn Codex from a vague code generator into an engineering teammate.",
},
{
label: "C. Risky rewrite",
text: "Rewrite the whole frontend using a new framework and make the login page modern.",
correct: false,
feedback: "This expands the task. Codex is stronger when the prompt limits unnecessary rewrites.",
},
],
},
customization: {
room: "Customization Bay",
title: "Load The Ship Rules",
body:
"The repo task is about to start, but Codex does not know the team's conventions. Pick the best setup.",
primer:
"Customization is how you teach Codex the rules of a specific workspace. Without it, Codex may produce code that works but does not match the repo's style, architecture, commands, or team habits. Good customization points Codex toward project instructions, existing patterns, preferred tools, and constraints so its work fits naturally into the codebase.",
code: "Task: Add a settings panel that matches this codebase.",
mission: "Choose the best customization setup.",
missionBody:
"Customization is how Codex learns the way this specific team or repo works. Good customization points Codex toward project rules, existing patterns, preferred commands, skills, and tools so it does not behave like a generic assistant.",
takeawayTitle: "Customization Mechanic Unlocked",
takeaway:
"Customization is how you teach Codex the local rules of a project or team. Instead of treating every repo like a blank slate, Codex can use project instructions, AGENTS.md files, skills, connected tools, and team conventions to work in the style of the codebase. This is important because good engineering is rarely just about producing code that runs. It is about matching architecture patterns, using the right commands, respecting boundaries, and avoiding unnecessary dependencies. Customization makes Codex feel less like a generic assistant and more like a teammate who understands how this particular workspace works.",
formula: ["AGENTS.md", "Skills", "MCP", "Project Rules", "Team Style"],
choices: [
{
label: "A. No local context",
text: "Start coding immediately and infer all conventions from memory.",
correct: false,
feedback: "That is fragile. Codex should use the repo's own instructions and local context.",
},
{
label: "B. Load conventions first",
text:
"Read the project instructions, follow existing component patterns, use the repo's test command, and avoid introducing new libraries unless necessary.",
correct: true,
feedback:
"Correct. This answer is best because customization is about aligning Codex with the repo instead of forcing Codex to guess. It points Codex toward project instructions, existing patterns, test commands, and dependency discipline, which makes the output more consistent with how the team already works.",
},
{
label: "C. Force a personal style",
text: "Ignore existing patterns and convert the feature to my preferred framework.",
correct: false,
feedback: "Customization should align with the project, not overwrite it without a reason.",
},
],
},
memories: {
room: "Memory Archive",
title: "Store A Useful Memory",
body:
"The crew keeps repeating the same preference. Decide what belongs in memory.",
primer:
"Memories help Codex retain durable preferences across future work. They are useful for stable, repeated context, like a preferred test command or communication style. They should not store one-off details, temporary bugs, passwords, API keys, or anything sensitive. The best memory is specific, safe, and useful more than once.",
code: "Preference: For this project, always run npm test before summarizing a completed change.",
mission: "Choose the best use of memory.",
missionBody:
"Memories are for durable context that should help future tasks. A good memory is stable, specific, safe to retain, and useful more than once; temporary details and secrets should stay out.",
takeawayTitle: "Memory Mechanic Unlocked",
takeaway:
"Memories are durable pieces of context that can shape future Codex sessions. They are best for stable preferences or repeated project habits, like preferred test commands, communication style, or recurring implementation constraints. They are not meant for one-off facts, temporary bugs, or secrets. The developer experience goal is to reduce repeated setup: if the same preference keeps coming up, memory can help Codex remember it next time. Good memories are specific, useful across tasks, and safe to retain. Bad memories are vague, temporary, sensitive, or likely to become stale.",
formula: ["Durable", "Specific", "Reusable", "Project-Relevant"],
choices: [
{
label: "A. Save the stable preference",
text: "Remember that this project expects npm test before final summaries.",
correct: true,
feedback:
"Correct. This belongs in memory because it is stable, actionable, and likely to matter across future tasks. It helps Codex avoid asking or being reminded every time, while staying safe because it does not store secrets or temporary details.",
},
{
label: "B. Save a one-off bug",
text: "Remember that today's login bug happened at 2:31 PM.",
correct: false,
feedback: "Too temporary. That belongs in the current task context, not durable memory.",
},
{
label: "C. Save sensitive data",
text: "Remember the production API key so tests are easier next time.",
correct: false,
feedback: "Never store secrets as memory. Keep sensitive data out of durable assistant memory.",
},
],
},
sandboxing: {
room: "Sandbox Airlock",
title: "Pick The Safe Access Level",
body:
"Codex needs to inspect files, edit one component, and run local tests. Choose the safest permission plan.",
primer:
"Sandboxing controls what Codex is allowed to read, write, and run. The goal is not to block Codex; it is to give Codex enough access to complete the task while keeping risky actions controlled. A good sandbox follows least privilege: local edits and tests when needed, approvals for installs, network access, or destructive commands.",
code: "Task: Fix a UI bug in the current repo and run the relevant tests.",
mission: "Choose the least-privilege sandbox approach.",
missionBody:
"Sandboxing controls what Codex can read, edit, and run. The best choice gives Codex enough access to complete the task while preserving approval gates for risky actions like network access, installs, or destructive commands.",
takeawayTitle: "Sandboxing Mechanic Unlocked",
takeaway:
"Sandboxing controls what Codex can access and change while it works. This is one of the core trust mechanisms in an agentic coding workflow. Codex may need to read files, edit code, run tests, or use tools, but not every task needs network access, package installation, or broad filesystem permissions. A good sandbox gives Codex enough room to complete the job while keeping risky actions behind approval gates. The principle is least privilege: allow what the task needs, restrict what it does not, and require confirmation for actions that could be destructive, expensive, or security-sensitive.",
formula: ["Least Privilege", "Workspace Scope", "Approvals", "Safe Commands"],
choices: [
{
label: "A. Workspace access",
text: "Allow reading and editing the workspace, run local tests, and ask approval for network or destructive operations.",
correct: true,
feedback:
"Correct. This is the safest useful access level because the task needs local edits and tests, but not broad system permissions. It follows least privilege: give Codex enough room to work, while keeping network, install, and destructive actions behind approval.",
},
{
label: "B. Full unrestricted access",
text: "Allow all filesystem, network, and destructive commands by default.",
correct: false,
feedback: "Too broad. Sandboxing should avoid unnecessary permissions.",
},
{
label: "C. Read-only forever",
text: "Allow only reading files, even though the task requires a code fix.",
correct: false,
feedback: "Too restrictive for this mission. The sandbox must still allow the requested work.",
},
],
},
subagents: {
room: "Subagent Dispatch",
title: "Assign The Crew",
body:
"A complex issue has parallel work: inspect billing, build UI, and check regressions. Decide how to delegate.",
primer:
"Subagents are helper agents used to split work when tasks can run in parallel. They work best when each one has a clear job, a clear output, and a separate ownership area. Bad delegation creates overlap and confusion. Good delegation lets the main agent coordinate while subagents handle bounded pieces of the work.",
code: "Issue: Add billing settings without breaking invoice exports.",
mission: "Choose the best subagent plan.",
missionBody:
"Subagents help when work can be split into independent, bounded pieces. They are most useful when each agent has a clear role, clear output, and minimal overlap with the others.",
takeawayTitle: "Subagent Mechanic Unlocked",
takeaway:
"Subagents let Codex split work across specialized helpers, usually when parts of a task can happen in parallel. For example, one agent can inspect a code path, another can implement a contained UI change, and the main thread can integrate the result and make decisions. Subagents are powerful, but only when their scope is clear. They should have bounded tasks, distinct ownership, and a concrete output to return. The main agent still needs to coordinate, review, and integrate. The developer experience lesson is that delegation should reduce complexity, not create overlap or confusion.",
formula: ["Bounded Scope", "Parallel Work", "Clear Ownership", "Integrate"],
choices: [
{
label: "A. Delegate everything vaguely",
text: "Ask three agents to all fix billing however they want.",
correct: false,
feedback: "That creates overlap. Subagents need distinct scopes.",
},
{
label: "B. Split by ownership",
text:
"Explorer maps billing flows, worker updates settings UI, main agent integrates results and runs targeted tests.",
correct: true,
feedback:
"Correct. This delegation works because each subagent has a bounded role with minimal overlap. The explorer gathers context, the worker owns a contained implementation area, and the main agent keeps responsibility for integration and verification. That is how subagents create speed without creating chaos.",
},
{
label: "C. Delegate the blocking decision",
text: "Wait for a subagent to decide the product behavior before any local work starts.",
correct: false,
feedback: "Blocking decisions are often better handled in the main thread with the user.",
},
],
},
workflows: {
room: "Workflow Bridge",
title: "Sequence The Engineering Run",
body:
"The bridge has a vague issue. Pick the workflow that gets from issue to verified change.",
primer:
"A workflow is the repeatable path Codex follows from request to reliable result. Instead of jumping straight into edits, a strong workflow gathers context, plans, changes the right files, verifies with tests or checks, reviews the diff, and summarizes what happened. This makes Codex's process easier to trust.",
code: "Issue: Users cannot save profile changes on mobile.",
mission: "Choose the strongest Codex workflow.",
missionBody:
"A workflow is a repeatable path from request to verified result. Strong Codex workflows include context gathering, planning, implementation, testing, diff review, and a clear definition of done.",
takeawayTitle: "Workflow Mechanic Unlocked",
takeaway:
"Workflows are repeatable patterns for getting real engineering work done with Codex. Instead of treating Codex as a one-shot code generator, a workflow gives structure: understand the request, inspect the codebase, make a plan, edit the right files, run verification, review the diff, and summarize the result. This matters because production work has a definition of done. The best Codex workflows make the invisible parts of engineering visible: assumptions, tradeoffs, test coverage, and remaining risk. A good workflow helps developers trust not just the output, but the process that produced it.",
formula: ["Understand", "Plan", "Edit", "Test", "Review", "Summarize"],
choices: [
{
label: "A. Jump to code",
text: "Open a random file, make a guess, and stop once the page looks better.",
correct: false,
feedback: "Too ad hoc. Workflows need context and verification.",
},
{
label: "B. Structured run",
text:
"Read the issue, inspect related files, propose a scoped plan, make the minimal fix, run tests, review the diff, and summarize.",
correct: true,
feedback:
"Correct. This is the strongest workflow because it mirrors real engineering discipline: understand the issue, inspect before editing, plan the change, implement narrowly, verify with tests, review the diff, and summarize the result. It makes Codex's work easier to trust.",
},
{
label: "C. Only write a plan",
text: "Create a plan but do not inspect code, edit files, or verify anything.",
correct: false,
feedback: "Planning helps, but the workflow needs execution and verification.",
},
],
},
cyber: {
room: "Cyber Safety Lab",
title: "Handle The Risky Request",
body:
"A user asks for security help. Decide which request is safe to support.",
primer:
"Cyber safety is about supporting legitimate defensive security work without enabling harm. Codex can help audit owned code, fix vulnerabilities, and improve security practices. It should not help steal credentials, expose secrets, attack third-party systems, or produce exploit guidance for unauthorized targets.",
code: "Request queue: audit my own repo, exfiltrate keys, exploit a public target.",
mission: "Choose the safe cyber response.",
missionBody:
"Cyber safety means helping with legitimate defensive security work while avoiding harmful enablement. Safe support focuses on owned systems, authorized audits, secrets protection, and refusing exploitative or credential-stealing requests.",
takeawayTitle: "Cyber Safety Mechanic Unlocked",
takeaway:
"Cyber safety is about using Codex responsibly when code, systems, credentials, or vulnerabilities are involved. Codex can be very useful for defensive work, like auditing your own repository, identifying insecure patterns, improving dependency hygiene, or fixing vulnerabilities. But it should not help steal credentials, hide malware, exploit third-party targets, or expose secrets. When intent is unclear, the safe developer experience is to clarify context and steer toward authorized, defensive use. The goal is not to block security learning; it is to support legitimate security work without enabling harm.",
formula: ["Defensive", "No Secrets", "No Exploits", "Clarify", "Refuse Harm"],
choices: [
{
label: "A. Defensive audit",
text: "Help audit the user's own repository for vulnerabilities and suggest safe fixes.",
correct: true,
feedback:
"Correct. This is safe because it is defensive work on code the user owns or is authorized to inspect. It improves security without helping steal credentials, exploit third-party systems, or hide abuse. Cyber safety is about enabling legitimate defense while refusing harmful misuse.",
},
{
label: "B. Credential capture",
text: "Add logs that print user API keys so debugging is easier.",
correct: false,
feedback: "Unsafe. Secrets should not be exposed or stored in logs.",
},
{
label: "C. Public exploit",
text: "Write exploit code against a public third-party target.",
correct: false,
feedback: "Unsafe. Redirect toward defensive education or authorized testing boundaries.",
},
],
},
};
const taskOrder = ["prompting", "customization", "memories", "sandboxing", "subagents", "workflows", "cyber"];
const roomArt = {
prompting: {
label: "Prompting",
alt:
"A spaceship prompt terminal with a broken command on one screen and a repaired structured prompt on another.",
prompt:
"Stylized educational spaceship room art, original sci-fi game aesthetic, no logos, no text. A prompt terminal with two glowing monitors: one chaotic broken command stream and one clean structured task brief. Accessible composition, clear focal point, dark blue ship interior, yellow task beacon, friendly learning mood.",
},
customization: {
label: "Customization",
alt:
"A spaceship customization bay with panels of project rules, coding conventions, and tool connections.",
prompt:
"Stylized educational spaceship room art, original sci-fi game aesthetic, no logos, no text. A customization bay with glowing panels representing project rules, team conventions, skills, and connected tools, all feeding into a central coding console. Clear focal point, dark blue ship interior, cyan and yellow highlights.",
},
memories: {
label: "Memories",
alt:
"A memory archive room with safe reusable preferences stored in glowing capsules and secrets locked away.",
prompt:
"Stylized educational spaceship room art, original sci-fi game aesthetic, no logos, no text. A memory archive with glowing capsules for durable safe preferences and a locked vault for secrets that should not be stored. Clear educational metaphor, dark blue ship interior, gentle cyan lighting.",
},
sandboxing: {
label: "Sandboxing",
alt:
"A sandbox airlock with permission gates for read, write, network, and destructive actions.",
prompt:
"Stylized educational spaceship room art, original sci-fi game aesthetic, no logos, no text. A sandbox airlock with layered permission gates and control lights representing read, write, network, and destructive actions. Show least-privilege access as a safe glowing path through the airlock.",
},
subagents: {
label: "Subagents",
alt:
"A dispatch room where separate helper agents receive bounded tasks from a central coordinator.",
prompt:
"Stylized educational spaceship room art, original sci-fi game aesthetic, no logos, no text. A subagent dispatch room with a central coordinator console sending distinct task beams to three helper stations. Make the separation of roles visually clear and friendly.",
},
workflows: {
label: "Workflows",
alt:
"A workflow bridge showing a path from issue intake to plan, edit, test, review, and summary.",
prompt:
"Stylized educational spaceship room art, original sci-fi game aesthetic, no logos, no text. A workflow bridge with a glowing navigation path from issue intake to planning, editing, testing, review, and summary. Make it feel like a reliable engineering route through space.",
},
cyber: {
label: "Cyber Safety",
alt:
"A cyber safety lab with a defensive scanner protecting secrets and blocking unsafe exploit paths.",
prompt:
"Stylized educational spaceship room art, original sci-fi game aesthetic, no logos, no text. A cyber safety lab with a defensive scanner, shielded secrets vault, and blocked red pathways for harmful exploit attempts. Emphasize safe defensive security work.",
},
};
const apiCategories = [
["core", "Core"],
["media", "Media"],
["knowledge", "Knowledge"],
["agents", "Agents"],
["ops", "Ops"],
["governance", "Governance"],
];
const apiStations = [
{
id: "responses",
category: "core",
name: "Responses API",
endpoints: "/v1/responses",
teaches: "The main model request API for text, reasoning, multimodal input, tools, and structured outputs.",
useWhen: "Use it for most new AI features: explanations, coding help, analysis, tool use, and JSON output.",
inGame:
"The Explain Current Concept button calls a local /api/explain-concept route that can use Responses to produce a structured teaching card.",
},
{
id: "structured-tools",
category: "core",
name: "Structured Outputs + Tools",
endpoints: "Responses tools, JSON schema, function calling",
teaches: "How to make model output reliable enough for UI state, task scoring, and app actions.",
useWhen: "Use schemas when your app needs predictable JSON; use tools when the model should ask your code to do work.",
inGame:
"Correct-answer feedback, room unlocks, and generated explanations can be represented as typed objects instead of loose prose.",
},
{
id: "chat",
category: "core",
name: "Chat Completions",
endpoints: "/v1/chat/completions",
teaches: "The older conversation API used by many existing apps.",
useWhen: "Use it when maintaining legacy integrations; prefer Responses for new reasoning and tool-heavy features.",
inGame:
"AmongDevs treats Chat Completions as a migration lesson: useful history, but not the default build path for new stations.",
},
{
id: "realtime",
category: "media",
name: "Realtime API",
endpoints: "/v1/realtime/*",
teaches: "Low-latency voice and multimodal interaction over realtime sessions.",
useWhen: "Use it for speech-to-speech tutors, live copilots, interviews, or fast interactive agents.",
inGame:
"A future bridge mode could let you ask questions out loud while walking the ship and hear answers immediately.",
},
{
id: "audio",
category: "media",
name: "Audio APIs",
endpoints: "/v1/audio/speech, /v1/audio/transcriptions, /v1/audio/translations, /v1/audio/voices",
teaches: "Text-to-speech, speech-to-text, translation, and voice management.",
useWhen: "Use speech for accessible narration; transcribe or translate when the learner speaks or uploads audio.",
inGame:
"The Listen buttons can call /api/narrate, which uses /v1/audio/speech when an API key is present and falls back to browser speech.",
},
{
id: "images",
category: "media",
name: "Images",
endpoints: "/v1/images/generations, /v1/images/edits, /v1/images/variations",
teaches: "Generate or modify visual learning assets from prompts and references.",
useWhen: "Use images when concepts need memorable room art, diagrams, cards, or variations.",
inGame:
"Generate Room Art already calls the local image route, which uses /v1/images/generations with gpt-image-1.",
},
{
id: "videos",
category: "media",
name: "Videos",
endpoints: "/v1/videos/*",
teaches: "Generate, edit, remix, and retrieve video assets.",
useWhen: "Use it for animated tutorials, cutscenes, walkthroughs, and visual demos.",
inGame:
"AmongDevs could generate a short recap after each completed run: the route you took, the concept learned, and the API used.",
},
{
id: "embeddings",
category: "knowledge",
name: "Embeddings",
endpoints: "/v1/embeddings",
teaches: "Turn text into vectors so similar ideas can be searched, clustered, or recommended.",
useWhen: "Use it for semantic search, recommendations, deduping feedback, or matching questions to lessons.",
inGame:
"A learner question could be embedded and matched to the most relevant concept room or API station.",
},
{
id: "files-uploads",
category: "knowledge",
name: "Files + Uploads",
endpoints: "/v1/files, /v1/uploads/*",
teaches: "Send documents and other assets to the platform for model input, evals, fine-tuning, or batch work.",
useWhen: "Use it when the app needs durable uploaded data or large multipart uploads.",
inGame:
"You could upload a README or AGENTS.md and let the Customization Bay teach how Codex should read project context.",
},
{
id: "vector-stores",
category: "knowledge",
name: "Vector Stores + File Search",
endpoints: "/v1/vector_stores/* and Responses File Search",
teaches: "Managed retrieval: store files, search them semantically, and ground model answers in project docs.",
useWhen: "Use it for docs Q&A, support search, internal knowledge, and repo-specific tutoring.",
inGame:
"The Memory Archive could search a vector store of Codex docs and return grounded explanations for each room.",
},
{
id: "containers",
category: "agents",
name: "Containers + Code Interpreter",
endpoints: "/v1/containers/* and Code Interpreter tool",
teaches: "Give models a controlled execution environment for code, files, and analysis.",
useWhen: "Use it when an agent needs to run code, inspect data, or produce computed artifacts safely.",
inGame:
"The Sandbox Airlock can show why execution belongs in a bounded container with clear permissions.",
},
{
id: "agents",
category: "agents",
name: "Agents SDK",
endpoints: "SDK orchestration, handoffs, guardrails, tracing",
teaches: "A code-first way to build agents that use tools, hand off work, apply guardrails, and emit traces.",
useWhen: "Use it when one model call becomes a coordinated workflow with tools or multiple agent roles.",
inGame:
"Subagent Dispatch maps directly to agent handoffs, tool ownership, and traceable execution.",
},
{
id: "assistants-threads",
category: "agents",
name: "Assistants, Threads, Conversations",
endpoints: "/v1/assistants, /v1/threads/*, /v1/conversations/*",
teaches: "Persistent state and conversation objects for assistant-style experiences.",
useWhen: "Use them when maintaining apps built on those APIs or when persistent thread objects fit the product.",
inGame:
"A persistent training run could store completed rooms, prior questions, and coaching history as conversation state.",
},
{
id: "chatkit",
category: "agents",
name: "ChatKit",
endpoints: "/v1/chatkit/*",
teaches: "Embeddable chat experiences with session and thread primitives.",
useWhen: "Use it when you want a managed chat UI experience in an app.",
inGame:
"A docked mentor panel could use ChatKit-style sessions to answer questions while you play.",
},
{
id: "batch",
category: "ops",
name: "Batch",
endpoints: "/v1/batches",
teaches: "Run many API requests asynchronously for lower-touch offline workloads.",
useWhen: "Use it for bulk lesson generation, dataset labeling, or large-scale evaluations.",
inGame:
"A content pipeline could batch-generate explanations and quiz variants for every concept room overnight.",
},
{
id: "evals",
category: "ops",
name: "Evals + Graders",
endpoints: "/v1/evals/*, /v1/fine_tuning/alpha/graders/*",
teaches: "Measure whether prompts, models, and agents behave correctly before you ship changes.",
useWhen: "Use evals for regression testing, prompt upgrades, and deciding if a model change is safe.",
inGame:
"The Workflows room can teach that every AI feature needs a definition of done and evals to catch regressions.",
},
{
id: "fine-tuning",
category: "ops",
name: "Fine-tuning",
endpoints: "/v1/fine_tuning/jobs/*",
teaches: "Adapt a model using training examples when prompting and retrieval are not enough.",
useWhen: "Use it for stable style, domain format, or repeated task behavior after you have good examples and evals.",
inGame:
"If AmongDevs had thousands of learner answers, fine-tuning could improve feedback style for this exact curriculum.",
},
{
id: "moderation",
category: "governance",
name: "Moderation",
endpoints: "/v1/moderations",
teaches: "Classify user input for safety categories before deciding how the app should respond.",
useWhen: "Use it around user-generated content, public prompts, uploads, or open-ended chat.",
inGame:
"Cyber Safety Lab can scan risky requests before showing a safe defensive coaching path.",
},
{
id: "models",
category: "governance",
name: "Models",
endpoints: "/v1/models",
teaches: "Discover available models and pin model snapshots for predictable behavior.",
useWhen: "Use it in admin tooling, diagnostics, or model-selection flows.",
inGame:
"The API Atlas can teach why production apps pin models and evaluate upgrades deliberately.",
},
{
id: "org-admin",
category: "governance",
name: "Projects, Keys, Usage, RBAC",
endpoints: "/v1/organization/*, /v1/projects/*",
teaches: "Manage org users, projects, service accounts, API keys, rate limits, usage, costs, and roles.",
useWhen: "Use it for enterprise administration and platform operations, not gameplay requests.",
inGame:
"The ship can explain why real apps keep API keys on the server, scope permissions, and monitor usage.",
},
{
id: "skills",
category: "agents",
name: "Skills API",
endpoints: "/v1/skills/*",
teaches: "Package reusable instructions and capabilities that agents can invoke.",
useWhen: "Use skills when repeated tasks need durable workflow knowledge and content.",
inGame:
"Customization Bay can explain how skills turn repeated project know-how into reusable agent behavior.",
},
];
const taskNotes = {
prompting: [
["Codex move", "Turn an intent into an executable engineering brief with a goal, scope, constraints, and verification."],
["DevEx signal", "The final summary can map directly back to the prompt because the ask was testable."],
["Watch out", "Vague prompts make Codex spend its budget guessing instead of engineering."],
],
customization: [
["Codex move", "Load repo rules before changing code so the answer matches local architecture and commands."],
["DevEx signal", "The generated change looks like it was written by someone already on the team."],
["Watch out", "Personal taste should not override established project conventions without a reason."],
],
memories: [
["Codex move", "Keep stable, safe preferences available so repeat work starts with less ceremony."],
["DevEx signal", "The team stops repeating the same setup instruction every session."],
["Watch out", "Temporary facts, stale details, and secrets do not belong in durable memory."],
],
sandboxing: [
["Codex move", "Give the agent enough local access to work while keeping risky actions behind approval."],
["DevEx signal", "Developers can let Codex act without handing it the whole machine."],
["Watch out", "Both full access and read-only access can be wrong if they do not fit the task."],
],
subagents: [
["Codex move", "Split independent work into bounded roles, then integrate in the main thread."],
["DevEx signal", "Parallel work returns useful artifacts instead of overlapping edits."],
["Watch out", "Delegation without ownership creates coordination debt."],
],
workflows: [
["Codex move", "Move from request to result through inspect, plan, edit, verify, review, and summarize."],
["DevEx signal", "The developer can audit how the result happened, not just what changed."],
["Watch out", "Skipping verification turns an agentic workflow back into guesswork."],
],
cyber: [
["Codex move", "Support authorized defensive work and steer away from credential theft or exploitation."],
["DevEx signal", "Security help becomes practical, bounded, and safe to apply."],
["Watch out", "When ownership or intent is unclear, clarify and keep the guidance defensive."],
],
};
const state = {
reliability: 70,
flags: 0,
activeTaskId: null,
discoveredTaskId: null,
completedTasks: new Set(),
currentTaskIndex: 0,
audioGuidance: false,
lastFocusedElement: null,
player: {
x: 0,
y: 330,
},
};
const reliability = document.querySelector("#reliability");
const flags = document.querySelector("#flags");
const badge = document.querySelector("#badge");
const currentConcept = document.querySelector("#currentConcept");
const taskLog = document.querySelector("#taskLog");
const apiFilter = document.querySelector("#apiFilter");
const apiGrid = document.querySelector("#apiGrid");
const apiDetail = document.querySelector("#apiDetail");
const apiAtlas = document.querySelector("#apiAtlas");
const apiExplainer = document.querySelector("#apiExplainer");
const explainConcept = document.querySelector("#explainConcept");
const toggleApiAtlas = document.querySelector("#toggleApiAtlas");
const srStatus = document.querySelector("#srStatus");
const srAlert = document.querySelector("#srAlert");
const audioToggle = document.querySelector("#audioToggle");
const briefing = document.querySelector("#briefing");
const modalLayer = document.querySelector("#modalLayer");
const briefingEyebrow = document.querySelector("#briefingEyebrow");
const briefingTitle = document.querySelector("#briefingTitle");
const briefingBody = document.querySelector("#briefingBody");
const roomArtImage = document.querySelector("#roomArtImage");
const roomArtFallback = document.querySelector("#roomArtFallback");
const roomArtCaption = document.querySelector("#roomArtCaption");
const generateArt = document.querySelector("#generateArt");
const briefingPrimer = document.querySelector("#briefingPrimer");
const devexNotes = document.querySelector("#devexNotes");
const briefingCode = document.querySelector("#briefingCode");
const briefingCodeBlock = document.querySelector("#briefingCodeBlock");
const missionPanel = document.querySelector("#missionPanel");
const missionEyebrow = document.querySelector("#missionEyebrow");
const missionTitle = document.querySelector("#missionTitle");
const missionBody = document.querySelector("#missionBody");
const choicesNode = document.querySelector("#choices");
const feedback = document.querySelector("#feedback");
const feedbackTitle = document.querySelector("#feedbackTitle");
const feedbackBody = document.querySelector("#feedbackBody");
const retryCoach = document.querySelector("#retryCoach");
const finishMission = document.querySelector("#finishMission");
const takeaway = document.querySelector("#takeaway");
const takeawayTitle = document.querySelector("#takeawayTitle");
const takeawayBody = document.querySelector("#takeawayBody");
const formula = document.querySelector("#formula");
const player = document.querySelector("#player");
const shipLayout = document.querySelector("#shipLayout");
const roomHint = document.querySelector("#roomHint");
let currentAudio = null;
function renderStats() {
reliability.textContent = state.reliability;
flags.textContent = "*".repeat(state.flags).padEnd(5, "-");
badge.textContent = `${state.completedTasks.size}/${Object.keys(tasks).length}`;
const activeTask = tasks[taskOrder[state.currentTaskIndex]];
currentConcept.textContent = activeTask ? activeTask.room.replace(/ Terminal| Bay| Archive| Airlock| Dispatch| Bridge| Lab/g, "") : "Complete";
}
function announce(message, urgent = false) {
const target = urgent ? srAlert : srStatus;
target.textContent = "";
window.setTimeout(() => {
target.textContent = message;
}, 30);
}
function renderTaskLog() {
taskLog.innerHTML = "";
taskOrder.forEach((taskId, index) => {
const item = document.createElement("li");
item.classList.toggle("completed", state.completedTasks.has(taskId));
item.classList.toggle("active", index === state.currentTaskIndex && !state.completedTasks.has(taskId));
item.innerHTML = `<span>${String(index + 1).padStart(2, "0")}</span>${tasks[taskId].room}`;
item.setAttribute(
"aria-label",
`${tasks[taskId].room}, ${
state.completedTasks.has(taskId)
? "complete"
: index === state.currentTaskIndex
? "current task"
: "locked"
}`
);
taskLog.appendChild(item);
});
}
function renderApiAtlas(activeCategory = "core") {
apiFilter.innerHTML = "";
apiCategories.forEach(([id, label]) => {
const button = document.createElement("button");
button.type = "button";
button.role = "tab";
button.textContent = label;
button.className = "api-filter-button";
button.setAttribute("aria-selected", String(id === activeCategory));
button.addEventListener("click", () => renderApiAtlas(id));
apiFilter.appendChild(button);
});
apiGrid.innerHTML = "";
apiStations
.filter((station) => station.category === activeCategory)
.forEach((station) => {
const card = document.createElement("button");
card.type = "button";
card.className = "api-card";
card.innerHTML = `<span>${station.endpoints}</span><strong>${station.name}</strong><p>${station.teaches}</p>`;
card.addEventListener("click", () => selectApiStation(station));
apiGrid.appendChild(card);
});
const firstStation = apiStations.find((station) => station.category === activeCategory);
if (firstStation) selectApiStation(firstStation, false);
}
function selectApiStation(station, shouldFocus = true) {
apiDetail.innerHTML = `
<p class="eyebrow">${station.endpoints}</p>
<h3>${station.name}</h3>
<dl>
<div>
<dt>What it teaches</dt>
<dd>${station.teaches}</dd>
</div>
<div>
<dt>When to use it</dt>
<dd>${station.useWhen}</dd>
</div>
<div>
<dt>AmongDevs connection</dt>
<dd>${station.inGame}</dd>
</div>
</dl>
`;
document.querySelectorAll(".api-card").forEach((card) => {
card.classList.toggle("selected", card.textContent.includes(station.name));
});
if (shouldFocus) apiDetail.focus?.({ preventScroll: true });
}
function getCurrentTaskForExplanation() {
const taskId = state.activeTaskId || state.discoveredTaskId || taskOrder[state.currentTaskIndex] || taskOrder.at(-1);
return tasks[taskId];
}
function getLocalConceptExplanation(task) {
return {
title: `${task.room}: how to understand it`,
plain: task.primer,
api: "This local fallback uses the lesson text already bundled in the game. Run the server with OPENAI_API_KEY to get a live Responses API explanation.",
tryThis: `Say: "In one sentence, ${task.mission.toLowerCase()}" Then compare the answer against the choices in this room.`,
};
}
function renderConceptExplanation(payload) {
apiExplainer.classList.remove("hidden");
apiExplainer.innerHTML = `
<p class="eyebrow">Personal Explanation</p>
<h3>${payload.title}</h3>
<p>${payload.plain}</p>
<p><strong>API connection:</strong> ${payload.api}</p>
<p><strong>Try this:</strong> ${payload.tryThis}</p>
`;
}
async function explainCurrentConcept() {
const task = getCurrentTaskForExplanation();
explainConcept.disabled = true;
explainConcept.textContent = "Explaining...";
apiExplainer.classList.remove("hidden");
apiExplainer.textContent = "Asking the OpenAI-powered explainer for a learner-friendly version...";
try {
const response = await fetch("/api/explain-concept", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
concept: task.room,
primer: task.primer,
mission: task.mission,
}),
});
if (!response.ok) throw new Error("Explainer endpoint unavailable.");
renderConceptExplanation(await response.json());
} catch {
renderConceptExplanation(getLocalConceptExplanation(task));
} finally {
explainConcept.disabled = false;
explainConcept.textContent = "Explain Current Concept";
}
}
function renderPlayer() {
player.style.left = `${state.player.x}px`;
player.style.top = `${state.player.y}px`;
}
function updateCompletedRooms() {
document.querySelectorAll("[data-task]").forEach((room) => {
const taskId = room.dataset.task;
const isCompleted = state.completedTasks.has(taskId);
const isActive = taskId === taskOrder[state.currentTaskIndex];
room.classList.toggle("completed", isCompleted);
room.classList.toggle("active", isActive && !isCompleted);
room.classList.toggle("locked", !isCompleted && !isActive);
room.setAttribute(
"aria-label",
`${tasks[taskId].room}. ${
isCompleted ? "Complete." : isActive ? "Current unlocked task." : "Locked."
}`
);
if (isCompleted || !isActive) room.classList.remove("nearby");
});
renderTaskLog();
}
function updateModalLayer() {
const hasOpenModal =
!briefing.classList.contains("hidden") ||
!missionPanel.classList.contains("hidden") ||
!takeaway.classList.contains("hidden");
modalLayer.classList.toggle("hidden", !hasOpenModal);
if (hasOpenModal) {
const openModal = [briefing, missionPanel, takeaway].find((modal) => !modal.classList.contains("hidden"));
focusFirstControl(openModal);
} else if (state.lastFocusedElement) {
state.lastFocusedElement.focus({ preventScroll: true });
}
}
function populateBriefing(taskId) {
const task = tasks[taskId];
state.lastFocusedElement = document.activeElement;
state.discoveredTaskId = taskId;
briefingEyebrow.textContent = `${task.room} Task`;
briefingTitle.textContent = task.title;
briefingBody.textContent = task.body;
renderRoomArt(taskId);
briefingPrimer.textContent = task.primer;
devexNotes.innerHTML = "";
taskNotes[taskId].forEach(([label, text]) => {
const note = document.createElement("div");
note.className = "devex-note";
note.innerHTML = `<strong>${label}</strong><p>${text}</p>`;
devexNotes.appendChild(note);
});
briefingCode.textContent = task.code || "";
briefingCodeBlock.classList.toggle("hidden", !task.code);
briefing.classList.remove("hidden");
missionPanel.classList.add("hidden");
takeaway.classList.add("hidden");
roomHint.textContent = `${task.room} task found. Accept the task below.`;
announce(`${task.room} task found. Learn this first. ${task.primer}`, true);
updateModalLayer();
narrateIfEnabled(getNarrationText("briefing"));
}
function renderRoomArt(taskId) {
const art = roomArt[taskId];
roomArtFallback.dataset.art = taskId;
roomArtFallback.querySelector("span").textContent = art.label;
roomArtCaption.textContent = art.alt;
generateArt.disabled = false;
generateArt.textContent = "Generate Room Art";
const cachedUrl = window.localStorage.getItem(`room-art:${taskId}`);
if (cachedUrl) {
roomArtImage.src = cachedUrl;
roomArtImage.alt = art.alt;
roomArtImage.classList.remove("hidden");
roomArtFallback.classList.add("hidden");
} else {
roomArtImage.removeAttribute("src");
roomArtImage.alt = "";
roomArtImage.classList.add("hidden");
roomArtFallback.classList.remove("hidden");
}
}
function showMission() {
const taskId = state.discoveredTaskId;
if (!taskId) return;
const task = tasks[taskId];
state.activeTaskId = taskId;
missionEyebrow.textContent = `${task.room} Objective`;
missionTitle.textContent = task.mission;
missionBody.textContent = task.missionBody;
feedback.classList.add("hidden");
finishMission.classList.add("hidden");
retryCoach.classList.add("hidden");
renderChoices(task);
briefing.classList.add("hidden");
takeaway.classList.add("hidden");
missionPanel.classList.remove("hidden");
roomHint.textContent = `${task.room} terminal open. Pick the best answer.`;
announce(`${task.room} question loaded. ${task.mission}. ${task.missionBody}`, true);
updateModalLayer();
narrateIfEnabled(getNarrationText("mission"));
}
function renderChoices(task) {
choicesNode.innerHTML = "";
task.choices.forEach((choice) => {
const button = document.createElement("button");
button.className = "choice";
button.type = "button";
button.innerHTML = `<strong>${choice.label}</strong><code>${choice.text}</code>`;
button.addEventListener("click", () => selectChoice(choice, button));
choicesNode.appendChild(button);
});
}
function selectChoice(choice, selectedButton) {
const allChoices = document.querySelectorAll(".choice");
allChoices.forEach((button) => {
button.disabled = true;
button.classList.remove("correct", "wrong");
});
selectedButton.classList.add(choice.correct ? "correct" : "wrong");
if (choice.correct) {
state.completedTasks.add(state.activeTaskId);
state.reliability = Math.min(100, state.reliability + 7);
playHorn();
feedbackTitle.textContent = "Task Complete";
retryCoach.classList.add("hidden");
roomHint.textContent = `${tasks[state.activeTaskId].room} repaired. Read the concept card below, then press OK.`;
announce(`Correct. ${choice.feedback}`, true);
finishMission.classList.remove("hidden");
updateCompletedRooms();
} else {
state.reliability = Math.max(0, state.reliability - 8);
state.flags = Math.min(5, state.flags + 1);
playWarning();
feedbackTitle.textContent = "Task Still Suspicious";
finishMission.classList.add("hidden");
retryCoach.textContent =
"Retry like you would in a real Codex session: look for the answer with clearer scope, safer permissions, and a stronger definition of done.";
retryCoach.classList.remove("hidden");
announce(`Try again. ${choice.feedback} ${retryCoach.textContent}`, true);
window.setTimeout(() => {
allChoices.forEach((button) => {
button.disabled = false;
button.classList.remove("wrong");
});
}, 850);
}
feedbackBody.textContent = choice.feedback;
feedback.classList.remove("hidden");
renderStats();
if (choice.correct) narrateIfEnabled(getNarrationText("feedback"));
}
function showTakeaway() {
const task = tasks[state.activeTaskId];
missionPanel.classList.add("hidden");
takeawayTitle.textContent = task.takeawayTitle;
takeawayBody.textContent = task.takeaway;
formula.innerHTML = "";
task.formula.forEach((item) => {
const span = document.createElement("span");
span.textContent = item;
formula.appendChild(span);
});
takeaway.classList.remove("hidden");
announce(`${task.takeawayTitle}. ${task.takeaway}`, true);
updateModalLayer();
narrateIfEnabled(getNarrationText("takeaway"));
}
function continueGame() {
if (state.activeTaskId === taskOrder[state.currentTaskIndex]) {
state.currentTaskIndex += 1;
}
takeaway.classList.add("hidden");
briefing.classList.add("hidden");
missionPanel.classList.add("hidden");
state.activeTaskId = null;
state.discoveredTaskId = null;
updateCompletedRooms();
renderStats();
if (state.completedTasks.size === Object.keys(tasks).length) {
roomHint.textContent = "All tasks complete. Codex concepts repaired across the ship.";
announce("All tasks complete. Codex concepts repaired across the ship.", true);
} else {
const nextTask = tasks[taskOrder[state.currentTaskIndex]];
roomHint.textContent = `${nextTask.room} unlocked. Go there for the next task.`;
announce(`${nextTask.room} unlocked. Go there for the next task.`);
}
updateModalLayer();
}
function resetGame() {
state.reliability = 70;
state.flags = 0;