ocx-cursor 1.0.5 → 1.0.7
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/package.json +1 -1
- package/src/catalog.mjs +1 -1
- package/src/cursor-patch.mjs +181 -24
- package/src/gateway.mjs +170 -2
package/package.json
CHANGED
package/src/catalog.mjs
CHANGED
|
@@ -39,7 +39,7 @@ export function aliasFor(sourceId) {
|
|
|
39
39
|
|
|
40
40
|
export function inferredEfforts(sourceId) {
|
|
41
41
|
const modelId = sourceId.split("/").at(-1);
|
|
42
|
-
if (/^claude-(?:fable-5
|
|
42
|
+
if (/^claude-(?:fable-5(?:-1)?|sonnet-5|opus-(?:5|4-[78]))$/.test(modelId)) return [...allowedEfforts];
|
|
43
43
|
if (/^claude-(?:opus-4-6|sonnet-4-6)$/.test(modelId)) return ["low", "medium", "high", "max"];
|
|
44
44
|
if (/^gpt-5\.6-(?:luna|sol|terra)$/.test(modelId)) return [...allowedEfforts];
|
|
45
45
|
if (/^gpt-5\.[45](?:$|-)/.test(modelId)) return ["low", "medium", "high", "xhigh"];
|
package/src/cursor-patch.mjs
CHANGED
|
@@ -16,12 +16,21 @@ 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
18
|
export const cursorSubagentModelsPatchMarker = "/*ocx-cursor-subagent-models-v1*/";
|
|
19
|
-
export const cursorSubagentCredentialsPatchMarker = "/*ocx-cursor-subagent-credentials-
|
|
20
|
-
export const
|
|
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*/";
|
|
21
23
|
export const cursorSubagentPromptRoutingPatchMarker = "/*ocx-cursor-subagent-prompt-routing-v3*/";
|
|
22
|
-
export const cursorSubagentExecutionRoutingPatchMarker = "/*ocx-cursor-subagent-execution-routing-
|
|
24
|
+
export const cursorSubagentExecutionRoutingPatchMarker = "/*ocx-cursor-subagent-execution-routing-v6*/";
|
|
23
25
|
export const cursorSubagentRunRoutingPatchMarker = "/*ocx-cursor-subagent-run-routing-v1*/";
|
|
24
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*/";
|
|
25
34
|
export const cursorRoutedSubagentTypesPatchMarker = "/*ocx-cursor-hide-routed-subagent-types-v1*/";
|
|
26
35
|
export const cursorLocalModeDisabled = "localMode:!1";
|
|
27
36
|
export const cursorLocalModeEnabled = "localMode:!0";
|
|
@@ -39,22 +48,34 @@ const previousCatalogInjection = /\/\*ocx-cursor-model-metadata(?:-v[2-5])?\*\/(
|
|
|
39
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;
|
|
40
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;
|
|
41
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;
|
|
42
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;
|
|
43
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;
|
|
44
54
|
const agentModelDetailsType = /(?<type>[A-Za-z_$][\w$]*)=[A-Za-z_$][\w$]*\.makeMessageType\("agent\.v1\.ModelDetails"/g;
|
|
45
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;
|
|
46
57
|
const agentCustomSystemPrompt = /customSystemPrompt:(?<options>[A-Za-z_$][\w$]*)\.customSystemPrompt(?=,harness:)/g;
|
|
47
58
|
const agentRequestAction = /(?<prefix>conversationState:[A-Za-z_$][\w$]*\([A-Za-z_$][\w$]*\),action:)(?<action>[A-Za-z_$][\w$]*)(?=,modelDetails:)/g;
|
|
48
59
|
const legacySubagentPromptRouting = /customSystemPrompt:\(\(\)=>\{\/\*ocx-cursor-subagent-prompt-routing-v1\*\/const ocxCursorModelIds=\((?<options>[A-Za-z_$][\w$]*)\.selectedSubagentModels[\s\S]*?\}\)\(\)(?=,harness:)/g;
|
|
49
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;
|
|
50
61
|
const createOrResumeSubagentStart = /async createOrResumeSubagent\((?<input>[A-Za-z_$][\w$]*)\)\{/g;
|
|
51
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 currentSubagentExecutionRouting = /(?<start>async createOrResumeSubagent\((?<input>[A-Za-z_$][\w$]*)\)\{)\/\*ocx-cursor-subagent-execution-routing-v5\*\/[\s\S]*?(?=const [A-Za-z_$][\w$]*=\{mode:)/g;
|
|
64
|
+
const subagentSpawnMethod = /(?<start>async _createOrResumeSubagentSpawn\((?<input>[A-Za-z_$][\w$]*),[A-Za-z_$][\w$]*\)\{)(?<body>[\s\S]*?)(?=async runSubagentWithHandle\()/g;
|
|
52
65
|
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;
|
|
53
66
|
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;
|
|
54
67
|
const appendSubagentComposer = /(?<declaration>const (?<result>[A-Za-z_$][\w$]*)=await this\._composerDataService\.appendSubComposer\((?<composer>[A-Za-z_$][\w$]*)\))/g;
|
|
55
68
|
const runSubagentWithHandle = /async runSubagentWithHandle\((?<input>[A-Za-z_$][\w$]*),(?<handle>[A-Za-z_$][\w$]*)\)\{return this\._runSubagent\(\k<input>,\k<handle>\)\}/g;
|
|
56
69
|
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;
|
|
57
70
|
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;
|
|
71
|
+
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;
|
|
72
|
+
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;
|
|
73
|
+
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;
|
|
74
|
+
const directSubagentTaskArgsContext = /subagentTypeName:(?<bubble>[A-Za-z_$][\w$]*)\.params\?\.name[\s\S]{0,2500}?taskParamModelName:(?<taskModel>[A-Za-z_$][\w$]*)\}\);return/g;
|
|
75
|
+
const legacySubagentTaskArgsDisplayV4 = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v4\*\/\(\(\)=>\{[\s\S]*?\}\)\(\)/g;
|
|
76
|
+
const legacySubagentTaskArgsDisplayV3 = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v3\*\/\(\(\)=>\{[\s\S]*?return ocxCursorTaskModel\}\)\(\)/g;
|
|
77
|
+
const legacySubagentTaskArgsDisplayV2 = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v2\*\/\(\(\)=>\{[\s\S]*?return ocxCursorTaskModel\}\)\(\)/g;
|
|
78
|
+
const legacySubagentTaskArgsDisplay = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v1\*\/(?<taskModel>[A-Za-z_$][\w$]*)\?\?(?<args>[A-Za-z_$][\w$]*)\?\.model/g;
|
|
58
79
|
const rawSubagentLoad = /(?<raw>[A-Za-z_$][\w$]*)=this\.subagentsService\.peekRawSubagents\(\)/g;
|
|
59
80
|
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;
|
|
60
81
|
const localRuntimeVisionCapability = /"boolean"==typeof (?<object>[A-Za-z_$][\w$]*)\.supports_vision\?\{supports_vision:\k<object>\.supports_vision\}:\{\}\)/g;
|
|
@@ -67,16 +88,45 @@ const directoryPatchQueues = new Map();
|
|
|
67
88
|
|
|
68
89
|
export function clearCursorAppQuarantine(options = {}) {
|
|
69
90
|
const execute = options.execFileSync || execFileSync;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
const appPath = options.appPath || cursorAppPath;
|
|
92
|
+
try {
|
|
93
|
+
execute("/usr/bin/xattr", ["-dr", "com.apple.quarantine", appPath], {
|
|
94
|
+
stdio: "ignore",
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
// Read-only nested app directories can make recursive xattr traversal fail
|
|
98
|
+
// even after the bundle root has no quarantine attribute. List root
|
|
99
|
+
// attributes so probe failures and a real remaining quarantine are never
|
|
100
|
+
// hidden.
|
|
101
|
+
try {
|
|
102
|
+
const rootAttributes = execute("/usr/bin/xattr", [appPath], {
|
|
103
|
+
encoding: "utf8",
|
|
104
|
+
});
|
|
105
|
+
const hasRootQuarantine = String(rootAttributes)
|
|
106
|
+
.split(/\r?\n/)
|
|
107
|
+
.some((attribute) => attribute.trim() === "com.apple.quarantine");
|
|
108
|
+
if (!hasRootQuarantine) return;
|
|
109
|
+
} catch {
|
|
110
|
+
// Fail closed when the root probe itself cannot be completed.
|
|
111
|
+
}
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
73
114
|
}
|
|
74
115
|
|
|
75
116
|
export function finalizeCursorAppPatch(options = {}) {
|
|
76
117
|
const execute = options.execFileSync || execFileSync;
|
|
77
118
|
const appPath = options.appPath || cursorAppPath;
|
|
119
|
+
const verifyArgs = ["--verify", "--deep", "--strict", appPath];
|
|
120
|
+
let signatureIsValid = false;
|
|
121
|
+
try {
|
|
122
|
+
execute("/usr/bin/codesign", verifyArgs, { stdio: "ignore" });
|
|
123
|
+
signatureIsValid = true;
|
|
124
|
+
} catch {
|
|
125
|
+
// A rewritten bundle invalidates Cursor's signature and needs a fresh ad-hoc signature.
|
|
126
|
+
}
|
|
127
|
+
if (signatureIsValid) return;
|
|
78
128
|
execute("/usr/bin/codesign", ["--force", "--sign", "-", appPath], { stdio: "ignore" });
|
|
79
|
-
execute("/usr/bin/codesign",
|
|
129
|
+
execute("/usr/bin/codesign", verifyArgs, { stdio: "ignore" });
|
|
80
130
|
clearCursorAppQuarantine({ ...options, appPath, execFileSync: execute });
|
|
81
131
|
}
|
|
82
132
|
|
|
@@ -158,6 +208,14 @@ export function patchCursorExplicitSubagentModelsSource(source) {
|
|
|
158
208
|
|
|
159
209
|
export function patchCursorSubagentCredentialsSource(source) {
|
|
160
210
|
if (source.includes(cursorSubagentCredentialsPatchMarker)) return { status: "already-patched", source };
|
|
211
|
+
if (source.includes(legacyCursorSubagentCredentialsPatchMarker)) {
|
|
212
|
+
let upgraded = source.replace(legacyCursorSubagentCredentialsPatchMarker, cursorSubagentCredentialsPatchMarker);
|
|
213
|
+
upgraded = replaceSingleMatch(upgraded, explicitSubagentByokGuard, (match) => {
|
|
214
|
+
const { hasByok, modelDetails, flagEnabled, maxMode } = match.groups;
|
|
215
|
+
return `if(${hasByok}(${modelDetails})&&!${modelDetails}.modelName?.startsWith("opencodex/")||!(${flagEnabled}||${maxMode}))return{};`;
|
|
216
|
+
}, "explicit subagent BYOK guard");
|
|
217
|
+
return { status: "patched", source: upgraded };
|
|
218
|
+
}
|
|
161
219
|
let patched = replaceSingleMatch(source, explicitSubagentModelSelection, (match) => {
|
|
162
220
|
const { functionName, input, modelDetails, flagEnabled, availableModels, storage, resolveParameters, maxMode } = match.groups;
|
|
163
221
|
return `function ${functionName}(${input}){${cursorSubagentCredentialsPatchMarker}const{modelDetails:${modelDetails},flagEnabled:${flagEnabled},availableModels:${availableModels},storage:${storage},resolveModelParametersForSubmission:${resolveParameters},resolveCredentialsForSubmission:ocxCursorResolveCredentials}=${input},${maxMode}=${modelDetails}.maxMode===!0;`;
|
|
@@ -170,19 +228,29 @@ export function patchCursorSubagentCredentialsSource(source) {
|
|
|
170
228
|
const { modelDetails, selector, featureGate, model, maxMode } = match.groups;
|
|
171
229
|
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})}`;
|
|
172
230
|
}, "explicit subagent selection caller");
|
|
231
|
+
patched = replaceSingleMatch(patched, explicitSubagentByokGuard, (match) => {
|
|
232
|
+
const { hasByok, modelDetails, flagEnabled, maxMode } = match.groups;
|
|
233
|
+
return `if(${hasByok}(${modelDetails})&&!${modelDetails}.modelName?.startsWith("opencodex/")||!(${flagEnabled}||${maxMode}))return{};`;
|
|
234
|
+
}, "explicit subagent BYOK guard");
|
|
173
235
|
return { status: "patched", source: patched };
|
|
174
236
|
}
|
|
175
237
|
|
|
176
238
|
export function patchCursorSubagentLegacyDetailsSource(source) {
|
|
177
239
|
if (source.includes(cursorSubagentLegacyDetailsPatchMarker)) return { status: "already-patched", source };
|
|
178
|
-
|
|
240
|
+
let restored = source;
|
|
241
|
+
if (restored.includes(legacyCursorSubagentLegacyDetailsPatchMarker)) {
|
|
242
|
+
restored = replaceSingleMatch(restored, legacySelectedSubagentDetailsInjection, (match) => {
|
|
243
|
+
return `selectedSubagentModelDetails:${match.groups.options}.selectedSubagentModelsLegacy`;
|
|
244
|
+
}, "legacy explicit subagent model details");
|
|
245
|
+
}
|
|
246
|
+
const modelDetailsMatches = [...restored.matchAll(agentModelDetailsType)];
|
|
179
247
|
if (modelDetailsMatches.length !== 1) {
|
|
180
248
|
throw new Error(`Expected one Cursor agent model details type, found ${modelDetailsMatches.length}`);
|
|
181
249
|
}
|
|
182
250
|
const modelDetailsType = modelDetailsMatches[0].groups.type;
|
|
183
|
-
const patched = replaceSingleMatch(
|
|
251
|
+
const patched = replaceSingleMatch(restored, selectedSubagentLegacyDetails, (match) => {
|
|
184
252
|
const { options } = match.groups;
|
|
185
|
-
return `selectedSubagentModelDetails:${cursorSubagentLegacyDetailsPatchMarker}${options}.selectedSubagentModelsLegacy
|
|
253
|
+
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}))]`;
|
|
186
254
|
}, "legacy explicit subagent model details");
|
|
187
255
|
return { status: "patched", source: patched };
|
|
188
256
|
}
|
|
@@ -215,11 +283,34 @@ export function patchCursorSubagentPromptRoutingSource(source) {
|
|
|
215
283
|
export function patchCursorSubagentExecutionRoutingSource(source) {
|
|
216
284
|
if (source.includes(cursorSubagentExecutionRoutingPatchMarker)) return { status: "already-patched", source };
|
|
217
285
|
let restored = source;
|
|
286
|
+
let restoredLegacySpawn = false;
|
|
287
|
+
if (restored.includes("/*ocx-cursor-subagent-execution-routing-v5*/")) {
|
|
288
|
+
restored = replaceSingleMatch(restored, currentSubagentExecutionRouting, (match) => match.groups.start, "current subagent execution routing");
|
|
289
|
+
const spawnMatches = [...restored.matchAll(subagentSpawnMethod)];
|
|
290
|
+
if (spawnMatches.length === 1) {
|
|
291
|
+
const { start, input, body } = spawnMatches[0].groups;
|
|
292
|
+
const routedBody = body
|
|
293
|
+
.replaceAll("ocxCursorRequestedModel?void 0:", `${input}.modelId?.startsWith("opencodex/")?void 0:`)
|
|
294
|
+
.replaceAll("ocxCursorRequestedModel&&", `${input}.modelId?.startsWith("opencodex/")&&`)
|
|
295
|
+
.replaceAll("modelName:ocxCursorRequestedModel", `modelName:${input}.modelId`)
|
|
296
|
+
.replaceAll("modelId:ocxCursorRequestedModel", `modelId:${input}.modelId`);
|
|
297
|
+
if (routedBody !== body) {
|
|
298
|
+
restored = replaceSingleMatch(restored, subagentSpawnMethod, `${start}${routedBody}`, "current subagent spawn routing");
|
|
299
|
+
restoredLegacySpawn = true;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
218
303
|
if (restored.includes("/*ocx-cursor-subagent-execution-routing-v4*/")) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
304
|
+
const legacyFixups = [...restored.matchAll(legacySubagentModelConfigFixup)];
|
|
305
|
+
if (legacyFixups.length > 1) {
|
|
306
|
+
throw new Error(`Expected at most one Cursor legacy subagent model config fixup, found ${legacyFixups.length}`);
|
|
307
|
+
}
|
|
308
|
+
if (legacyFixups.length === 1) {
|
|
309
|
+
restored = replaceSingleMatch(restored, legacySubagentModelConfigFixup, (match) => {
|
|
310
|
+
const { fixup, input, composerConfig } = match.groups;
|
|
311
|
+
return `${fixup}=typeof this._modelConfigService.fixupModelConfigForCurrentFlag=="function"?this._modelConfigService.fixupModelConfigForCurrentFlag({modelName:${input}.modelId,maxMode:${composerConfig}.maxMode===!0}):void 0`;
|
|
312
|
+
}, "legacy subagent model config fixup");
|
|
313
|
+
}
|
|
223
314
|
}
|
|
224
315
|
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*/")) {
|
|
225
316
|
restored = replaceSingleMatch(restored, legacySubagentExecutionRouting, (match) => match.groups.start, "legacy subagent execution routing");
|
|
@@ -228,14 +319,28 @@ export function patchCursorSubagentExecutionRoutingSource(source) {
|
|
|
228
319
|
const { input } = match.groups;
|
|
229
320
|
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)};`;
|
|
230
321
|
}, "subagent execution entrypoint");
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
322
|
+
const configFixups = [...patched.matchAll(subagentModelConfigFixup)];
|
|
323
|
+
if (configFixups.length > 1) throw new Error(`Expected at most one Cursor subagent model config fixup, found ${configFixups.length}`);
|
|
324
|
+
const allowMissingConfigFixup = restoredLegacySpawn || source.includes("/*ocx-cursor-subagent-execution-routing-v4*/");
|
|
325
|
+
if (configFixups.length === 0 && !allowMissingConfigFixup) {
|
|
326
|
+
throw new Error("Expected one Cursor subagent model config fixup, found 0");
|
|
327
|
+
}
|
|
328
|
+
if (configFixups.length === 1) {
|
|
329
|
+
patched = replaceSingleMatch(patched, subagentModelConfigFixup, (match) => {
|
|
330
|
+
const { fixup, input, composerConfig } = match.groups;
|
|
331
|
+
// The execution entrypoint and the composer-spawn helper are separate
|
|
332
|
+
// methods in current Cursor builds. The requested model marker is scoped
|
|
333
|
+
// to createOrResumeSubagent, so derive routing from the already-normalized
|
|
334
|
+
// request model here instead of referencing that out-of-scope variable.
|
|
335
|
+
return `${fixup}=${input}.modelId?.startsWith("opencodex/")?void 0:typeof this._modelConfigService.fixupModelConfigForCurrentFlag=="function"?this._modelConfigService.fixupModelConfigForCurrentFlag({modelName:${input}.modelId,maxMode:${composerConfig}.maxMode===!0}):void 0`;
|
|
336
|
+
}, "subagent model config fixup");
|
|
337
|
+
}
|
|
338
|
+
if (!restoredLegacySpawn) {
|
|
339
|
+
patched = replaceSingleMatch(patched, appendSubagentComposer, (match) => {
|
|
340
|
+
const { declaration, composer } = match.groups;
|
|
341
|
+
return `(${composer}.modelConfig?.modelName?.startsWith("opencodex/"))&&(${composer}.modelConfig={modelName:${composer}.modelConfig.modelName,maxMode:${composer}.modelConfig?.maxMode===!0,selectedModels:[{modelId:${composer}.modelConfig.modelName,parameters:[]}]});${declaration}`;
|
|
342
|
+
}, "final subagent model config");
|
|
343
|
+
}
|
|
239
344
|
return { status: "patched", source: patched };
|
|
240
345
|
}
|
|
241
346
|
|
|
@@ -261,6 +366,56 @@ export function patchCursorSubagentRuntimeCredentialsSource(source) {
|
|
|
261
366
|
return { status: "patched", source: patched };
|
|
262
367
|
}
|
|
263
368
|
|
|
369
|
+
export function patchCursorSubagentModelDisplaySource(source) {
|
|
370
|
+
if (source.includes(cursorSubagentModelDisplayPatchMarker)) return { status: "already-patched", source };
|
|
371
|
+
let restored = source;
|
|
372
|
+
if (restored.includes(legacyCursorSubagentModelDisplayPatchMarker)) {
|
|
373
|
+
restored = replaceSingleMatch(restored, legacySubagentModelDisplaySelection, (match) => {
|
|
374
|
+
const { functionName, input } = match.groups;
|
|
375
|
+
return `function ${functionName}(${input}){return ${input}.resumeTargetComposerId!==void 0?${input}.subagentModelName:${input}.bestOfNModelName!==void 0?${input}.bestOfNModelName:${input}.taskParamModelName??${input}.subagentModelName}`;
|
|
376
|
+
}, "legacy subagent model display selector");
|
|
377
|
+
}
|
|
378
|
+
const patched = replaceSingleMatch(restored, subagentModelDisplaySelection, (match) => {
|
|
379
|
+
const { functionName, input } = match.groups;
|
|
380
|
+
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}`;
|
|
381
|
+
}, "subagent model display selector");
|
|
382
|
+
return { status: "patched", source: patched };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export function patchCursorSubagentTaskArgsDisplaySource(source) {
|
|
386
|
+
if (source.includes(cursorSubagentTaskArgsDisplayPatchMarker)) return { status: "already-patched", source };
|
|
387
|
+
const legacyContexts = [...source.matchAll(subagentTaskArgsContext)];
|
|
388
|
+
const contexts = legacyContexts.length > 0
|
|
389
|
+
? legacyContexts
|
|
390
|
+
: [...source.matchAll(directSubagentTaskArgsContext)];
|
|
391
|
+
if (contexts.length !== 1) {
|
|
392
|
+
throw new Error(`Expected one Cursor subagent task args context, found ${contexts.length}`);
|
|
393
|
+
}
|
|
394
|
+
const { args, bubble, taskModel } = contexts[0].groups;
|
|
395
|
+
const prompt = args ? `${bubble}.params?.prompt??${args}?.prompt??""` : `${bubble}.params?.prompt??""`;
|
|
396
|
+
const fallback = args ? `${taskModel}??${args}?.model` : taskModel;
|
|
397
|
+
const replacement = `taskParamModelName:${cursorSubagentTaskArgsDisplayPatchMarker}/<ocx-subagent-model>(opencodex\\/[^<]+)<\\/ocx-subagent-model>/.exec(${prompt})?.[1]??${fallback}`;
|
|
398
|
+
if (source.includes(legacyCursorSubagentTaskArgsDisplayV4PatchMarker)) {
|
|
399
|
+
const upgraded = replaceSingleMatch(source, legacySubagentTaskArgsDisplayV4, replacement, "v4 subagent task args model display field");
|
|
400
|
+
return { status: "patched", source: upgraded };
|
|
401
|
+
}
|
|
402
|
+
if (source.includes(legacyCursorSubagentTaskArgsDisplayV3PatchMarker)) {
|
|
403
|
+
const upgraded = replaceSingleMatch(source, legacySubagentTaskArgsDisplayV3, replacement, "v3 subagent task args model display field");
|
|
404
|
+
return { status: "patched", source: upgraded };
|
|
405
|
+
}
|
|
406
|
+
if (source.includes(legacyCursorSubagentTaskArgsDisplayV2PatchMarker)) {
|
|
407
|
+
const upgraded = replaceSingleMatch(source, legacySubagentTaskArgsDisplayV2, replacement, "v2 subagent task args model display field");
|
|
408
|
+
return { status: "patched", source: upgraded };
|
|
409
|
+
}
|
|
410
|
+
if (source.includes(legacyCursorSubagentTaskArgsDisplayPatchMarker)) {
|
|
411
|
+
const upgraded = replaceSingleMatch(source, legacySubagentTaskArgsDisplay, replacement, "legacy subagent task args model display field");
|
|
412
|
+
return { status: "patched", source: upgraded };
|
|
413
|
+
}
|
|
414
|
+
const field = new RegExp(`taskParamModelName:${taskModel}(?=[},])`, "g");
|
|
415
|
+
const patched = replaceSingleMatch(source, field, replacement, "subagent task args model display field");
|
|
416
|
+
return { status: "patched", source: patched };
|
|
417
|
+
}
|
|
418
|
+
|
|
264
419
|
export function patchCursorRoutedSubagentTypesSource(source) {
|
|
265
420
|
if (source.includes(cursorRoutedSubagentTypesPatchMarker)) return { status: "already-patched", source };
|
|
266
421
|
const patched = replaceSingleMatch(source, rawSubagentLoad, (match) => {
|
|
@@ -280,9 +435,11 @@ export function patchCursorWorkbenchSource(source) {
|
|
|
280
435
|
const executionRouting = patchCursorSubagentExecutionRoutingSource(promptRouting.source);
|
|
281
436
|
const runRouting = patchCursorSubagentRunRoutingSource(executionRouting.source);
|
|
282
437
|
const runtimeCredentials = patchCursorSubagentRuntimeCredentialsSource(runRouting.source);
|
|
283
|
-
const
|
|
438
|
+
const modelDisplay = patchCursorSubagentModelDisplaySource(runtimeCredentials.source);
|
|
439
|
+
const taskArgsDisplay = patchCursorSubagentTaskArgsDisplaySource(modelDisplay.source);
|
|
440
|
+
const subagentTypes = patchCursorRoutedSubagentTypesSource(taskArgsDisplay.source);
|
|
284
441
|
return {
|
|
285
|
-
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" || subagentTypes.status === "patched"
|
|
442
|
+
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"
|
|
286
443
|
? "patched"
|
|
287
444
|
: "already-patched",
|
|
288
445
|
source: subagentTypes.source,
|
package/src/gateway.mjs
CHANGED
|
@@ -45,7 +45,169 @@ function disableStrictFunctionTools(tools) {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
function normalizeCursorTool(tool) {
|
|
49
|
+
if (!isRecord(tool) || tool.type !== undefined || typeof tool.name !== "string" || !isRecord(tool.input_schema)) {
|
|
50
|
+
return tool;
|
|
51
|
+
}
|
|
52
|
+
const definition = {
|
|
53
|
+
name: tool.name,
|
|
54
|
+
...(typeof tool.description === "string" ? { description: tool.description } : {}),
|
|
55
|
+
parameters: tool.input_schema,
|
|
56
|
+
...(typeof tool.strict === "boolean" ? { strict: tool.strict } : {}),
|
|
57
|
+
};
|
|
58
|
+
return { type: "function", function: definition };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function normalizeToolChoice(choice) {
|
|
62
|
+
if (!isRecord(choice) || typeof choice.type !== "string") return choice;
|
|
63
|
+
if (choice.type === "auto") return "auto";
|
|
64
|
+
if (choice.type === "none") return "none";
|
|
65
|
+
if (choice.type === "any") return "required";
|
|
66
|
+
if (choice.type === "tool" && typeof choice.name === "string") {
|
|
67
|
+
return { type: "function", function: { name: choice.name } };
|
|
68
|
+
}
|
|
69
|
+
return choice;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeCursorTooling(payload) {
|
|
73
|
+
if (Array.isArray(payload.tools)) payload.tools = payload.tools.map(normalizeCursorTool);
|
|
74
|
+
if (payload.tool_choice !== undefined) payload.tool_choice = normalizeToolChoice(payload.tool_choice);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function toolResultText(value) {
|
|
78
|
+
if (typeof value === "string") return value;
|
|
79
|
+
if (Array.isArray(value)) return value.map(toolResultText).filter(Boolean).join("\n");
|
|
80
|
+
if (isRecord(value)) {
|
|
81
|
+
if (typeof value.text === "string") return value.text;
|
|
82
|
+
if (value.content !== undefined) return toolResultText(value.content);
|
|
83
|
+
try {
|
|
84
|
+
return JSON.stringify(value);
|
|
85
|
+
} catch {
|
|
86
|
+
return "";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return value === undefined || value === null ? "" : String(value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function openAiToolCallFromCursorBlock(block) {
|
|
93
|
+
let args = "{}";
|
|
94
|
+
try {
|
|
95
|
+
args = JSON.stringify(block.input ?? {}) ?? "{}";
|
|
96
|
+
} catch {
|
|
97
|
+
// Keep the request valid if Cursor supplied a non-serializable input value.
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
id: block.id,
|
|
101
|
+
type: "function",
|
|
102
|
+
function: {
|
|
103
|
+
name: block.name,
|
|
104
|
+
arguments: args,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function normalizeCursorChatMessages(messages) {
|
|
110
|
+
if (!Array.isArray(messages)) return messages;
|
|
111
|
+
const normalized = [];
|
|
112
|
+
for (const message of messages) {
|
|
113
|
+
if (!isRecord(message) || !Array.isArray(message.content)) {
|
|
114
|
+
normalized.push(message);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (message.role === "assistant") {
|
|
118
|
+
const allToolUses = message.content.filter((block) => isRecord(block) && block.type === "tool_use");
|
|
119
|
+
const toolUses = allToolUses.filter((block) => typeof block.id === "string" && block.id.length > 0 && typeof block.name === "string");
|
|
120
|
+
if (allToolUses.length > toolUses.length) {
|
|
121
|
+
normalized.push(message);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (toolUses.length === 0) {
|
|
125
|
+
normalized.push(message);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const existingToolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
129
|
+
const existingIds = new Set(existingToolCalls.map((call) => call?.id).filter((id) => typeof id === "string"));
|
|
130
|
+
const newToolUses = toolUses.filter((block) => !existingIds.has(block.id));
|
|
131
|
+
const content = message.content.filter((block) => !(isRecord(block) && block.type === "tool_use"));
|
|
132
|
+
normalized.push({
|
|
133
|
+
...message,
|
|
134
|
+
content: content.length > 0 ? content : null,
|
|
135
|
+
tool_calls: [
|
|
136
|
+
...existingToolCalls,
|
|
137
|
+
...newToolUses.map((block) => openAiToolCallFromCursorBlock(block)),
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const toolResults = message.content.filter((block) => isRecord(block) && block.type === "tool_result");
|
|
143
|
+
if (message.role !== "user" || toolResults.length === 0) {
|
|
144
|
+
normalized.push(message);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (toolResults.some((block) => typeof block.tool_use_id !== "string" || block.tool_use_id.length === 0)) {
|
|
148
|
+
normalized.push(message);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
for (const block of toolResults) {
|
|
152
|
+
normalized.push({
|
|
153
|
+
role: "tool",
|
|
154
|
+
tool_call_id: block.tool_use_id,
|
|
155
|
+
content: toolResultText(block.content),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const remainingContent = message.content.filter((block) => !(isRecord(block) && block.type === "tool_result"));
|
|
159
|
+
if (remainingContent.length > 0) normalized.push({ ...message, content: remainingContent });
|
|
160
|
+
}
|
|
161
|
+
return normalized;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function enrichSubagentModelTools(tools, catalog) {
|
|
165
|
+
if (!Array.isArray(tools)) return;
|
|
166
|
+
const aliases = catalog.map(({ alias }) => alias).filter((alias) => alias.startsWith(managedPrefix));
|
|
167
|
+
if (aliases.length === 0) return;
|
|
168
|
+
const modelList = aliases.map((alias) => `- ${alias}`).join("\n");
|
|
169
|
+
const modelListPattern = /(If the user explicitly asks for the model of a subagent\/task,[\s\S]*?)(?=\n\nIf the user isn't asking)/;
|
|
170
|
+
for (const tool of tools) {
|
|
171
|
+
if (!isRecord(tool) || tool.type !== "function") continue;
|
|
172
|
+
const definition = isRecord(tool.function) ? tool.function : tool;
|
|
173
|
+
if (typeof definition.name !== "string" || !/^(subagent|task)$/i.test(definition.name)) continue;
|
|
174
|
+
if (typeof definition.description === "string" && modelListPattern.test(definition.description)) {
|
|
175
|
+
definition.description = definition.description.replace(modelListPattern, `$1\n${modelList}`);
|
|
176
|
+
}
|
|
177
|
+
const model = definition.parameters?.properties?.model;
|
|
178
|
+
if (!isRecord(model)) continue;
|
|
179
|
+
if (Array.isArray(model.enum)) {
|
|
180
|
+
model.enum = [...new Set([...model.enum, ...aliases])];
|
|
181
|
+
}
|
|
182
|
+
if (typeof model.description === "string") {
|
|
183
|
+
model.description += ` Available OpenCodex model slugs: ${aliases.join(", ")}.`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function restoreRoutedSubagentModels(messages, catalog) {
|
|
189
|
+
if (!Array.isArray(messages)) return;
|
|
190
|
+
const aliases = new Set(catalog.map(({ alias }) => alias));
|
|
191
|
+
const restore = (text) => {
|
|
192
|
+
if (typeof text !== "string") return text;
|
|
193
|
+
const requested = [...text.matchAll(/<ocx-subagent-model>(opencodex\/[^<]+)<\/ocx-subagent-model>/g)]
|
|
194
|
+
.map((match) => match[1])
|
|
195
|
+
.find((alias) => aliases.has(alias));
|
|
196
|
+
return requested ? text.split("composer-2.5").join(requested) : text;
|
|
197
|
+
};
|
|
198
|
+
for (const message of messages) {
|
|
199
|
+
if (!isRecord(message)) continue;
|
|
200
|
+
if (typeof message.content === "string") {
|
|
201
|
+
message.content = restore(message.content);
|
|
202
|
+
} else if (Array.isArray(message.content)) {
|
|
203
|
+
for (const part of message.content) {
|
|
204
|
+
if (isRecord(part) && typeof part.text === "string") part.text = restore(part.text);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function rewriteModelAliasBody(body, catalog, route = "POST /v1/chat/completions") {
|
|
49
211
|
if (!body?.length) return body;
|
|
50
212
|
let payload;
|
|
51
213
|
try {
|
|
@@ -54,6 +216,12 @@ export function rewriteModelAliasBody(body, catalog) {
|
|
|
54
216
|
return body;
|
|
55
217
|
}
|
|
56
218
|
if (!isRecord(payload) || typeof payload.model !== "string" || !payload.model.startsWith(managedPrefix)) return body;
|
|
219
|
+
restoreRoutedSubagentModels(payload.messages, catalog);
|
|
220
|
+
if (route === "POST /v1/chat/completions") {
|
|
221
|
+
payload.messages = normalizeCursorChatMessages(payload.messages);
|
|
222
|
+
normalizeCursorTooling(payload);
|
|
223
|
+
enrichSubagentModelTools(payload.tools, catalog);
|
|
224
|
+
}
|
|
57
225
|
|
|
58
226
|
const variant = /^(opencodex\/.+?)\[([^\]]+)\]$/.exec(payload.model);
|
|
59
227
|
let alias = variant?.[1] || payload.model;
|
|
@@ -203,7 +371,7 @@ export function startGateway(options) {
|
|
|
203
371
|
const upstream = options.upstream || opencodexEndpoint();
|
|
204
372
|
const receivedBody = await readBody(req);
|
|
205
373
|
if (downstreamClosed) return;
|
|
206
|
-
const body = rewriteModelAliasBody(receivedBody, catalog);
|
|
374
|
+
const body = rewriteModelAliasBody(receivedBody, catalog, route);
|
|
207
375
|
const token = options.serviceToken !== undefined ? options.serviceToken : await serviceToken();
|
|
208
376
|
if (downstreamClosed) return;
|
|
209
377
|
upstreamReq = http.request({
|