@kingingwang/pi-windows-x64 1.0.4 → 1.2.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.
Files changed (45) hide show
  1. package/bin/CHANGELOG.md +115 -0
  2. package/bin/docs/cli-integration.md +1 -1
  3. package/bin/docs/cli.md +5 -5
  4. package/bin/docs/codemode.md +24 -4
  5. package/bin/docs/compaction.md +2 -2
  6. package/bin/docs/environment-variables.md +3 -1
  7. package/bin/docs/extensions.md +7 -4
  8. package/bin/docs/json.md +3 -3
  9. package/bin/docs/keybindings.md +1 -0
  10. package/bin/docs/llama-cpp.md +8 -1
  11. package/bin/docs/mcp.md +1 -1
  12. package/bin/docs/message-types.md +22 -4
  13. package/bin/docs/models.md +5 -1
  14. package/bin/docs/providers.md +47 -0
  15. package/bin/docs/settings.md +6 -4
  16. package/bin/docs/terminal-setup.md +14 -0
  17. package/bin/docs/themes.md +4 -2
  18. package/bin/docs/tui.md +1 -1
  19. package/bin/examples/README.md +0 -3
  20. package/bin/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  21. package/bin/examples/extensions/custom-provider-anthropic/package.json +1 -1
  22. package/bin/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  23. package/bin/examples/extensions/dynamic-resources/dynamic.json +1 -1
  24. package/bin/examples/extensions/gondolin/package-lock.json +2 -2
  25. package/bin/examples/extensions/gondolin/package.json +1 -1
  26. package/bin/examples/extensions/overlay-test.ts +2 -2
  27. package/bin/examples/extensions/plan-mode/index.ts +6 -2
  28. package/bin/examples/extensions/sandbox/package-lock.json +2 -2
  29. package/bin/examples/extensions/sandbox/package.json +1 -1
  30. package/bin/examples/extensions/with-deps/package-lock.json +2 -2
  31. package/bin/examples/extensions/with-deps/package.json +1 -1
  32. package/bin/export-html/tool-renderer.d.ts.map +1 -1
  33. package/bin/export-html/tool-renderer.js +2 -0
  34. package/bin/export-html/tool-renderer.js.map +1 -1
  35. package/bin/package.json +10 -21
  36. package/bin/pi.exe +2 -2
  37. package/bin/theme/dark.json +1 -1
  38. package/bin/theme/light.json +1 -1
  39. package/package.json +1 -1
  40. package/bin/examples/plugins/pi-example-plugin/README.md +0 -38
  41. package/bin/examples/plugins/pi-example-plugin/package.json +0 -10
  42. package/bin/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
  43. package/bin/examples/plugins/pi-example-plugin/src/session.ts +0 -24
  44. package/bin/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
  45. package/bin/theme/theme-schema.json +0 -361
