@cydm/pie 2.5.1 → 2.6.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.
Files changed (26) hide show
  1. package/dist/builtin/extensions/ask-user/index.js +3 -3
  2. package/dist/builtin/extensions/computer-use/index.js +17 -2
  3. package/dist/builtin/extensions/computer-use/prebuilt/macos/arm64/pie-computer-use +0 -0
  4. package/dist/builtin/extensions/computer-use/prebuilt/macos/universal/pie-computer-use.app/Contents/MacOS/pie-computer-use +0 -0
  5. package/dist/builtin/extensions/computer-use/prebuilt/macos/x64/pie-computer-use +0 -0
  6. package/dist/builtin/extensions/computer-use/scripts/setup-helper.mjs +90 -9
  7. package/dist/builtin/extensions/mcp-adapter/index.js +135 -27
  8. package/dist/builtin/extensions/plan-mode/index.js +3 -3
  9. package/dist/builtin/extensions/subagent/index.js +8 -5
  10. package/dist/builtin/extensions/todo/index.js +3 -3
  11. package/dist/builtin/skills/chrome-devtools-axi/chrome-devtools-axi-bridge.js +199 -40
  12. package/dist/builtin/skills/chrome-devtools-axi/chrome-devtools-axi-cli.js +5 -2
  13. package/dist/builtin/skills/chrome-devtools-axi/chrome-devtools-axi-runtime.js +1 -1
  14. package/dist/builtin/skills/chrome-devtools-axi/{cli-P4VG6ZZG.js → cli-GB37NZHQ.js} +78 -29
  15. package/dist/builtin/skills/tui-use/SKILL.md +44 -0
  16. package/dist/builtin/skills/tui-use/tui-use-cli.cjs +40 -16
  17. package/dist/builtin/skills/tui-use/tui-use-daemon.cjs +7 -1
  18. package/dist/chunks/{chunk-REP6QANY.js → chunk-AZ336HKF.js} +405 -70
  19. package/dist/chunks/{chunk-N2MK46AE.js → chunk-G6KCJA6B.js} +7 -2
  20. package/dist/chunks/{chunk-YRXXRLMQ.js → chunk-O2DKMLNX.js} +152 -8
  21. package/dist/chunks/{chunk-ZKHMNEXL.js → chunk-Y4CYXJR7.js} +27 -2
  22. package/dist/chunks/src-VCWCE7YV.js +20 -0
  23. package/dist/chunks/{test-stream-OD3B57AS.js → test-stream-GOSEWMJ3.js} +1 -1
  24. package/dist/cli.js +67 -17
  25. package/package.json +2 -2
  26. package/dist/chunks/src-SXUQ7OY6.js +0 -14
@@ -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-REP6QANY.js";
5
- import "../../../chunks/chunk-ZKHMNEXL.js";
6
- import "../../../chunks/chunk-YRXXRLMQ.js";
4
+ } from "../../../chunks/chunk-AZ336HKF.js";
5
+ import "../../../chunks/chunk-Y4CYXJR7.js";
6
+ import "../../../chunks/chunk-O2DKMLNX.js";
7
7
  import "../../../chunks/chunk-XXGYUYXI.js";
8
8
  import "../../../chunks/chunk-LZQXQOPJ.js";
9
9
 
@@ -3787,8 +3787,23 @@ function wireableInputDescendants(node) {
3787
3787
  visit(node);
3788
3788
  return matches;
3789
3789
  }
