@earendil-works/pi-coding-agent 0.80.5 → 0.80.7

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 (109) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +4 -2
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +2 -2
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +1 -1
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +4 -4
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  11. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  12. package/dist/core/compaction/branch-summarization.js.map +1 -1
  13. package/dist/core/extensions/runner.d.ts +1 -0
  14. package/dist/core/extensions/runner.d.ts.map +1 -1
  15. package/dist/core/extensions/runner.js +5 -0
  16. package/dist/core/extensions/runner.js.map +1 -1
  17. package/dist/core/extensions/types.d.ts +2 -7
  18. package/dist/core/extensions/types.d.ts.map +1 -1
  19. package/dist/core/extensions/types.js.map +1 -1
  20. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  21. package/dist/core/extensions/wrapper.js +22 -3
  22. package/dist/core/extensions/wrapper.js.map +1 -1
  23. package/dist/core/keybindings.d.ts +8 -3
  24. package/dist/core/keybindings.d.ts.map +1 -1
  25. package/dist/core/keybindings.js +7 -3
  26. package/dist/core/keybindings.js.map +1 -1
  27. package/dist/core/model-registry.d.ts +1 -6
  28. package/dist/core/model-registry.d.ts.map +1 -1
  29. package/dist/core/model-registry.js +36 -8
  30. package/dist/core/model-registry.js.map +1 -1
  31. package/dist/core/model-resolver.d.ts.map +1 -1
  32. package/dist/core/model-resolver.js +1 -0
  33. package/dist/core/model-resolver.js.map +1 -1
  34. package/dist/core/package-manager.d.ts.map +1 -1
  35. package/dist/core/package-manager.js +7 -2
  36. package/dist/core/package-manager.js.map +1 -1
  37. package/dist/core/provider-display-names.d.ts.map +1 -1
  38. package/dist/core/provider-display-names.js +1 -0
  39. package/dist/core/provider-display-names.js.map +1 -1
  40. package/dist/core/radius.d.ts +21 -0
  41. package/dist/core/radius.d.ts.map +1 -0
  42. package/dist/core/radius.js +30 -0
  43. package/dist/core/radius.js.map +1 -0
  44. package/dist/core/settings-manager.d.ts +4 -3
  45. package/dist/core/settings-manager.d.ts.map +1 -1
  46. package/dist/core/settings-manager.js +2 -1
  47. package/dist/core/settings-manager.js.map +1 -1
  48. package/dist/core/system-prompt.d.ts.map +1 -1
  49. package/dist/core/system-prompt.js +1 -11
  50. package/dist/core/system-prompt.js.map +1 -1
  51. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  52. package/dist/modes/interactive/components/custom-editor.js +1 -1
  53. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  54. package/dist/modes/interactive/components/login-dialog.d.ts +4 -0
  55. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  56. package/dist/modes/interactive/components/login-dialog.js +9 -2
  57. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  58. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/settings-selector.js +2 -1
  60. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  61. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  62. package/dist/modes/interactive/components/thinking-selector.js +2 -1
  63. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  64. package/dist/modes/interactive/components/tree-selector.d.ts +5 -0
  65. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  66. package/dist/modes/interactive/components/tree-selector.js +49 -12
  67. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  68. package/dist/modes/interactive/interactive-mode.d.ts +1 -2
  69. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  70. package/dist/modes/interactive/interactive-mode.js +45 -41
  71. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  72. package/dist/modes/interactive/theme/dark.json +1 -0
  73. package/dist/modes/interactive/theme/light.json +1 -0
  74. package/dist/modes/interactive/theme/theme-schema.json +6 -2
  75. package/dist/modes/interactive/theme/theme.d.ts +3 -2
  76. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  77. package/dist/modes/interactive/theme/theme.js +10 -3
  78. package/dist/modes/interactive/theme/theme.js.map +1 -1
  79. package/dist/utils/clipboard-native.d.ts +1 -0
  80. package/dist/utils/clipboard-native.d.ts.map +1 -1
  81. package/dist/utils/clipboard-native.js.map +1 -1
  82. package/dist/utils/clipboard.d.ts +2 -0
  83. package/dist/utils/clipboard.d.ts.map +1 -1
  84. package/dist/utils/clipboard.js +13 -0
  85. package/dist/utils/clipboard.js.map +1 -1
  86. package/docs/custom-provider.md +6 -3
  87. package/docs/extensions.md +139 -1
  88. package/docs/keybindings.md +1 -0
  89. package/docs/models.md +47 -4
  90. package/docs/providers.md +3 -0
  91. package/docs/quickstart.md +1 -1
  92. package/docs/rpc.md +2 -2
  93. package/docs/sdk.md +1 -1
  94. package/docs/settings.md +2 -2
  95. package/docs/themes.md +5 -3
  96. package/docs/tui.md +1 -1
  97. package/docs/usage.md +2 -1
  98. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  99. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  100. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  101. package/examples/extensions/gondolin/package-lock.json +2 -2
  102. package/examples/extensions/gondolin/package.json +1 -1
  103. package/examples/extensions/preset.ts +2 -2
  104. package/examples/extensions/sandbox/package-lock.json +2 -2
  105. package/examples/extensions/sandbox/package.json +1 -1
  106. package/examples/extensions/with-deps/package-lock.json +2 -2
  107. package/examples/extensions/with-deps/package.json +1 -1
  108. package/npm-shrinkwrap.json +12 -12
  109. package/package.json +4 -4
