@alexkroman1/aai-cli 13.3.0 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/dist/{_agent-Clbq3a4h.mjs → _agent-BzUeqOdj.mjs} +32 -9
  2. package/dist/_agent.d.ts +23 -0
  3. package/dist/_artifacts-BJOYGQPp.mjs +21 -0
  4. package/dist/_artifacts.d.ts +16 -0
  5. package/dist/_build-target.d.ts +172 -0
  6. package/dist/{_bundler-DEkjO1d8.mjs → _bundler-DM0d0M7m.mjs} +8 -7
  7. package/dist/_cli-common.d.ts +45 -6
  8. package/dist/{_config-ZY2euhdh.mjs → _config-DYzC6WMD.mjs} +2 -2
  9. package/dist/_dev-env.d.ts +1 -1
  10. package/dist/{_dev-server-C13xuGB1.mjs → _dev-server-BzWB6-4y.mjs} +25 -24
  11. package/dist/_dev-vite-config.d.ts +3 -3
  12. package/dist/_e2e-test-utils.d.ts +1 -1
  13. package/dist/{_init-Ci4QGjp8.mjs → _init-Bsi3DZNJ.mjs} +9 -23
  14. package/dist/{_output-DbJt9gsl.mjs → _output-DBP9Op_d.mjs} +22 -13
  15. package/dist/_output.d.ts +10 -9
  16. package/dist/{_preflight-B2laWPdU.mjs → _preflight-CcAR577V.mjs} +2 -2
  17. package/dist/_resource-commands.d.ts +9 -9
  18. package/dist/_server-common-De0haHr9.mjs +70 -0
  19. package/dist/_server-common.d.ts +20 -1
  20. package/dist/{_slug-api-DlKpj91X.mjs → _slug-api-CGvc2YSQ.mjs} +18 -9
  21. package/dist/_slug-api.d.ts +19 -0
  22. package/dist/{_studio-DTi3EdXQ.mjs → _studio-DnR_BqFp.mjs} +7 -3
  23. package/dist/_studio-commands.d.ts +13 -13
  24. package/dist/{_templates-V4l5sZJy.mjs → _templates-CIlJ3Vay.mjs} +4 -4
  25. package/dist/_templates.d.ts +1 -1
  26. package/dist/{_typecheck-gate-sV4N5F93.mjs → _typecheck-gate-U3jcil8L.mjs} +3 -2
  27. package/dist/_typecheck-gate.d.ts +3 -1
  28. package/dist/{_utils-Dhd058D9.mjs → _utils-D5JGcjiW.mjs} +43 -1
  29. package/dist/_utils.d.ts +35 -0
  30. package/dist/_vercel-output.d.ts +63 -0
  31. package/dist/{_workflow-determinism-C9l8uC2R.mjs → _workflow-determinism-CvpqMpdh.mjs} +18 -26
  32. package/dist/_workflow-determinism.d.ts +3 -3
  33. package/dist/build-BhEaxBPu.mjs +481 -0
  34. package/dist/build.d.ts +23 -10
  35. package/dist/cli.d.ts +1 -2
  36. package/dist/cli.mjs +155 -138
  37. package/dist/{client-bundler-CT-_26c5.mjs → client-bundler-6mTLs6ny.mjs} +5 -5
  38. package/dist/client-bundler.d.ts +1 -1
  39. package/dist/client-bundler.mjs +1 -1
  40. package/dist/{delete-RqlQZ6Y_.mjs → delete-BdFnbnVR.mjs} +4 -4
  41. package/dist/{deploy-B7f3CK7v.mjs → deploy-CGqPU5U-.mjs} +11 -11
  42. package/dist/{dev-DamXieW_.mjs → dev-Bx9gYBHM.mjs} +3 -3
  43. package/dist/{eval-h3DTVfRw.mjs → eval-B3I7FqN9.mjs} +3 -3
  44. package/dist/{init-D-vFrV8h.mjs → init-CFyusRbq.mjs} +48 -20
  45. package/dist/init.d.ts +14 -0
  46. package/dist/{login-CDiemNmC.mjs → login-DaLrPOFg.mjs} +4 -6
  47. package/dist/{logs-DGwPNCzB.mjs → logs-7BRwq0cG.mjs} +21 -14
  48. package/dist/project-config.mjs +1 -1
  49. package/dist/scaffold/CLAUDE.md +155 -76
  50. package/dist/scaffold/package.json +6 -6
  51. package/dist/{secret-V1w7y9-j.mjs → secret-Cjp912yF.mjs} +5 -5
  52. package/dist/secret.d.ts +8 -6
  53. package/dist/start.d.ts +112 -0
  54. package/dist/start.mjs +156 -0
  55. package/dist/{studio-BpMCfUNx.mjs → studio-C_zuRC_z.mjs} +13 -15
  56. package/dist/templates/briefing-desk/agent.eval.test.ts +156 -0
  57. package/dist/templates/call-audit/agent.test.ts +11 -6
  58. package/dist/templates/call-audit/agent.ts +2 -2
  59. package/dist/templates/call-audit/client.tsx +27 -42
  60. package/dist/templates/call-audit/workflows/audit.ts +7 -5
  61. package/dist/templates/call-audit/workflows/ingest.ts +7 -7
  62. package/dist/templates/call-audit/workflows/media.ts +12 -37
  63. package/dist/templates/call-audit/workflows/summarize.ts +8 -8
  64. package/dist/templates/call-audit/workflows/sync-api.ts +3 -3
  65. package/dist/templates/code-interpreter/agent.test.ts +103 -0
  66. package/dist/templates/dispatch-center/agent.test.ts +7 -7
  67. package/dist/templates/dispatch-center/client.tsx +53 -61
  68. package/dist/templates/dispatch-center/shared.ts +0 -8
  69. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +4 -1
  70. package/dist/templates/embedded-assets/agent.eval.test.ts +4 -2
  71. package/dist/templates/health-assistant/agent.eval.test.ts +4 -2
  72. package/dist/templates/infocom-adventure/client.tsx +40 -36
  73. package/dist/templates/link-digest/agent.test.ts +3 -3
  74. package/dist/templates/link-digest/agent.ts +2 -2
  75. package/dist/templates/link-digest/client.tsx +81 -19
  76. package/dist/templates/link-digest/workflows/digest.ts +13 -16
  77. package/dist/templates/math-buddy/agent.test.ts +126 -0
  78. package/dist/templates/night-owl/agent.eval.test.ts +4 -2
  79. package/dist/templates/night-owl/client.tsx +8 -2
  80. package/dist/templates/personal-finance/agent.test.ts +127 -0
  81. package/dist/templates/pipeline-simple/agent.test.ts +2 -2
  82. package/dist/templates/pizza-ordering/client.tsx +5 -4
  83. package/dist/templates/pizza-ordering/shared.ts +3 -8
  84. package/dist/templates/pizza-ordering/tools/add_pizza.ts +3 -2
  85. package/dist/templates/pizza-ordering/tools/place_order.ts +3 -2
  86. package/dist/templates/pizza-ordering/tools/remove_pizza.ts +3 -2
  87. package/dist/templates/pizza-ordering/tools/update_pizza.ts +3 -2
  88. package/dist/templates/pizza-ordering/tools/view_order.ts +3 -2
  89. package/dist/templates/plan-and-execute/agent.test.ts +10 -5
  90. package/dist/templates/plan-and-execute/client.tsx +2 -2
  91. package/dist/templates/podcast-digest/agent.test.ts +20 -11
  92. package/dist/templates/podcast-digest/client.tsx +16 -22
  93. package/dist/templates/podcast-digest/workflows/digest.ts +21 -19
  94. package/dist/templates/podcast-digest/workflows/feeds.ts +61 -33
  95. package/dist/templates/podcast-digest/workflows/slack.ts +7 -7
  96. package/dist/templates/recap-workflow/agent.eval.test.ts +21 -13
  97. package/dist/templates/recap-workflow/agent.test.ts +27 -23
  98. package/dist/templates/recap-workflow/workflows/recap.ts +24 -28
  99. package/dist/templates/redline/agent.eval.test.ts +5 -8
  100. package/dist/templates/redline/agent.test.ts +1 -1
  101. package/dist/templates/redline/client.tsx +36 -34
  102. package/dist/templates/redline/workflows/redline.ts +17 -17
  103. package/dist/templates/research-workflow/agent.eval.test.ts +5 -5
  104. package/dist/templates/research-workflow/agent.test.ts +10 -7
  105. package/dist/templates/research-workflow/workflows/research.ts +28 -30
  106. package/dist/templates/retail/agent.test.ts +17 -15
  107. package/dist/templates/retail/cancel.ts +7 -6
  108. package/dist/templates/retail/client.tsx +39 -58
  109. package/dist/templates/retail/payment.ts +4 -3
  110. package/dist/templates/retail/pending.test.ts +19 -19
  111. package/dist/templates/retail/store.test.ts +5 -5
  112. package/dist/templates/retail/swap.ts +8 -11
  113. package/dist/templates/solo-rpg/agent.eval.test.ts +2 -16
  114. package/dist/templates/solo-rpg/agent.test.ts +12 -12
  115. package/dist/templates/solo-rpg/client.tsx +3 -3
  116. package/dist/templates/spoken-summary/agent.test.ts +9 -9
  117. package/dist/templates/spoken-summary/agent.ts +3 -3
  118. package/dist/templates/spoken-summary/client.tsx +23 -30
  119. package/dist/templates/spoken-summary/workflows/summarize.ts +11 -11
  120. package/dist/templates/spoken-summary/workflows/transcribe.ts +14 -14
  121. package/dist/templates/support-line/agent.ts +8 -0
  122. package/dist/templates/support-line/client.tsx +4 -3
  123. package/dist/templates/support-line/prompts.ts +0 -2
  124. package/dist/templates/transcription-workflow/agent.test.ts +20 -12
  125. package/dist/templates/transcription-workflow/agent.ts +2 -2
  126. package/dist/templates/transcription-workflow/client.tsx +15 -14
  127. package/dist/templates/transcription-workflow/recover.ts +1 -1
  128. package/dist/templates/transcription-workflow/run-panel.tsx +34 -14
  129. package/dist/templates/transcription-workflow/total-latency.tsx +46 -20
  130. package/dist/templates/transcription-workflow/workflows/batch.ts +15 -15
  131. package/dist/templates/transcription-workflow/workflows/downsample.ts +1 -1
  132. package/dist/templates/transcription-workflow/workflows/normalize.ts +15 -19
  133. package/dist/templates/transcription-workflow/workflows/stream.ts +12 -12
  134. package/dist/templates/transcription-workflow/workflows/sync-api.ts +3 -3
  135. package/dist/templates/transcription-workflow/workflows/transcribe.ts +23 -34
  136. package/dist/templates/travel-concierge/agent.eval.test.ts +2 -2
  137. package/dist/templates/travel-concierge/agent.test.ts +15 -7
  138. package/dist/templates/travel-concierge/client.tsx +2 -2
  139. package/dist/templates/travel-concierge/routing.ts +64 -55
  140. package/dist/templates/travel-concierge/shared.ts +10 -14
  141. package/dist/templates/travel-concierge/tools/cancel_action.ts +3 -1
  142. package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +3 -1
  143. package/dist/templates/travel-concierge/tools/confirm_action.ts +3 -1
  144. package/dist/templates/travel-concierge/tools/lookup_booking.ts +4 -3
  145. package/dist/templates/travel-concierge/tools/search_car_rentals.ts +3 -2
  146. package/dist/templates/travel-concierge/tools/search_excursions.ts +3 -2
  147. package/dist/templates/travel-concierge/tools/search_flights.ts +3 -2
  148. package/dist/templates/travel-concierge/tools/search_hotels.ts +3 -2
  149. package/dist/templates/web-researcher/agent.test.ts +130 -0
  150. package/dist/{test-guenJUxi.mjs → test-D032V2cA.mjs} +4 -17
  151. package/dist/typecheck.mjs +1 -1
  152. package/dist/worker-bundler.d.ts +1 -1
  153. package/dist/worker-bundler.mjs +8 -8
  154. package/dist/{workflow-BZyKLkhV.mjs → workflow-CVioyO3V.mjs} +6 -5
  155. package/package.json +9 -4
  156. package/dist/_server-common-BV4_CrP3.mjs +0 -43
  157. package/dist/build-CWyFGe-W.mjs +0 -69
  158. package/dist/scaffold/server.mjs +0 -204
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as serverOrigin, i as readProjectConfig, n as ensureApiKey, r as readGlobalConfig, t as approveServer } from "./_config-ZY2euhdh.mjs";
2
+ import { m as stripTrailingSlash } from "./_utils-D5JGcjiW.mjs";
3
+ import { a as serverOrigin, i as readProjectConfig, n as ensureApiKey, r as readGlobalConfig, t as approveServer } from "./_config-DYzC6WMD.mjs";
3
4
  import { existsSync } from "node:fs";
