@bitkyc08/opencodex 2.83.0 → 2.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/bin/ocx.mjs +61 -3
- package/gui/dist/assets/App-BjlttVLy.js +58 -0
- package/gui/dist/assets/Tray-DyR9yBee.js +1 -0
- package/gui/dist/assets/{index-BAbcj51J.css → index-BB6uK6tU.css} +1 -1
- package/gui/dist/assets/index-BMssP4k3.js +94 -0
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +2 -3
- package/src/adapters/anthropic/beta-allowlist.ts +87 -8
- package/src/adapters/anthropic/client-identity.ts +1 -1
- package/src/adapters/anthropic/passthrough.ts +33 -4
- package/src/adapters/azure.ts +19 -1
- package/src/adapters/client-fingerprint.ts +24 -0
- package/src/adapters/coding-agent/protocol.ts +15 -8
- package/src/adapters/coding-agent/turn.ts +22 -9
- package/src/adapters/cursor/arg-codec.ts +17 -5
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +3 -0
- package/src/adapters/cursor/freeform-args.ts +75 -0
- package/src/adapters/cursor/protobuf-events.ts +36 -23
- package/src/adapters/google-antigravity-wire.ts +22 -1
- package/src/adapters/google.ts +2 -2
- package/src/adapters/ollama-native.ts +29 -1
- package/src/adapters/openai-chat/messages.ts +8 -4
- package/src/adapters/openai-chat/response-events.ts +59 -1
- package/src/adapters/openai-chat.ts +8 -5
- package/src/adapters/openai-responses/empty-assistant.ts +32 -0
- package/src/adapters/openai-responses/passthrough.ts +3 -0
- package/src/adapters/openai-responses/request-strips.ts +10 -2
- package/src/adapters/qoder/adapter.ts +56 -15
- package/src/advisor/consult.ts +220 -0
- package/src/advisor/context.ts +226 -0
- package/src/advisor/disclosure.ts +20 -0
- package/src/advisor/runtime.ts +305 -0
- package/src/advisor/settings.ts +135 -0
- package/src/advisor/state.ts +369 -0
- package/src/advisor/synthetic-tool.ts +33 -0
- package/src/bridge/errors.ts +24 -0
- package/src/bridge.ts +1 -1
- package/src/chat/inbound.ts +30 -6
- package/src/chat/outbound.ts +5 -0
- package/src/claude/context-windows.ts +54 -1
- package/src/claude/desktop-3p-library.ts +43 -3
- package/src/claude/desktop-3p.ts +22 -8
- package/src/claude/desktop-picker-profile.ts +11 -4
- package/src/claude/desktop-profile.ts +10 -1
- package/src/claude/desktop-remote-store-io.ts +5 -3
- package/src/claude/inbound.ts +121 -50
- package/src/claude/intercept/connect-proxy.ts +10 -3
- package/src/claude/intercept/local-ca.ts +8 -1
- package/src/claude/intercept/picker-ca-persistence.ts +3 -1
- package/src/claude/intercept/picker-listener.ts +49 -1
- package/src/claude/intercept/settings-no-proxy.ts +146 -0
- package/src/claude/intercept/settings.ts +101 -9
- package/src/claude/model-default-settings.ts +154 -0
- package/src/claude/model-info.ts +7 -7
- package/src/claude/outbound.ts +13 -6
- package/src/cli/account-api.ts +65 -0
- package/src/cli/account-auth.ts +26 -2
- package/src/cli/account.ts +41 -5
- package/src/cli/advisor.ts +151 -0
- package/src/cli/auxiliary-listener-preflight.ts +37 -0
- package/src/cli/capabilities-access-remote.ts +8 -0
- package/src/cli/capabilities-agents-routing.ts +18 -0
- package/src/cli/capabilities-base.ts +14 -2
- package/src/cli/catalog-command-result.ts +2 -1
- package/src/cli/chatgpt-command.ts +35 -14
- package/src/cli/claude.ts +60 -11
- package/src/cli/config-command.ts +8 -2
- package/src/cli/debug.ts +8 -0
- package/src/cli/dispatch.ts +21 -4
- package/src/cli/doctor.ts +9 -0
- package/src/cli/ensure-desktop-cold-start.ts +329 -0
- package/src/cli/export-command.ts +15 -2
- package/src/cli/help.ts +2 -1
- package/src/cli/hub-disable.ts +99 -0
- package/src/cli/hub.ts +4 -2
- package/src/cli/index.ts +26 -8
- package/src/cli/integration-plan-dto.ts +2 -0
- package/src/cli/link-child.ts +104 -0
- package/src/cli/link-enrollment.ts +96 -0
- package/src/cli/link.ts +39 -24
- package/src/cli/observe-stream.ts +6 -1
- package/src/cli/observe.ts +10 -2
- package/src/cli/provider.ts +12 -0
- package/src/cli/registry.ts +20 -7
- package/src/cli/restart-scope.ts +3 -1
- package/src/cli/runtime-api.ts +27 -2
- package/src/cli/status.ts +104 -20
- package/src/client/connect.ts +39 -4
- package/src/client/hub-client.ts +14 -7
- package/src/client/hub-state.ts +25 -14
- package/src/client/link-home.ts +248 -0
- package/src/client/link-management-relay.ts +61 -0
- package/src/client/link-relay.ts +6 -1
- package/src/client/link-status.ts +14 -0
- package/src/client/machine-api.ts +14 -7
- package/src/client/machine-listener.ts +29 -4
- package/src/clients/config-export/contracts.ts +1 -0
- package/src/clients/config-export/model-metadata.ts +3 -2
- package/src/clients/config-export/qoder.ts +93 -0
- package/src/clients/config-export.ts +59 -16
- package/src/clients/omo-role-models.ts +6 -3
- package/src/codex/agent-role-models.ts +26 -8
- package/src/codex/auth-api/account-list.ts +17 -2
- package/src/codex/auth-api/account-selection.ts +17 -7
- package/src/codex/auth-api/pool-mode-gate.ts +5 -2
- package/src/codex/auth-api/routes.ts +5 -1
- package/src/codex/auth-context.ts +79 -5
- package/src/codex/autostart-health.ts +7 -1
- package/src/codex/catalog/build-entries.ts +11 -2
- package/src/codex/catalog/bundled.ts +5 -1
- package/src/codex/catalog/configured-tiers.ts +50 -0
- package/src/codex/catalog/effort.ts +6 -3
- package/src/codex/catalog/metadata.ts +4 -2
- package/src/codex/catalog/model-hints.ts +21 -3
- package/src/codex/catalog/native-models.ts +16 -2
- package/src/codex/catalog/native-ultrafast.ts +106 -0
- package/src/codex/catalog/parsing.ts +5 -0
- package/src/codex/catalog/provider-models.ts +4 -0
- package/src/codex/catalog/retained-sync.ts +2 -0
- package/src/codex/catalog/routed-gather.ts +22 -4
- package/src/codex/catalog/vendor-context.ts +82 -0
- package/src/codex/config-write-lock.ts +40 -2
- package/src/codex/convergence.ts +2 -0
- package/src/codex/desktop-app/types.ts +2 -0
- package/src/codex/desktop-app/windows.ts +29 -16
- package/src/codex/desktop-app-restart.ts +4 -2
- package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
- package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
- package/src/codex/desktop-compatibility/connection-store.ts +87 -0
- package/src/codex/desktop-compatibility/installed-build.ts +55 -0
- package/src/codex/desktop-compatibility/json-body.ts +29 -0
- package/src/codex/desktop-compatibility/native-identity.ts +62 -0
- package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
- package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
- package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
- package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
- package/src/codex/desktop-compatibility/runtime.ts +173 -0
- package/src/codex/desktop-compatibility/service.ts +30 -0
- package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
- package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
- package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
- package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
- package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
- package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
- package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
- package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
- package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
- package/src/codex/desktop-compatibility/windows-key-protection.ts +59 -0
- package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
- package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
- package/src/codex/history-provider.ts +1 -1
- package/src/codex/inject/config-write-section.ts +4 -1
- package/src/codex/inject/remove.ts +18 -6
- package/src/codex/journal.ts +16 -1
- package/src/codex/main-account.ts +99 -15
- package/src/codex/native-main-refresh-mode.ts +217 -0
- package/src/codex/native-profile-startup.ts +4 -1
- package/src/codex/prompt-journal.ts +20 -2
- package/src/codex/prompt-layers/encoding.ts +49 -0
- package/src/codex/prompt-layers/import-source.ts +30 -0
- package/src/codex/prompt-layers/toml-edit.ts +269 -37
- package/src/codex/prompt-layers/toml-read.ts +52 -4
- package/src/codex/prompt-layers.ts +383 -20
- package/src/codex/prompt-lock-claim.ts +13 -3
- package/src/codex/prompt-lock-evidence.ts +3 -3
- package/src/codex/prompt-lock-owner.ts +14 -6
- package/src/codex/prompt-text-probe.ts +34 -5
- package/src/codex/quota.ts +13 -6
- package/src/codex/role-auto-assign.ts +2 -5
- package/src/codex/routing/active-account.ts +21 -0
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/pool-recovery.ts +79 -0
- package/src/codex/routing.ts +1 -1
- package/src/codex/runtime.ts +196 -8
- package/src/codex/shim-diagnostics.ts +6 -0
- package/src/codex/shim-overlay.ts +15 -6
- package/src/codex/shim.ts +1 -1
- package/src/combos/failover.ts +3 -0
- package/src/config/api-key-reconcile.ts +34 -0
- package/src/config/diagnostics.ts +18 -1
- package/src/config/feature-flags.ts +5 -0
- package/src/config/live-reconcile.ts +56 -29
- package/src/config/live-topology.ts +29 -0
- package/src/config/load-degrade.ts +8 -2
- package/src/config/paths.ts +13 -3
- package/src/config/salvage.ts +3 -1
- package/src/config/schema/config-schema.ts +19 -0
- package/src/config/schema/desktop-compatibility.ts +9 -0
- package/src/config/schema/leaf-validators.ts +1 -0
- package/src/generated/compatibility-version.json +713 -285
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/grpc-web.ts +23 -1
- package/src/grok/reset-coupon-ledger.ts +75 -5
- package/src/grok/reset-coupons.ts +4 -2
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/config-io.ts +20 -2
- package/src/integrations/journal.ts +15 -1
- package/src/integrations/mutation-plan.ts +14 -7
- package/src/integrations/registry.ts +52 -11
- package/src/integrations/state.ts +19 -7
- package/src/integrations/store.ts +3 -0
- package/src/integrations/target.ts +42 -5
- package/src/integrations/writer.ts +24 -7
- package/src/lib/advisor-activation.ts +16 -0
- package/src/lib/app-owned-memory-stores.ts +4 -0
- package/src/lib/app-owned-memory.ts +11 -0
- package/src/lib/background-work.ts +65 -0
- package/src/lib/bounded-file-read.ts +58 -0
- package/src/lib/bun-path-runtime.d.mts +10 -1
- package/src/lib/bun-path-runtime.mjs +62 -3
- package/src/lib/desktop-proxy-route.ts +30 -0
- package/src/lib/desktop-upstream-tunnel.ts +107 -0
- package/src/lib/errors.ts +7 -0
- package/src/lib/json-byte-size.ts +31 -0
- package/src/lib/local-internal-call-capability.ts +55 -0
- package/src/lib/optional-shutdown-hooks.ts +35 -0
- package/src/lib/outbound-credential-registry.ts +210 -0
- package/src/lib/package-tree-integrity.ts +2 -2
- package/src/lib/passive-read.ts +35 -0
- package/src/lib/shadow-call.ts +44 -5
- package/src/lib/socks5-fetch.ts +48 -71
- package/src/lib/socks5-handshake.ts +112 -0
- package/src/lib/translator-budget.ts +7 -1
- package/src/link/codex-settings.ts +155 -0
- package/src/link/management-routes.ts +89 -0
- package/src/link/remote-probe.ts +38 -0
- package/src/link/ssh-argv.ts +26 -5
- package/src/link/ssh-runner.ts +58 -27
- package/src/link/status-projection.ts +22 -4
- package/src/link/supervisor.ts +144 -49
- package/src/link/tunnel-state.ts +11 -4
- package/src/oauth/anthropic-continuity.ts +4 -3
- package/src/oauth/anthropic-identity.ts +13 -2
- package/src/oauth/anthropic-model-quota.ts +13 -1
- package/src/oauth/anthropic-routing.ts +230 -23
- package/src/oauth/anthropic.ts +18 -4
- package/src/oauth/generic-account-failover.ts +29 -5
- package/src/oauth/google-antigravity.ts +24 -7
- package/src/oauth/index.ts +28 -17
- package/src/oauth/kiro-account-identity.ts +106 -0
- package/src/oauth/kiro-device-login.ts +2 -1
- package/src/oauth/kiro.ts +55 -6
- package/src/oauth/plan.ts +29 -0
- package/src/oauth/store.ts +40 -3
- package/src/oauth/types.ts +2 -0
- package/src/plugins/loader.ts +11 -0
- package/src/protocols/codecs/chat-ir.ts +467 -0
- package/src/protocols/codecs/chat.ts +10 -1
- package/src/protocols/codecs/messages-ir.ts +500 -0
- package/src/protocols/features.ts +10 -2
- package/src/protocols/path.ts +11 -1
- package/src/protocols/plan-snapshot.ts +39 -16
- package/src/protocols/plan.ts +9 -1
- package/src/protocols/settings.ts +8 -0
- package/src/providers/anthropic-billing-ownership.ts +74 -0
- package/src/providers/anthropic-billing-recovery.ts +31 -0
- package/src/providers/anthropic-billing-refusal.ts +55 -0
- package/src/providers/anthropic-reset-admission.ts +62 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +28 -6
- package/src/providers/antigravity-account-roster.ts +102 -0
- package/src/providers/antigravity-models.ts +85 -7
- package/src/providers/api-key-resolve.ts +17 -2
- package/src/providers/key-failover.ts +52 -5
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/model-rename-migration.ts +5 -3
- package/src/providers/model-rename-startup.ts +5 -3
- package/src/providers/quota/account-cache.ts +11 -0
- package/src/providers/quota/anthropic-account-quota.ts +4 -2
- package/src/providers/quota/report-cache.ts +14 -0
- package/src/providers/quota/vendor-probes-oauth.ts +3 -3
- package/src/providers/quota.ts +4 -1
- package/src/providers/registry/entries-core.ts +21 -20
- package/src/providers/registry/entries-extended.ts +19 -1
- package/src/providers/registry/model-seeds.ts +17 -0
- package/src/providers/request-pacing.ts +9 -0
- package/src/providers/stale-no-vision-seed-migration.ts +72 -0
- package/src/responses/compaction.ts +14 -0
- package/src/responses/parser.ts +37 -20
- package/src/responses/state.ts +32 -29
- package/src/routing/history/build-file.ts +92 -0
- package/src/routing/history/indexer.ts +248 -216
- package/src/routing/history/ingest.ts +238 -0
- package/src/routing/history/rebuild-job.ts +111 -0
- package/src/routing/history/rebuild-protocol.ts +15 -0
- package/src/routing/history/rebuild-worker.ts +34 -0
- package/src/server/admission-model-scope.ts +4 -1
- package/src/server/antigravity-direct-eligibility.ts +208 -0
- package/src/server/auth-cors.ts +46 -0
- package/src/server/chat-completions.ts +75 -9
- package/src/server/claude-messages.ts +110 -20
- package/src/server/configured-credentials.ts +194 -0
- package/src/server/index/desktop-compatibility-startup.ts +56 -0
- package/src/server/index/link-management.ts +58 -0
- package/src/server/index/optional-listeners.ts +5 -1
- package/src/server/index/serve-options.ts +15 -2
- package/src/server/index/startup-warnings.ts +9 -0
- package/src/server/index/websocket-handler.ts +9 -0
- package/src/server/index.ts +13 -10
- package/src/server/inference/client-encoder-delivery.ts +6 -5
- package/src/server/lifecycle.ts +2 -1
- package/src/server/login-shell-path.ts +146 -0
- package/src/server/management/advisor-routes.ts +191 -0
- package/src/server/management/agent-settings-routes.ts +50 -14
- package/src/server/management/anthropic-reset-grant-routes.ts +17 -1
- package/src/server/management/codex-agent-role-routes.ts +19 -6
- package/src/server/management/codex-prompt-routes.ts +136 -5
- package/src/server/management/codex-role-auto-assign.ts +1 -1
- package/src/server/management/companion-routes.ts +4 -0
- package/src/server/management/config-routes.ts +2 -2
- package/src/server/management/context.ts +4 -0
- package/src/server/management/desktop-compatibility-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
- package/src/server/management/grok-coupon-routes.ts +78 -27
- package/src/server/management/link-enrollment.ts +192 -0
- package/src/server/management/link-route-state.ts +185 -0
- package/src/server/management/link-routes.ts +82 -402
- package/src/server/management/model-routes.ts +2 -2
- package/src/server/management/model-rows.ts +3 -1
- package/src/server/management/protocol-settings-patch.ts +1 -0
- package/src/server/management/provider-overwrite-carry.ts +10 -0
- package/src/server/management/provider-routes.ts +1 -0
- package/src/server/management/route-registry.ts +16 -3
- package/src/server/management/shared.ts +3 -0
- package/src/server/management/sibling-guard.ts +1 -0
- package/src/server/management-api.ts +9 -1
- package/src/server/messages-antigravity-direct.ts +195 -0
- package/src/server/messages-native.ts +73 -43
- package/src/server/relay-eager.ts +2 -0
- package/src/server/request-log-filter.ts +6 -1
- package/src/server/request-log-terminal-status.ts +63 -6
- package/src/server/request-log.ts +7 -0
- package/src/server/responses/adapter-continuation.ts +35 -18
- package/src/server/responses/adapter-delivery.ts +24 -2
- package/src/server/responses/adapter-dispatch.ts +261 -81
- package/src/server/responses/advisor-plan-slot.ts +27 -0
- package/src/server/responses/advisor-slot.ts +336 -0
- package/src/server/responses/code-mode-collab-surface.ts +313 -0
- package/src/server/responses/codex-ws-exchange.ts +147 -6
- package/src/server/responses/codex-ws-wire.ts +203 -0
- package/src/server/responses/collaboration.ts +36 -7
- package/src/server/responses/compact.ts +126 -48
- package/src/server/responses/context-overflow.ts +13 -1
- package/src/server/responses/core-codex-account.ts +105 -1
- package/src/server/responses/core-combo-failure.ts +24 -1
- package/src/server/responses/core-combo.ts +28 -2
- package/src/server/responses/core-errors.ts +29 -1
- package/src/server/responses/core-lifetime.ts +2 -0
- package/src/server/responses/core-normalize.ts +11 -0
- package/src/server/responses/core-options.ts +48 -0
- package/src/server/responses/core-replay.ts +7 -4
- package/src/server/responses/core.ts +1 -1
- package/src/server/responses/deferred-collab-surface.ts +158 -0
- package/src/server/responses/deferred-send-reporter.ts +25 -0
- package/src/server/responses/fetch-helpers.ts +12 -1
- package/src/server/responses/passthrough-delivery.ts +20 -11
- package/src/server/responses/passthrough-dispatch.ts +29 -7
- package/src/server/responses/request-prepare.ts +74 -9
- package/src/server/responses/request-transport.ts +22 -8
- package/src/server/responses/run-turn-execution.ts +70 -8
- package/src/server/responses/sidecar-execution.ts +89 -5
- package/src/server/responses/terminal-guard.ts +2 -1
- package/src/server/responses/ws-upstream.ts +9 -1
- package/src/server/responses-undeclared-tool-guard.ts +30 -9
- package/src/server/startup-action-control.ts +38 -4
- package/src/server/startup-health-cache.ts +5 -0
- package/src/server/system-env-shell.ts +7 -2
- package/src/server/system-env.ts +11 -1
- package/src/service/cli.ts +5 -1
- package/src/service/desktop-startup.ts +161 -34
- package/src/service/desktop-supervision.d.mts +27 -1
- package/src/service/desktop-supervision.mjs +111 -5
- package/src/service/health-sentinel-launch.ts +176 -0
- package/src/service/health-sentinel-main.ts +466 -0
- package/src/service/health-sentinel-policy.ts +241 -0
- package/src/service/health-sentinel-probe.ts +71 -0
- package/src/service/health-sentinel-windows.ts +87 -0
- package/src/service/linux-linger.ts +20 -0
- package/src/service/logging.ts +72 -0
- package/src/tray/windows-tray.ps1 +43 -24
- package/src/types/config.ts +68 -6
- package/src/types/provider.ts +7 -0
- package/src/types/request.ts +15 -0
- package/src/types/tools.ts +2 -0
- package/src/update/async-check.ts +1 -1
- package/src/update/badge.ts +2 -2
- package/src/update/index.ts +88 -15
- package/src/update/job-supervised-restart.ts +107 -0
- package/src/update/job.ts +32 -32
- package/src/update/notify.ts +1 -1
- package/src/update/refresh-scheduler.ts +2 -2
- package/src/update/supervised-recovery.d.mts +27 -0
- package/src/update/supervised-recovery.mjs +157 -0
- package/src/usage/cache-diagnostic.ts +2 -0
- package/src/usage/expected-prices.ts +16 -0
- package/src/usage/log.ts +19 -1
- package/src/web-search/gemini-executor.ts +2 -2
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/App-Ba8uG696.js +0 -54
- package/gui/dist/assets/Tray-IImbNPRy.js +0 -1
- package/gui/dist/assets/index-BHRZdJuv.js +0 -94
- package/gui/dist/assets/quota-summary-DiJO6sYY.js +0 -1
|
@@ -139,6 +139,8 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
|
|
|
139
139
|
{ method: "POST", path: "/api/native-main-profiles/switch", module: "codex/native-profile-api", mutates: true },
|
|
140
140
|
// server/management/agent-settings-routes
|
|
141
141
|
{ method: "GET", path: "/api/claude-code", module: "server/management/agent-settings-routes", mutates: false },
|
|
142
|
+
{ method: "GET", path: "/api/claude-code/model-defaults", module: "server/management/agent-settings-routes", mutates: false, exempt: { reason: "interactive-preview", why: "Preview half of the GUI-only Claude Code tier-default apply; the fingerprint it returns is the only input POST accepts, and no terminal confirmation workflow exists yet." } },
|
|
143
|
+
{ method: "POST", path: "/api/claude-code/model-defaults", module: "server/management/agent-settings-routes", mutates: true, exempt: { reason: "interactive-preview", why: "Apply half of the GUI-only Claude Code tier-default preview; it writes the shared Claude settings only for a fingerprint the dashboard preview produced." } },
|
|
142
144
|
{ method: "GET", path: "/api/claude-desktop", module: "server/management/agent-settings-routes", mutates: false },
|
|
143
145
|
{ method: "GET", path: "/api/claude-desktop/status", module: "server/management/agent-settings-routes", mutates: false },
|
|
144
146
|
{ method: "GET", path: "/api/codex-auth/features/default-mode-request-user-input", module: "server/management/agent-settings-routes", mutates: false },
|
|
@@ -163,6 +165,12 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
|
|
|
163
165
|
// server/management/claude-desktop-picker-routes
|
|
164
166
|
{ method: "GET", path: "/api/claude-desktop/picker", module: "server/management/claude-desktop-picker-routes", mutates: false },
|
|
165
167
|
{ method: "PUT", path: "/api/claude-desktop/picker", module: "server/management/claude-desktop-picker-routes", mutates: true },
|
|
168
|
+
{ method: "GET", path: "/api/codex/desktop-compatibility/certificate", module: "server/management/desktop-compatibility-routes", mutates: false, mechanism: "path-constant", exempt: { reason: "deferred-verb", owner: "codex-desktop-compatibility", ownerDoc: "structure/clients/codex-desktop.md", why: "Certificate status is available over authenticated HTTP while the desktop compatibility runtime/CLI status contract is integrated." } },
|
|
169
|
+
{ method: "POST", path: "/api/codex/desktop-compatibility/certificate", module: "server/management/desktop-compatibility-routes", mutates: true, mechanism: "path-constant", exempt: { reason: "session-only", why: "Certificate setup requires a confirmed local dashboard session; raw admin tokens cannot enroll or remove OS trust." } },
|
|
170
|
+
{ method: "GET", path: "/api/codex/desktop-compatibility/runtime", module: "server/management/desktop-compatibility-runtime-routes", mutates: false, mechanism: "path-constant", exempt: { reason: "deferred-verb", owner: "codex-desktop-compatibility", ownerDoc: "structure/clients/codex-desktop.md", why: "Experimental runtime status is authenticated HTTP while the native compatibility UI and CLI contract are integrated." } },
|
|
171
|
+
{ method: "POST", path: "/api/codex/desktop-compatibility/runtime", module: "server/management/desktop-compatibility-runtime-routes", mutates: true, mechanism: "path-constant", exempt: { reason: "session-only", why: "Native app launch and account-wide UI trial require explicit local dashboard confirmation." } },
|
|
172
|
+
{ method: "GET", path: "/api/codex/desktop-compatibility/settings", module: "server/management/desktop-compatibility-settings-routes", mutates: false, mechanism: "path-constant", exempt: { reason: "deferred-verb", owner: "codex-desktop-compatibility", ownerDoc: "structure/clients/codex-desktop.md", why: "The persisted startup preference is currently owned by the local desktop compatibility panel." } },
|
|
173
|
+
{ method: "POST", path: "/api/codex/desktop-compatibility/settings", module: "server/management/desktop-compatibility-settings-routes", mutates: true, mechanism: "path-constant", exempt: { reason: "session-only", why: "Saved native observation intent requires an explicit local GUI action and fresh field revision." } },
|
|
166
174
|
{ method: "PUT", path: "/api/codex-auth/features/default-mode-request-user-input", module: "server/management/agent-settings-routes", mutates: true },
|
|
167
175
|
{ method: "PUT", path: "/api/effort-caps", module: "server/management/agent-settings-routes", mutates: true },
|
|
168
176
|
{ method: "PUT", path: "/api/grok/selection", module: "server/management/agent-settings-routes", mutates: true },
|
|
@@ -197,6 +205,7 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
|
|
|
197
205
|
{ method: "POST", path: "/api/codex-prompt/repair", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Prompt repair requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
198
206
|
{ method: "PUT", path: "/api/codex-prompt/base", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Base prompt write requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
199
207
|
{ method: "PUT", path: "/api/codex-prompt/base/select", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Base prompt selection requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
208
|
+
{ method: "POST", path: "/api/codex-prompt/base/import", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Base prompt import requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
200
209
|
{ method: "PUT", path: "/api/codex-prompt/custom", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Custom prompt write requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
201
210
|
{ method: "PUT", path: "/api/codex-prompt/toggle", module: "server/management/codex-prompt-routes", mutates: true, exempt: { reason: "session-only", why: "Prompt toggle requires the gui-session principal (codex-prompt-routes.ts:298)." } },
|
|
202
211
|
// server/management/combo-routes
|
|
@@ -266,6 +275,9 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
|
|
|
266
275
|
{ method: "GET", path: "/api/usage", module: "server/management/logs-usage-routes", mutates: false },
|
|
267
276
|
// server/management/usage-timeline-routes
|
|
268
277
|
{ method: "GET", path: "/api/usage/timeline", module: "server/management/usage-timeline-routes", mutates: false },
|
|
278
|
+
// server/management/advisor-routes
|
|
279
|
+
{ method: "GET", path: "/api/advisor/settings", module: "server/management/advisor-routes", mutates: false },
|
|
280
|
+
{ method: "PUT", path: "/api/advisor/settings", module: "server/management/advisor-routes", mutates: true },
|
|
269
281
|
// server/management/companion-routes
|
|
270
282
|
{ method: "POST", path: "/api/companion/open-in-browser", module: "server/management/companion-routes", mutates: true, exempt: { reason: "browser-navigation", why: "Opens the companion's current view in the system browser under normal management admission; the CLI reads the underlying settings and usage directly." } },
|
|
271
283
|
{ method: "GET", path: "/api/companion/settings", module: "server/management/companion-routes", mutates: false },
|
|
@@ -390,11 +402,12 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
|
|
|
390
402
|
// server/management/link-routes
|
|
391
403
|
{ method: "GET", path: "/api/link/status", module: "server/management/link-routes", mutates: false },
|
|
392
404
|
{ method: "GET", path: "/api/link/candidates", module: "server/management/link-routes", mutates: false, exempt: { reason: "session-only", why: "SSH candidates are a dashboard surface: a paired session, or on a standalone runtime the current loopback dashboard session on trusted loopback ingress; admin-token and Tailscale identity sessions are refused." } },
|
|
393
|
-
{ method: "POST", path: "/api/link/probe", module: "server/management/link-routes", mutates: true
|
|
394
|
-
{ method: "POST", path: "/api/link/confirm-host", module: "server/management/link-routes", mutates: true
|
|
405
|
+
{ method: "POST", path: "/api/link/probe", module: "server/management/link-routes", mutates: true },
|
|
406
|
+
{ method: "POST", path: "/api/link/confirm-host", module: "server/management/link-routes", mutates: true },
|
|
395
407
|
{ method: "POST", path: "/api/link/join", module: "server/management/link-routes", mutates: true, exempt: { reason: "session-only", why: "Joining a confirmed Home issues a link credential and restarts this standalone runtime as a client on its configured port, which briefly interrupts running Codex turns. It requires an operator-paired dashboard session and a pre-join notice; unpaired standalone loopback, admin-token and Tailscale identity sessions are refused." } },
|
|
396
|
-
{ method: "POST", path: "/api/link/apply", module: "server/management/link-routes", mutates: true
|
|
408
|
+
{ method: "POST", path: "/api/link/apply", module: "server/management/link-routes", mutates: true },
|
|
397
409
|
{ method: "DELETE", path: "/api/link/{id}", module: "server/management/link-routes", mutates: true, mechanism: "regex" },
|
|
410
|
+
{ method: "POST", path: "/api/link/{id}/reconnect", module: "server/management/link-routes", mutates: true, mechanism: "regex" },
|
|
398
411
|
{ method: "POST", path: "/api/link/issue", module: "server/management/link-routes", mutates: true },
|
|
399
412
|
// server/management/remote-workspace-routes
|
|
400
413
|
{ method: "GET", path: "/api/remote-workspace", module: "server/management/remote-workspace-routes", mutates: false },
|
|
@@ -3,6 +3,7 @@ import { captureInitialSelectionBaseline, finalizeInitialModelSelection } from "
|
|
|
3
3
|
import { captureModelDiscoveryBaseline, finalizeModelDiscovery } from "../../providers/new-model-policy-runtime";
|
|
4
4
|
import { CatalogGatherBusyError } from "../../codex/catalog/routed-gather";
|
|
5
5
|
import { initialModelSelectionPending } from "../../providers/initial-model-selection";
|
|
6
|
+
import { isPassiveRead } from "../../lib/passive-read";
|
|
6
7
|
import { readFileSync } from "node:fs";
|
|
7
8
|
import type { CatalogModel } from "../../codex/catalog";
|
|
8
9
|
import { catalogModelSlug, invalidateCodexModelsCache, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
@@ -291,6 +292,8 @@ export async function fetchAllModels(
|
|
|
291
292
|
/** Management renders disabled rows using this detached state after inventory drift. */
|
|
292
293
|
publishProjection?: (projection: OcxConfig) => void,
|
|
293
294
|
): Promise<CatalogModel[]> {
|
|
295
|
+
// A Remote Link read: no upstream, no initial selection, no write (#6959).
|
|
296
|
+
if (isPassiveRead()) return [];
|
|
294
297
|
const { gatherRoutedModels } = await import("../../codex/catalog");
|
|
295
298
|
const baseline = captureInitialSelectionBaseline(config);
|
|
296
299
|
const discoveryBaseline = captureModelDiscoveryBaseline(config);
|
|
@@ -30,6 +30,7 @@ export const SIBLING_REFUSED_MANAGEMENT_PATHS: readonly { readonly path: string;
|
|
|
30
30
|
{ path: "/api/client-integrations", children: true },
|
|
31
31
|
{ path: "/api/native-integrations", children: true },
|
|
32
32
|
{ path: "/api/claude-desktop", children: true },
|
|
33
|
+
{ path: "/api/codex/desktop-compatibility", children: true },
|
|
33
34
|
{ path: "/api/claude-code", children: false },
|
|
34
35
|
{ path: "/api/grok/apply", children: false },
|
|
35
36
|
{ path: "/api/grok/selection", children: false },
|
|
@@ -54,7 +54,7 @@ import { noteExplicitShutdownRequested } from "./management/system-restart";
|
|
|
54
54
|
import { filterRequestLogs, getRequestLogEntries, type RequestLogEntry } from "./request-log";
|
|
55
55
|
import { estimateComboCost, estimateRequestCost, normalizeCostTokens, tokensPerSecond } from "../usage/cost";
|
|
56
56
|
import type { PersistedUsageAttempt } from "../usage/log";
|
|
57
|
-
import { isAllowedManagementOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO } from "./auth-cors";
|
|
57
|
+
import { inheritTrustedHubManagementRequest, isAllowedManagementOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO } from "./auth-cors";
|
|
58
58
|
import { applySystemEnvToggle } from "./system-env";
|
|
59
59
|
|
|
60
60
|
import type { ManagementApiDeps } from "./management/context";
|
|
@@ -79,6 +79,9 @@ import { handleCodexPromptRoutes } from "./management/codex-prompt-routes";
|
|
|
79
79
|
import { handleIntegrationRoutes } from "./management/integration-routes";
|
|
80
80
|
import { handleNativeIntegrationRoutes } from "./management/native-integration-routes";
|
|
81
81
|
import { handleClaudeDesktopPickerRoutes } from "./management/claude-desktop-picker-routes";
|
|
82
|
+
import { handleDesktopCompatibilityRoutes } from "./management/desktop-compatibility-routes";
|
|
83
|
+
import { handleDesktopCompatibilityRuntimeRoutes } from "./management/desktop-compatibility-runtime-routes";
|
|
84
|
+
import { handleDesktopCompatibilitySettingsRoutes } from "./management/desktop-compatibility-settings-routes";
|
|
82
85
|
import { handleCursorIntegrationRoutes } from "./management/cursor-integration-routes";
|
|
83
86
|
import type { ManagementContext } from "./management/context";
|
|
84
87
|
import type { ManagementPrincipal, ManagementSessionControl } from "./management-auth";
|
|
@@ -242,7 +245,9 @@ export async function handleManagementAPI(
|
|
|
242
245
|
if (verified.status !== 200) {
|
|
243
246
|
return jsonResponse({ error: "account switch body rejected" }, verified.status, req, config);
|
|
244
247
|
}
|
|
248
|
+
const sourceReq = req;
|
|
245
249
|
req = new Request(req.url, { method: req.method, headers: req.headers, body: Buffer.from(verified.body) });
|
|
250
|
+
inheritTrustedHubManagementRequest(sourceReq, req);
|
|
246
251
|
}
|
|
247
252
|
// Management bodies are small JSON (provider names, key ids, settings). Reject oversized
|
|
248
253
|
// payloads before any handler buffers them — the data plane has its own decompression cap.
|
|
@@ -358,6 +363,9 @@ export async function handleManagementAPI(
|
|
|
358
363
|
?? (await handleNativeIntegrationRoutes(ctx))
|
|
359
364
|
?? (await handleCursorIntegrationRoutes(ctx))
|
|
360
365
|
?? (await handleClaudeDesktopPickerRoutes(ctx))
|
|
366
|
+
?? (await handleDesktopCompatibilityRoutes(ctx))
|
|
367
|
+
?? (await handleDesktopCompatibilityRuntimeRoutes(ctx))
|
|
368
|
+
?? (await handleDesktopCompatibilitySettingsRoutes(ctx))
|
|
361
369
|
?? (await handleAgentSettingsRoutes(ctx))
|
|
362
370
|
?? (await handleCodexPromptRoutes(ctx))
|
|
363
371
|
?? (await handleOauthAccountRoutes(ctx))
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Antigravity direct lane for the Messages ingress (`protocols.rollout.antigravityDirect`).
|
|
3
|
+
*
|
|
4
|
+
* The Messages ingress translates before it routes: the route is decided on the translated body's
|
|
5
|
+
* resolved model. So the lane is planned ahead of translation on the settings-only body
|
|
6
|
+
* (`anthropicSettingsTranslation`), whose model is that same resolved model, against a preview of
|
|
7
|
+
* the route; the conversation is decoded straight into the IR only when that preview is an
|
|
8
|
+
* eligible Antigravity route. The ingress then routes the settings-only body as usual and keeps
|
|
9
|
+
* the lane only if the settled route is still the one the plan was made for; otherwise it builds
|
|
10
|
+
* the full bridge translation, as it would have without the switch.
|
|
11
|
+
*
|
|
12
|
+
* Refusals stay the bridge's by construction: a settings error, or an undecodable proxy envelope
|
|
13
|
+
* (which the bridge retries through the native projection), returns no plan and the ingress takes
|
|
14
|
+
* its ordinary translation path, which answers exactly as before. Any other decode error is one
|
|
15
|
+
* the translator's walk raises with the same message at the same point
|
|
16
|
+
* (`tests/responses/protocol-messages-ir-parity.test.ts`).
|
|
17
|
+
*/
|
|
18
|
+
import { anthropicSettingsTranslation, AnthropicRequestError, type ClaudeCacheKeySource } from "../claude/inbound";
|
|
19
|
+
import { messagesToResponsesTranslation } from "../protocols/codecs/messages";
|
|
20
|
+
import {
|
|
21
|
+
MALFORMED_REASONING_SIGNATURE,
|
|
22
|
+
messagesConversationToIr,
|
|
23
|
+
messagesRouteEvidence,
|
|
24
|
+
withDirectMessagesConversation,
|
|
25
|
+
type MessagesIrConversation,
|
|
26
|
+
} from "../protocols/codecs/messages-ir";
|
|
27
|
+
import { resolveProtocolSettings } from "../protocols/settings";
|
|
28
|
+
import type { TranslatorBudget, TranslatorBufferKind } from "../lib/translator-budget";
|
|
29
|
+
import type { PolicyRequestEvidence } from "../routing/evaluator";
|
|
30
|
+
import { previewRouteModel, type RouteResult } from "../router";
|
|
31
|
+
import type { OcxClaudeCodeConfig, OcxConfig } from "../types";
|
|
32
|
+
import { antigravityMessagesDeclineReason, type AntigravityDirectDeclineReason } from "./antigravity-direct-eligibility";
|
|
33
|
+
import { cacheDiagnosticsObserving } from "./request-log";
|
|
34
|
+
import type { DirectRequestOption } from "./responses/core-options";
|
|
35
|
+
|
|
36
|
+
type Rec = Record<string, unknown>;
|
|
37
|
+
|
|
38
|
+
export interface MessagesDirectPlan {
|
|
39
|
+
/** The concrete route the plan was made for. */
|
|
40
|
+
providerName: string;
|
|
41
|
+
modelId: string;
|
|
42
|
+
/** The settings-only Responses body the core receives, and its prompt-cache key provenance. */
|
|
43
|
+
settingsBody: Rec;
|
|
44
|
+
cacheKeySource: ClaudeCacheKeySource;
|
|
45
|
+
conversation: MessagesIrConversation;
|
|
46
|
+
/** What the core would have read from the bridge body for policy routing. */
|
|
47
|
+
evidence: PolicyRequestEvidence;
|
|
48
|
+
/**
|
|
49
|
+
* Release the retained charges the plan's decode made. The ingress calls it when it abandons
|
|
50
|
+
* the plan for the full translation, which charges the same reasoning copies again.
|
|
51
|
+
*/
|
|
52
|
+
releaseDecodeCharges(): void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The request budget, with the retained charges made through it recorded so a decode the ingress
|
|
57
|
+
* abandons can hand them back. Without that, the bridge translation that follows would be held to
|
|
58
|
+
* a budget already spent on a copy nobody keeps, and could answer 413 where the bridge alone fits.
|
|
59
|
+
*/
|
|
60
|
+
function recordingBudget(budget: TranslatorBudget): { budget: TranslatorBudget; release(): void } {
|
|
61
|
+
const charged = new Map<TranslatorBufferKind, number>();
|
|
62
|
+
const recording: TranslatorBudget = {
|
|
63
|
+
openCall: id => budget.openCall(id),
|
|
64
|
+
closeCall: id => budget.closeCall(id),
|
|
65
|
+
reserveTransient: (bytes, scope) => budget.reserveTransient(bytes, scope),
|
|
66
|
+
chargeRetained: (delta, scope) => {
|
|
67
|
+
budget.chargeRetained(delta, scope);
|
|
68
|
+
charged.set(scope.kind, (charged.get(scope.kind) ?? 0) + delta);
|
|
69
|
+
},
|
|
70
|
+
releaseRetained: (bytes, scope) => {
|
|
71
|
+
budget.releaseRetained(bytes, scope);
|
|
72
|
+
charged.set(scope.kind, (charged.get(scope.kind) ?? 0) - bytes);
|
|
73
|
+
},
|
|
74
|
+
observeAcceptedRequestCopy: bytes => budget.observeAcceptedRequestCopy(bytes),
|
|
75
|
+
observeExternallyCapped: (kind, bytes) => budget.observeExternallyCapped(kind, bytes),
|
|
76
|
+
snapshot: () => budget.snapshot(),
|
|
77
|
+
dispose: () => budget.dispose(),
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
budget: recording,
|
|
81
|
+
release: () => {
|
|
82
|
+
for (const [kind, bytes] of charged) if (bytes > 0) budget.releaseRetained(bytes, { kind });
|
|
83
|
+
charged.clear();
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Plan the direct lane for a Messages body, or return `undefined` to translate as before. Throws
|
|
90
|
+
* only what the translator's conversation walk throws for the same body.
|
|
91
|
+
*/
|
|
92
|
+
export function planMessagesAntigravityDirect(
|
|
93
|
+
body: unknown,
|
|
94
|
+
cc: OcxClaudeCodeConfig | undefined,
|
|
95
|
+
config: OcxConfig,
|
|
96
|
+
budget: TranslatorBudget,
|
|
97
|
+
): MessagesDirectPlan | undefined {
|
|
98
|
+
if (!resolveProtocolSettings(config).rollout.antigravityDirect) return undefined;
|
|
99
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return undefined;
|
|
100
|
+
let settings: ReturnType<typeof anthropicSettingsTranslation>;
|
|
101
|
+
let route: RouteResult;
|
|
102
|
+
try {
|
|
103
|
+
settings = anthropicSettingsTranslation(body, cc);
|
|
104
|
+
route = previewRouteModel(config, settings.body.model as string);
|
|
105
|
+
} catch {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const model = settings.body.model as string;
|
|
109
|
+
if (!messagesDirectRouteHolds(route, body as Rec, model, config)) return undefined;
|
|
110
|
+
const recorded = recordingBudget(budget);
|
|
111
|
+
let conversation: MessagesIrConversation;
|
|
112
|
+
try {
|
|
113
|
+
conversation = messagesConversationToIr(body, cc, recorded.budget, model);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
if (err instanceof AnthropicRequestError && err.message === MALFORMED_REASONING_SIGNATURE) {
|
|
116
|
+
// The bridge retries this body through the native projection; give it the whole budget.
|
|
117
|
+
recorded.release();
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
providerName: route.providerName,
|
|
124
|
+
modelId: route.modelId,
|
|
125
|
+
settingsBody: settings.body,
|
|
126
|
+
cacheKeySource: settings.cacheKeySource,
|
|
127
|
+
conversation,
|
|
128
|
+
evidence: messagesRouteEvidence(body),
|
|
129
|
+
releaseDecodeCharges: recorded.release,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Whether a route may carry this body on the direct lane, judged as the ingress judges it. */
|
|
134
|
+
export function messagesDirectRouteHolds(
|
|
135
|
+
route: Pick<RouteResult, "providerName" | "modelId" | "provider" | "combo" | "routeKind">,
|
|
136
|
+
body: Rec,
|
|
137
|
+
model: string,
|
|
138
|
+
config: OcxConfig,
|
|
139
|
+
): boolean {
|
|
140
|
+
return antigravityMessagesDeclineReason(route, body, model, config, { cacheDiagnostics: cacheDiagnosticsObserving() }) === undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Whether the route the ingress settled is still the one `plan` was made for, and still eligible. */
|
|
144
|
+
export function messagesDirectPlanHolds(plan: MessagesDirectPlan, route: RouteResult, body: Rec, config: OcxConfig): boolean {
|
|
145
|
+
return route.providerName === plan.providerName && route.modelId === plan.modelId
|
|
146
|
+
&& messagesDirectRouteHolds(route, body, plan.settingsBody.model as string, config);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The lane's decline for a bridged request's trace, when it says something about this route: with
|
|
151
|
+
* the switch off or a non-Antigravity target the path already explains the bridge.
|
|
152
|
+
*/
|
|
153
|
+
export function messagesDirectTraceDecline(
|
|
154
|
+
route: RouteResult,
|
|
155
|
+
body: unknown,
|
|
156
|
+
model: string,
|
|
157
|
+
config: OcxConfig,
|
|
158
|
+
): AntigravityDirectDeclineReason | undefined {
|
|
159
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return undefined;
|
|
160
|
+
const reason = antigravityMessagesDeclineReason(route, body as Rec, model, config, { cacheDiagnostics: cacheDiagnosticsObserving() });
|
|
161
|
+
return reason === "rollout-disabled" || reason === "not-migrated" ? undefined : reason;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The core option for a planned request. The bridge conversation is translated at most once, and
|
|
166
|
+
* only if the core asks for it (a reroute, or an ambiguous-reset judgment).
|
|
167
|
+
*/
|
|
168
|
+
export function messagesDirectRequestOption(
|
|
169
|
+
plan: MessagesDirectPlan,
|
|
170
|
+
body: unknown,
|
|
171
|
+
cc: OcxClaudeCodeConfig | undefined,
|
|
172
|
+
budget: TranslatorBudget,
|
|
173
|
+
): DirectRequestOption {
|
|
174
|
+
let bridge: Rec | undefined;
|
|
175
|
+
return {
|
|
176
|
+
protocol: "messages",
|
|
177
|
+
providerName: plan.providerName,
|
|
178
|
+
modelId: plan.modelId,
|
|
179
|
+
evidence: plan.evidence,
|
|
180
|
+
decode: base => withDirectMessagesConversation(
|
|
181
|
+
base,
|
|
182
|
+
plan.conversation,
|
|
183
|
+
modelId => messagesConversationToIr(body, cc, budget, modelId),
|
|
184
|
+
),
|
|
185
|
+
bridgeConversation: () => {
|
|
186
|
+
bridge ??= messagesToResponsesTranslation(body, cc, budget).body;
|
|
187
|
+
return {
|
|
188
|
+
input: bridge.input,
|
|
189
|
+
...(bridge.instructions !== undefined ? { instructions: bridge.instructions } : {}),
|
|
190
|
+
...(bridge.tools !== undefined ? { tools: bridge.tools } : {}),
|
|
191
|
+
};
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
@@ -82,6 +82,7 @@ import { preservesPhysicalComboProvider, resolveComboId } from "../combos";
|
|
|
82
82
|
import { captureRouteStaticPolicy, routedProviderConfig, routeModel, type RouteResult } from "../router";
|
|
83
83
|
import { POLICY_NAMESPACE, resolvePolicyProfileId } from "../routing/profile";
|
|
84
84
|
import type { AnthropicClientIdentity } from "../adapters/anthropic/client-identity";
|
|
85
|
+
import { logDroppedAnthropicBetas } from "../adapters/anthropic/beta-allowlist";
|
|
85
86
|
import type { OcxConfig, OcxProviderConfig, OcxUsage } from "../types";
|
|
86
87
|
import { resolveWireProtocolOverride } from "./adapter-resolve";
|
|
87
88
|
import {
|
|
@@ -115,6 +116,8 @@ import {
|
|
|
115
116
|
type RequestLogContext,
|
|
116
117
|
} from "./request-log";
|
|
117
118
|
import { fetchWithHeaderTimeout, providerFetch, safeHostLabel, sendWithConnectionPolicy } from "./responses/fetch-helpers";
|
|
119
|
+
import { anthropicBillingPhysicalFetch } from "../providers/anthropic-billing-ownership";
|
|
120
|
+
import { rotateAnthropicBillingKeyOnResponse } from "../providers/anthropic-billing-recovery";
|
|
118
121
|
import { linkAbortSignal } from "./responses/core-lifetime";
|
|
119
122
|
import { attachRequestSpendTracker } from "./responses/request-spend";
|
|
120
123
|
import { workflowRefusalResponse } from "./workflow-refusal";
|
|
@@ -441,6 +444,8 @@ export async function handleNativeMessages(options: HandleNativeMessagesOptions)
|
|
|
441
444
|
if (oauthBinding) logCtx.provider = formatAnthropicProviderForLog(route.providerName, oauthBinding.snapshot.accountId, config);
|
|
442
445
|
const spendTracker = attachRequestSpendTracker(req, logCtx);
|
|
443
446
|
const physicalBudget = createInferenceSendBudget(req, logCtx);
|
|
447
|
+
// Observe-only spend is already booked by the tracker; count billing sends without booking again.
|
|
448
|
+
let physicalSendsUsed = 0;
|
|
444
449
|
let activeRequest: AnthropicMessagesPassthroughRequest;
|
|
445
450
|
let retainedRequestBytes = 0;
|
|
446
451
|
const releaseRetainedRequest = () => {
|
|
@@ -465,7 +470,10 @@ export async function handleNativeMessages(options: HandleNativeMessagesOptions)
|
|
|
465
470
|
clientIdentity: options.clientIdentity,
|
|
466
471
|
});
|
|
467
472
|
if (built.strippedOpaqueState && rejectUnrepresentable) throw new NativeOpaqueStateRefusal();
|
|
468
|
-
if (built.droppedBetas)
|
|
473
|
+
if (built.droppedBetas) {
|
|
474
|
+
addProtocolEntryReason(logCtx, "anthropic-beta-dropped");
|
|
475
|
+
logDroppedAnthropicBetas({ droppedNames: built.droppedBetaNames, droppedUnrecognized: built.droppedUnrecognizedBetas });
|
|
476
|
+
}
|
|
469
477
|
if (built.strippedOpaqueState) addProtocolEntryReason(logCtx, "opaque-state-stripped");
|
|
470
478
|
return built;
|
|
471
479
|
};
|
|
@@ -589,8 +597,9 @@ export async function handleNativeMessages(options: HandleNativeMessagesOptions)
|
|
|
589
597
|
// exact incarnation across its await; a returned response can never reserve one.
|
|
590
598
|
sendOwner = snapshot ? captureAnthropicPhysicalSendOwnership(snapshot) : null;
|
|
591
599
|
if (snapshot && !sendOwner) throw new NativeOAuthSelectionChangedError();
|
|
600
|
+
physicalSendsUsed++;
|
|
592
601
|
dispatched = await sendWithConnectionPolicy(
|
|
593
|
-
(activeProvider as OcxProviderTransport).fetch ?? execute,
|
|
602
|
+
anthropicBillingPhysicalFetch((activeProvider as OcxProviderTransport).fetch ?? execute, route.providerName, activeProvider),
|
|
594
603
|
wire.url,
|
|
595
604
|
applyUpstreamRecoveryInit({ ...init, method: "POST", headers, body: wire.body }, transportRecovery),
|
|
596
605
|
{ providerName: route.providerName, provider: activeProvider },
|
|
@@ -638,49 +647,70 @@ export async function handleNativeMessages(options: HandleNativeMessagesOptions)
|
|
|
638
647
|
let response: Response;
|
|
639
648
|
try {
|
|
640
649
|
response = await send();
|
|
641
|
-
// A credential-scoped 401 on a static key pool says nothing about the sibling keys.
|
|
642
|
-
while (response.status === 401 && hasKeyPoolFailover(activeProvider) && transientSendAvailable()) {
|
|
643
|
-
const rotated = rotateProviderTransportOn401(config, route.providerName, activeProvider, {
|
|
644
|
-
now: Date.now(),
|
|
645
|
-
attemptedKey: activeProvider.apiKey,
|
|
646
|
-
});
|
|
647
|
-
if (!rotated) break;
|
|
648
|
-
discard(response);
|
|
649
|
-
rebuildFor(rotated);
|
|
650
|
-
response = await send("key-401");
|
|
651
|
-
}
|
|
652
650
|
const retryPolicy = oauthBinding ? undefined : rateLimitRetryPolicyFor(activeProvider);
|
|
653
651
|
let retries = 0;
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
&&
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
//
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
652
|
+
let billingRecoveryActive = false;
|
|
653
|
+
const keySendAvailable = () => transientSendAvailable()
|
|
654
|
+
&& (!billingRecoveryActive || Math.max(physicalSendsUsed, physicalBudget.used) < physicalBudget.policy.maxTotalModelSends);
|
|
655
|
+
for (;;) {
|
|
656
|
+
const priorResponse = response;
|
|
657
|
+
// A credential-scoped 401 on a static key pool says nothing about the sibling keys.
|
|
658
|
+
while (response.status === 401 && hasKeyPoolFailover(activeProvider) && keySendAvailable()) {
|
|
659
|
+
const rotated = rotateProviderTransportOn401(config, route.providerName, activeProvider, {
|
|
660
|
+
now: Date.now(),
|
|
661
|
+
attemptedKey: activeProvider.apiKey,
|
|
662
|
+
});
|
|
663
|
+
if (!rotated) break;
|
|
664
|
+
discard(response);
|
|
665
|
+
rebuildFor(rotated);
|
|
666
|
+
response = await send("key-401");
|
|
667
|
+
}
|
|
668
|
+
while ((response.status === 400 || response.status === 402) && hasKeyPoolFailover(activeProvider)) {
|
|
669
|
+
const rotated = await rotateAnthropicBillingKeyOnResponse(response, config, route.providerName, activeProvider, { signal: upstream.signal });
|
|
670
|
+
if (!rotated) break;
|
|
671
|
+
billingRecoveryActive = true;
|
|
672
|
+
if (!keySendAvailable()) break;
|
|
673
|
+
discard(response);
|
|
674
|
+
rebuildFor(rotated);
|
|
675
|
+
response = await send("key-401");
|
|
676
|
+
}
|
|
677
|
+
// A refusal this proxy synthesized for a reset replay is not a provider rate limit.
|
|
678
|
+
while (
|
|
679
|
+
response.status === 429
|
|
680
|
+
&& !isNonReplayableResponse(response)
|
|
681
|
+
&& retryPolicy
|
|
682
|
+
&& retries < retryPolicy.attempts
|
|
683
|
+
&& keySendAvailable()
|
|
684
|
+
) {
|
|
685
|
+
retries += 1;
|
|
686
|
+
for await (const _ of prepareSameTarget429Wait({
|
|
687
|
+
body: response.body,
|
|
688
|
+
signal: upstream.signal,
|
|
689
|
+
delayMs: rateLimitRetryDelayMs(retryPolicy, response.headers.get("retry-after"), Date.now()),
|
|
690
|
+
})) { /* pre-stream wait */ }
|
|
691
|
+
if (upstream.signal.aborted) throw upstream.signal.reason;
|
|
692
|
+
response = await send("rate-limit-429");
|
|
693
|
+
}
|
|
694
|
+
while (response.status === 429 && !isNonReplayableResponse(response) && hasKeyPoolFailover(activeProvider)) {
|
|
695
|
+
const rotated = rotateProviderTransportOn429(config, route.providerName, activeProvider, {
|
|
696
|
+
retryAfter: response.headers.get("retry-after"),
|
|
697
|
+
now: Date.now(),
|
|
698
|
+
attemptedKey: activeProvider.apiKey,
|
|
699
|
+
attemptedSelection: activeProvider._apiKeyAttempt,
|
|
700
|
+
});
|
|
701
|
+
if (!rotated) break;
|
|
702
|
+
// Rotation already recorded the cooldown; keep the terminal 429 when no send remains.
|
|
703
|
+
if (!keySendAvailable()) break;
|
|
704
|
+
discard(response);
|
|
705
|
+
rebuildFor(rotated);
|
|
706
|
+
response = await send("key-429");
|
|
707
|
+
}
|
|
708
|
+
// A sibling's next refusal may change class; the same counters survive that transition.
|
|
709
|
+
if (response !== priorResponse && (
|
|
710
|
+
(billingRecoveryActive && [401, 429].includes(response.status))
|
|
711
|
+
|| (activeProvider.adapter === "anthropic" && hasKeyPoolFailover(activeProvider) && [400, 402].includes(response.status))
|
|
712
|
+
)) continue;
|
|
713
|
+
break;
|
|
684
714
|
}
|
|
685
715
|
const oauthRetryKey = {};
|
|
686
716
|
const triedAccountIds = new Set<string>();
|
|
@@ -480,6 +480,8 @@ export function relaySseEagerBounded(
|
|
|
480
480
|
}
|
|
481
481
|
}
|
|
482
482
|
} finally {
|
|
483
|
+
upstream.signal.removeEventListener("abort", wakeUp);
|
|
484
|
+
upstream.signal.removeEventListener("abort", wakeParkedRead);
|
|
483
485
|
// Release any retained rewrite-buffer bytes on every teardown path
|
|
484
486
|
// (error, cancel, upstream abort) — consumption/EOF release alone
|
|
485
487
|
// leaves them charged.
|
|
@@ -52,7 +52,7 @@ export function queryRequestLogs(
|
|
|
52
52
|
for (const entry of logs) {
|
|
53
53
|
if (provider && entry.provider !== provider && !entry.attempts?.some(attempt => attempt.provider === provider)) continue;
|
|
54
54
|
if (conversationId && !matchesLogConversationId(entry.conversationId, conversationId)) continue;
|
|
55
|
-
if (model && entry
|
|
55
|
+
if (model && !matchesModel(entry, model) && !entry.attempts?.some(attempt => matchesModel(attempt, model))) continue;
|
|
56
56
|
if (account && entry.accountLogLabel !== account && !entry.attempts?.some(attempt => attempt.accountLogLabel === account)) continue;
|
|
57
57
|
if (protocolMode && !matchesProtocolMode(entry, protocolMode)) continue;
|
|
58
58
|
if (status && (statusClass === undefined ? String(entry.status) !== status : Math.floor(entry.status / 100) !== statusClass)) continue;
|
|
@@ -91,3 +91,8 @@ export function filterRequestLogs(logs: RequestLogEntry[], params: URLSearchPara
|
|
|
91
91
|
export function filteredRequestLogCount(logs: RequestLogEntry[], params: URLSearchParams): number {
|
|
92
92
|
return queryRequestLogs(logs, params).total;
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
/** A bare ID may itself contain slashes; compare against each observed destination as a whole. */
|
|
96
|
+
function matchesModel(row: { provider?: string; model?: string }, model: string): boolean {
|
|
97
|
+
return row.model === model || (row.provider !== undefined && row.model !== undefined && `${row.provider}/${row.model}` === model);
|
|
98
|
+
}
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
recognizedUpstreamError,
|
|
8
8
|
} from "../lib/errors";
|
|
9
9
|
import { redactSecretString } from "../lib/redact";
|
|
10
|
+
import { outboundCredentials, type OutboundCredentialRegistry } from "../lib/outbound-credential-registry";
|
|
11
|
+
import { configuredCredentials, type ConfiguredCredentials } from "./configured-credentials";
|
|
12
|
+
import { codexWsWrapperStatus } from "./responses/codex-ws-wire";
|
|
10
13
|
|
|
11
14
|
/** First bounded upstream diagnostics shared by request contexts and final rows. */
|
|
12
15
|
export interface UpstreamErrorDiagnostics {
|
|
@@ -24,6 +27,9 @@ export interface UpstreamErrorPayload {
|
|
|
24
27
|
type?: unknown;
|
|
25
28
|
code?: unknown;
|
|
26
29
|
message?: unknown;
|
|
30
|
+
/** Codex WebSocket wrapper status (`status_code` is its alias); read only on bare `error` events. */
|
|
31
|
+
status?: unknown;
|
|
32
|
+
status_code?: unknown;
|
|
27
33
|
error?: { type?: unknown; code?: unknown; message?: unknown };
|
|
28
34
|
last_error?: { type?: unknown; code?: unknown; message?: unknown };
|
|
29
35
|
response?: {
|
|
@@ -74,9 +80,29 @@ function noteBoundedDiagnostic(
|
|
|
74
80
|
if (logCtx[field] !== undefined) return;
|
|
75
81
|
if (!isSafeDiagnosticToken(value)) return;
|
|
76
82
|
if (field === "upstreamErrorType" && !KNOWN_UPSTREAM_ERROR_TYPES.has(value)) return;
|
|
83
|
+
if (!diagnosticValueAllowed(value)) return;
|
|
77
84
|
logCtx[field] = value;
|
|
78
85
|
const status = logCtx.terminalHttpStatus;
|
|
79
|
-
if (status === undefined || status >= 500)
|
|
86
|
+
if (status === undefined || status >= 500) {
|
|
87
|
+
const { upstreamErrorCode: code, upstreamRequestId: requestId } = upstreamDiagnosticLogFields(logCtx);
|
|
88
|
+
console.warn(`[opencodex] upstream failure${status ? ` status=${status}` : ""}${code ? ` code=${code}` : ""}${requestId ? ` request_id=${requestId}` : ""}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A diagnostic must not repeat a configured credential or a credential this process sent upstream
|
|
94
|
+
* (an echo). All three fields fail closed while either source cannot vouch for complete coverage:
|
|
95
|
+
* a closed type vocabulary does not prove a member is not also a credential. Applied at capture,
|
|
96
|
+
* again for every retained value interpolated into a warning, and at projection, because a
|
|
97
|
+
* credential can be configured or sent after the value was captured.
|
|
98
|
+
*/
|
|
99
|
+
export function diagnosticValueAllowed(
|
|
100
|
+
value: string,
|
|
101
|
+
registry: OutboundCredentialRegistry = outboundCredentials(),
|
|
102
|
+
configured: ConfiguredCredentials = configuredCredentials(),
|
|
103
|
+
): boolean {
|
|
104
|
+
if (!registry.complete() || !configured.complete) return false;
|
|
105
|
+
return !registry.matches(value) && !configured.matches(value);
|
|
80
106
|
}
|
|
81
107
|
|
|
82
108
|
export function noteUpstreamRequestId(logCtx: TerminalStatusContext, headers: Headers): void {
|
|
@@ -94,13 +120,37 @@ export function captureUpstreamTerminalDiagnostics(logCtx: TerminalStatusContext
|
|
|
94
120
|
}
|
|
95
121
|
|
|
96
122
|
export function upstreamDiagnosticLogFields(logCtx: UpstreamErrorDiagnostics): UpstreamErrorDiagnostics {
|
|
123
|
+
// One verification per projection: the three fields are checked at the same instant, so they
|
|
124
|
+
// share one snapshot of each source, taken only once a field is actually populated (a row with
|
|
125
|
+
// no upstream diagnostic reads no credential store).
|
|
126
|
+
let sources: { registry: OutboundCredentialRegistry; configured: ConfiguredCredentials } | undefined;
|
|
127
|
+
const allowed = (field: keyof UpstreamErrorDiagnostics): string | undefined => {
|
|
128
|
+
const value = logCtx[field];
|
|
129
|
+
if (!value) return undefined;
|
|
130
|
+
sources ??= { registry: outboundCredentials(), configured: configuredCredentials() };
|
|
131
|
+
return diagnosticValueAllowed(value, sources.registry, sources.configured) ? value : undefined;
|
|
132
|
+
};
|
|
133
|
+
const code = allowed("upstreamErrorCode");
|
|
134
|
+
const type = allowed("upstreamErrorType");
|
|
135
|
+
const requestId = allowed("upstreamRequestId");
|
|
97
136
|
return {
|
|
98
|
-
...(
|
|
99
|
-
...(
|
|
100
|
-
...(
|
|
137
|
+
...(code ? { upstreamErrorCode: code } : {}),
|
|
138
|
+
...(type ? { upstreamErrorType: type } : {}),
|
|
139
|
+
...(requestId ? { upstreamRequestId: requestId } : {}),
|
|
101
140
|
};
|
|
102
141
|
}
|
|
103
142
|
|
|
143
|
+
/**
|
|
144
|
+
* The server status a Codex WebSocket `error` frame states on its wrapper ({@link codexWsWrapperStatus}),
|
|
145
|
+
* or undefined. Only 5xx is returned. Every 5xx is the same transient class for account health, so
|
|
146
|
+
* this changes what a row says, not where the next turn goes; a wrapper 4xx would move quota and
|
|
147
|
+
* credential routing and is left to its own review (#6740).
|
|
148
|
+
*/
|
|
149
|
+
export function wrappedServerErrorStatus(json: UpstreamErrorPayload): number | undefined {
|
|
150
|
+
const status = codexWsWrapperStatus(json as Record<string, unknown>);
|
|
151
|
+
return status !== undefined && status >= 500 ? status : undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
104
154
|
function captureTerminalHttpStatus(
|
|
105
155
|
logCtx: TerminalStatusContext,
|
|
106
156
|
json: UpstreamErrorPayload,
|
|
@@ -170,8 +220,15 @@ function captureTerminalHttpStatus(
|
|
|
170
220
|
code: candidates.find(candidate => typeof candidate?.code === "string")?.code,
|
|
171
221
|
type: candidates.slice(0, -1).find(candidate => typeof candidate?.type === "string")?.type,
|
|
172
222
|
});
|
|
173
|
-
|
|
174
|
-
|
|
223
|
+
if (structured) {
|
|
224
|
+
noteStatus(structured.httpStatus);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
// An unrecognized class keeps only the status the upstream itself stated on the WebSocket
|
|
228
|
+
// wrapper. Nothing is invented here: with no wrapper status the row stays the synthetic 502, and
|
|
229
|
+
// combo preflight still reads its wider set of explicit event fields after this one.
|
|
230
|
+
const wrapped = wrappedServerErrorStatus(json);
|
|
231
|
+
if (wrapped !== undefined) noteStatus(wrapped);
|
|
175
232
|
return;
|
|
176
233
|
}
|
|
177
234
|
if (type !== "response.failed" || !responseError || typeof responseError !== "object") return;
|