pi-cursor-bridge 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-bridge",
3
- "version": "6.0.3+codex.20260916040500",
3
+ "version": "6.0.4+codex.20260921093343",
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
@@ -1,8 +1,10 @@
1
1
  # Cursor Bridge for Pi
2
2
 
3
+ > **Windows only:** Cursor Bridge currently supports Windows only. macOS and Linux are not supported or covered by end-to-end acceptance.
4
+
3
5
  Use Cursor's project index and Agent search from Pi, with compact `path:line` evidence and optional bounded Cursor task execution.
4
6
 
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.
7
+ ⭐ If Cursor Bridge helps you, please consider giving it a Star on GitHub—it helps others discover the project.
6
8
 
7
9
  ```powershell
8
10
  pi install npm:pi-cursor-bridge
@@ -10,6 +12,6 @@ pi install npm:pi-cursor-bridge
10
12
 
11
13
  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
14
 
13
- Pi wrapper 0.2.3 embeds Cursor Bridge 6.0.3, including explicit request provenance for CCE and delegated tasks. Its result contract returns a compact receipt for background `cursor_do` work: poll `cursor_status(task_id)` normally, retrieve the terminal raw reply with `cursor_status(task_id, detail="result")`, check `isError` before using its content, and use `detail="full"` only when diagnostics are needed. Cursor must be installed and signed in. Current end-to-end compatibility claims remain scoped to the environments documented in the main repository.
15
+ Pi wrapper 0.2.4 embeds Cursor Bridge 6.0.4, including explicit request provenance for CCE and delegated tasks. Its result contract returns a compact receipt for background `cursor_do` work: poll `cursor_status(task_id)` normally, retrieve the terminal raw reply with `cursor_status(task_id, detail="result")`, check `isError` before using its content, and use `detail="full"` only when diagnostics are needed. Cursor must be installed and signed in. Current end-to-end compatibility claims remain scoped to the environments documented in the main repository.
14
16
 
15
17
  Full documentation: [English](https://github.com/Vanyangyang/cursor-bridge#readme) · [简体中文](https://github.com/Vanyangyang/cursor-bridge/blob/main/README.zh-CN.md)
@@ -3115,6 +3115,7 @@ var require_utils = __commonJS({
3115
3115
  "use strict";
3116
3116
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3117
3117
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
3118
+ var isPort = RegExp.prototype.test.bind(/^\d*$/u);
3118
3119
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3119
3120
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3120
3121
  var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
@@ -3580,8 +3581,12 @@ var require_utils = __commonJS({
3580
3581
  uriTokens.push(host);
3581
3582
  }
3582
3583
  if (typeof component.port === "number" || typeof component.port === "string") {
3584
+ const port = String(component.port);
3585
+ if (!isPort(port)) {
3586
+ throw new TypeError("URI port is malformed.");
3587
+ }
3583
3588
  uriTokens.push(":");
3584
- uriTokens.push(String(component.port));
3589
+ uriTokens.push(port);
3585
3590
  }
3586
3591
  return uriTokens.length ? uriTokens.join("") : void 0;
3587
3592
  }
@@ -4024,12 +4029,15 @@ var require_fast_uri = __commonJS({
4024
4029
  }
4025
4030
  return false;
4026
4031
  }
4032
+ function isIPLiteral(host) {
4033
+ return host[0] === "[" && host[host.length - 1] === "]";
4034
+ }
4027
4035
  function hasMalformedComponentPercentEncoding(matches) {
4028
4036
  const host = matches[4];
4029
- return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !(host[0] === "[" && host[host.length - 1] === "]") && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4037
+ return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4030
4038
  }
4031
4039
  function canonicalizeHost(parsed, options, schemeHandler, isIP) {
4032
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && parsed.host[0] !== "[" && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4040
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4033
4041
  try {
4034
4042
  parsed.host = new URL("http://" + parsed.host).hostname;
4035
4043
  } catch (e) {
@@ -4116,10 +4124,11 @@ var require_fast_uri = __commonJS({
4116
4124
  if (parsed.host) {
4117
4125
  const ipv4result = isIPv4(parsed.host);
4118
4126
  if (ipv4result === false) {
4119
- const bracketedIPLiteral = parsed.host[0] === "[" && parsed.host[parsed.host.length - 1] === "]";
4127
+ const bracketedIPLiteral = isIPLiteral(parsed.host);
4128
+ const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
4120
4129
  const ipv6result = normalizeIPv6(parsed.host);
4121
4130
  isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
4122
- malformedIPLiteral = bracketedIPLiteral && ipv6result.error === true;
4131
+ malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
4123
4132
  parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
4124
4133
  if (malformedIPLiteral) {
4125
4134
  parsed.error = parsed.error || "URI host is malformed.";
@@ -4142,14 +4151,17 @@ var require_fast_uri = __commonJS({
4142
4151
  parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
4143
4152
  }
4144
4153
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
4145
- malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4146
- if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4147
- if (uri.indexOf("%") !== -1) {
4148
- if (parsed.host !== void 0 && !malformedIPLiteral) {
4149
- const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4150
- parsed.host = reescapeHostDelimiters(host, isIP);
4151
- }
4154
+ if (!malformedIPLiteral) {
4155
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4156
+ }
4157
+ if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
4158
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4159
+ if (!isIP) {
4160
+ host = normalizePercentEncoding(host.toLowerCase());
4152
4161
  }
4162
+ parsed.host = reescapeHostDelimiters(host, isIP);
4163
+ }
4164
+ if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4153
4165
  if (parsed.path) {
4154
4166
  parsed.path = normalizePathEncoding(parsed.path);
4155
4167
  }
@@ -11618,6 +11630,11 @@ function cdpUp(timeoutMs = 1500) {
11618
11630
  });
11619
11631
  });
11620
11632
  }
11633
+ function cdpListIsCursor(payload) {
11634
+ const d = typeof payload === "string" ? payload : payload == null ? "" : JSON.stringify(payload);
11635
+ if (WINDSURF_CDP_PATH.test(d)) return false;
11636
+ return CURSOR_CDP_PATH.test(d);
11637
+ }
11621
11638
  function cdpIsCursor(timeoutMs = 1500) {
11622
11639
  return new Promise((resolve9) => {
11623
11640
  const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/list" }, (res) => {
@@ -11625,8 +11642,7 @@ function cdpIsCursor(timeoutMs = 1500) {
11625
11642
  res.on("data", (c) => d += c);
11626
11643
  res.on("end", () => {
11627
11644
  try {
11628
- if (/[\/\\](windsurf)[\/\\]/i.test(d)) return resolve9(false);
11629
- resolve9(/[\/\\]cursor[\/\\](resources|app)|cursor\.exe|vscode-app[^"]*[\/\\]cursor[\/\\]/i.test(d));
11645
+ resolve9(cdpListIsCursor(d));
11630
11646
  } catch {
11631
11647
  resolve9(false);
11632
11648
  }
@@ -12115,7 +12131,7 @@ async function waitForProjectCdpTarget(maxMs, projectPath, listImpl = listCdpPag
12115
12131
  }
12116
12132
  return null;
12117
12133
  }
12118
- var CDP_PORT, CDP_ORIGIN, CDP_HOST, PROJECT_TARGETS, CODEX_THREAD_PROJECTS, loadModule;
12134
+ var CDP_PORT, CDP_ORIGIN, CDP_HOST, PROJECT_TARGETS, CODEX_THREAD_PROJECTS, loadModule, WINDSURF_CDP_PATH, CURSOR_CDP_PATH;
12119
12135
  var init_cursor_ensure_core = __esm({
12120
12136
  "cursor-ensure-core.mjs"() {
12121
12137
  init_cursor_startup_window();
@@ -12126,6 +12142,8 @@ var init_cursor_ensure_core = __esm({
12126
12142
  PROJECT_TARGETS = /* @__PURE__ */ new Map();
12127
12143
  CODEX_THREAD_PROJECTS = /* @__PURE__ */ new Map();
12128
12144
  loadModule = createNodeRequire(import.meta.url);
12145
+ WINDSURF_CDP_PATH = /[\/\\](windsurf)[\/\\]/i;
12146
+ CURSOR_CDP_PATH = /[\/\\]cursor[\/\\](resources|app)|cursor\.exe|vscode-app[^"]*[\/\\]cursor[\/\\]|[\/\\]cursor\.app[\/\\]contents[\/\\]resources[\/\\]app[\/\\]/i;
12129
12147
  }
12130
12148
  });
12131
12149
 
@@ -22967,7 +22985,7 @@ function updateCursorSessionRegistry(filePath, mutator, options = {}) {
22967
22985
  // server.mjs
22968
22986
  init_cursor_ensure_core();
22969
22987
  init_lifecycle_paths();
22970
- var PLUGIN_VERSION = "6.0.3";
22988
+ var PLUGIN_VERSION = "6.0.4";
22971
22989
  var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
22972
22990
  var ORIGIN = `http://localhost:${CDP_PORT2}`;
