Agent-readable wiki

Pi Developer Reference Wiki

pi is a TypeScript monorepo for a provider-neutral coding-agent harness, spanning model APIs, an agent runtime, a CLI, and a terminal UI library. Repository code is the source of truth; no STRATEGY.md or docs/solutions sources were present, and the requested bundled Compound Engineering page-shape guidance is treated as portable workflow guidance.

Pages

  1. Technical OrientationWhat the repo is, how the four packages fit together, the main entry points, and how to navigate the rest of the developer reference.
  2. Agent Core RuntimeThe reusable agent loop, event stream, tool-call execution model, harness layer, session storage, and compaction responsibilities.
  3. Model and Provider AbstractionThe provider-neutral LLM surface: model metadata, stream contracts, lazy provider registration, API-key resolution, OAuth hooks, and custom provider wiring.
  4. CLI, Session, and Tool LifecycleHow the pi executable parses inputs, creates cwd-bound services, resolves models and settings, persists sessions, dispatches built-in tools, and bridges into the core agent.
  5. Interactive, Print, RPC, and TUI SurfacesThe runtime adapters above AgentSession: terminal interaction, print and JSON modes, JSONL RPC protocol, keybindings, editor components, overlays, and differential rendering.
  6. Resources, Extensions, Skills, and PackagesThe portable extension layer for loading repository, file, npm, git, and catalog-like resources without binding the architecture to one model provider or hosted service.

Complete Markdown

# Pi Developer Reference Wiki

> pi is a TypeScript monorepo for a provider-neutral coding-agent harness, spanning model APIs, an agent runtime, a CLI, and a terminal UI library. Repository code is the source of truth; no STRATEGY.md or docs/solutions sources were present, and the requested bundled Compound Engineering page-shape guidance is treated as portable workflow guidance.

## Context Links

