pi-cursor-bridge 0.1.12 → 0.1.14
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 +230 -73
- 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.
|
|
3
|
+
"version": "5.9.1+codex.20260905095629",
|
|
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.
|
|
13
|
+
This Pi package embeds Cursor Bridge 5.9.1. 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
|
@@ -22604,7 +22604,7 @@ function updateCursorSessionRegistry(filePath, mutator, options = {}) {
|
|
|
22604
22604
|
// server.mjs
|
|
22605
22605
|
init_cursor_ensure_core();
|
|
22606
22606
|
init_lifecycle_paths();
|
|
22607
|
-
var PLUGIN_VERSION = "5.
|
|
22607
|
+
var PLUGIN_VERSION = "5.9.1";
|
|
22608
22608
|
var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
22609
22609
|
var ORIGIN = `http://localhost:${CDP_PORT2}`;
|
|
22610
22610
|
var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
|
|
@@ -23116,8 +23116,22 @@ var EXPR_MODEL_PICKER_TRIGGER = `(function(){
|
|
|
23116
23116
|
const detail=String(trigger.querySelector('.ui-model-picker__trigger-variant-suffix,.vscode-model-picker__trigger-variant-suffix')?.innerText||'').replace(/\\s+/g,' ').trim();
|
|
23117
23117
|
return JSON.stringify({found:true,state:'ready',text,detail,x:Math.round(rect.x+rect.width/2),y:Math.round(rect.y+rect.height/2)});
|
|
23118
23118
|
})()`;
|
|
23119
|
+
function classifyModelPickerRowKind(text, hasItemName, menuTestId, submenuTestId, ariaHaspopup, hasParamCheck, inSubmenu) {
|
|
23120
|
+
const menu = String(menuTestId || "").toLowerCase();
|
|
23121
|
+
const submenu = String(submenuTestId || "").toLowerCase();
|
|
23122
|
+
const haystack = menu + " " + submenu;
|
|
23123
|
+
if (hasItemName) return "model";
|
|
23124
|
+
if (/^model(?:\s|$)/i.test(String(text || "")) && ariaHaspopup === "menu") return "model_control";
|
|
23125
|
+
if (/^effort(?:\s|$)/i.test(String(text || "")) && ariaHaspopup === "menu") return "effort_control";
|
|
23126
|
+
if (haystack.includes("parameter-submenu") || hasParamCheck) return "parameter";
|
|
23127
|
+
if (/^add models$/i.test(String(text || "").trim())) return "control";
|
|
23128
|
+
if (haystack.includes("model-list") || haystack.includes("model-picker-menu") || haystack.includes("model-selection")) return "model";
|
|
23129
|
+
if (inSubmenu) return "parameter";
|
|
23130
|
+
return "control";
|
|
23131
|
+
}
|
|
23119
23132
|
var EXPR_MODEL_PICKER_ROWS = `(function(){
|
|
23120
23133
|
${MODEL_PICKER_VISIBLE_BODY}
|
|
23134
|
+
${classifyModelPickerRowKind.toString()}
|
|
23121
23135
|
const menus=[...document.querySelectorAll('[data-testid="model-picker-menu"],[data-testid*="model-parameters"],[data-testid*="parameter-submenu"],[data-component="menu-popup"][data-submenu]')].filter(visible);
|
|
23122
23136
|
const rows=[];
|
|
23123
23137
|
const seen=new Set();
|
|
@@ -23128,13 +23142,18 @@ var EXPR_MODEL_PICKER_ROWS = `(function(){
|
|
|
23128
23142
|
const rect=row.getBoundingClientRect();
|
|
23129
23143
|
const text=String(row.innerText||row.textContent||'').replace(/\\s+/g,' ').trim();
|
|
23130
23144
|
if(!text)continue;
|
|
23131
|
-
const menuTestId=String(menu.getAttribute('data-testid')||'')
|
|
23132
|
-
|
|
23133
|
-
|
|
23134
|
-
|
|
23135
|
-
|
|
23136
|
-
|
|
23137
|
-
|
|
23145
|
+
const menuTestId=String(menu.getAttribute('data-testid')||'');
|
|
23146
|
+
const submenuRoot=row.closest('[data-submenu],[data-testid*="parameter-submenu"],[data-testid*="model-list"]');
|
|
23147
|
+
const submenuTestId=String((submenuRoot&&submenuRoot.getAttribute('data-testid'))||'');
|
|
23148
|
+
const kind=classifyModelPickerRowKind(
|
|
23149
|
+
text,
|
|
23150
|
+
!!row.querySelector('.ui-model-picker__item-content-name,.vscode-model-picker__item-content-name'),
|
|
23151
|
+
menuTestId,
|
|
23152
|
+
submenuTestId,
|
|
23153
|
+
row.getAttribute('aria-haspopup'),
|
|
23154
|
+
!!row.querySelector('.ui-model-picker__param-check,.vscode-model-picker__param-check'),
|
|
23155
|
+
!!row.closest('[data-submenu]')
|
|
23156
|
+
);
|
|
23138
23157
|
rows.push({
|
|
23139
23158
|
text,
|
|
23140
23159
|
kind,
|
|
@@ -23152,6 +23171,14 @@ var EXPR_MODEL_PICKER_ROWS = `(function(){
|
|
|
23152
23171
|
function normalizeModelPickerText(value) {
|
|
23153
23172
|
return String(value || "").trim().toLowerCase().replace(/extra[\s_-]*high/g, "xhigh").replace(/[^a-z0-9]+/g, "");
|
|
23154
23173
|
}
|
|
23174
|
+
function isCursorEffortOptionText(text) {
|
|
23175
|
+
return CURSOR_MODEL_EFFORTS.includes(normalizeModelPickerText(text));
|
|
23176
|
+
}
|
|
23177
|
+
function modelPickerAvailableIsDecisive(kind, available) {
|
|
23178
|
+
if (!Array.isArray(available) || available.length === 0) return false;
|
|
23179
|
+
if (kind !== "parameter") return true;
|
|
23180
|
+
return available.some((text) => isCursorEffortOptionText(text));
|
|
23181
|
+
}
|
|
23155
23182
|
function selectModelPickerRow(rows, requested, kind = "model") {
|
|
23156
23183
|
const wanted = normalizeModelPickerText(requested);
|
|
23157
23184
|
if (!wanted) return null;
|
|
@@ -23690,6 +23717,7 @@ var CursorBridge = class {
|
|
|
23690
23717
|
this.projectPath = options.projectPath !== void 0 ? resolve7(String(options.projectPath)) : persistedWorkspace && persistedWorkspace.projectPath || null;
|
|
23691
23718
|
this.workspaceSource = options.projectPath !== void 0 ? "constructor" : persistedWorkspace ? "persistent_init" : "auto_detect";
|
|
23692
23719
|
this.workspaceUpdatedAt = persistedWorkspace && persistedWorkspace.updatedAt || null;
|
|
23720
|
+
this.workspaceConfirmationRequired = this.workspaceKey === "default" && !!persistedWorkspace;
|
|
23693
23721
|
this.modelPreferencesFile = options.modelPreferencesFile === null ? null : resolve7(options.modelPreferencesFile || resolveCursorModelPreferencesFile());
|
|
23694
23722
|
this.modelPreferences = readCursorModelPreferences(this.modelPreferencesFile);
|
|
23695
23723
|
this.sessionFile = options.sessionFile === null ? null : resolve7(options.sessionFile || resolveCursorSessionRegistryFile());
|
|
@@ -23712,6 +23740,8 @@ var CursorBridge = class {
|
|
|
23712
23740
|
const resolvedProjectPath = this.projectPath || this._lastLifecycle && this._lastLifecycle.projectPath || null;
|
|
23713
23741
|
return {
|
|
23714
23742
|
workspaceKey: this.workspaceKey,
|
|
23743
|
+
workspaceConfirmationRequired: this.workspaceConfirmationRequired,
|
|
23744
|
+
workspaceBindingWarning: this.workspaceConfirmationRequired ? "This adapter has no host workspace identity. Run cursor_init with the intended project before using its shared saved binding." : null,
|
|
23715
23745
|
projectPath: resolvedProjectPath,
|
|
23716
23746
|
workspaceSource: this.projectPath ? this.workspaceSource : resolvedProjectPath ? "host_auto_detect" : this.workspaceSource,
|
|
23717
23747
|
workspaceUpdatedAt: this.workspaceUpdatedAt,
|
|
@@ -23746,7 +23776,10 @@ var CursorBridge = class {
|
|
|
23746
23776
|
agentLabel: session.agentLabel || null,
|
|
23747
23777
|
turnIndex: Number(session.turnIndex || 0),
|
|
23748
23778
|
activeTaskId: session.activeTaskId || null,
|
|
23749
|
-
lastTask: session.lastTask
|
|
23779
|
+
lastTask: session.lastTask ? {
|
|
23780
|
+
...session.lastTask,
|
|
23781
|
+
resultUnavailable: this.tasks.get(session.lastTask.taskId)?.result == null
|
|
23782
|
+
} : null,
|
|
23750
23783
|
modelPreference: session.modelPreference || null,
|
|
23751
23784
|
readOnly: session.scopeEnvelope && session.scopeEnvelope.readOnly === true,
|
|
23752
23785
|
allowedPaths: session.scopeEnvelope && Array.isArray(session.scopeEnvelope.allowedPaths) ? session.scopeEnvelope.allowedPaths : [],
|
|
@@ -23758,6 +23791,7 @@ var CursorBridge = class {
|
|
|
23758
23791
|
};
|
|
23759
23792
|
}
|
|
23760
23793
|
_claimNewSession({ taskId, projectPath, readOnly, allowedPaths, modelPreference, requestId, timeoutMs }) {
|
|
23794
|
+
this._ensureTaskCapacity();
|
|
23761
23795
|
if (!this.sessionFile) throw cursorSessionError("SESSION_STORAGE_DISABLED", "persistent session storage is disabled");
|
|
23762
23796
|
const sessionId = createCursorSessionId();
|
|
23763
23797
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -23803,6 +23837,7 @@ var CursorBridge = class {
|
|
|
23803
23837
|
if (requestId && session.lastRequestId === requestId) {
|
|
23804
23838
|
return { duplicate: true, session: { ...session } };
|
|
23805
23839
|
}
|
|
23840
|
+
this._ensureTaskCapacity();
|
|
23806
23841
|
if (!sameSessionProject(session.projectPath, projectPath)) {
|
|
23807
23842
|
throw cursorSessionError("SESSION_WORKSPACE_MISMATCH", `session is bound to ${session.projectPath}`);
|
|
23808
23843
|
}
|
|
@@ -23822,6 +23857,15 @@ var CursorBridge = class {
|
|
|
23822
23857
|
if (session.state !== "ready") {
|
|
23823
23858
|
throw cursorSessionError("SESSION_NOT_READY", `state=${session.state}; recovery=${session.recoveryState || "none"}`);
|
|
23824
23859
|
}
|
|
23860
|
+
if (session.lastTask?.status === "completed" && !session.lastTask.resultCollectedAt && this.tasks.has(session.lastTask.taskId)) {
|
|
23861
|
+
throw cursorSessionError("SESSION_RESULT_UNCOLLECTED", `read cursor_status(task_id=${session.lastTask.taskId}) before continuing`);
|
|
23862
|
+
}
|
|
23863
|
+
if (session.lastTask?.status === "completed" && !session.lastTask.resultCollectedAt && !this.tasks.has(session.lastTask.taskId) && session.recoveryState !== "reconciled_result_uncollected") {
|
|
23864
|
+
throw cursorSessionError("SESSION_RECONCILE_REQUIRED", "the prior reply was not read before this adapter restarted; reconcile the exact Agent before continuing");
|
|
23865
|
+
}
|
|
23866
|
+
if (session.recoveryState === "reconciled_result_uncollected" && (Number(session.turnIndex) <= 1 || session.responseBaseline)) {
|
|
23867
|
+
throw cursorSessionError("SESSION_RESULT_UNCOLLECTED", "collect the interrupted reply with cursor_session_control(action=collect_result) before continuing");
|
|
23868
|
+
}
|
|
23825
23869
|
if (!session.agentId) throw cursorSessionError("SESSION_AGENT_NOT_BOUND", sessionId);
|
|
23826
23870
|
const envelope = session.scopeEnvelope || { readOnly: false, allowedPaths: [] };
|
|
23827
23871
|
if (envelope.readOnly === true) {
|
|
@@ -23841,6 +23885,7 @@ var CursorBridge = class {
|
|
|
23841
23885
|
session.epoch = epoch;
|
|
23842
23886
|
session.activeTaskId = taskId;
|
|
23843
23887
|
session.lastRequestId = requestId || null;
|
|
23888
|
+
delete session.responseBaseline;
|
|
23844
23889
|
session.lease = {
|
|
23845
23890
|
taskId,
|
|
23846
23891
|
instanceId: this.sessionInstanceId,
|
|
@@ -23869,6 +23914,20 @@ var CursorBridge = class {
|
|
|
23869
23914
|
});
|
|
23870
23915
|
job.sessionState = "busy";
|
|
23871
23916
|
}
|
|
23917
|
+
_persistSessionResponseBaseline(job) {
|
|
23918
|
+
if (!job.sessionId) return;
|
|
23919
|
+
const baseline = job.responseBaseline;
|
|
23920
|
+
if (!baseline || !["messageCount", "replyLength", "replyHash"].every((key) => Number.isFinite(baseline[key]))) {
|
|
23921
|
+
throw cursorSessionError("SESSION_BASELINE_UNAVAILABLE", "a numeric response baseline is required before sending");
|
|
23922
|
+
}
|
|
23923
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23924
|
+
const session = registry2.sessions[job.sessionId];
|
|
23925
|
+
if (!session || session.activeTaskId !== job.id || Number(session.epoch) !== Number(job.sessionEpoch)) {
|
|
23926
|
+
throw cursorSessionError("SESSION_STALE_SENDER", "the sender no longer owns this session turn");
|
|
23927
|
+
}
|
|
23928
|
+
session.responseBaseline = { messageCount: baseline.messageCount, replyLength: baseline.replyLength, replyHash: baseline.replyHash };
|
|
23929
|
+
});
|
|
23930
|
+
}
|
|
23872
23931
|
_settleSessionJob(job, outcome, options = {}) {
|
|
23873
23932
|
if (!job || !job.sessionId || !this.sessionFile) return;
|
|
23874
23933
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -23896,6 +23955,7 @@ var CursorBridge = class {
|
|
|
23896
23955
|
}
|
|
23897
23956
|
session.activeTaskId = null;
|
|
23898
23957
|
session.lease = null;
|
|
23958
|
+
if (outcome !== "completed") delete session.responseBaseline;
|
|
23899
23959
|
session.recoveryState = null;
|
|
23900
23960
|
session.attention = null;
|
|
23901
23961
|
session.state = session.agentId ? "ready" : "failed";
|
|
@@ -23910,6 +23970,19 @@ var CursorBridge = class {
|
|
|
23910
23970
|
job.sessionError = error2 instanceof Error ? error2.message : String(error2);
|
|
23911
23971
|
}
|
|
23912
23972
|
}
|
|
23973
|
+
_markTaskResultCollected(job) {
|
|
23974
|
+
if (!isTerminalTask(job) || job.result == null || job.resultCollectedAt) return;
|
|
23975
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23976
|
+
if (job.sessionId && this.sessionFile) {
|
|
23977
|
+
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23978
|
+
const session = registry2.sessions[job.sessionId];
|
|
23979
|
+
if (!session || session.lastTask?.taskId !== job.id || Number(session.epoch) !== Number(job.sessionEpoch)) return;
|
|
23980
|
+
session.lastTask.resultCollectedAt = now;
|
|
23981
|
+
delete session.responseBaseline;
|
|
23982
|
+
});
|
|
23983
|
+
}
|
|
23984
|
+
job.resultCollectedAt = now;
|
|
23985
|
+
}
|
|
23913
23986
|
sessionStatus(sessionId) {
|
|
23914
23987
|
const session = this._readSession(sessionId);
|
|
23915
23988
|
return {
|
|
@@ -23918,17 +23991,28 @@ var CursorBridge = class {
|
|
|
23918
23991
|
...session ? this._sessionView(session) : { sessionId: String(sessionId || "") }
|
|
23919
23992
|
};
|
|
23920
23993
|
}
|
|
23921
|
-
async _reconcileSession(sessionId) {
|
|
23994
|
+
async _reconcileSession(sessionId, { collectResult = false } = {}) {
|
|
23995
|
+
const action = collectResult ? "collect_result" : "reconcile";
|
|
23922
23996
|
const initial = this._readSession(sessionId);
|
|
23923
|
-
if (!initial) return { found: false, action
|
|
23924
|
-
if (
|
|
23925
|
-
return { found: true, changed: false, action
|
|
23997
|
+
if (!initial) return { found: false, action, sessionId };
|
|
23998
|
+
if (collectResult && initial.recoveryState === "reconciled_result_collected") {
|
|
23999
|
+
return { found: true, changed: false, action, ...this._sessionView(initial), state: "already_collected", resultPersisted: false };
|
|
24000
|
+
}
|
|
24001
|
+
const unreadAfterRestart = initial.lastTask?.status === "completed" && !initial.lastTask.resultCollectedAt && !this.tasks.has(initial.lastTask.taskId);
|
|
24002
|
+
if (!collectResult && initial.state === "ready" && !unreadAfterRestart || initial.state === "closed" || initial.state === "failed") {
|
|
24003
|
+
return { found: true, changed: false, action, ...this._sessionView(initial) };
|
|
24004
|
+
}
|
|
24005
|
+
if (collectResult && (initial.state !== "ready" || initial.activeTaskId || initial.lastTask?.status !== "completed" || initial.recoveryState !== "reconciled_result_uncollected")) {
|
|
24006
|
+
throw cursorSessionError("SESSION_RESULT_NOT_READY", "reconcile a completed turn before collecting its reply");
|
|
24007
|
+
}
|
|
24008
|
+
if (collectResult && Number(initial.turnIndex) > 1 && !initial.responseBaseline) {
|
|
24009
|
+
throw cursorSessionError("SESSION_RESULT_BASELINE_UNAVAILABLE", "this older interrupted turn has no reply signature; inspect the original Agent manually before continuing");
|
|
23926
24010
|
}
|
|
23927
24011
|
if (!initial.agentId) {
|
|
23928
24012
|
return {
|
|
23929
24013
|
found: true,
|
|
23930
24014
|
changed: false,
|
|
23931
|
-
action
|
|
24015
|
+
action,
|
|
23932
24016
|
state: "agent_missing",
|
|
23933
24017
|
attention: "No exact agentId is available. Confirm the Cursor task state manually before explicit abandon.",
|
|
23934
24018
|
...this._sessionView(initial)
|
|
@@ -23946,21 +24030,44 @@ var CursorBridge = class {
|
|
|
23946
24030
|
effectiveExecution: "parallel_agent",
|
|
23947
24031
|
lastRecoveryAt: null,
|
|
23948
24032
|
recoveryState: null,
|
|
23949
|
-
error: null
|
|
24033
|
+
error: null,
|
|
24034
|
+
responseBaseline: initial.responseBaseline || null
|
|
23950
24035
|
};
|
|
23951
24036
|
const observed = await this._readStableParallelEntry(probe);
|
|
24037
|
+
let collectedReply;
|
|
24038
|
+
if (collectResult) {
|
|
24039
|
+
if (!observed.stable || !observed.entry || observed.entry.showSpinner || classifyParallelTerminalIcon(observed.entry.icon) !== "completed") {
|
|
24040
|
+
throw cursorSessionError("SESSION_RESULT_NOT_READY", "the exact Agent must have a stable completed state");
|
|
24041
|
+
}
|
|
24042
|
+
try {
|
|
24043
|
+
collectedReply = await this._withUiLock(() => this._collectParallelAgent(probe));
|
|
24044
|
+
} catch (error2) {
|
|
24045
|
+
if (probe.uiDiagnostic && error2 && typeof error2 === "object") error2.uiDiagnostic = probe.uiDiagnostic;
|
|
24046
|
+
throw error2;
|
|
24047
|
+
}
|
|
24048
|
+
if (!String(collectedReply || "").trim()) throw cursorSessionError("SESSION_RESULT_UNAVAILABLE", "no final reply was collected");
|
|
24049
|
+
}
|
|
23952
24050
|
let result;
|
|
23953
24051
|
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
23954
24052
|
const session = registry2.sessions[sessionId];
|
|
23955
24053
|
if (!session) {
|
|
23956
|
-
result = { found: false, action
|
|
24054
|
+
result = { found: false, action, sessionId };
|
|
23957
24055
|
return;
|
|
23958
24056
|
}
|
|
23959
|
-
if (Number(session.epoch || 0) !== Number(initial.epoch || 0) || session.activeTaskId !== initial.activeTaskId) {
|
|
23960
|
-
result = { found: true, changed: false, action
|
|
24057
|
+
if (Number(session.epoch || 0) !== Number(initial.epoch || 0) || session.activeTaskId !== initial.activeTaskId || session.state !== initial.state) {
|
|
24058
|
+
result = { found: true, changed: false, action, state: "stale_observation", ...this._sessionView(session) };
|
|
23961
24059
|
return;
|
|
23962
24060
|
}
|
|
23963
24061
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24062
|
+
if (collectResult) {
|
|
24063
|
+
session.lastTask.resultCollectedAt = now;
|
|
24064
|
+
delete session.responseBaseline;
|
|
24065
|
+
session.recoveryState = "reconciled_result_collected";
|
|
24066
|
+
session.attention = null;
|
|
24067
|
+
session.updatedAt = now;
|
|
24068
|
+
result = { found: true, changed: true, action, ...this._sessionView(session), result: collectedReply, resultPersisted: false, uiDiagnostic: probe.uiDiagnostic || null };
|
|
24069
|
+
return;
|
|
24070
|
+
}
|
|
23964
24071
|
if (!observed.stable || !observed.entry) {
|
|
23965
24072
|
session.state = "needs_attention";
|
|
23966
24073
|
session.recoveryState = probe.recoveryState || "history_unavailable";
|
|
@@ -24000,7 +24107,8 @@ var CursorBridge = class {
|
|
|
24000
24107
|
session.lease = null;
|
|
24001
24108
|
session.state = "ready";
|
|
24002
24109
|
session.recoveryState = terminalClass === "completed" ? "reconciled_result_uncollected" : null;
|
|
24003
|
-
session.attention = terminalClass === "completed" ? "The interrupted turn completed in Cursor, but its reply was not persisted.
|
|
24110
|
+
session.attention = terminalClass === "completed" ? Number(session.turnIndex) > 1 && !session.responseBaseline ? "This older interrupted turn has no saved reply signature. Inspect the original Agent manually before continuing; automatic collection cannot identify its reply safely." : "The interrupted turn completed in Cursor, but its reply was not persisted. Use cursor_session_control(action=collect_result) before the next turn." : null;
|
|
24111
|
+
if (terminalClass !== "completed") delete session.responseBaseline;
|
|
24004
24112
|
session.updatedAt = now;
|
|
24005
24113
|
result = { found: true, changed: true, action: "reconcile", state: terminalClass, ...this._sessionView(session) };
|
|
24006
24114
|
});
|
|
@@ -24010,10 +24118,11 @@ var CursorBridge = class {
|
|
|
24010
24118
|
const id = String(sessionId || "").trim();
|
|
24011
24119
|
if (!id) throw cursorSessionError("SESSION_REQUIRED", "session_id must not be empty");
|
|
24012
24120
|
const action = String(options.action || "").trim().toLowerCase();
|
|
24013
|
-
if (!["reconcile", "close", "forget", "abandon"].includes(action)) {
|
|
24014
|
-
throw cursorSessionError("SESSION_ACTION_UNSUPPORTED", "expected reconcile, close, forget, or abandon");
|
|
24121
|
+
if (!["reconcile", "collect_result", "close", "forget", "abandon"].includes(action)) {
|
|
24122
|
+
throw cursorSessionError("SESSION_ACTION_UNSUPPORTED", "expected reconcile, collect_result, close, forget, or abandon");
|
|
24015
24123
|
}
|
|
24016
24124
|
if (action === "reconcile") return this._reconcileSession(id);
|
|
24125
|
+
if (action === "collect_result") return this._reconcileSession(id, { collectResult: true });
|
|
24017
24126
|
let result;
|
|
24018
24127
|
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
24019
24128
|
const session = registry2.sessions[id];
|
|
@@ -24071,6 +24180,7 @@ var CursorBridge = class {
|
|
|
24071
24180
|
}
|
|
24072
24181
|
const previousProjectPath = this.projectPath || this._lastLifecycle && this._lastLifecycle.projectPath || null;
|
|
24073
24182
|
const saved = writeWorkspaceBinding(this.workspaceFile, this.workspaceKey, projectPath);
|
|
24183
|
+
this.workspaceConfirmationRequired = false;
|
|
24074
24184
|
this.projectPath = saved.projectPath;
|
|
24075
24185
|
this.workspaceSource = "persistent_init";
|
|
24076
24186
|
this.workspaceUpdatedAt = saved.updatedAt;
|
|
@@ -24265,6 +24375,7 @@ var CursorBridge = class {
|
|
|
24265
24375
|
const text = String(query || "").trim();
|
|
24266
24376
|
if (!text) throw new Error("query must not be empty");
|
|
24267
24377
|
if (text.length > 2e4) throw new Error("query exceeds the 20,000-character limit");
|
|
24378
|
+
this._assertWorkspaceConfirmed();
|
|
24268
24379
|
if (this._hasGlobalReservation()) {
|
|
24269
24380
|
throw new Error("A global Cursor reservation has an unconfirmed Stop state; resolve blockingTaskIds from cursor_status first");
|
|
24270
24381
|
}
|
|
@@ -24277,7 +24388,9 @@ var CursorBridge = class {
|
|
|
24277
24388
|
allowedPaths: [],
|
|
24278
24389
|
modelPreference: this._modelPreferenceFor("cce")
|
|
24279
24390
|
});
|
|
24280
|
-
|
|
24391
|
+
const result = normalizeCceSearchResult(await job.promise);
|
|
24392
|
+
this._markTaskResultCollected(job);
|
|
24393
|
+
return result;
|
|
24281
24394
|
}
|
|
24282
24395
|
// Unlisted compatibility aliases for clients that cached the pre-3.0 tool surface.
|
|
24283
24396
|
async search(query) {
|
|
@@ -24293,6 +24406,7 @@ var CursorBridge = class {
|
|
|
24293
24406
|
const text = String(prompt || "").trim();
|
|
24294
24407
|
if (!text) throw new Error("prompt must not be empty");
|
|
24295
24408
|
if (text.length > 1e5) throw new Error("prompt exceeds the 100,000-character limit");
|
|
24409
|
+
this._assertWorkspaceConfirmed();
|
|
24296
24410
|
if (this._hasGlobalReservation()) {
|
|
24297
24411
|
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");
|
|
24298
24412
|
}
|
|
@@ -24428,6 +24542,7 @@ var CursorBridge = class {
|
|
|
24428
24542
|
return `cursor-${Date.now().toString(36)}-${this.nextTaskId++}`;
|
|
24429
24543
|
}
|
|
24430
24544
|
_enqueue(kind, prompt, options) {
|
|
24545
|
+
this._ensureTaskCapacity();
|
|
24431
24546
|
const id = options.taskId || this._nextTaskId();
|
|
24432
24547
|
let resolvePromise;
|
|
24433
24548
|
let rejectPromise;
|
|
@@ -24471,6 +24586,8 @@ var CursorBridge = class {
|
|
|
24471
24586
|
targetUiFlavor: null,
|
|
24472
24587
|
fallbackReason: null,
|
|
24473
24588
|
result: null,
|
|
24589
|
+
resultCollectedAt: null,
|
|
24590
|
+
waitDeadlineAt: null,
|
|
24474
24591
|
error: null,
|
|
24475
24592
|
cancelRequested: false,
|
|
24476
24593
|
cancelReason: null,
|
|
@@ -24973,7 +25090,7 @@ var CursorBridge = class {
|
|
|
24973
25090
|
last = this._inspectModelPickerRows(await this._readModelPickerRows(c), requested, kind);
|
|
24974
25091
|
if (last.row) return { ...last, state: "matched" };
|
|
24975
25092
|
const signature = JSON.stringify(last.available);
|
|
24976
|
-
if (last.available
|
|
25093
|
+
if (modelPickerAvailableIsDecisive(kind, last.available)) {
|
|
24977
25094
|
if (signature !== stableSignature) {
|
|
24978
25095
|
stableSignature = signature;
|
|
24979
25096
|
stableSince = Date.now();
|
|
@@ -25613,6 +25730,7 @@ var CursorBridge = class {
|
|
|
25613
25730
|
await this._applyModelPreference(c, job.modelPreference, job);
|
|
25614
25731
|
this._throwIfCancelledBeforeSend(job);
|
|
25615
25732
|
job.responseBaseline = await this._captureSessionResponseBaseline(c);
|
|
25733
|
+
this._persistSessionResponseBaseline(job);
|
|
25616
25734
|
const providerErrorBaseline = providerErrorSignature(await this._readProviderError(c));
|
|
25617
25735
|
const filled = await this._fillPrompt(c, job.prompt, job);
|
|
25618
25736
|
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
@@ -25687,8 +25805,16 @@ var CursorBridge = class {
|
|
|
25687
25805
|
job.monitorPromise = monitorPromise;
|
|
25688
25806
|
return true;
|
|
25689
25807
|
}
|
|
25808
|
+
_taskWaitDeadline(job, timeoutMs = job?.timeoutMs) {
|
|
25809
|
+
if (!job) return Date.now() + timeoutMs;
|
|
25810
|
+
if (!Number.isFinite(job.waitDeadlineAt)) {
|
|
25811
|
+
const sentAt = Date.parse(job.sentAt || "");
|
|
25812
|
+
job.waitDeadlineAt = (Number.isFinite(sentAt) ? sentAt : Date.now()) + timeoutMs;
|
|
25813
|
+
}
|
|
25814
|
+
return job.waitDeadlineAt;
|
|
25815
|
+
}
|
|
25690
25816
|
async _monitorParallelAgent(job, generation) {
|
|
25691
|
-
const
|
|
25817
|
+
const deadline = this._taskWaitDeadline(job);
|
|
25692
25818
|
let sawGenerating = false;
|
|
25693
25819
|
let completedStable = 0;
|
|
25694
25820
|
let missingPolls = 0;
|
|
@@ -25697,7 +25823,7 @@ var CursorBridge = class {
|
|
|
25697
25823
|
let lastTerminalErrorSignature = "";
|
|
25698
25824
|
let collectionAttempts = 0;
|
|
25699
25825
|
let lastCollectionError = "";
|
|
25700
|
-
while (Date.now()
|
|
25826
|
+
while (Date.now() < deadline) {
|
|
25701
25827
|
if (!this._monitorOwns(job, generation)) return;
|
|
25702
25828
|
await sleep2(1400);
|
|
25703
25829
|
if (!this._monitorOwns(job, generation)) return;
|
|
@@ -25815,51 +25941,63 @@ var CursorBridge = class {
|
|
|
25815
25941
|
}
|
|
25816
25942
|
throw new Error(`After opening ${agentId}, it could not be confirmed as the selected Agent`);
|
|
25817
25943
|
}
|
|
25944
|
+
async _withRestoredAgentSelection(c, job, operation) {
|
|
25945
|
+
const entries = await this._readAgentEntries(c, true);
|
|
25946
|
+
const previousSelectedId = (entries.find((entry) => entry.isSelected) || {}).id || null;
|
|
25947
|
+
try {
|
|
25948
|
+
return await operation();
|
|
25949
|
+
} finally {
|
|
25950
|
+
if (previousSelectedId && previousSelectedId !== job.agentId) {
|
|
25951
|
+
try {
|
|
25952
|
+
const opened = await this._requestExactAgentSelection(c, previousSelectedId);
|
|
25953
|
+
if (opened !== "OPENED") throw new Error(`Unable to restore ${previousSelectedId}: ${opened}`);
|
|
25954
|
+
await this._waitForSelectedAgent(c, previousSelectedId);
|
|
25955
|
+
} catch (error2) {
|
|
25956
|
+
job.uiDiagnostic = { ...job.uiDiagnostic, selectionRestore: { state: "failed", agentId: previousSelectedId, detail: String(error2.message).slice(0, 500) } };
|
|
25957
|
+
}
|
|
25958
|
+
}
|
|
25959
|
+
}
|
|
25960
|
+
}
|
|
25818
25961
|
async _collectParallelAgent(job) {
|
|
25819
25962
|
const page = await findPage({ targetId: job.targetId, purpose: "parallel_agent" });
|
|
25820
25963
|
const c = makeClient(page.webSocketDebuggerUrl);
|
|
25821
25964
|
await c.ready;
|
|
25822
|
-
let previousSelectedId = null;
|
|
25823
25965
|
try {
|
|
25824
|
-
|
|
25825
|
-
|
|
25826
|
-
|
|
25827
|
-
|
|
25828
|
-
|
|
25829
|
-
|
|
25830
|
-
|
|
25831
|
-
|
|
25832
|
-
|
|
25833
|
-
|
|
25834
|
-
|
|
25835
|
-
|
|
25836
|
-
|
|
25837
|
-
|
|
25838
|
-
|
|
25839
|
-
|
|
25840
|
-
|
|
25841
|
-
|
|
25842
|
-
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
|
|
25846
|
-
|
|
25966
|
+
return await this._withRestoredAgentSelection(c, job, async () => {
|
|
25967
|
+
const opened = await evalJS(c, exprOpenAgent(job.agentId));
|
|
25968
|
+
if (opened !== "OPENED") throw new Error(`Unable to open ${job.agentId}: ${opened}`);
|
|
25969
|
+
await this._closeHistory(c);
|
|
25970
|
+
await this._waitForSelectedAgent(c, job.agentId);
|
|
25971
|
+
let answer = "";
|
|
25972
|
+
let lastKey = "";
|
|
25973
|
+
let stable = 0;
|
|
25974
|
+
for (let i = 0; i < 80; i++) {
|
|
25975
|
+
const snap = await this._readResponseSnapshot(c);
|
|
25976
|
+
const candidate = String(await evalJS(c, EXPR_EXTRACT) || "").trim();
|
|
25977
|
+
const hasNewTurn = isSessionTurnReplyReady(job.responseBaseline, snap);
|
|
25978
|
+
if (candidate && hasNewTurn && Number(snap.stop || 0) === 0) {
|
|
25979
|
+
const key = `${snap.replyLength}:${snap.replyHash}`;
|
|
25980
|
+
if (key === lastKey) stable++;
|
|
25981
|
+
else {
|
|
25982
|
+
lastKey = key;
|
|
25983
|
+
stable = 0;
|
|
25984
|
+
}
|
|
25985
|
+
if (stable >= 1) {
|
|
25986
|
+
answer = candidate;
|
|
25987
|
+
break;
|
|
25988
|
+
}
|
|
25847
25989
|
}
|
|
25990
|
+
await sleep2(300);
|
|
25848
25991
|
}
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
if (previousSelectedId && previousSelectedId !== job.agentId) {
|
|
25853
|
-
if (await this._ensureHistoryOpen(c)) {
|
|
25854
|
-
await evalJS(c, exprOpenAgent(previousSelectedId));
|
|
25855
|
-
await this._closeHistory(c);
|
|
25856
|
-
await this._waitForSelectedAgent(c, previousSelectedId);
|
|
25857
|
-
}
|
|
25858
|
-
}
|
|
25859
|
-
return answer;
|
|
25992
|
+
if (!answer) throw new Error(`${job.agentId} was opened, but no final assistant reply was found`);
|
|
25993
|
+
return answer;
|
|
25994
|
+
});
|
|
25860
25995
|
} finally {
|
|
25861
|
-
|
|
25862
|
-
|
|
25996
|
+
try {
|
|
25997
|
+
await this._closeHistory(c);
|
|
25998
|
+
} finally {
|
|
25999
|
+
c.close();
|
|
26000
|
+
}
|
|
25863
26001
|
}
|
|
25864
26002
|
}
|
|
25865
26003
|
_reapWithoutResult(job, error2, evidence) {
|
|
@@ -25874,7 +26012,6 @@ var CursorBridge = class {
|
|
|
25874
26012
|
job.terminalEvidence = evidence || "stable_completed_history_icon";
|
|
25875
26013
|
job.recoveryState = "terminal_result_uncollected";
|
|
25876
26014
|
job.reservationScope = uncertainSubmissionReservationScope(job, error2);
|
|
25877
|
-
job.recoveryState = error2.recoveryState || "monitoring_uncertain_submission";
|
|
25878
26015
|
this._safeSettleSessionJob(job, "terminal_result_uncollected", { needsAttention: true });
|
|
25879
26016
|
}
|
|
25880
26017
|
_abandonJob(job, reason) {
|
|
@@ -25951,6 +26088,7 @@ var CursorBridge = class {
|
|
|
25951
26088
|
job.phase = "running";
|
|
25952
26089
|
job.error = null;
|
|
25953
26090
|
job.recoveryState = "monitoring";
|
|
26091
|
+
if (options.reattach !== false && !job.cancelRequested) job.waitDeadlineAt = Date.now() + job.timeoutMs;
|
|
25954
26092
|
const attached = options.reattach !== false && !job.cancelRequested && this._startParallelMonitor(job);
|
|
25955
26093
|
return { changed: attached, state: "running", monitorReattached: attached, task: this._taskView(job, true) };
|
|
25956
26094
|
}
|
|
@@ -26349,12 +26487,12 @@ var CursorBridge = class {
|
|
|
26349
26487
|
}).catch((e) => console.error("\u26A0\uFE0F Failed to restore the original Cursor Agent: " + e.message));
|
|
26350
26488
|
}
|
|
26351
26489
|
async _waitComplete(c, timeoutMs = QUERY_TIMEOUT, baselineCount = 0, job = null, providerErrorBaseline = "") {
|
|
26352
|
-
const
|
|
26490
|
+
const deadline = this._taskWaitDeadline(job, timeoutMs);
|
|
26353
26491
|
const INTERVAL = 1e3;
|
|
26354
26492
|
let sawStop = false;
|
|
26355
26493
|
let lastReplyKey = "", stableReply = 0;
|
|
26356
26494
|
await sleep2(1200);
|
|
26357
|
-
while (Date.now()
|
|
26495
|
+
while (Date.now() < deadline) {
|
|
26358
26496
|
await this._throwIfNewProviderError(c, providerErrorBaseline);
|
|
26359
26497
|
if (job && job.cancelRequested) {
|
|
26360
26498
|
if (job.agentId) {
|
|
@@ -26419,6 +26557,7 @@ var CursorBridge = class {
|
|
|
26419
26557
|
throw new Error(`Cursor task timed out (${timeoutMs}ms) before generation was confirmed stopped with a complete assistant reply${taskHint}`);
|
|
26420
26558
|
}
|
|
26421
26559
|
_taskView(job, includeResult = false) {
|
|
26560
|
+
if (includeResult) this._markTaskResultCollected(job);
|
|
26422
26561
|
const view = {
|
|
26423
26562
|
taskId: job.id,
|
|
26424
26563
|
kind: job.kind,
|
|
@@ -26457,6 +26596,8 @@ var CursorBridge = class {
|
|
|
26457
26596
|
monitorAttached: job.monitorAttached,
|
|
26458
26597
|
monitorGeneration: job.monitorGeneration,
|
|
26459
26598
|
resultUnavailable: job.resultUnavailable,
|
|
26599
|
+
resultCollectedAt: job.resultCollectedAt || null,
|
|
26600
|
+
waitDeadlineAt: Number.isFinite(job.waitDeadlineAt) ? new Date(job.waitDeadlineAt).toISOString() : null,
|
|
26460
26601
|
terminalEvidence: job.terminalEvidence,
|
|
26461
26602
|
providerError: job.providerError,
|
|
26462
26603
|
uiDiagnostic: job.uiDiagnostic,
|
|
@@ -26466,7 +26607,7 @@ var CursorBridge = class {
|
|
|
26466
26607
|
blocksFifo: this.activeParallel.has(job.id) && !isTerminalTask(job),
|
|
26467
26608
|
blocksAll: this.activeParallel.has(job.id) && !isTerminalTask(job) && job.reservationScope === "global"
|
|
26468
26609
|
};
|
|
26469
|
-
if (includeResult
|
|
26610
|
+
if (includeResult) view.result = job.result;
|
|
26470
26611
|
if (job.phase === "orphaned") {
|
|
26471
26612
|
if (job.recoveryState === "terminal_result_uncollected") {
|
|
26472
26613
|
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.";
|
|
@@ -26476,11 +26617,22 @@ var CursorBridge = class {
|
|
|
26476
26617
|
}
|
|
26477
26618
|
return view;
|
|
26478
26619
|
}
|
|
26479
|
-
|
|
26480
|
-
if (this.
|
|
26620
|
+
_assertWorkspaceConfirmed() {
|
|
26621
|
+
if (this.workspaceConfirmationRequired) {
|
|
26622
|
+
throw new Error("WORKSPACE_CONFIRMATION_REQUIRED: the saved default binding has no host workspace identity. Run cursor_init with the intended project before submitting work.");
|
|
26623
|
+
}
|
|
26624
|
+
}
|
|
26625
|
+
_ensureTaskCapacity() {
|
|
26626
|
+
this._trimTasks(49);
|
|
26627
|
+
if (this.tasks.size >= 50) {
|
|
26628
|
+
throw new Error("TASK_RETENTION_FULL: 50 tasks are active or have unread replies. Read unreadResultTaskIds with cursor_status(task_id), or wait for active tasks before submitting more work.");
|
|
26629
|
+
}
|
|
26630
|
+
}
|
|
26631
|
+
_trimTasks(limit = 50) {
|
|
26632
|
+
if (this.tasks.size <= limit) return;
|
|
26481
26633
|
for (const [id, job] of this.tasks) {
|
|
26482
|
-
if (isTerminalTask(job)) this.tasks.delete(id);
|
|
26483
|
-
if (this.tasks.size <=
|
|
26634
|
+
if (isTerminalTask(job) && (job.result == null || job.resultCollectedAt)) this.tasks.delete(id);
|
|
26635
|
+
if (this.tasks.size <= limit) break;
|
|
26484
26636
|
}
|
|
26485
26637
|
}
|
|
26486
26638
|
async status(taskId = "") {
|
|
@@ -26510,6 +26662,8 @@ var CursorBridge = class {
|
|
|
26510
26662
|
blockedQueuedCount: this.activeParallel.size > 0 ? this.queue.filter((job) => globalBlocked || job.effectiveExecution !== "parallel_agent").length : 0,
|
|
26511
26663
|
activeParallel: [...this.activeParallel.values()].map((job) => this._taskView(job)),
|
|
26512
26664
|
recentTasks: [...this.tasks.values()].slice(-10).map((job) => this._taskView(job)),
|
|
26665
|
+
unreadResultTaskIds: [...this.tasks.values()].filter((job) => isTerminalTask(job) && job.result != null && !job.resultCollectedAt).map((job) => job.id),
|
|
26666
|
+
taskRetentionLimit: 50,
|
|
26513
26667
|
cdpPort: CDP_PORT2,
|
|
26514
26668
|
lifecycle: this._lastLifecycle || {
|
|
26515
26669
|
adapterPid: process.pid,
|
|
@@ -26575,7 +26729,7 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
26575
26729
|
background: { type: "boolean", default: true, description: "When true, return the task ID immediately. When false, wait for the task to finish or need attention." },
|
|
26576
26730
|
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." },
|
|
26577
26731
|
read_only: { type: "boolean", default: false, description: "Set true when Cursor must not change the workspace." },
|
|
26578
|
-
timeout_ms: { type: "integer", minimum: 3e4, maximum: 9e5, default: 6e5, description: "
|
|
26732
|
+
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." },
|
|
26579
26733
|
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." },
|
|
26580
26734
|
completion_contract: { type: "string", description: "Optional acceptance checks or a required final-report format." },
|
|
26581
26735
|
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." },
|
|
@@ -26603,12 +26757,12 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
26603
26757
|
},
|
|
26604
26758
|
{
|
|
26605
26759
|
name: "cursor_session_control",
|
|
26606
|
-
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.",
|
|
26760
|
+
description: "Recover, collect a reply, close, or forget one exact persistent cursor_do session. reconcile checks the exact Agent twice and never resends. collect_result reads the completed reconciled turn from that Agent without sending a prompt; it may temporarily select that Agent. 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.",
|
|
26607
26761
|
inputSchema: {
|
|
26608
26762
|
type: "object",
|
|
26609
26763
|
properties: {
|
|
26610
26764
|
session_id: { type: "string", description: "The exact session ID returned by cursor_do(session_mode=create)." },
|
|
26611
|
-
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." },
|
|
26765
|
+
action: { type: "string", enum: ["reconcile", "collect_result", "close", "forget", "abandon"], description: "reconcile reads the exact Agent state; collect_result retrieves its completed latest turn before continuing; close ends safe continuity; forget removes a closed mapping; abandon releases an uncertain mapping without stop proof." },
|
|
26612
26766
|
confirm: { type: "boolean", default: false, description: "Required for forget and abandon." },
|
|
26613
26767
|
reason: { type: "string", description: "Required and non-empty for abandon." },
|
|
26614
26768
|
acknowledge_may_still_write: { type: "boolean", default: false, description: "Required for abandon; acknowledges that the underlying Cursor Agent may still run or write." }
|
|
@@ -26840,6 +26994,7 @@ export {
|
|
|
26840
26994
|
buildContextEnginePrompt,
|
|
26841
26995
|
buildToolDefinitions,
|
|
26842
26996
|
classifyChatPanelDiagnostic,
|
|
26997
|
+
classifyModelPickerRowKind,
|
|
26843
26998
|
classifyParallelTerminalIcon,
|
|
26844
26999
|
createChatPanelUnavailableError,
|
|
26845
27000
|
createProviderError,
|
|
@@ -26850,10 +27005,12 @@ export {
|
|
|
26850
27005
|
exprInspectWorkspaceRepository,
|
|
26851
27006
|
exprOpenAgent,
|
|
26852
27007
|
isConfirmedCompletedReply,
|
|
27008
|
+
isCursorEffortOptionText,
|
|
26853
27009
|
isDurablyRegisteredParallelEntry,
|
|
26854
27010
|
isSessionTurnReplyReady,
|
|
26855
27011
|
isTargetedStopConfirmed,
|
|
26856
27012
|
lifecycleFailureSummary,
|
|
27013
|
+
modelPickerAvailableIsDecisive,
|
|
26857
27014
|
normalizeAllowedPath,
|
|
26858
27015
|
normalizeCceSearchResult,
|
|
26859
27016
|
normalizeCursorModelEffort,
|
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.14",
|
|
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.14",
|
|
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.
|
|
50
|
+
"embeddedProductVersion": "5.9.1"
|
|
51
51
|
}
|
|
52
52
|
}
|