@alexkroman1/aai 0.7.8 → 0.7.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/aai.js +1 -1
  2. package/dist/cli.js +1772 -1753
  3. package/dist/sdk/_internal_types.d.ts +2 -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/protocol.d.ts +0 -7
  7. package/dist/sdk/protocol.d.ts.map +1 -1
  8. package/dist/sdk/protocol.js +0 -6
  9. package/dist/sdk/protocol.js.map +1 -1
  10. package/dist/sdk/s2s.d.ts.map +1 -1
  11. package/dist/sdk/s2s.js +14 -2
  12. package/dist/sdk/s2s.js.map +1 -1
  13. package/dist/sdk/server.d.ts +2 -0
  14. package/dist/sdk/server.d.ts.map +1 -1
  15. package/dist/sdk/server.js +41 -5
  16. package/dist/sdk/server.js.map +1 -1
  17. package/dist/sdk/winterc_server.d.ts.map +1 -1
  18. package/dist/sdk/winterc_server.js +1 -2
  19. package/dist/sdk/winterc_server.js.map +1 -1
  20. package/dist/sdk/worker_shim.d.ts.map +1 -1
  21. package/dist/sdk/worker_shim.js +2 -3
  22. package/dist/sdk/worker_shim.js.map +1 -1
  23. package/dist/ui/session.d.ts.map +1 -1
  24. package/dist/ui/session.js +0 -13
  25. package/dist/ui/session.js.map +1 -1
  26. package/package.json +7 -4
  27. package/templates/_shared/CLAUDE.md +192 -38
  28. package/templates/_shared/biome.json +32 -0
  29. package/templates/_shared/index.html +16 -0
  30. package/templates/_shared/package.json +6 -6
  31. package/templates/code-interpreter/client.tsx +1 -0
  32. package/templates/dispatch-center/client.tsx +1 -0
  33. package/templates/embedded-assets/client.tsx +1 -0
  34. package/templates/health-assistant/client.tsx +1 -0
  35. package/templates/infocom-adventure/client.tsx +1 -0
  36. package/templates/math-buddy/client.tsx +1 -0
  37. package/templates/memory-agent/client.tsx +1 -0
  38. package/templates/night-owl/client.tsx +1 -0
  39. package/templates/personal-finance/client.tsx +1 -0
  40. package/templates/simple/client.tsx +1 -0
  41. package/templates/smart-research/client.tsx +1 -0
  42. package/templates/support/client.tsx +1 -0
  43. package/templates/travel-concierge/client.tsx +1 -0
  44. package/templates/web-researcher/client.tsx +1 -0
  45. package/ui/styles.css +73 -0
