Interface: OracleConfig
Defined in: packages/aiui-oracle/src/types.ts:242
The oracle's session configuration.
Deliberately spelled in the VENDOR's names, not ours (owner, 2026-07-30): this is a partial of the Realtime session object, so audio.input .turn_detection.silence_duration_ms here is the same string you would read in their docs, type into the params widget, or find in a session.updated echo. The package used to re-spell these (turnTuning, noiseReduction, transcribeInput, turn: "auto" | "semantic" | "manual"), which meant every value had to be translated twice to be reasoned about — and a setting you discover by experiment could not simply be pasted back in as a default.
The exceptions are the fields that are BEHAVIOUR we implement rather than parameters they accept: firstReplyGuard and mintTtlSeconds. Those keep our names precisely so the distinction stays visible.
Properties
audio?
optionalaudio?:OracleAudio
Defined in: packages/aiui-oracle/src/types.ts:269
The vendor's audio block: input turn detection / noise reduction / transcription, and output voice / speed.
firstReplyGuard?
optionalfirstReplyGuard?:boolean|FirstReplyGuard
Defined in: packages/aiui-oracle/src/types.ts:316
Protect the FIRST reply from the microphone's own echo. Default ON; pass false to disable, or an object to tune the timings.
The problem it solves: a browser's echo canceller is adaptive — it has no model of the room until it has actually heard far-end audio come back through the mic. So the very first reply leaks, the VAD hears "speech", and the session barges in on itself. Which is exactly the reported symptom: it happens on the first interaction and never again.
The fix is a window, not a setting: until the first reply has finished speaking, turn_detection carries interrupt_response: false, so what the mic hears cannot truncate that reply. Then the complete block is re-sent with the configured values — the suppressed fields restored EXPLICITLY, never by omission: the vendor's nested merge semantics are unverified, and under merge an omitted key keeps its last sent value (found live 2026-08-13 — an armed update that omitted create_response left the server holding false, and no utterance was ever answered again). After that the session behaves normally for the rest of its life.
That field and no other. create_response: false also rode here for one commit — to stop the echo being committed as a user turn and generating a reply to its own voice — and it deadlocked the session MUTE: the window closes when a reply happens, so suppressing reply creation means the human's first utterance never makes one, response.created never fires, the cap never starts, and no exit can ever be reached. Anything added here must leave the session able to produce the very reply this window waits for.
The one exception rides greeting: a greeting IS the first reply, created explicitly by us rather than by the VAD — so the window's exit no longer depends on reply creation being allowed, and create_response: false becomes safe to carry for exactly as long as the window is open.
Closing the window needs a real end-of-speech signal, and response.done is not one (the transcript finishes seconds ahead of the audio, and on WebRTC the reply is a track we cannot time). LedgerBody's reply-audio entry is that signal — the vendor's output_audio_buffer.* events, which this package used to discard as chatter.
greeting?
Defined in: packages/aiui-oracle/src/types.ts:347
Speak this line, verbatim, the moment the session opens — before the human has said anything. Off unless set.
This is echo-canceller PRIMING, not decoration. The canceller is adaptive and needs a few seconds of far-end audio before it can subtract the speakers from the mic — which is why the first thing a session says is the one thing it cannot protect (see firstReplyGuard). A greeting spends that convergence time on words that are DISPOSABLE: if the echo clips it or trips the VAD, nothing of value is lost, and the first real answer plays against a canceller that has already heard the room.
While the echo window is open, a configured greeting also lets the window carry create_response: false — so the greeting's own echo cannot commit as a user turn and be ANSWERED (the model chatting with itself at open). That suppression is only safe because the greeting reply is created explicitly; see the deadlock note on firstReplyGuard. The trade, stated honestly: a human who talks OVER the greeting is heard and committed but not answered until the window closes and they speak again. The window is the greeting's own length plus FirstReplyGuard.padMs.
A Greeting object replaces the say-exactly-this framing with a brief; a resolver computes the whole thing per session (returning undefined for "no greeting this time"). Whatever the form, it is resolved ONCE per start and frozen for the session's life — the echo window reads whether a greeting exists while building the audio block, and a value that changed between those reads could open the window suppressed and close it without restoring (see the note above on what that costs).
instructions
instructions:
Resolved<string|PromptSlots>
Defined in: packages/aiui-oracle/src/types.ts:264
The woven persona + app-specific prompt. Kept GENERIC about which tools exist — the tools array is the single source of truth (the documented "keep tool availability synchronized" failure mode).
Three forms, in rising order of power:
- a string — the whole prompt, stated. What most apps want, and the only form before slots existed.
- PromptSlots — the same thing composed from named parts, so a refresh can replace
contextwithout restatingapp. - a resolver over PromptContext — computed per compose. This is what a first-visit tutorial flow needs: the app checks its own storage, and answers with a different
stancefor a newcomer than for a regular.
A resolver runs BEFORE the mint, so the baked session already carries the right prompt — no opening correction, and one fewer whole-prompt send.
max_output_tokens?
optionalmax_output_tokens?:number|"inf"
Defined in: packages/aiui-oracle/src/types.ts:271
Cap on a single response's output tokens; "inf" for the model's own.
mintTtlSeconds?
optionalmintTtlSeconds?:number
Defined in: packages/aiui-oracle/src/types.ts:398
TTL for a minted ephemeral secret, seconds (10–7200). Default 600.
NOT read by OracleSession — and structurally cannot be: the session never mints, it asks a KeySource. TTL belongs to whoever holds the parent key, which is mintClientSecret's MintOptions for an in-browser mint and the mint SERVER's own option for a hosted one (the channel sets it on createMintBackend). Kept as documentation of the knob's existence and its range; a session-level value would be a lie about who decides.
model?
optionalmodel?:string
Defined in: packages/aiui-oracle/src/types.ts:244
Realtime model id — FROZEN at connect. Default DEFAULT_ORACLE_MODEL.
parkAfterIdleSeconds?
optionalparkAfterIdleSeconds?:number
Defined in: packages/aiui-oracle/src/types.ts:386
PARK the session after this many seconds with no activity. 0 disables. Default DEFAULT_PARK_AFTER_IDLE_SECONDS.
The failure it prevents is leaving a live microphone open because you walked away and forgot — which no amount of care fixes, since forgetting is the whole failure mode.
Park rather than close, deliberately: parking gates the mic and keeps the connection, which costs nothing while idle, so resuming is one click and the conversation survives. Closing would throw away the context you were mid-way through and need a fresh credential to rebuild.
"Activity" is what the LEDGER records as having happened — speech heard, a reply spoken or its audio playing, a tool running, something injected. Deliberately NOT config acks or session bookkeeping: those tick along on their own and would keep an abandoned session awake forever, which is precisely the state this exists to end.
Not to be confused with the vendor's turn_detection.idle_timeout_ms, which is the opposite behaviour — the model speaking UNPROMPTED after a silence. This one stops listening; that one starts talking.
reasoning?
optionalreasoning?:OracleReasoning
Defined in: packages/aiui-oracle/src/types.ts:274
The vendor's reasoning block — how hard the model thinks before it replies. Unset takes the model's own default (low).
recompose?
optionalrecompose?:"never"|"each-turn"
Defined in: packages/aiui-oracle/src/types.ts:362
Whether the prompt is recomposed during the session. Default "never".
"each-turn" re-runs the resolver after every completed user turn, which is what makes PromptContext.turns worth reading ("after five turns, stop explaining"). It is off by default because instructions are the largest thing on the session and are re-billed as input tokens on every subsequent turn — and because a prompt that churns mid-conversation is a hard thing to debug. A recompose that produces identical text sends nothing at all, so the cost is only paid when the prompt actually moved.
A first-visit tutorial flow does NOT need this: it wants a stance chosen once at open, which the resolver already does.
tools?
optionaltools?:OracleTool[]
Defined in: packages/aiui-oracle/src/types.ts:266
The tool surface presented at session start (live-updatable after).