@earendil-works/pi-coding-agent 0.81.0 → 0.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +13 -0
  3. package/dist/cli/config-selector.d.ts.map +1 -1
  4. package/dist/cli/config-selector.js +1 -1
  5. package/dist/cli/config-selector.js.map +1 -1
  6. package/dist/cli/startup-ui.d.ts.map +1 -1
  7. package/dist/cli/startup-ui.js +1 -1
  8. package/dist/cli/startup-ui.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +33 -0
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +37 -3
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/compaction/branch-summarization.d.ts +5 -0
  14. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  15. package/dist/core/compaction/branch-summarization.js +5 -7
  16. package/dist/core/compaction/branch-summarization.js.map +1 -1
  17. package/dist/core/compaction/compaction.d.ts +13 -4
  18. package/dist/core/compaction/compaction.d.ts.map +1 -1
  19. package/dist/core/compaction/compaction.js +29 -17
  20. package/dist/core/compaction/compaction.js.map +1 -1
  21. package/dist/core/extensions/runner.d.ts.map +1 -1
  22. package/dist/core/extensions/runner.js +4 -0
  23. package/dist/core/extensions/runner.js.map +1 -1
  24. package/dist/core/extensions/types.d.ts +5 -1
  25. package/dist/core/extensions/types.d.ts.map +1 -1
  26. package/dist/core/extensions/types.js.map +1 -1
  27. package/dist/core/model-config.d.ts +30 -0
  28. package/dist/core/model-config.d.ts.map +1 -1
  29. package/dist/core/model-config.js +6 -0
  30. package/dist/core/model-config.js.map +1 -1
  31. package/dist/core/model-registry.d.ts.map +1 -1
  32. package/dist/core/model-registry.js +2 -2
  33. package/dist/core/model-registry.js.map +1 -1
  34. package/dist/core/model-resolver.d.ts.map +1 -1
  35. package/dist/core/model-resolver.js +7 -0
  36. package/dist/core/model-resolver.js.map +1 -1
  37. package/dist/core/model-runtime.d.ts +0 -1
  38. package/dist/core/model-runtime.d.ts.map +1 -1
  39. package/dist/core/model-runtime.js +3 -5
  40. package/dist/core/model-runtime.js.map +1 -1
  41. package/dist/core/remote-catalog-provider.d.ts +1 -1
  42. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  43. package/dist/core/remote-catalog-provider.js +5 -10
  44. package/dist/core/remote-catalog-provider.js.map +1 -1
  45. package/dist/core/sdk.d.ts.map +1 -1
  46. package/dist/core/sdk.js +7 -3
  47. package/dist/core/sdk.js.map +1 -1
  48. package/dist/core/settings-manager.d.ts +1 -1
  49. package/dist/core/settings-manager.d.ts.map +1 -1
  50. package/dist/core/settings-manager.js.map +1 -1
  51. package/dist/core/system-prompt.d.ts.map +1 -1
  52. package/dist/core/system-prompt.js +1 -1
  53. package/dist/core/system-prompt.js.map +1 -1
  54. package/dist/core/tools/bash.d.ts +2 -0
  55. package/dist/core/tools/bash.d.ts.map +1 -1
  56. package/dist/core/tools/bash.js +34 -5
  57. package/dist/core/tools/bash.js.map +1 -1
  58. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  59. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  60. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  61. package/dist/extensions/llama/provider.d.ts.map +1 -1
  62. package/dist/extensions/llama/provider.js +1 -2
  63. package/dist/extensions/llama/provider.js.map +1 -1
  64. package/dist/main.d.ts.map +1 -1
  65. package/dist/main.js +2 -1
  66. package/dist/main.js.map +1 -1
  67. package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
  68. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  69. package/dist/modes/interactive/components/extension-editor.js +16 -46
  70. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  71. package/dist/modes/interactive/external-editor.d.ts +12 -0
  72. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  73. package/dist/modes/interactive/external-editor.js +37 -0
  74. package/dist/modes/interactive/external-editor.js.map +1 -0
  75. package/dist/modes/interactive/interactive-mode.d.ts +2 -1
  76. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  77. package/dist/modes/interactive/interactive-mode.js +58 -46
  78. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  79. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  80. package/dist/modes/rpc/rpc-mode.js +1 -0
  81. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  82. package/dist/utils/clipboard.d.ts.map +1 -1
  83. package/dist/utils/clipboard.js +19 -8
  84. package/dist/utils/clipboard.js.map +1 -1
  85. package/dist/utils/version-check.d.ts.map +1 -1
  86. package/dist/utils/version-check.js +3 -1
  87. package/dist/utils/version-check.js.map +1 -1
  88. package/docs/compaction.md +1 -1
  89. package/docs/custom-provider.md +5 -2
  90. package/docs/environment-variables.md +88 -0
  91. package/docs/extensions.md +11 -3
  92. package/docs/index.md +1 -0
  93. package/docs/json.md +5 -1
  94. package/docs/models.md +6 -2
  95. package/docs/providers.md +8 -1
  96. package/docs/rpc.md +56 -6
  97. package/docs/sdk.md +3 -0
  98. package/docs/settings.md +1 -1
  99. package/docs/usage.md +0 -13
  100. package/examples/extensions/custom-compaction.ts +3 -0
  101. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  102. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  103. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  104. package/examples/extensions/gondolin/package-lock.json +2 -2
  105. package/examples/extensions/gondolin/package.json +1 -1
  106. package/examples/extensions/handoff.ts +9 -1
  107. package/examples/extensions/sandbox/package-lock.json +2 -2
  108. package/examples/extensions/sandbox/package.json +1 -1
  109. package/examples/extensions/summarize.ts +3 -0
  110. package/examples/extensions/with-deps/package-lock.json +2 -2
  111. package/examples/extensions/with-deps/package.json +1 -1
  112. package/npm-shrinkwrap.json +16 -15
  113. package/package.json +5 -4
