@counterposition/skill-pi 0.99.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,7 +28,7 @@ If you develop inside this repo, Pi already loads the skill via `.pi/settings.js
28
28
 
29
29
  ## What this covers
30
30
 
31
- Pi's architecture and package layout. Skill authoring, discovery, and routing. Extensions, tools, and UI hooks. MCP servers, codemode, and nested tool calls. Settings, models, providers, classifiers, and packages. Sessions, forks, and compaction. The SDK and RPC interface. Checked against Pi 0.99.1.
31
+ Pi's architecture and package layout. Skill authoring, discovery, and routing. Extensions, tools, and UI hooks. MCP servers, codemode, and nested tool calls. Settings, models, providers, classifiers, and packages. Sessions, forks, and compaction. The SDK and RPC interface. Checked against Pi 1.0.2.
32
32
 
33
33
  The reference material lives in `references/` as task-specific documents — not a single monolith, but pieces sized for injection into a conversation when relevant.
34
34
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@counterposition/skill-pi",
3
- "version": "0.99.1",
3
+ "version": "1.0.2",
4
4
  "description": "Pi coding agent reference skill — extensions, settings, providers, SDK/RPC, packages, and .pi/ config",
5
5
  "keywords": [
6
6
  "ai-skill",
@@ -30,8 +30,8 @@
30
30
  "access": "public"
31
31
  },
32
32
  "devDependencies": {
33
- "@earendil-works/pi-ai": "^0.99.1",
34
- "@earendil-works/pi-coding-agent": "^0.99.1",
33
+ "@earendil-works/pi-ai": "^1.0.2",
34
+ "@earendil-works/pi-coding-agent": "^1.0.2",
35
35
  "@types/node": "^25.5.0",
36
36
  "typebox": "^1.3.16",
37
37
  "typescript": "^6.0.2",
@@ -28,7 +28,7 @@ If you develop inside this repo, Pi already loads the skill via `.pi/settings.js
28
28
 
29
29
  ## What this covers
30
30
 
31
- Pi's architecture and package layout. Skill authoring, discovery, and routing. Extensions, tools, and UI hooks. MCP servers, codemode, and nested tool calls. Settings, models, providers, classifiers, and packages. Sessions, forks, and compaction. The SDK and RPC interface. Checked against Pi 0.99.1.
31
+ Pi's architecture and package layout. Skill authoring, discovery, and routing. Extensions, tools, and UI hooks. MCP servers, codemode, and nested tool calls. Settings, models, providers, classifiers, and packages. Sessions, forks, and compaction. The SDK and RPC interface. Checked against Pi 1.0.2.
32
32
 
33
33
  The reference material lives in `references/` as task-specific documents — not a single monolith, but pieces sized for injection into a conversation when relevant.
34
34
 
@@ -7,7 +7,7 @@ compatibility: "Pi-specific guidance for agents that support Agent Skills"
7
7
 
8
8
  # Pi Coding Agent
9
9
 
10
- Pi is a minimal terminal coding harness. Default tools: `read`, `bash`, `edit`, `write`; `grep`, `find`, `ls`, and the Windows `powershell` tool are opt-in via the `defaultTools` setting or `--tools`. Built-in extensions (Pi 0.99.0) add MCP servers, the `codemode` tool (model-written JavaScript that calls other tools), and `tool_search`. `codemode` and `tool_search` start inactive: enable them with `defaultTools`/`--tools`, or let MCP activate them when a server needs them. Modes: interactive (`pi`), print (`pi -p`), JSON (`--mode json`), RPC (`--mode rpc`), or embedded (`createAgentSession()`). Sub-agents, plan mode, and permission flows are intentionally left to extensions and Pi packages. Skill synced with Pi 0.99.1.
10
+ Pi is a minimal terminal coding harness. Default tools: `read`, `bash`, `edit`, `write`; `grep`, `find`, `ls`, and the Windows `powershell` tool are opt-in via the `defaultTools` setting or `--tools`. Built-in extensions (Pi 0.99.0) add MCP servers, the `codemode` tool (model-written JavaScript that calls other tools), and `tool_search`. `codemode` and `tool_search` start inactive: enable them with `defaultTools`/`--tools`, or let MCP activate them when a server needs them. Modes: interactive (`pi`), print (`pi -p`), JSON (`--mode json`), RPC (`--mode rpc`), or embedded (`createAgentSession()`). Sub-agents, plan mode, and permission flows are intentionally left to extensions and Pi packages. Skill synced with Pi 1.0.2.
11
11
 
12
12
  Pi's philosophy: **adapt Pi to your workflows, not the other way around**.
13
13
 
