@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
|
@@ -12,9 +12,57 @@ import { waitForProviderRequestSlot } from "../../providers/request-pacing";
|
|
|
12
12
|
import { withUpstreamHttpVersion } from "../../lib/upstream-http-version";
|
|
13
13
|
import type { CodexWsQuotaObserver } from "./codex-ws-metadata";
|
|
14
14
|
import { configuredOutboundFetch } from "../../lib/proxy-env";
|
|
15
|
+
import {
|
|
16
|
+
describeProviderEgressForLog,
|
|
17
|
+
markEgressTransparentExecutor,
|
|
18
|
+
providerEgressSendInit,
|
|
19
|
+
providerEgressIsExplicit,
|
|
20
|
+
resolveProviderEgress,
|
|
21
|
+
type ProviderEgressBinding,
|
|
22
|
+
} from "../../lib/provider-egress";
|
|
23
|
+
import { redactSecretString } from "../../lib/redact";
|
|
15
24
|
|
|
16
25
|
export { withUpstreamHttpVersion };
|
|
17
26
|
|
|
27
|
+
const egressWebsocketDowngradeWarned = new Set<string>();
|
|
28
|
+
/** A provider name is configuration-controlled, so the notice set is bounded like any cache. */
|
|
29
|
+
const EGRESS_DOWNGRADE_NOTICE_LIMIT = 64;
|
|
30
|
+
/**
|
|
31
|
+
* Marks an init whose provider egress route an outer physical-send boundary already decided.
|
|
32
|
+
*
|
|
33
|
+
* Own symbol keys survive object spread, so the mark travels through the rebuild a
|
|
34
|
+
* `dispatchOverride` performs, and an unknown symbol on a `RequestInit` is inert at the wire.
|
|
35
|
+
*/
|
|
36
|
+
const EGRESS_DECIDED = Symbol.for("opencodex.provider-egress.decided");
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Announce once, per provider, that an explicit egress route moved this provider off the
|
|
40
|
+
* WebSocket fast lane.
|
|
41
|
+
*
|
|
42
|
+
* The WebSocket upstream selects its proxy from the process environment when it dials, so it
|
|
43
|
+
* cannot carry a per-provider route. Serving the turn over HTTP/SSE honours the operator's
|
|
44
|
+
* egress choice, which is the one that has to win — but a transport change the operator did
|
|
45
|
+
* not ask for is exactly the kind of substitution this batch refuses to make silently, so it
|
|
46
|
+
* is stated rather than merely done.
|
|
47
|
+
*/
|
|
48
|
+
function warnEgressWebsocketDowngradeOnce(providerName: string, egress: string): void {
|
|
49
|
+
if (egressWebsocketDowngradeWarned.has(providerName)) return;
|
|
50
|
+
if (egressWebsocketDowngradeWarned.size >= EGRESS_DOWNGRADE_NOTICE_LIMIT) return;
|
|
51
|
+
egressWebsocketDowngradeWarned.add(providerName);
|
|
52
|
+
console.warn(
|
|
53
|
+
// The name is caller-controlled and can be token-shaped, so it is redacted and JSON-escaped
|
|
54
|
+
// before it reaches a log, exactly as at the management error boundary.
|
|
55
|
+
`[opencodex] provider ${JSON.stringify(redactSecretString(providerName))} declares egress ${egress}; the WebSocket upstream `
|
|
56
|
+
+ "selects its proxy from the process environment and cannot carry a per-provider route, "
|
|
57
|
+
+ "so these turns are served over HTTP/SSE.",
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Test seam: the downgrade notice is once per provider per process, not once per request. */
|
|
62
|
+
export function __resetEgressWebsocketDowngradeNotices(): void {
|
|
63
|
+
egressWebsocketDowngradeWarned.clear();
|
|
64
|
+
}
|
|
65
|
+
|
|
18
66
|
export function disableResponsesRequestTimeout(req: Request, server: Pick<Server<WsData>, "timeout"> | undefined): boolean {
|
|
19
67
|
if (!server) return false;
|
|
20
68
|
try {
|
|
@@ -97,21 +145,43 @@ export function sendWithConnectionPolicy(
|
|
|
97
145
|
physicalFetch: typeof globalThis.fetch,
|
|
98
146
|
input: Parameters<typeof globalThis.fetch>[0],
|
|
99
147
|
init?: RequestInit,
|
|
148
|
+
egress?: ProviderEgressBinding,
|
|
100
149
|
): Promise<Response> {
|
|
101
150
|
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
|
|
102
151
|
const fresh = wantsFreshConnection(input);
|
|
103
152
|
if (fresh) {
|
|
104
153
|
headers.set("Connection", "close");
|
|
105
154
|
}
|
|
155
|
+
// Decided here, against the destination this send is actually going to, and around whichever
|
|
156
|
+
// executor was just selected. A `dispatchOverride` that rebuilds a queued request can change
|
|
157
|
+
// both the upstream host and the provider transport after the wrapper was constructed, so a
|
|
158
|
+
// route resolved at construction could be applied to a different host than it was decided for.
|
|
159
|
+
// These calls nest: an override decides with its own binding and then hands the send to the
|
|
160
|
+
// executor `providerFetch` supplied, which is another one of these. The outermost caller holds
|
|
161
|
+
// the reselected provider and the rebuilt destination, so it decides and marks the init; the
|
|
162
|
+
// inner pass honours that mark rather than recomputing from a stale closure.
|
|
163
|
+
const alreadyDecided = (init as Record<symbol, unknown> | undefined)?.[EGRESS_DECIDED] === true;
|
|
164
|
+
const decide = egress !== undefined && !alreadyDecided;
|
|
165
|
+
const egressInit = decide ? providerEgressSendInit(egress, physicalFetch, input) : {};
|
|
106
166
|
return physicalFetch(input, {
|
|
107
167
|
...init,
|
|
108
168
|
headers,
|
|
109
169
|
redirect: "manual",
|
|
110
170
|
...(fresh ? { keepalive: false } : {}),
|
|
171
|
+
...egressInit,
|
|
172
|
+
...(decide ? { [EGRESS_DECIDED]: true } : {}),
|
|
111
173
|
});
|
|
112
174
|
}
|
|
113
175
|
|
|
114
176
|
export interface ProviderFetchOptions {
|
|
177
|
+
/**
|
|
178
|
+
* Keep this send on HTTP even where the WebSocket upstream would normally be selected.
|
|
179
|
+
*
|
|
180
|
+
* Set by a caller replacing an HTTP stream that already failed: a WS create frame is a
|
|
181
|
+
* different send on a different transport, and the replacement has to be the same kind of
|
|
182
|
+
* exchange the client is already reading.
|
|
183
|
+
*/
|
|
184
|
+
httpOnly?: boolean;
|
|
115
185
|
nativeControl?: NativeResponseControl;
|
|
116
186
|
providerName?: string;
|
|
117
187
|
modelId?: string;
|
|
@@ -130,28 +200,63 @@ export function providerFetch(
|
|
|
130
200
|
runtime: BunRuntimeGateInput = currentBunRuntimeIdentity(),
|
|
131
201
|
options: ProviderFetchOptions = {},
|
|
132
202
|
): ProviderFetch {
|
|
133
|
-
const
|
|
203
|
+
const providerName = options.providerName ?? "<unnamed provider>";
|
|
204
|
+
const customExecutor = (provider as OcxProviderConfig & { fetch?: typeof globalThis.fetch }).fetch;
|
|
205
|
+
// The route is applied at the physical send (see `sendWithConnectionPolicy`). This binding is
|
|
206
|
+
// only what that boundary needs to decide it.
|
|
207
|
+
const egressBinding: ProviderEgressBinding = { providerName, provider };
|
|
208
|
+
// Resolved per request, not once per wrapper: `providers.<name>.noProxy` is evaluated against
|
|
209
|
+
// the destination, so two requests through the same executor can legitimately take different
|
|
210
|
+
// routes. A malformed value throws and rejects the request rather than degrading to the
|
|
211
|
+
// global proxy or to direct, either of which would read as success at the call site.
|
|
212
|
+
const egressFor = (input: Parameters<typeof globalThis.fetch>[0]) => resolveProviderEgress({
|
|
213
|
+
providerName,
|
|
214
|
+
provider,
|
|
215
|
+
url: typeof input === "string" ? input : input instanceof URL ? input : input.url,
|
|
216
|
+
});
|
|
217
|
+
// The built-in executor forwards its init to a transport that honours the proxy option.
|
|
218
|
+
const configuredFetch = markEgressTransparentExecutor(Object.assign(
|
|
134
219
|
(input: Parameters<typeof globalThis.fetch>[0], init?: RequestInit) => configuredOutboundFetch(input, init),
|
|
135
220
|
{ preconnect: globalThis.fetch.preconnect?.bind(globalThis.fetch) },
|
|
136
|
-
) as typeof globalThis.fetch;
|
|
137
|
-
const base =
|
|
221
|
+
) as typeof globalThis.fetch);
|
|
222
|
+
const base = customExecutor ?? configuredFetch;
|
|
138
223
|
const preconnect = (...args: Parameters<typeof globalThis.fetch.preconnect>): void => {
|
|
139
224
|
base.preconnect?.(...args);
|
|
140
225
|
};
|
|
141
226
|
// Rebuilt dispatches must use the same physical-send boundary as ordinary HTTP sends.
|
|
142
227
|
// Return the original 3xx so the response owner retains its retry/health/relay contract.
|
|
143
|
-
|
|
228
|
+
//
|
|
229
|
+
// Marked transparent because it forwards its init to a transport that honours the proxy
|
|
230
|
+
// option. Leaving it unmarked would make an ordinary configured provider refuse its own route
|
|
231
|
+
// on every overridden path, after the attempt had already been recorded — an override selects
|
|
232
|
+
// `provider.fetch ?? execute`, and `execute` is this wrapper. It still carries the binding, so
|
|
233
|
+
// an override that simply calls it gets the route decided rather than dropped; an override
|
|
234
|
+
// that decided for itself has already marked the init and this pass defers to that decision.
|
|
235
|
+
const dispatch = markEgressTransparentExecutor(Object.assign(
|
|
144
236
|
(input: Parameters<typeof globalThis.fetch>[0], init?: RequestInit) =>
|
|
145
|
-
sendWithConnectionPolicy(base, input, init),
|
|
237
|
+
sendWithConnectionPolicy(base, input, init, egressBinding),
|
|
146
238
|
{ preconnect },
|
|
147
|
-
) as typeof globalThis.fetch;
|
|
239
|
+
) as typeof globalThis.fetch);
|
|
148
240
|
const httpFetch = Object.assign(
|
|
149
241
|
async (input: Parameters<typeof globalThis.fetch>[0], init?: RequestInit) => {
|
|
242
|
+
// Refuse before any dispatch side effect where that is sound. `beforeDispatch` commits
|
|
243
|
+
// attempt accounting and consumes admission state, so a refusal firing after it would
|
|
244
|
+
// charge an attempt for a send that never happens, and a throwing hook would mask the
|
|
245
|
+
// egress error with an unrelated one.
|
|
246
|
+
//
|
|
247
|
+
// With no override, this input and `base` ARE the final destination and executor, so the
|
|
248
|
+
// full decision can be made now. With an override, only the configured value is checked:
|
|
249
|
+
// the override may rebuild against a different host and select a different transport, and
|
|
250
|
+
// refusing on this destination would reject a request whose real route is fine.
|
|
251
|
+
if (options.dispatchOverride) egressFor(input);
|
|
252
|
+
else providerEgressSendInit(egressBinding, base, input);
|
|
150
253
|
// The hook inspects the outgoing headers and refuses the send by throwing; it is not a
|
|
151
254
|
// mutator, and the copy it receives is deliberately not threaded onward. `Connection`
|
|
152
255
|
// is decided inside `dispatch`, which runs after this, so the fresh-connection policy
|
|
153
256
|
// wins regardless of what any caller or hook put in the header.
|
|
154
257
|
options.beforeDispatch?.(new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined)));
|
|
258
|
+
// No proxy option is attached here: a `dispatchOverride` may rebuild this request against
|
|
259
|
+
// a different destination, so the route is decided at the physical send instead.
|
|
155
260
|
const dispatchInit = { ...withUpstreamHttpVersion(input, init, provider), timeout: 0 };
|
|
156
261
|
return options.dispatchOverride
|
|
157
262
|
? options.dispatchOverride(input, dispatchInit, dispatch)
|
|
@@ -164,7 +269,13 @@ export function providerFetch(
|
|
|
164
269
|
// else keeps the provider's HTTP fetch. See ws-upstream.ts for the details.
|
|
165
270
|
const unpaced = async (input: Parameters<typeof globalThis.fetch>[0], init?: RequestInit) => {
|
|
166
271
|
const upstreamWebsocket = provider.upstreamWebsocket === true;
|
|
167
|
-
if (typeof input === "string" && init
|
|
272
|
+
if (!options.httpOnly && typeof input === "string" && init
|
|
273
|
+
&& shouldUseCodexWsUpstream(input, init, runtime, upstreamWebsocket)) {
|
|
274
|
+
const egress = egressFor(input);
|
|
275
|
+
if (providerEgressIsExplicit(egress)) {
|
|
276
|
+
warnEgressWebsocketDowngradeOnce(providerName, describeProviderEgressForLog(egress));
|
|
277
|
+
return httpFetch(input, init);
|
|
278
|
+
}
|
|
168
279
|
// The fallback has to be the same HTTP fetch the non-WS branch would have
|
|
169
280
|
// used, protocol pin included: a WS turn that falls back is serving the
|
|
170
281
|
// request over HTTP, and dropping the provider's `upstreamHttpVersion`
|
|
@@ -188,11 +299,16 @@ export function providerFetch(
|
|
|
188
299
|
await waitForPacing(init?.signal ?? undefined);
|
|
189
300
|
return unpaced(input, init);
|
|
190
301
|
};
|
|
191
|
-
|
|
302
|
+
// The returned wrapper forwards its init down to `dispatch`, which applies the route at the
|
|
303
|
+
// physical send. Adapters that hand this executor back as `provider.fetch` (Cursor does)
|
|
304
|
+
// therefore still carry a per-provider route instead of being refused as opaque.
|
|
305
|
+
const paceAware = Object.assign(wrapped, {
|
|
192
306
|
preconnect,
|
|
193
307
|
waitForPacing,
|
|
194
308
|
unpacedFetch: Object.assign(unpaced, { preconnect }),
|
|
195
309
|
});
|
|
310
|
+
markEgressTransparentExecutor(paceAware as unknown as typeof globalThis.fetch);
|
|
311
|
+
return paceAware;
|
|
196
312
|
}
|
|
197
313
|
|
|
198
314
|
|
|
@@ -85,9 +85,19 @@ function imageTokens(imageUrl: string): number {
|
|
|
85
85
|
function contentPartTokens(part: OcxContentPart, modelId: string): number {
|
|
86
86
|
if (part.type === "image") return imageTokens(part.imageUrl);
|
|
87
87
|
if (part.type === "video") return imageTokens(part.videoUrl);
|
|
88
|
+
// An inline document is a base64 payload, not a sentence: estimating it from its marker would
|
|
89
|
+
// admit a request whose real input is orders of magnitude larger. Counted arithmetically —
|
|
90
|
+
// rebuilding the data URL here would materialize a second request-sized string just to measure it.
|
|
91
|
+
if (part.type === "document") return base64PayloadTokens(part.data);
|
|
88
92
|
return estimateTokens(part.text, modelId);
|
|
89
93
|
}
|
|
90
94
|
|
|
95
|
+
function base64PayloadTokens(base64: string): number {
|
|
96
|
+
if (base64.length === 0) return 0;
|
|
97
|
+
const decoded = Math.floor((base64.length * 3) / 4);
|
|
98
|
+
return Math.max(1, Math.ceil(decoded / IMAGE_BYTES_PER_TOKEN));
|
|
99
|
+
}
|
|
100
|
+
|
|
91
101
|
function contentTokens(content: string | readonly OcxContentPart[], modelId: string): number {
|
|
92
102
|
if (typeof content === "string") return estimateTokens(content, modelId);
|
|
93
103
|
let total = 0;
|
|
@@ -35,10 +35,6 @@ import { consumeComboFailure } from "./core-combo-failure";
|
|
|
35
35
|
import { readDisplaySafeErrorText } from "./core-errors";
|
|
36
36
|
import { streamingContextOverflowResponse, jsonContextOverflowResponse } from "./context-overflow";
|
|
37
37
|
import { formatPassthroughUpstreamError } from "./passthrough-error";
|
|
38
|
-
import {
|
|
39
|
-
providerModelResponsesTerminalRepair,
|
|
40
|
-
providerModelResponsesUpstreamStreaming,
|
|
41
|
-
} from "../../providers/registry";
|
|
42
38
|
import {
|
|
43
39
|
resolvePassthroughWebSearchBridgeAuth,
|
|
44
40
|
planPassthroughWebSearchBridge,
|
|
@@ -83,7 +79,10 @@ import {
|
|
|
83
79
|
import { createRoutedCustomToolRestoreBlockRewrite } from "../responses-custom-tool-repair";
|
|
84
80
|
import { createRoutedToolSearchRestoreBlockRewrite } from "../responses-tool-search-repair";
|
|
85
81
|
import { createGithubCopilotResponsesBlockRewrite } from "../github-copilot-responses-repair";
|
|
86
|
-
import {
|
|
82
|
+
import {
|
|
83
|
+
createGrokResponsesControlFrameBlockRewrite,
|
|
84
|
+
createGrokResponsesTimestampBlockRewrite,
|
|
85
|
+
} from "../grok-responses-control-frame";
|
|
87
86
|
import { createGrokResponsesSparseTerminalBlockRewrite } from "../grok-responses-snapshot-repair";
|
|
88
87
|
import {
|
|
89
88
|
createPlaintextV2AgentMessageCallRestoreRewrite,
|
|
@@ -94,11 +93,30 @@ import { createResponsesFieldBackfillBlockRewrite } from "./responses-field-back
|
|
|
94
93
|
import { createResponsesFunctionToolRepairBlockRewrite } from "../responses-function-tool-repair";
|
|
95
94
|
import {
|
|
96
95
|
createUndeclaredToolCallGuardBlockRewrite,
|
|
96
|
+
currentTurnWireToolCatalogBody,
|
|
97
97
|
undeclaredToolCallNameInResponse,
|
|
98
98
|
undeclaredToolCallMessage,
|
|
99
99
|
normalizeDefaultNamespaceInJson,
|
|
100
100
|
} from "../responses-undeclared-tool-guard";
|
|
101
101
|
import { isWin32EagerRewrite, selectEagerPath } from "../../lib/bun-stream-caps";
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Platform override for the two relay-path policy calls below. Tests only.
|
|
105
|
+
*
|
|
106
|
+
* The eager relay is reachable only on win32 and darwin, so a Linux shard cannot exercise it
|
|
107
|
+
* without claiming to be one of them. Overwriting `process.platform` globally does that, and a
|
|
108
|
+
* great deal more: every filesystem, ACL and state-directory decision in the process follows it,
|
|
109
|
+
* and the spend-ledger owner lowercases its home on win32, which on a case-sensitive filesystem
|
|
110
|
+
* names a DIFFERENT directory. A row that did that stopped being able to reserve its send and
|
|
111
|
+
* delivered no terminal at all, reporting as a relay defect. This narrows the claim to the two
|
|
112
|
+
* calls that actually choose the relay path.
|
|
113
|
+
*/
|
|
114
|
+
let relayPlatformForTests: NodeJS.Platform | undefined;
|
|
115
|
+
|
|
116
|
+
/** Internal test contract, not operator configuration: no config key reaches this. */
|
|
117
|
+
export function setRelayPlatformForTests(platform: NodeJS.Platform | undefined): void {
|
|
118
|
+
relayPlatformForTests = platform;
|
|
119
|
+
}
|
|
102
120
|
import { linkAbortSignal, UPSTREAM_JSON_BODY_READ_OPTIONS } from "./core-lifetime";
|
|
103
121
|
import { registerTurn, unregisterTurn, trackStreamLifetime } from "../lifecycle";
|
|
104
122
|
import { relaySseEagerBounded } from "../relay-eager";
|
|
@@ -340,11 +358,7 @@ export async function deliverPassthroughResponse(
|
|
|
340
358
|
// before relay starts. Waiting for SSE completion would retain request state across the whole
|
|
341
359
|
// stream; a later body failure does not undo that this destination accepted and served the turn.
|
|
342
360
|
commitReasoningReplayServingRoute(nativeExchange.request.headers);
|
|
343
|
-
const terminalRepairPolicy =
|
|
344
|
-
route.providerName,
|
|
345
|
-
route.provider,
|
|
346
|
-
route.modelId,
|
|
347
|
-
);
|
|
361
|
+
const terminalRepairPolicy = route.staticPolicy.model.responsesTerminalRepair;
|
|
348
362
|
// #3761: opt-in hosted-web-search bridge. Codex always declares the hosted web_search tool,
|
|
349
363
|
// and this branch relays that declaration on the assumption the destination executes it.
|
|
350
364
|
// A KEY-auth gateway that does not (Ollama Cloud GLM) answers with a function_call named
|
|
@@ -499,7 +513,22 @@ export async function deliverPassthroughResponse(
|
|
|
499
513
|
? createGrokResponsesControlFrameBlockRewrite()
|
|
500
514
|
: undefined,
|
|
501
515
|
grokClientCompatibilityEnabled
|
|
502
|
-
?
|
|
516
|
+
? createGrokResponsesTimestampBlockRewrite()
|
|
517
|
+
: undefined,
|
|
518
|
+
grokClientCompatibilityEnabled
|
|
519
|
+
? createGrokResponsesSparseTerminalBlockRewrite(
|
|
520
|
+
translatorBudget,
|
|
521
|
+
nativeExchange.outboundRequestBody,
|
|
522
|
+
{
|
|
523
|
+
clientToolAuthorizationBody: currentTurnWireToolCatalogBody(
|
|
524
|
+
parsed._rawBody,
|
|
525
|
+
parsed._replayPrefixLen ?? 0,
|
|
526
|
+
),
|
|
527
|
+
routedNamespaceToolAliases: responseEffects.routedNamespaceToolAliases,
|
|
528
|
+
routedMuseToolNameAliases: responseEffects.routedMuseToolNameAliases,
|
|
529
|
+
convertedRoutedCustomToolNames: routedCustomToolNames,
|
|
530
|
+
},
|
|
531
|
+
)
|
|
503
532
|
: undefined,
|
|
504
533
|
snapshotRepairEnabled
|
|
505
534
|
? createResponsesSnapshotBlockRewrite(nativeExchange.outboundRequestBody, translatorBudget)
|
|
@@ -529,12 +558,13 @@ export async function deliverPassthroughResponse(
|
|
|
529
558
|
? composeSseBlockRewrites(...blockRewrites)
|
|
530
559
|
: undefined;
|
|
531
560
|
const needsClientRewrite = clientBlockRewrite !== undefined;
|
|
561
|
+
const relayPlatform = relayPlatformForTests ?? process.platform;
|
|
532
562
|
// #864: win32 rewrite traffic must never enter the tee()+JS-pull chain
|
|
533
563
|
// (Bun#32111 JS-sink segfault — text frames pass, the terminal block is
|
|
534
564
|
// lost). The eager single reader applies the same rewrites inline.
|
|
535
|
-
const win32EagerRewrite = isWin32EagerRewrite(
|
|
565
|
+
const win32EagerRewrite = isWin32EagerRewrite(relayPlatform, needsClientRewrite);
|
|
536
566
|
const eagerPath = selectEagerPath(
|
|
537
|
-
|
|
567
|
+
relayPlatform,
|
|
538
568
|
needsClientRewrite,
|
|
539
569
|
config.streamMode ?? "auto",
|
|
540
570
|
);
|
|
@@ -805,7 +835,7 @@ export async function deliverPassthroughResponse(
|
|
|
805
835
|
// stream that never closes. Non-streaming clients keep the plain JSON.
|
|
806
836
|
if (clientRequestedStream === true
|
|
807
837
|
&& options.inboundTransport !== "websocket"
|
|
808
|
-
&&
|
|
838
|
+
&& route.staticPolicy.model.responsesUpstreamStreaming === false
|
|
809
839
|
&& route.provider.adapter === "openai-responses") {
|
|
810
840
|
let completed: Record<string, unknown> | undefined;
|
|
811
841
|
try {
|
|
@@ -852,7 +882,7 @@ export async function deliverPassthroughResponse(
|
|
|
852
882
|
// WS turns reframe this JSON into events in the bridge, which is the
|
|
853
883
|
// other relay-free path — normalize ids so both bounded-JSON paths agree.
|
|
854
884
|
const outboundJson = options.inboundTransport === "websocket"
|
|
855
|
-
&&
|
|
885
|
+
&& route.staticPolicy.model.responsesUpstreamStreaming === false
|
|
856
886
|
&& hasResponsesItemIdRepair(route.provider.responsesItemIdRepair)
|
|
857
887
|
? (() => {
|
|
858
888
|
try {
|