realtime-voice-agents 2.2.0 → 2.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/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@ import { n as BackgroundAudioPreset, r as BackgroundAudioSpec, t as BackgroundAu
2
2
  import { t as TypedEmitter } from "./events-BUMYdETO.cjs";
3
3
  import { _ as Logger, a as ProviderToolSchema, c as SessionUpdateOptions, d as ProviderCloseInfo, f as ProviderEvents, g as VadTuningProfile, h as ProviderCapabilities, i as ProviderSessionInit, l as VadConfig, m as ProviderUsage, n as ProviderFactory, o as SendTextOptions, p as ProviderToolCall, r as ProviderFactoryContext, s as SendToolResultOptions, t as BaseRealtimeProvider, u as ProviderAudioDelta, v as consoleLogger, y as noopLogger } from "./BaseRealtimeProvider-BL75_HHh.cjs";
4
4
  import { i as WebSocketLike, m as TwilioStartEvent, r as TwilioMediaTransport } from "./transport-B_PJFIVd.cjs";
5
- import { a as emptyUsage, i as UsageInfo, n as SessionStore, o as TranscriptEntry, r as CallSnapshot, t as InMemorySessionStore } from "./InMemorySessionStore-B5_rq61L.cjs";
5
+ import { a as emptyUsage, i as UsageInfo, n as SessionStore, o as HandoffRecord, r as CallSnapshot, s as TranscriptEntry, t as InMemorySessionStore } from "./InMemorySessionStore-CRBmmCA6.cjs";
6
6
  import { i as TwilioRestClient, n as connectStreamTwiml } from "./twiml-z9LjoF4_.cjs";
7
7
  import { IncomingMessage } from "node:http";
8
8
  //#region src/tools/tool.d.ts
@@ -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.
@@ -671,6 +798,16 @@ interface SessionEventMap {
671
798
  to: Agent;
672
799
  reason?: string;
673
800
  }) => void;
