@alexkroman1/aai-cli 5.13.2 → 6.1.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 (236) hide show
  1. package/dist/{_agent-C8Jk-ZcP.mjs → _agent-vGW5SBnX.mjs} +23 -5
  2. package/dist/_agent.d.ts +18 -0
  3. package/dist/{_api-client-BX8Qv51n.mjs → _api-client-LgLksMhN.mjs} +45 -8
  4. package/dist/_api-client.d.ts +39 -0
  5. package/dist/{_bundler-BuIyDdvs.mjs → _bundler-BapCUUJN.mjs} +42 -14
  6. package/dist/_bundler.d.ts +43 -5
  7. package/dist/_cli-common.d.ts +67 -3
  8. package/dist/{_config-CzwAWOWr.mjs → _config-D_s09e7g.mjs} +36 -18
  9. package/dist/_deploy.d.ts +2 -5
  10. package/dist/_dev-restart.d.ts +6 -1
  11. package/dist/{_dev-server-Ch7K8e8i.mjs → _dev-server-DQ1t9sbw.mjs} +94 -38
  12. package/dist/_dev-server.d.ts +30 -2
  13. package/dist/_e2e-test-utils.d.ts +80 -0
  14. package/dist/_fault-mode.d.ts +175 -0
  15. package/dist/{_init-CbkrE2wq.mjs → _init-CpISqoR8.mjs} +3 -3
  16. package/dist/{_output-CC300DzW.mjs → _output-CKkmWs7i.mjs} +8 -1
  17. package/dist/_output.d.ts +9 -2
  18. package/dist/{_server-common-61fL__39.mjs → _server-common-6e0QI4mq.mjs} +1 -1
  19. package/dist/{_slug-api-D30zPmsn.mjs → _slug-api-D2heILuC.mjs} +2 -2
  20. package/dist/{_studio-D2ciIhbi.mjs → _studio-zAJycZ8j.mjs} +3 -3
  21. package/dist/{_templates-B1kt3BGt.mjs → _templates-iDGjGiPf.mjs} +4 -6
  22. package/dist/{_typecheck-gate-4v9UBZHh.mjs → _typecheck-gate-BXvUNnfs.mjs} +1 -1
  23. package/dist/{_utils-0DHbcqUb.mjs → _utils-B8QmtFhK.mjs} +21 -7
  24. package/dist/_utils.d.ts +18 -2
  25. package/dist/{_vite-env-Dg_QlVv0.mjs → _vite-env-BNveawd1.mjs} +31 -1
  26. package/dist/_vite-env.d.ts +30 -0
  27. package/dist/build-BO1Ni6oJ.mjs +62 -0
  28. package/dist/build.d.ts +19 -0
  29. package/dist/cli-workflow.d.ts +10 -0
  30. package/dist/cli.d.ts +1 -2
  31. package/dist/cli.mjs +406 -224
  32. package/dist/{client-bundler-C9qXL8A2.mjs → client-bundler-B8Q9F8gJ.mjs} +3 -22
  33. package/dist/client-bundler.mjs +1 -1
  34. package/dist/{delete-DCRX3d96.mjs → delete-4I0uNR4a.mjs} +6 -7
  35. package/dist/delete.d.ts +2 -8
  36. package/dist/{deploy-DjsEV-40.mjs → deploy-DJ3HRcia.mjs} +13 -14
  37. package/dist/{dev-CvPH5dbF.mjs → dev-C6SWaAZP.mjs} +3 -3
  38. package/dist/{eject-LgBRfnbH.mjs → eject-Zrzk5KV3.mjs} +41 -15
  39. package/dist/eject.d.ts +30 -3
  40. package/dist/{init-BppxMR8r.mjs → init-BTRGiPQr.mjs} +61 -26
  41. package/dist/init.d.ts +11 -0
  42. package/dist/{login-BQQaxJhL.mjs → login-k0Z3VQu9.mjs} +5 -5
  43. package/dist/project-config.mjs +1 -1
  44. package/dist/scaffold/CLAUDE.md +498 -91
  45. package/dist/scaffold/package.json +11 -8
  46. package/dist/scaffold/pnpm-workspace.yaml +32 -0
  47. package/dist/scaffold/server.mjs +53 -57
  48. package/dist/{secret-Ds9dvqAj.mjs → secret-DzSQx1Ds.mjs} +6 -3
  49. package/dist/{storage-DbrQSHFk.mjs → storage-C-Do0mIA.mjs} +18 -6
  50. package/dist/{studio-GSFd4s5O.mjs → studio-BGVmCA2G.mjs} +10 -10
  51. package/dist/templates/code-interpreter/agent.ts +0 -2
  52. package/dist/templates/dispatch-center/agent.test.ts +21 -30
  53. package/dist/templates/dispatch-center/agent.ts +4 -31
  54. package/dist/templates/dispatch-center/client.tsx +41 -28
  55. package/dist/templates/dispatch-center/shared.ts +73 -66
  56. package/dist/templates/dispatch-center/tools/incident_add_note.ts +13 -15
  57. package/dist/templates/dispatch-center/tools/incident_create.ts +42 -45
  58. package/dist/templates/dispatch-center/tools/incident_escalate.ts +60 -62
  59. package/dist/templates/dispatch-center/tools/incident_get.ts +10 -6
  60. package/dist/templates/dispatch-center/tools/incident_triage.ts +44 -46
  61. package/dist/templates/dispatch-center/tools/incident_update_status.ts +40 -42
  62. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +3 -6
  63. package/dist/templates/dispatch-center/tools/ops_protocols.ts +1 -1
  64. package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +21 -24
  65. package/dist/templates/dispatch-center/tools/resources_dispatch.ts +53 -55
  66. package/dist/templates/dispatch-center/tools/resources_get_available.ts +3 -5
  67. package/dist/templates/dispatch-center/tools/resources_update_status.ts +40 -43
  68. package/dist/templates/embedded-assets/agent.test.ts +83 -0
  69. package/dist/templates/embedded-assets/agent.ts +1 -48
  70. package/dist/templates/embedded-assets/shared.ts +17 -0
  71. package/dist/templates/embedded-assets/system-prompt.md +13 -0
  72. package/dist/templates/embedded-assets/tools/list_topics.ts +9 -0
  73. package/dist/templates/embedded-assets/tools/search_knowledge.ts +26 -0
  74. package/dist/templates/health-assistant/agent.test.ts +169 -0
  75. package/dist/templates/health-assistant/agent.ts +1 -166
  76. package/dist/templates/health-assistant/fda.ts +100 -0
  77. package/dist/templates/health-assistant/tools/check_drug_interaction.ts +59 -0
  78. package/dist/templates/health-assistant/tools/medication_lookup.ts +31 -0
  79. package/dist/templates/infocom-adventure/agent.test.ts +205 -0
  80. package/dist/templates/infocom-adventure/agent.ts +3 -106
  81. package/dist/templates/infocom-adventure/client.tsx +32 -28
  82. package/dist/templates/infocom-adventure/shared.ts +36 -16
  83. package/dist/templates/infocom-adventure/tools/game_state_drop.ts +13 -0
  84. package/dist/templates/infocom-adventure/tools/game_state_flag.ts +13 -0
  85. package/dist/templates/infocom-adventure/tools/game_state_get.ts +16 -0
  86. package/dist/templates/infocom-adventure/tools/game_state_history.ts +14 -0
  87. package/dist/templates/infocom-adventure/tools/game_state_move.ts +14 -0
  88. package/dist/templates/infocom-adventure/tools/game_state_restart.ts +13 -0
  89. package/dist/templates/infocom-adventure/tools/game_state_score.ts +13 -0
  90. package/dist/templates/infocom-adventure/tools/game_state_take.ts +13 -0
  91. package/dist/templates/link-digest/agent.test.ts +223 -0
  92. package/dist/templates/link-digest/agent.ts +79 -0
  93. package/dist/templates/link-digest/client.tsx +146 -0
  94. package/dist/templates/link-digest/workflows/digest.ts +248 -0
  95. package/dist/templates/math-buddy/agent.ts +0 -2
  96. package/dist/templates/night-owl/agent.test.ts +80 -0
  97. package/dist/templates/night-owl/agent.ts +1 -68
  98. package/dist/templates/night-owl/system-prompt.md +10 -0
  99. package/dist/templates/night-owl/tools/recommend.ts +64 -0
  100. package/dist/templates/personal-finance/agent.ts +0 -2
  101. package/dist/templates/pizza-ordering/agent.test.ts +59 -58
  102. package/dist/templates/pizza-ordering/agent.ts +10 -159
  103. package/dist/templates/pizza-ordering/client.tsx +4 -4
  104. package/dist/templates/pizza-ordering/shared.ts +44 -27
  105. package/dist/templates/pizza-ordering/tools/add_pizza.ts +31 -0
  106. package/dist/templates/pizza-ordering/tools/place_order.ts +22 -0
  107. package/dist/templates/pizza-ordering/tools/remove_pizza.ts +22 -0
  108. package/dist/templates/pizza-ordering/tools/set_customer_name.ts +13 -0
  109. package/dist/templates/pizza-ordering/tools/update_pizza.ts +28 -0
  110. package/dist/templates/pizza-ordering/tools/view_order.ts +21 -0
  111. package/dist/templates/plan-and-execute/agent.test.ts +411 -0
  112. package/dist/templates/plan-and-execute/agent.ts +23 -0
  113. package/dist/templates/plan-and-execute/client.tsx +115 -0
  114. package/dist/templates/plan-and-execute/graph.ts +178 -0
  115. package/dist/templates/plan-and-execute/prompts.ts +110 -0
  116. package/dist/templates/plan-and-execute/shared.ts +161 -0
  117. package/dist/templates/plan-and-execute/system-prompt.md +18 -0
  118. package/dist/templates/plan-and-execute/tools/plan_status.ts +20 -0
  119. package/dist/templates/plan-and-execute/tools/revise_plan.ts +63 -0
  120. package/dist/templates/plan-and-execute/tools/start_plan.ts +51 -0
  121. package/dist/templates/plan-and-execute/tools/work_next_step.ts +101 -0
  122. package/dist/templates/recap-workflow/agent.test.ts +762 -0
  123. package/dist/templates/recap-workflow/agent.ts +95 -0
  124. package/dist/templates/recap-workflow/shared.ts +43 -0
  125. package/dist/templates/recap-workflow/tools/cancel_recap.ts +23 -0
  126. package/dist/templates/recap-workflow/tools/keep_transcript.ts +31 -0
  127. package/dist/templates/recap-workflow/tools/recap_progress.ts +27 -0
  128. package/dist/templates/recap-workflow/tools/recap_status.ts +46 -0
  129. package/dist/templates/recap-workflow/tools/request_recap.ts +43 -0
  130. package/dist/templates/recap-workflow/workflows/recap.ts +544 -0
  131. package/dist/templates/recap-workflow/workflows/tokens.ts +26 -0
  132. package/dist/templates/redline/agent.test.ts +222 -0
  133. package/dist/templates/redline/agent.ts +89 -0
  134. package/dist/templates/redline/client.tsx +184 -0
  135. package/dist/templates/redline/workflows/prompts.ts +68 -0
  136. package/dist/templates/redline/workflows/redline.ts +258 -0
  137. package/dist/templates/research-workflow/agent.test.ts +525 -0
  138. package/dist/templates/research-workflow/agent.ts +94 -0
  139. package/dist/templates/research-workflow/shared.ts +35 -0
  140. package/dist/templates/research-workflow/tools/file_it_now.ts +22 -0
  141. package/dist/templates/research-workflow/tools/request_research.ts +33 -0
  142. package/dist/templates/research-workflow/tools/research_progress.ts +26 -0
  143. package/dist/templates/research-workflow/tools/research_status.ts +40 -0
  144. package/dist/templates/research-workflow/workflows/prompts.ts +143 -0
  145. package/dist/templates/research-workflow/workflows/research.ts +558 -0
  146. package/dist/templates/retail/agent.test.ts +319 -256
  147. package/dist/templates/retail/agent.ts +6 -46
  148. package/dist/templates/retail/authenticate.ts +2 -2
  149. package/dist/templates/retail/client.tsx +45 -24
  150. package/dist/templates/retail/registry.test.ts +40 -25
  151. package/dist/templates/retail/resolve.test.ts +17 -16
  152. package/dist/templates/retail/resolve.ts +32 -67
  153. package/dist/templates/retail/shared.test.ts +21 -19
  154. package/dist/templates/retail/shared.ts +61 -23
  155. package/dist/templates/retail/store.test.ts +47 -52
  156. package/dist/templates/retail/store.ts +92 -79
  157. package/dist/templates/retail/swap.test.ts +20 -17
  158. package/dist/templates/retail/swap.ts +7 -14
  159. package/dist/templates/retail/tools/cancel_pending_order.ts +7 -11
  160. package/dist/templates/retail/tools/exchange_delivered_order_items.ts +26 -14
  161. package/dist/templates/retail/tools/find_user_id_by_email.ts +5 -9
  162. package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +5 -7
  163. package/dist/templates/retail/tools/get_item_details.ts +7 -8
  164. package/dist/templates/retail/tools/get_order_details.ts +7 -8
  165. package/dist/templates/retail/tools/get_product_details.ts +6 -8
  166. package/dist/templates/retail/tools/get_user_details.ts +6 -8
  167. package/dist/templates/retail/tools/list_all_product_types.ts +5 -7
  168. package/dist/templates/retail/tools/modify_pending_order_address.ts +6 -10
  169. package/dist/templates/retail/tools/modify_pending_order_items.ts +8 -20
  170. package/dist/templates/retail/tools/modify_pending_order_payment.ts +7 -13
  171. package/dist/templates/retail/tools/modify_user_address.ts +6 -10
  172. package/dist/templates/retail/tools/return_delivered_order_items.ts +7 -13
  173. package/dist/templates/retail/tools/transfer_to_human_agents.ts +1 -1
  174. package/dist/templates/solo-rpg/agent.test.ts +169 -54
  175. package/dist/templates/solo-rpg/agent.ts +4 -22
  176. package/dist/templates/solo-rpg/client.tsx +19 -6
  177. package/dist/templates/solo-rpg/shared.ts +20 -20
  178. package/dist/templates/solo-rpg/tools/action_roll.ts +3 -5
  179. package/dist/templates/solo-rpg/tools/burn_momentum.ts +3 -5
  180. package/dist/templates/solo-rpg/tools/check_state.ts +7 -7
  181. package/dist/templates/solo-rpg/tools/load_game.ts +3 -3
  182. package/dist/templates/solo-rpg/tools/oracle.ts +19 -12
  183. package/dist/templates/solo-rpg/tools/save_game.ts +11 -8
  184. package/dist/templates/solo-rpg/tools/setup_character.ts +3 -3
  185. package/dist/templates/solo-rpg/tools/update_state.ts +13 -13
  186. package/dist/templates/support-line/agent.test.ts +343 -0
  187. package/dist/templates/support-line/agent.ts +27 -0
  188. package/dist/templates/support-line/client.tsx +122 -0
  189. package/dist/templates/support-line/graph.ts +224 -0
  190. package/dist/templates/support-line/knowledge.json +65 -0
  191. package/dist/templates/support-line/prompts.ts +95 -0
  192. package/dist/templates/support-line/shared.ts +240 -0
  193. package/dist/templates/support-line/system-prompt.md +17 -0
  194. package/dist/templates/support-line/tools/answer_question.ts +72 -0
  195. package/dist/templates/support-line/tools/list_topics.ts +22 -0
  196. package/dist/templates/support-line/tools/log_ticket.ts +30 -0
  197. package/dist/templates/transcription-workflow/agent.test.ts +563 -0
  198. package/dist/templates/transcription-workflow/agent.ts +92 -0
  199. package/dist/templates/transcription-workflow/client.tsx +234 -0
  200. package/dist/templates/transcription-workflow/workflows/transcribe.ts +416 -0
  201. package/dist/templates/transcription-workflow/workflows/wav.ts +314 -0
  202. package/dist/templates/travel-concierge/agent.test.ts +338 -0
  203. package/dist/templates/travel-concierge/agent.ts +29 -0
  204. package/dist/templates/travel-concierge/client.tsx +161 -0
  205. package/dist/templates/travel-concierge/routing.ts +138 -0
  206. package/dist/templates/travel-concierge/shared.ts +547 -0
  207. package/dist/templates/travel-concierge/system-prompt.md +24 -0
  208. package/dist/templates/travel-concierge/tools/book_car_rental.ts +20 -0
  209. package/dist/templates/travel-concierge/tools/book_excursion.ts +18 -0
  210. package/dist/templates/travel-concierge/tools/book_hotel.ts +20 -0
  211. package/dist/templates/travel-concierge/tools/cancel_action.ts +7 -0
  212. package/dist/templates/travel-concierge/tools/cancel_ticket.ts +16 -0
  213. package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +7 -0
  214. package/dist/templates/travel-concierge/tools/confirm_action.ts +7 -0
  215. package/dist/templates/travel-concierge/tools/lookup_booking.ts +35 -0
  216. package/dist/templates/travel-concierge/tools/search_car_rentals.ts +33 -0
  217. package/dist/templates/travel-concierge/tools/search_excursions.ts +37 -0
  218. package/dist/templates/travel-concierge/tools/search_flights.ts +43 -0
  219. package/dist/templates/travel-concierge/tools/search_hotels.ts +36 -0
  220. package/dist/templates/travel-concierge/tools/to_car_rental_assistant.ts +11 -0
  221. package/dist/templates/travel-concierge/tools/to_excursion_assistant.ts +11 -0
  222. package/dist/templates/travel-concierge/tools/to_flight_assistant.ts +11 -0
  223. package/dist/templates/travel-concierge/tools/to_hotel_assistant.ts +11 -0
  224. package/dist/templates/travel-concierge/tools/update_ticket.ts +22 -0
  225. package/dist/templates/web-researcher/agent.ts +0 -2
  226. package/dist/templates/web-researcher/system-prompt.md +10 -0
  227. package/dist/{test-2LWBcXvY.mjs → test-B6ZBxSpk.mjs} +2 -2
  228. package/dist/typecheck.mjs +17 -15
  229. package/dist/worker-bundler-CIdDhZ0c.mjs +398 -0
  230. package/dist/worker-bundler.d.ts +14 -0
  231. package/dist/worker-bundler.mjs +1 -108
  232. package/dist/workflow-B-O97AqI.mjs +125 -0
  233. package/dist/workflow-bundler.d.ts +133 -0
  234. package/dist/workflow.d.ts +67 -0
  235. package/package.json +13 -9
  236. package/dist/build-DQJ1ZSm9.mjs +0 -39
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env node
2
+ import { i as errorCode } from "./_utils-B8QmtFhK.mjs";
3
+ import { n as withPreservedNodeEnv } from "./_vite-env-BNveawd1.mjs";
4
+ import path from "node:path";
5
+ import fs from "node:fs/promises";
6
+ import { build } from "vite";
7
+ import { BaseBuilder, applySwcTransform, detectWorkflowPatterns, shouldTransformFile } from "@workflow/builders";
8
+ //#region workflow-bundler.ts
9
+ /**
10
+ * Workflow bundling — turning a project's `workflows/` directory into the two
11
+ * small artifacts a guest needs to run durable workflows.
12
+ *
13
+ * Public (no `_` prefix) for the same reason as `worker-bundler.ts`: the studio
14
+ * builds its workspaces through this too, so a workflow published from the
15
+ * browser comes out of the same pass as one from `aai build`.
16
+ *
17
+ * @internal — build hook for aai-server/the studio; not a supported public API
18
+ * and not covered by semver.
19
+ *
20
+ * ## Why this exists at all
21
+ *
22
+ * The Workflow Development Kit's own integrations (Next, Nitro, SvelteKit) build
23
+ * workflow routes while building the SERVER, by scanning `workflows/` and
24
+ * emitting handler files. That model does not fit this platform: the guest image
25
+ * is baked once and then serves MANY tenants' agents, each arriving over
26
+ * `bundle/load` at runtime. There is no `workflows/` directory in existence when
27
+ * the harness is built. So the transform has to happen per tenant at deploy
28
+ * time — here — and the guest receives the result as data.
29
+ *
30
+ * ## The two artifacts, and why they are small
31
+ *
32
+ * A default `workflow build` emits a 3.7 MB flow bundle and a 12 MB step bundle,
33
+ * because each one inlines the whole WDK runtime. Neither could ride the guest's
34
+ * single-ESM-string delivery. Two settings fix that, and both are load-bearing:
35
+ *
36
+ * - **`externalPackages: ["workflow", …]`** — the WDK is resolved from the
37
+ * guest's BAKED image instead of being inlined. Step bundle: 12 MB → ~7 KB.
38
+ * - **`bundleFinalOutput: false`** — skips wrapping the workflow-mode code in a
39
+ * runtime host, which is exactly what `workflowEntrypoint(code)` supplies
40
+ * itself at the other end. Flow bundle: 3.7 MB → ~69 KB.
41
+ *
42
+ * So `workflowCode` is passed to `workflowEntrypoint()` in the guest and
43
+ * `stepCode` is evaluated there to register its step functions.
44
+ *
45
+ * ## The THIRD transform, which is the agent bundle's
46
+ *
47
+ * Those two are what the queue runs. The agent bundle needs a third —
48
+ * `applySwcTransform(…, "client")`, wired in as {@link workflowClientPlugin} —
49
+ * and without it the whole mechanism is inert: `ctx.workflows.start` reads the
50
+ * `workflowId` the compiler attaches to a directive body, and Vite bundling
51
+ * `workflows/research.ts` the ordinary way attaches nothing. The symptom is
52
+ * `MISSING_WORKFLOW_ID_MESSAGE` at the first `start()` — an agent that builds,
53
+ * deploys, boots and answers the phone, and cannot start a run.
54
+ *
55
+ * The plugin transforms EXACTLY the files the builder scanned (`inputFiles`),
56
+ * not "every file with a directive". Same list, same `moduleSpecifierRoot`,
57
+ * therefore the same ids by construction — a body outside `workflows/` cannot
58
+ * acquire an id that no flow bundle registered, which would trade a clear
59
+ * failure at `start()` for a run that enqueues and then fails at replay.
60
+ *
61
+ * ## Config details that are not optional
62
+ *
63
+ * Each of these cost real time to find, and none fails in a way that names
64
+ * itself:
65
+ *
66
+ * - `dirs` must carry the `./` prefix. A bare `"workflows"` crashes inside
67
+ * enhanced-resolve with `Cannot read properties of undefined (reading
68
+ * 'length')` from `join(undefined, …)`, nowhere near the cause.
69
+ * - Every bundle path must be ABSOLUTE, for the same reason.
70
+ * - `projectRoot` and `moduleSpecifierRoot` must be set explicitly; they do not
71
+ * default to `workingDir` on the path this takes.
72
+ * - `keepInterimBundleContext` must stay off — it leaves an esbuild watch context
73
+ * alive, so a one-shot build never exits.
74
+ */
75
+ /**
76
+ * The directory a project's `"use workflow"` bodies live in.
77
+ *
78
+ * The WDK builder's convention, not ours, and it is why a workflow body cannot
79
+ * live in `agent.ts`: only files under here are transformed. A body outside it
80
+ * runs inline, once, with no durability and nothing reporting that.
81
+ */
82
+ const WORKFLOWS_DIR = "workflows";
83
+ /**
84
+ * Packages left OUT of both bundles, resolved from the guest's baked image.
85
+ *
86
+ * `workflow` alone would nearly do it — its subpaths are what user code imports —
87
+ * but the `@workflow/*` internals are what the transform's own emitted imports
88
+ * reach for, and inlining those is most of the 12 MB.
89
+ */
90
+ const WDK_EXTERNAL = [
91
+ "workflow",
92
+ "@workflow/core",
93
+ "@workflow/errors",
94
+ "@workflow/utils",
95
+ "@workflow/world"
96
+ ];
97
+ /** Scratch directory for the builder's file output, under the CLI's own dot-dir. */
98
+ const SCRATCH_REL = path.join(".aai", "workflow-build");
99
+ /**
100
+ * What makes a CJS dependency survive into the ESM step bundle.
101
+ *
102
+ * esbuild cannot statically rewrite `require("node:assert")` inside a bundled
103
+ * CommonJS module, so it emits a `__require` shim — and that shim's fallback
104
+ * THROWS: `Dynamic require of "node:assert" is not supported`. A step bundles
105
+ * everything it imports and npm is full of CJS, so any step reaching a package
106
+ * with a CJS dependency anywhere in its graph failed AT MODULE LOAD, before a
107
+ * single line of the step ran.
108
+ *
109
+ * That is not a hypothetical, and the way it presents is the argument for
110
+ * fixing it here. `research-workflow` imports `webSearch` from
111
+ * `@alexkroman1/aai/tools`, which reaches `host/ssrf.ts`, which imports
112
+ * **undici** — 118 dynamic requires, all of them `node:` builtins. `aai dev`
113
+ * then never listened at all: the message named a Node builtin the author
114
+ * never mentions, nothing named the package or the import that pulled it, and
115
+ * because the step bundle is loaded before the server binds there was no
116
+ * server to ask. It also does not reproduce in-tree, where `@dev/source`
117
+ * resolves the SDK to TypeScript — only against the published `dist` — so
118
+ * every gate short of the e2e suite was green.
119
+ *
120
+ * The shim itself is the fix's whole mechanism: esbuild writes
121
+ * `typeof require !== "undefined" ? require : <thrower>`, so a real `require`
122
+ * in scope is USED. This defines one from `import.meta.url` and is prepended,
123
+ * which puts it ahead of the `var __require = …` initializer that reads it.
124
+ * The flow bundle deliberately gets none — it is compiled in a `node:vm`
125
+ * Script, where `import.meta` does not exist.
126
+ */
127
+ const STEP_REQUIRE_SHIM = [
128
+ "import { createRequire as __aaiCreateRequire } from \"node:module\";",
129
+ "const require = __aaiCreateRequire(import.meta.url);",
130
+ ""
131
+ ].join("\n");
132
+ /**
133
+ * The builder, with the config gotchas above applied once.
134
+ *
135
+ * `BaseBuilder` is abstract over `build()`, so a subclass is the supported way to
136
+ * choose which bundles to emit and how — the WDK's own framework integrations do
137
+ * the same.
138
+ */
139
+ var AaiWorkflowBuilder = class extends BaseBuilder {
140
+ /**
141
+ * Where `build()` leaves its result.
142
+ *
143
+ * `BaseBuilder.build()` is typed `Promise<void>`, so a subclass cannot return
144
+ * the artifacts from it — and parameter properties are unavailable under this
145
+ * repo's `erasableSyntaxOnly`, so the fields are declared the long way too.
146
+ */
147
+ output;
148
+ outDir;
149
+ constructor(cwd, outDir) {
150
+ super({
151
+ buildTarget: "standalone",
152
+ dirs: [`./${WORKFLOWS_DIR}`],
153
+ workingDir: cwd,
154
+ projectRoot: cwd,
155
+ moduleSpecifierRoot: cwd,
156
+ externalPackages: [...WDK_EXTERNAL],
157
+ stepsBundlePath: path.join(outDir, "step.mjs"),
158
+ workflowsBundlePath: path.join(outDir, "flow.mjs"),
159
+ webhookBundlePath: path.join(outDir, "webhook.mjs"),
160
+ suppressCreateWorkflowsBundleLogs: true,
161
+ suppressCreateWebhookBundleLogs: true,
162
+ suppressCreateManifestLogs: true
163
+ });
164
+ this.outDir = outDir;
165
+ }
166
+ async build() {
167
+ const inputFiles = await this.getInputFiles();
168
+ const flowFile = path.join(this.outDir, "flow.mjs");
169
+ const stepFile = path.join(this.outDir, "step.mjs");
170
+ const { manifest } = await this.createWorkflowsBundle({
171
+ inputFiles,
172
+ format: "esm",
173
+ outfile: flowFile,
174
+ bundleFinalOutput: false
175
+ });
176
+ await this.createStepsBundle({
177
+ inputFiles,
178
+ format: "esm",
179
+ outfile: stepFile
180
+ });
181
+ const [workflowCode, stepCode] = await Promise.all([fs.readFile(flowFile, "utf-8"), fs.readFile(stepFile, "utf-8")]);
182
+ this.output = {
183
+ workflowCode,
184
+ stepCode: STEP_REQUIRE_SHIM + stepCode,
185
+ manifest,
186
+ inputFiles
187
+ };
188
+ }
189
+ };
190
+ /**
191
+ * Attach the compiler's `workflowId`/`stepId` to the agent bundle's copy of
192
+ * each directive body — the third transform, see the module doc.
193
+ *
194
+ * `enforce: "pre"` so it sees the file before Vite's own TypeScript pass: the
195
+ * swc transform reads types itself (it is given the filename to infer syntax),
196
+ * and its output is plain JS that the later pass leaves alone.
197
+ *
198
+ * @internal
199
+ */
200
+ function workflowClientPlugin(cwd, inputFiles) {
201
+ const scanned = new Set(inputFiles.map((file) => path.resolve(cwd, file)));
202
+ return {
203
+ name: "aai:workflow-client",
204
+ enforce: "pre",
205
+ async transform(code, id) {
206
+ const file = path.resolve(id.split("?")[0] ?? id);
207
+ if (!scanned.has(file)) return;
208
+ if (!shouldTransformFile(file, detectWorkflowPatterns(code))) return;
209
+ const { code: transformed } = await applySwcTransform(path.relative(cwd, file), code, "client", file, cwd, cwd);
210
+ return {
211
+ code: transformed,
212
+ map: null
213
+ };
214
+ }
215
+ };
216
+ }
217
+ /**
218
+ * Build a project's workflows, or resolve `undefined` when it declares none.
219
+ *
220
+ * `undefined` rather than empty strings, because "this project has no workflows"
221
+ * is the common case — every voice agent that never reaches for one — and the
222
+ * guest must be able to tell it apart from "the build produced nothing", which
223
+ * would be a bug.
224
+ *
225
+ * @internal
226
+ */
227
+ async function buildWorkflows(cwd) {
228
+ if (!await hasWorkflowsDir(cwd)) return;
229
+ const outDir = path.join(cwd, SCRATCH_REL);
230
+ await fs.mkdir(outDir, { recursive: true });
231
+ try {
232
+ const builder = new AaiWorkflowBuilder(cwd, outDir);
233
+ await builder.build();
234
+ const built = builder.output;
235
+ if (!built) return;
236
+ return built.workflowCode.trim() === "" ? void 0 : built;
237
+ } finally {
238
+ await fs.rm(outDir, {
239
+ recursive: true,
240
+ force: true
241
+ }).catch(() => void 0);
242
+ }
243
+ }
244
+ /** Does this project have a `workflows/` directory with anything in it? */
245
+ async function hasWorkflowsDir(cwd) {
246
+ try {
247
+ return (await fs.readdir(path.join(cwd, WORKFLOWS_DIR))).some((name) => /\.(ts|tsx|js|jsx|mjs)$/.test(name));
248
+ } catch {
249
+ return false;
250
+ }
251
+ }
252
+ //#endregion
253
+ //#region worker-bundler.ts
254
+ /**
255
+ * Generated wrapper entry, written under `.aai/` for the duration of the
256
+ * build (the CLI's own scratch dir — dot-paths are ignored by the dev
257
+ * watcher, and the studio's workspace materialization never writes there).
258
+ */
259
+ const WRAPPER_ENTRY_REL = path.join(".aai", "worker-entry.ts");
260
+ /** Extensions a tool module may be authored in (mirrors the SDK's registry). */
261
+ const TOOL_MODULE_EXT_RE = /\.(?:m?ts|tsx)$/;
262
+ /** A co-located spec is not a tool. */
263
+ const TOOL_SPEC_RE = /\.(?:test|spec)\.[^.]+$/;
264
+ /**
265
+ * The project's `tools/` directory, as file names relative to it.
266
+ *
267
+ * **This is the whole of "discovery", and it happens HERE because the guest has
268
+ * no filesystem.** A sandbox is handed one ESM string, so the only place a
269
+ * directory can be turned into modules is where the bundle is assembled — the
270
+ * same lowering eve does (`readdir` → static import list, which the bundler then
271
+ * follows). The names are validated by `toolRegistry` at bundle-evaluation time
272
+ * rather than here, so one implementation owns the rules.
273
+ *
274
+ * Sorted, so the emitted entry is byte-stable for a given directory: an entry
275
+ * that reordered per readdir would change the bundle hash for no reason.
276
+ */
277
+ async function discoverToolFiles(cwd) {
278
+ const root = path.join(cwd, "tools");
279
+ let entries;
280
+ try {
281
+ entries = await fs.readdir(root, {
282
+ withFileTypes: true,
283
+ recursive: true
284
+ });
285
+ } catch (err) {
286
+ if (errorCode(err) === "ENOENT") return [];
287
+ throw err;
288
+ }
289
+ return entries.filter((e) => e.isFile() && TOOL_MODULE_EXT_RE.test(e.name) && !TOOL_SPEC_RE.test(e.name)).map((e) => path.relative(root, path.join(e.parentPath, e.name)).split(path.sep).join("/")).sort();
290
+ }
291
+ /** The prose slot: one file, beside `agent.ts`, named by convention. */
292
+ const SYSTEM_PROMPT_FILE = "system-prompt.md";
293
+ /**
294
+ * Whether the project keeps its system prompt in a file.
295
+ *
296
+ * The other half of "a file beside `agent.ts` can BE the thing", and it happens
297
+ * here for the same reason `discoverToolFiles` does — the guest has no
298
+ * filesystem, so the read belongs where the bundle is assembled. What the file
299
+ * MEANS is `withSystemPrompt`'s to decide (the author may have imported and
300
+ * composed it, in which case discovery must not apply it twice); this only
301
+ * answers whether there is one.
302
+ */
303
+ async function hasSystemPromptFile(cwd) {
304
+ const nested = path.join(cwd, "system-prompt");
305
+ if ((await fs.stat(nested).catch(() => void 0))?.isDirectory() === true) throw new Error(`${nested} is a directory. A system prompt is ONE file — rename it to ${SYSTEM_PROMPT_FILE}, or import the pieces yourself and compose them into \`systemPrompt\`. There is deliberately no concatenation order for a directory.`);
306
+ try {
307
+ return (await fs.stat(path.join(cwd, SYSTEM_PROMPT_FILE))).isFile();
308
+ } catch (err) {
309
+ if (errorCode(err) === "ENOENT") return false;
310
+ throw err;
311
+ }
312
+ }
313
+ function wrapperEntrySource(runtime, workflows, toolFiles, systemPromptFile) {
314
+ const toolImports = toolFiles.map((file, i) => `import * as __aaiTool${i} from "../tools/${file}";`).join("\n");
315
+ const toolEntries = toolFiles.map((file, i) => ` ${JSON.stringify(`tools/${file}`)}: __aaiTool${i},`).join("\n");
316
+ return `import def from "../agent.ts";
317
+ import { agentToolsToSchemas, toAgentConfig, toolRegistry, withSystemPrompt, withTools } from "@alexkroman1/aai/manifest";
318
+ ${runtime ? `import { createRuntime } from "@alexkroman1/aai/runtime";` : ""}
319
+ ${systemPromptFile ? `import __aaiSystemPrompt from "../${SYSTEM_PROMPT_FILE}?raw";` : ""}
320
+ ${toolImports}
321
+ // A tool's name is its file name. The map is built here rather than written in
322
+ // agent.ts, so a file that exists is a tool the model can call — there is no
323
+ // registration step to forget.
324
+ //
325
+ // \`system-prompt.md\` arrives the same way, and the \`?raw\` lives HERE rather
326
+ // than in the author's own \`agent.ts\`: it is a Vite convention, and the whole
327
+ // point of generating this entry is that a bundler feature never has to appear
328
+ // in user-authored space.
329
+ const __aaiAgent = ${systemPromptFile ? "withSystemPrompt(" : ""}withTools(
330
+ def,
331
+ toolRegistry({
332
+ ${toolEntries}
333
+ }),
334
+ )${systemPromptFile ? ", __aaiSystemPrompt)" : ""};
335
+ export default __aaiAgent;
336
+ export const __aaiConfig = {
337
+ ...toAgentConfig(__aaiAgent),
338
+ toolSchemas: agentToolsToSchemas(__aaiAgent.tools ?? {}),
339
+ };
340
+ ${runtime ? `export const __aaiCreateRuntime = (opts: Record<string, unknown>) =>
341
+ createRuntime({ ...opts, agent: __aaiAgent });
342
+ ` : ""}${workflows ? `// The compiled workflow surface, carried as DATA. \`__aaiWorkflowCode\` goes to
343
+ // \`workflowEntrypoint(code)\` and \`__aaiStepCode\` is evaluated by the guest so its
344
+ // \`registerStepFunction\` calls run. Strings rather than modules because the guest
345
+ // receives exactly one ESM string and never sees this project's filesystem.
346
+ export const __aaiWorkflowCode = ${JSON.stringify(workflows.workflowCode)};
347
+ export const __aaiStepCode = ${JSON.stringify(workflows.stepCode)};
348
+ ` : ""}`;
349
+ }
350
+ /**
351
+ * Bundle agent.ts into a single ESM string for the sandbox worker.
352
+ *
353
+ * Zod is bundled in — zod 4's `Function()` usage is wrapped in try/catch
354
+ * and gracefully degrades in restricted environments like Deno.
355
+ *
356
+ * @internal — build hook for aai-server/the studio; not a supported public
357
+ * API and not covered by semver.
358
+ */
359
+ async function buildWorker(cwd, opts = {}) {
360
+ const wrapperPath = path.join(cwd, WRAPPER_ENTRY_REL);
361
+ await fs.mkdir(path.dirname(wrapperPath), { recursive: true });
362
+ await fs.writeFile(wrapperPath, wrapperEntrySource(opts.runtime !== false, opts.workflows, await discoverToolFiles(cwd), await hasSystemPromptFile(cwd)), "utf-8");
363
+ const plugins = [...opts.plugins ?? [], ...opts.workflows ? [workflowClientPlugin(cwd, opts.workflows.inputFiles)] : []];
364
+ let result;
365
+ try {
366
+ result = await withPreservedNodeEnv(() => build({
367
+ root: cwd,
368
+ logLevel: "silent",
369
+ ...opts.configFile === false && { configFile: false },
370
+ ...plugins.length > 0 && { plugins },
371
+ ssr: { noExternal: true },
372
+ build: {
373
+ ssr: true,
374
+ lib: {
375
+ entry: wrapperPath,
376
+ formats: ["es"],
377
+ fileName: "worker"
378
+ },
379
+ target: "node20",
380
+ minify: opts.minify ? "oxc" : false,
381
+ write: false,
382
+ rollupOptions: { output: {
383
+ entryFileNames: "[name].js",
384
+ codeSplitting: false
385
+ } }
386
+ }
387
+ }));
388
+ } finally {
389
+ await fs.rm(wrapperPath, { force: true }).catch(() => void 0);
390
+ }
391
+ const output = Array.isArray(result) ? result[0] : result;
392
+ if (!output) throw new Error("Vite produced no output for agent.ts");
393
+ const chunk = output.output.find((o) => o.type === "chunk" && o.isEntry);
394
+ if (!chunk) throw new Error("Vite produced no entry chunk for agent.ts");
395
+ return chunk.code;
396
+ }
397
+ //#endregion
398
+ export { buildWorkflows as n, buildWorker as t };
@@ -1,4 +1,5 @@
1
1
  import { type PluginOption } from "vite";
