@eminent337/aery 0.1.114 → 0.1.116
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 +4172 -16
- package/README.md +621 -34
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +2 -1
- package/dist/bun/cli.js.map +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +3 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -3
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +11 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +66 -46
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +5 -5
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +37 -36
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -2
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +3 -3
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/custom-openai-compatible.d.ts +1 -0
- package/dist/core/custom-openai-compatible.d.ts.map +1 -1
- package/dist/core/custom-openai-compatible.js +30 -4
- package/dist/core/custom-openai-compatible.js.map +1 -1
- package/dist/core/export-html/template.css +45 -1
- package/dist/core/export-html/template.js +68 -4
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -2
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +40 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +17 -3
- 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 +6 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +59 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +9 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/provider-display-names.d.ts +2 -0
- package/dist/core/provider-display-names.d.ts.map +1 -0
- package/dist/core/provider-display-names.js +35 -0
- package/dist/core/provider-display-names.js.map +1 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +1 -1
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +18 -10
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +3 -3
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +2 -2
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +5 -5
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +2 -2
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +105 -125
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -1
- 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 +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +50 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -0
- package/dist/core/tools/output-accumulator.js +178 -0
- package/dist/core/tools/output-accumulator.js.map +1 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +70 -13
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +2 -2
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +1 -1
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +23 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.js +2 -2
- package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +2 -0
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +6 -1
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +5 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +19 -5
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +1 -3
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +9 -17
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +24 -27
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +4 -2
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +2 -1
- package/dist/modes/interactive/components/tree-selector.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 +10 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +1 -1
- package/dist/modes/interactive/theme/light.json +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +8 -10
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +2 -2
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +2 -2
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +4 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/ansi.d.ts +2 -0
- package/dist/utils/ansi.d.ts.map +1 -0
- package/dist/utils/ansi.js +52 -0
- package/dist/utils/ansi.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +3 -3
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +9 -2
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/html.d.ts +7 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +40 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/mime.d.ts +1 -0
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js +59 -16
- package/dist/utils/mime.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +16 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/pi-user-agent.d.ts +2 -0
- package/dist/utils/pi-user-agent.d.ts.map +1 -0
- package/dist/utils/pi-user-agent.js +5 -0
- package/dist/utils/pi-user-agent.js.map +1 -0
- package/dist/utils/syntax-highlight.d.ts +12 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -0
- package/dist/utils/syntax-highlight.js +118 -0
- package/dist/utils/syntax-highlight.js.map +1 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +76 -7
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/uuid.d.ts +2 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +40 -0
- package/dist/utils/uuid.js.map +1 -0
- package/dist/utils/version-check.d.ts +7 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +12 -5
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +16 -16
- package/docs/custom-provider.md +40 -32
- package/docs/development.md +4 -4
- package/docs/docs.json +20 -5
- package/docs/extensions.md +152 -102
- package/docs/index.md +16 -7
- package/docs/json.md +7 -7
- package/docs/keybindings.md +3 -3
- package/docs/models.md +48 -8
- package/docs/packages.md +41 -36
- package/docs/prompt-templates.md +2 -2
- package/docs/providers.md +52 -36
- package/docs/quickstart.md +20 -20
- package/docs/rpc.md +9 -9
- package/docs/sdk.md +31 -53
- package/docs/session-format.md +10 -10
- package/docs/sessions.md +9 -9
- package/docs/settings.md +12 -6
- package/docs/skills.md +4 -4
- package/docs/terminal-setup.md +6 -6
- package/docs/termux.md +6 -6
- package/docs/themes.md +7 -7
- package/docs/tmux.md +1 -1
- package/docs/tui.md +8 -8
- package/docs/usage.md +41 -39
- package/examples/extensions/README.md +3 -5
- package/examples/extensions/antigravity-image-gen.ts +9 -9
- package/examples/extensions/auto-commit-on-exit.ts +1 -1
- package/examples/extensions/bash-spawn-hook.ts +2 -2
- package/examples/extensions/built-in-tool-renderer.ts +1 -1
- package/examples/extensions/custom-compaction.ts +1 -1
- package/examples/extensions/custom-header.ts +2 -2
- package/examples/extensions/custom-provider-anthropic/index.ts +2 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +4 -4
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/doom-overlay/README.md +2 -2
- package/examples/extensions/doom-overlay/doom/build.sh +2 -2
- package/examples/extensions/doom-overlay/index.ts +1 -1
- package/examples/extensions/dynamic-resources/dynamic.json +1 -1
- package/examples/extensions/handoff.ts +42 -5
- package/examples/extensions/hidden-thinking-label.ts +1 -1
- package/examples/extensions/inline-bash.ts +2 -2
- package/examples/extensions/input-transform.ts +3 -3
- package/examples/extensions/interactive-shell.ts +1 -1
- package/examples/extensions/mac-system-theme.ts +2 -2
- package/examples/extensions/minimal-mode.ts +1 -1
- package/examples/extensions/modal-editor.ts +1 -1
- package/examples/extensions/model-status.ts +1 -1
- package/examples/extensions/overlay-qa-tests.ts +6 -6
- package/examples/extensions/overlay-test.ts +1 -1
- package/examples/extensions/preset.ts +2 -2
- package/examples/extensions/provider-payload.ts +1 -1
- package/examples/extensions/rainbow-editor.ts +1 -1
- package/examples/extensions/rpc-demo.ts +1 -1
- package/examples/extensions/sandbox/index.ts +3 -3
- package/examples/extensions/sandbox/package-lock.json +7 -7
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/shutdown-command.ts +5 -5
- package/examples/extensions/ssh.ts +2 -2
- package/examples/extensions/subagent/README.md +2 -2
- package/examples/extensions/subagent/agents/aery-pods.md +1 -1
- package/examples/extensions/subagent/agents.ts +1 -1
- package/examples/extensions/subagent/index.ts +2 -2
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/tool-override.ts +2 -2
- package/examples/extensions/truncated-tool.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +4 -4
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/extensions/working-indicator.ts +4 -4
- package/examples/extensions/working-message-test.ts +1 -1
- package/examples/sdk/01-minimal.ts +14 -10
- package/examples/sdk/02-custom-model.ts +12 -8
- package/examples/sdk/03-custom-prompt.ts +24 -16
- package/examples/sdk/04-skills.ts +2 -2
- package/examples/sdk/05-tools.ts +8 -4
- package/examples/sdk/06-extensions.ts +11 -7
- package/examples/sdk/07-context-files.ts +2 -2
- package/examples/sdk/08-prompt-templates.ts +2 -2
- package/examples/sdk/09-api-keys-and-oauth.ts +8 -4
- package/examples/sdk/10-settings.ts +4 -4
- package/examples/sdk/11-sessions.ts +4 -0
- package/examples/sdk/12-full-control.ts +11 -7
- package/examples/sdk/README.md +5 -8
- package/package.json +8 -14
package/docs/extensions.md
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
>
|
|
1
|
+
> pi can create extensions. Ask it to build one for your use case.
|
|
2
2
|
|
|
3
3
|
# Extensions
|
|
4
4
|
|
|
5
|
-
Extensions are TypeScript modules that extend
|
|
5
|
+
Extensions are TypeScript modules that extend pi's behavior. They can subscribe to lifecycle events, register custom tools callable by the LLM, add commands, and more.
|
|
6
6
|
|
|
7
|
-
>
|
|
8
|
-
|
|
9
|
-
> **Placement for /reload:** Put extensions in `~/.aery/agent/extensions/` (global) or `.aery/extensions/` (project-local) for auto-discovery. Use `aery -e ./path.ts` only for quick tests. Extensions in auto-discovered locations can be hot-reloaded with `/reload`.
|
|
7
|
+
> **Placement for /reload:** Put extensions in `~/.aery/agent/extensions/` (global) or `.aery/extensions/` (project-local) for auto-discovery. Use `pi -e ./path.ts` only for quick tests. Extensions in auto-discovered locations can be hot-reloaded with `/reload`.
|
|
10
8
|
|
|
11
9
|
**Key capabilities:**
|
|
12
10
|
- **Custom tools** - Register tools the LLM can call via `pi.registerTool()`
|
|
@@ -14,7 +12,7 @@ Extensions are TypeScript modules that extend the agent's behavior. They can sub
|
|
|
14
12
|
- **User interaction** - Prompt users via `ctx.ui` (select, confirm, input, notify)
|
|
15
13
|
- **Custom UI components** - Full TUI components with keyboard input via `ctx.ui.custom()` for complex interactions
|
|
16
14
|
- **Custom commands** - Register commands like `/mycommand` via `pi.registerCommand()`
|
|
17
|
-
- **Session persistence** - Store state that survives restarts via `
|
|
15
|
+
- **Session persistence** - Store state that survives restarts via `pi.appendEntry()`
|
|
18
16
|
- **Custom rendering** - Control how tool calls/results and messages appear in TUI
|
|
19
17
|
|
|
20
18
|
**Example use cases:**
|
|
@@ -42,6 +40,7 @@ See [examples/extensions/](../examples/extensions/) for working implementations.
|
|
|
42
40
|
- [Resource Events](#resource-events)
|
|
43
41
|
- [Session Events](#session-events)
|
|
44
42
|
- [Agent Events](#agent-events)
|
|
43
|
+
- [Model Events](#model-events)
|
|
45
44
|
- [Tool Events](#tool-events)
|
|
46
45
|
- [ExtensionContext](#extensioncontext)
|
|
47
46
|
- [ExtensionCommandContext](#extensioncommandcontext)
|
|
@@ -59,9 +58,9 @@ Create `~/.aery/agent/extensions/my-extension.ts`:
|
|
|
59
58
|
|
|
60
59
|
```typescript
|
|
61
60
|
import type { ExtensionAPI } from "@eminent337/aery";
|
|
62
|
-
import { Type } from "
|
|
61
|
+
import { Type } from "typebox";
|
|
63
62
|
|
|
64
|
-
export default function (
|
|
63
|
+
export default function (pi: ExtensionAPI) {
|
|
65
64
|
// React to events
|
|
66
65
|
pi.on("session_start", async (_event, ctx) => {
|
|
67
66
|
ctx.ui.notify("Extension loaded!", "info");
|
|
@@ -103,7 +102,7 @@ export default function (aery: ExtensionAPI) {
|
|
|
103
102
|
Test with `--extension` (or `-e`) flag:
|
|
104
103
|
|
|
105
104
|
```bash
|
|
106
|
-
|
|
105
|
+
pi -e ./my-extension.ts
|
|
107
106
|
```
|
|
108
107
|
|
|
109
108
|
## Extension Locations
|
|
@@ -134,20 +133,20 @@ Additional paths via `settings.json`:
|
|
|
134
133
|
}
|
|
135
134
|
```
|
|
136
135
|
|
|
137
|
-
To share extensions via npm or git as
|
|
136
|
+
To share extensions via npm or git as pi packages, see [packages.md](packages.md).
|
|
138
137
|
|
|
139
138
|
## Available Imports
|
|
140
139
|
|
|
141
140
|
| Package | Purpose |
|
|
142
141
|
|---------|---------|
|
|
143
142
|
| `@eminent337/aery` | Extension types (`ExtensionAPI`, `ExtensionContext`, events) |
|
|
144
|
-
|
|
|
143
|
+
| `typebox` | Schema definitions for tool parameters |
|
|
145
144
|
| `@eminent337/aery-ai` | AI utilities (`StringEnum` for Google-compatible enums) |
|
|
146
145
|
| `@eminent337/aery-tui` | TUI components for custom rendering |
|
|
147
146
|
|
|
148
147
|
npm dependencies work too. Add a `package.json` next to your extension (or in a parent directory), run `npm install`, and imports from `node_modules/` are resolved automatically.
|
|
149
148
|
|
|
150
|
-
For distributed
|
|
149
|
+
For distributed pi packages installed with `pi install` (npm or git), runtime deps must be in `dependencies`. Package installation uses production installs (`npm install --omit=dev`) by default, so `devDependencies` are not available at runtime; when `npmCommand` is configured, git packages use plain `install` for compatibility with wrappers.
|
|
151
150
|
|
|
152
151
|
Node.js built-ins (`node:fs`, `node:path`, etc.) are also available.
|
|
153
152
|
|
|
@@ -158,7 +157,7 @@ An extension exports a default factory function that receives `ExtensionAPI`. Th
|
|
|
158
157
|
```typescript
|
|
159
158
|
import type { ExtensionAPI } from "@eminent337/aery";
|
|
160
159
|
|
|
161
|
-
export default function (
|
|
160
|
+
export default function (pi: ExtensionAPI) {
|
|
162
161
|
// Subscribe to events
|
|
163
162
|
pi.on("event_name", async (event, ctx) => {
|
|
164
163
|
// ctx.ui for user interaction
|
|
@@ -171,14 +170,14 @@ export default function (aery: ExtensionAPI) {
|
|
|
171
170
|
// Register tools, commands, shortcuts, flags
|
|
172
171
|
pi.registerTool({ ... });
|
|
173
172
|
pi.registerCommand("name", { ... });
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
pi.registerShortcut("ctrl+x", { ... });
|
|
174
|
+
pi.registerFlag("my-flag", { ... });
|
|
176
175
|
}
|
|
177
176
|
```
|
|
178
177
|
|
|
179
178
|
Extensions are loaded via [jiti](https://github.com/unjs/jiti), so TypeScript works without compilation.
|
|
180
179
|
|
|
181
|
-
If the factory returns a `Promise`,
|
|
180
|
+
If the factory returns a `Promise`, pi awaits it before continuing startup. That means async initialization completes before `session_start`, before `resources_discover`, and before provider registrations queued via `pi.registerProvider()` are flushed.
|
|
182
181
|
|
|
183
182
|
### Async factory functions
|
|
184
183
|
|
|
@@ -187,7 +186,7 @@ Use an async factory for one-time startup work such as fetching remote configura
|
|
|
187
186
|
```typescript
|
|
188
187
|
import type { ExtensionAPI } from "@eminent337/aery";
|
|
189
188
|
|
|
190
|
-
export default async function (
|
|
189
|
+
export default async function (pi: ExtensionAPI) {
|
|
191
190
|
const response = await fetch("http://localhost:1234/v1/models");
|
|
192
191
|
const payload = (await response.json()) as {
|
|
193
192
|
data: Array<{
|
|
@@ -215,7 +214,7 @@ export default async function (aery: ExtensionAPI) {
|
|
|
215
214
|
}
|
|
216
215
|
```
|
|
217
216
|
|
|
218
|
-
This pattern makes the fetched models available during normal startup and to `
|
|
217
|
+
This pattern makes the fetched models available during normal startup and to `pi --list-models`.
|
|
219
218
|
|
|
220
219
|
### Extension Styles
|
|
221
220
|
|
|
@@ -256,7 +255,7 @@ This pattern makes the fetched models available during normal startup and to `ae
|
|
|
256
255
|
"zod": "^3.0.0",
|
|
257
256
|
"chalk": "^5.0.0"
|
|
258
257
|
},
|
|
259
|
-
"
|
|
258
|
+
"pi": {
|
|
260
259
|
"extensions": ["./src/index.ts"]
|
|
261
260
|
}
|
|
262
261
|
}
|
|
@@ -269,7 +268,7 @@ Run `npm install` in the extension directory, then imports from `node_modules/`
|
|
|
269
268
|
### Lifecycle Overview
|
|
270
269
|
|
|
271
270
|
```
|
|
272
|
-
|
|
271
|
+
pi starts
|
|
273
272
|
│
|
|
274
273
|
├─► session_start { reason: "startup" }
|
|
275
274
|
└─► resources_discover { reason: "startup" }
|
|
@@ -325,8 +324,12 @@ user sends another prompt ◄─────────────────
|
|
|
325
324
|
└─► session_tree
|
|
326
325
|
|
|
327
326
|
/model or Ctrl+P (model selection/cycling)
|
|
327
|
+
├─► thinking_level_select (if model change changes/clamps thinking level)
|
|
328
328
|
└─► model_select
|
|
329
329
|
|
|
330
|
+
thinking level changes (settings, keybinding, pi.setThinkingLevel())
|
|
331
|
+
└─► thinking_level_select
|
|
332
|
+
|
|
330
333
|
exit (Ctrl+C, Ctrl+D, SIGHUP, SIGTERM)
|
|
331
334
|
└─► session_shutdown
|
|
332
335
|
```
|
|
@@ -382,7 +385,7 @@ pi.on("session_before_switch", async (event, ctx) => {
|
|
|
382
385
|
});
|
|
383
386
|
```
|
|
384
387
|
|
|
385
|
-
After a successful switch or new-session action,
|
|
388
|
+
After a successful switch or new-session action, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "new" | "resume"` and `previousSessionFile`.
|
|
386
389
|
Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
|
|
387
390
|
|
|
388
391
|
#### session_before_fork
|
|
@@ -399,7 +402,7 @@ pi.on("session_before_fork", async (event, ctx) => {
|
|
|
399
402
|
});
|
|
400
403
|
```
|
|
401
404
|
|
|
402
|
-
After a successful fork or clone,
|
|
405
|
+
After a successful fork or clone, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "fork"` and `previousSessionFile`.
|
|
403
406
|
Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
|
|
404
407
|
|
|
405
408
|
#### session_before_compact / session_compact
|
|
@@ -468,7 +471,8 @@ Fired after user submits prompt, before agent loop. Can inject a message and/or
|
|
|
468
471
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
469
472
|
// event.prompt - user's prompt text
|
|
470
473
|
// event.images - attached images (if any)
|
|
471
|
-
// event.systemPrompt - current system prompt
|
|
474
|
+
// event.systemPrompt - current chained system prompt for this handler
|
|
475
|
+
// (includes changes from earlier before_agent_start handlers)
|
|
472
476
|
// event.systemPromptOptions - structured options used to build the system prompt
|
|
473
477
|
// .customPrompt - any custom system prompt (from --system-prompt, SYSTEM.md, or custom templates)
|
|
474
478
|
// .selectedTools - tools currently active in the prompt
|
|
@@ -494,6 +498,8 @@ pi.on("before_agent_start", async (event, ctx) => {
|
|
|
494
498
|
|
|
495
499
|
The `systemPromptOptions` field gives extensions access to the same structured data Pi uses to build the system prompt. This lets you inspect what Pi has loaded — custom prompts, guidelines, tool snippets, context files, skills — without re-discovering resources or re-parsing flags. Use it when your extension needs to make deep, informed changes to the system prompt while respecting user-provided configuration.
|
|
496
500
|
|
|
501
|
+
Inside `before_agent_start`, `event.systemPrompt` and `ctx.getSystemPrompt()` both reflect the chained system prompt as of the current handler. Later `before_agent_start` handlers can still modify it again.
|
|
502
|
+
|
|
497
503
|
#### agent_start / agent_end
|
|
498
504
|
|
|
499
505
|
Fired once per user prompt.
|
|
@@ -526,6 +532,7 @@ Fired for message lifecycle updates.
|
|
|
526
532
|
|
|
527
533
|
- `message_start` and `message_end` fire for user, assistant, and toolResult messages.
|
|
528
534
|
- `message_update` fires for assistant streaming updates.
|
|
535
|
+
- `message_end` handlers can return `{ message }` to replace the finalized message. The replacement must keep the same `role`.
|
|
529
536
|
|
|
530
537
|
```typescript
|
|
531
538
|
pi.on("message_start", async (event, ctx) => {
|
|
@@ -538,7 +545,20 @@ pi.on("message_update", async (event, ctx) => {
|
|
|
538
545
|
});
|
|
539
546
|
|
|
540
547
|
pi.on("message_end", async (event, ctx) => {
|
|
541
|
-
|
|
548
|
+
if (event.message.role !== "assistant") return;
|
|
549
|
+
|
|
550
|
+
return {
|
|
551
|
+
message: {
|
|
552
|
+
...event.message,
|
|
553
|
+
usage: {
|
|
554
|
+
...event.message.usage,
|
|
555
|
+
cost: {
|
|
556
|
+
...event.message.usage.cost,
|
|
557
|
+
total: 0.123,
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
};
|
|
542
562
|
});
|
|
543
563
|
```
|
|
544
564
|
|
|
@@ -634,13 +654,28 @@ pi.on("model_select", async (event, ctx) => {
|
|
|
634
654
|
|
|
635
655
|
Use this to update UI elements (status bars, footers) or perform model-specific initialization when the active model changes.
|
|
636
656
|
|
|
657
|
+
#### thinking_level_select
|
|
658
|
+
|
|
659
|
+
Fired when the thinking level changes. This is notification-only; handler return values are ignored.
|
|
660
|
+
|
|
661
|
+
```typescript
|
|
662
|
+
pi.on("thinking_level_select", async (event, ctx) => {
|
|
663
|
+
// event.level - newly selected thinking level
|
|
664
|
+
// event.previousLevel - previous thinking level
|
|
665
|
+
|
|
666
|
+
ctx.ui.setStatus("thinking", `thinking: ${event.level}`);
|
|
667
|
+
});
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
Use this to update extension UI when `pi.setThinkingLevel()`, model changes, or built-in thinking-level controls change the active thinking level.
|
|
671
|
+
|
|
637
672
|
### Tool Events
|
|
638
673
|
|
|
639
674
|
#### tool_call
|
|
640
675
|
|
|
641
676
|
Fired after `tool_execution_start`, before the tool executes. **Can block.** Use `isToolCallEventType` to narrow and get typed inputs.
|
|
642
677
|
|
|
643
|
-
Before `tool_call` runs,
|
|
678
|
+
Before `tool_call` runs, pi waits for previously emitted Agent events to finish draining through `AgentSession`. This means `ctx.sessionManager` is up to date through the current assistant tool-calling message.
|
|
644
679
|
|
|
645
680
|
In the default parallel tool execution mode, sibling tool calls from the same assistant message are preflighted sequentially, then executed concurrently. `tool_call` is not guaranteed to see sibling tool results from that same assistant message in `ctx.sessionManager`.
|
|
646
681
|
|
|
@@ -751,7 +786,7 @@ pi.on("user_bash", (event, ctx) => {
|
|
|
751
786
|
// Option 1: Provide custom operations (e.g., SSH)
|
|
752
787
|
return { operations: remoteBashOps };
|
|
753
788
|
|
|
754
|
-
// Option 2: Wrap
|
|
789
|
+
// Option 2: Wrap pi's built-in local bash backend
|
|
755
790
|
const local = createLocalBashOperations();
|
|
756
791
|
return {
|
|
757
792
|
operations: {
|
|
@@ -856,7 +891,7 @@ Use this for abort-aware nested work started by extension handlers, for example:
|
|
|
856
891
|
- file or process helpers that accept `AbortSignal`
|
|
857
892
|
|
|
858
893
|
`ctx.signal` is typically defined during active turn events such as `tool_call`, `tool_result`, `message_update`, and `turn_end`.
|
|
859
|
-
It is usually `undefined` in idle or non-turn contexts such as session events, extension commands, and shortcuts fired while
|
|
894
|
+
It is usually `undefined` in idle or non-turn contexts such as session events, extension commands, and shortcuts fired while pi is idle.
|
|
860
895
|
|
|
861
896
|
```typescript
|
|
862
897
|
pi.on("tool_result", async (event, ctx) => {
|
|
@@ -877,7 +912,7 @@ Control flow helpers.
|
|
|
877
912
|
|
|
878
913
|
### ctx.shutdown()
|
|
879
914
|
|
|
880
|
-
Request a graceful shutdown of
|
|
915
|
+
Request a graceful shutdown of pi.
|
|
881
916
|
|
|
882
917
|
- **Interactive mode:** Deferred until the agent becomes idle (after processing all queued steering and follow-up messages).
|
|
883
918
|
- **RPC mode:** Deferred until the next idle state (after completing the current command response, when waiting for the next command).
|
|
@@ -984,7 +1019,7 @@ if (result.cancelled) {
|
|
|
984
1019
|
Options:
|
|
985
1020
|
- `parentSession`: parent session file to record in the new session header
|
|
986
1021
|
- `setup`: mutate the new session's `SessionManager` before `withSession` runs
|
|
987
|
-
- `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `
|
|
1022
|
+
- `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `pi` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
|
|
988
1023
|
|
|
989
1024
|
### ctx.fork(entryId, options?)
|
|
990
1025
|
|
|
@@ -1010,7 +1045,7 @@ if (cloneResult.cancelled) {
|
|
|
1010
1045
|
Options:
|
|
1011
1046
|
- `position`: `"before"` (default) forks before the selected user message, restoring that prompt into the editor
|
|
1012
1047
|
- `position`: `"at"` duplicates the active path through the selected entry without restoring editor text
|
|
1013
|
-
- `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `
|
|
1048
|
+
- `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `pi` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
|
|
1014
1049
|
|
|
1015
1050
|
### ctx.navigateTree(targetId, options?)
|
|
1016
1051
|
|
|
@@ -1047,7 +1082,7 @@ if (result.cancelled) {
|
|
|
1047
1082
|
```
|
|
1048
1083
|
|
|
1049
1084
|
Options:
|
|
1050
|
-
- `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `
|
|
1085
|
+
- `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `pi` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
|
|
1051
1086
|
|
|
1052
1087
|
To discover available sessions, use the static `SessionManager.list()` or `SessionManager.listAll()` methods:
|
|
1053
1088
|
|
|
@@ -1081,7 +1116,7 @@ pi.registerCommand("switch", {
|
|
|
1081
1116
|
Lifecycle and footguns:
|
|
1082
1117
|
- `withSession` runs only after the old session has emitted `session_shutdown`, the old runtime has been torn down, the replacement session has been rebound, and the new extension instance has already received `session_start`.
|
|
1083
1118
|
- The callback still executes in the original closure, not inside the new extension instance. That means your old extension instance may already have run its shutdown cleanup before `withSession` starts.
|
|
1084
|
-
- Captured old `
|
|
1119
|
+
- Captured old `pi` / old command `ctx` session-bound objects are stale after replacement and will throw if used. Use only the `ctx` passed to `withSession` for session-bound work.
|
|
1085
1120
|
- Previously extracted raw objects are still your responsibility. For example, if you capture `const sm = ctx.sessionManager` before replacement, `sm` is still the old `SessionManager` object. Do not reuse it after replacement.
|
|
1086
1121
|
- Code in `withSession` should assume any state invalidated by your `session_shutdown` handler is already gone. Only capture plain data that survives shutdown cleanly, such as strings, ids, and serialized config.
|
|
1087
1122
|
|
|
@@ -1147,9 +1182,9 @@ Example tool the LLM can call to trigger reload:
|
|
|
1147
1182
|
|
|
1148
1183
|
```typescript
|
|
1149
1184
|
import type { ExtensionAPI } from "@eminent337/aery";
|
|
1150
|
-
import { Type } from "
|
|
1185
|
+
import { Type } from "typebox";
|
|
1151
1186
|
|
|
1152
|
-
export default function (
|
|
1187
|
+
export default function (pi: ExtensionAPI) {
|
|
1153
1188
|
pi.registerCommand("reload-runtime", {
|
|
1154
1189
|
description: "Reload extensions, skills, prompts, and themes",
|
|
1155
1190
|
handler: async (_args, ctx) => {
|
|
@@ -1183,9 +1218,9 @@ Subscribe to events. See [Events](#events) for event types and return values.
|
|
|
1183
1218
|
|
|
1184
1219
|
Register a custom tool callable by the LLM. See [Custom Tools](#custom-tools) for full details.
|
|
1185
1220
|
|
|
1186
|
-
`pi.registerTool()` works both during extension load and after startup. You can call it inside `session_start`, command handlers, or other event handlers. New tools are refreshed immediately in the same session, so they appear in `
|
|
1221
|
+
`pi.registerTool()` works both during extension load and after startup. You can call it inside `session_start`, command handlers, or other event handlers. New tools are refreshed immediately in the same session, so they appear in `pi.getAllTools()` and are callable by the LLM without `/reload`.
|
|
1187
1222
|
|
|
1188
|
-
Use `
|
|
1223
|
+
Use `pi.setActiveTools()` to enable or disable tools (including dynamically added tools) at runtime.
|
|
1189
1224
|
|
|
1190
1225
|
Use `promptSnippet` to opt a custom tool into a one-line entry in `Available tools`, and `promptGuidelines` to append tool-specific bullets to the default `Guidelines` section when the tool is active.
|
|
1191
1226
|
|
|
@@ -1194,7 +1229,7 @@ Use `promptSnippet` to opt a custom tool into a one-line entry in `Available too
|
|
|
1194
1229
|
See [dynamic-tools.ts](../examples/extensions/dynamic-tools.ts) for a full example.
|
|
1195
1230
|
|
|
1196
1231
|
```typescript
|
|
1197
|
-
import { Type } from "
|
|
1232
|
+
import { Type } from "typebox";
|
|
1198
1233
|
import { StringEnum } from "@eminent337/aery-ai";
|
|
1199
1234
|
|
|
1200
1235
|
pi.registerTool({
|
|
@@ -1230,12 +1265,12 @@ pi.registerTool({
|
|
|
1230
1265
|
});
|
|
1231
1266
|
```
|
|
1232
1267
|
|
|
1233
|
-
###
|
|
1268
|
+
### pi.sendMessage(message, options?)
|
|
1234
1269
|
|
|
1235
1270
|
Inject a custom message into the session.
|
|
1236
1271
|
|
|
1237
1272
|
```typescript
|
|
1238
|
-
|
|
1273
|
+
pi.sendMessage({
|
|
1239
1274
|
customType: "my-extension",
|
|
1240
1275
|
content: "Message text",
|
|
1241
1276
|
display: true,
|
|
@@ -1281,12 +1316,12 @@ When not streaming, the message is sent immediately and triggers a new turn. Whe
|
|
|
1281
1316
|
|
|
1282
1317
|
See [send-user-message.ts](../examples/extensions/send-user-message.ts) for a complete example.
|
|
1283
1318
|
|
|
1284
|
-
###
|
|
1319
|
+
### pi.appendEntry(customType, data?)
|
|
1285
1320
|
|
|
1286
1321
|
Persist extension state (does NOT participate in LLM context).
|
|
1287
1322
|
|
|
1288
1323
|
```typescript
|
|
1289
|
-
|
|
1324
|
+
pi.appendEntry("my-state", { count: 42 });
|
|
1290
1325
|
|
|
1291
1326
|
// Restore on reload
|
|
1292
1327
|
pi.on("session_start", async (_event, ctx) => {
|
|
@@ -1298,35 +1333,35 @@ pi.on("session_start", async (_event, ctx) => {
|
|
|
1298
1333
|
});
|
|
1299
1334
|
```
|
|
1300
1335
|
|
|
1301
|
-
###
|
|
1336
|
+
### pi.setSessionName(name)
|
|
1302
1337
|
|
|
1303
1338
|
Set the session display name (shown in session selector instead of first message).
|
|
1304
1339
|
|
|
1305
1340
|
```typescript
|
|
1306
|
-
|
|
1341
|
+
pi.setSessionName("Refactor auth module");
|
|
1307
1342
|
```
|
|
1308
1343
|
|
|
1309
|
-
###
|
|
1344
|
+
### pi.getSessionName()
|
|
1310
1345
|
|
|
1311
1346
|
Get the current session name, if set.
|
|
1312
1347
|
|
|
1313
1348
|
```typescript
|
|
1314
|
-
const name =
|
|
1349
|
+
const name = pi.getSessionName();
|
|
1315
1350
|
if (name) {
|
|
1316
1351
|
console.log(`Session: ${name}`);
|
|
1317
1352
|
}
|
|
1318
1353
|
```
|
|
1319
1354
|
|
|
1320
|
-
###
|
|
1355
|
+
### pi.setLabel(entryId, label)
|
|
1321
1356
|
|
|
1322
1357
|
Set or clear a label on an entry. Labels are user-defined markers for bookmarking and navigation (shown in `/tree` selector).
|
|
1323
1358
|
|
|
1324
1359
|
```typescript
|
|
1325
1360
|
// Set a label
|
|
1326
|
-
|
|
1361
|
+
pi.setLabel(entryId, "checkpoint-before-refactor");
|
|
1327
1362
|
|
|
1328
1363
|
// Clear a label
|
|
1329
|
-
|
|
1364
|
+
pi.setLabel(entryId, undefined);
|
|
1330
1365
|
|
|
1331
1366
|
// Read labels via sessionManager
|
|
1332
1367
|
const label = ctx.sessionManager.getLabel(entryId);
|
|
@@ -1338,7 +1373,7 @@ Labels persist in the session and survive restarts. Use them to mark important p
|
|
|
1338
1373
|
|
|
1339
1374
|
Register a command.
|
|
1340
1375
|
|
|
1341
|
-
If multiple extensions register the same command name,
|
|
1376
|
+
If multiple extensions register the same command name, pi keeps them all and assigns numeric invocation suffixes in load order, for example `/review:1` and `/review:2`.
|
|
1342
1377
|
|
|
1343
1378
|
```typescript
|
|
1344
1379
|
pi.registerCommand("stats", {
|
|
@@ -1369,13 +1404,13 @@ pi.registerCommand("deploy", {
|
|
|
1369
1404
|
});
|
|
1370
1405
|
```
|
|
1371
1406
|
|
|
1372
|
-
###
|
|
1407
|
+
### pi.getCommands()
|
|
1373
1408
|
|
|
1374
1409
|
Get the slash commands available for invocation via `prompt` in the current session. Includes extension commands, prompt templates, and skill commands.
|
|
1375
1410
|
The list matches the RPC `get_commands` ordering: extensions first, then templates, then skills.
|
|
1376
1411
|
|
|
1377
1412
|
```typescript
|
|
1378
|
-
const commands =
|
|
1413
|
+
const commands = pi.getCommands();
|
|
1379
1414
|
const bySource = commands.filter((command) => command.source === "extension");
|
|
1380
1415
|
const userScoped = commands.filter((command) => command.sourceInfo.scope === "user");
|
|
1381
1416
|
```
|
|
@@ -1402,16 +1437,16 @@ Use `sourceInfo` as the canonical provenance field. Do not infer ownership from
|
|
|
1402
1437
|
Built-in interactive commands (like `/model` and `/settings`) are not included here. They are handled only in interactive
|
|
1403
1438
|
mode and would not execute if sent via `prompt`.
|
|
1404
1439
|
|
|
1405
|
-
###
|
|
1440
|
+
### pi.registerMessageRenderer(customType, renderer)
|
|
1406
1441
|
|
|
1407
1442
|
Register a custom TUI renderer for messages with your `customType`. See [Custom UI](#custom-ui).
|
|
1408
1443
|
|
|
1409
|
-
###
|
|
1444
|
+
### pi.registerShortcut(shortcut, options)
|
|
1410
1445
|
|
|
1411
1446
|
Register a keyboard shortcut. See [keybindings.md](keybindings.md) for the shortcut format and built-in keybindings.
|
|
1412
1447
|
|
|
1413
1448
|
```typescript
|
|
1414
|
-
|
|
1449
|
+
pi.registerShortcut("ctrl+shift+p", {
|
|
1415
1450
|
description: "Toggle plan mode",
|
|
1416
1451
|
handler: async (ctx) => {
|
|
1417
1452
|
ctx.ui.notify("Toggled!");
|
|
@@ -1419,39 +1454,39 @@ aery.registerShortcut("ctrl+shift+p", {
|
|
|
1419
1454
|
});
|
|
1420
1455
|
```
|
|
1421
1456
|
|
|
1422
|
-
###
|
|
1457
|
+
### pi.registerFlag(name, options)
|
|
1423
1458
|
|
|
1424
1459
|
Register a CLI flag.
|
|
1425
1460
|
|
|
1426
1461
|
```typescript
|
|
1427
|
-
|
|
1462
|
+
pi.registerFlag("plan", {
|
|
1428
1463
|
description: "Start in plan mode",
|
|
1429
1464
|
type: "boolean",
|
|
1430
1465
|
default: false,
|
|
1431
1466
|
});
|
|
1432
1467
|
|
|
1433
1468
|
// Check value
|
|
1434
|
-
if (
|
|
1469
|
+
if (pi.getFlag("plan")) {
|
|
1435
1470
|
// Plan mode enabled
|
|
1436
1471
|
}
|
|
1437
1472
|
```
|
|
1438
1473
|
|
|
1439
|
-
###
|
|
1474
|
+
### pi.exec(command, args, options?)
|
|
1440
1475
|
|
|
1441
1476
|
Execute a shell command.
|
|
1442
1477
|
|
|
1443
1478
|
```typescript
|
|
1444
|
-
const result = await
|
|
1479
|
+
const result = await pi.exec("git", ["status"], { signal, timeout: 5000 });
|
|
1445
1480
|
// result.stdout, result.stderr, result.code, result.killed
|
|
1446
1481
|
```
|
|
1447
1482
|
|
|
1448
|
-
###
|
|
1483
|
+
### pi.getActiveTools() / pi.getAllTools() / pi.setActiveTools(names)
|
|
1449
1484
|
|
|
1450
1485
|
Manage active tools. This works for both built-in tools and dynamically registered tools.
|
|
1451
1486
|
|
|
1452
1487
|
```typescript
|
|
1453
|
-
const active =
|
|
1454
|
-
const all =
|
|
1488
|
+
const active = pi.getActiveTools();
|
|
1489
|
+
const all = pi.getAllTools();
|
|
1455
1490
|
// [{
|
|
1456
1491
|
// name: "read",
|
|
1457
1492
|
// description: "Read file contents...",
|
|
@@ -1461,46 +1496,46 @@ const all = aery.getAllTools();
|
|
|
1461
1496
|
const names = all.map(t => t.name);
|
|
1462
1497
|
const builtinTools = all.filter((t) => t.sourceInfo.source === "builtin");
|
|
1463
1498
|
const extensionTools = all.filter((t) => t.sourceInfo.source !== "builtin" && t.sourceInfo.source !== "sdk");
|
|
1464
|
-
|
|
1499
|
+
pi.setActiveTools(["read", "bash"]); // Switch to read-only
|
|
1465
1500
|
```
|
|
1466
1501
|
|
|
1467
|
-
`
|
|
1502
|
+
`pi.getAllTools()` returns `name`, `description`, `parameters`, and `sourceInfo`.
|
|
1468
1503
|
|
|
1469
1504
|
Typical `sourceInfo.source` values:
|
|
1470
1505
|
- `builtin` for built-in tools
|
|
1471
1506
|
- `sdk` for tools passed via `createAgentSession({ customTools })`
|
|
1472
1507
|
- extension source metadata for tools registered by extensions
|
|
1473
1508
|
|
|
1474
|
-
###
|
|
1509
|
+
### pi.setModel(model)
|
|
1475
1510
|
|
|
1476
1511
|
Set the current model. Returns `false` if no API key is available for the model. See [models.md](models.md) for configuring custom models.
|
|
1477
1512
|
|
|
1478
1513
|
```typescript
|
|
1479
1514
|
const model = ctx.modelRegistry.find("anthropic", "claude-sonnet-4-5");
|
|
1480
1515
|
if (model) {
|
|
1481
|
-
const success = await
|
|
1516
|
+
const success = await pi.setModel(model);
|
|
1482
1517
|
if (!success) {
|
|
1483
1518
|
ctx.ui.notify("No API key for this model", "error");
|
|
1484
1519
|
}
|
|
1485
1520
|
}
|
|
1486
1521
|
```
|
|
1487
1522
|
|
|
1488
|
-
###
|
|
1523
|
+
### pi.getThinkingLevel() / pi.setThinkingLevel(level)
|
|
1489
1524
|
|
|
1490
|
-
Get or set the thinking level. Level is clamped to model capabilities (non-reasoning models always use "off").
|
|
1525
|
+
Get or set the thinking level. Level is clamped to model capabilities (non-reasoning models always use "off"). Changes emit `thinking_level_select`.
|
|
1491
1526
|
|
|
1492
1527
|
```typescript
|
|
1493
|
-
const current =
|
|
1494
|
-
|
|
1528
|
+
const current = pi.getThinkingLevel(); // "off" | "minimal" | "low" | "medium" | "high" | "xhigh"
|
|
1529
|
+
pi.setThinkingLevel("high");
|
|
1495
1530
|
```
|
|
1496
1531
|
|
|
1497
|
-
###
|
|
1532
|
+
### pi.events
|
|
1498
1533
|
|
|
1499
1534
|
Shared event bus for communication between extensions:
|
|
1500
1535
|
|
|
1501
1536
|
```typescript
|
|
1502
|
-
|
|
1503
|
-
|
|
1537
|
+
pi.events.on("my:event", (data) => { ... });
|
|
1538
|
+
pi.events.emit("my:event", { ... });
|
|
1504
1539
|
```
|
|
1505
1540
|
|
|
1506
1541
|
### pi.registerProvider(name, config)
|
|
@@ -1509,11 +1544,12 @@ Register or override a model provider dynamically. Useful for proxies, custom en
|
|
|
1509
1544
|
|
|
1510
1545
|
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`.
|
|
1511
1546
|
|
|
1512
|
-
If you need to discover models from a remote endpoint, prefer an async extension factory over deferring the fetch to `session_start`.
|
|
1547
|
+
If you need to discover models from a remote endpoint, prefer an async extension factory over deferring the fetch to `session_start`. pi waits for the factory before startup continues, so the registered models are available immediately, including to `pi --list-models`.
|
|
1513
1548
|
|
|
1514
1549
|
```typescript
|
|
1515
1550
|
// Register a new provider with custom models
|
|
1516
1551
|
pi.registerProvider("my-proxy", {
|
|
1552
|
+
name: "My Proxy",
|
|
1517
1553
|
baseUrl: "https://proxy.example.com",
|
|
1518
1554
|
apiKey: "PROXY_API_KEY", // env var name or literal
|
|
1519
1555
|
api: "anthropic-messages",
|
|
@@ -1560,18 +1596,19 @@ pi.registerProvider("corporate-ai", {
|
|
|
1560
1596
|
```
|
|
1561
1597
|
|
|
1562
1598
|
**Config options:**
|
|
1599
|
+
- `name` - Display name for the provider in UI such as `/login`.
|
|
1563
1600
|
- `baseUrl` - API endpoint URL. Required when defining models.
|
|
1564
1601
|
- `apiKey` - API key or environment variable name. Required when defining models (unless `oauth` provided).
|
|
1565
1602
|
- `api` - API type: `"anthropic-messages"`, `"openai-completions"`, `"openai-responses"`, etc.
|
|
1566
1603
|
- `headers` - Custom headers to include in requests.
|
|
1567
1604
|
- `authHeader` - If true, adds `Authorization: Bearer` header automatically.
|
|
1568
|
-
- `models` - Array of model definitions. If provided, replaces all existing models for this provider.
|
|
1605
|
+
- `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.
|
|
1569
1606
|
- `oauth` - OAuth provider config for `/login` support. When provided, the provider appears in the login menu.
|
|
1570
1607
|
- `streamSimple` - Custom streaming implementation for non-standard APIs.
|
|
1571
1608
|
|
|
1572
1609
|
See [custom-provider.md](custom-provider.md) for advanced topics: custom streaming APIs, OAuth details, model definition reference.
|
|
1573
1610
|
|
|
1574
|
-
###
|
|
1611
|
+
### pi.unregisterProvider(name)
|
|
1575
1612
|
|
|
1576
1613
|
Remove a previously registered provider and its models. Built-in models that were overridden by the provider are restored. Has no effect if the provider was not registered.
|
|
1577
1614
|
|
|
@@ -1581,7 +1618,7 @@ Like `registerProvider`, this takes effect immediately when called after the ini
|
|
|
1581
1618
|
pi.registerCommand("my-setup-teardown", {
|
|
1582
1619
|
description: "Remove the custom proxy provider",
|
|
1583
1620
|
handler: async (_args, _ctx) => {
|
|
1584
|
-
|
|
1621
|
+
pi.unregisterProvider("my-proxy");
|
|
1585
1622
|
},
|
|
1586
1623
|
});
|
|
1587
1624
|
```
|
|
@@ -1591,7 +1628,7 @@ pi.registerCommand("my-setup-teardown", {
|
|
|
1591
1628
|
Extensions with state should store it in tool result `details` for proper branching support:
|
|
1592
1629
|
|
|
1593
1630
|
```typescript
|
|
1594
|
-
export default function (
|
|
1631
|
+
export default function (pi: ExtensionAPI) {
|
|
1595
1632
|
let items: string[] = [];
|
|
1596
1633
|
|
|
1597
1634
|
// Reconstruct state from session
|
|
@@ -1626,7 +1663,7 @@ Register tools the LLM can call via `pi.registerTool()`. Tools appear in the sys
|
|
|
1626
1663
|
|
|
1627
1664
|
Use `promptSnippet` for a short one-line entry in the `Available tools` section in the default system prompt. If omitted, custom tools are left out of that section.
|
|
1628
1665
|
|
|
1629
|
-
Use `promptGuidelines` to add tool-specific bullets to the default system prompt `Guidelines` section. These bullets are included only while the tool is active (for example, after `
|
|
1666
|
+
Use `promptGuidelines` to add tool-specific bullets to the default system prompt `Guidelines` section. These bullets are included only while the tool is active (for example, after `pi.setActiveTools([...])`).
|
|
1630
1667
|
|
|
1631
1668
|
**Important:** `promptGuidelines` bullets are appended flat to the `Guidelines` section with no tool name prefix or grouping. Each guideline must name the tool it refers to — avoid "Use this tool when..." because the LLM cannot tell which tool "this" means. Write "Use my_tool when..." instead.
|
|
1632
1669
|
|
|
@@ -1665,7 +1702,7 @@ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
|
1665
1702
|
### Tool Definition
|
|
1666
1703
|
|
|
1667
1704
|
```typescript
|
|
1668
|
-
import { Type } from "
|
|
1705
|
+
import { Type } from "typebox";
|
|
1669
1706
|
import { StringEnum } from "@eminent337/aery-ai";
|
|
1670
1707
|
import { Text } from "@eminent337/aery-tui";
|
|
1671
1708
|
|
|
@@ -1702,8 +1739,8 @@ pi.registerTool({
|
|
|
1702
1739
|
details: { progress: 50 },
|
|
1703
1740
|
});
|
|
1704
1741
|
|
|
1705
|
-
// Run commands via
|
|
1706
|
-
const result = await
|
|
1742
|
+
// Run commands via pi.exec (captured from extension closure)
|
|
1743
|
+
const result = await pi.exec("some-command", [], { signal });
|
|
1707
1744
|
|
|
1708
1745
|
// Return result
|
|
1709
1746
|
return {
|
|
@@ -1737,7 +1774,7 @@ async execute(toolCallId, params) {
|
|
|
1737
1774
|
|
|
1738
1775
|
**Important:** Use `StringEnum` from `@eminent337/aery-ai` for string enums. `Type.Union`/`Type.Literal` doesn't work with Google's API.
|
|
1739
1776
|
|
|
1740
|
-
**Argument preparation:** `prepareArguments(args)` is optional. If defined, it runs before schema validation and before `execute()`. Use it to mimic an older accepted input shape when
|
|
1777
|
+
**Argument preparation:** `prepareArguments(args)` is optional. If defined, it runs before schema validation and before `execute()`. Use it to mimic an older accepted input shape when pi resumes an older session whose stored tool call arguments no longer match the current schema. Return the object you want validated against `parameters`. Keep the public schema strict. Do not add deprecated compatibility fields to `parameters` just to keep old resumed sessions working.
|
|
1741
1778
|
|
|
1742
1779
|
Example: an older session may contain an `edit` tool call with top-level `oldText` and `newText`, while the current schema only accepts `edits: [{ oldText, newText }]`.
|
|
1743
1780
|
|
|
@@ -1790,13 +1827,13 @@ Extensions can override built-in tools (`read`, `bash`, `edit`, `write`, `grep`,
|
|
|
1790
1827
|
|
|
1791
1828
|
```bash
|
|
1792
1829
|
# Extension's read tool replaces built-in read
|
|
1793
|
-
|
|
1830
|
+
pi -e ./tool-override.ts
|
|
1794
1831
|
```
|
|
1795
1832
|
|
|
1796
1833
|
Alternatively, use `--no-builtin-tools` to start without any built-in tools while keeping extension tools enabled:
|
|
1797
1834
|
```bash
|
|
1798
1835
|
# No built-in tools, only extension tools
|
|
1799
|
-
|
|
1836
|
+
pi --no-builtin-tools -e ./my-extension.ts
|
|
1800
1837
|
```
|
|
1801
1838
|
|
|
1802
1839
|
See [examples/extensions/tool-override.ts](../examples/extensions/tool-override.ts) for a complete example that overrides `read` with logging and access control.
|
|
@@ -1808,13 +1845,13 @@ See [examples/extensions/tool-override.ts](../examples/extensions/tool-override.
|
|
|
1808
1845
|
**Your implementation must match the exact result shape**, including the `details` type. The UI and session logic depend on these shapes for rendering and state tracking.
|
|
1809
1846
|
|
|
1810
1847
|
Built-in tool implementations:
|
|
1811
|
-
- [read.ts](https://github.com/
|
|
1812
|
-
- [bash.ts](https://github.com/
|
|
1813
|
-
- [edit.ts](https://github.com/
|
|
1814
|
-
- [write.ts](https://github.com/
|
|
1815
|
-
- [grep.ts](https://github.com/
|
|
1816
|
-
- [find.ts](https://github.com/
|
|
1817
|
-
- [ls.ts](https://github.com/
|
|
1848
|
+
- [read.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/read.ts) - `ReadToolDetails`
|
|
1849
|
+
- [bash.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/bash.ts) - `BashToolDetails`
|
|
1850
|
+
- [edit.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/edit.ts)
|
|
1851
|
+
- [write.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/write.ts)
|
|
1852
|
+
- [grep.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/grep.ts) - `GrepToolDetails`
|
|
1853
|
+
- [find.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/find.ts) - `FindToolDetails`
|
|
1854
|
+
- [ls.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/ls.ts) - `LsToolDetails`
|
|
1818
1855
|
|
|
1819
1856
|
### Remote Execution
|
|
1820
1857
|
|
|
@@ -1847,7 +1884,7 @@ pi.registerTool({
|
|
|
1847
1884
|
|
|
1848
1885
|
**Operations interfaces:** `ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, `LsOperations`, `GrepOperations`, `FindOperations`
|
|
1849
1886
|
|
|
1850
|
-
For `user_bash`, extensions can reuse
|
|
1887
|
+
For `user_bash`, extensions can reuse pi's local shell backend via `createLocalBashOperations()` instead of reimplementing local process spawning, shell resolution, and process-tree termination.
|
|
1851
1888
|
|
|
1852
1889
|
The bash tool also supports a spawn hook to adjust the command, cwd, or env before execution:
|
|
1853
1890
|
|
|
@@ -1922,7 +1959,7 @@ See [examples/extensions/truncated-tool.ts](../examples/extensions/truncated-too
|
|
|
1922
1959
|
One extension can register multiple tools with shared state:
|
|
1923
1960
|
|
|
1924
1961
|
```typescript
|
|
1925
|
-
export default function (
|
|
1962
|
+
export default function (pi: ExtensionAPI) {
|
|
1926
1963
|
let connection = null;
|
|
1927
1964
|
|
|
1928
1965
|
pi.registerTool({ name: "db_connect", ... });
|
|
@@ -1937,7 +1974,7 @@ export default function (aery: ExtensionAPI) {
|
|
|
1937
1974
|
|
|
1938
1975
|
### Custom Rendering
|
|
1939
1976
|
|
|
1940
|
-
Tools can provide `renderCall` and `renderResult` for custom TUI display. See [tui.md](tui.md) for the full component API and [tool-execution.ts](https://github.com/
|
|
1977
|
+
Tools can provide `renderCall` and `renderResult` for custom TUI display. See [tui.md](tui.md) for the full component API and [tool-execution.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/modes/interactive/components/tool-execution.ts) for how tool rows are composed.
|
|
1941
1978
|
|
|
1942
1979
|
By default, tool output is wrapped in a `Box` that handles padding and background. A defined `renderCall` or `renderResult` must return a `Component`. If a slot renderer is not defined, `tool-execution.ts` uses fallback rendering for that slot.
|
|
1943
1980
|
|
|
@@ -2186,7 +2223,7 @@ ctx.ui.setFooter((tui, theme) => ({
|
|
|
2186
2223
|
ctx.ui.setFooter(undefined); // Restore built-in footer
|
|
2187
2224
|
|
|
2188
2225
|
// Terminal title
|
|
2189
|
-
ctx.ui.setTitle("
|
|
2226
|
+
ctx.ui.setTitle("pi - my-project");
|
|
2190
2227
|
|
|
2191
2228
|
// Editor text
|
|
2192
2229
|
ctx.ui.setEditorText("Prefill text");
|
|
@@ -2224,6 +2261,10 @@ ctx.ui.setToolsExpanded(wasExpanded);
|
|
|
2224
2261
|
|
|
2225
2262
|
// Custom editor (vim mode, emacs mode, etc.)
|
|
2226
2263
|
ctx.ui.setEditorComponent((tui, theme, keybindings) => new VimEditor(tui, theme, keybindings));
|
|
2264
|
+
const currentEditor = ctx.ui.getEditorComponent();
|
|
2265
|
+
ctx.ui.setEditorComponent((tui, theme, keybindings) =>
|
|
2266
|
+
new WrappedEditor(tui, theme, keybindings, currentEditor?.(tui, theme, keybindings))
|
|
2267
|
+
);
|
|
2227
2268
|
ctx.ui.setEditorComponent(undefined); // Restore default editor
|
|
2228
2269
|
|
|
2229
2270
|
// Theme management (see themes.md for creating themes)
|
|
@@ -2365,7 +2406,7 @@ class VimEditor extends CustomEditor {
|
|
|
2365
2406
|
}
|
|
2366
2407
|
}
|
|
2367
2408
|
|
|
2368
|
-
export default function (
|
|
2409
|
+
export default function (pi: ExtensionAPI) {
|
|
2369
2410
|
pi.on("session_start", (_event, ctx) => {
|
|
2370
2411
|
ctx.ui.setEditorComponent((_tui, theme, keybindings) =>
|
|
2371
2412
|
new VimEditor(theme, keybindings)
|
|
@@ -2378,8 +2419,18 @@ export default function (aery: ExtensionAPI) {
|
|
|
2378
2419
|
- Extend `CustomEditor` (not base `Editor`) to get app keybindings (escape to abort, ctrl+d, model switching)
|
|
2379
2420
|
- Call `super.handleInput(data)` for keys you don't handle
|
|
2380
2421
|
- Factory receives `theme` and `keybindings` from the app
|
|
2422
|
+
- Use `ctx.ui.getEditorComponent()` before `setEditorComponent()` to wrap the previously configured custom editor
|
|
2381
2423
|
- Pass `undefined` to restore default: `ctx.ui.setEditorComponent(undefined)`
|
|
2382
2424
|
|
|
2425
|
+
To compose with another extension that already replaced the editor, capture the previous factory before setting yours:
|
|
2426
|
+
|
|
2427
|
+
```typescript
|
|
2428
|
+
const previous = ctx.ui.getEditorComponent();
|
|
2429
|
+
ctx.ui.setEditorComponent((tui, theme, keybindings) =>
|
|
2430
|
+
new MyEditor(tui, theme, keybindings, { base: previous?.(tui, theme, keybindings) })
|
|
2431
|
+
);
|
|
2432
|
+
```
|
|
2433
|
+
|
|
2383
2434
|
See [tui.md](tui.md) Pattern 7 for a complete example with mode indicator.
|
|
2384
2435
|
|
|
2385
2436
|
### Message Rendering
|
|
@@ -2389,7 +2440,7 @@ Register a custom renderer for messages with your `customType`:
|
|
|
2389
2440
|
```typescript
|
|
2390
2441
|
import { Text } from "@eminent337/aery-tui";
|
|
2391
2442
|
|
|
2392
|
-
|
|
2443
|
+
pi.registerMessageRenderer("my-extension", (message, options, theme) => {
|
|
2393
2444
|
const { expanded } = options;
|
|
2394
2445
|
let text = theme.fg("accent", `[${message.customType}] `);
|
|
2395
2446
|
text += message.content;
|
|
@@ -2402,10 +2453,10 @@ aery.registerMessageRenderer("my-extension", (message, options, theme) => {
|
|
|
2402
2453
|
});
|
|
2403
2454
|
```
|
|
2404
2455
|
|
|
2405
|
-
Messages are sent via `
|
|
2456
|
+
Messages are sent via `pi.sendMessage()`:
|
|
2406
2457
|
|
|
2407
2458
|
```typescript
|
|
2408
|
-
|
|
2459
|
+
pi.sendMessage({
|
|
2409
2460
|
customType: "my-extension", // Matches registerMessageRenderer
|
|
2410
2461
|
content: "Status update",
|
|
2411
2462
|
display: true, // Show in TUI
|
|
@@ -2535,12 +2586,11 @@ All examples in [examples/extensions/](../examples/extensions/).
|
|
|
2535
2586
|
| `custom-provider-gitlab-duo/` | GitLab Duo integration | `registerProvider` with OAuth |
|
|
2536
2587
|
| **Messages & Communication** |||
|
|
2537
2588
|
| `message-renderer.ts` | Custom message rendering | `registerMessageRenderer`, `sendMessage` |
|
|
2538
|
-
| `event-bus.ts` | Inter-extension events | `
|
|
2589
|
+
| `event-bus.ts` | Inter-extension events | `pi.events` |
|
|
2539
2590
|
| **Session Metadata** |||
|
|
2540
2591
|
| `session-name.ts` | Name sessions for selector | `setSessionName`, `getSessionName` |
|
|
2541
2592
|
| `bookmark.ts` | Bookmark entries for /tree | `setLabel` |
|
|
2542
2593
|
| **Misc** |||
|
|
2543
|
-
| `antigravity-image-gen.ts` | Image generation tool | `registerTool`, Google Antigravity |
|
|
2544
2594
|
| `inline-bash.ts` | Inline bash in tool calls | `on("tool_call")` |
|
|
2545
2595
|
| `bash-spawn-hook.ts` | Adjust bash command, cwd, and env before execution | `createBashTool`, `spawnHook` |
|
|
2546
2596
|
| `with-deps/` | Extension with npm dependencies | Package structure with `package.json` |
|