@bitkyc08/opencodex 2.79.0 → 2.81.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 +3 -3
- package/gui/dist/assets/App-7yhoys8a.js +54 -0
- package/gui/dist/assets/Tray-EwAOsP0S.js +1 -0
- package/gui/dist/assets/index-RsoaAxXY.js +94 -0
- package/gui/dist/assets/quota-summary-D2lN8tDm.js +1 -0
- package/gui/dist/index.html +1 -1
- package/gui/dist/provider-icons/claude-green.svg +1 -0
- package/package.json +3 -2
- package/src/adapters/anthropic-model-contract.ts +8 -2
- package/src/adapters/anthropic.ts +7 -2
- package/src/adapters/codebuddy/mcp-server.ts +2 -25
- package/src/adapters/codebuddy/tool-bridge.ts +1 -7
- package/src/adapters/coding-agent/mcp-server.ts +33 -0
- package/src/adapters/coding-agent/protocol.ts +122 -17
- package/src/adapters/coding-agent/tool-catalog.ts +75 -0
- package/src/adapters/coding-agent/turn.ts +101 -44
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +2 -0
- package/src/adapters/cursor/live-transport.ts +5 -3
- package/src/adapters/cursor/native-exec-fs.ts +12 -9
- package/src/adapters/cursor/native-exec.ts +13 -5
- package/src/adapters/cursor/protobuf-request.ts +13 -7
- package/src/adapters/cursor/request-builder.ts +23 -10
- package/src/adapters/cursor/tool-definitions.ts +9 -6
- package/src/adapters/cursor/tool-guidance.ts +29 -8
- package/src/adapters/cursor/tool-naming.ts +40 -7
- package/src/adapters/cursor/tool-wording.ts +46 -0
- package/src/adapters/cursor.ts +3 -0
- package/src/adapters/devin/live-models.ts +2 -0
- package/src/adapters/devin.ts +25 -11
- package/src/adapters/kiro-retry.ts +9 -4
- package/src/adapters/openai-chat/messages.ts +7 -4
- package/src/adapters/physical-send.ts +8 -5
- package/src/adapters/qoder/adapter.ts +41 -6
- package/src/adapters/qoder/scaffold-guard.ts +2 -3
- package/src/adapters/zed.ts +16 -1
- package/src/claude/inbound-cache-stabilize.ts +4 -9
- package/src/claude/inbound.ts +10 -0
- package/src/claude/intercept/model-bindings.ts +1 -0
- package/src/claude/intercept/picker-listener.ts +71 -4
- package/src/cli/account-anthropic-threshold.ts +14 -5
- package/src/cli/account-api.ts +3 -1
- package/src/cli/account-extended.ts +13 -9
- package/src/cli/account-policy-dto.ts +1 -1
- package/src/cli/account-policy.ts +13 -4
- package/src/cli/account.ts +3 -3
- package/src/cli/capabilities-accounts.ts +2 -2
- package/src/cli/capabilities-agents-routing.ts +24 -0
- package/src/cli/capabilities-integrations.ts +16 -0
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/dispatch.ts +10 -1
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +11 -5
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/integration-preview.ts +8 -1
- package/src/cli/integrations.ts +32 -3
- package/src/cli/message-args.ts +34 -0
- package/src/cli/message-command.ts +43 -0
- package/src/cli/message-runtime.ts +6 -0
- package/src/cli/observe.ts +3 -1
- package/src/cli/provider.ts +29 -6
- package/src/cli/registry.ts +24 -2
- package/src/clients/config-export/commandcode.ts +131 -0
- package/src/clients/config-export/contracts.ts +12 -0
- package/src/clients/config-export.ts +64 -1
- package/src/codex/auth-api/account-list.ts +11 -3
- package/src/codex/auth-api/main-account-probe.ts +25 -8
- package/src/codex/auth-api/pool-mode-gate.ts +33 -9
- package/src/codex/auth-api/pool-quota-probe.ts +15 -1
- package/src/codex/auth-api/routes.ts +1 -0
- package/src/codex/autostart-health.ts +3 -3
- package/src/codex/catalog/auto-review.ts +13 -6
- package/src/codex/catalog/build-entries.ts +15 -7
- package/src/codex/catalog/control-plane.ts +45 -0
- package/src/codex/catalog/effort.ts +11 -1
- package/src/codex/catalog/gather-capture.ts +5 -3
- package/src/codex/catalog/parsing.ts +2 -1
- package/src/codex/catalog/provider-models.ts +15 -9
- package/src/codex/control-plane-models.ts +6 -0
- package/src/codex/journal.ts +36 -5
- package/src/codex/shim-overlay.ts +9 -1
- package/src/combos/jev-decision-contract.ts +25 -2
- package/src/combos/jev.ts +6 -3
- package/src/combos/types.ts +1 -1
- package/src/config/diagnostics.ts +27 -16
- package/src/config/load-degrade.ts +34 -0
- package/src/config/schema/anthropic-account-pool.ts +143 -0
- package/src/config/schema/config-schema.ts +7 -1
- package/src/config/schema/leaf-validators.ts +4 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +347 -199
- package/src/generated/model-metadata.ts +5 -4
- package/src/images/loop.ts +2 -1
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/mutation-plan.ts +7 -0
- package/src/integrations/registry.ts +8 -0
- package/src/integrations/state.ts +9 -2
- package/src/integrations/writer.ts +8 -2
- package/src/lib/lab-live-route-production.ts +9 -1
- package/src/lib/request-execution-budget.ts +236 -58
- package/src/lib/spend-pool-alias-validation.ts +79 -0
- package/src/lib/spend-pool-continuity.ts +34 -0
- package/src/lib/spend-reservation-ledger.ts +412 -50
- package/src/lib/state-store-registrations.ts +20 -3
- package/src/lib/upstream-retry.ts +17 -5
- package/src/lib/workflow-budget.ts +53 -11
- package/src/messaging/budget.ts +45 -0
- package/src/messaging/discovery.ts +57 -0
- package/src/messaging/envelope.ts +49 -0
- package/src/messaging/input.ts +37 -0
- package/src/messaging/rpc.ts +196 -0
- package/src/messaging/send.ts +58 -0
- package/src/messaging/socket.ts +72 -0
- package/src/messaging/types.ts +29 -0
- package/src/oauth/anthropic-account-refusal.ts +51 -17
- package/src/oauth/anthropic-account-threshold.ts +8 -2
- package/src/oauth/anthropic-continuity.ts +15 -1
- package/src/oauth/anthropic-model-quota.ts +120 -48
- package/src/oauth/anthropic-model-routes.ts +11 -3
- package/src/oauth/anthropic-oauth-definitions.ts +98 -0
- package/src/oauth/anthropic-pool-config.ts +30 -0
- package/src/oauth/anthropic-rate-limit-policy.ts +64 -9
- package/src/oauth/anthropic-routing.ts +1202 -866
- package/src/oauth/anthropic-send-ownership.ts +41 -0
- package/src/oauth/anthropic.ts +4 -1
- package/src/oauth/generic-account-failover.ts +3 -1
- package/src/oauth/health.ts +8 -6
- package/src/oauth/index.ts +90 -173
- package/src/oauth/kiro.ts +22 -9
- package/src/oauth/login-cli.ts +2 -0
- package/src/oauth/model-discovery-auth.ts +43 -0
- package/src/oauth/pool-kernel.ts +11 -5
- package/src/oauth/pool-settings-capability.ts +5 -3
- package/src/oauth/store-anthropic-instance.ts +87 -0
- package/src/oauth/store.ts +67 -9
- package/src/oauth/token-guardian.ts +2 -0
- package/src/oauth/types.ts +2 -0
- package/src/protocols/plan-snapshot.ts +5 -0
- package/src/protocols/settings.ts +20 -5
- package/src/providers/anthropic-instance-id.ts +47 -0
- package/src/providers/anthropic-instance.ts +48 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +11 -1
- package/src/providers/derive.ts +2 -0
- package/src/providers/kiro-models.ts +3 -0
- package/src/providers/label.ts +1 -1
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/quota/account-cache.ts +37 -11
- package/src/providers/quota/anthropic-account-quota.ts +93 -0
- package/src/providers/quota/anthropic-cooldown-recovery.ts +222 -111
- package/src/providers/quota/report-cache.ts +24 -1
- package/src/providers/quota/vendor-probes-oauth.ts +46 -14
- package/src/providers/quota.ts +22 -32
- package/src/providers/registry/entries-core.ts +35 -30
- package/src/providers/registry/model-ids.ts +1 -0
- package/src/providers/registry/model-seeds.ts +7 -4
- package/src/providers/registry/types.ts +3 -0
- package/src/providers/registry.ts +3 -3
- package/src/responses/spill-store.ts +5 -1
- package/src/responses/state/metrics.ts +87 -0
- package/src/responses/state/snapshot-codec.ts +6 -0
- package/src/responses/state/spill-queue.ts +1 -0
- package/src/responses/state/unforced-store-false.ts +55 -0
- package/src/responses/state.ts +50 -66
- package/src/router.ts +28 -4
- package/src/routing/compatibility/assemble.ts +1 -1
- package/src/routing/quota.ts +7 -3
- package/src/server/auth-cors.ts +5 -0
- package/src/server/chat-completions.ts +6 -1
- package/src/server/chat-native.ts +45 -25
- package/src/server/claude-messages.ts +16 -2
- package/src/server/gui-static.ts +38 -4
- package/src/server/index/spend-ledger-lifecycle.ts +10 -4
- package/src/server/index.ts +16 -16
- package/src/server/inference/context.ts +1 -1
- package/src/server/lifecycle.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +30 -6
- package/src/server/management/anthropic-account-threshold.ts +12 -9
- package/src/server/management/anthropic-pool-settings.ts +72 -0
- package/src/server/management/anthropic-reset-grant-routes.ts +98 -39
- package/src/server/management/config-routes.ts +27 -7
- package/src/server/management/decision-routes.ts +2 -1
- package/src/server/management/integration-routes.ts +25 -2
- package/src/server/management/oauth-account-routes.ts +79 -98
- package/src/server/management/provider-instance-ownership.ts +44 -0
- package/src/server/management/provider-routes.ts +21 -28
- package/src/server/management/route-registry.ts +3 -1
- package/src/server/management/routing-profile-routes.ts +4 -4
- package/src/server/management/system-restart.ts +44 -14
- package/src/server/management/system-routes.ts +24 -2
- package/src/server/management/vision-sidecar-options.ts +9 -6
- package/src/server/management/web-search-sidecar-options.ts +51 -2
- package/src/server/management-auth.ts +6 -0
- package/src/server/messages-native-eligibility.ts +3 -2
- package/src/server/messages-native-oauth.ts +86 -41
- package/src/server/messages-native-scope.ts +4 -1
- package/src/server/messages-native-selector.ts +39 -0
- package/src/server/messages-native.ts +84 -25
- package/src/server/request-log.ts +5 -0
- package/src/server/responses/adapter-continuation.ts +29 -17
- package/src/server/responses/adapter-dispatch.ts +47 -17
- package/src/server/responses/antigravity-validation-refusal.ts +20 -6
- package/src/server/responses/combo-requested-effort.ts +14 -0
- package/src/server/responses/compact.ts +25 -8
- package/src/server/responses/compaction-recovery.ts +1 -1
- package/src/server/responses/core-combo-native.ts +4 -1
- package/src/server/responses/core-combo.ts +21 -18
- package/src/server/responses/core-normalize.ts +1 -0
- package/src/server/responses/core-options.ts +3 -1
- package/src/server/responses/core-replay.ts +5 -0
- package/src/server/responses/passthrough-delivery.ts +7 -3
- package/src/server/responses/passthrough-dispatch.ts +21 -8
- package/src/server/responses/request-prepare.ts +37 -5
- package/src/server/responses/request-send-budget.ts +74 -37
- package/src/server/responses/request-spend.ts +325 -33
- package/src/server/responses/request-transport.ts +132 -52
- package/src/server/responses/run-turn-execution.ts +11 -3
- package/src/server/responses/sidecar-execution.ts +96 -14
- package/src/server/responses-undeclared-tool-guard.ts +5 -1
- package/src/server/workflow-refusal.ts +30 -0
- package/src/service/desktop-startup.ts +100 -3
- package/src/service/ownership-mutation-lease.d.mts +1 -1
- package/src/service/ownership-mutation-lease.mjs +5 -7
- package/src/sidecar/anthropic-binding.ts +79 -0
- package/src/sidecar/auth.ts +76 -2
- package/src/types/anthropic-account-pool.ts +33 -0
- package/src/types/config.ts +12 -28
- package/src/types/provider.ts +5 -0
- package/src/types/tools.ts +7 -0
- package/src/types.ts +4 -0
- package/src/usage/cost.ts +3 -2
- package/src/usage/expected-prices.ts +66 -14
- package/src/vision/anthropic-describe.ts +16 -3
- package/src/vision/index.ts +3 -0
- package/src/vision/plan.ts +19 -6
- package/src/web-search/alpha-search.ts +7 -1
- package/src/web-search/anthropic-executor.ts +16 -4
- package/src/web-search/header-deadline.ts +67 -0
- package/src/web-search/index.ts +7 -3
- package/src/web-search/loop.ts +32 -10
- package/src/web-search/passthrough-bridge.ts +4 -1
- package/src/web-search/sidecar-providers.ts +4 -3
- package/gui/dist/assets/App-BOUa-ek3.js +0 -54
- package/gui/dist/assets/Tray-DUtzB4zF.js +0 -1
- package/gui/dist/assets/index-DwHNDF4s.js +0 -94
- package/gui/dist/assets/quota-summary-BuL4WIBo.js +0 -1
|
@@ -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, OcxTool, OcxToolResultMessage } from "../../types";
|
|
5
5
|
import { namespacedToolName } from "../../types";
|
|
6
6
|
import type { CursorRunRequest } from "./types";
|
|
7
7
|
import { cursorStructuredOutputInstructions } from "./structured-output";
|
|
@@ -224,6 +224,7 @@ function systemPromptBlobs(request: CursorRunRequest): RootBlobCandidate[] {
|
|
|
224
224
|
const cursorToolGuidance = buildCursorToolGuidanceSystemNote(
|
|
225
225
|
cursorToolsForActivePrompt(request.tools, activePromptText(request), request.toolChoice),
|
|
226
226
|
request.toolChoice,
|
|
227
|
+
request.modelId,
|
|
227
228
|
);
|
|
228
229
|
if (cursorToolGuidance) prompts.push(cursorToolGuidance);
|
|
229
230
|
return prompts.map(content => rootBlobCandidate({ role: "system", content }, "system"));
|
|
@@ -1299,12 +1300,13 @@ function toolCallStep(
|
|
|
1299
1300
|
requestScope: CursorBlobRequestScopeToken,
|
|
1300
1301
|
result?: OcxToolResultMessage,
|
|
1301
1302
|
codeMode = false,
|
|
1303
|
+
catalog?: readonly OcxTool[],
|
|
1302
1304
|
): Uint8Array {
|
|
1303
1305
|
const args: Record<string, Uint8Array> = {};
|
|
1304
1306
|
for (const [key, value] of Object.entries(part.arguments ?? {})) args[key] = argBytes(value);
|
|
1305
1307
|
// Replay the same provider-isolated identity advertised in this request. Returned calls are
|
|
1306
1308
|
// restored to the client name, so transcript parts carry the client name again on the next turn.
|
|
1307
|
-
const toolName = cursorToolWireName(part);
|
|
1309
|
+
const toolName = cursorToolWireName(part, catalog);
|
|
1308
1310
|
const decodedResult = result ? decodeResultParts(result) : undefined;
|
|
1309
1311
|
const serialize = (maxImages: number): Uint8Array => toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
1310
1312
|
message: {
|
|
@@ -1357,8 +1359,12 @@ function toolResultPart(message: OcxToolResultMessage, codeMode: boolean, decode
|
|
|
1357
1359
|
});
|
|
1358
1360
|
}
|
|
1359
1361
|
|
|
1360
|
-
function assistantStep(
|
|
1361
|
-
|
|
1362
|
+
function assistantStep(
|
|
1363
|
+
part: OcxAssistantContentPart,
|
|
1364
|
+
requestScope: CursorBlobRequestScopeToken,
|
|
1365
|
+
catalog?: readonly OcxTool[],
|
|
1366
|
+
): Uint8Array | undefined {
|
|
1367
|
+
if (part.type === "toolCall") return toolCallStep(part, requestScope, undefined, false, catalog);
|
|
1362
1368
|
if (part.type === "thinking") {
|
|
1363
1369
|
return storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
1364
1370
|
message: {
|
|
@@ -1409,7 +1415,7 @@ function conversationTurns(
|
|
|
1409
1415
|
const flush = () => {
|
|
1410
1416
|
if (!current) return;
|
|
1411
1417
|
for (const part of pendingToolCalls.values()) {
|
|
1412
|
-
current.steps.push(toolCallStep(part, requestScope, missingToolResultFor(part), codeMode));
|
|
1418
|
+
current.steps.push(toolCallStep(part, requestScope, missingToolResultFor(part), codeMode, request.tools));
|
|
1413
1419
|
}
|
|
1414
1420
|
turns.push(storeCursorBlob(toBinary(ConversationTurnStructureSchema, create(ConversationTurnStructureSchema, {
|
|
1415
1421
|
turn: {
|
|
@@ -1451,7 +1457,7 @@ function conversationTurns(
|
|
|
1451
1457
|
pendingToolCalls.set(part.id, part);
|
|
1452
1458
|
continue;
|
|
1453
1459
|
}
|
|
1454
|
-
const step = assistantStep(part, requestScope);
|
|
1460
|
+
const step = assistantStep(part, requestScope, request.tools);
|
|
1455
1461
|
if (step) current.steps.push(step);
|
|
1456
1462
|
}
|
|
1457
1463
|
continue;
|
|
@@ -1478,7 +1484,7 @@ function conversationTurns(
|
|
|
1478
1484
|
}
|
|
1479
1485
|
const priorCall = pendingToolCalls.get(message.toolCallId);
|
|
1480
1486
|
if (priorCall) {
|
|
1481
|
-
current.steps.push(toolCallStep(priorCall, requestScope, message, codeMode));
|
|
1487
|
+
current.steps.push(toolCallStep(priorCall, requestScope, message, codeMode, request.tools));
|
|
1482
1488
|
pendingToolCalls.delete(message.toolCallId);
|
|
1483
1489
|
} else {
|
|
1484
1490
|
current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
@@ -102,8 +102,8 @@ export function applyCursorToolBudget(
|
|
|
102
102
|
const tryKeep = (tool: OcxTool): boolean => {
|
|
103
103
|
if (keptSet.has(tool) || kept.length >= CURSOR_TOOL_COUNT_LIMIT) return keptSet.has(tool);
|
|
104
104
|
// Repeated protobuf message fields serialize as concatenated tag/length/value entries,
|
|
105
|
-
// so each
|
|
106
|
-
const candidateBytes = cursorMcpToolEncodedSize(tool, toolChoice);
|
|
105
|
+
// so each wrapper uses the same catalog-dependent names and choice as registration.
|
|
106
|
+
const candidateBytes = cursorMcpToolEncodedSize(tool, toolChoice, eligible);
|
|
107
107
|
if (keptBytes + candidateBytes > CURSOR_TOOL_BYTES_LIMIT) return false;
|
|
108
108
|
kept.push(tool);
|
|
109
109
|
keptSet.add(tool);
|
|
@@ -129,7 +129,7 @@ export function applyCursorToolBudget(
|
|
|
129
129
|
if (!occupant || isCursorExecutionPathTool(occupant)) continue;
|
|
130
130
|
kept.splice(i, 1);
|
|
131
131
|
keptSet.delete(occupant);
|
|
132
|
-
keptBytes -= cursorMcpToolEncodedSize(occupant, toolChoice);
|
|
132
|
+
keptBytes -= cursorMcpToolEncodedSize(occupant, toolChoice, eligible);
|
|
133
133
|
if (kept.length < CURSOR_TOOL_COUNT_LIMIT && keptBytes + needBytes <= CURSOR_TOOL_BYTES_LIMIT) {
|
|
134
134
|
return;
|
|
135
135
|
}
|
|
@@ -141,7 +141,7 @@ export function applyCursorToolBudget(
|
|
|
141
141
|
// earlier same-priority pins; evict wait/patch/filler rather than ship wait-only.
|
|
142
142
|
for (const tool of eligible) {
|
|
143
143
|
if (!isCursorExecutionPathTool(tool) || keptSet.has(tool)) continue;
|
|
144
|
-
const need = cursorMcpToolEncodedSize(tool, toolChoice);
|
|
144
|
+
const need = cursorMcpToolEncodedSize(tool, toolChoice, eligible);
|
|
145
145
|
if (need > CURSOR_TOOL_BYTES_LIMIT) continue;
|
|
146
146
|
evictNonExecutionPath(need);
|
|
147
147
|
tryKeep(tool);
|
|
@@ -157,7 +157,7 @@ export function applyCursorToolBudget(
|
|
|
157
157
|
keptSet.delete(tool);
|
|
158
158
|
const index = kept.indexOf(tool);
|
|
159
159
|
if (index >= 0) kept.splice(index, 1);
|
|
160
|
-
keptBytes -= cursorMcpToolEncodedSize(tool, toolChoice);
|
|
160
|
+
keptBytes -= cursorMcpToolEncodedSize(tool, toolChoice, eligible);
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
|
|
@@ -171,8 +171,8 @@ export function applyCursorToolBudget(
|
|
|
171
171
|
|
|
172
172
|
function catalogLimitNote(kept: readonly OcxTool[], omitted: readonly OcxTool[]): string | undefined {
|
|
173
173
|
if (omitted.length === 0) return undefined;
|
|
174
|
-
const recoverable = kept.some(tool => tool.toolSearch || cursorToolWireName(tool) === "tool_search");
|
|
175
|
-
const names = omitted.slice(0, 12).map(cursorToolWireName);
|
|
174
|
+
const recoverable = kept.some(tool => tool.toolSearch || cursorToolWireName(tool, kept) === "tool_search");
|
|
175
|
+
const names = omitted.slice(0, 12).map(tool => cursorToolWireName(tool, kept));
|
|
176
176
|
const remainder = omitted.length - names.length;
|
|
177
177
|
const omittedSummary = `${names.join(", ")}${remainder > 0 ? `, and ${remainder} more` : ""}`;
|
|
178
178
|
return recoverable
|
|
@@ -509,8 +509,21 @@ export function createCursorRequest(
|
|
|
509
509
|
): CursorRunRequest {
|
|
510
510
|
const messages = cursorRequestMessagesFromRaw(parsed.context.messages);
|
|
511
511
|
const activeText = [...messages].reverse().find(message => message.role === "user" || message.role === "developer")?.content ?? "";
|
|
512
|
-
const
|
|
513
|
-
const
|
|
512
|
+
const catalog = parsed.context.tools ?? [];
|
|
513
|
+
const originalChoice = parsed.options.toolChoice;
|
|
514
|
+
// Resolve accepted bare wire aliases before filtering can remove their shell-bridge context.
|
|
515
|
+
// Keep the original selection so a semantic name cannot widen to a namespaced sibling.
|
|
516
|
+
const selectedTools = catalog.filter(tool => cursorToolAllowedByChoice(tool, originalChoice, catalog));
|
|
517
|
+
const semanticChoiceName = (name: string): string => selectedTools.find(tool =>
|
|
518
|
+
!tool.namespace && cursorToolWireName(tool, catalog) === name
|
|
519
|
+
&& cursorToolAllowedByChoice(tool, { name }, catalog))?.name ?? name;
|
|
520
|
+
const toolChoice = originalChoice && typeof originalChoice === "object"
|
|
521
|
+
? "name" in originalChoice
|
|
522
|
+
? { ...originalChoice, name: semanticChoiceName(originalChoice.name) }
|
|
523
|
+
: { ...originalChoice, allowedTools: originalChoice.allowedTools.map(semanticChoiceName) }
|
|
524
|
+
: originalChoice;
|
|
525
|
+
const visibleTools = cursorToolsForActivePrompt(selectedTools, activeText, toolChoice);
|
|
526
|
+
const budget = applyCursorToolBudget(visibleTools, toolChoice);
|
|
514
527
|
const limitNote = catalogLimitNote(budget.tools, budget.omitted);
|
|
515
528
|
const model = normalizeCursorModelId(
|
|
516
529
|
parsed.modelId,
|
|
@@ -536,7 +549,7 @@ export function createCursorRequest(
|
|
|
536
549
|
...(budget.tools.length === 0 && !cursorClientThreadOwner(parsed)
|
|
537
550
|
? { suppressDefaultCursorToolCatalog: true }
|
|
538
551
|
: {}),
|
|
539
|
-
...(
|
|
552
|
+
...(toolChoice ? { toolChoice } : {}),
|
|
540
553
|
...(parsed.options.parallelToolCalls !== undefined ? { parallelToolCalls: parsed.options.parallelToolCalls } : {}),
|
|
541
554
|
};
|
|
542
555
|
const resolved = resolveCursorCheckpoint(parsed, request, options);
|
|
@@ -4,7 +4,7 @@ import type { OcxRequestOptions, OcxTool } from "../../types";
|
|
|
4
4
|
import { McpToolDefinitionSchema, McpToolsSchema, type McpToolDefinition } from "./gen/agent_pb";
|
|
5
5
|
import { CURSOR_EDIT_FILE_TOOL, CURSOR_MULTI_EDIT_TOOL, cursorRequestAdvertisesApplyPatch, cursorToolAllowedByChoice, cursorToolWireName, OCX_RESPONSES_TOOL_PROVIDER } from "./tool-naming";
|
|
6
6
|
import { CURSOR_EDIT_FILE_INPUT_SCHEMA, CURSOR_MULTI_EDIT_INPUT_SCHEMA, cursorToolInputSchema } from "./tool-schemas";
|
|
7
|
-
export { OCX_RESPONSES_TOOL_PROVIDER, CODEX_EXEC_COMMAND_TOOL, CODEX_SHELL_COMMAND_TOOL, CODEX_UNIFIED_EXEC_TOOL, CODEX_WAIT_TOOL, CODEX_APPLY_PATCH_TOOL, CODEX_TOOL_SEARCH_TOOL, CURSOR_EDIT_FILE_TOOL, CURSOR_MULTI_EDIT_TOOL, CURSOR_STRUCTURED_EDIT_TOOLS, CURSOR_EXEC_COMMAND_TOOL, CODEX_SHELL_BRIDGE_TOOL_NAMES, isCodexShellBridgeToolName, resolveShellBridgeAliasKey, cursorToolChoiceAliases, isBareCodexShellBridgeTool, isCursorExecutionPathTool, isCursorWaitTool, isCursorCodeModeExecTool, cursorRequestUsesCodeMode, cursorRequestHasShellAlias, cursorRequestAdvertisesApplyPatch, isCursorStructuredEditToolName, isCursorSyntheticStructuredEditTool, cursorToolWireName, normalizeCursorWireName, normalizeCursorTextToolMarkers, responsesToolNameFromCursorWire, cursorToolAllowedByChoice } from "./tool-naming";
|
|
7
|
+
export { OCX_RESPONSES_TOOL_PROVIDER, CODEX_EXEC_COMMAND_TOOL, CODEX_SHELL_COMMAND_TOOL, CODEX_UNIFIED_EXEC_TOOL, CODEX_WAIT_TOOL, CODEX_APPLY_PATCH_TOOL, CODEX_TOOL_SEARCH_TOOL, CURSOR_EDIT_FILE_TOOL, CURSOR_MULTI_EDIT_TOOL, CURSOR_STRUCTURED_EDIT_TOOLS, CURSOR_EXEC_COMMAND_TOOL, CODEX_SHELL_BRIDGE_TOOL_NAMES, isCodexShellBridgeToolName, resolveShellBridgeAliasKey, cursorToolChoiceAliases, isBareCodexShellBridgeTool, isCursorExecutionPathTool, isCursorWaitTool, isCursorCodeModeExecTool, cursorRequestUsesCodeMode, cursorRequestHasShellAlias, cursorRequestAdvertisesApplyPatch, isCursorStructuredEditToolName, isCursorSyntheticStructuredEditTool, CLAUDE_CLIENT_BARE_TOOL_NAMES, isClaudeClientBareToolName, isCursorBareClientToolWireAliased, cursorToolWireName, normalizeCursorWireName, normalizeCursorTextToolMarkers, responsesToolNameFromCursorWire, cursorToolAllowedByChoice } from "./tool-naming";
|
|
8
8
|
export { CURSOR_EXEC_COMMAND_INPUT_SCHEMA, CURSOR_FREEFORM_INPUT_SCHEMA, CURSOR_EDIT_FILE_INPUT_SCHEMA, CURSOR_MULTI_EDIT_INPUT_SCHEMA, CODEX_SHELL_BRIDGE_ARG_NORMALIZE_SCHEMA, cursorToolInputSchema, cursorToolArgNormalizeSchema, shellBridgeRequiredCommandKeys, defaultShellBridgeArgNormalizeSchema, cursorShellBridgeDropError, nonEmptyShellBridgeCommandFromArgs, cursorShellBridgeArgsValid } from "./tool-schemas";
|
|
9
9
|
export { CURSOR_SHELL_ALIAS_SYSTEM_NOTE, CURSOR_GENERIC_TOOL_USE_USER_HINT, isGenericToolUseCountDemoPrompt, requestedCursorToolUseCount, shouldAppendCursorGenericToolUseHint, appendCursorGenericToolUseHint, shouldUseNativeExecOnlyForGenericToolUse, cursorToolsForActivePrompt, buildCursorToolGuidanceSystemNote } from "./tool-guidance";
|
|
10
10
|
|
|
@@ -80,10 +80,11 @@ export function encodeCursorInputSchema(schema: unknown): Uint8Array {
|
|
|
80
80
|
export function buildCursorToolDefinitions(
|
|
81
81
|
tools: readonly OcxTool[] | undefined,
|
|
82
82
|
toolChoice?: OcxRequestOptions["toolChoice"],
|
|
83
|
+
catalog: readonly OcxTool[] = tools ?? [],
|
|
83
84
|
): McpToolDefinition[] {
|
|
84
85
|
if (!tools?.length) return [];
|
|
85
|
-
return tools.filter(tool => cursorToolAllowedByChoice(tool, toolChoice,
|
|
86
|
-
const wireName = cursorToolWireName(tool);
|
|
86
|
+
return tools.filter(tool => cursorToolAllowedByChoice(tool, toolChoice, catalog)).map(tool => {
|
|
87
|
+
const wireName = cursorToolWireName(tool, catalog);
|
|
87
88
|
return create(McpToolDefinitionSchema, {
|
|
88
89
|
name: wireName,
|
|
89
90
|
toolName: wireName,
|
|
@@ -98,15 +99,17 @@ export function buildCursorToolDefinitions(
|
|
|
98
99
|
export function cursorMcpToolsEncodedSize(
|
|
99
100
|
tools: readonly OcxTool[] | undefined,
|
|
100
101
|
toolChoice?: OcxRequestOptions["toolChoice"],
|
|
102
|
+
catalog: readonly OcxTool[] = tools ?? [],
|
|
101
103
|
): number {
|
|
102
|
-
const definitions = buildCursorToolDefinitions(tools, toolChoice);
|
|
104
|
+
const definitions = buildCursorToolDefinitions(tools, toolChoice, catalog);
|
|
103
105
|
return toBinary(McpToolsSchema, create(McpToolsSchema, { mcpTools: definitions })).byteLength;
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
/** Exact additive contribution of one
|
|
108
|
+
/** Exact additive contribution of one entry, using the registration catalog for wire naming and choice. */
|
|
107
109
|
export function cursorMcpToolEncodedSize(
|
|
108
110
|
tool: OcxTool,
|
|
109
111
|
toolChoice?: OcxRequestOptions["toolChoice"],
|
|
112
|
+
catalog: readonly OcxTool[] = [tool],
|
|
110
113
|
): number {
|
|
111
|
-
return cursorMcpToolsEncodedSize([tool], toolChoice);
|
|
114
|
+
return cursorMcpToolsEncodedSize([tool], toolChoice, catalog);
|
|
112
115
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { OcxRequestOptions, OcxTool } from "../../types";
|
|
2
|
+
import { CURSOR_TOOL_CALL_CONTINUATION, cursorUsesPlainToolWording } from "./tool-wording";
|
|
2
3
|
import { CODE_MODE_HOST_CONTRACT_SENTENCE, CODE_MODE_RESULT_ECHO_SENTENCE } from "../exec-tool-result-normalize";
|
|
3
|
-
import { CODEX_SHELL_BRIDGE_TOOL_NAMES, CODEX_TOOL_SEARCH_TOOL, CODEX_UNIFIED_EXEC_TOOL, clientSemanticToolNameFromCursorWire, cursorRequestAdvertisesApplyPatch, cursorRequestHasExecutionPath, cursorRequestHasShellAlias, cursorRequestUsesCodeMode, cursorToolAllowedByChoice, cursorToolWireName, isCodexShellBridgeToolName, isCursorExecutionPathTool, isCursorStructuredEditToolName } from "./tool-naming";
|
|
4
|
+
import { CODEX_SHELL_BRIDGE_TOOL_NAMES, CODEX_TOOL_SEARCH_TOOL, CODEX_UNIFIED_EXEC_TOOL, clientSemanticToolNameFromCursorWire, cursorRequestAdvertisesApplyPatch, cursorRequestHasExecutionPath, cursorRequestHasShellAlias, cursorRequestUsesCodeMode, cursorToolAllowedByChoice, cursorToolWireName, isBareCodexShellBridgeTool, isClaudeClientBareToolName, isCodexShellBridgeToolName, isCursorExecutionPathTool, isCursorStructuredEditToolName } from "./tool-naming";
|
|
4
5
|
|
|
5
6
|
export const CURSOR_SHELL_ALIAS_SYSTEM_NOTE =
|
|
6
7
|
'Shell commands use the Codex shell bridge tool shown in this turn\'s catalog (`shell_command` or `exec_command`) with JSON arguments like {"cmd":"..."}. The long `mcp_opencodex-responses_*` display name is the same tool. Prefer it over Cursor-native Shell.';
|
|
@@ -129,7 +130,15 @@ function advertisedCoversNeighbor(wireNames: readonly string[], neighbor: (typeo
|
|
|
129
130
|
return NEIGHBOR_AGENT_TOOL_ALIASES[neighbor].some(alias => advertised.has(alias.toLowerCase()));
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
function unavailableNeighborAgentToolNames(
|
|
133
|
+
function unavailableNeighborAgentToolNames(
|
|
134
|
+
wireNames: readonly string[],
|
|
135
|
+
catalog?: readonly Pick<OcxTool, "namespace" | "name">[],
|
|
136
|
+
): string[] {
|
|
137
|
+
if (
|
|
138
|
+
catalog
|
|
139
|
+
&& !catalog.some(isBareCodexShellBridgeTool)
|
|
140
|
+
&& catalog.some(tool => !tool.namespace && isClaudeClientBareToolName(tool.name))
|
|
141
|
+
) return [];
|
|
133
142
|
return NEIGHBOR_AGENT_TOOL_NAMES.filter(name => !advertisedCoversNeighbor(wireNames, name));
|
|
134
143
|
}
|
|
135
144
|
|
|
@@ -144,12 +153,14 @@ function discoveryToolLabel(wireNames: readonly string[]): string | undefined {
|
|
|
144
153
|
export function buildCursorToolGuidanceSystemNote(
|
|
145
154
|
tools: readonly Pick<OcxTool, "namespace" | "name" | "freeform">[] | undefined,
|
|
146
155
|
toolChoice?: OcxRequestOptions["toolChoice"],
|
|
156
|
+
modelId?: string,
|
|
147
157
|
): string | undefined {
|
|
148
158
|
if (!tools?.length) return undefined;
|
|
159
|
+
const plainToolWording = cursorUsesPlainToolWording(modelId);
|
|
149
160
|
const wireNames = [...new Set(
|
|
150
161
|
tools
|
|
151
162
|
.filter(tool => cursorToolAllowedByChoice(tool, toolChoice, tools))
|
|
152
|
-
.map(tool => cursorToolWireName(tool)),
|
|
163
|
+
.map(tool => cursorToolWireName(tool, tools)),
|
|
153
164
|
)];
|
|
154
165
|
if (wireNames.length === 0) return undefined;
|
|
155
166
|
|
|
@@ -170,7 +181,7 @@ export function buildCursorToolGuidanceSystemNote(
|
|
|
170
181
|
?.filter(tool => !tool.namespace && isCursorStructuredEditToolName(tool.name))
|
|
171
182
|
.map(tool => tool.name) ?? [];
|
|
172
183
|
const discoveryTools = discoveryToolLabel(wireNames);
|
|
173
|
-
const unavailableNeighborNames = unavailableNeighborAgentToolNames(wireNames);
|
|
184
|
+
const unavailableNeighborNames = unavailableNeighborAgentToolNames(wireNames, tools);
|
|
174
185
|
// Host-shell-neutral: the Codex client executes bridge commands, and may differ from
|
|
175
186
|
// the OpenCodex proxy OS (LAN/SSH remote-proxy). Always cover PowerShell 5.1 pitfalls.
|
|
176
187
|
const hostShellNote = hasBareExec
|
|
@@ -179,6 +190,9 @@ export function buildCursorToolGuidanceSystemNote(
|
|
|
179
190
|
const notes = [
|
|
180
191
|
`Cursor tool calls: available tool names are exactly ${listedNames}.`,
|
|
181
192
|
"Use the current tool catalog as ground truth and call only those exact names with their listed argument keys.",
|
|
193
|
+
plainToolWording && !codeMode && !hasBareExec
|
|
194
|
+
? "Cursor-native Read/Glob/Grep/LS/Shell/Write/Fetch are not available in this request. Use the listed tool that fits the operation."
|
|
195
|
+
: undefined,
|
|
182
196
|
unavailableNeighborNames.length > 0
|
|
183
197
|
? `This turn does not expose neighboring-agent tool names ${quotedNames(unavailableNeighborNames)}; do not call or suggest them unless the catalog lists them.`
|
|
184
198
|
: undefined,
|
|
@@ -191,18 +205,24 @@ export function buildCursorToolGuidanceSystemNote(
|
|
|
191
205
|
? CODE_MODE_RESULT_ECHO_SENTENCE + " There is no `require`, no `module`, and no filesystem or network globals; reach the host only through the nested helpers. " + CODE_MODE_HOST_CONTRACT_SENTENCE
|
|
192
206
|
: undefined,
|
|
193
207
|
codeMode
|
|
194
|
-
?
|
|
208
|
+
? plainToolWording
|
|
209
|
+
? "Cursor-native Shell, Read, Grep, List, and Write are not available in this request. The exec code cell and its nested helpers provide the execution path; separately listed tools remain callable at the top level."
|
|
210
|
+
: "NEVER attempt Cursor-native Shell, Read, Grep, List, Write, or any tool absent from the catalog — they are not executed in this environment and every probe wastes a turn. The exec code cell (with its nested helpers) is the ONLY execution surface; go to it directly on the FIRST attempt and do not narrate switching surfaces."
|
|
195
211
|
: undefined,
|
|
196
212
|
hasBareExec
|
|
197
213
|
? `${shellBridgeLabel} is the Codex Responses shell bridge for this turn, exposed through Cursor's tool protocol; it is not an external MCP server tool. \`shell_command\` and \`exec_command\` are aliases of the same bridge.`
|
|
198
214
|
: undefined,
|
|
199
215
|
hasBareExec
|
|
200
|
-
?
|
|
216
|
+
? plainToolWording
|
|
217
|
+
? "Your tool list may display the same shell bridge under a longer `mcp_opencodex-responses_shell_command` / `mcp_opencodex-responses_exec_command` name; call the name listed in the catalog."
|
|
218
|
+
: "Your tool list may display it under a longer `mcp_opencodex-responses_shell_command` / `mcp_opencodex-responses_exec_command` name; those are the SAME tool — call whichever your list shows, and do not comment on the naming difference to the user."
|
|
201
219
|
: undefined,
|
|
202
220
|
hasBareExec
|
|
203
|
-
?
|
|
221
|
+
? plainToolWording
|
|
222
|
+
? `Cursor-native Shell, Read, Grep, List, and Write are not available in this request. ${shellBridgeLabel} provides shell execution through the Codex client host.`
|
|
223
|
+
: `NEVER attempt Cursor-native Shell, Read, Grep, List, Write, or any tool not in the catalog above — they are not executed locally in this environment and every attempt wastes a turn and can stall the session. ${shellBridgeLabel} is the ONLY shell surface; go to it directly on the FIRST attempt, never as a fallback after probing a native tool. Do not narrate switching surfaces ("native is blocked, using the bridge instead") — there is exactly one surface.`
|
|
204
224
|
: undefined,
|
|
205
|
-
hasBareExec
|
|
225
|
+
hasBareExec && !plainToolWording
|
|
206
226
|
? "Tool-selection commentary is forbidden: for any shell, read, grep, list, or file operation, your FIRST visible action is the bridge call itself — never a sentence about which tool you will use, which tool was redirected, or switching surfaces. Words like 차단/전환/blocked/switching must not appear in your output for tool-routing reasons."
|
|
207
227
|
: undefined,
|
|
208
228
|
hostShellNote,
|
|
@@ -233,5 +253,6 @@ export function buildCursorToolGuidanceSystemNote(
|
|
|
233
253
|
? `For every file read, directory listing, grep, or shell operation use ${shellBridgeLabel} directly with host-shell-safe commands (POSIX: \`cat\`/\`ls\`/\`rg\`; Windows PowerShell: \`Get-Content\`/\`Get-ChildItem\`/\`Select-String\`). For file edits, use ${structuredEditNames.length > 0 ? `the structured edit tools (${quotedNames(structuredEditNames)}) or ` : ""}\`apply_patch\` when available.`
|
|
234
254
|
: undefined,
|
|
235
255
|
].filter((note): note is string => typeof note === "string");
|
|
256
|
+
if (plainToolWording) notes.push(CURSOR_TOOL_CALL_CONTINUATION);
|
|
236
257
|
return notes.join(" ");
|
|
237
258
|
}
|
|
@@ -67,11 +67,11 @@ function cursorToolChoiceMatches(
|
|
|
67
67
|
if (catalogHasBareCodexShellBridge(catalog)) {
|
|
68
68
|
return isBareCodexShellBridgeTool(tool);
|
|
69
69
|
}
|
|
70
|
-
return tool.name === choiceName || cursorToolWireName(tool) === choiceName;
|
|
70
|
+
return tool.name === choiceName || cursorToolWireName(tool, catalog) === choiceName;
|
|
71
71
|
}
|
|
72
72
|
if (tool.name === choiceName) return true;
|
|
73
73
|
if (cursorToolChoiceAliases(tool).includes(choiceName)) return true;
|
|
74
|
-
return cursorToolWireName(tool) === choiceName
|
|
74
|
+
return cursorToolWireName(tool, catalog) === choiceName
|
|
75
75
|
&& !catalog.some(candidate => candidate.name === choiceName);
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -178,16 +178,49 @@ const CURSOR_PROXY_OWNED_BARE_TOOL_NAMES = new Set([
|
|
|
178
178
|
CODEX_TOOL_SEARCH_TOOL,
|
|
179
179
|
]);
|
|
180
180
|
|
|
181
|
+
export const CLAUDE_CLIENT_BARE_TOOL_NAMES = new Set([
|
|
182
|
+
"Bash",
|
|
183
|
+
"Read",
|
|
184
|
+
"Write",
|
|
185
|
+
"Edit",
|
|
186
|
+
"Grep",
|
|
187
|
+
"Glob",
|
|
188
|
+
"Task",
|
|
189
|
+
"GetDynamicTools",
|
|
190
|
+
"EnterMode",
|
|
191
|
+
"ExitMode",
|
|
192
|
+
"Todo",
|
|
193
|
+
"AddTodo",
|
|
194
|
+
"UpdateTodo",
|
|
195
|
+
"ViewTodo",
|
|
196
|
+
"WebSearch",
|
|
197
|
+
"FetchUrl",
|
|
198
|
+
"KillProcess",
|
|
199
|
+
"ListProcesses",
|
|
200
|
+
"ViewProcessOutput",
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
export function isClaudeClientBareToolName(name: string): boolean {
|
|
204
|
+
return CLAUDE_CLIENT_BARE_TOOL_NAMES.has(name);
|
|
205
|
+
}
|
|
206
|
+
|
|
181
207
|
/** Avoid collisions with Cursor's private bare-tool namespace. */
|
|
182
|
-
function isCursorBareClientToolWireAliased(
|
|
208
|
+
export function isCursorBareClientToolWireAliased(
|
|
183
209
|
tool: Pick<OcxTool, "namespace" | "name">,
|
|
210
|
+
catalog?: readonly Pick<OcxTool, "namespace" | "name">[],
|
|
184
211
|
): boolean {
|
|
185
|
-
|
|
186
|
-
|
|
212
|
+
if (tool.namespace || CURSOR_PROXY_OWNED_BARE_TOOL_NAMES.has(tool.name)) return false;
|
|
213
|
+
if (isClaudeClientBareToolName(tool.name)) {
|
|
214
|
+
return catalog ? catalogHasBareCodexShellBridge(catalog) : false;
|
|
215
|
+
}
|
|
216
|
+
return true;
|
|
187
217
|
}
|
|
188
218
|
|
|
189
|
-
export function cursorToolWireName(
|
|
190
|
-
|
|
219
|
+
export function cursorToolWireName(
|
|
220
|
+
tool: Pick<OcxTool, "namespace" | "name">,
|
|
221
|
+
catalog?: readonly Pick<OcxTool, "namespace" | "name">[],
|
|
222
|
+
): string {
|
|
223
|
+
if (isCursorBareClientToolWireAliased(tool, catalog)) {
|
|
191
224
|
return `${CURSOR_CLIENT_TOOL_WIRE_PREFIX}${tool.name}`;
|
|
192
225
|
}
|
|
193
226
|
return namespacedToolName(tool.namespace, tool.name);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { OcxTool } from "../../types";
|
|
2
|
+
import type { McpToolDefinition } from "./gen/agent_pb";
|
|
3
|
+
import { CODE_MODE_RESULT_ECHO_SENTENCE } from "../exec-tool-result-normalize";
|
|
4
|
+
import { OCX_RESPONSES_TOOL_PROVIDER, cursorRequestHasExecutionPath, cursorRequestHasShellAlias, cursorRequestUsesCodeMode, cursorToolWireName } from "./tool-naming";
|
|
5
|
+
|
|
6
|
+
/** Cursor may expose Claude with version-first, family-first, or effort-suffixed ids. */
|
|
7
|
+
export function cursorUsesPlainToolWording(modelId?: string): boolean {
|
|
8
|
+
return /^(?:cursor\/)?claude-/i.test(modelId?.trim() ?? "");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const CURSOR_TOOL_CALL_CONTINUATION = "Continue the task with that tool call.";
|
|
12
|
+
const CURSOR_NO_CLIENT_TOOLS = "Cursor-native filesystem, shell, and fetch tools are not available through this execution channel. No client tools are available in this request. Answer without tools using the information already provided, or report that the requested operation cannot be completed without tools.";
|
|
13
|
+
|
|
14
|
+
/** Factual catalog wording; legacy model wording is owned by native-exec.ts unchanged. */
|
|
15
|
+
export function cursorPlainNativeExecRedirectHint(
|
|
16
|
+
tools: readonly Pick<OcxTool, "namespace" | "name" | "freeform">[] | undefined,
|
|
17
|
+
mcpToolDefs: readonly Pick<McpToolDefinition, "name" | "providerIdentifier">[],
|
|
18
|
+
): string | undefined {
|
|
19
|
+
const clientTools = tools ?? [];
|
|
20
|
+
const names = [...new Set([
|
|
21
|
+
...clientTools.map(tool => cursorToolWireName(tool, clientTools)),
|
|
22
|
+
...mcpToolDefs.map(def => `mcp_${def.providerIdentifier}_${def.name}`),
|
|
23
|
+
])];
|
|
24
|
+
if (names.length === 0) return CURSOR_NO_CLIENT_TOOLS;
|
|
25
|
+
const shown = names.slice(0, 16).map(name => `\`${name}\``).join(", ");
|
|
26
|
+
const more = names.length > 16 ? ` (+${names.length - 16} more)` : "";
|
|
27
|
+
const catalog = `Available tools in this request's catalog: ${shown}${more}. `
|
|
28
|
+
+ `The harness displays a \`${OCX_RESPONSES_TOOL_PROVIDER}\` entry as \`mcp_${OCX_RESPONSES_TOOL_PROVIDER}_<name>\`; that is the same tool. `
|
|
29
|
+
+ "Cursor-native Read/Glob/Grep/LS/Shell/Write/Fetch are not available in this request. ";
|
|
30
|
+
if (!cursorRequestHasShellAlias(clientTools) && cursorRequestUsesCodeMode(clientTools)) {
|
|
31
|
+
return catalog + "`exec` takes a JavaScript body evaluated in a V8 isolate, not a shell command. Shell, file, search, and fetch are nested helpers called inside it as `await tools.<name>(...)`, for example `text(await tools.exec_command({cmd: \"ls\"}))`. "
|
|
32
|
+
+ "`shell_command` and `exec_command` are nested helpers, not top-level tools here. Every other listed tool remains callable at the top level. "
|
|
33
|
+
+ CODE_MODE_RESULT_ECHO_SENTENCE + " " + CURSOR_TOOL_CALL_CONTINUATION;
|
|
34
|
+
}
|
|
35
|
+
const operation = cursorRequestHasExecutionPath(clientTools)
|
|
36
|
+
? "Use the listed execution tool with its advertised argument schema and the Codex client host's shell syntax; use a listed edit tool for file edits. "
|
|
37
|
+
: "Use the listed tool that fits the operation: a file, search, or fetch tool when provided, or a listed worker-agent delegation tool. ";
|
|
38
|
+
return catalog + operation + CURSOR_TOOL_CALL_CONTINUATION;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Fallback uses advertised dispatcher definitions, never assumed shell/edit tools. */
|
|
42
|
+
export function cursorPlainNativeExecFallback(
|
|
43
|
+
toolDefs: readonly Pick<McpToolDefinition, "name" | "providerIdentifier">[] = [],
|
|
44
|
+
): string {
|
|
45
|
+
return cursorPlainNativeExecRedirectHint([], toolDefs)!;
|
|
46
|
+
}
|
package/src/adapters/cursor.ts
CHANGED
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
type CursorTransportFactory,
|
|
66
66
|
} from "./cursor/transport";
|
|
67
67
|
import { cursorLiveRosterScope } from "./cursor/catalog";
|
|
68
|
+
import { cursorUsesPlainToolWording } from "./cursor/tool-wording";
|
|
68
69
|
|
|
69
70
|
export const CURSOR_API_URL = "https://api2.cursor.sh";
|
|
70
71
|
|
|
@@ -313,8 +314,10 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
313
314
|
const armMidstreamEcho = cursorNeedsExternalToolContinuation(activeRequest.modelId) && replaysToolResult;
|
|
314
315
|
const midstreamObserver = armMidstreamEcho ? new CursorMidstreamEchoObserver() : undefined;
|
|
315
316
|
const midstreamFilter = armMidstreamEcho ? new ToolEnvelopeEchoFilter() : undefined;
|
|
317
|
+
// Claude's factual redirects allow truthful routing prose; it is not a failure claim.
|
|
316
318
|
const armRoutingCommentarySniffer =
|
|
317
319
|
isCursorExternalWireModel(activeRequest.modelId)
|
|
320
|
+
&& !cursorUsesPlainToolWording(activeRequest.modelId)
|
|
318
321
|
&& (
|
|
319
322
|
cursorRequestUsesCodeMode(activeRequest.tools, activeRequest.toolChoice)
|
|
320
323
|
|| cursorRequestHasShellAlias(activeRequest.tools)
|
|
@@ -73,6 +73,8 @@ export const DEVIN_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
|
73
73
|
"claude-fable-5-1": 1_000_000,
|
|
74
74
|
// 260929 preemptive: Claude Sonnet 5.5 (1M per Anthropic) seeded before Devin's live catalog lists it.
|
|
75
75
|
"claude-sonnet-5-5": 1_000_000,
|
|
76
|
+
// 261008 preemptive: Haiku 5.5 (1M per Anthropic), ahead of Devin live discovery.
|
|
77
|
+
"claude-haiku-5-5": 1_000_000,
|
|
76
78
|
"claude-sonnet-5": 1_000_000,
|
|
77
79
|
"glm-5-2": 200_000,
|
|
78
80
|
"glm-5-3": 1_048_576,
|
package/src/adapters/devin.ts
CHANGED
|
@@ -524,30 +524,44 @@ export function mapOcxMessagesToDevin(
|
|
|
524
524
|
if (system) items.push({ role: "system", content: system });
|
|
525
525
|
|
|
526
526
|
let previousToolResult: ChatHistoryItem | undefined;
|
|
527
|
+
let textChunks: string[] | undefined;
|
|
528
|
+
const flushText = () => {
|
|
529
|
+
if (previousToolResult && textChunks) previousToolResult.content = textChunks.join("\n\n");
|
|
530
|
+
textChunks = undefined;
|
|
531
|
+
};
|
|
527
532
|
for (const message of parsed.context.messages) {
|
|
528
533
|
// Original-message adjacency matters even when an intervening message maps to nothing.
|
|
529
|
-
if (message.role !== "toolResult")
|
|
534
|
+
if (message.role !== "toolResult") {
|
|
535
|
+
flushText();
|
|
536
|
+
previousToolResult = undefined;
|
|
537
|
+
}
|
|
530
538
|
const mapped = mapOneMessage(message, parsed.modelId, options);
|
|
531
539
|
if (!mapped) continue;
|
|
532
540
|
if (mapped.role === "tool") {
|
|
533
541
|
if (previousToolResult && previousToolResult.tool_call_id === mapped.tool_call_id) {
|
|
534
542
|
const previous = previousToolResult;
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
543
|
+
if (textChunks && typeof mapped.content === "string") {
|
|
544
|
+
textChunks.push(mapped.content);
|
|
545
|
+
} else {
|
|
546
|
+
flushText();
|
|
547
|
+
// mapOneMessage owns these wire arrays; append without recopying their prefix.
|
|
548
|
+
const parts: ContentPart[] = typeof previous.content === "string"
|
|
549
|
+
? [{ type: "text", text: previous.content }] : previous.content;
|
|
550
|
+
parts.push({ type: "text", text: "\n\n" });
|
|
551
|
+
if (typeof mapped.content === "string") parts.push({ type: "text", text: mapped.content });
|
|
552
|
+
else for (const part of mapped.content) parts.push(part);
|
|
553
|
+
previous.content = parts;
|
|
554
|
+
}
|
|
544
555
|
if (mapped.is_error) previous.is_error = true;
|
|
545
556
|
continue;
|
|
546
557
|
}
|
|
558
|
+
flushText();
|
|
547
559
|
previousToolResult = mapped;
|
|
560
|
+
textChunks = typeof mapped.content === "string" ? [mapped.content] : undefined;
|
|
548
561
|
}
|
|
549
562
|
items.push(mapped);
|
|
550
563
|
}
|
|
564
|
+
flushText();
|
|
551
565
|
return items;
|
|
552
566
|
}
|
|
553
567
|
|
|
@@ -795,7 +809,7 @@ export function createDevinAdapter(
|
|
|
795
809
|
// An admitted HTTP turn owns globally shared capacity until this call
|
|
796
810
|
// emits. Without an explicit wait allowance, preserve the typed reset
|
|
797
811
|
// delay in generated diagnostic wording and return immediately.
|
|
798
|
-
const signedMessages =
|
|
812
|
+
const signedMessages = messages; // Reuse the history already mapped above.
|
|
799
813
|
// A Claude signature is replayed because it is what carries the reasoning into this
|
|
800
814
|
// turn, but Cognition streams Claude's thinking as a summary the signature does not
|
|
801
815
|
// cover, and some replays are refused with invalid_argument before any output. That
|
|
@@ -183,12 +183,12 @@ async function fetchWithResetRecovery(
|
|
|
183
183
|
sendClass: "transient", targetKey: destination, rebasedTarget: destinationRebased,
|
|
184
184
|
});
|
|
185
185
|
destinationRebased = false;
|
|
186
|
-
if (decision &&
|
|
186
|
+
if (decision && !decision.allowed) {
|
|
187
187
|
throw new SendBudgetExhaustedError(destination);
|
|
188
188
|
}
|
|
189
189
|
// Reported after admission and before dispatch, so a refused send is never counted and an
|
|
190
190
|
// admitted one is counted exactly once whichever way the fetch below settles.
|
|
191
|
-
|
|
191
|
+
const permit = decision?.allowed ? decision.permit : undefined;
|
|
192
192
|
const requestUrl = request.url;
|
|
193
193
|
try {
|
|
194
194
|
const headers = new Headers(request.headers);
|
|
@@ -201,7 +201,12 @@ async function fetchWithResetRecovery(
|
|
|
201
201
|
...(recovered ? { keepalive: false } : {}),
|
|
202
202
|
}, timeoutMs, ctx.abortSignal, ctx.stream, (async (input, init) => {
|
|
203
203
|
try {
|
|
204
|
-
|
|
204
|
+
const run = async (): Promise<Response> => {
|
|
205
|
+
if (permit && !permit.use()) throw new SendBudgetExhaustedError(destination);
|
|
206
|
+
notePhysicalSend(attempt > 0);
|
|
207
|
+
return (executor.unpacedFetch ?? executor)(input, init);
|
|
208
|
+
};
|
|
209
|
+
return await (permit?.execute ? permit.execute(run) : run());
|
|
205
210
|
} catch (error) {
|
|
206
211
|
if (ctx.abortSignal?.aborted) throw abortError(ctx.abortSignal);
|
|
207
212
|
const signal = init?.signal;
|
|
@@ -224,7 +229,7 @@ async function fetchWithResetRecovery(
|
|
|
224
229
|
baseDelayMs: RESET_RETRY_BASE_MS,
|
|
225
230
|
maxDelayMs: RESET_RETRY_MAX_MS,
|
|
226
231
|
}), ctx.abortSignal);
|
|
227
|
-
}
|
|
232
|
+
} finally { permit?.release(); }
|
|
228
233
|
} finally {
|
|
229
234
|
releaseProviderRequestSlot(slot);
|
|
230
235
|
}
|
|
@@ -142,9 +142,12 @@ export function messagesToChatFormat(parsed: OcxParsedRequest, provider: OcxProv
|
|
|
142
142
|
};
|
|
143
143
|
|
|
144
144
|
const nativeOpenAI = isNativeOpenAIChatTarget(provider);
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
|
|
145
|
+
// Two pinned chat templates reject non-leading system and every developer role while a
|
|
146
|
+
// later user renders in place: Qwen3.8-27B and the OrcaSAQ-2-Cyber-27B GGUF family.
|
|
147
|
+
// Gateways serve them under namespaced ids (LiteLLM's openai/Qwen3.8-27B, #6674), so any
|
|
148
|
+
// number of leading path segments is accepted; the final segment names the checkpoint.
|
|
149
|
+
// Orca's GGUF marker must precede any colon quant tag; other Orca variants are unchanged.
|
|
150
|
+
const leadingSystemTemplate = !nativeOpenAI && /^(?:[^/]+\/)*(?:Qwen3\.8-27B|qwen3-8-27b(?:-(?:fp8|lora))?|OrcaSAQ-2-Cyber-27B(?=[^:/\s]*GGUF)[^:/\s]*(?::[\w-]+)?)$/i.test(parsed.modelId);
|
|
148
151
|
// Which role a developer message carries, and why the unrecorded state folds, is stated once
|
|
149
152
|
// in ./developer-role.ts and read from there by the native passthrough as well. Either way the
|
|
150
153
|
// message keeps the slot it arrived in — only the role changes, never the position.
|
|
@@ -194,7 +197,7 @@ export function messagesToChatFormat(parsed: OcxParsedRequest, provider: OcxProv
|
|
|
194
197
|
// message instead is rejected by some upstreams. Native OpenAI keeps its existing
|
|
195
198
|
// empty-developer wire, which is a separate question from placement.
|
|
196
199
|
if (!nativeOpenAI && text.length === 0) break;
|
|
197
|
-
chatMsg = { role:
|
|
200
|
+
chatMsg = { role: leadingSystemTemplate ? "user" : developerWireRole, content: text };
|
|
198
201
|
} else if (typeof msg.content === "string") {
|
|
199
202
|
chatMsg = { role: "user", content: msg.content };
|
|
200
203
|
} else if (!hasStructured) {
|
|
@@ -36,11 +36,14 @@ export function createAdapterPhysicalSend(ctx: AdapterFetchContext = {}, fallbac
|
|
|
36
36
|
const physicalExecutor = (async (input, init) => {
|
|
37
37
|
if (ctx.abortSignal?.aborted) throw abortError(ctx.abortSignal);
|
|
38
38
|
if (init?.signal?.aborted) throw abortError(init.signal);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
const run = async (): Promise<Response> => {
|
|
40
|
+
if (dispatched || (permit && !permit.use())) throw new SendBudgetExhaustedError(options.url);
|
|
41
|
+
dispatched = true;
|
|
42
|
+
ordinal += 1;
|
|
43
|
+
ctx.onPhysicalSend?.({ ordinal, ...(options.recovery ? { recovery: options.recovery } : {}) });
|
|
44
|
+
return (executor.unpacedFetch ?? executor)(input, init);
|
|
45
|
+
};
|
|
46
|
+
return permit?.execute ? permit.execute(run) : run();
|
|
44
47
|
}) as typeof globalThis.fetch;
|
|
45
48
|
let slot: ProviderRequestSlot | undefined;
|
|
46
49
|
try {
|