@kingingwang/pi-windows-x64 0.99.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/CHANGELOG.md +86 -0
- package/bin/docs/cli.md +8 -18
- package/bin/docs/codemode.md +207 -0
- package/bin/docs/docs.json +5 -1
- package/bin/docs/environment-variables.md +2 -2
- package/bin/docs/extensions.md +3 -3
- package/bin/docs/index.md +1 -1
- package/bin/docs/mcp.md +142 -82
- package/bin/docs/models.md +22 -1
- package/bin/docs/providers.md +18 -6
- package/bin/docs/sdk.md +2 -2
- package/bin/docs/settings.md +5 -3
- package/bin/docs/usage.md +1 -1
- package/bin/examples/extensions/built-in-tool-renderer.ts +16 -40
- package/bin/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/bin/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/bin/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/bin/examples/extensions/gondolin/package-lock.json +2 -2
- package/bin/examples/extensions/gondolin/package.json +1 -1
- package/bin/examples/extensions/minimal-mode.ts +24 -96
- package/bin/examples/extensions/sandbox/package-lock.json +2 -2
- package/bin/examples/extensions/sandbox/package.json +1 -1
- package/bin/examples/extensions/with-deps/package-lock.json +2 -2
- package/bin/examples/extensions/with-deps/package.json +1 -1
- package/bin/examples/sdk/14-codemode-mcp.ts +2 -2
- package/bin/package.json +10 -10
- package/bin/pi.exe +2 -2
- package/package.json +1 -1
package/bin/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0] - 2026-10-01
|
|
4
|
+
|
|
5
|
+
### New Features
|
|
6
|
+
|
|
7
|
+
- **Fullscreen by default** — The TUI now runs fullscreen. Set `tuiMode` to `"regular"` to keep the terminal's normal scrollback. See [Terminal and display](docs/settings.md#terminal-and-display).
|
|
8
|
+
- **Leaner codemode** — About 40% fewer prompt tokens, and errors that tell the model how to recover. See [Codemode](docs/codemode.md).
|
|
9
|
+
- **Image generation in codemode** — Scripts call `models.generateImages()` with the session's credentials. See [Generate images](docs/codemode.md#generate-images) and [Use image models](docs/models.md#use-image-models).
|
|
10
|
+
- **Radius in `/login`** — Sign in with Radius and set up its MCP server in one step. See [Radius](docs/providers.md#radius).
|
|
11
|
+
- **Anthropic copy code login** — Sign in when the browser runs on another machine. See [Authenticate interactively](docs/providers.md#authenticate-interactively).
|
|
12
|
+
- **MCP OAuth hardening** — `oauth.authServerMetadataUrl`, RFC 9207 `iss` checks, credentials per server, and step-up sign-in that keeps granted scopes. See [Authenticate with OAuth](docs/mcp.md#authenticate-with-oauth).
|
|
13
|
+
- **Header-only quiet startup** — `quietStartup: "header"` keeps the version and key hints and hides the rest. See [Terminal and display](docs/settings.md#terminal-and-display).
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Added an `oauth.authServerMetadataUrl` setting for MCP servers that advertise a wrong OAuth authorization server or none. Pi uses the configured metadata document instead of discovery ([#10172](https://github.com/earendil-works/pi/issues/10172)).
|
|
18
|
+
- Added `quietStartup: "header"`, which keeps the startup header with version and key hints but hides the model scope line and loaded-resource listing.
|
|
19
|
+
- Added `models.generateImages()` to codemode scripts. It runs image models such as OpenRouter's with the session's credentials and returns base64 image blocks that `image()` attaches to the result; usage counts toward the session cost like `models.classify()`. Extensions can call `ctx.modelRegistry.generateImages()`. See [Use image models](docs/models.md#use-image-models).
|
|
20
|
+
- Added a copy code login method to Anthropic `/login` for headless setups where the browser runs on another machine ([#10194](https://github.com/earendil-works/pi/pull/10194) by [@lucasmeijer](https://github.com/lucasmeijer)).
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Changed the default TUI mode to fullscreen. Set `tuiMode` to `"regular"` or pass `--tui-mode regular` to keep the terminal's normal scrollback.
|
|
25
|
+
- `/login` now offers "Sign in with Radius" at the top level, as the last option, with its status. After a Radius sign-in, `/login` offers to configure the Radius MCP server in the global `mcp.json` with `"auth": { "provider": "radius" }` and reloads. Cancelling a login returns to the menu it was started from.
|
|
26
|
+
- The provider docs page is renamed to [Providers](docs/providers.md), its "Cloud Providers" section is now "Provider Specific Config", and it documents Radius first.
|
|
27
|
+
- MCP OAuth credentials are now stored per server name and URL, so MCP servers with the same URL can sign in with different accounts. Credentials stored by URL alone move to the first server that uses them ([#10252](https://github.com/earendil-works/pi/issues/10252)).
|
|
28
|
+
- Codemode costs far fewer prompt tokens: with the default tools and codemode active, a GPT-5.6 request shrinks from about 5,300 to 3,300 tokens. The `codemode` description lists the script globals in one line each and points to the new [Codemode](docs/codemode.md) reference for the `models` API, which the model reads when it needs it. Declared tools say in one line how scripts call them and what the call resolves to, instead of repeating their full declaration, and the system prompt's codemode guidance and MCP server section are shorter.
|
|
29
|
+
- Codemode errors now say how to recover: reading a tool or `models` member that does not exist names the close matches (`tools.Bash` suggests `tools.bash`), `models.classify()` and `models.generateImages()` reject malformed arguments with the expected shape, an unknown model points to `models.getAvailableOfType()`, an oversized `store()` value explains what the store is for, and a script that generates images without showing them gets a note. Scripts that probed for a tool with `typeof tools.name` must use `"name" in tools`.
|
|
30
|
+
- `/login` and `/logout` now label providers without credentials as "not configured" instead of "unconfigured".
|
|
31
|
+
- OAuth browser pages now show the color Pi logo.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- Fixed MCP OAuth sign-in accepting an authorization response whose `iss` parameter names another authorization server; the code is now rejected before it is exchanged (RFC 9207).
|
|
36
|
+
- Fixed MCP OAuth sign-in failing with `Invalid scope` when the token response contains `"scope": ""`, and similar failures for other empty or `null` optional OAuth fields ([#10266](https://github.com/earendil-works/pi/issues/10266)).
|
|
37
|
+
- Fixed the sign-in URL printed by `/mcp login` not being clickable when it wraps ([#10186](https://github.com/earendil-works/pi/issues/10186)).
|
|
38
|
+
- Fixed `--provider` without `--model` being silently ignored and running the default model from another provider; it now fails with an error ([#10236](https://github.com/earendil-works/pi/issues/10236)).
|
|
39
|
+
- Fixed MCP servers that ask for more scope (`insufficient_scope`) requesting sign-in over and over. The new sign-in requested only the missing scopes, so the new token lost access the previous one had; it now keeps the granted scopes.
|
|
40
|
+
- Fixed user messages in the transcript keeping two full-width copies of every rendered line; they keep one, with identical output.
|
|
41
|
+
- Fixed `/login` and `/logout` labeling every OAuth sign-in, including Radius, as a subscription; only subscription-backed providers say "subscription", other OAuth sign-ins say "account".
|
|
42
|
+
- Fixed the startup header logo rendering with gaps in Apple Terminal; it now shows a colored "Pi" with the version instead.
|
|
43
|
+
- Fixed deferred MCP tools that `tool_search` loaded being dropped on resume and `/reload` even when their server reconnected before the next prompt, because the session restored its tools before the MCP servers reconnected.
|
|
44
|
+
- Fixed the system theme making pastel palettes such as Catppuccin Frappe much more vivid; palette colors now keep their chroma ([#10255](https://github.com/earendil-works/pi/issues/10255), [#10293](https://github.com/earendil-works/pi/pull/10293) by [@dgtlntv](https://github.com/dgtlntv)).
|
|
45
|
+
- Fixed slash command autocompletion not triggering when the input starts with whitespace ([#10218](https://github.com/earendil-works/pi/pull/10218) by [@haoqixu](https://github.com/haoqixu)).
|
|
46
|
+
- Fixed color bleeding past mouse selections and search highlights in fullscreen mode when a styled token ends at the highlight boundary ([#10169](https://github.com/earendil-works/pi/issues/10169)).
|
|
47
|
+
- Fixed memory retained per rendered message in the transcript; a long assistant message keeps about a fifth of the heap it kept before.
|
|
48
|
+
|
|
49
|
+
## [0.99.2] - 2026-09-30
|
|
50
|
+
|
|
51
|
+
### New Features
|
|
52
|
+
|
|
53
|
+
- MCP servers stay out of the way: servers with the default `codemode` exposure are no longer listed in the `codemode` description and no longer block the first prompt. They appear in a short system prompt section, and scripts find their tools with `searchTools()` and `describeNamespace()`. See [Control tool exposure](docs/mcp.md#control-tool-exposure).
|
|
54
|
+
- More MCP authentication options: `oauth.clientName` for servers that only accept known OAuth clients, and `"auth": { "provider": "<provider>" }` to authenticate HTTP servers with a provider's `/login` token. See [Authenticate with OAuth](docs/mcp.md#authenticate-with-oauth).
|
|
55
|
+
- Anthropic workload identity federation from the Anthropic SDK environment variables. See [Use an API key from the environment](docs/providers.md#use-an-api-key-from-the-environment).
|
|
56
|
+
- `/reload` enables tools newly added to the `defaultTools` setting. See [Tools](docs/settings.md#tools).
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- Added a `description` field for MCP servers (`pi mcp add --description`), shown with the server in the system prompt and used to rank its tools in tool search, and a `describeNamespace(name)` codemode helper that returns a namespace's instructions and tool names. `describeNamespace()` and `searchTools()` accept a namespace as `mcp__dev-radius`, `mcp__dev_radius`, `dev-radius`, or `dev_radius`.
|
|
61
|
+
- Added an `oauth.clientName` setting for MCP servers (`pi mcp add --oauth-client-name`) to change the client name sent during OAuth client registration, for servers that only accept known clients ([#10226](https://github.com/earendil-works/pi/issues/10226)).
|
|
62
|
+
- Added `"auth": { "provider": "<provider>" }` for HTTP MCP servers to send a provider's current `/login` token as the bearer token instead of using MCP OAuth. The token is read on every request, so provider refreshes apply. Only allowed in the global `mcp.json` and from extensions, and requires https except on loopback hosts.
|
|
63
|
+
- Added Anthropic workload identity federation from the `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, and `ANTHROPIC_IDENTITY_TOKEN_FILE` environment variables (see [Providers](docs/providers.md)) ([#10177](https://github.com/earendil-works/pi/issues/10177), [#10242](https://github.com/earendil-works/pi/pull/10242) by [@philfreo](https://github.com/philfreo)).
|
|
64
|
+
- `/reload` now enables tools newly added to the `defaultTools` setting. Tools removed from it stay enabled, tools turned off during the session stay off unless newly added, and `--tools`, `--no-tools`, and `--no-builtin-tools` still override the setting ([#10245](https://github.com/earendil-works/pi/issues/10245)).
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- MCP servers with the default `codemode` exposure no longer appear in the `codemode` description; scripts find them with `searchTools()`. `codemode-deferred` is now an alias for `codemode`. Use `direct` exposure for tools the model should see without searching ([#10212](https://github.com/earendil-works/pi/issues/10212)).
|
|
69
|
+
- The `codemode` description no longer includes deferred tools, tool counts, or MCP server instructions, so it no longer changes when MCP servers connect or change their tools. The `tool_search` description no longer lists the servers whose tools it can load, for the same reason. Servers are listed instead in an `mcp_servers` system prompt section with a one-line summary, updated at the start of each prompt; a changed section is appended to the conversation. Scripts read server instructions with `describeNamespace()` ([#10212](https://github.com/earendil-works/pi/issues/10212)).
|
|
70
|
+
- The first prompt no longer waits for MCP servers without `direct` tools. They connect in the background and are waited for when a codemode script names them, a script searches tools, or `tool_search` runs ([#10212](https://github.com/earendil-works/pi/issues/10212)).
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- Fixed new sessions intermittently ignoring the saved default model, or warning that no models are available, when it belongs to an extension-registered native provider with a stored credential ([#9962](https://github.com/earendil-works/pi/issues/9962), [#10190](https://github.com/earendil-works/pi/pull/10190) by [@davidbrai](https://github.com/davidbrai)).
|
|
75
|
+
- Fixed the `/mcp` sign-in URL not being clickable when it wraps across lines, by emitting it as a terminal hyperlink with a `Cmd/Ctrl+click to open` line like `/login` ([#10186](https://github.com/earendil-works/pi/issues/10186)).
|
|
76
|
+
- Fixed codemode `image()` accepting malformed base64 data or unsupported image types, which persisted an invalid image block that made every later provider request fail with HTTP 400 ([#10215](https://github.com/earendil-works/pi/issues/10215)).
|
|
77
|
+
- Fixed codemode failing to start its script worker from the standalone Windows executable ([#10204](https://github.com/earendil-works/pi/issues/10204)).
|
|
78
|
+
- Fixed prompt submission slowing down with session length, because resolving the session's model selection looked up the model catalog once per assistant message ([#10198](https://github.com/earendil-works/pi/issues/10198)).
|
|
79
|
+
- Fixed model lookups slowing down for providers with a refreshed pi.dev catalog, because merging remote catalog models took quadratic time.
|
|
80
|
+
- Fixed the `built-in-tool-renderer.ts` and `minimal-mode.ts` extension examples removing the built-in tools' summaries and guidelines from the system prompt ([#10072](https://github.com/earendil-works/pi/issues/10072), [#10193](https://github.com/earendil-works/pi/pull/10193) by [@christianklotz](https://github.com/christianklotz)).
|
|
81
|
+
- Fixed context overflow detection for Z.AI CN endpoint `Prompt exceeds max length` errors ([#10208](https://github.com/earendil-works/pi/issues/10208)).
|
|
82
|
+
- Fixed Anthropic requests failing when a tool schema uses keywords Anthropic strict tool use rejects, such as `minimum`/`maximum`; such tools are now sent non-strict ([#9953](https://github.com/earendil-works/pi/issues/9953)).
|
|
83
|
+
- Fixed provider retries firing immediately when a `Retry-After` header contains an unparseable date; they now use exponential backoff ([#9571](https://github.com/earendil-works/pi/issues/9571)).
|
|
84
|
+
- Fixed extension commands registered without a string name or handler crashing pi when typing `/`; the extension now fails to load with an error instead ([#10054](https://github.com/earendil-works/pi/issues/10054)).
|
|
85
|
+
- Fixed collapsed `codemode` and MCP tool results filling the screen when the output is one long line, such as minified JSON. Like bash output, the preview is now limited to wrapped lines instead of logical lines.
|
|
86
|
+
- Fixed `codemode.mode: "only"` listing `read`, `bash`, `edit`, and `write` in the system prompt's tool list although requests only declare `codemode` ([#10192](https://github.com/earendil-works/pi/issues/10192)).
|
|
87
|
+
- Fixed codemode scripts calling the wrong MCP tool when two tool names differ only in `-` and `_`, such as `read-file` and `read_file`. Like in Codex, MCP tool and namespace names now replace `-` with `_` (`mcp__my-server__x` is now `mcp__my_server__x`), colliding tools of a server all get a hash suffix, and server names that differ only in `-` and `_` are rejected ([#10239](https://github.com/earendil-works/pi/issues/10239)).
|
|
88
|
+
|
|
3
89
|
## [0.99.1] - 2026-09-29
|
|
4
90
|
|
|
5
91
|
### New Features
|
package/bin/docs/cli.md
CHANGED
|
@@ -58,10 +58,10 @@ RPC mode rejects `@file` arguments. JSON and RPC modes reserve stdout for protoc
|
|
|
58
58
|
pi --model sonnet:high
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
See [Choose a Model](models.md) for model selection and [
|
|
61
|
+
See [Choose a Model](models.md) for model selection and [Providers](providers.md) for credentials.
|
|
62
62
|
|
|
63
63
|
- `--provider <name>`<br>
|
|
64
|
-
Restricts `--model` lookup to one provider.
|
|
64
|
+
Restricts `--model` lookup to one provider. It requires `--model`.
|
|
65
65
|
- `--model <pattern>`<br>
|
|
66
66
|
Selects by exact ID or fuzzy ID/name match. It accepts `provider/id` and an optional `:<thinking>` suffix.
|
|
67
67
|
- `--api-key <key>`<br>
|
|
@@ -161,21 +161,11 @@ This keeps `read`, `bash`, `edit`, and `write` and adds `codemode`. For one invo
|
|
|
161
161
|
pi --tools read,bash,edit,write,codemode
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
Codemode is useful without MCP: scripts can run several tool calls in parallel, filter large output before it reaches the model,
|
|
164
|
+
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)).
|
|
165
165
|
|
|
166
166
|
### How codemode works
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
A script may start with an options line such as `// @options: {"max_output_tokens": 2000, "timeout_ms": 60000}`. `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. `timeout_ms` is a hard deadline, unset by default.
|
|
171
|
-
|
|
172
|
-
While `codemode` is active, `codemode.mode` in [settings](settings.md#tools) decides how the other tools are presented. With `on` (default) declared tools keep being declared and their descriptions show how to call them from scripts. With `only` they are hidden from the model and listed in the `codemode` description instead, so the model calls them through scripts.
|
|
173
|
-
|
|
174
|
-
The `codemode` description lists the callable tools with their TypeScript declarations, grouped by namespace (for example one MCP server). Declarations share a budget of 3000 estimated tokens (`codemode.inlineBudget` in [settings](settings.md#tools)); every namespace is still listed with its tool count, and the description says whether the list is complete. Scripts find the rest with `await searchTools(query, { limit, namespace })`, which ranks tools with BM25, and `await describeTool(name)`, or by filtering `ALL_TOOLS`.
|
|
175
|
-
|
|
176
|
-
Tools with an output schema resolve to structured values: `bash` to `{ output, truncated, full_output_path?, exit_code, wall_time_seconds }`, also for non-zero exit codes, and MCP tools to their `CallToolResult`. Other tools resolve to their text output. The `output` of `bash` is not limited to the 2000 lines or 50KB the model sees: it holds up to 1 MiB, and longer output keeps its first and last 512 KiB around an omission marker, with `truncated` set and the full output in `full_output_path`.
|
|
177
|
-
|
|
178
|
-
`store(key, value)` and `load(key)` keep JSON values across `codemode` calls: each successful script that stores values appends a `codemode-store` custom entry to the session, so resumed sessions keep the values and each branch sees only the values written on its path. Scripts can also use `models`: `getModelsOfType`, `getAvailableOfType`, and `getModelOfType` list the model catalog, and `classify(model, context)` runs a classifier model with the session's credentials, at most four at a time per script.
|
|
168
|
+
Scripts run in a QuickJS sandbox and reach the other tools through `tools.<name>(args)`. [Codemode](codemode.md) describes the script API, how tools are listed and found, the `store()` and `models` globals, and the limits.
|
|
179
169
|
|
|
180
170
|
### Tool search
|
|
181
171
|
|
|
@@ -229,7 +219,7 @@ See [Configuration](configuration.md) for saved configuration, [Security](securi
|
|
|
229
219
|
- `--append-system-prompt <text|path>`<br>
|
|
230
220
|
Appends text or an existing file to the system prompt and is repeatable.
|
|
231
221
|
- `--tui-mode <mode>`<br>
|
|
232
|
-
Uses `
|
|
222
|
+
Uses `fullscreen` (default) or `regular` terminal mode.
|
|
233
223
|
- `--verbose`<br>
|
|
234
224
|
Shows verbose interactive startup information, overriding `quietStartup`.
|
|
235
225
|
- `-a`, `--approve`<br>
|
|
@@ -292,7 +282,7 @@ Add `--force` to reinstall Pi when the selected update includes Pi.
|
|
|
292
282
|
pi auth check --provider openai --json
|
|
293
283
|
```
|
|
294
284
|
|
|
295
|
-
Authentication commands require `--provider <provider>` or `--model <model>`. See [
|
|
285
|
+
Authentication commands require `--provider <provider>` or `--model <model>`. See [Providers](providers.md) for supported methods.
|
|
296
286
|
|
|
297
287
|
| Command | Description |
|
|
298
288
|
|---|---|
|
|
@@ -318,12 +308,12 @@ These commands work outside a session, so agents can run them through `bash`. Se
|
|
|
318
308
|
| Command | Description |
|
|
319
309
|
|---|---|
|
|
320
310
|
| `pi mcp add <server> [options] -- <command> [args...]` | Add or replace a stdio server in `mcp.json`; `--env KEY=VALUE` (repeatable) and `--cwd <dir>` set its environment and working directory. Arguments after the command are passed to it |
|
|
321
|
-
| `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`, and `--oauth-
|
|
311
|
+
| `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 |
|
|
322
312
|
| `pi mcp remove <server>` | Remove a server from `mcp.json`; stored OAuth credentials are kept |
|
|
323
313
|
| `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 |
|
|
324
314
|
| `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 |
|
|
325
315
|
| `pi mcp logout <server>` | Delete the stored OAuth credentials of a server |
|
|
326
316
|
|
|
327
|
-
`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 does not connect; run `pi mcp list` to check the server.
|
|
317
|
+
`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.
|
|
328
318
|
|
|
329
319
|
Project `.pi/mcp.json` files are only read for projects that are already trusted.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Codemode
|
|
2
|
+
|
|
3
|
+
The `codemode` tool lets the model write a JavaScript script that calls pi's other tools and runs non-LLM models, such as classifiers and image models. Only the script's output reaches the model, so a script can run calls in parallel and filter large results before the model sees them. To turn it on, see [Enable codemode](cli.md#enable-codemode).
|
|
4
|
+
|
|
5
|
+
## Scripts
|
|
6
|
+
|
|
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
|
+
|
|
9
|
+
A script may start with an options line:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
// @options: {"max_output_tokens": 2000, "timeout_ms": 60000}
|
|
13
|
+
```
|
|
14
|
+
|
|
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.
|
|
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
|
+
|
|
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.
|
|
19
|
+
|
|
20
|
+
## Globals
|
|
21
|
+
|
|
22
|
+
| Global | Purpose |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `tools.<name>(args)` | Call a tool. See [Call tools](#call-tools). |
|
|
25
|
+
| `text(value)` | Add a text item to the output. Strings are added as is, other values as JSON. |
|
|
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. |
|
|
27
|
+
| `console.log(...)` | Like `text()`; `info`, `warn`, `error`, and `debug` do the same. |
|
|
28
|
+
| `return value` | A top-level `return` adds the value like `text()`. |
|
|
29
|
+
| `exit()` | End the script successfully. |
|
|
30
|
+
| `store(key, value)` / `load(key)` | Keep small JSON values across `codemode` calls. See [Store values](#store-values). |
|
|
31
|
+
| `ALL_TOOLS` | Every callable tool as `{ name, description }`, including tools the description does not list. |
|
|
32
|
+
| `searchTools(query, { limit?, namespace? })` | Rank callable tools by relevance (BM25, default limit 8). Resolves to `{ name, description }[]`. |
|
|
33
|
+
| `describeTool(name)` | Resolves to a tool's description and TypeScript declaration, or `undefined`. |
|
|
34
|
+
| `describeNamespace(name)` | Resolves to `{ name, description?, instructions?, tools }` for a namespace such as an MCP server, or `undefined`. |
|
|
35
|
+
| `models` | List and run non-LLM models. See [Models](#models). |
|
|
36
|
+
|
|
37
|
+
## Call tools
|
|
38
|
+
|
|
39
|
+
Every tool the session can call is a method of `tools`, named by its identifier: characters that are not valid in a JavaScript identifier become `_`, so the MCP tool `mcp__dev-radius__search` is `tools.mcp__dev_radius__search`. Each method takes one object with the tool's arguments.
|
|
40
|
+
|
|
41
|
+
What a call resolves to depends on the tool:
|
|
42
|
+
|
|
43
|
+
- Tools with an output schema resolve to a structured value. `bash` resolves to `{ output, truncated, full_output_path?, exit_code, wall_time_seconds }`, also for non-zero exit codes. Its `output` is not limited to the 2000 lines or 50KB the model sees: it holds up to 1 MiB, and longer output keeps its first and last 512 KiB around an omission marker, with `truncated` set and the full output in `full_output_path`.
|
|
44
|
+
- MCP tools resolve to their `CallToolResult`, including `isError` and `structuredContent`.
|
|
45
|
+
- Other tools, such as `read`, `edit`, and `write`, resolve to their text output.
|
|
46
|
+
|
|
47
|
+
A call that fails, is blocked, or gets invalid arguments rejects with an `Error` that carries the tool's error text. Use `Promise.allSettled()` to keep the results of the calls that succeed.
|
|
48
|
+
|
|
49
|
+
The `codemode` description lists tools with their TypeScript declarations, grouped by namespace (for example one MCP server). Tools with `deferred` exposure, which includes MCP tools with the default `codemode` exposure, are not listed, so the description stays the same while MCP servers connect. Listed declarations share a budget of 3000 estimated tokens (`codemode.inlineBudget` in [settings](settings.md#tools)). Scripts find the other tools with `searchTools()`, `describeTool()`, `describeNamespace()`, or by filtering `ALL_TOOLS`.
|
|
50
|
+
|
|
51
|
+
While `codemode` is active, `codemode.mode` in [settings](settings.md#tools) decides how the other tools are presented. With `on` (default) declared tools stay declared, and their descriptions say how to call them from scripts. With `only` they are hidden from the model and listed in the `codemode` description instead, so the model calls them through scripts.
|
|
52
|
+
|
|
53
|
+
## Store values
|
|
54
|
+
|
|
55
|
+
`store(key, value)` keeps a JSON value under a string key for later `codemode` calls; storing `undefined` deletes the key. `load(key)` returns the value, or `undefined`. Writes are kept only when the script succeeds: each successful script that stores values appends a `codemode-store` custom entry to the session, so resumed sessions keep the values and each branch sees only the values written on its path.
|
|
56
|
+
|
|
57
|
+
The store is for small state such as IDs, cursors, or summaries. One value may have at most 262144 characters of JSON and all values together at most 1048576. Do not store image data; show images with `image()` or write them to a file with a tool.
|
|
58
|
+
|
|
59
|
+
## Models
|
|
60
|
+
|
|
61
|
+
`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
|
+
|
|
63
|
+
```ts
|
|
64
|
+
type ModelType = "chat" | "image" | "classifier";
|
|
65
|
+
|
|
66
|
+
/** A catalog entry. `provider` and `id` identify it; other fields depend on the type. */
|
|
67
|
+
interface ModelInfo {
|
|
68
|
+
type?: ModelType;
|
|
69
|
+
provider: string;
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
api: string;
|
|
73
|
+
input: ("text" | "image")[];
|
|
74
|
+
contextWindow?: number;
|
|
75
|
+
[key: string]: unknown;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
declare const models: {
|
|
79
|
+
/** Every known model of a type, optionally for one provider. */
|
|
80
|
+
getModelsOfType(type: ModelType, provider?: string): Promise<ModelInfo[]>;
|
|
81
|
+
/** Models of a type whose provider has working credentials. */
|
|
82
|
+
getAvailableOfType(type: ModelType, provider?: string): Promise<ModelInfo[]>;
|
|
83
|
+
/** One catalog entry, or undefined. */
|
|
84
|
+
getModelOfType(type: ModelType, provider: string, id: string): Promise<ModelInfo | undefined>;
|
|
85
|
+
/** Answer `context.questions` about `context.state`; answers are in `result.answers` by question ID. */
|
|
86
|
+
classify(model: ModelInfo, context: ClassifierContext): Promise<ClassifierResult>;
|
|
87
|
+
/** Generate images from `context.input` text and image blocks; show `result.output` blocks with image(). Can take minutes. */
|
|
88
|
+
generateImages(model: ModelInfo, context: ImagesContext): Promise<ImagesResult>;
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`classify()` and `generateImages()` use only the `provider` and `id` of `model`, so `{ provider, id }` works as well. They do not throw on provider errors: check `stopReason` and `errorMessage`. At most four such calls run at once per script; more calls wait for a free slot, so `Promise.all()` over many items is fine. Their usage is added to the `codemode` tool result and counts toward the session cost.
|
|
93
|
+
|
|
94
|
+
Model IDs differ between providers, for example `typesafe/jev-latest` and `openrouter/typesafe/jev-1.13`. Use `models.getAvailableOfType(type)` to find the IDs that work with the current credentials.
|
|
95
|
+
|
|
96
|
+
### Classify
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
interface ClassifierContext {
|
|
100
|
+
/** The data to classify. */
|
|
101
|
+
state: Record<string, unknown>;
|
|
102
|
+
/** Questions by ID. One call answers all of them. */
|
|
103
|
+
questions: Record<string, ClassifierQuestion>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type ClassifierQuestion =
|
|
107
|
+
/** Pick one label. `criteria` maps each label to what it means. */
|
|
108
|
+
| { type: "choice"; instructions: string; criteria: Record<string, string> }
|
|
109
|
+
/** Score on an ordered scale. `criteria` describes each level, lowest first. */
|
|
110
|
+
| { type: "score"; instructions: string; criteria: string[] }
|
|
111
|
+
/** Yes or no. */
|
|
112
|
+
| { type: "bool"; instructions: string; criteria: { true: string; false: string } };
|
|
113
|
+
|
|
114
|
+
interface ClassifierResult {
|
|
115
|
+
provider: string;
|
|
116
|
+
model: string;
|
|
117
|
+
/** Answers by question ID. */
|
|
118
|
+
answers: Record<string, ClassifierAnswer>;
|
|
119
|
+
usage?: ModelUsage;
|
|
120
|
+
stopReason: "stop" | "error" | "aborted";
|
|
121
|
+
errorMessage?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
type ClassifierAnswer =
|
|
125
|
+
| { type: "choice"; choice: string; probabilities: Record<string, number>; confidence: number }
|
|
126
|
+
/** `score` is the expected level index, from 0 to `criteria.length - 1`. */
|
|
127
|
+
| { type: "score"; score: number; confidence: number }
|
|
128
|
+
/** Probability of `true`. */
|
|
129
|
+
| { type: "bool"; probability: number };
|
|
130
|
+
|
|
131
|
+
/** Token counts and cost in USD, when the service reports them. */
|
|
132
|
+
type ModelUsage = { input: number; output: number; totalTokens: number; cost: { total: number } };
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Classify several items by calling `classify()` once per item. This script sorts feedback messages, for example ones a tool returned earlier in the script:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
const jev = await models.getModelOfType("classifier", "typesafe", "jev-latest");
|
|
139
|
+
const results = await Promise.all(
|
|
140
|
+
messages.map((message) =>
|
|
141
|
+
models.classify(jev, {
|
|
142
|
+
state: { message },
|
|
143
|
+
questions: {
|
|
144
|
+
sentiment: {
|
|
145
|
+
type: "choice",
|
|
146
|
+
instructions: "How does the user feel about the product?",
|
|
147
|
+
criteria: { positive: "Satisfied or happy", negative: "Unhappy or frustrated", neutral: "Neither" },
|
|
148
|
+
},
|
|
149
|
+
urgency: {
|
|
150
|
+
type: "score",
|
|
151
|
+
instructions: "How urgently does this need a reply?",
|
|
152
|
+
criteria: ["no reply needed", "reply this week", "reply today"],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
return results.map((result, i) =>
|
|
159
|
+
result.stopReason === "stop"
|
|
160
|
+
? { message: messages[i], sentiment: result.answers.sentiment.choice, urgency: result.answers.urgency.score }
|
|
161
|
+
: { message: messages[i], error: result.errorMessage },
|
|
162
|
+
);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Generate images
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
interface ImagesContext {
|
|
169
|
+
/** The prompt as text blocks, plus image blocks to edit or use as references. */
|
|
170
|
+
input: (TextBlock | ImageBlock)[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
interface ImagesResult {
|
|
174
|
+
provider: string;
|
|
175
|
+
model: string;
|
|
176
|
+
/** Generated images, and text blocks for models that also return text. */
|
|
177
|
+
output: (TextBlock | ImageBlock)[];
|
|
178
|
+
usage?: ModelUsage;
|
|
179
|
+
stopReason: "stop" | "error" | "aborted";
|
|
180
|
+
errorMessage?: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
type TextBlock = { type: "text"; text: string };
|
|
184
|
+
/** `data` is base64. */
|
|
185
|
+
type ImageBlock = { type: "image"; data: string; mimeType: string };
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Show generated images with `image(block)`. Do not print `data` with `text()`, `console`, or `return`: it is large and the model cannot read it as text. Generated images are not saved to disk; to keep one, write it to a file with a tool.
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
// @options: {"timeout_ms": 300000}
|
|
192
|
+
const painter = await models.getModelOfType("image", "openrouter", "google/gemini-2.5-flash-image");
|
|
193
|
+
const result = await models.generateImages(painter, {
|
|
194
|
+
input: [{ type: "text", text: "A red fox in the snow, watercolor" }],
|
|
195
|
+
});
|
|
196
|
+
if (result.stopReason !== "stop") return result.errorMessage;
|
|
197
|
+
for (const block of result.output) {
|
|
198
|
+
if (block.type === "image") image(block);
|
|
199
|
+
else text(block.text);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Limits
|
|
204
|
+
|
|
205
|
+
- A script's VM has 256 MB of memory. Running out throws `InternalError: out of memory`; filter or aggregate large data instead of accumulating it.
|
|
206
|
+
- A script that waits on a promise that can never settle (no tool call pending) fails immediately, since there are no timers.
|
|
207
|
+
- Scripts cannot start other `codemode` scripts.
|
package/bin/docs/docs.json
CHANGED
|
@@ -136,6 +136,10 @@
|
|
|
136
136
|
"title": "CLI",
|
|
137
137
|
"path": "cli.md"
|
|
138
138
|
},
|
|
139
|
+
{
|
|
140
|
+
"title": "Codemode",
|
|
141
|
+
"path": "codemode.md"
|
|
142
|
+
},
|
|
139
143
|
{
|
|
140
144
|
"title": "Slash Commands",
|
|
141
145
|
"path": "slash-commands.md"
|
|
@@ -153,7 +157,7 @@
|
|
|
153
157
|
"path": "keybindings.md"
|
|
154
158
|
},
|
|
155
159
|
{
|
|
156
|
-
"title": "
|
|
160
|
+
"title": "Providers",
|
|
157
161
|
"path": "providers.md"
|
|
158
162
|
},
|
|
159
163
|
{
|
|
@@ -6,7 +6,7 @@ Pi uses environment variables in three ways:
|
|
|
6
6
|
- Pi sets process markers so child processes can identify Pi as the launching agent.
|
|
7
7
|
- Commands run by the LLM-callable shell tools receive `PI_*` variables describing the current session.
|
|
8
8
|
|
|
9
|
-
Provider API-key variables are documented separately in [
|
|
9
|
+
Provider API-key variables are documented separately in [Providers](providers.md#use-an-api-key-from-the-environment).
|
|
10
10
|
|
|
11
11
|
## Process Marker
|
|
12
12
|
|
|
@@ -95,4 +95,4 @@ These variables are read by Pi itself:
|
|
|
95
95
|
| `VISUAL`, `EDITOR` | External editor fallback when `externalEditor` is unset |
|
|
96
96
|
| `HTTP_PROXY`, `HTTPS_PROXY` | Proxy outbound HTTP requests |
|
|
97
97
|
|
|
98
|
-
Provider credentials such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and
|
|
98
|
+
Provider credentials such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and provider-specific configuration are listed in [Providers](providers.md#use-an-api-key-from-the-environment).
|
package/bin/docs/extensions.md
CHANGED
|
@@ -159,7 +159,7 @@ See [`hello.ts`](../examples/extensions/hello.ts), [`todo.ts`](../examples/exten
|
|
|
159
159
|
- `deferred`: like `codemode`, but codemode tools do not list it; `tool_search` can find and activate it.
|
|
160
160
|
- `hidden`: registered but unreachable. Re-register a tool with `exposure: "hidden"` to withdraw it, since tools cannot be unregistered.
|
|
161
161
|
|
|
162
|
-
`namespace: { name, description }` groups related tools, as MCP servers do. Codemode tools list a namespace under one heading
|
|
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
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
165
|
|
|
@@ -177,7 +177,7 @@ pi.on("tool_call", async (event, ctx) => {
|
|
|
177
177
|
});
|
|
178
178
|
```
|
|
179
179
|
|
|
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 and namespace. 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. `codemode`
|
|
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 and namespace. 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. `codemode` uses only this hook, `exposure`, and `ctx.executeTool()`, so another tool can implement the same behavior under a different name.
|
|
181
181
|
|
|
182
182
|
### Activate tools dynamically
|
|
183
183
|
|
|
@@ -187,7 +187,7 @@ Pi records the initial prompt and tool set in the transcript's first system mess
|
|
|
187
187
|
|
|
188
188
|
### MCP servers
|
|
189
189
|
|
|
190
|
-
`pi.registerMcpServer(name, config)` adds an MCP server for the current session. `config` has the shape of an `mcpServers` entry in [`mcp.json`](mcp.md): `command`, `args`, `env`, and `cwd` for stdio servers, `url`, `headers`, and `oauth` for HTTP servers, plus `exposure`, `toolExposure`, `enabled`, and `timeout`.
|
|
190
|
+
`pi.registerMcpServer(name, config)` adds an MCP server for the current session. `config` has the shape of an `mcpServers` entry in [`mcp.json`](mcp.md): `command`, `args`, `env`, and `cwd` for stdio servers, `url`, `headers`, and `oauth` for HTTP servers, plus `exposure`, `toolExposure`, `description`, `enabled`, and `timeout`.
|
|
191
191
|
|
|
192
192
|
```typescript
|
|
193
193
|
pi.registerMcpServer("jira", { url: "https://mcp.example.com/jira", exposure: "codemode" });
|
package/bin/docs/index.md
CHANGED
|
@@ -30,7 +30,7 @@ Use the [Quickstart customization chooser](quickstart.md#choose-how-to-customize
|
|
|
30
30
|
|
|
31
31
|
## Find reference and setup information
|
|
32
32
|
|
|
33
|
-
Use the reference pages to look up [CLI options](cli.md), [settings](settings.md), [
|
|
33
|
+
Use the reference pages to look up [CLI options](cli.md), [settings](settings.md), [providers](providers.md), [keybindings](keybindings.md), and [environment variables](environment-variables.md).
|
|
34
34
|
|
|
35
35
|
For platform-specific help, see [Terminal Setup](terminal-setup.md), [Windows](windows.md), [tmux](tmux.md), [Termux on Android](termux.md), or [Containerization](containerization.md).
|
|
36
36
|
|