@alexkroman1/aai-cli 5.14.0 → 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 (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 +13 -9
  231. package/dist/build-DQJ1ZSm9.mjs +0 -39
@@ -6,28 +6,31 @@
6
6
  },
7
7
  "scripts": {
8
8
  "dev": "aai dev",
9
+ "prestart": "aai build --skip-tests",
9
10
  "start": "node server.mjs",
10
11
  "build": "aai build",
11
12
  "test": "aai test",
12
13
  "publish:agent": "aai publish"
13
14
  },
14
15
  "dependencies": {
15
- "@alexkroman1/aai": "^5.14.0",
16
- "@alexkroman1/aai-ui": "^5.14.0",
16
+ "@alexkroman1/aai": "^6.1.0",
17
+ "@alexkroman1/aai-ui": "^6.1.0",
18
+ "@workflow/world-postgres": "4.3.3",
17
19
  "react": "^19.2.8",
18
20
  "react-dom": "^19.2.8",
19
21
  "tailwindcss": "^4.0.0",
22
+ "workflow": "4.8.2",
20
23
  "zod": "^4.4.3"
21
24
  },
22
25
  "devDependencies": {
23
- "@alexkroman1/aai-cli": "^5.14.0",
26
+ "@alexkroman1/aai-cli": "^6.1.0",
24
27
  "@tailwindcss/vite": "^4.3.3",
25
- "@types/node": "^26.1.1",
26
- "@types/react": "^19.2.17",
27
- "@types/react-dom": "^19.2.3",
28
- "@vitejs/plugin-react": "^6.0.4",
28
+ "@types/node": "^26.2.0",
29
+ "@types/react": "^19.2.18",
30
+ "@types/react-dom": "^19.2.4",
31
+ "@vitejs/plugin-react": "^6.0.5",
29
32
  "typescript": "^7.0.2",
30
- "vite": "^8.1.5",
33
+ "vite": "^8.2.1",
31
34
  "vitest": "^4.1.10"
32
35
  }
33
36
  }
@@ -7,9 +7,17 @@
7
7
  # build script without this entry. pnpm 10 reads `onlyBuiltDependencies`;
8
8
  # pnpm 11 renamed it to `allowBuilds` and ignores the old key, so both are
9
9
  # declared. This file also marks the project as its own pnpm workspace root.
10
+ # `@swc/core` and `cbor-extract` arrive TRANSITIVELY, through the `workflow`
11
+ # dependency the DevKit templates need. pnpm 11 makes an undeclared build script
12
+ # a hard error (`ERR_PNPM_IGNORED_BUILDS`), so leaving them out fails the install
13
+ # — which is the very first thing `aai init` does after writing these files.
10
14
  onlyBuiltDependencies:
15
+ - "@swc/core"
16
+ - cbor-extract
11
17
  - esbuild
12
18
  allowBuilds:
19
+ "@swc/core": true
20
+ cbor-extract: true
13
21
  esbuild: true
14
22
 
15
23
  # Exempt our own packages from pnpm's release-age quarantine, and ONLY ours.
@@ -1,22 +1,21 @@
1
1
  // Self-hosted entrypoint — this is what `npm start` runs.
2
2
  //
3
3
  // It serves the agent over HTTP + WebSocket from your own Node process: no
4
- // platform account, no CLI at run time, no bundler. `aai dev` is the
5
- // development counterpart (file watching, Vite, a browser that opens itself);
6
- // this file is the deployment.
4
+ // platform account, no managed anything. `aai dev` is the development
5
+ // counterpart (file watching, Vite, a browser that opens itself); this file is
6
+ // the deployment.
7
7
  //
8
8
  // npm start # http://127.0.0.1:3000
9
9
  // PORT=8080 HOST=0.0.0.0 npm start # bind every interface, e.g. in a container
10
10
  //
11
11
  // Anything that can run Node can host it: copy the project, install
12
- // dependencies, provide the secrets, run this file. Deleting it costs nothing
13
- // — `aai dev`, `aai publish` and the managed platform never read it.
12
+ // dependencies, provide the secrets, run `npm start`. Deleting this file costs
13
+ // nothing — `aai dev`, `aai publish` and the managed platform never read it.
14
14
 
15
- import { existsSync, readFileSync } from "node:fs";
15
+ import { existsSync } from "node:fs";
16
16
  import { readFile } from "node:fs/promises";
17
- import { registerHooks } from "node:module";
18
17
  import path from "node:path";
19
- import { fileURLToPath } from "node:url";
18
+ import { fileURLToPath, pathToFileURL } from "node:url";
20
19
  import { parseEnv } from "node:util";
21
20
  import { createAgentServer, withHostCredentialFallback } from "@alexkroman1/aai/runtime";
22
21
  import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
@@ -24,56 +23,38 @@ import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
24
23
  const root = path.dirname(fileURLToPath(import.meta.url));
25
24
 
26
25
  /**
27
- * Teach Node the two non-JavaScript import shapes the `aai` bundler supports,
28
- * so one `agent.ts` runs unchanged under `aai dev`, `aai publish`, and here.
26
+ * The built agent `.aai/worker.mjs`, produced by `aai build`, which the
27
+ * `prestart` script runs for you. It is the SAME artifact `aai publish` uploads
28
+ * and the managed platform runs, so a self-hosted agent and a deployed one
29
+ * cannot behave differently.
29
30
  *
30
- * - `import prompt from "./system-prompt.md?raw"` a Vite convention; Node
31
- * would look for a file literally named `system-prompt.md?raw`.
32
- * - `import data from "./data.json"` with no import attribute TypeScript's
33
- * `resolveJsonModule` allows it; Node requires `with { type: "json" }` and
34
- * otherwise fails with ERR_IMPORT_ATTRIBUTE_MISSING. An import that DOES
35
- * carry the attribute is left to Node, whose own handling is correct.
31
+ * Loading the build rather than `agent.ts` directly is what makes `tools/`
32
+ * work. A tool is registered by EXISTING — its file name is the name the model
33
+ * calls, and nothing anywhere lists them and the only place a directory can
34
+ * be turned into modules is where the bundle is assembled: a deployed agent is
35
+ * handed one ESM string and has no filesystem to scan. So the bundler
36
+ * enumerates `tools/` and emits the imports, and every loader that skips it
37
+ * would serve an agent with no tools at all.
36
38
  *
37
- * Nothing is transformed beyond that: `.ts` itself needs no help, because Node
38
- * strips the types natively (this project needs Node 24+). That is why there
39
- * is no build step here, and no second copy of the agent in JavaScript that
40
- * could drift from the one you deploy.
39
+ * It also settles two things the bundler already resolves, and this file used
40
+ * to re-implement with `node:module` hooks: `import prompt from
41
+ * "./system-prompt.md?raw"` (a Vite convention Node looks for a file
42
+ * literally named `system-prompt.md?raw`) and `import data from "./data.json"`
43
+ * with no import attribute (TypeScript's `resolveJsonModule` allows it, Node
44
+ * requires `with { type: "json" }`). Both are inlined into the bundle, so
45
+ * there is nothing left to teach Node.
41
46
  */
