# earendil-works/pi

> Minimal TypeScript coding agent built on its own multi-provider LLM API and agent loop, extended through in-process TypeScript extensions.

- Category: [Open-source coding agents](https://llms-technical-reviews.com/coding-agents/)
- Repository: https://github.com/earendil-works/pi (reviewed at commit `636703a0a4f2f4d8558d08f2308cb41109585bf5`, 2026-10-06)
- Stars: 112925 · Language: TypeScript · License: MIT
- Canonical page: https://llms-technical-reviews.com/p/pi/

## Overview

Pi is a terminal coding agent that sells itself on what it leaves out. The README calls it "a minimal, extensible agent harness" and says plainly that it skips sub-agents, plan mode and a permission system. What it ships instead is a small core and a large extension surface. Users and packages add the missing pieces in TypeScript.

The repository is a layered monorepo. `pi-ai` is a unified streaming LLM API with a generated model catalog. `pi-agent-core` is a generic agent loop with hooks for steering, follow-ups, request preparation and tool interception. `pi-tui` is a differential-rendering terminal UI library. `pi-coding-agent` is the `pi` CLI, which puts sessions, compaction, tools, extensions, skills and MCP on top of those layers. Newer packages (`codemode`, `mcp`, `durable`, `protocol`, `server`, `chord`) add sandboxed tool scripting, a standalone MCP client and early remote-session work.

Pi suits developers who want a hackable harness more than a finished product. Out of the box the model gets four tools (`read`, `bash`, `edit`, `write`). Sessions are append-only JSONL trees that you can branch. Everything else is opt-in.

## Architecture

```mermaid
flowchart LR
  U["User"] --> MODE["Mode: TUI / print / json / rpc"]
  SDK["SDK: createAgentSession"] --> SESS
  MODE --> SESS["AgentSession"]
  SESS --> AG["Agent (pi-agent-core)"]
  AG --> LOOP["runLoop"]
  LOOP --> AI["pi-ai streamSimple"]
  AI --> PROV["Provider APIs"]
  LOOP --> TOOLS["Tools: read/bash/edit/write"]
  SESS --> EXT["ExtensionRunner"]
  EXT --> TOOLS
  SESS --> SM["SessionManager (JSONL tree)"]
  SESS --> CMP["Compaction"]
  SESS --> MCP["MCP servers / codemode"]
```

| Component | Path | Role |
|---|---|---|
| LLM API | `packages/ai/` | `stream`/`streamSimple`, about 20 wire APIs under `src/api/`, many provider catalogs under `src/providers/` |
| Agent core | `packages/agent/src/agent-loop.ts`, `agent.ts` | Turn loop, tool execution, before/after tool hooks, steering and follow-up queues |
| Coding agent session | `packages/coding-agent/src/core/agent-session.ts` | Wires the Agent to sessions, compaction, extensions, system prompt |
| Tools | `packages/coding-agent/src/core/tools/` | `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`, `powershell` |
| Sessions | `packages/coding-agent/src/core/session-manager.ts` | Append-only JSONL tree with branching |
| Compaction | `packages/coding-agent/src/core/compaction/` | Threshold check, cut point, structured summaries |
| Extensions | `packages/coding-agent/src/core/extensions/` | jiti-loaded TypeScript extensions and their API |
| Modes | `packages/coding-agent/src/modes/` | Interactive TUI, print, JSON event stream, RPC |
| MCP | `packages/mcp/`, `packages/coding-agent/src/core/mcp-servers.ts` | MCP client and exposure modes |
| Codemode | `packages/codemode/` | Sandboxed JS (QuickJS/WASI) whose only capability is calling tools |
| TUI | `packages/tui/` | Terminal rendering library |

## How a request flows

1. **Mode.** `main.ts` resolves the app mode: `rpc` or `json` if requested, `print` if `-p` is set or stdio is not a TTY, otherwise the interactive TUI ([main.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/main.ts#L112-L123)).
2. **Prompt.** `AgentSession.prompt` first offers `/commands` to extensions, refuses input while compaction runs, then expands templates and skills and hands the message to the `Agent` ([agent-session.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/agent-session.ts#L1966-L1990)).
3. **Turn start.** `runLoop` drains queued steering messages, calls `prepareNextTurn` (where the session compacts if over threshold), and lets `prepareRequest` swap the context, model or thinking level ([agent-loop.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent-loop.ts#L163-L239), [agent-session.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/agent-session.ts#L898-L912)).
4. **Stream.** `streamAssistantResponse` applies `transformContext`, converts agent messages to LLM messages, resolves the API key per call, and relays `text_delta`, `thinking_delta` and `toolcall_delta` events ([agent-loop.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent-loop.ts#L381-L469)). The stream function is `pi-ai`'s `streamSimple`, which dispatches to a built-in provider or an API registered by an extension ([compat.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/ai/src/compat.ts#L278-L293)).
5. **Tools.** An `error` or `aborted` stop ends the run. A `length` stop fails every tool call, because the arguments may be truncated. Otherwise `executeToolCalls` runs the batch in parallel, or sequentially when the config or any tool asks for it ([agent-loop.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent-loop.ts#L245-L278), [L508-L523](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent-loop.ts#L508-L523)). Each call passes through `beforeToolCall` and `afterToolCall`, which `AgentSession` routes to extension `tool_call` and `tool_result` handlers ([agent-session.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/agent-session.ts#L652-L700)).
6. **Continue or stop.** `finishTurn` can end or force another turn. The loop keeps going while there are tool results or steering messages, then checks the follow-up queue before it emits `agent_end` ([agent-loop.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent-loop.ts#L280-L321)). There is no turn cap in the loop.

## Key components

### Agent core

`pi-agent-core` knows nothing about coding. It is a loop with clear seams: `getSteeringMessages` (input typed while the agent works), `getFollowUpMessages`, `prepareNextTurn`, `prepareRequest`, `transformContext`, `convertToLlm`, `finishTurn` and the two tool hooks. `beforeToolCall` can return `{ block: true, reason }`, and `afterToolCall` can replace content, details, error flags or request termination ([types.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/types.ts#L60-L104)). Changes to the tool set are announced to the model as `toolsAdded`/`toolsRemoved` system messages, so a replayed transcript always matches the executable tools.

### Editing

`edit` takes a path and an `edits` array of `{oldText, newText}`. Each edit is matched against the original file, and overlapping edits are rejected ([edit.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/edit.ts#L32-L41)). Execution is serialised per file by a mutation queue. The tool strips the BOM, normalises line endings to LF, applies the edits, restores the original endings, and returns a display diff and a unified patch ([edit.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/edit.ts#L150-L216)). If there is no exact match, `fuzzyFindText` retries after normalising trailing whitespace, smart quotes and dashes ([edit-diff.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/edit-diff.ts#L201-L245)). The tool asks for JSON-schema constrained sampling where the provider supports it. Pi has no git integration, linting or test loop. The model uses `bash` for those.

### Sessions and compaction

`SessionManager` stores each session as an append-only tree in JSONL. Branching moves a leaf pointer instead of rewriting history ([session-manager.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/session-manager.ts#L976-L987)). Compaction triggers when context tokens exceed `contextWindow - reserveTokens`. The defaults reserve 16,384 tokens and keep the most recent 20,000 ([compaction.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/compaction/compaction.ts#L120-L130), [L267-L270](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/compaction/compaction.ts#L267-L270)). Older entries are summarised by the model into a structured note and stay in the tree. Pi also compacts and retries after a provider rejects a request as a context overflow. There is no embedding index. Project context comes from `AGENTS.override.md`, `AGENTS.md` or `CLAUDE.md` files ([resource-loader.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/resource-loader.ts#L184-L200)) and from skill descriptions.

### Tools and safety

The default active set is `read`, `bash`, `edit` and `write` ([agent-session.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/agent-session.ts#L3648-L3655)). `grep`, `find`, `ls` and `powershell` are available on request. `bash` spawns the user's shell with the process's own rights. There is no allow-list, approval prompt or sandbox in the core. The README points to containers, OpenShell, or the Gondolin extension, which routes tools into a micro-VM. Every tool takes a pluggable `Operations` object, so file and shell access can be redirected to SSH or a container. A project-trust prompt gates loading of project-local settings, packages and extensions.

### MCP and codemode

MCP servers connect over stdio or HTTP. Their tools are named `mcp__<server>__<tool>`, and each server has an exposure mode. `direct` declares the tools to the model. `deferred` hides them until a `tool_search` tool loads them. `codemode` makes them callable only from sandboxed scripts. `hidden` registers them without making them reachable ([mcp-servers.ts](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/mcp-servers.ts#L43-L52)). Codemode runs model-written JavaScript in QuickJS on WASI, and calling injected tools is its only capability. This keeps large tool catalogues out of the prompt.

## Extending it

- **Extensions.** TypeScript modules loaded through jiti, with a default export that receives the extension API. They can register tools, commands, providers and MCP servers, subscribe to events (`tool_call`, `tool_result`, input and more), send messages and draw UI. Extensions can be hot-reloaded.
- **Skills.** Directories containing `SKILL.md`. Only the name and description go into the prompt, and the body loads on demand.
- **Prompt templates and themes.** Reusable `/template` text and TUI colour themes.
- **Pi packages.** Bundles of the above, shared through npm or git and installed with the built-in package manager.
- **Custom models.** `models.json` points existing wire APIs (for example `openai-completions`) at Ollama, vLLM, LM Studio or any compatible endpoint.
- **Embedding.** `createAgentSession` in `sdk.ts`, RPC mode (JSONL over stdio) or JSON event output.

## Running it

- **Install.** Use the install script, `npm install -g @earendil-works/pi-coding-agent`, or `nix run`. Node.js 22.19 or newer is required.
- **Start.** Run `pi` in a project directory and `/login` to connect a subscription or API key. Use `pi -p "..."` for one-shot output or `--mode rpc` / `--mode json` for automation.
- **From source.** `npm install --ignore-scripts`, `npm run build`, then `./pi-test.sh`.

## Strengths and caveats

- **Strength: clean layering.** The LLM API, agent loop, TUI and coding agent are separate packages with small, documented seams. Each is useful on its own.
- **Strength: extension depth.** Extensions run in-process with access to tool interception, providers, commands and UI. Features other agents hard-code (sub-agents, plan mode, permissions) can be built as packages.
- **Strength: careful edit tool.** Multi-edit against the original file, per-file locking, line-ending and BOM preservation, and a narrow fuzzy fallback.
- **Strength: branchable sessions** and a structured, threshold-driven compaction.
- **Caveat: no guardrails by default.** No approvals, sandbox or checkpoints. `bash` runs with your full rights unless you containerise pi or add an extension.
- **Caveat: minimal on purpose.** No sub-agents, no plan mode, no repo index. Teams that want those must assemble them.
- **Caveat: a moving target.** The `durable`, `server`, `protocol` and `chord` packages are new and partly experimental. `agent-session.ts` alone is about 4,400 lines.

*Sources: code at 636703a, verified Q&A.*

## How earendil-works/pi answers the Open-source coding agents questions

### How is the agent loop implemented? (answered)

The agent loop lives in `packages/agent/` — a single-threaded, turn-based loop with no separate planner component. **Single loop, no planner.** The `runLoop()` function in `packages/agent/src/agent-loop.ts` (line 163) runs an inner loop processing assistant responses and tool calls, and an outer loop that checks for follow-up messages. There is no planning phase: every turn the model produces a response (text + optional tool calls) and the loop executes them. **Tool-call schema.** Tools are defined via `AgentTool<T>` in `packages/agent/src/types.ts` (line 464) with a `name`, `parameters` (typebox schema), `execute()` function, and optional `prepareArguments`/`beforeToolCall`/`afterToolCall` hooks. Tool calls from the model are validated against their schemas before execution; invalid calls produce error results. Execution can be `"parallel"` (default — concurrent) or `"sequential"` — see `executeToolCalls()` at line 508. **Turn structure.** Each turn: (1) pending messages (steering/follow-up) are emitted, (2) `prepareRequest` hook runs, (3) `streamAssistantResponse()` at line 381 converts `AgentMessage[]` to `Message[]` via `convertToLlm`, calls the LLM stream function, and emits streaming events (`start`, `text_delta`, `toolcall_start`, etc.), (4) any tool calls are executed with results pushed back to context, (5) `finishTurn` decides continuation. **Stop conditions.** A turn stops on `stopReason === "error"` or `"aborted"` (hard stop, line 245-256). Otherwise, if no tool calls remain, no steering/follow-up messages are queued, and `finishTurn` doesn't request continuation, the loop ends (line 310-318). **Sub-agents.** Pi explicitly chooses not to implement sub-agents (README line 19). Instead it supports nested tool calls via `ctx.executeTool()` in extensions (see `packages/coding-agent/src/core/extensions/types.ts` line 394), codemode scripts, and MCP tool delegation. Each model request is self-contained; the model may call tools that internally call other tools, but no child agent loops exist.


Citations: [packages/agent/src/agent-loop.ts:163-321](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent-loop.ts#L163-L321) · [packages/agent/src/agent-loop.ts:381-469](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent-loop.ts#L381-L469) · [packages/agent/src/types.ts:463-497](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/types.ts#L463-L497) · [packages/agent/src/types.ts:47-55](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/types.ts#L47-L55) · [packages/agent/src/agent.ts:298-306](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent.ts#L298-L306) · [packages/coding-agent/src/core/extensions/types.ts:383-395](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/extensions/types.ts#L383-L395)

### How is repository context gathered and kept within the context window? (answered)

Pi gathers repo context from multiple sources and uses compaction (LLM-generated summaries) to stay within context windows. **System prompt assembly.** `buildSystemPrompt()` in `packages/coding-agent/src/core/system-prompt.ts` (line 88) constructs the system prompt from multiple sections: tool snippets, tool guidelines, skill descriptions, context files (`.claude/` files), and an `appendSystemPrompt` setting. Context files are read via the resource loader as `{path, content}` pairs and wrapped in `<project_instructions>` tags. **Tools for context gathering.** Pi's built-in tools (`read`, `grep`, `find`, `ls`, `bash`) let the model explore the codebase. The read tool (`packages/coding-agent/src/core/tools/read.ts`) supports path, offset, and limit params. The grep tool provides regex search. Files are truncated to `DEFAULT_MAX_BYTES` / `DEFAULT_MAX_LINES` to avoid overflow. **Compaction (summarization).** When the estimated context tokens exceed `contextWindow - reserveTokens` (default 16k reserve), `shouldCompact()` in `packages/coding-agent/src/core/compaction/compaction.ts` (line 267) triggers compaction. The algorithm walks session entries from newest to oldest, accumulating tokens until `keepRecentTokens` (default 20k) are kept, then generates an LLM summary of the older conversation (line 645, `generateSummary()`). The summary uses a structured format (Goal, Progress, Key Decisions, Next Steps, Critical Context) and is inserted as a `compactionSummary` message in the session. After compaction, the older messages are hidden from the LLM context but remain in the session tree. **No embeddings or vector store.** Pi has no embeddings or RAG. All context comes from the system prompt, tool results, and conversation history. **Context transform hook.** Extensions can inject/modify context via the `transformContext` hook (defined in `AgentLoopConfig`, `packages/agent/src/agent-loop.ts` line 390), and Pi's session uses this to apply hidden tool declarations and forced prompts. **Context overflow recovery.** `isContextOverflow()` from the AI package detects when the provider rejects a request as too large; Pi then triggers `_compactBeforeNextAssistantResponse()` (`packages/coding-agent/src/core/agent-session.ts` around line 776) and retries.

> **Editor's note.** Correction: project instruction files are AGENTS.override.md, AGENTS.md or CLAUDE.md found by the resource loader, not files in a `.claude/` directory.

Citations: [packages/coding-agent/src/core/compaction/compaction.ts:267-270](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/compaction/compaction.ts#L267-L270) · [packages/coding-agent/src/core/compaction/compaction.ts:645-660](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/compaction/compaction.ts#L645-L660) · [packages/coding-agent/src/core/system-prompt.ts:79-86](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/system-prompt.ts#L79-L86) · [packages/coding-agent/src/core/compaction/compaction.ts:446-501](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/compaction/compaction.ts#L446-L501) · [packages/agent/src/types.ts:244-244](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/types.ts#L244-L244) · [packages/coding-agent/src/core/tools/read.ts:14-18](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/read.ts#L14-L18)

### How are code edits applied? (answered)

Pi provides two file-editing tools — `edit` and `write` — plus a `file-mutation-queue` for concurrency safety. **Edit tool (exact search/replace).** The `edit` tool in `packages/coding-agent/src/core/tools/edit.ts` uses an `edits` array of `{oldText, newText}` pairs. Edits are matched against the original file content, not incrementally, and overlapping edits are rejected. Matching is exact by default but falls back to fuzzy matching (`edit-diff.ts` line 207): Unicode normalization (smart quotes to ASCII, dashes to hyphens, spaces normalized) and trailing-whitespace stripping. The original file is read, the BOM is stripped, line endings are detected, content is normalized to LF, edits are applied, and native line endings are restored before writing. A unified diff and display diff with line numbers are returned in the result. **Write tool (whole file).** The `write` tool (`packages/coding-agent/src/core/tools/write.ts`) creates or overwrites a file, auto-creating parent directories. It uses a simple `{path, content}` schema. **Validation.** Tools use typebox schemas for argument validation (e.g., `editSchema` at line 32 of `edit.ts`). The `validateToolArguments()` function from `@earendil-works/pi-ai` rejects invalid arguments before execution. Truncated responses with `stopReason === "length"` have all tool calls failed immediately (agent-loop.ts line 267) because their arguments may be incomplete. **No linter or test runners.** Pi does not automatically run linters or tests after edits. The model is expected to verify its work via bash commands. **Git integration.** Pi does not auto-commit or branch before edits. The `hosted-git-info` package is a dependency for repository detection, but git operations are manual via bash. **File mutation queue.** Both edit and write tools use `withFileMutationQueue()` in `packages/coding-agent/src/core/tools/file-mutation-queue.ts` (referenced at edit.ts line 163) to serialize operations on the same file, preventing concurrent edits from different tool calls from racing. **Pluggable operations.** Both tools accept custom `ReadOperations`/`EditOperations`/`WriteOperations` interfaces, so file editing can be delegated (e.g., to SSH or containers).


Citations: [packages/coding-agent/src/core/tools/edit.ts:31-42](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/edit.ts#L31-L42) · [packages/coding-agent/src/core/tools/edit.ts:143-216](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/edit.ts#L143-L216) · [packages/coding-agent/src/core/tools/edit-diff.ts:207-245](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/edit-diff.ts#L207-L245) · [packages/coding-agent/src/core/tools/edit-diff.ts:300-362](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/edit-diff.ts#L300-L362) · [packages/coding-agent/src/core/tools/write.ts:44-97](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/write.ts#L44-L97) · [packages/agent/src/agent-loop.ts:267-270](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent-loop.ts#L267-L270)

### How are shell commands and file writes kept safe? (answered)

Pi explicitly states it "does not include a built-in permission system" (README line 90). Safety is provided through extensible checkpoints rather than built-in sandboxing. **No built-in permission manager.** By default, the bash tool (`packages/coding-agent/src/core/tools/bash.ts`) spawns child processes via `spawn()` with the same OS-level permissions as the Pi process. The filesystem tools (read, write, edit, grep, find) all operate with the process's user permissions. There is no allow/deny list, container, or landlock built in. **Pluggable tool operations.** Every built-in tool (bash, read, write, edit, grep, find, ls, powershell) defines an `Operations` interface (e.g., `BashOperations` at bash.ts line 75, `EditOperations` at edit.ts line 83). These can be overridden to delegate execution to remote systems, containers, or sandboxes. **Containerization patterns** are documented in `packages/coding-agent/docs/containerization.md`: (1) plain Docker runs the whole Pi process in a container, (2) Docker Sandboxes uses managed sandbox with credential proxy, (3) OpenShell provides policy-controlled sandboxes, (4) the Gondolin extension routes built-in tools into a local Linux micro-VM while keeping Pi and provider auth on the host. **Abort signals.** All tools accept an `AbortSignal` and check `signal.aborted` between operations, enabling responsive cancellation of long-running commands. The bash tool also kills the process tree on abort. **Tool execution hooks.** `beforeToolCall` and `afterToolCall` hooks (`packages/agent/src/types.ts` lines 66-104) allow extensions to block, modify, or monitor tool execution — effectively a permission callback. **Bash output truncation.** The bash executor (`packages/coding-agent/src/core/bash-executor.ts`) truncates output beyond `DEFAULT_MAX_BYTES` and writes full output to a temp file, preventing context overflow. Binary output is sanitized via `sanitizeBinaryOutput()`. **Project trust.** `packages/coding-agent/src/core/project-trust.ts` gates whether project-level settings and MCP server configurations are loaded, preventing untrusted projects from injecting tool configurations.


Citations: [packages/coding-agent/src/core/tools/bash.ts:75-94](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/bash.ts#L75-L94) · [packages/coding-agent/src/core/bash-executor.ts:48-152](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/bash-executor.ts#L48-L152) · [packages/coding-agent/docs/containerization.md:1-30](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/docs/containerization.md#L1-L30) · [packages/agent/src/types.ts:66-104](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/types.ts#L66-L104) · [packages/coding-agent/src/core/tools/edit.ts:83-90](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/tools/edit.ts#L83-L90)

### Which models are supported and how are they called? (answered)

Pi supports many providers through the `@earendil-works/pi-ai` package. **Providers.** The AI package at `packages/ai/src/` has provider implementations for Anthropic (`anthropic-messages.ts`), OpenAI (`openai-completions.ts`, `openai-responses.ts`), Google (`google-generative-ai.ts`), Mistral (`mistral-conversations.ts`), Bedrock (`bedrock-converse-stream.ts`), Azure OpenAI (`azure-openai-responses.ts`), Cloudflare Workers AI, llama.cpp, and a generic `pi-messages.ts` proxy. Each provider implements a `streamSimple()` function matching the `StreamFn` signature. **Model catalog.** Models are defined in a generated `models.generated.ts` and in `model-catalog.ts`, organized by provider with metadata (context window, cost, reasoning support, input types). The `ModelRuntime` class in `packages/coding-agent/src/core/model-runtime.ts` orchestrates providers, credentials, and model refresh. **Local models.** Pi directly integrates with llama.cpp via the router (`/llama` command). Compatible endpoints (Ollama, vLLM, SGLang, LM Studio) can be configured via `models.json` using existing API adapters (e.g., `"api": "openai-completions"`). **Tool-calling vs text formats.** Pi uses native tool-calling (function/tool schema) with all providers that support it. The `constrainedSampling` config on tools (`edit.ts` line 156) requests JSON Schema-guided generation when the provider supports it. For providers without native tool support, tools can be serialized as text instructions. **Per-model prompt tuning.** Tools contribute `promptSnippet` and `promptGuidelines` (e.g., `editToolSystemPromptContribution` at edit.ts line 43) that feed into the system prompt per active tool, allowing the model-specific prompt to be tuned. **Cost tracking.** Every assistant message carries a `usage` object with token counts and cost (`packages/agent/src/agent.ts` line 48-55). The `usage-totals.ts` module aggregates costs across the session. **Default model IDs** per provider are in `model-resolver.ts` (e.g., `claude-opus-4-8`, `gpt-5.5`, `gemini-3.1-pro-preview`).


Citations: [packages/coding-agent/src/core/model-resolver.ts:19-62](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/model-resolver.ts#L19-L62) · [packages/coding-agent/src/core/model-runtime.ts:1-60](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/model-runtime.ts#L1-L60) · [packages/coding-agent/src/core/provider-composer.ts:91-108](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/provider-composer.ts#L91-L108) · [packages/coding-agent/docs/models.md:47-62](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/docs/models.md#L47-L62) · [packages/agent/src/agent.ts:48-56](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/agent/src/agent.ts#L48-L56)

### How can it be extended and customised? (answered)

Pi has four major extension mechanisms: **Extensions (TypeScript).** Extensions are TypeScript modules that export a default factory receiving `ExtensionAPI` (see docs at `packages/coding-agent/docs/extensions.md` and types at `packages/coding-agent/src/core/extensions/types.ts`). They can `registerTool()` (line 1634), `registerCommand()` (line 1643), `registerProvider()` (line 1821), `registerMcpServer()` (line 1857), subscribe to lifecycle events via `pi.on()`, send messages, and access UI. Extensions run inside the Pi process and can inspect all state. Reload support means they can be hot-replaced. **MCP Protocol.** Pi connects to MCP servers over stdio or streamable HTTP (`packages/coding-agent/docs/mcp.md`). Tools from MCP servers are surfaced to the model as `mcp__<server>__<tool>` with configurable exposure (`direct`, `deferred`, `codemode`, `hidden`). Resource tools (`read_mcp_resource`, `list_mcp_resources`) are also available. **Skills.** Per the Agent Skills specification, skills are directories with `SKILD.md` files. Pi scans configured skill locations and adds name/description to the system prompt; full instructions load on demand when the model invokes them. **Pi Packages.** Bundles of extensions, skills, prompt templates, themes, and context files distributed via npm or git (`packages/coding-agent/docs/packages.md`). **Config/Instruction files.** Pi reads `settings.json` for tool config, `models.json` for custom provider endpoints, `mcp.json` for MCP servers, and context files (`.claude/` directory) as project instructions. **Headless/SDK use.** The `@earendil-works/pi-coding-agent` package exports a TypeScript SDK (`packages/coding-agent/src/core/sdk.ts`). The `CreateAgentSessionOptions` interface (line 48) allows programmatic session creation with custom tools, models, and configuration. There's also RPC mode accepting JSONL commands on stdin/stdout, JSON mode, and print mode. **Prompt templates** allow reusable message text expansion. **Themes** customize terminal colors.

> **Editor's note.** Correction: skills are directories containing a `SKILL.md` file (not `SKILD.md`).

Citations: [packages/coding-agent/src/core/extensions/types.ts:1634-1860](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/extensions/types.ts#L1634-L1860) · [packages/coding-agent/docs/extensions.md:1-50](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/docs/extensions.md#L1-L50) · [packages/coding-agent/docs/mcp.md:1-80](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/docs/mcp.md#L1-L80) · [packages/coding-agent/docs/skills.md:1-30](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/docs/skills.md#L1-L30) · [packages/coding-agent/docs/packages.md:1-1](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/docs/packages.md#L1-L1) · [packages/coding-agent/src/core/sdk.ts:48-100](https://github.com/earendil-works/pi/blob/636703a0a4f2f4d8558d08f2308cb41109585bf5/packages/coding-agent/src/core/sdk.ts#L48-L100)
