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.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/cursor-bridge.mjs +663 -135
- package/dist/cursor-lifecycle-supervisor.mjs +41 -11
- package/extensions/index.ts +1 -1
- package/package.json +2 -2
- package/skills/cursor-delegate/SKILL.md +13 -12
- package/skills/cursor-delegate/references/delegation-contract.md +15 -7
package/dist/cursor-bridge.mjs
CHANGED
|
@@ -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 (
|
|
11003
|
-
|
|
11004
|
-
|
|
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
|
-
|
|
11007
|
-
|
|
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 {
|
|
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
|
-
|
|
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 = "
|
|
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
|
|
22793
|
-
|
|
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
|
|
22848
|
-
|
|
22849
|
-
|
|
22850
|
-
|
|
22851
|
-
|
|
22852
|
-
|
|
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)
|
|
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 =
|
|
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.
|
|
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.
|
|
24956
|
+
if (options.background !== false) return this._compactTaskView(job);
|
|
24716
24957
|
await job.promise;
|
|
24717
|
-
return this.
|
|
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 =
|
|
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
|
|
25439
|
+
async _readModelPickerValue(c, expression, kind, { timeoutMs = 3e3 } = {}) {
|
|
25440
|
+
const startedAt = Date.now();
|
|
25199
25441
|
try {
|
|
25200
|
-
|
|
25201
|
-
|
|
25202
|
-
|
|
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
|
|
25206
|
-
|
|
25207
|
-
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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 (
|
|
26899
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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) }] };
|