Pi · 1 of 5
The three-layer stack
Pi separates provider transport, a reusable agent loop, and a full coding-agent product. Read each behavior at the layer that owns it.
§1
Use the current repository and package names
The former badlogic/pi-mono GitHub URL redirected to earendil-works/pi when this primer was researched. Older articles may use historical package names or layouts. This extension uses the current canonical repository and pins every source claim to commit b1efcf7….
The pinned README names the project “Pi Agent Harness” and identifies the coding agent, agent core, and multi-provider API as separate principal packages. Source · README 13–34
§2
The layer map
| Layer | Package | Owns | Does not own by itself |
|---|---|---|---|
| Provider/model wire | @earendil-works/pi-ai | Unified provider/model types, message vocabulary, streaming adapters, tool argument validation support. | Session UX, coding tools, repository resources, or compaction policy. |
| Reusable harness | @earendil-works/pi-agent-core | Stateful agent, agent/tool event stream, context transformation boundary, tool scheduling, steering/follow-up queues. | Which files to read, the 2,000-line cap, interactive commands, or a permission product. |
| Coding-agent integration | @earendil-works/pi-coding-agent | CLI and TUI integration, built-in coding tools, resources, session tree, compaction, retries, extensions, skills, model/auth selection. | The provider wire implementation or a universal OS sandbox. |
Pi also publishes @earendil-works/pi-tui, a separate terminal-rendering library used by the coding product. It supports the integration layer; it is not a fourth agent-runtime layer.
§3
The reusable layer preserves application messages
The core runtime works with flexible AgentMessage values, including application-defined message types. Before each model call, optional transformContext() can prune or inject agent messages, and required convertToLlm() filters/translates them into the smaller provider message vocabulary. Source · message flow
AgentMessage[]
→ transformContext() // optional working-set policy
→ AgentMessage[]
→ convertToLlm() // required provider boundary
→ Message[]
→ model stream
This is the foundation’s transcript/working-set distinction in code. Custom UI or session records can remain in application state without being sent to the provider.
§4
The core is reusable because product policy sits above it
The reusable loop emits agent, turn, message, and tool events and accepts hooks for transformation, tool pre/post processing, and stopping. Coding-agent AgentSession wraps that loop with the concerns of an interactive developer tool: resource-built prompts, selected tools, model/auth validation, retries, compaction, branch navigation, and extension events. Source · AgentSession run
When reading Pi, label each finding AI, core, or coding. A number in the read tool is not an agent-core invariant. A tool-ordering rule in agent-loop.ts is reusable core behavior.
§5
Pi favors direct interfaces over a universal plugin kernel
The core loop takes functions and configuration: stream function, context transform, message conversion, tool hooks, queue accessors, and a stop callback. The coding product then exposes a broad extension API around its own lifecycle.
This keeps the minimal loop trace compact. Permission and confinement depend on the embedding product or extension rather than a mandatory capability-seam architecture.