2
+ import { type WorkflowBundleOutput } from "./workflow-bundler.ts";
2
3
  /**
3
4
  * Options for worker bundling.
4
5
  *
@@ -24,6 +25,19 @@ export type BuildWorkerOptions = {
24
25
  * rebuild would turn the watch loop from sub-second into multi-second.
25
26
  */
26
27
  runtime?: boolean;
28
+ /**
29
+ * The project's compiled workflows, when it declares any.
30
+ *
31
+ * Embedded in the worker as two string exports rather than shipped as extra
32
+ * files, because the guest's `bundle/load` contract is ONE ESM string. See
33
+ * `wrapperEntrySource`.
34
+ *
35
+ * It also switches on the client transform (`workflowClientPlugin`), which is
36
+ * what puts a `workflowId` on the agent's own copy of each body. Passing the
37
+ * strings without it produces a bundle that serves every workflow route and
38
+ * cannot start a run.
39
+ */
40
+ workflows?: WorkflowBundleOutput | undefined;
27
41
  };
28
42
  /**
29
43
  * Bundle agent.ts into a single ESM string for the sandbox worker.
@@ -1,110 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { t as withPreservedNodeEnv } from "./_vite-env-Dg_QlVv0.mjs";
3
- import path from "node:path";
4
- import fs from "node:fs/promises";
5
- import { build } from "vite";
6
- //#region worker-bundler.ts
7
- /**
8
- * Worker bundling — the one implementation of "turn an `agent.ts` into the
9
- * ESM the guest sandbox loads".
10
- *
11
- * Public (no `_` prefix) for the same reason as `client-bundler.ts`: the
12
- * platform's browser studio builds its workspaces through this function, so a
13
- * worker published from the browser comes out of the same Vite/Rollup pass as
14
- * one from `aai deploy`.
15
- *
16
- * @internal — build hook for aai-server/the studio; not a supported public
17
- * API and not covered by semver. The lack of a `_` prefix is packaging (the
18
- * subpath must be importable cross-package), not an invitation: user code
19
- * should never import from `@alexkroman1/aai-cli`.
20
- *
21
- * Every worker is built through a generated wrapper entry that re-exports the
22
- * agent *and* its extracted config as `__aaiConfig` (via the dependency-free
23
- * `@alexkroman1/aai/manifest` helpers, bundled in). The guest harness returns
24
- * that export from `bundle/load`, which is how the platform obtains an
25
- * agent's config without ever evaluating tenant code on the host — the
26
- * `POST /deploy` route and the studio's sandbox inspection both rely on it.
27
- *
28
- * **The worker ships its own runtime.** Deploy builds also export
29
- * `__aaiCreateRuntime` — a factory over the *user's installed* SDK's
30
- * `createRuntime`, bundled in alongside the provider SDKs. The guest harness
31
- * builds the session runtime through it, so a deployed agent runs exactly the
32
- * runtime version it was built and tested against (identical to `aai dev`),
33
- * instead of whatever SDK the platform's harness image was baked with. The
34
- * harness↔bundle contract is deliberately tiny: the factory takes
35
- * `{ env, db?, runCode? }` and returns `{ startSession, shutdown }`.
36
- *
37
- * What the studio supplies via options, because a workspace is not a project:
38
- *
39
- * - **`configFile: false`.** Workspace files are untrusted and a Vite config
40
- * is executable host code.
41
- * - **`plugins`.** The studio adds its import allowlist; without it, Vite
42
- * would happily resolve any package in the server's `node_modules`.
43
- */
44
- /**
45
- * Generated wrapper entry, written under `.aai/` for the duration of the
46
- * build (the CLI's own scratch dir — dot-paths are ignored by the dev
47
- * watcher, and the studio's workspace materialization never writes there).
48
- */
49
- const WRAPPER_ENTRY_REL = path.join(".aai", "worker-entry.ts");
50
- function wrapperEntrySource(runtime) {
51
- return `import def from "../agent.ts";
52
- import { agentToolsToSchemas, toAgentConfig } from "@alexkroman1/aai/manifest";
53
- ${runtime ? `import { createRuntime } from "@alexkroman1/aai/runtime";` : ""}
54
- export default def;
55
- export const __aaiConfig = {
56
- ...toAgentConfig(def),
57
- toolSchemas: agentToolsToSchemas(def.tools ?? {}),
58
- };
59
- ${runtime ? `export const __aaiCreateRuntime = (opts: Record<string, unknown>) =>
60
- createRuntime({ ...opts, agent: def });
61
- ` : ""}`;
62
- }
63
- /**
64
- * Bundle agent.ts into a single ESM string for the sandbox worker.
65
- *
66
- * Zod is bundled in — zod 4's `Function()` usage is wrapped in try/catch
67
- * and gracefully degrades in restricted environments like Deno.
68
- *
69
- * @internal — build hook for aai-server/the studio; not a supported public
70
- * API and not covered by semver.
71
- */
72
- async function buildWorker(cwd, opts = {}) {
73
- const wrapperPath = path.join(cwd, WRAPPER_ENTRY_REL);
74
- await fs.mkdir(path.dirname(wrapperPath), { recursive: true });
75
- await fs.writeFile(wrapperPath, wrapperEntrySource(opts.runtime !== false), "utf-8");
76
- let result;
77
- try {
78
- result = await withPreservedNodeEnv(() => build({
79
- root: cwd,
80
- logLevel: "silent",
81
- ...opts.configFile === false && { configFile: false },
82
- ...opts.plugins && { plugins: opts.plugins },
83
- ssr: { noExternal: true },
84
- build: {
85
- ssr: true,
86
- lib: {
87
- entry: wrapperPath,
88
- formats: ["es"],
89
- fileName: "worker"
90
- },
91
- target: "node20",
92
- minify: opts.minify ? "oxc" : false,
93
- write: false,
94
- rollupOptions: { output: {
95
- entryFileNames: "[name].js",
96
- codeSplitting: false
97
- } }
98
- }
99
- }));
100
- } finally {
101
- await fs.rm(wrapperPath, { force: true }).catch(() => void 0);
102
- }
103
- const output = Array.isArray(result) ? result[0] : result;
104
- if (!output) throw new Error("Vite produced no output for agent.ts");
105
- const chunk = output.output.find((o) => o.type === "chunk" && o.isEntry);
106
- if (!chunk) throw new Error("Vite produced no entry chunk for agent.ts");
107
- return chunk.code;
108
- }
109
- //#endregion
2
+ import { t as buildWorker } from "./worker-bundler-CIdDhZ0c.mjs";
110
3
  export { buildWorker };
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
3
+ import { n as log } from "./_ui-u7T4YooX.mjs";
4
+ import { n as getServerInfo } from "./_agent-vGW5SBnX.mjs";
5
+ import { errorMessage, omitUndefined } from "@alexkroman1/aai/utils";
6
+ import { createWorkflowApiClient } from "@alexkroman1/aai/workflow-api";
7
+ //#region workflow.ts
8
+ /** Runs listed when the caller names no limit — a terminal is not a dashboard. */
9
+ const DEFAULT_RUN_LIMIT = 20;
10
+ /**
11
+ * A client for the agent's workflow API, plus the bearer when one was given.
12
+ *
13
+ * `getServerInfo` is what turns "this directory" into an origin and a published
14
+ * slug, and it already refuses a project that has never been deployed with the
15
+ * sentence naming `aai publish`. The client is handed the AGENT's base URL and
16
+ * appends the route prefix itself, so the `/workflows` literal is not spelled
17
+ * here — it is the same constant the server matches on.
18
+ *
19
+ * `serverUrl` is joined as-is: `resolveServerUrl` is the single producer of
20
+ * every origin that reaches here and strips trailing slashes once, at
21
+ * resolution time, precisely so join sites do not each carry a copy. The copy
22
+ * this used to hold also DISAGREED with it — `/\/$/` takes one slash where the
23
+ * upstream `/\/+$/` takes all — so the two would have differed on the only
24
+ * input either was written for.
25
+ */
26
+ async function target(cwd, opts) {
27
+ const { serverUrl, slug } = await getServerInfo(cwd, opts.server);
28
+ return {
29
+ api: createWorkflowApiClient({
30
+ baseUrl: `${serverUrl}/${slug}`,
31
+ ...omitUndefined({ token: opts.token })
32
+ }),
33
+ slug
34
+ };
35
+ }
36
+ /**
37
+ * Run one call, turning a rejection into a `CommandResult`.
38
+ *
39
+ * The client throws with the AGENT'S own sentence — an unknown workflow names
40
+ * the declared ones, a 503 says the sandbox is still booting — and that text is
41
+ * the whole diagnostic, so it is surfaced rather than replaced with a status
42
+ * code. `errorMessage` rather than `instanceof Error`, because a rejection that
43
+ * is message-bearing without being an `Error` would otherwise print as
44
+ * `[object Object]`.
45
+ */
46
+ async function attempt(call) {
47
+ try {
48
+ return {
49
+ ok: true,
50
+ value: await call()
51
+ };
52
+ } catch (err) {
53
+ return {
54
+ ok: false,
55
+ error: errorMessage(err)
56
+ };
57
+ }
58
+ }
59
+ /** `aai workflow list` — what this agent declares. */
60
+ async function executeWorkflowList(cwd, opts) {
61
+ const { api, slug } = await target(cwd, opts);
62
+ const res = await attempt(() => api.list());
63
+ if (!res.ok) return fail("workflow_list_failed", res.error, HINT_BROKER);
64
+ const workflows = res.value;
65
+ if (workflows.length === 0) log.info(`${slug} declares no workflows`);
66
+ else for (const w of workflows) log.info(`${w.name}${w.description ? ` — ${w.description}` : ""}`);
67
+ return ok({ workflows });
68
+ }
69
+ /**
70
+ * `aai workflow runs <name>` — recent runs, newest first.
71
+ *
72
+ * `recent` rather than `find`, so the query carries no `key`: a terminal has no
73
+ * correlation key to ask about, and most runs carry none.
74
+ */
75
+ async function executeWorkflowRuns(cwd, workflow, opts) {
76
+ const { api } = await target(cwd, opts);
77
+ const res = await attempt(() => api.recent(workflow, { limit: opts.limit ?? DEFAULT_RUN_LIMIT }));
78
+ if (!res.ok) return fail("workflow_runs_failed", res.error, HINT_BROKER);
79
+ const runs = res.value;
80
+ if (runs.length === 0) log.info(`No runs of ${workflow} yet`);
81
+ for (const run of runs) log.info(formatRun(run));
82
+ return ok({ runs });
83
+ }
84
+ /** One run on one line — the id first, because it is what every other verb takes. */
85
+ function formatRun(run) {
86
+ const parts = [run.runId, run.status];
87
+ if (run.key !== void 0) parts.push(`key=${run.key}`);
88
+ if (run.status === "failed") parts.push(run.error);
89
+ return parts.join(" ");
90
+ }
91
+ /** `aai workflow show <runId>` — one run in full, including its output. */
92
+ async function executeWorkflowShow(cwd, runId, opts) {
93
+ const { api } = await target(cwd, opts);
94
+ const res = await attempt(() => api.get(runId));
95
+ if (!res.ok) return fail("workflow_show_failed", res.error, HINT_BROKER);
96
+ if (res.value === void 0) return fail("workflow_show_failed", `No run ${runId}`, HINT_BROKER);
97
+ const run = res.value;
98
+ log.info(formatRun(run));
99
+ if (run.status === "completed") log.info(JSON.stringify(run.output, null, 2));
100
+ return ok({ run });
101
+ }
102
+ /** `aai workflow cancel <runId>` — stop a live run. */
103
+ async function executeWorkflowCancel(cwd, runId, opts) {
104
+ const { api } = await target(cwd, opts);
105
+ const res = await attempt(() => api.cancel(runId));
106
+ if (!res.ok) return fail("workflow_cancel_failed", res.error, HINT_BROKER);
107
+ const cancelled = res.value;
108
+ log.info(cancelled ? `Cancelled ${runId}` : `${runId} had already finished`);
109
+ return ok({
110
+ runId,
111
+ cancelled
112
+ });
113
+ }
114
+ /**
115
+ * The hint every failure here carries.
116
+ *
117
+ * Three causes dominate and they look similar from a terminal: the agent
118
+ * declares no workflows (404 from its own API), its sandbox is still booting
119
+ * (503 from the broker), or its operator closed the surface (401). Naming all
120
+ * three is cheap and cannot be wrong — the same reasoning
121
+ * `WORKFLOWS_UNAVAILABLE_MESSAGE` follows.
122
+ */
123
+ const HINT_BROKER = "The agent may declare no workflows, or its sandbox may still be starting — try again shortly. Pass --token if the agent sets AAI_WORKFLOW_API_TOKEN.";
124
+ //#endregion
125
+ export { executeWorkflowCancel, executeWorkflowList, executeWorkflowRuns, executeWorkflowShow };