42
- registerHooks({
43
- resolve(specifier, context, nextResolve) {
44
- if (specifier.startsWith(".") && specifier.endsWith("?raw")) {
45
- // Resolved by hand rather than through nextResolve: the default
46
- // resolver has no format for `.md` and the query would be lost anyway.
47
- return {
48
- url: new URL(specifier, context.parentURL).href,
49
- format: "module",
50
- shortCircuit: true,
51
- };
52
- }
53
- return nextResolve(specifier, context);
54
- },
55
- load(url, context, nextLoad) {
56
- const asRaw = url.endsWith("?raw");
57
- const asBareJson = url.endsWith(".json") && context.importAttributes?.type !== "json";
58
- if (!(asRaw || asBareJson)) return nextLoad(url, context);
59
- const text = readFileSync(fileURLToPath(asRaw ? url.slice(0, -"?raw".length) : url), "utf-8");
60
- return {
61
- format: "module",
62
- // `export default <literal>` for both: a JSON document is already a
63
- // valid JS expression, and JSON.stringify makes any file safe to embed
64
- // as a string. Emitting `format: "json"` instead would put the import
65
- // back under the attribute check this exists to satisfy.
66
- source: `export default ${asRaw ? JSON.stringify(text) : text};`,
67
- shortCircuit: true,
68
- };
69
- },
70
- });
71
-
72
- // Imported dynamically, and this is load-bearing: static `import` statements
73
- // are hoisted and evaluated BEFORE any statement in this file, so an
74
- // `import agent from "./agent.ts"` at the top would load the agent — and every
75
- // `?raw` import inside it — before the hooks above were ever registered.
76
- const { default: agent } = await import("./agent.ts");
47
+ const workerPath = path.join(root, ".aai", "worker.mjs");
48
+ if (!existsSync(workerPath)) {
49
+ console.error(
50
+ `No built agent at ${path.relative(root, workerPath)}.\n` +
51
+ "Run `npm run build` (or `aai build`) first `npm start` normally does it for you.",
52
+ );
53
+ process.exit(1);
54
+ }
55
+ // A file: URL rather than a relative specifier, so the path is correct on
56
+ // Windows, where a bare POSIX-looking path is not a valid module specifier.
57
+ const { default: agent } = await import(pathToFileURL(workerPath).href);
77
58
 
78
59
  /** Parse a dotenv-syntax file into a record; `{}` when it does not exist. */
