@voicethere/agent 0.2.17 → 0.4.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 +59 -57
- package/dist/agent.js +35 -2
- 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 +38 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/dist/runtime.d.ts +13 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +91 -1
- package/dist/runtime.js.map +1 -1
- package/dist/templates/build-seed-bundles-cli.d.ts +3 -0
- package/dist/templates/build-seed-bundles-cli.d.ts.map +1 -0
- package/dist/templates/build-seed-bundles-cli.js +8 -0
- package/dist/templates/build-seed-bundles-cli.js.map +1 -0
- package/dist/templates/echo/agent.js +563 -0
- package/dist/templates/echo-dc/agent.js +553 -0
- package/dist/templates/game-sync/agent.js +807 -0
- package/dist/templates/index.d.ts +19 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +77 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/registry.d.ts +18 -0
- package/dist/templates/registry.d.ts.map +1 -0
- package/dist/templates/registry.js +81 -0
- package/dist/templates/registry.js.map +1 -0
- package/dist/templates/voice-starter/agent.js +634 -0
- package/package.json +18 -4
- package/templates/README.md +53 -40
- package/templates/crash.ts +61 -0
- package/templates/echo-smoke.ts +27 -0
- package/templates/game-sync-smoke.ts +81 -0
- package/templates/redis-sync/agent.ts +255 -0
- package/templates/redis-sync/world-layout.ts +118 -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,46 @@ 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
|
-
| Export
|
|
181
|
-
|
|
|
182
|
-
| `defineAgent`
|
|
183
|
-
| `SpeechEvent`, `SpeechEventType`
|
|
184
|
-
| `SPEECH_EVENT_TYPE`
|
|
185
|
-
| `speak`
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
181
|
+
| Export | Purpose |
|
|
182
|
+
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
183
|
+
| `defineAgent` | Register `onAgentStart`, `onSessionStart`, `onSpeechEvent`, `onUserSpeechFinal`, `onSessionEnd` |
|
|
184
|
+
| `SpeechEvent`, `SpeechEventType` | Re-exported **types** from `@node-webrtc-rust/sdk/voice` |
|
|
185
|
+
| `SPEECH_EVENT_TYPE` | Import from `@node-webrtc-rust/sdk/voice` (runtime constants; not bundled into child) |
|
|
186
|
+
| `speak` | Request parent TTS |
|
|
187
|
+
| `startRecording` / `pauseRecording` / `resumeRecording` / `stopRecording` | Request parent conversation recording control |
|
|
188
|
+
| `agentLog` | Forward structured logs to parent |
|
|
189
|
+
| `ParentToChildMessage` / `ChildToParentMessage` | IPC contract shared with the VoiceThere agent runner |
|
|
188
190
|
|
|
189
191
|
### Runner runtime subpath (minimal shared sandbox API)
|
|
190
192
|
|
|
@@ -277,12 +279,12 @@ Customer code runs in a **forked child process**, separate from the trusted agen
|
|
|
277
279
|
|
|
278
280
|
### Layer 1 — Process isolation
|
|
279
281
|
|
|
280
|
-
| Mechanism
|
|
281
|
-
|
|
|
282
|
-
| **Separate process**
|
|
283
|
-
| **IPC only for media**
|
|
282
|
+
| Mechanism | What it means for your bundle |
|
|
283
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
284
|
+
| **Separate process** | Crash or `process.exit` in your bundle does not take down the parent voice stack |
|
|
285
|
+
| **IPC only for media** | WebRTC, mic, STT, and TTS go through the parent — use `defineAgent`, `speak`, and speech events |
|
|
284
286
|
| **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**
|
|
287
|
+
| **Console redirection** | `console.log` / `warn` / `error` → IPC logs |
|
|
286
288
|
|
|
287
289
|
### Layer 2 — Node `--permission` (runtime-enforced)
|
|
288
290
|
|
|
@@ -290,24 +292,24 @@ The parent starts the child with Node’s [Permission Model](https://nodejs.org/
|
|
|
290
292
|
|
|
291
293
|
**Granted today** (via `execArgv` on `fork()`):
|
|
292
294
|
|
|
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>`
|
|
295
|
+
| Flag | Effect |
|
|
296
|
+
| ----------------------------------- | --------------------------------------------------------------------------------- |
|
|
297
|
+
| `--permission` | Enables restriction mode |
|
|
298
|
+
| `--allow-fs-read=<loaderDir>` | Read files under the child loader directory |
|
|
299
|
+
| `--allow-fs-read=<bundleParentDir>` | Read files under the **directory containing your `agent.js`** (see below) |
|
|
300
|
+
| `--allow-net` | Outbound network (HTTPS/fetch, TCP) for customer LLM and tool APIs (Node **26+**) |
|
|
301
|
+
| `--allow-net=<host>` | Reserved for project Redis host entries when Node supports host-scoped net ACLs |
|
|
300
302
|
|
|
301
303
|
**Not granted → blocked at runtime:**
|
|
302
304
|
|
|
303
|
-
| Missing flag
|
|
304
|
-
|
|
|
305
|
-
| No `--allow-child-process`
|
|
306
|
-
| No `--allow-fs-write`
|
|
305
|
+
| Missing flag | What fails |
|
|
306
|
+
| -------------------------------- | ------------------------------------------------------------ |
|
|
307
|
+
| No `--allow-child-process` | `child_process`, `exec`, `spawn`, `fork` |
|
|
308
|
+
| No `--allow-fs-write` | Any file write (`writeFile`, logs to disk, etc.) |
|
|
307
309
|
| 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`
|
|
310
|
+
| No `--allow-addons` | Native `.node` addons (`bcrypt`, `sharp`, …) |
|
|
311
|
+
| No `--allow-worker-threads` | `worker_threads` |
|
|
312
|
+
| No `--allow-wasi` | WASI modules |
|
|
311
313
|
|
|
312
314
|
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
315
|
|
|
@@ -325,23 +327,23 @@ This is **not** an import allowlist — Node gates **capability classes**, not p
|
|
|
325
327
|
node_modules/ ← JS-only deps may resolve; native addons still blocked
|
|
326
328
|
```
|
|
327
329
|
|
|
328
|
-
| Artifact in bundle dir
|
|
329
|
-
|
|
|
330
|
-
| Single bundled `agent.js` (recommended)
|
|
331
|
-
| Extra pure `.js` / `.json` siblings
|
|
330
|
+
| Artifact in bundle dir | Works? |
|
|
331
|
+
| ------------------------------------------------- | ------------------------------------------------------------ |
|
|
332
|
+
| Single bundled `agent.js` (recommended) | Yes |
|
|
333
|
+
| Extra pure `.js` / `.json` siblings | Usually yes (same allowed tree) |
|
|
332
334
|
| `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**
|
|
335
|
+
| **Native** npm packages (`.node` binaries) | **No** — requires `--allow-addons` (not enabled) |
|
|
336
|
+
| Packages that **spawn subprocesses** | **No** — no `--allow-child-process` |
|
|
335
337
|
|
|
336
338
|
Prefer **one esbuild bundle** so production behavior matches `npm run verify:local`.
|
|
337
339
|
|
|
338
340
|
### Layer 3 — Platform policy
|
|
339
341
|
|
|
340
|
-
| Capability
|
|
341
|
-
|
|
|
342
|
+
| Capability | Behavior |
|
|
343
|
+
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
342
344
|
| **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**
|
|
345
|
+
| **`process.exit`** | Not blocked — kills your agent leg; parent may play crash TTS |
|
|
346
|
+
| **Direct WebRTC / mic / STT / TTS** | Parent only — use `speak()` and speech event handlers |
|
|
345
347
|
|
|
346
348
|
### What you should use in agent code
|
|
347
349
|
|
|
@@ -372,9 +374,9 @@ For iterative work: `npx @voicethere/agent build` then `npx @voicethere/agent ve
|
|
|
372
374
|
|
|
373
375
|
## Build outputs
|
|
374
376
|
|
|
375
|
-
| Path | Purpose
|
|
376
|
-
| --------------- |
|
|
377
|
-
| `dist/index.js` | Published npm library entry
|
|
377
|
+
| Path | Purpose |
|
|
378
|
+
| --------------- | -------------------------------------------------------------- |
|
|
379
|
+
| `dist/index.js` | Published npm library entry |
|
|
378
380
|
| `dist/agent.js` | Example bundle (`examples/agent.ts`) for local runner / verify |
|
|
379
381
|
|
|
380
382
|
## 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,15 @@ 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
|
+
}
|
|
132
138
|
function isParentMessage(value) {
|
|
133
139
|
if (!value || typeof value !== "object") return false;
|
|
134
140
|
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";
|
|
141
|
+
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";
|
|
136
142
|
}
|
|
137
143
|
function parseDataChannelPayload(raw) {
|
|
138
144
|
try {
|
|
@@ -143,6 +149,26 @@ function parseDataChannelPayload(raw) {
|
|
|
143
149
|
}
|
|
144
150
|
var peerEnvBySessionId = /* @__PURE__ */ new Map();
|
|
145
151
|
var endedSessionIds = /* @__PURE__ */ new Set();
|
|
152
|
+
var pendingRecordingAcks = /* @__PURE__ */ new Map();
|
|
153
|
+
function handleRecordingControlAck(message) {
|
|
154
|
+
const pending = pendingRecordingAcks.get(message.requestId);
|
|
155
|
+
if (!pending) return;
|
|
156
|
+
clearTimeout(pending.timer);
|
|
157
|
+
pendingRecordingAcks.delete(message.requestId);
|
|
158
|
+
pending.resolve({
|
|
159
|
+
ok: message.ok,
|
|
160
|
+
reason: message.reason,
|
|
161
|
+
requestId: message.requestId
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function clearPendingRecordingAcksForSession(sessionId, reason) {
|
|
165
|
+
for (const [requestId, pending] of pendingRecordingAcks) {
|
|
166
|
+
if (pending.sessionId !== sessionId) continue;
|
|
167
|
+
clearTimeout(pending.timer);
|
|
168
|
+
pendingRecordingAcks.delete(requestId);
|
|
169
|
+
pending.resolve({ ok: false, reason, requestId });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
146
172
|
var sessionExecutionContext = new AsyncLocalStorage();
|
|
147
173
|
var agentLogSessionContext = new AsyncLocalStorage();
|
|
148
174
|
var inboundQueueAuthority = null;
|
|
@@ -232,7 +258,8 @@ async function handleParentMessage(message, handlers) {
|
|
|
232
258
|
}
|
|
233
259
|
await (handlers.onClientJoin ?? handlers.onSessionStart)?.({
|
|
234
260
|
sessionId: message.sessionId,
|
|
235
|
-
env: message.env
|
|
261
|
+
env: message.env,
|
|
262
|
+
recordingAvailable: message.recordingAvailable ?? false
|
|
236
263
|
});
|
|
237
264
|
sendParentMessage({
|
|
238
265
|
type: "session_start_ack",
|
|
@@ -270,6 +297,7 @@ async function handleParentMessage(message, handlers) {
|
|
|
270
297
|
});
|
|
271
298
|
break;
|
|
272
299
|
case "session_end":
|
|
300
|
+
clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
|
|
273
301
|
peerEnvBySessionId.delete(message.sessionId);
|
|
274
302
|
await (handlers.onClientLeave ?? handlers.onSessionEnd)?.({
|
|
275
303
|
sessionId: message.sessionId
|
|
@@ -286,9 +314,14 @@ function defineAgent(handlers) {
|
|
|
286
314
|
inboundQueueAuthority = inboundBySession;
|
|
287
315
|
const agentStartReady = runAgentStartHook(handlers);
|
|
288
316
|
process.on("message", (message) => {
|
|
317
|
+
if (isRecordingControlAckMessage(message)) {
|
|
318
|
+
handleRecordingControlAck(message);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
289
321
|
if (!isParentMessage(message)) return;
|
|
290
322
|
if (message.type === "session_end") {
|
|
291
323
|
endedSessionIds.add(message.sessionId);
|
|
324
|
+
clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
|
|
292
325
|
inboundBySession.clear(message.sessionId);
|
|
293
326
|
}
|
|
294
327
|
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, } 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, } 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,GACxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,KAAK,EACL,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,GACxB,MAAM,cAAc,CAAC"}
|
|
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,GACxB,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,GACxB,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,GAuBvB,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,GASnB,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;
|
|
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;
|
|
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
|
/**
|
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":"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"}
|
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;AAkQH;;;;;;;;;GASG;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;
|
|
@@ -105,6 +107,16 @@ export declare function defineAgent(handlers: AgentHandlers): void;
|
|
|
105
107
|
export declare function resetAgentIpcStateForTests(): void;
|
|
106
108
|
/** Ask the runner parent to synthesize speech for the session. */
|
|
107
109
|
export declare function speak(sessionId: string, text: string): void;
|
|
110
|
+
/** True when {@link SessionStartMessage.recordingAvailable} was set for the session. */
|
|
111
|
+
export declare function isRecordingAvailable(ctx: SessionContext): boolean;
|
|
112
|
+
/** Ask the runner parent to start conversation recording for the session. */
|
|
113
|
+
export declare function startRecording(sessionId: string): Promise<RecordingControlResult>;
|
|
114
|
+
/** Ask the runner parent to pause an in-progress recording for the session. */
|
|
115
|
+
export declare function pauseRecording(sessionId: string): Promise<RecordingControlResult>;
|
|
116
|
+
/** Ask the runner parent to resume a paused recording for the session. */
|
|
117
|
+
export declare function resumeRecording(sessionId: string): Promise<RecordingControlResult>;
|
|
118
|
+
/** Ask the runner parent to stop conversation recording for the session. */
|
|
119
|
+
export declare function stopRecording(sessionId: string): Promise<RecordingControlResult>;
|
|
108
120
|
/** Send a JSON payload to the browser peer via the runner parent. */
|
|
109
121
|
export declare function sendToClient(sessionId: string, payload: unknown): void;
|
|
110
122
|
/** Send raw bytes to the browser peer via the runner parent. */
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
3
|
import { SessionSerialQueue } from "./session-serial-queue.js";
|
|
3
4
|
export const SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
|
|
4
5
|
export const SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
|
|
5
6
|
const DEFAULT_SESSION_START_INIT_DELAY_MS = 500;
|
|
7
|
+
function isRecordingControlAckMessage(value) {
|
|
8
|
+
if (!value || typeof value !== "object")
|
|
9
|
+
return false;
|
|
10
|
+
const msg = value;
|
|
11
|
+
return (msg.type === "recording_control_ack" && typeof msg.requestId === "string");
|
|
12
|
+
}
|
|
6
13
|
function isParentMessage(value) {
|
|
7
14
|
if (!value || typeof value !== "object")
|
|
8
15
|
return false;
|
|
@@ -12,7 +19,8 @@ function isParentMessage(value) {
|
|
|
12
19
|
msg.type === "session_end" ||
|
|
13
20
|
msg.type === "data_channel_message" ||
|
|
14
21
|
msg.type === "data_channel_binary" ||
|
|
15
|
-
msg.type === "idle_timeout"
|
|
22
|
+
msg.type === "idle_timeout" ||
|
|
23
|
+
msg.type === "recording_control_ack");
|
|
16
24
|
}
|
|
17
25
|
function parseDataChannelPayload(raw) {
|
|
18
26
|
try {
|
|
@@ -25,6 +33,35 @@ function parseDataChannelPayload(raw) {
|
|
|
25
33
|
const peerEnvBySessionId = new Map();
|
|
26
34
|
/** Sessions that received `session_end`; `speak()` becomes a no-op when no live gen. */
|
|
27
35
|
const endedSessionIds = new Set();
|
|
36
|
+
const RECORDING_CONTROL_ACK_TIMEOUT_MS = 5000;
|
|
37
|
+
const pendingRecordingAcks = new Map();
|
|
38
|
+
function handleRecordingControlAck(message) {
|
|
39
|
+
const pending = pendingRecordingAcks.get(message.requestId);
|
|
40
|
+
if (!pending)
|
|
41
|
+
return;
|
|
42
|
+
clearTimeout(pending.timer);
|
|
43
|
+
pendingRecordingAcks.delete(message.requestId);
|
|
44
|
+
pending.resolve({
|
|
45
|
+
ok: message.ok,
|
|
46
|
+
reason: message.reason,
|
|
47
|
+
requestId: message.requestId,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function clearPendingRecordingAcksForSession(sessionId, reason) {
|
|
51
|
+
for (const [requestId, pending] of pendingRecordingAcks) {
|
|
52
|
+
if (pending.sessionId !== sessionId)
|
|
53
|
+
continue;
|
|
54
|
+
clearTimeout(pending.timer);
|
|
55
|
+
pendingRecordingAcks.delete(requestId);
|
|
56
|
+
pending.resolve({ ok: false, reason, requestId });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function isVoicethereAgentChild() {
|
|
60
|
+
return (typeof process.send === "function" &&
|
|
61
|
+
process.connected !== false &&
|
|
62
|
+
typeof process.env.__CHILD_BUNDLE_PATH__ === "string" &&
|
|
63
|
+
process.env.__CHILD_BUNDLE_PATH__.trim().length > 0);
|
|
64
|
+
}
|
|
28
65
|
/**
|
|
29
66
|
* Generation-aware execution context for inbound handlers. Outbound helpers
|
|
30
67
|
* (`speak`, `sendToClient`, …) consult this so a late old-generation callback
|
|
@@ -145,6 +182,7 @@ async function handleParentMessage(message, handlers) {
|
|
|
145
182
|
await (handlers.onClientJoin ?? handlers.onSessionStart)?.({
|
|
146
183
|
sessionId: message.sessionId,
|
|
147
184
|
env: message.env,
|
|
185
|
+
recordingAvailable: message.recordingAvailable ?? false,
|
|
148
186
|
});
|
|
149
187
|
sendParentMessage({
|
|
150
188
|
type: "session_start_ack",
|
|
@@ -181,6 +219,7 @@ async function handleParentMessage(message, handlers) {
|
|
|
181
219
|
});
|
|
182
220
|
break;
|
|
183
221
|
case "session_end":
|
|
222
|
+
clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
|
|
184
223
|
peerEnvBySessionId.delete(message.sessionId);
|
|
185
224
|
await (handlers.onClientLeave ?? handlers.onSessionEnd)?.({
|
|
186
225
|
sessionId: message.sessionId,
|
|
@@ -207,6 +246,10 @@ export function defineAgent(handlers) {
|
|
|
207
246
|
inboundQueueAuthority = inboundBySession;
|
|
208
247
|
const agentStartReady = runAgentStartHook(handlers);
|
|
209
248
|
process.on("message", (message) => {
|
|
249
|
+
if (isRecordingControlAckMessage(message)) {
|
|
250
|
+
handleRecordingControlAck(message);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
210
253
|
if (!isParentMessage(message))
|
|
211
254
|
return;
|
|
212
255
|
// Invalidate immediately on arrival — do not wait behind queued work that
|
|
@@ -214,6 +257,7 @@ export function defineAgent(handlers) {
|
|
|
214
257
|
// the leave hook only.
|
|
215
258
|
if (message.type === "session_end") {
|
|
216
259
|
endedSessionIds.add(message.sessionId);
|
|
260
|
+
clearPendingRecordingAcksForSession(message.sessionId, "session_ended");
|
|
217
261
|
inboundBySession.clear(message.sessionId);
|
|
218
262
|
}
|
|
219
263
|
// session_start must not chain onto a dying session_end generation — clear
|
|
@@ -361,11 +405,57 @@ export function resetAgentIpcStateForTests() {
|
|
|
361
405
|
endedSessionIds.clear();
|
|
362
406
|
peerEnvBySessionId.clear();
|
|
363
407
|
inboundQueueAuthority = null;
|
|
408
|
+
for (const [requestId, pending] of pendingRecordingAcks) {
|
|
409
|
+
clearTimeout(pending.timer);
|
|
410
|
+
pendingRecordingAcks.delete(requestId);
|
|
411
|
+
}
|
|
364
412
|
}
|
|
365
413
|
/** Ask the runner parent to synthesize speech for the session. */
|
|
366
414
|
export function speak(sessionId, text) {
|
|
367
415
|
sendParentMessage({ type: "speak", sessionId, text });
|
|
368
416
|
}
|
|
417
|
+
/** True when {@link SessionStartMessage.recordingAvailable} was set for the session. */
|
|
418
|
+
export function isRecordingAvailable(ctx) {
|
|
419
|
+
return ctx.recordingAvailable;
|
|
420
|
+
}
|
|
421
|
+
async function sendRecordingControl(sessionId, action) {
|
|
422
|
+
const requestId = randomUUID();
|
|
423
|
+
if (!allowOutboundForSession(sessionId)) {
|
|
424
|
+
return { ok: false, reason: "session_ended", requestId };
|
|
425
|
+
}
|
|
426
|
+
if (!isVoicethereAgentChild()) {
|
|
427
|
+
return { ok: true, reason: "local_mock", requestId };
|
|
428
|
+
}
|
|
429
|
+
return new Promise((resolve) => {
|
|
430
|
+
const timer = setTimeout(() => {
|
|
431
|
+
pendingRecordingAcks.delete(requestId);
|
|
432
|
+
resolve({ ok: false, reason: "timeout", requestId });
|
|
433
|
+
}, RECORDING_CONTROL_ACK_TIMEOUT_MS);
|
|
434
|
+
pendingRecordingAcks.set(requestId, { sessionId, resolve, timer });
|
|
435
|
+
sendParentMessage({
|
|
436
|
+
type: "recording_control",
|
|
437
|
+
sessionId,
|
|
438
|
+
action,
|
|
439
|
+
requestId,
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
/** Ask the runner parent to start conversation recording for the session. */
|
|
444
|
+
export function startRecording(sessionId) {
|
|
445
|
+
return sendRecordingControl(sessionId, "start");
|
|
446
|
+
}
|
|
447
|
+
/** Ask the runner parent to pause an in-progress recording for the session. */
|
|
448
|
+
export function pauseRecording(sessionId) {
|
|
449
|
+
return sendRecordingControl(sessionId, "pause");
|
|
450
|
+
}
|
|
451
|
+
/** Ask the runner parent to resume a paused recording for the session. */
|
|
452
|
+
export function resumeRecording(sessionId) {
|
|
453
|
+
return sendRecordingControl(sessionId, "resume");
|
|
454
|
+
}
|
|
455
|
+
/** Ask the runner parent to stop conversation recording for the session. */
|
|
456
|
+
export function stopRecording(sessionId) {
|
|
457
|
+
return sendRecordingControl(sessionId, "stop");
|
|
458
|
+
}
|
|
369
459
|
/** Send a JSON payload to the browser peer via the runner parent. */
|
|
370
460
|
export function sendToClient(sessionId, payload) {
|
|
371
461
|
sendParentMessage({ type: "send_to_client", sessionId, payload });
|