@counterposition/skill-pi 1.2.0 → 1.4.0

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 1.2.0.
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.4.0.
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": "1.2.0",
3
+ "version": "1.4.0",
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": "^1.2.0",
34
- "@earendil-works/pi-coding-agent": "^1.2.0",
33
+ "@earendil-works/pi-ai": "^1.4.0",
34
+ "@earendil-works/pi-coding-agent": "^1.4.0",
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 1.2.0.
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.4.0.
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 1.2.0.
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.4.0.
11
11
 
12
12
  Pi's philosophy: **adapt Pi to your workflows, not the other way around**.
13
13
 
@@ -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 v1.2.0: [codemode.md](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/codemode.md), [cli.md#tools](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/cli.md#tools), [extensions.md#tool-exposure](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/extensions.md#tool-exposure), [settings.md#tools](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/settings.md#tools), [codemode tool source](https://github.com/earendil-works/pi/blob/v1.2.0/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.4.0: [codemode.md](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/codemode.md), [cli.md#tools](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/cli.md#tools), [extensions.md#tool-exposure](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/extensions.md#tool-exposure), [settings.md#tools](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/settings.md#tools), [codemode tool source](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/src/extensions/codemode/tool.ts).
4
4
 
5
5
  ## Enable
6
6
 
@@ -31,10 +31,11 @@ The script body is an async function: top-level `await` and `return` work.
31
31
  - 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. Since Pi 1.0.3 it also saves each image to a temp file and names the path in the result, so a later turn can copy or move it. `exit()` ends early and keeps output.
32
32
  - `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.
33
33
  - `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`; generation can take minutes, so avoid a short `timeout_ms`.
34
- - `models.classify()` also takes `images` (Pi 1.1.0): image blocks judged with the state, for classifiers whose `input` includes `"image"`, such as `openai/gpt-6-luna`. Other classifiers return an error result. `await tools.read({ path })` on an image file resolves to such a block (Pi 1.0.4).
34
+ - `models.classify()` also takes `images` (Pi 1.1.0): image blocks judged with the state, for classifiers whose `input` includes `"image"`: `openai/gpt-6-luna` and, since Pi 1.4.0, Jev, Clef, and OpenRouter classifiers that list image input (see `references/providers.md`). Other classifiers return an error result. `await tools.read({ path })` on an image file resolves to such a block (Pi 1.0.4).
35
35
  - Malformed `classify()`/`generateImages()` arguments are rejected with the expected shape, and an unknown model points to `models.getAvailableOfType()`.
36
36
  - An optional first line `// @options: {"max_output_tokens": 2000, "timeout_ms": 60000}`. It must be the first line: since Pi 1.2.0 an options line anywhere else fails the call before the script runs. 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.
37
37
  - 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.
38
+ - At most two codemode workers are alive at once across all sessions in one Pi process (Pi 1.3.0); a cancelled worker holds its slot until it exits. Since Pi 1.4.0 further scripts wait for a free worker in arrival order instead of failing, and fail without running after 60 seconds or their `timeout_ms`. `PI_CODEMODE_MAX_WORKERS` raises the limit, for example for in-process subagents.
38
39
 
39
40
  The result starts with `Script completed` or `Script failed`, the wall time, and the output. Since Pi 1.1.0, with more than one text item each starts with a `==> text N/M <==` line, and `console` calls follow the other output in one `<console_output>` block, one line per call. A failed script keeps its partial output followed by `Script error:`.
40
41
 
@@ -94,4 +95,4 @@ For a permission extension this means:
94
95
  - `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.
95
96
  - A replacement should declare `reachesTools` (Pi 1.2.0, see `references/extensions.md`): only then does Pi activate it for MCP servers and wait for the servers its calls need.
96
97
 
97
- `@earendil-works/pi-codemode` publishes the sandbox (`CodemodeSandbox`) without Pi dependencies, for exposing any functions to model-written scripts. Since Pi 1.2.0 its `/portable` entry adds sandboxes with the same API for Cloudflare Workers and other runtimes without worker threads.
98
+ `@earendil-works/pi-codemode` publishes the sandbox (`CodemodeSandbox`) without Pi dependencies, for exposing any functions to model-written scripts. Since Pi 1.2.0 its `/portable` entry adds sandboxes with the same API for Cloudflare Workers and other runtimes without worker threads. Breaking in Pi 1.3.0: results and `close()` settle once shutdown is requested, not when the worker exits; a host that must count workers until they exit uses the `onWorkerStarted(exited)` callback or reserves capacity with `acquireWorker()`.
@@ -25,6 +25,7 @@ import { Text } from "@earendil-works/pi-tui";
25
25
 
26
26
  - Use `typebox` (1.x) for schemas. Pi 0.83.0 bundles TypeBox 1.3.7, which removed deprecated APIs (`Type.Base`, `Type.Awaited`, `Type.Promise`, `Type.AsyncIterator`, `Type.Iterator`, `Type.Options`, `Value.Mutate`) — migrate extension sources off those before typechecking against current Pi.
27
27
  - Use `StringEnum` from `@earendil-works/pi-ai` for Google-compatible string enums.
28
+ - Since Pi 1.4.0 extensions can import `@earendil-works/pi-ai/models` (for example `clampThinkingLevel`); earlier the extension loader could not resolve that subpath.
28
29
  - Pi 0.80.0 moved pi-ai's old global API (`getModel`, `getModels`, `stream`, `complete`, `registerApiProvider`, ...) off the pi-ai root entrypoint to `@earendil-works/pi-ai/compat`. Extensions keep working unchanged at runtime (the extension loader aliases the root to the compat superset), but extension sources that typecheck against pi-ai's published types must import those APIs from `@earendil-works/pi-ai/compat`. `StringEnum` and the type surface remain on the root. The compat entrypoint and loader alias will be removed in a future release.
29
30
 
30
31
  ## Quick Start
@@ -101,7 +102,7 @@ Useful agent events:
101
102
  - `message_end` — return a replacement message to override usage/cost or rewrite the finalized assistant message
102
103
  - `context` — transforms conversation messages; since Pi 0.87.0 handlers no longer see prompt/tool system messages, and Pi restores that state afterward (filtering or slicing messages is safe)
103
104
  - `context_with_system` (Pi 0.87.0) — runs after `context` on the full transcript including system messages and is sent verbatim; use only when a request-local transform must own the whole transcript, and keep a system message at index 0
104
- - `before_provider_headers` (Pi 0.80.4) — mutate `event.headers` in place after outgoing HTTP headers are assembled: set a key to a string to add/override, `null` to delete. Runs once per provider request; retries reuse the same headers
105
+ - `before_provider_headers` (Pi 0.80.4) — mutate `event.headers` in place after outgoing HTTP headers are assembled: set a key to a string to add/override, `null` to delete. Runs once per provider request; retries reuse the same headers. Since Pi 1.4.0 handlers no longer see the OpenRouter, NVIDIA NIM, and OpenCode attribution headers, which pi-ai adds from its client identity; setting one to `null` still removes it
105
106
  - `before_provider_request`
106
107
  - `after_provider_response` — inspect the provider HTTP status and headers before stream consumption
107
108
  - `provider_stream_event` (Pi 0.99.0) — notification-only view of each parsed provider stream event before Pi normalizes it (`provider`, `api`, `model`, `data`). Treat `data` as read-only; handlers are awaited in stream order, so keep them fast. Not persisted
@@ -194,7 +195,7 @@ Important rules:
194
195
 
195
196
  ## Tool Exposure & Structured Results
196
197
 
197
- 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.2.0/packages/coding-agent/docs/extensions.md#tool-exposure), [types](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/src/core/extensions/types.ts)):
198
+ 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.4.0/packages/coding-agent/docs/extensions.md#tool-exposure), [types](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/src/core/extensions/types.ts)):
198
199
 
199
200
  - `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.
200
201
  - `isError: true` on a returned result: the model sees an error, while `details` and `structuredContent` are kept for the UI and scripts.
@@ -217,7 +218,7 @@ Pi 0.80.7 lets an extension register many tools while keeping only a small initi
217
218
  3. During loader execution, call `pi.setActiveTools([...pi.getActiveTools(), ...matches])`. Unknown names are ignored. Additive changes are the most cache-friendly everywhere.
218
219
  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.
219
220
 
220
- Adding tools keeps the cached prompt prefix on models that support native tool changes, which Pi's generated `compat` metadata marks (recent Claude, GPT, and Kimi models). On Anthropic's own API, Opus 4.8+, Sonnet and Haiku 5.5, and Fable 5+ use the `inline-tools-2026-09-15` beta (Pi 1.0.1), so removals and same-name redefinitions keep the prefix too. Any change a model cannot take natively falls back to a checkpoint with the full tool list: activation still works, but may invalidate the cached prefix. pi-ai's `hasToolRedefinitions()` is deprecated.
221
+ Adding tools keeps the cached prompt prefix on models that support native tool changes, which Pi's generated `compat` metadata marks (recent Claude, GPT, and Kimi models). On Anthropic's own API, Opus 4.8+, Sonnet and Haiku 5.5, and Fable 5+ use the `inline-tools-2026-09-15` beta (Pi 1.0.1), so removals and same-name redefinitions keep the prefix too. Since Pi 1.4.0 OpenAI Responses models with `additional_tools` declare every tool in those items and announce removals as text, so removals and redefinitions keep the prefix there as well. Any change a model cannot take natively falls back to a checkpoint with the full tool list: activation still works, but may invalidate the cached prefix. pi-ai's `hasToolRedefinitions()` is deprecated.
221
222
 
222
223
  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.
223
224
 
@@ -250,6 +251,8 @@ Context carries `messageType` (`"user" | "assistant" | "assistant-thinking"`), `
250
251
 
251
252
  Check `ctx.hasUI` first. Non-interactive modes may not support UI.
252
253
 
254
+ In interactive mode, a `select`/`confirm`/`input`/`editor` dialog or inline `custom()` component opened while another is visible shows on top, and closing it shows the previous one again (Pi 1.4.0). Before, the second replaced the first and the first promise never settled, so tools running in parallel could hang on their prompts.
255
+
253
256
  ```typescript
254
257
  const choice = await ctx.ui.select("Pick one:", ["A", "B"]);
255
258
  const ok = await ctx.ui.confirm("Delete?", "This cannot be undone");
@@ -1,6 +1,6 @@
1
1
  # MCP Servers
2
2
 
3
- Pi connects to MCP servers through a built-in extension. There is no package to install. Checked against Pi v1.2.0: [mcp.md](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/mcp.md), [cli.md#mcp-commands](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/cli.md#mcp-commands), [extensions.md#mcp-servers](https://github.com/earendil-works/pi/blob/v1.2.0/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.4.0: [mcp.md](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/mcp.md), [cli.md#mcp-commands](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/cli.md#mcp-commands), [extensions.md#mcp-servers](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/extensions.md#mcp-servers).
4
4
 
5
5
  ## Configure
6
6
 
@@ -96,7 +96,7 @@ If there is no `pi` manifest, Pi auto-discovers:
96
96
 
97
97
  ## Dependencies
98
98
 
99
- Checked against Pi v1.2.0 [packages.md](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/packages.md#declare-dependencies).
99
+ Checked against Pi v1.4.0 [packages.md](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/packages.md#declare-dependencies).
100
100
 
101
101
  - Normal runtime dependencies belong in `dependencies`
102
102
  - Pi supplies these to extensions; declare them as peer deps with `"*"` ranges and never bundle them:
@@ -110,7 +110,7 @@ Checked against Pi v1.2.0 [packages.md](https://github.com/earendil-works/pi/blo
110
110
  - Git package installs omit dev dependencies — runtime needs go in `dependencies`, not `devDependencies`
111
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.
112
112
  - Other Pi packages used as dependencies must be included in the published tarball and referenced through their `node_modules` resource paths.
113
- - Pin dev dependencies to the Pi line you test against (e.g. `^1.2.0`) 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.4.0`) 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.
114
114
 
115
115
  ## Package Filtering
116
116
 
@@ -49,7 +49,7 @@ Since Pi 0.80.8, built-in catalogs are complemented by dynamic ones: `/model` re
49
49
  | Fireworks | `FIREWORKS_API_KEY` | `fireworks` |
50
50
  | Together AI | `TOGETHER_API_KEY` | `together` |
51
51
  | Baseten | `BASETEN_API_KEY` | `baseten` |
52
- | Kimi For Coding | `KIMI_API_KEY` | `kimi-coding` |
52
+ | Kimi For Coding | `KIMI_API_KEY` | `kimi-coding` (Chat Completions since Pi 1.3.0; base URL overrides must include `/v1`) |
53
53
  | Meta (Muse) | `META_API_KEY` | `meta` |
54
54
  | MiniMax | `MINIMAX_API_KEY` | `minimax` |
55
55
  | MiniMax (China) | `MINIMAX_CN_API_KEY` | `minimax-cn` |
@@ -157,7 +157,7 @@ export GOOGLE_CLOUD_LOCATION=us-central1
157
157
 
158
158
  ## Custom Providers via `models.json`
159
159
 
160
- Use `models.json` for OpenAI-compatible, Anthropic-compatible, Google-compatible, or other supported APIs:
160
+ Use `models.json` for OpenAI-compatible, Anthropic-compatible, Google-compatible, or other supported APIs. Every provider, model, override, and `compat` field is listed in [models-configuration.md](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/models-configuration.md) (Pi 1.4.0):
161
161
 
162
162
  ```json
163
163
  {
@@ -211,13 +211,13 @@ Common `api` values:
211
211
  - `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`.
212
212
  - `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).
213
213
  - `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`).
214
- - `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.
214
+ - `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; on `openai-responses` (Pi 1.3.0, default `false`) it sends thinking level changes as `configuration_update` input items so they keep the prompt cache, and Sign in with ChatGPT ignores it.
215
215
  - OpenAI-compatible local servers: `vllmPriority` and `supportsMaxOutputTokens` (Pi 0.85.0) plus configurable thinking-token budget fields for vLLM, Qwen/SGLang, and llama.cpp (Pi 0.84.3).
216
216
  - `openRouterRouting` is forwarded as-is in the OpenRouter `provider` field (fallbacks, ZDR, ignore lists, throughput/latency).
217
217
  - `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`.
218
218
  - **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`.
219
219
  - The `compat.supportsMidConvo*`, `supportsToolSearch`, and `supportsAdditionalTools` flags enable native tool changes; set them only for verified custom endpoints (see `references/extensions.md#dynamic-tool-loading`). `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`.
220
- - 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.2.0/packages/coding-agent/docs/models.md) when a proxy rejects pi's defaults.
220
+ - 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.4.0/packages/coding-agent/docs/models.md) when a proxy rejects pi's defaults.
221
221
 
222
222
  ### Context Overflow Recovery
223
223
 
@@ -225,11 +225,11 @@ Pi can auto-compact and retry when a provider fails with a recognized context-wi
225
225
 
226
226
  ### llama.cpp
227
227
 
228
- 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)). Since Pi 1.1.0, decision models such as Julia-1, Laya, Kev, lev, and OpenJev served by llama.cpp 0.6.0+ are listed only as classifiers, answering natively through `/v1/systemone` with the `typesafe-system-one` API. `"extensions": ["-builtin:llama.cpp"]` removes the provider and `/llama`. See [Pi `docs/llama-cpp.md`](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/llama-cpp.md).
228
+ 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)). Since Pi 1.1.0, decision models such as Julia-1, Laya, Kev, lev, and OpenJev served by llama.cpp 0.6.0+ are listed only as classifiers, answering natively through `/v1/systemone` with the `typesafe-system-one` API. `"extensions": ["-builtin:llama.cpp"]` removes the provider and `/llama`. See [Pi `docs/llama-cpp.md`](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/llama-cpp.md).
229
229
 
