pi-cursor-bridge 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/cursor-bridge.mjs +362 -90
- package/extensions/index.ts +1 -1
- package/package.json +2 -2
- package/skills/cce-routing/SKILL.md +1 -1
- package/skills/cursor-delegate/SKILL.md +2 -0
- package/skills/cursor-delegate/references/delegation-contract.md +3 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-bridge",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.10.1+codex.20260908161937",
|
|
4
4
|
"description": "Evidence-backed Cursor Context Engine search and bounded Cursor Agent execution, including a UI-suppressed minimal runtime.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vanyangyang"
|
package/README.md
CHANGED
|
@@ -10,6 +10,6 @@ pi install npm:pi-cursor-bridge
|
|
|
10
10
|
|
|
11
11
|
Restart Pi after installation. Initialize the current project by asking Pi to initialize Cursor Bridge for the absolute project path, then use it normally. The package includes the `cce-routing` and `cursor-delegate` Skills and registers the native Cursor Bridge MCP tools directly in Pi.
|
|
12
12
|
|
|
13
|
-
This Pi package embeds Cursor Bridge 5.
|
|
13
|
+
This Pi package embeds Cursor Bridge 5.10.1, including explicit request provenance for CCE and delegated tasks. Cursor must be installed and signed in. Current end-to-end compatibility claims remain scoped to the environments documented in the main repository.
|
|
14
14
|
|
|
15
15
|
Full documentation: [English](https://github.com/Vanyangyang/cursor-bridge#readme) · [简体中文](https://github.com/Vanyangyang/cursor-bridge/blob/main/README.zh-CN.md)
|
package/dist/cursor-bridge.mjs
CHANGED
|
@@ -22344,6 +22344,7 @@ var StdioServerTransport = class {
|
|
|
22344
22344
|
|
|
22345
22345
|
// server.mjs
|
|
22346
22346
|
import { basename as basename6, dirname as dirname6, join as join8, resolve as resolve7 } from "node:path";
|
|
22347
|
+
import { statSync as statSync3 } from "node:fs";
|
|
22347
22348
|
|
|
22348
22349
|
// node_modules/ws/wrapper.mjs
|
|
22349
22350
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -22604,7 +22605,7 @@ function updateCursorSessionRegistry(filePath, mutator, options = {}) {
|
|
|
22604
22605
|
// server.mjs
|
|
22605
22606
|
init_cursor_ensure_core();
|
|
22606
22607
|
init_lifecycle_paths();
|
|
22607
|
-
var PLUGIN_VERSION = "5.
|
|
22608
|
+
var PLUGIN_VERSION = "5.10.1";
|
|
22608
22609
|
var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
22609
22610
|
var ORIGIN = `http://localhost:${CDP_PORT2}`;
|
|
22610
22611
|
var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
|
|
@@ -22646,8 +22647,28 @@ function searchResultContract() {
|
|
|
22646
22647
|
"confidence: <high|medium|low> (rate retrieval evidence only, not code correctness)"
|
|
22647
22648
|
];
|
|
22648
22649
|
}
|
|
22649
|
-
function
|
|
22650
|
+
function normalizeRequestContext(value) {
|
|
22651
|
+
if (value === void 0) return { sender: "unknown", source: "unknown" };
|
|
22652
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["sender", "source"].includes(key))) {
|
|
22653
|
+
throw new Error("request_context must contain only sender and source");
|
|
22654
|
+
}
|
|
22655
|
+
const sender = value.sender === void 0 ? "unknown" : value.sender;
|
|
22656
|
+
const source = value.source === void 0 ? "unknown" : value.source;
|
|
22657
|
+
if (!["user", "model", "unknown"].includes(sender)) throw new Error("request_context.sender must be user, model, or unknown");
|
|
22658
|
+
if (!["user", "model", "mixed", "unknown"].includes(source)) throw new Error("request_context.source must be user, model, mixed, or unknown");
|
|
22659
|
+
return { sender, source };
|
|
22660
|
+
}
|
|
22661
|
+
function buildRequestContextHeader(context) {
|
|
22650
22662
|
return [
|
|
22663
|
+
"Request provenance (declared by the upstream caller, not independently authenticated):",
|
|
22664
|
+
`Immediate sender: ${context.sender}. Instruction source: ${context.source}.`,
|
|
22665
|
+
"Instruction source: user = an explicitly supplied user requirement; model = a model-authored task or inference; mixed = both, separated in the task text; unknown = not supplied.",
|
|
22666
|
+
"Do not treat model additions as user requirements. These labels do not grant authority or change the read-only/write boundaries. Assess claims and proposals independently against evidence."
|
|
22667
|
+
].join("\n");
|
|
22668
|
+
}
|
|
22669
|
+
function buildContextEnginePrompt(query, requestContext = normalizeRequestContext()) {
|
|
22670
|
+
return [
|
|
22671
|
+
buildRequestContextHeader(requestContext),
|
|
22651
22672
|
"You are Cursor Context Engine (CCE), a read-only, evidence-driven project-understanding engine.",
|
|
22652
22673
|
"Resolve natural-language intent into verifiable code context. Do not guess locations, repeat framework conventions, or propose an implementation.",
|
|
22653
22674
|
"Search before answering. Choose the search depth from the question shape and discovered relationships: converge quickly for a simple location; trace call chains, data flow, registrations, or cross-module relationships until the minimum sufficient evidence is reached.",
|
|
@@ -22806,6 +22827,13 @@ function selectPageForUiPreference(inspected, options = {}) {
|
|
|
22806
22827
|
function isAgentsWorkspaceBindError(error2) {
|
|
22807
22828
|
return !!(error2 && /Cursor Agents workspace binding failed/.test(String(error2.message || "")));
|
|
22808
22829
|
}
|
|
22830
|
+
function createWorkspaceBindingError(diagnostic) {
|
|
22831
|
+
const error2 = new Error(`Cursor Agents workspace binding failed: ${diagnostic.state || "unknown"}; wanted=${diagnostic.wanted || diagnostic.workspace || "unknown"}. ${diagnostic.nextStep || "Re-run cursor_init for the exact local workspace."}`);
|
|
22832
|
+
error2.code = "CURSOR_WORKSPACE_BINDING_FAILED";
|
|
22833
|
+
error2.workspaceBindingFailure = diagnostic;
|
|
22834
|
+
error2.confirmedNotSent = true;
|
|
22835
|
+
return error2;
|
|
22836
|
+
}
|
|
22809
22837
|
async function findPage(options = {}) {
|
|
22810
22838
|
const list = await httpJson("/json/list");
|
|
22811
22839
|
const pages = list.filter((t) => t.type === "page" && t.webSocketDebuggerUrl);
|
|
@@ -22895,9 +22923,10 @@ var CURSOR_INPUT_SELECTOR = [
|
|
|
22895
22923
|
".aislash-editor-input"
|
|
22896
22924
|
].join(",");
|
|
22897
22925
|
var INPUT_PICKER_BODY = `
|
|
22898
|
-
const
|
|
22926
|
+
const inputCandidates=()=>[...document.querySelectorAll(${JSON.stringify(CURSOR_INPUT_SELECTOR)})]
|
|
22899
22927
|
.filter(e=>e.offsetParent!==null&&!e.disabled&&e.getAttribute('aria-disabled')!=='true'&&e.getAttribute('contenteditable')!=='false')
|
|
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))
|
|
22928
|
+
.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));
|
|
22929
|
+
const pickInput=()=>inputCandidates()[0]||null;`;
|
|
22901
22930
|
var EXPR_VISIBLE = `(function(){${INPUT_PICKER_BODY}return !!pickInput();})()`;
|
|
22902
22931
|
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
22932
|
function classifyChatPanelDiagnostic(snapshot = {}) {
|
|
@@ -23202,75 +23231,216 @@ function createModelSelectionError(message, failureClass, retryable, diagnostic
|
|
|
23202
23231
|
error2.modelSelectionFailure = { failureClass, retryable, ...diagnostic };
|
|
23203
23232
|
return error2;
|
|
23204
23233
|
}
|
|
23205
|
-
var WORKSPACE_SECTION_BODY = `
|
|
23234
|
+
var WORKSPACE_SECTION_BODY = String.raw`
|
|
23235
|
+
const workspaceScalar=value=>value&&typeof value==='object'&&'value' in value?value.value:value;
|
|
23236
|
+
const normalizeWorkspacePath=value=>{
|
|
23237
|
+
let path=String(value||'').replace(/\\/g,'/').replace(/^\/([a-z]:\/)/i,'$1').replace(/\/+$/,'');
|
|
23238
|
+
return /^[a-z]:\//i.test(path)||path.startsWith('//')?path.toLowerCase():path;
|
|
23239
|
+
};
|
|
23240
|
+
const localWorkspacePath=identifier=>{
|
|
23241
|
+
const uri=identifier&&(identifier.uri||identifier.configPath);
|
|
23242
|
+
if(!uri||uri.scheme!=='file'||uri.authority)return '';
|
|
23243
|
+
return normalizeWorkspacePath(uri.fsPath||uri.path);
|
|
23244
|
+
};
|
|
23245
|
+
const inputAgentHeaders=input=>{
|
|
23246
|
+
const headers=new Set();
|
|
23247
|
+
for(let node=input,n=0;node&&n<12;n++,node=node.parentElement){
|
|
23248
|
+
const key=Object.keys(node).find(key=>key.startsWith('__reactFiber$'));
|
|
23249
|
+
for(let f=key&&node[key],i=0;f&&i<36;i++,f=f.return){
|
|
23250
|
+
const selected=f.memoizedProps&&f.memoizedProps.selectedAgent;
|
|
23251
|
+
const header=selected&&selected.reference&&selected.reference.header;
|
|
23252
|
+
if(header)headers.add(header);
|
|
23253
|
+
}
|
|
23254
|
+
}
|
|
23255
|
+
return [...headers];
|
|
23256
|
+
};
|
|
23206
23257
|
const headText=(el)=>String(el&&(el.innerText||el.textContent)||'').trim();
|
|
23207
23258
|
const isNewAgentButton=(node)=>{
|
|
23208
23259
|
if(!node)return false;
|
|
23209
23260
|
const aria=String(node.getAttribute&&node.getAttribute('aria-label')||'').trim();
|
|
23210
|
-
const text=String(node.innerText||'').trim().split('
|
|
23261
|
+
const text=String(node.innerText||'').trim().split('\n')[0].trim();
|
|
23211
23262
|
return /^New Agent$/i.test(aria)||/^New Agent$/i.test(text);
|
|
23212
23263
|
};
|
|
23213
23264
|
const findNewAgent=(root)=>[...(root&&root.querySelectorAll?root.querySelectorAll('button,[role=button]'):[])].find(isNewAgentButton)||null;
|
|
23214
23265
|
const collectWorkspaceSections=()=>{
|
|
23215
23266
|
const sections=[];
|
|
23216
|
-
const
|
|
23217
|
-
const add=(head,node,button)=>{
|
|
23218
|
-
const name=String(head||'').trim();
|
|
23219
|
-
if(!name)return;
|
|
23220
|
-
const key=name.toLowerCase();
|
|
23221
|
-
if(seen.has(key))return;
|
|
23222
|
-
seen.add(key);
|
|
23223
|
-
sections.push({head:name,node,button:button||findNewAgent(node)});
|
|
23224
|
-
};
|
|
23267
|
+
const heads=new Set(document.querySelectorAll('.ui-sidebar-section-head'));
|
|
23225
23268
|
for(const section of document.querySelectorAll('section.glass-sidebar-workspace-section-root')){
|
|
23226
|
-
|
|
23269
|
+
const head=section.querySelector('.ui-sidebar-section-head');
|
|
23270
|
+
if(head)heads.add(head);
|
|
23227
23271
|
}
|
|
23228
|
-
for(const headEl of
|
|
23272
|
+
for(const headEl of heads){
|
|
23229
23273
|
const name=headText(headEl);
|
|
23230
23274
|
if(!name)continue;
|
|
23275
|
+
let metadata=null;
|
|
23276
|
+
const key=Object.keys(headEl).find(key=>key.startsWith('__reactFiber$'));
|
|
23277
|
+
for(let f=key&&headEl[key],i=0;f&&i<36;i++,f=f.return){
|
|
23278
|
+
const p=f.memoizedProps;
|
|
23279
|
+
if(p&&p.section&&Array.isArray(p.section.projects)){metadata=p.section;break;}
|
|
23280
|
+
}
|
|
23231
23281
|
let scope=headEl;
|
|
23232
23282
|
let chosen=null;
|
|
23233
23283
|
for(let i=0;scope&&i<16;i++,scope=scope.parentElement){
|
|
23234
|
-
const nested=[...scope.querySelectorAll('.ui-sidebar-section-head')]
|
|
23235
|
-
const unique=[...new Set(nested.map((h)=>h.toLowerCase()))];
|
|
23284
|
+
const nested=[...scope.querySelectorAll('.ui-sidebar-section-head')];
|
|
23236
23285
|
const button=findNewAgent(scope);
|
|
23237
|
-
if(button&&
|
|
23286
|
+
if(button&&nested.length===1&&nested[0]===headEl){
|
|
23238
23287
|
chosen={node:scope,button};
|
|
23239
23288
|
break;
|
|
23240
23289
|
}
|
|
23241
23290
|
}
|
|
23242
|
-
|
|
23291
|
+
const source=metadata&&(metadata.newAgentTargetSource||metadata);
|
|
23292
|
+
sections.push({head:name,metadata,source,button:chosen&&chosen.button});
|
|
23243
23293
|
}
|
|
23244
23294
|
return sections;
|
|
23245
23295
|
};
|
|
23296
|
+
const inspectWorkspace=projectPath=>{
|
|
23297
|
+
const wanted=normalizeWorkspacePath(projectPath);
|
|
23298
|
+
const sections=collectWorkspaceSections();
|
|
23299
|
+
const available=sections.map(s=>({
|
|
23300
|
+
title:s.head,sectionId:s.metadata&&s.metadata.id||null,
|
|
23301
|
+
projects:(s.source&&s.source.projects||[]).map(p=>({
|
|
23302
|
+
type:p.type,workspaceId:p.workspaceIdentifier&&p.workspaceIdentifier.id||null,
|
|
23303
|
+
path:localWorkspacePath(p.workspaceIdentifier)||null,remoteAuthority:p.remoteAuthority||null,
|
|
23304
|
+
repoUrls:Array.isArray(p.repoUrls)?p.repoUrls:[]
|
|
23305
|
+
}))
|
|
23306
|
+
}));
|
|
23307
|
+
const matches=[];
|
|
23308
|
+
for(const section of sections){
|
|
23309
|
+
for(const project of section.source&§ion.source.projects||[]){
|
|
23310
|
+
if(project.type==='workspace'&&localWorkspacePath(project.workspaceIdentifier)===wanted){
|
|
23311
|
+
matches.push({section,project});
|
|
23312
|
+
}
|
|
23313
|
+
}
|
|
23314
|
+
}
|
|
23315
|
+
const fail=state=>({ok:false,state,wanted,available,
|
|
23316
|
+
nextStep:'In Cursor Agents Window, add or open the exact local folder (or .code-workspace file) '+projectPath+'. Ensure it has one registered local workspace creation target, then run cursor_init with that same path. Repository titles and historical Agents cannot establish the target.'});
|
|
23317
|
+
if(!matches.length)return {diagnostic:fail(sections.some(s=>s.metadata)?'workspace_registration_required':'workspace_identity_unavailable')};
|
|
23318
|
+
if(matches.length!==1)return {diagnostic:fail('workspace_ambiguous')};
|
|
23319
|
+
const {section,project}=matches[0];
|
|
23320
|
+
if(project.remoteAuthority||!project.workspaceIdentifier.id)return {diagnostic:fail('workspace_environment_unverified')};
|
|
23321
|
+
// Cursor's section-level New Agent action chooses among registered targets.
|
|
23322
|
+
// Do not click it when a second workspace could win that choice.
|
|
23323
|
+
if(section.source.projects.length!==1)return {diagnostic:fail('workspace_creation_target_ambiguous')};
|
|
23324
|
+
if(!section.button)return {diagnostic:fail('workspace_new_agent_unavailable')};
|
|
23325
|
+
return {section,diagnostic:{ok:true,state:'workspace_ready',workspace:wanted,
|
|
23326
|
+
workspaceId:project.workspaceIdentifier.id,sectionId:section.metadata.id,
|
|
23327
|
+
environment:'local',identitySource:'registered_workspace_file_uri',repoUrls:Array.isArray(project.repoUrls)?project.repoUrls:[]}};
|
|
23328
|
+
};
|
|
23246
23329
|
`;
|
|
23247
23330
|
function exprCreateAgentForWorkspace(projectPath) {
|
|
23248
|
-
const workspaceLabel = JSON.stringify(basename6(String(projectPath || "")).trim().toLowerCase());
|
|
23249
23331
|
return `(function(){
|
|
23250
23332
|
${WORKSPACE_SECTION_BODY}
|
|
23251
|
-
|
|
23252
|
-
const
|
|
23253
|
-
|
|
23254
|
-
const
|
|
23255
|
-
|
|
23256
|
-
|
|
23257
|
-
|
|
23258
|
-
if(!button)return JSON.stringify({ok:false,state:'repository_new_agent_unavailable',wanted});
|
|
23259
|
-
button.click();
|
|
23260
|
-
return JSON.stringify({ok:true,state:'repository_agent_created',workspace:wanted});
|
|
23333
|
+
${INPUT_PICKER_BODY}
|
|
23334
|
+
const result=inspectWorkspace(${JSON.stringify(String(projectPath || ""))});
|
|
23335
|
+
if(!result.diagnostic.ok)return JSON.stringify(result.diagnostic);
|
|
23336
|
+
const previousAgentIds=[...new Set(inputCandidates().flatMap(inputAgentHeaders)
|
|
23337
|
+
.map(header=>String(workspaceScalar(header.id)||'')).filter(Boolean))];
|
|
23338
|
+
result.section.button.click();
|
|
23339
|
+
return JSON.stringify({...result.diagnostic,state:'workspace_agent_creation_requested',previousAgentIds});
|
|
23261
23340
|
})()`;
|
|
23262
23341
|
}
|
|
23263
23342
|
function exprInspectWorkspaceRepository(projectPath) {
|
|
23264
|
-
const workspaceLabel = JSON.stringify(basename6(String(projectPath || "")).trim().toLowerCase());
|
|
23265
23343
|
return `(function(){
|
|
23266
23344
|
${WORKSPACE_SECTION_BODY}
|
|
23267
|
-
|
|
23268
|
-
|
|
23269
|
-
|
|
23270
|
-
|
|
23271
|
-
|
|
23272
|
-
|
|
23273
|
-
|
|
23345
|
+
return JSON.stringify(inspectWorkspace(${JSON.stringify(String(projectPath || ""))}).diagnostic);
|
|
23346
|
+
})()`;
|
|
23347
|
+
}
|
|
23348
|
+
function exprRegisterAgentsWorkspace(projectPath, workspaceFile = false) {
|
|
23349
|
+
return `(async function(){
|
|
23350
|
+
${WORKSPACE_SECTION_BODY}
|
|
23351
|
+
const path=${JSON.stringify(String(projectPath || ""))};
|
|
23352
|
+
const before=inspectWorkspace(path).diagnostic;
|
|
23353
|
+
if(before.state!=='workspace_registration_required')return JSON.stringify(before);
|
|
23354
|
+
const fail=(state,error)=>JSON.stringify({...before,state,error});
|
|
23355
|
+
const services=new Set();
|
|
23356
|
+
for(const node of document.querySelectorAll('button[aria-label="Open Workspace"],.ui-sidebar-section-head')){
|
|
23357
|
+
const key=Object.keys(node).find(k=>k.startsWith('__reactFiber$'));
|
|
23358
|
+
for(let f=key&&node[key],i=0;f&&i<48;i++,f=f.return){
|
|
23359
|
+
for(let d=f.dependencies&&f.dependencies.firstContext;d;d=d.next){
|
|
23360
|
+
const service=d.memoizedValue&&d.memoizedValue.workspace&&d.memoizedValue.workspace.instantiationService;
|
|
23361
|
+
if(service)services.add(service);
|
|
23362
|
+
}
|
|
23363
|
+
}
|
|
23364
|
+
}
|
|
23365
|
+
const candidates=new Map();
|
|
23366
|
+
for(const service of services){
|
|
23367
|
+
const entries=new Map();
|
|
23368
|
+
for(let s=service,i=0;s&&i<8;i++,s=s._parent){
|
|
23369
|
+
for(const [id,value] of s._services&&s._services._entries||[]){
|
|
23370
|
+
if(!entries.has(String(id)))entries.set(String(id),value);
|
|
23371
|
+
}
|
|
23372
|
+
}
|
|
23373
|
+
const projects=entries.get('glassWorkspacesService');
|
|
23374
|
+
const workspaces=entries.get('workspacesService');
|
|
23375
|
+
const URI=entries.get('environmentService')?.userHome?.constructor;
|
|
23376
|
+
if(typeof projects?.replaceWorkspaceProject==='function'&&typeof URI?.file==='function'
|
|
23377
|
+
&&typeof workspaces?.getSingleFolderWorkspaceIdentifier==='function'
|
|
23378
|
+
&&typeof workspaces?.getWorkspaceIdentifier==='function'
|
|
23379
|
+
&&typeof projects?.refresh==='function'){
|
|
23380
|
+
candidates.set(projects,{projects,workspaces,URI});
|
|
23381
|
+
}
|
|
23382
|
+
}
|
|
23383
|
+
if(candidates.size!==1)return fail('workspace_registration_unavailable','Expected one Cursor workspace registration service');
|
|
23384
|
+
try{
|
|
23385
|
+
const {projects,workspaces,URI}=[...candidates.values()][0];
|
|
23386
|
+
const uri=URI.file(path);
|
|
23387
|
+
const identifier=await workspaces[${JSON.stringify(workspaceFile ? "getWorkspaceIdentifier" : "getSingleFolderWorkspaceIdentifier")}](uri);
|
|
23388
|
+
if(!identifier?.id||localWorkspacePath(identifier)!==normalizeWorkspacePath(path)){
|
|
23389
|
+
return fail('workspace_registration_identity_mismatch','Cursor returned a different workspace identity');
|
|
23390
|
+
}
|
|
23391
|
+
const current=inspectWorkspace(path).diagnostic;
|
|
23392
|
+
if(current.state!=='workspace_registration_required')return JSON.stringify(current);
|
|
23393
|
+
// addProject can skip folders already represented by Agent history.
|
|
23394
|
+
// With no 'replaces', this public service method upserts only this exact ID.
|
|
23395
|
+
await projects.replaceWorkspaceProject({project:{type:'workspace',workspaceIdentifier:identifier}});
|
|
23396
|
+
await projects.refresh();
|
|
23397
|
+
return JSON.stringify({ok:false,state:'workspace_registration_requested',wanted:before.wanted,
|
|
23398
|
+
workspaceId:identifier.id,registrationSource:'cursor_glass_workspaces_service'});
|
|
23399
|
+
}catch(error){return fail('workspace_registration_failed',String(error.message||error));}
|
|
23400
|
+
})()`;
|
|
23401
|
+
}
|
|
23402
|
+
function exprInspectAgentWorkspace(projectPath, options = {}) {
|
|
23403
|
+
return `(function(){
|
|
23404
|
+
${WORKSPACE_SECTION_BODY}
|
|
23405
|
+
${INPUT_PICKER_BODY}
|
|
23406
|
+
const wanted=normalizeWorkspacePath(${JSON.stringify(String(projectPath || ""))});
|
|
23407
|
+
const options=${JSON.stringify(options)};
|
|
23408
|
+
const rawId=id=>String(id||'').replace(/^local:/,'');
|
|
23409
|
+
const excluded=new Set((options.excludedAgentIds||[]).map(rawId));
|
|
23410
|
+
const fail=(state,observed=null)=>({ok:false,state,wanted,observed,
|
|
23411
|
+
nextStep:'Inspect task sendState before retrying. Select the exact local workspace '+${JSON.stringify(String(projectPath || ""))}+' in Cursor and retry cursor_init when no task may still be running. The Agent must expose an existing local environment for that same path; do not substitute a repository alias, cloud target, or another worktree.'});
|
|
23412
|
+
const inputs=inputCandidates();
|
|
23413
|
+
if(inputs.length!==1)return JSON.stringify(fail('agent_workspace_identity_unavailable',{inputCount:inputs.length}));
|
|
23414
|
+
const headers=inputAgentHeaders(inputs[0]);
|
|
23415
|
+
const ids=[...new Set(headers.map(header=>rawId(workspaceScalar(header.id))).filter(Boolean))];
|
|
23416
|
+
if(ids.length!==1||excluded.has(ids[0])||options.agentId&&ids[0]!==rawId(options.agentId)){
|
|
23417
|
+
return JSON.stringify(fail('agent_workspace_identity_unavailable',{
|
|
23418
|
+
inputCount:inputs.length,headerCount:headers.length,candidateAgentIds:ids,
|
|
23419
|
+
identitySource:'writable_input_react_ancestors',expectedAgentId:options.agentId||null,
|
|
23420
|
+
excludedAgentIdsMatched:ids.filter(id=>excluded.has(id))
|
|
23421
|
+
}));
|
|
23422
|
+
}
|
|
23423
|
+
const id=ids[0];
|
|
23424
|
+
if(options.requireInputFocus&&document.activeElement!==inputs[0])return JSON.stringify(fail('agent_input_focus_mismatch'));
|
|
23425
|
+
let observed;
|
|
23426
|
+
for(const header of headers){
|
|
23427
|
+
const target=workspaceScalar(header.targetEnvironment);
|
|
23428
|
+
const environment=workspaceScalar(header.environment);
|
|
23429
|
+
const location=workspaceScalar(header.location);
|
|
23430
|
+
observed={agentId:'local:'+id,targetType:target&&target.type||null,
|
|
23431
|
+
workspaceId:environment&&environment.id||null,path:localWorkspacePath(environment)||null,
|
|
23432
|
+
targetPath:localWorkspacePath(target&&target.environment)||null,locationType:location&&location.type||null};
|
|
23433
|
+
if(!wanted||!environment||!environment.id||!target||target.type!=='existing'
|
|
23434
|
+
||environment.remoteAuthority||target.remoteAuthority||target.environment&&target.environment.remoteAuthority
|
|
23435
|
+
||observed.path!==wanted||observed.targetPath!==wanted||target.environment.id!==environment.id
|
|
23436
|
+
||location&&(location.type!=='local'&&location.type!=='worktree'
|
|
23437
|
+
||localWorkspacePath(location.environment)!==wanted
|
|
23438
|
+
||location.type==='worktree'&&normalizeWorkspacePath(location.worktreePath)!==wanted)){
|
|
23439
|
+
return JSON.stringify(fail('agent_workspace_mismatch',observed));
|
|
23440
|
+
}
|
|
23441
|
+
}
|
|
23442
|
+
return JSON.stringify({ok:true,state:'agent_workspace_verified',workspace:wanted,
|
|
23443
|
+
environment:'local',identitySource:'selected_agent_existing_file_uri',...observed});
|
|
23274
23444
|
})()`;
|
|
23275
23445
|
}
|
|
23276
23446
|
var EXPR_HISTORY_OPEN = `(function(){return !![...document.querySelectorAll('.compact-agent-history-react-menu-label')].find(e=>e.offsetParent!==null);})()`;
|
|
@@ -23749,7 +23919,8 @@ var CursorBridge = class {
|
|
|
23749
23919
|
initialized: !!this.projectPath,
|
|
23750
23920
|
reinitializable: true,
|
|
23751
23921
|
interactionPreference: "agents_v2_when_open_else_legacy",
|
|
23752
|
-
cursorUiPreferencePreserved: true
|
|
23922
|
+
cursorUiPreferencePreserved: true,
|
|
23923
|
+
workspaceBinding: this._lastWorkspaceBinding || null
|
|
23753
23924
|
};
|
|
23754
23925
|
}
|
|
23755
23926
|
sessionRegistryView() {
|
|
@@ -24175,6 +24346,8 @@ var CursorBridge = class {
|
|
|
24175
24346
|
return result;
|
|
24176
24347
|
}
|
|
24177
24348
|
async initializeWorkspace(projectPath) {
|
|
24349
|
+
if (this._healing) await this._healing.catch(() => {
|
|
24350
|
+
});
|
|
24178
24351
|
if (this.busy || this.activeParallel.size > 0 || this.queue.length > 0) {
|
|
24179
24352
|
throw new Error("cursor_init cannot change workspace while Cursor tasks are queued or running");
|
|
24180
24353
|
}
|
|
@@ -24185,8 +24358,9 @@ var CursorBridge = class {
|
|
|
24185
24358
|
this.workspaceSource = "persistent_init";
|
|
24186
24359
|
this.workspaceUpdatedAt = saved.updatedAt;
|
|
24187
24360
|
this._lastLifecycle = null;
|
|
24361
|
+
this._lastWorkspaceBinding = null;
|
|
24188
24362
|
try {
|
|
24189
|
-
await this._ensureCursor();
|
|
24363
|
+
await this._ensureCursor({ registerWorkspace: true });
|
|
24190
24364
|
} catch (error2) {
|
|
24191
24365
|
const lifecycle = this._lastLifecycle;
|
|
24192
24366
|
const recoverableStatuses = /* @__PURE__ */ new Set([
|
|
@@ -24222,8 +24396,9 @@ var CursorBridge = class {
|
|
|
24222
24396
|
lifecycle: this._lastLifecycle
|
|
24223
24397
|
};
|
|
24224
24398
|
}
|
|
24225
|
-
async _findAgentsWorkspace(projectPath) {
|
|
24399
|
+
async _findAgentsWorkspace(projectPath, { registerWorkspace = false } = {}) {
|
|
24226
24400
|
if (!projectPath) return null;
|
|
24401
|
+
this._lastWorkspaceBinding = null;
|
|
24227
24402
|
let page;
|
|
24228
24403
|
try {
|
|
24229
24404
|
page = await findPage({ purpose: "fifo", preferAgentsV2: true });
|
|
@@ -24234,14 +24409,33 @@ var CursorBridge = class {
|
|
|
24234
24409
|
const c = makeClient(page.webSocketDebuggerUrl);
|
|
24235
24410
|
try {
|
|
24236
24411
|
await c.ready;
|
|
24237
|
-
|
|
24412
|
+
let repository = JSON.parse(await evalJS(c, exprInspectWorkspaceRepository(projectPath)) || "{}");
|
|
24413
|
+
if (registerWorkspace && repository.state === "workspace_registration_required") {
|
|
24414
|
+
const registration = await this._withUiLock(async () => {
|
|
24415
|
+
if (this.busy || this.activeParallel.size > 0 || this.queue.length > 0) {
|
|
24416
|
+
return { ok: false, state: "workspace_registration_busy", error: "Cursor tasks became queued or running before registration" };
|
|
24417
|
+
}
|
|
24418
|
+
return JSON.parse(await evalJS(c, exprRegisterAgentsWorkspace(projectPath, statSync3(projectPath).isFile())) || "{}");
|
|
24419
|
+
});
|
|
24420
|
+
repository = registration;
|
|
24421
|
+
if (registration.state === "workspace_registration_requested") {
|
|
24422
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
24423
|
+
repository = JSON.parse(await evalJS(c, exprInspectWorkspaceRepository(projectPath)) || "{}");
|
|
24424
|
+
if (repository.state !== "workspace_registration_required") break;
|
|
24425
|
+
await sleep2(250);
|
|
24426
|
+
}
|
|
24427
|
+
repository = { ...repository, registration };
|
|
24428
|
+
}
|
|
24429
|
+
}
|
|
24430
|
+
this._lastWorkspaceBinding = { ...repository, checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
24238
24431
|
if (!repository.ok) return null;
|
|
24239
24432
|
return {
|
|
24240
24433
|
targetId: page.id,
|
|
24241
24434
|
targetUiFlavor: "agents_v2",
|
|
24242
24435
|
workspace: repository.workspace
|
|
24243
24436
|
};
|
|
24244
|
-
} catch {
|
|
24437
|
+
} catch (error2) {
|
|
24438
|
+
this._lastWorkspaceBinding = { ok: false, state: "workspace_probe_failed", wanted: projectPath, error: String(error2.message || error2) };
|
|
24245
24439
|
return null;
|
|
24246
24440
|
} finally {
|
|
24247
24441
|
c.close();
|
|
@@ -24371,16 +24565,18 @@ var CursorBridge = class {
|
|
|
24371
24565
|
recovery: normalized === "minimal" ? "Switch CCE to normal mode before opening Cursor manually." : null
|
|
24372
24566
|
};
|
|
24373
24567
|
}
|
|
24374
|
-
async contextEngine(query) {
|
|
24568
|
+
async contextEngine(query, options = {}) {
|
|
24375
24569
|
const text = String(query || "").trim();
|
|
24376
24570
|
if (!text) throw new Error("query must not be empty");
|
|
24377
24571
|
if (text.length > 2e4) throw new Error("query exceeds the 20,000-character limit");
|
|
24572
|
+
const requestContext = normalizeRequestContext(options.requestContext);
|
|
24378
24573
|
this._assertWorkspaceConfirmed();
|
|
24379
24574
|
if (this._hasGlobalReservation()) {
|
|
24380
24575
|
throw new Error("A global Cursor reservation has an unconfirmed Stop state; resolve blockingTaskIds from cursor_status first");
|
|
24381
24576
|
}
|
|
24382
24577
|
await this._ensureCursor();
|
|
24383
|
-
const job = this._enqueue("context_engine", buildContextEnginePrompt(text), {
|
|
24578
|
+
const job = this._enqueue("context_engine", buildContextEnginePrompt(text, requestContext), {
|
|
24579
|
+
requestContext,
|
|
24384
24580
|
timeoutMs: QUERY_TIMEOUT,
|
|
24385
24581
|
newChat: true,
|
|
24386
24582
|
execution: "fifo",
|
|
@@ -24406,6 +24602,7 @@ var CursorBridge = class {
|
|
|
24406
24602
|
const text = String(prompt || "").trim();
|
|
24407
24603
|
if (!text) throw new Error("prompt must not be empty");
|
|
24408
24604
|
if (text.length > 1e5) throw new Error("prompt exceeds the 100,000-character limit");
|
|
24605
|
+
const requestContext = normalizeRequestContext(options.requestContext);
|
|
24409
24606
|
this._assertWorkspaceConfirmed();
|
|
24410
24607
|
if (this._hasGlobalReservation()) {
|
|
24411
24608
|
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");
|
|
@@ -24433,7 +24630,9 @@ var CursorBridge = class {
|
|
|
24433
24630
|
throw cursorSessionError("SESSION_EXECUTION_INVALID", "persistent sessions require execution=parallel_agent and never fall back to FIFO");
|
|
24434
24631
|
}
|
|
24435
24632
|
const readOnly = options.readOnly === true;
|
|
24436
|
-
const
|
|
24633
|
+
const requestedTimeoutMs = Number(options.timeoutMs ?? 6e5);
|
|
24634
|
+
if (!Number.isFinite(requestedTimeoutMs)) throw new Error("timeout_ms must be finite");
|
|
24635
|
+
const timeoutMs = Math.max(3e4, Math.min(9e5, requestedTimeoutMs));
|
|
24437
24636
|
const allowedPaths = Array.isArray(options.allowedPaths) ? options.allowedPaths.map((x) => String(x).trim()).filter(Boolean) : [];
|
|
24438
24637
|
if (readOnly && allowedPaths.length > 0) {
|
|
24439
24638
|
throw new Error("read_only=true cannot be combined with allowed_paths because a read-only task cannot declare a write scope");
|
|
@@ -24454,7 +24653,7 @@ var CursorBridge = class {
|
|
|
24454
24653
|
throw cursorSessionError("SESSION_WORKSPACE_REQUIRED", "initialize one workspace before creating or continuing a session");
|
|
24455
24654
|
}
|
|
24456
24655
|
const contract = String(options.completionContract || "").trim();
|
|
24457
|
-
let fullPrompt = text + DO_LANGUAGE_CONTRACT;
|
|
24656
|
+
let fullPrompt = buildRequestContextHeader(requestContext) + "\n\nTask:\n" + text + DO_LANGUAGE_CONTRACT;
|
|
24458
24657
|
if (readOnly) fullPrompt += "\n\nRead-only boundary: Do not modify, create, or delete files, and do not run commands that change workspace state.";
|
|
24459
24658
|
if (allowedPaths.length > 0) {
|
|
24460
24659
|
fullPrompt += "\n\nAllowed modification scope (do not cross this boundary):\n" + allowedPaths.map((x) => "- " + x).join("\n");
|
|
@@ -24493,8 +24692,10 @@ var CursorBridge = class {
|
|
|
24493
24692
|
}
|
|
24494
24693
|
}
|
|
24495
24694
|
const job = this._enqueue("do", fullPrompt, {
|
|
24695
|
+
requestContext,
|
|
24496
24696
|
taskId,
|
|
24497
24697
|
timeoutMs,
|
|
24698
|
+
requestedTimeoutMs,
|
|
24498
24699
|
newChat: sessionMode !== "continue",
|
|
24499
24700
|
execution,
|
|
24500
24701
|
readOnly,
|
|
@@ -24556,7 +24757,9 @@ var CursorBridge = class {
|
|
|
24556
24757
|
id,
|
|
24557
24758
|
kind,
|
|
24558
24759
|
prompt,
|
|
24760
|
+
requestContext: options.requestContext || normalizeRequestContext(),
|
|
24559
24761
|
timeoutMs: options.timeoutMs,
|
|
24762
|
+
requestedTimeoutMs: options.requestedTimeoutMs ?? options.timeoutMs,
|
|
24560
24763
|
newChat: options.newChat,
|
|
24561
24764
|
preferLegacyUi: options.preferLegacyUi === true,
|
|
24562
24765
|
execution: options.execution || "fifo",
|
|
@@ -24635,6 +24838,7 @@ var CursorBridge = class {
|
|
|
24635
24838
|
if (isTerminalTask(job)) return;
|
|
24636
24839
|
const e = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
24637
24840
|
job.error = e.message;
|
|
24841
|
+
if (e.workspaceBindingFailure) job.workspaceBinding = e.workspaceBindingFailure;
|
|
24638
24842
|
if (e.providerError) job.providerError = e.providerError;
|
|
24639
24843
|
if (e.uiDiagnostic) job.uiDiagnostic = e.uiDiagnostic;
|
|
24640
24844
|
if (e.terminalEvidence) job.terminalEvidence = e.terminalEvidence;
|
|
@@ -24721,7 +24925,7 @@ var CursorBridge = class {
|
|
|
24721
24925
|
// 自愈:每次查询前委托 ensureCursorRunning。并发去重。失败静默降级(_run 报清晰错)。
|
|
24722
24926
|
// 统一走 ensureCursorRunning 复用其【单一身份校验来源】(cdpUp + cdpIsCursor)——避免热路径裸 /json/version 检查
|
|
24723
24927
|
// 绕过身份校验、在别的 IDE 占 9223 时驱动错应用(2026-06-08 review #6)。
|
|
24724
|
-
_ensureCursor() {
|
|
24928
|
+
_ensureCursor({ registerWorkspace = false } = {}) {
|
|
24725
24929
|
this._refreshPersistedRuntimeMode();
|
|
24726
24930
|
if (this._healing) return this._healing;
|
|
24727
24931
|
this._healing = (async () => {
|
|
@@ -24734,23 +24938,17 @@ var CursorBridge = class {
|
|
|
24734
24938
|
...this.projectPath ? { projectPath: this.projectPath } : {}
|
|
24735
24939
|
});
|
|
24736
24940
|
this._lastLifecycle = lifecycleFromEnsureResult(rr, this.runtimeMode);
|
|
24737
|
-
if (
|
|
24738
|
-
const agentsWorkspace = await this._findAgentsWorkspace(rr.projectPath);
|
|
24941
|
+
if ((rr.ok || rr.status === "workspace-not-ready") && rr.projectPath) {
|
|
24942
|
+
const agentsWorkspace = await this._findAgentsWorkspace(rr.projectPath, { registerWorkspace });
|
|
24739
24943
|
if (agentsWorkspace) {
|
|
24740
24944
|
this._lastLifecycle = promoteAgentsWorkspaceLifecycle(this._lastLifecycle, agentsWorkspace);
|
|
24741
|
-
}
|
|
24742
|
-
}
|
|
24743
|
-
if (rr.ok && rr.lifecycleMode === "attached" && rr.workspaceAction === "reused-agents-window" && rr.projectPath) {
|
|
24744
|
-
const agentsWorkspace = await this._findAgentsWorkspace(rr.projectPath);
|
|
24745
|
-
if (agentsWorkspace) {
|
|
24746
|
-
this._lastLifecycle = promoteAgentsWorkspaceLifecycle(this._lastLifecycle, agentsWorkspace);
|
|
24747
|
-
} else {
|
|
24945
|
+
} else if (rr.ok && (rr.workspaceAction === "reused-agents-window" || this._lastWorkspaceBinding)) {
|
|
24748
24946
|
this._lastLifecycle = {
|
|
24749
24947
|
...this._lastLifecycle,
|
|
24750
24948
|
status: "workspace-not-ready",
|
|
24751
|
-
message: `Cursor is reachable, but Cursor Bridge could not verify workspace ${rr.projectPath} in the
|
|
24949
|
+
message: `Cursor is reachable, but Cursor Bridge could not verify workspace ${rr.projectPath} in the Agents Window (${this._lastWorkspaceBinding?.state || "workspace_probe_unavailable"}).`,
|
|
24752
24950
|
needsAction: "open_workspace_in_cursor",
|
|
24753
|
-
nextStep: `Open workspace ${rr.projectPath} in Cursor, then retry the same operation.`,
|
|
24951
|
+
nextStep: this._lastWorkspaceBinding?.nextStep || `Open workspace ${rr.projectPath} in Cursor, then retry the same operation.`,
|
|
24754
24952
|
retryable: true
|
|
24755
24953
|
};
|
|
24756
24954
|
}
|
|
@@ -24885,10 +25083,11 @@ var CursorBridge = class {
|
|
|
24885
25083
|
this.activeParallel.delete(job.id);
|
|
24886
25084
|
this._failJob(job, error2);
|
|
24887
25085
|
} else if (error2 && error2.sent) {
|
|
25086
|
+
job.firstWaitError ||= error2.message;
|
|
24888
25087
|
job.error = `Submission state is uncertain; monitoring continues by agentId: ${error2.message}`;
|
|
24889
25088
|
if (job.agentId) {
|
|
24890
25089
|
job.phase = "running";
|
|
24891
|
-
job.reservationScope = job.readOnly ? "agent" : "paths";
|
|
25090
|
+
job.reservationScope = job.effectiveExecution === "fifo" ? "global" : job.readOnly ? "agent" : "paths";
|
|
24892
25091
|
this.activeParallel.set(job.id, job);
|
|
24893
25092
|
this._startParallelMonitor(job);
|
|
24894
25093
|
} else {
|
|
@@ -24927,10 +25126,11 @@ var CursorBridge = class {
|
|
|
24927
25126
|
try {
|
|
24928
25127
|
await this._newChat(c, {
|
|
24929
25128
|
uiFlavor: options.targetUiFlavor,
|
|
24930
|
-
projectPath: options.projectPath || this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath
|
|
25129
|
+
projectPath: options.projectPath || this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath,
|
|
25130
|
+
job: options
|
|
24931
25131
|
});
|
|
24932
25132
|
} catch (error2) {
|
|
24933
|
-
if (attempt === 0 && options.targetUiFlavor === "agents_v2" && isAgentsWorkspaceBindError(error2)) {
|
|
25133
|
+
if (attempt === 0 && options.targetUiFlavor === "agents_v2" && isAgentsWorkspaceBindError(error2) && !error2.workspaceBindingFailure) {
|
|
24934
25134
|
const fallback = await findPage({ purpose: "fifo", preferLegacy: true });
|
|
24935
25135
|
if (fallback && fallback.id !== page.id) {
|
|
24936
25136
|
options.fallbackReason = "agents_window_unbound_use_workbench";
|
|
@@ -24956,11 +25156,13 @@ var CursorBridge = class {
|
|
|
24956
25156
|
baseline = JSON.parse(await evalJS(c, EXPR_SNAP));
|
|
24957
25157
|
} catch {
|
|
24958
25158
|
}
|
|
25159
|
+
options.responseBaseline = baseline;
|
|
24959
25160
|
const providerErrorBaseline = providerErrorSignature(await this._readProviderError(c));
|
|
25161
|
+
await this._verifyAgentsWorkspace(c, options, "before_send");
|
|
24960
25162
|
options.sendState = "dispatching";
|
|
24961
25163
|
try {
|
|
24962
25164
|
await chord(c, 0, "Enter", "Enter", 13);
|
|
24963
|
-
await this._confirmSubmission(c, baseline.messageCount || 0, providerErrorBaseline);
|
|
25165
|
+
await this._confirmSubmission(c, baseline.messageCount || 0, providerErrorBaseline, options);
|
|
24964
25166
|
options.sendState = "sent";
|
|
24965
25167
|
options.sentAt = options.sentAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
24966
25168
|
await this._bindFifoAgentAfterSend(c, options, historyBefore, providerErrorBaseline);
|
|
@@ -25350,11 +25552,19 @@ var CursorBridge = class {
|
|
|
25350
25552
|
async _newChat(c, options = {}) {
|
|
25351
25553
|
if (options.uiFlavor === "agents_v2" && options.projectPath) {
|
|
25352
25554
|
const created = JSON.parse(await evalJS(c, exprCreateAgentForWorkspace(options.projectPath)) || "{}");
|
|
25555
|
+
if (options.job) {
|
|
25556
|
+
options.job.workspaceBindingChecks = { before_create: { ...created, checkedAt: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
25557
|
+
}
|
|
25353
25558
|
if (!created.ok) {
|
|
25354
|
-
|
|
25355
|
-
throw new Error(`Cursor Agents workspace binding failed: ${created.state || "unknown"}; wanted=${created.wanted || basename6(options.projectPath)}${available}`);
|
|
25559
|
+
throw createWorkspaceBindingError(created);
|
|
25356
25560
|
}
|
|
25357
25561
|
await sleep2(1100);
|
|
25562
|
+
const actual = JSON.parse(await evalJS(c, exprInspectAgentWorkspace(options.projectPath, { excludedAgentIds: created.previousAgentIds })) || "{}");
|
|
25563
|
+
if (options.job) options.job.workspaceBindingChecks.after_create = { ...actual, checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
25564
|
+
if (!actual.ok) throw createWorkspaceBindingError(actual);
|
|
25565
|
+
if (actual.workspaceId !== created.workspaceId) {
|
|
25566
|
+
throw createWorkspaceBindingError({ ...actual, ok: false, state: "created_workspace_id_mismatch" });
|
|
25567
|
+
}
|
|
25358
25568
|
return true;
|
|
25359
25569
|
}
|
|
25360
25570
|
return this._clickNewAgent(c, true);
|
|
@@ -25491,6 +25701,7 @@ var CursorBridge = class {
|
|
|
25491
25701
|
}
|
|
25492
25702
|
}
|
|
25493
25703
|
async _fillPrompt(c, text, job, timeoutMs = 2500) {
|
|
25704
|
+
await this._verifyAgentsWorkspace(c, job);
|
|
25494
25705
|
const prepared = await evalJS(c, EXPR_PREPARE_INPUT);
|
|
25495
25706
|
if (prepared !== "READY") return prepared;
|
|
25496
25707
|
try {
|
|
@@ -25522,7 +25733,19 @@ var CursorBridge = class {
|
|
|
25522
25733
|
};
|
|
25523
25734
|
throw error2;
|
|
25524
25735
|
}
|
|
25525
|
-
async
|
|
25736
|
+
async _verifyAgentsWorkspace(c, job, stage = "before_fill") {
|
|
25737
|
+
if (!job || job.targetUiFlavor !== "agents_v2") return;
|
|
25738
|
+
const projectPath = job.projectPath || this.projectPath;
|
|
25739
|
+
const diagnostic = JSON.parse(await evalJS(c, exprInspectAgentWorkspace(projectPath, {
|
|
25740
|
+
agentId: job.agentId || job.provisionalAgentId || job.workspaceBinding?.agentId,
|
|
25741
|
+
requireInputFocus: stage === "before_send"
|
|
25742
|
+
})) || "{}");
|
|
25743
|
+
job.workspaceBinding = { ...diagnostic, checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
25744
|
+
job.workspaceBindingChecks = { ...job.workspaceBindingChecks, [stage]: job.workspaceBinding };
|
|
25745
|
+
if (!diagnostic.ok) throw createWorkspaceBindingError(diagnostic);
|
|
25746
|
+
}
|
|
25747
|
+
async _confirmSubmission(c, baselineCount = 0, providerErrorBaseline = "", job = null) {
|
|
25748
|
+
let lastSnapshot = {};
|
|
25526
25749
|
const accepted = async () => {
|
|
25527
25750
|
await this._throwIfNewProviderError(c, providerErrorBaseline);
|
|
25528
25751
|
let snap = {};
|
|
@@ -25530,6 +25753,7 @@ var CursorBridge = class {
|
|
|
25530
25753
|
snap = JSON.parse(await evalJS(c, EXPR_SNAP));
|
|
25531
25754
|
} catch {
|
|
25532
25755
|
}
|
|
25756
|
+
lastSnapshot = snap;
|
|
25533
25757
|
const inputTextLength = Number(snap.inputTextLength);
|
|
25534
25758
|
return Number(snap.stop || 0) > 0 || Number(snap.messageCount || 0) > Number(baselineCount || 0) || Number.isFinite(inputTextLength) && inputTextLength === 0;
|
|
25535
25759
|
};
|
|
@@ -25537,15 +25761,30 @@ var CursorBridge = class {
|
|
|
25537
25761
|
await sleep2(250);
|
|
25538
25762
|
if (await accepted()) return "enter";
|
|
25539
25763
|
}
|
|
25764
|
+
try {
|
|
25765
|
+
await this._verifyAgentsWorkspace(c, job, "before_send_fallback");
|
|
25766
|
+
} catch (error3) {
|
|
25767
|
+
error3.confirmedNotSent = false;
|
|
25768
|
+
error3.sent = true;
|
|
25769
|
+
throw error3;
|
|
25770
|
+
}
|
|
25540
25771
|
const clicked = await evalJS(c, EXPR_CLICK_SEND);
|
|
25541
|
-
if (clicked === "CLICKED") {
|
|
25772
|
+
if (clicked === "CLICKED" || clicked === "NO_SEND") {
|
|
25542
25773
|
for (let i = 0; i < 20; i++) {
|
|
25543
25774
|
await sleep2(250);
|
|
25544
|
-
if (await accepted()) return "button";
|
|
25775
|
+
if (await accepted()) return clicked === "CLICKED" ? "button" : "enter";
|
|
25545
25776
|
}
|
|
25546
25777
|
}
|
|
25547
|
-
const error2 = new Error(`Cursor
|
|
25548
|
-
error2.confirmedNotSent =
|
|
25778
|
+
const error2 = new Error(`Cursor submission could not be confirmed (submission_uncertain: ${clicked || "unknown"}); Enter was dispatched, so inspect the bound Agent before retrying`);
|
|
25779
|
+
error2.confirmedNotSent = false;
|
|
25780
|
+
error2.sent = true;
|
|
25781
|
+
error2.composerDiagnostic = {
|
|
25782
|
+
inputTextLength: Number.isFinite(Number(lastSnapshot.inputTextLength)) ? Number(lastSnapshot.inputTextLength) : null,
|
|
25783
|
+
sendReady: lastSnapshot.sendReady === true,
|
|
25784
|
+
stop: Number(lastSnapshot.stop || 0),
|
|
25785
|
+
messageCount: Number(lastSnapshot.messageCount || 0),
|
|
25786
|
+
fallbackResult: clicked || "unknown"
|
|
25787
|
+
};
|
|
25549
25788
|
throw error2;
|
|
25550
25789
|
}
|
|
25551
25790
|
async _readProviderError(c) {
|
|
@@ -25587,9 +25826,9 @@ var CursorBridge = class {
|
|
|
25587
25826
|
this._throwIfCancelledBeforeSend(job);
|
|
25588
25827
|
let createdForWorkspace = false;
|
|
25589
25828
|
try {
|
|
25590
|
-
createdForWorkspace = job.targetUiFlavor === "agents_v2" && job.projectPath ? await this._newChat(c, { uiFlavor: job.targetUiFlavor, projectPath: job.projectPath }) : await this._clickNewAgent(c, false);
|
|
25829
|
+
createdForWorkspace = job.targetUiFlavor === "agents_v2" && job.projectPath ? await this._newChat(c, { uiFlavor: job.targetUiFlavor, projectPath: job.projectPath, job }) : await this._clickNewAgent(c, false);
|
|
25591
25830
|
} catch (error2) {
|
|
25592
|
-
if (isAgentsWorkspaceBindError(error2)) {
|
|
25831
|
+
if (isAgentsWorkspaceBindError(error2) && !error2.workspaceBindingFailure) {
|
|
25593
25832
|
return { fallbackReason: "Agents Window could not bind the current repository; downgraded to FIFO/workbench before submission" };
|
|
25594
25833
|
}
|
|
25595
25834
|
throw error2;
|
|
@@ -25622,9 +25861,10 @@ var CursorBridge = class {
|
|
|
25622
25861
|
baseline = JSON.parse(await evalJS(c, EXPR_SNAP));
|
|
25623
25862
|
} catch {
|
|
25624
25863
|
}
|
|
25864
|
+
await this._verifyAgentsWorkspace(c, job, "before_send");
|
|
25625
25865
|
job.sendState = "dispatching";
|
|
25626
25866
|
await chord(c, 0, "Enter", "Enter", 13);
|
|
25627
|
-
await this._confirmSubmission(c, baseline.messageCount || 0, providerErrorBaseline);
|
|
25867
|
+
await this._confirmSubmission(c, baseline.messageCount || 0, providerErrorBaseline, job);
|
|
25628
25868
|
sent = true;
|
|
25629
25869
|
job.sendState = "sent";
|
|
25630
25870
|
job.sentAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -25739,9 +25979,10 @@ var CursorBridge = class {
|
|
|
25739
25979
|
}
|
|
25740
25980
|
await sleep2(350);
|
|
25741
25981
|
this._throwIfCancelledBeforeSend(job);
|
|
25982
|
+
await this._verifyAgentsWorkspace(c, job, "before_send");
|
|
25742
25983
|
job.sendState = "dispatching";
|
|
25743
25984
|
await chord(c, 0, "Enter", "Enter", 13);
|
|
25744
|
-
await this._confirmSubmission(c, job.responseBaseline.messageCount, providerErrorBaseline);
|
|
25985
|
+
await this._confirmSubmission(c, job.responseBaseline.messageCount, providerErrorBaseline, job);
|
|
25745
25986
|
sent = true;
|
|
25746
25987
|
job.sendState = "sent";
|
|
25747
25988
|
job.sentAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -25915,7 +26156,12 @@ var CursorBridge = class {
|
|
|
25915
26156
|
}
|
|
25916
26157
|
if (!this._monitorOwns(job, generation)) return;
|
|
25917
26158
|
const detail = lastCollectionError ? `; last collection error: ${lastCollectionError}` : "";
|
|
25918
|
-
|
|
26159
|
+
const recovered = await this._withJobLock(job, async () => {
|
|
26160
|
+
if (!this._monitorOwns(job, generation)) return true;
|
|
26161
|
+
const result = await this._reapParallelJobLocked(job, { reattach: false, preserveMonitor: true });
|
|
26162
|
+
return isTerminalTask(job) || result.state === "terminal_uncollected";
|
|
26163
|
+
});
|
|
26164
|
+
if (!recovered) throw new Error(`Cursor ${job.effectiveExecution || job.execution || "Agent"} task timed out (${job.timeoutMs}ms)${detail}`);
|
|
25919
26165
|
}
|
|
25920
26166
|
async _requestExactAgentSelection(c, agentId) {
|
|
25921
26167
|
if (!await this._ensureHistoryOpen(c)) return "REACT_ADAPTER_UNAVAILABLE";
|
|
@@ -26011,7 +26257,7 @@ var CursorBridge = class {
|
|
|
26011
26257
|
job.resultUnavailable = true;
|
|
26012
26258
|
job.terminalEvidence = evidence || "stable_completed_history_icon";
|
|
26013
26259
|
job.recoveryState = "terminal_result_uncollected";
|
|
26014
|
-
job.reservationScope = uncertainSubmissionReservationScope(job, error2);
|
|
26260
|
+
job.reservationScope = job.effectiveExecution === "fifo" ? "global" : uncertainSubmissionReservationScope(job, error2);
|
|
26015
26261
|
this._safeSettleSessionJob(job, "terminal_result_uncollected", { needsAttention: true });
|
|
26016
26262
|
}
|
|
26017
26263
|
_abandonJob(job, reason) {
|
|
@@ -26059,7 +26305,7 @@ var CursorBridge = class {
|
|
|
26059
26305
|
job.recoveryState = "agent_missing";
|
|
26060
26306
|
return { stable: false, error: "The bound agentId was not found in Agent History", entry: second || first || null };
|
|
26061
26307
|
}
|
|
26062
|
-
const stable = first.id === second.id && first.showSpinner === second.showSpinner && String(first.icon || "") === String(second.icon || "");
|
|
26308
|
+
const stable = first.id === second.id && second.id === job.agentId && first.showSpinner === second.showSpinner && String(first.icon || "") === String(second.icon || "");
|
|
26063
26309
|
return { stable, entry: second, error: stable ? null : "Agent History state is not yet stable" };
|
|
26064
26310
|
}
|
|
26065
26311
|
async _reapParallelJob(job, options = {}) {
|
|
@@ -26068,15 +26314,15 @@ var CursorBridge = class {
|
|
|
26068
26314
|
}
|
|
26069
26315
|
async _reapParallelJobLocked(job, options = {}) {
|
|
26070
26316
|
if (!job || isTerminalTask(job)) return { changed: false, state: "terminal", task: this._taskView(job, true) };
|
|
26071
|
-
if (
|
|
26317
|
+
if (!this.activeParallel.has(job.id)) {
|
|
26072
26318
|
return { changed: false, state: "not_parallel_reservation", task: this._taskView(job, true) };
|
|
26073
26319
|
}
|
|
26074
26320
|
if (!job.agentId) {
|
|
26075
26321
|
job.lastRecoveryAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26076
26322
|
job.recoveryState = "unbound_agent";
|
|
26077
|
-
return { changed: false, state: "unbound_agent", task: this._taskView(job, true) };
|
|
26323
|
+
return { changed: false, state: "unbound_agent", next: "The FIFO orphan has no agentId that can be safely rebound. Confirm that it stopped in the Cursor UI before explicitly abandoning it.", task: this._taskView(job, true) };
|
|
26078
26324
|
}
|
|
26079
|
-
this._invalidateParallelMonitor(job);
|
|
26325
|
+
if (!options.preserveMonitor) this._invalidateParallelMonitor(job);
|
|
26080
26326
|
const observed = await this._readStableParallelEntry(job);
|
|
26081
26327
|
if (!observed.stable || !observed.entry) {
|
|
26082
26328
|
return { changed: false, state: job.recoveryState || "unstable", error: observed.error, task: this._taskView(job, true) };
|
|
@@ -26324,7 +26570,7 @@ var CursorBridge = class {
|
|
|
26324
26570
|
if (action === "reap") {
|
|
26325
26571
|
const result = await this._reapParallelJobLocked(job, { reattach: true });
|
|
26326
26572
|
if (result.state === "not_parallel_reservation" && job.phase === "orphaned") {
|
|
26327
|
-
result.next = job.agentId ? "
|
|
26573
|
+
result.next = job.agentId ? "The bound task has no recoverable reservation. Inspect its current state before cancelling." : "The FIFO orphan has no agentId that can be safely rebound. Confirm that it stopped in the Cursor UI before explicitly abandoning it.";
|
|
26328
26574
|
}
|
|
26329
26575
|
return { found: true, action, ...result };
|
|
26330
26576
|
}
|
|
@@ -26382,10 +26628,13 @@ var CursorBridge = class {
|
|
|
26382
26628
|
}
|
|
26383
26629
|
const canTargetStop = job.execution === "parallel_agent" || !!job.agentId;
|
|
26384
26630
|
if (canTargetStop && (job.execution === "parallel_agent" || job.phase === "orphaned")) {
|
|
26385
|
-
if (job.
|
|
26631
|
+
if (this.activeParallel.has(job.id)) {
|
|
26386
26632
|
this._invalidateParallelMonitor(job);
|
|
26387
26633
|
const reaped = await this._reapParallelJobLocked(job, { reattach: false });
|
|
26388
|
-
if (isTerminalTask(job)
|
|
26634
|
+
if (isTerminalTask(job) || reaped.state === "terminal_uncollected") {
|
|
26635
|
+
job.cancelRequested = false;
|
|
26636
|
+
return { found: true, action, ...reaped };
|
|
26637
|
+
}
|
|
26389
26638
|
}
|
|
26390
26639
|
job.phase = "cancelling";
|
|
26391
26640
|
job.recoveryState = "stopping";
|
|
@@ -26520,6 +26769,7 @@ var CursorBridge = class {
|
|
|
26520
26769
|
} catch {
|
|
26521
26770
|
s = { stop: 0, messageCount: 0, replyLength: 0, replyHash: 0 };
|
|
26522
26771
|
}
|
|
26772
|
+
if (job) job.lastWaitObservation = { at: (/* @__PURE__ */ new Date()).toISOString(), stop: s.stop, messageCount: s.messageCount, replyLength: s.replyLength, sawStop: sawStop || s.stop > 0 };
|
|
26523
26773
|
if (s.stop > 0) sawStop = true;
|
|
26524
26774
|
if (sawStop && s.stop === 0 && s.replyLength > 0) {
|
|
26525
26775
|
await sleep2(800);
|
|
@@ -26560,6 +26810,10 @@ var CursorBridge = class {
|
|
|
26560
26810
|
if (includeResult) this._markTaskResultCollected(job);
|
|
26561
26811
|
const view = {
|
|
26562
26812
|
taskId: job.id,
|
|
26813
|
+
requestedTimeoutMs: job.requestedTimeoutMs ?? job.timeoutMs,
|
|
26814
|
+
effectiveTimeoutMs: job.timeoutMs,
|
|
26815
|
+
firstWaitError: job.firstWaitError || null,
|
|
26816
|
+
lastWaitObservation: job.lastWaitObservation || null,
|
|
26563
26817
|
kind: job.kind,
|
|
26564
26818
|
status: job.status,
|
|
26565
26819
|
phase: job.phase,
|
|
@@ -26569,6 +26823,9 @@ var CursorBridge = class {
|
|
|
26569
26823
|
allowedPaths: job.allowedPaths,
|
|
26570
26824
|
modelPreference: job.modelPreference,
|
|
26571
26825
|
modelSelection: job.modelSelection,
|
|
26826
|
+
requestContext: job.requestContext || normalizeRequestContext(),
|
|
26827
|
+
workspaceBinding: job.workspaceBinding || null,
|
|
26828
|
+
workspaceBindingChecks: job.workspaceBindingChecks || null,
|
|
26572
26829
|
projectPath: job.projectPath,
|
|
26573
26830
|
sessionMode: job.sessionMode || "isolated",
|
|
26574
26831
|
sessionId: job.sessionId || null,
|
|
@@ -26612,7 +26869,7 @@ var CursorBridge = class {
|
|
|
26612
26869
|
if (job.recoveryState === "terminal_result_uncollected") {
|
|
26613
26870
|
view.attention = "Agent History proves that the underlying task ended, but its final reply has not been collected. Retry with reap, or explicitly abandon only if a missing reply is acceptable.";
|
|
26614
26871
|
} else {
|
|
26615
|
-
view.attention = job.agentId ? job.execution === "parallel_agent" ? "Use cursor_task_control(action=reap) to recheck the original agentId. Use cancel when a stop is needed, and abandon only after manual confirmation while accepting residual write risk." : "FIFO is bound to an agentId. Use
|
|
26872
|
+
view.attention = job.agentId ? job.execution === "parallel_agent" ? "Use cursor_task_control(action=reap) to recheck the original agentId. Use cancel when a stop is needed, and abandon only after manual confirmation while accepting residual write risk." : "FIFO is bound to an agentId. Use reap to collect or resume monitoring; use cancel with the exact expected_agent_id to stop running work." : "This orphan has no agentId that can be safely rebound and globally blocks new delegation. Confirm that it stopped in the Cursor UI, then explicitly release it with cursor_task_control(action=abandon).";
|
|
26616
26873
|
}
|
|
26617
26874
|
}
|
|
26618
26875
|
return view;
|
|
@@ -26696,16 +26953,26 @@ function buildSearchInputSchema() {
|
|
|
26696
26953
|
return {
|
|
26697
26954
|
type: "object",
|
|
26698
26955
|
properties: {
|
|
26699
|
-
query: { type: "string", description: "Describe the behavior, concept, symbol relationship, or ownership boundary to locate. State intent instead of guessing a directory." }
|
|
26956
|
+
query: { type: "string", description: "Describe the behavior, concept, symbol relationship, or ownership boundary to locate. State intent instead of guessing a directory." },
|
|
26957
|
+
request_context: REQUEST_CONTEXT_SCHEMA
|
|
26700
26958
|
},
|
|
26701
26959
|
required: ["query"]
|
|
26702
26960
|
};
|
|
26703
26961
|
}
|
|
26962
|
+
var REQUEST_CONTEXT_SCHEMA = {
|
|
26963
|
+
type: "object",
|
|
26964
|
+
additionalProperties: false,
|
|
26965
|
+
description: "Caller-declared provenance for this request only, not authentication or authorization. Missing values remain unknown; never infer them from the chosen model. Separate user requirements and model additions in mixed task text.",
|
|
26966
|
+
properties: {
|
|
26967
|
+
sender: { type: "string", enum: ["user", "model", "unknown"], description: "Who directly sends this Bridge request. AI callers should declare model, even when acting on a user request." },
|
|
26968
|
+
source: { type: "string", enum: ["user", "model", "mixed", "unknown"], description: "user for explicitly supplied user requirements, model for model-authored tasks/inferences, mixed when the prompt clearly separates both; unknown when not established." }
|
|
26969
|
+
}
|
|
26970
|
+
};
|
|
26704
26971
|
function buildToolDefinitions(bridgeInstance) {
|
|
26705
26972
|
return [
|
|
26706
26973
|
{
|
|
26707
26974
|
name: "cursor_init",
|
|
26708
|
-
description: "Initialize or reinitialize CCE for one local workspace. Give only the project path: Bridge saves it, finds Cursor, ensures the required connection, and opens or verifies the matching project. If Cursor was opened too early without CCE access, initialization safely keeps the binding and tells the user to save, close Cursor once, and repeat the same initialization sentence; it never force-closes Cursor. Cursor login and the user's old/new UI preference are preserved. When both UIs are open, Bridge selects the new Agents Window and creates work in the matching repository instead of Home.",
|
|
26975
|
+
description: "Initialize or reinitialize CCE for one local workspace. Give only the project path: Bridge saves it, finds Cursor, ensures the required connection, and opens or verifies the matching project. In Agents Window, initialization registers a missing local workspace through Cursor's project service and verifies its exact path before reporting ready. If Cursor was opened too early without CCE access, initialization safely keeps the binding and tells the user to save, close Cursor once, and repeat the same initialization sentence; it never force-closes Cursor. Cursor login and the user's old/new UI preference are preserved. When both UIs are open, Bridge selects the new Agents Window and creates work in the matching repository instead of Home.",
|
|
26709
26976
|
inputSchema: {
|
|
26710
26977
|
type: "object",
|
|
26711
26978
|
properties: {
|
|
@@ -26726,10 +26993,11 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
26726
26993
|
type: "object",
|
|
26727
26994
|
properties: {
|
|
26728
26995
|
prompt: { type: "string", description: "The task Cursor should receive. State the goal, boundaries, and what a complete result looks like." },
|
|
26996
|
+
request_context: REQUEST_CONTEXT_SCHEMA,
|
|
26729
26997
|
background: { type: "boolean", default: true, description: "When true, return the task ID immediately. When false, wait for the task to finish or need attention." },
|
|
26730
26998
|
execution: { type: "string", enum: ["fifo", "parallel_agent"], default: "fifo", description: "fifo is the first-in, first-out serial queue and runs one task at a time in a clean chat. parallel_agent creates a separate top-level Cursor Agent." },
|
|
26731
26999
|
read_only: { type: "boolean", default: false, description: "Set true when Cursor must not change the workspace." },
|
|
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
|
|
27000
|
+
timeout_ms: { type: "integer", minimum: 3e4, maximum: 9e5, default: 6e5, description: "One monitoring budget after submission, shared by FIFO and automatic Agent recovery. Default 10 minutes, maximum 15 minutes; task status reports requested and effective budgets. Expiry probes the bound Agent once for completion; it does not cancel or resend work. Explicit reap may start a new budget for a still-running bound Agent, including FIFO." },
|
|
26733
27001
|
allowed_paths: { type: "array", items: { type: "string" }, description: "Workspace-relative paths Cursor may write. Parallel write tasks require non-overlapping paths. This declaration is not a filesystem sandbox." },
|
|
26734
27002
|
completion_contract: { type: "string", description: "Optional acceptance checks or a required final-report format." },
|
|
26735
27003
|
session_mode: { type: "string", enum: [...CURSOR_SESSION_MODES], default: "isolated", description: "isolated preserves the current clean-task behavior. create starts an update-safe persistent session. continue sends one new turn to the exact session_id." },
|
|
@@ -26855,11 +27123,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
26855
27123
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
26856
27124
|
}
|
|
26857
27125
|
if (name === "cursor_context_engine" || name === "cursor_search" || name === "cursor_search_deep") {
|
|
26858
|
-
const result = await bridge.contextEngine(String(args && args.query || ""));
|
|
27126
|
+
const result = await bridge.contextEngine(String(args && args.query || ""), { requestContext: args && args.request_context });
|
|
26859
27127
|
return { content: [{ type: "text", text: String(result) }] };
|
|
26860
27128
|
}
|
|
26861
27129
|
if (name === "cursor_do") {
|
|
26862
27130
|
const result = await bridge.doTask(String(args && args.prompt || ""), {
|
|
27131
|
+
requestContext: args && args.request_context,
|
|
26863
27132
|
background: !args || args.background !== false,
|
|
26864
27133
|
execution: args && args.execution,
|
|
26865
27134
|
readOnly: !!(args && args.read_only),
|
|
@@ -27002,8 +27271,10 @@ export {
|
|
|
27002
27271
|
exprClickBoundComposerStop,
|
|
27003
27272
|
exprClickSelectedAgentStop,
|
|
27004
27273
|
exprCreateAgentForWorkspace,
|
|
27274
|
+
exprInspectAgentWorkspace,
|
|
27005
27275
|
exprInspectWorkspaceRepository,
|
|
27006
27276
|
exprOpenAgent,
|
|
27277
|
+
exprRegisterAgentsWorkspace,
|
|
27007
27278
|
isConfirmedCompletedReply,
|
|
27008
27279
|
isCursorEffortOptionText,
|
|
27009
27280
|
isDurablyRegisteredParallelEntry,
|
|
@@ -27017,6 +27288,7 @@ export {
|
|
|
27017
27288
|
normalizeCursorRuntimeMode,
|
|
27018
27289
|
normalizeDelegationMode,
|
|
27019
27290
|
normalizeModelPickerText,
|
|
27291
|
+
normalizeRequestContext,
|
|
27020
27292
|
pathsOverlap,
|
|
27021
27293
|
promoteAgentsWorkspaceLifecycle,
|
|
27022
27294
|
providerErrorSignature,
|
package/extensions/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ const hostWorkspaceId = hostCwd.replace(/\\/g, "/").toLowerCase();
|
|
|
10
10
|
export default createStdioMcpExtension({
|
|
11
11
|
label: "Cursor Bridge",
|
|
12
12
|
clientName: "pi-cursor-bridge",
|
|
13
|
-
packageVersion: "0.1.
|
|
13
|
+
packageVersion: "0.1.17",
|
|
14
14
|
serverName: "cursor-bridge",
|
|
15
15
|
serverScript: join(packageRoot, "dist", "cursor-bridge.mjs"),
|
|
16
16
|
cwd: hostCwd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cursor-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Use Cursor Context Engine and bounded, explicitly continuous Cursor Agent execution from the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,6 @@
|
|
|
47
47
|
},
|
|
48
48
|
"piPackage": {
|
|
49
49
|
"embeddedProduct": "Cursor Bridge",
|
|
50
|
-
"embeddedProductVersion": "5.
|
|
50
|
+
"embeddedProductVersion": "5.10.1"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -41,7 +41,7 @@ Call `cursor_context_engine` once with the question's real intent. Include a kno
|
|
|
41
41
|
- Describe the relationship or behavior to establish and the evidence needed.
|
|
42
42
|
- Preserve the language of the user's current substantive request unless the user explicitly asks for another language. Do not infer or persist a different language from the operating system when the conversation already provides a clear signal.
|
|
43
43
|
- Do not prescribe Cursor's internal search sequence, harness, Explore usage, or number of files.
|
|
44
|
-
-
|
|
44
|
+
- Keep investigation intent in `query`. Use the optional `request_context` only to declare provenance: AI callers use `sender="model"`; `source` is `user`, `model`, `mixed`, or `unknown`. A retrieval question you inferred is model-authored even when it serves a user goal. Separate user requirements and your additions when using `mixed`; never guess missing provenance.
|
|
45
45
|
- Allow a cold or large workspace enough time to complete its serialized Cursor UI turn.
|
|
46
46
|
|
|
47
47
|
## Verify and continue
|
|
@@ -7,6 +7,8 @@ description: "Delegate bounded light-to-medium implementation, investigation, do
|
|
|
7
7
|
|
|
8
8
|
Use Cursor as an execution partner. Keep direction, scope decisions, risk ownership, result review, and final verification with the primary agent.
|
|
9
9
|
|
|
10
|
+
Declare `request_context` for each call: an AI caller uses `sender="model"`; set `source="user"` for explicitly supplied user requirements, `"model"` for your own task/inference, or `"mixed"` when both appear. In mixed prompts, label user-confirmed requirements separately from your additions. Use `"unknown"` where provenance is unavailable; a user asking you to use Cursor does not make your authored message user-authored. These labels are declarations, not authentication or extra permission, and do not carry over to later session turns.
|
|
11
|
+
|
|
10
12
|
## Respect execution controls
|
|
11
13
|
|
|
12
14
|
- Do not call `cursor_do` when the user explicitly says not to use Cursor or not to delegate. A direct user opt-out always wins.
|
|
@@ -16,6 +16,7 @@ Provide every task independently:
|
|
|
16
16
|
| Field | Requirement |
|
|
17
17
|
|---|---|
|
|
18
18
|
| `prompt` | State one objective, the necessary context, prohibited actions, and the expected report. Use the current user-task language unless the user explicitly requests another language. Do not ask Cursor to repeat the primary agent's scope decision. |
|
|
19
|
+
| `request_context` | Declare `sender` (`user/model/unknown`) and instruction `source` (`user/model/mixed/unknown`) for this turn. AI callers use sender=model. Label user-confirmed requirements and model additions separately for mixed prompts. Omitted fields remain unknown; these declarations never authorize work. |
|
|
19
20
|
| `execution` | Use only `fifo` or `parallel_agent`. Use `fifo` when safe parallelism cannot be demonstrated. |
|
|
20
21
|
| `read_only` | Use `true` for lookup and analysis; use `false` for any file modification. |
|
|
21
22
|
| `allowed_paths` | Required when `read_only=false`. Provide the smallest workspace-relative path set, with no glob, absolute path, or workspace-escaping `..`. Omit it when `read_only=true`. This is not a filesystem sandbox. |
|
|
@@ -40,6 +41,7 @@ Parallel read-only task:
|
|
|
40
41
|
```json
|
|
41
42
|
{
|
|
42
43
|
"prompt": "Read the specified files and return conclusions without modifying any file.",
|
|
44
|
+
"request_context": { "sender": "model", "source": "model" },
|
|
43
45
|
"execution": "parallel_agent",
|
|
44
46
|
"read_only": true,
|
|
45
47
|
"background": true,
|
|
@@ -52,6 +54,7 @@ Bounded write task:
|
|
|
52
54
|
```json
|
|
53
55
|
{
|
|
54
56
|
"prompt": "Implement the specified tool script under the fixed design without expanding scope.",
|
|
57
|
+
"request_context": { "sender": "model", "source": "model" },
|
|
55
58
|
"execution": "parallel_agent",
|
|
56
59
|
"read_only": false,
|
|
57
60
|
"background": true,
|