@alexkroman1/aai-cli 6.11.0 → 7.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 (162) hide show
  1. package/dist/{_agent-DpH2pBJd.mjs → _agent-BTwZJu4P.mjs} +2 -2
  2. package/dist/{_bundler-C2BDwU7r.mjs → _bundler-B4RqNF5Z.mjs} +2 -2
  3. package/dist/_dev-env.d.ts +22 -2
  4. package/dist/{_dev-server-LLLWnFBs.mjs → _dev-server-CiKFH9qw.mjs} +38 -47
  5. package/dist/_dev-server.d.ts +0 -17
  6. package/dist/{_init-C57bWAQ8.mjs → _init-CE4HKdgV.mjs} +3 -2
  7. package/dist/{_preflight-BtfaYtbE.mjs → _preflight-Co1G9Jww.mjs} +1 -1
  8. package/dist/_preflight.d.ts +1 -1
  9. package/dist/{_slug-api-BxrsJXVI.mjs → _slug-api-nRFaBEPJ.mjs} +2 -2
  10. package/dist/{_studio-U3jhKlA3.mjs → _studio-DR5yzZ1T.mjs} +2 -1
  11. package/dist/{_templates-Dxb_P2Wz.mjs → _templates-FI3xPEMj.mjs} +1 -1
  12. package/dist/_workflow-scan.d.ts +108 -0
  13. package/dist/{build-Dfza2pRI.mjs → build-6jfaakgc.mjs} +5 -2
  14. package/dist/cli.mjs +30 -25
  15. package/dist/{client-bundler-BEIqgOtd.mjs → client-bundler-CWnG42cU.mjs} +2 -1
  16. package/dist/client-bundler.mjs +1 -1
  17. package/dist/{delete-vbpjGzqs.mjs → delete-Dl_LSfvI.mjs} +3 -3
  18. package/dist/{deploy-B8lubiRT.mjs → deploy-DvYeD8ia.mjs} +5 -5
  19. package/dist/{dev-Uq5ujP8a.mjs → dev-B59JIfWO.mjs} +1 -1
  20. package/dist/{eject-31gjtaHF.mjs → eject-C9WJyyr2.mjs} +1 -1
  21. package/dist/{init-l6xfU4xX.mjs → init-CpJVjEg0.mjs} +3 -3
  22. package/dist/{login-C71-qz8F.mjs → login-BBuM1sxH.mjs} +3 -4
  23. package/dist/{logs-BWVGSjLR.mjs → logs-9q-psa4q.mjs} +2 -2
  24. package/dist/project-config.mjs +1 -1
  25. package/dist/scaffold/CLAUDE.md +40 -19
  26. package/dist/scaffold/package.json +5 -3
  27. package/dist/scaffold/server.mjs +1 -1
  28. package/dist/{secret-BuMuFR4B.mjs → secret-CVvSLIDV.mjs} +2 -2
  29. package/dist/{storage-DzRZ-eCw.mjs → storage-BvUrnvM3.mjs} +2 -2
  30. package/dist/{studio-BCNUpDgP.mjs → studio--MUV0cid.mjs} +4 -4
  31. package/dist/templates/call-audit/agent.test.ts +57 -204
  32. package/dist/templates/call-audit/agent.ts +32 -19
  33. package/dist/templates/call-audit/client.tsx +17 -54
  34. package/dist/templates/call-audit/workflows/audit.ts +11 -18
  35. package/dist/templates/call-audit/workflows/ingest.ts +106 -114
  36. package/dist/templates/call-audit/workflows/media.ts +2 -12
  37. package/dist/templates/call-audit/workflows/summarize.ts +47 -52
  38. package/dist/templates/call-audit/workflows/sync-api.ts +7 -7
  39. package/dist/templates/dispatch-center/agent.test.ts +149 -25
  40. package/dist/templates/dispatch-center/client.tsx +239 -129
  41. package/dist/templates/dispatch-center/shared.ts +99 -1
  42. package/dist/templates/dispatch-center/system-prompt.md +3 -1
  43. package/dist/templates/dispatch-center/tools/incident_add_note.ts +16 -13
  44. package/dist/templates/dispatch-center/tools/incident_create.ts +19 -1
  45. package/dist/templates/dispatch-center/tools/incident_escalate.ts +68 -59
  46. package/dist/templates/dispatch-center/tools/incident_triage.ts +57 -43
  47. package/dist/templates/dispatch-center/tools/incident_update_status.ts +49 -40
  48. package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +8 -2
  49. package/dist/templates/dispatch-center/tools/resources_dispatch.ts +69 -52
  50. package/dist/templates/dispatch-center/tools/resources_update_status.ts +45 -41
  51. package/dist/templates/embedded-assets/agent.test.ts +17 -14
  52. package/dist/templates/health-assistant/agent.test.ts +22 -4
  53. package/dist/templates/infocom-adventure/agent.test.ts +4 -6
  54. package/dist/templates/infocom-adventure/client.tsx +246 -164
  55. package/dist/templates/link-digest/agent.test.ts +24 -19
  56. package/dist/templates/link-digest/client.tsx +47 -61
  57. package/dist/templates/link-digest/workflows/digest.ts +19 -31
  58. package/dist/templates/night-owl/agent.test.ts +70 -19
  59. package/dist/templates/night-owl/agent.ts +5 -0
  60. package/dist/templates/night-owl/client.tsx +56 -56
  61. package/dist/templates/night-owl/shared.ts +24 -0
  62. package/dist/templates/night-owl/tools/recommend.ts +18 -6
  63. package/dist/templates/pizza-ordering/agent.test.ts +27 -27
  64. package/dist/templates/pizza-ordering/client.tsx +9 -26
  65. package/dist/templates/plan-and-execute/agent.test.ts +99 -58
  66. package/dist/templates/plan-and-execute/agent.ts +1 -1
  67. package/dist/templates/plan-and-execute/client.tsx +12 -15
  68. package/dist/templates/plan-and-execute/shared.ts +71 -2
  69. package/dist/templates/plan-and-execute/tools/plan_status.ts +18 -6
  70. package/dist/templates/plan-and-execute/tools/revise_plan.ts +18 -10
  71. package/dist/templates/plan-and-execute/tools/start_plan.ts +13 -2
  72. package/dist/templates/plan-and-execute/tools/work_next_step.ts +70 -22
  73. package/dist/templates/podcast-digest/agent.test.ts +746 -0
  74. package/dist/templates/podcast-digest/agent.ts +139 -0
  75. package/dist/templates/podcast-digest/client.tsx +154 -0
  76. package/dist/templates/podcast-digest/workflows/digest.ts +411 -0
  77. package/dist/templates/podcast-digest/workflows/feeds.ts +507 -0
  78. package/dist/templates/podcast-digest/workflows/slack.ts +209 -0
  79. package/dist/templates/recap-workflow/agent.test.ts +97 -94
  80. package/dist/templates/recap-workflow/tools/recap_progress.ts +9 -12
  81. package/dist/templates/recap-workflow/tools/recap_status.ts +8 -9
  82. package/dist/templates/recap-workflow/tools/request_recap.ts +2 -1
  83. package/dist/templates/recap-workflow/workflows/recap.ts +36 -40
  84. package/dist/templates/redline/agent.test.ts +17 -15
  85. package/dist/templates/redline/client.tsx +12 -12
  86. package/dist/templates/redline/workflows/redline.ts +19 -31
  87. package/dist/templates/research-workflow/agent.test.ts +60 -59
  88. package/dist/templates/research-workflow/tools/research_progress.ts +9 -12
  89. package/dist/templates/research-workflow/tools/research_status.ts +9 -11
  90. package/dist/templates/research-workflow/workflows/research.ts +44 -61
  91. package/dist/templates/retail/agent.test.ts +26 -23
  92. package/dist/templates/retail/client.tsx +226 -117
  93. package/dist/templates/retail/registry.test.ts +38 -6
  94. package/dist/templates/retail/store.test.ts +82 -15
  95. package/dist/templates/retail/store.ts +174 -47
  96. package/dist/templates/retail/system-prompt.md +11 -2
  97. package/dist/templates/retail/tools/cancel_pending_order.ts +2 -2
  98. package/dist/templates/retail/tools/exchange_delivered_order_items.ts +2 -2
  99. package/dist/templates/retail/tools/find_user_id_by_email.ts +8 -5
  100. package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +4 -5
  101. package/dist/templates/retail/tools/get_item_details.ts +3 -4
  102. package/dist/templates/retail/tools/get_order_details.ts +3 -4
  103. package/dist/templates/retail/tools/get_product_details.ts +3 -4
  104. package/dist/templates/retail/tools/get_user_details.ts +2 -2
  105. package/dist/templates/retail/tools/list_all_product_types.ts +5 -5
  106. package/dist/templates/retail/tools/modify_pending_order_address.ts +2 -2
  107. package/dist/templates/retail/tools/modify_pending_order_items.ts +2 -3
  108. package/dist/templates/retail/tools/modify_pending_order_payment.ts +2 -2
  109. package/dist/templates/retail/tools/modify_user_address.ts +2 -2
  110. package/dist/templates/retail/tools/return_delivered_order_items.ts +2 -2
  111. package/dist/templates/retail/tools/transfer_to_human_agents.ts +8 -4
  112. package/dist/templates/solo-rpg/agent.test.ts +227 -48
  113. package/dist/templates/solo-rpg/client.tsx +38 -37
  114. package/dist/templates/solo-rpg/shared.ts +145 -19
  115. package/dist/templates/solo-rpg/system-prompt.md +3 -2
  116. package/dist/templates/solo-rpg/tools/action_roll.ts +82 -63
  117. package/dist/templates/solo-rpg/tools/burn_momentum.ts +85 -50
  118. package/dist/templates/solo-rpg/tools/check_state.ts +24 -7
  119. package/dist/templates/solo-rpg/tools/load_game.ts +13 -1
  120. package/dist/templates/solo-rpg/tools/save_game.ts +16 -7
  121. package/dist/templates/solo-rpg/tools/setup_character.ts +22 -2
  122. package/dist/templates/solo-rpg/tools/update_state.ts +113 -100
  123. package/dist/templates/spoken-summary/agent.test.ts +68 -97
  124. package/dist/templates/spoken-summary/agent.ts +30 -17
  125. package/dist/templates/spoken-summary/client.tsx +10 -50
  126. package/dist/templates/spoken-summary/workflows/summarize.ts +17 -22
  127. package/dist/templates/spoken-summary/workflows/transcribe.ts +19 -26
  128. package/dist/templates/support-line/agent.test.ts +11 -16
  129. package/dist/templates/support-line/agent.ts +1 -1
  130. package/dist/templates/support-line/client.tsx +9 -9
  131. package/dist/templates/support-line/nodes.ts +100 -0
  132. package/dist/templates/support-line/procedure.ts +407 -0
  133. package/dist/templates/support-line/tools/answer_question.ts +17 -4
  134. package/dist/templates/transcription-workflow/agent.test.ts +94 -194
  135. package/dist/templates/transcription-workflow/agent.ts +1 -1
  136. package/dist/templates/transcription-workflow/client.tsx +17 -42
  137. package/dist/templates/transcription-workflow/workflows/batch.ts +19 -22
  138. package/dist/templates/transcription-workflow/workflows/normalize.ts +90 -186
  139. package/dist/templates/transcription-workflow/workflows/stitch.ts +0 -11
  140. package/dist/templates/transcription-workflow/workflows/stream.ts +4 -5
  141. package/dist/templates/transcription-workflow/workflows/sync-api.ts +6 -6
  142. package/dist/templates/transcription-workflow/workflows/transcribe.ts +32 -26
  143. package/dist/templates/transcription-workflow/workflows/wav.ts +0 -31
  144. package/dist/templates/travel-concierge/agent.test.ts +64 -33
  145. package/dist/templates/travel-concierge/client.tsx +11 -23
  146. package/dist/templates/travel-concierge/routing.ts +34 -15
  147. package/dist/templates/travel-concierge/shared.ts +70 -3
  148. package/dist/templates/travel-concierge/tools/book_car_rental.ts +2 -2
  149. package/dist/templates/travel-concierge/tools/book_excursion.ts +2 -2
  150. package/dist/templates/travel-concierge/tools/book_hotel.ts +2 -2
  151. package/dist/templates/travel-concierge/tools/cancel_ticket.ts +2 -2
  152. package/dist/templates/travel-concierge/tools/update_ticket.ts +2 -2
  153. package/dist/{worker-bundler-CGD4r8Kc.mjs → worker-bundler-COxnqstQ.mjs} +221 -3
  154. package/dist/worker-bundler.mjs +1 -1
  155. package/dist/{workflow-CFpxOFfQ.mjs → workflow-D2AQf2Pl.mjs} +28 -1
  156. package/dist/workflow-bundler.d.ts +6 -1
  157. package/dist/workflow.d.ts +1 -1
  158. package/package.json +5 -4
  159. package/dist/templates/call-audit/workflows/temp-media.ts +0 -138
  160. package/dist/templates/support-line/graph.ts +0 -224
  161. package/dist/{_config-D_s09e7g.mjs → _config-CmJOFsAP.mjs} +1 -1
  162. /package/dist/templates/plan-and-execute/{graph.ts → procedure.ts} +0 -0
