ocx-cursor 1.0.6 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ocx-cursor",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Use OpenCodex models in Cursor through an authenticated HTTPS gateway",
5
5
  "keywords": [
6
6
  "opencodex",
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|sonnet-5|opus-(?:5|4-[78]))$/.test(modelId)) return [...allowedEfforts];
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"];
@@ -21,7 +21,7 @@ export const legacyCursorSubagentCredentialsPatchMarker = "/*ocx-cursor-subagent
21
21
  export const cursorSubagentLegacyDetailsPatchMarker = "/*ocx-cursor-subagent-legacy-details-v2*/";
22
22
  export const legacyCursorSubagentLegacyDetailsPatchMarker = "/*ocx-cursor-subagent-legacy-details-v1*/";
23
23
  export const cursorSubagentPromptRoutingPatchMarker = "/*ocx-cursor-subagent-prompt-routing-v3*/";
24
- export const cursorSubagentExecutionRoutingPatchMarker = "/*ocx-cursor-subagent-execution-routing-v5*/";
24
+ export const cursorSubagentExecutionRoutingPatchMarker = "/*ocx-cursor-subagent-execution-routing-v6*/";
25
25
  export const cursorSubagentRunRoutingPatchMarker = "/*ocx-cursor-subagent-run-routing-v1*/";
26
26
  export const cursorSubagentRuntimeCredentialsPatchMarker = "/*ocx-cursor-subagent-runtime-credentials-v1*/";
27
27
  export const cursorSubagentModelDisplayPatchMarker = "/*ocx-cursor-subagent-model-display-v2*/";
