@alexkroman1/aai-cli 5.14.0 → 6.2.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 (231) 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 +8 -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 +17 -19
  53. package/dist/templates/dispatch-center/agent.ts +2 -29
  54. package/dist/templates/dispatch-center/client.tsx +33 -16
  55. package/dist/templates/dispatch-center/shared.ts +53 -19
  56. package/dist/templates/dispatch-center/tools/incident_add_note.ts +12 -14
  57. package/dist/templates/dispatch-center/tools/incident_create.ts +41 -44
  58. package/dist/templates/dispatch-center/tools/incident_escalate.ts +59 -61
  59. package/dist/templates/dispatch-center/tools/incident_get.ts +3 -4
  60. package/dist/templates/dispatch-center/tools/incident_triage.ts +43 -45
  61. package/dist/templates/dispatch-center/tools/incident_update_status.ts +39 -41
  62. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +2 -5
  63. package/dist/templates/dispatch-center/tools/ops_protocols.ts +1 -1
  64. package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +20 -23
  65. package/dist/templates/dispatch-center/tools/resources_dispatch.ts +52 -54
  66. package/dist/templates/dispatch-center/tools/resources_get_available.ts +2 -4
  67. package/dist/templates/dispatch-center/tools/resources_update_status.ts +39 -42
  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 +28 -12
  82. package/dist/templates/infocom-adventure/shared.ts +28 -11
  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 +52 -47
  102. package/dist/templates/pizza-ordering/agent.ts +9 -158
  103. package/dist/templates/pizza-ordering/shared.ts +35 -12
  104. package/dist/templates/pizza-ordering/tools/add_pizza.ts +31 -0
  105. package/dist/templates/pizza-ordering/tools/place_order.ts +22 -0
  106. package/dist/templates/pizza-ordering/tools/remove_pizza.ts +22 -0
  107. package/dist/templates/pizza-ordering/tools/set_customer_name.ts +13 -0
  108. package/dist/templates/pizza-ordering/tools/update_pizza.ts +28 -0
  109. package/dist/templates/pizza-ordering/tools/view_order.ts +21 -0
  110. package/dist/templates/plan-and-execute/agent.test.ts +411 -0
  111. package/dist/templates/plan-and-execute/agent.ts +23 -0
  112. package/dist/templates/plan-and-execute/client.tsx +115 -0
  113. package/dist/templates/plan-and-execute/graph.ts +178 -0
  114. package/dist/templates/plan-and-execute/prompts.ts +110 -0
  115. package/dist/templates/plan-and-execute/shared.ts +161 -0
  116. package/dist/templates/plan-and-execute/system-prompt.md +18 -0
  117. package/dist/templates/plan-and-execute/tools/plan_status.ts +20 -0
  118. package/dist/templates/plan-and-execute/tools/revise_plan.ts +63 -0
  119. package/dist/templates/plan-and-execute/tools/start_plan.ts +51 -0
  120. package/dist/templates/plan-and-execute/tools/work_next_step.ts +101 -0
  121. package/dist/templates/recap-workflow/agent.test.ts +762 -0
  122. package/dist/templates/recap-workflow/agent.ts +95 -0
  123. package/dist/templates/recap-workflow/shared.ts +43 -0
  124. package/dist/templates/recap-workflow/tools/cancel_recap.ts +23 -0
  125. package/dist/templates/recap-workflow/tools/keep_transcript.ts +31 -0
  126. package/dist/templates/recap-workflow/tools/recap_progress.ts +27 -0
  127. package/dist/templates/recap-workflow/tools/recap_status.ts +46 -0
  128. package/dist/templates/recap-workflow/tools/request_recap.ts +43 -0
  129. package/dist/templates/recap-workflow/workflows/recap.ts +544 -0
  130. package/dist/templates/recap-workflow/workflows/tokens.ts +26 -0
  131. package/dist/templates/redline/agent.test.ts +222 -0
  132. package/dist/templates/redline/agent.ts +89 -0
  133. package/dist/templates/redline/client.tsx +184 -0
  134. package/dist/templates/redline/workflows/prompts.ts +68 -0
  135. package/dist/templates/redline/workflows/redline.ts +258 -0
  136. package/dist/templates/research-workflow/agent.test.ts +525 -0
  137. package/dist/templates/research-workflow/agent.ts +94 -0
  138. package/dist/templates/research-workflow/shared.ts +35 -0
  139. package/dist/templates/research-workflow/tools/file_it_now.ts +22 -0
  140. package/dist/templates/research-workflow/tools/request_research.ts +33 -0
  141. package/dist/templates/research-workflow/tools/research_progress.ts +26 -0
  142. package/dist/templates/research-workflow/tools/research_status.ts +40 -0
  143. package/dist/templates/research-workflow/workflows/prompts.ts +143 -0
  144. package/dist/templates/research-workflow/workflows/research.ts +558 -0
  145. package/dist/templates/retail/agent.test.ts +236 -172
  146. package/dist/templates/retail/agent.ts +5 -42
  147. package/dist/templates/retail/client.tsx +33 -11
  148. package/dist/templates/retail/registry.test.ts +31 -16
  149. package/dist/templates/retail/resolve.ts +28 -64
  150. package/dist/templates/retail/shared.test.ts +4 -3
  151. package/dist/templates/retail/shared.ts +24 -7
  152. package/dist/templates/retail/store.test.ts +12 -10
  153. package/dist/templates/retail/store.ts +69 -30
  154. package/dist/templates/retail/tools/cancel_pending_order.ts +4 -9
  155. package/dist/templates/retail/tools/exchange_delivered_order_items.ts +22 -11
  156. package/dist/templates/retail/tools/find_user_id_by_email.ts +5 -9
  157. package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +5 -7
  158. package/dist/templates/retail/tools/get_item_details.ts +5 -7
  159. package/dist/templates/retail/tools/get_order_details.ts +5 -7
  160. package/dist/templates/retail/tools/get_product_details.ts +4 -7
  161. package/dist/templates/retail/tools/get_user_details.ts +4 -7
  162. package/dist/templates/retail/tools/list_all_product_types.ts +5 -7
  163. package/dist/templates/retail/tools/modify_pending_order_address.ts +4 -9
  164. package/dist/templates/retail/tools/modify_pending_order_items.ts +4 -16
  165. package/dist/templates/retail/tools/modify_pending_order_payment.ts +3 -9
  166. package/dist/templates/retail/tools/modify_user_address.ts +4 -9
  167. package/dist/templates/retail/tools/return_delivered_order_items.ts +3 -9
  168. package/dist/templates/retail/tools/transfer_to_human_agents.ts +1 -1
  169. package/dist/templates/solo-rpg/agent.test.ts +152 -29
  170. package/dist/templates/solo-rpg/agent.ts +3 -21
  171. package/dist/templates/solo-rpg/client.tsx +19 -6
  172. package/dist/templates/solo-rpg/shared.ts +19 -11
  173. package/dist/templates/solo-rpg/tools/action_roll.ts +2 -4
  174. package/dist/templates/solo-rpg/tools/burn_momentum.ts +2 -4
  175. package/dist/templates/solo-rpg/tools/check_state.ts +6 -6
  176. package/dist/templates/solo-rpg/tools/load_game.ts +1 -1
  177. package/dist/templates/solo-rpg/tools/oracle.ts +18 -11
  178. package/dist/templates/solo-rpg/tools/save_game.ts +10 -7
  179. package/dist/templates/solo-rpg/tools/setup_character.ts +1 -1
  180. package/dist/templates/solo-rpg/tools/update_state.ts +3 -4
  181. package/dist/templates/support-line/agent.test.ts +343 -0
  182. package/dist/templates/support-line/agent.ts +27 -0
  183. package/dist/templates/support-line/client.tsx +122 -0
  184. package/dist/templates/support-line/graph.ts +224 -0
  185. package/dist/templates/support-line/knowledge.json +65 -0
  186. package/dist/templates/support-line/prompts.ts +95 -0
  187. package/dist/templates/support-line/shared.ts +240 -0
  188. package/dist/templates/support-line/system-prompt.md +17 -0
  189. package/dist/templates/support-line/tools/answer_question.ts +72 -0
  190. package/dist/templates/support-line/tools/list_topics.ts +22 -0
  191. package/dist/templates/support-line/tools/log_ticket.ts +30 -0
  192. package/dist/templates/transcription-workflow/agent.test.ts +563 -0
  193. package/dist/templates/transcription-workflow/agent.ts +92 -0
  194. package/dist/templates/transcription-workflow/client.tsx +234 -0
  195. package/dist/templates/transcription-workflow/workflows/transcribe.ts +416 -0
  196. package/dist/templates/transcription-workflow/workflows/wav.ts +314 -0
  197. package/dist/templates/travel-concierge/agent.test.ts +338 -0
  198. package/dist/templates/travel-concierge/agent.ts +29 -0
  199. package/dist/templates/travel-concierge/client.tsx +161 -0
  200. package/dist/templates/travel-concierge/routing.ts +138 -0
  201. package/dist/templates/travel-concierge/shared.ts +547 -0
  202. package/dist/templates/travel-concierge/system-prompt.md +24 -0
  203. package/dist/templates/travel-concierge/tools/book_car_rental.ts +20 -0
  204. package/dist/templates/travel-concierge/tools/book_excursion.ts +18 -0
  205. package/dist/templates/travel-concierge/tools/book_hotel.ts +20 -0
  206. package/dist/templates/travel-concierge/tools/cancel_action.ts +7 -0
  207. package/dist/templates/travel-concierge/tools/cancel_ticket.ts +16 -0
  208. package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +7 -0
  209. package/dist/templates/travel-concierge/tools/confirm_action.ts +7 -0
  210. package/dist/templates/travel-concierge/tools/lookup_booking.ts +35 -0
  211. package/dist/templates/travel-concierge/tools/search_car_rentals.ts +33 -0
  212. package/dist/templates/travel-concierge/tools/search_excursions.ts +37 -0
  213. package/dist/templates/travel-concierge/tools/search_flights.ts +43 -0
  214. package/dist/templates/travel-concierge/tools/search_hotels.ts +36 -0
  215. package/dist/templates/travel-concierge/tools/to_car_rental_assistant.ts +11 -0
  216. package/dist/templates/travel-concierge/tools/to_excursion_assistant.ts +11 -0
  217. package/dist/templates/travel-concierge/tools/to_flight_assistant.ts +11 -0
  218. package/dist/templates/travel-concierge/tools/to_hotel_assistant.ts +11 -0
  219. package/dist/templates/travel-concierge/tools/update_ticket.ts +22 -0
  220. package/dist/templates/web-researcher/agent.ts +0 -2
  221. package/dist/templates/web-researcher/system-prompt.md +10 -0
  222. package/dist/{test-2LWBcXvY.mjs → test-B6ZBxSpk.mjs} +2 -2
  223. package/dist/typecheck.mjs +17 -15
  224. package/dist/worker-bundler-CIdDhZ0c.mjs +398 -0
  225. package/dist/worker-bundler.d.ts +14 -0
  226. package/dist/worker-bundler.mjs +1 -108
  227. package/dist/workflow-B-O97AqI.mjs +125 -0
  228. package/dist/workflow-bundler.d.ts +133 -0
  229. package/dist/workflow.d.ts +67 -0
  230. package/package.json +14 -10
  231. package/dist/build-DQJ1ZSm9.mjs +0 -39
@@ -1,15 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import { i as outputSilenced, n as log, r as notify } from "./_ui-u7T4YooX.mjs";
3
- import { a as errorMessage, r as errorCode } from "./_utils-0DHbcqUb.mjs";
4
- import { n as fallbackHtmlPlugin } from "./client-bundler-C9qXL8A2.mjs";
5
- import { buildWorker } from "./worker-bundler.mjs";
6
- import { n as ensureApiKey } from "./_config-CzwAWOWr.mjs";
7
- import { n as createWorkerEvaluator } from "./_bundler-BuIyDdvs.mjs";
8
- import { t as resolveServerEnv } from "./_server-common-61fL__39.mjs";
2
+ import { i as outputSilenced, r as notify } from "./_ui-u7T4YooX.mjs";
3
+ import { i as errorCode, o as errorMessage$1 } from "./_utils-B8QmtFhK.mjs";
4
+ import { n as fallbackHtmlPlugin } from "./client-bundler-B8Q9F8gJ.mjs";
5
+ import { t as DEDUPED_PEERS } from "./_vite-env-BNveawd1.mjs";
6
+ import { n as buildWorkflows, t as buildWorker } from "./worker-bundler-CIdDhZ0c.mjs";
7
+ import { n as ensureApiKey } from "./_config-D_s09e7g.mjs";
8
+ import { n as createWorkerEvaluator } from "./_bundler-BapCUUJN.mjs";
9
+ import { t as resolveServerEnv } from "./_server-common-6e0QI4mq.mjs";
9
10
  import { existsSync } from "node:fs";
