@alexkroman1/aai 0.8.3 → 0.8.5

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 (164) hide show
  1. package/dist/cli/tsconfig.tsbuildinfo +1 -0
  2. package/dist/cli.js +1215 -1706
  3. package/dist/sdk/_mock_ws.js +2 -2
  4. package/dist/sdk/_mock_ws.js.map +1 -1
  5. package/dist/sdk/_render_check.d.ts.map +1 -1
  6. package/dist/sdk/_render_check.js +29 -2
  7. package/dist/sdk/_render_check.js.map +1 -1
  8. package/dist/sdk/_utils.d.ts +4 -0
  9. package/dist/sdk/_utils.d.ts.map +1 -0
  10. package/dist/sdk/_utils.js +7 -0
  11. package/dist/sdk/_utils.js.map +1 -0
  12. package/dist/sdk/builtin_tools.d.ts +35 -11
  13. package/dist/sdk/builtin_tools.d.ts.map +1 -1
  14. package/dist/sdk/builtin_tools.js +118 -76
  15. package/dist/sdk/builtin_tools.js.map +1 -1
  16. package/dist/sdk/capnweb.d.ts +76 -47
  17. package/dist/sdk/capnweb.d.ts.map +1 -1
  18. package/dist/sdk/capnweb.js +99 -242
  19. package/dist/sdk/capnweb.js.map +1 -1
  20. package/dist/sdk/direct_executor.d.ts.map +1 -1
  21. package/dist/sdk/direct_executor.js +0 -2
  22. package/dist/sdk/direct_executor.js.map +1 -1
  23. package/dist/sdk/host.d.ts +59 -0
  24. package/dist/sdk/host.d.ts.map +1 -0
  25. package/dist/sdk/host.js +131 -0
  26. package/dist/sdk/host.js.map +1 -0
  27. package/dist/sdk/mod.d.ts +2 -4
  28. package/dist/sdk/mod.d.ts.map +1 -1
  29. package/dist/sdk/mod.js +2 -3
  30. package/dist/sdk/mod.js.map +1 -1
  31. package/dist/sdk/protocol.d.ts +33 -135
  32. package/dist/sdk/protocol.d.ts.map +1 -1
  33. package/dist/sdk/protocol.js +49 -51
  34. package/dist/sdk/protocol.js.map +1 -1
  35. package/dist/sdk/runtime.d.ts +0 -1
  36. package/dist/sdk/runtime.d.ts.map +1 -1
  37. package/dist/sdk/runtime.js +5 -24
  38. package/dist/sdk/runtime.js.map +1 -1
  39. package/dist/sdk/s2s.d.ts +14 -3
  40. package/dist/sdk/s2s.d.ts.map +1 -1
  41. package/dist/sdk/s2s.js +72 -113
  42. package/dist/sdk/s2s.js.map +1 -1
  43. package/dist/sdk/server.d.ts +1 -1
  44. package/dist/sdk/server.d.ts.map +1 -1
  45. package/dist/sdk/server.js +26 -10
  46. package/dist/sdk/server.js.map +1 -1
  47. package/dist/sdk/session.d.ts +5 -1
  48. package/dist/sdk/session.d.ts.map +1 -1
  49. package/dist/sdk/session.js +131 -137
  50. package/dist/sdk/session.js.map +1 -1
  51. package/dist/sdk/tsconfig.tsbuildinfo +1 -0
  52. package/dist/sdk/types.d.ts +30 -3
  53. package/dist/sdk/types.d.ts.map +1 -1
  54. package/dist/sdk/types.js +37 -0
  55. package/dist/sdk/types.js.map +1 -1
  56. package/dist/sdk/winterc_server.d.ts +0 -1
  57. package/dist/sdk/winterc_server.d.ts.map +1 -1
  58. package/dist/sdk/winterc_server.js +0 -1
  59. package/dist/sdk/winterc_server.js.map +1 -1
  60. package/dist/sdk/worker_entry.d.ts +3 -11
  61. package/dist/sdk/worker_entry.d.ts.map +1 -1
  62. package/dist/sdk/worker_entry.js +8 -18
  63. package/dist/sdk/worker_entry.js.map +1 -1
  64. package/dist/sdk/worker_shim.d.ts +5 -6
  65. package/dist/sdk/worker_shim.d.ts.map +1 -1
  66. package/dist/sdk/worker_shim.js +93 -136
  67. package/dist/sdk/worker_shim.js.map +1 -1
  68. package/dist/sdk/ws_handler.d.ts +1 -3
  69. package/dist/sdk/ws_handler.d.ts.map +1 -1
  70. package/dist/sdk/ws_handler.js +14 -25
  71. package/dist/sdk/ws_handler.js.map +1 -1
  72. package/dist/ui/_cn.d.ts +5 -0
  73. package/dist/ui/_cn.d.ts.map +1 -0
  74. package/dist/ui/_cn.js +22 -0
  75. package/dist/ui/_cn.js.map +1 -0
  76. package/dist/ui/_components/app.d.ts +3 -1
  77. package/dist/ui/_components/app.d.ts.map +1 -1
  78. package/dist/ui/_components/app.js +2 -2
  79. package/dist/ui/_components/app.js.map +1 -1
  80. package/dist/ui/_components/button.d.ts +11 -0
  81. package/dist/ui/_components/button.d.ts.map +1 -0
  82. package/dist/ui/_components/button.js +17 -0
  83. package/dist/ui/_components/button.js.map +1 -0
  84. package/dist/ui/_components/chat_view.d.ts +3 -1
  85. package/dist/ui/_components/chat_view.d.ts.map +1 -1
  86. package/dist/ui/_components/chat_view.js +4 -2
  87. package/dist/ui/_components/chat_view.js.map +1 -1
  88. package/dist/ui/_components/controls.d.ts +3 -1
  89. package/dist/ui/_components/controls.d.ts.map +1 -1
  90. package/dist/ui/_components/controls.js +4 -5
  91. package/dist/ui/_components/controls.js.map +1 -1
  92. package/dist/ui/_components/error_banner.d.ts +2 -1
  93. package/dist/ui/_components/error_banner.d.ts.map +1 -1
  94. package/dist/ui/_components/error_banner.js +3 -2
  95. package/dist/ui/_components/error_banner.js.map +1 -1
  96. package/dist/ui/_components/message_bubble.d.ts +2 -1
  97. package/dist/ui/_components/message_bubble.d.ts.map +1 -1
  98. package/dist/ui/_components/message_bubble.js +5 -3
  99. package/dist/ui/_components/message_bubble.js.map +1 -1
  100. package/dist/ui/_components/message_list.d.ts +3 -1
  101. package/dist/ui/_components/message_list.d.ts.map +1 -1
  102. package/dist/ui/_components/message_list.js +7 -15
  103. package/dist/ui/_components/message_list.js.map +1 -1
  104. package/dist/ui/_components/sidebar_layout.d.ts +2 -1
  105. package/dist/ui/_components/sidebar_layout.d.ts.map +1 -1
  106. package/dist/ui/_components/sidebar_layout.js +5 -7
  107. package/dist/ui/_components/sidebar_layout.js.map +1 -1
  108. package/dist/ui/_components/start_screen.d.ts +2 -1
  109. package/dist/ui/_components/start_screen.d.ts.map +1 -1
  110. package/dist/ui/_components/start_screen.js +5 -2
  111. package/dist/ui/_components/start_screen.js.map +1 -1
  112. package/dist/ui/_components/state_indicator.d.ts +2 -1
  113. package/dist/ui/_components/state_indicator.d.ts.map +1 -1
  114. package/dist/ui/_components/state_indicator.js +3 -2
  115. package/dist/ui/_components/state_indicator.js.map +1 -1
  116. package/dist/ui/_components/thinking_indicator.d.ts +3 -1
  117. package/dist/ui/_components/thinking_indicator.d.ts.map +1 -1
  118. package/dist/ui/_components/thinking_indicator.js +4 -2
  119. package/dist/ui/_components/thinking_indicator.js.map +1 -1
  120. package/dist/ui/_components/tool_call_block.d.ts +2 -1
  121. package/dist/ui/_components/tool_call_block.d.ts.map +1 -1
  122. package/dist/ui/_components/tool_call_block.js +13 -25
  123. package/dist/ui/_components/tool_call_block.js.map +1 -1
  124. package/dist/ui/_components/transcript.d.ts +2 -1
  125. package/dist/ui/_components/transcript.d.ts.map +1 -1
  126. package/dist/ui/_components/transcript.js +3 -2
  127. package/dist/ui/_components/transcript.js.map +1 -1
  128. package/dist/ui/_jsdom_setup.d.ts +1 -0
  129. package/dist/ui/_jsdom_setup.d.ts.map +1 -0
  130. package/dist/ui/_jsdom_setup.js +6 -0
  131. package/dist/ui/_jsdom_setup.js.map +1 -0
  132. package/dist/ui/audio.d.ts.map +1 -1
  133. package/dist/ui/audio.js +4 -4
  134. package/dist/ui/audio.js.map +1 -1
  135. package/dist/ui/components.d.ts +13 -55
  136. package/dist/ui/components.d.ts.map +1 -1
  137. package/dist/ui/components.js +13 -42
  138. package/dist/ui/components.js.map +1 -1
  139. package/dist/ui/components_mod.d.ts +14 -3
  140. package/dist/ui/components_mod.d.ts.map +1 -1
  141. package/dist/ui/components_mod.js +14 -3
  142. package/dist/ui/components_mod.js.map +1 -1
  143. package/dist/ui/mod.d.ts +1 -8
  144. package/dist/ui/mod.d.ts.map +1 -1
  145. package/dist/ui/mod.js +1 -5
  146. package/dist/ui/mod.js.map +1 -1
  147. package/dist/ui/mount.d.ts +1 -1
  148. package/dist/ui/mount.d.ts.map +1 -1
  149. package/dist/ui/mount.js +1 -0
  150. package/dist/ui/mount.js.map +1 -1
  151. package/dist/ui/session.d.ts +0 -2
  152. package/dist/ui/session.d.ts.map +1 -1
  153. package/dist/ui/session.js +9 -18
  154. package/dist/ui/session.js.map +1 -1
  155. package/dist/ui/signals.d.ts +8 -3
  156. package/dist/ui/signals.d.ts.map +1 -1
  157. package/dist/ui/signals.js +22 -11
  158. package/dist/ui/signals.js.map +1 -1
  159. package/dist/ui/tsconfig.tsbuildinfo +1 -0
  160. package/dist/ui/worklets/playback-processor.js +3 -3
  161. package/package.json +37 -17
  162. package/templates/_shared/CLAUDE.md +4 -7
  163. package/templates/dispatch-center/agent.ts +85 -397
  164. package/templates/solo-rpg/agent.ts +3 -3
package/dist/cli.js CHANGED
@@ -9,17 +9,143 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
- // cli/_colors.ts
12
+ // sdk/_utils.ts
13
+ function errorMessage(err) {
14
+ return err instanceof Error ? err.message : String(err);
15
+ }
16
+ var init_utils = __esm({
17
+ "sdk/_utils.ts"() {
18
+ "use strict";
19
+ }
20
+ });
21
+
22
+ // cli/_ink.tsx
23
+ import { Spinner, StatusMessage } from "@inkjs/ui";
13
24
  import chalk from "chalk";
25
+ import { Box, render, Static, Text, useApp } from "ink";
26
+ import React, { useRef, useState } from "react";
27
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
14
28
  function primary(s) {
15
29
  return chalk.hex(COLORS.primary)(s);
16
30
  }
17
31
  function interactive(s) {
18
32
  return chalk.hex(COLORS.interactive)(s);
19
33
  }
34
+ function StepBase({ action, msg, color }) {
35
+ return /* @__PURE__ */ jsxs(Text, { children: [
36
+ /* @__PURE__ */ jsx(Text, { bold: true, color, children: action }),
37
+ /* @__PURE__ */ jsxs(Text, { children: [
38
+ " ",
39
+ msg
40
+ ] })
41
+ ] });
42
+ }
43
+ function Step({ action, msg }) {
44
+ return /* @__PURE__ */ jsx(StepBase, { action, msg, color: COLORS.primary });
45
+ }
46
+ function StepInfo({ action, msg }) {
47
+ return /* @__PURE__ */ jsx(StepBase, { action, msg, color: COLORS.interactive });
48
+ }
49
+ function Info({ msg }) {
50
+ return /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
51
+ " ",
52
+ msg
53
+ ] });
54
+ }
55
+ function Detail({ msg }) {
56
+ return /* @__PURE__ */ jsxs(Text, { children: [
57
+ " ",
58
+ msg
59
+ ] });
60
+ }
61
+ function Warn({ msg }) {
62
+ return /* @__PURE__ */ jsx(StatusMessage, { variant: "warning", children: msg });
63
+ }
64
+ function ErrorLine({ msg }) {
65
+ return /* @__PURE__ */ jsx(StatusMessage, { variant: "error", children: msg });
66
+ }
67
+ function StepLog({ items }) {
68
+ return /* @__PURE__ */ jsx(Static, { items, children: (item) => /* @__PURE__ */ jsx(Box, { children: item.node }, item.id) });
69
+ }
70
+ function useStepLog() {
71
+ const [items, setItems] = useState([]);
72
+ const nextId = useRef(0);
73
+ const log = (node) => {
74
+ const id = nextId.current++;
75
+ setItems((prev) => [...prev, { id, node }]);
76
+ };
77
+ return { items, log };
78
+ }
79
+ function CommandRunner({
80
+ run,
81
+ onError
82
+ }) {
83
+ const { exit } = useApp();
84
+ const { items, log } = useStepLog();
85
+ const [spinning, setSpinning] = useState(true);
86
+ const [currentStep, setCurrentStep] = useState(null);
87
+ const [statusLine, setStatusLine] = useState(null);
88
+ const [err, setErr] = useState(null);
89
+ const currentStepRef = useRef(null);
90
+ const wrappedLog = (node) => {
91
+ if (currentStepRef.current) {
92
+ log(currentStepRef.current);
93
+ }
94
+ currentStepRef.current = node;
95
+ setCurrentStep(node);
96
+ };
97
+ const started = useRef(false);
98
+ React.useEffect(() => {
99
+ if (started.current) return;
100
+ started.current = true;
101
+ (async () => {
102
+ try {
103
+ await run({ log: wrappedLog, setStatus: setStatusLine });
104
+ } catch (e) {
105
+ const error = e instanceof Error ? e : new Error(String(e));
106
+ setErr(error.message);
107
+ onError?.(error);
108
+ }
109
+ if (currentStepRef.current) {
110
+ log(currentStepRef.current);
111
+ currentStepRef.current = null;
112
+ }
113
+ setCurrentStep(null);
114
+ setStatusLine(null);
115
+ setSpinning(false);
116
+ setTimeout(() => exit(), 0);
117
+ })();
118
+ });
119
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
120
+ /* @__PURE__ */ jsx(StepLog, { items }),
121
+ err && /* @__PURE__ */ jsx(ErrorLine, { msg: err }),
122
+ spinning && currentStep && /* @__PURE__ */ jsxs(Box, { children: [
123
+ /* @__PURE__ */ jsx(Spinner, {}),
124
+ /* @__PURE__ */ jsx(Text, { children: " " }),
125
+ currentStep
126
+ ] }),
127
+ spinning && statusLine && /* @__PURE__ */ jsx(Box, { children: statusLine })
128
+ ] });
129
+ }
130
+ async function runWithInk(fn) {
131
+ let thrownError;
132
+ const app = render(
133
+ /* @__PURE__ */ jsx(
134
+ CommandRunner,
135
+ {
136
+ onError: (e) => {
137
+ thrownError = e;
138
+ },
139
+ run: fn
140
+ }
141
+ )
142
+ );
143
+ await app.waitUntilExit();
144
+ if (thrownError) throw thrownError;
145
+ }
20
146
  var COLORS;
