English lecture · source-traced

Pi Agent Harness source analysis

**A self-extensible coding-agent monorepo architecture** · TypeScript · npm workspaces · 9 core packages · 1034 TS files commit `main` · MIT · 1034 TS/TSX Every claim is marked `file:line`, traceable to concrete source.

EN
lecture
source
file:line
no
embellishment
中文
sibling

A self-extensible coding-agent monorepo architecture · TypeScript · npm workspaces · 9 core packages · 1034 TS files

commit main · MIT · 1034 TS/TSX

Every claim is marked file:line, traceable to concrete source.


Part 1

I. Project overview

1.1 What Pi is

Pi is not another terminal coding agent. It is infrastructure for a self-extensible coding agent—a monorepo that splits “Agent” into 9 core npm packages, glued by explicit contracts:

Package What it is File count
pi-ai unified multi-provider LLM API (38 providers) 305
pi-agent-core Agent runtime (two-layer while loop + tools + state) 62
pi-coding-agent interactive coding-agent CLI (assembly layer) 488
pi-tui terminal UI library (differential rendering) 74
pi-protocol binary wire protocol (CBOR + length framing) 12
pi-server Unix-socket session server 38
pi-client client SDK (lease model) 18
pi-storage-sqlite-node SQLite event sourcing (harness SessionStore; not the CLI default path) 12
pi-evals agent-behavior evaluation framework 14

Its relationship to Claude Code: Claude Code is a monolith CLI, all capability stuffed into one process. Pi splits the Agent into protocol, server, client, storage, runtime, UI six layers, each an independent package—protocol-driven composable agent infrastructure.

Its relationship to CodePilot: CodePilot is an Electron + Next.js desktop client; Pi is a terminal CLI. CodePilot outsources three Runtimes to others; Pi wrote all of the core itself. CodePilot has no protocol layer; Pi has an explicit binary protocol.

1.2 The project shape in numbers

Dimension Number Note
TS/TSX files 1034 excluding node_modules
npm packages 8 workspaces monorepo
Providers 38 OpenAI/Anthropic/Google/Bedrock/DeepSeek…
Built-in tools 7 read/write/edit/bash/grep/find/ls
TUI components 17+ box/text/markdown/editor/scroll-view…
Extension hooks 30+ tool_call/before_provider_request/session_before_compact…
Slash commands 22 /settings /model /fork /tree /compact /share…
Evals 2 smoke + extensions
License MIT fully open source