3790
+ var FOCUS_CHANGING_KEYS = /* @__PURE__ */ new Set(["TAB", "SHIFT+TAB", "ENTER", "RETURN", "SPACE", "UP", "DOWN", "LEFT", "RIGHT", "UP_ARROW", "DOWN_ARROW", "LEFT_ARROW", "RIGHT_ARROW", "ESC", "ESCAPE", "HOME", "END", "PAGE_UP", "PAGE_DOWN", "PAGEDOWN", "PAGEUP"]);
3791
+ function isFocusChangingKey(keys2) {
3792
+ const list = typeof keys2 === "string" ? [keys2] : Array.isArray(keys2) ? keys2 : [];
3793
+ for (const raw of list) {
3794
+ if (typeof raw !== "string") continue;
3795
+ const token = raw.trim().toUpperCase().split("+").pop() ?? "";
3796
+ if (FOCUS_CHANGING_KEYS.has(token) || FOCUS_CHANGING_KEYS.has(raw.trim().toUpperCase())) return true;
3797
+ }
3798
+ return false;
3799
+ }
3790
3800
  function recordActionFocus(action, state, node, coordinateFocus = false) {
3791
- if (!["click", "press", "keypress", "typeText"].includes(action.action) || !action.ref && action.action !== "click") return;
3801
+ if (action.action === "keypress" && !action.ref && isFocusChangingKey(action.keys)) {
3802
+ state.currentFocus = true;
3803
+ state.focusRef = void 0;
3804
+ return;
3805
+ }
3806
+ if (!["click", "press", "keypress", "typeText"].includes(action.action) || !action.ref && action.action !== "click" && action.action !== "press" && !coordinateFocus) return;
3792
3807
  state.currentFocus = Boolean(node?.isTextInput && !node.pictureOnly) || coordinateFocus;
3793
3808
  state.focusRef = node?.isTextInput && node.wireRef && !node.pictureOnly ? node.ref : void 0;
3794
3809
  if (!state.currentFocus && node && !node.pictureOnly && action.ref && (action.action === "click" || action.action === "press") && containsEditable(node)) {
@@ -8546,7 +8561,7 @@ async function dispatchUiAction(action, target, look, headless, state, signal) {
8546
8561
  }
8547
8562
  const trace = await helperAct(target, prepared, headless, signal);
8548
8563
  if (!headless) {
8549
- const coordinateFocus = prepared.action === "click" && "x" in prepared.target;
8564
+ const coordinateFocus = (prepared.action === "click" || prepared.action === "press") && "x" in prepared.target;
8550
8565
  const node = action.ref ? outlineNodeByRef(action.ref.trim()) : coordinateFocus ? editorAtPoint(currentOutlineOrThrow().nodes, action.x, action.y) : void 0;
8551
8566
  recordActionFocus(action, state, node, coordinateFocus);
8552
8567
  }
@@ -313,19 +313,100 @@ async function installPrebuiltHelperApp(sourceAppPath) {
313
313
  const existingInfo = await fs.readFile(path.join(helperAppPath, "Contents", "Info.plist"), "utf8").catch(() => undefined);
314
314
  const sourceInfo = await fs.readFile(sourceInfoPath, "utf8");
315
315
  if (existingExecutable?.equals(sourceExecutable) && existingInfo === sourceInfo) {
316
- await registerHelperApp();
317
- return false;
316
+ // Byte equality alone is NOT sufficient for the no-change shortcut: a
317
+ // damaged/missing _CodeSignature (CodeResources tampered or deleted
318
+ // around an otherwise-identical executable) would otherwise survive
319
+ // every repair invocation. The existing bundle must still verify.
320
+ let existingVerifies = false;
321
+ try {
322
+ await run("codesign", ["--verify", "--strict", helperAppPath]);
323
+ existingVerifies = true;
324
+ } catch {
325
+ // Fall through to the replacement path below, which re-installs (and
326
+ // re-verifies) a complete bundle.
327
+ }
328
+ if (existingVerifies) {
329
+ await registerHelperApp();
330
+ return false;
331
+ }
318
332
  }
319
333
  // The sealed bundle must arrive intact — a broken signature would burn
320
334
  // the user's TCC grants on an identity that can never validate.
321
335
  await run("codesign", ["--verify", "--strict", sourceAppPath]);
322
- await fs.rm(helperAppPath, { force: true, recursive: true });
323
- // ditto preserves the bundle byte-for-byte (signature + stapled
324
- // notarization ticket). The sealed app is NEVER re-signed here: its
325
- // Developer ID designated requirement (identifier + team) is exactly
326
- // what gives release builds their best chance of retaining TCC grants.
327
- await run("/usr/bin/ditto", [sourceAppPath, helperAppPath]);
328
- await registerHelperApp();
336
+ // Sweep staging directories abandoned by crashed installers (the finally
337
+ // below only runs on ordinary unwinding). Reclaim is ownership-aware:
338
+ // the staging name embeds its creator's PID, and ONLY a demonstrably dead
339
+ // owner (ESRCH on signal-0) plus age beyond a generous slow-install window
340
+ // makes a stage garbage — mtime alone is not ownership evidence, so a
341
+ // live/slow installer's stage is never touched.
342
+ try {
343
+ const parent = path.dirname(helperAppPath);
344
+ const base = path.basename(helperAppPath);
345
+ const entries = await fs.readdir(parent);
346
+ const cutoff = Date.now() - 60 * 60 * 1000;
347
+ for (const entry of entries) {
348
+ // Exact staging format <app>.staged-<pid>-<ts36>: anything else that
349
+ // merely contains ".staged-" is not ours to touch.
350
+ const match = new RegExp(`^${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.staged-(\\d+)-[0-9a-z]+$`).exec(entry);
351
+ if (!match) continue;
352
+ const staged = path.join(parent, entry);
353
+ try {
354
+ const stat = await fs.stat(staged);
355
+ if (stat.mtimeMs >= cutoff) continue;
356
+ const ownerPid = Number(match[1]);
357
+ let ownerAlive = false;
358
+ try {
359
+ process.kill(ownerPid, 0);
360
+ ownerAlive = true;
361
+ } catch (error) {
362
+ // ESRCH: dead. EPERM (exists, other user): treat as alive —
363
+ // conservative, never delete on unknown liveness.
364
+ ownerAlive = error && error.code === "EPERM";
365
+ }
366
+ if (!ownerAlive) await fs.rm(staged, { force: true, recursive: true });
367
+ } catch { /* raced away */ }
368
+ }
369
+ } catch { /* no parent dir yet */ }
370
+ // Stage-then-swap: the staged copy is verified before publication and the
371
+ // previous installation survives every failure up to the final swap.
372
+ // NOTE: the swap itself (rm + rename) is NOT atomic — directory bundles
373
+ // have no atomic exchange on stock Node — but the failure window no longer
374
+ // starts before verification, and a crash mid-swap self-heals on the next
375
+ // run (which re-stages from the verified source).
376
+ const stagingPath = `${helperAppPath}.staged-${process.pid}-${Date.now().toString(36)}`;
377
+ await fs.rm(stagingPath, { force: true, recursive: true });
378
+ try {
379
+ // ditto preserves the bundle byte-for-byte (signature + stapled
380
+ // notarization ticket). The sealed app is NEVER re-signed here: its
381
+ // Developer ID designated requirement (identifier + team) is exactly
382
+ // what gives release builds their best chance of retaining TCC grants.
383
+ await run("/usr/bin/ditto", [sourceAppPath, stagingPath]);
384
+ // Verify the STAGED bundle (not just the source): source mutation
385
+ // between the verify above and the copy, or a torn copy, stops here.
386
+ await run("codesign", ["--verify", "--strict", stagingPath]);
387
+ let published = false;
388
+ try {
389
+ await fs.rm(helperAppPath, { force: true, recursive: true });
390
+ await fs.rename(stagingPath, helperAppPath);
391
+ published = true;
392
+ } catch (renameError) {
393
+ // Cross-device or otherwise unrenamable staging: fall back to a
394
+ // second ditto onto the final path, then verify the destination. A
395
+ // VERIFIED fallback is a successful installation — continue to
396
+ // registration instead of failing after doing the work.
397
+ try {
398
+ await run("/usr/bin/ditto", [stagingPath, helperAppPath]);
399
+ await run("codesign", ["--verify", "--strict", helperAppPath]);
400
+ published = true;
401
+ } catch (fallbackError) {
402
+ fallbackError.cause = renameError;
403
+ throw fallbackError;
404
+ }
405
+ }
406
+ if (published) await registerHelperApp();
407
+ } finally {
408
+ await fs.rm(stagingPath, { force: true, recursive: true }).catch(() => {});
409
+ }
329
410
  return true;
330
411
  }
331
412
 
@@ -24434,6 +24434,7 @@ var StdioClientTransport = class {
24434
24434
  };
24435
24435
 
24436
24436
  // builtin/extensions/mcp-adapter/src/server-manager.ts
24437
+ import { createHash } from "node:crypto";
24437
24438
  import * as fs2 from "node:fs";
24438
24439
  import * as path2 from "node:path";
24439
24440
  var CONNECT_TIMEOUT_MS = 3e4;
@@ -24442,6 +24443,56 @@ var PACKAGE_MANAGER_COMMANDS = /* @__PURE__ */ new Set(["npx", "npm", "bunx", "u
24442
24443
  var METADATA_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
24443
24444
  var CALL_TIMEOUT_MS = 12e4;
24444
24445
  var STDERR_TAIL_LIMIT = 4e3;
24446
+ var DIAGNOSTIC_TEXT_LIMIT = 1500;
24447
+ function definitionSecrets(definition) {
24448
+ const secrets = [];
24449
+ for (const value of Object.values(definition.env ?? {})) {
24450
+ if (typeof value === "string" && value.length > 0) secrets.push(value);
24451
+ }
24452
+ for (const value of Object.values(definition.headers ?? {})) {
24453
+ if (typeof value === "string" && value.length > 0) secrets.push(value);
24454
+ }
24455
+ return secrets.sort((a, b) => b.length - a.length);
24456
+ }
24457
+ function redactSecrets(text, secrets) {
24458
+ let sanitized = text;
24459
+ for (const secret of secrets) {
24460
+ sanitized = sanitized.split(secret).join("[redacted]");
24461
+ }
24462
+ return sanitized;
24463
+ }
24464
+ function redactLeadingSecretSuffix(text, secrets) {
24465
+ if (secrets.length === 0 || text.length === 0) return text;
24466
+ for (const secret of secrets) {
24467
+ for (let keep = Math.min(secret.length - 1, text.length); keep >= 4; keep--) {
24468
+ if (text.startsWith(secret.slice(secret.length - keep))) {
24469
+ return `[redacted]${text.slice(keep)}`;
24470
+ }
24471
+ }
24472
+ }
24473
+ return text;
24474
+ }
24475
+ function redactTrailingSecretPrefix(text, secrets) {
24476
+ if (secrets.length === 0 || text.length === 0) return text;
24477
+ for (const secret of secrets) {
24478
+ for (let keep = Math.min(secret.length - 1, text.length); keep >= 4; keep--) {
24479
+ if (text.endsWith(secret.slice(0, keep))) {
24480
+ return `${text.slice(0, text.length - keep)}[redacted]`;
24481
+ }
24482
+ }
24483
+ }
24484
+ return text;
24485
+ }
24486
+ function redactStderrTail(tail, secrets) {
24487
+ return redactTrailingSecretPrefix(redactLeadingSecretSuffix(redactSecrets(tail, secrets), secrets), secrets);
24488
+ }
24489
+ function sanitizeDiagnostic(text, secrets) {
24490
+ const sanitized = redactLeadingSecretSuffix(redactSecrets(text, secrets), secrets);
24491
+ if (sanitized.length > DIAGNOSTIC_TEXT_LIMIT) {
24492
+ return `${sanitized.slice(0, DIAGNOSTIC_TEXT_LIMIT)}\u2026[truncated ${sanitized.length - DIAGNOSTIC_TEXT_LIMIT} chars]`;
24493
+ }
24494
+ return sanitized;
24495
+ }
24445
24496
  function connectTimeoutFor(definition) {
24446
24497
  if (definition.connectTimeoutMs !== void 0) return definition.connectTimeoutMs;
24447
24498
  const base = path2.basename(definition.command ?? "");
@@ -24499,6 +24550,28 @@ function withTimeout(promise, timeoutMs, label, signal) {
24499
24550
  );
24500
24551
  });
24501
24552
  }
24553
+ function cacheFingerprint(definition) {
24554
+ return createHash("sha256").update(JSON.stringify({
24555
+ sourceScope: definition.sourceScope,
24556
+ sourcePath: definition.sourcePath,
24557
+ name: definition.name,
24558
+ command: definition.command,
24559
+ args: definition.args,
24560
+ // Canonicalized (key-sorted) so reordering equivalent env/header keys
24561
+ // does not invalidate cached metadata. Sorting must be locale-stable
24562
+ // (code-unit order, not localeCompare) so the same definition
24563
+ // fingerprints identically on every machine.
24564
+ env: canonicalizeKeys(definition.env),
24565
+ serverCwd: definition.cwd,
24566
+ url: definition.url,
24567
+ headers: canonicalizeKeys(definition.headers),
24568
+ inheritEnv: definition.inheritEnv
24569
+ })).digest("hex").slice(0, 24);
24570
+ }
24571
+ function canonicalizeKeys(record2) {
24572
+ if (!record2) return {};
24573
+ return Object.fromEntries(Object.entries(record2).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
24574
+ }
24502
24575
  var McpServerManager = class {
24503
24576
  constructor(definitions, cacheDir, log, now = Date.now) {
24504
24577
  this.now = now;
@@ -24577,8 +24650,9 @@ var McpServerManager = class {
24577
24650
  stderr: "pipe"
24578
24651
  });
24579
24652
  if (transport2.stderr) {
24653
+ const captureSecrets = definitionSecrets(definition);
24580
24654
  transport2.stderr.on("data", (chunk) => {
24581
- runtime.stderrTail = appendTail(runtime.stderrTail, chunk);
24655
+ runtime.stderrTail = appendTail(runtime.stderrTail, redactSecrets(String(chunk), captureSecrets));
24582
24656
  });
24583
24657
  }
24584
24658
  return { client, transport: transport2 };
@@ -24669,7 +24743,7 @@ var McpServerManager = class {
24669
24743
  if (!runtime.tools) continue;
24670
24744
  const shardPath = path2.join(this.cacheDir, `${key}.json`);
24671
24745
  const tempPath = `${shardPath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
24672
- fs2.writeFileSync(tempPath, JSON.stringify({ updatedAt: this.now(), tools: runtime.tools }, null, 2), "utf8");
24746
+ fs2.writeFileSync(tempPath, JSON.stringify({ updatedAt: this.now(), fingerprint: cacheFingerprint(runtime.definition), tools: runtime.tools }, null, 2), "utf8");
24673
24747
  try {
24674
24748
  fs2.renameSync(tempPath, shardPath);
24675
24749
  } catch (error2) {
@@ -24722,10 +24796,11 @@ var McpServerManager = class {
24722
24796
  catalog.set(tool.prefixedName, tool);
24723
24797
  ownerOf.set(tool.prefixedName, tool.server);
24724
24798
  };
24725
- for (const tools of Object.values(this.readCacheFile())) {
24726
- for (const tool of tools) {
24727
- const definition = this.definitions.get(tool.server);
24728
- if (!definition || definition.disabled) continue;
24799
+ for (const [key, shard] of Object.entries(this.readCacheFile())) {
24800
+ const definition = this.definitions.get(key);
24801
+ if (!definition || definition.disabled) continue;
24802
+ if (shard.fingerprint !== cacheFingerprint(definition)) continue;
24803
+ for (const tool of shard.tools) {
24729
24804
  claim2(tool, false);
24730
24805
  }
24731
24806
  }
@@ -24786,7 +24861,10 @@ var McpServerManager = class {
24786
24861
  if (typeof parsed.updatedAt !== "number" || this.now() - parsed.updatedAt > METADATA_TTL_MS) continue;
24787
24862
  if (!Array.isArray(parsed.tools)) continue;
24788
24863
  const key = name.slice(0, -".json".length);
24789
- snapshot[key] = parsed.tools;
24864
+ snapshot[key] = {
24865
+ tools: parsed.tools,
24866
+ ...typeof parsed.fingerprint === "string" ? { fingerprint: parsed.fingerprint } : {}
24867
+ };
24790
24868
  updatedAt[key] = parsed.updatedAt;
24791
24869
  } catch {
24792
24870
  }
@@ -24829,13 +24907,17 @@ var McpServerManager = class {
24829
24907
  runtime = await this.connect(server, signal);
24830
24908
  } catch (error2) {
24831
24909
  const runtimeState = this.runtimes.get(server);
24832
- const tail = runtimeState?.stderrTail.trim();
24910
+ const secrets = definitionSecrets(definition);
24911
+ const tail = redactStderrTail(runtimeState?.stderrTail ?? "", secrets).trim();
24833
24912
  return {
24834
24913
  delivery: "not_sent",
24835
- error: `MCP server "${server}" failed to start: ${error2 instanceof Error ? error2.message : String(error2)}` + (tail ? `
24914
+ error: sanitizeDiagnostic(
24915
+ `MCP server "${server}" failed to start: ${error2 instanceof Error ? error2.message : String(error2)}` + (tail ? `
24836
24916
 
24837
24917
  Server stderr tail:
24838
- ${tail}` : "")
24918
+ ${tail}` : ""),
24919
+ secrets
24920
+ )
24839
24921
  };
24840
24922
  }
24841
24923
  const client = runtime.client;
@@ -24879,7 +24961,7 @@ ${tail}` : "")
24879
24961
  isError: result.isError === true
24880
24962
  };
24881
24963
  } catch (error2) {
24882
- const message = error2 instanceof Error ? error2.message : String(error2);
24964
+ const message = sanitizeDiagnostic(error2 instanceof Error ? error2.message : String(error2), definitionSecrets(definition));
24883
24965
  if (error2 instanceof ProtocolError && (error2.code === ProtocolErrorCode.InvalidRequest || error2.code === ProtocolErrorCode.InvalidParams) && /output schema|structured content/i.test(message)) {
24884
24966
  return {
24885
24967
  delivery: "invalid_result",
@@ -24914,7 +24996,7 @@ ${tail}` : "")
24914
24996
  };
24915
24997
 
24916
24998
  // builtin/extensions/mcp-adapter/src/trust.ts
24917
- import { createHash } from "node:crypto";
24999
+ import { createHash as createHash2 } from "node:crypto";
24918
25000
  import * as fs3 from "node:fs";
24919
25001
  import * as path3 from "node:path";
24920
25002
  function serverFingerprint(cwd, definition) {
@@ -24933,7 +25015,7 @@ function serverFingerprint(cwd, definition) {
24933
25015
  inheritEnv: definition.inheritEnv,
24934
25016
  headers: definition.headers
24935
25017
  });
24936
- return createHash("sha256").update(spec).digest("hex").slice(0, 24);
25018
+ return createHash2("sha256").update(spec).digest("hex").slice(0, 24);
24937
25019
  }
24938
25020
  var McpTrustStore = class {
24939
25021
  filePath;
@@ -25034,8 +25116,9 @@ function structuredContentIsMirroredInText(textBlocks, structured) {
25034
25116
  function mapMcpOutcome(outcome, maxBytes) {
25035
25117
  if (outcome.delivery !== "ok") {
25036
25118
  const prefix = outcome.delivery === "may_have_run" || outcome.delivery === "invalid_result" ? "[outcome unknown]" : "[not executed]";
25119
+ const errorText = truncate(`${outcome.error ?? "MCP call failed"}`, maxBytes);
25037
25120
  return {
25038
- content: [{ type: "text", text: `${prefix} ${outcome.error ?? "MCP call failed"}` }],
25121
+ content: [{ type: "text", text: `${prefix} ${errorText}` }],
25039
25122
  details: { mode: "mcp", delivery: outcome.delivery, ...outcome.protocolCode !== void 0 ? { protocolCode: outcome.protocolCode } : {} },
25040
25123
  isError: true
25041
25124
  };
@@ -25070,8 +25153,9 @@ ${JSON.stringify(outcome.structuredContent, null, 2)}` });
25070
25153
  isError: outcome.isError === true
25071
25154
  };
25072
25155
  }
25073
- function createMcpProxyTool(getManager, ensureTrusted, options = {}) {
25074
- const maxBytes = options.outputMaxBytes ?? DEFAULT_OUTPUT_MAX_BYTES;
25156
+ function createMcpProxyTool(getManager, ensureTrusted, optionsOrVerify = {}, options = {}) {
25157
+ const verifyTrusted = typeof optionsOrVerify === "function" ? optionsOrVerify : void 0;
25158
+ const maxBytes = typeof optionsOrVerify === "object" && optionsOrVerify.outputMaxBytes || options.outputMaxBytes || DEFAULT_OUTPUT_MAX_BYTES;
25075
25159
  return {
25076
25160
  name: "mcp",
25077
25161
  label: "MCP",
@@ -25122,8 +25206,10 @@ ${truncate(JSON.stringify(schema ?? {}, null, 2), maxBytes)}` : `unknown MCP too
25122
25206
  }
25123
25207
  const resolved = getManager().resolvePrefixedName(params.tool);
25124
25208
  const server = descriptor?.server ?? resolved?.server ?? "";
25209
+ let dispatchManager;
25125
25210
  if (server) {
25126
- const trust = await ensureTrusted(server);
25211
+ const managerAtGate = getManager();
25212
+ const trust = await ensureTrusted(server, managerAtGate);
25127
25213
  if (!trust.trusted) {
25128
25214
  return {
25129
25215
  content: [{ type: "text", text: `[not executed] ${trust.reason ?? "MCP server not trusted"}` }],
@@ -25131,6 +25217,15 @@ ${truncate(JSON.stringify(schema ?? {}, null, 2), maxBytes)}` : `unknown MCP too
25131
25217
  isError: true
25132
25218
  };
25133
25219
  }
25220
+ const managerNow = getManager();
25221
+ if (managerNow !== managerAtGate || verifyTrusted !== void 0 && !verifyTrusted(server, managerNow)) {
25222
+ return {
25223
+ content: [{ type: "text", text: `[not executed] MCP config changed during approval for server "${server}" \u2014 the approved definition and the current one differ. Retry the call to approve the new definition.` }],
25224
+ details: { mode: "mcp", delivery: "not_sent" },
25225
+ isError: true
25226
+ };
25227
+ }
25228
+ dispatchManager = managerNow;
25134
25229
  }
25135
25230
  let args = {};
25136
25231
  if (typeof params.args === "string") {
@@ -25146,7 +25241,7 @@ ${truncate(JSON.stringify(schema ?? {}, null, 2), maxBytes)}` : `unknown MCP too
25146
25241
  } else if (params.args && typeof params.args === "object" && !Array.isArray(params.args)) {
25147
25242
  args = params.args;
25148
25243
  }
25149
- const outcome = await getManager().callTool(params.tool, args, toolContext.signal);
25244
+ const outcome = await (dispatchManager ?? getManager()).callTool(params.tool, args, toolContext.signal);
25150
25245
  return mapMcpOutcome(outcome, maxBytes);
25151
25246
  }
25152
25247
  };
@@ -25204,7 +25299,7 @@ function jsonSchemaToTypebox(schema) {
25204
25299
  // builtin/extensions/mcp-adapter/src/direct-tools.ts
25205
25300
  var FALLBACK_PARAMS = Type.Object({}, { description: "MCP tool arguments (schema unknown until first connect)" });
25206
25301
  var DEFAULT_DIRECT_OUTPUT_MAX_BYTES = 8192;
25207
- function registerDirectTools(ctx, getManager, ensureTrusted, outputMaxBytes) {
25302
+ function registerDirectTools(ctx, getManager, ensureTrusted, verifyTrusted, outputMaxBytes) {
25208
25303
  const registered = [];
25209
25304
  const seenNames = /* @__PURE__ */ new Set();
25210
25305
  for (const definition of getManager().listDefinitions()) {
@@ -25218,13 +25313,13 @@ function registerDirectTools(ctx, getManager, ensureTrusted, outputMaxBytes) {
25218
25313
  continue;
25219
25314
  }
25220
25315
  seenNames.add(descriptor.prefixedName);
25221
- registerDirectTool(ctx, getManager, ensureTrusted, definition, descriptor.prefixedName, descriptor.description, descriptor.inputSchema, outputMaxBytes);
25316
+ registerDirectTool(ctx, getManager, ensureTrusted, verifyTrusted, definition, descriptor.prefixedName, descriptor.description, descriptor.inputSchema, outputMaxBytes);
25222
25317
  registered.push(descriptor.prefixedName);
25223
25318
  }
25224
25319
  }
25225
25320
  return registered;
25226
25321
  }
25227
- function registerDirectTool(ctx, getManager, ensureTrusted, definition, prefixedName, description, inputSchema, outputMaxBytes) {
25322
+ function registerDirectTool(ctx, getManager, ensureTrusted, verifyTrusted, definition, prefixedName, description, inputSchema, outputMaxBytes) {
25228
25323
  const parameters = inputSchema ? jsonSchemaToTypebox(inputSchema) : FALLBACK_PARAMS;
25229
25324
  ctx.registerTool({
25230
25325
  name: prefixedName,
@@ -25232,7 +25327,8 @@ function registerDirectTool(ctx, getManager, ensureTrusted, definition, prefixed
25232
25327
  description: `${(description ?? prefixedName).trim()} (MCP server: ${definition.name})`,
25233
25328
  parameters,
25234
25329
  async execute(args, toolContext) {
25235
- const trust = await ensureTrusted(definition.name);
25330
+ const managerAtGate = getManager();
25331
+ const trust = await ensureTrusted(definition.name, managerAtGate);
25236
25332
  if (!trust.trusted) {
25237
25333
  return {
25238
25334
  content: [{ type: "text", text: `[not executed] ${trust.reason ?? "MCP server not trusted"}` }],
@@ -25240,7 +25336,15 @@ function registerDirectTool(ctx, getManager, ensureTrusted, definition, prefixed
25240
25336
  isError: true
25241
25337
  };
25242
25338
  }
25243
- const outcome = await getManager().callTool(
25339
+ const managerNow = getManager();
25340
+ if (managerNow !== managerAtGate || verifyTrusted !== void 0 && !verifyTrusted(definition.name, managerNow)) {
25341
+ return {
25342
+ content: [{ type: "text", text: `[not executed] MCP config changed during approval for server "${definition.name}" \u2014 retry the call to approve the new definition.` }],
25343
+ details: { delivery: "not_sent" },
25344
+ isError: true
25345
+ };
25346
+ }
25347
+ const outcome = await managerNow.callTool(
25244
25348
  prefixedName,
25245
25349
  args && typeof args === "object" && !Array.isArray(args) ? args : {},
25246
25350
  toolContext.signal
@@ -25335,8 +25439,8 @@ function mcpAdapter(ctx) {
25335
25439
  fs4.writeFileSync(filePath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
25336
25440
  return filePath;
25337
25441
  }
25338
- async function ensureTrusted(serverName) {
25339
- const definition = manager.getDefinition(serverName);
25442
+ async function ensureTrusted(serverName, managerAtGate = manager) {
25443
+ const definition = managerAtGate.getDefinition(serverName);
25340
25444
  if (!definition) return { trusted: false, reason: `unknown MCP server "${serverName}"` };
25341
25445
  if (trustStore.isTrusted(ctx.cwd, definition)) return { trusted: true };
25342
25446
  if (!ctx.interactions.isAvailable()) {
@@ -25360,8 +25464,12 @@ function mcpAdapter(ctx) {
25360
25464
  reason: `MCP server "${definition.name}" was not approved. It was NOT started. Ask the user to approve it, or remove it from ${definition.sourcePath}.`
25361
25465
  };
25362
25466
  }
25363
- ctx.registerTool(createMcpProxyTool(() => manager, ensureTrusted));
25364
- const eagerRegistered = registerDirectTools(ctx, () => manager, ensureTrusted);
25467
+ function verifyTrusted(serverName, currentManager) {
25468
+ const definition = currentManager.getDefinition(serverName);
25469
+ return !!definition && trustStore.isTrusted(ctx.cwd, definition);
25470
+ }
25471
+ ctx.registerTool(createMcpProxyTool(() => manager, ensureTrusted, verifyTrusted));
25472
+ const eagerRegistered = registerDirectTools(ctx, () => manager, ensureTrusted, verifyTrusted);
25365
25473
  if (eagerRegistered.length > 0) {
25366
25474
  ctx.log(`mcp: registered ${eagerRegistered.length} direct tool(s) from metadata cache: ${eagerRegistered.join(", ")}`);
25367
25475
  }
@@ -7,9 +7,9 @@ import {
7
7
  isPlanModeSafeCommand,
8
8
  markCompletedPlanSteps,
9
9
  restoreExecutionState
10
- } from "../../../chunks/chunk-REP6QANY.js";
11
- import "../../../chunks/chunk-ZKHMNEXL.js";
12
- import "../../../chunks/chunk-YRXXRLMQ.js";
10
+ } from "../../../chunks/chunk-AZ336HKF.js";
11
+ import "../../../chunks/chunk-Y4CYXJR7.js";
12
+ import "../../../chunks/chunk-O2DKMLNX.js";
13
13
  import {
14
14
  Type
15
15
  } from "../../../chunks/chunk-XXGYUYXI.js";
@@ -1,13 +1,13 @@
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-N2MK46AE.js";
4
+ } from "../../../chunks/chunk-G6KCJA6B.js";
5
5
  import {
6
6
  createSharedFileSystemTools,
7
7
  createSubagentCapability
8
- } from "../../../chunks/chunk-REP6QANY.js";
9
- import "../../../chunks/chunk-ZKHMNEXL.js";
10
- import "../../../chunks/chunk-YRXXRLMQ.js";
8
+ } from "../../../chunks/chunk-AZ336HKF.js";
9
+ import "../../../chunks/chunk-Y4CYXJR7.js";
10
+ import "../../../chunks/chunk-O2DKMLNX.js";
11
11
  import "../../../chunks/chunk-5353DEEC.js";
12
12
  import "../../../chunks/chunk-XXGYUYXI.js";
13
13
  import "../../../chunks/chunk-LZQXQOPJ.js";
@@ -39,11 +39,14 @@ function subagentExtension(ctx) {
39
39
  debugLog: (module, message, data) => ctx.log(`[subagent:${module}] ${message}${data !== void 0 ? ` ${JSON.stringify(data)}` : ""}`),
40
40
  resolveModelClass: (modelClass) => ctx.resolveModelClass?.(modelClass),
41
41
  resolveModelRef: (modelRef) => ctx.resolveModelRef?.(modelRef),
42
- createRuntimeTools: () => {
42
+ createRuntimeTools: (mode) => {
43
43
  const parentRuntimeTools = ctx.getRuntimeTools();
44
44
  if (Array.isArray(parentRuntimeTools) && parentRuntimeTools.length > 0) {
45
45
  return parentRuntimeTools.filter((tool) => tool?.name !== "spawn_subagents_parallel");
46
46
  }
47
+ if (mode === "edit") {
48
+ throw new Error("Edit delegation requires the parent's executable runtime tools; fallback tools are read-mode only.");
49
+ }
47
50
  const configDir = path.join(os.homedir(), ".pie");
48
51
  const fsOptions = ctx.yoloMode ? { allowlistedDirs: ["/"] } : {
49
52
  accessRoots: [
@@ -8,9 +8,9 @@ import {
8
8
  executeManageTodoList,
9
9
  executionStateToTodos,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-REP6QANY.js";
12
- import "../../../chunks/chunk-ZKHMNEXL.js";
13
- import "../../../chunks/chunk-YRXXRLMQ.js";
11
+ } from "../../../chunks/chunk-AZ336HKF.js";
12
+ import "../../../chunks/chunk-Y4CYXJR7.js";
13
+ import "../../../chunks/chunk-O2DKMLNX.js";
14
14
  import "../../../chunks/chunk-XXGYUYXI.js";
15
15
  import "../../../chunks/chunk-LZQXQOPJ.js";
16
16