pi-cursor-bridge 0.1.17 → 0.2.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.
@@ -10975,7 +10975,7 @@ function powershellWindowScript(options) {
10975
10975
  ` Remove-Item -LiteralPath '${showFlagPath}' -Force -ErrorAction SilentlyContinue`,
10976
10976
  "}"
10977
10977
  ].join("\n");
10978
- const apply = lifetime ? lifetimeLoop : loop ? `for ($i = 0; $i -lt ${iterations}; $i++) { ${hideIfAllowed}; Start-Sleep -Milliseconds ${intervalMs} }` : `$changed = [CursorBridgeWindowControl]::Apply(${targetPid}, ${show}); [Console]::Out.Write($changed)`;
10978
+ const apply = lifetime ? lifetimeLoop : loop ? `for ($i = 0; $i -lt ${iterations}; $i++) { ${hideIfAllowed}; Start-Sleep -Milliseconds ${intervalMs} }` : `$changed = [CursorBridgeWindowControl]::Apply(${targetPid}, ${show}${options.scope === "agents" ? ", $true" : ""}); [Console]::Out.Write($changed)`;
10979
10979
  return `$ErrorActionPreference = 'Stop'
10980
10980
  Add-Type -TypeDefinition @'
10981
10981
  ${WINDOW_CONTROL_TYPE}
@@ -10989,6 +10989,8 @@ function setCursorWindowPresentation(options = {}) {
10989
10989
  const platform = options.platform || process.platform;
10990
10990
  const action = String(options.action || "").trim().toLowerCase();
10991
10991
  if (!["hide", "show"].includes(action)) throw new Error(`unsupported Cursor window action: ${options.action}`);
10992
+ const scope = options.scope ?? "process";
10993
+ if (!["process", "agents"].includes(scope)) throw new Error(`unsupported Cursor window scope: ${scope}`);
10992
10994
  if (platform !== "win32") {
10993
10995
  return { supported: false, applied: false, action, reason: `window control is not implemented for ${platform}` };
10994
10996
  }
@@ -10999,12 +11001,14 @@ function setCursorWindowPresentation(options = {}) {
10999
11001
  }
11000
11002
  const showFlagPath = resolve(options.showFlagPath || join(dirname(resolveCursorRuntimeFile()), `show-${pid}.flag`));
11001
11003
  try {
11002
- if (action === "show") {
11003
- mkdirSync(dirname(showFlagPath), { recursive: true });
11004
- writeFileSync(showFlagPath, `${pid}
11004
+ if (scope === "process") {
11005
+ if (action === "show") {
11006
+ mkdirSync(dirname(showFlagPath), { recursive: true });
11007
+ writeFileSync(showFlagPath, `${pid}
11005
11008
  `, { encoding: "utf8", mode: 384 });
11006
- } else {
11007
- rmSync(showFlagPath, { force: true });
11009
+ } else {
11010
+ rmSync(showFlagPath, { force: true });
11011
+ }
11008
11012
  }