@@ -18,13 +18,14 @@ Core packages on npm (source: [github.com/earendil-works/pi](https://github.com/
18
18
  | Package | Purpose |
19
19
  |---------|---------|
20
20
  | `@earendil-works/pi-ai` | Unified LLM API across 20+ providers |
21
- | `@earendil-works/pi-agent-core` | Agent runtime with tool execution and state |
21
+ | `@earendil-works/pi-agent-core` | `Agent`, the agent loop, and the proxy stream (Pi 1.0.0 removed the experimental harness, sessions, and its `./node`/`./harness/*` subpaths) |
22
22
  | `@earendil-works/pi-tui` | Terminal UI components |
23
23
  | `@earendil-works/pi-coding-agent` | CLI, extensions, skills, sessions, settings (also exports `./client` for remote sessions and `./rpc-entry`) |
24
24
  | `@earendil-works/pi-codemode` | QuickJS sandbox that runs model-written JavaScript against injected tools (Pi 0.99.0) |
25
25
  | `@earendil-works/pi-mcp` | Standalone MCP client: stdio and streamable HTTP, OAuth (Pi 0.99.0) |
26
26
  | `@earendil-works/pi-client` / `@earendil-works/pi-protocol` | Experimental remote-session client and wire protocol (Pi 0.84.0) |
27
27
  | `@earendil-works/pi-telemetry` | Vendor-neutral telemetry contracts (Pi 0.84.0) |
28
+ | `@earendil-works/pi-durable` | Experimental durable agent harness: conversations, turns, and tool calls committed to storage and resumed after a crash (Pi 1.0.0; replaces the pi-agent-core harness) |
28
29
 
29
30
  ## File System Layout
30
31
 
@@ -50,13 +50,13 @@
50
50
  {
51
51
  "id": 4,
52
52
  "prompt": "Add the GitHub MCP server (https://api.githubcopilot.com/mcp/, token in GITHUB_TOKEN) to Pi for all my projects. It has ~90 tools, so keep them out of the model's tool list, but make search_code directly callable. Check that it works.",
53
- "expected_output": "An entry in ~/.pi/agent/mcp.json under mcpServers with the url, an Authorization header referencing ${GITHUB_TOKEN}, a non-direct server exposure (codemode, codemode-deferred, or deferred), and toolExposure making search_code direct; then pi mcp list to verify and /reload for a running session.",
53
+ "expected_output": "An entry in ~/.pi/agent/mcp.json under mcpServers with the url, an Authorization header referencing ${GITHUB_TOKEN}, a non-direct server exposure (codemode or deferred), and toolExposure making search_code direct; then pi mcp list to verify and /reload for a running session.",
54
54
  "files": [],
55
55
  "assertions": [
56
56
  "Writes the server to ~/.pi/agent/mcp.json (global), not settings.json and not the project .pi/mcp.json",
57
57
  "Uses the mcpServers object with a url field (streamable HTTP), not an sse type",
58
58
  "Keeps the secret out of the file with \"Authorization\": \"Bearer ${GITHUB_TOKEN}\" (or pi mcp add --bearer-token-env-var GITHUB_TOKEN)",
59
- "Sets server exposure to codemode, codemode-deferred, or deferred rather than direct",
59
+ "Sets server exposure to codemode or deferred (or the codemode-deferred alias) rather than direct",
60
60
  "Uses toolExposure with \"search_code\": \"direct\"",
61
61
  "Runs pi mcp list to verify the connection",
62
62
  "Tells the user to /reload or start a new session so a running session connects",
@@ -1,6 +1,6 @@
1
1
  # Codemode and Tool Search
2
2
 
3
- `codemode` is a built-in tool (Pi 0.99) that runs model-written JavaScript in a QuickJS sandbox. Scripts call Pi's other tools, filter their results, and return only what the model needs. `tool_search` declares tools the model cannot see yet. Checked against Pi v0.99.1: [cli.md#tools](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/cli.md#tools), [extensions.md#tool-exposure](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/extensions.md#tool-exposure), [settings.md#tools](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/settings.md#tools), [codemode tool source](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/src/extensions/codemode/tool.ts).
3
+ `codemode` is a built-in tool (Pi 0.99) that runs model-written JavaScript in a QuickJS sandbox. Scripts call Pi's other tools, filter their results, and return only what the model needs. `tool_search` declares tools the model cannot see yet. Checked against Pi v1.0.2: [codemode.md](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/codemode.md), [cli.md#tools](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/cli.md#tools), [extensions.md#tool-exposure](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/extensions.md#tool-exposure), [settings.md#tools](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/settings.md#tools), [codemode tool source](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/src/extensions/codemode/tool.ts).
4
4
 
5
5
  ## Enable
6
6
 
@@ -11,7 +11,7 @@ Both tools are registered inactive. The default tools stay `read`, `bash`, `edit
11
11
  ```
12
12
 
13
13
  - `+name` adds to the default selection; `--tools` replaces it, so list everything: `pi --tools read,bash,edit,write,codemode`. `--tools` is an allowlist over every tool, including extension and MCP `mcp__*` tools: unlisted tools are not registered, so scripts can call only the listed ones.
14
- - The MCP extension activates `codemode` for servers with `codemode`/`codemode-deferred` exposure and `tool_search` for `deferred` servers (see `references/mcp.md`).
14
+ - The MCP extension activates `codemode` for servers with `codemode` exposure and `tool_search` for `deferred` servers (see `references/mcp.md`).
15
15
  - SDK sessions need `createCodemodeExtension()` / `createToolSearchExtension()` in the resource loader (see `references/sdk.md`).
16
16
 
17
17
  | Setting | Default | Effect |
@@ -23,13 +23,15 @@ Both tools are registered inactive. The default tools stay `read`, `bash`, `edit
23
23
 
24
24
  The script body is an async function: top-level `await` and `return` work.
25
25
 
26
- - `await tools.<name>(args)` calls a tool. Names that are not identifiers are mangled (`my-tool` → `tools.my_tool`); `tools["my-tool"]` also works.
27
- - `ALL_TOOLS` lists `{ name, description }`. `await searchTools(query, { limit, namespace })` ranks tools with BM25; `await describeTool(name)` returns one declaration.
28
- - Output: `text(value)`, `image(dataUrlOrImageContent)`, `console.*`, and the returned value. `exit()` ends early and keeps output.
29
- - `store(key, value)` / `load(key)` keep JSON values across `codemode` calls. Successful scripts append a `codemode-store` custom entry, so values follow the session branch.
30
- - `models.getModelsOfType`, `getAvailableOfType`, `getModelOfType`, and `models.classify(model, { state, questions })` reach the model catalog and classifiers with session credentials (at most four classifications at a time). Their usage and cost are added to the `codemode` result.
31
- - An optional first line `// @options: {"max_output_tokens": 2000, "timeout_ms": 60000}`. Output is capped at 10000 estimated tokens by default (start and end kept, full text in a temp file); there is no timeout unless set.
32
- - No timers, `fetch`, `process`, `require`, or modules.
26
+ - `await tools.<name>(args)` calls a tool. Characters that are not valid in an identifier become `_` (`mcp__dev-radius__search` → `tools.mcp__dev_radius__search`).
27
+ - Since Pi 1.0.0, reading a member of `tools`, `models`, or another global namespace that does not exist throws an error naming the close matches (`tools.Bash` suggests `tools.bash`). Check for a tool with `"name" in tools`; `typeof tools.name` now throws.
28
+ - `ALL_TOOLS` lists `{ name, description }`. `await searchTools(query, { limit, namespace })` ranks tools with BM25; `await describeTool(name)` returns one declaration; `await describeNamespace(name)` returns `{ name, description?, instructions?, tools }` for a namespace such as an MCP server (`mcp__dev-radius`, `dev_radius`, and similar spellings all work).
29
+ - Output: `text(value)`, `image(dataUrlOrImageContent)`, `console.*`, and the returned value. Since Pi 0.99.2 `image()` accepts only base64 PNG, JPEG, GIF, or WebP (a `data:` URL, `{ image_url }`, or an image block); it derives the MIME type from the image bytes, rejects remote URLs, and throws a `TypeError` for anything else, such as an SVG. `exit()` ends early and keeps output.
30
+ - `store(key, value)` / `load(key)` keep JSON values across `codemode` calls (storing `undefined` deletes). Successful scripts append a `codemode-store` custom entry, so values follow the session branch. Limits: 262144 characters of JSON per value, 1048576 in total; do not store image data.
31
+ - `models.getModelsOfType`, `getAvailableOfType`, `getModelOfType`, `models.classify(model, { state, questions })`, and `models.generateImages(model, { input })` (Pi 1.0.0) reach the model catalog, classifiers, and image models with session credentials. Both calls use only the model's `provider` and `id`, never throw on provider errors (check `stopReason`), and run at most four at a time per script. Their usage and cost are added to the `codemode` result. Show generated images with `image(block)` rather than printing `data`; they are not saved to disk, and generation can take minutes, so avoid a short `timeout_ms`.
32
+ - Malformed `classify()`/`generateImages()` arguments are rejected with the expected shape, and an unknown model points to `models.getAvailableOfType()`.
33
+ - An optional first line `// @options: {"max_output_tokens": 2000, "timeout_ms": 60000}`. Output is capped at 10000 estimated tokens by default (start and end kept, full text in a temp file); there is no timeout unless set. Since Pi 1.0.1 a script fails with a `RangeError` once its output passes 16777216 characters (text plus base64 image data) or 100000 `text()`/`image()`/`console` calls; write large data to a file with a tool.
34
+ - No timers, `fetch`, `process`, `require`, or modules. The VM has 256 MB of memory; a script awaiting a promise that can never settle fails at once; scripts cannot start other `codemode` scripts.
33
35
 
34
36
  The result starts with `Script completed` or `Script failed`, the wall time, and the output. A failed script keeps its partial output followed by `Script error:`.
35
37
 
@@ -56,11 +58,13 @@ A failed, blocked, or invalid call without structured content rejects with an `E
56
58
  | `deferred` | After `tool_search` loads it | Always, but not listed; found by `tool_search`/`searchTools()` |
57
59
  | `hidden` | Never | Never |
58
60
 
61
+ MCP tools with the default server `codemode` exposure are registered like `deferred` tools: callable, but not listed in the `codemode` description, so it stays the same while servers connect. Scripts find them with `searchTools()` or `describeNamespace()`.
62
+
59
63
  So an inactive `codemode`/`deferred` tool is still callable, while an inactive `direct` tool is unreachable from scripts. Because `codemode`/`deferred` tools do not depend on the active set, they stay callable after `/tree`, resume, and fork. `ctx.tools` in a tool's `execute()` lists exactly the callable tools.
60
64
 
61
65
  ## Nested Calls and Permissions
62
66
 
63
- Each script call runs through `ctx.executeTool()`, the same pipeline as a model-issued call:
67
+ Each `tools.*` call runs through `ctx.executeTool()`, the same pipeline as a model-issued call. `models.classify()` and `models.generateImages()` do not: they run with the session's credentials and fire no `tool_call`/`tool_result` events, so a permission extension cannot gate them separately from the `codemode` call itself.
64
68
 
65
69
  - Argument validation, then `tool_call` and `tool_result` handlers, then `tool_execution_start`/`update`/`end` events.
66
70
  - Every event carries `parentToolCallId` (the `codemode` call's id); the nested call's `toolCallId` is `<parent id>/<n>`. Deeper calls nest the same way.
@@ -83,6 +87,6 @@ For a permission extension this means:
83
87
 
84
88
  - An extension that registers a tool named `codemode` or `tool_search` replaces the built-in one.
85
89
  - `"extensions": ["-builtin:codemode"]` or `["-builtin:tool-search"]` disables one (note the hyphen in the extension name); `pi config` lists them under Built-in.
86
- - Both built-ins use only `exposure`, `prepareLoadout()`, and `ctx.executeTool()`, so an extension can build the same behavior under another name.
90
+ - `codemode` uses only `exposure`, `prepareLoadout()`, and `ctx.executeTool()`, and `tool_search` only ranks registered tools (`pi.getAllTools()`) and adds matches with `pi.setActiveTools()`, so an extension can build either under another name.
87
91
 
88
92
  `@earendil-works/pi-codemode` publishes the sandbox (`CodemodeSandbox`) without Pi dependencies, for exposing any functions to model-written scripts.
@@ -140,7 +140,7 @@ Tool results may include `terminate: true` to end the current tool batch without
140
140
  - `ctx.sessionManager` (read-only; `buildContextEntries()` returns active-branch entries with compaction applied, Pi 0.80.4)
141
141
  - `ctx.modelRegistry` / `ctx.model` / `ctx.thinkingLevel` / `ctx.scopedModels` (Pi 0.83.0) — `ctx.modelRegistry` is the synchronous extension-facing facade; its `refresh()` became `Promise<void>` in Pi 0.80.8 (await it before synchronous registry reads). Since Pi 0.84.x it also exposes `getProvider(id)` (effective pi-ai provider) and `getProviderAuth(id)` (API key, headers, base URL, provider-scoped env — no loaded model required). `ctx.scopedModels` is the read-only session model scope resolved from `--models`/`enabledModels` (`{ model, thinkingLevel? }[]`, empty when unscoped); prefer it over enumerating `getAvailable()` for model pickers
142
142
  - `ctx.modelRegistry.stream()` / `streamSimple()` / `complete()` (Pi 0.86.0) — nested model calls through configured providers with request-time auth; prefer `streamSimple()` for provider-neutral calls, pass `ctx.signal`, and report the combined `usage` on the tool result
143
- - `ctx.modelRegistry.findOfType(type, provider, id)`, `getModelsOfType()`, `getAvailableOfType()`, and `classify(model, { state, questions }, { signal })` (Pi 0.99.0) — classifier and image models live beside chat models; `classify()` never rejects, so check `result.stopReason` (see `references/providers.md`)
143
+ - `ctx.modelRegistry.findOfType(type, provider, id)`, `getModelsOfType()`, `getAvailableOfType()`, and `classify(model, { state, questions }, { signal })` (Pi 0.99.0), plus `generateImages(model, { input }, options?)` (Pi 1.0.0) — classifier and image models live beside chat models; neither call rejects, so check `result.stopReason`, and report the returned `usage` on the tool result (see `references/providers.md`)
144
144
  - `ctx.hasUI` — `true` in TUI and RPC modes
145
145
  - `ctx.signal` — the active agent abort signal (or `undefined` when idle); pass it to `fetch`/model calls for abort-aware nested work
146
146
  - `ctx.isIdle()` / `ctx.hasPendingMessages()`
@@ -193,14 +193,14 @@ Important rules:
193
193
 
194
194
  ## Tool Exposure & Structured Results
195
195
 
196
- Pi 0.99.0 added fields for tools that other tools, codemode scripts, and permission extensions consume ([docs](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/extensions.md#tool-exposure), [types](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/src/core/extensions/types.ts)):
196
+ Pi 0.99.0 added fields for tools that other tools, codemode scripts, and permission extensions consume ([docs](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/extensions.md#tool-exposure), [types](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/src/core/extensions/types.ts)):
197
197
 
198
198
  - `outputSchema` (TypeBox) plus `structuredContent` on every result: the model still reads `content`; codemode scripts receive `structuredContent` instead of text. Without `outputSchema`, scripts get the text. `structuredContent` must be JSON-compatible. A `tool_result` handler that replaces `content` must also return `structuredContent`, or it is dropped.
199
199
  - `isError: true` on a returned result: the model sees an error, while `details` and `structuredContent` are kept for the UI and scripts.
200
200
  - `exposure`: `direct` (default), `model-only`, `codemode`, `deferred`, or `hidden`. `direct` and `model-only` tools activate on registration; the others do not (`defaultActive: false` also keeps a `direct` tool inactive until `--tools`/`defaultTools`/`setActiveTools()` names it). Tools cannot be unregistered: re-register with `exposure: "hidden"` to withdraw one. The table of what each value declares and allows is in `references/codemode.md`.
201
- - `namespace: { name, description? }` groups related tools under one heading in the `codemode` description, as MCP servers do.
201
+ - `namespace: { name, description?, instructions? }` groups related tools, as MCP servers do. Codemode lists a namespace under one heading with its short `description`. `instructions` (Pi 0.99.2) holds longer usage guidance that is not listed; scripts read it with `describeNamespace(name)`.
202
202
  - `annotations`: `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`, with MCP meanings. Missing hints mean "not read-only, may be destructive, open world". They are unverified; mark writes neither read-only nor casually idempotent.
203
- - `prepareLoadout(loadout)`: for tools that orchestrate others. Runs whenever the active tools change with `declared`, `callable`, and `registered` tools, and returns replacement `descriptions` and `hiddenDeclarations` (active tools left out of requests but still callable). The built-in `codemode` and `tool_search` use only this, `exposure`, and `ctx.executeTool()`.
203
+ - `prepareLoadout(loadout)`: for tools that orchestrate others. Runs whenever the active tools change with `declared`, `callable`, and `registered` tools, and returns replacement `descriptions` and `hiddenDeclarations` (active tools left out of requests but still callable). The built-in `codemode` uses only this, `exposure`, and `ctx.executeTool()`.
204
204
 
205
205
  `pi.getAllTools()` reports each tool's `exposure`, `namespace`, and `annotations`. `pi.setActiveTools()` ignores unknown and `hidden` names. Compilable examples: `examples/structured-tool.ts` and `examples/nested-tools.ts`.
206
206
 
@@ -212,10 +212,10 @@ Pi 0.80.7 lets an extension register many tools while keeping only a small initi
212
212
 
213
213
  1. Register every tool with `pi.registerTool()` (all appear in `pi.getAllTools()`).
214
214
  2. Keep a loader tool (e.g. `search_tools`) active; leave searchable tools inactive — e.g. on `session_start`, `pi.setActiveTools()` to the filtered set.
215
- 3. During loader execution, call `pi.setActiveTools([...pi.getActiveTools(), ...matches])`. The change must be purely additive; unknown names are ignored.
215
+ 3. During loader execution, call `pi.setActiveTools([...pi.getActiveTools(), ...matches])`. Unknown names are ignored. Additive changes are the most cache-friendly everywhere.
216
216
  4. Pi records the added tools on that tool result and exposes their definitions before the next model response. Since Pi 0.86.0 prompt and tool changes are transcript-backed system messages, so they survive resume and branch navigation.
217
217
 
218
- Native deferred loading preserves the cached prompt prefix on Anthropic Sonnet/Opus/Fable ≥ 4.5 (not Haiku), OpenAI `gpt-5.4`+, and Fireworks Messages models (Pi 0.86.0; name the loader `ToolSearch` or `tool_search`); Kimi K3 works via `compat.deferredToolsMode: "kimi"` (Pi 0.80.9). For verified custom models/proxies, enable `compat.supportsToolReferences: true` (`anthropic-messages`) or `compat.supportsToolSearch: true` (`openai-responses`/`openai-codex-responses`). All other models fall back to sending the full active tool list on the next request — activation still works, but may invalidate the provider's cached prefix. Non-additive changes (removals/replacements) always use the fallback.
218
+ Native deferred loading preserves the cached prompt prefix on Anthropic Sonnet/Opus/Fable ≥ 4.5 (not Haiku), OpenAI `gpt-5.4`+, and Fireworks Messages models (Pi 0.86.0; name the loader `ToolSearch` or `tool_search`); Kimi K3 works via `compat.deferredToolsMode: "kimi"` (Pi 0.80.9). For verified custom models/proxies, enable `compat.supportsToolReferences: true` (`anthropic-messages`) or `compat.supportsToolSearch: true` (`openai-responses`/`openai-codex-responses`). All other models fall back to sending the full active tool list on the next request — activation still works, but may invalidate the provider's cached prefix. Since Pi 1.0.1, Anthropic models with native mid-conversation tool changes (`compat.supportsMidConvoSystemMessages` and `supportsMidConvoToolChanges`) use the `inline-tools-2026-09-15` beta: the initial tool list never changes, later tools are defined by value in `tool_addition` blocks and withdrawn with `tool_removal`, so additions, removals, and same-name redefinitions all keep the cached prefix. Elsewhere, removals and replacements still fall back. pi-ai's `hasToolRedefinitions()` is deprecated.
219
219
 
220
220
  Cache tips: keep the loader active for the whole session; add rather than replace. Activating a tool that has `promptSnippet`/`promptGuidelines` rebuilds the system prompt and can invalidate the prefix even with native support — lazily loaded tools should rely on their `description` alone.
221
221
 
@@ -227,10 +227,12 @@ Cache tips: keep the loader active for the whole session; add rather than replac
227
227
  - `{ type: "grammar", variants: { openai_lark?: string, openai_regex?: string } }` — OpenAI Lark/regex grammar encodings
228
228
  - Set `false` to explicitly disable for that tool
229
229
 
230
- Model capability metadata gates what is actually sent: `compat.supportsStrictTools` / Anthropic built-ins enable strict JSON-schema tools, and `compat.supportsOpenAIGrammarTools` marks endpoints that accept Lark/regex grammar tools (enabled in generated metadata for GPT-5+ across OpenAI, Codex, Azure, GitHub Copilot, opencode, Cloudflare AI Gateway). Unsupported combinations fall back to normal function tools rather than failing. Since Pi 0.86.0, strict-prefer JSON-schema sampling is on by default for the built-in `read`, `bash`, `powershell`, `edit`, and `write` tools (no `PI_EXPERIMENTAL` needed); re-register a definition with `constrainedSampling: false` to opt out.
230
+ Model capability metadata gates what is actually sent: `compat.supportsStrictTools` / Anthropic built-ins enable strict JSON-schema tools, and `compat.supportsOpenAIGrammarTools` marks endpoints that accept Lark/regex grammar tools (enabled in generated metadata for GPT-5+ across OpenAI, Codex, Azure, GitHub Copilot, opencode, Cloudflare AI Gateway). With `strict: "prefer"`, unsupported combinations fall back to normal function tools; with `strict: "require"`, the request fails when the model has no strict tools or the schema uses keywords strict mode rejects. Since Pi 0.99.2 Anthropic strict tools also reject keywords such as `minimum`/`maximum`, so such `prefer` tools are sent non-strict. Since Pi 0.86.0, strict-prefer JSON-schema sampling is on by default for the built-in `read`, `bash`, `powershell`, `edit`, and `write` tools (no `PI_EXPERIMENTAL` needed); re-register a definition with `constrainedSampling: false` to opt out.
231
231
 
232
232
  ## Display Transformers
233
233
 
234
+ `pi.registerToolRenderer((toolName, next) => renderers)` (Pi 1.0.1) chooses `renderShell`/`renderCall`/`renderResult` for calls to any tool, including tools not registered yet, such as MCP tools in a resumed session before their server connects. Resolvers run in extension load order; `next()` returns what the remaining resolvers, then the registered tool, would use, so `next() ?? mine` only fills gaps. Renderers apply to the interactive transcript and HTML exports.
235
+
234
236
  `pi.registerMarkdownTransformer(transformer)` (Pi 0.84.0) chains display-only Markdown transforms over user text, assistant text, and thinking blocks:
235
237
 
236
238
  ```typescript
@@ -1,6 +1,6 @@
1
1
  # MCP Servers
2
2
 
3
- Pi 0.99 connects to MCP servers through a built-in extension. There is no package to install. Checked against Pi v0.99.1: [mcp.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/mcp.md), [cli.md#mcp-commands](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/cli.md#mcp-commands), [extensions.md#mcp-servers](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/extensions.md#mcp-servers).
3
+ Pi connects to MCP servers through a built-in extension. There is no package to install. Checked against Pi v1.0.2: [mcp.md](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/mcp.md), [cli.md#mcp-commands](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/cli.md#mcp-commands), [extensions.md#mcp-servers](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/extensions.md#mcp-servers).
4
4
 
5
5
  ## Configure
6
6
 
@@ -9,7 +9,7 @@ Pi 0.99 connects to MCP servers through a built-in extension. There is no packag
9
9
  | `~/.pi/agent/mcp.json` | Global. Put personal servers and servers with credentials here |
10
10
  | `.pi/mcp.json` | Project. Read only after the project is trusted, because stdio servers run commands |
11
11
 
12
- A project entry replaces a global entry with the same name. The format is the usual `mcpServers` object, so Claude Desktop, Claude Code, and Cursor entries copy over unchanged:
12
+ A project entry replaces a global entry with the same name, except that a project entry without `command`, `url`, or `type` (Pi 1.0.1) overrides only `enabled`, `exposure`, and `toolExposure` of the user-level server and keeps the rest (`env`, `headers`, `auth`); `{ "internal-tools": { "enabled": false } }` turns a user-level server off in one project. In a trusted project, `/mcp` offers "Enable/Disable in this project", which writes such an override. The format is the usual `mcpServers` object, so Claude Desktop, Claude Code, and Cursor entries copy over unchanged:
13
13
 
14
14
  ```json
15
15
  {
@@ -21,6 +21,7 @@ A project entry replaces a global entry with the same name. The format is the us
21
21
  "docs": {
22
22
  "url": "https://example.com/mcp",
23
23
  "headers": { "Authorization": "Bearer ${DOCS_TOKEN}" },
24
+ "description": "Search and read the product documentation",
24
25
  "exposure": "direct"
25
26
  }
26
27
  }
@@ -32,7 +33,8 @@ A project entry replaces a global entry with the same name. The format is the us
32
33
  - `type` is optional (`stdio`, `http`, or `streamable-http`); `command` implies stdio and `url` implies HTTP.
33
34
  - `env` and `headers` values accept `${NAME}` and `!command`. A command must produce the whole value: `"Authorization": "!echo Bearer $(gh auth token)"`.
34
35
  - `timeout` is seconds per request (default 60); progress notifications reset it. `enabled: false` keeps the entry without connecting.
35
- - Server names use letters, digits, `_`, `-`. Tools are named `mcp__<server>__<tool>`.
36
+ - `description` (Pi 0.99.2): one sentence on what the server offers. It lists the server in the system prompt's `mcp_servers` section, ranks its tools in tool search, and is returned by codemode's `describeNamespace()`. Without it, Pi uses the first line of the server instructions once connected.
37
+ - Server names use letters, digits, `_`, `-`. Tools are named `mcp__<server>__<tool>`, with every character other than letters, digits, and `_` replaced by `_` (Pi 0.99.2: `mcp__my-server__x` is now `mcp__my_server__x`; update permission rules that name the old spelling). Tools of one server whose names then collide all get a hash suffix. Server names that differ only in `-` and `_` count as the same server; a second one is rejected.
36
38
  - Invalid entries are skipped and reported; other servers still connect.
37
39
 
38
40
  Converting other clients: VS Code uses `servers` + `inputs` (move under `mcpServers`, replace `${input:...}` with `${NAME}`); Codex uses TOML `[mcp_servers.<name>]` (same fields as JSON); opencode uses `"type": "local"` with an array `command` (split into `command`/`args`), `environment` for `env`, and `{env:NAME}` for `${NAME}`.
@@ -43,7 +45,7 @@ These commands work outside a session, so an agent can run them through `bash`:
43
45
 
44
46
  ```bash
45
47
  pi mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem .
46
- pi mcp add docs --url https://example.com/mcp --bearer-token-env-var DOCS_TOKEN --exposure direct
48
+ pi mcp add docs --url https://example.com/mcp --bearer-token-env-var DOCS_TOKEN --exposure direct --description "Product docs"
47
49
  pi mcp add -l tools --env API_KEY='${TOOLS_KEY}' -- uvx tools-mcp # -l writes .pi/mcp.json
48
50
  pi mcp list # connects to every enabled server; exits 1 while anything is wrong
49
51
  pi mcp login sentry # OAuth: opens the browser and waits for the user to approve
@@ -51,11 +53,24 @@ pi mcp remove docs # keeps stored OAuth credentials
51
53
  pi mcp logout sentry
52
54
  ```
53
55
 
54
- `pi mcp add` does not connect; verify with `pi mcp list`. A running session picks up added or changed servers after `/reload` or a new session.
56
+ `pi mcp add` also takes `--oauth-client-id`, `--oauth-client-secret`, `--oauth-callback-port`, and `--oauth-client-name`. It does not connect; verify with `pi mcp list`. A running session picks up added or changed servers after `/reload` or a new session.
55
57
 
56
- In a session, `/mcp` opens the server manager (state, tools, errors, sign in/out, reconnect, exposure, enable/disable). Changes are written back to the `mcp.json` that defines the server. Outside the TUI, `/mcp` prints status and `/mcp login|logout|reconnect <server>` run directly.
58
+ In a session, `/mcp` opens the server manager (state, tools, errors, sign in/out, reconnect, exposure, enable/disable). Changes are written back to the `mcp.json` that defines the server, or to the project override once one exists for a user-level server. Outside the TUI, `/mcp` prints status and `/mcp login|logout|reconnect <server>` run directly.
57
59
 
58
- Pi connects on session start; the first prompt waits up to 10 seconds for startup connections. OAuth tokens live in `~/.pi/agent/mcp-auth.json`; server log notifications go to `~/.pi/agent/mcp.log`. OAuth is used only for HTTP servers whose config has no `Authorization` header. The optional `oauth: { clientId?, clientSecret?, callbackPort?, callbackUrl?, scope? }` sets a pre-registered client for servers without dynamic client registration, the loopback redirect (`callbackPort` alone gives `http://127.0.0.1:<port>/callback`; a `callbackUrl` without a port gets `callbackPort` or a free one), and `scope`, space-separated scopes to request when the server does not advertise the ones it needs.
60
+ Pi connects every enabled server in the background when a session starts (Pi 0.99.2). The first prompt waits up to 10 seconds only for servers with `direct` tools. Other servers are waited for when needed: a codemode script waits for the servers it names (`mcp__<server>`), and `searchTools()`, `ALL_TOOLS`, `tool_search`, and the resource tools wait for all of them. Server log notifications go to `~/.pi/agent/mcp.log`.
61
+
62
+ ### Authentication
63
+
64
+ - OAuth is used only for HTTP servers whose config has no `Authorization` header. Tokens live in `~/.pi/agent/mcp-auth.json`, stored per server name and URL (Pi 1.0.0), so two names for the same URL sign in with different accounts.
65
+ - `oauth: { clientId?, clientSecret?, callbackPort?, callbackUrl?, scope?, clientName?, clientRegistration?, authServerMetadataUrl? }`:
66
+ - `clientId`/`clientSecret` set a pre-registered client for servers without dynamic client registration.
67
+ - `callbackPort` alone gives `http://127.0.0.1:<port>/callback`; a `callbackUrl` (HTTP on a loopback host) without a port gets `callbackPort` or a free one.
68
+ - `scope`: space-separated scopes to request when the server does not advertise the ones it needs.
69
+ - `clientName` (Pi 0.99.2): the name sent at client registration (default `pi`), for servers that only accept known clients, e.g. `"Claude Code"`. Sign out first to register again under a new name.
70
+ - `clientRegistration: "cimd"` (Pi 1.0.1): identify as Pi's Client ID Metadata Document instead of registering, for authorization servers that allow clients by document URL. The client ID is `https://pi.dev/oauth/client.json` with redirect `http://127.0.0.1:<port>/callback`; when the authorization server does not send `iss` in authorization responses (RFC 9207), Pi uses a per-server document and path instead: `https://pi.dev/oauth/<id>/client.json` with `/callback/<id>`. The server must advertise CIMD and public-client support, or sign-in fails. `cimd` cannot be combined with `clientId` or `clientName`, and a `callbackUrl` must use `localhost` or `127.0.0.1` with the path `/callback`.
71
+ - `authServerMetadataUrl` (Pi 1.0.0): the authorization server metadata document to use instead of discovery, for servers that advertise a wrong authorization server or none. Trusted as configured; HTTPS except on loopback.
72
+ - Pi rejects an authorization response whose `iss` names another authorization server (RFC 9207), and step-up sign-in after `insufficient_scope` keeps the scopes already granted (Pi 1.0.0).
73
+ - `"auth": { "provider": "<provider>" }` (Pi 0.99.2) sends a `/login` provider's current token as the bearer token instead of MCP OAuth, read on every request. Allowed only in the global `mcp.json` and from extensions, and HTTPS except on loopback. After a Radius sign-in, `/login` offers to add the Radius MCP server this way.
59
74
 
60
75
  ## Exposure
61
76
 
@@ -63,8 +78,7 @@ Pi connects on session start; the first prompt waits up to 10 seconds for startu
63
78
 
64
79
  | Value | Declared to the model | Callable from codemode | Notes |
65
80
  |-------|----------------------|------------------------|-------|
66
- | `codemode` (default) | No | Yes, listed in the `codemode` description | Pi activates `codemode` when such a server connects |
67
- | `codemode-deferred` | No | Yes, found with `searchTools()` / `ALL_TOOLS` | Description names only the server and tool count; for large, rarely used servers |
81
+ | `codemode` (default) | No | Yes, found with `searchTools()`, `describeNamespace()`, or `ALL_TOOLS`; not listed in the `codemode` description | Pi activates `codemode` when such a server connects |
68
82
  | `deferred` | After `tool_search` loads them | Yes | Pi activates `tool_search`; for large servers without codemode |
69
83
  | `direct` | Yes, like built-in tools | Yes | |
70
84
  | `hidden` | No | No | Registered but unreachable |
@@ -83,12 +97,14 @@ Pi connects on session start; the first prompt waits up to 10 seconds for startu
83
97
  }
84
98
  ```
85
99
 
86
- Undeclared tools (`codemode`, `codemode-deferred`, `deferred`) are reachable through either tool: codemode scripts can call a `deferred` server's tools, and `tool_search` can load a `codemode` server's tools. Set top-level `"autoEnableCodemode": false` in `mcp.json` to stop Pi from activating `codemode`. Pi warns once when neither `codemode` nor `tool_search` is active, because the tools are then unreachable.
100
+ `codemode-deferred` is an alias for `codemode` since Pi 0.99.2. Servers with `codemode` or `deferred` tools are listed in an `mcp_servers` system prompt section with how their tools are reached and a one-line summary. Pi updates it when a prompt starts and appends a changed section to the conversation, so neither tool descriptions nor the cached prefix change when servers connect.
101
+
102
+ Undeclared tools (`codemode`, `deferred`) are reachable through either tool: codemode scripts can call a `deferred` server's tools, and `tool_search` can load a `codemode` server's tools. Set top-level `"autoEnableCodemode": false` in `mcp.json` to stop Pi from activating `codemode`. Pi warns once when neither `codemode` nor `tool_search` is active, because the tools are then unreachable.
87
103
 
88
104
  ## Results
89
105
 
90
106
  - Text results over 20KB reach the model with the middle cut out; the full text goes to a temp file named in the result.
91
- - Codemode scripts receive the whole `CallToolResult` (`content`, `structuredContent`, `isError`). A result with `isError` resolves in scripts and is reported to the model as an error for direct calls. `image(result.content[0])` forwards an image block.
107
+ - Codemode scripts receive the whole `CallToolResult` (`content`, `structuredContent`, `isError`). A result with `isError` resolves in scripts and is reported to the model as an error for direct calls. `image(result.content[0])` forwards an image block. Server instructions are not part of any tool description; scripts read them with `describeNamespace("mcp__<server>")`.
92
108
  - Servers with resources add `list_mcp_resources`, `list_mcp_resource_templates`, and `read_mcp_resource` (marked read-only).
93
109
  - Tool calls are never retried (the server may have run them); resource reads retry once on 408/429/5xx.
94
110
 
@@ -99,7 +115,7 @@ Every MCP call goes through Pi's tool pipeline, so `tool_call` and `tool_result`
99
115
  ## Servers From Extensions
100
116
 
101
117
  ```typescript
102
- pi.registerMcpServer("jira", { url: "https://mcp.example.com/jira", exposure: "codemode" });
118
+ pi.registerMcpServer("jira", { url: "https://mcp.example.com/jira", exposure: "codemode", description: "Jira issues" });
103
119
  pi.unregisterMcpServer("jira");
104
120
  ```
105
121
 
@@ -117,6 +133,6 @@ pi.unregisterMcpServer("jira");
117
133
 
118
134
  ## SDK
119
135
 
120
- SDK sessions do not load built-in extensions. Add `createMcpExtension()`, plus `createCodemodeExtension()` for `codemode`/`codemode-deferred` servers and `createToolSearchExtension()` for `deferred` servers, then call `session.bindExtensions()` so `session_start` connects the servers. See `references/sdk.md`.
136
+ SDK sessions do not load built-in extensions. Add `createMcpExtension()`, plus `createCodemodeExtension()` for `codemode` servers and `createToolSearchExtension()` for `deferred` servers, then call `session.bindExtensions()` so `session_start` connects the servers. See `references/sdk.md`.
121
137
 
122
- The standalone client is published as `@earendil-works/pi-mcp` (stdio and streamable HTTP transports, OAuth, an in-memory testing transport). It has no Pi session dependency.
138
+ The standalone client is published as `@earendil-works/pi-mcp` (stdio and streamable HTTP transports, OAuth, an in-memory testing transport). It has no Pi session dependency. Breaking in 1.0.1: `OAuthClientProvider.clientMetadataUrl` became `clientMetadataDocument(metadata)`, which returns `{ url, redirectUrl }` for an authorization server (its metadata may be `undefined`), or `undefined` to register dynamically. It is called whenever no client information is stored, also when the server does not advertise CIMD support, so check support yourself; the document is no longer stored as client information. `McpOAuthProviderOptions.clientMetadataDocument`, `OAuthCallbackServerOptions.extraPaths`, and a `path` argument to `OAuthCallbackServer.waitForCallback()` (which rejects responses on other paths) support per-server redirect paths.
@@ -16,7 +16,7 @@ pi install ./relative/path/to/package
16
16
  pi remove npm:@foo/bar
17
17
  pi uninstall npm:@foo/bar
18
18
  pi list
19
- pi update # update pi only (changed in Pi 0.79.7)
19
+ pi update # update pi only (changed in Pi 0.79.7; not for Nix installs)
20
20
  pi update --all # update pi + packages, reconcile pinned git refs
21
21
  pi update --extensions # update packages only
22
22
  pi update --models # refresh model catalogs only (Pi 0.80.8)
@@ -28,6 +28,8 @@ pi config -l # start in project overrides (.pi/settings.json)
28
28
 
29
29
  By default, `install` and `remove` write to user settings (`~/.pi/agent/settings.json`). Use `-l` to write to project settings (`.pi/settings.json`) instead. `pi update` installs the exact version returned by the update check.
30
30
 
31
+ Pi itself (Pi 1.0.1): the pi.dev installer pins all dependencies and `pi update` updates it. npm installs no longer pin transitive dependencies (the published `npm-shrinkwrap.json` is gone), and `pi update` on a global npm install recommends moving to the installer. Nix users run `nix profile add github:earendil-works/pi/stable` and update with `nix profile upgrade pi`; `pi update` cannot update a Nix install.
32
+
31
33
  Package commands follow the project trust flow (`pi update` never prompts); pass `--approve`/`-a` or `--no-approve`/`-na` to trust or ignore project-local settings for one command.
32
34
 
33
35
  Project installs are shareable with a team. Pi will install missing project packages automatically on startup once the project is trusted.
@@ -94,7 +96,7 @@ If there is no `pi` manifest, Pi auto-discovers:
94
96
 
95
97
  ## Dependencies
96
98
 
97
- Checked against Pi v0.99.1 [packages.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/packages.md#declare-dependencies).
99
+ Checked against Pi v1.0.2 [packages.md](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/packages.md#declare-dependencies).
98
100
 
99
101
  - Normal runtime dependencies belong in `dependencies`
100
102
  - Pi supplies these to extensions; declare them as peer deps with `"*"` ranges and never bundle them:
@@ -108,7 +110,7 @@ Checked against Pi v0.99.1 [packages.md](https://github.com/earendil-works/pi/bl
108
110
  - Git package installs omit dev dependencies — runtime needs go in `dependencies`, not `devDependencies`
109
111
  - Installed packages load with separate module roots. Do not rely on two packages sharing one dependency instance, or on one package resolving another package's undeclared dependency.
110
112
  - Other Pi packages used as dependencies must be included in the published tarball and referenced through their `node_modules` resource paths.
111
- - Pin dev dependencies to the Pi line you test against (e.g. `^0.99.1`) so typecheck/tests validate against what users run; keep peers `"*"` so installs stay compatible across Pi versions. `"*"` does not prove compatibility: document the minimum supported Pi version and feature-check newer APIs (for example `ctx.executeTool`, `pi.registerMcpServer`) if you support older hosts.
113
+ - Pin dev dependencies to the Pi line you test against (e.g. `^1.0.2`) so typecheck/tests validate against what users run; keep peers `"*"` so installs stay compatible across Pi versions. `"*"` does not prove compatibility: document the minimum supported Pi version and feature-check newer APIs (for example `ctx.executeTool`, `pi.registerMcpServer`) if you support older hosts.
112
114
 
113
115
  ## Package Filtering
114
116
 
@@ -23,7 +23,7 @@ Practical recipes for extending Pi. Each pattern is a self-contained example.
23
23
  17. [Nested Tool Calls](#nested-tool-calls)
24
24
  18. [Classifier Gate](#classifier-gate)
25
25
 
26
- Patterns 16–18 are full files under `examples/` that type-check against Pi 0.99.1 in this skill's package; read them rather than retyping.
26
+ Patterns 16–18 are full files under `examples/` that type-check against Pi 1.0.2 in this skill's package; read them rather than retyping.
27
27
 
28
28
  ---
29
29
 
@@ -252,7 +252,7 @@ Pi's TUI library (`@earendil-works/pi-tui`) provides:
252
252
  - `Container` — Vertical grouping
253
253
  - `Spacer` — Vertical space
254
254
  - `Markdown` — Rendered markdown
255
- - `Image` — Terminal image display
255
+ - `Image` — Terminal image display. Kitty-protocol terminals need PNG; since Pi 1.0.1 standalone pi-tui apps call `setImageTranscoder((base64Data, mimeType) => png | null)` to convert JPEG/GIF/WebP, and `Image` shows its text fallback without one or when conversion fails. Pi's interactive host registers a transcoder itself.
256
256
  - `SelectList` — Interactive selection list
257
257
  - `BorderedLoader` — Async spinner with cancel
258
258
 
@@ -4,15 +4,15 @@ Pi supports subscription-based providers via OAuth, API-key providers via env va
4
4
 
5
5
  ## Subscription Providers
6
6
 
7
- Use `/login` in interactive mode, then select a provider. The `/login` selector is fuzzy-searchable and shows where each entry's auth comes from (`--api-key`, env var, custom provider) without leaking the secret.
7
+ Use `/login` in interactive mode, then select a provider. On OAuth sign-in screens (`/login`, `/mcp`, `/mcp login`), `app.message.copy` (default `ctrl+x`, Pi 1.0.1) copies the sign-in URL when the browser cannot open or the wrapped link cannot be selected. The `/login` selector is fuzzy-searchable and shows where each entry's auth comes from (`--api-key`, env var, custom provider) without leaking the secret.
8
8
 
9
- - Anthropic Claude Pro / Max — third-party usage draws from extra usage and is billed per token (suppress the warning via `warnings.anthropicExtraUsage`)
9
+ - Anthropic Claude Pro / Max — third-party usage draws from extra usage and is billed per token (suppress the warning via `warnings.anthropicExtraUsage`). Since Pi 1.0.0 `/login anthropic` offers browser login (default) or **copy code** login, which shows the authorization code on Anthropic's page for pasting into Pi when the browser runs on another machine
10
10
  - OpenAI with a ChatGPT subscription (Pi 0.99.0) — `/login openai` offers **Sign in with ChatGPT**, an OAuth login that uses the subscription with the OpenAI API on the regular `openai` provider. Pi stores a stable `deviceId` for it in global settings. Subscription usage-limit errors are not retried
11
11
  - OpenAI Codex (legacy) — the older `openai-codex` provider, renamed in Pi 0.99.0 and superseded by Sign in with ChatGPT. Its `/login` still offers browser auth or a device-code flow; its default model is `gpt-6.1-sol` (Pi 0.99.1). Prefer Sign in with ChatGPT on `openai` for new setups
12
12
  - GitHub Copilot
13
13
  - OpenRouter (Pi 0.82.0) — `/login openrouter` runs a PKCE flow that mints a user-controlled API key billed from OpenRouter credits; on headless/SSH hosts paste the redirect URL or authorization code into the prompt (Pi 0.83.0). `OPENROUTER_API_KEY` still works via **Use an API key**
14
14
  - xAI (Grok/X subscription, Pi 0.80.8) — `/login xai` then **Use a subscription** (device-code OAuth); `XAI_API_KEY` remains available via **Use an API key**
15
- - Radius (Pi 0.80.8) — a dynamic `pi-messages` gateway; `/login radius` stores OAuth tokens, and custom Radius gateways can be declared in `models.json` with `"oauth": "radius"` plus a gateway `baseUrl`. Since Pi 0.86.0 a public Radius catalog ships for immediate/offline selection (cached and live gateway catalogs overlay it); custom gateways use their own catalog
15
+ - Radius (Pi 0.80.8) — a dynamic `pi-messages` gateway; `/login radius` stores OAuth tokens, and custom Radius gateways can be declared in `models.json` with `"oauth": "radius"` plus a gateway `baseUrl`. Since Pi 0.86.0 a public Radius catalog ships for immediate/offline selection (cached and live gateway catalogs overlay it); custom gateways use their own catalog. Since Pi 1.0.0 `/login` lists **Sign in with Radius** at the top level and, after sign-in, offers to add the Radius MCP server to the global `mcp.json` with `"auth": { "provider": "radius" }`
16
16
  - Meta Muse (Pi 0.86.1) — `/login meta` with automatic Model API key refresh; `META_API_KEY` also works
17
17
 
18
18
  `/login <provider>` with autocomplete works since Pi 0.80.4, and login methods are provider-owned since 0.80.8 (registered pi-ai providers expose their own auth options and status in `/login`). Use `/logout` to clear stored OAuth credentials. Pi 0.71.0 removed built-in Google Gemini CLI and Google Antigravity providers. `pi auth check [provider|model]` (Pi 0.84.1) preflights credential readiness; `pi auth print-api-key` / `pi auth print-bearer-token` (Pi 0.83.0) export resolved credentials to external clients, refreshing OAuth as needed.
@@ -58,6 +58,10 @@ Since Pi 0.80.8, built-in catalogs are complemented by dynamic ones: `/model` re
58
58
  | Xiaomi MiMo | `XIAOMI_API_KEY` | `xiaomi` |
59
59
  | Xiaomi MiMo Token Plan (CN/AMS/SGP) | `XIAOMI_TOKEN_PLAN_{CN,AMS,SGP}_API_KEY` | `xiaomi-token-plan-{cn,ams,sgp}` |
60
60
 
61
+ With no Anthropic key or token set, Pi 0.99.2+ uses Anthropic workload identity federation when `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, and `ANTHROPIC_IDENTITY_TOKEN_FILE` are set (plus optional `ANTHROPIC_SERVICE_ACCOUNT_ID` and `ANTHROPIC_WORKSPACE_ID`). The Anthropic SDK exchanges and refreshes the token, re-reading the identity token file, so keep that file fresh for long sessions.
62
+
63
+ Model IDs that changed in Pi 1.0.1, for explicit `--model`, `enabledModels`, and `modelOverrides` entries: Cloudflare AI Gateway Claude models use dashed IDs (`claude-opus-5-5`, not `claude-opus-5.5`); Together's DeepSeek V4 Pro is `deepseek-ai/DeepSeek-V4-Pro-0813`; NVIDIA's default is `nvidia/nemotron-3-ultra-550b-a55b`, since NVIDIA no longer serves `nvidia/nemotron-3-super-120b-a12b`.
64
+
61
65
  ## Auth File
62
66
 
63
67
  `~/.pi/agent/auth.json` stores API keys and OAuth tokens with `0600` permissions:
@@ -95,7 +99,7 @@ Entries may include an `env` object (Pi 0.79.5) for provider-scoped environment
95
99
 
96
100
  Provider extensions can define their own authentication behavior.
97
101
 
98
- ## Cloud Providers
102
+ ## Provider-Specific Setup
99
103
 
100
104
  ### Azure OpenAI
101
105
 
@@ -199,10 +203,10 @@ Common `api` values:
199
203
 
200
204
  ### Model & Compatibility Knobs
201
205
 
202
- - `samplingParams` (Pi 0.84.0) is a free-form object merged verbatim into every request body after pi's own fields (its keys win) — for OpenAI-compatible APIs only (`openai-completions`, `openai-responses`, `azure-openai-responses`). Use it for server-specific knobs like llama.cpp `min_p` or vLLM `top_k`; in `modelOverrides` it merges per key.
206
+ - `samplingParams` (Pi 0.84.0) is a free-form object merged verbatim into every request body after pi's own fields (its keys win) — for OpenAI-compatible APIs only (`openai-completions`, `openai-responses`, `azure-openai-responses`). Use it for server-specific knobs like llama.cpp `min_p` or vLLM `top_k`; in `modelOverrides` it merges per key. `samplingParamsByThinkingLevel` (Pi 1.0.2) overrides it per Pi thinking level (`off` … `max`, not `thinkingLevelMap` values): Pi clamps the level, then merges model `samplingParams`, the level's entry, and request-level `samplingParams`, later keys winning. Missing levels inherit the model defaults, and `modelOverrides` merges each level's entry per key with the base model.
203
207
  - `thinkingLevelMap` (Pi 0.72) replaces `compat.reasoningEffortMap`. Map pi levels (`off`/`minimal`/`low`/`medium`/`high`/`xhigh`/`max`) to provider values; use `null` to hide a level. Maps may contain holes (Pi 0.80.6) — e.g. expose `high` and `max` without `xhigh`. When a key is omitted, standard levels through `high` use the provider default mapping, but the extended `xhigh`/`max` levels are unsupported.
204
208
  - `cost` supports request-wide input pricing tiers (Pi 0.80.6): a `tiers` array where each tier supplies a complete alternate rate set and applies to the whole request when total input usage (`input + cacheRead + cacheWrite`) exceeds `inputTokensAbove`; the highest matching threshold wins. Also usable in `modelOverrides` and extension-registered providers.
205
- - `modelOverrides` applies to built-in and (since Pi 0.80.4) extension-registered provider models; per-model fields: `name`, `reasoning`, `thinkingLevelMap`, `input`, `cost` (partial), `contextWindow`, `maxTokens`, `samplingParams`, `headers`, `compat`, `inputLimits`, `promptCache`.
209
+ - `modelOverrides` applies to built-in and (since Pi 0.80.4) extension-registered provider models; per-model fields: `name`, `reasoning`, `thinkingLevelMap`, `input`, `cost` (partial), `contextWindow`, `maxTokens`, `samplingParams`, `samplingParamsByThinkingLevel` (Pi 1.0.2), `headers`, `compat`, `inputLimits`, `promptCache`.
206
210
  - `inputLimits.images.resize` (Pi 0.87.0) sets per-model cache-safe image encoding — `maxWidth`, `maxHeight`, `maxBytes` (base64 payload), `jpegQuality`; defaults 2000×2000, 4.5 MiB, quality 80 — applied once to attachments, `read` images, and tool-result images (switching models does not re-encode history).
207
211
  - `promptCache: { short?, long? }` (Pi 0.86.0) declares best-effort cache lifetimes in seconds per retention tier; models without a lifetime for the active tier are not eligible for cache warming (see `cacheWarming` in `references/settings.md`).
208
212
  - `compat.allowedFallbackModels` (Pi 0.86.0) overrides or disables Anthropic server-side fallback models; `compat.supportsMidConvoEffort` (Pi 0.84.4) marks custom Anthropic Messages models that accept per-turn effort changes.
@@ -211,7 +215,7 @@ Common `api` values:
211
215
  - `compat.thinkingFormat` supports OpenAI-compatible reasoning variants: `openrouter` sends `reasoning: { effort }`, `together` sends `reasoning: { enabled }` plus `reasoning_effort` when supported, `qwen-chat-template` targets local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking`, `chat-template` (Pi 0.79.9) sends configurable `chat_template_kwargs` via `compat.chatTemplateKwargs` — e.g. `{ "thinking": { "$var": "thinking.enabled" } }` for DeepSeek models behind vLLM/Hugging Face chat templates — and `baseten` (Pi 0.84.0) sends `chat_template_args` via `compat.chatTemplateArgs`.
212
216
  - **Breaking (Pi 0.80.7):** `compat.sendSessionIdHeader` was removed. Session affinity is now controlled by `compat.sessionAffinityFormat` (`"openai"` sends `session_id`/`x-client-request-id`, `"openai-nosession"` omits the underscore-containing `session_id` header, `"openrouter"` sends `x-session-id`; default auto-detected). Replace `sendSessionIdHeader: false` with `sessionAffinityFormat: "openai-nosession"`. `sendSessionAffinityHeaders` still gates the behavior for `openai-completions`.
213
217
  - `compat.deferredToolsMode: "kimi"` (Pi 0.80.9) enables Kimi's deferred tool serialization; `compat.supportsToolReferences` / `compat.supportsToolSearch` enable native dynamic tool loading on verified custom endpoints, and `compat.supportsStrictTools` / `compat.supportsOpenAIGrammarTools` / per-model `constrainedSampling` gate constrained sampling (see `references/extensions.md`). `compat.supportsFinishReason: false` (Pi 0.84.0) tells pi to infer stop/toolUse for OpenAI-compatible streams that omit `finish_reason`.
214
- - Advanced `compat` flags exist for proxy quirks (`cacheControlFormat` — now also covering tool-result text content, `supportsReasoningEffort`, `supportsLongCacheRetention`, `supportsEagerToolInputStreaming`, `supportsStrictMode`). See [Pi `docs/models.md`](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/models.md) when a proxy rejects pi's defaults.
218
+ - Advanced `compat` flags exist for proxy quirks (`cacheControlFormat` — now also covering tool-result text content, `supportsReasoningEffort`, `supportsLongCacheRetention`, `supportsEagerToolInputStreaming`, `supportsStrictMode`). See [Pi `docs/models.md`](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/models.md) when a proxy rejects pi's defaults.
215
219
 
216
220
  ### Context Overflow Recovery
217
221
 
@@ -219,11 +223,11 @@ Pi can auto-compact and retry when a provider fails with a recognized context-wi
219
223
 
220
224
  ### llama.cpp
221
225
 
222
- Pi supports the llama.cpp router server (Pi 0.81.0): configure with `/login llama.cpp`, search/download Hugging Face models and load/unload with live progress via `/llama`, then select loaded models in `/model`. The model catalog persists across restarts (fixed in Pi 0.82.1). Every llama.cpp chat model is also listed as a classifier model (see [Classifier Models](#classifier-models)). `"extensions": ["-builtin:llama.cpp"]` removes the provider and `/llama`. See [Pi `docs/llama-cpp.md`](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/llama-cpp.md).
226
+ Pi supports the llama.cpp router server (Pi 0.81.0): configure with `/login llama.cpp`, search/download Hugging Face models and load/unload with live progress via `/llama`, then select loaded models in `/model`. The model catalog persists across restarts (fixed in Pi 0.82.1). Every llama.cpp chat model is also listed as a classifier model (see [Classifier Models](#classifier-models)). `"extensions": ["-builtin:llama.cpp"]` removes the provider and `/llama`. See [Pi `docs/llama-cpp.md`](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/llama-cpp.md).
223
227
 
224
228
  ## Model Types
225
229
 
226
- Since Pi 0.99.0 catalogs hold typed models: `type` is `"chat"` (the default when omitted), `"image"`, or `"classifier"`. Chat-facing reads (`getModels()`, `getAvailable()`, `ctx.modelRegistry.getAll()`, `/model`) stay chat-only. `ModelRuntime`, `ctx.modelRegistry`, and pi-ai `Models` all have `getModelsOfType(type, provider?)`, `getModelOfType(type, provider, id)`, and async `getAvailableOfType(type, provider?)`. Only `ModelRuntime` and `Models` have `getAllModels()` and `getAllAvailable()` for every type; `ctx.modelRegistry` adds `findOfType(type, provider, id)` instead. pi-ai exports `isModelType()` and `getModelType()` for mixed lists. One upstream ID may have separate chat and image entries. Sources: [models.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/models.md#use-classifier-models), [pi-ai types](https://github.com/earendil-works/pi/blob/v0.99.1/packages/ai/src/types.ts).
230
+ Since Pi 0.99.0 catalogs hold typed models: `type` is `"chat"` (the default when omitted), `"image"`, or `"classifier"`. Chat-facing reads (`getModels()`, `getAvailable()`, `ctx.modelRegistry.getAll()`, `/model`) stay chat-only. `ModelRuntime`, `ctx.modelRegistry`, and pi-ai `Models` all have `getModelsOfType(type, provider?)`, `getModelOfType(type, provider, id)`, and async `getAvailableOfType(type, provider?)`. Only `ModelRuntime` and `Models` have `getAllModels()` and `getAllAvailable()` for every type; `ctx.modelRegistry` adds `findOfType(type, provider, id)` instead. pi-ai exports `isModelType()` and `getModelType()` for mixed lists. One upstream ID may have separate chat and image entries. Sources: [models.md](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/models.md#use-classifier-models), [pi-ai types](https://github.com/earendil-works/pi/blob/v1.0.2/packages/ai/src/types.ts).
227
231
 
228
232
  ### Classifier Models
229
233
 
@@ -233,7 +237,7 @@ Classifiers do not chat. They answer typed questions about JSON state with proba
233
237
  |----------|-----------|------|
234
238
  | `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
235
239
  | `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or `/login` |
236
- | `cloudflare-workers-ai` | `typesafe/jev` | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` |
240
+ | `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` (Clef, Pi 1.0.1) | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` (or the credential's `env` in `auth.json`) |
237
241
  | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
238
242
  | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
239
243
  | llama.cpp router | every loaded chat model ID | `/login llama.cpp` |
@@ -270,13 +274,13 @@ A gate that fails closed on classifier errors is in `examples/classifier-gate.ts
270
274
 
271
275
  ### Image Generation
272
276
 
273
- `ModelRuntime.generateImages(model, { input }, options?)` (Pi 0.99.0) generates images with runtime-resolved auth; list image models with `getModelsOfType("image")`. OpenRouter image models appear under the `openrouter` provider with its credential. Like `classify()`, it returns an error result rather than rejecting.
277
+ `ModelRuntime.generateImages(model, { input }, options?)` (Pi 0.99.0) generates images with runtime-resolved auth; list image models with `getModelsOfType("image")`. OpenRouter image models such as `google/gemini-2.5-flash-image` appear under the `openrouter` provider with its credential. Like `classify()`, it returns an error result rather than rejecting. Image models do not appear in `/model`: extensions call `ctx.modelRegistry.generateImages()` and codemode scripts call `models.generateImages()` (both Pi 1.0.0). Codemode adds a script's image usage to its result; an extension tool must report the returned `usage` on its own tool result, combined across calls. `input` takes text blocks plus `{ type: "image", data, mimeType }` blocks to edit or use as references; output images are base64 blocks and are not saved to disk.
274
278
 
275
279
  In pi-ai, image models live in the same catalog as chat models: `builtinModels()` includes them, `models.getModelOfType("image", provider, id)` finds one (type `ImageModel`), `models.generateImages()` runs it, and `createProvider({ models, images })` registers image implementations. The separate `*Images*` API was removed in pi-ai 0.99.0.
276
280
 
277
281
  ## Virtual Models
278
282
 
279
- A virtual model (experimental, Pi 0.99.0) is a selectable entry that routes each request to a physical model. It is for routing by task, cost, or conversation state, not for permission decisions. Source: [virtual-models.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/virtual-models.md), [`jev-router.ts`](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/examples/extensions/jev-router.ts).
283
+ A virtual model (experimental, Pi 0.99.0) is a selectable entry that routes each request to a physical model. It is for routing by task, cost, or conversation state, not for permission decisions. Source: [virtual-models.md](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/virtual-models.md), [`jev-router.ts`](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/examples/extensions/jev-router.ts).
280
284
 
281
285
  ```typescript
282
286
  pi.registerVirtualModel({
@@ -307,7 +311,7 @@ pi.registerVirtualModel({
307
311
 
308
312
  Use extensions when you need custom streaming, custom headers, OAuth/device-flow logic, or want to override an existing provider's `baseUrl`/`headers`:
309
313
 
310
- A config-form `models` list replaces the provider's models across chat, image, and classifier operations; image and classifier entries need an explicit `type` and implementations keyed by `api` through `images` and `classifiers`. Registering only `baseUrl` or `headers` keeps the built-in models. See [custom-provider.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/custom-provider.md).
314
+ A config-form `models` list replaces the provider's models across chat, image, and classifier operations; image and classifier entries need an explicit `type` and implementations keyed by `api` through `images` and `classifiers`. Registering only `baseUrl` or `headers` keeps the built-in models. See [custom-provider.md](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/custom-provider.md).
311
315
 
312
316
  ```typescript
313
317
  // Full registration
@@ -38,7 +38,7 @@ try {
38
38
  }
39
39
  ```
40
40
 
41
- Run with `npx tsx my-script.ts` (or Node's built-in type stripping). `prompt()` resolves when the run finishes, including automatic retries. `session.dispose()` aborts active work, invalidates extension contexts, and removes listeners. Checked against Pi v0.99.1: [sdk.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/sdk.md), [SDK examples](https://github.com/earendil-works/pi/tree/v0.99.1/packages/coding-agent/examples/sdk).
41
+ Run with `npx tsx my-script.ts` (or Node's built-in type stripping). `prompt()` resolves when the run finishes, including automatic retries. `session.dispose()` aborts active work, invalidates extension contexts, and removes listeners. Checked against Pi v1.0.2: [sdk.md](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/sdk.md), [SDK examples](https://github.com/earendil-works/pi/tree/v1.0.2/packages/coding-agent/examples/sdk).
42
42
 
43
43
  **Breaking (Pi 0.80.8):** `ModelRuntime` replaced the old `AuthStorage`/`ModelRegistry` pair as the SDK model/auth facade. `CreateAgentSessionOptions.authStorage` and `modelRegistry` are gone — pass the async `modelRuntime` instead. `AuthStorage` is no longer exported; use `ModelRuntime` (or a custom pi-ai `CredentialStore`), or `readStoredCredential()` for one-off reads of `auth.json`. `ModelRegistry` still exists only as the synchronous extension-facing compatibility facade, and its `refresh()` is now `Promise<void>`.
44
44
 
@@ -56,6 +56,8 @@ import {
56
56
  import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all";
57
57
  ```
58
58
 
59
+ **Lightweight model construction (pi-ai 0.99.2):** `@earendil-works/pi-ai/models` builds model collections and providers without loading TypeBox, the built-in catalogs, or provider SDKs; use it in startup-sensitive code that brings its own models.
60
+
59
61
  **pi-ai 0.80.0 API move:** the old global API (`getModel`, `getModels`, `getProviders`, `stream`, `complete`, `completeSimple`, `registerApiProvider`, `getEnvApiKey`, ...) moved off the `@earendil-works/pi-ai` root entrypoint to `@earendil-works/pi-ai/compat` (a deprecated shim slated for removal). For built-in model lookup use `getBuiltinModel(provider, modelId)` from `@earendil-works/pi-ai/providers/all`, or the provider-factory API (`createModels()` / `Models.getModel()`). Extensions loaded by Pi are unaffected at runtime — the extension loader aliases the pi-ai root to the compat superset — but standalone SDK scripts and typechecked extension sources must use the new paths. The selective `@earendil-works/pi-ai/base` / `@earendil-works/pi-agent-core/base` entrypoints introduced in 0.79.8 were removed again in 0.80.0.
60
62
 
61
63
  ## Core Options
@@ -94,7 +96,7 @@ Notes:
94
96
 
95
97
  ## Built-in Extensions: Codemode, Tool Search, MCP
96
98
 
97
- The CLI loads `codemode`, `tool_search`, MCP, and llama.cpp as built-in extensions. SDK sessions load none of them. Opt in through the resource loader (Pi 0.99.0, [example](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/examples/sdk/14-codemode-mcp.ts)):
99
+ The CLI loads `codemode`, `tool_search`, MCP, and llama.cpp as built-in extensions. SDK sessions load none of them. Opt in through the resource loader (Pi 0.99.0, [example](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/examples/sdk/14-codemode-mcp.ts)):
98
100
 
99
101
  ```typescript
100
102
  import {
@@ -321,7 +323,7 @@ Pi 0.84.0 added transport-neutral remote-session client APIs: `@earendil-works/p
321
323
 
322
324
  ## RPC Mode
323
325
 
324
- `pi --mode rpc` speaks newline-delimited JSON over stdio ([rpc.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/rpc.md), [rpc-commands.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/rpc-commands.md)). Additions since Pi 0.79:
326
+ `pi --mode rpc` speaks newline-delimited JSON over stdio ([rpc.md](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/rpc.md), [rpc-commands.md](https://github.com/earendil-works/pi/blob/v1.0.2/packages/coding-agent/docs/rpc-commands.md)). Additions since Pi 0.79:
325
327
 
326
328
  - Input dispositions (Pi 0.99.0): a successful `prompt` response carries `data.disposition` — `"started"` (a run started), `"queued"` (queued during a run), or `"handled"` (an extension command or input handler consumed it). `steer` and `follow_up` responses carry `"queued"` or `"handled"`. On `"handled"` no run started for that input: do not wait for `agent_settled`. Otherwise keep consuming events and wait for `agent_settled`, not `agent_end`. Failures after acceptance arrive as events, never as a second response. `RpcClient.prompt(message, images?, streamingBehavior?)`, `steer()`, and `followUp()` return the disposition.
327
329
  - `agent_settled` event (Pi 0.80.4) fires when a run is fully settled — no automatic retry, compaction retry, or queued continuation remains; `agent_end` now carries `willRetry`. `set_thinking_level` accepts `"max"` where the model supports it.
@@ -51,6 +51,7 @@ Edit JSON directly or use `/settings` for common interactive options.
51
51
  }
52
52
  ```
53
53
 
54
+ - `quietStartup`: `true` hides the startup header and loaded-resource listing; `"header"` (Pi 1.0.0) keeps the header (version and key hints) but hides the model scope line and resource listing
54
55
  - `doubleEscapeAction`: `"tree"`, `"fork"`, or `"none"`
55
56
  - `treeFilterMode`: `"default"`, `"no-tools"`, `"user-only"`, `"labeled-only"`, or `"all"`
56
57
  - In `/tree`, `Shift+T` toggles timestamps on entry labels
@@ -60,11 +61,11 @@ Edit JSON directly or use `/settings` for common interactive options.
60
61
 
61
62
  ## Fullscreen TUI Mode
62
63
 
63
- Experimental since Pi 0.84.0:
64
+ Fullscreen is the default since Pi 1.0.0 (added in 0.84.0):
64
65
 
65
66
  ```json
66
67
  {
67
- "tuiMode": "regular",
68
+ "tuiMode": "fullscreen",
68
69
  "fullscreenExitOutput": "transcript",
69
70
  "fullscreenScrollbar": "auto",
70
71
  "fullscreenCopyOnSelect": true,
@@ -72,7 +73,7 @@ Experimental since Pi 0.84.0:
72
73
  }
73
74
  ```
74
75
 
75
- - `tuiMode`: `"regular"` (default) or `"fullscreen"`; `/settings` switches at runtime, `--tui-mode` overrides per run
76
+ - `tuiMode`: `"fullscreen"` (default since Pi 1.0.0) or `"regular"` for the terminal's normal scrollback; `/settings` switches at runtime, `--tui-mode` overrides per run. Scripts that capture Pi's screen (VHS, tmux) see the fullscreen layout unless they set `regular`
76
77
  - `fullscreenExitOutput` (Pi 0.84.2): `"transcript"` prints the final transcript on exit; `"resume-hint"` restores the previous screen and prints only a resume hint
77
78
  - `fullscreenScrollbar`: `"auto"` shows while scrolling, `"always"` reserves the rightmost column, `"hidden"`
78
79
  - `fullscreenCopyOnSelect` (Pi 0.84.4, default `true`): when `false`, `Ctrl+X` copies the active selection (falling back to the last assistant message)
@@ -205,6 +206,7 @@ When multiple sources specify a session directory, `--session-dir` takes precede
205
206
 
206
207
  - `defaultTools` picks the tools enabled at startup (default `read`, `bash`, `edit`, `write`; also `grep`, `find`, `ls`, `powershell`, and the built-in extension tools `codemode` and `tool_search`). Plain names replace the defaults. A list of only `+name`/`-name` entries (Pi 0.99.0) changes the inherited selection instead; in a mixed list the plain names form the selection and `+`/`-` apply in order. A project list of only `+`/`-` entries applies on top of the user setting; a project list with a plain name replaces it. An empty array disables built-in tools but keeps extension/SDK tools.
207
208
  - `--tools` replaces the whole selection for one run and does not accept `+`/`-`; `--exclude-tools` filters the result.
209
+ - `/reload` (Pi 0.99.2) enables tools newly added to `defaultTools`. It does not disable tools removed from it or re-enable unchanged tools turned off in the session; `--tools`, `--no-tools`, and `--no-builtin-tools` still win on reload.
208
210
  - `codemode.mode`: `"on"` (default) keeps declared tools declared; `"only"` hides active built-in and extension tools from the model so it reaches them through `codemode`. `codemode.inlineBudget` (default `3000`, estimated tokens) caps tool declarations in the `codemode` description. See `references/codemode.md`.
209
211
  - MCP servers are configured in `~/.pi/agent/mcp.json` and the trust-gated `.pi/mcp.json`, not in `settings.json`. See `references/mcp.md`.
210
212
 
@@ -289,7 +291,7 @@ pi [options] [@files...] [messages...]
289
291
  --export <in> [out] export a session to HTML
290
292
 
291
293
  # model
292
- --provider <name> e.g. anthropic, openai, google
294
+ --provider <name> e.g. anthropic, openai, google; needs --model (Pi 1.0.0)
293
295
  --model <pattern> supports provider/id and optional :<thinking>
294
296
  --api-key <key>
295
297
  --thinking off|minimal|low|medium|high|xhigh|max
@@ -327,7 +329,7 @@ pi [options] [@files...] [messages...]
327
329
  # prompts and misc
328
330
  --system-prompt <text|path> replace default
329
331
  --append-system-prompt <text|path> repeatable; appended with double newlines
330
- --tui-mode regular|fullscreen experimental fullscreen TUI (Pi 0.84.0)
332
+ --tui-mode regular|fullscreen fullscreen is the default (Pi 1.0.0)
331
333
  --use-theme <name[/name]> per-run initial theme, saved settings untouched (Pi 0.84.2)
332
334
  --verbose
333
335
  ```