@earendil-works/pi-coding-agent 0.80.6 → 0.80.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 +77 -0
- package/README.md +9 -7
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +2 -0
- package/dist/bun/cli.js.map +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/list-models.d.ts +2 -2
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +3 -3
- package/dist/cli/list-models.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +3 -6
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +10 -9
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +6 -7
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +61 -34
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +15 -99
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +46 -259
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/cache-stats.d.ts +2 -2
- package/dist/core/cache-stats.d.ts.map +1 -1
- package/dist/core/cache-stats.js +1 -1
- package/dist/core/cache-stats.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +8 -2
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +2 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +8 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +9 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +22 -3
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/keybindings.d.ts +8 -3
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +7 -3
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +507 -0
- package/dist/core/model-config.d.ts.map +1 -0
- package/dist/core/model-config.js +242 -0
- package/dist/core/model-config.js.map +1 -0
- package/dist/core/model-registry.d.ts +13 -123
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +44 -731
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +6 -6
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +18 -17
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +77 -0
- package/dist/core/model-runtime.d.ts.map +1 -0
- package/dist/core/model-runtime.js +418 -0
- package/dist/core/model-runtime.js.map +1 -0
- package/dist/core/models-store.d.ts +17 -0
- package/dist/core/models-store.d.ts.map +1 -0
- package/dist/core/models-store.js +45 -0
- package/dist/core/models-store.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +7 -2
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/provider-composer.d.ts +55 -0
- package/dist/core/provider-composer.d.ts.map +1 -0
- package/dist/core/provider-composer.js +375 -0
- package/dist/core/provider-composer.js.map +1 -0
- package/dist/core/radius.d.ts +2 -0
- package/dist/core/radius.d.ts.map +1 -0
- package/dist/core/radius.js +2 -0
- package/dist/core/radius.js.map +1 -0
- package/dist/core/remote-catalog-provider.d.ts +5 -0
- package/dist/core/remote-catalog-provider.d.ts.map +1 -0
- package/dist/core/remote-catalog-provider.js +83 -0
- package/dist/core/remote-catalog-provider.js.map +1 -0
- package/dist/core/runtime-credentials.d.ts +15 -0
- package/dist/core/runtime-credentials.d.ts.map +1 -0
- package/dist/core/runtime-credentials.js +36 -0
- package/dist/core/runtime-credentials.js.map +1 -0
- package/dist/core/sdk.d.ts +3 -6
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +14 -25
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -11
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +11 -13
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +22 -10
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +1 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +5 -5
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +17 -8
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +11 -4
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +72 -41
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +4 -4
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +14 -27
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +5 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +49 -12
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +7 -5
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +187 -171
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -2
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +67 -5
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +2 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +13 -0
- package/dist/utils/clipboard.js.map +1 -1
- package/docs/custom-provider.md +8 -11
- package/docs/extensions.md +160 -1
- package/docs/keybindings.md +1 -0
- package/docs/models.md +3 -0
- package/docs/packages.md +1 -0
- package/docs/providers.md +16 -1
- package/docs/quickstart.md +1 -1
- package/docs/sdk.md +40 -50
- package/docs/usage.md +2 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/02-custom-model.ts +5 -8
- package/examples/sdk/09-api-keys-and-oauth.ts +13 -31
- package/examples/sdk/12-full-control.ts +7 -12
- package/examples/sdk/README.md +14 -18
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
- package/dist/core/provider-display-names.d.ts +0 -2
- package/dist/core/provider-display-names.d.ts.map +0 -1
- package/dist/core/provider-display-names.js +0 -36
- package/dist/core/provider-display-names.js.map +0 -1
package/docs/extensions.md
CHANGED
|
@@ -47,6 +47,7 @@ See [examples/extensions/](../examples/extensions/) for working implementations.
|
|
|
47
47
|
- [ExtensionAPI Methods](#extensionapi-methods)
|
|
48
48
|
- [State Management](#state-management)
|
|
49
49
|
- [Custom Tools](#custom-tools)
|
|
50
|
+
- [Dynamic Tool Loading](#dynamic-tool-loading)
|
|
50
51
|
- [Custom UI](#custom-ui)
|
|
51
52
|
- [Error Handling](#error-handling)
|
|
52
53
|
- [Mode Behavior](#mode-behavior)
|
|
@@ -1676,7 +1677,7 @@ Register or override a model provider dynamically. Useful for proxies, custom en
|
|
|
1676
1677
|
|
|
1677
1678
|
Calls made during the extension factory function are queued and applied once the runner initialises. Calls made after that — for example from a command handler following a user setup flow — take effect immediately without requiring a `/reload`.
|
|
1678
1679
|
|
|
1679
|
-
|
|
1680
|
+
Dynamic providers can implement `refreshModels`. Pi calls it during model refresh, publishes the returned list synchronously through the provider, and passes the canonical credential/store/network/signal context. The extension decides whether to persist the catalog through `context.store`; live servers such as llama.cpp can ignore it.
|
|
1680
1681
|
|
|
1681
1682
|
```typescript
|
|
1682
1683
|
// Register a new provider with custom models
|
|
@@ -1698,6 +1699,26 @@ pi.registerProvider("my-proxy", {
|
|
|
1698
1699
|
]
|
|
1699
1700
|
});
|
|
1700
1701
|
|
|
1702
|
+
// Register a live llama.cpp catalog without persisting discovered models
|
|
1703
|
+
pi.registerProvider("llama.cpp", {
|
|
1704
|
+
baseUrl: "http://localhost:8080/v1",
|
|
1705
|
+
apiKey: "local",
|
|
1706
|
+
api: "openai-completions",
|
|
1707
|
+
async refreshModels({ signal }) {
|
|
1708
|
+
const response = await fetch("http://localhost:8080/v1/models", { signal });
|
|
1709
|
+
const { data } = await response.json();
|
|
1710
|
+
return data.map(({ id }) => ({
|
|
1711
|
+
id,
|
|
1712
|
+
name: id,
|
|
1713
|
+
reasoning: false,
|
|
1714
|
+
input: ["text"],
|
|
1715
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
1716
|
+
contextWindow: 128000,
|
|
1717
|
+
maxTokens: 16384
|
|
1718
|
+
}));
|
|
1719
|
+
}
|
|
1720
|
+
});
|
|
1721
|
+
|
|
1701
1722
|
// Override baseUrl for an existing provider (keeps all models)
|
|
1702
1723
|
pi.registerProvider("anthropic", {
|
|
1703
1724
|
baseUrl: "https://proxy.example.com"
|
|
@@ -1735,6 +1756,7 @@ pi.registerProvider("corporate-ai", {
|
|
|
1735
1756
|
- `headers` - Custom headers to include in requests.
|
|
1736
1757
|
- `authHeader` - If true, adds `Authorization: Bearer` header automatically.
|
|
1737
1758
|
- `models` - Array of model definitions. If provided, replaces all existing models for this provider. Model definitions can set `baseUrl` to override the provider endpoint for that model.
|
|
1759
|
+
- `refreshModels` - Async dynamic discovery callback. Its returned models replace extension-provided models. Use the scoped `context.store` only when results should persist.
|
|
1738
1760
|
- `oauth` - OAuth provider config for `/login` support. When provided, the provider appears in the login menu.
|
|
1739
1761
|
- `streamSimple` - Custom streaming implementation for non-standard APIs.
|
|
1740
1762
|
|
|
@@ -2229,6 +2251,143 @@ If a slot renderer is not defined or throws:
|
|
|
2229
2251
|
- `renderCall`: Shows the tool name
|
|
2230
2252
|
- `renderResult`: Shows raw text from `content`
|
|
2231
2253
|
|
|
2254
|
+
### Dynamic Tool Loading
|
|
2255
|
+
|
|
2256
|
+
Extensions can register many tools while keeping only a small initial set active. A tool can then add more tools with `pi.setActiveTools()` during execution. Pi detects purely additive changes, records the newly available tool names on that tool result, and applies the updated active set before the next model request.
|
|
2257
|
+
|
|
2258
|
+
This works with every model. Models with native deferred-loading support preserve the stable prompt prefix and load the new definitions at the tool-result position. Other models use the fallback described below.
|
|
2259
|
+
|
|
2260
|
+
The lifecycle is:
|
|
2261
|
+
|
|
2262
|
+
1. Register every tool with `pi.registerTool()` so it appears in `pi.getAllTools()`.
|
|
2263
|
+
2. Keep loader tools, such as `search_tools`, active and leave searchable tools inactive.
|
|
2264
|
+
3. During loader execution, call `pi.setActiveTools([...currentTools, ...matchingTools])`. The change must be additive: do not remove currently active tools in the same call.
|
|
2265
|
+
4. Pi records which tools were added on the loader's tool result.
|
|
2266
|
+
5. Before the next model response, Pi exposes the added definitions using native deferred loading when supported, or the normal active tool list otherwise.
|
|
2267
|
+
|
|
2268
|
+
You do not need to return provider-specific tool references or mark the loader as a special search tool. The active-tool change is the signal. Names passed to `pi.setActiveTools()` must already be registered; unknown names are ignored.
|
|
2269
|
+
|
|
2270
|
+
#### Models with native deferred loading
|
|
2271
|
+
|
|
2272
|
+
- **Anthropic**
|
|
2273
|
+
- **Models:** Sonnet, Opus, Fable version 4.5 or newer (without Haiku)
|
|
2274
|
+
- **Native representation:** Deferred definitions use `defer_loading`; the load point uses `tool_reference` content.
|
|
2275
|
+
- **OpenAI**
|
|
2276
|
+
- **Models:** `gpt-5.4` and newer family
|
|
2277
|
+
- **Native representation:** Pi adds completed client `tool_search_call` and `tool_search_output` items at the load point.
|
|
2278
|
+
|
|
2279
|
+
For a verified custom model or proxy, native handling can be enabled with `compat.supportsToolReferences: true` for `anthropic-messages`, or `compat.supportsToolSearch: true` for `openai-responses` and `openai-codex-responses`. Leave these disabled unless the endpoint and model accept the corresponding native protocol.
|
|
2280
|
+
|
|
2281
|
+
#### Fallback behavior
|
|
2282
|
+
|
|
2283
|
+
For all other models and providers, dynamic activation still works: Pi sends the complete current active tool list normally on the next request. The model can call the newly activated tools, but adding their definitions may invalidate the provider's cached prompt prefix.
|
|
2284
|
+
|
|
2285
|
+
Pi also uses this safe fallback when the active set is not purely additive, such as replacing one group of tools with another. Tool removals therefore work, but they do not use deferred loading.
|
|
2286
|
+
|
|
2287
|
+
For the best cache behavior, keep the loader tool active for the whole session and add tools instead of replacing the active set. Also note that activating a tool with `promptSnippet` or `promptGuidelines` rebuilds the system prompt; that system-prompt change can invalidate the prefix even when the provider supports deferred schemas. Lazily loaded tools should usually rely on their tool `description` and omit active-only prompt metadata.
|
|
2288
|
+
|
|
2289
|
+
#### Search tool example
|
|
2290
|
+
|
|
2291
|
+
The following extension registers two searchable tools, removes them from the initial active set, and keeps only `search_tools` as their loader. The example uses simple keyword matching, but the search implementation could use BM25, embeddings, a remote catalog, or project-specific routing.
|
|
2292
|
+
|
|
2293
|
+
```typescript
|
|
2294
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2295
|
+
import { Type } from "typebox";
|
|
2296
|
+
|
|
2297
|
+
const SEARCHABLE_TOOL_NAMES = new Set(["lookup_weather", "search_issues"]);
|
|
2298
|
+
|
|
2299
|
+
export default function (pi: ExtensionAPI) {
|
|
2300
|
+
pi.registerTool({
|
|
2301
|
+
name: "lookup_weather",
|
|
2302
|
+
label: "Lookup Weather",
|
|
2303
|
+
description: "Look up the current weather for a city",
|
|
2304
|
+
parameters: Type.Object({ city: Type.String() }),
|
|
2305
|
+
async execute(_toolCallId, params) {
|
|
2306
|
+
return {
|
|
2307
|
+
content: [{ type: "text", text: `Weather for ${params.city}: sunny` }],
|
|
2308
|
+
details: {},
|
|
2309
|
+
};
|
|
2310
|
+
},
|
|
2311
|
+
});
|
|
2312
|
+
|
|
2313
|
+
pi.registerTool({
|
|
2314
|
+
name: "search_issues",
|
|
2315
|
+
label: "Search Issues",
|
|
2316
|
+
description: "Search project issues by keyword",
|
|
2317
|
+
parameters: Type.Object({ query: Type.String() }),
|
|
2318
|
+
async execute(_toolCallId, params) {
|
|
2319
|
+
return {
|
|
2320
|
+
content: [{ type: "text", text: `No open issues matching ${params.query}` }],
|
|
2321
|
+
details: {},
|
|
2322
|
+
};
|
|
2323
|
+
},
|
|
2324
|
+
});
|
|
2325
|
+
|
|
2326
|
+
pi.registerTool({
|
|
2327
|
+
name: "search_tools",
|
|
2328
|
+
label: "Search Tools",
|
|
2329
|
+
description: "Search for and enable tools relevant to a task",
|
|
2330
|
+
promptSnippet: "Search for additional tools when the active tools cannot perform the task",
|
|
2331
|
+
promptGuidelines: [
|
|
2332
|
+
"Use search_tools when a task requires a capability that is not currently available.",
|
|
2333
|
+
],
|
|
2334
|
+
parameters: Type.Object({
|
|
2335
|
+
query: Type.String({ description: "Capability or task to search for" }),
|
|
2336
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 10 })),
|
|
2337
|
+
}),
|
|
2338
|
+
async execute(_toolCallId, params) {
|
|
2339
|
+
const terms = params.query.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
2340
|
+
const matches = pi.getAllTools()
|
|
2341
|
+
.filter((tool) => SEARCHABLE_TOOL_NAMES.has(tool.name))
|
|
2342
|
+
.map((tool) => ({
|
|
2343
|
+
tool,
|
|
2344
|
+
score: terms.reduce(
|
|
2345
|
+
(score, term) =>
|
|
2346
|
+
score + (`${tool.name} ${tool.description}`.toLowerCase().includes(term) ? 1 : 0),
|
|
2347
|
+
0,
|
|
2348
|
+
),
|
|
2349
|
+
}))
|
|
2350
|
+
.filter((match) => match.score > 0)
|
|
2351
|
+
.sort((a, b) => b.score - a.score)
|
|
2352
|
+
.slice(0, params.limit ?? 3)
|
|
2353
|
+
.map((match) => match.tool.name);
|
|
2354
|
+
|
|
2355
|
+
if (matches.length === 0) {
|
|
2356
|
+
return {
|
|
2357
|
+
content: [{ type: "text", text: `No tools found for: ${params.query}` }],
|
|
2358
|
+
details: { matches: [] },
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
const active = pi.getActiveTools();
|
|
2363
|
+
const added = matches.filter((name) => !active.includes(name));
|
|
2364
|
+
pi.setActiveTools([...new Set([...active, ...added])]);
|
|
2365
|
+
|
|
2366
|
+
return {
|
|
2367
|
+
content: [{
|
|
2368
|
+
type: "text",
|
|
2369
|
+
text: added.length > 0
|
|
2370
|
+
? `Loaded tools: ${added.join(", ")}`
|
|
2371
|
+
: `Matching tools already active: ${matches.join(", ")}`,
|
|
2372
|
+
}],
|
|
2373
|
+
details: { matches, added },
|
|
2374
|
+
};
|
|
2375
|
+
},
|
|
2376
|
+
});
|
|
2377
|
+
|
|
2378
|
+
pi.on("session_start", () => {
|
|
2379
|
+
// Keep searchable tools registered but initially inactive. Preserve built-ins
|
|
2380
|
+
// and tools owned by other extensions, and keep the loader itself active.
|
|
2381
|
+
const initialTools = pi.getActiveTools().filter(
|
|
2382
|
+
(name) => !SEARCHABLE_TOOL_NAMES.has(name),
|
|
2383
|
+
);
|
|
2384
|
+
pi.setActiveTools([...new Set([...initialTools, "search_tools"])]);
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2387
|
+
```
|
|
2388
|
+
|
|
2389
|
+
When `search_tools` adds a match, the model receives that definition on the immediately following request. On a native-capable model the definition is anchored after the search result without changing the initial tool-schema prefix. On other models it appears in the normal tool list on that same following request.
|
|
2390
|
+
|
|
2232
2391
|
## Custom UI
|
|
2233
2392
|
|
|
2234
2393
|
Extensions can interact with users via `ctx.ui` methods and customize how messages/tools render.
|
package/docs/keybindings.md
CHANGED
|
@@ -119,6 +119,7 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1
|
|
|
119
119
|
| Keybinding id | Default | Description |
|
|
120
120
|
|--------|---------|-------------|
|
|
121
121
|
| `app.tools.expand` | `ctrl+o` | Collapse or expand tool output |
|
|
122
|
+
| `app.message.copy` | `ctrl+x` | Copy the last assistant message, or the selected message in `/tree` |
|
|
122
123
|
| `app.message.followUp` | `alt+enter` | Queue follow-up message |
|
|
123
124
|
| `app.message.dequeue` | `alt+up` | Restore queued messages to editor |
|
|
124
125
|
|
package/docs/models.md
CHANGED
|
@@ -136,6 +136,7 @@ Set `api` at provider level (default for all models) or model level (override pe
|
|
|
136
136
|
| `baseUrl` | API endpoint URL |
|
|
137
137
|
| `api` | API type (see above) |
|
|
138
138
|
| `apiKey` | Optional API key config (see value resolution below). Omit it when auth is provided by `/login`/`auth.json` or CLI `--api-key`. |
|
|
139
|
+
| `oauth` | Dynamic OAuth provider type. Currently supports `"radius"`; requires the gateway `baseUrl`. |
|
|
139
140
|
| `headers` | Custom headers (see value resolution below) |
|
|
140
141
|
| `authHeader` | Set `true` to add `Authorization: Bearer <apiKey>` automatically |
|
|
141
142
|
| `models` | Array of model configurations |
|
|
@@ -445,6 +446,8 @@ For providers with partial OpenAI compatibility, use the `compat` field.
|
|
|
445
446
|
| `thinkingFormat` | Use `reasoning_effort`, `openrouter`, `deepseek`, `together`, `zai`, `qwen`, `chat-template`, or `qwen-chat-template` thinking parameters |
|
|
446
447
|
| `chatTemplateKwargs` | `chat_template_kwargs` values for `thinkingFormat: "chat-template"`; use `{ "$var": "thinking.enabled" }` or `{ "$var": "thinking.effort" }` for pi-controlled thinking values |
|
|
447
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. |
|
|
449
|
+
| `sendSessionAffinityHeaders` | For `openai-completions`, send session-affinity headers from the session id when caching is enabled. Default: `false`. |
|
|
450
|
+
| `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. |
|
|
448
451
|
| `supportsStrictMode` | Include the `strict` field in tool definitions |
|
|
449
452
|
| `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`. |
|
|
450
453
|
| `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/packages.md
CHANGED
|
@@ -31,6 +31,7 @@ pi list # show installed packages from settings
|
|
|
31
31
|
pi update # update pi only
|
|
32
32
|
pi update --all # update pi, update packages, and reconcile pinned git refs
|
|
33
33
|
pi update --extensions # update packages and reconcile pinned git refs only
|
|
34
|
+
pi update --models # refresh model catalogs only
|
|
34
35
|
pi update --self # update pi only
|
|
35
36
|
pi update --self --force # reinstall pi even if current
|
|
36
37
|
pi update npm:@foo/bar # update one package
|
package/docs/providers.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Providers
|
|
2
2
|
|
|
3
|
-
Pi supports subscription-based providers via OAuth and API key providers via environment variables or auth file.
|
|
3
|
+
Pi supports subscription-based providers via OAuth and API key providers via environment variables or auth file. Built-in catalogs ship with pi; configured providers may refresh newer catalogs and cache them in `~/.pi/agent/models-store.json` for offline use.
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
@@ -18,6 +18,8 @@ Use `/login` in interactive mode, then select a provider:
|
|
|
18
18
|
- ChatGPT Plus/Pro (Codex)
|
|
19
19
|
- Claude Pro/Max
|
|
20
20
|
- GitHub Copilot
|
|
21
|
+
- xAI (Grok/X subscription)
|
|
22
|
+
- Radius
|
|
21
23
|
|
|
22
24
|
Use `/logout` to clear credentials. Tokens are stored in `~/.pi/agent/auth.json` and auto-refresh when expired.
|
|
23
25
|
|
|
@@ -35,6 +37,15 @@ Anthropic subscription auth is active for Claude Pro/Max accounts. Third-party h
|
|
|
35
37
|
- Press Enter for github.com, or enter your GitHub Enterprise Server domain
|
|
36
38
|
- If you get "model not supported", enable it in VS Code: Copilot Chat → model selector → select model → "Enable"
|
|
37
39
|
|
|
40
|
+
### xAI (Grok/X subscription)
|
|
41
|
+
|
|
42
|
+
- Run `/login xai`, then select **Use a subscription**
|
|
43
|
+
- `XAI_API_KEY` remains available through **Use an API key**
|
|
44
|
+
|
|
45
|
+
### Radius
|
|
46
|
+
|
|
47
|
+
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`.
|
|
48
|
+
|
|
38
49
|
## API Keys
|
|
39
50
|
|
|
40
51
|
### Environment Variables or Auth File
|
|
@@ -55,6 +66,7 @@ pi
|
|
|
55
66
|
| DeepSeek | `DEEPSEEK_API_KEY` | `deepseek` |
|
|
56
67
|
| NVIDIA NIM | `NVIDIA_API_KEY` | `nvidia` |
|
|
57
68
|
| Google Gemini | `GEMINI_API_KEY` | `google` |
|
|
69
|
+
| Amazon Bedrock | `AWS_BEARER_TOKEN_BEDROCK` | `amazon-bedrock` |
|
|
58
70
|
| Mistral | `MISTRAL_API_KEY` | `mistral` |
|
|
59
71
|
| Groq | `GROQ_API_KEY` | `groq` |
|
|
60
72
|
| Cerebras | `CEREBRAS_API_KEY` | `cerebras` |
|
|
@@ -67,6 +79,7 @@ pi
|
|
|
67
79
|
| ZAI Coding Plan (China) | `ZAI_CODING_CN_API_KEY` | `zai-coding-cn` |
|
|
68
80
|
| OpenCode Zen | `OPENCODE_API_KEY` | `opencode` |
|
|
69
81
|
| OpenCode Go | `OPENCODE_API_KEY` | `opencode-go` |
|
|
82
|
+
| Radius | `RADIUS_API_KEY` | `radius` |
|
|
70
83
|
| Hugging Face | `HF_TOKEN` | `huggingface` |
|
|
71
84
|
| Fireworks | `FIREWORKS_API_KEY` | `fireworks` |
|
|
72
85
|
| Together AI | `TOGETHER_API_KEY` | `together` |
|
|
@@ -170,6 +183,8 @@ export AZURE_OPENAI_DEPLOYMENT_NAME_MAP=gpt-4=my-gpt4,gpt-4o=my-gpt4o
|
|
|
170
183
|
|
|
171
184
|
### Amazon Bedrock
|
|
172
185
|
|
|
186
|
+
Use `/login amazon-bedrock` to store a Bedrock API key, or configure one of the ambient AWS credential sources below:
|
|
187
|
+
|
|
173
188
|
```bash
|
|
174
189
|
# Option 1: AWS Profile
|
|
175
190
|
export AWS_PROFILE=your-profile
|
package/docs/quickstart.md
CHANGED
|
@@ -113,7 +113,7 @@ pi @README.md "Summarize this"
|
|
|
113
113
|
pi @src/app.ts @src/app.test.ts "Review these together"
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
Images can be pasted with Ctrl+V (Alt+V on Windows)
|
|
116
|
+
Images or text can be pasted with Ctrl+V (Alt+V on Windows); images can also be dragged into supported terminals.
|
|
117
117
|
|
|
118
118
|
### Run shell commands
|
|
119
119
|
|
package/docs/sdk.md
CHANGED
|
@@ -16,16 +16,12 @@ See [examples/sdk/](../examples/sdk/) for working examples from minimal to full
|
|
|
16
16
|
## Quick Start
|
|
17
17
|
|
|
18
18
|
```typescript
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
// Set up credential storage and model registry
|
|
22
|
-
const authStorage = AuthStorage.create();
|
|
23
|
-
const modelRegistry = ModelRegistry.create(authStorage);
|
|
19
|
+
import { createAgentSession, ModelRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
24
20
|
|
|
21
|
+
const modelRuntime = await ModelRuntime.create();
|
|
25
22
|
const { session } = await createAgentSession({
|
|
26
23
|
sessionManager: SessionManager.inMemory(),
|
|
27
|
-
|
|
28
|
-
modelRegistry,
|
|
24
|
+
modelRuntime,
|
|
29
25
|
});
|
|
30
26
|
|
|
31
27
|
session.subscribe((event) => {
|
|
@@ -369,10 +365,9 @@ When you pass a custom `ResourceLoader`, `cwd` and `agentDir` no longer control
|
|
|
369
365
|
|
|
370
366
|
```typescript
|
|
371
367
|
import { getModel } from "@earendil-works/pi-ai";
|
|
372
|
-
import {
|
|
368
|
+
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
373
369
|
|
|
374
|
-
const
|
|
375
|
-
const modelRegistry = ModelRegistry.create(authStorage);
|
|
370
|
+
const modelRuntime = await ModelRuntime.create();
|
|
376
371
|
|
|
377
372
|
// Find specific built-in model (doesn't check if API key exists)
|
|
378
373
|
const opus = getModel("anthropic", "claude-opus-4-5");
|
|
@@ -380,10 +375,10 @@ if (!opus) throw new Error("Model not found");
|
|
|
380
375
|
|
|
381
376
|
// Find any model by provider/id, including custom models from models.json
|
|
382
377
|
// (doesn't check if API key exists)
|
|
383
|
-
const customModel =
|
|
378
|
+
const customModel = modelRuntime.getModel("my-provider", "my-model");
|
|
384
379
|
|
|
385
|
-
// Get only models that have valid
|
|
386
|
-
const available = await
|
|
380
|
+
// Get only models that have valid authentication configured
|
|
381
|
+
const available = await modelRuntime.getAvailable();
|
|
387
382
|
|
|
388
383
|
const { session } = await createAgentSession({
|
|
389
384
|
model: opus,
|
|
@@ -395,8 +390,7 @@ const { session } = await createAgentSession({
|
|
|
395
390
|
{ model: haiku, thinkingLevel: "off" },
|
|
396
391
|
],
|
|
397
392
|
|
|
398
|
-
|
|
399
|
-
modelRegistry,
|
|
393
|
+
modelRuntime,
|
|
400
394
|
});
|
|
401
395
|
```
|
|
402
396
|
|
|
@@ -415,14 +409,14 @@ import {
|
|
|
415
409
|
|
|
416
410
|
const cliModel = resolveCliModel({
|
|
417
411
|
cliModel: "anthropic/claude-opus-4-5:high",
|
|
418
|
-
|
|
412
|
+
modelRuntime,
|
|
419
413
|
});
|
|
420
414
|
if (cliModel.error) throw new Error(cliModel.error);
|
|
421
415
|
if (cliModel.warning) console.warn(cliModel.warning);
|
|
422
416
|
|
|
423
417
|
const { scopedModels, diagnostics } = await resolveModelScopeWithDiagnostics(
|
|
424
418
|
["anthropic/*:high", "gpt-5"],
|
|
425
|
-
|
|
419
|
+
modelRuntime,
|
|
426
420
|
);
|
|
427
421
|
for (const diagnostic of diagnostics) {
|
|
428
422
|
console.warn(diagnostic.message);
|
|
@@ -435,40 +429,41 @@ for (const diagnostic of diagnostics) {
|
|
|
435
429
|
|
|
436
430
|
### API Keys and OAuth
|
|
437
431
|
|
|
438
|
-
|
|
432
|
+
Authentication resolution priority (handled by `ModelRuntime`):
|
|
439
433
|
1. Runtime overrides (via `setRuntimeApiKey`, not persisted)
|
|
440
434
|
2. Stored credentials in `auth.json` (API keys or OAuth tokens)
|
|
441
435
|
3. Environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.)
|
|
442
436
|
4. Fallback resolver (for custom provider keys from `models.json`)
|
|
443
437
|
|
|
444
438
|
```typescript
|
|
445
|
-
import {
|
|
439
|
+
import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
|
|
440
|
+
import { createAgentSession, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
446
441
|
|
|
447
442
|
// Default: uses ~/.pi/agent/auth.json and ~/.pi/agent/models.json
|
|
448
|
-
const
|
|
449
|
-
const modelRegistry = ModelRegistry.create(authStorage);
|
|
443
|
+
const modelRuntime = await ModelRuntime.create();
|
|
450
444
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
445
|
+
// Provider-owned auth methods and current status
|
|
446
|
+
for (const provider of modelRuntime.getProviders()) {
|
|
447
|
+
const status = await modelRuntime.checkAuth(provider.id);
|
|
448
|
+
console.log(provider.name, provider.auth, status);
|
|
449
|
+
}
|
|
456
450
|
|
|
457
451
|
// Runtime API key override (not persisted to disk)
|
|
458
|
-
|
|
452
|
+
modelRuntime.setRuntimeApiKey("anthropic", "sk-my-temp-key");
|
|
453
|
+
|
|
454
|
+
// Custom credential and model locations
|
|
455
|
+
const customRuntime = await ModelRuntime.create({
|
|
456
|
+
authPath: "/my/app/auth.json",
|
|
457
|
+
modelsPath: "/my/app/models.json",
|
|
458
|
+
});
|
|
459
459
|
|
|
460
|
-
//
|
|
461
|
-
const
|
|
462
|
-
const
|
|
460
|
+
// Or inject any pi-ai CredentialStore
|
|
461
|
+
const credentials = new InMemoryCredentialStore();
|
|
462
|
+
const inMemoryRuntime = await ModelRuntime.create({ credentials });
|
|
463
463
|
|
|
464
464
|
const { session } = await createAgentSession({
|
|
465
|
-
|
|
466
|
-
authStorage: customAuth,
|
|
467
|
-
modelRegistry: customRegistry,
|
|
465
|
+
modelRuntime: customRuntime,
|
|
468
466
|
});
|
|
469
|
-
|
|
470
|
-
// No custom models.json (built-in models only)
|
|
471
|
-
const simpleRegistry = ModelRegistry.inMemory(authStorage);
|
|
472
467
|
```
|
|
473
468
|
|
|
474
469
|
> See [examples/sdk/09-api-keys-and-oauth.ts](../examples/sdk/09-api-keys-and-oauth.ts)
|
|
@@ -927,26 +922,22 @@ interface LoadExtensionsResult {
|
|
|
927
922
|
import { getModel } from "@earendil-works/pi-ai";
|
|
928
923
|
import { Type } from "typebox";
|
|
929
924
|
import {
|
|
930
|
-
AuthStorage,
|
|
931
925
|
createAgentSession,
|
|
932
926
|
DefaultResourceLoader,
|
|
933
927
|
defineTool,
|
|
934
|
-
|
|
928
|
+
ModelRuntime,
|
|
935
929
|
SessionManager,
|
|
936
930
|
SettingsManager,
|
|
937
931
|
} from "@earendil-works/pi-coding-agent";
|
|
938
932
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
933
|
+
const modelRuntime = await ModelRuntime.create({
|
|
934
|
+
authPath: "/custom/agent/auth.json",
|
|
935
|
+
modelsPath: "/custom/agent/models.json",
|
|
936
|
+
});
|
|
943
937
|
if (process.env.MY_KEY) {
|
|
944
|
-
|
|
938
|
+
modelRuntime.setRuntimeApiKey("anthropic", process.env.MY_KEY);
|
|
945
939
|
}
|
|
946
940
|
|
|
947
|
-
// Model registry (no custom models.json)
|
|
948
|
-
const modelRegistry = ModelRegistry.create(authStorage);
|
|
949
|
-
|
|
950
941
|
// Inline tool
|
|
951
942
|
const statusTool = defineTool({
|
|
952
943
|
name: "status",
|
|
@@ -982,8 +973,7 @@ const { session } = await createAgentSession({
|
|
|
982
973
|
|
|
983
974
|
model,
|
|
984
975
|
thinkingLevel: "off",
|
|
985
|
-
|
|
986
|
-
modelRegistry,
|
|
976
|
+
modelRuntime,
|
|
987
977
|
|
|
988
978
|
tools: ["read", "bash", "status"],
|
|
989
979
|
customTools: [statusTool],
|
|
@@ -1149,8 +1139,8 @@ createAgentSessionRuntime
|
|
|
1149
1139
|
AgentSessionRuntime
|
|
1150
1140
|
|
|
1151
1141
|
// Auth and Models
|
|
1152
|
-
|
|
1153
|
-
ModelRegistry
|
|
1142
|
+
ModelRuntime // implements pi-ai Models and owns credential storage
|
|
1143
|
+
ModelRegistry // synchronous extension compatibility facade
|
|
1154
1144
|
resolveCliModel
|
|
1155
1145
|
resolveModelScopeWithDiagnostics
|
|
1156
1146
|
|
package/docs/usage.md
CHANGED
|
@@ -22,6 +22,7 @@ The editor can be replaced temporarily by built-in UI such as `/settings` or by
|
|
|
22
22
|
| File reference | Type `@` to fuzzy-search project files |
|
|
23
23
|
| Path completion | Press Tab to complete paths |
|
|
24
24
|
| Multi-line input | Shift+Enter, or Ctrl+Enter on Windows Terminal |
|
|
25
|
+
| Copy response | Ctrl+X copies the last assistant message; in `/tree`, it copies the selected message |
|
|
25
26
|
| Images | Paste with Ctrl+V, Alt+V on Windows, or drag into the terminal |
|
|
26
27
|
| Shell command | `!command` runs and sends output to the model |
|
|
27
28
|
| Hidden shell command | `!!command` runs without sending output to the model |
|
|
@@ -150,6 +151,7 @@ pi uninstall <source> [-l] # Alias for remove
|
|
|
150
151
|
pi update [source|self|pi] # Update pi only, or one package source
|
|
151
152
|
pi update --all # Update pi and packages; reconcile pinned git refs
|
|
152
153
|
pi update --extensions # Update packages only; reconcile pinned git refs
|
|
154
|
+
pi update --models # Refresh model catalogs only
|
|
153
155
|
pi update --self # Update pi only
|
|
154
156
|
pi update --extension <src> # Update one package
|
|
155
157
|
pi list # List installed packages
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
47
47
|
|
|
48
48
|
// =============================================================================
|
|
49
|
-
// OAuth
|
|
49
|
+
// OAuth implementation adapted for the legacy extension compatibility interface.
|
|
50
50
|
// =============================================================================
|
|
51
51
|
|
|
52
52
|
const decode = (s: string) => atob(s);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-custom-provider",
|
|
3
|
-
"version": "0.80.
|
|
3
|
+
"version": "0.80.8",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-custom-provider",
|
|
9
|
-
"version": "0.80.
|
|
9
|
+
"version": "0.80.8",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.52.0"
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-gondolin",
|
|
3
|
-
"version": "0.80.
|
|
3
|
+
"version": "0.80.8",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-gondolin",
|
|
9
|
-
"version": "0.80.
|
|
9
|
+
"version": "0.80.8",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@earendil-works/gondolin": "0.12.0"
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-sandbox",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.8",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-sandbox",
|
|
9
|
-
"version": "1.10.
|
|
9
|
+
"version": "1.10.8",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sandbox-runtime": "^0.0.26"
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-with-deps",
|
|
3
|
-
"version": "0.80.
|
|
3
|
+
"version": "0.80.8",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-with-deps",
|
|
9
|
-
"version": "0.80.
|
|
9
|
+
"version": "0.80.8",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"ms": "^2.1.3"
|
|
12
12
|
},
|