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