801
+ /**
802
+ * A transfer the model asked for was refused. Fires INSTEAD of
803
+ * `agent.handoff` — the active agent did not change.
804
+ */
805
+ 'agent.handoff.blocked': (info: {
806
+ from: Agent;
807
+ to: Agent;
808
+ reason?: string;
809
+ cause: string;
810
+ }) => void;
674
811
  interruption: (info: {
675
812
  responseId: string;
676
813
  playedMs: number;
@@ -688,9 +825,16 @@ interface SessionEventMap {
688
825
  'background_audio.stopped': (info: {
689
826
  preset?: string;
690
827
  }) => void;
828
+ /** Raw keypress (every Twilio dtmf frame). With `keypad` configured it fires AFTER the collector consumed the key. */
691
829
  dtmf: (info: {
692
830
  digit: string;
693
831
  }) => void;
832
+ /** A complete keypad entry (`keypad` option): submit key, `maxDigits`, or inter-digit timeout. */
833
+ 'keypad.entry': (entry: KeypadEntry) => void;
834
+ /** The caller pressed the clear key; `discarded` is what the buffer held. */
835
+ 'keypad.cleared': (info: {
836
+ discarded: string;
837
+ }) => void;
694
838
  'usage.updated': (usage: UsageInfo, delta: ProviderUsage) => void;
695
839
  error: (error: Error) => void;
696
840
  }
@@ -729,11 +873,17 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
729
873
  readonly streamSid: string;
730
874
  readonly callInfo: ToolCallInfo;
731
875
  readonly context: SessionContext;
876
+ /**
877
+ * Keypad (DTMF) input handle: digits buffered so far, `clear()`, `submit()`.
878
+ * Inert (empty, no-ops) unless the `keypad` session option is configured.
879
+ */
880
+ readonly keypad: KeypadHandle;
732
881
  private stateValue;
733
882
  private readonly deps;
734
883
  private readonly log;
735
884
  private readonly tracker;
736
885
  private readonly interruptions;
886
+ private readonly keypadCollector;
737
887
  private readonly usageAccumulator;
738
888
  private readonly toolQueue;
739
889
  private readonly transcriptEntries;
@@ -780,6 +930,15 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
780
930
  private readonly agents;
781
931
  private readonly handoffHistory;
782
932
  private handoffInProgress;
933
+ /**
934
+ * An agent that just took over cannot transfer again until the caller has
935
+ * spoken. Without it every incoming agent re-derives intent from the same
936
+ * replayed transcript, decides the request is not its own, and transfers on
937
+ * — agents ping-ponging with no caller turn between them (field bug, Aug
938
+ * 2026). Programmatic `handoffTo()` is host intent and bypasses the lock,
939
+ * but still arms it for the agent it installs.
940
+ */
941
+ private handoffLockedUntilCallerTurn;
783
942
  /** Pre-synthesized greeting playout state. */
784
943
  private pregreeting;
785
944
  /** Noise-adaptive VAD (opt-in); built after connect from the provider's ACKed config. */
@@ -863,6 +1022,8 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
863
1022
  /** Immediate teardown (no goodbye). */
864
1023
  end(reason?: CallEndReason): Promise<void>;
865
1024
  private buildToolset;
1025
+ /** Agent instructions plus the kit's own notes that must survive handoffs (keypad). */
1026
+ private composeInstructions;
866
1027
  private buildProviderInit;
867
1028
  private wireTransport;
868
1029
  /** Emit 'error'; when the host attached no listener, log instead of losing it. */
@@ -927,6 +1088,12 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
927
1088
  private reconnectProvider;
928
1089
  /** After a reconnect the provider session is blank — restore conversational context. */
929
1090
  private reinjectHistory;
1091
+ /**
1092
+ * Refuse a model-initiated transfer from an agent the caller has not spoken
1093
+ * to yet. The refusal must reach the model as the tool result, or it simply
1094
+ * calls the same transfer tool again on its next turn.
1095
+ */
1096
+ private rejectHandoff;
930
1097
  private performHandoff;
931
1098
  /**
932
1099
  * Burst-write a stored μ-law greeting straight onto the Twilio socket —
@@ -934,6 +1101,9 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
934
1101
  * starts immediately while the provider session is still being built.
935
1102
  */
936
1103
  private playPreGreeting;
1104
+ private handleKeypress;
1105
+ private onKeypadEntry;
1106
+ private onKeypadCleared;
937
1107
  /** Armed whenever the agent goes quiet and we're waiting on the caller. */
938
1108
  private armIdleTimer;
939
1109
  private clearIdleTimer;
@@ -1126,4 +1296,4 @@ declare class PlaybackTracker {
1126
1296
  private maybeForget;
1127
1297
  }
1128
1298
  //#endregion
1129
- 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 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, emptyUsage, handoffToolName, isHandoffDirective, noopLogger, resolveSessionOptions, tool, zodToJsonSchema };
1299
+ 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 HandoffRecord, 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
@@ -2,7 +2,7 @@ import { n as BackgroundAudioPreset, r as BackgroundAudioSpec, t as BackgroundAu
2
2
  import { t as TypedEmitter } from "./events-BUMYdETO.mjs";
3
3
  import { _ as Logger, a as ProviderToolSchema, c as SessionUpdateOptions, d as ProviderCloseInfo, f as ProviderEvents, g as VadTuningProfile, h as ProviderCapabilities, i as ProviderSessionInit, l as VadConfig, m as ProviderUsage, n as ProviderFactory, o as SendTextOptions, p as ProviderToolCall, r as ProviderFactoryContext, s as SendToolResultOptions, t as BaseRealtimeProvider, u as ProviderAudioDelta, v as consoleLogger, y as noopLogger } from "./BaseRealtimeProvider-CWJ81HIt.mjs";
4
4
  import { i as WebSocketLike, m as TwilioStartEvent, r as TwilioMediaTransport } from "./transport-CEaLFV4E.mjs";
5
- import { a as emptyUsage, i as UsageInfo, n as SessionStore, o as TranscriptEntry, r as CallSnapshot, t as InMemorySessionStore } from "./InMemorySessionStore-B5_rq61L.mjs";
5
+ import { a as emptyUsage, i as UsageInfo, n as SessionStore, o as HandoffRecord, r as CallSnapshot, s as TranscriptEntry, t as InMemorySessionStore } from "./InMemorySessionStore-CRBmmCA6.mjs";
6
6
  import { i as TwilioRestClient, n as connectStreamTwiml } from "./twiml-z9LjoF4_.mjs";
7
7
  import { IncomingMessage } from "node:http";
8
8
  //#region src/tools/tool.d.ts
@@ -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.
@@ -671,6 +798,16 @@ interface SessionEventMap {
671
798
  to: Agent;
672
799
  reason?: string;
673
800
  }) => void;
801
+ /**
802
+ * A transfer the model asked for was refused. Fires INSTEAD of
803
+ * `agent.handoff` — the active agent did not change.
804
+ */
805
+ 'agent.handoff.blocked': (info: {
806
+ from: Agent;
807
+ to: Agent;
808
+ reason?: string;
809
+ cause: string;
810
+ }) => void;
674
811
  interruption: (info: {
675
812
  responseId: string;
676
813
  playedMs: number;
@@ -688,9 +825,16 @@ interface SessionEventMap {
688
825
  'background_audio.stopped': (info: {
689
826
  preset?: string;
690
827
  }) => void;
828
+ /** Raw keypress (every Twilio dtmf frame). With `keypad` configured it fires AFTER the collector consumed the key. */
691
829
  dtmf: (info: {
692
830
  digit: string;
693
831
  }) => void;
832
+ /** A complete keypad entry (`keypad` option): submit key, `maxDigits`, or inter-digit timeout. */
833
+ 'keypad.entry': (entry: KeypadEntry) => void;
834
+ /** The caller pressed the clear key; `discarded` is what the buffer held. */
835
+ 'keypad.cleared': (info: {
836
+ discarded: string;
837
+ }) => void;
694
838
  'usage.updated': (usage: UsageInfo, delta: ProviderUsage) => void;
695
839
  error: (error: Error) => void;
696
840
  }
@@ -729,11 +873,17 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
729
873
  readonly streamSid: string;
730
874
  readonly callInfo: ToolCallInfo;
731
875
  readonly context: SessionContext;
876
+ /**
877
+ * Keypad (DTMF) input handle: digits buffered so far, `clear()`, `submit()`.
878
+ * Inert (empty, no-ops) unless the `keypad` session option is configured.
879
+ */
880
+ readonly keypad: KeypadHandle;
732
881
  private stateValue;
733
882
  private readonly deps;
734
883
  private readonly log;
735
884
  private readonly tracker;
736
885
  private readonly interruptions;
886
+ private readonly keypadCollector;
737
887
  private readonly usageAccumulator;
738
888
  private readonly toolQueue;
739
889
  private readonly transcriptEntries;
@@ -780,6 +930,15 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
780
930
  private readonly agents;
781
931
  private readonly handoffHistory;
782
932
  private handoffInProgress;
933
+ /**
934
+ * An agent that just took over cannot transfer again until the caller has
935
+ * spoken. Without it every incoming agent re-derives intent from the same
936
+ * replayed transcript, decides the request is not its own, and transfers on
937
+ * — agents ping-ponging with no caller turn between them (field bug, Aug
938
+ * 2026). Programmatic `handoffTo()` is host intent and bypasses the lock,
939
+ * but still arms it for the agent it installs.
940
+ */
941
+ private handoffLockedUntilCallerTurn;
783
942
  /** Pre-synthesized greeting playout state. */
784
943
  private pregreeting;
785
944
  /** Noise-adaptive VAD (opt-in); built after connect from the provider's ACKed config. */
@@ -863,6 +1022,8 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
863
1022
  /** Immediate teardown (no goodbye). */
864
1023
  end(reason?: CallEndReason): Promise<void>;
865
1024
  private buildToolset;
1025
+ /** Agent instructions plus the kit's own notes that must survive handoffs (keypad). */
1026
+ private composeInstructions;
866
1027
  private buildProviderInit;
867
1028
  private wireTransport;
868
1029
  /** Emit 'error'; when the host attached no listener, log instead of losing it. */
@@ -927,6 +1088,12 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
927
1088
  private reconnectProvider;
928
1089
  /** After a reconnect the provider session is blank — restore conversational context. */
929
1090
  private reinjectHistory;
1091
+ /**
1092
+ * Refuse a model-initiated transfer from an agent the caller has not spoken
1093
+ * to yet. The refusal must reach the model as the tool result, or it simply
1094
+ * calls the same transfer tool again on its next turn.
1095
+ */
1096
+ private rejectHandoff;
930
1097
  private performHandoff;
931
1098
  /**
932
1099
  * Burst-write a stored μ-law greeting straight onto the Twilio socket —
@@ -934,6 +1101,9 @@ declare class CallSession extends TypedEmitter<SessionEventMap> {
934
1101
  * starts immediately while the provider session is still being built.
935
1102
  */
936
1103
  private playPreGreeting;
1104
+ private handleKeypress;
1105
+ private onKeypadEntry;
1106
+ private onKeypadCleared;
937
1107
  /** Armed whenever the agent goes quiet and we're waiting on the caller. */
938
1108
  private armIdleTimer;
939
1109
  private clearIdleTimer;
@@ -1126,4 +1296,4 @@ declare class PlaybackTracker {
1126
1296
  private maybeForget;
1127
1297
  }
1128
1298
  //#endregion
1129
- 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 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, emptyUsage, handoffToolName, isHandoffDirective, noopLogger, resolveSessionOptions, tool, zodToJsonSchema };
1299
+ 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 HandoffRecord, 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 };