@cydm/pie 2.3.0 → 2.4.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 (25) hide show
  1. package/dist/builtin/extensions/ask-user/index.js +3 -3
  2. package/dist/builtin/extensions/computer-use/index.js +26 -4
  3. package/dist/builtin/extensions/computer-use/package.json +3 -0
  4. package/dist/builtin/extensions/computer-use/prebuilt/macos/arm64/pie-computer-use +0 -0
  5. package/dist/builtin/extensions/computer-use/prebuilt/macos/universal/pie-computer-use.app/Contents/MacOS/pie-computer-use +0 -0
  6. package/dist/builtin/extensions/computer-use/prebuilt/macos/x64/pie-computer-use +0 -0
  7. package/dist/builtin/extensions/computer-use/scripts/build-native.mjs +2 -1
  8. package/dist/builtin/extensions/computer-use/scripts/setup-helper.mjs +2 -2
  9. package/dist/builtin/extensions/computer-use/scripts/test-native-retention.mjs +204 -0
  10. package/dist/builtin/extensions/mcp-adapter/index.js +48 -13
  11. package/dist/builtin/extensions/plan-mode/index.js +3 -3
  12. package/dist/builtin/extensions/subagent/index.js +4 -4
  13. package/dist/builtin/extensions/todo/index.js +3 -3
  14. package/dist/builtin/skills/chrome-devtools-axi/chrome-devtools-axi-runtime.js +1 -1
  15. package/dist/builtin/skills/chrome-devtools-axi/{cli-F2VCSVL4.js → cli-P4VG6ZZG.js} +48 -8
  16. package/dist/builtin/skills/skill-creator/scripts/pie_runner.mjs +33 -12
  17. package/dist/builtin/skills/tui-use/tui-use-daemon.cjs +5 -7
  18. package/dist/chunks/{chunk-UPPICLVS.js → chunk-FKUEIYFQ.js} +8212 -7366
  19. package/dist/chunks/{chunk-P3QPJXS7.js → chunk-XVUV3IM2.js} +108 -36
  20. package/dist/chunks/{chunk-IM43UYVV.js → chunk-YRXXRLMQ.js} +75 -14
  21. package/dist/chunks/{chunk-QJH6YN2W.js → chunk-ZKHMNEXL.js} +1 -1
  22. package/dist/chunks/{src-QSP2OPHU.js → src-SXUQ7OY6.js} +2 -2
  23. package/dist/chunks/{test-stream-CVGWFLSX.js → test-stream-OD3B57AS.js} +1 -1
  24. package/dist/cli.js +1302 -876
  25. package/package.json +2 -2
@@ -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-UPPICLVS.js";
5
- import "../../../chunks/chunk-QJH6YN2W.js";
6
- import "../../../chunks/chunk-IM43UYVV.js";
4
+ } from "../../../chunks/chunk-FKUEIYFQ.js";
5
+ import "../../../chunks/chunk-ZKHMNEXL.js";
6
+ import "../../../chunks/chunk-YRXXRLMQ.js";
7
7
  import "../../../chunks/chunk-XXGYUYXI.js";
8
8
  import "../../../chunks/chunk-LZQXQOPJ.js";
9
9
 
