@vaclav-synacek/pi-coding-agent-termux 0.50.9-0 → 0.51.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +3 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +24 -9
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/index.d.ts +3 -3
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +4 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +4 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +74 -5
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +4 -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 +1 -1
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +19 -17
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +11 -9
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +15 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +1 -0
- package/dist/core/skills.js.map +1 -1
- package/dist/core/tools/bash.d.ts +11 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +18 -3
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts +2 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +2 -0
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +2 -0
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +7 -7
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +5 -5
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +2 -0
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +2 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +2 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +0 -7
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +4 -1
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts +3 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js +13 -4
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +11 -2
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +58 -12
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +12 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +6 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +43 -16
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +18 -15
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +4 -0
- 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/clipboard-native.d.ts +7 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -0
- package/dist/utils/clipboard-native.js +14 -0
- package/dist/utils/clipboard-native.js.map +1 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +14 -0
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/extensions.md +57 -9
- package/docs/keybindings.md +1 -0
- package/docs/models.md +43 -14
- package/docs/rpc.md +188 -1
- package/docs/termux.md +127 -0
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/antigravity-image-gen.ts +1 -1
- package/examples/extensions/bash-spawn-hook.ts +30 -0
- 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/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/hello.ts +1 -1
- package/examples/extensions/question.ts +1 -1
- package/examples/extensions/questionnaire.ts +1 -1
- package/examples/extensions/rpc-demo.ts +124 -0
- package/examples/extensions/sandbox/index.ts +1 -1
- package/examples/extensions/shutdown-command.ts +2 -2
- package/examples/extensions/ssh.ts +4 -4
- package/examples/extensions/subagent/index.ts +1 -1
- package/examples/extensions/todo.ts +1 -1
- package/examples/extensions/tool-override.ts +1 -1
- package/examples/extensions/truncated-tool.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +632 -0
- package/examples/sdk/06-extensions.ts +1 -1
- package/package.json +5 -5
package/docs/extensions.md
CHANGED
|
@@ -79,7 +79,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
79
79
|
parameters: Type.Object({
|
|
80
80
|
name: Type.String({ description: "Name to greet" }),
|
|
81
81
|
}),
|
|
82
|
-
async execute(toolCallId, params, onUpdate, ctx
|
|
82
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
83
83
|
return {
|
|
84
84
|
content: [{ type: "text", text: `Hello, ${params.name}!` }],
|
|
85
85
|
details: {},
|
|
@@ -473,16 +473,49 @@ Use this to update UI elements (status bars, footers) or perform model-specific
|
|
|
473
473
|
|
|
474
474
|
#### tool_call
|
|
475
475
|
|
|
476
|
-
Fired before tool executes. **Can block.**
|
|
476
|
+
Fired before tool executes. **Can block.** Use `isToolCallEventType` to narrow and get typed inputs.
|
|
477
477
|
|
|
478
478
|
```typescript
|
|
479
|
+
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
|
480
|
+
|
|
479
481
|
pi.on("tool_call", async (event, ctx) => {
|
|
480
482
|
// event.toolName - "bash", "read", "write", "edit", etc.
|
|
481
483
|
// event.toolCallId
|
|
482
484
|
// event.input - tool parameters
|
|
483
485
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
+
// Built-in tools: no type params needed
|
|
487
|
+
if (isToolCallEventType("bash", event)) {
|
|
488
|
+
// event.input is { command: string; timeout?: number }
|
|
489
|
+
if (event.input.command.includes("rm -rf")) {
|
|
490
|
+
return { block: true, reason: "Dangerous command" };
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (isToolCallEventType("read", event)) {
|
|
495
|
+
// event.input is { path: string; offset?: number; limit?: number }
|
|
496
|
+
console.log(`Reading: ${event.input.path}`);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
#### Typing custom tool input
|
|
502
|
+
|
|
503
|
+
Custom tools should export their input type:
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
// my-extension.ts
|
|
507
|
+
export type MyToolInput = Static<typeof myToolSchema>;
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Use `isToolCallEventType` with explicit type parameters:
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
|
514
|
+
import type { MyToolInput } from "my-extension";
|
|
515
|
+
|
|
516
|
+
pi.on("tool_call", (event) => {
|
|
517
|
+
if (isToolCallEventType<"my_tool", MyToolInput>("my_tool", event)) {
|
|
518
|
+
event.input.action; // typed
|
|
486
519
|
}
|
|
487
520
|
});
|
|
488
521
|
```
|
|
@@ -756,7 +789,7 @@ pi.registerTool({
|
|
|
756
789
|
text: Type.Optional(Type.String()),
|
|
757
790
|
}),
|
|
758
791
|
|
|
759
|
-
async execute(toolCallId, params, onUpdate, ctx
|
|
792
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
760
793
|
// Stream progress
|
|
761
794
|
onUpdate?.({ content: [{ type: "text", text: "Working..." }] });
|
|
762
795
|
|
|
@@ -1082,7 +1115,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1082
1115
|
pi.registerTool({
|
|
1083
1116
|
name: "my_tool",
|
|
1084
1117
|
// ...
|
|
1085
|
-
async execute(toolCallId, params, onUpdate, ctx
|
|
1118
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
1086
1119
|
items.push("new item");
|
|
1087
1120
|
return {
|
|
1088
1121
|
content: [{ type: "text", text: "Added" }],
|
|
@@ -1113,7 +1146,7 @@ pi.registerTool({
|
|
|
1113
1146
|
text: Type.Optional(Type.String()),
|
|
1114
1147
|
}),
|
|
1115
1148
|
|
|
1116
|
-
async execute(toolCallId, params, onUpdate, ctx
|
|
1149
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
1117
1150
|
// Check for cancellation
|
|
1118
1151
|
if (signal?.aborted) {
|
|
1119
1152
|
return { content: [{ type: "text", text: "Cancelled" }] };
|
|
@@ -1191,7 +1224,7 @@ const remoteRead = createReadTool(cwd, {
|
|
|
1191
1224
|
// Register, checking flag at execution time
|
|
1192
1225
|
pi.registerTool({
|
|
1193
1226
|
...remoteRead,
|
|
1194
|
-
async execute(id, params, onUpdate, _ctx
|
|
1227
|
+
async execute(id, params, signal, onUpdate, _ctx) {
|
|
1195
1228
|
const ssh = getSshConfig();
|
|
1196
1229
|
if (ssh) {
|
|
1197
1230
|
const tool = createReadTool(cwd, { operations: createRemoteOps(ssh) });
|
|
@@ -1204,6 +1237,20 @@ pi.registerTool({
|
|
|
1204
1237
|
|
|
1205
1238
|
**Operations interfaces:** `ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, `LsOperations`, `GrepOperations`, `FindOperations`
|
|
1206
1239
|
|
|
1240
|
+
The bash tool also supports a spawn hook to adjust the command, cwd, or env before execution:
|
|
1241
|
+
|
|
1242
|
+
```typescript
|
|
1243
|
+
import { createBashTool } from "@mariozechner/pi-coding-agent";
|
|
1244
|
+
|
|
1245
|
+
const bashTool = createBashTool(cwd, {
|
|
1246
|
+
spawnHook: ({ command, cwd, env }) => ({
|
|
1247
|
+
command: `source ~/.profile\n${command}`,
|
|
1248
|
+
cwd: `/mnt/sandbox${cwd}`,
|
|
1249
|
+
env: { ...env, CI: "1" },
|
|
1250
|
+
}),
|
|
1251
|
+
});
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1207
1254
|
See [examples/extensions/ssh.ts](../examples/extensions/ssh.ts) for a complete SSH example with `--ssh` flag.
|
|
1208
1255
|
|
|
1209
1256
|
### Output Truncation
|
|
@@ -1225,7 +1272,7 @@ import {
|
|
|
1225
1272
|
DEFAULT_MAX_LINES, // 2000
|
|
1226
1273
|
} from "@mariozechner/pi-coding-agent";
|
|
1227
1274
|
|
|
1228
|
-
async execute(toolCallId, params, onUpdate, ctx
|
|
1275
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
1229
1276
|
const output = await runCommand();
|
|
1230
1277
|
|
|
1231
1278
|
// Apply truncation
|
|
@@ -1744,4 +1791,5 @@ All examples in [examples/extensions/](../examples/extensions/).
|
|
|
1744
1791
|
| **Misc** |||
|
|
1745
1792
|
| `antigravity-image-gen.ts` | Image generation tool | `registerTool`, Google Antigravity |
|
|
1746
1793
|
| `inline-bash.ts` | Inline bash in tool calls | `on("tool_call")` |
|
|
1794
|
+
| `bash-spawn-hook.ts` | Adjust bash command, cwd, and env before execution | `createBashTool`, `spawnHook` |
|
|
1747
1795
|
| `with-deps/` | Extension with npm dependencies | Package structure with `package.json` |
|
package/docs/keybindings.md
CHANGED
|
@@ -124,6 +124,7 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, etc.
|
|
|
124
124
|
|--------|---------|-------------|
|
|
125
125
|
| `toggleSessionPath` | `ctrl+p` | Toggle path display |
|
|
126
126
|
| `toggleSessionSort` | `ctrl+s` | Toggle sort mode |
|
|
127
|
+
| `toggleSessionNamedFilter` | `ctrl+n` | Toggle named-only filter |
|
|
127
128
|
| `renameSession` | `ctrl+r` | Rename session |
|
|
128
129
|
| `deleteSession` | `ctrl+d` | Delete session |
|
|
129
130
|
| `deleteSessionNoninvasive` | `ctrl+backspace` | Delete session (when query empty) |
|
package/docs/models.md
CHANGED
|
@@ -4,14 +4,39 @@ Add custom providers and models (Ollama, vLLM, LM Studio, proxies) via `~/.pi/ag
|
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
7
|
-
- [
|
|
7
|
+
- [Minimal Example](#minimal-example)
|
|
8
|
+
- [Full Example](#full-example)
|
|
8
9
|
- [Supported APIs](#supported-apis)
|
|
9
10
|
- [Provider Configuration](#provider-configuration)
|
|
10
11
|
- [Model Configuration](#model-configuration)
|
|
11
12
|
- [Overriding Built-in Providers](#overriding-built-in-providers)
|
|
12
13
|
- [OpenAI Compatibility](#openai-compatibility)
|
|
13
14
|
|
|
14
|
-
##
|
|
15
|
+
## Minimal Example
|
|
16
|
+
|
|
17
|
+
For local models (Ollama, LM Studio, vLLM), only `id` is required per model:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"providers": {
|
|
22
|
+
"ollama": {
|
|
23
|
+
"baseUrl": "http://localhost:11434/v1",
|
|
24
|
+
"api": "openai-completions",
|
|
25
|
+
"apiKey": "ollama",
|
|
26
|
+
"models": [
|
|
27
|
+
{ "id": "llama3.1:8b" },
|
|
28
|
+
{ "id": "qwen2.5-coder:7b" }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The `apiKey` is required but Ollama ignores it, so any value works.
|
|
36
|
+
|
|
37
|
+
## Full Example
|
|
38
|
+
|
|
39
|
+
Override defaults when you need specific values:
|
|
15
40
|
|
|
16
41
|
```json
|
|
17
42
|
{
|
|
@@ -19,12 +44,16 @@ Add custom providers and models (Ollama, vLLM, LM Studio, proxies) via `~/.pi/ag
|
|
|
19
44
|
"ollama": {
|
|
20
45
|
"baseUrl": "http://localhost:11434/v1",
|
|
21
46
|
"api": "openai-completions",
|
|
47
|
+
"apiKey": "ollama",
|
|
22
48
|
"models": [
|
|
23
49
|
{
|
|
24
|
-
"id": "
|
|
50
|
+
"id": "llama3.1:8b",
|
|
25
51
|
"name": "Llama 3.1 8B (Local)",
|
|
52
|
+
"reasoning": false,
|
|
53
|
+
"input": ["text"],
|
|
26
54
|
"contextWindow": 128000,
|
|
27
|
-
"maxTokens": 32000
|
|
55
|
+
"maxTokens": 32000,
|
|
56
|
+
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
|
|
28
57
|
}
|
|
29
58
|
]
|
|
30
59
|
}
|
|
@@ -95,16 +124,16 @@ The `apiKey` and `headers` fields support three formats:
|
|
|
95
124
|
|
|
96
125
|
## Model Configuration
|
|
97
126
|
|
|
98
|
-
| Field | Required | Description |
|
|
99
|
-
|
|
100
|
-
| `id` | Yes | Model identifier |
|
|
101
|
-
| `name` | No | Display name |
|
|
102
|
-
| `api` | No | Override provider's API for this model |
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `cost` | No | `{"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}` |
|
|
127
|
+
| Field | Required | Default | Description |
|
|
128
|
+
|-------|----------|---------|-------------|
|
|
129
|
+
| `id` | Yes | — | Model identifier (passed to the API) |
|
|
130
|
+
| `name` | No | `id` | Display name in model selector |
|
|
131
|
+
| `api` | No | provider's `api` | Override provider's API for this model |
|
|
132
|
+
| `reasoning` | No | `false` | Supports extended thinking |
|
|
133
|
+
| `input` | No | `["text"]` | Input types: `["text"]` or `["text", "image"]` |
|
|
134
|
+
| `contextWindow` | No | `128000` | Context window size in tokens |
|
|
135
|
+
| `maxTokens` | No | `16384` | Maximum output tokens |
|
|
136
|
+
| `cost` | No | all zeros | `{"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}` (per million tokens) |
|
|
108
137
|
|
|
109
138
|
## Overriding Built-in Providers
|
|
110
139
|
|
package/docs/rpc.md
CHANGED
|
@@ -903,6 +903,191 @@ Emitted when an extension throws an error.
|
|
|
903
903
|
}
|
|
904
904
|
```
|
|
905
905
|
|
|
906
|
+
## Extension UI Protocol
|
|
907
|
+
|
|
908
|
+
Extensions can request user interaction via `ctx.ui.select()`, `ctx.ui.confirm()`, etc. In RPC mode, these are translated into a request/response sub-protocol on top of the base command/event flow.
|
|
909
|
+
|
|
910
|
+
There are two categories of extension UI methods:
|
|
911
|
+
|
|
912
|
+
- **Dialog methods** (`select`, `confirm`, `input`, `editor`): emit an `extension_ui_request` on stdout and block until the client sends back an `extension_ui_response` on stdin with the matching `id`.
|
|
913
|
+
- **Fire-and-forget methods** (`notify`, `setStatus`, `setWidget`, `setTitle`, `set_editor_text`): emit an `extension_ui_request` on stdout but do not expect a response. The client can display the information or ignore it.
|
|
914
|
+
|
|
915
|
+
If a dialog method includes a `timeout` field, the agent-side will auto-resolve with a default value when the timeout expires. The client does not need to track timeouts.
|
|
916
|
+
|
|
917
|
+
Some `ExtensionUIContext` methods are not supported in RPC mode because they require direct TUI access:
|
|
918
|
+
- `custom()` returns `undefined`
|
|
919
|
+
- `setWorkingMessage()`, `setFooter()`, `setHeader()`, `setEditorComponent()` are no-ops
|
|
920
|
+
- `getEditorText()` returns `""`
|
|
921
|
+
|
|
922
|
+
### Extension UI Requests (stdout)
|
|
923
|
+
|
|
924
|
+
All requests have `type: "extension_ui_request"`, a unique `id`, and a `method` field.
|
|
925
|
+
|
|
926
|
+
#### select
|
|
927
|
+
|
|
928
|
+
Prompt the user to choose from a list. Dialog methods with a `timeout` field include the timeout in milliseconds; the agent auto-resolves with `undefined` if the client doesn't respond in time.
|
|
929
|
+
|
|
930
|
+
```json
|
|
931
|
+
{
|
|
932
|
+
"type": "extension_ui_request",
|
|
933
|
+
"id": "uuid-1",
|
|
934
|
+
"method": "select",
|
|
935
|
+
"title": "Allow dangerous command?",
|
|
936
|
+
"options": ["Allow", "Block"],
|
|
937
|
+
"timeout": 10000
|
|
938
|
+
}
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
Expected response: `extension_ui_response` with `value` (the selected option string) or `cancelled: true`.
|
|
942
|
+
|
|
943
|
+
#### confirm
|
|
944
|
+
|
|
945
|
+
Prompt the user for yes/no confirmation.
|
|
946
|
+
|
|
947
|
+
```json
|
|
948
|
+
{
|
|
949
|
+
"type": "extension_ui_request",
|
|
950
|
+
"id": "uuid-2",
|
|
951
|
+
"method": "confirm",
|
|
952
|
+
"title": "Clear session?",
|
|
953
|
+
"message": "All messages will be lost.",
|
|
954
|
+
"timeout": 5000
|
|
955
|
+
}
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
Expected response: `extension_ui_response` with `confirmed: true/false` or `cancelled: true`.
|
|
959
|
+
|
|
960
|
+
#### input
|
|
961
|
+
|
|
962
|
+
Prompt the user for free-form text.
|
|
963
|
+
|
|
964
|
+
```json
|
|
965
|
+
{
|
|
966
|
+
"type": "extension_ui_request",
|
|
967
|
+
"id": "uuid-3",
|
|
968
|
+
"method": "input",
|
|
969
|
+
"title": "Enter a value",
|
|
970
|
+
"placeholder": "type something..."
|
|
971
|
+
}
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
Expected response: `extension_ui_response` with `value` (the entered text) or `cancelled: true`.
|
|
975
|
+
|
|
976
|
+
#### editor
|
|
977
|
+
|
|
978
|
+
Open a multi-line text editor with optional prefilled content.
|
|
979
|
+
|
|
980
|
+
```json
|
|
981
|
+
{
|
|
982
|
+
"type": "extension_ui_request",
|
|
983
|
+
"id": "uuid-4",
|
|
984
|
+
"method": "editor",
|
|
985
|
+
"title": "Edit some text",
|
|
986
|
+
"prefill": "Line 1\nLine 2\nLine 3"
|
|
987
|
+
}
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
Expected response: `extension_ui_response` with `value` (the edited text) or `cancelled: true`.
|
|
991
|
+
|
|
992
|
+
#### notify
|
|
993
|
+
|
|
994
|
+
Display a notification. Fire-and-forget, no response expected.
|
|
995
|
+
|
|
996
|
+
```json
|
|
997
|
+
{
|
|
998
|
+
"type": "extension_ui_request",
|
|
999
|
+
"id": "uuid-5",
|
|
1000
|
+
"method": "notify",
|
|
1001
|
+
"message": "Command blocked by user",
|
|
1002
|
+
"notifyType": "warning"
|
|
1003
|
+
}
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
The `notifyType` field is `"info"`, `"warning"`, or `"error"`. Defaults to `"info"` if omitted.
|
|
1007
|
+
|
|
1008
|
+
#### setStatus
|
|
1009
|
+
|
|
1010
|
+
Set or clear a status entry in the footer/status bar. Fire-and-forget.
|
|
1011
|
+
|
|
1012
|
+
```json
|
|
1013
|
+
{
|
|
1014
|
+
"type": "extension_ui_request",
|
|
1015
|
+
"id": "uuid-6",
|
|
1016
|
+
"method": "setStatus",
|
|
1017
|
+
"statusKey": "my-ext",
|
|
1018
|
+
"statusText": "Turn 3 running..."
|
|
1019
|
+
}
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
Send `statusText: undefined` (or omit it) to clear the status entry for that key.
|
|
1023
|
+
|
|
1024
|
+
#### setWidget
|
|
1025
|
+
|
|
1026
|
+
Set or clear a widget (block of text lines) displayed above or below the editor. Fire-and-forget.
|
|
1027
|
+
|
|
1028
|
+
```json
|
|
1029
|
+
{
|
|
1030
|
+
"type": "extension_ui_request",
|
|
1031
|
+
"id": "uuid-7",
|
|
1032
|
+
"method": "setWidget",
|
|
1033
|
+
"widgetKey": "my-ext",
|
|
1034
|
+
"widgetLines": ["--- My Widget ---", "Line 1", "Line 2"],
|
|
1035
|
+
"widgetPlacement": "aboveEditor"
|
|
1036
|
+
}
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
Send `widgetLines: undefined` (or omit it) to clear the widget. The `widgetPlacement` field is `"aboveEditor"` (default) or `"belowEditor"`. Only string arrays are supported in RPC mode; component factories are ignored.
|
|
1040
|
+
|
|
1041
|
+
#### setTitle
|
|
1042
|
+
|
|
1043
|
+
Set the terminal window/tab title. Fire-and-forget.
|
|
1044
|
+
|
|
1045
|
+
```json
|
|
1046
|
+
{
|
|
1047
|
+
"type": "extension_ui_request",
|
|
1048
|
+
"id": "uuid-8",
|
|
1049
|
+
"method": "setTitle",
|
|
1050
|
+
"title": "pi - my project"
|
|
1051
|
+
}
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
#### set_editor_text
|
|
1055
|
+
|
|
1056
|
+
Set the text in the input editor. Fire-and-forget.
|
|
1057
|
+
|
|
1058
|
+
```json
|
|
1059
|
+
{
|
|
1060
|
+
"type": "extension_ui_request",
|
|
1061
|
+
"id": "uuid-9",
|
|
1062
|
+
"method": "set_editor_text",
|
|
1063
|
+
"text": "prefilled text for the user"
|
|
1064
|
+
}
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
### Extension UI Responses (stdin)
|
|
1068
|
+
|
|
1069
|
+
Responses are sent for dialog methods only (`select`, `confirm`, `input`, `editor`). The `id` must match the request.
|
|
1070
|
+
|
|
1071
|
+
#### Value response (select, input, editor)
|
|
1072
|
+
|
|
1073
|
+
```json
|
|
1074
|
+
{"type": "extension_ui_response", "id": "uuid-1", "value": "Allow"}
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
#### Confirmation response (confirm)
|
|
1078
|
+
|
|
1079
|
+
```json
|
|
1080
|
+
{"type": "extension_ui_response", "id": "uuid-2", "confirmed": true}
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
#### Cancellation response (any dialog)
|
|
1084
|
+
|
|
1085
|
+
Dismiss any dialog method. The extension receives `undefined` (for select/input/editor) or `false` (for confirm).
|
|
1086
|
+
|
|
1087
|
+
```json
|
|
1088
|
+
{"type": "extension_ui_response", "id": "uuid-3", "cancelled": true}
|
|
1089
|
+
```
|
|
1090
|
+
|
|
906
1091
|
## Error Handling
|
|
907
1092
|
|
|
908
1093
|
Failed commands return a response with `success: false`:
|
|
@@ -933,7 +1118,7 @@ Source files:
|
|
|
933
1118
|
- [`packages/ai/src/types.ts`](../../ai/src/types.ts) - `Model`, `UserMessage`, `AssistantMessage`, `ToolResultMessage`
|
|
934
1119
|
- [`packages/agent/src/types.ts`](../../agent/src/types.ts) - `AgentMessage`, `AgentEvent`
|
|
935
1120
|
- [`src/core/messages.ts`](../src/core/messages.ts) - `BashExecutionMessage`
|
|
936
|
-
- [`src/modes/rpc/rpc-types.ts`](../src/modes/rpc/rpc-types.ts) - RPC command/response types
|
|
1121
|
+
- [`src/modes/rpc/rpc-types.ts`](../src/modes/rpc/rpc-types.ts) - RPC command/response types, extension UI request/response types
|
|
937
1122
|
|
|
938
1123
|
### Model
|
|
939
1124
|
|
|
@@ -1082,6 +1267,8 @@ for event in read_events():
|
|
|
1082
1267
|
|
|
1083
1268
|
See [`test/rpc-example.ts`](../test/rpc-example.ts) for a complete interactive example, or [`src/modes/rpc/rpc-client.ts`](../src/modes/rpc/rpc-client.ts) for a typed client implementation.
|
|
1084
1269
|
|
|
1270
|
+
For a complete example of handling the extension UI protocol, see [`examples/rpc-extension-ui.ts`](../examples/rpc-extension-ui.ts) which pairs with the [`examples/extensions/rpc-demo.ts`](../examples/extensions/rpc-demo.ts) extension.
|
|
1271
|
+
|
|
1085
1272
|
```javascript
|
|
1086
1273
|
const { spawn } = require("child_process");
|
|
1087
1274
|
const readline = require("readline");
|
package/docs/termux.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Termux (Android) Setup
|
|
2
|
+
|
|
3
|
+
Pi runs on Android via [Termux](https://termux.dev/), a terminal emulator and Linux environment for Android.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
1. Install [Termux](https://github.com/termux/termux-app#installation) from GitHub or F-Droid (not Google Play, that version is deprecated)
|
|
8
|
+
2. Install [Termux:API](https://github.com/termux/termux-api#installation) from GitHub or F-Droid for clipboard and other device integrations
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Update packages
|
|
14
|
+
pkg update && pkg upgrade
|
|
15
|
+
|
|
16
|
+
# Install dependencies
|
|
17
|
+
pkg install nodejs termux-api git
|
|
18
|
+
|
|
19
|
+
# Install pi
|
|
20
|
+
npm install -g @mariozechner/pi-coding-agent
|
|
21
|
+
|
|
22
|
+
# Create config directory
|
|
23
|
+
mkdir -p ~/.pi/agent
|
|
24
|
+
|
|
25
|
+
# Run pi
|
|
26
|
+
pi
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Clipboard Support
|
|
30
|
+
|
|
31
|
+
Clipboard operations use `termux-clipboard-set` and `termux-clipboard-get` when running in Termux. The Termux:API app must be installed for these to work.
|
|
32
|
+
|
|
33
|
+
Image clipboard is not supported on Termux (the `ctrl+v` image paste feature will not work).
|
|
34
|
+
|
|
35
|
+
## Example AGENTS.md for Termux
|
|
36
|
+
|
|
37
|
+
Create `~/.pi/agent/AGENTS.md` to help the agent understand the Termux environment:
|
|
38
|
+
|
|
39
|
+
```markdown
|
|
40
|
+
# Agent Environment: Termux on Android
|
|
41
|
+
|
|
42
|
+
## Location
|
|
43
|
+
- **OS**: Android (Termux terminal emulator)
|
|
44
|
+
- **Home**: `/data/data/com.termux/files/home`
|
|
45
|
+
- **Prefix**: `/data/data/com.termux/files/usr`
|
|
46
|
+
- **Shared storage**: `/storage/emulated/0` (Downloads, Documents, etc.)
|
|
47
|
+
|
|
48
|
+
## Opening URLs
|
|
49
|
+
```bash
|
|
50
|
+
termux-open-url "https://example.com"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Opening Files
|
|
54
|
+
```bash
|
|
55
|
+
termux-open file.pdf # Opens with default app
|
|
56
|
+
termux-open -c image.jpg # Choose app
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Clipboard
|
|
60
|
+
```bash
|
|
61
|
+
termux-clipboard-set "text" # Copy
|
|
62
|
+
termux-clipboard-get # Paste
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Notifications
|
|
66
|
+
```bash
|
|
67
|
+
termux-notification -t "Title" -c "Content"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Device Info
|
|
71
|
+
```bash
|
|
72
|
+
termux-battery-status # Battery info
|
|
73
|
+
termux-wifi-connectioninfo # WiFi info
|
|
74
|
+
termux-telephony-deviceinfo # Device info
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Sharing
|
|
78
|
+
```bash
|
|
79
|
+
termux-share -a send file.txt # Share file
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Other Useful Commands
|
|
83
|
+
```bash
|
|
84
|
+
termux-toast "message" # Quick toast popup
|
|
85
|
+
termux-vibrate # Vibrate device
|
|
86
|
+
termux-tts-speak "hello" # Text to speech
|
|
87
|
+
termux-camera-photo out.jpg # Take photo
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Notes
|
|
91
|
+
- Termux:API app must be installed for `termux-*` commands
|
|
92
|
+
- Use `pkg install termux-api` for the command-line tools
|
|
93
|
+
- Storage permission needed for `/storage/emulated/0` access
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Limitations
|
|
97
|
+
|
|
98
|
+
- **No image clipboard**: Termux clipboard API only supports text
|
|
99
|
+
- **No native binaries**: Some optional native dependencies (like the clipboard module) are unavailable on Android ARM64 and are skipped during installation
|
|
100
|
+
- **Storage access**: To access files in `/storage/emulated/0` (Downloads, etc.), run `termux-setup-storage` once to grant permissions
|
|
101
|
+
|
|
102
|
+
## Troubleshooting
|
|
103
|
+
|
|
104
|
+
### Clipboard not working
|
|
105
|
+
|
|
106
|
+
Ensure both apps are installed:
|
|
107
|
+
1. Termux (from GitHub or F-Droid)
|
|
108
|
+
2. Termux:API (from GitHub or F-Droid)
|
|
109
|
+
|
|
110
|
+
Then install the CLI tools:
|
|
111
|
+
```bash
|
|
112
|
+
pkg install termux-api
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Permission denied for shared storage
|
|
116
|
+
|
|
117
|
+
Run once to grant storage permissions:
|
|
118
|
+
```bash
|
|
119
|
+
termux-setup-storage
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Node.js installation issues
|
|
123
|
+
|
|
124
|
+
If npm fails, try clearing the cache:
|
|
125
|
+
```bash
|
|
126
|
+
npm cache clean --force
|
|
127
|
+
```
|
|
@@ -53,6 +53,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
|
|
|
53
53
|
| `snake.ts` | Snake game with custom UI, keyboard handling, and session persistence |
|
|
54
54
|
| `send-user-message.ts` | Demonstrates `pi.sendUserMessage()` for sending user messages from extensions |
|
|
55
55
|
| `timed-confirm.ts` | Demonstrates AbortSignal for auto-dismissing `ctx.ui.confirm()` and `ctx.ui.select()` dialogs |
|
|
56
|
+
| `rpc-demo.ts` | Exercises all RPC-supported extension UI methods; pair with [`examples/rpc-extension-ui.ts`](../rpc-extension-ui.ts) |
|
|
56
57
|
| `modal-editor.ts` | Custom vim-like modal editor via `ctx.ui.setEditorComponent()` |
|
|
57
58
|
| `rainbow-editor.ts` | Animated rainbow text effect via custom editor |
|
|
58
59
|
| `notify.ts` | Desktop notifications via OSC 777 when agent finishes (Ghostty, iTerm2, WezTerm) |
|
|
@@ -352,7 +352,7 @@ export default function antigravityImageGen(pi: ExtensionAPI) {
|
|
|
352
352
|
description:
|
|
353
353
|
"Generate an image via Google Antigravity image models. Returns the image as a tool result attachment. Optional saving via save=project|global|custom|none, or PI_IMAGE_SAVE_MODE/PI_IMAGE_SAVE_DIR.",
|
|
354
354
|
parameters: TOOL_PARAMS,
|
|
355
|
-
async execute(_toolCallId, params: ToolParams, onUpdate, ctx
|
|
355
|
+
async execute(_toolCallId, params: ToolParams, signal, onUpdate, ctx) {
|
|
356
356
|
const { accessToken, projectId } = await getCredentials(ctx);
|
|
357
357
|
const model = params.model || DEFAULT_MODEL;
|
|
358
358
|
const aspectRatio = params.aspectRatio || DEFAULT_ASPECT_RATIO;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash Spawn Hook Example
|
|
3
|
+
*
|
|
4
|
+
* Adjusts command, cwd, and env before execution.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* pi -e ./bash-spawn-hook.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
11
|
+
import { createBashTool } from "@mariozechner/pi-coding-agent";
|
|
12
|
+
|
|
13
|
+
export default function (pi: ExtensionAPI) {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
|
|
16
|
+
const bashTool = createBashTool(cwd, {
|
|
17
|
+
spawnHook: ({ command, cwd, env }) => ({
|
|
18
|
+
command: `source ~/.profile\n${command}`,
|
|
19
|
+
cwd,
|
|
20
|
+
env: { ...env, PI_SPAWN_HOOK: "1" },
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
pi.registerTool({
|
|
25
|
+
...bashTool,
|
|
26
|
+
execute: async (id, params, signal, onUpdate, _ctx) => {
|
|
27
|
+
return bashTool.execute(id, params, signal, onUpdate);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-custom-provider",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-custom-provider",
|
|
9
|
-
"version": "1.1
|
|
9
|
+
"version": "1.2.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.52.0"
|
|
12
12
|
}
|
|
@@ -14,7 +14,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
14
14
|
name: Type.String({ description: "Name to greet" }),
|
|
15
15
|
}),
|
|
16
16
|
|
|
17
|
-
async execute(_toolCallId, params, _onUpdate, _ctx
|
|
17
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
18
18
|
const { name } = params as { name: string };
|
|
19
19
|
return {
|
|
20
20
|
content: [{ type: "text", text: `Hello, ${name}!` }],
|
|
@@ -40,7 +40,7 @@ export default function question(pi: ExtensionAPI) {
|
|
|
40
40
|
description: "Ask the user a question and let them pick from options. Use when you need user input to proceed.",
|
|
41
41
|
parameters: QuestionParams,
|
|
42
42
|
|
|
43
|
-
async execute(_toolCallId, params, _onUpdate, ctx
|
|
43
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
44
44
|
if (!ctx.hasUI) {
|
|
45
45
|
return {
|
|
46
46
|
content: [{ type: "text", text: "Error: UI not available (running in non-interactive mode)" }],
|