@earendil-works/pi-coding-agent 0.76.0 → 0.78.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 (119) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +9 -0
  3. package/dist/cli/args.d.ts +2 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +23 -0
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-session-services.d.ts +1 -0
  8. package/dist/core/agent-session-services.d.ts.map +1 -1
  9. package/dist/core/agent-session-services.js +1 -0
  10. package/dist/core/agent-session-services.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +4 -1
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +23 -4
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/extensions/runner.d.ts +1 -1
  16. package/dist/core/extensions/runner.d.ts.map +1 -1
  17. package/dist/core/extensions/runner.js +8 -2
  18. package/dist/core/extensions/runner.js.map +1 -1
  19. package/dist/core/extensions/types.d.ts +7 -5
  20. package/dist/core/extensions/types.d.ts.map +1 -1
  21. package/dist/core/extensions/types.js.map +1 -1
  22. package/dist/core/model-registry.d.ts.map +1 -1
  23. package/dist/core/model-registry.js +65 -13
  24. package/dist/core/model-registry.js.map +1 -1
  25. package/dist/core/model-resolver.d.ts.map +1 -1
  26. package/dist/core/model-resolver.js +1 -1
  27. package/dist/core/model-resolver.js.map +1 -1
  28. package/dist/core/resolve-config-value.d.ts +9 -1
  29. package/dist/core/resolve-config-value.d.ts.map +1 -1
  30. package/dist/core/resolve-config-value.js +134 -11
  31. package/dist/core/resolve-config-value.js.map +1 -1
  32. package/dist/core/sdk.d.ts +2 -0
  33. package/dist/core/sdk.d.ts.map +1 -1
  34. package/dist/core/sdk.js +4 -5
  35. package/dist/core/sdk.js.map +1 -1
  36. package/dist/core/session-manager.d.ts +3 -5
  37. package/dist/core/session-manager.d.ts.map +1 -1
  38. package/dist/core/session-manager.js +42 -17
  39. package/dist/core/session-manager.js.map +1 -1
  40. package/dist/core/system-prompt.d.ts.map +1 -1
  41. package/dist/core/system-prompt.js +0 -3
  42. package/dist/core/system-prompt.js.map +1 -1
  43. package/dist/core/tools/edit.d.ts.map +1 -1
  44. package/dist/core/tools/edit.js +7 -10
  45. package/dist/core/tools/edit.js.map +1 -1
  46. package/dist/core/tools/find.d.ts.map +1 -1
  47. package/dist/core/tools/find.js.map +1 -1
  48. package/dist/core/tools/grep.d.ts.map +1 -1
  49. package/dist/core/tools/grep.js.map +1 -1
  50. package/dist/core/tools/ls.d.ts.map +1 -1
  51. package/dist/core/tools/ls.js +5 -7
  52. package/dist/core/tools/ls.js.map +1 -1
  53. package/dist/core/tools/read.d.ts.map +1 -1
  54. package/dist/core/tools/read.js +6 -7
  55. package/dist/core/tools/read.js.map +1 -1
  56. package/dist/core/tools/render-utils.d.ts +5 -2
  57. package/dist/core/tools/render-utils.d.ts.map +1 -1
  58. package/dist/core/tools/render-utils.js +17 -1
  59. package/dist/core/tools/render-utils.js.map +1 -1
  60. package/dist/core/tools/write.d.ts.map +1 -1
  61. package/dist/core/tools/write.js +5 -6
  62. package/dist/core/tools/write.js.map +1 -1
  63. package/dist/index.d.ts +2 -0
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +2 -0
  66. package/dist/index.js.map +1 -1
  67. package/dist/main.d.ts.map +1 -1
  68. package/dist/main.js +15 -2
  69. package/dist/main.js.map +1 -1
  70. package/dist/migrations.d.ts.map +1 -1
  71. package/dist/migrations.js +118 -1
  72. package/dist/migrations.js.map +1 -1
  73. package/dist/modes/interactive/components/login-dialog.d.ts +1 -3
  74. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  75. package/dist/modes/interactive/components/login-dialog.js +2 -4
  76. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  77. package/dist/modes/interactive/interactive-mode.d.ts +3 -0
  78. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  79. package/dist/modes/interactive/interactive-mode.js +59 -6
  80. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  81. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  82. package/dist/modes/interactive/theme/theme.js +10 -0
  83. package/dist/modes/interactive/theme/theme.js.map +1 -1
  84. package/dist/utils/deprecation.d.ts +4 -0
  85. package/dist/utils/deprecation.d.ts.map +1 -0
  86. package/dist/utils/deprecation.js +13 -0
  87. package/dist/utils/deprecation.js.map +1 -0
  88. package/dist/utils/json.d.ts +3 -0
  89. package/dist/utils/json.d.ts.map +1 -0
  90. package/dist/utils/json.js +7 -0
  91. package/dist/utils/json.js.map +1 -0
  92. package/docs/custom-provider.md +13 -10
  93. package/docs/development.md +1 -1
  94. package/docs/extensions.md +12 -6
  95. package/docs/models.md +25 -12
  96. package/docs/providers.md +13 -5
  97. package/docs/quickstart.md +1 -0
  98. package/docs/rpc.md +2 -1
  99. package/docs/sdk.md +6 -0
  100. package/docs/session-format.md +1 -1
  101. package/docs/sessions.md +8 -0
  102. package/docs/settings.md +1 -1
  103. package/docs/terminal-setup.md +2 -0
  104. package/docs/tui.md +2 -2
  105. package/docs/usage.md +9 -0
  106. package/examples/extensions/README.md +1 -0
  107. package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
  108. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  109. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  110. package/examples/extensions/custom-provider-gitlab-duo/index.ts +54 -3
  111. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  112. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  113. package/examples/extensions/input-transform-streaming.ts +39 -0
  114. package/examples/extensions/sandbox/package-lock.json +2 -2
  115. package/examples/extensions/sandbox/package.json +1 -1
  116. package/examples/extensions/with-deps/package-lock.json +2 -2
  117. package/examples/extensions/with-deps/package.json +1 -1
  118. package/npm-shrinkwrap.json +71 -56
  119. package/package.json +6 -6