- [Agent index](https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/llms.txt)
- [Human interactive wiki](https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c)
- [GitHub repository](https://github.com/earendil-works/pi)

## Repository Metadata

- Repository: earendil-works/pi

- Generated: 2026-05-25T04:52:33.091Z
- Updated: 2026-05-25T05:00:27.990Z
- Runtime: Pi · Codex · gpt-5.5
- Format: Technical
- Pages: 6

## Page Index

- 01. [Technical Orientation](https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/01-technical-orientation.md) - What the repo is, how the four packages fit together, the main entry points, and how to navigate the rest of the developer reference.
- 02. [Agent Core Runtime](https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/02-agent-core-runtime.md) - The reusable agent loop, event stream, tool-call execution model, harness layer, session storage, and compaction responsibilities.
- 03. [Model and Provider Abstraction](https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/03-model-and-provider-abstraction.md) - The provider-neutral LLM surface: model metadata, stream contracts, lazy provider registration, API-key resolution, OAuth hooks, and custom provider wiring.
- 04. [CLI, Session, and Tool Lifecycle](https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/04-cli-session-and-tool-lifecycle.md) - How the pi executable parses inputs, creates cwd-bound services, resolves models and settings, persists sessions, dispatches built-in tools, and bridges into the core agent.
- 05. [Interactive, Print, RPC, and TUI Surfaces](https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/05-interactive-print-rpc-and-tui-surfaces.md) - The runtime adapters above AgentSession: terminal interaction, print and JSON modes, JSONL RPC protocol, keybindings, editor components, overlays, and differential rendering.
- 06. [Resources, Extensions, Skills, and Packages](https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/06-resources-extensions-skills-and-packages.md) - The portable extension layer for loading repository, file, npm, git, and catalog-like resources without binding the architecture to one model provider or hosted service.

## Source File Index

- `package.json`
- `packages/agent/package.json`
- `packages/agent/src/agent-loop.ts`
- `packages/agent/src/agent.ts`
- `packages/agent/src/harness/agent-harness.ts`
- `packages/agent/src/harness/compaction/compaction.ts`
- `packages/agent/src/harness/messages.ts`
- `packages/agent/src/harness/session/jsonl-repo.ts`
- `packages/agent/src/harness/session/session.ts`
- `packages/agent/src/harness/types.ts`
- `packages/agent/src/index.ts`
- `packages/ai/package.json`
- `packages/ai/src/api-registry.ts`
- `packages/ai/src/env-api-keys.ts`
- `packages/ai/src/models.ts`
- `packages/ai/src/oauth.ts`
- `packages/ai/src/providers/register-builtins.ts`
- `packages/ai/src/stream.ts`
- `packages/ai/src/types.ts`
- `packages/coding-agent/package.json`
- `packages/coding-agent/src/cli/args.ts`
- `packages/coding-agent/src/core/agent-session-services.ts`
- `packages/coding-agent/src/core/agent-session.ts`
- `packages/coding-agent/src/core/extensions/loader.ts`
- `packages/coding-agent/src/core/extensions/runner.ts`
- `packages/coding-agent/src/core/extensions/types.ts`
- `packages/coding-agent/src/core/keybindings.ts`
- `packages/coding-agent/src/core/model-registry.ts`
- `packages/coding-agent/src/core/package-manager.ts`
- `packages/coding-agent/src/core/prompt-templates.ts`
- `packages/coding-agent/src/core/resource-loader.ts`
- `packages/coding-agent/src/core/sdk.ts`
- `packages/coding-agent/src/core/session-manager.ts`
- `packages/coding-agent/src/core/settings-manager.ts`
- `packages/coding-agent/src/core/skills.ts`
- `packages/coding-agent/src/core/tools/bash.ts`
- `packages/coding-agent/src/core/tools/index.ts`
- `packages/coding-agent/src/main.ts`
- `packages/coding-agent/src/modes/interactive/interactive-mode.ts`
- `packages/coding-agent/src/modes/print-mode.ts`
- `packages/coding-agent/src/modes/rpc/rpc-mode.ts`
- `packages/coding-agent/src/modes/rpc/rpc-types.ts`
- `packages/tui/package.json`
- `packages/tui/src/components/editor.ts`
- `packages/tui/src/keybindings.ts`
- `packages/tui/src/tui.ts`
- `README.md`

---

## 01. Technical Orientation

> What the repo is, how the four packages fit together, the main entry points, and how to navigate the rest of the developer reference.

- Page Markdown: https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/01-technical-orientation.md
- Generated: 2026-05-25T04:49:10.133Z

### Source Files

- `README.md`
- `package.json`
- `packages/ai/package.json`
- `packages/agent/package.json`
- `packages/coding-agent/package.json`
- `packages/tui/package.json`
- `packages/coding-agent/src/main.ts`
- `packages/agent/src/index.ts`

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [README.md](README.md)
- [package.json](package.json)
- [packages/ai/package.json](packages/ai/package.json)
- [packages/agent/package.json](packages/agent/package.json)
- [packages/coding-agent/package.json](packages/coding-agent/package.json)
- [packages/tui/package.json](packages/tui/package.json)
- [packages/coding-agent/src/main.ts](packages/coding-agent/src/main.ts)
- [packages/coding-agent/src/index.ts](packages/coding-agent/src/index.ts)
- [packages/coding-agent/src/core/sdk.ts](packages/coding-agent/src/core/sdk.ts)
- [packages/coding-agent/src/core/agent-session.ts](packages/coding-agent/src/core/agent-session.ts)
- [packages/agent/src/index.ts](packages/agent/src/index.ts)
- [packages/agent/src/agent.ts](packages/agent/src/agent.ts)
- [packages/agent/src/agent-loop.ts](packages/agent/src/agent-loop.ts)
- [packages/ai/src/index.ts](packages/ai/src/index.ts)
- [packages/ai/src/models.ts](packages/ai/src/models.ts)
- [packages/ai/src/api-registry.ts](packages/ai/src/api-registry.ts)
- [packages/tui/src/index.ts](packages/tui/src/index.ts)
- [packages/tui/src/tui.ts](packages/tui/src/tui.ts)
- [packages/coding-agent/README.md](packages/coding-agent/README.md)
- [packages/agent/README.md](packages/agent/README.md)
- [packages/ai/README.md](packages/ai/README.md)
- [packages/tui/README.md](packages/tui/README.md)
</details>

# Technical Orientation

This page explains what `earendil-works/pi` contains, how its four packages fit together, where runtime control enters the system, and which files to open next when working on CLI behavior, agent runtime behavior, provider support, or terminal UI.

The repository is a TypeScript monorepo for the Pi agent harness. Its top-level README names the three core agent packages and the package table adds the terminal UI package; the root workspace configuration includes `packages/*` plus coding-agent examples as workspaces.  
Sources: [README.md:19-25](), [README.md:52-55](), [package.json:5-15]()

## Package map

| Package | Primary responsibility | Main evidence |
|---|---|---|
| `@earendil-works/pi-coding-agent` | User-facing `pi` CLI, SDK exports, sessions, tools, resources, modes, extensions. | Package description and `bin.pi`; dependency on all three library packages. |
| `@earendil-works/pi-agent-core` | Stateful agent runtime, event streaming, tool execution loop, message conversion boundary. | Package description and dependency on `pi-ai`; `Agent` and `agent-loop` exports. |
| `@earendil-works/pi-ai` | Provider/model abstraction, model registry, streaming APIs, TypeBox helper exports, provider registration. | Package description, provider exports, model registry, API registry. |
| `@earendil-works/pi-tui` | Terminal UI primitives used by interactive mode: `TUI`, terminal abstraction, components, keybindings, rendering. | Package description and exported components/classes. |

Sources: [packages/coding-agent/package.json:2-14](), [packages/coding-agent/package.json:41-44](), [packages/agent/package.json:2-8](), [packages/agent/package.json:31-33](), [packages/ai/package.json:2-8](), [packages/tui/package.json:2-4]()

## Runtime dependency shape

```mermaid
flowchart TD
  subgraph App["packages/coding-agent"]
    CLI["src/main.ts\nCLI args, modes, services"]
    SDK["src/core/sdk.ts\ncreateAgentSession()"]
    Session["src/core/agent-session.ts\nsessions, tools, extensions, compaction"]
  end

  subgraph Core["packages/agent"]
    Agent["Agent"]
    Loop["agent-loop"]
  end

  subgraph Provider["packages/ai"]
    Models["models.ts"]
    ApiRegistry["api-registry.ts"]
    Stream["stream / streamSimple"]
  end

  subgraph UI["packages/tui"]
    TUI["TUI"]
    Components["Editor, Input, Markdown, SelectList, ..."]
  end

  CLI --> SDK
  SDK --> Session
  SDK --> Agent
  Agent --> Loop
  Loop --> Stream
  Stream --> ApiRegistry
  SDK --> Models
  CLI --> TUI
  TUI --> Components
```

The build script also reflects the dependency order: `tui`, then `ai`, then `agent`, then `coding-agent`.  
Sources: [package.json:13-15](), [packages/coding-agent/src/main.ts:8-44](), [packages/coding-agent/src/core/sdk.ts:1-32](), [packages/agent/src/index.ts:2-44](), [packages/ai/src/index.ts:1-47](), [packages/tui/src/index.ts:12-106]()

## Main entry points

### CLI entry: `packages/coding-agent/src/main.ts`

`main(args, options)` is the CLI orchestration point. It handles offline flags, package/config subcommands, argument parsing, stdout handling for non-interactive modes, export, migrations, session selection, runtime service creation, model listing, piped stdin, initial message preparation, theme initialization, diagnostics, and final dispatch to RPC, interactive, or print/JSON mode.

```ts
if (appMode === "rpc") {
  await runRpcMode(runtime);
} else if (appMode === "interactive") {
  const interactiveMode = new InteractiveMode(runtime, { /* ... */ });
  await interactiveMode.run();
} else {
  const exitCode = await runPrintMode(runtime, { /* ... */ });
}
```

Sources: [packages/coding-agent/src/main.ts:424-455](), [packages/coding-agent/src/main.ts:533-614](), [packages/coding-agent/src/main.ts:630-709]()

### SDK surface: `packages/coding-agent/src/index.ts` and `core/sdk.ts`

The coding-agent package re-exports the programmatic API: `AgentSession`, auth storage, model registry, resource loaders, settings, skills, extensions, session manager, compaction helpers, and tool factories. The `createAgentSession()` SDK function creates or accepts `AuthStorage`, `ModelRegistry`, `SettingsManager`, `SessionManager`, and `ResourceLoader`, picks/restores the model, configures default tools, constructs an `Agent`, then wraps it in `AgentSession`.

Default built-in tools are `read`, `bash`, `edit`, and `write`; callers can suppress or allowlist tools via `noTools` and `tools`.

Sources: [packages/coding-agent/src/index.ts:1-260](), [packages/coding-agent/src/core/sdk.ts:34-78](), [packages/coding-agent/src/core/sdk.ts:202-218](), [packages/coding-agent/src/core/sdk.ts:280-417]()

### Session layer: `packages/coding-agent/src/core/agent-session.ts`

`AgentSession` is the shared abstraction above all CLI run modes. Its header states that it is used by interactive, print, and RPC modes and encapsulates agent state access, event subscription with session persistence, model/thinking management, compaction, bash execution, and session switching/branching. It subscribes to core agent events for persistence, extensions, auto-compaction, and retry logic.

Sources: [packages/coding-agent/src/core/agent-session.ts:1-12](), [packages/coding-agent/src/core/agent-session.ts:123-150](), [packages/coding-agent/src/core/agent-session.ts:156-194](), [packages/coding-agent/src/core/agent-session.ts:252-336](), [packages/coding-agent/src/core/agent-session.ts:962-1043](), [packages/coding-agent/src/core/agent-session.ts:1611-1723]()

## Agent core boundary

`@earendil-works/pi-agent-core` exports the `Agent`, low-level loop functions, harness utilities, compaction/session helpers, proxy utilities, and shared types. `Agent` owns transcript state, lifecycle event subscribers, steering and follow-up queues, the provider stream function, API-key lookup hooks, pre/post tool hooks, and transport/thinking/tool-execution settings.

The lower-level loop transforms application-facing `AgentMessage[]` to provider-facing `Message[]` only at the LLM call boundary, then streams assistant events, executes tool calls, appends tool results, checks stop conditions, and drains steering/follow-up queues. Tool execution can be sequential or parallel depending on config and per-tool settings.

Sources: [packages/agent/src/index.ts:2-44](), [packages/agent/src/agent.ts:155-268](), [packages/agent/src/agent-loop.ts:155-268](), [packages/agent/src/agent-loop.ts:273-387](), [packages/agent/README.md:36-54](), [packages/agent/README.md:102-157]()

## Provider and model layer

`@earendil-works/pi-ai` is provider-neutral: the code exposes registries and typed provider/model APIs rather than assuming a single hosted service. `models.ts` initializes an in-memory registry from generated model metadata and exposes `getModel`, `getProviders`, `getModels`, cost calculation, thinking-level support, and model equality helpers. `api-registry.ts` stores stream implementations by API name and allows providers to be registered and unregistered, including source-scoped unregistration.

The package README lists many supported providers and explicitly includes OpenAI-compatible APIs such as Ollama, vLLM, and LM Studio, which keeps the architecture BYOK/BYOC-friendly.

Sources: [packages/ai/src/index.ts:1-47](), [packages/ai/src/models.ts:1-39](), [packages/ai/src/models.ts:50-90](), [packages/ai/src/api-registry.ts:23-98](), [packages/ai/README.md:51-77](), [packages/ai/README.md:85-112]()

## Terminal UI layer

`@earendil-works/pi-tui` is a small terminal UI framework. Its exports include components (`Editor`, `Input`, `Markdown`, `SelectList`, `SettingsList`, `Text`, `Image`, `Box`), terminal abstractions, key parsing/keybindings, autocomplete, image rendering, and the `TUI` container. The core `Component` interface renders to lines for a viewport width, can handle focused input, and can invalidate cached rendering. `TUI` extends `Container`, manages focus, overlays, terminal lifecycle, and render requests.

Sources: [packages/tui/src/index.ts:12-106](), [packages/tui/src/tui.ts:39-77](), [packages/tui/src/tui.ts:200-239](), [packages/tui/src/tui.ts:329-408](), [packages/tui/src/tui.ts:443-449](), [packages/tui/src/tui.ts:495-515](), [packages/tui/README.md:3-13]()

## How to navigate the developer reference

| Task | Start here | Why |
|---|---|---|
| Understand user-facing CLI behavior | `packages/coding-agent/README.md`, then `packages/coding-agent/src/main.ts` | README lists modes, providers, sessions, settings, customization, SDK, and CLI reference; `main.ts` wires them together. |
| Change session, compaction, model switching, or prompt behavior | `packages/coding-agent/src/core/agent-session.ts` | This is the mode-shared session lifecycle layer. |
| Embed Pi in another app | `packages/coding-agent/src/core/sdk.ts` and exported symbols in `packages/coding-agent/src/index.ts` | `createAgentSession()` is the programmatic construction path. |
| Change agent event/tool loop behavior | `packages/agent/src/agent.ts` and `packages/agent/src/agent-loop.ts` | These own stateful event emission and tool-call loop semantics. |
| Add or modify providers/models | `packages/ai/src/api-registry.ts`, `packages/ai/src/models.ts`, provider exports in `packages/ai/src/index.ts` | Provider implementations are registered behind API/model registries. |
| Work on interactive terminal UI | `packages/tui/src/tui.ts`, `packages/tui/src/index.ts`, and coding-agent interactive mode files | `pi-tui` owns reusable UI primitives; coding-agent composes them for Pi. |

Sources: [packages/coding-agent/README.md:20-24](), [packages/coding-agent/README.md:43-64](), [packages/coding-agent/README.md:96-104](), [packages/coding-agent/README.md:140-173](), [packages/coding-agent/README.md:230-236](), [packages/coding-agent/README.md:315-434](), [packages/coding-agent/README.md:490-492]()

## Development commands and checks

The root development workflow is workspace-oriented. The README documents `npm install --ignore-scripts`, `npm run build`, `npm run check`, `./test.sh`, and `./pi-test.sh`. The root scripts define `build`, `check`, workspace `test`, release/version commands, pinned-dependency checks, shrinkwrap checks, TypeScript checking, and browser smoke checks.

Sources: [README.md:63-69](), [package.json:13-22](), [package.json:49-50]()

## Closing summary

At a high level, `packages/coding-agent` is the application and SDK layer, `packages/agent` is the stateful event/tool loop, `packages/ai` is the provider-neutral model/streaming abstraction, and `packages/tui` is the interactive terminal UI toolkit. Start from `packages/coding-agent/src/main.ts` for runtime behavior, then move downward into `core/sdk.ts`, `AgentSession`, `Agent`, and `pi-ai` or `pi-tui` depending on whether the change is about sessions/tools/providers or terminal interaction.  
Sources: [packages/coding-agent/src/main.ts:424-709](), [packages/coding-agent/src/core/sdk.ts:202-417](), [packages/agent/src/agent-loop.ts:155-387]()

Generation note: no `STRATEGY.md` or `docs/solutions/**` files were found in this checkout; this page uses repository code and README/package metadata as source of truth, with page-shape guidance from the bundled Compound Engineering profile described in the request.

---

## 02. Agent Core Runtime

> The reusable agent loop, event stream, tool-call execution model, harness layer, session storage, and compaction responsibilities.

- Page Markdown: https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/02-agent-core-runtime.md
- Generated: 2026-05-25T04:49:49.106Z

### Source Files

- `packages/agent/src/agent.ts`
- `packages/agent/src/agent-loop.ts`
- `packages/agent/src/harness/agent-harness.ts`
- `packages/agent/src/harness/types.ts`
- `packages/agent/src/harness/messages.ts`
- `packages/agent/src/harness/session/session.ts`
- `packages/agent/src/harness/session/jsonl-repo.ts`
- `packages/agent/src/harness/compaction/compaction.ts`

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [packages/agent/src/agent.ts](packages/agent/src/agent.ts)
- [packages/agent/src/agent-loop.ts](packages/agent/src/agent-loop.ts)
- [packages/agent/src/types.ts](packages/agent/src/types.ts)
- [packages/agent/src/harness/agent-harness.ts](packages/agent/src/harness/agent-harness.ts)
- [packages/agent/src/harness/types.ts](packages/agent/src/harness/types.ts)
- [packages/agent/src/harness/messages.ts](packages/agent/src/harness/messages.ts)
- [packages/agent/src/harness/session/session.ts](packages/agent/src/harness/session/session.ts)
- [packages/agent/src/harness/session/jsonl-repo.ts](packages/agent/src/harness/session/jsonl-repo.ts)
- [packages/agent/src/harness/session/jsonl-storage.ts](packages/agent/src/harness/session/jsonl-storage.ts)
- [packages/agent/src/harness/compaction/compaction.ts](packages/agent/src/harness/compaction/compaction.ts)
</details>

# Agent Core Runtime

The agent core runtime is the reusable execution layer behind Pi’s agent behavior. It separates the low-level loop that streams model responses and executes tools from higher-level harness concerns such as sessions, resources, provider request hooks, queueing, and compaction.

This page is source-anchored to repository code. No `STRATEGY.md`, `docs/solutions/**`, or prior generated wiki pages were present in this checkout; the requested Compound Engineering guidance was used only as portable page-shaping guidance from the bundled profile metadata, not as a runtime dependency.

## Runtime boundaries

```mermaid
flowchart LR
  subgraph API["Public runtime APIs"]
    Agent["Agent\npackages/agent/src/agent.ts"]
    Harness["AgentHarness\nharness/agent-harness.ts"]
  end

  subgraph Loop["Core loop"]
    RunLoop["runAgentLoop / runLoop"]
    Stream["streamAssistantResponse"]
    Tools["executeToolCalls"]
  end

  subgraph Persistence["Harness persistence"]
    Session["Session"]
    JsonlRepo["JsonlSessionRepo"]
    JsonlStorage["JsonlSessionStorage"]
    Compact["prepareCompaction / compact"]
  end

  Provider["pi-ai streamSimple / completeSimple"]
  ToolImpl["AgentTool.execute"]

  Agent --> RunLoop
  Harness --> RunLoop
  Harness --> Session
  Session --> JsonlStorage
  JsonlRepo --> JsonlStorage
  Harness --> Compact
  RunLoop --> Stream
  Stream --> Provider
  RunLoop --> Tools
  Tools --> ToolImpl
```

`Agent` is the transient stateful wrapper: it owns current transcript arrays, runtime state, event subscribers, steering and follow-up queues, and calls the low-level loop. `AgentHarness` is the application-facing orchestration layer: it builds turn snapshots from persisted sessions, resolves resources and system prompts, wraps provider request hooks, persists messages, and exposes compaction/tree operations. Sources: [packages/agent/src/agent.ts:166-218](), [packages/agent/src/harness/agent-harness.ts:164-213](), [packages/agent/src/harness/agent-harness.ts:313-451]()

## Low-level loop model

The core loop accepts `AgentMessage[]` internally and converts to provider-compatible `Message[]` only at the LLM call boundary. `runAgentLoop()` starts with new prompt messages and emits start/message events for them. `runAgentLoopContinue()` resumes from an existing context and rejects empty contexts or assistant-terminal contexts. Sources: [packages/agent/src/agent-loop.ts:95-141](), [packages/agent/src/agent-loop.ts:275-307]()

The main `runLoop()` has two nested responsibilities:

| Responsibility | Behavior |
|---|---|
| Turn lifecycle | Emit `turn_start`, stream one assistant response, execute requested tools, emit `turn_end`. |
| Continuation decisions | Continue when tool results require another model call, steering messages are available, or follow-up messages arrive after the agent would otherwise stop. |
| Stop points | Stop on assistant `error`/`aborted`, `shouldStopAfterTurn`, or when no tool calls/queues remain. |
| Snapshot refresh | `prepareNextTurn` may replace context/model/thinking level before the next provider request. |

Sources: [packages/agent/src/agent-loop.ts:155-263](), [packages/agent/src/types.ts:198-243]()

## Streaming and event stream

`streamAssistantResponse()` optionally transforms `AgentMessage[]`, converts them to provider messages, builds the `Context`, resolves a fresh API key, then calls the configured stream function. Provider stream events update a partial assistant message and emit `message_start`, `message_update`, and `message_end`. Sources: [packages/agent/src/agent-loop.ts:275-360](), [packages/agent/src/types.ts:135-196]()

The public event contract includes agent, turn, message, and tool execution events. `Agent` reduces these events into state: streaming message, persisted transcript messages, pending tool call IDs, and the last error message. Listeners are awaited in order; run settlement happens after event listeners finish. Sources: [packages/agent/src/types.ts:397-419](), [packages/agent/src/agent.ts:231-234](), [packages/agent/src/agent.ts:509-550]()

## Tool-call execution model

Tools are selected from `AgentContext.tools`. A batch runs sequentially when the loop config is `"sequential"` or any called tool has `executionMode: "sequential"`; otherwise the loop prepares calls sequentially and executes allowed calls concurrently. Tool result messages are emitted in assistant source order after finalized calls. Sources: [packages/agent/src/agent-loop.ts:373-512](), [packages/agent/src/types.ts:245-276]()

Tool execution has clear stages:

1. Emit `tool_execution_start`.
2. Find the tool, optionally prepare arguments, validate arguments.
3. Run `beforeToolCall`; blocked or invalid calls become immediate error results.
4. Execute `AgentTool.execute`, forwarding abort signal and partial update callback.
5. Run `afterToolCall`, allowing field-level result overrides.
6. Emit `tool_execution_end`, then `message_start`/`message_end` for the tool result.

Sources: [packages/agent/src/agent-loop.ts:543-743](), [packages/agent/src/types.ts:345-383]()

```ts
// packages/agent/src/types.ts
export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool<TParameters> {
  label: string;
  prepareArguments?: (args: unknown) => Static<TParameters>;
  execute: (
    toolCallId: string,
    params: Static<TParameters>,
    signal?: AbortSignal,
    onUpdate?: AgentToolUpdateCallback<TDetails>,
  ) => Promise<AgentToolResult<TDetails>>;
  executionMode?: ToolExecutionMode;
}
```

## Agent wrapper responsibilities

`Agent` provides a minimal reusable runtime API:

| API | Responsibility |
|---|---|
| `prompt()` | Start a new run from text, a message, or message batch. |
| `continue()` | Resume from current transcript if the last message is user/tool-result. |
| `steer()` | Queue a message to inject after the current assistant turn. |
| `followUp()` | Queue a message after the agent would otherwise stop. |
| `abort()` | Abort the active run’s `AbortController`. |
| `waitForIdle()` | Wait for the current run and awaited listeners. |

The wrapper creates loop configs from current state, including model, reasoning level, session ID, transport, dynamic API key resolver, tool hooks, and queue drain callbacks. Sources: [packages/agent/src/agent.ts:325-360](), [packages/agent/src/agent.ts:422-455](), [packages/agent/src/agent.ts:476-502]()

## Harness layer

`AgentHarness` turns the reusable loop into a durable application runtime. Each prompt/skill/template run starts by creating a turn state from the session context, current resources, session metadata, active tools, system prompt, model, thinking level, and stream options. It then runs `runAgentLoop()` with harness-specific hooks and a harness-created stream function. Sources: [packages/agent/src/harness/agent-harness.ts:313-451](), [packages/agent/src/harness/agent-harness.ts:526-616]()

Harness events and hooks cover queue updates, save points, provider request/payload/response hooks, tool-call hooks, compaction hooks, tree navigation hooks, model/thinking/resource updates, abort, and settled notifications. Sources: [packages/agent/src/harness/types.ts:493-657](), [packages/agent/src/harness/agent-harness.ts:969-1002]()

Provider neutrality is preserved by passing `Model`, stream options, headers, API keys, and `transport` through generic hooks and `streamSimple`; the harness does not assume a hosted service or specific provider. Sources: [packages/agent/src/harness/types.ts:81-105](), [packages/agent/src/harness/agent-harness.ts:358-388]()

## Session storage and context reconstruction

Sessions are append-only trees. `SessionTreeEntry` covers messages, model/thinking changes, compactions, branch summaries, custom entries/messages, labels, session info, and leaf pointers. The storage interface exposes metadata, leaf management, entry append/read, labels, path-to-root, and full entry listing. Sources: [packages/agent/src/harness/types.ts:366-485]()

`buildSessionContext()` reduces the active branch into model/thinking state and visible messages. If a compaction entry exists, it inserts a compaction-summary message and keeps messages from `firstKeptEntryId` onward. Sources: [packages/agent/src/harness/session/session.ts:21-75](), [packages/agent/src/harness/messages.ts:120-163]()

`JsonlSessionRepo` creates, opens, lists, deletes, and forks JSONL sessions under a sessions root grouped by encoded cwd. `JsonlSessionStorage` writes a header, appends entries as JSON lines, reconstructs the current leaf by replaying entries, and resolves the branch path by walking parent IDs. Sources: [packages/agent/src/harness/session/jsonl-repo.ts:75-159](), [packages/agent/src/harness/session/jsonl-storage.ts:136-158](), [packages/agent/src/harness/session/jsonl-storage.ts:191-258](), [packages/agent/src/harness/session/jsonl-storage.ts:275-288]()

## Compaction responsibilities

Compaction is a harness-owned structural operation that requires the harness to be idle. The harness prepares compaction from the current session branch, allows a `session_before_compact` hook to cancel or provide a result, runs summarization when needed, appends a compaction entry, and emits `session_compact`. Sources: [packages/agent/src/harness/agent-harness.ts:681-733]()

The compaction helpers estimate context tokens from provider usage when available, fall back to token heuristics, select a cut point that keeps recent context, preserve previous summaries, detect split turns, track file operations, and generate or update structured summaries with `completeSimple`. Sources: [packages/agent/src/harness/compaction/compaction.ts:112-198](), [packages/agent/src/harness/compaction/compaction.ts:455-537](), [packages/agent/src/harness/compaction/compaction.ts:541-704]()

## Branch navigation and follow-up queues

Tree navigation is separate from compaction. `navigateTree()` requires idle state, optionally summarizes the branch being left, moves the session leaf, and emits `session_tree`. During turns, `steer()` and `followUp()` are accepted only while non-idle, while `nextTurn()` queues messages for the next user-initiated turn. Abort clears steer/follow-up queues, aborts the active run, waits for idle, and emits `abort`; it does not clear `nextTurn`. Sources: [packages/agent/src/harness/agent-harness.ts:652-667](), [packages/agent/src/harness/agent-harness.ts:737-833](), [packages/agent/src/harness/agent-harness.ts:936-963]()

## Summary

The core runtime is intentionally layered: `agent-loop.ts` is the provider/tool/event engine, `agent.ts` is a lightweight stateful wrapper, and `harness/*` adds durable sessions, hooks, resources, provider request customization, tree navigation, and compaction. This keeps the loop reusable while letting host applications bring their own model registry, auth, tools, storage environment, and skill/resource sources. Sources: [packages/agent/src/agent-loop.ts:155-263](), [packages/agent/src/agent.ts:166-218](), [packages/agent/src/harness/agent-harness.ts:313-451]()

---

## 03. Model and Provider Abstraction

> The provider-neutral LLM surface: model metadata, stream contracts, lazy provider registration, API-key resolution, OAuth hooks, and custom provider wiring.

- Page Markdown: https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/03-model-and-provider-abstraction.md
- Generated: 2026-05-25T04:49:41.135Z

### Source Files

- `packages/ai/src/types.ts`
- `packages/ai/src/api-registry.ts`
- `packages/ai/src/stream.ts`
- `packages/ai/src/models.ts`
- `packages/ai/src/providers/register-builtins.ts`
- `packages/ai/src/env-api-keys.ts`
- `packages/ai/src/oauth.ts`
- `packages/coding-agent/src/core/model-registry.ts`

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [packages/ai/src/types.ts](packages/ai/src/types.ts)
- [packages/ai/src/api-registry.ts](packages/ai/src/api-registry.ts)
- [packages/ai/src/stream.ts](packages/ai/src/stream.ts)
- [packages/ai/src/models.ts](packages/ai/src/models.ts)
- [packages/ai/src/models.generated.ts](packages/ai/src/models.generated.ts)
- [packages/ai/src/providers/register-builtins.ts](packages/ai/src/providers/register-builtins.ts)
- [packages/ai/src/env-api-keys.ts](packages/ai/src/env-api-keys.ts)
- [packages/ai/src/oauth.ts](packages/ai/src/oauth.ts)
- [packages/ai/src/utils/oauth/types.ts](packages/ai/src/utils/oauth/types.ts)
- [packages/ai/src/utils/oauth/index.ts](packages/ai/src/utils/oauth/index.ts)
- [packages/ai/src/utils/event-stream.ts](packages/ai/src/utils/event-stream.ts)
- [packages/coding-agent/src/core/model-registry.ts](packages/coding-agent/src/core/model-registry.ts)
- [packages/coding-agent/src/core/auth-storage.ts](packages/coding-agent/src/core/auth-storage.ts)
- [packages/coding-agent/src/core/extensions/types.ts](packages/coding-agent/src/core/extensions/types.ts)
- [packages/coding-agent/src/core/resolve-config-value.ts](packages/coding-agent/src/core/resolve-config-value.ts)
</details>

# Model and Provider Abstraction

This page explains pi’s provider-neutral LLM surface: how model metadata is represented, how streaming calls are routed by API shape rather than vendor name, how built-in providers are lazily registered, and how the coding agent layers custom providers, API keys, headers, and OAuth on top.

Generation note: no local `STRATEGY.md`, `docs/solutions/**`, or prior generated wiki files were present in this checkout. Page shape guidance came from the bundled Compound Engineering wiki guidance, while implementation claims below are sourced from repository code.

## Architecture at a Glance

pi separates **model metadata** from **stream implementation**. A `Model` carries the requested provider, model id, API type, base URL, pricing, capabilities, and compatibility hints. Runtime calls use the model’s `api` to look up an API provider, so several commercial, local, proxy, or extension-backed providers can share one protocol adapter.

```mermaid
flowchart TD
  subgraph Metadata["Model metadata"]
    Generated["packages/ai/src/models.generated.ts\nMODELS"]
    Models["packages/ai/src/models.ts\ngetModels(), getModel()"]
    AgentRegistry["packages/coding-agent/src/core/model-registry.ts\nbuilt-ins + models.json + extensions"]
  end

  subgraph Runtime["Provider-neutral runtime"]
    Stream["packages/ai/src/stream.ts\nstream(), complete(), streamSimple()"]
    ApiRegistry["packages/ai/src/api-registry.ts\napi -> stream handlers"]
    Builtins["packages/ai/src/providers/register-builtins.ts\nlazy built-in adapters"]
  end

  subgraph Auth["Request auth"]
    AuthStorage["auth-storage.ts\nruntime/stored/OAuth/env/fallback"]
    EnvKeys["env-api-keys.ts\nknown env and ambient auth"]
    OAuth["utils/oauth/*\nlogin, refresh, getApiKey, modifyModels"]
  end

  Generated --> Models --> AgentRegistry
  AgentRegistry --> Stream
  Stream --> ApiRegistry --> Builtins
  AgentRegistry --> AuthStorage
  AuthStorage --> EnvKeys
  AuthStorage --> OAuth
```

Sources: [packages/ai/src/models.generated.ts:1-24](), [packages/ai/src/models.ts:1-37](), [packages/ai/src/stream.ts:1-58](), [packages/ai/src/api-registry.ts:23-98](), [packages/ai/src/providers/register-builtins.ts:162-406](), [packages/coding-agent/src/core/model-registry.ts:384-630]()

## Core Types and Model Metadata

`types.ts` defines open-ended API and provider identifiers. Known APIs and providers are enumerated, but `Api` and `Provider` also admit strings, which is what makes extension and custom-provider wiring possible without changing the core type list.

A `Model<TApi>` contains:

| Field group | Purpose |
| --- | --- |
| Identity | `id`, `name`, `provider`, `api` |
| Routing | `baseUrl`, optional `headers`, optional protocol-specific `compat` |
| Capabilities | `reasoning`, `thinkingLevelMap`, `input` |
| Limits | `contextWindow`, `maxTokens` |
| Cost tracking | per-million-token `input`, `output`, `cacheRead`, `cacheWrite` |

Compatibility metadata is intentionally protocol-specific. `OpenAICompletionsCompat`, `OpenAIResponsesCompat`, and `AnthropicMessagesCompat` let custom or proxy providers override behavior such as max-token field names, reasoning formats, cache retention, session-affinity headers, and Anthropic tool/cache behavior without creating a new stream adapter.

Sources: [packages/ai/src/types.ts:6-50](), [packages/ai/src/types.ts:365-432](), [packages/ai/src/types.ts:538-566]()

## Built-in Model Registry

Built-in metadata is generated into `MODELS` and loaded into an in-memory `Map<provider, Map<modelId, Model<Api>>>` at module initialization. `getProviders()`, `getModels(provider)`, and `getModel(provider, modelId)` expose that generated catalog. Utility functions then compute usage cost from model pricing and normalize thinking-level support.

The generated file is data, not behavior: each entry satisfies the `Model` shape and records the API adapter to use, such as `bedrock-converse-stream`, plus provider, base URL, reasoning support, input modalities, cost, context window, and max tokens.

Sources: [packages/ai/src/models.generated.ts:1-24](), [packages/ai/src/models.ts:1-91]()

## Stream Contracts

The central runtime contract is `StreamFunction<TApi, TOptions>`. It must return an `AssistantMessageEventStream`; after invocation, request/model/runtime failures are expected to be represented as stream events rather than thrown. A stream begins with `start`, can emit text, thinking, and tool-call deltas, and ends with either `done` or `error`.

`complete()` and `completeSimple()` are thin convenience wrappers over streaming: they call `stream()` or `streamSimple()` and await the stream’s final `result()`.

Sources: [packages/ai/src/types.ts:84-210](), [packages/ai/src/types.ts:277-359](), [packages/ai/src/utils/event-stream.ts:21-80](), [packages/ai/src/stream.ts:25-58]()

### Full vs. Simple Streaming

| API | Purpose |
| --- | --- |
| `stream(model, context, options)` | Provider-specific options through `ProviderStreamOptions`. |
| `complete(model, context, options)` | Await final `AssistantMessage` from `stream()`. |
| `streamSimple(model, context, options)` | Common options plus `reasoning` and `thinkingBudgets`. |
| `completeSimple(model, context, options)` | Await final `AssistantMessage` from `streamSimple()`. |

Sources: [packages/ai/src/types.ts:190-210](), [packages/ai/src/stream.ts:25-58]()

## API Provider Registry

`api-registry.ts` maps API identifiers to stream implementations. Registration stores both `stream` and `streamSimple`, optionally tagged with a `sourceId` so dynamically registered handlers can be removed later. The wrapper functions enforce that `model.api` matches the registered API before dispatching.

This means provider names and API shapes are separate. A custom provider can reuse `openai-completions`, `openai-responses`, `anthropic-messages`, or another registered API as long as its model metadata points at that API and its base URL/auth settings are valid.

Sources: [packages/ai/src/api-registry.ts:23-98](), [packages/ai/src/types.ts:538-566]()

## Lazy Built-in Provider Registration

Importing `stream.ts` imports `providers/register-builtins.ts`, whose module-level `registerBuiltInApiProviders()` call registers built-in API adapters. The adapters are lazy wrappers: the initial registry entry exists immediately, but the provider module is imported only when a request uses that API. Lazy load failures are converted into an `error` stream event with an `AssistantMessage` containing `stopReason: "error"`.

Built-in registered APIs include Anthropic Messages, OpenAI Completions, Mistral Conversations, OpenAI Responses, Azure OpenAI Responses, OpenAI Codex Responses, Google Generative AI, Google Vertex, and Bedrock Converse Stream. Bedrock uses a node-only import path helper and supports an override module for nonstandard runtime loading.

Sources: [packages/ai/src/stream.ts:1-23](), [packages/ai/src/providers/register-builtins.ts:89-92](), [packages/ai/src/providers/register-builtins.ts:162-204](), [packages/ai/src/providers/register-builtins.ts:206-406]()

## API Key and Header Resolution

There are two layers of auth resolution:

1. `packages/ai` knows common environment variables and ambient provider auth markers.
2. `packages/coding-agent` combines stored auth, OAuth, environment variables, `models.json`, and extension-provided request configuration.

`env-api-keys.ts` maps known providers to environment variables. Anthropic checks `ANTHROPIC_OAUTH_TOKEN` before `ANTHROPIC_API_KEY`; Vertex can report authenticated status through Application Default Credentials plus project/location; Bedrock can report authenticated status through AWS profile, IAM keys, bearer token, ECS, or web identity environment sources.

Sources: [packages/ai/src/env-api-keys.ts:91-221]()

`AuthStorage.getApiKey()` resolves credentials in this order: runtime override, stored API key, stored OAuth token with refresh, environment variable, and optional fallback resolver. `ModelRegistry.getApiKeyAndHeaders()` then combines that result with provider-level `apiKey`, provider headers, model headers, and static model headers. If `authHeader` is enabled, it injects `Authorization: Bearer <apiKey>` and returns an error if no key is available.

Sources: [packages/coding-agent/src/core/auth-storage.ts:454-522](), [packages/coding-agent/src/core/model-registry.ts:685-721](), [packages/coding-agent/src/core/resolve-config-value.ts:10-136]()

## OAuth Hooks

OAuth support is provider-neutral. An `OAuthProviderInterface` supplies:

| Hook | Responsibility |
| --- | --- |
| `login(callbacks)` | Run the provider’s login flow and return credentials to persist. |
| `refreshToken(credentials)` | Refresh expired credentials. |
| `getApiKey(credentials)` | Convert credentials into the API key/token string used for requests. |
| `modifyModels(models, credentials)` | Optionally rewrite model metadata, such as base URLs, after login. |

The OAuth registry starts with built-in Anthropic, GitHub Copilot, and OpenAI Codex providers. Custom OAuth providers can be registered, unregistered, reset to built-ins, and queried. `getOAuthApiKey()` refreshes expired credentials before returning the request token.

Sources: [packages/ai/src/utils/oauth/types.ts:43-72](), [packages/ai/src/utils/oauth/index.ts:29-151](), [packages/ai/src/oauth.ts:1]()

The coding-agent model registry applies OAuth in two places: while loading models, it lets stored OAuth providers modify the combined built-in/custom model list; when a dynamic provider includes `oauth`, it registers the OAuth provider under the dynamic provider name and can apply `modifyModels` after credentials exist.

Sources: [packages/coding-agent/src/core/model-registry.ts:384-410](), [packages/coding-agent/src/core/model-registry.ts:860-930]()

## Custom Provider Wiring

Custom providers can be declared in `models.json` or registered dynamically by extensions. The schema accepts provider-level `name`, `baseUrl`, `apiKey`, `api`, `headers`, `compat`, `authHeader`, `models`, and `modelOverrides`. Model definitions can specify model identity, API/base URL overrides, reasoning support, input modalities, cost, context window, max tokens, headers, and compatibility overrides.

For non-built-in providers with models, validation requires `baseUrl`, `apiKey`, and an API at the provider or model level. Built-in providers can inherit API and base URL defaults from existing models. Parsed custom models default missing `name` to `id`, `input` to `["text"]`, cost to zeros, `contextWindow` to `128000`, and `maxTokens` to `16384`.

Sources: [packages/coding-agent/src/core/model-registry.ts:144-202](), [packages/coding-agent/src/core/model-registry.ts:459-630]()

### Dynamic Extension Registration

Extensions expose `pi.registerProvider(name, config)` and `pi.unregisterProvider(name)`. Registration can add a new provider, override an existing provider’s base URL, include OAuth support, or supply a `streamSimple` handler for a custom API. During initial extension load, provider registrations are queued and then flushed when the runner binds context; afterward, calls take effect immediately.

In `ModelRegistry.registerProvider()`, providers with models replace all current models for that provider. Providers with only `baseUrl` or `headers` update existing models/request config. If `streamSimple` is provided, the registry also registers an API provider under a source id of `provider:<providerName>`, allowing refresh/unregister flows to rebuild dynamic API registrations cleanly.

Sources: [packages/coding-agent/src/core/extensions/types.ts:1248-1382](), [packages/coding-agent/src/core/extensions/runner.ts:294-336](), [packages/coding-agent/src/core/model-registry.ts:789-957]()

## Implementation Responsibilities

| Module | Responsibility |
| --- | --- |
| `packages/ai/src/types.ts` | Provider-neutral model, context, message, options, stream, compatibility, and OAuth-facing types. |
| `packages/ai/src/models.ts` | Load generated model metadata and expose catalog helpers. |
| `packages/ai/src/api-registry.ts` | Register API-shape stream handlers and enforce model/API matching. |
| `packages/ai/src/stream.ts` | Public stream/complete entry points that resolve adapters by `model.api`. |
| `packages/ai/src/providers/register-builtins.ts` | Lazy registration and reset of built-in API adapters. |
| `packages/ai/src/env-api-keys.ts` | Known provider environment-variable and ambient-auth discovery. |
| `packages/ai/src/utils/oauth/*` | OAuth provider registry, credential refresh, and token extraction interfaces. |
| `packages/coding-agent/src/core/model-registry.ts` | Merge built-ins, `models.json`, dynamic providers, OAuth model mutation, request auth, and custom headers. |
| `packages/coding-agent/src/core/auth-storage.ts` | Persisted/runtime/env/OAuth/fallback credential resolution. |

Sources: [packages/ai/src/types.ts:84-210](), [packages/ai/src/models.ts:1-91](), [packages/ai/src/api-registry.ts:23-98](), [packages/ai/src/stream.ts:1-58](), [packages/coding-agent/src/core/model-registry.ts:335-957](), [packages/coding-agent/src/core/auth-storage.ts:454-522]()

## Summary

pi’s abstraction is BYOC/BYOK-friendly because the stable boundary is the API protocol plus model metadata, not a hardcoded hosted provider. Built-ins are registered lazily, custom providers can reuse or add API handlers, auth can come from runtime keys, stored credentials, OAuth, environment variables, command-backed config values, or model config, and OAuth hooks can adjust model metadata after login. Sources: [packages/ai/src/api-registry.ts:23-98](), [packages/coding-agent/src/core/model-registry.ts:789-957]()

---

## 04. CLI, Session, and Tool Lifecycle

> How the pi executable parses inputs, creates cwd-bound services, resolves models and settings, persists sessions, dispatches built-in tools, and bridges into the core agent.

- Page Markdown: https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/04-cli-session-and-tool-lifecycle.md
- Generated: 2026-05-25T04:49:23.846Z

### Source Files

- `packages/coding-agent/src/main.ts`
- `packages/coding-agent/src/cli/args.ts`
- `packages/coding-agent/src/core/sdk.ts`
- `packages/coding-agent/src/core/agent-session-services.ts`
- `packages/coding-agent/src/core/agent-session.ts`
- `packages/coding-agent/src/core/session-manager.ts`
- `packages/coding-agent/src/core/tools/index.ts`
- `packages/coding-agent/src/core/tools/bash.ts`

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [packages/coding-agent/src/cli.ts](packages/coding-agent/src/cli.ts)
- [packages/coding-agent/src/main.ts](packages/coding-agent/src/main.ts)
- [packages/coding-agent/src/cli/args.ts](packages/coding-agent/src/cli/args.ts)
- [packages/coding-agent/src/core/sdk.ts](packages/coding-agent/src/core/sdk.ts)
- [packages/coding-agent/src/core/agent-session-services.ts](packages/coding-agent/src/core/agent-session-services.ts)
- [packages/coding-agent/src/core/agent-session-runtime.ts](packages/coding-agent/src/core/agent-session-runtime.ts)
- [packages/coding-agent/src/core/agent-session.ts](packages/coding-agent/src/core/agent-session.ts)
- [packages/coding-agent/src/core/session-manager.ts](packages/coding-agent/src/core/session-manager.ts)
- [packages/coding-agent/src/core/model-resolver.ts](packages/coding-agent/src/core/model-resolver.ts)
- [packages/coding-agent/src/core/settings-manager.ts](packages/coding-agent/src/core/settings-manager.ts)
- [packages/coding-agent/src/core/resource-loader.ts](packages/coding-agent/src/core/resource-loader.ts)
- [packages/coding-agent/src/core/tools/index.ts](packages/coding-agent/src/core/tools/index.ts)
- [packages/coding-agent/src/core/tools/bash.ts](packages/coding-agent/src/core/tools/bash.ts)
- [packages/coding-agent/src/modes/print-mode.ts](packages/coding-agent/src/modes/print-mode.ts)
- [packages/coding-agent/src/modes/rpc/rpc-mode.ts](packages/coding-agent/src/modes/rpc/rpc-mode.ts)
</details>

# CLI, Session, and Tool Lifecycle

This page explains how the `pi` executable becomes an agent runtime: CLI flags are parsed, a session target determines the effective `cwd`, cwd-bound services are created, models/settings/resources are resolved, built-in and extension tools are registered, and the resulting `AgentSession` is handed to interactive, print, JSON, or RPC modes.

No `STRATEGY.md` or `docs/solutions/**` source was present in this checkout, so implementation claims below are grounded in repository code. The page shape follows the bundled Compound Engineering wiki guidance and remains provider-neutral: models, skills, extensions, and tools are file/repository/catalog resources, not tied to one hosted model provider.

## Executable entrypoint and high-level boundary

The published package exposes `pi` as `dist/cli.js`; the source entrypoint is `packages/coding-agent/src/cli.ts`. That file sets the process title, marks `PI_CODING_AGENT`, configures the HTTP dispatcher once, and calls `main(process.argv.slice(2))`.

Sources: [packages/coding-agent/package.json:1-12](), [packages/coding-agent/src/cli.ts:1-20]()

```mermaid
flowchart LR
  subgraph Executable
    CLI["src/cli.ts\nprocess/env setup"]
    Main["src/main.ts\nparse, select cwd/session, choose mode"]
  end

  subgraph CwdBoundRuntime["cwd-bound runtime"]
    Services["agent-session-services.ts\nsettings, auth, models, resources"]
    Runtime["agent-session-runtime.ts\nsession replacement host"]
    Session["agent-session.ts\nAgent facade + extensions + tools"]
  end

  subgraph PersistenceAndTools["storage and tools"]
    Sessions["session-manager.ts\nJSONL tree sessions"]
    Tools["core/tools/*\nbuiltin tool definitions"]
  end

  subgraph Modes["I/O modes"]
    Interactive["InteractiveMode"]
    Print["runPrintMode"]
    RPC["runRpcMode"]
  end

  CLI --> Main
  Main --> Services
  Services --> Session
  Main --> Runtime
  Runtime --> Session
  Session --> Sessions
  Session --> Tools
  Runtime --> Interactive
  Runtime --> Print
  Runtime --> RPC
```

## CLI parsing and application mode selection

`parseArgs()` recognizes model/provider flags, session controls, tool controls, resource-loading flags, output modes, `@file` arguments, and unknown long flags. Unknown long flags are preserved in `unknownFlags`; later runtime creation applies them to extension-registered flags, which lets extensions add CLI surface without changing the base parser.

Sources: [packages/coding-agent/src/cli/args.ts:10-178](), [packages/coding-agent/src/core/agent-session-services.ts:68-123]()

| Concern | Flags / input | Runtime effect |
|---|---|---|
| Mode | `--mode text/json/rpc`, `--print`, piped stdin | `main.ts` chooses `interactive`, `print`, `json`, or `rpc`; non-TTY stdin forces print mode unless RPC is selected. |
| Session | `--session`, `--continue`, `--resume`, `--fork`, `--no-session`, `--session-dir` | `main.ts` creates, opens, resumes, forks, or disables persistence through `SessionManager`. |
| Model | `--provider`, `--model`, `--models`, `--thinking`, `--api-key` | Model resolution and thinking level are converted into `CreateAgentSessionOptions`. |
| Tools | `--tools`, `--no-tools`, `--no-builtin-tools` | Creates an allowlist or suppresses default built-ins. |
| Resources | `--extension`, `--skill`, `--prompt-template`, `--theme`, `--no-*` | Passed to `DefaultResourceLoader` as cwd-resolved additional paths and disable switches. |

Sources: [packages/coding-agent/src/main.ts:90-113](), [packages/coding-agent/src/main.ts:321-384](), [packages/coding-agent/src/main.ts:393-665]()

## Session selection determines the effective `cwd`

Startup settings are loaded from the process cwd only to resolve session lookup settings such as `sessionDir`. Then `main.ts` creates the target `SessionManager`. This matters because `--session` and `--resume` may select a session created in another project; project-local settings, context files, extensions, provider registrations, and enabled models are not resolved until after the session’s effective cwd is known.

Sources: [packages/coding-agent/src/main.ts:151-278](), [packages/coding-agent/src/main.ts:471-520]()

Session target behavior:

- `--no-session` uses `SessionManager.inMemory()`.
- `--fork` resolves a path or ID prefix and creates a new session in the current project.
- `--session` opens local/path sessions directly; if an ID resolves globally in another project, the CLI prompts to fork into the current directory.
- `--resume` opens a selector backed by local and global session listing.
- `--continue` opens the most recent session for the selected session directory.
- Otherwise, a new persisted session is created.

Sources: [packages/coding-agent/src/main.ts:202-278](), [packages/coding-agent/src/core/session-manager.ts:1311-1395]()

## Cwd-bound services

`createAgentSessionServices()` is the service factory for one effective cwd. It resolves `cwd` and `agentDir`, creates or reuses auth/settings/model services, constructs a `DefaultResourceLoader`, reloads resources, registers extension-provided model providers, and applies extension CLI flag values as diagnostics instead of printing or exiting.

Sources: [packages/coding-agent/src/core/agent-session-services.ts:18-65](), [packages/coding-agent/src/core/agent-session-services.ts:130-169]()

`createAgentSessionFromServices()` then passes those already-created services into the SDK-level `createAgentSession()`. This split is intentional: the CLI can first resolve cwd-bound model/tool/session options against the correct services, then construct the session.

Sources: [packages/coding-agent/src/core/agent-session-services.ts:174-198]()

`DefaultResourceLoader` owns portable resource discovery. It loads extensions, skills, prompt templates, themes, context files, system prompt sources, and package resources from configured settings plus CLI-provided paths. Context discovery includes global agent-dir context and ancestor project context files unless disabled.

Sources: [packages/coding-agent/src/core/resource-loader.ts:21-54](), [packages/coding-agent/src/core/resource-loader.ts:69-106](), [packages/coding-agent/src/core/resource-loader.ts:108-143](), [packages/coding-agent/src/core/resource-loader.ts:274-390]()

## Model and settings resolution

Settings are layered from global and project files using a recursive merge where project settings override global settings. The `Settings` shape includes default provider/model/thinking level, enabled model patterns, package/resource sources, terminal/image behavior, retry settings, shell path/prefix, and session directory.

Sources: [packages/coding-agent/src/core/settings-manager.ts:68-138](), [packages/coding-agent/src/core/settings-manager.ts:564-649](), [packages/coding-agent/src/core/settings-manager.ts:763-784](), [packages/coding-agent/src/core/settings-manager.ts:1008-1012]()

Model resolution has two related paths:

1. `resolveCliModel()` handles a single CLI model, including `provider/model`, provider inference, exact matching, fuzzy matching, optional `:<thinking>`, and custom model-id fallback for known providers.
2. `resolveModelScope()` expands `--models` or `enabledModels` into a cycling scope, supporting glob patterns and optional thinking suffixes.

Sources: [packages/coding-agent/src/core/model-resolver.ts:260-487]()

Inside `createAgentSession()`, existing sessions can restore their saved model when configured auth is still available. If no model is restored or provided, `findInitialModel()` falls back through scoped models, settings defaults, known provider defaults, then the first available authenticated model. Thinking level is restored from the session when possible, otherwise from settings/defaults, and finally clamped to model capabilities.

Sources: [packages/coding-agent/src/core/sdk.ts:202-287](), [packages/coding-agent/src/core/model-resolver.ts:490-562]()

## Session persistence model

`SessionManager` stores sessions as append-only JSONL trees. The header records session id, timestamp, cwd, version, and optional parent session. Every entry has `id` and `parentId`, so appending creates a child of the current leaf and branch navigation can move through history without rewriting existing entries.

Sources: [packages/coding-agent/src/core/session-manager.ts:1-140](), [packages/coding-agent/src/core/session-manager.ts:711-912]()

The LLM context is not simply “all lines in the file.” `buildSessionContext()` walks from the active leaf to the root, restores model/thinking state from entries, inserts compaction summaries, includes kept messages after compaction, and includes branch/custom messages that participate in context.

Sources: [packages/coding-agent/src/core/session-manager.ts:330-421]()

Persistence is delayed until a session has an assistant message; then accumulated entries are flushed and later entries append one JSON line at a time. This avoids saving incomplete one-sided sessions while preserving an append-only log once an assistant response exists.

Sources: [packages/coding-agent/src/core/session-manager.ts:827-867]()

## Built-in tools and bash lifecycle

The built-in tool registry knows seven tool names: `read`, `bash`, `edit`, `write`, `grep`, `find`, and `ls`. `createAllToolDefinitions()` creates definitions for all of them, while SDK defaults activate only `read`, `bash`, `edit`, and `write` unless CLI/tool options change the active set.

Sources: [packages/coding-agent/src/core/tools/index.ts:54-141](), [packages/coding-agent/src/core/sdk.ts:289-297]()

`AgentSession._buildRuntime()` rebuilds builtin definitions with cwd-bound options. The bash definition receives `shellCommandPrefix` and `shellPath` from settings; read receives image auto-resize settings. Extension tools and SDK custom tools are merged into the registry, then wrapped so extension hooks can observe or modify calls/results.

Sources: [packages/coding-agent/src/core/agent-session.ts:2253-2414]()

The bash tool is explicitly pluggable. `BashOperations.exec()` can be replaced, but the default local implementation uses the configured shell, validates cwd existence, spawns a child process, streams stdout/stderr, supports abort signals, supports per-call timeout, kills the process tree on abort/timeout, and tracks detached child pids. Output is accumulated, truncated by line/byte limits, and saved to a temp file when needed.

Sources: [packages/coding-agent/src/core/tools/bash.ts:22-149](), [packages/coding-agent/src/core/tools/bash.ts:252-426]()

## Bridging into the core agent

`createAgentSession()` constructs the core `Agent` with model, thinking level, tool list, context conversion, provider streaming, provider request/response extension hooks, queueing behavior, transport, retry settings, and session id. API keys and provider headers are resolved through `ModelRegistry` at request time, keeping provider choice outside the CLI lifecycle itself.

Sources: [packages/coding-agent/src/core/sdk.ts:299-374]()

`AgentSession` is the shared lifecycle facade used by all modes. It subscribes to core agent events, forwards them to extensions, emits them to UI/RPC/print subscribers, and persists finalized user/assistant/tool result messages through `SessionManager`. It also owns model/thinking changes, compaction, queues, extension bindings, tool registry state, and system prompt rebuilding.

Sources: [packages/coding-agent/src/core/agent-session.ts:302-340](), [packages/coding-agent/src/core/agent-session.ts:486-520](), [packages/coding-agent/src/core/agent-session.ts:2041-2414]()

Prompt submission goes through `AgentSession.prompt()`: extension slash commands can run immediately, input hooks can handle or transform text/images, skills and prompt templates expand, streaming prompts are queued as steer/follow-up messages, model/auth preflight is checked, pending bash messages are flushed, extension `before_agent_start` hooks can inject custom messages or modify the system prompt, and the core agent is finally prompted.

Sources: [packages/coding-agent/src/core/agent-session.ts:962-1116]()

## Runtime replacement: new, resume, fork, import

`AgentSessionRuntime` owns the active `AgentSession` plus cwd-bound services. Replacement flows emit extension lifecycle hooks, tear down the old session, create a new runtime through the same factory, apply the new session/services, and optionally rebind host UI or command contexts. This keeps `/new`, resume/switch, fork, and import behavior consistent with initial startup.

Sources: [packages/coding-agent/src/core/agent-session-runtime.ts:55-165](), [packages/coding-agent/src/core/agent-session-runtime.ts:166-407]()

## Run modes

After runtime creation and diagnostics, `main.ts` dispatches by mode:

- RPC mode calls `runRpcMode(runtime)`.
- Interactive mode constructs `InteractiveMode` with migrated-provider and startup-message context.
- Print/text and JSON modes call `runPrintMode()` and then restore stdout/exit with the returned code.

Sources: [packages/coding-agent/src/main.ts:650-716]()

`runPrintMode()` binds extension command actions, subscribes to session events for JSON mode, sends the initial message and follow-up messages, prints the final assistant text in text mode, and disposes the runtime in `finally`.

Sources: [packages/coding-agent/src/modes/print-mode.ts:1-142]()

`runRpcMode()` is a headless JSON stdin/stdout protocol. It takes over stdout, emits JSON responses/events, exposes extension UI requests over the protocol, and keeps the same `AgentSessionRuntime` replacement host as other modes.

Sources: [packages/coding-agent/src/modes/rpc/rpc-mode.ts:1-50]()

## Summary

The CLI lifecycle is intentionally staged: parse flags, select or create a session to establish the effective cwd, build cwd-bound services/resources, resolve model/thinking/tool options, construct `AgentSession`, then hand a runtime host to the selected I/O mode. Sessions persist as JSONL trees, tools are definition-first and cwd-bound, and provider/model selection stays behind registries and settings rather than being hardcoded into the executable.

Sources: [packages/coding-agent/src/main.ts:471-716](), [packages/coding-agent/src/core/sdk.ts:202-416](), [packages/coding-agent/src/core/session-manager.ts:711-912]()

---

## 05. Interactive, Print, RPC, and TUI Surfaces

> The runtime adapters above AgentSession: terminal interaction, print and JSON modes, JSONL RPC protocol, keybindings, editor components, overlays, and differential rendering.

- Page Markdown: https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/05-interactive-print-rpc-and-tui-surfaces.md
- Generated: 2026-05-25T04:51:56.446Z

### Source Files

- `packages/coding-agent/src/modes/interactive/interactive-mode.ts`
- `packages/coding-agent/src/modes/print-mode.ts`
- `packages/coding-agent/src/modes/rpc/rpc-mode.ts`
- `packages/coding-agent/src/modes/rpc/rpc-types.ts`
- `packages/coding-agent/src/core/keybindings.ts`
- `packages/tui/src/tui.ts`
- `packages/tui/src/components/editor.ts`
- `packages/tui/src/keybindings.ts`

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [packages/coding-agent/src/main.ts](packages/coding-agent/src/main.ts)
- [packages/coding-agent/src/cli/args.ts](packages/coding-agent/src/cli/args.ts)
- [packages/coding-agent/src/modes/interactive/interactive-mode.ts](packages/coding-agent/src/modes/interactive/interactive-mode.ts)
- [packages/coding-agent/src/modes/print-mode.ts](packages/coding-agent/src/modes/print-mode.ts)
- [packages/coding-agent/src/modes/rpc/rpc-mode.ts](packages/coding-agent/src/modes/rpc/rpc-mode.ts)
- [packages/coding-agent/src/modes/rpc/rpc-types.ts](packages/coding-agent/src/modes/rpc/rpc-types.ts)
- [packages/coding-agent/src/modes/rpc/jsonl.ts](packages/coding-agent/src/modes/rpc/jsonl.ts)
- [packages/coding-agent/src/core/keybindings.ts](packages/coding-agent/src/core/keybindings.ts)
- [packages/tui/src/tui.ts](packages/tui/src/tui.ts)
- [packages/tui/src/components/editor.ts](packages/tui/src/components/editor.ts)
- [packages/tui/src/keybindings.ts](packages/tui/src/keybindings.ts)
- [packages/tui/test/tui-render.test.ts](packages/tui/test/tui-render.test.ts)
- [packages/tui/test/overlay-options.test.ts](packages/tui/test/overlay-options.test.ts)
- [packages/coding-agent/test/rpc-jsonl.test.ts](packages/coding-agent/test/rpc-jsonl.test.ts)
- [packages/coding-agent/test/print-mode.test.ts](packages/coding-agent/test/print-mode.test.ts)
</details>

# Interactive, Print, RPC, and TUI Surfaces

This page explains the runtime adapters that sit above `AgentSession`: interactive terminal mode, single-shot print/JSON mode, JSONL RPC mode, keybinding resolution, TUI components, overlays, editor behavior, and differential rendering. These surfaces decide how user input, extension UI, and session events are transported; `AgentSession` remains the common execution layer.

This page follows the provided bundled Compound Engineering wiki guidance for page shape. No repository `STRATEGY.md`, generated wiki page, or `docs/solutions` source was found during this focused pass, so implementation code is the source of truth.

## Surface Selection and Adapter Boundary

The CLI resolves four app modes: `interactive`, `print`, `json`, and `rpc`. `--mode rpc` selects RPC, `--mode json` selects JSON print mode, `--print` or non-TTY stdin selects print mode, and otherwise the CLI starts interactive mode. The main entrypoint then dispatches to `runRpcMode`, constructs `InteractiveMode`, or calls `runPrintMode` with `"text"`/`"json"` output mode.

Sources: [packages/coding-agent/src/main.ts:97-112](), [packages/coding-agent/src/main.ts:678-714](), [packages/coding-agent/src/cli/args.ts:74-78]()

| Surface | Module | Input | Output | Primary use |
|---|---|---|---|---|
| Interactive | `interactive-mode.ts` | terminal keys, editor text, dialogs | differential TUI rendering | normal terminal chat |
| Print text | `print-mode.ts` | initial prompt and extra messages | final assistant text | scripts and one-shot CLI calls |
| Print JSON | `print-mode.ts` | initial prompt and extra messages | session header and events as JSON lines | event capture without RPC command loop |
| RPC | `rpc-mode.ts` | JSON commands on stdin | JSON responses, events, extension UI requests | embedding in other processes |

```mermaid
flowchart TB
  subgraph CLI["packages/coding-agent/src/main.ts"]
    Resolve["resolveAppMode()"]
  end

  subgraph Surfaces["Runtime adapters above AgentSession"]
    Interactive["InteractiveMode\ninteractive-mode.ts"]
    Print["runPrintMode\nprint-mode.ts"]
    RPC["runRpcMode\nrpc-mode.ts"]
  end

  subgraph UI["Terminal/TUI package"]
    TUI["TUI\npackages/tui/src/tui.ts"]
    Editor["Editor\npackages/tui/src/components/editor.ts"]
    Keybindings["KeybindingsManager\npackages/tui/src/keybindings.ts"]
  end

  Session["AgentSession / AgentSessionRuntime"]

  Resolve --> Interactive
  Resolve --> Print
  Resolve --> RPC
  Interactive --> TUI
  TUI --> Editor
  Editor --> Keybindings
  Interactive --> Session
  Print --> Session
  RPC --> Session
```

## Interactive Mode

`InteractiveMode` owns terminal interaction. Its constructor creates a `TUI` backed by `ProcessTerminal`, builds containers for header, chat, pending messages, status, widgets, editor, and footer, creates the app-level `KeybindingsManager`, and installs those keybindings globally for TUI components.

Sources: [packages/coding-agent/src/modes/interactive/interactive-mode.ts:359-384](), [packages/coding-agent/src/modes/interactive/interactive-mode.ts:568-699]()

During `init()`, interactive mode registers signal handlers, ensures tools used by autocomplete/search exist, builds startup hints from keybindings, adds the main containers to the TUI, focuses the editor, installs key handlers and submit handling, starts the TUI, then rebinds the current session so extensions can use interactive dialogs.

The main `run()` loop processes optional startup messages first, then repeatedly waits for editor input and sends it to `session.prompt()`. Session events are subscribed through `session.subscribe()`, and event handlers update chat components, pending-message displays, footer state, streaming assistant content, tool components, loaders, and terminal progress.

Sources: [packages/coding-agent/src/modes/interactive/interactive-mode.ts:716-784](), [packages/coding-agent/src/modes/interactive/interactive-mode.ts:2645-2729]()

### Interactive Extension UI

Interactive extensions receive a full `ExtensionUIContext`: selectors, confirms, inputs, notifications, terminal input listeners, status, working indicators, widgets, footer/header replacement, title changes, custom UI, editor text operations, autocomplete providers, custom editor components, theme access, and tool expansion state.

Sources: [packages/coding-agent/src/modes/interactive/interactive-mode.ts:1483-1564](), [packages/coding-agent/src/modes/interactive/interactive-mode.ts:1950-2028]()

Interactive submit handling is command-aware. Built-in slash commands such as `/settings`, `/model`, `/export`, `/import`, `/share`, `/fork`, `/tree`, `/login`, `/logout`, `/new`, `/compact`, `/reload`, `/resume`, and `/quit` are intercepted before ordinary prompt submission. Bash commands are recognized with `!` and `!!`. While compaction or streaming is active, text is queued or sent with `streamingBehavior` so steering and follow-up behavior remains explicit.

Sources: [packages/coding-agent/src/modes/interactive/interactive-mode.ts:2464-2643](), [packages/coding-agent/src/modes/interactive/interactive-mode.ts:3390-3425]()

## Print and JSON Modes

`runPrintMode()` is single-shot. It binds extensions with command-context actions, subscribes to session events, sends the initial prompt and extra messages, and exits. In `"json"` mode it writes the session header and every session event as JSON lines. In `"text"` mode it prints only text content from the final assistant message and returns non-zero if the final assistant stop reason is `error` or `aborted`.

Sources: [packages/coding-agent/src/modes/print-mode.ts:1-145](), [packages/coding-agent/test/print-mode.test.ts:93-151]()

Print mode also installs signal handlers for `SIGTERM` and non-Windows `SIGHUP`, kills tracked detached children on those signals, disposes the runtime once, removes signal handlers in `finally`, and flushes guarded raw stdout before returning.

Sources: [packages/coding-agent/src/modes/print-mode.ts:32-63](), [packages/coding-agent/src/modes/print-mode.ts:136-145]()

## RPC JSONL Protocol

RPC mode is headless and starts by taking over stdout. It serializes every response, session event, and extension UI request as one JSON line. The protocol types define commands for prompting, aborting, session management, state, model selection, thinking levels, queue modes, compaction, retry, bash, message retrieval, and command listing.

Sources: [packages/coding-agent/src/modes/rpc/rpc-mode.ts:48-80](), [packages/coding-agent/src/modes/rpc/rpc-types.ts:17-70](), [packages/coding-agent/src/modes/rpc/rpc-types.ts:111-208]()

RPC extension UI is represented as data. Requests such as `select`, `confirm`, `input`, `editor`, `notify`, `setStatus`, `setWidget`, `setTitle`, and `set_editor_text` are emitted on stdout. The client answers with `extension_ui_response` records correlated by `id`.

Sources: [packages/coding-agent/src/modes/rpc/rpc-mode.ts:82-263](), [packages/coding-agent/src/modes/rpc/rpc-types.ts:213-258]()

The command loop parses each JSON line, handles extension UI responses before treating input as an `RpcCommand`, writes a success or error response, and shuts down when stdin ends or an extension requests shutdown. Prompt commands are asynchronous: RPC sends the authoritative prompt response after prompt preflight succeeds, while session events continue streaming separately.

Sources: [packages/coding-agent/src/modes/rpc/rpc-mode.ts:306-410](), [packages/coding-agent/src/modes/rpc/rpc-mode.ts:668-755]()

JSONL framing is strict LF framing rather than Node `readline`; payload strings may contain Unicode separators such as `U+2028` and `U+2029`, and tests cover LF-only splitting, CRLF input, and final lines without trailing LF.

Sources: [packages/coding-agent/src/modes/rpc/jsonl.ts:1-50](), [packages/coding-agent/test/rpc-jsonl.test.ts:1-55]()

## Keybindings

The TUI package defines generic editor, input, and selection keybindings, plus a `KeybindingsManager` that resolves defaults and user overrides. It does not evict defaults when another action reuses a key; it records direct user-binding conflicts separately.

Sources: [packages/tui/src/keybindings.ts:1-230](), [packages/tui/test/keybindings.test.ts:1-34]()

The coding-agent package extends TUI keybindings through declaration merging with app actions such as interrupt, clear, exit, model cycling, model selection, tool expansion, thinking toggle, external editor, follow-up queueing, image paste, session tree/fork/resume, tree filters, and model selector actions. User config is read from `keybindings.json`, legacy names are migrated, and effective bindings can be retrieved from the app manager.

Sources: [packages/coding-agent/src/core/keybindings.ts:1-202](), [packages/coding-agent/src/core/keybindings.ts:270-365]()

## TUI Component Model, Focus, and Overlays

`TUI` is a `Container` of `Component`s. Components render to terminal-width-aware string arrays, may handle input when focused, may opt into key-release events, and can invalidate cached rendering. Focusable components expose a `focused` boolean and can emit `CURSOR_MARKER` so TUI can position the hardware cursor for IME candidate windows.

Sources: [packages/tui/src/tui.ts:35-90](), [packages/tui/src/tui.ts:196-237]()

Overlays are stacked components with sizing, positioning, visibility predicates, optional non-capturing behavior, focus ordering, and handles for hide/show/focus/unfocus. Interactive mode uses both overlay-style custom UI and editor-container replacement for extension selectors, inputs, editors, OAuth dialogs, reload boxes, and other modal flows.

Sources: [packages/tui/src/tui.ts:150-407](), [packages/tui/test/overlay-options.test.ts:1-180](), [packages/coding-agent/src/modes/interactive/interactive-mode.ts:2056-2185]()

## Editor Component

The editor stores lines plus cursor position, supports padding, dynamic border color, vertical scrolling, autocomplete, bracketed paste buffering, paste markers for large pastes, prompt history, Emacs-style kill/yank, character jump mode, sticky visual columns, and undo.

Sources: [packages/tui/src/components/editor.ts:220-335]()

Rendering word-wraps text to the current layout width, limits visible editor height to a fraction of terminal rows, keeps the cursor visible, emits `CURSOR_MARKER` when focused, draws top/bottom borders, and appends an autocomplete list when active.

Sources: [packages/tui/src/components/editor.ts:409-532]()

Input handling is keybinding-driven. It handles bracketed paste first, then undo, autocomplete navigation/application, tab completion, deletion, kill ring, cursor movement, newline insertion, submit, history navigation, paging, character jump triggers, shifted space, and printable input. `getExpandedText()` expands paste markers back to their original content for consumers that need full text.

Sources: [packages/tui/src/components/editor.ts:534-814](), [packages/tui/src/components/editor.ts:911-955]()

## Differential Rendering

The TUI renderer keeps previous rendered lines, previous dimensions, previous viewport position, previous Kitty image IDs, and cursor rows. A render request is coalesced and rate-limited to a minimum interval. Forced render clears previous state and schedules a full redraw.

Sources: [packages/tui/src/tui.ts:239-296](), [packages/tui/src/tui.ts:430-620]()

`doRender()` renders all components, composites overlays, extracts the cursor marker, applies line resets, then chooses between full redraw and differential update. Full redraw is used for first render, width changes, most height changes, optional clear-on-shrink, or changes above the visible viewport. Otherwise it computes first and last changed lines, deletes changed Kitty images when needed, writes only changed lines, clears deleted rows, and updates previous render state. Tests cover Kitty image cleanup and resize behavior.

Sources: [packages/tui/src/tui.ts:920-1279](), [packages/tui/test/tui-render.test.ts:1-200]()

## Implementation Responsibilities

| Area | Owner | Responsibility |
|---|---|---|
| Mode dispatch | `main.ts` | Convert CLI flags and stdin TTY state into runtime surface selection |
| Interactive adapter | `interactive-mode.ts` | Terminal UI, extension dialogs, editor lifecycle, session event rendering |
| Print adapter | `print-mode.ts` | One-shot prompts, final text output, JSON event stream mode |
| RPC adapter | `rpc-mode.ts` / `rpc-types.ts` | JSONL command loop, typed responses, event streaming, extension UI request/response bridge |
| TUI runtime | `tui.ts` | Component tree, focus, overlays, input forwarding, differential renderer |
| Editor | `components/editor.ts` | Multiline editing, autocomplete, paste handling, keybinding-driven text operations |
| Keybindings | `tui/src/keybindings.ts` and `coding-agent/src/core/keybindings.ts` | Shared TUI defaults plus app-specific configurable bindings |

Sources: [packages/coding-agent/src/main.ts:678-714](), [packages/coding-agent/src/modes/rpc/rpc-mode.ts:379-641](), [packages/tui/src/tui.ts:544-595]()

## Summary

The repository uses one common session runtime with multiple transport adapters: interactive mode renders a rich terminal UI, print mode turns a prompt sequence into text or JSON event output, and RPC mode exposes a JSONL command protocol for embedding. The TUI layer provides reusable components, focus, overlays, keybindings, editor behavior, and differential rendering without depending on a specific model provider or hosted service; provider/model choices flow through session state and commands rather than the UI transport itself. Sources: [packages/coding-agent/src/modes/rpc/rpc-types.ts:17-70](), [packages/coding-agent/src/modes/interactive/interactive-mode.ts:568-699](), [packages/tui/src/tui.ts:920-1279]().

---

## 06. Resources, Extensions, Skills, and Packages

> The portable extension layer for loading repository, file, npm, git, and catalog-like resources without binding the architecture to one model provider or hosted service.

- Page Markdown: https://grok-wiki.com/public/wiki/earendil-works-pi-121d322b171c/pages/06-resources-extensions-skills-and-packages.md
- Generated: 2026-05-25T04:52:33.086Z

### Source Files

- `packages/coding-agent/src/core/resource-loader.ts`
- `packages/coding-agent/src/core/package-manager.ts`
- `packages/coding-agent/src/core/extensions/loader.ts`
- `packages/coding-agent/src/core/extensions/types.ts`
- `packages/coding-agent/src/core/extensions/runner.ts`
- `packages/coding-agent/src/core/skills.ts`
- `packages/coding-agent/src/core/prompt-templates.ts`
- `packages/coding-agent/src/core/settings-manager.ts`

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [packages/coding-agent/src/core/resource-loader.ts](packages/coding-agent/src/core/resource-loader.ts)
- [packages/coding-agent/src/core/package-manager.ts](packages/coding-agent/src/core/package-manager.ts)
- [packages/coding-agent/src/core/extensions/loader.ts](packages/coding-agent/src/core/extensions/loader.ts)
- [packages/coding-agent/src/core/extensions/types.ts](packages/coding-agent/src/core/extensions/types.ts)
- [packages/coding-agent/src/core/extensions/runner.ts](packages/coding-agent/src/core/extensions/runner.ts)
- [packages/coding-agent/src/core/skills.ts](packages/coding-agent/src/core/skills.ts)
- [packages/coding-agent/src/core/prompt-templates.ts](packages/coding-agent/src/core/prompt-templates.ts)
- [packages/coding-agent/src/core/settings-manager.ts](packages/coding-agent/src/core/settings-manager.ts)
- [packages/coding-agent/src/core/source-info.ts](packages/coding-agent/src/core/source-info.ts)
- [packages/coding-agent/src/core/agent-session.ts](packages/coding-agent/src/core/agent-session.ts)
- [packages/coding-agent/docs/extensions.md](packages/coding-agent/docs/extensions.md)
- [packages/coding-agent/docs/skills.md](packages/coding-agent/docs/skills.md)
- [packages/coding-agent/docs/prompt-templates.md](packages/coding-agent/docs/prompt-templates.md)
- [packages/coding-agent/docs/packages.md](packages/coding-agent/docs/packages.md)
</details>

# Resources, Extensions, Skills, and Packages

This page explains Pi’s portable extension layer: how local files, repository resources, npm packages, git packages, and package-manifest resources become runtime extensions, skills, prompt templates, themes, context files, and system-prompt inputs.

The architecture is intentionally provider-neutral. Resource loading discovers and annotates capabilities; model providers are a separate extension capability through `registerProvider`, so a package or skill source does not imply a hosted service, model vendor, or proprietary catalog dependency.

## Architecture at a Glance

`DefaultResourceLoader` is the central facade. It exposes loaded extensions, skills, prompts, themes, context files, system prompt content, and append-system-prompt content through a small `ResourceLoader` interface. It delegates source resolution to `DefaultPackageManager`, extension module loading to `extensions/loader.ts`, and resource-specific parsing to `skills.ts`, `prompt-templates.ts`, and theme loading.

Sources: [packages/coding-agent/src/core/resource-loader.ts:23-37](), [packages/coding-agent/src/core/resource-loader.ts:321-493](), [packages/coding-agent/src/core/package-manager.ts:863-915]()

```mermaid
flowchart TD
  subgraph Config["Settings and CLI inputs"]
    Settings["SettingsManager\npackages/extensions/skills/prompts/themes"]
    CLI["additional* paths\n--extension, --skill, --prompt-template"]
  end

  subgraph Resolution["DefaultPackageManager"]
    PM["resolve() / resolveExtensionSources()"]
    Manifest["package.json pi manifest\nor convention dirs"]
  end

  subgraph Loading["DefaultResourceLoader"]
    RL["reload()"]
    ExtLoad["loadExtensions()"]
    Skills["loadSkills()"]
    Prompts["loadPromptTemplates()"]
    Themes["loadThemes()"]
    Context["AGENTS.md / SYSTEM.md"]
  end

  subgraph Runtime["Agent session runtime"]
    Runner["ExtensionRunner"]
    Prompt["System prompt and commands"]
  end

  Settings --> PM
  CLI --> PM
  PM --> Manifest
  PM --> RL
  RL --> ExtLoad
  RL --> Skills
  RL --> Prompts
  RL --> Themes
  RL --> Context
  ExtLoad --> Runner
  Skills --> Prompt
  Prompts --> Prompt
  Context --> Prompt
  Runner --> RL
```

## Resource Types and Provenance

Pi tracks four package-managed resource types: `extensions`, `skills`, `prompts`, and `themes`. Every resolved resource carries `PathMetadata` with a source label, scope (`user`, `project`, or `temporary`), origin (`package` or `top-level`), and optional base directory. `SourceInfo` is the runtime-facing form of that metadata and is attached to extension tools, commands, skills, prompts, and themes.

Sources: [packages/coding-agent/src/core/package-manager.ts:72-89](), [packages/coding-agent/src/core/source-info.ts:3-28](), [packages/coding-agent/src/core/resource-loader.ts:589-630]()

| Field | Meaning |
|---|---|
| `source` | Where the resource came from, such as a package spec, `local`, `auto`, or `extension:*`. |
| `scope` | Whether it is user, project, or temporary runtime input. |
| `origin` | Whether it came from an installed package or a top-level local/auto path. |
| `baseDir` | Directory used to resolve relative assets and provenance. |

This metadata is not cosmetic. It drives precedence, diagnostics, command provenance, and portable UI flows such as “show commands by source” without parsing paths.

## Settings, Package Sources, and Local Paths

Settings support both package sources and direct resource paths. A package source can be a string or an object with filters for `extensions`, `skills`, `prompts`, and `themes`. Direct arrays exist for local `extensions`, `skills`, `prompts`, and `themes`.

Sources: [packages/coding-agent/src/core/settings-manager.ts:67-100](), [packages/coding-agent/src/core/settings-manager.ts:118-138](), [packages/coding-agent/src/core/settings-manager.ts:407-437]()

```ts
// packages/coding-agent/src/core/settings-manager.ts
export type PackageSource =
  | string
  | {
      source: string;
      extensions?: string[];
      skills?: string[];
      prompts?: string[];
      themes?: string[];
    };
```

`SettingsManager` loads global and project settings and deep-merges project settings over global settings. `DefaultPackageManager.resolve()` then processes project packages before user packages, resolves local entries relative to the appropriate base directory, and adds auto-discovered resources.

## Package Manager Responsibilities

`DefaultPackageManager` accepts three broad source families:

| Source type | Example | Behavior |
|---|---|---|
| npm | `npm:@scope/pkg@1.2.3` | Installed under the user or project npm root; pinned versions are recognized. |
| git | `git:github.com/user/repo@v1` | Cloned under the user or project git root; temporary unpinned git sources can refresh. |
| local | `./path/to/package` | Used directly; files can be single extensions and directories can use package rules. |

Sources: [packages/coding-agent/src/core/package-manager.ts:1200-1280](), [packages/coding-agent/src/core/package-manager.ts:1380-1401](), [packages/coding-agent/docs/packages.md:31-93]()

Package directories are resolved by manifest first, then convention directories. A `package.json` `pi` manifest can list resource entries; otherwise convention directories such as `extensions/`, `skills/`, `prompts/`, and `themes/` are scanned. Package filters can narrow or disable resource types.

Sources: [packages/coding-agent/src/core/package-manager.ts:1985-2036](), [packages/coding-agent/src/core/package-manager.ts:2040-2130](), [packages/coding-agent/src/core/package-manager.ts:2135-2176]()

## Discovery and Precedence

The package manager sorts resolved resources so collision handling is deterministic. Project top-level settings have the highest precedence, followed by project auto-discovery, user settings, user auto-discovery, and finally package resources. After sorting, canonical path deduplication removes duplicate filesystem entries.

Sources: [packages/coding-agent/src/core/package-manager.ts:153-177](), [packages/coding-agent/src/core/package-manager.ts:2403-2423]()

Auto-discovery covers `.pi` resource directories and `.agents/skills`. Project `.agents/skills` directories are collected from the current working directory upward to the git repository root, or to the filesystem root when no git repository is found. User `.agents/skills` remains user-scoped.

Sources: [packages/coding-agent/src/core/package-manager.ts:387-427](), [packages/coding-agent/src/core/package-manager.ts:2184-2375]()

## Extension Loading and Runtime Binding

Extensions are TypeScript or JavaScript modules loaded through `jiti`. The loader creates an `Extension` record with maps for handlers, tools, renderers, commands, flags, and shortcuts. During module loading, registration methods write into that extension record, while action methods delegate to a shared runtime.

Sources: [packages/coding-agent/src/core/extensions/loader.ts:124-189](), [packages/coding-agent/src/core/extensions/loader.ts:177-318](), [packages/coding-agent/src/core/extensions/types.ts:1537-1568]()

Before the runner is bound, action methods throw or queue provider registrations. `ExtensionRunner.bindCore()` copies host-provided actions into the runtime and flushes queued provider registrations. This is the key separation point: extensions can register tools, commands, events, and providers, but the model registry and session actions are supplied by the host runtime.

Sources: [packages/coding-agent/src/core/extensions/loader.ts:124-169](), [packages/coding-agent/src/core/extensions/runner.ts:266-333](), [packages/coding-agent/src/core/extensions/types.ts:1450-1535]()

## Extension API Surface

The extension API includes event subscription, tool registration, commands, shortcuts, flags, message rendering, session actions, tool activation, model selection, thinking-level controls, provider registration, and an event bus.

Sources: [packages/coding-agent/src/core/extensions/types.ts:1084-1318](), [packages/coding-agent/src/core/extensions/types.ts:424-472]()

Provider registration is intentionally one API among many. It can add or override model providers, but resource packages are still loaded from files, npm, git, or local directories. That keeps BYOC/BYOK flows portable: a team can distribute skills and prompts without coupling them to a model vendor, or distribute a provider extension without changing the resource-loading architecture.

## Skills

A skill is loaded from Markdown, usually `SKILL.md`, with frontmatter metadata. The loader validates names and descriptions, skips skills with missing descriptions, records diagnostics for warnings, and preserves `disable-model-invocation` so hidden skills can be invoked explicitly without appearing in the system prompt.

Sources: [packages/coding-agent/src/core/skills.ts:67-120](), [packages/coding-agent/src/core/skills.ts:168-275](), [packages/coding-agent/src/core/skills.ts:277-327]()

Skills are formatted into an XML-like block for the system prompt. Only name, description, and location are always included; the model is instructed to read the skill file when the task matches. This implements progressive disclosure: descriptions are always visible, full instructions remain file-backed.

Sources: [packages/coding-agent/src/core/skills.ts:335-370](), [packages/coding-agent/docs/skills.md:35-76]()

## Prompt Templates

Prompt templates are Markdown files whose filename becomes the slash command name. Frontmatter can provide `description` and `argument-hint`; otherwise the first non-empty body line becomes a fallback description. Templates support positional substitution (`$1`, `$2`), all-argument substitution (`$@`, `$ARGUMENTS`), and bash-style slicing (`${@:N}` / `${@:N:L}`).

Sources: [packages/coding-agent/src/core/prompt-templates.ts:11-18](), [packages/coding-agent/src/core/prompt-templates.ts:68-102](), [packages/coding-agent/src/core/prompt-templates.ts:104-131]()

Prompt loading is non-recursive for default prompt directories, but explicit paths can point to files or directories. At runtime, `expandPromptTemplate()` only expands text that starts with `/` and matches a loaded template name.

Sources: [packages/coding-agent/src/core/prompt-templates.ts:194-262](), [packages/coding-agent/src/core/prompt-templates.ts:269-282](), [packages/coding-agent/docs/prompt-templates.md:1-68]()

## Context Files and System Prompt Files

Resource loading also discovers context and prompt files. Context files are `AGENTS.md`, `AGENTS.MD`, `CLAUDE.md`, or `CLAUDE.MD`, loaded first from the agent directory and then from ancestors of the current working directory. System prompt files are discovered as `.pi/SYSTEM.md` before global `SYSTEM.md`; append-system-prompt files use `.pi/APPEND_SYSTEM.md` before global `APPEND_SYSTEM.md`.

Sources: [packages/coding-agent/src/core/resource-loader.ts:40-111](), [packages/coding-agent/src/core/resource-loader.ts:468-487](), [packages/coding-agent/src/core/resource-loader.ts:853-880]()

## Runtime Resource Extension

Extensions can contribute additional skill, prompt, and theme paths through the `resources_discover` event. `ExtensionRunner.emitResourcesDiscover()` collects paths from handlers, and `AgentSession` passes them into `DefaultResourceLoader.extendResources()` with temporary `extension:*` provenance. The system prompt is then rebuilt with the newly loaded resources.

Sources: [packages/coding-agent/src/core/extensions/types.ts:493-502](), [packages/coding-agent/src/core/extensions/runner.ts:990-1036](), [packages/coding-agent/src/core/agent-session.ts:2058-2109](), [packages/coding-agent/src/core/resource-loader.ts:281-319]()

This is the main hook for catalog-like or repository-driven resource discovery. A connector can map an external index into ordinary file paths, but the runtime still receives portable resource paths and source metadata rather than a provider-specific object model.

## Collisions and Diagnostics

Collision handling favors first-loaded resources after package-manager precedence sorting. `loadSkills()` keeps the first skill for a given name and emits collision diagnostics for later duplicates. `DefaultResourceLoader` deduplicates prompts by command name and themes by theme name. Extension tool and flag conflicts are reported as errors while keeping extensions loaded; command conflicts are resolved by numeric invocation suffixes in the runner.

Sources: [packages/coding-agent/src/core/skills.ts:387-487](), [packages/coding-agent/src/core/resource-loader.ts:800-850](), [packages/coding-agent/src/core/resource-loader.ts:890-925](), [packages/coding-agent/src/core/extensions/runner.ts:514-556]()

## Trust Boundaries

Extensions are executable modules and package installs can fetch npm or git code. Skills are instructions that can cause the model to run tools or scripts. Pi’s code keeps provenance and diagnostics, but it does not sandbox third-party package code at the resource-loader layer. Installed or auto-discovered sources should be treated as trusted code/content.

Sources: [packages/coding-agent/src/core/extensions/loader.ts:331-389](), [packages/coding-agent/src/core/package-manager.ts:956-1005](), [packages/coding-agent/docs/extensions.md:92-110](), [packages/coding-agent/docs/packages.md:14-29]()

## Provider-Neutral Integration Pattern

For a Grok-Wiki-style or catalog-like integration, keep the boundary file-oriented:

1. Resolve remote/catalog selections to local files or cloned repositories.
2. Expose resources through package manifests or convention directories.
3. Preserve `SourceInfo` so UI and commands can show provenance.
4. Use `resources_discover` for dynamic session-time additions.
5. Keep model-provider setup in a separate extension or user settings path.

Sources: [packages/coding-agent/src/core/source-info.ts:3-28](), [packages/coding-agent/src/core/package-manager.ts:1985-2036](), [packages/coding-agent/src/core/extensions/types.ts:1253-1318]()

This design stays portable across local files, repositories, npm packages, git packages, and future catalog frontends because the core runtime consumes paths and metadata, not hosted-service-specific resource handles.

## Summary

Pi’s extension layer is a path-and-metadata resource system. `DefaultPackageManager` resolves sources, `DefaultResourceLoader` loads typed resources, `ExtensionRunner` binds executable extension capabilities, and `SourceInfo` preserves provenance across tools, commands, skills, prompts, and themes. The result is extensible without making repository knowledge, skill packs, or package catalogs depend on one model provider or hosted service. Sources: [packages/coding-agent/src/core/resource-loader.ts:23-37](), [packages/coding-agent/src/core/package-manager.ts:863-915](), [packages/coding-agent/src/core/extensions/runner.ts:266-333]()

---