@@ -60,6 +60,8 @@ const legacySubagentPromptRouting = /customSystemPrompt:\(\(\)=>\{\/\*ocx-cursor
60
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
61
  const createOrResumeSubagentStart = /async createOrResumeSubagent\((?<input>[A-Za-z_$][\w$]*)\)\{/g;
62
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;
63
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;
64
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;
65
67
  const appendSubagentComposer = /(?<declaration>const (?<result>[A-Za-z_$][\w$]*)=await this\._composerDataService\.appendSubComposer\((?<composer>[A-Za-z_$][\w$]*)\))/g;
@@ -69,6 +71,7 @@ const requestedModelCredentials = /(?<prefix>new [A-Za-z_$][\w$]*\(\{modelId:(?<
69
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;
70
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;
71
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;
72
75
  const legacySubagentTaskArgsDisplayV4 = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v4\*\/\(\(\)=>\{[\s\S]*?\}\)\(\)/g;
73
76
  const legacySubagentTaskArgsDisplayV3 = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v3\*\/\(\(\)=>\{[\s\S]*?return ocxCursorTaskModel\}\)\(\)/g;
74
77
  const legacySubagentTaskArgsDisplayV2 = /taskParamModelName:\/\*ocx-cursor-subagent-task-args-display-v2\*\/\(\(\)=>\{[\s\S]*?return ocxCursorTaskModel\}\)\(\)/g;
@@ -85,9 +88,29 @@ const directoryPatchQueues = new Map();
85
88
 
86
89
  export function clearCursorAppQuarantine(options = {}) {
87
90
  const execute = options.execFileSync || execFileSync;
88
- execute("/usr/bin/xattr", ["-dr", "com.apple.quarantine", options.appPath || cursorAppPath], {
89
- stdio: "ignore",
90
- });
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
+ }
91
114
  }
92
115
 
93
116
  export function finalizeCursorAppPatch(options = {}) {
@@ -260,11 +283,34 @@ export function patchCursorSubagentPromptRoutingSource(source) {
260
283
  export function patchCursorSubagentExecutionRoutingSource(source) {
261
284
  if (source.includes(cursorSubagentExecutionRoutingPatchMarker)) return { status: "already-patched", source };
262
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
+ }
263
303
  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");
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
+ }
268
314
  }
269
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*/")) {
270
316
  restored = replaceSingleMatch(restored, legacySubagentExecutionRouting, (match) => match.groups.start, "legacy subagent execution routing");
@@ -273,14 +319,28 @@ export function patchCursorSubagentExecutionRoutingSource(source) {
273
319
  const { input } = match.groups;
274
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)};`;
275
321
  }, "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");
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
+ }
284
344
  return { status: "patched", source: patched };
285
345
  }
286
346
 
@@ -324,12 +384,17 @@ export function patchCursorSubagentModelDisplaySource(source) {
324
384
 
325
385
  export function patchCursorSubagentTaskArgsDisplaySource(source) {
326
386
  if (source.includes(cursorSubagentTaskArgsDisplayPatchMarker)) return { status: "already-patched", source };
327
- const contexts = [...source.matchAll(subagentTaskArgsContext)];
387
+ const legacyContexts = [...source.matchAll(subagentTaskArgsContext)];
388
+ const contexts = legacyContexts.length > 0
389
+ ? legacyContexts
390
+ : [...source.matchAll(directSubagentTaskArgsContext)];
328
391
  if (contexts.length !== 1) {
329
392
  throw new Error(`Expected one Cursor subagent task args context, found ${contexts.length}`);
330
393
  }
331
394
  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`;
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}`;
333
398
  if (source.includes(legacyCursorSubagentTaskArgsDisplayV4PatchMarker)) {
334
399
  const upgraded = replaceSingleMatch(source, legacySubagentTaskArgsDisplayV4, replacement, "v4 subagent task args model display field");
335
400
  return { status: "patched", source: upgraded };
package/src/gateway.mjs CHANGED
@@ -45,6 +45,122 @@ function disableStrictFunctionTools(tools) {
45
45
  }
46
46
  }
47
47
 
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
+
48
164
  function enrichSubagentModelTools(tools, catalog) {
49
165
  if (!Array.isArray(tools)) return;
50
166
  const aliases = catalog.map(({ alias }) => alias).filter((alias) => alias.startsWith(managedPrefix));
@@ -91,7 +207,7 @@ function restoreRoutedSubagentModels(messages, catalog) {
91
207
  }
92
208
  }
93
209
 
94
- export function rewriteModelAliasBody(body, catalog) {
210
+ export function rewriteModelAliasBody(body, catalog, route = "POST /v1/chat/completions") {
95
211
  if (!body?.length) return body;
96
212
  let payload;
97
213
  try {
@@ -101,7 +217,11 @@ export function rewriteModelAliasBody(body, catalog) {
101
217
  }
102
218
  if (!isRecord(payload) || typeof payload.model !== "string" || !payload.model.startsWith(managedPrefix)) return body;
103
219
  restoreRoutedSubagentModels(payload.messages, catalog);
104
- enrichSubagentModelTools(payload.tools, catalog);
220
+ if (route === "POST /v1/chat/completions") {
221
+ payload.messages = normalizeCursorChatMessages(payload.messages);
222
+ normalizeCursorTooling(payload);
223
+ enrichSubagentModelTools(payload.tools, catalog);
224
+ }
105
225
 
106
226
  const variant = /^(opencodex\/.+?)\[([^\]]+)\]$/.exec(payload.model);
107
227
  let alias = variant?.[1] || payload.model;
@@ -251,7 +371,7 @@ export function startGateway(options) {
251
371
  const upstream = options.upstream || opencodexEndpoint();
252
372
  const receivedBody = await readBody(req);
253
373
  if (downstreamClosed) return;
254
- const body = rewriteModelAliasBody(receivedBody, catalog);
374
+ const body = rewriteModelAliasBody(receivedBody, catalog, route);
255
375
  const token = options.serviceToken !== undefined ? options.serviceToken : await serviceToken();
256
376
  if (downstreamClosed) return;
257
377
  upstreamReq = http.request({