pi-cursor-bridge 0.1.9 → 0.1.11
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 +3 -3
- package/README.md +4 -2
- package/dist/cursor-bridge.mjs +1037 -187
- package/extensions/index.ts +1 -1
- package/package.json +3 -3
- package/skills/cursor-delegate/SKILL.md +18 -3
package/dist/cursor-bridge.mjs
CHANGED
|
@@ -2990,7 +2990,7 @@ var require_compile = __commonJS({
|
|
|
2990
2990
|
const schOrFunc = root.refs[ref];
|
|
2991
2991
|
if (schOrFunc)
|
|
2992
2992
|
return schOrFunc;
|
|
2993
|
-
let _sch =
|
|
2993
|
+
let _sch = resolve8.call(this, root, ref);
|
|
2994
2994
|
if (_sch === void 0) {
|
|
2995
2995
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
2996
2996
|
const { schemaId } = this.opts;
|
|
@@ -3017,7 +3017,7 @@ var require_compile = __commonJS({
|
|
|
3017
3017
|
function sameSchemaEnv(s1, s2) {
|
|
3018
3018
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3019
3019
|
}
|
|
3020
|
-
function
|
|
3020
|
+
function resolve8(root, ref) {
|
|
3021
3021
|
let sch;
|
|
3022
3022
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3023
3023
|
ref = sch;
|
|
@@ -3842,7 +3842,7 @@ var require_fast_uri = __commonJS({
|
|
|
3842
3842
|
}
|
|
3843
3843
|
return uri;
|
|
3844
3844
|
}
|
|
3845
|
-
function
|
|
3845
|
+
function resolve8(baseURI, relativeURI, options) {
|
|
3846
3846
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3847
3847
|
const {
|
|
3848
3848
|
parsed: baseParsed,
|
|
@@ -4204,7 +4204,7 @@ var require_fast_uri = __commonJS({
|
|
|
4204
4204
|
var fastUri = {
|
|
4205
4205
|
SCHEMES,
|
|
4206
4206
|
normalize,
|
|
4207
|
-
resolve:
|
|
4207
|
+
resolve: resolve8,
|
|
4208
4208
|
resolveComponent,
|
|
4209
4209
|
equal,
|
|
4210
4210
|
serialize,
|
|
@@ -11359,8 +11359,8 @@ var init_workspace_binding = __esm({
|
|
|
11359
11359
|
import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
|
|
11360
11360
|
import { existsSync as existsSync2 } from "fs";
|
|
11361
11361
|
import { createRequire as createNodeRequire } from "node:module";
|
|
11362
|
-
import { homedir as
|
|
11363
|
-
import { basename as
|
|
11362
|
+
import { homedir as homedir6 } from "node:os";
|
|
11363
|
+
import { basename as basename4, extname as extname2, join as join6, resolve as resolve5, win32 as winPath, posix as posixPath } from "node:path";
|
|
11364
11364
|
import http from "http";
|
|
11365
11365
|
function resolveCursorLaunchCdpPort(port = process.env.CURSOR_BRIDGE_CDP_PORT) {
|
|
11366
11366
|
const parsed = Number(port == null || String(port).trim() === "" ? 9223 : port);
|
|
@@ -11387,13 +11387,13 @@ function resolveCodexThreadProjectPath(options = {}) {
|
|
|
11387
11387
|
try {
|
|
11388
11388
|
const lookupThreadCwd = options.lookupThreadCwd || ((id) => {
|
|
11389
11389
|
const { DatabaseSync } = (options.requireImpl || loadModule)("node:sqlite");
|
|
11390
|
-
const databasePath = options.databasePath ||
|
|
11390
|
+
const databasePath = options.databasePath || join6(homedir6(), ".codex", "state_5.sqlite");
|
|
11391
11391
|
database = new DatabaseSync(databasePath, { readOnly: true });
|
|
11392
11392
|
return database.prepare("SELECT cwd FROM threads WHERE id = ?").get(id)?.cwd || null;
|
|
11393
11393
|
});
|
|
11394
11394
|
const candidate = normalizeCodexThreadCwd(lookupThreadCwd(threadId));
|
|
11395
11395
|
const existsImpl = options.existsImpl || existsSync2;
|
|
11396
|
-
const resolved = candidate && !looksLikePluginRuntimePath(candidate) && existsImpl(candidate) ?
|
|
11396
|
+
const resolved = candidate && !looksLikePluginRuntimePath(candidate) && existsImpl(candidate) ? resolve5(candidate) : null;
|
|
11397
11397
|
if (options.useCache !== false) CODEX_THREAD_PROJECTS.set(threadId, resolved);
|
|
11398
11398
|
return resolved;
|
|
11399
11399
|
} catch {
|
|
@@ -11408,14 +11408,14 @@ function resolveCodexThreadProjectPath(options = {}) {
|
|
|
11408
11408
|
}
|
|
11409
11409
|
function resolveProjectPath(value = process.env.CURSOR_PROJECT_PATH, options = {}) {
|
|
11410
11410
|
const explicit = String(value || "").trim();
|
|
11411
|
-
if (explicit) return
|
|
11411
|
+
if (explicit) return resolve5(explicit);
|
|
11412
11412
|
const persisted = String(options.persistedProjectPath || "").trim();
|
|
11413
|
-
if (persisted) return
|
|
11413
|
+
if (persisted) return resolve5(normalizeCodexThreadCwd(persisted));
|
|
11414
11414
|
const threadProjectPath = options.threadProjectPath === void 0 ? resolveCodexThreadProjectPath(options) : options.threadProjectPath;
|
|
11415
|
-
if (threadProjectPath) return
|
|
11415
|
+
if (threadProjectPath) return resolve5(normalizeCodexThreadCwd(threadProjectPath));
|
|
11416
11416
|
const cwd = options.cwd ?? process.cwd();
|
|
11417
11417
|
if (!cwd || looksLikePluginRuntimePath(cwd)) return null;
|
|
11418
|
-
return
|
|
11418
|
+
return resolve5(cwd);
|
|
11419
11419
|
}
|
|
11420
11420
|
function cursorFromRegistry(options = {}) {
|
|
11421
11421
|
const execFileSyncImpl = options.execFileSyncImpl || execFileSync2;
|
|
@@ -11480,7 +11480,7 @@ function findCursorExeDetails(options = {}) {
|
|
|
11480
11480
|
existsImpl
|
|
11481
11481
|
});
|
|
11482
11482
|
if (fromReg) return { path: fromReg, source: "windows_registry", platform };
|
|
11483
|
-
const localAppData = env.LOCALAPPDATA ||
|
|
11483
|
+
const localAppData = env.LOCALAPPDATA || join6(homedir6(), "AppData", "Local");
|
|
11484
11484
|
const programFiles = env.ProgramFiles || env.PROGRAMFILES || "C:\\Program Files";
|
|
11485
11485
|
const programFilesX86 = env["ProgramFiles(x86)"] || env.PROGRAMFILES_X86 || "";
|
|
11486
11486
|
const candidates = [
|
|
@@ -11497,7 +11497,7 @@ function findCursorExeDetails(options = {}) {
|
|
|
11497
11497
|
return null;
|
|
11498
11498
|
}
|
|
11499
11499
|
if (platform === "darwin") {
|
|
11500
|
-
const userHome = env.HOME ||
|
|
11500
|
+
const userHome = env.HOME || homedir6();
|
|
11501
11501
|
const candidates = [
|
|
11502
11502
|
"/Applications/Cursor.app/Contents/MacOS/Cursor",
|
|
11503
11503
|
userHome && posixPath.join(userHome, "Applications", "Cursor.app", "Contents", "MacOS", "Cursor")
|
|
@@ -11516,42 +11516,42 @@ function findCursorExe(options = {}) {
|
|
|
11516
11516
|
return findCursorExeDetails(options)?.path || null;
|
|
11517
11517
|
}
|
|
11518
11518
|
function cdpUp(timeoutMs = 1500) {
|
|
11519
|
-
return new Promise((
|
|
11519
|
+
return new Promise((resolve8) => {
|
|
11520
11520
|
const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/version" }, (res) => {
|
|
11521
11521
|
res.resume();
|
|
11522
|
-
|
|
11522
|
+
resolve8(res.statusCode === 200);
|
|
11523
11523
|
});
|
|
11524
|
-
req.on("error", () =>
|
|
11524
|
+
req.on("error", () => resolve8(false));
|
|
11525
11525
|
req.setTimeout(timeoutMs, () => {
|
|
11526
11526
|
try {
|
|
11527
11527
|
req.destroy();
|
|
11528
11528
|
} catch {
|
|
11529
11529
|
}
|
|
11530
|
-
|
|
11530
|
+
resolve8(false);
|
|
11531
11531
|
});
|
|
11532
11532
|
});
|
|
11533
11533
|
}
|
|
11534
11534
|
function cdpIsCursor(timeoutMs = 1500) {
|
|
11535
|
-
return new Promise((
|
|
11535
|
+
return new Promise((resolve8) => {
|
|
11536
11536
|
const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/list" }, (res) => {
|
|
11537
11537
|
let d = "";
|
|
11538
11538
|
res.on("data", (c) => d += c);
|
|
11539
11539
|
res.on("end", () => {
|
|
11540
11540
|
try {
|
|
11541
|
-
if (/[\/\\](windsurf)[\/\\]/i.test(d)) return
|
|
11542
|
-
|
|
11541
|
+
if (/[\/\\](windsurf)[\/\\]/i.test(d)) return resolve8(false);
|
|
11542
|
+
resolve8(/[\/\\]cursor[\/\\](resources|app)|cursor\.exe|vscode-app[^"]*[\/\\]cursor[\/\\]/i.test(d));
|
|
11543
11543
|
} catch {
|
|
11544
|
-
|
|
11544
|
+
resolve8(false);
|
|
11545
11545
|
}
|
|
11546
11546
|
});
|
|
11547
11547
|
});
|
|
11548
|
-
req.on("error", () =>
|
|
11548
|
+
req.on("error", () => resolve8(false));
|
|
11549
11549
|
req.setTimeout(timeoutMs, () => {
|
|
11550
11550
|
try {
|
|
11551
11551
|
req.destroy();
|
|
11552
11552
|
} catch {
|
|
11553
11553
|
}
|
|
11554
|
-
|
|
11554
|
+
resolve8(false);
|
|
11555
11555
|
});
|
|
11556
11556
|
});
|
|
11557
11557
|
}
|
|
@@ -11643,7 +11643,7 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
11643
11643
|
const cdpIsCursorImpl = options.cdpIsCursorImpl || cdpIsCursor;
|
|
11644
11644
|
const cursorRunningImpl = options.cursorRunningImpl || cursorRunning;
|
|
11645
11645
|
const findCursorExeDetailsImpl = options.findCursorExeDetailsImpl || findCursorExeDetails;
|
|
11646
|
-
const projectPath = Object.hasOwn(options, "projectPath") ? options.projectPath ?
|
|
11646
|
+
const projectPath = Object.hasOwn(options, "projectPath") ? options.projectPath ? resolve5(String(options.projectPath)) : null : resolveProjectPath();
|
|
11647
11647
|
const listCdpPageTargetsImpl = options.listCdpPageTargetsImpl || listCdpPageTargets;
|
|
11648
11648
|
const spawnImpl = options.spawnImpl || spawn2;
|
|
11649
11649
|
const sleepImpl = options.sleepImpl || ((ms) => new Promise((resolveWait) => setTimeout(resolveWait, ms)));
|
|
@@ -11946,10 +11946,10 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
11946
11946
|
};
|
|
11947
11947
|
}
|
|
11948
11948
|
function normalizeProjectKey(projectPath) {
|
|
11949
|
-
return projectPath ?
|
|
11949
|
+
return projectPath ? resolve5(String(projectPath)).replace(/\\/g, "/").toLowerCase() : "";
|
|
11950
11950
|
}
|
|
11951
11951
|
function targetTitleMatchesProject(title, projectPath) {
|
|
11952
|
-
const name =
|
|
11952
|
+
const name = basename4(String(projectPath || "")).trim().toLowerCase();
|
|
11953
11953
|
if (!name) return false;
|
|
11954
11954
|
const extension2 = extname2(name);
|
|
11955
11955
|
const candidates = [...new Set([name, extension2 ? name.slice(0, -extension2.length) : name].filter(Boolean))];
|
|
@@ -12255,17 +12255,17 @@ import net from "node:net";
|
|
|
12255
12255
|
import { createHash as createHash2 } from "node:crypto";
|
|
12256
12256
|
import {
|
|
12257
12257
|
existsSync as existsSync4,
|
|
12258
|
-
mkdirSync as
|
|
12259
|
-
readFileSync as
|
|
12258
|
+
mkdirSync as mkdirSync6,
|
|
12259
|
+
readFileSync as readFileSync5,
|
|
12260
12260
|
realpathSync,
|
|
12261
12261
|
readdirSync,
|
|
12262
|
-
renameSync as
|
|
12263
|
-
rmSync as
|
|
12262
|
+
renameSync as renameSync5,
|
|
12263
|
+
rmSync as rmSync5,
|
|
12264
12264
|
unlinkSync,
|
|
12265
|
-
writeFileSync as
|
|
12265
|
+
writeFileSync as writeFileSync5
|
|
12266
12266
|
} from "node:fs";
|
|
12267
12267
|
import { fileURLToPath } from "node:url";
|
|
12268
|
-
import { basename as
|
|
12268
|
+
import { basename as basename5, dirname as dirname5, join as join7, resolve as resolve6, win32 as win32Path } from "node:path";
|
|
12269
12269
|
function resolveSupervisorSpawnCwd({
|
|
12270
12270
|
requestedCwd = null,
|
|
12271
12271
|
runtimeRoot = null,
|
|
@@ -12280,9 +12280,9 @@ function resolveSupervisorSpawnCwd({
|
|
|
12280
12280
|
return runtimeRoot;
|
|
12281
12281
|
}
|
|
12282
12282
|
function resolvePluginLocalLifecycleDir(sourceScript) {
|
|
12283
|
-
const scriptDir =
|
|
12284
|
-
const pluginRoot =
|
|
12285
|
-
return
|
|
12283
|
+
const scriptDir = dirname5(resolve6(sourceScript));
|
|
12284
|
+
const pluginRoot = basename5(scriptDir).toLowerCase() === "dist" ? dirname5(scriptDir) : scriptDir;
|
|
12285
|
+
return join7(pluginRoot, ".cursor-bridge-lifecycle");
|
|
12286
12286
|
}
|
|
12287
12287
|
function inspectWindowsAppContainerPath(target, options = {}) {
|
|
12288
12288
|
const platform = options.platform || process.platform;
|
|
@@ -12325,59 +12325,59 @@ function sleep(ms) {
|
|
|
12325
12325
|
}
|
|
12326
12326
|
function resolveSupervisorScript() {
|
|
12327
12327
|
if (process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT && existsSync4(process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT)) {
|
|
12328
|
-
return
|
|
12328
|
+
return resolve6(process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT);
|
|
12329
12329
|
}
|
|
12330
|
-
const here =
|
|
12330
|
+
const here = dirname5(fileURLToPath(import.meta.url));
|
|
12331
12331
|
const candidates = [];
|
|
12332
12332
|
if (typeof process.argv[1] === "string") {
|
|
12333
|
-
const entryDir =
|
|
12334
|
-
candidates.push(
|
|
12335
|
-
candidates.push(
|
|
12333
|
+
const entryDir = dirname5(resolve6(process.argv[1]));
|
|
12334
|
+
candidates.push(join7(entryDir, "dist", "cursor-lifecycle-supervisor.mjs"));
|
|
12335
|
+
candidates.push(join7(entryDir, "cursor-lifecycle-supervisor.mjs"));
|
|
12336
12336
|
}
|
|
12337
12337
|
candidates.push(
|
|
12338
|
-
|
|
12339
|
-
|
|
12338
|
+
join7(here, "dist", "cursor-lifecycle-supervisor.mjs"),
|
|
12339
|
+
join7(here, "cursor-lifecycle-supervisor.mjs")
|
|
12340
12340
|
);
|
|
12341
12341
|
for (const c of candidates) {
|
|
12342
12342
|
if (existsSync4(c)) return c;
|
|
12343
12343
|
}
|
|
12344
|
-
return
|
|
12344
|
+
return join7(here, "cursor-lifecycle-supervisor.mjs");
|
|
12345
12345
|
}
|
|
12346
12346
|
function writeRuntimeFile(target, content) {
|
|
12347
|
-
if (existsSync4(target) &&
|
|
12347
|
+
if (existsSync4(target) && readFileSync5(target).equals(content)) return;
|
|
12348
12348
|
const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
12349
|
-
|
|
12349
|
+
writeFileSync5(temporary, content);
|
|
12350
12350
|
try {
|
|
12351
|
-
|
|
12351
|
+
renameSync5(temporary, target);
|
|
12352
12352
|
} catch (error2) {
|
|
12353
12353
|
if (!existsSync4(target)) {
|
|
12354
|
-
|
|
12354
|
+
rmSync5(temporary, { force: true });
|
|
12355
12355
|
throw error2;
|
|
12356
12356
|
}
|
|
12357
|
-
if (
|
|
12358
|
-
|
|
12357
|
+
if (readFileSync5(target).equals(content)) {
|
|
12358
|
+
rmSync5(temporary, { force: true });
|
|
12359
12359
|
return;
|
|
12360
12360
|
}
|
|
12361
|
-
|
|
12362
|
-
|
|
12361
|
+
rmSync5(target, { force: true });
|
|
12362
|
+
renameSync5(temporary, target);
|
|
12363
12363
|
}
|
|
12364
12364
|
}
|
|
12365
12365
|
function materializeLifecycleSupervisorRuntime({ sourceScript, dir = defaultLifecycleDir() } = {}) {
|
|
12366
12366
|
const described = describeLifecycleSupervisorRuntime({ sourceScript, dir });
|
|
12367
12367
|
const { sourceScript: source, content, fingerprint } = described;
|
|
12368
|
-
const runtimeRoot =
|
|
12369
|
-
|
|
12370
|
-
const script =
|
|
12368
|
+
const runtimeRoot = join7(ensureLifecycleDir(dir), "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
|
|
12369
|
+
mkdirSync6(runtimeRoot, { recursive: true });
|
|
12370
|
+
const script = join7(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
|
|
12371
12371
|
writeRuntimeFile(script, content);
|
|
12372
12372
|
return { sourceScript: source, script, runtimeRoot, fingerprint };
|
|
12373
12373
|
}
|
|
12374
12374
|
function describeLifecycleSupervisorRuntime({ sourceScript, dir = defaultLifecycleDir() } = {}) {
|
|
12375
|
-
const source =
|
|
12375
|
+
const source = resolve6(sourceScript || resolveSupervisorScript());
|
|
12376
12376
|
if (!existsSync4(source)) throw new Error(`lifecycle supervisor script missing: ${source}`);
|
|
12377
|
-
const content =
|
|
12377
|
+
const content = readFileSync5(source);
|
|
12378
12378
|
const fingerprint = createHash2("sha256").update(content).digest("hex");
|
|
12379
|
-
const runtimeRoot =
|
|
12380
|
-
const script =
|
|
12379
|
+
const runtimeRoot = join7(dir, "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
|
|
12380
|
+
const script = join7(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
|
|
12381
12381
|
return { sourceScript: source, script, runtimeRoot, fingerprint, content };
|
|
12382
12382
|
}
|
|
12383
12383
|
function isProcessAlive(pid) {
|
|
@@ -12391,7 +12391,7 @@ function isProcessAlive(pid) {
|
|
|
12391
12391
|
}
|
|
12392
12392
|
function readPidFile(pidPath) {
|
|
12393
12393
|
try {
|
|
12394
|
-
const n = Number(String(
|
|
12394
|
+
const n = Number(String(readFileSync5(pidPath, "utf8")).trim());
|
|
12395
12395
|
return Number.isFinite(n) ? n : null;
|
|
12396
12396
|
} catch {
|
|
12397
12397
|
return null;
|
|
@@ -12506,7 +12506,7 @@ function tryUnlink(path) {
|
|
|
12506
12506
|
}
|
|
12507
12507
|
}
|
|
12508
12508
|
function writeBootEnv(dir, extra = {}) {
|
|
12509
|
-
const bootPath =
|
|
12509
|
+
const bootPath = join7(dir, `boot-env-${process.pid}-${Date.now()}.json`);
|
|
12510
12510
|
const payload = { ...extra };
|
|
12511
12511
|
for (const [key, value] of Object.entries(process.env)) {
|
|
12512
12512
|
if (key.startsWith("CURSOR_BRIDGE_") || key === "CURSOR_PROJECT_PATH" || key === "CURSOR_EXE") {
|
|
@@ -12517,7 +12517,7 @@ function writeBootEnv(dir, extra = {}) {
|
|
|
12517
12517
|
for (const [k, v] of Object.entries(payload)) {
|
|
12518
12518
|
if (v != null && v !== "") cleaned[k] = String(v);
|
|
12519
12519
|
}
|
|
12520
|
-
|
|
12520
|
+
writeFileSync5(bootPath, `${JSON.stringify(cleaned, null, 2)}
|
|
12521
12521
|
`, { encoding: "utf8" });
|
|
12522
12522
|
return bootPath;
|
|
12523
12523
|
}
|
|
@@ -12552,9 +12552,9 @@ async function ensureSupervisorConnected(options = {}) {
|
|
|
12552
12552
|
let targetRuntime = null;
|
|
12553
12553
|
try {
|
|
12554
12554
|
targetRuntime = options.persistSupervisorRuntime === false ? {
|
|
12555
|
-
sourceScript:
|
|
12556
|
-
script:
|
|
12557
|
-
runtimeRoot:
|
|
12555
|
+
sourceScript: resolve6(sourceScript),
|
|
12556
|
+
script: resolve6(sourceScript),
|
|
12557
|
+
runtimeRoot: dirname5(resolve6(sourceScript)),
|
|
12558
12558
|
fingerprint: null
|
|
12559
12559
|
} : describeLifecycleSupervisorRuntime({ sourceScript, dir });
|
|
12560
12560
|
} catch {
|
|
@@ -12638,9 +12638,9 @@ async function ensureSupervisorConnected(options = {}) {
|
|
|
12638
12638
|
try {
|
|
12639
12639
|
ensureLifecycleDir(dir);
|
|
12640
12640
|
runtime = options.persistSupervisorRuntime === false ? {
|
|
12641
|
-
sourceScript:
|
|
12642
|
-
script:
|
|
12643
|
-
runtimeRoot:
|
|
12641
|
+
sourceScript: resolve6(sourceScript),
|
|
12642
|
+
script: resolve6(sourceScript),
|
|
12643
|
+
runtimeRoot: dirname5(resolve6(sourceScript)),
|
|
12644
12644
|
fingerprint: null
|
|
12645
12645
|
} : (options.materializeRuntimeImpl || materializeLifecycleSupervisorRuntime)({ sourceScript, dir });
|
|
12646
12646
|
} catch (error2) {
|
|
@@ -21094,7 +21094,7 @@ var Protocol = class {
|
|
|
21094
21094
|
return;
|
|
21095
21095
|
}
|
|
21096
21096
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
21097
|
-
await new Promise((
|
|
21097
|
+
await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
|
|
21098
21098
|
options?.signal?.throwIfAborted();
|
|
21099
21099
|
}
|
|
21100
21100
|
} catch (error2) {
|
|
@@ -21111,7 +21111,7 @@ var Protocol = class {
|
|
|
21111
21111
|
*/
|
|
21112
21112
|
request(request2, resultSchema, options) {
|
|
21113
21113
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
21114
|
-
return new Promise((
|
|
21114
|
+
return new Promise((resolve8, reject) => {
|
|
21115
21115
|
const earlyReject = (error2) => {
|
|
21116
21116
|
reject(error2);
|
|
21117
21117
|
};
|
|
@@ -21189,7 +21189,7 @@ var Protocol = class {
|
|
|
21189
21189
|
if (!parseResult.success) {
|
|
21190
21190
|
reject(parseResult.error);
|
|
21191
21191
|
} else {
|
|
21192
|
-
|
|
21192
|
+
resolve8(parseResult.data);
|
|
21193
21193
|
}
|
|
21194
21194
|
} catch (error2) {
|
|
21195
21195
|
reject(error2);
|
|
@@ -21450,12 +21450,12 @@ var Protocol = class {
|
|
|
21450
21450
|
}
|
|
21451
21451
|
} catch {
|
|
21452
21452
|
}
|
|
21453
|
-
return new Promise((
|
|
21453
|
+
return new Promise((resolve8, reject) => {
|
|
21454
21454
|
if (signal.aborted) {
|
|
21455
21455
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
21456
21456
|
return;
|
|
21457
21457
|
}
|
|
21458
|
-
const timeoutId = setTimeout(
|
|
21458
|
+
const timeoutId = setTimeout(resolve8, interval);
|
|
21459
21459
|
signal.addEventListener("abort", () => {
|
|
21460
21460
|
clearTimeout(timeoutId);
|
|
21461
21461
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -22331,19 +22331,19 @@ var StdioServerTransport = class {
|
|
|
22331
22331
|
this.onclose?.();
|
|
22332
22332
|
}
|
|
22333
22333
|
send(message) {
|
|
22334
|
-
return new Promise((
|
|
22334
|
+
return new Promise((resolve8) => {
|
|
22335
22335
|
const json = serializeMessage(message);
|
|
22336
22336
|
if (this._stdout.write(json)) {
|
|
22337
|
-
|
|
22337
|
+
resolve8();
|
|
22338
22338
|
} else {
|
|
22339
|
-
this._stdout.once("drain",
|
|
22339
|
+
this._stdout.once("drain", resolve8);
|
|
22340
22340
|
}
|
|
22341
22341
|
});
|
|
22342
22342
|
}
|
|
22343
22343
|
};
|
|
22344
22344
|
|
|
22345
22345
|
// server.mjs
|
|
22346
|
-
import { basename as
|
|
22346
|
+
import { basename as basename6, dirname as dirname6, join as join8, resolve as resolve7 } from "node:path";
|
|
22347
22347
|
|
|
22348
22348
|
// node_modules/ws/wrapper.mjs
|
|
22349
22349
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -22467,9 +22467,144 @@ function updateCursorModelPreferences(filePath, { action, target, model, effort
|
|
|
22467
22467
|
|
|
22468
22468
|
// server.mjs
|
|
22469
22469
|
init_workspace_binding();
|
|
22470
|
+
|
|
22471
|
+
// cursor-session-registry.mjs
|
|
22472
|
+
import { randomUUID } from "node:crypto";
|
|
22473
|
+
import {
|
|
22474
|
+
closeSync,
|
|
22475
|
+
mkdirSync as mkdirSync5,
|
|
22476
|
+
openSync,
|
|
22477
|
+
readFileSync as readFileSync4,
|
|
22478
|
+
renameSync as renameSync4,
|
|
22479
|
+
rmSync as rmSync4,
|
|
22480
|
+
statSync as statSync2,
|
|
22481
|
+
writeFileSync as writeFileSync4
|
|
22482
|
+
} from "node:fs";
|
|
22483
|
+
import { homedir as homedir5 } from "node:os";
|
|
22484
|
+
import { basename as basename3, dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
|
|
22485
|
+
var CURSOR_SESSION_REGISTRY_VERSION = 1;
|
|
22486
|
+
var CURSOR_SESSION_MODES = Object.freeze(["isolated", "create", "continue"]);
|
|
22487
|
+
function sessionRegistryError(code, message, cause) {
|
|
22488
|
+
const error2 = new Error(`${code}: ${message}`);
|
|
22489
|
+
error2.code = code;
|
|
22490
|
+
if (cause) error2.cause = cause;
|
|
22491
|
+
return error2;
|
|
22492
|
+
}
|
|
22493
|
+
function normalizeCursorSessionMode(value, fallback = "isolated") {
|
|
22494
|
+
const normalized = String(value || fallback).trim().toLowerCase().replace(/-/g, "_");
|
|
22495
|
+
return CURSOR_SESSION_MODES.includes(normalized) ? normalized : "";
|
|
22496
|
+
}
|
|
22497
|
+
function createCursorSessionId() {
|
|
22498
|
+
return `cursor-session-${randomUUID()}`;
|
|
22499
|
+
}
|
|
22500
|
+
function resolveCursorSessionRegistryFile(value, env = process.env) {
|
|
22501
|
+
if (value === void 0) value = env.CURSOR_BRIDGE_SESSION_FILE;
|
|
22502
|
+
const configured = String(value || "").trim();
|
|
22503
|
+
if (configured) return resolve4(configured);
|
|
22504
|
+
const configRoot = process.platform === "win32" && env.APPDATA ? env.APPDATA : env.XDG_CONFIG_HOME || join5(homedir5(), ".config");
|
|
22505
|
+
return join5(configRoot, "cursor-bridge", "sessions-v1.json");
|
|
22506
|
+
}
|
|
22507
|
+
function emptyCursorSessionRegistry() {
|
|
22508
|
+
return { version: CURSOR_SESSION_REGISTRY_VERSION, sessions: {}, updatedAt: null };
|
|
22509
|
+
}
|
|
22510
|
+
function readCursorSessionRegistry(filePath) {
|
|
22511
|
+
if (!filePath) return emptyCursorSessionRegistry();
|
|
22512
|
+
try {
|
|
22513
|
+
const parsed = JSON.parse(readFileSync4(filePath, "utf8"));
|
|
22514
|
+
if (!parsed || parsed.version !== CURSOR_SESSION_REGISTRY_VERSION) {
|
|
22515
|
+
throw sessionRegistryError(
|
|
22516
|
+
"SESSION_SCHEMA_UNSUPPORTED",
|
|
22517
|
+
`expected version ${CURSOR_SESSION_REGISTRY_VERSION}, received ${parsed && parsed.version}`
|
|
22518
|
+
);
|
|
22519
|
+
}
|
|
22520
|
+
if (!parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
22521
|
+
throw sessionRegistryError("SESSION_REGISTRY_INVALID", "sessions must be an object");
|
|
22522
|
+
}
|
|
22523
|
+
return {
|
|
22524
|
+
version: CURSOR_SESSION_REGISTRY_VERSION,
|
|
22525
|
+
sessions: { ...parsed.sessions },
|
|
22526
|
+
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : null
|
|
22527
|
+
};
|
|
22528
|
+
} catch (error2) {
|
|
22529
|
+
if (error2 && error2.code === "ENOENT") return emptyCursorSessionRegistry();
|
|
22530
|
+
if (error2 && /^SESSION_/.test(String(error2.code || ""))) throw error2;
|
|
22531
|
+
throw sessionRegistryError(
|
|
22532
|
+
"SESSION_REGISTRY_INVALID",
|
|
22533
|
+
`failed to read ${filePath}: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
22534
|
+
error2
|
|
22535
|
+
);
|
|
22536
|
+
}
|
|
22537
|
+
}
|
|
22538
|
+
function writeCursorSessionRegistry(filePath, state, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
22539
|
+
if (!filePath) throw sessionRegistryError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
|
|
22540
|
+
const target = resolve4(filePath);
|
|
22541
|
+
const normalized = {
|
|
22542
|
+
version: CURSOR_SESSION_REGISTRY_VERSION,
|
|
22543
|
+
sessions: state && state.sessions && typeof state.sessions === "object" ? state.sessions : {},
|
|
22544
|
+
updatedAt: now
|
|
22545
|
+
};
|
|
22546
|
+
mkdirSync5(dirname4(target), { recursive: true });
|
|
22547
|
+
const temporary = join5(dirname4(target), `.${basename3(target)}.${process.pid}.${Date.now()}.tmp`);
|
|
22548
|
+
try {
|
|
22549
|
+
writeFileSync4(temporary, `${JSON.stringify(normalized, null, 2)}
|
|
22550
|
+
`, { encoding: "utf8", mode: 384 });
|
|
22551
|
+
renameSync4(temporary, target);
|
|
22552
|
+
} catch (error2) {
|
|
22553
|
+
rmSync4(temporary, { force: true });
|
|
22554
|
+
throw sessionRegistryError(
|
|
22555
|
+
"SESSION_REGISTRY_WRITE_FAILED",
|
|
22556
|
+
`failed to persist ${target}: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
22557
|
+
error2
|
|
22558
|
+
);
|
|
22559
|
+
}
|
|
22560
|
+
return normalized;
|
|
22561
|
+
}
|
|
22562
|
+
function acquireRegistryLock(filePath, options = {}) {
|
|
22563
|
+
const lockPath = `${resolve4(filePath)}.lock`;
|
|
22564
|
+
const waitMs = Number(options.waitMs || 2e3);
|
|
22565
|
+
const staleMs = Number(options.staleMs || 3e4);
|
|
22566
|
+
const deadline = Date.now() + waitMs;
|
|
22567
|
+
mkdirSync5(dirname4(lockPath), { recursive: true });
|
|
22568
|
+
for (; ; ) {
|
|
22569
|
+
try {
|
|
22570
|
+
const handle = openSync(lockPath, "wx", 384);
|
|
22571
|
+
closeSync(handle);
|
|
22572
|
+
return () => rmSync4(lockPath, { force: true });
|
|
22573
|
+
} catch (error2) {
|
|
22574
|
+
if (!error2 || error2.code !== "EEXIST") {
|
|
22575
|
+
throw sessionRegistryError("SESSION_REGISTRY_LOCK_FAILED", error2 instanceof Error ? error2.message : String(error2), error2);
|
|
22576
|
+
}
|
|
22577
|
+
try {
|
|
22578
|
+
if (Date.now() - statSync2(lockPath).mtimeMs > staleMs) {
|
|
22579
|
+
rmSync4(lockPath, { force: true });
|
|
22580
|
+
continue;
|
|
22581
|
+
}
|
|
22582
|
+
} catch {
|
|
22583
|
+
}
|
|
22584
|
+
if (Date.now() >= deadline) {
|
|
22585
|
+
throw sessionRegistryError("SESSION_REGISTRY_BUSY", "another Cursor Bridge process is updating the session registry");
|
|
22586
|
+
}
|
|
22587
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
|
|
22588
|
+
}
|
|
22589
|
+
}
|
|
22590
|
+
}
|
|
22591
|
+
function updateCursorSessionRegistry(filePath, mutator, options = {}) {
|
|
22592
|
+
if (!filePath) throw sessionRegistryError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
|
|
22593
|
+
const release = acquireRegistryLock(filePath, options);
|
|
22594
|
+
try {
|
|
22595
|
+
const state = readCursorSessionRegistry(filePath);
|
|
22596
|
+
const result = mutator(state);
|
|
22597
|
+
writeCursorSessionRegistry(filePath, state, options.now || (/* @__PURE__ */ new Date()).toISOString());
|
|
22598
|
+
return result;
|
|
22599
|
+
} finally {
|
|
22600
|
+
release();
|
|
22601
|
+
}
|
|
22602
|
+
}
|
|
22603
|
+
|
|
22604
|
+
// server.mjs
|
|
22470
22605
|
init_cursor_ensure_core();
|
|
22471
22606
|
init_lifecycle_paths();
|
|
22472
|
-
var PLUGIN_VERSION = "5.
|
|
22607
|
+
var PLUGIN_VERSION = "5.8.1";
|
|
22473
22608
|
var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
22474
22609
|
var ORIGIN = `http://localhost:${CDP_PORT2}`;
|
|
22475
22610
|
var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
|
|
@@ -22477,6 +22612,26 @@ function normalizeDelegationMode(value = process.env.CURSOR_BRIDGE_DELEGATION) {
|
|
|
22477
22612
|
return String(value || "on").trim().toLowerCase() === "off" ? "off" : "on";
|
|
22478
22613
|
}
|
|
22479
22614
|
var DELEGATION_MODE = normalizeDelegationMode();
|
|
22615
|
+
function cursorSessionError(code, message) {
|
|
22616
|
+
const error2 = new Error(`${code}: ${message}`);
|
|
22617
|
+
error2.code = code;
|
|
22618
|
+
return error2;
|
|
22619
|
+
}
|
|
22620
|
+
function normalizeSessionRequestId(value) {
|
|
22621
|
+
const id = String(value || "").trim();
|
|
22622
|
+
if (id.length > 200) throw cursorSessionError("SESSION_REQUEST_ID_INVALID", "request_id exceeds 200 characters");
|
|
22623
|
+
return id;
|
|
22624
|
+
}
|
|
22625
|
+
function sessionPathContains(parent, child) {
|
|
22626
|
+
const base = normalizeAllowedPath(parent);
|
|
22627
|
+
const candidate = normalizeAllowedPath(child);
|
|
22628
|
+
return candidate === base || candidate.startsWith(`${base}/`);
|
|
22629
|
+
}
|
|
22630
|
+
function sameSessionProject(left, right) {
|
|
22631
|
+
const a = resolve7(String(left || ""));
|
|
22632
|
+
const b = resolve7(String(right || ""));
|
|
22633
|
+
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
22634
|
+
}
|
|
22480
22635
|
function searchResultContract() {
|
|
22481
22636
|
return [
|
|
22482
22637
|
"Return only the minimum sufficient evidence set, ordered by evidence strength. Do not pad the result with similar matches.",
|
|
@@ -22522,17 +22677,26 @@ function isConfirmedCompletedReply({ answer, snapshot = {}, sawStop = false, bas
|
|
|
22522
22677
|
const hasCompletionEvidence = sawStop || Number(snapshot.messageCount || 0) >= Number(baselineCount || 0) + 2;
|
|
22523
22678
|
return hasAssistantEvidence && hasCompletionEvidence;
|
|
22524
22679
|
}
|
|
22680
|
+
function isSessionTurnReplyReady(responseBaseline, snapshot = {}) {
|
|
22681
|
+
if (!responseBaseline) return true;
|
|
22682
|
+
if (Number(snapshot.messageCount || 0) >= Number(responseBaseline.messageCount || 0) + 2) return true;
|
|
22683
|
+
const replyLength = Number(snapshot.replyLength || 0);
|
|
22684
|
+
return replyLength > 0 && (replyLength !== Number(responseBaseline.replyLength || 0) || Number(snapshot.replyHash || 0) !== Number(responseBaseline.replyHash || 0));
|
|
22685
|
+
}
|
|
22686
|
+
function shouldScheduleParallelOriginRestore(job) {
|
|
22687
|
+
return String(job && job.sessionMode || "isolated") === "isolated";
|
|
22688
|
+
}
|
|
22525
22689
|
var DO_DEFAULT_CONTRACT = "\n\nCompletion requirements: Work directly in the workspace currently open in Cursor; do not push to a remote. Before finishing, inspect the actual changes and run verification proportional to risk. The final reply must list completed work, changed files, verification results, and remaining risks or blockers.";
|
|
22526
22690
|
var DO_LANGUAGE_CONTRACT = "\n\nResponse language: Reply in the language of the user task unless it explicitly requests another language. Never translate paths, commands, identifiers, keys, enum values, exact options, or error/status codes.";
|
|
22527
22691
|
var CDP_HOST2 = "127.0.0.1";
|
|
22528
22692
|
function httpJson(path) {
|
|
22529
|
-
return new Promise((
|
|
22693
|
+
return new Promise((resolve8, reject) => {
|
|
22530
22694
|
const req = http2.get({ host: CDP_HOST2, port: CDP_PORT2, path }, (res) => {
|
|
22531
22695
|
let d = "";
|
|
22532
22696
|
res.on("data", (c) => d += c);
|
|
22533
22697
|
res.on("end", () => {
|
|
22534
22698
|
try {
|
|
22535
|
-
|
|
22699
|
+
resolve8(JSON.parse(d));
|
|
22536
22700
|
} catch {
|
|
22537
22701
|
reject(new Error("CDP returned a non-JSON response"));
|
|
22538
22702
|
}
|
|
@@ -22580,11 +22744,6 @@ function summarizeCdpPages(list) {
|
|
|
22580
22744
|
page: first && first.url ? String(first.url).slice(0, 60) : ""
|
|
22581
22745
|
};
|
|
22582
22746
|
}
|
|
22583
|
-
function isBlankAgentsWindow(page) {
|
|
22584
|
-
if (!page || !isAgentsWindowTitle(page.title)) return false;
|
|
22585
|
-
if (page.probeError || !page.capabilities) return true;
|
|
22586
|
-
return page.capabilities.uiFlavor !== "agents_v2" && !page.capabilities.hasWritableInput;
|
|
22587
|
-
}
|
|
22588
22747
|
var NORMAL_AGENTS_PRESENTATION_REFRESH_MS = 5 * 60 * 1e3;
|
|
22589
22748
|
function shouldRecoverNormalAgentsPresentation({
|
|
22590
22749
|
runtimeMode,
|
|
@@ -22605,25 +22764,6 @@ function shouldRecoverNormalAgentsPresentation({
|
|
|
22605
22764
|
if (!Number.isFinite(previousAt)) return true;
|
|
22606
22765
|
return Number(now) - previousAt >= Math.max(0, Number(refreshMs) || 0);
|
|
22607
22766
|
}
|
|
22608
|
-
async function reloadPageTarget(page, timeoutMs = 5e3) {
|
|
22609
|
-
if (!page || !page.webSocketDebuggerUrl) return false;
|
|
22610
|
-
const c = makeClient(page.webSocketDebuggerUrl);
|
|
22611
|
-
try {
|
|
22612
|
-
await Promise.race([
|
|
22613
|
-
c.ready,
|
|
22614
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("Timed out connecting to the CDP target")), timeoutMs))
|
|
22615
|
-
]);
|
|
22616
|
-
await Promise.race([
|
|
22617
|
-
c.send("Page.reload", { ignoreCache: true }),
|
|
22618
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("Timed out reloading the CDP target")), timeoutMs))
|
|
22619
|
-
]);
|
|
22620
|
-
return true;
|
|
22621
|
-
} catch {
|
|
22622
|
-
return false;
|
|
22623
|
-
} finally {
|
|
22624
|
-
c.close();
|
|
22625
|
-
}
|
|
22626
|
-
}
|
|
22627
22767
|
async function inspectPageTarget(page) {
|
|
22628
22768
|
const c = makeClient(page.webSocketDebuggerUrl);
|
|
22629
22769
|
const probeMs = Number(process.env.CURSOR_BRIDGE_PAGE_PROBE_TIMEOUT || 5e3);
|
|
@@ -22643,24 +22783,6 @@ async function inspectPageTarget(page) {
|
|
|
22643
22783
|
c.close();
|
|
22644
22784
|
}
|
|
22645
22785
|
}
|
|
22646
|
-
async function recoverBlankAgentsWindows(inspected) {
|
|
22647
|
-
const pages = Array.isArray(inspected) ? inspected : [];
|
|
22648
|
-
const recovered = [];
|
|
22649
|
-
for (const page of pages) {
|
|
22650
|
-
if (!isBlankAgentsWindow(page)) {
|
|
22651
|
-
recovered.push(page);
|
|
22652
|
-
continue;
|
|
22653
|
-
}
|
|
22654
|
-
const reloaded = await reloadPageTarget(page);
|
|
22655
|
-
if (!reloaded) {
|
|
22656
|
-
recovered.push(page);
|
|
22657
|
-
continue;
|
|
22658
|
-
}
|
|
22659
|
-
await sleep2(800);
|
|
22660
|
-
recovered.push(await inspectPageTarget(page));
|
|
22661
|
-
}
|
|
22662
|
-
return recovered;
|
|
22663
|
-
}
|
|
22664
22786
|
function pagesWithFlavor(pages, flavor) {
|
|
22665
22787
|
return (Array.isArray(pages) ? pages : []).filter((page) => page && page.capabilities && page.capabilities.uiFlavor === flavor);
|
|
22666
22788
|
}
|
|
@@ -22691,7 +22813,7 @@ async function findPage(options = {}) {
|
|
|
22691
22813
|
if (options.targetId && options.preferAgentsV2 !== true && options.preferLegacy !== true) {
|
|
22692
22814
|
return selectCursorPageCandidate(pages, options);
|
|
22693
22815
|
}
|
|
22694
|
-
const inspected = await
|
|
22816
|
+
const inspected = await Promise.all(pages.map(inspectPageTarget));
|
|
22695
22817
|
return selectPageForUiPreference(inspected, options) || pages[0];
|
|
22696
22818
|
}
|
|
22697
22819
|
function makeClient(wsUrl) {
|
|
@@ -22759,7 +22881,7 @@ function makeClient(wsUrl) {
|
|
|
22759
22881
|
}
|
|
22760
22882
|
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
22761
22883
|
async function evalJS(c, expr) {
|
|
22762
|
-
const r = await c.send("Runtime.evaluate", { expression: expr, returnByValue: true, includeCommandLineAPI: true });
|
|
22884
|
+
const r = await c.send("Runtime.evaluate", { expression: expr, returnByValue: true, includeCommandLineAPI: true, awaitPromise: true });
|
|
22763
22885
|
if (r.exceptionDetails) throw new Error("Page exception: " + (r.exceptionDetails.exception && r.exceptionDetails.exception.description || r.exceptionDetails.text));
|
|
22764
22886
|
return r.result && r.result.value;
|
|
22765
22887
|
}
|
|
@@ -22777,6 +22899,70 @@ var INPUT_PICKER_BODY = `
|
|
|
22777
22899
|
.filter(e=>e.offsetParent!==null&&!e.disabled&&e.getAttribute('aria-disabled')!=='true'&&e.getAttribute('contenteditable')!=='false')
|
|
22778
22900
|
.sort((a,b)=>(b.classList&&b.classList.contains('ui-prompt-input-editor__input')?1:0)-(a.classList&&a.classList.contains('ui-prompt-input-editor__input')?1:0))[0]||null;`;
|
|
22779
22901
|
var EXPR_VISIBLE = `(function(){${INPUT_PICKER_BODY}return !!pickInput();})()`;
|
|
22902
|
+
var CHAT_PANEL_NEXT_STEP = "Complete or close the current Customize/Settings dialog, open Cursor's main Agent/Chat panel or New Chat, then retry the same request.";
|
|
22903
|
+
function classifyChatPanelDiagnostic(snapshot = {}) {
|
|
22904
|
+
const evidence = {
|
|
22905
|
+
writableInputVisible: snapshot.writableInputVisible === true || snapshot.hasWritableInput === true,
|
|
22906
|
+
inputCandidateCount: Number.isFinite(Number(snapshot.inputCandidateCount)) ? Number(snapshot.inputCandidateCount) : 0,
|
|
22907
|
+
settingsOrCustomizeVisible: snapshot.settingsOrCustomizeVisible === true,
|
|
22908
|
+
modalVisible: snapshot.modalVisible === true,
|
|
22909
|
+
modalLabel: snapshot.modalLabel ? String(snapshot.modalLabel) : null,
|
|
22910
|
+
signInControlVisible: snapshot.signInControlVisible === true,
|
|
22911
|
+
signInVisible: snapshot.signInVisible === true,
|
|
22912
|
+
agentSurfaceVisible: snapshot.agentSurfaceVisible === true,
|
|
22913
|
+
composerCount: Number.isFinite(Number(snapshot.composerCount)) ? Number(snapshot.composerCount) : 0,
|
|
22914
|
+
visibilityState: String(snapshot.visibilityState || "unknown"),
|
|
22915
|
+
focused: typeof snapshot.focused === "boolean" ? snapshot.focused : null,
|
|
22916
|
+
pageTitle: snapshot.pageTitle ? String(snapshot.pageTitle) : null,
|
|
22917
|
+
probeError: snapshot.probeError ? String(snapshot.probeError) : null,
|
|
22918
|
+
inputStateChanged: snapshot.inputStateChanged === true
|
|
22919
|
+
};
|
|
22920
|
+
let state = "composer_input_unavailable";
|
|
22921
|
+
let needsAction = "open_new_chat";
|
|
22922
|
+
let message = "Cursor shows an Agent/Chat surface, but no writable input was detected.";
|
|
22923
|
+
if (evidence.signInVisible) {
|
|
22924
|
+
state = "sign_in_required";
|
|
22925
|
+
needsAction = "sign_in_to_cursor";
|
|
22926
|
+
message = "Cursor is showing a sign-in surface instead of a writable Agent/Chat input.";
|
|
22927
|
+
} else if (evidence.settingsOrCustomizeVisible) {
|
|
22928
|
+
state = "settings_or_customize_open";
|
|
22929
|
+
needsAction = "complete_or_close_configuration";
|
|
22930
|
+
message = "Cursor is showing Customize/Settings instead of a writable Agent/Chat input.";
|
|
22931
|
+
} else if (evidence.modalVisible) {
|
|
22932
|
+
state = "modal_dialog_open";
|
|
22933
|
+
needsAction = "complete_or_close_dialog";
|
|
22934
|
+
message = "A visible Cursor dialog is blocking access to the Agent/Chat input.";
|
|
22935
|
+
} else if (evidence.probeError || ["hidden", "prerender", "unloaded"].includes(evidence.visibilityState)) {
|
|
22936
|
+
state = "cursor_window_unavailable";
|
|
22937
|
+
needsAction = "make_cursor_window_available";
|
|
22938
|
+
message = "The selected Cursor page is unavailable or not visible.";
|
|
22939
|
+
} else if (evidence.inputStateChanged) {
|
|
22940
|
+
state = "input_state_changed";
|
|
22941
|
+
needsAction = "open_new_chat";
|
|
22942
|
+
message = "Cursor's Agent/Chat input changed while the request was being prepared.";
|
|
22943
|
+
} else if (!evidence.agentSurfaceVisible && evidence.composerCount === 0) {
|
|
22944
|
+
state = "agent_chat_panel_not_open";
|
|
22945
|
+
needsAction = "open_agent_chat_panel";
|
|
22946
|
+
message = "Cursor's main Agent/Chat panel is not open.";
|
|
22947
|
+
}
|
|
22948
|
+
return {
|
|
22949
|
+
schemaVersion: 1,
|
|
22950
|
+
code: "CURSOR_CHAT_PANEL_UNAVAILABLE",
|
|
22951
|
+
state,
|
|
22952
|
+
message,
|
|
22953
|
+
needsAction,
|
|
22954
|
+
nextStep: CHAT_PANEL_NEXT_STEP,
|
|
22955
|
+
retryable: true,
|
|
22956
|
+
evidence
|
|
22957
|
+
};
|
|
22958
|
+
}
|
|
22959
|
+
function createChatPanelUnavailableError(snapshot) {
|
|
22960
|
+
const uiDiagnostic = classifyChatPanelDiagnostic(snapshot);
|
|
22961
|
+
const error2 = new Error(`${uiDiagnostic.code}: ${uiDiagnostic.message} ${uiDiagnostic.nextStep}`);
|
|
22962
|
+
error2.code = uiDiagnostic.code;
|
|
22963
|
+
error2.uiDiagnostic = uiDiagnostic;
|
|
22964
|
+
return error2;
|
|
22965
|
+
}
|
|
22780
22966
|
function exprFill(text) {
|
|
22781
22967
|
const js = JSON.stringify(text);
|
|
22782
22968
|
return `(function(){${INPUT_PICKER_BODY}const inp=pickInput();if(!inp)return 'NO_INPUT';inp.focus();try{const s=getSelection();const r=document.createRange();r.selectNodeContents(inp);s.removeAllRanges();s.addRange(r);}catch(e){}const ok=document.execCommand('insertText',false,${js});try{inp.dispatchEvent(new InputEvent('input',{bubbles:true,inputType:'insertText',data:${js}}));}catch(e){inp.dispatchEvent(new Event('input',{bubbles:true}));}return ok?(inp.innerText||'').slice(0,30):'EXEC_FAIL';})()`;
|
|
@@ -23024,7 +23210,7 @@ var WORKSPACE_SECTION_BODY = `
|
|
|
23024
23210
|
};
|
|
23025
23211
|
`;
|
|
23026
23212
|
function exprCreateAgentForWorkspace(projectPath) {
|
|
23027
|
-
const workspaceLabel = JSON.stringify(
|
|
23213
|
+
const workspaceLabel = JSON.stringify(basename6(String(projectPath || "")).trim().toLowerCase());
|
|
23028
23214
|
return `(function(){
|
|
23029
23215
|
${WORKSPACE_SECTION_BODY}
|
|
23030
23216
|
const wanted=${workspaceLabel};
|
|
@@ -23040,7 +23226,7 @@ function exprCreateAgentForWorkspace(projectPath) {
|
|
|
23040
23226
|
})()`;
|
|
23041
23227
|
}
|
|
23042
23228
|
function exprInspectWorkspaceRepository(projectPath) {
|
|
23043
|
-
const workspaceLabel = JSON.stringify(
|
|
23229
|
+
const workspaceLabel = JSON.stringify(basename6(String(projectPath || "")).trim().toLowerCase());
|
|
23044
23230
|
return `(function(){
|
|
23045
23231
|
${WORKSPACE_SECTION_BODY}
|
|
23046
23232
|
const wanted=${workspaceLabel};
|
|
@@ -23111,6 +23297,7 @@ var REACT_ADAPTER_BODY = `
|
|
|
23111
23297
|
const findV2Props=()=>{
|
|
23112
23298
|
const found=[]; const seen=new Set();
|
|
23113
23299
|
let globalSelectAgent=null;
|
|
23300
|
+
let globalSelectedAgentId=null;
|
|
23114
23301
|
let sectionIdByAgentId=null;
|
|
23115
23302
|
for(const root of document.querySelectorAll('.glass-sidebar-agent-list-container')){
|
|
23116
23303
|
const nodes=[]; let n=root;
|
|
@@ -23122,7 +23309,10 @@ var REACT_ADAPTER_BODY = `
|
|
|
23122
23309
|
let f=key.startsWith('__reactFiber$')?seed:{memoizedProps:seed,return:null};
|
|
23123
23310
|
for(let j=0;f&&j<80;j++,f=f.return){
|
|
23124
23311
|
for(const p of [f.memoizedProps,f.pendingProps,f.stateNode&&f.stateNode.props]){
|
|
23125
|
-
if(p&&typeof p.onSelectAgent==='function'&&!globalSelectAgent)
|
|
23312
|
+
if(p&&typeof p.onSelectAgent==='function'&&!globalSelectAgent){
|
|
23313
|
+
globalSelectAgent=p.onSelectAgent;
|
|
23314
|
+
globalSelectedAgentId=p.selectedAgentId!==undefined?p.selectedAgentId:p.committedSelectedAgentId;
|
|
23315
|
+
}
|
|
23126
23316
|
if(p&&p.sectionIdByAgentId&&!sectionIdByAgentId)sectionIdByAgentId=p.sectionIdByAgentId;
|
|
23127
23317
|
const selectAgent=p&&(typeof p.onSelectAgent==='function'
|
|
23128
23318
|
?p.onSelectAgent
|
|
@@ -23140,6 +23330,7 @@ var REACT_ADAPTER_BODY = `
|
|
|
23140
23330
|
}
|
|
23141
23331
|
}
|
|
23142
23332
|
found.globalSelectAgent=globalSelectAgent;
|
|
23333
|
+
found.globalSelectedAgentId=globalSelectedAgentId;
|
|
23143
23334
|
found.sectionIdByAgentId=sectionIdByAgentId;
|
|
23144
23335
|
return found;
|
|
23145
23336
|
};
|
|
@@ -23149,9 +23340,10 @@ var REACT_ADAPTER_BODY = `
|
|
|
23149
23340
|
return {
|
|
23150
23341
|
kind:'agents_v2',
|
|
23151
23342
|
entries:()=>{
|
|
23152
|
-
const entries=[];const seen=new Set();let index=0;
|
|
23343
|
+
const entries=[];const seen=new Set();let index=0;
|
|
23344
|
+
let selectedRaw=String(readScalar(v2.globalSelectedAgentId)||'').replace(/^local:/,'');
|
|
23153
23345
|
for(const p of v2){
|
|
23154
|
-
const selectedId=readScalar(p.selectedAgentId);
|
|
23346
|
+
const selectedId=readScalar(v2.globalSelectedAgentId)||readScalar(p.selectedAgentId);
|
|
23155
23347
|
if(!selectedRaw&&selectedId)selectedRaw=String(selectedId).replace(/^local:/,'');
|
|
23156
23348
|
for(const header of p.section.headers){
|
|
23157
23349
|
const entry=normalizeV2(header,selectedId,index++,p.section);
|
|
@@ -23197,15 +23389,18 @@ var REACT_ADAPTER_BODY = `
|
|
|
23197
23389
|
}
|
|
23198
23390
|
return entries;
|
|
23199
23391
|
},
|
|
23200
|
-
open:id=>{
|
|
23392
|
+
open:async id=>{
|
|
23201
23393
|
const raw=String(id||'').replace(/^local:/,'');
|
|
23394
|
+
if(String(readScalar(v2.globalSelectedAgentId)||'').replace(/^local:/,'')===raw)return true;
|
|
23202
23395
|
if(v2.some(p=>String(readScalar(p.selectedAgentId)||'').replace(/^local:/,'')===raw))return true;
|
|
23203
23396
|
for(const p of v2){
|
|
23204
23397
|
const header=p.section.headers.find(h=>String(readScalar(h&&h.id)||'')===raw);
|
|
23205
|
-
if(header){p.onSelectAgent(header);
|
|
23398
|
+
if(header){return (await p.onSelectAgent(header))!==false;}
|
|
23206
23399
|
}
|
|
23207
23400
|
const registered=v2.sectionIdByAgentId&&(v2.sectionIdByAgentId instanceof Map?v2.sectionIdByAgentId.has(raw):v2.sectionIdByAgentId[raw]!==undefined);
|
|
23208
|
-
if(registered&&typeof v2.globalSelectAgent==='function'){
|
|
23401
|
+
if(registered&&typeof v2.globalSelectAgent==='function'){
|
|
23402
|
+
return (await v2.globalSelectAgent(raw,{preserveSidebarAction:true}))!==false;
|
|
23403
|
+
}
|
|
23209
23404
|
return false;
|
|
23210
23405
|
}
|
|
23211
23406
|
};
|
|
@@ -23225,17 +23420,44 @@ var REACT_ADAPTER_BODY = `
|
|
|
23225
23420
|
const a=findAdapter();`;
|
|
23226
23421
|
var EXPR_AGENT_ADAPTER_READY = `(function(){${REACT_ADAPTER_BODY}return !!a;})()`;
|
|
23227
23422
|
var EXPR_PAGE_CAPABILITIES = `(function(){${INPUT_PICKER_BODY}
|
|
23423
|
+
const visible=node=>!!(node&&(node.offsetParent!==null||(typeof node.getClientRects==='function'&&node.getClientRects().length>0)));
|
|
23424
|
+
const label=node=>String(node&&(node.getAttribute&&(
|
|
23425
|
+
node.getAttribute('aria-label')||node.getAttribute('title'))||node.innerText||node.textContent)||'').replace(/\\s+/g,' ').trim();
|
|
23228
23426
|
const input=pickInput();
|
|
23427
|
+
const inputCandidateCount=[...document.querySelectorAll(${JSON.stringify(CURSOR_INPUT_SELECTOR)})].filter(visible).length;
|
|
23229
23428
|
const hasV2Sidebar=!!document.querySelector('.glass-sidebar-agent-list-container');
|
|
23230
23429
|
const hasLegacyInput=!!document.querySelector('.aislash-editor-input');
|
|
23231
23430
|
const hasLegacyHistory=!!document.querySelector('.compact-agent-history-react-menu-label')||
|
|
23232
23431
|
[...document.querySelectorAll('button,[role=button],a.action-label,.codicon')].some(e=>/Show Chat History|Chat History|Agent History/i.test((e.getAttribute('aria-label')||'')+' '+(e.getAttribute('title')||'')));
|
|
23432
|
+
const dialogs=[...document.querySelectorAll('[role="dialog"],dialog[open],.monaco-dialog-box,.quick-input-widget')].filter(visible);
|
|
23433
|
+
const dialog=dialogs[dialogs.length-1]||null;
|
|
23434
|
+
const dialogHeading=dialog&&dialog.querySelector('h1,h2,h3,[role="heading"]');
|
|
23435
|
+
const dialogLabel=dialog?String(dialog.getAttribute('aria-label')||dialog.getAttribute('title')||label(dialogHeading)||'').slice(0,120):null;
|
|
23436
|
+
const pageTitle=String(document.title||'').replace(/\\s+/g,' ').trim();
|
|
23437
|
+
const pagePath=String(globalThis.location&&globalThis.location.pathname||'');
|
|
23438
|
+
const headings=[...document.querySelectorAll('h1,h2,h3,[role="heading"]')]
|
|
23439
|
+
.filter(node=>visible(node)&&!node.closest('.markdown-root,.composer-messages-container,.composer-react-transcript-root,.aichat-container'))
|
|
23440
|
+
.map(label).filter(Boolean).slice(0,40);
|
|
23441
|
+
const configurationText=[pageTitle,pagePath,...headings,label(dialogHeading)].join(' ');
|
|
23442
|
+
const configurationSelector='.ui-customize-view,.settings-editor,.settings-body,.preferences-editor,[class*="settings-editor"],[class*="preferences-editor"],[data-testid*="customize"]';
|
|
23443
|
+
const settingsOrCustomizeVisible=[...document.querySelectorAll(configurationSelector)].some(visible)||/(?:settings?|customize|preferences|\u8BBE\u7F6E|\u8A2D\u5B9A|\u81EA\u5B9A\u4E49|\u81EA\u8A02)/i.test(configurationText);
|
|
23444
|
+
const authControls=[...document.querySelectorAll('button,a,[role="button"],[role="link"]')]
|
|
23445
|
+
.filter(node=>visible(node)&&!node.closest('.markdown-root,.composer-messages-container,.composer-react-transcript-root'));
|
|
23446
|
+
const signInPattern=/(?:sign|log)\\s*in(?:\\s+to\\s+cursor)?|authenticate(?:\\s+cursor)?|continue with (?:google|github|email|sso)|\u767B\u5F55|\u767B\u5165/i;
|
|
23447
|
+
const signInControlVisible=authControls.some(node=>signInPattern.test(label(node)));
|
|
23448
|
+
const signInVisible=authControls.some(node=>signInPattern.test(label(node))&&!node.closest(configurationSelector));
|
|
23449
|
+
const composerCount=[...document.querySelectorAll('.composer-bar[data-composer-id],.composer-bar,.ui-prompt-input-root')].filter(visible).length;
|
|
23450
|
+
const agentSurfaceVisible=composerCount>0||[...document.querySelectorAll('.glass-sidebar-agent-list-container,.compact-agent-history-react-menu-label,.aichat-container')].some(visible)||authControls.some(node=>/(?:New (?:Agent|Chat)|(?:Chat|Agent) History)/i.test(label(node)));
|
|
23233
23451
|
const uiFlavor=hasV2Sidebar||(input&&input.classList&&input.classList.contains('ui-prompt-input-editor__input'))?'agents_v2':hasLegacyInput?'legacy':'unknown';
|
|
23234
23452
|
return JSON.stringify({
|
|
23235
|
-
hasWritableInput:!!input,uiFlavor,
|
|
23453
|
+
hasWritableInput:!!input,inputCandidateCount,uiFlavor,
|
|
23236
23454
|
agentAdapterKind:hasV2Sidebar?'agents_v2':hasLegacyHistory?'legacy':'none',
|
|
23237
23455
|
hasComposer:!!document.querySelector('.composer-bar[data-composer-id]'),
|
|
23238
|
-
|
|
23456
|
+
modalVisible:!!dialog,modalLabel:dialogLabel,
|
|
23457
|
+
settingsOrCustomizeVisible,signInControlVisible,signInVisible,agentSurfaceVisible,composerCount,
|
|
23458
|
+
visibilityState:String(document.visibilityState||'unknown'),
|
|
23459
|
+
visible:document.visibilityState==='visible',focused:typeof document.hasFocus==='function'&&document.hasFocus(),
|
|
23460
|
+
documentTitle:pageTitle,pageTitle:pageTitle.slice(0,160)
|
|
23239
23461
|
});
|
|
23240
23462
|
})()`;
|
|
23241
23463
|
var EXPR_HISTORY_ENTRIES = `(function(){${REACT_ADAPTER_BODY}
|
|
@@ -23244,9 +23466,9 @@ var EXPR_HISTORY_ENTRIES = `(function(){${REACT_ADAPTER_BODY}
|
|
|
23244
23466
|
})()`;
|
|
23245
23467
|
function exprOpenAgent(agentId) {
|
|
23246
23468
|
const id = JSON.stringify(String(agentId));
|
|
23247
|
-
return `(function(){${REACT_ADAPTER_BODY}
|
|
23469
|
+
return `(async function(){${REACT_ADAPTER_BODY}
|
|
23248
23470
|
if(!a)return 'REACT_ADAPTER_UNAVAILABLE';
|
|
23249
|
-
return a.open(${id})?'OPENED':'AGENT_NOT_FOUND';
|
|
23471
|
+
return (await a.open(${id}))?'OPENED':'AGENT_NOT_FOUND';
|
|
23250
23472
|
})()`;
|
|
23251
23473
|
}
|
|
23252
23474
|
function normalizeAllowedPath(value) {
|
|
@@ -23434,16 +23656,16 @@ function lifecycleFailureSummary(lifecycle, fallback) {
|
|
|
23434
23656
|
return [lifecycle.message || fallback, `[${diagnostic}]`, original].filter(Boolean).join(" ");
|
|
23435
23657
|
}
|
|
23436
23658
|
function releaseAdapterWorkingDirectory({ targetDir = null, chdir = process.chdir } = {}) {
|
|
23437
|
-
const target = targetDir ? ensureLifecycleDir(targetDir) :
|
|
23659
|
+
const target = targetDir ? ensureLifecycleDir(targetDir) : dirname6(process.execPath);
|
|
23438
23660
|
chdir(target);
|
|
23439
23661
|
return target;
|
|
23440
23662
|
}
|
|
23441
23663
|
var CursorBridge = class {
|
|
23442
23664
|
constructor(options = {}) {
|
|
23443
|
-
this.adapterStartCwd =
|
|
23665
|
+
this.adapterStartCwd = resolve7(options.adapterStartCwd || process.cwd());
|
|
23444
23666
|
this.environmentDelegationMode = normalizeDelegationMode(options.delegationMode || DELEGATION_MODE);
|
|
23445
23667
|
this._syncDelegationState();
|
|
23446
|
-
this.runtimeFile = options.runtimeFile === null ? null :
|
|
23668
|
+
this.runtimeFile = options.runtimeFile === null ? null : resolve7(options.runtimeFile || resolveCursorRuntimeFile());
|
|
23447
23669
|
this.runtimeModeDefault = normalizeCursorRuntimeMode(
|
|
23448
23670
|
options.runtimeModeDefault || process.env.CURSOR_BRIDGE_RUNTIME_MODE,
|
|
23449
23671
|
"normal"
|
|
@@ -23454,14 +23676,18 @@ var CursorBridge = class {
|
|
|
23454
23676
|
this.runtimeMode = normalizeCursorRuntimeMode(requestedRuntimeMode);
|
|
23455
23677
|
this.runtimeModeSource = options.runtimeMode !== void 0 ? "constructor" : persistedRuntimeMode ? "persistent" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
23456
23678
|
this.runtimeModeScope = persistedRuntimeMode ? "persistent" : options.runtimeMode !== void 0 ? "constructor" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
23457
|
-
this.workspaceFile = options.workspaceFile === null ? null :
|
|
23679
|
+
this.workspaceFile = options.workspaceFile === null ? null : resolve7(options.workspaceFile || resolveWorkspaceBindingFile());
|
|
23458
23680
|
this.workspaceKey = options.workspaceKey || resolveWorkspaceBindingKey();
|
|
23459
23681
|
const persistedWorkspace = options.projectPath === void 0 ? readWorkspaceBinding(this.workspaceFile, this.workspaceKey) : null;
|
|
23460
|
-
this.projectPath = options.projectPath !== void 0 ?
|
|
23682
|
+
this.projectPath = options.projectPath !== void 0 ? resolve7(String(options.projectPath)) : persistedWorkspace && persistedWorkspace.projectPath || null;
|
|
23461
23683
|
this.workspaceSource = options.projectPath !== void 0 ? "constructor" : persistedWorkspace ? "persistent_init" : "auto_detect";
|
|
23462
23684
|
this.workspaceUpdatedAt = persistedWorkspace && persistedWorkspace.updatedAt || null;
|
|
23463
|
-
this.modelPreferencesFile = options.modelPreferencesFile === null ? null :
|
|
23685
|
+
this.modelPreferencesFile = options.modelPreferencesFile === null ? null : resolve7(options.modelPreferencesFile || resolveCursorModelPreferencesFile());
|
|
23464
23686
|
this.modelPreferences = readCursorModelPreferences(this.modelPreferencesFile);
|
|
23687
|
+
this.sessionFile = options.sessionFile === null ? null : resolve7(options.sessionFile || resolveCursorSessionRegistryFile());
|
|
23688
|
+
this.sessionInstanceId = String(
|
|
23689
|
+
options.sessionInstanceId || `cursor-adapter-${createCursorSessionId().slice("cursor-session-".length)}`
|
|
23690
|
+
);
|
|
23465
23691
|
this._lastPresentation = null;
|
|
23466
23692
|
this.busy = false;
|
|
23467
23693
|
this.queue = [];
|
|
@@ -23488,6 +23714,349 @@ var CursorBridge = class {
|
|
|
23488
23714
|
cursorUiPreferencePreserved: true
|
|
23489
23715
|
};
|
|
23490
23716
|
}
|
|
23717
|
+
sessionRegistryView() {
|
|
23718
|
+
return {
|
|
23719
|
+
sessionStorageFile: this.sessionFile,
|
|
23720
|
+
sessionStoragePersistent: !!this.sessionFile,
|
|
23721
|
+
sessionStorageLocation: this.sessionFile ? "user-config" : "disabled"
|
|
23722
|
+
};
|
|
23723
|
+
}
|
|
23724
|
+
_readSession(sessionId) {
|
|
23725
|
+
const id = String(sessionId || "").trim();
|
|
23726
|
+
if (!id) throw cursorSessionError("SESSION_REQUIRED", "session_id must not be empty");
|
|
23727
|
+
if (!this.sessionFile) throw cursorSessionError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
|
|
23728
|
+
return readCursorSessionRegistry(this.sessionFile).sessions[id] || null;
|
|
23729
|
+
}
|
|
23730
|
+
_sessionView(session) {
|
|
23731
|
+
if (!session) return null;
|
|
23732
|
+
return {
|
|
23733
|
+
sessionId: session.id,
|
|
23734
|
+
sessionState: session.state,
|
|
23735
|
+
projectPath: session.projectPath,
|
|
23736
|
+
workspaceKey: session.workspaceKey,
|
|
23737
|
+
agentId: session.agentId || null,
|
|
23738
|
+
agentLabel: session.agentLabel || null,
|
|
23739
|
+
turnIndex: Number(session.turnIndex || 0),
|
|
23740
|
+
activeTaskId: session.activeTaskId || null,
|
|
23741
|
+
lastTask: session.lastTask || null,
|
|
23742
|
+
modelPreference: session.modelPreference || null,
|
|
23743
|
+
readOnly: session.scopeEnvelope && session.scopeEnvelope.readOnly === true,
|
|
23744
|
+
allowedPaths: session.scopeEnvelope && Array.isArray(session.scopeEnvelope.allowedPaths) ? session.scopeEnvelope.allowedPaths : [],
|
|
23745
|
+
createdAt: session.createdAt || null,
|
|
23746
|
+
updatedAt: session.updatedAt || null,
|
|
23747
|
+
closedAt: session.closedAt || null,
|
|
23748
|
+
recoveryState: session.recoveryState || null,
|
|
23749
|
+
attention: session.attention || null
|
|
23750
|
+
};
|
|
23751
|
+
}
|
|
23752
|
+
_claimNewSession({ taskId, projectPath, readOnly, allowedPaths, modelPreference, requestId, timeoutMs }) {
|
|
23753
|
+
if (!this.sessionFile) throw cursorSessionError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
|
|
23754
|
+
const sessionId = createCursorSessionId();
|
|
23755
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23756
|
+
const session = {
|
|
23757
|
+
id: sessionId,
|
|
23758
|
+
state: "creating",
|
|
23759
|
+
projectPath,
|
|
23760
|
+
workspaceKey: this.workspaceKey,
|
|
23761
|
+
agentId: null,
|
|
23762
|
+
agentLabel: null,
|
|
23763
|
+
modelPreference: modelPreference ? { ...modelPreference } : null,
|
|
23764
|
+
scopeEnvelope: { readOnly, allowedPaths: [...allowedPaths] },
|
|
23765
|
+
turnIndex: 1,
|
|
23766
|
+
epoch: 1,
|
|
23767
|
+
activeTaskId: taskId,
|
|
23768
|
+
lastRequestId: requestId || null,
|
|
23769
|
+
lastTask: null,
|
|
23770
|
+
lease: {
|
|
23771
|
+
taskId,
|
|
23772
|
+
instanceId: this.sessionInstanceId,
|
|
23773
|
+
epoch: 1,
|
|
23774
|
+
expiresAt: new Date(Date.now() + timeoutMs + 6e4).toISOString()
|
|
23775
|
+
},
|
|
23776
|
+
createdAt: now,
|
|
23777
|
+
updatedAt: now,
|
|
23778
|
+
closedAt: null,
|
|
23779
|
+
recoveryState: null,
|
|
23780
|
+
attention: null
|
|
23781
|
+
};
|
|
23782
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23783
|
+
if (registry2.sessions[sessionId]) throw cursorSessionError("SESSION_ID_COLLISION", sessionId);
|
|
23784
|
+
registry2.sessions[sessionId] = session;
|
|
23785
|
+
}, { now });
|
|
23786
|
+
return session;
|
|
23787
|
+
}
|
|
23788
|
+
_claimExistingSession({ sessionId, taskId, projectPath, readOnly, allowedPaths, requestId, timeoutMs }) {
|
|
23789
|
+
if (!this.sessionFile) throw cursorSessionError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
|
|
23790
|
+
const nowMs = Date.now();
|
|
23791
|
+
let expiredLease = false;
|
|
23792
|
+
const result = updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23793
|
+
const session = registry2.sessions[sessionId];
|
|
23794
|
+
if (!session) throw cursorSessionError("SESSION_NOT_FOUND", sessionId);
|
|
23795
|
+
if (requestId && session.lastRequestId === requestId) {
|
|
23796
|
+
return { duplicate: true, session: { ...session } };
|
|
23797
|
+
}
|
|
23798
|
+
if (!sameSessionProject(session.projectPath, projectPath)) {
|
|
23799
|
+
throw cursorSessionError("SESSION_WORKSPACE_MISMATCH", `session is bound to ${session.projectPath}`);
|
|
23800
|
+
}
|
|
23801
|
+
if (session.state === "closed") throw cursorSessionError("SESSION_CLOSED", sessionId);
|
|
23802
|
+
const leaseExpiresAt = Date.parse(session.lease && session.lease.expiresAt || "");
|
|
23803
|
+
if (session.activeTaskId || session.state === "busy" || session.state === "creating") {
|
|
23804
|
+
if (Number.isFinite(leaseExpiresAt) && leaseExpiresAt <= nowMs) {
|
|
23805
|
+
expiredLease = true;
|
|
23806
|
+
session.state = "needs_attention";
|
|
23807
|
+
session.recoveryState = "expired_sender_lease";
|
|
23808
|
+
session.attention = "The previous sender lease expired. Reconcile the exact Cursor Agent before continuing; no prompt was resent.";
|
|
23809
|
+
session.updatedAt = new Date(nowMs).toISOString();
|
|
23810
|
+
return { duplicate: false, session: { ...session } };
|
|
23811
|
+
}
|
|
23812
|
+
throw cursorSessionError("SESSION_BUSY", `active task ${session.activeTaskId || "unknown"}`);
|
|
23813
|
+
}
|
|
23814
|
+
if (session.state !== "ready") {
|
|
23815
|
+
throw cursorSessionError("SESSION_NOT_READY", `state=${session.state}; recovery=${session.recoveryState || "none"}`);
|
|
23816
|
+
}
|
|
23817
|
+
if (!session.agentId) throw cursorSessionError("SESSION_AGENT_NOT_BOUND", sessionId);
|
|
23818
|
+
const envelope = session.scopeEnvelope || { readOnly: false, allowedPaths: [] };
|
|
23819
|
+
if (envelope.readOnly === true) {
|
|
23820
|
+
if (readOnly !== true || allowedPaths.length > 0) {
|
|
23821
|
+
throw cursorSessionError("SESSION_SCOPE_EXPANSION", "a read-only session must remain explicitly read-only");
|
|
23822
|
+
}
|
|
23823
|
+
} else {
|
|
23824
|
+
if (readOnly === true || allowedPaths.length === 0) {
|
|
23825
|
+
throw cursorSessionError("SESSION_SCOPE_REQUIRED", "a writable continuation must repeat an allowed_paths subset");
|
|
23826
|
+
}
|
|
23827
|
+
const outside = allowedPaths.find((candidate) => !(envelope.allowedPaths || []).some((parent) => sessionPathContains(parent, candidate)));
|
|
23828
|
+
if (outside) throw cursorSessionError("SESSION_SCOPE_EXPANSION", `${outside} is outside the session envelope`);
|
|
23829
|
+
}
|
|
23830
|
+
const epoch = Number(session.epoch || 0) + 1;
|
|
23831
|
+
session.state = "busy";
|
|
23832
|
+
session.turnIndex = Number(session.turnIndex || 0) + 1;
|
|
23833
|
+
session.epoch = epoch;
|
|
23834
|
+
session.activeTaskId = taskId;
|
|
23835
|
+
session.lastRequestId = requestId || null;
|
|
23836
|
+
session.lease = {
|
|
23837
|
+
taskId,
|
|
23838
|
+
instanceId: this.sessionInstanceId,
|
|
23839
|
+
epoch,
|
|
23840
|
+
expiresAt: new Date(nowMs + timeoutMs + 6e4).toISOString()
|
|
23841
|
+
};
|
|
23842
|
+
session.updatedAt = new Date(nowMs).toISOString();
|
|
23843
|
+
session.recoveryState = null;
|
|
23844
|
+
session.attention = null;
|
|
23845
|
+
return { duplicate: false, session: { ...session } };
|
|
23846
|
+
}, { now: new Date(nowMs).toISOString() });
|
|
23847
|
+
if (expiredLease) {
|
|
23848
|
+
throw cursorSessionError("SESSION_RECONCILE_REQUIRED", "the previous sender lease expired; no prompt was sent");
|
|
23849
|
+
}
|
|
23850
|
+
return result;
|
|
23851
|
+
}
|
|
23852
|
+
_bindSessionAgent(job) {
|
|
23853
|
+
if (!job || !job.sessionId || !job.agentId || !this.sessionFile) return;
|
|
23854
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23855
|
+
const session = registry2.sessions[job.sessionId];
|
|
23856
|
+
if (!session || session.activeTaskId !== job.id || Number(session.epoch) !== Number(job.sessionEpoch)) return;
|
|
23857
|
+
session.agentId = job.agentId;
|
|
23858
|
+
session.agentLabel = job.agentLabel || session.agentLabel || null;
|
|
23859
|
+
session.state = "busy";
|
|
23860
|
+
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23861
|
+
});
|
|
23862
|
+
job.sessionState = "busy";
|
|
23863
|
+
}
|
|
23864
|
+
_settleSessionJob(job, outcome, options = {}) {
|
|
23865
|
+
if (!job || !job.sessionId || !this.sessionFile) return;
|
|
23866
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23867
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23868
|
+
const session = registry2.sessions[job.sessionId];
|
|
23869
|
+
if (!session || session.activeTaskId !== job.id || Number(session.epoch) !== Number(job.sessionEpoch)) return;
|
|
23870
|
+
if (job.agentId) session.agentId = job.agentId;
|
|
23871
|
+
if (job.agentLabel) session.agentLabel = job.agentLabel;
|
|
23872
|
+
session.lastTask = {
|
|
23873
|
+
taskId: job.id,
|
|
23874
|
+
turnIndex: job.sessionTurn,
|
|
23875
|
+
status: outcome,
|
|
23876
|
+
finishedAt: job.finishedAt || now,
|
|
23877
|
+
error: job.error || null,
|
|
23878
|
+
uiDiagnostic: job.uiDiagnostic || null,
|
|
23879
|
+
terminalEvidence: job.terminalEvidence || null,
|
|
23880
|
+
resultUnavailable: job.resultUnavailable === true
|
|
23881
|
+
};
|
|
23882
|
+
session.updatedAt = now;
|
|
23883
|
+
if (options.needsAttention === true) {
|
|
23884
|
+
session.state = "needs_attention";
|
|
23885
|
+
session.recoveryState = job.recoveryState || outcome;
|
|
23886
|
+
session.attention = options.attention || job.error || "The exact Cursor Agent requires reconciliation before another turn.";
|
|
23887
|
+
return;
|
|
23888
|
+
}
|
|
23889
|
+
session.activeTaskId = null;
|
|
23890
|
+
session.lease = null;
|
|
23891
|
+
session.recoveryState = null;
|
|
23892
|
+
session.attention = null;
|
|
23893
|
+
session.state = session.agentId ? "ready" : "failed";
|
|
23894
|
+
}, { now });
|
|
23895
|
+
job.sessionState = options.needsAttention === true ? "needs_attention" : job.agentId ? "ready" : "failed";
|
|
23896
|
+
}
|
|
23897
|
+
_safeSettleSessionJob(job, outcome, options = {}) {
|
|
23898
|
+
try {
|
|
23899
|
+
this._settleSessionJob(job, outcome, options);
|
|
23900
|
+
} catch (error2) {
|
|
23901
|
+
job.sessionState = "needs_attention";
|
|
23902
|
+
job.sessionError = error2 instanceof Error ? error2.message : String(error2);
|
|
23903
|
+
}
|
|
23904
|
+
}
|
|
23905
|
+
sessionStatus(sessionId) {
|
|
23906
|
+
const session = this._readSession(sessionId);
|
|
23907
|
+
return {
|
|
23908
|
+
found: !!session,
|
|
23909
|
+
...this.sessionRegistryView(),
|
|
23910
|
+
...session ? this._sessionView(session) : { sessionId: String(sessionId || "") }
|
|
23911
|
+
};
|
|
23912
|
+
}
|
|
23913
|
+
async _reconcileSession(sessionId) {
|
|
23914
|
+
const initial = this._readSession(sessionId);
|
|
23915
|
+
if (!initial) return { found: false, action: "reconcile", sessionId };
|
|
23916
|
+
if (initial.state === "ready" || initial.state === "closed" || initial.state === "failed") {
|
|
23917
|
+
return { found: true, changed: false, action: "reconcile", ...this._sessionView(initial) };
|
|
23918
|
+
}
|
|
23919
|
+
if (!initial.agentId) {
|
|
23920
|
+
return {
|
|
23921
|
+
found: true,
|
|
23922
|
+
changed: false,
|
|
23923
|
+
action: "reconcile",
|
|
23924
|
+
state: "agent_missing",
|
|
23925
|
+
attention: "No exact agentId is available. Confirm the Cursor task state manually before explicit abandon.",
|
|
23926
|
+
...this._sessionView(initial)
|
|
23927
|
+
};
|
|
23928
|
+
}
|
|
23929
|
+
const projectPath = this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath || null;
|
|
23930
|
+
if (!sameSessionProject(initial.projectPath, projectPath)) {
|
|
23931
|
+
throw cursorSessionError("SESSION_WORKSPACE_MISMATCH", `session is bound to ${initial.projectPath}`);
|
|
23932
|
+
}
|
|
23933
|
+
await this._ensureCursor();
|
|
23934
|
+
const probe = {
|
|
23935
|
+
agentId: initial.agentId,
|
|
23936
|
+
targetId: this._lastLifecycle && this._lastLifecycle.targetId,
|
|
23937
|
+
execution: "parallel_agent",
|
|
23938
|
+
effectiveExecution: "parallel_agent",
|
|
23939
|
+
lastRecoveryAt: null,
|
|
23940
|
+
recoveryState: null,
|
|
23941
|
+
error: null
|
|
23942
|
+
};
|
|
23943
|
+
const observed = await this._readStableParallelEntry(probe);
|
|
23944
|
+
let result;
|
|
23945
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23946
|
+
const session = registry2.sessions[sessionId];
|
|
23947
|
+
if (!session) {
|
|
23948
|
+
result = { found: false, action: "reconcile", sessionId };
|
|
23949
|
+
return;
|
|
23950
|
+
}
|
|
23951
|
+
if (Number(session.epoch || 0) !== Number(initial.epoch || 0) || session.activeTaskId !== initial.activeTaskId) {
|
|
23952
|
+
result = { found: true, changed: false, action: "reconcile", state: "stale_observation", ...this._sessionView(session) };
|
|
23953
|
+
return;
|
|
23954
|
+
}
|
|
23955
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23956
|
+
if (!observed.stable || !observed.entry) {
|
|
23957
|
+
session.state = "needs_attention";
|
|
23958
|
+
session.recoveryState = probe.recoveryState || "history_unavailable";
|
|
23959
|
+
session.attention = observed.error || "The exact Agent state is not stable.";
|
|
23960
|
+
session.updatedAt = now;
|
|
23961
|
+
result = { found: true, changed: false, action: "reconcile", state: session.recoveryState, ...this._sessionView(session) };
|
|
23962
|
+
return;
|
|
23963
|
+
}
|
|
23964
|
+
const entry = observed.entry;
|
|
23965
|
+
if (entry.showSpinner) {
|
|
23966
|
+
session.state = "needs_attention";
|
|
23967
|
+
session.recoveryState = "running_unowned";
|
|
23968
|
+
session.attention = "The exact Cursor Agent is still running without an attached adapter. Do not submit another turn.";
|
|
23969
|
+
session.updatedAt = now;
|
|
23970
|
+
result = { found: true, changed: false, action: "reconcile", state: "running_unowned", ...this._sessionView(session) };
|
|
23971
|
+
return;
|
|
23972
|
+
}
|
|
23973
|
+
const terminalClass = classifyParallelTerminalIcon(entry.icon);
|
|
23974
|
+
if (!["completed", "cancelled", "failed"].includes(terminalClass)) {
|
|
23975
|
+
session.state = "needs_attention";
|
|
23976
|
+
session.recoveryState = terminalClass === "needs_attention" ? "cursor_needs_attention" : "idle_unconfirmed";
|
|
23977
|
+
session.attention = `The exact Cursor Agent has no confirmed terminal state: ${entry.icon || "unknown"}`;
|
|
23978
|
+
session.updatedAt = now;
|
|
23979
|
+
result = { found: true, changed: false, action: "reconcile", state: session.recoveryState, ...this._sessionView(session) };
|
|
23980
|
+
return;
|
|
23981
|
+
}
|
|
23982
|
+
session.lastTask = {
|
|
23983
|
+
taskId: session.activeTaskId || session.lastTask && session.lastTask.taskId || null,
|
|
23984
|
+
turnIndex: session.turnIndex,
|
|
23985
|
+
status: terminalClass,
|
|
23986
|
+
finishedAt: now,
|
|
23987
|
+
error: terminalClass === "failed" ? `stable_history_icon:${entry.icon}` : null,
|
|
23988
|
+
terminalEvidence: `stable_history_icon:${entry.icon}`,
|
|
23989
|
+
resultUnavailable: terminalClass === "completed"
|
|
23990
|
+
};
|
|
23991
|
+
session.activeTaskId = null;
|
|
23992
|
+
session.lease = null;
|
|
23993
|
+
session.state = "ready";
|
|
23994
|
+
session.recoveryState = terminalClass === "completed" ? "reconciled_result_uncollected" : null;
|
|
23995
|
+
session.attention = terminalClass === "completed" ? "The interrupted turn completed in Cursor, but its reply was not persisted. Inspect that Agent before the next turn." : null;
|
|
23996
|
+
session.updatedAt = now;
|
|
23997
|
+
result = { found: true, changed: true, action: "reconcile", state: terminalClass, ...this._sessionView(session) };
|
|
23998
|
+
});
|
|
23999
|
+
return result;
|
|
24000
|
+
}
|
|
24001
|
+
async sessionControl(sessionId, options = {}) {
|
|
24002
|
+
const id = String(sessionId || "").trim();
|
|
24003
|
+
if (!id) throw cursorSessionError("SESSION_REQUIRED", "session_id must not be empty");
|
|
24004
|
+
const action = String(options.action || "").trim().toLowerCase();
|
|
24005
|
+
if (!["reconcile", "close", "forget", "abandon"].includes(action)) {
|
|
24006
|
+
throw cursorSessionError("SESSION_ACTION_UNSUPPORTED", "expected reconcile, close, forget, or abandon");
|
|
24007
|
+
}
|
|
24008
|
+
if (action === "reconcile") return this._reconcileSession(id);
|
|
24009
|
+
let result;
|
|
24010
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
24011
|
+
const session = registry2.sessions[id];
|
|
24012
|
+
if (!session) {
|
|
24013
|
+
result = { found: false, sessionId: id, action };
|
|
24014
|
+
return;
|
|
24015
|
+
}
|
|
24016
|
+
if (action === "forget") {
|
|
24017
|
+
if (options.confirm !== true) throw cursorSessionError("SESSION_CONFIRM_REQUIRED", "forget requires confirm=true");
|
|
24018
|
+
if (session.state !== "closed") throw cursorSessionError("SESSION_NOT_CLOSED", "close the session before forgetting it");
|
|
24019
|
+
delete registry2.sessions[id];
|
|
24020
|
+
result = { found: true, changed: true, action, sessionId: id, state: "forgotten" };
|
|
24021
|
+
return;
|
|
24022
|
+
}
|
|
24023
|
+
if (action === "abandon") {
|
|
24024
|
+
if (options.confirm !== true || options.acknowledgeMayStillWrite !== true || !String(options.reason || "").trim()) {
|
|
24025
|
+
throw cursorSessionError("SESSION_ABANDON_CONFIRM_REQUIRED", "abandon requires confirm=true, acknowledge_may_still_write=true, and a non-empty reason");
|
|
24026
|
+
}
|
|
24027
|
+
session.lastTask = {
|
|
24028
|
+
taskId: session.activeTaskId || session.lastTask && session.lastTask.taskId || null,
|
|
24029
|
+
turnIndex: session.turnIndex,
|
|
24030
|
+
status: "abandoned",
|
|
24031
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24032
|
+
error: String(options.reason).trim(),
|
|
24033
|
+
terminalEvidence: "explicit_session_abandon_acknowledgement",
|
|
24034
|
+
resultUnavailable: true
|
|
24035
|
+
};
|
|
24036
|
+
session.activeTaskId = null;
|
|
24037
|
+
session.lease = null;
|
|
24038
|
+
session.state = "closed";
|
|
24039
|
+
session.closedAt = session.lastTask.finishedAt;
|
|
24040
|
+
session.updatedAt = session.closedAt;
|
|
24041
|
+
session.recoveryState = "released_unconfirmed";
|
|
24042
|
+
session.attention = "The Bridge mapping was closed without proving that the Cursor Agent stopped; it may still run or write.";
|
|
24043
|
+
result = { found: true, changed: true, action, warning: session.attention, ...this._sessionView(session) };
|
|
24044
|
+
return;
|
|
24045
|
+
}
|
|
24046
|
+
if (session.activeTaskId || ["busy", "creating", "needs_attention"].includes(session.state)) {
|
|
24047
|
+
throw cursorSessionError("SESSION_CLOSE_BLOCKED", "an active or uncertain Agent must be resolved before close");
|
|
24048
|
+
}
|
|
24049
|
+
if (session.state === "closed") {
|
|
24050
|
+
result = { found: true, changed: false, action, ...this._sessionView(session) };
|
|
24051
|
+
return;
|
|
24052
|
+
}
|
|
24053
|
+
session.state = "closed";
|
|
24054
|
+
session.closedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24055
|
+
session.updatedAt = session.closedAt;
|
|
24056
|
+
result = { found: true, changed: true, action, ...this._sessionView(session) };
|
|
24057
|
+
});
|
|
24058
|
+
return result;
|
|
24059
|
+
}
|
|
23491
24060
|
async initializeWorkspace(projectPath) {
|
|
23492
24061
|
if (this.busy || this.activeParallel.size > 0 || this.queue.length > 0) {
|
|
23493
24062
|
throw new Error("cursor_init cannot change workspace while Cursor tasks are queued or running");
|
|
@@ -23719,11 +24288,28 @@ var CursorBridge = class {
|
|
|
23719
24288
|
if (this._hasGlobalReservation()) {
|
|
23720
24289
|
throw new Error("A global Cursor reservation has an unconfirmed Stop state; no new task may be submitted until it is explicitly recovered or released");
|
|
23721
24290
|
}
|
|
23722
|
-
|
|
23723
|
-
|
|
24291
|
+
const sessionMode = normalizeCursorSessionMode(options.sessionMode, "isolated");
|
|
24292
|
+
if (!sessionMode) {
|
|
24293
|
+
throw cursorSessionError("SESSION_MODE_INVALID", `expected ${CURSOR_SESSION_MODES.join(", ")}`);
|
|
24294
|
+
}
|
|
24295
|
+
const sessionId = String(options.sessionId || "").trim();
|
|
24296
|
+
if (sessionMode === "continue" && !sessionId) {
|
|
24297
|
+
throw cursorSessionError("SESSION_REQUIRED", "session_mode=continue requires session_id");
|
|
24298
|
+
}
|
|
24299
|
+
if (sessionMode !== "continue" && sessionId) {
|
|
24300
|
+
throw cursorSessionError("SESSION_ID_UNEXPECTED", "session_id is accepted only with session_mode=continue");
|
|
24301
|
+
}
|
|
24302
|
+
const requestId = normalizeSessionRequestId(options.requestId);
|
|
24303
|
+
if (sessionMode === "isolated" && requestId) {
|
|
24304
|
+
throw cursorSessionError("SESSION_REQUEST_ID_UNEXPECTED", "request_id is accepted only for a persistent session turn");
|
|
24305
|
+
}
|
|
24306
|
+
const execution = String(options.execution || (sessionMode === "isolated" ? "fifo" : "parallel_agent"));
|
|
23724
24307
|
if (execution !== "fifo" && execution !== "parallel_agent") {
|
|
23725
24308
|
throw new Error(`Unsupported execution value ${execution}; expected fifo or parallel_agent`);
|
|
23726
24309
|
}
|
|
24310
|
+
if (sessionMode !== "isolated" && execution !== "parallel_agent") {
|
|
24311
|
+
throw cursorSessionError("SESSION_EXECUTION_INVALID", "persistent sessions require execution=parallel_agent and never fall back to FIFO");
|
|
24312
|
+
}
|
|
23727
24313
|
const readOnly = options.readOnly === true;
|
|
23728
24314
|
const timeoutMs = Math.max(3e4, Math.min(9e5, Number(options.timeoutMs || 6e5)));
|
|
23729
24315
|
const allowedPaths = Array.isArray(options.allowedPaths) ? options.allowedPaths.map((x) => String(x).trim()).filter(Boolean) : [];
|
|
@@ -23737,6 +24323,14 @@ var CursorBridge = class {
|
|
|
23737
24323
|
this._validateParallelAllowedPaths(allowedPaths);
|
|
23738
24324
|
this._assertNoParallelPathConflict(allowedPaths);
|
|
23739
24325
|
}
|
|
24326
|
+
if (sessionMode === "continue" && options.readOnlySpecified !== true && options.allowedPathsSpecified !== true) {
|
|
24327
|
+
throw cursorSessionError("SESSION_SCOPE_REQUIRED", "repeat read_only=true or an allowed_paths subset for every continued turn");
|
|
24328
|
+
}
|
|
24329
|
+
await this._ensureCursor();
|
|
24330
|
+
const projectPath = this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath || null;
|
|
24331
|
+
if (sessionMode !== "isolated" && !projectPath) {
|
|
24332
|
+
throw cursorSessionError("SESSION_WORKSPACE_REQUIRED", "initialize one workspace before creating or continuing a session");
|
|
24333
|
+
}
|
|
23740
24334
|
const contract = String(options.completionContract || "").trim();
|
|
23741
24335
|
let fullPrompt = text + DO_LANGUAGE_CONTRACT;
|
|
23742
24336
|
if (readOnly) fullPrompt += "\n\nRead-only boundary: Do not modify, create, or delete files, and do not run commands that change workspace state.";
|
|
@@ -23744,14 +24338,56 @@ var CursorBridge = class {
|
|
|
23744
24338
|
fullPrompt += "\n\nAllowed modification scope (do not cross this boundary):\n" + allowedPaths.map((x) => "- " + x).join("\n");
|
|
23745
24339
|
}
|
|
23746
24340
|
fullPrompt += contract ? "\n\nAcceptance and reporting contract:\n" + contract : DO_DEFAULT_CONTRACT;
|
|
24341
|
+
const taskId = this._nextTaskId();
|
|
24342
|
+
let session = null;
|
|
24343
|
+
let duplicate = false;
|
|
24344
|
+
let modelPreference = this._modelPreferenceFor("cursor_do");
|
|
24345
|
+
if (sessionMode === "create") {
|
|
24346
|
+
session = this._claimNewSession({
|
|
24347
|
+
taskId,
|
|
24348
|
+
projectPath,
|
|
24349
|
+
readOnly,
|
|
24350
|
+
allowedPaths,
|
|
24351
|
+
modelPreference,
|
|
24352
|
+
requestId,
|
|
24353
|
+
timeoutMs
|
|
24354
|
+
});
|
|
24355
|
+
} else if (sessionMode === "continue") {
|
|
24356
|
+
const claimed = this._claimExistingSession({
|
|
24357
|
+
sessionId,
|
|
24358
|
+
taskId,
|
|
24359
|
+
projectPath,
|
|
24360
|
+
readOnly,
|
|
24361
|
+
allowedPaths,
|
|
24362
|
+
requestId,
|
|
24363
|
+
timeoutMs
|
|
24364
|
+
});
|
|
24365
|
+
session = claimed.session;
|
|
24366
|
+
duplicate = claimed.duplicate === true;
|
|
24367
|
+
modelPreference = session.modelPreference || null;
|
|
24368
|
+
if (duplicate) {
|
|
24369
|
+
const existing = this.tasks.get(session.activeTaskId || session.lastTask && session.lastTask.taskId || "");
|
|
24370
|
+
return existing ? { duplicate: true, ...this._taskView(existing, true) } : { duplicate: true, ...this._sessionView(session) };
|
|
24371
|
+
}
|
|
24372
|
+
}
|
|
23747
24373
|
const job = this._enqueue("do", fullPrompt, {
|
|
24374
|
+
taskId,
|
|
23748
24375
|
timeoutMs,
|
|
23749
|
-
newChat:
|
|
24376
|
+
newChat: sessionMode !== "continue",
|
|
23750
24377
|
execution,
|
|
23751
24378
|
readOnly,
|
|
23752
24379
|
allowedPaths,
|
|
23753
24380
|
preferLegacyUi: options.preferLegacyUi === true,
|
|
23754
|
-
modelPreference
|
|
24381
|
+
modelPreference,
|
|
24382
|
+
projectPath,
|
|
24383
|
+
sessionMode,
|
|
24384
|
+
sessionId: session && session.id || null,
|
|
24385
|
+
sessionTurn: session && session.turnIndex || null,
|
|
24386
|
+
sessionEpoch: session && session.epoch || null,
|
|
24387
|
+
sessionState: session && session.state || null,
|
|
24388
|
+
requestId,
|
|
24389
|
+
agentId: sessionMode === "continue" ? session && session.agentId : null,
|
|
24390
|
+
agentLabel: sessionMode === "continue" ? session && session.agentLabel : null
|
|
23755
24391
|
});
|
|
23756
24392
|
if (options.background !== false) return this._taskView(job);
|
|
23757
24393
|
await job.promise;
|
|
@@ -23780,12 +24416,15 @@ var CursorBridge = class {
|
|
|
23780
24416
|
}
|
|
23781
24417
|
}
|
|
23782
24418
|
}
|
|
24419
|
+
_nextTaskId() {
|
|
24420
|
+
return `cursor-${Date.now().toString(36)}-${this.nextTaskId++}`;
|
|
24421
|
+
}
|
|
23783
24422
|
_enqueue(kind, prompt, options) {
|
|
23784
|
-
const id =
|
|
24423
|
+
const id = options.taskId || this._nextTaskId();
|
|
23785
24424
|
let resolvePromise;
|
|
23786
24425
|
let rejectPromise;
|
|
23787
|
-
const promise = new Promise((
|
|
23788
|
-
resolvePromise =
|
|
24426
|
+
const promise = new Promise((resolve8, reject) => {
|
|
24427
|
+
resolvePromise = resolve8;
|
|
23789
24428
|
rejectPromise = reject;
|
|
23790
24429
|
});
|
|
23791
24430
|
promise.catch(() => {
|
|
@@ -23804,15 +24443,22 @@ var CursorBridge = class {
|
|
|
23804
24443
|
modelPreference: options.modelPreference ? { ...options.modelPreference } : null,
|
|
23805
24444
|
modelSelection: options.modelPreference ? { configured: true, applied: false, ...options.modelPreference } : null,
|
|
23806
24445
|
projectPath: options.projectPath || this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath || null,
|
|
24446
|
+
sessionMode: options.sessionMode || "isolated",
|
|
24447
|
+
sessionId: options.sessionId || null,
|
|
24448
|
+
sessionTurn: options.sessionTurn || null,
|
|
24449
|
+
sessionEpoch: options.sessionEpoch || null,
|
|
24450
|
+
sessionState: options.sessionState || null,
|
|
24451
|
+
requestId: options.requestId || null,
|
|
24452
|
+
responseBaseline: null,
|
|
23807
24453
|
status: "queued",
|
|
23808
24454
|
phase: "queued",
|
|
23809
24455
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23810
24456
|
startedAt: null,
|
|
23811
24457
|
finishedAt: null,
|
|
23812
24458
|
sentAt: null,
|
|
23813
|
-
agentId: null,
|
|
24459
|
+
agentId: options.agentId || null,
|
|
23814
24460
|
provisionalAgentId: null,
|
|
23815
|
-
agentLabel: null,
|
|
24461
|
+
agentLabel: options.agentLabel || null,
|
|
23816
24462
|
targetId: null,
|
|
23817
24463
|
targetUiFlavor: null,
|
|
23818
24464
|
fallbackReason: null,
|
|
@@ -23830,6 +24476,7 @@ var CursorBridge = class {
|
|
|
23830
24476
|
resultUnavailable: false,
|
|
23831
24477
|
terminalEvidence: null,
|
|
23832
24478
|
providerError: null,
|
|
24479
|
+
uiDiagnostic: null,
|
|
23833
24480
|
sendState: "not_sent",
|
|
23834
24481
|
reservationScope: null,
|
|
23835
24482
|
controlTail: Promise.resolve(),
|
|
@@ -23853,6 +24500,7 @@ var CursorBridge = class {
|
|
|
23853
24500
|
job.recoveryState = null;
|
|
23854
24501
|
job.cancelRequested = false;
|
|
23855
24502
|
job.resultUnavailable = false;
|
|
24503
|
+
this._safeSettleSessionJob(job, "completed");
|
|
23856
24504
|
if (!job.settled) {
|
|
23857
24505
|
job.settled = true;
|
|
23858
24506
|
job.resolve(result);
|
|
@@ -23863,6 +24511,7 @@ var CursorBridge = class {
|
|
|
23863
24511
|
const e = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
23864
24512
|
job.error = e.message;
|
|
23865
24513
|
if (e.providerError) job.providerError = e.providerError;
|
|
24514
|
+
if (e.uiDiagnostic) job.uiDiagnostic = e.uiDiagnostic;
|
|
23866
24515
|
if (e.terminalEvidence) job.terminalEvidence = e.terminalEvidence;
|
|
23867
24516
|
job.status = "failed";
|
|
23868
24517
|
job.phase = "failed";
|
|
@@ -23870,6 +24519,7 @@ var CursorBridge = class {
|
|
|
23870
24519
|
job.recoveryState = null;
|
|
23871
24520
|
job.cancelRequested = false;
|
|
23872
24521
|
job.reservationScope = null;
|
|
24522
|
+
this._safeSettleSessionJob(job, "failed");
|
|
23873
24523
|
if (!job.settled) {
|
|
23874
24524
|
job.settled = true;
|
|
23875
24525
|
job.reject(e);
|
|
@@ -23890,6 +24540,10 @@ var CursorBridge = class {
|
|
|
23890
24540
|
job.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23891
24541
|
job.recoveryState = options.underlyingStopConfirmed === true ? "stopped" : "released_unconfirmed";
|
|
23892
24542
|
job.reservationScope = null;
|
|
24543
|
+
this._safeSettleSessionJob(job, "cancelled", {
|
|
24544
|
+
needsAttention: options.underlyingStopConfirmed !== true && job.sendState === "sent",
|
|
24545
|
+
attention: "Cancellation did not prove that the exact Cursor Agent stopped."
|
|
24546
|
+
});
|
|
23893
24547
|
if (!job.settled) {
|
|
23894
24548
|
job.settled = true;
|
|
23895
24549
|
job.reject(new Error(message));
|
|
@@ -23909,6 +24563,7 @@ var CursorBridge = class {
|
|
|
23909
24563
|
job.recoveryState = "unverified";
|
|
23910
24564
|
job.reservationScope = job.execution === "parallel_agent" && job.agentId ? job.readOnly ? "agent" : "paths" : "global";
|
|
23911
24565
|
this.activeParallel.set(job.id, job);
|
|
24566
|
+
this._safeSettleSessionJob(job, "needs_attention", { needsAttention: true });
|
|
23912
24567
|
if (!job.settled) {
|
|
23913
24568
|
job.settled = true;
|
|
23914
24569
|
job.reject(e);
|
|
@@ -24027,8 +24682,14 @@ var CursorBridge = class {
|
|
|
24027
24682
|
try {
|
|
24028
24683
|
if (job.effectiveExecution === "parallel_agent") {
|
|
24029
24684
|
job.phase = "submitting";
|
|
24030
|
-
const submitted = await this._withUiLock(() => this._submitParallelAgent(job));
|
|
24685
|
+
const submitted = await this._withUiLock(() => job.sessionMode === "continue" ? this._submitSessionTurn(job) : this._submitParallelAgent(job));
|
|
24031
24686
|
if (submitted.fallbackReason) {
|
|
24687
|
+
if (job.sessionMode !== "isolated") {
|
|
24688
|
+
throw cursorSessionError(
|
|
24689
|
+
"SESSION_CONTINUITY_UNAVAILABLE",
|
|
24690
|
+
`${submitted.fallbackReason}; persistent sessions never fall back to FIFO`
|
|
24691
|
+
);
|
|
24692
|
+
}
|
|
24032
24693
|
job.effectiveExecution = "fifo";
|
|
24033
24694
|
job.fallbackReason = submitted.fallbackReason;
|
|
24034
24695
|
if (this.activeParallel.size > 0) {
|
|
@@ -24043,10 +24704,16 @@ var CursorBridge = class {
|
|
|
24043
24704
|
} else {
|
|
24044
24705
|
job.agentId = submitted.agent.id;
|
|
24045
24706
|
job.agentLabel = submitted.agent.label || null;
|
|
24707
|
+
try {
|
|
24708
|
+
this._bindSessionAgent(job);
|
|
24709
|
+
} catch (error2) {
|
|
24710
|
+
if (job.sendState === "sent" || job.sentAt) error2.sent = true;
|
|
24711
|
+
throw error2;
|
|
24712
|
+
}
|
|
24046
24713
|
job.sentAt = job.sentAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
24047
24714
|
job.phase = "running";
|
|
24048
24715
|
job.reservationScope = job.readOnly ? "agent" : "paths";
|
|
24049
|
-
if (!this.parallelRestoreAgentId && submitted.previousSelectedId) {
|
|
24716
|
+
if (shouldScheduleParallelOriginRestore(job) && !this.parallelRestoreAgentId && submitted.previousSelectedId) {
|
|
24050
24717
|
this.parallelRestoreAgentId = submitted.previousSelectedId;
|
|
24051
24718
|
this.parallelRestoreTargetId = job.targetId;
|
|
24052
24719
|
}
|
|
@@ -24155,6 +24822,7 @@ var CursorBridge = class {
|
|
|
24155
24822
|
await this._applyModelPreference(c, options.modelPreference, options);
|
|
24156
24823
|
this._throwIfCancelledBeforeSend(options);
|
|
24157
24824
|
const filled = await evalJS(c, exprFill(prompt));
|
|
24825
|
+
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
24158
24826
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") throw new Error("Failed to enter the query because the input state was invalid");
|
|
24159
24827
|
await sleep2(450);
|
|
24160
24828
|
this._throwIfCancelledBeforeSend(options);
|
|
@@ -24362,7 +25030,7 @@ var CursorBridge = class {
|
|
|
24362
25030
|
applied: true,
|
|
24363
25031
|
requestedModel,
|
|
24364
25032
|
requestedEffort,
|
|
24365
|
-
effectiveModel:
|
|
25033
|
+
effectiveModel: modelRow.text || trigger.text,
|
|
24366
25034
|
effectiveEffort,
|
|
24367
25035
|
pickerDetail: trigger.detail || null,
|
|
24368
25036
|
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -24370,19 +25038,24 @@ var CursorBridge = class {
|
|
|
24370
25038
|
if (job) job.modelSelection = result;
|
|
24371
25039
|
return result;
|
|
24372
25040
|
}
|
|
24373
|
-
async
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
vis = await evalJS(c, EXPR_VISIBLE);
|
|
24379
|
-
}
|
|
24380
|
-
if (!vis) {
|
|
24381
|
-
await chord(c, 2, "I", "KeyI", 73);
|
|
24382
|
-
await sleep2(1300);
|
|
24383
|
-
vis = await evalJS(c, EXPR_VISIBLE);
|
|
25041
|
+
async _readChatPanelDiagnostic(c) {
|
|
25042
|
+
try {
|
|
25043
|
+
return JSON.parse(await evalJS(c, EXPR_PAGE_CAPABILITIES) || "{}");
|
|
25044
|
+
} catch (error2) {
|
|
25045
|
+
return { probeError: error2 instanceof Error ? error2.message : String(error2) };
|
|
24384
25046
|
}
|
|
24385
|
-
|
|
25047
|
+
}
|
|
25048
|
+
async _ensureChatPanel(c) {
|
|
25049
|
+
const snapshot = await this._readChatPanelDiagnostic(c);
|
|
25050
|
+
if (snapshot.modalVisible !== true && snapshot.hasWritableInput === true) return snapshot;
|
|
25051
|
+
throw createChatPanelUnavailableError(snapshot);
|
|
25052
|
+
}
|
|
25053
|
+
async _throwChatPanelUnavailableAfterNoInput(c) {
|
|
25054
|
+
const snapshot = await this._readChatPanelDiagnostic(c);
|
|
25055
|
+
throw createChatPanelUnavailableError({
|
|
25056
|
+
...snapshot,
|
|
25057
|
+
inputStateChanged: snapshot.hasWritableInput === true
|
|
25058
|
+
});
|
|
24386
25059
|
}
|
|
24387
25060
|
// 清空对话上下文:定位 "New Agent" 钮后【Alt+click】——Alt 修饰使其执行 Replace Agent(清空旧对话),
|
|
24388
25061
|
// 而非新建(aria 标注 "New Agent (Ctrl+N) / [Alt] Replace Agent")。2026-06-08 实测回复区 markdown DOM 清空
|
|
@@ -24392,7 +25065,7 @@ var CursorBridge = class {
|
|
|
24392
25065
|
const created = JSON.parse(await evalJS(c, exprCreateAgentForWorkspace(options.projectPath)) || "{}");
|
|
24393
25066
|
if (!created.ok) {
|
|
24394
25067
|
const available = Array.isArray(created.available) ? `; available=${created.available.join(", ")}` : "";
|
|
24395
|
-
throw new Error(`Cursor Agents workspace binding failed: ${created.state || "unknown"}; wanted=${created.wanted ||
|
|
25068
|
+
throw new Error(`Cursor Agents workspace binding failed: ${created.state || "unknown"}; wanted=${created.wanted || basename6(options.projectPath)}${available}`);
|
|
24396
25069
|
}
|
|
24397
25070
|
await sleep2(1100);
|
|
24398
25071
|
return true;
|
|
@@ -24620,6 +25293,7 @@ var CursorBridge = class {
|
|
|
24620
25293
|
await this._applyModelPreference(c, job.modelPreference, job);
|
|
24621
25294
|
this._throwIfCancelledBeforeSend(job);
|
|
24622
25295
|
const filled = await evalJS(c, exprFill(job.prompt));
|
|
25296
|
+
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
24623
25297
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") throw new Error("Failed to enter the parallel_agent task");
|
|
24624
25298
|
await sleep2(350);
|
|
24625
25299
|
this._throwIfCancelledBeforeSend(job);
|
|
@@ -24672,6 +25346,97 @@ var CursorBridge = class {
|
|
|
24672
25346
|
c.close();
|
|
24673
25347
|
}
|
|
24674
25348
|
}
|
|
25349
|
+
async _readResponseSnapshot(c) {
|
|
25350
|
+
let snapshot = { messageCount: 0, replyLength: 0, replyHash: 0, stop: 0 };
|
|
25351
|
+
try {
|
|
25352
|
+
snapshot = JSON.parse(await evalJS(c, EXPR_SNAP) || "{}");
|
|
25353
|
+
} catch {
|
|
25354
|
+
}
|
|
25355
|
+
return {
|
|
25356
|
+
messageCount: Number(snapshot.messageCount || 0),
|
|
25357
|
+
replyLength: Number(snapshot.replyLength || 0),
|
|
25358
|
+
replyHash: Number(snapshot.replyHash || 0),
|
|
25359
|
+
stop: Number(snapshot.stop || 0)
|
|
25360
|
+
};
|
|
25361
|
+
}
|
|
25362
|
+
async _captureSessionResponseBaseline(c, timeoutMs = 15e3, pollMs = 250) {
|
|
25363
|
+
const deadline = Date.now() + timeoutMs;
|
|
25364
|
+
let lastKey = "";
|
|
25365
|
+
while (Date.now() < deadline) {
|
|
25366
|
+
const snapshot = await this._readResponseSnapshot(c);
|
|
25367
|
+
const key = snapshot.messageCount > 0 && snapshot.replyLength > 0 && snapshot.stop === 0 ? `${snapshot.messageCount}:${snapshot.replyLength}:${snapshot.replyHash}` : "";
|
|
25368
|
+
if (key && key === lastKey) {
|
|
25369
|
+
return {
|
|
25370
|
+
messageCount: snapshot.messageCount,
|
|
25371
|
+
replyLength: snapshot.replyLength,
|
|
25372
|
+
replyHash: snapshot.replyHash
|
|
25373
|
+
};
|
|
25374
|
+
}
|
|
25375
|
+
lastKey = key;
|
|
25376
|
+
await sleep2(pollMs);
|
|
25377
|
+
}
|
|
25378
|
+
throw cursorSessionError("SESSION_BASELINE_UNAVAILABLE", "the previous completed reply did not hydrate to a stable snapshot before continuation");
|
|
25379
|
+
}
|
|
25380
|
+
async _submitSessionTurn(job) {
|
|
25381
|
+
if (!job.agentId) throw cursorSessionError("SESSION_AGENT_NOT_BOUND", job.sessionId || "unknown session");
|
|
25382
|
+
const page = await findPage({
|
|
25383
|
+
targetId: this._lastLifecycle && this._lastLifecycle.targetId,
|
|
25384
|
+
purpose: "parallel_agent",
|
|
25385
|
+
preferAgentsV2: true
|
|
25386
|
+
});
|
|
25387
|
+
job.targetId = page.id;
|
|
25388
|
+
job.targetUiFlavor = page.capabilities && page.capabilities.uiFlavor || null;
|
|
25389
|
+
const c = makeClient(page.webSocketDebuggerUrl);
|
|
25390
|
+
await c.ready;
|
|
25391
|
+
let sent = false;
|
|
25392
|
+
try {
|
|
25393
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25394
|
+
await this._ensureChatPanel(c);
|
|
25395
|
+
const entries = await this._readAgentEntries(c, true);
|
|
25396
|
+
const target = entries.find((entry) => entry && entry.id === job.agentId);
|
|
25397
|
+
if (!target) {
|
|
25398
|
+
throw cursorSessionError("SESSION_AGENT_NOT_FOUND", `Agent History does not contain ${job.agentId}`);
|
|
25399
|
+
}
|
|
25400
|
+
const previousSelectedId = (entries.find((entry) => entry.isSelected) || {}).id || null;
|
|
25401
|
+
if (!target.isSelected) {
|
|
25402
|
+
const opened = await evalJS(c, exprOpenAgent(job.agentId));
|
|
25403
|
+
if (opened !== "OPENED") {
|
|
25404
|
+
throw cursorSessionError("SESSION_AGENT_OPEN_FAILED", `${job.agentId}: ${opened}`);
|
|
25405
|
+
}
|
|
25406
|
+
}
|
|
25407
|
+
await this._closeHistory(c);
|
|
25408
|
+
await this._waitForSelectedAgent(c, job.agentId);
|
|
25409
|
+
await this._ensureChatPanel(c);
|
|
25410
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25411
|
+
await this._applyModelPreference(c, job.modelPreference, job);
|
|
25412
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25413
|
+
job.responseBaseline = await this._captureSessionResponseBaseline(c);
|
|
25414
|
+
const providerErrorBaseline = providerErrorSignature(await this._readProviderError(c));
|
|
25415
|
+
const filled = await evalJS(c, exprFill(job.prompt));
|
|
25416
|
+
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
25417
|
+
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") {
|
|
25418
|
+
throw cursorSessionError("SESSION_INPUT_FAILED", "failed to enter the continued task");
|
|
25419
|
+
}
|
|
25420
|
+
await sleep2(350);
|
|
25421
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25422
|
+
job.sendState = "dispatching";
|
|
25423
|
+
await chord(c, 0, "Enter", "Enter", 13);
|
|
25424
|
+
await this._confirmSubmission(c, job.responseBaseline.messageCount, providerErrorBaseline);
|
|
25425
|
+
sent = true;
|
|
25426
|
+
job.sendState = "sent";
|
|
25427
|
+
job.sentAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25428
|
+
return {
|
|
25429
|
+
agent: { id: job.agentId, label: target.label || job.agentLabel || null },
|
|
25430
|
+
previousSelectedId
|
|
25431
|
+
};
|
|
25432
|
+
} catch (error2) {
|
|
25433
|
+
if (sent) error2.sent = true;
|
|
25434
|
+
throw error2;
|
|
25435
|
+
} finally {
|
|
25436
|
+
await this._closeHistory(c);
|
|
25437
|
+
c.close();
|
|
25438
|
+
}
|
|
25439
|
+
}
|
|
24675
25440
|
async _readParallelEntry(job) {
|
|
24676
25441
|
return this._withUiLock(async () => {
|
|
24677
25442
|
const page = await findPage({ targetId: job.targetId, purpose: "parallel_agent" });
|
|
@@ -24824,13 +25589,27 @@ var CursorBridge = class {
|
|
|
24824
25589
|
const detail = lastCollectionError ? `; last collection error: ${lastCollectionError}` : "";
|
|
24825
25590
|
throw new Error(`Cursor parallel_agent task timed out (${job.timeoutMs}ms)${detail}`);
|
|
24826
25591
|
}
|
|
24827
|
-
async
|
|
25592
|
+
async _requestExactAgentSelection(c, agentId) {
|
|
25593
|
+
if (!await this._ensureHistoryOpen(c)) return "REACT_ADAPTER_UNAVAILABLE";
|
|
25594
|
+
try {
|
|
25595
|
+
return await evalJS(c, exprOpenAgent(agentId));
|
|
25596
|
+
} finally {
|
|
25597
|
+
await this._closeHistory(c);
|
|
25598
|
+
}
|
|
25599
|
+
}
|
|
25600
|
+
async _waitForSelectedAgent(c, agentId, timeoutMs = 15e3, retryDelayMs = 2500, pollMs = 250) {
|
|
24828
25601
|
const deadline = Date.now() + timeoutMs;
|
|
25602
|
+
const retryAt = Date.now() + retryDelayMs;
|
|
25603
|
+
let retried = false;
|
|
24829
25604
|
while (Date.now() < deadline) {
|
|
24830
25605
|
const entries = await this._readAgentEntries(c);
|
|
24831
25606
|
const target = entries.find((e) => e.id === agentId);
|
|
24832
25607
|
if (target && target.isSelected) return true;
|
|
24833
|
-
|
|
25608
|
+
if (!retried && Date.now() >= retryAt) {
|
|
25609
|
+
retried = true;
|
|
25610
|
+
await this._requestExactAgentSelection(c, agentId);
|
|
25611
|
+
}
|
|
25612
|
+
await sleep2(pollMs);
|
|
24834
25613
|
}
|
|
24835
25614
|
throw new Error(`After opening ${agentId}, it could not be confirmed as the selected Agent`);
|
|
24836
25615
|
}
|
|
@@ -24850,13 +25629,10 @@ var CursorBridge = class {
|
|
|
24850
25629
|
let lastKey = "";
|
|
24851
25630
|
let stable = 0;
|
|
24852
25631
|
for (let i = 0; i < 80; i++) {
|
|
24853
|
-
|
|
24854
|
-
try {
|
|
24855
|
-
snap = JSON.parse(await evalJS(c, EXPR_SNAP));
|
|
24856
|
-
} catch {
|
|
24857
|
-
}
|
|
25632
|
+
const snap = await this._readResponseSnapshot(c);
|
|
24858
25633
|
const candidate = String(await evalJS(c, EXPR_EXTRACT) || "").trim();
|
|
24859
|
-
|
|
25634
|
+
const hasNewTurn = isSessionTurnReplyReady(job.responseBaseline, snap);
|
|
25635
|
+
if (candidate && hasNewTurn && Number(snap.stop || 0) === 0) {
|
|
24860
25636
|
const key = `${snap.replyLength}:${snap.replyHash}`;
|
|
24861
25637
|
if (key === lastKey) stable++;
|
|
24862
25638
|
else {
|
|
@@ -24897,6 +25673,7 @@ var CursorBridge = class {
|
|
|
24897
25673
|
job.recoveryState = "terminal_result_uncollected";
|
|
24898
25674
|
job.reservationScope = uncertainSubmissionReservationScope(job, error2);
|
|
24899
25675
|
job.recoveryState = error2.recoveryState || "monitoring_uncertain_submission";
|
|
25676
|
+
this._safeSettleSessionJob(job, "terminal_result_uncollected", { needsAttention: true });
|
|
24900
25677
|
}
|
|
24901
25678
|
_abandonJob(job, reason) {
|
|
24902
25679
|
if (isTerminalTask(job)) return this._taskView(job, true);
|
|
@@ -24913,6 +25690,10 @@ var CursorBridge = class {
|
|
|
24913
25690
|
job.terminalEvidence = "explicit_abandon_acknowledgement";
|
|
24914
25691
|
job.recoveryState = "released_unconfirmed";
|
|
24915
25692
|
job.reservationScope = null;
|
|
25693
|
+
this._safeSettleSessionJob(job, "abandoned", {
|
|
25694
|
+
needsAttention: true,
|
|
25695
|
+
attention: "The Bridge released the task reservation without proving that the Cursor Agent stopped. This session cannot continue safely."
|
|
25696
|
+
});
|
|
24916
25697
|
if (!job.settled) {
|
|
24917
25698
|
job.settled = true;
|
|
24918
25699
|
job.reject(new Error(message));
|
|
@@ -25448,6 +26229,12 @@ var CursorBridge = class {
|
|
|
25448
26229
|
modelPreference: job.modelPreference,
|
|
25449
26230
|
modelSelection: job.modelSelection,
|
|
25450
26231
|
projectPath: job.projectPath,
|
|
26232
|
+
sessionMode: job.sessionMode || "isolated",
|
|
26233
|
+
sessionId: job.sessionId || null,
|
|
26234
|
+
sessionTurn: job.sessionTurn || null,
|
|
26235
|
+
sessionState: job.sessionState || null,
|
|
26236
|
+
sessionError: job.sessionError || null,
|
|
26237
|
+
requestId: job.requestId || null,
|
|
25451
26238
|
agentId: job.agentId,
|
|
25452
26239
|
provisionalAgentId: job.provisionalAgentId || null,
|
|
25453
26240
|
agentLabel: job.agentLabel,
|
|
@@ -25470,6 +26257,7 @@ var CursorBridge = class {
|
|
|
25470
26257
|
resultUnavailable: job.resultUnavailable,
|
|
25471
26258
|
terminalEvidence: job.terminalEvidence,
|
|
25472
26259
|
providerError: job.providerError,
|
|
26260
|
+
uiDiagnostic: job.uiDiagnostic,
|
|
25473
26261
|
sendState: job.sendState,
|
|
25474
26262
|
reservationScope: job.reservationScope,
|
|
25475
26263
|
reservationHeld: this.activeParallel.has(job.id),
|
|
@@ -25496,8 +26284,8 @@ var CursorBridge = class {
|
|
|
25496
26284
|
async status(taskId = "") {
|
|
25497
26285
|
if (taskId) {
|
|
25498
26286
|
const job = this.tasks.get(String(taskId));
|
|
25499
|
-
if (!job) return { found: false, taskId: String(taskId), ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView() };
|
|
25500
|
-
return { found: true, ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this._taskView(job, true) };
|
|
26287
|
+
if (!job) return { found: false, taskId: String(taskId), ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this.sessionRegistryView() };
|
|
26288
|
+
return { found: true, ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this.sessionRegistryView(), ...this._taskView(job, true) };
|
|
25501
26289
|
}
|
|
25502
26290
|
const parallelRunning = this.activeParallel.size;
|
|
25503
26291
|
const uiBusy = this.busy;
|
|
@@ -25509,6 +26297,7 @@ var CursorBridge = class {
|
|
|
25509
26297
|
...this.delegationView(),
|
|
25510
26298
|
...this.runtimeModeView(),
|
|
25511
26299
|
...this.modelPreferencesView(),
|
|
26300
|
+
...this.sessionRegistryView(),
|
|
25512
26301
|
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0,
|
|
25513
26302
|
uiBusy,
|
|
25514
26303
|
parallelRunning,
|
|
@@ -25576,7 +26365,7 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25576
26365
|
},
|
|
25577
26366
|
bridgeInstance.environmentDelegationMode !== "off" ? {
|
|
25578
26367
|
name: "cursor_do",
|
|
25579
|
-
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. 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.",
|
|
26368
|
+
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.",
|
|
25580
26369
|
inputSchema: {
|
|
25581
26370
|
type: "object",
|
|
25582
26371
|
properties: {
|
|
@@ -25586,7 +26375,10 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25586
26375
|
read_only: { type: "boolean", default: false, description: "Set true when Cursor must not change the workspace." },
|
|
25587
26376
|
timeout_ms: { type: "integer", minimum: 3e4, maximum: 9e5, default: 6e5, description: "How long to wait before timing out, in milliseconds. The default is 10 minutes." },
|
|
25588
26377
|
allowed_paths: { type: "array", items: { type: "string" }, description: "Workspace-relative paths Cursor may write. Parallel write tasks require non-overlapping paths. This declaration is not a filesystem sandbox." },
|
|
25589
|
-
completion_contract: { type: "string", description: "Optional acceptance checks or a required final-report format." }
|
|
26378
|
+
completion_contract: { type: "string", description: "Optional acceptance checks or a required final-report format." },
|
|
26379
|
+
session_mode: { type: "string", enum: [...CURSOR_SESSION_MODES], default: "isolated", description: "isolated preserves the current clean-task behavior. create starts an update-safe persistent session. continue sends one new turn to the exact session_id." },
|
|
26380
|
+
session_id: { type: "string", description: "Required only for session_mode=continue. This stable Bridge ID is not a Cursor agentId." },
|
|
26381
|
+
request_id: { type: "string", description: "Optional idempotency key for a persistent session turn. Repeating the latest key returns its existing task/session instead of sending again." }
|
|
25590
26382
|
},
|
|
25591
26383
|
required: ["prompt"]
|
|
25592
26384
|
}
|
|
@@ -25607,6 +26399,21 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25607
26399
|
required: ["task_id", "action"]
|
|
25608
26400
|
}
|
|
25609
26401
|
},
|
|
26402
|
+
{
|
|
26403
|
+
name: "cursor_session_control",
|
|
26404
|
+
description: "Recover, close, or forget one exact persistent cursor_do session. reconcile checks the exact Agent twice and never resends. close prevents future sends but does not stop or delete the Cursor Agent. An active or uncertain session cannot be closed. abandon is the explicit last resort when exact stop evidence is unavailable. forget requires confirm=true and is allowed only after close; it deletes only the Bridge mapping.",
|
|
26405
|
+
inputSchema: {
|
|
26406
|
+
type: "object",
|
|
26407
|
+
properties: {
|
|
26408
|
+
session_id: { type: "string", description: "The exact session ID returned by cursor_do(session_mode=create)." },
|
|
26409
|
+
action: { type: "string", enum: ["reconcile", "close", "forget", "abandon"], description: "reconcile reads the exact Agent state; close ends safe continuity; forget removes a closed mapping; abandon releases an uncertain mapping without stop proof." },
|
|
26410
|
+
confirm: { type: "boolean", default: false, description: "Required for forget and abandon." },
|
|
26411
|
+
reason: { type: "string", description: "Required and non-empty for abandon." },
|
|
26412
|
+
acknowledge_may_still_write: { type: "boolean", default: false, description: "Required for abandon; acknowledges that the underlying Cursor Agent may still run or write." }
|
|
26413
|
+
},
|
|
26414
|
+
required: ["session_id", "action"]
|
|
26415
|
+
}
|
|
26416
|
+
},
|
|
25610
26417
|
{
|
|
25611
26418
|
name: "cursor_runtime",
|
|
25612
26419
|
description: "Persistently switch Cursor presentation between normal and minimal. normal is the fresh-install default and restores ordinary visible Cursor use. minimal is the recommended opt-in background experience on tested Windows 11 systems: Cursor and CCE keep running while Cursor windows stay hidden. Before enabling minimal, tell the user that manually opening Cursor will remain hidden until CCE is switched back to normal. This is UI suppression, not a headless reimplementation.",
|
|
@@ -25620,7 +26427,7 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25620
26427
|
},
|
|
25621
26428
|
{
|
|
25622
26429
|
name: "cursor_model",
|
|
25623
|
-
description: "Show, set, or reset persistent Cursor model defaults for CCE and cursor_do. Settings are independent per target and survive host tasks, MCP restarts, and Cursor Bridge restarts until the user explicitly changes or resets them. When configured, Bridge applies the model and optional effort in every newly created Cursor Agent before sending the prompt, then verifies the visible selection. An unavailable, ambiguous, unsupported, or unconfirmed selection fails before prompt submission instead of silently falling back to Auto. Use set/reset only when the user explicitly asks to change these defaults; use show for read-only inspection.",
|
|
26430
|
+
description: "Show, set, or reset persistent Cursor model defaults for CCE and cursor_do. Settings are independent per target and survive host tasks, MCP restarts, and Cursor Bridge restarts until the user explicitly changes or resets them. When configured, Bridge applies the model and optional effort in every newly created Cursor Agent before sending the prompt, then verifies the visible selection. A persistent session freezes that preference and reapplies it on continued turns instead of inheriting later global changes. An unavailable, ambiguous, unsupported, or unconfirmed selection fails before prompt submission instead of silently falling back to Auto. Use set/reset only when the user explicitly asks to change these defaults; use show for read-only inspection.",
|
|
25624
26431
|
inputSchema: {
|
|
25625
26432
|
type: "object",
|
|
25626
26433
|
properties: {
|
|
@@ -25634,8 +26441,14 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25634
26441
|
},
|
|
25635
26442
|
{
|
|
25636
26443
|
name: "cursor_status",
|
|
25637
|
-
description: "Read-only snapshot of Cursor connectivity, queued/running work, reservations, execution availability, persistent model/effort defaults, and normal/minimal runtime presentation. Pass
|
|
25638
|
-
inputSchema: {
|
|
26444
|
+
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.",
|
|
26445
|
+
inputSchema: {
|
|
26446
|
+
type: "object",
|
|
26447
|
+
properties: {
|
|
26448
|
+
task_id: { type: "string", description: "A task ID returned by cursor_do." },
|
|
26449
|
+
session_id: { type: "string", description: "A persistent session ID returned by cursor_do(session_mode=create)." }
|
|
26450
|
+
}
|
|
26451
|
+
}
|
|
25639
26452
|
}
|
|
25640
26453
|
].filter(Boolean);
|
|
25641
26454
|
}
|
|
@@ -25660,6 +26473,21 @@ async function ensureBridgeCursor(targetBridge, reason) {
|
|
|
25660
26473
|
}
|
|
25661
26474
|
return r;
|
|
25662
26475
|
}
|
|
26476
|
+
function toolErrorResult(error2) {
|
|
26477
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
26478
|
+
if (error2 && error2.uiDiagnostic) {
|
|
26479
|
+
const payload = {
|
|
26480
|
+
error: { code: error2.code || error2.uiDiagnostic.code || "CURSOR_BRIDGE_ERROR", message },
|
|
26481
|
+
uiDiagnostic: error2.uiDiagnostic
|
|
26482
|
+
};
|
|
26483
|
+
return {
|
|
26484
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
26485
|
+
structuredContent: payload,
|
|
26486
|
+
isError: true
|
|
26487
|
+
};
|
|
26488
|
+
}
|
|
26489
|
+
return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
|
|
26490
|
+
}
|
|
25663
26491
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
25664
26492
|
tools: buildToolDefinitions(bridge)
|
|
25665
26493
|
}));
|
|
@@ -25681,7 +26509,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
25681
26509
|
readOnly: !!(args && args.read_only),
|
|
25682
26510
|
timeoutMs: args && args.timeout_ms,
|
|
25683
26511
|
allowedPaths: args && args.allowed_paths,
|
|
25684
|
-
completionContract: args && args.completion_contract
|
|
26512
|
+
completionContract: args && args.completion_contract,
|
|
26513
|
+
sessionMode: args && args.session_mode,
|
|
26514
|
+
sessionId: args && args.session_id,
|
|
26515
|
+
requestId: args && args.request_id,
|
|
26516
|
+
readOnlySpecified: !!(args && Object.hasOwn(args, "read_only")),
|
|
26517
|
+
allowedPathsSpecified: !!(args && Object.hasOwn(args, "allowed_paths"))
|
|
25685
26518
|
});
|
|
25686
26519
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
25687
26520
|
}
|
|
@@ -25695,6 +26528,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
25695
26528
|
});
|
|
25696
26529
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
25697
26530
|
}
|
|
26531
|
+
if (name === "cursor_session_control") {
|
|
26532
|
+
const result = await bridge.sessionControl(args && args.session_id, {
|
|
26533
|
+
action: args && args.action,
|
|
26534
|
+
confirm: !!(args && args.confirm),
|
|
26535
|
+
reason: args && args.reason,
|
|
26536
|
+
acknowledgeMayStillWrite: !!(args && args.acknowledge_may_still_write)
|
|
26537
|
+
});
|
|
26538
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
26539
|
+
}
|
|
25698
26540
|
if (name === "cursor_runtime") {
|
|
25699
26541
|
const mode = args && args.mode;
|
|
25700
26542
|
const action = args && args.action;
|
|
@@ -25717,11 +26559,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
25717
26559
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
25718
26560
|
}
|
|
25719
26561
|
if (name === "cursor_status") {
|
|
26562
|
+
if (args && args.task_id && args.session_id) {
|
|
26563
|
+
throw cursorSessionError("STATUS_SELECTOR_AMBIGUOUS", "pass task_id or session_id, not both");
|
|
26564
|
+
}
|
|
25720
26565
|
const statusMs = Math.max(1e3, Number(process.env.CURSOR_BRIDGE_STATUS_TIMEOUT || 8e3));
|
|
25721
26566
|
let result;
|
|
25722
26567
|
try {
|
|
25723
26568
|
result = await Promise.race([
|
|
25724
|
-
bridge.status(args && args.task_id),
|
|
26569
|
+
args && args.session_id ? Promise.resolve(bridge.sessionStatus(args.session_id)) : bridge.status(args && args.task_id),
|
|
25725
26570
|
new Promise((_, reject) => setTimeout(() => reject(new Error(`cursor_status_timeout_${statusMs}`)), statusMs))
|
|
25726
26571
|
]);
|
|
25727
26572
|
} catch (error2) {
|
|
@@ -25732,7 +26577,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
25732
26577
|
error: error2 instanceof Error ? error2.message : String(error2),
|
|
25733
26578
|
...bridge.workspaceView(),
|
|
25734
26579
|
...bridge.delegationView(),
|
|
25735
|
-
...bridge.runtimeModeView()
|
|
26580
|
+
...bridge.runtimeModeView(),
|
|
26581
|
+
...bridge.sessionRegistryView()
|
|
25736
26582
|
};
|
|
25737
26583
|
}
|
|
25738
26584
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -25743,7 +26589,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
25743
26589
|
}
|
|
25744
26590
|
throw new Error(`Unknown tool: ${name}`);
|
|
25745
26591
|
} catch (error2) {
|
|
25746
|
-
return
|
|
26592
|
+
return toolErrorResult(error2);
|
|
25747
26593
|
}
|
|
25748
26594
|
});
|
|
25749
26595
|
async function main() {
|
|
@@ -25790,7 +26636,9 @@ export {
|
|
|
25790
26636
|
bridge,
|
|
25791
26637
|
buildContextEnginePrompt,
|
|
25792
26638
|
buildToolDefinitions,
|
|
26639
|
+
classifyChatPanelDiagnostic,
|
|
25793
26640
|
classifyParallelTerminalIcon,
|
|
26641
|
+
createChatPanelUnavailableError,
|
|
25794
26642
|
createProviderError,
|
|
25795
26643
|
cursorStartupBehavior,
|
|
25796
26644
|
exprClickBoundComposerStop,
|
|
@@ -25799,9 +26647,9 @@ export {
|
|
|
25799
26647
|
exprFill,
|
|
25800
26648
|
exprInspectWorkspaceRepository,
|
|
25801
26649
|
exprOpenAgent,
|
|
25802
|
-
isBlankAgentsWindow,
|
|
25803
26650
|
isConfirmedCompletedReply,
|
|
25804
26651
|
isDurablyRegisteredParallelEntry,
|
|
26652
|
+
isSessionTurnReplyReady,
|
|
25805
26653
|
isTargetedStopConfirmed,
|
|
25806
26654
|
lifecycleFailureSummary,
|
|
25807
26655
|
normalizeAllowedPath,
|
|
@@ -25824,7 +26672,9 @@ export {
|
|
|
25824
26672
|
selectUniqueNewAgentEntry,
|
|
25825
26673
|
shouldAutoLaunchCursor,
|
|
25826
26674
|
shouldRecoverNormalAgentsPresentation,
|
|
26675
|
+
shouldScheduleParallelOriginRestore,
|
|
25827
26676
|
summarizeCdpPages,
|
|
26677
|
+
toolErrorResult,
|
|
25828
26678
|
uncertainSubmissionReservationScope,
|
|
25829
26679
|
updateStableEntryObservation
|
|
25830
26680
|
};
|