mu-agent 0.1.7 → 0.1.8

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 (143) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +5 -4
  3. package/dist/bundle/chunks/anthropic-messages-MI4QHRXW.js +6 -0
  4. package/dist/bundle/chunks/anthropic.js +2 -2
  5. package/dist/bundle/chunks/azure-openai-responses-E3QGN4VN.js +2 -0
  6. package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
  7. package/dist/bundle/chunks/chunk-33KL6IIQ.js +20 -0
  8. package/dist/bundle/chunks/chunk-4OBXYPH4.js +4 -0
  9. package/dist/bundle/chunks/chunk-C47CLQZK.js +2 -0
  10. package/dist/bundle/chunks/chunk-F5ZABL6Z.js +20 -0
  11. package/dist/bundle/chunks/chunk-H3QXYIF7.js +31 -0
  12. package/dist/bundle/chunks/chunk-HQHD6PZI.js +2 -0
  13. package/dist/bundle/chunks/chunk-KJUDFE6Y.js +11 -0
  14. package/dist/bundle/chunks/chunk-LD2P6SFZ.js +2 -0
  15. package/dist/bundle/chunks/chunk-NG4PO5FP.js +1204 -0
  16. package/dist/bundle/chunks/chunk-NTSWBG4X.js +2 -0
  17. package/dist/bundle/chunks/chunk-RLZVFGW4.js +127 -0
  18. package/dist/bundle/chunks/{chunk-XDUWOHPL.js → chunk-S3GGRYIO.js} +2 -2
  19. package/dist/bundle/chunks/{chunk-4O2SCVYF.js → chunk-TAOL3JFA.js} +1 -1
  20. package/dist/bundle/chunks/chunk-TE3GUV6H.js +9 -0
  21. package/dist/bundle/chunks/chunk-VIV3CWXG.js +2 -0
  22. package/dist/bundle/chunks/{chunk-V7IXECST.js → chunk-WE4MXUQS.js} +3 -3
  23. package/dist/bundle/chunks/chunk-Y2PP32XV.js +2 -0
  24. package/dist/bundle/chunks/chunk-YLY6CKPK.js +132 -0
  25. package/dist/bundle/chunks/chunk-ZE5FMF3V.js +99 -0
  26. package/dist/bundle/chunks/cli-AOVD3SSZ.js +51 -0
  27. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NM3JRRWQ.js +2 -0
  28. package/dist/bundle/chunks/codemode-worker.js +374 -0
  29. package/dist/bundle/chunks/easter-egg-3d-G2LUBEMZ.js +2 -0
  30. package/dist/bundle/chunks/execute-MZVUC6BB.js +388 -0
  31. package/dist/bundle/chunks/github-copilot.js +1 -1
  32. package/dist/bundle/chunks/{google-generative-ai-C44KRRZ4.js → google-generative-ai-XMFHGQC5.js} +1 -1
  33. package/dist/bundle/chunks/{google-vertex-25DUXPTJ.js → google-vertex-4XRRNP74.js} +1 -1
  34. package/dist/bundle/chunks/{lib-HGQMNYVT.js → lib-TEDK3E5I.js} +2 -2
  35. package/dist/bundle/chunks/llama-cpp-classify-XT44PI3C.js +22 -0
  36. package/dist/bundle/chunks/mistral-conversations-Q22M3IBG.js +5 -0
  37. package/dist/bundle/chunks/node-N5BSDOA5.js +16 -0
  38. package/dist/bundle/chunks/openai-chatgpt.js +86 -0
  39. package/dist/bundle/chunks/{openai-codex-responses-3UAYOSSR.js → openai-codex-responses-4MYQSO5E.js} +1 -1
  40. package/dist/bundle/chunks/openai-codex.js +2 -2
  41. package/dist/bundle/chunks/openai-completions-OSRCWAAA.js +7 -0
  42. package/dist/bundle/chunks/openai-responses-UHF3DHNG.js +3 -0
  43. package/dist/bundle/chunks/{openrouter-images-JVEJN44Q.js → openrouter-images-ZVXWCSIM.js} +1 -1
  44. package/dist/bundle/chunks/openrouter.js +2 -2
  45. package/dist/bundle/chunks/radius.js +2 -2
  46. package/dist/bundle/chunks/runtime-VJMMDLVH.js +2 -0
  47. package/dist/bundle/chunks/typesafe-system-one-PRR5K6CI.js +2 -0
  48. package/dist/bundle/chunks/virtual-modules-RJXJGCPI.js +2 -0
  49. package/dist/bundle/cli-runtime.js +1 -1
  50. package/dist/bundle/index.js +1 -1
  51. package/dist/bundle/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  52. package/dist/bundle/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  53. package/dist/bundle/rpc-entry.js +1 -1
  54. package/dist/core/export-html/template.js +16 -0
  55. package/dist/modes/interactive/theme/dark.json +59 -72
  56. package/dist/modes/interactive/theme/light.json +57 -69
  57. package/dist/modes/interactive/theme/system-theme.d.ts +55 -0
  58. package/dist/modes/interactive/theme/system-theme.d.ts.map +1 -0
  59. package/dist/modes/interactive/theme/system-theme.js +546 -0
  60. package/dist/modes/interactive/theme/system-theme.js.map +1 -0
  61. package/dist/modes/interactive/theme/theme-controller.d.ts +43 -9
  62. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  63. package/dist/modes/interactive/theme/theme-controller.js +121 -79
  64. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  65. package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -1
  66. package/dist/modes/interactive/theme/theme-json.js +1 -1
  67. package/dist/modes/interactive/theme/theme-json.js.map +1 -1
  68. package/dist/modes/interactive/theme/theme-schema.json +2 -2
  69. package/dist/modes/interactive/theme/theme.d.ts +34 -40
  70. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  71. package/dist/modes/interactive/theme/theme.js +139 -126
  72. package/dist/modes/interactive/theme/theme.js.map +1 -1
  73. package/dist/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  74. package/dist/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  75. package/docs/cli.md +60 -7
  76. package/docs/codemode.md +207 -0
  77. package/docs/configuration.md +2 -0
  78. package/docs/docs.json +13 -1
  79. package/docs/environment-variables.md +2 -2
  80. package/docs/extensions.md +53 -0
  81. package/docs/index.md +1 -1
  82. package/docs/keybindings.md +2 -2
  83. package/docs/llama-cpp.md +13 -0
  84. package/docs/mcp.md +270 -0
  85. package/docs/models.md +83 -1
  86. package/docs/packages.md +1 -1
  87. package/docs/providers.md +19 -6
  88. package/docs/quickstart.md +16 -2
  89. package/docs/rpc-commands.md +9 -3
  90. package/docs/rpc.md +3 -1
  91. package/docs/sdk.md +8 -3
  92. package/docs/security.md +2 -0
  93. package/docs/session-format.md +5 -1
  94. package/docs/settings.md +24 -5
  95. package/docs/themes.md +28 -6
  96. package/docs/usage.md +1 -1
  97. package/docs/virtual-models.md +114 -0
  98. package/docs/windows.md +2 -0
  99. package/examples/README.md +1 -1
  100. package/examples/extensions/README.md +1 -0
  101. package/examples/extensions/built-in-tool-renderer.ts +16 -40
  102. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  103. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  104. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  105. package/examples/extensions/custom-provider-gitlab-duo/test.ts +5 -5
  106. package/examples/extensions/gondolin/package-lock.json +2 -2
  107. package/examples/extensions/gondolin/package.json +1 -1
  108. package/examples/extensions/jev-router.ts +113 -0
  109. package/examples/extensions/minimal-mode.ts +24 -96
  110. package/examples/extensions/sandbox/package-lock.json +2 -2
  111. package/examples/extensions/sandbox/package.json +1 -1
  112. package/examples/extensions/with-deps/package-lock.json +2 -2
  113. package/examples/extensions/with-deps/package.json +1 -1
  114. package/examples/rpc-client.ts +1 -1
  115. package/examples/rpc-extension-ui.ts +1 -1
  116. package/examples/sdk/14-codemode-mcp.ts +61 -0
  117. package/examples/sdk/README.md +2 -1
  118. package/judge/THIRD_PARTY_NOTICES.md +63 -0
  119. package/judge/dist/auth.js +128 -113
  120. package/judge/dist/import.js +10 -9
  121. package/judge/dist/kyrn-judge.js +5444 -5354
  122. package/judge/dist/setup.js +138 -12
  123. package/judge/manifest.json +414 -102
  124. package/kyrn/bin/kyrn-doctor +61 -11
  125. package/kyrn/bin/mu.mjs +84 -69
  126. package/package.json +4 -4
  127. package/dist/bundle/chunks/anthropic-messages-VMESUHQ7.js +0 -6
  128. package/dist/bundle/chunks/azure-openai-responses-K232MQFF.js +0 -2
  129. package/dist/bundle/chunks/chunk-AYFRR3OD.js +0 -2
  130. package/dist/bundle/chunks/chunk-DTD7JQ7Y.js +0 -42
  131. package/dist/bundle/chunks/chunk-FU4XQUSL.js +0 -11
  132. package/dist/bundle/chunks/chunk-GNYZK6XN.js +0 -2
  133. package/dist/bundle/chunks/chunk-HEMXN7VJ.js +0 -1653
  134. package/dist/bundle/chunks/chunk-MVPXOVOJ.js +0 -2
  135. package/dist/bundle/chunks/chunk-NUHFSC37.js +0 -25
  136. package/dist/bundle/chunks/chunk-XNGRGP62.js +0 -2
  137. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NR2TDCXU.js +0 -2
  138. package/dist/bundle/chunks/mistral-conversations-6IHQOR4R.js +0 -5
  139. package/dist/bundle/chunks/node-O3RIBJRH.js +0 -16
  140. package/dist/bundle/chunks/openai-completions-FIEQGWAH.js +0 -7
  141. package/dist/bundle/chunks/openai-responses-OZJXLHFC.js +0 -2
  142. package/dist/bundle/chunks/typesafe-system-one-VKYX4OGY.js +0 -2
  143. package/dist/bundle/chunks/virtual-modules-V7SXVVLP.js +0 -2
