# vastsa/PI-Desktop

> Electron desktop agent running the pi engine in a Node sidecar, with every tool call gated and executed by a Rust host-core.

- Category: [Open-source coding agents](https://llms-technical-reviews.com/coding-agents/)
- Repository: https://github.com/vastsa/PI-Desktop (reviewed at commit `d403c96030381009c266140b7fba97edb6c1ca5d`, 2026-10-07)
- Stars: 6421 · Language: TypeScript · License: LGPL-3.0
- Canonical page: https://llms-technical-reviews.com/p/pi-desktop/

## Overview

PI-Desktop is a desktop coding-agent app built around the `pi` agent engine (`@earendil-works/pi-ai` and `pi-agent-core`, pinned at 1.0.1). It is not a fork of pi's CLI. It wraps the pi `Agent` in its own runtime and adds a desktop shell, a privileged Rust host process, plugins, MCP, scheduled tasks and a Plan mode. The product targets people who want a Codex-app-style GUI over a local agent with bring-your-own models.

The architecture is a strict three-process split, frozen in the project's baseline spec. Electron main is a "thin orchestrator". The agent loop runs in a Node sidecar, not in the renderer. A Rust binary, `host-core`, owns SQLite exclusively and executes every tool. The processes talk NDJSON JSON-RPC over stdio ([docs/spec/00-baseline.md](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/docs/spec/00-baseline.md#L123-L152)). The repository is also heavily governed. It has more than 340 ADRs, an `AGENTS.md` that sets an order of priorities for contributors, and a baseline spec with versioned decisions.

## Architecture

```mermaid
flowchart LR
  R["React renderer"] -->|"preload IPC"| M["Electron main"]
  M -->|"stdio NDJSON"| S["pi agent sidecar (Node)"]
  M -->|"stdio NDJSON"| H["host-core (Rust)"]
  S --> RT["DesktopAgentRuntime"]
  RT --> AG["pi Agent loop"]
  AG --> PROV["pi-ai provider adapters"]
  RT -->|"tools.execute via main"| H
  H --> PERM["PermissionManager + tool budget"]
  H --> TOOLS["Read/Glob/Grep/Write/Edit/Bash"]
  H --> DB["SQLite"]
  RT --> EXT["plugins, MCP, trusted extensions"]
  PH["pi-host (headless, RACP-WS)"] --> S
```

| Component | Path | Role |
|---|---|---|
| Desktop shell | `apps/desktop/` | Electron main + preload + React/Tailwind renderer |
| Agent sidecar | `packages/agent-runtime/src/sidecar.ts`, `runtime.ts` | Runs `DesktopAgentRuntime` around the pi `Agent`; tool schemas, compaction, subagents |
| Host core | `crates/host-core/` | Rust: tool execution, permissions, tool budgets, sessions, plugins, MCP registry, SQLite |
| Edit contract | `crates/host-core/src/tools/hashline/` | Line-anchored `Edit` with 4-hex whole-file tags |
| Shared contracts | `packages/shared/` | IPC/RPC types and error codes |
| Agent host | `packages/agent-host/`, `host-runtime/`, `racp/` | Headless host: admission, turn queue, approvals, RACP-WS server |
| Headless app | `apps/pi-host/` | `pi-host` CLI exposing the agent over RACP-WS |
| Plugin SDK | `packages/plugin-sdk/`, `plugin-devkit/` | `.piplug` manifest types and author tooling |

## How a request flows

1. **Launch.** Electron main starts the sidecar as its own executable with `ELECTRON_RUN_AS_NODE=1` ([agent-sidecar.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/apps/desktop/electron/main/agent-sidecar.ts#L53-L85)). It also starts the `pi-desktop-host-core` binary. The sidecar speaks stdio JSON-RPC to main, and every host call is proxied through main to the single host-core process ([sidecar.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/sidecar.ts#L1-L6)).
2. **Agent construction.** `DesktopAgentRuntime` builds a pi `Agent` with its own `streamFn`, a `beforeToolCall` gate and `toolExecution: "parallel"`. Every tool except `Task` is declared sequential, so only a batch of pure `Task` calls actually runs concurrently ([runtime.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L2019-L2030), [L2170-L2185](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L2170-L2185)).
3. **Prompt.** `prompt()` resets per-turn state and activates any MCP tools selected for this prompt. It compacts before sending if the incoming message would cross the threshold, and fails early with `CONTEXT_TOO_LARGE` if it still would not fit. It then runs extension `before_agent_start` hooks and calls `agent.prompt(...)` ([runtime.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L8460-L8589)).
4. **Turn boundaries.** Between model requests, `prepareNextTurnWithoutExtensions` recomputes the context budget. It either adds a budget reminder or runs a checkpoint compaction. If compaction fails at the hard limit, it throws instead of sending an oversized request ([runtime.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L6558-L6612)).
5. **Tool call.** Each tool's `execute` loads path-scoped instructions and, for Bash, subscribes to `tools.output` notifications for streaming progress. It then calls host-core's `tools.execute` ([runtime.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L3266-L3340)).
6. **Host-side checks.** host-core verifies the Bash call targets the expected shell and dialect. It reads the session's durable mode, registers a cancellation handle before the permission check (so `tools.abort` can cancel an approval wait), and then resolves the effective permission mode ([rpc/mod.rs](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/rpc/mod.rs#L3591-L3700)).
7. **Decide and run.** `PermissionManager` returns allow, deny, or "ask the user". On "ask", a permission card is shown, and local approvals have no automatic timeout. The tool budget then admits the call, the tool runs, and the result flows back to the pi loop, which continues until the model stops.

## Key components

### Permission model

Risk is fixed per tool. Read/Glob/Grep are low. Write/Edit/Bash/GenerateImages are high. Plugin tools default to medium unless their manifest declares otherwise, and MCP tools are always medium because a server's self-declared annotations are not trusted ([permissions.rs](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/permissions.rs#L120-L148)). Plan and Goal modes have a hard allowlist (Read, Glob, Grep, Bash, BrowserPreview) that is checked before anything else. Outside those modes there are three permission modes. `ask` auto-allows only low-risk tools. `accept-edits` also allows Write/Edit. `auto` allows everything, including paths outside the workspace ([permissions.rs](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/permissions.rs#L191-L266)).

### Tool budgets

host-core caps concurrency: 16 tools in flight, 4 shells, 8 reads, 2 mutations (1 per session), 4 plugin calls, and a queue of 64 with a 30-second admission wait ([tool_budget.rs](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/tool_budget.rs#L7-L20)). The sidecar also serialises Write/Edit on the same path with a `PathMutex` ([runtime.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L745-L747)).

### Line-anchored Edit

`Edit` takes a `tag` and `ops` rather than old/new strings ([runtime.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L3210-L3241)). The tag is the low 16 bits of a SHA-256 over the normalised file, written as 4 hex digits ([hashline/tag.rs](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/tools/hashline/tag.rs#L1-L3)). Read, Grep, Write and Edit all return it. The ops are `PUT N.=M:`, insert before or after a line, `CUT`, `REM` and `MV`. A stale tag fails the edit instead of patching the wrong text. Legacy `old_string`/`new_string` is still accepted.

### Models

`apiBindingForStyle` maps a provider's `apiStyle` to a pi-ai adapter: OpenAI Chat Completions (the default), OpenAI Responses, Anthropic Messages, the ChatGPT/Codex Responses endpoint, Pi, Google Gemini, and OpenCode Go ([provider-binding.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/provider-binding.ts#L129-L176)). The chat model catalog comes from models.dev, and thinking levels are normalised per model.

### Subagents and compaction

The `Task` tool creates a `SubagentRun` with its own pi `Agent`, the same turn-boundary compaction, and sequential tool execution ([subagent.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/subagent.ts#L272-L298)). Session compaction defaults to a model-written `summary`. An environment variable switches it to `fresh_window` ([runtime.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L885-L894)).

## Extending it

- **Plugins.** `.piplug` zip packages with a manifest that declares a renderer entry, renderer actions, agent tools, skills, filesystem and network policy, and MCP servers ([plugin-sdk/src/index.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/plugin-sdk/src/index.ts#L48-L80)). Their tools appear as `plugin_<id>_*` and go through the same host-core permission gate. Samples live in `examples/plugins/`.
- **MCP.** User-configured stdio or HTTP servers. Tools are named `mcp_<server>_<tool>` and can be selected per prompt.
- **Trusted extensions.** These are compatible with pi-coding-agent's extension layout, and the event list covers session, provider request/response, turn, message, tool, compaction and input events ([event-capabilities.ts](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/extensions/event-capabilities.ts#L1-L32)).
- **Instructions.** Path-scoped `AGENTS.md`/`CLAUDE.md`, project memory, user skills and user-defined subagents.

## Running it

- **Prebuilt.** Release builds cover macOS (arm64 and x64), Windows x64, and Linux (AppImage, deb, rpm).
- **From source.** Requires Node, pnpm 10+ and Rust. Run `pnpm install`, `cargo build -p host-core`, `pnpm build:js`, then `pnpm dev`.
- **Headless.** `apps/pi-host` runs the same sidecar and host-core behind a RACP WebSocket server with device-token pairing.
- **Models.** Bring your own key or account: API-key providers, GitHub Copilot, a ChatGPT subscription, Pi, or any OpenAI-compatible endpoint.

## Strengths and caveats

- **Strength: privilege separation.** The model-facing code never touches the disk directly. Every tool call crosses into a Rust process that owns permissions, budgets and storage.
- **Strength: precise editing.** Tag-verified, line-anchored edits catch stale context instead of silently mis-applying a replacement.
- **Strength: wide provider support.** It supports seven wire styles through pi-ai, including subscription endpoints.
- **Caveat: no OS sandbox.** Bash runs as the user, with no sandbox, once permission is granted. In `auto` mode no tool call outside Plan/Goal needs approval, including access outside the workspace.
- **Caveat: process and code weight.** Three processes, an 8,800-line runtime file and hundreds of ADRs make the codebase hard to approach for outside contributors.
- **Caveat: engine coupling.** The loop semantics (steering, `finishTurn`, tool execution modes) come from pinned pi packages, so pi upgrades are breaking-change events, which the repo tracks with contract tests.

*Sources: code at d403c96, verified Q&A.*

## How vastsa/PI-Desktop answers the Open-source coding agents questions

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

**PI-Desktop has a turn-structured agent loop** built on the `pi-agent-core` library's `Agent` class, not a separate Planner. The `SessionRuntime` class in `packages/agent-runtime/src/runtime.ts` wraps an `Agent` instance and drives the conversation turn by turn.

**Turn structure** — Each user message enters through `SessionRuntime.prompt()` (`runtime.ts:8460`), which appends user content, checks context budget (auto-compacting if needed), then calls `this.agent.prompt(...)` (`runtime.ts:8555-8557`). The agent streams the model response, collects tool calls, executes them sequentially via `toolExecution: "sequential"` (`subagent.ts:296`), and loops back for the next assistant turn via `waitForIdle()` + model `.continue()`. Stop conditions include: the model emits no tool call and a stop reason (`stop`/`end_turn`); the user aborts (`abort()`, `runtime.ts:8735`); a graceful stop is requested (`requestGracefulStop()`, `runtime.ts:8751`); or a silent/progress-only turn is auto-recovered once then stopped (`SILENT_TURN_NUDGE`, `PROGRESS_TURN_NUDGE`, `runtime.ts:828-849`).

**Tool-call schema** — Tools are declared to the model via JSON Schema using `pi-ai`'s `Type.Object()` helpers. The core tools (Read, Write, Edit, Bash, Glob, Grep, ToolSearch, Task, TaskWait, etc.) are defined in `runtime.ts:3154-3263` with schema parameters. The Edit tool uses a line-anchored format with `tag`, `ops` (PUT/CUT/REM/MV), and optional legacy `old_string`/`new_string`. Bash accepts `command` + optional `timeout`.

**Sub-agents** — The `Task` tool (`SUBAGENT_TOOL_NAME`, `subagent.ts:83`) spawns a `SubagentRun` class that creates its own `Agent` instance inside the same sidecar process (`subagent.ts:278-298`). Delegates have their own system prompt, tool set, and model (possibly pinned). They share the host connection so all permission checks go through host-core. The parent only sees the final report; intermediate messages are filtered out of model context (`session-context.ts:33-41`). `TaskWait` converges on running delegates, `TaskList` reports status, `TaskStop` stops them. Delegates are bounded: max captures, max tokens, and keep the parent from overflowing.

> **Editor's note.** Correction: the runtime class is `DesktopAgentRuntime`, not `SessionRuntime`, and the session agent uses `toolExecution: "parallel"` with every tool except `Task` marked sequential (runtime.ts L2170-L2179); the `sequential` setting cited is the subagent's.

Citations: [packages/agent-runtime/src/runtime.ts:8460-8589](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L8460-L8589) · [packages/agent-runtime/src/runtime.ts:3154-3263](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L3154-L3263) · [packages/agent-runtime/src/subagent.ts:83-94](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/subagent.ts#L83-L94) · [packages/agent-runtime/src/subagent.ts:272-298](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/subagent.ts#L272-L298) · [packages/agent-runtime/src/session-context.ts:33-41](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/session-context.ts#L33-L41) · [packages/agent-runtime/src/runtime.ts:828-849](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L828-L849)

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

**Context is gathered from session history and managed via compaction.** The `SessionRuntime` maintains a `fullEntries` array of `Entry` objects (message, compaction, branch_summary, custom) — the append-only transcript. On each turn, `buildSessionContext()` in `session-context.ts:94-105` projects these entries into `{ messages: AgentMessage[] }` for the model, slicing from the latest compaction entry forward (`buildContextEntries()`, `session-context.ts:43-51`).

**Compaction** — When the context budget is near its limit (`automaticCompactionThresholdFor()`), `prepareNextTurn()` (`runtime.ts:6560-6609`) triggers compaction before the next provider request. This runs a pi-based summary: earlier conversation is summarized into a compact text block, the retained tail (latest user messages, pending tool results) is preserved, and a checkpoint is persisted. The strategy defaults to `summary` (spends a model request to generate a structured summary) but can be set to `fresh_window` via env var (`runtime.ts:887-894`). Fallback paths exist when summary generation fails (`COMPACTION_FALLBACK_MARKER`, `runtime.ts:701-704`).

**Repo maps and search** — The system prompt sections include a `runtime` section (mode prompt, tool list, shell guidance, scratch dir) and tool activation state (`system-transcript.ts:36-65`). The model calls tools like `Grep` (regex search), `Glob` (file pattern matching), and `Read` (bounded file windows with line numbers) to gather context at runtime — there is no offline embedding or vector index.

**Long sessions** — Context compaction is the primary mechanism for keeping sessions within the window. The compaction entry replaces old messages with a summary plus a retained tail of recent messages. Delegation helps too: subagent runs produce a compact report rather than their full transcript. The `context_budget` system section reminds the model of remaining tokens (`runtime.ts:938-958`).


Citations: [packages/agent-runtime/src/session-context.ts:94-105](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/session-context.ts#L94-L105) · [packages/agent-runtime/src/runtime.ts:6560-6609](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L6560-L6609) · [packages/agent-runtime/src/runtime.ts:887-894](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L887-L894) · [packages/agent-runtime/src/runtime.ts:701-704](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L701-L704) · [packages/agent-runtime/src/system-transcript.ts:36-65](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/system-transcript.ts#L36-L65) · [packages/agent-runtime/src/runtime.ts:938-958](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L938-L958)

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

**Edits use a line-anchored, tag-verified contract** (ADR 0087). The `Edit` tool (`runtime.ts:3215-3241`) takes `path`, `tag` (4-hex fingerprint of whole file from the latest Read/Write/Edit/Grep), and `ops` — operation strings with `+`-prefixed body rows. Operations are: `PUT N.=M:` (replace lines N–M), `PUT <N:` / `PUT >N:` / `PUT >$:` (insert before/after/append), `CUT N.=M` (delete), `REM` (delete file), `MV DEST` (rename). Each successful write returns a new tag for the next edit. The legacy mode (`old_string`/`new_string`) is also still available (`runtime.ts:3230-3241`) but the line-anchored format is the primary contract.

**Validation and recovery** — Edits are validated at the host-core level (`crates/host-core/src/tools/mod.rs`). Error codes include `EDIT_TAG_MISMATCH`, `EDIT_TAG_UNKNOWN`, `EDIT_LINES_UNSEEN`, `EDIT_PARSE_FAILED`, `EDIT_RANGE_INVALID`, `EDIT_NO_CHANGE` (`packages/shared/src/errors.ts:152-163`). The `RECOVERABLE_MUTATION_ERROR_CODES` set (`runtime.ts:403-407`) defines which errors get one free retry: tag mismatch, unknown tag, and unseen lines. Each error code has specific advice sent in the tool result (`mutationTerminationAdvice()`, `runtime.ts:409-430`). After a failed Edit, the guidance says to re-read the live file and regenerate with fresh tag and narrower anchors.

**Concurrency and safety** — Host-core enforces tool budgets: max 2 in-flight mutations globally, max 1 per session, max 16 total in-flight tools (`crates/host-core/src/tool_budget.rs:7-13`). `PATH_MUTATING_TOOLS` in the runtime also uses a `PathMutex` to prevent concurrent Write/Edit on the same path (`runtime.ts:747`).

**Write tool** — `Write` is simpler: `path` + `content`, create or overwrite. No diffing, no line anchoring. Both Write and Edit go through the same host-core mutation path and the same permission framework.


Citations: [packages/agent-runtime/src/runtime.ts:3215-3241](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L3215-L3241) · [packages/agent-runtime/src/runtime.ts:403-407](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L403-L407) · [packages/agent-runtime/src/runtime.ts:409-430](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L409-L430) · [packages/shared/src/errors.ts:152-163](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/shared/src/errors.ts#L152-L163) · [packages/agent-runtime/src/runtime.ts:745-747](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L745-L747) · [docs/adr/0087-line-anchored-edit-contract.md:48-59](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/docs/adr/0087-line-anchored-edit-contract.md#L48-L59)

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

**Shell commands and file writes go through a multi-layer safety system.**

**Permission modes** — The `PermissionManager` in `crates/host-core/src/permissions.rs` evaluates every tool call against an effective permission mode. Tools are classified by risk: Read/Glob/Grep = Low, Write/Edit/Bash = High, plugins = Medium (or declared), MCP = Medium (`permissions.rs:120-137`). The permission mode can be `auto` (auto-allow low-risk, prompt for high-risk), `accept-edits` (auto-allow Write/Edit only), or stricter modes that require user approval. Plan/Goal modes have an explicit allowlist (`plan_mode_allows()`, `permissions.rs:144-148`) that only admits Read, Glob, Grep, Bash, and BrowserPreview — never Write, Edit, plugins, or Task.

**Tool budgets** — `crates/host-core/src/tool_budget.rs` enforces concurrency caps: max 16 in-flight tools globally, 4 shell, 8 reads, 2 mutations, 4 plugins, 1 mutation per session, and a queue depth of 64. This prevents resource exhaustion and serializes dangerous operations.

**Bash execution** — Commands run through a resolved shell (POSIX, PowerShell, cmd) selected by the user (`crates/host-core/src/tools/shell.rs`). The `CommandShellOption` travels with the session. The runtime enforces a timeout clamp (1–21,600 seconds, `runtime.ts:1242-1250`) and detects patch commands (`isPatchCommand()`, `runtime.ts:1123-1134`) to redirect the model to use Edit/Write instead. External-path Bash calls require explicit permission (`runtime.ts:3097-3098`).

**Network policy** — Host-core mirrors the `networkPolicy` settings for LAN/enforcement decisions (`network_policy.rs:1-51`), supporting both `strict` mode (no insecure LAN endpoints) and `relaxed` mode.

**Checkpoint and audit** — The compaction system creates checkpoints before each model request, providing rollback points. The transcript is append-only. File mutations are serialized per-path. Plugin permissions are sandboxed via capabilities derived from the manifest.

> **Editor's note.** Correction: `auto` permission mode allows every tool (including outside-workspace paths) without a prompt; `ask` auto-allows only low-risk tools and `accept-edits` additionally allows Write/Edit (crates/host-core/src/permissions.rs L231-L256).

Citations: [crates/host-core/src/permissions.rs:120-137](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/permissions.rs#L120-L137) · [crates/host-core/src/permissions.rs:144-148](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/permissions.rs#L144-L148) · [crates/host-core/src/tool_budget.rs:7-19](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/tool_budget.rs#L7-L19) · [crates/host-core/src/tools/shell.rs:1-20](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/tools/shell.rs#L1-L20) · [packages/agent-runtime/src/runtime.ts:1242-1250](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L1242-L1250) · [crates/host-core/src/network_policy.rs:1-51](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/crates/host-core/src/network_policy.rs#L1-L51)

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

**PI-Desktop supports multiple model providers through the `pi-ai` library**, with a universal provider model (ADR 0012).

**Wire protocols** — `provider-binding.ts:129-176` maps user-configured `apiStyle` values to pi-ai wire adapters: `chat_completions` (OpenAI-compatible), `responses` (OpenAI Responses), `anthropic_messages` (Claude), `openai_codex_responses` (ChatGPT/Codex), `pi_messages` (Pi service), `google_generative_ai` (Gemini), and `opencode_go` (OpenCode Go API). If no explicit style is set, it defaults to OpenAI Chat Completions (`provider-binding.ts:170-175`).

**Model resolution** — Providers are stored with `apiStyle`, `baseUrl`, `modelId`, `apiKey`, and optional `modelConfig` with per-model overrides. The `apiBindingForProviderModel()` function (`provider-binding.ts:194-196`) resolves the correct wire protocol considering both provider-level and model-level API styles. Vendor accounts (GitHub Copilot, Pi, custom) are handled with special auth paths including OAuth token refresh (`provider-binding.ts:76`).

**Thinking/reasoning** — Models can be configured with thinking levels via `SessionThinkingLevel` / `SubagentThinkingLevel`. The `thinking-level.ts` module clamps and normalizes these. Claude models with adaptive thinking vs. budget-token thinking are handled differently (`provider-binding.ts:298+`). DeepSeek Flash gets special tool-declaration optimizations (`fixed-tool-declarations.ts:29-42`).

**Cost tracking** — Usage is tracked via `MessageUsage` objects accumulating tokens and costs per turn, surfaced through `request-usage.ts`. The `provider-retry.ts` module handles rate limits (up to 5 retries) and transient errors (up to 3 retries) at the provider level.

**Subagent models** — Delegations can pin their own model via `SubagentDefinition`. Electron main resolves credentials; subagent model keys must be explicitly opted-in for Task.model overrides via `subagentModelKeys` (`runtime.ts:1037-1039`). Missing or failed model bindings fall through a fallback chain.


Citations: [packages/agent-runtime/src/provider-binding.ts:129-176](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/provider-binding.ts#L129-L176) · [packages/agent-runtime/src/provider-binding.ts:47-77](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/provider-binding.ts#L47-L77) · [packages/agent-runtime/src/provider-binding.ts:194-196](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/provider-binding.ts#L194-L196) · [packages/agent-runtime/src/runtime.ts:1037-1039](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L1037-L1039) · [packages/agent-runtime/src/fixed-tool-declarations.ts:29-42](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/fixed-tool-declarations.ts#L29-L42) · [packages/agent-runtime/src/provider-binding.ts:298-299](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/provider-binding.ts#L298-L299)

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

**PI-Desktop has four extension systems.**

**1. Plugins** — Full plugin system with a `PluginManifest` (`packages/plugin-sdk/src/index.ts:48-78+`) declaring `id`, `version`, `renderer` entry, `agentTools` (tools the model can call), `skills`, `fsPolicy`, `netPolicy`, and `mcpServers`. Plugins are installed via the marketplace or local `.piplug` files. Host-core manages plugin lifecycle — install, resolve, validate, permission derivation, provider reconciliation — in `crates/host-core/src/plugins.rs`. Plugin tools are prefixed `plugin_<id>_` and go through the same host-core permission path. The `examples/plugins/` directory contains sample plugins. Plugin development CLI is in `packages/plugin-devkit/`.

**2. MCP (Model Context Protocol)** — User-configured MCP servers (`apps/desktop/electron/main/user-mcp.ts`) connect to external services via stdio or HTTP. Their tools are exposed as `mcp_<serverId>_<tool>` names. The runtime supports OAuth-based auth, connection caching per workspace, and timeout configuration. The `McpCallRegistry` tracks active calls. MCP tool selection can be per-prompt via `mcpServerIds`/`mcpToolNames` (`mcp-tool-selection.ts:20-48`). Plugins can also declare MCP servers in their manifest, which are resolved via the plugin-MCP bridge.

**3. Trusted Extensions** — Full-featured extension API (`packages/agent-runtime/src/extensions/runner.ts`) exposing lifecycle hooks (`before_agent_start`, `before_provider_request`, `after_provider_response`, `agent_settled`, etc.), custom tools, commands, UI requests, diagnostics, and model configuration. The `ExtensionAPI` object is built per session over a `TrustedExtensionBridge` (`agent-sidecar.ts:82-89`). Currently supported events are listed in `event-capabilities.ts`.

**4. Instruction files** — Per-project `AGENTS.md` and `CLAUDE.md` files (the repo itself is a showcase). The session runtime resolves path-scoped instructions from these files (`project-instructions.ts`), creates prompt sections from them, and includes them in the system prompt. Project memory is also supported via `projectMemoryPrompt` (`project-memory-prompt.ts`).


Citations: [packages/plugin-sdk/src/index.ts:48-78](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/plugin-sdk/src/index.ts#L48-L78) · [apps/desktop/electron/main/user-mcp.ts:1-75](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/apps/desktop/electron/main/user-mcp.ts#L1-L75) · [packages/agent-runtime/src/mcp-tool-selection.ts:20-48](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/mcp-tool-selection.ts#L20-L48) · [packages/agent-runtime/src/extensions/runner.ts:1-54](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/extensions/runner.ts#L1-L54) · [packages/host-runtime/src/agent-sidecar.ts:51-89](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/host-runtime/src/agent-sidecar.ts#L51-L89)
