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

# Session Runtime State

> Mirror per-turn runtime execution artefacts onto sessions for replay and cross-runtime handoff

Runtime state mirroring lets sessions remember lightweight per-turn execution artefacts (tool call IDs, transcript slices) so the native runtime and plugin harnesses can hand off, replay, or debug each other's turns.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="Assistant",
    memory={"session_id": "my-session"},
)
agent.start("Run a tool turn — enable mirror_runtime_state on your gateway SessionConfig")
```

The user hands off between native and plugin runtimes; mirrored state preserves tool IDs and transcript slices each turn.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    N[Native Runtime] --> S[(Session runtime_state)]
    P[Plugin Harness] --> S
    S --> R[Replay / Handoff / Hooks]

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef tool fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class N,P agent
    class S tool
    class R output
```

## Quick Start

<Steps>
  <Step title="Enable mirroring">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import GatewayConfig, SessionConfig

    config = GatewayConfig(
        session_config=SessionConfig(
            persist=True,
            mirror_runtime_state=True,
        )
    )
    ```
  </Step>

  <Step title="Read what was mirrored">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents.session import DefaultSessionStore

    store = DefaultSessionStore()
    turns = store.get_runtime_state(session_id="my-session", runtime_id="native")
    for turn_id, state in turns.items():
        print(turn_id, state.get("tool_calls", []))
    ```
  </Step>
</Steps>

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant Runtime as Native Runtime
    participant Store as DefaultSessionStore

    User->>Agent: prompt
    Agent->>Runtime: execute turn
    Runtime->>Store: set_runtime_state(session_id, runtime_id, turn_id, state)
    Note over Store: Deep-copied under session lock
    Runtime-->>Agent: response
    Agent-->>User: reply
```

## Configuration

| Option                 | Type   | Default | Description                                                             |
| ---------------------- | ------ | ------- | ----------------------------------------------------------------------- |
| `mirror_runtime_state` | `bool` | `False` | Opt in to persist lightweight per-turn runtime artefacts on the session |

Set on [`SessionConfig`](/sdk/reference/praisonaiagents/classes/SessionConfig) — typically via `GatewayConfig(session_config=...)`.

## Store API

| Method                                                                           | Description                                                                                          |
| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `set_runtime_state(session_id, runtime_id, turn_id, state, mirror_enabled=True)` | Save state for one turn. Returns `True` when saved or when `mirror_enabled=False` (no-op).           |
| `get_runtime_state(session_id, runtime_id, turn_id=None)`                        | One turn when `turn_id` is set; all turns for the runtime when `turn_id` is `None`; `{}` if missing. |
| `clear_runtime_state(session_id, runtime_id=None)`                               | Drop one runtime's state, or all runtime state when `runtime_id` is omitted.                         |

## On-disk shape

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "session_id": "my-session",
  "messages": [],
  "runtime_state": {
    "native": { "turn-1": { "tool_calls": ["call-1"] } },
    "plugin_harness": { "turn-1": { "transcript": "..." } }
  }
}
```

Shape: `{runtime_id: {turn_id: state}}`.

## Common patterns

<Tabs>
  <Tab title="Replay a turn">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    state = store.get_runtime_state("my-session", "native", "turn-1")
    tool_calls = state.get("tool_calls", [])
    # Feed IDs into a follow-up run or debugger
    ```
  </Tab>

  <Tab title="Cross-runtime handoff">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    native = store.get_runtime_state("my-session", "native", "turn-1")
    store.set_runtime_state(
        "my-session", "plugin_harness", "turn-2",
        {"prior_tool_calls": native.get("tool_calls", [])},
        mirror_enabled=True,
    )
    ```
  </Tab>

  <Tab title="after_agent export">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents.hooks import add_hook
    from praisonaiagents.session import DefaultSessionStore

    @add_hook("after_agent")
    def export_runtime_state(event):
        store = DefaultSessionStore()
        session_id = getattr(event, "session_id", None)
        if session_id:
            turns = store.get_runtime_state(session_id, "native")
            # Push lightweight slices to your observability backend
        return event
    ```
  </Tab>
</Tabs>

## Best Practices

<AccordionGroup>
  <Accordion title="Keep state lightweight">
    Aim for **≤1 KB per turn** and **≤10 KB per runtime**. Store IDs and transcript slices — not full tool outputs or full conversation history.
  </Accordion>

  <Accordion title="Redact before storing">
    Remove API keys, credentials, and PII before calling `set_runtime_state`.
  </Accordion>

  <Accordion title="Opt in deliberately">
    Leave `mirror_runtime_state=False` unless something reads the data — mirroring is off by default to avoid session file bloat.
  </Accordion>

  <Accordion title="Garbage-collect per runtime">
    Use `clear_runtime_state(session_id, runtime_id="...")` instead of letting unbounded turn maps grow.
  </Accordion>
</AccordionGroup>

<Note>
  Older session files without `runtime_state` load as `{}`. A JSON `null` value is also coerced to `{}`.
</Note>

## Related

<CardGroup cols={2}>
  <Card title="Gateway" icon="network-wired" href="/docs/features/gateway">
    Configure `SessionConfig` on the gateway
  </Card>

  <Card title="Persistence overview" icon="database" href="/docs/persistence/overview">
    Session storage and resume
  </Card>
</CardGroup>
