@alexkroman1/aai 0.8.3 → 0.8.4

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 +1223 -1701
  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,61 @@ 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
+ ] }));
203
+ }
204
+ function askEnter(message) {
205
+ return inkPrompt((done) => /* @__PURE__ */ jsxs2(Box2, { children: [
206
+ /* @__PURE__ */ jsx2(Text2, { color: COLORS.interactive, children: message }),
207
+ /* @__PURE__ */ jsx2(TextInput, { placeholder: "", onSubmit: () => done(void 0) })
208
+ ] }));
202
209
  }
203
210
  var init_prompts = __esm({
204
211
  "cli/_prompts.tsx"() {
205
212
  "use strict";
206
- init_colors();
213
+ init_ink();
207
214
  }
208
215
  });
209
216
 
210
217
  // 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
218
  import fs from "node:fs/promises";
224
219
  import path from "node:path";
225
220
  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;
221
+ function resolveCwd() {
222
+ return process.env.INIT_CWD || process.cwd();
223
+ }
224
+ function isDevMode(script) {
225
+ script ??= process.argv[1] ?? "";
226
+ return script.endsWith(".ts") || script.endsWith(".tsx");
240
227
  }
241
228
  function generateSlug() {
242
229
  return humanId({ separator: "-", capitalize: false });
@@ -284,6 +271,18 @@ async function writeProjectConfig(agentDir, data) {
284
271
  await fs.writeFile(path.join(aaiDir, "project.json"), `${JSON.stringify(data, null, 2)}
285
272
  `);
286
273
  }
274
+ async function getServerInfo(cwd, explicitServer, explicitApiKey) {
275
+ const config = await readProjectConfig(cwd);
276
+ if (!config) {
277
+ throw new Error("No .aai/project.json found \u2014 deploy first with `aai deploy`");
278
+ }
279
+ const apiKey = explicitApiKey ?? await getApiKey();
280
+ const serverUrl = resolveServerUrl(explicitServer, config.serverUrl);
281
+ return { serverUrl, slug: config.slug, apiKey };
282
+ }
283
+ function resolveServerUrl(explicit, configUrl) {
284
+ return explicit || configUrl || (isDevMode() ? "http://localhost:3100" : DEFAULT_SERVER);
285
+ }
287
286
  async function fileExists(p) {
288
287
  try {
289
288
  await fs.access(p);
@@ -316,90 +315,173 @@ var init_discover = __esm({
316
315
  }
317
316
  });
318
317
 
319
- // cli/_bundler.ts
318
+ // cli/_init.ts
319
+ var init_exports = {};
320
+ __export(init_exports, {
321
+ listTemplates: () => listTemplates,
322
+ runInit: () => runInit
323
+ });
320
324
  import fs2 from "node:fs/promises";
321
325
  import path2 from "node:path";
326
+ async function listTemplates(dir) {
327
+ const templates = [];
328
+ const entries = await fs2.readdir(dir, { withFileTypes: true });
329
+ for (const entry of entries) {
330
+ if (entry.isDirectory() && !entry.name.startsWith("_")) {
331
+ templates.push(entry.name);
332
+ }
333
+ }
334
+ return templates.sort();
335
+ }
336
+ async function copyDirNoOverwrite(src, dest) {
337
+ const entries = await fs2.readdir(src, { recursive: true, withFileTypes: true });
338
+ for (const entry of entries) {
339
+ if (!entry.isFile()) continue;
340
+ const rel = path2.relative(src, path2.join(entry.parentPath, entry.name));
341
+ const destPath = path2.join(dest, rel);
342
+ await fs2.mkdir(path2.dirname(destPath), { recursive: true });
343
+ try {
344
+ await fs2.copyFile(path2.join(src, rel), destPath, fs2.constants.COPYFILE_EXCL);
345
+ } catch (err) {
346
+ if (err.code !== "EEXIST") throw err;
347
+ }
348
+ }
349
+ }
350
+ async function runInit(opts) {
351
+ const { targetDir, template, templatesDir } = opts;
352
+ const available = await listTemplates(templatesDir);
353
+ if (!available.includes(template)) {
354
+ throw new Error(`unknown template '${template}' -- available: ${available.join(", ")}`);
355
+ }
356
+ await fs2.cp(path2.join(templatesDir, template), targetDir, { recursive: true, force: true });
357
+ await copyDirNoOverwrite(path2.join(templatesDir, "_shared"), targetDir);
358
+ try {
359
+ await fs2.copyFile(path2.join(targetDir, ".env.example"), path2.join(targetDir, ".env"));
360
+ } catch {
361
+ }
362
+ const readmePath = path2.join(targetDir, "README.md");
363
+ const slug = path2.basename(path2.resolve(targetDir));
364
+ const readme = `# ${slug}
365
+
366
+ A voice agent built with [aai](https://github.com/anthropics/aai).
367
+
368
+ ## Getting started
369
+
370
+ \`\`\`sh
371
+ npm install # Install dependencies
372
+ npm run dev # Run locally (opens browser)
373
+ npm run deploy # Deploy to production
374
+ \`\`\`
375
+
376
+ ## Environment variables
377
+
378
+ Secrets are managed on the server, not in local files:
379
+
380
+ \`\`\`sh
381
+ aai env add MY_KEY # Set a secret (prompts for value)
382
+ aai env ls # List secret names
383
+ aai env pull # Pull names into .env for reference
384
+ aai env rm MY_KEY # Remove a secret
385
+ \`\`\`
386
+
387
+ Access secrets in your agent via \`ctx.env.MY_KEY\`.
388
+
389
+ ## Learn more
390
+
391
+ See \`CLAUDE.md\` for the full agent API reference.
392
+ `;
393
+ try {
394
+ await fs2.writeFile(readmePath, readme, { flag: "wx" });
395
+ } catch (err) {
396
+ if (err.code !== "EEXIST") throw err;
397
+ }
398
+ return targetDir;
399
+ }
400
+ var init_init = __esm({
401
+ "cli/_init.ts"() {
402
+ "use strict";
403
+ }
404
+ });
405
+
406
+ // cli/_bundler.ts
407
+ import fs3 from "node:fs/promises";
408
+ import path3 from "node:path";
322
409
  import preact from "@preact/preset-vite";
323
410
  import tailwindcss from "@tailwindcss/vite";
324
411
  import { build } from "vite";
325
- async function readDirRecursive(dir, base = dir) {
326
- const files = {};
327
- let names;
412
+ function workerEntryPlugin() {
413
+ const virtualId = "virtual:worker-entry";
414
+ const resolvedId = `\0${virtualId}`;
415
+ return {
416
+ name: "aai-worker-entry",
417
+ resolveId(source) {
418
+ return source === virtualId ? resolvedId : null;
419
+ },
420
+ load(id) {
421
+ if (id !== resolvedId) return null;
422
+ return [
423
+ `import agent from "./agent.ts";`,
424
+ `import { initWorker } from "@alexkroman1/aai/worker-shim";`,
425
+ `initWorker(agent);`
426
+ ].join("\n");
427
+ }
428
+ };
429
+ }
430
+ async function readDirFiles(dir) {
431
+ let entries;
328
432
  try {
329
- names = await fs2.readdir(dir);
433
+ entries = await fs3.readdir(dir, { recursive: true, withFileTypes: true });
330
434
  } 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
- }
435
+ return {};
343
436
  }
437
+ const files = {};
438
+ await Promise.all(
439
+ entries.filter((e) => e.isFile()).map(async (e) => {
440
+ const full = path3.join(e.parentPath, e.name);
441
+ files[path3.relative(dir, full)] = await fs3.readFile(full, "utf-8");
442
+ })
443
+ );
344
444
  return files;
345
445
  }
346
446
  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
- );
447
+ const aaiDir = path3.join(agent.dir, ".aai");
448
+ const buildDir = path3.join(aaiDir, "build");
449
+ const clientDir = path3.join(aaiDir, "client");
450
+ const devMode = isDevMode();
451
+ const devResolve = devMode ? { conditions: ["source"] } : {};
360
452
  try {
361
453
  await build({
362
454
  configFile: false,
363
455
  root: agent.dir,
364
456
  logLevel: "warn",
457
+ plugins: [workerEntryPlugin()],
458
+ resolve: devResolve,
365
459
  build: {
460
+ rollupOptions: {
461
+ input: "virtual:worker-entry",
462
+ output: { format: "es", entryFileNames: "worker.js" }
463
+ },
366
464
  outDir: buildDir,
367
465
  emptyOutDir: true,
368
466
  minify: true,
369
- target: "es2022",
370
- rollupOptions: {
371
- input: workerEntry,
372
- output: {
373
- format: "es",
374
- entryFileNames: "worker.js",
375
- inlineDynamicImports: true
376
- }
377
- }
467
+ target: "es2022"
378
468
  }
379
469
  });
380
470
  } catch (err) {
381
- throw new BundleError(err instanceof Error ? err.message : String(err));
471
+ throw new BundleError(errorMessage(err));
382
472
  }
383
473
  const skipClient = opts?.skipClient || !agent.clientEntry;
384
474
  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
475
  try {
397
476
  await build({
398
477
  root: agent.dir,
399
478
  base: "./",
400
479
  logLevel: "warn",
401
480
  plugins: [preact(), tailwindcss()],
402
- ...Object.keys(devAlias).length > 0 && { resolve: { alias: devAlias } },
481
+ resolve: {
482
+ ...devResolve,
483
+ ...devMode && { dedupe: ["preact", "@preact/signals"] }
484
+ },
403
485
  build: {
404
486
  outDir: clientDir,
405
487
  emptyOutDir: true,
@@ -408,11 +490,11 @@ async function bundleAgent(agent, opts) {
408
490
  }
409
491
  });
410
492
  } catch (err) {
411
- throw new BundleError(err instanceof Error ? err.message : String(err));
493
+ throw new BundleError(errorMessage(err));
412
494
  }
413
495
  }
