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_DIR
  • RLM_GLOBAL_HARNESS_STATE_DIR
  • RLM_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 the HarnessState, allowing skills to save and restore arbitrary data.
  • IPython integration: The kernel shim in __init__.py provides direct access to get_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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →