@alexkroman1/aai 0.7.12 → 0.8.1

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 +462 -352
  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 +10 -0
  7. package/dist/sdk/_render_check.d.ts.map +1 -0
  8. package/dist/sdk/_render_check.js +42 -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,52 @@ var init_ink = __esm({
631
557
  }
632
558
  });
633
559
 
560
+ // cli/_build.ts
561
+ import React2 from "react";
562
+ async function buildAgentBundle(cwd, log) {
563
+ const agent = await loadAgent(cwd);
564
+ if (!agent) {
565
+ throw new Error("No agent found \u2014 run `aai init` first");
566
+ }
567
+ log(React2.createElement(Step, { action: "Bundle", msg: agent.slug }));
568
+ let bundle;
569
+ try {
570
+ bundle = await bundleAgent(agent);
571
+ } catch (err) {
572
+ if (err instanceof BundleError) {
573
+ throw new Error(`Bundle failed: ${err.message}`);
574
+ }
575
+ throw err;
576
+ }
577
+ const kb = (bundle.workerBytes / 1024).toFixed(1);
578
+ const clientCount = Object.keys(bundle.clientFiles).length;
579
+ log(React2.createElement(Info, { msg: `worker: ${kb} KB, client: ${clientCount} file(s)` }));
580
+ if (agent.clientEntry) {
581
+ try {
582
+ const renderCheckPath = "../sdk/_render_check.ts";
583
+ const { renderCheck } = await import(
584
+ /* @vite-ignore */
585
+ renderCheckPath
586
+ );
587
+ log(React2.createElement(Step, { action: "Render", msg: "check" }));
588
+ await renderCheck(agent.clientEntry, cwd);
589
+ } catch (err) {
590
+ const msg = err instanceof Error ? err.message : String(err);
591
+ if (msg.includes("linkedom") || msg.includes("_render_check")) return bundle;
592
+ throw new Error(`Render check failed: ${msg}`);
593
+ }
594
+ }
595
+ return bundle;
596
+ }
597
+ var init_build = __esm({
598
+ "cli/_build.ts"() {
599
+ "use strict";
600
+ init_bundler();
601
+ init_discover();
602
+ init_ink();
603
+ }
604
+ });
605
+
634
606
  // cli/_init.ts
635
607
  var init_exports = {};
636
608
  __export(init_exports, {
@@ -719,113 +691,58 @@ var init_init = __esm({
719
691
  }
720
692
  });
721
693
 
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
- }
694
+ // cli/_deploy.ts
695
+ async function attemptDeploy(url, slug, apiKey, env, worker, clientFiles) {
758
696
  try {
759
- await execFileAsync("npm", ["install"], { cwd });
697
+ return await _internals.fetch(`${url}/${slug}/deploy`, {
698
+ method: "POST",
699
+ headers: {
700
+ "Content-Type": "application/json",
701
+ Authorization: `Bearer ${apiKey}`
702
+ },
703
+ body: JSON.stringify({
704
+ env,
705
+ worker,
706
+ clientFiles
707
+ })
708
+ });
760
709
  } catch {
761
- log(/* @__PURE__ */ jsx3(Warn, { msg: "npm install failed" }));
710
+ throw new Error(`deployment failed: could not reach ${url}`);
762
711
  }
763
712
  }
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);
713
+ async function runDeploy(opts) {
714
+ const worker = opts.bundle.worker;
715
+ const clientFiles = opts.bundle.clientFiles;
716
+ let slug = opts.slug;
717
+ if (opts.dryRun) {
718
+ return { slug };
786
719
  }
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);
720
+ for (let i = 0; i < MAX_RETRIES; i++) {
721
+ const resp = await attemptDeploy(opts.url, slug, opts.apiKey, opts.env, worker, clientFiles);
722
+ if (resp.ok) {
723
+ return { slug };
796
724
  }
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);
725
+ if (resp.status === 403) {
726
+ const text2 = await resp.text();
727
+ if (text2.includes("Slug")) {
728
+ slug = generateSlug();
729
+ continue;
730
+ }
731
+ }
732
+ const text = await resp.text();
733
+ throw new Error(`deploy failed (${resp.status}): ${text}`);
804
734
  }
805
- return cwd;
735
+ throw new Error(`deploy failed: could not find available slug after ${MAX_RETRIES} attempts`);
806
736
  }
807
- var execFileAsync, initCommandDef;
808
- var init_init2 = __esm({
809
- "cli/init.tsx"() {
737
+ var _internals, MAX_RETRIES;
738
+ var init_deploy = __esm({
739
+ "cli/_deploy.ts"() {
810
740
  "use strict";
811
- init_colors();
812
741
  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
- ]
742
+ _internals = {
743
+ fetch: globalThis.fetch.bind(globalThis)
828
744
  };
745
+ MAX_RETRIES = 20;
829
746
  }
830
747
  });
831
748
 
@@ -834,33 +751,16 @@ var deploy_exports = {};
834
751
  __export(deploy_exports, {
835
752
  runDeployCommand: () => runDeployCommand
836
753
  });
837
- import path5 from "node:path";
838
- import minimist2 from "minimist";
839
- import { jsx as jsx4 } from "react/jsx-runtime";
754
+ import path4 from "node:path";
755
+ import minimist from "minimist";
756
+ import { jsx as jsx3 } from "react/jsx-runtime";
840
757
  function resolveServerUrl(parsed) {
841
758
  return parsed.server || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
842
759
  }
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
760
  async function deployBundle(opts) {
861
761
  const { bundle, serverUrl, apiKey, cwd, log } = opts;
862
762
  let { slug } = opts;
863
- log(/* @__PURE__ */ jsx4(Step, { action: "Deploy", msg: slug }));
763
+ log(/* @__PURE__ */ jsx3(Step, { action: "Deploy", msg: slug }));
864
764
  const deployed = await runDeploy({
865
765
  url: serverUrl,
866
766
  bundle,
@@ -872,11 +772,11 @@ async function deployBundle(opts) {
872
772
  slug = deployed.slug;
873
773
  await writeProjectConfig(cwd, { slug, serverUrl });
874
774
  const agentUrl = `${serverUrl}/${slug}`;
875
- log(/* @__PURE__ */ jsx4(Step, { action: "Ready", msg: agentUrl }));
775
+ log(/* @__PURE__ */ jsx3(Step, { action: "Ready", msg: agentUrl }));
876
776
  return agentUrl;
877
777
  }
878
778
  async function runDeployCommand(args, version) {
879
- const parsed = minimist2(args, {
779
+ const parsed = minimist(args, {
880
780
  string: ["server"],
881
781
  boolean: ["dry-run", "help", "yes"],
882
782
  alias: { s: "server", h: "help", y: "yes" }
@@ -886,7 +786,7 @@ async function runDeployCommand(args, version) {
886
786
  return;
887
787
  }
888
788
  const cwd = process.env.INIT_CWD || process.cwd();
889
- if (!await fileExists(path5.join(cwd, "agent.ts"))) {
789
+ if (!await fileExists(path4.join(cwd, "agent.ts"))) {
890
790
  await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
891
791
  }
892
792
  const serverUrl = resolveServerUrl(parsed);
@@ -896,9 +796,9 @@ async function runDeployCommand(args, version) {
896
796
  const slug = projectConfig?.slug ?? generateSlug();
897
797
  let agentUrl = "";
898
798
  await runWithInk(async (log) => {
899
- const bundle = await buildAgent(cwd, log);
799
+ const bundle = await buildAgentBundle(cwd, log);
900
800
  if (dryRun) {
901
- log(/* @__PURE__ */ jsx4(StepInfo, { action: "Dry run", msg: `would deploy as ${slug}` }));
801
+ log(/* @__PURE__ */ jsx3(StepInfo, { action: "Dry run", msg: `would deploy as ${slug}` }));
902
802
  return;
903
803
  }
904
804
  agentUrl = await deployBundle({ bundle, serverUrl, apiKey, slug, cwd, log });
@@ -913,7 +813,7 @@ var deployCommandDef;
913
813
  var init_deploy2 = __esm({
914
814
  "cli/deploy.tsx"() {
915
815
  "use strict";
916
- init_bundler();
816
+ init_build();
917
817
  init_deploy();
918
818
  init_discover();
919
819
  init_help();
@@ -935,6 +835,116 @@ var init_deploy2 = __esm({
935
835
  }
936
836
  });
937
837
 
838
+ // cli/init.tsx
839
+ import { execFile } from "node:child_process";
840
+ import fs4 from "node:fs/promises";
841
+ import path5 from "node:path";
842
+ import { fileURLToPath } from "node:url";
843
+ import { promisify } from "node:util";
844
+ import minimist2 from "minimist";
845
+ import { jsx as jsx4 } from "react/jsx-runtime";
846
+ async function rewriteDevDeps(cwd, cliDir2) {
847
+ const monorepoRoot = path5.join(cliDir2, "..");
848
+ const pkgJsonPath2 = path5.join(cwd, "package.json");
849
+ const pkgJson2 = JSON.parse(await fs4.readFile(pkgJsonPath2, "utf-8"));
850
+ const rootPkg = JSON.parse(await fs4.readFile(path5.join(monorepoRoot, "package.json"), "utf-8"));
851
+ const rootPkgName = rootPkg.name;
852
+ if (pkgJson2.dependencies[rootPkgName]) {
853
+ pkgJson2.dependencies[rootPkgName] = `file:${monorepoRoot}`;
854
+ }
855
+ await fs4.writeFile(pkgJsonPath2, `${JSON.stringify(pkgJson2, null, 2)}
856
+ `);
857
+ }
858
+ async function installDeps(cwd, log) {
859
+ if (await fileExists(path5.join(cwd, "node_modules"))) return;
860
+ let pkgJson2;
861
+ try {
862
+ pkgJson2 = JSON.parse(await fs4.readFile(path5.join(cwd, "package.json"), "utf-8"));
863
+ } catch {
864
+ pkgJson2 = {};
865
+ }
866
+ const deps = Object.keys(pkgJson2.dependencies ?? {});
867
+ const devDeps = Object.keys(pkgJson2.devDependencies ?? {});
868
+ if (deps.length > 0) {
869
+ log(/* @__PURE__ */ jsx4(Step, { action: "Install", msg: deps.join(", ") }));
870
+ }
871
+ if (devDeps.length > 0) {
872
+ log(/* @__PURE__ */ jsx4(Step, { action: "Install", msg: `dev: ${devDeps.join(", ")}` }));
873
+ }
874
+ try {
875
+ await execFileAsync("npm", ["install"], { cwd });
876
+ } catch {
877
+ log(/* @__PURE__ */ jsx4(Warn, { msg: "npm install failed" }));
878
+ }
879
+ }
880
+ async function runInitCommand(args, version, opts) {
881
+ const parsed = minimist2(args, {
882
+ string: ["template"],
883
+ boolean: ["force", "help"],
884
+ alias: { t: "template", f: "force", h: "help" }
885
+ });
886
+ if (parsed.help) {
887
+ console.log(subcommandHelp(initCommandDef, version));
888
+ return "";
889
+ }
890
+ const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
891
+ await getApiKey2();
892
+ let dir = parsed._[0];
893
+ if (!dir) {
894
+ dir = await askText("What is your project named?", "my-voice-agent");
895
+ }
896
+ const cwd = path5.resolve(process.env.INIT_CWD || process.cwd(), dir);
897
+ if (!parsed.force && await fileExists(path5.join(cwd, "agent.ts"))) {
898
+ console.log(
899
+ `agent.ts already exists in this directory. Use ${interactive("--force")} to overwrite.`
900
+ );
901
+ process.exit(1);
902
+ }
903
+ const cliDir2 = path5.dirname(fileURLToPath(import.meta.url));
904
+ const templatesDir = path5.join(cliDir2, "..", "templates");
905
+ const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
906
+ const template = parsed.template || "simple";
907
+ await runWithInk(async (log) => {
908
+ log(/* @__PURE__ */ jsx4(Step, { action: "Create", msg: dir }));
909
+ await runInit2({ targetDir: cwd, template, templatesDir });
910
+ if (isDevMode()) {
911
+ await rewriteDevDeps(cwd, cliDir2);
912
+ }
913
+ await installDeps(cwd, log);
914
+ });
915
+ process.chdir(cwd);
916
+ delete process.env.INIT_CWD;
917
+ if (!opts?.quiet) {
918
+ const { runDeployCommand: runDeployCommand2 } = await Promise.resolve().then(() => (init_deploy2(), deploy_exports));
919
+ await runDeployCommand2(["-y"], version);
920
+ }
921
+ return cwd;
922
+ }
923
+ var execFileAsync, initCommandDef;
924
+ var init_init2 = __esm({
925
+ "cli/init.tsx"() {
926
+ "use strict";
927
+ init_colors();
928
+ init_discover();
929
+ init_help();
930
+ init_ink();
931
+ init_prompts();
932
+ execFileAsync = promisify(execFile);
933
+ initCommandDef = {
934
+ name: "init",
935
+ description: "Scaffold a new agent project",
936
+ args: [{ name: "dir", optional: true }],
937
+ options: [
938
+ {
939
+ flags: "-t, --template <template>",
940
+ description: "Template to use"
941
+ },
942
+ { flags: "-f, --force", description: "Overwrite existing agent.ts" }
943
+ ]
944
+ };
945
+ }
946
+ });
947
+
938
948
  // sdk/protocol.ts
939
949
  import { z } from "zod";
940
950
  var DEFAULT_TTS_SAMPLE_RATE, AUDIO_FORMAT, _bitsPerSample, _channels, AudioFrameSpec, KvRequestBaseSchema, HOOK_TIMEOUT_MS, SessionErrorCodeSchema, TranscriptEventSchema, ClientEventSchema, ClientMessageSchema;
@@ -1094,9 +1104,86 @@ var init_internal_types = __esm({
1094
1104
  }
1095
1105
  });
1096
1106
 
1107
+ // sdk/types.ts
1108
+ function tool(def) {
1109
+ return def;
1110
+ }
1111
+ var DEFAULT_INSTRUCTIONS;
1112
+ var init_types = __esm({
1113
+ "sdk/types.ts"() {
1114
+ "use strict";
1115
+ DEFAULT_INSTRUCTIONS = `You are AAI, a helpful AI assistant.
1116
+
1117
+ Voice-First Rules:
1118
+ - Optimize for natural speech. Avoid jargon unless central to the answer. Use short, punchy sentences.
1119
+ - Never mention "search results," "sources," or "the provided text." Speak as if the knowledge is your own.
1120
+ - No visual formatting. Do not say "bullet point," "bold," or "bracketed one." If you need to list items, say "First," "Next," and "Finally."
1121
+ - Start with the most important information. No introductory filler.
1122
+ - Be concise. Keep answers to 1-3 sentences. For complex topics, provide a high-level summary.
1123
+ - Be confident. Avoid hedging phrases like "It seems that" or "I believe."
1124
+ - If you don't have enough information, say so directly rather than guessing.
1125
+ - Never use exclamation points. Keep your tone calm and conversational.`;
1126
+ }
1127
+ });
1128
+
1129
+ // sdk/memory_tools.ts
1130
+ import { z as z3 } from "zod";
1131
+ function memoryTools() {
1132
+ return {
1133
+ save_memory: tool({
1134
+ description: "Save a piece of information to persistent memory. Use a descriptive key like 'user:name' or 'project:status'.",
1135
+ parameters: z3.object({
1136
+ key: z3.string().describe("A descriptive key for this memory (e.g. 'user:name', 'preference:color')"),
1137
+ value: z3.string().describe("The information to remember")
1138
+ }),
1139
+ execute: async ({ key, value }, ctx) => {
1140
+ await ctx.kv.set(key, value);
1141
+ return { saved: key };
1142
+ }
1143
+ }),
1144
+ recall_memory: tool({
1145
+ description: "Retrieve a previously saved memory by its key.",
1146
+ parameters: z3.object({
1147
+ key: z3.string().describe("The key to look up")
1148
+ }),
1149
+ execute: async ({ key }, ctx) => {
1150
+ const value = await ctx.kv.get(key);
1151
+ if (value === null) return { found: false, key };
1152
+ return { found: true, key, value };
1153
+ }
1154
+ }),
1155
+ list_memories: tool({
1156
+ description: "List all saved memory keys, optionally filtered by a prefix (e.g. 'user:').",
1157
+ parameters: z3.object({
1158
+ prefix: z3.string().describe("Prefix to filter keys (e.g. 'user:'). Use empty string for all.").optional()
1159
+ }),
1160
+ execute: async ({ prefix }, ctx) => {
1161
+ const entries = await ctx.kv.list(prefix ?? "");
1162
+ return { count: entries.length, keys: entries.map((e) => e.key) };
1163
+ }
1164
+ }),
1165
+ forget_memory: tool({
1166
+ description: "Delete a previously saved memory by its key.",
1167
+ parameters: z3.object({
1168
+ key: z3.string().describe("The key to delete")
1169
+ }),
1170
+ execute: async ({ key }, ctx) => {
1171
+ await ctx.kv.delete(key);
1172
+ return { deleted: key };
1173
+ }
1174
+ })
1175
+ };
1176
+ }
1177
+ var init_memory_tools = __esm({
1178
+ "sdk/memory_tools.ts"() {
1179
+ "use strict";
1180
+ init_types();
1181
+ }
1182
+ });
1183
+
1097
1184
  // sdk/builtin_tools.ts
1098
1185
  import { convert } from "html-to-text";
1099
- import { z as z3 } from "zod";
1186
+ import { z as z4 } from "zod";
1100
1187
  function htmlToText(html) {
1101
1188
  return convert(html, { wordwrap: false });
1102
1189
  }
@@ -1249,12 +1336,27 @@ function getBuiltinToolDefs(names, opts) {
1249
1336
  defs[name] = createVectorSearch(opts.vectorSearch);
1250
1337
  }
1251
1338
  break;
1339
+ case "memory": {
1340
+ const mt = memoryTools();
1341
+ for (const [toolName, toolDef] of Object.entries(mt)) {
1342
+ defs[toolName] = toolDef;
1343
+ }
1344
+ break;
1345
+ }
1252
1346
  }
1253
1347
  }
1254
1348
  return defs;
1255
1349
  }
1256
1350
  function getBuiltinToolSchemas(names) {
1257
1351
  return names.flatMap((name) => {
1352
+ const multiCreator = MULTI_TOOL_BUILTINS[name];
1353
+ if (multiCreator) {
1354
+ return Object.entries(multiCreator()).map(([toolName, def2]) => ({
1355
+ name: toolName,
1356
+ description: def2.description,
1357
+ parameters: z4.toJSONSchema(def2.parameters ?? EMPTY_PARAMS)
1358
+ }));
1359
+ }
1258
1360
  const creator = TOOL_CREATORS[name];
1259
1361
  if (!creator) return [];
1260
1362
  const def = creator();
@@ -1262,49 +1364,50 @@ function getBuiltinToolSchemas(names) {
1262
1364
  {
1263
1365
  name,
1264
1366
  description: def.description,
1265
- parameters: z3.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1367
+ parameters: z4.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1266
1368
  }
1267
1369
  ];
1268
1370
  });
1269
1371
  }
1270
- var webSearchParams, BRAVE_SEARCH_URL, BraveSearchResponseSchema, MAX_PAGE_CHARS, MAX_HTML_BYTES, visitWebpageParams, fetchJsonParams, runCodeParams, vectorSearchParams, TOOL_CREATORS;
1372
+ var webSearchParams, BRAVE_SEARCH_URL, BraveSearchResponseSchema, MAX_PAGE_CHARS, MAX_HTML_BYTES, visitWebpageParams, fetchJsonParams, runCodeParams, vectorSearchParams, TOOL_CREATORS, MULTI_TOOL_BUILTINS;
1271
1373
  var init_builtin_tools = __esm({
1272
1374
  "sdk/builtin_tools.ts"() {
1273
1375
  "use strict";
1274
1376
  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()
1377
+ init_memory_tools();
1378
+ webSearchParams = z4.object({
1379
+ query: z4.string().describe("The search query"),
1380
+ max_results: z4.number().describe("Maximum number of results to return (default 5)").optional()
1278
1381
  });
1279
1382
  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()
1383
+ BraveSearchResponseSchema = z4.object({
1384
+ web: z4.object({
1385
+ results: z4.array(
1386
+ z4.object({
1387
+ title: z4.string(),
1388
+ url: z4.string(),
1389
+ description: z4.string()
1287
1390
  })
1288
1391
  )
1289
1392
  }).optional()
1290
1393
  });
1291
1394
  MAX_PAGE_CHARS = 1e4;
1292
1395
  MAX_HTML_BYTES = 2e5;
1293
- visitWebpageParams = z3.object({
1294
- url: z3.string().describe("The full URL to fetch (e.g., 'https://example.com/page')")
1396
+ visitWebpageParams = z4.object({
1397
+ url: z4.string().describe("The full URL to fetch (e.g., 'https://example.com/page')")
1295
1398
  });
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()
1399
+ fetchJsonParams = z4.object({
1400
+ url: z4.string().describe("The URL to fetch JSON from"),
1401
+ headers: z4.record(z4.string(), z4.string()).describe("Optional HTTP headers to include in the request").optional()
1299
1402
  });
1300
- runCodeParams = z3.object({
1301
- code: z3.string().describe("JavaScript code to execute. Use console.log() for output.")
1403
+ runCodeParams = z4.object({
1404
+ code: z4.string().describe("JavaScript code to execute. Use console.log() for output.")
1302
1405
  });
1303
- vectorSearchParams = z3.object({
1304
- query: z3.string().describe(
1406
+ vectorSearchParams = z4.object({
1407
+ query: z4.string().describe(
1305
1408
  '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
1409
  ),
1307
- topK: z3.number().describe("Maximum results to return (default: 5)").optional()
1410
+ topK: z4.number().describe("Maximum results to return (default: 5)").optional()
1308
1411
  });
1309
1412
  TOOL_CREATORS = {
1310
1413
  web_search: createWebSearch,
@@ -1314,6 +1417,9 @@ var init_builtin_tools = __esm({
1314
1417
  // vector_search uses a stub for schema generation
1315
1418
  vector_search: () => createVectorSearch(async () => "")
1316
1419
  };
1420
+ MULTI_TOOL_BUILTINS = {
1421
+ memory: memoryTools
1422
+ };
1317
1423
  }
1318
1424
  });
1319
1425
 
@@ -1384,7 +1490,7 @@ var init_kv = __esm({
1384
1490
  });
1385
1491
 
1386
1492
  // sdk/s2s.ts
1387
- import { z as z4 } from "zod";
1493
+ import { z as z5 } from "zod";
1388
1494
  function uint8ToBase64(bytes) {
1389
1495
  return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
1390
1496
  }
@@ -1554,7 +1660,7 @@ function connectS2s(opts) {
1554
1660
  return;
1555
1661
  }
1556
1662
  const obj = raw;
1557
- if (obj.type !== "reply.audio" && obj.type !== "input.audio") {
1663
+ if (obj.type !== "reply.audio" && obj.type !== "input.audio" && obj.type !== "transcript.agent.delta") {
1558
1664
  log.info(`S2S << ${obj.type}`);
1559
1665
  }
1560
1666
  if (obj.type === "reply.audio" && typeof obj.data === "string") {
@@ -1600,66 +1706,47 @@ var init_s2s = __esm({
1600
1706
  "use strict";
1601
1707
  init_runtime();
1602
1708
  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()
1709
+ S2sServerMessageSchema = z5.discriminatedUnion("type", [
1710
+ z5.object({ type: z5.literal("session.ready"), session_id: z5.string() }),
1711
+ z5.object({ type: z5.literal("session.updated") }).passthrough(),
1712
+ z5.object({ type: z5.literal("input.speech.started") }),
1713
+ z5.object({ type: z5.literal("input.speech.stopped") }),
1714
+ z5.object({ type: z5.literal("transcript.user.delta"), text: z5.string() }),
1715
+ z5.object({
1716
+ type: z5.literal("transcript.user"),
1717
+ item_id: z5.string(),
1718
+ text: z5.string()
1613
1719
  }),
1614
- z4.object({ type: z4.literal("reply.started"), reply_id: z4.string() }),
1720
+ z5.object({ type: z5.literal("reply.started"), reply_id: z5.string() }),
1615
1721
  // 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({})
1722
+ z5.object({ type: z5.literal("transcript.agent.delta"), delta: z5.string() }).passthrough(),
1723
+ z5.object({ type: z5.literal("transcript.agent"), text: z5.string() }),
1724
+ z5.object({ type: z5.literal("reply.content_part.started") }).passthrough(),
1725
+ z5.object({ type: z5.literal("reply.content_part.done") }).passthrough(),
1726
+ z5.object({
1727
+ type: z5.literal("tool.call"),
1728
+ call_id: z5.string(),
1729
+ name: z5.string(),
1730
+ args: z5.record(z5.string(), z5.unknown()).optional().default({})
1625
1731
  }),
1626
- z4.object({
1627
- type: z4.literal("reply.done"),
1628
- status: z4.string().optional()
1732
+ z5.object({
1733
+ type: z5.literal("reply.done"),
1734
+ status: z5.string().optional()
1629
1735
  }),
1630
- z4.object({
1631
- type: z4.literal("session.error"),
1632
- code: z4.string(),
1633
- message: z4.string()
1736
+ z5.object({
1737
+ type: z5.literal("session.error"),
1738
+ code: z5.string(),
1739
+ message: z5.string()
1634
1740
  }),
1635
1741
  // Connection-level error (bare "error" without "session." prefix).
1636
- z4.object({
1637
- type: z4.literal("error"),
1638
- message: z4.string()
1742
+ z5.object({
1743
+ type: z5.literal("error"),
1744
+ message: z5.string()
1639
1745
  })
1640
1746
  ]);
1641
1747
  }
1642
1748
  });
1643
1749
 
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
1750
  // sdk/system_prompt.ts
1664
1751
  function buildSystemPrompt(config, opts) {
1665
1752
  const { hasTools } = opts;
@@ -1884,8 +1971,8 @@ function createS2sSession(opts) {
1884
1971
  pendingTools = [];
1885
1972
  client.event({ type: "cancelled" });
1886
1973
  } else if (pendingTools.length > 0) {
1887
- for (const tool of pendingTools) {
1888
- s2s?.sendToolResult(tool.call_id, tool.result);
1974
+ for (const tool2 of pendingTools) {
1975
+ s2s?.sendToolResult(tool2.call_id, tool2.result);
1889
1976
  }
1890
1977
  pendingTools = [];
1891
1978
  } else {
@@ -1903,6 +1990,7 @@ function createS2sSession(opts) {
1903
1990
  code: "internal",
1904
1991
  message: e.detail.message
1905
1992
  });
1993
+ handle.close();
1906
1994
  });
1907
1995
  handle.addEventListener("close", () => {
1908
1996
  log.info("S2S closed");
@@ -2066,8 +2154,8 @@ function buildToolContext(opts) {
2066
2154
  };
2067
2155
  }
2068
2156
  async function executeToolCall(name, args, options) {
2069
- const { tool } = options;
2070
- const schema = tool.parameters ?? EMPTY_PARAMS;
2157
+ const { tool: tool2 } = options;
2158
+ const schema = tool2.parameters ?? EMPTY_PARAMS;
2071
2159
  const parsed = schema.safeParse(args);
2072
2160
  if (!parsed.success) {
2073
2161
  const issues = (parsed.error?.issues ?? []).map((i) => `${i.path.map(String).join(".")}: ${i.message}`).join(", ");
@@ -2076,7 +2164,7 @@ async function executeToolCall(name, args, options) {
2076
2164
  try {
2077
2165
  const ctx = buildToolContext(options);
2078
2166
  await yieldTick();
2079
- const result = await Promise.resolve(tool.execute(parsed.data, ctx));
2167
+ const result = await Promise.resolve(tool2.execute(parsed.data, ctx));
2080
2168
  await yieldTick();
2081
2169
  if (result == null) return "null";
2082
2170
  return typeof result === "string" ? result : JSON.stringify(result);
@@ -2104,8 +2192,7 @@ function buildAgentConfig(agent) {
2104
2192
  const config = {
2105
2193
  name: agent.name,
2106
2194
  instructions: agent.instructions,
2107
- greeting: agent.greeting,
2108
- voice: agent.voice
2195
+ greeting: agent.greeting
2109
2196
  };
2110
2197
  if (agent.sttPrompt !== void 0) config.sttPrompt = agent.sttPrompt;
2111
2198
  if (typeof agent.maxSteps !== "function") config.maxSteps = agent.maxSteps;
@@ -2160,10 +2247,10 @@ function createDirectExecutor(opts) {
2160
2247
  };
2161
2248
  }
2162
2249
  const executeTool = async (name, args, sessionId, messages) => {
2163
- const tool = allTools[name];
2164
- if (!tool) return JSON.stringify({ error: `Unknown tool: ${name}` });
2250
+ const tool2 = allTools[name];
2251
+ if (!tool2) return JSON.stringify({ error: `Unknown tool: ${name}` });
2165
2252
  return executeToolCall(name, args, {
2166
- tool,
2253
+ tool: tool2,
2167
2254
  env: frozenEnv,
2168
2255
  sessionId,
2169
2256
  state: getState(sessionId ?? ""),
@@ -2633,29 +2720,63 @@ var init_server = __esm({
2633
2720
 
2634
2721
  // cli/cli.ts
2635
2722
  init_help();
2636
- init_deploy2();
2637
2723
  import { readFileSync } from "node:fs";
2638
- import path10 from "node:path";
2724
+ import path11 from "node:path";
2639
2725
  import { fileURLToPath as fileURLToPath2 } from "node:url";
2640
- import minimist7 from "minimist";
2726
+ import minimist8 from "minimist";
2641
2727
 
2642
- // cli/dev.tsx
2643
- import path7 from "node:path";
2728
+ // cli/build.tsx
2729
+ init_build();
2730
+ init_discover();
2731
+ init_help();
2732
+ init_ink();
2733
+ init_init2();
2734
+ import path6 from "node:path";
2644
2735
  import minimist3 from "minimist";
2736
+ import { jsx as jsx5 } from "react/jsx-runtime";
2737
+ var buildCommandDef = {
2738
+ name: "build",
2739
+ description: "Bundle agent and client (validates code without deploying or starting a server)",
2740
+ options: [{ flags: "-y, --yes", description: "Accept defaults (no prompts)" }]
2741
+ };
2742
+ async function runBuildCommand(args, version) {
2743
+ const parsed = minimist3(args, {
2744
+ boolean: ["help", "yes"],
2745
+ alias: { h: "help", y: "yes" }
2746
+ });
2747
+ if (parsed.help) {
2748
+ console.log(subcommandHelp(buildCommandDef, version));
2749
+ return;
2750
+ }
2751
+ const cwd = process.env.INIT_CWD || process.cwd();
2752
+ if (!await fileExists(path6.join(cwd, "agent.ts"))) {
2753
+ await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
2754
+ }
2755
+ await runWithInk(async (log) => {
2756
+ await buildAgentBundle(cwd, log);
2757
+ log(/* @__PURE__ */ jsx5(Step, { action: "Build", msg: "ok" }));
2758
+ });
2759
+ }
2760
+
2761
+ // cli/cli.ts
2762
+ init_deploy2();
2763
+
2764
+ // cli/dev.tsx
2765
+ import path8 from "node:path";
2766
+ import minimist4 from "minimist";
2645
2767
 
2646
2768
  // cli/_dev.ts
2647
- init_bundler();
2648
- init_discover();
2769
+ init_build();
2649
2770
  init_ink();
2650
- import React2 from "react";
2771
+ import React3 from "react";
2651
2772
 
2652
2773
  // cli/_server_common.ts
2653
2774
  init_discover();
2654
2775
  import fs5 from "node:fs/promises";
2655
- import path6 from "node:path";
2776
+ import path7 from "node:path";
2656
2777
  import { createServer as createViteServer } from "vite";
2657
2778
  async function loadAgentDef(cwd) {
2658
- const agentPath = path6.resolve(cwd, "agent.ts");
2779
+ const agentPath = path7.resolve(cwd, "agent.ts");
2659
2780
  const vite = await createViteServer({
2660
2781
  root: cwd,
2661
2782
  logLevel: "silent",
@@ -2685,7 +2806,7 @@ async function bootServer(agentDef, clientDir, env, port) {
2685
2806
  const wsMod = await import("ws");
2686
2807
  const WS = wsMod.default ?? wsMod;
2687
2808
  const createWebSocket = (url, opts) => new WS(url, { headers: opts.headers });
2688
- const clientHtml = await fs5.readFile(path6.join(clientDir, "index.html"), "utf-8");
2809
+ const clientHtml = await fs5.readFile(path7.join(clientDir, "index.html"), "utf-8");
2689
2810
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2690
2811
  const server = createServer2({
2691
2812
  agent: agentDef,
@@ -2699,25 +2820,11 @@ async function bootServer(agentDef, clientDir, env, port) {
2699
2820
 
2700
2821
  // cli/_dev.ts
2701
2822
  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
- }
2823
+ const bundle = await buildAgentBundle(cwd, log);
2717
2824
  const agentDef = await loadAgentDef(cwd);
2718
2825
  const env = await resolveServerEnv();
2719
- await bootServer(agentDef, clientDir, env, port);
2720
- log(React2.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2826
+ await bootServer(agentDef, bundle.clientDir, env, port);
2827
+ log(React3.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2721
2828
  }
2722
2829
 
2723
2830
  // cli/dev.tsx
@@ -2737,7 +2844,7 @@ var devCommandDef = {
2737
2844
  ]
2738
2845
  };
2739
2846
  async function runDevCommand(args, version) {
2740
- const parsed = minimist3(args, {
2847
+ const parsed = minimist4(args, {
2741
2848
  string: ["port"],
2742
2849
  boolean: ["help", "yes"],
2743
2850
  alias: { p: "port", h: "help", y: "yes" }
@@ -2748,7 +2855,7 @@ async function runDevCommand(args, version) {
2748
2855
  }
2749
2856
  const cwd = process.env.INIT_CWD || process.cwd();
2750
2857
  const port = Number.parseInt(parsed.port ?? "3000", 10);
2751
- if (!await fileExists(path7.join(cwd, "agent.ts"))) {
2858
+ if (!await fileExists(path8.join(cwd, "agent.ts"))) {
2752
2859
  await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
2753
2860
  }
2754
2861
  await getApiKey();
@@ -2765,10 +2872,10 @@ init_discover();
2765
2872
  init_help();
2766
2873
  init_ink();
2767
2874
  import { render as render3, Text as Text3, useApp as useApp2 } from "ink";
2768
- import minimist4 from "minimist";
2875
+ import minimist5 from "minimist";
2769
2876
  import pLimit from "p-limit";
2770
2877
  import { useEffect, useState as useState2 } from "react";
2771
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
2878
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
2772
2879
  var ragCommandDef = {
2773
2880
  name: "rag",
2774
2881
  description: "Ingest a site's llms-full.txt into the vector store",
@@ -2811,8 +2918,8 @@ function RagUI({ url, apiKey, serverUrl, slug, chunkSize, onError }) {
2811
2918
  })();
2812
2919
  }, [apiKey, chunkSize, exit, log, onError, serverUrl, slug, url]);
2813
2920
  return /* @__PURE__ */ jsxs3(Fragment2, { children: [
2814
- /* @__PURE__ */ jsx5(StepLog, { items }),
2815
- err && /* @__PURE__ */ jsx5(ErrorLine, { msg: err }),
2921
+ /* @__PURE__ */ jsx6(StepLog, { items }),
2922
+ err && /* @__PURE__ */ jsx6(ErrorLine, { msg: err }),
2816
2923
  progress && /* @__PURE__ */ jsxs3(Text3, { children: [
2817
2924
  " ".repeat(PAD + 1),
2818
2925
  "Upsert ",
@@ -2827,7 +2934,7 @@ function RagUI({ url, apiKey, serverUrl, slug, chunkSize, onError }) {
2827
2934
  }
2828
2935
  async function runRag(opts) {
2829
2936
  const { url, apiKey, serverUrl, slug, chunkSize, log, setProgress } = opts;
2830
- log(/* @__PURE__ */ jsx5(Step, { action: "Fetch", msg: url }));
2937
+ log(/* @__PURE__ */ jsx6(Step, { action: "Fetch", msg: url }));
2831
2938
  const resp = await fetch(url, {
2832
2939
  headers: { "User-Agent": "aai-cli/1.0" },
2833
2940
  redirect: "follow",
@@ -2838,27 +2945,27 @@ async function runRag(opts) {
2838
2945
  }
2839
2946
  const content = await resp.text();
2840
2947
  if (content.length === 0) {
2841
- log(/* @__PURE__ */ jsx5(Warn, { msg: "File is empty" }));
2948
+ log(/* @__PURE__ */ jsx6(Warn, { msg: "File is empty" }));
2842
2949
  return;
2843
2950
  }
2844
- log(/* @__PURE__ */ jsx5(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
2951
+ log(/* @__PURE__ */ jsx6(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
2845
2952
  const origin = new URL(url).origin;
2846
2953
  const pages = splitPages(content);
2847
- log(/* @__PURE__ */ jsx5(Step, { action: "Parse", msg: `${pages.length} pages` }));
2954
+ log(/* @__PURE__ */ jsx6(Step, { action: "Parse", msg: `${pages.length} pages` }));
2848
2955
  const { RecursiveChunker } = await import("@chonkiejs/core");
2849
2956
  const chunker = await RecursiveChunker.create({ chunkSize });
2850
2957
  const siteSlug = slugify(origin);
2851
2958
  const allChunks = await chunkPages(pages, chunker, origin, siteSlug);
2852
- log(/* @__PURE__ */ jsx5(Step, { action: "Chunk", msg: `${allChunks.length} chunks` }));
2959
+ log(/* @__PURE__ */ jsx6(Step, { action: "Chunk", msg: `${allChunks.length} chunks` }));
2853
2960
  const vectorUrl = `${serverUrl}/${slug}/vector`;
2854
- log(/* @__PURE__ */ jsx5(Info, { msg: `target: ${vectorUrl}` }));
2961
+ log(/* @__PURE__ */ jsx6(Info, { msg: `target: ${vectorUrl}` }));
2855
2962
  const result = await upsertChunks(allChunks, vectorUrl, apiKey, setProgress);
2856
- log(/* @__PURE__ */ jsx5(Step, { action: "Done", msg: `${result.upserted} chunks upserted` }));
2963
+ log(/* @__PURE__ */ jsx6(Step, { action: "Done", msg: `${result.upserted} chunks upserted` }));
2857
2964
  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}` }));
2965
+ log(/* @__PURE__ */ jsx6(Warn, { msg: `${result.errors} failed` }));
2966
+ if (result.lastError) log(/* @__PURE__ */ jsx6(Info, { msg: `last error: ${result.lastError}` }));
2860
2967
  }
2861
- log(/* @__PURE__ */ jsx5(Detail, { msg: `Agent: ${slug}` }));
2968
+ log(/* @__PURE__ */ jsx6(Detail, { msg: `Agent: ${slug}` }));
2862
2969
  }
2863
2970
  async function chunkPages(pages, chunker, origin, siteSlug) {
2864
2971
  const allChunks = [];
@@ -2927,7 +3034,7 @@ async function upsertChunks(chunks, vectorUrl, apiKey, setProgress) {
2927
3034
  return { upserted, errors, lastError };
2928
3035
  }
2929
3036
  async function runRagCommand(args, version) {
2930
- const parsed = minimist4(args, {
3037
+ const parsed = minimist5(args, {
2931
3038
  string: ["server", "chunk-size"],
2932
3039
  boolean: ["help", "yes"],
2933
3040
  alias: { s: "server", h: "help", y: "yes" },
@@ -2959,7 +3066,7 @@ async function runRagCommand(args, version) {
2959
3066
  const chunkSize = Number.parseInt(parsed["chunk-size"] ?? "512", 10);
2960
3067
  let thrownError;
2961
3068
  const app = render3(
2962
- /* @__PURE__ */ jsx5(
3069
+ /* @__PURE__ */ jsx6(
2963
3070
  RagUI,
2964
3071
  {
2965
3072
  url,
@@ -3027,8 +3134,8 @@ init_discover();
3027
3134
  init_help();
3028
3135
  init_ink();
3029
3136
  init_prompts();
3030
- import minimist5 from "minimist";
3031
- import { jsx as jsx6 } from "react/jsx-runtime";
3137
+ import minimist6 from "minimist";
3138
+ import { jsx as jsx7 } from "react/jsx-runtime";
3032
3139
  var secretCommandDef = {
3033
3140
  name: "secret",
3034
3141
  description: "Manage secrets",
@@ -3046,7 +3153,7 @@ async function requireProjectConfig(cwd) {
3046
3153
  return config;
3047
3154
  }
3048
3155
  async function runSecretCommand(args, version) {
3049
- const parsed = minimist5(args, {
3156
+ const parsed = minimist6(args, {
3050
3157
  boolean: ["help", "yes"],
3051
3158
  alias: { h: "help", y: "yes" },
3052
3159
  stopEarly: true
@@ -3101,7 +3208,7 @@ async function secretPut(cwd, name, value) {
3101
3208
  const text = await resp.text();
3102
3209
  throw new Error(`Failed to set secret: ${text}`);
3103
3210
  }
3104
- log(/* @__PURE__ */ jsx6(Step, { action: "Set", msg: `${name} for ${slug}` }));
3211
+ log(/* @__PURE__ */ jsx7(Step, { action: "Set", msg: `${name} for ${slug}` }));
3105
3212
  });
3106
3213
  }
3107
3214
  async function secretDelete(cwd, name) {
@@ -3116,7 +3223,7 @@ async function secretDelete(cwd, name) {
3116
3223
  const text = await resp.text();
3117
3224
  throw new Error(`Failed to delete secret: ${text}`);
3118
3225
  }
3119
- log(/* @__PURE__ */ jsx6(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
3226
+ log(/* @__PURE__ */ jsx7(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
3120
3227
  });
3121
3228
  }
3122
3229
  async function secretList(cwd) {
@@ -3131,10 +3238,10 @@ async function secretList(cwd) {
3131
3238
  }
3132
3239
  const { vars } = await resp.json();
3133
3240
  if (vars.length === 0) {
3134
- log(/* @__PURE__ */ jsx6(StepInfo, { action: "Secrets", msg: "none set" }));
3241
+ log(/* @__PURE__ */ jsx7(StepInfo, { action: "Secrets", msg: "none set" }));
3135
3242
  } else {
3136
3243
  for (const name of vars) {
3137
- log(/* @__PURE__ */ jsx6(Detail, { msg: name }));
3244
+ log(/* @__PURE__ */ jsx7(Detail, { msg: name }));
3138
3245
  }
3139
3246
  }
3140
3247
  });
@@ -3144,24 +3251,24 @@ async function secretList(cwd) {
3144
3251
  init_discover();
3145
3252
  init_help();
3146
3253
  init_ink();
3147
- import path9 from "node:path";
3148
- import minimist6 from "minimist";
3254
+ import path10 from "node:path";
3255
+ import minimist7 from "minimist";
3149
3256
 
3150
3257
  // cli/_start.ts
3151
3258
  init_ink();
3152
- import path8 from "node:path";
3153
- import React3 from "react";
3259
+ import path9 from "node:path";
3260
+ import React4 from "react";
3154
3261
  async function _startProductionServer(cwd, port, log) {
3155
- const clientDir = path8.join(cwd, ".aai", "client");
3156
- log(React3.createElement(Step, { action: "Start", msg: "loading agent" }));
3262
+ const clientDir = path9.join(cwd, ".aai", "client");
3263
+ log(React4.createElement(Step, { action: "Start", msg: "loading agent" }));
3157
3264
  const agentDef = await loadAgentDef(cwd);
3158
3265
  const env = await resolveServerEnv();
3159
3266
  await bootServer(agentDef, clientDir, env, port);
3160
- log(React3.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
3267
+ log(React4.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
3161
3268
  }
3162
3269
 
3163
3270
  // cli/start.tsx
3164
- import { jsx as jsx7 } from "react/jsx-runtime";
3271
+ import { jsx as jsx8 } from "react/jsx-runtime";
3165
3272
  var startCommandDef = {
3166
3273
  name: "start",
3167
3274
  description: "Start the production server from a build",
@@ -3174,7 +3281,7 @@ var startCommandDef = {
3174
3281
  ]
3175
3282
  };
3176
3283
  async function runStartCommand(args, version) {
3177
- const parsed = minimist6(args, {
3284
+ const parsed = minimist7(args, {
3178
3285
  string: ["port"],
3179
3286
  boolean: ["help", "yes"],
3180
3287
  alias: { p: "port", h: "help", y: "yes" }
@@ -3185,24 +3292,24 @@ async function runStartCommand(args, version) {
3185
3292
  }
3186
3293
  const cwd = process.env.INIT_CWD || process.cwd();
3187
3294
  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"))) {
3295
+ const buildDir = path10.join(cwd, ".aai", "build");
3296
+ if (!await fileExists(path10.join(buildDir, "worker.js"))) {
3190
3297
  throw new Error("No build found \u2014 run `aai build` first");
3191
3298
  }
3192
3299
  await getApiKey();
3193
3300
  await runWithInk(async (log) => {
3194
- log(/* @__PURE__ */ jsx7(Step, { action: "Start", msg: `production server on port ${port}` }));
3301
+ log(/* @__PURE__ */ jsx8(Step, { action: "Start", msg: `production server on port ${port}` }));
3195
3302
  await _startProductionServer(cwd, port, log);
3196
3303
  });
3197
3304
  }
3198
3305
 
3199
3306
  // cli/cli.ts
3200
- var cliDir = path10.dirname(fileURLToPath2(import.meta.url));
3201
- var pkgJsonPath = path10.join(cliDir, "..", "package.json");
3307
+ var cliDir = path11.dirname(fileURLToPath2(import.meta.url));
3308
+ var pkgJsonPath = path11.join(cliDir, "..", "package.json");
3202
3309
  var pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
3203
3310
  var VERSION = pkgJson.version;
3204
3311
  async function main(args) {
3205
- const parsed = minimist7(args, {
3312
+ const parsed = minimist8(args, {
3206
3313
  boolean: ["help", "version"],
3207
3314
  alias: { h: "help", V: "version" },
3208
3315
  stopEarly: true
@@ -3221,6 +3328,9 @@ async function main(args) {
3221
3328
  case "init":
3222
3329
  await runInitCommand(subArgs, VERSION);
3223
3330
  return;
3331
+ case "build":
3332
+ await runBuildCommand(subArgs, VERSION);
3333
+ return;
3224
3334
  case "deploy":
3225
3335
  await runDeployCommand(subArgs, VERSION);
3226
3336
  return;