The Prime Agent control model, explained
Prime Agent — PrimeIntellect-ai/prime-agent, ~18k stars — is a hard fork of pi that pushed pi’s minimalism one step further: where pi ships four tools, Prime Agent ships one. A persistent ipython kernel is the universal tool — shell commands, file edits, Python, network access all execute as code in it — wrapped in the machinery for staying alive: daemon-backed sessions, heartbeats, schedules, recursive subagents (await rlm(...)), and a bounded /autonomous mode. It is a harness explicitly built for the unattended, long-running shape of agent.
What it did not add on the way: any permission system. This page covers what’s native, where the seam is, and the one signal that lies.
This page covers Prime Agent’s own model. For adding ACP, see the install guide; for the cross-harness picture, the controls comparison.
What Prime Agent ships natively
- No permission prompts, no rules, no sandbox. The kernel runs with the launching user’s full access. The extension docs list “permission gates (confirm before
rm -rf,sudo, etc.)” as the first example use case for an extension — control is homework, by design. - Bounded autonomy, not governed autonomy.
/autonomoustakes explicit limits —maxTurns,maxContinuations,maxTokens,timeoutMs— and print mode reports which limit stopped the run. These are budget throttles: they bound how much the agent does, not what it’s allowed to do. - A branchable session tree, not an audit record. Like pi, sessions are durable JSONL you can fork and rewind — the run’s own account, on the run’s own machine.
- A resident daemon. A supervisor process hosts session workers over sockets in
$TMPDIR/prime-agent-<uid>/; sessions survive the TUI closing. Operationally: extensions load per worker (/reloadpicks up changes), and akill -9‘d worker leaves a stale socket that hangs the next CLI start — clear the socket dir when testing.
The seam: pi’s two events, kept intact
Prime Agent kept pi’s extension API wholesale — same package name, same typed events — which makes it a first-class control target on day one:
| Event | Fires | An extension can |
|---|---|---|
tool_call |
before the tool runs | return { block: true, reason } to deny; mutate event.input in place to rewrite arguments (no re-validation after) |
tool_result |
after the tool runs | return { content, details, isError } patches to rewrite or redact what the model reads |
Handlers chain in load order; tool_call errors fail closed (the call is blocked). Coverage is total for the same reason as pi: there’s no MCP layer and no second dispatch path. The one tool being ipython cuts both ways — interception is coarse (you see code, not a parsed intent), but it is complete, and code-level policy is exactly what a server-side classifier is for.
Extensions load from ~/.prime/agent/extensions/*.ts (global, no trust prompt — where a control extension belongs) or .prime/agent/extensions/ (project-local, behind project trust), TypeScript loaded directly via jiti, no build step. Node 22.8+ enforced with a clear error. The provider seam (pi.registerProvider("anthropic", { baseUrl })) re-points model traffic through a proxy in three lines — the metering half of the story.
The signal that lies: hasUI in print mode
pi’s cleanest contribution to control was the empty-chair signal: ctx.hasUI tells an extension whether a human can answer, so an approval with nobody present can deny instead of hanging. Prime Agent inherited the API — but broke the semantics: 0.8.x reports hasUI = true in headless -p print mode. The docs table says print mode has no-op UI; the types comment says false; the runtime says true (verified against 0.8.1 with a live extension).
The consequence is not cosmetic — and argv alone doesn’t rescue it, because Prime Agent hosts every CLI session in a daemon worker process (argv --mode daemon, env PRIME_AGENT_INTERNAL_DAEMON_WORKER=1): the worker’s argv never shows -p, and the same worker class hosts attached TUI sessions and empty-chair print runs alike. An extension using pi-correct logic here treats unattended runs as attended: outage handling falls open when it should fail closed, and asks try to prompt a chair nobody is sitting in.
What does discriminate is the UI itself: a confirm dialog delivered to an attached client gets answered; the headless no-op UI cannot say yes. The reliable posture is therefore prompt-as-probe — route asks (and outage proceed-ungoverned questions) through the confirm dialog and let a false/no-op answer mean deny — plus conservative tier labeling for worker and headless processes. The ACP extension ships exactly that; the clean upstream fix would be per-session client-attachment state on ExtensionContext.
What Prime Agent’s model doesn’t include
The same honest zero as pi, at higher stakes because the harness is built to run longer with less supervision:
- No policy. Nothing decides allow/deny until an extension does; out of the box, everything the kernel can reach, runs.
- No independent record. The session tree is not a decision ledger — what was evaluated, against which policy, on whose authority.
- No identity. Kernel executions aren’t attributed to a user or delegation chain — and subagents multiply that.
- Autonomy limits ≠ controls.
maxTokensstops a runaway bill, not a governed action.
Where ACP fits
Prime Agent is the pi thesis with the autonomy dial turned up: a minimal core where control is an extension, running agents whose sessions outlive the terminal that started them. The ACP extension is that control — workspace policy decides allow/ask/deny on every tool_call, output scanning and the ledger land on tool_result, the argv fix restores the empty-chair posture that hasUI drops, and every decision is recorded off the machine, joined across your whole fleet. Same policy that governs Claude Code, pi, and dsh — which is the point: the more harnesses fork and diverge, the more the control has to live outside all of them.