@cydm/pie 2.6.12 → 2.7.1

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.
Files changed (35) hide show
  1. package/README.md +85 -3
  2. package/dist/builtin/extensions/ask-user/index.js +3 -3
  3. package/dist/builtin/extensions/plan-mode/index.js +130 -21
  4. package/dist/builtin/extensions/subagent/index.js +15 -6
  5. package/dist/builtin/extensions/todo/index.js +3 -3
  6. package/dist/builtin/skills/computer-use-axi/SKILL.md +172 -0
  7. package/dist/builtin/{extensions/computer-use/index.js → skills/computer-use-axi/computer-use-axi-broker.js} +2122 -708
  8. package/dist/builtin/skills/computer-use-axi/computer-use-axi-cli.js +37 -0
  9. package/dist/builtin/skills/computer-use-axi/computer-use-axi-runtime.js +728 -0
  10. package/dist/builtin/skills/computer-use-axi/package.json +11 -0
  11. package/dist/chunks/{chunk-PPEWRUMQ.js → chunk-M44CWVDZ.js} +1249 -752
  12. package/dist/chunks/{chunk-HHNOENEH.js → chunk-NJFPBU36.js} +14 -1
  13. package/dist/chunks/{chunk-AAEBH6KX.js → chunk-W74NRSRV.js} +239 -231
  14. package/dist/chunks/{chunk-7B4IIBVS.js → chunk-XUROMYTT.js} +1747 -14
  15. package/dist/chunks/{src-OWQI5M6W.js → src-C4KA4G75.js} +2 -2
  16. package/dist/chunks/{test-stream-Y7WXELAJ.js → test-stream-A5ZELKNG.js} +1 -1
  17. package/dist/cli.js +863 -305
  18. package/models.schema.json +57 -0
  19. package/package.json +3 -3
  20. package/dist/builtin/extensions/computer-use/package.json +0 -14
  21. package/dist/builtin/extensions/computer-use/scripts/build-native.mjs +0 -278
  22. package/dist/builtin/extensions/computer-use/scripts/test-native-retention.mjs +0 -204
  23. package/dist/chunks/chunk-5353DEEC.js +0 -130
  24. package/dist/chunks/webapi-MBCKO7ZV.js +0 -2481
  25. package/dist/code-intel-worker.cjs +0 -211227
  26. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/arm64/linux-bridge +0 -0
  27. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/x64/linux-bridge +0 -0
  28. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/arm64/pie-computer-use +0 -0
  29. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/Info.plist +0 -0
  30. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/MacOS/pie-computer-use +0 -0
  31. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/_CodeSignature/CodeResources +0 -0
  32. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/x64/pie-computer-use +0 -0
  33. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/windows/windows-bridge.exe +0 -0
  34. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/helper-path.mjs +0 -0
  35. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/setup-helper.mjs +0 -0
package/README.md CHANGED
@@ -83,7 +83,7 @@ Required model fields:
83
83
 
84
84
  - `id`
85
85
  - `input`
86
- - `cost`
86
+ - `cost` (`input`, `output`, `cacheRead`, `cacheWrite`; optional `cacheWriteLong`)
87
87
  - `contextWindow`
88
88
  - `maxTokens`
89
89
 
@@ -104,6 +104,22 @@ pie models init --stdout
104
104
 
105
105
  For editor auto-complete and validation, you can also point the optional `"$schema"` field in your local `models.json` at your installed `@cydm/pie/models.schema.json` file.
106
106
 
107
+ `cost` may additionally carry `cacheWriteLong` — the per-million-token rate for 1h-TTL cache writes on providers that report a TTL breakdown (Anthropic-style routes). It is optional; legacy configs without it keep working. When it is absent, long writes are priced at the Anthropic 2x-input estimate and the calculated cost is marked `cacheWriteLongEstimated` so it is not presented as a precise provider bill.
108
+
109
+ ### Settings
110
+
111
+ `~/.pie/settings.json` (or `./.pie/settings.json` for project scope) holds CLI defaults. Relevant prompt-cache keys:
112
+
113
+ ```jsonc
114
+ {
115
+ // Explicit Anthropic prompt-cache TTL: "none" | "short" | "long".
116
+ // Absent keeps the provider default (Anthropic: short/5m).
117
+ "promptCacheRetention": "short"
118
+ }
119
+ ```
120
+
121
+ Priority is **per-request option > CLI setting > provider default**. The setting is forwarded only on Anthropic prompt-cache routes; it never forces OpenAI-compatible `store` or another provider's cache mode. Pie does not auto-switch TTL from observed hit rates — choose `long` explicitly when the 1h write premium is worth it.
122
+
107
123
  ### Remote model catalogs
