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 +1 -1
- package/src/catalog.mjs +1 -1
- package/src/cursor-patch.mjs +83 -18
- package/src/gateway.mjs +123 -3
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
|
@@ -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-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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({
|