@alexkroman1/aai-cli 6.11.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +1 -0
  2. package/dist/{_agent-DpH2pBJd.mjs → _agent-BTwZJu4P.mjs} +2 -2
  3. package/dist/{_bundler-C2BDwU7r.mjs → _bundler-CDuYl5Gb.mjs} +2 -2
  4. package/dist/_dev-env.d.ts +22 -2
  5. package/dist/{_dev-server-LLLWnFBs.mjs → _dev-server-Me06wgeF.mjs} +38 -47
  6. package/dist/_dev-server.d.ts +0 -17
  7. package/dist/{_init-C57bWAQ8.mjs → _init-BhEfQ6Yi.mjs} +3 -2
  8. package/dist/{_preflight-BtfaYtbE.mjs → _preflight-Co1G9Jww.mjs} +1 -1
  9. package/dist/_preflight.d.ts +1 -1
  10. package/dist/_resource-commands.d.ts +23 -0
  11. package/dist/{_slug-api-BxrsJXVI.mjs → _slug-api-z-USxPtL.mjs} +16 -12
  12. package/dist/_slug-api.d.ts +5 -2
  13. package/dist/{_studio-U3jhKlA3.mjs → _studio-DR5yzZ1T.mjs} +2 -1
  14. package/dist/{_templates-Dxb_P2Wz.mjs → _templates-4WcKOjS5.mjs} +5 -6
  15. package/dist/_templates.d.ts +3 -4
  16. package/dist/_workflow-scan.d.ts +108 -0
  17. package/dist/{build-Dfza2pRI.mjs → build-BBxf8vom.mjs} +6 -3
  18. package/dist/cli.mjs +346 -349
  19. package/dist/{client-bundler-BEIqgOtd.mjs → client-bundler-CWnG42cU.mjs} +2 -1
  20. package/dist/client-bundler.mjs +1 -1
  21. package/dist/{delete-vbpjGzqs.mjs → delete-Dl_LSfvI.mjs} +3 -3
  22. package/dist/{deploy-B8lubiRT.mjs → deploy-DRC8jWr8.mjs} +5 -5
  23. package/dist/{dev-Uq5ujP8a.mjs → dev-BvcctCn2.mjs} +1 -1
  24. package/dist/eval-DfQ8cm5p.mjs +56 -0
  25. package/dist/eval.d.ts +43 -0
  26. package/dist/{init-l6xfU4xX.mjs → init-CUo87gcI.mjs} +3 -3
  27. package/dist/{login-C71-qz8F.mjs → login-BBuM1sxH.mjs} +3 -4
  28. package/dist/{logs-BWVGSjLR.mjs → logs-CQ2T46dr.mjs} +2 -2
  29. package/dist/project-config.mjs +1 -1
  30. package/dist/scaffold/CLAUDE.md +442 -80
  31. package/dist/scaffold/package.json +6 -3
  32. package/dist/scaffold/server.mjs +11 -4
  33. package/dist/scaffold/tsconfig.json +0 -1
  34. package/dist/{secret-BuMuFR4B.mjs → secret-Ct34kdgU.mjs} +2 -2
  35. package/dist/{storage-DzRZ-eCw.mjs → storage-i_79ZwAR.mjs} +2 -2
  36. package/dist/{studio-BCNUpDgP.mjs → studio-CzTzPqe1.mjs} +4 -4
  37. package/dist/templates/briefing-desk/agent.test.ts +350 -0
  38. package/dist/templates/briefing-desk/agent.ts +30 -0
  39. package/dist/templates/briefing-desk/shared.ts +200 -0
  40. package/dist/templates/briefing-desk/system-prompt.md +39 -0
  41. package/dist/templates/briefing-desk/tools/briefing_so_far.ts +35 -0
  42. package/dist/templates/briefing-desk/tools/research_topic.ts +88 -0
  43. package/dist/templates/briefing-desk/tools/verify_claim.ts +77 -0
  44. package/dist/templates/call-audit/agent.eval.test.ts +474 -0
  45. package/dist/templates/call-audit/agent.test.ts +57 -204
  46. package/dist/templates/call-audit/agent.ts +32 -19
  47. package/dist/templates/call-audit/client.tsx +17 -54
  48. package/dist/templates/call-audit/workflows/audit.ts +11 -18
  49. package/dist/templates/call-audit/workflows/ingest.ts +106 -114
  50. package/dist/templates/call-audit/workflows/media.ts +2 -12
  51. package/dist/templates/call-audit/workflows/summarize.ts +47 -52
  52. package/dist/templates/call-audit/workflows/sync-api.ts +7 -7
  53. package/dist/templates/code-interpreter/agent.eval.test.ts +165 -0
  54. package/dist/templates/dispatch-center/agent.eval.test.ts +349 -0
  55. package/dist/templates/dispatch-center/agent.test.ts +149 -25
  56. package/dist/templates/dispatch-center/client.tsx +239 -129
  57. package/dist/templates/dispatch-center/shared.ts +99 -1
  58. package/dist/templates/dispatch-center/system-prompt.md +3 -1
  59. package/dist/templates/dispatch-center/tools/incident_add_note.ts +16 -13
  60. package/dist/templates/dispatch-center/tools/incident_create.ts +19 -1
  61. package/dist/templates/dispatch-center/tools/incident_escalate.ts +68 -59
  62. package/dist/templates/dispatch-center/tools/incident_triage.ts +57 -43
  63. package/dist/templates/dispatch-center/tools/incident_update_status.ts +49 -40
  64. package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +8 -2
  65. package/dist/templates/dispatch-center/tools/resources_dispatch.ts +69 -52
  66. package/dist/templates/dispatch-center/tools/resources_update_status.ts +45 -41
  67. package/dist/templates/embedded-assets/agent.eval.test.ts +138 -0
  68. package/dist/templates/embedded-assets/agent.test.ts +17 -14
  69. package/dist/templates/health-assistant/agent.eval.test.ts +167 -0
  70. package/dist/templates/health-assistant/agent.test.ts +22 -4
  71. package/dist/templates/infocom-adventure/agent.eval.test.ts +158 -0
  72. package/dist/templates/infocom-adventure/agent.test.ts +53 -24
  73. package/dist/templates/infocom-adventure/agent.ts +21 -0
  74. package/dist/templates/infocom-adventure/client.tsx +246 -164
  75. package/dist/templates/infocom-adventure/shared.ts +20 -9
  76. package/dist/templates/infocom-adventure/system-prompt.md +1 -1
  77. package/dist/templates/infocom-adventure/tools/game_state_move.ts +3 -2
  78. package/dist/templates/link-digest/agent.eval.test.ts +195 -0
  79. package/dist/templates/link-digest/agent.test.ts +24 -19
  80. package/dist/templates/link-digest/client.tsx +47 -61
  81. package/dist/templates/link-digest/workflows/digest.ts +19 -31
  82. package/dist/templates/math-buddy/agent.eval.test.ts +146 -0
  83. package/dist/templates/night-owl/agent.eval.test.ts +272 -0
  84. package/dist/templates/night-owl/agent.test.ts +70 -19
  85. package/dist/templates/night-owl/agent.ts +5 -0
  86. package/dist/templates/night-owl/client.tsx +56 -56
  87. package/dist/templates/night-owl/shared.ts +24 -0
  88. package/dist/templates/night-owl/tools/recommend.ts +18 -6
  89. package/dist/templates/personal-finance/agent.eval.test.ts +168 -0
  90. package/dist/templates/personal-finance/system-prompt.md +4 -0
  91. package/dist/templates/pipeline-simple/agent.eval.test.ts +78 -0
  92. package/dist/templates/pipeline-simple/agent.ts +2 -2
  93. package/dist/templates/pizza-ordering/agent.eval.test.ts +171 -0
  94. package/dist/templates/pizza-ordering/agent.test.ts +42 -27
  95. package/dist/templates/pizza-ordering/client.tsx +9 -26
  96. package/dist/templates/pizza-ordering/shared.ts +20 -1
  97. package/dist/templates/pizza-ordering/system-prompt.md +6 -0
  98. package/dist/templates/plan-and-execute/agent.eval.test.ts +209 -0
  99. package/dist/templates/plan-and-execute/agent.test.ts +99 -58
  100. package/dist/templates/plan-and-execute/agent.ts +1 -1
  101. package/dist/templates/plan-and-execute/client.tsx +12 -15
  102. package/dist/templates/plan-and-execute/shared.ts +71 -2
  103. package/dist/templates/plan-and-execute/tools/plan_status.ts +18 -6
  104. package/dist/templates/plan-and-execute/tools/revise_plan.ts +18 -10
  105. package/dist/templates/plan-and-execute/tools/start_plan.ts +13 -2
  106. package/dist/templates/plan-and-execute/tools/work_next_step.ts +70 -22
  107. package/dist/templates/podcast-digest/agent.eval.test.ts +411 -0
  108. package/dist/templates/podcast-digest/agent.test.ts +717 -0
  109. package/dist/templates/podcast-digest/agent.ts +139 -0
  110. package/dist/templates/podcast-digest/client.tsx +154 -0
  111. package/dist/templates/podcast-digest/workflows/digest.ts +444 -0
  112. package/dist/templates/podcast-digest/workflows/feeds.ts +507 -0
  113. package/dist/templates/podcast-digest/workflows/slack.ts +88 -0
  114. package/dist/templates/recap-workflow/agent.eval.test.ts +366 -0
  115. package/dist/templates/recap-workflow/agent.test.ts +97 -94
  116. package/dist/templates/recap-workflow/tools/recap_progress.ts +9 -12
  117. package/dist/templates/recap-workflow/tools/recap_status.ts +8 -9
  118. package/dist/templates/recap-workflow/tools/request_recap.ts +2 -1
  119. package/dist/templates/recap-workflow/workflows/recap.ts +36 -40
  120. package/dist/templates/redline/agent.eval.test.ts +209 -0
  121. package/dist/templates/redline/agent.test.ts +17 -15
  122. package/dist/templates/redline/client.tsx +12 -12
  123. package/dist/templates/redline/workflows/redline.ts +19 -31
  124. package/dist/templates/research-workflow/agent.eval.test.ts +329 -0
  125. package/dist/templates/research-workflow/agent.test.ts +60 -59
  126. package/dist/templates/research-workflow/tools/research_progress.ts +9 -12
  127. package/dist/templates/research-workflow/tools/research_status.ts +9 -11
  128. package/dist/templates/research-workflow/workflows/research.ts +44 -61
  129. package/dist/templates/retail/address.ts +87 -1
  130. package/dist/templates/retail/agent.eval.test.ts +314 -0
  131. package/dist/templates/retail/agent.test.ts +195 -112
  132. package/dist/templates/retail/agent.ts +35 -1
  133. package/dist/templates/retail/cancel.ts +116 -0
  134. package/dist/templates/retail/client.tsx +265 -117
  135. package/dist/templates/retail/payment.ts +107 -0
  136. package/dist/templates/retail/pending.test.ts +196 -0
  137. package/dist/templates/retail/pending.ts +137 -0
  138. package/dist/templates/retail/registry.test.ts +150 -7
  139. package/dist/templates/retail/returns.ts +102 -0
  140. package/dist/templates/retail/shared.test.ts +42 -0
  141. package/dist/templates/retail/shared.ts +35 -0
  142. package/dist/templates/retail/store.test.ts +84 -15
  143. package/dist/templates/retail/store.ts +231 -48
  144. package/dist/templates/retail/swap.test.ts +4 -4
  145. package/dist/templates/retail/swap.ts +248 -15
  146. package/dist/templates/retail/system-prompt.md +69 -28
  147. package/dist/templates/retail/tools/cancel_change.ts +33 -0
  148. package/dist/templates/retail/tools/cancel_pending_order.ts +16 -60
  149. package/dist/templates/retail/tools/confirm_change.ts +42 -0
  150. package/dist/templates/retail/tools/exchange_delivered_order_items.ts +22 -67
  151. package/dist/templates/retail/tools/find_user_id_by_email.ts +8 -5
  152. package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +4 -5
  153. package/dist/templates/retail/tools/get_item_details.ts +3 -4
  154. package/dist/templates/retail/tools/get_order_details.ts +3 -4
  155. package/dist/templates/retail/tools/get_product_details.ts +3 -4
  156. package/dist/templates/retail/tools/get_user_details.ts +2 -2
  157. package/dist/templates/retail/tools/list_all_product_types.ts +5 -5
  158. package/dist/templates/retail/tools/modify_pending_order_address.ts +13 -25
  159. package/dist/templates/retail/tools/modify_pending_order_items.ts +23 -62
  160. package/dist/templates/retail/tools/modify_pending_order_payment.ts +16 -78
  161. package/dist/templates/retail/tools/modify_user_address.ts +13 -16
  162. package/dist/templates/retail/tools/return_delivered_order_items.ts +17 -72
  163. package/dist/templates/retail/tools/transfer_to_human_agents.ts +12 -6
  164. package/dist/templates/simple/agent.eval.test.ts +54 -0
  165. package/dist/templates/solo-rpg/agent.eval.test.ts +320 -0
  166. package/dist/templates/solo-rpg/agent.test.ts +227 -48
  167. package/dist/templates/solo-rpg/client.tsx +38 -37
  168. package/dist/templates/solo-rpg/shared.ts +145 -19
  169. package/dist/templates/solo-rpg/system-prompt.md +3 -2
  170. package/dist/templates/solo-rpg/tools/action_roll.ts +82 -63
  171. package/dist/templates/solo-rpg/tools/burn_momentum.ts +85 -50
  172. package/dist/templates/solo-rpg/tools/check_state.ts +24 -7
  173. package/dist/templates/solo-rpg/tools/load_game.ts +13 -1
  174. package/dist/templates/solo-rpg/tools/save_game.ts +16 -7
  175. package/dist/templates/solo-rpg/tools/setup_character.ts +22 -2
  176. package/dist/templates/solo-rpg/tools/update_state.ts +113 -100
  177. package/dist/templates/spoken-summary/agent.eval.test.ts +342 -0
  178. package/dist/templates/spoken-summary/agent.test.ts +68 -97
  179. package/dist/templates/spoken-summary/agent.ts +30 -17
  180. package/dist/templates/spoken-summary/client.tsx +10 -50
  181. package/dist/templates/spoken-summary/workflows/summarize.ts +17 -22
  182. package/dist/templates/spoken-summary/workflows/transcribe.ts +19 -26
  183. package/dist/templates/support-line/agent.eval.test.ts +273 -0
  184. package/dist/templates/support-line/agent.test.ts +11 -16
  185. package/dist/templates/support-line/agent.ts +1 -1
  186. package/dist/templates/support-line/client.tsx +9 -9
  187. package/dist/templates/support-line/nodes.ts +100 -0
  188. package/dist/templates/support-line/procedure.ts +407 -0
  189. package/dist/templates/support-line/system-prompt.md +2 -1
  190. package/dist/templates/support-line/tools/answer_question.ts +66 -4
  191. package/dist/templates/transcription-workflow/agent.eval.test.ts +234 -0
  192. package/dist/templates/transcription-workflow/agent.test.ts +94 -194
  193. package/dist/templates/transcription-workflow/agent.ts +1 -1
  194. package/dist/templates/transcription-workflow/client.tsx +17 -42
  195. package/dist/templates/transcription-workflow/workflows/batch.ts +19 -22
  196. package/dist/templates/transcription-workflow/workflows/normalize.ts +90 -186
  197. package/dist/templates/transcription-workflow/workflows/stitch.ts +0 -11
  198. package/dist/templates/transcription-workflow/workflows/stream.ts +4 -5
  199. package/dist/templates/transcription-workflow/workflows/sync-api.ts +6 -6
  200. package/dist/templates/transcription-workflow/workflows/transcribe.ts +32 -26
  201. package/dist/templates/transcription-workflow/workflows/wav.ts +0 -31
  202. package/dist/templates/travel-concierge/agent.eval.test.ts +323 -0
  203. package/dist/templates/travel-concierge/agent.test.ts +174 -43
  204. package/dist/templates/travel-concierge/agent.ts +18 -9
  205. package/dist/templates/travel-concierge/client.tsx +11 -23
  206. package/dist/templates/travel-concierge/routing.ts +34 -15
  207. package/dist/templates/travel-concierge/shared.ts +180 -12
  208. package/dist/templates/travel-concierge/system-prompt.md +7 -1
  209. package/dist/templates/travel-concierge/tools/book_car_rental.ts +8 -5
  210. package/dist/templates/travel-concierge/tools/book_excursion.ts +8 -5
  211. package/dist/templates/travel-concierge/tools/book_hotel.ts +8 -5
  212. package/dist/templates/travel-concierge/tools/cancel_ticket.ts +8 -5
  213. package/dist/templates/travel-concierge/tools/search_car_rentals.ts +9 -5
  214. package/dist/templates/travel-concierge/tools/search_excursions.ts +9 -5
  215. package/dist/templates/travel-concierge/tools/search_flights.ts +9 -6
  216. package/dist/templates/travel-concierge/tools/search_hotels.ts +9 -5
  217. package/dist/templates/travel-concierge/tools/update_ticket.ts +8 -5
  218. package/dist/templates/web-researcher/agent.eval.test.ts +121 -0
  219. package/dist/templates/web-researcher/system-prompt.md +15 -4
  220. package/dist/{test-9kPKJI-w.mjs → test-BvyT6sAc.mjs} +24 -10
  221. package/dist/test.d.ts +29 -5
  222. package/dist/{worker-bundler-CGD4r8Kc.mjs → worker-bundler-CCVEDjm1.mjs} +241 -3
  223. package/dist/worker-bundler.mjs +1 -1
  224. package/dist/{workflow-CFpxOFfQ.mjs → workflow-BflATMmU.mjs} +47 -12
  225. package/dist/workflow-bundler.d.ts +6 -1
  226. package/dist/workflow.d.ts +1 -1
  227. package/package.json +5 -4
  228. package/dist/eject-31gjtaHF.mjs +0 -116
  229. package/dist/eject.d.ts +0 -60
  230. package/dist/templates/call-audit/workflows/temp-media.ts +0 -138
  231. package/dist/templates/infocom-adventure/tools/game_state_history.ts +0 -14
  232. package/dist/templates/support-line/graph.ts +0 -224
  233. package/dist/{_config-D_s09e7g.mjs → _config-CmJOFsAP.mjs} +1 -1
  234. /package/dist/templates/plan-and-execute/{graph.ts → procedure.ts} +0 -0
