@alexkroman1/aai 0.7.12 → 0.8.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 (81) hide show
  1. package/dist/aai.js +1 -1
  2. package/dist/cli.js +656 -354
  3. package/dist/sdk/_internal_types.d.ts +0 -1
  4. package/dist/sdk/_internal_types.d.ts.map +1 -1
  5. package/dist/sdk/_internal_types.js.map +1 -1
  6. package/dist/sdk/_render_check.d.ts +7 -0
  7. package/dist/sdk/_render_check.d.ts.map +1 -0
  8. package/dist/sdk/_render_check.js +38 -0
  9. package/dist/sdk/_render_check.js.map +1 -0
  10. package/dist/sdk/builtin_tools.d.ts.map +1 -1
  11. package/dist/sdk/builtin_tools.js +21 -0
  12. package/dist/sdk/builtin_tools.js.map +1 -1
  13. package/dist/sdk/define_agent.d.ts.map +1 -1
  14. package/dist/sdk/define_agent.js +0 -1
  15. package/dist/sdk/define_agent.js.map +1 -1
  16. package/dist/sdk/direct_executor.d.ts.map +1 -1
  17. package/dist/sdk/direct_executor.js +0 -1
  18. package/dist/sdk/direct_executor.js.map +1 -1
  19. package/dist/sdk/memory_tools.d.ts +38 -0
  20. package/dist/sdk/memory_tools.d.ts.map +1 -0
  21. package/dist/sdk/memory_tools.js +77 -0
  22. package/dist/sdk/memory_tools.js.map +1 -0
  23. package/dist/sdk/mod.d.ts +3 -1
  24. package/dist/sdk/mod.d.ts.map +1 -1
  25. package/dist/sdk/mod.js +2 -0
  26. package/dist/sdk/mod.js.map +1 -1
  27. package/dist/sdk/protocol.d.ts +3 -3
  28. package/dist/sdk/protocol.js +1 -1
  29. package/dist/sdk/s2s.d.ts.map +1 -1
  30. package/dist/sdk/s2s.js +3 -1
  31. package/dist/sdk/s2s.js.map +1 -1
  32. package/dist/sdk/session.d.ts.map +1 -1
  33. package/dist/sdk/session.js +2 -0
  34. package/dist/sdk/session.js.map +1 -1
  35. package/dist/sdk/types.d.ts +23 -14
  36. package/dist/sdk/types.d.ts.map +1 -1
  37. package/dist/sdk/types.js +29 -0
  38. package/dist/sdk/types.js.map +1 -1
  39. package/dist/ui/_components/app.d.ts.map +1 -1
  40. package/dist/ui/_components/app.js +6 -7
  41. package/dist/ui/_components/app.js.map +1 -1
  42. package/dist/ui/_components/sidebar_layout.d.ts +19 -0
  43. package/dist/ui/_components/sidebar_layout.d.ts.map +1 -0
  44. package/dist/ui/_components/sidebar_layout.js +21 -0
  45. package/dist/ui/_components/sidebar_layout.js.map +1 -0
  46. package/dist/ui/_components/start_screen.d.ts +24 -0
  47. package/dist/ui/_components/start_screen.d.ts.map +1 -0
  48. package/dist/ui/_components/start_screen.js +25 -0
  49. package/dist/ui/_components/start_screen.js.map +1 -0
  50. package/dist/ui/_hooks.d.ts +21 -0
  51. package/dist/ui/_hooks.d.ts.map +1 -0
  52. package/dist/ui/_hooks.js +35 -0
  53. package/dist/ui/_hooks.js.map +1 -0
  54. package/dist/ui/components.d.ts +20 -0
  55. package/dist/ui/components.d.ts.map +1 -1
  56. package/dist/ui/components.js +12 -0
  57. package/dist/ui/components.js.map +1 -1
  58. package/dist/ui/components_mod.d.ts +2 -1
  59. package/dist/ui/components_mod.d.ts.map +1 -1
  60. package/dist/ui/components_mod.js +2 -1
  61. package/dist/ui/components_mod.js.map +1 -1
  62. package/dist/ui/mod.d.ts +2 -1
  63. package/dist/ui/mod.d.ts.map +1 -1
  64. package/dist/ui/mod.js +2 -1
  65. package/dist/ui/mod.js.map +1 -1
  66. package/dist/ui/signals.d.ts.map +1 -1
  67. package/dist/ui/signals.js +5 -2
  68. package/dist/ui/signals.js.map +1 -1
  69. package/package.json +17 -1
  70. package/templates/_shared/CLAUDE.md +241 -121
  71. package/templates/_shared/package.json +4 -1
  72. package/templates/dispatch-center/agent.ts +43 -72
  73. package/templates/embedded-assets/agent.ts +4 -5
  74. package/templates/health-assistant/agent.ts +7 -7
  75. package/templates/infocom-adventure/agent.ts +20 -20
  76. package/templates/memory-agent/agent.ts +1 -55
  77. package/templates/night-owl/agent.ts +4 -4
  78. package/templates/night-owl/client.tsx +6 -23
  79. package/templates/pizza-ordering/agent.ts +14 -15
  80. package/templates/pizza-ordering/client.tsx +41 -101
  81. package/templates/smart-research/agent.ts +10 -10
