Vizuara Harness Engineering
/ pi

06 Pi from Scratch

How Pi actually works, built up from an empty file the way you would build any coding agent, one layer at a time — but every layer is Pi's real code (earendil-works/pi, MIT). We start with the loop and add exactly one thing per chapter, each because the last chapter left something missing: the toolbox, tool safety, the system prompt, the execution environment, context and cache, subagents, surfaces, and the extension system. Every claim is read straight from the source.

Pi from scratch: the planSTART
The whole of Pi on one page before we build it: the three package layers (ai / agent / coding-agent), the small-core-plus-extensions philosophy, and the order we will build it up in. Your map for the rest of the section.
The loop: from a chat call to an agentBUILD
The beating heart of Pi. How a plain model call becomes an agent: the loop calls the model, detects tool-use, runs the tools, threads the results back, and repeats until the model stops asking. Pi's real agent runtime, and why state is a session tree, not just an array.
The toolbox: giving Pi handsBUILD
A loop that can only talk is a chatbot. Pi's real tools — read, write, edit, bash, grep, find, ls (plus MCP) — and the shape of a Pi tool: name, description, a typed parameter schema, and an execute function. Why the description string is the model's real API.
Tool safety: permission gates, protected paths, trustBUILD
The first time bash runs a command you didn't expect, you understand why this chapter exists. How Pi gates dangerous tools before they execute, protects paths, and asks for project trust — all as hooks around tool execution, so safety is policy you can see.
The system prompt: how Pi tells the model who it isBUILD
Not one magic string. Pi assembles the prompt compositionally — a base template, your appended instructions, a project_context block of every AGENTS.md/CLAUDE.md found walking up from the cwd, and an available_skills block — then stamps the date and working directory. Read from buildSystemPrompt().
The execution environment: local, SSH, sandboxedBUILD
Where do the tools actually run? Pi threads an operations layer through every tool so the same read/bash/edit can run on your machine, over SSH, or in a sandbox without changing the tool. The injection mechanism (ReadOperations and friends) and the blast-radius idea.
How Pi manages context (not 'the last three messages')DEEP
Pi does not keep a fixed message window. It compacts on a real token budget: trigger at contextWindow minus 16,384, keep about 20,000 tokens of recent context, and summarize everything older into a structured digest of decisions and files touched. Read straight from compaction.ts.
How Pi does cache control: the rolling breakpointDEEP
Pi attaches an Anthropic ephemeral cache breakpoint to the last tool schema and the newest user message, a rolling breakpoint that follows the conversation so the whole growing prefix is served from cache each turn. Why a 100-turn Pi session stays cheap.
Does Pi serve the agent everything? (No, it truncates)DEEP
Every Pi tool is capped at 2,000 lines or 50 KB, whichever hits first. read keeps the head with offset/limit paging; bash keeps the tail and spills the full output to a temp file with a pointer; grep caps at 100 matches and 500-char lines; find at 1,000 results. Bounded by default, with an escape hatch.
How Pi does subagents: three mechanisms, three scalesDEEP
Pi has three answers to 'one job too big for one context': the subagent tool spawns isolated pi subprocesses (single/parallel/chain) driven by markdown role files (scout/planner/worker/reviewer, each with its own tools + model); handoff starts a fresh focused session; the orchestrator drives a whole fleet of instances across machines. Read straight from the extension + orchestrator source.
Surfaces: one headless core, four front-endsBUILD
The same AgentSession core drives a full terminal UI, a one-shot print mode, a JSON mode, and a 100-command JSONL RPC protocol — plus an SDK you embed in your own app. How Pi separates the agent from the way you talk to it, and why that separation is the whole point of a harness.
Extensibility: everything is an extensionBUILD
The last layer, and the one that makes Pi Pi. A single ExtensionAPI lets you register tools, slash commands, shortcuts, whole LLM providers, and hook ~31 lifecycle events (tool_call, context, session_before_compact…). Skills add progressive disclosure on top. How a small core becomes anything you need.