108
124
 
109
125
  Pie can add centrally managed models without replacing local profiles or defaults. Add a catalog to `~/.pie/models.json` and keep the virtual key in an environment variable:
@@ -335,7 +351,7 @@ Platform notes:
335
351
  - **Windows**: the helper installs to `~/.pie/helpers/computer-use/windows-bridge.exe` and needs an interactive desktop session.
336
352
  - **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
353
 
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`. Run `/computer-use` (slash command) to inspect the loaded config, and `pie doctor` for helper install status. The extension can be toggled like any other (`/settings/extensions`, identity `computer-use`).
354
+ 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
355
 
340
356
  ## Web Research
341
357
 
@@ -349,7 +365,7 @@ Pie exposes one default model-visible web tool: `web_research`. Use it for publi
349
365
 
350
366
  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
367
 
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.
368
+ 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
369
 
354
370
  ## Code Intelligence
355
371
 
@@ -441,3 +457,69 @@ If the package version already exists on npm, bump `products/cli/package.json` f
441
457
  ## License
442
458
 
443
459
  MIT
460
+
461
+ ### Experimental Responses profiles
462
+
463
+ In `models.json`, set a profile's `api` to `"openai-responses"`, `baseUrl` to
464
+ its API root (for example `https://api.openai.com/v1`), and `apiKeyEnv` to your
465
+ credential environment variable. Supply model IDs and limits in `models` as
466
+ usual. Optional profile/model `transport` is `"sse"` (default), `"websocket"`
467
+ (strict), or `"auto"` (WS with conservative pre-submission SSE fallback).
468
+ Model transport overrides profile transport. API selection is explicit.
469
+
470
+ A profile may declare `endpoints: { "<api>": "https://..." }` and each model
471
+ may select `api` (otherwise it inherits the profile API). Supported keys are
472
+ `openai-completions`, `openai-responses`, and `anthropic-messages`; endpoint URLs
473
+ follow the same absolute HTTP(S) validation as `baseUrl`. The resolved API uses
474
+ `endpoints[api]` when present, otherwise `baseUrl`. An explicitly different model
475
+ API **requires** its endpoint entry; an explicit same-API selection can use
476
+ `baseUrl`. Remote catalogs additionally require every endpoint to share the
477
+ authenticated catalog origin. `modelOverrides` cannot change these routes.
478
+
479
+ For example, Bigmodel's Chat Completions and Responses roots differ. This is a
480
+ configuration example, not special provider logic; use distinct model IDs within
481
+ the profile (replace the Chat model ID with your supported model):
482
+
483
+ ```json
484
+ {
485
+ "profiles": {
486
+ "bigmodel": {
487
+ "api": "openai-completions",
488
+ "baseUrl": "https://open.bigmodel.cn/api/coding/paas/v4",
489
+ "endpoints": {
490
+ "openai-responses": "https://open.bigmodel.cn/api/v1"
491
+ },
492
+ "apiKeyEnv": "BIGMODEL_API_KEY",
493
+ "transport": "sse",
494
+ "models": [
495
+ {
496
+ "id": "glm-5.2", "api": "openai-completions", "input": ["text"],
497
+ "contextWindow": 128000, "maxTokens": 4096,
498
+ "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
499
+ },
500
+ {
501
+ "id": "glm-5.3", "api": "openai-responses", "input": ["text"],
502
+ "contextWindow": 128000, "maxTokens": 4096,
503
+ "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
504
+ }
505
+ ]
506
+ }
507
+ }
508
+ }
509
+ ```
510
+
511
+ Limits/prices above are illustrative, not provider metadata. Keep Bigmodel and
512
+ Kimi Responses on SSE for the tested endpoints/accounts: Responses support does
513
+ not imply usable WS sessions. Current benchmarks show continuation upload savings,
514
+ not consistent latency improvement; SSE stays default, WS/continuation opt-in.
515
+
516
+ Pie defaults to `store: false`. Optional `compat` fields are `store`,
517
+ `supportsStore`, `supportsReasoningEffort`, `supportsEncryptedReasoning`, and
518
+ `responsesContinuation`. Set the latter to true to enable verified same-connection
519
+ WS delta requests. SSE remains full-context. Local history remains authoritative;
520
+ rewind, compact, reset and model/credential changes discard or re-evaluate anchors.
521
+ Connection loss after submission is surfaced without automatic replay.
522
+
523
+ Text/images and function tools are supported; audio/video inputs fail explicitly.
524
+ Connection reuse, delta continuation and prompt-cache hits are separate facts.
525
+ 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-PPEWRUMQ.js";
5
- import "../../../chunks/chunk-HHNOENEH.js";
6
- import "../../../chunks/chunk-7B4IIBVS.js";
4
+ } from "../../../chunks/chunk-M44CWVDZ.js";
5
+ import "../../../chunks/chunk-NJFPBU36.js";
6
+ import "../../../chunks/chunk-XUROMYTT.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-PPEWRUMQ.js";
11
- import "../../../chunks/chunk-HHNOENEH.js";
12
- import "../../../chunks/chunk-7B4IIBVS.js";
10
+ } from "../../../chunks/chunk-M44CWVDZ.js";
11
+ import "../../../chunks/chunk-NJFPBU36.js";
12
+ import "../../../chunks/chunk-XUROMYTT.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, or plan_scratch for temporary drafts. Use /plan to leave Plan mode.
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) => `\u2713 ${step.title}`).join("\n");
197
- ctx.ui.notify(`**Plan Complete!** \u2713
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 extractedState = writeExecutionState(createExecutionStateFromPlan(extracted));
217
- const todoListText = extractedState.steps.map((step) => `${step.id}. \u2610 ${step.title}`).join("\n");
218
- ctx.ui.notify(`**Plan Steps (${extractedState.steps.length}):**
219
- ${todoListText}`, "info");
220
- const choice = await ctx.ui.select("Plan mode - what next?", [
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
- planModeEnabled = false;
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,16 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
- createCliHostCapabilities
4
- } from "../../../chunks/chunk-AAEBH6KX.js";
3
+ createCliHostCapabilities,
4
+ formatTransportLifecycle,
5
+ logTransportLifecycle
6
+ } from "../../../chunks/chunk-W74NRSRV.js";
5
7
  import {
6
8
  SUBAGENT_SESSION_RECORD_VERSION,
7
9
  createSharedFileSystemTools,
8
10
  createSubagentCapability
9
- } from "../../../chunks/chunk-PPEWRUMQ.js";
10
- import "../../../chunks/chunk-HHNOENEH.js";
11
- import "../../../chunks/chunk-7B4IIBVS.js";
12
- import "../../../chunks/chunk-5353DEEC.js";
11
+ } from "../../../chunks/chunk-M44CWVDZ.js";
12
+ import "../../../chunks/chunk-NJFPBU36.js";
13
+ import "../../../chunks/chunk-XUROMYTT.js";
13
14
  import "../../../chunks/chunk-XXGYUYXI.js";
14
15
  import "../../../chunks/chunk-LZQXQOPJ.js";
15
16
 
@@ -393,6 +394,14 @@ function subagentExtension(ctx) {
393
394
  log: (message) => ctx.log(message),
394
395
  notify: ctx.hasUI ? (message, type) => ctx.ui.notify(message, type === "success" ? "info" : type) : void 0,
395
396
  debugLog: (module, message, data) => ctx.log(`[subagent:${module}] ${message}${data !== void 0 ? ` ${JSON.stringify(data)}` : ""}`),
397
+ // Body-free Responses transport timeline for CHILD agents: 5 of the 7
398
+ // incident failures happened inside subagents with no transport record.
399
+ onTransportLifecycle: (event) => {
400
+ logTransportLifecycle(event);
401
+ if (event.phase === "timeout" || event.cleanupOutcome === "request_abandoned") {
402
+ ctx.log(formatTransportLifecycle(event));
403
+ }
404
+ },
396
405
  resolveModelClass: (modelClass) => ctx.resolveModelClass?.(modelClass),
397
406
  resolveModelRef: (modelRef) => ctx.resolveModelRef?.(modelRef),
398
407
  createRuntimeTools: (mode) => {
@@ -8,9 +8,9 @@ import {
8
8
  executeManageTodoList,
9
9
  executionStateToTodos,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-PPEWRUMQ.js";
12
- import "../../../chunks/chunk-HHNOENEH.js";
13
- import "../../../chunks/chunk-7B4IIBVS.js";
11
+ } from "../../../chunks/chunk-M44CWVDZ.js";
12
+ import "../../../chunks/chunk-NJFPBU36.js";
13
+ import "../../../chunks/chunk-XUROMYTT.js";
14
14
  import "../../../chunks/chunk-XXGYUYXI.js";
15
15
  import "../../../chunks/chunk-LZQXQOPJ.js";
16
16
 
@@ -0,0 +1,172 @@
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
+ ## How it runs
21
+
22
+ The CLI is a short-lived client. A local **broker** owns the execution engine,
23
+ native helper handles, observation state, resource scheduling, `@o` output
24
+ refs, and image artifacts. The broker starts on demand, is shared by every Pie
25
+ session on this desktop, and exits after 30 minutes with no in-flight or
26
+ unconfirmed work. It is not a system service and opens no network listener
27
+ (Unix socket on macOS/Linux, per-user named pipe on Windows).
28
+
29
+ ```bash
30
+ node <baseDir>/computer-use-axi-cli.js status # broker pid/generation/queues
31
+ node <baseDir>/computer-use-axi-cli.js stop # safe shutdown, owns only its processes
32
+ ```
33
+
34
+ A broker restart creates a new generation. State ids from the old generation
35
+ are reported as expired; they are never restored or replayed — run `roots` and
36
+ `observe` again.
37
+
38
+ `stop` is a safe drain, not a hard kill. While a native action is still
39
+ settling, the stopping broker keeps its endpoint bound and rejects new
40
+ connections with `broker_draining`; a bootstrapping broker waits for that
41
+ drain instead of starting a second execution authority. Retry shortly after a
42
+ `broker_draining` rejection rather than racing another broker.
43
+
44
+ ## Current guidance lives in the CLI
45
+
46
+ `--help` prints the full command surface; each command's output carries its
47
+ own next-step guidance. This file covers the flow semantics.
48
+
49
+ ## The loop
50
+
51
+ ```bash
52
+ node <baseDir>/computer-use-axi-cli.js roots # list @r roots
53
+ node <baseDir>/computer-use-axi-cli.js observe --root @r1 # outline + stateId
54
+ node <baseDir>/computer-use-axi-cli.js search --state S1 --text Save
55
+ node <baseDir>/computer-use-axi-cli.js act --state S1 --actions '[{"action":"press","ref":"@e12"}]'
56
+ ```
57
+
58
+ 1. `roots` finds controllable windows/pages and mints `@r` refs.
59
+ 2. `observe` captures one root and returns a folded outline plus a `stateId`.
60
+ `--mode semantic` is cheapest (no OCR/images), `fused` is the default,
61
+ `visual` forces image evidence.
62
+ 3. `search` / `expand` / `inspect` query that saved state (`--state S1`) —
63
+ no re-observation needed. Search needs at least one of `--text`, `--role`,
64
+ `--capability`; refine broad queries instead of paging.
65
+ 4. `act` performs checked actions against the same state and returns the
66
+ successor state with a new `stateId` — use it directly for follow-ups.
67
+ Observe again only after uncertain external changes or eviction errors.
68
+
69
+ ## Refs and state
70
+
71
+ - `@rN` root refs come from `roots`; they stay stable per window identity.
72
+ - `@eN` element refs + the owning `stateId` come from `observe`/`search`;
73
+ every state-consuming command takes `--state`.
74
+ - States are stale-checked by epoch: acting twice from one stateId fails the
75
+ second time by design — consume the successor stateId the act output names.
76
+ - `@oN` refs continue long truncated output: `read --ref @o1 --offset N`
77
+ (byte offsets; no `--state` needed for `@o`). The broker allocates these.
78
+ - `pictureOnly` nodes have no semantic element: coordinate actions only, and
79
+ only from an image-bearing (visual) state.
80
+
81
+ ## Image evidence
82
+
83
+ Visual observations do not print base64. The broker writes the original
84
+ screenshot to a private file (`0700` directory, `0600` file under the Pie
85
+ config directory) and the text result names its absolute path, dimensions,
86
+ sha256, broker generation, and owning `stateId`.
87
+
88
+ When the result includes an `Image evidence:` line, call the normal
89
+ `read_file` tool on that absolute path before using coordinates. The image
90
+ belongs only to the returned `stateId`: after acting, use the successor
91
+ image/state, not the old one. Artifacts are evicted with their state; if the
92
+ file is gone, observe again instead of guessing coordinates. The broker never
93
+ scales or transforms the bytes it stores.
94
+
95
+ ## Owners and concurrency
96
+
97
+ The Pie host passes an opaque owner identity into the shell; separate sessions
98
+ have separate observations, `@o` refs, and artifact directories. Manual CLI
99
+ use without that environment resolves to a stable `standalone` namespace.
100
+ Owners are **not** an OS security boundary — every client is the same local
101
+ user. Two owners acting on the same app/page share one resource lane and one
102
+ epoch counter, so the second write is rejected as stale rather than
103
+ overlapping. `status` reports only an owner **count** and non-reversible
104
+ fingerprints; it never prints another session's owner id.
105
+
106
+ ## Acting and batching
107
+
108
+ ```bash
109
+ node <baseDir>/computer-use-axi-cli.js act --state S1 --actions '[
110
+ {"action":"click","ref":"@e18"},
111
+ {"action":"typeText","text":"hello"}
112
+ ]' --expect '{"text":"hello"}'
113
+ ```
114
+
115
+ - Batch steps only when the second does not need to inspect the first's
116
+ result; steps run sequentially in one transaction.
117
+ - Attach `--expect` (JSON condition: `text`/`role`/`value`/`ref`/`scope-ref`/
118
+ `until`/`timeout-ms`) when an action has an observable completion signal —
119
+ but only within the same root: a modal/sheet or a closing window is a NEW
120
+ root, so expect cannot see it; run the action without `--expect` and read
121
+ the returned root note instead.
122
+ - Focus-following works only inside one act transaction: after a click into
123
+ an editable region, later `typeText`/`keypress` steps in the SAME
124
+ `--actions` array may omit `ref`. A standalone keypress (for example
125
+ `["cmd","w"]`) must carry `ref` (the window or a specific element).
126
+ - On failure the output still names the successor state — inspect it instead
127
+ of blindly replaying.
128
+ - Acting requires the platform helper: on macOS first use installs
129
+ `pie-computer-use.app` and asks for Accessibility + Screen Recording
130
+ grants. If a helper/setup error appears, run
131
+ `node <baseDir>/scripts/setup-helper.mjs` once, grant the permissions, and
132
+ retry.
133
+
134
+ ## Uncertain outcomes
135
+
136
+ If a command returns `Outcome unknown` (the client timed out or lost the
137
+ broker connection after dispatch), the native action may already have run.
138
+ Do **not** replay it. The broker keeps that resource lane occupied until the
139
+ native helper confirms completion; run `observe` to establish a successor
140
+ state and decide from there. Reusing the same `--request-id` retrieves the
141
+ original result (or `in-flight`/`outcome-unknown`); it never dispatches again.
142
+
143
+ ## Browser pages
144
+
145
+ ```bash
146
+ node <baseDir>/computer-use-axi-cli.js browser launch --url https://example.com
147
+ node <baseDir>/computer-use-axi-cli.js browser navigate --state S1 --url https://other.example
148
+ node <baseDir>/computer-use-axi-cli.js browser eval --state S1 --expr 'document.title'
149
+ ```
150
+
151
+ Browser states use the same outline/action/condition contracts as desktop
152
+ states. Native browser windows are ordinary desktop UI — use `roots`/
153
+ `observe`/`act` on them. Browser control may need enabling via the
154
+ computer-use config (`browser_use`).
155
+
156
+ ## Test-only backend
157
+
158
+ The deterministic fake desktop backend used by the broker test suite is not
159
+ shipped in release bundles. In a source checkout it activates only when both
160
+ `PIE_COMPUTER_USE_FAKE_BACKEND=1` and
161
+ `PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS=1` are set; otherwise the setting is
162
+ ignored with a warning and the real platform backend is used. Never rely on it
163
+ for real UI work.
164
+
165
+ ## Output bounds and exit codes
166
+
167
+ Model-facing output is capped (~48 KiB / 2 000 lines); oversized results
168
+ return a preview plus an `@o` continuation ref. Exit codes: `0` ok, `1` tool
169
+ or broker error (message on stderr), `2` usage error, `3` broker
170
+ version/generation mismatch (run `stop`, then retry). `--json` wraps
171
+ `{ content, details, isError }` for programmatic use; `--request-id` makes a
172
+ retry idempotent.