package/dist/cli.js CHANGED
@@ -12,206 +12,951 @@ var __export = (target, all) => {
12
12
  // cli/_colors.ts
13
13
  import chalk from "chalk";
14
14
  function primary(s) {
15
- return chalk.hex("#fab283")(s);
15
+ return chalk.hex(COLORS.primary)(s);
16
16
  }
17
17
  function interactive(s) {
18
- return chalk.hex("#56b6c2")(s);
19
- }
20
- function error(s) {
21
- return chalk.hex("#e06c75")(s);
22
- }
23
- function warning(s) {
24
- return chalk.hex("#f5a742")(s);
18
+ return chalk.hex(COLORS.interactive)(s);
25
19
  }
20
+ var COLORS;
26
21
  var init_colors = __esm({
27
22
  "cli/_colors.ts"() {
28
23
  "use strict";
24
+ COLORS = {
25
+ primary: "#fab283",
26
+ interactive: "#56b6c2",
27
+ error: "#e06c75",
28
+ warning: "#f5a742",
29
+ success: "#7fd88f",
30
+ accent: "#9d7cd8",
31
+ muted: "#808080"
32
+ };
29
33
  }
30
34
  });
31
35
 
32
- // cli/_output.ts
33
- import chalk3 from "chalk";
34
- function fmt(action, color, msg) {
35
- return `${color(chalk3.bold(action.padStart(PAD)))} ${msg}`;
36
- }
37
- function step(action, msg) {
38
- console.log(fmt(action, primary, msg));
39
- }
40
- function stepInfo(action, msg) {
41
- console.log(fmt(action, interactive, msg));
42
- }
43
- function info(msg) {
44
- console.log(chalk3.dim(`${" ".repeat(PAD + 1)}${msg}`));
45
- }
46
- function warn(msg) {
47
- console.error(fmt("warning", warning, msg));
36
+ // cli/_help.ts
37
+ import chalk2 from "chalk";
38
+ function rootHelp(version) {
39
+ const lines = [];
40
+ lines.push("");
41
+ lines.push(
42
+ ` ${primary(chalk2.bold(" \u2584\u2580\u2588 \u2584\u2580\u2588 \u2588"))} ${chalk2.dim("Voice agent development kit")}`
43
+ );
44
+ lines.push(` ${primary(chalk2.bold(" \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588"))} ${primary(`v${version}`)}`);
45
+ lines.push("");
46
+ lines.push(
47
+ ` ${chalk2.bold(interactive("Usage"))} ${primary("aai")} ${chalk2.dim("<command> [options]")}`
48
+ );
49
+ lines.push("");
50
+ lines.push(` ${chalk2.bold(interactive("Commands"))}`);
51
+ lines.push("");
52
+ const cmds = [
53
+ ["init", "[dir]", "Scaffold a new agent project"],
54
+ ["dev", "", "Start a local development server"],
55
+ ["deploy", "", "Bundle and deploy to production"],
56
+ ["start", "", "Start production server from build"],
57
+ ["secret", "<cmd>", "Manage secrets"],
58
+ ["rag", "<url>", "Ingest a site's llms-full.txt into the vector store"]
59
+ ];
60
+ for (const [name, args, desc] of cmds) {
61
+ const nameStr = interactive(name.padEnd(8));
62
+ const argsStr = args ? primary(args.padEnd(6)) : " ";
63
+ lines.push(` ${nameStr} ${argsStr} ${chalk2.dim(desc)}`);
64
+ }
65
+ lines.push("");
66
+ lines.push(` ${chalk2.bold(interactive("Options"))}`);
67
+ lines.push("");
68
+ lines.push(
69
+ ` ${interactive("-h")}${chalk2.dim(",")} ${interactive("--help")} ${chalk2.dim(
70
+ "Show this help"
71
+ )}`
72
+ );
73
+ lines.push(
74
+ ` ${interactive("-V")}${chalk2.dim(",")} ${interactive("--version")} ${chalk2.dim(
75
+ "Show the version number"
76
+ )}`
77
+ );
78
+ lines.push("");
79
+ lines.push(` ${chalk2.bold(interactive("Getting started"))}`);
80
+ lines.push("");
81
+ lines.push(
82
+ ` ${chalk2.dim("$")} ${primary("aai init")} ${interactive("my-agent")} ${chalk2.dim(
83
+ "Create a new agent"
84
+ )}`
85
+ );
86
+ lines.push(` ${chalk2.dim("$")} ${primary("cd my-agent")}`);
87
+ lines.push(
88
+ ` ${chalk2.dim("$")} ${primary("aai deploy")} ${chalk2.dim("Deploy to production")}`
89
+ );
90
+ lines.push("");
91
+ return lines.join("\n");
48
92
  }
49
- function error2(msg) {
50
- console.error(`${error(chalk3.bold("error"))}: ${msg}`);
93
+ function subcommandHelp(cmd, version) {
94
+ const lines = [];
95
+ lines.push("");
96
+ lines.push(
97
+ ` ${primary(chalk2.bold("aai"))} ${interactive(chalk2.bold(cmd.name))}${version ? chalk2.dim(` v${version}`) : ""}`
98
+ );
99
+ lines.push(` ${chalk2.dim(cmd.description)}`);
100
+ lines.push("");
101
+ if (cmd.args && cmd.args.length > 0) {
102
+ lines.push(` ${chalk2.bold(interactive("Arguments"))}`);
103
+ lines.push("");
104
+ for (const arg of cmd.args) {
105
+ const label = arg.optional ? primary(`[${arg.name}]`) : primary(`<${arg.name}>`);
106
+ lines.push(` ${label}`);
107
+ }
108
+ lines.push("");
109
+ }
110
+ const visibleOptions = (cmd.options ?? []).filter((o) => !o.hidden);
111
+ if (visibleOptions.length > 0) {
112
+ lines.push(` ${chalk2.bold(interactive("Options"))}`);
113
+ lines.push("");
114
+ for (const opt of visibleOptions) {
115
+ lines.push(` ${interactive(opt.flags)}`);
116
+ lines.push(` ${chalk2.dim(opt.description)}`);
117
+ }
118
+ lines.push(` ${interactive("-h")}${chalk2.dim(",")} ${interactive("--help")}`);
119
+ lines.push(` ${chalk2.dim("Show this help")}`);
120
+ lines.push("");
121
+ }
122
+ return lines.join("\n");
51
123
  }
52
- var PAD;
53
- var init_output = __esm({
54
- "cli/_output.ts"() {
124
+ var init_help = __esm({
125
+ "cli/_help.ts"() {
55
126
  "use strict";
56
127
  init_colors();
57
- PAD = 9;
58
128
  }
59
129
  });
60
130
 
61
- // cli/_new.ts
62
- var new_exports = {};
63
- __export(new_exports, {
64
- _internals: () => _internals2,
65
- listTemplates: () => listTemplates,
66
- runNew: () => runNew
67
- });
68
- import fs4 from "node:fs/promises";
69
- import path4 from "node:path";
70
- import glob from "fast-glob";
71
- import fsExtra from "fs-extra";
72
- async function listTemplates(dir) {
73
- const templates = [];
74
- const entries = await fs4.readdir(dir, { withFileTypes: true });
75
- for (const entry of entries) {
76
- if (entry.isDirectory() && !entry.name.startsWith("_")) {
77
- templates.push(entry.name);
78
- }
131
+ // cli/_bundler.ts
132
+ import fs from "node:fs/promises";
133
+ import path from "node:path";
134
+ import preact from "@preact/preset-vite";
135
+ import tailwindcss from "@tailwindcss/vite";
136
+ import { build } from "vite";
137
+ async function readDirRecursive(dir, base = dir) {
138
+ const files = {};
139
+ let names;
140
+ try {
141
+ names = await fs.readdir(dir);
142
+ } catch {
143
+ return files;
79
144
  }
80
- return templates.sort();
81
- }
82
- async function copyDirNoOverwrite(src, dest) {
83
- const files = await glob("**/*", { cwd: src, dot: true, onlyFiles: true });
84
- for (const file of files) {
85
- const destPath = path4.join(dest, file);
86
- try {
87
- await fs4.access(destPath);
88
- } catch {
89
- await fs4.mkdir(path4.dirname(destPath), { recursive: true });
90
- await fs4.copyFile(path4.join(src, file), destPath);
145
+ for (const name of names) {
146
+ const full = path.join(dir, name);
147
+ const stat = await fs.stat(full);
148
+ const entry = { name, isDirectory: () => stat.isDirectory() };
149
+ if (entry.isDirectory()) {
150
+ Object.assign(files, await readDirRecursive(full, base));
151
+ } else {
152
+ const rel = path.relative(base, full);
153
+ files[rel] = await fs.readFile(full, "utf-8");
91
154
  }
92
155
  }
156
+ return files;
93
157
  }
94
- async function runNew(opts) {
95
- const { targetDir, template, templatesDir } = opts;
96
- const available = await listTemplates(templatesDir);
97
- if (!available.includes(template)) {
98
- throw new Error(`unknown template '${template}' -- available: ${available.join(", ")}`);
99
- }
100
- _internals2.step("Create", `from template '${template}'`);
101
- await fsExtra.copy(path4.join(templatesDir, template), targetDir, { overwrite: true });
102
- await copyDirNoOverwrite(path4.join(templatesDir, "_shared"), targetDir);
158
+ async function bundleAgent(agent, opts) {
159
+ const aaiDir = path.join(agent.dir, ".aai");
160
+ const buildDir = path.join(aaiDir, "build");
161
+ const clientDir = path.join(aaiDir, "client");
162
+ await fs.mkdir(aaiDir, { recursive: true });
163
+ const workerEntry = path.join(aaiDir, "_worker_entry.ts");
164
+ await fs.writeFile(
165
+ workerEntry,
166
+ [
167
+ `import agent from "../agent.ts";`,
168
+ `import { initWorker } from "@alexkroman1/aai/worker-shim";`,
169
+ `initWorker(agent);`
170
+ ].join("\n")
171
+ );
103
172
  try {
104
- await fs4.copyFile(path4.join(targetDir, ".env.example"), path4.join(targetDir, ".env"));
105
- } catch {
173
+ await build({
174
+ configFile: false,
175
+ root: agent.dir,
176
+ logLevel: "warn",
177
+ build: {
178
+ outDir: buildDir,
179
+ emptyOutDir: true,
180
+ minify: true,
181
+ target: "es2022",
182
+ rollupOptions: {
183
+ input: workerEntry,
184
+ output: {
185
+ format: "es",
186
+ entryFileNames: "worker.js",
187
+ inlineDynamicImports: true
188
+ }
189
+ }
190
+ }
191
+ });
192
+ } catch (err) {
193
+ throw new BundleError(err instanceof Error ? err.message : String(err));
106
194
  }
107
- const readmePath = path4.join(targetDir, "README.md");
108
- try {
109
- await fs4.access(readmePath);
110
- } catch {
111
- const slug = path4.basename(path4.resolve(targetDir));
112
- const readme = `# ${slug}
113
-
114
- A voice agent built with [aai](https://github.com/anthropics/aai).
115
-
116
- ## Getting started
117
-
118
- \`\`\`sh
119
- npm install # Install dependencies
120
- npm run dev # Run locally (opens browser)
121
- npm run deploy # Deploy to production
122
- \`\`\`
123
-
124
- ## Environment variables
125
-
126
- Secrets are managed on the server, not in local files:
127
-
128
- \`\`\`sh
129
- aai env add MY_KEY # Set a secret (prompts for value)
130
- aai env ls # List secret names
131
- aai env pull # Pull names into .env for reference
132
- aai env rm MY_KEY # Remove a secret
133
- \`\`\`
134
-
135
- Access secrets in your agent via \`ctx.env.MY_KEY\`.
136
-
137
- ## Learn more
138
-
139
- See \`CLAUDE.md\` for the full agent API reference.
140
- `;
141
- await fs4.writeFile(readmePath, readme);
195
+ const skipClient = opts?.skipClient || !agent.clientEntry;
196
+ if (!skipClient) {
197
+ try {
198
+ await build({
199
+ root: agent.dir,
200
+ base: "./",
201
+ logLevel: "warn",
202
+ plugins: [preact(), tailwindcss()],
203
+ build: {
204
+ outDir: clientDir,
205
+ emptyOutDir: true,
206
+ minify: true,
207
+ target: "es2022"
208
+ }
209
+ });
210
+ } catch (err) {
211
+ throw new BundleError(err instanceof Error ? err.message : String(err));
212
+ }
142
213
  }
143
- _internals2.step("Done", targetDir);
144
- return targetDir;
214
+ const worker = await fs.readFile(path.join(buildDir, "worker.js"), "utf-8");
215
+ const clientFiles = await readDirRecursive(clientDir);
216
+ return {
217
+ worker,
218
+ clientFiles,
219
+ clientDir,
220
+ workerBytes: Buffer.byteLength(worker)
221
+ };
145
222
  }
146
- var _internals2;
147
- var init_new = __esm({
148
- "cli/_new.ts"() {
223
+ var BundleError;
224
+ var init_bundler = __esm({
225
+ "cli/_bundler.ts"() {
149
226
  "use strict";
150
- init_output();
151
- _internals2 = {
152
- step
227
+ BundleError = class extends Error {
228
+ constructor(message) {
229
+ super(message);
230
+ this.name = "BundleError";
231
+ }
153
232
  };
154
233
  }
155
234
  });
156
235
 
157
- // dist/sdk/protocol.js
158
- import { z } from "zod";
159
- var PROTOCOL_VERSION, DEFAULT_TTS_SAMPLE_RATE, AUDIO_FORMAT, _bitsPerSample, _channels, AudioFrameSpec, KvRequestBaseSchema, HOOK_TIMEOUT_MS, SessionErrorCodeSchema, TranscriptEventSchema, ClientEventSchema, ClientMessageSchema;
160
- var init_protocol = __esm({
161
- "dist/sdk/protocol.js"() {
162
- "use strict";
163
- PROTOCOL_VERSION = 1;
164
- DEFAULT_TTS_SAMPLE_RATE = 24e3;
165
- AUDIO_FORMAT = "pcm16";
166
- _bitsPerSample = 16;
167
- _channels = 1;
168
- AudioFrameSpec = {
169
- /** Audio codec identifier sent in the `ready` message. */
170
- format: AUDIO_FORMAT,
171
- /** Signed 16-bit integer samples. */
172
- bitsPerSample: _bitsPerSample,
173
- /** Little-endian byte order. */
174
- endianness: "little",
175
- /** Mono audio. */
176
- channels: _channels,
177
- /** Bytes per sample — derived from bitsPerSample and channels. */
178
- bytesPerSample: _bitsPerSample / 8 * _channels
179
- };
180
- KvRequestBaseSchema = z.discriminatedUnion("op", [
181
- z.object({ op: z.literal("get"), key: z.string().min(1) }),
182
- z.object({
183
- op: z.literal("set"),
184
- key: z.string().min(1),
185
- value: z.string(),
186
- ttl: z.number().int().positive().optional()
187
- }),
188
- z.object({ op: z.literal("del"), key: z.string().min(1) }),
189
- z.object({
190
- op: z.literal("list"),
191
- prefix: z.string(),
192
- limit: z.number().int().positive().optional(),
193
- reverse: z.boolean().optional()
194
- })
195
- ]);
196
- HOOK_TIMEOUT_MS = 5e3;
197
- SessionErrorCodeSchema = z.enum([
198
- "stt",
199
- "llm",
200
- "tts",
201
- "tool",
202
- "protocol",
203
- "connection",
204
- "audio",
205
- "internal"
206
- ]);
207
- TranscriptEventSchema = z.object({
208
- type: z.literal("transcript"),
209
- text: z.string(),
210
- isFinal: z.boolean(),
211
- turnOrder: z.number().int().nonnegative().optional()
212
- });
213
- ClientEventSchema = z.discriminatedUnion("type", [
214
- z.object({ type: z.literal("speech_started") }),
236
+ // cli/_prompts.tsx
237
+ import { ConfirmInput, PasswordInput, Select, TextInput } from "@inkjs/ui";
238
+ import { Box, render, Text } from "ink";
239
+ import { jsx, jsxs } from "react/jsx-runtime";
240
+ async function askPassword(message) {
241
+ return new Promise((resolve) => {
242
+ const app = render(
243
+ /* @__PURE__ */ jsxs(Box, { children: [
244
+ /* @__PURE__ */ jsxs(Text, { children: [
245
+ message,
246
+ ": "
247
+ ] }),
248
+ /* @__PURE__ */ jsx(
249
+ PasswordInput,
250
+ {
251
+ onSubmit: (value) => {
252
+ resolve(value);
253
+ app.unmount();
254
+ }
255
+ }
256
+ )
257
+ ] })
258
+ );
259
+ });
260
+ }
261
+ async function askText(message, defaultValue) {
262
+ return new Promise((resolve) => {
263
+ const app = render(
264
+ /* @__PURE__ */ jsxs(Box, { children: [
265
+ /* @__PURE__ */ jsxs(Text, { color: COLORS.interactive, children: [
266
+ message,
267
+ " \u203A "
268
+ ] }),
269
+ /* @__PURE__ */ jsx(
270
+ TextInput,
271
+ {
272
+ placeholder: defaultValue,
273
+ onSubmit: (value) => {
274
+ resolve(value || defaultValue);
275
+ app.unmount();
276
+ }
277
+ }
278
+ )
279
+ ] })
280
+ );
281
+ });
282
+ }
283
+ async function askEnter(message) {
284
+ return new Promise((resolve) => {
285
+ const app = render(
286
+ /* @__PURE__ */ jsxs(Box, { children: [
287
+ /* @__PURE__ */ jsx(Text, { color: COLORS.interactive, children: message }),
288
+ /* @__PURE__ */ jsx(
289
+ TextInput,
290
+ {
291
+ placeholder: "",
292
+ onSubmit: () => {
293
+ resolve();
294
+ app.unmount();
295
+ }
296
+ }
297
+ )
298
+ ] })
299
+ );
300
+ });
301
+ }
302
+ var init_prompts = __esm({
303
+ "cli/_prompts.tsx"() {
304
+ "use strict";
305
+ init_colors();
306
+ }
307
+ });
308
+
309
+ // cli/_discover.ts
310
+ var discover_exports = {};
311
+ __export(discover_exports, {
312
+ DEFAULT_SERVER: () => DEFAULT_SERVER,
313
+ fileExists: () => fileExists,
314
+ generateSlug: () => generateSlug,
315
+ getApiKey: () => getApiKey,
316
+ isDevMode: () => isDevMode,
317
+ loadAgent: () => loadAgent,
318
+ readProjectConfig: () => readProjectConfig,
319
+ writeProjectConfig: () => writeProjectConfig
320
+ });
321
+ import { accessSync } from "node:fs";
322
+ import fs2 from "node:fs/promises";
323
+ import path2 from "node:path";
324
+ import { humanId } from "human-id";
325
+ function isDevMode() {
326
+ const script = process.argv[1] ?? "";
327
+ if (script.endsWith(".ts") || script.endsWith(".tsx")) return true;
328
+ if (script.includes("/dist/") && !script.includes("node_modules")) {
329
+ const dir = path2.dirname(path2.dirname(script));
330
+ try {
331
+ accessSync(path2.join(dir, "sdk"));
332
+ accessSync(path2.join(dir, "cli"));
333
+ return true;
334
+ } catch {
335
+ return false;
336
+ }
337
+ }
338
+ return false;
339
+ }
340
+ function generateSlug() {
341
+ return humanId({ separator: "-", capitalize: false });
342
+ }
343
+ async function readAuthConfig() {
344
+ try {
345
+ return JSON.parse(await fs2.readFile(CONFIG_FILE, "utf-8"));
346
+ } catch {
347
+ return {};
348
+ }
349
+ }
350
+ async function writeAuthConfig(config) {
351
+ await fs2.mkdir(CONFIG_DIR, { recursive: true });
352
+ await fs2.writeFile(CONFIG_FILE, `${JSON.stringify(config, null, 2)}
353
+ `);
354
+ if (process.platform !== "win32") {
355
+ await fs2.chmod(CONFIG_FILE, 384);
356
+ }
357
+ }
358
+ async function getApiKey() {
359
+ const config = await readAuthConfig();
360
+ if (config.assemblyai_api_key) {
361
+ process.env.ASSEMBLYAI_API_KEY = config.assemblyai_api_key;
362
+ return config.assemblyai_api_key;
363
+ }
364
+ let key;
365
+ while (!key) {
366
+ key = await askPassword("ASSEMBLYAI_API_KEY");
367
+ }
368
+ config.assemblyai_api_key = key;
369
+ process.env.ASSEMBLYAI_API_KEY = key;
370
+ await writeAuthConfig(config);
371
+ return key;
372
+ }
373
+ async function readProjectConfig(agentDir) {
374
+ try {
375
+ return JSON.parse(await fs2.readFile(path2.join(agentDir, ".aai", "project.json"), "utf-8"));
376
+ } catch {
377
+ return null;
378
+ }
379
+ }
380
+ async function writeProjectConfig(agentDir, data) {
381
+ const aaiDir = path2.join(agentDir, ".aai");
382
+ await fs2.mkdir(aaiDir, { recursive: true });
383
+ await fs2.writeFile(path2.join(aaiDir, "project.json"), `${JSON.stringify(data, null, 2)}
384
+ `);
385
+ }
386
+ async function fileExists(p) {
387
+ try {
388
+ await fs2.access(p);
389
+ return true;
390
+ } catch {
391
+ return false;
392
+ }
393
+ }
394
+ async function loadAgent(dir) {
395
+ const hasAgentTs = await fileExists(path2.join(dir, "agent.ts"));
396
+ if (!hasAgentTs) return null;
397
+ const config = await readProjectConfig(dir);
398
+ const slug = config?.slug ?? generateSlug();
399
+ const clientEntry = await fileExists(path2.join(dir, "client.tsx")) ? path2.join(dir, "client.tsx") : "";
400
+ return {
401
+ slug,
402
+ dir,
403
+ entryPoint: path2.join(dir, "agent.ts"),
404
+ clientEntry
405
+ };
406
+ }
407
+ var CONFIG_DIR, CONFIG_FILE, DEFAULT_SERVER;
408
+ var init_discover = __esm({
409
+ "cli/_discover.ts"() {
410
+ "use strict";
411
+ init_prompts();
412
+ CONFIG_DIR = path2.join(process.env.HOME ?? process.env.USERPROFILE ?? ".", ".config", "aai");
413
+ CONFIG_FILE = path2.join(CONFIG_DIR, "config.json");
414
+ DEFAULT_SERVER = "https://aai-agent.fly.dev";
415
+ }
416
+ });
417
+
418
+ // cli/_deploy.ts
419
+ async function attemptDeploy(url, slug, apiKey, env, worker, clientFiles) {
420
+ return await _internals.fetch(`${url}/${slug}/deploy`, {
421
+ method: "POST",
422
+ headers: {
423
+ "Content-Type": "application/json",
424
+ Authorization: `Bearer ${apiKey}`
425
+ },
426
+ body: JSON.stringify({
427
+ env,
428
+ worker,
429
+ clientFiles
430
+ })
431
+ });
432
+ }
433
+ async function runDeploy(opts) {
434
+ const worker = opts.bundle.worker;
435
+ const clientFiles = opts.bundle.clientFiles;
436
+ let slug = opts.slug;
437
+ if (opts.dryRun) {
438
+ return { slug };
439
+ }
440
+ for (let i = 0; i < MAX_RETRIES; i++) {
441
+ const resp = await attemptDeploy(opts.url, slug, opts.apiKey, opts.env, worker, clientFiles);
442
+ if (resp.ok) {
443
+ return { slug };
444
+ }
445
+ if (resp.status === 403) {
446
+ const text2 = await resp.text();
447
+ if (text2.includes("Slug")) {
448
+ slug = generateSlug();
449
+ continue;
450
+ }
451
+ }
452
+ const text = await resp.text();
453
+ throw new Error(`deploy failed (${resp.status}): ${text}`);
454
+ }
455
+ throw new Error(`deploy failed: could not find available slug after ${MAX_RETRIES} attempts`);
456
+ }
457
+ var _internals, MAX_RETRIES;
458
+ var init_deploy = __esm({
459
+ "cli/_deploy.ts"() {
460
+ "use strict";
461
+ init_discover();
462
+ _internals = {
463
+ fetch: globalThis.fetch.bind(globalThis)
464
+ };
465
+ MAX_RETRIES = 20;
466
+ }
467
+ });
468
+
469
+ // cli/_ink.tsx
470
+ import { Spinner } from "@inkjs/ui";
471
+ import { Box as Box2, render as render2, Static, Text as Text2, useApp } from "ink";
472
+ import React, { useRef, useState } from "react";
473
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
474
+ function Step({ action, msg }) {
475
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
476
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.primary, children: action }),
477
+ /* @__PURE__ */ jsxs2(Text2, { children: [
478
+ " ",
479
+ msg
480
+ ] })
481
+ ] });
482
+ }
483
+ function StepInfo({ action, msg }) {
484
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
485
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.interactive, children: action }),
486
+ /* @__PURE__ */ jsxs2(Text2, { children: [
487
+ " ",
488
+ msg
489
+ ] })
490
+ ] });
491
+ }
492
+ function Info({ msg }) {
493
+ return /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: msg });
494
+ }
495
+ function Detail({ msg }) {
496
+ return /* @__PURE__ */ jsx2(Text2, { children: msg });
497
+ }
498
+ function Warn({ msg }) {
499
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
500
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.warning, children: "warning" }),
501
+ /* @__PURE__ */ jsxs2(Text2, { children: [
502
+ " ",
503
+ msg
504
+ ] })
505
+ ] });
506
+ }
507
+ function ErrorLine({ msg }) {
508
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
509
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.error, children: "error" }),
510
+ /* @__PURE__ */ jsxs2(Text2, { children: [
511
+ ": ",
512
+ msg
513
+ ] })
514
+ ] });
515
+ }
516
+ function StepLog({ items }) {
517
+ return /* @__PURE__ */ jsx2(Static, { items, children: (item) => /* @__PURE__ */ jsx2(Box2, { children: item.node }, item.id) });
518
+ }
519
+ function useStepLog() {
520
+ const [items, setItems] = useState([]);
521
+ const nextId = useRef(0);
522
+ const log = (node) => {
523
+ const id = nextId.current++;
524
+ setItems((prev) => [...prev, { id, node }]);
525
+ };
526
+ return { items, log };
527
+ }
528
+ function CommandRunner({
529
+ run,
530
+ onError
531
+ }) {
532
+ const { exit } = useApp();
533
+ const { items, log } = useStepLog();
534
+ const [spinning, setSpinning] = useState(true);
535
+ const [currentStep, setCurrentStep] = useState(null);
536
+ const [err, setErr] = useState(null);
537
+ const currentStepRef = useRef(null);
538
+ const wrappedLog = (node) => {
539
+ if (currentStepRef.current) {
540
+ log(currentStepRef.current);
541
+ }
542
+ currentStepRef.current = node;
543
+ setCurrentStep(node);
544
+ };
545
+ const started = useRef(false);
546
+ React.useEffect(() => {
547
+ if (started.current) return;
548
+ started.current = true;
549
+ (async () => {
550
+ try {
551
+ await run(wrappedLog);
552
+ } catch (e) {
553
+ const error = e instanceof Error ? e : new Error(String(e));
554
+ setErr(error.message);
555
+ onError?.(error);
556
+ }
557
+ if (currentStepRef.current) {
558
+ log(currentStepRef.current);
559
+ currentStepRef.current = null;
560
+ }
561
+ setCurrentStep(null);
562
+ setSpinning(false);
563
+ setTimeout(() => exit(), 0);
564
+ })();
565
+ });
566
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
567
+ /* @__PURE__ */ jsx2(StepLog, { items }),
568
+ err && /* @__PURE__ */ jsx2(ErrorLine, { msg: err }),
569
+ spinning && currentStep && /* @__PURE__ */ jsxs2(Box2, { children: [
570
+ /* @__PURE__ */ jsx2(Spinner, {}),
571
+ /* @__PURE__ */ jsx2(Text2, { children: " " }),
572
+ currentStep
573
+ ] })
574
+ ] });
575
+ }
576
+ async function runWithInk(fn) {
577
+ let thrownError;
578
+ const app = render2(
579
+ /* @__PURE__ */ jsx2(
580
+ CommandRunner,
581
+ {
582
+ onError: (e) => {
583
+ thrownError = e;
584
+ },
585
+ run: fn
586
+ }
587
+ )
588
+ );
589
+ await app.waitUntilExit();
590
+ if (thrownError) throw thrownError;
591
+ }
592
+ var init_ink = __esm({
593
+ "cli/_ink.tsx"() {
594
+ "use strict";
595
+ init_colors();
596
+ }
597
+ });
598
+
599
+ // cli/_init.ts
600
+ var init_exports = {};
601
+ __export(init_exports, {
602
+ listTemplates: () => listTemplates,
603
+ runInit: () => runInit
604
+ });
605
+ import fs3 from "node:fs/promises";
606
+ import path3 from "node:path";
607
+ import glob from "fast-glob";
608
+ import fsExtra from "fs-extra";
609
+ async function listTemplates(dir) {
610
+ const templates = [];
611
+ const entries = await fs3.readdir(dir, { withFileTypes: true });
612
+ for (const entry of entries) {
613
+ if (entry.isDirectory() && !entry.name.startsWith("_")) {
614
+ templates.push(entry.name);
615
+ }
616
+ }
617
+ return templates.sort();
618
+ }
619
+ async function copyDirNoOverwrite(src, dest) {
620
+ const files = await glob("**/*", { cwd: src, dot: true, onlyFiles: true });
621
+ for (const file of files) {
622
+ const destPath = path3.join(dest, file);
623
+ try {
624
+ await fs3.access(destPath);
625
+ } catch {
626
+ await fs3.mkdir(path3.dirname(destPath), { recursive: true });
627
+ await fs3.copyFile(path3.join(src, file), destPath);
628
+ }
629
+ }
630
+ }
631
+ async function runInit(opts) {
632
+ const { targetDir, template, templatesDir } = opts;
633
+ const available = await listTemplates(templatesDir);
634
+ if (!available.includes(template)) {
635
+ throw new Error(`unknown template '${template}' -- available: ${available.join(", ")}`);
636
+ }
637
+ await fsExtra.copy(path3.join(templatesDir, template), targetDir, { overwrite: true });
638
+ await copyDirNoOverwrite(path3.join(templatesDir, "_shared"), targetDir);
639
+ try {
640
+ await fs3.copyFile(path3.join(targetDir, ".env.example"), path3.join(targetDir, ".env"));
641
+ } catch {
642
+ }
643
+ const readmePath = path3.join(targetDir, "README.md");
644
+ try {
645
+ await fs3.access(readmePath);
646
+ } catch {
647
+ const slug = path3.basename(path3.resolve(targetDir));
648
+ const readme = `# ${slug}
649
+
650
+ A voice agent built with [aai](https://github.com/anthropics/aai).
651
+
652
+ ## Getting started
653
+
654
+ \`\`\`sh
655
+ npm install # Install dependencies
656
+ npm run dev # Run locally (opens browser)
657
+ npm run deploy # Deploy to production
658
+ \`\`\`
659
+
660
+ ## Environment variables
661
+
662
+ Secrets are managed on the server, not in local files:
663
+
664
+ \`\`\`sh
665
+ aai env add MY_KEY # Set a secret (prompts for value)
666
+ aai env ls # List secret names
667
+ aai env pull # Pull names into .env for reference
668
+ aai env rm MY_KEY # Remove a secret
669
+ \`\`\`
670
+
671
+ Access secrets in your agent via \`ctx.env.MY_KEY\`.
672
+
673
+ ## Learn more
674
+
675
+ See \`CLAUDE.md\` for the full agent API reference.
676
+ `;
677
+ await fs3.writeFile(readmePath, readme);
678
+ }
679
+ return targetDir;
680
+ }
681
+ var init_init = __esm({
682
+ "cli/_init.ts"() {
683
+ "use strict";
684
+ }
685
+ });
686
+
687
+ // cli/init.tsx
688
+ import { execFile } from "node:child_process";
689
+ import fs4 from "node:fs/promises";
690
+ import path4 from "node:path";
691
+ import { fileURLToPath } from "node:url";
692
+ import { promisify } from "node:util";
693
+ import minimist from "minimist";
694
+ import { jsx as jsx3 } from "react/jsx-runtime";
695
+ async function rewriteDevDeps(cwd, cliDir2) {
696
+ const monorepoRoot = path4.join(cliDir2, "..");
697
+ const pkgJsonPath2 = path4.join(cwd, "package.json");
698
+ const pkgJson2 = JSON.parse(await fs4.readFile(pkgJsonPath2, "utf-8"));
699
+ const rootPkg = JSON.parse(await fs4.readFile(path4.join(monorepoRoot, "package.json"), "utf-8"));
700
+ const rootPkgName = rootPkg.name;
701
+ if (pkgJson2.dependencies[rootPkgName]) {
702
+ pkgJson2.dependencies[rootPkgName] = `file:${monorepoRoot}`;
703
+ }
704
+ await fs4.writeFile(pkgJsonPath2, `${JSON.stringify(pkgJson2, null, 2)}
705
+ `);
706
+ }
707
+ async function installDeps(cwd, log) {
708
+ if (await fileExists(path4.join(cwd, "node_modules"))) return;
709
+ let pkgJson2;
710
+ try {
711
+ pkgJson2 = JSON.parse(await fs4.readFile(path4.join(cwd, "package.json"), "utf-8"));
712
+ } catch {
713
+ pkgJson2 = {};
714
+ }
715
+ const deps = Object.keys(pkgJson2.dependencies ?? {});
716
+ const devDeps = Object.keys(pkgJson2.devDependencies ?? {});
717
+ if (deps.length > 0) {
718
+ log(/* @__PURE__ */ jsx3(Step, { action: "Install", msg: deps.join(", ") }));
719
+ }
720
+ if (devDeps.length > 0) {
721
+ log(/* @__PURE__ */ jsx3(Step, { action: "Install", msg: `dev: ${devDeps.join(", ")}` }));
722
+ }
723
+ try {
724
+ await execFileAsync("npm", ["install"], { cwd });
725
+ } catch {
726
+ log(/* @__PURE__ */ jsx3(Step, { action: "Skip", msg: "npm install failed" }));
727
+ }
728
+ }
729
+ async function runInitCommand(args, version, opts) {
730
+ const parsed = minimist(args, {
731
+ string: ["template"],
732
+ boolean: ["force", "help"],
733
+ alias: { t: "template", f: "force", h: "help" }
734
+ });
735
+ if (parsed.help) {
736
+ console.log(subcommandHelp(initCommandDef, version));
737
+ return "";
738
+ }
739
+ const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
740
+ await getApiKey2();
741
+ let dir = parsed._[0];
742
+ if (!dir) {
743
+ dir = await askText("What is your project named?", "my-voice-agent");
744
+ }
745
+ const cwd = path4.resolve(process.env.INIT_CWD || process.cwd(), dir);
746
+ if (!parsed.force && await fileExists(path4.join(cwd, "agent.ts"))) {
747
+ console.log(
748
+ `agent.ts already exists in this directory. Use ${interactive("--force")} to overwrite.`
749
+ );
750
+ process.exit(1);
751
+ }
752
+ const cliDir2 = path4.dirname(fileURLToPath(import.meta.url));
753
+ const templatesDir = path4.join(cliDir2, "..", "templates");
754
+ const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
755
+ const template = parsed.template || "simple";
756
+ await runWithInk(async (log) => {
757
+ log(/* @__PURE__ */ jsx3(Step, { action: "Create", msg: dir }));
758
+ await runInit2({ targetDir: cwd, template, templatesDir });
759
+ if (isDevMode()) {
760
+ await rewriteDevDeps(cwd, cliDir2);
761
+ }
762
+ await installDeps(cwd, log);
763
+ });
764
+ process.chdir(cwd);
765
+ delete process.env.INIT_CWD;
766
+ if (!opts?.quiet) {
767
+ const { runDeployCommand: runDeployCommand2 } = await Promise.resolve().then(() => (init_deploy2(), deploy_exports));
768
+ await runDeployCommand2(["-y"], version);
769
+ }
770
+ return cwd;
771
+ }
772
+ var execFileAsync, initCommandDef;
773
+ var init_init2 = __esm({
774
+ "cli/init.tsx"() {
775
+ "use strict";
776
+ init_colors();
777
+ init_discover();
778
+ init_help();
779
+ init_ink();
780
+ init_prompts();
781
+ execFileAsync = promisify(execFile);
782
+ initCommandDef = {
783
+ name: "init",
784
+ description: "Scaffold a new agent project",
785
+ args: [{ name: "dir", optional: true }],
786
+ options: [
787
+ {
788
+ flags: "-t, --template <template>",
789
+ description: "Template to use"
790
+ },
791
+ { flags: "-f, --force", description: "Overwrite existing agent.ts" }
792
+ ]
793
+ };
794
+ }
795
+ });
796
+
797
+ // cli/deploy.tsx
798
+ var deploy_exports = {};
799
+ __export(deploy_exports, {
800
+ runDeployCommand: () => runDeployCommand
801
+ });
802
+ import path5 from "node:path";
803
+ import minimist2 from "minimist";
804
+ import { jsx as jsx4 } from "react/jsx-runtime";
805
+ function resolveServerUrl(parsed) {
806
+ return parsed.server || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
807
+ }
808
+ async function buildAgent(cwd, log) {
809
+ log(/* @__PURE__ */ jsx4(Step, { action: "Build", msg: "bundling agent" }));
810
+ const agent = await loadAgent(cwd);
811
+ if (!agent) {
812
+ throw new Error("No agent found \u2014 run `aai init` first");
813
+ }
814
+ let bundle;
815
+ try {
816
+ bundle = await bundleAgent(agent);
817
+ } catch (err) {
818
+ if (err instanceof BundleError) {
819
+ throw new Error(`Bundle failed: ${err.message}`);
820
+ }
821
+ throw err;
822
+ }
823
+ return bundle;
824
+ }
825
+ async function deployBundle(opts) {
826
+ const { bundle, serverUrl, apiKey, cwd, log } = opts;
827
+ let { slug } = opts;
828
+ log(/* @__PURE__ */ jsx4(Step, { action: "Deploy", msg: slug }));
829
+ const deployed = await runDeploy({
830
+ url: serverUrl,
831
+ bundle,
832
+ env: { ASSEMBLYAI_API_KEY: apiKey },
833
+ slug,
834
+ dryRun: false,
835
+ apiKey
836
+ });
837
+ slug = deployed.slug;
838
+ await writeProjectConfig(cwd, { slug, serverUrl });
839
+ const agentUrl = `${serverUrl}/${slug}`;
840
+ log(/* @__PURE__ */ jsx4(Step, { action: "Ready", msg: agentUrl }));
841
+ return agentUrl;
842
+ }
843
+ async function runDeployCommand(args, version) {
844
+ const parsed = minimist2(args, {
845
+ string: ["server"],
846
+ boolean: ["dry-run", "help", "yes"],
847
+ alias: { s: "server", h: "help", y: "yes" }
848
+ });
849
+ if (parsed.help) {
850
+ console.log(subcommandHelp(deployCommandDef, version));
851
+ return;
852
+ }
853
+ const cwd = process.env.INIT_CWD || process.cwd();
854
+ if (!await fileExists(path5.join(cwd, "agent.ts"))) {
855
+ await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
856
+ }
857
+ const serverUrl = resolveServerUrl(parsed);
858
+ const dryRun = parsed["dry-run"] ?? false;
859
+ const apiKey = dryRun ? "" : await getApiKey();
860
+ const projectConfig = await readProjectConfig(cwd);
861
+ const slug = projectConfig?.slug ?? generateSlug();
862
+ let agentUrl = "";
863
+ await runWithInk(async (log) => {
864
+ const bundle = await buildAgent(cwd, log);
865
+ if (dryRun) {
866
+ log(/* @__PURE__ */ jsx4(StepInfo, { action: "Dry run", msg: `would deploy as ${slug}` }));
867
+ return;
868
+ }
869
+ agentUrl = await deployBundle({ bundle, serverUrl, apiKey, slug, cwd, log });
870
+ });
871
+ if (agentUrl && !dryRun) {
872
+ await askEnter("Press enter to open in browser");
873
+ const { exec } = await import("node:child_process");
874
+ exec(`open "${agentUrl}"`);
875
+ }
876
+ }
877
+ var deployCommandDef;
878
+ var init_deploy2 = __esm({
879
+ "cli/deploy.tsx"() {
880
+ "use strict";
881
+ init_bundler();
882
+ init_deploy();
883
+ init_discover();
884
+ init_help();
885
+ init_ink();
886
+ init_prompts();
887
+ init_init2();
888
+ deployCommandDef = {
889
+ name: "deploy",
890
+ description: "Bundle and deploy to production",
891
+ options: [
892
+ { flags: "-s, --server <url>", description: "Server URL" },
893
+ {
894
+ flags: "--dry-run",
895
+ description: "Validate and bundle without deploying"
896
+ },
897
+ { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
898
+ ]
899
+ };
900
+ }
901
+ });
902
+
903
+ // sdk/protocol.ts
904
+ import { z } from "zod";
905
+ var DEFAULT_TTS_SAMPLE_RATE, AUDIO_FORMAT, _bitsPerSample, _channels, AudioFrameSpec, KvRequestBaseSchema, HOOK_TIMEOUT_MS, SessionErrorCodeSchema, TranscriptEventSchema, ClientEventSchema, ClientMessageSchema;
906
+ var init_protocol = __esm({
907
+ "sdk/protocol.ts"() {
908
+ "use strict";
909
+ DEFAULT_TTS_SAMPLE_RATE = 24e3;
910
+ AUDIO_FORMAT = "pcm16";
911
+ _bitsPerSample = 16;
912
+ _channels = 1;
913
+ AudioFrameSpec = {
914
+ /** Audio codec identifier sent in the `ready` message. */
915
+ format: AUDIO_FORMAT,
916
+ /** Signed 16-bit integer samples. */
917
+ bitsPerSample: _bitsPerSample,
918
+ /** Little-endian byte order. */
919
+ endianness: "little",
920
+ /** Mono audio. */
921
+ channels: _channels,
922
+ /** Bytes per sample — derived from bitsPerSample and channels. */
923
+ bytesPerSample: _bitsPerSample / 8 * _channels
924
+ };
925
+ KvRequestBaseSchema = z.discriminatedUnion("op", [
926
+ z.object({ op: z.literal("get"), key: z.string().min(1) }),
927
+ z.object({
928
+ op: z.literal("set"),
929
+ key: z.string().min(1),
930
+ value: z.string(),
931
+ ttl: z.number().int().positive().optional()
932
+ }),
933
+ z.object({ op: z.literal("del"), key: z.string().min(1) }),
934
+ z.object({
935
+ op: z.literal("list"),
936
+ prefix: z.string(),
937
+ limit: z.number().int().positive().optional(),
938
+ reverse: z.boolean().optional()
939
+ })
940
+ ]);
941
+ HOOK_TIMEOUT_MS = 5e3;
942
+ SessionErrorCodeSchema = z.enum([
943
+ "stt",
944
+ "llm",
945
+ "tts",
946
+ "tool",
947
+ "protocol",
948
+ "connection",
949
+ "audio",
950
+ "internal"
951
+ ]);
952
+ TranscriptEventSchema = z.object({
953
+ type: z.literal("transcript"),
954
+ text: z.string(),
955
+ isFinal: z.boolean(),
956
+ turnOrder: z.number().int().nonnegative().optional()
957
+ });
958
+ ClientEventSchema = z.discriminatedUnion("type", [
959
+ z.object({ type: z.literal("speech_started") }),
215
960
  z.object({ type: z.literal("speech_stopped") }),
216
961
  TranscriptEventSchema,
217
962
  z.object({
@@ -246,45 +991,39 @@ var init_protocol = __esm({
246
991
  z.object({ type: z.literal("reset") }),
247
992
  z.object({
248
993
  type: z.literal("history"),
249
- messages: z.array(z.object({
250
- role: z.enum(["user", "assistant"]),
251
- text: z.string().max(1e5)
252
- })).max(200)
994
+ messages: z.array(
995
+ z.object({
996
+ role: z.enum(["user", "assistant"]),
997
+ text: z.string().max(1e5)
998
+ })
999
+ ).max(200)
253
1000
  })
254
1001
  ]);
255
1002
  }
256
1003
  });
257
1004
 
258
- // dist/sdk/runtime.js
1005
+ // sdk/runtime.ts
259
1006
  var consoleLogger, noopMetrics, DEFAULT_S2S_CONFIG;
260
1007
  var init_runtime = __esm({
261
- "dist/sdk/runtime.js"() {
1008
+ "sdk/runtime.ts"() {
262
1009
  "use strict";
263
1010
  init_protocol();
264
1011
  consoleLogger = {
265
1012
  info(msg, ctx) {
266
- if (ctx)
267
- console.log(msg, ctx);
268
- else
269
- console.log(msg);
1013
+ if (ctx) console.log(msg, ctx);
1014
+ else console.log(msg);
270
1015
  },
271
1016
  warn(msg, ctx) {
272
- if (ctx)
273
- console.warn(msg, ctx);
274
- else
275
- console.warn(msg);
1017
+ if (ctx) console.warn(msg, ctx);
1018
+ else console.warn(msg);
276
1019
  },
277
1020
  error(msg, ctx) {
278
- if (ctx)
279
- console.error(msg, ctx);
280
- else
281
- console.error(msg);
1021
+ if (ctx) console.error(msg, ctx);
1022
+ else console.error(msg);
282
1023
  },
283
1024
  debug(msg, ctx) {
284
- if (ctx)
285
- console.debug(msg, ctx);
286
- else
287
- console.debug(msg);
1025
+ if (ctx) console.debug(msg, ctx);
1026
+ else console.debug(msg);
288
1027
  }
289
1028
  };
290
1029
  noopMetrics = {
@@ -302,7 +1041,7 @@ var init_runtime = __esm({
302
1041
  }
303
1042
  });
304
1043
 
305
- // dist/sdk/_internal_types.js
1044
+ // sdk/_internal_types.ts
306
1045
  import { z as z2 } from "zod";
307
1046
  function agentToolsToSchemas(tools) {
308
1047
  return Object.entries(tools).map(([name, def]) => ({
@@ -313,13 +1052,13 @@ function agentToolsToSchemas(tools) {
313
1052
  }
314
1053
  var EMPTY_PARAMS;
315
1054
  var init_internal_types = __esm({
316
- "dist/sdk/_internal_types.js"() {
1055
+ "sdk/_internal_types.ts"() {
317
1056
  "use strict";
318
1057
  EMPTY_PARAMS = z2.object({});
319
1058
  }
320
1059
  });
321
1060
 
322
- // dist/sdk/builtin_tools.js
1061
+ // sdk/builtin_tools.ts
323
1062
  import { convert } from "html-to-text";
324
1063
  import { z as z3 } from "zod";
325
1064
  function htmlToText(html) {
@@ -344,12 +1083,10 @@ function createWebSearch() {
344
1083
  headers: { "X-Subscription-Token": apiKey },
345
1084
  signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
346
1085
  });
347
- if (!resp.ok)
348
- return [];
1086
+ if (!resp.ok) return [];
349
1087
  const raw = await resp.json();
350
1088
  const data = BraveSearchResponseSchema.safeParse(raw);
351
- if (!data.success)
352
- return [];
1089
+ if (!data.success) return [];
353
1090
  return (data.data.web?.results ?? []).slice(0, maxResults).map((r) => ({
354
1091
  title: r.title,
355
1092
  url: r.url,
@@ -433,7 +1170,9 @@ function createRunCode() {
433
1170
  const fn = new AsyncFunction("console", code);
434
1171
  await Promise.race([
435
1172
  fn(fakeConsole),
436
- new Promise((_, reject) => setTimeout(() => reject(new Error("Code execution timed out")), RUN_CODE_TIMEOUT))
1173
+ new Promise(
1174
+ (_, reject) => setTimeout(() => reject(new Error("Code execution timed out")), RUN_CODE_TIMEOUT)
1175
+ )
437
1176
  ]);
438
1177
  const result = output.join("\n").trim();
439
1178
  return result || "Code ran successfully (no output)";
@@ -481,8 +1220,7 @@ function getBuiltinToolDefs(names, opts) {
481
1220
  function getBuiltinToolSchemas(names) {
482
1221
  return names.flatMap((name) => {
483
1222
  const creator = TOOL_CREATORS[name];
484
- if (!creator)
485
- return [];
1223
+ if (!creator) return [];
486
1224
  const def = creator();
487
1225
  return [
488
1226
  {
@@ -495,7 +1233,7 @@ function getBuiltinToolSchemas(names) {
495
1233
  }
496
1234
  var webSearchParams, BRAVE_SEARCH_URL, BraveSearchResponseSchema, MAX_PAGE_CHARS, MAX_HTML_BYTES, visitWebpageParams, fetchJsonParams, runCodeParams, vectorSearchParams, TOOL_CREATORS;
497
1235
  var init_builtin_tools = __esm({
498
- "dist/sdk/builtin_tools.js"() {
1236
+ "sdk/builtin_tools.ts"() {
499
1237
  "use strict";
500
1238
  init_internal_types();
501
1239
  webSearchParams = z3.object({
@@ -505,11 +1243,13 @@ var init_builtin_tools = __esm({
505
1243
  BRAVE_SEARCH_URL = "https://api.search.brave.com/res/v1/web/search";
506
1244
  BraveSearchResponseSchema = z3.object({
507
1245
  web: z3.object({
508
- results: z3.array(z3.object({
509
- title: z3.string(),
510
- url: z3.string(),
511
- description: z3.string()
512
- }))
1246
+ results: z3.array(
1247
+ z3.object({
1248
+ title: z3.string(),
1249
+ url: z3.string(),
1250
+ description: z3.string()
1251
+ })
1252
+ )
513
1253
  }).optional()
514
1254
  });
515
1255
  MAX_PAGE_CHARS = 1e4;
@@ -525,7 +1265,9 @@ var init_builtin_tools = __esm({
525
1265
  code: z3.string().describe("JavaScript code to execute. Use console.log() for output.")
526
1266
  });
527
1267
  vectorSearchParams = z3.object({
528
- query: z3.string().describe('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".'),
1268
+ query: z3.string().describe(
1269
+ '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".'
1270
+ ),
529
1271
  topK: z3.number().describe("Maximum results to return (default: 5)").optional()
530
1272
  });
531
1273
  TOOL_CREATORS = {
@@ -539,11 +1281,10 @@ var init_builtin_tools = __esm({
539
1281
  }
540
1282
  });
541
1283
 
542
- // dist/sdk/kv.js
1284
+ // sdk/kv.ts
543
1285
  function sortAndPaginate(entries, options) {
544
1286
  entries.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
545
- if (options?.reverse)
546
- entries.reverse();
1287
+ if (options?.reverse) entries.reverse();
547
1288
  if (options?.limit && options.limit > 0) {
548
1289
  entries.length = Math.min(entries.length, options.limit);
549
1290
  }
@@ -558,8 +1299,7 @@ function createMemoryKv() {
558
1299
  get(key) {
559
1300
  const entry = store.get(key);
560
1301
  if (!entry || isExpired(entry)) {
561
- if (entry)
562
- store.delete(key);
1302
+ if (entry) store.delete(key);
563
1303
  return Promise.resolve(null);
564
1304
  }
565
1305
  return Promise.resolve(JSON.parse(entry.raw));
@@ -586,8 +1326,7 @@ function createMemoryKv() {
586
1326
  const entries = [];
587
1327
  let i = 0;
588
1328
  for (const [key, entry] of store) {
589
- if (++i % 500 === 0)
590
- await new Promise((r) => setTimeout(r, 0));
1329
+ if (++i % 500 === 0) await new Promise((r) => setTimeout(r, 0));
591
1330
  if (entry.expiresAt && entry.expiresAt <= now) {
592
1331
  store.delete(key);
593
1332
  continue;
@@ -602,13 +1341,13 @@ function createMemoryKv() {
602
1341
  }
603
1342
  var MAX_VALUE_SIZE;
604
1343
  var init_kv = __esm({
605
- "dist/sdk/kv.js"() {
1344
+ "sdk/kv.ts"() {
606
1345
  "use strict";
607
1346
  MAX_VALUE_SIZE = 65536;
608
1347
  }
609
1348
  });
610
1349
 
611
- // dist/sdk/s2s.js
1350
+ // sdk/s2s.ts
612
1351
  import { z as z4 } from "zod";
613
1352
  function uint8ToBase64(bytes) {
614
1353
  return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
@@ -620,9 +1359,11 @@ function base64ToUint8(base64) {
620
1359
  function dispatchS2sMessage(target, msg) {
621
1360
  switch (msg.type) {
622
1361
  case "session.ready":
623
- target.dispatchEvent(new CustomEvent("ready", {
624
- detail: { session_id: msg.session_id }
625
- }));
1362
+ target.dispatchEvent(
1363
+ new CustomEvent("ready", {
1364
+ detail: { session_id: msg.session_id }
1365
+ })
1366
+ );
626
1367
  break;
627
1368
  case "session.updated":
628
1369
  target.dispatchEvent(new CustomEvent("session_updated", { detail: msg }));
@@ -634,54 +1375,80 @@ function dispatchS2sMessage(target, msg) {
634
1375
  target.dispatchEvent(new CustomEvent("speech_stopped"));
635
1376
  break;
636
1377
  case "transcript.user.delta":
637
- target.dispatchEvent(new CustomEvent("user_transcript_delta", {
638
- detail: { text: msg.text }
639
- }));
1378
+ target.dispatchEvent(
1379
+ new CustomEvent("user_transcript_delta", {
1380
+ detail: { text: msg.text }
1381
+ })
1382
+ );
640
1383
  break;
641
1384
  case "transcript.user":
642
- target.dispatchEvent(new CustomEvent("user_transcript", {
643
- detail: {
644
- item_id: msg.item_id,
645
- text: msg.text
646
- }
647
- }));
1385
+ target.dispatchEvent(
1386
+ new CustomEvent("user_transcript", {
1387
+ detail: {
1388
+ item_id: msg.item_id,
1389
+ text: msg.text
1390
+ }
1391
+ })
1392
+ );
648
1393
  break;
649
1394
  case "reply.started":
650
- target.dispatchEvent(new CustomEvent("reply_started", {
651
- detail: { reply_id: msg.reply_id }
652
- }));
1395
+ target.dispatchEvent(
1396
+ new CustomEvent("reply_started", {
1397
+ detail: { reply_id: msg.reply_id }
1398
+ })
1399
+ );
653
1400
  break;
654
1401
  // reply.audio handled on the fast path — never reaches dispatch.
1402
+ case "transcript.agent.delta":
1403
+ target.dispatchEvent(
1404
+ new CustomEvent("agent_transcript_delta", {
1405
+ detail: { text: msg.delta }
1406
+ })
1407
+ );
1408
+ break;
655
1409
  case "transcript.agent":
656
- target.dispatchEvent(new CustomEvent("agent_transcript", {
657
- detail: { text: msg.text }
658
- }));
1410
+ target.dispatchEvent(
1411
+ new CustomEvent("agent_transcript", {
1412
+ detail: { text: msg.text }
1413
+ })
1414
+ );
659
1415
  break;
660
1416
  case "tool.call":
661
- target.dispatchEvent(new CustomEvent("tool_call", {
662
- detail: {
663
- call_id: msg.call_id,
664
- name: msg.name,
665
- args: msg.args
666
- }
667
- }));
1417
+ target.dispatchEvent(
1418
+ new CustomEvent("tool_call", {
1419
+ detail: {
1420
+ call_id: msg.call_id,
1421
+ name: msg.name,
1422
+ args: msg.args
1423
+ }
1424
+ })
1425
+ );
668
1426
  break;
669
1427
  case "reply.done":
670
- target.dispatchEvent(new CustomEvent("reply_done", {
671
- detail: { status: msg.status }
672
- }));
1428
+ target.dispatchEvent(
1429
+ new CustomEvent("reply_done", {
1430
+ detail: { status: msg.status }
1431
+ })
1432
+ );
673
1433
  break;
674
1434
  case "session.error": {
675
1435
  const isExpired = msg.code === "session_not_found" || msg.code === "session_forbidden";
676
- target.dispatchEvent(new CustomEvent(isExpired ? "session_expired" : "error", {
677
- detail: { code: msg.code, message: msg.message }
678
- }));
1436
+ target.dispatchEvent(
1437
+ new CustomEvent(isExpired ? "session_expired" : "error", {
1438
+ detail: { code: msg.code, message: msg.message }
1439
+ })
1440
+ );
679
1441
  break;
680
1442
  }
1443
+ case "reply.content_part.started":
1444
+ case "reply.content_part.done":
1445
+ break;
681
1446
  case "error":
682
- target.dispatchEvent(new CustomEvent("error", {
683
- detail: { code: "connection", message: msg.message }
684
- }));
1447
+ target.dispatchEvent(
1448
+ new CustomEvent("error", {
1449
+ detail: { code: "connection", message: msg.message }
1450
+ })
1451
+ );
685
1452
  break;
686
1453
  }
687
1454
  }
@@ -695,20 +1462,24 @@ function connectS2s(opts) {
695
1462
  const target = new EventTarget();
696
1463
  let opened = false;
697
1464
  function send(msg) {
698
- if (ws.readyState !== WS_OPEN)
699
- return;
1465
+ if (ws.readyState !== WS_OPEN) return;
700
1466
  const json = JSON.stringify(msg);
701
1467
  if (msg.type !== "input.audio") {
702
- log.debug(`S2S >> ${msg.type}`);
1468
+ log.debug(
1469
+ `S2S >> ${msg.type}`,
1470
+ msg.type === "session.update" ? { payload: json } : void 0
1471
+ );
703
1472
  }
704
1473
  ws.send(json);
705
1474
  }
706
1475
  const handle = Object.assign(target, {
707
1476
  sendAudio(audio) {
708
- if (ws.readyState !== WS_OPEN)
709
- return;
1477
+ if (ws.readyState !== WS_OPEN) return;
710
1478
  if (ws.sendBinary) {
711
- const ab = audio.buffer.slice(audio.byteOffset, audio.byteOffset + audio.byteLength);
1479
+ const ab = audio.buffer.slice(
1480
+ audio.byteOffset,
1481
+ audio.byteOffset + audio.byteLength
1482
+ );
712
1483
  ws.sendBinary(ab);
713
1484
  } else {
714
1485
  ws.send(`{"type":"input.audio","audio":"${uint8ToBase64(audio)}"}`);
@@ -752,7 +1523,9 @@ function connectS2s(opts) {
752
1523
  }
753
1524
  const parsed = S2sServerMessageSchema.safeParse(raw);
754
1525
  if (!parsed.success) {
755
- log.debug("S2S << unrecognised message type");
1526
+ log.debug(
1527
+ `S2S << unrecognised message type: ${obj.type ?? JSON.stringify(raw).slice(0, 100)}`
1528
+ );
756
1529
  return;
757
1530
  }
758
1531
  const msg = parsed.data;
@@ -772,16 +1545,18 @@ function connectS2s(opts) {
772
1545
  if (!opened) {
773
1546
  reject(errObj);
774
1547
  } else {
775
- target.dispatchEvent(new CustomEvent("error", {
776
- detail: { code: "ws_error", message: errObj.message }
777
- }));
1548
+ target.dispatchEvent(
1549
+ new CustomEvent("error", {
1550
+ detail: { code: "ws_error", message: errObj.message }
1551
+ })
1552
+ );
778
1553
  }
779
1554
  });
780
1555
  });
781
1556
  }
782
1557
  var WS_OPEN, S2sServerMessageSchema;
783
1558
  var init_s2s = __esm({
784
- "dist/sdk/s2s.js"() {
1559
+ "sdk/s2s.ts"() {
785
1560
  "use strict";
786
1561
  init_runtime();
787
1562
  WS_OPEN = 1;
@@ -798,7 +1573,10 @@ var init_s2s = __esm({
798
1573
  }),
799
1574
  z4.object({ type: z4.literal("reply.started"), reply_id: z4.string() }),
800
1575
  // reply.audio is handled on the fast path before Zod — see message handler.
1576
+ z4.object({ type: z4.literal("transcript.agent.delta"), delta: z4.string() }).passthrough(),
801
1577
  z4.object({ type: z4.literal("transcript.agent"), text: z4.string() }),
1578
+ z4.object({ type: z4.literal("reply.content_part.started") }).passthrough(),
1579
+ z4.object({ type: z4.literal("reply.content_part.done") }).passthrough(),
802
1580
  z4.object({
803
1581
  type: z4.literal("tool.call"),
804
1582
  call_id: z4.string(),
@@ -823,10 +1601,10 @@ var init_s2s = __esm({
823
1601
  }
824
1602
  });
825
1603
 
826
- // dist/sdk/types.js
1604
+ // sdk/types.ts
827
1605
  var DEFAULT_INSTRUCTIONS;
828
1606
  var init_types = __esm({
829
- "dist/sdk/types.js"() {
1607
+ "sdk/types.ts"() {
830
1608
  "use strict";
831
1609
  DEFAULT_INSTRUCTIONS = `You are AAI, a helpful AI assistant.
832
1610
 
@@ -842,7 +1620,7 @@ Voice-First Rules:
842
1620
  }
843
1621
  });
844
1622
 
845
- // dist/sdk/system_prompt.js
1623
+ // sdk/system_prompt.ts
846
1624
  function buildSystemPrompt(config, opts) {
847
1625
  const { hasTools } = opts;
848
1626
  const agentInstructions = config.instructions && config.instructions !== DEFAULT_INSTRUCTIONS ? `
@@ -862,16 +1640,28 @@ Today's date is ${today}.` + agentInstructions + toolPreamble + (opts?.voice ? V
862
1640
  }
863
1641
  var VOICE_RULES;
864
1642
  var init_system_prompt = __esm({
865
- "dist/sdk/system_prompt.js"() {
1643
+ "sdk/system_prompt.ts"() {
866
1644
  "use strict";
867
1645
  init_types();
868
1646
  VOICE_RULES = '\n\nCRITICAL OUTPUT RULES \u2014 you MUST follow these for EVERY response:\nYour response will be spoken aloud by a TTS system and displayed as plain text.\n- NEVER use markdown: no **, no *, no _, no #, no `, no [](), no ---\n- NEVER use bullet points (-, *, \u2022) or numbered lists (1., 2.)\n- NEVER use code blocks or inline code\n- NEVER mention tools, search, APIs, or technical failures to the user. If a tool returns no results, just answer naturally without explaining why.\n- Write exactly as you would say it out loud to a friend\n- Use short conversational sentences. To list things, say "First," "Next," "Finally,"\n- Keep responses concise \u2014 1 to 3 sentences max';
869
1647
  }
870
1648
  });
871
1649
 
872
- // dist/sdk/session.js
1650
+ // sdk/session.ts
873
1651
  function createS2sSession(opts) {
874
- const { id, agent, client, toolSchemas, apiKey, s2sConfig, executeTool, createWebSocket, hookInvoker, logger: log = consoleLogger, metrics = noopMetrics } = opts;
1652
+ const {
1653
+ id,
1654
+ agent,
1655
+ client,
1656
+ toolSchemas,
1657
+ apiKey,
1658
+ s2sConfig,
1659
+ executeTool,
1660
+ createWebSocket,
1661
+ hookInvoker,
1662
+ logger: log = consoleLogger,
1663
+ metrics = noopMetrics
1664
+ } = opts;
875
1665
  const agentLabel = { agent };
876
1666
  const agentConfig = opts.skipGreeting ? { ...opts.agentConfig, greeting: "" } : opts.agentConfig;
877
1667
  const hasTools = toolSchemas.length > 0 || (agentConfig.builtinTools?.length ?? 0) > 0;
@@ -896,8 +1686,7 @@ function createS2sSession(opts) {
896
1686
  let pendingReconnect = false;
897
1687
  let pendingTools = [];
898
1688
  async function resolveTurnConfig() {
899
- if (!hookInvoker)
900
- return null;
1689
+ if (!hookInvoker) return null;
901
1690
  try {
902
1691
  return await hookInvoker.resolveTurnConfig(id, HOOK_TIMEOUT_MS);
903
1692
  } catch {
@@ -905,8 +1694,7 @@ function createS2sSession(opts) {
905
1694
  }
906
1695
  }
907
1696
  function invokeHook(hook, arg) {
908
- if (!hookInvoker)
909
- return;
1697
+ if (!hookInvoker) return;
910
1698
  const run = async () => {
911
1699
  switch (hook) {
912
1700
  case "onConnect":
@@ -992,7 +1780,7 @@ function createS2sSession(opts) {
992
1780
  }
993
1781
  connecting = true;
994
1782
  try {
995
- const handle = await _internals3.connectS2s({
1783
+ const handle = await _internals2.connectS2s({
996
1784
  apiKey,
997
1785
  config: s2sConfig,
998
1786
  createWebSocket,
@@ -1101,1507 +1889,730 @@ function createS2sSession(opts) {
1101
1889
  const msg = err instanceof Error ? err.message : String(err);
1102
1890
  log.error("S2S connect failed", { error: msg });
1103
1891
  client.event({ type: "error", code: "internal", message: msg });
1104
- } finally {
1105
- connecting = false;
1106
- if (pendingReconnect && !sessionAbort.signal.aborted) {
1107
- pendingReconnect = false;
1108
- connectAndSetup().catch((err) => {
1109
- const msg = err instanceof Error ? err.message : String(err);
1110
- log.error("S2S deferred reconnect failed", { error: msg });
1111
- });
1112
- }
1113
- }
1114
- }
1115
- return {
1116
- async start() {
1117
- metrics.sessionsTotal.inc(agentLabel);
1118
- metrics.sessionsActive.inc(agentLabel);
1119
- invokeHook("onConnect");
1120
- await connectAndSetup();
1121
- },
1122
- async stop() {
1123
- if (sessionAbort.signal.aborted)
1124
- return;
1125
- sessionAbort.abort();
1126
- metrics.sessionsActive.dec(agentLabel);
1127
- if (turnPromise)
1128
- await turnPromise;
1129
- s2s?.close();
1130
- invokeHook("onDisconnect");
1131
- },
1132
- onAudio(data) {
1133
- s2s?.sendAudio(data);
1134
- },
1135
- onAudioReady() {
1136
- if (audioReady)
1137
- return;
1138
- audioReady = true;
1139
- },
1140
- onCancel() {
1141
- client.event({ type: "cancelled" });
1142
- },
1143
- onReset() {
1144
- conversationMessages = [];
1145
- toolCallCount = 0;
1146
- turnPromise = null;
1147
- pendingTools = [];
1148
- s2sSessionId = null;
1149
- s2s?.close();
1150
- client.event({ type: "reset" });
1151
- },
1152
- onHistory(incoming) {
1153
- for (const msg of incoming) {
1154
- conversationMessages.push({ role: msg.role, content: msg.text });
1155
- }
1156
- },
1157
- waitForTurn() {
1158
- return turnPromise ?? Promise.resolve();
1159
- }
1160
- };
1161
- }
1162
- var _internals3;
1163
- var init_session = __esm({
1164
- "dist/sdk/session.js"() {
1165
- "use strict";
1166
- init_protocol();
1167
- init_runtime();
1168
- init_s2s();
1169
- init_system_prompt();
1170
- _internals3 = {
1171
- connectS2s
1172
- };
1173
- }
1174
- });
1175
-
1176
- // dist/sdk/vector.js
1177
- function createMemoryVectorStore() {
1178
- const store = /* @__PURE__ */ new Map();
1179
- return {
1180
- upsert(id, data, metadata) {
1181
- store.set(id, { data, metadata });
1182
- return Promise.resolve();
1183
- },
1184
- async query(text, options) {
1185
- const topK = options?.topK ?? 10;
1186
- const query = text.toLowerCase();
1187
- const words = query.split(/\s+/).filter(Boolean);
1188
- const results = [];
1189
- let i = 0;
1190
- for (const [id, entry] of store) {
1191
- if (++i % 500 === 0)
1192
- await new Promise((r) => setTimeout(r, 0));
1193
- const data = entry.data.toLowerCase();
1194
- const matches = words.filter((w) => data.includes(w)).length;
1195
- if (matches > 0) {
1196
- results.push({
1197
- id,
1198
- score: matches / Math.max(words.length, 1),
1199
- data: entry.data,
1200
- metadata: entry.metadata
1201
- });
1202
- }
1203
- }
1204
- results.sort((a, b) => b.score - a.score);
1205
- return results.slice(0, topK);
1206
- },
1207
- remove(ids) {
1208
- const idArray = Array.isArray(ids) ? ids : [ids];
1209
- for (const id of idArray) {
1210
- store.delete(id);
1211
- }
1212
- return Promise.resolve();
1213
- }
1214
- };
1215
- }
1216
- var init_vector = __esm({
1217
- "dist/sdk/vector.js"() {
1218
- "use strict";
1219
- }
1220
- });
1221
-
1222
- // dist/sdk/worker_entry.js
1223
- function buildToolContext(opts) {
1224
- const { env, sessionId, state, kv, vector, messages } = opts;
1225
- return {
1226
- sessionId: sessionId ?? "",
1227
- env: { ...env },
1228
- abortSignal: AbortSignal.timeout(TOOL_HANDLER_TIMEOUT),
1229
- state: state ?? {},
1230
- get kv() {
1231
- if (!kv)
1232
- throw new Error("KV not available");
1233
- return kv;
1234
- },
1235
- get vector() {
1236
- if (!vector)
1237
- throw new Error("Vector store not available");
1238
- return vector;
1239
- },
1240
- messages: messages ?? []
1241
- };
1242
- }
1243
- async function executeToolCall(name, args, options) {
1244
- const { tool } = options;
1245
- const schema = tool.parameters ?? EMPTY_PARAMS;
1246
- const parsed = schema.safeParse(args);
1247
- if (!parsed.success) {
1248
- const issues = (parsed.error?.issues ?? []).map((i) => `${i.path.map(String).join(".")}: ${i.message}`).join(", ");
1249
- return `Error: Invalid arguments for tool "${name}": ${issues}`;
1250
- }
1251
- try {
1252
- const ctx = buildToolContext(options);
1253
- await yieldTick();
1254
- const result = await Promise.resolve(tool.execute(parsed.data, ctx));
1255
- await yieldTick();
1256
- if (result == null)
1257
- return "null";
1258
- return typeof result === "string" ? result : JSON.stringify(result);
1259
- } catch (err) {
1260
- if (err instanceof DOMException && err.name === "TimeoutError") {
1261
- console.warn(`[tool-executor] Tool execution timed out: ${name}`);
1262
- return `Error: Tool "${name}" timed out after ${TOOL_HANDLER_TIMEOUT}ms`;
1263
- }
1264
- console.warn(`[tool-executor] Tool execution failed: ${name}`, err);
1265
- return `Error: ${err instanceof Error ? err.message : String(err)}`;
1266
- }
1267
- }
1268
- var yieldTick, TOOL_HANDLER_TIMEOUT;
1269
- var init_worker_entry = __esm({
1270
- "dist/sdk/worker_entry.js"() {
1271
- "use strict";
1272
- init_internal_types();
1273
- yieldTick = () => new Promise((r) => setTimeout(r, 0));
1274
- TOOL_HANDLER_TIMEOUT = 3e4;
1275
- }
1276
- });
1277
-
1278
- // dist/sdk/direct_executor.js
1279
- function buildAgentConfig(agent) {
1280
- const config = {
1281
- name: agent.name,
1282
- instructions: agent.instructions,
1283
- greeting: agent.greeting,
1284
- voice: agent.voice
1285
- };
1286
- if (agent.sttPrompt !== void 0)
1287
- config.sttPrompt = agent.sttPrompt;
1288
- if (typeof agent.maxSteps !== "function")
1289
- config.maxSteps = agent.maxSteps;
1290
- if (agent.toolChoice !== void 0)
1291
- config.toolChoice = agent.toolChoice;
1292
- if (agent.builtinTools)
1293
- config.builtinTools = [...agent.builtinTools];
1294
- if (agent.activeTools)
1295
- config.activeTools = [...agent.activeTools];
1296
- return config;
1297
- }
1298
- function createDirectExecutor(opts) {
1299
- const { agent, env, kv = createMemoryKv(), vector = createMemoryVectorStore(), vectorSearch, createWebSocket, logger = consoleLogger, metrics = noopMetrics, s2sConfig = DEFAULT_S2S_CONFIG } = opts;
1300
- const agentConfig = buildAgentConfig(agent);
1301
- const builtinDefs = getBuiltinToolDefs(agent.builtinTools ?? [], vectorSearch ? { vectorSearch } : void 0);
1302
- const allTools = {
1303
- ...builtinDefs,
1304
- ...agent.tools
1305
- };
1306
- const customSchemas = agentToolsToSchemas(agent.tools ?? {});
1307
- const builtinSchemas = getBuiltinToolSchemas(agent.builtinTools ?? []);
1308
- const toolSchemas = [...customSchemas, ...builtinSchemas];
1309
- const sessionState = /* @__PURE__ */ new Map();
1310
- const frozenEnv = Object.freeze({ ...env });
1311
- function getState(sessionId) {
1312
- if (!sessionState.has(sessionId) && agent.state) {
1313
- sessionState.set(sessionId, agent.state());
1314
- }
1315
- return sessionState.get(sessionId) ?? {};
1316
- }
1317
- function makeHookContext(sessionId) {
1318
- return {
1319
- sessionId,
1320
- env: frozenEnv,
1321
- state: getState(sessionId),
1322
- get kv() {
1323
- return kv;
1324
- },
1325
- get vector() {
1326
- return vector;
1327
- }
1328
- };
1329
- }
1330
- const executeTool = async (name, args, sessionId, messages) => {
1331
- const tool = allTools[name];
1332
- if (!tool)
1333
- return JSON.stringify({ error: `Unknown tool: ${name}` });
1334
- return executeToolCall(name, args, {
1335
- tool,
1336
- env: frozenEnv,
1337
- sessionId,
1338
- state: getState(sessionId ?? ""),
1339
- kv,
1340
- vector,
1341
- messages
1342
- });
1343
- };
1344
- const hookInvoker = {
1345
- async onConnect(sessionId) {
1346
- await agent.onConnect?.(makeHookContext(sessionId));
1347
- },
1348
- async onDisconnect(sessionId) {
1349
- await agent.onDisconnect?.(makeHookContext(sessionId));
1350
- sessionState.delete(sessionId);
1351
- },
1352
- async onTurn(sessionId, text) {
1353
- await agent.onTurn?.(text, makeHookContext(sessionId));
1354
- },
1355
- async onError(sessionId, error3) {
1356
- await agent.onError?.(new Error(error3.message), makeHookContext(sessionId));
1357
- },
1358
- async onStep(sessionId, step2) {
1359
- await agent.onStep?.(step2, makeHookContext(sessionId));
1360
- },
1361
- async resolveTurnConfig(sessionId) {
1362
- const ctx = makeHookContext(sessionId);
1363
- let maxSteps;
1364
- let activeTools;
1365
- if (typeof agent.maxSteps === "function") {
1366
- maxSteps = await agent.maxSteps(ctx) ?? void 0;
1367
- }
1368
- if (agent.onBeforeStep) {
1369
- const result = await agent.onBeforeStep(0, ctx);
1370
- activeTools = result?.activeTools;
1892
+ } finally {
1893
+ connecting = false;
1894
+ if (pendingReconnect && !sessionAbort.signal.aborted) {
1895
+ pendingReconnect = false;
1896
+ connectAndSetup().catch((err) => {
1897
+ const msg = err instanceof Error ? err.message : String(err);
1898
+ log.error("S2S deferred reconnect failed", { error: msg });
1899
+ });
1371
1900
  }
1372
- if (maxSteps === void 0 && activeTools === void 0)
1373
- return null;
1374
- const config = {};
1375
- if (maxSteps !== void 0)
1376
- config.maxSteps = maxSteps;
1377
- if (activeTools !== void 0)
1378
- config.activeTools = activeTools;
1379
- return config;
1380
- }
1381
- };
1382
- function createSession(sessionOpts) {
1383
- if (!createWebSocket) {
1384
- throw new Error("createWebSocket not provided \u2014 pass it in DirectExecutorOptions");
1385
- }
1386
- const apiKey = frozenEnv.ASSEMBLYAI_API_KEY ?? "";
1387
- return createS2sSession({
1388
- id: sessionOpts.id,
1389
- agent: sessionOpts.agent,
1390
- client: sessionOpts.client,
1391
- agentConfig,
1392
- toolSchemas,
1393
- apiKey,
1394
- s2sConfig,
1395
- executeTool,
1396
- createWebSocket,
1397
- hookInvoker,
1398
- skipGreeting: sessionOpts.skipGreeting ?? false,
1399
- logger,
1400
- metrics
1401
- });
1402
- }
1403
- return { executeTool, hookInvoker, toolSchemas, createSession };
1404
- }
1405
- var init_direct_executor = __esm({
1406
- "dist/sdk/direct_executor.js"() {
1407
- "use strict";
1408
- init_internal_types();
1409
- init_builtin_tools();
1410
- init_kv();
1411
- init_runtime();
1412
- init_session();
1413
- init_vector();
1414
- init_worker_entry();
1415
- }
1416
- });
1417
-
1418
- // dist/sdk/ws_handler.js
1419
- function isValidAudioChunk(data) {
1420
- return data.byteLength > 0 && data.byteLength <= MAX_AUDIO_CHUNK_BYTES && data.byteLength % 2 === 0;
1421
- }
1422
- function createClientSink(ws) {
1423
- function safeSend(data) {
1424
- try {
1425
- if (ws.readyState === 1)
1426
- ws.send(data);
1427
- } catch {
1428
1901
  }
1429
1902
  }
1430
1903
  return {
1431
- get open() {
1432
- return ws.readyState === 1;
1904
+ async start() {
1905
+ metrics.sessionsTotal.inc(agentLabel);
1906
+ metrics.sessionsActive.inc(agentLabel);
1907
+ invokeHook("onConnect");
1908
+ await connectAndSetup();
1433
1909
  },
1434
- event(e) {
1435
- safeSend(JSON.stringify(e));
1910
+ async stop() {
1911
+ if (sessionAbort.signal.aborted) return;
1912
+ sessionAbort.abort();
1913
+ metrics.sessionsActive.dec(agentLabel);
1914
+ if (turnPromise) await turnPromise;
1915
+ s2s?.close();
1916
+ invokeHook("onDisconnect");
1436
1917
  },
1437
- playAudioChunk(chunk) {
1438
- safeSend(chunk);
1918
+ onAudio(data) {
1919
+ s2s?.sendAudio(data);
1439
1920
  },
1440
- playAudioDone() {
1441
- safeSend(JSON.stringify({ type: "audio_done" }));
1921
+ onAudioReady() {
1922
+ if (audioReady) return;
1923
+ audioReady = true;
1924
+ },
1925
+ onCancel() {
1926
+ client.event({ type: "cancelled" });
1927
+ },
1928
+ onReset() {
1929
+ conversationMessages = [];
1930
+ toolCallCount = 0;
1931
+ turnPromise = null;
1932
+ pendingTools = [];
1933
+ s2sSessionId = null;
1934
+ s2s?.close();
1935
+ client.event({ type: "reset" });
1936
+ },
1937
+ onHistory(incoming) {
1938
+ for (const msg of incoming) {
1939
+ conversationMessages.push({ role: msg.role, content: msg.text });
1940
+ }
1941
+ },
1942
+ waitForTurn() {
1943
+ return turnPromise ?? Promise.resolve();
1442
1944
  }
1443
1945
  };
1444
1946
  }
1445
- function isBinaryData(data) {
1446
- return globalThis.Buffer?.isBuffer(data) || data instanceof ArrayBuffer || data instanceof Uint8Array;
1447
- }
1448
- function toUint8Array(data) {
1449
- if (data instanceof Uint8Array)
1450
- return data;
1451
- if (data instanceof ArrayBuffer)
1452
- return new Uint8Array(data);
1453
- const buf = data;
1454
- return new Uint8Array(buf.buffer ?? data, buf.byteOffset ?? 0, buf.byteLength);
1455
- }
1456
- function handleBinaryAudio(data, session, log, ctx, sid) {
1457
- if (!isBinaryData(data))
1458
- return false;
1459
- const chunk = toUint8Array(data);
1460
- if (!isValidAudioChunk(chunk)) {
1461
- log.warn("Invalid audio chunk, dropping", {
1462
- ...ctx,
1463
- sid,
1464
- bytes: chunk.byteLength,
1465
- aligned: chunk.byteLength % 2 === 0
1466
- });
1467
- return true;
1468
- }
1469
- session.onAudio(chunk);
1470
- return true;
1471
- }
1472
- function handleTextMessage(data, session, log, ctx, sid) {
1473
- if (typeof data !== "string")
1474
- return;
1475
- let json;
1476
- try {
1477
- json = JSON.parse(data);
1478
- } catch {
1479
- log.warn("Invalid JSON from client", { ...ctx, sid });
1480
- return;
1481
- }
1482
- const parsed = ClientMessageSchema.safeParse(json);
1483
- if (!parsed.success) {
1484
- log.warn("Invalid client message", { ...ctx, sid, error: parsed.error.message });
1485
- return;
1486
- }
1487
- const msg = parsed.data;
1488
- switch (msg.type) {
1489
- case "audio_ready":
1490
- session.onAudioReady();
1491
- break;
1492
- case "cancel":
1493
- session.onCancel();
1494
- break;
1495
- case "reset":
1496
- session.onReset();
1497
- break;
1498
- case "history":
1499
- session.onHistory(msg.messages);
1500
- break;
1501
- }
1502
- }
1503
- function wireSessionSocket(ws, opts) {
1504
- const { sessions, logger: log = consoleLogger } = opts;
1505
- const sessionId = crypto.randomUUID();
1506
- const sid = sessionId.slice(0, 8);
1507
- const ctx = opts.logContext ?? {};
1508
- let session = null;
1509
- function onOpen() {
1510
- opts.onOpen?.();
1511
- log.info("Session connected", { ...ctx, sid });
1512
- const client = createClientSink(ws);
1513
- session = opts.createSession(sessionId, client);
1514
- sessions.set(sessionId, session);
1515
- ws.send(JSON.stringify({ type: "config", ...opts.readyConfig }));
1516
- void session.start();
1517
- log.info("Session ready", { ...ctx, sid });
1518
- }
1519
- if (ws.readyState === 1) {
1520
- onOpen();
1521
- } else {
1522
- ws.addEventListener("open", onOpen);
1523
- }
1524
- ws.addEventListener("message", (event) => {
1525
- if (!session)
1526
- return;
1527
- const { data } = event;
1528
- if (handleBinaryAudio(data, session, log, ctx, sid))
1529
- return;
1530
- handleTextMessage(data, session, log, ctx, sid);
1531
- });
1532
- ws.addEventListener("close", () => {
1533
- log.info("Session disconnected", { ...ctx, sid });
1534
- if (session) {
1535
- void session.stop().finally(() => {
1536
- sessions.delete(sessionId);
1537
- });
1538
- }
1539
- opts.onClose?.();
1540
- });
1541
- ws.addEventListener("error", (event) => {
1542
- const msg = event instanceof ErrorEvent ? event.message : "WebSocket error";
1543
- log.error("WebSocket error", { ...ctx, sid, error: msg });
1544
- });
1545
- }
1546
- var MAX_AUDIO_CHUNK_BYTES;
1547
- var init_ws_handler = __esm({
1548
- "dist/sdk/ws_handler.js"() {
1947
+ var _internals2;
1948
+ var init_session = __esm({
1949
+ "sdk/session.ts"() {
1549
1950
  "use strict";
1550
1951
  init_protocol();
1551
1952
  init_runtime();
1552
- MAX_AUDIO_CHUNK_BYTES = 1048576;
1953
+ init_s2s();
1954
+ init_system_prompt();
1955
+ _internals2 = {
1956
+ connectS2s
1957
+ };
1553
1958
  }
1554
1959
  });
1555
1960
 
1556
- // dist/sdk/winterc_server.js
1557
- function createWintercServer(options) {
1558
- const { agent, env, kv = createMemoryKv(), vector = createMemoryVectorStore(), vectorSearch, clientHtml, logger = consoleLogger, metrics = noopMetrics, s2sConfig = DEFAULT_S2S_CONFIG } = options;
1559
- const executor = createDirectExecutor({
1560
- agent,
1561
- env,
1562
- kv,
1563
- vector,
1564
- ...vectorSearch ? { vectorSearch } : {},
1565
- createWebSocket: options.createWebSocket,
1566
- logger,
1567
- metrics,
1568
- s2sConfig
1569
- });
1570
- const sessions = /* @__PURE__ */ new Map();
1571
- const readyConfig = {
1572
- protocolVersion: PROTOCOL_VERSION,
1573
- audioFormat: AUDIO_FORMAT,
1574
- sampleRate: s2sConfig.inputSampleRate,
1575
- ttsSampleRate: s2sConfig.outputSampleRate
1576
- };
1961
+ // sdk/vector.ts
1962
+ function createMemoryVectorStore() {
1963
+ const store = /* @__PURE__ */ new Map();
1577
1964
  return {
1578
- async fetch(request) {
1579
- const url = new URL(request.url);
1580
- if (url.pathname === "/health") {
1581
- return new Response(JSON.stringify({ status: "ok", name: agent.name }), {
1582
- headers: { "Content-Type": "application/json" }
1583
- });
1584
- }
1585
- if (url.pathname === "/" && clientHtml) {
1586
- return new Response(clientHtml, {
1587
- headers: { "Content-Type": "text/html" }
1588
- });
1589
- }
1590
- if (url.pathname === "/") {
1591
- return new Response(`<!DOCTYPE html><html><body><h1>${agent.name}</h1><p>Agent server running.</p></body></html>`, { headers: { "Content-Type": "text/html" } });
1592
- }
1593
- return new Response("Not Found", { status: 404 });
1965
+ upsert(id, data, metadata) {
1966
+ store.set(id, { data, metadata });
1967
+ return Promise.resolve();
1594
1968
  },
1595
- handleWebSocket(ws, wsOpts) {
1596
- wireSessionSocket(ws, {
1597
- sessions,
1598
- createSession: (sid, client) => executor.createSession({
1599
- id: sid,
1600
- agent: agent.name,
1601
- client,
1602
- skipGreeting: wsOpts?.skipGreeting ?? false
1603
- }),
1604
- readyConfig,
1605
- logger
1606
- });
1969
+ async query(text, options) {
1970
+ const topK = options?.topK ?? 10;
1971
+ const query = text.toLowerCase();
1972
+ const words = query.split(/\s+/).filter(Boolean);
1973
+ const results = [];
1974
+ let i = 0;
1975
+ for (const [id, entry] of store) {
1976
+ if (++i % 500 === 0) await new Promise((r) => setTimeout(r, 0));
1977
+ const data = entry.data.toLowerCase();
1978
+ const matches = words.filter((w) => data.includes(w)).length;
1979
+ if (matches > 0) {
1980
+ results.push({
1981
+ id,
1982
+ score: matches / Math.max(words.length, 1),
1983
+ data: entry.data,
1984
+ metadata: entry.metadata
1985
+ });
1986
+ }
1987
+ }
1988
+ results.sort((a, b) => b.score - a.score);
1989
+ return results.slice(0, topK);
1607
1990
  },
1608
- async close() {
1609
- for (const session of sessions.values()) {
1610
- await session.stop();
1991
+ remove(ids) {
1992
+ const idArray = Array.isArray(ids) ? ids : [ids];
1993
+ for (const id of idArray) {
1994
+ store.delete(id);
1611
1995
  }
1612
- sessions.clear();
1996
+ return Promise.resolve();
1613
1997
  }
1614
1998
  };
1615
1999
  }
1616
- var init_winterc_server = __esm({
1617
- "dist/sdk/winterc_server.js"() {
2000
+ var init_vector = __esm({
2001
+ "sdk/vector.ts"() {
1618
2002
  "use strict";
1619
- init_direct_executor();
1620
- init_kv();
1621
- init_protocol();
1622
- init_runtime();
1623
- init_vector();
1624
- init_ws_handler();
1625
2003
  }
1626
2004
  });
1627
2005
 
1628
- // dist/sdk/server.js
1629
- var server_exports = {};
1630
- __export(server_exports, {
1631
- createServer: () => createServer
1632
- });
1633
- async function loadWsFactory() {
2006
+ // sdk/worker_entry.ts
2007
+ function buildToolContext(opts) {
2008
+ const { env, sessionId, state, kv, vector, messages } = opts;
2009
+ return {
2010
+ sessionId: sessionId ?? "",
2011
+ env: { ...env },
2012
+ abortSignal: AbortSignal.timeout(TOOL_HANDLER_TIMEOUT),
2013
+ state: state ?? {},
2014
+ get kv() {
2015
+ if (!kv) throw new Error("KV not available");
2016
+ return kv;
2017
+ },
2018
+ get vector() {
2019
+ if (!vector) throw new Error("Vector store not available");
2020
+ return vector;
2021
+ },
2022
+ messages: messages ?? []
2023
+ };
2024
+ }
2025
+ async function executeToolCall(name, args, options) {
2026
+ const { tool } = options;
2027
+ const schema = tool.parameters ?? EMPTY_PARAMS;
2028
+ const parsed = schema.safeParse(args);
2029
+ if (!parsed.success) {
2030
+ const issues = (parsed.error?.issues ?? []).map((i) => `${i.path.map(String).join(".")}: ${i.message}`).join(", ");
2031
+ return `Error: Invalid arguments for tool "${name}": ${issues}`;
2032
+ }
1634
2033
  try {
1635
- const mod = await import("ws");
1636
- const WS = mod.default ?? mod;
1637
- return (url, opts) => new WS(url, { headers: opts.headers });
1638
- } catch {
1639
- throw new Error("WebSocket factory not provided and `ws` package not found. Install `ws` (`npm install ws`) or pass `createWebSocket` option.");
2034
+ const ctx = buildToolContext(options);
2035
+ await yieldTick();
2036
+ const result = await Promise.resolve(tool.execute(parsed.data, ctx));
2037
+ await yieldTick();
2038
+ if (result == null) return "null";
2039
+ return typeof result === "string" ? result : JSON.stringify(result);
2040
+ } catch (err) {
2041
+ if (err instanceof DOMException && err.name === "TimeoutError") {
2042
+ console.warn(`[tool-executor] Tool execution timed out: ${name}`);
2043
+ return `Error: Tool "${name}" timed out after ${TOOL_HANDLER_TIMEOUT}ms`;
2044
+ }
2045
+ console.warn(`[tool-executor] Tool execution failed: ${name}`, err);
2046
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
1640
2047
  }
1641
2048
  }
1642
- function resolveEnv(env) {
1643
- const resolved = {};
1644
- for (const [key, value] of Object.entries(env)) {
1645
- if (value !== void 0)
1646
- resolved[key] = value;
2049
+ var yieldTick, TOOL_HANDLER_TIMEOUT;
2050
+ var init_worker_entry = __esm({
2051
+ "sdk/worker_entry.ts"() {
2052
+ "use strict";
2053
+ init_internal_types();
2054
+ yieldTick = () => new Promise((r) => setTimeout(r, 0));
2055
+ TOOL_HANDLER_TIMEOUT = 3e4;
1647
2056
  }
1648
- return resolved;
2057
+ });
2058
+
2059
+ // sdk/direct_executor.ts
2060
+ function buildAgentConfig(agent) {
2061
+ const config = {
2062
+ name: agent.name,
2063
+ instructions: agent.instructions,
2064
+ greeting: agent.greeting,
2065
+ voice: agent.voice
2066
+ };
2067
+ if (agent.sttPrompt !== void 0) config.sttPrompt = agent.sttPrompt;
2068
+ if (typeof agent.maxSteps !== "function") config.maxSteps = agent.maxSteps;
2069
+ if (agent.toolChoice !== void 0) config.toolChoice = agent.toolChoice;
2070
+ if (agent.builtinTools) config.builtinTools = [...agent.builtinTools];
2071
+ if (agent.activeTools) config.activeTools = [...agent.activeTools];
2072
+ return config;
1649
2073
  }
1650
- function createServer(options) {
1651
- const { agent, kv, clientHtml, logger = consoleLogger, s2sConfig = DEFAULT_S2S_CONFIG } = options;
1652
- const env = resolveEnv(options.env ?? (typeof process !== "undefined" ? process.env : {}));
1653
- let wsFactory = options.createWebSocket ?? null;
1654
- async function getWsFactory() {
1655
- if (!wsFactory) {
1656
- wsFactory = await loadWsFactory();
2074
+ function createDirectExecutor(opts) {
2075
+ const {
2076
+ agent,
2077
+ env,
2078
+ kv = createMemoryKv(),
2079
+ vector = createMemoryVectorStore(),
2080
+ vectorSearch,
2081
+ createWebSocket,
2082
+ logger = consoleLogger,
2083
+ metrics = noopMetrics,
2084
+ s2sConfig = DEFAULT_S2S_CONFIG
2085
+ } = opts;
2086
+ const agentConfig = buildAgentConfig(agent);
2087
+ const builtinDefs = getBuiltinToolDefs(
2088
+ agent.builtinTools ?? [],
2089
+ vectorSearch ? { vectorSearch } : void 0
2090
+ );
2091
+ const allTools = {
2092
+ ...builtinDefs,
2093
+ ...agent.tools
2094
+ };
2095
+ const customSchemas = agentToolsToSchemas(agent.tools ?? {});
2096
+ const builtinSchemas = getBuiltinToolSchemas(agent.builtinTools ?? []);
2097
+ const toolSchemas = [...customSchemas, ...builtinSchemas];
2098
+ const sessionState = /* @__PURE__ */ new Map();
2099
+ const frozenEnv = Object.freeze({ ...env });
2100
+ function getState(sessionId) {
2101
+ if (!sessionState.has(sessionId) && agent.state) {
2102
+ sessionState.set(sessionId, agent.state());
1657
2103
  }
1658
- return wsFactory;
2104
+ return sessionState.get(sessionId) ?? {};
1659
2105
  }
1660
- let winterc = null;
1661
- function getWinterc() {
1662
- if (!winterc) {
1663
- winterc = createWintercServer({
1664
- agent,
1665
- env,
1666
- ...kv ? { kv } : {},
1667
- createWebSocket: wsFactory ?? (() => {
1668
- throw new Error("WebSocket factory not loaded");
1669
- }),
1670
- ...clientHtml !== void 0 ? { clientHtml } : {},
1671
- logger,
1672
- s2sConfig
1673
- });
1674
- }
1675
- return winterc;
2106
+ function makeHookContext(sessionId) {
2107
+ return {
2108
+ sessionId,
2109
+ env: frozenEnv,
2110
+ state: getState(sessionId),
2111
+ get kv() {
2112
+ return kv;
2113
+ },
2114
+ get vector() {
2115
+ return vector;
2116
+ }
2117
+ };
1676
2118
  }
1677
- let serverHandle = null;
1678
- return {
1679
- fetch(request) {
1680
- return getWinterc().fetch(request);
2119
+ const executeTool = async (name, args, sessionId, messages) => {
2120
+ const tool = allTools[name];
2121
+ if (!tool) return JSON.stringify({ error: `Unknown tool: ${name}` });
2122
+ return executeToolCall(name, args, {
2123
+ tool,
2124
+ env: frozenEnv,
2125
+ sessionId,
2126
+ state: getState(sessionId ?? ""),
2127
+ kv,
2128
+ vector,
2129
+ messages
2130
+ });
2131
+ };
2132
+ const hookInvoker = {
2133
+ async onConnect(sessionId) {
2134
+ await agent.onConnect?.(makeHookContext(sessionId));
1681
2135
  },
1682
- async listen(port = 3e3) {
1683
- await getWsFactory();
1684
- const http = await import("node:http");
1685
- const nodeServer = http.createServer(async (req, res) => {
1686
- await nodeHttpHandler(req, res, port, getWinterc);
1687
- });
1688
- attachWsUpgrade(nodeServer, port, getWinterc, logger);
1689
- await new Promise((resolve) => {
1690
- nodeServer.listen(port, () => {
1691
- logger.info(`Agent "${agent.name}" listening on http://localhost:${port}`);
1692
- resolve();
1693
- });
1694
- });
1695
- serverHandle = {
1696
- async shutdown() {
1697
- await new Promise((resolve, reject) => {
1698
- nodeServer.close((err) => err ? reject(err) : resolve());
1699
- });
1700
- }
1701
- };
2136
+ async onDisconnect(sessionId) {
2137
+ await agent.onDisconnect?.(makeHookContext(sessionId));
2138
+ sessionState.delete(sessionId);
1702
2139
  },
1703
- async close() {
1704
- await winterc?.close();
1705
- await serverHandle?.shutdown();
2140
+ async onTurn(sessionId, text) {
2141
+ await agent.onTurn?.(text, makeHookContext(sessionId));
2142
+ },
2143
+ async onError(sessionId, error) {
2144
+ await agent.onError?.(new Error(error.message), makeHookContext(sessionId));
2145
+ },
2146
+ async onStep(sessionId, step) {
2147
+ await agent.onStep?.(step, makeHookContext(sessionId));
2148
+ },
2149
+ async resolveTurnConfig(sessionId) {
2150
+ const ctx = makeHookContext(sessionId);
2151
+ let maxSteps;
2152
+ let activeTools;
2153
+ if (typeof agent.maxSteps === "function") {
2154
+ maxSteps = await agent.maxSteps(ctx) ?? void 0;
2155
+ }
2156
+ if (agent.onBeforeStep) {
2157
+ const result = await agent.onBeforeStep(0, ctx);
2158
+ activeTools = result?.activeTools;
2159
+ }
2160
+ if (maxSteps === void 0 && activeTools === void 0) return null;
2161
+ const config = {};
2162
+ if (maxSteps !== void 0) config.maxSteps = maxSteps;
2163
+ if (activeTools !== void 0) config.activeTools = activeTools;
2164
+ return config;
1706
2165
  }
1707
2166
  };
1708
- }
1709
- async function nodeHttpHandler(req, res, port, getWinterc) {
1710
- try {
1711
- const protocol = req.socket.encrypted ? "https" : "http";
1712
- const host = req.headers.host ?? `localhost:${port}`;
1713
- const url = new URL(req.url ?? "/", `${protocol}://${host}`);
1714
- const headers = new Headers();
1715
- for (const [key, val] of Object.entries(req.headers)) {
1716
- if (val)
1717
- headers.set(key, Array.isArray(val) ? val[0] ?? "" : val);
2167
+ function createSession(sessionOpts) {
2168
+ if (!createWebSocket) {
2169
+ throw new Error("createWebSocket not provided \u2014 pass it in DirectExecutorOptions");
1718
2170
  }
1719
- const request = new Request(url, { method: req.method ?? "GET", headers });
1720
- const response = await getWinterc().fetch(request);
1721
- res.writeHead(response.status, Object.fromEntries(response.headers));
1722
- res.end(await response.text());
1723
- } catch (err) {
1724
- res.writeHead(500);
1725
- res.end(err instanceof Error ? err.message : "Internal Server Error");
1726
- }
1727
- }
1728
- function attachWsUpgrade(nodeServer, port, getWinterc, logger) {
1729
- import("ws").then((wsMod) => {
1730
- const WSServer = wsMod.WebSocketServer;
1731
- if (!WSServer)
1732
- return;
1733
- const wss = new WSServer({ noServer: true });
1734
- nodeServer.on("upgrade", (req, socket, head) => {
1735
- wss.handleUpgrade(req, socket, head, (ws) => {
1736
- const reqUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
1737
- getWinterc().handleWebSocket(ws, {
1738
- skipGreeting: reqUrl.searchParams.has("resume")
1739
- });
1740
- });
2171
+ const apiKey = frozenEnv.ASSEMBLYAI_API_KEY ?? "";
2172
+ return createS2sSession({
2173
+ id: sessionOpts.id,
2174
+ agent: sessionOpts.agent,
2175
+ client: sessionOpts.client,
2176
+ agentConfig,
2177
+ toolSchemas,
2178
+ apiKey,
2179
+ s2sConfig,
2180
+ executeTool,
2181
+ createWebSocket,
2182
+ hookInvoker,
2183
+ skipGreeting: sessionOpts.skipGreeting ?? false,
2184
+ logger,
2185
+ metrics
1741
2186
  });
1742
- }).catch(() => {
1743
- logger.warn("ws package not available for Node.js WebSocket upgrade");
1744
- });
2187
+ }
2188
+ return { executeTool, hookInvoker, toolSchemas, createSession };
1745
2189
  }
1746
- var init_server = __esm({
1747
- "dist/sdk/server.js"() {
2190
+ var init_direct_executor = __esm({
2191
+ "sdk/direct_executor.ts"() {
2192
+ "use strict";
2193
+ init_internal_types();
2194
+ init_builtin_tools();
2195
+ init_kv();
1748
2196
  init_runtime();
1749
- init_winterc_server();
2197
+ init_session();
2198
+ init_vector();
2199
+ init_worker_entry();
1750
2200
  }
1751
2201
  });
1752
2202
 
1753
- // cli/cli.ts
1754
- import { readFileSync } from "node:fs";
1755
- import path11 from "node:path";
1756
- import { fileURLToPath as fileURLToPath3 } from "node:url";
1757
- import minimist7 from "minimist";
1758
-
1759
- // cli/_help.ts
1760
- init_colors();
1761
- import chalk2 from "chalk";
1762
- function rootHelp(version) {
1763
- const lines = [];
1764
- lines.push("");
1765
- lines.push(
1766
- ` ${primary(chalk2.bold(" \u2584\u2580\u2588 \u2584\u2580\u2588 \u2588"))} ${chalk2.dim("Voice agent development kit")}`
1767
- );
1768
- lines.push(` ${primary(chalk2.bold(" \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588"))} ${primary(`v${version}`)}`);
1769
- lines.push("");
1770
- lines.push(
1771
- ` ${chalk2.bold(interactive("Usage"))} ${primary("aai")} ${chalk2.dim("<command> [options]")}`
1772
- );
1773
- lines.push("");
1774
- lines.push(` ${chalk2.bold(interactive("Commands"))}`);
1775
- lines.push("");
1776
- const cmds = [
1777
- ["init", "[dir]", "Scaffold a new agent project"],
1778
- ["dev", "", "Start a local development server"],
1779
- ["deploy", "", "Bundle and deploy to production"],
1780
- ["start", "", "Start production server from build"],
1781
- ["secret", "<cmd>", "Manage secrets"],
1782
- ["rag", "<url>", "Ingest a site into the vector store"]
1783
- ];
1784
- for (const [name, args, desc] of cmds) {
1785
- const nameStr = interactive(name.padEnd(8));
1786
- const argsStr = args ? primary(args.padEnd(6)) : " ";
1787
- lines.push(` ${nameStr} ${argsStr} ${chalk2.dim(desc)}`);
1788
- }
1789
- lines.push("");
1790
- lines.push(` ${chalk2.bold(interactive("Options"))}`);
1791
- lines.push("");
1792
- lines.push(
1793
- ` ${interactive("-h")}${chalk2.dim(",")} ${interactive("--help")} ${chalk2.dim(
1794
- "Show this help"
1795
- )}`
1796
- );
1797
- lines.push(
1798
- ` ${interactive("-V")}${chalk2.dim(",")} ${interactive("--version")} ${chalk2.dim(
1799
- "Show the version number"
1800
- )}`
1801
- );
1802
- lines.push("");
1803
- lines.push(` ${chalk2.bold(interactive("Getting started"))}`);
1804
- lines.push("");
1805
- lines.push(
1806
- ` ${chalk2.dim("$")} ${primary("aai init")} ${interactive("my-agent")} ${chalk2.dim(
1807
- "Create a new agent"
1808
- )}`
1809
- );
1810
- lines.push(` ${chalk2.dim("$")} ${primary("cd my-agent")}`);
1811
- lines.push(
1812
- ` ${chalk2.dim("$")} ${primary("aai deploy")} ${chalk2.dim("Deploy to production")}`
1813
- );
1814
- lines.push("");
1815
- return lines.join("\n");
2203
+ // sdk/ws_handler.ts
2204
+ function isValidAudioChunk(data) {
2205
+ return data.byteLength > 0 && data.byteLength <= MAX_AUDIO_CHUNK_BYTES && data.byteLength % 2 === 0;
1816
2206
  }
1817
- function subcommandHelp(cmd, version) {
1818
- const lines = [];
1819
- lines.push("");
1820
- lines.push(
1821
- ` ${primary(chalk2.bold("aai"))} ${interactive(chalk2.bold(cmd.name))}${version ? chalk2.dim(` v${version}`) : ""}`
1822
- );
1823
- lines.push(` ${chalk2.dim(cmd.description)}`);
1824
- lines.push("");
1825
- if (cmd.args && cmd.args.length > 0) {
1826
- lines.push(` ${chalk2.bold(interactive("Arguments"))}`);
1827
- lines.push("");
1828
- for (const arg of cmd.args) {
1829
- const label = arg.optional ? primary(`[${arg.name}]`) : primary(`<${arg.name}>`);
1830
- lines.push(` ${label}`);
2207
+ function createClientSink(ws) {
2208
+ function safeSend(data) {
2209
+ try {
2210
+ if (ws.readyState === 1) ws.send(data);
2211
+ } catch {
1831
2212
  }
1832
- lines.push("");
1833
2213
  }
1834
- const visibleOptions = (cmd.options ?? []).filter((o) => !o.hidden);
1835
- if (visibleOptions.length > 0) {
1836
- lines.push(` ${chalk2.bold(interactive("Options"))}`);
1837
- lines.push("");
1838
- for (const opt of visibleOptions) {
1839
- lines.push(` ${interactive(opt.flags)}`);
1840
- lines.push(` ${chalk2.dim(opt.description)}`);
2214
+ return {
2215
+ get open() {
2216
+ return ws.readyState === 1;
2217
+ },
2218
+ event(e) {
2219
+ safeSend(JSON.stringify(e));
2220
+ },
2221
+ playAudioChunk(chunk) {
2222
+ safeSend(chunk);
2223
+ },
2224
+ playAudioDone() {
2225
+ safeSend(JSON.stringify({ type: "audio_done" }));
1841
2226
  }
1842
- lines.push(` ${interactive("-h")}${chalk2.dim(",")} ${interactive("--help")}`);
1843
- lines.push(` ${chalk2.dim("Show this help")}`);
1844
- lines.push("");
1845
- }
1846
- return lines.join("\n");
1847
- }
1848
-
1849
- // cli/cli.ts
1850
- init_output();
1851
-
1852
- // cli/deploy.tsx
1853
- import path6 from "node:path";
1854
- import minimist2 from "minimist";
1855
-
1856
- // cli/_discover.ts
1857
- import fs from "node:fs/promises";
1858
- import path from "node:path";
1859
- import { fileURLToPath } from "node:url";
1860
- import { humanId } from "human-id";
1861
-
1862
- // cli/_exec.ts
1863
- import { execFile } from "node:child_process";
1864
- import { promisify } from "node:util";
1865
- var execFileAsync = promisify(execFile);
1866
-
1867
- // cli/_discover.ts
1868
- init_output();
1869
-
1870
- // cli/_prompts.tsx
1871
- import { ConfirmInput, PasswordInput, Select } from "@inkjs/ui";
1872
- import { Box, render, Text } from "ink";
1873
- import { jsx, jsxs } from "react/jsx-runtime";
1874
- async function askPassword(message) {
1875
- return new Promise((resolve) => {
1876
- const app = render(
1877
- /* @__PURE__ */ jsxs(Box, { children: [
1878
- /* @__PURE__ */ jsxs(Text, { children: [
1879
- message,
1880
- ": "
1881
- ] }),
1882
- /* @__PURE__ */ jsx(
1883
- PasswordInput,
1884
- {
1885
- onSubmit: (value) => {
1886
- resolve(value);
1887
- app.unmount();
1888
- }
1889
- }
1890
- )
1891
- ] })
1892
- );
1893
- });
1894
- }
1895
- async function askSelect(message, choices) {
1896
- return new Promise((resolve) => {
1897
- const app = render(
1898
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1899
- /* @__PURE__ */ jsx(Text, { children: message }),
1900
- /* @__PURE__ */ jsx(
1901
- Select,
1902
- {
1903
- options: choices,
1904
- visibleOptionCount: choices.length,
1905
- onChange: (value) => {
1906
- resolve(value);
1907
- app.unmount();
1908
- }
1909
- }
1910
- )
1911
- ] })
1912
- );
1913
- });
1914
- }
1915
-
1916
- // cli/_discover.ts
1917
- function isDevMode() {
1918
- const script = process.argv[1] ?? "";
1919
- return script.endsWith(".ts") || script.endsWith(".tsx");
1920
- }
1921
- function generateSlug() {
1922
- return humanId({ separator: "-", capitalize: false });
2227
+ };
1923
2228
  }
1924
- var CONFIG_DIR = path.join(process.env.HOME ?? process.env.USERPROFILE ?? ".", ".config", "aai");
1925
- var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
1926
- async function readAuthConfig() {
1927
- try {
1928
- return JSON.parse(await fs.readFile(CONFIG_FILE, "utf-8"));
1929
- } catch {
1930
- return {};
1931
- }
2229
+ function isBinaryData(data) {
2230
+ return globalThis.Buffer?.isBuffer(data) || data instanceof ArrayBuffer || data instanceof Uint8Array;
1932
2231
  }
1933
- async function writeAuthConfig(config) {
1934
- await fs.mkdir(CONFIG_DIR, { recursive: true });
1935
- await fs.writeFile(CONFIG_FILE, `${JSON.stringify(config, null, 2)}
1936
- `);
1937
- if (process.platform !== "win32") {
1938
- await fs.chmod(CONFIG_FILE, 384);
1939
- }
2232
+ function toUint8Array(data) {
2233
+ if (data instanceof Uint8Array) return data;
2234
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
2235
+ const buf = data;
2236
+ return new Uint8Array(buf.buffer ?? data, buf.byteOffset ?? 0, buf.byteLength);
1940
2237
  }
1941
- async function getApiKey() {
1942
- const config = await readAuthConfig();
1943
- if (config.assemblyai_api_key) {
1944
- process.env.ASSEMBLYAI_API_KEY = config.assemblyai_api_key;
1945
- return config.assemblyai_api_key;
1946
- }
1947
- step("Setup", "AssemblyAI API key required for speech-to-text");
1948
- console.log("Get one at https://www.assemblyai.com/dashboard/signup\n");
1949
- let key;
1950
- while (!key) {
1951
- key = await askPassword("ASSEMBLYAI_API_KEY");
2238
+ function handleBinaryAudio(data, session, log, ctx, sid) {
2239
+ if (!isBinaryData(data)) return false;
2240
+ const chunk = toUint8Array(data);
2241
+ if (!isValidAudioChunk(chunk)) {
2242
+ log.warn("Invalid audio chunk, dropping", {
2243
+ ...ctx,
2244
+ sid,
2245
+ bytes: chunk.byteLength,
2246
+ aligned: chunk.byteLength % 2 === 0
2247
+ });
2248
+ return true;
1952
2249
  }
1953
- config.assemblyai_api_key = key;
1954
- process.env.ASSEMBLYAI_API_KEY = key;
1955
- await writeAuthConfig(config);
1956
- step("Saved", CONFIG_FILE);
1957
- return key;
2250
+ session.onAudio(chunk);
2251
+ return true;
1958
2252
  }
1959
- async function readProjectConfig(agentDir) {
2253
+ function handleTextMessage(data, session, log, ctx, sid) {
2254
+ if (typeof data !== "string") return;
2255
+ let json;
1960
2256
  try {
1961
- return JSON.parse(await fs.readFile(path.join(agentDir, ".aai", "project.json"), "utf-8"));
2257
+ json = JSON.parse(data);
1962
2258
  } catch {
1963
- return null;
2259
+ log.warn("Invalid JSON from client", { ...ctx, sid });
2260
+ return;
1964
2261
  }
1965
- }
1966
- async function writeProjectConfig(agentDir, data) {
1967
- const aaiDir = path.join(agentDir, ".aai");
1968
- await fs.mkdir(aaiDir, { recursive: true });
1969
- await fs.writeFile(path.join(aaiDir, "project.json"), `${JSON.stringify(data, null, 2)}
1970
- `);
1971
- }
1972
- var DEFAULT_SERVER = "https://aai-agent.fly.dev";
1973
- async function fileExists(p) {
1974
- try {
1975
- await fs.access(p);
1976
- return true;
1977
- } catch {
1978
- return false;
2262
+ const parsed = ClientMessageSchema.safeParse(json);
2263
+ if (!parsed.success) {
2264
+ log.warn("Invalid client message", { ...ctx, sid, error: parsed.error.message });
2265
+ return;
2266
+ }
2267
+ const msg = parsed.data;
2268
+ switch (msg.type) {
2269
+ case "audio_ready":
2270
+ session.onAudioReady();
2271
+ break;
2272
+ case "cancel":
2273
+ session.onCancel();
2274
+ break;
2275
+ case "reset":
2276
+ session.onReset();
2277
+ break;
2278
+ case "history":
2279
+ session.onHistory(msg.messages);
2280
+ break;
1979
2281
  }
1980
2282
  }
1981
- async function loadAgent(dir) {
1982
- const hasAgentTs = await fileExists(path.join(dir, "agent.ts"));
1983
- if (!hasAgentTs) return null;
1984
- const config = await readProjectConfig(dir);
1985
- const slug = config?.slug ?? generateSlug();
1986
- const clientEntry = await fileExists(path.join(dir, "client.tsx")) ? path.join(dir, "client.tsx") : "";
1987
- return {
1988
- slug,
1989
- dir,
1990
- entryPoint: path.join(dir, "agent.ts"),
1991
- clientEntry
1992
- };
1993
- }
1994
- async function ensureClaudeMd(targetDir) {
1995
- const claudePath = path.join(targetDir, "CLAUDE.md");
1996
- const cliDir2 = path.dirname(fileURLToPath(import.meta.url));
1997
- const srcPath = path.join(cliDir2, "..", "templates", "_shared", "CLAUDE.md");
1998
- const srcContent = await fs.readFile(srcPath, "utf-8");
1999
- let existing = "";
2000
- try {
2001
- existing = await fs.readFile(claudePath, "utf-8");
2002
- } catch {
2283
+ function wireSessionSocket(ws, opts) {
2284
+ const { sessions, logger: log = consoleLogger } = opts;
2285
+ const sessionId = crypto.randomUUID();
2286
+ const sid = sessionId.slice(0, 8);
2287
+ const ctx = opts.logContext ?? {};
2288
+ let session = null;
2289
+ function onOpen() {
2290
+ opts.onOpen?.();
2291
+ log.info("Session connected", { ...ctx, sid });
2292
+ const client = createClientSink(ws);
2293
+ session = opts.createSession(sessionId, client);
2294
+ sessions.set(sessionId, session);
2295
+ ws.send(JSON.stringify({ type: "config", ...opts.readyConfig }));
2296
+ void session.start();
2297
+ log.info("Session ready", { ...ctx, sid });
2003
2298
  }
2004
- if (existing !== srcContent) {
2005
- await fs.writeFile(claudePath, srcContent);
2006
- step(existing ? "Updated" : "Wrote", "CLAUDE.md \u2014 aai agent API reference");
2299
+ if (ws.readyState === 1) {
2300
+ onOpen();
2301
+ } else {
2302
+ ws.addEventListener("open", onOpen);
2007
2303
  }
2008
- }
2009
- async function ensureDependencies(targetDir) {
2010
- if (!await fileExists(path.join(targetDir, "node_modules"))) {
2011
- step("Install", "dependencies");
2012
- try {
2013
- await execFileAsync("npm", ["install"], { cwd: targetDir });
2014
- } catch {
2015
- step("Skip", "npm install failed");
2304
+ ws.addEventListener("message", (event) => {
2305
+ if (!session) return;
2306
+ const { data } = event;
2307
+ if (handleBinaryAudio(data, session, log, ctx, sid)) return;
2308
+ handleTextMessage(data, session, log, ctx, sid);
2309
+ });
2310
+ ws.addEventListener("close", () => {
2311
+ log.info("Session disconnected", { ...ctx, sid });
2312
+ if (session) {
2313
+ void session.stop().finally(() => {
2314
+ sessions.delete(sessionId);
2315
+ });
2016
2316
  }
2017
- }
2018
- }
2019
-
2020
- // cli/_deploy.ts
2021
- init_output();
2022
- var _internals = {
2023
- fetch: globalThis.fetch.bind(globalThis)
2024
- };
2025
- async function attemptDeploy(url, slug, apiKey, env, worker, html) {
2026
- return await _internals.fetch(`${url}/${slug}/deploy`, {
2027
- method: "POST",
2028
- headers: {
2029
- "Content-Type": "application/json",
2030
- Authorization: `Bearer ${apiKey}`
2031
- },
2032
- body: JSON.stringify({
2033
- env,
2034
- worker,
2035
- html
2036
- })
2317
+ opts.onClose?.();
2318
+ });
2319
+ ws.addEventListener("error", (event) => {
2320
+ const msg = event instanceof ErrorEvent ? event.message : "WebSocket error";
2321
+ log.error("WebSocket error", { ...ctx, sid, error: msg });
2037
2322
  });
2038
2323
  }
2039
- var MAX_RETRIES = 20;
2040
- async function runDeploy(opts) {
2041
- const worker = opts.bundle.worker;
2042
- const html = opts.bundle.html;
2043
- let slug = opts.slug;
2044
- if (opts.dryRun) {
2045
- stepInfo("Dry run", "would deploy:");
2046
- info(`${slug} -> ${opts.url}/${slug}`);
2047
- return { slug };
2048
- }
2049
- for (let i = 0; i < MAX_RETRIES; i++) {
2050
- const resp = await attemptDeploy(opts.url, slug, opts.apiKey, opts.env, worker, html);
2051
- if (resp.ok) {
2052
- step("Deploy", `${slug} -> ${opts.url}/${slug}`);
2053
- try {
2054
- const healthResp = await _internals.fetch(`${opts.url}/${slug}/health`);
2055
- const ok = healthResp.ok && (await healthResp.json()).status === "ok";
2056
- if (ok) {
2057
- step("Ready", slug);
2058
- } else {
2059
- warn(`${slug} deployed but health check failed -- check for runtime errors`);
2060
- }
2061
- } catch {
2062
- }
2063
- return { slug };
2064
- }
2065
- if (resp.status === 403) {
2066
- const text2 = await resp.text();
2067
- if (text2.includes("Slug")) {
2068
- const next = generateSlug();
2069
- step("Retry", `slug "${slug}" taken, trying "${next}"`);
2070
- slug = next;
2071
- continue;
2072
- }
2073
- }
2074
- const text = await resp.text();
2075
- throw new Error(`deploy failed (${resp.status}): ${text}`);
2324
+ var MAX_AUDIO_CHUNK_BYTES;
2325
+ var init_ws_handler = __esm({
2326
+ "sdk/ws_handler.ts"() {
2327
+ "use strict";
2328
+ init_protocol();
2329
+ init_runtime();
2330
+ MAX_AUDIO_CHUNK_BYTES = 1048576;
2076
2331
  }
2077
- throw new Error(`deploy failed: could not find available slug after ${MAX_RETRIES} attempts`);
2078
- }
2332
+ });
2079
2333
 
2080
- // cli/_ink.tsx
2081
- import { Spinner } from "@inkjs/ui";
2082
- import { Box as Box2, render as render2, Static, Text as Text2, useApp } from "ink";
2083
- import React, { useRef, useState } from "react";
2084
- import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2085
- var PAD2 = 9;
2086
- var PRIMARY = "#fab283";
2087
- var INTERACTIVE = "#56b6c2";
2088
- var ERROR_COLOR = "#e06c75";
2089
- var WARNING_COLOR = "#f5a742";
2090
- function Step({ action, msg }) {
2091
- return /* @__PURE__ */ jsxs2(Text2, { children: [
2092
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: PRIMARY, children: action.padStart(PAD2) }),
2093
- /* @__PURE__ */ jsxs2(Text2, { children: [
2094
- " ",
2095
- msg
2096
- ] })
2097
- ] });
2098
- }
2099
- function StepInfo({ action, msg }) {
2100
- return /* @__PURE__ */ jsxs2(Text2, { children: [
2101
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: INTERACTIVE, children: action.padStart(PAD2) }),
2102
- /* @__PURE__ */ jsxs2(Text2, { children: [
2103
- " ",
2104
- msg
2105
- ] })
2106
- ] });
2107
- }
2108
- function Info({ msg }) {
2109
- return /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
2110
- " ".repeat(PAD2 + 1),
2111
- msg
2112
- ] });
2113
- }
2114
- function Detail({ msg }) {
2115
- return /* @__PURE__ */ jsxs2(Text2, { children: [
2116
- " ".repeat(PAD2 + 1),
2117
- msg
2118
- ] });
2119
- }
2120
- function Warn({ msg }) {
2121
- return /* @__PURE__ */ jsxs2(Text2, { children: [
2122
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: WARNING_COLOR, children: "warning".padStart(PAD2) }),
2123
- /* @__PURE__ */ jsxs2(Text2, { children: [
2124
- " ",
2125
- msg
2126
- ] })
2127
- ] });
2128
- }
2129
- function ErrorLine({ msg }) {
2130
- return /* @__PURE__ */ jsxs2(Text2, { children: [
2131
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: ERROR_COLOR, children: "error" }),
2132
- /* @__PURE__ */ jsxs2(Text2, { children: [
2133
- ": ",
2134
- msg
2135
- ] })
2136
- ] });
2137
- }
2138
- function StepLog({ items }) {
2139
- return /* @__PURE__ */ jsx2(Static, { items, children: (item) => /* @__PURE__ */ jsx2(Box2, { children: item.node }, item.id) });
2140
- }
2141
- function TaskSpinner({ label }) {
2142
- return /* @__PURE__ */ jsxs2(Box2, { children: [
2143
- /* @__PURE__ */ jsx2(Text2, { children: " ".repeat(PAD2 + 1) }),
2144
- /* @__PURE__ */ jsx2(Spinner, { label })
2145
- ] });
2146
- }
2147
- function useStepLog() {
2148
- const [items, setItems] = useState([]);
2149
- const nextId = useRef(0);
2150
- const log = (node) => {
2151
- const id = nextId.current++;
2152
- setItems((prev) => [...prev, { id, node }]);
2334
+ // sdk/winterc_server.ts
2335
+ function createWintercServer(options) {
2336
+ const {
2337
+ agent,
2338
+ env,
2339
+ kv = createMemoryKv(),
2340
+ vector = createMemoryVectorStore(),
2341
+ vectorSearch,
2342
+ clientHtml,
2343
+ logger = consoleLogger,
2344
+ metrics = noopMetrics,
2345
+ s2sConfig = DEFAULT_S2S_CONFIG
2346
+ } = options;
2347
+ const executor = createDirectExecutor({
2348
+ agent,
2349
+ env,
2350
+ kv,
2351
+ vector,
2352
+ ...vectorSearch ? { vectorSearch } : {},
2353
+ createWebSocket: options.createWebSocket,
2354
+ logger,
2355
+ metrics,
2356
+ s2sConfig
2357
+ });
2358
+ const sessions = /* @__PURE__ */ new Map();
2359
+ const readyConfig = {
2360
+ audioFormat: AUDIO_FORMAT,
2361
+ sampleRate: s2sConfig.inputSampleRate,
2362
+ ttsSampleRate: s2sConfig.outputSampleRate
2153
2363
  };
2154
- return { items, log };
2155
- }
2156
- function CommandRunner({
2157
- run,
2158
- spinnerLabel,
2159
- onError
2160
- }) {
2161
- const { exit } = useApp();
2162
- const { items, log } = useStepLog();
2163
- const [spinning, setSpinning] = useState(true);
2164
- const [err, setErr] = useState(null);
2165
- const started = useRef(false);
2166
- React.useEffect(() => {
2167
- if (started.current) return;
2168
- started.current = true;
2169
- (async () => {
2170
- try {
2171
- await run(log);
2172
- } catch (e) {
2173
- const error3 = e instanceof Error ? e : new Error(String(e));
2174
- setErr(error3.message);
2175
- onError?.(error3);
2364
+ return {
2365
+ async fetch(request) {
2366
+ const url = new URL(request.url);
2367
+ if (url.pathname === "/health") {
2368
+ return new Response(JSON.stringify({ status: "ok", name: agent.name }), {
2369
+ headers: { "Content-Type": "application/json" }
2370
+ });
2176
2371
  }
2177
- setSpinning(false);
2178
- exit();
2179
- })();
2180
- });
2181
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
2182
- /* @__PURE__ */ jsx2(StepLog, { items }),
2183
- err && /* @__PURE__ */ jsx2(ErrorLine, { msg: err }),
2184
- spinning && /* @__PURE__ */ jsx2(TaskSpinner, { label: spinnerLabel ?? "" })
2185
- ] });
2186
- }
2187
- async function runWithInk(label, fn) {
2188
- let thrownError;
2189
- const app = render2(
2190
- /* @__PURE__ */ jsx2(
2191
- CommandRunner,
2192
- {
2193
- spinnerLabel: label,
2194
- onError: (e) => {
2195
- thrownError = e;
2196
- },
2197
- run: fn
2372
+ if (url.pathname === "/" && clientHtml) {
2373
+ return new Response(clientHtml, {
2374
+ headers: { "Content-Type": "text/html" }
2375
+ });
2198
2376
  }
2199
- )
2200
- );
2201
- await app.waitUntilExit();
2202
- if (thrownError) throw thrownError;
2203
- }
2204
-
2205
- // cli/build.ts
2206
- import fs3 from "node:fs/promises";
2207
- import path3 from "node:path";
2208
-
2209
- // cli/_bundler.ts
2210
- import fs2 from "node:fs/promises";
2211
- import path2 from "node:path";
2212
- import preact from "@preact/preset-vite";
2213
- import tailwindcss from "@tailwindcss/vite";
2214
- import { build } from "vite";
2215
- import { viteSingleFile } from "vite-plugin-singlefile";
2216
- var BundleError = class extends Error {
2217
- constructor(message) {
2218
- super(message);
2219
- this.name = "BundleError";
2220
- }
2221
- };
2222
- function workerEntryPlugin(agentDir) {
2223
- const id = "virtual:worker-entry";
2224
- const resolved = `\0${id}`;
2225
- return {
2226
- name: "aai-worker-entry",
2227
- enforce: "pre",
2228
- resolveId(source) {
2229
- if (source === id) return resolved;
2377
+ if (url.pathname === "/") {
2378
+ return new Response(
2379
+ `<!DOCTYPE html><html><body><h1>${agent.name}</h1><p>Agent server running.</p></body></html>`,
2380
+ { headers: { "Content-Type": "text/html" } }
2381
+ );
2382
+ }
2383
+ return new Response("Not Found", { status: 404 });
2384
+ },
2385
+ handleWebSocket(ws, wsOpts) {
2386
+ wireSessionSocket(ws, {
2387
+ sessions,
2388
+ createSession: (sid, client) => executor.createSession({
2389
+ id: sid,
2390
+ agent: agent.name,
2391
+ client,
2392
+ skipGreeting: wsOpts?.skipGreeting ?? false
2393
+ }),
2394
+ readyConfig,
2395
+ logger
2396
+ });
2230
2397
  },
2231
- load(source) {
2232
- if (source === resolved) {
2233
- const agentPath = path2.resolve(agentDir, "agent.ts");
2234
- return [
2235
- `import agent from "${agentPath}";`,
2236
- `import { initWorker } from "@alexkroman1/aai/worker-shim";`,
2237
- `initWorker(agent);`
2238
- ].join("\n");
2398
+ async close() {
2399
+ for (const session of sessions.values()) {
2400
+ await session.stop();
2239
2401
  }
2402
+ sessions.clear();
2240
2403
  }
2241
2404
  };
2242
2405
  }
2243
- var DEFAULT_STYLES_CSS = `@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap");
2244
- @import "tailwindcss";
2245
- @source "./";
2246
- @source "./components/";
2247
- @source "./node_modules/@alexkroman1/aai/ui/";
2248
-
2249
- @theme {
2250
- --color-aai-bg: #101010;
2251
- --color-aai-surface: #151515;
2252
- --color-aai-surface-faint: rgba(255, 255, 255, 0.031);
2253
- --color-aai-surface-hover: rgba(255, 255, 255, 0.059);
2254
- --color-aai-border: #282828;
2255
- --color-aai-primary: #fab283;
2256
- --color-aai-text: rgba(255, 255, 255, 0.936);
2257
- --color-aai-text-secondary: rgba(255, 255, 255, 0.618);
2258
- --color-aai-text-muted: rgba(255, 255, 255, 0.284);
2259
- --color-aai-text-dim: rgba(255, 255, 255, 0.422);
2260
- --color-aai-error: #e06c75;
2261
- --color-aai-ring: #56b6c2;
2262
- --color-aai-state-disconnected: rgba(255, 255, 255, 0.422);
2263
- --color-aai-state-connecting: rgba(255, 255, 255, 0.422);
2264
- --color-aai-state-ready: #7fd88f;
2265
- --color-aai-state-listening: #56b6c2;
2266
- --color-aai-state-thinking: #f5a742;
2267
- --color-aai-state-speaking: #e06c75;
2268
- --color-aai-state-error: #e06c75;
2269
- --radius-aai: 6px;
2270
- --font-aai: "Inter", system-ui, -apple-system, sans-serif;
2271
- --font-aai-mono: "IBM Plex Mono", monospace;
2272
- }
2273
-
2274
- @layer base {
2275
- html, body {
2276
- margin: 0;
2277
- padding: 0;
2278
- background: var(--color-aai-bg);
2406
+ var init_winterc_server = __esm({
2407
+ "sdk/winterc_server.ts"() {
2408
+ "use strict";
2409
+ init_direct_executor();
2410
+ init_kv();
2411
+ init_protocol();
2412
+ init_runtime();
2413
+ init_vector();
2414
+ init_ws_handler();
2279
2415
  }
2280
- }
2416
+ });
2281
2417
 
2282
- @keyframes aai-bounce {
2283
- 0%, 80%, 100% {
2284
- opacity: 0.3;
2285
- transform: scale(0.8);
2286
- }
2287
- 40% {
2288
- opacity: 1;
2289
- transform: scale(1);
2418
+ // sdk/server.ts
2419
+ var server_exports = {};
2420
+ __export(server_exports, {
2421
+ createServer: () => createServer
2422
+ });
2423
+ async function loadWsFactory() {
2424
+ try {
2425
+ const mod = await import("ws");
2426
+ const WS = mod.default ?? mod;
2427
+ return (url, opts) => new WS(url, { headers: opts.headers });
2428
+ } catch {
2429
+ throw new Error(
2430
+ "WebSocket factory not provided and `ws` package not found. Install `ws` (`npm install ws`) or pass `createWebSocket` option."
2431
+ );
2290
2432
  }
2291
2433
  }
2292
-
2293
- @keyframes aai-shimmer {
2294
- 0% {
2295
- background-position: -200% 0;
2296
- }
2297
- 100% {
2298
- background-position: 200% 0;
2434
+ function resolveEnv(env) {
2435
+ const resolved = {};
2436
+ for (const [key, value] of Object.entries(env)) {
2437
+ if (value !== void 0) resolved[key] = value;
2299
2438
  }
2439
+ return resolved;
2300
2440
  }
2301
-
2302
- .tool-shimmer {
2303
- background: linear-gradient(
2304
- 90deg,
2305
- var(--color-aai-text) 25%,
2306
- var(--color-aai-text-dim) 50%,
2307
- var(--color-aai-text) 75%
2441
+ function createServer(options) {
2442
+ const {
2443
+ agent,
2444
+ kv,
2445
+ clientHtml,
2446
+ clientDir,
2447
+ logger = consoleLogger,
2448
+ s2sConfig = DEFAULT_S2S_CONFIG
2449
+ } = options;
2450
+ const env = resolveEnv(
2451
+ options.env ?? (typeof process !== "undefined" ? process.env : {})
2308
2452
  );
2309
- background-size: 200% 100%;
2310
- -webkit-background-clip: text;
2311
- background-clip: text;
2312
- -webkit-text-fill-color: transparent;
2313
- animation: aai-shimmer 2s ease-in-out infinite;
2314
- }
2315
- `;
2316
- var INDEX_HTML = `<!DOCTYPE html>
2317
- <html lang="en">
2318
- <head>
2319
- <meta charset="UTF-8" />
2320
- <meta
2321
- name="viewport"
2322
- content="width=device-width, initial-scale=1.0, viewport-fit=cover"
2323
- />
2324
- <title>aai</title>
2325
- <link rel="icon" href="data:," />
2326
- <link rel="stylesheet" href="../styles.css" />
2327
- </head>
2328
- <body>
2329
- <main id="app"></main>
2330
- <script type="module" src="../client.tsx"></script>
2331
- </body>
2332
- </html>
2333
- `;
2334
- async function bundleAgent(agent, opts) {
2335
- const aaiDir = path2.join(agent.dir, ".aai");
2336
- const buildDir = path2.join(aaiDir, "build");
2337
- await fs2.mkdir(aaiDir, { recursive: true });
2338
- await fs2.writeFile(path2.join(aaiDir, "index.html"), INDEX_HTML);
2339
- const stylesPath = path2.join(agent.dir, "styles.css");
2340
- try {
2341
- await fs2.access(stylesPath);
2342
- } catch {
2343
- await fs2.writeFile(stylesPath, DEFAULT_STYLES_CSS);
2344
- }
2345
- try {
2346
- await build({
2347
- configFile: false,
2348
- root: agent.dir,
2349
- logLevel: "warn",
2350
- plugins: [workerEntryPlugin(agent.dir)],
2351
- build: {
2352
- outDir: buildDir,
2353
- emptyOutDir: true,
2354
- minify: true,
2355
- target: "es2022",
2356
- rollupOptions: {
2357
- input: "virtual:worker-entry",
2358
- output: {
2359
- format: "es",
2360
- entryFileNames: "worker.js",
2361
- inlineDynamicImports: true
2362
- }
2363
- }
2364
- }
2365
- });
2366
- } catch (err) {
2367
- throw new BundleError(err instanceof Error ? err.message : String(err));
2453
+ let wsFactory = options.createWebSocket ?? null;
2454
+ async function getWsFactory() {
2455
+ if (!wsFactory) {
2456
+ wsFactory = await loadWsFactory();
2457
+ }
2458
+ return wsFactory;
2368
2459
  }
2369
- const skipClient = opts?.skipClient || !agent.clientEntry;
2370
- if (!skipClient) {
2371
- try {
2372
- await build({
2373
- configFile: false,
2374
- root: aaiDir,
2375
- logLevel: "warn",
2376
- plugins: [preact(), tailwindcss(), viteSingleFile()],
2377
- build: {
2378
- outDir: buildDir,
2379
- emptyOutDir: false,
2380
- minify: true,
2381
- target: "es2022"
2382
- }
2460
+ let winterc = null;
2461
+ function getWinterc() {
2462
+ if (!winterc) {
2463
+ winterc = createWintercServer({
2464
+ agent,
2465
+ env,
2466
+ ...kv ? { kv } : {},
2467
+ createWebSocket: wsFactory ?? (() => {
2468
+ throw new Error("WebSocket factory not loaded");
2469
+ }),
2470
+ ...clientHtml !== void 0 ? { clientHtml } : {},
2471
+ logger,
2472
+ s2sConfig
2383
2473
  });
2384
- } catch (err) {
2385
- throw new BundleError(err instanceof Error ? err.message : String(err));
2386
2474
  }
2475
+ return winterc;
2387
2476
  }
2388
- const worker = await fs2.readFile(path2.join(buildDir, "worker.js"), "utf-8");
2389
- const htmlPath = skipClient ? path2.join(aaiDir, "index.html") : path2.join(buildDir, "index.html");
2390
- const html = await fs2.readFile(htmlPath, "utf-8");
2477
+ let serverHandle = null;
2391
2478
  return {
2392
- worker,
2393
- html,
2394
- workerBytes: Buffer.byteLength(worker)
2479
+ fetch(request) {
2480
+ return getWinterc().fetch(request);
2481
+ },
2482
+ async listen(port = 3e3) {
2483
+ await getWsFactory();
2484
+ const http = await import("node:http");
2485
+ const nodeServer = http.createServer(async (req, res) => {
2486
+ if (clientDir && req.url) {
2487
+ const served = await serveStaticFile(req.url, clientDir, res);
2488
+ if (served) return;
2489
+ }
2490
+ await nodeHttpHandler(req, res, port, getWinterc);
2491
+ });
2492
+ attachWsUpgrade(nodeServer, port, getWinterc, logger);
2493
+ await new Promise((resolve) => {
2494
+ nodeServer.listen(port, () => resolve());
2495
+ });
2496
+ serverHandle = {
2497
+ async shutdown() {
2498
+ await new Promise((resolve, reject) => {
2499
+ nodeServer.close((err) => err ? reject(err) : resolve());
2500
+ });
2501
+ }
2502
+ };
2503
+ },
2504
+ async close() {
2505
+ await winterc?.close();
2506
+ await serverHandle?.shutdown();
2507
+ }
2395
2508
  };
2396
2509
  }
2397
-
2398
- // cli/build.ts
2399
- init_output();
2400
- async function writeBuildOutput(agentDir, bundle) {
2401
- const buildDir = path3.join(agentDir, ".aai", "build");
2402
- await fs3.mkdir(buildDir, { recursive: true });
2403
- await Promise.all([
2404
- fs3.writeFile(path3.join(buildDir, "worker.js"), bundle.worker),
2405
- fs3.writeFile(path3.join(buildDir, "index.html"), bundle.html)
2406
- ]);
2407
- }
2408
- async function runBuild(opts) {
2409
- const agent = await loadAgent(opts.agentDir);
2410
- if (!agent) {
2411
- throw new Error("No agent found \u2014 run `aai new` first");
2412
- }
2413
- step("Bundle", agent.slug);
2414
- let bundle;
2510
+ async function serveStaticFile(reqUrl, clientDir, res) {
2511
+ const { readFile } = await import("node:fs/promises");
2512
+ const { join, extname, normalize } = await import("node:path");
2513
+ const urlPath = new URL(reqUrl, "http://localhost").pathname;
2514
+ const relPath = urlPath === "/" ? "index.html" : urlPath.slice(1);
2515
+ const filePath = normalize(join(clientDir, relPath));
2516
+ if (!filePath.startsWith(clientDir)) return false;
2415
2517
  try {
2416
- bundle = await bundleAgent(agent);
2417
- } catch (err) {
2418
- if (err instanceof BundleError) {
2419
- error2(err.message);
2420
- throw new Error("Bundle failed \u2014 fix the errors above");
2421
- }
2422
- throw err;
2518
+ const content = await readFile(filePath);
2519
+ const ext = extname(filePath);
2520
+ const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
2521
+ res.writeHead(200, { "Content-Type": contentType });
2522
+ res.end(content);
2523
+ return true;
2524
+ } catch {
2525
+ return false;
2423
2526
  }
2424
- await writeBuildOutput(opts.agentDir, bundle);
2425
- return { agent, bundle };
2426
- }
2427
-
2428
- // cli/new.tsx
2429
- init_colors();
2430
- import fs5 from "node:fs/promises";
2431
- import path5 from "node:path";
2432
- import { fileURLToPath as fileURLToPath2 } from "node:url";
2433
- import chalk4 from "chalk";
2434
- import minimist from "minimist";
2435
- init_new();
2436
- var newCommandDef = {
2437
- name: "init",
2438
- description: "Scaffold a new agent project",
2439
- args: [{ name: "dir", optional: true }],
2440
- options: [
2441
- {
2442
- flags: "-t, --template <template>",
2443
- description: "Template to use"
2444
- },
2445
- { flags: "-f, --force", description: "Overwrite existing agent.ts" },
2446
- { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
2447
- ]
2448
- };
2449
- var TEMPLATE_DESCRIPTIONS = {
2450
- simple: "Minimal starter with search, code, and fetch tools",
2451
- "web-researcher": "Research assistant with web search and page visits",
2452
- "smart-research": "Phase-based research with dynamic tool filtering",
2453
- "memory-agent": "Persistent KV storage across conversations",
2454
- "code-interpreter": "Writes and runs JavaScript for calculations",
2455
- "math-buddy": "Calculations, unit conversions, dice rolls",
2456
- "health-assistant": "Medication lookup, drug interactions, BMI",
2457
- "personal-finance": "Currency, crypto, loans, savings projections",
2458
- "travel-concierge": "Trip planning, weather, flights, hotels",
2459
- "night-owl": "Movie/music/book recs by mood \u2014 custom UI",
2460
- "dispatch-center": "911 dispatch with triage \u2014 custom UI",
2461
- "infocom-adventure": "Zork-style text adventure \u2014 custom UI",
2462
- "embedded-assets": "FAQ bot using embedded JSON knowledge",
2463
- support: "RAG-powered support agent for a documentation site",
2464
- terminal: "STT-only mode for voice-driven commands"
2465
- };
2466
- async function selectTemplate(available) {
2467
- const sorted = ["simple", ...available.filter((t) => t !== "simple")];
2468
- const maxLen = Math.max(...sorted.map((t) => t.length));
2469
- const choices = sorted.map((name) => ({
2470
- label: `${name.padEnd(maxLen + 2)}${TEMPLATE_DESCRIPTIONS[name] ?? ""}`,
2471
- value: name
2472
- }));
2473
- return await askSelect("Which template?", choices);
2474
2527
  }
2475
- async function runNewCommand(args, version, opts) {
2476
- const parsed = minimist(args, {
2477
- string: ["template"],
2478
- boolean: ["force", "help", "yes"],
2479
- alias: { t: "template", f: "force", h: "help", y: "yes" }
2480
- });
2481
- if (parsed.help) {
2482
- console.log(subcommandHelp(newCommandDef, version));
2483
- return "";
2484
- }
2485
- const dir = parsed._[0];
2486
- const cwd = dir ?? (process.env.INIT_CWD || process.cwd());
2487
- if (!parsed.force && await fileExists(path5.join(cwd, "agent.ts"))) {
2488
- console.log(
2489
- `agent.ts already exists in this directory. Use ${interactive("--force")} to overwrite.`
2490
- );
2491
- process.exit(1);
2492
- }
2493
- const cliDir2 = path5.dirname(fileURLToPath2(import.meta.url));
2494
- const templatesDir = path5.join(cliDir2, "..", "templates");
2495
- const { runNew: runNew2 } = await Promise.resolve().then(() => (init_new(), new_exports));
2496
- const available = await listTemplates(templatesDir);
2497
- const template = parsed.template || (parsed.yes ? "simple" : await selectTemplate(available));
2498
- await runWithInk("Scaffolding...", async () => {
2499
- await runNew2({
2500
- targetDir: cwd,
2501
- template,
2502
- templatesDir
2503
- });
2504
- if (isDevMode()) {
2505
- const monorepoRoot = path5.join(cliDir2, "..");
2506
- const pkgJsonPath2 = path5.join(cwd, "package.json");
2507
- const pkgJson2 = JSON.parse(await fs5.readFile(pkgJsonPath2, "utf-8"));
2508
- for (const pkg of ["sdk", "ui"]) {
2509
- const localPkgPath = path5.join(monorepoRoot, pkg, "package.json");
2510
- const localPkg = JSON.parse(await fs5.readFile(localPkgPath, "utf-8"));
2511
- const pkgName = localPkg.name;
2512
- pkgJson2.dependencies[pkgName] = `file:${path5.join(monorepoRoot, pkg)}`;
2513
- }
2514
- await fs5.writeFile(pkgJsonPath2, `${JSON.stringify(pkgJson2, null, 2)}
2515
- `);
2516
- }
2517
- await ensureClaudeMd(cwd);
2518
- await ensureDependencies(cwd);
2519
- });
2520
- if (!opts?.quiet) {
2521
- const cdNeeded = dir != null;
2522
- console.log();
2523
- console.log(chalk4.bold("Next steps:"));
2524
- if (cdNeeded) {
2525
- console.log(` ${chalk4.dim("$")} ${primary(`cd ${path5.relative(process.cwd(), cwd)}`)}`);
2528
+ async function nodeHttpHandler(req, res, port, getWinterc) {
2529
+ try {
2530
+ const protocol = req.socket.encrypted ? "https" : "http";
2531
+ const host = req.headers.host ?? `localhost:${port}`;
2532
+ const url = new URL(req.url ?? "/", `${protocol}://${host}`);
2533
+ const headers = new Headers();
2534
+ for (const [key, val] of Object.entries(req.headers)) {
2535
+ if (val) headers.set(key, Array.isArray(val) ? val[0] ?? "" : val);
2526
2536
  }
2527
- console.log(
2528
- ` ${chalk4.dim("$")} ${primary("aai dev")} ${chalk4.dim("Start local dev server")}`
2529
- );
2530
- console.log(
2531
- ` ${chalk4.dim("$")} ${primary("aai deploy")} ${chalk4.dim("Deploy to production")}`
2532
- );
2537
+ const request = new Request(url, { method: req.method ?? "GET", headers });
2538
+ const response = await getWinterc().fetch(request);
2539
+ res.writeHead(response.status, Object.fromEntries(response.headers));
2540
+ res.end(await response.text());
2541
+ } catch (err) {
2542
+ res.writeHead(500);
2543
+ res.end(err instanceof Error ? err.message : "Internal Server Error");
2533
2544
  }
2534
- return cwd;
2535
2545
  }
2536
-
2537
- // cli/deploy.tsx
2538
- var deployCommandDef = {
2539
- name: "deploy",
2540
- description: "Bundle and deploy to production",
2541
- options: [
2542
- { flags: "-s, --server <url>", description: "Server URL" },
2543
- {
2544
- flags: "--local [url]",
2545
- description: "Use local server",
2546
- hidden: true
2547
- },
2548
- {
2549
- flags: "--dry-run",
2550
- description: "Validate and bundle without deploying"
2551
- },
2552
- { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
2553
- ]
2554
- };
2555
- async function runDeployCommand(args, version) {
2556
- const parsed = minimist2(args, {
2557
- string: ["server", "local"],
2558
- boolean: ["dry-run", "help", "yes"],
2559
- alias: { s: "server", h: "help", y: "yes" }
2560
- });
2561
- if (parsed.help) {
2562
- console.log(subcommandHelp(deployCommandDef, version));
2563
- return;
2564
- }
2565
- const cwd = process.env.INIT_CWD || process.cwd();
2566
- if (!await fileExists(path6.join(cwd, "agent.ts"))) {
2567
- await runNewCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
2568
- }
2569
- const local = parsed.local;
2570
- const serverUrl = local !== void 0 ? typeof local === "string" && local !== "" ? local : "http://localhost:3100" : parsed.server || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
2571
- const dryRun = parsed["dry-run"] ?? false;
2572
- const apiKey = dryRun ? "" : await getApiKey();
2573
- const projectConfig = await readProjectConfig(cwd);
2574
- const slug = projectConfig?.slug ?? generateSlug();
2575
- await runWithInk("Deploying...", async () => {
2576
- const result = await runBuild({ agentDir: cwd });
2577
- const deployed = await runDeploy({
2578
- url: serverUrl,
2579
- bundle: result.bundle,
2580
- env: dryRun ? {} : { ASSEMBLYAI_API_KEY: apiKey },
2581
- slug,
2582
- dryRun,
2583
- apiKey
2584
- });
2585
- await writeProjectConfig(cwd, {
2586
- slug: deployed.slug,
2587
- serverUrl
2546
+ function attachWsUpgrade(nodeServer, port, getWinterc, logger) {
2547
+ import("ws").then((wsMod) => {
2548
+ const WSServer = wsMod.WebSocketServer;
2549
+ if (!WSServer) return;
2550
+ const wss = new WSServer({ noServer: true });
2551
+ nodeServer.on("upgrade", (req, socket, head) => {
2552
+ wss.handleUpgrade(
2553
+ req,
2554
+ socket,
2555
+ head,
2556
+ (ws) => {
2557
+ const reqUrl = new URL(
2558
+ req.url ?? "/",
2559
+ `http://localhost:${port}`
2560
+ );
2561
+ getWinterc().handleWebSocket(ws, {
2562
+ skipGreeting: reqUrl.searchParams.has("resume")
2563
+ });
2564
+ }
2565
+ );
2588
2566
  });
2567
+ }).catch(() => {
2568
+ logger.warn("ws package not available for Node.js WebSocket upgrade");
2589
2569
  });
2590
2570
  }
2571
+ var MIME_TYPES;
2572
+ var init_server = __esm({
2573
+ "sdk/server.ts"() {
2574
+ "use strict";
2575
+ init_runtime();
2576
+ init_winterc_server();
2577
+ MIME_TYPES = {
2578
+ ".html": "text/html",
2579
+ ".js": "application/javascript",
2580
+ ".css": "text/css",
2581
+ ".json": "application/json",
2582
+ ".svg": "image/svg+xml",
2583
+ ".png": "image/png",
2584
+ ".ico": "image/x-icon",
2585
+ ".woff": "font/woff",
2586
+ ".woff2": "font/woff2"
2587
+ };
2588
+ }
2589
+ });
2590
+
2591
+ // cli/cli.ts
2592
+ init_help();
2593
+ init_deploy2();
2594
+ import { readFileSync } from "node:fs";
2595
+ import path10 from "node:path";
2596
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
2597
+ import minimist7 from "minimist";
2591
2598
 
2592
2599
  // cli/dev.tsx
2593
- import path8 from "node:path";
2600
+ import path7 from "node:path";
2594
2601
  import minimist3 from "minimist";
2595
2602
 
2596
2603
  // cli/_dev.ts
2597
- init_output();
2604
+ init_bundler();
2605
+ init_discover();
2606
+ init_ink();
2607
+ import React2 from "react";
2598
2608
 
2599
2609
  // cli/_server_common.ts
2600
- import path7 from "node:path";
2610
+ init_discover();
2611
+ import fs5 from "node:fs/promises";
2612
+ import path6 from "node:path";
2601
2613
  import { createServer as createViteServer } from "vite";
2602
- init_output();
2603
2614
  async function loadAgentDef(cwd) {
2604
- const agentPath = path7.resolve(cwd, "agent.ts");
2615
+ const agentPath = path6.resolve(cwd, "agent.ts");
2605
2616
  const vite = await createViteServer({
2606
2617
  root: cwd,
2607
2618
  logLevel: "silent",
@@ -2623,61 +2634,54 @@ async function resolveServerEnv() {
2623
2634
  Object.entries(process.env).filter((e) => e[1] !== void 0)
2624
2635
  );
2625
2636
  if (!env.ASSEMBLYAI_API_KEY) {
2626
- try {
2627
- env.ASSEMBLYAI_API_KEY = await getApiKey();
2628
- } catch {
2629
- error2("ASSEMBLYAI_API_KEY not set. Set it in your environment or run `aai env add`.");
2630
- throw new Error("ASSEMBLYAI_API_KEY is required");
2631
- }
2637
+ env.ASSEMBLYAI_API_KEY = await getApiKey();
2632
2638
  }
2633
2639
  return env;
2634
2640
  }
2635
- async function loadWsFromProject(cwd) {
2636
- const wsPath = path7.resolve(cwd, "node_modules", "ws", "index.js");
2637
- const mod = await import(wsPath);
2638
- const WS = mod.default ?? mod;
2639
- return (url, opts) => new WS(url, { headers: opts.headers });
2640
- }
2641
- async function bootServer(agentDef, html, env, port, cwd) {
2642
- step("Start", `http://localhost:${port}`);
2643
- const createWebSocket = await loadWsFromProject(cwd);
2641
+ async function bootServer(agentDef, clientDir, env, port) {
2642
+ const wsMod = await import("ws");
2643
+ const WS = wsMod.default ?? wsMod;
2644
+ const createWebSocket = (url, opts) => new WS(url, { headers: opts.headers });
2645
+ const clientHtml = await fs5.readFile(path6.join(clientDir, "index.html"), "utf-8");
2644
2646
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2645
2647
  const server = createServer2({
2646
2648
  agent: agentDef,
2647
- clientHtml: html,
2649
+ clientHtml,
2650
+ clientDir,
2648
2651
  env,
2649
2652
  createWebSocket
2650
2653
  });
2651
2654
  await server.listen(port);
2652
- step("Ready", `http://localhost:${port}`);
2653
2655
  }
2654
2656
 
2655
2657
  // cli/_dev.ts
2656
- async function _startDevServer(cwd, port) {
2658
+ async function _startDevServer(cwd, port, log) {
2657
2659
  const agent = await loadAgent(cwd);
2658
2660
  if (!agent) {
2659
2661
  throw new Error("No agent found \u2014 run `aai init` first");
2660
2662
  }
2661
- step("Bundle", agent.slug);
2662
- let html;
2663
+ log(React2.createElement(Step, { action: "Build", msg: agent.slug }));
2664
+ let clientDir;
2663
2665
  try {
2664
2666
  const bundle = await bundleAgent(agent);
2665
- html = bundle.html;
2667
+ clientDir = bundle.clientDir;
2666
2668
  } catch (err) {
2667
2669
  if (err instanceof BundleError) {
2668
- error2(err.message);
2669
- throw new Error("Bundle failed \u2014 fix the errors above");
2670
+ throw new Error(`Bundle failed: ${err.message}`);
2670
2671
  }
2671
2672
  throw err;
2672
2673
  }
2673
- step("Load", "agent.ts");
2674
2674
  const agentDef = await loadAgentDef(cwd);
2675
2675
  const env = await resolveServerEnv();
2676
- await bootServer(agentDef, html, env, port, cwd);
2676
+ await bootServer(agentDef, clientDir, env, port);
2677
+ log(React2.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2677
2678
  }
2678
2679
 
2679
2680
  // cli/dev.tsx
2680
- import { jsx as jsx3 } from "react/jsx-runtime";
2681
+ init_discover();
2682
+ init_help();
2683
+ init_ink();
2684
+ init_init2();
2681
2685
  var devCommandDef = {
2682
2686
  name: "dev",
2683
2687
  description: "Start a local development server",
@@ -2701,22 +2705,27 @@ async function runDevCommand(args, version) {
2701
2705
  }
2702
2706
  const cwd = process.env.INIT_CWD || process.cwd();
2703
2707
  const port = Number.parseInt(parsed.port ?? "3000", 10);
2704
- if (!await fileExists(path8.join(cwd, "agent.ts"))) {
2705
- await runNewCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
2708
+ if (!await fileExists(path7.join(cwd, "agent.ts"))) {
2709
+ await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
2706
2710
  }
2707
2711
  await getApiKey();
2708
- await runWithInk("Bundling...", async (log) => {
2709
- log(/* @__PURE__ */ jsx3(Step, { action: "Dev", msg: `starting on port ${port}` }));
2710
- await _startDevServer(cwd, port);
2712
+ await runWithInk(async (log) => {
2713
+ await _startDevServer(cwd, port, log);
2711
2714
  });
2712
2715
  }
2713
2716
 
2717
+ // cli/cli.ts
2718
+ init_init2();
2719
+
2714
2720
  // cli/rag.tsx
2721
+ init_discover();
2722
+ init_help();
2723
+ init_ink();
2715
2724
  import { render as render3, Text as Text3, useApp as useApp2 } from "ink";
2716
2725
  import minimist4 from "minimist";
2717
2726
  import pLimit from "p-limit";
2718
2727
  import { useEffect, useState as useState2 } from "react";
2719
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2728
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
2720
2729
  var ragCommandDef = {
2721
2730
  name: "rag",
2722
2731
  description: "Ingest a site's llms-full.txt into the vector store",
@@ -2731,7 +2740,7 @@ var ragCommandDef = {
2731
2740
  ]
2732
2741
  };
2733
2742
  var FETCH_TIMEOUT_MS = 6e4;
2734
- var PAD3 = 9;
2743
+ var PAD = 9;
2735
2744
  function RagUI({ url, apiKey, serverUrl, slug, chunkSize, onError }) {
2736
2745
  const { exit } = useApp2();
2737
2746
  const { items, log } = useStepLog();
@@ -2750,19 +2759,19 @@ function RagUI({ url, apiKey, serverUrl, slug, chunkSize, onError }) {
2750
2759
  setProgress
2751
2760
  });
2752
2761
  } catch (e) {
2753
- const error3 = e instanceof Error ? e : new Error(String(e));
2754
- setErr(error3.message);
2755
- onError?.(error3);
2762
+ const error = e instanceof Error ? e : new Error(String(e));
2763
+ setErr(error.message);
2764
+ onError?.(error);
2756
2765
  }
2757
2766
  setProgress(null);
2758
2767
  exit();
2759
2768
  })();
2760
2769
  }, [apiKey, chunkSize, exit, log, onError, serverUrl, slug, url]);
2761
2770
  return /* @__PURE__ */ jsxs3(Fragment2, { children: [
2762
- /* @__PURE__ */ jsx4(StepLog, { items }),
2763
- err && /* @__PURE__ */ jsx4(ErrorLine, { msg: err }),
2771
+ /* @__PURE__ */ jsx5(StepLog, { items }),
2772
+ err && /* @__PURE__ */ jsx5(ErrorLine, { msg: err }),
2764
2773
  progress && /* @__PURE__ */ jsxs3(Text3, { children: [
2765
- " ".repeat(PAD3 + 1),
2774
+ " ".repeat(PAD + 1),
2766
2775
  "Upsert ",
2767
2776
  progress.completed,
2768
2777
  "/",
@@ -2775,7 +2784,7 @@ function RagUI({ url, apiKey, serverUrl, slug, chunkSize, onError }) {
2775
2784
  }
2776
2785
  async function runRag(opts) {
2777
2786
  const { url, apiKey, serverUrl, slug, chunkSize, log, setProgress } = opts;
2778
- log(/* @__PURE__ */ jsx4(Step, { action: "Fetch", msg: url }));
2787
+ log(/* @__PURE__ */ jsx5(Step, { action: "Fetch", msg: url }));
2779
2788
  const resp = await fetch(url, {
2780
2789
  headers: { "User-Agent": "aai-cli/1.0" },
2781
2790
  redirect: "follow",
@@ -2786,27 +2795,27 @@ async function runRag(opts) {
2786
2795
  }
2787
2796
  const content = await resp.text();
2788
2797
  if (content.length === 0) {
2789
- log(/* @__PURE__ */ jsx4(Warn, { msg: "File is empty" }));
2798
+ log(/* @__PURE__ */ jsx5(Warn, { msg: "File is empty" }));
2790
2799
  return;
2791
2800
  }
2792
- log(/* @__PURE__ */ jsx4(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
2801
+ log(/* @__PURE__ */ jsx5(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
2793
2802
  const origin = new URL(url).origin;
2794
2803
  const pages = splitPages(content);
2795
- log(/* @__PURE__ */ jsx4(Step, { action: "Parsed", msg: `${pages.length} pages` }));
2804
+ log(/* @__PURE__ */ jsx5(Step, { action: "Parsed", msg: `${pages.length} pages` }));
2796
2805
  const { RecursiveChunker } = await import("@chonkiejs/core");
2797
2806
  const chunker = await RecursiveChunker.create({ chunkSize });
2798
2807
  const siteSlug = slugify(origin);
2799
2808
  const allChunks = await chunkPages(pages, chunker, origin, siteSlug);
2800
- log(/* @__PURE__ */ jsx4(Step, { action: "Chunked", msg: `${allChunks.length} chunks` }));
2809
+ log(/* @__PURE__ */ jsx5(Step, { action: "Chunked", msg: `${allChunks.length} chunks` }));
2801
2810
  const vectorUrl = `${serverUrl}/${slug}/vector`;
2802
- log(/* @__PURE__ */ jsx4(Info, { msg: `target: ${vectorUrl}` }));
2811
+ log(/* @__PURE__ */ jsx5(Info, { msg: `target: ${vectorUrl}` }));
2803
2812
  const result = await upsertChunks(allChunks, vectorUrl, apiKey, setProgress);
2804
- log(/* @__PURE__ */ jsx4(Step, { action: "Done", msg: `${result.upserted} chunks upserted` }));
2813
+ log(/* @__PURE__ */ jsx5(Step, { action: "Done", msg: `${result.upserted} chunks upserted` }));
2805
2814
  if (result.errors > 0) {
2806
- log(/* @__PURE__ */ jsx4(Warn, { msg: `${result.errors} failed` }));
2807
- if (result.lastError) log(/* @__PURE__ */ jsx4(Info, { msg: `last error: ${result.lastError}` }));
2815
+ log(/* @__PURE__ */ jsx5(Warn, { msg: `${result.errors} failed` }));
2816
+ if (result.lastError) log(/* @__PURE__ */ jsx5(Info, { msg: `last error: ${result.lastError}` }));
2808
2817
  }
2809
- log(/* @__PURE__ */ jsx4(Detail, { msg: `Agent: ${slug}` }));
2818
+ log(/* @__PURE__ */ jsx5(Detail, { msg: `Agent: ${slug}` }));
2810
2819
  }
2811
2820
  async function chunkPages(pages, chunker, origin, siteSlug) {
2812
2821
  const allChunks = [];
@@ -2907,7 +2916,7 @@ async function runRagCommand(args, version) {
2907
2916
  const chunkSize = Number.parseInt(parsed["chunk-size"] ?? "512", 10);
2908
2917
  let thrownError;
2909
2918
  const app = render3(
2910
- /* @__PURE__ */ jsx4(
2919
+ /* @__PURE__ */ jsx5(
2911
2920
  RagUI,
2912
2921
  {
2913
2922
  url,
@@ -2971,8 +2980,12 @@ function slugify(s) {
2971
2980
  }
2972
2981
 
2973
2982
  // cli/secret.tsx
2983
+ init_discover();
2984
+ init_help();
2985
+ init_ink();
2986
+ init_prompts();
2974
2987
  import minimist5 from "minimist";
2975
- import { jsx as jsx5 } from "react/jsx-runtime";
2988
+ import { jsx as jsx6 } from "react/jsx-runtime";
2976
2989
  var secretCommandDef = {
2977
2990
  name: "secret",
2978
2991
  description: "Manage secrets",
@@ -3031,7 +3044,7 @@ async function getServerInfo(cwd) {
3031
3044
  return { serverUrl, slug, apiKey };
3032
3045
  }
3033
3046
  async function secretPut(cwd, name, value) {
3034
- await runWithInk("Setting...", async (log) => {
3047
+ await runWithInk(async (log) => {
3035
3048
  const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
3036
3049
  const resp = await fetch(`${serverUrl}/${slug}/secret`, {
3037
3050
  method: "PUT",
@@ -3045,12 +3058,12 @@ async function secretPut(cwd, name, value) {
3045
3058
  const text = await resp.text();
3046
3059
  throw new Error(`Failed to set secret: ${text}`);
3047
3060
  }
3048
- log(/* @__PURE__ */ jsx5(Step, { action: "Set", msg: `${name} for ${slug}` }));
3061
+ log(/* @__PURE__ */ jsx6(Step, { action: "Set", msg: `${name} for ${slug}` }));
3049
3062
  });
3050
3063
  }
3051
3064
  async function secretDelete(cwd, name) {
3052
3065
  if (!name) throw new Error("Usage: aai secret delete <NAME>");
3053
- await runWithInk("Removing...", async (log) => {
3066
+ await runWithInk(async (log) => {
3054
3067
  const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
3055
3068
  const resp = await fetch(`${serverUrl}/${slug}/secret/${name}`, {
3056
3069
  method: "DELETE",
@@ -3060,11 +3073,11 @@ async function secretDelete(cwd, name) {
3060
3073
  const text = await resp.text();
3061
3074
  throw new Error(`Failed to delete secret: ${text}`);
3062
3075
  }
3063
- log(/* @__PURE__ */ jsx5(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
3076
+ log(/* @__PURE__ */ jsx6(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
3064
3077
  });
3065
3078
  }
3066
3079
  async function secretList(cwd) {
3067
- await runWithInk("Loading...", async (log) => {
3080
+ await runWithInk(async (log) => {
3068
3081
  const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
3069
3082
  const resp = await fetch(`${serverUrl}/${slug}/secret`, {
3070
3083
  headers: { Authorization: `Bearer ${apiKey}` }
@@ -3075,34 +3088,38 @@ async function secretList(cwd) {
3075
3088
  }
3076
3089
  const { vars } = await resp.json();
3077
3090
  if (vars.length === 0) {
3078
- log(/* @__PURE__ */ jsx5(StepInfo, { action: "Secrets", msg: "No secrets set" }));
3091
+ log(/* @__PURE__ */ jsx6(StepInfo, { action: "Secrets", msg: "No secrets set" }));
3079
3092
  } else {
3080
3093
  for (const name of vars) {
3081
- log(/* @__PURE__ */ jsx5(Detail, { msg: name }));
3094
+ log(/* @__PURE__ */ jsx6(Detail, { msg: name }));
3082
3095
  }
3083
3096
  }
3084
3097
  });
3085
3098
  }
3086
3099
 
3087
3100
  // cli/start.tsx
3088
- import path10 from "node:path";
3101
+ init_discover();
3102
+ init_help();
3103
+ init_ink();
3104
+ import path9 from "node:path";
3089
3105
  import minimist6 from "minimist";
3090
3106
 
3091
3107
  // cli/_start.ts
3092
- init_output();
3093
- import fs6 from "node:fs/promises";
3094
- import path9 from "node:path";
3095
- async function _startProductionServer(cwd, port) {
3096
- const buildDir = path9.join(cwd, ".aai", "build");
3097
- const html = await fs6.readFile(path9.join(buildDir, "index.html"), "utf-8");
3098
- step("Load", "agent");
3108
+ init_ink();
3109
+ import path8 from "node:path";
3110
+ import React3 from "react";
3111
+ async function _startProductionServer(cwd, port, log) {
3112
+ const clientDir = path8.join(cwd, ".aai", "client");
3113
+ log(React3.createElement(Step, { action: "Load", msg: "agent" }));
3099
3114
  const agentDef = await loadAgentDef(cwd);
3100
3115
  const env = await resolveServerEnv();
3101
- await bootServer(agentDef, html, env, port, cwd);
3116
+ log(React3.createElement(Step, { action: "Start", msg: `http://localhost:${port}` }));
3117
+ await bootServer(agentDef, clientDir, env, port);
3118
+ log(React3.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
3102
3119
  }
3103
3120
 
3104
3121
  // cli/start.tsx
3105
- import { jsx as jsx6 } from "react/jsx-runtime";
3122
+ import { jsx as jsx7 } from "react/jsx-runtime";
3106
3123
  var startCommandDef = {
3107
3124
  name: "start",
3108
3125
  description: "Start the production server from a build",
@@ -3126,20 +3143,20 @@ async function runStartCommand(args, version) {
3126
3143
  }
3127
3144
  const cwd = process.env.INIT_CWD || process.cwd();
3128
3145
  const port = Number.parseInt(parsed.port ?? "3000", 10);
3129
- const buildDir = path10.join(cwd, ".aai", "build");
3130
- if (!await fileExists(path10.join(buildDir, "worker.js"))) {
3146
+ const buildDir = path9.join(cwd, ".aai", "build");
3147
+ if (!await fileExists(path9.join(buildDir, "worker.js"))) {
3131
3148
  throw new Error("No build found \u2014 run `aai build` first");
3132
3149
  }
3133
3150
  await getApiKey();
3134
- await runWithInk("Starting...", async (log) => {
3135
- log(/* @__PURE__ */ jsx6(Step, { action: "Start", msg: `production server on port ${port}` }));
3136
- await _startProductionServer(cwd, port);
3151
+ await runWithInk(async (log) => {
3152
+ log(/* @__PURE__ */ jsx7(Step, { action: "Start", msg: `production server on port ${port}` }));
3153
+ await _startProductionServer(cwd, port, log);
3137
3154
  });
3138
3155
  }
3139
3156
 
3140
3157
  // cli/cli.ts
3141
- var cliDir = path11.dirname(fileURLToPath3(import.meta.url));
3142
- var pkgJsonPath = path11.join(cliDir, "..", "package.json");
3158
+ var cliDir = path10.dirname(fileURLToPath2(import.meta.url));
3159
+ var pkgJsonPath = path10.join(cliDir, "..", "package.json");
3143
3160
  var pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
3144
3161
  var VERSION = pkgJson.version;
3145
3162
  async function main(args) {
@@ -3160,8 +3177,7 @@ async function main(args) {
3160
3177
  const subArgs = rest.map(String);
3161
3178
  switch (subcommand) {
3162
3179
  case "init":
3163
- case "new":
3164
- await runNewCommand(subArgs, VERSION);
3180
+ await runInitCommand(subArgs, VERSION);
3165
3181
  return;
3166
3182
  case "deploy":
3167
3183
  await runDeployCommand(subArgs, VERSION);
@@ -3179,20 +3195,23 @@ async function main(args) {
3179
3195
  await runRagCommand(subArgs, VERSION);
3180
3196
  return;
3181
3197
  case "help":
3182
- case void 0:
3183
3198
  console.log(rootHelp(VERSION));
3184
3199
  return;
3200
+ case void 0:
3201
+ await runInitCommand(subArgs, VERSION);
3202
+ return;
3185
3203
  default:
3186
- error2(`Unknown command: ${subcommand}`);
3204
+ console.error(`Unknown command: ${subcommand}`);
3187
3205
  console.log(rootHelp(VERSION));
3188
3206
  process.exit(1);
3189
3207
  }
3190
3208
  }
3191
- try {
3192
- await main(process.argv.slice(2));
3193
- } catch (err) {
3194
- error2(err instanceof Error ? err.message : String(err));
3195
- process.exit(1);
3209
+ if (process.env.VITEST !== "true") {
3210
+ process.on("SIGINT", () => process.exit(0));
3211
+ main(process.argv.slice(2)).catch((err) => {
3212
+ console.error(err instanceof Error ? err.message : String(err));
3213
+ process.exit(1);
3214
+ });
3196
3215
  }
3197
3216
  export {
3198
3217
  main