230
230
  ## Model Types
231
231
 
232
- 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.2.0/packages/coding-agent/docs/models.md#use-classifier-models), [pi-ai types](https://github.com/earendil-works/pi/blob/v1.2.0/packages/ai/src/types.ts).
232
+ 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.4.0/packages/coding-agent/docs/models.md#use-classifier-models), [pi-ai types](https://github.com/earendil-works/pi/blob/v1.4.0/packages/ai/src/types.ts).
233
233
 
234
234
  ### Classifier Models
235
235
 
@@ -238,11 +238,11 @@ Classifiers do not chat. They answer typed questions about JSON state with proba
238
238
  | Provider | Model IDs | Auth |
239
239
  |----------|-----------|------|
240
240
  | `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
241
- | `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or `/login` |
242
- | `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`) |
241
+ | `openrouter` | `typesafe/jev-1.13`, `typesafe/jev-1.14`, `~typesafe/jev-latest`, `cloudflare/clef`, `cloudflare/clef-flash`, `cloudflare/clef-omni` (Pi 1.4.0) | `OPENROUTER_API_KEY` or `/login` |
242
+ | `cloudflare-workers-ai` | `typesafe/jev`, `typesafe/jev-1.14.0`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` (Clef, Pi 1.0.1), `@cf/cloudflare/clef-omni` (Pi 1.4.0) | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` (or the credential's `env` in `auth.json`) |
243
243
  | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
244
244
  | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
245
- | `openai` | `gpt-6-luna` (Pi 1.1.0; also judges `images`) | `OPENAI_API_KEY`, not Sign in with ChatGPT |
245
+ | `openai` | `gpt-6-luna` (Pi 1.1.0) | `OPENAI_API_KEY`, not Sign in with ChatGPT |
246
246
  | llama.cpp router | decision models (Julia-1, Laya, Kev, lev, OpenJev; Pi 1.1.0) and every loaded chat model ID | `/login llama.cpp` |
247
247
 
248
248
  ```typescript
@@ -269,6 +269,8 @@ if (jev) {
269
269
 
270
270
  - Question types: `choice` (`criteria` maps labels to descriptions; answer has `choice`, `probabilities`, `confidence`), `bool` (`criteria: { true, false }`; answer has `probability`), and `score` (`criteria` is an ordered list; answer has `score`, `confidence`). Pi translates `bool` to TypeSafe's wire format.
271
271
  - `classify()` never rejects. Failures and aborts return `stopReason: "error"` or `"aborted"` with `errorMessage`. Check `stopReason` and each answer's `type` before use, and validate probabilities yourself if a decision depends on them (for example finite and within `[0, 1]`).
272
+ - `classify(model, { state, questions, images })` judges image blocks with the state for classifiers whose `input` includes `"image"`; others return an error result. That covers GPT-6 Luna (Pi 1.1.0) and, since Pi 1.4.0, Jev on `typesafe` (experimental there) and `cloudflare-workers-ai`, Clef on `cloudflare-workers-ai`, and OpenRouter classifiers whose catalog lists image input. Jev takes PNG and JPEG; Clef takes PNG, JPEG, and WebP, at most four per request. Jev on `vercel-ai-gateway` and `opencode` takes text only.
273
+ - Each System One service takes images in its own wire form, set by the classifier's `compat.imageInput` (`"assets"`, `"images"`, or `"state-image-urls"`) plus `compat.sendImageOptInHeader` (Pi 1.4.0). Built-in models set them, so a `baseUrl` override keeps them; a custom provider proxying another service's classifiers sets them in its provider-level `compat` in `models.json`.
272
274
  - `result.usage` carries token counts and catalog cost when the service reports them; TypeSafe's direct `jev-latest` has no catalog price.
273
275
  - `jev-latest` moves with the service. Pin a versioned ID such as `openrouter`'s `typesafe/jev-1.13` when thresholds were tuned against one model.
274
276
  - Codemode scripts reach classifiers through `models.classify()` (see `references/codemode.md`). The SDK equivalent is `modelRuntime.classify()`. A llama.cpp chat-model classifier accepts a per-request `temperature` to soften overconfident label probabilities; System One and OpenAI Decisions ignore it.
@@ -283,7 +285,7 @@ In pi-ai, image models live in the same catalog as chat models: `builtinModels()
283
285
 
284
286
  ## Virtual Models
285
287
 
286
- 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.2.0/packages/coding-agent/docs/virtual-models.md), [`jev-router.ts`](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/examples/extensions/jev-router.ts).
288
+ 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.4.0/packages/coding-agent/docs/virtual-models.md), [`jev-router.ts`](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/examples/extensions/jev-router.ts).
287
289
 
