RFC: opt-in RLM context engine (prompt-as-variable) to cut long-session tokens
Description
RFC: add an opt-in RLM context engine so long OMP sessions can keep large evidence outside the neural context window (prompt-as-a-variable), inspect it programmatically, and pay tokens only for selected slices / bounded subcalls.
This is not another compaction method. Today's ladder (remote → snapcompact → handoff → shake → soft in packages/coding-agent/src/session/compaction-methods.ts) is intentionally lossy at the provider-context layer: older turns are replaced so the next request fits. That stays the right default.
RLM (Zhang, Kraska, Khattab, arXiv:2512.24601) replaces llm.completion(huge_prompt) with rlm.completion(prompt) where the corpus is a REPL object. The root model peeks, slices, and optionally llm_querys a substring. The paper's useful result for OMP: the external environment helps even at depth 0 (no recursion); recursive subcalls help on information-dense tasks but are not uniformly cheaper.
Prime Intellect productized this as nano-rlm (ACP + persistent IPython) and prime-agent (arXiv:2608.23552), which forked pi / Oh My Pi and made the REPL the native tool. This RFC is the upstream-shaped slice: reuse OMP's existing retained Python kernel (EvalRunner / packages/coding-agent/src/eval/py) instead of forking the agent loop.
Lineage (already filed, do not re-open as Hermes work)
I previously wrote this up for Hermes, then closed the leaf into a research index. No equivalent OMP issue exists.
| Ticket | State | Relevance to this RFC |
|---|---|---|
NousResearch/hermes-agent#93303 feat: add bounded RLM mode for prompt-as-environment long-context work |
Closed 2026-08-26 into #93319 (needs-decision, not implemented) |
This RFC is the OMP port of that leaf. Same budgets, sandbox, provenance, depth-0-first. |
| hermes-agent#93319 research program index | Open | Hermes-only. Do not block this OMP RFC on it. |
hermes-agent#93306 reversible harness refinement (Continual Harness / Prime /refine) |
Closed into #93319 | Out of scope. Harness-ledger evolution is not context-window economics. |
| hermes-agent#93305 environment adapter SDK | Closed into #93319 | Out of scope (games/task harnesses). |
| hermes-agent#111237 / Harness Evolver series | Open | Out of scope (self-tuning prompts/tools). |
Related OMP issues (complementary, not duplicates)
| Ticket | Why it is not this RFC |
|---|---|
| #6850 Lossless Context Management | Derived summary DAG over session JSONL, exclusive with native compaction per request. Still summaries. RLM keeps original bytes addressable and never injects them wholesale. Same exclusive-routing / fail-open pattern can be reused. |
| #1602 agent-managed ledger + self-handoff | Agent-initiated clean-context handoff. Complementary. RLM avoids needing a handoff for corpus that was never in the window. |
| #7196 cache-stable system prompt | Constraint: RLM must not mutate the byte-stable system prompt / prompt cache. Runtime guide is appended at the engine boundary, same as Prime's runtime-v1 vs task prompt split. |
| #10920 Astra compact | Provider-native compact. Stays on the native ladder. |
Use Case
Long coding sessions where the expensive part is re-sending or re-summarizing evidence, not the current decision:
- repo / log / eval JSONL / session dump larger than the model window;
- tool results that
shakedrops and compaction then cannot cite; - snapcompact image tokens when a 2k-token slice would have sufficed;
- multi-file reviews where compaction summaries lose cross-file relationships (the RLM paper's 10M-token setting).
Cost claim to validate, not assume: root turns stay O(working set); each llm_query bills only the slice + question; the corpus is not in the root prompt cache at all. Depth 0 should already reduce input tokens vs stuffing files. Depth 1 is opt-in and budgeted.
Area
Session management
Proposed Solution
Invariants
context.engine: nativeremains default. RLM is opt-in (context.engine: rlmand/or/rlmfor one session).- Session JSONL stays authoritative. The RLM environment is derived, disposable, rebuildable from the journal + workspace files.
- RLM and native compaction (and LCM if #6850 lands) are never composed on the same provider request. Unready / budget-exhausted / sandbox-down → fail open to the configured native method order.
- No new always-on core tool. JSON tools stay the default agent loop. Do not make IPython the only tool (Prime Agent's fork; wrong for upstream OMP).
- Hard mechanical budgets:
max_depth(default 0),max_calls,max_total_tokens,max_cost, wall clock. Exhaustion is a clean stop with a partial trajectory, not a silent continue. - Sandbox: session Python kernel with no ambient secrets, no extra network, no arbitrary filesystem beyond the session cwd / explicit corpus bind. Host
execis not the production env. - Cancellation kills in-flight subcalls and leaves an honest JSONL trajectory.
- Every answer span that came from the corpus cites byte/line/path; otherwise labeled model inference.
Why OMP already has the runtime hook
EvalRunner (packages/coding-agent/src/session/eval-runner.ts) already owns a retained per-session Python kernel (python.kernelMode, disposeKernelSessionsByOwner). pythonExecution messages can be excludeFromContext. That is depth-0 RLM without a second REPL stack:
- bind large artifacts as named objects in the kernel (
corpus,files,journalslices); - model-authored peeks return small strings that do enter the next root request;
- the full object stays in kernel memory / on disk, not in
agent.state.messages.
Compaction of the root chat can still run; it must not delete the kernel or the bound corpus. Same as nano-rlm: kernel survives compaction.
v1 (depth 0) — ship this first
Paper: REPL-only already helps. Nano-rlm also truncates tool results >20KB to head/tail before they enter conversation.
- Spill gate. When a tool result or file read exceeds a setting (start with 20KB, match nano-rlm), store it as a named env object and inject a stub + handle into the transcript (
rlm://session/<id>/<handle>, size, hash, preview head/tail). Do not inject the full payload into the next provider request. - Peek tools (thin, not a new agent runtime):
rlm_peek(handle, start, end),rlm_search(handle, regex, limit), implemented via the existing kernel. Results are size-capped. llm_query(handle | text, question)— one bounded subcall through the existing model registry + usage accounting. Defaultmax_depth=0means this is a non-recursive LM call over a slice, notrlm.spawn.- Settings under Context (mirror #6850's
context.enginesplit):
context:
engine: native # native | rlm (lossless later if #6850)
rlm:
maxDepth: 0
maxCalls: 32
maxTotalTokens: 1000000
spillBytes: 20480
subModel: null # default: active model- Slash:
/rlmenable this session,/rlm status(kernel up, bound handles, calls/tokens/cost used, fail-open count),/rlm off. - Prompt cache: append a short runtime guide after the stable system prompt (do not rewrite it). Role-aware append only if depth ≥ 1 exists later.
- Tests (offline): stub model + fixture corpus larger than window; assert full corpus bytes never appear in the serialized root request; assert peek/subcall citations; assert fail-open when kernel is down; assert
/compactdoes not drop handles.
v2 (depth 1, only after v1 numbers)
Bounded subcall(slice, task) with max_depth: 1, supervisor-owned like nano-rlm (spawn waits for registration, not completion). Reuse OMP task subagents or kernel-side llm_query — pick one in implementation, do not grow a third agent runtime. Depth ≥ 2 is a non-goal until evals say otherwise.
Explicitly not this RFC
- Prime Agent
/refineContinual Harness (Hermes #93306). - Replacing bash/read/edit with Python.
- Training RLMs / verifiers / prime-rl.
- ACP
ai.prime.rlm/runtime-v1contract (nano-rlm training surface). - Reconstructing a live kernel after process exit (
session/load— nano-rlm also refuses this). - Unrestricted host IPython.
Acceptance (v1)
- Corpus / spilled tool result larger than the model window can be processed without injecting it wholesale into the root prompt.
- Serialized provider request for the root turn never contains the spilled bytes (test fixture).
- Peek /
llm_queryresults are size-capped and cited. - Budgets are enforced; cancellation leaves a partial trajectory.
- Kernel down / budget hit → native compaction path, no hang.
- System prompt bytes unchanged for an existing session when RLM is toggled (cache rule).
- Bench vs native
soft/shake/snapcompacton one repo-understanding fixture and one session-dump fixture: report input tokens, cost, and answer citations — not a quality-only vibe check.
Alternatives Considered
- Just add another compaction method. Compaction deletes evidence from the provider window. RLM never put it there. Different failure mode (lossy summary vs addressable store).
- Wait for #6850 LCM. LCM is a citation-bearing summary projection. Useful, still not original-byte peek. Exclusive routing can be shared; engines should not merge.
- Adopt Prime Agent as-is. They forked
piand made the REPL the only native tool. Wrong default for OMP's JSON-tool coding loop; too large a cutover. - Implement on Hermes only (#93303). Closed,
needs-decision, and OMP is where compaction cost actually lands for this workflow. - Agent self-handoff (#1602). Complementary for task narrative; does not keep a 10MB log addressable.
References: RLM paper, alexzhang13/rlm, nano-rlm, prime-agent, Hermes #93303.
Source: can1357/oh-my-pi