@voicethere/agent 0.3.0 → 0.5.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.
- package/README.md +61 -57
- package/dist/agent.js +118 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +69 -5
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +3 -2
- package/dist/protocol.js.map +1 -1
- package/dist/runtime.d.ts +31 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +184 -2
- package/dist/runtime.js.map +1 -1
- package/dist/templates/echo/agent.js +127 -3
- package/dist/templates/echo-dc/agent.js +127 -3
- package/dist/templates/game-sync/agent.js +127 -3
- package/dist/templates/registry.d.ts.map +1 -1
- package/dist/templates/registry.js +16 -0
- package/dist/templates/registry.js.map +1 -1
- package/dist/templates/voice-starter/agent.js +127 -3
- package/dist/templates/webhooks/agent.js +674 -0
- package/dist/templates/webhooks-redis/agent.js +11449 -0
- package/package.json +4 -3
- package/templates/README.md +9 -1
- package/templates/webhooks-redis.ts +135 -0
- package/templates/webhooks.ts +111 -0
package/README.md
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
VoiceThere **customer agent SDK** — TypeScript types and runtime helpers for sandboxed child bundles running inside the **VoiceThere agent runner** (session worker).
|
|
4
4
|
|
|
5
|
+
**Website:** [voicethere.io](https://voicethere.io)
|
|
5
6
|
**npm:** `@voicethere/agent`
|
|
6
7
|
**Repo:** [`voicethere/agent`](https://github.com/voicethere/agent)
|
|
7
8
|
|
|
8
9
|
## Role
|
|
9
10
|
|
|
10
|
-
| Layer | Package
|
|
11
|
-
| --------- |
|
|
12
|
-
| Parent | VoiceThere agent runner
|
|
13
|
-
| **Child** | **`@voicethere/agent`**
|
|
11
|
+
| Layer | Package | Runs in |
|
|
12
|
+
| --------- | ----------------------- | ------------------------------------ |
|
|
13
|
+
| Parent | VoiceThere agent runner | Trusted Node + WebRTC + speech stack |
|
|
14
|
+
| **Child** | **`@voicethere/agent`** | Sandboxed customer `agent.js` bundle |
|
|
14
15
|
|
|
15
16
|
The child receives speech lifecycle events over IPC (same shapes as `@node-webrtc-rust/sdk/voice`) and calls `speak()` to request TTS from the parent.
|
|
16
17
|
|
|
@@ -41,12 +42,12 @@ npx @voicethere/agent verify-start --no-build --bundle ./dist/agent.js
|
|
|
41
42
|
|
|
42
43
|
`verify-start` launches the bundle in the sandboxed child with restricted Node flags (`--permission` + fs-read allowlist), sends `session_start`, and requires `session_start_ack`.
|
|
43
44
|
|
|
44
|
-
| Command
|
|
45
|
-
|
|
|
46
|
-
| `npx @voicethere/agent verify`
|
|
47
|
-
| `npx @voicethere/agent verify --no-build`
|
|
48
|
-
| `npx @voicethere/agent verify --no-build --bundle ./dist/agent.js`
|
|
49
|
-
| `npx @voicethere/agent verify-start --no-build --bundle ./dist/agent.js` | Verify sandbox startup + restricted Node flags on a specific bundle
|
|
45
|
+
| Command | When to use |
|
|
46
|
+
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
|
|
47
|
+
| `npx @voicethere/agent verify` | **Default** — build `agent.ts` → `dist/agent.js`, then run all static checks |
|
|
48
|
+
| `npx @voicethere/agent verify --no-build` | Re-run checks on an existing bundle |
|
|
49
|
+
| `npx @voicethere/agent verify --no-build --bundle ./dist/agent.js` | Verify a specific bundle path |
|
|
50
|
+
| `npx @voicethere/agent verify-start --no-build --bundle ./dist/agent.js` | Verify sandbox startup + restricted Node flags on a specific bundle |
|
|
50
51
|
|
|
51
52
|
Optional flags: `--entry` / `-e`, `--outfile` / `-o` (same as `build`).
|
|
52
53
|
|
|
@@ -146,45 +147,48 @@ import {
|
|
|
146
147
|
defineAgent,
|
|
147
148
|
speak,
|
|
148
149
|
type SpeechEvent,
|
|
149
|
-
} from
|
|
150
|
-
import { SPEECH_EVENT_TYPE } from
|
|
150
|
+
} from "@voicethere/agent";
|
|
151
|
+
import { SPEECH_EVENT_TYPE } from "@node-webrtc-rust/sdk/voice";
|
|
151
152
|
|
|
152
153
|
defineAgent({
|
|
153
154
|
async onAgentStart({ env }) {
|
|
154
155
|
// When project Redis is enabled, VoiceThere injects AGENT_REDIS_URL.
|
|
155
156
|
// Depend on `ioredis` in your agent package and connect here (once per child).
|
|
156
|
-
const redisUrl = env.AGENT_REDIS_URL // or process.env.AGENT_REDIS_URL
|
|
157
|
+
const redisUrl = env.AGENT_REDIS_URL; // or process.env.AGENT_REDIS_URL
|
|
157
158
|
if (redisUrl) {
|
|
158
159
|
// const Redis = (await import('ioredis')).default
|
|
159
160
|
// globalThis.redis = new Redis(redisUrl)
|
|
160
161
|
}
|
|
161
162
|
},
|
|
162
163
|
onSessionStart({ sessionId }) {
|
|
163
|
-
speak(sessionId,
|
|
164
|
+
speak(sessionId, "Hello!");
|
|
164
165
|
},
|
|
165
166
|
onUserSpeechFinal({ sessionId, text }) {
|
|
166
|
-
speak(sessionId, `You said: ${text}`)
|
|
167
|
+
speak(sessionId, `You said: ${text}`);
|
|
167
168
|
},
|
|
168
169
|
onSpeechEvent({ sessionId }, speech: SpeechEvent) {
|
|
169
170
|
if (speech.type === SPEECH_EVENT_TYPE.bargeIn) {
|
|
170
|
-
agentLog(
|
|
171
|
+
agentLog("info", `User interrupted on ${sessionId}`);
|
|
171
172
|
}
|
|
172
173
|
},
|
|
173
|
-
})
|
|
174
|
+
});
|
|
174
175
|
```
|
|
175
176
|
|
|
176
177
|
### Shared Redis (project-scoped)
|
|
177
178
|
|
|
178
179
|
On plans that include project Redis, the runner injects **`AGENT_REDIS_URL`** into the child environment and grants scoped `--allow-net` for that host. Add **`ioredis`** as a dependency of your agent, bundle it with the CLI, and open the client in **`onAgentStart`** so it is ready before any `onSessionStart` / session IPC.
|
|
179
180
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
|
183
|
-
|
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
181
|
+
For inbound HTTP webhooks, configure **`AGENT_WEBHOOK_SIGNING_SECRET`** in project settings. The runner forwards the exact request bytes on process-wide **`onWebhook`** IPC (not session-queued). Verify HMAC on `ctx.body` before `JSON.parse` — VoiceThere does not verify signatures in the SDK. See [`templates/webhooks.ts`](./templates/webhooks.ts).
|
|
182
|
+
|
|
183
|
+
| Export | Purpose |
|
|
184
|
+
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
185
|
+
| `defineAgent` | Register `onAgentStart`, `onWebhook`, `onSessionStart`, `onSpeechEvent`, `onUserSpeechFinal`, `onSessionEnd` |
|
|
186
|
+
| `SpeechEvent`, `SpeechEventType` | Re-exported **types** from `@node-webrtc-rust/sdk/voice` |
|
|
187
|
+
| `SPEECH_EVENT_TYPE` | Import from `@node-webrtc-rust/sdk/voice` (runtime constants; not bundled into child) |
|
|
188
|
+
| `speak` | Request parent TTS |
|
|
189
|
+
| `startRecording` / `pauseRecording` / `resumeRecording` / `stopRecording` | Request parent conversation recording control |
|
|
190
|
+
| `agentLog` | Forward structured logs to parent |
|
|
191
|
+
| `ParentToChildMessage` / `ChildToParentMessage` | IPC contract shared with the VoiceThere agent runner |
|
|
188
192
|
|
|
189
193
|
### Runner runtime subpath (minimal shared sandbox API)
|
|
190
194
|
|
|
@@ -277,12 +281,12 @@ Customer code runs in a **forked child process**, separate from the trusted agen
|
|
|
277
281
|
|
|
278
282
|
### Layer 1 — Process isolation
|
|
279
283
|
|
|
280
|
-
| Mechanism
|
|
281
|
-
|
|
|
282
|
-
| **Separate process**
|
|
283
|
-
| **IPC only for media**
|
|
284
|
+
| Mechanism | What it means for your bundle |
|
|
285
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
286
|
+
| **Separate process** | Crash or `process.exit` in your bundle does not take down the parent voice stack |
|
|
287
|
+
| **IPC only for media** | WebRTC, mic, STT, and TTS go through the parent — use `defineAgent`, `speak`, and speech events |
|
|
284
288
|
| **Stripped `process.env`** | Child receives only `NODE_ENV`, internal loader path, and allowlisted keys (`SESSION_ID`, `PROJECT_ID`, `BUILD_ID`) — not parent secrets |
|
|
285
|
-
| **Console redirection**
|
|
289
|
+
| **Console redirection** | `console.log` / `warn` / `error` → IPC logs |
|
|
286
290
|
|
|
287
291
|
### Layer 2 — Node `--permission` (runtime-enforced)
|
|
288
292
|
|
|
@@ -290,24 +294,24 @@ The parent starts the child with Node’s [Permission Model](https://nodejs.org/
|
|
|
290
294
|
|
|
291
295
|
**Granted today** (via `execArgv` on `fork()`):
|
|
292
296
|
|
|
293
|
-
| Flag
|
|
294
|
-
|
|
|
295
|
-
| `--permission`
|
|
296
|
-
| `--allow-fs-read=<loaderDir>`
|
|
297
|
-
| `--allow-fs-read=<bundleParentDir>` | Read files under the **directory containing your `agent.js`** (see below)
|
|
298
|
-
| `--allow-net`
|
|
299
|
-
| `--allow-net=<host>`
|
|
297
|
+
| Flag | Effect |
|
|
298
|
+
| ----------------------------------- | --------------------------------------------------------------------------------- |
|
|
299
|
+
| `--permission` | Enables restriction mode |
|
|
300
|
+
| `--allow-fs-read=<loaderDir>` | Read files under the child loader directory |
|
|
301
|
+
| `--allow-fs-read=<bundleParentDir>` | Read files under the **directory containing your `agent.js`** (see below) |
|
|
302
|
+
| `--allow-net` | Outbound network (HTTPS/fetch, TCP) for customer LLM and tool APIs (Node **26+**) |
|
|
303
|
+
| `--allow-net=<host>` | Reserved for project Redis host entries when Node supports host-scoped net ACLs |
|
|
300
304
|
|
|
301
305
|
**Not granted → blocked at runtime:**
|
|
302
306
|
|
|
303
|
-
| Missing flag
|
|
304
|
-
|
|
|
305
|
-
| No `--allow-child-process`
|
|
306
|
-
| No `--allow-fs-write`
|
|
307
|
+
| Missing flag | What fails |
|
|
308
|
+
| -------------------------------- | ------------------------------------------------------------ |
|
|
309
|
+
| No `--allow-child-process` | `child_process`, `exec`, `spawn`, `fork` |
|
|
310
|
+
| No `--allow-fs-write` | Any file write (`writeFile`, logs to disk, etc.) |
|
|
307
311
|
| No extra `--allow-fs-read` paths | Reading `/etc/passwd`, parent files, etc. outside bundle dir |
|
|
308
|
-
| No `--allow-addons`
|
|
309
|
-
| No `--allow-worker-threads`
|
|
310
|
-
| No `--allow-wasi`
|
|
312
|
+
| No `--allow-addons` | Native `.node` addons (`bcrypt`, `sharp`, …) |
|
|
313
|
+
| No `--allow-worker-threads` | `worker_threads` |
|
|
314
|
+
| No `--allow-wasi` | WASI modules |
|
|
311
315
|
|
|
312
316
|
This is **not** an import allowlist — Node gates **capability classes**, not package names. Using `node:fs` inside the allowed read tree can work; using it on `/etc/passwd` does not.
|
|
313
317
|
|
|
@@ -325,23 +329,23 @@ This is **not** an import allowlist — Node gates **capability classes**, not p
|
|
|
325
329
|
node_modules/ ← JS-only deps may resolve; native addons still blocked
|
|
326
330
|
```
|
|
327
331
|
|
|
328
|
-
| Artifact in bundle dir
|
|
329
|
-
|
|
|
330
|
-
| Single bundled `agent.js` (recommended)
|
|
331
|
-
| Extra pure `.js` / `.json` siblings
|
|
332
|
+
| Artifact in bundle dir | Works? |
|
|
333
|
+
| ------------------------------------------------- | ------------------------------------------------------------ |
|
|
334
|
+
| Single bundled `agent.js` (recommended) | Yes |
|
|
335
|
+
| Extra pure `.js` / `.json` siblings | Usually yes (same allowed tree) |
|
|
332
336
|
| `node_modules/` with **JavaScript-only** packages | Often yes (Node resolves imports by reading under that tree) |
|
|
333
|
-
| **Native** npm packages (`.node` binaries)
|
|
334
|
-
| Packages that **spawn subprocesses**
|
|
337
|
+
| **Native** npm packages (`.node` binaries) | **No** — requires `--allow-addons` (not enabled) |
|
|
338
|
+
| Packages that **spawn subprocesses** | **No** — no `--allow-child-process` |
|
|
335
339
|
|
|
336
340
|
Prefer **one esbuild bundle** so production behavior matches `npm run verify:local`.
|
|
337
341
|
|
|
338
342
|
### Layer 3 — Platform policy
|
|
339
343
|
|
|
340
|
-
| Capability
|
|
341
|
-
|
|
|
344
|
+
| Capability | Behavior |
|
|
345
|
+
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
342
346
|
| **Outbound network** (`fetch`, `http`, `https`) | **Public internet:** allowed — typical for LLM/tool calls from your agent code. **Internal platform / private network:** blocked on hosted sessions. |
|
|
343
|
-
| **`process.exit`**
|
|
344
|
-
| **Direct WebRTC / mic / STT / TTS**
|
|
347
|
+
| **`process.exit`** | Not blocked — kills your agent leg; parent may play crash TTS |
|
|
348
|
+
| **Direct WebRTC / mic / STT / TTS** | Parent only — use `speak()` and speech event handlers |
|
|
345
349
|
|
|
346
350
|
### What you should use in agent code
|
|
347
351
|
|
|
@@ -372,9 +376,9 @@ For iterative work: `npx @voicethere/agent build` then `npx @voicethere/agent ve
|
|
|
372
376
|
|
|
373
377
|
## Build outputs
|
|
374
378
|
|
|
375
|
-
| Path | Purpose
|
|
376
|
-
| --------------- |
|
|
377
|
-
| `dist/index.js` | Published npm library entry
|
|
379
|
+
| Path | Purpose |
|
|
380
|
+
| --------------- | -------------------------------------------------------------- |
|
|
381
|
+
| `dist/index.js` | Published npm library entry |
|
|
378
382
|
| `dist/agent.js` | Example bundle (`examples/agent.ts`) for local runner / verify |
|
|
379
383
|
|
|
380
384
|
## Scripts
|
package/dist/agent.js
CHANGED
|
@@ -3,6 +3,7 @@ const require = createRequire(import.meta.url);
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
// src/runtime.ts
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
6
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
8
|
|
|
8
9
|
// src/session-serial-queue.ts
|
|
@@ -129,10 +130,51 @@ var SessionSerialQueue = class {
|
|
|
129
130
|
var SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
|
|
130
131
|
var SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
|
|
131
132
|
var DEFAULT_SESSION_START_INIT_DELAY_MS = 500;
|
|
133
|
+
function isRecordingControlAckMessage(value) {
|
|
134
|
+
if (!value || typeof value !== "object") return false;
|
|
135
|
+
const msg = value;
|
|
136
|
+
return msg.type === "recording_control_ack" && typeof msg.requestId === "string";
|
|
137
|
+
}
|
|
138
|
+
function isWebhookMessage(value) {
|
|
139
|
+
if (!value || typeof value !== "object") return false;
|
|
140
|
+
const msg = value;
|
|
141
|
+
return msg.type === "webhook" && typeof msg.eventId === "string" && typeof msg.projectId === "string";
|
|
142
|
+
}
|
|
143
|
+
function coerceInboundBinary(value) {
|
|
144
|
+
if (!value) return null;
|
|
145
|
+
if (Buffer.isBuffer(value)) return value;
|
|
146
|
+
if (value instanceof Uint8Array) {
|
|
147
|
+
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
148
|
+
}
|
|
149
|
+
if (value instanceof ArrayBuffer) return Buffer.from(value);
|
|
150
|
+
if (ArrayBuffer.isView(value)) {
|
|
151
|
+
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
152
|
+
}
|
|
153
|
+
if (typeof value === "object") {
|
|
154
|
+
const maybeBufferLike = value;
|
|
155
|
+
if (maybeBufferLike.type === "Buffer" && Array.isArray(maybeBufferLike.data)) {
|
|
156
|
+
return Buffer.from(maybeBufferLike.data);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
function normalizeWebhookHeaders(value) {
|
|
162
|
+
if (!value || typeof value !== "object") return {};
|
|
163
|
+
const out = {};
|
|
164
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
165
|
+
if (typeof raw === "string") {
|
|
166
|
+
out[key] = raw;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
132
171
|
function isParentMessage(value) {
|
|
133
172
|
if (!value || typeof value !== "object") return false;
|
|
134
173
|
const msg = value;
|
|
135
|
-
return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout";
|
|
174
|
+
return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "webhook";
|
|
175
|
+
}
|
|
176
|
+
function isSessionScopedParentMessage(value) {
|
|
177
|
+
return isParentMessage(value) && !isWebhookMessage(value);
|
|
136
178
|
}
|
|
137
179
|
function parseDataChannelPayload(raw) {
|
|
138
180
|
try {
|
|
@@ -143,6 +185,26 @@ function parseDataChannelPayload(raw) {
|
|
|
143
185
|
}
|
|
144
186
|
var peerEnvBySessionId = /* @__PURE__ */ new Map();
|
|
145
187
|
var endedSessionIds = /* @__PURE__ */ new Set();
|
|
188
|
+
var pendingRecordingAcks = /* @__PURE__ */ new Map();
|
|
189
|
+
function handleRecordingControlAck(message) {
|
|
190
|
+
const pending = pendingRecordingAcks.get(message.requestId);
|
|
191
|
+
if (!pending) return;
|
|
192
|
+
clearTimeout(pending.timer);
|
|
193
|
+
pendingRecordingAcks.delete(message.requestId);
|
|
194
|
+
pending.resolve({
|
|
195
|
+
ok: message.ok,
|
|
196
|
+
reason: message.reason,
|
|
197
|
+
requestId: message.requestId
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function clearPendingRecordingAcksForSession(sessionId, reason) {
|
|
201
|
+
for (const [requestId, pending] of pendingRecordingAcks) {
|
|
202
|
+
if (pending.sessionId !== sessionId) continue;
|
|
203
|
+
clearTimeout(pending.timer);
|
|
204
|
+
pendingRecordingAcks.delete(requestId);
|
|
205
|
+
pending.resolve({ ok: false, reason, requestId });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
146
208
|
var sessionExecutionContext = new AsyncLocalStorage();
|
|
147
209
|
var agentLogSessionContext = new AsyncLocalStorage();
|
|
148
210
|
var inboundQueueAuthority = null;
|
|
@@ -219,6 +281,48 @@ function resolveSessionStartInitDelayMs() {
|
|
|
219
281
|
DEFAULT_SESSION_START_INIT_DELAY_MS
|
|
220
282
|
);
|
|
221
283
|
}
|
|
284
|
+
async function handleWebhookMessage(message, handlers) {
|
|
285
|
+
if (!handlers.onWebhook) return;
|
|
286
|
+
const body = coerceInboundBinary(message.body);
|
|
287
|
+
if (!body) {
|
|
288
|
+
agentLog("warn", "webhook ipc dropped: body is not binary");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const ctx = {
|
|
292
|
+
eventId: message.eventId,
|
|
293
|
+
projectId: message.projectId,
|
|
294
|
+
method: typeof message.method === "string" ? message.method : "POST",
|
|
295
|
+
path: typeof message.path === "string" ? message.path : "",
|
|
296
|
+
headers: normalizeWebhookHeaders(message.headers),
|
|
297
|
+
body,
|
|
298
|
+
contentType: typeof message.contentType === "string" ? message.contentType : null,
|
|
299
|
+
receivedAt: typeof message.receivedAt === "string" ? message.receivedAt : ""
|
|
300
|
+
};
|
|
301
|
+
try {
|
|
302
|
+
const started = Date.now();
|
|
303
|
+
await handlers.onWebhook(ctx);
|
|
304
|
+
sendParentMessage({
|
|
305
|
+
type: "webhook_handled",
|
|
306
|
+
projectId: message.projectId,
|
|
307
|
+
eventId: message.eventId,
|
|
308
|
+
durationMs: Date.now() - started
|
|
309
|
+
});
|
|
310
|
+
} catch (error) {
|
|
311
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
312
|
+
await runErrorHook(handlers, {
|
|
313
|
+
sessionId: "",
|
|
314
|
+
projectId: message.projectId,
|
|
315
|
+
env: process.env,
|
|
316
|
+
error: err
|
|
317
|
+
});
|
|
318
|
+
sendParentMessage({
|
|
319
|
+
type: "agent_error",
|
|
320
|
+
sessionId: "",
|
|
321
|
+
message: err.message,
|
|
322
|
+
stack: err.stack
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
222
326
|
async function handleParentMessage(message, handlers) {
|
|
223
327
|
switch (message.type) {
|
|
224
328
|
case "session_start":
|
|
@@ -232,7 +336,8 @@ async function handleParentMessage(message, handlers) {
|
|
|
232
336
|
}
|
|
233
337
|
await (handlers.onClientJoin ?? handlers.onSessionStart)?.({
|
|
234
338
|
sessionId: message.sessionId,
|
|
235
|
-
env: message.env
|
|
339
|
+
env: message.env,
|
|
340
|
+
recordingAvailable: message.recordingAvailable ?? false
|
|
236
341
|
});
|
|
237
342
|
sendParentMessage({
|
|
238
343
|
type: "session_start_ack",
|
|
@@ -270,6 +375,7 @@ async function handleParentMessage(message, handlers) {
|
|
|
270
375
|
});
|
|
271
376
|
break;
|
|
272
377
|
case "session_end":
|
|
378
|
+
clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
|
|
273
379
|
peerEnvBySessionId.delete(message.sessionId);
|
|
274
380
|
await (handlers.onClientLeave ?? handlers.onSessionEnd)?.({
|
|
275
381
|
sessionId: message.sessionId
|
|
@@ -286,9 +392,18 @@ function defineAgent(handlers) {
|
|
|
286
392
|
inboundQueueAuthority = inboundBySession;
|
|
287
393
|
const agentStartReady = runAgentStartHook(handlers);
|
|
288
394
|
process.on("message", (message) => {
|
|
289
|
-
if (
|
|
395
|
+
if (isRecordingControlAckMessage(message)) {
|
|
396
|
+
handleRecordingControlAck(message);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (isWebhookMessage(message)) {
|
|
400
|
+
void agentStartReady.then(() => handleWebhookMessage(message, handlers));
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (!isSessionScopedParentMessage(message)) return;
|
|
290
404
|
if (message.type === "session_end") {
|
|
291
405
|
endedSessionIds.add(message.sessionId);
|
|
406
|
+
clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
|
|
292
407
|
inboundBySession.clear(message.sessionId);
|
|
293
408
|
}
|
|
294
409
|
if (message.type === "session_start") {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export type { SpeechEvent, SpeechEventListener, SpeechEventName, SpeechEventType, } from "@node-webrtc-rust/sdk/voice";
|
|
2
|
-
export { ALLOWED_CHILD_ENV_KEYS, type AgentErrorMessage, type AgentLogLevel, type AgentLogMessage, type AllowedChildEnvKey, type ChildToParentMessage, type ParentToChildMessage, type SessionEndMessage, type SessionStartMessage, type SendToClientMessage, type DataChannelMessageMessage, type DataChannelBinaryMessage, type DataChannelKind, type DisconnectClientMessage, type IdleTimeoutDoneMessage, type IdleTimeoutMessage, type SendBinaryToClientMessage, type SpeakMessage, type SpeechEventMessage, } from "./protocol.js";
|
|
3
|
-
export { agentLog, defineAgent, disconnectClient, parseChatText, sendToClient, sendBinaryToClient, speak, broadcastToClients, type AgentHandlers, type AgentErrorContext, type AgentStartContext, type DataChannelContext, type IdleTimeoutContext, type SessionContext, type SpeechContext, type SpeechEventContext, } from "./runtime.js";
|
|
2
|
+
export { ALLOWED_CHILD_ENV_KEYS, type AgentErrorMessage, type AgentLogLevel, type AgentLogMessage, type AllowedChildEnvKey, type ChildToParentMessage, type ParentToChildMessage, type SessionEndMessage, type SessionStartMessage, type SendToClientMessage, type DataChannelMessageMessage, type DataChannelBinaryMessage, type DataChannelKind, type DisconnectClientMessage, type IdleTimeoutDoneMessage, type IdleTimeoutMessage, type RecordingControlMessage, type RecordingControlAckMessage, type RecordingControlAction, type RecordingControlResult, type SendBinaryToClientMessage, type SpeakMessage, type SpeechEventMessage, type WebhookMessage, } from "./protocol.js";
|
|
3
|
+
export { agentLog, defineAgent, disconnectClient, parseChatText, pauseRecording, resumeRecording, sendToClient, sendBinaryToClient, speak, startRecording, stopRecording, isRecordingAvailable, broadcastToClients, type AgentHandlers, type AgentErrorContext, type AgentStartContext, type DataChannelContext, type IdleTimeoutContext, type SessionContext, type SpeechContext, type SpeechEventContext, type WebhookContext, } from "./runtime.js";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,eAAe,GAChB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,kBAAkB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,eAAe,GAChB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,KAAK,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { ALLOWED_CHILD_ENV_KEYS, } from "./protocol.js";
|
|
2
|
-
export { agentLog, defineAgent, disconnectClient, parseChatText, sendToClient, sendBinaryToClient, speak, broadcastToClients, } from "./runtime.js";
|
|
2
|
+
export { agentLog, defineAgent, disconnectClient, parseChatText, pauseRecording, resumeRecording, sendToClient, sendBinaryToClient, speak, startRecording, stopRecording, isRecordingAvailable, broadcastToClients, } from "./runtime.js";
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,sBAAsB,GAwBvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,KAAK,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,kBAAkB,GAUnB,MAAM,cAAc,CAAC"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -16,13 +16,13 @@ export type { SpeechEvent } from "@node-webrtc-rust/sdk/voice";
|
|
|
16
16
|
* Register handlers via {@link defineAgent} in `@voicethere/agent` — do not read
|
|
17
17
|
* `process.on('message')` directly in customer bundles.
|
|
18
18
|
*/
|
|
19
|
-
export type ParentToChildMessage = SessionStartMessage | SpeechEventMessage | SessionEndMessage | DataChannelMessageMessage | DataChannelBinaryMessage | IdleTimeoutMessage;
|
|
19
|
+
export type ParentToChildMessage = SessionStartMessage | SpeechEventMessage | SessionEndMessage | DataChannelMessageMessage | DataChannelBinaryMessage | IdleTimeoutMessage | RecordingControlAckMessage | WebhookMessage;
|
|
20
20
|
/**
|
|
21
21
|
* Messages the customer child may send back to the runner parent.
|
|
22
22
|
*
|
|
23
|
-
* Prefer {@link speak}, {@link sendToClient}, {@link sendBinaryToClient}, and {@link agentLog} helpers over raw `process.send`.
|
|
23
|
+
* Prefer {@link speak}, {@link startRecording}, {@link sendToClient}, {@link sendBinaryToClient}, and {@link agentLog} helpers over raw `process.send`.
|
|
24
24
|
*/
|
|
25
|
-
export type ChildToParentMessage = SessionStartAckMessage | SpeakMessage | AgentLogMessage | AgentErrorMessage | SendToClientMessage | SendBinaryToClientMessage | IdleTimeoutDoneMessage | DisconnectClientMessage;
|
|
25
|
+
export type ChildToParentMessage = SessionStartAckMessage | SpeakMessage | RecordingControlMessage | AgentLogMessage | AgentErrorMessage | SendToClientMessage | SendBinaryToClientMessage | IdleTimeoutDoneMessage | DisconnectClientMessage | WebhookHandledMessage;
|
|
26
26
|
/** Which WebRTC data channel carried a binary IPC payload. */
|
|
27
27
|
export type DataChannelKind = "control" | "sync";
|
|
28
28
|
/**
|
|
@@ -40,6 +40,11 @@ export interface SessionStartMessage {
|
|
|
40
40
|
* Keys are a subset of {@link ALLOWED_CHILD_ENV_KEYS}.
|
|
41
41
|
*/
|
|
42
42
|
env: Record<string, string>;
|
|
43
|
+
/**
|
|
44
|
+
* When `true`, the runner has conversation recording enabled for this project.
|
|
45
|
+
* Absent on older runners — treat as `false`.
|
|
46
|
+
*/
|
|
47
|
+
recordingAvailable?: boolean;
|
|
43
48
|
}
|
|
44
49
|
/**
|
|
45
50
|
* Forwards one speech lifecycle event from the parent Sherpa/VAD/STT/TTS pipeline.
|
|
@@ -109,6 +114,36 @@ export interface SpeakMessage {
|
|
|
109
114
|
/** UTF-8 text passed to the parent TTS vendor. */
|
|
110
115
|
text: string;
|
|
111
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Ask the parent to start, pause, resume, or stop conversation recording for a session.
|
|
119
|
+
*
|
|
120
|
+
* Recording runs in the runner parent — use {@link startRecording}, {@link pauseRecording},
|
|
121
|
+
* {@link resumeRecording}, and {@link stopRecording} instead of raw `process.send`.
|
|
122
|
+
*/
|
|
123
|
+
export type RecordingControlAction = "start" | "pause" | "resume" | "stop";
|
|
124
|
+
export interface RecordingControlMessage {
|
|
125
|
+
type: "recording_control";
|
|
126
|
+
/** Target peer/session id (must match a prior {@link SessionStartMessage}). */
|
|
127
|
+
sessionId: string;
|
|
128
|
+
action: RecordingControlAction;
|
|
129
|
+
/** Correlates with {@link RecordingControlAckMessage.requestId}. */
|
|
130
|
+
requestId: string;
|
|
131
|
+
}
|
|
132
|
+
/** Runner acknowledgement for a {@link RecordingControlMessage}. */
|
|
133
|
+
export interface RecordingControlAckMessage {
|
|
134
|
+
type: "recording_control_ack";
|
|
135
|
+
sessionId: string;
|
|
136
|
+
action: RecordingControlAction;
|
|
137
|
+
requestId: string;
|
|
138
|
+
ok: boolean;
|
|
139
|
+
reason?: "applied" | "disabled" | "unsupported" | "stopped" | "local_mock" | "timeout" | string;
|
|
140
|
+
}
|
|
141
|
+
/** Result returned by {@link startRecording} and related helpers. */
|
|
142
|
+
export type RecordingControlResult = {
|
|
143
|
+
ok: boolean;
|
|
144
|
+
reason?: string;
|
|
145
|
+
requestId: string;
|
|
146
|
+
};
|
|
112
147
|
/** Log severity forwarded to the runner parent process. */
|
|
113
148
|
export type AgentLogLevel = "debug" | "info" | "warn" | "error";
|
|
114
149
|
/**
|
|
@@ -157,6 +192,34 @@ export interface SendBinaryToClientMessage {
|
|
|
157
192
|
data: Buffer;
|
|
158
193
|
channel?: DataChannelKind;
|
|
159
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Inbound HTTP webhook forwarded from the edge — process-wide, not tied to a session.
|
|
197
|
+
*
|
|
198
|
+
* Delivered to {@link AgentHandlers.onWebhook} on every child in the runner process
|
|
199
|
+
* (not session-queued). {@link WebhookMessage.body} is the exact inbound bytes — verify
|
|
200
|
+
* HMAC/signatures on `body` before `JSON.parse` in customer code.
|
|
201
|
+
*/
|
|
202
|
+
export interface WebhookMessage {
|
|
203
|
+
type: "webhook";
|
|
204
|
+
/** Edge-generated id for idempotency. */
|
|
205
|
+
eventId: string;
|
|
206
|
+
projectId: string;
|
|
207
|
+
method: string;
|
|
208
|
+
path: string;
|
|
209
|
+
/** All inbound request headers (string values). */
|
|
210
|
+
headers: Record<string, string>;
|
|
211
|
+
/** Exact inbound body bytes — do not parse in the SDK before the customer handler. */
|
|
212
|
+
body: Buffer;
|
|
213
|
+
contentType: string | null;
|
|
214
|
+
receivedAt: string;
|
|
215
|
+
}
|
|
216
|
+
/** Child reports onWebhook completion latency (process-wide, not session-scoped). */
|
|
217
|
+
export interface WebhookHandledMessage {
|
|
218
|
+
type: "webhook_handled";
|
|
219
|
+
projectId: string;
|
|
220
|
+
eventId: string;
|
|
221
|
+
durationMs: number;
|
|
222
|
+
}
|
|
160
223
|
/**
|
|
161
224
|
* Idle timeout fired — run {@link AgentHandlers.onIdleTimeout} before disconnect.
|
|
162
225
|
*/
|
|
@@ -189,8 +252,9 @@ export interface DisconnectClientMessage {
|
|
|
189
252
|
* The runner may add more project-specific keys over time; customer bundles must
|
|
190
253
|
* not read `process.env` for session fields — only the `env` object on session start.
|
|
191
254
|
*
|
|
192
|
-
* Process-wide secrets such as `AGENT_REDIS_URL` (when project Redis is enabled)
|
|
193
|
-
*
|
|
255
|
+
* Process-wide secrets such as `AGENT_REDIS_URL` (when project Redis is enabled) and
|
|
256
|
+
* `AGENT_WEBHOOK_SIGNING_SECRET` (for inbound webhook HMAC verification) are still
|
|
257
|
+
* available on `process.env` inside `onAgentStart` / `onWebhook`.
|
|
194
258
|
* Keys prefixed with `AGENT_` may also be forwarded into the child environment.
|
|
195
259
|
*/
|
|
196
260
|
export declare const ALLOWED_CHILD_ENV_KEYS: readonly ["SESSION_ID", "PROJECT_ID", "BUILD_ID", "IDLE_TIMEOUT_SEC", "AGENT_CUSTOMER_CONTEXT"];
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,YAAY,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAC5B,mBAAmB,GACnB,kBAAkB,GAClB,iBAAiB,GACjB,yBAAyB,GACzB,wBAAwB,GACxB,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,YAAY,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAC5B,mBAAmB,GACnB,kBAAkB,GAClB,iBAAiB,GACjB,yBAAyB,GACzB,wBAAwB,GACxB,kBAAkB,GAClB,0BAA0B,GAC1B,cAAc,CAAC;AAEnB;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAC5B,sBAAsB,GACtB,YAAY,GACZ,uBAAuB,GACvB,eAAe,GACf,iBAAiB,GACjB,mBAAmB,GACnB,yBAAyB,GACzB,sBAAsB,GACtB,uBAAuB,GACvB,qBAAqB,CAAC;AAE1B,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,cAAc,CAAC;IACrB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,KAAK,EAAE,WAAW,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE3E,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,oEAAoE;AACpE,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EACH,SAAS,GACT,UAAU,GACV,aAAa,GACb,SAAS,GACT,YAAY,GACZ,SAAS,GACT,MAAM,CAAC;CACZ;AAED,qEAAqE;AACrE,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,2DAA2D;AAC3D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,KAAK,CAAC;IACZ,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,gBAAgB,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qEAAqE;AACrE,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qFAAqF;AACrF,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,iGAOzB,CAAC;AAEX,0CAA0C;AAC1C,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC"}
|
package/dist/protocol.js
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
* The runner may add more project-specific keys over time; customer bundles must
|
|
15
15
|
* not read `process.env` for session fields — only the `env` object on session start.
|
|
16
16
|
*
|
|
17
|
-
* Process-wide secrets such as `AGENT_REDIS_URL` (when project Redis is enabled)
|
|
18
|
-
*
|
|
17
|
+
* Process-wide secrets such as `AGENT_REDIS_URL` (when project Redis is enabled) and
|
|
18
|
+
* `AGENT_WEBHOOK_SIGNING_SECRET` (for inbound webhook HMAC verification) are still
|
|
19
|
+
* available on `process.env` inside `onAgentStart` / `onWebhook`.
|
|
19
20
|
* Keys prefixed with `AGENT_` may also be forwarded into the child environment.
|
|
20
21
|
*/
|
|
21
22
|
export const ALLOWED_CHILD_ENV_KEYS = [
|
package/dist/protocol.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkSH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,YAAY;IACZ,YAAY;IACZ,UAAU;IACV,kBAAkB;IAClB,wEAAwE;IACxE,wBAAwB;CAChB,CAAC"}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { SpeechEvent } from "@node-webrtc-rust/sdk/voice";
|
|
2
|
-
import type { AgentLogLevel, DataChannelKind } from "./protocol.js";
|
|
2
|
+
import type { AgentLogLevel, DataChannelKind, RecordingControlResult } from "./protocol.js";
|
|
3
3
|
export declare const SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
|
|
4
4
|
export declare const SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
|
|
5
5
|
export interface SessionContext {
|
|
6
6
|
sessionId: string;
|
|
7
7
|
env: Record<string, string>;
|
|
8
|
+
/** `true` when the runner advertises conversation recording for this project. */
|
|
9
|
+
recordingAvailable: boolean;
|
|
8
10
|
}
|
|
9
11
|
export interface SpeechContext {
|
|
10
12
|
sessionId: string;
|
|
@@ -26,6 +28,18 @@ export interface AgentStartContext {
|
|
|
26
28
|
/** Snapshot of `process.env` at child start (includes `AGENT_REDIS_URL` when set). */
|
|
27
29
|
env: Record<string, string>;
|
|
28
30
|
}
|
|
31
|
+
/** Context for process-wide inbound webhook IPC (`type: "webhook"`). */
|
|
32
|
+
export interface WebhookContext {
|
|
33
|
+
eventId: string;
|
|
34
|
+
projectId: string;
|
|
35
|
+
method: string;
|
|
36
|
+
path: string;
|
|
37
|
+
headers: Record<string, string>;
|
|
38
|
+
/** Exact inbound bytes — verify HMAC on this, then `JSON.parse`. */
|
|
39
|
+
body: Buffer;
|
|
40
|
+
contentType: string | null;
|
|
41
|
+
receivedAt: string;
|
|
42
|
+
}
|
|
29
43
|
export interface AgentHandlers {
|
|
30
44
|
/**
|
|
31
45
|
* Runs once when the child registers handlers — before any session IPC is handled.
|
|
@@ -33,6 +47,12 @@ export interface AgentHandlers {
|
|
|
33
47
|
* Errors are logged and reported; session IPC is still accepted afterward so the child does not hang.
|
|
34
48
|
*/
|
|
35
49
|
onAgentStart?: (ctx: AgentStartContext) => void | Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Process-wide inbound HTTP webhook from the edge (not session-queued).
|
|
52
|
+
* VoiceThere does not verify signatures — use `AGENT_WEBHOOK_SIGNING_SECRET` in
|
|
53
|
+
* `process.env` and verify HMAC on {@link WebhookContext.body} before parsing JSON.
|
|
54
|
+
*/
|
|
55
|
+
onWebhook?: (ctx: WebhookContext) => void | Promise<void>;
|
|
36
56
|
/** Alias for {@link AgentHandlers.onSessionStart}. */
|
|
37
57
|
onClientJoin?: (ctx: SessionContext) => void | Promise<void>;
|
|
38
58
|
onSessionStart?: (ctx: SessionContext) => void | Promise<void>;
|
|
@@ -105,6 +125,16 @@ export declare function defineAgent(handlers: AgentHandlers): void;
|
|
|
105
125
|
export declare function resetAgentIpcStateForTests(): void;
|
|
106
126
|
/** Ask the runner parent to synthesize speech for the session. */
|
|
107
127
|
export declare function speak(sessionId: string, text: string): void;
|
|
128
|
+
/** True when {@link SessionStartMessage.recordingAvailable} was set for the session. */
|
|
129
|
+
export declare function isRecordingAvailable(ctx: SessionContext): boolean;
|
|
130
|
+
/** Ask the runner parent to start conversation recording for the session. */
|
|
131
|
+
export declare function startRecording(sessionId: string): Promise<RecordingControlResult>;
|
|
132
|
+
/** Ask the runner parent to pause an in-progress recording for the session. */
|
|
133
|
+
export declare function pauseRecording(sessionId: string): Promise<RecordingControlResult>;
|
|
134
|
+
/** Ask the runner parent to resume a paused recording for the session. */
|
|
135
|
+
export declare function resumeRecording(sessionId: string): Promise<RecordingControlResult>;
|
|
136
|
+
/** Ask the runner parent to stop conversation recording for the session. */
|
|
137
|
+
export declare function stopRecording(sessionId: string): Promise<RecordingControlResult>;
|
|
108
138
|
/** Send a JSON payload to the browser peer via the runner parent. */
|
|
109
139
|
export declare function sendToClient(sessionId: string, payload: unknown): void;
|
|
110
140
|
/** Send raw bytes to the browser peer via the runner parent. */
|