The state machine
Walking a session
Connecting
The call arrives — a phone caller on a SIP trunk, or a browser mic over QUIC
— and its media is negotiated and decoded to the 8 kHz PCM16 contract. The
runtime allocates a session on the call’s core, loads the agent definition,
and opens the provider connection (a streaming ASR/LLM/TTS cascade, or a
single duplex realtime session). If the provider connection can’t be opened
within its timeout, the session ends here and the call is released.
Greeting
If the agent has an opening line, it speaks first — the runtime enters
Speaking before it has heard anything. Leave the greeting empty and the
agent starts in Listening, waiting for the caller to talk first (the
usual shape for inbound support lines where the caller already knows why
they called).
Listening
The runtime watches the inbound audio for end-of-utterance. On a cascade
pipeline an on-device VAD marks the boundary; on a cloud duplex model the
provider’s own end-of-turn detection can do it. Backchannels (“mhm”,
“yeah”) are filtered out by a minimum-duration gate, so a caller’s
acknowledgement never counts as a turn. When the caller finishes, the turn’s
audio is handed to the model and the session moves to Thinking. See
turn detection & barge-in for
how the boundary is detected and tuned.
Thinking
The model runs. In a cascade this is the language-model call (fed by the
transcript); in a duplex model the audio streams straight in. Each stage has
its own timeout budget — if a model stalls past its budget, the runtime
cancels the stage rather than leaving the caller in dead air (see
Timeouts and budgets).
Speaking
The reply is synthesised (cascade TTS) or streamed out (duplex) as 8 kHz
PCM16, paced into 20 ms frames, and encoded for the caller’s transport —
G.711 back to a phone trunk, Opus back to a browser. The runtime stays ready
to interrupt: the caller can barge in at any moment during Speaking.
Barge-in cancels a turn in flight
A caller talking over the agent is not a new turn waiting its polite turn — it’s an interrupt. When a barge-in is confirmed, the runtime cancels whatever the agent is doing (Thinking or Speaking), clears the outbound audio buffer so the agent goes quiet immediately, and jumps back to Listening to catch what the caller is actually saying. “Confirmed” is the important word. The runtime uses hold-and-confirm: the onset of caller speech arms a pending barge, but it only fires if speech sustains pastbarge_confirm_ms (default 300 ms). A short backchannel hits
its end before the window elapses and never cancels the agent. Set
barge_confirm_ms: 0 for legacy immediate barge-in — the agent cuts out on the
very first audio frame.
Whether a barge-in can actually cancel work already sent to a provider depends
on the provider. Cloud realtime and streaming TTS support mid-flight cancel;
a plain HTTP language model runs to completion server-side and the runtime can
only silence the playback. The honest per-provider scorecard lives in
turn detection & barge-in.
Timeouts and budgets
Every session runs against a set of clocks. They exist so that one stuck model, one runaway response, or one caller who wandered off never turns into an open connection billing forever.| Budget | What it bounds | Default |
|---|---|---|
| Per-stage timeout | How long any single ASR / LLM / TTS or realtime step may run before it’s cancelled | Tunable (millisecond budgets) |
max_output_tokens | Ceiling on a single model response, so one turn can’t generate without bound | 4096 |
max_session | Hard ceiling on total session wall-clock; the session is torn down when it’s hit | 15 minutes |
max_output_tokens was previously unbounded on the realtime path and is now
capped by default — a single turn cannot run away. max_session is a hard stop,
not a warning: when the clock hits it the runtime tears the session down
regardless of state.
Changing budgets doesn’t disturb live calls
The per-stage timeout budgets are runtime tunables you can adjust and reload without restarting the gateway or dropping calls. Reloads are additive: a session captures its timeout budgets at the moment it’s created and keeps them for its whole life, so a reload only affects sessions that start after it. No in-flight call ever has a clock changed underneath it.Idle hangup guardrails
The most common way a voice session gets stuck is silence: the caller stops talking, the agent has nothing to respond to, and without a guardrail the session would sit there untilmax_session finally reaps it. The idle
watchdog handles this gracefully — it nudges the caller first, then hangs up.
When the caller has been silent for soft_prompt_s, the runtime injects a
re-engagement prompt (“Are you still there?”) and keeps listening. If silence
then continues to hangup_s, it ends the session cleanly. Configure it under
the agent’s turn-detection block:
Default-on idle guardrails on the duplex realtime path are a recent addition.
The behaviour ships and is enabled by default; if you’re running a duplex
agent and want to be certain of the exact re-engage and hangup moments for
your provider, verify them against a test call before relying on them in
production.
Ending and teardown
A session leaves the loop for one of three reasons — the caller hangs up, the call is handed off to a human or transferred, or a guardrail (max_session or the idle watchdog) fires. Teardown
is the same either way: the runtime cancels any in-flight model work, closes the
provider session, flushes and stops the outbound audio pacer, releases the media
leg, and frees the session’s per-core state.
As it goes, the runtime emits the call’s lifecycle transitions — answer, holds
and resumes, transfer or REFER, and the final end — to the events pipeline, each
tagged with which handler (AI or human) owned that segment. Those events are
what power your call timelines and CDRs; see
call lifecycle for the
telephony view and call traces
for the per-session timeline you’ll actually read after the fact.
Related
Turn detection & barge-in
How end-of-utterance and confirmed barge-in are detected and tuned.
Runtime configuration
Every knob for budgets, guardrails, and turn timing in one reference.
Tool calling
What happens to the loop when the model calls a tool mid-turn.
Call traces
Read a finished session’s timeline, segment by segment.