@@ -4420,6 +4420,9 @@ var CdpTab = class _CdpTab {
4420
4420
  /** Evaluates a JS expression in the page and returns its primitive value. */
4421
4421
  async evaluate(expression) {
4422
4422
  const result = await this.send("Runtime.evaluate", { expression, returnByValue: true, timeout: COMMAND_TIMEOUT_MS, awaitPromise: true });
4423
+ if (result?.exceptionDetails) {
4424
+ throw new Error(`Evaluation failed: ${exceptionDetailsMessage(result.exceptionDetails)}`);
4425
+ }
4423
4426
  return result?.result?.value;
4424
4427
  }
4425
4428
  async accessibilityTree() {
@@ -4444,6 +4447,9 @@ var CdpTab = class _CdpTab {
4444
4447
  async clickBackendNode(backendNodeId) {
4445
4448
  await this.withBackendNode(backendNodeId, "function(){ this.scrollIntoView({block:'center', inline:'center'}); this.click(); }");
4446
4449
  }
4450
+ async focusBackendNode(backendNodeId) {
4451
+ await this.withBackendNode(backendNodeId, "function(){ this.scrollIntoView({block:'center', inline:'center'}); this.focus(); }");
4452
+ }
4447
4453
  async typeIntoBackendNode(backendNodeId, text, replace) {
4448
4454
  await this.withBackendNode(backendNodeId, "function(text, replace){ this.scrollIntoView({block:'center', inline:'center'}); this.focus(); if (replace) { if ('value' in this) this.value = ''; else this.textContent = ''; } if ('value' in this) this.value += text; else this.textContent = (this.textContent || '') + text; this.dispatchEvent(new InputEvent('input', {bubbles:true, inputType:'insertText', data:text})); this.dispatchEvent(new Event('change', {bubbles:true})); }", [text, replace]);
4449
4455
  }
@@ -4479,11 +4485,14 @@ var CdpTab = class _CdpTab {
4479
4485
  const resolved = await this.send("DOM.resolveNode", { backendNodeId });
4480
4486
  const objectId = resolved?.object?.objectId;
4481
4487
  if (typeof objectId !== "string") throw new Error(`CDP could not resolve backend node ${backendNodeId}.`);
4482
- await this.send("Runtime.callFunctionOn", {
4488
+ const callResult = await this.send("Runtime.callFunctionOn", {
4483
4489
  objectId,
4484
4490
  functionDeclaration,
4485
4491
  arguments: args.map((value) => ({ value }))
4486
4492
  });
4493
+ if (callResult?.exceptionDetails) {
4494
+ throw new Error(`CDP call on backend node ${backendNodeId} failed: ${exceptionDetailsMessage(callResult.exceptionDetails)}`);
4495
+ }
4487
4496
  }
4488
4497
  /** Screen bounds of the browser window containing this tab. */
4489
4498
  async windowBounds() {
@@ -4670,6 +4679,12 @@ async function cdpTypeFocusedForContext(contextId, text) {
4670
4679
  return true;
4671
4680
  }) === true;
4672
4681
  }
4682
+ async function cdpFocusForContext(contextId, backendNodeId) {
4683
+ return await withCdpContextTab(contextId, async (tab) => {
4684
+ await tab.focusBackendNode(backendNodeId);
4685
+ return true;
4686
+ }) === true;
4687
+ }
4673
4688
  async function cdpKeypressForContext(contextId, keys2) {
4674
4689
  return await withCdpContextTab(contextId, async (tab) => {
4675
4690
  await tab.keypress(keys2);
@@ -4773,6 +4788,13 @@ function axString(raw) {
4773
4788
  const value = raw?.value ?? raw;
4774
4789
  return typeof value === "string" ? value.trim() : "";
4775
4790
  }
4791
+ function exceptionDetailsMessage(details) {
4792
+ const exception = details?.exception;
4793
+ if (typeof exception?.description === "string" && exception.description) return exception.description;
4794
+ if (typeof exception?.value === "string" && exception.value) return exception.value;
4795
+ if (typeof details?.text === "string" && details.text) return details.text;
4796
+ return "the script threw an exception.";
4797
+ }
4776
4798
  function cdpSnapshotOutline(snapshotId, nodes) {
4777
4799
  const records = /* @__PURE__ */ new Map();
4778
4800
  for (const raw of nodes) {
@@ -4789,7 +4811,7 @@ function cdpSnapshotOutline(snapshotId, nodes) {
4789
4811
  const actions = browserActionsForAxRole(role);
4790
4812
  const backendNodeId = Number.isFinite(raw?.backendDOMNodeId) ? Math.trunc(raw.backendDOMNodeId) : void 0;
4791
4813
  const wireRef = `cdp:${nodeId}`;
4792
- if (actions.length > 0 && name && (!actions.includes("click") || backendNodeId)) {
4814
+ if (actions.length > 0 && (!actions.includes("click") || backendNodeId)) {
4793
4815
  targets.push({ ref: wireRef, source: "browser_ax", role, name, value: axString(raw?.value) || void 0, actions, backendNodeId });
4794
4816
  }
4795
4817
  const childIds = Array.isArray(raw?.childIds) ? raw.childIds.map(String) : [];
@@ -8728,7 +8750,7 @@ async function performBrowserTransaction(params, actions, signal) {
8728
8750
  if (!BROWSER_TRANSACTION_ACTIONS.has(action.action)) throw new Error(`Browser transactions do not support '${action.action}'.`);
8729
8751
  if (action.action === "click" && action.ref && action.button && action.button !== "left") throw new Error("Browser ref clicks support only the left button; use coordinate clicks for right or middle buttons.");
8730
8752
  const target = browserSnapshotTarget(params.stateId, trimOrUndefined(action.ref));
8731
- if ((action.action === "press" || action.action === "setText" || action.action === "click" && action.ref || action.action === "typeText" && action.ref) && !Number.isFinite(target?.backendNodeId)) {
8753
+ if ((action.action === "press" || action.action === "setText" || action.action === "click" && action.ref || action.action === "typeText" && action.ref || action.action === "keypress" && action.ref) && !Number.isFinite(target?.backendNodeId)) {
8732
8754
  throw new Error(`Browser ${action.action} requires an actionable @e ref owned by ${params.stateId}.`);
8733
8755
  }
8734
8756
  if (action.ref && (!target || target.contextId !== contextId)) throw new Error(`Browser ${action.action} ref must be owned by ${params.stateId}.`);
@@ -8744,7 +8766,7 @@ async function performBrowserTransaction(params, actions, signal) {
8744
8766
  worked = await cdpMouseForContext(contextId, action.x, action.y, "mousePressed", action.button ?? "left", action.clickCount ?? 1) && await cdpMouseForContext(contextId, action.x, action.y, "mouseReleased", action.button ?? "left", action.clickCount ?? 1);
8745
8767
  } else if (action.action === "setText") worked = await cdpTypeForContext(contextId, target.backendNodeId, action.text ?? "", true);
8746
8768
  else if (action.action === "typeText") worked = target?.backendNodeId ? await cdpTypeForContext(contextId, target.backendNodeId, action.text ?? "", false) : await cdpTypeFocusedForContext(contextId, action.text ?? "");
8747
- else if (action.action === "keypress") worked = await cdpKeypressForContext(contextId, action.keys ?? []);
8769
+ else if (action.action === "keypress") worked = target?.backendNodeId ? await cdpFocusForContext(contextId, target.backendNodeId) && await cdpKeypressForContext(contextId, action.keys ?? []) : await cdpKeypressForContext(contextId, action.keys ?? []);
8748
8770
  else if (action.action === "scroll") worked = await cdpScrollForContext(contextId, toFiniteNumber(action.scrollX, 0), toFiniteNumber(action.scrollY, 0), target?.backendNodeId);
8749
8771
  else if (action.action === "drag") worked = await cdpDragForContext(contextId, normalizeActionPath(action.path));
8750
8772
  else if (action.action === "moveMouse") worked = await cdpMouseForContext(contextId, action.x, action.y, "mouseMoved");
@@ -4,6 +4,9 @@
4
4
  "description": "Desktop computer-use tools (observe/act) vendored from @injaneity/pi-computer-use",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
+ "scripts": {
8
+ "test:native-retention": "node scripts/test-native-retention.mjs"
9
+ },
7
10
  "pie": {
8
11
  "name": "computer-use",
9
12
  "main": "index.js"
@@ -11,6 +11,7 @@ const macosSourcePaths = [
11
11
  "agent_cursor.swift",
12
12
  "agent_cursor_motion.swift",
13
13
  "bridge.swift",
14
+ "ref_store.swift",
14
15
  ].map((file) => path.join(rootDir, "native", "macos", file));
15
16
  const windowsCrateDir = path.join(rootDir, "native", "windows", "bridge-rs");
16
17
  const linuxCrateDir = path.join(rootDir, "native", "linux", "bridge-rs");
@@ -20,7 +21,7 @@ const archTriples = {
20
21
  x64: "x86_64-apple-macosx",
21
22
  };
22
23
  const deploymentTarget = "14.0";
23
- const frameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI"];
24
+ const frameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI", "CryptoKit"];
24
25
  const defaultCodeSignIdentifier = "com.pie.computer-use";
25
26
 
26
27
  async function exists(filePath) {
@@ -20,7 +20,7 @@ const windowsCrateDir = path.join(rootDir, "native", "windows", "bridge-rs");
20
20
  const windowsHelperDestPath = process.env.PIE_COMPUTER_USE_WINDOWS_HELPER_PATH || path.join(resolvePieConfigDir(), "helpers", "computer-use", "windows-bridge.exe");
21
21
  const linuxCrateDir = path.join(rootDir, "native", "linux", "bridge-rs");
22
22
  const linuxHelperDestPath = process.env.PIE_COMPUTER_USE_LINUX_HELPER_PATH || path.join(resolvePieConfigDir(), "helpers", "computer-use", "linux-bridge");
23
- const helperSourcePaths = ["agent_cursor.swift", "agent_cursor_motion.swift", "bridge.swift"]
23
+ const helperSourcePaths = ["agent_cursor.swift", "agent_cursor_motion.swift", "bridge.swift", "ref_store.swift"]
24
24
  .map((file) => path.join(rootDir, "native", "macos", file));
25
25
  const packageJsonPath = path.join(rootDir, "package.json");
26
26
  const localCodeSignCommonName = "pie-computer-use Local Signing (com.pie.computer-use)";
@@ -57,7 +57,7 @@ const archTriples = {
57
57
  x64: "x86_64-apple-macosx",
58
58
  };
59
59
  const deploymentTarget = "14.0";
60
- const frameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI"];
60
+ const frameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI", "CryptoKit"];
61
61
  const defaultCodeSignIdentifier = "com.pie.computer-use";
62
62
 
63
63
  function normalizeArch(arch) {
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Compiles and runs the macOS native AXRefStore retention tests
4
+ // (native/macos/retention_tests.swift) from source, then compiles the full
5
+ // helper and drives its stdin JSON-lines transport with permission-free
6
+ // requests to assert the bridge-level stale-ref/stale-look integration.
7
+ //
8
+ // Nothing here needs Accessibility or Screen Recording permission or real UI
9
+ // interaction: the store tests use synthetic AXUIElement values, and the
10
+ // helper protocol probes only exercise failure paths (unknown look/element
11
+ // ids, missing windows). This script is macOS-only by design; on other
12
+ // platforms it skips with an explicit message (Linux/Windows helpers have
13
+ // their own native suites).
14
+
15
+ import { spawn } from "node:child_process";
16
+ import fs from "node:fs/promises";
17
+ import os from "node:os";
18
+ import path from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+
21
+ const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
22
+ const nativeDir = path.join(rootDir, "native", "macos");
23
+ const storeTestSources = ["ref_store.swift", "retention_tests.swift"].map((file) => path.join(nativeDir, file));
24
+ const helperSources = ["agent_cursor.swift", "agent_cursor_motion.swift", "bridge.swift", "ref_store.swift"].map((file) => path.join(nativeDir, file));
25
+ const deploymentTarget = "14.0";
26
+ const helperFrameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI", "CryptoKit"];
27
+
28
+ function run(command, args, options = {}) {
29
+ return new Promise((resolve, reject) => {
30
+ const child = spawn(command, args, { stdio: "inherit", ...options });
31
+ child.on("error", reject);
32
+ child.on("close", (code) => {
33
+ if (code === 0) {
34
+ resolve();
35
+ return;
36
+ }
37
+ reject(new Error(`Command failed (${code}): ${command} ${args.join(" ")}`));
38
+ });
39
+ });
40
+ }
41
+
42
+ function swiftcArgs(sources, output, moduleCache, frameworks) {
43
+ const args = [
44
+ "swiftc",
45
+ "-target",
46
+ `${process.arch === "arm64" ? "arm64" : "x86_64"}-apple-macosx${deploymentTarget}`,
47
+ "-module-cache-path",
48
+ moduleCache,
49
+ "-O",
50
+ ];
51
+ for (const framework of frameworks) args.push("-framework", framework);
52
+ args.push(...sources, "-o", output);
53
+ return args;
54
+ }
55
+
56
+ /** Drives the helper over its stdin transport and resolves one response per request id. */
57
+ function helperResponses(executable, requests, timeoutMs = 15_000) {
58
+ return new Promise((resolve, reject) => {
59
+ const child = spawn(executable, [], { stdio: ["pipe", "pipe", "ignore"] });
60
+ const pending = new Map(requests.map((request) => [request.id, request]));
61
+ const settled = new Map();
62
+ const fail = (error) => {
63
+ child.kill();
64
+ reject(error);
65
+ };
66
+ const timer = setTimeout(() => fail(new Error(`Helper did not answer all requests within ${timeoutMs}ms`)), timeoutMs);
67
+ let buffer = "";
68
+ child.stdout.setEncoding("utf8");
69
+ child.stdout.on("data", (chunk) => {
70
+ buffer += chunk;
71
+ let newline;
72
+ while ((newline = buffer.indexOf("\n")) >= 0) {
73
+ const line = buffer.slice(0, newline);
74
+ buffer = buffer.slice(newline + 1);
75
+ if (!line.trim()) continue;
76
+ let response;
77
+ try {
78
+ response = JSON.parse(line);
79
+ } catch {
80
+ continue;
81
+ }
82
+ if (typeof response.id === "string" && pending.delete(response.id)) {
83
+ settled.set(response.id, response);
84
+ if (pending.size === 0) {
85
+ clearTimeout(timer);
86
+ child.stdin.end();
87
+ child.on("close", () => resolve(settled));
88
+ child.kill();
89
+ }
90
+ }
91
+ }
92
+ });
93
+ child.on("error", fail);
94
+ for (const request of requests) child.stdin.write(`${JSON.stringify(request)}\n`);
95
+ });
96
+ }
97
+
98
+ /** Verifies the helper's true-exit path: shutdown answers, then the process exits zero on its own. */
99
+ function helperShutdownExits(executable, timeoutMs = 8_000) {
100
+ return new Promise((resolve, reject) => {
101
+ const child = spawn(executable, [], { stdio: ["pipe", "pipe", "ignore"] });
102
+ let buffer = "";
103
+ let sawResponse = false;
104
+ const fail = (error) => {
105
+ child.kill();
106
+ reject(error);
107
+ };
108
+ const timer = setTimeout(() => fail(new Error(`Helper did not shut down within ${timeoutMs}ms`)), timeoutMs);
109
+ child.stdout.setEncoding("utf8");
110
+ child.stdout.on("data", (chunk) => {
111
+ buffer += chunk;
112
+ let newline;
113
+ while ((newline = buffer.indexOf("\n")) >= 0) {
114
+ const line = buffer.slice(0, newline);
115
+ buffer = buffer.slice(newline + 1);
116
+ if (!line.trim()) continue;
117
+ let response;
118
+ try {
119
+ response = JSON.parse(line);
120
+ } catch {
121
+ continue;
122
+ }
123
+ if (response.id === "shutdown" && response.ok === true && response.result?.shuttingDown === true) sawResponse = true;
124
+ }
125
+ });
126
+ child.on("error", fail);
127
+ child.on("close", (code, signal) => {
128
+ clearTimeout(timer);
129
+ if (!sawResponse) {
130
+ reject(new Error("Helper did not acknowledge shutdown before exiting"));
131
+ return;
132
+ }
133
+ if (code === 0) {
134
+ resolve();
135
+ return;
136
+ }
137
+ reject(new Error(`Helper exited with ${code ?? "null"}${signal ? ` (signal ${signal})` : ""} instead of 0`));
138
+ });
139
+ child.stdin.write(`${JSON.stringify({ id: "shutdown", cmd: "shutdown" })}\n`);
140
+ });
141
+ }
142
+
143
+ function assert(condition, message) {
144
+ if (!condition) throw new Error(`[pie-computer-use] bridge integration assertion failed: ${message}`);
145
+ }
146
+
147
+ async function runBridgeIntegration(helperPath) {
148
+ const responses = await helperResponses(helperPath, [
149
+ { id: "diag", cmd: "diagnostics" },
150
+ { id: "act-stale", cmd: "act", lookId: "look_999", pid: 123, action: "press", target: { ref: "e1" } },
151
+ { id: "batch-stale", cmd: "actBatch", actions: [{ lookId: "look_999", pid: 123, action: "press", target: { ref: "e1" } }] },
152
+ { id: "hit-stale", cmd: "hitTest", lookId: "look_999", x: 10, y: 10 },
153
+ { id: "read-stale", cmd: "axReadText", elementRef: "e999999" },
154
+ { id: "look-stale-base", cmd: "look", baseLookId: "look_999", windowId: 12345 },
155
+ ]);
156
+ const diagnostics = responses.get("diag");
157
+ assert(diagnostics?.ok === true, "diagnostics must succeed");
158
+ assert(diagnostics.result?.protocolVersion === 6, "helper must keep protocol version 6");
159
+ assert(Array.isArray(diagnostics.result?.invariants) && diagnostics.result.invariants.includes("bounded-observation-history"), "helper must report the bounded-observation-history invariant");
160
+ for (const [id, expectedCode] of [
161
+ ["act-stale", "stale_look"],
162
+ ["batch-stale", "stale_look"],
163
+ ["hit-stale", "stale_look"],
164
+ ["read-stale", "element_ref_invalid"],
165
+ ["look-stale-base", "stale_look"],
166
+ ]) {
167
+ const response = responses.get(id);
168
+ assert(response?.ok === false, `${id} must fail`);
169
+ assert(response.error?.code === expectedCode, `${id} must fail with ${expectedCode}, got ${response.error?.code}`);
170
+ }
171
+ console.log("[pie-computer-use] bridge integration probes passed (stale look/ref failures, protocol v6).");
172
+ }
173
+
174
+ async function main() {
175
+ if (process.platform !== "darwin") {
176
+ console.log(`[pie-computer-use] skipping native retention tests: they are macOS-only and cannot run on ${process.platform}.`);
177
+ return;
178
+ }
179
+ let tempDir;
180
+ try {
181
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "pie-computer-use-retention-"));
182
+ const moduleCache = path.join(tempDir, "module-cache");
183
+ const storeTestOutput = path.join(tempDir, "retention_tests");
184
+ console.log("[pie-computer-use] compiling native retention tests...");
185
+ await run("xcrun", swiftcArgs(storeTestSources, storeTestOutput, moduleCache, ["ApplicationServices", "CryptoKit"]));
186
+ console.log("[pie-computer-use] running native retention tests...");
187
+ await run(storeTestOutput, []);
188
+
189
+ const helperOutput = path.join(tempDir, "pie-computer-use");
190
+ console.log("[pie-computer-use] compiling native helper for bridge integration probes...");
191
+ await run("xcrun", swiftcArgs(helperSources, helperOutput, moduleCache, helperFrameworks));
192
+ await runBridgeIntegration(helperOutput);
193
+ await helperShutdownExits(helperOutput);
194
+ console.log("[pie-computer-use] helper shutdown exits cleanly with code 0.");
195
+ console.log("[pie-computer-use] native retention tests passed.");
196
+ } finally {
197
+ if (tempDir) await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
198
+ }
199
+ }
200
+
201
+ main().catch((error) => {
202
+ console.error(error instanceof Error ? error.message : String(error));
203
+ process.exit(1);
204
+ });
@@ -24535,6 +24535,24 @@ var McpServerManager = class {
24535
24535
  getDefinition(server) {
24536
24536
  return this.definitions.get(sanitizeName(server));
24537
24537
  }
24538
+ /**
24539
+ * Resolve a `<server>_<tool>` prefixed name against CONFIGURED server names.
24540
+ * Splitting at the first underscore breaks servers whose own name contains
24541
+ * underscores (`my_server_echo` would resolve to server "my"), so match the
24542
+ * longest configured server name that prefixes the tool name instead. When
24543
+ * several configured names prefix (e.g. servers `my` and `my_server` both
24544
+ * exist), the longest match wins: that is how toPrefixedName() would have
24545
+ * constructed the deepest claim, and it is deterministic.
24546
+ */
24547
+ resolvePrefixedName(prefixedName) {
24548
+ let best = null;
24549
+ for (const key of this.definitions.keys()) {
24550
+ if (!prefixedName.startsWith(`${key}_`)) continue;
24551
+ if (best !== null && key.length <= best.server.length) continue;
24552
+ best = { server: key, tool: prefixedName.slice(key.length + 1) };
24553
+ }
24554
+ return best;
24555
+ }
24538
24556
  listDefinitions() {
24539
24557
  return [...this.definitions.values()];
24540
24558
  }
@@ -24788,13 +24806,10 @@ var McpServerManager = class {
24788
24806
  let server = descriptor?.server;
24789
24807
  let toolName = descriptor?.tool;
24790
24808
  if (!server) {
24791
- const separator = prefixedName.indexOf("_");
24792
- if (separator > 0) {
24793
- const candidate = prefixedName.slice(0, separator);
24794
- if (this.definitions.has(candidate)) {
24795
- server = candidate;
24796
- toolName = prefixedName.slice(separator + 1);
24797
- }
24809
+ const resolved = this.resolvePrefixedName(prefixedName);
24810
+ if (resolved) {
24811
+ server = resolved.server;
24812
+ toolName = resolved.tool;
24798
24813
  }
24799
24814
  }
24800
24815
  if (!server || !toolName) {
@@ -24828,9 +24843,15 @@ ${tail}` : "")
24828
24843
  error: `MCP connection to "${server}" closed before the call could start.`
24829
24844
  };
24830
24845
  }
24846
+ if (signal?.aborted) {
24847
+ return {
24848
+ delivery: "not_sent",
24849
+ error: `MCP call "${prefixedName}" was cancelled before the request was sent; nothing was executed.`
24850
+ };
24851
+ }
24831
24852
  try {
24832
24853
  const result = await withTimeout(
24833
- client.callTool({ name: toolName, arguments: args }),
24854
+ client.callTool({ name: toolName, arguments: args }, void 0, signal !== void 0 ? { signal } : void 0),
24834
24855
  CALL_TIMEOUT_MS,
24835
24856
  `call "${prefixedName}"`,
24836
24857
  signal
@@ -24843,6 +24864,12 @@ ${tail}` : "")
24843
24864
  };
24844
24865
  } catch (error2) {
24845
24866
  const message = error2 instanceof Error ? error2.message : String(error2);
24867
+ if (error2 instanceof ProtocolError && (error2.code === ProtocolErrorCode.InvalidRequest || error2.code === ProtocolErrorCode.InvalidParams) && /output schema|structured content/i.test(message)) {
24868
+ return {
24869
+ delivery: "invalid_result",
24870
+ error: `MCP server "${server}" returned a result that fails its own outputSchema: ${message}. The call DID reach the server and the tool likely executed \u2014 do NOT retry automatically; verify state with a read-only tool first.`
24871
+ };
24872
+ }
24846
24873
  if (runtime.client === void 0 || /closed|aborted|timed out|socket|ECONNRESET/i.test(message)) {
24847
24874
  return {
24848
24875
  delivery: "may_have_run",
@@ -24990,7 +25017,7 @@ function structuredContentIsMirroredInText(textBlocks, structured) {
24990
25017
  }
24991
25018
  function mapMcpOutcome(outcome, maxBytes) {
24992
25019
  if (outcome.delivery !== "ok") {
24993
- const prefix = outcome.delivery === "may_have_run" ? "[outcome unknown]" : "[not executed]";
25020
+ const prefix = outcome.delivery === "may_have_run" || outcome.delivery === "invalid_result" ? "[outcome unknown]" : "[not executed]";
24994
25021
  return {
24995
25022
  content: [{ type: "text", text: `${prefix} ${outcome.error ?? "MCP call failed"}` }],
24996
25023
  details: { mode: "mcp", delivery: outcome.delivery, ...outcome.protocolCode !== void 0 ? { protocolCode: outcome.protocolCode } : {} },
@@ -25077,8 +25104,8 @@ ${truncate(JSON.stringify(schema ?? {}, null, 2), maxBytes)}` : `unknown MCP too
25077
25104
  isError: descriptor === void 0
25078
25105
  };
25079
25106
  }
25080
- const separator = params.tool.indexOf("_");
25081
- const server = descriptor?.server ?? (separator > 0 ? params.tool.slice(0, separator) : "");
25107
+ const resolved = getManager().resolvePrefixedName(params.tool);
25108
+ const server = descriptor?.server ?? resolved?.server ?? "";
25082
25109
  if (server) {
25083
25110
  const trust = await ensureTrusted(server);
25084
25111
  if (!trust.trusted) {
@@ -25267,10 +25294,18 @@ function mcpAdapter(ctx) {
25267
25294
  const filePath = path4.join(ctx.cwd, ".pie", "mcp-adapter.json");
25268
25295
  const parsed = readProjectOverride();
25269
25296
  const servers = parsed.mcpServers && typeof parsed.mcpServers === "object" && !Array.isArray(parsed.mcpServers) ? { ...parsed.mcpServers } : {};
25297
+ const existing = servers[serverName];
25298
+ const existingObj = existing && typeof existing === "object" && !Array.isArray(existing) ? { ...existing } : {};
25270
25299
  if (disabled === null) {
25271
- delete servers[serverName];
25300
+ delete existingObj.disabled;
25301
+ if (Object.keys(existingObj).length === 0) {
25302
+ delete servers[serverName];
25303
+ } else {
25304
+ servers[serverName] = existingObj;
25305
+ }
25272
25306
  } else {
25273
- servers[serverName] = { disabled };
25307
+ existingObj.disabled = disabled;
25308
+ servers[serverName] = existingObj;
25274
25309
  }
25275
25310
  parsed.mcpServers = servers;
25276
25311
  fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
@@ -7,9 +7,9 @@ import {
7
7
  isPlanModeSafeCommand,
8
8
  markCompletedPlanSteps,
9
9
  restoreExecutionState
10
- } from "../../../chunks/chunk-UPPICLVS.js";
11
- import "../../../chunks/chunk-QJH6YN2W.js";
12
- import "../../../chunks/chunk-IM43UYVV.js";
10
+ } from "../../../chunks/chunk-FKUEIYFQ.js";
11
+ import "../../../chunks/chunk-ZKHMNEXL.js";
12
+ import "../../../chunks/chunk-YRXXRLMQ.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-P3QPJXS7.js";
4
+ } from "../../../chunks/chunk-XVUV3IM2.js";
5
5
  import {
6
6
  createSharedFileSystemTools,
7
7
  createSubagentCapability
8
- } from "../../../chunks/chunk-UPPICLVS.js";
9
- import "../../../chunks/chunk-QJH6YN2W.js";
10
- import "../../../chunks/chunk-IM43UYVV.js";
8
+ } from "../../../chunks/chunk-FKUEIYFQ.js";
9
+ import "../../../chunks/chunk-ZKHMNEXL.js";
10
+ import "../../../chunks/chunk-YRXXRLMQ.js";
11
11
  import "../../../chunks/chunk-5353DEEC.js";
12
12
  import "../../../chunks/chunk-XXGYUYXI.js";
13
13
  import "../../../chunks/chunk-LZQXQOPJ.js";
@@ -8,9 +8,9 @@ import {
8
8
  executeManageTodoList,
9
9
  executionStateToTodos,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-UPPICLVS.js";
12
- import "../../../chunks/chunk-QJH6YN2W.js";
13
- import "../../../chunks/chunk-IM43UYVV.js";
11
+ } from "../../../chunks/chunk-FKUEIYFQ.js";
12
+ import "../../../chunks/chunk-ZKHMNEXL.js";
13
+ import "../../../chunks/chunk-YRXXRLMQ.js";
14
14
  import "../../../chunks/chunk-XXGYUYXI.js";
15
15
  import "../../../chunks/chunk-LZQXQOPJ.js";
16
16
 
@@ -21,6 +21,6 @@ function tryFastPath(argv, options) {
21
21
 
22
22
  // vendor/chrome-devtools-axi/bin/chrome-devtools-axi.ts
23
23
  if (!tryFastPath(process.argv.slice(2), { version: VERSION })) {
24
- const { main } = await import("./cli-F2VCSVL4.js");
24
+ const { main } = await import("./cli-P4VG6ZZG.js");
25
25
  await main(process.argv.slice(2));
26
26
  }
@@ -1612,7 +1612,7 @@ function installSessionStartHooks(options = {}) {
1612
1612
 
1613
1613
  // vendor/chrome-devtools-axi/src/client.ts
1614
1614
  import { execFileSync, spawn as spawn2 } from "node:child_process";
1615
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
1615
+ import { readFileSync as readFileSync3, existsSync as existsSync3, unlinkSync } from "node:fs";
1616
1616
  import { request } from "node:http";
1617
1617
  import { dirname as dirname3 } from "node:path";
1618
1618
 
@@ -1945,6 +1945,39 @@ function isBridgeProcess(pid) {
1945
1945
  return false;
1946
1946
  }
1947
1947
  }
1948
+ async function probeBridgeIdentity(port, sessionName) {
1949
+ try {
1950
+ const resp = await httpGet(port, "/health", HEALTH_TIMEOUT_MS);
1951
+ const data = JSON.parse(resp);
1952
+ if (data.status !== "ok") return "unknown";
1953
+ if (typeof data.session === "string" && data.session !== sessionName) {
1954
+ return "other";
1955
+ }
1956
+ return "own";
1957
+ } catch {
1958
+ return "unknown";
1959
+ }
1960
+ }
1961
+ async function judgeRecordedBridge(pidInfo, sessionName) {
1962
+ const identity = await probeBridgeIdentity(pidInfo.port, sessionName);
1963
+ if (identity === "own") {
1964
+ return {
1965
+ terminate: true,
1966
+ killProcessGroup: isBridgeProcess(pidInfo.pid)
1967
+ };
1968
+ }
1969
+ if (identity === "other") {
1970
+ return { terminate: false, killProcessGroup: false };
1971
+ }
1972
+ const bridge = isBridgeProcess(pidInfo.pid);
1973
+ return { terminate: bridge, killProcessGroup: bridge };
1974
+ }
1975
+ function removePidFile(pidFile) {
1976
+ try {
1977
+ unlinkSync(pidFile);
1978
+ } catch {
1979
+ }
1980
+ }
1948
1981
  async function terminateBridgeProcess(pid, opts = {}) {
1949
1982
  if (!isProcessAlive(pid)) return;
1950
1983
  const killProcessGroup = opts.killProcessGroup === true;
@@ -2065,9 +2098,14 @@ async function ensureBridge(spawnBridge = spawnBridgeProcess, notice) {
2065
2098
  })) {
2066
2099
  return pidInfo.port;
2067
2100
  }
2068
- await terminateBridgeProcess(pidInfo.pid, {
2069
- killProcessGroup: isBridgeProcess(pidInfo.pid)
2070
- });
2101
+ const verdict = await judgeRecordedBridge(pidInfo, sessionName);
2102
+ if (verdict.terminate) {
2103
+ await terminateBridgeProcess(pidInfo.pid, {
2104
+ killProcessGroup: verdict.killProcessGroup
2105
+ });
2106
+ } else {
2107
+ removePidFile(pidFile);
2108
+ }
2071
2109
  }
2072
2110
  clearSelectedPageId();
2073
2111
  const child = spawnBridge(port, sessionName);
@@ -2338,8 +2376,10 @@ async function stopBridge() {
2338
2376
  const pidInfo = readPidFile();
2339
2377
  if (!pidInfo) return false;
2340
2378
  if (!isProcessAlive(pidInfo.pid)) return false;
2379
+ const verdict = await judgeRecordedBridge(pidInfo, resolveSessionName());
2380
+ if (!verdict.terminate) return false;
2341
2381
  await terminateBridgeProcess(pidInfo.pid, {
2342
- killProcessGroup: isBridgeProcess(pidInfo.pid)
2382
+ killProcessGroup: verdict.killProcessGroup
2343
2383
  });
2344
2384
  clearSelectedPageId();
2345
2385
  return true;
@@ -2350,7 +2390,7 @@ import {
2350
2390
  existsSync as existsSync4,
2351
2391
  mkdirSync as mkdirSync2,
2352
2392
  readFileSync as readFileSync4,
2353
- unlinkSync,
2393
+ unlinkSync as unlinkSync2,
2354
2394
  writeFileSync as writeFileSync2
2355
2395
  } from "node:fs";
2356
2396
  import { dirname as dirname4, join as join3 } from "node:path";
@@ -2379,7 +2419,7 @@ function bumpGeneration() {
2379
2419
  }
2380
2420
 
2381
2421
  // vendor/chrome-devtools-axi/src/run.ts
2382
- import { mkdtempSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, rmdirSync } from "node:fs";
2422
+ import { mkdtempSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, rmdirSync } from "node:fs";
2383
2423
  import { join as join4 } from "node:path";
2384
2424
  import { tmpdir } from "node:os";
2385
2425
  import { pathToFileURL } from "node:url";
@@ -2786,7 +2826,7 @@ async function runScript(content, callTool2) {
2786
2826
  globalThis.page = prevPage;
2787
2827
  }
2788
2828
  try {
2789
- unlinkSync2(tmpFile);
2829
+ unlinkSync3(tmpFile);
2790
2830
  rmdirSync(tmpDir);
2791
2831
  } catch {
2792
2832
  }