package/docs/mcp.md ADDED
@@ -0,0 +1,270 @@
1
+ # MCP Servers
2
+
3
+ Pi connects to [Model Context Protocol](https://modelcontextprotocol.io) servers over stdio or streamable HTTP and makes their tools and resources available to the model.
4
+
5
+ ## Quick setup
6
+
7
+ Add a local stdio server, check the connection, then start Pi:
8
+
9
+ ```bash
10
+ pi mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem .
11
+ pi mcp list
12
+ pi
13
+ ```
14
+
15
+ For a remote server:
16
+
17
+ ```bash
18
+ pi mcp add docs --url https://example.com/mcp --bearer-token-env-var DOCS_TOKEN
19
+ pi mcp list
20
+ ```
21
+
22
+ These commands add user-level servers by default. Add `--local` or `-l` to write the project configuration instead:
23
+
24
+ ```bash
25
+ pi mcp add -l tools --env API_KEY='${TOOLS_KEY}' -- uvx tools-mcp
26
+ ```
27
+
28
+ Use `/mcp` inside an interactive session to inspect connections, sign in, reconnect, change exposure, or enable and disable servers. Run `/reload` after adding, removing, or changing a server outside the session.
29
+
30
+ ## Configure servers
31
+
32
+ Pi reads user-level servers from `~/.pi/agent/mcp.json` and project servers from `.pi/mcp.json`. Project configuration is read only after [project trust](security.md#understand-project-trust) is granted. A project entry replaces a user-level entry with the same name.
33
+
34
+ A project entry without `command`, `url`, or `type` overrides only `enabled`, `exposure`, and `toolExposure` of the user-level server with the same name and keeps the rest, including `env`, `headers`, and `auth`. For example, this turns off a user-level server in one project:
35
+
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "internal-tools": { "enabled": false }
40
+ }
41
+ }
42
+ ```
43
+
44
+ The format matches other MCP clients:
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "filesystem": {
50
+ "command": "npx",
51
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
52
+ },
53
+ "docs": {
54
+ "url": "https://example.com/mcp",
55
+ "headers": { "Authorization": "Bearer ${DOCS_TOKEN}" },
56
+ "description": "Search and read the product documentation"
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ Stdio servers use `command`, `args`, `env`, and `cwd`. Relative `cwd` values resolve against the session directory. A leading `~/` in `command`, an argument, or `cwd` names the home directory.
63
+
64
+ HTTP servers use `url`, `headers`, and `oauth` (see [Authenticate with OAuth](#authenticate-with-oauth)). The legacy SSE transport is not supported.
65
+
66
+ Both server types support:
67
+
68
+ - `timeout`: per-request timeout in seconds (default 60). Progress notifications reset it.
69
+ - `enabled: false`: keep the entry without connecting to it.
70
+ - `exposure` and `toolExposure`: control how tools reach the model (see [Control tool exposure](#control-tool-exposure)).
71
+ - `description`: what the server offers, in a sentence. It lists the server in the system prompt (see [Control tool exposure](#control-tool-exposure)), tool search ranks the server's tools by it, and codemode's `describeNamespace()` returns it. Without it, the first line of the server instructions is used once the server connects.
72
+
73
+ Keep personal servers and servers with credentials in the user-level file. Use the project file only for servers the project requires, and only in trusted projects.
74
+
75
+ ### Configuration rules
76
+
77
+ - Server names may contain only letters, digits, `_`, and `-`. Tools are named `mcp__<server>__<tool>`, with every character other than letters, digits, and `_` replaced by `_`; tools of a server whose names then collide all get a hash suffix. Server names that differ only in `-` and `_` count as the same server: a second one is rejected, and a `mcp.json` server overrides a registered one.
78
+ - `type` is optional. A `command` selects stdio and a `url` selects streamable HTTP. When present, `type` must be `stdio`, `http`, or `streamable-http`.
79
+ - `sse` is rejected. Servers that document an SSE endpoint often also provide streamable HTTP, commonly at `/mcp` instead of `/sse`.
80
+ - `command` is one executable and `args` contains its arguments. It is not a shell command string.
81
+ - `env` and `headers` values can use environment variables such as `${GITHUB_TOKEN}`. They can also run a command with `!command`, but the command must make up the whole value, for example `"Authorization": "!echo Bearer $(gh auth token)"`.
82
+ - Invalid entries are reported and skipped without preventing other servers from connecting.
83
+
84
+ `pi mcp add` and `pi mcp remove` cover common changes from a shell. See [MCP commands](cli.md#mcp-commands) for their options.
85
+
86
+ ### Inspect or change a server
87
+
88
+ `/mcp` lists configured servers with their state, tool count, exposure, and configuration source. Servers that need attention appear first. Select a server to inspect its tools and connection details, reconnect, sign in or out, change exposure, or enable and disable it.
89
+
90
+ Exposure and enabled-state changes are saved to the file that defines the server without replacing unrelated content. In a trusted project, "Enable in this project" and "Disable in this project" add a project override for a user-level server; later changes to that server are saved to the override. Disabled servers remain listed. Outside the interactive TUI, `/mcp` prints server status; `/mcp login <server>`, `/mcp logout <server>`, and `/mcp reconnect <server>` perform those actions directly.
91
+
92
+ Shell commands work without a session: `pi mcp add`, `pi mcp remove`, `pi mcp list`, `pi mcp login`, and `pi mcp logout`. Shell commands do not load extensions.
93
+
94
+ ### Diagnose connection problems
95
+
96
+ Run `pi mcp list` to connect to every enabled server and print its state, tools, and errors. It exits with status 1 when an entry is invalid or an enabled server is not connected. `/mcp` shows the full connection error and the tail of stderr from a failed stdio server.
97
+
98
+ Pi reports configuration errors, failed connections, and required sign-ins once after startup. Server logging notifications are appended to `~/.pi/agent/mcp.log` as `<time> [<server>] <level> <logger>: <message>`. The file moves to `mcp.log.1` after it grows past 5 MB.
99
+
100
+ Pi connects every enabled server in the background when a session starts. A server's tools appear once it connects; the `codemode` description does not list them, so it does not change when servers connect. The first prompt waits up to 10 seconds only for servers with `direct` tools, which must be declared in its request. Other servers are waited for when they are needed: a codemode script waits for the servers it names (`mcp__<server>`) and, when it calls `searchTools()` or reads `ALL_TOOLS`, for all of them; `tool_search` and the resource tools also wait for all of them. HTTP network errors and transient statuses (408, 429, and 5xx) are retried twice. A dropped connection is shown as disconnected and reconnects on the next call. When a server announces a changed tool list, new tools are added and withdrawn tools become unreachable.
101
+
102
+ Stopping a stdio server closes its stdin, sends SIGTERM, then sends SIGKILL to its process group. This also stops servers launched through wrappers such as `npx` or `uvx`.
103
+
104
+ ## Migrate configuration from another client
105
+
106
+ Move the converted entry under `mcpServers` in `mcp.json`, then run `pi mcp list` to validate it.
107
+
108
+ | Client | Conversion |
109
+ |---|---|
110
+ | Claude Desktop, Claude Code, or Cursor | Copy the existing `mcpServers` entry. |
111
+ | VS Code | Move an entry from the top-level `servers` object and replace `${input:...}` prompts with `${NAME}` environment variables. |
112
+ | Codex | Convert `[mcp_servers.<name>]` TOML fields such as `command`, `args`, `env`, and `url` to JSON. |
113
+ | OpenCode | Convert `"type": "local"` to a stdio entry, split its `command` array into `command` and `args`, rename `environment` to `env`, and replace `{env:NAME}` with `${NAME}`. Convert `"type": "remote"` to a URL entry. |
114
+
115
+ ## Authenticate with OAuth
116
+
117
+ Remote servers that use OAuth, such as Sentry, need no credentials in `mcp.json`:
118
+
119
+ ```json
120
+ {
121
+ "mcpServers": {
122
+ "sentry": { "url": "https://mcp.sentry.dev/mcp" }
123
+ }
124
+ }
125
+ ```
126
+
127
+ When the server rejects an unauthenticated connection, `/mcp` shows that it needs sign-in. Select "Sign in", run `/mcp login sentry`, or run `pi mcp login sentry`. Pi opens the authorization page and waits for approval. If the browser runs on another machine, such as over SSH, paste its redirected URL into the sign-in screen. A running session uses the new credentials on its next turn.
128
+
129
+ Pi registers itself with the authorization server, stores tokens in `~/.pi/agent/mcp-auth.json`, and refreshes access tokens when they expire or the server rejects them. If a server later requests additional scope, Pi asks for sign-in again. Signing out deletes the stored credentials.
130
+
131
+ Credentials belong to a server name and URL. Servers with the same URL under different names, such as one per account, sign in separately; servers with the same name and URL in different `mcp.json` files share one sign-in.
132
+
133
+ OAuth applies to HTTP servers without an `Authorization` header. For a server that does not support dynamic client registration, configure a registered client:
134
+
135
+ ```json
136
+ {
137
+ "mcpServers": {
138
+ "example": {
139
+ "url": "https://mcp.example.com/mcp",
140
+ "oauth": { "clientId": "my-client", "clientSecret": "${EXAMPLE_SECRET}", "callbackPort": 8765 }
141
+ }
142
+ }
143
+ }
144
+ ```
145
+
146
+ The redirect URI must match the registered URI. `callbackPort` uses `http://127.0.0.1:<port>/callback`. To use another URI, set `callbackUrl`; it must use HTTP on `localhost`, `127.0.0.1`, or `[::1]`. Pi sends it exactly as written. When `callbackUrl` omits a port, Pi uses `callbackPort` or a free port and adds it to the URI, as allowed for loopback redirects by RFC 8252. `clientSecret` is optional and can use an environment variable or command.
147
+
148
+ Set `scope` to a space-separated list for servers that do not advertise their required scopes. Otherwise, Pi requests the advertised scopes. Later scope requests are added to the configured value.
149
+
150
+ Pi registers as `pi`. Some servers only accept registrations from known clients. Set `clientName` to send another name:
151
+
152
+ ```json
153
+ {
154
+ "mcpServers": {
155
+ "figma": { "url": "https://mcp.figma.com/mcp", "oauth": { "clientName": "Claude Code" } }
156
+ }
157
+ }
158
+ ```
159
+
160
+ The name is only sent when Pi registers a client. To register again under a new name, sign out first.
161
+
162
+ Some authorization servers allow clients by their Client ID Metadata Document URL instead of registering them. Set `clientRegistration` to `cimd` to identify as Pi's document on pi.dev instead of registering:
163
+
164
+ ```json
165
+ {
166
+ "mcpServers": {
167
+ "example": { "url": "https://mcp.example.com/mcp", "oauth": { "clientRegistration": "cimd" } }
168
+ }
169
+ }
170
+ ```
171
+
172
+ The client ID is `https://pi.dev/oauth/client.json` with the redirect URI `http://127.0.0.1:<port>/callback`. If the authorization server does not send the `iss` parameter in authorization responses (RFC 9207), Pi uses a document and redirect path specific to the MCP server instead: `https://pi.dev/oauth/<id>/client.json` with `http://127.0.0.1:<port>/callback/<id>`. The authorization server must advertise Client ID Metadata Document support and public clients, or sign-in fails. `cimd` cannot be combined with `clientId` or `clientName`, and a `callbackUrl` must use `localhost` or `127.0.0.1` with the path `/callback`.
173
+
174
+ Pi finds the authorization server through the server's protected resource metadata (RFC 9728) and checks that the authorization server's metadata names the expected issuer (RFC 8414). Some servers advertise the wrong authorization server or none, so sign-in opens a page that does not exist. Set `authServerMetadataUrl` to the metadata document of the right authorization server:
175
+
176
+ ```json
177
+ {
178
+ "mcpServers": {
179
+ "example": {
180
+ "url": "https://mcp.example.com/mcp",
181
+ "oauth": { "authServerMetadataUrl": "https://example.okta.com/.well-known/openid-configuration" }
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ Pi uses that document instead of discovery and trusts it as configured, so only point it at a document you trust. The URL must use HTTPS, except on `localhost`, `127.0.0.1`, or `[::1]`.
188
+
189
+ ## Control tool exposure
190
+
191
+ Each server tool is registered as `mcp__<server>__<tool>`. The server's `exposure` determines how the model reaches it:
192
+
193
+ | Exposure | Behavior | Typical use |
194
+ |---|---|---|
195
+ | `codemode` (default) | Callable from [`codemode`](cli.md#tools) scripts, but neither declared to the model nor listed in the codemode description. Scripts find tools with `searchTools()`, `describeTool()`, or `ALL_TOOLS`. | General MCP servers, especially when scripts should combine or filter calls. |
196
+ | `deferred` | Not declared until [`tool_search`](cli.md#tools) loads a match for the next model call. | Large servers whose tools should be called directly after discovery. |
197
+ | `direct` | Declared to the model like a built-in tool and also callable from codemode. | Small, frequently used tool sets. |
198
+ | `hidden` | Registered but unreachable. | Servers or tools that should remain unavailable. |
199
+
200
+ `codemode-deferred` is accepted as an alias for `codemode`.
201
+
202
+ Servers with `codemode` or `deferred` tools are listed in the `mcp_servers` section of the system prompt, with how their tools are reached and one line from the configured `description` or, once connected, from the server instructions. Pi updates the section when a prompt starts, after waiting for servers with `direct` tools. When it changed, for example because a server connected and its summary became available, Pi appends the new section to the conversation instead of changing tool declarations, so earlier messages stay cached. `describeNamespace()` and the `namespace` option of `searchTools()` accept `mcp__dev-radius`, `mcp__dev_radius`, `dev-radius`, or `dev_radius`.
203
+
204
+ Pi activates `codemode` when a server with `codemode` exposure connects. It activates `tool_search` for a server with `deferred` exposure. To make the model see a tool without searching, give it `direct` exposure with `toolExposure`.
205
+
206
+ `toolExposure` overrides the server exposure for individual tools. Keys are exact server tool names or patterns where `*` matches any characters. Exact names win over patterns; among patterns, the first match wins. A server with `hidden` exposure can expose only selected tools:
207
+
208
+ ```json
209
+ {
210
+ "mcpServers": {
211
+ "github": {
212
+ "url": "https://api.githubcopilot.com/mcp/",
213
+ "exposure": "deferred",
214
+ "toolExposure": {
215
+ "search_code": "direct",
216
+ "get_*": "codemode",
217
+ "delete_*": "hidden"
218
+ }
219
+ }
220
+ }
221
+ }
222
+ ```
223
+
224
+ `pi mcp list` marks tools whose exposure differs from their server. The Tools view in `/mcp` also shows the effective exposure.
225
+
226
+ Tools with `codemode` or `deferred` exposure can be reached through either indirect mechanism: codemode scripts can call them, and `tool_search` can load them. Codemode calls do not depend on the active tool set, so they remain available after `/tree`, resume, and fork. Tools loaded by `tool_search` are recorded in the transcript and remain declared on that branch.
227
+
228
+ To keep `codemode` active without MCP servers, add `"defaultTools": ["+codemode"]` to [settings](settings.md#tools). To prevent automatic codemode activation, set `"autoEnableCodemode": false` beside `mcpServers`. A project value overrides the user-level value. Pi warns once when neither `codemode` nor `tool_search` is active and non-direct tools cannot be called.
229
+
230
+ Text results over 20 KB reach the model with their middle removed around a `…N chars truncated…` marker. The full text is saved to a temporary file named in the result. Codemode scripts receive the complete result and can reduce it before returning output to the model.
231
+
232
+ Codemode scripts receive the complete MCP `CallToolResult`, including `content`, `structuredContent`, and `isError`. A result with `isError` resolves inside scripts but is reported as an error for direct calls. `image(result.content[0])` forwards an image block. Server instructions are not part of any tool description; scripts read them with `describeNamespace("mcp__<server>")`, which also returns the server's tool names.
233
+
234
+ ## Use resources
235
+
236
+ When a connected server offers [resources](https://modelcontextprotocol.io/specification/2025-11-25/server/resources), Pi adds the resource tools used by Codex and OpenCode:
237
+
238
+ - `list_mcp_resources` lists resources as JSON: `{ server?, resources: [{ server, uri, name, ... }], nextCursor? }`. With `server`, it lists one page; `cursor` continues with the next page. Without `server`, it lists every resource from every server.
239
+ - `list_mcp_resource_templates` lists URI templates for resources the servers do not list directly.
240
+ - `read_mcp_resource` reads a resource by `server` and `uri`. Text reaches the model as text and images as images. Other binary resources are saved to temporary files, and the model receives the path. Scripts receive `{ server, uri, contents }`.
241
+
242
+ These tools reach every enabled, non-hidden server with resources. Their exposure is the widest exposure among those servers: `direct`, then `codemode` or `deferred`. Resource links in tool results identify `read_mcp_resource` and the server.
243
+
244
+ Resources for MCP Apps, identified by `ui://` URIs or `text/html;profile=mcp-app`, are omitted because Pi does not render them. Resource icons are also omitted.
245
+
246
+ Reading and listing resources is retried once after a transient HTTP error (408, 429, or 5xx). Tool calls are not retried because the server may already have performed them.
247
+
248
+ ## Permissions
249
+
250
+ Every MCP call passes through Pi's tool pipeline. Extension `tool_call` and `tool_result` handlers, including permission gates, therefore apply to MCP tools. Calls made from codemode scripts carry the codemode call ID as `parentToolCallId`.
251
+
252
+ `pi.getAllTools()` reports the annotations declared by each server: `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`. Permission extensions can use these hints to decide which calls require confirmation (see [Tool exposure](extensions.md#tool-exposure)). Resource tools are marked read-only.
253
+
254
+ ## Extensions and SDK
255
+
256
+ ### Add servers from extensions
257
+
258
+ Extensions can add servers for the current session with `pi.registerMcpServer(name, config)`, using the same shape as an `mcpServers` entry (see [MCP servers in extensions](extensions.md#mcp-servers)). Registered servers connect like configured servers and appear in `/mcp` with the extension as their source.
259
+
260
+ Changes to enabled state or exposure apply only to the current session. A file-configured server with the same name takes precedence, and `/mcp` lists the overridden registration. `pi mcp` shell commands do not load extensions and only see file-configured servers.
261
+
262
+ ### Replace the built-in MCP support
263
+
264
+ 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).
265
+
266
+ 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.
267
+
268
+ ### Use MCP from the SDK
269
+
270
+ SDK sessions do not load built-in extensions. Add the MCP extension, the codemode extension for `codemode` servers, and the tool-search extension for `deferred` servers to the resource loader. See [Codemode and MCP](sdk.md#codemode-mcp).
package/docs/models.md CHANGED
@@ -18,7 +18,7 @@ Browse the [model catalog](https://pi.dev/models) for current providers, model I
18
18
 
19
19
  Run `/login` and select a provider. Pi stores credentials in [`auth.json`](configuration.md#agent-directory). Run `/logout` to remove stored credentials for a provider.
20
20
 
21
- You can instead provide an API key through the provider's environment variable. This is useful in CI and other environments where Pi should not write credentials. [Provider Authentication](providers.md) lists the variables and cloud-provider setup.
21
+ You can instead provide an API key through the provider's environment variable. This is useful in CI and other environments where Pi should not write credentials. [Providers](providers.md) lists the variables and provider-specific setup.
22
22
 
23
23
  When several credential sources are configured, Pi uses a runtime `--api-key` first, then a stored `auth.json` credential, an `apiKey` from `models.json`, and finally the provider's environment variables or ambient cloud credentials. Provider extensions can define their own authentication behavior.
24
24
 
@@ -98,8 +98,90 @@ Use `promptCache` to declare the provider's best-effort cache lifetime in second
98
98
 
99
99
  Choose the conservative end of any published range. A model without a lifetime for the active tier is not eligible for cache warming. A `modelOverrides` entry can set `inputLimits` or `promptCache` for a built-in or extension model, including a model accessed through a validated proxy. See [`cacheWarming`](settings.md#model-and-thinking).
100
100
 
101
+ ### Configure sampling by thinking level
102
+
103
+ OpenAI-compatible APIs support free-form `samplingParams` model defaults and `samplingParamsByThinkingLevel` overrides. The latter uses Pi thinking-level keys (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`), not provider values from `thinkingLevelMap`:
104
+
105
+ ```json
106
+ {
107
+ "id": "qwen-thinking-model",
108
+ "reasoning": true,
109
+ "samplingParams": {
110
+ "temperature": 1.0,
111
+ "top_p": 0.95
112
+ },
113
+ "samplingParamsByThinkingLevel": {
114
+ "off": {
115
+ "temperature": 0.7,
116
+ "top_p": 0.8
117
+ },
118
+ "high": {
119
+ "top_k": 20
120
+ }
121
+ }
122
+ }
123
+ ```
124
+
125
+ Pi first clamps unsupported thinking levels, then merges model `samplingParams`, the effective level's override, and request-level `samplingParams` in that order. Later values win per key. Missing levels inherit the model defaults. `modelOverrides` merges per-level entries per key with the base model. These fields apply only to `openai-completions`, `openai-responses`, and `azure-openai-responses`; other APIs ignore them.
126
+
101
127
  Compatibility settings should describe verified differences in the endpoint's request or response behavior. Do not enable them based only on an endpoint advertising OpenAI or Anthropic compatibility.
102
128
 
129
+ ## Use classifier models
130
+
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
+
133
+ | Provider | Model IDs | Authentication |
134
+ |---|---|---|
135
+ | `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
136
+ | `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or `/login` |
137
+ | `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
138
+ | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
139
+ | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
140
+
141
+ Chat models on a [llama.cpp router](llama-cpp.md#classification) are also listed as classifier models.
142
+
143
+ 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
+
145
+ ```js
146
+ const jev = await models.getModelOfType("classifier", "typesafe", "jev-latest");
147
+ const result = await models.classify(jev, {
148
+ state: { message: "The change works, thanks." },
149
+ questions: {
150
+ approved: {
151
+ type: "bool",
152
+ instructions: "Does the user approve of the result?",
153
+ criteria: { true: "Approval", false: "No approval" },
154
+ },
155
+ },
156
+ });
157
+ return result.answers;
158
+ ```
159
+
160
+ [Codemode](codemode.md#classify) describes the question and answer types.
161
+
162
+ When the service reports token counts, as all System One services do, `result.usage` carries them with their cost. Pi adds the usage of a script's classifier calls to the `codemode` tool result, so it counts toward the session cost in the footer and `/session`. The cost uses the model's catalog price; models without one, such as TypeSafe's direct `jev-latest`, report tokens at no cost.
163
+
164
+ Extensions call classifiers through `ctx.modelRegistry.classify()`, without codemode. [Virtual models](virtual-models.md#route-requests) can use them to route requests; see the `jev-router.ts` example.
165
+
166
+ ## Use image models
167
+
168
+ Image models generate images from a prompt and optional input images. Pi lists OpenRouter's image models, such as `google/gemini-2.5-flash-image` and `black-forest-labs/flux.2-pro`, under the `openrouter` provider; they use the same `OPENROUTER_API_KEY` or `/login` credential as its chat models.
169
+
170
+ Like classifier models, image models do not appear in `/model`; the model reaches them through the [`codemode`](cli.md#enable-codemode) tool. Scripts list them with `models.getAvailableOfType("image")` and call `models.generateImages(model, { input })`. The result's `output` holds base64 image blocks, which `image()` attaches to the `codemode` result so the model sees them:
171
+
172
+ ```js
173
+ const painter = await models.getModelOfType("image", "openrouter", "google/gemini-2.5-flash-image");
174
+ const result = await models.generateImages(painter, {
175
+ input: [{ type: "text", text: "A red fox in the snow, watercolor" }],
176
+ });
177
+ if (result.stopReason !== "stop") return result.errorMessage;
178
+ for (const block of result.output) if (block.type === "image") image(block);
179
+ ```
180
+
181
+ `input` can also contain `{ type: "image", data, mimeType }` blocks to edit or use as references. Pi adds the usage of a script's image calls to the `codemode` tool result, like classifier calls. Generated images are not saved to disk. [Codemode](codemode.md#generate-images) describes the full API.
182
+
183
+ Extensions generate images through `ctx.modelRegistry.generateImages()`, without codemode.
184
+
103
185
  ## Add a custom provider
104
186
 
105
187
  Use an extension when the provider needs custom streaming, model discovery, or authentication behavior. See [Custom Providers](custom-provider.md) for the extension workflow.
package/docs/packages.md CHANGED
@@ -118,7 +118,7 @@ For each resource type:
118
118
 
119
119
  Filters narrow the package manifest. They do not expose resources that the package itself did not declare.
120
120
 
121
- Run `pi config` to enable or disable discovered resources. It starts with personal configuration; press Tab to switch scope, or run `pi config --local` to start with project overrides.
121
+ Run `pi config` to enable or disable discovered resources and pi's built-in extensions. It starts with personal configuration; press Tab to switch scope, or run `pi config --local` to start with project overrides.
122
122
 
123
123
  ## Understand scope and identity
124
124
 
package/docs/providers.md CHANGED
@@ -1,4 +1,4 @@
1
- # Provider Authentication
1
+ # Providers
2
2
 
3
3
  Most hosted providers support one or both of these authentication methods:
4
4
 
@@ -17,8 +17,6 @@ Run `/logout` and select a provider to remove its stored credential. This does n
17
17
 
18
18
  `auth.json` can contain API keys and OAuth tokens. Keep it private and do not commit it.
19
19
 
20
- 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.
21
-
22
20
  ## Use an API key from the environment
23
21
 
24
22
  Environment variables are useful in CI and anywhere Pi should not store the key. Set the variable before starting Pi:
@@ -28,7 +26,7 @@ export ANTHROPIC_API_KEY=sk-ant-...
28
26
  pi
29
27
  ```
30
28
 
31
- This table covers providers with a single primary API-key variable. Providers that need additional configuration or support ambient credentials are covered under [Cloud providers](#cloud-providers).
29
+ This table covers providers with a single primary API-key variable. Providers that need additional configuration or support ambient credentials are covered under [Provider Specific Config](#provider-specific-config).
32
30
 
33
31
  | Provider | Environment variable |
34
32
  |---|---|
@@ -49,6 +47,7 @@ This table covers providers with a single primary API-key variable. Providers th
49
47
  | ZAI Coding Plan (China) | `ZAI_CODING_CN_API_KEY` |
50
48
  | OpenCode Zen and Go | `OPENCODE_API_KEY` |
51
49
  | Radius | `RADIUS_API_KEY` |
50
+ | TypeSafe ([classifier models](models.md#use-classifier-models)) | `TYPESAFE_API_KEY` |
52
51
  | Hugging Face | `HF_TOKEN` |
53
52
  | Fireworks | `FIREWORKS_API_KEY` |
54
53
  | Together AI | `TOGETHER_API_KEY` |
@@ -67,6 +66,8 @@ This table covers providers with a single primary API-key variable. Providers th
67
66
 
68
67
  Anthropic also recognizes `ANTHROPIC_OAUTH_TOKEN` as an API credential and `ANTHROPIC_AUTH_TOKEN` as bearer authentication.
69
68
 
69
+ With no key or token set, Anthropic uses workload identity federation when `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID` and `ANTHROPIC_IDENTITY_TOKEN_FILE` are set: the Anthropic SDK exchanges the identity token for a short-lived access token and refreshes it itself (re-reading the identity token file, so keep that file fresh for long sessions). `ANTHROPIC_SERVICE_ACCOUNT_ID` and `ANTHROPIC_WORKSPACE_ID` are passed through when set.
70
+
70
71
  ## Load an API key from a command
71
72
 
72
73
  To use a secret manager without writing the resolved key to disk, set a provider's `key` in `auth.json` to a command prefixed with `!`:
@@ -82,9 +83,9 @@ To use a secret manager without writing the resolved key to disk, set a provider
82
83
 
83
84
  Pi runs the command when the key is first needed and caches its standard output for the process lifetime. Empty output, a timeout, or a nonzero exit leaves the key unresolved until Pi restarts.
84
85
 
85
- ## Cloud Providers
86
+ ## Provider Specific Config
86
87
 
87
- The providers below need additional settings or can use credentials supplied by their cloud platform.
88
+ The providers below have additional setup, need additional settings, or can use credentials supplied by their platform.
88
89
 
89
90
  A stored API-key credential can include an `env` object. Its values take priority over the process environment for that provider:
90
91
 
@@ -100,6 +101,18 @@ A stored API-key credential can include an `env` object. Its values take priorit
100
101
  }
101
102
  ```
102
103
 
104
+ ### Radius
105
+
106
+ Radius is a service crafted for Pi by the builders of Pi, Earendil Works. It provides a customizable AI gateway with organization-level controls and analytics built in, and artifacts for sharing what you create with Pi.
107
+
108
+ To get started, run `/login radius` in Pi. This adds Radius as a provider, and its models appear in `/model` like any other provider's.
109
+
110
+ Radius also has an MCP server, so Pi can manage Radius for you.
111
+
112
+ Radius is currently in early alpha and evolving quickly. See [radius.earendil.com](https://radius.earendil.com) for more.
113
+
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
+
103
116
  ### Azure OpenAI
104
117
 
105
118
  Set an API key plus either a base URL or resource name:
@@ -12,7 +12,7 @@ On macOS or Linux, you can use the installer:
12
12
  curl -fsSL https://pi.dev/install.sh | sh
13
13
  ```
14
14
 
15
- Alternatively, install Pi from npm. This requires Node.js 22.19 or newer:
15
+ The installer pins all dependencies and updates Pi with `pi update`. Alternatively, install Pi from npm, which does not pin transitive dependencies. This requires Node.js 22.19 or newer:
16
16
 
17
17
  ```bash
18
18
  npm install -g --ignore-scripts @earendil-works/pi-coding-agent
@@ -20,6 +20,14 @@ npm install -g --ignore-scripts @earendil-works/pi-coding-agent
20
20
 
21
21
  Pi does not require dependency lifecycle scripts for a normal npm installation.
22
22
 
23
+ With Nix on macOS or Linux, install the latest release from Pi's flake. Nix builds Pi from source:
24
+
25
+ ```bash
26
+ nix profile add github:earendil-works/pi/stable
27
+ ```
28
+
29
+ Older Nix versions use `nix profile install` instead. Update with `nix profile upgrade pi`; `pi update` cannot update a Nix installation. To pin a release, use a tag such as `github:earendil-works/pi/v1.0.0`.
30
+
23
31
  Verify the installation:
24
32
 
25
33
  ```bash
@@ -119,4 +127,10 @@ If you used the installer, run it again and choose **Uninstall Pi**:
119
127
  curl -fsSL https://pi.dev/install.sh | sh
120
128
  ```
121
129
 
122
- Neither method removes configuration, credentials, sessions, or installed Pi packages from `~/.pi/agent/`.
130
+ If you installed Pi with Nix, run:
131
+
132
+ ```bash
133
+ nix profile remove pi
134
+ ```
135
+
136
+ None of these methods removes configuration, credentials, sessions, or installed Pi packages from `~/.pi/agent/`.
@@ -34,9 +34,11 @@ If the agent is streaming and no `streamingBehavior` is specified, the command r
34
34
 
35
35
  Response:
36
36
  ```json
37
- {"id": "req-1", "type": "response", "command": "prompt", "success": true}
37
+ {"id": "req-1", "type": "response", "command": "prompt", "success": true, "data": {"disposition": "started"}}
38
38
  ```
39
39
 
40
+ `data.disposition` is `"handled"` if an extension command or input handler consumed the prompt, `"queued"` if Pi queued it during a run, or `"started"` if Pi accepted it to start a run. This describes the submitted prompt, not independent work started by an extension or a guarantee of completion.
41
+
40
42
  `success: true` means the prompt was accepted, queued, or handled immediately. `success: false` means the prompt was rejected before acceptance. Failures after acceptance are reported through the normal event and message stream, not as a second `response` for the same request id.
41
43
 
42
44
  The `images` field is optional. Each image uses `ImageContent` format: `{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}`.
@@ -58,9 +60,11 @@ The `images` field is optional. Each image uses `ImageContent` format (same as `
58
60
 
59
61
  Response:
60
62
  ```json
61
- {"type": "response", "command": "steer", "success": true}
63
+ {"type": "response", "command": "steer", "success": true, "data": {"disposition": "queued"}}
62
64
  ```
63
65
 
66
+ `data.disposition` is `"handled"` if an input handler consumed this steer, or `"queued"` if Pi queued it (including after a handler transformed it). It does not guarantee this message remains queued.
67
+
64
68
  See [set_steering_mode](#set_steering_mode) for controlling how steering messages are processed.
65
69
 
66
70
  ### follow_up
@@ -80,9 +84,11 @@ The `images` field is optional. Each image uses `ImageContent` format (same as `
80
84
 
81
85
  Response:
82
86
  ```json
83
- {"type": "response", "command": "follow_up", "success": true}
87
+ {"type": "response", "command": "follow_up", "success": true, "data": {"disposition": "queued"}}
84
88
  ```
85
89
 
90
+ `data.disposition` has the same `"handled"` or `"queued"` meaning as for `steer`, applied to this follow-up.
91
+
86
92
  See [set_follow_up_mode](#set_follow_up_mode) for controlling how follow-up messages are processed.
87
93
 
88
94
  ### abort
package/docs/rpc.md CHANGED
@@ -61,9 +61,11 @@ A successful `prompt` response means the prompt was accepted, queued, or handled
61
61
 
62
62
  ```json
63
63
  {"id":"req-2","type":"prompt","message":"Review this repository"}
64
- {"id":"req-2","type":"response","command":"prompt","success":true}
64
+ {"id":"req-2","type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}
65
65
  ```
66
66
 
67
+ `data.disposition` reports what happened to the prompt. If it is `"handled"`, no run started for this prompt, so don't wait for `agent_settled`. See [RPC Commands](rpc-commands.md#prompt) for all values.
68
+
67
69
  Continue consuming [events](json.md) after that response. `agent_end` marks the end of one low-level agent run, but retries, overflow recovery, compaction, steering, or follow-up work can still follow. Wait for `agent_settled` when the client needs to know Pi will not continue automatically.
68
70
 
69
71
  Subscribe before sending a prompt to avoid missing a fast completion. `RpcClient.promptAndWait()` does this internally. If using separate `RpcClient` calls, install the event listener before `prompt()` and call `waitForIdle()` only while a run is active.
package/docs/sdk.md CHANGED
@@ -65,7 +65,7 @@ After a runtime replacement, subscriptions belong to the old `AgentSession` and
65
65
 
66
66
  A prompt sent while the session is already streaming must specify whether it should steer the current run or follow it. Calling `prompt()` without that choice rejects rather than guessing.
67
67
 
68
- A steering message enters after the current assistant turn and its tool calls. A follow-up enters after the current run finishes its pending work. `steer()` and `followUp()` expose those behaviors directly.
68
+ A steering message enters after the current assistant turn and its tool calls. A follow-up enters after the current run finishes its pending work. `steer()` and `followUp()` expose those behaviors directly and return `"queued"` if the input was queued (including after an extension transformed it), or `"handled"` if an extension consumed it.
69
69
 
70
70
  `abort()` stops the active operation and waits for the session to become idle. `waitForIdle()` waits without aborting it.
71
71
 
@@ -109,7 +109,11 @@ Use `DefaultResourceLoader` when you want standard discovery with selected overr
109
109
 
110
110
  <a id="inlineextension"></a>
111
111
 
112
- Inline extension factories can be supplied through `DefaultResourceLoader`. Give one an `InlineExtension` name only when it needs a stable name in diagnostics and startup output.
112
+ Inline extension factories can be supplied through `DefaultResourceLoader`. Give one an `InlineExtension` name only when it needs a stable name in diagnostics and startup output. A named inline extension with `replaceable: true` is left out when another extension registers a tool, command, or flag with a name it registers during loading, instead of both loading with a conflict. The CLI's built-in codemode, tool search, and MCP extensions are replaceable. A named entry with `builtin: true` is not an inline extension: it supplies the code of the `builtin:<name>` extension, which loads like a configured extension file. It loads by default, is listed in `pi config`, and is disabled by `-builtin:<name>` in the `extensions` setting or by `noExtensions`; `additionalExtensionPaths: ["builtin:<name>"]` loads it explicitly. It loads after project trust is resolved, so it cannot handle `project_trust`. The CLI's built-in extensions use it.
113
+
114
+ <a id="codemode-mcp"></a>
115
+
116
+ The CLI loads `codemode`, `tool_search`, and MCP as built-in extensions. SDK sessions do not; add `createCodemodeExtension()`, `createToolSearchExtension()`, and `createMcpExtension()` to the `extensionFactories` of `DefaultResourceLoader`. `codemode` and `tool_search` are registered inactive: enable them through the `defaultTools` setting (`["+codemode", "+tool_search"]` keeps the other default tools), or let the MCP extension activate them: `codemode` for servers with `codemode` exposure, `tool_search` for servers with `deferred` exposure. The MCP extension connects its servers on `session_start`, so call `session.bindExtensions()`. See [Codemode and MCP](../examples/sdk/14-codemode-mcp.ts).
113
117
 
114
118
  See the focused examples for [models](../examples/sdk/02-custom-model.ts), [tools](../examples/sdk/05-tools.ts), [extensions](../examples/sdk/06-extensions.ts), and [full control](../examples/sdk/12-full-control.ts).
115
119
 
@@ -130,12 +134,13 @@ See the focused examples for [models](../examples/sdk/02-custom-model.ts), [tool
130
134
  | [Sessions](../examples/sdk/11-sessions.ts) | Control session persistence and restoration |
131
135
  | [Full control](../examples/sdk/12-full-control.ts) | Replace default discovery and state services |
132
136
  | [Session runtime](../examples/sdk/13-session-runtime.ts) | Replace the active session safely |
137
+ | [Codemode and MCP](../examples/sdk/14-codemode-mcp.ts) | Add the `codemode`, `tool_search`, and MCP extensions |
133
138
 
134
139
  <a id="exports"></a>
135
140
 
136
141
  ## Resources
137
142
 
138
- - [Choose a Model](models.md) covers model selection and compatible endpoints; [Provider Authentication](providers.md) covers credentials and cloud-provider setup.
143
+ - [Choose a Model](models.md) covers model selection and compatible endpoints; [Providers](providers.md) covers credentials and provider-specific setup.
139
144
  - [Configuration](configuration.md) explains normal discovery and settings; [Settings](settings.md) lists every setting.
140
145
  - [Sessions and Context](sessions.md) explains session behavior; [Session Format](session-format.md) defines persisted entries; [Message Types](message-types.md) defines shared transcript values.
141
146
  - [Extensions](extensions.md), [Skills](skills.md), and [Prompt Templates](prompt-templates.md) document resources supplied through a `ResourceLoader`.
package/docs/security.md CHANGED
@@ -37,6 +37,7 @@ Project trust does not limit what tool calls can access or affect. After Pi star
37
37
  Pi requires a project-trust decision when it finds any of these resources from the current working directory:
38
38
 
39
39
  - `.pi/settings.json`
40
+ - `.pi/mcp.json`
40
41
  - `.pi/extensions`, `.pi/skills`, `.pi/prompts`, or `.pi/themes`
41
42
  - `.pi/SYSTEM.md` or `.pi/APPEND_SYSTEM.md`
42
43
  - project `.agents/skills` in the current directory or an ancestor directory
@@ -46,6 +47,7 @@ A bare `.pi` directory does not require project trust.
46
47
  Granting project trust allows Pi to load:
47
48
 
48
49
  - project settings
50
+ - project MCP servers from `.pi/mcp.json`
49
51
  - extensions, skills, prompt templates, themes, and system-prompt files under `.pi`
50
52
  - missing packages configured through project settings
51
53
  - project-local and project-package extensions
@@ -92,9 +92,11 @@ Sessions created before system messages existed have no leading system message;
92
92
  {"type":"message","id":"c3d4e5f6","parentId":"b2c3d4e5","timestamp":"2024-12-03T14:00:03.000Z","message":{"role":"toolResult","toolCallId":"call_123","toolName":"bash","content":[{"type":"text","text":"output"}],"isError":false,"timestamp":1733234403000}}
93
93
  ```
94
94
 
95
+ Assistant messages name the model that produced them. Newer messages also record `thinkingLevel`, the Pi thinking level requested for that response.
96
+
95
97
  ### ModelChangeEntry
96
98
 
97
- Emitted when the user switches models mid-session.
99
+ Emitted when the user switches models mid-session. The latest entry is the selected model, which may be a [virtual model](virtual-models.md); assistant messages then name the physical model that answered.
98
100
 
99
101
  ```json
100
102
  {"type":"model_change","id":"d4e5f6g7","parentId":"c3d4e5f6","timestamp":"2024-12-03T14:05:00.000Z","provider":"openai","modelId":"gpt-4o"}
@@ -169,6 +171,8 @@ Extension state persistence. Does NOT participate in LLM context.
169
171
 
170
172
  Use `customType` to identify your extension's entries on reload. Interactive mode can render custom entries via `pi.registerEntryRenderer(customType, renderer)`, but they still do not participate in LLM context.
171
173
 
174
+ Pi stores [virtual model](virtual-models.md) router state as custom entries with `customType` `pi.virtual-model-state` and `data` `{ provider, modelId, state }`.
175
+
172
176
  ### CustomMessageEntry
173
177
 
174
178
  Extension-injected messages that DO participate in LLM context.