@alexkroman1/aai-cli 14.0.0 → 15.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 (45) hide show
  1. package/dist/_artifacts-BJOYGQPp.mjs +21 -0
  2. package/dist/_artifacts.d.ts +16 -0
  3. package/dist/_build-target.d.ts +172 -0
  4. package/dist/{_bundler-DolUCMxu.mjs → _bundler-DM0d0M7m.mjs} +1 -1
  5. package/dist/{_dev-server-CSMqF8PN.mjs → _dev-server-BzWB6-4y.mjs} +9 -6
  6. package/dist/_e2e-test-utils.d.ts +1 -1
  7. package/dist/{_init-CQ8idAwo.mjs → _init-Bsi3DZNJ.mjs} +1 -1
  8. package/dist/_server-common-De0haHr9.mjs +70 -0
  9. package/dist/_server-common.d.ts +20 -1
  10. package/dist/{_templates-CK4oKoeX.mjs → _templates-CIlJ3Vay.mjs} +1 -1
  11. package/dist/_templates.d.ts +1 -1
  12. package/dist/_vercel-output.d.ts +63 -0
  13. package/dist/build-BhEaxBPu.mjs +481 -0
  14. package/dist/build.d.ts +18 -10
  15. package/dist/cli.mjs +51 -23
  16. package/dist/{client-bundler-BJgREAh6.mjs → client-bundler-6mTLs6ny.mjs} +4 -4
  17. package/dist/client-bundler.d.ts +1 -1
  18. package/dist/client-bundler.mjs +1 -1
  19. package/dist/{deploy-uAJ4NukN.mjs → deploy-CGqPU5U-.mjs} +2 -2
  20. package/dist/{dev-DApPSaE_.mjs → dev-Bx9gYBHM.mjs} +1 -1
  21. package/dist/{eval-BK47A_K5.mjs → eval-B3I7FqN9.mjs} +1 -1
  22. package/dist/{init-DukDxECd.mjs → init-CQbj3ycf.mjs} +49 -39
  23. package/dist/init.d.ts +19 -10
  24. package/dist/scaffold/CLAUDE.md +73 -22
  25. package/dist/scaffold/package.json +6 -6
  26. package/dist/start.d.ts +112 -0
  27. package/dist/start.mjs +156 -0
  28. package/dist/{studio-CpHlNHUZ.mjs → studio-C_zuRC_z.mjs} +2 -2
  29. package/dist/templates/briefing-desk/agent.eval.test.ts +156 -0
  30. package/dist/templates/code-interpreter/agent.test.ts +103 -0
  31. package/dist/templates/link-digest/client.tsx +55 -3
  32. package/dist/templates/math-buddy/agent.test.ts +126 -0
  33. package/dist/templates/personal-finance/agent.test.ts +127 -0
  34. package/dist/templates/support-line/agent.ts +8 -0
  35. package/dist/templates/travel-concierge/routing.ts +64 -55
  36. package/dist/templates/travel-concierge/tools/cancel_action.ts +3 -1
  37. package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +3 -1
  38. package/dist/templates/travel-concierge/tools/confirm_action.ts +3 -1
  39. package/dist/templates/web-researcher/agent.test.ts +130 -0
  40. package/dist/worker-bundler.d.ts +1 -1
  41. package/dist/worker-bundler.mjs +7 -7
  42. package/package.json +9 -4
  43. package/dist/_server-common-vILJp3it.mjs +0 -43
  44. package/dist/build-Mxk8gWvX.mjs +0 -108
  45. package/dist/scaffold/server.mjs +0 -308
package/dist/cli.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { i as installStdoutGuard, n as fail, o as writeLine, r as getOutputMode, t as CliError } from "./_output-DBP9Op_d.mjs";
3
- import { n as log, o as silenceOutput } from "./_ui-DCt4qZrk.mjs";
3
+ import { a as parsePort, n as log, o as silenceOutput } from "./_ui-DCt4qZrk.mjs";
4
4
  import { c as fileExists, f as readPackageJson, p as resolveCwd, s as errorMessage$1, t as AGENT_ENTRY } from "./_utils-D5JGcjiW.mjs";
5
5
  import { existsSync } from "node:fs";
6
6
  import path from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
- import { isRecord, plural } from "@alexkroman1/aai/utils";
8
+ import { isRecord, omitUndefined, plural } from "@alexkroman1/aai/utils";
9
9
  import { defineCommand, runMain, showUsage } from "citty";
