The Interpreter-Shaped Harness

what happens when a fork of pi replaces seven tools with one REPL

PrimeIntellect-ai/prime-agentView the repository
LANGUAGE
TypeScript + Python
SOURCE LOC
151,531 TS (+2.6k py)
PACKAGES
4 + 1 python runtime
BUILT-IN TOOLS
1 — ipython
PROVIDERS
32 · 9 wire APIs
OS SANDBOX
none — by inheritance
All 29 blocks

Surfaces & entry

The turn

Tools

Context & memory

Model layer

Safety

Extensibility

State

Isometric plate: the 29 subsystems of Prime AgentBlocks are grouped into districts by hatching and tint; block height is a rough proxy for code mass. Lines trace the data flow of a single turn. The block index beside this diagram carries the same information as text.ABDCEFNGOQHRIPXJSYZKTULMAAVABACW

Hover to read · drag to pan · ⌘/ctrl + scroll to zoom

How to read this plate

Each block is one subsystem of Prime Agent. Its height is a rough proxy for code mass, calibrated the same way across every harness so the plates are comparable. Hatching and tint group blocks into districts — surfaces, the turn, tools, context, the model layer, safety, extensibility and state.

Lines trace the real data flow of a single turn plus the major supporting links. Pick any block for a plain description and the files it was read from, or trace the turn to walk the path a request actually takes.

What this is

Prime Agent is a fork of pi that kept almost everything and replaced the one thing that matters most: the tool surface. Where pi ships seven tools — read, bash, edit, write, grep, find, ls — Prime Agent ships exactly one. src/core/tools/index.ts declares type ToolName = "ipython", and the single place that builds the agent's tool set passes only that. It is a larger program than its upstream, at 151,531 lines against pi's 125,552, with six fewer tools.

The bet is that a model writes better Python than it calls tools. A persistent IPython kernel stays alive for the whole session, so context lives as live variables rather than transcript text: read a file into a variable, slice it, and the intermediate never has to enter the prompt. Editing and shell access still exist, but as host operations and a bundled skill — reachable through the interpreter, not offered to the model as tools.

The second bet is that a harness should learn. The Continual Harness lets the agent persist durable lessons about how to work — supplemental prompts, memories, skill notes, subagent specs — with recorded history and rollback, while never touching the immutable base prompt. Every other harness in this atlas has a system prompt that is either static or template-assembled; this one has one the agent revises and can roll back.

The third bet is that a session should outlive its terminal. Roughly 24,000 lines — one line in six — are daemon and supervisor code: a background service owning every running agent, routing messages between them, restarting the ones that die. pi has no equivalent. The trade is stated openly rather than hidden: docs/architecture.md says the worker and kernel processes exist for lifecycle isolation and are "not security sandboxes", and there is no OS boundary anywhere in source.

Implementation

Lineage. This is an in-tree hard fork that never renamed anything. All four workspace packages are still published as @earendil-works/pi-agent-core, pi-ai, pi-coding-agent and pi-tui, pinned at pi's version 0.7.3; pi-coding-agent still declares bin: pi; and 72 of pi's worked example extensions survive under examples/extensions/. The loop itself is a light touch — packages/agent/src/agent-loop.ts is 963 lines against pi's 796 — while the orchestrator ballooned: core/agent-session.ts is 10,946 lines, 7.2% of the whole source tree in one file, against pi's 3,344.

The interpreter as the tool. core/kernel/index.ts (1,597 L) owns a real Python process through KernelManager, with bootstrap.ts (929 L) preparing the runtime and fork-server.ts (363 L) existing purely so a kernel starts fast enough to feel instant. The model's Python reaches back through a typed channel — HOST_COMM_TARGET = "host.request" and createHostRequestHandler — and renders diffs, attachments and inter-agent messages through custom display MIME types. Skills are importable Python packages, so third-party extension happens inside the model's runtime rather than around it.

Continuity. Sessions are one append-only .jsonl file plus an artifact directory (core/session-manager.ts, 2,090 L), which is what makes resume and fork cheap. Above that sits the daemon: daemon-mode.ts (7,095 L), daemon-supervisor.ts (5,239 L), daemon-agent-connection.ts (2,113 L) and a 1,187-line wire protocol. Turns can be re-entered with nobody watching, by heartbeat, cron schedule, standing goal or bounded autonomous mode (cron-jobs.ts 1,735 L, autonomous.ts 593 L, goals.ts 290 L), and agents message each other directly through the agent-message skill.

Safety, by subtraction. An exhaustive search of non-test source finds no sandbox-exec, bubblewrap, Landlock, seccomp or restricted-token path; @anthropic-ai/sandbox-runtime is a devDependency and sandboxing ships as an example extension. What remains is small and honest: prompt-admission.ts (42 L) gates what enters a prompt, output-guard.ts (74 L) what leaves a tool, and rlm-max-depth.ts (13 L) caps recursion. Given that the primary tool executes arbitrary model-written Python with your permissions, this is the most exposed posture in the atlas — and the one most clearly documented as such.