1.3 Three most distinctive designs

  1. Self-extensible: Extensions dynamically load TS files with jiti, 30+ event hooks + registerTool/registerCommand/registerShortcut/registerFlag. The agent can add tools, commands, shortcuts to itself at runtime.

  2. Protocol-driven process split: pi-protocol (CBOR + length framing + TypeBox validation + token auth) decouples server and client. Multiple clients can attach to the same session; the server does snapshot broadcast.

  3. Differential-rendering TUI: pi-tui has no React, no Virtual DOM, no flexbox—a component is render(width): string[], rendering diffs line-by-line strings and only paints changed lines, with \x1b[?2026h/l synchronized refresh.

1.4 The fundamental difference from Claude Code

Dimension Claude Code Pi
Architecture monolith CLI monorepo (9 core packages)
Protocol none (in-process calls) binary wire protocol (CBOR)
Agent loop single-layer while two-layer while (follow-up + tool-call)
Provider locked to Anthropic 38 providers
TUI Ink (React + Yoga full redraw) home-grown differential rendering
Permissions five-gate verdict not built in (containerization)
Extension plugins + MCP Extensions + Skills (MCP external; official No MCP)
Persistence JSONL files CLI default JSONL (SessionManager); also provides SQLite SessionStore
Sessions one process, one session multi session + attach/detach + fork
License MIT MIT

Part 2

II. Panorama

2.1 Package-dependency topology

pi-protocol  ←  pi-server  ←  pi-client
                  ↑                  ↓
           (PiSessionBackend)  (SessionHandle)
                  ↑                  ↓
pi-storage(sqlite)           pi-coding-agent  ←  pi-evals
                                  ↑
                            pi-agent-core  ←  pi-ai
                                  ↑
                              pi-tui
  • pi-protocol is the bottom-most contract, shared by server/client/storage three parties
  • pi-server implements PiSessionBackend/PiSessionRuntime interfaces; the runtime is actually provided by coding-agent
  • pi-client is the server’s peer SDK, mirroring protocol commands
  • pi-storage implements pi-agent-core’s SessionStore, used by coding-agent’s SessionManager
  • pi-evals stands at the top, treating coding-agent as the object under test

2.2 Process topology

┌──────────────────────────────────────────────────────┐
│  pi-server (常驻 daemon)                              │
│  ├── Unix domain socket 监听器                        │
│  ├── LiveSessionManager (多 session 并发)              │
│  ├── PiSessionBackend → AgentSession (coding-agent)   │
│  ├── JSONL SessionManager(CLI 默认)/ SQLite SessionStore(harness) │
│  └── 快照广播 (revision 化)                            │
└────────────────────┬─────────────────────────────────┘
                     │ CBOR + 长度帧 (pi-protocol)
┌────────────────────▼─────────────────────────────────┐
│  pi-client (SDK)                                      │
│  ├── Connection (握手 + 状态机)                       │
│  ├── SessionHandle (租约模型: shared/exclusive)       │
│  └── ClientState (本地快照镜像 + 监听器)              │
└────────────────────┬─────────────────────────────────┘
                     │
┌────────────────────▼─────────────────────────────────┐
│  pi-coding-agent (交互式 TUI / print / rpc)          │
│  ├── InteractiveMode (pi-tui 差分渲染)                │
│  ├── AgentSession (← pi-agent-core Agent)            │
│  ├── Extensions (jiti 动态加载, 30+ 钩子)             │
│  ├── Skills (SKILL.md + agentskills.io)              │
│  ├── Tools (read/write/edit/bash/grep/find/ls)       │
│  └── .pi/ 资源目录 (extensions/skills/prompts/themes) │
└──────────────────────────────────────────────────────┘

2.3 Data flow: from user input to AI response

用户输入 → pi-tui Input 组件
  → InteractiveMode.handleInput()
  → AgentSession.prompt(text)
  → AgentHarness.prepareContext()
    ├── Session.buildContext() (消息树 → 线性消息)
    ├── SystemPrompt 重建
    ├── Skills 注入 (<available_skills> XML)
    └── Compaction 检查
  → Agent.runWithLifecycle()
    → runLoop() (双层 while)
      ├── streamAssistantResponse() → pi-ai Models.streamSimple()
      │   → lazyStream → requireProvider → applyAuth → provider.streamSimple()
      │   → AssistantMessageEventStream (start → text_delta → toolcall → done)
      ├── executeToolCalls() (parallel: Promise.all)
      │   ├── prepareToolCall (validate + beforeToolCall hook)
      │   ├── execute (tool.execute)
      │   └── finalize (afterToolCall hook)
      ├── prepareNextTurn() (harness: 重建 context + compaction)
      └── shouldStopAfterTurn()
  → AgentEventSink → AgentSession → pi-tui 重绘
  → SessionManager JSONL 持久化(CLI 默认;harness 可用 SQLite SessionStore)

Part 3

III. pi-ai: unified multi-provider LLM API

3.1 38 providers

packages/ai/src/providers/all.ts:87-128’s builtinProviders() / KnownProvider export 38 providers:

amazon-bedrock, anthropic, azure-openai-responses, cerebras, cloudflare-ai-gateway, cloudflare-workers-ai, deepseek, fireworks, github-copilot, google, google-vertex, groq, huggingface, kimi-coding, minimax, mistral, moonshotai, nvidia, openai, openai-codex, opencode, openrouter, qwen-token-plan, radius, together, vercel-ai-gateway, xai, xiaomi, zai, etc.

3.2 Unified interface

Core abstractions in models.ts:

  • Provider<TApi> (models.ts:75): {id, name, baseUrl, auth, getModels(), stream(), streamSimple()}
  • Models (models.ts:127): provider container + auth resolution + unified stream/streamSimple/complete/completeSimple
  • ModelsImpl.streamSimple() (models.ts:512): lazyStream → requireProvider → applyAuth → provider.streamSimple()

10 KnownApi (types.ts:16-26): - openai-completions / openai-responses / azure-openai-responses / openai-codex-responses - anthropic-messages / bedrock-converse-stream - google-generative-ai / google-vertex - mistral-conversations / pi-messages

Each API is implemented in src/api/<name>.ts, exporting stream and streamSimple two StreamFunctions. Provider files are extremely thin (e.g. openai.ts only 15 lines), only responsible for createProvider({id, auth, models, api: openAIResponsesApi()}).

3.3 lazyStream: load on demand

api/lazy.ts:46’s lazyStream:

  • Synchronously returns a stream; asynchronously runs setup in the background (auth resolution, dynamic import)
  • lazyApi() (lazy.ts:68) wraps dynamic import()—provider implementations load on demand
  • The main bundle contains no SDK
  • anthropic-messages.ts is as high as 1351 lines (including @anthropic-ai/sdk calls, cache_control, thinking), but only loads when used

3.4 Relationship to Vercel AI SDK

None. pi-ai is an independent implementation, neither depending on nor compatible with @ai-sdk/*. pi-ai defines its own message/tool/event protocol and implements each provider’s SDK adaptation itself.

3.5 models.generated.ts

Only 2 .generated.ts: models.generated.ts (118 lines) and image-models.generated.ts (609 lines). Generated by scripts/generate-models.ts, aggregating 38 providers/<name>.models.ts exports into a MODELS dictionary. Real model metadata lives in providers/data/<name>.json.


Part 4

IV. pi-agent-core: Agent runtime

4.1 Core files

File Lines Note
agent-loop.ts 792 bottom-layer stateless agent loop
agent.ts 577 stateful Agent class (queue + lifecycle)
types.ts 437 core types
harness/agent-harness.ts 1185 upper-layer AgentHarness
harness/types.ts 980 harness-layer types
harness/session/session.ts 528 Session tree + context construction
harness/compaction/compaction.ts 880 context compaction
proxy.ts 367 LLM HTTP streamProxy (proxies a remote model stream; not MCP)

4.2 Two-layer while agent loop

agent-loop.ts:155-275’s runLoop():

// 外层:follow-up 消息
while (true) {
  // 内层:tool-call 循环
  while (hasMoreToolCalls || pendingMessages.length > 0) {
    // 1. 流式拉取 assistant 响应
    streamAssistantResponse() // → streamFn → AssistantMessageEventStream

    // 2. 错误/截断检查
    if (stopReason === "error" || "aborted") break
    if (stopReason === "length") failToolCallsFromTruncatedMessage()

    // 3. 执行工具(parallel: Promise.all)
    executeToolCalls()

    // 4. prepareNextTurn 钩子(compaction、刷新 system prompt)
    prepareNextTurn?.()

    // 5. shouldStopAfterTurn 钩子
    if (shouldStopAfterTurn?.()) break
  }

  // 外层:检查 follow-up
  const followUps = getFollowUpMessages?.()
  if (!followUps?.length) break
}

The essential difference from Claude Code:

Dimension Claude Code pi-agent-core
Loop structure single-layer while two-layer while (follow-up + tool-call)
Stream attach call Anthropic SDK directly inject via StreamFn (provider-agnostic)
Tool execution serial default parallel (Promise.all)
Control flow hard-coded parameterized by 4 callback hooks
Message model provider-native AgentMessage + CustomAgentMessages (declaration merging)

4.3 Tool interface

AgentTool<TParameters, TDetails> (types.ts:380-403) extends pi-ai’s Tool:

interface AgentTool<TParameters, TDetails> {
  label: string
  prepareArguments?(input): TransformedInput  // schema 验证前兼容垫片
  execute(toolCallId, params, signal, onUpdate): Promise<ToolResult>
  executionMode?: "sequential" | "parallel"
}

Execution pipeline (agent-loop.ts:600-754): 1. prepareToolCall: find the tool → prepareArguments → validateToolArguments → beforeToolCall hook (can block) 2. executePreparedToolCall: call tool.execute, supporting onUpdate streaming partial results 3. finalizeExecutedToolCall: afterToolCall hook (can override content/details/isError/usage/terminate)

Errors do not throw—wrapped with createErrorToolResult, following the contract “errors are encoded into a toolResult message.”

4.4 Three-layer state management

  1. AgentContext (types.ts:406): a pure snapshot, {systemPrompt, messages, tools}, snapshot every turn
  2. Agent class (agent.ts:171): holds MutableAgentState, subscribe() listens to events, steer()/followUp() go through PendingMessageQueue
  3. AgentHarness (agent-harness.ts:173): on top of Agent stacks Session persistence, compaction, skills, prompt templates, provider hooks

4.5 AgentMessage: an open union type

AgentMessage = Message | CustomAgentMessages[keyof ...] (types.ts:319)—extend custom messages via declaration merging (the harness injects bashExecution/custom/branchSummary/compactionSummary).

convertToLlm (messages.ts:120) projects these custom messages into LLM-understandable user messages or filters them out. This is one of the largest architectural differences between pi and Claude Code—UI/storage can use rich messages while the LLM only sees standard messages.


Part 5

V. pi-coding-agent: interactive coding agent

5.1 Entry and modes

  • CLI entry: src/cli.ts (20 lines) → main(process.argv.slice(2))
  • Main dispatch: src/main.ts (917 lines), core function main() at L521
  • Three modes: interactive (TUI) / print (one-shot output) / rpc (JSON-RPC over stdio for IDE integration)

5.2 Built-in tools

src/core/tools/index.ts:83 defines 7 tools: read | bash | edit | write | grep | find | ls

  • Factory functions: createCodingTools (read/bash/edit/write), createReadOnlyTools (read/grep/find/ls), createAllTools
  • Each tool injects the underlying fs/exec implementation via an Operations interface—easy for an extension to replace (e.g. Gondolin)

5.3 Permission system

Pi does not build in a permission system. README.md:39:

Pi does not include a built-in permission system for restricting filesystem, process, network, or credential access.

Three alternatives (docs/containerization.md): 1. Gondolin extension: route tools into a local Linux micro-VM (QEMU) 2. Plain Docker: run the whole pi process in a container 3. OpenShell (NVIDIA): policy sandbox

5.4 The .pi/ directory

config.ts:491 defines CONFIG_DIR_NAME = ".pi":

  • Global ~/.pi/agent/: auth.json, models.json, settings.json, themes/, prompts/, sessions/, extensions/, skills/
  • Project <cwd>/.pi/: extensions/, skills/, prompts/, themes/, AGENTS.md|CLAUDE.md

5.5 Extensions system

core/extensions/ (loader 713 lines / runner 1236 lines / types 1713 lines):

  • Dynamically load TS extensions with jiti
  • VIRTUAL_MODULES (loader.ts:50) pre-injects pi-agent-core/pi-ai/pi-tui/typebox
  • ExtensionAPI (types.ts:1193) exposes 30+ event hooks:
  • tool_call / before_provider_request / session_before_compact
  • registerTool / registerCommand / registerShortcut / registerFlag
  • Also supported under Bun compiled binaries

5.6 Skills system

core/skills.ts (487 lines). Spec follows agentskills.io:

  • Discover: loadSkillsFromDir (L168) recursively scans SKILL.md
  • Parse: loadSkillFromFile (L277) uses parseFrontmatter to extract name/description
  • Source grades: user (~/.pi/agent/skills), project (<cwd>/.pi/skills), path (--skills)
  • Inject: formatSkillsForPrompt (L335) generates an <available_skills> XML block, instructing the model to load the skill file with the read tool

5.7 AgentSession

core/agent-session.ts (3332 lines) is the bridge between pi-coding-agent and pi-agent-core:

  • Holds Agent (agent-core’s looper)
  • agent.subscribe() takes over events
  • Stacks: session persistence, compaction, branch summary, retry, bash abort, extension runner, tool registry, system-prompt rebuild, steering/follow-up/asides message queues

Part 6

VI. pi-tui: differential-rendering terminal UI

6.1 Rendering engine

src/tui.ts (1223 lines) + TuiMainScreen.ts (552 lines) + TuiAltScreen.ts (805 lines).

Component model: the Component interface (tui.ts:23) is only render(width): string[] + handleInput? + invalidate(). Extremely minimal, no Virtual DOM.

Render scheduling: requestRender() → setTimeout throttle (MIN_RENDER_INTERVAL_MS = 16, about 60fps).

6.2 Differential-rendering algorithm

TuiMainScreen.doRender() (TuiMainScreen.ts:146):

  1. render(width) renders all components as an array of string lines
  2. Line-by-line diff: walk previousLines vs newLines, find firstChanged/lastChanged
  3. Incremental write: wrap with \x1b[?2026h…\x1b[?2026l (synchronized output)
  4. Only for the firstChanged..lastChanged interval write \x1b[2K + new content
  5. Kitty images special-cased: incrementally delete disappeared image IDs

6.3 The essential difference from Ink

Dimension Ink (Claude Code) pi-tui
Framework React + Yoga no framework
Layout flexbox handwritten layout.ts
Rendering full redraw (each frame reconcile→render the whole tree) line-by-line string diff, only paint changed lines
Abstraction thick (reconciler + component tree) thin (a component is render(): string[])
I/O much (full output) little (only write changed lines)
Flicker yes (needs double buffering) none (synchronized output)

Essence: Ink is “retained mode + full reconcile”; pi-tui is “immediate mode + dirty-line refresh.” pi-tui is thinner, I/O less, friendlier to slow terminals, but layout capability is weak.

6.4 Keyboard input

src/keys.ts (1401 lines): - Supports both traditional escape sequences and the Kitty keyboard protocol - parseKey(data) returns KeyId - KeybindingsManager: configurable KeyId→action

src/stdin-buffer.ts (434 lines): splits stdin chunks into atomic events at ESC-sequence boundaries.


Part 7

VII. pi-protocol: binary wire protocol

7.1 Design

packages/protocol/ (12 TS files):

  • Not JSON-RPC, not MCP—a custom request/response + event-stream protocol
  • Each message = 4-byte big-endian length prefix + CBOR-encoded payload (framing.ts:1-39)
  • DEFAULT_MAX_FRAME_LENGTH = 16MB
  • PROTOCOL_VERSION = 2 (schemas.ts:3)

7.2 Message kinds

Client: ClientHello (token + version), RequestEnvelope (id + command)

Server: ServerHello (includes ServerSnapshot), ResponseEnvelope (ok/error), EventEnvelope

Commands: list / create / attach / detach / prompt / steer / abort / set_model / set_thinking

Events: server_snapshot / session_snapshot / session_progress / session_removed

7.3 Core data structures

TranscriptItem (user/assistant/tool; assistant has streaming/complete/error/aborted four states), TranscriptProgress (item_started/assistant_delta/item_updated/item_finished, incremental streaming), SessionSnapshot (includes revision + transcript + queuedSteer).

7.4 Relationship to MCP

Officially explicit No MCP (packages/coding-agent/README.md:495 / packages/coding-agent/docs/usage.md:301; there is no docs/ at the repo root): no built-in MCP client; when needed, go through an external extension (e.g. the example pi-mcp-adapter). pi-protocol is a session-level protocol (create/attach/detach/abort + transcript sync), orthogonal to the MCP tool protocol—do not mix the two.


Part 8

VIII. pi-server: Unix-socket session server

8.1 Architecture

packages/server/src/server.ts (399 lines):

  • Not an HTTP server, not WebSocket—a byte-stream session server based on a Unix domain socket
  • At construct time inject PiSessionBackend (durable storage boundary) and token (sha256 + timingSafeEqual check)
  • accept() → handshake timeout 5s → auth + version check → reply ServerHello + full ServerSnapshot

8.2 LiveSessionManager

sessions.ts:43-353:

  • Maintains a liveSessions Map
  • acquire() uses openingSessions to prevent concurrently opening the same session
  • maybeDispose() unloads the runtime when there is no connection + idle + no operation
  • handleRuntimeEvent() forwards the runtime’s progress/snapshot events to attached connections

8.3 Snapshot broadcast

ServerSnapshotPublisher does revisioned snapshot broadcast—clients discard old snapshots by monotonically increasing revision.


Part 9

IX. pi-client: client SDK

9.1 Connection model

PiClient.connect(options) static method does construct + connect in one step:

  1. transportFactory creates a ByteTransport, connects to the Unix socket path
  2. Connection.connect() sends ClientHello (version + token)
  3. Receive ServerHello → state disconnected → connecting → connected

9.2 Session leases

  • createSession() → exclusive lease
  • attachSession() → shared lease
  • Reference counting; detach is sent only when the last release happens
  • SessionHandle exposes prompt/steer/abort/setModel/setThinking

Part 10

X. pi-storage-sqlite-node: SQLite event sourcing (optional SessionStore)

Clear the path first: everyday pi CLI SessionManager writes sessions as *_sessionId.jsonl (supports /fork /tree /compact). @earendil-works/pi-storage-sqlite-node implements the harness’s SessionStore, not used by default by coding-agent’s SessionManager. What follows is the design of this optional SQLite path, not CLI default behavior.

10.1 Database

packages/storage/sqlite-node/:

  • Uses Node 22+ built-in node:sqlite, no third-party native dependency
  • PRAGMA journal_mode=WAL; synchronous=FULL; busy_timeout=5000

10.2 Event sourcing

6 tables: - sessions (id, cwd, parent_session_id, active_leaf_id, metadata) - session_entries (session_id, id, entry_seq, parent_id, type, timestamp, payload)— append-only event log - session_sequences (one next_seq counter per session) - branch_entries (materialized branch paths) - session_materialized + entry_materialized (cached current-state projections)

Entry types: message / thinking_level_change / model_change / active_tools_change / compaction / branch_summary / custom / custom_message / label / session_info / leaf

10.3 Materialized views

On appendEntry() also update session_materialized (whole-session summary) and entry_materialized (split by type), avoiding replaying the whole tree on read.

10.4 Branches and fork

  • A leaf entry triggers materializeBranch() to recompute branch_entries
  • A compaction entry supports retainedTail + firstKeptEntryId for compaction + keep the tail
  • fork() copies a selection into a new session inside a transaction

Part 11

XI. Design philosophy and trade-offs

11.1 Why a monorepo

Pi’s most core architectural decision is splitting the Agent into orthogonal packages. Why?

  • pi-protocol: the protocol layer decouples server and client; multiple clients can attach the same session
  • pi-ai: the provider layer is independent; 38 providers each load on demand
  • pi-agent-core: the runtime layer is independent, bound to no provider
  • pi-tui: the UI layer is independent, replaceable with a Web UI
  • pi-storage: the storage layer is independent, replaceable with PostgreSQL

Cost: high engineering complexity (dependency management, version sync, build order of 9 core packages), long debug chain.

11.2 Why no built-in permissions

Pi explicitly chooses not to build in a permission system. Why?

  • Permission systems are too complex (Claude Code’s five-gate verdict), and every user’s scene is different
  • Containerization is safer (OS-level isolation is far more reliable than an application-level popup)
  • Sink isolation to the OS/VM layer, let the user pick a fitting scheme

Cost: default run has no protection; the user must containerize themselves.

11.3 Why a home-grown TUI

  • Ink’s full redraw flickers badly on slow terminals
  • Differential rendering only paints changed lines, I/O extremely little
  • No React dependency, smaller bundle

Cost: weak layout (no flexbox); components must handle wrap/truncate themselves.

11.4 Trade-off contrast with Claude Code

Dimension Claude Code Pi Why the trade-off
Architecture monolith monorepo simple vs composable
Protocol none binary zero cost vs many clients
Provider locked to Anthropic 38 deep optimization vs open
TUI Ink full redraw differential rendering strong layout vs little I/O
Permissions five-gate verdict not built in safety vs simple
Extension plugins + MCP Extensions + Skills (MCP external) limited vs self-extensible
Persistence JSONL CLI also JSONL; plus SQLite SessionStore both a file tree + optional DB
Sessions single process multi session + fork single vs flexible

11.5 Three most distinctive designs

  1. Self-extensible Extensions: 30+ event hooks + registerTool/Command/Shortcut/Flag; the agent adds capability to itself at runtime
  2. Protocol-driven process split: CBOR + length framing + TypeBox validation + token auth; multiple clients attach the same session
  3. Differential-rendering TUI: no React, line-by-line diff, synchronized output, extremely little terminal I/O

Part 12

Side-by-side: Pi’s position outside the seventeen

Vs Claude Code (the most direct counterpart)

Dimension Claude Code Pi Essence of the difference
Architecture monolith CLI monorepo 9 core packages integrated vs composable
Agent loop single-layer while two-layer while imperative vs parameterized
Provider locked to Anthropic 38 closed vs open
TUI Ink (React + Yoga) differential rendering retained mode vs immediate mode
Permissions five-gate verdict not built in application-level vs OS-level
Extension plugins + MCP Extensions + Skills (MCP external; official No MCP) limited vs self-extensible
Persistence JSONL CLI JSONL + optional SQLite SessionStore default both a file tree
Sessions single process multi session + attach/detach single vs many clients
License MIT MIT the same

Vs CodePilot (both multi-provider)

Dimension CodePilot Pi Essence of the difference
UI Electron desktop GUI terminal TUI desktop vs terminal
Providers 17+ 38 fewer vs more
Runtime three replaceable one (agent-core) multi-engine vs single-engine
Protocol HTTP/SSE binary CBOR Web vs Unix
Extension MCP + Skills Extensions + Skills (MCP external) standard vs self-extensible
Persistence SQLite CLI JSONL; harness optional SQLite event sourcing different product defaults
Permissions three layers not built in has vs has not
Sessions single session + rewind multi session + fork rollback vs branch

Vs open-design (both “host shapes that do not write the agent loop”)

Dimension open-design Pi Essence of the difference
Host object 25 CLIs 38 providers quantity vs depth
Core innovation adapter as data self-extensible Extensions data vs code
Agent loop does not write (outsourced) two-layer while (home-grown) pure host vs autonomous
Protocol HTTP/SSE binary CBOR Web vs Unix

Pi’s distinctive contribution

Pi brought the field of agent engineering three designs none of the previous seventeen had:

  1. Self-extensible: Extensions add tools/commands/shortcuts to themselves at runtime. This is not “plugins” (plugins are predefined extension points); it is “the agent can change its own capability surface.”

  2. Protocol-driven process split: split the Agent into server/client/storage/runtime four layers, glued by an explicit binary protocol. That is what makes multiple clients attaching the same session possible.

  3. Differential-rendering TUI: no React, no Virtual DOM, line-by-line diff only paints changed lines. On a slow terminal flicker is tiny, I/O extremely little.


Part 13

Appendix: key-file index

File Lines Note
packages/agent/src/agent-loop.ts 792 agent loop
packages/agent/src/agent.ts 577 Agent class
packages/agent/src/types.ts 437 core types
packages/agent/src/harness/agent-harness.ts 1185 AgentHarness
packages/agent/src/harness/session/session.ts 528 Session tree
packages/agent/src/harness/compaction/compaction.ts 880 context compaction
packages/agent/src/proxy.ts 367 LLM HTTP streamProxy (not MCP)
packages/ai/src/models.ts — provider container
packages/ai/src/api/lazy.ts — lazyStream
packages/ai/src/providers/all.ts — 38 providers
packages/coding-agent/src/main.ts 917 main dispatch
packages/coding-agent/src/core/agent-session.ts 3332 AgentSession
packages/coding-agent/src/core/sdk.ts 398 SDK bridge
packages/coding-agent/src/core/skills.ts 487 Skills system
packages/coding-agent/src/core/extensions/loader.ts 713 Extensions loader
packages/coding-agent/src/core/extensions/runner.ts 1236 Extensions runner
packages/coding-agent/src/core/resource-loader.ts 1096 resource loader
packages/coding-agent/src/core/tools/index.ts 196 tool aggregation
packages/tui/src/tui.ts 1223 TUI base class
packages/tui/src/TuiMainScreen.ts 552 main-screen differential rendering
packages/tui/src/TuiAltScreen.ts 805 Alt-screen layout
packages/tui/src/keys.ts 1401 keyboard input
packages/protocol/src/schemas.ts 447 protocol schema
packages/protocol/src/framing.ts 165 length framing
packages/server/src/server.ts 399 PiServer
packages/server/src/sessions.ts 353 LiveSessionManager
packages/client/src/client.ts 433 PiClient
packages/storage/sqlite-node/src/sqlite/session-store.ts 275 Session storage

This lecture is written from a file-by-file analysis of pi’s main branch. Every file:line citation has been re-checked with grep -n / sed -n.

↑