pi-cursor-bridge 0.1.10 → 0.1.12
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 +4 -2
- package/dist/cursor-bridge.mjs +419 -139
- 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.8.
|
|
3
|
+
"version": "5.8.2+codex.20260904092304",
|
|
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
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Use Cursor's project index and Agent search from Pi, with compact `path:line` evidence and optional bounded Cursor task execution.
|
|
4
4
|
|
|
5
|
+
⭐ If Cursor Bridge helps you, please consider giving it a [Star on GitHub](https://github.com/Vanyangyang/cursor-bridge)—it helps others discover the project.
|
|
6
|
+
|
|
5
7
|
```powershell
|
|
6
8
|
pi install npm:pi-cursor-bridge
|
|
7
9
|
```
|
|
8
10
|
|
|
9
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.
|
|
10
12
|
|
|
11
|
-
This Pi package embeds Cursor Bridge 5.8.
|
|
13
|
+
This Pi package embeds Cursor Bridge 5.8.2. Cursor must be installed and signed in. Current end-to-end compatibility claims remain scoped to the environments documented in the main repository.
|
|
12
14
|
|
|
13
|
-
Full documentation: [English](https://github.com/Vanyangyang/cursor-bridge#readme) · [简体中文](https://github.com/Vanyangyang/cursor-bridge/blob/
|
|
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.8.
|
|
22607
|
+
var PLUGIN_VERSION = "5.8.2";
|
|
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);
|
|
@@ -22744,11 +22744,6 @@ function summarizeCdpPages(list) {
|
|
|
22744
22744
|
page: first && first.url ? String(first.url).slice(0, 60) : ""
|
|
22745
22745
|
};
|
|
22746
22746
|
}
|
|
22747
|
-
function isBlankAgentsWindow(page) {
|
|
22748
|
-
if (!page || !isAgentsWindowTitle(page.title)) return false;
|
|
22749
|
-
if (page.probeError || !page.capabilities) return true;
|
|
22750
|
-
return page.capabilities.uiFlavor !== "agents_v2" && !page.capabilities.hasWritableInput;
|
|
22751
|
-
}
|
|
22752
22747
|
var NORMAL_AGENTS_PRESENTATION_REFRESH_MS = 5 * 60 * 1e3;
|
|
22753
22748
|
function shouldRecoverNormalAgentsPresentation({
|
|
22754
22749
|
runtimeMode,
|
|
@@ -22769,25 +22764,6 @@ function shouldRecoverNormalAgentsPresentation({
|
|
|
22769
22764
|
if (!Number.isFinite(previousAt)) return true;
|
|
22770
22765
|
return Number(now) - previousAt >= Math.max(0, Number(refreshMs) || 0);
|
|
22771
22766
|
}
|
|
22772
|
-
async function reloadPageTarget(page, timeoutMs = 5e3) {
|
|
22773
|
-
if (!page || !page.webSocketDebuggerUrl) return false;
|
|
22774
|
-
const c = makeClient(page.webSocketDebuggerUrl);
|
|
22775
|
-
try {
|
|
22776
|
-
await Promise.race([
|
|
22777
|
-
c.ready,
|
|
22778
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("Timed out connecting to the CDP target")), timeoutMs))
|
|
22779
|
-
]);
|
|
22780
|
-
await Promise.race([
|
|
22781
|
-
c.send("Page.reload", { ignoreCache: true }),
|
|
22782
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("Timed out reloading the CDP target")), timeoutMs))
|
|
22783
|
-
]);
|
|
22784
|
-
return true;
|
|
22785
|
-
} catch {
|
|
22786
|
-
return false;
|
|
22787
|
-
} finally {
|
|
22788
|
-
c.close();
|
|
22789
|
-
}
|
|
22790
|
-
}
|
|
22791
22767
|
async function inspectPageTarget(page) {
|
|
22792
22768
|
const c = makeClient(page.webSocketDebuggerUrl);
|
|
22793
22769
|
const probeMs = Number(process.env.CURSOR_BRIDGE_PAGE_PROBE_TIMEOUT || 5e3);
|
|
@@ -22807,24 +22783,6 @@ async function inspectPageTarget(page) {
|
|
|
22807
22783
|
c.close();
|
|
22808
22784
|
}
|
|
22809
22785
|
}
|
|
22810
|
-
async function recoverBlankAgentsWindows(inspected) {
|
|
22811
|
-
const pages = Array.isArray(inspected) ? inspected : [];
|
|
22812
|
-
const recovered = [];
|
|
22813
|
-
for (const page of pages) {
|
|
22814
|
-
if (!isBlankAgentsWindow(page)) {
|
|
22815
|
-
recovered.push(page);
|
|
22816
|
-
continue;
|
|
22817
|
-
}
|
|
22818
|
-
const reloaded = await reloadPageTarget(page);
|
|
22819
|
-
if (!reloaded) {
|
|
22820
|
-
recovered.push(page);
|
|
22821
|
-
continue;
|
|
22822
|
-
}
|
|
22823
|
-
await sleep2(800);
|
|
22824
|
-
recovered.push(await inspectPageTarget(page));
|
|
22825
|
-
}
|
|
22826
|
-
return recovered;
|
|
22827
|
-
}
|
|
22828
22786
|
function pagesWithFlavor(pages, flavor) {
|
|
22829
22787
|
return (Array.isArray(pages) ? pages : []).filter((page) => page && page.capabilities && page.capabilities.uiFlavor === flavor);
|
|
22830
22788
|
}
|
|
@@ -22855,7 +22813,7 @@ async function findPage(options = {}) {
|
|
|
22855
22813
|
if (options.targetId && options.preferAgentsV2 !== true && options.preferLegacy !== true) {
|
|
22856
22814
|
return selectCursorPageCandidate(pages, options);
|
|
22857
22815
|
}
|
|
22858
|
-
const inspected = await
|
|
22816
|
+
const inspected = await Promise.all(pages.map(inspectPageTarget));
|
|
22859
22817
|
return selectPageForUiPreference(inspected, options) || pages[0];
|
|
22860
22818
|
}
|
|
22861
22819
|
function makeClient(wsUrl) {
|
|
@@ -22941,10 +22899,71 @@ var INPUT_PICKER_BODY = `
|
|
|
22941
22899
|
.filter(e=>e.offsetParent!==null&&!e.disabled&&e.getAttribute('aria-disabled')!=='true'&&e.getAttribute('contenteditable')!=='false')
|
|
22942
22900
|
.sort((a,b)=>(b.classList&&b.classList.contains('ui-prompt-input-editor__input')?1:0)-(a.classList&&a.classList.contains('ui-prompt-input-editor__input')?1:0))[0]||null;`;
|
|
22943
22901
|
var EXPR_VISIBLE = `(function(){${INPUT_PICKER_BODY}return !!pickInput();})()`;
|
|
22944
|
-
|
|
22945
|
-
|
|
22946
|
-
|
|
22902
|
+
var CHAT_PANEL_NEXT_STEP = "Complete or close the current Customize/Settings dialog, open Cursor's main Agent/Chat panel or New Chat, then retry the same request.";
|
|
22903
|
+
function classifyChatPanelDiagnostic(snapshot = {}) {
|
|
22904
|
+
const evidence = {
|
|
22905
|
+
writableInputVisible: snapshot.writableInputVisible === true || snapshot.hasWritableInput === true,
|
|
22906
|
+
inputCandidateCount: Number.isFinite(Number(snapshot.inputCandidateCount)) ? Number(snapshot.inputCandidateCount) : 0,
|
|
22907
|
+
settingsOrCustomizeVisible: snapshot.settingsOrCustomizeVisible === true,
|
|
22908
|
+
modalVisible: snapshot.modalVisible === true,
|
|
22909
|
+
modalLabel: snapshot.modalLabel ? String(snapshot.modalLabel) : null,
|
|
22910
|
+
signInControlVisible: snapshot.signInControlVisible === true,
|
|
22911
|
+
signInVisible: snapshot.signInVisible === true,
|
|
22912
|
+
agentSurfaceVisible: snapshot.agentSurfaceVisible === true,
|
|
22913
|
+
composerCount: Number.isFinite(Number(snapshot.composerCount)) ? Number(snapshot.composerCount) : 0,
|
|
22914
|
+
visibilityState: String(snapshot.visibilityState || "unknown"),
|
|
22915
|
+
focused: typeof snapshot.focused === "boolean" ? snapshot.focused : null,
|
|
22916
|
+
pageTitle: snapshot.pageTitle ? String(snapshot.pageTitle) : null,
|
|
22917
|
+
probeError: snapshot.probeError ? String(snapshot.probeError) : null,
|
|
22918
|
+
inputStateChanged: snapshot.inputStateChanged === true
|
|
22919
|
+
};
|
|
22920
|
+
let state = "composer_input_unavailable";
|
|
22921
|
+
let needsAction = "open_new_chat";
|
|
22922
|
+
let message = "Cursor shows an Agent/Chat surface, but no writable input was detected.";
|
|
22923
|
+
if (evidence.signInVisible) {
|
|
22924
|
+
state = "sign_in_required";
|
|
22925
|
+
needsAction = "sign_in_to_cursor";
|
|
22926
|
+
message = "Cursor is showing a sign-in surface instead of a writable Agent/Chat input.";
|
|
22927
|
+
} else if (evidence.settingsOrCustomizeVisible) {
|
|
22928
|
+
state = "settings_or_customize_open";
|
|
22929
|
+
needsAction = "complete_or_close_configuration";
|
|
22930
|
+
message = "Cursor is showing Customize/Settings instead of a writable Agent/Chat input.";
|
|
22931
|
+
} else if (evidence.modalVisible) {
|
|
22932
|
+
state = "modal_dialog_open";
|
|
22933
|
+
needsAction = "complete_or_close_dialog";
|
|
22934
|
+
message = "A visible Cursor dialog is blocking access to the Agent/Chat input.";
|
|
22935
|
+
} else if (evidence.probeError || ["hidden", "prerender", "unloaded"].includes(evidence.visibilityState)) {
|
|
22936
|
+
state = "cursor_window_unavailable";
|
|
22937
|
+
needsAction = "make_cursor_window_available";
|
|
22938
|
+
message = "The selected Cursor page is unavailable or not visible.";
|
|
22939
|
+
} else if (evidence.inputStateChanged) {
|
|
22940
|
+
state = "input_state_changed";
|
|
22941
|
+
needsAction = "open_new_chat";
|
|
22942
|
+
message = "Cursor's Agent/Chat input changed while the request was being prepared.";
|
|
22943
|
+
} else if (!evidence.agentSurfaceVisible && evidence.composerCount === 0) {
|
|
22944
|
+
state = "agent_chat_panel_not_open";
|
|
22945
|
+
needsAction = "open_agent_chat_panel";
|
|
22946
|
+
message = "Cursor's main Agent/Chat panel is not open.";
|
|
22947
|
+
}
|
|
22948
|
+
return {
|
|
22949
|
+
schemaVersion: 1,
|
|
22950
|
+
code: "CURSOR_CHAT_PANEL_UNAVAILABLE",
|
|
22951
|
+
state,
|
|
22952
|
+
message,
|
|
22953
|
+
needsAction,
|
|
22954
|
+
nextStep: CHAT_PANEL_NEXT_STEP,
|
|
22955
|
+
retryable: true,
|
|
22956
|
+
evidence
|
|
22957
|
+
};
|
|
22958
|
+
}
|
|
22959
|
+
function createChatPanelUnavailableError(snapshot) {
|
|
22960
|
+
const uiDiagnostic = classifyChatPanelDiagnostic(snapshot);
|
|
22961
|
+
const error2 = new Error(`${uiDiagnostic.code}: ${uiDiagnostic.message} ${uiDiagnostic.nextStep}`);
|
|
22962
|
+
error2.code = uiDiagnostic.code;
|
|
22963
|
+
error2.uiDiagnostic = uiDiagnostic;
|
|
22964
|
+
return error2;
|
|
22947
22965
|
}
|
|
22966
|
+
var EXPR_PREPARE_INPUT = `(function(){${INPUT_PICKER_BODY}const inp=pickInput();if(!inp)return 'NO_INPUT';inp.focus();try{const s=getSelection();const r=document.createRange();r.selectNodeContents(inp);s.removeAllRanges();s.addRange(r);}catch(e){}return 'READY';})()`;
|
|
22948
22967
|
var EXPR_SNAP = `(function(){
|
|
22949
22968
|
const md=[...document.querySelectorAll('.markdown-root,.aichat-container [class*=markdown]')]
|
|
22950
22969
|
.filter(e=>e.offsetParent!==null&&!e.closest('.ui-model-picker__trigger,[class*=model-picker]'));
|
|
@@ -22955,14 +22974,17 @@ var EXPR_SNAP = `(function(){
|
|
|
22955
22974
|
${INPUT_PICKER_BODY}
|
|
22956
22975
|
const input=pickInput();
|
|
22957
22976
|
const inputText=String(input&&(input.innerText||input.textContent)||'').trim();
|
|
22958
|
-
|
|
22977
|
+
const composer=input&&(input.closest('.composer-bar,.ui-prompt-input,.agent-prompt-input-root')||input.parentElement);
|
|
22978
|
+
const sendButtons=composer?[...composer.querySelectorAll('button.ui-prompt-input-submit-button[data-state="send"],button.ui-prompt-input-submit-button[aria-label="Send message"],button[aria-label="Send"]')]
|
|
22979
|
+
.filter(button=>button.offsetParent!==null&&!button.disabled&&button.closest('.composer-bar,.ui-prompt-input,.agent-prompt-input-root')===composer):[];
|
|
22980
|
+
return JSON.stringify({messageCount:texts.length,replyLength:last.length,replyHash:hash,stop,inputTextLength:inputText.length,sendReady:sendButtons.length===1});
|
|
22959
22981
|
})()`;
|
|
22960
22982
|
var EXPR_CLICK_SEND = `(function(){${INPUT_PICKER_BODY}
|
|
22961
22983
|
const input=pickInput();if(!input)return 'NO_INPUT';
|
|
22962
|
-
const composer=input.closest('.composer-bar')||input.parentElement;
|
|
22984
|
+
const composer=input.closest('.composer-bar,.ui-prompt-input,.agent-prompt-input-root')||input.parentElement;
|
|
22963
22985
|
if(!composer)return 'NO_COMPOSER';
|
|
22964
|
-
const buttons=[...composer.querySelectorAll('button.ui-prompt-input-submit-button[data-state="send"],button[aria-label="Send"]')]
|
|
22965
|
-
.filter(button=>button.offsetParent!==null&&!button.disabled&&button.closest('.composer-bar')===
|
|
22986
|
+
const buttons=[...composer.querySelectorAll('button.ui-prompt-input-submit-button[data-state="send"],button.ui-prompt-input-submit-button[aria-label="Send message"],button[aria-label="Send"]')]
|
|
22987
|
+
.filter(button=>button.offsetParent!==null&&!button.disabled&&button.closest('.composer-bar,.ui-prompt-input,.agent-prompt-input-root')===composer);
|
|
22966
22988
|
if(buttons.length!==1)return buttons.length?'AMBIGUOUS_SEND':'NO_SEND';
|
|
22967
22989
|
buttons[0].click();return 'CLICKED';
|
|
22968
22990
|
})()`;
|
|
@@ -23080,7 +23102,7 @@ function exprClickBoundComposerStop(agentId) {
|
|
|
23080
23102
|
var EXPR_FIND_NEWAGENT = `(function(){const b=[...document.querySelectorAll('button,[role=button],a.action-label,.codicon')].find(e=>{if(e.offsetParent===null||e.closest('.glass-sidebar-agent-menu-btn'))return false;const s=(e.getAttribute('aria-label')||'')+' '+(e.getAttribute('title')||'')+' '+(e.innerText||'');return /(?:^|\\s)New (?:Agent|Chat)(?:\\s|$)/i.test(s);});if(!b)return '';const r=b.getBoundingClientRect();return JSON.stringify({x:Math.round(r.x+r.width/2),y:Math.round(r.y+r.height/2)});})()`;
|
|
23081
23103
|
var MODEL_PICKER_VISIBLE_BODY = `
|
|
23082
23104
|
const visible=(node)=>!!(node&&(node.offsetParent!==null||(node.getClientRects&&node.getClientRects().length>0)));
|
|
23083
|
-
const composers=[...document.querySelectorAll('.composer-bar[data-composer-id],.composer-bar,.ui-prompt-input-root')].filter(visible);
|
|
23105
|
+
const composers=[...document.querySelectorAll('.composer-bar[data-composer-id],.composer-bar,.ui-prompt-input-root,.agent-prompt-input-root,.ui-prompt-input')].filter(visible);
|
|
23084
23106
|
const composer=composers[composers.length-1]||document;
|
|
23085
23107
|
`;
|
|
23086
23108
|
var EXPR_MODEL_PICKER_TRIGGER = `(function(){
|
|
@@ -23136,15 +23158,23 @@ function selectModelPickerRow(rows, requested, kind = "model") {
|
|
|
23136
23158
|
const candidates = (Array.isArray(rows) ? rows : []).filter((row) => row && row.disabled !== true && (kind === "any" || row.kind === kind)).map((row) => {
|
|
23137
23159
|
const normalized = normalizeModelPickerText(row.text);
|
|
23138
23160
|
let score = normalized === wanted ? 1e3 : 0;
|
|
23139
|
-
if (
|
|
23140
|
-
|
|
23141
|
-
|
|
23161
|
+
if (kind !== "parameter") {
|
|
23162
|
+
if (!score && normalized.startsWith(wanted)) score = 800;
|
|
23163
|
+
if (!score && wanted.startsWith(normalized)) score = 700;
|
|
23164
|
+
if (!score && normalized.includes(wanted)) score = 600;
|
|
23165
|
+
}
|
|
23142
23166
|
return { row, score, distance: Math.abs(normalized.length - wanted.length) };
|
|
23143
23167
|
}).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.distance - b.distance);
|
|
23144
23168
|
if (candidates.length === 0) return null;
|
|
23145
23169
|
if (candidates.length > 1 && candidates[0].score === candidates[1].score && candidates[0].distance === candidates[1].distance) return null;
|
|
23146
23170
|
return candidates[0].row;
|
|
23147
23171
|
}
|
|
23172
|
+
function createModelSelectionError(message, failureClass, retryable, diagnostic = {}) {
|
|
23173
|
+
const error2 = new Error(message);
|
|
23174
|
+
error2.code = `CURSOR_MODEL_${String(failureClass || "PROBE_ERROR").toUpperCase()}`;
|
|
23175
|
+
error2.modelSelectionFailure = { failureClass, retryable, ...diagnostic };
|
|
23176
|
+
return error2;
|
|
23177
|
+
}
|
|
23148
23178
|
var WORKSPACE_SECTION_BODY = `
|
|
23149
23179
|
const headText=(el)=>String(el&&(el.innerText||el.textContent)||'').trim();
|
|
23150
23180
|
const isNewAgentButton=(node)=>{
|
|
@@ -23398,17 +23428,44 @@ var REACT_ADAPTER_BODY = `
|
|
|
23398
23428
|
const a=findAdapter();`;
|
|
23399
23429
|
var EXPR_AGENT_ADAPTER_READY = `(function(){${REACT_ADAPTER_BODY}return !!a;})()`;
|
|
23400
23430
|
var EXPR_PAGE_CAPABILITIES = `(function(){${INPUT_PICKER_BODY}
|
|
23431
|
+
const visible=node=>!!(node&&(node.offsetParent!==null||(typeof node.getClientRects==='function'&&node.getClientRects().length>0)));
|
|
23432
|
+
const label=node=>String(node&&(node.getAttribute&&(
|
|
23433
|
+
node.getAttribute('aria-label')||node.getAttribute('title'))||node.innerText||node.textContent)||'').replace(/\\s+/g,' ').trim();
|
|
23401
23434
|
const input=pickInput();
|
|
23435
|
+
const inputCandidateCount=[...document.querySelectorAll(${JSON.stringify(CURSOR_INPUT_SELECTOR)})].filter(visible).length;
|
|
23402
23436
|
const hasV2Sidebar=!!document.querySelector('.glass-sidebar-agent-list-container');
|
|
23403
23437
|
const hasLegacyInput=!!document.querySelector('.aislash-editor-input');
|
|
23404
23438
|
const hasLegacyHistory=!!document.querySelector('.compact-agent-history-react-menu-label')||
|
|
23405
23439
|
[...document.querySelectorAll('button,[role=button],a.action-label,.codicon')].some(e=>/Show Chat History|Chat History|Agent History/i.test((e.getAttribute('aria-label')||'')+' '+(e.getAttribute('title')||'')));
|
|
23440
|
+
const dialogs=[...document.querySelectorAll('[role="dialog"],dialog[open],.monaco-dialog-box,.quick-input-widget')].filter(visible);
|
|
23441
|
+
const dialog=dialogs[dialogs.length-1]||null;
|
|
23442
|
+
const dialogHeading=dialog&&dialog.querySelector('h1,h2,h3,[role="heading"]');
|
|
23443
|
+
const dialogLabel=dialog?String(dialog.getAttribute('aria-label')||dialog.getAttribute('title')||label(dialogHeading)||'').slice(0,120):null;
|
|
23444
|
+
const pageTitle=String(document.title||'').replace(/\\s+/g,' ').trim();
|
|
23445
|
+
const pagePath=String(globalThis.location&&globalThis.location.pathname||'');
|
|
23446
|
+
const headings=[...document.querySelectorAll('h1,h2,h3,[role="heading"]')]
|
|
23447
|
+
.filter(node=>visible(node)&&!node.closest('.markdown-root,.composer-messages-container,.composer-react-transcript-root,.aichat-container'))
|
|
23448
|
+
.map(label).filter(Boolean).slice(0,40);
|
|
23449
|
+
const configurationText=[pageTitle,pagePath,...headings,label(dialogHeading)].join(' ');
|
|
23450
|
+
const configurationSelector='.ui-customize-view,.settings-editor,.settings-body,.preferences-editor,[class*="settings-editor"],[class*="preferences-editor"],[data-testid*="customize"]';
|
|
23451
|
+
const settingsOrCustomizeVisible=[...document.querySelectorAll(configurationSelector)].some(visible)||/(?:settings?|customize|preferences|\u8BBE\u7F6E|\u8A2D\u5B9A|\u81EA\u5B9A\u4E49|\u81EA\u8A02)/i.test(configurationText);
|
|
23452
|
+
const authControls=[...document.querySelectorAll('button,a,[role="button"],[role="link"]')]
|
|
23453
|
+
.filter(node=>visible(node)&&!node.closest('.markdown-root,.composer-messages-container,.composer-react-transcript-root'));
|
|
23454
|
+
const signInPattern=/(?:sign|log)\\s*in(?:\\s+to\\s+cursor)?|authenticate(?:\\s+cursor)?|continue with (?:google|github|email|sso)|\u767B\u5F55|\u767B\u5165/i;
|
|
23455
|
+
const signInControlVisible=authControls.some(node=>signInPattern.test(label(node)));
|
|
23456
|
+
const signInVisible=authControls.some(node=>signInPattern.test(label(node))&&!node.closest(configurationSelector));
|
|
23457
|
+
const composerCount=[...document.querySelectorAll('.composer-bar[data-composer-id],.composer-bar,.ui-prompt-input-root')].filter(visible).length;
|
|
23458
|
+
const agentSurfaceVisible=composerCount>0||[...document.querySelectorAll('.glass-sidebar-agent-list-container,.compact-agent-history-react-menu-label,.aichat-container')].some(visible)||authControls.some(node=>/(?:New (?:Agent|Chat)|(?:Chat|Agent) History)/i.test(label(node)));
|
|
23406
23459
|
const uiFlavor=hasV2Sidebar||(input&&input.classList&&input.classList.contains('ui-prompt-input-editor__input'))?'agents_v2':hasLegacyInput?'legacy':'unknown';
|
|
23407
23460
|
return JSON.stringify({
|
|
23408
|
-
hasWritableInput:!!input,uiFlavor,
|
|
23461
|
+
hasWritableInput:!!input,inputCandidateCount,uiFlavor,
|
|
23409
23462
|
agentAdapterKind:hasV2Sidebar?'agents_v2':hasLegacyHistory?'legacy':'none',
|
|
23410
23463
|
hasComposer:!!document.querySelector('.composer-bar[data-composer-id]'),
|
|
23411
|
-
|
|
23464
|
+
modalVisible:!!dialog,modalLabel:dialogLabel,
|
|
23465
|
+
settingsOrCustomizeVisible,signInControlVisible,signInVisible,agentSurfaceVisible,composerCount,
|
|
23466
|
+
visibilityState:String(document.visibilityState||'unknown'),
|
|
23467
|
+
visible:document.visibilityState==='visible',focused:typeof document.hasFocus==='function'&&document.hasFocus(),
|
|
23468
|
+
documentTitle:pageTitle,pageTitle:pageTitle.slice(0,160)
|
|
23412
23469
|
});
|
|
23413
23470
|
})()`;
|
|
23414
23471
|
var EXPR_HISTORY_ENTRIES = `(function(){${REACT_ADAPTER_BODY}
|
|
@@ -23826,6 +23883,7 @@ var CursorBridge = class {
|
|
|
23826
23883
|
status: outcome,
|
|
23827
23884
|
finishedAt: job.finishedAt || now,
|
|
23828
23885
|
error: job.error || null,
|
|
23886
|
+
uiDiagnostic: job.uiDiagnostic || null,
|
|
23829
23887
|
terminalEvidence: job.terminalEvidence || null,
|
|
23830
23888
|
resultUnavailable: job.resultUnavailable === true
|
|
23831
23889
|
};
|
|
@@ -24426,6 +24484,7 @@ var CursorBridge = class {
|
|
|
24426
24484
|
resultUnavailable: false,
|
|
24427
24485
|
terminalEvidence: null,
|
|
24428
24486
|
providerError: null,
|
|
24487
|
+
uiDiagnostic: null,
|
|
24429
24488
|
sendState: "not_sent",
|
|
24430
24489
|
reservationScope: null,
|
|
24431
24490
|
controlTail: Promise.resolve(),
|
|
@@ -24460,6 +24519,7 @@ var CursorBridge = class {
|
|
|
24460
24519
|
const e = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
24461
24520
|
job.error = e.message;
|
|
24462
24521
|
if (e.providerError) job.providerError = e.providerError;
|
|
24522
|
+
if (e.uiDiagnostic) job.uiDiagnostic = e.uiDiagnostic;
|
|
24463
24523
|
if (e.terminalEvidence) job.terminalEvidence = e.terminalEvidence;
|
|
24464
24524
|
job.status = "failed";
|
|
24465
24525
|
job.phase = "failed";
|
|
@@ -24769,7 +24829,8 @@ var CursorBridge = class {
|
|
|
24769
24829
|
this._throwIfCancelledBeforeSend(options);
|
|
24770
24830
|
await this._applyModelPreference(c, options.modelPreference, options);
|
|
24771
24831
|
this._throwIfCancelledBeforeSend(options);
|
|
24772
|
-
const filled = await
|
|
24832
|
+
const filled = await this._fillPrompt(c, prompt, options);
|
|
24833
|
+
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
24773
24834
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") throw new Error("Failed to enter the query because the input state was invalid");
|
|
24774
24835
|
await sleep2(450);
|
|
24775
24836
|
this._throwIfCancelledBeforeSend(options);
|
|
@@ -24888,25 +24949,83 @@ var CursorBridge = class {
|
|
|
24888
24949
|
modelRow = selectModelPickerRow(expanded && expanded.rows, requestedModel, "model");
|
|
24889
24950
|
return { snapshot: expanded, modelRow };
|
|
24890
24951
|
}
|
|
24891
|
-
|
|
24892
|
-
|
|
24893
|
-
|
|
24894
|
-
|
|
24895
|
-
|
|
24896
|
-
|
|
24952
|
+
_inspectModelPickerRows(snapshot, requested, kind) {
|
|
24953
|
+
const rows = (snapshot && Array.isArray(snapshot.rows) ? snapshot.rows : []).filter((row) => row && row.disabled !== true && row.kind === kind);
|
|
24954
|
+
const wanted = normalizeModelPickerText(requested);
|
|
24955
|
+
const exact = rows.filter((row) => normalizeModelPickerText(row.text) === wanted);
|
|
24956
|
+
return {
|
|
24957
|
+
snapshot,
|
|
24958
|
+
row: exact.length === 1 ? exact[0] : null,
|
|
24959
|
+
ambiguous: exact.length > 1,
|
|
24960
|
+
available: rows.map((row) => row.text)
|
|
24961
|
+
};
|
|
24962
|
+
}
|
|
24963
|
+
async _waitForModelPickerMatch(c, requested, kind, job, options = {}) {
|
|
24964
|
+
const timeoutMs = Math.max(0, Number(options.timeoutMs ?? 700));
|
|
24965
|
+
const pollMs = Math.max(10, Number(options.pollMs ?? 100));
|
|
24966
|
+
const stableMs = Math.max(0, Number(options.stableMs ?? 250));
|
|
24967
|
+
const deadline = Date.now() + timeoutMs;
|
|
24968
|
+
let stableSignature = null;
|
|
24969
|
+
let stableSince = 0;
|
|
24970
|
+
let last = this._inspectModelPickerRows({ open: false, rows: [] }, requested, kind);
|
|
24971
|
+
do {
|
|
24972
|
+
this._throwIfCancelledBeforeSend(job);
|
|
24973
|
+
last = this._inspectModelPickerRows(await this._readModelPickerRows(c), requested, kind);
|
|
24974
|
+
if (last.row) return { ...last, state: "matched" };
|
|
24975
|
+
const signature = JSON.stringify(last.available);
|
|
24976
|
+
if (last.available.length > 0) {
|
|
24977
|
+
if (signature !== stableSignature) {
|
|
24978
|
+
stableSignature = signature;
|
|
24979
|
+
stableSince = Date.now();
|
|
24980
|
+
} else if (Date.now() - stableSince >= stableMs) {
|
|
24981
|
+
return { ...last, state: last.ambiguous ? "ambiguous" : "unsupported" };
|
|
24982
|
+
}
|
|
24983
|
+
} else {
|
|
24984
|
+
stableSignature = null;
|
|
24985
|
+
stableSince = 0;
|
|
24986
|
+
}
|
|
24987
|
+
if (Date.now() >= deadline) break;
|
|
24988
|
+
await sleep2(Math.min(pollMs, Math.max(0, deadline - Date.now())));
|
|
24989
|
+
} while (Date.now() <= deadline);
|
|
24990
|
+
return { ...last, state: last.ambiguous ? "ambiguous" : "not_rendered" };
|
|
24991
|
+
}
|
|
24992
|
+
async _selectedEffortRow(c, modelRow, effort, job) {
|
|
24993
|
+
const requested = cursorEffortUiValue(effort);
|
|
24994
|
+
const snapshot = await this._readModelPickerRows(c);
|
|
24995
|
+
const immediate = this._inspectModelPickerRows(snapshot, requested, "parameter");
|
|
24996
|
+
if (immediate.row) return { ...immediate, state: "matched", attempts: ["visible"] };
|
|
24997
|
+
const attempts = [];
|
|
24998
|
+
const effortControl = (snapshot.rows || []).find((row) => row.kind === "effort_control" && row.disabled !== true);
|
|
24999
|
+
if (effortControl) {
|
|
25000
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25001
|
+
attempts.push("effort_control");
|
|
25002
|
+
await this._clickModelPickerPoint(c, effortControl);
|
|
25003
|
+
const result2 = await this._waitForModelPickerMatch(c, requested, "parameter", job, { timeoutMs: 700 });
|
|
25004
|
+
if (result2.state !== "not_rendered") return { ...result2, attempts };
|
|
24897
25005
|
}
|
|
24898
|
-
|
|
25006
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25007
|
+
attempts.push("model_hover");
|
|
24899
25008
|
await this._hoverModelPickerPoint(c, modelRow);
|
|
24900
|
-
await
|
|
24901
|
-
|
|
24902
|
-
|
|
24903
|
-
|
|
25009
|
+
let result = await this._waitForModelPickerMatch(c, requested, "parameter", job, { timeoutMs: 700 });
|
|
25010
|
+
if (result.state !== "not_rendered") return { ...result, attempts };
|
|
25011
|
+
if (modelRow && modelRow.hasSubmenu) {
|
|
25012
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25013
|
+
attempts.push("model_click");
|
|
24904
25014
|
await this._clickModelPickerPoint(c, modelRow);
|
|
24905
|
-
await
|
|
24906
|
-
snapshot = await this._readModelPickerRows(c);
|
|
24907
|
-
effortRow = selectModelPickerRow(snapshot.rows, cursorEffortUiValue(effort), "parameter");
|
|
25015
|
+
result = await this._waitForModelPickerMatch(c, requested, "parameter", job, { timeoutMs: 1100 });
|
|
24908
25016
|
}
|
|
24909
|
-
return
|
|
25017
|
+
return { ...result, attempts };
|
|
25018
|
+
}
|
|
25019
|
+
async _waitForSelectedModelPickerRow(c, requested, kind, job, timeoutMs = 1500) {
|
|
25020
|
+
const deadline = Date.now() + timeoutMs;
|
|
25021
|
+
do {
|
|
25022
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25023
|
+
const inspected = this._inspectModelPickerRows(await this._readModelPickerRows(c), requested, kind);
|
|
25024
|
+
if (inspected.row && inspected.row.selected) return inspected.row;
|
|
25025
|
+
if (Date.now() >= deadline) break;
|
|
25026
|
+
await sleep2(Math.min(100, Math.max(0, deadline - Date.now())));
|
|
25027
|
+
} while (Date.now() <= deadline);
|
|
25028
|
+
return null;
|
|
24910
25029
|
}
|
|
24911
25030
|
async _applyModelPreference(c, preference, job) {
|
|
24912
25031
|
if (!preference) {
|
|
@@ -24915,63 +25034,167 @@ var CursorBridge = class {
|
|
|
24915
25034
|
}
|
|
24916
25035
|
const requestedModel = String(preference.model || "").trim();
|
|
24917
25036
|
const requestedEffort = preference.effort ? normalizeCursorModelEffort(preference.effort, "") : null;
|
|
24918
|
-
|
|
24919
|
-
let located = await this._findModelPickerModel(c, opened, requestedModel);
|
|
24920
|
-
let modelRow = located.modelRow;
|
|
24921
|
-
if (!modelRow) {
|
|
24922
|
-
throw new Error(`Configured Cursor model is unavailable or ambiguous: ${requestedModel}`);
|
|
24923
|
-
}
|
|
24924
|
-
let selectedEffortRow = null;
|
|
24925
|
-
if (requestedEffort && modelRow.hasSubmenu) {
|
|
24926
|
-
selectedEffortRow = await this._selectedEffortRow(c, modelRow, requestedEffort);
|
|
24927
|
-
if (!selectedEffortRow) {
|
|
24928
|
-
throw new Error(`Cursor model ${requestedModel} does not expose effort ${requestedEffort}`);
|
|
24929
|
-
}
|
|
24930
|
-
if (!selectedEffortRow.selected || !modelRow.selected) {
|
|
24931
|
-
await this._clickModelPickerPoint(c, selectedEffortRow);
|
|
24932
|
-
await sleep2(550);
|
|
24933
|
-
}
|
|
24934
|
-
} else if (!modelRow.selected) {
|
|
24935
|
-
await this._clickModelPickerPoint(c, modelRow);
|
|
24936
|
-
await sleep2(550);
|
|
24937
|
-
}
|
|
24938
|
-
let trigger = await this._readModelPickerTrigger(c);
|
|
24939
|
-
if (!trigger.found || !normalizeModelPickerText(trigger.text).includes(normalizeModelPickerText(requestedModel))) {
|
|
24940
|
-
const reopened = await this._openModelPicker(c);
|
|
24941
|
-
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
24942
|
-
const selected = selectModelPickerRow(located.snapshot.rows.filter((row) => row.selected), requestedModel, "model");
|
|
24943
|
-
if (!selected) throw new Error(`Cursor did not confirm configured model ${requestedModel}`);
|
|
24944
|
-
modelRow = selected;
|
|
24945
|
-
}
|
|
25037
|
+
let modelRow = null;
|
|
24946
25038
|
let effectiveEffort = null;
|
|
24947
|
-
|
|
24948
|
-
|
|
24949
|
-
|
|
25039
|
+
let primaryError = null;
|
|
25040
|
+
try {
|
|
25041
|
+
const opened = await this._openModelPicker(c);
|
|
25042
|
+
let located = await this._findModelPickerModel(c, opened, requestedModel);
|
|
24950
25043
|
modelRow = located.modelRow;
|
|
24951
|
-
if (!modelRow)
|
|
24952
|
-
|
|
24953
|
-
|
|
24954
|
-
|
|
25044
|
+
if (!modelRow) {
|
|
25045
|
+
throw createModelSelectionError(
|
|
25046
|
+
`Configured Cursor model is unavailable or ambiguous: ${requestedModel}`,
|
|
25047
|
+
"model_unavailable",
|
|
25048
|
+
false,
|
|
25049
|
+
{ available: (located.snapshot.rows || []).filter((row) => row.kind === "model").map((row) => row.text) }
|
|
25050
|
+
);
|
|
24955
25051
|
}
|
|
24956
|
-
|
|
24957
|
-
|
|
24958
|
-
await
|
|
25052
|
+
let effortPickerReopened = false;
|
|
25053
|
+
const resolveEffort = async () => {
|
|
25054
|
+
let outcome = await this._selectedEffortRow(c, modelRow, requestedEffort, job);
|
|
25055
|
+
if (!outcome.row && outcome.state === "not_rendered" && !effortPickerReopened) {
|
|
25056
|
+
effortPickerReopened = true;
|
|
25057
|
+
await this._closeModelPicker(c);
|
|
25058
|
+
const reopened = await this._openModelPicker(c);
|
|
25059
|
+
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
25060
|
+
modelRow = located.modelRow;
|
|
25061
|
+
if (!modelRow) {
|
|
25062
|
+
throw createModelSelectionError(
|
|
25063
|
+
`Cursor model row disappeared while applying effort: ${requestedModel}`,
|
|
25064
|
+
"model_unavailable",
|
|
25065
|
+
true
|
|
25066
|
+
);
|
|
25067
|
+
}
|
|
25068
|
+
outcome = await this._selectedEffortRow(c, modelRow, requestedEffort, job);
|
|
25069
|
+
outcome.attempts = ["picker_reopen", ...outcome.attempts || []];
|
|
25070
|
+
}
|
|
25071
|
+
return outcome;
|
|
25072
|
+
};
|
|
25073
|
+
const throwEffortFailure = (outcome) => {
|
|
25074
|
+
const failureClass = outcome.state === "ambiguous" ? "effort_ambiguous" : outcome.state === "unsupported" ? "effort_unsupported" : "effort_menu_not_rendered";
|
|
25075
|
+
const message = failureClass === "effort_menu_not_rendered" ? `Cursor effort menu did not render for model ${requestedModel}` : failureClass === "effort_ambiguous" ? `Cursor model ${requestedModel} exposes ambiguous effort ${requestedEffort}` : `Cursor model ${requestedModel} does not expose effort ${requestedEffort}`;
|
|
25076
|
+
throw createModelSelectionError(
|
|
25077
|
+
message,
|
|
25078
|
+
failureClass,
|
|
25079
|
+
failureClass === "effort_menu_not_rendered",
|
|
25080
|
+
{ available: outcome.available || [], attempts: outcome.attempts || [] }
|
|
25081
|
+
);
|
|
25082
|
+
};
|
|
25083
|
+
if (requestedEffort && modelRow.hasSubmenu) {
|
|
25084
|
+
const selectedEffort = await resolveEffort();
|
|
25085
|
+
if (!selectedEffort.row) throwEffortFailure(selectedEffort);
|
|
25086
|
+
if (!selectedEffort.row.selected || !modelRow.selected) {
|
|
25087
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25088
|
+
await this._clickModelPickerPoint(c, selectedEffort.row);
|
|
25089
|
+
await sleep2(550);
|
|
25090
|
+
}
|
|
25091
|
+
} else if (!modelRow.selected) {
|
|
25092
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25093
|
+
await this._clickModelPickerPoint(c, modelRow);
|
|
25094
|
+
await sleep2(550);
|
|
24959
25095
|
}
|
|
24960
|
-
|
|
24961
|
-
|
|
24962
|
-
|
|
24963
|
-
|
|
24964
|
-
const
|
|
24965
|
-
|
|
24966
|
-
|
|
25096
|
+
let trigger2 = await this._readModelPickerTrigger(c);
|
|
25097
|
+
if (!trigger2.found || !normalizeModelPickerText(trigger2.text).includes(normalizeModelPickerText(requestedModel))) {
|
|
25098
|
+
const reopened = await this._openModelPicker(c);
|
|
25099
|
+
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
25100
|
+
const selected = selectModelPickerRow(located.snapshot.rows.filter((row) => row.selected), requestedModel, "model");
|
|
25101
|
+
if (!selected) {
|
|
25102
|
+
throw createModelSelectionError(
|
|
25103
|
+
`Cursor did not confirm configured model ${requestedModel}`,
|
|
25104
|
+
"model_not_confirmed",
|
|
25105
|
+
true
|
|
25106
|
+
);
|
|
25107
|
+
}
|
|
25108
|
+
modelRow = selected;
|
|
25109
|
+
}
|
|
25110
|
+
if (requestedEffort) {
|
|
25111
|
+
const reopened = await this._openModelPicker(c);
|
|
25112
|
+
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
25113
|
+
modelRow = located.modelRow;
|
|
25114
|
+
if (!modelRow) {
|
|
25115
|
+
throw createModelSelectionError(
|
|
25116
|
+
`Cursor model row disappeared while applying effort: ${requestedModel}`,
|
|
25117
|
+
"model_unavailable",
|
|
25118
|
+
true
|
|
25119
|
+
);
|
|
25120
|
+
}
|
|
25121
|
+
const effortOutcome = await resolveEffort();
|
|
25122
|
+
if (!effortOutcome.row) throwEffortFailure(effortOutcome);
|
|
25123
|
+
let effortRow = effortOutcome.row;
|
|
25124
|
+
if (!effortRow.selected) {
|
|
25125
|
+
effortRow = await this._waitForSelectedModelPickerRow(
|
|
25126
|
+
c,
|
|
25127
|
+
cursorEffortUiValue(requestedEffort),
|
|
25128
|
+
"parameter",
|
|
25129
|
+
job
|
|
25130
|
+
);
|
|
25131
|
+
}
|
|
24967
25132
|
if (!effortRow || !effortRow.selected) {
|
|
24968
|
-
throw
|
|
25133
|
+
throw createModelSelectionError(
|
|
25134
|
+
`Cursor did not confirm effort ${requestedEffort} for model ${requestedModel}`,
|
|
25135
|
+
"effort_not_confirmed",
|
|
25136
|
+
true
|
|
25137
|
+
);
|
|
25138
|
+
}
|
|
25139
|
+
effectiveEffort = requestedEffort;
|
|
25140
|
+
}
|
|
25141
|
+
} catch (error2) {
|
|
25142
|
+
primaryError = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
25143
|
+
let failure = primaryError.modelSelectionFailure;
|
|
25144
|
+
if (!failure) {
|
|
25145
|
+
const pickerUnavailable = /model picker (?:is unavailable|did not open)/i.test(primaryError.message);
|
|
25146
|
+
failure = {
|
|
25147
|
+
failureClass: pickerUnavailable ? "picker_did_not_open" : "probe_error",
|
|
25148
|
+
retryable: true
|
|
25149
|
+
};
|
|
25150
|
+
}
|
|
25151
|
+
const diagnostic = {
|
|
25152
|
+
configured: true,
|
|
25153
|
+
applied: false,
|
|
25154
|
+
requestedModel,
|
|
25155
|
+
requestedEffort,
|
|
25156
|
+
failureClass: failure.failureClass,
|
|
25157
|
+
retryable: failure.retryable === true,
|
|
25158
|
+
errorCode: primaryError.code || `CURSOR_MODEL_${String(failure.failureClass).toUpperCase()}`,
|
|
25159
|
+
available: failure.available || [],
|
|
25160
|
+
attempts: failure.attempts || [],
|
|
25161
|
+
runtimeMode: this.runtimeMode,
|
|
25162
|
+
lastError: primaryError.message,
|
|
25163
|
+
failedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25164
|
+
};
|
|
25165
|
+
primaryError.modelSelection = diagnostic;
|
|
25166
|
+
if (job) job.modelSelection = diagnostic;
|
|
25167
|
+
throw primaryError;
|
|
25168
|
+
} finally {
|
|
25169
|
+
try {
|
|
25170
|
+
await this._closeModelPicker(c);
|
|
25171
|
+
} catch (cleanupError) {
|
|
25172
|
+
const message = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
|
|
25173
|
+
if (!primaryError) {
|
|
25174
|
+
const error2 = createModelSelectionError(message, "picker_cleanup_failed", true);
|
|
25175
|
+
const diagnostic = {
|
|
25176
|
+
configured: true,
|
|
25177
|
+
applied: false,
|
|
25178
|
+
requestedModel,
|
|
25179
|
+
requestedEffort,
|
|
25180
|
+
failureClass: "picker_cleanup_failed",
|
|
25181
|
+
retryable: true,
|
|
25182
|
+
errorCode: error2.code,
|
|
25183
|
+
available: [],
|
|
25184
|
+
attempts: [],
|
|
25185
|
+
runtimeMode: this.runtimeMode,
|
|
25186
|
+
lastError: message,
|
|
25187
|
+
failedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25188
|
+
};
|
|
25189
|
+
error2.modelSelection = diagnostic;
|
|
25190
|
+
if (job) job.modelSelection = diagnostic;
|
|
25191
|
+
throw error2;
|
|
24969
25192
|
}
|
|
25193
|
+
if (primaryError.modelSelection) primaryError.modelSelection.cleanupError = message;
|
|
25194
|
+
if (job && job.modelSelection) job.modelSelection.cleanupError = message;
|
|
24970
25195
|
}
|
|
24971
|
-
effectiveEffort = requestedEffort;
|
|
24972
25196
|
}
|
|
24973
|
-
await this.
|
|
24974
|
-
trigger = await this._readModelPickerTrigger(c);
|
|
25197
|
+
const trigger = await this._readModelPickerTrigger(c);
|
|
24975
25198
|
const result = {
|
|
24976
25199
|
configured: true,
|
|
24977
25200
|
applied: true,
|
|
@@ -24985,19 +25208,24 @@ var CursorBridge = class {
|
|
|
24985
25208
|
if (job) job.modelSelection = result;
|
|
24986
25209
|
return result;
|
|
24987
25210
|
}
|
|
24988
|
-
async
|
|
24989
|
-
|
|
24990
|
-
|
|
24991
|
-
|
|
24992
|
-
|
|
24993
|
-
vis = await evalJS(c, EXPR_VISIBLE);
|
|
24994
|
-
}
|
|
24995
|
-
if (!vis) {
|
|
24996
|
-
await chord(c, 2, "I", "KeyI", 73);
|
|
24997
|
-
await sleep2(1300);
|
|
24998
|
-
vis = await evalJS(c, EXPR_VISIBLE);
|
|
25211
|
+
async _readChatPanelDiagnostic(c) {
|
|
25212
|
+
try {
|
|
25213
|
+
return JSON.parse(await evalJS(c, EXPR_PAGE_CAPABILITIES) || "{}");
|
|
25214
|
+
} catch (error2) {
|
|
25215
|
+
return { probeError: error2 instanceof Error ? error2.message : String(error2) };
|
|
24999
25216
|
}
|
|
25000
|
-
|
|
25217
|
+
}
|
|
25218
|
+
async _ensureChatPanel(c) {
|
|
25219
|
+
const snapshot = await this._readChatPanelDiagnostic(c);
|
|
25220
|
+
if (snapshot.modalVisible !== true && snapshot.hasWritableInput === true) return snapshot;
|
|
25221
|
+
throw createChatPanelUnavailableError(snapshot);
|
|
25222
|
+
}
|
|
25223
|
+
async _throwChatPanelUnavailableAfterNoInput(c) {
|
|
25224
|
+
const snapshot = await this._readChatPanelDiagnostic(c);
|
|
25225
|
+
throw createChatPanelUnavailableError({
|
|
25226
|
+
...snapshot,
|
|
25227
|
+
inputStateChanged: snapshot.hasWritableInput === true
|
|
25228
|
+
});
|
|
25001
25229
|
}
|
|
25002
25230
|
// 清空对话上下文:定位 "New Agent" 钮后【Alt+click】——Alt 修饰使其执行 Replace Agent(清空旧对话),
|
|
25003
25231
|
// 而非新建(aria 标注 "New Agent (Ctrl+N) / [Alt] Replace Agent")。2026-06-08 实测回复区 markdown DOM 清空
|
|
@@ -25145,6 +25373,38 @@ var CursorBridge = class {
|
|
|
25145
25373
|
}
|
|
25146
25374
|
}
|
|
25147
25375
|
}
|
|
25376
|
+
async _fillPrompt(c, text, job, timeoutMs = 2500) {
|
|
25377
|
+
const prepared = await evalJS(c, EXPR_PREPARE_INPUT);
|
|
25378
|
+
if (prepared !== "READY") return prepared;
|
|
25379
|
+
try {
|
|
25380
|
+
await c.send("Input.insertText", { text: String(text) });
|
|
25381
|
+
} catch {
|
|
25382
|
+
return "EXEC_FAIL";
|
|
25383
|
+
}
|
|
25384
|
+
const deadline = Date.now() + timeoutMs;
|
|
25385
|
+
let snapshot = {};
|
|
25386
|
+
do {
|
|
25387
|
+
this._throwIfCancelledBeforeSend(job);
|
|
25388
|
+
try {
|
|
25389
|
+
snapshot = JSON.parse(await evalJS(c, EXPR_SNAP));
|
|
25390
|
+
} catch {
|
|
25391
|
+
}
|
|
25392
|
+
if (Number(snapshot.inputTextLength || 0) > 0 && snapshot.sendReady === true) {
|
|
25393
|
+
return String(text).slice(0, 30);
|
|
25394
|
+
}
|
|
25395
|
+
if (Date.now() >= deadline) break;
|
|
25396
|
+
await sleep2(Math.min(100, Math.max(0, deadline - Date.now())));
|
|
25397
|
+
} while (Date.now() <= deadline);
|
|
25398
|
+
const error2 = new Error("Cursor composer did not become send-ready after trusted input");
|
|
25399
|
+
error2.code = "CURSOR_COMPOSER_NOT_SEND_READY";
|
|
25400
|
+
error2.confirmedNotSent = true;
|
|
25401
|
+
error2.composerDiagnostic = {
|
|
25402
|
+
inputTextLength: Number(snapshot.inputTextLength || 0),
|
|
25403
|
+
sendReady: snapshot.sendReady === true,
|
|
25404
|
+
runtimeMode: this.runtimeMode
|
|
25405
|
+
};
|
|
25406
|
+
throw error2;
|
|
25407
|
+
}
|
|
25148
25408
|
async _confirmSubmission(c, baselineCount = 0, providerErrorBaseline = "") {
|
|
25149
25409
|
const accepted = async () => {
|
|
25150
25410
|
await this._throwIfNewProviderError(c, providerErrorBaseline);
|
|
@@ -25234,7 +25494,8 @@ var CursorBridge = class {
|
|
|
25234
25494
|
this._throwIfCancelledBeforeSend(job);
|
|
25235
25495
|
await this._applyModelPreference(c, job.modelPreference, job);
|
|
25236
25496
|
this._throwIfCancelledBeforeSend(job);
|
|
25237
|
-
const filled = await
|
|
25497
|
+
const filled = await this._fillPrompt(c, job.prompt, job);
|
|
25498
|
+
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
25238
25499
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") throw new Error("Failed to enter the parallel_agent task");
|
|
25239
25500
|
await sleep2(350);
|
|
25240
25501
|
this._throwIfCancelledBeforeSend(job);
|
|
@@ -25353,7 +25614,8 @@ var CursorBridge = class {
|
|
|
25353
25614
|
this._throwIfCancelledBeforeSend(job);
|
|
25354
25615
|
job.responseBaseline = await this._captureSessionResponseBaseline(c);
|
|
25355
25616
|
const providerErrorBaseline = providerErrorSignature(await this._readProviderError(c));
|
|
25356
|
-
const filled = await
|
|
25617
|
+
const filled = await this._fillPrompt(c, job.prompt, job);
|
|
25618
|
+
if (filled === "NO_INPUT") await this._throwChatPanelUnavailableAfterNoInput(c);
|
|
25357
25619
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") {
|
|
25358
25620
|
throw cursorSessionError("SESSION_INPUT_FAILED", "failed to enter the continued task");
|
|
25359
25621
|
}
|
|
@@ -26197,6 +26459,7 @@ var CursorBridge = class {
|
|
|
26197
26459
|
resultUnavailable: job.resultUnavailable,
|
|
26198
26460
|
terminalEvidence: job.terminalEvidence,
|
|
26199
26461
|
providerError: job.providerError,
|
|
26462
|
+
uiDiagnostic: job.uiDiagnostic,
|
|
26200
26463
|
sendState: job.sendState,
|
|
26201
26464
|
reservationScope: job.reservationScope,
|
|
26202
26465
|
reservationHeld: this.activeParallel.has(job.id),
|
|
@@ -26412,6 +26675,21 @@ async function ensureBridgeCursor(targetBridge, reason) {
|
|
|
26412
26675
|
}
|
|
26413
26676
|
return r;
|
|
26414
26677
|
}
|
|
26678
|
+
function toolErrorResult(error2) {
|
|
26679
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
26680
|
+
if (error2 && error2.uiDiagnostic) {
|
|
26681
|
+
const payload = {
|
|
26682
|
+
error: { code: error2.code || error2.uiDiagnostic.code || "CURSOR_BRIDGE_ERROR", message },
|
|
26683
|
+
uiDiagnostic: error2.uiDiagnostic
|
|
26684
|
+
};
|
|
26685
|
+
return {
|
|
26686
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
26687
|
+
structuredContent: payload,
|
|
26688
|
+
isError: true
|
|
26689
|
+
};
|
|
26690
|
+
}
|
|
26691
|
+
return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
|
|
26692
|
+
}
|
|
26415
26693
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
26416
26694
|
tools: buildToolDefinitions(bridge)
|
|
26417
26695
|
}));
|
|
@@ -26513,7 +26791,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
26513
26791
|
}
|
|
26514
26792
|
throw new Error(`Unknown tool: ${name}`);
|
|
26515
26793
|
} catch (error2) {
|
|
26516
|
-
return
|
|
26794
|
+
return toolErrorResult(error2);
|
|
26517
26795
|
}
|
|
26518
26796
|
});
|
|
26519
26797
|
async function main() {
|
|
@@ -26553,6 +26831,7 @@ export {
|
|
|
26553
26831
|
EXPR_MODEL_PICKER_ROWS,
|
|
26554
26832
|
EXPR_MODEL_PICKER_TRIGGER,
|
|
26555
26833
|
EXPR_PAGE_CAPABILITIES,
|
|
26834
|
+
EXPR_PREPARE_INPUT,
|
|
26556
26835
|
EXPR_PROVIDER_ERROR,
|
|
26557
26836
|
EXPR_VISIBLE,
|
|
26558
26837
|
EXPR_VISIBLE_COMPOSER,
|
|
@@ -26560,16 +26839,16 @@ export {
|
|
|
26560
26839
|
bridge,
|
|
26561
26840
|
buildContextEnginePrompt,
|
|
26562
26841
|
buildToolDefinitions,
|
|
26842
|
+
classifyChatPanelDiagnostic,
|
|
26563
26843
|
classifyParallelTerminalIcon,
|
|
26844
|
+
createChatPanelUnavailableError,
|
|
26564
26845
|
createProviderError,
|
|
26565
26846
|
cursorStartupBehavior,
|
|
26566
26847
|
exprClickBoundComposerStop,
|
|
26567
26848
|
exprClickSelectedAgentStop,
|
|
26568
26849
|
exprCreateAgentForWorkspace,
|
|
26569
|
-
exprFill,
|
|
26570
26850
|
exprInspectWorkspaceRepository,
|
|
26571
26851
|
exprOpenAgent,
|
|
26572
|
-
isBlankAgentsWindow,
|
|
26573
26852
|
isConfirmedCompletedReply,
|
|
26574
26853
|
isDurablyRegisteredParallelEntry,
|
|
26575
26854
|
isSessionTurnReplyReady,
|
|
@@ -26597,6 +26876,7 @@ export {
|
|
|
26597
26876
|
shouldRecoverNormalAgentsPresentation,
|
|
26598
26877
|
shouldScheduleParallelOriginRestore,
|
|
26599
26878
|
summarizeCdpPages,
|
|
26879
|
+
toolErrorResult,
|
|
26600
26880
|
uncertainSubmissionReservationScope,
|
|
26601
26881
|
updateStableEntryObservation
|
|
26602
26882
|
};
|
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.12",
|
|
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.12",
|
|
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.8.
|
|
50
|
+
"embeddedProductVersion": "5.8.2"
|
|
51
51
|
}
|
|
52
52
|
}
|