10
10
  //#region src/_cli-common.ts
11
11
  /** Shared arg definitions for citty commands. */
@@ -395,7 +395,7 @@ const list = defineExec({
395
395
  args: { ...platformArgs },
396
396
  cwd: "any",
397
397
  async run({ args, cwd }) {
398
- const { executeList } = await import("./studio-CpHlNHUZ.mjs");
398
+ const { executeList } = await import("./studio-C_zuRC_z.mjs");
399
399
  return executeList({
400
400
  cwd,
401
401
  server: args.server
@@ -427,7 +427,7 @@ const pull = defineExec({
427
427
  },
428
428
  cwd: "any",
429
429
  async run({ args, cwd }) {
430
- const { executePull } = await import("./studio-CpHlNHUZ.mjs");
430
+ const { executePull } = await import("./studio-C_zuRC_z.mjs");
431
431
  return executePull({
432
432
  cwd,
433
433
  project: args.project,
@@ -448,7 +448,7 @@ const push = defineExec({
448
448
  },
449
449
  cwd: "agent",
450
450
  async run({ args, cwd }) {
451
- const { executePush } = await import("./studio-CpHlNHUZ.mjs");
451
+ const { executePush } = await import("./studio-C_zuRC_z.mjs");
452
452
  return executePush({
453
453
  cwd,
454
454
  server: args.server,
@@ -471,7 +471,7 @@ const publish = defineExec({
471
471
  },
472
472
  cwd: "agent",
473
473
  async run({ args, cwd }) {
474
- const { executePublish } = await import("./studio-CpHlNHUZ.mjs");
474
+ const { executePublish } = await import("./studio-C_zuRC_z.mjs");
475
475
  return executePublish({
476
476
  cwd,
477
477
  server: args.server,
@@ -693,26 +693,19 @@ const init = defineExec({
693
693
  template: {
694
694
  type: "string",
695
695
  alias: "t",
696
- description: "Template to use (run `aai templates` for the list)"
696
+ description: "Template to use (prompted when omitted)"
697
697
  },
698
- server: sharedArgs.server,
699
698
  yes: sharedArgs.yes,
700
- json: sharedArgs.json,
701
- skipDeploy: {
702
- type: "boolean",
703
- description: "Skip deploy after scaffolding"
704
- }
699
+ json: sharedArgs.json
705
700
  },
706
701
  cwd: "none",
707
702
  async run({ args, mode }) {
708
- const { executeInit } = await import("./init-DukDxECd.mjs");
703
+ const { executeInit } = await import("./init-CQbj3ycf.mjs");
709
704
  return executeInit({
710
705
  dir: args.dir,
711
706
  force: args.force,
712
707
  template: args.template,
713
- yes: mode === "json" ? true : args.yes,
714
- skipDeploy: args.skipDeploy,
715
- server: args.server
708
+ yes: mode === "json" ? true : args.yes
716
709
  }, mode === "json" ? { silent: true } : void 0);
717
710
  }
718
711
  });
@@ -736,7 +729,7 @@ const dev = defineExec({
736
729
  },
737
730
  cwd: "agent",
738
731
  async run({ args, cwd }) {
739
- const { executeDev } = await import("./dev-DApPSaE_.mjs");
732
+ const { executeDev } = await import("./dev-Bx9gYBHM.mjs");
740
733
  return executeDev({
741
734
  cwd,
742
735
  port: args.port,
@@ -744,6 +737,35 @@ const dev = defineExec({
744
737
  });
745
738
  }
746
739
  });
740
+ const start = defineExec({
741
+ meta: {
742
+ name: "start",
743
+ description: "Serve the built agent (production)"
744
+ },
745
+ args: {
746
+ port: {
747
+ type: "string",
748
+ alias: "p",
749
+ description: "Port to listen on"
750
+ },
751
+ host: {
752
+ type: "string",
753
+ description: "Address to bind (default: loopback)"
754
+ },
755
+ json: sharedArgs.json
756
+ },
757
+ cwd: "agent",
758
+ async run({ args, cwd }) {
759
+ const { executeStart } = await import("./start.mjs");
760
+ return executeStart({
761
+ cwd,
762
+ ...omitUndefined({
763
+ port: args.port === void 0 ? void 0 : parsePort(args.port),
764
+ host: args.host?.trim() || void 0
765
+ })
766
+ });
767
+ }
768
+ });
747
769
  const test = defineExec({
748
770
  meta: {
749
771
  name: "test",
@@ -770,7 +792,7 @@ const evalCommand = defineExec({
770
792
  args: { json: sharedArgs.json },
771
793
  cwd: "agent",
772
794
  async run({ cwd }) {
773
- const { executeEval } = await import("./eval-BK47A_K5.mjs");
795
+ const { executeEval } = await import("./eval-B3I7FqN9.mjs");
774
796
  return executeEval(cwd);
775
797
  }
776
798
  });
@@ -788,15 +810,20 @@ const build = defineExec({
788
810
  skipTypecheck: {
789
811
  type: "boolean",
790
812
  description: "Skip type checking before build"
813
+ },
814
+ target: {
815
+ type: "string",
816
+ description: "Deployment shape to emit (node, vercel; default: detected)"
791
817
  }
792
818
  },
793
819
  cwd: "agent",
794
820
  async run({ args, cwd }) {
795
- const { executeBuild } = await import("./build-Mxk8gWvX.mjs");
821
+ const { executeBuild } = await import("./build-BhEaxBPu.mjs");
796
822
  return executeBuild({
797
823
  cwd,
798
824
  skipTests: args.skipTests,
799
- skipTypecheck: args.skipTypecheck
825
+ skipTypecheck: args.skipTypecheck,
826
+ target: args.target
800
827
  });
801
828
  }
802
829
  });
@@ -819,7 +846,7 @@ const deploy = defineExec({
819
846
  },
820
847
  cwd: "agent",
821
848
  async run({ args, cwd }) {
822
- const { executeDeploy } = await import("./deploy-uAJ4NukN.mjs");
849
+ const { executeDeploy } = await import("./deploy-CGqPU5U-.mjs");
823
850
  return executeDeploy({
824
851
  cwd,
825
852
  server: args.server,
@@ -863,7 +890,7 @@ const templates = defineExec({
863
890
  args: { json: sharedArgs.json },
864
891
  cwd: "none",
865
892
  async run({ mode }) {
866
- const { listTemplates } = await import("./_templates-CK4oKoeX.mjs");
893
+ const { listTemplates } = await import("./_templates-CIlJ3Vay.mjs");
867
894
  const names = await listTemplates();
868
895
  if (mode === "human") {
869
896
  for (const name of names) log.message(name);
@@ -880,6 +907,7 @@ const mainCommand = defineCommand({
880
907
  subCommands: {
881
908
  init,
882
909
  dev,
910
+ start,
883
911
  test,
884
912
  eval: evalCommand,
885
913
  build,
@@ -81,10 +81,10 @@ const DEFAULT_OUT_DIR = ".aai/client";
81
81
  * @internal — build hook for aai-server/the studio; not a supported public
82
82
  * API and not covered by semver.
83
83
  */
84
- async function buildClient(cwd, opts = {}) {
84
+ async function buildClient(cwd, options = {}) {
85
85
  const clientEntry = path.join(cwd, "client.tsx");
86
86
  if (!await fileExists(clientEntry)) return {};
87
- const outDir = opts.outDir ?? DEFAULT_OUT_DIR;
87
+ const outDir = options.outDir ?? DEFAULT_OUT_DIR;
88
88
  const clientDir = path.join(cwd, outDir);
89
89
  let cleanupHtml = () => {};
90
90
  try {
@@ -93,8 +93,8 @@ async function buildClient(cwd, opts = {}) {
93
93
  root: cwd,
94
94
  base: "./",
95
95
  logLevel: "silent",
96
- ...opts.configFile === false && { configFile: false },
97
- ...omitUndefined({ plugins: opts.plugins }),
96
+ ...options.configFile === false && { configFile: false },
97
+ ...omitUndefined({ plugins: options.plugins }),
98
98
  resolve: { dedupe: DEDUPED_PEERS },
99
99
  build: {
100
100
  outDir,
@@ -27,4 +27,4 @@ export type BuildClientOptions = {
27
27
  * @internal — build hook for aai-server/the studio; not a supported public
28
28
  * API and not covered by semver.
29
29
  */
30
- export declare function buildClient(cwd: string, opts?: BuildClientOptions): Promise<Record<string, string>>;
30
+ export declare function buildClient(cwd: string, options?: BuildClientOptions): Promise<Record<string, string>>;
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { t as buildClient } from "./client-bundler-BJgREAh6.mjs";
2
+ import { t as buildClient } from "./client-bundler-6mTLs6ny.mjs";
3
3
  export { buildClient };
@@ -3,8 +3,8 @@ import { a as ok } from "./_output-DBP9Op_d.mjs";
3
3
  import { n as log, r as notify, t as fmtUrl } from "./_ui-DCt4qZrk.mjs";
4
4
  import { s as errorMessage$1 } from "./_utils-D5JGcjiW.mjs";
5
5
  import { s as updateProjectConfig } from "./_config-DYzC6WMD.mjs";
6
- import { i as evalWorkerConfig, t as buildAgentBundle } from "./_bundler-DolUCMxu.mjs";
7
- import { t as resolveServerEnv } from "./_server-common-vILJp3it.mjs";
6
+ import { n as resolveServerEnv } from "./_server-common-De0haHr9.mjs";
7
+ import { i as evalWorkerConfig, t as buildAgentBundle } from "./_bundler-DM0d0M7m.mjs";
8
8
  import { o as resolveDeployTarget } from "./_agent-BzUeqOdj.mjs";
9
9
  import { assertTypechecks } from "./_typecheck-gate-U3jcil8L.mjs";
10
10
  import { n as scanWorkflowDeterminism, t as determinismWarnings } from "./_workflow-determinism-CvpqMpdh.mjs";
@@ -12,7 +12,7 @@ import { styleText } from "node:util";
12
12
  async function executeDev(opts) {
13
13
  const port = parsePort(opts.port);
14
14
  const agentName = path.basename(path.resolve(opts.cwd));
15
- const { startDevServer } = await import("./_dev-server-CSMqF8PN.mjs");
15
+ const { startDevServer } = await import("./_dev-server-BzWB6-4y.mjs");
16
16
  let cleanup;
17
17
  let shuttingDown = false;
18
18
  const onSignal = () => {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as ok, n as fail } from "./_output-DBP9Op_d.mjs";
3
3
  import { n as log } from "./_ui-DCt4qZrk.mjs";
4
- import { t as resolveServerEnv } from "./_server-common-vILJp3it.mjs";
4
+ import { n as resolveServerEnv } from "./_server-common-De0haHr9.mjs";
5
5
  import { classifyVitestError, runVitest } from "./test-D032V2cA.mjs";
6
6
  //#region src/eval.ts
7
7
  /**
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as ok } from "./_output-DBP9Op_d.mjs";
3
3
  import { n as log, s as unwrapCancel } from "./_ui-DCt4qZrk.mjs";
4
- import { c as fileExists, d as readJson, p as resolveCwd, s as errorMessage$1, t as AGENT_ENTRY } from "./_utils-D5JGcjiW.mjs";
4
+ import { c as fileExists, d as readJson, p as resolveCwd, s as errorMessage, t as AGENT_ENTRY } from "./_utils-D5JGcjiW.mjs";
5
5
  import { r as isDevMode, t as getMonorepoRoot } from "./_agent-BzUeqOdj.mjs";
6
+ import { listTemplates } from "./_templates-CIlJ3Vay.mjs";
6
7
  import path from "node:path";
7
- import { omitUndefined } from "@alexkroman1/aai/utils";
8
8
  import { styleText } from "node:util";
9
9
  import * as p from "@clack/prompts";
10
10
  import { execa } from "execa";
@@ -39,6 +39,41 @@ async function promptProjectName(yes) {
39
39
  defaultValue: DEFAULT_PROJECT_NAME
40
40
  }), "Setup cancelled") || DEFAULT_PROJECT_NAME;
41
41
  }
42
+ /**
43
+ * The template a non-interactive `init` gets, and the entry the selector opens
44
+ * on — so an author who just presses Enter lands where they used to.
45
+ */
46
+ const DEFAULT_TEMPLATE = "simple";
47
+ /**
48
+ * Ask which template to scaffold, listing what this CLI actually ships.
49
+ *
50
+ * The options are derived from {@link listTemplates} rather than a roster kept
51
+ * here: that function already backs `aai templates` AND the unknown-template
52
+ * error, so a template added to the package shows up in the picker with no
53
+ * second list to update. {@link DEFAULT_TEMPLATE} is hoisted to the top and
54
+ * pre-selected, which is what keeps a bare `aai init` a single Enter away from
55
+ * the project it produced before there was a picker.
56
+ *
57
+ * Callers must not reach here when there is nobody to answer — `--yes` and
58
+ * JSON mode (auto-detected on a pipe) resolve the default without prompting.
59
+ */
60
+ async function promptTemplate(list = listTemplates) {
61
+ const [first, ...rest] = await list();
62
+ if (first === void 0) return DEFAULT_TEMPLATE;
63
+ if (rest.length === 0) return first;
64
+ const names = [first, ...rest];
65
+ const hasDefault = names.includes(DEFAULT_TEMPLATE);
66
+ const ordered = hasDefault ? [DEFAULT_TEMPLATE, ...names.filter((name) => name !== DEFAULT_TEMPLATE)] : names;
67
+ return unwrapCancel(await p.select({
68
+ message: "Which template?",
69
+ maxItems: 12,
70
+ initialValue: hasDefault ? DEFAULT_TEMPLATE : first,
71
+ options: ordered.map((name) => name === DEFAULT_TEMPLATE ? {
72
+ value: name,
73
+ hint: "the default starting point"
74
+ } : { value: name })
75
+ }), "Setup cancelled");
76
+ }
42
77
  /** Best-effort corepack enable so pnpm is available (scaffold declares packageManager: pnpm). */
43
78
  async function ensurePnpm() {
44
79
  await execa("corepack", ["enable"], { reject: false });
@@ -73,9 +108,14 @@ async function runPnpmInstall(cwd) {
73
108
  const pnpmArgs = isDevMode() ? ["install"] : ["install", "--ignore-workspace"];
74
109
  await execa(cmd, [...args, ...pnpmArgs], { cwd });
75
110
  }
76
- /** Install deps with pnpm. Returns true on success (or no deps to install). */
111
+ /**
112
+ * Install deps with pnpm, reporting a failure through `warn` rather than
113
+ * throwing: the project is scaffolded either way, and the two warnings say how
114
+ * to finish the install by hand. Nothing downstream branches on the outcome —
115
+ * `init` stops here — so it returns nothing.
116
+ */
77
117
  async function installDeps(cwd, warn, silent) {
78
- if (!await hasDeps(cwd)) return true;
118
+ if (!await hasDeps(cwd)) return;
79
119
  await ensurePnpm();
80
120
  try {
81
121
  await withSpinner(silent, {
@@ -83,11 +123,9 @@ async function installDeps(cwd, warn, silent) {
83
123
  done: "Dependencies installed",
84
124
  failed: "Dependency install failed"
85
125
  }, () => runPnpmInstall(cwd));
86
- return true;
87
126
  } catch (err) {
88
- warn(`pnpm install failed: ${errorMessage$1(err)}`);
127
+ warn(`pnpm install failed: ${errorMessage(err)}`);
89
128
  warn("Install pnpm (`npm install -g pnpm`), then run `pnpm install` in the project.");
90
- return false;
91
129
  }
92
130
  }
93
131
  /** Resolve target directory relative to the user's current directory. */
@@ -104,30 +142,9 @@ function collectWarnings() {
104
142
  }
105
143
  };
106
144
  }
107
- /** Publish after init and return deploy metadata if successful. */
108
- async function tryPublish(cwd, server, warn) {
109
- const { executePublish } = await import("./studio-CpHlNHUZ.mjs");
110
- try {
111
- const result = await executePublish({
112
- cwd,
113
- ...omitUndefined({ server })
114
- });
115
- if (result.ok) return {
116
- slug: result.data.slug,
117
- url: result.data.url,
118
- studioUrl: result.data.studioUrl
119
- };
120
- warn(`Publish failed: ${result.error}`);
121
- return null;
122
- } catch (err) {
123
- warn(`Publish failed: ${errorMessage$1(err)}`);
124
- warn("Your project was still created — run `aai publish` in it to retry.");
125
- return null;
126
- }
127
- }
128
145
  /** Scaffold the project, optionally showing a spinner. */
129
146
  async function scaffoldProject(dir, cwd, template, silent) {
130
- const { runInit } = await import("./_init-CQ8idAwo.mjs");
147
+ const { runInit } = await import("./_init-Bsi3DZNJ.mjs");
131
148
  await withSpinner(silent, {
132
149
  start: `Creating ${dir}`,
133
150
  done: "Project created",
@@ -150,22 +167,15 @@ async function executeInit(opts, extra) {
150
167
  const monorepoRoot = getMonorepoRoot();
151
168
  const cwd = resolveTargetDir(dir);
152
169
  if (!opts.force && await fileExists(path.join(cwd, "agent.ts"))) throw new Error(`${AGENT_ENTRY} already exists in this directory. Use ${styleText("cyanBright", "--force")} to overwrite.`);
153
- const template = opts.template ?? "simple";
170
+ const template = opts.template ?? (opts.yes || suppressUi ? DEFAULT_TEMPLATE : await promptTemplate());
154
171
  const { warn, warnings } = collectWarnings();
155
172
  await scaffoldProject(dir, cwd, template, suppressUi);
156
- const installed = await installDeps(cwd, warn, suppressUi);
157
- if (!installed) warn("Skipping publish because dependencies were not installed.");
158
- const published = installed && !opts.skipDeploy ? await tryPublish(cwd, opts.server, warn) : null;
173
+ await installDeps(cwd, warn, suppressUi);
159
174
  if (!suppressUi) printPostInitInfo(cwd, monorepoRoot);
160
175
  const data = {
161
176
  dir: cwd,
162
- template,
163
- deployed: published !== null
177
+ template
164
178
  };
165
- if (published) {
166
- data.slug = published.slug;
167
- data.url = published.url;
168
- }
169
179
  if (warnings.length > 0) data.warnings = warnings;
170
180
  return ok(data);
171
181
  }
package/dist/init.d.ts CHANGED
@@ -2,21 +2,32 @@ import { type CommandResult } from "./_output.ts";
2
2
  type InitData = {
3
3
  dir: string;
4
4
  template: string;
5
- deployed: boolean;
6
- slug?: string;
7
- url?: string;
8
5
  /**
9
- * Diagnostics a human sees as `log.warn` lines: a failed `pnpm install`, a
10
- * failed publish, the publish skipped because of the former.
6
+ * Diagnostics a human sees as `log.warn` lines today only a failed
7
+ * `pnpm install`.
11
8
  *
12
9
  * They have to ride the result for the same reason `PushOutcome.warnings`
13
10
  * does — `log.warn` is silenced in JSON mode and JSON mode is AUTO-DETECTED
14
- * on a pipe, so a scripted `aai init` was told `{ ok: true, deployed: false }`
15
- * for a project whose dependencies never installed and could not tell that
16
- * apart from `--skipDeploy`.
11
+ * on a pipe, so a scripted `aai init` was told `{ ok: true }` for a project
12
+ * whose dependencies never installed and could not tell that apart from a
13
+ * clean run.
17
14
  */
18
15
  warnings?: string[];
19
16
  };
17
+ /**
18
+ * Ask which template to scaffold, listing what this CLI actually ships.
19
+ *
20
+ * The options are derived from {@link listTemplates} rather than a roster kept
21
+ * here: that function already backs `aai templates` AND the unknown-template
22
+ * error, so a template added to the package shows up in the picker with no
23
+ * second list to update. {@link DEFAULT_TEMPLATE} is hoisted to the top and
24
+ * pre-selected, which is what keeps a bare `aai init` a single Enter away from
25
+ * the project it produced before there was a picker.
26
+ *
27
+ * Callers must not reach here when there is nobody to answer — `--yes` and
28
+ * JSON mode (auto-detected on a pipe) resolve the default without prompting.
29
+ */
30
+ export declare function promptTemplate(list?: () => Promise<string[]>): Promise<string>;
20
31
  /** Build the command + args for running pnpm, routing through safe-chain when available. */
21
32
  export declare function resolvePnpmCommand(checkSafeChain?: () => Promise<boolean>): Promise<{
22
33
  cmd: string;
@@ -27,8 +38,6 @@ export declare function executeInit(opts: {
27
38
  force?: boolean | undefined;
28
39
  template?: string | undefined;
29
40
  yes?: boolean | undefined;
30
- skipDeploy?: boolean | undefined;
31
- server?: string | undefined;
32
41
  }, extra?: {
33
42
  silent?: boolean | undefined;
34
43
  }): Promise<CommandResult<InitData>>;
@@ -133,8 +133,8 @@ gate in front of `aai build`.
133
133
 
134
134
  ## Running it yourself (`npm start`)
135
135
 
136
- `server.mjs` serves this agent from a plain Node process — no platform
137
- account, nothing managed. It is the deployment counterpart of `aai dev`:
136
+ `aai start` serves this agent from a plain Node process — no platform account,
137
+ nothing managed. It is the deployment counterpart of `aai dev`:
138
138
 
139
139
  ```sh
140
140
  npm start # http://127.0.0.1:3000
@@ -142,11 +142,18 @@ PORT=8080 HOST=0.0.0.0 npm start # bind every interface, e.g. in a container
142
142
  ```
143
143
 
144
144
  `npm start` **builds first** (that is the `prestart` script) and then serves
145
- the result: `server.mjs` boots `.aai/worker.mjs`, the same artifact
145
+ the result: `aai start` boots `.aai/worker.mjs`, the same artifact
146
146
  `aai publish` uploads. It serves your own `client.tsx` build when there is one
147
- and falls back to `defaultClientDir()` (`@alexkroman1/aai-ui/client-dir`), the
148
- prebuilt default UI shipped inside the package — the only export of `aai-ui`
149
- that runs on Node rather than in the browser.
147
+ and falls back to the prebuilt default UI shipped inside `@alexkroman1/aai-ui`.
148
+
149
+ There is no server file in your project, and that is deliberate — the boot
150
+ belongs to the framework, so it improves when you update rather than being
151
+ frozen at the moment you scaffolded. When you need to own it, import
152
+ `createProjectServer` from `@alexkroman1/aai-cli/start`: it builds the server
153
+ and binds nothing, so you decide how it is served. Building one from scratch
154
+ instead, `defaultClientDir()` (`@alexkroman1/aai-ui/client-dir`) is where that
155
+ prebuilt UI lives — the only export of `aai-ui` that runs on Node rather than
156
+ in the browser.
150
157
 
151
158
  The build is what makes `tools/` work — a tool is registered by existing, and
152
159
  the enumeration happens where the bundle is assembled, so a server that loaded
@@ -161,9 +168,31 @@ One thing to know: it binds **loopback by default**, because this server has
161
168
  no request authentication of its own; set `HOST=0.0.0.0` only behind your own
162
169
  proxy or auth.
163
170
 
164
- Deleting `server.mjs` costs nothing: `aai dev`, `aai publish` and the managed
165
- platform never read it. `run_code` is the one feature that does not follow —
166
- it needs the platform's sandbox and refuses outside one.
171
+ `run_code` is the one feature that does not follow — it needs the platform's
172
+ sandbox and refuses outside one.
173
+
174
+ ### Deploying to a host that wants its own entry file
175
+
176
+ `aai build --target <host>` writes the deployment that host expects into the
177
+ build output. Nothing host-specific lives in your project: the files are
178
+ generated, gitignored, and rewritten by the host's own build.
179
+
180
+ ```sh
181
+ aai build --target vercel # writes .vercel/output/ (Build Output API v3)
182
+ ```
183
+
184
+ You rarely type it. The target is detected from the host's own build
185
+ environment, so `vercel deploy` (or a git push) picks it up with nothing
186
+ configured; `--target node`, the default everywhere else, emits nothing extra
187
+ and is what `npm start` runs.
188
+
189
+ One thing to know before deploying a VOICE agent to a serverless host: the
190
+ session is a WebSocket, so the host has to support one. Vercel does — it hands
191
+ the function the raw upgrade, and the emitted entry passes it to the same
192
+ server `aai dev` runs. A host that serves only request/response still runs the
193
+ HTTP surface — `/health`, `/client-config`,
194
+ `/workflows/*` and your static assets — which is everything a workflow app
195
+ needs and none of what a voice agent needs.
167
196
 
168
197
  ## Project structure
169
198
 
@@ -1199,11 +1228,23 @@ statement about pipeline mode, not about the SDK.
1199
1228
 
1200
1229
  ### Answering a phone call
1201
1230
 
1202
- A deployed voice agent already serves carrier media streams there is nothing
1203
- to switch on. `createRuntimeServer` mounts `WS /phone` whenever the agent is a voice
1204
- agent (`telephony` defaults to `true`, and to `false` for a `page: "static"`
1205
- workflow app, which has no stages to put on a call). Point the carrier at it
1206
- with a `carrier` query parameter naming who is dialling:
1231
+ **Say which carriers may call, and `WS /phone` is served for exactly those.**
1232
+ `telephony` is an allow-list on `agent()`, and it is the whole of the wiring:
1233
+
1234
+ ```ts
1235
+ import { agent } from "@alexkroman1/aai";
1236
+
1237
+ export default agent({
1238
+ name: "Support",
1239
+ greeting: "Support line — what's happened?",
1240
+ // The carrier this agent's number is with. `true` admits every carrier this
1241
+ // build decodes; omit the field and `/phone` is not served at all.
1242
+ telephony: ["twilio"],
1243
+ });
1244
+ ```
1245
+
1246
+ Point the carrier at the deployed agent with a `carrier` query parameter naming
1247
+ who is dialling:
1207
1248
 
1208
1249
  ```text
1209
1250
  wss://<your-agent-url>/phone?carrier=twilio
@@ -1211,15 +1252,25 @@ wss://<your-agent-url>/phone?carrier=telnyx
1211
1252
  ```
1212
1253
 
1213
1254
  Twilio and Telnyx are the two carriers this build decodes (`CARRIER_CODECS`);
1214
- an unknown `carrier` is declined at the upgrade. Both speak 8 kHz mu-law, which
1215
- the bridge transcodes in both directions, so the agent, its tools and its slots
1255
+ an unknown `carrier` is declined at the upgrade with a `400`, and a real one
1256
+ this agent did not declare with a `404`. Both speak 8 kHz mu-law, which the
1257
+ bridge transcodes in both directions, so the agent, its tools and its slots
1216
1258
  behave exactly as they do in the browser — a phone call is a transport, not a
1217
- mode. Nothing about `agent.ts` changes to support one.
1218
-
1219
- Turn the route off with `telephony: false` on `createRuntimeServer`. If you are
1220
- embedding the runtime yourself rather than deploying, the pieces are
1221
- `createTelephonyBridge`, `startTelephonySession`, `TELEPHONY_PATH` and
1222
- `carrierByName`, all on `@alexkroman1/aai-runtime`.
1259
+ mode. Nothing else about `agent.ts` changes to support one.
1260
+
1261
+ **An agent that declares nothing answers no carrier**, which is a change from
1262
+ earlier releases: every voice agent used to serve both carriers' framing from
1263
+ the moment it booted, whether or not it had a phone number. `/phone` is the one
1264
+ door dialled from OUTSIDE your deployment, by a carrier following a number, so
1265
+ it is opened by a sentence in `agent.ts` rather than inherited. `aai dev` and a
1266
+ deployed sandbox honour the same declaration, so a call refused after a deploy
1267
+ is refused on your laptop too.
1268
+
1269
+ `telephony: false` is the same refusal stated out loud, and an operator can pass
1270
+ `telephony` to `createAgentServer` to override one deployment of an agent that
1271
+ does declare a carrier. If you are embedding the runtime yourself rather than
1272
+ deploying, the pieces are `createTelephonyBridge`, `startTelephonySession`,
1273
+ `TELEPHONY_PATH` and `carrierByName`, all on `@alexkroman1/aai-runtime`.
1223
1274
 
1224
1275
  **Silence nudge (pipeline only):** set `silenceTimeoutMs` to make the
1225
1276
  assistant proactively take a turn after that much user silence (e.g.
@@ -7,7 +7,7 @@
7
7
  "scripts": {
8
8
  "dev": "aai dev",
9
9
  "prestart": "aai build --skip-tests",
10
- "start": "node server.mjs",
10
+ "start": "aai start",
11
11
  "build": "aai build",
12
12
  "test": "vitest run --exclude \"**/*.eval.test.*\"",
13
13
  "test:agent": "aai test",
@@ -15,9 +15,10 @@
15
15
  "publish:agent": "aai publish"
16
16
  },
17
17
  "dependencies": {
18
- "@alexkroman1/aai": "^14.0.0",
19
- "@alexkroman1/aai-runtime": "^14.0.0",
20
- "@alexkroman1/aai-ui": "^14.0.0",
18
+ "@alexkroman1/aai": "^15.1.0",
19
+ "@alexkroman1/aai-cli": "^15.1.0",
20
+ "@alexkroman1/aai-runtime": "^15.1.0",
21
+ "@alexkroman1/aai-ui": "^15.1.0",
21
22
  "react": "^19.2.8",
22
23
  "react-dom": "^19.2.8",
23
24
  "tailwindcss": "^4.0.0",
@@ -25,9 +26,8 @@
25
26
  "zod": "^4.5.4"
26
27
  },
27
28
  "devDependencies": {
28
- "@alexkroman1/aai-cli": "^14.0.0",
29
29
  "@tailwindcss/vite": "^4.3.3",
30
- "@types/node": "^26.4.1",
30
+ "@types/node": "^24.10.1",
31
31
  "@types/react": "^19.2.18",
32
32
  "@types/react-dom": "^19.2.5",
33
33
  "@vitejs/plugin-react": "^6.1.1",