@alexkroman1/aai 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.turbo/turbo-build.log +11 -11
  2. package/CHANGELOG.md +14 -0
  3. package/dist/_internal-types-CoDTiBd1.js +61 -0
  4. package/dist/host/_mock-ws.d.ts +0 -24
  5. package/dist/host/runtime-barrel.d.ts +0 -1
  6. package/dist/host/runtime-barrel.js +55 -5
  7. package/dist/host/runtime.d.ts +2 -0
  8. package/dist/host/tool-executor.d.ts +1 -0
  9. package/dist/host/ws-handler.d.ts +2 -0
  10. package/dist/sdk/manifest-barrel.d.ts +3 -5
  11. package/dist/sdk/manifest-barrel.js +2 -52
  12. package/dist/sdk/protocol.d.ts +8 -25
  13. package/dist/sdk/protocol.js +6 -3
  14. package/dist/sdk/types.d.ts +2 -0
  15. package/host/_mock-ws.ts +0 -50
  16. package/host/_test-utils.ts +1 -0
  17. package/host/runtime-barrel.ts +0 -1
  18. package/host/runtime.ts +13 -1
  19. package/host/session-ctx.test.ts +387 -0
  20. package/host/session-fixture-replay.test.ts +2 -10
  21. package/host/session.test.ts +19 -41
  22. package/host/tool-executor.test.ts +36 -0
  23. package/host/tool-executor.ts +4 -0
  24. package/host/ws-handler.ts +3 -0
  25. package/package.json +1 -1
  26. package/sdk/__snapshots__/exports.test.ts.snap +77 -0
  27. package/sdk/__snapshots__/schema-shapes.test.ts.snap +187 -0
  28. package/sdk/_test-matchers.test.ts +75 -0
  29. package/sdk/_test-matchers.ts +73 -0
  30. package/sdk/exports.test.ts +31 -0
  31. package/sdk/manifest-barrel.ts +13 -7
  32. package/sdk/manifest.test.ts +66 -2
  33. package/sdk/protocol-compat.test.ts +0 -6
  34. package/sdk/protocol-snapshot.test.ts +7 -5
  35. package/sdk/protocol.test.ts +107 -21
  36. package/sdk/protocol.ts +7 -15
  37. package/sdk/schema-alignment.test.ts +1 -27
  38. package/sdk/schema-shapes.test.ts +103 -0
  39. package/sdk/tsconfig.json +1 -1
  40. package/sdk/types.test.ts +56 -1
  41. package/sdk/types.ts +2 -0
  42. package/sdk/ws-upgrade.test.ts +8 -8
  43. package/tsconfig.build.json +8 -1
  44. package/tsconfig.json +1 -1
  45. package/vitest.config.ts +1 -0
  46. package/dist/system-prompt-nik_iavo.js +0 -92
@@ -1,5 +1,5 @@
1
1
 
2
- > @alexkroman1/aai@1.0.5 build /home/runner/work/agent/agent/packages/aai
2
+ > @alexkroman1/aai@1.1.0 build /home/runner/work/agent/agent/packages/aai
3
3
  > tsdown && tsc -p tsconfig.build.json
4
4
 
5
5
  ℹ tsdown v0.21.7 powered by rolldown v1.0.0-rc.12
@@ -8,13 +8,13 @@
8
8
  ℹ target: node22
9
9
  ℹ tsconfig: tsconfig.json
10
10
  ℹ Build start
