@counterposition/skill-pi 0.99.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 +14 -10
- package/skills/pi/references/extensions.md +5 -5
- package/skills/pi/references/mcp.md +26 -11
- package/skills/pi/references/packages.md +2 -2
- package/skills/pi/references/patterns.md +1 -1
- package/skills/pi/references/providers.md +11 -9
- 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.0.
|
|
32
32
|
|
|
33
33
|
The reference material lives in `references/` as task-specific documents — not a single monolith, but pieces sized for injection into a conversation when relevant.
|
|
34
34
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@counterposition/skill-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Pi coding agent reference skill — extensions, settings, providers, SDK/RPC, packages, and .pi/ config",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-skill",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"access": "public"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@earendil-works/pi-ai": "^0.
|
|
34
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
33
|
+
"@earendil-works/pi-ai": "^1.0.0",
|
|
34
|
+
"@earendil-works/pi-coding-agent": "^1.0.0",
|
|
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.0.
|
|
32
32
|
|
|
33
33
|
The reference material lives in `references/` as task-specific documents — not a single monolith, but pieces sized for injection into a conversation when relevant.
|
|
34
34
|
|
package/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.0.
|
|
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.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).
|
|
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
|
-
- `
|
|
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()`.
|
|
31
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.
|
|
32
|
-
- No timers, `fetch`, `process`, `require`, or modules.
|
|
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.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)):
|
|
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
|
|
|
@@ -227,7 +227,7 @@ 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
|
|
|
@@ -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.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).
|
|
4
4
|
|
|
5
5
|
## Configure
|
|
6
6
|
|
|
@@ -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,23 @@ 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
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.
|
|
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?, 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
|
+
- `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
|
+
- 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
|
+
- `"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
73
|
|
|
60
74
|
## Exposure
|
|
61
75
|
|
|
@@ -63,8 +77,7 @@ Pi connects on session start; the first prompt waits up to 10 seconds for startu
|
|
|
63
77
|
|
|
64
78
|
| Value | Declared to the model | Callable from codemode | Notes |
|
|
65
79
|
|-------|----------------------|------------------------|-------|
|
|
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 |
|
|
80
|
+
| `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
81
|
| `deferred` | After `tool_search` loads them | Yes | Pi activates `tool_search`; for large servers without codemode |
|
|
69
82
|
| `direct` | Yes, like built-in tools | Yes | |
|
|
70
83
|
| `hidden` | No | No | Registered but unreachable |
|
|
@@ -83,12 +96,14 @@ Pi connects on session start; the first prompt waits up to 10 seconds for startu
|
|
|
83
96
|
}
|
|
84
97
|
```
|
|
85
98
|
|
|
86
|
-
|
|
99
|
+
`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.
|
|
100
|
+
|
|
101
|
+
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
102
|
|
|
88
103
|
## Results
|
|
89
104
|
|
|
90
105
|
- 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.
|
|
106
|
+
- 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
107
|
- Servers with resources add `list_mcp_resources`, `list_mcp_resource_templates`, and `read_mcp_resource` (marked read-only).
|
|
93
108
|
- Tool calls are never retried (the server may have run them); resource reads retry once on 408/429/5xx.
|
|
94
109
|
|
|
@@ -99,7 +114,7 @@ Every MCP call goes through Pi's tool pipeline, so `tool_call` and `tool_result`
|
|
|
99
114
|
## Servers From Extensions
|
|
100
115
|
|
|
101
116
|
```typescript
|
|
102
|
-
pi.registerMcpServer("jira", { url: "https://mcp.example.com/jira", exposure: "codemode" });
|
|
117
|
+
pi.registerMcpServer("jira", { url: "https://mcp.example.com/jira", exposure: "codemode", description: "Jira issues" });
|
|
103
118
|
pi.unregisterMcpServer("jira");
|
|
104
119
|
```
|
|
105
120
|
|
|
@@ -117,6 +132,6 @@ pi.unregisterMcpServer("jira");
|
|
|
117
132
|
|
|
118
133
|
## SDK
|
|
119
134
|
|
|
120
|
-
SDK sessions do not load built-in extensions. Add `createMcpExtension()`, plus `createCodemodeExtension()` for `codemode
|
|
135
|
+
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
136
|
|
|
122
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.
|
|
@@ -94,7 +94,7 @@ If there is no `pi` manifest, Pi auto-discovers:
|
|
|
94
94
|
|
|
95
95
|
## Dependencies
|
|
96
96
|
|
|
97
|
-
Checked against Pi
|
|
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).
|
|
98
98
|
|
|
99
99
|
- Normal runtime dependencies belong in `dependencies`
|
|
100
100
|
- Pi supplies these to extensions; declare them as peer deps with `"*"` ranges and never bundle them:
|
|
@@ -108,7 +108,7 @@ Checked against Pi v0.99.1 [packages.md](https://github.com/earendil-works/pi/bl
|
|
|
108
108
|
- Git package installs omit dev dependencies — runtime needs go in `dependencies`, not `devDependencies`
|
|
109
109
|
- 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
110
|
- 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.
|
|
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.
|
|
112
112
|
|
|
113
113
|
## Package Filtering
|
|
114
114
|
|
|
@@ -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.0 in this skill's package; read them rather than retyping.
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -6,13 +6,13 @@ Pi supports subscription-based providers via OAuth, API-key providers via env va
|
|
|
6
6
|
|
|
7
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.
|
|
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,8 @@ 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
|
+
|
|
61
63
|
## Auth File
|
|
62
64
|
|
|
63
65
|
`~/.pi/agent/auth.json` stores API keys and OAuth tokens with `0600` permissions:
|
|
@@ -95,7 +97,7 @@ Entries may include an `env` object (Pi 0.79.5) for provider-scoped environment
|
|
|
95
97
|
|
|
96
98
|
Provider extensions can define their own authentication behavior.
|
|
97
99
|
|
|
98
|
-
##
|
|
100
|
+
## Provider-Specific Setup
|
|
99
101
|
|
|
100
102
|
### Azure OpenAI
|
|
101
103
|
|
|
@@ -211,7 +213,7 @@ Common `api` values:
|
|
|
211
213
|
- `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
214
|
- **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
215
|
- `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/
|
|
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.
|
|
215
217
|
|
|
216
218
|
### Context Overflow Recovery
|
|
217
219
|
|
|
@@ -219,11 +221,11 @@ Pi can auto-compact and retry when a provider fails with a recognized context-wi
|
|
|
219
221
|
|
|
220
222
|
### llama.cpp
|
|
221
223
|
|
|
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/
|
|
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).
|
|
223
225
|
|
|
224
226
|
## Model Types
|
|
225
227
|
|
|
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/
|
|
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).
|
|
227
229
|
|
|
228
230
|
### Classifier Models
|
|
229
231
|
|
|
@@ -270,13 +272,13 @@ A gate that fails closed on classifier errors is in `examples/classifier-gate.ts
|
|
|
270
272
|
|
|
271
273
|
### Image Generation
|
|
272
274
|
|
|
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.
|
|
275
|
+
`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
276
|
|
|
275
277
|
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
278
|
|
|
277
279
|
## Virtual Models
|
|
278
280
|
|
|
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/
|
|
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).
|
|
280
282
|
|
|
281
283
|
```typescript
|
|
282
284
|
pi.registerVirtualModel({
|
|
@@ -307,7 +309,7 @@ pi.registerVirtualModel({
|
|
|
307
309
|
|
|
308
310
|
Use extensions when you need custom streaming, custom headers, OAuth/device-flow logic, or want to override an existing provider's `baseUrl`/`headers`:
|
|
309
311
|
|
|
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/
|
|
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).
|
|
311
313
|
|
|
312
314
|
```typescript
|
|
313
315
|
// 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.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).
|
|
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.0/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.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:
|
|
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
|
```
|