package/bin/CHANGELOG.md CHANGED
@@ -1,5 +1,120 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0] - 2026-10-12
4
+
5
+ ### New Features
6
+
7
+ - **Per-key OpenRouter model lists**: with an OpenRouter key, Pi lists only the models that key may use, with the key's context size, max output, and prices, including regional endpoints set in `models.json`. See [OpenRouter](docs/providers.md#openrouter).
8
+ - **Published configuration schemas**: add `$schema` to `settings.json`, `models.json`, keybindings, and themes for editor validation and completion. See [Settings](docs/settings.md), [Models](docs/models.md), [Keybindings](docs/keybindings.md), and [Themes](docs/themes.md).
9
+ - **Clipboard copies over remote sessions**: `PI_CLIPBOARD_OSC52=1` sends copied text to a remote client's clipboard when Pi cannot detect the remote session, such as a multiplexer attached from another machine. See [Pi Process Configuration](docs/environment-variables.md#pi-process-configuration).
10
+ - **Codemode improvements**: tool declarations show input constraints such as integer, minimum/maximum, and default, and codemode rows show elapsed and total time. See [Codemode](docs/codemode.md).
11
+ - **`before_agent_start` for extension-triggered runs**: runs started by `pi.sendMessage(..., { triggerTurn: true })` go through the same setup as prompts. See [Events and concurrency](docs/extensions.md#events-and-concurrency).
12
+
13
+ ### Breaking Changes
14
+
15
+ - Published generated configuration schemas for models, settings, keybindings, and themes at `https://pi.dev/schemas/*.schema.json`. The theme schema moved to `https://pi.dev/schemas/theme.schema.json`; themes loaded during startup now receive the same schema validation as later reloads. Update theme `$schema` references, define reusable colors under `vars`, and remove unsupported metadata ([#9880](https://github.com/earendil-works/pi/pull/9880), [#10751](https://github.com/earendil-works/pi/pull/10751) by [@christianklotz](https://github.com/christianklotz))
16
+ - Changed `before_agent_start` to also fire for runs started by `pi.sendMessage(..., { triggerTurn: true })` while idle. `BeforeAgentStartEvent` is now a union on `messageType`: `"user"` events carry `prompt` and `images`, and `"custom"` events carry the starting `message` and no `prompt`. Messages returned by handlers are added to both kinds of run, and pending `deliverAs: "nextTurn"` messages are delivered with the custom-message run. Handlers that should act only on user prompts must check `event.messageType === "user"` ([#10267](https://github.com/earendil-works/pi/issues/10267), [#5581](https://github.com/earendil-works/pi/issues/5581), [#10739](https://github.com/earendil-works/pi/pull/10739) by [@davidbrai](https://github.com/davidbrai))
17
+ - Changed the session to count as busy while a run starts, including during `before_agent_start` handlers: `ctx.isIdle()` is false, `pi.sendMessage()` queues into the starting run instead of starting a second run that failed with "Agent is already processing", `pi.sendUserMessage()` requires `deliverAs`, and an abort requested meanwhile aborts the run once it starts ([#10739](https://github.com/earendil-works/pi/pull/10739) by [@davidbrai](https://github.com/davidbrai))
18
+ - Changed `pi mcp list --json` to report `resources: true` for servers that offer resources instead of `resources` and `resourceTemplates` counts; Pi no longer lists resources when connecting ([#10526](https://github.com/earendil-works/pi/issues/10526))
19
+
20
+ ### Added
21
+
22
+ - Added `reachesTools` to tool definitions, so tools that reach other tools (such as `codemode` and `tool_search`) declare which tools a call may reach; the MCP extension uses it to wait for the servers a call needs and to decide whether a replacement `codemode` or `tool_search` tool can reach MCP tools
23
+ - Added `aliases` to tool namespaces; codemode's `describeNamespace()` and the `namespace` option of `searchTools()` match them, and MCP namespaces use the server name
24
+ - Added per-key OpenRouter model lists: with an OpenRouter key, Pi lists only the chat models that key may use, with the key's context size, max output, and prices, including guardrails and regional endpoints set as the base URL in `models.json`. The list is saved and used offline ([#10353](https://github.com/earendil-works/pi/issues/10353), [#10672](https://github.com/earendil-works/pi/pull/10672) by [@davidbrai](https://github.com/davidbrai))
25
+ - Added `PI_CLIPBOARD_OSC52=1` to send copied text to the terminal with OSC 52 after the local clipboard write, so copies reach a remote client's clipboard when Pi cannot detect the remote session, such as multiplexer attaches from another machine or remote IDE daemons ([#10826](https://github.com/earendil-works/pi/issues/10826), [#10835](https://github.com/earendil-works/pi/issues/10835), [#10114](https://github.com/earendil-works/pi/issues/10114))
26
+ - Added elapsed time while a codemode script runs and its total time once finished to the codemode tool row, like bash rows ([#10708](https://github.com/earendil-works/pi/issues/10708))
27
+
28
+ ### Changed
29
+
30
+ - Changed the `showHardwareCursor` setting to use only the terminal cursor instead of also drawing Pi's reverse-video cursor
31
+ - Changed codemode tool declarations to show input constraints such as integer, minimum/maximum, length, pattern, and default as `@` tags on property comments, so scripts see them without the tool repeating them in descriptions ([#10707](https://github.com/earendil-works/pi/issues/10707))
32
+ - Changed codemode declarations of MCP tools to spell out the result type (`content`, `structuredContent`, `isError`) instead of referring to `CallToolResult<T>`, which `describeTool()`, `searchTools()`, and `ALL_TOOLS` showed without defining it; the codemode description no longer includes the shared MCP types
33
+ - Changed `serializeConversation()` (compaction, branch summaries, bug reports) to omit thinking blocks; replayed thinking is often not in the live context, so including it could make a summarization request larger than the context it summarizes and fail
34
+
35
+ ### Fixed
36
+
37
+ - Fixed codemode ignoring a `// @options:` line that is not the first line of the script, which ran the script without the requested `timeout_ms`; the call now fails with an error before the script runs ([#10631](https://github.com/earendil-works/pi/issues/10631))
38
+ - Fixed Mistral streams being aborted mid-generation (e.g. during long thinking) after the request timeout; the timeout now applies only to waiting for response headers ([#10609](https://github.com/earendil-works/pi/issues/10609))
39
+ - Fixed a symlinked `AGENTS.md` in a git worktree nested inside its main repo causing no project context file to load ([#10681](https://github.com/earendil-works/pi/issues/10681))
40
+ - Fixed the code mode tool description claiming every failed tool call rejects; a tool that declares an output schema resolves with its value on failure too (e.g. `bash` with a nonzero exit)
41
+ - Fixed runs started by `pi.sendMessage(..., { triggerTurn: true })` removing system prompt sections added in `before_agent_start` after their first tool call, which changed the prompt mid-run and broke the provider prompt cache twice per run ([#10267](https://github.com/earendil-works/pi/issues/10267), [#10739](https://github.com/earendil-works/pi/pull/10739) by [@davidbrai](https://github.com/davidbrai))
42
+ - Fixed codemode blocking the process for seconds to minutes after scripts with many `text()` calls; joining the output items was quadratic ([#10678](https://github.com/earendil-works/pi/issues/10678))
43
+ - Fixed print mode (`-p`) exiting 0 without a warning when the final response was cut off at the model's output token limit; it now prints the partial text, writes a warning to stderr, and exits 1 ([#10799](https://github.com/earendil-works/pi/issues/10799))
44
+ - Fixed `pi mcp login --help` and the CLI docs describing `--timeout` as only the wait for the browser; it limits the whole sign-in ([#10636](https://github.com/earendil-works/pi/issues/10636))
45
+ - Fixed `~` not being expanded in `!pattern`, `+path`, and `-path` entries of the `extensions`, `skills`, `prompts`, and `themes` settings, so entries like `!~/skills/pdf` matched nothing ([#10765](https://github.com/earendil-works/pi/issues/10765))
46
+ - Fixed MCP servers with resources registering no tools until every resource was listed; Pi no longer lists resources when connecting, so `/mcp` and `pi mcp list` show that a server offers resources instead of counting them ([#10526](https://github.com/earendil-works/pi/issues/10526))
47
+ - Fixed MCP tool calls failing when the server returns structured content that is not an object (an array, `null`, or a primitive, as MCP 2026-07-28 allows); codemode declarations of MCP tools without an output schema now type `structuredContent` as `unknown` instead of an object ([#10416](https://github.com/earendil-works/pi/issues/10416))
48
+ - Fixed reading or writing `auth.json` on Windows failing with `EPERM` when the previous lock folder was still being deleted; Pi now retries briefly before reporting the error ([#10801](https://github.com/earendil-works/pi/issues/10801))
49
+ - Fixed OpenRouter router models such as `openrouter/auto` showing a cost of -$1,000,000 per 1M tokens; OpenRouter's negative placeholder prices are now 0 ([#10672](https://github.com/earendil-works/pi/pull/10672) by [@davidbrai](https://github.com/davidbrai))
50
+ - Fixed Anthropic requests failing when the provider rejects the strict tool set for grammar size, tool count, or union limits; Pi now retries once with preferred tools non-strict ([#10409](https://github.com/earendil-works/pi/issues/10409))
51
+ - Fixed "The upstream provider returned an error" not being retried as a transient provider error ([#10842](https://github.com/earendil-works/pi/issues/10842))
52
+ - Fixed focused fullscreen overlays blocking mouse-wheel scrolling of the transcript ([#10769](https://github.com/earendil-works/pi/issues/10769))
53
+ - Fixed fragmented terminal color replies leaking hex values into the prompt and opening the external editor (related reports: [#10256](https://github.com/earendil-works/pi/issues/10256), [#10657](https://github.com/earendil-works/pi/issues/10657))
54
+ - Fixed inline images not rendering in Herdr since 1.1.0 ([#10774](https://github.com/earendil-works/pi/pull/10774) by [@ogulcancelik](https://github.com/ogulcancelik))
55
+ - Fixed key bindings that name a shifted symbol, such as `+` or `ctrl++`, not matching under the Kitty keyboard protocol or modifyOtherKeys ([#9880](https://github.com/earendil-works/pi/pull/9880) by [@christianklotz](https://github.com/christianklotz))
56
+ - Fixed the compaction status indicator embedded in a custom editor using the default muted colors instead of the editor border or thinking-level color, as the working indicator does ([#10809](https://github.com/earendil-works/pi/issues/10809), [#10810](https://github.com/earendil-works/pi/pull/10810) by [@Jay-Madden](https://github.com/Jay-Madden))
57
+ - Fixed MCP tool calls that were cancelled or timed out while waiting for an OAuth refresh still being sent to the server once the refresh finished ([#10598](https://github.com/earendil-works/pi/issues/10598))
58
+ - Fixed MCP OAuth sign-in accepting an authorization response with an `iss` parameter that could not be checked because no authorization server metadata was discovered; sign-in now fails with an issuer mismatch. Reported by [@AizenvoltPrime](https://github.com/AizenvoltPrime)
59
+ - Fixed codemode failing with a broken bridge error when Pi runs under `node --watch` on Node 24 or 26 ([#10725](https://github.com/earendil-works/pi/issues/10725))
60
+
61
+ ### Removed
62
+
63
+ - Removed the unreleased experimental server, client, and plugin stack (`src/experimental`, `src/cli/experimental`) and its source-only `./client` and `./experimental/plugin` exports; none of it was part of the published package. The `@earendil-works/pi-client`, `@earendil-works/pi-protocol`, and `@earendil-works/pi-server` packages it used are removed and no longer released
64
+ - Removed the dependency on `@earendil-works/chord`
65
+
66
+ ## [1.1.0] - 2026-10-07
67
+
68
+ ### New Features
69
+
70
+ - **Program status reporting**: terminals and agent dashboards that support OSC 7501 see whether Pi is working, blocked on a dialog or login, done, or failed. See [Program status](docs/terminal-setup.md#program-status).
71
+ - **Claude Haiku 5.5**: `anthropic/claude-haiku-5-5`, with adaptive thinking up to `xhigh`/`max` effort.
72
+ - **Adjust default tools with `+name`/`-name`**: `--tools` entries like `pi -t +codemode,-write` change the default selection instead of replacing it. See [Tools](docs/cli.md#tools).
73
+ - **GPT-6 Luna and image classification**: OpenAI's GPT-6 Luna is available as a classifier model through the Decisions API, and codemode's `models.classify()` accepts images for classifiers that support them. See [Use classifier models](docs/models.md#use-classifier-models).
74
+ - **Native llama.cpp decision models**: Julia-1, Laya, Kev, lev, and OpenJev served by llama.cpp 0.6.0 or later run natively as classifiers through `/v1/systemone`. See [Classification](docs/llama-cpp.md#classification).
75
+
76
+ ### Added
77
+
78
+ - Added `+name` and `-name` entries to `--tools`, which change the default tool selection instead of replacing it, for example `pi -t +codemode`
79
+ - Added `durationMs` to the tool render context and to `tool_execution_end` extension events: the recorded execution time of a final tool result ([#10549](https://github.com/earendil-works/pi/issues/10549))
80
+ - Added `outputPad` to the tool render context ([#10557](https://github.com/earendil-works/pi/pull/10557) by [@rwachtler](https://github.com/rwachtler))
81
+ - Added OpenAI's GPT-6 Luna as a classifier model through the Decisions API, available with `OPENAI_API_KEY` (see [Use classifier models](docs/models.md#use-classifier-models))
82
+ - Added `images` to codemode's `models.classify()` context, so classifiers that accept images, such as GPT-6 Luna, can judge them
83
+ - Added program status reporting with OSC 7501: terminals and agent dashboards that support it see whether Pi is working, blocked on a dialog or login, done, or failed. `PI_PROGRAM_STATUS=1|0` overrides detection (see [Terminal setup](docs/terminal-setup.md#program-status)) ([#10607](https://github.com/earendil-works/pi/issues/10607))
84
+ - Added `aborted` to `agent_settled` session, extension, and JSON events, so integrations can tell a cancelled run from a finished one ([#10607](https://github.com/earendil-works/pi/issues/10607))
85
+ - Added Claude Haiku 5.5 (`anthropic/claude-haiku-5-5`), with adaptive thinking up to `xhigh`/`max` effort and prompt caching on Bedrock
86
+ - Added native llama.cpp decision models: Julia-1, Laya, Kev, lev, and OpenJev served by llama.cpp 0.6.0 or later are listed only as classifiers through `/v1/systemone` instead of as chat models (see [Classification](docs/llama-cpp.md#classification)) ([#10382](https://github.com/earendil-works/pi/pull/10382))
87
+
88
+ ### Changed
89
+
90
+ - Changed `outputPad` to also apply to `!` command output, tool output, and summary blocks ([#9946](https://github.com/earendil-works/pi/issues/9946), [#10557](https://github.com/earendil-works/pi/pull/10557) by [@rwachtler](https://github.com/rwachtler))
91
+ - Changed `pi mcp login --timeout` to limit the whole sign-in, including requests to the authorization server, instead of only the wait for the browser ([#10565](https://github.com/earendil-works/pi/issues/10565))
92
+
93
+ ### Fixed
94
+
95
+ - Fixed bash and PowerShell results losing `Took` after reloading a session, and the live `Took` including wall-clock steps; both now show the recorded execution time ([#10549](https://github.com/earendil-works/pi/issues/10549))
96
+ - Fixed managed installs keeping every old release; `pi update` now keeps only the new release and the one it updated from ([#10392](https://github.com/earendil-works/pi/issues/10392), [#10511](https://github.com/earendil-works/pi/pull/10511) by [@davidbrai](https://github.com/davidbrai))
97
+ - Fixed standalone binaries loading `.env`, `.env.local`, and `.env.development` from the launch directory into Pi's environment ([#10473](https://github.com/earendil-works/pi/issues/10473))
98
+ - Fixed `!!` command headers losing their dim color once output arrives ([#10557](https://github.com/earendil-works/pi/pull/10557) by [@rwachtler](https://github.com/rwachtler))
99
+ - Fixed the codemode description not marking `searchTools()`, `describeTool()`, and `describeNamespace()` as async, which led models to serialize the unawaited promise as `{}` ([#10555](https://github.com/earendil-works/pi/issues/10555))
100
+ - Fixed codemode output items running together, so models could not tell where one `text()` or `console.log()` output ended and the next began. With several text items, each now starts with a `==> text N/M <==` line, and `console` calls follow the other output in one `<console_output>` block with one line per call
101
+ - Fixed `/mcp` waiting for all servers to connect before opening; the manager now updates live and remains usable while enabling, reconnecting, or disabling servers ([#10562](https://github.com/earendil-works/pi/issues/10562))
102
+ - Fixed images being dropped as "could not be resized" when running under `node --watch` on Node 24.19+ and 26.x, where Node posts its own messages on the image resize worker channel ([#10527](https://github.com/earendil-works/pi/issues/10527))
103
+ - Fixed clipboard paste doing nothing in Termux, and failed copies there omitting the Termux:API install hint ([#10391](https://github.com/earendil-works/pi/issues/10391))
104
+ - Fixed `!` and RPC `bash` output keeping fragments of color codes, such as a stray `m`, when a code was split across output chunks ([#10504](https://github.com/earendil-works/pi/issues/10504))
105
+ - Fixed MCP OAuth sign-ins that could not be cancelled while waiting on the authorization server and kept running after the session ended. The sign-in screen now cancels with Esc at every step, session shutdown aborts a running sign-in, and each request to the authorization server times out after 15 seconds ([#10565](https://github.com/earendil-works/pi/issues/10565))
106
+ - Fixed shutdown waiting up to 15 seconds to refresh an MCP OAuth token that was about to expire, only to close the server's session ([#10565](https://github.com/earendil-works/pi/issues/10565))
107
+ - Fixed the fullscreen text selection surviving session switches and other transcript rebuilds, which highlighted unrelated text in the new transcript ([#9311](https://github.com/earendil-works/pi/issues/9311), [#10567](https://github.com/earendil-works/pi/pull/10567) by [@christianklotz](https://github.com/christianklotz))
108
+ - Fixed OpenAI models on Bedrock ignoring the thinking level and always running at Bedrock's default reasoning effort ([#9331](https://github.com/earendil-works/pi/issues/9331), [#10142](https://github.com/earendil-works/pi/pull/10142) by [@jsanter27](https://github.com/jsanter27))
109
+ - Fixed model `headers` in `models.json` not overriding the `originator` and `User-Agent` headers of Codex requests ([#10429](https://github.com/earendil-works/pi/pull/10429) by [@lucasmeijer](https://github.com/lucasmeijer))
110
+ - Fixed `server_busy` and `servers are currently busy` provider errors ending the turn instead of being retried ([#10543](https://github.com/earendil-works/pi/issues/10543))
111
+ - Fixed Mistral responses that end with `finish_reason: "error"` not being retried ([#10487](https://github.com/earendil-works/pi/issues/10487))
112
+ - Reduced context-limit request failures by estimating input at 3.5 characters per token instead of 4 when calculating output limits ([#10497](https://github.com/earendil-works/pi/issues/10497))
113
+ - Fixed Radius models disabled by an organization owner still being listed
114
+ - Fixed Anthropic browser login failing with "localhost refused to connect" when port 53692 is reserved or in use, for example by Hyper-V/WSL port exclusions on Windows: login now falls back to a free loopback port ([#10571](https://github.com/earendil-works/pi/issues/10571))
115
+ - Fixed session costs undercounting long prompts on models with prompt-length pricing tiers, such as Claude Haiku 5.5, Gemini 3.1 Pro, and GPT-5.4, through OpenCode, OpenCode Go, OpenRouter, Vercel AI Gateway, Google, MiniMax, and other providers
116
+ - Fixed Markdown links not being clickable in Herdr ([#10573](https://github.com/earendil-works/pi/issues/10573))
117
+
3
118
  ## [1.0.4] - 2026-10-05
4
119
 
5
120
  ### New Features
@@ -27,7 +27,7 @@ pi --print "Summarize the changes in this repository"
27
27
 
28
28
  Use print mode when only the final text is needed, including command substitution, pipelines, and one-shot jobs. Intermediate events are not exposed.
29
29
 
30
- Print mode writes errors to stderr. A final assistant response with an `error` or `aborted` stop reason produces a nonzero exit status.
30
+ Print mode writes errors to stderr. A final assistant response with an `error` or `aborted` stop reason produces a nonzero exit status. A response cut off at the model's output token limit (`length` stop reason) still prints its partial text to stdout, writes a warning to stderr, and produces a nonzero exit status.
31
31
 
32
32
  When no mode is selected explicitly, non-TTY stdin or stdout also selects print mode. This allows piped input and output without adding `--print`.
33
33
 
package/bin/docs/cli.md CHANGED
@@ -117,7 +117,7 @@ pi --tools read,grep,find,ls --print "Review this project"
117
117
  See [Settings](settings.md#tools) for configuring the default tool selection.
118
118
 
119
119
  - `-t`, `--tools <list>`<br>
120
- Replaces the default selection with a comma-separated allowlist of built-in, extension, or custom tools. Entries are tool names or patterns where `*` matches any characters. MCP tools are kept unless an entry starts with `mcp__` (see [MCP tools](#mcp-tools)).
120
+ Replaces the default selection with a comma-separated allowlist of built-in, extension, or custom tools. Entries are tool names or patterns where `*` matches any characters. MCP tools are kept unless an entry starts with `mcp__` (see [MCP tools](#mcp-tools)). A list of only `+name` and `-name` entries is not an allowlist; it changes the default selection instead.
121
121
  - `-xt`, `--exclude-tools <list>`<br>
122
122
  Disables comma-separated tool names or patterns after all other selection options, MCP tools included.
123
123
  - `-nbt`, `--no-builtin-tools`<br>
@@ -125,7 +125,7 @@ See [Settings](settings.md#tools) for configuring the default tool selection.
125
125
  - `-nt`, `--no-tools`<br>
126
126
  Starts with all built-in, extension, custom, and MCP tools disabled.
127
127
 
128
- Default enabled tools are `read`, `bash`, `edit`, and `write`, unless `defaultTools` changes them. `--tools` replaces the whole selection, so name every tool you want; `defaultTools` also accepts `+name` and `-name` to change the defaults instead.
128
+ Default enabled tools are `read`, `bash`, `edit`, and `write`, unless `defaultTools` changes them. `--tools` with plain names replaces the whole selection, so name every tool you want. Like `defaultTools`, it also accepts a list of only `+name` and `-name` entries, which adds tools to or removes them from the default selection: `pi --tools +codemode,-write` keeps the other default tools, enables `codemode`, and disables `write`. These entries take exact tool names, not `*` patterns; use `--exclude-tools` to disable tools by pattern. Plain names and `+name`/`-name` entries cannot be mixed. `/reload` enables tools newly added to `defaultTools`, but a tool removed with `-name` stays removed.
129
129
 
130
130
  <a id="mcp-tools"></a>
131
131
 
@@ -165,10 +165,10 @@ To turn on `codemode` for every session, add it to the default tools in `~/.pi/a
165
165
  }
166
166
  ```
167
167
 
168
- This keeps `read`, `bash`, `edit`, and `write` and adds `codemode`. For one invocation, list every tool, since `--tools` replaces the selection:
168
+ This keeps `read`, `bash`, `edit`, and `write` and adds `codemode`. For one invocation, add it with `--tools`:
169
169
 
170
170
  ```sh
171
- pi --tools read,bash,edit,write,codemode
171
+ pi --tools +codemode
172
172
  ```
173
173
 
174
174
  Codemode is useful without MCP: scripts can run several tool calls in parallel, filter large output before it reaches the model, call classifier models such as TypeSafe's Jev through `models.classify()` (see [Classifier models](models.md#use-classifier-models)), and generate images through `models.generateImages()` (see [Image models](models.md#use-image-models)).
@@ -325,7 +325,7 @@ These commands work outside a session, so agents can run them through `bash`. Se
325
325
  | `pi mcp add <server> [options] --url <url>` | Add or replace a streamable HTTP server; `--header KEY=VALUE` (repeatable), `--bearer-token-env-var <NAME>` (sends `Authorization: Bearer ${NAME}`), `--oauth-client-id`, `--oauth-client-secret`, `--oauth-callback-port`, and `--oauth-client-name` configure authentication |
326
326
  | `pi mcp remove <server>` | Remove a server from `mcp.json`; stored OAuth credentials are kept |
327
327
  | `pi mcp list [--json]` | Connect to every enabled server and print its state, tools, and errors; exit with `1` when a config entry is invalid or an enabled server is not connected |
328
- | `pi mcp login <server> [--timeout <seconds>]` | Sign in to an OAuth server: open the authorization page and wait for the browser (default 300 seconds); a terminal also accepts the pasted redirect URL |
328
+ | `pi mcp login <server> [--timeout <seconds>]` | Sign in to an OAuth server: open the authorization page and wait for the browser; a terminal also accepts the pasted redirect URL. `--timeout` limits the whole sign-in, including requests to the authorization server (default 300 seconds) |
329
329
  | `pi mcp logout <server>` | Delete the stored OAuth credentials of a server |
330
330
 
331
331
  `add` and `remove` change `~/.pi/agent/mcp.json`, or `.pi/mcp.json` in the current directory with `--local` (`-l`). `add` also takes `--exposure <mode>` (see [Exposure](mcp.md#exposure)) and `--description <text>` and does not connect; run `pi mcp list` to check the server.
@@ -6,7 +6,7 @@ The `codemode` tool lets the model write a JavaScript script that calls pi's oth
6
6
 
7
7
  The tool input is raw JavaScript source, not JSON and not a markdown code fence. It runs as the body of an async function in a QuickJS sandbox, so top-level `await` and `return` work. The sandbox has no Node APIs, file system, network, or timers; scripts reach the outside world only through tools and `models`.
8
8
 
9
- A script may start with an options line:
9
+ A script may start with an options line. It must be the first line; an options line anywhere else fails the call before the script runs:
10
10
 
11
11
  ```js
12
12
  // @options: {"max_output_tokens": 2000, "timeout_ms": 60000}
@@ -15,7 +15,7 @@ A script may start with an options line:
15
15
  - `max_output_tokens` (default 10000) limits the output. Longer output keeps its start and end, and the full text is written to a temp file whose path is included in the result. A script fails when its output passes 16777216 characters of text and base64 image data or 100000 `text()`, `image()`, and `console` calls; write large data to a file with a tool instead.
16
16
  - `timeout_ms` is a hard deadline for the whole script. It is unset by default. Image generation can take minutes, so do not set a short deadline for scripts that generate images.
17
17
 
18
- 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:` and the error. Tool calls are real: calls made before a failure are not undone. Calls still running when the script ends are cancelled, and unawaited promises are discarded.
18
+ The result starts with `Script completed` or `Script failed`, the wall time, and the output. Text and image items appear in order, each on its own line. When the output has more than one text item (from `text()` or `return`), each starts with a `==> text N/M <==` line. `console` calls follow in one `<console_output>` block with one line per call. A failed script keeps its partial output, followed by `Script error:` and the error. Tool calls are real: calls made before a failure are not undone. Calls still running when the script ends are cancelled, and unawaited promises are discarded.
19
19
 
20
20
  ## Globals
21
21
 
@@ -24,7 +24,7 @@ The result starts with `Script completed` or `Script failed`, the wall time, and
24
24
  | `tools.<name>(args)` | Call a tool. See [Call tools](#call-tools). |
25
25
  | `text(value)` | Add a text item to the output. Strings are added as is, other values as JSON. |
26
26
  | `image(value)` | Add an image to the output: a base64 `data:` URL, an `{ image_url }` object, or an image block `{ type: "image", data, mimeType }` such as those returned by MCP tools and `models.generateImages()`. Remote URLs are not supported. PNG, JPEG, GIF, and WebP are accepted. Each image is also saved to a temp file, and the result names the path before the image. |
27
- | `console.log(...)` | Like `text()`; `info`, `warn`, `error`, and `debug` do the same. |
27
+ | `console.log(...)` | Add a line to the `<console_output>` block after the other output. Arguments are joined with spaces; `info`, `warn`, `error`, and `debug` do the same. |
28
28
  | `return value` | A top-level `return` adds the value like `text()`. |
29
29
  | `exit()` | End the script successfully. |
30
30
  | `store(key, value)` / `load(key)` | Keep small JSON values across `codemode` calls. See [Store values](#store-values). |
@@ -59,7 +59,7 @@ The store is for small state such as IDs, cursors, or summaries. One value may h
59
59
 
60
60
  ## Models
61
61
 
62
- `models` reaches the model catalog and runs non-LLM models with the session's credentials: classifiers, which answer typed questions about JSON state, and image models, which generate images. Chat models are listed but cannot be run from scripts. Which classifier and image models exist is described in [Use classifier models](models.md#use-classifier-models) and [Use image models](models.md#use-image-models).
62
+ `models` reaches the model catalog and runs non-LLM models with the session's credentials: classifiers, which answer typed questions about JSON state and, for some models, images, and image models, which generate images. Chat models are listed but cannot be run from scripts. Which classifier and image models exist is described in [Use classifier models](models.md#use-classifier-models) and [Use image models](models.md#use-image-models).
63
63
 
64
64
  ```ts
65
65
  type ModelType = "chat" | "image" | "classifier";
@@ -100,6 +100,8 @@ Model IDs differ between providers, for example `typesafe/jev-latest` and `openr
100
100
  interface ClassifierContext {
101
101
  /** The data to classify. */
102
102
  state: Record<string, unknown>;
103
+ /** Images judged together with `state`. Only models whose `input` includes "image" accept them. */
104
+ images?: { type: "image"; data: string; mimeType: string }[];
103
105
  /** Questions by ID. One call answers all of them. */
104
106
  questions: Record<string, ClassifierQuestion>;
105
107
  }
@@ -163,6 +165,24 @@ return results.map((result, i) =>
163
165
  );
164
166
  ```
165
167
 
168
+ Classifiers whose `input` includes `"image"` also judge images. `tools.read()` returns an image file as an image block that `images` accepts. Other classifiers return an error result when `images` is not empty.
169
+
170
+ ```js
171
+ const luna = await models.getModelOfType("classifier", "openai", "gpt-6-luna");
172
+ const photo = await tools.read({ path: "screenshot.png" });
173
+ const result = await models.classify(luna, {
174
+ state: { task: "Settings page redesign" },
175
+ images: [photo],
176
+ questions: {
177
+ broken: {
178
+ type: "bool",
179
+ instructions: "Does the screenshot show a broken layout?",
180
+ criteria: { true: "Overlapping, cut-off, or misaligned elements", false: "Clean layout" },
181
+ },
182
+ },
183
+ });
184
+ ```
185
+
166
186
  ### Generate images
167
187
 
168
188
  ```ts
@@ -279,7 +279,6 @@ Before summarization, messages are serialized to text via [`serializeConversatio
279
279
 
280
280
  ```
281
281
  [User]: What they said
282
- [Assistant thinking]: Internal reasoning
283
282
  [Assistant]: Response text
284
283
  [Assistant tool calls]: read(path="foo.ts"); edit(path="bar.ts", ...)
285
284
  [Tool result]: Output from tool
@@ -287,6 +286,8 @@ Before summarization, messages are serialized to text via [`serializeConversatio
287
286
 
288
287
  This prevents the model from treating it as a conversation to continue.
289
288
 
289
+ Thinking blocks are omitted. Providers return summaries of the real reasoning, and replayed thinking is often not part of the model's live context, so including it could make the summarization request larger than the context it summarizes.
290
+
290
291
  Tool results are truncated to 2000 characters during serialization. Content beyond that limit is replaced with a marker indicating how many characters were truncated. This keeps summarization requests within reasonable token budgets, since tool results (especially from `read` and `bash`) are typically the largest contributors to context size.
291
292
 
292
293
  ## Custom Summarization via Extensions
@@ -346,7 +347,6 @@ pi.on("session_before_compact", async (event, ctx) => {
346
347
  );
347
348
  // Returns:
348
349
  // [User]: message text
349
- // [Assistant thinking]: thinking content
350
350
  // [Assistant]: response text
351
351
  // [Assistant tool calls]: read(path="..."); bash(command="...")
352
352
  // [Tool result]: output text
@@ -86,9 +86,11 @@ These variables are read by Pi itself:
86
86
  | `PI_TELEMETRY` | Override install/update telemetry and provider attribution headers: `1`/`true`/`yes` or `0`/`false`/`no` |
87
87
  | `PI_CACHE_RETENTION` | Set to `long` for extended provider prompt caching where supported |
88
88
  | `PI_SHARE_VIEWER_URL` | Override the base URL used by `/share` |
89
- | `PI_RADIUS_GATEWAY` | Override the Radius gateway origin used by `/bug` uploads and Radius relay connections |
89
+ | `PI_RADIUS_GATEWAY` | Override the Radius gateway origin used by `/bug` uploads |
90
90
  | `PI_HARDWARE_CURSOR` | Set to `1` to show the hardware cursor; see [Terminal setup](terminal-setup.md) |
91
+ | `PI_CLIPBOARD_OSC52` | Set to `1` to also send copied text to the terminal with OSC 52 after the local clipboard write, as Pi does over SSH. Use it when a remote client reaches Pi without SSH variables, such as a multiplexer attached from another machine or a remote IDE daemon |
91
92
  | `PI_HYPERLINKS` | Override OSC 8 hyperlink detection with `1`, `0`, or `auto` |
93
+ | `PI_PROGRAM_STATUS` | Override OSC 7501 program status detection: `1` always reports, `0` never reports; otherwise Pi reports only after the terminal confirms support. See [Terminal setup](terminal-setup.md#program-status) |
92
94
  | `PI_IMAGE_PROTOCOL` | Override inline image detection with `kitty`, `iterm2`, `none`, or `auto` |
93
95
  | `PI_TRUE_COLOR` | Override truecolor detection with `1`, `0`, or `auto` |
94
96
  | `PI_TUI_ESC_TIMEOUT` | How long to wait after a lone ESC before treating it as Escape, in milliseconds; defaults to `100` over SSH and `10` otherwise. Increase if Alt-key input is misread as Escape |
@@ -60,6 +60,7 @@ Start long-lived resources from `session_start` or from the command or tool that
60
60
  Close session-scoped resources from an idempotent `session_shutdown` handler.
61
61
 
62
62
  A run proceeds from input and `before_agent_start`, through model, message, and tool events, to `agent_end`.
63
+ Pi counts as busy while a run starts, including the compaction check and `before_agent_start`, so messages sent meanwhile go into the starting run.
63
64
  Automatic retries, recovery, compaction, or queued work can continue afterward.
64
65
  <a id="agent_start--agent_end--agent_before_settle--agent_settled"></a>
65
66
 
@@ -100,7 +101,7 @@ Use each event’s declared result type rather than assuming every return value
100
101
 
101
102
  Events cover resource discovery, sessions, agent and message lifecycle, providers, tools, and raw input.
102
103
 
103
- `before_agent_start` exposes both the current prompt and its structured `systemPromptOptions`. Prefer changing prompt sections, selected tools, or guidelines so Pi can append a transcript delta. Returning `systemPrompt`, or setting `forceSystemPrompt`, replaces the whole prompt for that run while the transcript continues recording the structured sections. Providers receive the forced text as their leading system prompt.
104
+ `before_agent_start` exposes both the current prompt and its structured `systemPromptOptions`. `event.messageType` says what started the run: `"user"` for a prompt or `pi.sendUserMessage()`, with `prompt` and `images`; `"custom"` for `pi.sendMessage(..., { triggerTurn: true })`, with the starting `message` and no `prompt`. A returned `message` is added to either kind of run; check `messageType` to skip runs started by custom messages. Prefer changing prompt sections, selected tools, or guidelines so Pi can append a transcript delta. Returning `systemPrompt`, or setting `forceSystemPrompt`, replaces the whole prompt for that run while the transcript continues recording the structured sections. Providers receive the forced text as their leading system prompt.
104
105
 
105
106
  `message_end` can replace a finalized message while preserving its role. `tool_call` can mutate input or block execution. `tool_result` handlers compose, with each handler seeing prior changes.
106
107
 
@@ -159,9 +160,9 @@ See [`hello.ts`](../examples/extensions/hello.ts), [`todo.ts`](../examples/exten
159
160
  - `deferred`: like `codemode`, but codemode tools do not list it; `tool_search` can find and activate it.
160
161
  - `hidden`: registered but unreachable. Re-register a tool with `exposure: "hidden"` to withdraw it, since tools cannot be unregistered.
161
162
 
162
- `namespace: { name, description, instructions }` groups related tools, as MCP servers do. Codemode tools list a namespace under one heading with its `description`. `instructions` holds longer usage guidance; it is not listed, and codemode scripts read it with `describeNamespace(name)`.
163
+ `namespace: { name, aliases, description, instructions }` groups related tools, as MCP servers do. Codemode tools list a namespace under one heading with its `description`. `instructions` holds longer usage guidance; it is not listed, and codemode scripts read it with `describeNamespace(name)`. `aliases` are other names scripts may look the namespace up by, for example the server name `docs` for `mcp__docs`; a name and the same name as a script identifier (`my-docs`, `my_docs`) match alike.
163
164
 
164
- Registering a `direct` or `model-only` tool activates it; the other exposures are not activated on registration. The active set (`pi.getActiveTools()`, `pi.setActiveTools()`) is the set of tools declared to the model. `pi.getAllTools()` reports each tool's `exposure`, `namespace`, and `annotations`.
165
+ Registering a `direct` or `model-only` tool activates it; the other exposures are not activated on registration. The active set (`pi.getActiveTools()`, `pi.setActiveTools()`) is the set of tools declared to the model. `pi.getAllTools()` reports each tool's `exposure`, `namespace`, `annotations`, and `reachesTools`.
165
166
 
166
167
  `annotations` are hints about what a tool does, with the meaning of MCP tool annotations: `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`. MCP tools carry the hints their server declares. Missing hints take the MCP defaults: a tool is not read-only, and may be destructive and reach an open world. The hints are not verified, but a permission extension can use them to decide which calls to confirm. This confirms the calls Codex asks approval for:
167
168
 
@@ -177,7 +178,9 @@ pi.on("tool_call", async (event, ctx) => {
177
178
  });
178
179
  ```
179
180
 
180
- A tool that orchestrates other tools can adjust what the model sees while it is active with `prepareLoadout(loadout)`. It runs whenever the active tools change and receives the declared tools, the callable tools, and every registered tool with its exposure, namespace, and prompt guidelines. It returns replacement `descriptions` for declared tools (including its own) and `hiddenDeclarations`: active tools whose declarations requests leave out while they stay active and callable. The default system prompt leaves hidden tools out of its tool list and rules, and names no tool in the skills hint when the file reader is hidden, so the orchestrating tool should present their guidelines itself. `codemode` uses only this hook, `exposure`, and `ctx.executeTool()`, so another tool can implement the same behavior under a different name.
181
+ A tool that orchestrates other tools can adjust what the model sees while it is active with `prepareLoadout(loadout)`. It runs whenever the active tools change and receives the declared tools, the callable tools, and every registered tool with its exposure, namespace, and prompt guidelines. It returns replacement `descriptions` for declared tools (including its own) and `hiddenDeclarations`: active tools whose declarations requests leave out while they stay active and callable. The default system prompt leaves hidden tools out of its tool list and rules, and names no tool in the skills hint when the file reader is hidden, so the orchestrating tool should present their guidelines itself. `codemode` uses only this hook, `exposure`, `reachesTools`, and `ctx.executeTool()`, so another tool can implement the same behavior under a different name.
182
+
183
+ A tool that reaches other tools declares which ones a call may reach with `reachesTools(input)`: `"all"`, or the words of the input that may name them, where a word refers to every tool or namespace whose name it contains. `codemode` returns `"all"` for scripts that search or list tools and the words of the script otherwise; `tool_search` always returns `"all"`. Extensions that register tools late use it from their `tool_call` handler to finish registering the tools a call needs before it runs, as the MCP extension does for servers that are still connecting. The MCP extension also activates `codemode` or `tool_search` for MCP tools only when the tool of that name declares `reachesTools`.
181
184
 
182
185
  ### Activate tools dynamically
183
186
 
package/bin/docs/json.md CHANGED
@@ -42,7 +42,7 @@ A basic run produces records like these:
42
42
  {"type":"message_end","message":{"role":"assistant","...":"..."}}
43
43
  {"type":"turn_end","message":{"role":"assistant","...":"..."},"toolResults":[]}
44
44
  {"type":"agent_end","messages":[{"...":"..."}],"willRetry":false}
45
- {"type":"agent_settled"}
45
+ {"type":"agent_settled","aborted":false}
46
46
  ```
47
47
 
48
48
  `agent_end` closes one low-level agent run. Automatic retry, overflow recovery, compaction retry, steering, or follow-up work can still continue. `agent_settled` means Pi has no remaining automatic work for that session-level run.
@@ -53,7 +53,7 @@ A basic run produces records like these:
53
53
  |---|---|---|
54
54
  | `agent_start` | None | A low-level agent run started. |
55
55
  | `agent_end` | `messages`, `willRetry` | That low-level run ended. `messages` contains messages generated by the run. |
56
- | `agent_settled` | None | Pi will not continue automatically through retries, compaction recovery, or queued messages. |
56
+ | `agent_settled` | `aborted` | Pi will not continue automatically through retries, compaction recovery, or queued messages. `aborted` is `true` when the run ended because it was aborted. |
57
57
  | `turn_start` | None | One assistant turn started. |
58
58
  | `turn_end` | `message`, `toolResults` | One assistant response and its resulting tool calls finished. |
59
59
 
@@ -104,7 +104,7 @@ The top-level `usage` is the latest cumulative provider-reported usage for the a
104
104
  |---|---|---|
105
105
  | `tool_execution_start` | `toolCallId`, `toolName`, `args` | Tool execution started. |
106
106
  | `tool_execution_update` | `toolCallId`, `toolName`, `args`, `partialResult` | The tool reported a partial result. |
107
- | `tool_execution_end` | `toolCallId`, `toolName`, `result`, `isError` | Tool execution finished. |
107
+ | `tool_execution_end` | `toolCallId`, `toolName`, `result`, `isError`, `durationMs` | Tool execution finished. `durationMs` is how long the tool's `execute()` took, measured with a monotonic clock; absent when the tool did not run. |
108
108
 
109
109
  Use `toolCallId` to correlate the lifecycle. `partialResult` is the latest partial result supplied by the tool. Whether it replaces or extends an earlier update depends on that tool's result contract.
110
110
 
@@ -12,6 +12,7 @@ Map each action identifier to one key or a list of keys:
12
12
 
13
13
  ```json
14
14
  {
15
+ "$schema": "https://pi.dev/schemas/keybindings.schema.json",
15
16
  "app.session.new": "ctrl+shift+n",
16
17
  "app.session.tree": ["ctrl+shift+t", "alt+shift+t"]
17
18
  }
@@ -88,7 +88,14 @@ If the router disconnects, `/llama` shows **Retry** and **Close**. Retry reconne
88
88
 
89
89
  ## Classification
90
90
 
91
- Every model listed for chat is also listed as a classifier model with the same ID and the `llama-cpp-classify` API. Classifier models answer typed `choice`, `bool`, and `score` questions about JSON state, like TypeSafe's Jev models. The model reaches them from [`codemode`](cli.md#enable-codemode) scripts, and extensions through `ctx.modelRegistry.classify()`; see [Classifier models](models.md#use-classifier-models).
91
+ Classifier models answer typed `choice`, `bool`, and `score` questions about JSON state, like TypeSafe's Jev models. The model reaches them from [`codemode`](cli.md#enable-codemode) scripts, and extensions through `ctx.modelRegistry.classify()`; see [Classifier models](models.md#use-classifier-models). Pi lists llama.cpp models as classifiers in two ways:
92
+
93
+ - **Decision models** such as [Julia-1, Laya, Kev, lev, and OpenJev](https://huggingface.co/collections/ggml-org/decision-models-6abf80cca3c83f127060a769) answer natively through llama.cpp's `/v1/systemone` endpoint. They appear only as classifiers, with the `typesafe-system-one` API, and not in `/model`.
94
+ - **Chat models** are also listed as classifiers with the same ID and the `llama-cpp-classify` API, which reads answers from next-token probabilities as described below.
95
+
96
+ llama.cpp 0.6.0 and later report decision models in the router's model list: their `architecture.output_modalities` contains `decisions`. The router reads this from the GGUF metadata without loading the model, so Pi recognizes unloaded and sleeping decision models too. Older llama.cpp builds do not report it, and Pi lists their decision models as chat models.
97
+
98
+ ### Chat models as classifiers
92
99
 
93
100
  The model does not generate an answer. Each question becomes one chat prompt: the state, every question of the request, the state again, and then the question with its answers under single-token labels. Labels are letters for a choice (up to 62 options), `Yes`/`No` for a bool, and digits for a score (up to 10 levels). The second copy of the state is read with the questions in view, which improved accuracy on JevBench with small models. Pi reads the probabilities of the labels as the next token and normalizes them. A choice returns every option's probability and a confidence of `(n * peak - 1) / (n - 1)`; a score returns the expected level.
94
101
 
package/bin/docs/mcp.md CHANGED
@@ -265,7 +265,7 @@ Changes to enabled state or exposure apply only to the current session. A file-c
265
265
 
266
266
  An installed extension that registers `/mcp`, such as `pi-mcp-adapter`, replaces the built-in MCP support for sessions. Pi then does not read `mcp.json` or connect its servers in a session, and `/mcp` belongs to the extension. Remove the extension to restore the built-in behavior. To disable built-in MCP support without a replacement, disable `mcp` under Built-in in `pi config`, or set `"extensions": ["-builtin:mcp"]` in [settings](settings.md#resources). `--no-mcp` disables it for one run.
267
267
 
268
- An extension that registers `codemode` or `tool_search` similarly replaces the built-in tool with that name. Shell-level `pi mcp` commands always use the built-in implementation.
268
+ An extension that registers `codemode` or `tool_search` similarly replaces the built-in tool with that name. Pi activates the replacement for MCP servers and waits for the servers its calls need only when it declares [`reachesTools`](extensions.md#tool-exposure). Shell-level `pi mcp` commands always use the built-in implementation.
269
269
 
270
270
  ### Use MCP from the SDK
271
271
 
@@ -100,12 +100,11 @@ interface SystemMessage {
100
100
  sections?: Record<string, string | null>;
101
101
  toolsAdded?: Tool[];
102
102
  toolsRemoved?: ToolReference[];
103
- replace?: boolean;
104
103
  timestamp: number;
105
104
  }
106
105
  ```
107
106
 
108
- The leading system message declares the initial prompt and tools. Later system messages can append instructions, replace or remove named prompt sections, and add or remove tools. Replaying them in order yields the current state. A message with `replace: true` discards the earlier state and establishes a complete new baseline.
107
+ The leading system message declares the initial prompt and tools. Later system messages can append instructions, replace or remove named prompt sections, and add or remove tools. Replaying them in order yields the current state.
109
108
 
110
109
  ### UserMessage
111
110
 
@@ -129,6 +128,7 @@ interface AssistantMessage {
129
128
  responseModel?: string;
130
129
  responseId?: string;
131
130
  providerThinkingLevel?: string;
131
+ thinkingLevel?: ModelThinkingLevel;
132
132
  diagnostics?: AssistantMessageDiagnostic[];
133
133
  usage: Usage;
134
134
  stopReason: "pending" | "stop" | "length" | "toolUse" | "error" | "aborted" | "deferred";
@@ -140,7 +140,7 @@ interface AssistantMessage {
140
140
  }
141
141
  ```
142
142
 
143
- `responseModel` records a concrete provider response model when it differs from the requested model. `responseId`, `providerThinkingLevel`, `diagnostics`, and `rawStopReason` preserve provider or runtime details.
143
+ `responseModel` records a concrete provider response model when it differs from the requested model. `responseId`, `providerThinkingLevel`, `thinkingLevel`, `diagnostics`, and `rawStopReason` preserve provider or runtime details.
144
144
 
145
145
  `"pending"` is used for a partial assistant message while it streams. The completed message in `message_end` has a terminal stop reason, and Pi does not persist `"pending"` assistant messages in session JSONL.
146
146
 
@@ -168,12 +168,30 @@ interface ToolResultMessage<TDetails = any> {
168
168
  content: (TextContent | ImageContent)[];
169
169
  details?: TDetails;
170
170
  usage?: Usage;
171
+ nestedCalls?: NestedToolCalls;
171
172
  isError: boolean;
172
173
  timestamp: number;
173
174
  }
174
175
  ```
175
176
 
176
- `details` is tool-specific. Optional `usage` reports nested model work performed by the tool and contributes to full-session statistics, but it is not part of the main model-call usage.
177
+ `details` is tool-specific. Optional `usage` reports nested model work performed by the tool and contributes to full-session statistics, but it is not part of the main model-call usage. `nestedCalls` records bounded metadata about calls this tool made to other tools:
178
+
179
+ ```typescript
180
+ interface NestedToolCalls {
181
+ calls: NestedToolCallRecord[];
182
+ complete: boolean;
183
+ }
184
+
185
+ interface NestedToolCallRecord {
186
+ id: string;
187
+ name: string;
188
+ arguments?: JsonObject;
189
+ argumentsBytes?: number;
190
+ status: "ok" | "error" | "unfinished";
191
+ durationMs?: number;
192
+ error?: string;
193
+ }
194
+ ```
177
195
 
178
196
  ## Coding-agent messages
179
197
 
@@ -48,6 +48,7 @@ Use [`models.json`](configuration.md#agent-directory) when an endpoint speaks an
48
48
 
49
49
  ```json
50
50
  {
51
+ "$schema": "https://pi.dev/schemas/models.schema.json",
51
52
  "providers": {
52
53
  "ollama": {
53
54
  "baseUrl": "http://localhost:11434/v1",
@@ -128,7 +129,7 @@ Compatibility settings should describe verified differences in the endpoint's re
128
129
 
129
130
  ## Use classifier models
130
131
 
131
- Classifier models do not chat. They answer typed questions about JSON state: pick one of several choices, answer yes or no, or give a score, each with probabilities. Pi includes TypeSafe's Jev model from these providers, and Cloudflare's Clef and Clef Flash models from Workers AI:
132
+ Classifier models do not chat. They answer typed questions about JSON state: pick one of several choices, answer yes or no, or give a score, each with probabilities. Pi includes TypeSafe's Jev model from these providers, Cloudflare's Clef and Clef Flash models from Workers AI, and OpenAI's GPT-6 Luna through the [Decisions API](https://developers.openai.com/api/docs/guides/decisions):
132
133
 
133
134
  | Provider | Model IDs | Authentication |
134
135
  |---|---|---|
@@ -137,9 +138,12 @@ Classifier models do not chat. They answer typed questions about JSON state: pic
137
138
  | `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
138
139
  | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
139
140
  | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
141
+ | `openai` | `gpt-6-luna` | `OPENAI_API_KEY` |
140
142
 
141
143
  Chat models on a [llama.cpp router](llama-cpp.md#classification) are also listed as classifier models.
142
144
 
145
+ OpenAI's Decisions API needs an API key. Sign in with ChatGPT credentials do not work with it, so `gpt-6-luna` is not listed as available while `openai` is logged in through `/login`, even when `OPENAI_API_KEY` is set; log out of `openai` to use the key. GPT-6 Luna also judges images passed in `images` (see [Codemode](codemode.md#classify)); other classifier models return an error for them. The API rejects inputs above 922K tokens, but requests that run longer than about five seconds, currently above roughly 600K input tokens, fail with a gateway timeout.
146
+
143
147
  Classifier models do not appear in `/model`. The model reaches them through the [`codemode`](cli.md#enable-codemode) tool, which is off unless an MCP server turned it on. Enable it with `"defaultTools": ["+codemode"]` in [settings](settings.md#tools). Scripts then list classifier models with `models.getAvailableOfType("classifier")` and call `models.classify(model, { state, questions })`:
144
148
 
145
149
  ```js
@@ -113,8 +113,32 @@ Radius is currently in early alpha and evolving quickly. See [radius.earendil.co
113
113
 
114
114
  Radius authentication uses its gateway catalog and caches refreshed model metadata for later offline startup. A custom Radius gateway configured in `models.json` uses its own catalog rather than inheriting the public `radius.pi.dev` catalog.
115
115
 
116
+ ### OpenRouter
117
+
118
+ Authenticate with `/login openrouter` or `OPENROUTER_API_KEY`.
119
+
120
+ With a key, Pi lists only the OpenRouter chat models that key may use, with the key's context size, max output, and prices. These come from OpenRouter's `/models/user` endpoint and reflect guardrails and provider restrictions set in OpenRouter. Image and classifier models are not filtered.
121
+
122
+ Pi refreshes the list in the background at startup in interactive mode, when `/model` opens, after `/login`, and on `pi update --models`. The last list is saved and used offline and in `pi -p` and RPC mode. After switching keys outside `/login`, the previous key's list stays until the next refresh.
123
+
124
+ Regional endpoints filter models to their region. To use one, set the base URL in [`models.json`](models.md#configure-a-compatible-endpoint):
125
+
126
+ ```json
127
+ {
128
+ "providers": {
129
+ "openrouter": { "baseUrl": "https://us.openrouter.ai/api/v1" }
130
+ }
131
+ }
132
+ ```
133
+
134
+ If the base URL rejects the key at `/models/user` or does not have that endpoint, for example a proxy configured as the OpenRouter base URL, Pi lists every OpenRouter model.
135
+
116
136
  ### Azure OpenAI
117
137
 
138
+ The provider ID is `azure` (formerly `azure-openai-responses`). Use it as the key in `auth.json`, `models.json`, and `settings.json`, and in model references such as `--model azure/gpt-5.4`.
139
+
140
+ The `azure` provider serves OpenAI models through the Responses API and Microsoft Foundry models through Chat Completions, such as `azure/deepseek-v4-pro`.
141
+
118
142
  Set an API key plus either a base URL or resource name:
119
143
 
120
144
  ```bash
@@ -126,6 +150,29 @@ export AZURE_OPENAI_RESOURCE_NAME=your-resource
126
150
 
127
151
  Resource root URLs under `ai.azure.com`, `cognitiveservices.azure.com`, and `openai.azure.com` are normalized to the OpenAI API path.
128
152
 
153
+ Pi sends the model ID as the deployment name. If a deployment has a different name, map it with `AZURE_OPENAI_DEPLOYMENT_NAME_MAP`:
154
+
155
+ ```bash
156
+ export AZURE_OPENAI_DEPLOYMENT_NAME_MAP=gpt-5.4=my-gpt-deployment,deepseek-v4-pro=my-deepseek
157
+ ```
158
+
159
+ `AZURE_OPENAI_API_VERSION` overrides the API version for OpenAI models (default `v1`).
160
+
161
+ To use a Foundry model that Pi does not include, add it under `azure` in [`models.json`](models.md#configure-a-compatible-endpoint) with `api: "openai-completions"`. Custom models require a `baseUrl`; `AZURE_OPENAI_BASE_URL` and `AZURE_OPENAI_RESOURCE_NAME` take priority over it when set:
162
+
163
+ ```json
164
+ {
165
+ "providers": {
166
+ "azure": {
167
+ "baseUrl": "https://your-resource.services.ai.azure.com",
168
+ "models": [
169
+ { "id": "your-deployment", "api": "openai-completions" }
170
+ ]
171
+ }
172
+ }
173
+ }
174
+ ```
175
+
129
176
  ### Amazon Bedrock
130
177
 
131
178
  Bedrock can use a bearer token or an ambient AWS credential source:
@@ -2,6 +2,8 @@
2
2
 
3
3
  This reference lists user-configurable settings, their types, defaults, and purposes. Project settings override agent-directory settings. Resource lists are combined. See [Configuration](configuration.md) for file locations and trust behavior.
4
4
 
5
+ Add `"$schema": "https://pi.dev/schemas/settings.schema.json"` to `settings.json` for editor validation and completion.
6
+
5
7
  ## Model and thinking
6
8
 
7
9
  <a id="model-cycling"></a>
@@ -53,9 +55,9 @@ A list of only `+name` and `-name` entries changes the inherited selection inste
53
55
 
54
56
  This replaces `bash` with `powershell` and enables `grep`: `["-bash", "+powershell", "+grep"]`. Project settings apply on top of user settings: a project list with only `+name` and `-name` entries changes the user's selection, and a project list with a plain name replaces it. In one list, plain names form the selection, and `+name` and `-name` then apply in order.
55
57
 
56
- `/reload` enables tools newly added to `defaultTools`. It does not disable tools removed from it or re-enable unchanged tools you turned off. `--tools`, `--no-tools`, and `--no-builtin-tools` override `defaultTools`, also on reload.
58
+ `/reload` enables tools newly added to `defaultTools`. It does not disable tools removed from it or re-enable unchanged tools you turned off. `--tools` with plain names, `--no-tools`, and `--no-builtin-tools` override `defaultTools`, also on reload.
57
59
 
58
- CLI tool options override this setting for one invocation; `--tools` does not accept `+name` or `-name`. See [Command Line](cli.md#tools).
60
+ CLI tool options override this setting for one invocation. `--tools` with only `+name` and `-name` entries changes the resolved `defaultTools` selection instead, for example `pi --tools +codemode`. On `/reload`, these entries apply to the reloaded setting too, so a tool removed with `-name` stays removed. See [Command Line](cli.md#tools).
59
61
 
60
62
  ## Sessions and context
61
63
 
@@ -97,9 +99,9 @@ See [Compaction Reference](compaction.md) for trigger, summarization, and valida
97
99
  | `fullscreenCopyOnSelect` | boolean | `true` | Copy selected text automatically in fullscreen mode. |
98
100
  | `fullscreenWheelScrollLines` | `"auto"` \| number | `"auto"` | Lines per mouse-wheel event in fullscreen mode, from 1 to 100. `"auto"` moves one line per event in local macOS terminals, which already accelerate wheel and trackpad input; elsewhere, and over SSH, it speeds up fast wheel spins to at most 6 lines per event. Alt+wheel moves five times as far. |
99
101
  | `editorPaddingX` | number | `0` | Horizontal editor padding from 0 to 3 cells. |
100
- | `outputPad` | `0 \| 1` | `1` | Horizontal transcript padding. |
102
+ | `outputPad` | `0 \| 1` | `1` | Horizontal transcript padding for messages, tool output, `!` command output, and summary blocks. |
101
103
  | `autocompleteMaxVisible` | number | `5` | Visible autocomplete entries, from 3 to 20. |
102
- | `showHardwareCursor` | boolean | `false` | Show the terminal cursor while Pi positions it for input methods. |
104
+ | `showHardwareCursor` | boolean | `false` | Use the terminal cursor instead of Pi's drawn cursor. Pi still positions it for input methods. |
103
105
  | `terminal.showImages` | boolean | `true` | Display inline images when supported. |
104
106
  | `terminal.imageWidthCells` | number | `60` | Preferred inline image width in terminal cells. |
105
107
  | `terminal.clearOnShrink` | boolean | `false` | Clear empty rows when rendered content shrinks. |