@bitkyc08/opencodex 2.59.0 → 2.61.0-preview.20260922
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 +64 -0
- package/README.md +28 -1
- package/bin/ocx.mjs +382 -209
- package/gui/dist/assets/App-E64Rzjap.js +50 -0
- package/gui/dist/assets/Tray-_nfzD8k4.js +1 -0
- package/gui/dist/assets/index-DpdfZWMK.js +86 -0
- package/gui/dist/assets/index-_bpvxJu0.css +1 -0
- package/gui/dist/assets/usage-companion-chart-DtoK7T6h.js +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/stepfun-color.svg +1 -0
- package/package.json +8 -1
- package/src/adapters/anthropic-image-codec.ts +16 -2
- package/src/adapters/anthropic-image-normalize.ts +49 -2
- package/src/adapters/anthropic.ts +20 -1
- package/src/adapters/base.ts +23 -0
- package/src/adapters/coding-agent/protocol.ts +36 -6
- package/src/adapters/coding-agent/turn.ts +32 -4
- package/src/adapters/command-code.ts +52 -4
- package/src/adapters/cursor/catalog.ts +51 -7
- package/src/adapters/cursor/checkpoint-store.ts +3 -0
- package/src/adapters/cursor/discovery.ts +11 -8
- package/src/adapters/cursor/live-transport.ts +26 -9
- package/src/adapters/cursor/protobuf-request.ts +6 -3
- package/src/adapters/cursor/request-builder.ts +20 -4
- package/src/adapters/cursor/transport.ts +19 -0
- package/src/adapters/cursor.ts +14 -3
- package/src/adapters/declaration-carrier.ts +45 -0
- package/src/adapters/devin/cloud-direct/chat.ts +3 -1
- package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +42 -5
- package/src/adapters/devin.ts +125 -36
- package/src/adapters/google-antigravity-replay.ts +1 -1
- package/src/adapters/google-antigravity-wire.ts +34 -7
- package/src/adapters/google-errors.ts +7 -1
- package/src/adapters/google-http.ts +49 -10
- package/src/adapters/google-tool-schema.ts +595 -31
- package/src/adapters/google-wire-compiler.ts +93 -10
- package/src/adapters/google-wire-shape.ts +461 -0
- package/src/adapters/google.ts +66 -11
- package/src/adapters/image.ts +4 -1
- package/src/adapters/input-media-guard.ts +21 -9
- package/src/adapters/kiro/usage.ts +3 -2
- package/src/adapters/kiro-tool-fallback.ts +1 -1
- package/src/adapters/ollama-native.ts +6 -0
- package/src/adapters/openai-chat/developer-role.ts +61 -0
- package/src/adapters/openai-chat/messages.ts +46 -27
- package/src/adapters/openai-chat/parallel-tool-calls.ts +32 -0
- package/src/adapters/openai-chat/passthrough.ts +33 -9
- package/src/adapters/openai-chat/reasoning-wire.ts +89 -0
- package/src/adapters/openai-chat-images.ts +3 -1
- package/src/adapters/openai-chat.ts +23 -58
- package/src/adapters/openai-responses/image-gen.ts +8 -6
- package/src/adapters/openai-responses/passthrough.ts +17 -3
- package/src/adapters/openai-responses/reasoning.ts +7 -0
- package/src/adapters/opencode-go-additional-tools.ts +12 -2
- package/src/adapters/registry.ts +3 -2
- package/src/adapters/run-turn-queue.ts +178 -29
- package/src/adapters/xai-web-search.ts +16 -1
- package/src/bridge/errors.ts +8 -2
- package/src/bridge/response-json.ts +9 -1
- package/src/bridge/sse.ts +13 -151
- package/src/chat/inbound.ts +141 -5
- package/src/claude/desktop-3p.ts +7 -1
- package/src/claude/desktop-first-party.ts +183 -0
- package/src/claude/desktop-gateway-state.ts +41 -0
- package/src/claude/inbound-content-options.ts +6 -0
- package/src/claude/inbound.ts +32 -6
- package/src/claude/intercept/connect-proxy.ts +179 -0
- package/src/claude/intercept/listener.ts +122 -0
- package/src/claude/intercept/local-ca.ts +298 -0
- package/src/claude/intercept/runtime.ts +98 -0
- package/src/claude/intercept/settings.ts +189 -0
- package/src/cli/access.ts +87 -0
- package/src/cli/account-auth.ts +19 -0
- package/src/cli/account-extended.ts +4 -4
- package/src/cli/capabilities.ts +31 -0
- package/src/cli/claude-desktop.ts +206 -16
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/companion.ts +56 -0
- package/src/cli/dispatch.ts +46 -7
- package/src/cli/doctor.ts +28 -9
- package/src/cli/ensure-desired-integrations.ts +43 -5
- package/src/cli/help.ts +7 -9
- package/src/cli/hub.ts +3 -2
- package/src/cli/index.ts +203 -59
- package/src/cli/init.ts +8 -0
- package/src/cli/integrations.ts +7 -1
- package/src/cli/opencode.ts +2 -2
- package/src/cli/provider.ts +13 -1
- package/src/cli/registry.ts +41 -2
- package/src/cli/resolve.ts +230 -0
- package/src/cli/root.ts +24 -1
- package/src/cli/start-ownership-publication.ts +56 -0
- package/src/cli/status-probes.ts +2 -18
- package/src/cli/status.ts +62 -0
- package/src/cli/stop-report.ts +143 -0
- package/src/cli/uninstall-plan.ts +9 -0
- package/src/client/machine-api.ts +2 -2
- package/src/client/machine-listener.ts +4 -7
- package/src/client/runtime.ts +26 -2
- package/src/clients/aside-profiles.ts +4 -0
- package/src/clients/config-export/zcode-store.ts +157 -0
- package/src/clients/config-export.ts +36 -0
- package/src/codex/account-store.ts +65 -0
- package/src/codex/app-server-processes.ts +72 -40
- package/src/codex/auth-api/account-list.ts +19 -11
- package/src/codex/auth-api/login-flow.ts +6 -1
- package/src/codex/auth-api/pool-quota-probe.ts +30 -7
- package/src/codex/autostart-health.ts +28 -0
- package/src/codex/catalog/build-entries.ts +2 -2
- package/src/codex/catalog/effort.ts +3 -3
- package/src/codex/catalog/gather-capture.ts +21 -2
- package/src/codex/catalog/model-hints.ts +29 -28
- package/src/codex/catalog/parsing.ts +7 -0
- package/src/codex/catalog/provider-models.ts +43 -17
- package/src/codex/catalog/retained-sync.ts +24 -28
- package/src/codex/catalog/routed-gather.ts +19 -0
- package/src/codex/context-compat.ts +5 -2
- package/src/codex/convergence.ts +2 -2
- package/src/codex/desired-state.ts +4 -1
- package/src/codex/history-job.ts +6 -6
- package/src/codex/history-provider.ts +31 -166
- package/src/codex/history-rollout-read.ts +174 -0
- package/src/codex/inject/config-toml.ts +41 -6
- package/src/codex/inject/paginated-openai-compat.ts +90 -0
- package/src/codex/inject.ts +18 -15
- package/src/codex/injected-marker.ts +18 -0
- package/src/codex/internal/catalog-writer.ts +33 -1
- package/src/codex/main-account.ts +6 -0
- package/src/codex/model-cache.ts +99 -6
- package/src/codex/model-entitlement-admission.ts +59 -0
- package/src/codex/model-entitlements.ts +116 -54
- package/src/codex/native-main-admission.ts +83 -0
- package/src/codex/observed-model-denials.ts +101 -8
- package/src/codex/prompt-text-probe.ts +9 -6
- package/src/codex/routing/health-store.ts +39 -0
- package/src/codex/routing/selection.ts +37 -1
- package/src/codex/routing.ts +12 -42
- package/src/codex/shim-templates.ts +29 -3
- package/src/codex/shim.ts +1 -1
- package/src/codex/subagent-model-fallback.ts +22 -4
- package/src/combos/failover.ts +3 -0
- package/src/companion/settings.ts +132 -0
- package/src/config/admitted-identity.ts +222 -0
- package/src/config/atomic-write.ts +117 -5
- package/src/config/diagnostics.ts +22 -1
- package/src/config/feature-flags.ts +5 -0
- package/src/config/load-degrade.ts +52 -7
- package/src/config/process-state.ts +1 -1
- package/src/config/proxy-env.ts +8 -2
- package/src/config/schema/compaction-triggers.ts +11 -0
- package/src/config/schema/config-schema.ts +31 -1
- package/src/config/schema/leaf-validators.ts +59 -0
- package/src/config.ts +3 -3
- package/src/generated/compatibility-version.json +604 -280
- package/src/grok/reset-coupons.ts +38 -19
- package/src/images/loop.ts +6 -1
- package/src/integrations/aside-profile-context.ts +37 -3
- package/src/integrations/aside-profile-journal.ts +68 -3
- package/src/integrations/aside-profiles.ts +128 -3
- package/src/integrations/config-io.ts +44 -10
- package/src/integrations/merge.ts +120 -13
- package/src/integrations/mutation-plan.ts +921 -0
- package/src/integrations/registry.ts +38 -0
- package/src/integrations/state.ts +78 -45
- package/src/integrations/target.ts +208 -0
- package/src/integrations/writer.ts +134 -110
- package/src/lab/conformance/fixture-provider.ts +5 -0
- package/src/lab/live/transport.ts +4 -0
- package/src/lab/live/types.ts +5 -0
- package/src/lab/subject/behavior-fingerprint.ts +1 -1
- package/src/lib/admin-secrets.ts +9 -1
- package/src/lib/browser-launch-notice.ts +59 -0
- package/src/lib/bun-runtime.ts +6 -2
- package/src/lib/debug-log-buffer.ts +6 -1
- package/src/lib/debug.ts +63 -0
- package/src/lib/errors.ts +79 -0
- package/src/lib/http-response-semantics.ts +57 -0
- package/src/lib/lab-live-pinned-sender.ts +26 -12
- package/src/lib/open-url.ts +51 -7
- package/src/lib/package-tree-integrity.ts +2 -1
- package/src/lib/package-version.ts +8 -0
- package/src/lib/pinned-http.ts +142 -2
- package/src/lib/plain-data.ts +103 -0
- package/src/lib/process-control.ts +13 -5
- package/src/lib/provider-egress.ts +310 -0
- package/src/lib/provider-outbound.ts +109 -16
- package/src/lib/proxy-env.ts +82 -7
- package/src/lib/request-execution-budget.ts +72 -0
- package/src/lib/request-failure-attribution.ts +183 -0
- package/src/lib/request-failure-model.ts +236 -0
- package/src/lib/request-resend-gate.ts +138 -0
- package/src/lib/socks5-fetch.ts +136 -26
- package/src/lib/spend-ledger-owner.ts +364 -0
- package/src/lib/spend-reservation-ledger.ts +218 -27
- package/src/lib/standalone.ts +16 -0
- package/src/lib/upstream-retry.ts +167 -16
- package/src/lib/windows-system-proxy.ts +16 -11
- package/src/lib/winsw.ts +2 -2
- package/src/oauth/callback-server.ts +4 -3
- package/src/oauth/generic-account-failover.ts +1 -0
- package/src/oauth/health.ts +12 -1
- package/src/oauth/index.ts +27 -108
- package/src/oauth/login-cli.ts +80 -29
- package/src/oauth/login-flow-state.ts +127 -0
- package/src/providers/api-key-resolve.ts +133 -0
- package/src/providers/api-key-selection.ts +5 -1
- package/src/providers/derive.ts +34 -17
- package/src/providers/devin-cli-authmode-migration.ts +14 -10
- package/src/providers/key-failover.ts +97 -19
- package/src/providers/key-store.ts +34 -110
- package/src/providers/model-rename-fields.ts +147 -0
- package/src/providers/model-rename-migration.ts +179 -38
- package/src/providers/model-rename-startup.ts +7 -5
- package/src/providers/openai-virtual-models.ts +42 -2
- package/src/providers/quota/antigravity.ts +22 -2
- package/src/providers/quota/vendor-probes-key.ts +38 -23
- package/src/providers/reasoning-metadata.ts +43 -18
- package/src/providers/registry/entries-core.ts +47 -20
- package/src/providers/registry/entries-extended.ts +63 -4
- package/src/providers/registry/model-ids.ts +168 -0
- package/src/providers/registry/model-seeds.ts +56 -10
- package/src/providers/registry/types.ts +2 -0
- package/src/providers/resolved-model-policy-merge.ts +167 -0
- package/src/providers/resolved-model-policy.ts +406 -0
- package/src/providers/stale-vision-classification-migration.ts +137 -0
- package/src/providers/xai-transport.ts +12 -1
- package/src/reasoning-effort.ts +8 -0
- package/src/responses/apply-patch-envelope.ts +0 -12
- package/src/responses/freeform-wrapper-scan.ts +279 -0
- package/src/responses/function-call-compat.ts +38 -1
- package/src/responses/inline-document.ts +65 -0
- package/src/responses/input-media.ts +42 -8
- package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
- package/src/responses/muse-tool-name-alias.ts +19 -0
- package/src/responses/parser-content.ts +8 -2
- package/src/responses/parser-tools.ts +3 -0
- package/src/responses/parser.ts +3 -1
- package/src/responses/progressive-freeform-input.ts +130 -0
- package/src/responses/reasoning-envelope.ts +30 -0
- package/src/responses/schema.ts +3 -0
- package/src/responses/state.ts +5 -12
- package/src/responses/tool-name-aliases.ts +15 -1
- package/src/router.ts +108 -117
- package/src/routing/compatibility/behavior.ts +9 -0
- package/src/routing/compatibility/subject.ts +16 -1
- package/src/server/adapter-resolve.ts +9 -0
- package/src/server/admission-model-scope.ts +219 -0
- package/src/server/audio-live.ts +9 -3
- package/src/server/audio-upstream.ts +18 -0
- package/src/server/auth-cors.ts +29 -0
- package/src/server/chat-completions.ts +60 -4
- package/src/server/chat-native.ts +19 -4
- package/src/server/claude-messages.ts +61 -20
- package/src/server/effort-row.ts +11 -3
- package/src/server/grok-responses-control-frame.ts +160 -1
- package/src/server/grok-responses-snapshot-repair.ts +113 -11
- package/src/server/gui-freshness.ts +103 -0
- package/src/server/gui-static.ts +7 -9
- package/src/server/images.ts +59 -6
- package/src/server/index/claude-intercept-lifecycle.ts +49 -0
- package/src/server/index/serve-options.ts +142 -39
- package/src/server/index/spend-ledger-lifecycle.ts +92 -0
- package/src/server/index/startup-warnings.ts +24 -0
- package/src/server/index/websocket-handler.ts +6 -1
- package/src/server/index.ts +34 -38
- package/src/server/lifecycle.ts +4 -4
- package/src/server/live-call-bindings.ts +6 -0
- package/src/server/live.ts +88 -3
- package/src/server/management/agent-settings-routes.ts +121 -36
- package/src/server/management/aside-profile-routes.ts +266 -7
- package/src/server/management/companion-routes.ts +77 -0
- package/src/server/management/config-routes.ts +18 -2
- package/src/server/management/context.ts +3 -0
- package/src/server/management/integration-routes.ts +287 -5
- package/src/server/management/logs-usage-routes.ts +19 -0
- package/src/server/management/metrics-routes.ts +20 -0
- package/src/server/management/model-rows.ts +224 -12
- package/src/server/management/native-integration-routes.ts +103 -6
- package/src/server/management/oauth-account-routes.ts +45 -7
- package/src/server/management/route-registry.ts +20 -0
- package/src/server/management/shared.ts +28 -4
- package/src/server/management/system-restart.ts +7 -2
- package/src/server/management/system-routes.ts +2 -0
- package/src/server/management/usage-aggregate-cache.ts +4 -0
- package/src/server/management/usage-timeline-routes.ts +44 -0
- package/src/server/management-api.ts +10 -9
- package/src/server/management-auth.ts +15 -1
- package/src/server/proxy-liveness.ts +75 -0
- package/src/server/readiness.ts +29 -10
- package/src/server/relay-eager.ts +24 -2
- package/src/server/relay.ts +138 -12
- package/src/server/request-log-failure-attribution.ts +99 -0
- package/src/server/request-log.ts +169 -2
- package/src/server/request-metrics.ts +298 -0
- package/src/server/responses/adapter-continuation.ts +3 -3
- package/src/server/responses/adapter-dispatch.ts +11 -6
- package/src/server/responses/codex-ws-wire.ts +34 -8
- package/src/server/responses/combo-stream-preflight.ts +168 -6
- package/src/server/responses/compact.ts +43 -10
- package/src/server/responses/compaction-routing.ts +111 -0
- package/src/server/responses/core-codex-account.ts +8 -3
- package/src/server/responses/core-combo.ts +7 -7
- package/src/server/responses/core-normalize.ts +6 -12
- package/src/server/responses/core-opaque-recovery.ts +91 -0
- package/src/server/responses/core-options.ts +4 -0
- package/src/server/responses/encrypted-payload.ts +20 -2
- package/src/server/responses/fetch-helpers.ts +124 -8
- package/src/server/responses/input-admission.ts +10 -0
- package/src/server/responses/passthrough-delivery.ts +45 -15
- package/src/server/responses/passthrough-dispatch.ts +215 -44
- package/src/server/responses/passthrough-error.ts +27 -8
- package/src/server/responses/policy-fallback.ts +5 -13
- package/src/server/responses/request-prepare.ts +109 -18
- package/src/server/responses/request-send-budget.ts +17 -1
- package/src/server/responses/request-sidecar-auth.ts +1 -1
- package/src/server/responses/request-transport.ts +26 -6
- package/src/server/responses/reset-replay.ts +108 -0
- package/src/server/responses/run-turn-execution.ts +25 -3
- package/src/server/responses/sidecar-execution.ts +17 -2
- package/src/server/responses/ws-upstream.ts +14 -27
- package/src/server/responses-custom-tool-repair.ts +27 -54
- package/src/server/responses-request-tool-scope.ts +214 -0
- package/src/server/responses-undeclared-tool-guard.ts +35 -2
- package/src/server/search.ts +25 -1
- package/src/server/sse-payload-rewrite.ts +1 -1
- package/src/server/usage-ledger-retention.ts +73 -0
- package/src/service/cli.ts +48 -2
- package/src/service/health.ts +3 -2
- package/src/service/install-state-contract.d.mts +27 -0
- package/src/service/install-state-contract.mjs +34 -0
- package/src/service/launchd.ts +1 -1
- package/src/service/orchestration.ts +2 -4
- package/src/service/ownership-compatibility.ts +164 -0
- package/src/service/ownership-mutation-lease.d.mts +32 -0
- package/src/service/ownership-mutation-lease.mjs +211 -0
- package/src/service/repair.ts +45 -1
- package/src/service/state-lock.ts +269 -0
- package/src/service/state-record.d.mts +36 -0
- package/src/service/state-record.mjs +138 -0
- package/src/service/state.ts +582 -68
- package/src/service/windows-taskxml.ts +11 -10
- package/src/service.ts +7 -3
- package/src/tray/windows-tray.ps1 +156 -4
- package/src/types/config.ts +48 -3
- package/src/types/provider.ts +91 -0
- package/src/types/request.ts +30 -2
- package/src/types/tools.ts +33 -0
- package/src/types.ts +4 -0
- package/src/update/index.ts +207 -63
- package/src/update/job.ts +9 -5
- package/src/update/ownership-transaction.ts +47 -0
- package/src/update/restart-ownership.ts +54 -0
- package/src/update/runtime-ownership.d.mts +40 -0
- package/src/update/runtime-ownership.mjs +122 -0
- package/src/usage/attempt-delivery.ts +198 -0
- package/src/usage/cache-diagnostic.ts +305 -0
- package/src/usage/failure-fingerprint.ts +118 -0
- package/src/usage/failure-projection-cache.ts +174 -0
- package/src/usage/failure-projection.ts +174 -0
- package/src/usage/ledger-retention.ts +165 -0
- package/src/usage/log.ts +126 -79
- package/src/usage/request-outcome.ts +150 -0
- package/src/usage/retention-contract.ts +28 -0
- package/src/usage/summary.ts +2 -2
- package/src/usage/telemetry-contract.ts +237 -0
- package/src/usage/timeline.ts +236 -0
- package/src/vision/eligibility.ts +88 -9
- package/src/vision/plan.ts +34 -10
- package/src/web-search/alpha-search.ts +21 -1
- package/src/web-search/executor.ts +41 -2
- package/src/web-search/loop.ts +6 -1
- package/gui/dist/assets/index-C5IebErG.js +0 -136
- package/gui/dist/assets/index-OESInAjC.css +0 -1
package/src/vision/plan.ts
CHANGED
|
@@ -5,7 +5,11 @@ import type { ResolvedOpenAiForwardSidecar } from "../providers/openai-sidecar";
|
|
|
5
5
|
import type { CodexAuthPolicyConfig } from "../codex/auth-context";
|
|
6
6
|
import { isCodexReserveRequestEligible } from "../codex/loopback-target";
|
|
7
7
|
import type { DataPlaneAdmission } from "../server/auth-cors";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
customRowInputModalities,
|
|
10
|
+
isModelVisionSidecarConsumer as isModelTextOnly,
|
|
11
|
+
modelAcceptsImageInput,
|
|
12
|
+
} from "./eligibility";
|
|
9
13
|
import { normalizeVisionReasoningForModel } from "./reasoning";
|
|
10
14
|
import { resolveSidecarAuth } from "../sidecar/auth";
|
|
11
15
|
import { DEFAULT_VISION_TIMEOUT_MS, MAX_VISION_TIMEOUT_MS, MIN_VISION_TIMEOUT_MS } from "./timeout-bounds";
|
|
@@ -95,23 +99,40 @@ function messagesHaveImage(parsed: OcxParsedRequest): boolean {
|
|
|
95
99
|
/**
|
|
96
100
|
* Direct-image admission for a routed target. Returns true when capability evidence proves the
|
|
97
101
|
* target cannot accept image input, so the caller must describe or strip the image first.
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
102
|
+
* Evidence is consulted highest-first: `modelCapabilities` (the dedicated per-model capability
|
|
103
|
+
* axis), an explicit custom row for the same routed identity, `noVisionModels`, an explicit
|
|
104
|
+
* per-model modality list without `image`, and finally proven-negative registry/vendor metadata.
|
|
105
|
+
* Any of them can require the vision preprocessor. A genuinely unknown custom model is NOT
|
|
106
|
+
* guessed blind: it keeps the established pass-through behaviour. The provider-only fallback
|
|
107
|
+
* keeps legacy unit callers stable; production dispatch always supplies providerName so the
|
|
108
|
+
* complete capability chain is consulted.
|
|
103
109
|
*/
|
|
104
110
|
export function requiresVisionPreprocessing(
|
|
105
|
-
config: Pick<OcxConfig, "providers"
|
|
111
|
+
config: Pick<OcxConfig, "providers"> & { customModels?: OcxConfig["customModels"] },
|
|
106
112
|
provider: Pick<OcxProviderConfig, "noVisionModels" | "modelInputModalities" | "modelCapabilities">,
|
|
107
113
|
modelId: string,
|
|
108
114
|
providerName?: string,
|
|
109
115
|
): boolean {
|
|
110
|
-
if (isModelTextOnly(provider, modelId)) return true;
|
|
111
116
|
const runtimeDeclared = Object.hasOwn(provider.modelCapabilities ?? {}, modelId)
|
|
112
117
|
? provider.modelCapabilities?.[modelId]?.inputModalities
|
|
113
118
|
: undefined;
|
|
119
|
+
// `modelCapabilities` is the dedicated per-model capability axis, so it outranks everything
|
|
120
|
+
// below — including an explicit custom row that disagrees. The CLI `--text-only` flag writes it.
|
|
114
121
|
if (runtimeDeclared !== undefined) return !runtimeDeclared.includes("image");
|
|
122
|
+
// An explicit custom row outranks the provider-level hints below, mirroring the catalog overlay
|
|
123
|
+
// in `src/codex/catalog/routed-gather.ts` that already copies this same declaration onto the
|
|
124
|
+
// advertised row. Without it the dashboard advertised "text, image" from the operator's own row
|
|
125
|
+
// while this predicate stripped the image before dispatch.
|
|
126
|
+
const customDeclared = providerName === undefined
|
|
127
|
+
? undefined
|
|
128
|
+
: customRowInputModalities(config, providerName, modelId);
|
|
129
|
+
// Same rule as the `modelCapabilities` branch above: the declaration answers "can this model
|
|
130
|
+
// take an image", not "is it a text model". A row that lists only `audio` or `video` excludes
|
|
131
|
+
// image input just as `["text"]` does, and `modelAcceptsImageInput` already answers "no image"
|
|
132
|
+
// for it; the narrower `includes("text")` test here made the two predicates disagree and sent
|
|
133
|
+
// the attachment to a model that cannot read it.
|
|
134
|
+
if (customDeclared !== undefined) return !customDeclared.includes("image");
|
|
135
|
+
if (isModelTextOnly(provider, modelId)) return true;
|
|
115
136
|
const runtimeModalities = modelRecordValue(provider.modelInputModalities, modelId);
|
|
116
137
|
if (Array.isArray(runtimeModalities) && runtimeModalities.length > 0) {
|
|
117
138
|
return !runtimeModalities.includes("image");
|
|
@@ -129,9 +150,12 @@ function usableRoutedVisionModel(config: OcxConfig): string | undefined {
|
|
|
129
150
|
if (!routedModel || sep <= 0) return undefined;
|
|
130
151
|
const targetProvider = routedModel.slice(0, sep);
|
|
131
152
|
const targetId = routedModel.slice(sep + 1);
|
|
132
|
-
|
|
153
|
+
// `modelAcceptsImageInput` is the one predicate here: it resolves the whole capability chain,
|
|
154
|
+
// custom row included. The provider-only `isModelTextOnly` that used to be ANDed in could only
|
|
155
|
+
// subtract — it never saw a custom row, so a describer the operator had declared image-capable
|
|
156
|
+
// was refused for a provider hint that same row overrides.
|
|
133
157
|
return modelAcceptsImageInput(config, { provider: targetProvider, id: targetId }) !== false
|
|
134
|
-
|
|
158
|
+
? routedModel : undefined;
|
|
135
159
|
}
|
|
136
160
|
|
|
137
161
|
export function shouldResolveOpenAiVisionSidecar(
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
import { formatErrorResponse } from "../bridge";
|
|
15
15
|
import { redactSecretString } from "../lib/redact";
|
|
16
16
|
import { sidecarEnter } from "../lib/sidecar-tracker";
|
|
17
|
+
import { admissionScopeDenial } from "../server/admission-model-scope";
|
|
18
|
+
import type { DataPlaneAdmission } from "../server/auth-cors";
|
|
17
19
|
import type { OcxConfig, OcxProviderConfig, OcxWebSearchSidecarConfig } from "../types";
|
|
18
20
|
import { runAnthropicWebSearch } from "./anthropic-executor";
|
|
19
21
|
import { runExaWebSearch } from "./exa-executor";
|
|
@@ -252,6 +254,7 @@ export async function handleAlphaSearchSidecarFallback(
|
|
|
252
254
|
config: OcxConfig,
|
|
253
255
|
signal?: AbortSignal,
|
|
254
256
|
logCtx?: { provider: string },
|
|
257
|
+
admission?: DataPlaneAdmission,
|
|
255
258
|
): Promise<Response> {
|
|
256
259
|
const resolution = resolveAlphaSearchSidecar(config);
|
|
257
260
|
if (resolution.status === "missing-credential") {
|
|
@@ -266,6 +269,24 @@ export async function handleAlphaSearchSidecarFallback(
|
|
|
266
269
|
const resolved = resolution.sidecar;
|
|
267
270
|
if (logCtx) logCtx.provider = resolved.backend;
|
|
268
271
|
|
|
272
|
+
// This backend is a paid destination like any other, and the operator's
|
|
273
|
+
// configuration -- not the caller -- decides which one and which model. A key
|
|
274
|
+
// scoped away from it must not spend it by asking the search endpoint instead
|
|
275
|
+
// of the inference one. Exa has no configured provider entry, so its own
|
|
276
|
+
// backend name is the destination.
|
|
277
|
+
const settings = sidecarSettingsForAlphaSearch(resolved.backend, config);
|
|
278
|
+
const requestedModel = (body as { model?: unknown } | null)?.model;
|
|
279
|
+
const denial = admissionScopeDenial(
|
|
280
|
+
config,
|
|
281
|
+
admission,
|
|
282
|
+
typeof requestedModel === "string" && requestedModel.trim() ? requestedModel : undefined,
|
|
283
|
+
{
|
|
284
|
+
providerName: resolved.backend === "exa" ? resolved.backend : resolved.providerName,
|
|
285
|
+
modelId: settings.model,
|
|
286
|
+
},
|
|
287
|
+
);
|
|
288
|
+
if (denial) return denial;
|
|
289
|
+
|
|
269
290
|
const queries = extractAlphaSearchQueries(body);
|
|
270
291
|
if (queries.length === 0) {
|
|
271
292
|
return formatErrorResponse(
|
|
@@ -275,7 +296,6 @@ export async function handleAlphaSearchSidecarFallback(
|
|
|
275
296
|
);
|
|
276
297
|
}
|
|
277
298
|
|
|
278
|
-
const settings = sidecarSettingsForAlphaSearch(resolved.backend, config);
|
|
279
299
|
const sidecarExit = sidecarEnter("search");
|
|
280
300
|
try {
|
|
281
301
|
const texts: string[] = [];
|
|
@@ -3,7 +3,14 @@ import { FORWARD_HEADERS } from "../adapters/openai-responses";
|
|
|
3
3
|
import { signalWithTimeout, cancelBodyOnAbort } from "../lib/abort";
|
|
4
4
|
import { redactSecretString } from "../lib/redact";
|
|
5
5
|
import { sidecarEnter } from "../lib/sidecar-tracker";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
applyUpstreamRecoveryInit,
|
|
8
|
+
fetchWithResetRetry,
|
|
9
|
+
releaseResponseBodyBestEffort,
|
|
10
|
+
retryBackoffDelayMs,
|
|
11
|
+
sleepWithAbort,
|
|
12
|
+
RETRY_AFTER_CEILING_MS,
|
|
13
|
+
} from "../lib/upstream-retry";
|
|
7
14
|
import { withUpstreamHttpVersion } from "../lib/upstream-http-version";
|
|
8
15
|
import { parseSidecarSSE, type WebSearchResult } from "./parse";
|
|
9
16
|
import type { CodexUpstreamOutcome } from "../codex/routing";
|
|
@@ -35,6 +42,22 @@ export const IMAGE_INSTRUCTION =
|
|
|
35
42
|
|
|
36
43
|
/** A search result, or an `error` string when the search couldn't run (surfaced as a tool result). */
|
|
37
44
|
export type SidecarOutcome = WebSearchResult & { error?: string };
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Bounded same-search 429 replays for the sidecar POST.
|
|
48
|
+
*
|
|
49
|
+
* The forward backend throttles burst sidecar traffic, and without a replay the 429 becomes a
|
|
50
|
+
* failed tool result that poisons the query for the whole turn (see failedQueries in loop.ts).
|
|
51
|
+
* 1 initial send + 2 replays; Retry-After is honored as a lower bound and capped by
|
|
52
|
+
* RETRY_AFTER_CEILING_MS (an instruction past the ceiling ends with the 429 instead of
|
|
53
|
+
* parking the search). Each wait releases the unread 429 body first so sockets do not
|
|
54
|
+
* accumulate under a rate-limit storm. Abort or timeout ends the wait through the existing
|
|
55
|
+
* catch, exactly like an abort during the SSE parse.
|
|
56
|
+
*/
|
|
57
|
+
const SIDECAR_429_MAX_ATTEMPTS = 3;
|
|
58
|
+
const SIDECAR_429_BASE_DELAY_MS = 1_000;
|
|
59
|
+
const SIDECAR_429_MAX_DELAY_MS = 10_000;
|
|
60
|
+
|
|
38
61
|
export type SidecarOutcomeRecorder = (outcome: CodexUpstreamOutcome) => void;
|
|
39
62
|
|
|
40
63
|
/**
|
|
@@ -79,7 +102,7 @@ export async function runWebSearch(
|
|
|
79
102
|
const sidecarExit = sidecarEnter("web-search");
|
|
80
103
|
const t0 = Date.now();
|
|
81
104
|
try {
|
|
82
|
-
const
|
|
105
|
+
const sendOnce = () => fetchWithResetRetry(
|
|
83
106
|
// Recovery nests INSIDE the version helper: applyUpstreamRecoveryInit then always receives a
|
|
84
107
|
// defined init, and withUpstreamHttpVersion spreads the result, so `protocol` and the
|
|
85
108
|
// recovery fields (`connection: close` + Bun's transport-level `keepalive: false`) survive
|
|
@@ -96,6 +119,22 @@ export async function runWebSearch(
|
|
|
96
119
|
}, recovery), forwardProvider)),
|
|
97
120
|
{ replaySafe: true, abortSignal: linkedSignal.signal, label: "web-search-sidecar" },
|
|
98
121
|
);
|
|
122
|
+
let res = await sendOnce();
|
|
123
|
+
for (let attempt = 0; res.status === 429 && attempt + 1 < SIDECAR_429_MAX_ATTEMPTS; attempt++) {
|
|
124
|
+
const delay = retryBackoffDelayMs(attempt, {
|
|
125
|
+
baseDelayMs: SIDECAR_429_BASE_DELAY_MS,
|
|
126
|
+
maxDelayMs: SIDECAR_429_MAX_DELAY_MS,
|
|
127
|
+
headers: res.headers,
|
|
128
|
+
retryAfterIsLowerBound: true,
|
|
129
|
+
});
|
|
130
|
+
// A deadline, not a clamp: an instruction past the ceiling ends the search with the
|
|
131
|
+
// 429 instead of parking it at a provider that already said it would refuse.
|
|
132
|
+
if (delay > RETRY_AFTER_CEILING_MS) break;
|
|
133
|
+
console.warn(`[web-search] sidecar HTTP 429 — retrying (${attempt + 2}/${SIDECAR_429_MAX_ATTEMPTS}) after ${delay}ms`);
|
|
134
|
+
await releaseResponseBodyBestEffort(res.body, linkedSignal.signal);
|
|
135
|
+
await sleepWithAbort(delay, linkedSignal.signal);
|
|
136
|
+
res = await sendOnce();
|
|
137
|
+
}
|
|
99
138
|
// Attach the body guard before ANY branch reads it. The success path guarded itself below,
|
|
100
139
|
// but the failure branch's `res.text()` runs first, so a cancel landing between fetch
|
|
101
140
|
// resolution and reader attach orphaned the internal rejection (found investigating #1419).
|
package/src/web-search/loop.ts
CHANGED
|
@@ -333,10 +333,15 @@ export interface WebSearchLoopDeps {
|
|
|
333
333
|
* 429 states the window's reset epoch even when it omits Retry-After -- and a rotation that
|
|
334
334
|
* cannot see it cools the drained account for the short default instead of until the window
|
|
335
335
|
* actually reopens. Optional so existing callers keep compiling.
|
|
336
|
+
*
|
|
337
|
+
* `retryParsed` is the exact iteration-local request the retry will be built from. The loop
|
|
338
|
+
* sends a shallow copy of the outer parsed request, so a rotation that rebinds only the outer
|
|
339
|
+
* object never reaches the wire. Optional so existing callers keep compiling.
|
|
336
340
|
*/
|
|
337
341
|
on429?: (
|
|
338
342
|
retryAfterHeader: string | null,
|
|
339
343
|
responseHeaders?: Headers,
|
|
344
|
+
retryParsed?: OcxParsedRequest,
|
|
340
345
|
) => ProviderAdapter | null | Promise<ProviderAdapter | null>;
|
|
341
346
|
/** Opt-in same-target 429 policy (key-auth providers). When present, 429 replays on the SAME key before on429 rotation. */
|
|
342
347
|
retryOn429Policy?: Required<RateLimitRetryPolicy> | null;
|
|
@@ -558,7 +563,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
|
|
|
558
563
|
// 429 key-failover parity with the normal routed path: rotate pool keys until one responds
|
|
559
564
|
// or the pool is exhausted (deps.on429 returns null — cooldown map guarantees termination).
|
|
560
565
|
while (prepared.response.status === 429 && deps.on429) {
|
|
561
|
-
const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers);
|
|
566
|
+
const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers, iterParsed);
|
|
562
567
|
if (!rotated) break;
|
|
563
568
|
// Never let a broken body's cancel promise outlive the cumulative header deadline. Observe
|
|
564
569
|
// it, but proceed immediately to the rotated fetch under the SAME deadline signal.
|