@bitkyc08/opencodex 2.8.0 → 2.9.0
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 +24 -0
- package/bin/ocx.mjs +32 -4
- package/gui/dist/assets/index-CHwf3tTD.css +1 -0
- package/gui/dist/assets/index-u5eFOv2y.js +67 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic-image-normalize.ts +114 -20
- package/src/adapters/anthropic.ts +126 -10
- package/src/adapters/azure.ts +3 -3
- package/src/adapters/base.ts +7 -3
- package/src/adapters/cursor/discovery.ts +14 -4
- package/src/adapters/cursor/effort-map.ts +18 -6
- package/src/adapters/cursor/framing.ts +102 -27
- package/src/adapters/cursor/kv-store.ts +30 -3
- package/src/adapters/cursor/live-models.ts +22 -2
- package/src/adapters/cursor/live-transport.ts +245 -49
- package/src/adapters/cursor/mcp-manager.ts +105 -8
- package/src/adapters/cursor/native-exec-mcp.ts +5 -3
- package/src/adapters/cursor/native-exec-shell.ts +296 -14
- package/src/adapters/cursor/native-exec.ts +381 -33
- package/src/adapters/cursor/protobuf-events.ts +28 -1
- package/src/adapters/cursor/protobuf-request.ts +71 -39
- package/src/adapters/cursor/request-builder.ts +2 -2
- package/src/adapters/cursor/transport.ts +2 -0
- package/src/adapters/cursor.ts +13 -2
- package/src/adapters/google-antigravity-replay.ts +184 -17
- package/src/adapters/google.ts +58 -8
- package/src/adapters/kiro-thinking.ts +23 -9
- package/src/adapters/kiro-tools.ts +49 -18
- package/src/adapters/kiro.ts +377 -133
- package/src/adapters/mimo-free.ts +36 -4
- package/src/adapters/openai-chat.ts +143 -17
- package/src/adapters/openai-responses.ts +130 -14
- package/src/adapters/run-turn-queue.ts +7 -1
- package/src/bridge.ts +466 -69
- package/src/chat/outbound.ts +144 -38
- package/src/claude/inbound-debug.ts +53 -8
- package/src/claude/outbound.ts +224 -38
- package/src/cli/agent-driven.ts +34 -1
- package/src/cli/catalog-prewarm.ts +5 -2
- package/src/cli/claude-desktop.ts +2 -2
- package/src/cli/doctor.ts +12 -0
- package/src/cli/export-command.ts +187 -0
- package/src/cli/help.ts +11 -0
- package/src/cli/index.ts +13 -3
- package/src/cli/init.ts +129 -102
- package/src/cli/opencode.ts +36 -151
- package/src/cli/star-prompt.ts +13 -4
- package/src/cli/status-oauth.ts +12 -2
- package/src/clients/config-export.ts +377 -0
- package/src/codex/account-runtime-state.ts +19 -1
- package/src/codex/account-store.ts +162 -82
- package/src/codex/auth-api.ts +467 -159
- package/src/codex/auth-context.ts +15 -2
- package/src/codex/catalog/aggregation.ts +15 -0
- package/src/codex/catalog/effort.ts +16 -6
- package/src/codex/catalog/metadata.ts +6 -0
- package/src/codex/catalog/parsing.ts +3 -1
- package/src/codex/catalog/provider-fetch.ts +29 -0
- package/src/codex/catalog/sync.ts +64 -7
- package/src/codex/catalog.ts +2 -2
- package/src/codex/inject.ts +5 -5
- package/src/codex/main-account-cache.ts +8 -1
- package/src/codex/model-cache.ts +81 -2
- package/src/codex/pool-rotation.ts +39 -0
- package/src/codex/project-config-warnings.ts +12 -1
- package/src/codex/quota.ts +35 -3
- package/src/codex/routing.ts +46 -1
- package/src/codex/shim.ts +10 -4
- package/src/codex/subagent-model-fallback.ts +12 -0
- package/src/codex/websocket-registry.ts +27 -0
- package/src/combos/failover.ts +31 -1
- package/src/combos/request.ts +9 -0
- package/src/combos/resolve.ts +60 -4
- package/src/combos/types.ts +12 -0
- package/src/config.ts +510 -55
- package/src/github/star-state.ts +13 -1
- package/src/images/fulfill.ts +39 -1
- package/src/images/loop.ts +52 -12
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +173 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bun-stream-caps.ts +31 -7
- package/src/lib/config-ownership.ts +33 -0
- package/src/lib/crash-guard.ts +65 -5
- package/src/lib/debug-log-buffer.ts +47 -6
- package/src/lib/destination-policy.ts +12 -1
- package/src/lib/errors.ts +3 -0
- package/src/lib/gcp-adc.ts +40 -2
- package/src/lib/injection-debug-log.ts +26 -2
- package/src/lib/provider-outbound.ts +3 -0
- package/src/lib/sidecar-tracker.ts +5 -2
- package/src/lib/sse-decoder.ts +257 -37
- package/src/lib/state-store-registrations.ts +109 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/translator-budget.ts +356 -0
- package/src/lib/windows-secret-acl.ts +33 -12
- package/src/lib/winsw.ts +14 -1
- package/src/oauth/anthropic-routing.ts +31 -7
- package/src/oauth/google-antigravity.ts +2 -1
- package/src/oauth/health.ts +30 -12
- package/src/oauth/index.ts +127 -23
- package/src/oauth/kiro-credentials.ts +72 -1
- package/src/oauth/kiro.ts +23 -4
- package/src/oauth/store.ts +165 -18
- package/src/oauth/token-guardian.ts +43 -4
- package/src/oauth/types.ts +2 -1
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/derive.ts +12 -0
- package/src/providers/free-directory.ts +4 -1
- package/src/providers/key-failover.ts +12 -0
- package/src/providers/openai-sidecar.ts +4 -1
- package/src/providers/quota.ts +68 -7
- package/src/providers/registry.ts +279 -3
- package/src/responses/parser.ts +5 -1
- package/src/responses/spill-store.ts +394 -0
- package/src/responses/state.ts +520 -102
- package/src/router.ts +18 -1
- package/src/server/adapter-resolve.ts +20 -3
- package/src/server/auth-cors.ts +121 -28
- package/src/server/chat-completions.ts +57 -12
- package/src/server/claude-messages.ts +85 -13
- package/src/server/index.ts +242 -100
- package/src/server/lifecycle.ts +155 -25
- package/src/server/management/agent-settings-routes.ts +79 -36
- package/src/server/management/api-key-usage.ts +167 -0
- package/src/server/management/body.ts +35 -0
- package/src/server/management/combo-routes.ts +5 -1
- package/src/server/management/config-routes.ts +42 -12
- package/src/server/management/logs-usage-routes.ts +41 -21
- package/src/server/management/model-routes.ts +188 -54
- package/src/server/management/oauth-account-routes.ts +115 -26
- package/src/server/management/provider-routes.ts +56 -6
- package/src/server/management/shared.ts +16 -3
- package/src/server/management/sidebar-routes.ts +50 -1
- package/src/server/management/system-restart.ts +13 -6
- package/src/server/management/system-routes.ts +15 -3
- package/src/server/management/usage-summary-cache.ts +86 -0
- package/src/server/management-api.ts +39 -5
- package/src/server/management-auth.ts +65 -14
- package/src/server/port-reclaim.ts +58 -12
- package/src/server/ports.ts +2 -0
- package/src/server/proxy-liveness.ts +60 -14
- package/src/server/relay-eager.ts +20 -4
- package/src/server/relay.ts +548 -154
- package/src/server/request-decompress.ts +51 -4
- package/src/server/request-log.ts +134 -15
- package/src/server/responses/collaboration.ts +15 -4
- package/src/server/responses/compact.ts +3 -0
- package/src/server/responses/core.ts +241 -66
- package/src/server/responses-image-gen-repair.ts +19 -5
- package/src/server/responses-item-id-repair.ts +23 -5
- package/src/server/sse-payload-rewrite.ts +71 -12
- package/src/server/startup-health-cache.ts +14 -1
- package/src/server/system-env.ts +8 -1
- package/src/server/windows-tcp-drop.ts +15 -5
- package/src/server/ws-bridge.ts +25 -0
- package/src/service.ts +179 -13
- package/src/storage/policy-job.ts +93 -23
- package/src/storage/policy-worker.ts +6 -0
- package/src/storage/restore-job.ts +62 -16
- package/src/storage/restore-worker.ts +6 -0
- package/src/storage/storage-mutation-coordinator.ts +36 -6
- package/src/storage/worker-lifecycle.ts +181 -47
- package/src/tray/windows.ts +97 -25
- package/src/types.ts +39 -6
- package/src/update/index.ts +24 -5
- package/src/update/job.ts +598 -73
- package/src/usage/log.ts +115 -17
- package/src/usage/summary.ts +67 -2
- package/src/vision/index.ts +112 -22
- package/src/web-search/loop.ts +38 -6
- package/src/web-search/progress-stream.ts +14 -3
- package/gui/dist/assets/index-BDjpkcRN.js +0 -67
- package/gui/dist/assets/index-BHsKRFh9.css +0 -1
|
@@ -6,7 +6,13 @@ import { namespacedToolName } from "../../types";
|
|
|
6
6
|
import type { CursorRunRequest } from "./types";
|
|
7
7
|
import { isCursorExternalWireModel } from "./discovery";
|
|
8
8
|
import { debugProviderDiagnostic } from "../../lib/debug";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
createCursorBlobRequestScope,
|
|
11
|
+
releaseCursorBlobRequestScope,
|
|
12
|
+
sealCursorBlobRequestScope,
|
|
13
|
+
storeCursorBlob,
|
|
14
|
+
type CursorBlobRequestScopeToken,
|
|
15
|
+
} from "./native-exec";
|
|
10
16
|
import { estimateTokens } from "../../lib/token-estimate";
|
|
11
17
|
import {
|
|
12
18
|
AgentClientMessageSchema,
|
|
@@ -82,8 +88,8 @@ function jsonBlob(value: unknown): { data: Uint8Array; serialized: string } {
|
|
|
82
88
|
return { data: encoder.encode(serialized), serialized };
|
|
83
89
|
}
|
|
84
90
|
|
|
85
|
-
type
|
|
86
|
-
|
|
91
|
+
type RootBlobCandidate = {
|
|
92
|
+
data: Uint8Array;
|
|
87
93
|
byteLength: number;
|
|
88
94
|
/**
|
|
89
95
|
* The exact JSON handed to storeCursorBlob(). Retained so a token estimate can read
|
|
@@ -97,14 +103,14 @@ type StoredRootBlob = {
|
|
|
97
103
|
text?: string;
|
|
98
104
|
};
|
|
99
105
|
|
|
100
|
-
function
|
|
106
|
+
function rootBlobCandidate(
|
|
101
107
|
value: unknown,
|
|
102
|
-
role:
|
|
108
|
+
role: RootBlobCandidate["role"],
|
|
103
109
|
opts?: { messageIndex?: number; text?: string },
|
|
104
|
-
):
|
|
110
|
+
): RootBlobCandidate {
|
|
105
111
|
const { data, serialized } = jsonBlob(value);
|
|
106
112
|
return {
|
|
107
|
-
|
|
113
|
+
data,
|
|
108
114
|
byteLength: data.byteLength,
|
|
109
115
|
serialized,
|
|
110
116
|
role,
|
|
@@ -113,7 +119,7 @@ function storedRootBlob(
|
|
|
113
119
|
};
|
|
114
120
|
}
|
|
115
121
|
|
|
116
|
-
function truncateToolResultBlob(entry:
|
|
122
|
+
function truncateToolResultBlob(entry: RootBlobCandidate, maxBytes: number): RootBlobCandidate | null {
|
|
117
123
|
if (entry.byteLength <= maxBytes) return entry;
|
|
118
124
|
if (entry.role !== "toolResult" || entry.text === undefined) return null;
|
|
119
125
|
const marker = "\n…[truncated for Cursor external replay budget]";
|
|
@@ -124,7 +130,7 @@ function truncateToolResultBlob(entry: StoredRootBlob, maxBytes: number): Stored
|
|
|
124
130
|
let end = keepBytes;
|
|
125
131
|
while (end > 0 && end < encoded.byteLength && (encoded[end]! & 0xc0) === 0x80) end -= 1;
|
|
126
132
|
const truncated = `${decoder.decode(encoded.subarray(0, end))}${marker}`;
|
|
127
|
-
const result =
|
|
133
|
+
const result = rootBlobCandidate(
|
|
128
134
|
{ role: "user", content: [{ type: "text", text: truncated }] },
|
|
129
135
|
"toolResult",
|
|
130
136
|
{ messageIndex: entry.messageIndex, text: truncated },
|
|
@@ -133,7 +139,7 @@ function truncateToolResultBlob(entry: StoredRootBlob, maxBytes: number): Stored
|
|
|
133
139
|
if (end === 0) break;
|
|
134
140
|
keepBytes = Math.max(0, end - (result.byteLength - maxBytes) - 16);
|
|
135
141
|
}
|
|
136
|
-
const markerOnly =
|
|
142
|
+
const markerOnly = rootBlobCandidate(
|
|
137
143
|
{ role: "user", content: [{ type: "text", text: marker.trimStart() }] },
|
|
138
144
|
"toolResult",
|
|
139
145
|
{ messageIndex: entry.messageIndex, text: marker.trimStart() },
|
|
@@ -141,7 +147,7 @@ function truncateToolResultBlob(entry: StoredRootBlob, maxBytes: number): Stored
|
|
|
141
147
|
return markerOnly.byteLength <= maxBytes ? markerOnly : null;
|
|
142
148
|
}
|
|
143
149
|
|
|
144
|
-
function systemPromptBlobs(request: CursorRunRequest):
|
|
150
|
+
function systemPromptBlobs(request: CursorRunRequest): RootBlobCandidate[] {
|
|
145
151
|
const prompts = request.system.length > 0 ? [...request.system] : ["You are a helpful assistant."];
|
|
146
152
|
if (cursorRequestHasShellAlias(request.tools)) prompts.push(CURSOR_SHELL_ALIAS_SYSTEM_NOTE);
|
|
147
153
|
const cursorToolGuidance = buildCursorToolGuidanceSystemNote(
|
|
@@ -149,7 +155,7 @@ function systemPromptBlobs(request: CursorRunRequest): StoredRootBlob[] {
|
|
|
149
155
|
request.toolChoice,
|
|
150
156
|
);
|
|
151
157
|
if (cursorToolGuidance) prompts.push(cursorToolGuidance);
|
|
152
|
-
return prompts.map(content =>
|
|
158
|
+
return prompts.map(content => rootBlobCandidate({ role: "system", content }, "system"));
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
function assistantRootText(
|
|
@@ -169,7 +175,7 @@ function assistantRootText(
|
|
|
169
175
|
// because it travels in the action. Tool results are rendered as user-role text with a marker, and
|
|
170
176
|
// each entry is a SHA-256 blob ID (Cursor fetches the bytes back via getBlobArgs). Mirrors the
|
|
171
177
|
// danger-pi reference buildRootPromptMessagesJson.
|
|
172
|
-
function rootPromptMessages(request: CursorRunRequest): {
|
|
178
|
+
function rootPromptMessages(request: CursorRunRequest, requestScope: CursorBlobRequestScopeToken): {
|
|
173
179
|
ids: Uint8Array[];
|
|
174
180
|
byteLength: number;
|
|
175
181
|
historyMessageStart: number;
|
|
@@ -181,7 +187,7 @@ function rootPromptMessages(request: CursorRunRequest): {
|
|
|
181
187
|
const messages = request.rawMessages;
|
|
182
188
|
if (!messages?.length) {
|
|
183
189
|
return {
|
|
184
|
-
ids: entries.map(entry => entry.
|
|
190
|
+
ids: entries.map(entry => storeCursorBlob(entry.data, requestScope)),
|
|
185
191
|
byteLength: entries.reduce((sum, entry) => sum + entry.byteLength, 0),
|
|
186
192
|
historyMessageStart: 0,
|
|
187
193
|
serialized: entries.map(entry => entry.serialized),
|
|
@@ -202,7 +208,7 @@ function rootPromptMessages(request: CursorRunRequest): {
|
|
|
202
208
|
// A bare string survives blob hydration but external workers reject the completed replay
|
|
203
209
|
// before tokenization (`usedTokens: 0`, then invalid_argument).
|
|
204
210
|
if (text.length > 0) {
|
|
205
|
-
entries.push(
|
|
211
|
+
entries.push(rootBlobCandidate({
|
|
206
212
|
role: "user",
|
|
207
213
|
content: [{ type: "text", text }],
|
|
208
214
|
}, "user", { messageIndex: i }));
|
|
@@ -212,7 +218,7 @@ function rootPromptMessages(request: CursorRunRequest): {
|
|
|
212
218
|
// Native Composer state can preserve it through ThinkingMessage/history structures.
|
|
213
219
|
const text = assistantRootText(message, !externalModel).trim();
|
|
214
220
|
if (text.length > 0) {
|
|
215
|
-
entries.push(
|
|
221
|
+
entries.push(rootBlobCandidate(
|
|
216
222
|
{ role: "assistant", content: [{ type: "text", text }] },
|
|
217
223
|
"assistant",
|
|
218
224
|
{ messageIndex: i },
|
|
@@ -222,7 +228,7 @@ function rootPromptMessages(request: CursorRunRequest): {
|
|
|
222
228
|
} else if (message.role === "toolResult") {
|
|
223
229
|
const prefix = message.isError ? "[Tool Error]" : "[Tool Result]";
|
|
224
230
|
const text = `${prefix}\n${toolResultToText(message)}`;
|
|
225
|
-
entries.push(
|
|
231
|
+
entries.push(rootBlobCandidate(
|
|
226
232
|
{ role: "user", content: [{ type: "text", text }] },
|
|
227
233
|
"toolResult",
|
|
228
234
|
{ messageIndex: i, text },
|
|
@@ -247,7 +253,7 @@ function rootPromptMessages(request: CursorRunRequest): {
|
|
|
247
253
|
const active = history
|
|
248
254
|
.slice(activeStart)
|
|
249
255
|
.map(entry => truncateToolResultBlob(entry, historyBudget))
|
|
250
|
-
.filter((entry): entry is
|
|
256
|
+
.filter((entry): entry is RootBlobCandidate => entry !== null);
|
|
251
257
|
let activeBytes = active.reduce((sum, entry) => sum + entry.byteLength, 0);
|
|
252
258
|
while (active.length > 1 && activeBytes > historyBudget) {
|
|
253
259
|
const dropped = active.shift();
|
|
@@ -265,7 +271,7 @@ function rootPromptMessages(request: CursorRunRequest): {
|
|
|
265
271
|
}
|
|
266
272
|
|
|
267
273
|
const prior = history.slice(0, activeStart);
|
|
268
|
-
const keptPrior:
|
|
274
|
+
const keptPrior: RootBlobCandidate[] = [];
|
|
269
275
|
let priorBytes = 0;
|
|
270
276
|
// Take complete turns from the end: a turn starts at a user/developer root entry.
|
|
271
277
|
let i = prior.length - 1;
|
|
@@ -298,7 +304,7 @@ function rootPromptMessages(request: CursorRunRequest): {
|
|
|
298
304
|
}
|
|
299
305
|
|
|
300
306
|
return {
|
|
301
|
-
ids: selected.map(entry => entry.
|
|
307
|
+
ids: selected.map(entry => storeCursorBlob(entry.data, requestScope)),
|
|
302
308
|
byteLength: selected.reduce((sum, entry) => sum + entry.byteLength, 0),
|
|
303
309
|
historyMessageStart,
|
|
304
310
|
serialized: selected.map(entry => entry.serialized),
|
|
@@ -345,7 +351,11 @@ function argBytes(value: unknown): Uint8Array {
|
|
|
345
351
|
}
|
|
346
352
|
}
|
|
347
353
|
|
|
348
|
-
function toolCallStep(
|
|
354
|
+
function toolCallStep(
|
|
355
|
+
part: Extract<OcxAssistantContentPart, { type: "toolCall" }>,
|
|
356
|
+
requestScope: CursorBlobRequestScopeToken,
|
|
357
|
+
result?: OcxToolResultMessage,
|
|
358
|
+
): Uint8Array {
|
|
349
359
|
const args: Record<string, Uint8Array> = {};
|
|
350
360
|
for (const [key, value] of Object.entries(part.arguments ?? {})) args[key] = argBytes(value);
|
|
351
361
|
const toolName = namespacedToolName(part.namespace, part.name);
|
|
@@ -368,7 +378,7 @@ function toolCallStep(part: Extract<OcxAssistantContentPart, { type: "toolCall"
|
|
|
368
378
|
},
|
|
369
379
|
}),
|
|
370
380
|
},
|
|
371
|
-
})));
|
|
381
|
+
})), requestScope);
|
|
372
382
|
}
|
|
373
383
|
|
|
374
384
|
function toolResultPart(message: OcxToolResultMessage) {
|
|
@@ -385,15 +395,15 @@ function toolResultPart(message: OcxToolResultMessage) {
|
|
|
385
395
|
});
|
|
386
396
|
}
|
|
387
397
|
|
|
388
|
-
function assistantStep(part: OcxAssistantContentPart): Uint8Array | undefined {
|
|
389
|
-
if (part.type === "toolCall") return toolCallStep(part);
|
|
398
|
+
function assistantStep(part: OcxAssistantContentPart, requestScope: CursorBlobRequestScopeToken): Uint8Array | undefined {
|
|
399
|
+
if (part.type === "toolCall") return toolCallStep(part, requestScope);
|
|
390
400
|
if (part.type === "thinking") {
|
|
391
401
|
return storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
392
402
|
message: {
|
|
393
403
|
case: "thinkingMessage",
|
|
394
404
|
value: create(ThinkingMessageSchema, { text: part.thinking }),
|
|
395
405
|
},
|
|
396
|
-
})));
|
|
406
|
+
})), requestScope);
|
|
397
407
|
}
|
|
398
408
|
if (part.text.length === 0) return undefined;
|
|
399
409
|
return storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
@@ -401,7 +411,7 @@ function assistantStep(part: OcxAssistantContentPart): Uint8Array | undefined {
|
|
|
401
411
|
case: "assistantMessage",
|
|
402
412
|
value: create(AssistantMessageSchema, { text: part.text }),
|
|
403
413
|
},
|
|
404
|
-
})));
|
|
414
|
+
})), requestScope);
|
|
405
415
|
}
|
|
406
416
|
|
|
407
417
|
function lastActionIndex(messages: readonly OcxMessage[] | undefined): number {
|
|
@@ -414,7 +424,11 @@ function lastActionIndex(messages: readonly OcxMessage[] | undefined): number {
|
|
|
414
424
|
return -1;
|
|
415
425
|
}
|
|
416
426
|
|
|
417
|
-
function conversationTurns(
|
|
427
|
+
function conversationTurns(
|
|
428
|
+
request: CursorRunRequest,
|
|
429
|
+
requestScope: CursorBlobRequestScopeToken,
|
|
430
|
+
historyMessageStart = 0,
|
|
431
|
+
): Uint8Array[] {
|
|
418
432
|
const messages = request.rawMessages;
|
|
419
433
|
if (!messages?.length) return [];
|
|
420
434
|
const end = lastActionIndex(messages);
|
|
@@ -426,13 +440,13 @@ function conversationTurns(request: CursorRunRequest, historyMessageStart = 0):
|
|
|
426
440
|
const pendingToolCalls = new Map<string, Extract<OcxAssistantContentPart, { type: "toolCall" }>>();
|
|
427
441
|
const flush = () => {
|
|
428
442
|
if (!current) return;
|
|
429
|
-
for (const part of pendingToolCalls.values()) current.steps.push(toolCallStep(part));
|
|
443
|
+
for (const part of pendingToolCalls.values()) current.steps.push(toolCallStep(part, requestScope));
|
|
430
444
|
turns.push(storeCursorBlob(toBinary(ConversationTurnStructureSchema, create(ConversationTurnStructureSchema, {
|
|
431
445
|
turn: {
|
|
432
446
|
case: "agentConversationTurn",
|
|
433
447
|
value: create(AgentConversationTurnStructureSchema, current),
|
|
434
448
|
},
|
|
435
|
-
}))));
|
|
449
|
+
})), requestScope));
|
|
436
450
|
current = undefined;
|
|
437
451
|
pendingToolCalls.clear();
|
|
438
452
|
};
|
|
@@ -451,7 +465,7 @@ function conversationTurns(request: CursorRunRequest, historyMessageStart = 0):
|
|
|
451
465
|
case: "assistantMessage",
|
|
452
466
|
value: create(AssistantMessageSchema, { text: part.text }),
|
|
453
467
|
},
|
|
454
|
-
}))));
|
|
468
|
+
})), requestScope));
|
|
455
469
|
}
|
|
456
470
|
continue;
|
|
457
471
|
}
|
|
@@ -459,7 +473,7 @@ function conversationTurns(request: CursorRunRequest, historyMessageStart = 0):
|
|
|
459
473
|
pendingToolCalls.set(part.id, part);
|
|
460
474
|
continue;
|
|
461
475
|
}
|
|
462
|
-
const step = assistantStep(part);
|
|
476
|
+
const step = assistantStep(part, requestScope);
|
|
463
477
|
if (step) current.steps.push(step);
|
|
464
478
|
}
|
|
465
479
|
continue;
|
|
@@ -473,12 +487,12 @@ function conversationTurns(request: CursorRunRequest, historyMessageStart = 0):
|
|
|
473
487
|
case: "assistantMessage",
|
|
474
488
|
value: create(AssistantMessageSchema, { text: `${prefix}\n${contentToText(message.content)}` }),
|
|
475
489
|
},
|
|
476
|
-
}))));
|
|
490
|
+
})), requestScope));
|
|
477
491
|
continue;
|
|
478
492
|
}
|
|
479
493
|
const priorCall = pendingToolCalls.get(message.toolCallId);
|
|
480
494
|
if (priorCall) {
|
|
481
|
-
current.steps.push(toolCallStep(priorCall, message));
|
|
495
|
+
current.steps.push(toolCallStep(priorCall, requestScope, message));
|
|
482
496
|
pendingToolCalls.delete(message.toolCallId);
|
|
483
497
|
} else {
|
|
484
498
|
current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
@@ -486,7 +500,7 @@ function conversationTurns(request: CursorRunRequest, historyMessageStart = 0):
|
|
|
486
500
|
case: "assistantMessage",
|
|
487
501
|
value: create(AssistantMessageSchema, { text: toolResultToText(message) }),
|
|
488
502
|
},
|
|
489
|
-
}))));
|
|
503
|
+
})), requestScope));
|
|
490
504
|
}
|
|
491
505
|
continue;
|
|
492
506
|
}
|
|
@@ -495,7 +509,7 @@ function conversationTurns(request: CursorRunRequest, historyMessageStart = 0):
|
|
|
495
509
|
userMessage: storeCursorBlob(toBinary(UserMessageSchema, create(UserMessageSchema, {
|
|
496
510
|
text: contentText(message),
|
|
497
511
|
messageId: crypto.randomUUID(),
|
|
498
|
-
}))),
|
|
512
|
+
})), requestScope),
|
|
499
513
|
steps: [],
|
|
500
514
|
};
|
|
501
515
|
}
|
|
@@ -537,6 +551,7 @@ function modelVisibleToolText(definition: McpToolDefinition): string {
|
|
|
537
551
|
|
|
538
552
|
export interface PreparedCursorRunRequest {
|
|
539
553
|
bytes: Uint8Array;
|
|
554
|
+
blobRequestScope: CursorBlobRequestScopeToken;
|
|
540
555
|
/** Only present when the caller asked for it; see prepareCursorRunRequest(). */
|
|
541
556
|
estimatedInputTokens?: number;
|
|
542
557
|
}
|
|
@@ -552,8 +567,9 @@ export interface PreparedCursorRunRequest {
|
|
|
552
567
|
* it honest: history the pruner dropped and tools the filter removed are already
|
|
553
568
|
* gone by this point.
|
|
554
569
|
*/
|
|
555
|
-
|
|
570
|
+
function buildPreparedCursorRunRequest(
|
|
556
571
|
request: CursorRunRequest,
|
|
572
|
+
requestScope: CursorBlobRequestScopeToken,
|
|
557
573
|
options?: { estimateInputTokens?: boolean },
|
|
558
574
|
): PreparedCursorRunRequest {
|
|
559
575
|
const rawText = activePromptText(request);
|
|
@@ -585,9 +601,9 @@ export function prepareCursorRunRequest(
|
|
|
585
601
|
}),
|
|
586
602
|
},
|
|
587
603
|
});
|
|
588
|
-
const rootPromptMessagesState = rootPromptMessages(request);
|
|
604
|
+
const rootPromptMessagesState = rootPromptMessages(request, requestScope);
|
|
589
605
|
const rootPromptMessageIds = rootPromptMessagesState.ids;
|
|
590
|
-
const turnIds = conversationTurns(request, rootPromptMessagesState.historyMessageStart);
|
|
606
|
+
const turnIds = conversationTurns(request, requestScope, rootPromptMessagesState.historyMessageStart);
|
|
591
607
|
// Hoisted out of the mcp_tools spread below so the estimate can read the same
|
|
592
608
|
// filtered definitions the wire carries. Both helpers are pure.
|
|
593
609
|
const visibleTools = cursorToolsForActivePrompt(request.tools, rawText, request.toolChoice);
|
|
@@ -661,7 +677,7 @@ export function prepareCursorRunRequest(
|
|
|
661
677
|
message: { case: "runRequest", value: runRequest },
|
|
662
678
|
});
|
|
663
679
|
const bytes = toBinary(AgentClientMessageSchema, message);
|
|
664
|
-
if (!options?.estimateInputTokens) return { bytes };
|
|
680
|
+
if (!options?.estimateInputTokens) return { bytes, blobRequestScope: requestScope };
|
|
665
681
|
|
|
666
682
|
// Same instances that produced `bytes`, so the estimate cannot count history or
|
|
667
683
|
// tools the payload dropped — the defect that blocked PR #376.
|
|
@@ -672,10 +688,26 @@ export function prepareCursorRunRequest(
|
|
|
672
688
|
];
|
|
673
689
|
return {
|
|
674
690
|
bytes,
|
|
691
|
+
blobRequestScope: requestScope,
|
|
675
692
|
estimatedInputTokens: estimateTokens(modelVisibleParts.join("\n"), request.modelId),
|
|
676
693
|
};
|
|
677
694
|
}
|
|
678
695
|
|
|
696
|
+
export function prepareCursorRunRequest(
|
|
697
|
+
request: CursorRunRequest,
|
|
698
|
+
options?: { estimateInputTokens?: boolean },
|
|
699
|
+
): PreparedCursorRunRequest {
|
|
700
|
+
const requestScope = createCursorBlobRequestScope();
|
|
701
|
+
try {
|
|
702
|
+
const prepared = buildPreparedCursorRunRequest(request, requestScope, options);
|
|
703
|
+
sealCursorBlobRequestScope(requestScope);
|
|
704
|
+
return prepared;
|
|
705
|
+
} catch (error) {
|
|
706
|
+
releaseCursorBlobRequestScope(requestScope);
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
679
711
|
/** Back-compat wrapper: callers that only need the wire bytes. */
|
|
680
712
|
export function encodeCursorRunRequest(request: CursorRunRequest): Uint8Array {
|
|
681
713
|
return prepareCursorRunRequest(request).bytes;
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
import { isAllowedToolChoice, namespacedToolName, toolChoiceAliases, type OcxTool, type OcxToolChoice } from "../../types";
|
|
11
11
|
import type { CursorRequestMessage, CursorRunRequest } from "./types";
|
|
12
12
|
import { cursorWireModelSelection, type CursorRoutingLevel } from "./discovery";
|
|
13
|
-
import { cursorEffortSuffix } from "./effort-map";
|
|
13
|
+
import { cursorEffortSuffix, cursorWireModelIdWithEffort } from "./effort-map";
|
|
14
14
|
import {
|
|
15
15
|
cursorMcpToolEncodedSize,
|
|
16
16
|
cursorMcpToolsEncodedSize,
|
|
@@ -127,7 +127,7 @@ function normalizeCursorModelId(modelId: string, reasoning?: string): { modelId:
|
|
|
127
127
|
const selection = cursorWireModelSelection(modelId);
|
|
128
128
|
const id = selection.modelId;
|
|
129
129
|
const suffix = cursorEffortSuffix(id, reasoning);
|
|
130
|
-
return { ...selection, modelId: suffix ?
|
|
130
|
+
return { ...selection, modelId: suffix ? cursorWireModelIdWithEffort(id, suffix) : id };
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
function contentPartToText(part: OcxContentPart | OcxAssistantContentPart): string | undefined {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { OcxProviderConfig } from "../../types";
|
|
2
2
|
import type { CursorClientMessage, CursorRunRequest, CursorServerMessage } from "./types";
|
|
3
|
+
import type { TranslatorBudget } from "../../lib/translator-budget";
|
|
3
4
|
|
|
4
5
|
export interface CursorTransport {
|
|
5
6
|
run(request: CursorRunRequest, signal?: AbortSignal): AsyncIterable<CursorServerMessage>;
|
|
@@ -15,6 +16,7 @@ export interface CursorTransport {
|
|
|
15
16
|
|
|
16
17
|
export interface CursorTransportFactoryInput {
|
|
17
18
|
provider: OcxProviderConfig;
|
|
19
|
+
translatorBudget: TranslatorBudget;
|
|
18
20
|
headers?: Headers;
|
|
19
21
|
/** Pre-first-frame deadline (dial + first server frame). Defaults to 30s when omitted. */
|
|
20
22
|
firstFrameTimeoutMs?: number;
|
package/src/adapters/cursor.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import type { AdapterEvent, OcxProviderConfig } from "../types";
|
|
3
3
|
import type { ProviderAdapter } from "./base";
|
|
4
|
+
import { isTranslatorBudgetExceededError } from "../lib/translator-budget";
|
|
4
5
|
import { cursorExecDeniedMessage, cursorRequestDeclaresFullAccess } from "./cursor/exec-policy";
|
|
5
6
|
import { isCursorBenignCancelError, isCursorInvalidArgumentError, safeCursorErrorMessage } from "./cursor/cursor-errors";
|
|
6
7
|
import { isCursorExternalWireModel } from "./cursor/discovery";
|
|
@@ -78,7 +79,7 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
78
79
|
}
|
|
79
80
|
try {
|
|
80
81
|
const makeTransport = deps.createTransport ?? createLiveCursorTransport;
|
|
81
|
-
const kv = deps.kv ?? createCursorKvStore();
|
|
82
|
+
const kv = deps.kv ?? createCursorKvStore({}, incoming.translatorBudget);
|
|
82
83
|
const rekeyContextUsage = deps.rekeyContextUsage ?? rekeyCursorContextUsage;
|
|
83
84
|
// Namespace thread→conversation derivation by the authenticated Cursor credential so
|
|
84
85
|
// shared-proxy tenants with different Cursor accounts cannot collide on a parent thread id.
|
|
@@ -118,6 +119,7 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
118
119
|
{
|
|
119
120
|
provider,
|
|
120
121
|
headers: incoming.headers,
|
|
122
|
+
translatorBudget: incoming.translatorBudget,
|
|
121
123
|
requestDeclaresFullAccess: cursorRequestDeclaresFullAccess(activeRequest),
|
|
122
124
|
},
|
|
123
125
|
activeRequest,
|
|
@@ -178,7 +180,16 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
178
180
|
} catch (err) {
|
|
179
181
|
if (isCursorBenignCancelError(err)) return;
|
|
180
182
|
const partialUsage = (err as { partialUsage?: import("../types").OcxUsage }).partialUsage;
|
|
181
|
-
emit({
|
|
183
|
+
emit({
|
|
184
|
+
type: "error",
|
|
185
|
+
message: isTranslatorBudgetExceededError(err)
|
|
186
|
+
? "upstream translation buffer exceeded the safe limit"
|
|
187
|
+
: safeCursorTransportError(err),
|
|
188
|
+
...(isTranslatorBudgetExceededError(err)
|
|
189
|
+
? { status: 502, errorType: "upstream_error", code: "translation_buffer_limit" }
|
|
190
|
+
: {}),
|
|
191
|
+
...(partialUsage ? { usage: partialUsage } : {}),
|
|
192
|
+
});
|
|
182
193
|
}
|
|
183
194
|
},
|
|
184
195
|
};
|