288
290
  ```typescript
289
291
  pi.registerVirtualModel({
@@ -314,7 +316,7 @@ pi.registerVirtualModel({
314
316
 
315
317
  Use extensions when you need custom streaming, custom headers, OAuth/device-flow logic, or want to override an existing provider's `baseUrl`/`headers`:
316
318
 
317
- 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.2.0/packages/coding-agent/docs/custom-provider.md).
319
+ 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.4.0/packages/coding-agent/docs/custom-provider.md).
318
320
 
319
321
  ```typescript
320
322
  // Full registration
@@ -342,7 +344,7 @@ pi.registerProvider("anthropic", { baseUrl: "https://proxy.example.com" });
342
344
 
343
345
  Since pi-ai 1.1.0 a stream function must return an `AssistantMessageEventStream`, for example from `createAssistantMessageEventStream()`; a hand-written `EventStream<AssistantMessageEvent, AssistantMessage>` subclass no longer type-checks. Dynamic catalogs receive `RefreshModelsContext.baseUrl` (pi-ai 1.2.0), the provider's base URL with configuration overrides, so `refreshModels()` can follow a `models.json` base URL.
344
346
 
345
- Custom `streamSimple` implementations receive a normalized `TranscriptContext` instead of `Context`. System prompts and tool declarations live in transcript system messages: read them with `getCurrentSystemPrompt(context.messages)` and `getCurrentTools(context.messages)` (not `context.systemPrompt` / `context.tools`), and call `collapseSystemMessages(context)` if the backend cannot accept mid-conversation system messages. `ToolCall.arguments` and `ToolResultMessage.details` must be JSON-compatible. Pi 0.84.3 also renamed `GoogleThinkingLevel` to `GoogleApiThinkingLevel`.
347
+ Custom `streamSimple` implementations receive a normalized `TranscriptContext` instead of `Context`. System prompts and tool declarations live in transcript system messages: read them with `getCurrentSystemPrompt(context.messages)` and `getCurrentTools(context.messages)` (not `context.systemPrompt` / `context.tools`), and call `collapseSystemMessages(context)` if the backend cannot accept mid-conversation system messages. Breaking in pi-ai 1.4.0: `resolveTranscriptTools()` takes a `TranscriptToolSupport` (`"none"`, `"additions"`, or `"changes"`) instead of a boolean, and `TranscriptTools.anchorsAdditions` became `foldedSystemMessages`. `ToolCall.arguments` and `ToolResultMessage.details` must be JSON-compatible. Since pi-ai 1.3.0 a tool call whose complete arguments text is not a JSON object carries `arguments: {}` and `argumentsError` (the problem plus the raw text), and validation fails with it; a custom adapter should report malformed arguments that way rather than run the fields it managed to parse. Pi 0.84.3 also renamed `GoogleThinkingLevel` to `GoogleApiThinkingLevel`.
346
348
 