@@ -1,17 +1,26 @@
1
1
  import { z } from "zod";
2
- import { gameSlot, saveSlotKey, saveSlotParam, saveState } from "../shared.ts";
2
+ import { gameSlot, saveSlotKey, saveSlotParam, saveState, storyFlow } from "../shared.ts";
3
3
 
4
4
  // Requires storage — `aai storage enable` (or DATABASE_URL in .env under
5
5
  // `aai dev`); the rest of the game works without it.
6
6
  //
7
- // `gameSlot.tool` even though the body AWAITS: the reading half places no
8
- // constraint on the body (only `updateTool` must be synchronous, because its
9
- // draft is stored when it returns). What it does place is the frozen value, and
10
- // a save is the purest read there is.
11
- export default gameSlot.tool({
7
+ /**
8
+ * Gated on `playing` or `gameOver` i.e. anything but `awaitingSetup`.
9
+ *
10
+ * Saving before a character exists writes an empty campaign under a slot name,
11
+ * and `load_game` would then cheerfully restore it over a real game. That is a
12
+ * position, so it is a `when`.
13
+ *
14
+ * The body AWAITS, which a flow tool allows: only `slot.updateTool` must be
15
+ * synchronous, because its draft is stored when it returns. What a save needs is
16
+ * the frozen value, and `gameSlot.get` is what hands it over.
17
+ */
18
+ export default storyFlow.tool({
12
19
  description: "Save current game to persistent storage.",
13
20
  inputSchema: z.object({ slot: saveSlotParam }),
14
- async execute(args, game, ctx) {
21
+ when: ["playing", "gameOver"],
22
+ async execute(args, ctx) {
23
+ const game = gameSlot.get(ctx);
15
24
  await saveState(ctx, saveSlotKey(args.slot), game);
16
25
  return {
17
26
  saved: true,
@@ -14,10 +14,24 @@ import {
14
14
  makeNpc,
15
15
  shuffle,
16
16
  stateSummary,
17
+ storyFlow,
17
18
  TIME_PHASES,
18
19
  TONES,
19
20
  } from "../shared.ts";
20
21
 
22
+ /**
23
+ * **Not a `storyFlow.tool`, deliberately.** Setting up is legal in every state —
24
+ * a player may start over at any point, an ended story included — so a `when`
25
+ * listing all of them would be a gate that gates nothing. It drives the flow
26
+ * itself, which is what `dialog.reset` and `dialog.send` are public for, and reports
27
+ * the position it landed in so the narrator reads what the opening turn expects.
28
+ *
29
+ * **`reset` before `send`, because a restart is not a transition.** This body
30
+ * replaces the campaign with a pristine `DEFAULT_STATE`; resetting the flow is
31
+ * the same statement on the other side, and it is what makes starting over work
32
+ * from `gameOver` — a final state delivers no events, so a `SETUP` transition
33
+ * out of it would be dead config.
34
+ */
21
35
  export default tool({
22
36
  description:
23
37
  "Set up the entire game in one call. Starts a completely fresh game (any previous unsaved game is replaced), generates stats, initializes state, and marks the game as ready. After this returns, just narrate the opening scene. No need to call update_state — everything is already done.",
@@ -186,12 +200,16 @@ export default tool({
186
200
  storyComplete: false,
187
201
  };
188
202
 
189
- // Mark initialized
203
+ // Mark initialized. `initialized` is the CLIENT's render flag; the flow is
204
+ // what gates the tools, and `SETUP` is what puts it in `playing` — so the
205
+ // roll tools become available in the same call that gives them stats to
206
+ // roll against.
190
207
  state.initialized = true;
191
- state.phase = "playing";
192
208
  state.sceneCount = 1;
193
209
 
194
210
  gameSlot.set(ctx, state);
211
+ storyFlow.reset(ctx);
212
+ const at = storyFlow.send(ctx, { type: "SETUP" });
195
213
 
196
214
  return {
197
215
  success: true,
@@ -201,6 +219,8 @@ export default tool({
201
219
  archetypeLabel: ARCHETYPES[args.archetype as keyof typeof ARCHETYPES] || args.archetype,
202
220
  openingSituation: args.openingSituation,
203
221
  creativitySeed: creativitySeed(),
222
+ // The position spread verbatim — the same keys every gated tool reports.
223
+ ...at,
204
224
  // The real saved state — never hardcoded values
205
225
  ...stateSummary(state),
206
226
  };
@@ -15,13 +15,23 @@ import {
15
15
  makeNpc,
16
16
  nextSeqId,
17
17
  stateSummary,
18
+ storyFlow,
18
19
  TIME_PHASES,
19
- updateCrisisFlags,
20
20
  } from "../shared.ts";
21
21
 
22
- export default gameSlot.updateTool({
22
+ /**
23
+ * Gated on `playing`: there is nothing to sync before a character exists (that
24
+ * is `setup_character`'s whole job, and the prompt says not to call this after
25
+ * it), and nothing to sync after the story has ended.
26
+ *
27
+ * It sends `SETTLED`, because moving the scene on is what SPENDS a standing
28
+ * roll: once the narrator has changed the location or logged the scene, the
29
+ * player's window to burn momentum on the last roll has closed.
30
+ */
31
+ export default storyFlow.tool({
23
32
  description:
24
33
  "Lightweight state sync for during gameplay. Handles location changes, NPC additions, clock additions, time changes, and session log entries. Resource changes (health/spirit/supply/momentum) are auto-applied by action_roll — only use those fields here for manual adjustments like resting or trading. Pass only what changed.",
34
+ when: "playing",
25
35
  inputSchema: z.object({
26
36
  location: z.string().max(200).describe("New location name").optional(),
27
37
  locationDesc: z.string().max(500).describe("Short location description").optional(),
@@ -54,117 +64,120 @@ export default gameSlot.updateTool({
54
64
  storyComplete: z.boolean().describe("Mark story as complete").optional(),
55
65
  logEntry: z.string().max(500).describe("Short log entry for this scene").optional(),
56
66
  }),
57
- execute(args, state) {
58
- const warnings: string[] = [];
59
- const clockEvents: { clock: string; trigger: string }[] = [];
67
+ execute: (args, ctx) =>
68
+ gameSlot.update(ctx, (state) => {
69
+ const warnings: string[] = [];
70
+ const clockEvents: { clock: string; trigger: string }[] = [];
60
71
 
61
- // Resources
62
- if (args.health !== undefined) state.health = Math.max(0, Math.min(MAX_RESOURCE, args.health));
63
- if (args.spirit !== undefined) state.spirit = Math.max(0, Math.min(MAX_RESOURCE, args.spirit));
64
- if (args.supply !== undefined) state.supply = Math.max(0, Math.min(MAX_RESOURCE, args.supply));
65
- if (args.momentum !== undefined)
66
- state.momentum = Math.max(MIN_MOMENTUM, Math.min(state.maxMomentum, args.momentum));
72
+ // Resources
73
+ if (args.health !== undefined)
74
+ state.health = Math.max(0, Math.min(MAX_RESOURCE, args.health));
75
+ if (args.spirit !== undefined)
76
+ state.spirit = Math.max(0, Math.min(MAX_RESOURCE, args.spirit));
77
+ if (args.supply !== undefined)
78
+ state.supply = Math.max(0, Math.min(MAX_RESOURCE, args.supply));
79
+ if (args.momentum !== undefined)
80
+ state.momentum = Math.max(MIN_MOMENTUM, Math.min(state.maxMomentum, args.momentum));
67
81
 
68
- // Location
69
- if (args.location !== undefined) state.currentLocation = args.location;
70
- if (args.locationDesc !== undefined) state.currentSceneContext = args.locationDesc;
71
- if (args.timeOfDay !== undefined) state.timeOfDay = args.timeOfDay;
82
+ // Location
83
+ if (args.location !== undefined) state.currentLocation = args.location;
84
+ if (args.locationDesc !== undefined) state.currentSceneContext = args.locationDesc;
85
+ if (args.timeOfDay !== undefined) state.timeOfDay = args.timeOfDay;
72
86
 
73
- // Add NPC
74
- if (args.addNpcName) {
75
- if (state.npcs.length >= MAX_NPCS) {
76
- warnings.push(
77
- `NPC limit of ${MAX_NPCS} reached — mark an existing NPC deceased or background instead.`,
78
- );
79
- } else {
80
- state.npcs.push(
81
- makeNpc({
82
- id: nextSeqId(state.npcs, "npc"),
83
- name: args.addNpcName,
84
- description: args.addNpcDesc,
85
- disposition: args.addNpcDisposition,
86
- agenda: args.addNpcAgenda,
87
- }),
88
- );
87
+ // Add NPC
88
+ if (args.addNpcName) {
89
+ if (state.npcs.length >= MAX_NPCS) {
90
+ warnings.push(
91
+ `NPC limit of ${MAX_NPCS} reached — mark an existing NPC deceased or background instead.`,
92
+ );
93
+ } else {
94
+ state.npcs.push(
95
+ makeNpc({
96
+ id: nextSeqId(state.npcs, "npc"),
97
+ name: args.addNpcName,
98
+ description: args.addNpcDesc,
99
+ disposition: args.addNpcDisposition,
100
+ agenda: args.addNpcAgenda,
101
+ }),
102
+ );
103
+ }
89
104
  }
90
- }
91
105
 
92
- // Update NPC
93
- if (args.updateNpcId) {
94
- const npc = state.npcs.find((n) => n.id === args.updateNpcId);
95
- if (npc) {
96
- if (args.updateNpcDisposition !== undefined) npc.disposition = args.updateNpcDisposition;
97
- if (args.updateNpcBond !== undefined) npc.bond = args.updateNpcBond;
98
- if (args.updateNpcStatus !== undefined) npc.status = args.updateNpcStatus;
99
- } else {
100
- warnings.push(`No NPC with id ${args.updateNpcId}.`);
106
+ // Update NPC
107
+ if (args.updateNpcId) {
108
+ const npc = state.npcs.find((n) => n.id === args.updateNpcId);
109
+ if (npc) {
110
+ if (args.updateNpcDisposition !== undefined) npc.disposition = args.updateNpcDisposition;
111
+ if (args.updateNpcBond !== undefined) npc.bond = args.updateNpcBond;
112
+ if (args.updateNpcStatus !== undefined) npc.status = args.updateNpcStatus;
113
+ } else {
114
+ warnings.push(`No NPC with id ${args.updateNpcId}.`);
115
+ }
101
116
  }
102
- }
103
117
 
104
- // Add clock
105
- if (args.addClockName) {
106
- if (state.clocks.length >= MAX_CLOCKS) {
107
- warnings.push(`Clock limit of ${MAX_CLOCKS} reached — remove a finished clock first.`);
108
- } else {
109
- state.clocks.push({
110
- id: nextSeqId(state.clocks, "clock"),
111
- name: args.addClockName,
112
- clockType: args.addClockType ?? "threat",
113
- segments: args.addClockSegments ?? DEFAULT_CLOCK_SEGMENTS,
114
- filled: 0,
115
- triggerDescription: args.addClockTrigger ?? "",
116
- });
118
+ // Add clock
119
+ if (args.addClockName) {
120
+ if (state.clocks.length >= MAX_CLOCKS) {
121
+ warnings.push(`Clock limit of ${MAX_CLOCKS} reached — remove a finished clock first.`);
122
+ } else {
123
+ state.clocks.push({
124
+ id: nextSeqId(state.clocks, "clock"),
125
+ name: args.addClockName,
126
+ clockType: args.addClockType ?? "threat",
127
+ segments: args.addClockSegments ?? DEFAULT_CLOCK_SEGMENTS,
128
+ filled: 0,
129
+ triggerDescription: args.addClockTrigger ?? "",
130
+ });
131
+ }
117
132
  }
118
- }
119
133
 
120
- // Advance clock
121
- if (args.advanceClockName) {
122
- const clock = state.clocks.find((c) => c.name === args.advanceClockName);
123
- if (clock && clock.filled < clock.segments) {
124
- clock.filled = Math.min(clock.segments, clock.filled + 1);
125
- if (clock.filled >= clock.segments) {
126
- clockEvents.push({ clock: clock.name, trigger: clock.triggerDescription });
134
+ // Advance clock
135
+ if (args.advanceClockName) {
136
+ const clock = state.clocks.find((c) => c.name === args.advanceClockName);
137
+ if (clock && clock.filled < clock.segments) {
138
+ clock.filled = Math.min(clock.segments, clock.filled + 1);
139
+ if (clock.filled >= clock.segments) {
140
+ clockEvents.push({ clock: clock.name, trigger: clock.triggerDescription });
141
+ }
127
142
  }
128
143
  }
129
- }
130
-
131
- // Remove clock
132
- if (args.removeClockName) {
133
- state.clocks = state.clocks.filter((c) => c.name !== args.removeClockName);
134
- }
135
144
 
136
- // Story arc
137
- if (args.advanceAct && state.storyBlueprint) {
138
- state.storyBlueprint.currentAct = Math.min(
139
- state.storyBlueprint.acts.length,
140
- state.storyBlueprint.currentAct + 1,
141
- );
142
- }
143
- if (args.storyComplete && state.storyBlueprint) {
144
- state.storyBlueprint.storyComplete = true;
145
- }
145
+ // Remove clock
146
+ if (args.removeClockName) {
147
+ state.clocks = state.clocks.filter((c) => c.name !== args.removeClockName);
148
+ }
146
149
 
147
- // Session log
148
- if (args.logEntry) {
149
- pushCapped(
150
- state.sessionLog,
151
- {
152
- scene: state.sceneCount,
153
- summary: args.logEntry,
154
- location: state.currentLocation,
155
- },
156
- MAX_SESSION_LOG,
157
- );
158
- }
150
+ // Story arc
151
+ if (args.advanceAct && state.storyBlueprint) {
152
+ state.storyBlueprint.currentAct = Math.min(
153
+ state.storyBlueprint.acts.length,
154
+ state.storyBlueprint.currentAct + 1,
155
+ );
156
+ }
157
+ if (args.storyComplete && state.storyBlueprint) {
158
+ state.storyBlueprint.storyComplete = true;
159
+ }
159
160
 
160
- // Crisis check
161
- updateCrisisFlags(state);
161
+ // Session log
162
+ if (args.logEntry) {
163
+ pushCapped(
164
+ state.sessionLog,
165
+ {
166
+ scene: state.sceneCount,
167
+ summary: args.logEntry,
168
+ location: state.currentLocation,
169
+ },
170
+ MAX_SESSION_LOG,
171
+ );
172
+ }
162
173
 
163
- return {
164
- success: true,
165
- ...(warnings.length > 0 ? { warnings } : {}),
166
- clockEvents,
167
- ...stateSummary(state),
168
- };
169
- },
174
+ return {
175
+ success: true,
176
+ ...(warnings.length > 0 ? { warnings } : {}),
177
+ clockEvents,
178
+ ...stateSummary(state),
179
+ };
180
+ }),
181
+ sendFrom: (result) =>
182
+ result.gameOver ? { type: "DOWNED" as const } : { type: "SETTLED" as const },
170
183
  });