11
- ℹ dist/host/runtime-barrel.js 47.10 kB │ gzip: 14.49 kB
12
- ℹ dist/sdk/protocol.js  4.83 kB │ gzip: 1.77 kB
13
- ℹ dist/index.js  2.49 kB │ gzip: 1.04 kB
14
- ℹ dist/sdk/manifest-barrel.js  2.19 kB │ gzip: 0.99 kB
15
- ℹ dist/system-prompt-nik_iavo.js  4.77 kB │ gzip: 2.09 kB
16
- ℹ dist/constants-VTFoymJ-.js  2.75 kB │ gzip: 1.23 kB
17
- ℹ dist/types-Cfx_4QDK.js  1.74 kB │ gzip: 0.93 kB
18
- ℹ dist/ws-upgrade-BeOQ7fXL.js  1.14 kB │ gzip: 0.54 kB
19
- ℹ 8 files, total: 67.02 kB
20
- ✔ Build complete in 40ms
11
+ ℹ dist/host/runtime-barrel.js 50.02 kB │ gzip: 15.70 kB
12
+ ℹ dist/sdk/protocol.js  4.75 kB │ gzip: 1.76 kB
13
+ ℹ dist/index.js  2.49 kB │ gzip: 1.04 kB
14
+ ℹ dist/sdk/manifest-barrel.js  0.26 kB │ gzip: 0.17 kB
15
+ ℹ dist/constants-VTFoymJ-.js  2.75 kB │ gzip: 1.23 kB
16
+ ℹ dist/_internal-types-CoDTiBd1.js  2.33 kB │ gzip: 0.99 kB
17
+ ℹ dist/types-Cfx_4QDK.js  1.74 kB │ gzip: 0.93 kB
18
+ ℹ dist/ws-upgrade-BeOQ7fXL.js  1.14 kB │ gzip: 0.54 kB
19
+ ℹ 8 files, total: 65.47 kB
20
+ ✔ Build complete in 51ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @alexkroman1/aai
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5cda7c5: Add ctx.send for real-time tool-to-client events
8
+
9
+ Tools can now push arbitrary events to the browser client via `ctx.send(event, data)`. Events flow over the existing WebSocket as `custom_event` messages. The new `useEvent` React hook subscribes to named events. Migrated solo-rpg, pizza-ordering, dispatch-center, and night-owl templates from `useToolResult` to `ctx.send` + `useEvent`.
10
+
11
+ ### Patch Changes
12
+
13
+ - 41fab1a: Remove dead code: unused exports, wrappers, and test hooks
14
+
15
+ ## 1.0.6
16
+
3
17
  ## 1.0.5
4
18
 
5
19
  ## 1.0.4
@@ -0,0 +1,61 @@
1
+ import { i as ToolChoiceSchema, t as BuiltinToolSchema } from "./types-Cfx_4QDK.js";
2
+ import { z } from "zod";
3
+ //#region sdk/_internal-types.ts
4
+ /**
5
+ * Zod schema for serializable agent configuration sent over the wire.
6
+ *
7
+ * This is the JSON-safe subset of the agent definition that can be
8
+ * transmitted between the worker and the host process via structured clone.
9
+ */
10
+ const AgentConfigSchema = z.object({
11
+ name: z.string().min(1),
12
+ systemPrompt: z.string(),
13
+ greeting: z.string(),
14
+ sttPrompt: z.string().optional(),
15
+ maxSteps: z.number().int().positive().optional(),
16
+ toolChoice: ToolChoiceSchema.optional(),
17
+ builtinTools: z.array(BuiltinToolSchema).readonly().optional(),
18
+ idleTimeoutMs: z.number().nonnegative().optional()
19
+ });
20
+ /** Extract the serializable {@link AgentConfig} subset from a source object. */
21
+ function toAgentConfig(src) {
22
+ const config = {
23
+ name: src.name,
24
+ systemPrompt: src.systemPrompt,
25
+ greeting: src.greeting
26
+ };
27
+ if (src.sttPrompt !== void 0) config.sttPrompt = src.sttPrompt;
28
+ if (src.maxSteps !== void 0) config.maxSteps = src.maxSteps;
29
+ if (src.toolChoice !== void 0) config.toolChoice = src.toolChoice;
30
+ if (src.builtinTools) config.builtinTools = [...src.builtinTools];
31
+ if (src.idleTimeoutMs !== void 0) config.idleTimeoutMs = src.idleTimeoutMs;
32
+ return config;
33
+ }
34
+ /**
35
+ * Zod schema for serialized tool definitions sent over the wire.
36
+ *
37
+ * `parameters` must be a valid JSON Schema object (with `type`, `properties`,
38
+ * etc.) — the Vercel AI SDK wraps it via `jsonSchema()`.
39
+ */
40
+ const ToolSchemaSchema = z.object({
41
+ name: z.string().min(1),
42
+ description: z.string().min(1),
43
+ parameters: z.record(z.string(), z.unknown())
44
+ });
45
+ /** Empty Zod object schema used as default when tools have no parameters. */
46
+ const EMPTY_PARAMS = z.object({});
47
+ /**
48
+ * Convert agent tool definitions to JSON Schema format for wire transport.
49
+ *
50
+ * Transforms the Zod-based `parameters` of each tool into a plain JSON Schema
51
+ * object suitable for structured clone / JSON serialization.
52
+ */
53
+ function agentToolsToSchemas(tools) {
54
+ return Object.entries(tools).map(([name, def]) => ({
55
+ name,
56
+ description: def.description,
57
+ parameters: z.toJSONSchema(def.parameters ?? EMPTY_PARAMS)
58
+ }));
59
+ }
60
+ //#endregion
61
+ export { toAgentConfig as a, agentToolsToSchemas as i, EMPTY_PARAMS as n, ToolSchemaSchema as r, AgentConfigSchema as t };
@@ -89,27 +89,3 @@ export declare class MockWebSocket extends EventTarget {
89
89
  */
90
90
  sentJson(): Record<string, unknown>[];
91
91
  }
