TypeScript types
All public types exported from cosmo-ai.
All types are available as named imports from cosmo-ai. This page is the type index; the classes (RealtimeClient, RealtimeAgent, RealtimeSession) are documented on RealtimeClient, the React surface on React.
Client options
RealtimeClientOptions
| Field | Type | Description |
|---|---|---|
apiKey? | string | Workspace-scoped API key (server-side secret). At most one of apiKey / token. |
token? | string | TokenSource | Minted end-user JWT (from mintToken), or a TokenSource that fetches and refreshes one. Safe on devices; can open sessions but not mint. |
getAuthHeaders? | () => Record<string,string> | Promise<...> | Custom auth headers; a configured credential's Authorization wins. |
transport? | "webrtc" | "websocket" | "livekit" | Media carrier; defaults to WebRTC. livekit is a deprecated alias. WebSocket targets a local OSS server. |
transportFactory? | () => RealtimeTransport | Transport override, constructed per session start. Defaults to LiveKit. |
Token sources
A TokenSource is the credential shape for distributed apps: it fetches a fresh minted token from your backend, caches it in memory, and re-fetches when the cached token has less than 60 seconds of life left, so new RealtimeClient({ token: TokenSource.endpoint(...) }) stays valid for the life of the process. Concurrent callers share one in-flight fetch, and a session start rejected with HTTP 401 drops the cache.
| Export | Description |
|---|---|
TokenSource | The class. Its constructor is private — build one with the two statics below. |
TokenSource.endpoint(url: string | URL, options?: TokenSourceEndpointOptions): TokenSource | POSTs url (empty JSON body, redirects refused) and reads { jwt, expires_at } — the wire shape of POST /api/v1/external/auth/token, so any backend that forwards a mint response qualifies (a serialized MintedToken's expiresAt spelling is accepted too). url is a string or a URL instance (the pair fetch itself accepts) and may be a relative path (string form; rides the page's own origin) or an absolute https URL; http is allowed only for localhost, and scheme-relative //host URLs are refused. Failures throw TokenSourceError; on a rejection its serverCode is the server's slug when the body parses, http_<status> otherwise. |
TokenSource.custom(fetchToken: () => Promise<MintedToken>): TokenSource | A source backed by your own async function — full control over transport and auth. Resolves with a MintedToken (the shape mintToken returns); an empty jwt or invalid expiresAt throws TokenSourceError with code fetcher_failed. |
TokenSourceEndpointOptions | { headers?: Record<string,string> | (() => Record<string,string> | Promise<Record<string,string>>) } — headers attached to every token request (the app's own auth). Static, or a per-fetch callback. |
Entry points
The package publishes the root barrel plus the fixed set of subpaths in its build entry map — everything importable is listed here; anything else has no dist file.
| Specifier | Purpose |
|---|---|
cosmo-ai | The root barrel — every export on this page unless a row says otherwise. The React bindings are not here; they are at cosmo-ai/react, so a non-React project never installs react. |
cosmo-ai/server | The credential-holding half of an app: minting and verifying, without the session and agent API. Exports exactly: RealtimeClient, RealtimeClientOptions, MintedToken, MintTokenErrorCode, CredentialsError, CredentialsErrorCode, MintTokenError, TokenSourceError, TokenSourceErrorCode, VerifyError, CredentialInfo, setLogLevel, getLogLevel, LogLevel. |
cosmo-ai/presets | The naturalness presets, also re-exported from the root. |
cosmo-ai/core/events, cosmo-ai/core/types, cosmo-ai/core/state, cosmo-ai/core/realtime_client | Core modules, importable directly — core/events is where the four event payload types the barrel omits live. |
cosmo-ai/transport/types | The RealtimeTransport interface and its option types. See Transport. |
cosmo-ai/react | The React bindings — provider, hooks and components. The only place they live. A client-boundary entry: a server component that imports it fails at build. |
cosmo-ai/react/hooks, cosmo-ai/react/RealtimeProvider, cosmo-ai/react/components/RealtimeAudio, cosmo-ai/react/components/StartAudio, cosmo-ai/react/components/MicToggle, cosmo-ai/react/components/BarVisualizer | Individual React modules, for importing one piece without the rest. |
cosmo-ai/tool, cosmo-ai/tool/draw, cosmo-ai/tool/screen, cosmo-ai/tool/video_geometry, cosmo-ai/tool/zod | Tool helpers: drawing/geometry parsing, screen-tool shapes, zod schema conversion. |
cosmo-ai/desktop/local_desktop_preset_union.gen | Generated preset union for Cosmo's own desktop surfaces — published for compatibility, not a consumer API. |
Agent configuration
| Type | Kind | Description |
|---|---|---|
AgentConfig | object | The inline persona: instructions?, model?, voice?, tools?, interruptionSensitivity?, greeting?, audio?, skills?, hooks?. |
VoiceConfig | object | How the agent sounds: name? (provider voice id), speakingStyle? (delivery guidance appended after the persona). Accepted anywhere a plain voice-id string is. |
AudioConfig | object | The audio pipeline: output? (false runs text-only), noiseCancellation? ('off' / 'denoise' / 'voice_focus'). |
CatalogAgentOptions | object | Per-run ride-alongs for client.catalogAgent(name, {...}): inputs?, tools?, voice?, hooks? (client hooks only). |
SessionStartOptions | object | Per-run options for agent.start(): resumeSessionId?, storeRecording?, the per-artifact storeAudio? / storeTranscript? / storeVideo? (each wins over storeRecording, and all narrow only), publishMicrophone? (default true; false joins as a silent observer). |
RealtimeModel | union | string | RealtimeModelBlock — a model id or provider alias, or one provider block. |
RealtimeModelBlock | union | GeminiModel | OpenAIModel | OpenAIMiniModel | OpenAILiveModel | GrokModel, discriminated on provider. Each block type is also a same-named constructor that stamps the tag — GeminiModel({ modelId: 'gemini-live' }) — so you name the provider once by calling it and never write provider yourself; the tagged literal stays valid as the wire shape. Each carries an optional modelId? pinning the concrete model within that provider; unset runs that provider's default, and a modelId belonging to another provider is rejected at session start. |
GeminiModel | union | provider: 'gemini', modelId?, temperature?, maxOutputTokens?, thinkingLevel?, includeThoughts?, toolResponsePolicy?, toolResponseOverrides?, plus one turn detector's knobs: turnDetection?: 'server_vad' with endOfSpeechSensitivity? ('low' | 'high') / silenceDurationMs? / prefixPaddingMs?, or turnDetection: 'cosmo_vad' (Cosmo's semantic detection, also the unset default) with cosmoVad?: CosmoVadConfig. Mixing the two detectors' knobs is a type error. |
CosmoVadConfig | object | Tuning for the cosmo_vad detector: pauseMs?, prefixMs?, maxHoldMs? (each 0–5000 ms). |
OpenAIModel | union | provider: 'openai', modelId?, plus one turn detector's knobs: turnDetection?: 'server_vad' with silenceDurationMs? / prefixPaddingMs?, or turnDetection: 'semantic_vad' with eagerness? ('low' | 'medium' | 'high' | 'auto'). Mixing the two detectors' knobs is a type error. |
OpenAIMiniModel | object | provider: 'openai_mini', modelId? — the mini tier of the same API; no other knobs today. |
OpenAILiveModel | object | provider: 'openai_live', modelId?, plus the knobs of the backend Responses model GPT Live delegates tool calls and reasoning to: responsesModel?, responsesInstructions? (defaults to the agent's own), reasoningEffort? (OpenAILiveReasoningEffort: 'minimal' | 'low' | 'medium' | 'high'), verbosity? (OpenAILiveVerbosity: 'low' | 'medium' | 'high'), toolChoice? (OpenAILiveToolChoice: 'auto' | 'required' | 'none'), parallelToolCalls?, maxOutputTokens? (16–32768), serviceTier? (OpenAILiveServiceTier: 'auto' | 'default' | 'flex' | 'priority'), and delegation? (OpenAILiveDelegation: 'responses' | 'client' | 'cosmo') — who does the work the voice model hands off; under 'client' and 'cosmo' the responses* knobs are unused, the agent declares no tools, and the session emits a delegation-created event. GPT Live is full-duplex and owns its turn-taking, so it has no detector knobs; it is audio-only, so video and screen frames are ignored on it. |
GrokModel | object | provider: 'grok', modelId? — the xAI Grok Voice provider — plus its one detector's knobs: turnDetection?: 'server_vad', silenceDurationMs?, prefixPaddingMs?, and three provider knobs: reasoningEffort? (GrokReasoningEffort: 'high' — Grok's own default, deliberate answers at multi-second latency — or 'none', answering immediately), speed? (0.7–1.5 playback-rate multiplier), idleTimeoutMs? (the server re-engages the user after this much post-response silence, re-arming each response). Grok offers no semantic detector, so 'semantic_vad' is not spellable. |
Tools
| Type | Kind | Description |
|---|---|---|
AgentTool | opaque | Everything tools accepts. Build one by calling its constructor — webSearchTool(), clientTool({…}), drawBoxTool(onDraw) — which is the only tool type you name. The type is nominally opaque: a hand-written object literal does not satisfy it, and the per-tool models it lowers to are internal. |
ClientToolHandler | function | (args) => Promise<Record<string, unknown> | null | undefined | void> — returned object is the tool result; throw to surface an error. |
BackgroundClientToolHandler | function | (args, job: ClientToolJob) => Promise<void> — job.ack(note) then job.complete(...) / job.fail(...). |
ClientToolJob | class | Handle for a background tool call: ack, complete, fail. |
Skills and hooks
| Type | Kind | Description |
|---|---|---|
Skill | object | { name, description, body } — see Skills. |
parseSkillMd | function | (text: string, defaultName: string) => Skill — parse a SKILL.md document. defaultName is used when frontmatter omits name. Throws SkillError. |
Hook | class | One declared client hook (seam + callback + optional matcher). Build via the factories below. |
sessionStart / sessionEnd | factories | (fn) => Hook — the session seams take no options. |
preToolUse / postToolUse | factories | (fn, options?: { matcher?: string }) => Hook — matcher restricts to matching tool names (glob grammar; a malformed matcher throws HookError at declaration, not at session start). |
HookEventName | union | 'SessionStart' | 'PreToolUse' | 'PostToolUse' | 'SessionEnd'. |
SessionStartContext / PreToolUseContext / PostToolUseContext / SessionEndContext | objects | Per-seam contexts passed to hook callbacks. |
SessionStartResult | object | { additionalContext? } — injected into the instructions. |
PreToolUseResult | object | { permission?: 'allow' | 'deny', reason?, updatedArguments? }. |
SessionStartHook / PreToolUseHook / PostToolUseHook / SessionEndHook | functions | Callback signatures per seam. |
ToolOutcome | union | { kind: 'ok', result } | { kind: 'error', message } | { kind: 'denied', reason } — what PostToolUse observes. |
ServerHook | alias | SilenceTimeout — declarative server-executed hook config. |
ServerHookAction | union | The say / end_call action a fired server hook performed. |
SilenceTimeout / Say / EndCall | wire types | Re-exported wire shapes for server hooks. |
See Hooks for semantics.
State
| Type | Values / shape |
|---|---|
TransportState | 'disconnected' | 'requesting-permission' | 'connecting' | 'connected' | 'ready' | 'reconnecting' | 'disconnecting' | 'failed' |
SessionState | { kind: 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected'; disconnectReason?; detail? } — the formal cross-SDK lifecycle. See Lifecycle. |
DisconnectReason | 'client_ended' | 'client_closed' | 'handshake_failed' | 'server_ended' | 'transport_error' |
AgentState | 'idle' | 'listening' | 'thinking' | 'speaking' |
MicState | 'unknown' | 'requesting' | 'granted' | 'denied' | 'muted' | 'not-found' | 'in-use' |
OutputState | 'blocked' | 'playing' | 'silent' — 'blocked' means the browser is suppressing autoplay; use <StartAudio />. |
MediaState | { mic: MicState; screen: ScreenShareState; output: OutputState } |
ScreenShareState | { kind: 'inactive' } | { kind: 'requesting' } | { kind: 'active'; startedAt } | { kind: 'error'; error } |
Events
Payloads for session.on(...) / RealtimeEventMap:
| Type | Shape |
|---|---|
TranscriptRole | 'user' | 'assistant' — speaker for a transcript delta or item. The wire spells these uppercase; the SDK normalizes, so the wire form never reaches user code. |
TranscriptDeltaEvent | { role: TranscriptRole, text, isFinal } — a raw streaming delta; the session folds these into session.transcript for you. |
TranscriptItem | { id, role: TranscriptRole, text, isFinal } — one coalesced turn in session.transcript; id is a stable render key, and an item never changes once isFinal is true. |
TranscriptUpdatedEvent | { items: readonly TranscriptItem[] } — the coalesced transcript changed; items is the complete updated list, replayed on subscribe. |
ModelTextEvent | { text, isFinal } — model text channel, not a transcription. |
TurnCompleteEvent | { role: 'user' | 'assistant' } |
ToolCallEvent | { toolCallId, name } |
ToolDispatchStartedEvent | { toolCallId, name } |
ToolResultEvent | { toolCallId, ok, summary } |
ReadyEvent | { sessionId, rejectedTools, maxSessionSeconds, agent: ResolvedAgentInfo | null } |
ResolvedAgentInfo | { name, tools } — resolved catalog agent, informational. |
UsageEvent | { inputTextTokens, inputImageTokens, inputAudioTokens, inputCachedTokens, outputTextTokens, outputAudioTokens, totalTokens } — cumulative totals for the session so far, not a per-turn delta; each event supersedes the previous one. |
SessionStateWriteEvent | { state, updatedKeys, warnings, stage } — full canonical state after a server-side set_state write, not a delta; stage is state.stage hoisted by the server (string | null). |
SessionEndingSoonEvent | { secondsRemaining, reason } — the server ends the session in secondsRemaining seconds; reason is a stable slug (e.g. max_session_duration). |
SessionEndedEvent | { reason } — payload of the terminal session_ended event; the server's slug when the server ended it, otherwise the disconnect reason (e.g. client_ended). |
ReconnectingEvent | { secondsRemaining } |
UserSpeechTimeoutEvent | { sessionId, silenceMs, triggerCount, maxCount, action } |
VolumeEvent | { mic, output } — 0–1 RMS, per animation frame while subscribed. |
PongEvent | {} |
RealtimeEventMap | Event-name → payload map: transport_state, agent_state, media_state, lifecycle, transcript, model_text, tool_call, tool_dispatch_started, tool_result, session_state, volume, error, ready, session_started, reconnecting, session_ending_soon, session_ended, turn_complete, pong, user_speech_timeout, delegation_created, usage. |
RealtimeEventName | keyof RealtimeEventMap |
Unsubscribe | () => void |
Four payload types — UsageEvent, SessionStateWriteEvent, SessionEndingSoonEvent, and SessionEndedEvent — are not exported from the package root; import them from the cosmo-ai/core/events subpath. Every other type in this table is a named import from cosmo-ai.
Session stream items
Yielded by for await (const event of session):
| Type | Shape |
|---|---|
RealtimeSessionEvent | Every event type above, plus the two below. The stream and on() deliver the same values, so a type named here fits either. Switch on type. |
SessionEndedEventItem | { type: 'session_ended', reason } — SDK-local terminal item, always last. |
UnknownEvent | { type: 'unknown', rawType, payload, rawText? } — forward-compat item for unrecognized frames; rawType is null and rawText carries the frame verbatim when it could not be decoded at all. Never terminal. |
Errors
Every SDK-thrown error extends RealtimeError and sets a distinct name, so err instanceof RealtimeError catches the whole family. Branch on the concrete class (err instanceof DialError) or on err.code for the specific failure.
| Type | Description |
|---|---|
RealtimeError | Base class. Every error the SDK throws extends it. |
ErrorEvent | { code: ErrorCode; message: string; fatal: boolean } — an error the server reported on a live session, delivered on the error event. |
ErrorCode | 'auth_failed' | 'workspace_forbidden' | 'voice_disabled' | 'upstream_disconnect' | 'internal_error' | 'invalid_message' | 'version_mismatch' — the server enum, the same one Python and Swift publish. |
SessionStateError | Thrown by imperative methods the session can't serve in its current state. code is a closed SessionStateErrorCode: not_connected when the session isn't live — ended, never started, or recovering from a transport drop — plus already_started, audio_publish_already_active, video_publish_already_active, screen_share_unavailable, invalid_payload. |
SessionStartError | agent.start() did not produce a live session. code is a closed SessionStartErrorCode naming how far the attempt got; serverCode carries the server's own slug (an open set), status the HTTP status of a rejection (null when nothing answered), and detail a SessionStartRejection when the server sent structured extras. |
SessionStartRejection | The server's structured reason for a rejection, published identically by every SDK. Each group of fields belongs to one code: limit / active (concurrent_session_limit), granted_minutes / used_minutes (free_minutes_exhausted), balance_cents / top_up_path (insufficient_credits), meter / included / used / reset_at (quota_exceeded), provider / allowed_providers / plan / upgrade_path (provider_not_entitled). |
SessionStartErrorCode | 'transport' | 'invalid_response' | 'join_failed' | 'config' | 'busy' | 'entitlement' | 'version_mismatch' | 'voice_disabled' | 'rejected' | 'handshake_failed' | 'ready_timeout' |
CredentialsError | The client has no usable credential. code is a closed CredentialsErrorCode: no_credential, profile_not_found, file_invalid, expired, base_url_mismatch, conflicting_credentials, api_key_in_token_slot, insecure_base_url. |
MintTokenError | mintToken() failed; code: MintTokenErrorCode is request_failed, invalid_response, request_rejected, or missing_api_key. On request_rejected, serverCode carries the server's own slug. |
TokenSourceError | Resolving a TokenSource failed; code: TokenSourceErrorCode is request_failed, request_rejected, invalid_response, or fetcher_failed. On request_rejected, serverCode carries the token endpoint's slug. |
DialError | session.dial() failed; code: DialErrorCode is request_failed, request_rejected, invalid_response, or invalid_request. On request_rejected, serverCode carries the server's own slug (phone_calls_disabled, minute_limit_exceeded, session_not_live, …). |
VerifyError | client.verify() failed; code: VerifyErrorCode is request_failed, request_rejected, or invalid_response. On request_rejected, serverCode carries the server's own slug. A valid but under-scoped credential resolves instead of throwing. |
SkillError | The skills input is unusable; code: SkillErrorCode names which failure. |
DialErrorCode is an open-ended string alias — treat unknown codes defensively. SkillErrorCode, MintTokenErrorCode, TokenSourceErrorCode and AudioUnavailableErrorCode are closed unions: the SDK raises every one of them, so a switch over one is exhaustive. The open half of a mint or token-source failure is serverCode, which belongs to whichever backend answered.
Auth and telephony
| Type | Shape |
|---|---|
MintedToken | { jwt: string; expiresAt: Date; tokenId?: string } — from client.mintToken(externalUserId). See End-user credentials. |
DialResult | { dialId: string } — from session.dial(phoneNumber). See Telephony. |
CredentialInfo | { credential: CredentialKind; workspace: WorkspaceInfo | null; scopes: string[]; canStartSessions: boolean; realtimeVoiceAvailable: boolean; externalUserId: string | null } — resolved by client.verify(). |
CredentialKind | 'api_key' | 'user_token' | (string & {}) — which credential the server saw. Open-ended; treat unknown values defensively. |
WorkspaceInfo | { name: string; slug: string } — the workspace a credential is bound to. |
VerifyErrorCode | 'request_failed' | 'request_rejected' | 'invalid_response' — the closed code on VerifyError. The server's own slug is on serverCode. |
Screen tools
The shapes screen-tool handlers receive and answer with, all barrel exports. See Screen tools for the task-level walkthrough.
| Export | Shape / value |
|---|---|
screenLocateTool(capture) / screenClickElementTool(onClick) / screenHighlightElementTool(onHighlight) / screenHighlightBoxTool(onHighlight) | Constructors returning an AgentTool wired to your handlers. |
ScreenCaptureHandler | (request: ScreenCaptureRequest) => ScreenCapture | Promise<ScreenCapture> — produces the snapshot the locator grounds against; what screenLocateTool(capture) takes. |
ScreenCaptureRequest | The capture being asked for. It carries no options today; future capture options land here, inside the parameter every handler already accepts. |
ScreenCapture | { imageJpeg: Uint8Array; elements: ScreenElement[]; context?: unknown } — context is opaque per-capture state your handler can read back at click time. |
ScreenElement | { index, role, frame: [x, y, w, h], title?, label?, value? } — one pickable element; frame is in platform screen coordinates. |
ScreenBox | { x, y, width, height }, all 0..1 — a rectangle the model located itself, as fractions of the shared surface. Not interchangeable with ScreenElement.frame. |
ScreenElementHint | { title, role? } — what the model believes the target is called; a handler with an accessibility tree can resolve its exact frame, others ignore it. |
ScreenClickRequest | { element, capture, action } — what a click handler is asked to do. |
ScreenClickAction | { button: ScreenClickButton; double: boolean }. |
ScreenClickButton | 'left' | 'right' — left click / tap versus right click / long-press. |
ScreenClickOutcome | { clicked: true } | { clicked: false; reason: string } — answer with the clicked constant, or notClicked(reason) so the agent can say why. |
ScreenHighlightRequest | { element, capture, label, placement, interaction } — highlight by locator handle. |
ScreenHighlightBoxRequest | { box, elementGuess?, label, placement, interaction } — highlight when the model gave a box instead of a handle. |
ScreenHighlightOutcome | { shown: true; exact: boolean } | NotShown — answer with landedOnControl (resolved the real control), landedOnEstimate (drew the box as given), or notShown(reason). |
ScreenPlacement | 'auto' | 'top' | 'bottom' | 'left' | 'right' — where the tooltip sits. |
ScreenAffordance | 'pointer' | 'click' | 'double_click' | 'left_click' | 'right_click' | 'drag_show' | 'press_hold' | 'inform' — which glyph the highlight draws; a highlight never acts on the user's behalf. |
parseScreenHighlightBoxRequest(args) | ScreenHighlightBoxRequest | null — decode a raw tool-call args object; null for a malformed one (answer notShown, don't throw). For hand-rolled handlers registered outside the factories. |
SCREEN_CLICK_TOOL_NAME / SCREEN_HIGHLIGHT_TOOL_NAME / SCREEN_HIGHLIGHT_BOX_TOOL_NAME | 'cosmo_sdk_screen_click_element' / 'cosmo_sdk_screen_highlight_element' / 'cosmo_sdk_screen_highlight_box' — the wire names of the SDK-shipped client tools, for matching in hooks or tool-invocation events. |
Logging
| Export | Description |
|---|---|
setLogLevel(level: LogLevel): void | Set how much the SDK logs. Applies to every SDK logger immediately. |
getLogLevel(): LogLevel | The current level. |
LogLevel | 'silent' | 'error' | 'warn' | 'info' | 'debug'. The default is 'warn': warn and above print, info and debug stay off until the app asks. |
Constants and presets
| Export | Description |
|---|---|
SDK_NAME / SDK_VERSION | The SDK identity stamped on session-config and sent as the X-Cosmo-SDK header — the npm package name (cosmo-ai) and its version, both read from package.json. |
NaturalnessRung | 'warm' | 'delivery' | 'human'. |
naturalness(rung) | Resolves a rung to the verbatim speakingStyle instruction text. |
NATURALNESS_RUNGS / NATURALNESS_INSTRUCTIONS / NATURALNESS_VERSION | The rung list, prompt catalog, and its schema version. |
React types
RealtimeProviderProps, RealtimeSnapshotState, RealtimeToolCallItem — documented with the provider, hooks (useTransportState, useAgentState, useMediaState, useTranscript, useToolCalls, useRealtimeError, useMicLevel, useOutputLevel), and components (RealtimeAudio, MicToggle, BarVisualizer, StartAudio) on React.
Combined usage
The core types working together — options into a client, an AgentConfig into an agent, session options into start(), and a typed event payload out:
import {
RealtimeClient,
TokenSource,
type AgentConfig,
type TranscriptDeltaEvent,
} from 'cosmo-ai';
const config: AgentConfig = {
instructions: 'You are a terse voice assistant.',
voice: { name: 'Puck' },
audio: { noiseCancellation: 'denoise' },
};
const client = new RealtimeClient({
token: TokenSource.endpoint('/api/cosmo/token'),
});
const session = await client.agent(config).start({ storeRecording: false });
session.on('transcript', (event: TranscriptDeltaEvent) => {
if (event.isFinal) console.log(event.role, event.text);
});Gemini tool response policies
GeminiToolResponsePolicy has behavior: 'blocking' | 'non_blocking' and optional scheduling: 'when_idle' | 'silent' | 'interrupt'. Set GeminiModel.toolResponsePolicy for the default and toolResponseOverrides for a map from declared tool names to replacement policies. Omitted policies keep tools blocking on standard Gemini Live. Non-blocking results default to when_idle; silent absorbs the result without speaking and interrupt interrupts current speech.
import { GeminiModel } from 'cosmo-ai';
const model = GeminiModel({
modelId: 'gemini-3.8-live',
toolResponseOverrides: {
lookup: { behavior: 'non_blocking', scheduling: 'when_idle' },
},
});gemini-3.8-live-extended-thinking defaults to non-blocking tools, accepts low, medium or high thinking, and rejects minimal, blocking policies and explicit scheduling. An utterance can finish while the model is still thinking. Select independent lookups for non-blocking behavior; keep dependent actions blocking on standard Gemini Live. Background job tools retain their acknowledgement and completion contract.