@counterposition/skill-pi 0.84.2 → 0.99.1
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 +8 -7
- package/package.json +15 -4
- package/skills/pi/README.md +8 -7
- package/skills/pi/SKILL.md +13 -7
- package/skills/pi/evals/evals.json +81 -0
- package/skills/pi/examples/classifier-gate.ts +46 -0
- package/skills/pi/examples/nested-tools.ts +52 -0
- package/skills/pi/examples/structured-tool.ts +61 -0
- package/skills/pi/references/codemode.md +88 -0
- package/skills/pi/references/extensions.md +59 -13
- package/skills/pi/references/mcp.md +122 -0
- package/skills/pi/references/packages.md +11 -5
- package/skills/pi/references/patterns.md +37 -2
- package/skills/pi/references/providers.md +113 -14
- package/skills/pi/references/sdk.md +84 -16
- package/skills/pi/references/settings.md +49 -16
|
@@ -7,11 +7,13 @@ Pi supports subscription-based providers via OAuth, API-key providers via env va
|
|
|
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
9
|
- Anthropic Claude Pro / Max — third-party usage draws from extra usage and is billed per token (suppress the warning via `warnings.anthropicExtraUsage`)
|
|
10
|
-
- OpenAI
|
|
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
|
+
- 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
|
|
11
12
|
- GitHub Copilot
|
|
12
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**
|
|
13
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**
|
|
14
|
-
- 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
|
|
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
|
|
16
|
+
- Meta Muse (Pi 0.86.1) — `/login meta` with automatic Model API key refresh; `META_API_KEY` also works
|
|
15
17
|
|
|
16
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.
|
|
17
19
|
|
|
@@ -42,11 +44,13 @@ Since Pi 0.80.8, built-in catalogs are complemented by dynamic ones: `/model` re
|
|
|
42
44
|
| OpenCode Go | `OPENCODE_API_KEY` | `opencode-go` |
|
|
43
45
|
| Amazon Bedrock | `AWS_BEARER_TOKEN_BEDROCK` | `amazon-bedrock` |
|
|
44
46
|
| Radius | `RADIUS_API_KEY` | `radius` |
|
|
47
|
+
| TypeSafe (classifier models only) | `TYPESAFE_API_KEY` | `typesafe` |
|
|
45
48
|
| Hugging Face | `HF_TOKEN` | `huggingface` |
|
|
46
49
|
| Fireworks | `FIREWORKS_API_KEY` | `fireworks` |
|
|
47
50
|
| Together AI | `TOGETHER_API_KEY` | `together` |
|
|
48
51
|
| Baseten | `BASETEN_API_KEY` | `baseten` |
|
|
49
52
|
| Kimi For Coding | `KIMI_API_KEY` | `kimi-coding` |
|
|
53
|
+
| Meta (Muse) | `META_API_KEY` | `meta` |
|
|
50
54
|
| MiniMax | `MINIMAX_API_KEY` | `minimax` |
|
|
51
55
|
| MiniMax (China) | `MINIMAX_CN_API_KEY` | `minimax-cn` |
|
|
52
56
|
| Qwen Token Plan / Individual | `QWEN_TOKEN_PLAN_API_KEY` | `qwen-token-plan` / `qwen-token-plan-individual` (Pi 0.81.0 / 0.84.1) |
|
|
@@ -84,10 +88,12 @@ Entries may include an `env` object (Pi 0.79.5) for provider-scoped environment
|
|
|
84
88
|
|
|
85
89
|
## Credential Resolution Order
|
|
86
90
|
|
|
87
|
-
1. `--api-key`
|
|
91
|
+
1. `--api-key` (runtime override)
|
|
88
92
|
2. `auth.json`
|
|
89
|
-
3.
|
|
90
|
-
4.
|
|
93
|
+
3. `apiKey` from `models.json`
|
|
94
|
+
4. Environment variables or ambient cloud credentials
|
|
95
|
+
|
|
96
|
+
Provider extensions can define their own authentication behavior.
|
|
91
97
|
|
|
92
98
|
## Cloud Providers
|
|
93
99
|
|
|
@@ -149,12 +155,11 @@ Use `models.json` for OpenAI-compatible, Anthropic-compatible, Google-compatible
|
|
|
149
155
|
|
|
150
156
|
```json
|
|
151
157
|
{
|
|
152
|
-
"providers":
|
|
153
|
-
{
|
|
154
|
-
"name": "ollama",
|
|
158
|
+
"providers": {
|
|
159
|
+
"ollama": {
|
|
155
160
|
"baseUrl": "http://localhost:11434/v1",
|
|
156
161
|
"api": "openai-completions",
|
|
157
|
-
"apiKey": "
|
|
162
|
+
"apiKey": "ollama",
|
|
158
163
|
"models": [
|
|
159
164
|
{
|
|
160
165
|
"id": "llama3.2",
|
|
@@ -173,10 +178,12 @@ Use `models.json` for OpenAI-compatible, Anthropic-compatible, Google-compatible
|
|
|
173
178
|
}
|
|
174
179
|
]
|
|
175
180
|
}
|
|
176
|
-
|
|
181
|
+
}
|
|
177
182
|
}
|
|
178
183
|
```
|
|
179
184
|
|
|
185
|
+
`providers` is an object keyed by provider ID, not an array; the optional `name` field is only a display label. A `models` entry adds or replaces a model with the same ID on that provider; use `modelOverrides` to change metadata of an existing built-in or extension model.
|
|
186
|
+
|
|
180
187
|
`apiKey` is optional (Pi 0.80.0): omit it when auth comes from `/login`, `auth.json`, or CLI `--api-key`. Like `auth.json` keys, `apiKey` and `headers` values support `!command` execution and `$ENV_VAR` interpolation; plain uppercase strings are literals. Keyless local servers (e.g. Ollama) should keep a dummy value so their models appear in `/model`.
|
|
181
188
|
|
|
182
189
|
Common `api` values:
|
|
@@ -195,12 +202,16 @@ Common `api` values:
|
|
|
195
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.
|
|
196
203
|
- `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.
|
|
197
204
|
- `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.
|
|
198
|
-
- `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`.
|
|
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`.
|
|
206
|
+
- `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
|
+
- `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
|
+
- `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.
|
|
209
|
+
- OpenAI-compatible local servers: `vllmPriority` and `supportsMaxOutputTokens` (Pi 0.85.0) plus configurable thinking-token budget fields for vLLM, Qwen/SGLang, and llama.cpp (Pi 0.84.3).
|
|
199
210
|
- `openRouterRouting` is forwarded as-is in the OpenRouter `provider` field (fallbacks, ZDR, ignore lists, throughput/latency).
|
|
200
211
|
- `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`.
|
|
201
212
|
- **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`.
|
|
202
213
|
- `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`.
|
|
203
|
-
- 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/
|
|
214
|
+
- Advanced `compat` flags exist for proxy quirks (`cacheControlFormat` — now also covering tool-result text content, `supportsReasoningEffort`, `supportsLongCacheRetention`, `supportsEagerToolInputStreaming`, `supportsStrictMode`). See [Pi `docs/models.md`](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/models.md) when a proxy rejects pi's defaults.
|
|
204
215
|
|
|
205
216
|
### Context Overflow Recovery
|
|
206
217
|
|
|
@@ -208,12 +219,96 @@ Pi can auto-compact and retry when a provider fails with a recognized context-wi
|
|
|
208
219
|
|
|
209
220
|
### llama.cpp
|
|
210
221
|
|
|
211
|
-
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). See [Pi `docs/llama-cpp.md`](https://github.com/earendil-works/pi/blob/
|
|
222
|
+
Pi supports the llama.cpp router server (Pi 0.81.0): configure with `/login llama.cpp`, search/download Hugging Face models and load/unload with live progress via `/llama`, then select loaded models in `/model`. The model catalog persists across restarts (fixed in Pi 0.82.1). Every llama.cpp chat model is also listed as a classifier model (see [Classifier Models](#classifier-models)). `"extensions": ["-builtin:llama.cpp"]` removes the provider and `/llama`. See [Pi `docs/llama-cpp.md`](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/llama-cpp.md).
|
|
223
|
+
|
|
224
|
+
## Model Types
|
|
225
|
+
|
|
226
|
+
Since Pi 0.99.0 catalogs hold typed models: `type` is `"chat"` (the default when omitted), `"image"`, or `"classifier"`. Chat-facing reads (`getModels()`, `getAvailable()`, `ctx.modelRegistry.getAll()`, `/model`) stay chat-only. `ModelRuntime`, `ctx.modelRegistry`, and pi-ai `Models` all have `getModelsOfType(type, provider?)`, `getModelOfType(type, provider, id)`, and async `getAvailableOfType(type, provider?)`. Only `ModelRuntime` and `Models` have `getAllModels()` and `getAllAvailable()` for every type; `ctx.modelRegistry` adds `findOfType(type, provider, id)` instead. pi-ai exports `isModelType()` and `getModelType()` for mixed lists. One upstream ID may have separate chat and image entries. Sources: [models.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/models.md#use-classifier-models), [pi-ai types](https://github.com/earendil-works/pi/blob/v0.99.1/packages/ai/src/types.ts).
|
|
227
|
+
|
|
228
|
+
### Classifier Models
|
|
229
|
+
|
|
230
|
+
Classifiers do not chat. They answer typed questions about JSON state with probabilities, and do not appear in `/model`.
|
|
231
|
+
|
|
232
|
+
| Provider | Model IDs | Auth |
|
|
233
|
+
|----------|-----------|------|
|
|
234
|
+
| `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
|
|
235
|
+
| `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` |
|
|
237
|
+
| `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
|
|
238
|
+
| `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
|
|
239
|
+
| llama.cpp router | every loaded chat model ID | `/login llama.cpp` |
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
const jev = ctx.modelRegistry.findOfType("classifier", "typesafe", "jev-latest");
|
|
243
|
+
if (jev) {
|
|
244
|
+
const result = await ctx.modelRegistry.classify(
|
|
245
|
+
jev,
|
|
246
|
+
{
|
|
247
|
+
state: { message: "The change works, thanks." },
|
|
248
|
+
questions: {
|
|
249
|
+
approved: {
|
|
250
|
+
type: "bool",
|
|
251
|
+
instructions: "Does the user approve of the result?",
|
|
252
|
+
criteria: { true: "Approval", false: "No approval" },
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
{ signal: ctx.signal },
|
|
257
|
+
);
|
|
258
|
+
const answer = result.stopReason === "stop" ? result.answers.approved : undefined;
|
|
259
|
+
if (answer?.type === "bool") console.log(answer.probability);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
- Question types: `choice` (`criteria` maps labels to descriptions; answer has `choice`, `probabilities`, `confidence`), `bool` (`criteria: { true, false }`; answer has `probability`), and `score` (`criteria` is an ordered list; answer has `score`, `confidence`). Pi translates `bool` to TypeSafe's wire format.
|
|
264
|
+
- `classify()` never rejects. Failures and aborts return `stopReason: "error"` or `"aborted"` with `errorMessage`. Check `stopReason` and each answer's `type` before use, and validate probabilities yourself if a decision depends on them (for example finite and within `[0, 1]`).
|
|
265
|
+
- `result.usage` carries token counts and catalog cost when the service reports them; TypeSafe's direct `jev-latest` has no catalog price.
|
|
266
|
+
- `jev-latest` moves with the service. Pin a versioned ID such as `openrouter`'s `typesafe/jev-1.13` when thresholds were tuned against one model.
|
|
267
|
+
- Codemode scripts reach classifiers through `models.classify()` (see `references/codemode.md`). The SDK equivalent is `modelRuntime.classify()`. A llama.cpp classifier accepts a per-request `temperature` to soften overconfident label probabilities.
|
|
268
|
+
|
|
269
|
+
A gate that fails closed on classifier errors is in `examples/classifier-gate.ts`.
|
|
270
|
+
|
|
271
|
+
### Image Generation
|
|
272
|
+
|
|
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.
|
|
274
|
+
|
|
275
|
+
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
|
+
|
|
277
|
+
## Virtual Models
|
|
278
|
+
|
|
279
|
+
A virtual model (experimental, Pi 0.99.0) is a selectable entry that routes each request to a physical model. It is for routing by task, cost, or conversation state, not for permission decisions. Source: [virtual-models.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/virtual-models.md), [`jev-router.ts`](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/examples/extensions/jev-router.ts).
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
pi.registerVirtualModel({
|
|
283
|
+
provider: "router",
|
|
284
|
+
id: "auto",
|
|
285
|
+
name: "Auto",
|
|
286
|
+
thinkingLevels: ["low", "high"],
|
|
287
|
+
route(request, ctx) {
|
|
288
|
+
const sticky = request.failed ?? request.previous;
|
|
289
|
+
if (request.reason !== "user" && sticky) {
|
|
290
|
+
return { model: sticky.model, thinkingLevel: sticky.thinkingLevel ?? "medium" };
|
|
291
|
+
}
|
|
292
|
+
const id = request.thinkingLevel === "high" ? "claude-sonnet-4-5" : "claude-haiku-4-5";
|
|
293
|
+
const model = ctx.modelRegistry.find("anthropic", id);
|
|
294
|
+
if (!model) throw new Error(`anthropic/${id} is not in the catalog`);
|
|
295
|
+
return { model, thinkingLevel: "medium" };
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
- `route()` runs before every request with `reason` (`user`, `continuation`, `retry`, `direct`), `previous`, `failed`, `state`, `messages`, and `signal`. Returning `previous` for continuations and `failed` for retries keeps prompt caches and thinking signatures valid.
|
|
301
|
+
- Returned `state` (JSON) is stored on the session branch and comes back as `request.state`; `direct` requests (compaction summaries, `ctx.modelRegistry.streamSimple()`) have none.
|
|
302
|
+
- The route must be a physical model with credentials; throwing or returning a virtual model ends the request with an error.
|
|
303
|
+
- Selection (`ctx.model`, `model_change`) names the virtual model; each assistant message records the physical `provider`/`model`/`thinkingLevel`. The footer shows both; `/session` lists cost per physical model.
|
|
304
|
+
- `pi.unregisterVirtualModel(provider, id)` removes one (`unregisterProvider()` does not). SDK code can call `modelRuntime.registerVirtualModel(definition)`.
|
|
212
305
|
|
|
213
306
|
## Custom Providers via Extensions
|
|
214
307
|
|
|
215
308
|
Use extensions when you need custom streaming, custom headers, OAuth/device-flow logic, or want to override an existing provider's `baseUrl`/`headers`:
|
|
216
309
|
|
|
310
|
+
A config-form `models` list replaces the provider's models across chat, image, and classifier operations; image and classifier entries need an explicit `type` and implementations keyed by `api` through `images` and `classifiers`. Registering only `baseUrl` or `headers` keeps the built-in models. See [custom-provider.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/custom-provider.md).
|
|
311
|
+
|
|
217
312
|
```typescript
|
|
218
313
|
// Full registration
|
|
219
314
|
pi.registerProvider("my-provider", {
|
|
@@ -236,6 +331,10 @@ pi.registerProvider("my-provider", {
|
|
|
236
331
|
pi.registerProvider("anthropic", { baseUrl: "https://proxy.example.com" });
|
|
237
332
|
```
|
|
238
333
|
|
|
334
|
+
### Custom Streaming (Breaking, Pi 0.86.0)
|
|
335
|
+
|
|
336
|
+
Custom `streamSimple` implementations receive a normalized `TranscriptContext` instead of `Context`. System prompts and tool declarations live in transcript system messages: read them with `getCurrentSystemPrompt(context.messages)` and `getCurrentTools(context.messages)` (not `context.systemPrompt` / `context.tools`), and call `collapseSystemMessages(context)` if the backend cannot accept mid-conversation system messages. `ToolCall.arguments` and `ToolResultMessage.details` must be JSON-compatible. Pi 0.84.3 also renamed `GoogleThinkingLevel` to `GoogleApiThinkingLevel`.
|
|
337
|
+
|
|
239
338
|
## SDK / Extension Auth Lookup
|
|
240
339
|
|
|
241
340
|
If extension code needs auth for a specific model request, use `getApiKeyAndHeaders(model)` rather than the removed `getApiKey(model)`:
|
|
@@ -247,4 +346,4 @@ if (!auth.ok) throw new Error(auth.error);
|
|
|
247
346
|
const { apiKey, headers } = auth;
|
|
248
347
|
```
|
|
249
348
|
|
|
250
|
-
This matters for providers whose headers or auth values resolve dynamically on every request. In SDK code, use `ModelRuntime.getAuth(providerOrModel)` instead (Pi 0.80.8) — passing a model also resolves built-in, `models.json`, and extension model headers; see `references/sdk.md`.
|
|
349
|
+
This matters for providers whose headers or auth values resolve dynamically on every request. For nested model calls, `ctx.modelRegistry.streamSimple(model, context, options)` (Pi 0.86.0) resolves auth itself. In SDK code, use `ModelRuntime.getAuth(providerOrModel)` instead (Pi 0.80.8) — passing a model also resolves built-in, `models.json`, and extension model headers; see `references/sdk.md`.
|
|
@@ -30,10 +30,15 @@ session.subscribe((event) => {
|
|
|
30
30
|
}
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
try {
|
|
34
|
+
await session.prompt("What files are in the current directory?");
|
|
35
|
+
console.log(session.getLastAssistantText());
|
|
36
|
+
} finally {
|
|
37
|
+
session.dispose();
|
|
38
|
+
}
|
|
34
39
|
```
|
|
35
40
|
|
|
36
|
-
Run with `npx tsx my-script.ts
|
|
41
|
+
Run with `npx tsx my-script.ts` (or Node's built-in type stripping). `prompt()` resolves when the run finishes, including automatic retries. `session.dispose()` aborts active work, invalidates extension contexts, and removes listeners. Checked against Pi v0.99.1: [sdk.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/sdk.md), [SDK examples](https://github.com/earendil-works/pi/tree/v0.99.1/packages/coding-agent/examples/sdk).
|
|
37
42
|
|
|
38
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>`.
|
|
39
44
|
|
|
@@ -44,6 +49,7 @@ import {
|
|
|
44
49
|
createAgentSession,
|
|
45
50
|
DefaultResourceLoader,
|
|
46
51
|
defineTool,
|
|
52
|
+
getAgentDir,
|
|
47
53
|
ModelRuntime,
|
|
48
54
|
SessionManager,
|
|
49
55
|
} from "@earendil-works/pi-coding-agent";
|
|
@@ -56,11 +62,14 @@ import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all";
|
|
|
56
62
|
|
|
57
63
|
```typescript
|
|
58
64
|
const cwd = "/path/to/project";
|
|
65
|
+
const agentDir = getAgentDir();
|
|
59
66
|
const modelRuntime = await ModelRuntime.create();
|
|
67
|
+
const resourceLoader = new DefaultResourceLoader({ cwd, agentDir });
|
|
68
|
+
await resourceLoader.reload(); // a loader you pass in is not reloaded for you
|
|
60
69
|
|
|
61
70
|
const { session } = await createAgentSession({
|
|
62
71
|
cwd,
|
|
63
|
-
agentDir
|
|
72
|
+
agentDir,
|
|
64
73
|
modelRuntime,
|
|
65
74
|
model: getBuiltinModel("anthropic", "claude-opus-4-8"),
|
|
66
75
|
thinkingLevel: "medium", // off | minimal | low | medium | high | xhigh | max
|
|
@@ -69,9 +78,8 @@ const { session } = await createAgentSession({
|
|
|
69
78
|
],
|
|
70
79
|
tools: ["read", "bash", "edit", "write"],
|
|
71
80
|
customTools: [/* defineTool(...) entries */],
|
|
72
|
-
resourceLoader
|
|
81
|
+
resourceLoader,
|
|
73
82
|
sessionManager: SessionManager.inMemory(),
|
|
74
|
-
shouldStopAfterTurn: (state) => state.turnCount >= 5,
|
|
75
83
|
});
|
|
76
84
|
```
|
|
77
85
|
|
|
@@ -81,12 +89,61 @@ Notes:
|
|
|
81
89
|
- Pi 0.68.0 changed the SDK `tools` option from `Tool[]` to a `string[]` allowlist of built-in, extension, and custom tool names. Use `noTools: "builtin"` to disable built-ins while keeping extension/custom tools enabled, or `noTools: "all"` for none.
|
|
82
90
|
- `customTools` accepts `ToolDefinition[]`. Build them with `defineTool({...})` for full TypeScript inference.
|
|
83
91
|
- The `create*Tool(cwd)` factories still exist for code that needs explicit `AgentTool` instances (e.g. when wiring tools into pi-agent-core directly), but they are no longer the value passed to `createAgentSession({ tools })`.
|
|
84
|
-
- `DefaultResourceLoader` loads extensions, skills, prompt templates, themes, and context files. Replace it to drive resource discovery from custom sources (and it must implement `loadProjectContextFiles()` if you want `AGENTS.md`/`CLAUDE.md` discovery; that helper is also exported standalone).
|
|
85
|
-
-
|
|
92
|
+
- `DefaultResourceLoader` requires `{ cwd, agentDir }` and loads extensions, skills, prompt templates, themes, and context files. Replace it to drive resource discovery from custom sources (and it must implement `loadProjectContextFiles()` if you want `AGENTS.md`/`CLAUDE.md` discovery; that helper is also exported standalone).
|
|
93
|
+
- **Breaking (Pi 0.87.0):** pi-agent-core's `shouldStopAfterTurn` option was removed (it was never a `createAgentSession()` option). On a low-level `Agent`, set `finishTurn` instead: it runs after the assistant message and tool results are finalized but before `turn_end`, also for error/aborted responses. Return `{ action: "end" }` to stop, `{ action: "continue" }` to ensure one more request, or `undefined` for normal scheduling — guard hard exits first: `if (turn.message.stopReason === "error" || turn.message.stopReason === "aborted") return;`. Inside `createAgentSession()`, use an extension's actionable `turn_end` / `agent_before_settle` boundaries (see `references/extensions.md`).
|
|
94
|
+
|
|
95
|
+
## Built-in Extensions: Codemode, Tool Search, MCP
|
|
96
|
+
|
|
97
|
+
The CLI loads `codemode`, `tool_search`, MCP, and llama.cpp as built-in extensions. SDK sessions load none of them. Opt in through the resource loader (Pi 0.99.0, [example](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/examples/sdk/14-codemode-mcp.ts)):
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import {
|
|
101
|
+
createAgentSession,
|
|
102
|
+
createCodemodeExtension,
|
|
103
|
+
createMcpExtension,
|
|
104
|
+
createToolSearchExtension,
|
|
105
|
+
DefaultResourceLoader,
|
|
106
|
+
getAgentDir,
|
|
107
|
+
SessionManager,
|
|
108
|
+
SettingsManager,
|
|
109
|
+
} from "@earendil-works/pi-coding-agent";
|
|
110
|
+
|
|
111
|
+
const cwd = process.cwd();
|
|
112
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
113
|
+
cwd,
|
|
114
|
+
agentDir: getAgentDir(),
|
|
115
|
+
extensionFactories: [
|
|
116
|
+
createCodemodeExtension({ mode: "on" }), // options override codemode.mode / inlineBudget
|
|
117
|
+
createToolSearchExtension(),
|
|
118
|
+
createMcpExtension(), // reads mcp.json like the CLI (project file only when trusted)
|
|
119
|
+
],
|
|
120
|
+
});
|
|
121
|
+
await resourceLoader.reload();
|
|
122
|
+
|
|
123
|
+
const settingsManager = SettingsManager.create(cwd);
|
|
124
|
+
settingsManager.applyOverrides({ defaultTools: ["+codemode", "+tool_search"] });
|
|
125
|
+
|
|
126
|
+
const { session } = await createAgentSession({
|
|
127
|
+
resourceLoader,
|
|
128
|
+
settingsManager,
|
|
129
|
+
sessionManager: SessionManager.inMemory(),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await session.bindExtensions({}); // emits session_start; MCP connects here, in the background
|
|
134
|
+
await session.prompt("Use codemode to count the TypeScript files in src/.");
|
|
135
|
+
} finally {
|
|
136
|
+
session.dispose();
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
- `codemode` and `tool_search` are registered inactive. Enable them with `defaultTools` (`+name` keeps the other defaults), or let MCP activate them for servers that need them. Passing `tools: [...]` instead restricts the session to the named tools, which hides MCP tools.
|
|
141
|
+
- Extension `session_start` handlers run only when the host calls `session.bindExtensions()`. Without it, MCP servers never connect.
|
|
142
|
+
- Inline extensions can be named `{ name, factory, replaceable?: true, builtin?: true }`. `replaceable` drops the extension when another one registers the same tool, command, or flag name. `builtin` supplies the code for `builtin:<name>`: it loads by default, is listed in `pi config`, is disabled by `-builtin:<name>` or `noExtensions`, and loads after project trust (so it cannot handle `project_trust`).
|
|
86
143
|
|
|
87
144
|
## Models & Auth (`ModelRuntime`)
|
|
88
145
|
|
|
89
|
-
`ModelRuntime` (Pi 0.80.8) implements the pi-ai `Models` interface and owns credential storage. Auth resolution priority: runtime overrides (`setRuntimeApiKey`, not persisted) → stored credentials in `auth.json` →
|
|
146
|
+
`ModelRuntime` (Pi 0.80.8) implements the pi-ai `Models` interface and owns credential storage. Auth resolution priority: runtime overrides (`setRuntimeApiKey`, not persisted) → stored credentials in `auth.json` → `apiKey` from `models.json` or an extension registration → the provider's environment variables or ambient cloud credentials.
|
|
90
147
|
|
|
91
148
|
```typescript
|
|
92
149
|
import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
|
|
@@ -116,6 +173,8 @@ const inMemory = await ModelRuntime.create({ credentials: new InMemoryCredential
|
|
|
116
173
|
|
|
117
174
|
Pi 0.84.x made catalog refresh cancellation-aware: `refresh(options?)` accepts `{ providers?, allowNetwork?, force?, signal? }` and returns a `ModelsRefreshResult` (`{ errors, aborted }`) instead of discarding provider errors; `create({ signal })`, `login()`, `logout()`, and the now-async `setRuntimeApiKey()/removeRuntimeApiKey()` accept optional abort signals too (unbounded when omitted — applications own deadline policy). Credential mutations resolve once local state is consistent, without waiting for remote freshness; if that synchronization fails they reject with the exported `CredentialSynchronizationError` (inspect `providerId`, `operation`, `credential`, `cause` rather than retrying blindly). A failed refresh never undoes a committed credential change, and each `refresh()` starts a fresh generation so stale refreshes cannot publish afterward.
|
|
118
175
|
|
|
176
|
+
Non-chat models (Pi 0.99.0): `getModelsOfType(type, provider?)`, `getModelOfType(type, provider, id)`, `getAvailableOfType()`, `getAllModels()`, and `getAllAvailable()` cover `"chat"`, `"image"`, and `"classifier"` entries; `getModels()` stays chat-only. `modelRuntime.classify(model, context, options?)` and `modelRuntime.generateImages(model, context, options?)` resolve auth at request time and return error results (`stopReason: "error" | "aborted"`) instead of rejecting. `modelRuntime.registerVirtualModel(definition)` registers a routing model without an extension. See `references/providers.md#model-types`.
|
|
177
|
+
|
|
119
178
|
To match CLI model parsing, use the exported resolver helpers (Pi 0.80.4):
|
|
120
179
|
|
|
121
180
|
```typescript
|
|
@@ -139,11 +198,11 @@ await session.prompt("Review the current directory");
|
|
|
139
198
|
await session.prompt("Stop and do this instead", { streamingBehavior: "steer" });
|
|
140
199
|
await session.prompt("After you finish, also check tests", { streamingBehavior: "followUp" });
|
|
141
200
|
|
|
142
|
-
await session.steer("Use a smaller diff");
|
|
201
|
+
const disposition = await session.steer("Use a smaller diff"); // "queued" | "handled"
|
|
143
202
|
await session.followUp("Summarize the changes afterward");
|
|
144
203
|
```
|
|
145
204
|
|
|
146
|
-
`prompt()` expands file-based prompt templates. During active streaming, calling it without `streamingBehavior` throws.
|
|
205
|
+
`prompt()` expands file-based prompt templates and resolves after an accepted run finishes. During active streaming, calling it without `streamingBehavior` throws. Since Pi 0.99.0 `steer()` and `followUp()` return a disposition: `"queued"` (also after an extension transformed the input) or `"handled"` (an extension consumed it, so nothing was queued). Both throw for extension commands (`/name`); send those through `prompt()`, which runs them immediately, even while streaming. Read state through `session.messages`, `session.model`, `session.thinkingLevel`, `session.systemPrompt`, and `session.getActiveToolNames()`.
|
|
147
206
|
|
|
148
207
|
## Session Runtime
|
|
149
208
|
|
|
@@ -215,10 +274,12 @@ Use `runtime.newSession()`, `runtime.switchSession()`, and `runtime.fork()` for
|
|
|
215
274
|
Useful state:
|
|
216
275
|
|
|
217
276
|
- `session.sessionFile` / `session.sessionId`
|
|
218
|
-
- `session.agent.state.messages`
|
|
277
|
+
- `session.agent.state.messages` — read-only view in practice: since Pi 0.87.0 `SessionManager` is canonical for provider context, so assigning it no longer changes future requests
|
|
219
278
|
- `session.model`
|
|
220
279
|
- `session.thinkingLevel`
|
|
221
280
|
|
|
281
|
+
To change model context from the SDK (Pi 0.87.0): restore external history with `SessionManager.inMemory(cwd, { id }, entries)` (Pi 0.85.0), navigate with `session.navigateTree()`, or append through `session.sessionManager` and call `session.refreshContext()`. `sessionManager.appendContextEdit(entryId, null)` omits one message from future provider context (a `{ content }` replacement rewrites it) without touching raw history, usage, or UI; `appendCompaction(summary, null, tokensBefore)` records a retain-none compaction. `SessionEntry` now includes `ContextEditEntry` (`type: "context_edit"`), so exhaustive entry switches must handle it.
|
|
282
|
+
|
|
222
283
|
## Events
|
|
223
284
|
|
|
224
285
|
```typescript
|
|
@@ -229,14 +290,14 @@ session.subscribe((event) => {
|
|
|
229
290
|
process.stdout.write(event.assistantMessageEvent.delta);
|
|
230
291
|
}
|
|
231
292
|
break;
|
|
232
|
-
case "tool_execution_start":
|
|
293
|
+
case "tool_execution_start": // nested calls (ctx.executeTool, codemode) carry parentToolCallId
|
|
233
294
|
case "tool_execution_update":
|
|
234
295
|
case "tool_execution_end":
|
|
235
296
|
case "agent_start":
|
|
236
297
|
case "agent_end": // one low-level run; may be followed by retry/compaction/queued follow-ups
|
|
237
298
|
case "agent_settled": // fully settled — no automatic continuation left (Pi 0.80.4)
|
|
238
299
|
case "turn_start":
|
|
239
|
-
case "turn_end":
|
|
300
|
+
case "turn_end": // Pi 0.87.0: carries turnIndex, messageEntryId, toolResultEntryIds
|
|
240
301
|
case "queue_update":
|
|
241
302
|
case "compaction_start":
|
|
242
303
|
case "compaction_end":
|
|
@@ -260,9 +321,11 @@ Pi 0.84.0 added transport-neutral remote-session client APIs: `@earendil-works/p
|
|
|
260
321
|
|
|
261
322
|
## RPC Mode
|
|
262
323
|
|
|
263
|
-
`pi --mode rpc` speaks newline-delimited JSON over stdio. Additions since Pi 0.79:
|
|
324
|
+
`pi --mode rpc` speaks newline-delimited JSON over stdio ([rpc.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/rpc.md), [rpc-commands.md](https://github.com/earendil-works/pi/blob/v0.99.1/packages/coding-agent/docs/rpc-commands.md)). Additions since Pi 0.79:
|
|
264
325
|
|
|
326
|
+
- Input dispositions (Pi 0.99.0): a successful `prompt` response carries `data.disposition` — `"started"` (a run started), `"queued"` (queued during a run), or `"handled"` (an extension command or input handler consumed it). `steer` and `follow_up` responses carry `"queued"` or `"handled"`. On `"handled"` no run started for that input: do not wait for `agent_settled`. Otherwise keep consuming events and wait for `agent_settled`, not `agent_end`. Failures after acceptance arrive as events, never as a second response. `RpcClient.prompt(message, images?, streamingBehavior?)`, `steer()`, and `followUp()` return the disposition.
|
|
265
327
|
- `agent_settled` event (Pi 0.80.4) fires when a run is fully settled — no automatic retry, compaction retry, or queued continuation remains; `agent_end` now carries `willRetry`. `set_thinking_level` accepts `"max"` where the model supports it.
|
|
328
|
+
- `clear_queue` (Pi 0.84.4) returns and removes queued steering and follow-up messages; direct RPC `steer`/`follow_up` commands pass through extension `input` handlers since Pi 0.86.0.
|
|
266
329
|
- `get_entries` / `get_tree` (Pi 0.80.3) read session entries and tree snapshots over RPC; `get_available_thinking_levels` (Pi 0.81.0) lists the current model's supported thinking levels.
|
|
267
330
|
- Direct RPC `bash` commands stream `bash_execution_update` chunks correlated by request `id` (Pi 0.82.0); include an `id` on the command to associate them.
|
|
268
331
|
- `compact` responses include summary `usage`; `get_tokens`/`get_status` totals cover assistant messages, tool-reported usage, and summaries (Pi 0.81.0).
|
|
@@ -283,13 +346,18 @@ Pi 0.84.0 added transport-neutral remote-session client APIs: `@earendil-works/p
|
|
|
283
346
|
To name an inline factory in the startup Extensions list (instead of `<inline:1>`), wrap it in an `InlineExtension` (Pi 0.80.4):
|
|
284
347
|
|
|
285
348
|
```typescript
|
|
286
|
-
import type
|
|
349
|
+
import { DefaultResourceLoader, getAgentDir, type InlineExtension } from "@earendil-works/pi-coding-agent";
|
|
287
350
|
|
|
288
351
|
const myProvider: InlineExtension = {
|
|
289
352
|
name: "my-provider",
|
|
290
353
|
factory: (pi) => { /* ... */ },
|
|
291
354
|
};
|
|
292
|
-
const loader = new DefaultResourceLoader({
|
|
355
|
+
const loader = new DefaultResourceLoader({
|
|
356
|
+
cwd: process.cwd(),
|
|
357
|
+
agentDir: getAgentDir(),
|
|
358
|
+
extensionFactories: [myProvider],
|
|
359
|
+
});
|
|
360
|
+
await loader.reload();
|
|
293
361
|
```
|
|
294
362
|
|
|
295
363
|
Bare factory functions are still accepted.
|
|
@@ -18,6 +18,7 @@ Edit JSON directly or use `/settings` for common interactive options.
|
|
|
18
18
|
"defaultThinkingLevel": "medium",
|
|
19
19
|
"hideThinkingBlock": false,
|
|
20
20
|
"showCacheMissNotices": false,
|
|
21
|
+
"cacheWarming": "streaming",
|
|
21
22
|
"thinkingBudgets": {
|
|
22
23
|
"minimal": 1024,
|
|
23
24
|
"low": 4096,
|
|
@@ -29,13 +30,15 @@ Edit JSON directly or use `/settings` for common interactive options.
|
|
|
29
30
|
|
|
30
31
|
- Thinking levels: `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"` — `max` (Pi 0.80.6) is an opt-in level above `xhigh`, exposed only when the selected model supports it (some models, e.g. GPT-5.6, expose both). Custom themes can define `thinkingMax`; existing themes fall back to `thinkingXhigh`.
|
|
31
32
|
- `hideThinkingBlock` hides visible thinking output in the UI
|
|
32
|
-
- `showCacheMissNotices` (Pi 0.80.4) shows transcript notices for significant prompt-cache misses
|
|
33
|
+
- `showCacheMissNotices` (Pi 0.80.4) shows transcript notices for significant prompt-cache misses; newer releases also report successful cache warming, compaction/branch-summary usage, and provider recovery
|
|
34
|
+
- `cacheWarming` (Pi 0.86.0, global only): `"off"`, `"streaming"` (default — keep eligible caches alive during long tool runs), or `"idle"` (also between runs). Pi warms only when the model declares a `promptCache` lifetime and it estimates at least $0.05 in avoided cache-miss cost; refresh usage counts toward session totals but never enters model context. `/session` shows the next decision; extensions can override via `cache_warming_decision`
|
|
35
|
+
- `/model` and `/thinking` (Pi 0.84.3) selections are session-scoped; press `Ctrl+S` in the selector to save as the global default
|
|
33
36
|
|
|
34
37
|
## UI & Display
|
|
35
38
|
|
|
36
39
|
```json
|
|
37
40
|
{
|
|
38
|
-
"theme": "
|
|
41
|
+
"theme": "system",
|
|
39
42
|
"quietStartup": false,
|
|
40
43
|
"collapseChangelog": false,
|
|
41
44
|
"doubleEscapeAction": "tree",
|
|
@@ -51,7 +54,7 @@ Edit JSON directly or use `/settings` for common interactive options.
|
|
|
51
54
|
- `doubleEscapeAction`: `"tree"`, `"fork"`, or `"none"`
|
|
52
55
|
- `treeFilterMode`: `"default"`, `"no-tools"`, `"user-only"`, `"labeled-only"`, or `"all"`
|
|
53
56
|
- In `/tree`, `Shift+T` toggles timestamps on entry labels
|
|
54
|
-
- `theme`
|
|
57
|
+
- `theme` defaults to `"system"` (Pi 0.99.0), which builds Pi's colors from the terminal's reported foreground, background, and ANSI palette and follows light/dark switches; `system` is a reserved theme name. `dark` and `light` are the other built-ins. `"light-name/dark-name"` (Pi 0.79.7) switches themes with the terminal color scheme; `/` is reserved in theme names for this. `--use-theme <name[/name]>` (Pi 0.84.2) sets a per-run initial theme without changing saved settings.
|
|
55
58
|
- `outputPad` (Pi 0.80.3, `0` or `1`) sets horizontal padding for user messages, assistant messages, and thinking blocks; custom message renderers receive it as `options.outputPad` (Pi 0.82.1).
|
|
56
59
|
- `externalEditor` (Pi 0.80.3) sets the `Ctrl+G` external editor command and takes precedence over `$VISUAL`/`$EDITOR`.
|
|
57
60
|
|
|
@@ -63,13 +66,17 @@ Experimental since Pi 0.84.0:
|
|
|
63
66
|
{
|
|
64
67
|
"tuiMode": "regular",
|
|
65
68
|
"fullscreenExitOutput": "transcript",
|
|
66
|
-
"fullscreenScrollbar": "auto"
|
|
69
|
+
"fullscreenScrollbar": "auto",
|
|
70
|
+
"fullscreenCopyOnSelect": true,
|
|
71
|
+
"fullscreenWheelScrollLines": "auto"
|
|
67
72
|
}
|
|
68
73
|
```
|
|
69
74
|
|
|
70
75
|
- `tuiMode`: `"regular"` (default) or `"fullscreen"`; `/settings` switches at runtime, `--tui-mode` overrides per run
|
|
71
76
|
- `fullscreenExitOutput` (Pi 0.84.2): `"transcript"` prints the final transcript on exit; `"resume-hint"` restores the previous screen and prints only a resume hint
|
|
72
77
|
- `fullscreenScrollbar`: `"auto"` shows while scrolling, `"always"` reserves the rightmost column, `"hidden"`
|
|
78
|
+
- `fullscreenCopyOnSelect` (Pi 0.84.4, default `true`): when `false`, `Ctrl+X` copies the active selection (falling back to the last assistant message)
|
|
79
|
+
- `fullscreenWheelScrollLines` (Pi 0.99.0): `"auto"` (default) or 1–100 lines per mouse-wheel event. `"auto"` moves one line per event in local macOS terminals and accelerates fast spins elsewhere (up to 6 lines); Alt+wheel moves five times as far
|
|
73
80
|
- Fullscreen keeps editor/status/widgets/footer docked while the transcript scrolls independently; transcript search (`Ctrl+Shift+F`, Pi 0.84.2), page/half-page/line scrolling, and marked-message navigation are configurable via `tui.altScreen.*` keybindings
|
|
74
81
|
- Theme tokens `scrollbarThumb`, `searchMatchBg`, `searchMatchText` are optional additions with fallbacks (`selectedBg`, `selectedBg`, `text`)
|
|
75
82
|
|
|
@@ -80,7 +87,10 @@ Experimental since Pi 0.84.0:
|
|
|
80
87
|
"compaction": {
|
|
81
88
|
"enabled": true,
|
|
82
89
|
"reserveTokens": 16384,
|
|
83
|
-
"keepRecentTokens": 20000
|
|
90
|
+
"keepRecentTokens": 20000,
|
|
91
|
+
"modelOverrides": {
|
|
92
|
+
"anthropic/claude-opus-5-5": { "reserveTokens": 32768 }
|
|
93
|
+
}
|
|
84
94
|
},
|
|
85
95
|
"branchSummary": {
|
|
86
96
|
"reserveTokens": 16384,
|
|
@@ -90,6 +100,7 @@ Experimental since Pi 0.84.0:
|
|
|
90
100
|
"enabled": true,
|
|
91
101
|
"maxRetries": 3,
|
|
92
102
|
"baseDelayMs": 2000,
|
|
103
|
+
"maxAgentDelayMs": 60000,
|
|
93
104
|
"provider": {
|
|
94
105
|
"timeoutMs": 3600000,
|
|
95
106
|
"maxRetries": 0,
|
|
@@ -101,7 +112,8 @@ Experimental since Pi 0.84.0:
|
|
|
101
112
|
|
|
102
113
|
Notes:
|
|
103
114
|
|
|
104
|
-
- `
|
|
115
|
+
- `compaction.modelOverrides` (Pi 0.86.0) sets per-model `reserveTokens`/`keepRecentTokens` keyed by exact `provider/modelId`; each value falls back to the ordinary compaction setting, then the default.
|
|
116
|
+
- `retry.maxRetries` (default `3`) and `retry.baseDelayMs` (default `2000`) govern Pi's own agent-level retry with exponential backoff, capped per attempt by `retry.maxAgentDelayMs` (default `60000`, Pi 0.86.0).
|
|
105
117
|
- `retry.provider.*` controls the underlying provider SDK's request timeout and retry/backoff — useful for slow local LLMs and flaky proxies. `retry.provider.timeoutMs` sets the SDK request timeout; `retry.provider.maxRetries` defaults to `0` (keep it there unless you need SDK-level retries, since raising it can let provider retries swallow usage-limit errors before Pi handles them).
|
|
106
118
|
- `retry.provider.maxRetryDelayMs` (default `60000`) caps how long a server-requested retry delay can be before the request fails immediately with an informative error; set to `0` to disable the cap. The old top-level `retry.maxDelayMs` was renamed to this and is auto-migrated.
|
|
107
119
|
- `branchSummary.skipPrompt` skips the confirmation step when navigating with `/tree`.
|
|
@@ -133,7 +145,10 @@ Notes:
|
|
|
133
145
|
"showImages": true,
|
|
134
146
|
"clearOnShrink": false,
|
|
135
147
|
"showTerminalProgress": false,
|
|
136
|
-
"imageWidthCells": 60
|
|
148
|
+
"imageWidthCells": 60,
|
|
149
|
+
"hyperlinks": "auto",
|
|
150
|
+
"images": "auto",
|
|
151
|
+
"trueColor": "auto"
|
|
137
152
|
},
|
|
138
153
|
"images": {
|
|
139
154
|
"autoResize": true,
|
|
@@ -149,6 +164,7 @@ Notes:
|
|
|
149
164
|
- `npmCommand` is argv-style and is used for npm lookup/install operations, including git-package installs. User-scoped npm packages install under `~/.pi/agent/npm/`; project-scoped npm packages install under `.pi/npm/`.
|
|
150
165
|
- `terminal.showTerminalProgress` (default `false` since Pi 0.70.0) toggles OSC 9;4 progress reporting in supporting terminals (iTerm2, WezTerm, Windows Terminal, Kitty).
|
|
151
166
|
- `terminal.imageWidthCells` (Pi 0.68.1) caps inline tool-output image width in terminal cells.
|
|
167
|
+
- `terminal.hyperlinks` / `terminal.images` (`"kitty" | "iterm2" | "auto" | false`) / `terminal.trueColor` (Pi 0.84.4) override capability detection (settings win over `PI_HYPERLINKS`, `PI_IMAGE_PROTOCOL`, `PI_TRUE_COLOR`). Only force what the full terminal path (tmux, SSH) supports.
|
|
152
168
|
|
|
153
169
|
## Warnings & Telemetry
|
|
154
170
|
|
|
@@ -167,7 +183,6 @@ Notes:
|
|
|
167
183
|
{
|
|
168
184
|
"sessionDir": ".pi/sessions",
|
|
169
185
|
"enabledModels": ["claude-*", "gpt-4o", "gemini-2*"],
|
|
170
|
-
"defaultTools": ["bash", "read", "edit"],
|
|
171
186
|
"markdown": {
|
|
172
187
|
"codeBlockIndent": " ",
|
|
173
188
|
"mermaid": "streaming"
|
|
@@ -177,12 +192,25 @@ Notes:
|
|
|
177
192
|
|
|
178
193
|
When multiple sources specify a session directory, `--session-dir` takes precedence over `sessionDir` in settings. Pi 0.65.0 removed the old `session_directory` extension/settings hook.
|
|
179
194
|
|
|
180
|
-
- `defaultTools` (Pi 0.84.2) picks the built-in tools enabled at startup (global or per project; project replaces global). An empty array starts with no built-in tools while keeping extension/SDK custom tools. `--tools` remains a strict allowlist for all tools, `--exclude-tools` filters the resulting list.
|
|
181
195
|
- `markdown.mermaid` (Pi 0.84.0): `"off"`, `"final"`, or `"streaming"` — themed Unicode rendering of supported Mermaid diagrams in interactive messages. Pi 0.84.0 also renders terminal-friendly Unicode for LaTeX expressions.
|
|
182
196
|
|
|
197
|
+
## Tools & Codemode
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"defaultTools": ["+codemode"],
|
|
202
|
+
"codemode": { "mode": "on", "inlineBudget": 3000 }
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- `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
|
+
- `--tools` replaces the whole selection for one run and does not accept `+`/`-`; `--exclude-tools` filters the result.
|
|
208
|
+
- `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
|
+
- 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
|
+
|
|
183
211
|
## Project Trust
|
|
184
212
|
|
|
185
|
-
Since Pi 0.79.0, project-local settings, resources, and packages (`.pi/` settings/extensions/skills/prompts/themes
|
|
213
|
+
Since Pi 0.79.0, project-local settings, resources, and packages (`.pi/` settings/extensions/skills/prompts/themes/`mcp.json`, project packages) only load after the project is trusted. `AGENTS.md` / `CLAUDE.md` context files load regardless of trust. Trust is a loading gate, not a sandbox.
|
|
186
214
|
|
|
187
215
|
```json
|
|
188
216
|
{
|
|
@@ -224,6 +252,7 @@ Notes:
|
|
|
224
252
|
- `packages` loads npm/git/local Pi packages.
|
|
225
253
|
- `extensions`, `skills`, `prompts`, and `themes` are for local files/directories.
|
|
226
254
|
- `enableSkillCommands` controls `/skill:name` registration.
|
|
255
|
+
- Built-in extensions (Pi 0.99.0) are `builtin:mcp`, `builtin:llama.cpp`, `builtin:codemode`, and `builtin:tool-search`. They load by default; `"extensions": ["-builtin:mcp"]` disables one, and a project `+builtin:<name>`/`-builtin:<name>` entry overrides the user setting. `pi config` lists them under Built-in.
|
|
227
256
|
|
|
228
257
|
## Include / Exclude Patterns
|
|
229
258
|
|
|
@@ -252,6 +281,7 @@ Run `pi --help` for the authoritative list. Most commonly:
|
|
|
252
281
|
pi [options] [@files...] [messages...]
|
|
253
282
|
|
|
254
283
|
# package commands: install/remove/uninstall/update/list/config (all support -l for project scope)
|
|
284
|
+
# MCP commands: pi mcp add|remove|list|login|logout (see references/mcp.md)
|
|
255
285
|
|
|
256
286
|
# modes
|
|
257
287
|
-p, --print single-shot text output
|
|
@@ -277,15 +307,16 @@ pi [options] [@files...] [messages...]
|
|
|
277
307
|
--no-session ephemeral
|
|
278
308
|
|
|
279
309
|
# tools and resources
|
|
280
|
-
--tools <list>, -t name allowlist (read,bash,edit,write,grep,find,ls,...)
|
|
310
|
+
--tools <list>, -t name allowlist (read,bash,edit,write,grep,find,ls,codemode,...)
|
|
281
311
|
--exclude-tools <list>, -xt disable specific built-in/extension/custom tools, keep the rest
|
|
282
312
|
--no-tools, -nt disable everything
|
|
283
313
|
--no-builtin-tools, -nbt disable built-ins, keep extension/custom
|
|
284
|
-
-e, --extension <source> repeatable; npm/git/path
|
|
314
|
+
-e, --extension <source> repeatable; npm/git/path, or builtin:<name>
|
|
285
315
|
--skill <path> repeatable
|
|
286
316
|
--prompt-template <path> repeatable
|
|
287
317
|
--theme <path> repeatable
|
|
288
|
-
--no-extensions
|
|
318
|
+
--no-extensions, -ne also disables built-in extensions (pi -ne -e builtin:mcp keeps only MCP)
|
|
319
|
+
--no-skills / --no-prompt-templates / --no-themes
|
|
289
320
|
--no-context-files, -nc skip AGENTS.md / CLAUDE.md discovery
|
|
290
321
|
|
|
291
322
|
# trust and network
|
|
@@ -294,13 +325,15 @@ pi [options] [@files...] [messages...]
|
|
|
294
325
|
--offline disable startup network operations (same as PI_OFFLINE=1)
|
|
295
326
|
|
|
296
327
|
# prompts and misc
|
|
297
|
-
--system-prompt <text>
|
|
298
|
-
--append-system-prompt <text>
|
|
328
|
+
--system-prompt <text|path> replace default
|
|
329
|
+
--append-system-prompt <text|path> repeatable; appended with double newlines
|
|
299
330
|
--tui-mode regular|fullscreen experimental fullscreen TUI (Pi 0.84.0)
|
|
300
331
|
--use-theme <name[/name]> per-run initial theme, saved settings untouched (Pi 0.84.2)
|
|
301
332
|
--verbose
|
|
302
333
|
```
|
|
303
334
|
|
|
335
|
+
Use `/bug [description]` (Pi 0.86.0) to report a problem with redacted diagnostics, optionally the transcript or a model-written summary; it uploads to Radius or exports a ZIP. Crashes are recorded in `~/.pi/agent/crashes.json` and attached to the next report.
|
|
336
|
+
|
|
304
337
|
## Environment Variables
|
|
305
338
|
|
|
306
339
|
| Variable | Effect |
|
|
@@ -316,7 +349,7 @@ pi [options] [@files...] [messages...]
|
|
|
316
349
|
| `PI_SHARE_VIEWER_URL` | Override the base URL used by `/share` |
|
|
317
350
|
| `PI_HARDWARE_CURSOR` | `1` shows the hardware cursor (see terminal-setup docs) |
|
|
318
351
|
| `PI_TUI_ESC_TIMEOUT` | Ms to wait after a lone ESC before treating it as Escape (default 100 over SSH, 10 otherwise; Pi 0.84.2) — raise it if Alt-key input is misread |
|
|
319
|
-
| `PI_EXPERIMENTAL` | `1` enables experimental features
|
|
352
|
+
| `PI_EXPERIMENTAL` | `1` enables experimental features (strict JSON-schema sampling for built-in tools no longer needs it since Pi 0.86.0) |
|
|
320
353
|
| `AI_AGENT=pi` / `PI_CODING_AGENT=true` | Set automatically by CLI/RPC entry points so child processes can detect Pi (generic vs Pi-specific marker) |
|
|
321
354
|
| `VISUAL`, `EDITOR` | External editor for `Ctrl+G` (the `externalEditor` setting takes precedence) |
|
|
322
355
|
| Provider API key env vars | See `references/providers.md` |
|