ocx-cursor 1.0.4 → 1.0.6
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/README.md +1 -1
- package/package.json +1 -1
- package/src/cursor-patch.mjs +255 -3
- package/src/gateway.mjs +48 -0
package/README.md
CHANGED
|
@@ -121,7 +121,7 @@ The installer patches these files:
|
|
|
121
121
|
/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.glass.main.js
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
The patch keeps the model metadata that Cursor would discard. It leaves Cursor's local-agent mode disabled and does not edit either local-agent runtime.
|
|
124
|
+
The patch keeps the model metadata that Cursor would discard and allows managed `opencodex/` models in Cursor's explicit subagent model list, including the gateway credentials and legacy model details used by Cursor's agent protocol. Generated OpenCodex agent definitions are hidden from Cursor so model routing uses Cursor's built-in subagent types instead of creating one custom type per model. Other user-added and BYOK models remain excluded from the model list. It leaves Cursor's local-agent mode disabled and does not edit either local-agent runtime.
|
|
125
125
|
|
|
126
126
|
The login service refreshes the catalog every 15 seconds. It waits for Cursor to quit before writing the state database or reapplying a patch after a Cursor update.
|
|
127
127
|
|
package/package.json
CHANGED
package/src/cursor-patch.mjs
CHANGED
|
@@ -15,6 +15,23 @@ import {
|
|
|
15
15
|
export const cursorPatchMarker = "/*ocx-cursor-model-metadata-v6*/";
|
|
16
16
|
export const legacyCursorPatchMarker = "/*ocx-cursor-model-metadata*/";
|
|
17
17
|
export const cursorByokRoutingPatchMarker = "/*ocx-cursor-byok-model-routing-v1*/";
|
|
18
|
+
export const cursorSubagentModelsPatchMarker = "/*ocx-cursor-subagent-models-v1*/";
|
|
19
|
+
export const cursorSubagentCredentialsPatchMarker = "/*ocx-cursor-subagent-credentials-v2*/";
|
|
20
|
+
export const legacyCursorSubagentCredentialsPatchMarker = "/*ocx-cursor-subagent-credentials-v1*/";
|
|
21
|
+
export const cursorSubagentLegacyDetailsPatchMarker = "/*ocx-cursor-subagent-legacy-details-v2*/";
|
|
22
|
+
export const legacyCursorSubagentLegacyDetailsPatchMarker = "/*ocx-cursor-subagent-legacy-details-v1*/";
|
|
23
|
+
export const cursorSubagentPromptRoutingPatchMarker = "/*ocx-cursor-subagent-prompt-routing-v3*/";
|
|
24
|
+
export const cursorSubagentExecutionRoutingPatchMarker = "/*ocx-cursor-subagent-execution-routing-v5*/";
|
|
25
|
+
export const cursorSubagentRunRoutingPatchMarker = "/*ocx-cursor-subagent-run-routing-v1*/";
|
|
26
|
+
export const cursorSubagentRuntimeCredentialsPatchMarker = "/*ocx-cursor-subagent-runtime-credentials-v1*/";
|
|
27
|
+
export const cursorSubagentModelDisplayPatchMarker = "/*ocx-cursor-subagent-model-display-v2*/";
|
|
28
|
+
export const legacyCursorSubagentModelDisplayPatchMarker = "/*ocx-cursor-subagent-model-display-v1*/";
|
|
29
|
+
export const cursorSubagentTaskArgsDisplayPatchMarker = "/*ocx-cursor-subagent-task-args-display-v5*/";
|
|
30
|
+
export const legacyCursorSubagentTaskArgsDisplayV4PatchMarker = "/*ocx-cursor-subagent-task-args-display-v4*/";
|
|
31
|
+
export const legacyCursorSubagentTaskArgsDisplayV3PatchMarker = "/*ocx-cursor-subagent-task-args-display-v3*/";
|
|
32
|
+
export const legacyCursorSubagentTaskArgsDisplayV2PatchMarker = "/*ocx-cursor-subagent-task-args-display-v2*/";
|
|
33
|
+
export const legacyCursorSubagentTaskArgsDisplayPatchMarker = "/*ocx-cursor-subagent-task-args-display-v1*/";
|
|
34
|
+
export const cursorRoutedSubagentTypesPatchMarker = "/*ocx-cursor-hide-routed-subagent-types-v1*/";
|
|
18
35
|
export const cursorLocalModeDisabled = "localMode:!1";
|
|
19
36
|
export const cursorLocalModeEnabled = "localMode:!0";
|
|
20
37
|
export const cursorLocalRuntimePatchMarker = "/*ocx-cursor-local-model-display-v2*/";
|
|
@@ -29,6 +46,34 @@ export function isCursorModelMetadataBundle(file) {
|
|
|
29
46
|
const catalogNormalization = /(?<normalization>\b(?<catalog>[A-Za-z_$][\w$]*)=\k<catalog>\.map\((?<item>[A-Za-z_$][\w$]*)=>(?<plain>[A-Za-z_$][\w$]*)\(\k<item>\)\)),(?=(?<batch>[A-Za-z_$][\w$]*)\(\(\)=>\{this\._reactiveStorageService\.setApplicationUserPersistentStorage\("availableDefaultModels2",\k<catalog>\))/g;
|
|
30
47
|
const previousCatalogInjection = /\/\*ocx-cursor-model-metadata(?:-v[2-5])?\*\/(?<catalog>[A-Za-z_$][\w$]*)=[\s\S]*?,(?=(?<batch>[A-Za-z_$][\w$]*)\(\(\)=>\{this\._reactiveStorageService\.setApplicationUserPersistentStorage\("availableDefaultModels2",\k<catalog>\))/g;
|
|
31
48
|
const byokModelRouting = /function (?<functionName>[A-Za-z_$][\w$]*)\((?<model>[A-Za-z_$][\w$]*),(?<settings>[A-Za-z_$][\w$]*)\)\{return (?<isClaude>[A-Za-z_$][\w$]*)\(\k<model>\)\?\k<settings>\.useClaudeKey\?"anthropic":void 0:(?<isGemini>[A-Za-z_$][\w$]*)\(\k<model>\)\?\k<settings>\.useGoogleKey\?"google":void 0:\k<settings>\.useOpenAIKey\?"openai":void 0\}/g;
|
|
49
|
+
const explicitSubagentModelFilter = /function (?<functionName>[A-Za-z_$][\w$]*)\((?<model>[A-Za-z_$][\w$]*),(?<isUserAdded>[A-Za-z_$][\w$]*),(?<storage>[A-Za-z_$][\w$]*)\)\{return \k<isUserAdded>===!0\?!0:(?<hasProviderOverride>[A-Za-z_$][\w$]*)\(\k<storage>\)\|\|(?<usesByok>[A-Za-z_$][\w$]*)\(\k<model>,\k<storage>\)\}(?=function [A-Za-z_$][\w$]*\([A-Za-z_$][\w$]*\)\{const\{modelDetails:)/g;
|
|
50
|
+
const explicitSubagentModelSelection = /function (?<functionName>[A-Za-z_$][\w$]*)\((?<input>[A-Za-z_$][\w$]*)\)\{const\{modelDetails:(?<modelDetails>[A-Za-z_$][\w$]*),flagEnabled:(?<flagEnabled>[A-Za-z_$][\w$]*),availableModels:(?<availableModels>[A-Za-z_$][\w$]*),storage:(?<storage>[A-Za-z_$][\w$]*),resolveModelParametersForSubmission:(?<resolveParameters>[A-Za-z_$][\w$]*)\}=\k<input>,(?<maxMode>[A-Za-z_$][\w$]*)=\k<modelDetails>\.maxMode===!0;/g;
|
|
51
|
+
const explicitSubagentByokGuard = /if\((?<hasByok>[A-Za-z_$][\w$]*)\((?<modelDetails>[A-Za-z_$][\w$]*)\)\|\|!\((?<flagEnabled>[A-Za-z_$][\w$]*)\|\|(?<maxMode>[A-Za-z_$][\w$]*)\)\)return\{\};/g;
|
|
52
|
+
const explicitSubagentRequestedModel = /new (?<requestedModelType>[A-Za-z_$][\w$]*)\(\{modelId:(?<model>[A-Za-z_$][\w$]*)\.name,maxMode:(?<maxMode>[A-Za-z_$][\w$]*),parameters:(?<parameters>[A-Za-z_$][\w$]*)\.map\((?<parameter>[A-Za-z_$][\w$]*)=>new (?<parameterType>[A-Za-z_$][\w$]*)\(\{id:\k<parameter>\.id,value:\k<parameter>\.value\}\)\)\}\)(?=\}\)\}\})/g;
|
|
53
|
+
const explicitSubagentSelectionCall = /getSelectedSubagentModelSelections\((?<modelDetails>[A-Za-z_$][\w$]*)\)\{return (?<selector>[A-Za-z_$][\w$]*)\(\{modelDetails:\k<modelDetails>,flagEnabled:(?<featureGate>[\s\S]*?),availableModels:this\.modelConfigService\.getAvailableDefaultModels\(\),storage:this\.reactiveStorageService\.applicationUserPersistentStorage,resolveModelParametersForSubmission:\((?<model>[A-Za-z_$][\w$]*),(?<maxMode>[A-Za-z_$][\w$]*)\)=>this\.modelConfigService\.resolveModelParametersForSubmission\(\k<model>,void 0,\k<maxMode>\)\}\)\}/g;
|
|
54
|
+
const agentModelDetailsType = /(?<type>[A-Za-z_$][\w$]*)=[A-Za-z_$][\w$]*\.makeMessageType\("agent\.v1\.ModelDetails"/g;
|
|
55
|
+
const selectedSubagentLegacyDetails = /selectedSubagentModelDetails:(?<options>[A-Za-z_$][\w$]*)\.selectedSubagentModelsLegacy/g;
|
|
56
|
+
const legacySelectedSubagentDetailsInjection = /selectedSubagentModelDetails:\/\*ocx-cursor-subagent-legacy-details-v1\*\/(?<options>[A-Za-z_$][\w$]*)\.selectedSubagentModelsLegacy\?\.length\?[\s\S]*?credentials:ocxCursorModel\.credentials\}\)\)/g;
|
|
57
|
+
const agentCustomSystemPrompt = /customSystemPrompt:(?<options>[A-Za-z_$][\w$]*)\.customSystemPrompt(?=,harness:)/g;
|
|
58
|
+
const agentRequestAction = /(?<prefix>conversationState:[A-Za-z_$][\w$]*\([A-Za-z_$][\w$]*\),action:)(?<action>[A-Za-z_$][\w$]*)(?=,modelDetails:)/g;
|
|
59
|
+
const legacySubagentPromptRouting = /customSystemPrompt:\(\(\)=>\{\/\*ocx-cursor-subagent-prompt-routing-v1\*\/const ocxCursorModelIds=\((?<options>[A-Za-z_$][\w$]*)\.selectedSubagentModels[\s\S]*?\}\)\(\)(?=,harness:)/g;
|
|
60
|
+
const legacySubagentRequestRouting = /(?<prefix>conversationState:[A-Za-z_$][\w$]*\([A-Za-z_$][\w$]*\),action:)\(\(\)=>\{\/\*ocx-cursor-subagent-prompt-routing-v2\*\/[\s\S]*?return (?<action>[A-Za-z_$][\w$]*)\}\)\(\)(?=,modelDetails:)/g;
|
|
61
|
+
const createOrResumeSubagentStart = /async createOrResumeSubagent\((?<input>[A-Za-z_$][\w$]*)\)\{/g;
|
|
62
|
+
const legacySubagentExecutionRouting = /(?<start>async createOrResumeSubagent\((?<input>[A-Za-z_$][\w$]*)\)\{)\/\*ocx-cursor-subagent-execution-routing-v[1-4]\*\/[\s\S]*?(?=let [A-Za-z_$][\w$]*,[A-Za-z_$][\w$]*=\k<input>\.resumeAgentId;)/g;
|
|
63
|
+
const legacySubagentModelConfigFixup = /(?<fixup>[A-Za-z_$][\w$]*)=ocxCursorRequestedModel\?void 0:typeof this\._modelConfigService\.fixupModelConfigForCurrentFlag=="function"\?this\._modelConfigService\.fixupModelConfigForCurrentFlag\(\{modelName:(?<input>[A-Za-z_$][\w$]*)\.modelId,maxMode:(?<composerConfig>[A-Za-z_$][\w$]*)\.maxMode===!0\}\):void 0/g;
|
|
64
|
+
const subagentModelConfigFixup = /(?<fixup>[A-Za-z_$][\w$]*)=typeof this\._modelConfigService\.fixupModelConfigForCurrentFlag=="function"\?this\._modelConfigService\.fixupModelConfigForCurrentFlag\(\{modelName:(?<input>[A-Za-z_$][\w$]*)\.modelId,maxMode:(?<composerConfig>[A-Za-z_$][\w$]*)\.maxMode===!0\}\):void 0/g;
|
|
65
|
+
const appendSubagentComposer = /(?<declaration>const (?<result>[A-Za-z_$][\w$]*)=await this\._composerDataService\.appendSubComposer\((?<composer>[A-Za-z_$][\w$]*)\))/g;
|
|
66
|
+
const runSubagentWithHandle = /async runSubagentWithHandle\((?<input>[A-Za-z_$][\w$]*),(?<handle>[A-Za-z_$][\w$]*)\)\{return this\._runSubagent\(\k<input>,\k<handle>\)\}/g;
|
|
67
|
+
const runSubagentModelConfigFixup = /(?<fixed>[A-Za-z_$][\w$]*)=\(typeof this\._modelConfigService\.fixupModelConfigForCurrentFlag=="function"\?this\._modelConfigService\.fixupModelConfigForCurrentFlag\(\{modelName:(?<resolved>[A-Za-z_$][\w$]*),maxMode:(?<maxMode>[A-Za-z_$][\w$]*)\}\):void 0\)\?\.selectedModels\?\?\[\],(?<stored>[A-Za-z_$][\w$]*)=(?<config>[A-Za-z_$][\w$]*)\?\.selectedModels\?\?\[\]/g;
|
|
68
|
+
const requestedModelCredentials = /(?<prefix>new [A-Za-z_$][\w$]*\(\{modelId:(?<model>[A-Za-z_$][\w$]*),maxMode:(?<details>[A-Za-z_$][\w$]*)\.maxMode,parameters:[\s\S]*?,credentials:)this\.convertModelDetailsToCredentials\(\k<details>\)(?<suffix>\}\))(?=\}convertModelDetailsToCredentials)/g;
|
|
69
|
+
const subagentModelDisplaySelection = /function (?<functionName>[A-Za-z_$][\w$]*)\((?<input>[A-Za-z_$][\w$]*)\)\{return \k<input>\.resumeTargetComposerId!==void 0\?\k<input>\.subagentModelName:\k<input>\.bestOfNModelName!==void 0\?\k<input>\.bestOfNModelName:\k<input>\.taskParamModelName\?\?\k<input>\.subagentModelName\}/g;
|
|
70
|
+
const legacySubagentModelDisplaySelection = /function (?<functionName>[A-Za-z_$][\w$]*)\((?<input>[A-Za-z_$][\w$]*)\)\{\/\*ocx-cursor-subagent-model-display-v1\*\/return \k<input>\.subagentModelName\?\.startsWith\("opencodex\/"\)\?\k<input>\.subagentModelName:\k<input>\.resumeTargetComposerId!==void 0\?\k<input>\.subagentModelName:\k<input>\.bestOfNModelName!==void 0\?\k<input>\.bestOfNModelName:\k<input>\.taskParamModelName\?\?\k<input>\.subagentModelName\}/g;
|
|
71
|
+
const subagentTaskArgsContext = /(?<args>[A-Za-z_$][\w$]*)=(?<argsExtractor>[A-Za-z_$][\w$]*)\((?<bubble>[A-Za-z_$][\w$]*)\),(?<resume>[A-Za-z_$][\w$]*)=(?<resumeExtractor>[A-Za-z_$][\w$]*)\(\k<args>\),[\s\S]{0,400}?(?<taskModel>[A-Za-z_$][\w$]*)=\k<resume>===void 0\?\k<bubble>\.params\?\.model:void 0/g;
|
|
72
|
+
const legacySubagentTaskArgsDisplayV4 = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v4\*\/\(\(\)=>\{[\s\S]*?\}\)\(\)/g;
|
|
73
|
+
const legacySubagentTaskArgsDisplayV3 = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v3\*\/\(\(\)=>\{[\s\S]*?return ocxCursorTaskModel\}\)\(\)/g;
|
|
74
|
+
const legacySubagentTaskArgsDisplayV2 = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v2\*\/\(\(\)=>\{[\s\S]*?return ocxCursorTaskModel\}\)\(\)/g;
|
|
75
|
+
const legacySubagentTaskArgsDisplay = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v1\*\/(?<taskModel>[A-Za-z_$][\w$]*)\?\?(?<args>[A-Za-z_$][\w$]*)\?\.model/g;
|
|
76
|
+
const rawSubagentLoad = /(?<raw>[A-Za-z_$][\w$]*)=this\.subagentsService\.peekRawSubagents\(\)/g;
|
|
32
77
|
const localModelConstructor = /function (?<functionName>[A-Za-z_$][\w$]*)\(e,t\)\{return new (?<modelType>[A-Za-z_$][\w$]*\.[A-Za-z_$][\w$]*)\(\{modelId:e,displayModelId:e,displayName:null!=t\?t:e,displayNameShort:null!=t\?t:e,aliases:\[\]\}\)\}/g;
|
|
33
78
|
const localRuntimeVisionCapability = /"boolean"==typeof (?<object>[A-Za-z_$][\w$]*)\.supports_vision\?\{supports_vision:\k<object>\.supports_vision\}:\{\}\)/g;
|
|
34
79
|
const localRuntimePickerInput = /toPickerInput\((?<model>[A-Za-z_$][\w$]*)\)\{var [A-Za-z_$][\w$]*;const [A-Za-z_$][\w$]*=this\.modelMetadataById\.get\(\k<model>\.modelId\),(?<capabilities>[A-Za-z_$][\w$]*)=.*?;return\{.*?supportsReasoning:[^,]+,supportsVision:/g;
|
|
@@ -48,8 +93,17 @@ export function clearCursorAppQuarantine(options = {}) {
|
|
|
48
93
|
export function finalizeCursorAppPatch(options = {}) {
|
|
49
94
|
const execute = options.execFileSync || execFileSync;
|
|
50
95
|
const appPath = options.appPath || cursorAppPath;
|
|
96
|
+
const verifyArgs = ["--verify", "--deep", "--strict", appPath];
|
|
97
|
+
let signatureIsValid = false;
|
|
98
|
+
try {
|
|
99
|
+
execute("/usr/bin/codesign", verifyArgs, { stdio: "ignore" });
|
|
100
|
+
signatureIsValid = true;
|
|
101
|
+
} catch {
|
|
102
|
+
// A rewritten bundle invalidates Cursor's signature and needs a fresh ad-hoc signature.
|
|
103
|
+
}
|
|
104
|
+
if (signatureIsValid) return;
|
|
51
105
|
execute("/usr/bin/codesign", ["--force", "--sign", "-", appPath], { stdio: "ignore" });
|
|
52
|
-
execute("/usr/bin/codesign",
|
|
106
|
+
execute("/usr/bin/codesign", verifyArgs, { stdio: "ignore" });
|
|
53
107
|
clearCursorAppQuarantine({ ...options, appPath, execFileSync: execute });
|
|
54
108
|
}
|
|
55
109
|
|
|
@@ -120,12 +174,210 @@ export function patchCursorByokModelRoutingSource(source) {
|
|
|
120
174
|
return { status: "patched", source: patched };
|
|
121
175
|
}
|
|
122
176
|
|
|
177
|
+
export function patchCursorExplicitSubagentModelsSource(source) {
|
|
178
|
+
if (source.includes(cursorSubagentModelsPatchMarker)) return { status: "already-patched", source };
|
|
179
|
+
const patched = replaceSingleMatch(source, explicitSubagentModelFilter, (match) => {
|
|
180
|
+
const { functionName, model, isUserAdded, storage, hasProviderOverride, usesByok } = match.groups;
|
|
181
|
+
return `function ${functionName}(${model},${isUserAdded},${storage}){${cursorSubagentModelsPatchMarker}return ${model}.startsWith("opencodex/")?!1:${isUserAdded}===!0?!0:${hasProviderOverride}(${storage})||${usesByok}(${model},${storage})}`;
|
|
182
|
+
}, "explicit subagent model filter");
|
|
183
|
+
return { status: "patched", source: patched };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function patchCursorSubagentCredentialsSource(source) {
|
|
187
|
+
if (source.includes(cursorSubagentCredentialsPatchMarker)) return { status: "already-patched", source };
|
|
188
|
+
if (source.includes(legacyCursorSubagentCredentialsPatchMarker)) {
|
|
189
|
+
let upgraded = source.replace(legacyCursorSubagentCredentialsPatchMarker, cursorSubagentCredentialsPatchMarker);
|
|
190
|
+
upgraded = replaceSingleMatch(upgraded, explicitSubagentByokGuard, (match) => {
|
|
191
|
+
const { hasByok, modelDetails, flagEnabled, maxMode } = match.groups;
|
|
192
|
+
return `if(${hasByok}(${modelDetails})&&!${modelDetails}.modelName?.startsWith("opencodex/")||!(${flagEnabled}||${maxMode}))return{};`;
|
|
193
|
+
}, "explicit subagent BYOK guard");
|
|
194
|
+
return { status: "patched", source: upgraded };
|
|
195
|
+
}
|
|
196
|
+
let patched = replaceSingleMatch(source, explicitSubagentModelSelection, (match) => {
|
|
197
|
+
const { functionName, input, modelDetails, flagEnabled, availableModels, storage, resolveParameters, maxMode } = match.groups;
|
|
198
|
+
return `function ${functionName}(${input}){${cursorSubagentCredentialsPatchMarker}const{modelDetails:${modelDetails},flagEnabled:${flagEnabled},availableModels:${availableModels},storage:${storage},resolveModelParametersForSubmission:${resolveParameters},resolveCredentialsForSubmission:ocxCursorResolveCredentials}=${input},${maxMode}=${modelDetails}.maxMode===!0;`;
|
|
199
|
+
}, "explicit subagent selection hook");
|
|
200
|
+
patched = replaceSingleMatch(patched, explicitSubagentRequestedModel, (match) => {
|
|
201
|
+
const { requestedModelType, model, maxMode, parameters, parameter, parameterType } = match.groups;
|
|
202
|
+
return `new ${requestedModelType}({modelId:${model}.name,maxMode:${maxMode},credentials:ocxCursorResolveCredentials?.(${model}.name),parameters:${parameters}.map(${parameter}=>new ${parameterType}({id:${parameter}.id,value:${parameter}.value}))})`;
|
|
203
|
+
}, "explicit subagent requested model");
|
|
204
|
+
patched = replaceSingleMatch(patched, explicitSubagentSelectionCall, (match) => {
|
|
205
|
+
const { modelDetails, selector, featureGate, model, maxMode } = match.groups;
|
|
206
|
+
return `getSelectedSubagentModelSelections(${modelDetails}){return ${selector}({modelDetails:${modelDetails},flagEnabled:${featureGate},availableModels:this.modelConfigService.getAvailableDefaultModels(),storage:this.reactiveStorageService.applicationUserPersistentStorage,resolveModelParametersForSubmission:(${model},${maxMode})=>this.modelConfigService.resolveModelParametersForSubmission(${model},void 0,${maxMode}),resolveCredentialsForSubmission:ocxCursorModel=>ocxCursorModel.startsWith("opencodex/")?this.convertModelDetailsToCredentials({apiKey:this.cursorAuthenticationService.openAIKey()??void 0,openaiApiBaseUrl:this.reactiveStorageService.applicationUserPersistentStorage.openAIBaseUrl}):void 0})}`;
|
|
207
|
+
}, "explicit subagent selection caller");
|
|
208
|
+
patched = replaceSingleMatch(patched, explicitSubagentByokGuard, (match) => {
|
|
209
|
+
const { hasByok, modelDetails, flagEnabled, maxMode } = match.groups;
|
|
210
|
+
return `if(${hasByok}(${modelDetails})&&!${modelDetails}.modelName?.startsWith("opencodex/")||!(${flagEnabled}||${maxMode}))return{};`;
|
|
211
|
+
}, "explicit subagent BYOK guard");
|
|
212
|
+
return { status: "patched", source: patched };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function patchCursorSubagentLegacyDetailsSource(source) {
|
|
216
|
+
if (source.includes(cursorSubagentLegacyDetailsPatchMarker)) return { status: "already-patched", source };
|
|
217
|
+
let restored = source;
|
|
218
|
+
if (restored.includes(legacyCursorSubagentLegacyDetailsPatchMarker)) {
|
|
219
|
+
restored = replaceSingleMatch(restored, legacySelectedSubagentDetailsInjection, (match) => {
|
|
220
|
+
return `selectedSubagentModelDetails:${match.groups.options}.selectedSubagentModelsLegacy`;
|
|
221
|
+
}, "legacy explicit subagent model details");
|
|
222
|
+
}
|
|
223
|
+
const modelDetailsMatches = [...restored.matchAll(agentModelDetailsType)];
|
|
224
|
+
if (modelDetailsMatches.length !== 1) {
|
|
225
|
+
throw new Error(`Expected one Cursor agent model details type, found ${modelDetailsMatches.length}`);
|
|
226
|
+
}
|
|
227
|
+
const modelDetailsType = modelDetailsMatches[0].groups.type;
|
|
228
|
+
const patched = replaceSingleMatch(restored, selectedSubagentLegacyDetails, (match) => {
|
|
229
|
+
const { options } = match.groups;
|
|
230
|
+
return `selectedSubagentModelDetails:${cursorSubagentLegacyDetailsPatchMarker}[...(${options}.selectedSubagentModelsLegacy??[]),...(${options}.selectedSubagentModels??[]).filter(ocxCursorModel=>ocxCursorModel.modelId.startsWith("opencodex/")&&!(${options}.selectedSubagentModelsLegacy??[]).some(ocxCursorLegacyModel=>ocxCursorLegacyModel.modelId===ocxCursorModel.modelId)).map(ocxCursorModel=>new ${modelDetailsType}({modelId:ocxCursorModel.modelId,displayModelId:ocxCursorModel.modelId,displayName:ocxCursorModel.modelId,displayNameShort:ocxCursorModel.modelId,aliases:[],maxMode:ocxCursorModel.maxMode,credentials:ocxCursorModel.credentials}))]`;
|
|
231
|
+
}, "legacy explicit subagent model details");
|
|
232
|
+
return { status: "patched", source: patched };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function patchCursorSubagentPromptRoutingSource(source) {
|
|
236
|
+
if (source.includes(cursorSubagentPromptRoutingPatchMarker)) return { status: "already-patched", source };
|
|
237
|
+
let restored = source;
|
|
238
|
+
if (restored.includes("/*ocx-cursor-subagent-prompt-routing-v1*/")) {
|
|
239
|
+
restored = replaceSingleMatch(restored, legacySubagentPromptRouting, (match) => {
|
|
240
|
+
return `customSystemPrompt:${match.groups.options}.customSystemPrompt`;
|
|
241
|
+
}, "legacy subagent prompt routing");
|
|
242
|
+
}
|
|
243
|
+
if (restored.includes("/*ocx-cursor-subagent-prompt-routing-v2*/")) {
|
|
244
|
+
restored = replaceSingleMatch(restored, legacySubagentRequestRouting, (match) => {
|
|
245
|
+
return `${match.groups.prefix}${match.groups.action}`;
|
|
246
|
+
}, "legacy subagent request routing");
|
|
247
|
+
}
|
|
248
|
+
const promptMatches = [...restored.matchAll(agentCustomSystemPrompt)];
|
|
249
|
+
if (promptMatches.length !== 1) {
|
|
250
|
+
throw new Error(`Expected one Cursor custom system prompt field, found ${promptMatches.length}`);
|
|
251
|
+
}
|
|
252
|
+
const options = promptMatches[0].groups.options;
|
|
253
|
+
const patched = replaceSingleMatch(restored, agentRequestAction, (match) => {
|
|
254
|
+
const { prefix, action } = match.groups;
|
|
255
|
+
return `${prefix}(()=>{${cursorSubagentPromptRoutingPatchMarker}const ocxCursorUserMessage=${action}.action.case==="userMessageAction"?${action}.action.value.userMessage:void 0;if(ocxCursorUserMessage){const ocxCursorModelIds=(${options}.selectedSubagentModels??[]).map(ocxCursorModel=>ocxCursorModel.modelId).filter(ocxCursorModelId=>ocxCursorModelId.startsWith("opencodex/")),ocxCursorRequestedModel=ocxCursorModelIds.find(ocxCursorModelId=>ocxCursorUserMessage.text?.includes(ocxCursorModelId)||ocxCursorUserMessage.richText?.includes(ocxCursorModelId));if(ocxCursorRequestedModel){for(const ocxCursorField of["text","richText"])typeof ocxCursorUserMessage[ocxCursorField]==="string"&&(ocxCursorUserMessage[ocxCursorField]=ocxCursorUserMessage[ocxCursorField].split(ocxCursorRequestedModel).join("composer-2.5"));ocxCursorUserMessage.text+=("\\n\\nLocal routing requirement: when calling the Subagent tool, its prompt must begin exactly with <ocx-subagent-model>"+ocxCursorRequestedModel+"</ocx-subagent-model>. Preserve this marker exactly.")}}return ${action}})()`;
|
|
256
|
+
}, "agent request action");
|
|
257
|
+
return { status: "patched", source: patched };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function patchCursorSubagentExecutionRoutingSource(source) {
|
|
261
|
+
if (source.includes(cursorSubagentExecutionRoutingPatchMarker)) return { status: "already-patched", source };
|
|
262
|
+
let restored = source;
|
|
263
|
+
if (restored.includes("/*ocx-cursor-subagent-execution-routing-v4*/")) {
|
|
264
|
+
restored = replaceSingleMatch(restored, legacySubagentModelConfigFixup, (match) => {
|
|
265
|
+
const { fixup, input, composerConfig } = match.groups;
|
|
266
|
+
return `${fixup}=typeof this._modelConfigService.fixupModelConfigForCurrentFlag=="function"?this._modelConfigService.fixupModelConfigForCurrentFlag({modelName:${input}.modelId,maxMode:${composerConfig}.maxMode===!0}):void 0`;
|
|
267
|
+
}, "legacy subagent model config fixup");
|
|
268
|
+
}
|
|
269
|
+
if (restored.includes("/*ocx-cursor-subagent-execution-routing-v1*/") || restored.includes("/*ocx-cursor-subagent-execution-routing-v2*/") || restored.includes("/*ocx-cursor-subagent-execution-routing-v3*/") || restored.includes("/*ocx-cursor-subagent-execution-routing-v4*/")) {
|
|
270
|
+
restored = replaceSingleMatch(restored, legacySubagentExecutionRouting, (match) => match.groups.start, "legacy subagent execution routing");
|
|
271
|
+
}
|
|
272
|
+
let patched = replaceSingleMatch(restored, createOrResumeSubagentStart, (match) => {
|
|
273
|
+
const { input } = match.groups;
|
|
274
|
+
return `${match[0]}${cursorSubagentExecutionRoutingPatchMarker}const ocxCursorModelMarker=typeof ${input}.prompt==="string"?/^\\s*<ocx-subagent-model>(opencodex\\/[A-Za-z0-9._\\/-]+)<\\/ocx-subagent-model>\\s*/.exec(${input}.prompt):null,ocxCursorRequestedModel=ocxCursorModelMarker?.[1];if(ocxCursorRequestedModel&&!${input}.resumeAgentId){const ocxCursorModelAvailable=this._modelConfigService.getAvailableDefaultModels().some(ocxCursorModel=>ocxCursorModel?.name===ocxCursorRequestedModel);if(!ocxCursorModelAvailable)throw new Error("OpenCodex subagent model is unavailable: "+ocxCursorRequestedModel);${input}={...${input},modelId:ocxCursorRequestedModel}}if(ocxCursorModelMarker)${input}={...${input},prompt:${input}.prompt.slice(ocxCursorModelMarker[0].length)};`;
|
|
275
|
+
}, "subagent execution entrypoint");
|
|
276
|
+
patched = replaceSingleMatch(patched, subagentModelConfigFixup, (match) => {
|
|
277
|
+
const { fixup, input, composerConfig } = match.groups;
|
|
278
|
+
return `${fixup}=ocxCursorRequestedModel?void 0:typeof this._modelConfigService.fixupModelConfigForCurrentFlag=="function"?this._modelConfigService.fixupModelConfigForCurrentFlag({modelName:${input}.modelId,maxMode:${composerConfig}.maxMode===!0}):void 0`;
|
|
279
|
+
}, "subagent model config fixup");
|
|
280
|
+
patched = replaceSingleMatch(patched, appendSubagentComposer, (match) => {
|
|
281
|
+
const { declaration, composer } = match.groups;
|
|
282
|
+
return `ocxCursorRequestedModel&&(${composer}.modelConfig={modelName:ocxCursorRequestedModel,maxMode:${composer}.modelConfig?.maxMode===!0,selectedModels:[{modelId:ocxCursorRequestedModel,parameters:[]}]});${declaration}`;
|
|
283
|
+
}, "final subagent model config");
|
|
284
|
+
return { status: "patched", source: patched };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function patchCursorSubagentRunRoutingSource(source) {
|
|
288
|
+
if (source.includes(cursorSubagentRunRoutingPatchMarker)) return { status: "already-patched", source };
|
|
289
|
+
let patched = replaceSingleMatch(source, runSubagentWithHandle, (match) => {
|
|
290
|
+
const { input, handle } = match.groups;
|
|
291
|
+
return `async runSubagentWithHandle(${input},${handle}){${cursorSubagentRunRoutingPatchMarker}const ocxCursorStoredModel=${handle}.data.modelConfig?.modelName;return this._runSubagent(ocxCursorStoredModel?.startsWith("opencodex/")?{...${input},modelId:ocxCursorStoredModel}:${input},${handle})}`;
|
|
292
|
+
}, "subagent run input");
|
|
293
|
+
patched = replaceSingleMatch(patched, runSubagentModelConfigFixup, (match) => {
|
|
294
|
+
const { fixed, resolved, maxMode, stored, config } = match.groups;
|
|
295
|
+
return `${fixed}=${resolved}.startsWith("opencodex/")?${config}?.selectedModels??[]:(typeof this._modelConfigService.fixupModelConfigForCurrentFlag=="function"?this._modelConfigService.fixupModelConfigForCurrentFlag({modelName:${resolved},maxMode:${maxMode}}):void 0)?.selectedModels??[],${stored}=${config}?.selectedModels??[]`;
|
|
296
|
+
}, "subagent run model config");
|
|
297
|
+
return { status: "patched", source: patched };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function patchCursorSubagentRuntimeCredentialsSource(source) {
|
|
301
|
+
if (source.includes(cursorSubagentRuntimeCredentialsPatchMarker)) return { status: "already-patched", source };
|
|
302
|
+
const patched = replaceSingleMatch(source, requestedModelCredentials, (match) => {
|
|
303
|
+
const { prefix, model, details, suffix } = match.groups;
|
|
304
|
+
return `${prefix}${cursorSubagentRuntimeCredentialsPatchMarker}${model}.startsWith("opencodex/")?this.convertModelDetailsToCredentials({apiKey:this.cursorAuthenticationService.openAIKey()??void 0,openaiApiBaseUrl:this.reactiveStorageService.applicationUserPersistentStorage.openAIBaseUrl}):this.convertModelDetailsToCredentials(${details})${suffix}`;
|
|
305
|
+
}, "subagent runtime credentials");
|
|
306
|
+
return { status: "patched", source: patched };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function patchCursorSubagentModelDisplaySource(source) {
|
|
310
|
+
if (source.includes(cursorSubagentModelDisplayPatchMarker)) return { status: "already-patched", source };
|
|
311
|
+
let restored = source;
|
|
312
|
+
if (restored.includes(legacyCursorSubagentModelDisplayPatchMarker)) {
|
|
313
|
+
restored = replaceSingleMatch(restored, legacySubagentModelDisplaySelection, (match) => {
|
|
314
|
+
const { functionName, input } = match.groups;
|
|
315
|
+
return `function ${functionName}(${input}){return ${input}.resumeTargetComposerId!==void 0?${input}.subagentModelName:${input}.bestOfNModelName!==void 0?${input}.bestOfNModelName:${input}.taskParamModelName??${input}.subagentModelName}`;
|
|
316
|
+
}, "legacy subagent model display selector");
|
|
317
|
+
}
|
|
318
|
+
const patched = replaceSingleMatch(restored, subagentModelDisplaySelection, (match) => {
|
|
319
|
+
const { functionName, input } = match.groups;
|
|
320
|
+
return `function ${functionName}(${input}){${cursorSubagentModelDisplayPatchMarker}return ${input}.resumeTargetComposerId!==void 0?${input}.subagentModelName:${input}.bestOfNModelName!==void 0?${input}.bestOfNModelName:${input}.taskParamModelName?.startsWith("opencodex/")?${input}.taskParamModelName:${input}.taskParamModelName?.startsWith("composer-")&&${input}.subagentModelName?.startsWith("opencodex/")?${input}.subagentModelName:${input}.taskParamModelName??${input}.subagentModelName}`;
|
|
321
|
+
}, "subagent model display selector");
|
|
322
|
+
return { status: "patched", source: patched };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function patchCursorSubagentTaskArgsDisplaySource(source) {
|
|
326
|
+
if (source.includes(cursorSubagentTaskArgsDisplayPatchMarker)) return { status: "already-patched", source };
|
|
327
|
+
const contexts = [...source.matchAll(subagentTaskArgsContext)];
|
|
328
|
+
if (contexts.length !== 1) {
|
|
329
|
+
throw new Error(`Expected one Cursor subagent task args context, found ${contexts.length}`);
|
|
330
|
+
}
|
|
331
|
+
const { args, bubble, taskModel } = contexts[0].groups;
|
|
332
|
+
const replacement = `taskParamModelName:${cursorSubagentTaskArgsDisplayPatchMarker}/<ocx-subagent-model>(opencodex\\/[^<]+)<\\/ocx-subagent-model>/.exec(${bubble}.params?.prompt??${args}?.prompt??"")?.[1]??${taskModel}??${args}?.model`;
|
|
333
|
+
if (source.includes(legacyCursorSubagentTaskArgsDisplayV4PatchMarker)) {
|
|
334
|
+
const upgraded = replaceSingleMatch(source, legacySubagentTaskArgsDisplayV4, replacement, "v4 subagent task args model display field");
|
|
335
|
+
return { status: "patched", source: upgraded };
|
|
336
|
+
}
|
|
337
|
+
if (source.includes(legacyCursorSubagentTaskArgsDisplayV3PatchMarker)) {
|
|
338
|
+
const upgraded = replaceSingleMatch(source, legacySubagentTaskArgsDisplayV3, replacement, "v3 subagent task args model display field");
|
|
339
|
+
return { status: "patched", source: upgraded };
|
|
340
|
+
}
|
|
341
|
+
if (source.includes(legacyCursorSubagentTaskArgsDisplayV2PatchMarker)) {
|
|
342
|
+
const upgraded = replaceSingleMatch(source, legacySubagentTaskArgsDisplayV2, replacement, "v2 subagent task args model display field");
|
|
343
|
+
return { status: "patched", source: upgraded };
|
|
344
|
+
}
|
|
345
|
+
if (source.includes(legacyCursorSubagentTaskArgsDisplayPatchMarker)) {
|
|
346
|
+
const upgraded = replaceSingleMatch(source, legacySubagentTaskArgsDisplay, replacement, "legacy subagent task args model display field");
|
|
347
|
+
return { status: "patched", source: upgraded };
|
|
348
|
+
}
|
|
349
|
+
const field = new RegExp(`taskParamModelName:${taskModel}(?=[},])`, "g");
|
|
350
|
+
const patched = replaceSingleMatch(source, field, replacement, "subagent task args model display field");
|
|
351
|
+
return { status: "patched", source: patched };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function patchCursorRoutedSubagentTypesSource(source) {
|
|
355
|
+
if (source.includes(cursorRoutedSubagentTypesPatchMarker)) return { status: "already-patched", source };
|
|
356
|
+
const patched = replaceSingleMatch(source, rawSubagentLoad, (match) => {
|
|
357
|
+
const { raw } = match.groups;
|
|
358
|
+
return `${raw}=this.subagentsService.peekRawSubagents()?.filter(ocxCursorSubagent=>{${cursorRoutedSubagentTypesPatchMarker}const ocxCursorPrompt=ocxCursorSubagent?.prompt;return!ocxCursorPrompt?.includes("generated-by: opencodex")&&!ocxCursorPrompt?.includes("generated-by: ocx-cursor")})`;
|
|
359
|
+
}, "custom subagent loader");
|
|
360
|
+
return { status: "patched", source: patched };
|
|
361
|
+
}
|
|
362
|
+
|
|
123
363
|
export function patchCursorWorkbenchSource(source) {
|
|
124
364
|
const metadata = patchCursorModelMetadataSource(source);
|
|
125
365
|
const routing = patchCursorByokModelRoutingSource(metadata.source);
|
|
366
|
+
const subagents = patchCursorExplicitSubagentModelsSource(routing.source);
|
|
367
|
+
const credentials = patchCursorSubagentCredentialsSource(subagents.source);
|
|
368
|
+
const legacyDetails = patchCursorSubagentLegacyDetailsSource(credentials.source);
|
|
369
|
+
const promptRouting = patchCursorSubagentPromptRoutingSource(legacyDetails.source);
|
|
370
|
+
const executionRouting = patchCursorSubagentExecutionRoutingSource(promptRouting.source);
|
|
371
|
+
const runRouting = patchCursorSubagentRunRoutingSource(executionRouting.source);
|
|
372
|
+
const runtimeCredentials = patchCursorSubagentRuntimeCredentialsSource(runRouting.source);
|
|
373
|
+
const modelDisplay = patchCursorSubagentModelDisplaySource(runtimeCredentials.source);
|
|
374
|
+
const taskArgsDisplay = patchCursorSubagentTaskArgsDisplaySource(modelDisplay.source);
|
|
375
|
+
const subagentTypes = patchCursorRoutedSubagentTypesSource(taskArgsDisplay.source);
|
|
126
376
|
return {
|
|
127
|
-
status: metadata.status === "patched" || routing.status === "patched"
|
|
128
|
-
|
|
377
|
+
status: metadata.status === "patched" || routing.status === "patched" || subagents.status === "patched" || credentials.status === "patched" || legacyDetails.status === "patched" || promptRouting.status === "patched" || executionRouting.status === "patched" || runRouting.status === "patched" || runtimeCredentials.status === "patched" || modelDisplay.status === "patched" || taskArgsDisplay.status === "patched" || subagentTypes.status === "patched"
|
|
378
|
+
? "patched"
|
|
379
|
+
: "already-patched",
|
|
380
|
+
source: subagentTypes.source,
|
|
129
381
|
};
|
|
130
382
|
}
|
|
131
383
|
|
package/src/gateway.mjs
CHANGED
|
@@ -45,6 +45,52 @@ function disableStrictFunctionTools(tools) {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
function enrichSubagentModelTools(tools, catalog) {
|
|
49
|
+
if (!Array.isArray(tools)) return;
|
|
50
|
+
const aliases = catalog.map(({ alias }) => alias).filter((alias) => alias.startsWith(managedPrefix));
|
|
51
|
+
if (aliases.length === 0) return;
|
|
52
|
+
const modelList = aliases.map((alias) => `- ${alias}`).join("\n");
|
|
53
|
+
const modelListPattern = /(If the user explicitly asks for the model of a subagent\/task,[\s\S]*?)(?=\n\nIf the user isn't asking)/;
|
|
54
|
+
for (const tool of tools) {
|
|
55
|
+
if (!isRecord(tool) || tool.type !== "function") continue;
|
|
56
|
+
const definition = isRecord(tool.function) ? tool.function : tool;
|
|
57
|
+
if (typeof definition.name !== "string" || !/^(subagent|task)$/i.test(definition.name)) continue;
|
|
58
|
+
if (typeof definition.description === "string" && modelListPattern.test(definition.description)) {
|
|
59
|
+
definition.description = definition.description.replace(modelListPattern, `$1\n${modelList}`);
|
|
60
|
+
}
|
|
61
|
+
const model = definition.parameters?.properties?.model;
|
|
62
|
+
if (!isRecord(model)) continue;
|
|
63
|
+
if (Array.isArray(model.enum)) {
|
|
64
|
+
model.enum = [...new Set([...model.enum, ...aliases])];
|
|
65
|
+
}
|
|
66
|
+
if (typeof model.description === "string") {
|
|
67
|
+
model.description += ` Available OpenCodex model slugs: ${aliases.join(", ")}.`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function restoreRoutedSubagentModels(messages, catalog) {
|
|
73
|
+
if (!Array.isArray(messages)) return;
|
|
74
|
+
const aliases = new Set(catalog.map(({ alias }) => alias));
|
|
75
|
+
const restore = (text) => {
|
|
76
|
+
if (typeof text !== "string") return text;
|
|
77
|
+
const requested = [...text.matchAll(/<ocx-subagent-model>(opencodex\/[^<]+)<\/ocx-subagent-model>/g)]
|
|
78
|
+
.map((match) => match[1])
|
|
79
|
+
.find((alias) => aliases.has(alias));
|
|
80
|
+
return requested ? text.split("composer-2.5").join(requested) : text;
|
|
81
|
+
};
|
|
82
|
+
for (const message of messages) {
|
|
83
|
+
if (!isRecord(message)) continue;
|
|
84
|
+
if (typeof message.content === "string") {
|
|
85
|
+
message.content = restore(message.content);
|
|
86
|
+
} else if (Array.isArray(message.content)) {
|
|
87
|
+
for (const part of message.content) {
|
|
88
|
+
if (isRecord(part) && typeof part.text === "string") part.text = restore(part.text);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
48
94
|
export function rewriteModelAliasBody(body, catalog) {
|
|
49
95
|
if (!body?.length) return body;
|
|
50
96
|
let payload;
|
|
@@ -54,6 +100,8 @@ export function rewriteModelAliasBody(body, catalog) {
|
|
|
54
100
|
return body;
|
|
55
101
|
}
|
|
56
102
|
if (!isRecord(payload) || typeof payload.model !== "string" || !payload.model.startsWith(managedPrefix)) return body;
|
|
103
|
+
restoreRoutedSubagentModels(payload.messages, catalog);
|
|
104
|
+
enrichSubagentModelTools(payload.tools, catalog);
|
|
57
105
|
|
|
58
106
|
const variant = /^(opencodex\/.+?)\[([^\]]+)\]$/.exec(payload.model);
|
|
59
107
|
let alias = variant?.[1] || payload.model;
|