@counterposition/skill-pi 1.0.0 → 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 1.0.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.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": "1.0.0",
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": "^1.0.0",
34
- "@earendil-works/pi-coding-agent": "^1.0.0",
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 1.0.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.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 1.0.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.0.2.
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.0.0: [codemode.md](https://github.com/earendil-works/pi/blob/v1.0.0/packages/coding-agent/docs/codemode.md), [cli.md#tools](https://github.com/earendil-works/pi/blob/v1.0.0/packages/coding-agent/docs/cli.md#tools), [extensions.md#tool-exposure](https://github.com/earendil-works/pi/blob/v1.0.0/packages/coding-agent/docs/extensions.md#tool-exposure), [settings.md#tools](https://github.com/earendil-works/pi/blob/v1.0.0/packages/coding-agent/docs/settings.md#tools), [codemode tool source](https://github.com/earendil-works/pi/blob/v1.0.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.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
 
@@ -30,7 +30,7 @@ The script body is an async function: top-level `await` and `return` work.
30
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
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
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.
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
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.
35
35
 
36
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:`.
@@ -193,7 +193,7 @@ 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/v1.0.0/packages/coding-agent/docs/extensions.md#tool-exposure), [types](https://github.com/earendil-works/pi/blob/v1.0.0/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.
@@ -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
 
@@ -231,6 +231,8 @@ Model capability metadata gates what is actually sent: `compat.supportsStrictToo
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 connects to MCP servers through a built-in extension. There is no package to install. Checked against Pi v1.0.0: [mcp.md](https://github.com/earendil-works/pi/blob/v1.0.0/packages/coding-agent/docs/mcp.md), [cli.md#mcp-commands](https://github.com/earendil-works/pi/blob/v1.0.0/packages/coding-agent/docs/cli.md#mcp-commands), [extensions.md#mcp-servers](https://github.com/earendil-works/pi/blob/v1.0.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.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 connects to MCP servers through a built-in extension. There is no package to
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
  {
@@ -55,18 +55,19 @@ pi mcp logout sentry
55
55
 
56
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.
57
57
 
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. 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.
59
59
 
60
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
61
 
62
62
  ### Authentication
63
63
 
64
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?, authServerMetadataUrl? }`:
65
+ - `oauth: { clientId?, clientSecret?, callbackPort?, callbackUrl?, scope?, clientName?, clientRegistration?, authServerMetadataUrl? }`:
66
66
  - `clientId`/`clientSecret` set a pre-registered client for servers without dynamic client registration.
67
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
68
  - `scope`: space-separated scopes to request when the server does not advertise the ones it needs.
69
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`.
70
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.
71
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).
72
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.
@@ -134,4 +135,4 @@ pi.unregisterMcpServer("jira");
134
135
 
135
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`.
136
137
 
137
- 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 v1.0.0 [packages.md](https://github.com/earendil-works/pi/blob/v1.0.0/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 v1.0.0 [packages.md](https://github.com/earendil-works/pi/blo
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. `^1.0.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.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 1.0.0 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,7 +4,7 @@ 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
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
@@ -60,6 +60,8 @@ Since Pi 0.80.8, built-in catalogs are complemented by dynamic ones: `/model` re
60
60
 
61
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
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
+
63
65
  ## Auth File
64
66
 
65
67
  `~/.pi/agent/auth.json` stores API keys and OAuth tokens with `0600` permissions:
@@ -201,10 +203,10 @@ Common `api` values:
201
203
 
202
204
  ### Model & Compatibility Knobs
203
205
 
204
- - `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.
205
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.
206
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.
207
- - `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`.
208
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).
209
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`).
210
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.
@@ -213,7 +215,7 @@ Common `api` values:
213
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`.
214
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`.
215
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`.
216
- - 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.0/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.
217
219
 
218
220
  ### Context Overflow Recovery
219
221
 
@@ -221,11 +223,11 @@ Pi can auto-compact and retry when a provider fails with a recognized context-wi
221
223
 
222
224
  ### llama.cpp
223
225
 
224
- 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.0/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).
225
227
 
226
228
  ## Model Types
227
229
 
228
- 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.0/packages/coding-agent/docs/models.md#use-classifier-models), [pi-ai types](https://github.com/earendil-works/pi/blob/v1.0.0/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).
229
231
 
230
232
  ### Classifier Models
231
233
 
@@ -235,7 +237,7 @@ Classifiers do not chat. They answer typed questions about JSON state with proba
235
237
  |----------|-----------|------|
236
238
  | `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
237
239
  | `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or `/login` |
238
- | `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`) |
239
241
  | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
240
242
  | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
241
243
  | llama.cpp router | every loaded chat model ID | `/login llama.cpp` |
@@ -278,7 +280,7 @@ In pi-ai, image models live in the same catalog as chat models: `builtinModels()
278
280
 
279
281
  ## Virtual Models
280
282
 
281
- 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.0/packages/coding-agent/docs/virtual-models.md), [`jev-router.ts`](https://github.com/earendil-works/pi/blob/v1.0.0/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).
282
284
 
283
285
  ```typescript
284
286
  pi.registerVirtualModel({
@@ -309,7 +311,7 @@ pi.registerVirtualModel({
309
311
 
310
312
  Use extensions when you need custom streaming, custom headers, OAuth/device-flow logic, or want to override an existing provider's `baseUrl`/`headers`:
311
313
 
312
- 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.0/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).
313
315
 
314
316
  ```typescript
315
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 v1.0.0: [sdk.md](https://github.com/earendil-works/pi/blob/v1.0.0/packages/coding-agent/docs/sdk.md), [SDK examples](https://github.com/earendil-works/pi/tree/v1.0.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.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
 
@@ -96,7 +96,7 @@ Notes:
96
96
 
97
97
  ## Built-in Extensions: Codemode, Tool Search, MCP
98
98
 
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.0/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)):
100
100
 
101
101
  ```typescript
102
102
  import {
@@ -323,7 +323,7 @@ Pi 0.84.0 added transport-neutral remote-session client APIs: `@earendil-works/p
323
323
 
324
324
  ## RPC Mode
325
325
 
326
- `pi --mode rpc` speaks newline-delimited JSON over stdio ([rpc.md](https://github.com/earendil-works/pi/blob/v1.0.0/packages/coding-agent/docs/rpc.md), [rpc-commands.md](https://github.com/earendil-works/pi/blob/v1.0.0/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:
327
327
 
328
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.
329
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.