Part II Chapter 1: The Map's Scale — pi-mono

Xiao A finished Part I · Concepts and opened pi-mono full of confidence. Three minutes later he was staring at the screen. Under packages/ lay more than a dozen directories: ai, agent, tui, coding-agent, protocol… He clicked one at random and found hundreds of files inside.
"Lao Z," he called out, "this map is enormous. Which directory do I start from?"
Lao Z walked over, glanced at the screen, and asked back: "What questions did Part I · Concepts leave you with?"
"Where, inside one real project, do the boundaries of the model, the loop, the session, and the terminal actually land," Xiao A recited — he had it memorized.
"Right. You are not here to 'finish reading Pi'. You are here to check those boundaries," Lao Z said. "So do not guess by directory. First fix the scale of the map, then draw the main path."
Every confirmed fact in this part is anchored to tag v1.0.0 of the upstream pi repository (github.com/earendil-works/pi, migrated in 2026-09 from badlogic/pi-mono), commit a13d35a742c6ef8462812a28fbe1d8c8b7431c32, committed 2026-10-01. This book keeps using the name pi-mono for that monorepo. The eight packages listed below all report version 1.0.0 in their package.json, while the root package.json gives the monorepo version as 0.0.3. The two must not be mixed. The pi/ directory in this book's repository is a snapshot of that tag with Chinese comments added; paths and symbols match upstream, but line numbers shift because of the comments.
That "eight" is this book's reading scope, not the total in the root workspace: packages/ holds 13 packages. The other five are chord, codemode, durable, mcp, and telemetry. chord and telemetry get named only where they come up; codemode and mcp get their own appendices (Appendix 10 and Appendix 11); durable is the subject of Part II Chapter 13. The root workspace also contains extension examples. packages/evals is a private workspace package, not a published one.
First, fix the scale of the map
Xiao A: "Eight packages… are they eight separate programs?"
"No," Lao Z said. "A workspace name and a published-package boundary prove a unit can be imported independently. They do not prove it runs as a separate process. Read the package metadata and the exports first, then look for evidence by following the control flow of one input."
Metaphor: the scale of a map
Set "how many kilometres one centimetre stands for" before the map can stop misleading you. The eight packages are this book's reading scale, not the whole repository, and certainly not eight separate processes.
What this book calls "the eight packages" means the eight product and infrastructure packages selected in the table below. Each package's responsibility comes from the name, description, dependencies, and exports in its package.json. A description is what the maintainer declares; it is not proof of everything the code does.
| Package | npm name | Boundary evidence | What this book follows it for | Not asserted in this chapter |
|---|---|---|---|---|
packages/ai | @earendil-works/pi-ai | root, api/*, providers/* and other exports | models, auth, stream adapters | the service quality of any vendor |
packages/agent | @earendil-works/pi-agent-core | depends only on pi-ai; root export only | Agent and the loop | session storage (no longer in this package) |
packages/tui | @earendil-works/pi-tui | terminal UI package with zero internal dependencies | components and rendering | Agent decisions |
packages/coding-agent | @earendil-works/pi-coding-agent | the pi binary entry; depends on 9 internal packages | CLI assembly, session, compaction, tools, extensions | the behaviour of all extensions |
packages/protocol | @earendil-works/pi-protocol | transport-neutral CBOR protocol package; depends on chord | frames, envelope schemas, versioning | network reliability |
packages/client | @earendil-works/pi-client | depends on chord and pi-protocol | remote client connection and requests | server-side policy |
packages/server | @earendil-works/pi-server | described as experimental; depends on chord and pi-protocol | handshake, request routing, session attachment | production deployment guarantees |
packages/evals | @earendil-works/pi-evals | private evaluation workspace package | harness, controlled experiments, judge | conclusions about product quality |
Xiao A's eyes stopped on the coding-agent row. "It depends on nine internal packages? And what about agent?"
"agent depends directly on AI only," Lao Z said. "That is also the easiest line in this table to skip: the generic loop is designed so it does not have to know the terminal, the remote protocol, or the session file. v1.0.0 also moved the experimental harness that used to sit in the agent package out entirely, and session and compaction landed in coding-agent. From this you may infer that the generic loop is independent of the product shell. What you may not infer from the dependency graph is that any product must persist sessions the same way."
Seven product changes in Pi 1.0
Xiao A asked, "Since the source baseline is v1.0.0, what did this release add for users?"
Lao Z said, "Start with the pi-coding-agent 1.0.0 release notes. They list seven product changes under New Features. These are highlights announced at the 1.0 release; they do not mean every underlying capability first appeared in 1.0. Codemode and MCP, for example, already existed in earlier versions, and 1.0 continued to improve them."
| 1.0 release highlight | Change | Read next |
|---|---|---|
| Full-screen terminal interface | The interactive UI uses full-screen mode by default; set tuiMode: "regular" to restore ordinary terminal scrolling. | Part II Ch. 6 Terminal; terminal settings |
| Leaner Codemode | The release notes report roughly 40% fewer prompt tokens under the default tool configuration and improved errors that show likely matches and repair directions for missing names. This figure is a release-note summary, not a fixed guarantee for every model and tool combination. | Codemode documentation |
| Image generation in Codemode | Scripts can call image models with models.generateImages() using the current session credentials; image() can return generated results to the model. | Codemode image generation |
| Radius login | /login can sign in to Radius and then configure the MCP servers it provides. | Provider documentation |
| Anthropic copy-code login | When the browser is on another machine, copy the authorization code from the authorization page and paste it back into Pi. This supports remote or headless environments. | Interactive authentication |
| Hardened MCP OAuth | You can specify authorization-server metadata; iss in the authorization response is checked according to RFC 9207; credentials are separated by server name and URL; extending permissions preserves granted scopes. | MCP OAuth documentation |
| Header-only startup | quietStartup: "header" keeps the version and shortcut hints while hiding the model scope and loaded-resource list. | Terminal settings |
"These seven changes mainly affect interaction, access to model capabilities, and authentication." Lao Z added, "One architectural change needs its own note: @earendil-works/pi-durable was first released in 1.0. In the same release, the experimental harness was removed from pi-agent-core as a breaking API change, and durable-session support moved to pi-durable. This is a package-boundary change, not one of the seven CLI features above, and it does not mean Pi stopped supporting durable sessions." (durable changelog; agent-core changelog)
Later chapters in Part II explain session, terminal, and extension boundaries in Ch. 5, Ch. 6, and Ch. 8. This section gives the version map; to verify the complete scope and each statement, return to the changelogs and documentation for the fixed v1.0.0 tag.
The eight packages in three layers: who depends on whom
The table still looked scattered to Xiao A. Lao Z took the pen and drew a three-layer diagram on paper. "Layer by dependency direction and the relationships read at a glance. An arrow means 'depends on'. The upper layer calls the lower one; the lower one does not know the upper one exists. The parentheses name packages this book does not open but does mention."
┌────────────────────────────────────────────────────────────────────────┐
│ Product layer │
│ ┌──────────────────────────────────────────┐ ┌────────────────────┐ │
│ │ pi-coding-agent │ │ pi-evals │ │
│ │ (CLI product, session, tools, extensions)│◀──│ (private evals) │ │
│ └──────────┬────────────────────┬──────────┘ └────────────────────┘ │
└─────────────┼────────────────────┼─────────────────────────────────────┘
│ │
┌─────────────┼────────────────────┼─────────────────────────────────────┐
│ Core layer ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ pi-agent-core │ │ pi-client │ │ pi-server │ │
│ │ (Agent + loop) │ │ (remote client) │ │ (request routing)│ │
│ └──────────┬───────┘ └────────┬─────────┘ └────────┬─────────┘ │
└─────────────┼────────────────────┼──────────────────────┼──────────────┘
│ └───────────────────┬──┘
┌─────────────┼────────────────────────────────────────┼─────────────────┐
│ Base layer ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ pi-ai │ │ pi-tui │ │ pi-protocol │ │
│ │ (model stream) │ │ (terminal UI) │ │ (codec, framing) │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ telemetry, chord, codemode, mcp, durable: named on use only │
└────────────────────────────────────────────────────────────────────────┘"Three points matter when reading this diagram," Lao Z said.
- The three base-layer packages do not depend on each other: pi-ai handles model streams only, pi-tui handles the terminal only, pi-protocol handles byte encoding only. pi-ai depends on telemetry and pi-protocol depends on chord; this book names those two only where they come up.
- The core layer sits on the base layer: pi-agent-core depends only on pi-ai; pi-client and pi-server both depend on pi-protocol and chord. In
v1.0.0pi-server no longer depends on AI or the CLI — it does handshake, routing, and attachment only.- The product layer assembles the parts below it: pi-coding-agent depends directly on agent, ai, tui, protocol, client, and server, plus chord, codemode, and mcp. pi-evals uses ai and coding-agent as development dependencies. The product layer is the thickest, and in
v1.0.0session and compaction live there too.
Xiao A: "So which layer do I start reading from?"
"Start from the core path. pi-ai and pi-agent-core are the spine of the whole main path. Dip into the base layer as needed — open protocol when you reach the protocol, open tui when you reach the terminal," Lao Z said. "That is the reading order of the thirteen chapters in this book's Part II · Pi in Source: Part II chapters 2 to 5 first, to get models, the loop, and the session working; then chapters 6 to 8 for the terminal and the product; then chapters 9 to 11 for protocol, remote, and evaluation; chapter 12 closes the main path; and chapter 13 opens one more engine — pi-durable, and how it answers 'what lets a session survive a crash' with atomic commits and task checkpoints."
Read the replaceable surface from the public exports
Xiao A: "Where is the entry? Do I just start at index.ts?"
"Yes, but first tell the root entry apart from the subpaths." Lao Z opened packages/ai/src/index.ts:
// packages/ai/src/index.ts (module entry; v1.0.0, excerpt)
// Core only, side-effect free: no generated catalogs, no provider factories,
// no api-registry, no OAuth implementations, no compat. Provider factories
// live under "@earendil-works/pi-ai/providers/*", API implementations under
// "@earendil-works/pi-ai/api/*", the old global API under
// "@earendil-works/pi-ai/compat".
export * from "./api/lazy.ts";
// ……the rest are type exports and modules such as models, types"Look at the comment at the top: the root entry holds core code only and carries no side effects. Provider factories live under providers/*, API implementations under api/*, and the old global API under compat. That is not just a directory convention — the exports field of package.json also states this subpath boundary," Lao Z said. "The code above only tells you which API the root entry can reach. It does not tell you what order one request goes through."
He then opened packages/agent/src/index.ts. "On this side v1.0.0 is down to five lines: it exports agent.ts, agent-loop.ts, proxy.ts, types.ts, and setDefaultStreamFn. Session storage, compaction, and skills are none of them here. That says the generic Agent binds to no persistence method. Treating 'Agent' as 'must write JSONL' is a false premise; the one that writes JSONL is coding-agent."
| Export layer | What a caller may rely on | What you cannot conclude from it |
|---|---|---|
pi-ai root entry | Models, the event stream, types, lazy-loading utilities | which Providers are registered |
pi-ai/providers/* | one Provider factory | that auth is configured or the model is definitely available |
pi-ai/api/* | the request and conversion implementation of one API | that other APIs behave the same way |
pi-agent-core root entry | Agent, the loop, and turn hooks | which storage or UI the host uses |
pi-coding-agent binary | the product entry of the pi CLI | that remote mode or any extension is enabled |
One main path, not a complete architecture diagram
Xiao A: "Then which line do I actually follow? I can't read every package through."
"The chapters on the main line go by control flow, not by reading filenames out of directories," Lao Z said. He drew a main path with station numbers, one station per chapter: start at the model request, pass through the loop and the session, and end by assembling the complete request. Part II Chapter 13 stands off that main line: it opens a second engine — pi-durable — and answers the crash-recovery question Part II Chapter 5 left behind.
① model request ② streaming ③ loop drives ④ session
┌───────────────┐ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ ch2 │──▶│ ch3 │──▶│ ch4 │──▶│ ch5 │
│ pi-ai │ │ pi-ai │ │ pi-agent │ │ coding-agent │
│ models.ts │ │ lazy.ts │ │ loop.ts │ │ session │
└───────────────┘ └───────────────┘ └───────────────┘ └───────────────┘
│
⑤ terminal ⑥ product startup ⑦ tool trust ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ ch6 │ │ ch7 │──▶│ ch8 │ │ ch12 │
│ pi-tui │ │ coding-agent │ │ coding-agent │ │ end-to-end │
│ main-screen │ │ startup │ │ tools, trust │ │ full request │
└───────────────┘ └───────────────┘ └───────────────┘ └───────────────┘
│ │ ▲
⑧ protocol bytes ⑨ remote parity ⑩ eval verdict │
┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ ch9 │──▶│ ch10 │ │ ch11 │───────────┘
│ protocol │ │ server │ │ evals │
│ codec.ts │ │ router │ │ harness │
└───────────────┘ └───────────────┘ └───────────────┘"Read this diagram in groups," Lao Z said.
- The main line is ①→②→③→④: the model request starts it, the stream passes through the adapter layer, the loop drives state forward, the session outlives the process. The first three stations stand on pi-ai and pi-agent-core. In
v1.0.0the session lives in coding-agent, so station ④ has already entered a core part of the product layer. Read these four chapters first.- ⑤⑥⑦ are the product layer: how the terminal refreshes, how the product starts, how tools and trust connect. They stand on the core layer. Read them as the second group.
- ⑧⑨⑩ are remote and evaluation: how the protocol becomes bytes, how remote stays consistent, how evaluation judges. They are separate capability boundaries; read them as needed. Part II Chapter 12 then assembles every station into one complete request.
Which files of which package each chapter reads
Xiao A: "What about specific files? When I open ch2, which file do I actually read?"
Lao Z drew up a full lookup table. "This table is your navigation for Part II · Pi in Source. When you open any chapter, first see which package it stands on and which core files it reads."
| Chapter | Which package you are reading | Core files (these are enough) | What this station answers |
|---|---|---|---|
| 1 The map | all eight packages | package.json × 8 | how the eight packages layer out, how the main path runs |
| 2 The model request | pi-ai | models.ts, auth/resolve.ts | how one request gets from auth to a Provider |
| 3 The streaming adapter | pi-ai | api/lazy.ts, api/openai-responses.ts | how vendor events become uniform events |
| 4 The loop | pi-agent-core | agent.ts, agent-loop.ts | how model output pushes state forward |
| 5 The session | pi-coding-agent | core/agent-session.ts, core/session-manager.ts | how a session outlives one process |
| 6 The terminal | pi-tui | tui-main-screen.ts, stdin-buffer.ts | why the terminal can refresh stably |
| 7 Startup | pi-coding-agent | main.ts, core/agent-session-services.ts | how the CLI product starts |
| 8 Tool trust | pi-coding-agent | tools/file-mutation-queue.ts, extensions/runner.ts | how tools and extensions connect safely |
| 9 The protocol | pi-protocol | codec.ts, framing.ts, protocol.ts | how messages become reliable bytes |
| 10 Remote | pi-server + pi-client | server.ts, session-router.ts, client.ts | how a remote session handshakes, routes, and attaches |
| 11 Evaluation | pi-evals | src/harness.ts, src/plan.ts, src/report.ts | how evaluation judges one change |
| 12 End-to-end | across all packages | (revisit the files of Chapters 2 to 11) | how one complete request runs to the end |
| 13 Durable runtime | pi-durable | session/session.ts, harness/scheduler.ts, harness/tool.ts, storage/ | how one commit and tool call recover after a crash |
"Note this. There are two kinds of data on this line. Do not merge them into one," Lao Z said, and added another table.
| Data | Its shape on the main path | The key boundary |
|---|---|---|
| configuration | model, headers, auth, signal, retry options | handed to the Provider only after ModelsImpl.applyAuth merges it |
| context | AgentMessage[], converted to AI Message[] before the model call | convertToLlm inside streamAssistantResponse |
| generation | start, delta, done / error on AssistantMessageEventStream | the adapter produces it, the loop consumes it |
| side effects | tool result, session append, terminal refresh | an Agent event and AgentSession writing the session are not the same thing |
Xiao A: "Why not just start at the CLI entry and read all the way to the network request? Isn't that more direct?"
"That line drags configuration, interactive mode, the terminal, and extensions in at the same time," Lao Z said. "Lock down the stream and loop boundaries that stay stable across packages first; otherwise you will mistake one product's assembly for the core mechanism. Part II Chapter 7: The Address on the Household Register — pi-coding-agent reconnects the CLI to this line later."
How to keep the evidence strong while reading
Xiao A opened his notebook, ready to start on the code. Lao Z stopped him. "Remember this first: source walkthroughs make three kinds of mistakes easily. I have made them. Don't."
Three common reading mistakes
- Expanding a comment or description into a runtime guarantee: the description of
pi-servercontains experimental, so all you may confirm is that it calls itself experimental. You may not fill that in as "unsuitable for any production scenario".- Reading optional typing as a certain missing implementation:
Provider.refreshModels?is an optional method, meaning a static Provider may have no refresh path. It does not mean every dynamic Provider refreshes successfully.- Flattening different failure outcomes into
false: an unknown Provider, unconfigured auth, an aborted stream, a failed tool, and a JSONL parse failure sit at different layers and need different recovery strategies. Later chapters compare them in one failure-semantics table.
"Read a fixed snapshot in this order: exports and types first; then the complete branches of the entry function; then follow into the interface implementation in the next package; finally use the tests, or one controlled call, to verify the three paths — normal, error, abort. When you have to reason across layers, mark it as reasoning. Do not supply the author's motives on the source's behalf."
What this split buys, and what it costs
"Is splitting it this way worth it?" Xiao A asked.
"Engineering opinion: organize reading around package boundaries plus the call chain. The gain is that when you replace a model adapter, a session backend, or the UI, you can look for the contract first instead of understanding all the product code first," Lao Z said. "The cost is that the reader has to travel between source files in several places, and without looking at a specific host you will not know where the default configuration comes from."
Xiao A went on: "What does this chapter deliberately leave out?"
"The complete model catalog of each Provider, the protocol fields of each API, the tool definitions, the TUI's event-subscription implementation, the Protocol's framing, Server / Client reconnection, and the evaluation cases," Lao Z said. "Those are for the matching call-chain chapters. This chapter does not open them."
Break the main path into reading stations you can stop at
Xiao A: "How long should a chapter take? Follow the whole chain in one go?"
"Don't. Source reading should not treat a chain as a long function you must finish in one sitting. Stop at every public type or event boundary and write down the input, the output, and the failure outcome. That catches cross-layer assumptions far better than recording only 'what this package does'." Lao Z handed over a reading-station table.
| Reading station | The smallest question | Next station | Where to look first on failure |
|---|---|---|---|
| model selection | whom does Model.provider point to | ModelsImpl | unknown Provider |
| request preparation | how credential and options merge | Provider.stream* | auth, header transform |
| stream adaptation | how upstream events become uniform events | runLoop | error, aborted, pending |
| Agent turn | what enters the next context | tool execution / queue | length, tool error, terminate |
| session persistence | which events get appended | AgentSession / SessionManager | write failure, bad line skipped |
| host display | who subscribes to events and refreshes | TUI | rendering, terminal, input boundary |
"For example: a network failure should not be explained by jumping straight from 'request preparation' to 'terminal display'. First check whether the Provider normalized it into an error event. Then check whether the loop wrote the final error message into the context. Only after that do you look at whether the session layer or the UI recorded or showed it," Lao Z said. "That is a reading order, not a runtime retry chain the source promises."
Xiao A: "But if a chapter reads only one function, won't I miss state that crosses files?"
"You will not read one function only. The point is that each chapter fixes one entry, one exit, and a few branches," Lao Z said. "When a function calls across files, keep following its direct contract instead of reading the whole package first. On a fixed commit, git grep can confirm a symbol exists, but existence does not replace reading from the call site to the return site, and the same type name does not mean the same semantics. For example, Context at the AI layer and an entry in the session tree sit at different levels of abstraction. This book marks that explicitly at every boundary."
Summary
This chapter answers the first question of Part II · Pi in Source: facing more than a dozen packages in pi-mono, where to start. The answer is not to read directory by directory. Pin three coordinates first: version, dependency direction, main path.
- Version coordinate: the baseline is upstream tag
v1.0.0(commita13d35a7). The eight packages this book selects are all1.0.0(pi-evalsis a private workspace package); the root monorepo is0.0.3. The two must not be mixed. The eight packages are the reading scope; the repository has 13 packages. - Dependency coordinate: the eight packages form three layers. The base-layer packages (ai, tui, protocol) do not depend on each other; the core layer (agent-core, client, server) stands on the base; the product layer (coding-agent, plus evals which uses it) does the assembly, and in
v1.0.0session and compaction live there too. The generic loop is designed so it does not have to know the terminal, the remote protocol, or the session file. - Path coordinate: the main path splits "input to stream, stream to loop, loop to session / UI" into boundaries you can verify one at a time. Each station is one chapter.
Remember
A package boundary is not a process boundary. A workspace name and a dependency graph prove "a unit that can be imported independently". They do not prove a separate process at runtime. When reading source, locate yourself with these three layer coordinates first, then follow the call chain, instead of reading filenames off directory by directory.
Source Walkthrough
- Clone the upstream repository and check out the tag:
git clone https://github.com/earendil-works/pi && cd pi && git checkout v1.0.0, then rungit rev-parse HEAD; the expected complete output isa13d35a742c6ef8462812a28fbe1d8c8b7431c32. Thepi/directory in this book's repository is the annotated snapshot of the same tag. It has no git history of its own: the paths match, the line numbers shift. - Run
node -p 'require("./packages/ai/package.json").version'and the same command for the agent package; confirm they report1.0.0, then compare against theversionin the rootpackage.jsonso the two are not mixed up. - Open the
exportsfield ofpackages/ai/package.json; record the root entry,./providers/*,./api/*, and./compatseparately. Do not treat a subpath as a root export. - Open the
dependenciesfield ofpackages/agent/package.json; confirm@earendil-works/pi-aiis there andpi-tuiis not, to check the direct-dependency claims in the table above. - Read the opening comment and the exports of
packages/ai/src/index.tsline by line; tell a runtimeexport *apart from anexport typethat only exports types. - Read the five export lines of
packages/agent/src/index.ts; then, underpackages/coding-agent/src/core/, findsession-manager.ts,compaction/, andskills.tsto confirm that session, compaction, and Skills belong to the product layer inv1.0.0.