347
349
  ## SDK / Extension Auth Lookup
348
350
 
@@ -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 v1.2.0: [sdk.md](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/sdk.md), [SDK examples](https://github.com/earendil-works/pi/tree/v1.2.0/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.4.0: [sdk.md](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/sdk.md), [SDK examples](https://github.com/earendil-works/pi/tree/v1.4.0/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
 
@@ -58,6 +58,8 @@ import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all";
58
58
 
59
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
60
 
61
+ **Client identity (pi-ai 1.4.0):** `createModels({ identity })` or a per-request `identity` option sets the `User-Agent` (`"detailed"`, `"short"`, or `{ custom: "..." }`) and the OpenRouter, NVIDIA NIM, and OpenCode attribution headers (`attribution: { name, url, categories? }`). `withClientIdentity()` applies one to a `Models` received from a host, and `AgentOptions.identity` does the same for agent-loop requests. Breaking: `@earendil-works/pi-ai/utils/pi-user-agent` (`getPiUserAgent()`) was removed, so use `formatUserAgent()`; the request option `telemetryContext` became `telemetry`, a parent `TelemetrySpan`; and `generateSummary()`/`generateSummaryWithUsage()` ignore `reserveTokens`, using the model's output limit.
62
+
61
63
  **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.
62
64
 
63
65
  ## Core Options
@@ -96,7 +98,7 @@ Notes:
96
98
 
97
99
  ## Built-in Extensions: Codemode, Tool Search, MCP
98
100
 
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.2.0/packages/coding-agent/examples/sdk/14-codemode-mcp.ts)):
101
+ 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.4.0/packages/coding-agent/examples/sdk/14-codemode-mcp.ts)):
100
102
 