11009
11013
  } catch (error2) {
11010
11014
  return {
@@ -11018,7 +11022,7 @@ function setCursorWindowPresentation(options = {}) {
11018
11022
  }
11019
11023
  const run = options.execFileSyncImpl || execFileSync;
11020
11024
  try {
11021
- const script = powershellWindowScript({ pid, action });
11025
+ const script = powershellWindowScript({ pid, action, scope });
11022
11026
  const output = run("powershell.exe", [
11023
11027
  "-NoLogo",
11024
11028
  "-NoProfile",
@@ -11034,9 +11038,18 @@ function setCursorWindowPresentation(options = {}) {
11034
11038
  timeout: Number(options.timeoutMs || 15e3)
11035
11039
  });
11036
11040
  const changedWindows = Number(String(output || "").trim() || 0);
11037
- return { supported: true, applied: true, action, port, pid, changedWindows, showFlagPath };
11041
+ return {
11042
+ supported: true,
11043
+ applied: scope !== "agents" || changedWindows > 0,
11044
+ action,
11045
+ scope,
11046
+ port,
11047
+ pid,
11048
+ changedWindows,
11049
+ ...scope === "process" ? { showFlagPath } : {}
11050
+ };
11038
11051
  } catch (error2) {
11039
- if (action === "show") rmSync(showFlagPath, { force: true });
11052
+ if (action === "show" && scope === "process") rmSync(showFlagPath, { force: true });
11040
11053
  return {
11041
11054
  supported: true,
11042
11055
  applied: false,
@@ -11100,6 +11113,7 @@ var init_cursor_runtime = __esm({
11100
11113
  using System;
11101
11114
  using System.Runtime.InteropServices;
11102
11115
  using System.Text;
11116
+ using System.Collections.Generic;
11103
11117
 
11104
11118
  public static class CursorBridgeWindowControl {
11105
11119
  [StructLayout(LayoutKind.Sequential)]
@@ -11119,6 +11133,7 @@ public static class CursorBridgeWindowControl {
11119
11133
  [DllImport("user32.dll", EntryPoint = "IsWindowArranged")] private static extern bool IsWindowArranged(IntPtr hWnd);
11120
11134
  [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
11121
11135
  [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowTextLengthW(IntPtr hWnd);
11136
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowTextW(IntPtr hWnd, StringBuilder text, int maxCount);
11122
11137
  [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetClassNameW(IntPtr hWnd, StringBuilder className, int maxCount);
11123
11138
  [DllImport("user32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int command);
11124
11139
  [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr insertAfter, int x, int y, int width, int height, uint flags);
@@ -11142,7 +11157,12 @@ public static class CursorBridgeWindowControl {
11142
11157
  }
11143
11158
 
11144
11159
  public static int Apply(int expectedProcessId, bool show) {
11160
+ return Apply(expectedProcessId, show, false);
11161
+ }
11162
+
11163
+ public static int Apply(int expectedProcessId, bool show, bool agentsOnly) {
11145
11164
  int changed = 0;
11165
+ List<IntPtr> windows = new List<IntPtr>();
11146
11166
  EnumWindows((hWnd, lParam) => {
11147
11167
  uint processId;
11148
11168
  GetWindowThreadProcessId(hWnd, out processId);
@@ -11150,6 +11170,17 @@ public static class CursorBridgeWindowControl {
11150
11170
  StringBuilder className = new StringBuilder(256);
11151
11171
  GetClassNameW(hWnd, className, className.Capacity);
11152
11172
  if (!String.Equals(className.ToString(), "Chrome_WidgetWin_1", StringComparison.Ordinal)) return true;
11173
+ if (agentsOnly) {
11174
+ StringBuilder title = new StringBuilder(GetWindowTextLengthW(hWnd) + 1);
11175
+ GetWindowTextW(hWnd, title, title.Capacity);
11176
+ if (!String.Equals(title.ToString(), "Cursor Agents", StringComparison.Ordinal)) return true;
11177
+ }
11178
+ windows.Add(hWnd);
11179
+ return true;
11180
+ }, IntPtr.Zero);
11181
+ // Automatic recovery must never broaden an absent or ambiguous Agents match.
11182
+ if (agentsOnly && windows.Count != 1) return 0;
11183
+ foreach (IntPtr hWnd in windows) {
11153
11184
  bool visible = IsWindowVisible(hWnd);
11154
11185
  if (show) {
11155
11186
  // SWP_SHOWWINDOW + SWP_NOACTIVATE preserves minimized/maximized/arranged
@@ -11177,8 +11208,7 @@ public static class CursorBridgeWindowControl {
11177
11208
  if (restored || pulsed || redrawn) changed++;
11178
11209
  }
11179
11210
  if (!show && visible) { if (ShowWindowAsync(hWnd, 0)) changed++; }
11180
- return true;
11181
- }, IntPtr.Zero);
11211
+ }
11182
11212
  return changed;
11183
11213
  }
11184
11214
  }
@@ -22356,6 +22386,272 @@ var import_subprotocol = __toESM(require_subprotocol(), 1);
22356
22386
  var import_websocket = __toESM(require_websocket(), 1);
22357
22387
  var import_websocket_server = __toESM(require_websocket_server(), 1);
22358
22388
 
22389
+ // cdp-client.mjs
22390
+ var ERROR_JSON_LIMIT = 2048;
22391
+ function positiveTimeout(name, value, fallback) {
22392
+ const timeout = value === void 0 ? fallback : Number(value);
22393
+ if (!Number.isFinite(timeout) || timeout <= 0) {
22394
+ const error2 = new TypeError(`${name} must be a positive finite number`);
22395
+ error2.code = "CDP_INVALID_TIMEOUT";
22396
+ error2.stage = "configuration";
22397
+ error2.cdp = { stage: "configuration", elapsedMs: 0 };
22398
+ throw error2;
22399
+ }
22400
+ return timeout;
22401
+ }
22402
+ function clientError(message, { code, stage, method, cause, elapsedMs = 0 } = {}) {
22403
+ const error2 = new Error(message, cause ? { cause } : void 0);
22404
+ error2.name = "CdpClientError";
22405
+ error2.code = code || cause?.code || "CDP_CLIENT_ERROR";
22406
+ error2.stage = stage || "unknown";
22407
+ if (method) error2.method = method;
22408
+ error2.cdp = {
22409
+ stage: error2.stage,
22410
+ ...method ? { method } : {},
22411
+ elapsedMs: Math.max(0, Number(elapsedMs) || 0)
22412
+ };
22413
+ return error2;
22414
+ }
22415
+ function boundedJson(value) {
22416
+ let text;
22417
+ try {
22418
+ text = JSON.stringify(value);
22419
+ } catch {
22420
+ text = String(value);
22421
+ }
22422
+ if (text === void 0) text = String(value);
22423
+ return text.length <= ERROR_JSON_LIMIT ? text : `${text.slice(0, ERROR_JSON_LIMIT)}\u2026`;
22424
+ }
22425
+ function makeClient(wsUrl, options = {}) {
22426
+ const createdAt = Date.now();
22427
+ const connectTimeoutMs = positiveTimeout("connectTimeoutMs", options.connectTimeoutMs, 5e3);
22428
+ const commandTimeoutMs = positiveTimeout("commandTimeoutMs", options.commandTimeoutMs, 3e4);
22429
+ const WebSocketImpl = options.WebSocketImpl || import_websocket.default;
22430
+ const websocketOptions = { handshakeTimeout: connectTimeoutMs };
22431
+ if (options.origin !== void 0) websocketOptions.origin = options.origin;
22432
+ let ws;
22433
+ let state = "connecting";
22434
+ let nextId = 0;
22435
+ let connectTimer;
22436
+ let readyResolve;
22437
+ let readyReject;
22438
+ const pending = /* @__PURE__ */ new Map();
22439
+ const ready = new Promise((resolve8, reject) => {
22440
+ readyResolve = resolve8;
22441
+ readyReject = reject;
22442
+ });
22443
+ void ready.catch(() => {
22444
+ });
22445
+ const terminate = () => {
22446
+ if (!ws) return;
22447
+ try {
22448
+ if (typeof ws.terminate === "function") ws.terminate();
22449
+ else ws.close();
22450
+ } catch {
22451
+ }
22452
+ };
22453
+ const rejectPending = (error2) => {
22454
+ for (const entry of pending.values()) {
22455
+ clearTimeout(entry.timer);
22456
+ entry.reject(typeof error2 === "function" ? error2(entry) : error2);
22457
+ }
22458
+ pending.clear();
22459
+ };
22460
+ const rejectReady = (error2) => {
22461
+ if (state !== "connecting") return;
22462
+ state = "failed";
22463
+ clearTimeout(connectTimer);
22464
+ readyReject(error2);
22465
+ };
22466
+ const failConnection = (cause) => {
22467
+ const error2 = clientError(`CDP WebSocket connection failed: ${cause?.message || cause}`, {
22468
+ stage: "connect",
22469
+ cause,
22470
+ elapsedMs: Date.now() - createdAt
22471
+ });
22472
+ rejectReady(error2);
22473
+ terminate();
22474
+ return error2;
22475
+ };
22476
+ const failSocket = (cause) => {
22477
+ const error2 = clientError(`CDP WebSocket failed: ${cause?.message || cause}`, {
22478
+ stage: "socket",
22479
+ cause,
22480
+ elapsedMs: Date.now() - createdAt
22481
+ });
22482
+ if (state === "connecting") return failConnection(cause);
22483
+ if (state === "open") state = "failed";
22484
+ rejectPending((entry) => clientError(`CDP WebSocket failed during ${entry.method}: ${cause?.message || cause}`, {
22485
+ stage: "socket",
22486
+ method: entry.method,
22487
+ cause,
22488
+ elapsedMs: Date.now() - entry.startedAt
22489
+ }));
22490
+ terminate();
22491
+ return error2;
22492
+ };
22493
+ try {
22494
+ ws = new WebSocketImpl(wsUrl, websocketOptions);
22495
+ } catch (cause) {
22496
+ failConnection(cause);
22497
+ }
22498
+ if (ws) {
22499
+ connectTimer = setTimeout(() => {
22500
+ const error2 = clientError(`CDP WebSocket connection timed out (${connectTimeoutMs}ms)`, {
22501
+ code: "CDP_CONNECT_TIMEOUT",
22502
+ stage: "connect",
22503
+ elapsedMs: Date.now() - createdAt
22504
+ });
22505
+ rejectReady(error2);
22506
+ terminate();
22507
+ }, connectTimeoutMs);
22508
+ ws.on("open", () => {
22509
+ if (state !== "connecting") {
22510
+ terminate();
22511
+ return;
22512
+ }
22513
+ clearTimeout(connectTimer);
22514
+ state = "open";
22515
+ readyResolve();
22516
+ });
22517
+ ws.on("message", (data) => {
22518
+ let message;
22519
+ try {
22520
+ message = JSON.parse(data.toString());
22521
+ } catch {
22522
+ return;
22523
+ }
22524
+ if (!message || !pending.has(message.id)) return;
22525
+ const entry = pending.get(message.id);
22526
+ pending.delete(message.id);
22527
+ clearTimeout(entry.timer);
22528
+ if (message.error) {
22529
+ const detail = boundedJson(message.error);
22530
+ entry.reject(clientError(`CDP command failed: ${entry.method}: ${detail}`, {
22531
+ code: message.error.code ?? "CDP_PROTOCOL_ERROR",
22532
+ stage: "protocol",
22533
+ method: entry.method,
22534
+ elapsedMs: Date.now() - entry.startedAt
22535
+ }));
22536
+ } else {
22537
+ entry.resolve(message.result);
22538
+ }
22539
+ });
22540
+ ws.on("error", (cause) => {
22541
+ if (state === "closed" || state === "failed") return;
22542
+ failSocket(cause);
22543
+ });
22544
+ ws.on("close", (code, reason) => {
22545
+ if (state === "closed" || state === "failed") return;
22546
+ const suffix = code ? ` (code=${code}${reason?.length ? ` reason=${String(reason).slice(0, 256)}` : ""})` : "";
22547
+ const error2 = clientError(`CDP WebSocket closed${suffix}`, {
22548
+ code: "CDP_SOCKET_CLOSED",
22549
+ stage: state === "connecting" ? "connect" : "socket",
22550
+ elapsedMs: Date.now() - createdAt
22551
+ });
22552
+ if (state === "connecting") rejectReady(error2);
22553
+ else {
22554
+ state = "closed";
22555
+ rejectPending((entry) => clientError(`CDP WebSocket closed during ${entry.method}${suffix}`, {
22556
+ code: "CDP_SOCKET_CLOSED",
22557
+ stage: "socket",
22558
+ method: entry.method,
22559
+ elapsedMs: Date.now() - entry.startedAt
22560
+ }));
22561
+ }
22562
+ });
22563
+ }
22564
+ const send = (method, params = {}, sendOptions = {}) => {
22565
+ if (state !== "open" || !ws || ws.readyState !== (WebSocketImpl.OPEN ?? 1)) {
22566
+ return Promise.reject(clientError(`CDP command cannot be sent before the WebSocket is open: ${method}`, {
22567
+ code: "CDP_NOT_OPEN",
22568
+ stage: "send",
22569
+ method,
22570
+ elapsedMs: 0
22571
+ }));
22572
+ }
22573
+ let timeoutMs;
22574
+ try {
22575
+ timeoutMs = sendOptions.timeoutMs === void 0 ? commandTimeoutMs : Math.min(commandTimeoutMs, positiveTimeout("timeoutMs", sendOptions.timeoutMs));
22576
+ } catch (error2) {
22577
+ return Promise.reject(error2);
22578
+ }
22579
+ const id = ++nextId;
22580
+ let payload;
22581
+ try {
22582
+ payload = JSON.stringify({ id, method, params });
22583
+ } catch (cause) {
22584
+ return Promise.reject(clientError(`CDP command could not be serialized: ${method}: ${cause.message}`, {
22585
+ stage: "send",
22586
+ method,
22587
+ cause,
22588
+ elapsedMs: 0
22589
+ }));
22590
+ }
22591
+ return new Promise((resolve8, reject) => {
22592
+ const startedAt = Date.now();
22593
+ const timer = setTimeout(() => {
22594
+ if (!pending.delete(id)) return;
22595
+ reject(clientError(`CDP command timed out: ${method} (${timeoutMs}ms)`, {
22596
+ code: "CDP_COMMAND_TIMEOUT",
22597
+ stage: "command",
22598
+ method,
22599
+ elapsedMs: Date.now() - startedAt
22600
+ }));
22601
+ }, timeoutMs);
22602
+ pending.set(id, { method, resolve: resolve8, reject, timer, startedAt });
22603
+ try {
22604
+ ws.send(payload, (cause) => {
22605
+ if (!cause || !pending.has(id)) return;
22606
+ const entry = pending.get(id);
22607
+ pending.delete(id);
22608
+ clearTimeout(entry.timer);
22609
+ entry.reject(clientError(`CDP command send failed: ${method}: ${cause.message}`, {
22610
+ stage: "send",
22611
+ method,
22612
+ cause,
22613
+ elapsedMs: Date.now() - startedAt
22614
+ }));
22615
+ });
22616
+ } catch (cause) {
22617
+ pending.delete(id);
22618
+ clearTimeout(timer);
22619
+ reject(clientError(`CDP command send failed: ${method}: ${cause.message}`, {
22620
+ stage: "send",
22621
+ method,
22622
+ cause,
22623
+ elapsedMs: Date.now() - startedAt
22624
+ }));
22625
+ }
22626
+ });
22627
+ };
22628
+ const close = () => {
22629
+ if (state === "closed") return;
22630
+ const wasConnecting = state === "connecting";
22631
+ state = "closed";
22632
+ clearTimeout(connectTimer);
22633
+ const error2 = clientError("CDP client closed", {
22634
+ code: "CDP_CLIENT_CLOSED",
22635
+ stage: wasConnecting ? "connect" : "socket",
22636
+ elapsedMs: Date.now() - createdAt
22637
+ });
22638
+ if (wasConnecting) readyReject(error2);
22639
+ rejectPending((entry) => clientError(`CDP client closed during ${entry.method}`, {
22640
+ code: "CDP_CLIENT_CLOSED",
22641
+ stage: "socket",
22642
+ method: entry.method,
22643
+ elapsedMs: Date.now() - entry.startedAt
22644
+ }));
22645
+ if (!ws) return;
22646
+ try {
22647
+ if (wasConnecting && typeof ws.terminate === "function") ws.terminate();
22648
+ else ws.close();
22649
+ } catch {
22650
+ }
22651
+ };
22652
+ return { ready, send, close };
22653
+ }
22654
+
22359
22655
  // server.mjs
22360
22656
  init_cursor_runtime();
22361
22657
  import http2 from "http";
@@ -22605,7 +22901,7 @@ function updateCursorSessionRegistry(filePath, mutator, options = {}) {
22605
22901
  // server.mjs
22606
22902
  init_cursor_ensure_core();
22607
22903
  init_lifecycle_paths();
22608
- var PLUGIN_VERSION = "5.10.1";
22904
+ var PLUGIN_VERSION = "6.0.1";
22609
22905
  var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
22610
22906
  var ORIGIN = `http://localhost:${CDP_PORT2}`;
22611
22907
  var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
@@ -22786,17 +23082,11 @@ function shouldRecoverNormalAgentsPresentation({
22786
23082
  return Number(now) - previousAt >= Math.max(0, Number(refreshMs) || 0);
22787
23083
  }
22788
23084
  async function inspectPageTarget(page) {
22789
- const c = makeClient(page.webSocketDebuggerUrl);
22790
23085
  const probeMs = Number(process.env.CURSOR_BRIDGE_PAGE_PROBE_TIMEOUT || 5e3);
23086
+ const c = makeClient2(page.webSocketDebuggerUrl, { connectTimeoutMs: probeMs });
22791
23087
  try {
22792
- await Promise.race([
22793
- c.ready,
22794
- new Promise((_, reject) => setTimeout(() => reject(new Error("Timed out connecting to the CDP target")), probeMs))
22795
- ]);
22796
- const raw = await Promise.race([
22797
- evalJS(c, EXPR_PAGE_CAPABILITIES),
22798
- new Promise((_, reject) => setTimeout(() => reject(new Error("Timed out probing the CDP target")), probeMs))
22799
- ]);
23088
+ await c.ready;
23089
+ const raw = await evalJS(c, EXPR_PAGE_CAPABILITIES, { timeoutMs: probeMs });
22800
23090
  return { ...page, capabilities: JSON.parse(raw || "{}") };
22801
23091
  } catch (error2) {
22802
23092
  return { ...page, capabilities: null, probeError: error2.message };
@@ -22844,73 +23134,22 @@ async function findPage(options = {}) {
22844
23134
  const inspected = await Promise.all(pages.map(inspectPageTarget));
22845
23135
  return selectPageForUiPreference(inspected, options) || pages[0];
22846
23136
  }
22847
- function makeClient(wsUrl) {
22848
- const ws = new import_websocket.default(wsUrl, { origin: ORIGIN });
22849
- let id = 0;
22850
- const pending = /* @__PURE__ */ new Map();
22851
- const failAll = (msg) => {
22852
- for (const { rej } of pending.values()) {
22853
- try {
22854
- rej(new Error(msg));
22855
- } catch {
22856
- }
22857
- }
22858
- pending.clear();
22859
- };
22860
- const ready = new Promise((res, rej) => {
22861
- ws.on("open", res);
22862
- ws.once("error", rej);
22863
- });
22864
- ws.on("message", (data) => {
22865
- let m;
22866
- try {
22867
- m = JSON.parse(data.toString());
22868
- } catch {
22869
- return;
22870
- }
22871
- if (m.id && pending.has(m.id)) {
22872
- const { res, rej } = pending.get(m.id);
22873
- pending.delete(m.id);
22874
- if (m.error) rej(new Error(JSON.stringify(m.error)));
22875
- else res(m.result);
22876
- }
23137
+ function makeClient2(wsUrl, options = {}) {
23138
+ return makeClient(wsUrl, {
23139
+ origin: ORIGIN,
23140
+ connectTimeoutMs: Number(process.env.CURSOR_BRIDGE_CONNECT_TIMEOUT || 5e3),
23141
+ commandTimeoutMs: Number(process.env.CURSOR_BRIDGE_CMD_TIMEOUT || 3e4),
23142
+ ...options
22877
23143
  });
22878
- ws.on("close", () => failAll("CDP WebSocket closed because the page or renderer disappeared"));
22879
- ws.on("error", (e) => failAll("CDP WebSocket error: " + (e && e.message)));
22880
- const CMD_TIMEOUT = Number(process.env.CURSOR_BRIDGE_CMD_TIMEOUT || 3e4);
22881
- const send = (method, params = {}) => {
22882
- const myId = ++id;
22883
- return new Promise((res, rej) => {
22884
- const t = setTimeout(() => {
22885
- if (pending.delete(myId)) rej(new Error(`CDP command timed out: ${method} (${CMD_TIMEOUT}ms)`));
22886
- }, CMD_TIMEOUT);
22887
- pending.set(myId, { res: (v) => {
22888
- clearTimeout(t);
22889
- res(v);
22890
- }, rej: (e) => {
22891
- clearTimeout(t);
22892
- rej(e);
22893
- } });
22894
- try {
22895
- ws.send(JSON.stringify({ id: myId, method, params }));
22896
- } catch (e) {
22897
- clearTimeout(t);
22898
- pending.delete(myId);
22899
- rej(e);
22900
- }
22901
- });
22902
- };
22903
- return { ready, send, close: () => {
22904
- try {
22905
- ws.close();
22906
- } catch {
22907
- }
22908
- } };
22909
23144
  }
22910
23145
  var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
22911
- async function evalJS(c, expr) {
22912
- const r = await c.send("Runtime.evaluate", { expression: expr, returnByValue: true, includeCommandLineAPI: true, awaitPromise: true });
22913
- if (r.exceptionDetails) throw new Error("Page exception: " + (r.exceptionDetails.exception && r.exceptionDetails.exception.description || r.exceptionDetails.text));
23146
+ async function evalJS(c, expr, options) {
23147
+ const r = await c.send("Runtime.evaluate", { expression: expr, returnByValue: true, includeCommandLineAPI: true, awaitPromise: true }, options);
23148
+ if (r.exceptionDetails) {
23149
+ const error2 = new Error("Page exception: " + (r.exceptionDetails.exception && r.exceptionDetails.exception.description || r.exceptionDetails.text));
23150
+ error2.code = "CDP_EVALUATE_FAILED";
23151
+ throw error2;
23152
+ }
22914
23153
  return r.result && r.result.value;
22915
23154
  }
22916
23155
  async function chord(c, modifiers, key, code, vk) {
@@ -23188,6 +23427,7 @@ var EXPR_MODEL_PICKER_ROWS = `(function(){
23188
23427
  kind,
23189
23428
  selected:row.getAttribute('data-selected')==='true'||row.getAttribute('aria-checked')==='true'||!!row.querySelector('.ui-model-picker__item-check,.ui-model-picker__param-check'),
23190
23429
  disabled:row.getAttribute('data-disabled')==='true'||row.getAttribute('aria-disabled')==='true',
23430
+ pointerEvents:getComputedStyle(row).pointerEvents,
23191
23431
  hasSubmenu:row.getAttribute('aria-haspopup')==='menu',
23192
23432
  submenu:!!row.closest('[data-submenu]'),
23193
23433
  x:Math.round(rect.x+rect.width/2),
@@ -24029,7 +24269,7 @@ var CursorBridge = class {
24029
24269
  throw cursorSessionError("SESSION_NOT_READY", `state=${session.state}; recovery=${session.recoveryState || "none"}`);
24030
24270
  }
24031
24271
  if (session.lastTask?.status === "completed" && !session.lastTask.resultCollectedAt && this.tasks.has(session.lastTask.taskId)) {
24032
- throw cursorSessionError("SESSION_RESULT_UNCOLLECTED", `read cursor_status(task_id=${session.lastTask.taskId}) before continuing`);
24272
+ throw cursorSessionError("SESSION_RESULT_UNCOLLECTED", `read cursor_status(task_id=${session.lastTask.taskId}, detail="result") before continuing`);
24033
24273
  }
24034
24274
  if (session.lastTask?.status === "completed" && !session.lastTask.resultCollectedAt && !this.tasks.has(session.lastTask.taskId) && session.recoveryState !== "reconciled_result_uncollected") {
24035
24275
  throw cursorSessionError("SESSION_RECONCILE_REQUIRED", "the prior reply was not read before this adapter restarted; reconcile the exact Agent before continuing");
@@ -24406,7 +24646,7 @@ var CursorBridge = class {
24406
24646
  return null;
24407
24647
  }
24408
24648
  if (!page || page.capabilities && page.capabilities.uiFlavor !== "agents_v2") return null;
24409
- const c = makeClient(page.webSocketDebuggerUrl);
24649
+ const c = makeClient2(page.webSocketDebuggerUrl);
24410
24650
  try {
24411
24651
  await c.ready;
24412
24652
  let repository = JSON.parse(await evalJS(c, exprInspectWorkspaceRepository(projectPath)) || "{}");
@@ -24518,14 +24758,15 @@ var CursorBridge = class {
24518
24758
  lastPresentation: this._lastPresentation
24519
24759
  };
24520
24760
  }
24521
- async applyRuntimePresentation(action) {
24761
+ async applyRuntimePresentation(action, { scope = "process" } = {}) {
24522
24762
  const normalizedAction = String(action || "").trim().toLowerCase();
24523
24763
  if (!["hide", "show"].includes(normalizedAction)) {
24524
24764
  throw new Error("cursor_runtime action supports hide or show");
24525
24765
  }
24526
24766
  const result = setCursorWindowPresentation({
24527
24767
  action: normalizedAction,
24528
- port: CDP_PORT2
24768
+ port: CDP_PORT2,
24769
+ scope
24529
24770
  });
24530
24771
  this._lastPresentation = { ...result, at: (/* @__PURE__ */ new Date()).toISOString() };
24531
24772
  return this._lastPresentation;
@@ -24540,7 +24781,7 @@ var CursorBridge = class {
24540
24781
  })) {
24541
24782
  return null;
24542
24783
  }
24543
- const presentation = await this.applyRuntimePresentation("show");
24784
+ const presentation = await this.applyRuntimePresentation("show", { scope: "agents" });
24544
24785
  if (lifecycle && typeof lifecycle === "object") lifecycle.presentation = presentation;
24545
24786
  return presentation;
24546
24787
  }
@@ -24688,7 +24929,7 @@ var CursorBridge = class {
24688
24929
  modelPreference = session.modelPreference || null;
24689
24930
  if (duplicate) {
24690
24931
  const existing = this.tasks.get(session.activeTaskId || session.lastTask && session.lastTask.taskId || "");
24691
- return existing ? { duplicate: true, ...this._taskView(existing, true) } : { duplicate: true, ...this._sessionView(session) };
24932
+ return existing ? { duplicate: true, ...options.background === false ? this._collectedTaskView(existing) : this._compactTaskView(existing) } : { duplicate: true, ...this._sessionView(session) };
24692
24933
  }
24693
24934
  }
24694
24935
  const job = this._enqueue("do", fullPrompt, {
@@ -24712,9 +24953,9 @@ var CursorBridge = class {
24712
24953
  agentId: sessionMode === "continue" ? session && session.agentId : null,
24713
24954
  agentLabel: sessionMode === "continue" ? session && session.agentLabel : null
24714
24955
  });
24715
- if (options.background !== false) return this._taskView(job);
24956
+ if (options.background !== false) return this._compactTaskView(job);
24716
24957
  await job.promise;
24717
- return this._taskView(job, true);
24958
+ return this._collectedTaskView(job);
24718
24959
  }
24719
24960
  _assertNoParallelPathConflict(allowedPaths) {
24720
24961
  if (this._hasGlobalReservation()) {
@@ -25115,7 +25356,7 @@ var CursorBridge = class {
25115
25356
  for (let attempt = 0; attempt < 2; attempt++) {
25116
25357
  options.targetId = page.id;
25117
25358
  options.targetUiFlavor = page.capabilities && page.capabilities.uiFlavor || options.targetUiFlavor || null;
25118
- const c = makeClient(page.webSocketDebuggerUrl);
25359
+ const c = makeClient2(page.webSocketDebuggerUrl);
25119
25360
  await c.ready;
25120
25361
  try {
25121
25362
  this._throwIfCancelledBeforeSend(options);
@@ -25195,20 +25436,29 @@ var CursorBridge = class {
25195
25436
  error2.preSend = true;
25196
25437
  throw error2;
25197
25438
  }
25198
- async _readModelPickerTrigger(c) {
25439
+ async _readModelPickerValue(c, expression, kind, { timeoutMs = 3e3 } = {}) {
25440
+ const startedAt = Date.now();
25199
25441
  try {
25200
- return JSON.parse(await evalJS(c, EXPR_MODEL_PICKER_TRIGGER) || "{}");
25201
- } catch {
25202
- return { found: false, state: "trigger_unreadable" };
25442
+ const value = JSON.parse(await evalJS(c, expression, { timeoutMs }));
25443
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
25444
+ const error2 = new Error("Cursor picker returned an invalid snapshot");
25445
+ error2.code = "CURSOR_PICKER_RESPONSE_INVALID";
25446
+ throw error2;
25447
+ }
25448
+ return value;
25449
+ } catch (cause) {
25450
+ const error2 = cause instanceof Error ? cause : new Error(String(cause));
25451
+ if (error2 instanceof SyntaxError) error2.code = "CURSOR_PICKER_PARSE_FAILED";
25452
+ error2.pickerRead = { kind, elapsedMs: Date.now() - startedAt, code: error2.code || "CDP_READ_FAILED", message: error2.message };
25453
+ throw error2;
25203
25454
  }
25204
25455
  }
25205
- async _readModelPickerRows(c) {
25206
- try {
25207
- const snapshot = JSON.parse(await evalJS(c, EXPR_MODEL_PICKER_ROWS) || "{}");
25208
- return { open: snapshot.open === true, rows: Array.isArray(snapshot.rows) ? snapshot.rows : [] };
25209
- } catch {
25210
- return { open: false, rows: [] };
25211
- }
25456
+ async _readModelPickerTrigger(c, options) {
25457
+ return this._readModelPickerValue(c, EXPR_MODEL_PICKER_TRIGGER, "trigger", options);
25458
+ }
25459
+ async _readModelPickerRows(c, options) {
25460
+ const snapshot = await this._readModelPickerValue(c, EXPR_MODEL_PICKER_ROWS, "rows", options);
25461
+ return { open: snapshot.open === true, rows: Array.isArray(snapshot.rows) ? snapshot.rows : [] };
25212
25462
  }
25213
25463
  async _clickModelPickerPoint(c, point) {
25214
25464
  if (!point || !Number.isFinite(Number(point.x)) || !Number.isFinite(Number(point.y))) {
@@ -25289,7 +25539,7 @@ var CursorBridge = class {
25289
25539
  let last = this._inspectModelPickerRows({ open: false, rows: [] }, requested, kind);
25290
25540
  do {
25291
25541
  this._throwIfCancelledBeforeSend(job);
25292
- last = this._inspectModelPickerRows(await this._readModelPickerRows(c), requested, kind);
25542
+ last = this._inspectModelPickerRows(await this._readModelPickerRows(c, { timeoutMs: Math.max(1, deadline - Date.now()) }), requested, kind);
25293
25543
  if (last.row) return { ...last, state: "matched" };
25294
25544
  const signature = JSON.stringify(last.available);
25295
25545
  if (modelPickerAvailableIsDecisive(kind, last.available)) {
@@ -25310,17 +25560,41 @@ var CursorBridge = class {
25310
25560
  }
25311
25561
  async _selectedEffortRow(c, modelRow, effort, job) {
25312
25562
  const requested = cursorEffortUiValue(effort);
25313
- const snapshot = await this._readModelPickerRows(c);
25563
+ let snapshot = await this._readModelPickerRows(c);
25314
25564
  const immediate = this._inspectModelPickerRows(snapshot, requested, "parameter");
25315
25565
  if (immediate.row) return { ...immediate, state: "matched", attempts: ["visible"] };
25316
25566
  const attempts = [];
25317
- const effortControl = (snapshot.rows || []).find((row) => row.kind === "effort_control" && row.disabled !== true);
25567
+ let effortControl = (snapshot.rows || []).find((row) => row.kind === "effort_control" && row.disabled !== true);
25318
25568
  if (effortControl) {
25319
25569
  this._throwIfCancelledBeforeSend(job);
25570
+ const modelSubmenuOpen = () => snapshot.rows.some((row) => row.kind === "model" && row.submenu === true);
25571
+ if (modelSubmenuOpen()) {
25572
+ attempts.push("close_model_submenu");
25573
+ for (const type of ["keyDown", "keyUp"]) {
25574
+ await c.send("Input.dispatchKeyEvent", { type, key: "Escape", code: "Escape", windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });
25575
+ }
25576
+ const deadline = Date.now() + 700;
25577
+ do {
25578
+ this._throwIfCancelledBeforeSend(job);
25579
+ snapshot = await this._readModelPickerRows(c, { timeoutMs: Math.max(1, deadline - Date.now()) });
25580
+ if (!snapshot.open) break;
25581
+ effortControl = snapshot.rows.find((row) => row.kind === "effort_control" && row.disabled !== true && row.pointerEvents !== "none");
25582
+ if (effortControl && !modelSubmenuOpen()) {
25583
+ attempts.push("fresh_effort_control");
25584
+ break;
25585
+ }
25586
+ if (Date.now() >= deadline) break;
25587
+ await sleep2(Math.min(50, deadline - Date.now()));
25588
+ } while (Date.now() <= deadline);
25589
+ }
25590
+ if (!snapshot.open || !effortControl || effortControl.pointerEvents === "none" || modelSubmenuOpen()) {
25591
+ return { ...this._inspectModelPickerRows(snapshot, requested, "parameter"), state: "not_rendered", attempts };
25592
+ }
25320
25593
  attempts.push("effort_control");
25594
+ this._throwIfCancelledBeforeSend(job);
25321
25595
  await this._clickModelPickerPoint(c, effortControl);
25322
25596
  const result2 = await this._waitForModelPickerMatch(c, requested, "parameter", job, { timeoutMs: 700 });
25323
- if (result2.state !== "not_rendered") return { ...result2, attempts };
25597
+ return { ...result2, attempts };
25324
25598
  }
25325
25599
  this._throwIfCancelledBeforeSend(job);
25326
25600
  attempts.push("model_hover");
@@ -25339,7 +25613,7 @@ var CursorBridge = class {
25339
25613
  const deadline = Date.now() + timeoutMs;
25340
25614
  do {
25341
25615
  this._throwIfCancelledBeforeSend(job);
25342
- const inspected = this._inspectModelPickerRows(await this._readModelPickerRows(c), requested, kind);
25616
+ const inspected = this._inspectModelPickerRows(await this._readModelPickerRows(c, { timeoutMs: Math.max(1, deadline - Date.now()) }), requested, kind);
25343
25617
  if (inspected.row && inspected.row.selected) return inspected.row;
25344
25618
  if (Date.now() >= deadline) break;
25345
25619
  await sleep2(Math.min(100, Math.max(0, deadline - Date.now())));
@@ -25356,8 +25630,11 @@ var CursorBridge = class {
25356
25630
  let modelRow = null;
25357
25631
  let effectiveEffort = null;
25358
25632
  let primaryError = null;
25633
+ const selectionStartedAt = Date.now();
25634
+ let stage = "open_picker";
25359
25635
  try {
25360
25636
  const opened = await this._openModelPicker(c);
25637
+ stage = "locate_model";
25361
25638
  let located = await this._findModelPickerModel(c, opened, requestedModel);
25362
25639
  modelRow = located.modelRow;
25363
25640
  if (!modelRow) {
@@ -25396,10 +25673,15 @@ var CursorBridge = class {
25396
25673
  message,
25397
25674
  failureClass,
25398
25675
  failureClass === "effort_menu_not_rendered",
25399
- { available: outcome.available || [], attempts: outcome.attempts || [] }
25676
+ {
25677
+ available: outcome.available || [],
25678
+ attempts: outcome.attempts || [],
25679
+ menu: outcome.snapshot ? { open: outcome.snapshot.open, rows: (outcome.snapshot.rows || []).slice(0, 20).map(({ text, kind, selected, disabled, pointerEvents, submenu }) => ({ text, kind, selected, disabled, pointerEvents, submenu })) } : null
25680
+ }
25400
25681
  );
25401
25682
  };
25402
25683
  if (requestedEffort && modelRow.hasSubmenu) {
25684
+ stage = "select_effort";
25403
25685
  const selectedEffort = await resolveEffort();
25404
25686
  if (!selectedEffort.row) throwEffortFailure(selectedEffort);
25405
25687
  if (!selectedEffort.row.selected || !modelRow.selected) {
@@ -25408,10 +25690,12 @@ var CursorBridge = class {
25408
25690
  await sleep2(550);
25409
25691
  }
25410
25692
  } else if (!modelRow.selected) {
25693
+ stage = "select_model";
25411
25694
  this._throwIfCancelledBeforeSend(job);
25412
25695
  await this._clickModelPickerPoint(c, modelRow);
25413
25696
  await sleep2(550);
25414
25697
  }
25698
+ stage = "verify_model";
25415
25699
  let trigger2 = await this._readModelPickerTrigger(c);
25416
25700
  if (!trigger2.found || !normalizeModelPickerText(trigger2.text).includes(normalizeModelPickerText(requestedModel))) {
25417
25701
  const reopened = await this._openModelPicker(c);
@@ -25427,6 +25711,7 @@ var CursorBridge = class {
25427
25711
  modelRow = selected;
25428
25712
  }
25429
25713
  if (requestedEffort) {
25714
+ stage = "verify_effort";
25430
25715
  const reopened = await this._openModelPicker(c);
25431
25716
  located = await this._findModelPickerModel(c, reopened, requestedModel);
25432
25717
  modelRow = located.modelRow;
@@ -25463,13 +25748,19 @@ var CursorBridge = class {
25463
25748
  if (!failure) {
25464
25749
  const pickerUnavailable = /model picker (?:is unavailable|did not open)/i.test(primaryError.message);
25465
25750
  failure = {
25466
- failureClass: pickerUnavailable ? "picker_did_not_open" : "probe_error",
25751
+ failureClass: primaryError.pickerRead ? "picker_read_failed" : pickerUnavailable ? "picker_did_not_open" : "probe_error",
25467
25752
  retryable: true
25468
25753
  };
25469
25754
  }
25470
25755
  const diagnostic = {
25471
25756
  configured: true,
25472
25757
  applied: false,
25758
+ stage,
25759
+ elapsedMs: Date.now() - selectionStartedAt,
25760
+ taskId: job?.id || null,
25761
+ targetId: job?.targetId || null,
25762
+ pickerRead: primaryError.pickerRead || null,
25763
+ cdp: primaryError.cdp || null,
25473
25764
  requestedModel,
25474
25765
  requestedEffort,
25475
25766
  failureClass: failure.failureClass,
@@ -25477,6 +25768,7 @@ var CursorBridge = class {
25477
25768
  errorCode: primaryError.code || `CURSOR_MODEL_${String(failure.failureClass).toUpperCase()}`,
25478
25769
  available: failure.available || [],
25479
25770
  attempts: failure.attempts || [],
25771
+ menu: failure.menu || null,
25480
25772
  runtimeMode: this.runtimeMode,
25481
25773
  lastError: primaryError.message,
25482
25774
  failedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -25513,7 +25805,29 @@ var CursorBridge = class {
25513
25805
  if (job && job.modelSelection) job.modelSelection.cleanupError = message;
25514
25806
  }
25515
25807
  }
25516
- const trigger = await this._readModelPickerTrigger(c);
25808
+ let trigger;
25809
+ try {
25810
+ trigger = await this._readModelPickerTrigger(c);
25811
+ } catch (error2) {
25812
+ error2.modelSelection = {
25813
+ configured: true,
25814
+ applied: false,
25815
+ requestedModel,
25816
+ requestedEffort,
25817
+ taskId: job?.id || null,
25818
+ targetId: job?.targetId || null,
25819
+ stage: "final_trigger",
25820
+ failureClass: "picker_read_failed",
25821
+ retryable: true,
25822
+ errorCode: error2.code || "CDP_READ_FAILED",
25823
+ pickerRead: error2.pickerRead || null,
25824
+ cdp: error2.cdp || null,
25825
+ elapsedMs: Date.now() - selectionStartedAt,
25826
+ lastError: error2.message
25827
+ };
25828
+ if (job) job.modelSelection = error2.modelSelection;
25829
+ throw error2;
25830
+ }
25517
25831
  const result = {
25518
25832
  configured: true,
25519
25833
  applied: true,
@@ -25810,7 +26124,7 @@ var CursorBridge = class {
25810
26124
  });
25811
26125
  job.targetId = page.id;
25812
26126
  job.targetUiFlavor = page.capabilities && page.capabilities.uiFlavor || null;
25813
- const c = makeClient(page.webSocketDebuggerUrl);
26127
+ const c = makeClient2(page.webSocketDebuggerUrl);
25814
26128
  await c.ready;
25815
26129
  let sent = false;
25816
26130
  try {
@@ -25945,7 +26259,7 @@ var CursorBridge = class {
25945
26259
  });
25946
26260
  job.targetId = page.id;
25947
26261
  job.targetUiFlavor = page.capabilities && page.capabilities.uiFlavor || null;
25948
- const c = makeClient(page.webSocketDebuggerUrl);
26262
+ const c = makeClient2(page.webSocketDebuggerUrl);
25949
26263
  await c.ready;
25950
26264
  let sent = false;
25951
26265
  try {
@@ -26001,7 +26315,7 @@ var CursorBridge = class {
26001
26315
  async _readParallelEntry(job) {
26002
26316
  return this._withUiLock(async () => {
26003
26317
  const page = await findPage({ targetId: job.targetId, purpose: "parallel_agent" });
26004
- const c = makeClient(page.webSocketDebuggerUrl);
26318
+ const c = makeClient2(page.webSocketDebuggerUrl);
26005
26319
  await c.ready;
26006
26320
  try {
26007
26321
  let entries = await this._readAgentEntries(c);
@@ -26206,7 +26520,7 @@ var CursorBridge = class {
26206
26520
  }
26207
26521
  async _collectParallelAgent(job) {
26208
26522
  const page = await findPage({ targetId: job.targetId, purpose: "parallel_agent" });
26209
- const c = makeClient(page.webSocketDebuggerUrl);
26523
+ const c = makeClient2(page.webSocketDebuggerUrl);
26210
26524
  await c.ready;
26211
26525
  try {
26212
26526
  return await this._withRestoredAgentSelection(c, job, async () => {
@@ -26530,7 +26844,7 @@ var CursorBridge = class {
26530
26844
  if (!job.agentId) return { confirmed: false, state: "unbound_agent" };
26531
26845
  return this._withUiLock(async () => {
26532
26846
  const page = await findPage({ targetId: job.targetId, purpose: "parallel_agent" });
26533
- const c = makeClient(page.webSocketDebuggerUrl);
26847
+ const c = makeClient2(page.webSocketDebuggerUrl);
26534
26848
  await c.ready;
26535
26849
  try {
26536
26850
  return await this._stopBoundAgentOnClient(c, job, { restorePrevious: true });
@@ -26560,11 +26874,13 @@ var CursorBridge = class {
26560
26874
  job.cancelReason = reason || (job.execution === "parallel_agent" ? "User requested Cursor Agent cancellation" : "User requested FIFO task cancellation");
26561
26875
  job.cancelRequestSeq = Number(job.cancelRequestSeq || 0) + 1;
26562
26876
  }
26563
- return this._withJobLock(job, () => this._taskControlLocked(job, action, {
26877
+ const result = await this._withJobLock(job, () => this._taskControlLocked(job, action, {
26564
26878
  ...options,
26565
26879
  reason,
26566
26880
  expectedAgentId
26567
26881
  }));
26882
+ if (result && result.task) result.task = this._compactTaskView(job);
26883
+ return result;
26568
26884
  }
26569
26885
  async _taskControlLocked(job, action, options) {
26570
26886
  if (action === "reap") {
@@ -26722,7 +27038,7 @@ var CursorBridge = class {
26722
27038
  this.parallelRestoreTargetId = null;
26723
27039
  this._withUiLock(async () => {
26724
27040
  const page = await findPage({ targetId, purpose: "parallel_agent" });
26725
- const c = makeClient(page.webSocketDebuggerUrl);
27041
+ const c = makeClient2(page.webSocketDebuggerUrl);
26726
27042
  await c.ready;
26727
27043
  try {
26728
27044
  if (await this._ensureHistoryOpen(c)) {
@@ -26807,7 +27123,6 @@ var CursorBridge = class {
26807
27123
  throw new Error(`Cursor task timed out (${timeoutMs}ms) before generation was confirmed stopped with a complete assistant reply${taskHint}`);
26808
27124
  }
26809
27125
  _taskView(job, includeResult = false) {
26810
- if (includeResult) this._markTaskResultCollected(job);
26811
27126
  const view = {
26812
27127
  taskId: job.id,
26813
27128
  requestedTimeoutMs: job.requestedTimeoutMs ?? job.timeoutMs,
@@ -26874,6 +27189,165 @@ var CursorBridge = class {
26874
27189
  }
26875
27190
  return view;
26876
27191
  }
27192
+ _collectedTaskView(job) {
27193
+ this._markTaskResultCollected(job);
27194
+ return this._taskView(job, true);
27195
+ }
27196
+ _compactTaskView(job, { summary = false } = {}) {
27197
+ const full = this._taskView(job);
27198
+ const selection = full.modelSelection && {
27199
+ configured: full.modelSelection.configured,
27200
+ applied: full.modelSelection.applied,
27201
+ model: full.modelSelection.model,
27202
+ effort: full.modelSelection.effort,
27203
+ requestedModel: full.modelSelection.requestedModel ?? full.modelSelection.model ?? full.modelPreference?.model,
27204
+ requestedEffort: full.modelSelection.requestedEffort ?? full.modelSelection.effort ?? full.modelPreference?.effort,
27205
+ effectiveModel: full.modelSelection.effectiveModel,
27206
+ effectiveEffort: full.modelSelection.effectiveEffort,
27207
+ failureClass: full.modelSelection.failureClass,
27208
+ stage: full.modelSelection.stage,
27209
+ elapsedMs: full.modelSelection.elapsedMs,
27210
+ pickerRead: full.modelSelection.pickerRead,
27211
+ cdp: full.modelSelection.cdp,
27212
+ available: full.modelSelection.available,
27213
+ attempts: full.modelSelection.attempts,
27214
+ menu: full.modelSelection.menu,
27215
+ cleanupError: full.modelSelection.cleanupError,
27216
+ retryable: full.modelSelection.retryable,
27217
+ errorCode: full.modelSelection.errorCode,
27218
+ lastError: full.modelSelection.lastError,
27219
+ failedAt: full.modelSelection.failedAt,
27220
+ verifiedAt: full.modelSelection.verifiedAt
27221
+ };
27222
+ const view = {
27223
+ taskId: full.taskId,
27224
+ kind: full.kind,
27225
+ status: full.status,
27226
+ phase: full.phase,
27227
+ execution: full.execution,
27228
+ effectiveExecution: full.effectiveExecution,
27229
+ projectPath: full.projectPath,
27230
+ sessionId: full.sessionId,
27231
+ agentId: full.agentId,
27232
+ sendState: full.sendState,
27233
+ modelSelection: selection,
27234
+ reservationHeld: full.reservationHeld,
27235
+ reservationScope: full.reservationScope,
27236
+ blocksFifo: full.blocksFifo,
27237
+ blocksAll: full.blocksAll,
27238
+ recoveryState: full.recoveryState,
27239
+ attention: full.attention,
27240
+ cancelRequested: full.cancelRequested,
27241
+ cancelReason: full.cancelReason,
27242
+ underlyingStopConfirmed: full.underlyingStopConfirmed,
27243
+ lastRecoveryAt: full.lastRecoveryAt,
27244
+ terminalEvidence: full.terminalEvidence,
27245
+ resultAvailable: job.result != null,
27246
+ resultLength: job.result == null ? 0 : String(job.result).length,
27247
+ resultUnread: job.result != null && !full.resultCollectedAt,
27248
+ resultUnavailable: full.resultUnavailable,
27249
+ resultCollectedAt: full.resultCollectedAt,
27250
+ error: full.error
27251
+ };
27252
+ if (selection && Object.values(selection).every((value) => value === void 0)) view.modelSelection = null;
27253
+ if (full.sessionError) view.sessionError = full.sessionError;
27254
+ if (full.firstWaitError) view.firstWaitError = full.firstWaitError;
27255
+ if (full.providerError) view.providerError = full.providerError;
27256
+ if (full.uiDiagnostic) view.uiDiagnostic = full.uiDiagnostic;
27257
+ if (full.workspaceBinding && full.workspaceBinding.ok === false) view.workspaceBinding = full.workspaceBinding;
27258
+ if ((full.error || full.status === "needs_attention") && full.workspaceBindingChecks) {
27259
+ view.workspaceBindingChecks = full.workspaceBindingChecks;
27260
+ }
27261
+ if (full.status === "needs_attention" && full.lastWaitObservation) view.lastWaitObservation = full.lastWaitObservation;
27262
+ if (summary) {
27263
+ return {
27264
+ taskId: view.taskId,
27265
+ status: view.status,
27266
+ phase: view.phase,
27267
+ agentId: view.agentId,
27268
+ sessionId: view.sessionId,
27269
+ resultAvailable: view.resultAvailable,
27270
+ resultLength: view.resultLength,
27271
+ resultUnread: view.resultUnread,
27272
+ resultCollectedAt: view.resultCollectedAt,
27273
+ ...view.sendState ? { sendState: view.sendState } : {},
27274
+ ...view.reservationHeld ? { reservationHeld: true, reservationScope: view.reservationScope } : {},
27275
+ ...view.blocksFifo ? { blocksFifo: true } : {},
27276
+ ...view.blocksAll ? { blocksAll: true } : {},
27277
+ ...view.recoveryState ? { recoveryState: view.recoveryState } : {},
27278
+ ...view.attention ? { attention: view.attention } : {},
27279
+ ...view.cancelRequested ? { cancelRequested: true } : {},
27280
+ ...view.cancelReason ? { cancelReason: view.cancelReason } : {},
27281
+ ...view.underlyingStopConfirmed != null ? { underlyingStopConfirmed: view.underlyingStopConfirmed } : {},
27282
+ ...view.lastRecoveryAt ? { lastRecoveryAt: view.lastRecoveryAt } : {},
27283
+ ...view.terminalEvidence ? { terminalEvidence: view.terminalEvidence } : {},
27284
+ ...view.resultUnavailable ? { resultUnavailable: true } : {},
27285
+ ...view.error ? { error: view.error } : {},
27286
+ ...view.providerError ? { providerError: view.providerError } : {},
27287
+ ...view.uiDiagnostic ? { uiDiagnostic: view.uiDiagnostic } : {},
27288
+ ...view.workspaceBinding ? { workspaceBinding: view.workspaceBinding } : {},
27289
+ ...view.workspaceBindingChecks ? { workspaceBindingChecks: view.workspaceBindingChecks } : {}
27290
+ };
27291
+ }
27292
+ return {
27293
+ ...view,
27294
+ requestedTimeoutMs: full.requestedTimeoutMs,
27295
+ effectiveTimeoutMs: full.effectiveTimeoutMs,
27296
+ readOnly: full.readOnly,
27297
+ allowedPaths: full.allowedPaths,
27298
+ modelPreference: full.modelPreference,
27299
+ requestContext: full.requestContext,
27300
+ sessionMode: full.sessionMode,
27301
+ sessionTurn: full.sessionTurn,
27302
+ sessionState: full.sessionState,
27303
+ requestId: full.requestId,
27304
+ provisionalAgentId: full.provisionalAgentId,
27305
+ agentLabel: full.agentLabel,
27306
+ createdAt: full.createdAt,
27307
+ startedAt: full.startedAt,
27308
+ sentAt: full.sentAt,
27309
+ finishedAt: full.finishedAt
27310
+ };
27311
+ }
27312
+ _compactStatusCommon() {
27313
+ const workspace = this.workspaceView();
27314
+ const runtime = this.runtimeModeView();
27315
+ const models = this.modelPreferencesView();
27316
+ return {
27317
+ pluginVersion: PLUGIN_VERSION,
27318
+ statusPath: "json-list",
27319
+ workspaceKey: workspace.workspaceKey,
27320
+ workspaceConfirmationRequired: workspace.workspaceConfirmationRequired,
27321
+ workspaceBindingWarning: workspace.workspaceBindingWarning,
27322
+ projectPath: workspace.projectPath,
27323
+ initialized: workspace.initialized,
27324
+ ...workspace.workspaceBinding && workspace.workspaceBinding.ok === false ? { workspaceBinding: workspace.workspaceBinding } : {},
27325
+ ...this.delegationView(),
27326
+ runtimeMode: runtime.runtimeMode,
27327
+ minimalModeWarning: runtime.minimalModeWarning,
27328
+ modelPreferences: models.modelPreferences,
27329
+ modelPreferencesUpdatedAt: models.modelPreferencesUpdatedAt,
27330
+ sessionStoragePersistent: !!this.sessionFile
27331
+ };
27332
+ }
27333
+ _compactLifecycleView() {
27334
+ const lifecycle = this._lastLifecycle;
27335
+ if (!lifecycle) {
27336
+ return { adapterPid: process.pid, status: null, lifecycleMode: null, persistent: null, degradedReason: null };
27337
+ }
27338
+ if (lifecycle.degradedReason || lifecycle.spawnErrorCode || lifecycle.error || lifecycle.supervisorError || lifecycle.needsAction || lifecycle.nextStep || lifecycle.retryable || lifecycle.status === "failed") {
27339
+ return lifecycle;
27340
+ }
27341
+ return {
27342
+ adapterPid: lifecycle.adapterPid,
27343
+ supervisorPid: lifecycle.supervisorPid,
27344
+ status: lifecycle.status,
27345
+ lifecycleMode: lifecycle.lifecycleMode,
27346
+ persistent: lifecycle.persistent,
27347
+ degradedReason: lifecycle.degradedReason,
27348
+ cursorPid: lifecycle.cursorPid
27349
+ };
27350
+ }
26877
27351
  _assertWorkspaceConfirmed() {
26878
27352
  if (this.workspaceConfirmationRequired) {
26879
27353
  throw new Error("WORKSPACE_CONFIRMATION_REQUIRED: the saved default binding has no host workspace identity. Run cursor_init with the intended project before submitting work.");
@@ -26882,7 +27356,7 @@ var CursorBridge = class {
26882
27356
  _ensureTaskCapacity() {
26883
27357
  this._trimTasks(49);
26884
27358
  if (this.tasks.size >= 50) {
26885
- throw new Error("TASK_RETENTION_FULL: 50 tasks are active or have unread replies. Read unreadResultTaskIds with cursor_status(task_id), or wait for active tasks before submitting more work.");
27359
+ throw new Error('TASK_RETENTION_FULL: 50 tasks are active or have unread replies. Read each unreadResultTaskId with cursor_status(task_id, detail="result"), or wait for active tasks before submitting more work.');
26886
27360
  }
26887
27361
  }
26888
27362
  _trimTasks(limit = 50) {
@@ -26892,16 +27366,31 @@ var CursorBridge = class {
26892
27366
  if (this.tasks.size <= limit) break;
26893
27367
  }
26894
27368
  }
26895
- async status(taskId = "") {
27369
+ async status(taskId = "", { detail = "compact" } = {}) {
27370
+ const normalizedDetail = normalizeStatusDetail(detail);
27371
+ if (normalizedDetail === "result") {
27372
+ if (!taskId) throw new Error('RESULT_TASK_REQUIRED: detail="result" requires task_id');
27373
+ const job = this.tasks.get(String(taskId));
27374
+ if (!job) throw new Error(`RESULT_TASK_NOT_FOUND: ${taskId}`);
27375
+ if (!isTerminalTask(job) || job.result == null) {
27376
+ throw new Error(`RESULT_NOT_AVAILABLE: ${taskId}; inspect compact status before retrying`);
27377
+ }
27378
+ const text = String(job.result);
27379
+ this._markTaskResultCollected(job);
27380
+ return text;
27381
+ }
26896
27382
  if (taskId) {
26897
27383
  const job = this.tasks.get(String(taskId));
26898
- if (!job) return { found: false, taskId: String(taskId), ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this.sessionRegistryView() };
26899
- return { found: true, ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this.sessionRegistryView(), ...this._taskView(job, true) };
27384
+ if (normalizedDetail === "full") {
27385
+ if (!job) return { found: false, taskId: String(taskId), ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this.sessionRegistryView() };
27386
+ return { found: true, ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this.sessionRegistryView(), ...this._collectedTaskView(job) };
27387
+ }
27388
+ return { found: !!job, ...this._compactStatusCommon(), ...job ? this._compactTaskView(job) : { taskId: String(taskId) } };
26900
27389
  }
26901
27390
  const parallelRunning = this.activeParallel.size;
26902
27391
  const uiBusy = this.busy;
26903
27392
  const globalBlocked = this._hasGlobalReservation();
26904
- const common = {
27393
+ const common = normalizedDetail === "full" ? {
26905
27394
  pluginVersion: PLUGIN_VERSION,
26906
27395
  statusPath: "json-list",
26907
27396
  ...this.workspaceView(),
@@ -26939,6 +27428,21 @@ var CursorBridge = class {
26939
27428
  runtimeMode: this.runtimeMode,
26940
27429
  presentation: null
26941
27430
  }
27431
+ } : {
27432
+ ...this._compactStatusCommon(),
27433
+ busy: uiBusy || parallelRunning > 0 || this.queue.length > 0,
27434
+ uiBusy,
27435
+ parallelRunning,
27436
+ idle: !uiBusy && parallelRunning === 0 && this.queue.length === 0,
27437
+ queued: this.queue.length,
27438
+ blockingTaskIds: [...this.activeParallel.values()].filter((job) => !isTerminalTask(job)).map((job) => job.id),
27439
+ globallyBlocked: globalBlocked,
27440
+ blockedQueuedCount: this.activeParallel.size > 0 ? this.queue.filter((job) => globalBlocked || job.effectiveExecution !== "parallel_agent").length : 0,
27441
+ activeParallel: [...this.activeParallel.values()].map((job) => this._compactTaskView(job, { summary: true })),
27442
+ recentTasks: [...this.tasks.values()].filter((job) => !this.activeParallel.has(job.id)).slice(-10).map((job) => this._compactTaskView(job, { summary: true })),
27443
+ unreadResultTaskIds: [...this.tasks.values()].filter((job) => isTerminalTask(job) && job.result != null && !job.resultCollectedAt).map((job) => job.id),
27444
+ taskRetentionLimit: 50,
27445
+ lifecycle: this._compactLifecycleView()
26942
27446
  };
26943
27447
  try {
26944
27448
  const ver = await httpJson("/json/version");
@@ -26959,6 +27463,13 @@ function buildSearchInputSchema() {
26959
27463
  required: ["query"]
26960
27464
  };
26961
27465
  }
27466
+ function normalizeStatusDetail(detail) {
27467
+ if (detail === void 0) return "compact";
27468
+ if (typeof detail !== "string" || !["compact", "full", "result"].includes(detail)) {
27469
+ throw new Error("detail must be compact, full or result");
27470
+ }
27471
+ return detail;
27472
+ }
26962
27473
  var REQUEST_CONTEXT_SCHEMA = {
26963
27474
  type: "object",
26964
27475
  additionalProperties: false,
@@ -26988,7 +27499,7 @@ function buildToolDefinitions(bridgeInstance) {
26988
27499
  },
26989
27500
  bridgeInstance.environmentDelegationMode !== "off" ? {
26990
27501
  name: "cursor_do",
26991
- description: "Give Cursor a clearly bounded task and get back a task ID. fifo means first in, first out: Bridge runs one queued task at a time, starting it in a clean chat. parallel_agent creates a separate top-level Cursor Agent. Persistent continuity is explicit: session_mode=create starts one durable top-level Agent association, and session_mode=continue requires its exact session_id. Omission keeps the existing isolated behavior. Parallel write tasks must declare non-overlapping allowed_paths; mark read-only work with read_only=true. Collect the result with cursor_status(task_id). Cursor can do the work, but the main agent still owns review and final verification. A direct user opt-out always wins.",
27502
+ description: 'Give Cursor a clearly bounded task and get back a task ID. fifo means first in, first out: Bridge runs one queued task at a time, starting it in a clean chat. parallel_agent creates a separate top-level Cursor Agent. Persistent continuity is explicit: session_mode=create starts one durable top-level Agent association, and session_mode=continue requires its exact session_id. Omission keeps the existing isolated behavior. Parallel write tasks must declare non-overlapping allowed_paths; mark read-only work with read_only=true. Collect the result with cursor_status(task_id, detail="result"); ordinary status calls are compact and do not acknowledge the reply. Cursor can do the work, but the main agent still owns review and final verification. A direct user opt-out always wins.',
26992
27503
  inputSchema: {
26993
27504
  type: "object",
26994
27505
  properties: {
@@ -27009,7 +27520,7 @@ function buildToolDefinitions(bridgeInstance) {
27009
27520
  } : null,
27010
27521
  {
27011
27522
  name: "cursor_task_control",
27012
- description: "Recover or terminate one exact in-memory Cursor task without resubmitting it; task records do not survive this MCP server process. Use reap for needs_attention/orphaned work only when it has a bound agentId; it explicitly rechecks that Agent and collects a stable terminal result when possible. Use cancel with confirm=true and the exact expected_agent_id to target Stop safely. FIFO or unbound orphans globally block delegation and require manual verification before abandon. Use abandon only with an explicit reason and acknowledge_may_still_write=true; it releases reservations without proving the Cursor Agent stopped.",
27523
+ description: 'Recover or terminate one exact in-memory Cursor task without resubmitting it; task records do not survive this MCP server process. Use reap for needs_attention/orphaned work only when it has a bound agentId; it explicitly rechecks that Agent and stores a stable terminal result when possible. Responses stay compact and never acknowledge the result; collect it later with cursor_status(task_id, detail="result"). Use cancel with confirm=true and the exact expected_agent_id to target Stop safely. FIFO or unbound orphans globally block delegation and require manual verification before abandon. Use abandon only with an explicit reason and acknowledge_may_still_write=true; it releases reservations without proving the Cursor Agent stopped.',
27013
27524
  inputSchema: {
27014
27525
  type: "object",
27015
27526
  properties: {
@@ -27065,12 +27576,14 @@ function buildToolDefinitions(bridgeInstance) {
27065
27576
  },
27066
27577
  {
27067
27578
  name: "cursor_status",
27068
- description: "Read-only snapshot of Cursor connectivity, queued/running work, reservations, execution availability, persistent model/effort defaults, sessions, and normal/minimal runtime presentation. Pass task_id for its configured and effective model selection, or session_id for the durable association; never pass both. This tool never switches Agents, reconciles, or stops work.",
27579
+ description: 'Read-only snapshot of Cursor connectivity, queued/running work, reservations, execution availability, persistent model/effort defaults, sessions, and normal/minimal runtime presentation. Compact is the default and never includes or acknowledges a task result. Use detail="result" with task_id for only the complete reply as plain text, or detail="full" for JSON diagnostics and the reply; both record receipt and allow repeat reads while retained. Pass task_id for its configured and effective model selection or session_id for the durable association; never pass both. This tool never switches Agents, reconciles, or stops work.',
27069
27580
  inputSchema: {
27070
27581
  type: "object",
27582
+ additionalProperties: false,
27071
27583
  properties: {
27072
27584
  task_id: { type: "string", description: "A task ID returned by cursor_do." },
27073
- session_id: { type: "string", description: "A persistent session ID returned by cursor_do(session_mode=create)." }
27585
+ session_id: { type: "string", description: "A persistent session ID returned by cursor_do(session_mode=create)." },
27586
+ detail: { type: "string", enum: ["compact", "full", "result"], default: "compact", description: "compact returns status without collecting the reply. result requires task_id and returns only its complete reply as plain text, recording receipt. full returns complete JSON details and the reply. Explicit result/full reads are repeatable while retained." }
27074
27587
  }
27075
27588
  }
27076
27589
  }
@@ -27099,10 +27612,13 @@ async function ensureBridgeCursor(targetBridge, reason) {
27099
27612
  }
27100
27613
  function toolErrorResult(error2) {
27101
27614
  const message = error2 instanceof Error ? error2.message : String(error2);
27102
- if (error2 && error2.uiDiagnostic) {
27615
+ if (error2 && (error2.uiDiagnostic || error2.modelSelection || error2.pickerRead || error2.cdp)) {
27103
27616
  const payload = {
27104
- error: { code: error2.code || error2.uiDiagnostic.code || "CURSOR_BRIDGE_ERROR", message },
27105
- uiDiagnostic: error2.uiDiagnostic
27617
+ error: { code: error2.code || error2.uiDiagnostic?.code || "CURSOR_BRIDGE_ERROR", message },
27618
+ ...error2.uiDiagnostic ? { uiDiagnostic: error2.uiDiagnostic } : {},
27619
+ ...error2.modelSelection ? { modelSelection: error2.modelSelection } : {},
27620
+ ...error2.pickerRead ? { pickerRead: error2.pickerRead } : {},
27621
+ ...error2.cdp ? { cdp: error2.cdp } : {}
27106
27622
  };
27107
27623
  return {
27108
27624
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
@@ -27187,15 +27703,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
27187
27703
  if (args && args.task_id && args.session_id) {
27188
27704
  throw cursorSessionError("STATUS_SELECTOR_AMBIGUOUS", "pass task_id or session_id, not both");
27189
27705
  }
27706
+ const detail = normalizeStatusDetail(args && args.detail);
27707
+ if (detail === "result") {
27708
+ if (!args.task_id || args.session_id) throw new Error('RESULT_TASK_REQUIRED: detail="result" requires task_id only');
27709
+ return { content: [{ type: "text", text: await bridge.status(args.task_id, { detail }) }] };
27710
+ }
27190
27711
  const statusMs = Math.max(1e3, Number(process.env.CURSOR_BRIDGE_STATUS_TIMEOUT || 8e3));
27191
27712
  let result;
27192
27713
  try {
27193
27714
  result = await Promise.race([
27194
- args && args.session_id ? Promise.resolve(bridge.sessionStatus(args.session_id)) : bridge.status(args && args.task_id),
27715
+ args && args.session_id ? Promise.resolve(bridge.sessionStatus(args.session_id)) : bridge.status(args && args.task_id, { detail }),
27195
27716
  new Promise((_, reject) => setTimeout(() => reject(new Error(`cursor_status_timeout_${statusMs}`)), statusMs))
27196
27717
  ]);
27197
27718
  } catch (error2) {
27198
- result = {
27719
+ result = detail === "full" ? {
27199
27720
  connected: false,
27200
27721
  pluginVersion: PLUGIN_VERSION,
27201
27722
  statusPath: "json-list",
@@ -27203,7 +27724,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
27203
27724
  ...bridge.workspaceView(),
27204
27725
  ...bridge.delegationView(),
27205
27726
  ...bridge.runtimeModeView(),
27727
+ ...bridge.modelPreferencesView(),
27206
27728
  ...bridge.sessionRegistryView()
27729
+ } : {
27730
+ connected: false,
27731
+ ...bridge._compactStatusCommon(),
27732
+ ...args && args.task_id ? { found: false, taskId: String(args.task_id) } : {},
27733
+ ...args && args.session_id ? { found: false, sessionId: String(args.session_id) } : {},
27734
+ error: error2 instanceof Error ? error2.message : String(error2)
27207
27735
  };
27208
27736
  }
27209
27737
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };