Skip to content
Agentic Control Plane

pi ships no permission system, on purpose. We added one as an extension.

David Crowe David Crowe · · 5 min read
pi earendil coding-agents agentic-control-plane plugin-launch extensions
Share X HN LinkedIn

Run agents? ACP lets you see, control, and price every tool call they make — free on-device install for Claude Code, Cursor, Codex, and OpenClaw, nothing leaves your machine:

curl -sf https://agenticcontrolplane.com/install.sh | bash -s -- --local

Getting started →  ·  or use the free cloud console →  ·  free for individuals

pi — the minimal terminal coding agent from Mario Zechner (libGDX) and Armin Ronacher (Flask, Jinja) — does something most harnesses don’t: it ships no permission system at all, and says so. Four tools (read, write, edit, bash), no approval prompts, no allowlist, no sandbox. Its security docs don’t hedge — they tell you to containerize if you want a boundary, and point at the Gondolin micro-VM extension, Docker, or OpenShell.

That’s not a gap to dunk on. It’s a design, and a coherent one: keep the core tiny, make everything else an extension. Which means the right way to add control to pi isn’t to bolt something on the side — it’s to be an extension, on the same seam pi’s own tools use. So that’s what we built. pi now has ACP support via @agenticcontrolplane/pi:

curl -sf https://agenticcontrolplane.com/install.sh | bash

The installer detects pi, drops one TypeScript file at ~/.pi/agent/extensions/acp.ts, and signs you in once in the browser — free for individuals. From the next session, every tool call pi makes gets policy-checked against your workspace before it runs, and every decision lands in your activity log with the reason attached.

The seam pi gives you

pi dispatches every tool through two typed events, and an extension is an ordinary TypeScript module that subscribes to them. The ACP extension registers on both:

  • tool_call fires before the tool runs. Return { block: true, reason } to deny; mutate event.input in place to rewrite arguments. This is where the pre-call policy decision lands — POST /govern/tool-use returns allow, ask, or deny.
  • tool_result fires after. Return replacement content to redact or rewrite what the model reads. This is output scanning — POST /govern/tool-output for PII, secrets, and injected instructions.

That’s the full trifecta: deny, input-rewrite, and inline output-rewrite. The same capability set we get from dsh and Claude Code, and stronger than opencode, whose post-hook can only observe. And here’s the part that makes pi genuinely clean to govern: it has no MCP layer and no second tool path. bash, read, write, edit, and any custom or extension-registered tool all flow through those two events. One extension, complete coverage — nothing to supplement, no built-in that quietly doesn’t emit. Where our Codex integration needs a hook and an MCP connector, pi needs one file.

pi tells you when the chair is empty

The detail that made this a pleasure to write: pi hands the extension ctx.hasUItrue in the interactive TUI, false in print (pi -p) and JSON modes. That’s the attended-vs-unattended signal most harnesses leave you to infer from a timeout, handed over directly. We use it exactly as pi’s own shipped permission-gate.ts example does:

  • Attended: an ask decision prompts you inline through pi’s confirm dialog. Approve and the call runs; decline and it’s blocked.
  • Unattended: an ask becomes a deny, with the request surfaced in the console for later review.

No timeout guessing, no silent auto-yes. An agent nobody is watching cannot self-approve — that’s the empty-chair test, and pi lets an extension pass it in one line.

What it looks like

Point pi at an unattended run and ask it to do something your policy forbids, and the deny comes back through pi’s own transcript:

$ pi -p "run: rm -rf ./build-artifacts-nonexistent"
[ACP] Session receipt: 1 tool call governed · 1 denied —
      review this session: https://cloud.agenticcontrolplane.com/sessions/01a0…
The command was denied by policy: approvers notified; action blocked until approved.

A benign call passes and is counted, and the session ends with one honest line — how many calls ACP governed, and a deep link to the timeline. Zero governed calls produce zero receipt, because nobody needs to be told nothing happened.

Where pi lands on the coverage table

(The living version, updated with every integration, is at /coverage.)

Harness What the pre-call check intercepts Can policy ask a human? Can the result be rewritten inline?
Claude Code All native tools + MCP Yes (native ask) Yes
dsh Everything, incl. Code Mode sub-calls Yes (native approval) Yes
pi Everything — four built-ins + custom tools; no MCP to miss Yes — inline confirm when attended, deny when not Yes — tool_result replaces content
Hermes Everything, in-process Inline prompt Yes (transform_tool_result)
opencode Permission-routed + backstop Yes No (post-hook observes)
Codex CLI Full, via its 2026 hooks Guardian reviewer / dashboard Via hooks

pi joins the top tier — not because it ships strong controls, but because it ships a clean enough seam that the controls attach without compromise.

The philosophy is the point

pi is the sharpest version of an argument this site keeps making: in a minimal-core harness, control is an extension, and the extension seam is where it has to live. pi’s authors made that literal — they left the permission system out and handed you the events to build one. We think that’s the right instinct, and the ACP extension is what it looks like filled in: workspace policy that decides, an audit record the agent didn’t author, approvals that know whether you’re there, and the same rules running on pi as on every other harness you touch.

It’s one MIT TypeScript file that imports only a pi type — zero runtime dependencies, no build step, readable in a couple of minutes. It adds nothing to pi except the half pi deliberately left for you.

Share X HN LinkedIn
Get the next data drop
What agents actually cost, new tool-surface captures, and the occasional incident post-mortem — sent when we publish something worth your inbox, not on a schedule. Unsubscribe anytime.
Share: Twitter LinkedIn
Related posts

← back to blog