@@ -259,7 +259,7 @@ models: [{
259
259
  ```
260
260
 
261
261
  Use `openrouter` for OpenRouter-style `reasoning: { effort }` controls. Use `together` for Together-style `reasoning: { enabled }` controls; with `supportsReasoningEffort`, it also sends `reasoning_effort`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`.
262
- Use `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` on the system prompt, last tool definition, and last user/assistant text content.
262
+ Use `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` on the system prompt, last tool definition, and last user, assistant, or tool-result text content.
263
263
 
264
264
  For Anthropic-compatible providers using `api: "anthropic-messages"`, set `compat.forceAdaptiveThinking: true` on models or providers whose upstream model requires adaptive thinking (`thinking.type: "adaptive"` plus `output_config.effort`). Built-in adaptive Claude models set this automatically. Set `compat.allowEmptySignature: true` only for providers that emit empty thinking signatures and expect `signature: ""` on replay.
265
265
 
@@ -737,6 +737,8 @@ interface ProviderModelConfig {
737
737
  supportsDeveloperRole?: boolean;
738
738
  supportsReasoningEffort?: boolean;
739
739
  supportsUsageInStreaming?: boolean;
740
+ supportsStrictMode?: boolean;
741
+ supportsOpenAIGrammarTools?: boolean; // openai-completions/openai-responses; false falls back to normal function tools
740
742
  maxTokensField?: "max_completion_tokens" | "max_tokens";
741
743
  requiresToolResultName?: boolean;
742
744
  requiresAssistantAfterToolResult?: boolean;
@@ -755,9 +757,10 @@ interface ProviderModelConfig {
755
757
  supportsCacheControlOnTools?: boolean;
756
758
  forceAdaptiveThinking?: boolean;
757
759
  allowEmptySignature?: boolean;
760
+ supportsStrictTools?: boolean;
758
761
  };
759
762
  }
760
763
  ```
761
764
 
762
765
  `openrouter` sends `reasoning: { effort }`. `deepseek` sends `thinking: { type: "enabled" | "disabled" }` and `reasoning_effort` when enabled. `together` sends `reasoning: { enabled }` and also `reasoning_effort` when `supportsReasoningEffort` is enabled. `qwen` is for DashScope-style top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`. Use `chat-template` for configurable `chat_template_kwargs`, for example DeepSeek V3.x behind vLLM with `chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }`.
763
- `cacheControlFormat: "anthropic"` applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user/assistant text content.
766
+ `cacheControlFormat: "anthropic"` applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user, assistant, or tool-result text content.
@@ -0,0 +1,88 @@
1
+ # Environment Variables
2
+
3
+ Pi uses environment variables in three ways:
4
+
5
+ - Variables such as `PI_OFFLINE` configure the Pi process.
6
+ - Pi sets `PI_CODING_AGENT` so child processes can detect that they run inside Pi.
7
+ - Commands run by the LLM-callable bash tool receive `PI_*` variables describing the current session.
8
+
9
+ Provider API-key variables are documented separately in [Providers](providers.md#environment-variables-or-auth-file).
10
+
11
+ ## Process Marker
12
+
13
+ The CLI and RPC entry points set `PI_CODING_AGENT=true`. Child processes inherit it and can use it to detect that they run inside Pi. It is not session-specific and is not set automatically when Pi is embedded through the SDK.
14
+
15
+ ## Bash Tool Session Environment
16
+
17
+ Commands run by the bash tool receive the current Pi session state:
18
+
19
+ | Variable | Description |
20
+ |----------|-------------|
21
+ | `PI_SESSION_ID` | Current session ID |
22
+ | `PI_SESSION_FILE` | Absolute path to the current session JSONL file; unset for ephemeral sessions |
23
+ | `PI_PROVIDER` | Currently selected model provider |
24
+ | `PI_MODEL` | Currently selected model ID |
25
+ | `PI_REASONING_LEVEL` | Current effective reasoning level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` |
26
+
27
+ The values are resolved when each command starts. Switching models or changing the reasoning level therefore affects the next bash command without restarting Pi. `PI_PROVIDER` and `PI_MODEL` identify the selected Pi model, not a different upstream model that a router may choose internally.
28
+
29
+ When asked which model or provider is running, inspect these variables instead of inferring the answer from the system prompt:
30
+
31
+ ```bash
32
+ printf '%s/%s\n' "$PI_PROVIDER" "$PI_MODEL"
33
+ printf 'reasoning=%s session=%s\n' "$PI_REASONING_LEVEL" "$PI_SESSION_ID"
34
+ ```
35
+
36
+ The session file can be inspected directly when the session is persistent:
37
+
38
+ ```bash
39
+ if [ -n "$PI_SESSION_FILE" ]; then
40
+ tail -n 1 "$PI_SESSION_FILE"
41
+ fi
42
+ ```
43
+
44
+ These variables are injected into the LLM-callable bash tool. They are not injected into user-entered `!` or `!!` commands.
45
+
46
+ ### Custom Bash Tools
47
+
48
+ Bash tools created with `createBashTool()` expose the session environment by default when registered with Pi. Injection happens before `spawnHook`, so a hook receives the variables in `ctx.env`:
49
+
50
+ ```typescript
51
+ const bashTool = createBashTool(cwd, {
52
+ spawnHook: (ctx) => ({
53
+ ...ctx,
54
+ env: { ...ctx.env, CI: "1" },
55
+ }),
56
+ });
57
+ ```
58
+
59
+ Disable session metadata independently of the spawn hook:
60
+
61
+ ```typescript
62
+ const bashTool = createBashTool(cwd, {
63
+ exposeSessionEnvironment: false,
64
+ spawnHook: (ctx) => ctx,
65
+ });
66
+ ```
67
+
68
+ When disabled, Pi removes inherited values for these variables so nested Pi processes do not expose stale parent-session metadata.
69
+
70
+ ## Pi Process Configuration
71
+
72
+ These variables are read by Pi itself:
73
+
74
+ | Variable | Description |
75
+ |----------|-------------|
76
+ | `PI_CODING_AGENT_DIR` | Override the config directory; default is `~/.pi/agent` |
77
+ | `PI_CODING_AGENT_SESSION_DIR` | Override session storage; overridden by `--session-dir` |
78
+ | `PI_PACKAGE_DIR` | Override the package directory, useful for Nix/Guix store paths |
79
+ | `PI_OFFLINE` | Disable startup network operations, including update checks, package updates, and install/update telemetry |
80
+ | `PI_SKIP_VERSION_CHECK` | Disable the `pi.dev` latest-version request |
81
+ | `PI_TELEMETRY` | Override install/update telemetry and provider attribution headers: `1`/`true`/`yes` or `0`/`false`/`no` |
82
+ | `PI_CACHE_RETENTION` | Set to `long` for extended provider prompt caching where supported |
83
+ | `PI_SHARE_VIEWER_URL` | Override the base URL used by `/share` |
84
+ | `PI_HARDWARE_CURSOR` | Set to `1` to show the hardware cursor; see [Terminal setup](terminal-setup.md) |
85
+ | `VISUAL`, `EDITOR` | External editor fallback when `externalEditor` is unset |
86
+ | `HTTP_PROXY`, `HTTPS_PROXY` | Proxy outbound HTTP requests |
87
+
88
+ Provider credentials such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and cloud-provider configuration are listed in [Providers](providers.md#environment-variables-or-auth-file).
@@ -982,9 +982,9 @@ ctx.sessionManager.buildContextEntries() // Active branch entries with compac
982
982
  ctx.sessionManager.getLeafId() // Current leaf entry ID
983
983
  ```
984
984
 
985
- ### ctx.modelRegistry / ctx.model
985
+ ### ctx.modelRegistry / ctx.model / ctx.thinkingLevel
986
986
 
987
- Access to models, providers, and resolved authentication. `ctx.modelRegistry.getProvider(id)` returns the effective pi-ai provider, while `getProviderAuth(id)` resolves its current API key, headers, base URL, and provider-scoped environment without requiring a loaded model. `ctx.model` is the active model.
987
+ Access to models, providers, and resolved authentication. `ctx.modelRegistry.getProvider(id)` returns the effective pi-ai provider, while `getProviderAuth(id)` resolves its current API key, headers, base URL, and provider-scoped environment without requiring a loaded model. `ctx.model` is the active model, and `ctx.thinkingLevel` is its current effective thinking level.
988
988
 
989
989
  ### ctx.signal
990
990
 
@@ -2096,7 +2096,15 @@ const bashTool = createBashTool(cwd, {
2096
2096
  });
2097
2097
  ```
2098
2098
 
2099
- See [examples/extensions/ssh.ts](../examples/extensions/ssh.ts) for a complete SSH example with `--ssh` flag.
2099
+ `createBashTool()` exposes the current session to commands through `PI_SESSION_ID`, `PI_SESSION_FILE`, `PI_PROVIDER`, `PI_MODEL`, and `PI_REASONING_LEVEL`. Injection happens before `spawnHook`, so hooks receive these values in `env` and preserve them when they spread the existing environment as above. Set `exposeSessionEnvironment: false` to disable them:
2100
+
2101
+ ```typescript
2102
+ const bashTool = createBashTool(cwd, {
2103
+ exposeSessionEnvironment: false,
2104
+ });
2105
+ ```
2106
+
2107
+ See [Bash tool session environment](environment-variables.md#bash-tool-session-environment) for variable semantics. See [examples/extensions/ssh.ts](../examples/extensions/ssh.ts) for a complete SSH example with `--ssh` flag.
2100
2108
 
2101
2109
  ### Output Truncation
2102
2110
 
package/docs/index.md CHANGED
@@ -68,6 +68,7 @@ For the full first-run flow, see [Quickstart](quickstart.md).
68
68
 
69
69
  ## Reference
70
70
 
71
+ - [Environment variables](environment-variables.md) - Pi process configuration and session metadata available to bash tools.
71
72
  - [Session format](session-format.md) - JSONL session file format, entry types, and SessionManager API.
72
73
 
73
74
  ## Platform setup
package/docs/json.md CHANGED
@@ -17,7 +17,11 @@ type AgentSessionEvent =
17
17
  | { type: "compaction_start"; reason: "manual" | "threshold" | "overflow" }
18
18
  | { type: "compaction_end"; reason: "manual" | "threshold" | "overflow"; result: CompactionResult | undefined; aborted: boolean; willRetry: boolean; errorMessage?: string }
19
19
  | { type: "auto_retry_start"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }
20
- | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string };
20
+ | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string }
21
+ | { type: "summarization_retry_scheduled"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }
22
+ | { type: "summarization_retry_attempt_start"; source: "branchSummary" }
23
+ | { type: "summarization_retry_attempt_start"; source: "compaction"; reason: "manual" | "threshold" | "overflow" }
24
+ | { type: "summarization_retry_finished" };
21
25
  ```
22
26
 
23
27
  `queue_update` emits the full pending steering and follow-up queues whenever they change. `compaction_start` and `compaction_end` cover both manual and automatic compaction.
package/docs/models.md CHANGED
@@ -375,6 +375,8 @@ Some Anthropic models require adaptive thinking (`thinking.type: "adaptive"` plu
375
375
 
376
376
  Some Anthropic-compatible providers emit thinking blocks with empty signatures and still expect them on replay. Set `allowEmptySignature` to `true` only for those providers; real Anthropic rejects empty thinking signatures.
377
377
 
378
+ Built-in Anthropic models enable `supportsStrictTools` in their model metadata. Custom Anthropic-compatible models must set it to `true` when their endpoint accepts strict JSON-schema tool definitions.
379
+
378
380
  ```json
379
381
  {
380
382
  "providers": {
@@ -408,6 +410,7 @@ Some Anthropic-compatible providers emit thinking blocks with empty signatures a
408
410
  | `supportsCacheControlOnTools` | Whether the provider accepts Anthropic-style `cache_control` markers on tool definitions. Default: `true`. |
409
411
  | `forceAdaptiveThinking` | Whether to send adaptive thinking (`thinking.type: "adaptive"` plus `output_config.effort`) for this model. Built-in adaptive models set this automatically. Default: `false`. |
410
412
  | `allowEmptySignature` | Whether to replay empty thinking signatures as `signature: ""` instead of converting thinking to text. Default: `false`. |
413
+ | `supportsStrictTools` | Whether the provider accepts strict JSON-schema tool definitions. Default: `false`; built-in Anthropic models enable it in generated metadata. |
411
414
 
412
415
  ## OpenAI Compatibility
413
416
 
@@ -445,10 +448,11 @@ For providers with partial OpenAI compatibility, use the `compat` field.
445
448
  | `requiresReasoningContentOnAssistantMessages` | Include empty `reasoning_content` on all replayed assistant messages when reasoning is enabled |
446
449
  | `thinkingFormat` | Use `reasoning_effort`, `openrouter`, `deepseek`, `together`, `zai`, `qwen`, `chat-template`, or `qwen-chat-template` thinking parameters |
447
450
  | `chatTemplateKwargs` | `chat_template_kwargs` values for `thinkingFormat: "chat-template"`; use `{ "$var": "thinking.enabled" }` or `{ "$var": "thinking.effort" }` for pi-controlled thinking values |
448
- | `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. |
451
+ | `cacheControlFormat` | Use Anthropic-style `cache_control` markers on the system prompt, last tool definition, and last user, assistant, or tool-result text content. Currently only `anthropic` is supported. |
449
452
  | `sendSessionAffinityHeaders` | For `openai-completions`, send session-affinity headers from the session id when caching is enabled. Default: `false`. |
450
453
  | `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. |
451
- | `supportsStrictMode` | Include the `strict` field in tool definitions |
454
+ | `supportsStrictMode` | Whether the provider accepts strict JSON-schema function tool definitions. Defaults depend on the API; built-in OpenAI models carry explicit capability metadata. |
455
+ | `supportsOpenAIGrammarTools` | Whether OpenAI-compatible APIs emit custom Lark/regex grammar tools. When `false`, grammar-constrained tools fall back to normal function tools. Default: `false`; the built-in model catalog enables it for GPT-5+ models on OpenAI, OpenAI Codex, Azure OpenAI, GitHub Copilot, opencode, and Cloudflare AI Gateway. |
452
456
  | `deferredToolsMode` | Use provider-specific deferred tool serialization. Currently only `"kimi"` is supported for Kimi's OpenAI-compatible Chat Completions format. |
453
457
  | `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`. |
454
458
  | `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
@@ -20,9 +20,10 @@ Use `/login` in interactive mode, then select a provider:
20
20
  - Claude Pro/Max
21
21
  - GitHub Copilot
22
22
  - xAI (Grok/X subscription)
23
+ - OpenRouter (OAuth-minted API key billed from OpenRouter credits)
23
24
  - Radius
24
25
 
25
- Use `/logout` to clear credentials. Tokens are stored in `~/.pi/agent/auth.json` and auto-refresh when expired.
26
+ Use `/logout` to clear credentials. Tokens are stored in `~/.pi/agent/auth.json` and auto-refresh when expired. OpenRouter instead mints a user-controlled API key that does not expire automatically.
26
27
 
27
28
  ### OpenAI Codex
28
29
 
@@ -43,6 +44,12 @@ Anthropic subscription auth is active for Claude Pro/Max accounts. Third-party h
43
44
  - Run `/login xai`, then select **Use a subscription**
44
45
  - `XAI_API_KEY` remains available through **Use an API key**
45
46
 
47
+ ### OpenRouter
48
+
49
+ - Run `/login openrouter`, then select **Sign in with OpenRouter** to open the OpenRouter PKCE authorization flow
50
+ - The authorization creates a user-controlled OpenRouter API key billed from your OpenRouter credits
51
+ - `OPENROUTER_API_KEY` remains available through **Use an API key**
52
+
46
53
  ### Radius
47
54
 
48
55
  Radius is a dynamic `pi-messages` gateway. `/login radius` stores OAuth tokens in `auth.json`; the gateway catalog is refreshed independently and cached in `models-store.json`. Custom Radius gateways can be declared in `models.json` with `"oauth": "radius"` and a gateway `baseUrl`.
package/docs/rpc.md CHANGED
@@ -23,7 +23,7 @@ Common options:
23
23
  - **Responses**: JSON objects with `type: "response"` indicating command success/failure
24
24
  - **Events**: Agent events streamed to stdout as JSON lines
25
25
 
26
- All commands support an optional `id` field for request/response correlation. If provided, the corresponding response will include the same `id`.
26
+ All commands support an optional `id` field for request/response correlation. If provided, the corresponding response will include the same `id`. `bash_execution_update` events also include the `id` of their originating `bash` command.
27
27
 
28
28
  ### Framing
29
29
 
@@ -455,15 +455,18 @@ Response:
455
455
 
456
456
  #### bash
457
457
 
458
- Execute a shell command and add output to conversation context.
458
+ Execute a shell command and add output to conversation context. Output streams as `bash_execution_update` events while the command runs; the response contains the final result.
459
459
 
460
460
  ```json
461
- {"type": "bash", "command": "ls -la"}
461
+ {"id": "req-1", "type": "bash", "command": "ls -la"}
462
462
  ```
463
463
 
464
+ Include an `id` to associate streamed `bash_execution_update` events with this command.
465
+
464
466
  Response:
465
467
  ```json
466
468
  {
469
+ "id": "req-1",
467
470
  "type": "response",
468
471
  "command": "bash",
469
472
  "success": true,
@@ -494,7 +497,7 @@ If output was truncated, includes `fullOutputPath`:
494
497
 
495
498
  **How bash results reach the LLM:**
496
499
 
497
- The `bash` command executes immediately and returns a `BashResult`. Internally, a `BashExecutionMessage` is created and stored in the agent's message state. This message does NOT emit an event.
500
+ The `bash` command executes immediately and returns a `BashResult`. Internally, a `BashExecutionMessage` is created and stored in the agent's message state.
498
501
 
499
502
  When the next `prompt` command is sent, all messages (including `BashExecutionMessage`) are transformed before being sent to the LLM. The `BashExecutionMessage` is converted to a `UserMessage` with this format:
500
503
 
@@ -509,7 +512,6 @@ drwxr-xr-x ...
509
512
  This means:
510
513
  1. Bash output is included in the LLM context on the **next prompt**, not immediately
511
514
  2. Multiple bash commands can be executed before a prompt; all outputs will be included
512
- 3. No event is emitted for the `BashExecutionMessage` itself
513
515
 
514
516
  #### abort_bash
515
517
 
@@ -829,7 +831,7 @@ Each command has:
829
831
 
830
832
  ## Events
831
833
 
832
- Events are streamed to stdout as JSON lines during agent operation. Events do NOT include an `id` field (only responses do).
834
+ Events are streamed to stdout as JSON lines during agent operation. Events do not generally include an `id` field; `bash_execution_update` includes the `id` of its originating `bash` command when one was provided.
833
835
 
834
836
  ### Event Types
835
837
 
@@ -843,6 +845,7 @@ Events are streamed to stdout as JSON lines during agent operation. Events do NO
843
845
  | `message_start` | Message begins |
844
846
  | `message_update` | Streaming update (text/thinking/toolcall deltas) |
845
847
  | `message_end` | Message completes |
848
+ | `bash_execution_update` | Direct RPC bash command output chunk |
846
849
  | `tool_execution_start` | Tool begins execution |
847
850
  | `tool_execution_update` | Tool execution progress (streaming output) |
848
851
  | `tool_execution_end` | Tool completes |
@@ -851,6 +854,9 @@ Events are streamed to stdout as JSON lines during agent operation. Events do NO
851
854
  | `compaction_end` | Compaction completes |
852
855
  | `auto_retry_start` | Auto-retry begins (after transient error) |
853
856
  | `auto_retry_end` | Auto-retry completes (success or final failure) |
857
+ | `summarization_retry_scheduled` | Retry scheduled for a transient compaction or branch-summary summarization error |
858
+ | `summarization_retry_attempt_start` | Retried summarization request starts |
859
+ | `summarization_retry_finished` | Summarization retry loop completes |
854
860
  | `extension_error` | Extension threw an error |
855
861
 
856
862
  ### agent_start
@@ -948,6 +954,20 @@ Example streaming a text response:
948
954
  {"type":"message_update","message":{...},"assistantMessageEvent":{"type":"text_end","contentIndex":0,"content":"Hello world","partial":{...}}}
949
955
  ```
950
956
 
957
+ ### bash_execution_update
958
+
959
+ Emitted once for each output chunk from a direct `bash` command. `id` matches the command's `id`, allowing clients to associate output with the correct command.
960
+
961
+ Events stream all output while the command runs, even if the final `bash` response's `output` is truncated.
962
+
963
+ ```json
964
+ {
965
+ "type": "bash_execution_update",
966
+ "id": "req-1",
967
+ "delta": "total 48\n"
968
+ }
969
+ ```
970
+
951
971
  ### tool_execution_start / tool_execution_update / tool_execution_end
952
972
 
953
973
  Emitted when a tool begins, streams progress, and completes execution.
@@ -1077,6 +1097,36 @@ On final failure (max retries exceeded):
1077
1097
  }
1078
1098
  ```
1079
1099
 
1100
+ ### summarization_retry_scheduled / summarization_retry_attempt_start / summarization_retry_finished
1101
+
1102
+ Emitted when compaction or branch-summary summarization retries after a transient provider error. These events use the same retry settings as automatic assistant-turn retries.
1103
+
1104
+ ```json
1105
+ {
1106
+ "type": "summarization_retry_scheduled",
1107
+ "attempt": 1,
1108
+ "maxAttempts": 3,
1109
+ "delayMs": 2000,
1110
+ "errorMessage": "terminated"
1111
+ }
1112
+ ```
1113
+
1114
+ ```json
1115
+ {
1116
+ "type": "summarization_retry_attempt_start",
1117
+ "source": "compaction",
1118
+ "reason": "threshold"
1119
+ }
1120
+ ```
1121
+
1122
+ For branch summaries, `source` is `"branchSummary"` and no `reason` is present.
1123
+
1124
+ ```json
1125
+ {
1126
+ "type": "summarization_retry_finished"
1127
+ }
1128
+ ```
1129
+
1080
1130
  ### extension_error
1081
1131
 
1082
1132
  Emitted when an extension throws an error.
package/docs/sdk.md CHANGED
@@ -319,6 +319,9 @@ session.subscribe((event) => {
319
319
  case "compaction_end":
320
320
  case "auto_retry_start":
321
321
  case "auto_retry_end":
322
+ case "summarization_retry_scheduled":
323
+ case "summarization_retry_attempt_start":
324
+ case "summarization_retry_finished":
322
325
  break;
323
326
  }
324
327
  });
package/docs/settings.md CHANGED
@@ -142,7 +142,7 @@ Set `PI_SKIP_VERSION_CHECK=1` to disable the Pi version update check. Use `--off
142
142
  | `retry.provider.maxRetries` | number | `0` | Provider/SDK retry attempts |
143
143
  | `retry.provider.maxRetryDelayMs` | number | `60000` | Max server-requested delay before failing (60s) |
144
144
 
145
- When a provider requests a retry delay longer than `retry.provider.maxRetryDelayMs` (e.g., Google's "quota will reset after 5h"), the request fails immediately with an informative error instead of waiting silently. Set to `0` to disable the cap.
145
+ When a provider requests a retry delay longer than `retry.provider.maxRetryDelayMs`, the request fails immediately with an informative error instead of waiting silently. Set it to `0` to disable the limit.
146
146
 
147
147
  Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explicitly needed. Setting it above `0` can make SDK/provider retries handle out-of-usage-limit errors before Pi sees them, which may block the agent until the provider quota resets in some circumstances.
148
148
 
package/docs/usage.md CHANGED
@@ -289,19 +289,6 @@ pi --tools read,grep,find,ls -p "Review the code"
289
289
  pi --exclude-tools ask_question
290
290
  ```
291
291
 
292
- ### Environment Variables
293
-
294
- | Variable | Description |
295
- |----------|-------------|
296
- | `PI_CODING_AGENT_DIR` | Override config directory; default is `~/.pi/agent` |
297
- | `PI_CODING_AGENT_SESSION_DIR` | Override session storage directory; overridden by `--session-dir` |
298
- | `PI_PACKAGE_DIR` | Override package directory, useful for Nix/Guix store paths |
299
- | `PI_OFFLINE` | Disable startup network operations, including update checks, package update checks, and install/update telemetry |
300
- | `PI_SKIP_VERSION_CHECK` | Skip the Pi version update check at startup. This prevents the `pi.dev` latest-version request |
301
- | `PI_TELEMETRY` | Override install/update telemetry and provider attribution headers: `1`/`true`/`yes` or `0`/`false`/`no`. This does not disable update checks |
302
- | `PI_CACHE_RETENTION` | Set to `long` for extended prompt cache where supported |
303
- | `VISUAL`, `EDITOR` | Fallback external editor for Ctrl+G when `externalEditor` is unset; defaults to Notepad on Windows and `nano` elsewhere |
304
-
305
292
  ## Design Principles
306
293
 
307
294
  Pi keeps the core small and pushes workflow-specific behavior into extensions, skills, prompt templates, and packages.
@@ -13,6 +13,7 @@
13
13
  * pi --extension examples/extensions/custom-compaction.ts
14
14
  */
15
15
 
16
+ import { uuidv7 } from "@earendil-works/pi-ai";
16
17
  import { complete } from "@earendil-works/pi-ai/compat";
17
18
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
18
19
  import { convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent";
@@ -96,6 +97,8 @@ ${conversationText}
96
97
  env: auth.env,
97
98
  maxTokens: 8192,
98
99
  signal,
100
+ cacheRetention: "none",
101
+ sessionId: uuidv7(),
99
102
  },
100
103
  );
101
104
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.81.0",
3
+ "version": "0.82.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.81.0",
9
+ "version": "0.82.0",
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.81.0",
4
+ "version": "0.82.0",
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.81.0",
4
+ "version": "0.82.0",
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.81.0",
3
+ "version": "0.82.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-gondolin",
9
- "version": "0.81.0",
9
+ "version": "0.82.0",
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.81.0",
4
+ "version": "0.82.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -13,6 +13,7 @@
13
13
  */
14
14
 
15
15
  import type { AgentMessage } from "@earendil-works/pi-agent-core";
16
+ import { uuidv7 } from "@earendil-works/pi-ai";
16
17
  import { complete, type Message } from "@earendil-works/pi-ai/compat";
17
18
  import type { ExtensionAPI, SessionEntry } from "@earendil-works/pi-coding-agent";
18
19
  import { BorderedLoader, convertToLlm, serializeConversation } from "@earendil-works/pi-coding-agent";
@@ -136,7 +137,14 @@ export default function (pi: ExtensionAPI) {
136
137
  const response = await complete(
137
138
  ctx.model!,
138
139
  { systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
139
- { apiKey: auth.apiKey, headers: auth.headers, env: auth.env, signal: loader.signal },
140
+ {
141
+ apiKey: auth.apiKey,
142
+ headers: auth.headers,
143
+ env: auth.env,
144
+ signal: loader.signal,
145
+ cacheRetention: "none",
146
+ sessionId: uuidv7(),
147
+ },
140
148
  );
141
149
 
142
150
  if (response.stopReason === "aborted") {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "1.11.0",
9
+ "version": "1.12.0",
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.11.0",
4
+ "version": "1.12.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,3 +1,4 @@
1
+ import { uuidv7 } from "@earendil-works/pi-ai";
1
2
  import { complete, getModel } from "@earendil-works/pi-ai/compat";
2
3
  import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
3
4
  import { DynamicBorder, getMarkdownTheme } from "@earendil-works/pi-coding-agent";
@@ -193,6 +194,8 @@ export default function (pi: ExtensionAPI) {
193
194
  headers: auth.headers,
194
195
  env: auth.env,
195
196
  reasoningEffort: "high",
197
+ cacheRetention: "none",
198
+ sessionId: uuidv7(),
196
199
  },
197
200
  );
198
201
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "0.81.0",
3
+ "version": "0.82.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "0.81.0",
9
+ "version": "0.82.0",
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.81.0",
4
+ "version": "0.82.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",