101
103
  ```typescript
102
104
  import {
@@ -319,7 +321,7 @@ session.subscribe((event) => {
319
321
 
320
322
  ## RPC Mode
321
323
 
322
- `pi --mode rpc` speaks newline-delimited JSON over stdio ([rpc.md](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/rpc.md), [rpc-commands.md](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/rpc-commands.md)). Additions since Pi 0.79:
324
+ `pi --mode rpc` speaks newline-delimited JSON over stdio ([rpc.md](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/rpc.md), [rpc-commands.md](https://github.com/earendil-works/pi/blob/v1.4.0/packages/coding-agent/docs/rpc-commands.md)). Additions since Pi 0.79:
323
325
 
324
326
  - 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.
325
327
  - `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`. Since Pi 1.1.0 `agent_settled` carries `aborted`, and `tool_execution_end` carries `durationMs`. `set_thinking_level` accepts `"max"` where the model supports it.
@@ -115,6 +115,7 @@ Fullscreen is the default since Pi 1.0.0 (added in 0.84.0):
115
115
 
116
116
  Notes:
117
117
 
118
+ - `compaction.reserveTokens` only decides when auto-compaction starts: once free context falls below it. Since Pi 1.4.0 compaction and branch summaries use the model's output limit instead of a fixed cap, so long reasoning no longer makes them fail.
118
119
  - `compaction.modelOverrides` (Pi 0.86.0) sets per-model `reserveTokens`/`keepRecentTokens` keyed by exact `provider/modelId`; each value falls back to the ordinary compaction setting, then the default.
119
120
  - `retry.maxRetries` (default `3`) and `retry.baseDelayMs` (default `2000`) govern Pi's own agent-level retry with exponential backoff, capped per attempt by `retry.maxAgentDelayMs` (default `60000`, Pi 0.86.0).
120
121
  - `retry.provider.*` controls the underlying provider SDK's request timeout and retry/backoff — useful for slow local LLMs and flaky proxies. `retry.provider.timeoutMs` sets the SDK request timeout; `retry.provider.maxRetries` defaults to `0` (keep it there unless you need SDK-level retries, since raising it can let provider retries swallow usage-limit errors before Pi handles them).
@@ -185,6 +186,7 @@ Notes:
185
186
  ```json