92
- /**
93
- * Replace `globalThis.WebSocket` with {@link MockWebSocket} for testing.
94
- *
95
- * Returns a handle that tracks all created mock sockets and can restore the
96
- * original `WebSocket` constructor. Supports the `using` declaration via
97
- * `Symbol.dispose` for automatic cleanup.
98
- *
99
- * @returns An object with `created` array, `lastWs` getter, `restore()`, and `[Symbol.dispose]()`.
100
- *
101
- * @example
102
- * ```ts
103
- * using mock = installMockWebSocket();
104
- * const session = new Session("wss://example.com");
105
- * const ws = mock.lastWs!;
106
- * ws.simulateMessage(JSON.stringify({ type: "ready" }));
107
- * // mock automatically restores WebSocket when disposed
108
- * ```
109
- */
110
- export declare function installMockWebSocket(): {
111
- restore: () => void;
112
- created: MockWebSocket[];
113
- get lastWs(): MockWebSocket | null;
114
- [Symbol.dispose]: () => void;
115
- };
@@ -6,7 +6,6 @@
6
6
  export * from "./builtin-tools.ts";
7
7
  export * from "./runtime.ts";
8
8
  export * from "./runtime-config.ts";
9
- export * from "./s2s.ts";
10
9
  export * from "./server.ts";
11
10
  export * from "./session.ts";
12
11
  export * from "./session-ctx.ts";
@@ -1,7 +1,8 @@
1
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 { r as DEFAULT_SYSTEM_PROMPT } from "../types-Cfx_4QDK.js";
2
3
  import { i as toolError, n as errorDetail, r as errorMessage, t as parseWsUpgradeParams } from "../ws-upgrade-BeOQ7fXL.js";
3
4
  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
+ import { a as toAgentConfig, i as agentToolsToSchemas, n as EMPTY_PARAMS } from "../_internal-types-CoDTiBd1.js";
5
6
  import { z } from "zod";
6
7
  import { convert } from "html-to-text";
7
8
  import vm from "node:vm";
@@ -384,6 +385,37 @@ const DEFAULT_S2S_CONFIG = {
384
385
  outputSampleRate: DEFAULT_TTS_SAMPLE_RATE
385
386
  };
386
387
  //#endregion
388
+ //#region sdk/system-prompt.ts
389
+ function getFormattedDate() {
390
+ return (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
391
+ weekday: "long",
392
+ year: "numeric",
393
+ month: "long",
394
+ day: "numeric"
395
+ });
396
+ }
397
+ const VOICE_RULES = "\n\nCRITICAL OUTPUT RULES — 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 (-, *, •) 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 — 1 to 3 sentences max";
398
+ /**
399
+ * Build the system prompt sent to the LLM from the agent configuration.
400
+ *
401
+ * Assembles the default system prompt, today's date, agent-specific instructions,
402
+ * and optional sections for tool usage preamble and voice output rules.
403
+ *
404
+ * @param config - The serializable agent configuration (name, systemPrompt, etc.).
405
+ * @param opts.hasTools - When `true`, appends a preamble instructing the LLM to
406
+ * speak a brief phrase before each tool call to fill silence.
407
+ * @param opts.voice - When `true`, appends strict voice-specific output rules
408
+ * (no markdown, no bullet points, conversational tone, concise responses).
409
+ * @returns The assembled system prompt string.
410
+ */
411
+ function buildSystemPrompt(config, opts) {
412
+ const { hasTools } = opts;
413
+ const agentInstructions = config.systemPrompt && config.systemPrompt !== DEFAULT_SYSTEM_PROMPT ? `\n\nAgent-Specific Instructions:\n${config.systemPrompt}` : "";
414
+ const toolPreamble = hasTools ? "\n\nWhen you decide to use a tool, ALWAYS say a brief natural phrase BEFORE the tool call (e.g. \"Let me look that up\" or \"One moment while I check\"). This fills silence while the tool executes. Keep preambles to one short sentence." : "";
415
+ const guidance = opts.toolGuidance && opts.toolGuidance.length > 0 ? `\n\nBuilt-in Tool Usage:\n${opts.toolGuidance.join("\n")}` : "";
416
+ return DEFAULT_SYSTEM_PROMPT + `\n\nToday's date is ${getFormattedDate()}.` + agentInstructions + toolPreamble + guidance + (opts.voice ? VOICE_RULES : "");
417
+ }
418
+ //#endregion
387
419
  //#region host/s2s.ts
388
420
  const uint8ToBase64 = (bytes) => Buffer.from(bytes).toString("base64");