21
- var init_colors = __esm({
22
- "cli/_colors.ts"() {
147
+ var init_ink = __esm({
148
+ "cli/_ink.tsx"() {
23
149
  "use strict";
24
150
  if (chalk.level === 0 && !process.env.NO_COLOR) {
25
151
  const ct = process.env.COLORTERM;
@@ -43,200 +169,55 @@ var init_colors = __esm({
43
169
  }
44
170
  });
45
171
 
46
- // cli/_help.ts
47
- import chalk2 from "chalk";
48
- function rootHelp(version) {
49
- const lines = [];
50
- lines.push("");
51
- lines.push(
52
- ` ${primary(chalk2.bold(" \u2584\u2580\u2588 \u2584\u2580\u2588 \u2588"))} ${chalk2.dim("Voice agent development kit")}`
53
- );
54
- lines.push(` ${primary(chalk2.bold(" \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588"))} ${primary(`v${version}`)}`);
55
- lines.push("");
56
- lines.push(
57
- ` ${chalk2.bold(interactive("Usage"))} ${primary("aai")} ${chalk2.dim("<command> [options]")}`
58
- );
59
- lines.push("");
60
- lines.push(` ${chalk2.bold(interactive("Commands"))}`);
61
- lines.push("");
62
- const cmds = [
63
- ["init", "[dir]", "Scaffold a new agent project"],
64
- ["dev", "", "Start a local development server"],
65
- ["build", "", "Bundle and validate (no server or deploy)"],
66
- ["deploy", "", "Bundle and deploy to production"],
67
- ["start", "", "Start production server from build"],
68
- ["secret", "<cmd>", "Manage secrets"],
69
- ["rag", "<url>", "Ingest a site into the vector store"]
70
- ];
71
- for (const [name, args, desc] of cmds) {
72
- const nameStr = interactive(name.padEnd(8));
73
- const argsStr = args ? primary(args.padEnd(6)) : " ";
74
- lines.push(` ${nameStr} ${argsStr} ${chalk2.dim(desc)}`);
75
- }
76
- lines.push("");
77
- lines.push(` ${chalk2.bold(interactive("Options"))}`);
78
- lines.push("");
79
- lines.push(
80
- ` ${interactive("-h")}${chalk2.dim(",")} ${interactive("--help")} ${chalk2.dim(
81
- "Show this help"
82
- )}`
83
- );
84
- lines.push(
85
- ` ${interactive("-V")}${chalk2.dim(",")} ${interactive("--version")} ${chalk2.dim(
86
- "Show the version number"
87
- )}`
88
- );
89
- lines.push("");
90
- lines.push(` ${chalk2.bold(interactive("Getting started"))}`);
91
- lines.push("");
92
- lines.push(` ${chalk2.dim("$")} ${primary("aai init")} ${interactive("my-agent")}`);
93
- lines.push(` ${chalk2.dim("$")} ${primary("cd")} ${interactive("my-agent")}`);
94
- lines.push(` ${chalk2.dim("$")} ${primary("aai dev")}`);
95
- lines.push("");
96
- return lines.join("\n");
97
- }
98
- function subcommandHelp(cmd, version) {
99
- const lines = [];
100
- lines.push("");
101
- lines.push(
102
- ` ${primary(chalk2.bold("aai"))} ${interactive(chalk2.bold(cmd.name))}${version ? chalk2.dim(` v${version}`) : ""}`
103
- );
104
- lines.push(` ${chalk2.dim(cmd.description)}`);
105
- lines.push("");
106
- if (cmd.args && cmd.args.length > 0) {
107
- lines.push(` ${chalk2.bold(interactive("Arguments"))}`);
108
- lines.push("");
109
- for (const arg of cmd.args) {
110
- const label = arg.optional ? primary(`[${arg.name}]`) : primary(`<${arg.name}>`);
111
- lines.push(` ${label}`);
112
- }
113
- lines.push("");
114
- }
115
- const visibleOptions = (cmd.options ?? []).filter((o) => !o.hidden);
116
- if (visibleOptions.length > 0) {
117
- lines.push(` ${chalk2.bold(interactive("Options"))}`);
118
- lines.push("");
119
- for (const opt of visibleOptions) {
120
- lines.push(` ${interactive(opt.flags)}`);
121
- lines.push(` ${chalk2.dim(opt.description)}`);
122
- }
123
- lines.push(` ${interactive("-h")}${chalk2.dim(",")} ${interactive("--help")}`);
124
- lines.push(` ${chalk2.dim("Show this help")}`);
125
- lines.push("");
126
- }
127
- return lines.join("\n");
128
- }
129
- var init_help = __esm({
130
- "cli/_help.ts"() {
131
- "use strict";
132
- init_colors();
133
- }
134
- });
135
-
136
172
  // cli/_prompts.tsx
137
173
  import { ConfirmInput, PasswordInput, Select, TextInput } from "@inkjs/ui";
138
- import { Box, render, Text } from "ink";
139
- import { jsx, jsxs } from "react/jsx-runtime";
140
- async function askPassword(message) {
141
- return new Promise((resolve) => {
142
- const app = render(
143
- /* @__PURE__ */ jsxs(Box, { children: [
144
- /* @__PURE__ */ jsxs(Text, { children: [
145
- message,
146
- ": "
147
- ] }),
148
- /* @__PURE__ */ jsx(
149
- PasswordInput,
150
- {
151
- onSubmit: (value) => {
152
- resolve(value);
153
- app.unmount();
154
- }
155
- }
156
- )
157
- ] })
158
- );
159
- });
160
- }
161
- async function askText(message, defaultValue) {
174
+ import { Box as Box2, render as render2, Text as Text2 } from "ink";
175
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
176
+ function inkPrompt(ui) {
162
177
  return new Promise((resolve) => {
163
- const app = render(
164
- /* @__PURE__ */ jsxs(Box, { children: [
165
- /* @__PURE__ */ jsxs(Text, { color: COLORS.interactive, children: [
166
- message,
167
- " \u203A "
168
- ] }),
169
- /* @__PURE__ */ jsx(
170
- TextInput,
171
- {
172
- placeholder: defaultValue,
173
- onSubmit: (value) => {
174
- resolve(value || defaultValue);
175
- app.unmount();
176
- app.clear();
177
- }
178
- }
179
- )
180
- ] })
178
+ const app = render2(
179
+ ui((value) => {
180
+ resolve(value);
181
+ app.unmount();
182
+ })
181
183
  );
182
184
  });
183
185
  }
184
- async function askEnter(message) {
185
- return new Promise((resolve) => {
186
- const app = render(
187
- /* @__PURE__ */ jsxs(Box, { children: [
188
- /* @__PURE__ */ jsx(Text, { color: COLORS.interactive, children: message }),
189
- /* @__PURE__ */ jsx(
190
- TextInput,
191
- {
192
- placeholder: "",
193
- onSubmit: () => {
194
- resolve();
195
- app.unmount();
196
- }
197
- }
198
- )
199
- ] })
200
- );
201
- });
186
+ function askPassword(message) {
187
+ return inkPrompt((done) => /* @__PURE__ */ jsxs2(Box2, { children: [
188
+ /* @__PURE__ */ jsxs2(Text2, { children: [
189
+ message,
190
+ ": "
191
+ ] }),
192
+ /* @__PURE__ */ jsx2(PasswordInput, { onSubmit: done })
193
+ ] }));
194
+ }
195
+ function askText(message, defaultValue) {
196
+ return inkPrompt((done) => /* @__PURE__ */ jsxs2(Box2, { children: [
197
+ /* @__PURE__ */ jsxs2(Text2, { color: COLORS.interactive, children: [
198
+ message,
199
+ " \u203A "
200
+ ] }),
201
+ /* @__PURE__ */ jsx2(TextInput, { placeholder: defaultValue, onSubmit: (value) => done(value || defaultValue) })
202
+ ] }));
202
203
  }
203
204
  var init_prompts = __esm({
204
205
  "cli/_prompts.tsx"() {
205
206
  "use strict";
206
- init_colors();
207
+ init_ink();
207
208
  }
208
209
  });
209
210
 
210
211
  // cli/_discover.ts
211
- var discover_exports = {};
212
- __export(discover_exports, {
213
- DEFAULT_SERVER: () => DEFAULT_SERVER,
214
- fileExists: () => fileExists,
215
- generateSlug: () => generateSlug,
216
- getApiKey: () => getApiKey,
217
- isDevMode: () => isDevMode,
218
- loadAgent: () => loadAgent,
219
- readProjectConfig: () => readProjectConfig,
220
- writeProjectConfig: () => writeProjectConfig
221
- });
222
- import { accessSync } from "node:fs";
223
212
  import fs from "node:fs/promises";
224
213
  import path from "node:path";
225
214
  import { humanId } from "human-id";
226
- function isDevMode() {
227
- const script = process.argv[1] ?? "";
228
- if (script.endsWith(".ts") || script.endsWith(".tsx")) return true;
229
- if (script.includes("/dist/") && !script.includes("node_modules")) {
230
- const dir = path.dirname(path.dirname(script));
231
- try {
232
- accessSync(path.join(dir, "sdk"));
233
- accessSync(path.join(dir, "cli"));
234
- return true;
235
- } catch {
236
- return false;
237
- }
238
- }
239
- return false;
215
+ function resolveCwd() {
216
+ return process.env.INIT_CWD || process.cwd();
217
+ }
218
+ function isDevMode(script) {
219
+ script ??= process.argv[1] ?? "";
220
+ return script.endsWith(".ts") || script.endsWith(".tsx");
240
221
  }
241
222
  function generateSlug() {
242
223
  return humanId({ separator: "-", capitalize: false });
@@ -284,6 +265,18 @@ async function writeProjectConfig(agentDir, data) {
284
265
  await fs.writeFile(path.join(aaiDir, "project.json"), `${JSON.stringify(data, null, 2)}
285
266
  `);
286
267
  }
268
+ async function getServerInfo(cwd, explicitServer, explicitApiKey) {
269
+ const config = await readProjectConfig(cwd);
270
+ if (!config) {
271
+ throw new Error("No .aai/project.json found \u2014 deploy first with `aai deploy`");
272
+ }
273
+ const apiKey = explicitApiKey ?? await getApiKey();
274
+ const serverUrl = resolveServerUrl(explicitServer, config.serverUrl);
275
+ return { serverUrl, slug: config.slug, apiKey };
276
+ }
277
+ function resolveServerUrl(explicit, configUrl) {
278
+ return explicit || configUrl || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
279
+ }
287
280
  async function fileExists(p) {
288
281
  try {
289
282
  await fs.access(p);
@@ -316,90 +309,173 @@ var init_discover = __esm({
316
309
  }
317
310
  });
318
311
 
319
- // cli/_bundler.ts
312
+ // cli/_init.ts
313
+ var init_exports = {};
314
+ __export(init_exports, {
315
+ listTemplates: () => listTemplates,
316
+ runInit: () => runInit
317
+ });
320
318
  import fs2 from "node:fs/promises";
321
319
  import path2 from "node:path";
320
+ async function listTemplates(dir) {
321
+ const templates = [];
322
+ const entries = await fs2.readdir(dir, { withFileTypes: true });
323
+ for (const entry of entries) {
324
+ if (entry.isDirectory() && !entry.name.startsWith("_")) {
325
+ templates.push(entry.name);
326
+ }
327
+ }
328
+ return templates.sort();
329
+ }
330
+ async function copyDirNoOverwrite(src, dest) {
331
+ const entries = await fs2.readdir(src, { recursive: true, withFileTypes: true });
332
+ for (const entry of entries) {
333
+ if (!entry.isFile()) continue;
334
+ const rel = path2.relative(src, path2.join(entry.parentPath, entry.name));
335
+ const destPath = path2.join(dest, rel);
336
+ await fs2.mkdir(path2.dirname(destPath), { recursive: true });
337
+ try {
338
+ await fs2.copyFile(path2.join(src, rel), destPath, fs2.constants.COPYFILE_EXCL);
339
+ } catch (err) {
340
+ if (err.code !== "EEXIST") throw err;
341
+ }
342
+ }
343
+ }
344
+ async function runInit(opts) {
345
+ const { targetDir, template, templatesDir } = opts;
346
+ const available = await listTemplates(templatesDir);
347
+ if (!available.includes(template)) {
348
+ throw new Error(`unknown template '${template}' -- available: ${available.join(", ")}`);
349
+ }
350
+ await fs2.cp(path2.join(templatesDir, template), targetDir, { recursive: true, force: true });
351
+ await copyDirNoOverwrite(path2.join(templatesDir, "_shared"), targetDir);
352
+ try {
353
+ await fs2.copyFile(path2.join(targetDir, ".env.example"), path2.join(targetDir, ".env"));
354
+ } catch {
355
+ }
356
+ const readmePath = path2.join(targetDir, "README.md");
357
+ const slug = path2.basename(path2.resolve(targetDir));
358
+ const readme = `# ${slug}
359
+
360
+ A voice agent built with [aai](https://github.com/anthropics/aai).
361
+
362
+ ## Getting started
363
+
364
+ \`\`\`sh
365
+ npm install # Install dependencies
366
+ npm run dev # Run locally (opens browser)
367
+ npm run deploy # Deploy to production
368
+ \`\`\`
369
+
370
+ ## Environment variables
371
+
372
+ Secrets are managed on the server, not in local files:
373
+
374
+ \`\`\`sh
375
+ aai env add MY_KEY # Set a secret (prompts for value)
376
+ aai env ls # List secret names
377
+ aai env pull # Pull names into .env for reference
378
+ aai env rm MY_KEY # Remove a secret
379
+ \`\`\`
380
+
381
+ Access secrets in your agent via \`ctx.env.MY_KEY\`.
382
+
383
+ ## Learn more
384
+
385
+ See \`CLAUDE.md\` for the full agent API reference.
386
+ `;
387
+ try {
388
+ await fs2.writeFile(readmePath, readme, { flag: "wx" });
389
+ } catch (err) {
390
+ if (err.code !== "EEXIST") throw err;
391
+ }
392
+ return targetDir;
393
+ }
394
+ var init_init = __esm({
395
+ "cli/_init.ts"() {
396
+ "use strict";
397
+ }
398
+ });
399
+
400
+ // cli/_bundler.ts
401
+ import fs3 from "node:fs/promises";
402
+ import path3 from "node:path";
322
403
  import preact from "@preact/preset-vite";
323
404
  import tailwindcss from "@tailwindcss/vite";
324
405
  import { build } from "vite";
325
- async function readDirRecursive(dir, base = dir) {
326
- const files = {};
327
- let names;
406
+ function workerEntryPlugin() {
407
+ const virtualId = "virtual:worker-entry";
408
+ const resolvedId = `\0${virtualId}`;
409
+ return {
410
+ name: "aai-worker-entry",
411
+ resolveId(source) {
412
+ return source === virtualId ? resolvedId : null;
413
+ },
414
+ load(id) {
415
+ if (id !== resolvedId) return null;
416
+ return [
417
+ `import agent from "./agent.ts";`,
418
+ `import { initWorker } from "@alexkroman1/aai/worker-shim";`,
419
+ `initWorker(agent);`
420
+ ].join("\n");
421
+ }
422
+ };
423
+ }
424
+ async function readDirFiles(dir) {
425
+ let entries;
328
426
  try {
329
- names = await fs2.readdir(dir);
427
+ entries = await fs3.readdir(dir, { recursive: true, withFileTypes: true });
330
428
  } catch {
331
- return files;
332
- }
333
- for (const name of names) {
334
- const full = path2.join(dir, name);
335
- const stat = await fs2.stat(full);
336
- const entry = { name, isDirectory: () => stat.isDirectory() };
337
- if (entry.isDirectory()) {
338
- Object.assign(files, await readDirRecursive(full, base));
339
- } else {
340
- const rel = path2.relative(base, full);
341
- files[rel] = await fs2.readFile(full, "utf-8");
342
- }
429
+ return {};
343
430
  }
431
+ const files = {};
432
+ await Promise.all(
433
+ entries.filter((e) => e.isFile()).map(async (e) => {
434
+ const full = path3.join(e.parentPath, e.name);
435
+ files[path3.relative(dir, full)] = await fs3.readFile(full, "utf-8");
436
+ })
437
+ );
344
438
  return files;
345
439
  }
346
440
  async function bundleAgent(agent, opts) {
347
- const aaiDir = path2.join(agent.dir, ".aai");
348
- const buildDir = path2.join(aaiDir, "build");
349
- const clientDir = path2.join(aaiDir, "client");
350
- await fs2.mkdir(aaiDir, { recursive: true });
351
- const workerEntry = path2.join(aaiDir, "_worker_entry.ts");
352
- await fs2.writeFile(
353
- workerEntry,
354
- [
355
- `import agent from "../agent.ts";`,
356
- `import { initWorker } from "@alexkroman1/aai/worker-shim";`,
357
- `initWorker(agent);`
358
- ].join("\n")
359
- );
441
+ const aaiDir = path3.join(agent.dir, ".aai");
442
+ const buildDir = path3.join(aaiDir, "build");
443
+ const clientDir = path3.join(aaiDir, "client");
444
+ const devMode = isDevMode();
445
+ const devResolve = devMode ? { conditions: ["source"] } : {};
360
446
  try {
361
447
  await build({
362
448
  configFile: false,
363
449
  root: agent.dir,
364
450
  logLevel: "warn",
451
+ plugins: [workerEntryPlugin()],
452
+ resolve: devResolve,
365
453
  build: {
454
+ rollupOptions: {
455
+ input: "virtual:worker-entry",
456
+ output: { format: "es", entryFileNames: "worker.js" }
457
+ },
366
458
  outDir: buildDir,
367
459
  emptyOutDir: true,
368
460
  minify: true,
369
- target: "es2022",
370
- rollupOptions: {
371
- input: workerEntry,
372
- output: {
373
- format: "es",
374
- entryFileNames: "worker.js",
375
- inlineDynamicImports: true
376
- }
377
- }
461
+ target: "es2022"
378
462
  }
379
463
  });
380
464
  } catch (err) {
381
- throw new BundleError(err instanceof Error ? err.message : String(err));
465
+ throw new BundleError(errorMessage(err));
382
466
  }
383
467
  const skipClient = opts?.skipClient || !agent.clientEntry;
384
468
  if (!skipClient) {
385
- const devAlias = {};
386
- if (isDevMode()) {
387
- const monorepoRoot = path2.resolve(import.meta.dirname ?? __dirname, "..");
388
- devAlias["@alexkroman1/aai/ui/styles.css"] = path2.join(monorepoRoot, "ui/styles.css");
389
- devAlias["@alexkroman1/aai/ui"] = path2.join(monorepoRoot, "ui/mod.ts");
390
- devAlias["@alexkroman1/aai"] = path2.join(monorepoRoot, "sdk/mod.ts");
391
- const userPreact = path2.join(agent.dir, "node_modules/preact");
392
- const userSignals = path2.join(agent.dir, "node_modules/@preact/signals");
393
- devAlias.preact = userPreact;
394
- devAlias["@preact/signals"] = userSignals;
395
- }
396
469
  try {
397
470
  await build({
398
471
  root: agent.dir,
399
472
  base: "./",
400
473
  logLevel: "warn",
401
474
  plugins: [preact(), tailwindcss()],
402
- ...Object.keys(devAlias).length > 0 && { resolve: { alias: devAlias } },
475
+ resolve: {
476
+ ...devResolve,
477
+ ...devMode && { dedupe: ["preact", "@preact/signals"] }
478
+ },
403
479
  build: {
404
480
  outDir: clientDir,
405
481
  emptyOutDir: true,
@@ -408,11 +484,11 @@ async function bundleAgent(agent, opts) {
408
484
  }
409
485
  });
410
486
  } catch (err) {
411
- throw new BundleError(err instanceof Error ? err.message : String(err));
487
+ throw new BundleError(errorMessage(err));
412
488
  }
413
489
  }
414
- const worker = await fs2.readFile(path2.join(buildDir, "worker.js"), "utf-8");
415
- const clientFiles = await readDirRecursive(clientDir);
490
+ const worker = await fs3.readFile(path3.join(buildDir, "worker.js"), "utf-8");
491
+ const clientFiles = await readDirFiles(clientDir);
416
492
  return {
417
493
  worker,
418
494
  clientFiles,
@@ -424,6 +500,7 @@ var BundleError;
424
500
  var init_bundler = __esm({
425
501
  "cli/_bundler.ts"() {
426
502
  "use strict";
503
+ init_utils();
427
504
  init_discover();
428
505
  BundleError = class extends Error {
429
506
  constructor(message) {
@@ -434,280 +511,68 @@ var init_bundler = __esm({
434
511
  }
435
512
  });
436
513
 
437
- // cli/_ink.tsx
438
- import { Spinner } from "@inkjs/ui";
439
- import { Box as Box2, render as render2, Static, Text as Text2, useApp } from "ink";
440
- import React, { useRef, useState } from "react";
441
- import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
442
- function Step({ action, msg }) {
443
- return /* @__PURE__ */ jsxs2(Text2, { children: [
444
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.primary, children: action }),
445
- /* @__PURE__ */ jsxs2(Text2, { children: [
446
- " ",
447
- msg
448
- ] })
449
- ] });
450
- }
451
- function StepInfo({ action, msg }) {
452
- return /* @__PURE__ */ jsxs2(Text2, { children: [
453
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.interactive, children: action }),
454
- /* @__PURE__ */ jsxs2(Text2, { children: [
455
- " ",
456
- msg
457
- ] })
458
- ] });
459
- }
460
- function Info({ msg }) {
461
- return /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
462
- " ",
463
- msg
464
- ] });
465
- }
466
- function Detail({ msg }) {
467
- return /* @__PURE__ */ jsxs2(Text2, { children: [
468
- " ",
469
- msg
470
- ] });
471
- }
472
- function Warn({ msg }) {
473
- return /* @__PURE__ */ jsxs2(Text2, { children: [
474
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.warning, children: "\u25B2" }),
475
- /* @__PURE__ */ jsxs2(Text2, { children: [
476
- " ",
477
- msg
478
- ] })
479
- ] });
480
- }
481
- function ErrorLine({ msg }) {
482
- return /* @__PURE__ */ jsxs2(Text2, { children: [
483
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.error, children: "\u2717" }),
484
- /* @__PURE__ */ jsxs2(Text2, { children: [
485
- " ",
486
- msg
487
- ] })
488
- ] });
489
- }
490
- function StepLog({ items }) {
491
- return /* @__PURE__ */ jsx2(Static, { items, children: (item) => /* @__PURE__ */ jsx2(Box2, { children: item.node }, item.id) });
492
- }
493
- function useStepLog() {
494
- const [items, setItems] = useState([]);
495
- const nextId = useRef(0);
496
- const log = (node) => {
497
- const id = nextId.current++;
498
- setItems((prev) => [...prev, { id, node }]);
499
- };
500
- return { items, log };
501
- }
502
- function CommandRunner({
503
- run,
504
- onError
505
- }) {
506
- const { exit } = useApp();
507
- const { items, log } = useStepLog();
508
- const [spinning, setSpinning] = useState(true);
509
- const [currentStep, setCurrentStep] = useState(null);
510
- const [err, setErr] = useState(null);
511
- const currentStepRef = useRef(null);
512
- const wrappedLog = (node) => {
513
- if (currentStepRef.current) {
514
- log(currentStepRef.current);
515
- }
516
- currentStepRef.current = node;
517
- setCurrentStep(node);
518
- };
519
- const started = useRef(false);
520
- React.useEffect(() => {
521
- if (started.current) return;
522
- started.current = true;
523
- (async () => {
524
- try {
525
- await run(wrappedLog);
526
- } catch (e) {
527
- const error = e instanceof Error ? e : new Error(String(e));
528
- setErr(error.message);
529
- onError?.(error);
530
- }
531
- if (currentStepRef.current) {
532
- log(currentStepRef.current);
533
- currentStepRef.current = null;
534
- }
535
- setCurrentStep(null);
536
- setSpinning(false);
537
- setTimeout(() => exit(), 0);
538
- })();
539
- });
540
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
541
- /* @__PURE__ */ jsx2(StepLog, { items }),
542
- err && /* @__PURE__ */ jsx2(ErrorLine, { msg: err }),
543
- spinning && currentStep && /* @__PURE__ */ jsxs2(Box2, { children: [
544
- /* @__PURE__ */ jsx2(Spinner, {}),
545
- /* @__PURE__ */ jsx2(Text2, { children: " " }),
546
- currentStep
547
- ] })
548
- ] });
549
- }
550
- async function runWithInk(fn) {
551
- let thrownError;
552
- const app = render2(
553
- /* @__PURE__ */ jsx2(
554
- CommandRunner,
555
- {
556
- onError: (e) => {
557
- thrownError = e;
558
- },
559
- run: fn
560
- }
561
- )
562
- );
563
- await app.waitUntilExit();
564
- if (thrownError) process.exit(1);
565
- }
566
- var init_ink = __esm({
567
- "cli/_ink.tsx"() {
568
- "use strict";
569
- init_colors();
570
- }
514
+ // cli/_build.tsx
515
+ var build_exports = {};
516
+ __export(build_exports, {
517
+ buildAgentBundle: () => buildAgentBundle,
518
+ runBuildCommand: () => runBuildCommand
571
519
  });
572
-
573
- // cli/_build.ts
574
- import React2 from "react";
520
+ import { jsx as jsx3 } from "react/jsx-runtime";
575
521
  async function buildAgentBundle(cwd, log, opts) {
576
522
  const agent = await loadAgent(cwd);
577
523
  if (!agent) {
578
524
  throw new Error("No agent found \u2014 run `aai init` first");
579
525
  }
580
- log(React2.createElement(Step, { action: "Bundle", msg: agent.slug }));
526
+ log(/* @__PURE__ */ jsx3(Step, { action: "Bundle", msg: agent.slug }));
581
527
  let bundle;
582
528
  try {
583
529
  bundle = await bundleAgent(agent);
584
530
  } catch (err) {
585
531
  if (err instanceof BundleError) {
586
532
  throw new Error(`Bundle failed: ${err.message}`);
587
- }
588
- throw err;
589
- }
590
- const kb = (bundle.workerBytes / 1024).toFixed(1);
591
- const clientCount = Object.keys(bundle.clientFiles).length;
592
- log(React2.createElement(Info, { msg: `worker: ${kb} KB, client: ${clientCount} file(s)` }));
593
- if (agent.clientEntry && !opts?.skipRenderCheck) {
594
- try {
595
- const renderCheckPath = "../sdk/_render_check.ts";
596
- const { renderCheck } = await import(
597
- /* @vite-ignore */
598
- renderCheckPath
599
- );
600
- log(React2.createElement(Step, { action: "Render", msg: "check" }));
601
- await renderCheck(agent.clientEntry, cwd);
602
- } catch (err) {
603
- const msg = err instanceof Error ? err.message : String(err);
604
- if (msg.includes("linkedom") || msg.includes("_render_check")) return bundle;
605
- throw new Error(`Render check failed: ${msg}`);
606
- }
607
- }
608
- return bundle;
609
- }
610
- var init_build = __esm({
611
- "cli/_build.ts"() {
612
- "use strict";
613
- init_bundler();
614
- init_discover();
615
- init_ink();
616
- }
617
- });
618
-
619
- // cli/_init.ts
620
- var init_exports = {};
621
- __export(init_exports, {
622
- listTemplates: () => listTemplates,
623
- runInit: () => runInit
624
- });
625
- import fs3 from "node:fs/promises";
626
- import path3 from "node:path";
627
- import glob from "fast-glob";
628
- import fsExtra from "fs-extra";
629
- async function listTemplates(dir) {
630
- const templates = [];
631
- const entries = await fs3.readdir(dir, { withFileTypes: true });
632
- for (const entry of entries) {
633
- if (entry.isDirectory() && !entry.name.startsWith("_")) {
634
- templates.push(entry.name);
635
- }
636
- }
637
- return templates.sort();
638
- }
639
- async function copyDirNoOverwrite(src, dest) {
640
- const files = await glob("**/*", { cwd: src, dot: true, onlyFiles: true });
641
- for (const file of files) {
642
- const destPath = path3.join(dest, file);
643
- try {
644
- await fs3.access(destPath);
645
- } catch {
646
- await fs3.mkdir(path3.dirname(destPath), { recursive: true });
647
- await fs3.copyFile(path3.join(src, file), destPath);
648
- }
649
- }
650
- }
651
- async function runInit(opts) {
652
- const { targetDir, template, templatesDir } = opts;
653
- const available = await listTemplates(templatesDir);
654
- if (!available.includes(template)) {
655
- throw new Error(`unknown template '${template}' -- available: ${available.join(", ")}`);
656
- }
657
- await fsExtra.copy(path3.join(templatesDir, template), targetDir, { overwrite: true });
658
- await copyDirNoOverwrite(path3.join(templatesDir, "_shared"), targetDir);
659
- try {
660
- await fs3.copyFile(path3.join(targetDir, ".env.example"), path3.join(targetDir, ".env"));
661
- } catch {
662
- }
663
- const readmePath = path3.join(targetDir, "README.md");
664
- try {
665
- await fs3.access(readmePath);
666
- } catch {
667
- const slug = path3.basename(path3.resolve(targetDir));
668
- const readme = `# ${slug}
669
-
670
- A voice agent built with [aai](https://github.com/anthropics/aai).
671
-
672
- ## Getting started
673
-
674
- \`\`\`sh
675
- npm install # Install dependencies
676
- npm run dev # Run locally (opens browser)
677
- npm run deploy # Deploy to production
678
- \`\`\`
679
-
680
- ## Environment variables
681
-
682
- Secrets are managed on the server, not in local files:
683
-
684
- \`\`\`sh
685
- aai env add MY_KEY # Set a secret (prompts for value)
686
- aai env ls # List secret names
687
- aai env pull # Pull names into .env for reference
688
- aai env rm MY_KEY # Remove a secret
689
- \`\`\`
690
-
691
- Access secrets in your agent via \`ctx.env.MY_KEY\`.
692
-
693
- ## Learn more
694
-
695
- See \`CLAUDE.md\` for the full agent API reference.
696
- `;
697
- await fs3.writeFile(readmePath, readme);
533
+ }
534
+ throw err;
698
535
  }
699
- return targetDir;
536
+ const kb = (bundle.workerBytes / 1024).toFixed(1);
537
+ const clientCount = Object.keys(bundle.clientFiles).length;
538
+ log(/* @__PURE__ */ jsx3(Info, { msg: `worker: ${kb} KB, client: ${clientCount} file(s)` }));
539
+ if (agent.clientEntry && !opts?.skipRenderCheck) {
540
+ const renderCheckPath = "../sdk/_render_check.ts";
541
+ const mod = await import(
542
+ /* @vite-ignore */
543
+ renderCheckPath
544
+ ).catch(() => null);
545
+ if (mod) {
546
+ log(/* @__PURE__ */ jsx3(Step, { action: "Render", msg: "check" }));
547
+ try {
548
+ await mod.renderCheck(agent.clientEntry, cwd);
549
+ } catch (err) {
550
+ throw new Error(`Render check failed: ${errorMessage(err)}`);
551
+ }
552
+ }
553
+ }
554
+ return bundle;
700
555
  }
701
- var init_init = __esm({
702
- "cli/_init.ts"() {
556
+ async function runBuildCommand(cwd) {
557
+ await runWithInk(async ({ log }) => {
558
+ await buildAgentBundle(cwd, log);
559
+ log(/* @__PURE__ */ jsx3(Step, { action: "Build", msg: "ok" }));
560
+ });
561
+ }
562
+ var init_build = __esm({
563
+ "cli/_build.tsx"() {
703
564
  "use strict";
565
+ init_utils();
566
+ init_bundler();
567
+ init_discover();
568
+ init_ink();
704
569
  }
705
570
  });
706
571
 
707
572
  // cli/_deploy.ts
708
- async function attemptDeploy(url, slug, apiKey, env, worker, clientFiles) {
573
+ async function attemptDeploy(fetchFn, url, slug, apiKey, env, worker, clientFiles) {
709
574
  try {
710
- return await _internals.fetch(`${url}/${slug}/deploy`, {
575
+ return await fetchFn(`${url}/${slug}/deploy`, {
711
576
  method: "POST",
712
577
  headers: {
713
578
  "Content-Type": "application/json",
@@ -724,37 +589,36 @@ async function attemptDeploy(url, slug, apiKey, env, worker, clientFiles) {
724
589
  }
725
590
  }
726
591
  async function runDeploy(opts) {
727
- const worker = opts.bundle.worker;
728
- const clientFiles = opts.bundle.clientFiles;
592
+ const { worker, clientFiles } = opts.bundle;
593
+ const fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
729
594
  let slug = opts.slug;
730
- if (opts.dryRun) {
731
- return { slug };
732
- }
733
595
  for (let i = 0; i < MAX_RETRIES; i++) {
734
- const resp = await attemptDeploy(opts.url, slug, opts.apiKey, opts.env, worker, clientFiles);
596
+ const resp = await attemptDeploy(
597
+ fetchFn,
598
+ opts.url,
599
+ slug,
600
+ opts.apiKey,
601
+ opts.env,
602
+ worker,
603
+ clientFiles
604
+ );
735
605
  if (resp.ok) {
736
606
  return { slug };
737
607
  }
738
- if (resp.status === 403) {
739
- const text2 = await resp.text();
740
- if (text2.includes("Slug")) {
741
- slug = generateSlug();
742
- continue;
743
- }
744
- }
745
608
  const text = await resp.text();
609
+ if (resp.status === 403 && text.includes("Slug")) {
610
+ slug = generateSlug();
611
+ continue;
612
+ }
746
613
  throw new Error(`deploy failed (${resp.status}): ${text}`);
747
614
  }
748
615
  throw new Error(`deploy failed: could not find available slug after ${MAX_RETRIES} attempts`);
749
616
  }
750
- var _internals, MAX_RETRIES;
617
+ var MAX_RETRIES;
751
618
  var init_deploy = __esm({
752
619
  "cli/_deploy.ts"() {
753
620
  "use strict";
754
621
  init_discover();
755
- _internals = {
756
- fetch: globalThis.fetch.bind(globalThis)
757
- };
758
622
  MAX_RETRIES = 20;
759
623
  }
760
624
  });
@@ -764,103 +628,66 @@ var deploy_exports = {};
764
628
  __export(deploy_exports, {
765
629
  runDeployCommand: () => runDeployCommand
766
630
  });
767
- import path4 from "node:path";
768
- import minimist from "minimist";
769
- import { jsx as jsx3 } from "react/jsx-runtime";
770
- function resolveServerUrl(parsed) {
771
- return parsed.server || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
772
- }
631
+ import { jsx as jsx4 } from "react/jsx-runtime";
773
632
  async function deployBundle(opts) {
774
633
  const { bundle, serverUrl, apiKey, cwd, log } = opts;
775
634
  let { slug } = opts;
776
- log(/* @__PURE__ */ jsx3(Step, { action: "Deploy", msg: slug }));
635
+ log(/* @__PURE__ */ jsx4(Step, { action: "Deploy", msg: slug }));
777
636
  const deployed = await runDeploy({
778
637
  url: serverUrl,
779
638
  bundle,
780
639
  env: { ASSEMBLYAI_API_KEY: apiKey },
781
640
  slug,
782
- dryRun: false,
783
641
  apiKey
784
642
  });
785
643
  slug = deployed.slug;
786
644
  await writeProjectConfig(cwd, { slug, serverUrl });
787
645
  const agentUrl = `${serverUrl}/${slug}`;
788
- log(/* @__PURE__ */ jsx3(Step, { action: "Ready", msg: agentUrl }));
646
+ log(/* @__PURE__ */ jsx4(Step, { action: "Ready", msg: agentUrl }));
789
647
  return agentUrl;
790
648
  }
791
- async function runDeployCommand(args, version) {
792
- const parsed = minimist(args, {
793
- string: ["server"],
794
- boolean: ["dry-run", "help", "yes"],
795
- alias: { s: "server", h: "help", y: "yes" }
796
- });
797
- if (parsed.help) {
798
- console.log(subcommandHelp(deployCommandDef, version));
799
- return;
800
- }
801
- const cwd = process.env.INIT_CWD || process.cwd();
802
- if (!await fileExists(path4.join(cwd, "agent.ts"))) {
803
- await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
804
- }
805
- const serverUrl = resolveServerUrl(parsed);
806
- const dryRun = parsed["dry-run"] ?? false;
649
+ async function runDeployCommand(opts) {
650
+ const { cwd } = opts;
651
+ const dryRun = opts.dryRun ?? false;
807
652
  const apiKey = dryRun ? "" : await getApiKey();
808
653
  const projectConfig = await readProjectConfig(cwd);
654
+ const serverUrl = resolveServerUrl(opts.server, projectConfig?.serverUrl);
809
655
  const slug = projectConfig?.slug ?? generateSlug();
810
- let agentUrl = "";
811
- await runWithInk(async (log) => {
656
+ await runWithInk(async ({ log }) => {
812
657
  const bundle = await buildAgentBundle(cwd, log);
813
658
  if (dryRun) {
814
- log(/* @__PURE__ */ jsx3(StepInfo, { action: "Dry run", msg: `would deploy as ${slug}` }));
659
+ log(/* @__PURE__ */ jsx4(StepInfo, { action: "Dry run", msg: `would deploy as ${slug}` }));
815
660
  return;
816
661
  }
817
- agentUrl = await deployBundle({ bundle, serverUrl, apiKey, slug, cwd, log });
662
+ await deployBundle({ bundle, serverUrl, apiKey, slug, cwd, log });
818
663
  });
819
- if (agentUrl && !dryRun) {
820
- await askEnter("Press enter to open in browser");
821
- const { exec } = await import("node:child_process");
822
- exec(`open "${agentUrl}"`);
823
- }
824
664
  }
825
- var deployCommandDef;
826
665
  var init_deploy2 = __esm({
827
666
  "cli/deploy.tsx"() {
828
667
  "use strict";
829
668
  init_build();
830
669
  init_deploy();
831
670
  init_discover();
832
- init_help();
833
671
  init_ink();
834
- init_prompts();
835
- init_init2();
836
- deployCommandDef = {
837
- name: "deploy",
838
- description: "Bundle and deploy to production",
839
- options: [
840
- { flags: "-s, --server <url>", description: "Server URL" },
841
- {
842
- flags: "--dry-run",
843
- description: "Validate and bundle without deploying"
844
- },
845
- { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
846
- ]
847
- };
848
672
  }
849
673
  });
850
674
 
851
675
  // cli/init.tsx
676
+ var init_exports2 = {};
677
+ __export(init_exports2, {
678
+ runInitCommand: () => runInitCommand
679
+ });
852
680
  import { execFile } from "node:child_process";
853
681
  import fs4 from "node:fs/promises";
854
- import path5 from "node:path";
682
+ import path4 from "node:path";
855
683
  import { fileURLToPath } from "node:url";
856
684
  import { promisify } from "node:util";
857
- import minimist2 from "minimist";
858
- import { jsx as jsx4 } from "react/jsx-runtime";
685
+ import { jsx as jsx5 } from "react/jsx-runtime";
859
686
  async function rewriteDevDeps(cwd, cliDir2) {
860
- const monorepoRoot = path5.join(cliDir2, "..");
861
- const pkgJsonPath2 = path5.join(cwd, "package.json");
687
+ const monorepoRoot = path4.join(cliDir2, "..");
688
+ const pkgJsonPath2 = path4.join(cwd, "package.json");
862
689
  const pkgJson2 = JSON.parse(await fs4.readFile(pkgJsonPath2, "utf-8"));
863
- const rootPkg = JSON.parse(await fs4.readFile(path5.join(monorepoRoot, "package.json"), "utf-8"));
690
+ const rootPkg = JSON.parse(await fs4.readFile(path4.join(monorepoRoot, "package.json"), "utf-8"));
864
691
  const rootPkgName = rootPkg.name;
865
692
  if (pkgJson2.dependencies[rootPkgName]) {
866
693
  pkgJson2.dependencies[rootPkgName] = `file:${monorepoRoot}`;
@@ -869,56 +696,45 @@ async function rewriteDevDeps(cwd, cliDir2) {
869
696
  `);
870
697
  }
871
698
  async function installDeps(cwd, log) {
872
- if (await fileExists(path5.join(cwd, "node_modules"))) return;
699
+ if (await fileExists(path4.join(cwd, "node_modules"))) return;
873
700
  let pkgJson2;
874
701
  try {
875
- pkgJson2 = JSON.parse(await fs4.readFile(path5.join(cwd, "package.json"), "utf-8"));
702
+ pkgJson2 = JSON.parse(await fs4.readFile(path4.join(cwd, "package.json"), "utf-8"));
876
703
  } catch {
877
704
  pkgJson2 = {};
878
705
  }
879
706
  const deps = Object.keys(pkgJson2.dependencies ?? {});
880
707
  const devDeps = Object.keys(pkgJson2.devDependencies ?? {});
881
708
  if (deps.length > 0) {
882
- log(/* @__PURE__ */ jsx4(Step, { action: "Install", msg: deps.join(", ") }));
709
+ log(/* @__PURE__ */ jsx5(Step, { action: "Install", msg: deps.join(", ") }));
883
710
  }
884
711
  if (devDeps.length > 0) {
885
- log(/* @__PURE__ */ jsx4(Step, { action: "Install", msg: `dev: ${devDeps.join(", ")}` }));
712
+ log(/* @__PURE__ */ jsx5(Step, { action: "Install", msg: `dev: ${devDeps.join(", ")}` }));
886
713
  }
887
714
  try {
888
715
  await execFileAsync("npm", ["install"], { cwd });
889
716
  } catch {
890
- log(/* @__PURE__ */ jsx4(Warn, { msg: "npm install failed" }));
717
+ log(/* @__PURE__ */ jsx5(Warn, { msg: "npm install failed" }));
891
718
  }
892
719
  }
893
- async function runInitCommand(args, version, opts) {
894
- const parsed = minimist2(args, {
895
- string: ["template"],
896
- boolean: ["force", "help"],
897
- alias: { t: "template", f: "force", h: "help" }
898
- });
899
- if (parsed.help) {
900
- console.log(subcommandHelp(initCommandDef, version));
901
- return "";
902
- }
903
- const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
904
- await getApiKey2();
905
- let dir = parsed._[0];
720
+ async function runInitCommand(opts, extra) {
721
+ await getApiKey();
722
+ let dir = opts.dir;
906
723
  if (!dir) {
907
724
  dir = await askText("What is your project named?", "my-voice-agent");
908
725
  }
909
- const cwd = path5.resolve(process.env.INIT_CWD || process.cwd(), dir);
910
- if (!parsed.force && await fileExists(path5.join(cwd, "agent.ts"))) {
911
- console.log(
726
+ const cwd = path4.resolve(resolveCwd(), dir);
727
+ if (!opts.force && await fileExists(path4.join(cwd, "agent.ts"))) {
728
+ throw new Error(
912
729
  `agent.ts already exists in this directory. Use ${interactive("--force")} to overwrite.`
913
730
  );
914
- process.exit(1);
915
731
  }
916
- const cliDir2 = path5.dirname(fileURLToPath(import.meta.url));
917
- const templatesDir = path5.join(cliDir2, "..", "templates");
732
+ const cliDir2 = path4.dirname(fileURLToPath(import.meta.url));
733
+ const templatesDir = path4.join(cliDir2, "..", "templates");
918
734
  const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
919
- const template = parsed.template || "simple";
920
- await runWithInk(async (log) => {
921
- log(/* @__PURE__ */ jsx4(Step, { action: "Create", msg: dir }));
735
+ const template = opts.template || "simple";
736
+ await runWithInk(async ({ log }) => {
737
+ log(/* @__PURE__ */ jsx5(Step, { action: "Create", msg: dir }));
922
738
  await runInit2({ targetDir: cwd, template, templatesDir });
923
739
  if (isDevMode()) {
924
740
  await rewriteDevDeps(cwd, cliDir2);
@@ -927,60 +743,32 @@ async function runInitCommand(args, version, opts) {
927
743
  });
928
744
  process.chdir(cwd);
929
745
  delete process.env.INIT_CWD;
930
- if (!opts?.quiet) {
746
+ if (!extra?.quiet) {
931
747
  const { runDeployCommand: runDeployCommand2 } = await Promise.resolve().then(() => (init_deploy2(), deploy_exports));
932
- await runDeployCommand2(["-y"], version);
748
+ await runDeployCommand2({ cwd });
933
749
  }
934
750
  return cwd;
935
751
  }
936
- var execFileAsync, initCommandDef;
752
+ var execFileAsync;
937
753
  var init_init2 = __esm({
938
754
  "cli/init.tsx"() {
939
755
  "use strict";
940
- init_colors();
941
756
  init_discover();
942
- init_help();
943
757
  init_ink();
944
758
  init_prompts();
945
759
  execFileAsync = promisify(execFile);
946
- initCommandDef = {
947
- name: "init",
948
- description: "Scaffold a new agent project",
949
- args: [{ name: "dir", optional: true }],
950
- options: [
951
- {
952
- flags: "-t, --template <template>",
953
- description: "Template to use"
954
- },
955
- { flags: "-f, --force", description: "Overwrite existing agent.ts" }
956
- ]
957
- };
958
760
  }
959
761
  });
960
762
 
961
763
  // sdk/protocol.ts
962
764
  import { z } from "zod";
963
- var DEFAULT_TTS_SAMPLE_RATE, AUDIO_FORMAT, _bitsPerSample, _channels, AudioFrameSpec, KvRequestBaseSchema, HOOK_TIMEOUT_MS, SessionErrorCodeSchema, TranscriptEventSchema, ClientEventSchema, ClientMessageSchema;
765
+ var DEFAULT_TTS_SAMPLE_RATE, AUDIO_FORMAT, KvRequestSchema, VectorRequestSchema, HOOK_TIMEOUT_MS, TOOL_EXECUTION_TIMEOUT_MS, SessionErrorCodeSchema, ev, textEv, turnOrder, ClientEventSchema, ClientMessageSchema;
964
766
  var init_protocol = __esm({
965
767
  "sdk/protocol.ts"() {
966
768
  "use strict";
967
769
  DEFAULT_TTS_SAMPLE_RATE = 24e3;
968
770
  AUDIO_FORMAT = "pcm16";
969
- _bitsPerSample = 16;
970
- _channels = 1;
971
- AudioFrameSpec = {
972
- /** Audio codec identifier sent in the `ready` message. */
973
- format: AUDIO_FORMAT,
974
- /** Signed 16-bit integer samples. */
975
- bitsPerSample: _bitsPerSample,
976
- /** Little-endian byte order. */
977
- endianness: "little",
978
- /** Mono audio. */
979
- channels: _channels,
980
- /** Bytes per sample — derived from bitsPerSample and channels. */
981
- bytesPerSample: _bitsPerSample / 8 * _channels
982
- };
983
- KvRequestBaseSchema = z.discriminatedUnion("op", [
771
+ KvRequestSchema = z.discriminatedUnion("op", [
984
772
  z.object({ op: z.literal("get"), key: z.string().min(1) }),
985
773
  z.object({
986
774
  op: z.literal("set"),
@@ -994,9 +782,29 @@ var init_protocol = __esm({
994
782
  prefix: z.string(),
995
783
  limit: z.number().int().positive().optional(),
996
784
  reverse: z.boolean().optional()
785
+ }),
786
+ z.object({ op: z.literal("keys"), pattern: z.string().optional() })
787
+ ]);
788
+ VectorRequestSchema = z.discriminatedUnion("op", [
789
+ z.object({
790
+ op: z.literal("upsert"),
791
+ id: z.string().min(1),
792
+ data: z.string().min(1),
793
+ metadata: z.record(z.string(), z.unknown()).optional()
794
+ }),
795
+ z.object({
796
+ op: z.literal("query"),
797
+ text: z.string().min(1),
798
+ topK: z.number().int().positive().max(100).optional(),
799
+ filter: z.string().optional()
800
+ }),
801
+ z.object({
802
+ op: z.literal("remove"),
803
+ ids: z.array(z.string().min(1)).min(1)
997
804
  })
998
805
  ]);
999
806
  HOOK_TIMEOUT_MS = 5e3;
807
+ TOOL_EXECUTION_TIMEOUT_MS = 3e4;
1000
808
  SessionErrorCodeSchema = z.enum([
1001
809
  "stt",
1002
810
  "llm",
@@ -1007,23 +815,16 @@ var init_protocol = __esm({
1007
815
  "audio",
1008
816
  "internal"
1009
817
  ]);
1010
- TranscriptEventSchema = z.object({
1011
- type: z.literal("transcript"),
1012
- text: z.string(),
1013
- isFinal: z.boolean(),
1014
- turnOrder: z.number().int().nonnegative().optional()
1015
- });
818
+ ev = (t) => z.object({ type: z.literal(t) });
819
+ textEv = (t) => z.object({ type: z.literal(t), text: z.string() });
820
+ turnOrder = z.number().int().nonnegative().optional();
1016
821
  ClientEventSchema = z.discriminatedUnion("type", [
1017
- z.object({ type: z.literal("speech_started") }),
1018
- z.object({ type: z.literal("speech_stopped") }),
1019
- TranscriptEventSchema,
1020
- z.object({
1021
- type: z.literal("turn"),
1022
- text: z.string(),
1023
- turnOrder: z.number().int().nonnegative().optional()
1024
- }),
1025
- z.object({ type: z.literal("chat"), text: z.string() }),
1026
- z.object({ type: z.literal("chat_delta"), text: z.string() }),
822
+ ev("speech_started"),
823
+ ev("speech_stopped"),
824
+ z.object({ type: z.literal("transcript"), text: z.string(), isFinal: z.boolean(), turnOrder }),
825
+ textEv("turn").extend({ turnOrder }),
826
+ textEv("chat"),
827
+ textEv("chat_delta"),
1027
828
  z.object({
1028
829
  type: z.literal("tool_call_start"),
1029
830
  toolCallId: z.string(),
@@ -1035,55 +836,35 @@ var init_protocol = __esm({
1035
836
  toolCallId: z.string(),
1036
837
  result: z.string().max(4e3)
1037
838
  }),
1038
- z.object({ type: z.literal("tts_done") }),
1039
- z.object({ type: z.literal("cancelled") }),
1040
- z.object({ type: z.literal("reset") }),
1041
- z.object({
1042
- type: z.literal("error"),
1043
- code: SessionErrorCodeSchema,
1044
- message: z.string()
1045
- })
839
+ ev("tts_done"),
840
+ ev("cancelled"),
841
+ ev("reset"),
842
+ z.object({ type: z.literal("error"), code: SessionErrorCodeSchema, message: z.string() })
1046
843
  ]);
1047
844
  ClientMessageSchema = z.discriminatedUnion("type", [
1048
- z.object({ type: z.literal("audio_ready") }),
1049
- z.object({ type: z.literal("cancel") }),
1050
- z.object({ type: z.literal("reset") }),
845
+ ev("audio_ready"),
846
+ ev("cancel"),
847
+ ev("reset"),
1051
848
  z.object({
1052
849
  type: z.literal("history"),
1053
- messages: z.array(
1054
- z.object({
1055
- role: z.enum(["user", "assistant"]),
1056
- text: z.string().max(1e5)
1057
- })
1058
- ).max(200)
850
+ messages: z.array(z.object({ role: z.enum(["user", "assistant"]), text: z.string().max(1e5) })).max(200)
1059
851
  })
1060
852
  ]);
1061
853
  }
1062
854
  });
1063
855
 
1064
856
  // sdk/runtime.ts
1065
- var consoleLogger, noopMetrics, DEFAULT_S2S_CONFIG;
857
+ var _log, consoleLogger, noopMetrics, DEFAULT_S2S_CONFIG;
1066
858
  var init_runtime = __esm({
1067
859
  "sdk/runtime.ts"() {
1068
860
  "use strict";
1069
861
  init_protocol();
862
+ _log = (m) => (msg, ctx) => console[m](msg, ...ctx ? [ctx] : []);
1070
863
  consoleLogger = {
1071
- info(msg, ctx) {
1072
- if (ctx) console.log(msg, ctx);
1073
- else console.log(msg);
1074
- },
1075
- warn(msg, ctx) {
1076
- if (ctx) console.warn(msg, ctx);
1077
- else console.warn(msg);
1078
- },
1079
- error(msg, ctx) {
1080
- if (ctx) console.error(msg, ctx);
1081
- else console.error(msg);
1082
- },
1083
- debug(msg, ctx) {
1084
- if (ctx) console.debug(msg, ctx);
1085
- else console.debug(msg);
1086
- }
864
+ info: _log("log"),
865
+ warn: _log("warn"),
866
+ error: _log("error"),
867
+ debug: _log("debug")
1087
868
  };
1088
869
  noopMetrics = {
1089
870
  sessionsTotal: { inc() {
@@ -1100,20 +881,230 @@ var init_runtime = __esm({
1100
881
  }
1101
882
  });
1102
883
 
1103
- // sdk/_internal_types.ts
884
+ // sdk/s2s.ts
885
+ var s2s_exports = {};
886
+ __export(s2s_exports, {
887
+ connectS2s: () => connectS2s,
888
+ wrapOnStyleWebSocket: () => wrapOnStyleWebSocket
889
+ });
1104
890
  import { z as z2 } from "zod";
891
+ function uint8ToBase64(bytes) {
892
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
893
+ }
894
+ function base64ToUint8(base64) {
895
+ const buf = Buffer.from(base64, "base64");
896
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
897
+ }
898
+ function wrapOnStyleWebSocket(ws) {
899
+ const target = new EventTarget();
900
+ ws.on("open", () => target.dispatchEvent(new Event("open")));
901
+ ws.on("message", (data) => target.dispatchEvent(new MessageEvent("message", { data })));
902
+ ws.on("close", (code, reason) => {
903
+ const init = { reason: String(reason ?? "") };
904
+ if (typeof code === "number") init.code = code;
905
+ target.dispatchEvent(new CloseEvent("close", init));
906
+ });
907
+ ws.on(
908
+ "error",
909
+ (err) => target.dispatchEvent(
910
+ new ErrorEvent("error", {
911
+ message: errorMessage(err)
912
+ })
913
+ )
914
+ );
915
+ Object.defineProperties(target, {
916
+ readyState: { get: () => ws.readyState, enumerable: true },
917
+ send: { value: (data) => ws.send(data), enumerable: true },
918
+ close: { value: () => ws.close(), enumerable: true }
919
+ });
920
+ return target;
921
+ }
922
+ function dispatchS2sMessage(target, msg) {
923
+ const entry = S2S_DISPATCH[msg.type]?.(msg);
924
+ if (entry) target.dispatchEvent(new CustomEvent(entry[0], { detail: entry[1] }));
925
+ }
926
+ function connectS2s(opts) {
927
+ const { apiKey, config, createWebSocket, logger: log = consoleLogger } = opts;
928
+ return new Promise((resolve, reject) => {
929
+ log.info("S2S connecting", { url: config.wssUrl });
930
+ const ws = createWebSocket(config.wssUrl, {
931
+ headers: { Authorization: `Bearer ${apiKey}` }
932
+ });
933
+ const target = new EventTarget();
934
+ let opened = false;
935
+ function send(msg) {
936
+ if (ws.readyState !== WS_OPEN) return;
937
+ const json = JSON.stringify(msg);
938
+ if (msg.type !== "input.audio") {
939
+ log.info(
940
+ `S2S >> ${msg.type}`,
941
+ msg.type === "session.update" ? { payload: json } : void 0
942
+ );
943
+ }
944
+ ws.send(json);
945
+ }
946
+ const handle = Object.assign(target, {
947
+ sendAudio(audio) {
948
+ if (ws.readyState !== WS_OPEN) return;
949
+ ws.send(`{"type":"input.audio","audio":"${uint8ToBase64(audio)}"}`);
950
+ },
951
+ sendToolResult(callId, result) {
952
+ const msg = { type: "tool.result", call_id: callId, result };
953
+ log.info("S2S >> tool.result", { call_id: callId, resultLength: result.length });
954
+ send(msg);
955
+ },
956
+ updateSession(sessionConfig) {
957
+ send({ type: "session.update", session: sessionConfig });
958
+ },
959
+ resumeSession(sessionId) {
960
+ send({ type: "session.resume", session_id: sessionId });
961
+ },
962
+ close() {
963
+ log.info("S2S closing");
964
+ ws.close();
965
+ }
966
+ });
967
+ ws.addEventListener("open", () => {
968
+ opened = true;
969
+ log.info("S2S WebSocket open");
970
+ resolve(handle);
971
+ });
972
+ function handleS2sMessage(ev2) {
973
+ const data = ev2.data;
974
+ let raw;
975
+ try {
976
+ raw = JSON.parse(String(data));
977
+ } catch {
978
+ log.warn("S2S << invalid JSON", { data: String(data).slice(0, 200) });
979
+ return;
980
+ }
981
+ const obj = raw;
982
+ if (obj.type !== "reply.audio" && obj.type !== "input.audio") {
983
+ log.info(
984
+ `S2S << ${obj.type}`,
985
+ obj.type === "transcript.agent.delta" ? { delta: obj.delta } : void 0
986
+ );
987
+ }
988
+ if (obj.type === "reply.audio" && typeof obj.data === "string") {
989
+ const audioBytes = base64ToUint8(obj.data);
990
+ target.dispatchEvent(new CustomEvent("audio", { detail: { audio: audioBytes } }));
991
+ return;
992
+ }
993
+ const parsed = S2sServerMessageSchema.safeParse(raw);
994
+ if (!parsed.success) {
995
+ log.warn(
996
+ `S2S << unrecognised message type: ${obj.type ?? JSON.stringify(raw).slice(0, 200)}`
997
+ );
998
+ return;
999
+ }
1000
+ dispatchS2sMessage(target, parsed.data);
1001
+ }
1002
+ ws.addEventListener("message", handleS2sMessage);
1003
+ ws.addEventListener("close", ((ev2) => {
1004
+ log.info("S2S WebSocket closed", {
1005
+ code: ev2.code ?? 0,
1006
+ reason: ev2.reason ?? ""
1007
+ });
1008
+ target.dispatchEvent(new CustomEvent("close"));
1009
+ }));
1010
+ ws.addEventListener("error", ((ev2) => {
1011
+ const message = ev2 instanceof ErrorEvent ? ev2.message : "WebSocket error";
1012
+ const errObj = new Error(message);
1013
+ log.error("S2S WebSocket error", { error: errObj.message });
1014
+ if (!opened) {
1015
+ reject(errObj);
1016
+ } else {
1017
+ target.dispatchEvent(
1018
+ new CustomEvent("error", {
1019
+ detail: { code: "ws_error", message: errObj.message }
1020
+ })
1021
+ );
1022
+ }
1023
+ }));
1024
+ });
1025
+ }
1026
+ var WS_OPEN, S2sServerMessageSchema, S2S_DISPATCH;
1027
+ var init_s2s = __esm({
1028
+ "sdk/s2s.ts"() {
1029
+ "use strict";
1030
+ init_utils();
1031
+ init_runtime();
1032
+ WS_OPEN = 1;
1033
+ S2sServerMessageSchema = z2.discriminatedUnion("type", [
1034
+ z2.object({ type: z2.literal("session.ready"), session_id: z2.string() }),
1035
+ z2.object({ type: z2.literal("session.updated") }).passthrough(),
1036
+ z2.object({ type: z2.literal("input.speech.started") }),
1037
+ z2.object({ type: z2.literal("input.speech.stopped") }),
1038
+ z2.object({ type: z2.literal("transcript.user.delta"), text: z2.string() }),
1039
+ z2.object({
1040
+ type: z2.literal("transcript.user"),
1041
+ item_id: z2.string(),
1042
+ text: z2.string()
1043
+ }),
1044
+ z2.object({ type: z2.literal("reply.started"), reply_id: z2.string() }),
1045
+ // reply.audio is handled on the fast path before Zod.
1046
+ z2.object({ type: z2.literal("transcript.agent.delta"), delta: z2.string() }).passthrough(),
1047
+ z2.object({ type: z2.literal("transcript.agent"), text: z2.string() }),
1048
+ z2.object({ type: z2.literal("reply.content_part.started") }).passthrough(),
1049
+ z2.object({ type: z2.literal("reply.content_part.done") }).passthrough(),
1050
+ z2.object({
1051
+ type: z2.literal("tool.call"),
1052
+ call_id: z2.string(),
1053
+ name: z2.string(),
1054
+ args: z2.record(z2.string(), z2.unknown()).optional().default({})
1055
+ }),
1056
+ z2.object({
1057
+ type: z2.literal("reply.done"),
1058
+ status: z2.string().optional()
1059
+ }),
1060
+ z2.object({
1061
+ type: z2.literal("session.error"),
1062
+ code: z2.string(),
1063
+ message: z2.string()
1064
+ }),
1065
+ // Connection-level error (bare "error" without "session." prefix).
1066
+ z2.object({
1067
+ type: z2.literal("error"),
1068
+ message: z2.string()
1069
+ })
1070
+ ]);
1071
+ S2S_DISPATCH = {
1072
+ "session.ready": (m) => ["ready", { session_id: m.session_id }],
1073
+ "session.updated": (m) => ["session_updated", m],
1074
+ "input.speech.started": () => ["speech_started", void 0],
1075
+ "input.speech.stopped": () => ["speech_stopped", void 0],
1076
+ "transcript.user.delta": (m) => ["user_transcript_delta", { text: m.text }],
1077
+ "transcript.user": (m) => ["user_transcript", { item_id: m.item_id, text: m.text }],
1078
+ "reply.started": (m) => ["reply_started", { reply_id: m.reply_id }],
1079
+ "transcript.agent.delta": (m) => ["agent_transcript_delta", { text: m.delta }],
1080
+ "transcript.agent": (m) => ["agent_transcript", { text: m.text }],
1081
+ "tool.call": (m) => ["tool_call", { call_id: m.call_id, name: m.name, args: m.args }],
1082
+ "reply.done": (m) => ["reply_done", { status: m.status }],
1083
+ "session.error": (m) => [
1084
+ m.code === "session_not_found" || m.code === "session_forbidden" ? "session_expired" : "error",
1085
+ { code: m.code, message: m.message }
1086
+ ],
1087
+ error: (m) => ["error", { code: "connection", message: m.message }],
1088
+ "reply.content_part.started": () => void 0,
1089
+ "reply.content_part.done": () => void 0
1090
+ };
1091
+ }
1092
+ });
1093
+
1094
+ // sdk/_internal_types.ts
1095
+ import { z as z3 } from "zod";
1105
1096
  function agentToolsToSchemas(tools) {
1106
1097
  return Object.entries(tools).map(([name, def]) => ({
1107
1098
  name,
1108
1099
  description: def.description,
1109
- parameters: z2.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1100
+ parameters: z3.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1110
1101
  }));
1111
1102
  }
1112
1103
  var EMPTY_PARAMS;
1113
1104
  var init_internal_types = __esm({
1114
1105
  "sdk/_internal_types.ts"() {
1115
1106
  "use strict";
1116
- EMPTY_PARAMS = z2.object({});
1107
+ EMPTY_PARAMS = z3.object({});
1117
1108
  }
1118
1109
  });
1119
1110
 
@@ -1139,68 +1130,16 @@ Voice-First Rules:
1139
1130
  }
1140
1131
  });
1141
1132
 
1142
- // sdk/memory_tools.ts
1143
- import { z as z3 } from "zod";
1144
- function memoryTools() {
1145
- return {
1146
- save_memory: tool({
1147
- description: "Save a piece of information to persistent memory. Use a descriptive key like 'user:name' or 'project:status'.",
1148
- parameters: z3.object({
1149
- key: z3.string().describe("A descriptive key for this memory (e.g. 'user:name', 'preference:color')"),
1150
- value: z3.string().describe("The information to remember")
1151
- }),
1152
- execute: async ({ key, value }, ctx) => {
1153
- await ctx.kv.set(key, value);
1154
- return { saved: key };
1155
- }
1156
- }),
1157
- recall_memory: tool({
1158
- description: "Retrieve a previously saved memory by its key.",
1159
- parameters: z3.object({
1160
- key: z3.string().describe("The key to look up")
1161
- }),
1162
- execute: async ({ key }, ctx) => {
1163
- const value = await ctx.kv.get(key);
1164
- if (value === null) return { found: false, key };
1165
- return { found: true, key, value };
1166
- }
1167
- }),
1168
- list_memories: tool({
1169
- description: "List all saved memory keys, optionally filtered by a prefix (e.g. 'user:').",
1170
- parameters: z3.object({
1171
- prefix: z3.string().describe("Prefix to filter keys (e.g. 'user:'). Use empty string for all.").optional()
1172
- }),
1173
- execute: async ({ prefix }, ctx) => {
1174
- const entries = await ctx.kv.list(prefix ?? "");
1175
- return { count: entries.length, keys: entries.map((e) => e.key) };
1176
- }
1177
- }),
1178
- forget_memory: tool({
1179
- description: "Delete a previously saved memory by its key.",
1180
- parameters: z3.object({
1181
- key: z3.string().describe("The key to delete")
1182
- }),
1183
- execute: async ({ key }, ctx) => {
1184
- await ctx.kv.delete(key);
1185
- return { deleted: key };
1186
- }
1187
- })
1188
- };
1189
- }
1190
- var init_memory_tools = __esm({
1191
- "sdk/memory_tools.ts"() {
1192
- "use strict";
1193
- init_types();
1194
- }
1195
- });
1196
-
1197
1133
  // sdk/builtin_tools.ts
1198
1134
  import { convert } from "html-to-text";
1199
1135
  import { z as z4 } from "zod";
1136
+ function fetchSignal(toolSignal) {
1137
+ return AbortSignal.any([toolSignal, AbortSignal.timeout(FETCH_TIMEOUT_MS)]);
1138
+ }
1200
1139
  function htmlToText(html) {
1201
1140
  return convert(html, { wordwrap: false });
1202
1141
  }
1203
- function createWebSearch() {
1142
+ function createWebSearch(fetchFn = globalThis.fetch) {
1204
1143
  return {
1205
1144
  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.",
1206
1145
  parameters: webSearchParams,
@@ -1215,9 +1154,9 @@ function createWebSearch() {
1215
1154
  count: String(maxResults),
1216
1155
  text_decorations: "false"
1217
1156
  })}`;
1218
- const resp = await fetch(url, {
1157
+ const resp = await fetchFn(url, {
1219
1158
  headers: { "X-Subscription-Token": apiKey },
1220
- signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
1159
+ signal: fetchSignal(ctx.abortSignal)
1221
1160
  });
1222
1161
  if (!resp.ok) return [];
1223
1162
  const raw = await resp.json();
@@ -1231,19 +1170,19 @@ function createWebSearch() {
1231
1170
  }
1232
1171
  };
1233
1172
  }
1234
- function createVisitWebpage() {
1173
+ function createVisitWebpage(fetchFn = globalThis.fetch) {
1235
1174
  return {
1236
1175
  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.",
1237
1176
  parameters: visitWebpageParams,
1238
1177
  async execute(args, ctx) {
1239
1178
  const { url } = args;
1240
- const resp = await fetch(url, {
1179
+ const resp = await fetchFn(url, {
1241
1180
  headers: {
1242
1181
  "User-Agent": "Mozilla/5.0 (compatible; VoiceAgent/1.0; +https://github.com/AssemblyAI/aai)",
1243
1182
  Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
1244
1183
  },
1245
1184
  redirect: "follow",
1246
- signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
1185
+ signal: fetchSignal(ctx.abortSignal)
1247
1186
  });
1248
1187
  if (!resp.ok) {
1249
1188
  return { error: `Failed to fetch: ${resp.status} ${resp.statusText}`, url };
@@ -1261,15 +1200,15 @@ function createVisitWebpage() {
1261
1200
  }
1262
1201
  };
1263
1202
  }
1264
- function createFetchJson() {
1203
+ function createFetchJson(fetchFn = globalThis.fetch) {
1265
1204
  return {
1266
1205
  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.",
1267
1206
  parameters: fetchJsonParams,
1268
1207
  async execute(args, ctx) {
1269
1208
  const { url, headers } = args;
1270
- const resp = await fetch(url, {
1209
+ const resp = await fetchFn(url, {
1271
1210
  ...headers && { headers },
1272
- signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
1211
+ signal: fetchSignal(ctx.abortSignal)
1273
1212
  });
1274
1213
  if (!resp.ok) {
1275
1214
  return { error: `HTTP ${resp.status} ${resp.statusText}`, url };
@@ -1299,7 +1238,6 @@ function createRunCode() {
1299
1238
  error: capture,
1300
1239
  debug: capture
1301
1240
  };
1302
- const RUN_CODE_TIMEOUT = 5e3;
1303
1241
  const AsyncFunction = Object.getPrototypeOf(async () => {
1304
1242
  }).constructor;
1305
1243
  try {
@@ -1313,7 +1251,7 @@ function createRunCode() {
1313
1251
  const result = output.join("\n").trim();
1314
1252
  return result || "Code ran successfully (no output)";
1315
1253
  } catch (err) {
1316
- return { error: err instanceof Error ? err.message : String(err) };
1254
+ return { error: errorMessage(err) };
1317
1255
  }
1318
1256
  }
1319
1257
  };
@@ -1328,66 +1266,84 @@ function createVectorSearch(vectorSearchFn) {
1328
1266
  }
1329
1267
  };
1330
1268
  }
1269
+ function resolveBuiltin(name, opts) {
1270
+ const entry = TOOL_REGISTRY[name];
1271
+ if (!entry) return [];
1272
+ if ("multi" in entry) return Object.entries(entry.multi());
1273
+ if (name === "vector_search" && !opts?.vectorSearch) return [];
1274
+ return [[name, entry.create(opts)]];
1275
+ }
1331
1276
  function getBuiltinToolDefs(names, opts) {
1332
1277
  const defs = {};
1333
1278
  for (const name of names) {
1334
- switch (name) {
1335
- case "web_search":
1336
- defs[name] = createWebSearch();
1337
- break;
1338
- case "visit_webpage":
1339
- defs[name] = createVisitWebpage();
1340
- break;
1341
- case "fetch_json":
1342
- defs[name] = createFetchJson();
1343
- break;
1344
- case "run_code":
1345
- defs[name] = createRunCode();
1346
- break;
1347
- case "vector_search":
1348
- if (opts?.vectorSearch) {
1349
- defs[name] = createVectorSearch(opts.vectorSearch);
1350
- }
1351
- break;
1352
- case "memory": {
1353
- const mt = memoryTools();
1354
- for (const [toolName, toolDef] of Object.entries(mt)) {
1355
- defs[toolName] = toolDef;
1356
- }
1357
- break;
1279
+ for (const [k, v] of resolveBuiltin(name, opts)) defs[k] = v;
1280
+ }
1281
+ return defs;
1282
+ }
1283
+ function getBuiltinToolSchemas(names) {
1284
+ return names.flatMap(
1285
+ (name) => resolveBuiltin(name, { vectorSearch: async () => "" }).map(([toolName, def]) => ({
1286
+ name: toolName,
1287
+ description: def.description,
1288
+ parameters: z4.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1289
+ }))
1290
+ );
1291
+ }
1292
+ function memoryTools() {
1293
+ return {
1294
+ save_memory: tool({
1295
+ description: "Save a piece of information to persistent memory. Use a descriptive key like 'user:name' or 'project:status'.",
1296
+ parameters: z4.object({
1297
+ key: z4.string().describe("A descriptive key for this memory (e.g. 'user:name', 'preference:color')"),
1298
+ value: z4.string().describe("The information to remember")
1299
+ }),
1300
+ execute: async ({ key, value }, ctx) => {
1301
+ await ctx.kv.set(key, value);
1302
+ return { saved: key };
1303
+ }
1304
+ }),
1305
+ recall_memory: tool({
1306
+ description: "Retrieve a previously saved memory by its key.",
1307
+ parameters: z4.object({
1308
+ key: z4.string().describe("The key to look up")
1309
+ }),
1310
+ execute: async ({ key }, ctx) => {
1311
+ const value = await ctx.kv.get(key);
1312
+ if (value === null) return { found: false, key };
1313
+ return { found: true, key, value };
1314
+ }
1315
+ }),
1316
+ list_memories: tool({
1317
+ description: "List all saved memory keys, optionally filtered by a prefix (e.g. 'user:').",
1318
+ parameters: z4.object({
1319
+ prefix: z4.string().describe("Prefix to filter keys (e.g. 'user:'). Use empty string for all.").optional()
1320
+ }),
1321
+ execute: async ({ prefix }, ctx) => {
1322
+ const entries = await ctx.kv.list(prefix ?? "");
1323
+ return { count: entries.length, keys: entries.map((e) => e.key) };
1358
1324
  }
1359
- }
1360
- }
1361
- return defs;
1362
- }
1363
- function getBuiltinToolSchemas(names) {
1364
- return names.flatMap((name) => {
1365
- const multiCreator = MULTI_TOOL_BUILTINS[name];
1366
- if (multiCreator) {
1367
- return Object.entries(multiCreator()).map(([toolName, def2]) => ({
1368
- name: toolName,
1369
- description: def2.description,
1370
- parameters: z4.toJSONSchema(def2.parameters ?? EMPTY_PARAMS)
1371
- }));
1372
- }
1373
- const creator = TOOL_CREATORS[name];
1374
- if (!creator) return [];
1375
- const def = creator();
1376
- return [
1377
- {
1378
- name,
1379
- description: def.description,
1380
- parameters: z4.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1325
+ }),
1326
+ forget_memory: tool({
1327
+ description: "Delete a previously saved memory by its key.",
1328
+ parameters: z4.object({
1329
+ key: z4.string().describe("The key to delete")
1330
+ }),
1331
+ execute: async ({ key }, ctx) => {
1332
+ await ctx.kv.delete(key);
1333
+ return { deleted: key };
1381
1334
  }
1382
- ];
1383
- });
1335
+ })
1336
+ };
1384
1337
  }
1385
- var webSearchParams, BRAVE_SEARCH_URL, BraveSearchResponseSchema, MAX_PAGE_CHARS, MAX_HTML_BYTES, visitWebpageParams, fetchJsonParams, runCodeParams, vectorSearchParams, TOOL_CREATORS, MULTI_TOOL_BUILTINS;
1338
+ var FETCH_TIMEOUT_MS, RUN_CODE_TIMEOUT, webSearchParams, BRAVE_SEARCH_URL, BraveSearchResponseSchema, MAX_PAGE_CHARS, MAX_HTML_BYTES, visitWebpageParams, fetchJsonParams, runCodeParams, vectorSearchParams, TOOL_REGISTRY;
1386
1339
  var init_builtin_tools = __esm({
1387
1340
  "sdk/builtin_tools.ts"() {
1388
1341
  "use strict";
1389
1342
  init_internal_types();
1390
- init_memory_tools();
1343
+ init_utils();
1344
+ init_types();
1345
+ FETCH_TIMEOUT_MS = 15e3;
1346
+ RUN_CODE_TIMEOUT = 5e3;
1391
1347
  webSearchParams = z4.object({
1392
1348
  query: z4.string().describe("The search query"),
1393
1349
  max_results: z4.number().describe("Maximum number of results to return (default 5)").optional()
@@ -1422,16 +1378,13 @@ var init_builtin_tools = __esm({
1422
1378
  ),
1423
1379
  topK: z4.number().describe("Maximum results to return (default: 5)").optional()
1424
1380
  });
1425
- TOOL_CREATORS = {
1426
- web_search: createWebSearch,
1427
- visit_webpage: createVisitWebpage,
1428
- fetch_json: createFetchJson,
1429
- run_code: createRunCode,
1430
- // vector_search uses a stub for schema generation
1431
- vector_search: () => createVectorSearch(async () => "")
1432
- };
1433
- MULTI_TOOL_BUILTINS = {
1434
- memory: memoryTools
1381
+ TOOL_REGISTRY = {
1382
+ web_search: { create: (opts) => createWebSearch(opts?.fetch) },
1383
+ visit_webpage: { create: (opts) => createVisitWebpage(opts?.fetch) },
1384
+ fetch_json: { create: (opts) => createFetchJson(opts?.fetch) },
1385
+ run_code: { create: createRunCode },
1386
+ vector_search: { create: (opts) => createVectorSearch(opts?.vectorSearch ?? (async () => "")) },
1387
+ memory: { multi: memoryTools }
1435
1388
  };
1436
1389
  }
1437
1390
  });
@@ -1502,291 +1455,6 @@ var init_kv = __esm({
1502
1455
  }
1503
1456
  });
1504
1457
 
1505
- // sdk/s2s.ts
1506
- import { z as z5 } from "zod";
1507
- function uint8ToBase64(bytes) {
1508
- return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
1509
- }
1510
- function base64ToUint8(base64) {
1511
- const buf = Buffer.from(base64, "base64");
1512
- return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
1513
- }
1514
- function dispatchS2sMessage(target, msg) {
1515
- switch (msg.type) {
1516
- case "session.ready":
1517
- target.dispatchEvent(
1518
- new CustomEvent("ready", {
1519
- detail: { session_id: msg.session_id }
1520
- })
1521
- );
1522
- break;
1523
- case "session.updated":
1524
- target.dispatchEvent(new CustomEvent("session_updated", { detail: msg }));
1525
- break;
1526
- case "input.speech.started":
1527
- target.dispatchEvent(new CustomEvent("speech_started"));
1528
- break;
1529
- case "input.speech.stopped":
1530
- target.dispatchEvent(new CustomEvent("speech_stopped"));
1531
- break;
1532
- case "transcript.user.delta":
1533
- target.dispatchEvent(
1534
- new CustomEvent("user_transcript_delta", {
1535
- detail: { text: msg.text }
1536
- })
1537
- );
1538
- break;
1539
- case "transcript.user":
1540
- target.dispatchEvent(
1541
- new CustomEvent("user_transcript", {
1542
- detail: {
1543
- item_id: msg.item_id,
1544
- text: msg.text
1545
- }
1546
- })
1547
- );
1548
- break;
1549
- case "reply.started":
1550
- target.dispatchEvent(
1551
- new CustomEvent("reply_started", {
1552
- detail: { reply_id: msg.reply_id }
1553
- })
1554
- );
1555
- break;
1556
- // reply.audio handled on the fast path — never reaches dispatch.
1557
- case "transcript.agent.delta":
1558
- target.dispatchEvent(
1559
- new CustomEvent("agent_transcript_delta", {
1560
- detail: { text: msg.delta }
1561
- })
1562
- );
1563
- break;
1564
- case "transcript.agent":
1565
- target.dispatchEvent(
1566
- new CustomEvent("agent_transcript", {
1567
- detail: { text: msg.text }
1568
- })
1569
- );
1570
- break;
1571
- case "tool.call":
1572
- target.dispatchEvent(
1573
- new CustomEvent("tool_call", {
1574
- detail: {
1575
- call_id: msg.call_id,
1576
- name: msg.name,
1577
- args: msg.args
1578
- }
1579
- })
1580
- );
1581
- break;
1582
- case "reply.done":
1583
- target.dispatchEvent(
1584
- new CustomEvent("reply_done", {
1585
- detail: { status: msg.status }
1586
- })
1587
- );
1588
- break;
1589
- case "session.error": {
1590
- const isExpired = msg.code === "session_not_found" || msg.code === "session_forbidden";
1591
- target.dispatchEvent(
1592
- new CustomEvent(isExpired ? "session_expired" : "error", {
1593
- detail: { code: msg.code, message: msg.message }
1594
- })
1595
- );
1596
- break;
1597
- }
1598
- case "reply.content_part.started":
1599
- case "reply.content_part.done":
1600
- break;
1601
- case "error":
1602
- target.dispatchEvent(
1603
- new CustomEvent("error", {
1604
- detail: { code: "connection", message: msg.message }
1605
- })
1606
- );
1607
- break;
1608
- }
1609
- }
1610
- function connectS2s(opts) {
1611
- const { apiKey, config, createWebSocket, logger: log = consoleLogger } = opts;
1612
- return new Promise((resolve, reject) => {
1613
- log.debug("S2S connecting", { url: config.wssUrl });
1614
- const ws = createWebSocket(config.wssUrl, {
1615
- headers: { Authorization: `Bearer ${apiKey}` }
1616
- });
1617
- const target = new EventTarget();
1618
- let opened = false;
1619
- function send(msg) {
1620
- if (ws.readyState !== WS_OPEN) return;
1621
- const json = JSON.stringify(msg);
1622
- if (msg.type !== "input.audio") {
1623
- log.debug(
1624
- `S2S >> ${msg.type}`,
1625
- msg.type === "session.update" ? { payload: json } : void 0
1626
- );
1627
- }
1628
- ws.send(json);
1629
- }
1630
- const handle = Object.assign(target, {
1631
- sendAudio(audio) {
1632
- if (ws.readyState !== WS_OPEN) return;
1633
- if (ws.sendBinary) {
1634
- const ab = audio.buffer.slice(
1635
- audio.byteOffset,
1636
- audio.byteOffset + audio.byteLength
1637
- );
1638
- ws.sendBinary(ab);
1639
- } else {
1640
- ws.send(`{"type":"input.audio","audio":"${uint8ToBase64(audio)}"}`);
1641
- }
1642
- },
1643
- sendToolResult(callId, result) {
1644
- const msg = { type: "tool.result", call_id: callId, result };
1645
- log.debug("S2S >> tool.result", { call_id: callId, resultLength: result.length });
1646
- send(msg);
1647
- },
1648
- updateSession(sessionConfig) {
1649
- send({ type: "session.update", session: sessionConfig });
1650
- },
1651
- resumeSession(sessionId) {
1652
- send({ type: "session.resume", session_id: sessionId });
1653
- },
1654
- close() {
1655
- log.debug("S2S closing");
1656
- ws.close();
1657
- }
1658
- });
1659
- ws.on("open", () => {
1660
- opened = true;
1661
- log.info("S2S WebSocket open");
1662
- resolve(handle);
1663
- });
1664
- ws.on("audio", (data) => {
1665
- const ab = data;
1666
- target.dispatchEvent(new CustomEvent("audio", { detail: { audio: new Uint8Array(ab) } }));
1667
- });
1668
- ws.on("message", (data) => {
1669
- let raw;
1670
- try {
1671
- raw = JSON.parse(String(data));
1672
- } catch {
1673
- return;
1674
- }
1675
- const obj = raw;
1676
- if (obj.type !== "reply.audio" && obj.type !== "input.audio" && obj.type !== "transcript.agent.delta") {
1677
- log.info(`S2S << ${obj.type}`);
1678
- }
1679
- if (obj.type === "reply.audio" && typeof obj.data === "string") {
1680
- const audioBytes = base64ToUint8(obj.data);
1681
- target.dispatchEvent(new CustomEvent("audio", { detail: { audio: audioBytes } }));
1682
- return;
1683
- }
1684
- const parsed = S2sServerMessageSchema.safeParse(raw);
1685
- if (!parsed.success) {
1686
- log.warn(
1687
- `S2S << unrecognised message type: ${obj.type ?? JSON.stringify(raw).slice(0, 200)}`
1688
- );
1689
- return;
1690
- }
1691
- const msg = parsed.data;
1692
- dispatchS2sMessage(target, msg);
1693
- });
1694
- ws.on("close", (code, reason) => {
1695
- log.info("S2S WebSocket closed", {
1696
- code: typeof code === "number" ? code : 0,
1697
- reason: reason instanceof Uint8Array ? new TextDecoder().decode(reason) : String(reason ?? "")
1698
- });
1699
- target.dispatchEvent(new CustomEvent("close"));
1700
- });
1701
- ws.on("error", (err) => {
1702
- const errObj = err instanceof Error ? err : new Error(String(err));
1703
- log.error("S2S WebSocket error", { error: errObj.message });
1704
- if (!opened) {
1705
- reject(errObj);
1706
- } else {
1707
- target.dispatchEvent(
1708
- new CustomEvent("error", {
1709
- detail: { code: "ws_error", message: errObj.message }
1710
- })
1711
- );
1712
- }
1713
- });
1714
- });
1715
- }
1716
- var WS_OPEN, S2sServerMessageSchema;
1717
- var init_s2s = __esm({
1718
- "sdk/s2s.ts"() {
1719
- "use strict";
1720
- init_runtime();
1721
- WS_OPEN = 1;
1722
- S2sServerMessageSchema = z5.discriminatedUnion("type", [
1723
- z5.object({ type: z5.literal("session.ready"), session_id: z5.string() }),
1724
- z5.object({ type: z5.literal("session.updated") }).passthrough(),
1725
- z5.object({ type: z5.literal("input.speech.started") }),
1726
- z5.object({ type: z5.literal("input.speech.stopped") }),
1727
- z5.object({ type: z5.literal("transcript.user.delta"), text: z5.string() }),
1728
- z5.object({
1729
- type: z5.literal("transcript.user"),
1730
- item_id: z5.string(),
1731
- text: z5.string()
1732
- }),
1733
- z5.object({ type: z5.literal("reply.started"), reply_id: z5.string() }),
1734
- // reply.audio is handled on the fast path before Zod — see message handler.
1735
- z5.object({ type: z5.literal("transcript.agent.delta"), delta: z5.string() }).passthrough(),
1736
- z5.object({ type: z5.literal("transcript.agent"), text: z5.string() }),
1737
- z5.object({ type: z5.literal("reply.content_part.started") }).passthrough(),
1738
- z5.object({ type: z5.literal("reply.content_part.done") }).passthrough(),
1739
- z5.object({
1740
- type: z5.literal("tool.call"),
1741
- call_id: z5.string(),
1742
- name: z5.string(),
1743
- args: z5.record(z5.string(), z5.unknown()).optional().default({})
1744
- }),
1745
- z5.object({
1746
- type: z5.literal("reply.done"),
1747
- status: z5.string().optional()
1748
- }),
1749
- z5.object({
1750
- type: z5.literal("session.error"),
1751
- code: z5.string(),
1752
- message: z5.string()
1753
- }),
1754
- // Connection-level error (bare "error" without "session." prefix).
1755
- z5.object({
1756
- type: z5.literal("error"),
1757
- message: z5.string()
1758
- })
1759
- ]);
1760
- }
1761
- });
1762
-
1763
- // sdk/system_prompt.ts
1764
- function buildSystemPrompt(config, opts) {
1765
- const { hasTools } = opts;
1766
- const agentInstructions = config.instructions && config.instructions !== DEFAULT_INSTRUCTIONS ? `
1767
-
1768
- Agent-Specific Instructions:
1769
- ${config.instructions}` : "";
1770
- 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.' : "";
1771
- const today = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
1772
- weekday: "long",
1773
- year: "numeric",
1774
- month: "long",
1775
- day: "numeric"
1776
- });
1777
- return DEFAULT_INSTRUCTIONS + `
1778
-
1779
- Today's date is ${today}.` + agentInstructions + toolPreamble + (opts?.voice ? VOICE_RULES : "");
1780
- }
1781
- var VOICE_RULES;
1782
- var init_system_prompt = __esm({
1783
- "sdk/system_prompt.ts"() {
1784
- "use strict";
1785
- init_types();
1786
- 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';
1787
- }
1788
- });
1789
-
1790
1458
  // sdk/session.ts
1791
1459
  function createS2sSession(opts) {
1792
1460
  const {
@@ -1817,7 +1485,6 @@ function createS2sSession(opts) {
1817
1485
  }));
1818
1486
  let s2s = null;
1819
1487
  const sessionAbort = new AbortController();
1820
- let audioReady = false;
1821
1488
  let toolCallCount = 0;
1822
1489
  let turnPromise = null;
1823
1490
  let conversationMessages = [];
@@ -1829,36 +1496,21 @@ function createS2sSession(opts) {
1829
1496
  if (!hookInvoker) return null;
1830
1497
  try {
1831
1498
  return await hookInvoker.resolveTurnConfig(id, HOOK_TIMEOUT_MS);
1832
- } catch {
1499
+ } catch (err) {
1500
+ log.warn("resolveTurnConfig hook failed", { err: errorMessage(err) });
1833
1501
  return null;
1834
1502
  }
1835
1503
  }
1836
1504
  function invokeHook(hook, arg) {
1837
1505
  if (!hookInvoker) return;
1838
- const run = async () => {
1839
- switch (hook) {
1840
- case "onConnect":
1841
- await hookInvoker.onConnect(id, HOOK_TIMEOUT_MS);
1842
- break;
1843
- case "onDisconnect":
1844
- await hookInvoker.onDisconnect(id, HOOK_TIMEOUT_MS);
1845
- break;
1846
- case "onTurn":
1847
- await hookInvoker.onTurn(id, arg, HOOK_TIMEOUT_MS);
1848
- break;
1849
- case "onError":
1850
- await hookInvoker.onError(id, arg, HOOK_TIMEOUT_MS);
1851
- break;
1852
- case "onStep":
1853
- await hookInvoker.onStep(id, arg, HOOK_TIMEOUT_MS);
1854
- break;
1855
- }
1856
- };
1857
- run().catch((err) => {
1858
- log.warn(`${hook} hook failed`, {
1859
- err: err instanceof Error ? err.message : String(err)
1506
+ try {
1507
+ const h = hookInvoker[hook];
1508
+ Promise.resolve(h.call(hookInvoker, id, arg, HOOK_TIMEOUT_MS)).catch((err) => {
1509
+ log.warn(`${hook} hook failed`, { err: errorMessage(err) });
1860
1510
  });
1861
- });
1511
+ } catch (err) {
1512
+ log.warn(`${hook} hook failed`, { err: errorMessage(err) });
1513
+ }
1862
1514
  }
1863
1515
  function checkTurnLimits(turnConfig, name) {
1864
1516
  const maxSteps = turnConfig?.maxSteps ?? agentConfig.maxSteps;
@@ -1898,7 +1550,7 @@ function createS2sSession(opts) {
1898
1550
  try {
1899
1551
  result = await executeTool(name, parsedArgs, id, conversationMessages);
1900
1552
  } catch (err) {
1901
- const msg = err instanceof Error ? err.message : String(err);
1553
+ const msg = errorMessage(err);
1902
1554
  log.error("Tool execution failed", { tool: name, error: msg });
1903
1555
  result = JSON.stringify({ error: msg });
1904
1556
  }
@@ -1913,6 +1565,79 @@ function createS2sSession(opts) {
1913
1565
  function on(target, event, handler) {
1914
1566
  target.addEventListener(event, handler);
1915
1567
  }
1568
+ function setupListeners(handle) {
1569
+ on(handle, "ready", (e) => {
1570
+ s2sSessionId = e.detail.session_id;
1571
+ log.info("S2S session ready", { session_id: s2sSessionId });
1572
+ });
1573
+ on(handle, "session_expired", () => {
1574
+ log.info("S2S session expired, reconnecting fresh");
1575
+ s2sSessionId = null;
1576
+ handle.close();
1577
+ });
1578
+ for (const type of ["speech_started", "speech_stopped"]) {
1579
+ handle.addEventListener(type, () => client.event({ type }));
1580
+ }
1581
+ on(handle, "user_transcript_delta", (e) => {
1582
+ client.event({ type: "transcript", text: e.detail.text, isFinal: false });
1583
+ });
1584
+ on(handle, "user_transcript", (e) => {
1585
+ const { text } = e.detail;
1586
+ log.info("S2S user transcript", { text });
1587
+ client.event({ type: "transcript", text, isFinal: true });
1588
+ client.event({ type: "turn", text });
1589
+ conversationMessages.push({ role: "user", content: text });
1590
+ invokeHook("onTurn", text);
1591
+ });
1592
+ handle.addEventListener("reply_started", () => {
1593
+ toolCallCount = 0;
1594
+ });
1595
+ on(handle, "audio", (e) => {
1596
+ client.playAudioChunk(e.detail.audio);
1597
+ });
1598
+ on(handle, "agent_transcript_delta", (e) => {
1599
+ client.event({ type: "chat_delta", text: e.detail.text });
1600
+ });
1601
+ on(handle, "agent_transcript", (e) => {
1602
+ const { text } = e.detail;
1603
+ client.event({ type: "chat", text });
1604
+ conversationMessages.push({ role: "assistant", content: text });
1605
+ });
1606
+ on(handle, "tool_call", (e) => {
1607
+ const p = handleToolCall(e.detail).catch((err) => {
1608
+ log.error("Tool call handler failed", { err: errorMessage(err) });
1609
+ });
1610
+ turnPromise = (turnPromise ?? Promise.resolve()).then(() => p);
1611
+ });
1612
+ on(handle, "reply_done", (e) => {
1613
+ if (e.detail.status === "interrupted") {
1614
+ log.info("S2S reply interrupted (barge-in)");
1615
+ pendingTools = [];
1616
+ client.event({ type: "cancelled" });
1617
+ } else if (pendingTools.length > 0) {
1618
+ for (const tool2 of pendingTools) s2s?.sendToolResult(tool2.call_id, tool2.result);
1619
+ pendingTools = [];
1620
+ } else {
1621
+ client.playAudioDone();
1622
+ client.event({ type: "tts_done" });
1623
+ }
1624
+ });
1625
+ on(handle, "error", (e) => {
1626
+ log.error("S2S error", { code: e.detail.code, message: e.detail.message });
1627
+ client.event({ type: "error", code: "internal", message: e.detail.message });
1628
+ handle.close();
1629
+ });
1630
+ handle.addEventListener("close", () => {
1631
+ log.info("S2S closed");
1632
+ s2s = null;
1633
+ if (!sessionAbort.signal.aborted) {
1634
+ log.info("Attempting S2S reconnect");
1635
+ connectAndSetup().catch((err) => {
1636
+ log.error("S2S reconnect failed", { error: errorMessage(err) });
1637
+ });
1638
+ }
1639
+ });
1640
+ }
1916
1641
  async function connectAndSetup() {
1917
1642
  if (connecting) {
1918
1643
  pendingReconnect = true;
@@ -1920,106 +1645,15 @@ function createS2sSession(opts) {
1920
1645
  }
1921
1646
  connecting = true;
1922
1647
  try {
1923
- const handle = await _internals2.connectS2s({
1648
+ const handle = await _internals.connectS2s({
1924
1649
  apiKey,
1925
1650
  config: s2sConfig,
1926
1651
  createWebSocket,
1927
1652
  logger: log
1928
1653
  });
1929
- on(handle, "ready", (e) => {
1930
- s2sSessionId = e.detail.session_id;
1931
- log.info("S2S session ready", { session_id: s2sSessionId });
1932
- });
1933
- on(handle, "session_expired", () => {
1934
- log.info("S2S session expired, reconnecting fresh");
1935
- s2sSessionId = null;
1936
- handle.close();
1937
- });
1938
- handle.addEventListener("speech_started", () => {
1939
- client.event({ type: "speech_started" });
1940
- });
1941
- handle.addEventListener("speech_stopped", () => {
1942
- client.event({ type: "speech_stopped" });
1943
- });
1944
- on(handle, "user_transcript_delta", (e) => {
1945
- client.event({
1946
- type: "transcript",
1947
- text: e.detail.text,
1948
- isFinal: false
1949
- });
1950
- });
1951
- on(handle, "user_transcript", (e) => {
1952
- const { text } = e.detail;
1953
- log.info("S2S user transcript", { text });
1954
- client.event({ type: "transcript", text, isFinal: true });
1955
- client.event({ type: "turn", text });
1956
- conversationMessages.push({ role: "user", content: text });
1957
- invokeHook("onTurn", text);
1958
- });
1959
- handle.addEventListener("reply_started", () => {
1960
- toolCallCount = 0;
1961
- });
1962
- on(handle, "audio", (e) => {
1963
- client.playAudioChunk(e.detail.audio);
1964
- });
1965
- on(handle, "agent_transcript_delta", (e) => {
1966
- client.event({ type: "chat_delta", text: e.detail.text });
1967
- });
1968
- on(handle, "agent_transcript", (e) => {
1969
- const { text } = e.detail;
1970
- client.event({ type: "chat", text });
1971
- conversationMessages.push({ role: "assistant", content: text });
1972
- });
1973
- on(handle, "tool_call", (e) => {
1974
- const p = handleToolCall(e.detail).catch((err) => {
1975
- log.error("Tool call handler failed", {
1976
- err: err instanceof Error ? err.message : String(err)
1977
- });
1978
- });
1979
- turnPromise = (turnPromise ?? Promise.resolve()).then(() => p);
1980
- });
1981
- on(handle, "reply_done", (e) => {
1982
- if (e.detail.status === "interrupted") {
1983
- log.info("S2S reply interrupted (barge-in)");
1984
- pendingTools = [];
1985
- client.event({ type: "cancelled" });
1986
- } else if (pendingTools.length > 0) {
1987
- for (const tool2 of pendingTools) {
1988
- s2s?.sendToolResult(tool2.call_id, tool2.result);
1989
- }
1990
- pendingTools = [];
1991
- } else {
1992
- client.playAudioDone();
1993
- client.event({ type: "tts_done" });
1994
- }
1995
- });
1996
- on(handle, "error", (e) => {
1997
- log.error("S2S error", {
1998
- code: e.detail.code,
1999
- message: e.detail.message
2000
- });
2001
- client.event({
2002
- type: "error",
2003
- code: "internal",
2004
- message: e.detail.message
2005
- });
2006
- handle.close();
2007
- });
2008
- handle.addEventListener("close", () => {
2009
- log.info("S2S closed");
2010
- s2s = null;
2011
- if (!sessionAbort.signal.aborted) {
2012
- log.info("Attempting S2S reconnect");
2013
- connectAndSetup().catch((err) => {
2014
- const msg = err instanceof Error ? err.message : String(err);
2015
- log.error("S2S reconnect failed", { error: msg });
2016
- });
2017
- }
2018
- });
1654
+ setupListeners(handle);
2019
1655
  if (s2sSessionId) {
2020
- log.info("Attempting S2S session resume", {
2021
- session_id: s2sSessionId
2022
- });
1656
+ log.info("Attempting S2S session resume", { session_id: s2sSessionId });
2023
1657
  handle.resumeSession(s2sSessionId);
2024
1658
  } else {
2025
1659
  handle.updateSession({
@@ -2030,7 +1664,7 @@ function createS2sSession(opts) {
2030
1664
  }
2031
1665
  s2s = handle;
2032
1666
  } catch (err) {
2033
- const msg = err instanceof Error ? err.message : String(err);
1667
+ const msg = errorMessage(err);
2034
1668
  log.error("S2S connect failed", { error: msg });
2035
1669
  client.event({ type: "error", code: "internal", message: msg });
2036
1670
  } finally {
@@ -2038,8 +1672,7 @@ function createS2sSession(opts) {
2038
1672
  if (pendingReconnect && !sessionAbort.signal.aborted) {
2039
1673
  pendingReconnect = false;
2040
1674
  connectAndSetup().catch((err) => {
2041
- const msg = err instanceof Error ? err.message : String(err);
2042
- log.error("S2S deferred reconnect failed", { error: msg });
1675
+ log.error("S2S deferred reconnect failed", { error: errorMessage(err) });
2043
1676
  });
2044
1677
  }
2045
1678
  }
@@ -2063,8 +1696,6 @@ function createS2sSession(opts) {
2063
1696
  s2s?.sendAudio(data);
2064
1697
  },
2065
1698
  onAudioReady() {
2066
- if (audioReady) return;
2067
- audioReady = true;
2068
1699
  },
2069
1700
  onCancel() {
2070
1701
  client.event({ type: "cancelled" });
@@ -2088,17 +1719,36 @@ function createS2sSession(opts) {
2088
1719
  }
2089
1720
  };
2090
1721
  }
2091
- var _internals2;
1722
+ function buildSystemPrompt(config, opts) {
1723
+ const { hasTools } = opts;
1724
+ const agentInstructions = config.instructions && config.instructions !== DEFAULT_INSTRUCTIONS ? `
1725
+
1726
+ Agent-Specific Instructions:
1727
+ ${config.instructions}` : "";
1728
+ 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.' : "";
1729
+ const today = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
1730
+ weekday: "long",
1731
+ year: "numeric",
1732
+ month: "long",
1733
+ day: "numeric"
1734
+ });
1735
+ return DEFAULT_INSTRUCTIONS + `
1736
+
1737
+ Today's date is ${today}.` + agentInstructions + toolPreamble + (opts.voice ? VOICE_RULES : "");
1738
+ }
1739
+ var _internals, VOICE_RULES;
2092
1740
  var init_session = __esm({
2093
1741
  "sdk/session.ts"() {
2094
1742
  "use strict";
1743
+ init_utils();
2095
1744
  init_protocol();
2096
1745
  init_runtime();
2097
1746
  init_s2s();
2098
- init_system_prompt();
2099
- _internals2 = {
1747
+ init_types();
1748
+ _internals = {
2100
1749
  connectS2s
2101
1750
  };
1751
+ 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';
2102
1752
  }
2103
1753
  });
2104
1754
 
@@ -2149,11 +1799,10 @@ var init_vector = __esm({
2149
1799
 
2150
1800
  // sdk/worker_entry.ts
2151
1801
  function buildToolContext(opts) {
2152
- const { env, sessionId, state, kv, vector, messages } = opts;
1802
+ const { env, state, kv, vector, messages } = opts;
2153
1803
  return {
2154
- sessionId: sessionId ?? "",
2155
1804
  env: { ...env },
2156
- abortSignal: AbortSignal.timeout(TOOL_HANDLER_TIMEOUT),
1805
+ abortSignal: AbortSignal.timeout(TOOL_EXECUTION_TIMEOUT_MS),
2157
1806
  state: state ?? {},
2158
1807
  get kv() {
2159
1808
  if (!kv) throw new Error("KV not available");
@@ -2182,21 +1831,18 @@ async function executeToolCall(name, args, options) {
2182
1831
  if (result == null) return "null";
2183
1832
  return typeof result === "string" ? result : JSON.stringify(result);
2184
1833
  } catch (err) {
2185
- if (err instanceof DOMException && err.name === "TimeoutError") {
2186
- console.warn(`[tool-executor] Tool execution timed out: ${name}`);
2187
- return `Error: Tool "${name}" timed out after ${TOOL_HANDLER_TIMEOUT}ms`;
2188
- }
2189
1834
  console.warn(`[tool-executor] Tool execution failed: ${name}`, err);
2190
- return `Error: ${err instanceof Error ? err.message : String(err)}`;
1835
+ return `Error: ${errorMessage(err)}`;
2191
1836
  }
2192
1837
  }
2193
- var yieldTick, TOOL_HANDLER_TIMEOUT;
1838
+ var yieldTick;
2194
1839
  var init_worker_entry = __esm({
2195
1840
  "sdk/worker_entry.ts"() {
2196
1841
  "use strict";
2197
1842
  init_internal_types();
1843
+ init_utils();
1844
+ init_protocol();
2198
1845
  yieldTick = () => new Promise((r) => setTimeout(r, 0));
2199
- TOOL_HANDLER_TIMEOUT = 3e4;
2200
1846
  }
2201
1847
  });
2202
1848
 
@@ -2248,7 +1894,6 @@ function createDirectExecutor(opts) {
2248
1894
  }
2249
1895
  function makeHookContext(sessionId) {
2250
1896
  return {
2251
- sessionId,
2252
1897
  env: frozenEnv,
2253
1898
  state: getState(sessionId),
2254
1899
  get kv() {
@@ -2265,7 +1910,6 @@ function createDirectExecutor(opts) {
2265
1910
  return executeToolCall(name, args, {
2266
1911
  tool: tool2,
2267
1912
  env: frozenEnv,
2268
- sessionId,
2269
1913
  state: getState(sessionId ?? ""),
2270
1914
  kv,
2271
1915
  vector,
@@ -2344,9 +1988,6 @@ var init_direct_executor = __esm({
2344
1988
  });
2345
1989
 
2346
1990
  // sdk/ws_handler.ts
2347
- function isValidAudioChunk(data) {
2348
- return data.byteLength > 0 && data.byteLength <= MAX_AUDIO_CHUNK_BYTES && data.byteLength % 2 === 0;
2349
- }
2350
1991
  function createClientSink(ws) {
2351
1992
  function safeSend(data) {
2352
1993
  try {
@@ -2378,19 +2019,9 @@ function toUint8Array(data) {
2378
2019
  const buf = data;
2379
2020
  return new Uint8Array(buf.buffer ?? data, buf.byteOffset ?? 0, buf.byteLength);
2380
2021
  }
2381
- function handleBinaryAudio(data, session, log, ctx, sid) {
2022
+ function handleBinaryAudio(data, session) {
2382
2023
  if (!isBinaryData(data)) return false;
2383
- const chunk = toUint8Array(data);
2384
- if (!isValidAudioChunk(chunk)) {
2385
- log.warn("Invalid audio chunk, dropping", {
2386
- ...ctx,
2387
- sid,
2388
- bytes: chunk.byteLength,
2389
- aligned: chunk.byteLength % 2 === 0
2390
- });
2391
- return true;
2392
- }
2393
- session.onAudio(chunk);
2024
+ session.onAudio(toUint8Array(data));
2394
2025
  return true;
2395
2026
  }
2396
2027
  function handleTextMessage(data, session, log, ctx, sid) {
@@ -2425,7 +2056,7 @@ function handleTextMessage(data, session, log, ctx, sid) {
2425
2056
  }
2426
2057
  function wireSessionSocket(ws, opts) {
2427
2058
  const { sessions, logger: log = consoleLogger } = opts;
2428
- const sessionId = opts.uid ?? crypto.randomUUID();
2059
+ const sessionId = crypto.randomUUID();
2429
2060
  const sid = sessionId.slice(0, 8);
2430
2061
  const ctx = opts.logContext ?? {};
2431
2062
  let session = null;
@@ -2436,8 +2067,11 @@ function wireSessionSocket(ws, opts) {
2436
2067
  session = opts.createSession(sessionId, client);
2437
2068
  sessions.set(sessionId, session);
2438
2069
  ws.send(JSON.stringify({ type: "config", ...opts.readyConfig }));
2439
- void session.start();
2440
- log.info("Session ready", { ...ctx, sid });
2070
+ session.start().then(() => {
2071
+ log.info("Session ready", { ...ctx, sid });
2072
+ }).catch((err) => {
2073
+ log.error("Session start failed", { ...ctx, sid, error: errorMessage(err) });
2074
+ });
2441
2075
  }
2442
2076
  if (ws.readyState === 1) {
2443
2077
  onOpen();
@@ -2447,7 +2081,7 @@ function wireSessionSocket(ws, opts) {
2447
2081
  ws.addEventListener("message", (event) => {
2448
2082
  if (!session) return;
2449
2083
  const { data } = event;
2450
- if (handleBinaryAudio(data, session, log, ctx, sid)) return;
2084
+ if (handleBinaryAudio(data, session)) return;
2451
2085
  handleTextMessage(data, session, log, ctx, sid);
2452
2086
  });
2453
2087
  ws.addEventListener("close", () => {
@@ -2464,13 +2098,12 @@ function wireSessionSocket(ws, opts) {
2464
2098
  log.error("WebSocket error", { ...ctx, sid, error: msg });
2465
2099
  });
2466
2100
  }
2467
- var MAX_AUDIO_CHUNK_BYTES;
2468
2101
  var init_ws_handler = __esm({
2469
2102
  "sdk/ws_handler.ts"() {
2470
2103
  "use strict";
2104
+ init_utils();
2471
2105
  init_protocol();
2472
2106
  init_runtime();
2473
- MAX_AUDIO_CHUNK_BYTES = 1048576;
2474
2107
  }
2475
2108
  });
2476
2109
 
@@ -2529,8 +2162,7 @@ function createWintercServer(options) {
2529
2162
  skipGreeting: wsOpts?.skipGreeting ?? false
2530
2163
  }),
2531
2164
  readyConfig,
2532
- logger,
2533
- uid: wsOpts?.uid
2165
+ logger
2534
2166
  });
2535
2167
  },
2536
2168
  async close() {
@@ -2565,7 +2197,7 @@ async function loadWsFactory() {
2565
2197
  try {
2566
2198
  const mod = await import("ws");
2567
2199
  const WS = mod.default ?? mod;
2568
- return (url, opts) => new WS(url, { headers: opts.headers });
2200
+ return (url, opts) => wrapOnStyleWebSocket(new WS(url, { headers: opts.headers }));
2569
2201
  } catch {
2570
2202
  throw new Error(
2571
2203
  "WebSocket factory not provided and `ws` package not found. Install `ws` (`npm install ws`) or pass `createWebSocket` option."
@@ -2573,11 +2205,7 @@ async function loadWsFactory() {
2573
2205
  }
2574
2206
  }
2575
2207
  function resolveEnv(env) {
2576
- const resolved = {};
2577
- for (const [key, value] of Object.entries(env)) {
2578
- if (value !== void 0) resolved[key] = value;
2579
- }
2580
- return resolved;
2208
+ return Object.fromEntries(Object.entries(env).filter(([, v]) => v !== void 0));
2581
2209
  }
2582
2210
  function createServer(options) {
2583
2211
  const {
@@ -2624,6 +2252,23 @@ function createServer(options) {
2624
2252
  await getWsFactory();
2625
2253
  const wintercServer = getWinterc();
2626
2254
  const app = new Hono2();
2255
+ app.onError((err, c) => {
2256
+ logger.error(`${c.req.method} ${new URL(c.req.url).pathname} error: ${err.message}`);
2257
+ return c.json({ error: "Internal Server Error" }, 500);
2258
+ });
2259
+ app.use("/*", async (c, next) => {
2260
+ const start = Date.now();
2261
+ await next();
2262
+ const ms = Date.now() - start;
2263
+ const status = c.res.status;
2264
+ const method = c.req.method;
2265
+ const path8 = new URL(c.req.url).pathname;
2266
+ if (status >= 400) {
2267
+ logger.error(`${method} ${path8} ${status} ${ms}ms`);
2268
+ } else {
2269
+ logger.info(`${method} ${path8} ${status} ${ms}ms`);
2270
+ }
2271
+ });
2627
2272
  if (clientDir) {
2628
2273
  app.use("/*", serveStatic({ root: clientDir }));
2629
2274
  }
@@ -2636,11 +2281,12 @@ function createServer(options) {
2636
2281
  const wsMod = await import("ws");
2637
2282
  const wss = new wsMod.WebSocketServer({ noServer: true });
2638
2283
  nodeServer.on("upgrade", (req, socket, head) => {
2284
+ const reqUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
2285
+ const resume = reqUrl.searchParams.has("resume");
2286
+ logger.info(`WS upgrade ${reqUrl.pathname}${resume ? " (resume)" : ""}`);
2639
2287
  wss.handleUpgrade(req, socket, head, (ws) => {
2640
- const reqUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
2641
2288
  wintercServer.handleWebSocket(ws, {
2642
- skipGreeting: reqUrl.searchParams.has("resume"),
2643
- uid: reqUrl.searchParams.get("uid") ?? void 0
2289
+ skipGreeting: resume
2644
2290
  });
2645
2291
  });
2646
2292
  });
@@ -2665,88 +2311,30 @@ var init_server = __esm({
2665
2311
  "sdk/server.ts"() {
2666
2312
  "use strict";
2667
2313
  init_runtime();
2314
+ init_s2s();
2668
2315
  init_winterc_server();
2669
2316
  }
2670
2317
  });
2671
2318
 
2672
- // cli/cli.ts
2673
- init_help();
2674
- import { readFileSync } from "node:fs";
2675
- import path11 from "node:path";
2676
- import { fileURLToPath as fileURLToPath2 } from "node:url";
2677
- import minimist8 from "minimist";
2678
-
2679
- // cli/build.tsx
2680
- init_build();
2681
- init_discover();
2682
- init_help();
2683
- init_ink();
2684
- init_init2();
2685
- import path6 from "node:path";
2686
- import minimist3 from "minimist";
2687
- import { jsx as jsx5 } from "react/jsx-runtime";
2688
- var buildCommandDef = {
2689
- name: "build",
2690
- description: "Bundle agent and client (validates code without deploying or starting a server)",
2691
- options: [{ flags: "-y, --yes", description: "Accept defaults (no prompts)" }]
2692
- };
2693
- async function runBuildCommand(args, version) {
2694
- const parsed = minimist3(args, {
2695
- boolean: ["help", "yes"],
2696
- alias: { h: "help", y: "yes" }
2697
- });
2698
- if (parsed.help) {
2699
- console.log(subcommandHelp(buildCommandDef, version));
2700
- return;
2701
- }
2702
- const cwd = process.env.INIT_CWD || process.cwd();
2703
- if (!await fileExists(path6.join(cwd, "agent.ts"))) {
2704
- await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
2705
- }
2706
- await runWithInk(async (log) => {
2707
- await buildAgentBundle(cwd, log);
2708
- log(/* @__PURE__ */ jsx5(Step, { action: "Build", msg: "ok" }));
2709
- });
2710
- }
2711
-
2712
- // cli/cli.ts
2713
- init_deploy2();
2714
-
2715
- // cli/dev.tsx
2716
- import path8 from "node:path";
2717
- import minimist4 from "minimist";
2718
-
2719
- // cli/_dev.ts
2720
- init_build();
2721
- init_ink();
2722
- import React3 from "react";
2723
-
2724
2319
  // cli/_server_common.ts
2725
- init_discover();
2726
2320
  import fs5 from "node:fs/promises";
2727
- import path7 from "node:path";
2728
- import { createServer as createViteServer } from "vite";
2321
+ import path5 from "node:path";
2322
+ import { tsImport } from "tsx/esm/api";
2729
2323
  async function loadAgentDef(cwd) {
2730
- const agentPath = path7.resolve(cwd, "agent.ts");
2731
- const vite = await createViteServer({
2732
- root: cwd,
2733
- logLevel: "silent",
2734
- server: { middlewareMode: true }
2735
- });
2736
- try {
2737
- const agentModule = await vite.ssrLoadModule(agentPath);
2738
- const agentDef = agentModule.default;
2739
- if (!agentDef || typeof agentDef !== "object" || !agentDef.name) {
2740
- throw new Error("agent.ts must export a default agent definition (from defineAgent())");
2741
- }
2742
- return agentDef;
2743
- } finally {
2744
- await vite.close();
2324
+ const agentPath = path5.resolve(cwd, "agent.ts");
2325
+ const agentModule = await tsImport(agentPath, cwd);
2326
+ let agentDef = agentModule.default;
2327
+ if (agentDef?.__esModule && agentDef.default) {
2328
+ agentDef = agentDef.default;
2329
+ }
2330
+ if (!agentDef || typeof agentDef !== "object" || !agentDef.name) {
2331
+ throw new Error("agent.ts must export a default agent definition (from defineAgent())");
2745
2332
  }
2333
+ return agentDef;
2746
2334
  }
2747
- async function resolveServerEnv() {
2335
+ async function resolveServerEnv(baseEnv) {
2748
2336
  const env = Object.fromEntries(
2749
- Object.entries(process.env).filter((e) => e[1] !== void 0)
2337
+ Object.entries(baseEnv ?? process.env).filter((e) => e[1] !== void 0)
2750
2338
  );
2751
2339
  if (!env.ASSEMBLYAI_API_KEY) {
2752
2340
  env.ASSEMBLYAI_API_KEY = await getApiKey();
@@ -2754,10 +2342,11 @@ async function resolveServerEnv() {
2754
2342
  return env;
2755
2343
  }
2756
2344
  async function bootServer(agentDef, clientDir, env, port) {
2345
+ const { wrapOnStyleWebSocket: wrapOnStyleWebSocket2 } = await Promise.resolve().then(() => (init_s2s(), s2s_exports));
2757
2346
  const wsMod = await import("ws");
2758
2347
  const WS = wsMod.default ?? wsMod;
2759
- const createWebSocket = (url, opts) => new WS(url, { headers: opts.headers });
2760
- const clientHtml = await fs5.readFile(path7.join(clientDir, "index.html"), "utf-8");
2348
+ const createWebSocket = (url, opts) => wrapOnStyleWebSocket2(new WS(url, { headers: opts.headers }));
2349
+ const clientHtml = await fs5.readFile(path5.join(clientDir, "index.html"), "utf-8");
2761
2350
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2762
2351
  const server = createServer2({
2763
2352
  agent: agentDef,
@@ -2768,155 +2357,186 @@ async function bootServer(agentDef, clientDir, env, port) {
2768
2357
  });
2769
2358
  await server.listen(port);
2770
2359
  }
2360
+ var init_server_common = __esm({
2361
+ "cli/_server_common.ts"() {
2362
+ "use strict";
2363
+ init_discover();
2364
+ }
2365
+ });
2771
2366
 
2772
- // cli/_dev.ts
2367
+ // cli/dev.tsx
2368
+ var dev_exports = {};
2369
+ __export(dev_exports, {
2370
+ _startDevServer: () => _startDevServer,
2371
+ runDevCommand: () => runDevCommand
2372
+ });
2373
+ import { jsx as jsx6 } from "react/jsx-runtime";
2773
2374
  async function _startDevServer(cwd, port, log) {
2774
2375
  const bundle = await buildAgentBundle(cwd, log, { skipRenderCheck: true });
2775
2376
  const agentDef = await loadAgentDef(cwd);
2776
2377
  const env = await resolveServerEnv();
2777
2378
  await bootServer(agentDef, bundle.clientDir, env, port);
2778
- log(React3.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2379
+ log(/* @__PURE__ */ jsx6(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2779
2380
  }
2780
-
2781
- // cli/dev.tsx
2782
- init_discover();
2783
- init_help();
2784
- init_ink();
2785
- init_init2();
2786
- var devCommandDef = {
2787
- name: "dev",
2788
- description: "Start a local development server",
2789
- options: [
2790
- {
2791
- flags: "-p, --port <number>",
2792
- description: "Port to listen on (default: 3000)"
2793
- },
2794
- { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
2795
- ]
2796
- };
2797
- async function runDevCommand(args, version) {
2798
- const parsed = minimist4(args, {
2799
- string: ["port"],
2800
- boolean: ["help", "yes"],
2801
- alias: { p: "port", h: "help", y: "yes" }
2381
+ async function runDevCommand(opts) {
2382
+ const port = Number.parseInt(opts.port, 10);
2383
+ await runWithInk(async ({ log }) => {
2384
+ await _startDevServer(opts.cwd, port, log);
2802
2385
  });
2803
- if (parsed.help) {
2804
- console.log(subcommandHelp(devCommandDef, version));
2805
- return;
2386
+ }
2387
+ var init_dev = __esm({
2388
+ "cli/dev.tsx"() {
2389
+ "use strict";
2390
+ init_build();
2391
+ init_ink();
2392
+ init_server_common();
2806
2393
  }
2807
- const cwd = process.env.INIT_CWD || process.cwd();
2808
- const port = Number.parseInt(parsed.port ?? "3000", 10);
2809
- if (!await fileExists(path8.join(cwd, "agent.ts"))) {
2810
- await runInitCommand(parsed.yes ? ["-y"] : [], version, { quiet: true });
2394
+ });
2395
+
2396
+ // cli/start.tsx
2397
+ var start_exports = {};
2398
+ __export(start_exports, {
2399
+ _startProductionServer: () => _startProductionServer,
2400
+ runStartCommand: () => runStartCommand
2401
+ });
2402
+ import path6 from "node:path";
2403
+ import { jsx as jsx7 } from "react/jsx-runtime";
2404
+ async function _startProductionServer(cwd, port, log) {
2405
+ const clientDir = path6.join(cwd, ".aai", "client");
2406
+ log(/* @__PURE__ */ jsx7(Step, { action: "Start", msg: "loading agent" }));
2407
+ const agentDef = await loadAgentDef(cwd);
2408
+ const env = await resolveServerEnv();
2409
+ await bootServer(agentDef, clientDir, env, port);
2410
+ log(/* @__PURE__ */ jsx7(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2411
+ }
2412
+ async function runStartCommand(opts) {
2413
+ const port = Number.parseInt(opts.port, 10);
2414
+ const buildDir = path6.join(opts.cwd, ".aai", "build");
2415
+ if (!await fileExists(path6.join(buildDir, "worker.js"))) {
2416
+ throw new Error("No build found \u2014 run `aai build` first");
2811
2417
  }
2812
- await getApiKey();
2813
- await runWithInk(async (log) => {
2814
- await _startDevServer(cwd, port, log);
2418
+ await runWithInk(async ({ log }) => {
2419
+ log(/* @__PURE__ */ jsx7(Step, { action: "Start", msg: `production server on port ${port}` }));
2420
+ await _startProductionServer(opts.cwd, port, log);
2815
2421
  });
2816
2422
  }
2423
+ var init_start = __esm({
2424
+ "cli/start.tsx"() {
2425
+ "use strict";
2426
+ init_discover();
2427
+ init_ink();
2428
+ init_server_common();
2429
+ }
2430
+ });
2817
2431
 
2818
- // cli/cli.ts
2819
- init_init2();
2432
+ // cli/secret.tsx
2433
+ var secret_exports = {};
2434
+ __export(secret_exports, {
2435
+ runSecretDelete: () => runSecretDelete,
2436
+ runSecretList: () => runSecretList,
2437
+ runSecretPut: () => runSecretPut
2438
+ });
2439
+ import { jsx as jsx8 } from "react/jsx-runtime";
2440
+ async function apiFetch(cwd, pathSuffix, init) {
2441
+ const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
2442
+ const resp = await fetch(`${serverUrl}/${slug}/secret${pathSuffix}`, {
2443
+ ...init,
2444
+ headers: { Authorization: `Bearer ${apiKey}`, ...init?.headers }
2445
+ });
2446
+ if (!resp.ok) {
2447
+ const text = await resp.text();
2448
+ throw new Error(`Secret operation failed: ${text}`);
2449
+ }
2450
+ return { resp, slug };
2451
+ }
2452
+ async function runSecretPut(cwd, name) {
2453
+ const value = await askPassword(`Enter value for ${name}`);
2454
+ if (!value) throw new Error("No value provided");
2455
+ await runWithInk(async ({ log }) => {
2456
+ const { slug } = await apiFetch(cwd, "", {
2457
+ method: "PUT",
2458
+ headers: { "Content-Type": "application/json" },
2459
+ body: JSON.stringify({ [name]: value })
2460
+ });
2461
+ log(/* @__PURE__ */ jsx8(Step, { action: "Set", msg: `${name} for ${slug}` }));
2462
+ });
2463
+ }
2464
+ async function runSecretDelete(cwd, name) {
2465
+ await runWithInk(async ({ log }) => {
2466
+ const { slug } = await apiFetch(cwd, `/${name}`, { method: "DELETE" });
2467
+ log(/* @__PURE__ */ jsx8(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
2468
+ });
2469
+ }
2470
+ async function runSecretList(cwd) {
2471
+ await runWithInk(async ({ log }) => {
2472
+ const { resp } = await apiFetch(cwd, "");
2473
+ const { vars } = await resp.json();
2474
+ if (vars.length === 0) {
2475
+ log(/* @__PURE__ */ jsx8(StepInfo, { action: "Secrets", msg: "none set" }));
2476
+ } else {
2477
+ for (const name of vars) {
2478
+ log(/* @__PURE__ */ jsx8(Detail, { msg: name }));
2479
+ }
2480
+ }
2481
+ });
2482
+ }
2483
+ var init_secret = __esm({
2484
+ "cli/secret.tsx"() {
2485
+ "use strict";
2486
+ init_discover();
2487
+ init_ink();
2488
+ init_prompts();
2489
+ }
2490
+ });
2820
2491
 
2821
2492
  // cli/rag.tsx
2822
- init_discover();
2823
- init_help();
2824
- init_ink();
2825
- import { render as render3, Text as Text3, useApp as useApp2 } from "ink";
2826
- import minimist5 from "minimist";
2493
+ var rag_exports = {};
2494
+ __export(rag_exports, {
2495
+ chunkPages: () => chunkPages,
2496
+ parsePage: () => parsePage,
2497
+ runRagCommand: () => runRagCommand,
2498
+ slugify: () => slugify,
2499
+ splitPages: () => splitPages,
2500
+ stripNoise: () => stripNoise,
2501
+ upsertChunks: () => upsertChunks
2502
+ });
2503
+ import { Text as Text3 } from "ink";
2827
2504
  import pLimit from "p-limit";
2828
- import { useEffect, useState as useState2 } from "react";
2829
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
2830
- var ragCommandDef = {
2831
- name: "rag",
2832
- description: "Ingest a site's llms-full.txt into the vector store",
2833
- args: [{ name: "url" }],
2834
- options: [
2835
- { flags: "-s, --server <url>", description: "Server URL" },
2836
- {
2837
- flags: "--chunk-size <n>",
2838
- description: "Max chunk size in tokens (default: 512)"
2839
- },
2840
- { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
2841
- ]
2842
- };
2843
- var FETCH_TIMEOUT_MS = 6e4;
2844
- var PAD = 2;
2845
- function RagUI({ url, apiKey, serverUrl, slug, chunkSize, onError }) {
2846
- const { exit } = useApp2();
2847
- const { items, log } = useStepLog();
2848
- const [progress, setProgress] = useState2(null);
2849
- const [err, setErr] = useState2(null);
2850
- useEffect(() => {
2851
- (async () => {
2852
- try {
2853
- await runRag({
2854
- url,
2855
- apiKey,
2856
- serverUrl,
2857
- slug,
2858
- chunkSize,
2859
- log,
2860
- setProgress
2861
- });
2862
- } catch (e) {
2863
- const error = e instanceof Error ? e : new Error(String(e));
2864
- setErr(error.message);
2865
- onError?.(error);
2866
- }
2867
- setProgress(null);
2868
- exit();
2869
- })();
2870
- }, [apiKey, chunkSize, exit, log, onError, serverUrl, slug, url]);
2871
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
2872
- /* @__PURE__ */ jsx6(StepLog, { items }),
2873
- err && /* @__PURE__ */ jsx6(ErrorLine, { msg: err }),
2874
- progress && /* @__PURE__ */ jsxs3(Text3, { children: [
2875
- " ".repeat(PAD + 1),
2876
- "Upsert ",
2877
- progress.completed,
2878
- "/",
2879
- progress.total,
2880
- " (",
2881
- Math.round(progress.completed / progress.total * 100),
2882
- "%)"
2883
- ] })
2884
- ] });
2885
- }
2505
+ import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
2886
2506
  async function runRag(opts) {
2887
- const { url, apiKey, serverUrl, slug, chunkSize, log, setProgress } = opts;
2888
- log(/* @__PURE__ */ jsx6(Step, { action: "Fetch", msg: url }));
2507
+ const { url, apiKey, serverUrl, slug, chunkSize, log, setStatus } = opts;
2508
+ log(/* @__PURE__ */ jsx9(Step, { action: "Fetch", msg: url }));
2889
2509
  const resp = await fetch(url, {
2890
2510
  headers: { "User-Agent": "aai-cli/1.0" },
2891
2511
  redirect: "follow",
2892
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
2512
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
2893
2513
  });
2894
2514
  if (!resp.ok) {
2895
2515
  throw new Error(`Failed to fetch: ${resp.status} ${resp.statusText}`);
2896
2516
  }
2897
2517
  const content = await resp.text();
2898
2518
  if (content.length === 0) {
2899
- log(/* @__PURE__ */ jsx6(Warn, { msg: "File is empty" }));
2519
+ log(/* @__PURE__ */ jsx9(Warn, { msg: "File is empty" }));
2900
2520
  return;
2901
2521
  }
2902
- log(/* @__PURE__ */ jsx6(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
2522
+ log(/* @__PURE__ */ jsx9(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
2903
2523
  const origin = new URL(url).origin;
2904
2524
  const pages = splitPages(content);
2905
- log(/* @__PURE__ */ jsx6(Step, { action: "Parse", msg: `${pages.length} pages` }));
2525
+ log(/* @__PURE__ */ jsx9(Step, { action: "Parse", msg: `${pages.length} pages` }));
2906
2526
  const { RecursiveChunker } = await import("@chonkiejs/core");
2907
2527
  const chunker = await RecursiveChunker.create({ chunkSize });
2908
2528
  const siteSlug = slugify(origin);
2909
2529
  const allChunks = await chunkPages(pages, chunker, origin, siteSlug);
2910
- log(/* @__PURE__ */ jsx6(Step, { action: "Chunk", msg: `${allChunks.length} chunks` }));
2530
+ log(/* @__PURE__ */ jsx9(Step, { action: "Chunk", msg: `${allChunks.length} chunks` }));
2911
2531
  const vectorUrl = `${serverUrl}/${slug}/vector`;
2912
- log(/* @__PURE__ */ jsx6(Info, { msg: `target: ${vectorUrl}` }));
2913
- const result = await upsertChunks(allChunks, vectorUrl, apiKey, setProgress);
2914
- log(/* @__PURE__ */ jsx6(Step, { action: "Done", msg: `${result.upserted} chunks upserted` }));
2532
+ log(/* @__PURE__ */ jsx9(Info, { msg: `target: ${vectorUrl}` }));
2533
+ const result = await upsertChunks(allChunks, vectorUrl, apiKey, setStatus);
2534
+ log(/* @__PURE__ */ jsx9(Step, { action: "Done", msg: `${result.upserted} chunks upserted` }));
2915
2535
  if (result.errors > 0) {
2916
- log(/* @__PURE__ */ jsx6(Warn, { msg: `${result.errors} failed` }));
2917
- if (result.lastError) log(/* @__PURE__ */ jsx6(Info, { msg: `last error: ${result.lastError}` }));
2536
+ log(/* @__PURE__ */ jsx9(Warn, { msg: `${result.errors} failed` }));
2537
+ if (result.lastError) log(/* @__PURE__ */ jsx9(Info, { msg: `last error: ${result.lastError}` }));
2918
2538
  }
2919
- log(/* @__PURE__ */ jsx6(Detail, { msg: `Agent: ${slug}` }));
2539
+ log(/* @__PURE__ */ jsx9(Detail, { msg: `Agent: ${slug}` }));
2920
2540
  }
2921
2541
  async function chunkPages(pages, chunker, origin, siteSlug) {
2922
2542
  const allChunks = [];
@@ -2942,19 +2562,34 @@ ${c.text}` : c.text;
2942
2562
  }
2943
2563
  return allChunks;
2944
2564
  }
2945
- async function upsertChunks(chunks, vectorUrl, apiKey, setProgress) {
2565
+ async function upsertChunks(chunks, vectorUrl, apiKey, setStatus, fetchFn = globalThis.fetch) {
2946
2566
  const total = chunks.length;
2947
2567
  let completed = 0;
2948
2568
  let upserted = 0;
2949
2569
  let errors = 0;
2950
2570
  let lastError = "";
2951
- setProgress({ completed: 0, total });
2571
+ const updateStatus = () => {
2572
+ const pct = Math.round(completed / total * 100);
2573
+ setStatus(
2574
+ /* @__PURE__ */ jsxs3(Text3, { children: [
2575
+ " ",
2576
+ "Upsert ",
2577
+ completed,
2578
+ "/",
2579
+ total,
2580
+ " (",
2581
+ pct,
2582
+ "%)"
2583
+ ] })
2584
+ );
2585
+ };
2586
+ updateStatus();
2952
2587
  const limit = pLimit(5);
2953
2588
  await Promise.all(
2954
2589
  chunks.map(
2955
2590
  (chunk) => limit(async () => {
2956
2591
  try {
2957
- const r = await fetch(vectorUrl, {
2592
+ const r = await fetchFn(vectorUrl, {
2958
2593
  method: "POST",
2959
2594
  headers: {
2960
2595
  "Content-Type": "application/json",
@@ -2974,65 +2609,29 @@ async function upsertChunks(chunks, vectorUrl, apiKey, setProgress) {
2974
2609
  upserted++;
2975
2610
  }
2976
2611
  } catch (err) {
2977
- lastError = err instanceof Error ? err.message : String(err);
2612
+ lastError = errorMessage(err);
2978
2613
  errors++;
2979
2614
  }
2980
2615
  completed++;
2981
- setProgress({ completed, total });
2616
+ updateStatus();
2982
2617
  })
2983
2618
  )
2984
2619
  );
2620
+ setStatus(null);
2985
2621
  return { upserted, errors, lastError };
2986
2622
  }
2987
- async function runRagCommand(args, version) {
2988
- const parsed = minimist5(args, {
2989
- string: ["server", "chunk-size"],
2990
- boolean: ["help", "yes"],
2991
- alias: { s: "server", h: "help", y: "yes" },
2992
- stopEarly: true
2993
- });
2994
- if (parsed.help) {
2995
- console.log(subcommandHelp(ragCommandDef, version));
2996
- return;
2997
- }
2998
- const url = String(parsed._[0] ?? "");
2999
- if (!url) {
3000
- throw new Error(
3001
- "Usage: aai rag <url>\n\nProvide the full URL to a site's llms-full.txt file"
3002
- );
3003
- }
2623
+ async function runRagCommand(opts) {
2624
+ const { url, cwd } = opts;
3004
2625
  try {
3005
2626
  new URL(url);
3006
2627
  } catch {
3007
2628
  throw new Error(`Invalid URL: ${url}`);
3008
2629
  }
3009
- const cwd = process.env.INIT_CWD || process.cwd();
3010
- const config = await readProjectConfig(cwd);
3011
- if (!config) {
3012
- throw new Error("No .aai/project.json found \u2014 deploy first with `aai deploy`");
3013
- }
3014
- const apiKey = await getApiKey();
3015
- const serverUrl = parsed.server || config.serverUrl || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
3016
- const slug = config.slug;
3017
- const chunkSize = Number.parseInt(parsed["chunk-size"] ?? "512", 10);
3018
- let thrownError;
3019
- const app = render3(
3020
- /* @__PURE__ */ jsx6(
3021
- RagUI,
3022
- {
3023
- url,
3024
- apiKey,
3025
- serverUrl,
3026
- slug,
3027
- chunkSize,
3028
- onError: (e) => {
3029
- thrownError = e;
3030
- }
3031
- }
3032
- )
3033
- );
3034
- await app.waitUntilExit();
3035
- if (thrownError) throw thrownError;
2630
+ const { apiKey, serverUrl, slug } = await getServerInfo(cwd, opts.server);
2631
+ const chunkSize = Number.parseInt(opts.chunkSize ?? "512", 10);
2632
+ await runWithInk(async ({ log, setStatus }) => {
2633
+ await runRag({ url, apiKey, serverUrl, slug, chunkSize, log, setStatus });
2634
+ });
3036
2635
  }
3037
2636
  function splitPages(content) {
3038
2637
  const raw = content.split(/^\*{3,}$/m);
@@ -3079,243 +2678,153 @@ function stripNoise(text) {
3079
2678
  function slugify(s) {
3080
2679
  return s.replace(/^https?:\/\//, "").replace(/^#+\s*/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase().slice(0, 80);
3081
2680
  }
2681
+ var FETCH_TIMEOUT_MS2;
2682
+ var init_rag = __esm({
2683
+ "cli/rag.tsx"() {
2684
+ "use strict";
2685
+ init_utils();
2686
+ init_discover();
2687
+ init_ink();
2688
+ FETCH_TIMEOUT_MS2 = 6e4;
2689
+ }
2690
+ });
3082
2691
 
3083
- // cli/secret.tsx
2692
+ // cli/cli.ts
2693
+ init_utils();
3084
2694
  init_discover();
3085
- init_help();
3086
2695
  init_ink();
3087
- init_prompts();
3088
- import minimist6 from "minimist";
3089
- import { jsx as jsx7 } from "react/jsx-runtime";
3090
- var secretCommandDef = {
3091
- name: "secret",
3092
- description: "Manage secrets",
3093
- options: [
3094
- { flags: "put <name>", description: "Create or update a secret" },
3095
- { flags: "delete <name>", description: "Delete a secret" },
3096
- { flags: "list", description: "List secret names" }
3097
- ]
3098
- };
3099
- async function requireProjectConfig(cwd) {
3100
- const config = await readProjectConfig(cwd);
3101
- if (!config) {
3102
- throw new Error("No .aai/project.json found \u2014 deploy first with `aai deploy`");
3103
- }
3104
- return config;
3105
- }
3106
- async function runSecretCommand(args, version) {
3107
- const parsed = minimist6(args, {
3108
- boolean: ["help", "yes"],
3109
- alias: { h: "help", y: "yes" },
3110
- stopEarly: true
2696
+ import { readFileSync } from "node:fs";
2697
+ import path7 from "node:path";
2698
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
2699
+ import chalk2 from "chalk";
2700
+ import { Command } from "commander";
2701
+ var cliDir = path7.dirname(fileURLToPath2(import.meta.url));
2702
+ var pkgJsonPath = path7.join(cliDir, "..", "package.json");
2703
+ var pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
2704
+ var VERSION = pkgJson.version;
2705
+ var banner = [
2706
+ "",
2707
+ ` ${primary(chalk2.bold(" \u2584\u2580\u2588 \u2584\u2580\u2588 \u2588"))} ${chalk2.dim("Voice agent development kit")}`,
2708
+ ` ${primary(chalk2.bold(" \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588"))} ${primary(`v${VERSION}`)}`,
2709
+ ""
2710
+ ].join("\n");
2711
+ var gettingStarted = [
2712
+ "",
2713
+ ` ${chalk2.bold(interactive("Getting started"))}`,
2714
+ "",
2715
+ ` ${chalk2.dim("$")} ${primary("aai init")} ${interactive("my-agent")}`,
2716
+ ` ${chalk2.dim("$")} ${primary("cd")} ${interactive("my-agent")}`,
2717
+ ` ${chalk2.dim("$")} ${primary("aai dev")}`,
2718
+ ""
2719
+ ].join("\n");
2720
+ async function ensureAgent(cwd, yes) {
2721
+ if (!await fileExists(path7.join(cwd, "agent.ts"))) {
2722
+ const { runInitCommand: runInitCommand2 } = await Promise.resolve().then(() => (init_init2(), init_exports2));
2723
+ await runInitCommand2({ yes }, { quiet: true });
2724
+ }
2725
+ }
2726
+ function withCwd(cmd) {
2727
+ return cmd.hook("preAction", (thisCmd) => {
2728
+ thisCmd.setOptionValue("cwd", resolveCwd());
3111
2729
  });
3112
- if (parsed.help || parsed._.length === 0) {
3113
- console.log(subcommandHelp(secretCommandDef, version));
3114
- return;
3115
- }
3116
- const sub = String(parsed._[0]);
3117
- const cwd = process.env.INIT_CWD || process.cwd();
3118
- await getApiKey();
3119
- let secretValue;
3120
- if (sub === "put") {
3121
- const name = String(parsed._[1] ?? "");
3122
- if (!name) throw new Error("Usage: aai secret put <NAME>");
3123
- secretValue = await askPassword(`Enter value for ${name}`);
3124
- if (!secretValue) throw new Error("No value provided");
3125
- }
3126
- switch (sub) {
3127
- case "put":
3128
- await secretPut(cwd, String(parsed._[1] ?? ""), secretValue ?? "");
3129
- break;
3130
- case "delete":
3131
- await secretDelete(cwd, String(parsed._[1] ?? ""));
3132
- break;
3133
- case "list":
3134
- await secretList(cwd);
3135
- break;
3136
- default:
3137
- throw new Error(`Unknown secret subcommand: ${sub}`);
3138
- }
3139
- }
3140
- async function getServerInfo(cwd) {
3141
- const config = await requireProjectConfig(cwd);
3142
- const apiKey = await getApiKey();
3143
- const serverUrl = config.serverUrl || DEFAULT_SERVER;
3144
- const slug = config.slug;
3145
- return { serverUrl, slug, apiKey };
3146
2730
  }
3147
- async function secretPut(cwd, name, value) {
3148
- await runWithInk(async (log) => {
3149
- const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
3150
- const resp = await fetch(`${serverUrl}/${slug}/secret`, {
3151
- method: "PUT",
3152
- headers: {
3153
- "Content-Type": "application/json",
3154
- Authorization: `Bearer ${apiKey}`
3155
- },
3156
- body: JSON.stringify({ [name]: value })
3157
- });
3158
- if (!resp.ok) {
3159
- const text = await resp.text();
3160
- throw new Error(`Failed to set secret: ${text}`);
3161
- }
3162
- log(/* @__PURE__ */ jsx7(Step, { action: "Set", msg: `${name} for ${slug}` }));
2731
+ function withAgentGuard(cmd) {
2732
+ return cmd.hook("preAction", async (thisCmd) => {
2733
+ await ensureAgent(thisCmd.getOptionValue("cwd"), thisCmd.opts().yes);
3163
2734
  });
3164
2735
  }
3165
- async function secretDelete(cwd, name) {
3166
- if (!name) throw new Error("Usage: aai secret delete <NAME>");
3167
- await runWithInk(async (log) => {
3168
- const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
3169
- const resp = await fetch(`${serverUrl}/${slug}/secret/${name}`, {
3170
- method: "DELETE",
3171
- headers: { Authorization: `Bearer ${apiKey}` }
3172
- });
3173
- if (!resp.ok) {
3174
- const text = await resp.text();
3175
- throw new Error(`Failed to delete secret: ${text}`);
3176
- }
3177
- log(/* @__PURE__ */ jsx7(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
2736
+ function withApiKey(cmd) {
2737
+ return cmd.hook("preAction", async () => {
2738
+ await getApiKey();
3178
2739
  });
3179
2740
  }
3180
- async function secretList(cwd) {
3181
- await runWithInk(async (log) => {
3182
- const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
3183
- const resp = await fetch(`${serverUrl}/${slug}/secret`, {
3184
- headers: { Authorization: `Bearer ${apiKey}` }
3185
- });
3186
- if (!resp.ok) {
3187
- const text = await resp.text();
3188
- throw new Error(`Failed to list secrets: ${text}`);
3189
- }
3190
- const { vars } = await resp.json();
3191
- if (vars.length === 0) {
3192
- log(/* @__PURE__ */ jsx7(StepInfo, { action: "Secrets", msg: "none set" }));
3193
- } else {
3194
- for (const name of vars) {
3195
- log(/* @__PURE__ */ jsx7(Detail, { msg: name }));
3196
- }
2741
+ function createProgram() {
2742
+ const program = new Command();
2743
+ program.name("aai").version(VERSION, "-V, --version").addHelpText("before", banner).addHelpText("after", gettingStarted);
2744
+ program.command("init").description("Scaffold a new agent project").argument("[dir]", "Project directory").option("-t, --template <template>", "Template to use").option("-f, --force", "Overwrite existing agent.ts").option("-y, --yes", "Accept defaults (no prompts)").action(
2745
+ async (dir, opts) => {
2746
+ const { runInitCommand: runInitCommand2 } = await Promise.resolve().then(() => (init_init2(), init_exports2));
2747
+ await runInitCommand2({ dir, ...opts });
3197
2748
  }
2749
+ );
2750
+ withApiKey(
2751
+ withAgentGuard(
2752
+ withCwd(
2753
+ program.command("dev").description("Start a local development server").option("-p, --port <number>", "Port to listen on", "3000").option("-y, --yes", "Accept defaults (no prompts)").action(async (opts) => {
2754
+ const { runDevCommand: runDevCommand2 } = await Promise.resolve().then(() => (init_dev(), dev_exports));
2755
+ await runDevCommand2(opts);
2756
+ })
2757
+ )
2758
+ )
2759
+ );
2760
+ withAgentGuard(
2761
+ withCwd(
2762
+ program.command("build").description("Bundle and validate (no server or deploy)").option("-y, --yes", "Accept defaults (no prompts)").action(async (opts) => {
2763
+ const { runBuildCommand: runBuildCommand2 } = await Promise.resolve().then(() => (init_build(), build_exports));
2764
+ await runBuildCommand2(opts.cwd);
2765
+ })
2766
+ )
2767
+ );
2768
+ withAgentGuard(
2769
+ withCwd(
2770
+ program.command("deploy").description("Bundle and deploy to production").option("-s, --server <url>", "Server URL").option("--dry-run", "Validate and bundle without deploying").option("-y, --yes", "Accept defaults (no prompts)").action(async (opts) => {
2771
+ const { runDeployCommand: runDeployCommand2 } = await Promise.resolve().then(() => (init_deploy2(), deploy_exports));
2772
+ await runDeployCommand2(opts);
2773
+ })
2774
+ )
2775
+ );
2776
+ withApiKey(
2777
+ withCwd(
2778
+ program.command("start").description("Start production server from build").option("-p, --port <number>", "Port to listen on", "3000").option("-y, --yes", "Accept defaults (no prompts)").action(async (opts) => {
2779
+ const { runStartCommand: runStartCommand2 } = await Promise.resolve().then(() => (init_start(), start_exports));
2780
+ await runStartCommand2(opts);
2781
+ })
2782
+ )
2783
+ );
2784
+ const secret = program.command("secret").description("Manage secrets").action(() => secret.help());
2785
+ withApiKey(withCwd(secret));
2786
+ secret.command("put").description("Create or update a secret").argument("<name>", "Secret name").action(async (name, _opts, cmd) => {
2787
+ const cwd = cmd.parent?.getOptionValue("cwd");
2788
+ const { runSecretPut: runSecretPut2 } = await Promise.resolve().then(() => (init_secret(), secret_exports));
2789
+ await runSecretPut2(cwd, name);
3198
2790
  });
3199
- }
3200
-
3201
- // cli/start.tsx
3202
- init_discover();
3203
- init_help();
3204
- init_ink();
3205
- import path10 from "node:path";
3206
- import minimist7 from "minimist";
3207
-
3208
- // cli/_start.ts
3209
- init_ink();
3210
- import path9 from "node:path";
3211
- import React4 from "react";
3212
- async function _startProductionServer(cwd, port, log) {
3213
- const clientDir = path9.join(cwd, ".aai", "client");
3214
- log(React4.createElement(Step, { action: "Start", msg: "loading agent" }));
3215
- const agentDef = await loadAgentDef(cwd);
3216
- const env = await resolveServerEnv();
3217
- await bootServer(agentDef, clientDir, env, port);
3218
- log(React4.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
3219
- }
3220
-
3221
- // cli/start.tsx
3222
- import { jsx as jsx8 } from "react/jsx-runtime";
3223
- var startCommandDef = {
3224
- name: "start",
3225
- description: "Start the production server from a build",
3226
- options: [
3227
- {
3228
- flags: "-p, --port <number>",
3229
- description: "Port to listen on (default: 3000)"
3230
- },
3231
- { flags: "-y, --yes", description: "Accept defaults (no prompts)" }
3232
- ]
3233
- };
3234
- async function runStartCommand(args, version) {
3235
- const parsed = minimist7(args, {
3236
- string: ["port"],
3237
- boolean: ["help", "yes"],
3238
- alias: { p: "port", h: "help", y: "yes" }
2791
+ secret.command("delete").description("Delete a secret").argument("<name>", "Secret name").action(async (name, _opts, cmd) => {
2792
+ const cwd = cmd.parent?.getOptionValue("cwd");
2793
+ const { runSecretDelete: runSecretDelete2 } = await Promise.resolve().then(() => (init_secret(), secret_exports));
2794
+ await runSecretDelete2(cwd, name);
3239
2795
  });
3240
- if (parsed.help) {
3241
- console.log(subcommandHelp(startCommandDef, version));
3242
- return;
3243
- }
3244
- const cwd = process.env.INIT_CWD || process.cwd();
3245
- const port = Number.parseInt(parsed.port ?? "3000", 10);
3246
- const buildDir = path10.join(cwd, ".aai", "build");
3247
- if (!await fileExists(path10.join(buildDir, "worker.js"))) {
3248
- throw new Error("No build found \u2014 run `aai build` first");
3249
- }
3250
- await getApiKey();
3251
- await runWithInk(async (log) => {
3252
- log(/* @__PURE__ */ jsx8(Step, { action: "Start", msg: `production server on port ${port}` }));
3253
- await _startProductionServer(cwd, port, log);
2796
+ secret.command("list").description("List secret names").action(async (_opts, cmd) => {
2797
+ const cwd = cmd.parent?.getOptionValue("cwd");
2798
+ const { runSecretList: runSecretList2 } = await Promise.resolve().then(() => (init_secret(), secret_exports));
2799
+ await runSecretList2(cwd);
3254
2800
  });
2801
+ withApiKey(
2802
+ withCwd(
2803
+ program.command("rag").description("Ingest a site's llms-full.txt into the vector store").argument("<url>", "URL to ingest").option("-s, --server <url>", "Server URL").option("--chunk-size <n>", "Max chunk size in tokens", "512").option("-y, --yes", "Accept defaults (no prompts)").action(async (url, opts) => {
2804
+ const { runRagCommand: runRagCommand2 } = await Promise.resolve().then(() => (init_rag(), rag_exports));
2805
+ await runRagCommand2({ url, ...opts });
2806
+ })
2807
+ )
2808
+ );
2809
+ return program;
3255
2810
  }
3256
-
3257
- // cli/cli.ts
3258
- var cliDir = path11.dirname(fileURLToPath2(import.meta.url));
3259
- var pkgJsonPath = path11.join(cliDir, "..", "package.json");
3260
- var pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
3261
- var VERSION = pkgJson.version;
3262
2811
  async function main(args) {
3263
- const parsed = minimist8(args, {
3264
- boolean: ["help", "version"],
3265
- alias: { h: "help", V: "version" },
3266
- stopEarly: true
3267
- });
3268
- if (parsed.version) {
3269
- console.log(VERSION);
2812
+ if (args.length === 0) {
2813
+ const { runInitCommand: runInitCommand2 } = await Promise.resolve().then(() => (init_init2(), init_exports2));
2814
+ await runInitCommand2({});
3270
2815
  return;
3271
2816
  }
3272
- if (parsed.help && parsed._.length === 0) {
3273
- console.log(rootHelp(VERSION));
3274
- return;
3275
- }
3276
- const [subcommand, ...rest] = parsed._;
3277
- const subArgs = rest.map(String);
3278
- switch (subcommand) {
3279
- case "init":
3280
- await runInitCommand(subArgs, VERSION);
3281
- return;
3282
- case "build":
3283
- await runBuildCommand(subArgs, VERSION);
3284
- return;
3285
- case "deploy":
3286
- await runDeployCommand(subArgs, VERSION);
3287
- return;
3288
- case "dev":
3289
- await runDevCommand(subArgs, VERSION);
3290
- return;
3291
- case "start":
3292
- await runStartCommand(subArgs, VERSION);
3293
- return;
3294
- case "secret":
3295
- await runSecretCommand(subArgs, VERSION);
3296
- return;
3297
- case "rag":
3298
- await runRagCommand(subArgs, VERSION);
3299
- return;
3300
- case "help":
3301
- console.log(rootHelp(VERSION));
3302
- return;
3303
- case void 0:
3304
- await runInitCommand(subArgs, VERSION);
3305
- return;
3306
- default:
3307
- console.error(`Unknown command: ${subcommand}`);
3308
- console.log(rootHelp(VERSION));
3309
- process.exit(1);
3310
- }
2817
+ const program = createProgram();
2818
+ await program.parseAsync(args, { from: "user" });
3311
2819
  }
3312
2820
  if (process.env.VITEST !== "true") {
3313
2821
  process.on("SIGINT", () => process.exit(0));
3314
2822
  main(process.argv.slice(2)).catch((err) => {
3315
- console.error(err instanceof Error ? err.message : String(err));
2823
+ console.error(errorMessage(err));
3316
2824
  process.exit(1);
3317
2825
  });
3318
2826
  }
3319
2827
  export {
2828
+ createProgram,
3320
2829
  main
3321
2830
  };