/** * withRealtimeVoice — functional mixin adding realtime voice to an * AIChatAgent-derived Durable Object class. * * RFC-07 convergence: this mixin is now pure transport glue. All * orchestration — tool dispatch, flow transitions, hooks, handoffs, session * persistence, reconnect strategy, chat_ctx replay — lives in AriaFlow's * `OrchestrationAuthority` and `CloudflareRealtimeAdapter`, mirroring the * Node path that uses `LiveKitRealtimeAdapter`. * * Responsibilities that stay in the mixin: * - Intercept voice-protocol frames on the browser ↔ DO WebSocket. * - Per-connection concurrent-session cap (`maxConcurrentSessions`). * - DO-specific lifecycle (`keepAlive()` during an active call). * - Expose subclass-override hooks (`beforeSessionStart`, `onSessionStart`, * `onSessionEnd`, `onInterrupt`, `onUserTranscript`, `onModelTranscript`). * - Build the `OrchestrationAuthority` per call (via `new Runtime(...)`), * wire in a `BridgeSessionStore`, and hand control to the adapter. * * Responsibilities moved OUT of the mixin: * - Tool execution → `OrchestrationAuthority.executeToolCall` * - Flow reconfigure / handoff → same, via `ToolExecutionOutcome.action` * - Transcript persistence → `authority.recordUserInput/Output` + * existing `AIChatAgent.persistMessages` * - Resumption handle + chat_ctx replay → provider client (in-memory) + * adapter (reconnect dispatch) * - SQLite tables `cf_realtime_resumption` and `cf_realtime_chat_ctx` * — DELETED. Storage flows through `SessionStore` (BridgeSessionStore on CF). * * Capture-and-patch lifecycle wrap mirrors `@cloudflare/voice`'s pattern * (voice.ts:251-334, MIT, © 2025 Cloudflare, Inc.). */ import type { Connection } from "agents"; import type { RealtimeAudioClient } from "@ariaflowagents/core/realtime"; type Constructor = new (...args: any[]) => T; type AgentLike = Constructor; export interface RealtimeVoiceOptions { /** Audio wire format. Fixed to PCM16 today; future provider-specific variants. */ audioFormat?: "pcm16"; /** Per-DO concurrent-session cap. 5th `start_call` rejected with an `error` frame. Default 4. */ maxConcurrentSessions?: number; /** Max outbound-reconnect attempts under backoff before giving up. Default 3. */ reconnectMaxAttempts?: number; /** Base delay for backoff (delay ∈ [0, min(cap, base * 2^attempt)]). Default 500ms. */ reconnectBaseDelayMs?: number; /** Cap on the backoff delay. Default 8000ms. */ reconnectCapDelayMs?: number; /** Bytes of PCM to ring-buffer per-connection across a reconnect window. Default 48_000. */ reconnectAudioBufferBytes?: number; } export interface RealtimeVoiceMixinMembers { realtimeModel?: RealtimeAudioClient; createRealtimeModel?(env: unknown): RealtimeAudioClient; beforeSessionStart(conn: Connection): boolean | Promise; onSessionStart(conn: Connection): void | Promise; onSessionEnd(conn: Connection): void | Promise; onInterrupt(conn: Connection): void | Promise; onUserTranscript(text: string, isFinal: boolean, conn: Connection): void | Promise; onModelTranscript(text: string, isFinal: boolean, conn: Connection): void | Promise; forceEndSession(conn: Connection): void; } export declare function withRealtimeVoice(Base: TBase, opts?: RealtimeVoiceOptions): TBase & Constructor; export {};