22973
22991
  var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
@@ -23508,6 +23526,27 @@ var EXPR_MODEL_PICKER_ROWS = `(function(){
23508
23526
  }
23509
23527
  return JSON.stringify({open:menus.length>0,rows});
23510
23528
  })()`;
23529
+ var EXPR_SELECTED_AGENT_MODEL_CONFIG = `(function(){
23530
+ ${INPUT_PICKER_BODY}
23531
+ const inputs=inputCandidates();
23532
+ if(inputs.length!==1)return JSON.stringify({found:false,state:'input_ambiguous',inputCount:inputs.length});
23533
+ const configs=[];
23534
+ for(let node=inputs[0],n=0;node&&n<12;n++,node=node.parentElement){
23535
+ const key=Object.keys(node).find(key=>key.startsWith('__reactFiber$'));
23536
+ for(let f=key&&node[key],i=0;f&&i<48;i++,f=f.return){
23537
+ const reference=f.memoizedProps&&f.memoizedProps.selectedAgent&&f.memoizedProps.selectedAgent.reference;
23538
+ const handle=reference&&(reference._composerDataHandle||reference.composerDataHandle);
23539
+ const data=handle&&(handle._cachedData||handle.data);
23540
+ if(data&&data.modelConfig&&!configs.includes(data.modelConfig))configs.push(data.modelConfig);
23541
+ }
23542
+ }
23543
+ if(configs.length!==1)return JSON.stringify({found:false,state:'model_config_ambiguous',configCount:configs.length});
23544
+ const config=configs[0];
23545
+ return JSON.stringify({found:true,modelName:String(config.modelName||''),selectedModels:(config.selectedModels||[]).map(model=>({
23546
+ modelId:String(model&&model.modelId||''),
23547
+ parameters:Object.fromEntries((model&&model.parameters||[]).map(parameter=>[String(parameter&&parameter.id||''),String(parameter&&parameter.value||'')]))
23548
+ }))});
23549
+ })()`;
23511
23550
  function normalizeModelPickerText(value) {
23512
23551
  return String(value || "").trim().toLowerCase().replace(/extra[\s_-]*high/g, "xhigh").replace(/[^a-z0-9]+/g, "");
23513
23552
  }
