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
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.

Diagram

Native Runtime

Session runtime_state

Plugin Harness

Replay / Handoff / Hooks

Quick Start

  1. Enable mirroring
    python
    from praisonaiagents import GatewayConfig, SessionConfig
    
    config = GatewayConfig(
        session_config=SessionConfig(
            persist=True,
            mirror_runtime_state=True,
        )
    )
  2. Read what was mirrored
    python
    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", []))

How It Works

Diagram
DefaultSessionStoreNative RuntimeAgentUserDefaultSessionStoreNative RuntimeAgentUserDeep-copied under session lockpromptexecute turnset_runtime_state(session_id, runtime_id, turn_id, state)responsereply

Configuration

OptionTypeDefaultDescription
mirror_runtime_stateboolFalseOpt in to persist lightweight per-turn runtime artefacts on the session

Set on SessionConfig — typically via GatewayConfig(session_config=...).

Store API

MethodDescription
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
{
  "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

Replay a turn

python
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

Cross-runtime handoff

python
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,
)

after_agent export

python
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

Best Practices

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.

Redact before storing

Remove API keys, credentials, and PII before calling set_runtime_state.

Opt in deliberately

Leave mirror_runtime_state=False unless something reads the data — mirroring is off by default to avoid session file bloat.

Garbage-collect per runtime

Use clear_runtime_state(session_id, runtime_id="...") instead of letting unbounded turn maps grow.

Related