@fleetagent/pi-coding-agent 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +3 -3
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -3
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -2
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +12 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +123 -18
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/export-html/template.js +6 -3
- package/dist/core/extensions/runner.d.ts +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +8 -2
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +4 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +65 -13
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/output-guard.d.ts +1 -0
- package/dist/core/output-guard.d.ts.map +1 -1
- package/dist/core/output-guard.js +52 -22
- package/dist/core/output-guard.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +31 -12
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-agent.d.ts.map +1 -1
- package/dist/core/pi-agent.js +12 -3
- package/dist/core/pi-agent.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts +9 -1
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +134 -11
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/session/jsonl-helpers.d.ts +2 -1
- package/dist/core/session/jsonl-helpers.d.ts.map +1 -1
- package/dist/core/session/jsonl-helpers.js +6 -3
- package/dist/core/session/jsonl-helpers.js.map +1 -1
- package/dist/core/session/local-session-manager.d.ts +1 -0
- package/dist/core/session/local-session-manager.d.ts.map +1 -1
- package/dist/core/session/local-session-manager.js +12 -4
- package/dist/core/session/local-session-manager.js.map +1 -1
- package/dist/core/session/session-manager.d.ts +1 -0
- package/dist/core/session/session-manager.d.ts.map +1 -1
- package/dist/core/session/session-manager.js.map +1 -1
- package/dist/core/session/stores/jsonl-session-store.d.ts +2 -1
- package/dist/core/session/stores/jsonl-session-store.d.ts.map +1 -1
- package/dist/core/session/stores/jsonl-session-store.js +105 -78
- package/dist/core/session/stores/jsonl-session-store.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +14 -9
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +73 -63
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +45 -76
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +27 -12
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +11 -2
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +3 -3
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +13 -4
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts +1 -0
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +37 -0
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +7 -6
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +24 -32
- package/dist/core/tools/write.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +3 -2
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +118 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +14 -5
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +1 -1
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +34 -8
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +10 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +3 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +64 -7
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +15 -3
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +3 -1
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js +14 -8
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/deprecation.d.ts +4 -0
- package/dist/utils/deprecation.d.ts.map +1 -0
- package/dist/utils/deprecation.js +13 -0
- package/dist/utils/deprecation.js.map +1 -0
- package/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -0
- package/dist/utils/image-resize-core.js +124 -0
- package/dist/utils/image-resize-core.js.map +1 -0
- package/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/utils/image-resize-worker.d.ts.map +1 -0
- package/dist/utils/image-resize-worker.js +31 -0
- package/dist/utils/image-resize-worker.js.map +1 -0
- package/dist/utils/image-resize.d.ts +6 -27
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +60 -116
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/json.d.ts +3 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/json.js +7 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +10 -4
- package/dist/utils/version-check.js.map +1 -1
- package/docs/custom-provider.md +22 -9
- package/docs/extensions.md +4 -3
- package/docs/models.md +34 -12
- package/docs/packages.md +5 -4
- package/docs/providers.md +13 -5
- package/docs/sdk.md +56 -0
- package/docs/settings.md +4 -2
- package/docs/terminal-setup.md +6 -0
- package/docs/usage.md +3 -3
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +54 -3
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/git-merge-and-resolve.ts +115 -0
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +13 -12
- package/package.json +5 -5
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
|
|
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
|
|
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
|
}
|
|
@@ -319,16 +328,24 @@ For providers or proxies using `api: "anthropic-messages"`, use `compat.supports
|
|
|
319
328
|
|
|
320
329
|
By default pi sends per-tool `eager_input_streaming: true`. If a proxy or Anthropic-compatible backend rejects that field, set `supportsEagerToolInputStreaming` to `false`. Pi will omit `tools[].eager_input_streaming` and send the legacy `fine-grained-tool-streaming-2025-05-14` beta header for tool-enabled requests instead.
|
|
321
330
|
|
|
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`.
|
|
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
|
+
|
|
335
|
+
|
|
322
336
|
```json
|
|
323
337
|
{
|
|
324
338
|
"providers": {
|
|
325
339
|
"anthropic-proxy": {
|
|
326
340
|
"baseUrl": "https://proxy.example.com",
|
|
327
341
|
"api": "anthropic-messages",
|
|
328
|
-
"apiKey": "ANTHROPIC_PROXY_KEY",
|
|
342
|
+
"apiKey": "$ANTHROPIC_PROXY_KEY",
|
|
329
343
|
"compat": {
|
|
330
344
|
"supportsEagerToolInputStreaming": false,
|
|
331
|
-
"supportsLongCacheRetention": true
|
|
345
|
+
"supportsLongCacheRetention": true,
|
|
346
|
+
"forceAdaptiveThinking": true,
|
|
347
|
+
"allowEmptySignature": true
|
|
348
|
+
|
|
332
349
|
},
|
|
333
350
|
"models": [
|
|
334
351
|
{
|
|
@@ -346,6 +363,11 @@ By default pi sends per-tool `eager_input_streaming: true`. If a proxy or Anthro
|
|
|
346
363
|
|-------|-------------|
|
|
347
364
|
| `supportsEagerToolInputStreaming` | Whether the provider accepts per-tool `eager_input_streaming`. Default: `true`. Set to `false` to omit that field and use the legacy fine-grained tool streaming beta header on tool-enabled requests. |
|
|
348
365
|
| `supportsLongCacheRetention` | Whether the provider accepts Anthropic long cache retention (`cache_control.ttl: "1h"`) when cache retention is `long`. Default: `true`. |
|
|
366
|
+
| `sendSessionAffinityHeaders` | Whether to send `x-session-affinity` from the session id when caching is enabled. Default: auto-detected for known providers. |
|
|
367
|
+
| `supportsCacheControlOnTools` | Whether the provider accepts Anthropic-style `cache_control` markers on tool definitions. Default: `true`. |
|
|
368
|
+
| `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`. |
|
|
369
|
+
| `allowEmptySignature` | Whether to replay empty thinking signatures as `signature: ""` instead of converting thinking to text. Default: `false`. |
|
|
370
|
+
|
|
349
371
|
|
|
350
372
|
## OpenAI Compatibility
|
|
351
373
|
|
|
@@ -399,7 +421,7 @@ Example:
|
|
|
399
421
|
"providers": {
|
|
400
422
|
"openrouter": {
|
|
401
423
|
"baseUrl": "https://openrouter.ai/api/v1",
|
|
402
|
-
"apiKey": "OPENROUTER_API_KEY",
|
|
424
|
+
"apiKey": "$OPENROUTER_API_KEY",
|
|
403
425
|
"api": "openai-completions",
|
|
404
426
|
"models": [
|
|
405
427
|
{
|
|
@@ -449,7 +471,7 @@ Vercel AI Gateway example:
|
|
|
449
471
|
"providers": {
|
|
450
472
|
"vercel-ai-gateway": {
|
|
451
473
|
"baseUrl": "https://ai-gateway.vercel.sh/v1",
|
|
452
|
-
"apiKey": "AI_GATEWAY_API_KEY",
|
|
474
|
+
"apiKey": "$AI_GATEWAY_API_KEY",
|
|
453
475
|
"api": "openai-completions",
|
|
454
476
|
"models": [
|
|
455
477
|
{
|
package/docs/packages.md
CHANGED
|
@@ -28,8 +28,8 @@ pi install ./relative/path/to/package
|
|
|
28
28
|
|
|
29
29
|
pi remove npm:@foo/bar
|
|
30
30
|
pi list # show installed packages from settings
|
|
31
|
-
pi update # update pi and
|
|
32
|
-
pi update --extensions # update
|
|
31
|
+
pi update # update pi, update packages, and reconcile pinned git refs
|
|
32
|
+
pi update --extensions # update packages and reconcile pinned git refs only
|
|
33
33
|
pi update --self # update pi only
|
|
34
34
|
pi update --self --force # reinstall pi even if current
|
|
35
35
|
pi update npm:@foo/bar # update one package
|
|
@@ -85,9 +85,10 @@ ssh://git@github.com/user/repo@v1
|
|
|
85
85
|
- HTTPS and SSH URLs are both supported.
|
|
86
86
|
- SSH URLs use your configured SSH keys automatically (respects `~/.ssh/config`).
|
|
87
87
|
- For non-interactive runs (for example CI), you can set `GIT_TERMINAL_PROMPT=0` to disable credential prompts and set `GIT_SSH_COMMAND` (for example `ssh -o BatchMode=yes -o ConnectTimeout=5`) to fail fast.
|
|
88
|
-
- Refs are pinned tags or commits
|
|
88
|
+
- Refs are pinned tags or commits. `pi update` and `pi update --extensions` do not move them to newer refs, but they do reconcile an existing clone to the configured ref.
|
|
89
|
+
- Use `pi install git:host/user/repo@new-ref` to update settings and move an existing package to a new pinned ref.
|
|
89
90
|
- Cloned to `~/.pi/agent/git/<host>/<path>` (global) or `.pi/git/<host>/<path>` (project).
|
|
90
|
-
-
|
|
91
|
+
- When reconciliation changes the checkout, pi resets and cleans the clone, then runs `npm install` if `package.json` exists.
|
|
91
92
|
|
|
92
93
|
**SSH examples:**
|
|
93
94
|
```bash
|
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
|
|
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
|
|
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
|
|
package/docs/sdk.md
CHANGED
|
@@ -47,6 +47,52 @@ const pi = await PiAgent.create({
|
|
|
47
47
|
const session = await pi.createAgentSession();
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
The session manages agent lifecycle, message history, model state, compaction, and event streaming.
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
interface AgentSession {
|
|
54
|
+
// Send a prompt and wait for completion
|
|
55
|
+
prompt(text: string, options?: PromptOptions): Promise<void>;
|
|
56
|
+
|
|
57
|
+
// Queue messages during streaming
|
|
58
|
+
steer(text: string): Promise<void>;
|
|
59
|
+
followUp(text: string): Promise<void>;
|
|
60
|
+
|
|
61
|
+
// Subscribe to events (returns unsubscribe function)
|
|
62
|
+
subscribe(listener: (event: AgentSessionEvent) => void): () => void;
|
|
63
|
+
|
|
64
|
+
// Session info
|
|
65
|
+
sessionFile: string | undefined;
|
|
66
|
+
sessionId: string;
|
|
67
|
+
|
|
68
|
+
// Model control
|
|
69
|
+
setModel(model: Model): Promise<void>;
|
|
70
|
+
setThinkingLevel(level: ThinkingLevel): void;
|
|
71
|
+
cycleModel(): Promise<ModelCycleResult | undefined>;
|
|
72
|
+
cycleThinkingLevel(): ThinkingLevel | undefined;
|
|
73
|
+
|
|
74
|
+
// State access
|
|
75
|
+
agent: Agent;
|
|
76
|
+
model: Model | undefined;
|
|
77
|
+
thinkingLevel: ThinkingLevel;
|
|
78
|
+
messages: AgentMessage[];
|
|
79
|
+
isStreaming: boolean;
|
|
80
|
+
|
|
81
|
+
// In-place tree navigation within the current session file
|
|
82
|
+
navigateTree(targetId: string, options?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string }): Promise<{ editorText?: string; cancelled: boolean }>;
|
|
83
|
+
|
|
84
|
+
// Compaction
|
|
85
|
+
compact(customInstructions?: string): Promise<CompactionResult>;
|
|
86
|
+
abortCompaction(): void;
|
|
87
|
+
|
|
88
|
+
// Abort current operation
|
|
89
|
+
abort(): Promise<void>;
|
|
90
|
+
|
|
91
|
+
// Cleanup
|
|
92
|
+
dispose(): void;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
50
96
|
Most one-off session options can be passed directly to `PiAgent.create()`:
|
|
51
97
|
|
|
52
98
|
```typescript
|
|
@@ -73,6 +119,16 @@ Use `session.sessionReference` when you need the backend-neutral active session
|
|
|
73
119
|
|
|
74
120
|
Session replacement APIs such as new-session, resume, fork, clone, and import live on `PiAgent`, not on `AgentSession`.
|
|
75
121
|
|
|
122
|
+
```typescript
|
|
123
|
+
interface PromptOptions {
|
|
124
|
+
expandPromptTemplates?: boolean;
|
|
125
|
+
images?: ImageContent[];
|
|
126
|
+
streamingBehavior?: "steer" | "followUp";
|
|
127
|
+
source?: InputSource;
|
|
128
|
+
preflightResult?: (success: boolean) => void;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
76
132
|
### SessionManager and Session
|
|
77
133
|
|
|
78
134
|
`SessionManager` handles lifecycle and discovery: create, open, continue, list, fork, and import. It returns a `Session`.
|
package/docs/settings.md
CHANGED
|
@@ -50,7 +50,7 @@ Edit directly or use `/settings` for common options.
|
|
|
50
50
|
|
|
51
51
|
### Telemetry and update checks
|
|
52
52
|
|
|
53
|
-
`enableInstallTelemetry` only controls the anonymous install/update ping to `https://pi.dev/api/report-install`. Opting out of telemetry does not disable update checks; Pi can still fetch
|
|
53
|
+
`enableInstallTelemetry` only controls the anonymous install/update ping to `https://pi.dev/api/report-install`. Opting out of telemetry does not disable update checks; Pi can still fetch npm metadata for `@fleetagent/pi-coding-agent` to look for the latest version.
|
|
54
54
|
|
|
55
55
|
Set `PI_SKIP_VERSION_CHECK=1` to disable the Pi version update check. Use `--offline` or `PI_OFFLINE=1` to disable all startup network operations described here, including update checks, package update checks, and install/update telemetry.
|
|
56
56
|
|
|
@@ -101,11 +101,13 @@ Set `PI_SKIP_VERSION_CHECK=1` to disable the Pi version update check. Use `--off
|
|
|
101
101
|
| `retry.maxRetries` | number | `3` | Maximum agent-level retry attempts |
|
|
102
102
|
| `retry.baseDelayMs` | number | `2000` | Base delay for agent-level exponential backoff (2s, 4s, 8s) |
|
|
103
103
|
| `retry.provider.timeoutMs` | number | SDK default | Provider/SDK request timeout in milliseconds |
|
|
104
|
-
| `retry.provider.maxRetries` | number |
|
|
104
|
+
| `retry.provider.maxRetries` | number | `0` | Provider/SDK retry attempts |
|
|
105
105
|
| `retry.provider.maxRetryDelayMs` | number | `60000` | Max server-requested delay before failing (60s) |
|
|
106
106
|
|
|
107
107
|
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.
|
|
108
108
|
|
|
109
|
+
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.
|
|
110
|
+
|
|
109
111
|
```json
|
|
110
112
|
{
|
|
111
113
|
"retry": {
|
package/docs/terminal-setup.md
CHANGED
|
@@ -6,6 +6,12 @@ Pi uses the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-p
|
|
|
6
6
|
|
|
7
7
|
Work out of the box.
|
|
8
8
|
|
|
9
|
+
## Apple Terminal
|
|
10
|
+
|
|
11
|
+
Pi enables enhanced key reporting when available. If Terminal.app still sends plain Return for `Shift+Enter`, pi uses a local macOS modifier fallback to treat that Return as `Shift+Enter`.
|
|
12
|
+
|
|
13
|
+
This fallback only works when pi runs on the same Mac as Terminal.app. It cannot detect the local keyboard over remote SSH.
|
|
14
|
+
|
|
9
15
|
## Ghostty
|
|
10
16
|
|
|
11
17
|
Add to your Ghostty config (`~/Library/Application Support/com.mitchellh.ghostty/config` on macOS, `~/.config/ghostty/config` on Linux):
|
package/docs/usage.md
CHANGED
|
@@ -129,8 +129,8 @@ pi [options] [@files...] [messages...]
|
|
|
129
129
|
pi install <source> [-l] # Install package, -l for project-local
|
|
130
130
|
pi remove <source> [-l] # Remove package
|
|
131
131
|
pi uninstall <source> [-l] # Alias for remove
|
|
132
|
-
pi update [source|self|pi] # Update pi and packages;
|
|
133
|
-
pi update --extensions # Update packages only
|
|
132
|
+
pi update [source|self|pi] # Update pi and packages; reconcile pinned git refs
|
|
133
|
+
pi update --extensions # Update packages only; reconcile pinned git refs
|
|
134
134
|
pi update --self # Update pi only
|
|
135
135
|
pi update --extension <src> # Update one package
|
|
136
136
|
pi list # List installed packages
|
|
@@ -267,7 +267,7 @@ pi --tools read,grep,find,ls -p "Review the code"
|
|
|
267
267
|
| `PI_CODING_AGENT_SESSION_DIR` | Override session storage directory; overridden by `--session-dir` |
|
|
268
268
|
| `PI_PACKAGE_DIR` | Override package directory, useful for Nix/Guix store paths |
|
|
269
269
|
| `PI_OFFLINE` | Disable startup network operations, including update checks, package update checks, and install/update telemetry |
|
|
270
|
-
| `PI_SKIP_VERSION_CHECK` | Skip the Pi version update check at startup. This prevents the
|
|
270
|
+
| `PI_SKIP_VERSION_CHECK` | Skip the Pi version update check at startup. This prevents the npm metadata request |
|
|
271
271
|
| `PI_TELEMETRY` | Override install/update telemetry: `1`/`true`/`yes` or `0`/`false`/`no`. This does not disable update checks |
|
|
272
272
|
| `PI_CACHE_RETENTION` | Set to `long` for extended prompt cache where supported |
|
|
273
273
|
| `VISUAL`, `EDITOR` | External editor for Ctrl+G |
|
|
@@ -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: [
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type SimpleStreamOptions,
|
|
21
21
|
streamSimpleAnthropic,
|
|
22
22
|
streamSimpleOpenAIResponses,
|
|
23
|
+
type ThinkingLevelMap,
|
|
23
24
|
} from "@fleetagent/pi-ai";
|
|
24
25
|
import type { ExtensionAPI } from "@fleetagent/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(
|
|
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,
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge and Resolve
|
|
3
|
+
*
|
|
4
|
+
* Keeps the working branch up to date with its upstream tracking ref.
|
|
5
|
+
* After each agent turn, fetches and merges. Clean merges complete
|
|
6
|
+
* silently. When conflicts arise, the working tree is left dirty and
|
|
7
|
+
* the agent receives a follow-up message listing each conflict block
|
|
8
|
+
* with file, line range, and ours/theirs sections so it can resolve them.
|
|
9
|
+
* Also re-sends unresolved conflicts from a previous incomplete merge.
|
|
10
|
+
*
|
|
11
|
+
* Start pi with this extension:
|
|
12
|
+
* pi -e ./examples/extensions/git-merge-and-resolve.ts
|
|
13
|
+
*/
|
|
14
|
+
import { createReadStream } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { createInterface } from "node:readline";
|
|
17
|
+
import type { ExtensionAPI } from "@fleetagent/pi-coding-agent";
|
|
18
|
+
|
|
19
|
+
interface ConflictBlock {
|
|
20
|
+
file: string;
|
|
21
|
+
startLine: number;
|
|
22
|
+
separatorLine: number;
|
|
23
|
+
endLine: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Parse conflict markers from working tree files with unmerged paths. */
|
|
27
|
+
async function findConflicts(pi: ExtensionAPI, cwd: string): Promise<ConflictBlock[]> {
|
|
28
|
+
const { stdout, code } = await pi.exec("git", ["diff", "--name-only", "--diff-filter=U"]);
|
|
29
|
+
if (code !== 0 || !stdout.trim()) return [];
|
|
30
|
+
|
|
31
|
+
const blocks: ConflictBlock[] = [];
|
|
32
|
+
for (const file of stdout.trim().split("\n")) {
|
|
33
|
+
try {
|
|
34
|
+
const rl = createInterface({ input: createReadStream(join(cwd, file), "utf-8") });
|
|
35
|
+
let lineNo = 0;
|
|
36
|
+
let blockStart: number | undefined;
|
|
37
|
+
let separatorLine: number | undefined;
|
|
38
|
+
for await (const line of rl) {
|
|
39
|
+
lineNo++;
|
|
40
|
+
if (line.startsWith("<<<<<<<")) {
|
|
41
|
+
blockStart = lineNo;
|
|
42
|
+
separatorLine = undefined;
|
|
43
|
+
} else if (line.startsWith("=======") && blockStart !== undefined) {
|
|
44
|
+
separatorLine = lineNo;
|
|
45
|
+
} else if (line.startsWith(">>>>>>>") && blockStart !== undefined && separatorLine !== undefined) {
|
|
46
|
+
blocks.push({ file, startLine: blockStart, separatorLine, endLine: lineNo });
|
|
47
|
+
blockStart = undefined;
|
|
48
|
+
separatorLine = undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch {}
|
|
52
|
+
}
|
|
53
|
+
return blocks;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function formatRange(start: number, end: number): string {
|
|
57
|
+
if (start > end) return "empty";
|
|
58
|
+
if (start === end) return `${start}`;
|
|
59
|
+
return `${start}-${end}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function formatConflicts(ref: string, blocks: ConflictBlock[]): string {
|
|
63
|
+
const lines = [`Merged ${ref} with conflicts:`, ""];
|
|
64
|
+
for (const b of blocks) {
|
|
65
|
+
const ours = formatRange(b.startLine + 1, b.separatorLine - 1);
|
|
66
|
+
const theirs = formatRange(b.separatorLine + 1, b.endLine - 1);
|
|
67
|
+
lines.push(` ${b.file}:${b.startLine}-${b.endLine} (ours ${ours}, theirs ${theirs})`);
|
|
68
|
+
}
|
|
69
|
+
lines.push("", "Resolve these conflicts.");
|
|
70
|
+
return lines.join("\n");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default function (pi: ExtensionAPI) {
|
|
74
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
75
|
+
const { code: revParseCode } = await pi.exec("git", ["rev-parse", "--git-dir"]);
|
|
76
|
+
if (revParseCode !== 0) return;
|
|
77
|
+
|
|
78
|
+
let ref = "MERGE_HEAD";
|
|
79
|
+
|
|
80
|
+
// If not already in a merge, attempt one
|
|
81
|
+
const { code: mergeHeadCode } = await pi.exec("git", ["rev-parse", "MERGE_HEAD"]);
|
|
82
|
+
if (mergeHeadCode !== 0) {
|
|
83
|
+
// Only attempt a new merge if the working tree is clean
|
|
84
|
+
const { stdout: status } = await pi.exec("git", ["status", "--porcelain"]);
|
|
85
|
+
if (status.trim()) return;
|
|
86
|
+
|
|
87
|
+
const { stdout: upstream, code: upstreamCode } = await pi.exec("git", [
|
|
88
|
+
"rev-parse",
|
|
89
|
+
"--abbrev-ref",
|
|
90
|
+
"--symbolic-full-name",
|
|
91
|
+
"@{u}",
|
|
92
|
+
]);
|
|
93
|
+
if (upstreamCode !== 0) return;
|
|
94
|
+
|
|
95
|
+
ref = upstream.trim();
|
|
96
|
+
const remote = ref.split("/")[0];
|
|
97
|
+
ctx.ui.notify(`git-merge-and-resolve: fetching ${remote}, merging ${ref}`, "info");
|
|
98
|
+
|
|
99
|
+
const { code: fetchCode, stderr: fetchErr } = await pi.exec("git", ["fetch", remote]);
|
|
100
|
+
if (fetchCode !== 0) {
|
|
101
|
+
ctx.ui.notify(`git-merge-and-resolve: fetch failed: ${fetchErr.trim()}`, "warning");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { code: mergeCode } = await pi.exec("git", ["merge", "--no-ff", ref]);
|
|
106
|
+
if (mergeCode === 0) return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Either we just merged with conflicts, or we were already in an unfinished merge
|
|
110
|
+
const conflicts = await findConflicts(pi, ctx.cwd);
|
|
111
|
+
if (conflicts.length === 0) return;
|
|
112
|
+
|
|
113
|
+
pi.sendUserMessage(formatConflicts(ref, conflicts), { deliverAs: "followUp" });
|
|
114
|
+
});
|
|
115
|
+
}
|