414
- const worker = await fs2.readFile(path2.join(buildDir, "worker.js"), "utf-8");
415
- const clientFiles = await readDirRecursive(clientDir);
496
+ const worker = await fs3.readFile(path3.join(buildDir, "worker.js"), "utf-8");
497
+ const clientFiles = await readDirFiles(clientDir);
416
498
  return {
417
499
  worker,
418
500
  clientFiles,
@@ -424,6 +506,7 @@ var BundleError;
424
506
  var init_bundler = __esm({
425
507
  "cli/_bundler.ts"() {
426
508
  "use strict";
509
+ init_utils();
427
510
  init_discover();
428
511
  BundleError = class extends Error {
429
512
  constructor(message) {
@@ -434,280 +517,68 @@ var init_bundler = __esm({
434
517
  }
435
518
  });
436
519
 
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
- }
520
+ // cli/_build.tsx
521
+ var build_exports = {};
522
+ __export(build_exports, {
523
+ buildAgentBundle: () => buildAgentBundle,
524
+ runBuildCommand: () => runBuildCommand
571
525
  });
572
-
573
- // cli/_build.ts
574
- import React2 from "react";
526
+ import { jsx as jsx3 } from "react/jsx-runtime";
575
527
  async function buildAgentBundle(cwd, log, opts) {
576
528
  const agent = await loadAgent(cwd);
577
529
  if (!agent) {
578
530
  throw new Error("No agent found \u2014 run `aai init` first");
579
531
  }
580
- log(React2.createElement(Step, { action: "Bundle", msg: agent.slug }));
532
+ log(/* @__PURE__ */ jsx3(Step, { action: "Bundle", msg: agent.slug }));
581
533
  let bundle;
582
534
  try {
583
535
  bundle = await bundleAgent(agent);
584
536
  } catch (err) {
585
- if (err instanceof BundleError) {
586
- 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);
537
+ if (err instanceof BundleError) {
538
+ throw new Error(`Bundle failed: ${err.message}`);
539
+ }
540
+ throw err;
698
541
  }
699
- return targetDir;
542
+ const kb = (bundle.workerBytes / 1024).toFixed(1);
543
+ const clientCount = Object.keys(bundle.clientFiles).length;
544
+ log(/* @__PURE__ */ jsx3(Info, { msg: `worker: ${kb} KB, client: ${clientCount} file(s)` }));
545
+ if (agent.clientEntry && !opts?.skipRenderCheck) {
546
+ const renderCheckPath = "../sdk/_render_check.ts";
547
+ const mod = await import(
548
+ /* @vite-ignore */
549
+ renderCheckPath
550
+ ).catch(() => null);
551
+ if (mod) {
552
+ log(/* @__PURE__ */ jsx3(Step, { action: "Render", msg: "check" }));
553
+ try {
554
+ await mod.renderCheck(agent.clientEntry, cwd);
555
+ } catch (err) {
556
+ throw new Error(`Render check failed: ${errorMessage(err)}`);
557
+ }
558
+ }
559
+ }
560
+ return bundle;
700
561
  }
701
- var init_init = __esm({
702
- "cli/_init.ts"() {
562
+ async function runBuildCommand(cwd) {
563
+ await runWithInk(async ({ log }) => {
564
+ await buildAgentBundle(cwd, log);
565
+ log(/* @__PURE__ */ jsx3(Step, { action: "Build", msg: "ok" }));
566
+ });
567
+ }
568
+ var init_build = __esm({
569
+ "cli/_build.tsx"() {
703
570
  "use strict";
571
+ init_utils();
572
+ init_bundler();
573
+ init_discover();
574
+ init_ink();
704
575
  }
705
576
  });
706
577
 
707
578
  // cli/_deploy.ts
708
- async function attemptDeploy(url, slug, apiKey, env, worker, clientFiles) {
579
+ async function attemptDeploy(fetchFn, url, slug, apiKey, env, worker, clientFiles) {
709
580
  try {
710
- return await _internals.fetch(`${url}/${slug}/deploy`, {
581
+ return await fetchFn(`${url}/${slug}/deploy`, {
711
582
  method: "POST",
712
583
  headers: {
713
584
  "Content-Type": "application/json",
@@ -724,37 +595,36 @@ async function attemptDeploy(url, slug, apiKey, env, worker, clientFiles) {
724
595
  }
725
596
  }
726
597
  async function runDeploy(opts) {
727
- const worker = opts.bundle.worker;
728
- const clientFiles = opts.bundle.clientFiles;
598
+ const { worker, clientFiles } = opts.bundle;
599
+ const fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
729
600
  let slug = opts.slug;
730
- if (opts.dryRun) {
731
- return { slug };
732
- }
733
601
  for (let i = 0; i < MAX_RETRIES; i++) {
734
- const resp = await attemptDeploy(opts.url, slug, opts.apiKey, opts.env, worker, clientFiles);
602
+ const resp = await attemptDeploy(
603
+ fetchFn,
604
+ opts.url,
605
+ slug,
606
+ opts.apiKey,
607
+ opts.env,
608
+ worker,
609
+ clientFiles
610
+ );
735
611
  if (resp.ok) {
736
612
  return { slug };
737
613
  }
738
- if (resp.status === 403) {
739
- const text2 = await resp.text();
740
- if (text2.includes("Slug")) {
741
- slug = generateSlug();
742
- continue;
743
- }
744
- }
745
614
  const text = await resp.text();
615
+ if (resp.status === 403 && text.includes("Slug")) {
616
+ slug = generateSlug();
617
+ continue;
618
+ }
746
619
  throw new Error(`deploy failed (${resp.status}): ${text}`);
747
620
  }
748
621
  throw new Error(`deploy failed: could not find available slug after ${MAX_RETRIES} attempts`);
749
622
  }
750
- var _internals, MAX_RETRIES;
623
+ var MAX_RETRIES;
751
624
  var init_deploy = __esm({
752
625
  "cli/_deploy.ts"() {
753
626
  "use strict";
754
627
  init_discover();
755
- _internals = {
756
- fetch: globalThis.fetch.bind(globalThis)
757
- };
758
628
  MAX_RETRIES = 20;
759
629
  }
760
630
  });
@@ -764,103 +634,73 @@ var deploy_exports = {};
764
634
  __export(deploy_exports, {
765
635
  runDeployCommand: () => runDeployCommand
766
636
  });
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
- }
637
+ import { jsx as jsx4 } from "react/jsx-runtime";
773
638
  async function deployBundle(opts) {
774
639
  const { bundle, serverUrl, apiKey, cwd, log } = opts;
775
640
  let { slug } = opts;
776
- log(/* @__PURE__ */ jsx3(Step, { action: "Deploy", msg: slug }));
641
+ log(/* @__PURE__ */ jsx4(Step, { action: "Deploy", msg: slug }));
777
642
  const deployed = await runDeploy({
778
643
  url: serverUrl,
779
644
  bundle,
780
645
  env: { ASSEMBLYAI_API_KEY: apiKey },
781
646
  slug,
782
- dryRun: false,
783
647
  apiKey
784
648
  });
785
649
  slug = deployed.slug;
786
650
  await writeProjectConfig(cwd, { slug, serverUrl });
787
651
  const agentUrl = `${serverUrl}/${slug}`;
788
- log(/* @__PURE__ */ jsx3(Step, { action: "Ready", msg: agentUrl }));
652
+ log(/* @__PURE__ */ jsx4(Step, { action: "Ready", msg: agentUrl }));
789
653
  return agentUrl;
790
654
  }
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;
655
+ async function runDeployCommand(opts) {
656
+ const { cwd } = opts;
657
+ const dryRun = opts.dryRun ?? false;
807
658
  const apiKey = dryRun ? "" : await getApiKey();
808
659
  const projectConfig = await readProjectConfig(cwd);
660
+ const serverUrl = resolveServerUrl(opts.server, projectConfig?.serverUrl);
809
661
  const slug = projectConfig?.slug ?? generateSlug();
810
662
  let agentUrl = "";
811
- await runWithInk(async (log) => {
663
+ await runWithInk(async ({ log }) => {
812
664
  const bundle = await buildAgentBundle(cwd, log);
813
665
  if (dryRun) {
814
- log(/* @__PURE__ */ jsx3(StepInfo, { action: "Dry run", msg: `would deploy as ${slug}` }));
666
+ log(/* @__PURE__ */ jsx4(StepInfo, { action: "Dry run", msg: `would deploy as ${slug}` }));
815
667
  return;
816
668
  }
817
669
  agentUrl = await deployBundle({ bundle, serverUrl, apiKey, slug, cwd, log });
818
670
  });
819
- if (agentUrl && !dryRun) {
671
+ if (agentUrl) {
820
672
  await askEnter("Press enter to open in browser");
821
673
  const { exec } = await import("node:child_process");
822
674
  exec(`open "${agentUrl}"`);
823
675
  }
824
676
  }
825
- var deployCommandDef;
826
677
  var init_deploy2 = __esm({
827
678
  "cli/deploy.tsx"() {
828
679
  "use strict";
829
680
  init_build();
830
681
  init_deploy();
831
682
  init_discover();
832
- init_help();
833
683
  init_ink();
834
684
  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
685
  }
849
686
  });
850
687
 
851
688
  // cli/init.tsx
689
+ var init_exports2 = {};
690
+ __export(init_exports2, {
691
+ runInitCommand: () => runInitCommand
692
+ });
852
693
  import { execFile } from "node:child_process";
853
694
  import fs4 from "node:fs/promises";
854
- import path5 from "node:path";
695
+ import path4 from "node:path";
855
696
  import { fileURLToPath } from "node:url";
856
697
  import { promisify } from "node:util";
857
- import minimist2 from "minimist";
858
- import { jsx as jsx4 } from "react/jsx-runtime";
698
+ import { jsx as jsx5 } from "react/jsx-runtime";
859
699
  async function rewriteDevDeps(cwd, cliDir2) {
860
- const monorepoRoot = path5.join(cliDir2, "..");
861
- const pkgJsonPath2 = path5.join(cwd, "package.json");
700
+ const monorepoRoot = path4.join(cliDir2, "..");
701
+ const pkgJsonPath2 = path4.join(cwd, "package.json");
862
702
  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"));
703
+ const rootPkg = JSON.parse(await fs4.readFile(path4.join(monorepoRoot, "package.json"), "utf-8"));
864
704
  const rootPkgName = rootPkg.name;
865
705
  if (pkgJson2.dependencies[rootPkgName]) {
866
706
  pkgJson2.dependencies[rootPkgName] = `file:${monorepoRoot}`;
@@ -869,56 +709,45 @@ async function rewriteDevDeps(cwd, cliDir2) {
869
709
  `);
870
710
  }
871
711
  async function installDeps(cwd, log) {
872
- if (await fileExists(path5.join(cwd, "node_modules"))) return;
712
+ if (await fileExists(path4.join(cwd, "node_modules"))) return;
873
713
  let pkgJson2;
874
714
  try {
875
- pkgJson2 = JSON.parse(await fs4.readFile(path5.join(cwd, "package.json"), "utf-8"));
715
+ pkgJson2 = JSON.parse(await fs4.readFile(path4.join(cwd, "package.json"), "utf-8"));
876
716
  } catch {
877
717
  pkgJson2 = {};
878
718
  }
879
719
  const deps = Object.keys(pkgJson2.dependencies ?? {});
880
720
  const devDeps = Object.keys(pkgJson2.devDependencies ?? {});
881
721
  if (deps.length > 0) {
882
- log(/* @__PURE__ */ jsx4(Step, { action: "Install", msg: deps.join(", ") }));
722
+ log(/* @__PURE__ */ jsx5(Step, { action: "Install", msg: deps.join(", ") }));
883
723
  }
884
724
  if (devDeps.length > 0) {
885
- log(/* @__PURE__ */ jsx4(Step, { action: "Install", msg: `dev: ${devDeps.join(", ")}` }));
725
+ log(/* @__PURE__ */ jsx5(Step, { action: "Install", msg: `dev: ${devDeps.join(", ")}` }));
886
726
  }
887
727
  try {
888
728
  await execFileAsync("npm", ["install"], { cwd });
889
729
  } catch {
890
- log(/* @__PURE__ */ jsx4(Warn, { msg: "npm install failed" }));
730
+ log(/* @__PURE__ */ jsx5(Warn, { msg: "npm install failed" }));
891
731
  }
892
732
  }
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];
733
+ async function runInitCommand(opts, extra) {
734
+ await getApiKey();
735
+ let dir = opts.dir;
906
736
  if (!dir) {
907
737
  dir = await askText("What is your project named?", "my-voice-agent");
908
738
  }
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(
739
+ const cwd = path4.resolve(resolveCwd(), dir);
740
+ if (!opts.force && await fileExists(path4.join(cwd, "agent.ts"))) {
741
+ throw new Error(
912
742
  `agent.ts already exists in this directory. Use ${interactive("--force")} to overwrite.`
913
743
  );
914
- process.exit(1);
915
744
  }
916
- const cliDir2 = path5.dirname(fileURLToPath(import.meta.url));
917
- const templatesDir = path5.join(cliDir2, "..", "templates");
745
+ const cliDir2 = path4.dirname(fileURLToPath(import.meta.url));
746
+ const templatesDir = path4.join(cliDir2, "..", "templates");
918
747
  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 }));
748
+ const template = opts.template || "simple";
749
+ await runWithInk(async ({ log }) => {
750
+ log(/* @__PURE__ */ jsx5(Step, { action: "Create", msg: dir }));
922
751
  await runInit2({ targetDir: cwd, template, templatesDir });
923
752
  if (isDevMode()) {
924
753
  await rewriteDevDeps(cwd, cliDir2);
@@ -927,60 +756,32 @@ async function runInitCommand(args, version, opts) {
927
756
  });
928
757
  process.chdir(cwd);
929
758
  delete process.env.INIT_CWD;
930
- if (!opts?.quiet) {
759
+ if (!extra?.quiet) {
931
760
  const { runDeployCommand: runDeployCommand2 } = await Promise.resolve().then(() => (init_deploy2(), deploy_exports));
932
- await runDeployCommand2(["-y"], version);
761
+ await runDeployCommand2({ cwd });
933
762
  }
934
763
  return cwd;
935
764
  }
936
- var execFileAsync, initCommandDef;
765
+ var execFileAsync;
937
766
  var init_init2 = __esm({
938
767
  "cli/init.tsx"() {
939
768
  "use strict";
940
- init_colors();
941
769
  init_discover();
942
- init_help();
943
770
  init_ink();
944
771
  init_prompts();
945
772
  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
773
  }
959
774
  });
960
775
 
961
776
  // sdk/protocol.ts
962
777
  import { z } from "zod";
963
- var DEFAULT_TTS_SAMPLE_RATE, AUDIO_FORMAT, _bitsPerSample, _channels, AudioFrameSpec, KvRequestBaseSchema, HOOK_TIMEOUT_MS, SessionErrorCodeSchema, TranscriptEventSchema, ClientEventSchema, ClientMessageSchema;
778
+ var DEFAULT_TTS_SAMPLE_RATE, AUDIO_FORMAT, KvRequestSchema, VectorRequestSchema, HOOK_TIMEOUT_MS, TOOL_EXECUTION_TIMEOUT_MS, SessionErrorCodeSchema, ev, textEv, turnOrder, ClientEventSchema, ClientMessageSchema;
964
779
  var init_protocol = __esm({
965
780
  "sdk/protocol.ts"() {
966
781
  "use strict";
967
782
  DEFAULT_TTS_SAMPLE_RATE = 24e3;
968
783
  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", [
784
+ KvRequestSchema = z.discriminatedUnion("op", [
984
785
  z.object({ op: z.literal("get"), key: z.string().min(1) }),
985
786
  z.object({
986
787
  op: z.literal("set"),
@@ -994,9 +795,29 @@ var init_protocol = __esm({
994
795
  prefix: z.string(),
995
796
  limit: z.number().int().positive().optional(),
996
797
  reverse: z.boolean().optional()
798
+ }),
799
+ z.object({ op: z.literal("keys"), pattern: z.string().optional() })
800
+ ]);
801
+ VectorRequestSchema = z.discriminatedUnion("op", [
802
+ z.object({
803
+ op: z.literal("upsert"),
804
+ id: z.string().min(1),
805
+ data: z.string().min(1),
806
+ metadata: z.record(z.string(), z.unknown()).optional()
807
+ }),
808
+ z.object({
809
+ op: z.literal("query"),
810
+ text: z.string().min(1),
811
+ topK: z.number().int().positive().max(100).optional(),
812
+ filter: z.string().optional()
813
+ }),
814
+ z.object({
815
+ op: z.literal("remove"),
816
+ ids: z.array(z.string().min(1)).min(1)
997
817
  })
998
818
  ]);
999
819
  HOOK_TIMEOUT_MS = 5e3;
820
+ TOOL_EXECUTION_TIMEOUT_MS = 3e4;
1000
821
  SessionErrorCodeSchema = z.enum([
1001
822
  "stt",
1002
823
  "llm",
@@ -1007,23 +828,16 @@ var init_protocol = __esm({
1007
828
  "audio",
1008
829
  "internal"
1009
830
  ]);
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
- });
831
+ ev = (t) => z.object({ type: z.literal(t) });
832
+ textEv = (t) => z.object({ type: z.literal(t), text: z.string() });
833
+ turnOrder = z.number().int().nonnegative().optional();
1016
834
  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() }),
835
+ ev("speech_started"),
836
+ ev("speech_stopped"),
837
+ z.object({ type: z.literal("transcript"), text: z.string(), isFinal: z.boolean(), turnOrder }),
838
+ textEv("turn").extend({ turnOrder }),
839
+ textEv("chat"),
840
+ textEv("chat_delta"),
1027
841
  z.object({
1028
842
  type: z.literal("tool_call_start"),
1029
843
  toolCallId: z.string(),
@@ -1035,55 +849,35 @@ var init_protocol = __esm({
1035
849
  toolCallId: z.string(),
1036
850
  result: z.string().max(4e3)
1037
851
  }),
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
- })
852
+ ev("tts_done"),
853
+ ev("cancelled"),
854
+ ev("reset"),
855
+ z.object({ type: z.literal("error"), code: SessionErrorCodeSchema, message: z.string() })
1046
856
  ]);
1047
857
  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") }),
858
+ ev("audio_ready"),
859
+ ev("cancel"),
860
+ ev("reset"),
1051
861
  z.object({
1052
862
  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)
863
+ messages: z.array(z.object({ role: z.enum(["user", "assistant"]), text: z.string().max(1e5) })).max(200)
1059
864
  })
1060
865
  ]);
1061
866
  }
1062
867
  });
1063
868
 
1064
869
  // sdk/runtime.ts
1065
- var consoleLogger, noopMetrics, DEFAULT_S2S_CONFIG;
870
+ var _log, consoleLogger, noopMetrics, DEFAULT_S2S_CONFIG;
1066
871
  var init_runtime = __esm({
1067
872
  "sdk/runtime.ts"() {
1068
873
  "use strict";
1069
874
  init_protocol();
875
+ _log = (m) => (msg, ctx) => console[m](msg, ...ctx ? [ctx] : []);
1070
876
  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
- }
877
+ info: _log("log"),
878
+ warn: _log("warn"),
879
+ error: _log("error"),
880
+ debug: _log("debug")
1087
881
  };
1088
882
  noopMetrics = {
1089
883
  sessionsTotal: { inc() {
@@ -1100,20 +894,230 @@ var init_runtime = __esm({
1100
894
  }
1101
895
  });
1102
896
 
1103
- // sdk/_internal_types.ts
897
+ // sdk/s2s.ts
898
+ var s2s_exports = {};
899
+ __export(s2s_exports, {
900
+ connectS2s: () => connectS2s,
901
+ wrapOnStyleWebSocket: () => wrapOnStyleWebSocket
902
+ });
1104
903
  import { z as z2 } from "zod";
904
+ function uint8ToBase64(bytes) {
905
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
906
+ }
907
+ function base64ToUint8(base64) {
908
+ const buf = Buffer.from(base64, "base64");
909
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
910
+ }
911
+ function wrapOnStyleWebSocket(ws) {
912
+ const target = new EventTarget();
913
+ ws.on("open", () => target.dispatchEvent(new Event("open")));
914
+ ws.on("message", (data) => target.dispatchEvent(new MessageEvent("message", { data })));
915
+ ws.on("close", (code, reason) => {
916
+ const init = { reason: String(reason ?? "") };
917
+ if (typeof code === "number") init.code = code;
918
+ target.dispatchEvent(new CloseEvent("close", init));
919
+ });
920
+ ws.on(
921
+ "error",
922
+ (err) => target.dispatchEvent(
923
+ new ErrorEvent("error", {
924
+ message: errorMessage(err)
925
+ })
926
+ )
927
+ );
928
+ Object.defineProperties(target, {
929
+ readyState: { get: () => ws.readyState, enumerable: true },
930
+ send: { value: (data) => ws.send(data), enumerable: true },
931
+ close: { value: () => ws.close(), enumerable: true }
932
+ });
933
+ return target;
934
+ }
935
+ function dispatchS2sMessage(target, msg) {
936
+ const entry = S2S_DISPATCH[msg.type]?.(msg);
937
+ if (entry) target.dispatchEvent(new CustomEvent(entry[0], { detail: entry[1] }));
938
+ }
939
+ function connectS2s(opts) {
940
+ const { apiKey, config, createWebSocket, logger: log = consoleLogger } = opts;
941
+ return new Promise((resolve, reject) => {
942
+ log.info("S2S connecting", { url: config.wssUrl });
943
+ const ws = createWebSocket(config.wssUrl, {
944
+ headers: { Authorization: `Bearer ${apiKey}` }
945
+ });
946
+ const target = new EventTarget();
947
+ let opened = false;
948
+ function send(msg) {
949
+ if (ws.readyState !== WS_OPEN) return;
950
+ const json = JSON.stringify(msg);
951
+ if (msg.type !== "input.audio") {
952
+ log.info(
953
+ `S2S >> ${msg.type}`,
954
+ msg.type === "session.update" ? { payload: json } : void 0
955
+ );
956
+ }
957
+ ws.send(json);
958
+ }
959
+ const handle = Object.assign(target, {
960
+ sendAudio(audio) {
961
+ if (ws.readyState !== WS_OPEN) return;
962
+ ws.send(`{"type":"input.audio","audio":"${uint8ToBase64(audio)}"}`);
963
+ },
964
+ sendToolResult(callId, result) {
965
+ const msg = { type: "tool.result", call_id: callId, result };
966
+ log.info("S2S >> tool.result", { call_id: callId, resultLength: result.length });
967
+ send(msg);
968
+ },
969
+ updateSession(sessionConfig) {
970
+ send({ type: "session.update", session: sessionConfig });
971
+ },
972
+ resumeSession(sessionId) {
973
+ send({ type: "session.resume", session_id: sessionId });
974
+ },
975
+ close() {
976
+ log.info("S2S closing");
977
+ ws.close();
978
+ }
979
+ });
980
+ ws.addEventListener("open", () => {
981
+ opened = true;
982
+ log.info("S2S WebSocket open");
983
+ resolve(handle);
984
+ });
985
+ function handleS2sMessage(ev2) {
986
+ const data = ev2.data;
987
+ let raw;
988
+ try {
989
+ raw = JSON.parse(String(data));
990
+ } catch {
991
+ log.warn("S2S << invalid JSON", { data: String(data).slice(0, 200) });
992
+ return;
993
+ }
994
+ const obj = raw;
995
+ if (obj.type !== "reply.audio" && obj.type !== "input.audio") {
996
+ log.info(
997
+ `S2S << ${obj.type}`,
998
+ obj.type === "transcript.agent.delta" ? { delta: obj.delta } : void 0
999
+ );
1000
+ }
1001
+ if (obj.type === "reply.audio" && typeof obj.data === "string") {
1002
+ const audioBytes = base64ToUint8(obj.data);
1003
+ target.dispatchEvent(new CustomEvent("audio", { detail: { audio: audioBytes } }));
1004
+ return;
1005
+ }
1006
+ const parsed = S2sServerMessageSchema.safeParse(raw);
1007
+ if (!parsed.success) {
1008
+ log.warn(
1009
+ `S2S << unrecognised message type: ${obj.type ?? JSON.stringify(raw).slice(0, 200)}`
1010
+ );
1011
+ return;
1012
+ }
1013
+ dispatchS2sMessage(target, parsed.data);
1014
+ }
1015
+ ws.addEventListener("message", handleS2sMessage);
1016
+ ws.addEventListener("close", ((ev2) => {
1017
+ log.info("S2S WebSocket closed", {
1018
+ code: ev2.code ?? 0,
1019
+ reason: ev2.reason ?? ""
1020
+ });
1021
+ target.dispatchEvent(new CustomEvent("close"));
1022
+ }));
1023
+ ws.addEventListener("error", ((ev2) => {
1024
+ const message = ev2 instanceof ErrorEvent ? ev2.message : "WebSocket error";
1025
+ const errObj = new Error(message);
1026
+ log.error("S2S WebSocket error", { error: errObj.message });
1027
+ if (!opened) {
1028
+ reject(errObj);
1029
+ } else {
1030
+ target.dispatchEvent(
1031
+ new CustomEvent("error", {
1032
+ detail: { code: "ws_error", message: errObj.message }
1033
+ })
1034
+ );
1035
+ }
1036
+ }));
1037
+ });
1038
+ }
1039
+ var WS_OPEN, S2sServerMessageSchema, S2S_DISPATCH;
1040
+ var init_s2s = __esm({
1041
+ "sdk/s2s.ts"() {
1042
+ "use strict";
1043
+ init_utils();
1044
+ init_runtime();
1045
+ WS_OPEN = 1;
1046
+ S2sServerMessageSchema = z2.discriminatedUnion("type", [
1047
+ z2.object({ type: z2.literal("session.ready"), session_id: z2.string() }),
1048
+ z2.object({ type: z2.literal("session.updated") }).passthrough(),
1049
+ z2.object({ type: z2.literal("input.speech.started") }),
1050
+ z2.object({ type: z2.literal("input.speech.stopped") }),
1051
+ z2.object({ type: z2.literal("transcript.user.delta"), text: z2.string() }),
1052
+ z2.object({
1053
+ type: z2.literal("transcript.user"),
1054
+ item_id: z2.string(),
1055
+ text: z2.string()
1056
+ }),
1057
+ z2.object({ type: z2.literal("reply.started"), reply_id: z2.string() }),
1058
+ // reply.audio is handled on the fast path before Zod.
1059
+ z2.object({ type: z2.literal("transcript.agent.delta"), delta: z2.string() }).passthrough(),
1060
+ z2.object({ type: z2.literal("transcript.agent"), text: z2.string() }),
1061
+ z2.object({ type: z2.literal("reply.content_part.started") }).passthrough(),
1062
+ z2.object({ type: z2.literal("reply.content_part.done") }).passthrough(),
1063
+ z2.object({
1064
+ type: z2.literal("tool.call"),
1065
+ call_id: z2.string(),
1066
+ name: z2.string(),
1067
+ args: z2.record(z2.string(), z2.unknown()).optional().default({})
1068
+ }),
1069
+ z2.object({
1070
+ type: z2.literal("reply.done"),
1071
+ status: z2.string().optional()
1072
+ }),
1073
+ z2.object({
1074
+ type: z2.literal("session.error"),
1075
+ code: z2.string(),
1076
+ message: z2.string()
1077
+ }),
1078
+ // Connection-level error (bare "error" without "session." prefix).
1079
+ z2.object({
1080
+ type: z2.literal("error"),
1081
+ message: z2.string()
1082
+ })
1083
+ ]);
1084
+ S2S_DISPATCH = {
1085
+ "session.ready": (m) => ["ready", { session_id: m.session_id }],
1086
+ "session.updated": (m) => ["session_updated", m],
1087
+ "input.speech.started": () => ["speech_started", void 0],
1088
+ "input.speech.stopped": () => ["speech_stopped", void 0],
1089
+ "transcript.user.delta": (m) => ["user_transcript_delta", { text: m.text }],
1090
+ "transcript.user": (m) => ["user_transcript", { item_id: m.item_id, text: m.text }],
1091
+ "reply.started": (m) => ["reply_started", { reply_id: m.reply_id }],
1092
+ "transcript.agent.delta": (m) => ["agent_transcript_delta", { text: m.delta }],
1093
+ "transcript.agent": (m) => ["agent_transcript", { text: m.text }],
1094
+ "tool.call": (m) => ["tool_call", { call_id: m.call_id, name: m.name, args: m.args }],
1095
+ "reply.done": (m) => ["reply_done", { status: m.status }],
1096
+ "session.error": (m) => [
1097
+ m.code === "session_not_found" || m.code === "session_forbidden" ? "session_expired" : "error",
1098
+ { code: m.code, message: m.message }
1099
+ ],
1100
+ error: (m) => ["error", { code: "connection", message: m.message }],
1101
+ "reply.content_part.started": () => void 0,
1102
+ "reply.content_part.done": () => void 0
1103
+ };
1104
+ }
1105
+ });
1106
+
1107
+ // sdk/_internal_types.ts
1108
+ import { z as z3 } from "zod";
1105
1109
  function agentToolsToSchemas(tools) {
1106
1110
  return Object.entries(tools).map(([name, def]) => ({
1107
1111
  name,
1108
1112
  description: def.description,
1109
- parameters: z2.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1113
+ parameters: z3.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1110
1114
  }));
1111
1115
  }
1112
1116
  var EMPTY_PARAMS;
1113
1117
  var init_internal_types = __esm({
1114
1118
  "sdk/_internal_types.ts"() {
1115
1119
  "use strict";
1116
- EMPTY_PARAMS = z2.object({});
1120
+ EMPTY_PARAMS = z3.object({});
1117
1121
  }
1118
1122
  });
1119
1123
 
@@ -1139,68 +1143,16 @@ Voice-First Rules:
1139
1143
  }
1140
1144
  });
1141
1145
 
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
1146
  // sdk/builtin_tools.ts
1198
1147
  import { convert } from "html-to-text";
1199
1148
  import { z as z4 } from "zod";
1149
+ function fetchSignal(toolSignal) {
1150
+ return AbortSignal.any([toolSignal, AbortSignal.timeout(FETCH_TIMEOUT_MS)]);
1151
+ }
1200
1152
  function htmlToText(html) {
1201
1153
  return convert(html, { wordwrap: false });
1202
1154
  }
1203
- function createWebSearch() {
1155
+ function createWebSearch(fetchFn = globalThis.fetch) {
1204
1156
  return {
1205
1157
  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
1158
  parameters: webSearchParams,
@@ -1215,9 +1167,9 @@ function createWebSearch() {
1215
1167
  count: String(maxResults),
1216
1168
  text_decorations: "false"
1217
1169
  })}`;
1218
- const resp = await fetch(url, {
1170
+ const resp = await fetchFn(url, {
1219
1171
  headers: { "X-Subscription-Token": apiKey },
1220
- signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
1172
+ signal: fetchSignal(ctx.abortSignal)
1221
1173
  });
1222
1174
  if (!resp.ok) return [];
1223
1175
  const raw = await resp.json();
@@ -1231,19 +1183,19 @@ function createWebSearch() {
1231
1183
  }
1232
1184
  };
1233
1185
  }
1234
- function createVisitWebpage() {
1186
+ function createVisitWebpage(fetchFn = globalThis.fetch) {
1235
1187
  return {
1236
1188
  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
1189
  parameters: visitWebpageParams,
1238
1190
  async execute(args, ctx) {
1239
1191
  const { url } = args;
1240
- const resp = await fetch(url, {
1192
+ const resp = await fetchFn(url, {
1241
1193
  headers: {
1242
1194
  "User-Agent": "Mozilla/5.0 (compatible; VoiceAgent/1.0; +https://github.com/AssemblyAI/aai)",
1243
1195
  Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
1244
1196
  },
1245
1197
  redirect: "follow",
1246
- signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
1198
+ signal: fetchSignal(ctx.abortSignal)
1247
1199
  });
1248
1200
  if (!resp.ok) {
1249
1201
  return { error: `Failed to fetch: ${resp.status} ${resp.statusText}`, url };
@@ -1261,15 +1213,15 @@ function createVisitWebpage() {
1261
1213
  }
1262
1214
  };
1263
1215
  }
1264
- function createFetchJson() {
1216
+ function createFetchJson(fetchFn = globalThis.fetch) {
1265
1217
  return {
1266
1218
  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
1219
  parameters: fetchJsonParams,
1268
1220
  async execute(args, ctx) {
1269
1221
  const { url, headers } = args;
1270
- const resp = await fetch(url, {
1222
+ const resp = await fetchFn(url, {
1271
1223
  ...headers && { headers },
1272
- signal: ctx.abortSignal ?? AbortSignal.timeout(15e3)
1224
+ signal: fetchSignal(ctx.abortSignal)
1273
1225
  });
1274
1226
  if (!resp.ok) {
1275
1227
  return { error: `HTTP ${resp.status} ${resp.statusText}`, url };
@@ -1299,7 +1251,6 @@ function createRunCode() {
1299
1251
  error: capture,
1300
1252
  debug: capture
1301
1253
  };
1302
- const RUN_CODE_TIMEOUT = 5e3;
1303
1254
  const AsyncFunction = Object.getPrototypeOf(async () => {
1304
1255
  }).constructor;
1305
1256
  try {
@@ -1313,7 +1264,7 @@ function createRunCode() {
1313
1264
  const result = output.join("\n").trim();
1314
1265
  return result || "Code ran successfully (no output)";
1315
1266
  } catch (err) {
1316
- return { error: err instanceof Error ? err.message : String(err) };
1267
+ return { error: errorMessage(err) };
1317
1268
  }
1318
1269
  }
1319
1270
  };
@@ -1328,66 +1279,84 @@ function createVectorSearch(vectorSearchFn) {
1328
1279
  }
1329
1280
  };
1330
1281
  }
1282
+ function resolveBuiltin(name, opts) {
1283
+ const entry = TOOL_REGISTRY[name];
1284
+ if (!entry) return [];
1285
+ if ("multi" in entry) return Object.entries(entry.multi());
1286
+ if (name === "vector_search" && !opts?.vectorSearch) return [];
1287
+ return [[name, entry.create(opts)]];
1288
+ }
1331
1289
  function getBuiltinToolDefs(names, opts) {
1332
1290
  const defs = {};
1333
1291
  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;
1292
+ for (const [k, v] of resolveBuiltin(name, opts)) defs[k] = v;
1293
+ }
1294
+ return defs;
1295
+ }
1296
+ function getBuiltinToolSchemas(names) {
1297
+ return names.flatMap(
1298
+ (name) => resolveBuiltin(name, { vectorSearch: async () => "" }).map(([toolName, def]) => ({
1299
+ name: toolName,
1300
+ description: def.description,
1301
+ parameters: z4.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
1302
+ }))
1303
+ );
1304
+ }
1305
+ function memoryTools() {
1306
+ return {
1307
+ save_memory: tool({
1308
+ description: "Save a piece of information to persistent memory. Use a descriptive key like 'user:name' or 'project:status'.",
1309
+ parameters: z4.object({
1310
+ key: z4.string().describe("A descriptive key for this memory (e.g. 'user:name', 'preference:color')"),
1311
+ value: z4.string().describe("The information to remember")
1312
+ }),
1313
+ execute: async ({ key, value }, ctx) => {
1314
+ await ctx.kv.set(key, value);
1315
+ return { saved: key };
1316
+ }
1317
+ }),
1318
+ recall_memory: tool({
1319
+ description: "Retrieve a previously saved memory by its key.",
1320
+ parameters: z4.object({
1321
+ key: z4.string().describe("The key to look up")
1322
+ }),
1323
+ execute: async ({ key }, ctx) => {
1324
+ const value = await ctx.kv.get(key);
1325
+ if (value === null) return { found: false, key };
1326
+ return { found: true, key, value };
1327
+ }
1328
+ }),
1329
+ list_memories: tool({
1330
+ description: "List all saved memory keys, optionally filtered by a prefix (e.g. 'user:').",
1331
+ parameters: z4.object({
1332
+ prefix: z4.string().describe("Prefix to filter keys (e.g. 'user:'). Use empty string for all.").optional()
1333
+ }),
1334
+ execute: async ({ prefix }, ctx) => {
1335
+ const entries = await ctx.kv.list(prefix ?? "");
1336
+ return { count: entries.length, keys: entries.map((e) => e.key) };
1358
1337
  }
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)
1338
+ }),
1339
+ forget_memory: tool({
1340
+ description: "Delete a previously saved memory by its key.",
1341
+ parameters: z4.object({
1342
+ key: z4.string().describe("The key to delete")
1343
+ }),
1344
+ execute: async ({ key }, ctx) => {
1345
+ await ctx.kv.delete(key);
1346
+ return { deleted: key };
1381
1347
  }
1382
- ];
1383
- });
1348
+ })
1349
+ };
1384
1350
  }
1385
- var webSearchParams, BRAVE_SEARCH_URL, BraveSearchResponseSchema, MAX_PAGE_CHARS, MAX_HTML_BYTES, visitWebpageParams, fetchJsonParams, runCodeParams, vectorSearchParams, TOOL_CREATORS, MULTI_TOOL_BUILTINS;
1351
+ 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
1352
  var init_builtin_tools = __esm({
1387
1353
  "sdk/builtin_tools.ts"() {
1388
1354
  "use strict";
1389
1355
  init_internal_types();
1390
- init_memory_tools();
1356
+ init_utils();
1357
+ init_types();
1358
+ FETCH_TIMEOUT_MS = 15e3;
1359
+ RUN_CODE_TIMEOUT = 5e3;
1391
1360
  webSearchParams = z4.object({
1392
1361
  query: z4.string().describe("The search query"),
1393
1362
  max_results: z4.number().describe("Maximum number of results to return (default 5)").optional()
@@ -1422,16 +1391,13 @@ var init_builtin_tools = __esm({
1422
1391
  ),
1423
1392
  topK: z4.number().describe("Maximum results to return (default: 5)").optional()
1424
1393
  });
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
1394
+ TOOL_REGISTRY = {
1395
+ web_search: { create: (opts) => createWebSearch(opts?.fetch) },
1396
+ visit_webpage: { create: (opts) => createVisitWebpage(opts?.fetch) },
1397
+ fetch_json: { create: (opts) => createFetchJson(opts?.fetch) },
1398
+ run_code: { create: createRunCode },
1399
+ vector_search: { create: (opts) => createVectorSearch(opts?.vectorSearch ?? (async () => "")) },
1400
+ memory: { multi: memoryTools }
1435
1401
  };
1436
1402
  }
1437
1403
  });
@@ -1502,291 +1468,6 @@ var init_kv = __esm({
1502
1468
  }
1503
1469
  });
1504
1470
 
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
1471
  // sdk/session.ts
1791
1472
  function createS2sSession(opts) {
1792
1473
  const {
@@ -1817,7 +1498,6 @@ function createS2sSession(opts) {
1817
1498
  }));
1818
1499
  let s2s = null;
1819
1500
  const sessionAbort = new AbortController();
1820
- let audioReady = false;
1821
1501
  let toolCallCount = 0;
1822
1502
  let turnPromise = null;
1823
1503
  let conversationMessages = [];
@@ -1829,36 +1509,21 @@ function createS2sSession(opts) {
1829
1509
  if (!hookInvoker) return null;
1830
1510
  try {
1831
1511
  return await hookInvoker.resolveTurnConfig(id, HOOK_TIMEOUT_MS);
1832
- } catch {
1512
+ } catch (err) {
1513
+ log.warn("resolveTurnConfig hook failed", { err: errorMessage(err) });
1833
1514
  return null;
1834
1515
  }
1835
1516
  }
1836
1517
  function invokeHook(hook, arg) {
1837
1518
  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)
1519
+ try {
1520
+ const h = hookInvoker[hook];
1521
+ Promise.resolve(h.call(hookInvoker, id, arg, HOOK_TIMEOUT_MS)).catch((err) => {
1522
+ log.warn(`${hook} hook failed`, { err: errorMessage(err) });
1860
1523
  });
1861
- });
1524
+ } catch (err) {
1525
+ log.warn(`${hook} hook failed`, { err: errorMessage(err) });
1526
+ }
1862
1527
  }
1863
1528
  function checkTurnLimits(turnConfig, name) {
1864
1529
  const maxSteps = turnConfig?.maxSteps ?? agentConfig.maxSteps;
@@ -1898,7 +1563,7 @@ function createS2sSession(opts) {
1898
1563
  try {
1899
1564
  result = await executeTool(name, parsedArgs, id, conversationMessages);
1900
1565
  } catch (err) {
1901
- const msg = err instanceof Error ? err.message : String(err);
1566
+ const msg = errorMessage(err);
1902
1567
  log.error("Tool execution failed", { tool: name, error: msg });
1903
1568
  result = JSON.stringify({ error: msg });
1904
1569
  }
@@ -1913,6 +1578,79 @@ function createS2sSession(opts) {
1913
1578
  function on(target, event, handler) {
1914
1579
  target.addEventListener(event, handler);
1915
1580
  }
1581
+ function setupListeners(handle) {
1582
+ on(handle, "ready", (e) => {
1583
+ s2sSessionId = e.detail.session_id;
1584
+ log.info("S2S session ready", { session_id: s2sSessionId });
1585
+ });
1586
+ on(handle, "session_expired", () => {
1587
+ log.info("S2S session expired, reconnecting fresh");
1588
+ s2sSessionId = null;
1589
+ handle.close();
1590
+ });
1591
+ for (const type of ["speech_started", "speech_stopped"]) {
1592
+ handle.addEventListener(type, () => client.event({ type }));
1593
+ }
1594
+ on(handle, "user_transcript_delta", (e) => {
1595
+ client.event({ type: "transcript", text: e.detail.text, isFinal: false });
1596
+ });
1597
+ on(handle, "user_transcript", (e) => {
1598
+ const { text } = e.detail;
1599
+ log.info("S2S user transcript", { text });
1600
+ client.event({ type: "transcript", text, isFinal: true });
1601
+ client.event({ type: "turn", text });
1602
+ conversationMessages.push({ role: "user", content: text });
1603
+ invokeHook("onTurn", text);
1604
+ });
1605
+ handle.addEventListener("reply_started", () => {
1606
+ toolCallCount = 0;
1607
+ });
1608
+ on(handle, "audio", (e) => {
1609
+ client.playAudioChunk(e.detail.audio);
1610
+ });
1611
+ on(handle, "agent_transcript_delta", (e) => {
1612
+ client.event({ type: "chat_delta", text: e.detail.text });
1613
+ });
1614
+ on(handle, "agent_transcript", (e) => {
1615
+ const { text } = e.detail;
1616
+ client.event({ type: "chat", text });
1617
+ conversationMessages.push({ role: "assistant", content: text });
1618
+ });
1619
+ on(handle, "tool_call", (e) => {
1620
+ const p = handleToolCall(e.detail).catch((err) => {
1621
+ log.error("Tool call handler failed", { err: errorMessage(err) });
1622
+ });
1623
+ turnPromise = (turnPromise ?? Promise.resolve()).then(() => p);
1624
+ });
1625
+ on(handle, "reply_done", (e) => {
1626
+ if (e.detail.status === "interrupted") {
1627
+ log.info("S2S reply interrupted (barge-in)");
1628
+ pendingTools = [];
1629
+ client.event({ type: "cancelled" });
1630
+ } else if (pendingTools.length > 0) {
1631
+ for (const tool2 of pendingTools) s2s?.sendToolResult(tool2.call_id, tool2.result);
1632
+ pendingTools = [];
1633
+ } else {
1634
+ client.playAudioDone();
1635
+ client.event({ type: "tts_done" });
1636
+ }
1637
+ });
1638
+ on(handle, "error", (e) => {
1639
+ log.error("S2S error", { code: e.detail.code, message: e.detail.message });
1640
+ client.event({ type: "error", code: "internal", message: e.detail.message });
1641
+ handle.close();
1642
+ });
1643
+ handle.addEventListener("close", () => {
1644
+ log.info("S2S closed");
1645
+ s2s = null;
1646
+ if (!sessionAbort.signal.aborted) {
1647
+ log.info("Attempting S2S reconnect");
1648
+ connectAndSetup().catch((err) => {
1649
+ log.error("S2S reconnect failed", { error: errorMessage(err) });
1650
+ });
1651
+ }
1652
+ });
1653
+ }
1916
1654
  async function connectAndSetup() {
1917
1655
  if (connecting) {
1918
1656
  pendingReconnect = true;
@@ -1920,106 +1658,15 @@ function createS2sSession(opts) {
1920
1658
  }
1921
1659
  connecting = true;
1922
1660
  try {
1923
- const handle = await _internals2.connectS2s({
1661
+ const handle = await _internals.connectS2s({
1924
1662
  apiKey,
1925
1663
  config: s2sConfig,
1926
1664
  createWebSocket,
1927
1665
  logger: log
1928
1666
  });
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
- });
1667
+ setupListeners(handle);
2019
1668
  if (s2sSessionId) {
2020
- log.info("Attempting S2S session resume", {
2021
- session_id: s2sSessionId
2022
- });
1669
+ log.info("Attempting S2S session resume", { session_id: s2sSessionId });
2023
1670
  handle.resumeSession(s2sSessionId);
2024
1671
  } else {
2025
1672
  handle.updateSession({
@@ -2030,7 +1677,7 @@ function createS2sSession(opts) {
2030
1677
  }
2031
1678
  s2s = handle;
2032
1679
  } catch (err) {
2033
- const msg = err instanceof Error ? err.message : String(err);
1680
+ const msg = errorMessage(err);
2034
1681
  log.error("S2S connect failed", { error: msg });
2035
1682
  client.event({ type: "error", code: "internal", message: msg });
2036
1683
  } finally {
@@ -2038,8 +1685,7 @@ function createS2sSession(opts) {
2038
1685
  if (pendingReconnect && !sessionAbort.signal.aborted) {
2039
1686
  pendingReconnect = false;
2040
1687
  connectAndSetup().catch((err) => {
2041
- const msg = err instanceof Error ? err.message : String(err);
2042
- log.error("S2S deferred reconnect failed", { error: msg });
1688
+ log.error("S2S deferred reconnect failed", { error: errorMessage(err) });
2043
1689
  });
2044
1690
  }
2045
1691
  }
@@ -2063,8 +1709,6 @@ function createS2sSession(opts) {
2063
1709
  s2s?.sendAudio(data);
2064
1710
  },
2065
1711
  onAudioReady() {
2066
- if (audioReady) return;
2067
- audioReady = true;
2068
1712
  },
2069
1713
  onCancel() {
2070
1714
  client.event({ type: "cancelled" });
@@ -2088,17 +1732,36 @@ function createS2sSession(opts) {
2088
1732
  }
2089
1733
  };
2090
1734
  }
2091
- var _internals2;
1735
+ function buildSystemPrompt(config, opts) {
1736
+ const { hasTools } = opts;
1737
+ const agentInstructions = config.instructions && config.instructions !== DEFAULT_INSTRUCTIONS ? `
1738
+
1739
+ Agent-Specific Instructions:
1740
+ ${config.instructions}` : "";
1741
+ 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.' : "";
1742
+ const today = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
1743
+ weekday: "long",
1744
+ year: "numeric",
1745
+ month: "long",
1746
+ day: "numeric"
1747
+ });
1748
+ return DEFAULT_INSTRUCTIONS + `
1749
+
1750
+ Today's date is ${today}.` + agentInstructions + toolPreamble + (opts.voice ? VOICE_RULES : "");
1751
+ }
1752
+ var _internals, VOICE_RULES;
2092
1753
  var init_session = __esm({
2093
1754
  "sdk/session.ts"() {
2094
1755
  "use strict";
1756
+ init_utils();
2095
1757
  init_protocol();
2096
1758
  init_runtime();
2097
1759
  init_s2s();
2098
- init_system_prompt();
2099
- _internals2 = {
1760
+ init_types();
1761
+ _internals = {
2100
1762
  connectS2s
2101
1763
  };
1764
+ 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
1765
  }
2103
1766
  });
2104
1767
 
@@ -2149,11 +1812,10 @@ var init_vector = __esm({
2149
1812
 
2150
1813
  // sdk/worker_entry.ts
2151
1814
  function buildToolContext(opts) {
2152
- const { env, sessionId, state, kv, vector, messages } = opts;
1815
+ const { env, state, kv, vector, messages } = opts;
2153
1816
  return {
2154
- sessionId: sessionId ?? "",
2155
1817
  env: { ...env },
2156
- abortSignal: AbortSignal.timeout(TOOL_HANDLER_TIMEOUT),
1818
+ abortSignal: AbortSignal.timeout(TOOL_EXECUTION_TIMEOUT_MS),
2157
1819
  state: state ?? {},
2158
1820
  get kv() {
2159
1821
  if (!kv) throw new Error("KV not available");
@@ -2182,21 +1844,18 @@ async function executeToolCall(name, args, options) {
2182
1844
  if (result == null) return "null";
2183
1845
  return typeof result === "string" ? result : JSON.stringify(result);
2184
1846
  } 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
1847
  console.warn(`[tool-executor] Tool execution failed: ${name}`, err);
2190
- return `Error: ${err instanceof Error ? err.message : String(err)}`;
1848
+ return `Error: ${errorMessage(err)}`;
2191
1849
  }
2192
1850
  }
2193
- var yieldTick, TOOL_HANDLER_TIMEOUT;
1851
+ var yieldTick;
2194
1852
  var init_worker_entry = __esm({
2195
1853
  "sdk/worker_entry.ts"() {
2196
1854
  "use strict";
2197
1855
  init_internal_types();
1856
+ init_utils();
1857
+ init_protocol();
2198
1858
  yieldTick = () => new Promise((r) => setTimeout(r, 0));
2199
- TOOL_HANDLER_TIMEOUT = 3e4;
2200
1859
  }
2201
1860
  });
2202
1861
 
@@ -2248,7 +1907,6 @@ function createDirectExecutor(opts) {
2248
1907
  }
2249
1908
  function makeHookContext(sessionId) {
2250
1909
  return {
2251
- sessionId,
2252
1910
  env: frozenEnv,
2253
1911
  state: getState(sessionId),
2254
1912
  get kv() {
@@ -2265,7 +1923,6 @@ function createDirectExecutor(opts) {
2265
1923
  return executeToolCall(name, args, {
2266
1924
  tool: tool2,
2267
1925
  env: frozenEnv,
2268
- sessionId,
2269
1926
  state: getState(sessionId ?? ""),
2270
1927
  kv,
2271
1928
  vector,
@@ -2344,9 +2001,6 @@ var init_direct_executor = __esm({
2344
2001
  });
2345
2002
 
2346
2003
  // 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
2004
  function createClientSink(ws) {
2351
2005
  function safeSend(data) {
2352
2006
  try {
@@ -2378,19 +2032,9 @@ function toUint8Array(data) {
2378
2032
  const buf = data;
2379
2033
  return new Uint8Array(buf.buffer ?? data, buf.byteOffset ?? 0, buf.byteLength);
2380
2034
  }
2381
- function handleBinaryAudio(data, session, log, ctx, sid) {
2035
+ function handleBinaryAudio(data, session) {
2382
2036
  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);
2037
+ session.onAudio(toUint8Array(data));
2394
2038
  return true;
2395
2039
  }
2396
2040
  function handleTextMessage(data, session, log, ctx, sid) {
@@ -2425,7 +2069,7 @@ function handleTextMessage(data, session, log, ctx, sid) {
2425
2069
  }
2426
2070
  function wireSessionSocket(ws, opts) {
2427
2071
  const { sessions, logger: log = consoleLogger } = opts;
2428
- const sessionId = opts.uid ?? crypto.randomUUID();
2072
+ const sessionId = crypto.randomUUID();
2429
2073
  const sid = sessionId.slice(0, 8);
2430
2074
  const ctx = opts.logContext ?? {};
2431
2075
  let session = null;
@@ -2436,8 +2080,11 @@ function wireSessionSocket(ws, opts) {
2436
2080
  session = opts.createSession(sessionId, client);
2437
2081
  sessions.set(sessionId, session);
2438
2082
  ws.send(JSON.stringify({ type: "config", ...opts.readyConfig }));
2439
- void session.start();
2440
- log.info("Session ready", { ...ctx, sid });
2083
+ session.start().then(() => {
2084
+ log.info("Session ready", { ...ctx, sid });
2085
+ }).catch((err) => {
2086
+ log.error("Session start failed", { ...ctx, sid, error: errorMessage(err) });
2087
+ });
2441
2088
  }
2442
2089
  if (ws.readyState === 1) {
2443
2090
  onOpen();
@@ -2447,7 +2094,7 @@ function wireSessionSocket(ws, opts) {
2447
2094
  ws.addEventListener("message", (event) => {
2448
2095
  if (!session) return;
2449
2096
  const { data } = event;
2450
- if (handleBinaryAudio(data, session, log, ctx, sid)) return;
2097
+ if (handleBinaryAudio(data, session)) return;
2451
2098
  handleTextMessage(data, session, log, ctx, sid);
2452
2099
  });
2453
2100
  ws.addEventListener("close", () => {
@@ -2464,13 +2111,12 @@ function wireSessionSocket(ws, opts) {
2464
2111
  log.error("WebSocket error", { ...ctx, sid, error: msg });
2465
2112
  });
2466
2113
  }
2467
- var MAX_AUDIO_CHUNK_BYTES;
2468
2114
  var init_ws_handler = __esm({
2469
2115
  "sdk/ws_handler.ts"() {
2470
2116
  "use strict";
2117
+ init_utils();
2471
2118
  init_protocol();
2472
2119
  init_runtime();
2473
- MAX_AUDIO_CHUNK_BYTES = 1048576;
2474
2120
  }
2475
2121
  });
2476
2122
 
@@ -2529,8 +2175,7 @@ function createWintercServer(options) {
2529
2175
  skipGreeting: wsOpts?.skipGreeting ?? false
2530
2176
  }),
2531
2177
  readyConfig,
2532
- logger,
2533
- uid: wsOpts?.uid
2178
+ logger
2534
2179
  });
2535
2180
  },
2536
2181
  async close() {
@@ -2565,7 +2210,7 @@ async function loadWsFactory() {
2565
2210
  try {
2566
2211
  const mod = await import("ws");
2567
2212
  const WS = mod.default ?? mod;
2568
- return (url, opts) => new WS(url, { headers: opts.headers });
2213
+ return (url, opts) => wrapOnStyleWebSocket(new WS(url, { headers: opts.headers }));
2569
2214
  } catch {
2570
2215
  throw new Error(
2571
2216
  "WebSocket factory not provided and `ws` package not found. Install `ws` (`npm install ws`) or pass `createWebSocket` option."
@@ -2573,11 +2218,7 @@ async function loadWsFactory() {
2573
2218
  }
2574
2219
  }
2575
2220
  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;
2221
+ return Object.fromEntries(Object.entries(env).filter(([, v]) => v !== void 0));
2581
2222
  }
2582
2223
  function createServer(options) {
2583
2224
  const {
@@ -2624,6 +2265,23 @@ function createServer(options) {
2624
2265
  await getWsFactory();
2625
2266
  const wintercServer = getWinterc();
2626
2267
  const app = new Hono2();
2268
+ app.onError((err, c) => {
2269
+ logger.error(`${c.req.method} ${new URL(c.req.url).pathname} error: ${err.message}`);
2270
+ return c.json({ error: "Internal Server Error" }, 500);
2271
+ });
2272
+ app.use("/*", async (c, next) => {
2273
+ const start = Date.now();
2274
+ await next();
2275
+ const ms = Date.now() - start;
2276
+ const status = c.res.status;
2277
+ const method = c.req.method;
2278
+ const path8 = new URL(c.req.url).pathname;
2279
+ if (status >= 400) {
2280
+ logger.error(`${method} ${path8} ${status} ${ms}ms`);
2281
+ } else {
2282
+ logger.info(`${method} ${path8} ${status} ${ms}ms`);
2283
+ }
2284
+ });
2627
2285
  if (clientDir) {
2628
2286
  app.use("/*", serveStatic({ root: clientDir }));
2629
2287
  }
@@ -2636,11 +2294,12 @@ function createServer(options) {
2636
2294
  const wsMod = await import("ws");
2637
2295
  const wss = new wsMod.WebSocketServer({ noServer: true });
2638
2296
  nodeServer.on("upgrade", (req, socket, head) => {
2297
+ const reqUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
2298
+ const resume = reqUrl.searchParams.has("resume");
2299
+ logger.info(`WS upgrade ${reqUrl.pathname}${resume ? " (resume)" : ""}`);
2639
2300
  wss.handleUpgrade(req, socket, head, (ws) => {
2640
- const reqUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
2641
2301
  wintercServer.handleWebSocket(ws, {
2642
- skipGreeting: reqUrl.searchParams.has("resume"),
2643
- uid: reqUrl.searchParams.get("uid") ?? void 0
2302
+ skipGreeting: resume
2644
2303
  });
2645
2304
  });
2646
2305
  });
@@ -2665,88 +2324,30 @@ var init_server = __esm({
2665
2324
  "sdk/server.ts"() {
2666
2325
  "use strict";
2667
2326
  init_runtime();
2327
+ init_s2s();
2668
2328
  init_winterc_server();
2669
2329
  }
2670
2330
  });
2671
2331
 
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
2332
  // cli/_server_common.ts
2725
- init_discover();
2726
2333
  import fs5 from "node:fs/promises";
2727
- import path7 from "node:path";
2728
- import { createServer as createViteServer } from "vite";
2334
+ import path5 from "node:path";
2335
+ import { tsImport } from "tsx/esm/api";
2729
2336
  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();
2337
+ const agentPath = path5.resolve(cwd, "agent.ts");
2338
+ const agentModule = await tsImport(agentPath, cwd);
2339
+ let agentDef = agentModule.default;
2340
+ if (agentDef?.__esModule && agentDef.default) {
2341
+ agentDef = agentDef.default;
2342
+ }
2343
+ if (!agentDef || typeof agentDef !== "object" || !agentDef.name) {
2344
+ throw new Error("agent.ts must export a default agent definition (from defineAgent())");
2745
2345
  }
2346
+ return agentDef;
2746
2347
  }
2747
- async function resolveServerEnv() {
2348
+ async function resolveServerEnv(baseEnv) {
2748
2349
  const env = Object.fromEntries(
2749
- Object.entries(process.env).filter((e) => e[1] !== void 0)
2350
+ Object.entries(baseEnv ?? process.env).filter((e) => e[1] !== void 0)
2750
2351
  );
2751
2352
  if (!env.ASSEMBLYAI_API_KEY) {
2752
2353
  env.ASSEMBLYAI_API_KEY = await getApiKey();
@@ -2754,10 +2355,11 @@ async function resolveServerEnv() {
2754
2355
  return env;
2755
2356
  }
2756
2357
  async function bootServer(agentDef, clientDir, env, port) {
2358
+ const { wrapOnStyleWebSocket: wrapOnStyleWebSocket2 } = await Promise.resolve().then(() => (init_s2s(), s2s_exports));
2757
2359
  const wsMod = await import("ws");
2758
2360
  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");
2361
+ const createWebSocket = (url, opts) => wrapOnStyleWebSocket2(new WS(url, { headers: opts.headers }));
2362
+ const clientHtml = await fs5.readFile(path5.join(clientDir, "index.html"), "utf-8");
2761
2363
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2762
2364
  const server = createServer2({
2763
2365
  agent: agentDef,
@@ -2768,155 +2370,186 @@ async function bootServer(agentDef, clientDir, env, port) {
2768
2370
  });
2769
2371
  await server.listen(port);
2770
2372
  }
2373
+ var init_server_common = __esm({
2374
+ "cli/_server_common.ts"() {
2375
+ "use strict";
2376
+ init_discover();
2377
+ }
2378
+ });
2771
2379
 
2772
- // cli/_dev.ts
2380
+ // cli/dev.tsx
2381
+ var dev_exports = {};
2382
+ __export(dev_exports, {
2383
+ _startDevServer: () => _startDevServer,
2384
+ runDevCommand: () => runDevCommand
2385
+ });
2386
+ import { jsx as jsx6 } from "react/jsx-runtime";
2773
2387
  async function _startDevServer(cwd, port, log) {
2774
2388
  const bundle = await buildAgentBundle(cwd, log, { skipRenderCheck: true });
2775
2389
  const agentDef = await loadAgentDef(cwd);
2776
2390
  const env = await resolveServerEnv();
2777
2391
  await bootServer(agentDef, bundle.clientDir, env, port);
2778
- log(React3.createElement(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2392
+ log(/* @__PURE__ */ jsx6(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2779
2393
  }
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" }
2394
+ async function runDevCommand(opts) {
2395
+ const port = Number.parseInt(opts.port, 10);
2396
+ await runWithInk(async ({ log }) => {
2397
+ await _startDevServer(opts.cwd, port, log);
2802
2398
  });
2803
- if (parsed.help) {
2804
- console.log(subcommandHelp(devCommandDef, version));
2805
- return;
2399
+ }
2400
+ var init_dev = __esm({
2401
+ "cli/dev.tsx"() {
2402
+ "use strict";
2403
+ init_build();
2404
+ init_ink();
2405
+ init_server_common();
2806
2406
  }
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 });
2407
+ });
2408
+
2409
+ // cli/start.tsx
2410
+ var start_exports = {};
2411
+ __export(start_exports, {
2412
+ _startProductionServer: () => _startProductionServer,
2413
+ runStartCommand: () => runStartCommand
2414
+ });
2415
+ import path6 from "node:path";
2416
+ import { jsx as jsx7 } from "react/jsx-runtime";
2417
+ async function _startProductionServer(cwd, port, log) {
2418
+ const clientDir = path6.join(cwd, ".aai", "client");
2419
+ log(/* @__PURE__ */ jsx7(Step, { action: "Start", msg: "loading agent" }));
2420
+ const agentDef = await loadAgentDef(cwd);
2421
+ const env = await resolveServerEnv();
2422
+ await bootServer(agentDef, clientDir, env, port);
2423
+ log(/* @__PURE__ */ jsx7(Step, { action: "Ready", msg: `http://localhost:${port}` }));
2424
+ }
2425
+ async function runStartCommand(opts) {
2426
+ const port = Number.parseInt(opts.port, 10);
2427
+ const buildDir = path6.join(opts.cwd, ".aai", "build");
2428
+ if (!await fileExists(path6.join(buildDir, "worker.js"))) {
2429
+ throw new Error("No build found \u2014 run `aai build` first");
2811
2430
  }
2812
- await getApiKey();
2813
- await runWithInk(async (log) => {
2814
- await _startDevServer(cwd, port, log);
2431
+ await runWithInk(async ({ log }) => {
2432
+ log(/* @__PURE__ */ jsx7(Step, { action: "Start", msg: `production server on port ${port}` }));
2433
+ await _startProductionServer(opts.cwd, port, log);
2815
2434
  });
2816
2435
  }
2436
+ var init_start = __esm({
2437
+ "cli/start.tsx"() {
2438
+ "use strict";
2439
+ init_discover();
2440
+ init_ink();
2441
+ init_server_common();
2442
+ }
2443
+ });
2817
2444
 
2818
- // cli/cli.ts
2819
- init_init2();
2445
+ // cli/secret.tsx
2446
+ var secret_exports = {};
2447
+ __export(secret_exports, {
2448
+ runSecretDelete: () => runSecretDelete,
2449
+ runSecretList: () => runSecretList,
2450
+ runSecretPut: () => runSecretPut
2451
+ });
2452
+ import { jsx as jsx8 } from "react/jsx-runtime";
2453
+ async function apiFetch(cwd, pathSuffix, init) {
2454
+ const { serverUrl, slug, apiKey } = await getServerInfo(cwd);
2455
+ const resp = await fetch(`${serverUrl}/${slug}/secret${pathSuffix}`, {
2456
+ ...init,
2457
+ headers: { Authorization: `Bearer ${apiKey}`, ...init?.headers }
2458
+ });
2459
+ if (!resp.ok) {
2460
+ const text = await resp.text();
2461
+ throw new Error(`Secret operation failed: ${text}`);
2462
+ }
2463
+ return { resp, slug };
2464
+ }
2465
+ async function runSecretPut(cwd, name) {
2466
+ const value = await askPassword(`Enter value for ${name}`);
2467
+ if (!value) throw new Error("No value provided");
2468
+ await runWithInk(async ({ log }) => {
2469
+ const { slug } = await apiFetch(cwd, "", {
2470
+ method: "PUT",
2471
+ headers: { "Content-Type": "application/json" },
2472
+ body: JSON.stringify({ [name]: value })
2473
+ });
2474
+ log(/* @__PURE__ */ jsx8(Step, { action: "Set", msg: `${name} for ${slug}` }));
2475
+ });
2476
+ }
2477
+ async function runSecretDelete(cwd, name) {
2478
+ await runWithInk(async ({ log }) => {
2479
+ const { slug } = await apiFetch(cwd, `/${name}`, { method: "DELETE" });
2480
+ log(/* @__PURE__ */ jsx8(Step, { action: "Deleted", msg: `${name} from ${slug}` }));
2481
+ });
2482
+ }
2483
+ async function runSecretList(cwd) {
2484
+ await runWithInk(async ({ log }) => {
2485
+ const { resp } = await apiFetch(cwd, "");
2486
+ const { vars } = await resp.json();
2487
+ if (vars.length === 0) {
2488
+ log(/* @__PURE__ */ jsx8(StepInfo, { action: "Secrets", msg: "none set" }));
2489
+ } else {
2490
+ for (const name of vars) {
2491
+ log(/* @__PURE__ */ jsx8(Detail, { msg: name }));
2492
+ }
2493
+ }
2494
+ });
2495
+ }
2496
+ var init_secret = __esm({
2497
+ "cli/secret.tsx"() {
2498
+ "use strict";
2499
+ init_discover();
2500
+ init_ink();
2501
+ init_prompts();
2502
+ }
2503
+ });
2820
2504
 
2821
2505
  // 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";
2506
+ var rag_exports = {};
2507
+ __export(rag_exports, {
2508
+ chunkPages: () => chunkPages,
2509
+ parsePage: () => parsePage,
2510
+ runRagCommand: () => runRagCommand,
2511
+ slugify: () => slugify,
2512
+ splitPages: () => splitPages,
2513
+ stripNoise: () => stripNoise,
2514
+ upsertChunks: () => upsertChunks
2515
+ });
2516
+ import { Text as Text3 } from "ink";
2827
2517
  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
- }
2518
+ import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
2886
2519
  async function runRag(opts) {
2887
- const { url, apiKey, serverUrl, slug, chunkSize, log, setProgress } = opts;
2888
- log(/* @__PURE__ */ jsx6(Step, { action: "Fetch", msg: url }));
2520
+ const { url, apiKey, serverUrl, slug, chunkSize, log, setStatus } = opts;
2521
+ log(/* @__PURE__ */ jsx9(Step, { action: "Fetch", msg: url }));
2889
2522
  const resp = await fetch(url, {
2890
2523
  headers: { "User-Agent": "aai-cli/1.0" },
2891
2524
  redirect: "follow",
2892
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
2525
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
2893
2526
  });
2894
2527
  if (!resp.ok) {
2895
2528
  throw new Error(`Failed to fetch: ${resp.status} ${resp.statusText}`);
2896
2529
  }
2897
2530
  const content = await resp.text();
2898
2531
  if (content.length === 0) {
2899
- log(/* @__PURE__ */ jsx6(Warn, { msg: "File is empty" }));
2532
+ log(/* @__PURE__ */ jsx9(Warn, { msg: "File is empty" }));
2900
2533
  return;
2901
2534
  }
2902
- log(/* @__PURE__ */ jsx6(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
2535
+ log(/* @__PURE__ */ jsx9(Info, { msg: `${(content.length / 1024).toFixed(0)} KB` }));
2903
2536
  const origin = new URL(url).origin;
2904
2537
  const pages = splitPages(content);
2905
- log(/* @__PURE__ */ jsx6(Step, { action: "Parse", msg: `${pages.length} pages` }));
2538
+ log(/* @__PURE__ */ jsx9(Step, { action: "Parse", msg: `${pages.length} pages` }));
2906
2539
  const { RecursiveChunker } = await import("@chonkiejs/core");
2907
2540
  const chunker = await RecursiveChunker.create({ chunkSize });
2908
2541
  const siteSlug = slugify(origin);
2909
2542
  const allChunks = await chunkPages(pages, chunker, origin, siteSlug);
2910
- log(/* @__PURE__ */ jsx6(Step, { action: "Chunk", msg: `${allChunks.length} chunks` }));
2543
+ log(/* @__PURE__ */ jsx9(Step, { action: "Chunk", msg: `${allChunks.length} chunks` }));
2911
2544
  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` }));
2545
+ log(/* @__PURE__ */ jsx9(Info, { msg: `target: ${vectorUrl}` }));
2546
+ const result = await upsertChunks(allChunks, vectorUrl, apiKey, setStatus);
2547
+ log(/* @__PURE__ */ jsx9(Step, { action: "Done", msg: `${result.upserted} chunks upserted` }));
2915
2548
  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}` }));
2549
+ log(/* @__PURE__ */ jsx9(Warn, { msg: `${result.errors} failed` }));
2550
+ if (result.lastError) log(/* @__PURE__ */ jsx9(Info, { msg: `last error: ${result.lastError}` }));
2918
2551
  }
2919
- log(/* @__PURE__ */ jsx6(Detail, { msg: `Agent: ${slug}` }));
2552
+ log(/* @__PURE__ */ jsx9(Detail, { msg: `Agent: ${slug}` }));
2920
2553
  }
2921
2554
  async function chunkPages(pages, chunker, origin, siteSlug) {
2922
2555
  const allChunks = [];
@@ -2942,19 +2575,34 @@ ${c.text}` : c.text;
2942
2575
  }
2943
2576
  return allChunks;
2944
2577
  }
2945
- async function upsertChunks(chunks, vectorUrl, apiKey, setProgress) {
2578
+ async function upsertChunks(chunks, vectorUrl, apiKey, setStatus, fetchFn = globalThis.fetch) {
2946
2579
  const total = chunks.length;
2947
2580
  let completed = 0;
2948
2581
  let upserted = 0;
2949
2582
  let errors = 0;
2950
2583
  let lastError = "";
2951
- setProgress({ completed: 0, total });
2584
+ const updateStatus = () => {
2585
+ const pct = Math.round(completed / total * 100);
2586
+ setStatus(
2587
+ /* @__PURE__ */ jsxs3(Text3, { children: [
2588
+ " ",
2589
+ "Upsert ",
2590
+ completed,
2591
+ "/",
2592
+ total,
2593
+ " (",
2594
+ pct,
2595
+ "%)"
2596
+ ] })
2597
+ );
2598
+ };
2599
+ updateStatus();
2952
2600
  const limit = pLimit(5);
2953
2601
  await Promise.all(
2954
2602
  chunks.map(
2955
2603
  (chunk) => limit(async () => {
2956
2604
  try {
2957
- const r = await fetch(vectorUrl, {
2605
+ const r = await fetchFn(vectorUrl, {
2958
2606
  method: "POST",
2959
2607
  headers: {
2960
2608
  "Content-Type": "application/json",
@@ -2974,65 +2622,29 @@ async function upsertChunks(chunks, vectorUrl, apiKey, setProgress) {
2974
2622
  upserted++;
2975
2623
  }
2976
2624
  } catch (err) {
2977
- lastError = err instanceof Error ? err.message : String(err);
2625
+ lastError = errorMessage(err);
2978
2626
  errors++;
2979
2627
  }
2980
2628
  completed++;
2981
- setProgress({ completed, total });
2629
+ updateStatus();
2982
2630
  })
2983
2631
  )
2984
2632
  );
2633
+ setStatus(null);
2985
2634
  return { upserted, errors, lastError };
2986
2635
  }
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
- }
2636
+ async function runRagCommand(opts) {
2637
+ const { url, cwd } = opts;
3004
2638
  try {
3005
2639
  new URL(url);
3006
2640
  } catch {
3007
2641
  throw new Error(`Invalid URL: ${url}`);
3008
2642
  }
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;
2643
+ const { apiKey, serverUrl, slug } = await getServerInfo(cwd, opts.server);
2644
+ const chunkSize = Number.parseInt(opts.chunkSize ?? "512", 10);
2645
+ await runWithInk(async ({ log, setStatus }) => {
2646
+ await runRag({ url, apiKey, serverUrl, slug, chunkSize, log, setStatus });
2647
+ });
3036
2648
  }
3037
2649
  function splitPages(content) {
3038
2650
  const raw = content.split(/^\*{3,}$/m);
@@ -3079,243 +2691,153 @@ function stripNoise(text) {
3079
2691
  function slugify(s) {
3080
2692
  return s.replace(/^https?:\/\//, "").replace(/^#+\s*/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase().slice(0, 80);
3081
2693
  }
2694
+ var FETCH_TIMEOUT_MS2;
2695
+ var init_rag = __esm({
2696
+ "cli/rag.tsx"() {
2697
+ "use strict";
2698
+ init_utils();
2699
+ init_discover();
2700
+ init_ink();
2701
+ FETCH_TIMEOUT_MS2 = 6e4;
2702
+ }
2703
+ });
3082
2704
 
3083
- // cli/secret.tsx
2705
+ // cli/cli.ts
2706
+ init_utils();
3084
2707
  init_discover();
3085
- init_help();
3086
2708
  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
2709
+ import { readFileSync } from "node:fs";
2710
+ import path7 from "node:path";
2711
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
2712
+ import chalk2 from "chalk";
2713
+ import { Command } from "commander";
2714
+ var cliDir = path7.dirname(fileURLToPath2(import.meta.url));
2715
+ var pkgJsonPath = path7.join(cliDir, "..", "package.json");
2716
+ var pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
2717
+ var VERSION = pkgJson.version;
2718
+ var banner = [
2719
+ "",
2720
+ ` ${primary(chalk2.bold(" \u2584\u2580\u2588 \u2584\u2580\u2588 \u2588"))} ${chalk2.dim("Voice agent development kit")}`,
2721
+ ` ${primary(chalk2.bold(" \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588"))} ${primary(`v${VERSION}`)}`,
2722
+ ""
2723
+ ].join("\n");
2724
+ var gettingStarted = [
2725
+ "",
2726
+ ` ${chalk2.bold(interactive("Getting started"))}`,
2727
+ "",
2728
+ ` ${chalk2.dim("$")} ${primary("aai init")} ${interactive("my-agent")}`,
2729
+ ` ${chalk2.dim("$")} ${primary("cd")} ${interactive("my-agent")}`,
2730
+ ` ${chalk2.dim("$")} ${primary("aai dev")}`,
2731
+ ""
2732
+ ].join("\n");
2733
+ async function ensureAgent(cwd, yes) {
2734
+ if (!await fileExists(path7.join(cwd, "agent.ts"))) {
2735
+ const { runInitCommand: runInitCommand2 } = await Promise.resolve().then(() => (init_init2(), init_exports2));
2736
+ await runInitCommand2({ yes }, { quiet: true });
2737
+ }
2738
+ }
2739
+ function withCwd(cmd) {
2740
+ return cmd.hook("preAction", (thisCmd) => {
2741
+ thisCmd.setOptionValue("cwd", resolveCwd());
3111
2742
  });
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
2743
  }
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}` }));
2744
+ function withAgentGuard(cmd) {
2745
+ return cmd.hook("preAction", async (thisCmd) => {
2746
+ await ensureAgent(thisCmd.getOptionValue("cwd"), thisCmd.opts().yes);
3163
2747
  });
3164
2748
  }
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}` }));
2749
+ function withApiKey(cmd) {
2750
+ return cmd.hook("preAction", async () => {
2751
+ await getApiKey();
3178
2752
  });
3179
2753
  }
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
- }
2754
+ function createProgram() {
2755
+ const program = new Command();
2756
+ program.name("aai").version(VERSION, "-V, --version").addHelpText("before", banner).addHelpText("after", gettingStarted);
2757
+ 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(
2758
+ async (dir, opts) => {
2759
+ const { runInitCommand: runInitCommand2 } = await Promise.resolve().then(() => (init_init2(), init_exports2));
2760
+ await runInitCommand2({ dir, ...opts });
3197
2761
  }
2762
+ );
2763
+ withApiKey(
2764
+ withAgentGuard(
2765
+ withCwd(
2766
+ 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) => {
2767
+ const { runDevCommand: runDevCommand2 } = await Promise.resolve().then(() => (init_dev(), dev_exports));
2768
+ await runDevCommand2(opts);
2769
+ })
2770
+ )
2771
+ )
2772
+ );
2773
+ withAgentGuard(
2774
+ withCwd(
2775
+ program.command("build").description("Bundle and validate (no server or deploy)").option("-y, --yes", "Accept defaults (no prompts)").action(async (opts) => {
2776
+ const { runBuildCommand: runBuildCommand2 } = await Promise.resolve().then(() => (init_build(), build_exports));
2777
+ await runBuildCommand2(opts.cwd);
2778
+ })
2779
+ )
2780
+ );
2781
+ withAgentGuard(
2782
+ withCwd(
2783
+ 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) => {
2784
+ const { runDeployCommand: runDeployCommand2 } = await Promise.resolve().then(() => (init_deploy2(), deploy_exports));
2785
+ await runDeployCommand2(opts);
2786
+ })
2787
+ )
2788
+ );
2789
+ withApiKey(
2790
+ withCwd(
2791
+ 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) => {
2792
+ const { runStartCommand: runStartCommand2 } = await Promise.resolve().then(() => (init_start(), start_exports));
2793
+ await runStartCommand2(opts);
2794
+ })
2795
+ )
2796
+ );
2797
+ const secret = program.command("secret").description("Manage secrets").action(() => secret.help());
2798
+ withApiKey(withCwd(secret));
2799
+ secret.command("put").description("Create or update a secret").argument("<name>", "Secret name").action(async (name, _opts, cmd) => {
2800
+ const cwd = cmd.parent?.getOptionValue("cwd");
2801
+ const { runSecretPut: runSecretPut2 } = await Promise.resolve().then(() => (init_secret(), secret_exports));
2802
+ await runSecretPut2(cwd, name);
3198
2803
  });
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" }
2804
+ secret.command("delete").description("Delete a secret").argument("<name>", "Secret name").action(async (name, _opts, cmd) => {
2805
+ const cwd = cmd.parent?.getOptionValue("cwd");
2806
+ const { runSecretDelete: runSecretDelete2 } = await Promise.resolve().then(() => (init_secret(), secret_exports));
2807
+ await runSecretDelete2(cwd, name);
3239
2808
  });
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);
2809
+ secret.command("list").description("List secret names").action(async (_opts, cmd) => {
2810
+ const cwd = cmd.parent?.getOptionValue("cwd");
2811
+ const { runSecretList: runSecretList2 } = await Promise.resolve().then(() => (init_secret(), secret_exports));
2812
+ await runSecretList2(cwd);
3254
2813
  });
2814
+ withApiKey(
2815
+ withCwd(
2816
+ 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) => {
2817
+ const { runRagCommand: runRagCommand2 } = await Promise.resolve().then(() => (init_rag(), rag_exports));
2818
+ await runRagCommand2({ url, ...opts });
2819
+ })
2820
+ )
2821
+ );
2822
+ return program;
3255
2823
  }
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
2824
  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);
2825
+ if (args.length === 0) {
2826
+ const { runInitCommand: runInitCommand2 } = await Promise.resolve().then(() => (init_init2(), init_exports2));
2827
+ await runInitCommand2({});
3270
2828
  return;
3271
2829
  }
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
- }
2830
+ const program = createProgram();
2831
+ await program.parseAsync(args, { from: "user" });
3311
2832
  }
3312
2833
  if (process.env.VITEST !== "true") {
3313
2834
  process.on("SIGINT", () => process.exit(0));
3314
2835
  main(process.argv.slice(2)).catch((err) => {
3315
- console.error(err instanceof Error ? err.message : String(err));
2836
+ console.error(errorMessage(err));
3316
2837
  process.exit(1);
3317
2838
  });
3318
2839
  }
3319
2840
  export {
2841
+ createProgram,
3320
2842
  main
3321
2843
  };