@@ -47,6 +47,7 @@ See [examples/extensions/](../examples/extensions/) for working implementations.
47
47
  - [ExtensionAPI Methods](#extensionapi-methods)
48
48
  - [State Management](#state-management)
49
49
  - [Custom Tools](#custom-tools)
50
+ - [Dynamic Tool Loading](#dynamic-tool-loading)
50
51
  - [Custom UI](#custom-ui)
51
52
  - [Error Handling](#error-handling)
52
53
  - [Mode Behavior](#mode-behavior)
@@ -1657,7 +1658,7 @@ if (model) {
1657
1658
  Get or set the thinking level. Level is clamped to model capabilities (non-reasoning models always use "off"). Changes emit `thinking_level_select`.
1658
1659
 
1659
1660
  ```typescript
1660
- const current = pi.getThinkingLevel(); // "off" | "minimal" | "low" | "medium" | "high" | "xhigh"
1661
+ const current = pi.getThinkingLevel(); // "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
1661
1662
  pi.setThinkingLevel("high");
1662
1663
  ```
1663
1664
 
@@ -2229,6 +2230,143 @@ If a slot renderer is not defined or throws:
2229
2230
  - `renderCall`: Shows the tool name
2230
2231
  - `renderResult`: Shows raw text from `content`
2231
2232
 
2233
+ ### Dynamic Tool Loading
2234
+
2235
+ Extensions can register many tools while keeping only a small initial set active. A tool can then add more tools with `pi.setActiveTools()` during execution. Pi detects purely additive changes, records the newly available tool names on that tool result, and applies the updated active set before the next model request.
2236
+
2237
+ This works with every model. Models with native deferred-loading support preserve the stable prompt prefix and load the new definitions at the tool-result position. Other models use the fallback described below.
2238
+
2239
+ The lifecycle is:
2240
+
2241
+ 1. Register every tool with `pi.registerTool()` so it appears in `pi.getAllTools()`.
2242
+ 2. Keep loader tools, such as `search_tools`, active and leave searchable tools inactive.
2243
+ 3. During loader execution, call `pi.setActiveTools([...currentTools, ...matchingTools])`. The change must be additive: do not remove currently active tools in the same call.
2244
+ 4. Pi records which tools were added on the loader's tool result.
2245
+ 5. Before the next model response, Pi exposes the added definitions using native deferred loading when supported, or the normal active tool list otherwise.
2246
+
2247
+ You do not need to return provider-specific tool references or mark the loader as a special search tool. The active-tool change is the signal. Names passed to `pi.setActiveTools()` must already be registered; unknown names are ignored.
2248
+
2249
+ #### Models with native deferred loading
2250
+
2251
+ - **Anthropic**
2252
+ - **Models:** Sonnet, Opus, Fable version 4.5 or newer (without Haiku)
2253
+ - **Native representation:** Deferred definitions use `defer_loading`; the load point uses `tool_reference` content.
2254
+ - **OpenAI**
2255
+ - **Models:** `gpt-5.4` and newer family
2256
+ - **Native representation:** Pi adds completed client `tool_search_call` and `tool_search_output` items at the load point.
2257
+
2258
+ For a verified custom model or proxy, native handling can be enabled with `compat.supportsToolReferences: true` for `anthropic-messages`, or `compat.supportsToolSearch: true` for `openai-responses` and `openai-codex-responses`. Leave these disabled unless the endpoint and model accept the corresponding native protocol.
2259
+
2260
+ #### Fallback behavior
2261
+
2262
+ For all other models and providers, dynamic activation still works: Pi sends the complete current active tool list normally on the next request. The model can call the newly activated tools, but adding their definitions may invalidate the provider's cached prompt prefix.
2263
+
2264
+ Pi also uses this safe fallback when the active set is not purely additive, such as replacing one group of tools with another. Tool removals therefore work, but they do not use deferred loading.
2265
+
2266
+ For the best cache behavior, keep the loader tool active for the whole session and add tools instead of replacing the active set. Also note that activating a tool with `promptSnippet` or `promptGuidelines` rebuilds the system prompt; that system-prompt change can invalidate the prefix even when the provider supports deferred schemas. Lazily loaded tools should usually rely on their tool `description` and omit active-only prompt metadata.
2267
+
2268
+ #### Search tool example
2269
+
2270
+ The following extension registers two searchable tools, removes them from the initial active set, and keeps only `search_tools` as their loader. The example uses simple keyword matching, but the search implementation could use BM25, embeddings, a remote catalog, or project-specific routing.
2271
+
2272
+ ```typescript
2273
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2274
+ import { Type } from "typebox";
2275
+
2276
+ const SEARCHABLE_TOOL_NAMES = new Set(["lookup_weather", "search_issues"]);
2277
+
2278
+ export default function (pi: ExtensionAPI) {
2279
+ pi.registerTool({
2280
+ name: "lookup_weather",
2281
+ label: "Lookup Weather",
2282
+ description: "Look up the current weather for a city",
2283
+ parameters: Type.Object({ city: Type.String() }),
2284
+ async execute(_toolCallId, params) {
2285
+ return {
2286
+ content: [{ type: "text", text: `Weather for ${params.city}: sunny` }],
2287
+ details: {},
2288
+ };
2289
+ },
2290
+ });
2291
+
2292
+ pi.registerTool({
2293
+ name: "search_issues",
2294
+ label: "Search Issues",
2295
+ description: "Search project issues by keyword",
2296
+ parameters: Type.Object({ query: Type.String() }),
2297
+ async execute(_toolCallId, params) {
2298
+ return {
2299
+ content: [{ type: "text", text: `No open issues matching ${params.query}` }],
2300
+ details: {},
2301
+ };
2302
+ },
2303
+ });
2304
+
2305
+ pi.registerTool({
2306
+ name: "search_tools",
2307
+ label: "Search Tools",
2308
+ description: "Search for and enable tools relevant to a task",
2309
+ promptSnippet: "Search for additional tools when the active tools cannot perform the task",
2310
+ promptGuidelines: [
2311
+ "Use search_tools when a task requires a capability that is not currently available.",
2312
+ ],
2313
+ parameters: Type.Object({
2314
+ query: Type.String({ description: "Capability or task to search for" }),
2315
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 10 })),
2316
+ }),
2317
+ async execute(_toolCallId, params) {
2318
+ const terms = params.query.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
2319
+ const matches = pi.getAllTools()
2320
+ .filter((tool) => SEARCHABLE_TOOL_NAMES.has(tool.name))
2321
+ .map((tool) => ({
2322
+ tool,
2323
+ score: terms.reduce(
2324
+ (score, term) =>
2325
+ score + (`${tool.name} ${tool.description}`.toLowerCase().includes(term) ? 1 : 0),
2326
+ 0,
2327
+ ),
2328
+ }))
2329
+ .filter((match) => match.score > 0)
2330
+ .sort((a, b) => b.score - a.score)
2331
+ .slice(0, params.limit ?? 3)
2332
+ .map((match) => match.tool.name);
2333
+
2334
+ if (matches.length === 0) {
2335
+ return {
2336
+ content: [{ type: "text", text: `No tools found for: ${params.query}` }],
2337
+ details: { matches: [] },
2338
+ };
2339
+ }
2340
+
2341
+ const active = pi.getActiveTools();
2342
+ const added = matches.filter((name) => !active.includes(name));
2343
+ pi.setActiveTools([...new Set([...active, ...added])]);
2344
+
2345
+ return {
2346
+ content: [{
2347
+ type: "text",
2348
+ text: added.length > 0
2349
+ ? `Loaded tools: ${added.join(", ")}`
2350
+ : `Matching tools already active: ${matches.join(", ")}`,
2351
+ }],
2352
+ details: { matches, added },
2353
+ };
2354
+ },
2355
+ });
2356
+
2357
+ pi.on("session_start", () => {
2358
+ // Keep searchable tools registered but initially inactive. Preserve built-ins
2359
+ // and tools owned by other extensions, and keep the loader itself active.
2360
+ const initialTools = pi.getActiveTools().filter(
2361
+ (name) => !SEARCHABLE_TOOL_NAMES.has(name),
2362
+ );
2363
+ pi.setActiveTools([...new Set([...initialTools, "search_tools"])]);
2364
+ });
2365
+ }
2366
+ ```
2367
+
2368
+ When `search_tools` adds a match, the model receives that definition on the immediately following request. On a native-capable model the definition is anchored after the search result without changing the initial tool-schema prefix. On other models it appears in the normal tool list on that same following request.
2369
+
2232
2370
  ## Custom UI
2233
2371
 
2234
2372
  Extensions can interact with users via `ctx.ui` methods and customize how messages/tools render.
@@ -119,6 +119,7 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1
119
119
  | Keybinding id | Default | Description |
120
120
  |--------|---------|-------------|
121
121
  | `app.tools.expand` | `ctrl+o` | Collapse or expand tool output |
122
+ | `app.message.copy` | `ctrl+x` | Copy the last assistant message, or the selected message in `/tree` |
122
123
  | `app.message.followUp` | `alt+enter` | Queue follow-up message |
123
124
  | `app.message.dequeue` | `alt+up` | Restore queued messages to editor |
124
125
 
package/docs/models.md CHANGED
@@ -205,22 +205,44 @@ If your command is slow, expensive, rate-limited, or should keep using a previou
205
205
  | `input` | No | `["text"]` | Input types: `["text"]` or `["text", "image"]` |
206
206
  | `contextWindow` | No | `128000` | Context window size in tokens |
207
207
  | `maxTokens` | No | `16384` | Maximum output tokens |
208
- | `cost` | No | all zeros | `{"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}` (per million tokens) |
208
+ | `cost` | No | all zeros | Per-million-token rates with optional request-wide input pricing tiers |
209
209
  | `compat` | No | provider `compat` | Provider compatibility overrides. Merged with provider-level `compat` when both are set. |
210
210
 
211
+ A cost tier supplies a complete alternate rate set and applies to the full request when total input usage (`input + cacheRead + cacheWrite`) exceeds `inputTokensAbove`. When multiple tiers match, the highest threshold wins.
212
+
213
+ ```json
214
+ {
215
+ "cost": {
216
+ "input": 5,
217
+ "output": 30,
218
+ "cacheRead": 0.5,
219
+ "cacheWrite": 6.25,
220
+ "tiers": [
221
+ {
222
+ "inputTokensAbove": 272000,
223
+ "input": 10,
224
+ "output": 45,
225
+ "cacheRead": 1,
226
+ "cacheWrite": 12.5
227
+ }
228
+ ]
229
+ }
230
+ }
231
+ ```
232
+
211
233
  Current behavior:
212
234
  - `/model`, `--list-models`, and the interactive footer display entries by model `id`.
213
235
  - The configured `name` is used for model matching and secondary model detail text. It does not replace the footer/status-bar model id.
214
236
 
215
237
  ### Thinking Level Map
216
238
 
217
- Use `thinkingLevelMap` on a model to describe model-specific thinking controls. Keys are pi thinking levels: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`.
239
+ Use `thinkingLevelMap` on a model to describe model-specific thinking controls. Keys are pi thinking levels: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Maps may contain holes; for example, a model can expose `high` and `max` without exposing `xhigh`.
218
240
 
219
241
  Values are tristate:
220
242
 
221
243
  | Value | Meaning |
222
244
  |-------|---------|
223
- | omitted | Level is supported and uses the provider's default mapping |
245
+ | omitted | Standard levels through `high` use the provider's default mapping; extended `xhigh` and `max` levels are unsupported |
224
246
  | string | Level is supported and this value is sent to the provider |
225
247
  | `null` | Level is unsupported and hidden/skipped/clamped away |
226
248
 
@@ -235,7 +257,8 @@ Example for a model that only supports off, high, and max reasoning:
235
257
  "low": null,
236
258
  "medium": null,
237
259
  "high": "high",
238
- "xhigh": "max"
260
+ "xhigh": null,
261
+ "max": "max"
239
262
  }
240
263
  }
241
264
  ```
@@ -316,6 +339,24 @@ Use `modelOverrides` to customize built-in models and matching extension-registe
316
339
 
317
340
  `modelOverrides` supports these fields per model: `name`, `reasoning`, `thinkingLevelMap`, `input`, `cost` (partial), `contextWindow`, `maxTokens`, `headers`, `compat`.
318
341
 
342
+ Direct OpenAI GPT-5.6 Sol, Terra, and Luna default to a `272000` context window so requests remain within OpenAI's short-context pricing tier. To opt into OpenAI's 1.05M context window, increase it for each model you use:
343
+
344
+ ```json
345
+ {
346
+ "providers": {
347
+ "openai": {
348
+ "modelOverrides": {
349
+ "gpt-5.6-sol": {
350
+ "contextWindow": 1050000
351
+ }
352
+ }
353
+ }
354
+ }
355
+ }
356
+ ```
357
+
358
+ The override preserves the built-in pricing metadata. Requests with more than 272K total input tokens use GPT-5.6's long-context rates for the entire request. Apply the same override to `gpt-5.6-terra` or `gpt-5.6-luna` when needed.
359
+
319
360
  Behavior notes:
320
361
  - `modelOverrides` are applied to built-in provider models and matching extension-registered provider models.
321
362
  - Unknown model IDs are ignored.
@@ -404,6 +445,8 @@ For providers with partial OpenAI compatibility, use the `compat` field.
404
445
  | `thinkingFormat` | Use `reasoning_effort`, `openrouter`, `deepseek`, `together`, `zai`, `qwen`, `chat-template`, or `qwen-chat-template` thinking parameters |
405
446
  | `chatTemplateKwargs` | `chat_template_kwargs` values for `thinkingFormat: "chat-template"`; use `{ "$var": "thinking.enabled" }` or `{ "$var": "thinking.effort" }` for pi-controlled thinking values |
406
447
  | `cacheControlFormat` | Use Anthropic-style `cache_control` markers on the system prompt, last tool definition, and last user/assistant text content. Currently only `anthropic` is supported. |
448
+ | `sendSessionAffinityHeaders` | For `openai-completions`, send session-affinity headers from the session id when caching is enabled. Default: `false`. |
449
+ | `sessionAffinityFormat` | For `openai-completions` and `openai-responses`, the session-affinity header format: `openai` sends `session_id`/`x-client-request-id` (completions also `x-session-affinity`), `openai-nosession` omits the underscore-containing `session_id` header, `openrouter` sends `x-session-id`. Does not affect the `prompt_cache_key` body param. Default: auto-detected. |
407
450
  | `supportsStrictMode` | Include the `strict` field in tool definitions |
408
451
  | `supportsLongCacheRetention` | Whether the provider accepts long cache retention when cache retention is `long`: `prompt_cache_retention: "24h"` for OpenAI prompt caching, or `cache_control.ttl: "1h"` when `cacheControlFormat` is `anthropic`. Default: `true`. |
409
452
  | `openRouterRouting` | OpenRouter provider routing preferences. This object is sent as-is in the `provider` field of the [OpenRouter API request](https://openrouter.ai/docs/guides/routing/provider-selection). |
package/docs/providers.md CHANGED
@@ -55,6 +55,7 @@ pi
55
55
  | DeepSeek | `DEEPSEEK_API_KEY` | `deepseek` |
56
56
  | NVIDIA NIM | `NVIDIA_API_KEY` | `nvidia` |
57
57
  | Google Gemini | `GEMINI_API_KEY` | `google` |
58
+ | Amazon Bedrock | `AWS_BEARER_TOKEN_BEDROCK` | `amazon-bedrock` |
58
59
  | Mistral | `MISTRAL_API_KEY` | `mistral` |
59
60
  | Groq | `GROQ_API_KEY` | `groq` |
60
61
  | Cerebras | `CEREBRAS_API_KEY` | `cerebras` |
@@ -170,6 +171,8 @@ export AZURE_OPENAI_DEPLOYMENT_NAME_MAP=gpt-4=my-gpt4,gpt-4o=my-gpt4o
170
171
 
171
172
  ### Amazon Bedrock
172
173
 
174
+ Use `/login amazon-bedrock` to store a Bedrock API key, or configure one of the ambient AWS credential sources below:
175
+
173
176
  ```bash
174
177
  # Option 1: AWS Profile
175
178
  export AWS_PROFILE=your-profile
@@ -113,7 +113,7 @@ pi @README.md "Summarize this"
113
113
  pi @src/app.ts @src/app.test.ts "Review these together"
114
114
  ```
115
115
 
116
- Images can be pasted with Ctrl+V (Alt+V on Windows) or dragged into supported terminals.
116
+ Images or text can be pasted with Ctrl+V (Alt+V on Windows); images can also be dragged into supported terminals.
117
117
 
118
118
  ### Run shell commands
119
119
 
package/docs/rpc.md CHANGED
@@ -286,9 +286,9 @@ Set the reasoning/thinking level for models that support it.
286
286
  {"type": "set_thinking_level", "level": "high"}
287
287
  ```
288
288
 
289
- Levels: `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`
289
+ Levels: `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"`
290
290
 
291
- Note: `"xhigh"` is only supported by OpenAI codex-max models.
291
+ `"xhigh"` and `"max"` are exposed only when supported by the selected model. Some models, including GPT-5.6, expose both.
292
292
 
293
293
  Response:
294
294
  ```json
package/docs/sdk.md CHANGED
@@ -387,7 +387,7 @@ const available = await modelRegistry.getAvailable();
387
387
 
388
388
  const { session } = await createAgentSession({
389
389
  model: opus,
390
- thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh
390
+ thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh, max
391
391
 
392
392
  // Models for cycling (Ctrl+P in interactive mode)
393
393
  scopedModels: [
package/docs/settings.md CHANGED
@@ -29,7 +29,7 @@ Use `/trust` in interactive mode to save a project trust decision for future ses
29
29
  |---------|------|---------|-------------|
30
30
  | `defaultProvider` | string | - | Default provider (e.g., `"anthropic"`, `"openai"`) |
31
31
  | `defaultModel` | string | - | Default model ID |
32
- | `defaultThinkingLevel` | string | - | `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"` |
32
+ | `defaultThinkingLevel` | string | - | `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"` |
33
33
  | `hideThinkingBlock` | boolean | `false` | Hide thinking blocks in output |
34
34
  | `showCacheMissNotices` | boolean | `false` | Show transcript notices for significant prompt-cache misses |
35
35
  | `thinkingBudgets` | object | - | Custom token budgets per thinking level |
@@ -185,7 +185,7 @@ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explic
185
185
 
186
186
  | Setting | Type | Default | Description |
187
187
  |---------|------|---------|-------------|
188
- | `shellPath` | string | - | Custom shell path (e.g., for Cygwin on Windows) |
188
+ | `shellPath` | string | - | Custom shell path (e.g., for Cygwin on Windows); supports a leading `~` for the home directory |
189
189
  | `shellCommandPrefix` | string | - | Prefix for every bash command (e.g., `"shopt -s expand_aliases"`) |
190
190
  | `npmCommand` | string[] | - | Command argv used for npm package lookup/install operations (e.g., `["mise", "exec", "node@20", "--", "npm"]`) |
191
191
 
package/docs/themes.md CHANGED
@@ -109,6 +109,7 @@ vim ~/.pi/agent/themes/my-theme.json
109
109
  "thinkingMedium": "#00ffff",
110
110
  "thinkingHigh": "#ff00ff",
111
111
  "thinkingXhigh": "#ff0000",
112
+ "thinkingMax": "#ff0088",
112
113
  "bashMode": "#ffaa00"
113
114
  }
114
115
  }
@@ -139,13 +140,13 @@ vim ~/.pi/agent/themes/my-theme.json
139
140
 
140
141
  - `name` is required, must be unique, and must not contain `/`.
141
142
  - `vars` is optional. Define reusable colors here, then reference them in `colors`.
142
- - `colors` must define all 51 required tokens.
143
+ - `colors` must define all 51 required tokens. `thinkingMax` is optional and falls back to `thinkingXhigh`.
143
144
 
144
145
  The `$schema` field enables editor auto-completion and validation.
145
146
 
146
147
  ## Color Tokens
147
148
 
148
- Every theme must define all 51 color tokens. There are no optional colors.
149
+ Every theme must define all 51 required color tokens. `thinkingMax` is optional for compatibility with existing themes; when omitted, it uses `thinkingXhigh`.
149
150
 
150
151
  ### Core UI (11 colors)
151
152
 
@@ -216,7 +217,7 @@ Every theme must define all 51 color tokens. There are no optional colors.
216
217
  | `syntaxOperator` | Operators |
217
218
  | `syntaxPunctuation` | Punctuation |
218
219
 
219
- ### Thinking Level Borders (6 colors)
220
+ ### Thinking Level Borders (6 required, 1 optional)
220
221
 
221
222
  Editor border colors indicating thinking level (visual hierarchy from subtle to prominent):
222
223
 
@@ -228,6 +229,7 @@ Editor border colors indicating thinking level (visual hierarchy from subtle to
228
229
  | `thinkingMedium` | Medium thinking |
229
230
  | `thinkingHigh` | High thinking |
230
231
  | `thinkingXhigh` | Extra high thinking |
232
+ | `thinkingMax` | Maximum thinking; optional, falls back to `thinkingXhigh` |
231
233
 
232
234
  ### Bash Mode (1 color)
233
235
 
package/docs/tui.md CHANGED
@@ -424,7 +424,7 @@ renderResult(result, options, theme, context) {
424
424
  | Diffs | `toolDiffAdded`, `toolDiffRemoved`, `toolDiffContext` |
425
425
  | Markdown | `mdHeading`, `mdLink`, `mdLinkUrl`, `mdCode`, `mdCodeBlock`, `mdCodeBlockBorder`, `mdQuote`, `mdQuoteBorder`, `mdHr`, `mdListBullet` |
426
426
  | Syntax | `syntaxComment`, `syntaxKeyword`, `syntaxFunction`, `syntaxVariable`, `syntaxString`, `syntaxNumber`, `syntaxType`, `syntaxOperator`, `syntaxPunctuation` |
427
- | Thinking | `thinkingOff`, `thinkingMinimal`, `thinkingLow`, `thinkingMedium`, `thinkingHigh`, `thinkingXhigh` |
427
+ | Thinking | `thinkingOff`, `thinkingMinimal`, `thinkingLow`, `thinkingMedium`, `thinkingHigh`, `thinkingXhigh`, `thinkingMax` |
428
428
  | Modes | `bashMode` |
429
429
 
430
430
  **Background colors** (`theme.bg(color, text)`):
package/docs/usage.md CHANGED
@@ -22,6 +22,7 @@ The editor can be replaced temporarily by built-in UI such as `/settings` or by
22
22
  | File reference | Type `@` to fuzzy-search project files |
23
23
  | Path completion | Press Tab to complete paths |
24
24
  | Multi-line input | Shift+Enter, or Ctrl+Enter on Windows Terminal |
25
+ | Copy response | Ctrl+X copies the last assistant message; in `/tree`, it copies the selected message |
25
26
  | Images | Paste with Ctrl+V, Alt+V on Windows, or drag into the terminal |
26
27
  | Shell command | `!command` runs and sends output to the model |
27
28
  | Hidden shell command | `!!command` runs without sending output to the model |
@@ -183,7 +184,7 @@ cat README.md | pi -p "Summarize this text"
183
184
  | `--provider <name>` | Provider, such as `anthropic`, `openai`, or `google` |
184
185
  | `--model <pattern>` | Model pattern or ID; supports `provider/id` and optional `:<thinking>` |
185
186
  | `--api-key <key>` | API key, overriding environment variables |
186
- | `--thinking <level>` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh` |
187
+ | `--thinking <level>` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` |
187
188
  | `--models <patterns>` | Comma-separated patterns for Ctrl+P cycling |
188
189
  | `--list-models [search]` | List available models |
189
190
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.80.5",
3
+ "version": "0.80.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.80.5",
9
+ "version": "0.80.7",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.80.5",
4
+ "version": "0.80.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.80.5",
4
+ "version": "0.80.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
- "version": "0.80.5",
3
+ "version": "0.80.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-gondolin",
9
- "version": "0.80.5",
9
+ "version": "0.80.7",
10
10
  "dependencies": {
11
11
  "@earendil-works/gondolin": "0.12.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
3
  "private": true,
4
- "version": "0.80.5",
4
+ "version": "0.80.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -52,7 +52,7 @@ interface Preset {
52
52
  /** Model ID (e.g., "claude-sonnet-4-5") */
53
53
  model?: string;
54
54
  /** Thinking level */
55
- thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
55
+ thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
56
56
  /** Tools to enable (replaces default set) */
57
57
  tools?: string[];
58
58
  /** Instructions to append to system prompt */
@@ -100,7 +100,7 @@ function loadPresets(cwd: string): PresetsConfig {
100
100
 
101
101
  interface OriginalState {
102
102
  model: Model<Api> | undefined;
103
- thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
103
+ thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
104
104
  tools: string[];
105
105
  }
106
106
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
- "version": "1.10.5",
3
+ "version": "1.10.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "1.10.5",
9
+ "version": "1.10.7",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sandbox-runtime": "^0.0.26"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "1.10.5",
4
+ "version": "1.10.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "0.80.5",
3
+ "version": "0.80.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "0.80.5",
9
+ "version": "0.80.7",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "0.80.5",
4
+ "version": "0.80.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-coding-agent",
3
- "version": "0.80.5",
3
+ "version": "0.80.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@earendil-works/pi-coding-agent",
9
- "version": "0.80.5",
9
+ "version": "0.80.7",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@earendil-works/pi-agent-core": "^0.80.5",
13
- "@earendil-works/pi-ai": "^0.80.5",
14
- "@earendil-works/pi-tui": "^0.80.5",
12
+ "@earendil-works/pi-agent-core": "^0.80.7",
13
+ "@earendil-works/pi-ai": "^0.80.7",
14
+ "@earendil-works/pi-tui": "^0.80.7",
15
15
  "@silvia-odwyer/photon-node": "0.3.4",
16
16
  "chalk": "5.6.2",
17
17
  "cross-spawn": "7.0.6",
@@ -474,11 +474,11 @@
474
474
  }
475
475
  },
476
476
  "node_modules/@earendil-works/pi-agent-core": {
477
- "version": "0.80.5",
478
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.80.5.tgz",
477
+ "version": "0.80.7",
478
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.80.7.tgz",
479
479
  "license": "MIT",
480
480
  "dependencies": {
481
- "@earendil-works/pi-ai": "^0.80.5",
481
+ "@earendil-works/pi-ai": "^0.80.7",
482
482
  "ignore": "7.0.5",
483
483
  "typebox": "1.1.38",
484
484
  "yaml": "2.9.0"
@@ -488,8 +488,8 @@
488
488
  }
489
489
  },
490
490
  "node_modules/@earendil-works/pi-ai": {
491
- "version": "0.80.5",
492
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.5.tgz",
491
+ "version": "0.80.7",
492
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.7.tgz",
493
493
  "license": "MIT",
494
494
  "dependencies": {
495
495
  "@anthropic-ai/sdk": "0.91.1",
@@ -512,8 +512,8 @@
512
512
  }
513
513
  },
514
514
  "node_modules/@earendil-works/pi-tui": {
515
- "version": "0.80.5",
516
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.5.tgz",
515
+ "version": "0.80.7",
516
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.7.tgz",
517
517
  "license": "MIT",
518
518
  "dependencies": {
519
519
  "get-east-asian-width": "1.6.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-coding-agent",
3
- "version": "0.80.5",
3
+ "version": "0.80.7",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -39,9 +39,9 @@
39
39
  "prepublishOnly": "npm run clean && npm run build && npm run shrinkwrap"
40
40
  },
41
41
  "dependencies": {
42
- "@earendil-works/pi-agent-core": "^0.80.5",
43
- "@earendil-works/pi-ai": "^0.80.5",
44
- "@earendil-works/pi-tui": "^0.80.5",
42
+ "@earendil-works/pi-agent-core": "^0.80.7",
43
+ "@earendil-works/pi-ai": "^0.80.7",
44
+ "@earendil-works/pi-tui": "^0.80.7",
45
45
  "@silvia-odwyer/photon-node": "0.3.4",
46
46
  "chalk": "5.6.2",
47
47
  "cross-spawn": "7.0.6",