@bitkyc08/opencodex 2.42.0 → 2.43.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/AGENTS_INSTALL.md +2 -2
- package/README.md +31 -0
- package/bin/ocx.mjs +10 -1
- package/gui/dist/assets/index-DS1NE4Jn.css +1 -0
- package/gui/dist/assets/index-Djowl68T.js +112 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic-image-codec.ts +304 -0
- package/src/adapters/anthropic-image-normalize.ts +8 -298
- package/src/adapters/anthropic.ts +30 -7
- package/src/adapters/command-code.ts +7 -5
- package/src/adapters/cursor/desktop-executor-contract.ts +15 -0
- package/src/adapters/cursor/images.ts +36 -6
- package/src/adapters/cursor/live-transport.ts +7 -2
- package/src/adapters/cursor/native-exec-desktop.ts +2 -15
- package/src/adapters/cursor/protobuf-request.ts +13 -3
- package/src/adapters/cursor/tool-definitions.ts +5 -670
- package/src/adapters/cursor/tool-guidance.ts +236 -0
- package/src/adapters/cursor/tool-naming.ts +252 -0
- package/src/adapters/cursor/tool-schemas.ts +195 -0
- package/src/adapters/cursor/types.ts +6 -3
- package/src/adapters/exec-tool-result-normalize.ts +1 -1
- package/src/adapters/google-errors.ts +9 -1
- package/src/adapters/google.ts +1 -0
- package/src/adapters/kiro-calibration.ts +181 -0
- package/src/adapters/kiro.ts +135 -3
- package/src/adapters/openai-responses.ts +216 -26
- package/src/adapters/responses-code-mode.ts +59 -0
- package/src/adapters/tool-catalog-nudge.ts +1 -1
- package/src/adapters/xai-schema-analysis.ts +86 -0
- package/src/adapters/xai-tool-schema.ts +2 -87
- package/src/adapters/xai-web-search.ts +1 -1
- package/src/bridge.ts +22 -10
- package/src/chat/inbound.ts +11 -3
- package/src/claude/inbound-content-options.ts +60 -0
- package/src/claude/inbound-model-options.ts +142 -0
- package/src/claude/inbound-records.ts +7 -0
- package/src/claude/inbound.ts +10 -202
- package/src/claude/model-info.ts +45 -0
- package/src/cli/account-auth.ts +21 -6
- package/src/cli/capabilities.ts +13 -2
- package/src/cli/claude.ts +232 -39
- package/src/cli/config-command.ts +9 -1
- package/src/cli/dispatch.ts +5 -1
- package/src/cli/doctor.ts +10 -0
- package/src/cli/effort.ts +372 -0
- package/src/cli/export-command.ts +3 -9
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/init.ts +4 -0
- package/src/cli/model-selection-guidance.ts +30 -0
- package/src/cli/models-runtime.ts +3 -2
- package/src/cli/models.ts +8 -3
- package/src/cli/opencode.ts +4 -1
- package/src/cli/provider-runtime.ts +65 -0
- package/src/cli/provider.ts +8 -0
- package/src/cli/registry.ts +16 -2
- package/src/cli/runtime-api.ts +3 -1
- package/src/cli/star-prompt.ts +22 -6
- package/src/cli/status-probes.ts +168 -0
- package/src/cli/status.ts +5 -168
- package/src/clients/config-export/constants.ts +69 -0
- package/src/clients/config-export/contracts.ts +154 -0
- package/src/clients/config-export/dsh.ts +132 -0
- package/src/clients/config-export/fast-models.ts +29 -0
- package/src/clients/config-export/mcode.ts +83 -0
- package/src/clients/config-export/model-metadata.ts +108 -0
- package/src/clients/config-export/omp.ts +104 -0
- package/src/clients/config-export/zcode.ts +92 -0
- package/src/clients/config-export.ts +18 -710
- package/src/codex/account-lifecycle.ts +20 -3
- package/src/codex/account-usability.ts +2 -0
- package/src/codex/auth-api.ts +170 -23
- package/src/codex/auth-context.ts +328 -24
- package/src/codex/catalog/effort.ts +30 -4
- package/src/codex/catalog/metadata.ts +39 -9
- package/src/codex/catalog/native-models.ts +53 -12
- package/src/codex/catalog/parsing.ts +119 -5
- package/src/codex/catalog/provider-fetch.ts +150 -20
- package/src/codex/catalog/reserve.ts +52 -0
- package/src/codex/catalog/sync.ts +89 -16
- package/src/codex/convergence-types.ts +1 -0
- package/src/codex/convergence.ts +2 -0
- package/src/codex/data/upstream-models.json +1 -1
- package/src/codex/forward-transport-headers.ts +25 -0
- package/src/codex/inject.ts +3 -28
- package/src/codex/legacy-config-keys.ts +68 -0
- package/src/codex/log-guard/inspect-schema.ts +137 -0
- package/src/codex/log-guard/inspect.ts +2 -134
- package/src/codex/loopback-target.ts +54 -0
- package/src/codex/main-account-cache.ts +63 -1
- package/src/codex/main-account-hard-lock.ts +52 -0
- package/src/codex/main-account.ts +3 -1
- package/src/codex/management-convergence.ts +3 -0
- package/src/codex/model-entitlements.ts +54 -4
- package/src/codex/project-config-warnings.ts +92 -2
- package/src/codex/prompt-layers/encoding.ts +80 -0
- package/src/codex/prompt-layers/paths.ts +54 -0
- package/src/codex/prompt-layers/revision.ts +55 -0
- package/src/codex/prompt-layers/toml-edit.ts +163 -0
- package/src/codex/prompt-layers/toml-read.ts +181 -0
- package/src/codex/prompt-layers.ts +14 -520
- package/src/codex/quota-auto-refresh-state.ts +16 -0
- package/src/codex/quota-auto-refresh.ts +219 -0
- package/src/codex/quota-types.ts +51 -0
- package/src/codex/quota.ts +252 -93
- package/src/codex/reserve-availability.ts +177 -0
- package/src/codex/routing.ts +28 -9
- package/src/codex/shim.ts +53 -11
- package/src/codex/subagent-model-fallback.ts +23 -3
- package/src/combos/failover.ts +125 -7
- package/src/combos/identifiers.ts +89 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/resolve.ts +80 -9
- package/src/combos/types.ts +20 -93
- package/src/config/subagent-models.ts +24 -0
- package/src/config.ts +156 -13
- package/src/generated/compatibility-version.json +454 -166
- package/src/generated/model-metadata.ts +1 -1
- package/src/integrations/journal.ts +65 -4
- package/src/integrations/store.ts +5 -0
- package/src/lab/events/limits.ts +4 -0
- package/src/lib/destination-policy.ts +31 -2
- package/src/lib/errors.ts +39 -0
- package/src/lib/provider-outbound.ts +69 -3
- package/src/lib/proxy-env.ts +22 -0
- package/src/lib/redact-folding.ts +176 -0
- package/src/lib/redact.ts +2 -175
- package/src/lib/state-store-sweeper.ts +20 -6
- package/src/lib/token-estimate.ts +94 -27
- package/src/lib/windows-user-principal.ts +53 -5
- package/src/oauth/anthropic-routing.ts +99 -3
- package/src/oauth/generic-account-failover.ts +36 -13
- package/src/oauth/index.ts +127 -26
- package/src/oauth/login-cli.ts +5 -0
- package/src/oauth/meta-muse.ts +117 -15
- package/src/oauth/pool-settings-capability.ts +15 -4
- package/src/providers/api-keys.ts +8 -10
- package/src/providers/default-aliases.ts +39 -0
- package/src/providers/derive.ts +10 -2
- package/src/providers/fastwire.ts +36 -7
- package/src/providers/initial-model-selection-runtime.ts +90 -0
- package/src/providers/initial-model-selection.ts +120 -0
- package/src/providers/key-failover.ts +134 -54
- package/src/providers/key-store.ts +11 -1
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery.ts +76 -0
- package/src/providers/model-rename-startup.ts +72 -8
- package/src/providers/openai-sidecar.ts +17 -5
- package/src/providers/openai-tiers-destination.ts +102 -0
- package/src/providers/openai-tiers.ts +2 -99
- package/src/providers/opencode-go-transport.ts +41 -0
- package/src/providers/quota-key-accounts.ts +141 -0
- package/src/providers/quota-types.ts +9 -0
- package/src/providers/quota.ts +535 -104
- package/src/providers/registry.ts +60 -17
- package/src/providers/xai-responses-opt-in.ts +31 -5
- package/src/quota/reset-activation.ts +81 -0
- package/src/quota/reset-detector.ts +305 -0
- package/src/quota/reset-notify-config.ts +162 -0
- package/src/quota/reset-observer.ts +125 -0
- package/src/quota/reset-poller.ts +160 -0
- package/src/quota/reset-seen-store.ts +385 -0
- package/src/quota/reset-sinks.ts +199 -0
- package/src/quota/window-mapping.ts +106 -0
- package/src/responses/apply-patch-envelope.ts +46 -0
- package/src/responses/code-mode-helper-compat.ts +39 -1
- package/src/responses/custom-tool-compat.ts +10 -4
- package/src/responses/hosted-tool-policy.ts +12 -4
- package/src/responses/parser-content.ts +133 -0
- package/src/responses/parser-text-format.ts +24 -0
- package/src/responses/parser-tools.ts +188 -0
- package/src/responses/parser.ts +3 -326
- package/src/responses/state.ts +124 -28
- package/src/router.ts +48 -13
- package/src/routing/analytics.ts +1 -0
- package/src/routing/capability.ts +17 -4
- package/src/server/auth-cors.ts +7 -1
- package/src/server/background-lifecycle.ts +23 -1
- package/src/server/chat-completions.ts +25 -3
- package/src/server/claude-messages.ts +62 -5
- package/src/server/effort-row.ts +1 -1
- package/src/server/fast-row.ts +295 -0
- package/src/server/gui-static.ts +30 -4
- package/src/server/index.ts +107 -21
- package/src/server/management/agent-settings-routes.ts +2 -2
- package/src/server/management/combo-routes.ts +37 -9
- package/src/server/management/config-routes.ts +93 -2
- package/src/server/management/integration-routes.ts +108 -0
- package/src/server/management/model-routes.ts +13 -3
- package/src/server/management/model-rows.ts +20 -1
- package/src/server/management/native-integration-routes.ts +4 -1
- package/src/server/management/oauth-account-routes.ts +38 -10
- package/src/server/management/provider-routes.ts +34 -3
- package/src/server/management/quota-reset-routes.ts +57 -0
- package/src/server/management/route-registry.ts +7 -4
- package/src/server/management/shared.ts +19 -5
- package/src/server/management/system-routes.ts +3 -2
- package/src/server/management-api.ts +14 -2
- package/src/server/ports.ts +12 -2
- package/src/server/relay-eager.ts +38 -23
- package/src/server/relay.ts +4 -0
- package/src/server/request-log.ts +6 -0
- package/src/server/responses/codex-ws-correlation.ts +65 -0
- package/src/server/responses/codex-ws-exchange.ts +261 -0
- package/src/server/responses/codex-ws-metadata.ts +134 -0
- package/src/server/responses/codex-ws-pool.ts +162 -0
- package/src/server/responses/codex-ws-request.ts +87 -0
- package/src/server/responses/codex-ws-session.ts +93 -0
- package/src/server/responses/codex-ws-wire.ts +144 -0
- package/src/server/responses/collaboration.ts +41 -1
- package/src/server/responses/compact.ts +105 -12
- package/src/server/responses/core.ts +476 -57
- package/src/server/responses/fetch-helpers.ts +10 -3
- package/src/server/responses/input-admission.ts +16 -9
- package/src/server/responses/responses-field-backfill.ts +1 -1
- package/src/server/responses/ws-upstream.ts +34 -318
- package/src/server/responses-custom-tool-repair.ts +20 -4
- package/src/server/responses-undeclared-tool-guard.ts +100 -8
- package/src/server/safe-response-headers.ts +23 -0
- package/src/server/search.ts +9 -0
- package/src/server/subagent-models-startup.ts +27 -0
- package/src/server/system-env-shell.ts +238 -0
- package/src/server/system-env.ts +7 -234
- package/src/server/ws-bridge.ts +3 -25
- package/src/server/xai-responses-startup.ts +21 -0
- package/src/service-manager-probe.ts +1 -1
- package/src/service.ts +55 -16
- package/src/types/config.ts +108 -12
- package/src/types/provider.ts +36 -7
- package/src/types/tools.ts +26 -1
- package/src/types.ts +2 -0
- package/src/update/notify.ts +8 -2
- package/src/usage/cost.ts +38 -28
- package/src/usage/expected-prices.ts +34 -15
- package/src/usage/log.ts +2 -0
- package/src/usage/model-identity.ts +26 -0
- package/src/usage/summary.ts +15 -1
- package/src/vision/describe.ts +6 -0
- package/src/vision/image-rewrite.ts +108 -0
- package/src/vision/index.ts +19 -306
- package/src/vision/plan.ts +205 -0
- package/src/web-search/executor.ts +6 -0
- package/src/web-search/index.ts +8 -1
- package/gui/dist/assets/index-BU1tE0sr.js +0 -112
- package/gui/dist/assets/index-DL9-iS6J.css +0 -1
|
@@ -642,6 +642,19 @@ function orphanToolResultText(msg: OcxToolResultMessage): string {
|
|
|
642
642
|
return `[tool_result without adjacent tool_use: ${label}]\n${content}`;
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
+
function orphanToolResultContent(msg: OcxToolResultMessage): string | unknown[] {
|
|
646
|
+
if (typeof msg.content === "string" || !msg.content.some(p => p.type === "image")) {
|
|
647
|
+
return orphanToolResultText(msg);
|
|
648
|
+
}
|
|
649
|
+
const label = msg.toolName ? `${msg.toolName} (${msg.toolCallId})` : msg.toolCallId;
|
|
650
|
+
return [
|
|
651
|
+
{ type: "text", text: `[tool_result without adjacent tool_use: ${label}]` },
|
|
652
|
+
...msg.content
|
|
653
|
+
.map(toAnthropicContentPart)
|
|
654
|
+
.filter(p => !((p as { type?: string }).type === "text" && !(p as { text?: string }).text)),
|
|
655
|
+
];
|
|
656
|
+
}
|
|
657
|
+
|
|
645
658
|
/**
|
|
646
659
|
* AgentRouter answers 400 `content-blocked` when the first user message is not in English
|
|
647
660
|
* (#2074), while the same request in English returns 200. The gateway is inspecting the opening
|
|
@@ -737,7 +750,7 @@ function messagesToAnthropicFormat(
|
|
|
737
750
|
if (toolUseIds.length > 0) {
|
|
738
751
|
const requiredIds = new Set(toolUseIds);
|
|
739
752
|
const resultBlocks: Record<string, unknown>[] = [];
|
|
740
|
-
const orphanBlocks:
|
|
753
|
+
const orphanBlocks: unknown[] = [];
|
|
741
754
|
const seen = new Set<string>();
|
|
742
755
|
let j = i + 1;
|
|
743
756
|
while (j < parsed.context.messages.length && parsed.context.messages[j].role === "toolResult") {
|
|
@@ -750,7 +763,8 @@ function messagesToAnthropicFormat(
|
|
|
750
763
|
resultBlocks.push(toAnthropicToolResult(tr, wireResultId));
|
|
751
764
|
seen.add(wireResultId);
|
|
752
765
|
} else {
|
|
753
|
-
|
|
766
|
+
const orphan = orphanToolResultContent(tr);
|
|
767
|
+
orphanBlocks.push(...(typeof orphan === "string" ? [{ type: "text", text: orphan }] : orphan));
|
|
754
768
|
}
|
|
755
769
|
j++;
|
|
756
770
|
}
|
|
@@ -771,8 +785,8 @@ function messagesToAnthropicFormat(
|
|
|
771
785
|
}
|
|
772
786
|
case "toolResult": {
|
|
773
787
|
// A standalone Anthropic tool_result is invalid unless it immediately follows an
|
|
774
|
-
// assistant tool_use. Preserve
|
|
775
|
-
messages.push({ role: "user", content:
|
|
788
|
+
// assistant tool_use. Preserve text and images as user content without fabricating a pairing.
|
|
789
|
+
messages.push({ role: "user", content: orphanToolResultContent(msg as OcxToolResultMessage) });
|
|
776
790
|
break;
|
|
777
791
|
}
|
|
778
792
|
}
|
|
@@ -895,11 +909,20 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
895
909
|
enforceAnthropicImageLimits(messages);
|
|
896
910
|
const tools = toolsToAnthropicFormat(parsed, toolNames);
|
|
897
911
|
|
|
912
|
+
// Codex never sends `max_output_tokens`, so the omitted-limit default decides how
|
|
913
|
+
// long a Claude answer may run. Honor the provider's configured output budget
|
|
914
|
+
// (`modelMaxOutputTokens` / `defaultMaxOutputTokens`) before falling back to the
|
|
915
|
+
// conservative 8192, which truncates long answers with stop_reason=max_tokens.
|
|
916
|
+
const configuredMaxOut = modelRecordValue(provider.modelMaxOutputTokens, parsed.modelId)
|
|
917
|
+
?? provider.defaultMaxOutputTokens;
|
|
918
|
+
const omittedMaxTokens = typeof configuredMaxOut === "number" && configuredMaxOut > 0
|
|
919
|
+
? configuredMaxOut
|
|
920
|
+
: DEFAULT_MAX_TOKENS;
|
|
898
921
|
const body: Record<string, unknown> = {
|
|
899
922
|
model: parsed.modelId,
|
|
900
923
|
messages,
|
|
901
924
|
stream: parsed.stream,
|
|
902
|
-
max_tokens: parsed.options.maxOutputTokens ??
|
|
925
|
+
max_tokens: parsed.options.maxOutputTokens ?? omittedMaxTokens,
|
|
903
926
|
};
|
|
904
927
|
if (isOAuth) {
|
|
905
928
|
// Claude OAuth (Pro/Max) requires the first system block to be the Claude Code identity.
|
|
@@ -942,13 +965,13 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
942
965
|
// so effort=max (budget=32k) still leaves OUTPUT_HEADROOM tokens for visible output.
|
|
943
966
|
body.max_tokens = explicitMaxOut !== undefined
|
|
944
967
|
? explicitMaxOut
|
|
945
|
-
: Math.min(ADAPTIVE_THINKING_CEILING, Math.max(DEFAULT_MAX_TOKENS, floor));
|
|
968
|
+
: Math.max(omittedMaxTokens, Math.min(ADAPTIVE_THINKING_CEILING, Math.max(DEFAULT_MAX_TOKENS, floor)));
|
|
946
969
|
} else {
|
|
947
970
|
// Anthropic requires max_tokens > thinking.budget_tokens (max_tokens caps thinking +
|
|
948
971
|
// visible output) and budget_tokens >= 1024. Codex sends the SAME value for both, which
|
|
949
972
|
// 400s ("max_tokens must be greater than thinking.budget_tokens"). Size them so max_tokens
|
|
950
973
|
// always exceeds the budget within a model-safe ceiling, reserving room for visible output.
|
|
951
|
-
const maxOut = parsed.options.maxOutputTokens ??
|
|
974
|
+
const maxOut = parsed.options.maxOutputTokens ?? omittedMaxTokens;
|
|
952
975
|
const wantBudget = reasoningBudget(effectiveReasoning);
|
|
953
976
|
const maxTokens = Math.min(REASONING_MAX_TOKENS_CEILING, Math.max(maxOut, wantBudget + OUTPUT_HEADROOM));
|
|
954
977
|
const budget = Math.max(MIN_THINKING_BUDGET, Math.min(wantBudget, maxTokens - OUTPUT_FLOOR));
|
|
@@ -59,7 +59,7 @@ function wireImagePart(imageUrl: string): Record<string, unknown> {
|
|
|
59
59
|
* (#1383). This builder keeps the pairing invariant:
|
|
60
60
|
*
|
|
61
61
|
* - a `toolResult` that matches a declared assistant call emits the native `tool-result`;
|
|
62
|
-
* - a `toolResult` with no matching declared call degrades to a
|
|
62
|
+
* - a `toolResult` with no matching declared call degrades to a user carrier so the model
|
|
63
63
|
* still sees the outcome without a 400-prone standalone `tool` message;
|
|
64
64
|
* - every declared assistant call that never received a result gets an explicit error
|
|
65
65
|
* `tool-result`, so the upstream never sees an unpaired call.
|
|
@@ -108,6 +108,9 @@ function wireMessages(messages: OcxMessage[]): Array<Record<string, unknown>> {
|
|
|
108
108
|
continue;
|
|
109
109
|
}
|
|
110
110
|
if (message.role === "toolResult") {
|
|
111
|
+
const images = typeof message.content === "string" ? [] : message.content
|
|
112
|
+
.filter(part => part.type === "image")
|
|
113
|
+
.map(part => wireImagePart((part as { imageUrl: string }).imageUrl));
|
|
111
114
|
const callIndex = pendingCalls.findIndex(call => call.id === message.toolCallId);
|
|
112
115
|
const paired = callIndex >= 0;
|
|
113
116
|
if (paired) pendingCalls.splice(callIndex, 1);
|
|
@@ -116,11 +119,11 @@ function wireMessages(messages: OcxMessage[]): Array<Record<string, unknown>> {
|
|
|
116
119
|
// message lands, or their synthesized results would follow the orphan carrier.
|
|
117
120
|
closePendingCalls();
|
|
118
121
|
// The upstream rejects a standalone tool message whose call was never declared by an
|
|
119
|
-
// assistant turn. Preserve the outcome
|
|
122
|
+
// assistant turn. Preserve the outcome and images so the model can still act on it.
|
|
120
123
|
const label = message.toolName ? `${message.toolName} (${message.toolCallId})` : message.toolCallId;
|
|
121
124
|
const text = toolResultText(message.content);
|
|
122
125
|
// The orphan result cannot ride a `tool` message; carry it in a user message instead.
|
|
123
|
-
out.push({ role: "user", content: [{ type: "text", text: `[tool result without adjacent tool call: ${label}]\n${text}` }] });
|
|
126
|
+
out.push({ role: "user", content: [{ type: "text", text: `[tool result without adjacent tool call: ${label}]\n${text}` }, ...images] });
|
|
124
127
|
continue;
|
|
125
128
|
}
|
|
126
129
|
out.push({ role: "tool", content: [{
|
|
@@ -132,9 +135,8 @@ function wireMessages(messages: OcxMessage[]): Array<Record<string, unknown>> {
|
|
|
132
135
|
// The proprietary wire's tool-result output is text-only; image parts returned by a
|
|
133
136
|
// tool (e.g. Codex view_image) cannot live inside it. Carry them in a follow-up user
|
|
134
137
|
// message using the same image encoding as the user branch so the bytes reach the model.
|
|
135
|
-
const images = typeof message.content === "string" ? [] : message.content.filter(part => part.type === "image");
|
|
136
138
|
if (images.length > 0) {
|
|
137
|
-
pendingImageCarriers.push({ role: "user", content: images
|
|
139
|
+
pendingImageCarriers.push({ role: "user", content: images });
|
|
138
140
|
}
|
|
139
141
|
continue;
|
|
140
142
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in external executor for computer-use / record-screen. opencodex is a headless proxy and
|
|
3
|
+
* cannot drive a screen itself; set these commands only when running on a host that can. Each
|
|
4
|
+
* command receives the request as JSON on stdin and must print a JSON result on stdout.
|
|
5
|
+
*/
|
|
6
|
+
export interface DesktopExecutorConfig {
|
|
7
|
+
/** Command (run via the platform shell) handling computer-use. Receives `{toolCallId, actions}` on stdin. */
|
|
8
|
+
computerUseCommand?: string;
|
|
9
|
+
/** Command handling record-screen. Receives `{mode, toolCallId, saveAsFilename?}` on stdin. */
|
|
10
|
+
recordScreenCommand?: string;
|
|
11
|
+
cwd?: string;
|
|
12
|
+
env?: Record<string, string>;
|
|
13
|
+
/** Max time to wait for the external process. Default 30s. */
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
}
|
|
@@ -84,6 +84,8 @@ export interface ResolvedCursorImage {
|
|
|
84
84
|
uuid: string;
|
|
85
85
|
/** Codex/OpenAI image detail hint; affects JPEG soft-cap tier. */
|
|
86
86
|
detail?: string;
|
|
87
|
+
/** Bounded client-supplied provenance for opted-in trailing tool-result images only. */
|
|
88
|
+
sourceLabel?: string;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
export type PrepareCursorImageOutcome =
|
|
@@ -546,7 +548,7 @@ export function buildSelectedContext(
|
|
|
546
548
|
|
|
547
549
|
/**
|
|
548
550
|
* Resolve data: images for the active user/developer turn onto SelectedImage.
|
|
549
|
-
*
|
|
551
|
+
* Opted-in tool-result runs use prepareCursorRawMessages directly instead.
|
|
550
552
|
*/
|
|
551
553
|
export async function resolveActiveCursorImages(
|
|
552
554
|
messages: readonly OcxMessage[] | undefined,
|
|
@@ -645,7 +647,7 @@ async function prepareCursorContentParts(
|
|
|
645
647
|
* Historical messages before this index are left untouched (no decode).
|
|
646
648
|
*/
|
|
647
649
|
export function cursorVisionPrepareStartIndex(messages: readonly OcxMessage[]): number {
|
|
648
|
-
//
|
|
650
|
+
// Default window excludes tool results; their preparation requires explicit opt-in.
|
|
649
651
|
if (messages.at(-1)?.role === "toolResult") return messages.length;
|
|
650
652
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
651
653
|
const role = messages[i]?.role;
|
|
@@ -658,7 +660,8 @@ export function cursorVisionPrepareStartIndex(messages: readonly OcxMessage[]):
|
|
|
658
660
|
* Rewrite image data URLs in the active vision window (last user/developer turn) through
|
|
659
661
|
* the JPEG soft-cap path before protobuf encode. Historical messages are left by
|
|
660
662
|
* reference. Undecodable images become {@link CURSOR_VISION_IMAGE_OMITTED} text so
|
|
661
|
-
* image-only turns stay userMessageAction.
|
|
663
|
+
* image-only turns stay userMessageAction. Opted-in trailing tool results use the
|
|
664
|
+
* same preparation path, with an aggregate image cap and ready-image source labels.
|
|
662
665
|
*/
|
|
663
666
|
export interface PreparedCursorRawMessages {
|
|
664
667
|
messages: readonly OcxMessage[] | undefined;
|
|
@@ -668,10 +671,26 @@ export interface PreparedCursorRawMessages {
|
|
|
668
671
|
export async function prepareCursorRawMessages(
|
|
669
672
|
messages: readonly OcxMessage[] | undefined,
|
|
670
673
|
signal?: AbortSignal,
|
|
674
|
+
options?: { trailingToolImages?: boolean },
|
|
671
675
|
): Promise<PreparedCursorRawMessages> {
|
|
672
676
|
if (!messages?.length) return { messages, images: [] };
|
|
673
677
|
throwIfImagePhaseAborted(signal);
|
|
674
|
-
const
|
|
678
|
+
const trailingToolImages = options?.trailingToolImages === true && messages.at(-1)?.role === "toolResult";
|
|
679
|
+
let prepareFrom = cursorVisionPrepareStartIndex(messages);
|
|
680
|
+
if (trailingToolImages) {
|
|
681
|
+
let imageCount = 0;
|
|
682
|
+
// Count the entire contiguous run before any image URL is decoded or normalized.
|
|
683
|
+
while (prepareFrom > 0) {
|
|
684
|
+
throwIfImagePhaseAborted(signal);
|
|
685
|
+
const message = messages[prepareFrom - 1]!;
|
|
686
|
+
if (message.role !== "toolResult") break;
|
|
687
|
+
prepareFrom--;
|
|
688
|
+
imageCount += extractCursorImageParts(message.content).length;
|
|
689
|
+
if (imageCount > MAX_CURSOR_IMAGES) {
|
|
690
|
+
throw new CursorImageError(`Too many images in one request (max ${MAX_CURSOR_IMAGES}).`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
675
694
|
const active = messages[prepareFrom];
|
|
676
695
|
if (
|
|
677
696
|
active
|
|
@@ -688,10 +707,21 @@ export async function prepareCursorRawMessages(
|
|
|
688
707
|
const message = messages[i]!;
|
|
689
708
|
if (
|
|
690
709
|
i >= prepareFrom
|
|
691
|
-
&& (message.role === "user" || message.role === "developer"
|
|
710
|
+
&& (message.role === "user" || message.role === "developer"
|
|
711
|
+
|| (trailingToolImages && message.role === "toolResult"))
|
|
692
712
|
) {
|
|
693
713
|
const prepared = await prepareCursorContentParts(message.content, signal);
|
|
694
|
-
|
|
714
|
+
if (trailingToolImages && message.role === "toolResult") {
|
|
715
|
+
images.push(...prepared.images.map((image, index) => ({
|
|
716
|
+
...image,
|
|
717
|
+
sourceLabel: `tool result ${i - prepareFrom + 1}, image ${index + 1}: ${JSON.stringify({
|
|
718
|
+
tool: message.toolName.slice(0, 128),
|
|
719
|
+
call_id: message.toolCallId.slice(0, 128),
|
|
720
|
+
})}`,
|
|
721
|
+
})));
|
|
722
|
+
} else {
|
|
723
|
+
images.push(...prepared.images);
|
|
724
|
+
}
|
|
695
725
|
if (prepared.content !== message.content) {
|
|
696
726
|
changed = true;
|
|
697
727
|
out.push({ ...message, content: prepared.content } as OcxMessage);
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "../../lib/translator-budget";
|
|
15
15
|
import { activePromptText, prepareCursorRunRequest } from "./protobuf-request";
|
|
16
16
|
import { prepareCursorRawMessages, resolveActiveCursorImages } from "./images";
|
|
17
|
+
import { isCursorExternalWireModel } from "./discovery";
|
|
17
18
|
import { cursorRequestMessagesFromRaw } from "./request-builder";
|
|
18
19
|
import {
|
|
19
20
|
createCursorContextUsageTracker,
|
|
@@ -618,9 +619,13 @@ class LiveCursorTransport implements CursorTransport {
|
|
|
618
619
|
// JPEG soft-cap rewrite for active-turn data: images before encode. Rebuild text
|
|
619
620
|
// messages from the prepared raw channel so omission markers replace stale
|
|
620
621
|
// pre-rewrite content that activePromptText and the tool filter would otherwise see.
|
|
621
|
-
const
|
|
622
|
+
const externalToolImages = isCursorExternalWireModel(request.modelId)
|
|
623
|
+
&& request.rawMessages?.at(-1)?.role === "toolResult";
|
|
624
|
+
const preparedRaw = await prepareCursorRawMessages(request.rawMessages, signal, {
|
|
625
|
+
trailingToolImages: externalToolImages,
|
|
626
|
+
});
|
|
622
627
|
const preparedRawMessages = preparedRaw.messages;
|
|
623
|
-
const selectedImages = await resolveActiveCursorImages(
|
|
628
|
+
const selectedImages = externalToolImages ? preparedRaw.images : await resolveActiveCursorImages(
|
|
624
629
|
preparedRawMessages,
|
|
625
630
|
signal,
|
|
626
631
|
preparedRaw.images,
|
|
@@ -17,24 +17,11 @@ import {
|
|
|
17
17
|
} from "./gen/agent_pb";
|
|
18
18
|
import { errorText } from "./native-exec-common";
|
|
19
19
|
import type { CursorNativeToolDeps } from "./native-exec-tools";
|
|
20
|
+
import type { DesktopExecutorConfig } from "./desktop-executor-contract";
|
|
20
21
|
|
|
21
22
|
const DEFAULT_DESKTOP_TIMEOUT_MS = 30_000;
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
* Opt-in external executor for computer-use / record-screen. opencodex is a headless proxy and
|
|
25
|
-
* cannot drive a screen itself; set these commands only when running on a host that can. Each
|
|
26
|
-
* command receives the request as JSON on stdin and must print a JSON result on stdout.
|
|
27
|
-
*/
|
|
28
|
-
export interface DesktopExecutorConfig {
|
|
29
|
-
/** Command (run via the platform shell) handling computer-use. Receives `{toolCallId, actions}` on stdin. */
|
|
30
|
-
computerUseCommand?: string;
|
|
31
|
-
/** Command handling record-screen. Receives `{mode, toolCallId, saveAsFilename?}` on stdin. */
|
|
32
|
-
recordScreenCommand?: string;
|
|
33
|
-
cwd?: string;
|
|
34
|
-
env?: Record<string, string>;
|
|
35
|
-
/** Max time to wait for the external process. Default 30s. */
|
|
36
|
-
timeoutMs?: number;
|
|
37
|
-
}
|
|
24
|
+
export type { DesktopExecutorConfig } from "./desktop-executor-contract";
|
|
38
25
|
|
|
39
26
|
/**
|
|
40
27
|
* Build `computerUse` / `recordScreen` deps from external executor commands. Returns `{}` when no
|
|
@@ -379,7 +379,7 @@ function rootPromptMessages(
|
|
|
379
379
|
}
|
|
380
380
|
// Assistant tool CALLS are NOT replayed as a separate visible "[Tool Call]" entry: a model
|
|
381
381
|
// few-shot-mimics that marker and emits later tool calls as inert text (363-B guard in
|
|
382
|
-
// tests/cursor-tool-continuation.test.ts). The invocation is instead named INSIDE the paired
|
|
382
|
+
// tests/providers/cursor/cursor-tool-continuation.test.ts). The invocation is instead named INSIDE the paired
|
|
383
383
|
// "[Tool Result]" envelope below, which carries the same information without a mimickable
|
|
384
384
|
// call template (devlog 260829 002_audit_round2).
|
|
385
385
|
} else if (message.role === "toolResult") {
|
|
@@ -954,7 +954,7 @@ function toolCallArgumentsText(args: Record<string, unknown>): string {
|
|
|
954
954
|
*
|
|
955
955
|
* Why not a separate "[Tool Call]" entry: a model few-shot-mimics that marker and starts emitting
|
|
956
956
|
* later tool calls as inert text instead of real tool frames, which halts multi-tool continuations
|
|
957
|
-
* (363-B guard, tests/cursor-tool-continuation.test.ts). Why it must exist at all: without any
|
|
957
|
+
* (363-B guard, tests/providers/cursor/cursor-tool-continuation.test.ts). Why it must exist at all: without any
|
|
958
958
|
* record of the invocation, the replayed result is orphaned — its `call_id` refers to nothing the
|
|
959
959
|
* model can see — and live cursor/grok-4.6 turns re-ran commands that had already succeeded while
|
|
960
960
|
* narrating a phantom interrupt (devlog 260829 000_rca). A prose line inside the result satisfies
|
|
@@ -1388,11 +1388,21 @@ function buildPreparedCursorRunRequest(
|
|
|
1388
1388
|
)
|
|
1389
1389
|
? "userMessageAction"
|
|
1390
1390
|
: "resumeAction";
|
|
1391
|
-
|
|
1391
|
+
let actionText = externalToolContinuation
|
|
1392
1392
|
? (request.echoRetryContinuationText ?? CURSOR_EXTERNAL_TOOL_CONTINUATION_TEXT)
|
|
1393
1393
|
: request.echoRetryContinuationText
|
|
1394
1394
|
? `${text}\n\n[correction] ${request.echoRetryContinuationText}`
|
|
1395
1395
|
: text;
|
|
1396
|
+
if (lastRawIsToolResult && isCursorExternalWireModel(request.modelId)) {
|
|
1397
|
+
// Image preparation bounds these labels and keeps them in attachment order. The
|
|
1398
|
+
// active action survives root pruning/checkpoint fallback, including echo retries.
|
|
1399
|
+
const sources = selectedImages.flatMap((image, index) => image.sourceLabel
|
|
1400
|
+
? [`${index + 1}. ${image.sourceLabel}`]
|
|
1401
|
+
: []);
|
|
1402
|
+
if (sources.length > 0) {
|
|
1403
|
+
actionText += `\n\n[Client-supplied tool screenshot sources (attachment order)]\n${sources.join("\n")}`;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1396
1406
|
const action = create(ConversationActionSchema, {
|
|
1397
1407
|
action: actionCase === "userMessageAction"
|
|
1398
1408
|
? {
|