@cydm/pie 2.6.11 → 2.7.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/README.md +68 -2
- package/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/plan-mode/index.js +130 -21
- package/dist/builtin/extensions/subagent/index.js +4 -5
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/builtin/skills/computer-use-axi/SKILL.md +103 -0
- package/dist/builtin/skills/computer-use-axi/computer-use-axi-cli.js +37 -0
- package/dist/builtin/{extensions/computer-use/index.js → skills/computer-use-axi/computer-use-axi-runtime.js} +754 -642
- package/dist/builtin/skills/computer-use-axi/package.json +11 -0
- package/dist/chunks/{chunk-ZVM234WE.js → chunk-6RBSTII7.js} +669 -594
- package/dist/chunks/{chunk-MA4CHFN4.js → chunk-IPNAFMOJ.js} +172 -230
- package/dist/chunks/{chunk-7B4IIBVS.js → chunk-R5I2QLF2.js} +1269 -4
- package/dist/chunks/{chunk-PAC62XU4.js → chunk-TWILEFLD.js} +25 -3
- package/dist/chunks/{src-HPIBCE2I.js → src-E7NCXMU5.js} +2 -2
- package/dist/chunks/{test-stream-Y7WXELAJ.js → test-stream-BWBJTFNI.js} +1 -1
- package/dist/cli.js +1979 -384
- package/models.schema.json +52 -0
- package/package.json +3 -3
- package/dist/builtin/extensions/computer-use/package.json +0 -14
- package/dist/builtin/extensions/computer-use/scripts/build-native.mjs +0 -278
- package/dist/builtin/extensions/computer-use/scripts/test-native-retention.mjs +0 -204
- package/dist/chunks/chunk-5353DEEC.js +0 -130
- package/dist/chunks/webapi-MBCKO7ZV.js +0 -2481
- package/dist/code-intel-worker.cjs +0 -211227
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/arm64/linux-bridge +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/x64/linux-bridge +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/arm64/pie-computer-use +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/Info.plist +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/MacOS/pie-computer-use +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/_CodeSignature/CodeResources +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/x64/pie-computer-use +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/windows/windows-bridge.exe +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/helper-path.mjs +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/setup-helper.mjs +0 -0
package/README.md
CHANGED
|
@@ -335,7 +335,7 @@ Platform notes:
|
|
|
335
335
|
- **Windows**: the helper installs to `~/.pie/helpers/computer-use/windows-bridge.exe` and needs an interactive desktop session.
|
|
336
336
|
- **Linux**: the helper installs to `~/.pie/helpers/computer-use/linux-bridge` and needs an AT-SPI2 bus (a running desktop session; headless servers are not supported).
|
|
337
337
|
|
|
338
|
-
Config: `~/.pie/extensions/computer-use.json` (user) or `.pie/computer-use.json` (project) with `browser_use`, `headless`, `cursor_overlay`, `managed_browser`; env overrides `PIE_COMPUTER_USE_BROWSER_USE` / `PIE_COMPUTER_USE_HEADLESS` / `PIE_COMPUTER_USE_CURSOR_OVERLAY` / `PIE_COMPUTER_USE_MANAGED_BROWSER`.
|
|
338
|
+
Config: `~/.pie/extensions/computer-use.json` (user) or `.pie/computer-use.json` (project) with `browser_use`, `headless`, `cursor_overlay`, `managed_browser`; env overrides `PIE_COMPUTER_USE_BROWSER_USE` / `PIE_COMPUTER_USE_HEADLESS` / `PIE_COMPUTER_USE_CURSOR_OVERLAY` / `PIE_COMPUTER_USE_MANAGED_BROWSER`. The engine ships as the `computer-use-axi` skill: invoke it through bash (`node <skill baseDir>/computer-use-axi-cli.js <command>`) per its SKILL.md, and run `pie doctor` for helper install status.
|
|
339
339
|
|
|
340
340
|
## Web Research
|
|
341
341
|
|
|
@@ -349,7 +349,7 @@ Pie exposes one default model-visible web tool: `web_research`. Use it for publi
|
|
|
349
349
|
|
|
350
350
|
Pie's default tools operate from the current workspace. File tools are workspace-scoped, `~/.pie` is allowlisted for Pie configuration/session support, and shell commands return structured timeout/truncation/error details. The canonical shell tool is still named `bash` for compatibility; on Windows `shell:auto` uses PowerShell, and callers can explicitly request `bash`, `powershell`, or `cmd`. High-risk shell commands require confirmation outside YOLO mode. `/settings/yolo` relaxes filesystem sandbox directory restrictions and should only be used in trusted workspaces. `pie permissions` prints the current policy-derived safety summary.
|
|
351
351
|
|
|
352
|
-
In interactive Plan mode (`/tools/plan`), workspace editing tools are unavailable. The `bash` guard accepts simple read commands and read-only pipelines such as `rg 'TODO' . | head -20`; it blocks redirects, command substitution, and write-capable subcommands. Use `plan_scratch` to write, read, or list temporary text drafts in a directory dedicated to the current session. Shell redirection to `/tmp` remains blocked in Plan mode. Leave Plan mode before changing workspace files.
|
|
352
|
+
In interactive Plan mode (`/tools/plan`), workspace editing tools are unavailable. The `bash` guard accepts simple read commands and read-only pipelines such as `rg 'TODO' . | head -20`; it blocks redirects, command substitution, and write-capable subcommands. Use `plan_scratch` to write, read, or list temporary text drafts in a directory dedicated to the current session. Shell redirection to `/tmp` remains blocked in Plan mode. When the plan is ready, the agent submits the ordered steps with `plan_submit` and you choose to execute immediately (progress is tracked through `[DONE:n]` markers in responses), keep planning, or refine the plan in a prefilled editor; a plan written as plain text under a `Plan:`/`计划:` heading is still detected as a fallback. Leave Plan mode before changing workspace files.
|
|
353
353
|
|
|
354
354
|
## Code Intelligence
|
|
355
355
|
|
|
@@ -441,3 +441,69 @@ If the package version already exists on npm, bump `products/cli/package.json` f
|
|
|
441
441
|
## License
|
|
442
442
|
|
|
443
443
|
MIT
|
|
444
|
+
|
|
445
|
+
### Experimental Responses profiles
|
|
446
|
+
|
|
447
|
+
In `models.json`, set a profile's `api` to `"openai-responses"`, `baseUrl` to
|
|
448
|
+
its API root (for example `https://api.openai.com/v1`), and `apiKeyEnv` to your
|
|
449
|
+
credential environment variable. Supply model IDs and limits in `models` as
|
|
450
|
+
usual. Optional profile/model `transport` is `"sse"` (default), `"websocket"`
|
|
451
|
+
(strict), or `"auto"` (WS with conservative pre-submission SSE fallback).
|
|
452
|
+
Model transport overrides profile transport. API selection is explicit.
|
|
453
|
+
|
|
454
|
+
A profile may declare `endpoints: { "<api>": "https://..." }` and each model
|
|
455
|
+
may select `api` (otherwise it inherits the profile API). Supported keys are
|
|
456
|
+
`openai-completions`, `openai-responses`, and `anthropic-messages`; endpoint URLs
|
|
457
|
+
follow the same absolute HTTP(S) validation as `baseUrl`. The resolved API uses
|
|
458
|
+
`endpoints[api]` when present, otherwise `baseUrl`. An explicitly different model
|
|
459
|
+
API **requires** its endpoint entry; an explicit same-API selection can use
|
|
460
|
+
`baseUrl`. Remote catalogs additionally require every endpoint to share the
|
|
461
|
+
authenticated catalog origin. `modelOverrides` cannot change these routes.
|
|
462
|
+
|
|
463
|
+
For example, Bigmodel's Chat Completions and Responses roots differ. This is a
|
|
464
|
+
configuration example, not special provider logic; use distinct model IDs within
|
|
465
|
+
the profile (replace the Chat model ID with your supported model):
|
|
466
|
+
|
|
467
|
+
```json
|
|
468
|
+
{
|
|
469
|
+
"profiles": {
|
|
470
|
+
"bigmodel": {
|
|
471
|
+
"api": "openai-completions",
|
|
472
|
+
"baseUrl": "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
473
|
+
"endpoints": {
|
|
474
|
+
"openai-responses": "https://open.bigmodel.cn/api/v1"
|
|
475
|
+
},
|
|
476
|
+
"apiKeyEnv": "BIGMODEL_API_KEY",
|
|
477
|
+
"transport": "sse",
|
|
478
|
+
"models": [
|
|
479
|
+
{
|
|
480
|
+
"id": "glm-5.2", "api": "openai-completions", "input": ["text"],
|
|
481
|
+
"contextWindow": 128000, "maxTokens": 4096,
|
|
482
|
+
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
"id": "glm-5.3", "api": "openai-responses", "input": ["text"],
|
|
486
|
+
"contextWindow": 128000, "maxTokens": 4096,
|
|
487
|
+
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
|
|
488
|
+
}
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Limits/prices above are illustrative, not provider metadata. Keep Bigmodel and
|
|
496
|
+
Kimi Responses on SSE for the tested endpoints/accounts: Responses support does
|
|
497
|
+
not imply usable WS sessions. Current benchmarks show continuation upload savings,
|
|
498
|
+
not consistent latency improvement; SSE stays default, WS/continuation opt-in.
|
|
499
|
+
|
|
500
|
+
Pie defaults to `store: false`. Optional `compat` fields are `store`,
|
|
501
|
+
`supportsStore`, `supportsReasoningEffort`, `supportsEncryptedReasoning`, and
|
|
502
|
+
`responsesContinuation`. Set the latter to true to enable verified same-connection
|
|
503
|
+
WS delta requests. SSE remains full-context. Local history remains authoritative;
|
|
504
|
+
rewind, compact, reset and model/credential changes discard or re-evaluate anchors.
|
|
505
|
+
Connection loss after submission is surfaced without automatic replay.
|
|
506
|
+
|
|
507
|
+
Text/images and function tools are supported; audio/video inputs fail explicitly.
|
|
508
|
+
Connection reuse, delta continuation and prompt-cache hits are separate facts.
|
|
509
|
+
See [transport validation and opt-in benchmarks](../../docs/maintenance/responses-transport.md).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createAskUserCapability
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
5
|
-
import "../../../chunks/chunk-
|
|
6
|
-
import "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-6RBSTII7.js";
|
|
5
|
+
import "../../../chunks/chunk-TWILEFLD.js";
|
|
6
|
+
import "../../../chunks/chunk-R5I2QLF2.js";
|
|
7
7
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
8
8
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
9
9
|
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
isPlanModeSafeCommand,
|
|
8
8
|
markCompletedPlanSteps,
|
|
9
9
|
restoreExecutionState
|
|
10
|
-
} from "../../../chunks/chunk-
|
|
11
|
-
import "../../../chunks/chunk-
|
|
12
|
-
import "../../../chunks/chunk-
|
|
10
|
+
} from "../../../chunks/chunk-6RBSTII7.js";
|
|
11
|
+
import "../../../chunks/chunk-TWILEFLD.js";
|
|
12
|
+
import "../../../chunks/chunk-R5I2QLF2.js";
|
|
13
13
|
import {
|
|
14
14
|
Type
|
|
15
15
|
} from "../../../chunks/chunk-XXGYUYXI.js";
|
|
@@ -26,10 +26,13 @@ var EXECUTION_FLAG_KEY = "planExecution";
|
|
|
26
26
|
var SCRATCH_METADATA_KEY = "planScratchId";
|
|
27
27
|
var SCRATCH_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
|
|
28
28
|
var MAX_SCRATCH_CHARS = 64e3;
|
|
29
|
+
var MAX_PLAN_STEPS = 50;
|
|
30
|
+
var MAX_PLAN_ITEM_CHARS = 500;
|
|
29
31
|
function toPlanTodoItems(state) {
|
|
30
32
|
return state.steps.map((step) => ({
|
|
31
33
|
step: step.id,
|
|
32
34
|
text: step.title,
|
|
35
|
+
detail: step.description || void 0,
|
|
33
36
|
completed: step.status === "completed"
|
|
34
37
|
}));
|
|
35
38
|
}
|
|
@@ -37,6 +40,8 @@ function planModeExtension(ctx) {
|
|
|
37
40
|
const capability = createPlanCapability();
|
|
38
41
|
let planModeEnabled = false;
|
|
39
42
|
let executingPlan = false;
|
|
43
|
+
let approvalOfferedThisTurn = false;
|
|
44
|
+
let lastOfferedPlanKey = "";
|
|
40
45
|
function setExecutingPlan(value) {
|
|
41
46
|
executingPlan = value;
|
|
42
47
|
ctx.setSessionMetadata(EXECUTION_FLAG_KEY, value);
|
|
@@ -113,6 +118,34 @@ function planModeExtension(ctx) {
|
|
|
113
118
|
ctx.setExecutionReminder(buildExecutionReminder(next) ?? void 0);
|
|
114
119
|
return next;
|
|
115
120
|
}
|
|
121
|
+
function formatPlanText(items) {
|
|
122
|
+
return items.map((item, index) => `${index + 1}. ${item.text}${item.detail ? `
|
|
123
|
+
${item.detail}` : ""}`).join("\n");
|
|
124
|
+
}
|
|
125
|
+
function renderPlanMarkdown(goal, items) {
|
|
126
|
+
const heading = goal ? `## \u{1F4CB} ${goal} \u2014 ${items.length} steps` : `## \u{1F4CB} Plan \u2014 ${items.length} steps`;
|
|
127
|
+
const steps = items.map((item, index) => {
|
|
128
|
+
const line = `${index + 1}. \u2610 ${item.text}`;
|
|
129
|
+
return item.detail ? `${line}
|
|
130
|
+
- \u21B3 ${item.detail}` : line;
|
|
131
|
+
}).join("\n");
|
|
132
|
+
return `${heading}
|
|
133
|
+
|
|
134
|
+
${steps}`;
|
|
135
|
+
}
|
|
136
|
+
function startPlanExecution(items) {
|
|
137
|
+
planModeEnabled = false;
|
|
138
|
+
ctx.setSessionMetadata(SESSION_METADATA_KEY, false);
|
|
139
|
+
ctx.setPlanMode(false);
|
|
140
|
+
setExecutingPlan(items.length > 0);
|
|
141
|
+
writeExecutionState(createExecutionStateFromPlan(items));
|
|
142
|
+
updateStatus();
|
|
143
|
+
return `Execute the approved plan:
|
|
144
|
+
|
|
145
|
+
${formatPlanText(items)}
|
|
146
|
+
|
|
147
|
+
Execute the steps in order, starting with step 1. After completing a step, include [DONE:n] in your response.`;
|
|
148
|
+
}
|
|
116
149
|
function updateStatus() {
|
|
117
150
|
const state = readExecutionState();
|
|
118
151
|
if (executingPlan && state.steps.length > 0) {
|
|
@@ -134,12 +167,83 @@ function planModeExtension(ctx) {
|
|
|
134
167
|
setExecutingPlan(false);
|
|
135
168
|
writeExecutionState(null);
|
|
136
169
|
ctx.setSessionMetadata(SESSION_METADATA_KEY, planModeEnabled);
|
|
170
|
+
approvalOfferedThisTurn = false;
|
|
171
|
+
lastOfferedPlanKey = "";
|
|
137
172
|
applyPlanModeState(planModeEnabled, true);
|
|
138
173
|
ctx.ui.notify(
|
|
139
174
|
planModeEnabled ? `Plan mode enabled. Available tools: ${ctx.getActiveTools().join(", ")}` : "Plan mode disabled. Full access restored."
|
|
140
175
|
);
|
|
141
176
|
updateStatus();
|
|
142
177
|
}
|
|
178
|
+
ctx.registerTool({
|
|
179
|
+
name: "plan_submit",
|
|
180
|
+
label: "plan_submit",
|
|
181
|
+
description: "Submit the finished plan for user approval. Only useful in Plan mode: the user chooses to execute now, keep planning, or refine the plan. Use short imperative step titles; put constraints, risks, and verification notes in each step's optional detail. Do not execute any step before the user approves.",
|
|
182
|
+
parameters: Type.Object({
|
|
183
|
+
goal: Type.Optional(Type.String({ description: "One-line summary of what the plan accomplishes" })),
|
|
184
|
+
steps: Type.Array(
|
|
185
|
+
Type.Object({
|
|
186
|
+
title: Type.String({ description: "Short imperative step title", maxLength: MAX_PLAN_ITEM_CHARS }),
|
|
187
|
+
detail: Type.Optional(Type.String({ description: "Constraints, risks, or verification notes for this step", maxLength: 2e3 }))
|
|
188
|
+
}),
|
|
189
|
+
{ minItems: 1, maxItems: MAX_PLAN_STEPS }
|
|
190
|
+
)
|
|
191
|
+
}),
|
|
192
|
+
risk: { riskClass: "session_mutation", permissionScope: "session", mutatesSession: true, availableInPlanMode: true },
|
|
193
|
+
async execute(args) {
|
|
194
|
+
const reply = (text, isError = false) => ({ content: [{ type: "text", text }], isError });
|
|
195
|
+
if (!planModeEnabled) {
|
|
196
|
+
return reply("plan_submit is only available in Plan mode. Enter Plan mode with /tools/plan first, or present the plan as text if you are not planning.", true);
|
|
197
|
+
}
|
|
198
|
+
const items = [];
|
|
199
|
+
for (const step of (args.steps ?? []).slice(0, MAX_PLAN_STEPS)) {
|
|
200
|
+
const title = String(step?.title ?? "").trim().slice(0, MAX_PLAN_ITEM_CHARS);
|
|
201
|
+
if (!title) continue;
|
|
202
|
+
const detail = typeof step?.detail === "string" && step.detail.trim() ? step.detail.trim().slice(0, 2e3) : void 0;
|
|
203
|
+
items.push({ step: items.length + 1, text: title, detail, completed: false });
|
|
204
|
+
}
|
|
205
|
+
if (items.length === 0) {
|
|
206
|
+
return reply("plan_submit requires at least one step with a non-empty title.", true);
|
|
207
|
+
}
|
|
208
|
+
approvalOfferedThisTurn = true;
|
|
209
|
+
lastOfferedPlanKey = JSON.stringify(items);
|
|
210
|
+
const rendered = items.map((item) => `${item.step}. \u2610 ${item.text}${item.detail ? `
|
|
211
|
+
${item.detail}` : ""}`).join("\n");
|
|
212
|
+
if (!ctx.hasUI) {
|
|
213
|
+
return reply(`Plan submitted (${items.length} steps) in headless mode; interactive approval is unavailable, so nothing will be executed:
|
|
214
|
+
|
|
215
|
+
${rendered}`);
|
|
216
|
+
}
|
|
217
|
+
const goal = typeof args.goal === "string" && args.goal.trim() ? args.goal.trim() : void 0;
|
|
218
|
+
ctx.ui.notify(renderPlanMarkdown(goal, items), "info", { markdown: true });
|
|
219
|
+
const yoloOption = typeof ctx.setYoloMode === "function" && !ctx.yoloMode ? ["Execute in YOLO mode (auto-approve everything)"] : [];
|
|
220
|
+
const choice = await ctx.ui.select(`\u{1F4CB} Plan (${items.length} steps) \u2014 what next?`, [
|
|
221
|
+
"Execute the plan (track progress)",
|
|
222
|
+
...yoloOption,
|
|
223
|
+
"Stay in plan mode",
|
|
224
|
+
"Refine the plan"
|
|
225
|
+
]);
|
|
226
|
+
if (choice?.startsWith("Execute")) {
|
|
227
|
+
if (choice.includes("YOLO")) {
|
|
228
|
+
ctx.setYoloMode?.(true);
|
|
229
|
+
ctx.ui.notify("\u26A0\uFE0F YOLO mode enabled for plan execution (sandbox directory restrictions disabled).", "warning");
|
|
230
|
+
}
|
|
231
|
+
return reply(startPlanExecution(items));
|
|
232
|
+
}
|
|
233
|
+
if (choice === "Refine the plan") {
|
|
234
|
+
const refinement = await ctx.ui.editor("Refine the plan (edit the plan or add feedback):", formatPlanText(items));
|
|
235
|
+
if (refinement?.trim()) {
|
|
236
|
+
return reply(`The user refined the plan:
|
|
237
|
+
|
|
238
|
+
${refinement.trim()}
|
|
239
|
+
|
|
240
|
+
Update the plan accordingly and call plan_submit again with the revised steps. Do not execute anything yet.`);
|
|
241
|
+
}
|
|
242
|
+
return reply("The user closed refinement without changes. Stay in Plan mode and wait for guidance.");
|
|
243
|
+
}
|
|
244
|
+
return reply("The user chose to stay in Plan mode. Do not execute anything; wait for guidance or refine the plan when asked.");
|
|
245
|
+
}
|
|
246
|
+
});
|
|
143
247
|
ctx.registerCommand({
|
|
144
248
|
path: capability.commands?.[0]?.path ?? ["tools", "plan"],
|
|
145
249
|
description: capability.commands?.[0]?.description ?? "Toggle plan mode (read-only exploration)",
|
|
@@ -158,23 +262,27 @@ function planModeExtension(ctx) {
|
|
|
158
262
|
const command = typeof event.args === "object" && event.args !== null ? event.args.command : void 0;
|
|
159
263
|
if (typeof command === "string" && !isPlanModeSafeCommand(command)) {
|
|
160
264
|
ctx.ui.notify(
|
|
161
|
-
`Plan mode: Command blocked. Use read-only commands
|
|
265
|
+
`Plan mode: Command blocked. Use read-only commands or plan_scratch for temporary drafts; submit the finished plan with plan_submit. Use /tools/plan to leave Plan mode.
|
|
162
266
|
Command: ${command}`,
|
|
163
267
|
"error"
|
|
164
268
|
);
|
|
165
|
-
return { block: true, reason: `Plan mode: command "${command}" is not a read-only shell command. Use plan_scratch for temporary drafts, or /plan to leave Plan mode.` };
|
|
269
|
+
return { block: true, reason: `Plan mode: command "${command}" is not a read-only shell command. Use plan_scratch for temporary drafts, submit the finished plan with plan_submit, or use /tools/plan to leave Plan mode.` };
|
|
166
270
|
}
|
|
167
271
|
});
|
|
168
272
|
ctx.on("agent:start", async () => {
|
|
169
273
|
const persisted = readPersistedPlanMode();
|
|
170
274
|
applyPlanModeState(persisted);
|
|
171
275
|
executingPlan = !persisted && readPersistedExecuting();
|
|
276
|
+
approvalOfferedThisTurn = false;
|
|
277
|
+
if (!persisted) lastOfferedPlanKey = "";
|
|
172
278
|
writeExecutionState(readExecutionState());
|
|
173
279
|
});
|
|
174
280
|
ctx.on("session:changed", async () => {
|
|
175
281
|
const enabled = readPersistedPlanMode();
|
|
176
282
|
applyPlanModeState(enabled, enabled);
|
|
177
283
|
executingPlan = !enabled && readPersistedExecuting();
|
|
284
|
+
approvalOfferedThisTurn = false;
|
|
285
|
+
lastOfferedPlanKey = "";
|
|
178
286
|
writeExecutionState(readExecutionState());
|
|
179
287
|
});
|
|
180
288
|
ctx.on("turn:end", async (event) => {
|
|
@@ -193,16 +301,18 @@ Command: ${command}`,
|
|
|
193
301
|
const state = readExecutionState();
|
|
194
302
|
if (executingPlan && state.steps.length > 0) {
|
|
195
303
|
if (state.steps.every((step) => step.status === "completed")) {
|
|
196
|
-
const completedList = state.steps.map((step) =>
|
|
197
|
-
|
|
304
|
+
const completedList = state.steps.map((step) => `${step.id}. \u2713 ${step.title}${step.description ? `
|
|
305
|
+
- \u21B3 ${step.description}` : ""}`).join("\n");
|
|
306
|
+
ctx.ui.notify(`## \u2705 Plan complete
|
|
198
307
|
|
|
199
|
-
${completedList}`, "info");
|
|
308
|
+
${completedList}`, "info", { markdown: true });
|
|
200
309
|
setExecutingPlan(false);
|
|
201
310
|
writeExecutionState(null);
|
|
202
311
|
}
|
|
203
312
|
return;
|
|
204
313
|
}
|
|
205
314
|
if (!planModeEnabled || !ctx.hasUI) return;
|
|
315
|
+
if (approvalOfferedThisTurn) return;
|
|
206
316
|
const messages = event.messages || [];
|
|
207
317
|
const lastAssistant = [...messages].reverse().find(
|
|
208
318
|
(message) => message.role === "assistant" && Array.isArray(message.content)
|
|
@@ -213,27 +323,26 @@ ${completedList}`, "info");
|
|
|
213
323
|
const text = content.filter((item) => item.type === "text").map((item) => item.text).join("\n");
|
|
214
324
|
const extracted = extractPlanTodoItems(text);
|
|
215
325
|
if (extracted.length === 0) return;
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
326
|
+
const planKey = JSON.stringify(extracted);
|
|
327
|
+
if (planKey === lastOfferedPlanKey) return;
|
|
328
|
+
lastOfferedPlanKey = planKey;
|
|
329
|
+
approvalOfferedThisTurn = true;
|
|
330
|
+
const todoListText = extracted.map((item, index) => `${index + 1}. \u2610 ${item.text}${item.detail ? `
|
|
331
|
+
- \u21B3 ${item.detail}` : ""}`).join("\n");
|
|
332
|
+
ctx.ui.notify(`## \u{1F4CB} Plan \u2014 ${extracted.length} steps
|
|
333
|
+
|
|
334
|
+
${todoListText}`, "info", { markdown: true });
|
|
335
|
+
const choice = await ctx.ui.select(`\u{1F4CB} Plan (${extracted.length} steps) \u2014 what next?`, [
|
|
221
336
|
"Execute the plan (track progress)",
|
|
222
337
|
"Stay in plan mode",
|
|
223
338
|
"Refine the plan"
|
|
224
339
|
]);
|
|
225
340
|
if (choice?.startsWith("Execute")) {
|
|
226
|
-
|
|
227
|
-
setExecutingPlan(extractedState.steps.length > 0);
|
|
228
|
-
ctx.setSessionMetadata(SESSION_METADATA_KEY, false);
|
|
229
|
-
ctx.setPlanMode(false);
|
|
230
|
-
updateStatus();
|
|
231
|
-
const execMessage = extractedState.steps.length > 0 ? `Execute the plan. Start with: ${extractedState.steps[0].title}. Mark completed steps with [DONE:n] in your response.` : "Execute the plan you just created.";
|
|
232
|
-
ctx.sendUserMessage(execMessage);
|
|
341
|
+
ctx.sendUserMessage(startPlanExecution(extracted));
|
|
233
342
|
return;
|
|
234
343
|
}
|
|
235
344
|
if (choice === "Refine the plan") {
|
|
236
|
-
const refinement = await ctx.ui.editor("Refine the plan:",
|
|
345
|
+
const refinement = await ctx.ui.editor("Refine the plan (edit the plan or add feedback):", formatPlanText(extracted));
|
|
237
346
|
if (refinement?.trim()) {
|
|
238
347
|
ctx.sendUserMessage(refinement.trim());
|
|
239
348
|
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createCliHostCapabilities
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-IPNAFMOJ.js";
|
|
5
5
|
import {
|
|
6
6
|
SUBAGENT_SESSION_RECORD_VERSION,
|
|
7
7
|
createSharedFileSystemTools,
|
|
8
8
|
createSubagentCapability
|
|
9
|
-
} from "../../../chunks/chunk-
|
|
10
|
-
import "../../../chunks/chunk-
|
|
11
|
-
import "../../../chunks/chunk-
|
|
12
|
-
import "../../../chunks/chunk-5353DEEC.js";
|
|
9
|
+
} from "../../../chunks/chunk-6RBSTII7.js";
|
|
10
|
+
import "../../../chunks/chunk-TWILEFLD.js";
|
|
11
|
+
import "../../../chunks/chunk-R5I2QLF2.js";
|
|
13
12
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
14
13
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
15
14
|
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
executeManageTodoList,
|
|
9
9
|
executionStateToTodos,
|
|
10
10
|
restoreExecutionState
|
|
11
|
-
} from "../../../chunks/chunk-
|
|
12
|
-
import "../../../chunks/chunk-
|
|
13
|
-
import "../../../chunks/chunk-
|
|
11
|
+
} from "../../../chunks/chunk-6RBSTII7.js";
|
|
12
|
+
import "../../../chunks/chunk-TWILEFLD.js";
|
|
13
|
+
import "../../../chunks/chunk-R5I2QLF2.js";
|
|
14
14
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
15
15
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
16
16
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: computer-use-axi
|
|
3
|
+
description: "Drive desktop apps and managed CDP browser pages through the computer-use-axi CLI run via bash: list UI roots, capture bounded outlines, search/expand/inspect element refs, perform checked actions with postconditions, and wait for conditions. Use whenever a task needs real UI interaction - clicking through a native app, reading a screen, driving a desktop window, or acting on a browser page state. Read this before the first UI-control task or after any command usage error."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# computer-use-axi
|
|
7
|
+
|
|
8
|
+
Resolve the launcher against this skill's `baseDir` and run it through the
|
|
9
|
+
host's shell tool (axi-style; on Windows use `shell:auto`/PowerShell, not
|
|
10
|
+
bash.exe):
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
node <baseDir>/computer-use-axi-cli.js <command> [options]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Desktop control is a last resort — prefer API/CLI/web tools when they suffice.
|
|
17
|
+
First use on macOS installs the `pie-computer-use.app` helper; the CLI then
|
|
18
|
+
asks you to grant it Accessibility and Screen Recording permissions.
|
|
19
|
+
|
|
20
|
+
## Current guidance lives in the CLI
|
|
21
|
+
|
|
22
|
+
`--help` prints the full command surface; each command's output carries its
|
|
23
|
+
own next-step guidance. This file covers the flow semantics.
|
|
24
|
+
|
|
25
|
+
## The loop
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
node <baseDir>/computer-use-axi-cli.js roots # list @r roots
|
|
29
|
+
node <baseDir>/computer-use-axi-cli.js observe --root @r1 # outline + stateId
|
|
30
|
+
node <baseDir>/computer-use-axi-cli.js search --state S1 --text Save
|
|
31
|
+
node <baseDir>/computer-use-axi-cli.js act --state S1 --actions '[{"action":"press","ref":"@e12"}]'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
1. `roots` finds controllable windows/pages and mints `@r` refs.
|
|
35
|
+
2. `observe` captures one root and returns a folded outline plus a `stateId`.
|
|
36
|
+
`--mode semantic` is cheapest (no OCR/images), `fused` is the default,
|
|
37
|
+
`visual` forces image evidence.
|
|
38
|
+
3. `search` / `expand` / `inspect` query that saved state (`--state S1`) —
|
|
39
|
+
no re-observation needed. Search needs at least one of `--text`, `--role`,
|
|
40
|
+
`--capability`; refine broad queries instead of paging.
|
|
41
|
+
4. `act` performs checked actions against the same state and returns the
|
|
42
|
+
successor state with a new `stateId` — use it directly for follow-ups.
|
|
43
|
+
Observe again only after uncertain external changes or eviction errors.
|
|
44
|
+
|
|
45
|
+
## Refs and state
|
|
46
|
+
|
|
47
|
+
- `@rN` root refs come from `roots`; they stay stable per window identity.
|
|
48
|
+
- `@eN` element refs + the owning `stateId` come from `observe`/`search`;
|
|
49
|
+
every state-consuming command takes `--state`.
|
|
50
|
+
- States are stale-checked by epoch: acting twice from one stateId fails the
|
|
51
|
+
second time by design — consume the successor stateId the act output names.
|
|
52
|
+
- `@oN` refs continue long truncated output: `read --ref @o1 --offset N`
|
|
53
|
+
(byte offsets; no `--state` needed for `@o`).
|
|
54
|
+
- `pictureOnly` nodes have no semantic element: coordinate actions only, and
|
|
55
|
+
only from an image-bearing (visual) state.
|
|
56
|
+
|
|
57
|
+
## Acting and batching
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node <baseDir>/computer-use-axi-cli.js act --state S1 --actions '[
|
|
61
|
+
{"action":"click","ref":"@e18"},
|
|
62
|
+
{"action":"typeText","text":"hello"}
|
|
63
|
+
]' --expect '{"text":"hello"}'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- Batch steps only when the second does not need to inspect the first's
|
|
67
|
+
result; steps run sequentially in one transaction.
|
|
68
|
+
- Attach `--expect` (JSON condition: `text`/`role`/`value`/`ref`/`scope-ref`/
|
|
69
|
+
`until`/`timeout-ms`) when an action has an observable completion signal —
|
|
70
|
+
but only within the same root: a modal/sheet or a closing window is a NEW
|
|
71
|
+
root, so expect cannot see it; run the action without `--expect` and read
|
|
72
|
+
the returned root note instead.
|
|
73
|
+
- Focus-following works only inside one act transaction: after a click into
|
|
74
|
+
an editable region, later `typeText`/`keypress` steps in the SAME
|
|
75
|
+
`--actions` array may omit `ref`. A standalone keypress (for example
|
|
76
|
+
`["cmd","w"]`) must carry `ref` (the window or a specific element).
|
|
77
|
+
- On failure the output still names the successor state — inspect it instead
|
|
78
|
+
of blindly replaying.
|
|
79
|
+
- Acting requires the platform helper: on macOS first use installs
|
|
80
|
+
`pie-computer-use.app` and asks for Accessibility + Screen Recording
|
|
81
|
+
grants. If a helper/setup error appears, run
|
|
82
|
+
`node <baseDir>/scripts/setup-helper.mjs` once, grant the permissions, and
|
|
83
|
+
retry.
|
|
84
|
+
|
|
85
|
+
## Browser pages
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
node <baseDir>/computer-use-axi-cli.js browser launch --url https://example.com
|
|
89
|
+
node <baseDir>/computer-use-axi-cli.js browser navigate --state S1 --url https://other.example
|
|
90
|
+
node <baseDir>/computer-use-axi-cli.js browser eval --state S1 --expr 'document.title'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Browser states use the same outline/action/condition contracts as desktop
|
|
94
|
+
states. Native browser windows are ordinary desktop UI — use `roots`/
|
|
95
|
+
`observe`/`act` on them. Browser control may need enabling via the
|
|
96
|
+
computer-use config (`browser_use`).
|
|
97
|
+
|
|
98
|
+
## Output bounds and exit codes
|
|
99
|
+
|
|
100
|
+
Model-facing output is capped (~48 KiB / 2 000 lines); oversized results
|
|
101
|
+
return a preview plus an `@o` continuation ref. Exit codes: `0` ok, `1` tool
|
|
102
|
+
error (message on stderr), `2` usage error. `--json` wraps
|
|
103
|
+
`{ content, details, isError }` for programmatic use.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thin launcher for Pie's packaged computer-use-axi runtime (mirrors the
|
|
5
|
+
* chrome-devtools-axi launcher). Resolves the esbuild bundle next to this
|
|
6
|
+
* file and runs it with the same argv; an explicit override wins when the
|
|
7
|
+
* skill directory is copied out of the package.
|
|
8
|
+
*/
|
|
9
|
+
import { spawnSync } from "node:child_process";
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
export function resolveComputerUseAxiCli() {
|
|
15
|
+
const override = process.env.PIE_COMPUTER_USE_AXI_CLI_PATH?.trim();
|
|
16
|
+
if (override) return override;
|
|
17
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
for (const candidate of [
|
|
19
|
+
path.join(here, "computer-use-axi-runtime.js"),
|
|
20
|
+
path.resolve(here, "../../../dist/builtin/skills/computer-use-axi/computer-use-axi-runtime.js"),
|
|
21
|
+
]) {
|
|
22
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
23
|
+
}
|
|
24
|
+
throw new Error("Pie's computer-use-axi runtime is missing. Rebuild or reinstall Pie.");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function main() {
|
|
28
|
+
const runtime = resolveComputerUseAxiCli();
|
|
29
|
+
const result = spawnSync(process.execPath, [runtime, ...process.argv.slice(2)], {
|
|
30
|
+
stdio: "inherit",
|
|
31
|
+
shell: false,
|
|
32
|
+
env: process.env,
|
|
33
|
+
});
|
|
34
|
+
process.exit(result.status ?? 1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
main();
|