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