@@ -199,7 +199,7 @@ export default async function (pi: ExtensionAPI) {
199
199
 
200
200
  pi.registerProvider("local-openai", {
201
201
  baseUrl: "http://localhost:1234/v1",
202
- apiKey: "LOCAL_OPENAI_API_KEY",
202
+ apiKey: "$LOCAL_OPENAI_API_KEY",
203
203
  api: "openai-completions",
204
204
  models: payload.data.map((model) => ({
205
205
  id: model.id,
@@ -819,6 +819,9 @@ pi.on("input", async (event, ctx) => {
819
819
  // event.text - raw input (before skill/template expansion)
820
820
  // event.images - attached images, if any
821
821
  // event.source - "interactive" (typed), "rpc" (API), or "extension" (via sendUserMessage)
822
+ // event.streamingBehavior - "steer" | "followUp" | undefined
823
+ // undefined when idle, "steer" for mid-stream interrupts,
824
+ // "followUp" for messages queued until the agent finishes
822
825
 
823
826
  // Transform: rewrite input before expansion
824
827
  if (event.text.startsWith("?quick "))
@@ -847,7 +850,7 @@ pi.on("input", async (event, ctx) => {
847
850
  - `transform` - modify text/images, then continue to expansion
848
851
  - `handled` - skip agent entirely (first handler to return this wins)
849
852
 
850
- Transforms chain across handlers. See [input-transform.ts](../examples/extensions/input-transform.ts).
853
+ Transforms chain across handlers. See [input-transform.ts](../examples/extensions/input-transform.ts) and [input-transform-streaming.ts](../examples/extensions/input-transform-streaming.ts) for `streamingBehavior`-aware routing.
851
854
 
852
855
  ## ExtensionContext
853
856
 
@@ -1490,7 +1493,8 @@ const all = pi.getAllTools();
1490
1493
  // [{
1491
1494
  // name: "read",
1492
1495
  // description: "Read file contents...",
1493
- // parameters: ...,
1496
+ // parameters: ...,
1497
+ // promptGuidelines: ["Use read to examine files instead of cat or sed."],
1494
1498
  // sourceInfo: { path: "<builtin:read>", source: "builtin", scope: "temporary", origin: "top-level" }
1495
1499
  // }, ...]
1496
1500
  const names = all.map(t => t.name);
@@ -1499,7 +1503,7 @@ const extensionTools = all.filter((t) => t.sourceInfo.source !== "builtin" && t.
1499
1503
  pi.setActiveTools(["read", "bash"]); // Switch to read-only
1500
1504
  ```
1501
1505
 
1502
- `pi.getAllTools()` returns `name`, `description`, `parameters`, and `sourceInfo`.
1506
+ `pi.getAllTools()` returns `name`, `description`, `parameters`, `promptGuidelines`, and `sourceInfo`.
1503
1507
 
1504
1508
  Typical `sourceInfo.source` values:
1505
1509
  - `builtin` for built-in tools
@@ -1551,7 +1555,7 @@ If you need to discover models from a remote endpoint, prefer an async extension
1551
1555
  pi.registerProvider("my-proxy", {
1552
1556
  name: "My Proxy",
1553
1557
  baseUrl: "https://proxy.example.com",
1554
- apiKey: "PROXY_API_KEY", // env var name or literal
1558
+ apiKey: "$PROXY_API_KEY", // env var reference
1555
1559
  api: "anthropic-messages",
1556
1560
  models: [
1557
1561
  {
@@ -1598,7 +1602,7 @@ pi.registerProvider("corporate-ai", {
1598
1602
  **Config options:**
1599
1603
  - `name` - Display name for the provider in UI such as `/login`.
1600
1604
  - `baseUrl` - API endpoint URL. Required when defining models.
1601
- - `apiKey` - API key or environment variable name. Required when defining models (unless `oauth` provided).
1605
+ - `apiKey` - API key literal, environment interpolation (`$ENV_VAR` or `${ENV_VAR}`), or leading `!command`. Required when defining models (unless `oauth` provided). `$$` escapes `$`, and `$!` escapes a literal `!` without triggering command execution.
1602
1606
  - `api` - API type: `"anthropic-messages"`, `"openai-completions"`, `"openai-responses"`, etc.
1603
1607
  - `headers` - Custom headers to include in requests.
1604
1608
  - `authHeader` - If true, adds `Authorization: Bearer` header automatically.
@@ -2543,6 +2547,7 @@ All examples in [examples/extensions/](../examples/extensions/).
2543
2547
  | `confirm-destructive.ts` | Confirm session changes | `on("session_before_switch")`, `on("session_before_fork")` |
2544
2548
  | `dirty-repo-guard.ts` | Warn on dirty git repo | `on("session_before_*")`, `exec` |
2545
2549
  | `input-transform.ts` | Transform user input | `on("input")` |
2550
+ | `input-transform-streaming.ts` | Streaming-aware input transform | `on("input")`, `streamingBehavior` |
2546
2551
  | `model-status.ts` | React to model changes | `on("model_select")`, `setStatus` |
2547
2552
  | `provider-payload.ts` | Inspect payloads and provider response headers | `on("before_provider_request")`, `on("after_provider_response")` |
2548
2553
  | `system-prompt-header.ts` | Display system prompt info | `on("agent_start")`, `getSystemPrompt` |
@@ -2553,6 +2558,7 @@ All examples in [examples/extensions/](../examples/extensions/).
2553
2558
  | `custom-compaction.ts` | Custom compaction summary | `on("session_before_compact")` |
2554
2559
  | `trigger-compact.ts` | Trigger compaction manually | `compact()` |
2555
2560
  | `git-checkpoint.ts` | Git stash on turns | `on("turn_start")`, `on("session_before_fork")`, `exec` |
2561
+ | `git-merge-and-resolve.ts` | Fetch, merge, and resolve conflicts | `on("agent_end")`, `exec`, `sendUserMessage` |
2556
2562
  | `auto-commit-on-exit.ts` | Commit on shutdown | `on("session_shutdown")`, `exec` |
2557
2563
  | **UI Components** |||
2558
2564
  | `status-line.ts` | Footer status indicator | `setStatus`, session events |
package/docs/models.md CHANGED
@@ -101,7 +101,7 @@ Use `google-generative-ai` with a `baseUrl` to add models from Google AI Studio,
101
101
  "my-google": {
102
102
  "baseUrl": "https://generativelanguage.googleapis.com/v1beta",
103
103
  "api": "google-generative-ai",
104
- "apiKey": "GEMINI_API_KEY",
104
+ "apiKey": "$GEMINI_API_KEY",
105
105
  "models": [
106
106
  {
107
107
  "id": "gemma-4-31b-it",
@@ -143,22 +143,31 @@ Set `api` at provider level (default for all models) or model level (override pe
143
143
 
144
144
  ### Value Resolution
145
145
 
146
- The `apiKey` and `headers` fields support three formats:
146
+ The `apiKey` and `headers` fields support command execution, environment interpolation, and literals:
147
147
 
148
- - **Shell command:** `"!command"` executes and uses stdout
148
+ - **Shell command:** `"!command"` at the start executes the whole value as a command and uses stdout
149
149
  ```json
150
150
  "apiKey": "!security find-generic-password -ws 'anthropic'"
151
151
  "apiKey": "!op read 'op://vault/item/credential'"
152
152
  ```
153
- - **Environment variable:** Uses the value of the named variable
153
+ - **Environment interpolation:** `"$ENV_VAR"` or `"${ENV_VAR}"` uses the value of the named variable. Interpolation works inside larger literals.
154
154
  ```json
155
- "apiKey": "MY_API_KEY"
155
+ "apiKey": "$MY_API_KEY"
156
+ "apiKey": "${KEY_PREFIX}_${KEY_SUFFIX}"
157
+ ```
158
+ `$FOO_BAR` is the variable `FOO_BAR`; use `${FOO}_BAR` when `BAR` is literal text. Missing environment variables make the value unresolved.
159
+ - **Escapes:** `"$$"` emits a literal `"$"`; `"$!"` emits a literal `"!"` without triggering command execution.
160
+ ```json
161
+ "apiKey": "$$literal-dollar-prefix"
162
+ "apiKey": "$!literal-bang-prefix"
156
163
  ```
157
164
  - **Literal value:** Used directly
158
165
  ```json
159
166
  "apiKey": "sk-..."
160
167
  ```
161
168
 
169
+ Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup.
170
+
162
171
  For `models.json`, shell commands are resolved at request time. pi intentionally does not apply built-in TTL, stale reuse, or recovery logic for arbitrary commands. Different commands need different caching and failure strategies, and pi cannot infer the right one.
163
172
 
164
173
  If your command is slow, expensive, rate-limited, or should keep using a previous value on transient failures, wrap it in your own script or command that implements the caching or TTL behavior you want.
@@ -172,10 +181,10 @@ If your command is slow, expensive, rate-limited, or should keep using a previou
172
181
  "providers": {
173
182
  "custom-proxy": {
174
183
  "baseUrl": "https://proxy.example.com/v1",
175
- "apiKey": "MY_API_KEY",
184
+ "apiKey": "$MY_API_KEY",
176
185
  "api": "anthropic-messages",
177
186
  "headers": {
178
- "x-portkey-api-key": "PORTKEY_API_KEY",
187
+ "x-portkey-api-key": "$PORTKEY_API_KEY",
179
188
  "x-secret": "!op read 'op://vault/item/secret'"
180
189
  },
181
190
  "models": [...]
@@ -268,7 +277,7 @@ To merge custom models into a built-in provider, include the `models` array:
268
277
  "providers": {
269
278
  "anthropic": {
270
279
  "baseUrl": "https://my-proxy.example.com/v1",
271
- "apiKey": "ANTHROPIC_API_KEY",
280
+ "apiKey": "$ANTHROPIC_API_KEY",
272
281
  "api": "anthropic-messages",
273
282
  "models": [...]
274
283
  }
@@ -321,17 +330,20 @@ By default pi sends per-tool `eager_input_streaming: true`. If a proxy or Anthro
321
330
 
322
331
  Some Anthropic models require adaptive thinking (`thinking.type: "adaptive"` plus `output_config.effort`) instead of the legacy budget-based thinking payload. Built-in models set this automatically. For custom providers or aliases that route to those models, set `forceAdaptiveThinking` to `true`.
323
332
 
333
+ 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.
334
+
324
335
  ```json
325
336
  {
326
337
  "providers": {
327
338
  "anthropic-proxy": {
328
339
  "baseUrl": "https://proxy.example.com",
329
340
  "api": "anthropic-messages",
330
- "apiKey": "ANTHROPIC_PROXY_KEY",
341
+ "apiKey": "$ANTHROPIC_PROXY_KEY",
331
342
  "compat": {
332
343
  "supportsEagerToolInputStreaming": false,
333
344
  "supportsLongCacheRetention": true,
334
- "forceAdaptiveThinking": true
345
+ "forceAdaptiveThinking": true,
346
+ "allowEmptySignature": true
335
347
  },
336
348
  "models": [
337
349
  {
@@ -352,6 +364,7 @@ Some Anthropic models require adaptive thinking (`thinking.type: "adaptive"` plu
352
364
  | `sendSessionAffinityHeaders` | Whether to send `x-session-affinity` from the session id when caching is enabled. Default: auto-detected for known providers. |
353
365
  | `supportsCacheControlOnTools` | Whether the provider accepts Anthropic-style `cache_control` markers on tool definitions. Default: `true`. |
354
366
  | `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`. |
367
+ | `allowEmptySignature` | Whether to replay empty thinking signatures as `signature: ""` instead of converting thinking to text. Default: `false`. |
355
368
 
356
369
  ## OpenAI Compatibility
357
370
 
@@ -405,7 +418,7 @@ Example:
405
418
  "providers": {
406
419
  "openrouter": {
407
420
  "baseUrl": "https://openrouter.ai/api/v1",
408
- "apiKey": "OPENROUTER_API_KEY",
421
+ "apiKey": "$OPENROUTER_API_KEY",
409
422
  "api": "openai-completions",
410
423
  "models": [
411
424
  {
@@ -455,7 +468,7 @@ Vercel AI Gateway example:
455
468
  "providers": {
456
469
  "vercel-ai-gateway": {
457
470
  "baseUrl": "https://ai-gateway.vercel.sh/v1",
458
- "apiKey": "AI_GATEWAY_API_KEY",
471
+ "apiKey": "$AI_GATEWAY_API_KEY",
459
472
  "api": "openai-completions",
460
473
  "models": [
461
474
  {
package/docs/providers.md CHANGED
@@ -101,23 +101,31 @@ The file is created with `0600` permissions (user read/write only). Auth file cr
101
101
 
102
102
  ### Key Resolution
103
103
 
104
- The `key` field supports three formats:
104
+ The `key` field supports command execution, environment interpolation, and literals:
105
105
 
106
- - **Shell command:** `"!command"` executes and uses stdout (cached for process lifetime)
106
+ - **Shell command:** `"!command"` at the start executes the whole value as a command and uses stdout (cached for process lifetime)
107
107
  ```json
108
108
  { "type": "api_key", "key": "!security find-generic-password -ws 'anthropic'" }
109
109
  { "type": "api_key", "key": "!op read 'op://vault/item/credential'" }
110
110
  ```
111
- - **Environment variable:** Uses the value of the named variable
111
+ - **Environment interpolation:** `"$ENV_VAR"` or `"${ENV_VAR}"` uses the value of the named variable. Interpolation works inside larger literals.
112
112
  ```json
113
- { "type": "api_key", "key": "MY_ANTHROPIC_KEY" }
113
+ { "type": "api_key", "key": "$MY_ANTHROPIC_KEY" }
114
+ { "type": "api_key", "key": "${KEY_PREFIX}_${KEY_SUFFIX}" }
115
+ ```
116
+ `$FOO_BAR` is the variable `FOO_BAR`; use `${FOO}_BAR` when `BAR` is literal text. Missing environment variables make the value unresolved.
117
+ - **Escapes:** `"$$"` emits a literal `"$"`; `"$!"` emits a literal `"!"` without triggering command execution.
118
+ ```json
119
+ { "type": "api_key", "key": "$$literal-dollar-prefix" }
120
+ { "type": "api_key", "key": "$!literal-bang-prefix" }
114
121
  ```
115
122
  - **Literal value:** Used directly
116
123
  ```json
117
124
  { "type": "api_key", "key": "sk-ant-..." }
125
+ { "type": "api_key", "key": "public" }
118
126
  ```
119
127
 
120
- OAuth credentials are also stored here after `/login` and managed automatically.
128
+ Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup. OAuth credentials are also stored here after `/login` and managed automatically.
121
129
 
122
130
  ## Cloud Providers
123
131
 
@@ -136,6 +136,7 @@ Sessions are saved automatically:
136
136
  ```bash
137
137
  pi -c # Continue most recent session
138
138
  pi -r # Browse previous sessions
139
+ pi --name "my task" # Set session display name at startup
139
140
  pi --session <path|id> # Open a specific session
140
141
  ```
141
142
 
package/docs/rpc.md CHANGED
@@ -13,6 +13,7 @@ pi --mode rpc [options]
13
13
  Common options:
14
14
  - `--provider <name>`: Set the LLM provider (anthropic, openai, google, etc.)
15
15
  - `--model <pattern>`: Model pattern or ID (supports `provider/id` and optional `:<thinking>`)
16
+ - `--name <name>` / `-n <name>`: Set the session display name at startup
16
17
  - `--no-session`: Disable session persistence
17
18
  - `--session-dir <path>`: Custom session storage directory
18
19
 
@@ -694,7 +695,7 @@ Response:
694
695
  }
695
696
  ```
696
697
 
697
- The current session name is available via `get_state` in the `sessionName` field.
698
+ The current session name is available via `get_state` in the `sessionName` field. To set the initial name when starting RPC mode, pass `--name <name>` or `-n <name>` to the `pi --mode rpc` process.
698
699
 
699
700
  ### Commands
700
701
 
package/docs/sdk.md CHANGED
@@ -472,6 +472,7 @@ Specify which built-in tools to enable:
472
472
  - Default built-ins: `read`, `bash`, `edit`, `write`
473
473
  - `noTools: "all"` disables all tools
474
474
  - `noTools: "builtin"` disables default built-ins while keeping extension and custom tools enabled
475
+ - `excludeTools` disables specific built-in, extension, or custom tool names after any `tools` allowlist is applied
475
476
 
476
477
  The `edit` tool returns `details.diff` for Pi's TUI display and `details.patch` as a standard unified patch for SDK consumers.
477
478
 
@@ -487,6 +488,11 @@ const { session } = await createAgentSession({
487
488
  const { session } = await createAgentSession({
488
489
  tools: ["read", "bash", "grep"],
489
490
  });
491
+
492
+ // Disable one tool while keeping the rest available
493
+ const { session } = await createAgentSession({
494
+ excludeTools: ["ask_question"],
495
+ });
490
496
  ```
491
497
 
492
498
  #### Tools with Custom cwd
@@ -282,7 +282,7 @@ Set `label` to `undefined` to clear a label.
282
282
 
283
283
  ### SessionInfoEntry
284
284
 
285
- Session metadata (e.g., user-defined display name). Set via `/name` command or `pi.setSessionName()` in extensions.
285
+ Session metadata (e.g., user-defined display name). Set via `/name`, `--name` / `-n`, or `pi.setSessionName()` in extensions.
286
286
 
287
287
  ```json
288
288
  {"type":"session_info","id":"k1l2m3n4","parentId":"j0k1l2m3","timestamp":"2024-12-03T14:35:00.000Z","name":"Refactor auth module"}
package/docs/sessions.md CHANGED
@@ -10,6 +10,7 @@ Sessions auto-save to `~/.pi/agent/sessions/`, organized by working directory. E
10
10
  pi -c # Continue most recent session
11
11
  pi -r # Browse and select from past sessions
12
12
  pi --no-session # Ephemeral mode; do not save
13
+ pi --name "my task" # Set session display name at startup
13
14
  pi --session <path|id> # Use a specific session file or partial session ID
14
15
  pi --fork <path|id> # Fork a session file or partial session ID into a new session
15
16
  ```
@@ -56,6 +57,13 @@ Use `/name <name>` to set a human-readable session name:
56
57
  /name Refactor auth module
57
58
  ```
58
59
 
60
+ Set the name at startup with `--name` or `-n`:
61
+
62
+ ```bash
63
+ pi --name "Refactor auth module"
64
+ pi --name "CI audit" -p "Review this build failure"
65
+ ```
66
+
59
67
  Named sessions are easier to find in `/resume` and `pi -r`.
60
68
 
61
69
  ## Branching with `/tree`
package/docs/settings.md CHANGED
@@ -46,7 +46,7 @@ Edit directly or use `/settings` for common options.
46
46
  | `treeFilterMode` | string | `"default"` | Default filter for `/tree`: `"default"`, `"no-tools"`, `"user-only"`, `"labeled-only"`, `"all"` |
47
47
  | `editorPaddingX` | number | `0` | Horizontal padding for input editor (0-3) |
48
48
  | `autocompleteMaxVisible` | number | `5` | Max visible items in autocomplete dropdown (3-20) |
49
- | `showHardwareCursor` | boolean | `false` | Show terminal cursor |
49
+ | `showHardwareCursor` | boolean | `false` | Show the terminal cursor while TUI positions it for IME support |
50
50
 
51
51
  ### Telemetry and update checks
52
52
 
@@ -49,6 +49,8 @@ config.enable_kitty_keyboard = true
49
49
  return config
50
50
  ```
51
51
 
52
+ On WSL, WezTerm may require a visible hardware cursor for IME candidate window positioning. If CJK IME candidates do not follow the text cursor, set `PI_HARDWARE_CURSOR=1` before running pi or set `showHardwareCursor` to `true` in settings.
53
+
52
54
  ## VS Code (Integrated Terminal)
53
55
 
54
56
  `keybindings.json` locations:
package/docs/tui.md CHANGED
@@ -50,9 +50,9 @@ When a `Focusable` component has focus, TUI:
50
50
  1. Sets `focused = true` on the component
51
51
  2. Scans rendered output for `CURSOR_MARKER` (a zero-width APC escape sequence)
52
52
  3. Positions the hardware terminal cursor at that location
53
- 4. Shows the hardware cursor
53
+ 4. Shows the hardware cursor only when `showHardwareCursor` is enabled
54
54
 
55
- This enables IME candidate windows to appear at the correct position for CJK input methods. The `Editor` and `Input` built-in components already implement this interface.
55
+ The cursor remains hidden by default. This keeps the fake cursor rendering, while still positioning the hardware cursor for terminals that track IME candidate windows with hidden cursors. Some terminals require a visible hardware cursor for IME positioning; enable it with `showHardwareCursor`, `setShowHardwareCursor(true)`, or `PI_HARDWARE_CURSOR=1`. The `Editor` and `Input` built-in components already implement this interface.
56
56
 
57
57
  ### Container Components with Embedded Inputs
58
58
 
package/docs/usage.md CHANGED
@@ -76,6 +76,7 @@ Sessions are saved automatically to `~/.pi/agent/sessions/`, organized by workin
76
76
  pi -c # Continue most recent session
77
77
  pi -r # Browse and select a session
78
78
  pi --no-session # Ephemeral mode; do not save
79
+ pi --name "my task" # Set session display name at startup
79
80
  pi --session <path|id> # Use a specific session file or session ID
80
81
  pi --fork <path|id> # Fork a session into a new session file
81
82
  ```
@@ -178,12 +179,14 @@ cat README.md | pi -p "Summarize this text"
178
179
  | `--fork <path\|id>` | Fork a session file or partial UUID into a new session |
179
180
  | `--session-dir <dir>` | Custom session storage directory |
180
181
  | `--no-session` | Ephemeral mode; do not save |
182
+ | `--name <name>`, `-n <name>` | Set session display name at startup |
181
183
 
182
184
  ### Tool Options
183
185
 
184
186
  | Option | Description |
185
187
  |--------|-------------|
186
188
  | `--tools <list>`, `-t <list>` | Allowlist specific built-in, extension, and custom tools |
189
+ | `--exclude-tools <list>`, `-xt <list>` | Disable specific built-in, extension, and custom tools |
187
190
  | `--no-builtin-tools`, `-nbt` | Disable built-in tools but keep extension/custom tools enabled |
188
191
  | `--no-tools`, `-nt` | Disable all tools |
189
192
 
@@ -241,6 +244,9 @@ pi -p "Summarize this codebase"
241
244
  # Non-interactive with piped stdin
242
245
  cat README.md | pi -p "Summarize this text"
243
246
 
247
+ # Named one-shot session
248
+ pi --name "release audit" -p "Audit this repository"
249
+
244
250
  # Different model
245
251
  pi --provider openai --model gpt-4o "Help me refactor"
246
252
 
@@ -255,6 +261,9 @@ pi --models "claude-*,gpt-4o"
255
261
 
256
262
  # Read-only mode
257
263
  pi --tools read,grep,find,ls -p "Review the code"
264
+
265
+ # Disable one extension or built-in tool while keeping the rest available
266
+ pi --exclude-tools ask_question
258
267
  ```
259
268
 
260
269
  ### Environment Variables
@@ -75,6 +75,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
75
75
  | `reload-runtime.ts` | Adds `/reload-runtime` and `reload_runtime` tool showing safe reload flow |
76
76
  | `interactive-shell.ts` | Run interactive commands (vim, htop) with full terminal via `user_bash` hook |
77
77
  | `inline-bash.ts` | Expands `!{command}` patterns in prompts via `input` event transformation |
78
+ | `input-transform-streaming.ts` | Skips expensive input preprocessing for mid-stream steering via `streamingBehavior` |
78
79
 
79
80
  ### Git Integration
80
81
 
@@ -568,7 +568,7 @@ function streamCustomAnthropic(
568
568
  export default function (pi: ExtensionAPI) {
569
569
  pi.registerProvider("custom-anthropic", {
570
570
  baseUrl: "https://api.anthropic.com",
571
- apiKey: "CUSTOM_ANTHROPIC_API_KEY",
571
+ apiKey: "$CUSTOM_ANTHROPIC_API_KEY",
572
572
  api: "custom-anthropic-api",
573
573
 
574
574
  models: [
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.76.0",
3
+ "version": "0.78.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.76.0",
9
+ "version": "0.78.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.76.0",
4
+ "version": "0.78.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -20,6 +20,7 @@ import {
20
20
  type SimpleStreamOptions,
21
21
  streamSimpleAnthropic,
22
22
  streamSimpleOpenAIResponses,
23
+ type ThinkingLevelMap,
23
24
  } from "@earendil-works/pi-ai";
24
25
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
25
26
 
@@ -49,6 +50,7 @@ interface GitLabModel {
49
50
  backend: Backend;
50
51
  baseUrl: string;
51
52
  reasoning: boolean;
53
+ thinkingLevelMap?: ThinkingLevelMap;
52
54
  input: ("text" | "image")[];
53
55
  cost: { input: number; output: number; cacheRead: number; cacheWrite: number };
54
56
  contextWindow: number;
@@ -57,12 +59,37 @@ interface GitLabModel {
57
59
 
58
60
  export const MODELS: GitLabModel[] = [
59
61
  // Anthropic
62
+ {
63
+ id: "claude-opus-4-8",
64
+ name: "Claude Opus 4.8",
65
+ backend: "anthropic",
66
+ baseUrl: ANTHROPIC_PROXY_URL,
67
+ reasoning: true,
68
+ thinkingLevelMap: { xhigh: "max" },
69
+ input: ["text", "image"],
70
+ cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
71
+ contextWindow: 1000000,
72
+ maxTokens: 128000,
73
+ },
74
+ {
75
+ id: "claude-sonnet-4-6",
76
+ name: "Claude Sonnet 4.6",
77
+ backend: "anthropic",
78
+ baseUrl: ANTHROPIC_PROXY_URL,
79
+ reasoning: true,
80
+ thinkingLevelMap: { xhigh: "max" },
81
+ input: ["text", "image"],
82
+ cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
83
+ contextWindow: 1000000,
84
+ maxTokens: 64000,
85
+ },
60
86
  {
61
87
  id: "claude-opus-4-5-20251101",
62
88
  name: "Claude Opus 4.5",
63
89
  backend: "anthropic",
64
90
  baseUrl: ANTHROPIC_PROXY_URL,
65
91
  reasoning: true,
92
+ thinkingLevelMap: { xhigh: "max" },
66
93
  input: ["text", "image"],
67
94
  cost: { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
68
95
  contextWindow: 200000,
@@ -74,6 +101,7 @@ export const MODELS: GitLabModel[] = [
74
101
  backend: "anthropic",
75
102
  baseUrl: ANTHROPIC_PROXY_URL,
76
103
  reasoning: true,
104
+ thinkingLevelMap: { xhigh: "max" },
77
105
  input: ["text", "image"],
78
106
  cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
79
107
  contextWindow: 200000,
@@ -85,12 +113,24 @@ export const MODELS: GitLabModel[] = [
85
113
  backend: "anthropic",
86
114
  baseUrl: ANTHROPIC_PROXY_URL,
87
115
  reasoning: true,
116
+ thinkingLevelMap: { xhigh: "max" },
88
117
  input: ["text", "image"],
89
118
  cost: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
90
119
  contextWindow: 200000,
91
120
  maxTokens: 8192,
92
121
  },
93
122
  // OpenAI (all use Responses API)
123
+ {
124
+ id: "gpt-5.5-2026-04-23",
125
+ name: "GPT-5.5",
126
+ backend: "openai",
127
+ baseUrl: OPENAI_PROXY_URL,
128
+ reasoning: true,
129
+ input: ["text", "image"],
130
+ cost: { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
131
+ contextWindow: 272000,
132
+ maxTokens: 128000,
133
+ },
94
134
  {
95
135
  id: "gpt-5.1-2025-11-13",
96
136
  name: "GPT-5.1",
@@ -285,7 +325,17 @@ export function streamGitLabDuo(
285
325
 
286
326
  const innerStream =
287
327
  cfg.backend === "anthropic"
288
- ? streamSimpleAnthropic(modelWithBaseUrl as Model<"anthropic-messages">, context, streamOptions)
328
+ ? streamSimpleAnthropic(
329
+ {
330
+ ...(modelWithBaseUrl as Model<"anthropic-messages">),
331
+ compat: {
332
+ ...(modelWithBaseUrl as Model<"anthropic-messages">).compat,
333
+ forceAdaptiveThinking: true,
334
+ },
335
+ },
336
+ context,
337
+ streamOptions,
338
+ )
289
339
  : streamSimpleOpenAIResponses(modelWithBaseUrl as Model<"openai-responses">, context, streamOptions);
290
340
 
291
341
  for await (const event of innerStream) stream.push(event);
@@ -327,12 +377,13 @@ export function streamGitLabDuo(
327
377
  export default function (pi: ExtensionAPI) {
328
378
  pi.registerProvider("gitlab-duo", {
329
379
  baseUrl: AI_GATEWAY_URL,
330
- apiKey: "GITLAB_TOKEN",
380
+ apiKey: "$GITLAB_TOKEN",
331
381
  api: "gitlab-duo-api",
332
- models: MODELS.map(({ id, name, reasoning, input, cost, contextWindow, maxTokens }) => ({
382
+ models: MODELS.map(({ id, name, reasoning, thinkingLevelMap, input, cost, contextWindow, maxTokens }) => ({
333
383
  id,
334
384
  name,
335
385
  reasoning,
386
+ thinkingLevelMap,
336
387
  input,
337
388
  cost,
338
389
  contextWindow,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.76.0",
4
+ "version": "0.78.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",