@voicelayer/sdk 0.1.7 → 0.1.9

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.
@@ -1,837 +0,0 @@
1
- import { JobContext, voice } from '@livekit/agents';
2
- import { z } from 'zod';
3
-
4
- declare const brand: unique symbol;
5
- type Brand<T, B> = T & {
6
- readonly [brand]: B;
7
- };
8
- type TenantId = Brand<string, 'TenantId'>;
9
- declare const TenantId: (s: string) => TenantId;
10
- type CallId = Brand<string, 'CallId'>;
11
- declare const CallId: (s: string) => CallId;
12
- type ModuleId = Brand<string, 'ModuleId'>;
13
- declare const ModuleId: (s: string) => ModuleId;
14
-
15
- declare const participantBrand: unique symbol;
16
- type ParticipantId = string & {
17
- readonly [participantBrand]: 'ParticipantId';
18
- };
19
- declare const ParticipantId: (s: string) => ParticipantId;
20
- /**
21
- * How this participant is connected to the room. Determines which transport
22
- * adapter owns the underlying media track.
23
- * - `sip` — PSTN-side via LiveKit SIP (Twilio/Telnyx trunk)
24
- * - `webrtc` — browser/native WebRTC peer
25
- * - `agent` — the AI agent itself (server-published track)
26
- * - `bridge` — incoming non-WebRTC ingress (e.g. an Egress recorder)
27
- */
28
- type ParticipantKind = 'sip' | 'webrtc' | 'agent' | 'bridge';
29
- /**
30
- * One participant in a room. Snapshot semantics: callers read from
31
- * `ParticipantSet`, which owns lifecycle. Fields are readonly because
32
- * mutations must go through the routing primitive to stay observable.
33
- */
34
- interface Participant {
35
- readonly id: ParticipantId;
36
- readonly kind: ParticipantKind;
37
- /** True iff `kind === 'agent'`. Convenience for filtering. */
38
- readonly isAgent: boolean;
39
- /** E.164 when known (always for SIP, never for agent, sometimes for WebRTC). */
40
- readonly phone: string | null;
41
- /** Human-readable label (caller name, agent name). */
42
- readonly displayName: string | null;
43
- /**
44
- * Free-form attributes attached by the dispatcher or runtime. Standardised
45
- * keys the platform reads:
46
- * - `lang` — BCP-47 (e.g. `en-US`, `es-MX`). Used by audio-experience.
47
- * - `role` — e.g. `claimant`, `witness`, `interpreter`. Used by handoff.
48
- * - `tier` — e.g. `vip`, `standard`. Used by audio-experience.
49
- */
50
- readonly attributes: Readonly<Record<string, string>>;
51
- readonly joinedAt: Date;
52
- /** Set when the participant has left. Stays in the set until call end. */
53
- readonly leftAt: Date | null;
54
- }
55
- /**
56
- * Live view of room participants. The agent-worker owns the underlying
57
- * collection; primitives consume it read-only. Snapshot semantics: methods
58
- * return arrays/values valid at call time. The set itself updates in place
59
- * as participants join/leave — re-call to see new state.
60
- */
61
- interface ParticipantSet {
62
- /** All participants ever in the room, including those who have left. */
63
- readonly all: readonly Participant[];
64
- /** Currently-connected, non-agent participants. */
65
- readonly humans: readonly Participant[];
66
- /** Currently-connected agents. Usually exactly one. */
67
- readonly agents: readonly Participant[];
68
- readonly size: number;
69
- get(id: ParticipantId): Participant | undefined;
70
- where(pred: (p: Participant) => boolean): readonly Participant[];
71
- }
72
- /**
73
- * The call container. One Room per call. Replaces the singular call+caller
74
- * model for multi-participant primitives; existing single-caller primitives
75
- * keep reading CallContext.callerId until migrated.
76
- */
77
- interface Room {
78
- readonly id: CallId;
79
- readonly tenantId: TenantId;
80
- readonly moduleId: ModuleId;
81
- readonly startedAt: Date;
82
- readonly participants: ParticipantSet;
83
- /** Dispatch metadata (LK job metadata) — projectId, agentId, etc. */
84
- readonly metadata: Readonly<Record<string, unknown>>;
85
- }
86
-
87
- declare const FlowProgram: z.ZodObject<{
88
- v: z.ZodLiteral<1>;
89
- id: z.ZodString;
90
- entry: z.ZodArray<z.ZodString, "many">;
91
- nodes: z.ZodRecord<z.ZodString, z.ZodObject<{
92
- id: z.ZodString;
93
- type: z.ZodEnum<["start", "say", "ask", "confirm", "tool", "decision", "handoff", "end", "trigger", "gate", "playbook", "menu", "code", "set", "subflow", "llm", "rag", "memory", "classify", "router", "note"]>;
94
- config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
95
- out: z.ZodArray<z.ZodObject<{
96
- to: z.ZodString;
97
- condition: z.ZodOptional<z.ZodString>;
98
- sourceHandle: z.ZodOptional<z.ZodString>;
99
- label: z.ZodOptional<z.ZodString>;
100
- }, "strip", z.ZodTypeAny, {
101
- to: string;
102
- sourceHandle?: string | undefined;
103
- label?: string | undefined;
104
- condition?: string | undefined;
105
- }, {
106
- to: string;
107
- sourceHandle?: string | undefined;
108
- label?: string | undefined;
109
- condition?: string | undefined;
110
- }>, "many">;
111
- }, "strip", z.ZodTypeAny, {
112
- type: "code" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "set" | "subflow" | "llm" | "rag" | "memory" | "classify" | "router" | "note";
113
- id: string;
114
- config: Record<string, unknown>;
115
- out: {
116
- to: string;
117
- sourceHandle?: string | undefined;
118
- label?: string | undefined;
119
- condition?: string | undefined;
120
- }[];
121
- }, {
122
- type: "code" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "set" | "subflow" | "llm" | "rag" | "memory" | "classify" | "router" | "note";
123
- id: string;
124
- config: Record<string, unknown>;
125
- out: {
126
- to: string;
127
- sourceHandle?: string | undefined;
128
- label?: string | undefined;
129
- condition?: string | undefined;
130
- }[];
131
- }>>;
132
- completionGate: z.ZodObject<{
133
- requiredFields: z.ZodArray<z.ZodString, "many">;
134
- }, "strip", z.ZodTypeAny, {
135
- requiredFields: string[];
136
- }, {
137
- requiredFields: string[];
138
- }>;
139
- diagnostics: z.ZodOptional<z.ZodObject<{
140
- acyclic: z.ZodBoolean;
141
- droppedEdges: z.ZodArray<z.ZodString, "many">;
142
- unsupportedNodes: z.ZodArray<z.ZodString, "many">;
143
- }, "strip", z.ZodTypeAny, {
144
- acyclic: boolean;
145
- droppedEdges: string[];
146
- unsupportedNodes: string[];
147
- }, {
148
- acyclic: boolean;
149
- droppedEdges: string[];
150
- unsupportedNodes: string[];
151
- }>>;
152
- }, "strip", z.ZodTypeAny, {
153
- v: 1;
154
- id: string;
155
- nodes: Record<string, {
156
- type: "code" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "set" | "subflow" | "llm" | "rag" | "memory" | "classify" | "router" | "note";
157
- id: string;
158
- config: Record<string, unknown>;
159
- out: {
160
- to: string;
161
- sourceHandle?: string | undefined;
162
- label?: string | undefined;
163
- condition?: string | undefined;
164
- }[];
165
- }>;
166
- entry: string[];
167
- completionGate: {
168
- requiredFields: string[];
169
- };
170
- diagnostics?: {
171
- acyclic: boolean;
172
- droppedEdges: string[];
173
- unsupportedNodes: string[];
174
- } | undefined;
175
- }, {
176
- v: 1;
177
- id: string;
178
- nodes: Record<string, {
179
- type: "code" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "set" | "subflow" | "llm" | "rag" | "memory" | "classify" | "router" | "note";
180
- id: string;
181
- config: Record<string, unknown>;
182
- out: {
183
- to: string;
184
- sourceHandle?: string | undefined;
185
- label?: string | undefined;
186
- condition?: string | undefined;
187
- }[];
188
- }>;
189
- entry: string[];
190
- completionGate: {
191
- requiredFields: string[];
192
- };
193
- diagnostics?: {
194
- acyclic: boolean;
195
- droppedEdges: string[];
196
- unsupportedNodes: string[];
197
- } | undefined;
198
- }>;
199
- type FlowProgram = z.infer<typeof FlowProgram>;
200
-
201
- type SessionState = 'initializing' | 'idle' | 'listening' | 'thinking' | 'speaking';
202
- declare const LK_AGENT_STATE_MAP: Readonly<Record<string, SessionState>>;
203
- declare const NON_BUSY_STATES: ReadonlySet<SessionState>;
204
- /** Normalize a raw LK state string to SessionState; unknown -> 'thinking' (fail-safe). */
205
- declare function normalizeState(raw: string): SessionState;
206
- interface UserInputEvent {
207
- readonly transcript: string;
208
- readonly isFinal: boolean;
209
- readonly speakerId: string | null;
210
- }
211
- interface AgentTurnEvent {
212
- readonly text: string;
213
- }
214
- interface DtmfEvent$1 {
215
- readonly digit: string;
216
- readonly code: number;
217
- readonly participantId: string | null;
218
- }
219
- interface StateChangeEvent {
220
- readonly oldState: SessionState | null;
221
- readonly newState: SessionState | null;
222
- readonly rawOldState: string | null;
223
- readonly rawNewState: string | null;
224
- }
225
- interface SessionErrorEvent {
226
- readonly error: unknown;
227
- readonly source: 'llm' | 'stt' | 'tts' | 'realtime' | 'unknown';
228
- readonly fatalTransport: boolean;
229
- }
230
- interface MetricsEvent {
231
- readonly metrics: unknown;
232
- }
233
- interface SessionEventMap {
234
- user_input: UserInputEvent;
235
- agent_turn: AgentTurnEvent;
236
- dtmf: DtmfEvent$1;
237
- state: StateChangeEvent;
238
- error: SessionErrorEvent;
239
- metrics: MetricsEvent;
240
- close: void;
241
- }
242
- type SessionEvent = keyof SessionEventMap;
243
- type Unsubscribe = () => void;
244
- type ChannelKind = 'voice' | 'text';
245
- interface SessionCapabilities {
246
- readonly channel: ChannelKind;
247
- readonly usingRealtime: boolean;
248
- readonly hasStreamingTranscripts: boolean;
249
- readonly canBargeIn: boolean;
250
- readonly canReceiveDtmf: boolean;
251
- readonly canPublishDtmf: boolean;
252
- readonly canResolveSpeaker: boolean;
253
- }
254
- interface SayOptions$1 {
255
- readonly allowInterruptions?: boolean;
256
- readonly to?: string | readonly string[];
257
- }
258
- interface SpeechResult {
259
- readonly interrupted: boolean;
260
- }
261
- interface EndCallOutcome {
262
- readonly torn: 'room' | 'agent' | 'none';
263
- readonly stranded: boolean;
264
- }
265
- interface SessionOutbound {
266
- /** Speak verbatim. Under usingRealtime the LiveKit adapter routes to
267
- * generateReply(verbatim) NOT session.say (no TTS in S2S). Resolves after
268
- * playout (voice) or after the text turn is committed (text). */
269
- say(text: string, opts?: SayOptions$1): Promise<SpeechResult>;
270
- /** Ask the LLM to produce a turn. */
271
- generateReply(opts?: {
272
- readonly instructions?: string;
273
- }): Promise<void>;
274
- /** Barge-in cancel (puppet interrupt mode). text => no-op. */
275
- interrupt(): Promise<void>;
276
- /** Publish outbound DTMF from the agent leg. Throws if !canPublishDtmf. */
277
- sendDtmf(code: number, digit: string): Promise<void>;
278
- /** Hang up via the SDK end-call controller (deletes room so the SIP leg drops). */
279
- endCall(opts?: {
280
- readonly reason?: string;
281
- readonly trigger?: string;
282
- }): Promise<EndCallOutcome>;
283
- }
284
- type TurnEvent = {
285
- readonly kind: 'partial';
286
- readonly text: string;
287
- readonly speakerId: string | null;
288
- } | {
289
- readonly kind: 'final';
290
- readonly text: string;
291
- readonly speakerId: string | null;
292
- readonly at: Date;
293
- } | {
294
- readonly kind: 'dtmf';
295
- readonly digit: string;
296
- readonly code: number;
297
- readonly participantId: string | null;
298
- readonly at: Date;
299
- } | {
300
- readonly kind: 'hangup';
301
- } | {
302
- readonly kind: 'timeout';
303
- };
304
- interface InboundOptions {
305
- readonly idleTimeoutMs?: number;
306
- readonly finalsOnly?: boolean;
307
- }
308
- interface SessionAdapter extends SessionOutbound {
309
- readonly capabilities: SessionCapabilities;
310
- on<E extends SessionEvent>(event: E, handler: (payload: SessionEventMap[E]) => void): Unsubscribe;
311
- once<E extends SessionEvent>(event: E, handler: (payload: SessionEventMap[E]) => void): Unsubscribe;
312
- /** Graph read side. ONE underlying subscription. Mutually exclusive with the
313
- * flat on('user_input') consumer (THROWS if both active). The iterator's
314
- * return()/finally MUST unsubscribe. */
315
- inbound(opts?: InboundOptions): AsyncIterableIterator<TurnEvent>;
316
- /** Begin the session (was session.start + waitForSessionActivity). The LiveKit
317
- * impl preserves the activity sequence VERBATIM so a provider/LLM init failure
318
- * REJECTS rather than producing a silently-dead answered call. */
319
- start(opts?: {
320
- readonly agent?: unknown;
321
- readonly room?: unknown;
322
- }): Promise<void>;
323
- readonly state: SessionState;
324
- /** Resolve when idle/listening, on timeout, or on close. Synchronous fast-path:
325
- * if already non-busy, resolves 'idle' in the same microtask. */
326
- waitForIdle(timeoutMs: number): Promise<'idle' | 'timeout' | 'closed'>;
327
- waitForClose(): Promise<void>;
328
- /** Escape hatch for genuinely LK-coupled sites. undefined on the text adapter. */
329
- readonly raw?: {
330
- readonly job: JobContext;
331
- readonly session: unknown;
332
- readonly room: unknown;
333
- };
334
- dispose(): void;
335
- }
336
- interface EndCallControllerHandle {
337
- hangUp(opts?: {
338
- readonly trigger?: string;
339
- readonly reason?: string;
340
- }): Promise<void>;
341
- armIdle(): void;
342
- cancelIdle(): void;
343
- noteEngineClosed(): void;
344
- }
345
- /**
346
- * Fold the user's `speech.interruption` / `speech.endpointing` config into an
347
- * LK turnHandling object. User-set fields are merged over whatever mode
348
- * defaults (puppet/hostControlled) already put there — explicit agent config
349
- * wins. No-op when the config is absent, so existing agents keep LK defaults.
350
- * Lives here (not session-adapter-livekit) so agent.ts can import it without
351
- * pulling @livekit/agents in at module load.
352
- */
353
- declare function applySpeechTurnHandling(turnHandling: Record<string, unknown>, speech?: {
354
- readonly interruption?: {
355
- readonly enabled?: boolean;
356
- readonly minWords?: number;
357
- readonly minDuration?: number;
358
- };
359
- readonly endpointing?: {
360
- readonly minDelay?: number;
361
- readonly maxDelay?: number;
362
- };
363
- }): Record<string, unknown>;
364
- interface SessionFactoryInput {
365
- readonly usingRealtime: boolean;
366
- readonly graphMode: boolean;
367
- readonly puppetMode: boolean;
368
- readonly hostControlled: boolean;
369
- readonly captureDtmf: boolean;
370
- readonly pipeline?: {
371
- readonly stt?: unknown;
372
- readonly tts?: unknown;
373
- readonly vad?: unknown;
374
- readonly llm: unknown;
375
- };
376
- readonly turnHandling?: Record<string, unknown>;
377
- /** Per-agent barge-in + endpointing tuning (AgentConfig `speech`). Merged
378
- * over mode defaults by applySpeechTurnHandling — explicit config wins. */
379
- readonly speech?: {
380
- readonly interruption?: {
381
- readonly enabled?: boolean;
382
- readonly minWords?: number;
383
- readonly minDuration?: number;
384
- };
385
- readonly endpointing?: {
386
- readonly minDelay?: number;
387
- readonly maxDelay?: number;
388
- };
389
- };
390
- readonly resolveSpeaker?: (speakerId: string | null) => {
391
- readonly id: string | null;
392
- readonly label: string;
393
- };
394
- readonly classifyEngineClosed?: (err: unknown) => boolean;
395
- readonly endCallController?: EndCallControllerHandle;
396
- }
397
- type SessionFactory = (input: SessionFactoryInput) => Promise<SessionAdapter>;
398
- interface UserTurn {
399
- readonly text: string;
400
- readonly speakerLabel: string;
401
- readonly speakerId: string | null;
402
- readonly at: Date;
403
- }
404
- interface FakeSessionAdapter extends SessionAdapter {
405
- emitUserInput(e: UserInputEvent): void;
406
- emitAgentTurn(text: string): void;
407
- emitDtmf(digit: string): void;
408
- /** Drives 'state' with correct oldState/rawOldState AND runs the SAME
409
- * arm/cancel + idle-release as production. Unknown string => 'thinking'. */
410
- setState(next: SessionState | string): void;
411
- emitError(error: unknown, fatalTransport?: boolean): void;
412
- emitClose(): void;
413
- readonly spoken: ReadonlyArray<string>;
414
- readonly generatedReplies: ReadonlyArray<string | undefined>;
415
- readonly sentDtmf: ReadonlyArray<string>;
416
- readonly endCalls: ReadonlyArray<{
417
- readonly reason?: string;
418
- readonly trigger?: string;
419
- }>;
420
- }
421
-
422
- interface ProcessSchema {
423
- readonly id: string;
424
- readonly fields: readonly FieldSpec[];
425
- readonly completionGate: GateSpec;
426
- }
427
- interface FieldSpec {
428
- readonly name: string;
429
- readonly required: boolean;
430
- /**
431
- * Validation rule. Two accepted forms:
432
- * 1. A Zod schema (preferred — e.g. `z.string().regex(...)` or `z.number().min(0)`).
433
- * 2. The legacy JSON spec object — `{ type, pattern, min, max, enum }`.
434
- * Both are compiled to a Zod schema at `loadSchema` time. New code should
435
- * use Zod directly.
436
- */
437
- readonly validation: unknown;
438
- }
439
- interface GateSpec {
440
- /** Names of fields that must be captured before canCallEnd() returns ok. */
441
- readonly requiredFields: readonly string[];
442
- /** Optional external ACK webhook — gate stays open until ACK received. */
443
- readonly backendAck?: {
444
- readonly url: string;
445
- readonly timeoutMs: number;
446
- };
447
- }
448
- /**
449
- * Side-channel event the actor records alongside its state machine — tool
450
- * calls, form-page activity, webhook dispatches, anything the agent does
451
- * that isn't a field capture. Recorded into context.lifecycle (rolling,
452
- * capped) so the dashboard React Flow board can render a timeline of "what
453
- * happened on this call" without us having to invent a separate event bus.
454
- */
455
- interface ProcessLifecycleEvent {
456
- /** Stable id (ULID/UUID) so dashboards can de-dup across reconnects. */
457
- readonly id: string;
458
- /** ISO timestamp. */
459
- readonly at: string;
460
- /**
461
- * Event kind. Convention: `<surface>.<verb_past>` —
462
- * form.link_sent, form.field_set, form.submitted,
463
- * email.sent, email.send_failed,
464
- * webhook.enqueued, webhook.delivered,
465
- * tool.invoked, tool.errored
466
- * Keep these short and stable; the dashboard switches on them.
467
- */
468
- readonly kind: string;
469
- /** Where the event came from. */
470
- readonly source: 'agent' | 'caller' | 'form' | 'platform';
471
- /** Free-form structured data. Kept small — this rides every snapshot. */
472
- readonly data: Readonly<Record<string, unknown>>;
473
- }
474
- interface ProcessSnapshot$1 {
475
- readonly schemaId: string;
476
- readonly currentNode: string;
477
- readonly fieldsCaptured: Readonly<Record<string, unknown>>;
478
- readonly backendAckReceived: boolean;
479
- readonly backendAckReceivedAt?: Date;
480
- /**
481
- * Side-channel events (tool calls, form events, webhooks). Empty when no
482
- * lifecycle events have been recorded; capped at PROCESS_LIFECYCLE_MAX_EVENTS
483
- * (oldest events dropped on overflow).
484
- */
485
- readonly lifecycle: readonly ProcessLifecycleEvent[];
486
- }
487
-
488
- interface TtsVoiceConfig {
489
- readonly provider: 'deepgram' | 'openai' | 'elevenlabs' | 'cartesia';
490
- readonly model: string;
491
- readonly voice?: string;
492
- readonly instructions?: string;
493
- }
494
- interface AudioProfile {
495
- readonly holdMusicUrl: string;
496
- readonly duckingDb: number;
497
- readonly dtmfFallbackThreshold: number;
498
- readonly languageHint: string;
499
- readonly tts: TtsVoiceConfig;
500
- }
501
-
502
- type RoutingRule = {
503
- readonly kind: 'passthrough';
504
- } | {
505
- readonly kind: 'mute';
506
- } | {
507
- readonly kind: 'duck';
508
- readonly db: number;
509
- } | {
510
- readonly kind: 'transform';
511
- readonly id: string;
512
- };
513
- declare const passthrough: () => RoutingRule;
514
- declare const mute: () => RoutingRule;
515
- declare const duck: (db: number) => RoutingRule;
516
- declare const transform: (id: string) => RoutingRule;
517
- interface RouteEntry {
518
- readonly source: ParticipantId;
519
- readonly destination: ParticipantId;
520
- readonly rule: RoutingRule;
521
- }
522
- interface FloorControlConfig {
523
- /**
524
- * Minimum quiet window (ms) after the last human utterance before the
525
- * agent is allowed to speak. Prevents the agent from talking over a
526
- * human who hasn't yielded the floor. Default 400ms.
527
- */
528
- readonly humanQuietWindowMs: number;
529
- /**
530
- * If true, the agent may speak immediately at call start before any
531
- * human has spoken (the greeting). Default true.
532
- */
533
- readonly allowAgentGreeting: boolean;
534
- }
535
- interface RoutingSnapshot {
536
- readonly entries: readonly RouteEntry[];
537
- readonly lastSpokeAt: ReadonlyMap<ParticipantId, number>;
538
- }
539
-
540
- interface CallInfo {
541
- /** Stable id for the call; comes from dispatch metadata or room name. */
542
- readonly callId: string;
543
- /** Caller phone number (E.164) when available, else participant identity. */
544
- readonly callerId: string;
545
- /** Phone number that was called (E.164) when available. */
546
- readonly to: string | null;
547
- /** Wall-clock when the agent joined. */
548
- readonly startedAt: Date;
549
- /** Free-form metadata from the dispatch rule (e.g. projectId, agentId). */
550
- readonly metadata: Record<string, unknown>;
551
- }
552
- interface MemoryHandle {
553
- get(key: string): Promise<unknown>;
554
- set(key: string, value: unknown): Promise<void>;
555
- }
556
- interface ProcessSnapshot {
557
- /** Captured field values. */
558
- readonly data: Record<string, unknown>;
559
- /** True once the process completion gate has fired. */
560
- readonly complete: boolean;
561
- }
562
- /**
563
- * Input to ctx.recordEvent — id and timestamp are stamped by the runtime so
564
- * callers only supply the semantic fields. `source` defaults to 'agent' for
565
- * tool-call events; pass 'form'/'platform'/'caller' when emitting from a
566
- * different surface (form-page bridge, webhook handler).
567
- */
568
- interface RecordEventInput$1 {
569
- readonly kind: string;
570
- readonly source?: 'agent' | 'caller' | 'form' | 'platform';
571
- readonly data?: Record<string, unknown>;
572
- }
573
- /**
574
- * Targets for participant-scoped speech operations. Either a raw
575
- * `ParticipantId`, a `Participant` snapshot, or an array of either.
576
- * `'broadcast'` is the explicit form of the default — everyone in the room.
577
- */
578
- type SpeechTarget = ParticipantId | Participant | readonly (ParticipantId | Participant)[] | 'broadcast';
579
- /**
580
- * Audio surface — per-participant profiles, routing matrix, and floor
581
- * control. Exposed on `ctx.audio` so user hooks (e.g. translation in
582
- * onUtterance) can drive the underlying primitives without importing
583
- * them directly.
584
- *
585
- * In PR#4 these are wired to live LK participant events but the audio
586
- * fanout itself (per-participant STT, per-listener TTS) is still single-
587
- * pipeline; users that need actual per-listener delivery should go
588
- * through `ctx.livekit` for now.
589
- */
590
- interface AudioSurface {
591
- /** Profile (voice/lang/DTMF) for one participant. */
592
- profileFor(participantId: ParticipantId): AudioProfile;
593
- /** Update one participant's language hint (also swaps TTS voice). */
594
- setLanguage(participantId: ParticipantId, languageHint: string): AudioProfile;
595
- /** Current routing matrix snapshot. */
596
- routes(): RoutingSnapshot;
597
- /** Override a single (source, destination) edge in the routing matrix. */
598
- setRoute(source: ParticipantId, destination: ParticipantId, rule: RoutingRule): void;
599
- /** Floor-control: true iff no human has spoken inside the quiet window. */
600
- canAgentSpeak(): boolean;
601
- }
602
- interface SayOptions {
603
- /** Restrict the spoken track to one or more participants. Default: broadcast. */
604
- readonly to?: SpeechTarget;
605
- }
606
- /**
607
- * A DTMF keypress received from a participant. `code` is the RFC 4733 event
608
- * code (0–15: digits 0–9, *=10, #=11, A–D=12–15); `digit` is the canonical
609
- * single-character representation. `participantId` identifies the sender in
610
- * the synthesized room (matches Room.participants).
611
- */
612
- interface DtmfEvent {
613
- readonly participantId: ParticipantId;
614
- readonly code: number;
615
- readonly digit: string;
616
- readonly at: Date;
617
- }
618
- /** Handler signature for ctx.onDtmf. Returning false unsubscribes. */
619
- type DtmfHandler = (event: DtmfEvent) => void | Promise<void>;
620
- interface SendDtmfOptions {
621
- /** Milliseconds between consecutive digits. Default 100. */
622
- readonly delayMs?: number;
623
- }
624
- interface RespondOptions {
625
- readonly instructions?: string;
626
- readonly to?: SpeechTarget;
627
- }
628
- interface AgentContext {
629
- readonly call: CallInfo;
630
- readonly memory: MemoryHandle;
631
- /** Typed connector APIs. Empty record when no connectors are configured. */
632
- readonly connectors: Readonly<Record<string, unknown>>;
633
- /** Read-only view of process state. Undefined when no process is configured. */
634
- readonly process: ProcessSnapshot | undefined;
635
- /**
636
- * Record a side-channel lifecycle event into the process actor. Visible
637
- * to the dashboard React Flow board in real time alongside field
638
- * captures. Use for "this happened on the call" facts — tool calls,
639
- * form-page activity, webhook dispatches — that aren't field captures.
640
- * No-op when the agent has no process configured.
641
- */
642
- recordEvent(event: RecordEventInput$1): void;
643
- /**
644
- * Subscribe the agent to a form-page session's WS stream so values typed
645
- * on the form-page mirror into the process actor (channel-agnostic field
646
- * capture). Tools call this after a successful sendFormLink; the runtime
647
- * owns the lifecycle of the underlying WebSocket.
648
- *
649
- * Returns a function the tool can call to tear down the subscription
650
- * early. The subscription also self-closes on form submission or idle.
651
- *
652
- * No-op (returns a no-op teardown) when the agent has no process or
653
- * VOICELAYER_API_URL is unset.
654
- */
655
- watchFormSession(token: string): () => void;
656
- /** Free-form scratch space for hooks to share state within a call. */
657
- readonly metadata: Record<string, unknown>;
658
- /**
659
- * Live view of the call as a participant room. Always populated — for
660
- * 1:1 calls the room contains one human participant + the agent.
661
- */
662
- readonly room: Room;
663
- /** Per-participant audio profiles + routing matrix + floor control. */
664
- readonly audio: AudioSurface;
665
- /**
666
- * Speak text (via TTS). Returns when the speech is queued.
667
- * `opts.to` selects a participant subset; default is broadcast. The
668
- * runtime currently publishes a single shared track; targeted delivery
669
- * lands with the per-listener track work (PR#4).
670
- */
671
- say(text: string, opts?: SayOptions): Promise<void>;
672
- /** Ask the LLM to produce a reply now (optionally with extra instructions). */
673
- respond(instructionsOrOpts?: string | RespondOptions): Promise<void>;
674
- /** Pose a question and resolve with the caller's transcribed answer. */
675
- ask(question: string): Promise<string>;
676
- /** Transfer the caller to another phone number. Ends the agent's leg.
677
- * `mode` overrides the agent-level warm/cold default for this transfer. */
678
- handoff(to: string, opts?: {
679
- reason?: string;
680
- briefing?: string;
681
- mode?: 'warm' | 'cold';
682
- }): Promise<void>;
683
- /**
684
- * Hang up the call. Optionally records a reason (surfaced to logs and
685
- * `endCall.onEnd`). Teardown is owned by the SDK end-call controller, which
686
- * deletes the whole LiveKit room by default so the SIP/PSTN leg drops too —
687
- * not just the agent participant.
688
- */
689
- endCall(opts?: {
690
- reason?: string;
691
- }): Promise<void>;
692
- /**
693
- * Subscribe to inbound DTMF keypresses from participants. Returns an
694
- * unsubscribe function. Handlers are called once per digit in the order
695
- * received. Errors thrown inside a handler are caught and logged so one
696
- * bad handler can't break others.
697
- *
698
- * @example
699
- * ctx.onDtmf(async (ev) => {
700
- * if (ev.digit === '0') await ctx.handoff('+18005551212');
701
- * });
702
- */
703
- onDtmf(handler: DtmfHandler): () => void;
704
- /**
705
- * Send DTMF digits from the agent's leg. Each character is published as
706
- * a separate RFC 4733 event; non-DTMF characters (anything not in
707
- * 0-9*#A-D) are dropped. `opts.delayMs` controls pacing between digits.
708
- *
709
- * Useful for IVR navigation post-handoff or during outbound calls.
710
- */
711
- sendDtmf(digits: string, opts?: SendDtmfOptions): Promise<void>;
712
- /** Escape hatch to LiveKit primitives — use when a hook isn't enough. */
713
- readonly livekit: {
714
- readonly job: JobContext;
715
- readonly session: voice.AgentSession;
716
- };
717
- /** Transport-agnostic session seam. When present, say/respond/ask/handoff and
718
- * the graph executors route through this instead of the raw LiveKit session
719
- * (the lift onto the omnichannel SessionAdapter). Undefined in legacy/unit
720
- * paths that build ctx with only a session. */
721
- readonly adapter?: SessionAdapter;
722
- }
723
- /**
724
- * Map a single character to its RFC 4733 event code, or null when the
725
- * character isn't a valid DTMF digit. 0–9 → 0–9, * → 10, # → 11, A–D → 12–15.
726
- */
727
- declare function dtmfDigitToCode(digit: string): number | null;
728
- /** Inverse of dtmfDigitToCode. Returns null for codes outside 0..15. */
729
- declare function dtmfCodeToDigit(code: number): string | null;
730
-
731
- /** What the agent passes into recordEvent — id/at filled in by the runtime. */
732
- interface RecordEventInput {
733
- readonly kind: string;
734
- readonly source?: ProcessLifecycleEvent['source'];
735
- readonly data?: Record<string, unknown>;
736
- }
737
- interface ProcessRuntime {
738
- /** Add field-collection guidance to the system prompt. */
739
- augmentPrompt(basePrompt: string): string;
740
- /** Run on each finalized caller utterance. */
741
- ingest(utterance: string, ctx: AgentContext): Promise<void>;
742
- /** Read the current captured data bag (used by handoff briefings). */
743
- getData(): Record<string, unknown>;
744
- /** True if the process completion gate has fired. */
745
- isComplete(): boolean;
746
- /**
747
- * Capture a field directly — bypasses the LLM extractor. Used by
748
- * non-voice sources (form-page websocket, prefill, etc.) so they drive
749
- * the same state machine as spoken utterances. Returns true if the
750
- * primitive accepted the value.
751
- */
752
- captureField(name: string, value: unknown): boolean;
753
- /**
754
- * Record a side-channel lifecycle event into the actor's context — tool
755
- * call, form-page activity, webhook dispatch. Triggers the snapshot
756
- * subscription so the dashboard timeline updates in real time.
757
- */
758
- recordEvent(event: RecordEventInput): void;
759
- /** @internal — current primitive snapshot (used by publishers). */
760
- getSnapshot(): ProcessSnapshot$1 | null;
761
- /** @internal — the compiled schema (used for publisher boot + dashboard registration). */
762
- getSchema(): ProcessSchema | null;
763
- /** @internal — subscribe to state transitions. Returns unsubscribe. */
764
- subscribe(listener: (snap: ProcessSnapshot$1) => void): () => void;
765
- }
766
-
767
- /** How the whole flow run finished. */
768
- type FlowOutcome = {
769
- readonly kind: 'completed';
770
- } | {
771
- readonly kind: 'ended';
772
- readonly nodeId: string;
773
- } | {
774
- readonly kind: 'handoff';
775
- readonly to: string;
776
- } | {
777
- readonly kind: 'hangup';
778
- } | {
779
- readonly kind: 'error';
780
- readonly nodeId: string;
781
- readonly message: string;
782
- };
783
-
784
- /** One graph-lifecycle event, for the builder's dry-run trace overlay. */
785
- interface GraphTraceEvent {
786
- readonly kind: string;
787
- readonly data: Record<string, unknown>;
788
- }
789
- interface RunTextSessionOptions {
790
- readonly program: FlowProgram;
791
- /** Process runtime for slot capture. Defaults to a no-op (say/llm/code flows). */
792
- readonly processRt?: ProcessRuntime;
793
- /** Sink for outbound agent text (say / generated replies). */
794
- readonly onAgentText: (text: string) => void | Promise<void>;
795
- /** Produce an autonomous LLM turn for generateReply (llm/clarify nodes). */
796
- readonly generate?: (instructions?: string) => Promise<string>;
797
- /** Optional call metadata surfaced on ctx.call. */
798
- readonly call?: {
799
- readonly callId: string;
800
- readonly metadata?: Record<string, unknown>;
801
- };
802
- readonly maxSteps?: number;
803
- /** Tap the graph lifecycle (node.entered / edge.traversed / notes) — the
804
- * same stream the live timeline records, surfaced for the dry-run trace. */
805
- readonly onTrace?: (event: GraphTraceEvent) => void;
806
- }
807
- interface TextSession {
808
- /** Feed an inbound user message (a final turn). */
809
- sendUserMessage(text: string): void;
810
- /** Resolves with the flow outcome when the graph completes/ends/hangs up. */
811
- readonly result: Promise<FlowOutcome>;
812
- /** End the session (hangup). */
813
- end(): void;
814
- }
815
- declare function runTextSession(opts: RunTextSessionOptions): TextSession;
816
- interface RunTextTranscriptOptions {
817
- readonly program: FlowProgram;
818
- /** Scripted caller turns, consumed in order at the flow's ask points. */
819
- readonly messages: readonly string[];
820
- readonly processRt?: ProcessRuntime;
821
- readonly generate?: (instructions?: string) => Promise<string>;
822
- readonly call?: RunTextSessionOptions['call'];
823
- readonly maxSteps?: number;
824
- }
825
- interface TextTranscriptResult {
826
- readonly replies: readonly string[];
827
- readonly outcome: FlowOutcome;
828
- /** Graph lifecycle in execution order — the dry-run trace. */
829
- readonly trace: readonly GraphTraceEvent[];
830
- }
831
- /** Batch-drive a flow over text with a fixed list of caller turns and collect the
832
- * agent's replies. The turns are queued (createUtteranceStream buffers), so the
833
- * flow consumes them in order as it reaches ask points. Stateless — the request/
834
- * response shape the text playground route uses. */
835
- declare function runTextTranscript(opts: RunTextTranscriptOptions): Promise<TextTranscriptResult>;
836
-
837
- export { duck as $, type AgentContext as A, type SessionOutbound as B, type CallInfo as C, type DtmfEvent as D, type EndCallControllerHandle as E, type FakeSessionAdapter as F, type SessionState as G, type SpeechResult as H, type InboundOptions as I, type SpeechTarget as J, type StateChangeEvent as K, LK_AGENT_STATE_MAP as L, type MemoryHandle as M, NON_BUSY_STATES as N, type TextTranscriptResult as O, type Participant as P, type TtsVoiceConfig as Q, type RespondOptions as R, type SayOptions as S, type TextSession as T, type TurnEvent as U, type Unsubscribe as V, type UserInputEvent as W, type UserTurn as X, applySpeechTurnHandling as Y, dtmfCodeToDigit as Z, dtmfDigitToCode as _, type AgentTurnEvent as a, mute as a0, normalizeState as a1, passthrough as a2, runTextSession as a3, runTextTranscript as a4, transform as a5, type GraphTraceEvent as a6, type AudioProfile as b, type AudioSurface as c, type ChannelKind as d, type DtmfHandler as e, type EndCallOutcome as f, type FloorControlConfig as g, type MetricsEvent as h, ParticipantId as i, type ParticipantKind as j, type ParticipantSet as k, type ProcessSnapshot as l, type Room as m, type RouteEntry as n, type RoutingRule as o, type RoutingSnapshot as p, type RunTextSessionOptions as q, type RunTextTranscriptOptions as r, type SendDtmfOptions as s, type SessionAdapter as t, type SessionCapabilities as u, type SessionErrorEvent as v, type SessionEvent as w, type SessionEventMap as x, type SessionFactory as y, type SessionFactoryInput as z };