realtime-voice-agents 2.1.0 → 2.3.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 +52 -2
- package/dist/gemini.cjs +14 -5
- package/dist/gemini.d.cts +6 -1
- package/dist/gemini.d.mts +6 -1
- package/dist/gemini.mjs +14 -5
- package/dist/index.cjs +215 -14
- package/dist/index.d.cts +187 -1
- package/dist/index.d.mts +187 -1
- package/dist/index.mjs +211 -15
- package/dist/openai.cjs +14 -5
- package/dist/openai.d.cts +8 -1
- package/dist/openai.d.mts +8 -1
- package/dist/openai.mjs +14 -5
- package/dist/testing.cjs +29 -2
- package/dist/testing.d.cts +30 -0
- package/dist/testing.d.mts +30 -0
- package/dist/testing.mjs +29 -2
- package/dist/xai.cjs +14 -5
- package/dist/xai.d.cts +6 -1
- package/dist/xai.d.mts +6 -1
- package/dist/xai.mjs +14 -5
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -193,6 +193,124 @@ declare function decorateTool(tool: Tool, middlewares: readonly ToolMiddleware[]
|
|
|
193
193
|
/** Compose wrapExecute chains around an innermost executor. */
|
|
194
194
|
declare function composeExecution(tool: Tool, input: unknown, ctx: ToolContext, middlewares: readonly ToolMiddleware[], innermost: () => Promise<unknown>): Promise<unknown>;
|
|
195
195
|
//#endregion
|
|
196
|
+
//#region src/dtmf/KeypadCollector.d.ts
|
|
197
|
+
/**
|
|
198
|
+
* KeypadCollector — turns single DTMF keypresses into complete keypad entries.
|
|
199
|
+
*
|
|
200
|
+
* Callers type digits with ~1s pauses between them; the model should hear the
|
|
201
|
+
* whole number once, not ten fragments. The collector buffers digits and
|
|
202
|
+
* completes an entry on the submit key (`#`), on `maxDigits`, or after
|
|
203
|
+
* `interDigitTimeoutMs` of no keypresses (the caller stopped without `#` —
|
|
204
|
+
* flushing lets the agent react, typically "that's only N digits, please
|
|
205
|
+
* retype"). The clear key (`*`) discards the buffer so the caller can start
|
|
206
|
+
* over. Letters A–D and anything exotic are ignored.
|
|
207
|
+
*
|
|
208
|
+
* Pure state machine: no session, no transport. `CallSession` feeds it from
|
|
209
|
+
* the Twilio `dtmf` frame (before emitting the raw `dtmf` event), turns the
|
|
210
|
+
* hooks into `keypad.entry` / `keypad.cleared` events, and injects the default
|
|
211
|
+
* user-turn message. Field-tested shape (workshop DTMF agents, Aug 2026).
|
|
212
|
+
*/
|
|
213
|
+
type KeypadEntryReason = 'submit' | 'timeout' | 'maxDigits';
|
|
214
|
+
interface KeypadEntry {
|
|
215
|
+
/** The digits typed, in order (0–9 only). */
|
|
216
|
+
digits: string;
|
|
217
|
+
/** What completed the entry: the submit key, the inter-digit timeout, or `maxDigits`. */
|
|
218
|
+
reason: KeypadEntryReason;
|
|
219
|
+
}
|
|
220
|
+
interface KeypadOptions {
|
|
221
|
+
/** Key that submits the current entry immediately. Default `'#'`. */
|
|
222
|
+
submitKey?: string;
|
|
223
|
+
/** Key that discards the current entry so the caller can start over. Default `'*'`. */
|
|
224
|
+
clearKey?: string;
|
|
225
|
+
/**
|
|
226
|
+
* Silence after the last keypress that completes the entry anyway. Callers
|
|
227
|
+
* pause ~1s between digits; default 4000.
|
|
228
|
+
*/
|
|
229
|
+
interDigitTimeoutMs?: number;
|
|
230
|
+
/**
|
|
231
|
+
* Auto-submit once this many digits are buffered (an ID or phone number of
|
|
232
|
+
* known length — the caller never has to press `#`). Default: unlimited.
|
|
233
|
+
*/
|
|
234
|
+
maxDigits?: number;
|
|
235
|
+
/**
|
|
236
|
+
* Stop the agent mid-sentence on every keypress — typing means "I'm
|
|
237
|
+
* answering". Bypasses the interruption guard like `session.interrupt()`.
|
|
238
|
+
* Without it a flushed entry queues its readback behind stale speech.
|
|
239
|
+
* Default true.
|
|
240
|
+
*/
|
|
241
|
+
interruptOnKeypress?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* How a completed entry reaches the model: a function returning the text of
|
|
244
|
+
* the user turn injected for it (default `defaultKeypadMessage`), or `false`
|
|
245
|
+
* to inject nothing and handle `keypad.entry` yourself.
|
|
246
|
+
*/
|
|
247
|
+
message?: ((entry: KeypadEntry) => string) | false;
|
|
248
|
+
/**
|
|
249
|
+
* User turn injected when the caller presses the clear key (default text
|
|
250
|
+
* tells the model the caller is starting over), or `false` for none.
|
|
251
|
+
*/
|
|
252
|
+
clearMessage?: string | false;
|
|
253
|
+
/**
|
|
254
|
+
* Appended to the agent instructions so the model knows what `[keypad]`
|
|
255
|
+
* messages are (default `DEFAULT_KEYPAD_INSTRUCTIONS`), or `false` to leave
|
|
256
|
+
* the instructions untouched — say it in your own prompt instead.
|
|
257
|
+
*/
|
|
258
|
+
instructions?: string | false;
|
|
259
|
+
}
|
|
260
|
+
/** Host-facing handle: `session.keypad`. */
|
|
261
|
+
interface KeypadHandle {
|
|
262
|
+
/** Digits buffered so far (empty when keypad input is not configured). */
|
|
263
|
+
readonly digits: string;
|
|
264
|
+
/** Discard the buffer silently — no event, no message to the model. */
|
|
265
|
+
clear(): void;
|
|
266
|
+
/** Complete the buffered entry now (reason `'submit'`); no-op when empty. */
|
|
267
|
+
submit(): void;
|
|
268
|
+
}
|
|
269
|
+
declare const DEFAULT_KEYPAD_OPTIONS: {
|
|
270
|
+
readonly submitKey: "#";
|
|
271
|
+
readonly clearKey: "*";
|
|
272
|
+
readonly interDigitTimeoutMs: 4000;
|
|
273
|
+
readonly interruptOnKeypress: true;
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Appended to the agent instructions when keypad input is enabled. Short and
|
|
277
|
+
* neutral on purpose: what the messages are, not how to run the dialog.
|
|
278
|
+
*/
|
|
279
|
+
declare const DEFAULT_KEYPAD_INSTRUCTIONS: string;
|
|
280
|
+
/** Default text of the user turn injected for a completed entry. */
|
|
281
|
+
declare function defaultKeypadMessage(entry: KeypadEntry): string;
|
|
282
|
+
/** Default text of the user turn injected when the caller presses the clear key. */
|
|
283
|
+
declare const DEFAULT_KEYPAD_CLEAR_MESSAGE = "[keypad] I pressed star — I want to start over and retype from the beginning.";
|
|
284
|
+
/** What a keypress meant to the collector. */
|
|
285
|
+
type KeypadKeyKind = 'digit' | 'submit' | 'clear' | 'ignored';
|
|
286
|
+
interface KeypadCollectorHooks {
|
|
287
|
+
onEntry: (entry: KeypadEntry) => void;
|
|
288
|
+
onClear: (info: {
|
|
289
|
+
discarded: string;
|
|
290
|
+
}) => void;
|
|
291
|
+
}
|
|
292
|
+
declare class KeypadCollector implements KeypadHandle {
|
|
293
|
+
private readonly submitKey;
|
|
294
|
+
private readonly clearKey;
|
|
295
|
+
private readonly interDigitTimeoutMs;
|
|
296
|
+
private readonly maxDigits;
|
|
297
|
+
private readonly hooks;
|
|
298
|
+
private buffer;
|
|
299
|
+
private timer;
|
|
300
|
+
private disposed;
|
|
301
|
+
constructor(options: KeypadOptions, hooks: KeypadCollectorHooks);
|
|
302
|
+
get digits(): string;
|
|
303
|
+
/** Feed one keypress (a Twilio `dtmf` frame). Returns what the key meant. */
|
|
304
|
+
press(key: string): KeypadKeyKind;
|
|
305
|
+
clear(): void;
|
|
306
|
+
submit(): void;
|
|
307
|
+
/** Release the timer; pending digits are dropped (the call is over). */
|
|
308
|
+
dispose(): void;
|
|
309
|
+
private complete;
|
|
310
|
+
private armTimer;
|
|
311
|
+
private cancelTimer;
|
|
312
|
+
}
|
|
313
|
+
//#endregion
|
|
196
314
|
//#region src/interruption/InterruptionController.d.ts
|
|
197
315
|
/**
|
|
198
316
|
* InterruptionController — decides whether a barge-in is honored.
|
|
@@ -462,6 +580,15 @@ interface SessionOptions {
|
|
|
462
580
|
* this also serializes ALL mid-call session updates for the session.
|
|
463
581
|
*/
|
|
464
582
|
noiseAdaptiveVad?: NoiseAdaptiveVadOptions;
|
|
583
|
+
/**
|
|
584
|
+
* Opt-in keypad (DTMF) input: buffer keypresses into complete entries —
|
|
585
|
+
* `#` submits, `*` clears, `maxDigits` auto-submits, 4s of no keypresses
|
|
586
|
+
* flushes — stop the agent on every keypress, and inject each entry as a
|
|
587
|
+
* `[keypad] ...` user turn the model answers. Emits `keypad.entry` /
|
|
588
|
+
* `keypad.cleared`; the raw `dtmf` event keeps firing per key. Set `{}` to
|
|
589
|
+
* enable with defaults; unset = raw `dtmf` events only, as before.
|
|
590
|
+
*/
|
|
591
|
+
keypad?: KeypadOptions;
|
|
465
592
|
/**
|
|
466
593
|
* `afterPlayback` (default): tool results wait until current agent audio
|
|
467
594
|
* finishes playing. `immediate`: send as soon as the tool completes.
|
|
@@ -535,6 +662,19 @@ interface BridgeConfig {
|
|
|
535
662
|
/** The (root) agent, or a resolver for multi-tenant routing per call. */
|
|
536
663
|
agent: Agent | ((start: TwilioStartEvent) => Agent | Promise<Agent>);
|
|
537
664
|
provider: ProviderFactory;
|
|
665
|
+
/**
|
|
666
|
+
* Backup providers tried in order when `provider` fails to come up at call
|
|
667
|
+
* start (bad key, exhausted quota, outage, connect timeout). Each attempt
|
|
668
|
+
* emits `provider.fallback`; the call fails only when every fallback is
|
|
669
|
+
* exhausted. Connect-time only: once a provider answers, the call stays
|
|
670
|
+
* with it (mid-call reconnects keep using the same provider).
|
|
671
|
+
*
|
|
672
|
+
* ```ts
|
|
673
|
+
* provider: openaiRealtime(),
|
|
674
|
+
* fallbacks: [xaiRealtime(), geminiLive()],
|
|
675
|
+
* ```
|
|
676
|
+
*/
|
|
677
|
+
fallbacks?: ProviderFactory[];
|
|
538
678
|
session?: Partial<SessionOptions> | ((start: TwilioStartEvent) => Partial<SessionOptions> | Promise<Partial<SessionOptions>>);
|
|
539
679
|
/** Twilio REST credentials — enables clean hangup, transfer, SMS/WhatsApp. */
|
|
540
680
|
twilio?: TwilioRestOptions;
|
|
@@ -593,6 +733,19 @@ interface ApprovalRequestInfo {
|
|
|
593
733
|
type VadSuggestionInfo = VadAdjustment & {
|
|
594
734
|
willAutoApply: boolean;
|
|
595
735
|
};
|
|
736
|
+
/**
|
|
737
|
+
* While ESTABLISHING the call, a provider failed to come up and the session
|
|
738
|
+
* is trying the next factory in the fallback chain. Fires only before the
|
|
739
|
+
* call is active — once a provider has connected, the call stays with it.
|
|
740
|
+
*/
|
|
741
|
+
interface ProviderFallbackInfo {
|
|
742
|
+
/** Name of the provider whose connect failed. */
|
|
743
|
+
from: string;
|
|
744
|
+
/** Name of the provider being tried instead. */
|
|
745
|
+
to: string;
|
|
746
|
+
/** The connect error that triggered the fallback. */
|
|
747
|
+
error: Error;
|
|
748
|
+
}
|
|
596
749
|
interface SessionEventMap {
|
|
597
750
|
'call.started': (info: CallStartedInfo) => void;
|
|
598
751
|
'call.ended': (info: {
|
|
@@ -611,6 +764,8 @@ interface SessionEventMap {
|
|
|
611
764
|
code?: number;
|
|
612
765
|
reason?: string;
|
|
613
766
|
}) => void;
|
|
767
|
+
/** Connect-time fallback: trying the next provider in the configured chain. */
|
|
768
|
+
'provider.fallback': (info: ProviderFallbackInfo) => void;
|
|
614
769
|
/** Generation-side: the model started/finished producing a response. */
|
|
615
770
|
'agent.speech.started': (info: {
|
|
616
771
|
responseId: string;
|
|
@@ -660,9 +815,16 @@ interface SessionEventMap {
|
|
|
660
815
|
'background_audio.stopped': (info: {
|
|
661
816
|
preset?: string;
|
|
662
817
|
}) => void;
|
|
818
|
+
/** Raw keypress (every Twilio dtmf frame). With `keypad` configured it fires AFTER the collector consumed the key. */
|
|
663
819
|
dtmf: (info: {
|
|
664
820
|
digit: string;
|
|
665
821
|
}) => void;
|
|
822
|
+
/** A complete keypad entry (`keypad` option): submit key, `maxDigits`, or inter-digit timeout. */
|
|
823
|
+
'keypad.entry': (entry: KeypadEntry) => void;
|
|
824
|
+
/** The caller pressed the clear key; `discarded` is what the buffer held. */
|
|
825
|
+
'keypad.cleared': (info: {
|
|
826
|
+
discarded: string;
|
|
827
|
+
}) => void;
|
|
666
828
|
'usage.updated': (usage: UsageInfo, delta: ProviderUsage) => void;
|
|
667
829
|
error: (error: Error) => void;
|
|
668
830
|
}
|
|
@@ -683,6 +845,8 @@ interface CallSessionDeps {
|
|
|
683
845
|
transport: TwilioMediaTransport;
|
|
684
846
|
start: TwilioStartEvent;
|
|
685
847
|
providerFactory: ProviderFactory;
|
|
848
|
+
/** Backup factories tried in order when `providerFactory` fails to connect. */
|
|
849
|
+
fallbacks?: readonly ProviderFactory[];
|
|
686
850
|
agent: Agent;
|
|
687
851
|
options: SessionOptions;
|
|
688
852
|
store: SessionStore;
|
|
@@ -699,11 +863,17 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
699
863
|
readonly streamSid: string;
|
|
700
864
|
readonly callInfo: ToolCallInfo;
|
|
701
865
|
readonly context: SessionContext;
|
|
866
|
+
/**
|
|
867
|
+
* Keypad (DTMF) input handle: digits buffered so far, `clear()`, `submit()`.
|
|
868
|
+
* Inert (empty, no-ops) unless the `keypad` session option is configured.
|
|
869
|
+
*/
|
|
870
|
+
readonly keypad: KeypadHandle;
|
|
702
871
|
private stateValue;
|
|
703
872
|
private readonly deps;
|
|
704
873
|
private readonly log;
|
|
705
874
|
private readonly tracker;
|
|
706
875
|
private readonly interruptions;
|
|
876
|
+
private readonly keypadCollector;
|
|
707
877
|
private readonly usageAccumulator;
|
|
708
878
|
private readonly toolQueue;
|
|
709
879
|
private readonly transcriptEntries;
|
|
@@ -713,6 +883,8 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
713
883
|
private readonly runningTools;
|
|
714
884
|
private readonly timers;
|
|
715
885
|
private provider;
|
|
886
|
+
/** Primary + fallbacks, in try-order. Only walked while connecting. */
|
|
887
|
+
private readonly providerChain;
|
|
716
888
|
private activeAgentValue;
|
|
717
889
|
private generating;
|
|
718
890
|
private currentResponseId;
|
|
@@ -772,6 +944,15 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
772
944
|
get transcript(): readonly TranscriptEntry[];
|
|
773
945
|
/** Connect the provider and activate the call. Called by the bridge. */
|
|
774
946
|
begin(): Promise<void>;
|
|
947
|
+
/**
|
|
948
|
+
* Walk primary + fallbacks until one connects. A provider that fails to
|
|
949
|
+
* come up is detached BEFORE the chain advances, so a half-open socket's
|
|
950
|
+
* late events can never reach the session once the next provider owns the
|
|
951
|
+
* call. Connect-time only by design: once a provider has answered, the
|
|
952
|
+
* call stays with it. Returns false after failing the call (chain
|
|
953
|
+
* exhausted) or when the call was torn down while connecting.
|
|
954
|
+
*/
|
|
955
|
+
private connectInitialProvider;
|
|
775
956
|
/** The outbound leg was answered (host's Twilio status callback). */
|
|
776
957
|
notifyAnswered(): void;
|
|
777
958
|
sendText(text: string, options?: {
|
|
@@ -822,6 +1003,8 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
822
1003
|
/** Immediate teardown (no goodbye). */
|
|
823
1004
|
end(reason?: CallEndReason): Promise<void>;
|
|
824
1005
|
private buildToolset;
|
|
1006
|
+
/** Agent instructions plus the kit's own notes that must survive handoffs (keypad). */
|
|
1007
|
+
private composeInstructions;
|
|
825
1008
|
private buildProviderInit;
|
|
826
1009
|
private wireTransport;
|
|
827
1010
|
/** Emit 'error'; when the host attached no listener, log instead of losing it. */
|
|
@@ -893,6 +1076,9 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
893
1076
|
* starts immediately while the provider session is still being built.
|
|
894
1077
|
*/
|
|
895
1078
|
private playPreGreeting;
|
|
1079
|
+
private handleKeypress;
|
|
1080
|
+
private onKeypadEntry;
|
|
1081
|
+
private onKeypadCleared;
|
|
896
1082
|
/** Armed whenever the agent goes quiet and we're waiting on the caller. */
|
|
897
1083
|
private armIdleTimer;
|
|
898
1084
|
private clearIdleTimer;
|
|
@@ -1085,4 +1271,4 @@ declare class PlaybackTracker {
|
|
|
1085
1271
|
private maybeForget;
|
|
1086
1272
|
}
|
|
1087
1273
|
//#endregion
|
|
1088
|
-
export { Agent, type AgentDefinition, type ApprovalRequestInfo, type BackgroundAudioOptions, type BackgroundAudioPreset, type BackgroundAudioSpec, BaseRealtimeProvider, type BridgeConfig, type BridgeEventMap, type BuiltinToolsConfig, type CallEndReason, CallSession, type CallSessionFacade, type CallSnapshot, type CallStartedInfo, type CallState, type CaptureGreetingOptions, type CapturedGreeting, DEFAULT_RECONNECT_POLICY, DEFAULT_SESSION_OPTIONS, type DeafnessOptions, type FinishCallToolOptions, type GreetingOptions, type HandoffDirective, type HangupOptions, type IdleOptions, InMemorySessionStore, type InferSchema, type InterruptionBlockCause, InterruptionController, type InterruptionDecision, type InterruptionRateLimit, type InterruptionSettings, type Logger, type MarkEchoResult, NoiseAdaptiveVadController, type NoiseAdaptiveVadOptions, PlaybackTracker, type ProviderAudioDelta, type ProviderCapabilities, type ProviderCloseInfo, type ProviderEvents, type ProviderFactory, type ProviderFactoryContext, type ProviderSessionInit, type ProviderToolCall, type ProviderToolSchema, type ProviderUsage, type ReconnectPolicy, type SendTextOptions, type SendToolResultOptions, SessionContext, type SessionEventMap, type SessionOptions, type SessionStore, type SessionUpdateOptions, type Tool, type ToolCallInfo, type ToolContext, type ToolDecoration, type ToolDefinition, type ToolMiddleware, type ToolRunInfo, type ToolStrategy, type TranscriptEntry, type TransferCallToolOptions, TwilioRealtimeBridge, type TwilioRestOptions, type TwilioStartEvent, type UsageInfo, type VadAdjustment, type VadConfig, type VadNoiseMetrics, type VadSuggestionInfo, type VadTuningProfile, type WebSocketLike, type ZodSchemaLike, captureGreetingAudio, collectAgentGraph, composeExecution, connectStreamTwiml, consoleLogger, createFinishCallTool, createHandoffTool, createTransferCallTool, decorateTool, emptyUsage, handoffToolName, isHandoffDirective, noopLogger, resolveSessionOptions, tool, zodToJsonSchema };
|
|
1274
|
+
export { Agent, type AgentDefinition, type ApprovalRequestInfo, type BackgroundAudioOptions, type BackgroundAudioPreset, type BackgroundAudioSpec, BaseRealtimeProvider, type BridgeConfig, type BridgeEventMap, type BuiltinToolsConfig, type CallEndReason, CallSession, type CallSessionFacade, type CallSnapshot, type CallStartedInfo, type CallState, type CaptureGreetingOptions, type CapturedGreeting, DEFAULT_KEYPAD_CLEAR_MESSAGE, DEFAULT_KEYPAD_INSTRUCTIONS, DEFAULT_KEYPAD_OPTIONS, DEFAULT_RECONNECT_POLICY, DEFAULT_SESSION_OPTIONS, type DeafnessOptions, type FinishCallToolOptions, type GreetingOptions, type HandoffDirective, type HangupOptions, type IdleOptions, InMemorySessionStore, type InferSchema, type InterruptionBlockCause, InterruptionController, type InterruptionDecision, type InterruptionRateLimit, type InterruptionSettings, KeypadCollector, type KeypadCollectorHooks, type KeypadEntry, type KeypadEntryReason, type KeypadHandle, type KeypadKeyKind, type KeypadOptions, type Logger, type MarkEchoResult, NoiseAdaptiveVadController, type NoiseAdaptiveVadOptions, PlaybackTracker, type ProviderAudioDelta, type ProviderCapabilities, type ProviderCloseInfo, type ProviderEvents, type ProviderFactory, type ProviderFactoryContext, type ProviderFallbackInfo, type ProviderSessionInit, type ProviderToolCall, type ProviderToolSchema, type ProviderUsage, type ReconnectPolicy, type SendTextOptions, type SendToolResultOptions, SessionContext, type SessionEventMap, type SessionOptions, type SessionStore, type SessionUpdateOptions, type Tool, type ToolCallInfo, type ToolContext, type ToolDecoration, type ToolDefinition, type ToolMiddleware, type ToolRunInfo, type ToolStrategy, type TranscriptEntry, type TransferCallToolOptions, TwilioRealtimeBridge, type TwilioRestOptions, type TwilioStartEvent, type UsageInfo, type VadAdjustment, type VadConfig, type VadNoiseMetrics, type VadSuggestionInfo, type VadTuningProfile, type WebSocketLike, type ZodSchemaLike, captureGreetingAudio, collectAgentGraph, composeExecution, connectStreamTwiml, consoleLogger, createFinishCallTool, createHandoffTool, createTransferCallTool, decorateTool, defaultKeypadMessage, emptyUsage, handoffToolName, isHandoffDirective, noopLogger, resolveSessionOptions, tool, zodToJsonSchema };
|
package/dist/index.d.mts
CHANGED
|
@@ -193,6 +193,124 @@ declare function decorateTool(tool: Tool, middlewares: readonly ToolMiddleware[]
|
|
|
193
193
|
/** Compose wrapExecute chains around an innermost executor. */
|
|
194
194
|
declare function composeExecution(tool: Tool, input: unknown, ctx: ToolContext, middlewares: readonly ToolMiddleware[], innermost: () => Promise<unknown>): Promise<unknown>;
|
|
195
195
|
//#endregion
|
|
196
|
+
//#region src/dtmf/KeypadCollector.d.ts
|
|
197
|
+
/**
|
|
198
|
+
* KeypadCollector — turns single DTMF keypresses into complete keypad entries.
|
|
199
|
+
*
|
|
200
|
+
* Callers type digits with ~1s pauses between them; the model should hear the
|
|
201
|
+
* whole number once, not ten fragments. The collector buffers digits and
|
|
202
|
+
* completes an entry on the submit key (`#`), on `maxDigits`, or after
|
|
203
|
+
* `interDigitTimeoutMs` of no keypresses (the caller stopped without `#` —
|
|
204
|
+
* flushing lets the agent react, typically "that's only N digits, please
|
|
205
|
+
* retype"). The clear key (`*`) discards the buffer so the caller can start
|
|
206
|
+
* over. Letters A–D and anything exotic are ignored.
|
|
207
|
+
*
|
|
208
|
+
* Pure state machine: no session, no transport. `CallSession` feeds it from
|
|
209
|
+
* the Twilio `dtmf` frame (before emitting the raw `dtmf` event), turns the
|
|
210
|
+
* hooks into `keypad.entry` / `keypad.cleared` events, and injects the default
|
|
211
|
+
* user-turn message. Field-tested shape (workshop DTMF agents, Aug 2026).
|
|
212
|
+
*/
|
|
213
|
+
type KeypadEntryReason = 'submit' | 'timeout' | 'maxDigits';
|
|
214
|
+
interface KeypadEntry {
|
|
215
|
+
/** The digits typed, in order (0–9 only). */
|
|
216
|
+
digits: string;
|
|
217
|
+
/** What completed the entry: the submit key, the inter-digit timeout, or `maxDigits`. */
|
|
218
|
+
reason: KeypadEntryReason;
|
|
219
|
+
}
|
|
220
|
+
interface KeypadOptions {
|
|
221
|
+
/** Key that submits the current entry immediately. Default `'#'`. */
|
|
222
|
+
submitKey?: string;
|
|
223
|
+
/** Key that discards the current entry so the caller can start over. Default `'*'`. */
|
|
224
|
+
clearKey?: string;
|
|
225
|
+
/**
|
|
226
|
+
* Silence after the last keypress that completes the entry anyway. Callers
|
|
227
|
+
* pause ~1s between digits; default 4000.
|
|
228
|
+
*/
|
|
229
|
+
interDigitTimeoutMs?: number;
|
|
230
|
+
/**
|
|
231
|
+
* Auto-submit once this many digits are buffered (an ID or phone number of
|
|
232
|
+
* known length — the caller never has to press `#`). Default: unlimited.
|
|
233
|
+
*/
|
|
234
|
+
maxDigits?: number;
|
|
235
|
+
/**
|
|
236
|
+
* Stop the agent mid-sentence on every keypress — typing means "I'm
|
|
237
|
+
* answering". Bypasses the interruption guard like `session.interrupt()`.
|
|
238
|
+
* Without it a flushed entry queues its readback behind stale speech.
|
|
239
|
+
* Default true.
|
|
240
|
+
*/
|
|
241
|
+
interruptOnKeypress?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* How a completed entry reaches the model: a function returning the text of
|
|
244
|
+
* the user turn injected for it (default `defaultKeypadMessage`), or `false`
|
|
245
|
+
* to inject nothing and handle `keypad.entry` yourself.
|
|
246
|
+
*/
|
|
247
|
+
message?: ((entry: KeypadEntry) => string) | false;
|
|
248
|
+
/**
|
|
249
|
+
* User turn injected when the caller presses the clear key (default text
|
|
250
|
+
* tells the model the caller is starting over), or `false` for none.
|
|
251
|
+
*/
|
|
252
|
+
clearMessage?: string | false;
|
|
253
|
+
/**
|
|
254
|
+
* Appended to the agent instructions so the model knows what `[keypad]`
|
|
255
|
+
* messages are (default `DEFAULT_KEYPAD_INSTRUCTIONS`), or `false` to leave
|
|
256
|
+
* the instructions untouched — say it in your own prompt instead.
|
|
257
|
+
*/
|
|
258
|
+
instructions?: string | false;
|
|
259
|
+
}
|
|
260
|
+
/** Host-facing handle: `session.keypad`. */
|
|
261
|
+
interface KeypadHandle {
|
|
262
|
+
/** Digits buffered so far (empty when keypad input is not configured). */
|
|
263
|
+
readonly digits: string;
|
|
264
|
+
/** Discard the buffer silently — no event, no message to the model. */
|
|
265
|
+
clear(): void;
|
|
266
|
+
/** Complete the buffered entry now (reason `'submit'`); no-op when empty. */
|
|
267
|
+
submit(): void;
|
|
268
|
+
}
|
|
269
|
+
declare const DEFAULT_KEYPAD_OPTIONS: {
|
|
270
|
+
readonly submitKey: "#";
|
|
271
|
+
readonly clearKey: "*";
|
|
272
|
+
readonly interDigitTimeoutMs: 4000;
|
|
273
|
+
readonly interruptOnKeypress: true;
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Appended to the agent instructions when keypad input is enabled. Short and
|
|
277
|
+
* neutral on purpose: what the messages are, not how to run the dialog.
|
|
278
|
+
*/
|
|
279
|
+
declare const DEFAULT_KEYPAD_INSTRUCTIONS: string;
|
|
280
|
+
/** Default text of the user turn injected for a completed entry. */
|
|
281
|
+
declare function defaultKeypadMessage(entry: KeypadEntry): string;
|
|
282
|
+
/** Default text of the user turn injected when the caller presses the clear key. */
|
|
283
|
+
declare const DEFAULT_KEYPAD_CLEAR_MESSAGE = "[keypad] I pressed star — I want to start over and retype from the beginning.";
|
|
284
|
+
/** What a keypress meant to the collector. */
|
|
285
|
+
type KeypadKeyKind = 'digit' | 'submit' | 'clear' | 'ignored';
|
|
286
|
+
interface KeypadCollectorHooks {
|
|
287
|
+
onEntry: (entry: KeypadEntry) => void;
|
|
288
|
+
onClear: (info: {
|
|
289
|
+
discarded: string;
|
|
290
|
+
}) => void;
|
|
291
|
+
}
|
|
292
|
+
declare class KeypadCollector implements KeypadHandle {
|
|
293
|
+
private readonly submitKey;
|
|
294
|
+
private readonly clearKey;
|
|
295
|
+
private readonly interDigitTimeoutMs;
|
|
296
|
+
private readonly maxDigits;
|
|
297
|
+
private readonly hooks;
|
|
298
|
+
private buffer;
|
|
299
|
+
private timer;
|
|
300
|
+
private disposed;
|
|
301
|
+
constructor(options: KeypadOptions, hooks: KeypadCollectorHooks);
|
|
302
|
+
get digits(): string;
|
|
303
|
+
/** Feed one keypress (a Twilio `dtmf` frame). Returns what the key meant. */
|
|
304
|
+
press(key: string): KeypadKeyKind;
|
|
305
|
+
clear(): void;
|
|
306
|
+
submit(): void;
|
|
307
|
+
/** Release the timer; pending digits are dropped (the call is over). */
|
|
308
|
+
dispose(): void;
|
|
309
|
+
private complete;
|
|
310
|
+
private armTimer;
|
|
311
|
+
private cancelTimer;
|
|
312
|
+
}
|
|
313
|
+
//#endregion
|
|
196
314
|
//#region src/interruption/InterruptionController.d.ts
|
|
197
315
|
/**
|
|
198
316
|
* InterruptionController — decides whether a barge-in is honored.
|
|
@@ -462,6 +580,15 @@ interface SessionOptions {
|
|
|
462
580
|
* this also serializes ALL mid-call session updates for the session.
|
|
463
581
|
*/
|
|
464
582
|
noiseAdaptiveVad?: NoiseAdaptiveVadOptions;
|
|
583
|
+
/**
|
|
584
|
+
* Opt-in keypad (DTMF) input: buffer keypresses into complete entries —
|
|
585
|
+
* `#` submits, `*` clears, `maxDigits` auto-submits, 4s of no keypresses
|
|
586
|
+
* flushes — stop the agent on every keypress, and inject each entry as a
|
|
587
|
+
* `[keypad] ...` user turn the model answers. Emits `keypad.entry` /
|
|
588
|
+
* `keypad.cleared`; the raw `dtmf` event keeps firing per key. Set `{}` to
|
|
589
|
+
* enable with defaults; unset = raw `dtmf` events only, as before.
|
|
590
|
+
*/
|
|
591
|
+
keypad?: KeypadOptions;
|
|
465
592
|
/**
|
|
466
593
|
* `afterPlayback` (default): tool results wait until current agent audio
|
|
467
594
|
* finishes playing. `immediate`: send as soon as the tool completes.
|
|
@@ -535,6 +662,19 @@ interface BridgeConfig {
|
|
|
535
662
|
/** The (root) agent, or a resolver for multi-tenant routing per call. */
|
|
536
663
|
agent: Agent | ((start: TwilioStartEvent) => Agent | Promise<Agent>);
|
|
537
664
|
provider: ProviderFactory;
|
|
665
|
+
/**
|
|
666
|
+
* Backup providers tried in order when `provider` fails to come up at call
|
|
667
|
+
* start (bad key, exhausted quota, outage, connect timeout). Each attempt
|
|
668
|
+
* emits `provider.fallback`; the call fails only when every fallback is
|
|
669
|
+
* exhausted. Connect-time only: once a provider answers, the call stays
|
|
670
|
+
* with it (mid-call reconnects keep using the same provider).
|
|
671
|
+
*
|
|
672
|
+
* ```ts
|
|
673
|
+
* provider: openaiRealtime(),
|
|
674
|
+
* fallbacks: [xaiRealtime(), geminiLive()],
|
|
675
|
+
* ```
|
|
676
|
+
*/
|
|
677
|
+
fallbacks?: ProviderFactory[];
|
|
538
678
|
session?: Partial<SessionOptions> | ((start: TwilioStartEvent) => Partial<SessionOptions> | Promise<Partial<SessionOptions>>);
|
|
539
679
|
/** Twilio REST credentials — enables clean hangup, transfer, SMS/WhatsApp. */
|
|
540
680
|
twilio?: TwilioRestOptions;
|
|
@@ -593,6 +733,19 @@ interface ApprovalRequestInfo {
|
|
|
593
733
|
type VadSuggestionInfo = VadAdjustment & {
|
|
594
734
|
willAutoApply: boolean;
|
|
595
735
|
};
|
|
736
|
+
/**
|
|
737
|
+
* While ESTABLISHING the call, a provider failed to come up and the session
|
|
738
|
+
* is trying the next factory in the fallback chain. Fires only before the
|
|
739
|
+
* call is active — once a provider has connected, the call stays with it.
|
|
740
|
+
*/
|
|
741
|
+
interface ProviderFallbackInfo {
|
|
742
|
+
/** Name of the provider whose connect failed. */
|
|
743
|
+
from: string;
|
|
744
|
+
/** Name of the provider being tried instead. */
|
|
745
|
+
to: string;
|
|
746
|
+
/** The connect error that triggered the fallback. */
|
|
747
|
+
error: Error;
|
|
748
|
+
}
|
|
596
749
|
interface SessionEventMap {
|
|
597
750
|
'call.started': (info: CallStartedInfo) => void;
|
|
598
751
|
'call.ended': (info: {
|
|
@@ -611,6 +764,8 @@ interface SessionEventMap {
|
|
|
611
764
|
code?: number;
|
|
612
765
|
reason?: string;
|
|
613
766
|
}) => void;
|
|
767
|
+
/** Connect-time fallback: trying the next provider in the configured chain. */
|
|
768
|
+
'provider.fallback': (info: ProviderFallbackInfo) => void;
|
|
614
769
|
/** Generation-side: the model started/finished producing a response. */
|
|
615
770
|
'agent.speech.started': (info: {
|
|
616
771
|
responseId: string;
|
|
@@ -660,9 +815,16 @@ interface SessionEventMap {
|
|
|
660
815
|
'background_audio.stopped': (info: {
|
|
661
816
|
preset?: string;
|
|
662
817
|
}) => void;
|
|
818
|
+
/** Raw keypress (every Twilio dtmf frame). With `keypad` configured it fires AFTER the collector consumed the key. */
|
|
663
819
|
dtmf: (info: {
|
|
664
820
|
digit: string;
|
|
665
821
|
}) => void;
|
|
822
|
+
/** A complete keypad entry (`keypad` option): submit key, `maxDigits`, or inter-digit timeout. */
|
|
823
|
+
'keypad.entry': (entry: KeypadEntry) => void;
|
|
824
|
+
/** The caller pressed the clear key; `discarded` is what the buffer held. */
|
|
825
|
+
'keypad.cleared': (info: {
|
|
826
|
+
discarded: string;
|
|
827
|
+
}) => void;
|
|
666
828
|
'usage.updated': (usage: UsageInfo, delta: ProviderUsage) => void;
|
|
667
829
|
error: (error: Error) => void;
|
|
668
830
|
}
|
|
@@ -683,6 +845,8 @@ interface CallSessionDeps {
|
|
|
683
845
|
transport: TwilioMediaTransport;
|
|
684
846
|
start: TwilioStartEvent;
|
|
685
847
|
providerFactory: ProviderFactory;
|
|
848
|
+
/** Backup factories tried in order when `providerFactory` fails to connect. */
|
|
849
|
+
fallbacks?: readonly ProviderFactory[];
|
|
686
850
|
agent: Agent;
|
|
687
851
|
options: SessionOptions;
|
|
688
852
|
store: SessionStore;
|
|
@@ -699,11 +863,17 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
699
863
|
readonly streamSid: string;
|
|
700
864
|
readonly callInfo: ToolCallInfo;
|
|
701
865
|
readonly context: SessionContext;
|
|
866
|
+
/**
|
|
867
|
+
* Keypad (DTMF) input handle: digits buffered so far, `clear()`, `submit()`.
|
|
868
|
+
* Inert (empty, no-ops) unless the `keypad` session option is configured.
|
|
869
|
+
*/
|
|
870
|
+
readonly keypad: KeypadHandle;
|
|
702
871
|
private stateValue;
|
|
703
872
|
private readonly deps;
|
|
704
873
|
private readonly log;
|
|
705
874
|
private readonly tracker;
|
|
706
875
|
private readonly interruptions;
|
|
876
|
+
private readonly keypadCollector;
|
|
707
877
|
private readonly usageAccumulator;
|
|
708
878
|
private readonly toolQueue;
|
|
709
879
|
private readonly transcriptEntries;
|
|
@@ -713,6 +883,8 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
713
883
|
private readonly runningTools;
|
|
714
884
|
private readonly timers;
|
|
715
885
|
private provider;
|
|
886
|
+
/** Primary + fallbacks, in try-order. Only walked while connecting. */
|
|
887
|
+
private readonly providerChain;
|
|
716
888
|
private activeAgentValue;
|
|
717
889
|
private generating;
|
|
718
890
|
private currentResponseId;
|
|
@@ -772,6 +944,15 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
772
944
|
get transcript(): readonly TranscriptEntry[];
|
|
773
945
|
/** Connect the provider and activate the call. Called by the bridge. */
|
|
774
946
|
begin(): Promise<void>;
|
|
947
|
+
/**
|
|
948
|
+
* Walk primary + fallbacks until one connects. A provider that fails to
|
|
949
|
+
* come up is detached BEFORE the chain advances, so a half-open socket's
|
|
950
|
+
* late events can never reach the session once the next provider owns the
|
|
951
|
+
* call. Connect-time only by design: once a provider has answered, the
|
|
952
|
+
* call stays with it. Returns false after failing the call (chain
|
|
953
|
+
* exhausted) or when the call was torn down while connecting.
|
|
954
|
+
*/
|
|
955
|
+
private connectInitialProvider;
|
|
775
956
|
/** The outbound leg was answered (host's Twilio status callback). */
|
|
776
957
|
notifyAnswered(): void;
|
|
777
958
|
sendText(text: string, options?: {
|
|
@@ -822,6 +1003,8 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
822
1003
|
/** Immediate teardown (no goodbye). */
|
|
823
1004
|
end(reason?: CallEndReason): Promise<void>;
|
|
824
1005
|
private buildToolset;
|
|
1006
|
+
/** Agent instructions plus the kit's own notes that must survive handoffs (keypad). */
|
|
1007
|
+
private composeInstructions;
|
|
825
1008
|
private buildProviderInit;
|
|
826
1009
|
private wireTransport;
|
|
827
1010
|
/** Emit 'error'; when the host attached no listener, log instead of losing it. */
|
|
@@ -893,6 +1076,9 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
|
|
|
893
1076
|
* starts immediately while the provider session is still being built.
|
|
894
1077
|
*/
|
|
895
1078
|
private playPreGreeting;
|
|
1079
|
+
private handleKeypress;
|
|
1080
|
+
private onKeypadEntry;
|
|
1081
|
+
private onKeypadCleared;
|
|
896
1082
|
/** Armed whenever the agent goes quiet and we're waiting on the caller. */
|
|
897
1083
|
private armIdleTimer;
|
|
898
1084
|
private clearIdleTimer;
|
|
@@ -1085,4 +1271,4 @@ declare class PlaybackTracker {
|
|
|
1085
1271
|
private maybeForget;
|
|
1086
1272
|
}
|
|
1087
1273
|
//#endregion
|
|
1088
|
-
export { Agent, type AgentDefinition, type ApprovalRequestInfo, type BackgroundAudioOptions, type BackgroundAudioPreset, type BackgroundAudioSpec, BaseRealtimeProvider, type BridgeConfig, type BridgeEventMap, type BuiltinToolsConfig, type CallEndReason, CallSession, type CallSessionFacade, type CallSnapshot, type CallStartedInfo, type CallState, type CaptureGreetingOptions, type CapturedGreeting, DEFAULT_RECONNECT_POLICY, DEFAULT_SESSION_OPTIONS, type DeafnessOptions, type FinishCallToolOptions, type GreetingOptions, type HandoffDirective, type HangupOptions, type IdleOptions, InMemorySessionStore, type InferSchema, type InterruptionBlockCause, InterruptionController, type InterruptionDecision, type InterruptionRateLimit, type InterruptionSettings, type Logger, type MarkEchoResult, NoiseAdaptiveVadController, type NoiseAdaptiveVadOptions, PlaybackTracker, type ProviderAudioDelta, type ProviderCapabilities, type ProviderCloseInfo, type ProviderEvents, type ProviderFactory, type ProviderFactoryContext, type ProviderSessionInit, type ProviderToolCall, type ProviderToolSchema, type ProviderUsage, type ReconnectPolicy, type SendTextOptions, type SendToolResultOptions, SessionContext, type SessionEventMap, type SessionOptions, type SessionStore, type SessionUpdateOptions, type Tool, type ToolCallInfo, type ToolContext, type ToolDecoration, type ToolDefinition, type ToolMiddleware, type ToolRunInfo, type ToolStrategy, type TranscriptEntry, type TransferCallToolOptions, TwilioRealtimeBridge, type TwilioRestOptions, type TwilioStartEvent, type UsageInfo, type VadAdjustment, type VadConfig, type VadNoiseMetrics, type VadSuggestionInfo, type VadTuningProfile, type WebSocketLike, type ZodSchemaLike, captureGreetingAudio, collectAgentGraph, composeExecution, connectStreamTwiml, consoleLogger, createFinishCallTool, createHandoffTool, createTransferCallTool, decorateTool, emptyUsage, handoffToolName, isHandoffDirective, noopLogger, resolveSessionOptions, tool, zodToJsonSchema };
|
|
1274
|
+
export { Agent, type AgentDefinition, type ApprovalRequestInfo, type BackgroundAudioOptions, type BackgroundAudioPreset, type BackgroundAudioSpec, BaseRealtimeProvider, type BridgeConfig, type BridgeEventMap, type BuiltinToolsConfig, type CallEndReason, CallSession, type CallSessionFacade, type CallSnapshot, type CallStartedInfo, type CallState, type CaptureGreetingOptions, type CapturedGreeting, DEFAULT_KEYPAD_CLEAR_MESSAGE, DEFAULT_KEYPAD_INSTRUCTIONS, DEFAULT_KEYPAD_OPTIONS, DEFAULT_RECONNECT_POLICY, DEFAULT_SESSION_OPTIONS, type DeafnessOptions, type FinishCallToolOptions, type GreetingOptions, type HandoffDirective, type HangupOptions, type IdleOptions, InMemorySessionStore, type InferSchema, type InterruptionBlockCause, InterruptionController, type InterruptionDecision, type InterruptionRateLimit, type InterruptionSettings, KeypadCollector, type KeypadCollectorHooks, type KeypadEntry, type KeypadEntryReason, type KeypadHandle, type KeypadKeyKind, type KeypadOptions, type Logger, type MarkEchoResult, NoiseAdaptiveVadController, type NoiseAdaptiveVadOptions, PlaybackTracker, type ProviderAudioDelta, type ProviderCapabilities, type ProviderCloseInfo, type ProviderEvents, type ProviderFactory, type ProviderFactoryContext, type ProviderFallbackInfo, type ProviderSessionInit, type ProviderToolCall, type ProviderToolSchema, type ProviderUsage, type ReconnectPolicy, type SendTextOptions, type SendToolResultOptions, SessionContext, type SessionEventMap, type SessionOptions, type SessionStore, type SessionUpdateOptions, type Tool, type ToolCallInfo, type ToolContext, type ToolDecoration, type ToolDefinition, type ToolMiddleware, type ToolRunInfo, type ToolStrategy, type TranscriptEntry, type TransferCallToolOptions, TwilioRealtimeBridge, type TwilioRestOptions, type TwilioStartEvent, type UsageInfo, type VadAdjustment, type VadConfig, type VadNoiseMetrics, type VadSuggestionInfo, type VadTuningProfile, type WebSocketLike, type ZodSchemaLike, captureGreetingAudio, collectAgentGraph, composeExecution, connectStreamTwiml, consoleLogger, createFinishCallTool, createHandoffTool, createTransferCallTool, decorateTool, defaultKeypadMessage, emptyUsage, handoffToolName, isHandoffDirective, noopLogger, resolveSessionOptions, tool, zodToJsonSchema };
|