186
187
  {
187
188
  "sessionDir": ".pi/sessions",
189
+ "sessionShareDestination": "auto",
188
190
  "enabledModels": ["claude-*", "gpt-4o", "gemini-2*"],
189
191
  "markdown": {
190
192
  "codeBlockIndent": " ",
@@ -195,6 +197,7 @@ Notes:
195
197
 
196
198
  When multiple sources specify a session directory, `--session-dir` takes precedence over `sessionDir` in settings. Pi 0.65.0 removed the old `session_directory` extension/settings hook.
197
199
 
200
+ - `sessionShareDestination` (Pi 1.3.0): `"auto"` (default) uploads `/share` to Radius when signed in and otherwise to a private GitHub gist; `"gist"` or `"radius"` forces one, and `"radius"` never falls back.
198
201
  - `markdown.mermaid` (Pi 0.84.0): `"off"`, `"final"`, or `"streaming"` — themed Unicode rendering of supported Mermaid diagrams in interactive messages. Pi 0.84.0 also renders terminal-friendly Unicode for LaTeX expressions.
199
202
 
200
203
  ## Tools & Codemode
@@ -208,6 +211,7 @@ When multiple sources specify a session directory, `--session-dir` takes precede
208
211
 
209
212
  - `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.
210
213
  - `--tools` with plain names replaces the whole selection for one run; entries may be `*` patterns (Pi 1.0.4), and MCP tools stay unless an entry starts with `mcp__`. A list of only `+name`/`-name` entries (Pi 1.1.0, exact names) changes the `defaultTools` selection instead, e.g. `pi --tools +codemode,-write`; the two forms cannot be mixed. `--exclude-tools` filters the result, takes patterns, and covers MCP tools; `--no-mcp` (Pi 1.0.4) turns off built-in MCP for one run.
214
+ - Resumed sessions restore their active tools (Pi 1.3.0), and `--tools +name`/`-name` applies on top of them; `--tools` with plain names, `--no-tools`, and `--no-builtin-tools` still pick tools explicitly. Since Pi 1.4.0, resume and `/tree` also activate extension tools registered after the branch recorded its tools, unless they use `defaultActive: false`; tools the branch removed stay inactive.
211
215
  - `/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` with plain names, `--no-tools`, and `--no-builtin-tools` still win on reload, and a tool removed with `--tools -name` stays removed.
212
216
  - `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`.
213
217
  - MCP servers are configured in `~/.pi/agent/mcp.json` and the trust-gated `.pi/mcp.json`, not in `settings.json`. See `references/mcp.md`.
@@ -294,10 +298,10 @@ pi [options] [@files...] [messages...]
294
298
 
295
299
  # model
296
300
  --provider <name> e.g. anthropic, openai, google; needs --model (Pi 1.0.0)
297
- --model <pattern> supports provider/id and optional :<thinking>
301
+ --model <pattern> supports provider/id and optional :<thinking>; prefers the provider with credentials when several match (Pi 1.3.0)
298
302
  --api-key <key>
299
303
  --thinking off|minimal|low|medium|high|xhigh|max
300
- --models <patterns> Ctrl+P cycling allowlist
304
+ --models <patterns> Ctrl+P cycling allowlist; a model several providers offer is added for each, with a warning (Pi 1.3.0)
301
305
  --list-models [search]
302
306
 
303
307
  # sessions
@@ -353,6 +357,7 @@ Use `/bug [description]` (Pi 0.86.0) to report a problem with redacted diagnosti
353
357
  | `PI_CACHE_RETENTION` | Set to `long` for extended prompt-cache retention where supported |
354
358
  | `PI_OAUTH_CALLBACK_HOST` | Bind the OAuth callback server to a custom interface |
355
359
  | `PI_SHARE_VIEWER_URL` | Override the base URL used by `/share` |
360
+ | `PI_CODEMODE_MAX_WORKERS` | Maximum codemode workers alive at once across the process (default `2`, Pi 1.4.0); raise it when one process runs several sessions |
356
361
  | `PI_HARDWARE_CURSOR` | `1` shows the hardware cursor (see terminal-setup docs) |
357
362
  | `PI_CLIPBOARD_OSC52` | `1` (Pi 1.2.0) also sends copied text to the terminal with OSC 52, for remote clients Pi cannot detect, such as a multiplexer attached from another machine |
358
363
  | `PI_PROGRAM_STATUS` | OSC 7501 program status (Pi 1.1.0): Pi reports working/blocked/done/error/idle to terminals and agent dashboards that confirm support; `1` always reports, `0` never |