@yansigit/opencodex 2.32.0 → 2.33.1
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 +2 -2
- package/gui/dist/assets/index-CIDo4y4k.js +102 -0
- package/gui/dist/assets/index-DrSQdTRd.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +8 -5
- package/src/adapters/anthropic.ts +20 -6
- package/src/adapters/azure.ts +20 -4
- package/src/adapters/base.ts +3 -1
- package/src/adapters/command-code.ts +131 -17
- package/src/adapters/cursor/envelope-echo.ts +162 -0
- package/src/adapters/cursor/live-transport.ts +4 -2
- package/src/adapters/cursor/native-exec-fs.ts +13 -12
- package/src/adapters/cursor/native-exec-network.ts +3 -5
- package/src/adapters/cursor/native-exec-policy.ts +47 -0
- package/src/adapters/cursor/native-exec-shell.ts +13 -25
- package/src/adapters/cursor/native-exec.ts +18 -10
- package/src/adapters/cursor/protobuf-events.ts +186 -9
- package/src/adapters/cursor/protobuf-request.ts +53 -18
- package/src/adapters/cursor/request-builder.ts +11 -3
- package/src/adapters/cursor/tool-definitions.ts +49 -2
- package/src/adapters/cursor/tool-result-normalize.ts +21 -8
- package/src/adapters/cursor/types.ts +11 -3
- package/src/adapters/cursor.ts +123 -0
- package/src/adapters/google-aistudio-parser.ts +49 -0
- package/src/adapters/google-antigravity-replay.ts +2 -2
- package/src/adapters/google-antigravity-wire.ts +7 -0
- package/src/adapters/google-errors.ts +6 -2
- package/src/adapters/google-http.ts +30 -7
- package/src/adapters/google-truncation.ts +5 -0
- package/src/adapters/google-wire-compiler.ts +38 -6
- package/src/adapters/google.ts +242 -28
- package/src/adapters/kiro-tools.ts +20 -9
- package/src/adapters/openai-chat.ts +9 -0
- package/src/adapters/openai-responses.ts +2 -1
- package/src/bridge.ts +112 -9
- package/src/chat/inbound.ts +15 -0
- package/src/claude/context-windows.ts +16 -9
- package/src/cli/doctor.ts +2 -2
- package/src/cli/index.ts +10 -3
- package/src/cli/provider.ts +6 -0
- package/src/cli/status.ts +23 -0
- package/src/codex/auth-api.ts +4 -2
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +12 -12
- package/src/codex/catalog/effort.ts +18 -3
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/model-metadata.ts +39 -12
- package/src/codex/catalog/parsing.ts +38 -27
- package/src/codex/catalog/provider-fetch.ts +232 -133
- package/src/codex/catalog/sync.ts +1 -1
- package/src/codex/convergence.ts +5 -0
- package/src/codex/shim.ts +56 -3
- package/src/config/provider-validation.ts +37 -0
- package/src/config.ts +78 -2
- package/src/generated/compatibility-version.json +187 -119
- package/src/generated/model-metadata.ts +3 -0
- package/src/images/loop.ts +37 -6
- package/src/lib/azure-identity.ts +154 -0
- package/src/lib/debug.ts +42 -0
- package/src/lib/errors.ts +14 -0
- package/src/lib/provider-outbound.ts +45 -33
- package/src/lib/provider-tls-profile.ts +309 -0
- package/src/lib/proxy-env.ts +49 -0
- package/src/lib/redact.ts +10 -1
- package/src/oauth/aistudio-native-daemon.ts +62 -0
- package/src/oauth/aistudio-session-sync.ts +95 -0
- package/src/oauth/antigravity-routing.ts +282 -236
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/command-code.ts +5 -16
- package/src/oauth/google-aistudio-auth.ts +98 -0
- package/src/oauth/google-antigravity.ts +42 -5
- package/src/oauth/index.ts +15 -3
- package/src/oauth/key-providers.ts +8 -0
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/login-cli.ts +66 -1
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +6 -0
- package/src/providers/antigravity-quota.ts +3 -1
- package/src/providers/api-keys.ts +2 -1
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/derive.ts +5 -1
- package/src/providers/key-failover.ts +5 -1
- package/src/providers/openai-tiers.ts +5 -0
- package/src/providers/provider-id-rewrite.ts +1 -0
- package/src/providers/quota.ts +148 -50
- package/src/providers/registry.ts +27 -4
- package/src/providers/request-pacing.ts +33 -6
- package/src/providers/xai-transport.ts +21 -0
- package/src/responses/google-provider-options.ts +36 -0
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +11 -0
- package/src/responses/provider-opaque-metadata.ts +3 -3
- package/src/responses/schema.ts +37 -0
- package/src/responses/state.ts +94 -4
- package/src/router.ts +11 -2
- package/src/routing/account-pool/cooldown.ts +8 -0
- package/src/routing/account-pool/index.ts +1 -0
- package/src/server/aistudio-ws-hub.ts +295 -0
- package/src/server/auth-cors.ts +29 -0
- package/src/server/chat-completions.ts +2 -0
- package/src/server/images.ts +19 -35
- package/src/server/index.ts +94 -0
- package/src/server/management/agent-settings-routes.ts +205 -15
- package/src/server/management/combo-routes.ts +6 -0
- package/src/server/management/config-routes.ts +31 -5
- package/src/server/management/logs-usage-routes.ts +11 -5
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/oauth-account-routes.ts +38 -7
- package/src/server/management/provider-routes.ts +113 -15
- package/src/server/management/routing-profile-routes.ts +3 -0
- package/src/server/port-reclaim.ts +19 -1
- package/src/server/request-log-conversation.ts +12 -0
- package/src/server/request-log.ts +23 -1
- package/src/server/responses/agent-task-recovery.ts +1 -1
- package/src/server/responses/compact.ts +30 -1
- package/src/server/responses/core.ts +363 -156
- package/src/server/responses/empty-completion-guard.ts +35 -6
- package/src/server/responses/fetch-helpers.ts +18 -5
- package/src/server/responses/policy-fallback.ts +1 -1
- package/src/server/responses/v2-native-parent-override.ts +59 -0
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-undeclared-tool-guard.ts +90 -8
- package/src/server/ws-bridge.ts +2 -1
- package/src/service.ts +1 -1
- package/src/smoke/fingerprint-cache.ts +133 -0
- package/src/smoke/live-scenarios.ts +33 -0
- package/src/smoke/runner.ts +119 -0
- package/src/types/config.ts +16 -1
- package/src/types/provider.ts +18 -1
- package/src/types/request.ts +30 -0
- package/src/types/tools.ts +51 -0
- package/src/types.ts +6 -0
- package/src/usage/command-code-manifest.ts +116 -0
- package/src/usage/cost.ts +2 -2
- package/src/usage/expected-prices.ts +83 -0
- package/src/usage/log.ts +2 -2
- package/src/usage/summary.ts +34 -12
- package/src/web-search/gemini-executor.ts +6 -4
- package/src/web-search/index.ts +16 -8
- package/src/web-search/loop.ts +42 -6
- package/gui/dist/assets/index-BG43zwVe.js +0 -102
- package/gui/dist/assets/index-CiSI-jrP.css +0 -1
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
type ExecServerMessage,
|
|
20
20
|
} from "./gen/agent_pb";
|
|
21
21
|
import { errorText, execBytes, execStreamCloseBytes } from "./native-exec-common";
|
|
22
|
+
export { nativeShellDisabledMessage, type CursorNativeExecPolicyContext } from "./native-exec-policy";
|
|
23
|
+
import { nativeShellDisabledMessage, type CursorNativeExecPolicyContext } from "./native-exec-policy";
|
|
22
24
|
import {
|
|
23
25
|
createAdmissionGate,
|
|
24
26
|
type AdmissionLease,
|
|
@@ -81,21 +83,7 @@ let absoluteTerminations = 0;
|
|
|
81
83
|
let unresolvedKills = 0;
|
|
82
84
|
let killFailures = 0;
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
export function nativeShellDisabledMessage(): string {
|
|
86
|
-
// Do not insist on "the same command" — that steers models into replaying bash/CMD
|
|
87
|
-
// idioms through the Codex bridge on Windows PowerShell 5.1 and looping (#604).
|
|
88
|
-
// Keep this host-shell-neutral: OpenCodex may run on a different OS than the Codex
|
|
89
|
-
// client that executes the bridge (LAN/SSH remote-proxy).
|
|
90
|
-
return (
|
|
91
|
-
"Route this through the Codex bridge shell tool from the current catalog (`shell_command` or `exec_command`, including the long `mcp_opencodex-responses_*` display name if listed). "
|
|
92
|
-
+ "Adapt the command for the Codex client host shell before calling the bridge "
|
|
93
|
-
+ "(Windows PowerShell 5.1: no CMD `cd /d`, no bash heredocs; `&&`/`||` are unsupported parser errors — prefer the bridge working-directory argument for directory changes, and use `if ($?) { ... }` for success-gated follow-up steps; do not treat `;` as a substitute for `&&`). "
|
|
94
|
-
+ "Make at most one corrected bridge attempt after a failure, then report the error and stop — do not repeat equivalent failing commands."
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function rejectedShellResult(command: string, cwd: string, started: number) {
|
|
86
|
+
function rejectedShellResult(command: string, cwd: string, started: number, opts: CursorNativeExecPolicyContext = {}) {
|
|
99
87
|
return create(ShellResultSchema, {
|
|
100
88
|
result: {
|
|
101
89
|
case: "failure",
|
|
@@ -105,7 +93,7 @@ function rejectedShellResult(command: string, cwd: string, started: number) {
|
|
|
105
93
|
exitCode: 1,
|
|
106
94
|
signal: "",
|
|
107
95
|
stdout: "",
|
|
108
|
-
stderr: nativeShellDisabledMessage(),
|
|
96
|
+
stderr: nativeShellDisabledMessage(opts),
|
|
109
97
|
executionTime: Date.now() - started,
|
|
110
98
|
aborted: true,
|
|
111
99
|
}),
|
|
@@ -113,10 +101,10 @@ function rejectedShellResult(command: string, cwd: string, started: number) {
|
|
|
113
101
|
});
|
|
114
102
|
}
|
|
115
103
|
|
|
116
|
-
export function rejectShellExecForPolicy(execMsg: ExecServerMessage): Uint8Array {
|
|
104
|
+
export function rejectShellExecForPolicy(execMsg: ExecServerMessage, opts: CursorNativeExecPolicyContext = {}): Uint8Array {
|
|
117
105
|
if (execMsg.message.case !== "shellArgs") throw new Error("invalid shell exec");
|
|
118
106
|
const args = execMsg.message.value;
|
|
119
|
-
return execBytes(execMsg, "shellResult", rejectedShellResult(args.command, resolve(args.workingDirectory || process.cwd()), Date.now()));
|
|
107
|
+
return execBytes(execMsg, "shellResult", rejectedShellResult(args.command, resolve(args.workingDirectory || process.cwd()), Date.now(), opts));
|
|
120
108
|
}
|
|
121
109
|
|
|
122
110
|
export function shellExec(execMsg: ExecServerMessage): Uint8Array {
|
|
@@ -154,7 +142,7 @@ export function shellExec(execMsg: ExecServerMessage): Uint8Array {
|
|
|
154
142
|
}));
|
|
155
143
|
}
|
|
156
144
|
|
|
157
|
-
export function rejectShellStreamExecForPolicy(execMsg: ExecServerMessage): Uint8Array[] {
|
|
145
|
+
export function rejectShellStreamExecForPolicy(execMsg: ExecServerMessage, opts: CursorNativeExecPolicyContext = {}): Uint8Array[] {
|
|
158
146
|
if (execMsg.message.case !== "shellStreamArgs") throw new Error("invalid shell stream exec");
|
|
159
147
|
const args = execMsg.message.value;
|
|
160
148
|
const cwd = resolve(args.workingDirectory || process.cwd());
|
|
@@ -164,12 +152,12 @@ export function rejectShellStreamExecForPolicy(execMsg: ExecServerMessage): Uint
|
|
|
164
152
|
event: { case: "start", value: create(ShellStreamStartSchema, { sandboxPolicy: args.requestedSandboxPolicy }) },
|
|
165
153
|
})),
|
|
166
154
|
execBytes(execMsg, "shellStream", create(ShellStreamSchema, {
|
|
167
|
-
event: { case: "stderr", value: create(ShellStreamStderrSchema, { data: nativeShellDisabledMessage() }) },
|
|
155
|
+
event: { case: "stderr", value: create(ShellStreamStderrSchema, { data: nativeShellDisabledMessage(opts) }) },
|
|
168
156
|
})),
|
|
169
157
|
execBytes(execMsg, "shellStream", create(ShellStreamSchema, {
|
|
170
158
|
event: { case: "exit", value: create(ShellStreamExitSchema, { code: 1, cwd, aborted: true }) },
|
|
171
159
|
})),
|
|
172
|
-
execBytes(execMsg, "shellResult", rejectedShellResult(args.command, cwd, started)),
|
|
160
|
+
execBytes(execMsg, "shellResult", rejectedShellResult(args.command, cwd, started, opts)),
|
|
173
161
|
execStreamCloseBytes(execMsg),
|
|
174
162
|
];
|
|
175
163
|
}
|
|
@@ -260,12 +248,12 @@ export async function shellStreamExec(execMsg: ExecServerMessage): Promise<Uint8
|
|
|
260
248
|
return replies;
|
|
261
249
|
}
|
|
262
250
|
|
|
263
|
-
export function rejectBackgroundShellSpawnExecForPolicy(execMsg: ExecServerMessage): Uint8Array {
|
|
251
|
+
export function rejectBackgroundShellSpawnExecForPolicy(execMsg: ExecServerMessage, opts: CursorNativeExecPolicyContext = {}): Uint8Array {
|
|
264
252
|
if (execMsg.message.case !== "backgroundShellSpawnArgs") throw new Error("invalid background shell exec");
|
|
265
253
|
const args = execMsg.message.value;
|
|
266
254
|
const cwd = resolve(args.workingDirectory || process.cwd());
|
|
267
255
|
return execBytes(execMsg, "backgroundShellSpawnResult", create(BackgroundShellSpawnResultSchema, {
|
|
268
|
-
result: { case: "error", value: create(BackgroundShellSpawnErrorSchema, { command: args.command, workingDirectory: cwd, error: nativeShellDisabledMessage() }) },
|
|
256
|
+
result: { case: "error", value: create(BackgroundShellSpawnErrorSchema, { command: args.command, workingDirectory: cwd, error: nativeShellDisabledMessage(opts) }) },
|
|
269
257
|
}));
|
|
270
258
|
}
|
|
271
259
|
|
|
@@ -517,10 +505,10 @@ export function backgroundShellSpawnExec(execMsg: ExecServerMessage, sessionId:
|
|
|
517
505
|
}
|
|
518
506
|
}
|
|
519
507
|
|
|
520
|
-
export function rejectWriteShellStdinExecForPolicy(execMsg: ExecServerMessage): Uint8Array {
|
|
508
|
+
export function rejectWriteShellStdinExecForPolicy(execMsg: ExecServerMessage, opts: CursorNativeExecPolicyContext = {}): Uint8Array {
|
|
521
509
|
if (execMsg.message.case !== "writeShellStdinArgs") throw new Error("invalid shell stdin exec");
|
|
522
510
|
return execBytes(execMsg, "writeShellStdinResult", create(WriteShellStdinResultSchema, {
|
|
523
|
-
result: { case: "error", value: create(WriteShellStdinErrorSchema, { error: nativeShellDisabledMessage() }) },
|
|
511
|
+
result: { case: "error", value: create(WriteShellStdinErrorSchema, { error: nativeShellDisabledMessage(opts) }) },
|
|
524
512
|
}));
|
|
525
513
|
}
|
|
526
514
|
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
type CursorNativeToolDeps,
|
|
52
52
|
} from "./native-exec-tools";
|
|
53
53
|
import { clientBytes, execBytes, execStreamCloseBytes, execThrowBytes } from "./native-exec-common";
|
|
54
|
+
import { type CursorNativeExecPolicyContext } from "./native-exec-policy";
|
|
54
55
|
import type { McpToolDefinition } from "./gen/agent_pb";
|
|
55
56
|
import { OCX_RESPONSES_TOOL_PROVIDER } from "./tool-definitions";
|
|
56
57
|
|
|
@@ -72,12 +73,18 @@ export interface CursorNativeExecContext extends CursorNativeExecDeps {
|
|
|
72
73
|
rejectNativeFileMutations?: boolean;
|
|
73
74
|
/** The synthetic exact-match edit tools (edit_file / multi_edit) are advertised this request. */
|
|
74
75
|
structuredEditAvailable?: boolean;
|
|
76
|
+
/** Codex code mode advertises one freeform exec tool; native rejections must steer to nested helpers. */
|
|
77
|
+
codeMode?: boolean;
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
export function cursorUnsafeNativeLocalExecEnabled(input: Pick<CursorNativeExecContext, "unsafeAllowNativeLocalExec"> = {}): boolean {
|
|
78
81
|
return input.unsafeAllowNativeLocalExec === true;
|
|
79
82
|
}
|
|
80
83
|
|
|
84
|
+
function nativeExecPolicyContext(deps: CursorNativeExecContext): CursorNativeExecPolicyContext {
|
|
85
|
+
return deps.codeMode === true ? { codeMode: true } : {};
|
|
86
|
+
}
|
|
87
|
+
|
|
81
88
|
/**
|
|
82
89
|
* Content-addressed blob store shared across streams. Bounded: without eviction a long-running
|
|
83
90
|
* proxy accumulates every conversation's prompt blobs forever (unbounded memory) and any stale
|
|
@@ -556,16 +563,17 @@ export async function handleCursorNativeExec(execMsg: ExecServerMessage, deps: C
|
|
|
556
563
|
}))];
|
|
557
564
|
}
|
|
558
565
|
if (!cursorUnsafeNativeLocalExecEnabled(deps)) {
|
|
559
|
-
|
|
560
|
-
if (execCase === "
|
|
561
|
-
if (execCase === "
|
|
562
|
-
if (execCase === "
|
|
563
|
-
if (execCase === "
|
|
564
|
-
if (execCase === "
|
|
565
|
-
if (execCase === "
|
|
566
|
-
if (execCase === "
|
|
567
|
-
if (execCase === "
|
|
568
|
-
if (execCase === "
|
|
566
|
+
const policy = nativeExecPolicyContext(deps);
|
|
567
|
+
if (execCase === "readArgs") return [rejectReadExecForPolicy(execMsg, policy)];
|
|
568
|
+
if (execCase === "writeArgs") return [rejectWriteExecForPolicy(execMsg, policy)];
|
|
569
|
+
if (execCase === "deleteArgs") return [rejectDeleteExecForPolicy(execMsg, policy)];
|
|
570
|
+
if (execCase === "lsArgs") return [rejectLsExecForPolicy(execMsg, policy)];
|
|
571
|
+
if (execCase === "grepArgs") return [rejectGrepExecForPolicy(execMsg, policy)];
|
|
572
|
+
if (execCase === "shellArgs") return [rejectShellExecForPolicy(execMsg, policy)];
|
|
573
|
+
if (execCase === "shellStreamArgs") return rejectShellStreamExecForPolicy(execMsg, policy);
|
|
574
|
+
if (execCase === "backgroundShellSpawnArgs") return [rejectBackgroundShellSpawnExecForPolicy(execMsg, policy)];
|
|
575
|
+
if (execCase === "writeShellStdinArgs") return [rejectWriteShellStdinExecForPolicy(execMsg, policy)];
|
|
576
|
+
if (execCase === "fetchArgs") return [rejectFetchExecForPolicy(execMsg, policy)];
|
|
569
577
|
}
|
|
570
578
|
if (execCase === "readArgs") return [readExec(execMsg)];
|
|
571
579
|
if (execCase === "writeArgs") return [deps.rejectNativeFileMutations ? rejectWriteExecForApplyPatch(execMsg, deps.structuredEditAvailable === true) : writeExec(execMsg)];
|
|
@@ -176,6 +176,7 @@ export interface CursorProtobufEventState {
|
|
|
176
176
|
*/
|
|
177
177
|
syntheticStructuredEditToolNames?: ReadonlySet<string>;
|
|
178
178
|
translatorBudget?: TranslatorBudget;
|
|
179
|
+
textToolCallBuffer?: string;
|
|
179
180
|
}
|
|
180
181
|
|
|
181
182
|
|
|
@@ -329,7 +330,7 @@ function isCompleteJson(text: string): boolean {
|
|
|
329
330
|
|
|
330
331
|
/** Schema-normalize a JSON-text argument blob for a named tool, if a schema is known. */
|
|
331
332
|
function normalizeJsonText(text: string, toolName: string | undefined, state: CursorProtobufEventState): string {
|
|
332
|
-
const schema = toolSchemaForWireName(state, toolName);
|
|
333
|
+
const schema = toolSchemaForWireName(state, toolName) ?? (toolName ? defaultShellBridgeArgNormalizeSchema(toolName) : undefined);
|
|
333
334
|
if (!schema) return text;
|
|
334
335
|
try {
|
|
335
336
|
const parsed = JSON.parse(text);
|
|
@@ -1135,6 +1136,31 @@ function cursorFreeformWrapperValid(args: string): boolean {
|
|
|
1135
1136
|
}
|
|
1136
1137
|
}
|
|
1137
1138
|
|
|
1139
|
+
/**
|
|
1140
|
+
* Cursor sometimes sends a freeform body as a JSON string or as a one-key object instead of the
|
|
1141
|
+
* advertised `{input: string}` wrapper. Preserve incomplete JSON for the late native-args path,
|
|
1142
|
+
* but wrap complete/raw bodies before the Responses bridge validates them.
|
|
1143
|
+
*/
|
|
1144
|
+
function normalizeFreeformArgs(args: string): string {
|
|
1145
|
+
if (args.length === 0) return args;
|
|
1146
|
+
try {
|
|
1147
|
+
const parsed = JSON.parse(args) as unknown;
|
|
1148
|
+
if (typeof parsed === "string") return JSON.stringify({ input: parsed });
|
|
1149
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1150
|
+
const object = parsed as Record<string, unknown>;
|
|
1151
|
+
if ("input" in object || Object.keys(object).length === 0) return args;
|
|
1152
|
+
const values = Object.values(object);
|
|
1153
|
+
const input = values.length === 1 && typeof values[0] === "string" ? values[0] : JSON.stringify(object);
|
|
1154
|
+
return JSON.stringify({ input });
|
|
1155
|
+
}
|
|
1156
|
+
return args;
|
|
1157
|
+
} catch {
|
|
1158
|
+
return args.trimStart().startsWith("{") || args.trimStart().startsWith("[")
|
|
1159
|
+
? args
|
|
1160
|
+
: JSON.stringify({ input: args });
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1138
1164
|
function dropInvalidFreeformCall(state: CursorProtobufEventState, callId: string, toolName: string): CursorServerMessage[] {
|
|
1139
1165
|
state.openToolCalls.delete(callId);
|
|
1140
1166
|
state.translatorBudget?.closeCall(callId);
|
|
@@ -1161,6 +1187,7 @@ function dropStructuredEditCall(state: CursorProtobufEventState, callId: string,
|
|
|
1161
1187
|
function commitToolCall(state: CursorProtobufEventState, callId: string, finalArgs: string): CursorServerMessage[] {
|
|
1162
1188
|
const open = state.openToolCalls.get(callId);
|
|
1163
1189
|
if (!open) return [];
|
|
1190
|
+
if (state.freeformToolNames?.has(open.name)) finalArgs = normalizeFreeformArgs(finalArgs);
|
|
1164
1191
|
if (state.freeformToolNames?.has(open.name) && !cursorFreeformWrapperValid(finalArgs)) {
|
|
1165
1192
|
return dropInvalidFreeformCall(state, callId, open.name);
|
|
1166
1193
|
}
|
|
@@ -1240,14 +1267,159 @@ export function mapCursorProtobufServerMessage(
|
|
|
1240
1267
|
return [];
|
|
1241
1268
|
}
|
|
1242
1269
|
|
|
1270
|
+
const TOOL_CALL_START_PREFIX = "[TOOL_CALL]";
|
|
1271
|
+
const TOOL_CALL_ARGS_MARKER = "[ARGS]";
|
|
1272
|
+
|
|
1273
|
+
function findPotentialMarkerPrefix(text: string): number {
|
|
1274
|
+
for (let len = Math.min(TOOL_CALL_START_PREFIX.length - 1, text.length); len >= 1; len--) {
|
|
1275
|
+
const candidate = text.slice(text.length - len);
|
|
1276
|
+
if (TOOL_CALL_START_PREFIX.startsWith(candidate)) {
|
|
1277
|
+
return text.length - len;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
return -1;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
function parseCursorTextToolCalls(text: string, state: CursorProtobufEventState): CursorServerMessage[] {
|
|
1284
|
+
if (!state.clientToolNames) {
|
|
1285
|
+
return text ? [{ type: "text", text: normalizeCursorTextToolMarkers(text) }] : [];
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
let remaining = (state.textToolCallBuffer ?? "") + text;
|
|
1289
|
+
state.textToolCallBuffer = undefined;
|
|
1290
|
+
|
|
1291
|
+
if (!remaining) return [];
|
|
1292
|
+
|
|
1293
|
+
const out: CursorServerMessage[] = [];
|
|
1294
|
+
|
|
1295
|
+
while (remaining.length > 0) {
|
|
1296
|
+
const startIndex = remaining.indexOf(TOOL_CALL_START_PREFIX);
|
|
1297
|
+
if (startIndex === -1) {
|
|
1298
|
+
const partialIndex = findPotentialMarkerPrefix(remaining);
|
|
1299
|
+
if (partialIndex !== -1) {
|
|
1300
|
+
if (partialIndex > 0) {
|
|
1301
|
+
out.push({ type: "text", text: normalizeCursorTextToolMarkers(remaining.slice(0, partialIndex)) });
|
|
1302
|
+
}
|
|
1303
|
+
state.textToolCallBuffer = remaining.slice(partialIndex);
|
|
1304
|
+
break;
|
|
1305
|
+
}
|
|
1306
|
+
out.push({ type: "text", text: normalizeCursorTextToolMarkers(remaining) });
|
|
1307
|
+
break;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
// Check if the candidate at startIndex is actually a tool call
|
|
1311
|
+
const candidateAfter = remaining.slice(startIndex + TOOL_CALL_START_PREFIX.length);
|
|
1312
|
+
const argsPos = candidateAfter.indexOf(TOOL_CALL_ARGS_MARKER);
|
|
1313
|
+
const namePart = argsPos !== -1 ? candidateAfter.slice(0, argsPos).trim() : candidateAfter;
|
|
1314
|
+
const isCandidateValid = namePart.length > 0 && namePart.length <= 64 && /^[a-zA-Z0-9_-]+$/.test(namePart);
|
|
1315
|
+
|
|
1316
|
+
if (!isCandidateValid && argsPos === -1) {
|
|
1317
|
+
// Not a valid tool call and no [ARGS]; treat as plain text
|
|
1318
|
+
out.push({ type: "text", text: normalizeCursorTextToolMarkers(remaining) });
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
if (startIndex > 0) {
|
|
1323
|
+
out.push({ type: "text", text: normalizeCursorTextToolMarkers(remaining.slice(0, startIndex)) });
|
|
1324
|
+
remaining = remaining.slice(startIndex);
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
const argsMarkerIndex = remaining.indexOf(TOOL_CALL_ARGS_MARKER, TOOL_CALL_START_PREFIX.length);
|
|
1329
|
+
if (argsMarkerIndex === -1) {
|
|
1330
|
+
const nameCandidate = remaining.slice(TOOL_CALL_START_PREFIX.length);
|
|
1331
|
+
if (nameCandidate.length > 64 || /[^a-zA-Z0-9_-]/.test(nameCandidate)) {
|
|
1332
|
+
out.push({ type: "text", text: normalizeCursorTextToolMarkers(remaining) });
|
|
1333
|
+
break;
|
|
1334
|
+
}
|
|
1335
|
+
state.textToolCallBuffer = remaining;
|
|
1336
|
+
break;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
const rawName = remaining.slice(TOOL_CALL_START_PREFIX.length, argsMarkerIndex).trim();
|
|
1340
|
+
const wireName = normalizeCursorWireName(rawName);
|
|
1341
|
+
const advertisedName = resolveAdvertisedClientToolName(state, wireName);
|
|
1342
|
+
|
|
1343
|
+
if (!advertisedName) {
|
|
1344
|
+
const endOfMarker = argsMarkerIndex + TOOL_CALL_ARGS_MARKER.length;
|
|
1345
|
+
out.push({ type: "text", text: normalizeCursorTextToolMarkers(remaining.slice(0, endOfMarker)) });
|
|
1346
|
+
remaining = remaining.slice(endOfMarker);
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
const argsStart = argsMarkerIndex + TOOL_CALL_ARGS_MARKER.length;
|
|
1351
|
+
let argsEnd = -1;
|
|
1352
|
+
const trimmedOffset = remaining.slice(argsStart).search(/\S/);
|
|
1353
|
+
if (trimmedOffset !== -1 && remaining[argsStart + trimmedOffset] === "{") {
|
|
1354
|
+
let depth = 0;
|
|
1355
|
+
let inString = false;
|
|
1356
|
+
let escape = false;
|
|
1357
|
+
const jsonStart = argsStart + trimmedOffset;
|
|
1358
|
+
for (let i = jsonStart; i < remaining.length; i++) {
|
|
1359
|
+
const char = remaining[i];
|
|
1360
|
+
if (escape) {
|
|
1361
|
+
escape = false;
|
|
1362
|
+
continue;
|
|
1363
|
+
}
|
|
1364
|
+
if (char === "\\") {
|
|
1365
|
+
escape = true;
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
if (char === '"') {
|
|
1369
|
+
inString = !inString;
|
|
1370
|
+
continue;
|
|
1371
|
+
}
|
|
1372
|
+
if (!inString) {
|
|
1373
|
+
if (char === "{") depth++;
|
|
1374
|
+
else if (char === "}") {
|
|
1375
|
+
depth--;
|
|
1376
|
+
if (depth === 0) {
|
|
1377
|
+
argsEnd = i + 1;
|
|
1378
|
+
break;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
if (argsEnd === -1) {
|
|
1386
|
+
state.textToolCallBuffer = remaining;
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
const argsJson = remaining.slice(argsStart, argsEnd).trim();
|
|
1391
|
+
const toolCallId = `call_${crypto.randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
1392
|
+
|
|
1393
|
+
const recordEvents = recordToolCall(state, toolCallId, wireName);
|
|
1394
|
+
out.push(...recordEvents);
|
|
1395
|
+
if (!recordEvents.some(e => e.type === "error")) {
|
|
1396
|
+
const open = state.openToolCalls.get(toolCallId);
|
|
1397
|
+
if (open) open.args = argsJson;
|
|
1398
|
+
let finalArgs = normalizeJsonText(argsJson, wireName, state);
|
|
1399
|
+
if (state.freeformToolNames?.has(open?.name ?? "") && !cursorFreeformWrapperValid(finalArgs)) {
|
|
1400
|
+
try {
|
|
1401
|
+
const parsed = JSON.parse(finalArgs) as unknown;
|
|
1402
|
+
if (typeof parsed === "string") {
|
|
1403
|
+
finalArgs = JSON.stringify({ input: parsed });
|
|
1404
|
+
}
|
|
1405
|
+
} catch {
|
|
1406
|
+
finalArgs = JSON.stringify({ input: finalArgs });
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
out.push(...commitToolCall(state, toolCallId, finalArgs));
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
remaining = remaining.slice(argsEnd);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
return out;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1243
1418
|
if (serverMessage.message.case !== "interactionUpdate") return [];
|
|
1244
1419
|
const update = serverMessage.message.value.message;
|
|
1245
1420
|
switch (update.case) {
|
|
1246
1421
|
case "textDelta":
|
|
1247
|
-
|
|
1248
|
-
// the advertised wire name before any client sees the text. Real frames are already
|
|
1249
|
-
// normalized structurally (mcpWireNameFromArgs above).
|
|
1250
|
-
return update.value.text ? [{ type: "text", text: normalizeCursorTextToolMarkers(update.value.text) }] : [];
|
|
1422
|
+
return update.value.text ? parseCursorTextToolCalls(update.value.text, state) : [];
|
|
1251
1423
|
case "thinkingDelta":
|
|
1252
1424
|
return update.value.text ? [{ type: "thinking", thinking: update.value.text }] : [];
|
|
1253
1425
|
case "toolCallStarted": {
|
|
@@ -1290,7 +1462,7 @@ export function mapCursorProtobufServerMessage(
|
|
|
1290
1462
|
&& (
|
|
1291
1463
|
(name !== undefined && openBeforeStart.args.length === 0)
|
|
1292
1464
|
|| (state.freeformToolNames?.has(openBeforeStart.name) === true
|
|
1293
|
-
&& !cursorFreeformWrapperValid(openBeforeStart.args)
|
|
1465
|
+
&& !cursorFreeformWrapperValid(normalizeFreeformArgs(openBeforeStart.args))
|
|
1294
1466
|
)
|
|
1295
1467
|
)
|
|
1296
1468
|
) {
|
|
@@ -1314,7 +1486,7 @@ export function mapCursorProtobufServerMessage(
|
|
|
1314
1486
|
if (
|
|
1315
1487
|
!openBeforeStart && open && !hasMcpArgBytes(args)
|
|
1316
1488
|
&& state.freeformToolNames?.has(open.name) === true
|
|
1317
|
-
&& !cursorFreeformWrapperValid(open.args)
|
|
1489
|
+
&& !cursorFreeformWrapperValid(normalizeFreeformArgs(open.args))
|
|
1318
1490
|
) {
|
|
1319
1491
|
open.awaitingNativeArgs = true;
|
|
1320
1492
|
return [];
|
|
@@ -1364,18 +1536,23 @@ export function resolvedTurnUsage(state: CursorProtobufEventState): OcxUsage {
|
|
|
1364
1536
|
*/
|
|
1365
1537
|
export function finalizeTurnEvents(state: CursorProtobufEventState): CursorServerMessage[] {
|
|
1366
1538
|
state.terminated = true;
|
|
1539
|
+
const prefixEvents: CursorServerMessage[] = [];
|
|
1540
|
+
if (state.textToolCallBuffer) {
|
|
1541
|
+
prefixEvents.push({ type: "text", text: normalizeCursorTextToolMarkers(state.textToolCallBuffer) });
|
|
1542
|
+
state.textToolCallBuffer = undefined;
|
|
1543
|
+
}
|
|
1367
1544
|
if (state.openToolCalls.size > 0) {
|
|
1368
1545
|
const openCallIds = [...state.openToolCalls.keys()];
|
|
1369
1546
|
const openIds = openCallIds.join(", ");
|
|
1370
1547
|
// Clear so a second turnEnded (should not happen, but defensive) doesn't re-emit.
|
|
1371
1548
|
for (const callId of openCallIds) state.translatorBudget?.closeCall(callId);
|
|
1372
1549
|
state.openToolCalls.clear();
|
|
1373
|
-
return [{ type: "error", message: `Cursor stream ended with incomplete tool call(s): ${openIds}. Arguments may be truncated; the call was not committed.` }];
|
|
1550
|
+
return [...prefixEvents, { type: "error", message: `Cursor stream ended with incomplete tool call(s): ${openIds}. Arguments may be truncated; the call was not committed.` }];
|
|
1374
1551
|
}
|
|
1375
1552
|
// Surface the absolute context size (when Cursor reported a checkpoint) as both totalTokens and
|
|
1376
1553
|
// the estimated input side of Codex's visible `input + output` counter. Codex status lines can
|
|
1377
1554
|
// render the additive pair instead of total_tokens, so leaving inputTokens at 0 makes a 16k-context
|
|
1378
1555
|
// first turn display as "9 used". Keep outputTokens as the per-turn delta and clamp the inferred
|
|
1379
1556
|
// input to 0 in case Cursor reports a checkpoint smaller than the streamed output delta.
|
|
1380
|
-
return [{ type: "done", usage: resolvedTurnUsage(state) }];
|
|
1557
|
+
return [...prefixEvents, { type: "done", usage: resolvedTurnUsage(state) }];
|
|
1381
1558
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { create, fromBinary, toBinary, toJson } from "@bufbuild/protobuf";
|
|
2
2
|
import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
|
3
3
|
import { ValueSchema } from "@bufbuild/protobuf/wkt";
|
|
4
|
-
import type { OcxAssistantContentPart, OcxMessage, OcxToolResultMessage } from "../../types";
|
|
4
|
+
import type { OcxAssistantContentPart, OcxMessage, OcxRequestOptions, OcxToolResultMessage } from "../../types";
|
|
5
5
|
import { namespacedToolName } from "../../types";
|
|
6
6
|
import type { CursorRunRequest } from "./types";
|
|
7
7
|
import { cursorNeedsExternalToolContinuation, isCursorExternalWireModel } from "./discovery";
|
|
@@ -77,7 +77,22 @@ export const CURSOR_EXTERNAL_ROOT_BYTE_LIMIT = 512 * 1024;
|
|
|
77
77
|
* results already stored in history blobs are visible without a ResumeAction.
|
|
78
78
|
*/
|
|
79
79
|
export const CURSOR_EXTERNAL_TOOL_CONTINUATION_TEXT =
|
|
80
|
-
"Continue: the requested tool results are provided in the conversation history above.";
|
|
80
|
+
"Continue: the requested tool results are provided in the conversation history above. Answer the user request or proceed with the next step directly without repeating status summaries or greetings.";
|
|
81
|
+
|
|
82
|
+
export function externalToolContinuationText(rawMessages?: readonly OcxMessage[]): string {
|
|
83
|
+
const last = rawMessages?.at(-1);
|
|
84
|
+
if (last?.role === "toolResult") {
|
|
85
|
+
const raw = typeof last.content === "string" ? last.content : JSON.stringify(last.content ?? "");
|
|
86
|
+
const trimmed = raw.trim();
|
|
87
|
+
if (
|
|
88
|
+
(last.toolName?.includes("list_agents") || last.toolName?.includes("search"))
|
|
89
|
+
&& (trimmed === "[]" || trimmed === "" || trimmed === "{}" || trimmed === "null")
|
|
90
|
+
) {
|
|
91
|
+
return `${CURSOR_EXTERNAL_TOOL_CONTINUATION_TEXT} If a prior discovery or list tool returned empty results (e.g. no sub-agents currently active), proceed directly with your next concrete action using available tools.`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return CURSOR_EXTERNAL_TOOL_CONTINUATION_TEXT;
|
|
95
|
+
}
|
|
81
96
|
|
|
82
97
|
/** Runtime timezone for protobuf RequestContextEnv (dynamic, never hardcoded). */
|
|
83
98
|
function runtimeTimeZone(): string {
|
|
@@ -165,9 +180,26 @@ function truncateToolResultBlob(entry: RootBlobCandidate, maxBytes: number): Roo
|
|
|
165
180
|
return markerOnly.byteLength <= maxBytes ? markerOnly : null;
|
|
166
181
|
}
|
|
167
182
|
|
|
183
|
+
function structuredOutputPrompt(textFormat: OcxRequestOptions["textFormat"]): string | undefined {
|
|
184
|
+
if (!textFormat) return undefined;
|
|
185
|
+
if (textFormat.type === "json_schema" && textFormat.schema) {
|
|
186
|
+
return [
|
|
187
|
+
"Your response must be a single valid JSON object strictly conforming to this JSON schema:",
|
|
188
|
+
JSON.stringify(textFormat.schema),
|
|
189
|
+
"Do not include any surrounding markdown fences, preamble, or commentary; return raw JSON only.",
|
|
190
|
+
].join("\n");
|
|
191
|
+
}
|
|
192
|
+
if (textFormat.type === "json_object") {
|
|
193
|
+
return "Your response must be a single valid JSON object. Do not include any markdown fences or commentary; return raw JSON only.";
|
|
194
|
+
}
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
168
198
|
function systemPromptBlobs(request: CursorRunRequest): RootBlobCandidate[] {
|
|
169
199
|
const prompts = request.system.length > 0 ? [...request.system] : ["You are a helpful assistant."];
|
|
170
200
|
if (cursorRequestHasShellAlias(request.tools)) prompts.push(CURSOR_SHELL_ALIAS_SYSTEM_NOTE);
|
|
201
|
+
const structuredPrompt = structuredOutputPrompt(request.textFormat);
|
|
202
|
+
if (structuredPrompt) prompts.push(structuredPrompt);
|
|
171
203
|
const cursorToolGuidance = buildCursorToolGuidanceSystemNote(
|
|
172
204
|
cursorToolsForActivePrompt(request.tools, activePromptText(request), request.toolChoice),
|
|
173
205
|
request.toolChoice,
|
|
@@ -190,10 +222,11 @@ function assistantRootText(
|
|
|
190
222
|
// Cursor builds the actual model prompt from rootPromptMessagesJson (turns[] is UI/display metadata),
|
|
191
223
|
// so prior history must be replayed here or a ResumeAction has nothing model-visible to continue from.
|
|
192
224
|
// The active user message is excluded because it travels in the action. When the continuation cannot
|
|
193
|
-
// rely on native MCP turn state, tool results stay assistant-role text
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
225
|
+
// rely on native MCP turn state, tool results stay assistant-role text so Cursor does not wrap them
|
|
226
|
+
// as `<user_query>` (#1992). External replay uses a neutral "Tool output" label; protocol markers
|
|
227
|
+
// such as [Tool Result] are reserved for native wire encoding because external models echo them.
|
|
228
|
+
// Native resume models already carry the paired MCP result on turns[], so it is omitted from root
|
|
229
|
+
// replay. Each entry is a SHA-256 blob ID.
|
|
197
230
|
function rootPromptMessages(request: CursorRunRequest, requestScope: CursorBlobRequestScopeToken): {
|
|
198
231
|
ids: Uint8Array[];
|
|
199
232
|
byteLength: number;
|
|
@@ -246,14 +279,10 @@ function rootPromptMessages(request: CursorRunRequest, requestScope: CursorBlobR
|
|
|
246
279
|
}
|
|
247
280
|
// Assistant tool CALLS are intentionally NOT replayed as visible "[Tool Call]" text here.
|
|
248
281
|
} else if (message.role === "toolResult") {
|
|
249
|
-
// Native resume models already receive the paired MCP result through turns[].
|
|
250
|
-
//
|
|
251
|
-
// to echo that envelope as chat instead of continuing from the structured result.
|
|
282
|
+
// Native resume models already receive the paired MCP result through turns[]. External
|
|
283
|
+
// replay uses neutral text here so models do not echo protocol envelopes as chat.
|
|
252
284
|
if (!echoToolResultInRoot) continue;
|
|
253
|
-
|
|
254
|
-
// node_repl result is an error even when the runtime said isError=false).
|
|
255
|
-
const prefix = normalizedToolResult(message, contentToText(message.content)).isError ? "[Tool Error]" : "[Tool Result]";
|
|
256
|
-
const text = `${prefix}\n${toolResultToText(message)}`;
|
|
285
|
+
const text = externalToolResultToText(message);
|
|
257
286
|
entries.push(rootBlobCandidate(
|
|
258
287
|
toolResultRootPayload(text),
|
|
259
288
|
"toolResult",
|
|
@@ -538,6 +567,12 @@ function toolResultToText(message: OcxToolResultMessage): string {
|
|
|
538
567
|
].join("\n");
|
|
539
568
|
}
|
|
540
569
|
|
|
570
|
+
function externalToolResultToText(message: OcxToolResultMessage): string {
|
|
571
|
+
const normalized = normalizedToolResult(message, contentToText(message.content));
|
|
572
|
+
const label = normalized.isError ? "Tool error" : "Tool output";
|
|
573
|
+
return `${label} for ${namespacedToolName(message.toolNamespace, message.toolName)} (call_id: ${message.toolCallId}, is_error: ${normalized.isError}):\n${normalized.text}`;
|
|
574
|
+
}
|
|
575
|
+
|
|
541
576
|
/**
|
|
542
577
|
* Shared #1920 normalization entry: pure-text results only. Image-bearing or
|
|
543
578
|
* encrypted results pass through untouched (their content is not plain text).
|
|
@@ -721,12 +756,10 @@ function conversationTurns(
|
|
|
721
756
|
// #1920/#1866: this external-replay site bypasses toolResultToText, so it
|
|
722
757
|
// must consume the normalizer directly — cursor/grok-4.6 is the exact
|
|
723
758
|
// reported repro path for empty Computer Use results.
|
|
724
|
-
const normalized = normalizedToolResult(message, contentToText(message.content));
|
|
725
|
-
const prefix = normalized.isError ? "[Tool Error]" : "[Tool Result]";
|
|
726
759
|
current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
727
760
|
message: {
|
|
728
761
|
case: "assistantMessage",
|
|
729
|
-
value: create(AssistantMessageSchema, { text:
|
|
762
|
+
value: create(AssistantMessageSchema, { text: externalToolResultToText(message) }),
|
|
730
763
|
},
|
|
731
764
|
})), requestScope));
|
|
732
765
|
continue;
|
|
@@ -837,8 +870,10 @@ function buildPreparedCursorRunRequest(
|
|
|
837
870
|
? "userMessageAction"
|
|
838
871
|
: "resumeAction";
|
|
839
872
|
const actionText = externalToolContinuation
|
|
840
|
-
?
|
|
841
|
-
:
|
|
873
|
+
? (request.echoRetryContinuationText ?? externalToolContinuationText(request.rawMessages))
|
|
874
|
+
: request.echoRetryContinuationText
|
|
875
|
+
? `${text}\n\n[correction] ${request.echoRetryContinuationText}`
|
|
876
|
+
: text;
|
|
842
877
|
const action = create(ConversationActionSchema, {
|
|
843
878
|
action: actionCase === "userMessageAction"
|
|
844
879
|
? {
|
|
@@ -202,6 +202,13 @@ function normalizeCursorModelId(modelId: string, reasoning?: string): {
|
|
|
202
202
|
],
|
|
203
203
|
};
|
|
204
204
|
}
|
|
205
|
+
if (id === "composer-2.5") {
|
|
206
|
+
return {
|
|
207
|
+
...selection,
|
|
208
|
+
modelId: id,
|
|
209
|
+
requestedModelParameters: [{ id: "fast", value: "false" }],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
205
212
|
return { ...selection, modelId: suffix ? cursorRequestWireModelIdWithEffort(id, suffix) : id };
|
|
206
213
|
}
|
|
207
214
|
|
|
@@ -442,9 +449,10 @@ export function createCursorRequest(
|
|
|
442
449
|
rawMessages: parsed.context.messages,
|
|
443
450
|
...(parsed._compactionRequest === true || parsed._contextCompactionBoundary === true ? { contextUsageReset: true } : {}),
|
|
444
451
|
...(parsed._compactionRequest === true ? { contextUsageStoreCheckpoints: false } : {}),
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
...(parsed.options.
|
|
452
|
+
...(budget.tools.length ? { tools: budget.tools } : {}),
|
|
453
|
+
...(parsed.options.toolChoice ? { toolChoice: parsed.options.toolChoice } : {}),
|
|
454
|
+
...(parsed.options.textFormat ? { textFormat: parsed.options.textFormat } : {}),
|
|
455
|
+
...(parsed.options.parallelToolCalls !== undefined ? { parallelToolCalls: parsed.options.parallelToolCalls } : {}),
|
|
448
456
|
};
|
|
449
457
|
const resolved = resolveCursorCheckpoint(parsed, request, options);
|
|
450
458
|
if ("reason" in resolved) {
|