@alexkroman1/aai 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli.js +3436 -0
  3. package/package.json +78 -0
  4. package/sdk/_internal_types.ts +89 -0
  5. package/sdk/_mock_ws.ts +172 -0
  6. package/sdk/_timeout.ts +24 -0
  7. package/sdk/builtin_tools.ts +309 -0
  8. package/sdk/capnweb.ts +341 -0
  9. package/sdk/define_agent.ts +70 -0
  10. package/sdk/direct_executor.ts +195 -0
  11. package/sdk/kv.ts +183 -0
  12. package/sdk/mod.ts +35 -0
  13. package/sdk/protocol.ts +313 -0
  14. package/sdk/runtime.ts +65 -0
  15. package/sdk/s2s.ts +271 -0
  16. package/sdk/server.ts +198 -0
  17. package/sdk/session.ts +438 -0
  18. package/sdk/system_prompt.ts +47 -0
  19. package/sdk/types.ts +406 -0
  20. package/sdk/vector.ts +133 -0
  21. package/sdk/winterc_server.ts +141 -0
  22. package/sdk/worker_entry.ts +99 -0
  23. package/sdk/worker_shim.ts +170 -0
  24. package/sdk/ws_handler.ts +190 -0
  25. package/templates/_shared/.env.example +5 -0
  26. package/templates/_shared/package.json +17 -0
  27. package/templates/code-interpreter/agent.ts +27 -0
  28. package/templates/code-interpreter/client.tsx +2 -0
  29. package/templates/dispatch-center/agent.ts +1536 -0
  30. package/templates/dispatch-center/client.tsx +504 -0
  31. package/templates/embedded-assets/agent.ts +49 -0
  32. package/templates/embedded-assets/client.tsx +2 -0
  33. package/templates/embedded-assets/knowledge.json +20 -0
  34. package/templates/health-assistant/agent.ts +160 -0
  35. package/templates/health-assistant/client.tsx +2 -0
  36. package/templates/infocom-adventure/agent.ts +164 -0
  37. package/templates/infocom-adventure/client.tsx +299 -0
  38. package/templates/math-buddy/agent.ts +21 -0
  39. package/templates/math-buddy/client.tsx +2 -0
  40. package/templates/memory-agent/agent.ts +74 -0
  41. package/templates/memory-agent/client.tsx +2 -0
  42. package/templates/night-owl/agent.ts +98 -0
  43. package/templates/night-owl/client.tsx +28 -0
  44. package/templates/personal-finance/agent.ts +26 -0
  45. package/templates/personal-finance/client.tsx +2 -0
  46. package/templates/simple/agent.ts +6 -0
  47. package/templates/simple/client.tsx +2 -0
  48. package/templates/smart-research/agent.ts +164 -0
  49. package/templates/smart-research/client.tsx +2 -0
  50. package/templates/support/README.md +62 -0
  51. package/templates/support/agent.ts +19 -0
  52. package/templates/support/client.tsx +2 -0
  53. package/templates/travel-concierge/agent.ts +29 -0
  54. package/templates/travel-concierge/client.tsx +2 -0
  55. package/templates/web-researcher/agent.ts +17 -0
  56. package/templates/web-researcher/client.tsx +2 -0
  57. package/ui/_components/app.tsx +37 -0
  58. package/ui/_components/chat_view.tsx +36 -0
  59. package/ui/_components/controls.tsx +32 -0
  60. package/ui/_components/error_banner.tsx +18 -0
  61. package/ui/_components/message_bubble.tsx +21 -0
  62. package/ui/_components/message_list.tsx +61 -0
  63. package/ui/_components/state_indicator.tsx +17 -0
  64. package/ui/_components/thinking_indicator.tsx +19 -0
  65. package/ui/_components/tool_call_block.tsx +110 -0
  66. package/ui/_components/tool_icons.tsx +101 -0
  67. package/ui/_components/transcript.tsx +20 -0
  68. package/ui/audio.ts +170 -0
  69. package/ui/components.ts +49 -0
  70. package/ui/components_mod.ts +37 -0
  71. package/ui/mod.ts +48 -0
  72. package/ui/mount.tsx +112 -0
  73. package/ui/mount_context.ts +19 -0
  74. package/ui/session.ts +456 -0
  75. package/ui/session_mod.ts +27 -0
  76. package/ui/signals.ts +111 -0
  77. package/ui/types.ts +50 -0
  78. package/ui/worklets/capture-processor.js +62 -0
  79. package/ui/worklets/playback-processor.js +110 -0
