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.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/cursor-bridge.mjs +130 -32
- package/extensions/index.ts +1 -1
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-bridge",
|
|
3
|
-
"version": "5.10.
|
|
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.
|
|
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)
|
package/dist/cursor-bridge.mjs
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
|
25703
|
-
error2.confirmedNotSent =
|
|
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
|
-
|
|
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 (
|
|
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 ? "
|
|
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.
|
|
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)
|
|
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
|
|
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
|
|
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,
|
package/extensions/index.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
50
|
+
"embeddedProductVersion": "5.10.1"
|
|
51
51
|
}
|
|
52
52
|
}
|