@@ -23629,13 +23668,12 @@ var WORKSPACE_SECTION_BODY = String.raw`
23629
23668
  if(matches.length!==1)return {diagnostic:fail('workspace_ambiguous')};
23630
23669
  const {section,project}=matches[0];
23631
23670
  if(project.remoteAuthority||!project.workspaceIdentifier.id)return {diagnostic:fail('workspace_environment_unverified')};
23632
- // Cursor's section-level New Agent action chooses among registered targets.
23633
- // Do not click it when a second workspace could win that choice.
23634
- if(section.source.projects.length!==1)return {diagnostic:fail('workspace_creation_target_ambiguous')};
23635
23671
  if(!section.button)return {diagnostic:fail('workspace_new_agent_unavailable')};
23636
23672
  return {section,diagnostic:{ok:true,state:'workspace_ready',workspace:wanted,
23637
23673
  workspaceId:project.workspaceIdentifier.id,sectionId:section.metadata.id,
23638
- environment:'local',identitySource:'registered_workspace_file_uri',repoUrls:Array.isArray(project.repoUrls)?project.repoUrls:[]}};
23674
+ environment:'local',identitySource:'registered_workspace_file_uri',
23675
+ workspaceSelectionRequired:section.source.projects.length!==1,
23676
+ repoUrls:Array.isArray(project.repoUrls)?project.repoUrls:[]}};
23639
23677
  };
23640
23678
  `;
