pi-cursor-bridge 0.1.8 → 0.1.10
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 +1 -1
- package/dist/cursor-bridge.mjs +975 -158
- 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,16 +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
|
+
realpathSync,
|
|
12260
12261
|
readdirSync,
|
|
12261
|
-
renameSync as
|
|
12262
|
-
rmSync as
|
|
12262
|
+
renameSync as renameSync5,
|
|
12263
|
+
rmSync as rmSync5,
|
|
12263
12264
|
unlinkSync,
|
|
12264
|
-
writeFileSync as
|
|
12265
|
+
writeFileSync as writeFileSync5
|
|
12265
12266
|
} from "node:fs";
|
|
12266
12267
|
import { fileURLToPath } from "node:url";
|
|
12267
|
-
import { basename as
|
|
12268
|
+
import { basename as basename5, dirname as dirname5, join as join7, resolve as resolve6, win32 as win32Path } from "node:path";
|
|
12268
12269
|
function resolveSupervisorSpawnCwd({
|
|
12269
12270
|
requestedCwd = null,
|
|
12270
12271
|
runtimeRoot = null,
|
|
@@ -12279,9 +12280,25 @@ function resolveSupervisorSpawnCwd({
|
|
|
12279
12280
|
return runtimeRoot;
|
|
12280
12281
|
}
|
|
12281
12282
|
function resolvePluginLocalLifecycleDir(sourceScript) {
|
|
12282
|
-
const scriptDir =
|
|
12283
|
-
const pluginRoot =
|
|
12284
|
-
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
|
+
}
|
|
12287
|
+
function inspectWindowsAppContainerPath(target, options = {}) {
|
|
12288
|
+
const platform = options.platform || process.platform;
|
|
12289
|
+
const requested = win32Path.normalize(win32Path.resolve(String(target || "")));
|
|
12290
|
+
if (platform !== "win32") {
|
|
12291
|
+
return { redirected: false, requested, physical: null };
|
|
12292
|
+
}
|
|
12293
|
+
try {
|
|
12294
|
+
const realpathImpl = options.realpathImpl || realpathSync.native || realpathSync;
|
|
12295
|
+
const physical = win32Path.normalize(win32Path.resolve(String(realpathImpl(target))));
|
|
12296
|
+
const physicalLower = physical.toLowerCase();
|
|
12297
|
+
const redirected = requested.toLowerCase() !== physicalLower && physicalLower.includes("\\appdata\\local\\packages\\") && physicalLower.includes("\\localcache\\local\\");
|
|
12298
|
+
return { redirected, requested, physical };
|
|
12299
|
+
} catch {
|
|
12300
|
+
return { redirected: false, requested, physical: null };
|
|
12301
|
+
}
|
|
12285
12302
|
}
|
|
12286
12303
|
function probeOutsideJobCwd(cwd, options = {}) {
|
|
12287
12304
|
const platform = options.platform || process.platform;
|
|
@@ -12308,59 +12325,59 @@ function sleep(ms) {
|
|
|
12308
12325
|
}
|
|
12309
12326
|
function resolveSupervisorScript() {
|
|
12310
12327
|
if (process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT && existsSync4(process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT)) {
|
|
12311
|
-
return
|
|
12328
|
+
return resolve6(process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT);
|
|
12312
12329
|
}
|
|
12313
|
-
const here =
|
|
12330
|
+
const here = dirname5(fileURLToPath(import.meta.url));
|
|
12314
12331
|
const candidates = [];
|
|
12315
12332
|
if (typeof process.argv[1] === "string") {
|
|
12316
|
-
const entryDir =
|
|
12317
|
-
candidates.push(
|
|
12318
|
-
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"));
|
|
12319
12336
|
}
|
|
12320
12337
|
candidates.push(
|
|
12321
|
-
|
|
12322
|
-
|
|
12338
|
+
join7(here, "dist", "cursor-lifecycle-supervisor.mjs"),
|
|
12339
|
+
join7(here, "cursor-lifecycle-supervisor.mjs")
|
|
12323
12340
|
);
|
|
12324
12341
|
for (const c of candidates) {
|
|
12325
12342
|
if (existsSync4(c)) return c;
|
|
12326
12343
|
}
|
|
12327
|
-
return
|
|
12344
|
+
return join7(here, "cursor-lifecycle-supervisor.mjs");
|
|
12328
12345
|
}
|
|
12329
12346
|
function writeRuntimeFile(target, content) {
|
|
12330
|
-
if (existsSync4(target) &&
|
|
12347
|
+
if (existsSync4(target) && readFileSync5(target).equals(content)) return;
|
|
12331
12348
|
const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
12332
|
-
|
|
12349
|
+
writeFileSync5(temporary, content);
|
|
12333
12350
|
try {
|
|
12334
|
-
|
|
12351
|
+
renameSync5(temporary, target);
|
|
12335
12352
|
} catch (error2) {
|
|
12336
12353
|
if (!existsSync4(target)) {
|
|
12337
|
-
|
|
12354
|
+
rmSync5(temporary, { force: true });
|
|
12338
12355
|
throw error2;
|
|
12339
12356
|
}
|
|
12340
|
-
if (
|
|
12341
|
-
|
|
12357
|
+
if (readFileSync5(target).equals(content)) {
|
|
12358
|
+
rmSync5(temporary, { force: true });
|
|
12342
12359
|
return;
|
|
12343
12360
|
}
|
|
12344
|
-
|
|
12345
|
-
|
|
12361
|
+
rmSync5(target, { force: true });
|
|
12362
|
+
renameSync5(temporary, target);
|
|
12346
12363
|
}
|
|
12347
12364
|
}
|
|
12348
12365
|
function materializeLifecycleSupervisorRuntime({ sourceScript, dir = defaultLifecycleDir() } = {}) {
|
|
12349
12366
|
const described = describeLifecycleSupervisorRuntime({ sourceScript, dir });
|
|
12350
12367
|
const { sourceScript: source, content, fingerprint } = described;
|
|
12351
|
-
const runtimeRoot =
|
|
12352
|
-
|
|
12353
|
-
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");
|
|
12354
12371
|
writeRuntimeFile(script, content);
|
|
12355
12372
|
return { sourceScript: source, script, runtimeRoot, fingerprint };
|
|
12356
12373
|
}
|
|
12357
12374
|
function describeLifecycleSupervisorRuntime({ sourceScript, dir = defaultLifecycleDir() } = {}) {
|
|
12358
|
-
const source =
|
|
12375
|
+
const source = resolve6(sourceScript || resolveSupervisorScript());
|
|
12359
12376
|
if (!existsSync4(source)) throw new Error(`lifecycle supervisor script missing: ${source}`);
|
|
12360
|
-
const content =
|
|
12377
|
+
const content = readFileSync5(source);
|
|
12361
12378
|
const fingerprint = createHash2("sha256").update(content).digest("hex");
|
|
12362
|
-
const runtimeRoot =
|
|
12363
|
-
const script =
|
|
12379
|
+
const runtimeRoot = join7(dir, "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
|
|
12380
|
+
const script = join7(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
|
|
12364
12381
|
return { sourceScript: source, script, runtimeRoot, fingerprint, content };
|
|
12365
12382
|
}
|
|
12366
12383
|
function isProcessAlive(pid) {
|
|
@@ -12374,7 +12391,7 @@ function isProcessAlive(pid) {
|
|
|
12374
12391
|
}
|
|
12375
12392
|
function readPidFile(pidPath) {
|
|
12376
12393
|
try {
|
|
12377
|
-
const n = Number(String(
|
|
12394
|
+
const n = Number(String(readFileSync5(pidPath, "utf8")).trim());
|
|
12378
12395
|
return Number.isFinite(n) ? n : null;
|
|
12379
12396
|
} catch {
|
|
12380
12397
|
return null;
|
|
@@ -12489,7 +12506,7 @@ function tryUnlink(path) {
|
|
|
12489
12506
|
}
|
|
12490
12507
|
}
|
|
12491
12508
|
function writeBootEnv(dir, extra = {}) {
|
|
12492
|
-
const bootPath =
|
|
12509
|
+
const bootPath = join7(dir, `boot-env-${process.pid}-${Date.now()}.json`);
|
|
12493
12510
|
const payload = { ...extra };
|
|
12494
12511
|
for (const [key, value] of Object.entries(process.env)) {
|
|
12495
12512
|
if (key.startsWith("CURSOR_BRIDGE_") || key === "CURSOR_PROJECT_PATH" || key === "CURSOR_EXE") {
|
|
@@ -12500,7 +12517,7 @@ function writeBootEnv(dir, extra = {}) {
|
|
|
12500
12517
|
for (const [k, v] of Object.entries(payload)) {
|
|
12501
12518
|
if (v != null && v !== "") cleaned[k] = String(v);
|
|
12502
12519
|
}
|
|
12503
|
-
|
|
12520
|
+
writeFileSync5(bootPath, `${JSON.stringify(cleaned, null, 2)}
|
|
12504
12521
|
`, { encoding: "utf8" });
|
|
12505
12522
|
return bootPath;
|
|
12506
12523
|
}
|
|
@@ -12535,9 +12552,9 @@ async function ensureSupervisorConnected(options = {}) {
|
|
|
12535
12552
|
let targetRuntime = null;
|
|
12536
12553
|
try {
|
|
12537
12554
|
targetRuntime = options.persistSupervisorRuntime === false ? {
|
|
12538
|
-
sourceScript:
|
|
12539
|
-
script:
|
|
12540
|
-
runtimeRoot:
|
|
12555
|
+
sourceScript: resolve6(sourceScript),
|
|
12556
|
+
script: resolve6(sourceScript),
|
|
12557
|
+
runtimeRoot: dirname5(resolve6(sourceScript)),
|
|
12541
12558
|
fingerprint: null
|
|
12542
12559
|
} : describeLifecycleSupervisorRuntime({ sourceScript, dir });
|
|
12543
12560
|
} catch {
|
|
@@ -12578,54 +12595,69 @@ async function ensureSupervisorConnected(options = {}) {
|
|
|
12578
12595
|
}, initialConnection.error);
|
|
12579
12596
|
}
|
|
12580
12597
|
const canProbeFallback = platform === "win32" && options._disablePluginLocalFallback !== true && !options.dir && !options.sock && !options.pidPath && !options.lockPath && (!options.spawnNodeOutsideJobImpl || options.lifecycleCwdProbeImpl);
|
|
12598
|
+
const runProbe = canProbeFallback ? options.lifecycleCwdProbeImpl || ((target) => probeOutsideJobCwd(target, {
|
|
12599
|
+
platform,
|
|
12600
|
+
systemRoot: options.systemRoot,
|
|
12601
|
+
commandInterpreter: options.commandInterpreter,
|
|
12602
|
+
spawnOutsideJobImpl: options.probeSpawnOutsideJobImpl,
|
|
12603
|
+
existsImpl: options.existsImpl,
|
|
12604
|
+
env: options.env || process.env
|
|
12605
|
+
})) : null;
|
|
12606
|
+
const connectPluginLocalFallback = async ({ sharedFailure = null } = {}) => {
|
|
12607
|
+
const fallbackDir = options.pluginLocalLifecycleDir || resolvePluginLocalLifecycleDir(sourceScript);
|
|
12608
|
+
ensureLifecycleDir(fallbackDir);
|
|
12609
|
+
const fallbackProbe = runProbe ? runProbe(fallbackDir) : { ok: true, method: "not-required" };
|
|
12610
|
+
if (!fallbackProbe || fallbackProbe.ok !== true) {
|
|
12611
|
+
throw lifecycleClientError(`shared lifecycle storage cannot host the persistent Supervisor and plugin-local fallback is also unavailable: ${fallbackDir}`, {
|
|
12612
|
+
errorKind: fallbackProbe?.errorKind || sharedFailure?.errorKind || "unknown",
|
|
12613
|
+
degradedReason: fallbackProbe?.degradedReason || "plugin-local-lifecycle-unavailable",
|
|
12614
|
+
errorCode: fallbackProbe && (fallbackProbe.errorCode ?? fallbackProbe.returnValue) || null,
|
|
12615
|
+
canAttachFallback: true,
|
|
12616
|
+
spawnCwd: fallbackDir
|
|
12617
|
+
});
|
|
12618
|
+
}
|
|
12619
|
+
return ensureSupervisorConnected({
|
|
12620
|
+
...options,
|
|
12621
|
+
dir: fallbackDir,
|
|
12622
|
+
sock: void 0,
|
|
12623
|
+
pidPath: void 0,
|
|
12624
|
+
lockPath: void 0,
|
|
12625
|
+
_disablePluginLocalFallback: true,
|
|
12626
|
+
_lifecycleStorageMode: "plugin-cache-fallback"
|
|
12627
|
+
});
|
|
12628
|
+
};
|
|
12581
12629
|
if (canProbeFallback) {
|
|
12582
12630
|
ensureLifecycleDir(dir);
|
|
12583
|
-
const runProbe = options.lifecycleCwdProbeImpl || ((target) => probeOutsideJobCwd(target, {
|
|
12584
|
-
platform,
|
|
12585
|
-
systemRoot: options.systemRoot,
|
|
12586
|
-
commandInterpreter: options.commandInterpreter,
|
|
12587
|
-
spawnOutsideJobImpl: options.probeSpawnOutsideJobImpl,
|
|
12588
|
-
existsImpl: options.existsImpl,
|
|
12589
|
-
env: options.env || process.env
|
|
12590
|
-
}));
|
|
12591
12631
|
const sharedProbe = runProbe(dir);
|
|
12592
12632
|
const sharedCode = sharedProbe && (sharedProbe.errorCode ?? sharedProbe.returnValue);
|
|
12593
12633
|
if (sharedProbe && sharedProbe.ok !== true && (sharedCode === 8 || sharedProbe.degradedReason === "wmi-unknown-8")) {
|
|
12594
|
-
|
|
12595
|
-
ensureLifecycleDir(fallbackDir);
|
|
12596
|
-
const fallbackProbe = runProbe(fallbackDir);
|
|
12597
|
-
if (!fallbackProbe || fallbackProbe.ok !== true) {
|
|
12598
|
-
throw lifecycleClientError(`shared lifecycle storage is inaccessible to WMI and plugin-local fallback is also unavailable: ${fallbackDir}`, {
|
|
12599
|
-
errorKind: fallbackProbe?.errorKind || sharedProbe.errorKind || "unknown",
|
|
12600
|
-
degradedReason: fallbackProbe?.degradedReason || "plugin-local-lifecycle-unavailable",
|
|
12601
|
-
errorCode: fallbackProbe && (fallbackProbe.errorCode ?? fallbackProbe.returnValue) || null,
|
|
12602
|
-
canAttachFallback: true,
|
|
12603
|
-
spawnCwd: fallbackDir
|
|
12604
|
-
});
|
|
12605
|
-
}
|
|
12606
|
-
return ensureSupervisorConnected({
|
|
12607
|
-
...options,
|
|
12608
|
-
dir: fallbackDir,
|
|
12609
|
-
sock: void 0,
|
|
12610
|
-
pidPath: void 0,
|
|
12611
|
-
lockPath: void 0,
|
|
12612
|
-
_disablePluginLocalFallback: true,
|
|
12613
|
-
_lifecycleStorageMode: "plugin-cache-fallback"
|
|
12614
|
-
});
|
|
12634
|
+
return connectPluginLocalFallback({ sharedFailure: sharedProbe });
|
|
12615
12635
|
}
|
|
12616
12636
|
}
|
|
12617
12637
|
let runtime;
|
|
12618
12638
|
try {
|
|
12619
12639
|
ensureLifecycleDir(dir);
|
|
12620
12640
|
runtime = options.persistSupervisorRuntime === false ? {
|
|
12621
|
-
sourceScript:
|
|
12622
|
-
script:
|
|
12623
|
-
runtimeRoot:
|
|
12641
|
+
sourceScript: resolve6(sourceScript),
|
|
12642
|
+
script: resolve6(sourceScript),
|
|
12643
|
+
runtimeRoot: dirname5(resolve6(sourceScript)),
|
|
12624
12644
|
fingerprint: null
|
|
12625
12645
|
} : (options.materializeRuntimeImpl || materializeLifecycleSupervisorRuntime)({ sourceScript, dir });
|
|
12626
12646
|
} catch (error2) {
|
|
12627
12647
|
throw lifecycleClientError(`failed to prepare lifecycle supervisor runtime: ${error2 instanceof Error ? error2.message : String(error2)}`, filesystemFallbackDetails(error2), error2);
|
|
12628
12648
|
}
|
|
12649
|
+
const runtimePath = inspectWindowsAppContainerPath(runtime.script, {
|
|
12650
|
+
platform,
|
|
12651
|
+
realpathImpl: options.realpathImpl
|
|
12652
|
+
});
|
|
12653
|
+
if (canProbeFallback && runtimePath.redirected) {
|
|
12654
|
+
return connectPluginLocalFallback({
|
|
12655
|
+
sharedFailure: {
|
|
12656
|
+
errorKind: "policy-blocked",
|
|
12657
|
+
degradedReason: "appcontainer-path-redirected"
|
|
12658
|
+
}
|
|
12659
|
+
});
|
|
12660
|
+
}
|
|
12629
12661
|
const stalePid = readPidFile(pidPath);
|
|
12630
12662
|
if (stalePid && !isProcessAlive(stalePid)) {
|
|
12631
12663
|
tryUnlink(pidPath);
|
|
@@ -12708,7 +12740,20 @@ async function ensureSupervisorConnected(options = {}) {
|
|
|
12708
12740
|
}
|
|
12709
12741
|
await sleep(100);
|
|
12710
12742
|
}
|
|
12711
|
-
|
|
12743
|
+
if (canProbeFallback && !isProcessAlive(spawned.pid)) {
|
|
12744
|
+
return connectPluginLocalFallback({
|
|
12745
|
+
sharedFailure: {
|
|
12746
|
+
errorKind: "supervisor-start-failed",
|
|
12747
|
+
degradedReason: "supervisor-exited-before-ipc"
|
|
12748
|
+
}
|
|
12749
|
+
});
|
|
12750
|
+
}
|
|
12751
|
+
throw lifecycleClientError(`spawned supervisor (${spawned.method} pid=${spawned.pid}) but IPC not ready within ${createWaitMs}ms sock=${sock}`, {
|
|
12752
|
+
errorKind: "supervisor-unresponsive",
|
|
12753
|
+
degradedReason: "supervisor-ipc-timeout",
|
|
12754
|
+
canAttachFallback: true,
|
|
12755
|
+
spawnCwd: spawned.spawnCwd || spawnCwd || null
|
|
12756
|
+
});
|
|
12712
12757
|
} finally {
|
|
12713
12758
|
tryUnlink(bootEnvPath);
|
|
12714
12759
|
}
|
|
@@ -21049,7 +21094,7 @@ var Protocol = class {
|
|
|
21049
21094
|
return;
|
|
21050
21095
|
}
|
|
21051
21096
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
21052
|
-
await new Promise((
|
|
21097
|
+
await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
|
|
21053
21098
|
options?.signal?.throwIfAborted();
|
|
21054
21099
|
}
|
|
21055
21100
|
} catch (error2) {
|
|
@@ -21066,7 +21111,7 @@ var Protocol = class {
|
|
|
21066
21111
|
*/
|
|
21067
21112
|
request(request2, resultSchema, options) {
|
|
21068
21113
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
21069
|
-
return new Promise((
|
|
21114
|
+
return new Promise((resolve8, reject) => {
|
|
21070
21115
|
const earlyReject = (error2) => {
|
|
21071
21116
|
reject(error2);
|
|
21072
21117
|
};
|
|
@@ -21144,7 +21189,7 @@ var Protocol = class {
|
|
|
21144
21189
|
if (!parseResult.success) {
|
|
21145
21190
|
reject(parseResult.error);
|
|
21146
21191
|
} else {
|
|
21147
|
-
|
|
21192
|
+
resolve8(parseResult.data);
|
|
21148
21193
|
}
|
|
21149
21194
|
} catch (error2) {
|
|
21150
21195
|
reject(error2);
|
|
@@ -21405,12 +21450,12 @@ var Protocol = class {
|
|
|
21405
21450
|
}
|
|
21406
21451
|
} catch {
|
|
21407
21452
|
}
|
|
21408
|
-
return new Promise((
|
|
21453
|
+
return new Promise((resolve8, reject) => {
|
|
21409
21454
|
if (signal.aborted) {
|
|
21410
21455
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
21411
21456
|
return;
|
|
21412
21457
|
}
|
|
21413
|
-
const timeoutId = setTimeout(
|
|
21458
|
+
const timeoutId = setTimeout(resolve8, interval);
|
|
21414
21459
|
signal.addEventListener("abort", () => {
|
|
21415
21460
|
clearTimeout(timeoutId);
|
|
21416
21461
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -22286,19 +22331,19 @@ var StdioServerTransport = class {
|
|
|
22286
22331
|
this.onclose?.();
|
|
22287
22332
|
}
|
|
22288
22333
|
send(message) {
|
|
22289
|
-
return new Promise((
|
|
22334
|
+
return new Promise((resolve8) => {
|
|
22290
22335
|
const json = serializeMessage(message);
|
|
22291
22336
|
if (this._stdout.write(json)) {
|
|
22292
|
-
|
|
22337
|
+
resolve8();
|
|
22293
22338
|
} else {
|
|
22294
|
-
this._stdout.once("drain",
|
|
22339
|
+
this._stdout.once("drain", resolve8);
|
|
22295
22340
|
}
|
|
22296
22341
|
});
|
|
22297
22342
|
}
|
|
22298
22343
|
};
|
|
22299
22344
|
|
|
22300
22345
|
// server.mjs
|
|
22301
|
-
import { basename as
|
|
22346
|
+
import { basename as basename6, dirname as dirname6, join as join8, resolve as resolve7 } from "node:path";
|
|
22302
22347
|
|
|
22303
22348
|
// node_modules/ws/wrapper.mjs
|
|
22304
22349
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -22422,9 +22467,144 @@ function updateCursorModelPreferences(filePath, { action, target, model, effort
|
|
|
22422
22467
|
|
|
22423
22468
|
// server.mjs
|
|
22424
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
|
|
22425
22605
|
init_cursor_ensure_core();
|
|
22426
22606
|
init_lifecycle_paths();
|
|
22427
|
-
var PLUGIN_VERSION = "5.
|
|
22607
|
+
var PLUGIN_VERSION = "5.8.0";
|
|
22428
22608
|
var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
22429
22609
|
var ORIGIN = `http://localhost:${CDP_PORT2}`;
|
|
22430
22610
|
var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
|
|
@@ -22432,6 +22612,26 @@ function normalizeDelegationMode(value = process.env.CURSOR_BRIDGE_DELEGATION) {
|
|
|
22432
22612
|
return String(value || "on").trim().toLowerCase() === "off" ? "off" : "on";
|
|
22433
22613
|
}
|
|
22434
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
|
+
}
|
|
22435
22635
|
function searchResultContract() {
|
|
22436
22636
|
return [
|
|
22437
22637
|
"Return only the minimum sufficient evidence set, ordered by evidence strength. Do not pad the result with similar matches.",
|
|
@@ -22477,17 +22677,26 @@ function isConfirmedCompletedReply({ answer, snapshot = {}, sawStop = false, bas
|
|
|
22477
22677
|
const hasCompletionEvidence = sawStop || Number(snapshot.messageCount || 0) >= Number(baselineCount || 0) + 2;
|
|
22478
22678
|
return hasAssistantEvidence && hasCompletionEvidence;
|
|
22479
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
|
+
}
|
|
22480
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.";
|
|
22481
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.";
|
|
22482
22691
|
var CDP_HOST2 = "127.0.0.1";
|
|
22483
22692
|
function httpJson(path) {
|
|
22484
|
-
return new Promise((
|
|
22693
|
+
return new Promise((resolve8, reject) => {
|
|
22485
22694
|
const req = http2.get({ host: CDP_HOST2, port: CDP_PORT2, path }, (res) => {
|
|
22486
22695
|
let d = "";
|
|
22487
22696
|
res.on("data", (c) => d += c);
|
|
22488
22697
|
res.on("end", () => {
|
|
22489
22698
|
try {
|
|
22490
|
-
|
|
22699
|
+
resolve8(JSON.parse(d));
|
|
22491
22700
|
} catch {
|
|
22492
22701
|
reject(new Error("CDP returned a non-JSON response"));
|
|
22493
22702
|
}
|
|
@@ -22714,7 +22923,7 @@ function makeClient(wsUrl) {
|
|
|
22714
22923
|
}
|
|
22715
22924
|
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
22716
22925
|
async function evalJS(c, expr) {
|
|
22717
|
-
const r = await c.send("Runtime.evaluate", { expression: expr, returnByValue: true, includeCommandLineAPI: true });
|
|
22926
|
+
const r = await c.send("Runtime.evaluate", { expression: expr, returnByValue: true, includeCommandLineAPI: true, awaitPromise: true });
|
|
22718
22927
|
if (r.exceptionDetails) throw new Error("Page exception: " + (r.exceptionDetails.exception && r.exceptionDetails.exception.description || r.exceptionDetails.text));
|
|
22719
22928
|
return r.result && r.result.value;
|
|
22720
22929
|
}
|
|
@@ -22979,7 +23188,7 @@ var WORKSPACE_SECTION_BODY = `
|
|
|
22979
23188
|
};
|
|
22980
23189
|
`;
|
|
22981
23190
|
function exprCreateAgentForWorkspace(projectPath) {
|
|
22982
|
-
const workspaceLabel = JSON.stringify(
|
|
23191
|
+
const workspaceLabel = JSON.stringify(basename6(String(projectPath || "")).trim().toLowerCase());
|
|
22983
23192
|
return `(function(){
|
|
22984
23193
|
${WORKSPACE_SECTION_BODY}
|
|
22985
23194
|
const wanted=${workspaceLabel};
|
|
@@ -22995,7 +23204,7 @@ function exprCreateAgentForWorkspace(projectPath) {
|
|
|
22995
23204
|
})()`;
|
|
22996
23205
|
}
|
|
22997
23206
|
function exprInspectWorkspaceRepository(projectPath) {
|
|
22998
|
-
const workspaceLabel = JSON.stringify(
|
|
23207
|
+
const workspaceLabel = JSON.stringify(basename6(String(projectPath || "")).trim().toLowerCase());
|
|
22999
23208
|
return `(function(){
|
|
23000
23209
|
${WORKSPACE_SECTION_BODY}
|
|
23001
23210
|
const wanted=${workspaceLabel};
|
|
@@ -23066,6 +23275,7 @@ var REACT_ADAPTER_BODY = `
|
|
|
23066
23275
|
const findV2Props=()=>{
|
|
23067
23276
|
const found=[]; const seen=new Set();
|
|
23068
23277
|
let globalSelectAgent=null;
|
|
23278
|
+
let globalSelectedAgentId=null;
|
|
23069
23279
|
let sectionIdByAgentId=null;
|
|
23070
23280
|
for(const root of document.querySelectorAll('.glass-sidebar-agent-list-container')){
|
|
23071
23281
|
const nodes=[]; let n=root;
|
|
@@ -23077,7 +23287,10 @@ var REACT_ADAPTER_BODY = `
|
|
|
23077
23287
|
let f=key.startsWith('__reactFiber$')?seed:{memoizedProps:seed,return:null};
|
|
23078
23288
|
for(let j=0;f&&j<80;j++,f=f.return){
|
|
23079
23289
|
for(const p of [f.memoizedProps,f.pendingProps,f.stateNode&&f.stateNode.props]){
|
|
23080
|
-
if(p&&typeof p.onSelectAgent==='function'&&!globalSelectAgent)
|
|
23290
|
+
if(p&&typeof p.onSelectAgent==='function'&&!globalSelectAgent){
|
|
23291
|
+
globalSelectAgent=p.onSelectAgent;
|
|
23292
|
+
globalSelectedAgentId=p.selectedAgentId!==undefined?p.selectedAgentId:p.committedSelectedAgentId;
|
|
23293
|
+
}
|
|
23081
23294
|
if(p&&p.sectionIdByAgentId&&!sectionIdByAgentId)sectionIdByAgentId=p.sectionIdByAgentId;
|
|
23082
23295
|
const selectAgent=p&&(typeof p.onSelectAgent==='function'
|
|
23083
23296
|
?p.onSelectAgent
|
|
@@ -23095,6 +23308,7 @@ var REACT_ADAPTER_BODY = `
|
|
|
23095
23308
|
}
|
|
23096
23309
|
}
|
|
23097
23310
|
found.globalSelectAgent=globalSelectAgent;
|
|
23311
|
+
found.globalSelectedAgentId=globalSelectedAgentId;
|
|
23098
23312
|
found.sectionIdByAgentId=sectionIdByAgentId;
|
|
23099
23313
|
return found;
|
|
23100
23314
|
};
|
|
@@ -23104,9 +23318,10 @@ var REACT_ADAPTER_BODY = `
|
|
|
23104
23318
|
return {
|
|
23105
23319
|
kind:'agents_v2',
|
|
23106
23320
|
entries:()=>{
|
|
23107
|
-
const entries=[];const seen=new Set();let index=0;
|
|
23321
|
+
const entries=[];const seen=new Set();let index=0;
|
|
23322
|
+
let selectedRaw=String(readScalar(v2.globalSelectedAgentId)||'').replace(/^local:/,'');
|
|
23108
23323
|
for(const p of v2){
|
|
23109
|
-
const selectedId=readScalar(p.selectedAgentId);
|
|
23324
|
+
const selectedId=readScalar(v2.globalSelectedAgentId)||readScalar(p.selectedAgentId);
|
|
23110
23325
|
if(!selectedRaw&&selectedId)selectedRaw=String(selectedId).replace(/^local:/,'');
|
|
23111
23326
|
for(const header of p.section.headers){
|
|
23112
23327
|
const entry=normalizeV2(header,selectedId,index++,p.section);
|
|
@@ -23152,15 +23367,18 @@ var REACT_ADAPTER_BODY = `
|
|
|
23152
23367
|
}
|
|
23153
23368
|
return entries;
|
|
23154
23369
|
},
|
|
23155
|
-
open:id=>{
|
|
23370
|
+
open:async id=>{
|
|
23156
23371
|
const raw=String(id||'').replace(/^local:/,'');
|
|
23372
|
+
if(String(readScalar(v2.globalSelectedAgentId)||'').replace(/^local:/,'')===raw)return true;
|
|
23157
23373
|
if(v2.some(p=>String(readScalar(p.selectedAgentId)||'').replace(/^local:/,'')===raw))return true;
|
|
23158
23374
|
for(const p of v2){
|
|
23159
23375
|
const header=p.section.headers.find(h=>String(readScalar(h&&h.id)||'')===raw);
|
|
23160
|
-
if(header){p.onSelectAgent(header);
|
|
23376
|
+
if(header){return (await p.onSelectAgent(header))!==false;}
|
|
23161
23377
|
}
|
|
23162
23378
|
const registered=v2.sectionIdByAgentId&&(v2.sectionIdByAgentId instanceof Map?v2.sectionIdByAgentId.has(raw):v2.sectionIdByAgentId[raw]!==undefined);
|
|
23163
|
-
if(registered&&typeof v2.globalSelectAgent==='function'){
|
|
23379
|
+
if(registered&&typeof v2.globalSelectAgent==='function'){
|
|
23380
|
+
return (await v2.globalSelectAgent(raw,{preserveSidebarAction:true}))!==false;
|
|
23381
|
+
}
|
|
23164
23382
|
return false;
|
|
23165
23383
|
}
|
|
23166
23384
|
};
|
|
@@ -23199,9 +23417,9 @@ var EXPR_HISTORY_ENTRIES = `(function(){${REACT_ADAPTER_BODY}
|
|
|
23199
23417
|
})()`;
|
|
23200
23418
|
function exprOpenAgent(agentId) {
|
|
23201
23419
|
const id = JSON.stringify(String(agentId));
|
|
23202
|
-
return `(function(){${REACT_ADAPTER_BODY}
|
|
23420
|
+
return `(async function(){${REACT_ADAPTER_BODY}
|
|
23203
23421
|
if(!a)return 'REACT_ADAPTER_UNAVAILABLE';
|
|
23204
|
-
return a.open(${id})?'OPENED':'AGENT_NOT_FOUND';
|
|
23422
|
+
return (await a.open(${id}))?'OPENED':'AGENT_NOT_FOUND';
|
|
23205
23423
|
})()`;
|
|
23206
23424
|
}
|
|
23207
23425
|
function normalizeAllowedPath(value) {
|
|
@@ -23389,16 +23607,16 @@ function lifecycleFailureSummary(lifecycle, fallback) {
|
|
|
23389
23607
|
return [lifecycle.message || fallback, `[${diagnostic}]`, original].filter(Boolean).join(" ");
|
|
23390
23608
|
}
|
|
23391
23609
|
function releaseAdapterWorkingDirectory({ targetDir = null, chdir = process.chdir } = {}) {
|
|
23392
|
-
const target = targetDir ? ensureLifecycleDir(targetDir) :
|
|
23610
|
+
const target = targetDir ? ensureLifecycleDir(targetDir) : dirname6(process.execPath);
|
|
23393
23611
|
chdir(target);
|
|
23394
23612
|
return target;
|
|
23395
23613
|
}
|
|
23396
23614
|
var CursorBridge = class {
|
|
23397
23615
|
constructor(options = {}) {
|
|
23398
|
-
this.adapterStartCwd =
|
|
23616
|
+
this.adapterStartCwd = resolve7(options.adapterStartCwd || process.cwd());
|
|
23399
23617
|
this.environmentDelegationMode = normalizeDelegationMode(options.delegationMode || DELEGATION_MODE);
|
|
23400
23618
|
this._syncDelegationState();
|
|
23401
|
-
this.runtimeFile = options.runtimeFile === null ? null :
|
|
23619
|
+
this.runtimeFile = options.runtimeFile === null ? null : resolve7(options.runtimeFile || resolveCursorRuntimeFile());
|
|
23402
23620
|
this.runtimeModeDefault = normalizeCursorRuntimeMode(
|
|
23403
23621
|
options.runtimeModeDefault || process.env.CURSOR_BRIDGE_RUNTIME_MODE,
|
|
23404
23622
|
"normal"
|
|
@@ -23409,14 +23627,18 @@ var CursorBridge = class {
|
|
|
23409
23627
|
this.runtimeMode = normalizeCursorRuntimeMode(requestedRuntimeMode);
|
|
23410
23628
|
this.runtimeModeSource = options.runtimeMode !== void 0 ? "constructor" : persistedRuntimeMode ? "persistent" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
23411
23629
|
this.runtimeModeScope = persistedRuntimeMode ? "persistent" : options.runtimeMode !== void 0 ? "constructor" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
23412
|
-
this.workspaceFile = options.workspaceFile === null ? null :
|
|
23630
|
+
this.workspaceFile = options.workspaceFile === null ? null : resolve7(options.workspaceFile || resolveWorkspaceBindingFile());
|
|
23413
23631
|
this.workspaceKey = options.workspaceKey || resolveWorkspaceBindingKey();
|
|
23414
23632
|
const persistedWorkspace = options.projectPath === void 0 ? readWorkspaceBinding(this.workspaceFile, this.workspaceKey) : null;
|
|
23415
|
-
this.projectPath = options.projectPath !== void 0 ?
|
|
23633
|
+
this.projectPath = options.projectPath !== void 0 ? resolve7(String(options.projectPath)) : persistedWorkspace && persistedWorkspace.projectPath || null;
|
|
23416
23634
|
this.workspaceSource = options.projectPath !== void 0 ? "constructor" : persistedWorkspace ? "persistent_init" : "auto_detect";
|
|
23417
23635
|
this.workspaceUpdatedAt = persistedWorkspace && persistedWorkspace.updatedAt || null;
|
|
23418
|
-
this.modelPreferencesFile = options.modelPreferencesFile === null ? null :
|
|
23636
|
+
this.modelPreferencesFile = options.modelPreferencesFile === null ? null : resolve7(options.modelPreferencesFile || resolveCursorModelPreferencesFile());
|
|
23419
23637
|
this.modelPreferences = readCursorModelPreferences(this.modelPreferencesFile);
|
|
23638
|
+
this.sessionFile = options.sessionFile === null ? null : resolve7(options.sessionFile || resolveCursorSessionRegistryFile());
|
|
23639
|
+
this.sessionInstanceId = String(
|
|
23640
|
+
options.sessionInstanceId || `cursor-adapter-${createCursorSessionId().slice("cursor-session-".length)}`
|
|
23641
|
+
);
|
|
23420
23642
|
this._lastPresentation = null;
|
|
23421
23643
|
this.busy = false;
|
|
23422
23644
|
this.queue = [];
|
|
@@ -23443,6 +23665,348 @@ var CursorBridge = class {
|
|
|
23443
23665
|
cursorUiPreferencePreserved: true
|
|
23444
23666
|
};
|
|
23445
23667
|
}
|
|
23668
|
+
sessionRegistryView() {
|
|
23669
|
+
return {
|
|
23670
|
+
sessionStorageFile: this.sessionFile,
|
|
23671
|
+
sessionStoragePersistent: !!this.sessionFile,
|
|
23672
|
+
sessionStorageLocation: this.sessionFile ? "user-config" : "disabled"
|
|
23673
|
+
};
|
|
23674
|
+
}
|
|
23675
|
+
_readSession(sessionId) {
|
|
23676
|
+
const id = String(sessionId || "").trim();
|
|
23677
|
+
if (!id) throw cursorSessionError("SESSION_REQUIRED", "session_id must not be empty");
|
|
23678
|
+
if (!this.sessionFile) throw cursorSessionError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
|
|
23679
|
+
return readCursorSessionRegistry(this.sessionFile).sessions[id] || null;
|
|
23680
|
+
}
|
|
23681
|
+
_sessionView(session) {
|
|
23682
|
+
if (!session) return null;
|
|
23683
|
+
return {
|
|
23684
|
+
sessionId: session.id,
|
|
23685
|
+
sessionState: session.state,
|
|
23686
|
+
projectPath: session.projectPath,
|
|
23687
|
+
workspaceKey: session.workspaceKey,
|
|
23688
|
+
agentId: session.agentId || null,
|
|
23689
|
+
agentLabel: session.agentLabel || null,
|
|
23690
|
+
turnIndex: Number(session.turnIndex || 0),
|
|
23691
|
+
activeTaskId: session.activeTaskId || null,
|
|
23692
|
+
lastTask: session.lastTask || null,
|
|
23693
|
+
modelPreference: session.modelPreference || null,
|
|
23694
|
+
readOnly: session.scopeEnvelope && session.scopeEnvelope.readOnly === true,
|
|
23695
|
+
allowedPaths: session.scopeEnvelope && Array.isArray(session.scopeEnvelope.allowedPaths) ? session.scopeEnvelope.allowedPaths : [],
|
|
23696
|
+
createdAt: session.createdAt || null,
|
|
23697
|
+
updatedAt: session.updatedAt || null,
|
|
23698
|
+
closedAt: session.closedAt || null,
|
|
23699
|
+
recoveryState: session.recoveryState || null,
|
|
23700
|
+
attention: session.attention || null
|
|
23701
|
+
};
|
|
23702
|
+
}
|
|
23703
|
+
_claimNewSession({ taskId, projectPath, readOnly, allowedPaths, modelPreference, requestId, timeoutMs }) {
|
|
23704
|
+
if (!this.sessionFile) throw cursorSessionError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
|
|
23705
|
+
const sessionId = createCursorSessionId();
|
|
23706
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23707
|
+
const session = {
|
|
23708
|
+
id: sessionId,
|
|
23709
|
+
state: "creating",
|
|
23710
|
+
projectPath,
|
|
23711
|
+
workspaceKey: this.workspaceKey,
|
|
23712
|
+
agentId: null,
|
|
23713
|
+
agentLabel: null,
|
|
23714
|
+
modelPreference: modelPreference ? { ...modelPreference } : null,
|
|
23715
|
+
scopeEnvelope: { readOnly, allowedPaths: [...allowedPaths] },
|
|
23716
|
+
turnIndex: 1,
|
|
23717
|
+
epoch: 1,
|
|
23718
|
+
activeTaskId: taskId,
|
|
23719
|
+
lastRequestId: requestId || null,
|
|
23720
|
+
lastTask: null,
|
|
23721
|
+
lease: {
|
|
23722
|
+
taskId,
|
|
23723
|
+
instanceId: this.sessionInstanceId,
|
|
23724
|
+
epoch: 1,
|
|
23725
|
+
expiresAt: new Date(Date.now() + timeoutMs + 6e4).toISOString()
|
|
23726
|
+
},
|
|
23727
|
+
createdAt: now,
|
|
23728
|
+
updatedAt: now,
|
|
23729
|
+
closedAt: null,
|
|
23730
|
+
recoveryState: null,
|
|
23731
|
+
attention: null
|
|
23732
|
+
};
|
|
23733
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23734
|
+
if (registry2.sessions[sessionId]) throw cursorSessionError("SESSION_ID_COLLISION", sessionId);
|
|
23735
|
+
registry2.sessions[sessionId] = session;
|
|
23736
|
+
}, { now });
|
|
23737
|
+
return session;
|
|
23738
|
+
}
|
|
23739
|
+
_claimExistingSession({ sessionId, taskId, projectPath, readOnly, allowedPaths, requestId, timeoutMs }) {
|
|
23740
|
+
if (!this.sessionFile) throw cursorSessionError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
|
|
23741
|
+
const nowMs = Date.now();
|
|
23742
|
+
let expiredLease = false;
|
|
23743
|
+
const result = updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23744
|
+
const session = registry2.sessions[sessionId];
|
|
23745
|
+
if (!session) throw cursorSessionError("SESSION_NOT_FOUND", sessionId);
|
|
23746
|
+
if (requestId && session.lastRequestId === requestId) {
|
|
23747
|
+
return { duplicate: true, session: { ...session } };
|
|
23748
|
+
}
|
|
23749
|
+
if (!sameSessionProject(session.projectPath, projectPath)) {
|
|
23750
|
+
throw cursorSessionError("SESSION_WORKSPACE_MISMATCH", `session is bound to ${session.projectPath}`);
|
|
23751
|
+
}
|
|
23752
|
+
if (session.state === "closed") throw cursorSessionError("SESSION_CLOSED", sessionId);
|
|
23753
|
+
const leaseExpiresAt = Date.parse(session.lease && session.lease.expiresAt || "");
|
|
23754
|
+
if (session.activeTaskId || session.state === "busy" || session.state === "creating") {
|
|
23755
|
+
if (Number.isFinite(leaseExpiresAt) && leaseExpiresAt <= nowMs) {
|
|
23756
|
+
expiredLease = true;
|
|
23757
|
+
session.state = "needs_attention";
|
|
23758
|
+
session.recoveryState = "expired_sender_lease";
|
|
23759
|
+
session.attention = "The previous sender lease expired. Reconcile the exact Cursor Agent before continuing; no prompt was resent.";
|
|
23760
|
+
session.updatedAt = new Date(nowMs).toISOString();
|
|
23761
|
+
return { duplicate: false, session: { ...session } };
|
|
23762
|
+
}
|
|
23763
|
+
throw cursorSessionError("SESSION_BUSY", `active task ${session.activeTaskId || "unknown"}`);
|
|
23764
|
+
}
|
|
23765
|
+
if (session.state !== "ready") {
|
|
23766
|
+
throw cursorSessionError("SESSION_NOT_READY", `state=${session.state}; recovery=${session.recoveryState || "none"}`);
|
|
23767
|
+
}
|
|
23768
|
+
if (!session.agentId) throw cursorSessionError("SESSION_AGENT_NOT_BOUND", sessionId);
|
|
23769
|
+
const envelope = session.scopeEnvelope || { readOnly: false, allowedPaths: [] };
|
|
23770
|
+
if (envelope.readOnly === true) {
|
|
23771
|
+
if (readOnly !== true || allowedPaths.length > 0) {
|
|
23772
|
+
throw cursorSessionError("SESSION_SCOPE_EXPANSION", "a read-only session must remain explicitly read-only");
|
|
23773
|
+
}
|
|
23774
|
+
} else {
|
|
23775
|
+
if (readOnly === true || allowedPaths.length === 0) {
|
|
23776
|
+
throw cursorSessionError("SESSION_SCOPE_REQUIRED", "a writable continuation must repeat an allowed_paths subset");
|
|
23777
|
+
}
|
|
23778
|
+
const outside = allowedPaths.find((candidate) => !(envelope.allowedPaths || []).some((parent) => sessionPathContains(parent, candidate)));
|
|
23779
|
+
if (outside) throw cursorSessionError("SESSION_SCOPE_EXPANSION", `${outside} is outside the session envelope`);
|
|
23780
|
+
}
|
|
23781
|
+
const epoch = Number(session.epoch || 0) + 1;
|
|
23782
|
+
session.state = "busy";
|
|
23783
|
+
session.turnIndex = Number(session.turnIndex || 0) + 1;
|
|
23784
|
+
session.epoch = epoch;
|
|
23785
|
+
session.activeTaskId = taskId;
|
|
23786
|
+
session.lastRequestId = requestId || null;
|
|
23787
|
+
session.lease = {
|
|
23788
|
+
taskId,
|
|
23789
|
+
instanceId: this.sessionInstanceId,
|
|
23790
|
+
epoch,
|
|
23791
|
+
expiresAt: new Date(nowMs + timeoutMs + 6e4).toISOString()
|
|
23792
|
+
};
|
|
23793
|
+
session.updatedAt = new Date(nowMs).toISOString();
|
|
23794
|
+
session.recoveryState = null;
|
|
23795
|
+
session.attention = null;
|
|
23796
|
+
return { duplicate: false, session: { ...session } };
|
|
23797
|
+
}, { now: new Date(nowMs).toISOString() });
|
|
23798
|
+
if (expiredLease) {
|
|
23799
|
+
throw cursorSessionError("SESSION_RECONCILE_REQUIRED", "the previous sender lease expired; no prompt was sent");
|
|
23800
|
+
}
|
|
23801
|
+
return result;
|
|
23802
|
+
}
|
|
23803
|
+
_bindSessionAgent(job) {
|
|
23804
|
+
if (!job || !job.sessionId || !job.agentId || !this.sessionFile) return;
|
|
23805
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23806
|
+
const session = registry2.sessions[job.sessionId];
|
|
23807
|
+
if (!session || session.activeTaskId !== job.id || Number(session.epoch) !== Number(job.sessionEpoch)) return;
|
|
23808
|
+
session.agentId = job.agentId;
|
|
23809
|
+
session.agentLabel = job.agentLabel || session.agentLabel || null;
|
|
23810
|
+
session.state = "busy";
|
|
23811
|
+
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23812
|
+
});
|
|
23813
|
+
job.sessionState = "busy";
|
|
23814
|
+
}
|
|
23815
|
+
_settleSessionJob(job, outcome, options = {}) {
|
|
23816
|
+
if (!job || !job.sessionId || !this.sessionFile) return;
|
|
23817
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23818
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23819
|
+
const session = registry2.sessions[job.sessionId];
|
|
23820
|
+
if (!session || session.activeTaskId !== job.id || Number(session.epoch) !== Number(job.sessionEpoch)) return;
|
|
23821
|
+
if (job.agentId) session.agentId = job.agentId;
|
|
23822
|
+
if (job.agentLabel) session.agentLabel = job.agentLabel;
|
|
23823
|
+
session.lastTask = {
|
|
23824
|
+
taskId: job.id,
|
|
23825
|
+
turnIndex: job.sessionTurn,
|
|
23826
|
+
status: outcome,
|
|
23827
|
+
finishedAt: job.finishedAt || now,
|
|
23828
|
+
error: job.error || null,
|
|
23829
|
+
terminalEvidence: job.terminalEvidence || null,
|
|
23830
|
+
resultUnavailable: job.resultUnavailable === true
|
|
23831
|
+
};
|
|
23832
|
+
session.updatedAt = now;
|
|
23833
|
+
if (options.needsAttention === true) {
|
|
23834
|
+
session.state = "needs_attention";
|
|
23835
|
+
session.recoveryState = job.recoveryState || outcome;
|
|
23836
|
+
session.attention = options.attention || job.error || "The exact Cursor Agent requires reconciliation before another turn.";
|
|
23837
|
+
return;
|
|
23838
|
+
}
|
|
23839
|
+
session.activeTaskId = null;
|
|
23840
|
+
session.lease = null;
|
|
23841
|
+
session.recoveryState = null;
|
|
23842
|
+
session.attention = null;
|
|
23843
|
+
session.state = session.agentId ? "ready" : "failed";
|
|
23844
|
+
}, { now });
|
|
23845
|
+
job.sessionState = options.needsAttention === true ? "needs_attention" : job.agentId ? "ready" : "failed";
|
|
23846
|
+
}
|
|
23847
|
+
_safeSettleSessionJob(job, outcome, options = {}) {
|
|
23848
|
+
try {
|
|
23849
|
+
this._settleSessionJob(job, outcome, options);
|
|
23850
|
+
} catch (error2) {
|
|
23851
|
+
job.sessionState = "needs_attention";
|
|
23852
|
+
job.sessionError = error2 instanceof Error ? error2.message : String(error2);
|
|
23853
|
+
}
|
|
23854
|
+
}
|
|
23855
|
+
sessionStatus(sessionId) {
|
|
23856
|
+
const session = this._readSession(sessionId);
|
|
23857
|
+
return {
|
|
23858
|
+
found: !!session,
|
|
23859
|
+
...this.sessionRegistryView(),
|
|
23860
|
+
...session ? this._sessionView(session) : { sessionId: String(sessionId || "") }
|
|
23861
|
+
};
|
|
23862
|
+
}
|
|
23863
|
+
async _reconcileSession(sessionId) {
|
|
23864
|
+
const initial = this._readSession(sessionId);
|
|
23865
|
+
if (!initial) return { found: false, action: "reconcile", sessionId };
|
|
23866
|
+
if (initial.state === "ready" || initial.state === "closed" || initial.state === "failed") {
|
|
23867
|
+
return { found: true, changed: false, action: "reconcile", ...this._sessionView(initial) };
|
|
23868
|
+
}
|
|
23869
|
+
if (!initial.agentId) {
|
|
23870
|
+
return {
|
|
23871
|
+
found: true,
|
|
23872
|
+
changed: false,
|
|
23873
|
+
action: "reconcile",
|
|
23874
|
+
state: "agent_missing",
|
|
23875
|
+
attention: "No exact agentId is available. Confirm the Cursor task state manually before explicit abandon.",
|
|
23876
|
+
...this._sessionView(initial)
|
|
23877
|
+
};
|
|
23878
|
+
}
|
|
23879
|
+
const projectPath = this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath || null;
|
|
23880
|
+
if (!sameSessionProject(initial.projectPath, projectPath)) {
|
|
23881
|
+
throw cursorSessionError("SESSION_WORKSPACE_MISMATCH", `session is bound to ${initial.projectPath}`);
|
|
23882
|
+
}
|
|
23883
|
+
await this._ensureCursor();
|
|
23884
|
+
const probe = {
|
|
23885
|
+
agentId: initial.agentId,
|
|
23886
|
+
targetId: this._lastLifecycle && this._lastLifecycle.targetId,
|
|
23887
|
+
execution: "parallel_agent",
|
|
23888
|
+
effectiveExecution: "parallel_agent",
|
|
23889
|
+
lastRecoveryAt: null,
|
|
23890
|
+
recoveryState: null,
|
|
23891
|
+
error: null
|
|
23892
|
+
};
|
|
23893
|
+
const observed = await this._readStableParallelEntry(probe);
|
|
23894
|
+
let result;
|
|
23895
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23896
|
+
const session = registry2.sessions[sessionId];
|
|
23897
|
+
if (!session) {
|
|
23898
|
+
result = { found: false, action: "reconcile", sessionId };
|
|
23899
|
+
return;
|
|
23900
|
+
}
|
|
23901
|
+
if (Number(session.epoch || 0) !== Number(initial.epoch || 0) || session.activeTaskId !== initial.activeTaskId) {
|
|
23902
|
+
result = { found: true, changed: false, action: "reconcile", state: "stale_observation", ...this._sessionView(session) };
|
|
23903
|
+
return;
|
|
23904
|
+
}
|
|
23905
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23906
|
+
if (!observed.stable || !observed.entry) {
|
|
23907
|
+
session.state = "needs_attention";
|
|
23908
|
+
session.recoveryState = probe.recoveryState || "history_unavailable";
|
|
23909
|
+
session.attention = observed.error || "The exact Agent state is not stable.";
|
|
23910
|
+
session.updatedAt = now;
|
|
23911
|
+
result = { found: true, changed: false, action: "reconcile", state: session.recoveryState, ...this._sessionView(session) };
|
|
23912
|
+
return;
|
|
23913
|
+
}
|
|
23914
|
+
const entry = observed.entry;
|
|
23915
|
+
if (entry.showSpinner) {
|
|
23916
|
+
session.state = "needs_attention";
|
|
23917
|
+
session.recoveryState = "running_unowned";
|
|
23918
|
+
session.attention = "The exact Cursor Agent is still running without an attached adapter. Do not submit another turn.";
|
|
23919
|
+
session.updatedAt = now;
|
|
23920
|
+
result = { found: true, changed: false, action: "reconcile", state: "running_unowned", ...this._sessionView(session) };
|
|
23921
|
+
return;
|
|
23922
|
+
}
|
|
23923
|
+
const terminalClass = classifyParallelTerminalIcon(entry.icon);
|
|
23924
|
+
if (!["completed", "cancelled", "failed"].includes(terminalClass)) {
|
|
23925
|
+
session.state = "needs_attention";
|
|
23926
|
+
session.recoveryState = terminalClass === "needs_attention" ? "cursor_needs_attention" : "idle_unconfirmed";
|
|
23927
|
+
session.attention = `The exact Cursor Agent has no confirmed terminal state: ${entry.icon || "unknown"}`;
|
|
23928
|
+
session.updatedAt = now;
|
|
23929
|
+
result = { found: true, changed: false, action: "reconcile", state: session.recoveryState, ...this._sessionView(session) };
|
|
23930
|
+
return;
|
|
23931
|
+
}
|
|
23932
|
+
session.lastTask = {
|
|
23933
|
+
taskId: session.activeTaskId || session.lastTask && session.lastTask.taskId || null,
|
|
23934
|
+
turnIndex: session.turnIndex,
|
|
23935
|
+
status: terminalClass,
|
|
23936
|
+
finishedAt: now,
|
|
23937
|
+
error: terminalClass === "failed" ? `stable_history_icon:${entry.icon}` : null,
|
|
23938
|
+
terminalEvidence: `stable_history_icon:${entry.icon}`,
|
|
23939
|
+
resultUnavailable: terminalClass === "completed"
|
|
23940
|
+
};
|
|
23941
|
+
session.activeTaskId = null;
|
|
23942
|
+
session.lease = null;
|
|
23943
|
+
session.state = "ready";
|
|
23944
|
+
session.recoveryState = terminalClass === "completed" ? "reconciled_result_uncollected" : null;
|
|
23945
|
+
session.attention = terminalClass === "completed" ? "The interrupted turn completed in Cursor, but its reply was not persisted. Inspect that Agent before the next turn." : null;
|
|
23946
|
+
session.updatedAt = now;
|
|
23947
|
+
result = { found: true, changed: true, action: "reconcile", state: terminalClass, ...this._sessionView(session) };
|
|
23948
|
+
});
|
|
23949
|
+
return result;
|
|
23950
|
+
}
|
|
23951
|
+
async sessionControl(sessionId, options = {}) {
|
|
23952
|
+
const id = String(sessionId || "").trim();
|
|
23953
|
+
if (!id) throw cursorSessionError("SESSION_REQUIRED", "session_id must not be empty");
|
|
23954
|
+
const action = String(options.action || "").trim().toLowerCase();
|
|
23955
|
+
if (!["reconcile", "close", "forget", "abandon"].includes(action)) {
|
|
23956
|
+
throw cursorSessionError("SESSION_ACTION_UNSUPPORTED", "expected reconcile, close, forget, or abandon");
|
|
23957
|
+
}
|
|
23958
|
+
if (action === "reconcile") return this._reconcileSession(id);
|
|
23959
|
+
let result;
|
|
23960
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23961
|
+
const session = registry2.sessions[id];
|
|
23962
|
+
if (!session) {
|
|
23963
|
+
result = { found: false, sessionId: id, action };
|
|
23964
|
+
return;
|
|
23965
|
+
}
|
|
23966
|
+
if (action === "forget") {
|
|
23967
|
+
if (options.confirm !== true) throw cursorSessionError("SESSION_CONFIRM_REQUIRED", "forget requires confirm=true");
|
|
23968
|
+
if (session.state !== "closed") throw cursorSessionError("SESSION_NOT_CLOSED", "close the session before forgetting it");
|
|
23969
|
+
delete registry2.sessions[id];
|
|
23970
|
+
result = { found: true, changed: true, action, sessionId: id, state: "forgotten" };
|
|
23971
|
+
return;
|
|
23972
|
+
}
|
|
23973
|
+
if (action === "abandon") {
|
|
23974
|
+
if (options.confirm !== true || options.acknowledgeMayStillWrite !== true || !String(options.reason || "").trim()) {
|
|
23975
|
+
throw cursorSessionError("SESSION_ABANDON_CONFIRM_REQUIRED", "abandon requires confirm=true, acknowledge_may_still_write=true, and a non-empty reason");
|
|
23976
|
+
}
|
|
23977
|
+
session.lastTask = {
|
|
23978
|
+
taskId: session.activeTaskId || session.lastTask && session.lastTask.taskId || null,
|
|
23979
|
+
turnIndex: session.turnIndex,
|
|
23980
|
+
status: "abandoned",
|
|
23981
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23982
|
+
error: String(options.reason).trim(),
|
|
23983
|
+
terminalEvidence: "explicit_session_abandon_acknowledgement",
|
|
23984
|
+
resultUnavailable: true
|
|
23985
|
+
};
|
|
23986
|
+
session.activeTaskId = null;
|
|
23987
|
+
session.lease = null;
|
|
23988
|
+
session.state = "closed";
|
|
23989
|
+
session.closedAt = session.lastTask.finishedAt;
|
|
23990
|
+
session.updatedAt = session.closedAt;
|
|
23991
|
+
session.recoveryState = "released_unconfirmed";
|
|
23992
|
+
session.attention = "The Bridge mapping was closed without proving that the Cursor Agent stopped; it may still run or write.";
|
|
23993
|
+
result = { found: true, changed: true, action, warning: session.attention, ...this._sessionView(session) };
|
|
23994
|
+
return;
|
|
23995
|
+
}
|
|
23996
|
+
if (session.activeTaskId || ["busy", "creating", "needs_attention"].includes(session.state)) {
|
|
23997
|
+
throw cursorSessionError("SESSION_CLOSE_BLOCKED", "an active or uncertain Agent must be resolved before close");
|
|
23998
|
+
}
|
|
23999
|
+
if (session.state === "closed") {
|
|
24000
|
+
result = { found: true, changed: false, action, ...this._sessionView(session) };
|
|
24001
|
+
return;
|
|
24002
|
+
}
|
|
24003
|
+
session.state = "closed";
|
|
24004
|
+
session.closedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24005
|
+
session.updatedAt = session.closedAt;
|
|
24006
|
+
result = { found: true, changed: true, action, ...this._sessionView(session) };
|
|
24007
|
+
});
|
|
24008
|
+
return result;
|
|
24009
|
+
}
|
|
23446
24010
|
async initializeWorkspace(projectPath) {
|
|
23447
24011
|
if (this.busy || this.activeParallel.size > 0 || this.queue.length > 0) {
|
|
23448
24012
|
throw new Error("cursor_init cannot change workspace while Cursor tasks are queued or running");
|
|
@@ -23674,11 +24238,28 @@ var CursorBridge = class {
|
|
|
23674
24238
|
if (this._hasGlobalReservation()) {
|
|
23675
24239
|
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");
|
|
23676
24240
|
}
|
|
23677
|
-
|
|
23678
|
-
|
|
24241
|
+
const sessionMode = normalizeCursorSessionMode(options.sessionMode, "isolated");
|
|
24242
|
+
if (!sessionMode) {
|
|
24243
|
+
throw cursorSessionError("SESSION_MODE_INVALID", `expected ${CURSOR_SESSION_MODES.join(", ")}`);
|
|
24244
|
+
}
|
|
24245
|
+
const sessionId = String(options.sessionId || "").trim();
|
|
24246
|
+
if (sessionMode === "continue" && !sessionId) {
|
|
24247
|
+
throw cursorSessionError("SESSION_REQUIRED", "session_mode=continue requires session_id");
|
|
24248
|
+
}
|
|
24249
|
+
if (sessionMode !== "continue" && sessionId) {
|
|
24250
|
+
throw cursorSessionError("SESSION_ID_UNEXPECTED", "session_id is accepted only with session_mode=continue");
|
|
24251
|
+
}
|
|
24252
|
+
const requestId = normalizeSessionRequestId(options.requestId);
|
|
24253
|
+
if (sessionMode === "isolated" && requestId) {
|
|
24254
|
+
throw cursorSessionError("SESSION_REQUEST_ID_UNEXPECTED", "request_id is accepted only for a persistent session turn");
|
|
24255
|
+
}
|
|
24256
|
+
const execution = String(options.execution || (sessionMode === "isolated" ? "fifo" : "parallel_agent"));
|
|
23679
24257
|
if (execution !== "fifo" && execution !== "parallel_agent") {
|
|
23680
24258
|
throw new Error(`Unsupported execution value ${execution}; expected fifo or parallel_agent`);
|
|
23681
24259
|
}
|
|
24260
|
+
if (sessionMode !== "isolated" && execution !== "parallel_agent") {
|
|
24261
|
+
throw cursorSessionError("SESSION_EXECUTION_INVALID", "persistent sessions require execution=parallel_agent and never fall back to FIFO");
|
|
24262
|
+
}
|
|
23682
24263
|
const readOnly = options.readOnly === true;
|
|
23683
24264
|
const timeoutMs = Math.max(3e4, Math.min(9e5, Number(options.timeoutMs || 6e5)));
|
|
23684
24265
|
const allowedPaths = Array.isArray(options.allowedPaths) ? options.allowedPaths.map((x) => String(x).trim()).filter(Boolean) : [];
|
|
@@ -23692,6 +24273,14 @@ var CursorBridge = class {
|
|
|
23692
24273
|
this._validateParallelAllowedPaths(allowedPaths);
|
|
23693
24274
|
this._assertNoParallelPathConflict(allowedPaths);
|
|
23694
24275
|
}
|
|
24276
|
+
if (sessionMode === "continue" && options.readOnlySpecified !== true && options.allowedPathsSpecified !== true) {
|
|
24277
|
+
throw cursorSessionError("SESSION_SCOPE_REQUIRED", "repeat read_only=true or an allowed_paths subset for every continued turn");
|
|
24278
|
+
}
|
|
24279
|
+
await this._ensureCursor();
|
|
24280
|
+
const projectPath = this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath || null;
|
|
24281
|
+
if (sessionMode !== "isolated" && !projectPath) {
|
|
24282
|
+
throw cursorSessionError("SESSION_WORKSPACE_REQUIRED", "initialize one workspace before creating or continuing a session");
|
|
24283
|
+
}
|
|
23695
24284
|
const contract = String(options.completionContract || "").trim();
|
|
23696
24285
|
let fullPrompt = text + DO_LANGUAGE_CONTRACT;
|
|
23697
24286
|
if (readOnly) fullPrompt += "\n\nRead-only boundary: Do not modify, create, or delete files, and do not run commands that change workspace state.";
|
|
@@ -23699,14 +24288,56 @@ var CursorBridge = class {
|
|
|
23699
24288
|
fullPrompt += "\n\nAllowed modification scope (do not cross this boundary):\n" + allowedPaths.map((x) => "- " + x).join("\n");
|
|
23700
24289
|
}
|
|
23701
24290
|
fullPrompt += contract ? "\n\nAcceptance and reporting contract:\n" + contract : DO_DEFAULT_CONTRACT;
|
|
24291
|
+
const taskId = this._nextTaskId();
|
|
24292
|
+
let session = null;
|
|
24293
|
+
let duplicate = false;
|
|
24294
|
+
let modelPreference = this._modelPreferenceFor("cursor_do");
|
|
24295
|
+
if (sessionMode === "create") {
|
|
24296
|
+
session = this._claimNewSession({
|
|
24297
|
+
taskId,
|
|
24298
|
+
projectPath,
|
|
24299
|
+
readOnly,
|
|
24300
|
+
allowedPaths,
|
|
24301
|
+
modelPreference,
|
|
24302
|
+
requestId,
|
|
24303
|
+
timeoutMs
|
|
24304
|
+
});
|
|
24305
|
+
} else if (sessionMode === "continue") {
|
|
24306
|
+
const claimed = this._claimExistingSession({
|
|
24307
|
+
sessionId,
|
|
24308
|
+
taskId,
|
|
24309
|
+
projectPath,
|
|
24310
|
+
readOnly,
|
|
24311
|
+
allowedPaths,
|
|
24312
|
+
requestId,
|
|
24313
|
+
timeoutMs
|
|
24314
|
+
});
|
|
24315
|
+
session = claimed.session;
|
|
24316
|
+
duplicate = claimed.duplicate === true;
|
|
24317
|
+
modelPreference = session.modelPreference || null;
|
|
24318
|
+
if (duplicate) {
|
|
24319
|
+
const existing = this.tasks.get(session.activeTaskId || session.lastTask && session.lastTask.taskId || "");
|
|
24320
|
+
return existing ? { duplicate: true, ...this._taskView(existing, true) } : { duplicate: true, ...this._sessionView(session) };
|
|
24321
|
+
}
|
|
24322
|
+
}
|
|
23702
24323
|
const job = this._enqueue("do", fullPrompt, {
|
|
24324
|
+
taskId,
|
|
23703
24325
|
timeoutMs,
|
|
23704
|
-
newChat:
|
|
24326
|
+
newChat: sessionMode !== "continue",
|
|
23705
24327
|
execution,
|
|
23706
24328
|
readOnly,
|
|
23707
24329
|
allowedPaths,
|
|
23708
24330
|
preferLegacyUi: options.preferLegacyUi === true,
|
|
23709
|
-
modelPreference
|
|
24331
|
+
modelPreference,
|
|
24332
|
+
projectPath,
|
|
24333
|
+
sessionMode,
|
|
24334
|
+
sessionId: session && session.id || null,
|
|
24335
|
+
sessionTurn: session && session.turnIndex || null,
|
|
24336
|
+
sessionEpoch: session && session.epoch || null,
|
|
24337
|
+
sessionState: session && session.state || null,
|
|
24338
|
+
requestId,
|
|
24339
|
+
agentId: sessionMode === "continue" ? session && session.agentId : null,
|
|
24340
|
+
agentLabel: sessionMode === "continue" ? session && session.agentLabel : null
|
|
23710
24341
|
});
|
|
23711
24342
|
if (options.background !== false) return this._taskView(job);
|
|
23712
24343
|
await job.promise;
|
|
@@ -23735,12 +24366,15 @@ var CursorBridge = class {
|
|
|
23735
24366
|
}
|
|
23736
24367
|
}
|
|
23737
24368
|
}
|
|
24369
|
+
_nextTaskId() {
|
|
24370
|
+
return `cursor-${Date.now().toString(36)}-${this.nextTaskId++}`;
|
|
24371
|
+
}
|
|
23738
24372
|
_enqueue(kind, prompt, options) {
|
|
23739
|
-
const id =
|
|
24373
|
+
const id = options.taskId || this._nextTaskId();
|
|
23740
24374
|
let resolvePromise;
|
|
23741
24375
|
let rejectPromise;
|
|
23742
|
-
const promise = new Promise((
|
|
23743
|
-
resolvePromise =
|
|
24376
|
+
const promise = new Promise((resolve8, reject) => {
|
|
24377
|
+
resolvePromise = resolve8;
|
|
23744
24378
|
rejectPromise = reject;
|
|
23745
24379
|
});
|
|
23746
24380
|
promise.catch(() => {
|
|
@@ -23759,15 +24393,22 @@ var CursorBridge = class {
|
|
|
23759
24393
|
modelPreference: options.modelPreference ? { ...options.modelPreference } : null,
|
|
23760
24394
|
modelSelection: options.modelPreference ? { configured: true, applied: false, ...options.modelPreference } : null,
|
|
23761
24395
|
projectPath: options.projectPath || this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath || null,
|
|
24396
|
+
sessionMode: options.sessionMode || "isolated",
|
|
24397
|
+
sessionId: options.sessionId || null,
|
|
24398
|
+
sessionTurn: options.sessionTurn || null,
|
|
24399
|
+
sessionEpoch: options.sessionEpoch || null,
|
|
24400
|
+
sessionState: options.sessionState || null,
|
|
24401
|
+
requestId: options.requestId || null,
|
|
24402
|
+
responseBaseline: null,
|
|
23762
24403
|
status: "queued",
|
|
23763
24404
|
phase: "queued",
|
|
23764
24405
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23765
24406
|
startedAt: null,
|
|
23766
24407
|
finishedAt: null,
|
|
23767
24408
|
sentAt: null,
|
|
23768
|
-
agentId: null,
|
|
24409
|
+
agentId: options.agentId || null,
|
|
23769
24410
|
provisionalAgentId: null,
|
|
23770
|
-
agentLabel: null,
|
|
24411
|
+
agentLabel: options.agentLabel || null,
|
|
23771
24412
|
targetId: null,
|
|
23772
24413
|
targetUiFlavor: null,
|
|
23773
24414
|
fallbackReason: null,
|
|
@@ -23808,6 +24449,7 @@ var CursorBridge = class {
|
|
|
23808
24449
|
job.recoveryState = null;
|
|
23809
24450
|
job.cancelRequested = false;
|
|
23810
24451
|
job.resultUnavailable = false;
|
|
24452
|
+
this._safeSettleSessionJob(job, "completed");
|
|
23811
24453
|
if (!job.settled) {
|
|
23812
24454
|
job.settled = true;
|
|
23813
24455
|
job.resolve(result);
|
|
@@ -23825,6 +24467,7 @@ var CursorBridge = class {
|
|
|
23825
24467
|
job.recoveryState = null;
|
|
23826
24468
|
job.cancelRequested = false;
|
|
23827
24469
|
job.reservationScope = null;
|
|
24470
|
+
this._safeSettleSessionJob(job, "failed");
|
|
23828
24471
|
if (!job.settled) {
|
|
23829
24472
|
job.settled = true;
|
|
23830
24473
|
job.reject(e);
|
|
@@ -23845,6 +24488,10 @@ var CursorBridge = class {
|
|
|
23845
24488
|
job.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23846
24489
|
job.recoveryState = options.underlyingStopConfirmed === true ? "stopped" : "released_unconfirmed";
|
|
23847
24490
|
job.reservationScope = null;
|
|
24491
|
+
this._safeSettleSessionJob(job, "cancelled", {
|
|
24492
|
+
needsAttention: options.underlyingStopConfirmed !== true && job.sendState === "sent",
|
|
24493
|
+
attention: "Cancellation did not prove that the exact Cursor Agent stopped."
|
|
24494
|
+
});
|
|
23848
24495
|
if (!job.settled) {
|
|
23849
24496
|
job.settled = true;
|
|
23850
24497
|
job.reject(new Error(message));
|
|
@@ -23864,6 +24511,7 @@ var CursorBridge = class {
|
|
|
23864
24511
|
job.recoveryState = "unverified";
|
|
23865
24512
|
job.reservationScope = job.execution === "parallel_agent" && job.agentId ? job.readOnly ? "agent" : "paths" : "global";
|
|
23866
24513
|
this.activeParallel.set(job.id, job);
|
|
24514
|
+
this._safeSettleSessionJob(job, "needs_attention", { needsAttention: true });
|
|
23867
24515
|
if (!job.settled) {
|
|
23868
24516
|
job.settled = true;
|
|
23869
24517
|
job.reject(e);
|
|
@@ -23982,8 +24630,14 @@ var CursorBridge = class {
|
|
|
23982
24630
|
try {
|
|
23983
24631
|
if (job.effectiveExecution === "parallel_agent") {
|
|
23984
24632
|
job.phase = "submitting";
|
|
23985
|
-
const submitted = await this._withUiLock(() => this._submitParallelAgent(job));
|
|
24633
|
+
const submitted = await this._withUiLock(() => job.sessionMode === "continue" ? this._submitSessionTurn(job) : this._submitParallelAgent(job));
|
|
23986
24634
|
if (submitted.fallbackReason) {
|
|
24635
|
+
if (job.sessionMode !== "isolated") {
|
|
24636
|
+
throw cursorSessionError(
|
|
24637
|
+
"SESSION_CONTINUITY_UNAVAILABLE",
|
|
24638
|
+
`${submitted.fallbackReason}; persistent sessions never fall back to FIFO`
|
|
24639
|
+
);
|
|
24640
|
+
}
|
|
23987
24641
|
job.effectiveExecution = "fifo";
|
|
23988
24642
|
job.fallbackReason = submitted.fallbackReason;
|
|
23989
24643
|
if (this.activeParallel.size > 0) {
|
|
@@ -23998,10 +24652,16 @@ var CursorBridge = class {
|
|
|
23998
24652
|
} else {
|
|
23999
24653
|
job.agentId = submitted.agent.id;
|
|
24000
24654
|
job.agentLabel = submitted.agent.label || null;
|
|
24655
|
+
try {
|
|
24656
|
+
this._bindSessionAgent(job);
|
|
24657
|
+
} catch (error2) {
|
|
24658
|
+
if (job.sendState === "sent" || job.sentAt) error2.sent = true;
|
|
24659
|
+
throw error2;
|
|
24660
|
+
}
|
|
24001
24661
|
job.sentAt = job.sentAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
24002
24662
|
job.phase = "running";
|
|
24003
24663
|
job.reservationScope = job.readOnly ? "agent" : "paths";
|
|
24004
|
-
if (!this.parallelRestoreAgentId && submitted.previousSelectedId) {
|
|
24664
|
+
if (shouldScheduleParallelOriginRestore(job) && !this.parallelRestoreAgentId && submitted.previousSelectedId) {
|
|
24005
24665
|
this.parallelRestoreAgentId = submitted.previousSelectedId;
|
|
24006
24666
|
this.parallelRestoreTargetId = job.targetId;
|
|
24007
24667
|
}
|
|
@@ -24317,7 +24977,7 @@ var CursorBridge = class {
|
|
|
24317
24977
|
applied: true,
|
|
24318
24978
|
requestedModel,
|
|
24319
24979
|
requestedEffort,
|
|
24320
|
-
effectiveModel:
|
|
24980
|
+
effectiveModel: modelRow.text || trigger.text,
|
|
24321
24981
|
effectiveEffort,
|
|
24322
24982
|
pickerDetail: trigger.detail || null,
|
|
24323
24983
|
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -24347,7 +25007,7 @@ var CursorBridge = class {
|
|
|
24347
25007
|
const created = JSON.parse(await evalJS(c, exprCreateAgentForWorkspace(options.projectPath)) || "{}");
|
|
24348
25008
|
if (!created.ok) {
|
|
24349
25009
|
const available = Array.isArray(created.available) ? `; available=${created.available.join(", ")}` : "";
|
|
24350
|
-
throw new Error(`Cursor Agents workspace binding failed: ${created.state || "unknown"}; wanted=${created.wanted ||
|
|
25010
|
+
throw new Error(`Cursor Agents workspace binding failed: ${created.state || "unknown"}; wanted=${created.wanted || basename6(options.projectPath)}${available}`);
|
|
24351
25011
|
}
|
|
24352
25012
|
await sleep2(1100);
|
|
24353
25013
|
return true;
|
|
@@ -24627,6 +25287,96 @@ var CursorBridge = class {
|
|
|
24627
25287
|
c.close();
|
|
24628
25288
|
}
|
|
24629
25289
|
}
|
|
25290
|
+
async _readResponseSnapshot(c) {
|
|
25291
|
+
let snapshot = { messageCount: 0, replyLength: 0, replyHash: 0, stop: 0 };
|
|
25292
|
+
try {
|
|
25293
|
+
snapshot = JSON.parse(await evalJS(c, EXPR_SNAP) || "{}");
|
|
25294
|
+
} catch {
|
|
25295
|
+
}
|
|
25296
|
+
return {
|
|
25297
|
+
messageCount: Number(snapshot.messageCount || 0),
|
|
25298
|
+
replyLength: Number(snapshot.replyLength || 0),
|
|
25299
|
+
replyHash: Number(snapshot.replyHash || 0),
|
|
25300
|
+
stop: Number(snapshot.stop || 0)
|
|
25301
|
+
};
|
|
25302
|
+
}
|
|
25303
|
+
async _captureSessionResponseBaseline(c, timeoutMs = 15e3, pollMs = 250) {
|
|
25304
|
+
const deadline = Date.now() + timeoutMs;
|
|
25305
|
+
let lastKey = "";
|
|
25306
|
+
while (Date.now() < deadline) {
|
|
25307
|
+
const snapshot = await this._readResponseSnapshot(c);
|
|
25308
|
+
const key = snapshot.messageCount > 0 && snapshot.replyLength > 0 && snapshot.stop === 0 ? `${snapshot.messageCount}:${snapshot.replyLength}:${snapshot.replyHash}` : "";
|
|
25309
|
+
if (key && key === lastKey) {
|
|
25310
|
+
return {
|
|
25311
|
+
messageCount: snapshot.messageCount,
|
|
25312
|
+
replyLength: snapshot.replyLength,
|
|
25313
|
+
replyHash: snapshot.replyHash
|
|
25314
|
+
};
|
|
25315
|
+
}
|
|
25316
|
+
lastKey = key;
|
|
25317
|
+
await sleep2(pollMs);
|
|
25318
|
+
}
|
|
25319
|
+
throw cursorSessionError("SESSION_BASELINE_UNAVAILABLE", "the previous completed reply did not hydrate to a stable snapshot before continuation");
|
|
25320
|
+
}
|
|
25321
|
+
async _submitSessionTurn(job) {
|
|
25322
|
+
if (!job.agentId) throw cursorSessionError("SESSION_AGENT_NOT_BOUND", job.sessionId || "unknown session");
|
|
25323
|
+
const page = await findPage({
|
|
25324
|
+
targetId: this._lastLifecycle && this._lastLifecycle.targetId,
|
|
25325
|
+
purpose: "parallel_agent",
|
|
25326
|
+
preferAgentsV2: true
|
|
25327
|
+
});
|
|
25328
|
+
job.targetId = page.id;
|
|
25329
|
+
job.targetUiFlavor = page.capabilities && page.capabilities.uiFlavor || null;
|
|
25330
|
+
const c = makeClient(page.webSocketDebuggerUrl);
|
|
25331
|
+
await c.ready;
|
|
25332
|
+
let sent = false;
|
|
25333
|
+
try {
|
|
25334
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25335
|
+
await this._ensureChatPanel(c);
|
|
25336
|
+
const entries = await this._readAgentEntries(c, true);
|
|
25337
|
+
const target = entries.find((entry) => entry && entry.id === job.agentId);
|
|
25338
|
+
if (!target) {
|
|
25339
|
+
throw cursorSessionError("SESSION_AGENT_NOT_FOUND", `Agent History does not contain ${job.agentId}`);
|
|
25340
|
+
}
|
|
25341
|
+
const previousSelectedId = (entries.find((entry) => entry.isSelected) || {}).id || null;
|
|
25342
|
+
if (!target.isSelected) {
|
|
25343
|
+
const opened = await evalJS(c, exprOpenAgent(job.agentId));
|
|
25344
|
+
if (opened !== "OPENED") {
|
|
25345
|
+
throw cursorSessionError("SESSION_AGENT_OPEN_FAILED", `${job.agentId}: ${opened}`);
|
|
25346
|
+
}
|
|
25347
|
+
}
|
|
25348
|
+
await this._closeHistory(c);
|
|
25349
|
+
await this._waitForSelectedAgent(c, job.agentId);
|
|
25350
|
+
await this._ensureChatPanel(c);
|
|
25351
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25352
|
+
await this._applyModelPreference(c, job.modelPreference, job);
|
|
25353
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25354
|
+
job.responseBaseline = await this._captureSessionResponseBaseline(c);
|
|
25355
|
+
const providerErrorBaseline = providerErrorSignature(await this._readProviderError(c));
|
|
25356
|
+
const filled = await evalJS(c, exprFill(job.prompt));
|
|
25357
|
+
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") {
|
|
25358
|
+
throw cursorSessionError("SESSION_INPUT_FAILED", "failed to enter the continued task");
|
|
25359
|
+
}
|
|
25360
|
+
await sleep2(350);
|
|
25361
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25362
|
+
job.sendState = "dispatching";
|
|
25363
|
+
await chord(c, 0, "Enter", "Enter", 13);
|
|
25364
|
+
await this._confirmSubmission(c, job.responseBaseline.messageCount, providerErrorBaseline);
|
|
25365
|
+
sent = true;
|
|
25366
|
+
job.sendState = "sent";
|
|
25367
|
+
job.sentAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25368
|
+
return {
|
|
25369
|
+
agent: { id: job.agentId, label: target.label || job.agentLabel || null },
|
|
25370
|
+
previousSelectedId
|
|
25371
|
+
};
|
|
25372
|
+
} catch (error2) {
|
|
25373
|
+
if (sent) error2.sent = true;
|
|
25374
|
+
throw error2;
|
|
25375
|
+
} finally {
|
|
25376
|
+
await this._closeHistory(c);
|
|
25377
|
+
c.close();
|
|
25378
|
+
}
|
|
25379
|
+
}
|
|
24630
25380
|
async _readParallelEntry(job) {
|
|
24631
25381
|
return this._withUiLock(async () => {
|
|
24632
25382
|
const page = await findPage({ targetId: job.targetId, purpose: "parallel_agent" });
|
|
@@ -24779,13 +25529,27 @@ var CursorBridge = class {
|
|
|
24779
25529
|
const detail = lastCollectionError ? `; last collection error: ${lastCollectionError}` : "";
|
|
24780
25530
|
throw new Error(`Cursor parallel_agent task timed out (${job.timeoutMs}ms)${detail}`);
|
|
24781
25531
|
}
|
|
24782
|
-
async
|
|
25532
|
+
async _requestExactAgentSelection(c, agentId) {
|
|
25533
|
+
if (!await this._ensureHistoryOpen(c)) return "REACT_ADAPTER_UNAVAILABLE";
|
|
25534
|
+
try {
|
|
25535
|
+
return await evalJS(c, exprOpenAgent(agentId));
|
|
25536
|
+
} finally {
|
|
25537
|
+
await this._closeHistory(c);
|
|
25538
|
+
}
|
|
25539
|
+
}
|
|
25540
|
+
async _waitForSelectedAgent(c, agentId, timeoutMs = 15e3, retryDelayMs = 2500, pollMs = 250) {
|
|
24783
25541
|
const deadline = Date.now() + timeoutMs;
|
|
25542
|
+
const retryAt = Date.now() + retryDelayMs;
|
|
25543
|
+
let retried = false;
|
|
24784
25544
|
while (Date.now() < deadline) {
|
|
24785
25545
|
const entries = await this._readAgentEntries(c);
|
|
24786
25546
|
const target = entries.find((e) => e.id === agentId);
|
|
24787
25547
|
if (target && target.isSelected) return true;
|
|
24788
|
-
|
|
25548
|
+
if (!retried && Date.now() >= retryAt) {
|
|
25549
|
+
retried = true;
|
|
25550
|
+
await this._requestExactAgentSelection(c, agentId);
|
|
25551
|
+
}
|
|
25552
|
+
await sleep2(pollMs);
|
|
24789
25553
|
}
|
|
24790
25554
|
throw new Error(`After opening ${agentId}, it could not be confirmed as the selected Agent`);
|
|
24791
25555
|
}
|
|
@@ -24805,13 +25569,10 @@ var CursorBridge = class {
|
|
|
24805
25569
|
let lastKey = "";
|
|
24806
25570
|
let stable = 0;
|
|
24807
25571
|
for (let i = 0; i < 80; i++) {
|
|
24808
|
-
|
|
24809
|
-
try {
|
|
24810
|
-
snap = JSON.parse(await evalJS(c, EXPR_SNAP));
|
|
24811
|
-
} catch {
|
|
24812
|
-
}
|
|
25572
|
+
const snap = await this._readResponseSnapshot(c);
|
|
24813
25573
|
const candidate = String(await evalJS(c, EXPR_EXTRACT) || "").trim();
|
|
24814
|
-
|
|
25574
|
+
const hasNewTurn = isSessionTurnReplyReady(job.responseBaseline, snap);
|
|
25575
|
+
if (candidate && hasNewTurn && Number(snap.stop || 0) === 0) {
|
|
24815
25576
|
const key = `${snap.replyLength}:${snap.replyHash}`;
|
|
24816
25577
|
if (key === lastKey) stable++;
|
|
24817
25578
|
else {
|
|
@@ -24852,6 +25613,7 @@ var CursorBridge = class {
|
|
|
24852
25613
|
job.recoveryState = "terminal_result_uncollected";
|
|
24853
25614
|
job.reservationScope = uncertainSubmissionReservationScope(job, error2);
|
|
24854
25615
|
job.recoveryState = error2.recoveryState || "monitoring_uncertain_submission";
|
|
25616
|
+
this._safeSettleSessionJob(job, "terminal_result_uncollected", { needsAttention: true });
|
|
24855
25617
|
}
|
|
24856
25618
|
_abandonJob(job, reason) {
|
|
24857
25619
|
if (isTerminalTask(job)) return this._taskView(job, true);
|
|
@@ -24868,6 +25630,10 @@ var CursorBridge = class {
|
|
|
24868
25630
|
job.terminalEvidence = "explicit_abandon_acknowledgement";
|
|
24869
25631
|
job.recoveryState = "released_unconfirmed";
|
|
24870
25632
|
job.reservationScope = null;
|
|
25633
|
+
this._safeSettleSessionJob(job, "abandoned", {
|
|
25634
|
+
needsAttention: true,
|
|
25635
|
+
attention: "The Bridge released the task reservation without proving that the Cursor Agent stopped. This session cannot continue safely."
|
|
25636
|
+
});
|
|
24871
25637
|
if (!job.settled) {
|
|
24872
25638
|
job.settled = true;
|
|
24873
25639
|
job.reject(new Error(message));
|
|
@@ -25403,6 +26169,12 @@ var CursorBridge = class {
|
|
|
25403
26169
|
modelPreference: job.modelPreference,
|
|
25404
26170
|
modelSelection: job.modelSelection,
|
|
25405
26171
|
projectPath: job.projectPath,
|
|
26172
|
+
sessionMode: job.sessionMode || "isolated",
|
|
26173
|
+
sessionId: job.sessionId || null,
|
|
26174
|
+
sessionTurn: job.sessionTurn || null,
|
|
26175
|
+
sessionState: job.sessionState || null,
|
|
26176
|
+
sessionError: job.sessionError || null,
|
|
26177
|
+
requestId: job.requestId || null,
|
|
25406
26178
|
agentId: job.agentId,
|
|
25407
26179
|
provisionalAgentId: job.provisionalAgentId || null,
|
|
25408
26180
|
agentLabel: job.agentLabel,
|
|
@@ -25451,8 +26223,8 @@ var CursorBridge = class {
|
|
|
25451
26223
|
async status(taskId = "") {
|
|
25452
26224
|
if (taskId) {
|
|
25453
26225
|
const job = this.tasks.get(String(taskId));
|
|
25454
|
-
if (!job) return { found: false, taskId: String(taskId), ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView() };
|
|
25455
|
-
return { found: true, ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this._taskView(job, true) };
|
|
26226
|
+
if (!job) return { found: false, taskId: String(taskId), ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this.sessionRegistryView() };
|
|
26227
|
+
return { found: true, ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this.sessionRegistryView(), ...this._taskView(job, true) };
|
|
25456
26228
|
}
|
|
25457
26229
|
const parallelRunning = this.activeParallel.size;
|
|
25458
26230
|
const uiBusy = this.busy;
|
|
@@ -25464,6 +26236,7 @@ var CursorBridge = class {
|
|
|
25464
26236
|
...this.delegationView(),
|
|
25465
26237
|
...this.runtimeModeView(),
|
|
25466
26238
|
...this.modelPreferencesView(),
|
|
26239
|
+
...this.sessionRegistryView(),
|
|
25467
26240
|
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0,
|
|
25468
26241
|
uiBusy,
|
|
25469
26242
|
parallelRunning,
|
|
@@ -25531,7 +26304,7 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25531
26304
|
},
|
|
25532
26305
|
bridgeInstance.environmentDelegationMode !== "off" ? {
|
|
25533
26306
|
name: "cursor_do",
|
|
25534
|
-
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.",
|
|
26307
|
+
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.",
|
|
25535
26308
|
inputSchema: {
|
|
25536
26309
|
type: "object",
|
|
25537
26310
|
properties: {
|
|
@@ -25541,7 +26314,10 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25541
26314
|
read_only: { type: "boolean", default: false, description: "Set true when Cursor must not change the workspace." },
|
|
25542
26315
|
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." },
|
|
25543
26316
|
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." },
|
|
25544
|
-
completion_contract: { type: "string", description: "Optional acceptance checks or a required final-report format." }
|
|
26317
|
+
completion_contract: { type: "string", description: "Optional acceptance checks or a required final-report format." },
|
|
26318
|
+
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." },
|
|
26319
|
+
session_id: { type: "string", description: "Required only for session_mode=continue. This stable Bridge ID is not a Cursor agentId." },
|
|
26320
|
+
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." }
|
|
25545
26321
|
},
|
|
25546
26322
|
required: ["prompt"]
|
|
25547
26323
|
}
|
|
@@ -25562,6 +26338,21 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25562
26338
|
required: ["task_id", "action"]
|
|
25563
26339
|
}
|
|
25564
26340
|
},
|
|
26341
|
+
{
|
|
26342
|
+
name: "cursor_session_control",
|
|
26343
|
+
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.",
|
|
26344
|
+
inputSchema: {
|
|
26345
|
+
type: "object",
|
|
26346
|
+
properties: {
|
|
26347
|
+
session_id: { type: "string", description: "The exact session ID returned by cursor_do(session_mode=create)." },
|
|
26348
|
+
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." },
|
|
26349
|
+
confirm: { type: "boolean", default: false, description: "Required for forget and abandon." },
|
|
26350
|
+
reason: { type: "string", description: "Required and non-empty for abandon." },
|
|
26351
|
+
acknowledge_may_still_write: { type: "boolean", default: false, description: "Required for abandon; acknowledges that the underlying Cursor Agent may still run or write." }
|
|
26352
|
+
},
|
|
26353
|
+
required: ["session_id", "action"]
|
|
26354
|
+
}
|
|
26355
|
+
},
|
|
25565
26356
|
{
|
|
25566
26357
|
name: "cursor_runtime",
|
|
25567
26358
|
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.",
|
|
@@ -25575,7 +26366,7 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25575
26366
|
},
|
|
25576
26367
|
{
|
|
25577
26368
|
name: "cursor_model",
|
|
25578
|
-
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.",
|
|
26369
|
+
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.",
|
|
25579
26370
|
inputSchema: {
|
|
25580
26371
|
type: "object",
|
|
25581
26372
|
properties: {
|
|
@@ -25589,8 +26380,14 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
25589
26380
|
},
|
|
25590
26381
|
{
|
|
25591
26382
|
name: "cursor_status",
|
|
25592
|
-
description: "Read-only snapshot of Cursor connectivity, queued/running work, reservations, execution availability, persistent model/effort defaults, and normal/minimal runtime presentation. Pass
|
|
25593
|
-
inputSchema: {
|
|
26383
|
+
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.",
|
|
26384
|
+
inputSchema: {
|
|
26385
|
+
type: "object",
|
|
26386
|
+
properties: {
|
|
26387
|
+
task_id: { type: "string", description: "A task ID returned by cursor_do." },
|
|
26388
|
+
session_id: { type: "string", description: "A persistent session ID returned by cursor_do(session_mode=create)." }
|
|
26389
|
+
}
|
|
26390
|
+
}
|
|
25594
26391
|
}
|
|
25595
26392
|
].filter(Boolean);
|
|
25596
26393
|
}
|
|
@@ -25636,7 +26433,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
25636
26433
|
readOnly: !!(args && args.read_only),
|
|
25637
26434
|
timeoutMs: args && args.timeout_ms,
|
|
25638
26435
|
allowedPaths: args && args.allowed_paths,
|
|
25639
|
-
completionContract: args && args.completion_contract
|
|
26436
|
+
completionContract: args && args.completion_contract,
|
|
26437
|
+
sessionMode: args && args.session_mode,
|
|
26438
|
+
sessionId: args && args.session_id,
|
|
26439
|
+
requestId: args && args.request_id,
|
|
26440
|
+
readOnlySpecified: !!(args && Object.hasOwn(args, "read_only")),
|
|
26441
|
+
allowedPathsSpecified: !!(args && Object.hasOwn(args, "allowed_paths"))
|
|
25640
26442
|
});
|
|
25641
26443
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
25642
26444
|
}
|
|
@@ -25650,6 +26452,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
25650
26452
|
});
|
|
25651
26453
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
25652
26454
|
}
|
|
26455
|
+
if (name === "cursor_session_control") {
|
|
26456
|
+
const result = await bridge.sessionControl(args && args.session_id, {
|
|
26457
|
+
action: args && args.action,
|
|
26458
|
+
confirm: !!(args && args.confirm),
|
|
26459
|
+
reason: args && args.reason,
|
|
26460
|
+
acknowledgeMayStillWrite: !!(args && args.acknowledge_may_still_write)
|
|
26461
|
+
});
|
|
26462
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
26463
|
+
}
|
|
25653
26464
|
if (name === "cursor_runtime") {
|
|
25654
26465
|
const mode = args && args.mode;
|
|
25655
26466
|
const action = args && args.action;
|
|
@@ -25672,11 +26483,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
25672
26483
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
25673
26484
|
}
|
|
25674
26485
|
if (name === "cursor_status") {
|
|
26486
|
+
if (args && args.task_id && args.session_id) {
|
|
26487
|
+
throw cursorSessionError("STATUS_SELECTOR_AMBIGUOUS", "pass task_id or session_id, not both");
|
|
26488
|
+
}
|
|
25675
26489
|
const statusMs = Math.max(1e3, Number(process.env.CURSOR_BRIDGE_STATUS_TIMEOUT || 8e3));
|
|
25676
26490
|
let result;
|
|
25677
26491
|
try {
|
|
25678
26492
|
result = await Promise.race([
|
|
25679
|
-
bridge.status(args && args.task_id),
|
|
26493
|
+
args && args.session_id ? Promise.resolve(bridge.sessionStatus(args.session_id)) : bridge.status(args && args.task_id),
|
|
25680
26494
|
new Promise((_, reject) => setTimeout(() => reject(new Error(`cursor_status_timeout_${statusMs}`)), statusMs))
|
|
25681
26495
|
]);
|
|
25682
26496
|
} catch (error2) {
|
|
@@ -25687,7 +26501,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
25687
26501
|
error: error2 instanceof Error ? error2.message : String(error2),
|
|
25688
26502
|
...bridge.workspaceView(),
|
|
25689
26503
|
...bridge.delegationView(),
|
|
25690
|
-
...bridge.runtimeModeView()
|
|
26504
|
+
...bridge.runtimeModeView(),
|
|
26505
|
+
...bridge.sessionRegistryView()
|
|
25691
26506
|
};
|
|
25692
26507
|
}
|
|
25693
26508
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -25757,6 +26572,7 @@ export {
|
|
|
25757
26572
|
isBlankAgentsWindow,
|
|
25758
26573
|
isConfirmedCompletedReply,
|
|
25759
26574
|
isDurablyRegisteredParallelEntry,
|
|
26575
|
+
isSessionTurnReplyReady,
|
|
25760
26576
|
isTargetedStopConfirmed,
|
|
25761
26577
|
lifecycleFailureSummary,
|
|
25762
26578
|
normalizeAllowedPath,
|
|
@@ -25779,6 +26595,7 @@ export {
|
|
|
25779
26595
|
selectUniqueNewAgentEntry,
|
|
25780
26596
|
shouldAutoLaunchCursor,
|
|
25781
26597
|
shouldRecoverNormalAgentsPresentation,
|
|
26598
|
+
shouldScheduleParallelOriginRestore,
|
|
25782
26599
|
summarizeCdpPages,
|
|
25783
26600
|
uncertainSubmissionReservationScope,
|
|
25784
26601
|
updateStableEntryObservation
|