pi-cursor-bridge 0.1.16 → 0.1.17

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-bridge",
3
- "version": "5.10.0+codex.20260908042457",
3
+ "version": "5.10.1+codex.20260908161937",
4
4
  "description": "Evidence-backed Cursor Context Engine search and bounded Cursor Agent execution, including a UI-suppressed minimal runtime.",
5
5
  "author": {
6
6
  "name": "Vanyangyang"
package/README.md CHANGED
@@ -10,6 +10,6 @@ pi install npm:pi-cursor-bridge
10
10
 
11
11
  Restart Pi after installation. Initialize the current project by asking Pi to initialize Cursor Bridge for the absolute project path, then use it normally. The package includes the `cce-routing` and `cursor-delegate` Skills and registers the native Cursor Bridge MCP tools directly in Pi.
12
12
 
13
- This Pi package embeds Cursor Bridge 5.10.0, including explicit request provenance for CCE and delegated tasks. Cursor must be installed and signed in. Current end-to-end compatibility claims remain scoped to the environments documented in the main repository.
13
+ This Pi package embeds Cursor Bridge 5.10.1, including explicit request provenance for CCE and delegated tasks. Cursor must be installed and signed in. Current end-to-end compatibility claims remain scoped to the environments documented in the main repository.
14
14
 
15
15
  Full documentation: [English](https://github.com/Vanyangyang/cursor-bridge#readme) · [简体中文](https://github.com/Vanyangyang/cursor-bridge/blob/main/README.zh-CN.md)
@@ -22344,6 +22344,7 @@ var StdioServerTransport = class {
22344
22344
 
22345
22345
  // server.mjs
22346
22346
  import { basename as basename6, dirname as dirname6, join as join8, resolve as resolve7 } from "node:path";
22347
+ import { statSync as statSync3 } from "node:fs";
22347
22348
 
22348
22349
  // node_modules/ws/wrapper.mjs
22349
22350
  var import_stream = __toESM(require_stream(), 1);
@@ -22604,7 +22605,7 @@ function updateCursorSessionRegistry(filePath, mutator, options = {}) {
22604
22605
  // server.mjs
22605
22606
  init_cursor_ensure_core();
22606
22607
  init_lifecycle_paths();
22607
- var PLUGIN_VERSION = "5.10.0";
22608
+ var PLUGIN_VERSION = "5.10.1";
22608
22609
  var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
22609
22610
  var ORIGIN = `http://localhost:${CDP_PORT2}`;
22610
22611
  var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
@@ -23344,6 +23345,60 @@ function exprInspectWorkspaceRepository(projectPath) {
23344
23345
  return JSON.stringify(inspectWorkspace(${JSON.stringify(String(projectPath || ""))}).diagnostic);
23345
23346
  })()`;
23346
23347
  }
23348
+ function exprRegisterAgentsWorkspace(projectPath, workspaceFile = false) {
23349
+ return `(async function(){
23350
+ ${WORKSPACE_SECTION_BODY}
23351
+ const path=${JSON.stringify(String(projectPath || ""))};
23352
+ const before=inspectWorkspace(path).diagnostic;
23353
+ if(before.state!=='workspace_registration_required')return JSON.stringify(before);
23354
+ const fail=(state,error)=>JSON.stringify({...before,state,error});
23355
+ const services=new Set();
23356
+ for(const node of document.querySelectorAll('button[aria-label="Open Workspace"],.ui-sidebar-section-head')){
23357
+ const key=Object.keys(node).find(k=>k.startsWith('__reactFiber$'));
23358
+ for(let f=key&&node[key],i=0;f&&i<48;i++,f=f.return){
23359
+ for(let d=f.dependencies&&f.dependencies.firstContext;d;d=d.next){
23360
+ const service=d.memoizedValue&&d.memoizedValue.workspace&&d.memoizedValue.workspace.instantiationService;
23361
+ if(service)services.add(service);
23362
+ }
23363
+ }
23364
+ }
23365
+ const candidates=new Map();
23366
+ for(const service of services){
23367
+ const entries=new Map();
23368
+ for(let s=service,i=0;s&&i<8;i++,s=s._parent){
23369
+ for(const [id,value] of s._services&&s._services._entries||[]){
23370
+ if(!entries.has(String(id)))entries.set(String(id),value);
23371
+ }
23372
+ }
23373
+ const projects=entries.get('glassWorkspacesService');
23374
+ const workspaces=entries.get('workspacesService');
23375
+ const URI=entries.get('environmentService')?.userHome?.constructor;
23376
+ if(typeof projects?.replaceWorkspaceProject==='function'&&typeof URI?.file==='function'
23377
+ &&typeof workspaces?.getSingleFolderWorkspaceIdentifier==='function'
23378
+ &&typeof workspaces?.getWorkspaceIdentifier==='function'
23379
+ &&typeof projects?.refresh==='function'){
23380
+ candidates.set(projects,{projects,workspaces,URI});
23381
+ }
23382
+ }
23383
+ if(candidates.size!==1)return fail('workspace_registration_unavailable','Expected one Cursor workspace registration service');
23384
+ try{
23385
+ const {projects,workspaces,URI}=[...candidates.values()][0];
23386
+ const uri=URI.file(path);
23387
+ const identifier=await workspaces[${JSON.stringify(workspaceFile ? "getWorkspaceIdentifier" : "getSingleFolderWorkspaceIdentifier")}](uri);
23388
+ if(!identifier?.id||localWorkspacePath(identifier)!==normalizeWorkspacePath(path)){
23389
+ return fail('workspace_registration_identity_mismatch','Cursor returned a different workspace identity');
23390
+ }
23391
+ const current=inspectWorkspace(path).diagnostic;
23392
+ if(current.state!=='workspace_registration_required')return JSON.stringify(current);
23393
+ // addProject can skip folders already represented by Agent history.
23394
+ // With no 'replaces', this public service method upserts only this exact ID.
23395
+ await projects.replaceWorkspaceProject({project:{type:'workspace',workspaceIdentifier:identifier}});
23396
+ await projects.refresh();
23397
+ return JSON.stringify({ok:false,state:'workspace_registration_requested',wanted:before.wanted,
23398
+ workspaceId:identifier.id,registrationSource:'cursor_glass_workspaces_service'});
23399
+ }catch(error){return fail('workspace_registration_failed',String(error.message||error));}
23400
+ })()`;
23401
+ }
23347
23402
  function exprInspectAgentWorkspace(projectPath, options = {}) {
23348
23403
  return `(function(){
23349
23404
  ${WORKSPACE_SECTION_BODY}
@@ -24291,6 +24346,8 @@ var CursorBridge = class {
24291
24346
  return result;
24292
24347
  }
24293
24348
  async initializeWorkspace(projectPath) {
24349
+ if (this._healing) await this._healing.catch(() => {
24350
+ });
24294
24351
  if (this.busy || this.activeParallel.size > 0 || this.queue.length > 0) {
24295
24352
  throw new Error("cursor_init cannot change workspace while Cursor tasks are queued or running");
24296
24353
  }
@@ -24303,7 +24360,7 @@ var CursorBridge = class {
24303
24360
  this._lastLifecycle = null;
24304
24361
  this._lastWorkspaceBinding = null;
24305
24362
  try {
24306
- await this._ensureCursor();
24363
+ await this._ensureCursor({ registerWorkspace: true });
24307
24364
  } catch (error2) {
24308
24365
  const lifecycle = this._lastLifecycle;
24309
24366
  const recoverableStatuses = /* @__PURE__ */ new Set([
@@ -24339,7 +24396,7 @@ var CursorBridge = class {
24339
24396
  lifecycle: this._lastLifecycle
24340
24397
  };
24341
24398
  }
24342
- async _findAgentsWorkspace(projectPath) {
24399
+ async _findAgentsWorkspace(projectPath, { registerWorkspace = false } = {}) {
24343
24400
  if (!projectPath) return null;
24344
24401
  this._lastWorkspaceBinding = null;
24345
24402
  let page;
@@ -24352,7 +24409,24 @@ var CursorBridge = class {
24352
24409
  const c = makeClient(page.webSocketDebuggerUrl);
24353
24410
  try {
24354
24411
  await c.ready;
24355
- const repository = JSON.parse(await evalJS(c, exprInspectWorkspaceRepository(projectPath)) || "{}");
24412
+ let repository = JSON.parse(await evalJS(c, exprInspectWorkspaceRepository(projectPath)) || "{}");
24413
+ if (registerWorkspace && repository.state === "workspace_registration_required") {
24414
+ const registration = await this._withUiLock(async () => {
24415
+ if (this.busy || this.activeParallel.size > 0 || this.queue.length > 0) {
24416
+ return { ok: false, state: "workspace_registration_busy", error: "Cursor tasks became queued or running before registration" };
24417
+ }
24418
+ return JSON.parse(await evalJS(c, exprRegisterAgentsWorkspace(projectPath, statSync3(projectPath).isFile())) || "{}");
24419
+ });
24420
+ repository = registration;
24421
+ if (registration.state === "workspace_registration_requested") {
24422
+ for (let attempt = 0; attempt < 20; attempt++) {
24423
+ repository = JSON.parse(await evalJS(c, exprInspectWorkspaceRepository(projectPath)) || "{}");
24424
+ if (repository.state !== "workspace_registration_required") break;
24425
+ await sleep2(250);
24426
+ }
24427
+ repository = { ...repository, registration };
24428
+ }
24429
+ }
24356
24430
  this._lastWorkspaceBinding = { ...repository, checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
24357
24431
  if (!repository.ok) return null;
24358
24432
  return {
@@ -24556,7 +24630,9 @@ var CursorBridge = class {
24556
24630
  throw cursorSessionError("SESSION_EXECUTION_INVALID", "persistent sessions require execution=parallel_agent and never fall back to FIFO");
24557
24631
  }
24558
24632
  const readOnly = options.readOnly === true;
24559
- const timeoutMs = Math.max(3e4, Math.min(9e5, Number(options.timeoutMs || 6e5)));
24633
+ const requestedTimeoutMs = Number(options.timeoutMs ?? 6e5);
24634
+ if (!Number.isFinite(requestedTimeoutMs)) throw new Error("timeout_ms must be finite");
24635
+ const timeoutMs = Math.max(3e4, Math.min(9e5, requestedTimeoutMs));
24560
24636
  const allowedPaths = Array.isArray(options.allowedPaths) ? options.allowedPaths.map((x) => String(x).trim()).filter(Boolean) : [];
24561
24637
  if (readOnly && allowedPaths.length > 0) {
24562
24638
  throw new Error("read_only=true cannot be combined with allowed_paths because a read-only task cannot declare a write scope");
@@ -24619,6 +24695,7 @@ var CursorBridge = class {
24619
24695
  requestContext,
24620
24696
  taskId,
24621
24697
  timeoutMs,
24698
+ requestedTimeoutMs,
24622
24699
  newChat: sessionMode !== "continue",
24623
24700
  execution,
24624
24701
  readOnly,
@@ -24682,6 +24759,7 @@ var CursorBridge = class {
24682
24759
  prompt,
24683
24760
  requestContext: options.requestContext || normalizeRequestContext(),
24684
24761
  timeoutMs: options.timeoutMs,
24762
+ requestedTimeoutMs: options.requestedTimeoutMs ?? options.timeoutMs,
24685
24763
  newChat: options.newChat,
24686
24764
  preferLegacyUi: options.preferLegacyUi === true,
24687
24765
  execution: options.execution || "fifo",
@@ -24847,7 +24925,7 @@ var CursorBridge = class {
24847
24925
  // 自愈:每次查询前委托 ensureCursorRunning。并发去重。失败静默降级(_run 报清晰错)。
24848
24926
  // 统一走 ensureCursorRunning 复用其【单一身份校验来源】(cdpUp + cdpIsCursor)——避免热路径裸 /json/version 检查
24849
24927
  // 绕过身份校验、在别的 IDE 占 9223 时驱动错应用(2026-06-08 review #6)。
24850
- _ensureCursor() {
24928
+ _ensureCursor({ registerWorkspace = false } = {}) {
24851
24929
  this._refreshPersistedRuntimeMode();
24852
24930
  if (this._healing) return this._healing;
24853
24931
  this._healing = (async () => {
@@ -24860,17 +24938,11 @@ var CursorBridge = class {
24860
24938
  ...this.projectPath ? { projectPath: this.projectPath } : {}
24861
24939
  });
24862
24940
  this._lastLifecycle = lifecycleFromEnsureResult(rr, this.runtimeMode);
24863
- if (!rr.ok && rr.status === "workspace-not-ready" && rr.projectPath) {
24864
- const agentsWorkspace = await this._findAgentsWorkspace(rr.projectPath);
24941
+ if ((rr.ok || rr.status === "workspace-not-ready") && rr.projectPath) {
24942
+ const agentsWorkspace = await this._findAgentsWorkspace(rr.projectPath, { registerWorkspace });
24865
24943
  if (agentsWorkspace) {
24866
24944
  this._lastLifecycle = promoteAgentsWorkspaceLifecycle(this._lastLifecycle, agentsWorkspace);
24867
- }
24868
- }
24869
- if (rr.ok && rr.workspaceAction === "reused-agents-window" && rr.projectPath) {
24870
- const agentsWorkspace = await this._findAgentsWorkspace(rr.projectPath);
24871
- if (agentsWorkspace) {
24872
- this._lastLifecycle = promoteAgentsWorkspaceLifecycle(this._lastLifecycle, agentsWorkspace);
24873
- } else {
24945
+ } else if (rr.ok && (rr.workspaceAction === "reused-agents-window" || this._lastWorkspaceBinding)) {
24874
24946
  this._lastLifecycle = {
24875
24947
  ...this._lastLifecycle,
24876
24948
  status: "workspace-not-ready",
@@ -25011,10 +25083,11 @@ var CursorBridge = class {
25011
25083
  this.activeParallel.delete(job.id);
25012
25084
  this._failJob(job, error2);
25013
25085
  } else if (error2 && error2.sent) {
25086
+ job.firstWaitError ||= error2.message;
25014
25087
  job.error = `Submission state is uncertain; monitoring continues by agentId: ${error2.message}`;
25015
25088
  if (job.agentId) {
25016
25089
  job.phase = "running";
25017
- job.reservationScope = job.readOnly ? "agent" : "paths";
25090
+ job.reservationScope = job.effectiveExecution === "fifo" ? "global" : job.readOnly ? "agent" : "paths";
25018
25091
  this.activeParallel.set(job.id, job);
25019
25092
  this._startParallelMonitor(job);
25020
25093
  } else {
@@ -25083,6 +25156,7 @@ var CursorBridge = class {
25083
25156
  baseline = JSON.parse(await evalJS(c, EXPR_SNAP));
25084
25157
  } catch {
25085
25158
  }
25159
+ options.responseBaseline = baseline;
25086
25160
  const providerErrorBaseline = providerErrorSignature(await this._readProviderError(c));
25087
25161
  await this._verifyAgentsWorkspace(c, options, "before_send");
25088
25162
  options.sendState = "dispatching";
@@ -25671,6 +25745,7 @@ var CursorBridge = class {
25671
25745
  if (!diagnostic.ok) throw createWorkspaceBindingError(diagnostic);
25672
25746
  }
25673
25747
  async _confirmSubmission(c, baselineCount = 0, providerErrorBaseline = "", job = null) {
25748
+ let lastSnapshot = {};
25674
25749
  const accepted = async () => {
25675
25750
  await this._throwIfNewProviderError(c, providerErrorBaseline);
25676
25751
  let snap = {};
@@ -25678,6 +25753,7 @@ var CursorBridge = class {
25678
25753
  snap = JSON.parse(await evalJS(c, EXPR_SNAP));
25679
25754
  } catch {
25680
25755
  }
25756
+ lastSnapshot = snap;
25681
25757
  const inputTextLength = Number(snap.inputTextLength);
25682
25758
  return Number(snap.stop || 0) > 0 || Number(snap.messageCount || 0) > Number(baselineCount || 0) || Number.isFinite(inputTextLength) && inputTextLength === 0;
25683
25759
  };
@@ -25693,14 +25769,22 @@ var CursorBridge = class {
25693
25769
  throw error3;
25694
25770
  }
25695
25771
  const clicked = await evalJS(c, EXPR_CLICK_SEND);
25696
- if (clicked === "CLICKED") {
25772
+ if (clicked === "CLICKED" || clicked === "NO_SEND") {
25697
25773
  for (let i = 0; i < 20; i++) {
25698
25774
  await sleep2(250);
25699
- if (await accepted()) return "button";
25775
+ if (await accepted()) return clicked === "CLICKED" ? "button" : "enter";
25700
25776
  }
25701
25777
  }
25702
- const error2 = new Error(`Cursor did not accept the submission (submit_not_accepted: ${clicked || "unknown"}); the prompt remains in the input and no orphan task was created`);
25703
- error2.confirmedNotSent = true;
25778
+ const error2 = new Error(`Cursor submission could not be confirmed (submission_uncertain: ${clicked || "unknown"}); Enter was dispatched, so inspect the bound Agent before retrying`);
25779
+ error2.confirmedNotSent = false;
25780
+ error2.sent = true;
25781
+ error2.composerDiagnostic = {
25782
+ inputTextLength: Number.isFinite(Number(lastSnapshot.inputTextLength)) ? Number(lastSnapshot.inputTextLength) : null,
25783
+ sendReady: lastSnapshot.sendReady === true,
25784
+ stop: Number(lastSnapshot.stop || 0),
25785
+ messageCount: Number(lastSnapshot.messageCount || 0),
25786
+ fallbackResult: clicked || "unknown"
25787
+ };
25704
25788
  throw error2;
25705
25789
  }
25706
25790
  async _readProviderError(c) {
@@ -26072,7 +26156,12 @@ var CursorBridge = class {
26072
26156
  }
26073
26157
  if (!this._monitorOwns(job, generation)) return;
26074
26158
  const detail = lastCollectionError ? `; last collection error: ${lastCollectionError}` : "";
26075
- throw new Error(`Cursor parallel_agent task timed out (${job.timeoutMs}ms)${detail}`);
26159
+ const recovered = await this._withJobLock(job, async () => {
26160
+ if (!this._monitorOwns(job, generation)) return true;
26161
+ const result = await this._reapParallelJobLocked(job, { reattach: false, preserveMonitor: true });
26162
+ return isTerminalTask(job) || result.state === "terminal_uncollected";
26163
+ });
26164
+ if (!recovered) throw new Error(`Cursor ${job.effectiveExecution || job.execution || "Agent"} task timed out (${job.timeoutMs}ms)${detail}`);
26076
26165
  }
26077
26166
  async _requestExactAgentSelection(c, agentId) {
26078
26167
  if (!await this._ensureHistoryOpen(c)) return "REACT_ADAPTER_UNAVAILABLE";
@@ -26168,7 +26257,7 @@ var CursorBridge = class {
26168
26257
  job.resultUnavailable = true;
26169
26258
  job.terminalEvidence = evidence || "stable_completed_history_icon";
26170
26259
  job.recoveryState = "terminal_result_uncollected";
26171
- job.reservationScope = uncertainSubmissionReservationScope(job, error2);
26260
+ job.reservationScope = job.effectiveExecution === "fifo" ? "global" : uncertainSubmissionReservationScope(job, error2);
26172
26261
  this._safeSettleSessionJob(job, "terminal_result_uncollected", { needsAttention: true });
26173
26262
  }
26174
26263
  _abandonJob(job, reason) {
@@ -26216,7 +26305,7 @@ var CursorBridge = class {
26216
26305
  job.recoveryState = "agent_missing";
26217
26306
  return { stable: false, error: "The bound agentId was not found in Agent History", entry: second || first || null };
26218
26307
  }
26219
- const stable = first.id === second.id && first.showSpinner === second.showSpinner && String(first.icon || "") === String(second.icon || "");
26308
+ const stable = first.id === second.id && second.id === job.agentId && first.showSpinner === second.showSpinner && String(first.icon || "") === String(second.icon || "");
26220
26309
  return { stable, entry: second, error: stable ? null : "Agent History state is not yet stable" };
26221
26310
  }
26222
26311
  async _reapParallelJob(job, options = {}) {
@@ -26225,15 +26314,15 @@ var CursorBridge = class {
26225
26314
  }
26226
26315
  async _reapParallelJobLocked(job, options = {}) {
26227
26316
  if (!job || isTerminalTask(job)) return { changed: false, state: "terminal", task: this._taskView(job, true) };
26228
- if (job.execution !== "parallel_agent" || !this.activeParallel.has(job.id)) {
26317
+ if (!this.activeParallel.has(job.id)) {
26229
26318
  return { changed: false, state: "not_parallel_reservation", task: this._taskView(job, true) };
26230
26319
  }
26231
26320
  if (!job.agentId) {
26232
26321
  job.lastRecoveryAt = (/* @__PURE__ */ new Date()).toISOString();
26233
26322
  job.recoveryState = "unbound_agent";
26234
- return { changed: false, state: "unbound_agent", task: this._taskView(job, true) };
26323
+ return { changed: false, state: "unbound_agent", next: "The FIFO orphan has no agentId that can be safely rebound. Confirm that it stopped in the Cursor UI before explicitly abandoning it.", task: this._taskView(job, true) };
26235
26324
  }
26236
- this._invalidateParallelMonitor(job);
26325
+ if (!options.preserveMonitor) this._invalidateParallelMonitor(job);
26237
26326
  const observed = await this._readStableParallelEntry(job);
26238
26327
  if (!observed.stable || !observed.entry) {
26239
26328
  return { changed: false, state: job.recoveryState || "unstable", error: observed.error, task: this._taskView(job, true) };
@@ -26481,7 +26570,7 @@ var CursorBridge = class {
26481
26570
  if (action === "reap") {
26482
26571
  const result = await this._reapParallelJobLocked(job, { reattach: true });
26483
26572
  if (result.state === "not_parallel_reservation" && job.phase === "orphaned") {
26484
- result.next = job.agentId ? "FIFO is bound to an agentId, but reap applies only to parallel_agent. Use cancel for a targeted stop, or abandon after confirmation." : "The FIFO orphan has no agentId that can be safely rebound. Confirm that it stopped in the Cursor UI before explicitly abandoning it.";
26573
+ result.next = job.agentId ? "The bound task has no recoverable reservation. Inspect its current state before cancelling." : "The FIFO orphan has no agentId that can be safely rebound. Confirm that it stopped in the Cursor UI before explicitly abandoning it.";
26485
26574
  }
26486
26575
  return { found: true, action, ...result };
26487
26576
  }
@@ -26539,10 +26628,13 @@ var CursorBridge = class {
26539
26628
  }
26540
26629
  const canTargetStop = job.execution === "parallel_agent" || !!job.agentId;
26541
26630
  if (canTargetStop && (job.execution === "parallel_agent" || job.phase === "orphaned")) {
26542
- if (job.execution === "parallel_agent") {
26631
+ if (this.activeParallel.has(job.id)) {
26543
26632
  this._invalidateParallelMonitor(job);
26544
26633
  const reaped = await this._reapParallelJobLocked(job, { reattach: false });
26545
- if (isTerminalTask(job)) return { found: true, action, ...reaped };
26634
+ if (isTerminalTask(job) || reaped.state === "terminal_uncollected") {
26635
+ job.cancelRequested = false;
26636
+ return { found: true, action, ...reaped };
26637
+ }
26546
26638
  }
26547
26639
  job.phase = "cancelling";
26548
26640
  job.recoveryState = "stopping";
@@ -26677,6 +26769,7 @@ var CursorBridge = class {
26677
26769
  } catch {
26678
26770
  s = { stop: 0, messageCount: 0, replyLength: 0, replyHash: 0 };
26679
26771
  }
26772
+ if (job) job.lastWaitObservation = { at: (/* @__PURE__ */ new Date()).toISOString(), stop: s.stop, messageCount: s.messageCount, replyLength: s.replyLength, sawStop: sawStop || s.stop > 0 };
26680
26773
  if (s.stop > 0) sawStop = true;
26681
26774
  if (sawStop && s.stop === 0 && s.replyLength > 0) {
26682
26775
  await sleep2(800);
@@ -26717,6 +26810,10 @@ var CursorBridge = class {
26717
26810
  if (includeResult) this._markTaskResultCollected(job);
26718
26811
  const view = {
26719
26812
  taskId: job.id,
26813
+ requestedTimeoutMs: job.requestedTimeoutMs ?? job.timeoutMs,
26814
+ effectiveTimeoutMs: job.timeoutMs,
26815
+ firstWaitError: job.firstWaitError || null,
26816
+ lastWaitObservation: job.lastWaitObservation || null,
26720
26817
  kind: job.kind,
26721
26818
  status: job.status,
26722
26819
  phase: job.phase,
@@ -26772,7 +26869,7 @@ var CursorBridge = class {
26772
26869
  if (job.recoveryState === "terminal_result_uncollected") {
26773
26870
  view.attention = "Agent History proves that the underlying task ended, but its final reply has not been collected. Retry with reap, or explicitly abandon only if a missing reply is acceptable.";
26774
26871
  } else {
26775
- view.attention = job.agentId ? job.execution === "parallel_agent" ? "Use cursor_task_control(action=reap) to recheck the original agentId. Use cancel when a stop is needed, and abandon only after manual confirmation while accepting residual write risk." : "FIFO is bound to an agentId. Use cursor_task_control(action=cancel) with the exact expected_agent_id for a targeted stop." : "This orphan has no agentId that can be safely rebound and globally blocks new delegation. Confirm that it stopped in the Cursor UI, then explicitly release it with cursor_task_control(action=abandon).";
26872
+ view.attention = job.agentId ? job.execution === "parallel_agent" ? "Use cursor_task_control(action=reap) to recheck the original agentId. Use cancel when a stop is needed, and abandon only after manual confirmation while accepting residual write risk." : "FIFO is bound to an agentId. Use reap to collect or resume monitoring; use cancel with the exact expected_agent_id to stop running work." : "This orphan has no agentId that can be safely rebound and globally blocks new delegation. Confirm that it stopped in the Cursor UI, then explicitly release it with cursor_task_control(action=abandon).";
26776
26873
  }
26777
26874
  }
26778
26875
  return view;
@@ -26875,7 +26972,7 @@ function buildToolDefinitions(bridgeInstance) {
26875
26972
  return [
26876
26973
  {
26877
26974
  name: "cursor_init",
26878
- description: "Initialize or reinitialize CCE for one local workspace. Give only the project path: Bridge saves it, finds Cursor, ensures the required connection, and opens or verifies the matching project. If Cursor was opened too early without CCE access, initialization safely keeps the binding and tells the user to save, close Cursor once, and repeat the same initialization sentence; it never force-closes Cursor. Cursor login and the user's old/new UI preference are preserved. When both UIs are open, Bridge selects the new Agents Window and creates work in the matching repository instead of Home.",
26975
+ description: "Initialize or reinitialize CCE for one local workspace. Give only the project path: Bridge saves it, finds Cursor, ensures the required connection, and opens or verifies the matching project. In Agents Window, initialization registers a missing local workspace through Cursor's project service and verifies its exact path before reporting ready. If Cursor was opened too early without CCE access, initialization safely keeps the binding and tells the user to save, close Cursor once, and repeat the same initialization sentence; it never force-closes Cursor. Cursor login and the user's old/new UI preference are preserved. When both UIs are open, Bridge selects the new Agents Window and creates work in the matching repository instead of Home.",
26879
26976
  inputSchema: {
26880
26977
  type: "object",
26881
26978
  properties: {
@@ -26900,7 +26997,7 @@ function buildToolDefinitions(bridgeInstance) {
26900
26997
  background: { type: "boolean", default: true, description: "When true, return the task ID immediately. When false, wait for the task to finish or need attention." },
26901
26998
  execution: { type: "string", enum: ["fifo", "parallel_agent"], default: "fifo", description: "fifo is the first-in, first-out serial queue and runs one task at a time in a clean chat. parallel_agent creates a separate top-level Cursor Agent." },
26902
26999
  read_only: { type: "boolean", default: false, description: "Set true when Cursor must not change the workspace." },
26903
- timeout_ms: { type: "integer", minimum: 3e4, maximum: 9e5, default: 6e5, description: "One monitoring budget after submission, shared by FIFO and automatic Agent recovery. Expiry needs attention; it does not cancel work. Explicit reap may start a new budget. The default is 10 minutes." },
27000
+ timeout_ms: { type: "integer", minimum: 3e4, maximum: 9e5, default: 6e5, description: "One monitoring budget after submission, shared by FIFO and automatic Agent recovery. Default 10 minutes, maximum 15 minutes; task status reports requested and effective budgets. Expiry probes the bound Agent once for completion; it does not cancel or resend work. Explicit reap may start a new budget for a still-running bound Agent, including FIFO." },
26904
27001
  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." },
26905
27002
  completion_contract: { type: "string", description: "Optional acceptance checks or a required final-report format." },
26906
27003
  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." },
@@ -27177,6 +27274,7 @@ export {
27177
27274
  exprInspectAgentWorkspace,
27178
27275
  exprInspectWorkspaceRepository,
27179
27276
  exprOpenAgent,
27277
+ exprRegisterAgentsWorkspace,
27180
27278
  isConfirmedCompletedReply,
27181
27279
  isCursorEffortOptionText,
27182
27280
  isDurablyRegisteredParallelEntry,
@@ -10,7 +10,7 @@ const hostWorkspaceId = hostCwd.replace(/\\/g, "/").toLowerCase();
10
10
  export default createStdioMcpExtension({
11
11
  label: "Cursor Bridge",
12
12
  clientName: "pi-cursor-bridge",
13
- packageVersion: "0.1.16",
13
+ packageVersion: "0.1.17",
14
14
  serverName: "cursor-bridge",
15
15
  serverScript: join(packageRoot, "dist", "cursor-bridge.mjs"),
16
16
  cwd: hostCwd,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cursor-bridge",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Use Cursor Context Engine and bounded, explicitly continuous Cursor Agent execution from the Pi coding agent.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -47,6 +47,6 @@
47
47
  },
48
48
  "piPackage": {
49
49
  "embeddedProduct": "Cursor Bridge",
50
- "embeddedProductVersion": "5.10.0"
50
+ "embeddedProductVersion": "5.10.1"
51
51
  }
52
52
  }