How the RLM Runtime Handles Persistent IPython State Across Turns in Prime Agent
Prime Agent’s RLM kernel maintains IPython continuity across recursive turns by persisting session data in a JSON-backed harness that reloads from disk on every interaction.
The PrimeIntellect-ai/prime-agent repository implements a deterministic state management layer that allows Python-based skills to survive between /refine calls. Unlike standard Jupyter kernels that reset between executions, the RLM runtime handles persistent IPython state across turns by leveraging a file-backed architecture centered in the harness subsystem.
The Harness Architecture
The persistence mechanism relies on two core components: the kernel shim that interfaces with IPython and the harness manager that handles serialization.
Kernel Initialization and IPython Integration
In prime-agent-runtime/src/rlm/__init__.py, the runtime imports IPython.get_ipython when a Jupyter kernel is detected. This module registers Jupyter comm handlers, enabling Python-based skills to communicate bidirectionally with the host process. This integration allows the RLM runtime to manipulate the IPython namespace directly while maintaining the session context required for multi-turn conversations.
HarnessState Storage Location
The prime-agent-runtime/src/rlm/harness.py file implements the get_harness_state() function, which resolves storage paths through a hierarchy of environment variables. The resolution order is:
RLM_HARNESS_STATE_DIRRLM_GLOBAL_HARNESS_STATE_DIRRLM_SESSION_DIR
If none are set, the system falls back to ~/.prime/agent/harness/harness_state.json (lines 78-90). This deterministic path resolution ensures that state remains accessible regardless of how the agent process was launched.
Disk Synchronization Across Turns
To prevent state loss during recursive agent calls, the harness implements an aggressive synchronization strategy that treats the JSON file as the single source of truth.
The _sync_from_disk() Mechanism
Every turn triggers _sync_from_disk() before any read or write operations occur. The constructor invokes load() immediately upon instantiation, ensuring the in-memory representation reflects the latest disk state. This design accommodates external modifications made by the host process, such as when the /refine command updates the harness between turns.
Mtime-Based Cache Invalidation
The system monitors file modification timestamps to avoid unnecessary reloads. When the on-disk mtime differs from the cached value, the runtime refreshes the in-memory snapshot (lines 86-95). This optimization balances performance with consistency, ensuring that concurrent edits or parent agent updates propagate correctly to child processes.
Storing IPython State in Memory Entries
The HarnessState object organizes data into a dictionary keyed by HarnessKind, with valid types including "prompt", "memory", "skill", and "subagent". The "memory" kind specifically handles arbitrary Python objects, making it the vehicle for IPython session persistence.
Persisting Global Variables
Skills upsert memory entries to store namespace data between turns. The following pattern captures the current IPython globals:
from prime_agent_runtime.rlm import get_harness_state
h = get_harness_state()
h.upsert(
kind="memory",
title="IPython globals",
content="Persisted IPython globals",
id="ipython_state",
reference={"type": "python", "import": "builtins", "callable": "globals"},
)
Restoring Session Context
On subsequent turns, skills retrieve the stored entry and re-inject values into the running kernel:
from prime_agent_runtime.rlm import get_harness_state, get_ipython
h = get_harness_state()
entry = h.entries["memory"].get("ipython_state")
if entry:
ip = get_ipython()
if ip:
# Restore saved globals into current namespace
ip.user_ns.update(entry.reference["callable"]())
Practical State Management Examples
Saving Variables Between Executions
To persist a specific variable across turns:
# ---------- Example: Persisting a variable ----------
from prime_agent_runtime.rlm import get_harness_state
state = get_harness_state()
state.upsert(
kind="memory",
title="my_var",
content="value of my_var",
id="my_var",
reference={"type": "python", "import": "builtins", "callable": "repr"},
arguments={"value": 42},
)
Recovering State in Later Turns
To restore that variable in a future execution:
# ---------- Example: Restoring the variable in a later turn ----------
from prime_agent_runtime.rlm import get_harness_state, get_ipython
h = get_harness_state()
entry = h.entries["memory"].get("my_var")
if entry:
ip = get_ipython()
if ip:
ip.user_ns["my_var"] = entry.arguments["value"]
Summary
- File-backed persistence: The RLM runtime stores state in
harness_state.json, located via environment variables or a default path in the user's home directory. - Cross-turn synchronization: The
_sync_from_disk()method and mtime checking ensure fresh state on every turn, supporting concurrent modifications by parent agents. - Memory entry system: Python objects persist through
"memory"kind entries in theHarnessState, allowing skills to save and restore arbitrary data. - IPython integration: The kernel shim in
__init__.pyprovides direct access toget_ipython(), enabling namespace manipulation for true session continuity.
Frequently Asked Questions
Where does Prime Agent store the persistent harness state?
The runtime resolves the storage location through the environment variables RLM_HARNESS_STATE_DIR, RLM_GLOBAL_HARNESS_STATE_DIR, or RLM_SESSION_DIR, defaulting to ~/.prime/agent/harness/harness_state.json if none are configured. This path is determined in prime-agent-runtime/src/rlm/harness.py during HarnessState initialization.
How does the RLM runtime detect external changes to the state file?
Before every read or write operation, the harness calls _sync_from_disk() to compare the cached modification time against the filesystem mtime. If they differ, the runtime reloads the JSON content into memory, ensuring that changes made by the /refine command or parent processes are immediately visible.
What types of data can be stored in the harness "memory" entries?
The "memory" kind accepts arbitrary Python objects, including dictionaries, lists, and serialized representations of IPython globals. Skills store data by calling upsert() with a unique ID, then retrieve it via h.entries["memory"][id] in future turns, effectively creating a persistent key-value store for the session.
Does the IPython integration require a running Jupyter kernel?
While prime-agent-runtime/src/rlm/__init__.py attempts to import IPython.get_ipython and register comm handlers, the harness system functions independently. Skills can persist state to disk even without an active Jupyter kernel, though the ability to manipulate ip.user_ns requires the IPython environment to be present.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →