@bitkyc08/opencodex 2.10.0 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +77 -0
- package/README.md +4 -10
- package/bin/ocx.mjs +71 -18
- package/gui/dist/assets/index-Cd6_PBKn.css +1 -0
- package/gui/dist/assets/index-ChZQsmBY.js +70 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +8 -4
- package/src/adapters/anthropic.ts +208 -14
- package/src/adapters/base.ts +16 -5
- package/src/adapters/cursor/effort-map.ts +3 -2
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/live-transport.ts +105 -95
- package/src/adapters/cursor/native-exec.ts +32 -6
- package/src/adapters/cursor/protobuf-request.ts +20 -15
- package/src/adapters/cursor/request-builder.ts +21 -7
- package/src/adapters/cursor/types.ts +7 -0
- package/src/adapters/google-antigravity-replay.ts +237 -21
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +50 -9
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +10 -2
- package/src/adapters/kiro-tools.ts +10 -1
- package/src/adapters/kiro.ts +37 -11
- package/src/adapters/openai-chat.ts +284 -83
- package/src/adapters/openai-responses.ts +182 -24
- package/src/bridge.ts +177 -7
- package/src/chat/outbound.ts +78 -23
- package/src/claude/agents-inject.ts +27 -5
- package/src/claude/inbound.ts +11 -1
- package/src/claude/model-info.ts +13 -10
- package/src/claude/outbound.ts +17 -0
- package/src/cli/account-api.ts +24 -0
- package/src/cli/account-auth.ts +31 -6
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +5 -0
- package/src/cli/claude.ts +2 -1
- package/src/cli/doctor.ts +93 -22
- package/src/cli/export-command.ts +26 -12
- package/src/cli/help.ts +8 -6
- package/src/cli/index.ts +56 -22
- package/src/cli/integrations.ts +84 -1
- package/src/cli/observe.ts +54 -1
- package/src/cli/opencode.ts +2 -1
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +1 -1
- package/src/cli/v2.ts +36 -9
- package/src/clients/config-export.ts +687 -10
- package/src/codex/account-lifecycle.ts +30 -5
- package/src/codex/account-usability.ts +22 -2
- package/src/codex/admission.ts +255 -0
- package/src/codex/auth-api.ts +427 -140
- package/src/codex/auth-context.ts +155 -30
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +62 -0
- package/src/codex/catalog/aggregation.ts +14 -1
- package/src/codex/catalog/bundled.ts +282 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/metadata.ts +51 -6
- package/src/codex/catalog/parsing.ts +6 -3
- package/src/codex/catalog/provider-fetch.ts +576 -41
- package/src/codex/catalog/sync.ts +505 -66
- package/src/codex/catalog-admission.ts +197 -0
- package/src/codex/catalog-write-serialization.ts +241 -0
- package/src/codex/catalog.ts +2 -1
- package/src/codex/codex-write-lock.ts +372 -0
- package/src/codex/convergence-types.ts +593 -0
- package/src/codex/convergence.ts +441 -0
- package/src/codex/desired-state.ts +177 -0
- package/src/codex/features.ts +52 -8
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +257 -0
- package/src/codex/history-lock.ts +241 -0
- package/src/codex/history-migration-guardian.ts +18 -5
- package/src/codex/history-provider.ts +9 -2
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +176 -0
- package/src/codex/inject-coordination.ts +245 -0
- package/src/codex/inject.ts +605 -124
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +10 -1
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/management-convergence.ts +114 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +167 -0
- package/src/codex/native-main-lock-file.ts +158 -0
- package/src/codex/native-main-owner.ts +315 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1512 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +340 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +557 -0
- package/src/codex/project-config-warnings.ts +18 -4
- package/src/codex/prompt-journal.ts +311 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +224 -0
- package/src/codex/quota.ts +86 -3
- package/src/codex/routing.ts +299 -62
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +39 -13
- package/src/codex/subagent-model-fallback.ts +73 -12
- package/src/codex/transition-state.ts +604 -0
- package/src/codex/upstream-host-health.ts +70 -0
- package/src/codex/user-identity.ts +266 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/config.ts +562 -26
- package/src/generated/jawcode-model-metadata.ts +2 -2
- package/src/grok/inject.ts +15 -4
- package/src/grok/inspect.ts +45 -0
- package/src/images/loop.ts +113 -20
- package/src/integrations/config-io.ts +151 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/native/ownership-preflight.ts +165 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +101 -0
- package/src/integrations/serialize.ts +235 -0
- package/src/integrations/state.ts +290 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer.ts +492 -0
- package/src/lib/bounded-body.ts +46 -8
- package/src/lib/bun-runtime.ts +110 -1
- package/src/lib/bun-stream-caps.ts +2 -1
- package/src/lib/redact.ts +407 -2
- package/src/lib/shadow-call.ts +24 -0
- package/src/lib/translator-budget.ts +10 -0
- package/src/lib/upstream-reachability.ts +91 -0
- package/src/lib/upstream-retry.ts +154 -2
- package/src/lib/windows-secret-acl.ts +212 -11
- package/src/lib/winsw.ts +9 -3
- package/src/oauth/index.ts +61 -3
- package/src/oauth/key-providers.ts +4 -0
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/store.ts +31 -6
- package/src/oauth/token-guardian.ts +9 -3
- package/src/providers/codex-capacity.ts +288 -0
- package/src/providers/derive.ts +33 -1
- package/src/providers/free-directory.ts +3 -1
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/openai-sidecar.ts +64 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/quota.ts +334 -26
- package/src/providers/registry.ts +284 -16
- package/src/providers/xai-transport.ts +11 -4
- package/src/responses/compaction.ts +8 -1
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/parser.ts +44 -2
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +105 -0
- package/src/responses/spill-store.ts +45 -8
- package/src/responses/state.ts +161 -17
- package/src/router.ts +302 -16
- package/src/routing/analytics.ts +377 -0
- package/src/routing/capability.ts +204 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +444 -0
- package/src/routing/health.ts +401 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +590 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile.ts +423 -0
- package/src/routing/quota.ts +124 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +686 -0
- package/src/server/auth-cors.ts +46 -6
- package/src/server/chat-completions.ts +28 -13
- package/src/server/claude-messages.ts +23 -15
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +10 -1
- package/src/server/index.ts +238 -52
- package/src/server/lifecycle.ts +228 -9
- package/src/server/live.ts +14 -3
- package/src/server/management/agent-settings-routes.ts +64 -14
- package/src/server/management/combo-routes.ts +5 -5
- package/src/server/management/config-routes.ts +1 -1
- package/src/server/management/context.ts +42 -2
- package/src/server/management/integration-routes.ts +538 -0
- package/src/server/management/logs-usage-routes.ts +1 -1
- package/src/server/management/model-routes.ts +32 -113
- package/src/server/management/model-rows.ts +117 -0
- package/src/server/management/native-integration-routes.ts +587 -0
- package/src/server/management/oauth-account-routes.ts +1 -1
- package/src/server/management/provider-routes.ts +218 -117
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +177 -0
- package/src/server/management/shared.ts +2 -2
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +276 -24
- package/src/server/management/system-routes.ts +4 -0
- package/src/server/management-api.ts +84 -9
- package/src/server/management-auth.ts +43 -5
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +120 -6
- package/src/server/request-log.ts +26 -6
- package/src/server/responses/collaboration.ts +63 -8
- package/src/server/responses/compact.ts +272 -41
- package/src/server/responses/core.ts +730 -132
- package/src/server/responses/fetch-helpers.ts +15 -1
- package/src/server/responses-item-id-repair.ts +32 -3
- package/src/server/responses-json-events.ts +52 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/search.ts +51 -6
- package/src/server/sse-payload-rewrite.ts +89 -12
- package/src/server/startup-health-cache.ts +7 -1
- package/src/server/ws-bridge.ts +11 -17
- package/src/service-manager-probe.ts +297 -0
- package/src/service.ts +222 -32
- package/src/tray/windows-tray.ps1 +9 -0
- package/src/tray/windows.ts +15 -7
- package/src/types.ts +194 -14
- package/src/update/index.ts +13 -13
- package/src/update/job.ts +24 -21
- package/src/update/notify.ts +7 -3
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +129 -10
- package/src/usage/log.ts +50 -15
- package/src/usage/summary.ts +4 -4
- package/src/vision/index.ts +6 -1
- package/src/web-search/loop.ts +161 -34
- package/gui/dist/assets/index-OY43ubAq.css +0 -1
- package/gui/dist/assets/index-YwNnKZcL.js +0 -67
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
|
@@ -4,6 +4,7 @@ import { namespacedToolName, type AdapterEvent, type OcxParsedRequest, type OcxP
|
|
|
4
4
|
import { catalogModelSupportsReasoningSummaries } from "../codex/catalog";
|
|
5
5
|
import { COMPACT_PROMPT, decodeCompactionSummary, SUMMARY_PREFIX } from "../responses/compaction";
|
|
6
6
|
import { collectResponsesToolGroups } from "../responses/tool-groups";
|
|
7
|
+
import { isHostedToolUnsupportedForModel } from "../responses/hosted-tool-policy";
|
|
7
8
|
import { decodeServerSentEvents } from "../lib/sse-decoder";
|
|
8
9
|
import { isCanonicalOpenAiForwardProvider } from "../providers/openai-tiers";
|
|
9
10
|
import { OCX_REASONING_PREFIX } from "../responses/reasoning-envelope";
|
|
@@ -32,7 +33,10 @@ export const FORWARD_HEADERS = [
|
|
|
32
33
|
"x-responsesapi-include-timing-metrics",
|
|
33
34
|
];
|
|
34
35
|
|
|
35
|
-
export function sanitizeReasoningInputContent(
|
|
36
|
+
export function sanitizeReasoningInputContent(
|
|
37
|
+
body: unknown,
|
|
38
|
+
opts?: { preserveRawReasoningContent?: boolean },
|
|
39
|
+
): unknown {
|
|
36
40
|
if (!body || typeof body !== "object" || Array.isArray(body)) return body;
|
|
37
41
|
const raw = body as Record<string, unknown>;
|
|
38
42
|
if (!Array.isArray(raw.input)) return body;
|
|
@@ -47,13 +51,23 @@ export function sanitizeReasoningInputContent(body: unknown): unknown {
|
|
|
47
51
|
// backend cannot decrypt them and would reject the request. Strip regardless of content shape.
|
|
48
52
|
const hasOcxEnvelope = typeof rec.encrypted_content === "string" && rec.encrypted_content.startsWith(OCX_REASONING_PREFIX);
|
|
49
53
|
if (!hasRawContent && !hasOcxEnvelope) return item;
|
|
50
|
-
|
|
54
|
+
if (hasOcxEnvelope) {
|
|
55
|
+
changed = true;
|
|
56
|
+
const next: Record<string, unknown> = { ...rec };
|
|
57
|
+
delete next.encrypted_content;
|
|
58
|
+
if (!opts?.preserveRawReasoningContent) next.content = [];
|
|
59
|
+
return next;
|
|
60
|
+
}
|
|
51
61
|
// Routed models can produce raw `reasoning_text` output items. Codex echoes those in later
|
|
52
62
|
// native GPT requests, but ChatGPT's Responses backend accepts reasoning input only with empty
|
|
53
63
|
// `content`; keep summaries/ids and drop the raw content so native passthrough does not 400.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
// DeepSeek's Responses API instead ACCEPTS plaintext reasoning replay (its compatibility
|
|
65
|
+
// guide merges reasoning items into the adjacent assistant message), so providers flagged
|
|
66
|
+
// `preserveResponsesReasoningContent` keep it — deleting valid replay content there breaks
|
|
67
|
+
// continuations after tool calls (issue #875 family).
|
|
68
|
+
if (opts?.preserveRawReasoningContent) return item;
|
|
69
|
+
changed = true;
|
|
70
|
+
return { ...rec, content: [] };
|
|
57
71
|
});
|
|
58
72
|
|
|
59
73
|
return changed ? { ...raw, input } : body;
|
|
@@ -148,18 +162,6 @@ function scrubOcxCompactionItems(body: unknown): unknown {
|
|
|
148
162
|
return changed ? { ...body, input } : body;
|
|
149
163
|
}
|
|
150
164
|
|
|
151
|
-
/**
|
|
152
|
-
* Hosted (OpenAI-executed) tool types that specific native slugs reject at request time. Codex
|
|
153
|
-
* attaches these for app skills (e.g. `image_generation` for imagegen) regardless of the target
|
|
154
|
-
* model, and the passthrough path forwards the raw body untouched — so a slug that doesn't support
|
|
155
|
-
* the tool 400s (`Tool 'image_generation' is not supported with gpt-5.3-codex-spark.`). Each entry
|
|
156
|
-
* maps a model-slug matcher to the hosted tool types that must be stripped before forwarding.
|
|
157
|
-
* Extend this when another native slug rejects a hosted tool (e.g. `code_interpreter`).
|
|
158
|
-
*/
|
|
159
|
-
const UNSUPPORTED_HOSTED_TOOLS: ReadonlyArray<{ match: (model: string) => boolean; tools: ReadonlySet<string> }> = [
|
|
160
|
-
{ match: model => model.includes("codex-spark"), tools: new Set(["image_generation", "tool_search"]) },
|
|
161
|
-
];
|
|
162
|
-
|
|
163
165
|
/**
|
|
164
166
|
* Strip unsupported `reasoning` sub-parameters for native slugs that reject them (e.g. Spark).
|
|
165
167
|
* codex-rs injects `reasoning.context` and `reasoning.summary` based on catalog flags; Spark's
|
|
@@ -471,6 +473,33 @@ function toolOutputText(output: unknown): string {
|
|
|
471
473
|
* reasoning chain is intact and must be preserved.
|
|
472
474
|
* Runs on every forward request; with intact pairs it returns the original reference.
|
|
473
475
|
*/
|
|
476
|
+
/**
|
|
477
|
+
* Backfill `queries` on a replayed single-query `web_search_call`.
|
|
478
|
+
*
|
|
479
|
+
* `webSearchAction()` in the bridge now emits both keys, but that only helps items
|
|
480
|
+
* created after the fix. A conversation that already recorded
|
|
481
|
+
* `{type:"search", query:"..."}` replays that stored item on every subsequent turn, and
|
|
482
|
+
* DeepSeek's native Responses parser requires `queries` — so upgrading alone leaves
|
|
483
|
+
* those threads permanently 400ing with `missing field 'queries'` (#930).
|
|
484
|
+
*
|
|
485
|
+
* Runs on every Responses request, on both `input` items and the `action` nested inside
|
|
486
|
+
* them. Returns the original reference when nothing needs repair, so the common path
|
|
487
|
+
* allocates nothing.
|
|
488
|
+
*/
|
|
489
|
+
function backfillWebSearchQueries(body: unknown): unknown {
|
|
490
|
+
if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
|
|
491
|
+
let changed = false;
|
|
492
|
+
const input = body.input.map(item => {
|
|
493
|
+
if (!isPlainObject(item) || item.type !== "web_search_call") return item;
|
|
494
|
+
const action = item.action;
|
|
495
|
+
if (!isPlainObject(action) || action.type !== "search") return item;
|
|
496
|
+
if (typeof action.query !== "string" || Array.isArray(action.queries)) return item;
|
|
497
|
+
changed = true;
|
|
498
|
+
return { ...item, action: { ...action, queries: [action.query] } };
|
|
499
|
+
});
|
|
500
|
+
return changed ? { ...body, input } : body;
|
|
501
|
+
}
|
|
502
|
+
|
|
474
503
|
function repairOrphanedInputItems(body: unknown, dropReasoning: boolean): unknown {
|
|
475
504
|
if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
|
|
476
505
|
const input = body.input;
|
|
@@ -608,6 +637,128 @@ function declaresImageGenClientTool(tool: unknown): boolean {
|
|
|
608
637
|
return isImageGenClientName(tool.name);
|
|
609
638
|
}
|
|
610
639
|
|
|
640
|
+
/** Rewrite client image-gen selectors to the hosted tool without widening caller restrictions. */
|
|
641
|
+
function preferHostedImageGenToolChoice(toolChoice: unknown): unknown {
|
|
642
|
+
if (!isPlainObject(toolChoice)) return toolChoice;
|
|
643
|
+
if ((toolChoice.type === "function" || toolChoice.type === "custom") && typeof toolChoice.name === "string") {
|
|
644
|
+
return isImageGenClientName(toolChoice.name) ? { type: HOSTED_IMAGE_GENERATION_TOOL } : toolChoice;
|
|
645
|
+
}
|
|
646
|
+
if (toolChoice.type !== "allowed_tools" || !Array.isArray(toolChoice.tools)) return toolChoice;
|
|
647
|
+
const hasHostedImageTool = toolChoice.tools.some(tool => isPlainObject(tool) && tool.type === HOSTED_IMAGE_GENERATION_TOOL);
|
|
648
|
+
let changed = false;
|
|
649
|
+
let addedHostedImageTool = false;
|
|
650
|
+
const tools: unknown[] = [];
|
|
651
|
+
for (const tool of toolChoice.tools) {
|
|
652
|
+
const isClientImageTool = isPlainObject(tool)
|
|
653
|
+
&& (tool.type === "function" || tool.type === "custom")
|
|
654
|
+
&& typeof tool.name === "string"
|
|
655
|
+
&& isImageGenClientName(tool.name);
|
|
656
|
+
if (!isClientImageTool) {
|
|
657
|
+
tools.push(tool);
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
changed = true;
|
|
661
|
+
if (!hasHostedImageTool && !addedHostedImageTool) {
|
|
662
|
+
tools.push({ type: HOSTED_IMAGE_GENERATION_TOOL });
|
|
663
|
+
addedHostedImageTool = true;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return changed ? { ...toolChoice, tools } : toolChoice;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Some Responses-compatible gateways reserve the hosted image namespace even when the request
|
|
671
|
+
* does not explicitly declare `image_generation`. For an explicitly configured model, remove only
|
|
672
|
+
* colliding client declarations so the gateway's hosted tool can take precedence.
|
|
673
|
+
*/
|
|
674
|
+
function preferConfiguredHostedTools(
|
|
675
|
+
body: unknown,
|
|
676
|
+
provider: OcxProviderConfig,
|
|
677
|
+
modelId: string,
|
|
678
|
+
selectedModelId?: string,
|
|
679
|
+
): unknown {
|
|
680
|
+
// A virtual model's advertised id takes precedence over its resolved wire-model id.
|
|
681
|
+
// Read own properties only: a routed model id of `constructor`/`toString` would
|
|
682
|
+
// otherwise resolve to an inherited Object.prototype function and throw on the
|
|
683
|
+
// membership test below, failing the request before it is dispatched.
|
|
684
|
+
const preferenceMap = provider.modelPreferHostedTools;
|
|
685
|
+
const ownPreference = (key: string | undefined): string[] | undefined => {
|
|
686
|
+
if (!key || !preferenceMap || !Object.prototype.hasOwnProperty.call(preferenceMap, key)) return undefined;
|
|
687
|
+
const entry = preferenceMap[key];
|
|
688
|
+
return Array.isArray(entry) ? entry : undefined;
|
|
689
|
+
};
|
|
690
|
+
const preferredTools = ownPreference(selectedModelId) ?? ownPreference(modelId);
|
|
691
|
+
if (!preferredTools?.includes(HOSTED_IMAGE_GENERATION_TOOL) || !isPlainObject(body)) return body;
|
|
692
|
+
|
|
693
|
+
const stripGroup = (tools: unknown[]): unknown[] => {
|
|
694
|
+
const filtered = tools.filter(tool => !declaresImageGenClientTool(tool));
|
|
695
|
+
return filtered.length === tools.length ? tools : filtered;
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
let changed = false;
|
|
699
|
+
let tools = body.tools;
|
|
700
|
+
let strippedTopLevelImageGenTool = false;
|
|
701
|
+
if (Array.isArray(body.tools)) {
|
|
702
|
+
tools = stripGroup(body.tools);
|
|
703
|
+
strippedTopLevelImageGenTool = tools !== body.tools;
|
|
704
|
+
changed ||= strippedTopLevelImageGenTool;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
let input = body.input;
|
|
708
|
+
const strippedAdditionalToolsIndices = new Set<number>();
|
|
709
|
+
if (Array.isArray(body.input)) {
|
|
710
|
+
let nestedChanged = false;
|
|
711
|
+
const mappedInput = body.input.map((item, index) => {
|
|
712
|
+
if (!isPlainObject(item) || item.type !== "additional_tools" || !Array.isArray(item.tools)) return item;
|
|
713
|
+
const nestedTools = stripGroup(item.tools);
|
|
714
|
+
if (nestedTools === item.tools) return item;
|
|
715
|
+
strippedAdditionalToolsIndices.add(index);
|
|
716
|
+
nestedChanged = true;
|
|
717
|
+
return { ...item, tools: nestedTools };
|
|
718
|
+
});
|
|
719
|
+
if (nestedChanged) {
|
|
720
|
+
input = mappedInput;
|
|
721
|
+
changed = true;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const hasToolChoice = Object.hasOwn(body, "tool_choice");
|
|
726
|
+
const toolChoice = hasToolChoice ? preferHostedImageGenToolChoice(body.tool_choice) : body.tool_choice;
|
|
727
|
+
const toolChoiceChanged = hasToolChoice && toolChoice !== body.tool_choice;
|
|
728
|
+
const hasHostedImageGenTool = (toolGroup: unknown): boolean => Array.isArray(toolGroup)
|
|
729
|
+
&& toolGroup.some(tool => isPlainObject(tool) && tool.type === HOSTED_IMAGE_GENERATION_TOOL);
|
|
730
|
+
const hasHostedImageGenDeclaration = hasHostedImageGenTool(tools)
|
|
731
|
+
|| (Array.isArray(input) && input.some(item => isPlainObject(item)
|
|
732
|
+
&& item.type === "additional_tools"
|
|
733
|
+
&& hasHostedImageGenTool(item.tools)));
|
|
734
|
+
if ((strippedTopLevelImageGenTool || strippedAdditionalToolsIndices.size > 0) && !hasHostedImageGenDeclaration) {
|
|
735
|
+
if (strippedTopLevelImageGenTool && Array.isArray(tools)) {
|
|
736
|
+
tools = [...tools, { type: HOSTED_IMAGE_GENERATION_TOOL }];
|
|
737
|
+
} else if (strippedAdditionalToolsIndices.size > 0 && Array.isArray(input)) {
|
|
738
|
+
// Restore into the FIRST stripped container only. Tool declarations are
|
|
739
|
+
// request-scoped, not container-scoped — the containers are separate carriers for
|
|
740
|
+
// one tool set, so a single hosted declaration covers the request. An earlier
|
|
741
|
+
// revision restored into every stripped container and put `image_generation` on
|
|
742
|
+
// the wire twice; review caught it.
|
|
743
|
+
const firstStripped = Math.min(...strippedAdditionalToolsIndices);
|
|
744
|
+
input = input.map((item, index) => index === firstStripped
|
|
745
|
+
&& isPlainObject(item)
|
|
746
|
+
&& Array.isArray(item.tools)
|
|
747
|
+
? { ...item, tools: [...item.tools, { type: HOSTED_IMAGE_GENERATION_TOOL }] }
|
|
748
|
+
: item);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
changed ||= toolChoiceChanged;
|
|
752
|
+
if (!changed) return body;
|
|
753
|
+
const next: Record<string, unknown> = {
|
|
754
|
+
...body,
|
|
755
|
+
...(Array.isArray(body.tools) ? { tools } : {}),
|
|
756
|
+
...(Array.isArray(body.input) ? { input } : {}),
|
|
757
|
+
};
|
|
758
|
+
if (toolChoiceChanged) next.tool_choice = toolChoice;
|
|
759
|
+
return next;
|
|
760
|
+
}
|
|
761
|
+
|
|
611
762
|
/**
|
|
612
763
|
* Lower one complete Codex image-gen namespace to public Responses function tools.
|
|
613
764
|
*
|
|
@@ -866,13 +1017,9 @@ function normalizeImageGenClientTools(body: unknown): unknown {
|
|
|
866
1017
|
function stripUnsupportedHostedTools(body: unknown): unknown {
|
|
867
1018
|
if (!isPlainObject(body) || !Array.isArray(body.tools)) return body;
|
|
868
1019
|
const model = typeof body.model === "string" ? body.model : "";
|
|
869
|
-
const unsupported = UNSUPPORTED_HOSTED_TOOLS.filter(e => e.match(model));
|
|
870
|
-
if (unsupported.length === 0) return body;
|
|
871
|
-
|
|
872
1020
|
const tools = body.tools.filter(t => {
|
|
873
1021
|
const type = isPlainObject(t) && typeof t.type === "string" ? t.type : undefined;
|
|
874
|
-
|
|
875
|
-
return !unsupported.some(e => e.tools.has(type));
|
|
1022
|
+
return !type || !isHostedToolUnsupportedForModel(model, type);
|
|
876
1023
|
});
|
|
877
1024
|
return tools.length === body.tools.length ? body : { ...body, tools };
|
|
878
1025
|
}
|
|
@@ -1012,19 +1159,30 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
1012
1159
|
}
|
|
1013
1160
|
if (forward) {
|
|
1014
1161
|
outBody = stripUnsupportedForwardParams(outBody);
|
|
1162
|
+
} else {
|
|
1163
|
+
outBody = preferConfiguredHostedTools(
|
|
1164
|
+
outBody,
|
|
1165
|
+
provider,
|
|
1166
|
+
parsed.modelId,
|
|
1167
|
+
parsed._openAiVirtualSelectedModelId,
|
|
1168
|
+
);
|
|
1169
|
+
outBody = normalizeImageGenClientTools(outBody);
|
|
1015
1170
|
}
|
|
1016
|
-
else outBody = normalizeImageGenClientTools(outBody);
|
|
1017
1171
|
if (forward || parsed._previousResponseInputExpanded === true) {
|
|
1018
1172
|
outBody = repairOversizedReplayCallIds(outBody);
|
|
1019
1173
|
}
|
|
1020
1174
|
outBody = stripUnsupportedReasoningSummaryDelivery(outBody, parsed.modelId);
|
|
1175
|
+
// Repair stored history from before the bridge emitted both keys: a conversation
|
|
1176
|
+
// that already recorded a single-query web_search_call replays it every turn, and
|
|
1177
|
+
// a strict parser rejects the whole request over it (#930).
|
|
1178
|
+
outBody = backfillWebSearchQueries(outBody);
|
|
1021
1179
|
// Same predicate as the routedCompaction gate in handleResponses(): an
|
|
1022
1180
|
// authMode check would let a noncanonical custom forward provider skip this
|
|
1023
1181
|
// rewrite while the server still routes it as a summarizer turn (#422).
|
|
1024
1182
|
if (parsed._compactionRequest === true && !isCanonicalOpenAiForwardProvider(provider)) {
|
|
1025
1183
|
outBody = buildRoutedCompactionBody(outBody);
|
|
1026
1184
|
}
|
|
1027
|
-
const sanitizedBody = normalizeToolSchemas(stripSparkCompatibility(stripUnsupportedReasoningParams(stripItemIdsWhenUnstored(stripInvalidItemIds(stripUnsupportedHostedTools(sanitizeReasoningInputContent(scrubOcxCompactionItems(outBody))))))));
|
|
1185
|
+
const sanitizedBody = normalizeToolSchemas(stripSparkCompatibility(stripUnsupportedReasoningParams(stripItemIdsWhenUnstored(stripInvalidItemIds(stripUnsupportedHostedTools(sanitizeReasoningInputContent(scrubOcxCompactionItems(outBody), { preserveRawReasoningContent: provider.preserveResponsesReasoningContent === true })))))));
|
|
1028
1186
|
const body = JSON.stringify(stripDisabledReasoningSummaries(
|
|
1029
1187
|
normalizeConfiguredReasoningSummaryDelivery(sanitizedBody, provider, parsed.modelId),
|
|
1030
1188
|
provider,
|