pi-cursor-bridge 0.1.9 → 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.
@@ -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 = resolve7.call(this, root, ref);
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 resolve7(root, ref) {
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 resolve7(baseURI, relativeURI, options) {
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: resolve7,
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 homedir5 } from "node:os";
11363
- import { basename as basename3, extname as extname2, join as join5, resolve as resolve4, win32 as winPath, posix as posixPath } from "node:path";
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 || join5(homedir5(), ".codex", "state_5.sqlite");
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) ? resolve4(candidate) : null;
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 resolve4(explicit);
11411
+ if (explicit) return resolve5(explicit);
11412
11412
  const persisted = String(options.persistedProjectPath || "").trim();
11413
- if (persisted) return resolve4(normalizeCodexThreadCwd(persisted));
11413
+ if (persisted) return resolve5(normalizeCodexThreadCwd(persisted));
11414
11414
  const threadProjectPath = options.threadProjectPath === void 0 ? resolveCodexThreadProjectPath(options) : options.threadProjectPath;
11415
- if (threadProjectPath) return resolve4(normalizeCodexThreadCwd(threadProjectPath));
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 resolve4(cwd);
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 || join5(homedir5(), "AppData", "Local");
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 || homedir5();
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((resolve7) => {
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
- resolve7(res.statusCode === 200);
11522
+ resolve8(res.statusCode === 200);
11523
11523
  });
11524
- req.on("error", () => resolve7(false));
11524
+ req.on("error", () => resolve8(false));
11525
11525
  req.setTimeout(timeoutMs, () => {
11526
11526
  try {
11527
11527
  req.destroy();
11528
11528
  } catch {
11529
11529
  }
11530
- resolve7(false);
11530
+ resolve8(false);
11531
11531
  });
11532
11532
  });
11533
11533
  }
11534
11534
  function cdpIsCursor(timeoutMs = 1500) {
11535
- return new Promise((resolve7) => {
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 resolve7(false);
11542
- resolve7(/[\/\\]cursor[\/\\](resources|app)|cursor\.exe|vscode-app[^"]*[\/\\]cursor[\/\\]/i.test(d));
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
- resolve7(false);
11544
+ resolve8(false);
11545
11545
  }
11546
11546
  });
11547
11547
  });