10
11
  import path from "node:path";
11
- import { setTimeout } from "node:timers/promises";
12
- import { consoleLogger, createRuntime, createServer, requiredProviderEnvVars, withHostCredentialFallback } from "@alexkroman1/aai/runtime";
12
+ import { omitUndefined } from "@alexkroman1/aai/utils";
13
+ import { createCoalescingRunner, sleep } from "@alexkroman1/aai/internal";
14
+ import { WORKFLOW_API_PREFIX, configureWorkflowWorld, consoleLogger, createRuntime, createServer, createWorkflowSurface, handleWorkflowRequest, publishStepEnv, requiredProviderEnvVars, startWorkflowWorldIfDeclared, withHostCredentialFallback } from "@alexkroman1/aai/runtime";
13
15
  import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
14
16
  import { watch } from "chokidar";
15
17
  import getPort, { portNumbers } from "get-port";
@@ -94,36 +96,41 @@ const LISTEN_RETRY_DELAY_MS = 250;
94
96
  * the built server (or {@link RestartSupervisor.close} on startup failure).
95
97
  */
96
98
  function createRestartSupervisor(ops) {
97
- const wait = ops.sleep ?? setTimeout;
98
- let restarting = true;
99
- let pendingRestart = false;
99
+ const wait = ops.sleep ?? sleep;
100
+ let booting = true;
101
+ let queuedDuringBoot = false;
100
102
  let closed = false;
101
103
  let current;
102
104
  let cleanupPromise;
105
+ /**
106
+ * At most one rebuild in flight; every change landing during one collapses
107
+ * into a SINGLE trailing rebuild started after it settles.
108
+ *
109
+ * That is exactly the repo's `createCoalescingRunner` contract, and this
110
+ * module used to re-derive it with a `restarting` flag, a `pendingRestart`
111
+ * flag and a `do/while` loop. Coalescing is the right policy for the same
112
+ * reason it is there: a rebuild reads the files as they are WHEN IT RUNS, so
113
+ * N queued rebuilds would do the trailing one's work N times, while dropping
114
+ * the trigger outright would leave the newest save unserved (or, when the
115
+ * in-flight rebuild died on a mid-edit syntax error, leave the server down).
116
+ */
117
+ const rebuilds = createCoalescingRunner(() => restartOnce());
103
118
  function request() {
104
- if (restarting) {
105
- pendingRestart = true;
119
+ if (booting) {
120
+ queuedDuringBoot = true;
106
121
  return;
107
122
  }
108
- restarting = true;
109
- restart().catch((err) => {
110
- ops.notify("error", `Restart failed: ${errorMessage(err)}`);
111
- }).finally(() => {
112
- restarting = false;
123
+ rebuilds.trigger().catch((err) => {
124
+ ops.notify("error", `Restart failed: ${errorMessage$1(err)}`);
113
125
  });
114
126
  }
115
- async function restart() {
116
- do {
117
- pendingRestart = false;
118
- await restartOnce();
119
- } while (pendingRestart && !closed);
120
- }
121
127
  async function restartOnce() {
128
+ if (closed) return;
122
129
  let newServer;
123
130
  try {
124
131
  newServer = await ops.build();
125
132
  } catch (err) {
126
- ops.notify("error", `Restart failed: ${errorMessage(err)} (previous server still running)`);
133
+ ops.notify("error", `Restart failed: ${errorMessage$1(err)} (previous server still running)`);
127
134
  return;
128
135
  }
129
136
  if (closed) {
@@ -138,7 +145,7 @@ function createRestartSupervisor(ops) {
138
145
  try {
139
146
  await listenWithRetry(newServer);
140
147
  } catch (err) {
141
- ops.notify("error", `Restart failed: ${errorMessage(err)} — dev server is down; save a file to retry.`);
148
+ ops.notify("error", `Restart failed: ${errorMessage$1(err)} — dev server is down; save a file to retry.`);
142
149
  await closeQuietly(newServer);
143
150
  return;
144
151
  }
@@ -174,8 +181,8 @@ function createRestartSupervisor(ops) {
174
181
  request,
175
182
  adopt(server) {
176
183
  current = server;
177
- restarting = false;
178
- if (pendingRestart) request();
184
+ booting = false;
185
+ if (queuedDuringBoot) request();
179
186
  },
180
187
  current: () => current,
181
188
  close() {
@@ -229,7 +236,7 @@ async function resolveAgentEnv(root, agentDef) {
229
236
  const required = requiredProviderEnvVars(agentDef);
230
237
  const hasShellKey = Boolean(process.env.ASSEMBLYAI_API_KEY);
231
238
  if (required.includes("ASSEMBLYAI_API_KEY") && !env.ASSEMBLYAI_API_KEY && !hasShellKey) env.ASSEMBLYAI_API_KEY = await ensureApiKey();
232
- for (const warning of agentEnvWarnings(agentDef, env)) log.warn(warning);
239
+ for (const warning of agentEnvWarnings(agentDef, env)) notify("warn", warning);
233
240
  return env;
234
241
  }
235
242
  /**
@@ -244,9 +251,19 @@ async function resolveAgentEnv(root, agentDef) {
244
251
  * errors in the agent's code propagate — the restart loop reports them and
245
252
  * keeps the old server. Evaluation goes through the memoizing evaluator so
246
253
  * a no-op save doesn't leak another module into the ESM registry.
254
+ *
255
+ * The project's `workflows/` directory is compiled by the same pass deploy
256
+ * runs (`buildWorkflows`) and rides the bundle as two string exports, which is
257
+ * how `aai dev` serves a workflow at all: nothing here hands the bundle to a
258
+ * guest, so the CLI is both ends of that contract locally. A project with no
259
+ * `workflows/` directory pays nothing — `buildWorkflows` resolves `undefined`
260
+ * without starting a builder.
247
261
  */
248
- async function loadAgentDef(cwd, evaluate) {
249
- return evaluate(await buildWorker(cwd, { runtime: false }));
262
+ async function loadWorker(cwd, evaluate) {
263
+ return evaluate(await buildWorker(cwd, {
264
+ runtime: false,
265
+ workflows: await buildWorkflows(cwd)
266
+ }));
250
267
  }
251
268
  /**
252
269
  * True for paths that should never trigger a restart: anything inside
@@ -310,11 +327,11 @@ function watchDirectory(dir, onChange) {
310
327
  });
311
328
  watcher.on("error", (err) => {
312
329
  const hint = errorCode(err) === "ENOSPC" ? " The inotify watch limit was reached — raise the fs.inotify max_user_watches sysctl." : "";
313
- notify("error", `File watcher error: ${errorMessage(err)}.${hint} Auto-restart on file changes may have stopped; restart \`aai dev\` after fixing.`);
330
+ notify("error", `File watcher error: ${errorMessage$1(err)}.${hint} Auto-restart on file changes may have stopped; restart \`aai dev\` after fixing.`);
314
331
  });
315
332
  watcher.on("all", () => {
316
333
  debouncedChange().catch((err) => {
317
- notify("error", `Watch handler failed: ${errorMessage(err)}`);
334
+ notify("error", `Watch handler failed: ${errorMessage$1(err)}`);
318
335
  });
319
336
  });
320
337
  return watcher;
@@ -324,25 +341,48 @@ function watchDirectory(dir, onChange) {
324
341
  * is unit-testable: `/websocket` MUST proxy with `ws: true` or `aai dev`
325
342
  * with a `client.tsx` serves a page whose WebSocket never connects.
326
343
  *
344
+ * **This table is the whole agent API as the browser can see it**, which is
345
+ * the thing to hold in mind before adding a route to `createServer`. Vite owns
346
+ * the port the user is told to open and answers everything not listed here
347
+ * itself — with a bare 404 carrying none of the agent server's headers, so the
348
+ * failure looks like a missing route rather than a missing proxy entry.
349
+ *
350
+ * `/workflows` is why that matters beyond voice. A WORKFLOW APP
351
+ * (`workflowApp()`) has no session and no socket: `page()` mounts a
352
+ * form and every single thing it does — listing workflows, starting a run,
353
+ * polling it, streaming its events — is a same-origin `fetch` under that
354
+ * prefix. Unproxied, the two workflow-app templates were dead on arrival under
355
+ * `aai dev` (`404 POST /workflows/runs` the instant the form is submitted)
356
+ * while the backend served the API correctly one port over. A string key
357
+ * prefix-matches, so this one entry covers `/runs`, `/runs/:id` and the
358
+ * `/runs/:id/events` SSE stream.
359
+ *
327
360
  * `strictPort` because the reported URL is `http://localhost:<port>` —
328
361
  * without it, Vite silently binds port+N when the port is busy and the
329
362
  * printed/JSON-returned URL points at whatever else was listening.
363
+ *
364
+ * `AAI_DEV_HOST` reaches BOTH servers. Binding only the backend left Vite —
365
+ * the port the user is told to open — on loopback, i.e. failing exactly the
366
+ * case that variable exists for (`aai dev` in a container, reached from the host).
330
367
  */
331
368
  function viteDevConfig(cwd, vitePort, backendPort) {
332
369
  const target = `http://localhost:${backendPort}`;
333
370
  return {
334
371
  root: cwd,
335
372
  plugins: [fallbackHtmlPlugin(cwd)],
373
+ resolve: { dedupe: DEDUPED_PEERS },
336
374
  server: {
337
375
  port: vitePort,
338
376
  strictPort: true,
377
+ ...omitUndefined({ host: devBindHost() }),
339
378
  proxy: {
340
379
  "/health": target,
341
380
  "/client-config": target,
342
381
  "/websocket": {
343
382
  target,
344
383
  ws: true
345
- }
384
+ },
385
+ [WORKFLOW_API_PREFIX]: target
346
386
  }
347
387
  }
348
388
  };
@@ -360,22 +400,38 @@ async function startDevServer(opts) {
360
400
  const clientDirOpt = hasClient ? {} : { clientDir: defaultClientDir() };
361
401
  const evaluateWorker = createWorkerEvaluator();
362
402
  const devLogger = createDevLogger(outputSilenced());
403
+ let workflowWorldStarted = false;
363
404
  /** Full build sequence, shared by initial startup and every restart. */
364
405
  async function buildServer() {
365
- const agentDef = await loadAgentDef(cwd, evaluateWorker);
406
+ const worker = await loadWorker(cwd, evaluateWorker);
407
+ const agentDef = worker.agent;
366
408
  const env = await resolveAgentEnv(cwd, agentDef);
409
+ const world = configureWorkflowWorld({
410
+ databaseUrl: env.DATABASE_URL,
411
+ port: backendPort,
412
+ dataDir: path.join(cwd, ".workflow-data")
413
+ });
414
+ publishStepEnv(env);
415
+ const workflows = await createWorkflowSurface(worker.workflowCode, worker.stepCode);
416
+ if (workflows && !workflowWorldStarted) {
417
+ workflowWorldStarted = true;
418
+ await startWorkflowWorldIfDeclared(true, world);
419
+ }
367
420
  const providerEnv = withHostCredentialFallback(env);
368
421
  return createServer({
369
422
  runtime: createRuntime({
370
423
  agent: agentDef,
371
424
  env,
372
425
  providerEnv,
373
- logger: devLogger
426
+ logger: devLogger,
427
+ publicUrl: process.env.PUBLIC_URL?.trim() || `http://localhost:${backendPort}`
374
428
  }),
375
429
  name: agentDef.name,
376
430
  env: hostModeEnv(providerEnv),
377
431
  hostBaseAgent: agentDef,
378
432
  greeting: agentDef.greeting,
433
+ ...omitUndefined({ page: agentDef.page }),
434
+ request: (req, res, url, method) => handleWorkflowRequest(workflows, req, res, url, method),
379
435
  ...clientDirOpt
380
436
  });
381
437
  }
@@ -402,7 +458,7 @@ async function startDevServer(opts) {
402
458
  viteServer = await createViteServer(viteDevConfig(cwd, vitePort, backendPort));
403
459
  await viteServer.listen();
404
460
  viteServer.httpServer?.on("error", (err) => {
405
- notify("error", `Vite dev server error: ${errorMessage(err)}`);
461
+ notify("error", `Vite dev server error: ${errorMessage$1(err)}`);
406
462
  });
407
463
  }
408
464
  supervisor.adopt(initialServer);
@@ -9,6 +9,7 @@
9
9
  import type { AgentDef } from "@alexkroman1/aai";
10
10
  import { type Logger } from "@alexkroman1/aai/runtime";
11
11
  import { type FSWatcher } from "chokidar";
12
+ import { type EvaluatedWorker } from "./_bundler.ts";
12
13
  /**
13
14
  * Warnings about the agent's credentials, computed against the `.env`-derived
14
15
  * env and the shell. Pure so it is directly testable; `resolveAgentEnv` logs
@@ -22,7 +23,7 @@ import { type FSWatcher } from "chokidar";
22
23
  * it at all: custom keys never fall back to the shell, so a shell export
23
24
  * can't mask one that would be missing both here and after deploy.
24
25
  */
25
- export declare function agentEnvWarnings(agentDef: Pick<AgentDef, "stt" | "llm" | "tts" | "s2s" | "requiredEnv">, env: Record<string, string>, shellEnv?: Record<string, string | undefined>): string[];
26
+ export declare function agentEnvWarnings(agentDef: Pick<AgentDef, "stt" | "llm" | "tts" | "s2s" | "requiredEnv" | "page">, env: Record<string, string>, shellEnv?: Record<string, string | undefined>): string[];
26
27
  /**
27
28
  * Load the agent definition by bundling agent.ts (and all its local imports)
28
29
  * into a single ESM file, then importing that. A raw `import(agent.ts?t=...)`
@@ -35,8 +36,15 @@ export declare function agentEnvWarnings(agentDef: Pick<AgentDef, "stt" | "llm"
35
36
  * errors in the agent's code propagate — the restart loop reports them and
36
37
  * keeps the old server. Evaluation goes through the memoizing evaluator so
37
38
  * a no-op save doesn't leak another module into the ESM registry.
39
+ *
40
+ * The project's `workflows/` directory is compiled by the same pass deploy
41
+ * runs (`buildWorkflows`) and rides the bundle as two string exports, which is
42
+ * how `aai dev` serves a workflow at all: nothing here hands the bundle to a
43
+ * guest, so the CLI is both ends of that contract locally. A project with no
44
+ * `workflows/` directory pays nothing — `buildWorkflows` resolves `undefined`
45
+ * without starting a builder.
38
46
  */
39
- export declare function loadAgentDef(cwd: string, evaluate: (code: string) => Promise<AgentDef>): Promise<AgentDef>;
47
+ export declare function loadWorker(cwd: string, evaluate: (code: string) => Promise<EvaluatedWorker>): Promise<EvaluatedWorker>;
40
48
  /**
41
49
  * True for paths that should never trigger a restart: anything inside
42
50
  * `node_modules/` and any dot-entry (`.git/`, `.aai/`, `.DS_Store`, …).
@@ -78,9 +86,29 @@ export type DevServerOptions = {
78
86
  * is unit-testable: `/websocket` MUST proxy with `ws: true` or `aai dev`
79
87
  * with a `client.tsx` serves a page whose WebSocket never connects.
80
88
  *
89
+ * **This table is the whole agent API as the browser can see it**, which is
90
+ * the thing to hold in mind before adding a route to `createServer`. Vite owns
91
+ * the port the user is told to open and answers everything not listed here
92
+ * itself — with a bare 404 carrying none of the agent server's headers, so the
93
+ * failure looks like a missing route rather than a missing proxy entry.
94
+ *
95
+ * `/workflows` is why that matters beyond voice. A WORKFLOW APP
96
+ * (`workflowApp()`) has no session and no socket: `page()` mounts a
97
+ * form and every single thing it does — listing workflows, starting a run,
98
+ * polling it, streaming its events — is a same-origin `fetch` under that
99
+ * prefix. Unproxied, the two workflow-app templates were dead on arrival under
100
+ * `aai dev` (`404 POST /workflows/runs` the instant the form is submitted)
101
+ * while the backend served the API correctly one port over. A string key
102
+ * prefix-matches, so this one entry covers `/runs`, `/runs/:id` and the
103
+ * `/runs/:id/events` SSE stream.
104
+ *
81
105
  * `strictPort` because the reported URL is `http://localhost:<port>` —
82
106
  * without it, Vite silently binds port+N when the port is busy and the
83
107
  * printed/JSON-returned URL points at whatever else was listening.
108
+ *
109
+ * `AAI_DEV_HOST` reaches BOTH servers. Binding only the backend left Vite —
110
+ * the port the user is told to open — on loopback, i.e. failing exactly the
111
+ * case that variable exists for (`aai dev` in a container, reached from the host).
84
112
  */
85
113
  export declare function viteDevConfig(cwd: string, vitePort: number, backendPort: number): import("vite").InlineConfig;
86
114
  /**
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Shared helpers for the e2e test suites (e2e*.test.ts): CLI build, mock
3
+ * registry setup, dependency installation, and process/server utilities.
4
+ * Each e2e suite performs its own setup/teardown using these helpers.
5
+ */
6
+ import type { ChildProcess } from "node:child_process";
7
+ import type { MockRegistry } from "./_mock-registry.ts";
8
+ export declare const dir: string;
9
+ export declare const packagesDir: string;
10
+ export declare const pm: "pnpm" | "npm" | "yarn";
11
+ export declare function aaiEnv(): NodeJS.ProcessEnv;
12
+ export declare function aai(aaiBin: string, args: string[], cwd: string, timeoutMs?: number): void;
13
+ /**
14
+ * Build the CLI and return the path to the built binary.
15
+ *
16
+ * Runs the package's own `build` script rather than bare `tsdown`, because
17
+ * the build is two steps now: tsdown, then `bundle-templates.mjs` copying the
18
+ * templates into `dist/`. Calling the bundler directly produced a `dist/`
19
+ * that no published tarball ever looks like — one with no templates in it.
20
+ */
21
+ export declare function buildCli(): string;
22
+ /**
23
+ * Copy a built `dist/` somewhere with no pnpm-workspace.yaml above it, so the
24
+ * CLI resolves templates the way an npm-installed copy does.
25
+ *
26
+ * `getMonorepoRoot()` keys off the *module's* location, so a build run from
27
+ * `packages/aai-cli/dist` always finds the workspace root and takes the
28
+ * monorepo branch — no test running in-tree can reach the bundled branch,
29
+ * which is the only one real users hit.
30
+ */
31
+ export declare function detachedCli(aaiBin: string, into: string): string;
32
+ /**
33
+ * Start a mock npm registry and publish workspace packages to it.
34
+ * Packages are built + published inside startMockRegistry, so consumers
35
+ * (npm/pnpm/yarn install) resolve them exactly as they would from the real registry.
36
+ */
37
+ export declare function startRegistry(): Promise<MockRegistry>;
38
+ /** Poll a health endpoint, capturing child stderr for diagnostics on timeout. */
39
+ export declare function waitForHealth(url: string, child?: ChildProcess, timeoutMs?: number): Promise<void>;
40
+ /** Wait for a child process to exit (for clean teardown). */
41
+ export declare function waitForExit(child: ChildProcess, timeoutMs?: number): Promise<void>;
42
+ /**
43
+ * Install dependencies using the mock registry.
44
+ *
45
+ * **`ignore-scripts` is set HERE and nowhere else, and that placement is
46
+ * load-bearing.** It exists to stop a linked package's `postinstall` running
47
+ * during THIS install — but `npm_config_ignore_scripts` is read by every npm
48
+ * invocation, so while it sat in `aaiEnv()` it silently suppressed every
49
+ * lifecycle script in every spawned command. That took out `npm start`'s
50
+ * `prestart`, i.e. the build the self-hosted entrypoint cannot boot without: the
51
+ * child printed `> start` with no `> prestart` above it and died on the missing
52
+ * artifact, naming a project file rather than the env var that skipped the step.
53
+ *
54
+ * The trap generalizes past this repo — an install-time protection that is
55
+ * really a global one — and the second half is worse than the failure: had the
56
+ * artifact happened to exist from an earlier build, the test would have PASSED
57
+ * while never running the script it exists to exercise.
58
+ */
59
+ /**
60
+ * Whether a failed install may be excused as the mock registry's npmjs
61
+ * passthrough failing rather than as a real dependency-resolution break.
62
+ *
63
+ * **`AAI_REQUIRE_REGISTRY` turns every excuse off**, and CI sets it — the same
64
+ * shape as `AAI_REQUIRE_PG`, for the same reason. A skip predicate over ERROR TEXT
65
+ * is a guess, and this tier is where a broken `exports` map surfaces: with three
66
+ * of four tests able to self-skip, a genuine break whose message happens to
67
+ * contain a matched substring would report green in the one job that could have
68
+ * caught it. Locally the excuse stays, because a developer behind a proxy has no
69
+ * way to make the passthrough work and a hard failure there is just noise.
70
+ *
71
+ * The predicate itself is narrowed to TRANSPORT-level failures. `fetch failed`
72
+ * and `network` are gone: both are substrings a real 404 can carry, which made
73
+ * them the two patterns most likely to excuse the thing this suite exists to
74
+ * find. verdaccio maps a failed upstream fetch to a plain 404, so
75
+ * `ERR_PNPM_FETCH_*` on a THIRD-PARTY package still counts — but never one naming
76
+ * our own scope, since those live in verdaccio's local storage and failing to
77
+ * resolve one means the published packages are actually broken.
78
+ */
79
+ export declare function isRegistryProxyFailure(err: unknown): boolean;
80
+ export declare function installDeps(registry: MockRegistry, projectDir: string): void;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Fault mode: run a test's dev server as a child that gets HARD-KILLED and
3
+ * restarted at declared points, so a suite can be run against a process that
4
+ * keeps dying under it.
5
+ *
6
+ * `AAI_FAULT_PROFILE=<name>` turns it on for every test that boots its server
7
+ * through {@link startSupervisedDevServer}; unset, the helper is an ordinary
8
+ * spawn and nothing about a suite changes. That is the whole shape of the
9
+ * feature: one env var covers the suite, and the off path has no supervisor in
10
+ * it at all.
11
+ *
12
+ * ## Why the kill is a SIGKILL, and why that is the only faithful option
13
+ *
14
+ * A graceful stop lets graphile-worker's runner release the queue locks it
15
+ * holds. That is exactly the difference that decides whether an in-flight step
16
+ * is redelivered, so a fault mode built on SIGTERM would exercise the recovery
17
+ * path that already works and never the one that does not. Measured: one hard
18
+ * kill of the process (or of its Postgres) strands every locked step until
19
+ * graphile-worker's own `interval '4 hours'` reclaim, with the run sitting
20
+ * `running` and a page showing "Working…" forever.
21
+ *
22
+ * ## Why there is no seed and no PRNG
23
+ *
24
+ * "Consistent fault mode" is the requirement, and the cheapest way to be
25
+ * consistent is to have nothing to reproduce: a profile is an ORDERED LIST of
26
+ * fault points keyed on logical events, so the Nth kill lands after the same
27
+ * observed event on every machine at every speed. Wall-clock kills are what
28
+ * `tmp/transcribe-load/chaos.mjs` does, and they are why its runs cannot be
29
+ * compared to each other.
30
+ *
31
+ * Randomized exploration is a different job with a different tool: this repo
32
+ * drives every randomized suite with fast-check (see "Property tests run on
33
+ * fast-check" in the root guide) precisely so nobody hand-rolls a seventh PRNG.
34
+ * A fault mode that grew a seed would be that seventh. If the fault SCHEDULE
35
+ * should be explored rather than declared, that belongs in a fast-check
36
+ * property over this module's plan type, not in an env var here.
37
+ *
38
+ * ## Triggers are log lines, and the plan is CHECKED
39
+ *
40
+ * A fault point names a pattern the server writes and which occurrence to fire
41
+ * on. Keying on log output couples this to strings the server happens to print,
42
+ * which is a real cost and is accepted for one reason: the alternative is a
43
+ * fault hook in production code, and a test-only mechanism should not be able to
44
+ * fire in production at all.
45
+ *
46
+ * The cost is paid down by {@link SupervisedServer.assertPlanConsumed}, which
47
+ * THROWS when a declared fault point never fired. Without it a renamed log line
48
+ * turns the entire mode into a no-op and the suite passes "under faults" having
49
+ * injected none — the failure this repo keeps paying for (a gate that reports
50
+ * success while checking nothing). Call it before asserting anything else.
51
+ */
52
+ /**
53
+ * One kill, described by what the server DID rather than by when — the two
54
+ * kinds being a line it printed and a boot it completed.
55
+ *
56
+ * `downMs` holds the process down before the restart on either. The default is
57
+ * 0, i.e. restart immediately, because the fault under test is the DEATH rather
58
+ * than the downtime.
59
+ *
60
+ * Both kinds exist because a log trigger cannot reach the boot. `aai dev`
61
+ * announces itself through `log.success`, which JSON mode SILENCES — and JSON
62
+ * mode is what the e2e suite runs (and what a pipe auto-selects), so a
63
+ * boot-level profile keyed on a startup line matched nothing and the mode
64
+ * injected no faults at all. Workflow lines are unaffected: the agent server's
65
+ * logger writes straight to stderr rather than through `log`, so
66
+ * `"Workflow run started"` survives JSON mode. `afterHealthy` needs no log at
67
+ * all, which makes it the right kind for anything about the process lifecycle.
68
+ */
69
+ export type FaultPoint = {
70
+ /** A substring of the log line to fire after. */
71
+ after: string;
72
+ /** Which occurrence of `after` to fire on. 1-based; defaults to the first. */
73
+ nth?: number;
74
+ /** Milliseconds to stay down. Defaults to 0. */
75
+ downMs?: number;
76
+ afterHealthy?: never;
77
+ } | {
78
+ /**
79
+ * Fire once the server has answered `/health` this many times — the first
80
+ * boot is 1, the generation after one restart is 2.
81
+ */
82
+ afterHealthy: number;
83
+ downMs?: number;
84
+ after?: never;
85
+ nth?: never;
86
+ };
87
+ export type FaultProfile = {
88
+ description: string;
89
+ points: readonly FaultPoint[];
90
+ };
91
+ /**
92
+ * The declared profiles.
93
+ *
94
+ * Named scenarios rather than knobs, so a suite run says what it exercised and
95
+ * a failure names something a reader can picture. Add one here rather than
96
+ * assembling a plan at a call site — a plan that lives in one test cannot be
97
+ * run across the suite, which is the point of the mode.
98
+ */
99
+ export declare const FAULT_PROFILES: Readonly<Record<string, FaultProfile>>;
100
+ /**
101
+ * The profile `AAI_FAULT_PROFILE` names, or undefined when the mode is off.
102
+ *
103
+ * An unknown name THROWS naming the declared set rather than degrading to off:
104
+ * a typo would otherwise produce a green run that injected nothing, which is
105
+ * indistinguishable from a healthy one and is the failure this whole module is
106
+ * careful about.
107
+ */
108
+ export declare function resolveFaultProfile(name?: string | undefined): FaultProfile | undefined;
109
+ export type SupervisedServer = {
110
+ /** Origin the server answers on, stable across restarts (the port is fixed). */
111
+ url: string;
112
+ /**
113
+ * How many times the process has been killed and is BACK — incremented once
114
+ * the replacement answers `/health`, so polling this to `n` means the nth
115
+ * replacement is serving.
116
+ */
117
+ restarts: () => number;
118
+ /**
119
+ * Resolve once every declared fault point has fired AND the surviving
120
+ * generation answers `/health`.
121
+ *
122
+ * This is the point a test should assert from: it is the only moment at which
123
+ * "the faults happened and the server is back" is true, and without it a
124
+ * request races the restart window — so every test would have to be written
125
+ * retry-aware to survive a profile it does not know about. Resolves
126
+ * immediately when the mode is off.
127
+ *
128
+ * Throws on timeout, naming the points that never fired — the same shortfall
129
+ * {@link assertPlanConsumed} reports, surfaced at the moment a test waited for
130
+ * it rather than at teardown.
131
+ */
132
+ awaitSettled: (timeoutMs?: number) => Promise<void>;
133
+ /**
134
+ * Throw unless every declared fault point fired.
135
+ *
136
+ * For a test whose SUBJECT is the profile. A test merely running under one
137
+ * should call {@link awaitSettled} instead — a profile whose triggers that
138
+ * test never produces (a step-level trigger in a test that runs no workflow)
139
+ * would otherwise fail it for something that is not a bug.
140
+ */
141
+ assertPlanConsumed: () => void;
142
+ /** Every line the server wrote, across all generations. */
143
+ lines: () => readonly string[];
144
+ stop: () => Promise<void>;
145
+ };
146
+ export type SupervisedDevServerOptions = {
147
+ /** Absolute path to the CLI entry (`bin.mjs`). */
148
+ aaiBin: string;
149
+ /** The project directory to run in. */
150
+ cwd: string;
151
+ /** A fixed port: the URL has to survive a restart. */
152
+ port: number;
153
+ env: NodeJS.ProcessEnv;
154
+ /** Extra `aai dev` arguments. */
155
+ args?: readonly string[];
156
+ /** Defaults to `resolveFaultProfile()`. Pass one to drive the mode from a test. */
157
+ profile?: FaultProfile | undefined;
158
+ /**
159
+ * How long a restarted server has to answer `/health`. Defaults to
160
+ * {@link HEALTH_TIMEOUT_MS}.
161
+ *
162
+ * A test seam, and one worth having: the ONLY way to exercise "the server did
163
+ * not survive a kill this mode inflicted" is to let that wait expire, and at
164
+ * the shipped 60s that is a minute of wall clock per assertion.
165
+ */
166
+ healthTimeoutMs?: number | undefined;
167
+ };
168
+ /**
169
+ * Boot `aai dev` as a child and, under a fault profile, keep killing it.
170
+ *
171
+ * With no profile this is a plain spawn plus a health wait — the same thing the
172
+ * e2e suite did inline — so converting a test to it changes nothing until the
173
+ * env var is set.
174
+ */
175
+ export declare function startSupervisedDevServer(opts: SupervisedDevServerOptions): Promise<SupervisedServer>;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { c as readJson, d as writeJson, s as isEexist } from "./_utils-0DHbcqUb.mjs";
3
- import { r as isDevMode, t as getMonorepoRoot } from "./_agent-C8Jk-ZcP.mjs";
4
- import { REPO_URL, downloadAndMergeTemplate } from "./_templates-B1kt3BGt.mjs";
2
+ import { c as isEexist, l as readJson, p as writeJson } from "./_utils-B8QmtFhK.mjs";
3
+ import { r as isDevMode, t as getMonorepoRoot } from "./_agent-vGW5SBnX.mjs";
4
+ import { REPO_URL, downloadAndMergeTemplate } from "./_templates-iDGjGiPf.mjs";
5
5
  import path from "node:path";
6
6
  import fs from "node:fs/promises";
7
7
  //#region _init.ts
@@ -45,7 +45,14 @@ function ok(data) {
45
45
  data
46
46
  };
47
47
  }
48
- /** Create an error result. */
48
+ /**
49
+ * Create an error result.
50
+ *
51
+ * `CommandResult<never>`, not `CommandResult<T>`: a failure carries no `data`,
52
+ * so the parameter was inferred as `unknown` at all ten call sites and named
53
+ * a type this value can never hold. `never` widens into any `CommandResult<T>`
54
+ * the caller declares, so `return fail(...)` still type-checks everywhere.
55
+ */
49
56
  function fail(code, error, hint) {
50
57
  return hint ? {
51
58
  ok: false,
package/dist/_output.d.ts CHANGED
@@ -40,8 +40,15 @@ export declare function writeLine(line: string): Promise<void>;
40
40
  export declare function installStdoutGuard(stream?: NodeJS.WriteStream): void;
41
41
  /** Create an ok result. */
42
42
  export declare function ok<T>(data: T): CommandResult<T>;
43
- /** Create an error result. */
44
- export declare function fail<T>(code: string, error: string, hint?: string): CommandResult<T>;
43
+ /**
44
+ * Create an error result.
45
+ *
46
+ * `CommandResult<never>`, not `CommandResult<T>`: a failure carries no `data`,
47
+ * so the parameter was inferred as `unknown` at all ten call sites and named
48
+ * a type this value can never hold. `never` widens into any `CommandResult<T>`
49
+ * the caller declares, so `return fail(...)` still type-checks everywhere.
50
+ */
51
+ export declare function fail(code: string, error: string, hint?: string): CommandResult<never>;
45
52
  /** Typed CLI error that carries a structured error code and optional hint. */
46
53
  export declare class CliError extends Error {
47
54
  readonly code: string;
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { r as errorCode } from "./_utils-0DHbcqUb.mjs";
2
+ import { i as errorCode } from "./_utils-B8QmtFhK.mjs";
3
3
  import path from "node:path";
4
4
  import { parseEnv } from "node:util";
5
5
  import fs from "node:fs/promises";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { n as getServerInfo } from "./_agent-C8Jk-ZcP.mjs";
3
- import { n as apiRequest, t as HINT_NOT_DEPLOYED } from "./_api-client-BX8Qv51n.mjs";
2
+ import { n as getServerInfo } from "./_agent-vGW5SBnX.mjs";
3
+ import { n as apiRequest, t as HINT_NOT_DEPLOYED } from "./_api-client-LgLksMhN.mjs";
4
4
  //#region _slug-api.ts
5
5
  /**
6
6
  * Authenticated request against a deployed agent's slug-scoped resource
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { n as apiRequest } from "./_api-client-BX8Qv51n.mjs";
2
+ import { a as isStringArray, i as checkedResponse, n as apiRequest } from "./_api-client-LgLksMhN.mjs";
3
3
  import path from "node:path";
4
- import { MAX_SLUG_LENGTH, PREVIEW_SLUG_SUFFIX, VALID_SLUG_RE } from "@alexkroman1/aai/utils";
4
+ import { MAX_SLUG_LENGTH, PREVIEW_SLUG_SUFFIX, VALID_SLUG_RE, isRecord } from "@alexkroman1/aai/utils";
5
5
  import { slugifyName } from "@alexkroman1/aai/slugify";
6
6
  import { isLocalOnlyFile, snapshotWorkspaceFiles } from "@alexkroman1/aai/workspace-files";
7
7
  //#region _studio.ts
@@ -61,7 +61,7 @@ function listStudioProjects(serverUrl, apiKey) {
61
61
  return apiRequest(`${serverUrl}/studio/projects`, {
62
62
  apiKey,
63
63
  action: "list"
64
- }).then((res) => res.projects);
64
+ }).then((res) => checkedResponse(res, (value) => isRecord(value) && isStringArray(value.projects), `the studio project list at ${serverUrl}`).projects);
65
65
  }
66
66
  /** Fetch a project, or null when it doesn't exist (the push existence probe). */
67
67
  function fetchStudioProject(serverUrl, apiKey, project) {