package/dist/cli.js CHANGED
@@ -62,6 +62,7 @@ function rootHelp(version) {
62
62
  const cmds = [
63
63
  ["init", "[dir]", "Scaffold a new agent project"],
64
64
  ["dev", "", "Start a local development server"],
65
+ ["build", "", "Bundle and validate (no server or deploy)"],
65
66
  ["deploy", "", "Bundle and deploy to production"],
66
67
  ["start", "", "Start production server from build"],
67
68
  ["secret", "<cmd>", "Manage secrets"],
@@ -134,7 +135,6 @@ var init_help = __esm({
134
135
 
135
136
  // cli/_bundler.ts
136
137
  import fs from "node:fs/promises";
137
- import { createRequire } from "node:module";
138
138
  import path from "node:path";
139
139
  import preact from "@preact/preset-vite";
140
140
  import tailwindcss from "@tailwindcss/vite";
@@ -160,18 +160,6 @@ async function readDirRecursive(dir, base = dir) {
160
160
  }
161
161
  return files;
162
162
  }
163
- async function findPackageDir(resolvedEntry, packageName) {
164
- let dir = path.dirname(resolvedEntry);
165
- while (dir !== path.dirname(dir)) {
166
- try {
167
- const pkg = JSON.parse(await fs.readFile(path.join(dir, "package.json"), "utf-8"));
168
- if (pkg.name === packageName) return dir;
169
- } catch {
170
- }
171
- dir = path.dirname(dir);
172
- }
173
- return path.dirname(resolvedEntry);
174
- }
175
163
  async function bundleAgent(agent, opts) {
176
164
  const aaiDir = path.join(agent.dir, ".aai");
177
165
  const buildDir = path.join(aaiDir, "build");
@@ -211,24 +199,12 @@ async function bundleAgent(agent, opts) {
211
199
  }
212
200
  const skipClient = opts?.skipClient || !agent.clientEntry;
213
201
  if (!skipClient) {
214
- const _require = createRequire(import.meta.url);
215
- const preactDir = path.dirname(_require.resolve("preact/package.json"));
216
- const preactSignalsDir = await findPackageDir(
217
- _require.resolve("@preact/signals"),
218
- "@preact/signals"
219
- );
220
202
  try {
221
203
  await build({
222
204
  root: agent.dir,
223
205
  base: "./",
224
206
  logLevel: "warn",
225
207
  plugins: [preact(), tailwindcss()],
226
- resolve: {
227
- alias: {
228
- preact: preactDir,
229
- "@preact/signals": preactSignalsDir
230
- }
231
- },
232
208
  build: {
233
209
  outDir: clientDir,
234
210
  emptyOutDir: true,
@@ -302,6 +278,7 @@ async function askText(message, defaultValue) {
302
278
  onSubmit: (value) => {
303
279
  resolve(value || defaultValue);
304
280
  app.unmount();
281
+ app.clear();
305
282
  }
306
283
  }
307
284
  )
@@ -444,57 +421,6 @@ var init_discover = __esm({
444
421
  }
445
422
  });
446
423
 
447
- // cli/_deploy.ts
448
- async function attemptDeploy(url, slug, apiKey, env, worker, clientFiles) {
449
- return await _internals.fetch(`${url}/${slug}/deploy`, {
450
- method: "POST",
451
- headers: {
452
- "Content-Type": "application/json",
453
- Authorization: `Bearer ${apiKey}`
454
- },
455
- body: JSON.stringify({
456
- env,
457
- worker,
458
- clientFiles
459
- })
460
- });
461
- }
462
- async function runDeploy(opts) {
463
- const worker = opts.bundle.worker;
464
- const clientFiles = opts.bundle.clientFiles;
465
- let slug = opts.slug;
466
- if (opts.dryRun) {
467
- return { slug };
468
- }
469
- for (let i = 0; i < MAX_RETRIES; i++) {
470
- const resp = await attemptDeploy(opts.url, slug, opts.apiKey, opts.env, worker, clientFiles);
471
- if (resp.ok) {
472
- return { slug };
473
- }
474
- if (resp.status === 403) {
475
- const text2 = await resp.text();
476
- if (text2.includes("Slug")) {
477
- slug = generateSlug();
478
- continue;
479
- }
480
- }
481
- const text = await resp.text();
482
- throw new Error(`deploy failed (${resp.status}): ${text}`);
483
- }
484
- throw new Error(`deploy failed: could not find available slug after ${MAX_RETRIES} attempts`);
485
- }
486
- var _internals, MAX_RETRIES;
487
- var init_deploy = __esm({
488
- "cli/_deploy.ts"() {
489
- "use strict";
490
- init_discover();
491
- _internals = {
492
- fetch: globalThis.fetch.bind(globalThis)
493
- };
494
- MAX_RETRIES = 20;
495
- }
496
- });
497
-
498
424
  // cli/_ink.tsx
499
425
  import { Spinner } from "@inkjs/ui";
500
426
  import { Box as Box2, render as render2, Static, Text as Text2, useApp } from "ink";
@@ -622,7 +548,7 @@ async function runWithInk(fn) {
622
548
  )
623
549
  );
624
550
  await app.waitUntilExit();
625
- if (thrownError) throw thrownError;
551
+ if (thrownError) process.exit(1);
626
552
  }
627
553
  var init_ink = __esm({
628
554
  "cli/_ink.tsx"() {
@@ -631,6 +557,244 @@ var init_ink = __esm({
631
557
  }
632
558
  });
633
559
 
560
+ // ui/_dom_shim.ts
561
+ import { DOMParser } from "linkedom";
562
+ function installDomShim() {
563
+ if (installed) return;
564
+ installed = true;
565
+ const doc = new DOMParser().parseFromString(
566
+ "<!DOCTYPE html><html><head></head><body></body></html>",
567
+ "text/html"
568
+ );
569
+ const g2 = globalThis;
570
+ g2.document = doc;
571
+ g2.HTMLElement = doc.documentElement.constructor;
572
+ if (!Object.getOwnPropertyDescriptor(
573
+ g2.HTMLElement.prototype,
574
+ "scrollIntoView"
575
+ )) {
576
+ g2.HTMLElement.prototype.scrollIntoView = () => {
577
+ };
578
+ }
579
+ }
580
+ var installed;
581
+ var init_dom_shim = __esm({
582
+ "ui/_dom_shim.ts"() {
583
+ "use strict";
584
+ installed = false;
585
+ }
586
+ });
587
+
588
+ // sdk/_mock_ws.ts
589
+ function installMockWebSocket() {
590
+ const saved = globalThis.WebSocket;
591
+ const created = [];
592
+ g.WebSocket = class extends MockWebSocket {
593
+ constructor(url, protocols) {
594
+ super(url, protocols);
595
+ created.push(this);
596
+ }
597
+ };
598
+ return {
599
+ created,
600
+ get lastWs() {
601
+ return created.at(-1) ?? null;
602
+ },
603
+ restore() {
604
+ globalThis.WebSocket = saved;
605
+ },
606
+ [Symbol.dispose]() {
607
+ globalThis.WebSocket = saved;
608
+ }
609
+ };
610
+ }
611
+ var MockWebSocket, g;
612
+ var init_mock_ws = __esm({
613
+ "sdk/_mock_ws.ts"() {
614
+ "use strict";
615
+ MockWebSocket = class _MockWebSocket extends EventTarget {
616
+ // mirrors the WebSocket API
617
+ static CONNECTING = 0;
618
+ // mirrors the WebSocket API
619
+ static OPEN = 1;
620
+ // mirrors the WebSocket API
621
+ static CLOSING = 2;
622
+ // mirrors the WebSocket API
623
+ static CLOSED = 3;
624
+ readyState = _MockWebSocket.CONNECTING;
625
+ binaryType = "arraybuffer";
626
+ /** All messages passed to {@linkcode send}, in order. */
627
+ sent = [];
628
+ url;
629
+ /**
630
+ * Create a new MockWebSocket.
631
+ *
632
+ * Automatically transitions to `OPEN` state on the next microtask,
633
+ * dispatching an `"open"` event.
634
+ *
635
+ * @param url - The WebSocket URL.
636
+ * @param _protocols - Ignored; accepted for API compatibility.
637
+ */
638
+ constructor(url, _protocols) {
639
+ super();
640
+ this.url = typeof url === "string" ? url : url.toString();
641
+ queueMicrotask(() => {
642
+ if (this.readyState === _MockWebSocket.CONNECTING) {
643
+ this.readyState = _MockWebSocket.OPEN;
644
+ this.dispatchEvent(new Event("open"));
645
+ }
646
+ });
647
+ }
648
+ /**
649
+ * Record a sent message without transmitting it.
650
+ *
651
+ * @param data - The message data to record.
652
+ */
653
+ send(data) {
654
+ this.sent.push(data);
655
+ }
656
+ /**
657
+ * Transition to `CLOSED` state and dispatch a `"close"` event.
658
+ *
659
+ * @param code - The close code (defaults to 1000).
660
+ * @param _reason - Ignored; accepted for API compatibility.
661
+ */
662
+ close(code, _reason) {
663
+ this.readyState = _MockWebSocket.CLOSED;
664
+ this.dispatchEvent(new CloseEvent("close", { code: code ?? 1e3 }));
665
+ }
666
+ /**
667
+ * Simulate receiving a message from the server.
668
+ *
669
+ * @param data - The message data (string or binary).
670
+ */
671
+ simulateMessage(data) {
672
+ this.dispatchEvent(new MessageEvent("message", { data }));
673
+ }
674
+ /** Transition to `OPEN` state and dispatch an `"open"` event. */
675
+ open() {
676
+ this.readyState = _MockWebSocket.OPEN;
677
+ this.dispatchEvent(new Event("open"));
678
+ }
679
+ /**
680
+ * Shorthand for {@linkcode simulateMessage}.
681
+ *
682
+ * @param data - The message data to dispatch.
683
+ */
684
+ msg(data) {
685
+ this.dispatchEvent(new MessageEvent("message", { data }));
686
+ }
687
+ /**
688
+ * Simulate a connection close from the server.
689
+ *
690
+ * @param code - The close code (defaults to 1000).
691
+ */
692
+ disconnect(code = 1e3) {
693
+ this.dispatchEvent(new CloseEvent("close", { code }));
694
+ }
695
+ /** Dispatch an `"error"` event on this socket. */
696
+ error() {
697
+ this.dispatchEvent(new Event("error"));
698
+ }
699
+ /**
700
+ * Return all sent string messages parsed as JSON objects.
701
+ *
702
+ * Binary messages are filtered out.
703
+ *
704
+ * @returns An array of parsed JSON objects from sent string messages.
705
+ */
706
+ sentJson() {
707
+ return this.sent.filter((d) => typeof d === "string").map((s) => JSON.parse(s));
708
+ }
709
+ };
710
+ g = globalThis;
711
+ }
712
+ });
713
+
714
+ // sdk/_render_check.ts
715
+ var render_check_exports = {};
716
+ __export(render_check_exports, {
717
+ renderCheck: () => renderCheck
718
+ });
719
+ import { createServer as createViteServer } from "vite";
720
+ async function renderCheck(clientEntry, cwd) {
721
+ installDomShim();
722
+ const g2 = globalThis;
723
+ const doc = new DOMParser().parseFromString(
724
+ '<!DOCTYPE html><html><head></head><body><main id="app"></main></body></html>',
725
+ "text/html"
726
+ );
727
+ const prevDoc = g2.document;
728
+ const prevLocation = g2.location;
729
+ g2.document = doc;
730
+ g2.location = { origin: "http://localhost:3000", pathname: "/", href: "http://localhost:3000/" };
731
+ const mock = installMockWebSocket();
732
+ const vite = await createViteServer({
733
+ root: cwd,
734
+ logLevel: "silent",
735
+ server: { middlewareMode: true }
736
+ });
737
+ try {
738
+ await vite.ssrLoadModule(clientEntry);
739
+ const app = doc.querySelector("#app");
740
+ if (!app?.innerHTML) {
741
+ throw new Error("client.tsx render check failed: #app is empty after mount()");
742
+ }
743
+ } finally {
744
+ await vite.close();
745
+ mock.restore();
746
+ g2.document = prevDoc;
747
+ g2.location = prevLocation;
748
+ }
749
+ }
750
+ var init_render_check = __esm({
751
+ "sdk/_render_check.ts"() {
752
+ "use strict";
753
+ init_dom_shim();
754
+ init_mock_ws();
755
+ }
756
+ });
757
+
758
+ // cli/_build.ts
759
+ import React2 from "react";
760
+ async function buildAgentBundle(cwd, log) {
761
+ const agent = await loadAgent(cwd);
762
+ if (!agent) {
763
+ throw new Error("No agent found \u2014 run `aai init` first");
764
+ }
765
+ log(React2.createElement(Step, { action: "Bundle", msg: agent.slug }));
766
+ let bundle;
767
+ try {
768
+ bundle = await bundleAgent(agent);
769
+ } catch (err) {
770
+ if (err instanceof BundleError) {
771
+ throw new Error(`Bundle failed: ${err.message}`);
772
+ }
773
+ throw err;
774
+ }
775
+ const kb = (bundle.workerBytes / 1024).toFixed(1);
776
+ const clientCount = Object.keys(bundle.clientFiles).length;
777
+ log(React2.createElement(Info, { msg: `worker: ${kb} KB, client: ${clientCount} file(s)` }));
778
+ if (agent.clientEntry) {
779
+ log(React2.createElement(Step, { action: "Render", msg: "check" }));
780
+ try {
781
+ const { renderCheck: renderCheck2 } = await Promise.resolve().then(() => (init_render_check(), render_check_exports));
782
+ await renderCheck2(agent.clientEntry, cwd);
783
+ } catch (err) {
784
+ throw new Error(`Render check failed: ${err instanceof Error ? err.message : String(err)}`);
785
+ }
786
+ }
787
+ return bundle;
788
+ }
789
+ var init_build = __esm({
790
+ "cli/_build.ts"() {
791
+ "use strict";
792
+ init_bundler();
793
+ init_discover();
794
+ init_ink();
795
+ }
796
+ });
797
+
634
798
  // cli/_init.ts
635
799
  var init_exports = {};
636
800
  __export(init_exports, {
@@ -719,113 +883,58 @@ var init_init = __esm({
719
883
  }
720
884
  });
721
885
 
722
- // cli/init.tsx
723
- import { execFile } from "node:child_process";
724
- import fs4 from "node:fs/promises";
725
- import path4 from "node:path";
726
- import { fileURLToPath } from "node:url";
727
- import { promisify } from "node:util";
728
- import minimist from "minimist";
729
- import { jsx as jsx3 } from "react/jsx-runtime";
730
- async function rewriteDevDeps(cwd, cliDir2) {
731
- const monorepoRoot = path4.join(cliDir2, "..");
732
- const pkgJsonPath2 = path4.join(cwd, "package.json");
733
- const pkgJson2 = JSON.parse(await fs4.readFile(pkgJsonPath2, "utf-8"));
734
- const rootPkg = JSON.parse(await fs4.readFile(path4.join(monorepoRoot, "package.json"), "utf-8"));
735
- const rootPkgName = rootPkg.name;
736
- if (pkgJson2.dependencies[rootPkgName]) {
737
- pkgJson2.dependencies[rootPkgName] = `file:${monorepoRoot}`;
738
- }
739
- await fs4.writeFile(pkgJsonPath2, `${JSON.stringify(pkgJson2, null, 2)}
740
- `);
741
- }
742
- async function installDeps(cwd, log) {
743
- if (await fileExists(path4.join(cwd, "node_modules"))) return;
744
- let pkgJson2;
745
- try {
746
- pkgJson2 = JSON.parse(await fs4.readFile(path4.join(cwd, "package.json"), "utf-8"));
747
- } catch {
748
- pkgJson2 = {};
749
- }
750
- const deps = Object.keys(pkgJson2.dependencies ?? {});
751
- const devDeps = Object.keys(pkgJson2.devDependencies ?? {});
752
- if (deps.length > 0) {
753
- log(/* @__PURE__ */ jsx3(Step, { action: "Install", msg: deps.join(", ") }));
754
- }
755
- if (devDeps.length > 0) {
756
- log(/* @__PURE__ */ jsx3(Step, { action: "Install", msg: `dev: ${devDeps.join(", ")}` }));
757
- }
886
+ // cli/_deploy.ts
887
+ async function attemptDeploy(url, slug, apiKey, env, worker, clientFiles) {
758
888
  try {
759
- await execFileAsync("npm", ["install"], { cwd });
889
+ return await _internals.fetch(`${url}/${slug}/deploy`, {
890
+ method: "POST",
891
+ headers: {
892
+ "Content-Type": "application/json",
893
+ Authorization: `Bearer ${apiKey}`
894
+ },
895
+ body: JSON.stringify({
896
+ env,
897
+ worker,
898
+ clientFiles
899
+ })
900
+ });
760
901
  } catch {
761
- log(/* @__PURE__ */ jsx3(Warn, { msg: "npm install failed" }));
902
+ throw new Error(`deployment failed: could not reach ${url}`);
762
903
  }
763
904
  }
764
- async function runInitCommand(args, version, opts) {
765
- const parsed = minimist(args, {
766
- string: ["template"],
767
- boolean: ["force", "help"],
768
- alias: { t: "template", f: "force", h: "help" }
769
- });
770
- if (parsed.help) {
771
- console.log(subcommandHelp(initCommandDef, version));
772
- return "";
773
- }
774
- const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
775
- await getApiKey2();
776
- let dir = parsed._[0];
777
- if (!dir) {
778
- dir = await askText("What is your project named?", "my-voice-agent");
779
- }
780
- const cwd = path4.resolve(process.env.INIT_CWD || process.cwd(), dir);
781
- if (!parsed.force && await fileExists(path4.join(cwd, "agent.ts"))) {
782
- console.log(
783
- `agent.ts already exists in this directory. Use ${interactive("--force")} to overwrite.`
784
- );
785
- process.exit(1);
905
+ async function runDeploy(opts) {
906
+ const worker = opts.bundle.worker;
907
+ const clientFiles = opts.bundle.clientFiles;
908
+ let slug = opts.slug;
909
+ if (opts.dryRun) {
910
+ return { slug };
786
911
  }
787
- const cliDir2 = path4.dirname(fileURLToPath(import.meta.url));
788
- const templatesDir = path4.join(cliDir2, "..", "templates");
789
- const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
790
- const template = parsed.template || "simple";
791
- await runWithInk(async (log) => {
792
- log(/* @__PURE__ */ jsx3(Step, { action: "Create", msg: dir }));
793
- await runInit2({ targetDir: cwd, template, templatesDir });
794
- if (isDevMode()) {
795
- await rewriteDevDeps(cwd, cliDir2);
912
+ for (let i = 0; i < MAX_RETRIES; i++) {
913
+ const resp = await attemptDeploy(opts.url, slug, opts.apiKey, opts.env, worker, clientFiles);
914
+ if (resp.ok) {
915
+ return { slug };
796
916
  }
797
- await installDeps(cwd, log);
798
- });
799
- process.chdir(cwd);
800
- delete process.env.INIT_CWD;
801
- if (!opts?.quiet) {
802
- const { runDeployCommand: runDeployCommand2 } = await Promise.resolve().then(() => (init_deploy2(), deploy_exports));
803
- await runDeployCommand2(["-y"], version);
917
+ if (resp.status === 403) {
918
+ const text2 = await resp.text();
919
+ if (text2.includes("Slug")) {
920
+ slug = generateSlug();
921
+ continue;
922
+ }
923
+ }
924
+ const text = await resp.text();
925
+ throw new Error(`deploy failed (${resp.status}): ${text}`);
804
926
  }
805
- return cwd;
927
+ throw new Error(`deploy failed: could not find available slug after ${MAX_RETRIES} attempts`);
806
928
  }
807
- var execFileAsync, initCommandDef;
808
- var init_init2 = __esm({
809
- "cli/init.tsx"() {
929
+ var _internals, MAX_RETRIES;
930
+ var init_deploy = __esm({
931
+ "cli/_deploy.ts"() {
810
932
  "use strict";
811
- init_colors();
812
933
  init_discover();
813
- init_help();
814
- init_ink();
815
- init_prompts();
816
- execFileAsync = promisify(execFile);
817
- initCommandDef = {
818
- name: "init",
819
- description: "Scaffold a new agent project",
820
- args: [{ name: "dir", optional: true }],
821
- options: [
822
- {
823
- flags: "-t, --template <template>",
824
- description: "Template to use"
825
- },
826
- { flags: "-f, --force", description: "Overwrite existing agent.ts" }
827
- ]
934
+ _internals = {
935
+ fetch: globalThis.fetch.bind(globalThis)
828
936
  };
937
+ MAX_RETRIES = 20;
829
938
  }
830
939
  });
831
940
 
@@ -834,33 +943,16 @@ var deploy_exports = {};
834
943
  __export(deploy_exports, {
835
944
  runDeployCommand: () => runDeployCommand
836
945
  });
837
- import path5 from "node:path";
838
- import minimist2 from "minimist";
839
- import { jsx as jsx4 } from "react/jsx-runtime";
946
+ import path4 from "node:path";
947
+ import minimist from "minimist";
948
+ import { jsx as jsx3 } from "react/jsx-runtime";
840
949
  function resolveServerUrl(parsed) {
841
950
  return parsed.server || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
842
951
  }
843
- async function buildAgent(cwd, log) {
844
- log(/* @__PURE__ */ jsx4(Step, { action: "Bundle", msg: "agent" }));
845
- const agent = await loadAgent(cwd);
846
- if (!agent) {
847
- throw new Error("No agent found \u2014 run `aai init` first");
848
- }
849
- let bundle;
850
- try {
851
- bundle = await bundleAgent(agent);
852
- } catch (err) {
853
- if (err instanceof BundleError) {
854
- throw new Error(`Bundle failed: ${err.message}`);
855
- }
856
- throw err;
857
- }
858
- return bundle;
859
- }
860
952
  async function deployBundle(opts) {
861
953
  const { bundle, serverUrl, apiKey, cwd, log } = opts;
862
954
  let { slug } = opts;
863
- log(/* @__PURE__ */ jsx4(Step, { action: "Deploy", msg: slug }));
955
+ log(/* @__PURE__ */ jsx3(Step, { action: "Deploy", msg: slug }));
864
956
  const deployed = await runDeploy({
865
957
  url: serverUrl,
866
958
  bundle,
@@ -872,11 +964,11 @@ async function deployBundle(opts) {
872
964
  slug = deployed.slug;
873
965
  await writeProjectConfig(cwd, { slug, serverUrl });
874
966
  const agentUrl = `${serverUrl}/${slug}`;
875
- log(/* @__PURE__ */ jsx4(Step, { action: "Ready", msg: agentUrl }));
967
+ log(/* @__PURE__ */ jsx3(Step, { action: "Ready", msg: agentUrl }));
876
968
  return agentUrl;
877
969
  }
878
970
  async function runDeployCommand(args, version) {
879
- const parsed = minimist2(args, {
971
+ const parsed = minimist(args, {
880
972
  string: ["server"],
881
973
  boolean: ["dry-run", "help", "yes"],
882
974
  alias: { s: "server", h: "help", y: "yes" }
@@ -886,7 +978,7 @@ async function runDeployCommand(args, version) {
886
978
  return;
887
979
  }
888
980
  const cwd = process.env.INIT_CWD || process.cwd();
889
- if (!await fileExists(path5.join(cwd, "agent.ts"))) {
981
+ if (!await fileExists(path4.join(cwd, "agent.ts"))) {
890
982
  await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
891
983
  }
892
984
  const serverUrl = resolveServerUrl(parsed);
@@ -896,9 +988,9 @@ async function runDeployCommand(args, version) {
896
988
  const slug = projectConfig?.slug ?? generateSlug();
897
989
  let agentUrl = "";
898
990
  await runWithInk(async (log) => {
899
- const bundle = await buildAgent(cwd, log);
991
+ const bundle = await buildAgentBundle(cwd, log);
900
992
  if (dryRun) {
901
- log(/* @__PURE__ */ jsx4(StepInfo, { action: "Dry run", msg: `would deploy as ${slug}` }));
993
+ log(/* @__PURE__ */ jsx3(StepInfo, { action: "Dry run", msg: `would deploy as ${slug}` }));
902
994
  return;
903
995
  }
904
996
  agentUrl = await deployBundle({ bundle, serverUrl, apiKey, slug, cwd, log });
@@ -913,7 +1005,7 @@ var deployCommandDef;
913
1005
  var init_deploy2 = __esm({
914
1006
  "cli/deploy.tsx"() {
915
1007
  "use strict";
916
- init_bundler();
1008
+ init_build();
917
1009
  init_deploy();
918
1010
  init_discover();
919
1011
  init_help();
@@ -935,6 +1027,116 @@ var init_deploy2 = __esm({
935
1027
  }
936
1028
  });
937
1029
 
1030
+ // cli/init.tsx
1031
+ import { execFile } from "node:child_process";
1032
+ import fs4 from "node:fs/promises";
1033
+ import path5 from "node:path";
1034
+ import { fileURLToPath } from "node:url";
1035
+ import { promisify } from "node:util";
1036
+ import minimist2 from "minimist";
1037
+ import { jsx as jsx4 } from "react/jsx-runtime";
1038
+ async function rewriteDevDeps(cwd, cliDir2) {
1039
+ const monorepoRoot = path5.join(cliDir2, "..");
1040
+ const pkgJsonPath2 = path5.join(cwd, "package.json");
1041
+ const pkgJson2 = JSON.parse(await fs4.readFile(pkgJsonPath2, "utf-8"));
1042
+ const rootPkg = JSON.parse(await fs4.readFile(path5.join(monorepoRoot, "package.json"), "utf-8"));
1043
+ const rootPkgName = rootPkg.name;
1044
+ if (pkgJson2.dependencies[rootPkgName]) {
1045
+ pkgJson2.dependencies[rootPkgName] = `file:${monorepoRoot}`;
1046
+ }
1047
+ await fs4.writeFile(pkgJsonPath2, `${JSON.stringify(pkgJson2, null, 2)}
1048
+ `);
1049
+ }
1050
+ async function installDeps(cwd, log) {
1051
+ if (await fileExists(path5.join(cwd, "node_modules"))) return;
1052
+ let pkgJson2;
1053
+ try {
1054
+ pkgJson2 = JSON.parse(await fs4.readFile(path5.join(cwd, "package.json"), "utf-8"));
1055
+ } catch {
1056
+ pkgJson2 = {};
1057
+ }
1058
+ const deps = Object.keys(pkgJson2.dependencies ?? {});
1059
+ const devDeps = Object.keys(pkgJson2.devDependencies ?? {});
1060
+ if (deps.length > 0) {
1061
+ log(/* @__PURE__ */ jsx4(Step, { action: "Install", msg: deps.join(", ") }));
1062
+ }
1063
+ if (devDeps.length > 0) {
1064
+ log(/* @__PURE__ */ jsx4(Step, { action: "Install", msg: `dev: ${devDeps.join(", ")}` }));
1065
+ }
1066
+ try {
1067
+ await execFileAsync("npm", ["install"], { cwd });
1068
+ } catch {
1069
+ log(/* @__PURE__ */ jsx4(Warn, { msg: "npm install failed" }));
1070
+ }
1071
+ }
1072
+ async function runInitCommand(args, version, opts) {
1073
+ const parsed = minimist2(args, {
1074
+ string: ["template"],
1075
+ boolean: ["force", "help"],
1076
+ alias: { t: "template", f: "force", h: "help" }
1077
+ });
1078
+ if (parsed.help) {
1079
+ console.log(subcommandHelp(initCommandDef, version));
1080
+ return "";
1081
+ }
1082
+ const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
1083
+ await getApiKey2();
1084
+ let dir = parsed._[0];
1085
+ if (!dir) {
1086
+ dir = await askText("What is your project named?", "my-voice-agent");
1087
+ }
1088
+ const cwd = path5.resolve(process.env.INIT_CWD || process.cwd(), dir);
1089
+ if (!parsed.force && await fileExists(path5.join(cwd, "agent.ts"))) {
1090
+ console.log(
1091
+ `agent.ts already exists in this directory. Use ${interactive("--force")} to overwrite.`
1092
+ );
1093
+ process.exit(1);
1094
+ }
1095
+ const cliDir2 = path5.dirname(fileURLToPath(import.meta.url));
1096
+ const templatesDir = path5.join(cliDir2, "..", "templates");
1097
+ const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
1098
+ const template = parsed.template || "simple";
1099
+ await runWithInk(async (log) => {
1100
+ log(/* @__PURE__ */ jsx4(Step, { action: "Create", msg: dir }));
1101
+ await runInit2({ targetDir: cwd, template, templatesDir });
1102
+ if (isDevMode()) {
1103
+ await rewriteDevDeps(cwd, cliDir2);
1104
+ }
1105
+ await installDeps(cwd, log);
1106
+ });
1107
+ process.chdir(cwd);
1108
+ delete process.env.INIT_CWD;
1109
+ if (!opts?.quiet) {
1110
+ const { runDeployCommand: runDeployCommand2 } = await Promise.resolve().then(() => (init_deploy2(), deploy_exports));
1111
+ await runDeployCommand2(["-y"], version);
1112
+ }
1113
+ return cwd;
1114
+ }
1115
+ var execFileAsync, initCommandDef;
1116
+ var init_init2 = __esm({
1117
+ "cli/init.tsx"() {
1118
+ "use strict";
1119
+ init_colors();
1120
+ init_discover();
1121
+ init_help();
1122
+ init_ink();
1123
+ init_prompts();
1124
+ execFileAsync = promisify(execFile);
1125
+ initCommandDef = {
1126
+ name: "init",
1127
+ description: "Scaffold a new agent project",
1128
+ args: [{ name: "dir", optional: true }],
1129
+ options: [
1130
+ {
1131
+ flags: "-t, --template <template>",
1132
+ description: "Template to use"
1133
+ },
1134
+ { flags: "-f, --force", description: "Overwrite existing agent.ts" }
1135
+ ]
1136
+ };
1137
+ }
1138
+ });
1139
+
938
1140
  // sdk/protocol.ts
939
1141
  import { z } from "zod";
940
1142
  var DEFAULT_TTS_SAMPLE_RATE, AUDIO_FORMAT, _bitsPerSample, _channels, AudioFrameSpec, KvRequestBaseSchema, HOOK_TIMEOUT_MS, SessionErrorCodeSchema, TranscriptEventSchema, ClientEventSchema, ClientMessageSchema;
@@ -1094,9 +1296,86 @@ var init_internal_types = __esm({
1094
1296
  }
1095
1297
  });
1096
1298
 
1299
+ // sdk/types.ts
1300
+ function tool(def) {
1301
+ return def;
1302
+ }
1303
+ var DEFAULT_INSTRUCTIONS;
1304
+ var init_types = __esm({
1305
+ "sdk/types.ts"() {
1306
+ "use strict";
1307
+ DEFAULT_INSTRUCTIONS = `You are AAI, a helpful AI assistant.
1308
+
1309
+ Voice-First Rules:
1310
+ - Optimize for natural speech. Avoid jargon unless central to the answer. Use short, punchy sentences.
1311
+ - Never mention "search results," "sources," or "the provided text." Speak as if the knowledge is your own.
1312
+ - No visual formatting. Do not say "bullet point," "bold," or "bracketed one." If you need to list items, say "First," "Next," and "Finally."
1313
+ - Start with the most important information. No introductory filler.
1314
+ - Be concise. Keep answers to 1-3 sentences. For complex topics, provide a high-level summary.
1315
+ - Be confident. Avoid hedging phrases like "It seems that" or "I believe."
1316
+ - If you don't have enough information, say so directly rather than guessing.
1317
+ - Never use exclamation points. Keep your tone calm and conversational.`;
1318
+ }
1319
+ });
1320
+
1321
+ // sdk/memory_tools.ts
1322
+ import { z as z3 } from "zod";
1323
+ function memoryTools() {
1324
+ return {
1325
+ save_memory: tool({
1326
+ description: "Save a piece of information to persistent memory. Use a descriptive key like 'user:name' or 'project:status'.",
1327
+ parameters: z3.object({
1328
+ key: z3.string().describe("A descriptive key for this memory (e.g. 'user:name', 'preference:color')"),
1329
+ value: z3.string().describe("The information to remember")
1330
+ }),
1331
+ execute: async ({ key, value }, ctx) => {
1332
+ await ctx.kv.set(key, value);
1333
+ return { saved: key };
1334
+ }
1335
+ }),
1336
+ recall_memory: tool({
1337
+ description: "Retrieve a previously saved memory by its key.",
1338
+ parameters: z3.object({
1339
+ key: z3.string().describe("The key to look up")
1340
+ }),
1341
+ execute: async ({ key }, ctx) => {
1342
+ const value = await ctx.kv.get(key);
1343
+ if (value === null) return { found: false, key };
1344
+ return { found: true, key, value };
1345
+ }
1346
+ }),
1347
+ list_memories: tool({
1348
+ description: "List all saved memory keys, optionally filtered by a prefix (e.g. 'user:').",
1349
+ parameters: z3.object({
1350
+ prefix: z3.string().describe("Prefix to filter keys (e.g. 'user:'). Use empty string for all.").optional()
1351
+ }),
1352
+ execute: async ({ prefix }, ctx) => {
1353
+ const entries = await ctx.kv.list(prefix ?? "");
1354
+ return { count: entries.length, keys: entries.map((e) => e.key) };
1355
+ }
1356
+ }),
1357
+ forget_memory: tool({
1358
+ description: "Delete a previously saved memory by its key.",
1359
+ parameters: z3.object({
1360
+ key: z3.string().describe("The key to delete")
1361
+ }),
1362
+ execute: async ({ key }, ctx) => {
1363
+ await ctx.kv.delete(key);
1364
+ return { deleted: key };
1365
+ }
1366
+ })
1367
+ };
1368
+ }
1369
+ var init_memory_tools = __esm({
1370
+ "sdk/memory_tools.ts"() {
1371
+ "use strict";
1372
+ init_types();
1373
+ }
1374
+ });
1375
+
1097
1376
  // sdk/builtin_tools.ts
1098
1377
  import { convert } from "html-to-text";
1099
- import { z as z3 } from "zod";
1378
+ import { z as z4 } from "zod";
1100
1379
  function htmlToText(html) {
1101
1380
  return convert(html, { wordwrap: false });
1102
1381
  }
@@ -1249,12 +1528,27 @@ function getBuiltinToolDefs(names, opts) {
1249
1528
  defs[name] = createVectorSearch(opts.vectorSearch);
1250
1529
  }
1251
1530
  break;
1531
+ case "memory": {
1532
+ const mt = memoryTools();
1533
+ for (const [toolName, toolDef] of Object.entries(mt)) {
1534
+ defs[toolName] = toolDef;
1535
+ }
1536
+ break;
1537
+ }
1252
1538
  }
1253
1539
  }
1254
1540
  return defs;
1255
1541
  }
1256
1542
  function getBuiltinToolSchemas(names) {
1257
1543
  return names.flatMap((name) => {
1544
+ const multiCreator = MULTI_TOOL_BUILTINS[name];
1545
+ if (multiCreator) {
1546
+ return Object.entries(multiCreator()).map(([toolName, def2]) => ({
1547
+ name: toolName,
1548
+ description: def2.description,
1549
+ parameters: z4.toJSONSchema(def2.parameters ?? EMPTY_PARAMS)
1550
+ }));
1551
+ }
1258
1552
  const creator = TOOL_CREATORS[name];
1259
1553
  if (!creator) return [];
1260
1554
  const def = creator();
@@ -1262,49 +1556,50 @@ function getBuiltinToolSchemas(names) {
1262
1556
  {
1263
1557
  name,
1264
1558
  description: def.description,
1265
- parameters: z3.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1559
+ parameters: z4.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1266
1560
  }
1267
1561
  ];
1268
1562
  });
1269
1563
  }
1270
- var webSearchParams, BRAVE_SEARCH_URL, BraveSearchResponseSchema, MAX_PAGE_CHARS, MAX_HTML_BYTES, visitWebpageParams, fetchJsonParams, runCodeParams, vectorSearchParams, TOOL_CREATORS;
1564
+ var webSearchParams, BRAVE_SEARCH_URL, BraveSearchResponseSchema, MAX_PAGE_CHARS, MAX_HTML_BYTES, visitWebpageParams, fetchJsonParams, runCodeParams, vectorSearchParams, TOOL_CREATORS, MULTI_TOOL_BUILTINS;
1271
1565
  var init_builtin_tools = __esm({
1272
1566
  "sdk/builtin_tools.ts"() {
1273
1567
  "use strict";
1274
1568
  init_internal_types();
1275
- webSearchParams = z3.object({
1276
- query: z3.string().describe("The search query"),
1277
- max_results: z3.number().describe("Maximum number of results to return (default 5)").optional()
1569
+ init_memory_tools();
1570
+ webSearchParams = z4.object({
1571
+ query: z4.string().describe("The search query"),
1572
+ max_results: z4.number().describe("Maximum number of results to return (default 5)").optional()
1278
1573
  });
1279
1574
  BRAVE_SEARCH_URL = "https://api.search.brave.com/res/v1/web/search";
1280
- BraveSearchResponseSchema = z3.object({
1281
- web: z3.object({
1282
- results: z3.array(
1283
- z3.object({
1284
- title: z3.string(),
1285
- url: z3.string(),
1286
- description: z3.string()
1575
+ BraveSearchResponseSchema = z4.object({
1576
+ web: z4.object({
1577
+ results: z4.array(
1578
+ z4.object({
1579
+ title: z4.string(),
1580
+ url: z4.string(),
1581
+ description: z4.string()
1287
1582
  })
1288
1583
  )
1289
1584
  }).optional()
1290
1585
  });
1291
1586
  MAX_PAGE_CHARS = 1e4;
1292
1587
  MAX_HTML_BYTES = 2e5;
1293
- visitWebpageParams = z3.object({
1294
- url: z3.string().describe("The full URL to fetch (e.g., 'https://example.com/page')")
1588
+ visitWebpageParams = z4.object({
1589
+ url: z4.string().describe("The full URL to fetch (e.g., 'https://example.com/page')")
1295
1590
  });
1296
- fetchJsonParams = z3.object({
1297
- url: z3.string().describe("The URL to fetch JSON from"),
1298
- headers: z3.record(z3.string(), z3.string()).describe("Optional HTTP headers to include in the request").optional()
1591
+ fetchJsonParams = z4.object({
1592
+ url: z4.string().describe("The URL to fetch JSON from"),
1593
+ headers: z4.record(z4.string(), z4.string()).describe("Optional HTTP headers to include in the request").optional()
1299
1594
  });
1300
- runCodeParams = z3.object({
1301
- code: z3.string().describe("JavaScript code to execute. Use console.log() for output.")
1595
+ runCodeParams = z4.object({
1596
+ code: z4.string().describe("JavaScript code to execute. Use console.log() for output.")
1302
1597
  });
1303
- vectorSearchParams = z3.object({
1304
- query: z3.string().describe(
1598
+ vectorSearchParams = z4.object({
1599
+ query: z4.string().describe(
1305
1600
  'Short keyword query to search the knowledge base. Use specific topic terms, not full sentences. Do NOT include the company or product name since all documents are from the same source. For example, if the user asks "how much does Acme cost", search for "pricing plans rates".'
1306
1601
  ),
1307
- topK: z3.number().describe("Maximum results to return (default: 5)").optional()
1602
+ topK: z4.number().describe("Maximum results to return (default: 5)").optional()
1308
1603
  });
1309
1604
  TOOL_CREATORS = {
1310
1605
  web_search: createWebSearch,
@@ -1314,6 +1609,9 @@ var init_builtin_tools = __esm({
1314
1609
  // vector_search uses a stub for schema generation
1315
1610
  vector_search: () => createVectorSearch(async () => "")
1316
1611
  };
1612
+ MULTI_TOOL_BUILTINS = {
1613
+ memory: memoryTools
1614
+ };
1317
1615
  }
1318
1616
  });
1319
1617
 
@@ -1384,7 +1682,7 @@ var init_kv = __esm({
1384
1682
  });
1385
1683
 
1386
1684
  // sdk/s2s.ts
1387
- import { z as z4 } from "zod";
1685
+ import { z as z5 } from "zod";
1388
1686
  function uint8ToBase64(bytes) {
1389
1687
  return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
1390
1688
  }
@@ -1554,7 +1852,7 @@ function connectS2s(opts) {
1554
1852
  return;
1555
1853
  }
1556
1854
  const obj = raw;
1557
- if (obj.type !== "reply.audio" && obj.type !== "input.audio") {
1855
+ if (obj.type !== "reply.audio" && obj.type !== "input.audio" && obj.type !== "transcript.agent.delta") {
1558
1856
  log.info(`S2S << ${obj.type}`);
1559
1857
  }
1560
1858
  if (obj.type === "reply.audio" && typeof obj.data === "string") {
@@ -1600,66 +1898,47 @@ var init_s2s = __esm({
1600
1898
  "use strict";
1601
1899
  init_runtime();
1602
1900
  WS_OPEN = 1;
1603
- S2sServerMessageSchema = z4.discriminatedUnion("type", [
1604
- z4.object({ type: z4.literal("session.ready"), session_id: z4.string() }),
1605
- z4.object({ type: z4.literal("session.updated") }).passthrough(),
1606
- z4.object({ type: z4.literal("input.speech.started") }),
1607
- z4.object({ type: z4.literal("input.speech.stopped") }),
1608
- z4.object({ type: z4.literal("transcript.user.delta"), text: z4.string() }),
1609
- z4.object({
1610
- type: z4.literal("transcript.user"),
1611
- item_id: z4.string(),
1612
- text: z4.string()
1901
+ S2sServerMessageSchema = z5.discriminatedUnion("type", [
1902
+ z5.object({ type: z5.literal("session.ready"), session_id: z5.string() }),
1903
+ z5.object({ type: z5.literal("session.updated") }).passthrough(),
1904
+ z5.object({ type: z5.literal("input.speech.started") }),
1905
+ z5.object({ type: z5.literal("input.speech.stopped") }),
1906
+ z5.object({ type: z5.literal("transcript.user.delta"), text: z5.string() }),
1907
+ z5.object({
1908
+ type: z5.literal("transcript.user"),
1909
+ item_id: z5.string(),
1910
+ text: z5.string()
1613
1911
  }),
1614
- z4.object({ type: z4.literal("reply.started"), reply_id: z4.string() }),
1912
+ z5.object({ type: z5.literal("reply.started"), reply_id: z5.string() }),
1615
1913
  // reply.audio is handled on the fast path before Zod — see message handler.
1616
- z4.object({ type: z4.literal("transcript.agent.delta"), delta: z4.string() }).passthrough(),
1617
- z4.object({ type: z4.literal("transcript.agent"), text: z4.string() }),
1618
- z4.object({ type: z4.literal("reply.content_part.started") }).passthrough(),
1619
- z4.object({ type: z4.literal("reply.content_part.done") }).passthrough(),
1620
- z4.object({
1621
- type: z4.literal("tool.call"),
1622
- call_id: z4.string(),
1623
- name: z4.string(),
1624
- args: z4.record(z4.string(), z4.unknown()).optional().default({})
1914
+ z5.object({ type: z5.literal("transcript.agent.delta"), delta: z5.string() }).passthrough(),
1915
+ z5.object({ type: z5.literal("transcript.agent"), text: z5.string() }),
1916
+ z5.object({ type: z5.literal("reply.content_part.started") }).passthrough(),
1917
+ z5.object({ type: z5.literal("reply.content_part.done") }).passthrough(),
1918
+ z5.object({
1919
+ type: z5.literal("tool.call"),
1920
+ call_id: z5.string(),
1921
+ name: z5.string(),
1922
+ args: z5.record(z5.string(), z5.unknown()).optional().default({})
1625
1923
  }),
1626
- z4.object({
1627
- type: z4.literal("reply.done"),
1628
- status: z4.string().optional()
1924
+ z5.object({
1925
+ type: z5.literal("reply.done"),
1926
+ status: z5.string().optional()
1629
1927
  }),
1630
- z4.object({
1631
- type: z4.literal("session.error"),
1632
- code: z4.string(),
1633
- message: z4.string()
1928
+ z5.object({
1929
+ type: z5.literal("session.error"),
1930
+ code: z5.string(),
1931
+ message: z5.string()
1634
1932
  }),
1635
1933
  // Connection-level error (bare "error" without "session." prefix).
1636
- z4.object({
1637
- type: z4.literal("error"),
1638
- message: z4.string()
1934
+ z5.object({
1935
+ type: z5.literal("error"),
1936
+ message: z5.string()
1639
1937
  })
1640
1938
  ]);
1641
1939
  }
1642
1940
  });
1643
1941
 
1644
- // sdk/types.ts
1645
- var DEFAULT_INSTRUCTIONS;
1646
- var init_types = __esm({
1647
- "sdk/types.ts"() {
1648
- "use strict";
1649
- DEFAULT_INSTRUCTIONS = `You are AAI, a helpful AI assistant.
1650
-
1651
- Voice-First Rules:
1652
- - Optimize for natural speech. Avoid jargon unless central to the answer. Use short, punchy sentences.
1653
- - Never mention "search results," "sources," or "the provided text." Speak as if the knowledge is your own.
1654
- - No visual formatting. Do not say "bullet point," "bold," or "bracketed one." If you need to list items, say "First," "Next," and "Finally."
1655
- - Start with the most important information. No introductory filler.
1656
- - Be concise. Keep answers to 1-3 sentences. For complex topics, provide a high-level summary.
1657
- - Be confident. Avoid hedging phrases like "It seems that" or "I believe."
1658
- - If you don't have enough information, say so directly rather than guessing.
1659
- - Never use exclamation points. Keep your tone calm and conversational.`;
1660
- }
1661
- });
1662
-
1663
1942
  // sdk/system_prompt.ts
1664
1943
  function buildSystemPrompt(config, opts) {
1665
1944
  const { hasTools } = opts;
@@ -1884,8 +2163,8 @@ function createS2sSession(opts) {
1884
2163
  pendingTools = [];
1885
2164
  client.event({ type: "cancelled" });
1886
2165
  } else if (pendingTools.length > 0) {
1887
- for (const tool of pendingTools) {
1888
- s2s?.sendToolResult(tool.call_id, tool.result);
2166
+ for (const tool2 of pendingTools) {
2167
+ s2s?.sendToolResult(tool2.call_id, tool2.result);
1889
2168
  }
1890
2169
  pendingTools = [];
1891
2170
  } else {
@@ -1903,6 +2182,7 @@ function createS2sSession(opts) {
1903
2182
  code: "internal",
1904
2183
  message: e.detail.message
1905
2184
  });
2185
+ handle.close();
1906
2186
  });
1907
2187
  handle.addEventListener("close", () => {
1908
2188
  log.info("S2S closed");
@@ -2066,8 +2346,8 @@ function buildToolContext(opts) {
2066
2346
  };
2067
2347
  }
2068
2348
  async function executeToolCall(name, args, options) {
2069
- const { tool } = options;
2070
- const schema = tool.parameters ?? EMPTY_PARAMS;
2349
+ const { tool: tool2 } = options;
2350
+ const schema = tool2.parameters ?? EMPTY_PARAMS;
2071
2351
  const parsed = schema.safeParse(args);
2072
2352
  if (!parsed.success) {
2073
2353
  const issues = (parsed.error?.issues ?? []).map((i) => `${i.path.map(String).join(".")}: ${i.message}`).join(", ");
@@ -2076,7 +2356,7 @@ async function executeToolCall(name, args, options) {
2076
2356
  try {
2077
2357
  const ctx = buildToolContext(options);
2078
2358
  await yieldTick();
2079
- const result = await Promise.resolve(tool.execute(parsed.data, ctx));
2359
+ const result = await Promise.resolve(tool2.execute(parsed.data, ctx));
2080
2360
  await yieldTick();
2081
2361
  if (result == null) return "null";
2082
2362
  return typeof result === "string" ? result : JSON.stringify(result);
@@ -2104,8 +2384,7 @@ function buildAgentConfig(agent) {
2104
2384
  const config = {
2105
2385
  name: agent.name,
2106
2386
  instructions: agent.instructions,
2107
- greeting: agent.greeting,
2108
- voice: agent.voice
2387
+ greeting: agent.greeting
2109
2388
  };
2110
2389
  if (agent.sttPrompt !== void 0) config.sttPrompt = agent.sttPrompt;
2111
2390
  if (typeof agent.maxSteps !== "function") config.maxSteps = agent.maxSteps;
@@ -2160,10 +2439,10 @@ function createDirectExecutor(opts) {
2160
2439
  };
2161
2440
  }
2162
2441
  const executeTool = async (name, args, sessionId, messages) => {
2163
- const tool = allTools[name];
2164
- if (!tool) return JSON.stringify({ error: `Unknown tool: ${name}` });
2442
+ const tool2 = allTools[name];
2443
+ if (!tool2) return JSON.stringify({ error: `Unknown tool: ${name}` });
2165
2444
  return executeToolCall(name, args, {
2166
- tool,
2445
+ tool: tool2,
2167
2446
  env: frozenEnv,
2168
2447
  sessionId,
2169
2448
  state: getState(sessionId ?? ""),
@@ -2633,30 +2912,64 @@ var init_server = __esm({
2633
2912
 
2634
2913
  // cli/cli.ts
2635
2914
  init_help();
2636
- init_deploy2();
2637
2915
  import { readFileSync } from "node:fs";
2638
- import path10 from "node:path";
2916
+ import path11 from "node:path";
2639
2917
  import { fileURLToPath as fileURLToPath2 } from "node:url";
2640
- import minimist7 from "minimist";
2918
+ import minimist8 from "minimist";
2641
2919
 
2642
- // cli/dev.tsx
2643
- import path7 from "node:path";
2920
+ // cli/build.tsx
2921
+ init_build();
2922
+ init_discover();
2923
+ init_help();
2924
+ init_ink();
2925
+ init_init2();
2926
+ import path6 from "node:path";
2644
2927
  import minimist3 from "minimist";
2928
+ import { jsx as jsx5 } from "react/jsx-runtime";
2929
+ var buildCommandDef = {
2930
+ name: "build",
2931
+ description: "Bundle agent and client (validates code without deploying or starting a server)",
2932
+ options: [{ flags: "-y, --yes", description: "Accept defaults (no prompts)" }]
2933
+ };
2934
+ async function runBuildCommand(args, version) {
2935
+ const parsed = minimist3(args, {
2936
+ boolean: ["help", "yes"],
2937
+ alias: { h: "help", y: "yes" }
2938
+ });
2939
+ if (parsed.help) {
2940
+ console.log(subcommandHelp(buildCommandDef, version));
2941
+ return;
2942
+ }
2943
+ const cwd = process.env.INIT_CWD || process.cwd();
2944
+ if (!await fileExists(path6.join(cwd, "agent.ts"))) {
2945
+ await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
2946
+ }
2947
+ await runWithInk(async (log) => {
2948
+ await buildAgentBundle(cwd, log);
2949
+ log(/* @__PURE__ */ jsx5(Step, { action: "Build", msg: "ok" }));
2950
+ });
2951
+ }
2952
+
2953
+ // cli/cli.ts
2954
+ init_deploy2();
2955
+
2956
+ // cli/dev.tsx
2957
+ import path8 from "node:path";
2958
+ import minimist4 from "minimist";
2645
2959
 
2646
2960
  // cli/_dev.ts
2647
- init_bundler();
2648
- init_discover();
2961
+ init_build();
2649
2962
  init_ink();
2650
- import React2 from "react";
2963
+ import React3 from "react";
2651
2964
 
2652
2965
  // cli/_server_common.ts
2653
2966
  init_discover();
2654
2967
  import fs5 from "node:fs/promises";
2655
- import path6 from "node:path";
2656
- import { createServer as createViteServer } from "vite";
2968
+ import path7 from "node:path";
2969
+ import { createServer as createViteServer2 } from "vite";
2657
2970
  async function loadAgentDef(cwd) {
2658
- const agentPath = path6.resolve(cwd, "agent.ts");
2659
- const vite = await createViteServer({
2971
+ const agentPath = path7.resolve(cwd, "agent.ts");
2972
+ const vite = await createViteServer2({
2660
2973
  root: cwd,
2661
2974
  logLevel: "silent",
2662
2975
  server: { middlewareMode: true }
@@ -2685,7 +2998,7 @@ async function bootServer(agentDef, clientDir, env, port) {
2685
2998
  const wsMod = await import("ws");
2686
2999
  const WS = wsMod.default ?? wsMod;
2687
3000
  const createWebSocket = (url, opts) => new WS(url, { headers: opts.headers });
2688
- const clientHtml = await fs5.readFile(path6.join(clientDir, "index.html"), "utf-8");
3001
+ const clientHtml = await fs5.readFile(path7.join(clientDir, "index.html"), "utf-8");
2689
3002
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2690
3003
  const server = createServer2({
2691
3004
  agent: agentDef,
@@ -2699,25 +3012,11 @@ async function bootServer(agentDef, clientDir, env, port) {
2699
3012
 
2700
3013
  // cli/_dev.ts
2701
3014
  async function _startDevServer(cwd, port, log) {
2702
- const agent = await loadAgent(cwd);
2703
- if (!agent) {
2704
- throw new Error("No agent found \u2014 run `aai init` first");
2705
- }
2706
- log(React2.createElement(Step, { action: "Bundle", msg: agent.slug }));
2707
- let clientDir;
2708
- try {
2709
- const bundle = await bundleAgent(agent);
2710
- clientDir = bundle.clientDir;
2711
- } catch (err) {
2712
- if (err instanceof BundleError) {
2713
- throw new Error(`Bundle failed: ${err.message}`);
2714
- }
2715
- throw err;
2716
- }
3015
+ const bundle = await buildAgentBundle(cwd, log);
2717
3016
  const agentDef = await loadAgentDef(cwd);
2718
3017
  const env = await resolveServerEnv();
2719
- await bootServer(agentDef, clientDir, env, port);
2720
- log(React2.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
3018
+ await bootServer(agentDef, bundle.clientDir, env, port);
3019
+ log(React3.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2721
3020
  }
2722
3021
 
2723
3022
  // cli/dev.tsx
@@ -2737,7 +3036,7 @@ var devCommandDef = {
2737
3036
  ]
2738
3037
  };
2739
3038
  async function runDevCommand(args, version) {
2740
- const parsed = minimist3(args, {
3039
+ const parsed = minimist4(args, {
2741
3040
  string: ["port"],
2742
3041
  boolean: ["help", "yes"],
2743
3042
  alias: { p: "port", h: "help", y: "yes" }
@@ -2748,7 +3047,7 @@ async function runDevCommand(args, version) {
2748
3047
  }
2749
3048
  const cwd = process.env.INIT_CWD || process.cwd();
2750
3049
  const port = Number.parseInt(parsed.port ?? "3000", 10);
2751
- if (!await fileExists(path7.join(cwd, "agent.ts"))) {
3050
+ if (!await fileExists(path8.join(cwd, "agent.ts"))) {
2752
3051
  await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
2753
3052
  }
2754
3053
  await getApiKey();
@@ -2765,10 +3064,10 @@ init_discover();
2765
3064
  init_help();
2766
3065
  init_ink();
2767
3066
  import { render as render3, Text as Text3, useApp as useApp2 } from "ink";
2768
- import minimist4 from "minimist";
3067
+ import minimist5 from "minimist";
2769
3068
  import pLimit from "p-limit";
2770
3069
  import { useEffect, useState as useState2 } from "react";
2771
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
3070
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
2772
3071
  var ragCommandDef = {
2773
3072
  name: "rag",
2774
3073
  description: "Ingest a site's llms-full.txt into the vector store",
@@ -2811,8 +3110,8 @@ function RagUI({ url, apiKey, serverUrl, slug, chunkSize, onError }) {
2811
3110
  })();
2812
3111
  }, [apiKey, chunkSize, exit, log, onError, serverUrl, slug, url]);
2813
3112
  return /* @__PURE__ */ jsxs3(Fragment2, { children: [
2814
- /* @__PURE__ */ jsx5(StepLog, { items }),
2815
- err && /* @__PURE__ */ jsx5(ErrorLine, { msg: err }),
3113
+ /* @__PURE__ */ jsx6(StepLog, { items }),
3114
+ err && /* @__PURE__ */ jsx6(ErrorLine, { msg: err }),
2816
3115
  progress && /* @__PURE__ */ jsxs3(Text3, { children: [
2817
3116
  " ".repeat(PAD + 1),
2818
3117
  "Upsert ",
@@ -2827,7 +3126,7 @@ function RagUI({ url, apiKey, serverUrl, slug, chunkSize, onError }) {
2827
3126
  }
2828
3127
  async function runRag(opts) {
2829
3128
  const { url, apiKey, serverUrl, slug, chunkSize, log, setProgress } = opts;
2830
- log(/* @__PURE__ */ jsx5(Step, { action: "Fetch", msg: url }));
3129
+ log(/* @__PURE__ */ jsx6(Step, { action: "Fetch", msg: url }));
2831
3130
  const resp = await fetch(url, {
2832
3131
  headers: { "User-Agent": "aai-cli/1.0" },
2833
3132
  redirect: "follow",
@@ -2838,27 +3137,27 @@ async function runRag(opts) {
2838
3137
  }
2839
3138
  const content = await resp.text();
2840
3139
  if (content.length === 0) {
2841
- log(/* @__PURE__ */ jsx5(Warn, { msg: "File is empty" }));
3140
+ log(/* @__PURE__ */ jsx6(Warn, { msg: "File is empty" }));
2842
3141
  return;
2843
3142
  }
2844
- log(/* @__PURE__ */ jsx5(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
3143
+ log(/* @__PURE__ */ jsx6(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
2845
3144
  const origin = new URL(url).origin;
2846
3145
  const pages = splitPages(content);
2847
- log(/* @__PURE__ */ jsx5(Step, { action: "Parse", msg: `${pages.length} pages` }));
3146
+ log(/* @__PURE__ */ jsx6(Step, { action: "Parse", msg: `${pages.length} pages` }));
2848
3147
  const { RecursiveChunker } = await import("@chonkiejs/core");
2849
3148
  const chunker = await RecursiveChunker.create({ chunkSize });
2850
3149
  const siteSlug = slugify(origin);
2851
3150
  const allChunks = await chunkPages(pages, chunker, origin, siteSlug);
2852
- log(/* @__PURE__ */ jsx5(Step, { action: "Chunk", msg: `${allChunks.length} chunks` }));
3151
+ log(/* @__PURE__ */ jsx6(Step, { action: "Chunk", msg: `${allChunks.length} chunks` }));
2853
3152
  const vectorUrl = `${serverUrl}/${slug}/vector`;
2854
- log(/* @__PURE__ */ jsx5(Info, { msg: `target: ${vectorUrl}` }));
3153
+ log(/* @__PURE__ */ jsx6(Info, { msg: `target: ${vectorUrl}` }));
2855
3154
  const result = await upsertChunks(allChunks, vectorUrl, apiKey, setProgress);
2856
- log(/* @__PURE__ */ jsx5(Step, { action: "Done", msg: `${result.upserted} chunks upserted` }));
3155
+ log(/* @__PURE__ */ jsx6(Step, { action: "Done", msg: `${result.upserted} chunks upserted` }));
2857
3156
  if (result.errors > 0) {
2858
- log(/* @__PURE__ */ jsx5(Warn, { msg: `${result.errors} failed` }));
2859
- if (result.lastError) log(/* @__PURE__ */ jsx5(Info, { msg: `last error: ${result.lastError}` }));
3157
+ log(/* @__PURE__ */ jsx6(Warn, { msg: `${result.errors} failed` }));
3158
+ if (result.lastError) log(/* @__PURE__ */ jsx6(Info, { msg: `last error: ${result.lastError}` }));
2860
3159
  }
2861
- log(/* @__PURE__ */ jsx5(Detail, { msg: `Agent: ${slug}` }));
3160
+ log(/* @__PURE__ */ jsx6(Detail, { msg: `Agent: ${slug}` }));
2862
3161
  }
2863
3162
  async function chunkPages(pages, chunker, origin, siteSlug) {
2864
3163
  const allChunks = [];
@@ -2927,7 +3226,7 @@ async function upsertChunks(chunks, vectorUrl, apiKey, setProgress) {
2927
3226
  return { upserted, errors, lastError };
2928
3227
  }
2929
3228
  async function runRagCommand(args, version) {
2930
- const parsed = minimist4(args, {
3229
+ const parsed = minimist5(args, {
2931
3230
  string: ["server", "chunk-size"],
2932
3231
  boolean: ["help", "yes"],
2933
3232
  alias: { s: "server", h: "help", y: "yes" },
@@ -2959,7 +3258,7 @@ async function runRagCommand(args, version) {
2959
3258
  const chunkSize = Number.parseInt(parsed["chunk-size"] ?? "512", 10);
2960
3259
  let thrownError;
2961
3260
  const app = render3(
2962
- /* @__PURE__ */ jsx5(
3261
+ /* @__PURE__ */ jsx6(
2963
3262
  RagUI,
2964
3263
  {
2965
3264
  url,
@@ -3027,8 +3326,8 @@ init_discover();
3027
3326
  init_help();
3028
3327
  init_ink();
3029
3328
  init_prompts();
3030
- import minimist5 from "minimist";
3031
- import { jsx as jsx6 } from "react/jsx-runtime";
3329
+ import minimist6 from "minimist";
3330
+ import { jsx as jsx7 } from "react/jsx-runtime";
3032
3331
  var secretCommandDef = {
3033
3332
  name: "secret",
3034
3333
  description: "Manage secrets",
@@ -3046,7 +3345,7 @@ async function requireProjectConfig(cwd) {
3046
3345
  return config;
3047
3346
  }
3048
3347
  async function runSecretCommand(args, version) {
3049
- const parsed = minimist5(args, {
3348
+ const parsed = minimist6(args, {
3050
3349
  boolean: ["help", "yes"],
3051
3350
  alias: { h: "help", y: "yes" },
3052
3351
  stopEarly: true
@@ -3101,7 +3400,7 @@ async function secretPut(cwd, name, value) {
3101
3400
  const text = await resp.text();
3102
3401
  throw new Error(`Failed to set secret: ${text}`);
3103
3402
  }
3104
- log(/* @__PURE__ */ jsx6(Step, { action: "Set", msg: `${name} for ${slug}` }));
3403
+ log(/* @__PURE__ */ jsx7(Step, { action: "Set", msg: `${name} for ${slug}` }));
3105
3404
  });
3106
3405
  }
3107
3406
  async function secretDelete(cwd, name) {
@@ -3116,7 +3415,7 @@ async function secretDelete(cwd, name) {
3116
3415
  const text = await resp.text();
3117
3416
  throw new Error(`Failed to delete secret: ${text}`);
3118
3417
  }
3119
- log(/* @__PURE__ */ jsx6(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
3418
+ log(/* @__PURE__ */ jsx7(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
3120
3419
  });
3121
3420
  }
3122
3421
  async function secretList(cwd) {
@@ -3131,10 +3430,10 @@ async function secretList(cwd) {
3131
3430
  }
3132
3431
  const { vars } = await resp.json();
3133
3432
  if (vars.length === 0) {
3134
- log(/* @__PURE__ */ jsx6(StepInfo, { action: "Secrets", msg: "none set" }));
3433
+ log(/* @__PURE__ */ jsx7(StepInfo, { action: "Secrets", msg: "none set" }));
3135
3434
  } else {
3136
3435
  for (const name of vars) {
3137
- log(/* @__PURE__ */ jsx6(Detail, { msg: name }));
3436
+ log(/* @__PURE__ */ jsx7(Detail, { msg: name }));
3138
3437
  }
3139
3438
  }
3140
3439
  });
@@ -3144,24 +3443,24 @@ async function secretList(cwd) {
3144
3443
  init_discover();
3145
3444
  init_help();
3146
3445
  init_ink();
3147
- import path9 from "node:path";
3148
- import minimist6 from "minimist";
3446
+ import path10 from "node:path";
3447
+ import minimist7 from "minimist";
3149
3448
 
3150
3449
  // cli/_start.ts
3151
3450
  init_ink();
3152
- import path8 from "node:path";
3153
- import React3 from "react";
3451
+ import path9 from "node:path";
3452
+ import React4 from "react";
3154
3453
  async function _startProductionServer(cwd, port, log) {
3155
- const clientDir = path8.join(cwd, ".aai", "client");
3156
- log(React3.createElement(Step, { action: "Start", msg: "loading agent" }));
3454
+ const clientDir = path9.join(cwd, ".aai", "client");
3455
+ log(React4.createElement(Step, { action: "Start", msg: "loading agent" }));
3157
3456
  const agentDef = await loadAgentDef(cwd);
3158
3457
  const env = await resolveServerEnv();
3159
3458
  await bootServer(agentDef, clientDir, env, port);
3160
- log(React3.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
3459
+ log(React4.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
3161
3460
  }
3162
3461
 
3163
3462
  // cli/start.tsx
3164
- import { jsx as jsx7 } from "react/jsx-runtime";
3463
+ import { jsx as jsx8 } from "react/jsx-runtime";
3165
3464
  var startCommandDef = {
3166
3465
  name: "start",
3167
3466
  description: "Start the production server from a build",
@@ -3174,7 +3473,7 @@ var startCommandDef = {
3174
3473
  ]
3175
3474
  };
3176
3475
  async function runStartCommand(args, version) {
3177
- const parsed = minimist6(args, {
3476
+ const parsed = minimist7(args, {
3178
3477
  string: ["port"],
3179
3478
  boolean: ["help", "yes"],
3180
3479
  alias: { p: "port", h: "help", y: "yes" }
@@ -3185,24 +3484,24 @@ async function runStartCommand(args, version) {
3185
3484
  }
3186
3485
  const cwd = process.env.INIT_CWD || process.cwd();
3187
3486
  const port = Number.parseInt(parsed.port ?? "3000", 10);
3188
- const buildDir = path9.join(cwd, ".aai", "build");
3189
- if (!await fileExists(path9.join(buildDir, "worker.js"))) {
3487
+ const buildDir = path10.join(cwd, ".aai", "build");
3488
+ if (!await fileExists(path10.join(buildDir, "worker.js"))) {
3190
3489
  throw new Error("No build found \u2014 run `aai build` first");
3191
3490
  }
3192
3491
  await getApiKey();
3193
3492
  await runWithInk(async (log) => {
3194
- log(/* @__PURE__ */ jsx7(Step, { action: "Start", msg: `production server on port ${port}` }));
3493
+ log(/* @__PURE__ */ jsx8(Step, { action: "Start", msg: `production server on port ${port}` }));
3195
3494
  await _startProductionServer(cwd, port, log);
3196
3495
  });
3197
3496
  }
3198
3497
 
3199
3498
  // cli/cli.ts
3200
- var cliDir = path10.dirname(fileURLToPath2(import.meta.url));
3201
- var pkgJsonPath = path10.join(cliDir, "..", "package.json");
3499
+ var cliDir = path11.dirname(fileURLToPath2(import.meta.url));
3500
+ var pkgJsonPath = path11.join(cliDir, "..", "package.json");
3202
3501
  var pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
3203
3502
  var VERSION = pkgJson.version;
3204
3503
  async function main(args) {
3205
- const parsed = minimist7(args, {
3504
+ const parsed = minimist8(args, {
3206
3505
  boolean: ["help", "version"],
3207
3506
  alias: { h: "help", V: "version" },
3208
3507
  stopEarly: true
@@ -3221,6 +3520,9 @@ async function main(args) {
3221
3520
  case "init":
3222
3521
  await runInitCommand(subArgs, VERSION);
3223
3522
  return;
3523
+ case "build":
3524
+ await runBuildCommand(subArgs, VERSION);
3525
+ return;
3224
3526
  case "deploy":
3225
3527
  await runDeployCommand(subArgs, VERSION);
3226
3528
  return;