@alexkroman1/aai 0.12.3 → 1.0.3
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/.turbo/turbo-build.log +20 -0
- package/CHANGELOG.md +176 -0
- package/dist/constants-VTFoymJ-.js +47 -0
- package/dist/host/_run-code.d.ts +1 -1
- package/dist/host/_runtime-conformance.d.ts +4 -5
- package/dist/host/builtin-tools.d.ts +11 -9
- package/dist/host/runtime-barrel.d.ts +15 -0
- package/dist/{direct-executor-DRRrZUp0.js → host/runtime-barrel.js} +453 -348
- package/dist/host/runtime-config.d.ts +42 -0
- package/dist/host/runtime.d.ts +119 -35
- package/dist/host/s2s.d.ts +14 -38
- package/dist/host/server.d.ts +16 -8
- package/dist/host/session-ctx.d.ts +55 -0
- package/dist/host/session.d.ts +20 -70
- package/dist/host/tool-executor.d.ts +20 -0
- package/dist/host/unstorage-kv.d.ts +1 -1
- package/dist/host/ws-handler.d.ts +4 -2
- package/dist/index.d.ts +9 -20
- package/dist/index.js +63 -2
- package/dist/{isolate → sdk}/_internal-types.d.ts +5 -9
- package/dist/{isolate → sdk}/constants.d.ts +6 -4
- package/dist/sdk/define.d.ts +66 -0
- package/dist/{isolate → sdk}/kv.d.ts +1 -49
- package/dist/sdk/manifest-barrel.d.ts +8 -0
- package/dist/sdk/manifest-barrel.js +52 -0
- package/dist/sdk/manifest.d.ts +50 -0
- package/dist/{isolate → sdk}/protocol.d.ts +59 -36
- package/dist/sdk/protocol.js +163 -0
- package/dist/{isolate → sdk}/system-prompt.d.ts +2 -2
- package/dist/sdk/types.d.ts +201 -0
- package/dist/sdk/ws-upgrade.d.ts +5 -0
- package/dist/{system-prompt-DYAYFW99.js → system-prompt-nik_iavo.js} +10 -10
- package/dist/types-Cfx_4QDK.js +39 -0
- package/dist/ws-upgrade-BeOQ7fXL.js +30 -0
- package/exports-no-dev-deps.test.ts +62 -0
- package/host/_mock-ws.ts +185 -0
- package/host/_run-code.ts +217 -0
- package/host/_runtime-conformance.ts +143 -0
- package/host/_test-utils.ts +276 -0
- package/host/builtin-tools.test.ts +774 -0
- package/host/builtin-tools.ts +255 -0
- package/host/cleanup.test.ts +422 -0
- package/host/fixture-replay.test.ts +463 -0
- package/host/fixtures/README.md +40 -0
- package/host/fixtures/greeting-session-sequence.json +40 -0
- package/host/fixtures/reply-audio-samples.json +42 -0
- package/host/fixtures/reply-lifecycle.json +21 -0
- package/host/fixtures/session-ready.json +48 -0
- package/host/fixtures/session-updated.json +45 -0
- package/host/fixtures/simple-question-sequence.json +73 -0
- package/host/fixtures/tool-call-sequence.json +114 -0
- package/host/fixtures/tool-calls.json +11 -0
- package/host/fixtures/tool-config-session-sequence.json +51 -0
- package/host/fixtures/user-speech-recognition.json +30 -0
- package/host/fixtures/web-search-sequence.json +122 -0
- package/host/integration.test.ts +222 -0
- package/host/runtime-barrel.ts +25 -0
- package/host/runtime-config.test.ts +71 -0
- package/host/runtime-config.ts +99 -0
- package/host/runtime.test.ts +641 -0
- package/host/runtime.ts +308 -0
- package/host/s2s-fixtures.test.ts +237 -0
- package/host/s2s.test.ts +562 -0
- package/host/s2s.ts +310 -0
- package/host/server-shutdown.test.ts +76 -0
- package/host/server.test.ts +116 -0
- package/host/server.ts +223 -0
- package/host/session-ctx.ts +107 -0
- package/host/session-fixture-replay.test.ts +136 -0
- package/host/session-prompt.test.ts +77 -0
- package/host/session.test.ts +590 -0
- package/host/session.ts +370 -0
- package/host/tool-executor.test.ts +124 -0
- package/host/tool-executor.ts +80 -0
- package/host/unstorage-kv.test.ts +99 -0
- package/host/unstorage-kv.ts +69 -0
- package/host/ws-handler.test.ts +739 -0
- package/host/ws-handler.ts +255 -0
- package/index.ts +16 -0
- package/package.json +24 -72
- package/sdk/_internal-types.test.ts +34 -0
- package/sdk/_internal-types.ts +115 -0
- package/sdk/compat-fixtures/README.md +26 -0
- package/sdk/compat-fixtures/v1.json +68 -0
- package/sdk/constants.ts +77 -0
- package/sdk/define.test.ts +57 -0
- package/sdk/define.ts +88 -0
- package/sdk/kv.ts +60 -0
- package/sdk/manifest-barrel.ts +12 -0
- package/sdk/manifest.test.ts +56 -0
- package/sdk/manifest.ts +89 -0
- package/sdk/protocol-compat.test.ts +187 -0
- package/sdk/protocol-snapshot.test.ts +199 -0
- package/sdk/protocol.test.ts +170 -0
- package/sdk/protocol.ts +223 -0
- package/sdk/schema-alignment.test.ts +191 -0
- package/sdk/system-prompt.test.ts +111 -0
- package/sdk/system-prompt.ts +74 -0
- package/sdk/tsconfig.json +12 -0
- package/sdk/types-inference.test.ts +122 -0
- package/sdk/types.test.ts +14 -0
- package/sdk/types.ts +226 -0
- package/sdk/utils.test.ts +52 -0
- package/sdk/utils.ts +20 -0
- package/sdk/ws-upgrade.test.ts +48 -0
- package/sdk/ws-upgrade.ts +13 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +10 -0
- package/tsdown.config.ts +26 -0
- package/vitest.config.ts +17 -0
- package/dist/host/_test-utils.d.ts +0 -73
- package/dist/host/direct-executor.d.ts +0 -130
- package/dist/host/index.d.ts +0 -19
- package/dist/host/index.js +0 -165
- package/dist/host/matchers.d.ts +0 -20
- package/dist/host/matchers.js +0 -41
- package/dist/host/server.js +0 -164
- package/dist/host/testing.d.ts +0 -294
- package/dist/host/testing.js +0 -2
- package/dist/host/vite-plugin.d.ts +0 -15
- package/dist/host/vite-plugin.js +0 -83
- package/dist/isolate/_kv-utils.d.ts +0 -10
- package/dist/isolate/_utils.js +0 -17
- package/dist/isolate/hooks.d.ts +0 -44
- package/dist/isolate/hooks.js +0 -58
- package/dist/isolate/index.d.ts +0 -18
- package/dist/isolate/index.js +0 -6
- package/dist/isolate/kv.js +0 -1
- package/dist/isolate/protocol.js +0 -2
- package/dist/isolate/types.d.ts +0 -418
- package/dist/isolate/types.js +0 -175
- package/dist/protocol-rcOrz7T3.js +0 -183
- package/dist/testing-BreLdpq-.js +0 -513
- package/dist/types.test-d.d.ts +0 -7
- /package/dist/{isolate/_utils.d.ts → sdk/utils.d.ts} +0 -0
|
@@ -1,18 +1,76 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { errorDetail, errorMessage,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { a as DEFAULT_SHUTDOWN_TIMEOUT_MS, c as FETCH_TIMEOUT_MS, d as MAX_PAGE_CHARS, f as MAX_TOOL_RESULT_CHARS, g as TOOL_EXECUTION_TIMEOUT_MS, h as RUN_CODE_TIMEOUT_MS, l as MAX_HTML_BYTES, m as MAX_WS_PAYLOAD_BYTES, o as DEFAULT_STT_SAMPLE_RATE, p as MAX_VALUE_SIZE, s as DEFAULT_TTS_SAMPLE_RATE, t as AGENT_CSP } from "../constants-VTFoymJ-.js";
|
|
2
|
+
import { i as toolError, n as errorDetail, r as errorMessage, t as parseWsUpgradeParams } from "../ws-upgrade-BeOQ7fXL.js";
|
|
3
|
+
import { ClientMessageSchema, buildReadyConfig, lenientParse } from "../sdk/protocol.js";
|
|
4
|
+
import { a as agentToolsToSchemas, o as toAgentConfig, r as EMPTY_PARAMS, t as buildSystemPrompt } from "../system-prompt-nik_iavo.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import { convert } from "html-to-text";
|
|
6
7
|
import vm from "node:vm";
|
|
7
8
|
import pTimeout from "p-timeout";
|
|
8
9
|
import { createStorage, prefixStorage } from "unstorage";
|
|
9
10
|
import { createNanoEvents } from "nanoevents";
|
|
10
|
-
import WsWebSocket from "ws";
|
|
11
|
+
import WsWebSocket, { WebSocketServer } from "ws";
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import http from "node:http";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import escapeHtml from "escape-html";
|
|
16
|
+
import { lookup } from "mime-types";
|
|
11
17
|
//#region host/_run-code.ts
|
|
12
18
|
/**
|
|
13
19
|
* run_code built-in tool — executes user JavaScript in a fresh `node:vm`
|
|
14
20
|
* context with no network, filesystem, or process access.
|
|
15
21
|
*/
|
|
22
|
+
const SKIPPED_CLASS_KEYS = new Set([
|
|
23
|
+
"constructor",
|
|
24
|
+
"prototype",
|
|
25
|
+
"length",
|
|
26
|
+
"name"
|
|
27
|
+
]);
|
|
28
|
+
/**
|
|
29
|
+
* Copy static members from a class constructor to a wrapper function,
|
|
30
|
+
* skipping built-in keys that must not be forwarded.
|
|
31
|
+
*/
|
|
32
|
+
function copyStaticMembers(src, dst) {
|
|
33
|
+
for (const key of Object.getOwnPropertyNames(src)) {
|
|
34
|
+
if (SKIPPED_CLASS_KEYS.has(key)) continue;
|
|
35
|
+
try {
|
|
36
|
+
const desc = Object.getOwnPropertyDescriptor(src, key);
|
|
37
|
+
if (desc) Object.defineProperty(dst, key, desc);
|
|
38
|
+
} catch {}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Neuter the `.constructor` chain on a host function or class constructor.
|
|
43
|
+
*
|
|
44
|
+
* For plain functions: wraps the function so calling `.constructor` or
|
|
45
|
+
* `.constructor.constructor` no longer exposes the host `Function`.
|
|
46
|
+
*
|
|
47
|
+
* For class constructors: additionally copies static methods and neutralizes
|
|
48
|
+
* `prototype.constructor` so instances created via `new` also cannot escape.
|
|
49
|
+
*
|
|
50
|
+
* This prevents sandbox code from reaching the host `Function` constructor
|
|
51
|
+
* via patterns like `fn.constructor.constructor('return process')()`.
|
|
52
|
+
*/
|
|
53
|
+
function neutralizeConstructor(fn) {
|
|
54
|
+
const hasPrototype = typeof fn.prototype === "object" && fn.prototype !== null;
|
|
55
|
+
function Wrapper(...args) {
|
|
56
|
+
if (hasPrototype) return new fn(...args);
|
|
57
|
+
return fn(...args);
|
|
58
|
+
}
|
|
59
|
+
if (hasPrototype) {
|
|
60
|
+
copyStaticMembers(fn, Wrapper);
|
|
61
|
+
if (Wrapper.prototype) Object.defineProperty(Wrapper.prototype, "constructor", {
|
|
62
|
+
value: void 0,
|
|
63
|
+
writable: false,
|
|
64
|
+
configurable: false
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
Object.defineProperty(Wrapper, "constructor", {
|
|
68
|
+
value: void 0,
|
|
69
|
+
writable: false,
|
|
70
|
+
configurable: false
|
|
71
|
+
});
|
|
72
|
+
return Wrapper;
|
|
73
|
+
}
|
|
16
74
|
const runCodeParams = z.object({ code: z.string().describe("JavaScript code to execute. Use console.log() for output.") });
|
|
17
75
|
/**
|
|
18
76
|
* Execute JavaScript code inside a fresh `node:vm` context.
|
|
@@ -48,34 +106,67 @@ function createRunCode() {
|
|
|
48
106
|
async function executeInIsolate(code) {
|
|
49
107
|
const output = [];
|
|
50
108
|
const capture = (...args) => output.push(args.map(String).join(" "));
|
|
109
|
+
const activeTimers = /* @__PURE__ */ new Set();
|
|
110
|
+
const sandboxSetTimeout = (fn, delay, ...args) => {
|
|
111
|
+
const id = setTimeout((...a) => {
|
|
112
|
+
activeTimers.delete(id);
|
|
113
|
+
fn(...a);
|
|
114
|
+
}, delay, ...args);
|
|
115
|
+
activeTimers.add(id);
|
|
116
|
+
return id;
|
|
117
|
+
};
|
|
118
|
+
const sandboxClearTimeout = (id) => {
|
|
119
|
+
if (id !== void 0) {
|
|
120
|
+
activeTimers.delete(id);
|
|
121
|
+
clearTimeout(id);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const sandboxSetInterval = (fn, delay, ...args) => {
|
|
125
|
+
const id = setInterval(fn, delay, ...args);
|
|
126
|
+
activeTimers.add(id);
|
|
127
|
+
return id;
|
|
128
|
+
};
|
|
129
|
+
const sandboxClearInterval = (id) => {
|
|
130
|
+
if (id !== void 0) {
|
|
131
|
+
activeTimers.delete(id);
|
|
132
|
+
clearInterval(id);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
51
135
|
const context = vm.createContext({
|
|
52
|
-
console: {
|
|
53
|
-
log: capture,
|
|
54
|
-
info: capture,
|
|
55
|
-
warn: capture,
|
|
56
|
-
error: capture,
|
|
57
|
-
debug: capture
|
|
58
|
-
},
|
|
59
|
-
setTimeout,
|
|
60
|
-
clearTimeout,
|
|
61
|
-
setInterval,
|
|
62
|
-
clearInterval,
|
|
63
|
-
URL,
|
|
64
|
-
URLSearchParams,
|
|
65
|
-
TextEncoder,
|
|
66
|
-
TextDecoder,
|
|
67
|
-
atob,
|
|
68
|
-
btoa,
|
|
69
|
-
structuredClone
|
|
70
|
-
|
|
71
|
-
|
|
136
|
+
console: Object.freeze({
|
|
137
|
+
log: neutralizeConstructor(capture),
|
|
138
|
+
info: neutralizeConstructor(capture),
|
|
139
|
+
warn: neutralizeConstructor(capture),
|
|
140
|
+
error: neutralizeConstructor(capture),
|
|
141
|
+
debug: neutralizeConstructor(capture)
|
|
142
|
+
}),
|
|
143
|
+
setTimeout: neutralizeConstructor(sandboxSetTimeout),
|
|
144
|
+
clearTimeout: neutralizeConstructor(sandboxClearTimeout),
|
|
145
|
+
setInterval: neutralizeConstructor(sandboxSetInterval),
|
|
146
|
+
clearInterval: neutralizeConstructor(sandboxClearInterval),
|
|
147
|
+
URL: neutralizeConstructor(URL),
|
|
148
|
+
URLSearchParams: neutralizeConstructor(URLSearchParams),
|
|
149
|
+
TextEncoder: neutralizeConstructor(TextEncoder),
|
|
150
|
+
TextDecoder: neutralizeConstructor(TextDecoder),
|
|
151
|
+
atob: neutralizeConstructor(atob),
|
|
152
|
+
btoa: neutralizeConstructor(btoa),
|
|
153
|
+
structuredClone: neutralizeConstructor(structuredClone)
|
|
154
|
+
}, { codeGeneration: {
|
|
155
|
+
strings: false,
|
|
156
|
+
wasm: false
|
|
157
|
+
} });
|
|
72
158
|
try {
|
|
73
159
|
const wrapped = `(async () => {\n${code}\n})()`;
|
|
74
|
-
|
|
75
|
-
await Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("Code execution timed out")), RUN_CODE_TIMEOUT_MS))]);
|
|
160
|
+
await new vm.Script(wrapped, { filename: "run_code.js" }).runInContext(context, { timeout: RUN_CODE_TIMEOUT_MS });
|
|
76
161
|
return output.join("\n").trim() || "Code ran successfully (no output)";
|
|
77
162
|
} catch (err) {
|
|
78
163
|
return { error: errorMessage(err) };
|
|
164
|
+
} finally {
|
|
165
|
+
for (const id of activeTimers) {
|
|
166
|
+
clearTimeout(id);
|
|
167
|
+
clearInterval(id);
|
|
168
|
+
}
|
|
169
|
+
activeTimers.clear();
|
|
79
170
|
}
|
|
80
171
|
}
|
|
81
172
|
//#endregion
|
|
@@ -88,10 +179,7 @@ async function executeInIsolate(code) {
|
|
|
88
179
|
* Network requests go through the host's fetch proxy (with SSRF protection).
|
|
89
180
|
*/
|
|
90
181
|
const fetchSignal = () => AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
91
|
-
|
|
92
|
-
function htmlToText(html) {
|
|
93
|
-
return html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/'/g, "'").replace(/ /g, " ").replace(/\s{2,}/g, " ").trim();
|
|
94
|
-
}
|
|
182
|
+
const htmlToText = (html) => convert(html, { wordwrap: false });
|
|
95
183
|
const webSearchParams = z.object({
|
|
96
184
|
query: z.string().describe("The search query"),
|
|
97
185
|
max_results: z.number().describe("Maximum number of results to return (default 5)").optional()
|
|
@@ -226,40 +314,46 @@ function resolveBuiltin(name, opts) {
|
|
|
226
314
|
}
|
|
227
315
|
}
|
|
228
316
|
/**
|
|
229
|
-
*
|
|
230
|
-
*
|
|
317
|
+
* Resolve all builtin tools in one pass, returning defs, schemas, and guidance.
|
|
318
|
+
* Avoids redundant calls to `resolveBuiltin` and `z.toJSONSchema`.
|
|
231
319
|
*/
|
|
232
|
-
function
|
|
320
|
+
function resolveAllBuiltins(names, opts) {
|
|
233
321
|
const defs = {};
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
322
|
+
const schemas = [];
|
|
323
|
+
const guidance = [];
|
|
324
|
+
for (const name of names) for (const [toolName, def] of resolveBuiltin(name, opts)) {
|
|
325
|
+
defs[toolName] = def;
|
|
326
|
+
schemas.push({
|
|
327
|
+
name: toolName,
|
|
328
|
+
description: def.description,
|
|
329
|
+
parameters: z.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
|
|
330
|
+
});
|
|
331
|
+
const g = def.guidance;
|
|
332
|
+
if (g) guidance.push(g);
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
defs,
|
|
336
|
+
schemas,
|
|
337
|
+
guidance
|
|
338
|
+
};
|
|
248
339
|
}
|
|
249
340
|
//#endregion
|
|
250
|
-
//#region host/runtime.ts
|
|
341
|
+
//#region host/runtime-config.ts
|
|
251
342
|
/**
|
|
252
343
|
* Runtime dependencies injected into the session pipeline.
|
|
253
344
|
*
|
|
254
345
|
* Defines the {@link Logger} interface, a default {@link consoleLogger},
|
|
255
346
|
* and the {@link S2SConfig} for Speech-to-Speech endpoint configuration.
|
|
256
347
|
*/
|
|
348
|
+
function consoleLog(fn) {
|
|
349
|
+
return (msg, ctx) => ctx ? fn(msg, ctx) : fn(msg);
|
|
350
|
+
}
|
|
257
351
|
/** Default console-backed logger. */
|
|
258
352
|
const consoleLogger = {
|
|
259
|
-
info: (
|
|
260
|
-
warn: (
|
|
261
|
-
error: (
|
|
262
|
-
debug: (
|
|
353
|
+
info: consoleLog(console.log),
|
|
354
|
+
warn: consoleLog(console.warn),
|
|
355
|
+
error: consoleLog(console.error),
|
|
356
|
+
debug: consoleLog(console.debug)
|
|
263
357
|
};
|
|
264
358
|
/**
|
|
265
359
|
* Structured JSON logger for production diagnostics. Each log entry is a
|
|
@@ -285,7 +379,7 @@ const jsonLogger = {
|
|
|
285
379
|
};
|
|
286
380
|
/** Default S2S endpoint configuration. */
|
|
287
381
|
const DEFAULT_S2S_CONFIG = {
|
|
288
|
-
wssUrl: "wss://
|
|
382
|
+
wssUrl: "wss://agents.assemblyai.com/v1/voice",
|
|
289
383
|
inputSampleRate: DEFAULT_STT_SAMPLE_RATE,
|
|
290
384
|
outputSampleRate: DEFAULT_TTS_SAMPLE_RATE
|
|
291
385
|
};
|
|
@@ -293,7 +387,6 @@ const DEFAULT_S2S_CONFIG = {
|
|
|
293
387
|
//#region host/s2s.ts
|
|
294
388
|
const uint8ToBase64 = (bytes) => Buffer.from(bytes).toString("base64");
|
|
295
389
|
const base64ToUint8 = (base64) => new Uint8Array(Buffer.from(base64, "base64"));
|
|
296
|
-
const WS_OPEN = 1;
|
|
297
390
|
const defaultCreateS2sWebSocket = (url, opts) => new WsWebSocket(url, { headers: opts.headers });
|
|
298
391
|
const S2sMessageSchema = z.discriminatedUnion("type", [
|
|
299
392
|
z.object({
|
|
@@ -303,10 +396,6 @@ const S2sMessageSchema = z.discriminatedUnion("type", [
|
|
|
303
396
|
z.object({ type: z.literal("session.updated") }).passthrough(),
|
|
304
397
|
z.object({ type: z.literal("input.speech.started") }),
|
|
305
398
|
z.object({ type: z.literal("input.speech.stopped") }),
|
|
306
|
-
z.object({
|
|
307
|
-
type: z.literal("transcript.user.delta"),
|
|
308
|
-
text: z.string()
|
|
309
|
-
}),
|
|
310
399
|
z.object({
|
|
311
400
|
type: z.literal("transcript.user"),
|
|
312
401
|
item_id: z.string(),
|
|
@@ -316,10 +405,6 @@ const S2sMessageSchema = z.discriminatedUnion("type", [
|
|
|
316
405
|
type: z.literal("reply.started"),
|
|
317
406
|
reply_id: z.string()
|
|
318
407
|
}),
|
|
319
|
-
z.object({
|
|
320
|
-
type: z.literal("transcript.agent.delta"),
|
|
321
|
-
delta: z.string()
|
|
322
|
-
}).passthrough(),
|
|
323
408
|
z.object({
|
|
324
409
|
type: z.literal("transcript.agent"),
|
|
325
410
|
text: z.string(),
|
|
@@ -327,8 +412,6 @@ const S2sMessageSchema = z.discriminatedUnion("type", [
|
|
|
327
412
|
item_id: z.string().optional().default(""),
|
|
328
413
|
interrupted: z.boolean().optional().default(false)
|
|
329
414
|
}),
|
|
330
|
-
z.object({ type: z.literal("reply.content_part.started") }).passthrough(),
|
|
331
|
-
z.object({ type: z.literal("reply.content_part.done") }).passthrough(),
|
|
332
415
|
z.object({
|
|
333
416
|
type: z.literal("tool.call"),
|
|
334
417
|
call_id: z.string(),
|
|
@@ -358,66 +441,48 @@ function dispatchS2sMessage(emitter, msg) {
|
|
|
358
441
|
case "session.ready":
|
|
359
442
|
emitter.emit("ready", { sessionId: msg.session_id });
|
|
360
443
|
break;
|
|
361
|
-
case "session.updated":
|
|
362
|
-
emitter.emit("sessionUpdated", msg);
|
|
363
|
-
break;
|
|
444
|
+
case "session.updated": break;
|
|
364
445
|
case "input.speech.started":
|
|
365
|
-
emitter.emit("
|
|
446
|
+
emitter.emit("event", { type: "speech_started" });
|
|
366
447
|
break;
|
|
367
448
|
case "input.speech.stopped":
|
|
368
|
-
emitter.emit("
|
|
369
|
-
break;
|
|
370
|
-
case "transcript.user.delta":
|
|
371
|
-
emitter.emit("userTranscriptDelta", { text: msg.text });
|
|
449
|
+
emitter.emit("event", { type: "speech_stopped" });
|
|
372
450
|
break;
|
|
373
451
|
case "transcript.user":
|
|
374
|
-
emitter.emit("
|
|
375
|
-
|
|
452
|
+
emitter.emit("event", {
|
|
453
|
+
type: "user_transcript",
|
|
376
454
|
text: msg.text
|
|
377
455
|
});
|
|
378
456
|
break;
|
|
379
457
|
case "reply.started":
|
|
380
458
|
emitter.emit("replyStarted", { replyId: msg.reply_id });
|
|
381
459
|
break;
|
|
382
|
-
case "transcript.agent.delta":
|
|
383
|
-
emitter.emit("agentTranscriptDelta", { text: msg.delta });
|
|
384
|
-
break;
|
|
385
460
|
case "transcript.agent":
|
|
386
|
-
emitter.emit("
|
|
461
|
+
emitter.emit("event", {
|
|
462
|
+
type: "agent_transcript",
|
|
387
463
|
text: msg.text,
|
|
388
|
-
|
|
389
|
-
itemId: msg.item_id,
|
|
390
|
-
interrupted: msg.interrupted
|
|
464
|
+
_interrupted: msg.interrupted
|
|
391
465
|
});
|
|
392
466
|
break;
|
|
393
467
|
case "tool.call":
|
|
394
|
-
emitter.emit("
|
|
395
|
-
|
|
396
|
-
|
|
468
|
+
emitter.emit("event", {
|
|
469
|
+
type: "tool_call",
|
|
470
|
+
toolCallId: msg.call_id,
|
|
471
|
+
toolName: msg.name,
|
|
397
472
|
args: msg.args
|
|
398
473
|
});
|
|
399
474
|
break;
|
|
400
475
|
case "reply.done":
|
|
401
|
-
emitter.emit("
|
|
476
|
+
if (msg.status === "interrupted") emitter.emit("event", { type: "cancelled" });
|
|
477
|
+
else emitter.emit("event", { type: "reply_done" });
|
|
402
478
|
break;
|
|
403
479
|
case "session.error":
|
|
404
|
-
if (msg.code === "session_not_found" || msg.code === "session_forbidden") emitter.emit("sessionExpired"
|
|
405
|
-
|
|
406
|
-
message: msg.message
|
|
407
|
-
});
|
|
408
|
-
else emitter.emit("error", {
|
|
409
|
-
code: msg.code,
|
|
410
|
-
message: msg.message
|
|
411
|
-
});
|
|
480
|
+
if (msg.code === "session_not_found" || msg.code === "session_forbidden") emitter.emit("sessionExpired");
|
|
481
|
+
else emitter.emit("error", new Error(msg.message));
|
|
412
482
|
break;
|
|
413
483
|
case "error":
|
|
414
|
-
emitter.emit("error",
|
|
415
|
-
code: "connection",
|
|
416
|
-
message: msg.message
|
|
417
|
-
});
|
|
484
|
+
emitter.emit("error", new Error(msg.message));
|
|
418
485
|
break;
|
|
419
|
-
case "reply.content_part.started":
|
|
420
|
-
case "reply.content_part.done": break;
|
|
421
486
|
default: break;
|
|
422
487
|
}
|
|
423
488
|
}
|
|
@@ -429,18 +494,19 @@ function connectS2s(opts) {
|
|
|
429
494
|
const emitter = createNanoEvents();
|
|
430
495
|
let opened = false;
|
|
431
496
|
function send(msg) {
|
|
432
|
-
if (ws.readyState !==
|
|
497
|
+
if (ws.readyState !== 1) {
|
|
433
498
|
log.debug("S2S send dropped: socket not open", { type: msg.type });
|
|
434
499
|
return;
|
|
435
500
|
}
|
|
436
501
|
const json = JSON.stringify(msg);
|
|
437
|
-
if (msg.type !== "input.audio") log.info(`S2S >> ${msg.type}`,
|
|
502
|
+
if (msg.type !== "input.audio") if (msg.type === "session.update") log.info(`S2S >> ${msg.type}`, { payload: json });
|
|
503
|
+
else log.info(`S2S >> ${msg.type}`);
|
|
438
504
|
ws.send(json);
|
|
439
505
|
}
|
|
440
506
|
const handle = {
|
|
441
507
|
on: emitter.on.bind(emitter),
|
|
442
508
|
sendAudio(audio) {
|
|
443
|
-
if (ws.readyState !==
|
|
509
|
+
if (ws.readyState !== 1) {
|
|
444
510
|
log.debug("S2S sendAudio dropped: socket not open");
|
|
445
511
|
return;
|
|
446
512
|
}
|
|
@@ -501,7 +567,7 @@ function connectS2s(opts) {
|
|
|
501
567
|
}
|
|
502
568
|
function logIncoming(obj) {
|
|
503
569
|
if (obj.type === "reply.audio" || obj.type === "input.audio") return;
|
|
504
|
-
log.info(`S2S << ${obj.type}
|
|
570
|
+
log.info(`S2S << ${obj.type}`);
|
|
505
571
|
}
|
|
506
572
|
function handleS2sMessage(ev) {
|
|
507
573
|
const raw = tryParseJson(ev.data);
|
|
@@ -522,32 +588,29 @@ function connectS2s(opts) {
|
|
|
522
588
|
}
|
|
523
589
|
ws.addEventListener("message", handleS2sMessage);
|
|
524
590
|
ws.addEventListener("close", (ev) => {
|
|
591
|
+
const code = ev.code ?? 0;
|
|
592
|
+
const reason = ev.reason ?? "";
|
|
525
593
|
log.info("S2S WebSocket closed", {
|
|
526
|
-
code
|
|
527
|
-
reason
|
|
594
|
+
code,
|
|
595
|
+
reason
|
|
528
596
|
});
|
|
529
|
-
if (!opened) reject(/* @__PURE__ */ new Error(`WebSocket closed before open (code: ${
|
|
530
|
-
emitter.emit("close");
|
|
597
|
+
if (!opened) reject(/* @__PURE__ */ new Error(`WebSocket closed before open (code: ${code})`));
|
|
598
|
+
emitter.emit("close", code, reason);
|
|
531
599
|
});
|
|
532
600
|
ws.addEventListener("error", (ev) => {
|
|
533
601
|
const message = typeof ev.message === "string" ? ev.message : "WebSocket error";
|
|
534
602
|
const errObj = new Error(message);
|
|
535
603
|
log.error("S2S WebSocket error", { error: errObj.message });
|
|
536
604
|
if (!opened) reject(errObj);
|
|
537
|
-
else emitter.emit("error",
|
|
538
|
-
code: "ws_error",
|
|
539
|
-
message: errObj.message
|
|
540
|
-
});
|
|
605
|
+
else emitter.emit("error", errObj);
|
|
541
606
|
});
|
|
542
607
|
});
|
|
543
608
|
}
|
|
544
609
|
//#endregion
|
|
545
|
-
//#region host/session.ts
|
|
610
|
+
//#region host/session-ctx.ts
|
|
546
611
|
function buildCtx(opts) {
|
|
547
|
-
const {
|
|
612
|
+
const { agentConfig, log } = opts;
|
|
548
613
|
const maxHistory = opts.maxHistory ?? 200;
|
|
549
|
-
/** Track in-flight hook promises so they can be awaited during shutdown. */
|
|
550
|
-
const pendingHooks = /* @__PURE__ */ new Set();
|
|
551
614
|
const ctx = {
|
|
552
615
|
...opts,
|
|
553
616
|
s2s: null,
|
|
@@ -559,12 +622,9 @@ function buildCtx(opts) {
|
|
|
559
622
|
turnPromise: null,
|
|
560
623
|
conversationMessages: [],
|
|
561
624
|
maxHistory,
|
|
562
|
-
|
|
563
|
-
return callResolveTurnConfig(hooks, id, HOOK_TIMEOUT_MS);
|
|
564
|
-
},
|
|
565
|
-
consumeToolCallStep(turnConfig, _name, replyId) {
|
|
625
|
+
consumeToolCallStep(_name, replyId) {
|
|
566
626
|
if (replyId === null || replyId !== ctx.reply.currentReplyId) return toolError("Reply was interrupted. Discarding stale tool call.");
|
|
567
|
-
const maxSteps =
|
|
627
|
+
const maxSteps = agentConfig.maxSteps;
|
|
568
628
|
ctx.reply.toolCallCount++;
|
|
569
629
|
if (maxSteps !== void 0 && ctx.reply.toolCallCount > maxSteps) {
|
|
570
630
|
log.info("maxSteps exceeded, refusing tool call", {
|
|
@@ -575,32 +635,9 @@ function buildCtx(opts) {
|
|
|
575
635
|
}
|
|
576
636
|
return null;
|
|
577
637
|
},
|
|
578
|
-
fireHook(name, ...args) {
|
|
579
|
-
if (!hooks) return;
|
|
580
|
-
const notifyOnError = (err) => {
|
|
581
|
-
log.warn(`${name} hook failed`, { err: errorMessage(err) });
|
|
582
|
-
if (name !== "error") {
|
|
583
|
-
const ep = hooks.callHook("error", id, { message: errorMessage(err) });
|
|
584
|
-
if (ep && typeof ep.catch === "function") ep.catch((e) => {
|
|
585
|
-
log.warn("error hook failed", { err: errorMessage(e) });
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
try {
|
|
590
|
-
const result = hooks.callHook(name, ...args);
|
|
591
|
-
if (result == null) return;
|
|
592
|
-
const p = result.catch(notifyOnError).finally(() => pendingHooks.delete(p));
|
|
593
|
-
pendingHooks.add(p);
|
|
594
|
-
} catch (err) {
|
|
595
|
-
notifyOnError(err);
|
|
596
|
-
}
|
|
597
|
-
},
|
|
598
|
-
async drainHooks() {
|
|
599
|
-
if (pendingHooks.size > 0) await Promise.all([...pendingHooks]);
|
|
600
|
-
},
|
|
601
638
|
pushMessages(...msgs) {
|
|
602
639
|
ctx.conversationMessages.push(...msgs);
|
|
603
|
-
if (maxHistory > 0 && ctx.conversationMessages.length > maxHistory) ctx.conversationMessages
|
|
640
|
+
if (maxHistory > 0 && ctx.conversationMessages.length > maxHistory) ctx.conversationMessages.splice(0, ctx.conversationMessages.length - maxHistory);
|
|
604
641
|
},
|
|
605
642
|
beginReply(replyId) {
|
|
606
643
|
ctx.reply = {
|
|
@@ -623,8 +660,15 @@ function buildCtx(opts) {
|
|
|
623
660
|
};
|
|
624
661
|
return ctx;
|
|
625
662
|
}
|
|
663
|
+
//#endregion
|
|
664
|
+
//#region host/session.ts
|
|
626
665
|
/** @internal Not part of the public API. Exposed for testing only. */
|
|
627
666
|
const _internals = { connectS2s };
|
|
667
|
+
/**
|
|
668
|
+
* Create an idle timer that closes the S2S connection after inactivity.
|
|
669
|
+
* Convention: `timeoutMs <= 0` disables the timer entirely (returns a no-op).
|
|
670
|
+
* This allows agents to opt out of idle timeout via `idleTimeoutMs: 0` in their config.
|
|
671
|
+
*/
|
|
628
672
|
function createIdleTimer(opts) {
|
|
629
673
|
if (opts.timeoutMs <= 0) return {
|
|
630
674
|
reset() {},
|
|
@@ -671,25 +715,11 @@ function finishToolCall(ctx, callId, result, replyId) {
|
|
|
671
715
|
if (ctx.maxHistory > 0 && ctx.reply.pendingTools.length > ctx.maxHistory) ctx.reply.pendingTools.shift();
|
|
672
716
|
}
|
|
673
717
|
}
|
|
674
|
-
async function handleToolCall(ctx,
|
|
675
|
-
const { callId, name, args: parsedArgs } =
|
|
718
|
+
async function handleToolCall(ctx, event) {
|
|
719
|
+
const { toolCallId: callId, toolName: name, args: parsedArgs } = event;
|
|
676
720
|
const replyId = ctx.reply.currentReplyId;
|
|
677
|
-
ctx.client.event(
|
|
678
|
-
|
|
679
|
-
toolCallId: callId,
|
|
680
|
-
toolName: name,
|
|
681
|
-
args: parsedArgs
|
|
682
|
-
});
|
|
683
|
-
let turnConfig;
|
|
684
|
-
try {
|
|
685
|
-
turnConfig = await ctx.resolveTurnConfig();
|
|
686
|
-
} catch (err) {
|
|
687
|
-
const msg = `resolveTurnConfig hook error: ${errorMessage(err)}`;
|
|
688
|
-
ctx.log.error(msg);
|
|
689
|
-
finishToolCall(ctx, callId, toolError(msg), replyId);
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
const refused = ctx.consumeToolCallStep(turnConfig, name, replyId);
|
|
721
|
+
ctx.client.event(event);
|
|
722
|
+
const refused = ctx.consumeToolCallStep(name, replyId);
|
|
693
723
|
if (refused !== null) {
|
|
694
724
|
finishToolCall(ctx, callId, refused, replyId);
|
|
695
725
|
return;
|
|
@@ -721,23 +751,17 @@ async function handleToolCall(ctx, detail) {
|
|
|
721
751
|
function handleUserTranscript(ctx, text) {
|
|
722
752
|
ctx.log.info("S2S user transcript", { text });
|
|
723
753
|
ctx.client.event({
|
|
724
|
-
type: "
|
|
725
|
-
text,
|
|
726
|
-
isFinal: true
|
|
727
|
-
});
|
|
728
|
-
ctx.client.event({
|
|
729
|
-
type: "turn",
|
|
754
|
+
type: "user_transcript",
|
|
730
755
|
text
|
|
731
756
|
});
|
|
732
757
|
ctx.pushMessages({
|
|
733
758
|
role: "user",
|
|
734
759
|
content: text
|
|
735
760
|
});
|
|
736
|
-
ctx.fireHook("turn", ctx.id, text, HOOK_TIMEOUT_MS);
|
|
737
761
|
}
|
|
738
762
|
function handleAgentTranscript(ctx, text, interrupted) {
|
|
739
763
|
ctx.client.event({
|
|
740
|
-
type: "
|
|
764
|
+
type: "agent_transcript",
|
|
741
765
|
text
|
|
742
766
|
});
|
|
743
767
|
if (!interrupted) ctx.pushMessages({
|
|
@@ -745,13 +769,12 @@ function handleAgentTranscript(ctx, text, interrupted) {
|
|
|
745
769
|
content: text
|
|
746
770
|
});
|
|
747
771
|
}
|
|
748
|
-
function
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
772
|
+
function handleReplyCancelled(ctx) {
|
|
773
|
+
ctx.log.info("S2S reply interrupted (barge-in)");
|
|
774
|
+
ctx.cancelReply();
|
|
775
|
+
ctx.client.event({ type: "cancelled" });
|
|
776
|
+
}
|
|
777
|
+
function handleReplyDone(ctx) {
|
|
755
778
|
const doneReplyId = ctx.reply.currentReplyId;
|
|
756
779
|
const sendPending = () => {
|
|
757
780
|
if (ctx.reply.currentReplyId !== doneReplyId) {
|
|
@@ -768,7 +791,7 @@ function handleReplyDone(ctx, status) {
|
|
|
768
791
|
agent: ctx.agent
|
|
769
792
|
});
|
|
770
793
|
ctx.client.playAudioDone();
|
|
771
|
-
ctx.client.event({ type: "
|
|
794
|
+
ctx.client.event({ type: "reply_done" });
|
|
772
795
|
}
|
|
773
796
|
};
|
|
774
797
|
if (ctx.turnPromise !== null) ctx.turnPromise.then(sendPending);
|
|
@@ -776,54 +799,58 @@ function handleReplyDone(ctx, status) {
|
|
|
776
799
|
}
|
|
777
800
|
function setupListeners(ctx, handle) {
|
|
778
801
|
handle.on("ready", ({ sessionId }) => ctx.log.info("S2S session ready", { sessionId }));
|
|
802
|
+
handle.on("replyStarted", ({ replyId }) => {
|
|
803
|
+
ctx.beginReply(replyId);
|
|
804
|
+
});
|
|
779
805
|
handle.on("sessionExpired", () => {
|
|
780
806
|
ctx.log.info("S2S session expired");
|
|
781
807
|
handle.close();
|
|
782
808
|
});
|
|
783
|
-
handle.on("speechStarted", () => ctx.client.event({ type: "speech_started" }));
|
|
784
|
-
handle.on("speechStopped", () => ctx.client.event({ type: "speech_stopped" }));
|
|
785
|
-
handle.on("userTranscriptDelta", ({ text }) => ctx.client.event({
|
|
786
|
-
type: "transcript",
|
|
787
|
-
text,
|
|
788
|
-
isFinal: false
|
|
789
|
-
}));
|
|
790
|
-
handle.on("userTranscript", ({ text }) => handleUserTranscript(ctx, text));
|
|
791
|
-
handle.on("replyStarted", ({ replyId }) => {
|
|
792
|
-
ctx.beginReply(replyId);
|
|
793
|
-
});
|
|
794
809
|
handle.on("audio", ({ audio }) => ctx.client.playAudioChunk(audio));
|
|
795
|
-
handle.on("
|
|
796
|
-
|
|
797
|
-
text
|
|
798
|
-
}));
|
|
799
|
-
handle.on("agentTranscript", ({ text, interrupted }) => handleAgentTranscript(ctx, text, interrupted));
|
|
800
|
-
handle.on("toolCall", (detail) => {
|
|
801
|
-
const p = handleToolCall(ctx, detail).catch((err) => {
|
|
802
|
-
ctx.log.error("Tool call handler failed", { err: errorMessage(err) });
|
|
803
|
-
});
|
|
804
|
-
ctx.chainTurn(p);
|
|
805
|
-
});
|
|
806
|
-
handle.on("replyDone", ({ status }) => handleReplyDone(ctx, status));
|
|
807
|
-
handle.on("error", ({ code, message }) => {
|
|
808
|
-
ctx.log.error("S2S error", {
|
|
809
|
-
code,
|
|
810
|
-
message
|
|
811
|
-
});
|
|
810
|
+
handle.on("error", (err) => {
|
|
811
|
+
ctx.log.error("S2S error", { message: err.message });
|
|
812
812
|
ctx.client.event({
|
|
813
813
|
type: "error",
|
|
814
814
|
code: "internal",
|
|
815
|
-
message
|
|
815
|
+
message: err.message
|
|
816
816
|
});
|
|
817
817
|
handle.close();
|
|
818
818
|
});
|
|
819
|
-
handle.on("close", () => {
|
|
820
|
-
ctx.log.info("S2S closed"
|
|
819
|
+
handle.on("close", (code, reason) => {
|
|
820
|
+
ctx.log.info("S2S closed", {
|
|
821
|
+
code,
|
|
822
|
+
reason
|
|
823
|
+
});
|
|
821
824
|
ctx.s2s = null;
|
|
822
825
|
ctx.cancelReply();
|
|
823
826
|
});
|
|
827
|
+
handle.on("event", (event) => {
|
|
828
|
+
switch (event.type) {
|
|
829
|
+
case "user_transcript":
|
|
830
|
+
handleUserTranscript(ctx, event.text);
|
|
831
|
+
break;
|
|
832
|
+
case "agent_transcript":
|
|
833
|
+
handleAgentTranscript(ctx, event.text, event._interrupted ?? false);
|
|
834
|
+
break;
|
|
835
|
+
case "tool_call": {
|
|
836
|
+
const p = handleToolCall(ctx, event).catch((err) => {
|
|
837
|
+
ctx.log.error("Tool call handler failed", { err: errorMessage(err) });
|
|
838
|
+
});
|
|
839
|
+
ctx.chainTurn(p);
|
|
840
|
+
break;
|
|
841
|
+
}
|
|
842
|
+
case "reply_done":
|
|
843
|
+
handleReplyDone(ctx);
|
|
844
|
+
break;
|
|
845
|
+
case "cancelled":
|
|
846
|
+
handleReplyCancelled(ctx);
|
|
847
|
+
break;
|
|
848
|
+
default: ctx.client.event(event);
|
|
849
|
+
}
|
|
850
|
+
});
|
|
824
851
|
}
|
|
825
852
|
function createS2sSession(opts) {
|
|
826
|
-
const { id, agent, client, toolSchemas, apiKey, s2sConfig, executeTool, createWebSocket = defaultCreateS2sWebSocket,
|
|
853
|
+
const { id, agent, client, toolSchemas, apiKey, s2sConfig, executeTool, createWebSocket = defaultCreateS2sWebSocket, logger: log = consoleLogger } = opts;
|
|
827
854
|
const agentConfig = opts.skipGreeting ? {
|
|
828
855
|
...opts.agentConfig,
|
|
829
856
|
greeting: ""
|
|
@@ -846,7 +873,6 @@ function createS2sSession(opts) {
|
|
|
846
873
|
client,
|
|
847
874
|
agentConfig,
|
|
848
875
|
executeTool,
|
|
849
|
-
hooks,
|
|
850
876
|
log,
|
|
851
877
|
maxHistory: opts.maxHistory
|
|
852
878
|
});
|
|
@@ -893,7 +919,6 @@ function createS2sSession(opts) {
|
|
|
893
919
|
}
|
|
894
920
|
return {
|
|
895
921
|
async start() {
|
|
896
|
-
ctx.fireHook("connect", id, HOOK_TIMEOUT_MS);
|
|
897
922
|
await connectAndSetup();
|
|
898
923
|
},
|
|
899
924
|
async stop() {
|
|
@@ -901,10 +926,7 @@ function createS2sSession(opts) {
|
|
|
901
926
|
sessionAbort.abort();
|
|
902
927
|
idle.clear();
|
|
903
928
|
if (ctx.turnPromise !== null) await ctx.turnPromise;
|
|
904
|
-
await ctx.drainHooks();
|
|
905
929
|
ctx.s2s?.close();
|
|
906
|
-
ctx.fireHook("disconnect", id, HOOK_TIMEOUT_MS);
|
|
907
|
-
await ctx.drainHooks();
|
|
908
930
|
},
|
|
909
931
|
onAudio(data) {
|
|
910
932
|
idle.reset();
|
|
@@ -936,33 +958,50 @@ function createS2sSession(opts) {
|
|
|
936
958
|
};
|
|
937
959
|
}
|
|
938
960
|
//#endregion
|
|
939
|
-
//#region
|
|
940
|
-
/**
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
961
|
+
//#region host/tool-executor.ts
|
|
962
|
+
/**
|
|
963
|
+
* Tool execution — validates arguments and invokes tool handlers.
|
|
964
|
+
*
|
|
965
|
+
* {@link executeToolCall} is the single entry point used by both the
|
|
966
|
+
* direct (self-hosted) runtime and the platform sandbox sidecar.
|
|
967
|
+
*/
|
|
968
|
+
const yieldTick = () => new Promise((r) => setTimeout(r, 0));
|
|
969
|
+
function buildToolContext(opts) {
|
|
970
|
+
const { env, state, kv, messages, sessionId } = opts;
|
|
971
|
+
return {
|
|
972
|
+
env,
|
|
973
|
+
state: state ?? {},
|
|
974
|
+
get kv() {
|
|
975
|
+
if (!kv) throw new Error("KV not available");
|
|
976
|
+
return kv;
|
|
977
|
+
},
|
|
978
|
+
messages: messages ?? [],
|
|
979
|
+
sessionId: sessionId ?? ""
|
|
980
|
+
};
|
|
947
981
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
982
|
+
async function executeToolCall(name, args, options) {
|
|
983
|
+
const { tool } = options;
|
|
984
|
+
const parsed = (tool.parameters ?? EMPTY_PARAMS).safeParse(args);
|
|
985
|
+
if (!parsed.success) return toolError(`Invalid arguments for tool "${name}": ${(parsed.error?.issues ?? []).map((i) => `${i.path.map(String).join(".")}: ${i.message}`).join(", ")}`);
|
|
986
|
+
try {
|
|
987
|
+
const ctx = buildToolContext(options);
|
|
988
|
+
await yieldTick();
|
|
989
|
+
const result = await pTimeout(Promise.resolve(tool.execute(parsed.data, ctx)), {
|
|
990
|
+
milliseconds: TOOL_EXECUTION_TIMEOUT_MS,
|
|
991
|
+
message: `Tool "${name}" timed out after ${TOOL_EXECUTION_TIMEOUT_MS}ms`
|
|
992
|
+
});
|
|
993
|
+
await yieldTick();
|
|
994
|
+
if (result == null) return "null";
|
|
995
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
996
|
+
} catch (err) {
|
|
997
|
+
const log = options.logger;
|
|
998
|
+
if (log) log.warn("Tool execution failed", {
|
|
999
|
+
tool: name,
|
|
1000
|
+
error: errorDetail(err)
|
|
1001
|
+
});
|
|
1002
|
+
else console.warn(`[tool-executor] Tool execution failed: ${name}`, err);
|
|
1003
|
+
return toolError(errorMessage(err));
|
|
964
1004
|
}
|
|
965
|
-
return pos <= end;
|
|
966
1005
|
}
|
|
967
1006
|
//#endregion
|
|
968
1007
|
//#region host/unstorage-kv.ts
|
|
@@ -1003,23 +1042,6 @@ function createUnstorageKv(options) {
|
|
|
1003
1042
|
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
1004
1043
|
await Promise.all(keyArray.map((k) => store.removeItem(k)));
|
|
1005
1044
|
},
|
|
1006
|
-
async list(listPrefix, listOptions) {
|
|
1007
|
-
const allKeys = await store.getKeys(listPrefix);
|
|
1008
|
-
const entries = [];
|
|
1009
|
-
for (const key of allKeys) {
|
|
1010
|
-
const value = await store.getItem(key);
|
|
1011
|
-
if (value != null) entries.push({
|
|
1012
|
-
key,
|
|
1013
|
-
value
|
|
1014
|
-
});
|
|
1015
|
-
}
|
|
1016
|
-
return sortAndPaginate(entries, listOptions);
|
|
1017
|
-
},
|
|
1018
|
-
async keys(pattern) {
|
|
1019
|
-
const allKeys = await store.getKeys();
|
|
1020
|
-
if (!pattern) return allKeys.sort((a, b) => a.localeCompare(b));
|
|
1021
|
-
return allKeys.filter((key) => matchGlob(key, pattern)).sort((a, b) => a.localeCompare(b));
|
|
1022
|
-
},
|
|
1023
1045
|
close() {
|
|
1024
1046
|
store.dispose();
|
|
1025
1047
|
}
|
|
@@ -1086,12 +1108,12 @@ function handleTextMessage(data, session, log, ctx, sid) {
|
|
|
1086
1108
|
});
|
|
1087
1109
|
return;
|
|
1088
1110
|
}
|
|
1089
|
-
const parsed = ClientMessageSchema
|
|
1090
|
-
if (!parsed.
|
|
1091
|
-
log.warn("Invalid client message", {
|
|
1111
|
+
const parsed = lenientParse(ClientMessageSchema, json);
|
|
1112
|
+
if (!parsed.ok) {
|
|
1113
|
+
if (parsed.malformed) log.warn("Invalid client message", {
|
|
1092
1114
|
...ctx,
|
|
1093
1115
|
sid,
|
|
1094
|
-
error: parsed.error
|
|
1116
|
+
error: parsed.error
|
|
1095
1117
|
});
|
|
1096
1118
|
return;
|
|
1097
1119
|
}
|
|
@@ -1183,7 +1205,7 @@ function wireSessionSocket(ws, opts) {
|
|
|
1183
1205
|
ws.addEventListener("message", (event) => {
|
|
1184
1206
|
if (!session) return;
|
|
1185
1207
|
if (!sessionReady) {
|
|
1186
|
-
messageBuffer
|
|
1208
|
+
if (messageBuffer && messageBuffer.length < 100) messageBuffer.push(event);
|
|
1187
1209
|
return;
|
|
1188
1210
|
}
|
|
1189
1211
|
const { data } = event;
|
|
@@ -1203,6 +1225,7 @@ function wireSessionSocket(ws, opts) {
|
|
|
1203
1225
|
});
|
|
1204
1226
|
}).finally(() => {
|
|
1205
1227
|
sessions.delete(sessionId);
|
|
1228
|
+
opts.onSessionEnd?.(sessionId);
|
|
1206
1229
|
});
|
|
1207
1230
|
opts.onClose?.();
|
|
1208
1231
|
});
|
|
@@ -1216,7 +1239,7 @@ function wireSessionSocket(ws, opts) {
|
|
|
1216
1239
|
});
|
|
1217
1240
|
}
|
|
1218
1241
|
//#endregion
|
|
1219
|
-
//#region host/
|
|
1242
|
+
//#region host/runtime.ts
|
|
1220
1243
|
/**
|
|
1221
1244
|
* Agent runtime — the execution engine for voice agents.
|
|
1222
1245
|
*
|
|
@@ -1224,45 +1247,6 @@ function wireSessionSocket(ws, opts) {
|
|
|
1224
1247
|
* self-hosted servers and the platform sandbox. It wires up tool execution,
|
|
1225
1248
|
* lifecycle hooks, and session management.
|
|
1226
1249
|
*/
|
|
1227
|
-
const yieldTick = () => new Promise((r) => setTimeout(r, 0));
|
|
1228
|
-
function buildToolContext(opts) {
|
|
1229
|
-
const { env, state, kv, messages, fetch: fetchFn, sessionId } = opts;
|
|
1230
|
-
return {
|
|
1231
|
-
env: { ...env },
|
|
1232
|
-
state: state ?? {},
|
|
1233
|
-
get kv() {
|
|
1234
|
-
if (!kv) throw new Error("KV not available");
|
|
1235
|
-
return kv;
|
|
1236
|
-
},
|
|
1237
|
-
messages: messages ?? [],
|
|
1238
|
-
fetch: fetchFn ?? globalThis.fetch,
|
|
1239
|
-
sessionId: sessionId ?? ""
|
|
1240
|
-
};
|
|
1241
|
-
}
|
|
1242
|
-
async function executeToolCall(name, args, options) {
|
|
1243
|
-
const { tool } = options;
|
|
1244
|
-
const parsed = (tool.parameters ?? EMPTY_PARAMS).safeParse(args);
|
|
1245
|
-
if (!parsed.success) return toolError(`Invalid arguments for tool "${name}": ${(parsed.error?.issues ?? []).map((i) => `${i.path.map(String).join(".")}: ${i.message}`).join(", ")}`);
|
|
1246
|
-
try {
|
|
1247
|
-
const ctx = buildToolContext(options);
|
|
1248
|
-
await yieldTick();
|
|
1249
|
-
const result = await pTimeout(Promise.resolve(tool.execute(parsed.data, ctx)), {
|
|
1250
|
-
milliseconds: TOOL_EXECUTION_TIMEOUT_MS,
|
|
1251
|
-
message: `Tool "${name}" timed out after ${TOOL_EXECUTION_TIMEOUT_MS}ms`
|
|
1252
|
-
});
|
|
1253
|
-
await yieldTick();
|
|
1254
|
-
if (result == null) return "null";
|
|
1255
|
-
return typeof result === "string" ? result : JSON.stringify(result);
|
|
1256
|
-
} catch (err) {
|
|
1257
|
-
const log = options.logger;
|
|
1258
|
-
if (log) log.warn("Tool execution failed", {
|
|
1259
|
-
tool: name,
|
|
1260
|
-
error: errorDetail(err)
|
|
1261
|
-
});
|
|
1262
|
-
else console.warn(`[tool-executor] Tool execution failed: ${name}`, err);
|
|
1263
|
-
return toolError(errorMessage(err));
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
1250
|
/** Create an in-memory KV store (default for self-hosted). */
|
|
1267
1251
|
function createLocalKv() {
|
|
1268
1252
|
return createUnstorageKv({ storage: createStorage() });
|
|
@@ -1285,40 +1269,43 @@ function createRuntime(opts) {
|
|
|
1285
1269
|
const sessions = /* @__PURE__ */ new Map();
|
|
1286
1270
|
const readyConfig = buildReadyConfig(s2sConfig);
|
|
1287
1271
|
let executeTool;
|
|
1288
|
-
let hooks;
|
|
1289
1272
|
let toolSchemas;
|
|
1290
1273
|
let toolGuidance = [];
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1274
|
+
const builtinFetchOpt = opts.fetch ? { fetch: opts.fetch } : void 0;
|
|
1275
|
+
if (opts.executeTool && opts.toolSchemas) {
|
|
1276
|
+
const builtinDefs = opts.builtinDefs ?? resolveAllBuiltins(agent.builtinTools ?? [], builtinFetchOpt).defs;
|
|
1277
|
+
const rpcExecuteTool = opts.executeTool;
|
|
1278
|
+
const frozenEnv = Object.freeze({ ...env });
|
|
1279
|
+
executeTool = async (name, args, sessionId, messages) => {
|
|
1280
|
+
if (builtinDefs[name]) {
|
|
1281
|
+
const tool = builtinDefs[name];
|
|
1282
|
+
return executeToolCall(name, args, {
|
|
1283
|
+
tool,
|
|
1284
|
+
env: frozenEnv,
|
|
1285
|
+
sessionId: sessionId ?? "",
|
|
1286
|
+
kv,
|
|
1287
|
+
messages,
|
|
1288
|
+
logger
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
return rpcExecuteTool(name, args, sessionId, messages);
|
|
1292
|
+
};
|
|
1294
1293
|
toolSchemas = opts.toolSchemas;
|
|
1295
1294
|
toolGuidance = opts.toolGuidance ?? [];
|
|
1296
1295
|
} else {
|
|
1296
|
+
const builtins = resolveAllBuiltins(agent.builtinTools ?? [], builtinFetchOpt);
|
|
1297
1297
|
const allTools = {
|
|
1298
|
-
...
|
|
1298
|
+
...builtins.defs,
|
|
1299
1299
|
...agent.tools
|
|
1300
1300
|
};
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
toolSchemas = [...customSchemas, ...builtinSchemas];
|
|
1304
|
-
toolGuidance = getBuiltinToolGuidance(agent.builtinTools ?? []);
|
|
1301
|
+
toolSchemas = [...agentToolsToSchemas(agent.tools ?? {}), ...builtins.schemas];
|
|
1302
|
+
toolGuidance = builtins.guidance;
|
|
1305
1303
|
const stateMap = /* @__PURE__ */ new Map();
|
|
1306
1304
|
const getState = (sid) => {
|
|
1307
1305
|
if (!stateMap.has(sid) && agent.state) stateMap.set(sid, agent.state());
|
|
1308
1306
|
return stateMap.get(sid) ?? {};
|
|
1309
1307
|
};
|
|
1310
1308
|
const frozenEnv = Object.freeze({ ...env });
|
|
1311
|
-
function makeHookContext(sessionId) {
|
|
1312
|
-
return {
|
|
1313
|
-
env: frozenEnv,
|
|
1314
|
-
state: getState(sessionId),
|
|
1315
|
-
sessionId,
|
|
1316
|
-
get kv() {
|
|
1317
|
-
return kv;
|
|
1318
|
-
},
|
|
1319
|
-
fetch: globalThis.fetch
|
|
1320
|
-
};
|
|
1321
|
-
}
|
|
1322
1309
|
executeTool = async (name, args, sessionId, messages) => {
|
|
1323
1310
|
const tool = allTools[name];
|
|
1324
1311
|
if (!tool) return toolError(`Unknown tool: ${name}`);
|
|
@@ -1329,17 +1316,9 @@ function createRuntime(opts) {
|
|
|
1329
1316
|
sessionId: sessionId ?? "",
|
|
1330
1317
|
kv,
|
|
1331
1318
|
messages,
|
|
1332
|
-
logger
|
|
1333
|
-
fetch: globalThis.fetch
|
|
1319
|
+
logger
|
|
1334
1320
|
});
|
|
1335
1321
|
};
|
|
1336
|
-
hooks = createAgentHooks({
|
|
1337
|
-
agent,
|
|
1338
|
-
makeCtx: makeHookContext
|
|
1339
|
-
});
|
|
1340
|
-
hooks.hook("disconnect", async (sessionId) => {
|
|
1341
|
-
stateMap.delete(sessionId);
|
|
1342
|
-
});
|
|
1343
1322
|
}
|
|
1344
1323
|
function createSession(sessionOpts) {
|
|
1345
1324
|
const apiKey = env.ASSEMBLYAI_API_KEY ?? "";
|
|
@@ -1354,7 +1333,6 @@ function createRuntime(opts) {
|
|
|
1354
1333
|
s2sConfig,
|
|
1355
1334
|
executeTool,
|
|
1356
1335
|
...createWebSocket ? { createWebSocket } : {},
|
|
1357
|
-
hooks,
|
|
1358
1336
|
skipGreeting: sessionOpts.skipGreeting ?? false,
|
|
1359
1337
|
logger,
|
|
1360
1338
|
...sessionOpts.resumeFrom ? { resumeFrom: sessionOpts.resumeFrom } : {}
|
|
@@ -1376,32 +1354,23 @@ function createRuntime(opts) {
|
|
|
1376
1354
|
...startOpts?.logContext ? { logContext: startOpts.logContext } : {},
|
|
1377
1355
|
...startOpts?.onOpen ? { onOpen: startOpts.onOpen } : {},
|
|
1378
1356
|
...startOpts?.onClose ? { onClose: startOpts.onClose } : {},
|
|
1357
|
+
...startOpts?.onSessionEnd ? { onSessionEnd: startOpts.onSessionEnd } : {},
|
|
1379
1358
|
...sessionStartTimeoutMs !== void 0 ? { sessionStartTimeoutMs } : {},
|
|
1380
1359
|
...resumeFrom ? { resumeFrom } : {}
|
|
1381
1360
|
});
|
|
1382
1361
|
}
|
|
1383
1362
|
async function shutdown() {
|
|
1384
1363
|
if (sessions.size === 0) return;
|
|
1385
|
-
let timer;
|
|
1386
|
-
const timeout = new Promise((resolve) => {
|
|
1387
|
-
timer = setTimeout(resolve, shutdownTimeoutMs, "timeout");
|
|
1388
|
-
});
|
|
1389
|
-
const graceful = Promise.allSettled([...sessions.values()].map((s) => s.stop())).then((results) => {
|
|
1390
|
-
for (const r of results) if (r.status === "rejected") logger.warn(`Session stop failed during shutdown: ${r.reason}`);
|
|
1391
|
-
return "done";
|
|
1392
|
-
});
|
|
1393
|
-
let outcome;
|
|
1394
1364
|
try {
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1365
|
+
const results = await pTimeout(Promise.allSettled([...sessions.values()].map((s) => s.stop())), { milliseconds: shutdownTimeoutMs });
|
|
1366
|
+
for (const r of results) if (r.status === "rejected") logger.warn(`Session stop failed during shutdown: ${r.reason}`);
|
|
1367
|
+
} catch {
|
|
1368
|
+
logger.warn(`Shutdown timeout (${shutdownTimeoutMs}ms) exceeded — force-closing ${sessions.size} remaining session(s)`);
|
|
1398
1369
|
}
|
|
1399
|
-
if (outcome === "timeout") logger.warn(`Shutdown timeout (${shutdownTimeoutMs}ms) exceeded — force-closing ${sessions.size} remaining session(s)`);
|
|
1400
1370
|
sessions.clear();
|
|
1401
1371
|
}
|
|
1402
1372
|
return {
|
|
1403
1373
|
executeTool,
|
|
1404
|
-
hooks,
|
|
1405
1374
|
toolSchemas,
|
|
1406
1375
|
createSession,
|
|
1407
1376
|
startSession,
|
|
@@ -1410,4 +1379,140 @@ function createRuntime(opts) {
|
|
|
1410
1379
|
};
|
|
1411
1380
|
}
|
|
1412
1381
|
//#endregion
|
|
1413
|
-
|
|
1382
|
+
//#region host/server.ts
|
|
1383
|
+
/**
|
|
1384
|
+
* Agent HTTP+WebSocket server.
|
|
1385
|
+
*
|
|
1386
|
+
* {@link createServer} wraps a {@link Runtime} with an HTTP + WebSocket
|
|
1387
|
+
* server using only `node:http` and `ws` (no framework dependencies).
|
|
1388
|
+
*
|
|
1389
|
+
* **Internal module** — used by `aai-cli` dev server. Not a public API.
|
|
1390
|
+
* Import via `aai/host`.
|
|
1391
|
+
*/
|
|
1392
|
+
async function serveStatic(dir, req, res) {
|
|
1393
|
+
const url = req.url?.split("?")[0] ?? "/";
|
|
1394
|
+
const filePath = path.join(dir, url === "/" ? "index.html" : url);
|
|
1395
|
+
const resolved = path.resolve(dir);
|
|
1396
|
+
if (!filePath.startsWith(resolved + path.sep) && filePath !== resolved) return false;
|
|
1397
|
+
try {
|
|
1398
|
+
const stat = await fs.promises.stat(filePath);
|
|
1399
|
+
if (!stat.isFile()) return false;
|
|
1400
|
+
const mime = lookup(path.extname(filePath).toLowerCase()) || "application/octet-stream";
|
|
1401
|
+
res.writeHead(200, {
|
|
1402
|
+
"Content-Type": mime,
|
|
1403
|
+
"Content-Length": stat.size
|
|
1404
|
+
});
|
|
1405
|
+
fs.createReadStream(filePath).pipe(res);
|
|
1406
|
+
return true;
|
|
1407
|
+
} catch {
|
|
1408
|
+
return false;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
function handleKvGet(kv, req, res) {
|
|
1412
|
+
const key = new URL(req.url ?? "/", "http://localhost").searchParams.get("key");
|
|
1413
|
+
if (!key) {
|
|
1414
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1415
|
+
res.end(JSON.stringify({ error: "Missing key query parameter" }));
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
kv.get(key).then((value) => {
|
|
1419
|
+
if (value === null) {
|
|
1420
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1421
|
+
res.end("null");
|
|
1422
|
+
} else {
|
|
1423
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1424
|
+
res.end(JSON.stringify(value));
|
|
1425
|
+
}
|
|
1426
|
+
}).catch(() => {
|
|
1427
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1428
|
+
res.end(JSON.stringify({ error: "KV error" }));
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
/**
|
|
1432
|
+
* Create an HTTP + WebSocket server for an agent.
|
|
1433
|
+
*
|
|
1434
|
+
* @internal Used by aai-cli dev server.
|
|
1435
|
+
*/
|
|
1436
|
+
function createServer(options) {
|
|
1437
|
+
const { runtime, clientHtml, clientDir, logger = consoleLogger, kv } = options;
|
|
1438
|
+
const name = options.name ?? "agent";
|
|
1439
|
+
if (clientHtml && clientDir) throw new Error("clientHtml and clientDir are mutually exclusive");
|
|
1440
|
+
const escapedName = escapeHtml(name);
|
|
1441
|
+
const defaultHtml = clientHtml ?? `<!DOCTYPE html><html><body><h1>${escapedName}</h1><p>Agent server running.</p></body></html>`;
|
|
1442
|
+
const httpServer = http.createServer((req, res) => {
|
|
1443
|
+
const url = req.url?.split("?")[0] ?? "/";
|
|
1444
|
+
const method = req.method ?? "GET";
|
|
1445
|
+
res.setHeader("Content-Security-Policy", AGENT_CSP);
|
|
1446
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
1447
|
+
res.setHeader("X-Frame-Options", "SAMEORIGIN");
|
|
1448
|
+
if (method === "GET" && url === "/health") {
|
|
1449
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1450
|
+
res.end(JSON.stringify({
|
|
1451
|
+
status: "ok",
|
|
1452
|
+
name
|
|
1453
|
+
}));
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
if (kv && method === "GET" && url === "/kv") {
|
|
1457
|
+
handleKvGet(kv, req, res);
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
handleRequest(req, res, url, method);
|
|
1461
|
+
});
|
|
1462
|
+
async function handleRequest(req, res, url, method) {
|
|
1463
|
+
if (clientDir && await serveStatic(clientDir, req, res)) return;
|
|
1464
|
+
if (method === "GET" && url === "/") {
|
|
1465
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1466
|
+
res.end(defaultHtml);
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
logger.error(`${method} ${url} 404`);
|
|
1470
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1471
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
1472
|
+
}
|
|
1473
|
+
const wss = new WebSocketServer({
|
|
1474
|
+
noServer: true,
|
|
1475
|
+
maxPayload: MAX_WS_PAYLOAD_BYTES
|
|
1476
|
+
});
|
|
1477
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
1478
|
+
const url = req.url?.split("?")[0] ?? "";
|
|
1479
|
+
if (!url.startsWith("/websocket")) return;
|
|
1480
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
1481
|
+
const startOpts = parseWsUpgradeParams(req.url ?? "");
|
|
1482
|
+
logger.info(`WS upgrade ${url}${startOpts.skipGreeting ? " (resume)" : ""}`);
|
|
1483
|
+
runtime.startSession(ws, startOpts);
|
|
1484
|
+
});
|
|
1485
|
+
});
|
|
1486
|
+
let listenPort;
|
|
1487
|
+
return {
|
|
1488
|
+
get port() {
|
|
1489
|
+
return listenPort;
|
|
1490
|
+
},
|
|
1491
|
+
async listen(port = 3e3) {
|
|
1492
|
+
await new Promise((resolve, reject) => {
|
|
1493
|
+
httpServer.on("error", reject);
|
|
1494
|
+
httpServer.listen(port, () => {
|
|
1495
|
+
const addr = httpServer.address();
|
|
1496
|
+
listenPort = typeof addr === "object" && addr ? addr.port : port;
|
|
1497
|
+
resolve();
|
|
1498
|
+
});
|
|
1499
|
+
});
|
|
1500
|
+
},
|
|
1501
|
+
async close() {
|
|
1502
|
+
try {
|
|
1503
|
+
await runtime.shutdown();
|
|
1504
|
+
} finally {
|
|
1505
|
+
try {
|
|
1506
|
+
wss.close();
|
|
1507
|
+
} finally {
|
|
1508
|
+
if (listenPort !== void 0) await new Promise((resolve, reject) => {
|
|
1509
|
+
httpServer.close((err) => err ? reject(err) : resolve());
|
|
1510
|
+
});
|
|
1511
|
+
listenPort = void 0;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
//#endregion
|
|
1518
|
+
export { DEFAULT_S2S_CONFIG, _internals, buildCtx, connectS2s, consoleLogger, createRuntime, createS2sSession, createServer, createUnstorageKv, defaultCreateS2sWebSocket, executeInIsolate, executeToolCall, jsonLogger, resolveAllBuiltins, wireSessionSocket };
|