> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroruntime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Context Control

> How ChatContext records conversation memory, survives pipeline switches, and powers multi-agent fork.

Every `Agent` keeps a `ChatContext`: a structured log of messages (with their tool calls) plus agent handoffs. It’s the single source of truth across cascade and realtime pipelines and between agents, so you can switch pipelines or hand off mid-call without losing history.

<Tip>
  For automatic summaries and token-budget trimming on long calls, see [Context Window](/build/context-management/context-window).
</Tip>

## What `ChatContext` Records

Unlike a plain message list, `ChatContext` keeps an ordered log of `ChatMessage` items (accessible via `.items`) plus a separate list of agent handoffs, so the full history survives a pipeline switch or an agent handoff.

| Record             | Where It Lives                                                                                              |
| :----------------- | :---------------------------------------------------------------------------------------------------------- |
| **`ChatMessage`**  | User and assistant turns (final transcripts) and system instructions, returned by `.items` and `messages()` |
| **`FunctionCall`** | Every tool the agent invoked, with its arguments, embedded in each message's `tool_calls`                   |
| **`AgentHandoff`** | Transfer markers when one agent hands the conversation to another, read via `handoffs()` / `last_handoff()` |

<CodeGroup>
  ```python title="main.py" Python theme={null}
  # Inspect the agent's context at any time
  items = await self.session.fetch_context_history()
  recent_user_message = items[-1]
  ```
</CodeGroup>

<Note>
  Python doesn't expose a `ChatContext`-returning accessor yet: `session.fetch_context_history()`
  returns the same conversation as a plain list of message dicts (`role`, `content`,
  `message_id`, `tool_calls`, ...) instead of `ChatMessage` objects with `.items` / `.messages()`.
  `self.chat_context` is reserved for future use and is `None` today. Don't read from it.
</Note>

## Mid-Call Pipeline Switching

You can switch an `AgentSession`'s pipeline mid-call (for example, from a cascade stack, `STT` → `LLM` → `TTS`, to a realtime speech-to-speech model) using `pipeline.change_pipeline(...)`. The agent's `chat_context` is preserved, and the realtime model seeds itself from that context when it connects.

### Idempotency

The switch tool stays available on the agent after the switch happens. Without a guard, a realtime model, seeded with a conversation that's all about switching, can loop on the same tool. Track a flag such as `self._switched` and make the tool a safe no-op on repeat calls.

### Supported Realtime Providers

`change_pipeline(...)` works with every realtime provider that records back into `ChatContext`:

| Provider             | Model class        |
| :------------------- | :----------------- |
| Google Gemini Live   | `GeminiRealtime`   |
| OpenAI Realtime (GA) | `OpenAIRealtime`   |
| xAI                  | `XaiRealtime`      |
| Ultravox             | `UltravoxRealtime` |

Each provider seeds its prior conversation into the realtime session's instructions on connect, so the realtime half starts already aware of what was said and which tools were called.

<Tip>
  For the full mid-call switch, see [Configure a Pipeline](/build/configure-a-pipeline/overview).
</Tip>

## Multi-Agent Context Patterns

When two or more agents share a conversation, `ChatContext` provides primitives for transferring control, isolating sub-agent work, and merging results back.

### Hand Off to a Peer Agent

`add_handoff(...)` records a transfer marker on the shared context so the receiving agent's first turn is informed by what the previous agent did and why.

<CodeGroup>
  ```python title="main.py" Python theme={null}
  # In the intake agent, before transferring to billing
  self.chat_context.add_handoff(
      to_agent="billing",
      from_agent="intake",
      reason="Caller wants to dispute a charge on order 456.",
  )
  ```
</CodeGroup>

The billing agent reads the handoff marker on takeover and can greet the caller with full context: *"Hi, I see you'd like to dispute the charge on order 456. Let me pull that up."*

## Realtime Tool-Call Recording

Realtime tool calls are automatically logged to ChatContext as both a `FunctionCall` and its `FunctionCallOutput` (deduped by `call_id`). This lets a cascade LLM read prior results after a realtime→cascade switch, preventing duplicate tool invocations like re-calling `lookup_order(456)`.

No configuration is needed. This happens automatically for every realtime provider listed above.

## What's Next

<CardGroup cols={2}>
  <Card title="Context Window" icon="window-maximize" href="/build/context-management/context-window">
    Manage conversation history automatically.
  </Card>

  <Card title="Agent Handoffs" icon="arrow-right-arrow-left" href="/build/context-management/agent-handoffs">
    Share context across specialized agents.
  </Card>
</CardGroup>

## References

<Tabs>
  <Tab title="Python">
    #### Examples

    <CardGroup cols={2}>
      <Card title="Chat Context" icon="github" href="https://github.com/ZeroRuntimeAI/zrt-python-sdk-examples/blob/main/features/chat_context.py">
        Read and pass conversation context.
      </Card>
    </CardGroup>

    #### SDK Reference

    <CardGroup cols={2}>
      <Card title="ChatContext" icon="book" href="/api-reference/python/core/chat-and-context">
        `ChatContext` in the Python API reference.
      </Card>

      <Card title="Pipeline" icon="book" href="/api-reference/python/core/pipeline">
        `Pipeline` in the Python API reference.
      </Card>
    </CardGroup>
  </Tab>
</Tabs>
