Pi's Wager: Keep the Agent Small, Keep the Workflow Yours

Pi Agent Harness is the subject of this series: a codebase that puts a terminal coding agent, an agent runtime, a multi-provider model layer, session management, and host-facing integration surfaces under one architectural roof. This opening asks what “minimal” means in Pi and why that choice matters. The practical outcome is a map: you should be able to tell which part of Pi is responsible for a model request, a tool effect, a long-lived conversation, a custom workflow, or the boundary around the process.

That distinction matters because an AI coding agent is never only a conversation with a model. It reads and changes a real source tree, carries credentials, receives untrusted text, and must remain understandable when a request fails halfway through. A product can hide those responsibilities behind a large set of built-in features, or it can expose seams that let users and application authors decide how the system should behave. Pi chooses the second path. The benefit is control without forking the core. The cost is that the host must make more decisions deliberately.

Pi is a harness, not a single feature

The repository's name can make Pi sound like one command, but its own description is broader: it is the home of an agent harness and a self-extensible coding agent. The package that most users encounter, @earendil-works/pi-coding-agent, supplies the interactive CLI. It also supplies an SDK and several non-interactive modes. Under that layer, @earendil-works/pi-agent-core owns the general loop that holds messages, streams model responses, executes tools, and emits lifecycle events. @earendil-works/pi-ai gives the loop a common language for models and providers.

Those three layers answer different questions. The CLI asks how a person starts and controls a session. Agent core asks how an agent turn proceeds. The AI package asks how a request reaches Anthropic, OpenAI, Google, a local server, or another supported endpoint. A session layer records what happened and reconstructs the active context. The TUI package renders the terminal experience. Extensions and packages add workflow decisions without requiring edits to the core. Newer supporting packages—Chord, telemetry, and experimental client/server/protocol pieces—extend the same idea toward hostable services and remote presentations.

The useful way to read the repository is therefore not as a directory tree but as a set of responsibilities. The coding-agent CLI is an entry point. The agent runtime is the control loop. The provider layer is the model boundary. Sessions are continuity. Extensions are workflow policy. The host remains responsible for the environment in which effects happen.

Consider a small request such as “find the failing test and patch it.” The CLI receives the request and presents progress, but it does not need to know how an Anthropic or OpenAI payload is encoded. Agent core can represent the request, the model's response, and the tool result as a coherent event sequence, while the provider layer translates the model-facing context into the selected API. The coding-agent layer decides which tools and project instructions are active. The session layer can record the resulting messages and tool activity. If the user later wants a different terminal presentation, a different model, or a repository-specific guard around edits, each change has a corresponding seam rather than requiring a rewrite of the entire path.

That separation also explains why package names alone are a poor mental model. A package boundary is useful only when it carries an ownership rule. pi-ai owns the provider conversation, not the repository's permission policy. An extension can add a permission gate, but it does not become an operating-system sandbox. A session can preserve a branch, but it does not make a model's summary perfectly faithful. A TUI can show a tool call clearly, but presentation does not decide whether the underlying effect is safe. The repository's architecture is strongest when these distinctions remain visible.

The supporting packages widen the possible host without changing that principle. TUI supplies reusable terminal components. Chord supplies a more general way to compose services and replicated state across facets. Telemetry supplies an explicit diagnostic contract that an application can connect to its own backend. The experimental protocol, client, and server packages explore remote presentation and routing. They are not interchangeable replacements for the local coding-agent path; they are additional boundaries with their own lifecycle and maturity conditions. The central subject remains the same: a harness coordinating model reasoning, external effects, and durable state under policies chosen at the surrounding seams.

flowchart TB U["User request"] --> CLI["coding-agent CLI"] CLI --> H["Pi Agent Harness"] H --> CORE["agent core"] CORE --> AI["pi-ai provider layer"] H --> SES["sessions and compaction"] H --> EXT["extensions and packages"] H --> UI["TUI / SDK / RPC boundaries"] HOST["host-controlled security"] -. constrains .-> H

The diagram is intentionally uneven. The model provider is not the whole agent, and the terminal interface is not the whole product. They are replaceable boundaries around a shared harness. That arrangement is what allows the same underlying behavior to appear as an interactive terminal conversation, a one-shot command, a JSON event stream, or a component embedded in another application.

What Pi chooses not to own

Pi's minimalism is easiest to understand through its omissions. The coding-agent documentation says that Pi does not build sub-agents, plan mode, built-in to-dos, permission popups, background bash, or MCP into the core. This does not mean those workflows are impossible. It means they are not treated as universal answers. An extension can add a plan mode. A package can add a sub-agent strategy. A user can use a file-based task list or a tool that fits a particular repository.

That choice rejects a familiar assumption: more built-in features do not automatically make an agent more adaptable. A feature baked into the core arrives with assumptions about how work should be planned, how tools should be authorized, or how output should be displayed. If those assumptions are wrong for a team, the team either works around them or forks the product. Pi instead keeps a smaller default: read, write, edit, and bash are the normal coding tools, while other capabilities can be selected or supplied by the host.

This is not minimalism as austerity. It is minimalism as an ownership decision. Pi owns the machinery needed to make a model-driven coding turn work. It does not claim ownership of every workflow that can be built around that machinery. The difference is visible in the extension surface. TypeScript modules can register tools, intercept tool calls, change prompts, add commands, customize rendering, persist their own state, register providers, or replace parts of the user interface. Skills and prompt templates can add reusable instructions without changing the runtime. Packages can bundle those resources for sharing.

The resulting system has a useful asymmetry. The core is stable enough to provide a common lifecycle, but the behavior a particular team cares about can remain close to that team's project. A repository may need a protected-path rule; another may need a deployment command; a third may need a domain-specific search tool. Those are not merely different settings of one universal workflow. They are different policies, and Pi gives them a place to live.

The seam is the product

Visible seams create two kinds of freedom. First, they let a user change the model without rewriting the agent loop. Second, they let an application change the workflow without rewriting provider adapters or terminal rendering. A custom provider can speak a supported API shape. An extension can add a tool or gate an existing one. A session can be stored in memory for a test or on disk for ongoing work. The same conceptual agent can be placed behind several interfaces.

But every seam also creates a question that a more integrated product might answer implicitly. Who is allowed to run a tool? What does a failed request mean? Which messages should survive compaction? Does a model switch preserve provider-specific reasoning? Should a package run in the project, the user's home directory, or a contained environment? Pi's architecture does not make those questions disappear. It makes them visible and gives them distinct control points.

The most important boundary is security. Pi explicitly does not include a built-in sandbox or a general permission system. Its tools and extensions run with the permissions of the process that launched it. Project trust can stop project-local settings and extensions from loading before approval, but it is not a restriction on what the model can ask the tools to do after work begins. Real containment must come from the operating system, a container, a VM, a micro-VM, or a policy-controlled sandbox. This is a direct consequence of the small-core thesis: the harness can expose a hook for a permission gate, but it cannot honestly pretend that an in-process check is the same as an isolation boundary.

This series will follow that distinction from the inside out. It will first trace what happens after a prompt is submitted. It will then examine the provider and authentication boundary, the tools and extension ecosystem, and the session model that keeps work coherent as context grows. From there, it will turn to the durable AgentHarness, integration boundaries such as the SDK and RPC, and the newer service-oriented pieces. Security, observability, and current maturity will be treated as part of the architecture, not footnotes added after the features.

Pi's wager is therefore precise: a coding agent becomes more useful when its core does less pretending. The model can propose; the harness can coordinate; extensions can shape a workflow; sessions can preserve a path; and the host can decide what environment is safe enough for effects. To understand whether that bargain works, the next step is to follow one request all the way through the control loop—past the model's text, to the tool call, its validation, its result, and the decision about what happens next.

References