import type { CallCredentials, CallPrivateStory, CallRecordingUnavailable, CallTrimmed, CallVideo, CallVideoUnavailable, CallWorldBrief } from './client.js'; /** Lets the live voice call invoke the app's declared tools (pause_game, * set_feature, …). The companion's voice LLM calls a tool → invoke() runs the * app's onToolCall handler and resolves with a result string fed back to the * model. Wired by connectCall from the tools declared at createCompanion(). */ export interface VoiceToolBridge { tools: { name: string; description?: string; parameters?: Record; }[]; invoke: (name: string, argsJson: string) => Promise; } /** Universal "host control" verbs the companion can call on ANY surface (web / * game / app / hardware), regardless of provider. They're generic on purpose — * the SPECIFIC intent rides in the string params, which YOUR surface interprets * — so a single fixed schema set serves every embed AND can be pre-registered on * the (otherwise static) ElevenLabs Convai agent. `invoke_action` is the * catch-all trigger; the SDK unwraps it to the named action so it reaches your * onToolCall just like a declared tool. The others pass through by name. */ export declare const HOST_CONTROL_TOOLS: VoiceToolBridge['tools']; /** Names of the host-control verbs (for routing). */ export declare const HOST_CONTROL_TOOL_NAMES: readonly string[]; /** Avatar gesture/expression tools the embodied ("形象") companion calls to emote * — drive a VRM/3D body's gestures and facial expression. These are offered on * EVERY embed (like the host verbs) so the companion can express itself without * each third party re-declaring them, and — crucially — so the SDK can register * a no-op handler for them on the ElevenLabs Convai path, where the shared agent * is pre-configured to call play_gesture / play_expression regardless of what the * embed declares. An integrator that actually renders an avatar opts in by * declaring (or handling) the same tool name and the SDK routes the call to them; * otherwise it answers with a silent no-op success (see invokeVoiceTool / emit), * so a pure audio or 2D embed can ignore avatars entirely and the companion's * emote calls never error. Enum values mirror the canonical GESTURE_CATEGORIES / * EXPRESSION_INTENTS the avatar runtime understands (kept inline so the SDK stays * self-contained). */ export declare const AVATAR_VISUAL_TOOLS: VoiceToolBridge['tools']; /** Remove Eleven v4 performed cues ("[laughs]", "[whispers]", "[short pause]") * from a call transcript line (0.64.0). A call opened with `audioTags: true` * lets the voice ACT OUT these cues, and the provider's transcript still * contains them; `connectCall` strips them before `onTranscript` and before * the transcript reaches memory. Exported for integrations that open the * ElevenLabs session themselves from `startCall` credentials. * * Only ASCII-word cues are removed (the cue vocabulary is English): a line's * own brackets — `【重点】`, `[1]` — are left alone. Padded (`[ laughs ]`), * full-width (`[laughs]`) and trailing-ellipsis (`[sighs…]`) spellings of a * cue count. */ export declare function stripAudioTags(text: string): string; /** The performed cues in a call transcript line, in order, without brackets * (0.70.0): `'[laughs] 好呀 [whispers] 秘密'` → `['laughs', 'whispers']`. The * same cues `stripAudioTags` removes, so the two always agree. `connectCall` * reports them on each companion line of a call where `audioTags` was * granted (`onTranscript`'s `audioTags`); exported for self-plumbed * ElevenLabs sessions. */ export declare function extractAudioTags(text: string): string[]; /** Names of the avatar visual tools (for routing + the no-op fallback). */ export declare const AVATAR_VISUAL_TOOL_NAMES: readonly string[]; /** How a call ended (0.71.0), reported once per call to `onEnd`. * * - `user` — your own `close()`. * - `agent` — the companion ended the call itself: ElevenLabs' built-in * `end_call` system tool. Only on `elevenlabs-convai`, and only when the * agent both has that tool enabled and sends its tool responses to the * client — otherwise the same hang-up arrives as `remote`. * - `remote` — the provider closed the call cleanly without saying why (on * ElevenLabs: the agent left the room, a duration limit, an `end_call` whose * response the client never saw). Not a fault, and not known to be the * companion's decision. * - `error` — a failure; `error` carries it, and `onError` received the same * `Error` first. `openai-realtime` has no clean remote end, so every end * there that is not your `close()` is `error`. */ export interface CallEnd { reason: 'user' | 'agent' | 'remote' | 'error'; error?: Error; } export interface CompanionCallOptions { /** Element to route remote audio to. One is created if omitted. */ audioElement?: HTMLAudioElement; /** One finished line. `text` never carries performed cues. On a call where * `audioTags` was GRANTED (0.70.0), every companion line also carries * `audioTags`: the cues it performed, in order (`[]` when it performed * none). Absent on user lines and on calls without the grant, so absence * means "not applicable", never "zero". */ onTranscript?: (e: { role: 'user' | 'assistant'; text: string; audioTags?: string[]; }) => void; onSpeakingChange?: (speaking: boolean) => void; /** A failure that ended the call. Every `onError` is terminal: the call * is already torn down. Without `onEnd`, a clean remote end (the companion * or the provider hanging up) also arrives here as `call disconnected`. */ onError?: (err: Error) => void; /** The call ended, and why (0.71.0). Fires exactly once per call, on * every path including your own `close()`. Passing it moves a clean remote * end (`agent` / `remote`) out of `onError`, so a hang-up is no longer * reported as a fault. See `CallEnd`. */ onEnd?: (end: CallEnd) => void; /** OPT-IN full duplex: keep the mic OPEN while the companion speaks so the * user can interrupt mid-utterance (both providers' native VAD interruption * then works). Default false = half-duplex — the mic is muted during agent * speech, because on phone SPEAKERS browser echo cancellation does not fully * remove the companion's own voice and it barges in on itself (the SDK-voice * "说一句就断了" field report). Enable only where AEC actually holds: * headphones, desktop, or devices you have verified. */ bargeIn?: boolean; /** Open the call with the reader's microphone MUTED (0.69.0); see * `CompanionCall.setMuted`. On `openai-realtime` the track is disabled * before it is attached, so nothing is ever sent. On `elevenlabs-convai` * the mute is applied the moment the session is up; the provider client * has no start-muted option, so the connect handshake itself (before the * agent's first line) can carry audio. */ muted?: boolean; } export interface CompanionCall { /** Hang up. Idempotent. */ close: () => void; /** Inject an out-of-band context line. `speak: true` makes the companion react * now; false stages it for the next turn. * * NOT the reader's words: the line does not reach `onTranscript` or the * memory `/end` consolidates, and `connectCall` uses this same lever for * server-pushed world moments. For something the reader TYPED, use * `sendText`. */ injectEvent: (text: string, speak?: boolean) => void; /** Send a line the READER typed, as their turn in the call (0.69.0). The * companion answers it as something the reader said; it is reported to * `onTranscript` as `role: 'user'` (once, by the SDK, the moment it is * sent) and so reaches the memory `connectCall` hands to `/end` at hang-up, * in order with the spoken lines. * * It takes the floor the way speech does: an utterance the companion is * in the middle of is cut, then it replies to this line. * * Nothing is sent before this call — the provider sees the text only when * you pass it here, so a check you run first runs before anyone reads it. * Returns `false` (and sends nothing) for blank text, after `close()`, or * when the call's channel is not open; `true` once it was handed to the * provider. Same behaviour on both providers. */ sendText: (text: string) => boolean; /** The reader's own mute (0.69.0). `true` stops the microphone sending * audio until `setMuted(false)`. Distinct from the SDK's half-duplex mute * (the mic is muted while the companion speaks, unless `bargeIn`): a * reader who muted stays muted when the companion finishes a line. A * no-op after `close()`. Check `canMute` before showing a mute control. */ setMuted: (muted: boolean) => void; /** The reader's mute as last set (0.69.0) — the `setMuted` / `muted` * state, not whether the half-duplex mute is momentarily engaged. A * method, not a field, so it stays live on a spread copy of the handle. */ isMuted: () => boolean; /** Whether `setMuted` actually controls the microphone on this call * (0.69.0). `true` on `openai-realtime`; on `elevenlabs-convai` it is * `true` when the installed `@elevenlabs/client` exposes `setMicMuted` * (every version the peer range allows does). When `false`, `setMuted` * only records the state: do not show a mute control. */ canMute: boolean; /** SILENT CUT (0.36.0): stop the utterance currently being spoken WITHOUT * generating any new speech — the "stale commentary at a beat boundary" * case (field request: fast-paced game, last hand's commentary still * playing when the next hand starts). Idempotent: a no-op when nothing is * playing, after close(), and mid-connect. * * Provider tiers (check `provider`): * - `openai-realtime` — GUARANTEED: cancels the active response and clears * the WebRTC output buffer (`response.cancel` + `output_audio_buffer.clear`); * playback stops within ~an audio frame. The session continues normally. * - `elevenlabs-convai` — BEST-EFFORT: the EL client exposes no public * "stop playback" API; we signal `user_activity` (halts the agent's * stream when the agent's interruption handling allows) and attempt the * client's internal output-buffer cut when the installed build exposes * it. Verify on your rig; worst case it degrades to a no-op — for a * guaranteed preemption on EL, `injectEvent(text, true)` (which DOES * speak new content) remains the reliable lever. */ interrupt: () => void; provider: CallCredentials['provider']; /** The call's generation handle (0.65.0, set by `connectCall`) — the key * `getCallRecording` / `deleteCallRecording` take. */ callGen?: string; /** Whether this call is recorded (0.65.0) — present when it asked * (`connectCall({ record: true })`). */ recording?: boolean; /** Why a call that asked is not recorded (0.65.0). */ recordingUnavailable?: CallRecordingUnavailable; /** The video avatar's Simli session (0.75.0), when `connectCall({ avatar })` * got one. */ video?: CallVideo; /** Why a call that asked for an avatar has none (0.75.0). */ videoUnavailable?: CallVideoUnavailable; /** Pouchy World role sessions (0.67.0): whether the call carries the * story's current state. See `CallWorldBrief`. */ worldBrief?: CallWorldBrief; /** Pouchy World role sessions (0.68.0): whether the role can reach its * private layer on this call. See `CallPrivateStory`. */ privateStory?: CallPrivateStory; /** What the server cut to fit the realtime cap (0.68.0). See `CallTrimmed`. */ trimmed?: CallTrimmed; } /** Open a live call from pre-minted credentials. */ export declare function openCompanionCall(creds: CallCredentials, opts?: CompanionCallOptions, bridge?: VoiceToolBridge): Promise; /** Map the EL client's `DisconnectionDetails` onto `CallEnd`. An absent or * unrecognised shape is a failure, never a clean end: unknown is not the * benign answer. */ export declare function convaiCallEnd(details?: { reason?: string; message?: string; context?: { type?: string; }; }): CallEnd; //# sourceMappingURL=call.d.ts.map