4
5
  import path from "node:path";
5
6
  import { fileURLToPath } from "node:url";
@@ -18,9 +19,6 @@ function isDevMode() {
18
19
  if (process.env.AAI_NO_DEV === "1") return false;
19
20
  return getMonorepoRoot() !== null;
20
21
  }
21
- function stripTrailingSlash(url) {
22
- return url.replace(/\/+$/, "");
23
- }
24
22
  /**
25
23
  * Whether `origin` may receive a credential without prior user approval.
26
24
  *
@@ -82,14 +80,39 @@ function assertValidConfigSlug(slug) {
82
80
  throw new Error(`Invalid slug in .aai/project.json: ${JSON.stringify(slug)}\n Expected lowercase letters, digits, \`-\`, \`_\` (2-64 chars). Fix or delete the file — \`aai publish\` will create a fresh deployment.`);
83
81
  }
84
82
  /**
85
- * Resolve everything needed to talk to the platform: project config (null if
86
- * the project has never been deployed), server URL, and API key.
83
+ * Everything `resolveDeployTarget` decides BEFORE a credential is involved:
84
+ * the project config (null when never deployed, or when `cwd` is null), the
85
+ * user's global config, and the trust-checked, approved server URL.
87
86
  */
88
- async function resolveDeployTarget(cwd, explicitServer) {
89
- const [config, globalConfig] = await Promise.all([readProjectConfig(cwd), readGlobalConfig()]);
87
+ async function resolveApprovedServer(cwd, explicitServer) {
88
+ const [config, globalConfig] = await Promise.all([cwd === null ? null : readProjectConfig(cwd), readGlobalConfig()]);
90
89
  const serverUrl = resolveServerUrl(explicitServer, config?.serverUrl, globalConfig.approvedServers ?? []);
91
90
  if (explicitServer) await approveServer(serverUrl);
92
91
  assertValidConfigSlug(config?.slug);
92
+ return {
93
+ config,
94
+ globalConfig,
95
+ serverUrl
96
+ };
97
+ }
98
+ /**
99
+ * Resolve everything needed to talk to the platform: project config (null if
100
+ * the project has never been deployed), server URL, and API key.
101
+ *
102
+ * `resolveApprovedServer` plus the key, rather than one function, because
103
+ * `aai login` needs the first half and cannot have the second — it is the
104
+ * command that PUTS the key on disk. It had its own copy of the
105
+ * read → `resolveServerUrl` → `approveServer` sequence, which is security
106
+ * policy ("passing `--server` is what approves an origin"), so a change to that
107
+ * policy landed here and silently missed the one command whose whole job is to
108
+ * write a credential for the origin in question. Same shape as the slug-guard
109
+ * incident `assertValidConfigSlug` was moved down here for.
110
+ *
111
+ * The key comes from the config document already in hand — `ensureApiKey` would
112
+ * read and parse the same file a second time, on every platform command.
113
+ */
114
+ async function resolveDeployTarget(cwd, explicitServer) {
115
+ const { config, serverUrl } = await resolveApprovedServer(cwd, explicitServer);
93
116
  return {
94
117
  config,
95
118
  serverUrl,
@@ -126,4 +149,4 @@ async function getServerInfo(cwd, explicitServer) {
126
149
  };
127
150
  }
128
151
  //#endregion
129
- export { resolveDeployTarget as a, requireDeployedSlug as i, getServerInfo as n, resolveServerUrl as o, isDevMode as r, getMonorepoRoot as t };
152
+ export { resolveApprovedServer as a, requireDeployedSlug as i, getServerInfo as n, resolveDeployTarget as o, isDevMode as r, getMonorepoRoot as t };
package/dist/_agent.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type GlobalConfig, type ProjectConfig } from "./_config.ts";
1
2
  export declare const DEFAULT_SERVER = "https://alexkroman--aai-server-web-server.modal.run";
2
3
  export declare const DEFAULT_DEV_SERVER = "http://localhost:8080";
3
4
  export declare function getMonorepoRoot(): string | null;
@@ -19,9 +20,31 @@ export declare function isDevMode(): boolean;
19
20
  * @param approvedOrigins - Origins from the user-owned global config.
20
21
  */
21
22
  export declare function resolveServerUrl(explicit?: string, configUrl?: string, approvedOrigins?: readonly string[]): string;
23
+ /**
24
+ * Everything `resolveDeployTarget` decides BEFORE a credential is involved:
25
+ * the project config (null when never deployed, or when `cwd` is null), the
26
+ * user's global config, and the trust-checked, approved server URL.
27
+ */
28
+ export declare function resolveApprovedServer(cwd: string | null, explicitServer?: string): Promise<{
29
+ config: ProjectConfig | null;
30
+ globalConfig: GlobalConfig;
31
+ serverUrl: string;
32
+ }>;
22
33
  /**
23
34
  * Resolve everything needed to talk to the platform: project config (null if
24
35
  * the project has never been deployed), server URL, and API key.
36
+ *
37
+ * `resolveApprovedServer` plus the key, rather than one function, because
38
+ * `aai login` needs the first half and cannot have the second — it is the
39
+ * command that PUTS the key on disk. It had its own copy of the
40
+ * read → `resolveServerUrl` → `approveServer` sequence, which is security
41
+ * policy ("passing `--server` is what approves an origin"), so a change to that
42
+ * policy landed here and silently missed the one command whose whole job is to
43
+ * write a credential for the origin in question. Same shape as the slug-guard
44
+ * incident `assertValidConfigSlug` was moved down here for.
45
+ *
46
+ * The key comes from the config document already in hand — `ensureApiKey` would
47
+ * read and parse the same file a second time, on every platform command.
25
48
  */
26
49
  export declare function resolveDeployTarget(cwd: string, explicitServer?: string): Promise<{
27
50
  config: {
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ //#region src/_artifacts.ts
4
+ /**
5
+ * The `.aai/` layout — where `aai build` leaves what `aai start` and the
6
+ * deployment targets read back.
7
+ *
8
+ * A LEAF module, and that is the whole reason it exists. These two paths were
9
+ * declared where they were first needed (`build.ts`, `start.ts`), which was
10
+ * fine while the readers were the writers. `_vercel-output.ts` needs both and
11
+ * is imported BY `build.ts`, so taking them from there is an import cycle —
12
+ * one Biome rejects, and one that would fail at runtime rather than at build
13
+ * time, since a `const` read across a cycle is a `ReferenceError` decided by
14
+ * import order. Both original homes re-export, so no published subpath moved.
15
+ */
16
+ /** Where `aai build` leaves the worker bundle, relative to the project root. */
17
+ const WORKER_ARTIFACT_REL = path.join(".aai", "worker.mjs");
18
+ /** Where `aai build` leaves the built browser client, relative to the root. */
19
+ const CLIENT_ARTIFACT_REL = path.join(".aai", "client");
20
+ //#endregion
21
+ export { WORKER_ARTIFACT_REL as n, CLIENT_ARTIFACT_REL as t };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The `.aai/` layout — where `aai build` leaves what `aai start` and the
3
+ * deployment targets read back.
4
+ *
5
+ * A LEAF module, and that is the whole reason it exists. These two paths were
6
+ * declared where they were first needed (`build.ts`, `start.ts`), which was
7
+ * fine while the readers were the writers. `_vercel-output.ts` needs both and
8
+ * is imported BY `build.ts`, so taking them from there is an import cycle —
9
+ * one Biome rejects, and one that would fail at runtime rather than at build
10
+ * time, since a `const` read across a cycle is a `ReferenceError` decided by
11
+ * import order. Both original homes re-export, so no published subpath moved.
12
+ */
13
+ /** Where `aai build` leaves the worker bundle, relative to the project root. */
14
+ export declare const WORKER_ARTIFACT_REL: string;
15
+ /** Where `aai build` leaves the built browser client, relative to the root. */
16
+ export declare const CLIENT_ARTIFACT_REL: string;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Deployment TARGETS — what `aai build` emits beside the worker so a host can
3
+ * run this project without the project holding anything host-specific.
4
+ *
5
+ * ## Why a target rather than a file in the scaffold
6
+ *
7
+ * Every host wants a different entry shape: Vercel wants a module whose default
8
+ * export is an `http.Server` it binds itself, a container wants a long-lived
9
+ * process, another platform wants something else again. Committing one of those
10
+ * to the scaffold makes every project assert a fact that is load-bearing on
11
+ * exactly one host — the same objection that removed `server.mjs`.
12
+ *
13
+ * Nitro is the worked precedent: one codebase, a preset per provider, and the
14
+ * preset EMITS the host's expected shape into the build directory. `node-server`
15
+ * is its default and Vercel/Netlify/Cloudflare are detected from the CI
16
+ * environment with no configuration. Next does the same narrower thing with
17
+ * `output: "standalone"` — it generates a `server.js` rather than asking anyone
18
+ * to write one. Either way the user's repository contains no host file, which is
19
+ * the property to preserve here.
20
+ *
21
+ * ## Auto-detection, and why it is safe
22
+ *
23
+ * A target is chosen with `--target`, or detected from the environment when the
24
+ * flag is absent. Detection reads the variables the hosts set on their own build
25
+ * containers ({@link TARGET_ENV_MARKERS}), so it only ever fires where the build
26
+ * is genuinely running on that host — a laptop sets none of them and gets
27
+ * {@link DEFAULT_BUILD_TARGET}, which emits nothing extra and is what every
28
+ * existing project already does.
29
+ */
30
+ /** The targets `aai build --target` accepts. */
31
+ export declare const BUILD_TARGETS: readonly ["node", "vercel"];
32
+ export type BuildTarget = (typeof BUILD_TARGETS)[number];
33
+ /**
34
+ * What a build with no `--target` and no host environment produces: the worker
35
+ * and the client, and nothing else.
36
+ *
37
+ * `node` rather than a `"none"` sentinel because it NAMES the deployment it
38
+ * serves — a long-lived process running `aai start` — which is what every
39
+ * container platform wants and what the scaffold's own `start` script runs.
40
+ */
41
+ export declare const DEFAULT_BUILD_TARGET: BuildTarget;
42
+ /**
43
+ * The environment variable each host sets on its own build container.
44
+ *
45
+ * `VERCEL` is set for every Vercel build and deployment. Detection is per host
46
+ * rather than a single "am I in CI" test, because a GitHub Action building a
47
+ * container image is CI too and wants the default.
48
+ */
49
+ export declare const TARGET_ENV_MARKERS: Readonly<Record<string, BuildTarget>>;
50
+ export declare function isBuildTarget(value: string): value is BuildTarget;
51
+ /**
52
+ * Resolve the target for this build: an explicit flag wins, then the
53
+ * environment, then {@link DEFAULT_BUILD_TARGET}.
54
+ *
55
+ * An unrecognised `--target` is REFUSED naming what is accepted, rather than
56
+ * falling back to the default — a typo'd target that silently built the default
57
+ * would deploy a project missing the entry its host needs, and the failure would
58
+ * arrive as a 404 from the platform rather than as an error from the build.
59
+ */
60
+ export declare function resolveBuildTarget(explicit: string | undefined, env?: Record<string, string | undefined>): BuildTarget;
61
+ /**
62
+ * Where a PREBUILT Vercel deployment lives, relative to the project root.
63
+ *
64
+ * The Build Output API rather than an `api/` entry plus a `vercel.json`, and
65
+ * the reason is ORDERING. Vercel reads `vercel.json` and decides what to build
66
+ * BEFORE it runs the build command, so a `vercel.json` that the build WRITES
67
+ * configures the NEXT deployment and not this one — a clean clone deploys with
68
+ * no rewrite and no function at all. The `api/` shape only ever appeared to
69
+ * work because a previous local `aai build --target vercel` had left both
70
+ * files in the working tree, which is a property of one laptop rather than of
71
+ * the repository. `.vercel/output/` is read AFTER the build command; it is the
72
+ * only place a build can describe its own deployment.
73
+ *
74
+ * Two more things fall out of owning the directory, both of which the `api/`
75
+ * shape got wrong and could not fix. The function bundle is ASSEMBLED here
76
+ * rather than traced, so `.aai/worker.mjs` — loaded through a dynamic
77
+ * `import(pathToFileURL(...))` that no static tracer can follow — and
78
+ * `.env.example` — the file that DECLARES which variables become `ctx.env` —
79
+ * are present because they were copied in. And the built client is served by
80
+ * the CDN out of `static/` instead of through the function.
81
+ *
82
+ * Nitro's vercel preset is the worked precedent and lands in exactly here:
83
+ * `output.dir = {{rootDir}}/.vercel/output`, `serverDir` a `.func` under it.
84
+ *
85
+ * @see https://vercel.com/docs/build-output-api/v3
86
+ */
87
+ export declare const VERCEL_OUTPUT_DIR: string;
88
+ /**
89
+ * The one function every request that is not a static file reaches.
90
+ *
91
+ * The Build Output API derives a function's ROUTE from its path, so the
92
+ * directory name IS a URL and must not collide with one the static output
93
+ * claims. **`index.func` collides**, which a deployment is the only way to
94
+ * find out: it is served at `/index`, and Vercel's directory index resolves
95
+ * `/` to the extensionless `/index` — so the function won `/`, every other
96
+ * asset came off the CDN correctly, and the home page 500'd on a deployment
97
+ * whose static output was perfect. Measured on a real preview:
98
+ * `/favicon.ico`, `/index.html` and both hashed `/assets/*` returned 200
99
+ * while `/` and `/index` did not.
100
+ *
101
+ * `__server` is Nitro's answer to the same problem (`__server.func`) and the
102
+ * reason is this one: a double-underscore prefix is not a path any bundler
103
+ * emits, so no static file can ever take the name.
104
+ */
105
+ export declare const VERCEL_FUNCTION_DIR: string;
106
+ /**
107
+ * The route {@link VERCEL_FUNCTION_DIR} is served at — its directory name
108
+ * without `.func`, which is how the Build Output API names a function.
109
+ */
110
+ export declare const VERCEL_FUNCTION_ROUTE = "/__server";
111
+ /** Static assets the Vercel CDN serves directly, never reaching the function. */
112
+ export declare const VERCEL_STATIC_DIR: string;
113
+ /** `nodejs<major>.x` for the Node running this build, clamped to what Vercel offers. */
114
+ export declare function vercelNodeRuntime(version?: string): string;
115
+ /**
116
+ * The Vercel function entry, emitted into {@link VERCEL_FUNCTION_DIR}.
117
+ *
118
+ * ## Why a `(req, res)` handler and not `export default server`
119
+ *
120
+ * `export default <http.Server>` is what Vercel's own `@vercel/node` BUILDER
121
+ * accepts, and it is the shape the previous `api/index.mjs` used. The Build
122
+ * Output API has no builder in the path: `launcherType: "Nodejs"` invokes the
123
+ * module's default export as a request handler, so the server never gets
124
+ * bound and there is nothing to raise an `upgrade` event on it.
125
+ *
126
+ * ## How a WebSocket survives that
127
+ *
128
+ * Vercel hands a Node function the raw upgrade through its PER-REQUEST
129
+ * context — `globalThis[Symbol.for("@vercel/request-context")].get()
130
+ * .upgradeWebSocket()` returns the `{ req, socket, head }` triple — rather
131
+ * than as an event. Nitro reaches it through `crossws/adapters/vercel`; here
132
+ * the adapter is three lines, because {@link AgentServer.node} is a real
133
+ * `http.Server` that already has both an `upgrade` and a `request` listener
134
+ * registered. Re-emitting onto it is the whole translation, and it means the
135
+ * deployed path through `server.ts` is the same one `aai dev` and `aai start`
136
+ * take — no second WebSocket entry point to keep in step.
137
+ *
138
+ * The `204` afterwards is what the launcher needs to consider the invocation
139
+ * finished; the socket the agent is now talking on is not this `res`.
140
+ *
141
+ * ## `import.meta.dirname`, not `process.cwd()`
142
+ *
143
+ * The function's working directory belongs to the platform, but `.aai/` and
144
+ * `.env.example` were copied in BESIDE this file. Resolving from the module
145
+ * keeps that a fact about the bundle rather than about how Vercel happens to
146
+ * invoke it.
147
+ */
148
+ export declare const VERCEL_ENTRY_SOURCE = "// Generated by `aai build --target vercel` \u2014 do not edit, and do not commit.\n// Vercel invokes this handler per request and delivers a WebSocket upgrade\n// through its request context. See @alexkroman1/aai-cli/start.\nimport { createProjectServer } from \"@alexkroman1/aai-cli/start\";\n\nconst server = (await createProjectServer({ cwd: import.meta.dirname })).node;\n\nconst REQUEST_CONTEXT = Symbol.for(\"@vercel/request-context\");\n\nexport default function handler(req, res) {\n if (req.method === \"GET\" && req.headers.upgrade?.toLowerCase() === \"websocket\") {\n const upgrade = globalThis[REQUEST_CONTEXT]?.get?.()?.upgradeWebSocket?.();\n if (upgrade) {\n server.emit(\"upgrade\", upgrade.req, upgrade.socket, upgrade.head);\n if (!res.headersSent && !res.writableEnded) {\n res.statusCode = 204;\n res.end();\n }\n return;\n }\n }\n server.emit(\"request\", req, res);\n}\n";
149
+ /**
150
+ * `.vc-config.json` — how the platform runs {@link VERCEL_ENTRY_SOURCE}.
151
+ *
152
+ * `supportsResponseStreaming` is not optional here: an agent streams TTS audio
153
+ * and SSE workflow events, and without it the platform buffers a response to
154
+ * completion, which for a stream that ends when the call does means it never
155
+ * arrives. `shouldAddHelpers` stays off — the entry speaks `node:http`, and
156
+ * the helpers exist to bolt Express-shaped sugar onto a handler that does not.
157
+ */
158
+ export declare function vercelFunctionConfigSource(runtime?: string): string;
159
+ /**
160
+ * `config.json` — the routing table, and the reason static assets stop paying
161
+ * for a function invocation.
162
+ *
163
+ * `{ "handle": "filesystem" }` serves anything present in
164
+ * {@link VERCEL_STATIC_DIR} from the CDN and only then falls through, so the
165
+ * client bundle, its assets and the worklets are edge-served while
166
+ * `/client-config`, `/websocket`, `/workflows/*` and the webhook route reach
167
+ * the agent. The `api/` shape routed EVERY request through the function, which
168
+ * this file's earlier revision noted as deliberate and "not what makes a
169
+ * deployment work or not" — true of correctness, false of cost, and free here
170
+ * because the Build Output API already separates the two directories.
171
+ */
172
+ export declare const VERCEL_BUILD_CONFIG_SOURCE: string;
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { f as validateAgentExport } from "./_utils-Dhd058D9.mjs";
3
- import { t as buildClient } from "./client-bundler-CT-_26c5.mjs";
2
+ import { h as validateAgentExport } from "./_utils-D5JGcjiW.mjs";
3
+ import { t as buildClient } from "./client-bundler-6mTLs6ny.mjs";
4
4
  import { buildWorker } from "./worker-bundler.mjs";
5
5
  import path from "node:path";
6
6
  import { pathToFileURL } from "node:url";
@@ -86,14 +86,15 @@ async function evalWorkerConfig(code) {
86
86
  * distinct-build leak is accepted for the reasons documented there.
87
87
  */
88
88
  function createWorkerEvaluator() {
89
- let lastHash;
90
- let lastWorker;
89
+ let last;
91
90
  return async (code) => {
92
91
  const codeHash = hash("sha256", code);
93
- if (lastWorker && codeHash === lastHash) return lastWorker;
92
+ if (last?.hash === codeHash) return last.worker;
94
93
  const worker = await evalWorkerBundle(code);
95
- lastHash = codeHash;
96
- lastWorker = worker;
94
+ last = {
95
+ hash: codeHash,
96
+ worker
97
+ };
97
98
  return worker;
98
99
  };
99
100
  }
@@ -17,6 +17,26 @@ export declare const sharedArgs: {
17
17
  readonly description: "Output JSON (auto-detected in non-TTY)";
18
18
  };
19
19
  };
20
+ /**
21
+ * What every PLATFORM command accepts — the pair, as a group to spread.
22
+ *
23
+ * `sharedArgs` supplies the field VALUES, so each of the twelve credentialed
24
+ * commands still restated the shape (`server: sharedArgs.server, json:
25
+ * sharedArgs.json`). A flag all of them should take is then twelve edits, and
26
+ * `assertKnownArgv` refuses the flag on whichever one was missed — after
27
+ * release. `cli-workflow.ts`'s `workflowArgs` is the same pattern for its group.
28
+ */
29
+ export declare const platformArgs: {
30
+ readonly server: {
31
+ readonly type: "string";
32
+ readonly alias: "s";
33
+ readonly description: "Platform server URL";
34
+ };
35
+ readonly json: {
36
+ readonly type: "boolean";
37
+ readonly description: "Output JSON (auto-detected in non-TTY)";
38
+ };
39
+ };
20
40
  /**
21
41
  * Flags in `rawArgs` that `argsDef` doesn't declare, in the form the user
22
42
  * typed them.
@@ -44,15 +64,34 @@ type AnyCommandDef = CommandDef<ArgsDef>;
44
64
  * the narrowing.
45
65
  */
46
66
  export declare function commandPath<T extends ArgsDef>(cmd: CommandDef<T>, parent?: CommandDef<T>): string;
67
+ /** What {@link resolveArgv} learned by walking the command tree once. */
68
+ export type ArgvSelection = {
69
+ /** `["aai", "secret", "put"]` — the path walked, for a `--help` sentence. */
70
+ named: string[];
71
+ /**
72
+ * The first non-flag token that named no subcommand where one was expected,
73
+ * i.e. a mistyped command. `undefined` when the walk reached a real command.
74
+ */
75
+ unknownCommand?: string;
76
+ /** Args declared by the command the argv selected. */
77
+ argsDef: ArgsDef;
78
+ /** argv from the first token that is not a subcommand — flags and positionals. */
79
+ rest: string[];
80
+ };
47
81
  /**
48
- * Unknown flags in `argv` for whichever (possibly nested) subcommand it
49
- * selects `[]` when everything is declared.
82
+ * Walk the real command tree for `argv` and report everything the pre-parse
83
+ * guards in `cli.ts` need: which command it selects, what that command
84
+ * declares, and whether a token mistyped a subcommand.
50
85
  *
51
- * Walks the real command tree rather than re-listing flags, so this cannot
52
- * drift from what the commands accept. An unknown SUBCOMMAND is not reported:
53
- * citty already answers that with usage text and a non-zero exit.
86
+ * ONE walk, because there used to be two this one and a copy in `cli.ts`,
87
+ * each with its own `Resolvable` narrowing, whose only difference was the
88
+ * terminal action. The copies also had an ORDERING dependency nothing
89
+ * enforced: the flag guard reports nothing for a mistyped subcommand (the
90
+ * flags would be matched against the wrong command's definition), which is
91
+ * only safe because the command guard ran first. Answering both from one
92
+ * result removes the ordering rather than documenting it.
54
93
  */
55
- export declare function unknownFlagsForArgv(root: AnyCommandDef, argv: string[]): Promise<string[]>;
94
+ export declare function resolveArgv(root: AnyCommandDef, argv: string[]): Promise<ArgvSelection>;
56
95
  /** Shared command setup: resolve cwd, optionally require agent.ts. */
57
96
  export declare function setup(opts?: {
58
97
  agent?: boolean;
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as CliError } from "./_output-DbJt9gsl.mjs";
3
- import { c as isEexist, l as readJson, o as errorMessage, p as writeJson } from "./_utils-Dhd058D9.mjs";
2
+ import { t as CliError } from "./_output-DBP9Op_d.mjs";
3
+ import { d as readJson, g as writeJson, s as errorMessage, u as isEexist } from "./_utils-D5JGcjiW.mjs";
4
4
  import { mkdtempSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import fs from "node:fs/promises";
@@ -17,7 +17,7 @@
17
17
  */
18
18
  import type { Logger } from "@alexkroman1/aai-runtime";
19
19
  /**
20
- * The env handed to `createServer` for host-mode connections: provider
20
+ * The env handed to `createRuntimeServer` for host-mode connections: provider
21
21
  * credentials plus the `AAI_ALLOW_HOST` gate read straight from the shell
22
22
  * (it is a control variable, not something an agent declares in `.env`).
23
23
  */
@@ -1,27 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import { i as outputSilenced, r as notify } from "./_ui-DCt4qZrk.mjs";
3
- import { i as errorCode, o as errorMessage$1 } from "./_utils-Dhd058D9.mjs";
4
- import { n as fallbackHtmlPlugin } from "./client-bundler-CT-_26c5.mjs";
3
+ import { a as errorCode, s as errorMessage$1 } from "./_utils-D5JGcjiW.mjs";
4
+ import { n as fallbackHtmlPlugin } from "./client-bundler-6mTLs6ny.mjs";
5
5
  import { t as DEDUPED_PEERS } from "./_vite-env-kejF0p2L.mjs";
6
6
  import { buildWorker } from "./worker-bundler.mjs";
7
7
  import { typecheckProject } from "./typecheck.mjs";
8
- import { n as ensureApiKey } from "./_config-ZY2euhdh.mjs";
9
- import { n as createWorkerEvaluator } from "./_bundler-DEkjO1d8.mjs";
10
- import { t as resolveServerEnv } from "./_server-common-BV4_CrP3.mjs";
8
+ import { n as ensureApiKey } from "./_config-DYzC6WMD.mjs";
9
+ import { n as resolveServerEnv } from "./_server-common-De0haHr9.mjs";
10
+ import { n as createWorkerEvaluator } from "./_bundler-DM0d0M7m.mjs";
11
11
  import { existsSync, statSync } from "node:fs";
12
12
  import path from "node:path";
13
- import { omitUndefined } from "@alexkroman1/aai/utils";
13
+ import { omitUndefined, plural } from "@alexkroman1/aai/utils";
14
14
  import { createCoalescingRunner, requestPath, sleep } from "@alexkroman1/aai/internal";
15
- import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
16
- import { DEFAULT_LISTEN_HOST, WORKFLOW_API_PREFIX, createRuntime, createServer, ensureSessionStateSchema, ensureWorkflowJournalSchema, requiredProviderEnvVars, withHostCredentialFallback } from "@alexkroman1/aai-runtime";
17
- import { WORKFLOW_DATA_DIR_ENV, consoleLogger, createMemoryJournal, handleWorkflowRequest, publishStepEnv } from "@alexkroman1/aai-runtime/internal";
15
+ import { DEFAULT_LISTEN_HOST, WORKFLOW_API_PREFIX, createRuntime, createRuntimeServer, ensureSessionStateSchema, ensureWorkflowJournalSchema, requiredProviderEnvVars, withHostCredentialFallback } from "@alexkroman1/aai-runtime";
18
16
  import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
17
+ import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
18
+ import { WORKFLOW_DATA_DIR_ENV, consoleLogger, createMemoryJournal, handleWorkflowRequest, isPathInside, publishStepEnv } from "@alexkroman1/aai-runtime/internal";
19
19
  import { watch } from "chokidar";
20
20
  import getPort, { portNumbers } from "get-port";
21
21
  import pDebounce from "p-debounce";
22
22
  //#region src/_dev-env.ts
23
23
  /**
24
- * The env handed to `createServer` for host-mode connections: provider
24
+ * The env handed to `createRuntimeServer` for host-mode connections: provider
25
25
  * credentials plus the `AAI_ALLOW_HOST` gate read straight from the shell
26
26
  * (it is a control variable, not something an agent declares in `.env`).
27
27
  */
@@ -338,7 +338,7 @@ function createDevTypecheck(cwd, report = notify) {
338
338
  * send `..` where a browser would normalize it — and a malformed percent-escape
339
339
  * is left to the API, which is where a path we cannot resolve belongs.
340
340
  */
341
- function workflowPathServedByVite(root, rawUrl) {
341
+ function workflowPathServedByVite(base, rawUrl) {
342
342
  if (rawUrl === void 0) return void 0;
343
343
  let decoded;
344
344
  try {
@@ -346,9 +346,8 @@ function workflowPathServedByVite(root, rawUrl) {
346
346
  } catch {
347
347
  return;
348
348
  }
349
- const base = path.resolve(root);
350
349
  const resolved = path.resolve(base, `.${decoded}`);
351
- if (resolved !== base && !resolved.startsWith(base + path.sep)) return void 0;
350
+ if (!isPathInside(base, resolved)) return void 0;
352
351
  try {
353
352
  if (!statSync(resolved).isFile()) return void 0;
354
353
  } catch {
@@ -362,13 +361,13 @@ function workflowPathServedByVite(root, rawUrl) {
362
361
  * with a `client.tsx` serves a page whose WebSocket never connects.
363
362
  *
364
363
  * **This table is the whole agent API as the browser can see it**, which is
365
- * the thing to hold in mind before adding a route to `createServer`. Vite owns
364
+ * the thing to hold in mind before adding a route to `createRuntimeServer`. Vite owns
366
365
  * the port the user is told to open and answers everything not listed here
367
366
  * itself — with a bare 404 carrying none of the agent server's headers, so the
368
367
  * failure looks like a missing route rather than a missing proxy entry.
369
368
  *
370
369
  * `/workflows` is why that matters beyond voice. A WORKFLOW APP
371
- * (`workflowApp()`) has no session and no socket: `page()` mounts a
370
+ * (`workflowApp()`) has no session and no socket: `mountPage()` mounts a
372
371
  * form and every single thing it does — listing workflows, starting a run,
373
372
  * polling it, streaming its events — is a same-origin `fetch` under that
374
373
  * prefix. Unproxied, the two workflow-app templates were dead on arrival under
@@ -396,7 +395,7 @@ function workflowPathServedByVite(root, rawUrl) {
396
395
  * `http://localhost:<port>` either way — a caller that resolves IPv4-only, or
397
396
  * dials the literal, gets a connection refused against a server that is up.
398
397
  *
399
- * The two halves of `aai dev` also disagreed: `createServer` binds
398
+ * The two halves of `aai dev` also disagreed: `createRuntimeServer` binds
400
399
  * {@link DEFAULT_LISTEN_HOST} explicitly, Vite took its own default, and only a
401
400
  * set `AAI_DEV_HOST` brought them back together. Taking the same constant is
402
401
  * what makes them agree by construction rather than by two matching literals.
@@ -444,6 +443,7 @@ function workflowPathServedByVite(root, rawUrl) {
444
443
  */
445
444
  function viteDevConfig(cwd, vitePort, backendPort) {
446
445
  const target = `http://127.0.0.1:${backendPort}`;
446
+ const viteRoot = path.resolve(cwd);
447
447
  return {
448
448
  root: cwd,
449
449
  plugins: [fallbackHtmlPlugin(cwd)],
@@ -461,7 +461,7 @@ function viteDevConfig(cwd, vitePort, backendPort) {
461
461
  },
462
462
  [WORKFLOW_API_PREFIX]: {
463
463
  target,
464
- bypass: (req) => workflowPathServedByVite(cwd, req.url)
464
+ bypass: (req) => workflowPathServedByVite(viteRoot, req.url)
465
465
  }
466
466
  }
467
467
  }
@@ -491,16 +491,14 @@ function viteDevConfig(cwd, vitePort, backendPort) {
491
491
  * can't mask one that would be missing both here and after deploy.
492
492
  */
493
493
  function agentEnvWarnings(agentDef, env, shellEnv = process.env) {
494
- const s = (names) => names.length > 1 ? "s" : "";
495
- const them = (names) => names.length > 1 ? "them" : "it";
496
494
  const required = requiredProviderEnvVars(agentDef);
497
495
  const warnings = [];
498
496
  const missing = required.filter((name) => !(env[name] || shellEnv[name]));
499
- if (missing.length > 0) warnings.push(`Missing provider credential${s(missing)}: ${missing.join(", ")}. Set ${them(missing)} in .env or the environment.`);
497
+ if (missing.length > 0) warnings.push(`Missing provider ${plural(missing.length, "credential")}: ${missing.join(", ")}. Set ${plural(missing.length, "it", "them")} in .env or the environment.`);
500
498
  const shellOnly = required.filter((name) => !env[name] && shellEnv[name]);
501
- if (shellOnly.length > 0) warnings.push(`${shellOnly.join(", ")} resolved from your shell, not .env — deployed agents won't have ${them(shellOnly)}. Declare ${them(shellOnly)} in .env before \`aai publish\`.`);
499
+ if (shellOnly.length > 0) warnings.push(`${shellOnly.join(", ")} resolved from your shell, not .env — deployed agents won't have ${plural(shellOnly.length, "it", "them")}. Declare ${plural(shellOnly.length, "it", "them")} in .env before \`aai publish\`.`);
502
500
  const declared = (agentDef.requiredEnv ?? []).filter((name) => !env[name]);
503
- if (declared.length > 0) warnings.push(`Missing requiredEnv key${s(declared)} declared by the agent: ${declared.join(", ")}. Set ${them(declared)} in .env — ctx.env will not contain ${them(declared)} otherwise.`);
501
+ if (declared.length > 0) warnings.push(`Missing requiredEnv ${plural(declared.length, "key")} declared by the agent: ${declared.join(", ")}. Set ${plural(declared.length, "it", "them")} in .env — ctx.env will not contain ${plural(declared.length, "it", "them")} otherwise.`);
504
502
  return warnings;
505
503
  }
506
504
  async function resolveAgentEnv(root, agentDef) {
@@ -634,13 +632,16 @@ async function startDevServer(opts) {
634
632
  journal,
635
633
  publicUrl: process.env.PUBLIC_URL?.trim() || `http://localhost:${backendPort}`
636
634
  });
637
- return createServer({
635
+ return createRuntimeServer({
638
636
  runtime,
639
637
  name: agentDef.name,
640
638
  env: hostModeEnv(providerEnv),
641
639
  hostBaseAgent: agentDef,
642
640
  greeting: agentDef.greeting,
643
- ...omitUndefined({ page: agentDef.page }),
641
+ ...omitUndefined({
642
+ page: agentDef.page,
643
+ telephony: agentDef.telephony
644
+ }),
644
645
  request: (req, res, url, method) => handleWorkflowRequest(req, res, url, method, {
645
646
  deliver: () => runtime.deliverWorkflow,
646
647
  logger: devLogger
@@ -12,13 +12,13 @@
12
12
  * with a `client.tsx` serves a page whose WebSocket never connects.
13
13
  *
14
14
  * **This table is the whole agent API as the browser can see it**, which is
15
- * the thing to hold in mind before adding a route to `createServer`. Vite owns
15
+ * the thing to hold in mind before adding a route to `createRuntimeServer`. Vite owns
16
16
  * the port the user is told to open and answers everything not listed here
17
17
  * itself — with a bare 404 carrying none of the agent server's headers, so the
18
18
  * failure looks like a missing route rather than a missing proxy entry.
19
19
  *
20
20
  * `/workflows` is why that matters beyond voice. A WORKFLOW APP
21
- * (`workflowApp()`) has no session and no socket: `page()` mounts a
21
+ * (`workflowApp()`) has no session and no socket: `mountPage()` mounts a
22
22
  * form and every single thing it does — listing workflows, starting a run,
23
23
  * polling it, streaming its events — is a same-origin `fetch` under that
24
24
  * prefix. Unproxied, the two workflow-app templates were dead on arrival under
@@ -46,7 +46,7 @@
46
46
  * `http://localhost:<port>` either way — a caller that resolves IPv4-only, or
47
47
  * dials the literal, gets a connection refused against a server that is up.
48
48
  *
49
- * The two halves of `aai dev` also disagreed: `createServer` binds
49
+ * The two halves of `aai dev` also disagreed: `createRuntimeServer` binds
50
50
  * {@link DEFAULT_LISTEN_HOST} explicitly, Vite took its own default, and only a
51
51
  * set `AAI_DEV_HOST` brought them back together. Taking the same constant is
52
52
  * what makes them agree by construction rather than by two matching literals.
@@ -61,7 +61,7 @@ export declare function waitForHealth(url: string, child?: ChildProcess, timeout
61
61
  export declare function waitForExit(child: ChildProcess, timeoutMs?: number): Promise<void>;
62
62
  /**
63
63
  * Run a scaffolded project the way a self-hoster does — `npm start`, which runs
64
- * the project's own `prestart` (`aai build`) and then `server.mjs`.
64
+ * the project's own `prestart` (`aai build`) and then `aai start`.
65
65
  *
66
66
  * Extracted because there are two legs now and the spawn is the fiddly half: the
67
67
  * port has to be read off stdout, and every way this can fail — a build error, a