389
421
  const base64ToUint8 = (base64) => new Uint8Array(Buffer.from(base64, "base64"));
@@ -976,7 +1008,10 @@ function buildToolContext(opts) {
976
1008
  return kv;
977
1009
  },
978
1010
  messages: messages ?? [],
979
- sessionId: sessionId ?? ""
1011
+ sessionId: sessionId ?? "",
1012
+ send(event, data) {
1013
+ opts.send?.(event, data);
1014
+ }
980
1015
  };
981
1016
  }
982
1017
  async function executeToolCall(name, args, options) {
@@ -1173,6 +1208,7 @@ function wireSessionSocket(ws, opts) {
1173
1208
  const client = createClientSink(ws, log);
1174
1209
  session = opts.createSession(sessionId, client);
1175
1210
  sessions.set(sessionId, session);
1211
+ opts.onSinkCreated?.(sessionId, client);
1176
1212
  ws.send(JSON.stringify({
1177
1213
  type: "config",
1178
1214
  ...opts.readyConfig,
@@ -1267,6 +1303,7 @@ function createRuntime(opts) {
1267
1303
  const { agent, env, kv = createLocalKv(), createWebSocket, logger = consoleLogger, s2sConfig = DEFAULT_S2S_CONFIG, sessionStartTimeoutMs, shutdownTimeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS } = opts;
1268
1304
  const agentConfig = toAgentConfig(agent);
1269
1305
  const sessions = /* @__PURE__ */ new Map();
1306
+ const sinkMap = /* @__PURE__ */ new Map();
1270
1307
  const readyConfig = buildReadyConfig(s2sConfig);
1271
1308
  let executeTool;
1272
1309
  let toolSchemas;
@@ -1309,6 +1346,7 @@ function createRuntime(opts) {
1309
1346
  executeTool = async (name, args, sessionId, messages) => {
1310
1347
  const tool = allTools[name];
1311
1348
  if (!tool) return toolError(`Unknown tool: ${name}`);
1349
+ const sink = sinkMap.get(sessionId ?? "");
1312
1350
  return executeToolCall(name, args, {
1313
1351
  tool,
1314
1352
  env: frozenEnv,
@@ -1316,11 +1354,17 @@ function createRuntime(opts) {
1316
1354
  sessionId: sessionId ?? "",
1317
1355
  kv,
1318
1356
  messages,
1319
- logger
1357
+ logger,
1358
+ send: sink ? (event, data) => sink.event({
1359
+ type: "custom_event",
1360
+ event,
1361
+ data
1362
+ }) : void 0
1320
1363
  });
1321
1364
  };
1322
1365
  }
1323
1366
  function createSession(sessionOpts) {
1367
+ sinkMap.set(sessionOpts.id, sessionOpts.client);
1324
1368
  const apiKey = env.ASSEMBLYAI_API_KEY ?? "";
1325
1369
  return createS2sSession({
1326
1370
  id: sessionOpts.id,
@@ -1340,6 +1384,7 @@ function createRuntime(opts) {
1340
1384
  }
1341
1385
  function startSession(ws, startOpts) {
1342
1386
  const resumeFrom = startOpts?.resumeFrom;
1387
+ const userOnSessionEnd = startOpts?.onSessionEnd;
1343
1388
  wireSessionSocket(ws, {
1344
1389
  sessions,
1345
1390
  createSession: (sid, client) => createSession({
@@ -1354,7 +1399,11 @@ function createRuntime(opts) {
1354
1399
  ...startOpts?.logContext ? { logContext: startOpts.logContext } : {},
1355
1400
  ...startOpts?.onOpen ? { onOpen: startOpts.onOpen } : {},
1356
1401
  ...startOpts?.onClose ? { onClose: startOpts.onClose } : {},
1357
- ...startOpts?.onSessionEnd ? { onSessionEnd: startOpts.onSessionEnd } : {},
1402
+ ...startOpts?.onSinkCreated ? { onSinkCreated: startOpts.onSinkCreated } : {},
1403
+ onSessionEnd: (sid) => {
1404
+ sinkMap.delete(sid);
1405
+ userOnSessionEnd?.(sid);
1406
+ },
1358
1407
  ...sessionStartTimeoutMs !== void 0 ? { sessionStartTimeoutMs } : {},
1359
1408
  ...resumeFrom ? { resumeFrom } : {}
1360
1409
  });
@@ -1368,6 +1417,7 @@ function createRuntime(opts) {
1368
1417
  logger.warn(`Shutdown timeout (${shutdownTimeoutMs}ms) exceeded — force-closing ${sessions.size} remaining session(s)`);
1369
1418
  }
1370
1419
  sessions.clear();
1420
+ sinkMap.clear();
1371
1421
  }
1372
1422
  return {
1373
1423
  executeTool,
@@ -1515,4 +1565,4 @@ function createServer(options) {
1515
1565
  };
1516
1566
  }
1517
1567
  //#endregion
1518
- export { DEFAULT_S2S_CONFIG, _internals, buildCtx, connectS2s, consoleLogger, createRuntime, createS2sSession, createServer, createUnstorageKv, defaultCreateS2sWebSocket, executeInIsolate, executeToolCall, jsonLogger, resolveAllBuiltins, wireSessionSocket };
1568
+ export { DEFAULT_S2S_CONFIG, _internals, buildCtx, consoleLogger, createRuntime, createS2sSession, createServer, createUnstorageKv, executeInIsolate, executeToolCall, jsonLogger, resolveAllBuiltins, wireSessionSocket };
@@ -24,6 +24,8 @@ export type SessionStartOptions = {
24
24
  onClose?: () => void;
25
25
  /** Called with session ID after session cleanup, for guest state cleanup. */
26
26
  onSessionEnd?: (sessionId: string) => void;
27
+ /** Called with session ID and client sink after session setup. Used by sandbox to route custom events. */
28
+ onSinkCreated?: (sessionId: string, sink: ClientSink) => void;
27
29
  };
28
30
  /**
29
31
  * Common interface for agent runtimes.
@@ -16,5 +16,6 @@ export type ExecuteToolCallOptions = {
16
16
  kv?: Kv | undefined;
17
17
  messages?: readonly Message[] | undefined;
18
18
  logger?: Logger | undefined;
19
+ send?: ((event: string, data: unknown) => void) | undefined;
19
20
  };
20
21
  export declare function executeToolCall(name: string, args: Readonly<Record<string, unknown>>, options: ExecuteToolCallOptions): Promise<string>;
@@ -38,6 +38,8 @@ export type WsSessionOptions = {
38
38
  onClose?: () => void;
39
39
  /** Callback invoked with the session ID after session cleanup. */
40
40
  onSessionEnd?: (sessionId: string) => void;
41
+ /** Callback invoked with the session ID and client sink after session setup. */
42
+ onSinkCreated?: (sessionId: string, sink: ClientSink) => void;
41
43
  /** Logger instance. Defaults to console. */
42
44
  logger?: Logger;
43
45
  /** Timeout in ms for session.start(). Defaults to 10 000 (10s). */
@@ -1,8 +1,6 @@
1
1
  /**
2
- * Manifest barrel — agent manifest parsing and tool schema conversion.
2
+ * Manifest barrel — agent config conversion and tool schema handling.
3
3
  *
4
- * Used by aai-cli (scanner, bundler) and aai-server (tests).
4
+ * Used by aai-cli (bundler) and aai-server (rpc-schemas).
5
5
  */
6
- export * from "./_internal-types.ts";
7
- export * from "./manifest.ts";
8
- export * from "./system-prompt.ts";
6
+ export { type AgentConfig, AgentConfigSchema, type AgentConfigSource, agentToolsToSchemas, EMPTY_PARAMS, type ExecuteTool, type ToolSchema, ToolSchemaSchema, toAgentConfig, } from "./_internal-types.ts";
@@ -1,52 +1,2 @@
1
- import { r as DEFAULT_SYSTEM_PROMPT, t as BuiltinToolSchema } from "../types-Cfx_4QDK.js";
2
- import { a as agentToolsToSchemas, i as ToolSchemaSchema, n as AgentConfigSchema, o as toAgentConfig, r as EMPTY_PARAMS, t as buildSystemPrompt } from "../system-prompt-nik_iavo.js";
3
- import { z } from "zod";
4
- //#region sdk/manifest.ts
5
- /**
6
- * Canonical manifest format for directory-based agents.
7
- *
8
- * Flows from build → host → sdk. Validated via Zod at the boundary,
9
- * then used as a plain typed object throughout the runtime.
10
- */
11
- const ToolManifestSchema = z.object({
12
- description: z.string(),
13
- parameters: z.record(z.string(), z.unknown()).optional()
14
- });
15
- const ManifestSchema = z.object({
16
- name: z.string().min(1),
17
- systemPrompt: z.string().optional(),
18
- greeting: z.string().optional(),
19
- sttPrompt: z.string().optional(),
20
- builtinTools: z.array(BuiltinToolSchema).optional(),
21
- maxSteps: z.number().int().positive().optional(),
22
- toolChoice: z.enum(["auto", "required"]).optional(),
23
- idleTimeoutMs: z.number().int().positive().optional(),
24
- theme: z.record(z.string(), z.string()).optional(),
25
- tools: z.record(z.string(), ToolManifestSchema).optional()
26
- });
27
- /**
28
- * Parse and normalize a raw agent manifest, applying defaults for all
29
- * optional fields. Input is typically the JSON from a bundled agent.ts.
30
- *
31
- * Key defaults:
32
- * - `maxSteps`: 5 — prevents runaway tool-call loops in a single reply
33
- * - `toolChoice`: "auto" — LLM decides when to use tools vs respond directly
34
- * - `builtinTools`: [] — no built-in tools unless explicitly opted in
35
- */
36
- function parseManifest(input) {
37
- const parsed = ManifestSchema.parse(input);
38
- return {
39
- name: parsed.name,
40
- systemPrompt: parsed.systemPrompt ?? DEFAULT_SYSTEM_PROMPT,
41
- greeting: parsed.greeting ?? "Hey there. I'm a voice assistant. What can I help you with?",
42
- sttPrompt: parsed.sttPrompt,
43
- builtinTools: parsed.builtinTools ?? [],
44
- maxSteps: parsed.maxSteps ?? 5,
45
- toolChoice: parsed.toolChoice ?? "auto",
46
- idleTimeoutMs: parsed.idleTimeoutMs,
47
- theme: parsed.theme,
48
- tools: parsed.tools ?? {}
49
- };
50
- }
51
- //#endregion
52
- export { AgentConfigSchema, EMPTY_PARAMS, ToolSchemaSchema, agentToolsToSchemas, buildSystemPrompt, parseManifest, toAgentConfig };
1
+ import { a as toAgentConfig, i as agentToolsToSchemas, n as EMPTY_PARAMS, r as ToolSchemaSchema, t as AgentConfigSchema } from "../_internal-types-CoDTiBd1.js";
2
+ export { AgentConfigSchema, EMPTY_PARAMS, ToolSchemaSchema, agentToolsToSchemas, toAgentConfig };
@@ -4,23 +4,6 @@
4
4
  * Note: this module is for internal use only and should not be used directly.
5
5
  */
6
6
  import { z } from "zod";
7
- /**
8
- * Audio codec identifier used in the wire protocol.
9
- *
10
- * All audio frames are 16-bit signed PCM, little-endian, mono.
11
- */
12
- export declare const AUDIO_FORMAT = "pcm16";
13
- /**
14
- * Minimal envelope schema for two-phase message parsing.
15
- *
16
- * When a strict schema (ServerMessageSchema / ClientMessageSchema) rejects a
17
- * message, this schema determines whether the message is a valid but
18
- * *unrecognised* type (safe to ignore during rolling upgrades) or genuinely
19
- * malformed (should be warned about).
20
- */
21
- export declare const MessageEnvelopeSchema: z.ZodObject<{
22
- type: z.ZodString;
23
- }, z.core.$loose>;
24
7
  /**
25
8
  * Two-phase message parse: tries the strict schema first, then falls back to
26
9
  * the envelope to distinguish unknown-but-valid types (safe to ignore during
@@ -134,6 +117,10 @@ export declare const ClientEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
134
117
  internal: "internal";
135
118
  }>;
136
119
  message: z.ZodString;
120
+ }, z.core.$strip>, z.ZodObject<{
121
+ type: z.ZodLiteral<"custom_event">;
122
+ event: z.ZodString;
123
+ data: z.ZodUnknown;
137
124
  }, z.core.$strip>], "type">;
138
125
  /** Discriminated union of all server→client session events. */
139
126
  export type ClientEvent = z.infer<typeof ClientEventSchema>;
@@ -152,8 +139,6 @@ export interface ClientSink {
152
139
  /** Signal that TTS audio is complete. */
153
140
  playAudioDone(): void;
154
141
  }
155
- /** Supported audio formats for the wire protocol. */
156
- export type AudioFormatId = "pcm16";
157
142
  /** Zod schema for {@link ReadyConfig}. */
158
143
  export declare const ReadyConfigSchema: z.ZodObject<{
159
144
  audioFormat: z.ZodEnum<{
@@ -214,6 +199,10 @@ export declare const ServerMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
214
199
  internal: "internal";
215
200
  }>;
216
201
  message: z.ZodString;
202
+ }, z.core.$strip>, z.ZodObject<{
203
+ type: z.ZodLiteral<"custom_event">;
204
+ event: z.ZodString;
205
+ data: z.ZodUnknown;
217
206
  }, z.core.$strip>], "type">;
218
207
  /** Server→client text messages (binary frames carry raw PCM16 audio). */
219
208
  export type ServerMessage = z.infer<typeof ServerMessageSchema>;
@@ -241,9 +230,3 @@ export declare function buildReadyConfig(s2sConfig: {
241
230
  inputSampleRate: number;
242
231
  outputSampleRate: number;
243
232
  }): ReadyConfig;
244
- /** Zod schema for {@link TurnConfig}. */
245
- export declare const TurnConfigSchema: z.ZodObject<{
246
- maxSteps: z.ZodOptional<z.ZodNumber>;
247
- }, z.core.$strip>;
248
- /** Combined turn configuration resolved from the worker before a turn starts. */
249
- export type TurnConfig = z.infer<typeof TurnConfigSchema>;
@@ -116,6 +116,11 @@ const ClientEventSchema = z.discriminatedUnion("type", [
116
116
  type: z.literal("error"),
117
117
  code: SessionErrorCodeSchema,
118
118
  message: z.string()
119
+ }),
120
+ z.object({
121
+ type: z.literal("custom_event"),
122
+ event: z.string().min(1),
123
+ data: z.unknown()
119
124
  })
120
125
  ]);
121
126
  /** Zod schema for {@link ReadyConfig}. */
@@ -157,7 +162,5 @@ function buildReadyConfig(s2sConfig) {
157
162
  ttsSampleRate: s2sConfig.outputSampleRate
158
163
  };
159
164
  }
160
- /** Zod schema for {@link TurnConfig}. */
161
- const TurnConfigSchema = z.object({ maxSteps: z.number().int().positive().optional() });
162
165
  //#endregion
163
- export { AUDIO_FORMAT, ClientEventSchema, ClientMessageSchema, KvDelSchema, KvGetSchema, KvRequestSchema, KvSetSchema, MessageEnvelopeSchema, ReadyConfigSchema, ServerMessageSchema, SessionErrorCodeSchema, TurnConfigSchema, buildReadyConfig, lenientParse };
166
+ export { ClientEventSchema, ClientMessageSchema, KvDelSchema, KvGetSchema, KvRequestSchema, KvSetSchema, ReadyConfigSchema, ServerMessageSchema, SessionErrorCodeSchema, buildReadyConfig, lenientParse };
@@ -77,6 +77,8 @@ export type ToolContext<S = Record<string, unknown>> = {
77
77
  messages: readonly Message[];
78
78
  /** Unique identifier for the current session. Useful for correlating logs across concurrent sessions. */
79
79
  sessionId: string;
80
+ /** Push a custom event to the connected browser client. Fire-and-forget. */
81
+ send(event: string, data: unknown): void;
80
82
  };
81
83
  /**
82
84
  * Definition of a custom tool that the agent can invoke.
package/host/_mock-ws.ts CHANGED
@@ -133,53 +133,3 @@ export class MockWebSocket extends EventTarget {
133
133
  return this.sent.filter((d): d is string => typeof d === "string").map((s) => JSON.parse(s));
134
134
  }
135
135
  }
136
-
137
- const g: { WebSocket: unknown } = globalThis;
138
-
139
- /**
140
- * Replace `globalThis.WebSocket` with {@link MockWebSocket} for testing.
141
- *
142
- * Returns a handle that tracks all created mock sockets and can restore the
143
- * original `WebSocket` constructor. Supports the `using` declaration via
144
- * `Symbol.dispose` for automatic cleanup.
145
- *
146
- * @returns An object with `created` array, `lastWs` getter, `restore()`, and `[Symbol.dispose]()`.
147
- *
148
- * @example
149
- * ```ts
150
- * using mock = installMockWebSocket();
151
- * const session = new Session("wss://example.com");
152
- * const ws = mock.lastWs!;
153
- * ws.simulateMessage(JSON.stringify({ type: "ready" }));
154
- * // mock automatically restores WebSocket when disposed
155
- * ```
156
- */
157
- export function installMockWebSocket(): {
158
- restore: () => void;
159
- created: MockWebSocket[];
160
- get lastWs(): MockWebSocket | null;
161
- [Symbol.dispose]: () => void;
162
- } {
163
- const saved = globalThis.WebSocket;
164
- const created: MockWebSocket[] = [];
165
-
166
- g.WebSocket = class extends MockWebSocket {
167
- constructor(url: string | URL, protocols?: string | string[] | Record<string, unknown>) {
168
- super(url, protocols);
169
- created.push(this);
170
- }
171
- };
172
-
173
- return {
174
- created,
175
- get lastWs() {
176
- return created.at(-1) ?? null;
177
- },
178
- restore() {
179
- globalThis.WebSocket = saved;
180
- },
181
- [Symbol.dispose]() {
182
- this.restore();
183
- },
184
- };
185
- }
@@ -25,6 +25,7 @@ export function createMockToolContext(overrides?: Partial<ToolContext>): ToolCon
25
25
  kv: {} as never,
26
26
  messages: [],
27
27
  sessionId: "test-session",
28
+ send: vi.fn(),
28
29
  ...overrides,
29
30
  };
30
31
  }
@@ -16,7 +16,6 @@
16
16
  export * from "./builtin-tools.ts";
17
17
  export * from "./runtime.ts";
18
18
  export * from "./runtime-config.ts";
19
- export * from "./s2s.ts";
20
19
  export * from "./server.ts";
21
20
  export * from "./session.ts";
22
21
  export * from "./session-ctx.ts";
package/host/runtime.ts CHANGED
@@ -36,6 +36,8 @@ export type SessionStartOptions = {
36
36
  onClose?: () => void;
37
37
  /** Called with session ID after session cleanup, for guest state cleanup. */
38
38
  onSessionEnd?: (sessionId: string) => void;
39
+ /** Called with session ID and client sink after session setup. Used by sandbox to route custom events. */
40
+ onSinkCreated?: (sessionId: string, sink: ClientSink) => void;
39
41
  };
40
42
 
41
43
  /**
@@ -160,6 +162,7 @@ export function createRuntime(opts: RuntimeOptions): Runtime {
160
162
  } = opts;
161
163
  const agentConfig = toAgentConfig(agent);
162
164
  const sessions = new Map<string, Session>();
165
+ const sinkMap = new Map<string, ClientSink>();
163
166
  const readyConfig: ReadyConfig = buildReadyConfig(s2sConfig);
164
167
 
165
168
  // When overrides are provided (sandbox mode), skip in-process tool setup
@@ -216,6 +219,7 @@ export function createRuntime(opts: RuntimeOptions): Runtime {
216
219
  executeTool = async (name, args, sessionId, messages) => {
217
220
  const tool = allTools[name];
218
221
  if (!tool) return toolError(`Unknown tool: ${name}`);
222
+ const sink = sinkMap.get(sessionId ?? "");
219
223
  return executeToolCall(name, args, {
220
224
  tool,
221
225
  env: frozenEnv,
@@ -224,6 +228,7 @@ export function createRuntime(opts: RuntimeOptions): Runtime {
224
228
  kv,
225
229
  messages,
226
230
  logger,
231
+ send: sink ? (event, data) => sink.event({ type: "custom_event", event, data }) : undefined,
227
232
  });
228
233
  };
229
234
  }
@@ -235,6 +240,7 @@ export function createRuntime(opts: RuntimeOptions): Runtime {
235
240
  skipGreeting?: boolean;
236
241
  resumeFrom?: string;
237
242
  }): Session {
243
+ sinkMap.set(sessionOpts.id, sessionOpts.client);
238
244
  const apiKey = env.ASSEMBLYAI_API_KEY ?? "";
239
245
  return createS2sSession({
240
246
  id: sessionOpts.id,
@@ -257,6 +263,7 @@ export function createRuntime(opts: RuntimeOptions): Runtime {
257
263
 
258
264
  function startSession(ws: SessionWebSocket, startOpts?: SessionStartOptions): void {
259
265
  const resumeFrom = startOpts?.resumeFrom;
266
+ const userOnSessionEnd = startOpts?.onSessionEnd;
260
267
  wireSessionSocket(ws, {
261
268
  sessions,
262
269
  createSession: (sid, client) =>
@@ -272,7 +279,11 @@ export function createRuntime(opts: RuntimeOptions): Runtime {
272
279
  ...(startOpts?.logContext ? { logContext: startOpts.logContext } : {}),
273
280
  ...(startOpts?.onOpen ? { onOpen: startOpts.onOpen } : {}),
274
281
  ...(startOpts?.onClose ? { onClose: startOpts.onClose } : {}),
275
- ...(startOpts?.onSessionEnd ? { onSessionEnd: startOpts.onSessionEnd } : {}),
282
+ ...(startOpts?.onSinkCreated ? { onSinkCreated: startOpts.onSinkCreated } : {}),
283
+ onSessionEnd: (sid) => {
284
+ sinkMap.delete(sid);
285
+ userOnSessionEnd?.(sid);
286
+ },
276
287
  ...(sessionStartTimeoutMs !== undefined ? { sessionStartTimeoutMs } : {}),
277
288
  ...(resumeFrom ? { resumeFrom } : {}),
278
289
  });
@@ -295,6 +306,7 @@ export function createRuntime(opts: RuntimeOptions): Runtime {
295
306
  );
296
307
  }
297
308
  sessions.clear();
309
+ sinkMap.clear();
298
310
  }
299
311
 
300
312
  return {