package/dist/cli.js ADDED
@@ -0,0 +1,3436 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // cli/_colors.ts
13
+ import chalk from "chalk";
14
+ function primary(s) {
15
+ return chalk.hex("#fab283")(s);
16
+ }
17
+ function interactive(s) {
18
+ return chalk.hex("#9dbefe")(s);
19
+ }
20
+ function error(s) {
21
+ return chalk.hex("#fc533a")(s);
22
+ }
23
+ function warning(s) {
24
+ return chalk.hex("#fcd53a")(s);
25
+ }
26
+ var init_colors = __esm({
27
+ "cli/_colors.ts"() {
28
+ "use strict";
29
+ }
30
+ });
31
+
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));
48
+ }
49
+ function error2(msg) {
50
+ console.error(`${error(chalk3.bold("error"))}: ${msg}`);
51
+ }
52
+ var PAD;
53
+ var init_output = __esm({
54
+ "cli/_output.ts"() {
55
+ "use strict";
56
+ init_colors();
57
+ PAD = 9;
58
+ }
59
+ });
60
+
61
+ // sdk/types.ts
62
+ var DEFAULT_INSTRUCTIONS, DEFAULT_GREETING;
63
+ var init_types = __esm({
64
+ "sdk/types.ts"() {
65
+ "use strict";
66
+ DEFAULT_INSTRUCTIONS = `You are AAI, a helpful AI assistant.
67
+
68
+ Voice-First Rules:
69
+ - Optimize for natural speech. Avoid jargon unless central to the answer. Use short, punchy sentences.
70
+ - Never mention "search results," "sources," or "the provided text." Speak as if the knowledge is your own.
71
+ - No visual formatting. Do not say "bullet point," "bold," or "bracketed one." If you need to list items, say "First," "Next," and "Finally."
72
+ - Start with the most important information. No introductory filler.
73
+ - Be concise. Keep answers to 1-3 sentences. For complex topics, provide a high-level summary.
74
+ - Be confident. Avoid hedging phrases like "It seems that" or "I believe."
75
+ - If you don't have enough information, say so directly rather than guessing.
76
+ - Never use exclamation points. Keep your tone calm and conversational.`;
77
+ DEFAULT_GREETING = "Hey there. I'm a voice assistant. What can I help you with?";
78
+ }
79
+ });
80
+
81
+ // cli/_new.ts
82
+ var new_exports = {};
83
+ __export(new_exports, {
84
+ _internals: () => _internals2,
85
+ listTemplates: () => listTemplates,
86
+ runNew: () => runNew
87
+ });
88
+ import fs5 from "node:fs/promises";
89
+ import path4 from "node:path";
90
+ import glob from "fast-glob";
91
+ import fsExtra from "fs-extra";
92
+ async function listTemplates(dir) {
93
+ const templates = [];
94
+ const entries = await fs5.readdir(dir, { withFileTypes: true });
95
+ for (const entry of entries) {
96
+ if (entry.isDirectory() && !entry.name.startsWith("_")) {
97
+ templates.push(entry.name);
98
+ }
99
+ }
100
+ return templates.sort();
101
+ }
102
+ async function copyDirNoOverwrite(src, dest) {
103
+ const files = await glob("**/*", { cwd: src, dot: true, onlyFiles: true });
104
+ for (const file of files) {
105
+ const destPath = path4.join(dest, file);
106
+ try {
107
+ await fs5.access(destPath);
108
+ } catch {
109
+ await fs5.mkdir(path4.dirname(destPath), { recursive: true });
110
+ await fs5.copyFile(path4.join(src, file), destPath);
111
+ }
112
+ }
113
+ }
114
+ async function runNew(opts) {
115
+ const { targetDir, template, templatesDir } = opts;
116
+ const available = await listTemplates(templatesDir);
117
+ if (!available.includes(template)) {
118
+ throw new Error(`unknown template '${template}' -- available: ${available.join(", ")}`);
119
+ }
120
+ _internals2.step("Create", `from template '${template}'`);
121
+ await fsExtra.copy(path4.join(templatesDir, template), targetDir, { overwrite: true });
122
+ await copyDirNoOverwrite(path4.join(templatesDir, "_shared"), targetDir);
123
+ try {
124
+ await fs5.copyFile(path4.join(targetDir, ".env.example"), path4.join(targetDir, ".env"));
125
+ } catch {
126
+ }
127
+ const readmePath = path4.join(targetDir, "README.md");
128
+ try {
129
+ await fs5.access(readmePath);
130
+ } catch {
131
+ const slug = path4.basename(path4.resolve(targetDir));
132
+ const readme = `# ${slug}
133
+
134
+ A voice agent built with [aai](https://github.com/anthropics/aai).
135
+
136
+ ## Getting started
137
+
138
+ \`\`\`sh
139
+ npm install # Install dependencies
140
+ npm run dev # Run locally (opens browser)
141
+ npm run deploy # Deploy to production
142
+ \`\`\`
143
+
144
+ ## Environment variables
145
+
146
+ Secrets are managed on the server, not in local files:
147
+
148
+ \`\`\`sh
149
+ aai env add MY_KEY # Set a secret (prompts for value)
150
+ aai env ls # List secret names
151
+ aai env pull # Pull names into .env for reference
152
+ aai env rm MY_KEY # Remove a secret
153
+ \`\`\`
154
+
155
+ Access secrets in your agent via \`ctx.env.MY_KEY\`.
156
+
157
+ ## Learn more
158
+
159
+ See \`CLAUDE.md\` for the full agent API reference.
160
+ `;
161
+ await fs5.writeFile(readmePath, readme);
162
+ }
163
+ _internals2.step("Done", targetDir);
164
+ return targetDir;
165
+ }
166
+ var _internals2;
167
+ var init_new = __esm({
168
+ "cli/_new.ts"() {
169
+ "use strict";
170
+ init_output();
171
+ _internals2 = {
172
+ step
173
+ };
174
+ }
175
+ });
176
+
177
+ // sdk/protocol.ts
178
+ import { z } from "zod";
179
+ var PROTOCOL_VERSION, DEFAULT_TTS_SAMPLE_RATE, AUDIO_FORMAT, _bitsPerSample, _channels, AudioFrameSpec, KvRequestBaseSchema, HOOK_TIMEOUT_MS, SessionErrorCodeSchema, TranscriptEventSchema, ClientEventSchema, ClientMessageSchema;
180
+ var init_protocol = __esm({
181
+ "sdk/protocol.ts"() {
182
+ "use strict";
183
+ PROTOCOL_VERSION = 1;
184
+ DEFAULT_TTS_SAMPLE_RATE = 24e3;
185
+ AUDIO_FORMAT = "pcm16";
186
+ _bitsPerSample = 16;
187
+ _channels = 1;
188
+ AudioFrameSpec = {
189
+ /** Audio codec identifier sent in the `ready` message. */
190
+ format: AUDIO_FORMAT,
191
+ /** Signed 16-bit integer samples. */
192
+ bitsPerSample: _bitsPerSample,
193
+ /** Little-endian byte order. */
194
+ endianness: "little",
195
+ /** Mono audio. */
196
+ channels: _channels,
197
+ /** Bytes per sample — derived from bitsPerSample and channels. */
198
+ bytesPerSample: _bitsPerSample / 8 * _channels
199
+ };
200
+ KvRequestBaseSchema = z.discriminatedUnion("op", [
201
+ z.object({ op: z.literal("get"), key: z.string().min(1) }),
202
+ z.object({
203
+ op: z.literal("set"),
204
+ key: z.string().min(1),
205
+ value: z.string(),
206
+ ttl: z.number().int().positive().optional()
207
+ }),
208
+ z.object({ op: z.literal("del"), key: z.string().min(1) }),
209
+ z.object({
210
+ op: z.literal("list"),
211
+ prefix: z.string(),
212
+ limit: z.number().int().positive().optional(),
213
+ reverse: z.boolean().optional()
214
+ })
215
+ ]);
216
+ HOOK_TIMEOUT_MS = 5e3;
217
+ SessionErrorCodeSchema = z.enum([
218
+ "stt",
219
+ "llm",
220
+ "tts",
221
+ "tool",
222
+ "protocol",
223
+ "connection",
224
+ "audio",
225
+ "internal"
226
+ ]);
227
+ TranscriptEventSchema = z.object({
228
+ type: z.literal("transcript"),
229
+ text: z.string(),
230
+ isFinal: z.boolean(),
231
+ turnOrder: z.number().int().nonnegative().optional()
232
+ });
233
+ ClientEventSchema = z.discriminatedUnion("type", [
234
+ z.object({ type: z.literal("speech_started") }),
235
+ z.object({ type: z.literal("speech_stopped") }),
236
+ TranscriptEventSchema,
237
+ z.object({
238
+ type: z.literal("turn"),
239
+ text: z.string(),
240
+ turnOrder: z.number().int().nonnegative().optional()
241
+ }),
242
+ z.object({ type: z.literal("chat"), text: z.string() }),
243
+ z.object({
244
+ type: z.literal("tool_call_start"),
245
+ toolCallId: z.string(),
246
+ toolName: z.string(),
247
+ args: z.record(z.string(), z.unknown())
248
+ }),
249
+ z.object({
250
+ type: z.literal("tool_call_done"),
251
+ toolCallId: z.string(),
252
+ result: z.string().max(4e3)
253
+ }),
254
+ z.object({ type: z.literal("tts_done") }),
255
+ z.object({ type: z.literal("cancelled") }),
256
+ z.object({ type: z.literal("reset") }),
257
+ z.object({
258
+ type: z.literal("error"),
259
+ code: SessionErrorCodeSchema,
260
+ message: z.string()
261
+ })
262
+ ]);
263
+ ClientMessageSchema = z.discriminatedUnion("type", [
264
+ z.object({ type: z.literal("audio_ready") }),
265
+ z.object({ type: z.literal("cancel") }),
266
+ z.object({ type: z.literal("reset") }),
267
+ z.object({
268
+ type: z.literal("history"),
269
+ messages: z.array(
270
+ z.object({
271
+ role: z.enum(["user", "assistant"]),
272
+ text: z.string().max(1e5)
273
+ })
274
+ ).max(200)
275
+ })
276
+ ]);
277
+ }
278
+ });
279
+
280
+ // sdk/runtime.ts
281
+ var consoleLogger, noopMetrics, DEFAULT_S2S_CONFIG;
282
+ var init_runtime = __esm({
283
+ "sdk/runtime.ts"() {
284
+ "use strict";
285
+ init_protocol();
286
+ consoleLogger = {
287
+ info(msg, ctx) {
288
+ if (ctx) console.log(msg, ctx);
289
+ else console.log(msg);
290
+ },
291
+ warn(msg, ctx) {
292
+ if (ctx) console.warn(msg, ctx);
293
+ else console.warn(msg);
294
+ },
295
+ error(msg, ctx) {
296
+ if (ctx) console.error(msg, ctx);
297
+ else console.error(msg);
298
+ },
299
+ debug(msg, ctx) {
300
+ if (ctx) console.debug(msg, ctx);
301
+ else console.debug(msg);
302
+ }
303
+ };
304
+ noopMetrics = {
305
+ sessionsTotal: { inc() {
306
+ } },
307
+ sessionsActive: { inc() {
308
+ }, dec() {
309
+ } }
310
+ };
311
+ DEFAULT_S2S_CONFIG = {
312
+ wssUrl: "wss://speech-to-speech.us.assemblyai.com/v1/realtime",
313
+ inputSampleRate: DEFAULT_TTS_SAMPLE_RATE,
314
+ outputSampleRate: DEFAULT_TTS_SAMPLE_RATE
315
+ };
316
+ }
317
+ });
318
+
319
+ // sdk/_internal_types.ts
320
+ import { z as z2 } from "zod";
321
+ function agentToolsToSchemas(tools) {
322
+ return Object.entries(tools).map(([name, def]) => ({
323
+ name,
324
+ description: def.description,
325
+ parameters: z2.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
326
+ }));
327
+ }
328
+ var EMPTY_PARAMS;
329
+ var init_internal_types = __esm({
330
+ "sdk/_internal_types.ts"() {
331
+ "use strict";
332
+ EMPTY_PARAMS = z2.object({});
333
+ }
334
+ });
335
+
336
+ // sdk/builtin_tools.ts
337
+ import { z as z3 } from "zod";
338
+ function htmlToText(html) {
339
+ return html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<(br|hr)\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|tr|blockquote)>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#0?39;/g, "'").replace(/[ \t]+/g, " ").replace(/\n /g, "\n").replace(/\n{3,}/g, "\n\n").trim();
340
+ }
341
+ function createWebSearch() {
342
+ return {
343
+ description: "Search the web for current information, facts, news, or answers to questions. Returns a list of results with title, URL, and description. Use this when the user asks about something you don't know, need up-to-date information, or want to verify facts.",
344
+ parameters: webSearchParams,
345
+ async execute(args, ctx) {
346
+ const { query, max_results: maxResults = 5 } = args;
347
+ const apiKey = ctx.env.BRAVE_API_KEY ?? "";
348
+ if (!apiKey) {
349
+ return { error: "BRAVE_API_KEY is not set \u2014 web search unavailable" };
350
+ }
351
+ const url = `${BRAVE_SEARCH_URL}?${new URLSearchParams({
352
+ q: query,
353
+ count: String(maxResults),
354
+ text_decorations: "false"
355
+ })}`;
356
+ const resp = await fetch(url, {
357
+ headers: { "X-Subscription-Token": apiKey },
358
+ signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
359
+ });
360
+ if (!resp.ok) return [];
361
+ const raw = await resp.json();
362
+ const data = BraveSearchResponseSchema.safeParse(raw);
363
+ if (!data.success) return [];
364
+ return (data.data.web?.results ?? []).slice(0, maxResults).map((r) => ({
365
+ title: r.title,
366
+ url: r.url,
367
+ description: r.description
368
+ }));
369
+ }
370
+ };
371
+ }
372
+ function createVisitWebpage() {
373
+ return {
374
+ description: "Fetch a webpage and return its content as clean text. Use this to read the full content of a URL found via web_search, or any link the user shares. Good for reading articles, documentation, blog posts, or product pages.",
375
+ parameters: visitWebpageParams,
376
+ async execute(args, ctx) {
377
+ const { url } = args;
378
+ const resp = await fetch(url, {
379
+ headers: {
380
+ "User-Agent": "Mozilla/5.0 (compatible; VoiceAgent/1.0; +https://github.com/AssemblyAI/aai)",
381
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
382
+ },
383
+ redirect: "follow",
384
+ signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
385
+ });
386
+ if (!resp.ok) {
387
+ return { error: `Failed to fetch: ${resp.status} ${resp.statusText}`, url };
388
+ }
389
+ const htmlContent = await resp.text();
390
+ const trimmedHtml = htmlContent.length > MAX_HTML_BYTES ? htmlContent.slice(0, MAX_HTML_BYTES) : htmlContent;
391
+ const text = htmlToText(trimmedHtml);
392
+ const truncated = text.length > MAX_PAGE_CHARS;
393
+ const content = truncated ? text.slice(0, MAX_PAGE_CHARS) : text;
394
+ return {
395
+ url,
396
+ content,
397
+ ...truncated ? { truncated: true, totalChars: text.length } : {}
398
+ };
399
+ }
400
+ };
401
+ }
402
+ function createFetchJson() {
403
+ return {
404
+ description: "Call a REST API endpoint via HTTP GET and return the JSON response. Use this to fetch structured data from APIs \u2014 for example, weather data, stock prices, exchange rates, or any public JSON API. Supports custom headers for authenticated APIs.",
405
+ parameters: fetchJsonParams,
406
+ async execute(args, ctx) {
407
+ const { url, headers } = args;
408
+ const resp = await fetch(url, {
409
+ ...headers && { headers },
410
+ signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
411
+ });
412
+ if (!resp.ok) {
413
+ return { error: `HTTP ${resp.status} ${resp.statusText}`, url };
414
+ }
415
+ try {
416
+ return await resp.json();
417
+ } catch {
418
+ return { error: "Response was not valid JSON", url };
419
+ }
420
+ }
421
+ };
422
+ }
423
+ function createRunCode() {
424
+ return {
425
+ description: "Execute JavaScript code in a secure sandbox and return the output. Use this for calculations, data transformations, string manipulation, or any task that benefits from running code. Output is captured from console.log(). No network or filesystem access.",
426
+ parameters: runCodeParams,
427
+ async execute(args) {
428
+ const { code } = args;
429
+ const output = [];
430
+ function capture(...captureArgs) {
431
+ output.push(captureArgs.map(String).join(" "));
432
+ }
433
+ const fakeConsole = {
434
+ log: capture,
435
+ info: capture,
436
+ warn: capture,
437
+ error: capture,
438
+ debug: capture
439
+ };
440
+ const AsyncFunction = Object.getPrototypeOf(async () => {
441
+ }).constructor;
442
+ try {
443
+ const fn = new AsyncFunction("console", code);
444
+ await fn(fakeConsole);
445
+ const result = output.join("\n").trim();
446
+ return result || "Code ran successfully (no output)";
447
+ } catch (err) {
448
+ return { error: err instanceof Error ? err.message : String(err) };
449
+ }
450
+ }
451
+ };
452
+ }
453
+ function createVectorSearch(vectorSearchFn) {
454
+ return {
455
+ description: "Search the agent's knowledge base for relevant information. Use this when the user asks a question that might be answered by previously ingested documents or data. Returns the most relevant matches ranked by similarity.",
456
+ parameters: vectorSearchParams,
457
+ async execute(args) {
458
+ const { query, topK = 5 } = args;
459
+ return vectorSearchFn(query, topK);
460
+ }
461
+ };
462
+ }
463
+ function getBuiltinToolDefs(names, opts) {
464
+ const defs = {};
465
+ for (const name of names) {
466
+ switch (name) {
467
+ case "web_search":
468
+ defs[name] = createWebSearch();
469
+ break;
470
+ case "visit_webpage":
471
+ defs[name] = createVisitWebpage();
472
+ break;
473
+ case "fetch_json":
474
+ defs[name] = createFetchJson();
475
+ break;
476
+ case "run_code":
477
+ defs[name] = createRunCode();
478
+ break;
479
+ case "vector_search":
480
+ if (opts?.vectorSearch) {
481
+ defs[name] = createVectorSearch(opts.vectorSearch);
482
+ }
483
+ break;
484
+ }
485
+ }
486
+ return defs;
487
+ }
488
+ function getBuiltinToolSchemas(names) {
489
+ return names.flatMap((name) => {
490
+ const creator = TOOL_CREATORS[name];
491
+ if (!creator) return [];
492
+ const def = creator();
493
+ return [
494
+ {
495
+ name,
496
+ description: def.description,
497
+ parameters: z3.toJSONSchema(def.parameters ?? EMPTY_PARAMS2)
498
+ }
499
+ ];
500
+ });
501
+ }
502
+ var EMPTY_PARAMS2, webSearchParams, BRAVE_SEARCH_URL, BraveSearchResponseSchema, MAX_PAGE_CHARS, MAX_HTML_BYTES, visitWebpageParams, fetchJsonParams, runCodeParams, vectorSearchParams, TOOL_CREATORS;
503
+ var init_builtin_tools = __esm({
504
+ "sdk/builtin_tools.ts"() {
505
+ "use strict";
506
+ EMPTY_PARAMS2 = z3.object({});
507
+ webSearchParams = z3.object({
508
+ query: z3.string().describe("The search query"),
509
+ max_results: z3.number().describe("Maximum number of results to return (default 5)").optional()
510
+ });
511
+ BRAVE_SEARCH_URL = "https://api.search.brave.com/res/v1/web/search";
512
+ BraveSearchResponseSchema = z3.object({
513
+ web: z3.object({
514
+ results: z3.array(
515
+ z3.object({
516
+ title: z3.string(),
517
+ url: z3.string(),
518
+ description: z3.string()
519
+ })
520
+ )
521
+ }).optional()
522
+ });
523
+ MAX_PAGE_CHARS = 1e4;
524
+ MAX_HTML_BYTES = 2e5;
525
+ visitWebpageParams = z3.object({
526
+ url: z3.string().describe("The full URL to fetch (e.g., 'https://example.com/page')")
527
+ });
528
+ fetchJsonParams = z3.object({
529
+ url: z3.string().describe("The URL to fetch JSON from"),
530
+ headers: z3.record(z3.string(), z3.string()).describe("Optional HTTP headers to include in the request").optional()
531
+ });
532
+ runCodeParams = z3.object({
533
+ code: z3.string().describe("JavaScript code to execute. Use console.log() for output.")
534
+ });
535
+ vectorSearchParams = z3.object({
536
+ query: z3.string().describe(
537
+ '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".'
538
+ ),
539
+ topK: z3.number().describe("Maximum results to return (default: 5)").optional()
540
+ });
541
+ TOOL_CREATORS = {
542
+ web_search: createWebSearch,
543
+ visit_webpage: createVisitWebpage,
544
+ fetch_json: createFetchJson,
545
+ run_code: createRunCode,
546
+ // vector_search uses a stub for schema generation
547
+ vector_search: () => createVectorSearch(async () => "")
548
+ };
549
+ }
550
+ });
551
+
552
+ // sdk/kv.ts
553
+ function sortAndPaginate(entries, options) {
554
+ entries.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
555
+ if (options?.reverse) entries.reverse();
556
+ if (options?.limit && options.limit > 0) {
557
+ entries.length = Math.min(entries.length, options.limit);
558
+ }
559
+ return entries;
560
+ }
561
+ function createMemoryKv() {
562
+ const store = /* @__PURE__ */ new Map();
563
+ function isExpired(entry) {
564
+ return entry.expiresAt !== void 0 && entry.expiresAt <= Date.now();
565
+ }
566
+ return {
567
+ get(key) {
568
+ const entry = store.get(key);
569
+ if (!entry || isExpired(entry)) {
570
+ if (entry) store.delete(key);
571
+ return Promise.resolve(null);
572
+ }
573
+ return Promise.resolve(JSON.parse(entry.raw));
574
+ },
575
+ set(key, value, options) {
576
+ const raw = JSON.stringify(value);
577
+ if (raw.length > MAX_VALUE_SIZE) {
578
+ throw new Error(`Value exceeds max size of ${MAX_VALUE_SIZE} bytes`);
579
+ }
580
+ const expireIn = options?.expireIn;
581
+ const entry = { raw };
582
+ if (expireIn && expireIn > 0) {
583
+ entry.expiresAt = Date.now() + expireIn;
584
+ }
585
+ store.set(key, entry);
586
+ return Promise.resolve();
587
+ },
588
+ delete(key) {
589
+ store.delete(key);
590
+ return Promise.resolve();
591
+ },
592
+ list(prefix, options) {
593
+ const now = Date.now();
594
+ const entries = [];
595
+ for (const [key, entry] of store) {
596
+ if (entry.expiresAt && entry.expiresAt <= now) {
597
+ store.delete(key);
598
+ continue;
599
+ }
600
+ if (key.startsWith(prefix)) {
601
+ entries.push({ key, value: JSON.parse(entry.raw) });
602
+ }
603
+ }
604
+ return Promise.resolve(sortAndPaginate(entries, options));
605
+ }
606
+ };
607
+ }
608
+ var MAX_VALUE_SIZE;
609
+ var init_kv = __esm({
610
+ "sdk/kv.ts"() {
611
+ "use strict";
612
+ MAX_VALUE_SIZE = 65536;
613
+ }
614
+ });
615
+
616
+ // sdk/s2s.ts
617
+ function uint8ToBase64(bytes) {
618
+ const CHUNK = 32768;
619
+ const parts = [];
620
+ for (let i = 0; i < bytes.length; i += CHUNK) {
621
+ parts.push(String.fromCharCode(...bytes.subarray(i, i + CHUNK)));
622
+ }
623
+ return btoa(parts.join(""));
624
+ }
625
+ function base64ToUint8(base64) {
626
+ const binary = atob(base64);
627
+ const bytes = new Uint8Array(binary.length);
628
+ for (let i = 0; i < binary.length; i++) {
629
+ bytes[i] = binary.charCodeAt(i);
630
+ }
631
+ return bytes;
632
+ }
633
+ function connectS2s(opts) {
634
+ const { apiKey, config, createWebSocket, logger: log = consoleLogger } = opts;
635
+ return new Promise((resolve, reject) => {
636
+ log.debug("S2S connecting", { url: config.wssUrl });
637
+ const ws = createWebSocket(config.wssUrl, {
638
+ headers: { Authorization: `Bearer ${apiKey}` }
639
+ });
640
+ const target = new EventTarget();
641
+ let opened = false;
642
+ function send(msg) {
643
+ if (ws.readyState !== WS_OPEN) return;
644
+ const type = msg.type;
645
+ if (type !== "input.audio") {
646
+ log.info(`S2S >> ${JSON.stringify(msg)}`);
647
+ }
648
+ ws.send(JSON.stringify(msg));
649
+ }
650
+ const handle = Object.assign(target, {
651
+ sendAudio(audio) {
652
+ send({ type: "input.audio", audio: uint8ToBase64(audio) });
653
+ },
654
+ sendToolResult(callId, result) {
655
+ send({ type: "tool.result", call_id: callId, result });
656
+ },
657
+ updateSession(sessionConfig) {
658
+ send({ type: "session.update", session: sessionConfig });
659
+ },
660
+ resumeSession(sessionId) {
661
+ send({ type: "session.resume", session_id: sessionId });
662
+ },
663
+ close() {
664
+ log.debug("S2S closing");
665
+ ws.close();
666
+ }
667
+ });
668
+ ws.on("open", () => {
669
+ opened = true;
670
+ log.info("S2S WebSocket open");
671
+ resolve(handle);
672
+ });
673
+ ws.on("message", (data) => {
674
+ let msg;
675
+ try {
676
+ msg = JSON.parse(String(data));
677
+ } catch {
678
+ return;
679
+ }
680
+ const type = msg.type;
681
+ if (type !== "reply.audio") {
682
+ log.info(`S2S << ${JSON.stringify(msg)}`);
683
+ }
684
+ switch (type) {
685
+ case "session.ready":
686
+ target.dispatchEvent(
687
+ new CustomEvent("ready", {
688
+ detail: { session_id: msg.session_id }
689
+ })
690
+ );
691
+ break;
692
+ case "session.updated":
693
+ target.dispatchEvent(new CustomEvent("session_updated", { detail: msg }));
694
+ break;
695
+ case "input.speech.started":
696
+ target.dispatchEvent(new CustomEvent("speech_started"));
697
+ break;
698
+ case "input.speech.stopped":
699
+ target.dispatchEvent(new CustomEvent("speech_stopped"));
700
+ break;
701
+ case "transcript.user.delta":
702
+ target.dispatchEvent(
703
+ new CustomEvent("user_transcript_delta", {
704
+ detail: { text: msg.text }
705
+ })
706
+ );
707
+ break;
708
+ case "transcript.user":
709
+ target.dispatchEvent(
710
+ new CustomEvent("user_transcript", {
711
+ detail: {
712
+ item_id: msg.item_id,
713
+ text: msg.text
714
+ }
715
+ })
716
+ );
717
+ break;
718
+ case "reply.started":
719
+ target.dispatchEvent(
720
+ new CustomEvent("reply_started", {
721
+ detail: { reply_id: msg.reply_id }
722
+ })
723
+ );
724
+ break;
725
+ case "reply.audio": {
726
+ const audioBytes = base64ToUint8(msg.data);
727
+ target.dispatchEvent(new CustomEvent("audio", { detail: { audio: audioBytes } }));
728
+ break;
729
+ }
730
+ case "transcript.agent":
731
+ target.dispatchEvent(
732
+ new CustomEvent("agent_transcript", {
733
+ detail: { text: msg.text }
734
+ })
735
+ );
736
+ break;
737
+ case "tool.call":
738
+ target.dispatchEvent(
739
+ new CustomEvent("tool_call", {
740
+ detail: {
741
+ call_id: msg.call_id,
742
+ name: msg.name,
743
+ args: msg.args ?? {}
744
+ }
745
+ })
746
+ );
747
+ break;
748
+ case "reply.done":
749
+ target.dispatchEvent(
750
+ new CustomEvent("reply_done", {
751
+ detail: { status: msg.status ?? void 0 }
752
+ })
753
+ );
754
+ break;
755
+ case "session.error": {
756
+ const code = msg.code;
757
+ const isExpired = code === "session_not_found" || code === "session_forbidden";
758
+ target.dispatchEvent(
759
+ new CustomEvent(isExpired ? "session_expired" : "error", {
760
+ detail: { code, message: msg.message }
761
+ })
762
+ );
763
+ break;
764
+ }
765
+ }
766
+ });
767
+ ws.on("close", (code, reason) => {
768
+ log.info("S2S WebSocket closed", {
769
+ code,
770
+ reason: reason instanceof Uint8Array ? new TextDecoder().decode(reason) : String(reason ?? "")
771
+ });
772
+ target.dispatchEvent(new CustomEvent("close"));
773
+ });
774
+ ws.on("error", (err) => {
775
+ const errObj = err instanceof Error ? err : new Error(String(err));
776
+ log.error("S2S WebSocket error", { error: errObj.message });
777
+ if (!opened) {
778
+ reject(errObj);
779
+ } else {
780
+ target.dispatchEvent(
781
+ new CustomEvent("error", {
782
+ detail: { code: "ws_error", message: errObj.message }
783
+ })
784
+ );
785
+ }
786
+ });
787
+ });
788
+ }
789
+ var WS_OPEN;
790
+ var init_s2s = __esm({
791
+ "sdk/s2s.ts"() {
792
+ "use strict";
793
+ init_runtime();
794
+ WS_OPEN = 1;
795
+ }
796
+ });
797
+
798
+ // sdk/system_prompt.ts
799
+ function buildSystemPrompt(config, opts) {
800
+ const { hasTools } = opts;
801
+ const agentInstructions = config.instructions ? `
802
+
803
+ Agent-Specific Instructions:
804
+ ${config.instructions}` : "";
805
+ const toolPreamble = hasTools ? '\n\nWhen you decide to use a tool, ALWAYS say a brief natural phrase BEFORE the tool call (e.g. "Let me look that up" or "One moment while I check"). This fills silence while the tool executes. Keep preambles to one short sentence.' : "";
806
+ const today = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
807
+ weekday: "long",
808
+ year: "numeric",
809
+ month: "long",
810
+ day: "numeric"
811
+ });
812
+ return DEFAULT_INSTRUCTIONS + `
813
+
814
+ Today's date is ${today}.` + agentInstructions + toolPreamble + (opts?.voice ? VOICE_RULES : "");
815
+ }
816
+ var VOICE_RULES;
817
+ var init_system_prompt = __esm({
818
+ "sdk/system_prompt.ts"() {
819
+ "use strict";
820
+ init_types();
821
+ 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';
822
+ }
823
+ });
824
+
825
+ // sdk/session.ts
826
+ function createS2sSession(opts) {
827
+ const {
828
+ id,
829
+ agent,
830
+ client,
831
+ toolSchemas,
832
+ apiKey,
833
+ s2sConfig,
834
+ executeTool,
835
+ createWebSocket,
836
+ hookInvoker,
837
+ logger: log = consoleLogger,
838
+ metrics = noopMetrics
839
+ } = opts;
840
+ const agentLabel = { agent };
841
+ const agentConfig = opts.skipGreeting ? { ...opts.agentConfig, greeting: "" } : opts.agentConfig;
842
+ const hasTools = toolSchemas.length > 0 || (agentConfig.builtinTools?.length ?? 0) > 0;
843
+ const systemPrompt = buildSystemPrompt(agentConfig, {
844
+ hasTools,
845
+ voice: true
846
+ });
847
+ const s2sTools = toolSchemas.map((ts) => ({
848
+ type: "function",
849
+ name: ts.name,
850
+ description: ts.description,
851
+ parameters: ts.parameters
852
+ }));
853
+ let s2s = null;
854
+ const sessionAbort = new AbortController();
855
+ let audioReady = false;
856
+ let toolCallCount = 0;
857
+ let turnPromise = null;
858
+ let conversationMessages = [];
859
+ let s2sSessionId = null;
860
+ let pendingTools = [];
861
+ async function resolveTurnConfig() {
862
+ if (!hookInvoker) return null;
863
+ try {
864
+ return await hookInvoker.resolveTurnConfig(id, HOOK_TIMEOUT_MS);
865
+ } catch {
866
+ return null;
867
+ }
868
+ }
869
+ async function invokeHook(hook, ...args) {
870
+ if (!hookInvoker) return;
871
+ try {
872
+ switch (hook) {
873
+ case "onConnect":
874
+ await hookInvoker.onConnect(id, HOOK_TIMEOUT_MS);
875
+ break;
876
+ case "onDisconnect":
877
+ await hookInvoker.onDisconnect(id, HOOK_TIMEOUT_MS);
878
+ break;
879
+ case "onTurn":
880
+ await hookInvoker.onTurn(id, args[0], HOOK_TIMEOUT_MS);
881
+ break;
882
+ case "onError":
883
+ await hookInvoker.onError(id, args[0], HOOK_TIMEOUT_MS);
884
+ break;
885
+ case "onStep":
886
+ await hookInvoker.onStep(id, args[0], HOOK_TIMEOUT_MS);
887
+ break;
888
+ }
889
+ } catch (err) {
890
+ log.warn(`${hook} hook failed`, {
891
+ err: err instanceof Error ? err.message : String(err)
892
+ });
893
+ }
894
+ }
895
+ async function handleToolCall(detail) {
896
+ const { call_id, name, args: parsedArgs } = detail;
897
+ client.event({
898
+ type: "tool_call_start",
899
+ toolCallId: call_id,
900
+ toolName: name,
901
+ args: parsedArgs
902
+ });
903
+ const turnConfig = await resolveTurnConfig();
904
+ const maxSteps = turnConfig?.maxSteps ?? agentConfig.maxSteps;
905
+ toolCallCount++;
906
+ if (maxSteps !== void 0 && toolCallCount > maxSteps) {
907
+ log.info("maxSteps exceeded, refusing tool call", {
908
+ toolCallCount,
909
+ maxSteps
910
+ });
911
+ pendingTools.push({
912
+ call_id,
913
+ result: "Maximum tool steps reached. Please respond to the user now."
914
+ });
915
+ client.event({ type: "tool_call_done", toolCallId: call_id, result: "" });
916
+ return;
917
+ }
918
+ if (turnConfig?.activeTools && !turnConfig.activeTools.includes(name)) {
919
+ log.info("Tool filtered by activeTools", { name });
920
+ const errResult = JSON.stringify({
921
+ error: `Tool "${name}" is not available at this step.`
922
+ });
923
+ pendingTools.push({ call_id, result: errResult });
924
+ client.event({
925
+ type: "tool_call_done",
926
+ toolCallId: call_id,
927
+ result: errResult
928
+ });
929
+ return;
930
+ }
931
+ invokeHook("onStep", {
932
+ stepNumber: toolCallCount - 1,
933
+ toolCalls: [{ toolName: name, args: parsedArgs }],
934
+ text: ""
935
+ });
936
+ log.info("S2S tool call", { tool: name, call_id, args: parsedArgs, agent });
937
+ let result;
938
+ try {
939
+ result = await executeTool(name, parsedArgs, id, conversationMessages);
940
+ } catch (err) {
941
+ const msg = err instanceof Error ? err.message : String(err);
942
+ log.error("Tool execution failed", { tool: name, error: msg });
943
+ result = JSON.stringify({ error: msg });
944
+ }
945
+ log.info("S2S tool result", {
946
+ tool: name,
947
+ call_id,
948
+ resultLength: result.length
949
+ });
950
+ pendingTools.push({ call_id, result });
951
+ client.event({ type: "tool_call_done", toolCallId: call_id, result });
952
+ }
953
+ async function connectAndSetup() {
954
+ try {
955
+ const handle = await _internals3.connectS2s({
956
+ apiKey,
957
+ config: s2sConfig,
958
+ createWebSocket,
959
+ logger: log
960
+ });
961
+ if (s2sSessionId) {
962
+ log.info("Attempting S2S session resume", {
963
+ session_id: s2sSessionId
964
+ });
965
+ handle.resumeSession(s2sSessionId);
966
+ }
967
+ handle.updateSession({
968
+ system_prompt: systemPrompt,
969
+ tools: s2sTools
970
+ });
971
+ handle.addEventListener("ready", ((e) => {
972
+ s2sSessionId = e.detail.session_id;
973
+ log.info("S2S session ready", { session_id: s2sSessionId });
974
+ }));
975
+ handle.addEventListener("session_expired", (() => {
976
+ log.info("S2S session expired, reconnecting fresh");
977
+ s2sSessionId = null;
978
+ handle.close();
979
+ }));
980
+ handle.addEventListener("speech_started", () => {
981
+ client.event({ type: "speech_started" });
982
+ });
983
+ handle.addEventListener("speech_stopped", () => {
984
+ client.event({ type: "speech_stopped" });
985
+ });
986
+ handle.addEventListener("user_transcript_delta", ((e) => {
987
+ client.event({
988
+ type: "transcript",
989
+ text: e.detail.text,
990
+ isFinal: false
991
+ });
992
+ }));
993
+ handle.addEventListener("user_transcript", ((e) => {
994
+ const { text } = e.detail;
995
+ log.info("S2S user transcript", { text });
996
+ client.event({ type: "transcript", text, isFinal: true });
997
+ client.event({ type: "turn", text });
998
+ conversationMessages.push({ role: "user", content: text });
999
+ invokeHook("onTurn", text);
1000
+ }));
1001
+ handle.addEventListener("reply_started", () => {
1002
+ toolCallCount = 0;
1003
+ });
1004
+ handle.addEventListener("audio", ((e) => {
1005
+ client.playAudioChunk(e.detail.audio);
1006
+ }));
1007
+ handle.addEventListener("agent_transcript", ((e) => {
1008
+ const { text } = e.detail;
1009
+ client.event({ type: "chat", text });
1010
+ conversationMessages.push({ role: "assistant", content: text });
1011
+ }));
1012
+ handle.addEventListener("tool_call", ((e) => {
1013
+ const p = handleToolCall(e.detail).catch((err) => {
1014
+ log.error("Tool call handler failed", {
1015
+ err: err instanceof Error ? err.message : String(err)
1016
+ });
1017
+ });
1018
+ const prev = turnPromise;
1019
+ turnPromise = (prev ?? Promise.resolve()).then(() => p).finally(() => {
1020
+ turnPromise = null;
1021
+ });
1022
+ }));
1023
+ handle.addEventListener("reply_done", ((e) => {
1024
+ if (e.detail.status === "interrupted") {
1025
+ log.info("S2S reply interrupted (barge-in)");
1026
+ pendingTools = [];
1027
+ client.event({ type: "cancelled" });
1028
+ } else {
1029
+ if (pendingTools.length > 0) {
1030
+ for (const tool of pendingTools) {
1031
+ s2s?.sendToolResult(tool.call_id, tool.result);
1032
+ }
1033
+ pendingTools = [];
1034
+ } else {
1035
+ client.playAudioDone();
1036
+ client.event({ type: "tts_done" });
1037
+ }
1038
+ }
1039
+ }));
1040
+ handle.addEventListener("error", ((e) => {
1041
+ log.error("S2S error", {
1042
+ code: e.detail.code,
1043
+ message: e.detail.message
1044
+ });
1045
+ client.event({
1046
+ type: "error",
1047
+ code: "internal",
1048
+ message: e.detail.message
1049
+ });
1050
+ }));
1051
+ handle.addEventListener("close", () => {
1052
+ log.info("S2S closed");
1053
+ s2s = null;
1054
+ if (!sessionAbort.signal.aborted) {
1055
+ log.info("Attempting S2S reconnect");
1056
+ connectAndSetup().catch((err) => {
1057
+ const msg = err instanceof Error ? err.message : String(err);
1058
+ log.error("S2S reconnect failed", { error: msg });
1059
+ });
1060
+ }
1061
+ });
1062
+ s2s = handle;
1063
+ } catch (err) {
1064
+ const msg = err instanceof Error ? err.message : String(err);
1065
+ log.error("S2S connect failed", { error: msg });
1066
+ client.event({ type: "error", code: "internal", message: msg });
1067
+ }
1068
+ }
1069
+ return {
1070
+ async start() {
1071
+ metrics.sessionsTotal.inc(agentLabel);
1072
+ metrics.sessionsActive.inc(agentLabel);
1073
+ invokeHook("onConnect");
1074
+ await connectAndSetup();
1075
+ },
1076
+ async stop() {
1077
+ if (sessionAbort.signal.aborted) return;
1078
+ sessionAbort.abort();
1079
+ metrics.sessionsActive.dec(agentLabel);
1080
+ if (turnPromise) await turnPromise;
1081
+ s2s?.close();
1082
+ invokeHook("onDisconnect");
1083
+ },
1084
+ onAudio(data) {
1085
+ s2s?.sendAudio(data);
1086
+ },
1087
+ onAudioReady() {
1088
+ if (audioReady) return;
1089
+ audioReady = true;
1090
+ if (agentConfig.greeting && s2s) {
1091
+ s2s.updateSession({
1092
+ system_prompt: systemPrompt,
1093
+ tools: s2sTools,
1094
+ greeting: agentConfig.greeting
1095
+ });
1096
+ }
1097
+ },
1098
+ onCancel() {
1099
+ client.event({ type: "cancelled" });
1100
+ },
1101
+ onReset() {
1102
+ conversationMessages = [];
1103
+ toolCallCount = 0;
1104
+ pendingTools = [];
1105
+ s2sSessionId = null;
1106
+ s2s?.close();
1107
+ client.event({ type: "reset" });
1108
+ },
1109
+ onHistory(incoming) {
1110
+ for (const msg of incoming) {
1111
+ conversationMessages.push({ role: msg.role, content: msg.text });
1112
+ }
1113
+ },
1114
+ waitForTurn() {
1115
+ return turnPromise ?? Promise.resolve();
1116
+ }
1117
+ };
1118
+ }
1119
+ var _internals3;
1120
+ var init_session = __esm({
1121
+ "sdk/session.ts"() {
1122
+ "use strict";
1123
+ init_protocol();
1124
+ init_runtime();
1125
+ init_s2s();
1126
+ init_system_prompt();
1127
+ _internals3 = {
1128
+ connectS2s
1129
+ };
1130
+ }
1131
+ });
1132
+
1133
+ // sdk/worker_entry.ts
1134
+ import { z as z4 } from "zod";
1135
+ async function executeToolCall(name, args, options) {
1136
+ const { tool, env, sessionId, state, kv, messages } = options;
1137
+ const schema = tool.parameters ?? z4.object({});
1138
+ const parsed = schema.safeParse(args);
1139
+ if (!parsed.success) {
1140
+ const issues = (parsed.error?.issues ?? []).map((i) => `${i.path.map(String).join(".")}: ${i.message}`).join(", ");
1141
+ return `Error: Invalid arguments for tool "${name}": ${issues}`;
1142
+ }
1143
+ try {
1144
+ const abortSignal = AbortSignal.timeout(TOOL_HANDLER_TIMEOUT);
1145
+ const envCopy = { ...env };
1146
+ const ctx = {
1147
+ sessionId: sessionId ?? "",
1148
+ env: envCopy,
1149
+ abortSignal,
1150
+ state: state ?? {},
1151
+ get kv() {
1152
+ if (!kv) throw new Error("KV not available");
1153
+ return kv;
1154
+ },
1155
+ messages: messages ?? []
1156
+ };
1157
+ const result = await Promise.resolve(tool.execute(parsed.data, ctx));
1158
+ if (result == null) return "null";
1159
+ return typeof result === "string" ? result : JSON.stringify(result);
1160
+ } catch (err) {
1161
+ if (err instanceof DOMException && err.name === "TimeoutError") {
1162
+ console.warn(`[tool-executor] Tool execution timed out: ${name}`);
1163
+ return `Error: Tool "${name}" timed out after ${TOOL_HANDLER_TIMEOUT}ms`;
1164
+ }
1165
+ console.warn(`[tool-executor] Tool execution failed: ${name}`, err);
1166
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
1167
+ }
1168
+ }
1169
+ var TOOL_HANDLER_TIMEOUT;
1170
+ var init_worker_entry = __esm({
1171
+ "sdk/worker_entry.ts"() {
1172
+ "use strict";
1173
+ TOOL_HANDLER_TIMEOUT = 3e4;
1174
+ }
1175
+ });
1176
+
1177
+ // sdk/direct_executor.ts
1178
+ function buildAgentConfig(agent) {
1179
+ const config = {
1180
+ name: agent.name,
1181
+ instructions: agent.instructions,
1182
+ greeting: agent.greeting,
1183
+ voice: agent.voice
1184
+ };
1185
+ if (agent.sttPrompt !== void 0) config.sttPrompt = agent.sttPrompt;
1186
+ if (typeof agent.maxSteps !== "function") config.maxSteps = agent.maxSteps;
1187
+ if (agent.toolChoice !== void 0) config.toolChoice = agent.toolChoice;
1188
+ if (agent.builtinTools) config.builtinTools = [...agent.builtinTools];
1189
+ if (agent.activeTools) config.activeTools = [...agent.activeTools];
1190
+ return config;
1191
+ }
1192
+ function createDirectExecutor(opts) {
1193
+ const {
1194
+ agent,
1195
+ env,
1196
+ kv = createMemoryKv(),
1197
+ vectorSearch,
1198
+ createWebSocket,
1199
+ logger = consoleLogger,
1200
+ metrics = noopMetrics,
1201
+ s2sConfig = DEFAULT_S2S_CONFIG
1202
+ } = opts;
1203
+ const agentConfig = buildAgentConfig(agent);
1204
+ const builtinDefs = getBuiltinToolDefs(
1205
+ agent.builtinTools ?? [],
1206
+ vectorSearch ? { vectorSearch } : void 0
1207
+ );
1208
+ const allTools = {
1209
+ ...builtinDefs,
1210
+ ...agent.tools
1211
+ };
1212
+ const customSchemas = agentToolsToSchemas(agent.tools ?? {});
1213
+ const builtinSchemas = getBuiltinToolSchemas(agent.builtinTools ?? []);
1214
+ const toolSchemas = [...customSchemas, ...builtinSchemas];
1215
+ const sessionState = /* @__PURE__ */ new Map();
1216
+ const frozenEnv = Object.freeze({ ...env });
1217
+ function getState(sessionId) {
1218
+ if (!sessionState.has(sessionId) && agent.state) {
1219
+ sessionState.set(sessionId, agent.state());
1220
+ }
1221
+ return sessionState.get(sessionId) ?? {};
1222
+ }
1223
+ function makeHookContext(sessionId) {
1224
+ return {
1225
+ sessionId,
1226
+ env: frozenEnv,
1227
+ state: getState(sessionId),
1228
+ get kv() {
1229
+ return kv;
1230
+ }
1231
+ };
1232
+ }
1233
+ const executeTool = async (name, args, sessionId, messages) => {
1234
+ const tool = allTools[name];
1235
+ if (!tool) return JSON.stringify({ error: `Unknown tool: ${name}` });
1236
+ return executeToolCall(name, args, {
1237
+ tool,
1238
+ env: frozenEnv,
1239
+ sessionId,
1240
+ state: getState(sessionId ?? ""),
1241
+ kv,
1242
+ messages
1243
+ });
1244
+ };
1245
+ const hookInvoker = {
1246
+ async onConnect(sessionId) {
1247
+ await agent.onConnect?.(makeHookContext(sessionId));
1248
+ },
1249
+ async onDisconnect(sessionId) {
1250
+ await agent.onDisconnect?.(makeHookContext(sessionId));
1251
+ sessionState.delete(sessionId);
1252
+ },
1253
+ async onTurn(sessionId, text) {
1254
+ await agent.onTurn?.(text, makeHookContext(sessionId));
1255
+ },
1256
+ async onError(sessionId, error3) {
1257
+ await agent.onError?.(new Error(error3.message), makeHookContext(sessionId));
1258
+ },
1259
+ async onStep(sessionId, step2) {
1260
+ await agent.onStep?.(step2, makeHookContext(sessionId));
1261
+ },
1262
+ async resolveTurnConfig(sessionId) {
1263
+ const ctx = makeHookContext(sessionId);
1264
+ let maxSteps;
1265
+ let activeTools;
1266
+ if (typeof agent.maxSteps === "function") {
1267
+ maxSteps = await agent.maxSteps(ctx) ?? void 0;
1268
+ }
1269
+ if (agent.onBeforeStep) {
1270
+ const result = await agent.onBeforeStep(0, ctx);
1271
+ activeTools = result?.activeTools;
1272
+ }
1273
+ if (maxSteps === void 0 && activeTools === void 0) return null;
1274
+ const config = {};
1275
+ if (maxSteps !== void 0) config.maxSteps = maxSteps;
1276
+ if (activeTools !== void 0) config.activeTools = activeTools;
1277
+ return config;
1278
+ }
1279
+ };
1280
+ function createSession(sessionOpts) {
1281
+ if (!createWebSocket) {
1282
+ throw new Error("createWebSocket not provided \u2014 pass it in DirectExecutorOptions");
1283
+ }
1284
+ const apiKey = frozenEnv.ASSEMBLYAI_API_KEY ?? "";
1285
+ return createS2sSession({
1286
+ id: sessionOpts.id,
1287
+ agent: sessionOpts.agent,
1288
+ client: sessionOpts.client,
1289
+ agentConfig,
1290
+ toolSchemas,
1291
+ apiKey,
1292
+ s2sConfig,
1293
+ executeTool,
1294
+ createWebSocket,
1295
+ hookInvoker,
1296
+ skipGreeting: sessionOpts.skipGreeting ?? false,
1297
+ logger,
1298
+ metrics
1299
+ });
1300
+ }
1301
+ return { executeTool, hookInvoker, toolSchemas, createSession };
1302
+ }
1303
+ var init_direct_executor = __esm({
1304
+ "sdk/direct_executor.ts"() {
1305
+ "use strict";
1306
+ init_internal_types();
1307
+ init_builtin_tools();
1308
+ init_kv();
1309
+ init_runtime();
1310
+ init_session();
1311
+ init_worker_entry();
1312
+ }
1313
+ });
1314
+
1315
+ // sdk/ws_handler.ts
1316
+ function isValidAudioChunk(data) {
1317
+ return data.byteLength > 0 && data.byteLength <= MAX_AUDIO_CHUNK_BYTES && data.byteLength % 2 === 0;
1318
+ }
1319
+ function createClientSink(ws) {
1320
+ return {
1321
+ get open() {
1322
+ return ws.readyState === 1;
1323
+ },
1324
+ event(e) {
1325
+ if (ws.readyState === 1) {
1326
+ ws.send(JSON.stringify(e));
1327
+ }
1328
+ },
1329
+ playAudioChunk(chunk) {
1330
+ if (ws.readyState === 1) {
1331
+ ws.send(chunk);
1332
+ }
1333
+ },
1334
+ playAudioDone() {
1335
+ if (ws.readyState === 1) {
1336
+ ws.send(JSON.stringify({ type: "audio_done" }));
1337
+ }
1338
+ }
1339
+ };
1340
+ }
1341
+ function wireSessionSocket(ws, opts) {
1342
+ const { sessions, logger: log = consoleLogger } = opts;
1343
+ const sessionId = crypto.randomUUID();
1344
+ const sid = sessionId.slice(0, 8);
1345
+ const ctx = opts.logContext ?? {};
1346
+ let session = null;
1347
+ ws.addEventListener("open", () => {
1348
+ opts.onOpen?.();
1349
+ log.info("Session connected", { ...ctx, sid });
1350
+ const client = createClientSink(ws);
1351
+ session = opts.createSession(sessionId, client);
1352
+ sessions.set(sessionId, session);
1353
+ ws.send(JSON.stringify({ type: "config", ...opts.readyConfig }));
1354
+ void session.start();
1355
+ log.info("Session ready", { ...ctx, sid });
1356
+ });
1357
+ ws.addEventListener("message", (event) => {
1358
+ if (!session) return;
1359
+ const msgEvent = event;
1360
+ const { data } = msgEvent;
1361
+ if (data instanceof ArrayBuffer) {
1362
+ const chunk = new Uint8Array(data);
1363
+ if (!isValidAudioChunk(chunk)) {
1364
+ log.warn("Invalid audio chunk, dropping", {
1365
+ ...ctx,
1366
+ sid,
1367
+ bytes: chunk.byteLength,
1368
+ aligned: chunk.byteLength % 2 === 0
1369
+ });
1370
+ return;
1371
+ }
1372
+ session.onAudio(chunk);
1373
+ return;
1374
+ }
1375
+ if (typeof data !== "string") return;
1376
+ let json;
1377
+ try {
1378
+ json = JSON.parse(data);
1379
+ } catch {
1380
+ log.warn("Invalid JSON from client", { ...ctx, sid });
1381
+ return;
1382
+ }
1383
+ const parsed = ClientMessageSchema.safeParse(json);
1384
+ if (!parsed.success) {
1385
+ log.warn("Invalid client message", {
1386
+ ...ctx,
1387
+ sid,
1388
+ error: parsed.error.message
1389
+ });
1390
+ return;
1391
+ }
1392
+ const msg = parsed.data;
1393
+ switch (msg.type) {
1394
+ case "audio_ready":
1395
+ session.onAudioReady();
1396
+ break;
1397
+ case "cancel":
1398
+ session.onCancel();
1399
+ break;
1400
+ case "reset":
1401
+ session.onReset();
1402
+ break;
1403
+ case "history":
1404
+ session.onHistory(msg.messages);
1405
+ break;
1406
+ }
1407
+ });
1408
+ ws.addEventListener("close", () => {
1409
+ log.info("Session disconnected", { ...ctx, sid });
1410
+ if (session) {
1411
+ void session.stop().finally(() => {
1412
+ sessions.delete(sessionId);
1413
+ });
1414
+ }
1415
+ opts.onClose?.();
1416
+ });
1417
+ ws.addEventListener("error", (event) => {
1418
+ const msg = event instanceof ErrorEvent ? event.message : "WebSocket error";
1419
+ log.error("WebSocket error", { ...ctx, sid, error: msg });
1420
+ });
1421
+ }
1422
+ var MAX_AUDIO_CHUNK_BYTES;
1423
+ var init_ws_handler = __esm({
1424
+ "sdk/ws_handler.ts"() {
1425
+ "use strict";
1426
+ init_protocol();
1427
+ init_runtime();
1428
+ MAX_AUDIO_CHUNK_BYTES = 1048576;
1429
+ }
1430
+ });
1431
+
1432
+ // sdk/winterc_server.ts
1433
+ function createWintercServer(options) {
1434
+ const {
1435
+ agent,
1436
+ env,
1437
+ kv = createMemoryKv(),
1438
+ vectorSearch,
1439
+ clientHtml,
1440
+ logger = consoleLogger,
1441
+ metrics = noopMetrics,
1442
+ s2sConfig = DEFAULT_S2S_CONFIG
1443
+ } = options;
1444
+ const executor = createDirectExecutor({
1445
+ agent,
1446
+ env,
1447
+ kv,
1448
+ ...vectorSearch ? { vectorSearch } : {},
1449
+ createWebSocket: options.createWebSocket,
1450
+ logger,
1451
+ metrics,
1452
+ s2sConfig
1453
+ });
1454
+ const sessions = /* @__PURE__ */ new Map();
1455
+ const readyConfig = {
1456
+ protocolVersion: PROTOCOL_VERSION,
1457
+ audioFormat: AUDIO_FORMAT,
1458
+ sampleRate: s2sConfig.inputSampleRate,
1459
+ ttsSampleRate: s2sConfig.outputSampleRate,
1460
+ mode: "s2s"
1461
+ };
1462
+ return {
1463
+ async fetch(request) {
1464
+ const url = new URL(request.url);
1465
+ if (url.pathname === "/health") {
1466
+ return new Response(JSON.stringify({ status: "ok", name: agent.name }), {
1467
+ headers: { "Content-Type": "application/json" }
1468
+ });
1469
+ }
1470
+ if (url.pathname === "/" && clientHtml) {
1471
+ return new Response(clientHtml, {
1472
+ headers: { "Content-Type": "text/html" }
1473
+ });
1474
+ }
1475
+ if (url.pathname === "/") {
1476
+ return new Response(
1477
+ `<!DOCTYPE html><html><body><h1>${agent.name}</h1><p>Agent server running.</p></body></html>`,
1478
+ { headers: { "Content-Type": "text/html" } }
1479
+ );
1480
+ }
1481
+ return new Response("Not Found", { status: 404 });
1482
+ },
1483
+ handleWebSocket(ws, wsOpts) {
1484
+ wireSessionSocket(ws, {
1485
+ sessions,
1486
+ createSession: (sid, client) => executor.createSession({
1487
+ id: sid,
1488
+ agent: agent.name,
1489
+ client,
1490
+ skipGreeting: wsOpts?.skipGreeting ?? false
1491
+ }),
1492
+ readyConfig,
1493
+ logger
1494
+ });
1495
+ },
1496
+ async close() {
1497
+ for (const session of sessions.values()) {
1498
+ await session.stop();
1499
+ }
1500
+ sessions.clear();
1501
+ }
1502
+ };
1503
+ }
1504
+ var init_winterc_server = __esm({
1505
+ "sdk/winterc_server.ts"() {
1506
+ "use strict";
1507
+ init_direct_executor();
1508
+ init_kv();
1509
+ init_protocol();
1510
+ init_runtime();
1511
+ init_ws_handler();
1512
+ }
1513
+ });
1514
+
1515
+ // sdk/server.ts
1516
+ var server_exports = {};
1517
+ __export(server_exports, {
1518
+ createServer: () => createServer
1519
+ });
1520
+ async function loadWsFactory() {
1521
+ try {
1522
+ const mod = await import("ws");
1523
+ const WS = mod.default ?? mod;
1524
+ return (url, opts) => new WS(url, { headers: opts.headers });
1525
+ } catch {
1526
+ throw new Error(
1527
+ "WebSocket factory not provided and `ws` package not found. Install `ws` (`npm install ws`) or pass `createWebSocket` option."
1528
+ );
1529
+ }
1530
+ }
1531
+ function resolveEnv(env) {
1532
+ const resolved = {};
1533
+ for (const [key, value] of Object.entries(env)) {
1534
+ if (value !== void 0) resolved[key] = value;
1535
+ }
1536
+ return resolved;
1537
+ }
1538
+ function createServer(options) {
1539
+ const { agent, kv, clientHtml, logger = consoleLogger, s2sConfig = DEFAULT_S2S_CONFIG } = options;
1540
+ const env = resolveEnv(
1541
+ options.env ?? (typeof process !== "undefined" ? process.env : {})
1542
+ );
1543
+ let wsFactory = options.createWebSocket ?? null;
1544
+ async function getWsFactory() {
1545
+ if (!wsFactory) {
1546
+ wsFactory = await loadWsFactory();
1547
+ }
1548
+ return wsFactory;
1549
+ }
1550
+ let winterc = null;
1551
+ function getWinterc() {
1552
+ if (!winterc) {
1553
+ winterc = createWintercServer({
1554
+ agent,
1555
+ env,
1556
+ ...kv ? { kv } : {},
1557
+ createWebSocket: wsFactory,
1558
+ clientHtml,
1559
+ logger,
1560
+ s2sConfig
1561
+ });
1562
+ }
1563
+ return winterc;
1564
+ }
1565
+ let serverHandle = null;
1566
+ return {
1567
+ fetch(request) {
1568
+ return getWinterc().fetch(request);
1569
+ },
1570
+ async listen(port = 3e3) {
1571
+ await getWsFactory();
1572
+ const http = await import("node:http");
1573
+ const nodeServer = http.createServer(async (req, res) => {
1574
+ try {
1575
+ const protocol = req.socket.encrypted ? "https" : "http";
1576
+ const host = req.headers.host ?? `localhost:${port}`;
1577
+ const url = new URL(req.url ?? "/", `${protocol}://${host}`);
1578
+ const headers = new Headers();
1579
+ for (const [key, val] of Object.entries(req.headers)) {
1580
+ if (val) headers.set(key, Array.isArray(val) ? val[0] : val);
1581
+ }
1582
+ const request = new Request(url, {
1583
+ method: req.method ?? "GET",
1584
+ headers
1585
+ });
1586
+ const response = await getWinterc().fetch(request);
1587
+ res.writeHead(response.status, Object.fromEntries(response.headers));
1588
+ const body = await response.text();
1589
+ res.end(body);
1590
+ } catch (err) {
1591
+ res.writeHead(500);
1592
+ res.end(err instanceof Error ? err.message : "Internal Server Error");
1593
+ }
1594
+ });
1595
+ try {
1596
+ const wsMod = await import("ws");
1597
+ const WSServer = wsMod.WebSocketServer ?? wsMod.default?.Server;
1598
+ if (WSServer) {
1599
+ const wss = new WSServer({ noServer: true });
1600
+ nodeServer.on("upgrade", (req, socket, head) => {
1601
+ wss.handleUpgrade(
1602
+ req,
1603
+ socket,
1604
+ head,
1605
+ (ws) => {
1606
+ const reqUrl = new URL(
1607
+ req.url ?? "/",
1608
+ `http://localhost:${port}`
1609
+ );
1610
+ const resume = reqUrl.searchParams.has("resume");
1611
+ getWinterc().handleWebSocket(ws, { skipGreeting: resume });
1612
+ }
1613
+ );
1614
+ });
1615
+ }
1616
+ } catch {
1617
+ logger.warn("ws package not available for Node.js WebSocket upgrade");
1618
+ }
1619
+ await new Promise((resolve) => {
1620
+ nodeServer.listen(port, () => {
1621
+ logger.info(`Agent "${agent.name}" listening on http://localhost:${port}`);
1622
+ resolve();
1623
+ });
1624
+ });
1625
+ serverHandle = {
1626
+ async shutdown() {
1627
+ await new Promise((resolve, reject) => {
1628
+ nodeServer.close((err) => err ? reject(err) : resolve());
1629
+ });
1630
+ }
1631
+ };
1632
+ },
1633
+ async close() {
1634
+ await winterc?.close();
1635
+ await serverHandle?.shutdown();
1636
+ }
1637
+ };
1638
+ }
1639
+ var init_server = __esm({
1640
+ "sdk/server.ts"() {
1641
+ init_runtime();
1642
+ init_winterc_server();
1643
+ }
1644
+ });
1645
+
1646
+ // cli/cli.ts
1647
+ import { readFileSync } from "node:fs";
1648
+ import path11 from "node:path";
1649
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
1650
+ import minimist7 from "minimist";
1651
+
1652
+ // cli/_help.ts
1653
+ init_colors();
1654
+ import chalk2 from "chalk";
1655
+ function rootHelp(version) {
1656
+ const lines = [];
1657
+ lines.push("");
1658
+ lines.push(
1659
+ ` ${primary(chalk2.bold(" \u2584\u2580\u2588 \u2584\u2580\u2588 \u2588"))} ${chalk2.dim("Voice agent development kit")}`
1660
+ );
1661
+ lines.push(` ${primary(chalk2.bold(" \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588"))} ${primary(`v${version}`)}`);
1662
+ lines.push("");
1663
+ lines.push(
1664
+ ` ${chalk2.bold(interactive("Usage"))} ${primary("aai")} ${chalk2.dim("<command> [options]")}`
1665
+ );
1666
+ lines.push("");
1667
+ lines.push(` ${chalk2.bold(interactive("Commands"))}`);
1668
+ lines.push("");
1669
+ const cmds = [
1670
+ ["init", "[dir]", "Scaffold a new agent project"],
1671
+ ["dev", "", "Start a local development server"],
1672
+ ["deploy", "", "Bundle and deploy to production"],
1673
+ ["start", "", "Start production server from build"],
1674
+ ["secret", "<cmd>", "Manage secrets"],
1675
+ ["rag", "<url>", "Ingest a site into the vector store"]
1676
+ ];
1677
+ for (const [name, args, desc] of cmds) {
1678
+ const nameStr = interactive(name.padEnd(8));
1679
+ const argsStr = args ? primary(args.padEnd(6)) : " ";
1680
+ lines.push(` ${nameStr} ${argsStr} ${chalk2.dim(desc)}`);
1681
+ }
1682
+ lines.push("");
1683
+ lines.push(` ${chalk2.bold(interactive("Options"))}`);
1684
+ lines.push("");
1685
+ lines.push(
1686
+ ` ${interactive("-h")}${chalk2.dim(",")} ${interactive("--help")} ${chalk2.dim(
1687
+ "Show this help"
1688
+ )}`
1689
+ );
1690
+ lines.push(
1691
+ ` ${interactive("-V")}${chalk2.dim(",")} ${interactive("--version")} ${chalk2.dim(
1692
+ "Show the version number"
1693
+ )}`
1694
+ );
1695
+ lines.push("");
1696
+ lines.push(` ${chalk2.bold(interactive("Getting started"))}`);
1697
+ lines.push("");
1698
+ lines.push(
1699
+ ` ${chalk2.dim("$")} ${primary("aai init")} ${interactive("my-agent")} ${chalk2.dim(
1700
+ "Create a new agent"
1701
+ )}`
1702
+ );
1703
+ lines.push(` ${chalk2.dim("$")} ${primary("cd my-agent")}`);
1704
+ lines.push(
1705
+ ` ${chalk2.dim("$")} ${primary("aai deploy")} ${chalk2.dim("Deploy to production")}`
1706
+ );
1707
+ lines.push("");
1708
+ return lines.join("\n");
1709
+ }
1710
+ function subcommandHelp(cmd, version) {
1711
+ const lines = [];
1712
+ lines.push("");
1713
+ lines.push(
1714
+ ` ${primary(chalk2.bold("aai"))} ${interactive(chalk2.bold(cmd.name))}${version ? chalk2.dim(` v${version}`) : ""}`
1715
+ );
1716
+ lines.push(` ${chalk2.dim(cmd.description)}`);
1717
+ lines.push("");
1718
+ if (cmd.args && cmd.args.length > 0) {
1719
+ lines.push(` ${chalk2.bold(interactive("Arguments"))}`);
1720
+ lines.push("");
1721
+ for (const arg of cmd.args) {
1722
+ const label = arg.optional ? primary(`[${arg.name}]`) : primary(`<${arg.name}>`);
1723
+ lines.push(` ${label}`);
1724
+ }
1725
+ lines.push("");
1726
+ }
1727
+ const visibleOptions = (cmd.options ?? []).filter((o) => !o.hidden);
1728
+ if (visibleOptions.length > 0) {
1729
+ lines.push(` ${chalk2.bold(interactive("Options"))}`);
1730
+ lines.push("");
1731
+ for (const opt of visibleOptions) {
1732
+ lines.push(` ${interactive(opt.flags)}`);
1733
+ lines.push(` ${chalk2.dim(opt.description)}`);
1734
+ }
1735
+ lines.push(` ${interactive("-h")}${chalk2.dim(",")} ${interactive("--help")}`);
1736
+ lines.push(` ${chalk2.dim("Show this help")}`);
1737
+ lines.push("");
1738
+ }
1739
+ return lines.join("\n");
1740
+ }
1741
+
1742
+ // cli/cli.ts
1743
+ init_output();
1744
+
1745
+ // cli/deploy.tsx
1746
+ import path6 from "node:path";
1747
+ import minimist2 from "minimist";
1748
+
1749
+ // cli/_discover.ts
1750
+ import fs from "node:fs/promises";
1751
+ import path from "node:path";
1752
+ import { fileURLToPath } from "node:url";
1753
+ import { humanId } from "human-id";
1754
+
1755
+ // cli/_exec.ts
1756
+ import { execFile } from "node:child_process";
1757
+ import { promisify } from "node:util";
1758
+ var execFileAsync = promisify(execFile);
1759
+
1760
+ // cli/_discover.ts
1761
+ init_output();
1762
+
1763
+ // cli/_prompts.tsx
1764
+ import { ConfirmInput, PasswordInput, Select } from "@inkjs/ui";
1765
+ import { Box, render, Text } from "ink";
1766
+ import { jsx, jsxs } from "react/jsx-runtime";
1767
+ async function askPassword(message) {
1768
+ return new Promise((resolve) => {
1769
+ const app = render(
1770
+ /* @__PURE__ */ jsxs(Box, { children: [
1771
+ /* @__PURE__ */ jsxs(Text, { children: [
1772
+ message,
1773
+ ": "
1774
+ ] }),
1775
+ /* @__PURE__ */ jsx(
1776
+ PasswordInput,
1777
+ {
1778
+ onSubmit: (value) => {
1779
+ resolve(value);
1780
+ app.unmount();
1781
+ }
1782
+ }
1783
+ )
1784
+ ] })
1785
+ );
1786
+ });
1787
+ }
1788
+ async function askSelect(message, choices) {
1789
+ return new Promise((resolve) => {
1790
+ const app = render(
1791
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1792
+ /* @__PURE__ */ jsx(Text, { children: message }),
1793
+ /* @__PURE__ */ jsx(
1794
+ Select,
1795
+ {
1796
+ options: choices,
1797
+ onChange: (value) => {
1798
+ resolve(value);
1799
+ app.unmount();
1800
+ }
1801
+ }
1802
+ )
1803
+ ] })
1804
+ );
1805
+ });
1806
+ }
1807
+
1808
+ // cli/_discover.ts
1809
+ function isDevMode() {
1810
+ return path.basename(process.argv[0] ?? "") !== "aai";
1811
+ }
1812
+ function generateSlug() {
1813
+ return humanId({ separator: "-", capitalize: false });
1814
+ }
1815
+ var CONFIG_DIR = path.join(process.env.HOME ?? process.env.USERPROFILE ?? ".", ".config", "aai");
1816
+ var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
1817
+ async function readAuthConfig() {
1818
+ try {
1819
+ return JSON.parse(await fs.readFile(CONFIG_FILE, "utf-8"));
1820
+ } catch {
1821
+ return {};
1822
+ }
1823
+ }
1824
+ async function writeAuthConfig(config) {
1825
+ await fs.mkdir(CONFIG_DIR, { recursive: true });
1826
+ await fs.writeFile(CONFIG_FILE, `${JSON.stringify(config, null, 2)}
1827
+ `);
1828
+ if (process.platform !== "win32") {
1829
+ await fs.chmod(CONFIG_FILE, 384);
1830
+ }
1831
+ }
1832
+ async function getApiKey() {
1833
+ const config = await readAuthConfig();
1834
+ if (config.assemblyai_api_key) {
1835
+ process.env.ASSEMBLYAI_API_KEY = config.assemblyai_api_key;
1836
+ return config.assemblyai_api_key;
1837
+ }
1838
+ step("Setup", "AssemblyAI API key required for speech-to-text");
1839
+ console.log("Get one at https://www.assemblyai.com/dashboard/signup\n");
1840
+ let key;
1841
+ while (!key) {
1842
+ key = await askPassword("ASSEMBLYAI_API_KEY");
1843
+ }
1844
+ config.assemblyai_api_key = key;
1845
+ process.env.ASSEMBLYAI_API_KEY = key;
1846
+ await writeAuthConfig(config);
1847
+ step("Saved", CONFIG_FILE);
1848
+ return key;
1849
+ }
1850
+ async function readProjectConfig(agentDir) {
1851
+ try {
1852
+ return JSON.parse(await fs.readFile(path.join(agentDir, ".aai", "project.json"), "utf-8"));
1853
+ } catch {
1854
+ return null;
1855
+ }
1856
+ }
1857
+ async function writeProjectConfig(agentDir, data) {
1858
+ const aaiDir = path.join(agentDir, ".aai");
1859
+ await fs.mkdir(aaiDir, { recursive: true });
1860
+ await fs.writeFile(path.join(aaiDir, "project.json"), `${JSON.stringify(data, null, 2)}
1861
+ `);
1862
+ }
1863
+ var DEFAULT_SERVER = "https://aai-agent.fly.dev";
1864
+ async function fileExists(p) {
1865
+ try {
1866
+ await fs.access(p);
1867
+ return true;
1868
+ } catch {
1869
+ return false;
1870
+ }
1871
+ }
1872
+ async function loadAgent(dir) {
1873
+ const hasAgentTs = await fileExists(path.join(dir, "agent.ts"));
1874
+ if (!hasAgentTs) return null;
1875
+ const config = await readProjectConfig(dir);
1876
+ const slug = config?.slug ?? generateSlug();
1877
+ const clientEntry = await fileExists(path.join(dir, "client.tsx")) ? path.join(dir, "client.tsx") : "";
1878
+ return {
1879
+ slug,
1880
+ dir,
1881
+ entryPoint: path.join(dir, "agent.ts"),
1882
+ env: {},
1883
+ clientEntry,
1884
+ transport: ["websocket"]
1885
+ };
1886
+ }
1887
+ async function ensureClaudeMd(targetDir) {
1888
+ const claudePath = path.join(targetDir, "CLAUDE.md");
1889
+ const cliDir2 = path.dirname(fileURLToPath(import.meta.url));
1890
+ const srcPath = path.join(cliDir2, "..", "templates", "_shared", "CLAUDE.md");
1891
+ const srcContent = await fs.readFile(srcPath, "utf-8");
1892
+ let existing = "";
1893
+ try {
1894
+ existing = await fs.readFile(claudePath, "utf-8");
1895
+ } catch {
1896
+ }
1897
+ if (existing !== srcContent) {
1898
+ await fs.writeFile(claudePath, srcContent);
1899
+ step(existing ? "Updated" : "Wrote", "CLAUDE.md \u2014 aai agent API reference");
1900
+ }
1901
+ }
1902
+ async function ensureDependencies(targetDir) {
1903
+ if (!await fileExists(path.join(targetDir, "node_modules"))) {
1904
+ step("Install", "dependencies");
1905
+ try {
1906
+ await execFileAsync("npm", ["install"], { cwd: targetDir });
1907
+ } catch {
1908
+ step("Skip", "npm install failed");
1909
+ }
1910
+ }
1911
+ }
1912
+
1913
+ // cli/_deploy.ts
1914
+ init_output();
1915
+ var _internals = {
1916
+ fetch: globalThis.fetch.bind(globalThis)
1917
+ };
1918
+ async function attemptDeploy(url, slug, apiKey, env, transport, worker, html, config, toolSchemas) {
1919
+ return await _internals.fetch(`${url}/${slug}/deploy`, {
1920
+ method: "POST",
1921
+ headers: {
1922
+ "Content-Type": "application/json",
1923
+ Authorization: `Bearer ${apiKey}`
1924
+ },
1925
+ body: JSON.stringify({
1926
+ env,
1927
+ worker,
1928
+ html,
1929
+ transport,
1930
+ config,
1931
+ toolSchemas
1932
+ })
1933
+ });
1934
+ }
1935
+ var MAX_RETRIES = 20;
1936
+ async function runDeploy(opts) {
1937
+ const manifest = JSON.parse(opts.bundle.manifest);
1938
+ const worker = opts.bundle.worker;
1939
+ const html = opts.bundle.html;
1940
+ const transport = manifest.transport ?? ["websocket"];
1941
+ const config = manifest.config;
1942
+ const toolSchemas = manifest.toolSchemas;
1943
+ let slug = opts.slug;
1944
+ if (opts.dryRun) {
1945
+ stepInfo("Dry run", "would deploy:");
1946
+ info(`${slug} -> ${opts.url}/${slug}`);
1947
+ return { slug };
1948
+ }
1949
+ for (let i = 0; i < MAX_RETRIES; i++) {
1950
+ const resp = await attemptDeploy(
1951
+ opts.url,
1952
+ slug,
1953
+ opts.apiKey,
1954
+ opts.env,
1955
+ transport,
1956
+ worker,
1957
+ html,
1958
+ config,
1959
+ toolSchemas
1960
+ );
1961
+ if (resp.ok) {
1962
+ step("Deploy", `${slug} -> ${opts.url}/${slug}`);
1963
+ try {
1964
+ const healthResp = await _internals.fetch(`${opts.url}/${slug}/health`);
1965
+ const ok = healthResp.ok && (await healthResp.json()).status === "ok";
1966
+ if (ok) {
1967
+ step("Ready", slug);
1968
+ } else {
1969
+ warn(`${slug} deployed but health check failed -- check for runtime errors`);
1970
+ }
1971
+ } catch {
1972
+ }
1973
+ return { slug };
1974
+ }
1975
+ if (resp.status === 403) {
1976
+ const text2 = await resp.text();
1977
+ if (text2.includes("Slug")) {
1978
+ const next = generateSlug();
1979
+ step("Retry", `slug "${slug}" taken, trying "${next}"`);
1980
+ slug = next;
1981
+ continue;
1982
+ }
1983
+ }
1984
+ const text = await resp.text();
1985
+ throw new Error(`deploy failed (${resp.status}): ${text}`);
1986
+ }
1987
+ throw new Error(`deploy failed: could not find available slug after ${MAX_RETRIES} attempts`);
1988
+ }
1989
+
1990
+ // cli/_ink.tsx
1991
+ import { Spinner } from "@inkjs/ui";
1992
+ import { Box as Box2, render as render2, Static, Text as Text2, useApp } from "ink";
1993
+ import React, { useRef, useState } from "react";
1994
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1995
+ var PAD2 = 9;
1996
+ var PRIMARY = "#fab283";
1997
+ var INTERACTIVE = "#9dbefe";
1998
+ var ERROR_COLOR = "#fc533a";
1999
+ var WARNING_COLOR = "#fcd53a";
2000
+ function Step({ action, msg }) {
2001
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
2002
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: PRIMARY, children: action.padStart(PAD2) }),
2003
+ /* @__PURE__ */ jsxs2(Text2, { children: [
2004
+ " ",
2005
+ msg
2006
+ ] })
2007
+ ] });
2008
+ }
2009
+ function StepInfo({ action, msg }) {
2010
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
2011
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: INTERACTIVE, children: action.padStart(PAD2) }),
2012
+ /* @__PURE__ */ jsxs2(Text2, { children: [
2013
+ " ",
2014
+ msg
2015
+ ] })
2016
+ ] });
2017
+ }
2018
+ function Info({ msg }) {
2019
+ return /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
2020
+ " ".repeat(PAD2 + 1),
2021
+ msg
2022
+ ] });
2023
+ }
2024
+ function Detail({ msg }) {
2025
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
2026
+ " ".repeat(PAD2 + 1),
2027
+ msg
2028
+ ] });
2029
+ }
2030
+ function Warn({ msg }) {
2031
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
2032
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: WARNING_COLOR, children: "warning".padStart(PAD2) }),
2033
+ /* @__PURE__ */ jsxs2(Text2, { children: [
2034
+ " ",
2035
+ msg
2036
+ ] })
2037
+ ] });
2038
+ }
2039
+ function ErrorLine({ msg }) {
2040
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
2041
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: ERROR_COLOR, children: "error" }),
2042
+ /* @__PURE__ */ jsxs2(Text2, { children: [
2043
+ ": ",
2044
+ msg
2045
+ ] })
2046
+ ] });
2047
+ }
2048
+ function StepLog({ items }) {
2049
+ return /* @__PURE__ */ jsx2(Static, { items, children: (item) => /* @__PURE__ */ jsx2(Box2, { children: item.node }, item.id) });
2050
+ }
2051
+ function TaskSpinner({ label }) {
2052
+ return /* @__PURE__ */ jsxs2(Box2, { children: [
2053
+ /* @__PURE__ */ jsx2(Text2, { children: " ".repeat(PAD2 + 1) }),
2054
+ /* @__PURE__ */ jsx2(Spinner, { label })
2055
+ ] });
2056
+ }
2057
+ function useStepLog() {
2058
+ const [items, setItems] = useState([]);
2059
+ const nextId = useRef(0);
2060
+ const log = (node) => {
2061
+ const id = nextId.current++;
2062
+ setItems((prev) => [...prev, { id, node }]);
2063
+ };
2064
+ return { items, log };
2065
+ }
2066
+ function CommandRunner({
2067
+ run,
2068
+ spinnerLabel,
2069
+ onError
2070
+ }) {
2071
+ const { exit } = useApp();
2072
+ const { items, log } = useStepLog();
2073
+ const [spinning, setSpinning] = useState(true);
2074
+ const [err, setErr] = useState(null);
2075
+ React.useEffect(() => {
2076
+ (async () => {
2077
+ try {
2078
+ await run(log);
2079
+ } catch (e) {
2080
+ const error3 = e instanceof Error ? e : new Error(String(e));
2081
+ setErr(error3.message);
2082
+ onError?.(error3);
2083
+ }
2084
+ setSpinning(false);
2085
+ exit();
2086
+ })();
2087
+ }, [exit, log, onError, run]);
2088
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
2089
+ /* @__PURE__ */ jsx2(StepLog, { items }),
2090
+ err && /* @__PURE__ */ jsx2(ErrorLine, { msg: err }),
2091
+ spinning && /* @__PURE__ */ jsx2(TaskSpinner, { label: spinnerLabel ?? "" })
2092
+ ] });
2093
+ }
2094
+ async function runWithInk(label, fn) {
2095
+ let thrownError;
2096
+ const app = render2(
2097
+ /* @__PURE__ */ jsx2(
2098
+ CommandRunner,
2099
+ {
2100
+ spinnerLabel: label,
2101
+ onError: (e) => {
2102
+ thrownError = e;
2103
+ },
2104
+ run: fn
2105
+ }
2106
+ )
2107
+ );
2108
+ await app.waitUntilExit();
2109
+ if (thrownError) throw thrownError;
2110
+ }
2111
+
2112
+ // cli/build.ts
2113
+ import fs4 from "node:fs/promises";
2114
+ import path3 from "node:path";
2115
+
2116
+ // cli/_bundler.ts
2117
+ import fs3 from "node:fs/promises";
2118
+ import path2 from "node:path";
2119
+ import preact from "@preact/preset-vite";
2120
+ import tailwindcss from "@tailwindcss/vite";
2121
+ import { build } from "vite";
2122
+ import { viteSingleFile } from "vite-plugin-singlefile";
2123
+
2124
+ // cli/_static_config.ts
2125
+ init_types();
2126
+ import fs2 from "node:fs/promises";
2127
+ import {
2128
+ Project,
2129
+ SyntaxKind
2130
+ } from "ts-morph";
2131
+ var project = new Project({ useInMemoryFileSystem: true });
2132
+ async function extractStaticConfig(agentPath) {
2133
+ const source = await fs2.readFile(agentPath, "utf-8");
2134
+ return extractStaticConfigFromSource(source, agentPath);
2135
+ }
2136
+ function extractStaticConfigFromSource(source, fileName = "agent.ts") {
2137
+ const sf = project.createSourceFile(fileName, source, { overwrite: true });
2138
+ const call = findDefineAgentCall(sf);
2139
+ if (!call) {
2140
+ throw new BundleError(
2141
+ `Could not find a defineAgent({...}) call in ${fileName}. Make sure your agent.ts has \`export default defineAgent({...})\`.`
2142
+ );
2143
+ }
2144
+ const args = call.getArguments();
2145
+ const arg = args[0];
2146
+ if (!arg || !arg.isKind(SyntaxKind.ObjectLiteralExpression)) {
2147
+ throw new BundleError("The argument to defineAgent() must be an inline object literal");
2148
+ }
2149
+ const obj = arg.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
2150
+ const config = extractConfig(obj, fileName);
2151
+ const toolSchemas = extractToolSchemas(obj, fileName);
2152
+ return { config, toolSchemas };
2153
+ }
2154
+ function findDefineAgentCall(sf) {
2155
+ return sf.getFirstDescendant((node) => {
2156
+ if (!node.isKind(SyntaxKind.CallExpression)) return false;
2157
+ const expr = node.getExpression();
2158
+ return expr.isKind(SyntaxKind.Identifier) && expr.getText() === "defineAgent";
2159
+ });
2160
+ }
2161
+ function extractConfig(obj, fileName) {
2162
+ const name = requireString(obj, "name", fileName);
2163
+ const config = {
2164
+ name,
2165
+ instructions: optionalString(obj, "instructions", fileName) ?? DEFAULT_INSTRUCTIONS,
2166
+ greeting: optionalString(obj, "greeting", fileName) ?? DEFAULT_GREETING,
2167
+ voice: optionalString(obj, "voice", fileName) ?? ""
2168
+ };
2169
+ const mode = optionalString(obj, "mode", fileName);
2170
+ config.mode = mode ?? "s2s";
2171
+ const sttPrompt = optionalString(obj, "sttPrompt", fileName);
2172
+ if (sttPrompt !== void 0) config.sttPrompt = sttPrompt;
2173
+ const maxStepsProp = getProperty(obj, "maxSteps");
2174
+ if (maxStepsProp) {
2175
+ const init = maxStepsProp.getInitializer();
2176
+ if (init.isKind(SyntaxKind.NumericLiteral)) {
2177
+ config.maxSteps = Number(init.getLiteralValue());
2178
+ }
2179
+ }
2180
+ const toolChoiceProp = getProperty(obj, "toolChoice");
2181
+ if (toolChoiceProp) {
2182
+ config.toolChoice = evalLiteral(
2183
+ toolChoiceProp.getInitializer(),
2184
+ "toolChoice",
2185
+ fileName
2186
+ );
2187
+ }
2188
+ const builtinToolsProp = getProperty(obj, "builtinTools");
2189
+ if (builtinToolsProp) {
2190
+ config.builtinTools = evalStringArray(
2191
+ builtinToolsProp.getInitializer(),
2192
+ "builtinTools",
2193
+ fileName
2194
+ );
2195
+ }
2196
+ const activeToolsProp = getProperty(obj, "activeTools");
2197
+ if (activeToolsProp) {
2198
+ config.activeTools = evalStringArray(
2199
+ activeToolsProp.getInitializer(),
2200
+ "activeTools",
2201
+ fileName
2202
+ );
2203
+ }
2204
+ const transportProp = getProperty(obj, "transport");
2205
+ if (transportProp) {
2206
+ const init = transportProp.getInitializer();
2207
+ if (init.isKind(SyntaxKind.StringLiteral)) {
2208
+ config.transport = [init.getLiteralValue()];
2209
+ } else if (init.isKind(SyntaxKind.ArrayLiteralExpression)) {
2210
+ config.transport = evalStringArray(init, "transport", fileName);
2211
+ } else {
2212
+ throw new BundleError(
2213
+ `${fileName}: \`transport\` must be a string literal or array of strings.`
2214
+ );
2215
+ }
2216
+ }
2217
+ return config;
2218
+ }
2219
+ function extractToolSchemas(obj, fileName) {
2220
+ const toolsProp = getProperty(obj, "tools");
2221
+ if (!toolsProp) return [];
2222
+ const init = toolsProp.getInitializer();
2223
+ if (!init.isKind(SyntaxKind.ObjectLiteralExpression)) {
2224
+ throw new BundleError(`${fileName}: \`tools\` must be an inline object literal.`);
2225
+ }
2226
+ const toolsObj = init.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
2227
+ const schemas = [];
2228
+ for (const member of toolsObj.getProperties()) {
2229
+ if (member.isKind(SyntaxKind.SpreadAssignment)) {
2230
+ const text = member.getExpression().getText();
2231
+ throw new BundleError(
2232
+ `${fileName}: Spread expressions like \`...${text}\` in tools cannot be statically analyzed. Define each tool directly in the \`tools\` object.`
2233
+ );
2234
+ }
2235
+ if (!member.isKind(SyntaxKind.PropertyAssignment)) continue;
2236
+ const prop = member;
2237
+ const toolName = prop.getName();
2238
+ const toolInit = prop.getInitializer();
2239
+ if (!toolInit.isKind(SyntaxKind.ObjectLiteralExpression)) {
2240
+ throw new BundleError(`${fileName}: Tool \`${toolName}\` must be an inline object literal.`);
2241
+ }
2242
+ const toolObj = toolInit.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
2243
+ const descProp = getProperty(toolObj, "description");
2244
+ if (!descProp) {
2245
+ throw new BundleError(`${fileName}: Tool \`${toolName}\` is missing a \`description\`.`);
2246
+ }
2247
+ const description = evalStringLiteral(descProp.getInitializer(), toolName, fileName);
2248
+ const paramsProp = getProperty(toolObj, "parameters");
2249
+ const parameters = paramsProp ? zodAstToJsonSchema(paramsProp.getInitializer(), toolName, fileName) : { type: "object", properties: {}, additionalProperties: false };
2250
+ schemas.push({ name: toolName, description, parameters });
2251
+ }
2252
+ return schemas;
2253
+ }
2254
+ function zodAstToJsonSchema(node, toolName, fileName) {
2255
+ return parseZodExpr(node, toolName, fileName).schema;
2256
+ }
2257
+ function parseZodExpr(node, toolName, fileName) {
2258
+ if (!node.isKind(SyntaxKind.CallExpression)) {
2259
+ throw zodError(node, toolName, fileName);
2260
+ }
2261
+ const call = node.asKindOrThrow(SyntaxKind.CallExpression);
2262
+ const expr = call.getExpression();
2263
+ if (expr.isKind(SyntaxKind.PropertyAccessExpression)) {
2264
+ const propAccess = expr.asKindOrThrow(SyntaxKind.PropertyAccessExpression);
2265
+ const obj = propAccess.getExpression();
2266
+ const method = propAccess.getName();
2267
+ if (obj.isKind(SyntaxKind.Identifier) && obj.getText() === "z") {
2268
+ return parseZodBase(method, call, toolName, fileName);
2269
+ }
2270
+ if (method === "describe") {
2271
+ const result = parseZodExpr(obj, toolName, fileName);
2272
+ const args = call.getArguments();
2273
+ if (args[0]) {
2274
+ result.schema.description = evalStringLiteral(args[0], toolName, fileName);
2275
+ }
2276
+ return result;
2277
+ }
2278
+ if (method === "optional") {
2279
+ const result = parseZodExpr(obj, toolName, fileName);
2280
+ result.optional = true;
2281
+ return result;
2282
+ }
2283
+ if (method === "default" || method === "nullable") {
2284
+ return parseZodExpr(obj, toolName, fileName);
2285
+ }
2286
+ return parseZodExpr(obj, toolName, fileName);
2287
+ }
2288
+ throw zodError(node, toolName, fileName);
2289
+ }
2290
+ function parseZodBase(method, call, toolName, fileName) {
2291
+ switch (method) {
2292
+ case "string":
2293
+ return { schema: { type: "string" }, optional: false };
2294
+ case "number":
2295
+ return { schema: { type: "number" }, optional: false };
2296
+ case "boolean":
2297
+ return { schema: { type: "boolean" }, optional: false };
2298
+ case "enum": {
2299
+ const args = call.getArguments();
2300
+ const arg = args[0];
2301
+ if (!arg || !arg.isKind(SyntaxKind.ArrayLiteralExpression)) {
2302
+ throw new BundleError(
2303
+ `${fileName}: Tool \`${toolName}\`: z.enum() requires an array literal argument.`
2304
+ );
2305
+ }
2306
+ const arr = arg.asKindOrThrow(SyntaxKind.ArrayLiteralExpression);
2307
+ const values = arr.getElements().map((el) => {
2308
+ if (!el.isKind(SyntaxKind.StringLiteral)) {
2309
+ throw new BundleError(
2310
+ `${fileName}: Tool \`${toolName}\`: z.enum() values must be string literals.`
2311
+ );
2312
+ }
2313
+ return el.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
2314
+ });
2315
+ return {
2316
+ schema: { type: "string", enum: values },
2317
+ optional: false
2318
+ };
2319
+ }
2320
+ case "array": {
2321
+ const args = call.getArguments();
2322
+ const arg = args[0];
2323
+ if (!arg) {
2324
+ return { schema: { type: "array" }, optional: false };
2325
+ }
2326
+ const items = parseZodExpr(arg, toolName, fileName);
2327
+ return {
2328
+ schema: { type: "array", items: items.schema },
2329
+ optional: false
2330
+ };
2331
+ }
2332
+ case "object": {
2333
+ const args = call.getArguments();
2334
+ const arg = args[0];
2335
+ if (!arg || !arg.isKind(SyntaxKind.ObjectLiteralExpression)) {
2336
+ return {
2337
+ schema: {
2338
+ type: "object",
2339
+ properties: {},
2340
+ additionalProperties: false
2341
+ },
2342
+ optional: false
2343
+ };
2344
+ }
2345
+ const objLit = arg.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
2346
+ const properties = {};
2347
+ const required = [];
2348
+ for (const member of objLit.getProperties()) {
2349
+ if (!member.isKind(SyntaxKind.PropertyAssignment)) continue;
2350
+ const prop = member;
2351
+ const propName = prop.getName();
2352
+ const result = parseZodExpr(prop.getInitializer(), toolName, fileName);
2353
+ properties[propName] = result.schema;
2354
+ if (!result.optional) {
2355
+ required.push(propName);
2356
+ }
2357
+ }
2358
+ const schema = {
2359
+ type: "object",
2360
+ properties,
2361
+ additionalProperties: false
2362
+ };
2363
+ if (required.length > 0) {
2364
+ schema.required = required;
2365
+ }
2366
+ return { schema, optional: false };
2367
+ }
2368
+ default:
2369
+ throw new BundleError(
2370
+ `${fileName}: Tool \`${toolName}\`: unsupported Zod type \`z.${method}()\`. Supported: z.string(), z.number(), z.boolean(), z.enum([...]), z.array(...), z.object({...}).`
2371
+ );
2372
+ }
2373
+ }
2374
+ function zodError(node, toolName, fileName) {
2375
+ const text = node.getText().slice(0, 60);
2376
+ return new BundleError(
2377
+ `${fileName}: Tool \`${toolName}\`: unsupported Zod pattern \`${text}\`. Supported: z.string(), z.number(), z.boolean(), z.enum([...]), z.array(...), z.object({...}), .describe(), .optional().`
2378
+ );
2379
+ }
2380
+ function getProperty(obj, name) {
2381
+ const prop = obj.getProperty(name);
2382
+ if (!prop) return void 0;
2383
+ if (!prop.isKind(SyntaxKind.PropertyAssignment)) return void 0;
2384
+ return prop;
2385
+ }
2386
+ function requireString(obj, field, fileName) {
2387
+ const prop = getProperty(obj, field);
2388
+ if (!prop) {
2389
+ throw new BundleError(`${fileName}: The \`${field}\` field is required in defineAgent({...}).`);
2390
+ }
2391
+ return evalStringLiteral(prop.getInitializer(), field, fileName);
2392
+ }
2393
+ function optionalString(obj, field, fileName) {
2394
+ const prop = getProperty(obj, field);
2395
+ if (!prop) return void 0;
2396
+ return evalStringLiteral(prop.getInitializer(), field, fileName);
2397
+ }
2398
+ function evalStringLiteral(node, context, fileName) {
2399
+ if (node.isKind(SyntaxKind.StringLiteral)) {
2400
+ return node.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
2401
+ }
2402
+ if (node.isKind(SyntaxKind.NoSubstitutionTemplateLiteral)) {
2403
+ return node.asKindOrThrow(SyntaxKind.NoSubstitutionTemplateLiteral).getLiteralValue();
2404
+ }
2405
+ if (node.isKind(SyntaxKind.TemplateExpression)) {
2406
+ throw new BundleError(
2407
+ `${fileName}: \`${context}\` uses template expressions with substitutions, which cannot be statically analyzed. Use a plain string literal.`
2408
+ );
2409
+ }
2410
+ throw new BundleError(
2411
+ `${fileName}: \`${context}\` must be a static string literal. Dynamic expressions cannot be analyzed at build time.`
2412
+ );
2413
+ }
2414
+ function evalStringArray(node, context, fileName) {
2415
+ if (!node.isKind(SyntaxKind.ArrayLiteralExpression)) {
2416
+ throw new BundleError(`${fileName}: \`${context}\` must be an array literal.`);
2417
+ }
2418
+ const arr = node.asKindOrThrow(SyntaxKind.ArrayLiteralExpression);
2419
+ return arr.getElements().map((el) => evalStringLiteral(el, context, fileName));
2420
+ }
2421
+ function evalLiteral(node, context, fileName) {
2422
+ if (node.isKind(SyntaxKind.StringLiteral)) {
2423
+ return node.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
2424
+ }
2425
+ if (node.isKind(SyntaxKind.NoSubstitutionTemplateLiteral)) {
2426
+ return node.asKindOrThrow(SyntaxKind.NoSubstitutionTemplateLiteral).getLiteralValue();
2427
+ }
2428
+ if (node.isKind(SyntaxKind.NumericLiteral)) {
2429
+ return Number(node.asKindOrThrow(SyntaxKind.NumericLiteral).getLiteralValue());
2430
+ }
2431
+ if (node.isKind(SyntaxKind.TrueKeyword)) return true;
2432
+ if (node.isKind(SyntaxKind.FalseKeyword)) return false;
2433
+ if (node.isKind(SyntaxKind.ArrayLiteralExpression)) {
2434
+ return node.asKindOrThrow(SyntaxKind.ArrayLiteralExpression).getElements().map((el) => evalLiteral(el, context, fileName));
2435
+ }
2436
+ if (node.isKind(SyntaxKind.ObjectLiteralExpression)) {
2437
+ const obj = node.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
2438
+ const result = {};
2439
+ for (const member of obj.getProperties()) {
2440
+ if (member.isKind(SyntaxKind.PropertyAssignment)) {
2441
+ const prop = member;
2442
+ result[prop.getName()] = evalLiteral(
2443
+ prop.getInitializer(),
2444
+ context,
2445
+ fileName
2446
+ );
2447
+ }
2448
+ }
2449
+ return result;
2450
+ }
2451
+ throw new BundleError(
2452
+ `${fileName}: \`${context}\` must be a static literal value. Dynamic expressions cannot be analyzed at build time.`
2453
+ );
2454
+ }
2455
+
2456
+ // cli/_bundler.ts
2457
+ var BundleError = class extends Error {
2458
+ constructor(message) {
2459
+ super(message);
2460
+ this.name = "BundleError";
2461
+ }
2462
+ };
2463
+ function workerEntryPlugin(agentDir) {
2464
+ const id = "virtual:worker-entry";
2465
+ const resolved = `\0${id}`;
2466
+ return {
2467
+ name: "aai-worker-entry",
2468
+ enforce: "pre",
2469
+ resolveId(source) {
2470
+ if (source === id) return resolved;
2471
+ },
2472
+ load(source) {
2473
+ if (source === resolved) {
2474
+ const agentPath = path2.resolve(agentDir, "agent.ts");
2475
+ return [
2476
+ `import agent from "${agentPath}";`,
2477
+ `import { initWorker } from "@alexkroman1/aai/worker-shim";`,
2478
+ `initWorker(agent);`
2479
+ ].join("\n");
2480
+ }
2481
+ }
2482
+ };
2483
+ }
2484
+ 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");
2485
+ @import "tailwindcss";
2486
+ @source "./";
2487
+ @source "./components/";
2488
+
2489
+ @theme {
2490
+ --color-aai-bg: #101010;
2491
+ --color-aai-surface: #151515;
2492
+ --color-aai-surface-faint: rgba(255, 255, 255, 0.031);
2493
+ --color-aai-surface-hover: rgba(255, 255, 255, 0.059);
2494
+ --color-aai-border: #282828;
2495
+ --color-aai-primary: #fab283;
2496
+ --color-aai-text: rgba(255, 255, 255, 0.936);
2497
+ --color-aai-text-secondary: rgba(255, 255, 255, 0.618);
2498
+ --color-aai-text-muted: rgba(255, 255, 255, 0.284);
2499
+ --color-aai-text-dim: rgba(255, 255, 255, 0.422);
2500
+ --color-aai-error: #fc533a;
2501
+ --color-aai-ring: #9dbefe;
2502
+ --color-aai-state-disconnected: rgba(255, 255, 255, 0.422);
2503
+ --color-aai-state-connecting: rgba(255, 255, 255, 0.422);
2504
+ --color-aai-state-ready: #12c905;
2505
+ --color-aai-state-listening: #9dbefe;
2506
+ --color-aai-state-thinking: #fcd53a;
2507
+ --color-aai-state-speaking: #fc533a;
2508
+ --color-aai-state-error: #fc533a;
2509
+ --radius-aai: 6px;
2510
+ --font-aai: "Inter", system-ui, -apple-system, sans-serif;
2511
+ --font-aai-mono: "IBM Plex Mono", monospace;
2512
+ }
2513
+
2514
+ @layer base {
2515
+ html, body {
2516
+ margin: 0;
2517
+ padding: 0;
2518
+ background: var(--color-aai-bg);
2519
+ }
2520
+ }
2521
+
2522
+ @keyframes aai-bounce {
2523
+ 0%, 80%, 100% {
2524
+ opacity: 0.3;
2525
+ transform: scale(0.8);
2526
+ }
2527
+ 40% {
2528
+ opacity: 1;
2529
+ transform: scale(1);
2530
+ }
2531
+ }
2532
+
2533
+ @keyframes aai-shimmer {
2534
+ 0% {
2535
+ background-position: -200% 0;
2536
+ }
2537
+ 100% {
2538
+ background-position: 200% 0;
2539
+ }
2540
+ }
2541
+
2542
+ .tool-shimmer {
2543
+ background: linear-gradient(
2544
+ 90deg,
2545
+ var(--color-aai-text) 25%,
2546
+ var(--color-aai-text-dim) 50%,
2547
+ var(--color-aai-text) 75%
2548
+ );
2549
+ background-size: 200% 100%;
2550
+ -webkit-background-clip: text;
2551
+ background-clip: text;
2552
+ -webkit-text-fill-color: transparent;
2553
+ animation: aai-shimmer 2s ease-in-out infinite;
2554
+ }
2555
+ `;
2556
+ var INDEX_HTML = `<!DOCTYPE html>
2557
+ <html lang="en">
2558
+ <head>
2559
+ <meta charset="UTF-8" />
2560
+ <meta
2561
+ name="viewport"
2562
+ content="width=device-width, initial-scale=1.0, viewport-fit=cover"
2563
+ />
2564
+ <title>aai</title>
2565
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
2566
+ <link rel="stylesheet" href="../styles.css" />
2567
+ </head>
2568
+ <body>
2569
+ <main id="app"></main>
2570
+ <script type="module" src="../client.tsx"></script>
2571
+ </body>
2572
+ </html>
2573
+ `;
2574
+ async function bundleAgent(agent, opts) {
2575
+ const aaiDir = path2.join(agent.dir, ".aai");
2576
+ const buildDir = path2.join(aaiDir, "build");
2577
+ await fs3.mkdir(aaiDir, { recursive: true });
2578
+ const { config: agentConfig, toolSchemas } = await extractStaticConfig(agent.entryPoint);
2579
+ await fs3.writeFile(path2.join(aaiDir, "index.html"), INDEX_HTML);
2580
+ const stylesPath = path2.join(agent.dir, "styles.css");
2581
+ try {
2582
+ await fs3.access(stylesPath);
2583
+ } catch {
2584
+ await fs3.writeFile(stylesPath, DEFAULT_STYLES_CSS);
2585
+ }
2586
+ try {
2587
+ await build({
2588
+ configFile: false,
2589
+ root: agent.dir,
2590
+ logLevel: "warn",
2591
+ plugins: [workerEntryPlugin(agent.dir)],
2592
+ build: {
2593
+ outDir: buildDir,
2594
+ emptyOutDir: true,
2595
+ minify: true,
2596
+ target: "es2022",
2597
+ rollupOptions: {
2598
+ input: "virtual:worker-entry",
2599
+ output: {
2600
+ format: "es",
2601
+ entryFileNames: "worker.js",
2602
+ inlineDynamicImports: true
2603
+ }
2604
+ }
2605
+ }
2606
+ });
2607
+ } catch (err) {
2608
+ throw new BundleError(err instanceof Error ? err.message : String(err));
2609
+ }
2610
+ const skipClient = opts?.skipClient || !agent.clientEntry;
2611
+ if (!skipClient) {
2612
+ try {
2613
+ await build({
2614
+ configFile: false,
2615
+ root: aaiDir,
2616
+ logLevel: "warn",
2617
+ plugins: [preact(), tailwindcss(), viteSingleFile()],
2618
+ build: {
2619
+ outDir: buildDir,
2620
+ emptyOutDir: false,
2621
+ minify: true,
2622
+ target: "es2022"
2623
+ }
2624
+ });
2625
+ } catch (err) {
2626
+ throw new BundleError(err instanceof Error ? err.message : String(err));
2627
+ }
2628
+ }
2629
+ const worker = await fs3.readFile(path2.join(buildDir, "worker.js"), "utf-8");
2630
+ const htmlPath = skipClient ? path2.join(aaiDir, "index.html") : path2.join(buildDir, "index.html");
2631
+ const html = await fs3.readFile(htmlPath, "utf-8");
2632
+ const manifest = JSON.stringify(
2633
+ {
2634
+ transport: agentConfig.transport ?? agent.transport,
2635
+ config: agentConfig,
2636
+ toolSchemas
2637
+ },
2638
+ null,
2639
+ 2
2640
+ );
2641
+ return {
2642
+ worker,
2643
+ html,
2644
+ manifest,
2645
+ workerBytes: Buffer.byteLength(worker)
2646
+ };
2647
+ }
2648
+
2649
+ // cli/build.ts
2650
+ init_output();
2651
+ async function writeBuildOutput(agentDir, bundle) {
2652
+ const buildDir = path3.join(agentDir, ".aai", "build");
2653
+ await fs4.mkdir(buildDir, { recursive: true });
2654
+ await Promise.all([
2655
+ fs4.writeFile(path3.join(buildDir, "worker.js"), bundle.worker),
2656
+ fs4.writeFile(path3.join(buildDir, "manifest.json"), bundle.manifest),
2657
+ fs4.writeFile(path3.join(buildDir, "index.html"), bundle.html)
2658
+ ]);
2659
+ }
2660
+ async function checkAgent(agentDir) {
2661
+ const userFiles = ["agent.ts"];
2662
+ for (const f of ["client.tsx", "components.tsx"]) {
2663
+ try {
2664
+ await fs4.stat(path3.join(agentDir, f));
2665
+ userFiles.push(f);
2666
+ } catch {
2667
+ }
2668
+ }
2669
+ const checks = [{ args: ["--noEmit", ...userFiles], label: "Type-check" }];
2670
+ const results = await Promise.allSettled(
2671
+ checks.map(({ args }) => execFileAsync("npx", ["tsc", ...args], { cwd: agentDir }))
2672
+ );
2673
+ const errors = [];
2674
+ for (let i = 0; i < results.length; i++) {
2675
+ const r = results[i];
2676
+ if (r.status === "rejected") {
2677
+ const msg = r.reason.stderr?.trim() ?? String(r.reason);
2678
+ error2(`${checks[i].label}: ${msg}`);
2679
+ errors.push(checks[i].label);
2680
+ }
2681
+ }
2682
+ if (errors.length > 0) {
2683
+ throw new Error(`${errors.join(", ")} failed \u2014 fix the errors above`);
2684
+ }
2685
+ }
2686
+ async function runBuild(opts) {
2687
+ const agent = await loadAgent(opts.agentDir);
2688
+ if (!agent) {
2689
+ throw new Error("No agent found \u2014 run `aai new` first");
2690
+ }
2691
+ step("Check", agent.slug);
2692
+ await checkAgent(opts.agentDir);
2693
+ step("Bundle", agent.slug);
2694
+ let bundle;
2695
+ try {
2696
+ bundle = await bundleAgent(agent);
2697
+ } catch (err) {
2698
+ if (err instanceof BundleError) {
2699
+ error2(err.message);
2700
+ throw new Error("Bundle failed \u2014 fix the errors above");
2701
+ }
2702
+ throw err;
2703
+ }
2704
+ await writeBuildOutput(opts.agentDir, bundle);
2705
+ return { agent, bundle };
2706
+ }
2707
+
2708
+ // cli/new.tsx
2709
+ init_colors();
2710
+ import fs6 from "node:fs/promises";
2711
+ import path5 from "node:path";
2712
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
2713
+ import minimist from "minimist";
2714
+ init_new();
2715
+ var newCommandDef = {
2716
+ name: "init",
2717
+ description: "Scaffold a new agent project",
2718
+ args: [{ name: "dir", optional: true }],
2719
+ options: [
2720
+ {
2721
+ flags: "-t, --template <template>",
2722
+ description: "Template to use"
2723
+ },
2724
+ { flags: "-f, --force", description: "Overwrite existing agent.ts" },
2725
+ { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
2726
+ ]
2727
+ };
2728
+ var TEMPLATE_DESCRIPTIONS = {
2729
+ simple: "Minimal starter with search, code, and fetch tools",
2730
+ "web-researcher": "Research assistant with web search and page visits",
2731
+ "smart-research": "Phase-based research with dynamic tool filtering",
2732
+ "memory-agent": "Persistent KV storage across conversations",
2733
+ "code-interpreter": "Writes and runs JavaScript for calculations",
2734
+ "math-buddy": "Calculations, unit conversions, dice rolls",
2735
+ "health-assistant": "Medication lookup, drug interactions, BMI",
2736
+ "personal-finance": "Currency, crypto, loans, savings projections",
2737
+ "travel-concierge": "Trip planning, weather, flights, hotels",
2738
+ "night-owl": "Movie/music/book recs by mood \u2014 custom UI",
2739
+ "dispatch-center": "911 dispatch with triage \u2014 custom UI",
2740
+ "infocom-adventure": "Zork-style text adventure \u2014 custom UI",
2741
+ "embedded-assets": "FAQ bot using embedded JSON knowledge",
2742
+ support: "RAG-powered support agent for a documentation site",
2743
+ terminal: "STT-only mode for voice-driven commands"
2744
+ };
2745
+ async function selectTemplate(available) {
2746
+ const sorted = ["simple", ...available.filter((t) => t !== "simple")];
2747
+ const maxLen = Math.max(...sorted.map((t) => t.length));
2748
+ const choices = sorted.map((name) => ({
2749
+ label: `${name.padEnd(maxLen + 2)}${TEMPLATE_DESCRIPTIONS[name] ?? ""}`,
2750
+ value: name
2751
+ }));
2752
+ return await askSelect("Which template?", choices);
2753
+ }
2754
+ async function runNewCommand(args, version) {
2755
+ const parsed = minimist(args, {
2756
+ string: ["template"],
2757
+ boolean: ["force", "help", "yes"],
2758
+ alias: { t: "template", f: "force", h: "help", y: "yes" }
2759
+ });
2760
+ if (parsed.help) {
2761
+ console.log(subcommandHelp(newCommandDef, version));
2762
+ return "";
2763
+ }
2764
+ const dir = parsed._[0];
2765
+ const cwd = dir ?? (process.env.INIT_CWD || process.cwd());
2766
+ if (!parsed.force && await fileExists(path5.join(cwd, "agent.ts"))) {
2767
+ console.log(
2768
+ `agent.ts already exists in this directory. Use ${interactive("--force")} to overwrite.`
2769
+ );
2770
+ process.exit(1);
2771
+ }
2772
+ const cliDir2 = path5.dirname(fileURLToPath2(import.meta.url));
2773
+ const templatesDir = path5.join(cliDir2, "..", "templates");
2774
+ const { runNew: runNew2 } = await Promise.resolve().then(() => (init_new(), new_exports));
2775
+ const available = await listTemplates(templatesDir);
2776
+ const template = parsed.template || (parsed.yes ? "simple" : await selectTemplate(available));
2777
+ await runWithInk("Scaffolding...", async () => {
2778
+ await runNew2({
2779
+ targetDir: cwd,
2780
+ template,
2781
+ templatesDir
2782
+ });
2783
+ if (isDevMode()) {
2784
+ const monorepoRoot = path5.join(cliDir2, "..");
2785
+ const pkgJsonPath2 = path5.join(cwd, "package.json");
2786
+ const pkgJson2 = JSON.parse(await fs6.readFile(pkgJsonPath2, "utf-8"));
2787
+ for (const pkg of ["sdk", "ui"]) {
2788
+ const localPkgPath = path5.join(monorepoRoot, pkg, "package.json");
2789
+ const localPkg = JSON.parse(await fs6.readFile(localPkgPath, "utf-8"));
2790
+ const pkgName = localPkg.name;
2791
+ pkgJson2.dependencies[pkgName] = `file:${path5.join(monorepoRoot, pkg)}`;
2792
+ }
2793
+ await fs6.writeFile(pkgJsonPath2, `${JSON.stringify(pkgJson2, null, 2)}
2794
+ `);
2795
+ }
2796
+ await ensureClaudeMd(cwd);
2797
+ await ensureDependencies(cwd);
2798
+ });
2799
+ return cwd;
2800
+ }
2801
+
2802
+ // cli/deploy.tsx
2803
+ var deployCommandDef = {
2804
+ name: "deploy",
2805
+ description: "Bundle and deploy to production",
2806
+ options: [
2807
+ { flags: "-s, --server <url>", description: "Server URL" },
2808
+ {
2809
+ flags: "--local [url]",
2810
+ description: "Use local server",
2811
+ hidden: true
2812
+ },
2813
+ {
2814
+ flags: "--dry-run",
2815
+ description: "Validate and bundle without deploying"
2816
+ },
2817
+ { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
2818
+ ]
2819
+ };
2820
+ async function runDeployCommand(args, version) {
2821
+ const parsed = minimist2(args, {
2822
+ string: ["server", "local"],
2823
+ boolean: ["dry-run", "help", "yes"],
2824
+ alias: { s: "server", h: "help", y: "yes" }
2825
+ });
2826
+ if (parsed.help) {
2827
+ console.log(subcommandHelp(deployCommandDef, version));
2828
+ return;
2829
+ }
2830
+ const cwd = process.env.INIT_CWD || process.cwd();
2831
+ if (!await fileExists(path6.join(cwd, "agent.ts"))) {
2832
+ await runNewCommand(parsed.yes ? ["-y"] : [], version);
2833
+ }
2834
+ const local = parsed.local;
2835
+ const serverUrl = local !== void 0 ? typeof local === "string" && local !== "" ? local : "http://localhost:3100" : parsed.server || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
2836
+ const dryRun = parsed["dry-run"] ?? false;
2837
+ const apiKey = dryRun ? "" : await getApiKey();
2838
+ const projectConfig = await readProjectConfig(cwd);
2839
+ const slug = projectConfig?.slug ?? generateSlug();
2840
+ await runWithInk("Deploying...", async () => {
2841
+ const result = await runBuild({ agentDir: cwd });
2842
+ const deployed = await runDeploy({
2843
+ url: serverUrl,
2844
+ bundle: result.bundle,
2845
+ env: dryRun ? {} : { ASSEMBLYAI_API_KEY: apiKey },
2846
+ slug,
2847
+ dryRun,
2848
+ apiKey
2849
+ });
2850
+ await writeProjectConfig(cwd, {
2851
+ slug: deployed.slug,
2852
+ serverUrl
2853
+ });
2854
+ });
2855
+ }
2856
+
2857
+ // cli/dev.tsx
2858
+ import path8 from "node:path";
2859
+ import minimist3 from "minimist";
2860
+
2861
+ // cli/_dev.ts
2862
+ init_output();
2863
+
2864
+ // cli/_server_common.ts
2865
+ import path7 from "node:path";
2866
+ init_output();
2867
+ async function loadAgentDef(cwd) {
2868
+ const agentPath = path7.resolve(cwd, "agent.ts");
2869
+ const agentModule = await import(agentPath);
2870
+ const agentDef = agentModule.default;
2871
+ if (!agentDef || typeof agentDef !== "object" || !agentDef.name) {
2872
+ throw new Error("agent.ts must export a default agent definition (from defineAgent())");
2873
+ }
2874
+ return agentDef;
2875
+ }
2876
+ async function resolveServerEnv() {
2877
+ const env = { ...process.env };
2878
+ if (!env.ASSEMBLYAI_API_KEY) {
2879
+ try {
2880
+ env.ASSEMBLYAI_API_KEY = await getApiKey();
2881
+ } catch {
2882
+ error2("ASSEMBLYAI_API_KEY not set. Set it in your environment or run `aai env add`.");
2883
+ throw new Error("ASSEMBLYAI_API_KEY is required");
2884
+ }
2885
+ }
2886
+ return env;
2887
+ }
2888
+ async function bootServer(agentDef, html, env, port) {
2889
+ step("Start", `http://localhost:${port}`);
2890
+ const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2891
+ const server = createServer2({
2892
+ agent: agentDef,
2893
+ clientHtml: html,
2894
+ env
2895
+ });
2896
+ await server.listen(port);
2897
+ step("Ready", `http://localhost:${port}`);
2898
+ }
2899
+
2900
+ // cli/_dev.ts
2901
+ async function _startDevServer(cwd, port) {
2902
+ const agent = await loadAgent(cwd);
2903
+ if (!agent) {
2904
+ throw new Error("No agent found \u2014 run `aai new` first");
2905
+ }
2906
+ step("Bundle", agent.slug);
2907
+ let html;
2908
+ try {
2909
+ const bundle = await bundleAgent(agent);
2910
+ html = bundle.html;
2911
+ } catch (err) {
2912
+ if (err instanceof BundleError) {
2913
+ error2(err.message);
2914
+ throw new Error("Bundle failed \u2014 fix the errors above");
2915
+ }
2916
+ throw err;
2917
+ }
2918
+ step("Load", "agent.ts");
2919
+ const agentDef = await loadAgentDef(cwd);
2920
+ const env = await resolveServerEnv();
2921
+ await bootServer(agentDef, html, env, port);
2922
+ }
2923
+
2924
+ // cli/dev.tsx
2925
+ import { jsx as jsx3 } from "react/jsx-runtime";
2926
+ var devCommandDef = {
2927
+ name: "dev",
2928
+ description: "Start a local development server",
2929
+ options: [
2930
+ {
2931
+ flags: "-p, --port <number>",
2932
+ description: "Port to listen on (default: 3000)"
2933
+ },
2934
+ { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
2935
+ ]
2936
+ };
2937
+ async function runDevCommand(args, version) {
2938
+ const parsed = minimist3(args, {
2939
+ string: ["port"],
2940
+ boolean: ["help", "yes"],
2941
+ alias: { p: "port", h: "help", y: "yes" }
2942
+ });
2943
+ if (parsed.help) {
2944
+ console.log(subcommandHelp(devCommandDef, version));
2945
+ return;
2946
+ }
2947
+ const cwd = process.env.INIT_CWD || process.cwd();
2948
+ const port = Number.parseInt(parsed.port ?? "3000", 10);
2949
+ if (!await fileExists(path8.join(cwd, "agent.ts"))) {
2950
+ await runNewCommand(parsed.yes ? ["-y"] : [], version);
2951
+ }
2952
+ await getApiKey();
2953
+ await runWithInk("Bundling...", async (log) => {
2954
+ log(/* @__PURE__ */ jsx3(Step, { action: "Dev", msg: `starting on port ${port}` }));
2955
+ await _startDevServer(cwd, port);
2956
+ });
2957
+ }
2958
+
2959
+ // cli/rag.tsx
2960
+ import { render as render3, Text as Text3, useApp as useApp2 } from "ink";
2961
+ import minimist4 from "minimist";
2962
+ import pLimit from "p-limit";
2963
+ import { useEffect, useState as useState2 } from "react";
2964
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2965
+ var ragCommandDef = {
2966
+ name: "rag",
2967
+ description: "Ingest a site's llms-full.txt into the vector store",
2968
+ args: [{ name: "url" }],
2969
+ options: [
2970
+ { flags: "-s, --server <url>", description: "Server URL" },
2971
+ {
2972
+ flags: "--chunk-size <n>",
2973
+ description: "Max chunk size in tokens (default: 512)"
2974
+ },
2975
+ { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
2976
+ ]
2977
+ };
2978
+ var FETCH_TIMEOUT_MS = 6e4;
2979
+ var PAD3 = 9;
2980
+ function RagUI({ url, apiKey, serverUrl, slug, chunkSize, onError }) {
2981
+ const { exit } = useApp2();
2982
+ const { items, log } = useStepLog();
2983
+ const [progress, setProgress] = useState2(null);
2984
+ const [err, setErr] = useState2(null);
2985
+ useEffect(() => {
2986
+ (async () => {
2987
+ try {
2988
+ await runRag({
2989
+ url,
2990
+ apiKey,
2991
+ serverUrl,
2992
+ slug,
2993
+ chunkSize,
2994
+ log,
2995
+ setProgress
2996
+ });
2997
+ } catch (e) {
2998
+ const error3 = e instanceof Error ? e : new Error(String(e));
2999
+ setErr(error3.message);
3000
+ onError?.(error3);
3001
+ }
3002
+ setProgress(null);
3003
+ exit();
3004
+ })();
3005
+ }, [apiKey, chunkSize, exit, log, onError, serverUrl, slug, url]);
3006
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
3007
+ /* @__PURE__ */ jsx4(StepLog, { items }),
3008
+ err && /* @__PURE__ */ jsx4(ErrorLine, { msg: err }),
3009
+ progress && /* @__PURE__ */ jsxs3(Text3, { children: [
3010
+ " ".repeat(PAD3 + 1),
3011
+ "Upsert ",
3012
+ progress.completed,
3013
+ "/",
3014
+ progress.total,
3015
+ " (",
3016
+ Math.round(progress.completed / progress.total * 100),
3017
+ "%)"
3018
+ ] })
3019
+ ] });
3020
+ }
3021
+ async function runRag(opts) {
3022
+ const { url, apiKey, serverUrl, slug, chunkSize, log, setProgress } = opts;
3023
+ log(/* @__PURE__ */ jsx4(Step, { action: "Fetch", msg: url }));
3024
+ const resp = await fetch(url, {
3025
+ headers: { "User-Agent": "aai-cli/1.0" },
3026
+ redirect: "follow",
3027
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
3028
+ });
3029
+ if (!resp.ok) {
3030
+ throw new Error(`Failed to fetch: ${resp.status} ${resp.statusText}`);
3031
+ }
3032
+ const content = await resp.text();
3033
+ if (content.length === 0) {
3034
+ log(/* @__PURE__ */ jsx4(Warn, { msg: "File is empty" }));
3035
+ return;
3036
+ }
3037
+ log(/* @__PURE__ */ jsx4(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
3038
+ const origin = new URL(url).origin;
3039
+ const pages = splitPages(content);
3040
+ log(/* @__PURE__ */ jsx4(Step, { action: "Parsed", msg: `${pages.length} pages` }));
3041
+ const { RecursiveChunker } = await import("@chonkiejs/core");
3042
+ const chunker = await RecursiveChunker.create({ chunkSize });
3043
+ const allChunks = [];
3044
+ const siteSlug = slugify(origin);
3045
+ for (const page of pages) {
3046
+ page.body = stripNoise(page.body);
3047
+ if (!page.body) continue;
3048
+ const raw = await chunker.chunk(page.body);
3049
+ for (let i = 0; i < raw.length; i++) {
3050
+ const c = raw[i];
3051
+ const data = page.title ? `${page.title}
3052
+
3053
+ ${c.text}` : c.text;
3054
+ const id = `${siteSlug}:${slugify(page.title || "page")}:${i}`;
3055
+ allChunks.push({
3056
+ id,
3057
+ data,
3058
+ metadata: {
3059
+ source: origin,
3060
+ ...page.title ? { title: page.title } : {},
3061
+ tokenCount: c.tokenCount
3062
+ }
3063
+ });
3064
+ }
3065
+ }
3066
+ log(/* @__PURE__ */ jsx4(Step, { action: "Chunked", msg: `${allChunks.length} chunks` }));
3067
+ const vectorUrl = `${serverUrl}/${slug}/vector`;
3068
+ log(/* @__PURE__ */ jsx4(Info, { msg: `target: ${vectorUrl}` }));
3069
+ const total = allChunks.length;
3070
+ let completed = 0;
3071
+ let upserted = 0;
3072
+ let errors = 0;
3073
+ let lastError = "";
3074
+ setProgress({ completed: 0, total });
3075
+ const limit = pLimit(5);
3076
+ await Promise.all(
3077
+ allChunks.map(
3078
+ (chunk) => limit(async () => {
3079
+ try {
3080
+ const r = await fetch(vectorUrl, {
3081
+ method: "POST",
3082
+ headers: {
3083
+ "Content-Type": "application/json",
3084
+ Authorization: `Bearer ${apiKey}`
3085
+ },
3086
+ body: JSON.stringify({
3087
+ op: "upsert",
3088
+ id: chunk.id,
3089
+ data: chunk.data,
3090
+ metadata: chunk.metadata
3091
+ })
3092
+ });
3093
+ if (!r.ok) {
3094
+ lastError = await r.text();
3095
+ errors++;
3096
+ } else {
3097
+ upserted++;
3098
+ }
3099
+ } catch (err) {
3100
+ lastError = err instanceof Error ? err.message : String(err);
3101
+ errors++;
3102
+ }
3103
+ completed++;
3104
+ setProgress({ completed, total });
3105
+ })
3106
+ )
3107
+ );
3108
+ log(/* @__PURE__ */ jsx4(Step, { action: "Done", msg: `${upserted} chunks upserted` }));
3109
+ if (errors > 0) {
3110
+ log(/* @__PURE__ */ jsx4(Warn, { msg: `${errors} failed` }));
3111
+ if (lastError) log(/* @__PURE__ */ jsx4(Info, { msg: `last error: ${lastError}` }));
3112
+ }
3113
+ log(/* @__PURE__ */ jsx4(Detail, { msg: `Agent: ${slug}` }));
3114
+ }
3115
+ async function runRagCommand(args, version) {
3116
+ const parsed = minimist4(args, {
3117
+ string: ["server", "chunk-size"],
3118
+ boolean: ["help", "yes"],
3119
+ alias: { s: "server", h: "help", y: "yes" },
3120
+ stopEarly: true
3121
+ });
3122
+ if (parsed.help) {
3123
+ console.log(subcommandHelp(ragCommandDef, version));
3124
+ return;
3125
+ }
3126
+ const url = String(parsed._[0] ?? "");
3127
+ if (!url) {
3128
+ throw new Error(
3129
+ "Usage: aai rag <url>\n\nProvide the full URL to a site's llms-full.txt file"
3130
+ );
3131
+ }
3132
+ try {
3133
+ new URL(url);
3134
+ } catch {
3135
+ throw new Error(`Invalid URL: ${url}`);
3136
+ }
3137
+ const cwd = process.env.INIT_CWD || process.cwd();
3138
+ const config = await readProjectConfig(cwd);
3139
+ if (!config) {
3140
+ throw new Error("No .aai/project.json found \u2014 deploy first with `aai deploy`");
3141
+ }
3142
+ const apiKey = await getApiKey();
3143
+ const serverUrl = parsed.server || config.serverUrl || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
3144
+ const slug = config.slug;
3145
+ const chunkSize = Number.parseInt(parsed["chunk-size"] ?? "512", 10);
3146
+ let thrownError;
3147
+ const app = render3(
3148
+ /* @__PURE__ */ jsx4(
3149
+ RagUI,
3150
+ {
3151
+ url,
3152
+ apiKey,
3153
+ serverUrl,
3154
+ slug,
3155
+ chunkSize,
3156
+ onError: (e) => {
3157
+ thrownError = e;
3158
+ }
3159
+ }
3160
+ )
3161
+ );
3162
+ await app.waitUntilExit();
3163
+ if (thrownError) throw thrownError;
3164
+ }
3165
+ function splitPages(content) {
3166
+ const raw = content.split(/^\*{3,}$/m);
3167
+ const pages = [];
3168
+ for (const section of raw) {
3169
+ const trimmed = section.trim();
3170
+ if (!trimmed) continue;
3171
+ let title = "";
3172
+ let body = trimmed;
3173
+ const dashIndex = trimmed.search(/^-{3,}$/m);
3174
+ if (dashIndex !== -1) {
3175
+ const frontmatter = trimmed.slice(0, dashIndex);
3176
+ body = trimmed.slice(dashIndex).replace(/^-+$/m, "").trim();
3177
+ const titleMatch = frontmatter.match(/^title:\s*(.+)$/m);
3178
+ if (titleMatch) {
3179
+ title = titleMatch[1].trim();
3180
+ }
3181
+ }
3182
+ if (!title) {
3183
+ const titleLineMatch = body.match(/^#{1,2}\s+title:\s*(.+)$/m);
3184
+ if (titleLineMatch) {
3185
+ title = titleLineMatch[1].trim();
3186
+ body = body.replace(/^#{1,2}\s+title:\s*.+\n?/m, "").trim();
3187
+ } else {
3188
+ const headingMatch = body.match(/^(#{1,3})\s+(.+)$/m);
3189
+ if (headingMatch) {
3190
+ title = headingMatch[2].trim();
3191
+ }
3192
+ }
3193
+ }
3194
+ if (body.length > 0) {
3195
+ pages.push({ title, body });
3196
+ }
3197
+ }
3198
+ return pages;
3199
+ }
3200
+ function stripNoise(text) {
3201
+ return text.replace(/^(`{3,}|~{3,}).*[\s\S]*?^\1/gm, "").replace(/^(?:[ ]{4,}|\t).+$/gm, "").replace(/`[^`]+`/g, "").replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/<[^>]+>/g, "").replace(/^\s*\}[^}\n]*$/gm, "").replace(/^\s+$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
3202
+ }
3203
+ function slugify(s) {
3204
+ return s.replace(/^https?:\/\//, "").replace(/^#+\s*/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase().slice(0, 80);
3205
+ }
3206
+
3207
+ // cli/secret.tsx
3208
+ import minimist5 from "minimist";
3209
+ import { jsx as jsx5 } from "react/jsx-runtime";
3210
+ var secretCommandDef = {
3211
+ name: "secret",
3212
+ description: "Manage secrets",
3213
+ options: [
3214
+ { flags: "put <name>", description: "Create or update a secret" },
3215
+ { flags: "delete <name>", description: "Delete a secret" },
3216
+ { flags: "list", description: "List secret names" }
3217
+ ]
3218
+ };
3219
+ async function requireProjectConfig(cwd) {
3220
+ const config = await readProjectConfig(cwd);
3221
+ if (!config) {
3222
+ throw new Error("No .aai/project.json found \u2014 deploy first with `aai deploy`");
3223
+ }
3224
+ return config;
3225
+ }
3226
+ async function runSecretCommand(args, version) {
3227
+ const parsed = minimist5(args, {
3228
+ boolean: ["help", "yes"],
3229
+ alias: { h: "help", y: "yes" },
3230
+ stopEarly: true
3231
+ });
3232
+ if (parsed.help || parsed._.length === 0) {
3233
+ console.log(subcommandHelp(secretCommandDef, version));
3234
+ return;
3235
+ }
3236
+ const sub = String(parsed._[0]);
3237
+ const cwd = process.env.INIT_CWD || process.cwd();
3238
+ await getApiKey();
3239
+ let secretValue;
3240
+ if (sub === "put") {
3241
+ const name = String(parsed._[1] ?? "");
3242
+ if (!name) throw new Error("Usage: aai secret put <NAME>");
3243
+ secretValue = await askPassword(`Enter value for ${name}`);
3244
+ if (!secretValue) throw new Error("No value provided");
3245
+ }
3246
+ switch (sub) {
3247
+ case "put":
3248
+ await secretPut(cwd, String(parsed._[1] ?? ""), secretValue);
3249
+ break;
3250
+ case "delete":
3251
+ await secretDelete(cwd, String(parsed._[1] ?? ""));
3252
+ break;
3253
+ case "list":
3254
+ await secretList(cwd);
3255
+ break;
3256
+ default:
3257
+ throw new Error(`Unknown secret subcommand: ${sub}`);
3258
+ }
3259
+ }
3260
+ async function getServerInfo(cwd) {
3261
+ const config = await requireProjectConfig(cwd);
3262
+ const apiKey = await getApiKey();
3263
+ const serverUrl = config.serverUrl || DEFAULT_SERVER;
3264
+ const slug = config.slug;
3265
+ return { serverUrl, slug, apiKey };
3266
+ }
3267
+ async function secretPut(cwd, name, value) {
3268
+ await runWithInk("Setting...", async (log) => {
3269
+ const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
3270
+ const resp = await fetch(`${serverUrl}/${slug}/secret`, {
3271
+ method: "PUT",
3272
+ headers: {
3273
+ "Content-Type": "application/json",
3274
+ Authorization: `Bearer ${apiKey}`
3275
+ },
3276
+ body: JSON.stringify({ [name]: value })
3277
+ });
3278
+ if (!resp.ok) {
3279
+ const text = await resp.text();
3280
+ throw new Error(`Failed to set secret: ${text}`);
3281
+ }
3282
+ log(/* @__PURE__ */ jsx5(Step, { action: "Set", msg: `${name} for ${slug}` }));
3283
+ });
3284
+ }
3285
+ async function secretDelete(cwd, name) {
3286
+ if (!name) throw new Error("Usage: aai secret delete <NAME>");
3287
+ await runWithInk("Removing...", async (log) => {
3288
+ const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
3289
+ const resp = await fetch(`${serverUrl}/${slug}/secret/${name}`, {
3290
+ method: "DELETE",
3291
+ headers: { Authorization: `Bearer ${apiKey}` }
3292
+ });
3293
+ if (!resp.ok) {
3294
+ const text = await resp.text();
3295
+ throw new Error(`Failed to delete secret: ${text}`);
3296
+ }
3297
+ log(/* @__PURE__ */ jsx5(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
3298
+ });
3299
+ }
3300
+ async function secretList(cwd) {
3301
+ await runWithInk("Loading...", async (log) => {
3302
+ const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
3303
+ const resp = await fetch(`${serverUrl}/${slug}/secret`, {
3304
+ headers: { Authorization: `Bearer ${apiKey}` }
3305
+ });
3306
+ if (!resp.ok) {
3307
+ const text = await resp.text();
3308
+ throw new Error(`Failed to list secrets: ${text}`);
3309
+ }
3310
+ const { vars } = await resp.json();
3311
+ if (vars.length === 0) {
3312
+ log(/* @__PURE__ */ jsx5(StepInfo, { action: "Secrets", msg: "No secrets set" }));
3313
+ } else {
3314
+ for (const name of vars) {
3315
+ log(/* @__PURE__ */ jsx5(Detail, { msg: name }));
3316
+ }
3317
+ }
3318
+ });
3319
+ }
3320
+
3321
+ // cli/start.tsx
3322
+ import path10 from "node:path";
3323
+ import minimist6 from "minimist";
3324
+
3325
+ // cli/_start.ts
3326
+ init_output();
3327
+ import fs7 from "node:fs/promises";
3328
+ import path9 from "node:path";
3329
+ async function _startProductionServer(cwd, port) {
3330
+ const buildDir = path9.join(cwd, ".aai", "build");
3331
+ const html = await fs7.readFile(path9.join(buildDir, "index.html"), "utf-8");
3332
+ step("Load", "agent");
3333
+ const agentDef = await loadAgentDef(cwd);
3334
+ const env = await resolveServerEnv();
3335
+ await bootServer(agentDef, html, env, port);
3336
+ }
3337
+
3338
+ // cli/start.tsx
3339
+ import { jsx as jsx6 } from "react/jsx-runtime";
3340
+ var startCommandDef = {
3341
+ name: "start",
3342
+ description: "Start the production server from a build",
3343
+ options: [
3344
+ {
3345
+ flags: "-p, --port <number>",
3346
+ description: "Port to listen on (default: 3000)"
3347
+ },
3348
+ { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
3349
+ ]
3350
+ };
3351
+ async function runStartCommand(args, version) {
3352
+ const parsed = minimist6(args, {
3353
+ string: ["port"],
3354
+ boolean: ["help", "yes"],
3355
+ alias: { p: "port", h: "help", y: "yes" }
3356
+ });
3357
+ if (parsed.help) {
3358
+ console.log(subcommandHelp(startCommandDef, version));
3359
+ return;
3360
+ }
3361
+ const cwd = process.env.INIT_CWD || process.cwd();
3362
+ const port = Number.parseInt(parsed.port ?? "3000", 10);
3363
+ const buildDir = path10.join(cwd, ".aai", "build");
3364
+ if (!await fileExists(path10.join(buildDir, "worker.js"))) {
3365
+ throw new Error("No build found \u2014 run `aai build` first");
3366
+ }
3367
+ await getApiKey();
3368
+ await runWithInk("Starting...", async (log) => {
3369
+ log(/* @__PURE__ */ jsx6(Step, { action: "Start", msg: `production server on port ${port}` }));
3370
+ await _startProductionServer(cwd, port);
3371
+ });
3372
+ }
3373
+
3374
+ // cli/cli.ts
3375
+ var cliDir = path11.dirname(fileURLToPath3(import.meta.url));
3376
+ var pkgJsonPath = path11.join(cliDir, "..", "package.json");
3377
+ var pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
3378
+ var VERSION = pkgJson.version;
3379
+ async function main(args) {
3380
+ const parsed = minimist7(args, {
3381
+ boolean: ["help", "version"],
3382
+ alias: { h: "help", V: "version" },
3383
+ stopEarly: true
3384
+ });
3385
+ if (parsed.version) {
3386
+ console.log(VERSION);
3387
+ return;
3388
+ }
3389
+ if (parsed.help && parsed._.length === 0) {
3390
+ console.log(rootHelp(VERSION));
3391
+ return;
3392
+ }
3393
+ const [subcommand, ...rest] = parsed._;
3394
+ const subArgs = rest.map(String);
3395
+ switch (subcommand) {
3396
+ case "init":
3397
+ case "new":
3398
+ await runNewCommand(subArgs, VERSION);
3399
+ return;
3400
+ case "deploy":
3401
+ await runDeployCommand(subArgs, VERSION);
3402
+ return;
3403
+ case "dev":
3404
+ await runDevCommand(subArgs, VERSION);
3405
+ return;
3406
+ case "start":
3407
+ await runStartCommand(subArgs, VERSION);
3408
+ return;
3409
+ case "secret":
3410
+ await runSecretCommand(subArgs, VERSION);
3411
+ return;
3412
+ case "rag":
3413
+ await runRagCommand(subArgs, VERSION);
3414
+ return;
3415
+ case "help":
3416
+ case void 0:
3417
+ console.log(rootHelp(VERSION));
3418
+ return;
3419
+ default:
3420
+ error2(`Unknown command: ${subcommand}`);
3421
+ console.log(rootHelp(VERSION));
3422
+ process.exit(1);
3423
+ }
3424
+ }
3425
+ var isMain = process.argv[1] === fileURLToPath3(import.meta.url);
3426
+ if (isMain) {
3427
+ try {
3428
+ await main(process.argv.slice(2));
3429
+ } catch (err) {
3430
+ error2(err instanceof Error ? err.message : String(err));
3431
+ process.exit(1);
3432
+ }
3433
+ }
3434
+ export {
3435
+ main
3436
+ };