@@ -1,10 +1,226 @@
1
1
  #!/usr/bin/env node
2
2
  import { i as errorCode } from "./_utils-B8QmtFhK.mjs";
3
3
  import { n as withPreservedNodeEnv } from "./_vite-env-BNveawd1.mjs";
4
+ import { builtinModules } from "node:module";
4
5
  import path from "node:path";
5
6
  import fs from "node:fs/promises";
6
7
  import { build } from "vite";
7
8
  import { BaseBuilder, applySwcTransform, detectWorkflowPatterns, shouldTransformFile } from "@workflow/builders";
9
+ //#region _workflow-scan.ts
10
+ /**
11
+ * What the BUILT flow bundle carries — the two scans that read it, and the
12
+ * checks over them.
13
+ *
14
+ * Split out of `workflow-bundler.ts` when that file crossed the 500-line cap,
15
+ * along the seam the two scans already share: both read the same artifact after
16
+ * the builder has written it, both attribute a line to a module through
17
+ * esbuild's `// <path>` headers, and neither has anything to do with
18
+ * CONFIGURING the build. One of them fails the build (a `require` the workflow
19
+ * VM cannot answer) and the other warns (a call that replays differently), which
20
+ * is the only real difference between them.
21
+ *
22
+ * Internal: `workflow-bundler.ts` is the surface the studio and the CLI's own
23
+ * build call, and it re-exports nothing from here that they need.
24
+ *
25
+ * @module _workflow-scan
26
+ */
27
+ /**
28
+ * Every Node builtin, in both spellings esbuild can emit for one.
29
+ *
30
+ * `node:child_process` and bare `child_process` are the same module and the
31
+ * bundle may name it either way — a bare name only reaches the output when the
32
+ * source imported it bare, which npm is still full of.
33
+ */
34
+ const RUNTIME_MODULES = /* @__PURE__ */ new Set([...builtinModules, ...builtinModules.map((name) => `node:${name}`)]);
35
+ /**
36
+ * A `require(…)` CALL, excluding esbuild's own `__require` shim.
37
+ *
38
+ * The lookbehind is what separates the two: `__require` is the shim esbuild
39
+ * writes for a bundled CJS module's dynamic requires, and the STEP bundle
40
+ * defines a real `require` for it (see {@link STEP_REQUIRE_SHIM}). A bare
41
+ * `require` in the FLOW bundle is the different thing this scan is for.
42
+ */
43
+ const REQUIRE_CALL = /(?<![\w$.])require\(\s*"([^"]+)"\s*\)/g;
44
+ /** esbuild's per-module header — `// node_modules/pkg/index.js`, and nothing else. */
45
+ const MODULE_COMMENT = /^\/\/ (\S+\.[cm]?[jt]sx?)$/;
46
+ /**
47
+ * Find the Node builtins a flow bundle would `require` at load.
48
+ *
49
+ * The flow bundle is compiled in a `node:vm` `Script` whose context has
50
+ * `module` and `exports` and **no `require`**, so one of these is a run that
51
+ * dies at replay with `ReferenceError: require is not defined` — never a build
52
+ * failure, and never a symptom before the first run. The WDK's own builder
53
+ * bundles everything for exactly this reason and carries
54
+ * `createNodeModuleErrorPlugin` to reject a builtin import at build time.
55
+ *
56
+ * That plugin has two blind spots this scan covers, and both are the DEPLOYED
57
+ * shape rather than an exotic one:
58
+ *
59
+ * - It reports a violation only when it can point at the import LINE in a
60
+ * first-party file, matched with a single-line regex — so a multi-line
61
+ * `import {\n x,\n} from "pkg"` finds nothing and the builtin is marked
62
+ * external in silence.
63
+ * - It resolves that file against `process.cwd()`, which is not the project
64
+ * being built when the studio builds a workspace, so the read fails and the
65
+ * same silent path is taken.
66
+ *
67
+ * Both were reproduced. What reaches the VM either way is
68
+ * `var import_node_child_process = require("node:child_process");` at the top
69
+ * of the bundle, i.e. every run of every workflow in the project fails, and the
70
+ * stack names a line of generated code inside a dependency.
71
+ *
72
+ * Restricted to builtin specifiers deliberately: those are the only ones this
73
+ * builder leaves external (it marks nothing else so, precisely so nothing can
74
+ * need a `require`), and a narrow set is what keeps the scan from reading the
75
+ * text of a prompt as a violation.
76
+ *
77
+ * @internal
78
+ */
79
+ function findVmRequires(workflowCode) {
80
+ const found = [];
81
+ const seen = /* @__PURE__ */ new Set();
82
+ let module;
83
+ for (const line of workflowCode.split("\n")) {
84
+ const header = MODULE_COMMENT.exec(line.trim());
85
+ if (header) {
86
+ module = header[1];
87
+ continue;
88
+ }
89
+ for (const [, specifier] of line.matchAll(REQUIRE_CALL)) {
90
+ if (specifier === void 0 || !RUNTIME_MODULES.has(specifier)) continue;
91
+ const key = `${specifier}\u0000${module ?? ""}`;
92
+ if (seen.has(key)) continue;
93
+ seen.add(key);
94
+ found.push({
95
+ specifier,
96
+ module
97
+ });
98
+ }
99
+ }
100
+ return found;
101
+ }
102
+ /**
103
+ * Calls whose answer differs between a run and its replays, and what to say
104
+ * about each.
105
+ *
106
+ * **A workflow body REPLAYS from the top on every resume** — after a `sleep`,
107
+ * after a redeploy, after the container was reclaimed — and only a step's
108
+ * result is journaled. So a body that reads the clock gets a different time on
109
+ * every pass, and a body that fetches performs the request again, both silently:
110
+ * the run completes, and its output is built from values that disagree with the
111
+ * ones the earlier passes saw. The scaffold guide has always carried this rule
112
+ * with the words "all of which fail silently if broken", and nothing checked it.
113
+ */
114
+ const REPLAY_UNSAFE = [
115
+ {
116
+ re: /(?<![\w$.])Date\.now\s*\(/g,
117
+ fix: "reads a different clock on every replay — take the time in a `\"use step\"` body, whose result is journaled"
118
+ },
119
+ {
120
+ re: /(?<![\w$.])new Date\s*\(\s*\)/g,
121
+ fix: "reads a different clock on every replay — take the time in a `\"use step\"` body, whose result is journaled"
122
+ },
123
+ {
124
+ re: /(?<![\w$.])Math\.random\s*\(/g,
125
+ fix: "draws a different number on every replay — draw it in a `\"use step\"` body"
126
+ },
127
+ {
128
+ re: /(?<![\w$.])crypto\.randomUUID\s*\(/g,
129
+ fix: "mints a different id on every replay — mint it in a `\"use step\"` body"
130
+ },
131
+ {
132
+ re: /(?<![\w$.])fetch\s*\(/g,
133
+ fix: "runs again on every replay, and the VM has no fetch to run it with — call `stepFetch` from a `\"use step\"` body"
134
+ }
135
+ ];
136
+ /**
137
+ * Replay-unsafe calls the flow bundle carries, attributed to the project's OWN
138
+ * `workflows/` files.
139
+ *
140
+ * Attribution is the whole design. The bundle inlines every non-external
141
+ * dependency a workflow module imports — zod, a markdown parser, whatever — and
142
+ * third-party code is full of `Date.now()` on paths a workflow never reaches, so
143
+ * a scan of the bundle's text reports a library and blocks a correct project.
144
+ * esbuild writes a `// <path>` header per module (the same one
145
+ * {@link findVmRequires} reads), so lines can be charged to the file they were
146
+ * written in, and only the project's own workflow sources are read.
147
+ *
148
+ * Scanning the BUNDLE rather than the sources is what makes a `"use step"` body
149
+ * exempt for free: the workflow-mode transform has already removed them, so
150
+ * what is left is the part that really does replay.
151
+ *
152
+ * @internal
153
+ */
154
+ function findReplayUnsafeCalls(workflowCode) {
155
+ const found = [];
156
+ const seen = /* @__PURE__ */ new Set();
157
+ let module;
158
+ for (const line of workflowCode.split("\n")) {
159
+ const header = MODULE_COMMENT.exec(line.trim());
160
+ if (header) {
161
+ module = header[1];
162
+ continue;
163
+ }
164
+ if (module === void 0 || !isProjectWorkflowModule(module)) continue;
165
+ for (const { re, fix } of REPLAY_UNSAFE) for (const [call] of line.matchAll(re)) {
166
+ const key = `${call}\u0000${module}`;
167
+ if (seen.has(key)) continue;
168
+ seen.add(key);
169
+ found.push({
170
+ call,
171
+ fix,
172
+ module
173
+ });
174
+ }
175
+ }
176
+ return found;
177
+ }
178
+ /**
179
+ * Is this bundled module one of the project's own `workflows/` files?
180
+ *
181
+ * A dependency's path runs through `node_modules/`, which is excluded first so
182
+ * a package that happens to live in a directory called `workflows` cannot be
183
+ * read as the project's.
184
+ */
185
+ function isProjectWorkflowModule(module) {
186
+ const posix = module.split(path.sep).join("/");
187
+ return !posix.includes("node_modules/") && /(?:^|\/)workflows\//.test(posix);
188
+ }
189
+ /**
190
+ * The warning `aai build` and `aai dev` print for a replay-unsafe call.
191
+ *
192
+ * A WARNING and not a build failure, deliberately. The attribution above makes
193
+ * the scan accurate about which FILE a call is in, and it cannot know whether a
194
+ * plain function in a `workflows/` module is reached from a body (where the
195
+ * rule bites) or only from a step (where it does not) — so the one thing it
196
+ * must not do is refuse a correct project. A silent build was the actual
197
+ * problem; a line naming the file solves it without that risk.
198
+ */
199
+ function replayWarnings(workflowCode) {
200
+ return findReplayUnsafeCalls(workflowCode).map(({ call, fix, module }) => `${module}: \`${call}…\` ${fix}.`);
201
+ }
202
+ /**
203
+ * Fail the build when the flow bundle carries a `require` — see
204
+ * {@link findVmRequires} for what that means and why nothing upstream catches it.
205
+ *
206
+ * The message has to name the MODULE as well as the specifier, because the
207
+ * import that caused it is not in the file an author is looking at: only a
208
+ * `"use step"` body is stripped from this bundle, so a value a `workflows/`
209
+ * module holds at module scope — an exported helper, a constant — keeps its
210
+ * import, and that import's whole graph rides into the VM.
211
+ */
212
+ function assertNoVmRequires(workflowCode) {
213
+ const sites = findVmRequires(workflowCode);
214
+ if (sites.length === 0) return;
215
+ const lines = sites.map(({ specifier, module }) => ` ${specifier}${module === void 0 ? "" : ` — from ${module}`}`);
216
+ throw new Error([
217
+ `This project's workflows cannot run: the workflow bundle requires ${sites.length === 1 ? "a Node module" : "Node modules"} that the workflow VM has no \`require\` for.`,
218
+ ...lines,
219
+ "",
220
+ "Only a `\"use step\"` body is removed from this bundle, so anything a `workflows/` module holds at MODULE scope keeps its import — including an exported helper that a step body is the only caller of. Move that use inside the step body, or into a module only a step body imports."
221
+ ].join("\n"));
222
+ }
223
+ //#endregion
8
224
  //#region workflow-bundler.ts
9
225
  /**
10
226
  * Workflow bundling — turning a project's `workflows/` directory into the two
@@ -37,7 +253,7 @@ import { BaseBuilder, applySwcTransform, detectWorkflowPatterns, shouldTransform
37
253
  * guest's BAKED image instead of being inlined. Step bundle: 12 MB → ~7 KB.
38
254
  * - **`bundleFinalOutput: false`** — skips wrapping the workflow-mode code in a
39
255
  * runtime host, which is exactly what `workflowEntrypoint(code)` supplies
40
- * itself at the other end. Flow bundle: 3.7 MB → ~69 KB.
256
+ * itself at the other end. Dialog bundle: 3.7 MB → ~69 KB.
41
257
  *
42
258
  * So `workflowCode` is passed to `workflowEntrypoint()` in the guest and
43
259
  * `stepCode` is evaluated there to register its step functions.
@@ -189,11 +405,13 @@ var AaiWorkflowBuilder = class extends BaseBuilder {
189
405
  outfile: this.stepFile
190
406
  });
191
407
  const [workflowCode, stepCode] = await Promise.all([fs.readFile(this.flowFile, "utf-8"), fs.readFile(this.stepFile, "utf-8")]);
408
+ assertNoVmRequires(workflowCode);
192
409
  this.output = {
193
410
  workflowCode,
194
411
  stepCode: STEP_REQUIRE_SHIM + stepCode,
195
412
  manifest,
196
- inputFiles
413
+ inputFiles,
414
+ warnings: replayWarnings(workflowCode)
197
415
  };
198
416
  }
199
417
  };
@@ -325,7 +543,7 @@ function wrapperEntrySource(runtime, workflows, toolFiles, systemPromptFile) {
325
543
  const toolEntries = toolFiles.map((file, i) => ` ${JSON.stringify(`tools/${file}`)}: __aaiTool${i},`).join("\n");
326
544
  return `import def from "../agent.ts";
327
545
  import { agentToolsToSchemas, toAgentConfig, toolRegistry, withSystemPrompt, withTools } from "@alexkroman1/aai/manifest";
328
- ${runtime ? `import { createRuntime } from "@alexkroman1/aai/runtime";` : ""}
546
+ ${runtime ? `import { createRuntime } from "@alexkroman1/aai-runtime";` : ""}
329
547
  ${systemPromptFile ? `import __aaiSystemPrompt from "../${SYSTEM_PROMPT_FILE}?raw";` : ""}
330
548
  ${toolImports}
331
549
  // A tool's name is its file name. The map is built here rather than written in
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { t as buildWorker } from "./worker-bundler-CGD4r8Kc.mjs";
2
+ import { t as buildWorker } from "./worker-bundler-COxnqstQ.mjs";
3
3
  export { buildWorker };
@@ -1,10 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
3
3
  import { n as log } from "./_ui-u7T4YooX.mjs";
4
- import { n as getServerInfo } from "./_agent-DpH2pBJd.mjs";
4
+ import { n as getServerInfo } from "./_agent-BTwZJu4P.mjs";
5
5
  import { errorMessage, omitUndefined } from "@alexkroman1/aai/utils";
6
6
  import { createWorkflowApiClient } from "@alexkroman1/aai/workflow-api";
7
7
  //#region workflow.ts
8
+ /**
9
+ * `aai workflow` — reading and steering a deployed agent's durable runs from a
10
+ * terminal.
11
+ *
12
+ * A run outlives every surface that can show it: the studio's runs card is one
13
+ * project's, and a page holds only the id it started. Without this the only way
14
+ * to ask "what has this agent been doing" is to hand-build a `curl` against
15
+ * `/:slug/workflows`, which needs the platform origin and the PUBLISHED slug —
16
+ * neither of which is the project's name.
17
+ *
18
+ * **It talks to the platform's brokered route, unauthenticated by default**,
19
+ * which is the same posture the page has: that surface carries no credential
20
+ * unless the agent's operator set `AAI_WORKFLOW_API_TOKEN`, and `--token` is how
21
+ * a caller passes it. So this is deliberately NOT an `apiRequest` — the caller's
22
+ * API key is not what authorizes here, and sending it would put a platform
23
+ * credential on a route that does not want one.
24
+ *
25
+ * Every request BROKERS, so the first one may boot the agent's sandbox. That is
26
+ * the same trade the studio card makes and worth knowing before scripting a loop
27
+ * around it.
28
+ *
29
+ * **The requests are the SDK's** (`createWorkflowApiClient`,
30
+ * `@alexkroman1/aai/workflow-api`). What is left here is the two things that are
31
+ * genuinely the CLI's: turning "this directory" into an origin plus a published
32
+ * slug, and PRINTING — which is most of why the verbs exist separately from the
33
+ * client's methods.
34
+ */
8
35
  /** Runs listed when the caller names no limit — a terminal is not a dashboard. */
9
36
  const DEFAULT_RUN_LIMIT = 20;
10
37
  /**
@@ -29,7 +29,7 @@
29
29
  * guest's BAKED image instead of being inlined. Step bundle: 12 MB → ~7 KB.
30
30
  * - **`bundleFinalOutput: false`** — skips wrapping the workflow-mode code in a
31
31
  * runtime host, which is exactly what `workflowEntrypoint(code)` supplies
32
- * itself at the other end. Flow bundle: 3.7 MB → ~69 KB.
32
+ * itself at the other end. Dialog bundle: 3.7 MB → ~69 KB.
33
33
  *
34
34
  * So `workflowCode` is passed to `workflowEntrypoint()` in the guest and
35
35
  * `stepCode` is evaluated there to register its step functions.
@@ -108,6 +108,11 @@ export type WorkflowBundleOutput = {
108
108
  * section for why it is this list and not a content sniff.
109
109
  */
110
110
  inputFiles: readonly string[];
111
+ /**
112
+ * Replay-safety findings, as lines to print. See {@link replayWarnings} for
113
+ * why they are warnings rather than a failed build.
114
+ */
115
+ warnings: readonly string[];
111
116
  };
112
117
  /**
113
118
  * Attach the compiler's `workflowId`/`stepId` to the agent bundle's copy of
@@ -25,7 +25,7 @@
25
25
  * slug, and PRINTING — which is most of why the verbs exist separately from the
26
26
  * client's methods.
27
27
  */
28
- import type { WorkflowRunSnapshot, WorkflowSummary } from "@alexkroman1/aai";
28
+ import type { WorkflowRunSnapshot, WorkflowSummary } from "@alexkroman1/aai/workflow-api";
29
29
  import { type CommandResult } from "./_output.ts";
30
30
  /**
31
31
  * One run, as the API reports it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexkroman1/aai-cli",
3
- "version": "6.11.0",
3
+ "version": "7.0.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "aai": "bin.mjs"
@@ -44,8 +44,9 @@
44
44
  "p-timeout": "^7.0.1",
45
45
  "vite": "^8.2.1",
46
46
  "zod": "^4.4.3",
47
- "@alexkroman1/aai": "6.11.0",
48
- "@alexkroman1/aai-ui": "6.11.0"
47
+ "@alexkroman1/aai-ui": "7.0.0",
48
+ "@alexkroman1/aai": "7.0.0",
49
+ "@alexkroman1/aai-runtime": "7.0.0"
49
50
  },
50
51
  "devDependencies": {
51
52
  "playwright": "^1.62.1",
@@ -53,7 +54,7 @@
53
54
  "verdaccio": "^6.9.2",
54
55
  "vitest": "^4.1.10",
55
56
  "workflow": "4.8.2",
56
- "aai-templates": "0.3.6"
57
+ "aai-templates": "0.3.7"
57
58
  },
58
59
  "peerDependencies": {
59
60
  "vitest": "^4.1.10"
@@ -1,138 +0,0 @@
1
- // Copyright 2026 the AAI authors. MIT license.
2
- /**
3
- * Moving bytes between the upload store and a local file, which is what an
4
- * ffmpeg step spends most of its lines on.
5
- *
6
- * No directive, so it sits under `workflows/` untransformed and is called FROM
7
- * steps, inheriting their environment. It exists because both ffmpeg steps in
8
- * this template need the same three things and the third one is the one that is
9
- * easy to get wrong.
10
- *
11
- * ## Why a temp file at all
12
- *
13
- * `@alexkroman1/aai/ffmpeg` takes bytes as happily as a path, and for a short
14
- * clip bytes are the better call. This desk uses paths, for two reasons that are
15
- * both properties of real recordings rather than preferences:
16
- *
17
- * - **A pipe cannot seek.** An `.m4a` off a phone usually carries its `moov`
18
- * index at the END of the file, so ffmpeg reading it from `pipe:0` fails with
19
- * `moov atom not found`. That is the flagship input.
20
- * - **Piped output is capped**, at `DEFAULT_MAX_FFMPEG_OUTPUT_BYTES` (64 MiB),
21
- * which is about half an hour of this desk's 16 kHz mono PCM. The desk exists
22
- * for the two-hour call.
23
- *
24
- * ## A temp file may not outlive its step
25
- *
26
- * A step is journaled by its RETURN VALUE and may be dispatched into a different
27
- * process than its neighbours, so a path in a return value is a path that is
28
- * replayed after the file behind it is gone — and the failure mode is a resumed
29
- * run reading a directory that another run is using. {@link withTempDir} makes
30
- * the lifetime a lexical scope: the directory is created on entry, removed on
31
- * exit, and what crosses the step boundary is an upload id.
32
- */
33
-
34
- import { mkdtemp, open, rm } from "node:fs/promises";
35
- import { tmpdir } from "node:os";
36
- import { join } from "node:path";
37
- import { readUpload } from "@alexkroman1/aai/utils";
38
-
39
- /**
40
- * Bytes moved per `readUpload`, and per write.
41
- *
42
- * Large enough that a two-hour recording is a few hundred round trips rather
43
- * than tens of thousands, and small enough that a step's resident set is a
44
- * constant rather than a function of the recording. The number this must NOT be
45
- * is "the whole file", which is the shape every first draft has.
46
- */
47
- export const WINDOW_BYTES = 8 * 1024 * 1024;
48
-
49
- /**
50
- * Run `work` with a private temp directory, and remove it afterwards.
51
- *
52
- * `join(tmpdir(), …)` rather than a `/tmp` literal, which is this repo's rule
53
- * (`guard-invariants` rule 11) and not merely portability theatre: on Windows a
54
- * literal `/tmp/x` is DRIVE-RELATIVE, so it resolves somewhere that does not
55
- * exist and every write fails with ENOENT. A step runs in a Linux guest when it
56
- * is deployed and on the developer's own machine under `aai dev`, which is the
57
- * half that makes it matter.
58
- *
59
- * The removal is in a `finally`, so it also runs on the failure paths — a guest's
60
- * disk is small, and a step that leaves a copy of every recording it touched
61
- * fills it. `force` so a run that never created its output does not fail HERE and
62
- * replace the real error with this one.
63
- */
64
- export async function withTempDir<T>(work: (dir: string) => Promise<T>): Promise<T> {
65
- const dir = await mkdtemp(join(tmpdir(), "aai-call-audit-"));
66
- try {
67
- return await work(dir);
68
- } finally {
69
- await rm(dir, { recursive: true, force: true });
70
- }
71
- }
72
-
73
- /**
74
- * Write an upload to a local path, a window at a time.
75
- *
76
- * A `for` loop rather than a fan-out deliberately: the bytes land in one file at
77
- * one offset each, so concurrency buys nothing and costs exactly the memory the
78
- * windows are here to bound.
79
- *
80
- * `windowBytes` defaults to {@link WINDOW_BYTES}; see {@link fileChunks} for why
81
- * it is a parameter at all.
82
- */
83
- export async function materializeUpload(
84
- uploadId: string,
85
- size: number,
86
- path: string,
87
- windowBytes: number = WINDOW_BYTES,
88
- ): Promise<void> {
89
- const handle = await open(path, "w");
90
- try {
91
- for (let at = 0; at < size; at += windowBytes) {
92
- const slice = await readUpload(uploadId, {
93
- start: at,
94
- end: Math.min(at + windowBytes, size),
95
- });
96
- await handle.write(slice.bytes);
97
- }
98
- } finally {
99
- await handle.close();
100
- }
101
- }
102
-
103
- /**
104
- * A local file as the stream `writeUpload` takes.
105
- *
106
- * A generator rather than `readFile`, for the same reason the windows above
107
- * exist: the normalized PCM is the largest thing this desk touches, and handing
108
- * the store an `AsyncIterable` is what keeps it off the heap.
109
- *
110
- * **The `.slice()` is load-bearing.** One buffer is reused across reads, so
111
- * yielding a view of it hands the consumer memory the next read overwrites — a
112
- * bug whose symptom is a stored file made of the LAST chunk repeated, and which
113
- * does not reproduce whenever the consumer happens to copy before the next
114
- * iteration.
115
- *
116
- * `windowBytes` is a parameter with a default for exactly that reason, and it is
117
- * the one testability seam in this template. The aliasing bug above only manifests
118
- * across MULTIPLE reads, so at the real 8 MiB window a spec would have to write a
119
- * 16 MB file to reach it — and a first draft of that spec used 200 KB, passed with
120
- * the `.slice()` deleted, and would have shipped a test proving nothing. A small
121
- * window makes the multi-chunk path a few kilobytes instead.
122
- */
123
- export async function* fileChunks(
124
- path: string,
125
- windowBytes: number = WINDOW_BYTES,
126
- ): AsyncIterable<Uint8Array> {
127
- const handle = await open(path, "r");
128
- try {
129
- const buffer = new Uint8Array(windowBytes);
130
- for (;;) {
131
- const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
132
- if (bytesRead === 0) return;
133
- yield buffer.subarray(0, bytesRead).slice();
134
- }
135
- } finally {
136
- await handle.close();
137
- }
138
- }