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.
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.
Quick Start
- Enable mirroring
python from praisonaiagents import GatewayConfig, SessionConfig config = GatewayConfig( session_config=SessionConfig( persist=True, mirror_runtime_state=True, ) ) - 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
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 — 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
{
"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
state = store.get_runtime_state("my-session", "native", "turn-1")
tool_calls = state.get("tool_calls", [])
# Feed IDs into a follow-up run or debuggerCross-runtime handoff
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
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 eventBest 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.