23641
23679
  function exprCreateAgentForWorkspace(projectPath) {
@@ -23650,6 +23688,49 @@ function exprCreateAgentForWorkspace(projectPath) {
23650
23688
  return JSON.stringify({...result.diagnostic,state:'workspace_agent_creation_requested',previousAgentIds});
23651
23689
  })()`;
23652
23690
  }
23691
+ function matchSelectedAgentModelConfig(snapshot, requestedModel, requestedEffort) {
23692
+ if (!snapshot || snapshot.found !== true) return null;
23693
+ const requested = normalizeModelPickerText(requestedModel);
23694
+ const entries = Array.isArray(snapshot.selectedModels) && snapshot.selectedModels.length ? snapshot.selectedModels : [{ modelId: snapshot.modelName, parameters: {} }];
23695
+ const matches = entries.filter((entry) => {
23696
+ const candidate = normalizeModelPickerText(entry && entry.modelId);
23697
+ return candidate && (candidate === requested || requested === `cursor${candidate}`);
23698
+ });
23699
+ if (matches.length !== 1) return null;
23700
+ const match = matches[0];
23701
+ const effort = normalizeCursorModelEffort(match.parameters && match.parameters.effort, "");
23702
+ if (requestedEffort && effort !== requestedEffort) return null;
23703
+ return { modelId: match.modelId, effort: effort || null };
23704
+ }
23705
+ function exprSelectAgentWorkspace(projectPath) {
23706
+ return `(async function(){
23707
+ const wanted=String(${JSON.stringify(String(projectPath || ""))}).replace(/\\\\/g,'/').replace(/^\\/([a-z]:\\/)/i,'$1').replace(/\\/+$/,'').toLowerCase();
23708
+ const normalize=value=>String(value||'').replace(/\\\\/g,'/').replace(/^\\/([a-z]:\\/)/i,'$1').replace(/\\/+$/,'').toLowerCase();
23709
+ const visible=node=>!!(node&&(node.offsetParent!==null||(node.getClientRects&&node.getClientRects().length>0)));
23710
+ const pathTriggers=()=>[...document.querySelectorAll('button.ui-select-trigger,button[aria-haspopup="menu"]')]
23711
+ .filter(visible).filter(node=>/^[a-z]:\\//i.test(normalize(node.innerText||node.textContent)));
23712
+ const triggers=pathTriggers();
23713
+ const fail=(state,extra={})=>JSON.stringify({ok:false,state,wanted,...extra,
23714
+ nextStep:'Select the exact local workspace '+${JSON.stringify(String(projectPath || ""))}+' in the new Agent project picker before retrying.'});
23715
+ if(triggers.length!==1)return fail(triggers.length?'workspace_project_selector_ambiguous':'workspace_project_selector_unavailable',{triggerCount:triggers.length});
23716
+ triggers[0].click();
23717
+ await new Promise(resolve=>setTimeout(resolve,350));
23718
+ const menus=[...document.querySelectorAll('[data-component="menu-popup"],[role="menu"]')].filter(visible)
23719
+ .filter(menu=>String(menu.getAttribute('aria-label')||'')==='Select a project'||menu.querySelector('[aria-label="Select a project"]'));
23720
+ const rows=[];const seen=new Set();
23721
+ for(const menu of menus){
23722
+ for(const row of menu.querySelectorAll('[data-component="menu-row"][role="menuitem"],[role="option"]')){
23723
+ if(!visible(row)||seen.has(row))continue;seen.add(row);rows.push(row);
23724
+ }
23725
+ }
23726
+ const available=rows.map(row=>String(row.innerText||row.textContent||'').replace(/\\s+/g,' ').trim());
23727
+ const matches=rows.filter(row=>normalize(row.innerText||row.textContent)===wanted);
23728
+ if(matches.length!==1)return fail(matches.length?'workspace_project_option_ambiguous':'workspace_project_option_unavailable',{available});
23729
+ matches[0].click();
23730
+ await new Promise(resolve=>setTimeout(resolve,350));
23731
+ return JSON.stringify({ok:true,state:'workspace_project_selection_requested',workspace:wanted,available});
23732
+ })()`;
23733
+ }
23653
23734
  function exprInspectWorkspaceRepository(projectPath) {
23654
23735
  return `(function(){
23655
23736
  ${WORKSPACE_SECTION_BODY}
@@ -23945,7 +24026,8 @@ var EXPR_PAGE_CAPABILITIES = `(function(){${INPUT_PICKER_BODY}
23945
24026
  const hasLegacyInput=!!document.querySelector('.aislash-editor-input');
23946
24027
  const hasLegacyHistory=!!document.querySelector('.compact-agent-history-react-menu-label')||
23947
24028
  [...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')||'')));
23948
- const dialogs=[...document.querySelectorAll('[role="dialog"],dialog[open],.monaco-dialog-box,.quick-input-widget')].filter(visible);
24029
+ const dialogs=[...document.querySelectorAll('[role="dialog"],dialog[open],.monaco-dialog-box,.quick-input-widget')]
24030
+ .filter(visible).filter(node=>node.getAttribute('data-component')!=='preview-card-layer-popup');
23949
24031
  const dialog=dialogs[dialogs.length-1]||null;
23950
24032
  const dialogHeading=dialog&&dialog.querySelector('h1,h2,h3,[role="heading"]');
23951
24033
  const dialogLabel=dialog?String(dialog.getAttribute('aria-label')||dialog.getAttribute('title')||label(dialogHeading)||'').slice(0,120):null;
@@ -25532,6 +25614,9 @@ var CursorBridge = class {
25532
25614
  const snapshot = await this._readModelPickerValue(c, EXPR_MODEL_PICKER_ROWS, "rows", options);
25533
25615
  return { open: snapshot.open === true, rows: Array.isArray(snapshot.rows) ? snapshot.rows : [] };
25534
25616
  }
25617
+ async _readSelectedAgentModelConfig(c, options) {
25618
+ return this._readModelPickerValue(c, EXPR_SELECTED_AGENT_MODEL_CONFIG, "selected_agent_model_config", options);
25619
+ }
25535
25620
  async _clickModelPickerPoint(c, point) {
25536
25621
  if (!point || !Number.isFinite(Number(point.x)) || !Number.isFinite(Number(point.y))) {
25537
25622
  throw new Error("Cursor model picker returned an invalid target");
@@ -25705,6 +25790,32 @@ var CursorBridge = class {
25705
25790
  const selectionStartedAt = Date.now();
25706
25791
  let stage = "open_picker";
25707
25792
  try {
25793
+ if (this.runtimeMode === "minimal") {
25794
+ stage = "verify_hidden_model_config";
25795
+ const snapshot = await this._readSelectedAgentModelConfig(c);
25796
+ const verified = matchSelectedAgentModelConfig(snapshot, requestedModel, requestedEffort);
25797
+ if (!verified) {
25798
+ throw createModelSelectionError(
25799
+ `Cursor minimal runtime could not confirm ${requestedModel}${requestedEffort ? ` / ${requestedEffort}` : ""}; switch Cursor runtime to normal and retry`,
25800
+ "hidden_model_not_confirmed",
25801
+ true,
25802
+ { available: (snapshot.selectedModels || []).map((entry) => entry.modelId).filter(Boolean) }
25803
+ );
25804
+ }
25805
+ const result2 = {
25806
+ configured: true,
25807
+ applied: true,
25808
+ requestedModel,
25809
+ requestedEffort,
25810
+ effectiveModel: verified.modelId,
25811
+ effectiveEffort: verified.effort,
25812
+ pickerDetail: null,
25813
+ verificationSource: "selected_agent_model_config",
25814
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
25815
+ };
25816
+ if (job) job.modelSelection = result2;
25817
+ return result2;
25818
+ }
25708
25819
  const opened = await this._openModelPicker(c);
25709
25820
  stage = "locate_model";
25710
25821
  let located = await this._findModelPickerModel(c, opened, requestedModel);
@@ -25761,6 +25872,31 @@ var CursorBridge = class {
25761
25872
  await this._clickModelPickerPoint(c, selectedEffort.row);
25762
25873
  await sleep2(550);
25763
25874
  }
25875
+ } else if (requestedEffort) {
25876
+ if (!modelRow.selected) {
25877
+ stage = "select_model";
25878
+ this._throwIfCancelledBeforeSend(job);
25879
+ await this._clickModelPickerPoint(c, modelRow);
25880
+ await sleep2(550);
25881
+ const reopened = await this._openModelPicker(c);
25882
+ located = await this._findModelPickerModel(c, reopened, requestedModel);
25883
+ modelRow = located.modelRow;
25884
+ if (!modelRow) {
25885
+ throw createModelSelectionError(
25886
+ `Cursor model row disappeared while applying effort: ${requestedModel}`,
25887
+ "model_unavailable",
25888
+ true
25889
+ );
25890
+ }
25891
+ }
25892
+ stage = "select_effort";
25893
+ const selectedEffort = await resolveEffort();
25894
+ if (!selectedEffort.row) throwEffortFailure(selectedEffort);
25895
+ if (!selectedEffort.row.selected) {
25896
+ this._throwIfCancelledBeforeSend(job);
25897
+ await this._clickModelPickerPoint(c, selectedEffort.row);
25898
+ await sleep2(550);
25899
+ }
25764
25900
  } else if (!modelRow.selected) {
25765
25901
  stage = "select_model";
25766
25902
  this._throwIfCancelledBeforeSend(job);
@@ -25944,7 +26080,13 @@ var CursorBridge = class {
25944
26080
  if (!created.ok) {
25945
26081
  throw createWorkspaceBindingError(created);
25946
26082
  }
25947
- await sleep2(1100);
26083
+ if (created.workspaceSelectionRequired) {
26084
+ await sleep2(250);
26085
+ const selected = JSON.parse(await evalJS(c, exprSelectAgentWorkspace(options.projectPath)) || "{}");
26086
+ if (options.job) options.job.workspaceBindingChecks.project_selection = { ...selected, checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
26087
+ if (!selected.ok) throw createWorkspaceBindingError(selected);
26088
+ }
26089
+ await sleep2(created.workspaceSelectionRequired ? 750 : 1100);
25948
26090
  const actual = JSON.parse(await evalJS(c, exprInspectAgentWorkspace(options.projectPath, { excludedAgentIds: created.previousAgentIds })) || "{}");
25949
26091
  if (options.job) options.job.workspaceBindingChecks.after_create = { ...actual, checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
25950
26092
  if (!actual.ok) throw createWorkspaceBindingError(actual);
@@ -27856,6 +27998,7 @@ export {
27856
27998
  EXPR_PAGE_CAPABILITIES,
27857
27999
  EXPR_PREPARE_INPUT,
27858
28000
  EXPR_PROVIDER_ERROR,
28001
+ EXPR_SELECTED_AGENT_MODEL_CONFIG,
27859
28002
  EXPR_SNAP,
27860
28003
  EXPR_VISIBLE,
27861
28004
  EXPR_VISIBLE_COMPOSER,
@@ -27876,12 +28019,14 @@ export {
27876
28019
  exprInspectWorkspaceRepository,
27877
28020
  exprOpenAgent,
27878
28021
  exprRegisterAgentsWorkspace,
28022
+ exprSelectAgentWorkspace,
27879
28023
  isConfirmedCompletedReply,
27880
28024
  isCursorEffortOptionText,
27881
28025
  isDurablyRegisteredParallelEntry,
27882
28026
  isSessionTurnReplyReady,
27883
28027
  isTargetedStopConfirmed,
27884
28028
  lifecycleFailureSummary,
28029
+ matchSelectedAgentModelConfig,
27885
28030
  modelPickerAvailableIsDecisive,
27886
28031
  normalizeAllowedPath,
27887
28032
  normalizeCceSearchResult,
@@ -4104,6 +4104,7 @@ __export(cursor_ensure_core_exports, {
4104
4104
  CDP_ORIGIN: () => CDP_ORIGIN,
4105
4105
  CDP_PORT: () => CDP_PORT,
4106
4106
  cdpIsCursor: () => cdpIsCursor,
4107
+ cdpListIsCursor: () => cdpListIsCursor,
4107
4108
  cdpUp: () => cdpUp,
4108
4109
  cursorRunning: () => cursorRunning,
4109
4110
  ensureCursorRunningLocal: () => ensureCursorRunningLocal,
@@ -4298,6 +4299,11 @@ function cdpUp(timeoutMs = 1500) {
4298
4299
  });
4299
4300
  });
4300
4301
  }
4302
+ function cdpListIsCursor(payload) {
4303
+ const d = typeof payload === "string" ? payload : payload == null ? "" : JSON.stringify(payload);
4304
+ if (WINDSURF_CDP_PATH.test(d)) return false;
4305
+ return CURSOR_CDP_PATH.test(d);
4306
+ }
4301
4307
  function cdpIsCursor(timeoutMs = 1500) {
4302
4308
  return new Promise((resolve4) => {
4303
4309
  const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/list" }, (res) => {
@@ -4305,8 +4311,7 @@ function cdpIsCursor(timeoutMs = 1500) {
4305
4311
  res.on("data", (c) => d += c);
4306
4312
  res.on("end", () => {
4307
4313
  try {
4308
- if (/[\/\\](windsurf)[\/\\]/i.test(d)) return resolve4(false);
4309
- resolve4(/[\/\\]cursor[\/\\](resources|app)|cursor\.exe|vscode-app[^"]*[\/\\]cursor[\/\\]/i.test(d));
4314
+ resolve4(cdpListIsCursor(d));
4310
4315
  } catch {
4311
4316
  resolve4(false);
4312
4317
  }
@@ -4795,7 +4800,7 @@ async function waitForProjectCdpTarget(maxMs, projectPath, listImpl = listCdpPag
4795
4800
  }
4796
4801
  return null;
4797
4802
  }
4798
- var CDP_PORT, CDP_ORIGIN, CDP_HOST, PROJECT_TARGETS, CODEX_THREAD_PROJECTS, loadModule;
4803
+ var CDP_PORT, CDP_ORIGIN, CDP_HOST, PROJECT_TARGETS, CODEX_THREAD_PROJECTS, loadModule, WINDSURF_CDP_PATH, CURSOR_CDP_PATH;
4799
4804
  var init_cursor_ensure_core = __esm({
4800
4805
  "cursor-ensure-core.mjs"() {
4801
4806
  init_cursor_startup_window();
@@ -4806,6 +4811,8 @@ var init_cursor_ensure_core = __esm({
4806
4811
  PROJECT_TARGETS = /* @__PURE__ */ new Map();
4807
4812
  CODEX_THREAD_PROJECTS = /* @__PURE__ */ new Map();
4808
4813
  loadModule = createNodeRequire(import.meta.url);
4814
+ WINDSURF_CDP_PATH = /[\/\\](windsurf)[\/\\]/i;
4815
+ CURSOR_CDP_PATH = /[\/\\]cursor[\/\\](resources|app)|cursor\.exe|vscode-app[^"]*[\/\\]cursor[\/\\]|[\/\\]cursor\.app[\/\\]contents[\/\\]resources[\/\\]app[\/\\]/i;
4809
4816
  }
4810
4817
  });
4811
4818
 
@@ -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.2.3",
13
+ packageVersion: "0.2.4",
14
14
  serverName: "cursor-bridge",
15
15
  serverScript: join(packageRoot, "dist", "cursor-bridge.mjs"),
16
16
  cwd: hostCwd,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-cursor-bridge",
3
- "version": "0.2.3",
4
- "description": "Use Cursor Context Engine and bounded, explicitly continuous Cursor Agent execution from the Pi coding agent.",
3
+ "version": "0.2.4",
4
+ "description": "Windows only. Use Cursor Context Engine and bounded, explicitly continuous Cursor Agent execution from the Pi coding agent.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Vanyangyang",
@@ -47,6 +47,6 @@
47
47
  },
48
48
  "piPackage": {
49
49
  "embeddedProduct": "Cursor Bridge",
50
- "embeddedProductVersion": "6.0.3"
50
+ "embeddedProductVersion": "6.0.4"
51
51
  }
52
52
  }