79
60
  async function readEnvFile(file) {
@@ -116,8 +97,8 @@ async function resolveAgentEnv() {
116
97
 
117
98
  /**
118
99
  * Static assets served at `/`: this project's own UI once `client.tsx` has been
119
- * built (`npm run build` leaves it in `.aai/client`), otherwise the prebuilt
120
- * default client that ships inside @alexkroman1/aai-ui.
100
+ * built (the same `aai build` leaves it in `.aai/client`), otherwise the
101
+ * prebuilt default client that ships inside @alexkroman1/aai-ui.
121
102
  */
122
103
  function resolveClientDir() {
123
104
  const built = path.join(root, ".aai", "client");
@@ -130,6 +111,20 @@ function resolveClientDir() {
130
111
 
131
112
  const env = await resolveAgentEnv();
132
113
 
114
+ /**
115
+ * Where THIS server is reachable from outside — `PUBLIC_URL`, e.g.
116
+ * `https://agent.example.com`. Set it whenever a durable workflow has to hand a
117
+ * URL to somebody else: `ctx.workflows.publicWebhookUrl(token)` is built from it,
118
+ * and without it that call throws rather than minting a `http://localhost:3000`
119
+ * URL that a payment provider will try, days later, and fail.
120
+ *
121
+ * Nothing else reads it, so it is not a startup requirement: an agent with no
122
+ * webhooks needs no value here. It is deliberately NOT derived from `PORT`/`HOST`
123
+ * — those describe the socket this process binds, which behind a reverse proxy is
124
+ * not what the outside world dials.
125
+ */
126
+ const publicUrl = process.env.PUBLIC_URL?.trim();
127
+
133
128
  const server = createAgentServer({
134
129
  agent,
135
130
  env,
@@ -138,6 +133,7 @@ const server = createAgentServer({
138
133
  // ASSEMBLYAI_API_KEY to a container. Anything in `env` still wins.
139
134
  providerEnv: withHostCredentialFallback(env),
140
135
  clientDir: resolveClientDir(),
136
+ ...(publicUrl ? { publicUrl } : {}),
141
137
  });
142
138
 
143
139
  // Loopback by default: this server has no request authentication of its own,
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok, n as fail } from "./_output-CC300DzW.mjs";
2
+ import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
3
3
  import { n as log, s as unwrapCancel } from "./_ui-u7T4YooX.mjs";
4
- import { t as secretRequest } from "./_slug-api-D30zPmsn.mjs";
4
+ import { a as isStringArray, i as checkedResponse } from "./_api-client-LgLksMhN.mjs";
5
+ import { t as secretRequest } from "./_slug-api-D2heILuC.mjs";
6
+ import { isRecord } from "@alexkroman1/aai/utils";
5
7
  import * as p from "@clack/prompts";
6
8
  import { text } from "node:stream/consumers";
7
9
  //#region secret.ts
@@ -46,7 +48,8 @@ async function executeSecretDelete(cwd, name, server) {
46
48
  return ok({ name });
47
49
  }
48
50
  async function executeSecretList(cwd, server) {
49
- const { data: { vars } } = await secretRequest(cwd, "", { action: "secret" }, server);
51
+ const { data, target } = await secretRequest(cwd, "", { action: "secret" }, server);
52
+ const { vars } = checkedResponse(data, (value) => isRecord(value) && isStringArray(value.vars), `the secret list for ${target}`);
50
53
  if (vars.length === 0) log.info("No secrets set. Use `aai secret put <name>` to add one.");
51
54
  else {
52
55
  log.message(`${vars.length} secret${vars.length === 1 ? "" : "s"}:`);
@@ -1,17 +1,29 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok, n as fail } from "./_output-CC300DzW.mjs";
2
+ import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
3
3
  import { n as log } from "./_ui-u7T4YooX.mjs";
4
- import { n as slugRequest } from "./_slug-api-D30zPmsn.mjs";
4
+ import { i as checkedResponse } from "./_api-client-LgLksMhN.mjs";
5
+ import { n as slugRequest } from "./_slug-api-D2heILuC.mjs";
6
+ import { isRecord } from "@alexkroman1/aai/utils";
5
7
  import * as p from "@clack/prompts";
6
8
  //#region storage.ts
9
+ /**
10
+ * The storage route's answer, CHECKED — every caller here reads `enabled` and
11
+ * a body without it reported "Storage is disabled for <slug>" (and returned
12
+ * `enabled: undefined` to a script) for a server that never said so. See
13
+ * `checkedResponse`.
14
+ */
7
15
  async function storageRequest(cwd, init, server) {
8
- return slugRequest(cwd, "/storage", {
16
+ const { data, slug } = await slugRequest(cwd, "/storage", {
9
17
  ...init,
10
18
  action: "storage"
11
19
  }, server);
20
+ return {
21
+ enabled: checkedResponse(data, (value) => isRecord(value) && typeof value.enabled === "boolean", `the storage route for ${slug}`).enabled,
22
+ slug
23
+ };
12
24
  }
13
25
  async function executeStorageStatus(cwd, server) {
14
- const { data: { enabled }, slug } = await storageRequest(cwd, void 0, server);
26
+ const { enabled, slug } = await storageRequest(cwd, void 0, server);
15
27
  if (enabled) log.info(`Storage is enabled for ${slug}`);
16
28
  else log.info(`Storage is disabled for ${slug}. Use \`aai storage enable\` to turn it on.`);
17
29
  return ok({
@@ -20,7 +32,7 @@ async function executeStorageStatus(cwd, server) {
20
32
  });
21
33
  }
22
34
  async function executeStorageEnable(cwd, server) {
23
- const { data: { enabled }, slug } = await storageRequest(cwd, { method: "POST" }, server);
35
+ const { enabled, slug } = await storageRequest(cwd, { method: "POST" }, server);
24
36
  log.success(`Storage enabled for ${slug}`);
25
37
  log.info("Tool code can now use ctx.db.query(sql, params).");
26
38
  return ok({
@@ -43,7 +55,7 @@ async function executeStorageDisable(cwd, opts = {}) {
43
55
  return fail("cancelled", "Disable cancelled");
44
56
  }
45
57
  }
46
- const { data: { enabled }, slug } = await storageRequest(cwd, { method: "DELETE" }, opts.server);
58
+ const { enabled, slug } = await storageRequest(cwd, { method: "DELETE" }, opts.server);
47
59
  log.success(`Storage disabled for ${slug} — database schema and data dropped`);
48
60
  return ok({
49
61
  slug,
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok, t as CliError } from "./_output-CC300DzW.mjs";
2
+ import { a as ok, t as CliError } from "./_output-CKkmWs7i.mjs";
3
3
  import { n as log, t as fmtUrl } from "./_ui-u7T4YooX.mjs";
4
- import { s as updateProjectConfig } from "./_config-CzwAWOWr.mjs";
5
- import { t as resolveServerEnv } from "./_server-common-61fL__39.mjs";
6
- import { i as resolveDeployTarget } from "./_agent-C8Jk-ZcP.mjs";
7
- import { layerScaffold } from "./_templates-B1kt3BGt.mjs";
8
- import { n as apiRequest } from "./_api-client-BX8Qv51n.mjs";
9
- import { a as publishStudioProject, i as projectNameFromDir, n as fetchStudioProject, o as pushStudioSource, r as listStudioProjects, s as studioProjectUrl, t as collectSourceFiles } from "./_studio-D2ciIhbi.mjs";
4
+ import { s as updateProjectConfig } from "./_config-D_s09e7g.mjs";
5
+ import { t as resolveServerEnv } from "./_server-common-6e0QI4mq.mjs";
6
+ import { a as resolveDeployTarget } from "./_agent-vGW5SBnX.mjs";
7
+ import { layerScaffold } from "./_templates-iDGjGiPf.mjs";
8
+ import { i as checkedResponse, n as apiRequest } from "./_api-client-LgLksMhN.mjs";
9
+ import { a as publishStudioProject, i as projectNameFromDir, n as fetchStudioProject, o as pushStudioSource, r as listStudioProjects, s as studioProjectUrl, t as collectSourceFiles } from "./_studio-zAJycZ8j.mjs";
10
10
  import path from "node:path";
11
+ import { isRecord } from "@alexkroman1/aai/utils";
11
12
  import { mkdir, readdir, writeFile } from "node:fs/promises";
12
13
  //#region studio.ts
13
14
  /**
@@ -158,7 +159,7 @@ async function syncEnvSecrets(cwd, serverUrl, apiKey, project) {
158
159
  }
159
160
  async function executePublish(opts) {
160
161
  if (!opts.skipTypecheck) {
161
- const { assertTypechecks } = await import("./_typecheck-gate-4v9UBZHh.mjs");
162
+ const { assertTypechecks } = await import("./_typecheck-gate-BXvUNnfs.mjs");
162
163
  await assertTypechecks(opts.cwd);
163
164
  }
164
165
  const pushed = await pushProject(opts);
@@ -166,8 +167,7 @@ async function executePublish(opts) {
166
167
  const hadSlug = pushed.slug !== void 0;
167
168
  if (pushed.slug) await syncEnvSecrets(opts.cwd, serverUrl, apiKey, project);
168
169
  log.step(`Publishing ${project} (builds in the project's sandbox)…`);
169
- const result = await publishStudioProject(serverUrl, apiKey, project);
170
- if (typeof result?.slug !== "string" || typeof result?.output !== "string") throw new CliError("bad_publish_response", `Unexpected response from the publish route at ${serverUrl}.`, "Check that --server points at an aai platform server, then try again.");
170
+ const result = checkedResponse(await publishStudioProject(serverUrl, apiKey, project), (value) => isRecord(value) && typeof value.slug === "string" && typeof value.output === "string", `the publish route at ${serverUrl}`);
171
171
  if (result.output.trim()) log.message(result.output.trim());
172
172
  await updateProjectConfig(opts.cwd, {
173
173
  serverUrl,
@@ -1,9 +1,7 @@
1
1
  import { agent } from "@alexkroman1/aai";
2
- import systemPrompt from "./system-prompt.md?raw";
3
2
 
4
3
  export default agent({
5
4
  name: "Coda",
6
- systemPrompt,
7
5
  greeting:
8
6
  "Hey, I'm Coda. I solve problems by writing and running code. Try asking me something like, what's the 50th fibonacci number, or what day of the week was January 1st 2000.",
9
7
  builtinTools: ["run_code"],
@@ -1,20 +1,18 @@
1
1
  import type { ToolContext } from "@alexkroman1/aai";
2
2
  import { createToolContext } from "@alexkroman1/aai/testing";
3
3
  import { describe, expect, test } from "vitest";
4
- import { dispatchSlot, type StateSlot } from "./shared.ts";
5
- import { incidentCreate } from "./tools/incident_create.ts";
6
- import { incidentEscalate } from "./tools/incident_escalate.ts";
7
- import { incidentTriage } from "./tools/incident_triage.ts";
8
- import { incidentUpdateStatus } from "./tools/incident_update_status.ts";
9
- import { resourcesDispatch } from "./tools/resources_dispatch.ts";
10
- import { resourcesUpdateStatus } from "./tools/resources_update_status.ts";
11
-
12
- /** The dispatch board lives in ctx.state, one per session and
13
- * `createToolContext` gives each call its own session id, so two contexts are
14
- * two boards by construction. */
15
- function makeCtx(): { ctx: ToolContext<StateSlot> } {
16
- return { ctx: createToolContext<StateSlot>() };
17
- }
4
+ import { dispatchSlot } from "./shared.ts";
5
+ import incidentCreate from "./tools/incident_create.ts";
6
+ import incidentEscalate from "./tools/incident_escalate.ts";
7
+ import incidentTriage from "./tools/incident_triage.ts";
8
+ import incidentUpdateStatus from "./tools/incident_update_status.ts";
9
+ import resourcesDispatch from "./tools/resources_dispatch.ts";
10
+ import resourcesUpdateStatus from "./tools/resources_update_status.ts";
11
+
12
+ /** The dispatch board lives in a session slot, and `createToolContext` gives
13
+ * each call its own slot store so two contexts are two boards by
14
+ * construction. */
15
+ const makeCtx = (): ToolContext => createToolContext();
18
16
 
19
17
  async function createIncidentFor(
20
18
  ctx: ToolContext,
@@ -29,7 +27,7 @@ async function createIncidentFor(
29
27
 
30
28
  describe("dispatch-center template", () => {
31
29
  test("resolving an incident does not yank a reassigned unit off its new incident", async () => {
32
- const { ctx } = makeCtx();
30
+ const ctx = makeCtx();
33
31
 
34
32
  const inc1 = await createIncidentFor(ctx);
35
33
  await resourcesDispatch.execute({ incidentId: inc1, callsigns: ["Medic-1"] }, ctx);
@@ -51,7 +49,7 @@ describe("dispatch-center template", () => {
51
49
  });
52
50
 
53
51
  test("callsigns: ['auto'] triggers auto-dispatch as the description promises", async () => {
54
- const { ctx } = makeCtx();
52
+ const ctx = makeCtx();
55
53
  const incidentId = await createIncidentFor(ctx, "cardiac arrest, patient not breathing");
56
54
 
57
55
  const result = (await resourcesDispatch.execute({ incidentId, callsigns: ["auto"] }, ctx)) as {
@@ -64,7 +62,7 @@ describe("dispatch-center template", () => {
64
62
  });
65
63
 
66
64
  test("concurrent tool calls are serialized — no lost updates", async () => {
67
- const { ctx } = makeCtx();
65
+ const ctx = makeCtx();
68
66
 
69
67
  // Parallel tool calls in one LLM turn run concurrently. The per-session
70
68
  // mutex in updateState makes each one run against the previous one's
@@ -102,7 +100,7 @@ describe("dispatch-center template", () => {
102
100
  });
103
101
 
104
102
  test("mutual-aid units get unique ids and callsigns across escalations", async () => {
105
- const { ctx } = makeCtx();
103
+ const ctx = makeCtx();
106
104
  const incidentId = await createIncidentFor(ctx);
107
105
 
108
106
  await incidentEscalate.execute(
@@ -122,7 +120,7 @@ describe("dispatch-center template", () => {
122
120
  });
123
121
 
124
122
  test("resolved is terminal: no escalation, re-resolution, or dispatch", async () => {
125
- const { ctx } = makeCtx();
123
+ const ctx = makeCtx();
126
124
  const incidentId = await createIncidentFor(ctx);
127
125
  await incidentUpdateStatus.execute({ incidentId, status: "resolved" }, ctx);
128
126
 
@@ -1,21 +1,10 @@
1
1
  import { agent } from "@alexkroman1/aai";
2
2
  import { dashboardView, dispatchSlot } from "./shared.ts";
3
- import systemPrompt from "./system-prompt.md?raw";
4
- import { incidentAddNote } from "./tools/incident_add_note.ts";
5
- import { incidentCreate } from "./tools/incident_create.ts";
6
- import { incidentEscalate } from "./tools/incident_escalate.ts";
7
- import { incidentGet } from "./tools/incident_get.ts";
8
- import { incidentTriage } from "./tools/incident_triage.ts";
9
- import { incidentUpdateStatus } from "./tools/incident_update_status.ts";
10
- import { opsDashboard } from "./tools/ops_dashboard.ts";
11
- import { opsProtocols } from "./tools/ops_protocols.ts";
12
- import { opsRunScenario } from "./tools/ops_run_scenario.ts";
13
- import { resourcesDispatch } from "./tools/resources_dispatch.ts";
14
- import { resourcesGetAvailable } from "./tools/resources_get_available.ts";
15
- import { resourcesUpdateStatus } from "./tools/resources_update_status.ts";
16
3
 
17
4
  export default agent({
18
5
  name: "Dispatch Command Center",
6
+ // The board exists before the first tool call, so a resumed connection has
7
+ // something to project rather than an empty state object.
19
8
  // One projection replaces eleven `ctx.send("incidents", ...)` calls, and
20
9
  // is the single place that decides caller PII stays server-side.
21
10
  syncState: dispatchSlot.projection(dashboardView),
@@ -23,26 +12,10 @@ export default agent({
23
12
  // one message ("unit twelve … respond to"). The default pipeline's
24
13
  // `max_turn_silence` already tolerates that; reach for
25
14
  // `assemblyAIStt({ maxTurnSilenceMs })` only if your callers pause longer.
26
- systemPrompt,
27
15
  greeting:
28
16
  "Dispatch Command Center online. Restoring operational state. I'm ready to take incoming calls, manage active incidents, or run dispatch operations. Say 'dashboard' for a full status report. What do we have.",
29
17
 
30
18
  // The system prompt instructs the model to use web_search and run_code, so
31
19
  // they must be enabled here — the default builtin set does not include them.
32
20
  builtinTools: ["think", "remember", "recall", "calculate", "web_search", "run_code"],
33
-
34
- tools: {
35
- incident_add_note: incidentAddNote,
36
- incident_create: incidentCreate,
37
- incident_escalate: incidentEscalate,
38
- incident_get: incidentGet,
39
- incident_triage: incidentTriage,
40
- incident_update_status: incidentUpdateStatus,
41
- ops_dashboard: opsDashboard,
42
- ops_protocols: opsProtocols,
43
- ops_run_scenario: opsRunScenario,
44
- resources_dispatch: resourcesDispatch,
45
- resources_get_available: resourcesGetAvailable,
46
- resources_update_status: resourcesUpdateStatus,
47
- },
48
21
  });
@@ -1,6 +1,12 @@
1
1
  import "@alexkroman1/aai-ui/styles.css";
2
- import type { ChatMessage } from "@alexkroman1/aai-ui";
3
- import { AutoScroll, client, useAgentState, useSession } from "@alexkroman1/aai-ui";
2
+ import type { AgentState, ChatMessage } from "@alexkroman1/aai-ui";
3
+ import {
4
+ AutoScroll,
5
+ client,
6
+ useAgentState,
7
+ useSession,
8
+ useUserTranscript,
9
+ } from "@alexkroman1/aai-ui";
4
10
  import type { DashboardView, DispatchState, IncidentSummary, Severity, Status } from "./shared.ts";
5
11
  import { dashboardView, dispatchSlot } from "./shared.ts";
6
12
 
@@ -55,17 +61,24 @@ const statusColors: Record<string, string> = {
55
61
  // new DashboardView field can't silently miss the pre-first-tool-call render.
56
62
  const EMPTY_DASH: DashboardView = dispatchSlot.projection(dashboardView)(undefined);
57
63
 
58
- function stateColor(state: string): string {
59
- return state === "listening"
60
- ? "#22c55e"
61
- : state === "thinking"
62
- ? "#eab308"
63
- : state === "speaking"
64
- ? "#3b82f6"
65
- : state === "ready"
66
- ? "#22c55e"
67
- : "#6b7280";
68
- }
64
+ // The dot's colour per session state, as an EXHAUSTIVE map rather than an
65
+ // if-chain with a grey default.
66
+ //
67
+ // The palette is this template's own — every client here paints the same six
68
+ // states in its own colours, so the SDK has nothing to share but the union. What
69
+ // the SDK does own is `AgentState`, and `satisfies Record<AgentState, string>`
70
+ // is what borrows it: a state added there stops compiling here, where the
71
+ // `state === ""` chain this replaced answered a new state with a silent grey
72
+ // badge in three separate files and no way to notice.
73
+ const STATE_COLORS = {
74
+ disconnected: "#6b7280",
75
+ connecting: "#6b7280",
76
+ ready: "#22c55e",
77
+ listening: "#22c55e",
78
+ thinking: "#eab308",
79
+ speaking: "#3b82f6",
80
+ error: "#6b7280",
81
+ } satisfies Record<AgentState, string>;
69
82
 
70
83
  function Panel({ title, children }: { title: string; children: React.ReactNode }) {
71
84
  return (
@@ -132,6 +145,10 @@ function IncidentCard({ inc }: { inc: IncidentSummary }) {
132
145
 
133
146
  function App() {
134
147
  const session = useSession();
148
+ // `speaking` and `text` in place of a `userTranscript !== null` check: `null`
149
+ // is silence and `""` is speech detected with no words back yet, and reading
150
+ // them as one falsy value hides the indicator at the start of every turn.
151
+ const { speaking, text: userTranscript } = useUserTranscript();
135
152
  // The agent's own board, projected by `syncState`. This replaced a
136
153
  // useState mirror that merged incident deltas out of tool events — the
137
154
  // projection is already the complete list, so there is nothing to merge.
@@ -169,7 +186,7 @@ function App() {
169
186
  <span
170
187
  className="w-2.5 h-2.5 rounded-full inline-block"
171
188
  style={{
172
- background: stateColor(session.state),
189
+ background: STATE_COLORS[session.state],
173
190
  animation:
174
191
  session.state === "listening"
175
192
  ? "dc-pulse 1.5s ease-in-out infinite"
@@ -249,7 +266,7 @@ function App() {
249
266
  ))}
250
267
  </AutoScroll>
251
268
 
252
- {session.userTranscript !== null && (
269
+ {speaking && (
253
270
  <div
254
271
  className="flex items-center px-4 py-2 text-xs italic min-h-8"
255
272
  style={{ background: "#111827", borderTop: "1px solid #1e293b", color: "#64748b" }}
@@ -258,7 +275,7 @@ function App() {
258
275
  className="w-2.5 h-2.5 rounded-full inline-block mr-2"
259
276
  style={{ background: "#22c55e", animation: "dc-pulse 1.5s ease-in-out infinite" }}
260
277
  />
261
- {session.userTranscript === "" ? "..." : session.userTranscript}
278
+ {userTranscript}
262
279
  </div>
263
280
  )}
264
281
  {session.error && (