11548
- req.on("error", () => resolve7(false));
11548
+ req.on("error", () => resolve8(false));
11549
11549
  req.setTimeout(timeoutMs, () => {
11550
11550
  try {
11551
11551
  req.destroy();
11552
11552
  } catch {
11553
11553
  }
11554
- resolve7(false);
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 ? resolve4(String(options.projectPath)) : null : resolveProjectPath();
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 ? resolve4(String(projectPath)).replace(/\\/g, "/").toLowerCase() : "";
11949
+ return projectPath ? resolve5(String(projectPath)).replace(/\\/g, "/").toLowerCase() : "";
11950
11950
  }
11951
11951
  function targetTitleMatchesProject(title, projectPath) {
11952
- const name = basename3(String(projectPath || "")).trim().toLowerCase();
11952
+ const name = basename4(String(projectPath || "")).trim().toLowerCase();
11953
11953
  if (!name) return false;
11954
11954
  const extension2 = extname2(name);
11955
11955
  const candidates = [...new Set([name, extension2 ? name.slice(0, -extension2.length) : name].filter(Boolean))];
@@ -12255,17 +12255,17 @@ import net from "node:net";
12255
12255
  import { createHash as createHash2 } from "node:crypto";
12256
12256
  import {
12257
12257
  existsSync as existsSync4,
12258
- mkdirSync as mkdirSync5,
12259
- readFileSync as readFileSync4,
12258
+ mkdirSync as mkdirSync6,
12259
+ readFileSync as readFileSync5,
12260
12260
  realpathSync,
12261
12261
  readdirSync,
12262
- renameSync as renameSync4,
12263
- rmSync as rmSync4,
12262
+ renameSync as renameSync5,
12263
+ rmSync as rmSync5,
12264
12264
  unlinkSync,
12265
- writeFileSync as writeFileSync4
12265
+ writeFileSync as writeFileSync5
12266
12266
  } from "node:fs";
12267
12267
  import { fileURLToPath } from "node:url";
12268
- import { basename as basename4, dirname as dirname4, join as join6, resolve as resolve5, win32 as win32Path } from "node:path";
12268
+ import { basename as basename5, dirname as dirname5, join as join7, resolve as resolve6, win32 as win32Path } from "node:path";
12269
12269
  function resolveSupervisorSpawnCwd({
12270
12270
  requestedCwd = null,
12271
12271
  runtimeRoot = null,
@@ -12280,9 +12280,9 @@ function resolveSupervisorSpawnCwd({
12280
12280
  return runtimeRoot;
12281
12281
  }
12282
12282
  function resolvePluginLocalLifecycleDir(sourceScript) {
12283
- const scriptDir = dirname4(resolve5(sourceScript));
12284
- const pluginRoot = basename4(scriptDir).toLowerCase() === "dist" ? dirname4(scriptDir) : scriptDir;
12285
- return join6(pluginRoot, ".cursor-bridge-lifecycle");
12283
+ const scriptDir = dirname5(resolve6(sourceScript));
12284
+ const pluginRoot = basename5(scriptDir).toLowerCase() === "dist" ? dirname5(scriptDir) : scriptDir;
12285
+ return join7(pluginRoot, ".cursor-bridge-lifecycle");
12286
12286
  }
12287
12287
  function inspectWindowsAppContainerPath(target, options = {}) {
12288
12288
  const platform = options.platform || process.platform;
@@ -12325,59 +12325,59 @@ function sleep(ms) {
12325
12325
  }
12326
12326
  function resolveSupervisorScript() {
12327
12327
  if (process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT && existsSync4(process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT)) {
12328
- return resolve5(process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT);
12328
+ return resolve6(process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT);
12329
12329
  }
12330
- const here = dirname4(fileURLToPath(import.meta.url));
12330
+ const here = dirname5(fileURLToPath(import.meta.url));
12331
12331
  const candidates = [];
12332
12332
  if (typeof process.argv[1] === "string") {
12333
- const entryDir = dirname4(resolve5(process.argv[1]));
12334
- candidates.push(join6(entryDir, "dist", "cursor-lifecycle-supervisor.mjs"));
12335
- candidates.push(join6(entryDir, "cursor-lifecycle-supervisor.mjs"));
12333
+ const entryDir = dirname5(resolve6(process.argv[1]));
12334
+ candidates.push(join7(entryDir, "dist", "cursor-lifecycle-supervisor.mjs"));
12335
+ candidates.push(join7(entryDir, "cursor-lifecycle-supervisor.mjs"));
12336
12336
  }
12337
12337
  candidates.push(
12338
- join6(here, "dist", "cursor-lifecycle-supervisor.mjs"),
12339
- join6(here, "cursor-lifecycle-supervisor.mjs")
12338
+ join7(here, "dist", "cursor-lifecycle-supervisor.mjs"),
12339
+ join7(here, "cursor-lifecycle-supervisor.mjs")
12340
12340
  );
12341
12341
  for (const c of candidates) {
12342
12342
  if (existsSync4(c)) return c;
12343
12343
  }
12344
- return join6(here, "cursor-lifecycle-supervisor.mjs");
12344
+ return join7(here, "cursor-lifecycle-supervisor.mjs");
12345
12345
  }
12346
12346
  function writeRuntimeFile(target, content) {
12347
- if (existsSync4(target) && readFileSync4(target).equals(content)) return;
12347
+ if (existsSync4(target) && readFileSync5(target).equals(content)) return;
12348
12348
  const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
12349
- writeFileSync4(temporary, content);
12349
+ writeFileSync5(temporary, content);
12350
12350
  try {
12351
- renameSync4(temporary, target);
12351
+ renameSync5(temporary, target);
12352
12352
  } catch (error2) {
12353
12353
  if (!existsSync4(target)) {
12354
- rmSync4(temporary, { force: true });
12354
+ rmSync5(temporary, { force: true });
12355
12355
  throw error2;
12356
12356
  }
12357
- if (readFileSync4(target).equals(content)) {
12358
- rmSync4(temporary, { force: true });
12357
+ if (readFileSync5(target).equals(content)) {
12358
+ rmSync5(temporary, { force: true });
12359
12359
  return;
12360
12360
  }
12361
- rmSync4(target, { force: true });
12362
- renameSync4(temporary, target);
12361
+ rmSync5(target, { force: true });
12362
+ renameSync5(temporary, target);
12363
12363
  }
12364
12364
  }
12365
12365
  function materializeLifecycleSupervisorRuntime({ sourceScript, dir = defaultLifecycleDir() } = {}) {
12366
12366
  const described = describeLifecycleSupervisorRuntime({ sourceScript, dir });
12367
12367
  const { sourceScript: source, content, fingerprint } = described;
12368
- const runtimeRoot = join6(ensureLifecycleDir(dir), "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
12369
- mkdirSync5(runtimeRoot, { recursive: true });
12370
- const script = join6(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
12368
+ const runtimeRoot = join7(ensureLifecycleDir(dir), "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
12369
+ mkdirSync6(runtimeRoot, { recursive: true });
12370
+ const script = join7(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
12371
12371
  writeRuntimeFile(script, content);
12372
12372
  return { sourceScript: source, script, runtimeRoot, fingerprint };
12373
12373
  }
12374
12374
  function describeLifecycleSupervisorRuntime({ sourceScript, dir = defaultLifecycleDir() } = {}) {
12375
- const source = resolve5(sourceScript || resolveSupervisorScript());
12375
+ const source = resolve6(sourceScript || resolveSupervisorScript());
12376
12376
  if (!existsSync4(source)) throw new Error(`lifecycle supervisor script missing: ${source}`);
12377
- const content = readFileSync4(source);
12377
+ const content = readFileSync5(source);
12378
12378
  const fingerprint = createHash2("sha256").update(content).digest("hex");
12379
- const runtimeRoot = join6(dir, "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
12380
- const script = join6(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
12379
+ const runtimeRoot = join7(dir, "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
12380
+ const script = join7(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
12381
12381
  return { sourceScript: source, script, runtimeRoot, fingerprint, content };
12382
12382
  }
12383
12383
  function isProcessAlive(pid) {
@@ -12391,7 +12391,7 @@ function isProcessAlive(pid) {
12391
12391
  }
12392
12392
  function readPidFile(pidPath) {
12393
12393
  try {
12394
- const n = Number(String(readFileSync4(pidPath, "utf8")).trim());
12394
+ const n = Number(String(readFileSync5(pidPath, "utf8")).trim());
12395
12395
  return Number.isFinite(n) ? n : null;
12396
12396
  } catch {
12397
12397
  return null;
@@ -12506,7 +12506,7 @@ function tryUnlink(path) {
12506
12506
  }
12507
12507
  }
12508
12508
  function writeBootEnv(dir, extra = {}) {
12509
- const bootPath = join6(dir, `boot-env-${process.pid}-${Date.now()}.json`);
12509
+ const bootPath = join7(dir, `boot-env-${process.pid}-${Date.now()}.json`);
12510
12510
  const payload = { ...extra };
12511
12511
  for (const [key, value] of Object.entries(process.env)) {
12512
12512
  if (key.startsWith("CURSOR_BRIDGE_") || key === "CURSOR_PROJECT_PATH" || key === "CURSOR_EXE") {
@@ -12517,7 +12517,7 @@ function writeBootEnv(dir, extra = {}) {
12517
12517
  for (const [k, v] of Object.entries(payload)) {
12518
12518
  if (v != null && v !== "") cleaned[k] = String(v);
12519
12519
  }
12520
- writeFileSync4(bootPath, `${JSON.stringify(cleaned, null, 2)}
12520
+ writeFileSync5(bootPath, `${JSON.stringify(cleaned, null, 2)}
12521
12521
  `, { encoding: "utf8" });
12522
12522
  return bootPath;
12523
12523
  }
@@ -12552,9 +12552,9 @@ async function ensureSupervisorConnected(options = {}) {
12552
12552
  let targetRuntime = null;
12553
12553
  try {
12554
12554
  targetRuntime = options.persistSupervisorRuntime === false ? {
12555
- sourceScript: resolve5(sourceScript),
12556
- script: resolve5(sourceScript),
12557
- runtimeRoot: dirname4(resolve5(sourceScript)),
12555
+ sourceScript: resolve6(sourceScript),
12556
+ script: resolve6(sourceScript),
12557
+ runtimeRoot: dirname5(resolve6(sourceScript)),
12558
12558
  fingerprint: null
12559
12559
  } : describeLifecycleSupervisorRuntime({ sourceScript, dir });
12560
12560
  } catch {
@@ -12638,9 +12638,9 @@ async function ensureSupervisorConnected(options = {}) {
12638
12638
  try {
12639
12639
  ensureLifecycleDir(dir);
12640
12640
  runtime = options.persistSupervisorRuntime === false ? {
12641
- sourceScript: resolve5(sourceScript),
12642
- script: resolve5(sourceScript),
12643
- runtimeRoot: dirname4(resolve5(sourceScript)),
12641
+ sourceScript: resolve6(sourceScript),
12642
+ script: resolve6(sourceScript),
12643
+ runtimeRoot: dirname5(resolve6(sourceScript)),
12644
12644
  fingerprint: null
12645
12645
  } : (options.materializeRuntimeImpl || materializeLifecycleSupervisorRuntime)({ sourceScript, dir });
12646
12646
  } catch (error2) {
@@ -21094,7 +21094,7 @@ var Protocol = class {
21094
21094
  return;
21095
21095
  }
21096
21096
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
21097
- await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
21097
+ await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
21098
21098
  options?.signal?.throwIfAborted();
21099
21099
  }
21100
21100
  } catch (error2) {
@@ -21111,7 +21111,7 @@ var Protocol = class {
21111
21111
  */
21112
21112
  request(request2, resultSchema, options) {
21113
21113
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
21114
- return new Promise((resolve7, reject) => {
21114
+ return new Promise((resolve8, reject) => {
21115
21115
  const earlyReject = (error2) => {
21116
21116
  reject(error2);
21117
21117
  };
@@ -21189,7 +21189,7 @@ var Protocol = class {
21189
21189
  if (!parseResult.success) {
21190
21190
  reject(parseResult.error);
21191
21191
  } else {
21192
- resolve7(parseResult.data);
21192
+ resolve8(parseResult.data);
21193
21193
  }
21194
21194
  } catch (error2) {
21195
21195
  reject(error2);
@@ -21450,12 +21450,12 @@ var Protocol = class {
21450
21450
  }
21451
21451
  } catch {
21452
21452
  }
21453
- return new Promise((resolve7, reject) => {
21453
+ return new Promise((resolve8, reject) => {
21454
21454
  if (signal.aborted) {
21455
21455
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
21456
21456
  return;
21457
21457
  }
21458
- const timeoutId = setTimeout(resolve7, interval);
21458
+ const timeoutId = setTimeout(resolve8, interval);
21459
21459
  signal.addEventListener("abort", () => {
21460
21460
  clearTimeout(timeoutId);
21461
21461
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -22331,19 +22331,19 @@ var StdioServerTransport = class {
22331
22331
  this.onclose?.();
22332
22332
  }
22333
22333
  send(message) {
22334
- return new Promise((resolve7) => {
22334
+ return new Promise((resolve8) => {
22335
22335
  const json = serializeMessage(message);
22336
22336
  if (this._stdout.write(json)) {
22337
- resolve7();
22337
+ resolve8();
22338
22338
  } else {
22339
- this._stdout.once("drain", resolve7);
22339
+ this._stdout.once("drain", resolve8);
22340
22340
  }
22341
22341
  });
22342
22342
  }
22343
22343
  };
22344
22344
 
22345
22345
  // server.mjs
22346
- import { basename as basename5, dirname as dirname5, join as join7, resolve as resolve6 } from "node:path";
22346
+ import { basename as basename6, dirname as dirname6, join as join8, resolve as resolve7 } from "node:path";
22347
22347
 
22348
22348
  // node_modules/ws/wrapper.mjs
22349
22349
  var import_stream = __toESM(require_stream(), 1);
@@ -22467,9 +22467,144 @@ function updateCursorModelPreferences(filePath, { action, target, model, effort
22467
22467
 
22468
22468
  // server.mjs
22469
22469
  init_workspace_binding();
22470
+
22471
+ // cursor-session-registry.mjs
22472
+ import { randomUUID } from "node:crypto";
22473
+ import {
22474
+ closeSync,
22475
+ mkdirSync as mkdirSync5,
22476
+ openSync,
22477
+ readFileSync as readFileSync4,
22478
+ renameSync as renameSync4,
22479
+ rmSync as rmSync4,
22480
+ statSync as statSync2,
22481
+ writeFileSync as writeFileSync4
22482
+ } from "node:fs";
22483
+ import { homedir as homedir5 } from "node:os";
22484
+ import { basename as basename3, dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
22485
+ var CURSOR_SESSION_REGISTRY_VERSION = 1;
22486
+ var CURSOR_SESSION_MODES = Object.freeze(["isolated", "create", "continue"]);
22487
+ function sessionRegistryError(code, message, cause) {
22488
+ const error2 = new Error(`${code}: ${message}`);
22489
+ error2.code = code;
22490
+ if (cause) error2.cause = cause;
22491
+ return error2;
22492
+ }
22493
+ function normalizeCursorSessionMode(value, fallback = "isolated") {
22494
+ const normalized = String(value || fallback).trim().toLowerCase().replace(/-/g, "_");
22495
+ return CURSOR_SESSION_MODES.includes(normalized) ? normalized : "";
22496
+ }
22497
+ function createCursorSessionId() {
22498
+ return `cursor-session-${randomUUID()}`;
22499
+ }
22500
+ function resolveCursorSessionRegistryFile(value, env = process.env) {
22501
+ if (value === void 0) value = env.CURSOR_BRIDGE_SESSION_FILE;
22502
+ const configured = String(value || "").trim();
22503
+ if (configured) return resolve4(configured);
22504
+ const configRoot = process.platform === "win32" && env.APPDATA ? env.APPDATA : env.XDG_CONFIG_HOME || join5(homedir5(), ".config");
22505
+ return join5(configRoot, "cursor-bridge", "sessions-v1.json");
22506
+ }
22507
+ function emptyCursorSessionRegistry() {
22508
+ return { version: CURSOR_SESSION_REGISTRY_VERSION, sessions: {}, updatedAt: null };
22509
+ }
22510
+ function readCursorSessionRegistry(filePath) {
22511
+ if (!filePath) return emptyCursorSessionRegistry();
22512
+ try {
22513
+ const parsed = JSON.parse(readFileSync4(filePath, "utf8"));
22514
+ if (!parsed || parsed.version !== CURSOR_SESSION_REGISTRY_VERSION) {
22515
+ throw sessionRegistryError(
22516
+ "SESSION_SCHEMA_UNSUPPORTED",
22517
+ `expected version ${CURSOR_SESSION_REGISTRY_VERSION}, received ${parsed && parsed.version}`
22518
+ );
22519
+ }
22520
+ if (!parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
22521
+ throw sessionRegistryError("SESSION_REGISTRY_INVALID", "sessions must be an object");
22522
+ }
22523
+ return {
22524
+ version: CURSOR_SESSION_REGISTRY_VERSION,
22525
+ sessions: { ...parsed.sessions },
22526
+ updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : null
22527
+ };
22528
+ } catch (error2) {
22529
+ if (error2 && error2.code === "ENOENT") return emptyCursorSessionRegistry();
22530
+ if (error2 && /^SESSION_/.test(String(error2.code || ""))) throw error2;
22531
+ throw sessionRegistryError(
22532
+ "SESSION_REGISTRY_INVALID",
22533
+ `failed to read ${filePath}: ${error2 instanceof Error ? error2.message : String(error2)}`,
22534
+ error2
22535
+ );
22536
+ }
22537
+ }
22538
+ function writeCursorSessionRegistry(filePath, state, now = (/* @__PURE__ */ new Date()).toISOString()) {
22539
+ if (!filePath) throw sessionRegistryError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
22540
+ const target = resolve4(filePath);
22541
+ const normalized = {
22542
+ version: CURSOR_SESSION_REGISTRY_VERSION,
22543
+ sessions: state && state.sessions && typeof state.sessions === "object" ? state.sessions : {},
22544
+ updatedAt: now
22545
+ };
22546
+ mkdirSync5(dirname4(target), { recursive: true });
22547
+ const temporary = join5(dirname4(target), `.${basename3(target)}.${process.pid}.${Date.now()}.tmp`);
22548
+ try {
22549
+ writeFileSync4(temporary, `${JSON.stringify(normalized, null, 2)}
22550
+ `, { encoding: "utf8", mode: 384 });
22551
+ renameSync4(temporary, target);
22552
+ } catch (error2) {
22553
+ rmSync4(temporary, { force: true });
22554
+ throw sessionRegistryError(
22555
+ "SESSION_REGISTRY_WRITE_FAILED",
22556
+ `failed to persist ${target}: ${error2 instanceof Error ? error2.message : String(error2)}`,
22557
+ error2
22558
+ );
22559
+ }
22560
+ return normalized;
22561
+ }
22562
+ function acquireRegistryLock(filePath, options = {}) {
22563
+ const lockPath = `${resolve4(filePath)}.lock`;
22564
+ const waitMs = Number(options.waitMs || 2e3);
22565
+ const staleMs = Number(options.staleMs || 3e4);
22566
+ const deadline = Date.now() + waitMs;
22567
+ mkdirSync5(dirname4(lockPath), { recursive: true });
22568
+ for (; ; ) {
22569
+ try {
22570
+ const handle = openSync(lockPath, "wx", 384);
22571
+ closeSync(handle);
22572
+ return () => rmSync4(lockPath, { force: true });
22573
+ } catch (error2) {
22574
+ if (!error2 || error2.code !== "EEXIST") {
22575
+ throw sessionRegistryError("SESSION_REGISTRY_LOCK_FAILED", error2 instanceof Error ? error2.message : String(error2), error2);
22576
+ }
22577
+ try {
22578
+ if (Date.now() - statSync2(lockPath).mtimeMs > staleMs) {
22579
+ rmSync4(lockPath, { force: true });
22580
+ continue;
22581
+ }
22582
+ } catch {
22583
+ }
22584
+ if (Date.now() >= deadline) {
22585
+ throw sessionRegistryError("SESSION_REGISTRY_BUSY", "another Cursor Bridge process is updating the session registry");
22586
+ }
22587
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
22588
+ }
22589
+ }
22590
+ }
22591
+ function updateCursorSessionRegistry(filePath, mutator, options = {}) {
22592
+ if (!filePath) throw sessionRegistryError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
22593
+ const release = acquireRegistryLock(filePath, options);
22594
+ try {
22595
+ const state = readCursorSessionRegistry(filePath);
22596
+ const result = mutator(state);
22597
+ writeCursorSessionRegistry(filePath, state, options.now || (/* @__PURE__ */ new Date()).toISOString());
22598
+ return result;
22599
+ } finally {
22600
+ release();
22601
+ }
22602
+ }
22603
+
22604
+ // server.mjs
22470
22605
  init_cursor_ensure_core();
22471
22606
  init_lifecycle_paths();
22472
- var PLUGIN_VERSION = "5.7.1";
22607
+ var PLUGIN_VERSION = "5.8.0";
22473
22608
  var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
22474
22609
  var ORIGIN = `http://localhost:${CDP_PORT2}`;
22475
22610
  var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
@@ -22477,6 +22612,26 @@ function normalizeDelegationMode(value = process.env.CURSOR_BRIDGE_DELEGATION) {
22477
22612
  return String(value || "on").trim().toLowerCase() === "off" ? "off" : "on";
22478
22613
  }
22479
22614
  var DELEGATION_MODE = normalizeDelegationMode();
22615
+ function cursorSessionError(code, message) {
22616
+ const error2 = new Error(`${code}: ${message}`);
22617
+ error2.code = code;
22618
+ return error2;
22619
+ }
22620
+ function normalizeSessionRequestId(value) {
22621
+ const id = String(value || "").trim();
22622
+ if (id.length > 200) throw cursorSessionError("SESSION_REQUEST_ID_INVALID", "request_id exceeds 200 characters");
22623
+ return id;
22624
+ }
22625
+ function sessionPathContains(parent, child) {
22626
+ const base = normalizeAllowedPath(parent);
22627
+ const candidate = normalizeAllowedPath(child);
22628
+ return candidate === base || candidate.startsWith(`${base}/`);
22629
+ }
22630
+ function sameSessionProject(left, right) {
22631
+ const a = resolve7(String(left || ""));
22632
+ const b = resolve7(String(right || ""));
22633
+ return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
22634
+ }
22480
22635
  function searchResultContract() {
22481
22636
  return [
22482
22637
  "Return only the minimum sufficient evidence set, ordered by evidence strength. Do not pad the result with similar matches.",
@@ -22522,17 +22677,26 @@ function isConfirmedCompletedReply({ answer, snapshot = {}, sawStop = false, bas
22522
22677
  const hasCompletionEvidence = sawStop || Number(snapshot.messageCount || 0) >= Number(baselineCount || 0) + 2;
22523
22678
  return hasAssistantEvidence && hasCompletionEvidence;
22524
22679
  }
22680
+ function isSessionTurnReplyReady(responseBaseline, snapshot = {}) {
22681
+ if (!responseBaseline) return true;
22682
+ if (Number(snapshot.messageCount || 0) >= Number(responseBaseline.messageCount || 0) + 2) return true;
22683
+ const replyLength = Number(snapshot.replyLength || 0);
22684
+ return replyLength > 0 && (replyLength !== Number(responseBaseline.replyLength || 0) || Number(snapshot.replyHash || 0) !== Number(responseBaseline.replyHash || 0));
22685
+ }
22686
+ function shouldScheduleParallelOriginRestore(job) {
22687
+ return String(job && job.sessionMode || "isolated") === "isolated";
22688
+ }
22525
22689
  var DO_DEFAULT_CONTRACT = "\n\nCompletion requirements: Work directly in the workspace currently open in Cursor; do not push to a remote. Before finishing, inspect the actual changes and run verification proportional to risk. The final reply must list completed work, changed files, verification results, and remaining risks or blockers.";
22526
22690
  var DO_LANGUAGE_CONTRACT = "\n\nResponse language: Reply in the language of the user task unless it explicitly requests another language. Never translate paths, commands, identifiers, keys, enum values, exact options, or error/status codes.";
22527
22691
  var CDP_HOST2 = "127.0.0.1";
22528
22692
  function httpJson(path) {
22529
- return new Promise((resolve7, reject) => {
22693
+ return new Promise((resolve8, reject) => {
22530
22694
  const req = http2.get({ host: CDP_HOST2, port: CDP_PORT2, path }, (res) => {
22531
22695
  let d = "";
22532
22696
  res.on("data", (c) => d += c);
22533
22697
  res.on("end", () => {
22534
22698
  try {
22535
- resolve7(JSON.parse(d));
22699
+ resolve8(JSON.parse(d));
22536
22700
  } catch {
22537
22701
  reject(new Error("CDP returned a non-JSON response"));
22538
22702
  }
@@ -22759,7 +22923,7 @@ function makeClient(wsUrl) {
22759
22923
  }
22760
22924
  var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
22761
22925
  async function evalJS(c, expr) {
22762
- 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 });
22763
22927
  if (r.exceptionDetails) throw new Error("Page exception: " + (r.exceptionDetails.exception && r.exceptionDetails.exception.description || r.exceptionDetails.text));
22764
22928
  return r.result && r.result.value;
22765
22929
  }
@@ -23024,7 +23188,7 @@ var WORKSPACE_SECTION_BODY = `
23024
23188
  };
23025
23189
  `;
23026
23190
  function exprCreateAgentForWorkspace(projectPath) {
23027
- const workspaceLabel = JSON.stringify(basename5(String(projectPath || "")).trim().toLowerCase());
23191
+ const workspaceLabel = JSON.stringify(basename6(String(projectPath || "")).trim().toLowerCase());
23028
23192
  return `(function(){
23029
23193
  ${WORKSPACE_SECTION_BODY}
23030
23194
  const wanted=${workspaceLabel};
@@ -23040,7 +23204,7 @@ function exprCreateAgentForWorkspace(projectPath) {
23040
23204
  })()`;
23041
23205
  }
23042
23206
  function exprInspectWorkspaceRepository(projectPath) {
23043
- const workspaceLabel = JSON.stringify(basename5(String(projectPath || "")).trim().toLowerCase());
23207
+ const workspaceLabel = JSON.stringify(basename6(String(projectPath || "")).trim().toLowerCase());
23044
23208
  return `(function(){
23045
23209
  ${WORKSPACE_SECTION_BODY}
23046
23210
  const wanted=${workspaceLabel};
@@ -23111,6 +23275,7 @@ var REACT_ADAPTER_BODY = `
23111
23275
  const findV2Props=()=>{
23112
23276
  const found=[]; const seen=new Set();
23113
23277
  let globalSelectAgent=null;
23278
+ let globalSelectedAgentId=null;
23114
23279
  let sectionIdByAgentId=null;
23115
23280
  for(const root of document.querySelectorAll('.glass-sidebar-agent-list-container')){
23116
23281
  const nodes=[]; let n=root;
@@ -23122,7 +23287,10 @@ var REACT_ADAPTER_BODY = `
23122
23287
  let f=key.startsWith('__reactFiber$')?seed:{memoizedProps:seed,return:null};
23123
23288
  for(let j=0;f&&j<80;j++,f=f.return){
23124
23289
  for(const p of [f.memoizedProps,f.pendingProps,f.stateNode&&f.stateNode.props]){
23125
- if(p&&typeof p.onSelectAgent==='function'&&!globalSelectAgent)globalSelectAgent=p.onSelectAgent;
23290
+ if(p&&typeof p.onSelectAgent==='function'&&!globalSelectAgent){
23291
+ globalSelectAgent=p.onSelectAgent;
23292
+ globalSelectedAgentId=p.selectedAgentId!==undefined?p.selectedAgentId:p.committedSelectedAgentId;
23293
+ }
23126
23294
  if(p&&p.sectionIdByAgentId&&!sectionIdByAgentId)sectionIdByAgentId=p.sectionIdByAgentId;
23127
23295
  const selectAgent=p&&(typeof p.onSelectAgent==='function'
23128
23296
  ?p.onSelectAgent
@@ -23140,6 +23308,7 @@ var REACT_ADAPTER_BODY = `
23140
23308
  }
23141
23309
  }
23142
23310
  found.globalSelectAgent=globalSelectAgent;
23311
+ found.globalSelectedAgentId=globalSelectedAgentId;
23143
23312
  found.sectionIdByAgentId=sectionIdByAgentId;
23144
23313
  return found;
23145
23314
  };
@@ -23149,9 +23318,10 @@ var REACT_ADAPTER_BODY = `
23149
23318
  return {
23150
23319
  kind:'agents_v2',
23151
23320
  entries:()=>{
23152
- const entries=[];const seen=new Set();let index=0;let selectedRaw='';
23321
+ const entries=[];const seen=new Set();let index=0;
23322
+ let selectedRaw=String(readScalar(v2.globalSelectedAgentId)||'').replace(/^local:/,'');
23153
23323
  for(const p of v2){
23154
- const selectedId=readScalar(p.selectedAgentId);
23324
+ const selectedId=readScalar(v2.globalSelectedAgentId)||readScalar(p.selectedAgentId);
23155
23325
  if(!selectedRaw&&selectedId)selectedRaw=String(selectedId).replace(/^local:/,'');
23156
23326
  for(const header of p.section.headers){
23157
23327
  const entry=normalizeV2(header,selectedId,index++,p.section);
@@ -23197,15 +23367,18 @@ var REACT_ADAPTER_BODY = `
23197
23367
  }
23198
23368
  return entries;
23199
23369
  },
23200
- open:id=>{
23370
+ open:async id=>{
23201
23371
  const raw=String(id||'').replace(/^local:/,'');
23372
+ if(String(readScalar(v2.globalSelectedAgentId)||'').replace(/^local:/,'')===raw)return true;
23202
23373
  if(v2.some(p=>String(readScalar(p.selectedAgentId)||'').replace(/^local:/,'')===raw))return true;
23203
23374
  for(const p of v2){
23204
23375
  const header=p.section.headers.find(h=>String(readScalar(h&&h.id)||'')===raw);
23205
- if(header){p.onSelectAgent(header);return true;}
23376
+ if(header){return (await p.onSelectAgent(header))!==false;}
23206
23377
  }
23207
23378
  const registered=v2.sectionIdByAgentId&&(v2.sectionIdByAgentId instanceof Map?v2.sectionIdByAgentId.has(raw):v2.sectionIdByAgentId[raw]!==undefined);
23208
- if(registered&&typeof v2.globalSelectAgent==='function'){v2.globalSelectAgent(raw);return true;}
23379
+ if(registered&&typeof v2.globalSelectAgent==='function'){
23380
+ return (await v2.globalSelectAgent(raw,{preserveSidebarAction:true}))!==false;
23381
+ }
23209
23382
  return false;
23210
23383
  }
23211
23384
  };
@@ -23244,9 +23417,9 @@ var EXPR_HISTORY_ENTRIES = `(function(){${REACT_ADAPTER_BODY}
23244
23417
  })()`;
23245
23418
  function exprOpenAgent(agentId) {
23246
23419
  const id = JSON.stringify(String(agentId));
23247
- return `(function(){${REACT_ADAPTER_BODY}
23420
+ return `(async function(){${REACT_ADAPTER_BODY}
23248
23421
  if(!a)return 'REACT_ADAPTER_UNAVAILABLE';
23249
- return a.open(${id})?'OPENED':'AGENT_NOT_FOUND';
23422
+ return (await a.open(${id}))?'OPENED':'AGENT_NOT_FOUND';
23250
23423
  })()`;
23251
23424
  }
23252
23425
  function normalizeAllowedPath(value) {
@@ -23434,16 +23607,16 @@ function lifecycleFailureSummary(lifecycle, fallback) {
23434
23607
  return [lifecycle.message || fallback, `[${diagnostic}]`, original].filter(Boolean).join(" ");
23435
23608
  }
23436
23609
  function releaseAdapterWorkingDirectory({ targetDir = null, chdir = process.chdir } = {}) {
23437
- const target = targetDir ? ensureLifecycleDir(targetDir) : dirname5(process.execPath);
23610
+ const target = targetDir ? ensureLifecycleDir(targetDir) : dirname6(process.execPath);
23438
23611
  chdir(target);
23439
23612
  return target;
23440
23613
  }
23441
23614
  var CursorBridge = class {
23442
23615
  constructor(options = {}) {
23443
- this.adapterStartCwd = resolve6(options.adapterStartCwd || process.cwd());
23616
+ this.adapterStartCwd = resolve7(options.adapterStartCwd || process.cwd());
23444
23617
  this.environmentDelegationMode = normalizeDelegationMode(options.delegationMode || DELEGATION_MODE);
23445
23618
  this._syncDelegationState();
23446
- this.runtimeFile = options.runtimeFile === null ? null : resolve6(options.runtimeFile || resolveCursorRuntimeFile());
23619
+ this.runtimeFile = options.runtimeFile === null ? null : resolve7(options.runtimeFile || resolveCursorRuntimeFile());
23447
23620
  this.runtimeModeDefault = normalizeCursorRuntimeMode(
23448
23621
  options.runtimeModeDefault || process.env.CURSOR_BRIDGE_RUNTIME_MODE,
23449
23622
  "normal"
@@ -23454,14 +23627,18 @@ var CursorBridge = class {
23454
23627
  this.runtimeMode = normalizeCursorRuntimeMode(requestedRuntimeMode);
23455
23628
  this.runtimeModeSource = options.runtimeMode !== void 0 ? "constructor" : persistedRuntimeMode ? "persistent" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
23456
23629
  this.runtimeModeScope = persistedRuntimeMode ? "persistent" : options.runtimeMode !== void 0 ? "constructor" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
23457
- this.workspaceFile = options.workspaceFile === null ? null : resolve6(options.workspaceFile || resolveWorkspaceBindingFile());
23630
+ this.workspaceFile = options.workspaceFile === null ? null : resolve7(options.workspaceFile || resolveWorkspaceBindingFile());
23458
23631
  this.workspaceKey = options.workspaceKey || resolveWorkspaceBindingKey();
23459
23632
  const persistedWorkspace = options.projectPath === void 0 ? readWorkspaceBinding(this.workspaceFile, this.workspaceKey) : null;
23460
- this.projectPath = options.projectPath !== void 0 ? resolve6(String(options.projectPath)) : persistedWorkspace && persistedWorkspace.projectPath || null;
23633
+ this.projectPath = options.projectPath !== void 0 ? resolve7(String(options.projectPath)) : persistedWorkspace && persistedWorkspace.projectPath || null;
23461
23634
  this.workspaceSource = options.projectPath !== void 0 ? "constructor" : persistedWorkspace ? "persistent_init" : "auto_detect";
23462
23635
  this.workspaceUpdatedAt = persistedWorkspace && persistedWorkspace.updatedAt || null;
23463
- this.modelPreferencesFile = options.modelPreferencesFile === null ? null : resolve6(options.modelPreferencesFile || resolveCursorModelPreferencesFile());
23636
+ this.modelPreferencesFile = options.modelPreferencesFile === null ? null : resolve7(options.modelPreferencesFile || resolveCursorModelPreferencesFile());
23464
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
+ );
23465
23642
  this._lastPresentation = null;
23466
23643
  this.busy = false;
23467
23644
  this.queue = [];
@@ -23488,6 +23665,348 @@ var CursorBridge = class {
23488
23665
  cursorUiPreferencePreserved: true
23489
23666
  };
23490
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
+ }
23491
24010
  async initializeWorkspace(projectPath) {
23492
24011
  if (this.busy || this.activeParallel.size > 0 || this.queue.length > 0) {
23493
24012
  throw new Error("cursor_init cannot change workspace while Cursor tasks are queued or running");
@@ -23719,11 +24238,28 @@ var CursorBridge = class {
23719
24238
  if (this._hasGlobalReservation()) {
23720
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");
23721
24240
  }
23722
- await this._ensureCursor();
23723
- const execution = String(options.execution || "fifo");
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"));
23724
24257
  if (execution !== "fifo" && execution !== "parallel_agent") {
23725
24258
  throw new Error(`Unsupported execution value ${execution}; expected fifo or parallel_agent`);
23726
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
+ }
23727
24263
  const readOnly = options.readOnly === true;
23728
24264
  const timeoutMs = Math.max(3e4, Math.min(9e5, Number(options.timeoutMs || 6e5)));
23729
24265
  const allowedPaths = Array.isArray(options.allowedPaths) ? options.allowedPaths.map((x) => String(x).trim()).filter(Boolean) : [];
@@ -23737,6 +24273,14 @@ var CursorBridge = class {
23737
24273
  this._validateParallelAllowedPaths(allowedPaths);
23738
24274
  this._assertNoParallelPathConflict(allowedPaths);
23739
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
+ }
23740
24284
  const contract = String(options.completionContract || "").trim();
23741
24285
  let fullPrompt = text + DO_LANGUAGE_CONTRACT;
23742
24286
  if (readOnly) fullPrompt += "\n\nRead-only boundary: Do not modify, create, or delete files, and do not run commands that change workspace state.";
@@ -23744,14 +24288,56 @@ var CursorBridge = class {
23744
24288
  fullPrompt += "\n\nAllowed modification scope (do not cross this boundary):\n" + allowedPaths.map((x) => "- " + x).join("\n");
23745
24289
  }
23746
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
+ }
23747
24323
  const job = this._enqueue("do", fullPrompt, {
24324
+ taskId,
23748
24325
  timeoutMs,
23749
- newChat: true,
24326
+ newChat: sessionMode !== "continue",
23750
24327
  execution,
23751
24328
  readOnly,
23752
24329
  allowedPaths,
23753
24330
  preferLegacyUi: options.preferLegacyUi === true,
23754
- modelPreference: this._modelPreferenceFor("cursor_do")
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
23755
24341
  });
23756
24342
  if (options.background !== false) return this._taskView(job);
23757
24343
  await job.promise;
@@ -23780,12 +24366,15 @@ var CursorBridge = class {
23780
24366
  }
23781
24367
  }
23782
24368
  }
24369
+ _nextTaskId() {
24370
+ return `cursor-${Date.now().toString(36)}-${this.nextTaskId++}`;
24371
+ }
23783
24372
  _enqueue(kind, prompt, options) {
23784
- const id = `cursor-${Date.now().toString(36)}-${this.nextTaskId++}`;
24373
+ const id = options.taskId || this._nextTaskId();
23785
24374
  let resolvePromise;
23786
24375
  let rejectPromise;
23787
- const promise = new Promise((resolve7, reject) => {
23788
- resolvePromise = resolve7;
24376
+ const promise = new Promise((resolve8, reject) => {
24377
+ resolvePromise = resolve8;
23789
24378
  rejectPromise = reject;
23790
24379
  });
23791
24380
  promise.catch(() => {
@@ -23804,15 +24393,22 @@ var CursorBridge = class {
23804
24393
  modelPreference: options.modelPreference ? { ...options.modelPreference } : null,
23805
24394
  modelSelection: options.modelPreference ? { configured: true, applied: false, ...options.modelPreference } : null,
23806
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,
23807
24403
  status: "queued",
23808
24404
  phase: "queued",
23809
24405
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
23810
24406
  startedAt: null,
23811
24407
  finishedAt: null,
23812
24408
  sentAt: null,
23813
- agentId: null,
24409
+ agentId: options.agentId || null,
23814
24410
  provisionalAgentId: null,
23815
- agentLabel: null,
24411
+ agentLabel: options.agentLabel || null,
23816
24412
  targetId: null,
23817
24413
  targetUiFlavor: null,
23818
24414
  fallbackReason: null,
@@ -23853,6 +24449,7 @@ var CursorBridge = class {
23853
24449
  job.recoveryState = null;
23854
24450
  job.cancelRequested = false;
23855
24451
  job.resultUnavailable = false;
24452
+ this._safeSettleSessionJob(job, "completed");
23856
24453
  if (!job.settled) {
23857
24454
  job.settled = true;
23858
24455
  job.resolve(result);
@@ -23870,6 +24467,7 @@ var CursorBridge = class {
23870
24467
  job.recoveryState = null;
23871
24468
  job.cancelRequested = false;
23872
24469
  job.reservationScope = null;
24470
+ this._safeSettleSessionJob(job, "failed");
23873
24471
  if (!job.settled) {
23874
24472
  job.settled = true;
23875
24473
  job.reject(e);
@@ -23890,6 +24488,10 @@ var CursorBridge = class {
23890
24488
  job.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
23891
24489
  job.recoveryState = options.underlyingStopConfirmed === true ? "stopped" : "released_unconfirmed";
23892
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
+ });
23893
24495
  if (!job.settled) {
23894
24496
  job.settled = true;
23895
24497
  job.reject(new Error(message));
@@ -23909,6 +24511,7 @@ var CursorBridge = class {
23909
24511
  job.recoveryState = "unverified";
23910
24512
  job.reservationScope = job.execution === "parallel_agent" && job.agentId ? job.readOnly ? "agent" : "paths" : "global";
23911
24513
  this.activeParallel.set(job.id, job);
24514
+ this._safeSettleSessionJob(job, "needs_attention", { needsAttention: true });
23912
24515
  if (!job.settled) {
23913
24516
  job.settled = true;
23914
24517
  job.reject(e);
@@ -24027,8 +24630,14 @@ var CursorBridge = class {
24027
24630
  try {
24028
24631
  if (job.effectiveExecution === "parallel_agent") {
24029
24632
  job.phase = "submitting";
24030
- const submitted = await this._withUiLock(() => this._submitParallelAgent(job));
24633
+ const submitted = await this._withUiLock(() => job.sessionMode === "continue" ? this._submitSessionTurn(job) : this._submitParallelAgent(job));
24031
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
+ }
24032
24641
  job.effectiveExecution = "fifo";
24033
24642
  job.fallbackReason = submitted.fallbackReason;
24034
24643
  if (this.activeParallel.size > 0) {
@@ -24043,10 +24652,16 @@ var CursorBridge = class {
24043
24652
  } else {
24044
24653
  job.agentId = submitted.agent.id;
24045
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
+ }
24046
24661
  job.sentAt = job.sentAt || (/* @__PURE__ */ new Date()).toISOString();
24047
24662
  job.phase = "running";
24048
24663
  job.reservationScope = job.readOnly ? "agent" : "paths";
24049
- if (!this.parallelRestoreAgentId && submitted.previousSelectedId) {
24664
+ if (shouldScheduleParallelOriginRestore(job) && !this.parallelRestoreAgentId && submitted.previousSelectedId) {
24050
24665
  this.parallelRestoreAgentId = submitted.previousSelectedId;
24051
24666
  this.parallelRestoreTargetId = job.targetId;
24052
24667
  }
@@ -24362,7 +24977,7 @@ var CursorBridge = class {
24362
24977
  applied: true,
24363
24978
  requestedModel,
24364
24979
  requestedEffort,
24365
- effectiveModel: trigger.text || modelRow.text,
24980
+ effectiveModel: modelRow.text || trigger.text,
24366
24981
  effectiveEffort,
24367
24982
  pickerDetail: trigger.detail || null,
24368
24983
  verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -24392,7 +25007,7 @@ var CursorBridge = class {
24392
25007
  const created = JSON.parse(await evalJS(c, exprCreateAgentForWorkspace(options.projectPath)) || "{}");
24393
25008
  if (!created.ok) {
24394
25009
  const available = Array.isArray(created.available) ? `; available=${created.available.join(", ")}` : "";
24395
- throw new Error(`Cursor Agents workspace binding failed: ${created.state || "unknown"}; wanted=${created.wanted || basename5(options.projectPath)}${available}`);
25010
+ throw new Error(`Cursor Agents workspace binding failed: ${created.state || "unknown"}; wanted=${created.wanted || basename6(options.projectPath)}${available}`);
24396
25011
  }
24397
25012
  await sleep2(1100);
24398
25013
  return true;
@@ -24672,6 +25287,96 @@ var CursorBridge = class {
24672
25287
  c.close();
24673
25288
  }
24674
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
+ }
24675
25380
  async _readParallelEntry(job) {
24676
25381
  return this._withUiLock(async () => {
24677
25382
  const page = await findPage({ targetId: job.targetId, purpose: "parallel_agent" });
@@ -24824,13 +25529,27 @@ var CursorBridge = class {
24824
25529
  const detail = lastCollectionError ? `; last collection error: ${lastCollectionError}` : "";
24825
25530
  throw new Error(`Cursor parallel_agent task timed out (${job.timeoutMs}ms)${detail}`);
24826
25531
  }
24827
- async _waitForSelectedAgent(c, agentId, timeoutMs = 15e3) {
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) {
24828
25541
  const deadline = Date.now() + timeoutMs;
25542
+ const retryAt = Date.now() + retryDelayMs;
25543
+ let retried = false;
24829
25544
  while (Date.now() < deadline) {
24830
25545
  const entries = await this._readAgentEntries(c);
24831
25546
  const target = entries.find((e) => e.id === agentId);
24832
25547
  if (target && target.isSelected) return true;
24833
- await sleep2(250);
25548
+ if (!retried && Date.now() >= retryAt) {
25549
+ retried = true;
25550
+ await this._requestExactAgentSelection(c, agentId);
25551
+ }
25552
+ await sleep2(pollMs);
24834
25553
  }
24835
25554
  throw new Error(`After opening ${agentId}, it could not be confirmed as the selected Agent`);
24836
25555
  }
@@ -24850,13 +25569,10 @@ var CursorBridge = class {
24850
25569
  let lastKey = "";
24851
25570
  let stable = 0;
24852
25571
  for (let i = 0; i < 80; i++) {
24853
- let snap = { messageCount: 0, replyLength: 0, replyHash: 0 };
24854
- try {
24855
- snap = JSON.parse(await evalJS(c, EXPR_SNAP));
24856
- } catch {
24857
- }
25572
+ const snap = await this._readResponseSnapshot(c);
24858
25573
  const candidate = String(await evalJS(c, EXPR_EXTRACT) || "").trim();
24859
- if (candidate && Number(snap.stop || 0) === 0) {
25574
+ const hasNewTurn = isSessionTurnReplyReady(job.responseBaseline, snap);
25575
+ if (candidate && hasNewTurn && Number(snap.stop || 0) === 0) {
24860
25576
  const key = `${snap.replyLength}:${snap.replyHash}`;
24861
25577
  if (key === lastKey) stable++;
24862
25578
  else {
@@ -24897,6 +25613,7 @@ var CursorBridge = class {
24897
25613
  job.recoveryState = "terminal_result_uncollected";
24898
25614
  job.reservationScope = uncertainSubmissionReservationScope(job, error2);
24899
25615
  job.recoveryState = error2.recoveryState || "monitoring_uncertain_submission";
25616
+ this._safeSettleSessionJob(job, "terminal_result_uncollected", { needsAttention: true });
24900
25617
  }
24901
25618
  _abandonJob(job, reason) {
24902
25619
  if (isTerminalTask(job)) return this._taskView(job, true);
@@ -24913,6 +25630,10 @@ var CursorBridge = class {
24913
25630
  job.terminalEvidence = "explicit_abandon_acknowledgement";
24914
25631
  job.recoveryState = "released_unconfirmed";
24915
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
+ });
24916
25637
  if (!job.settled) {
24917
25638
  job.settled = true;
24918
25639
  job.reject(new Error(message));
@@ -25448,6 +26169,12 @@ var CursorBridge = class {
25448
26169
  modelPreference: job.modelPreference,
25449
26170
  modelSelection: job.modelSelection,
25450
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,
25451
26178
  agentId: job.agentId,
25452
26179
  provisionalAgentId: job.provisionalAgentId || null,
25453
26180
  agentLabel: job.agentLabel,
@@ -25496,8 +26223,8 @@ var CursorBridge = class {
25496
26223
  async status(taskId = "") {
25497
26224
  if (taskId) {
25498
26225
  const job = this.tasks.get(String(taskId));
25499
- if (!job) return { found: false, taskId: String(taskId), ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView() };
25500
- return { found: true, ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this._taskView(job, true) };
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) };
25501
26228
  }
25502
26229
  const parallelRunning = this.activeParallel.size;
25503
26230
  const uiBusy = this.busy;
@@ -25509,6 +26236,7 @@ var CursorBridge = class {
25509
26236
  ...this.delegationView(),
25510
26237
  ...this.runtimeModeView(),
25511
26238
  ...this.modelPreferencesView(),
26239
+ ...this.sessionRegistryView(),
25512
26240
  busy: uiBusy || parallelRunning > 0 || this.queue.length > 0,
25513
26241
  uiBusy,
25514
26242
  parallelRunning,
@@ -25576,7 +26304,7 @@ function buildToolDefinitions(bridgeInstance) {
25576
26304
  },
25577
26305
  bridgeInstance.environmentDelegationMode !== "off" ? {
25578
26306
  name: "cursor_do",
25579
- description: "Give Cursor a clearly bounded task and get back a task ID. fifo means first in, first out: Bridge runs one queued task at a time, starting it in a clean chat. parallel_agent creates a separate top-level Cursor Agent. Parallel write tasks must declare non-overlapping allowed_paths; mark read-only work with read_only=true. Collect the result with cursor_status(task_id). Cursor can do the work, but the main agent still owns review and final verification. A direct user opt-out always wins.",
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.",
25580
26308
  inputSchema: {
25581
26309
  type: "object",
25582
26310
  properties: {
@@ -25586,7 +26314,10 @@ function buildToolDefinitions(bridgeInstance) {
25586
26314
  read_only: { type: "boolean", default: false, description: "Set true when Cursor must not change the workspace." },
25587
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." },
25588
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." },
25589
- 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." }
25590
26321
  },
25591
26322
  required: ["prompt"]
25592
26323
  }
@@ -25607,6 +26338,21 @@ function buildToolDefinitions(bridgeInstance) {
25607
26338
  required: ["task_id", "action"]
25608
26339
  }
25609
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
+ },
25610
26356
  {
25611
26357
  name: "cursor_runtime",
25612
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.",
@@ -25620,7 +26366,7 @@ function buildToolDefinitions(bridgeInstance) {
25620
26366
  },
25621
26367
  {
25622
26368
  name: "cursor_model",
25623
- description: "Show, set, or reset persistent Cursor model defaults for CCE and cursor_do. Settings are independent per target and survive host tasks, MCP restarts, and Cursor Bridge restarts until the user explicitly changes or resets them. When configured, Bridge applies the model and optional effort in every newly created Cursor Agent before sending the prompt, then verifies the visible selection. An unavailable, ambiguous, unsupported, or unconfirmed selection fails before prompt submission instead of silently falling back to Auto. Use set/reset only when the user explicitly asks to change these defaults; use show for read-only inspection.",
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.",
25624
26370
  inputSchema: {
25625
26371
  type: "object",
25626
26372
  properties: {
@@ -25634,8 +26380,14 @@ function buildToolDefinitions(bridgeInstance) {
25634
26380
  },
25635
26381
  {
25636
26382
  name: "cursor_status",
25637
- description: "Read-only snapshot of Cursor connectivity, queued/running work, reservations, execution availability, persistent model/effort defaults, and normal/minimal runtime presentation. Pass a task ID to read its configured and effective model selection plus any result already collected; this tool never switches Agents, reconciles, or stops work.",
25638
- inputSchema: { type: "object", properties: { task_id: { type: "string", description: "A task ID returned by cursor_do. Omit it for an overall status view." } } }
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
+ }
25639
26391
  }
25640
26392
  ].filter(Boolean);
25641
26393
  }
@@ -25681,7 +26433,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
25681
26433
  readOnly: !!(args && args.read_only),
25682
26434
  timeoutMs: args && args.timeout_ms,
25683
26435
  allowedPaths: args && args.allowed_paths,
25684
- 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"))
25685
26442
  });
25686
26443
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25687
26444
  }
@@ -25695,6 +26452,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
25695
26452
  });
25696
26453
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25697
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
+ }
25698
26464
  if (name === "cursor_runtime") {
25699
26465
  const mode = args && args.mode;
25700
26466
  const action = args && args.action;
@@ -25717,11 +26483,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
25717
26483
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25718
26484
  }
25719
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
+ }
25720
26489
  const statusMs = Math.max(1e3, Number(process.env.CURSOR_BRIDGE_STATUS_TIMEOUT || 8e3));
25721
26490
  let result;
25722
26491
  try {
25723
26492
  result = await Promise.race([
25724
- bridge.status(args && args.task_id),
26493
+ args && args.session_id ? Promise.resolve(bridge.sessionStatus(args.session_id)) : bridge.status(args && args.task_id),
25725
26494
  new Promise((_, reject) => setTimeout(() => reject(new Error(`cursor_status_timeout_${statusMs}`)), statusMs))
25726
26495
  ]);
25727
26496
  } catch (error2) {
@@ -25732,7 +26501,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
25732
26501
  error: error2 instanceof Error ? error2.message : String(error2),
25733
26502
  ...bridge.workspaceView(),
25734
26503
  ...bridge.delegationView(),
25735
- ...bridge.runtimeModeView()
26504
+ ...bridge.runtimeModeView(),
26505
+ ...bridge.sessionRegistryView()
25736
26506
  };
25737
26507
  }
25738
26508
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -25802,6 +26572,7 @@ export {
25802
26572
  isBlankAgentsWindow,
25803
26573
  isConfirmedCompletedReply,
25804
26574
  isDurablyRegisteredParallelEntry,
26575
+ isSessionTurnReplyReady,
25805
26576
  isTargetedStopConfirmed,
25806
26577
  lifecycleFailureSummary,
25807
26578
  normalizeAllowedPath,
@@ -25824,6 +26595,7 @@ export {
25824
26595
  selectUniqueNewAgentEntry,
25825
26596
  shouldAutoLaunchCursor,
25826
26597
  shouldRecoverNormalAgentsPresentation,
26598
+ shouldScheduleParallelOriginRestore,
25827
26599
  summarizeCdpPages,
25828
26600
  uncertainSubmissionReservationScope,
25829
26601
  updateStableEntryObservation