@iislee/opencodex 2.11.0 → 2.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/README.md +117 -22
- package/bin/ocx.mjs +245 -88
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +21 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +475 -62
- package/src/adapters/base.ts +82 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +637 -0
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +308 -0
- package/src/adapters/cursor/cursor-errors.ts +144 -6
- package/src/adapters/cursor/discovery.ts +122 -14
- package/src/adapters/cursor/effort-map.ts +106 -4
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +180 -59
- package/src/adapters/cursor/live-transport.ts +623 -170
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -3
- package/src/adapters/cursor/native-exec.ts +121 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +447 -75
- package/src/adapters/cursor/request-builder.ts +271 -35
- package/src/adapters/cursor/tool-definitions.ts +252 -12
- package/src/adapters/cursor/tool-result-normalize.ts +115 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +48 -1
- package/src/adapters/cursor.ts +336 -39
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +713 -41
- package/src/adapters/google-antigravity-wire.ts +48 -10
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +742 -86
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +65 -17
- package/src/adapters/kiro.ts +270 -73
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1377 -344
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1164 -64
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +105 -21
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +186 -0
- package/src/bridge.ts +512 -92
- package/src/chat/inbound.ts +57 -20
- package/src/chat/outbound.ts +97 -33
- package/src/claude/agents-inject.ts +40 -10
- package/src/claude/context-windows.ts +37 -14
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +88 -21
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +150 -19
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +648 -28
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +97 -19
- package/src/cli/agent.ts +53 -2
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +60 -15
- package/src/cli/claude.ts +129 -35
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +821 -0
- package/src/cli/doctor.ts +440 -56
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +34 -274
- package/src/cli/index.ts +417 -528
- package/src/cli/init.ts +5 -17
- package/src/cli/inspect.ts +230 -0
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +130 -2
- package/src/cli/models.ts +118 -16
- package/src/cli/observe.ts +123 -11
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +34 -3
- package/src/cli/provider.ts +25 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +476 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +57 -10
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +51 -4
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +137 -18
- package/src/cli/version-skew.ts +46 -0
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1442 -23
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +35 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +84 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +570 -107
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +893 -246
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +348 -32
- package/src/codex/autostart-health.ts +24 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +86 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +138 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +555 -45
- package/src/codex/catalog/native-models.ts +75 -0
- package/src/codex/catalog/parsing.ts +269 -30
- package/src/codex/catalog/provider-fetch.ts +1626 -144
- package/src/codex/catalog/sync.ts +1576 -187
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +386 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +676 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +436 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +30 -24
- package/src/codex/history-provider.ts +1016 -235
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +223 -0
- package/src/codex/inject-coordination.ts +373 -0
- package/src/codex/inject.ts +1114 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +81 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +524 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +360 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +622 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +675 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +1313 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +264 -21
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +514 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1065 -31
- package/src/codex/subagent-model-fallback.ts +437 -43
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +720 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +47 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +91 -9
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +1713 -709
- package/src/generated/compatibility-version.json +3324 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +157 -33
- package/src/images/plan.ts +24 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +155 -0
- package/src/integrations/registry.ts +166 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +433 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +715 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +167 -11
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +26 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +80 -5
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +6 -2
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +253 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +30 -4
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +462 -71
- package/src/oauth/key-providers.ts +25 -0
- package/src/oauth/kimi.ts +25 -3
- package/src/oauth/kiro.ts +95 -6
- package/src/oauth/local-token-detect.ts +13 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +133 -26
- package/src/oauth/token-guardian.ts +9 -3
- package/src/oauth/types.ts +15 -0
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +176 -0
- package/src/providers/context-cap.ts +26 -8
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +281 -6
- package/src/providers/fastwire.ts +507 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +35 -2
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +30 -0
- package/src/providers/quota.ts +1379 -40
- package/src/providers/registry.ts +1545 -112
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +94 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +32 -4
- package/src/reasoning-effort.ts +68 -3
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +384 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +435 -0
- package/src/responses/parser.ts +225 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +504 -0
- package/src/responses/schema.ts +12 -2
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +659 -31
- package/src/responses/thought-signature-replay.ts +364 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +405 -31
- package/src/routing/analytics.ts +379 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +155 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +289 -48
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +154 -72
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +445 -0
- package/src/server/claude-messages.ts +160 -44
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +1021 -190
- package/src/server/lifecycle.ts +336 -14
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +433 -116
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +64 -25
- package/src/server/management/config-routes.ts +511 -51
- package/src/server/management/context.ts +89 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +127 -52
- package/src/server/management/model-routes.ts +461 -133
- package/src/server/management/model-rows.ts +167 -0
- package/src/server/management/native-integration-routes.ts +770 -0
- package/src/server/management/oauth-account-routes.ts +90 -8
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +836 -159
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +289 -33
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +126 -23
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +161 -10
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +221 -54
- package/src/server/relay.ts +479 -87
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +298 -31
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +239 -36
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +444 -72
- package/src/server/responses/core.ts +3579 -436
- package/src/server/responses/empty-completion-guard.ts +333 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +89 -99
- package/src/server/responses/input-admission.ts +187 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +343 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +381 -0
- package/src/server/responses-custom-tool-repair.ts +363 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +363 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +376 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +319 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +53 -40
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1153 -94
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +911 -0
- package/src/types/provider.ts +614 -0
- package/src/types/request.ts +397 -0
- package/src/types/tools.ts +236 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +107 -1237
- package/src/update/index.ts +37 -23
- package/src/update/job.ts +445 -68
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +266 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +287 -16
- package/src/usage/log.ts +666 -50
- package/src/usage/summary.ts +388 -17
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +27 -16
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +119 -0
- package/src/web-search/executor.ts +21 -6
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +284 -54
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -34,7 +34,8 @@ import { sweepExpiredAnthropicRoutingHealth } from "../oauth/anthropic-routing";
|
|
|
34
34
|
import { listLiveOAuthAccountKeys, reconcileOAuthReauthState } from "../oauth/store";
|
|
35
35
|
import { reconcileGuardianBackoff } from "../oauth/token-guardian";
|
|
36
36
|
import { sweepExpiredApiKeyCooldowns } from "../providers/key-failover";
|
|
37
|
-
import {
|
|
37
|
+
import { reconcileProviderRequestPacing } from "../providers/request-pacing";
|
|
38
|
+
import { sweepAbandonedResponseStateTemps, sweepExpiredResponseStates } from "../responses/state";
|
|
38
39
|
import { sweepExpiredAntigravityReplay } from "../adapters/google-antigravity-replay";
|
|
39
40
|
import { reconcileProviderAccountQuotaRows } from "../providers/quota";
|
|
40
41
|
import { reconcileRouterWarningMemos } from "../router";
|
|
@@ -75,6 +76,7 @@ export function buildGenerationContext(): GenerationContext {
|
|
|
75
76
|
export const STATE_STORE_REGISTRATIONS = [
|
|
76
77
|
{ name: "subagent-model-health", sweepExpired: sweepExpiredSubagentModelHealth },
|
|
77
78
|
{ name: "api-key-cooldowns", sweepExpired: sweepExpiredApiKeyCooldowns },
|
|
79
|
+
{ name: "provider-request-pacing", reconcileGeneration: reconcileProviderRequestPacing },
|
|
78
80
|
{
|
|
79
81
|
name: "combo-target-cooldowns",
|
|
80
82
|
sweepExpired: sweepExpiredComboTargetCooldowns,
|
|
@@ -82,7 +84,13 @@ export const STATE_STORE_REGISTRATIONS = [
|
|
|
82
84
|
},
|
|
83
85
|
{ name: "anthropic-routing-health", sweepExpired: sweepExpiredAnthropicRoutingHealth },
|
|
84
86
|
{ name: "xai-refresh-verdicts", sweepExpired: sweepExpiredXaiPermanentFailureVerdicts },
|
|
85
|
-
{
|
|
87
|
+
{
|
|
88
|
+
name: "responses-continuation",
|
|
89
|
+
sweepExpired: sweepExpiredResponseStates,
|
|
90
|
+
// Disk reclaim rides the liveness tick, not the TTL tick: sweepExpiredOnWrite puts
|
|
91
|
+
// sweepExpired on hot write paths, where a directory scan does not belong.
|
|
92
|
+
sweepLiveness: sweepAbandonedResponseStateTemps,
|
|
93
|
+
},
|
|
86
94
|
{ name: "antigravity-replay", sweepExpired: sweepExpiredAntigravityReplay },
|
|
87
95
|
{ name: "config-warning-memos", reconcileGeneration: (context: GenerationContext) => reconcileConfigWarningMemos(context.generation) },
|
|
88
96
|
{ name: "catalog-warning-memos", reconcileGeneration: (context: GenerationContext) => reconcileCatalogWarningMemos(context.generation) },
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { isLocalAttestationSecret } from "./local-management-attestation";
|
|
3
|
+
|
|
4
|
+
export const SYSTEM_RESTART_METHOD = "POST";
|
|
5
|
+
export const SYSTEM_RESTART_PATH = "/api/system/restart";
|
|
6
|
+
export const SYSTEM_RESTART_CAPABILITY_VERSION = "v1";
|
|
7
|
+
export const SYSTEM_RESTART_EXPECTED_PID_HEADER = "x-opencodex-restart-expected-pid";
|
|
8
|
+
export const SYSTEM_RESTART_NONCE_HEADER = "x-opencodex-restart-nonce";
|
|
9
|
+
export const SYSTEM_RESTART_CAPABILITY_HEADER = "x-opencodex-restart-capability";
|
|
10
|
+
|
|
11
|
+
/** Fixed drain and replacement budgets shared by the server, CLI, and tray. */
|
|
12
|
+
export const MEMORY_DRAIN_RESTART_MS = 60_000;
|
|
13
|
+
export const REPLACEMENT_READY_TIMEOUT_MS = 70_000;
|
|
14
|
+
|
|
15
|
+
const BASE64URL_256 = /^[A-Za-z0-9_-]{43}$/;
|
|
16
|
+
|
|
17
|
+
export type ExpectedSystemRestartPid =
|
|
18
|
+
| { kind: "absent" }
|
|
19
|
+
| { kind: "invalid" }
|
|
20
|
+
| { kind: "present"; pid: number };
|
|
21
|
+
|
|
22
|
+
export function parseExpectedSystemRestartPid(value: string | null): ExpectedSystemRestartPid {
|
|
23
|
+
if (value === null) return { kind: "absent" };
|
|
24
|
+
if (!/^[1-9]\d*$/.test(value)) return { kind: "invalid" };
|
|
25
|
+
const pid = Number(value);
|
|
26
|
+
return Number.isSafeInteger(pid) ? { kind: "present", pid } : { kind: "invalid" };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function restartCapabilityPayload(
|
|
30
|
+
nonce: string,
|
|
31
|
+
method: string,
|
|
32
|
+
path: string,
|
|
33
|
+
pid: number,
|
|
34
|
+
port: number,
|
|
35
|
+
): string | null {
|
|
36
|
+
if (!BASE64URL_256.test(nonce)) return null;
|
|
37
|
+
if (method !== SYSTEM_RESTART_METHOD || path !== SYSTEM_RESTART_PATH) return null;
|
|
38
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return null;
|
|
39
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;
|
|
40
|
+
return `opencodex-system-restart-v1\n${nonce}\n${method}\n${path}\n${pid}\n${port}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Process-scoped, operation-only authorization. It is not a reusable management credential. */
|
|
44
|
+
export function createSystemRestartCapability(
|
|
45
|
+
secret: string,
|
|
46
|
+
nonce: string,
|
|
47
|
+
method: string,
|
|
48
|
+
path: string,
|
|
49
|
+
pid: number,
|
|
50
|
+
port: number,
|
|
51
|
+
): string | null {
|
|
52
|
+
if (!isLocalAttestationSecret(secret)) return null;
|
|
53
|
+
const payload = restartCapabilityPayload(nonce, method, path, pid, port);
|
|
54
|
+
if (!payload) return null;
|
|
55
|
+
return createHmac("sha256", secret).update(payload).digest("base64url");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function verifySystemRestartCapability(
|
|
59
|
+
secret: string,
|
|
60
|
+
nonce: string | null,
|
|
61
|
+
method: string,
|
|
62
|
+
path: string,
|
|
63
|
+
pid: number,
|
|
64
|
+
port: number,
|
|
65
|
+
capability: string | null,
|
|
66
|
+
): boolean {
|
|
67
|
+
if (!nonce || !capability || !BASE64URL_256.test(capability)) return false;
|
|
68
|
+
const expected = createSystemRestartCapability(secret, nonce, method, path, pid, port);
|
|
69
|
+
if (!expected) return false;
|
|
70
|
+
const expectedBytes = Buffer.from(expected);
|
|
71
|
+
const actualBytes = Buffer.from(capability);
|
|
72
|
+
return expectedBytes.length === actualBytes.length && timingSafeEqual(expectedBytes, actualBytes);
|
|
73
|
+
}
|
|
@@ -55,15 +55,32 @@ function cjkRatio(text: string): number {
|
|
|
55
55
|
return sampled === 0 ? 0 : cjk / sampled;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Cap an estimated token count at a model's context window (codex-router PR #140).
|
|
60
|
+
*
|
|
61
|
+
* A request the provider answered cannot have exceeded the window, so the estimate is never
|
|
62
|
+
* allowed to claim it did: the estimate only ever substitutes a reported-zero (or missing)
|
|
63
|
+
* input count, and a value above the window would be provably false. Positive or missing
|
|
64
|
+
* reported counts are never rewritten here — this bounds the heuristic estimate itself.
|
|
65
|
+
* Non-positive or non-integer windows (unknown model) leave the estimate untouched.
|
|
66
|
+
*/
|
|
67
|
+
export function capEstimateAtContextWindow(estimate: number, contextWindow: number | undefined): number {
|
|
68
|
+
return typeof contextWindow === "number" && Number.isInteger(contextWindow) && contextWindow > 0
|
|
69
|
+
? Math.min(estimate, contextWindow)
|
|
70
|
+
: estimate;
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
/**
|
|
59
74
|
* Estimate the token count of a text blob. Pure and deterministic.
|
|
60
75
|
* Returns 0 for empty/whitespace-free-empty input; otherwise ceil(length / ratio), min 1.
|
|
76
|
+
* When `contextWindow` is a positive integer the estimate is capped at it: a request the
|
|
77
|
+
* provider answered cannot have exceeded the window, so the estimate must not claim it did.
|
|
61
78
|
*/
|
|
62
|
-
export function estimateTokens(text: string, modelId?: string): number {
|
|
79
|
+
export function estimateTokens(text: string, modelId?: string, contextWindow?: number): number {
|
|
63
80
|
if (!text) return 0;
|
|
64
81
|
const len = text.length;
|
|
65
82
|
if (len === 0) return 0;
|
|
66
83
|
let ratio = charsPerToken(modelId);
|
|
67
84
|
if (cjkRatio(text) > CJK_RATIO_THRESHOLD) ratio = Math.min(ratio, CJK_CHARS_PER_TOKEN);
|
|
68
|
-
return Math.max(1, Math.ceil(len / ratio));
|
|
85
|
+
return capEstimateAtContextWindow(Math.max(1, Math.ceil(len / ratio)), contextWindow);
|
|
69
86
|
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// Schema-aware repair for tool arguments whose serialized representation disagrees
|
|
2
|
+
// with the declared schema in a way that has exactly one faithful reading.
|
|
3
|
+
//
|
|
4
|
+
// Case 1 — integer fields arriving as integral floats (issue #1611).
|
|
5
|
+
//
|
|
6
|
+
// Grok serializes integer tool-call arguments through a float representation, so
|
|
7
|
+
// `yield_time_ms: 120000` leaves the provider as `120000.0`. Codex declares those
|
|
8
|
+
// fields as Rust integer types, so it rejects the call BEFORE running the tool:
|
|
9
|
+
//
|
|
10
|
+
// failed to parse function arguments: invalid type: floating point `120000.0`, expected u64
|
|
11
|
+
//
|
|
12
|
+
// That is a hard failure, not a degradation — the model gets no result and retries
|
|
13
|
+
// the same float. Nothing on the routed path reconciled argument values against the
|
|
14
|
+
// declared schema, so the `.0` passed straight through.
|
|
15
|
+
//
|
|
16
|
+
// The boundary this file draws is INTENT, not convenience:
|
|
17
|
+
// - an integral float in an integer-typed field is a representation artifact, and
|
|
18
|
+
// `120000.0` has exactly one integer reading, so it is repaired;
|
|
19
|
+
// - `1.5` in an integer field is a genuine disagreement with the schema and is left
|
|
20
|
+
// alone so it still fails, rather than being truncated into a plausible lie.
|
|
21
|
+
//
|
|
22
|
+
// Case 2 — string fields arriving as bare integers (issue #1938).
|
|
23
|
+
//
|
|
24
|
+
// Cursor-served models emit `{"cell_id": 4}` where the schema declares
|
|
25
|
+
// `{"type": "string"}`. Codex rejects the call before the tool runs (invalid type:
|
|
26
|
+
// integer `4`, expected a string), and the model never self-corrects, so every such
|
|
27
|
+
// call is a hard failure loop. A safely-integral JSON number in a string-declared
|
|
28
|
+
// field has exactly one faithful string reading (`4` -> `"4"`), so it is repaired
|
|
29
|
+
// under the same intent boundary:
|
|
30
|
+
// - only when the field declares `string` and no numeric type (a
|
|
31
|
+
// `["integer","string"]` union accepts the number as-is and is left alone);
|
|
32
|
+
// - a non-integral number (`4.5`) in a string field is a genuine disagreement and
|
|
33
|
+
// is left alone so it still fails;
|
|
34
|
+
// - beyond 2^53-1 the parsed value may differ from the serialized text, so the
|
|
35
|
+
// original bytes stay.
|
|
36
|
+
//
|
|
37
|
+
// Anything without a declared `integer`/`string` type is never touched.
|
|
38
|
+
|
|
39
|
+
/** JSON Schema subset we need; provider tool schemas are untrusted input. */
|
|
40
|
+
type SchemaNode = Record<string, unknown>;
|
|
41
|
+
|
|
42
|
+
function asSchema(value: unknown): SchemaNode | undefined {
|
|
43
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
44
|
+
? value as SchemaNode
|
|
45
|
+
: undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** True when the node declares `integer`, including `["integer","null"]` unions. */
|
|
49
|
+
function declaresInteger(schema: SchemaNode): boolean {
|
|
50
|
+
const type = schema.type;
|
|
51
|
+
if (type === "integer") return true;
|
|
52
|
+
return Array.isArray(type) && type.includes("integer");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** True when the node declares `string`, including `["string","null"]` unions. */
|
|
56
|
+
function declaresString(schema: SchemaNode): boolean {
|
|
57
|
+
const type = schema.type;
|
|
58
|
+
if (type === "string") return true;
|
|
59
|
+
return Array.isArray(type) && type.includes("string");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Issue #2316: Codex advertises `multi_agent_v1__wait_agent`'s `timeout_ms` as a JSON
|
|
63
|
+
// Schema `number`, but its Rust runtime deserializes the field as `u64` and rejects
|
|
64
|
+
// `120000.0` with "invalid type: floating point `120000.0`, expected u64" before the
|
|
65
|
+
// tool ever runs. The schema lookup is not the problem — the error text comes from
|
|
66
|
+
// Codex's own deserializer, so the call reached it — the problem is that `number`
|
|
67
|
+
// alone never authorizes the integral-float repair below.
|
|
68
|
+
//
|
|
69
|
+
// This allowlist is deliberately one field wide. It names only what has a live
|
|
70
|
+
// reproduction against a real `u64`, because the repair is only unambiguous for a
|
|
71
|
+
// field that cannot legitimately hold a fraction. Generic names (`start`, `end`,
|
|
72
|
+
// `priority`, `port`) would silently rewrite a third-party tool's fractional value,
|
|
73
|
+
// so a new entry needs its own evidence, not a plausible-sounding name.
|
|
74
|
+
//
|
|
75
|
+
// Cursor's sibling `yield_time_ms` (src/adapters/cursor/tool-definitions.ts) is also
|
|
76
|
+
// declared `number`; it is NOT included here because no rejection has been captured
|
|
77
|
+
// against it. It gets its own change when it gets its own reproduction.
|
|
78
|
+
const U64_NUMBER_FIELDS = new Set(["timeout_ms"]);
|
|
79
|
+
|
|
80
|
+
// Issue #2443 / #2451: Codex Desktop's bare `wait` tool has the same schema/runtime
|
|
81
|
+
// split for `yield_time_ms` and `max_tokens`. Scope these names to that bare tool so a
|
|
82
|
+
// third-party or namespaced tool can still use fractional values legitimately.
|
|
83
|
+
// #2448 allowlisted the hyphenated `yield-time_ms`; live Grok 4.6 calls and the
|
|
84
|
+
// advertised wait schema both use the underscore form, so that name is the one
|
|
85
|
+
// with a captured u64 rejection. Cursor still uses the same underscore name on a
|
|
86
|
+
// namespaced tool; the wait-only map keeps that path byte-identical.
|
|
87
|
+
const U64_NUMBER_FIELDS_BY_TOOL = new Map<string, ReadonlySet<string>>([
|
|
88
|
+
["wait", new Set(["yield_time_ms", "max_tokens"])],
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
/** True when the node accepts a JSON number (`integer` or `number`), so a numeric
|
|
92
|
+
* value is already schema-valid and must not be rewritten into a string. */
|
|
93
|
+
function declaresNumeric(schema: SchemaNode): boolean {
|
|
94
|
+
const type = schema.type;
|
|
95
|
+
if (type === "integer" || type === "number") return true;
|
|
96
|
+
return Array.isArray(type) && (type.includes("integer") || type.includes("number"));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Resolve a local `$ref` (`#/$defs/Foo`, `#/definitions/Foo`).
|
|
101
|
+
*
|
|
102
|
+
* Only same-document refs are followed: a remote ref is not fetchable here, and a
|
|
103
|
+
* schema we cannot resolve must leave its values untouched rather than guessed at.
|
|
104
|
+
*/
|
|
105
|
+
function resolveRef(schema: SchemaNode, root: SchemaNode, seen: Set<string>): SchemaNode | undefined {
|
|
106
|
+
const ref = schema.$ref;
|
|
107
|
+
if (typeof ref !== "string" || !ref.startsWith("#/")) return schema;
|
|
108
|
+
if (seen.has(ref)) return undefined; // cyclic $ref: stop rather than recurse forever
|
|
109
|
+
seen.add(ref);
|
|
110
|
+
let node: unknown = root;
|
|
111
|
+
for (const rawSegment of ref.slice(2).split("/")) {
|
|
112
|
+
const segment = rawSegment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
113
|
+
const current = asSchema(node);
|
|
114
|
+
if (!current) return undefined;
|
|
115
|
+
node = current[segment];
|
|
116
|
+
}
|
|
117
|
+
const resolved = asSchema(node);
|
|
118
|
+
return resolved ? resolveRef(resolved, root, seen) : undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Composition keywords whose branches may carry the integer declaration. */
|
|
122
|
+
const COMPOSITION_KEYS = ["anyOf", "oneOf", "allOf"] as const;
|
|
123
|
+
|
|
124
|
+
function compositionBranches(schema: SchemaNode): SchemaNode[] {
|
|
125
|
+
const branches: SchemaNode[] = [];
|
|
126
|
+
for (const key of COMPOSITION_KEYS) {
|
|
127
|
+
const value = schema[key];
|
|
128
|
+
if (!Array.isArray(value)) continue;
|
|
129
|
+
for (const branch of value) {
|
|
130
|
+
const node = asSchema(branch);
|
|
131
|
+
if (node) branches.push(node);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return branches;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* A JS number can only represent integers exactly up to 2^53-1. Beyond that a
|
|
139
|
+
* rewrite would emit a silently different value, so the original text stays.
|
|
140
|
+
*/
|
|
141
|
+
function safelyIntegral(value: number): boolean {
|
|
142
|
+
return Number.isInteger(value) && Math.abs(value) <= Number.MAX_SAFE_INTEGER;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
interface CoerceResult {
|
|
146
|
+
value: unknown;
|
|
147
|
+
changed: boolean;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function coerceValue(
|
|
151
|
+
value: unknown,
|
|
152
|
+
schema: SchemaNode | undefined,
|
|
153
|
+
root: SchemaNode,
|
|
154
|
+
depth: number,
|
|
155
|
+
toolName?: string,
|
|
156
|
+
propertyName?: string,
|
|
157
|
+
): CoerceResult {
|
|
158
|
+
// A hostile or deeply nested schema must not blow the stack.
|
|
159
|
+
if (depth > 64) return { value, changed: false };
|
|
160
|
+
const resolved = schema ? resolveRef(schema, root, new Set()) : undefined;
|
|
161
|
+
|
|
162
|
+
if (typeof value === "number") {
|
|
163
|
+
if (!resolved) return { value, changed: false };
|
|
164
|
+
const branches = compositionBranches(resolved);
|
|
165
|
+
// #2316: a known Codex-native u64 field counts as integer-declared even when the
|
|
166
|
+
// advertised schema says `number`, but only when the field really is numeric —
|
|
167
|
+
// an allowlisted name over a string-typed field is a disagreement, not a repair.
|
|
168
|
+
const nativeU64Field = propertyName !== undefined
|
|
169
|
+
&& (
|
|
170
|
+
U64_NUMBER_FIELDS.has(propertyName)
|
|
171
|
+
|| U64_NUMBER_FIELDS_BY_TOOL.get(toolName ?? "")?.has(propertyName) === true
|
|
172
|
+
);
|
|
173
|
+
const nativeU64Declared = nativeU64Field
|
|
174
|
+
&& (declaresNumeric(resolved) || branches.some(declaresNumeric));
|
|
175
|
+
const integerDeclared = declaresInteger(resolved)
|
|
176
|
+
|| branches.some(declaresInteger)
|
|
177
|
+
|| nativeU64Declared;
|
|
178
|
+
if (!integerDeclared && safelyIntegral(value)) {
|
|
179
|
+
// Issue #1938: a bare integer in a string-only field has exactly one faithful
|
|
180
|
+
// string reading. A field that also accepts a numeric type keeps the number.
|
|
181
|
+
const stringDeclared = declaresString(resolved) || branches.some(declaresString);
|
|
182
|
+
const numericDeclared = declaresNumeric(resolved) || branches.some(declaresNumeric);
|
|
183
|
+
if (stringDeclared && !numericDeclared) {
|
|
184
|
+
return { value: String(value), changed: true };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Not an integer field, already an integer, non-integral, or unrepresentable:
|
|
188
|
+
// in every one of those cases the received value is the right thing to keep.
|
|
189
|
+
if (!integerDeclared || !safelyIntegral(value)) return { value, changed: false };
|
|
190
|
+
// `120000.0` and `120000` are the same JS number; the difference is only in the
|
|
191
|
+
// serialized text, which is repaired by re-stringifying the parsed value.
|
|
192
|
+
return { value, changed: true };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (Array.isArray(value)) {
|
|
196
|
+
const itemSchema = resolved ? asSchema(resolved.items) : undefined;
|
|
197
|
+
let changed = false;
|
|
198
|
+
const next = value.map(entry => {
|
|
199
|
+
// Array items have no property name of their own; passing the array's key would
|
|
200
|
+
// let `timeout_ms: [1.5]` inherit the allowlist. Items are judged by schema only.
|
|
201
|
+
const result = coerceValue(entry, itemSchema, root, depth + 1, toolName);
|
|
202
|
+
if (result.changed) changed = true;
|
|
203
|
+
return result.value;
|
|
204
|
+
});
|
|
205
|
+
return changed ? { value: next, changed } : { value, changed: false };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const object = asSchema(value);
|
|
209
|
+
if (!object) return { value, changed: false };
|
|
210
|
+
|
|
211
|
+
const properties = resolved ? asSchema(resolved.properties) : undefined;
|
|
212
|
+
const additional = resolved ? asSchema(resolved.additionalProperties) : undefined;
|
|
213
|
+
let changed = false;
|
|
214
|
+
const next: Record<string, unknown> = {};
|
|
215
|
+
for (const [key, entry] of Object.entries(object)) {
|
|
216
|
+
const childSchema = asSchema(properties?.[key]) ?? additional;
|
|
217
|
+
const result = coerceValue(entry, childSchema, root, depth + 1, toolName, key);
|
|
218
|
+
if (result.changed) changed = true;
|
|
219
|
+
next[key] = result.value;
|
|
220
|
+
}
|
|
221
|
+
return changed ? { value: next, changed } : { value, changed: false };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Repair integral floats in a tool-call arguments STRING against the tool's declared
|
|
226
|
+
* parameter schema.
|
|
227
|
+
*
|
|
228
|
+
* Returns the original string unchanged when there is no schema, the payload does not
|
|
229
|
+
* parse, or nothing needed repair — so an unaffected call keeps its exact bytes and
|
|
230
|
+
* this stays a no-op for every provider that already emits real integers.
|
|
231
|
+
*/
|
|
232
|
+
export function coerceIntegerToolArguments(
|
|
233
|
+
args: string,
|
|
234
|
+
parameters: Record<string, unknown> | undefined,
|
|
235
|
+
toolName?: string,
|
|
236
|
+
): string {
|
|
237
|
+
if (!parameters || !args) return args;
|
|
238
|
+
// Cheap reject: a payload with no digit cannot need either repair (integral-float
|
|
239
|
+
// -> integer, or bare-integer -> string).
|
|
240
|
+
if (!/\d/.test(args)) return args;
|
|
241
|
+
let parsed: unknown;
|
|
242
|
+
try {
|
|
243
|
+
parsed = JSON.parse(args);
|
|
244
|
+
} catch {
|
|
245
|
+
// Malformed or still-streaming arguments are not this function's problem; the
|
|
246
|
+
// existing paths already handle them.
|
|
247
|
+
return args;
|
|
248
|
+
}
|
|
249
|
+
const root = parameters as SchemaNode;
|
|
250
|
+
const result = coerceValue(parsed, root, root, 0, toolName);
|
|
251
|
+
if (!result.changed) return args;
|
|
252
|
+
return JSON.stringify(result.value);
|
|
253
|
+
}
|
|
@@ -71,6 +71,40 @@ export interface TranslatorBudget {
|
|
|
71
71
|
|
|
72
72
|
const retainedEventOwnership = new WeakMap<object, { budget: TranslatorBudget; bytes: number }>();
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Charge one event appended to an incrementally materialized adapter-event batch.
|
|
76
|
+
* The newest event owns the closing array bracket; moving that byte from the old
|
|
77
|
+
* tail keeps in-order release accounting equal to the still-retained JSON array.
|
|
78
|
+
*/
|
|
79
|
+
export function retainTranslatedEvent<T extends object>(
|
|
80
|
+
event: T,
|
|
81
|
+
budget: TranslatorBudget,
|
|
82
|
+
previousTail?: object,
|
|
83
|
+
): void {
|
|
84
|
+
if (retainedEventOwnership.has(event)) {
|
|
85
|
+
throw new Error("translated event is already retained");
|
|
86
|
+
}
|
|
87
|
+
if (previousTail === event) {
|
|
88
|
+
throw new Error("incremental translated event tail must be a distinct object");
|
|
89
|
+
}
|
|
90
|
+
const previousOwnership = previousTail === undefined
|
|
91
|
+
? undefined
|
|
92
|
+
: retainedEventOwnership.get(previousTail);
|
|
93
|
+
if (
|
|
94
|
+
previousTail !== undefined
|
|
95
|
+
&& (!previousOwnership || previousOwnership.budget !== budget || previousOwnership.bytes < 2)
|
|
96
|
+
) {
|
|
97
|
+
throw new Error("incremental translated event tail is not retained by this budget");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const serializedBytes = Buffer.byteLength(JSON.stringify(event));
|
|
101
|
+
budget.chargeRetained(serializedBytes + (previousTail === undefined ? 2 : 1), {
|
|
102
|
+
kind: "retained_collectors",
|
|
103
|
+
});
|
|
104
|
+
if (previousOwnership) previousOwnership.bytes -= 1;
|
|
105
|
+
retainedEventOwnership.set(event, { budget, bytes: serializedBytes + 2 });
|
|
106
|
+
}
|
|
107
|
+
|
|
74
108
|
/**
|
|
75
109
|
* Charge a materialized adapter-event batch and attach its lease to the events themselves.
|
|
76
110
|
* A copied event array (for example terminal-guard collection) preserves the event objects, so
|
|
@@ -315,6 +349,16 @@ export function translatorObservedOverflowCount(): number {
|
|
|
315
349
|
return aggregateOverflows;
|
|
316
350
|
}
|
|
317
351
|
|
|
352
|
+
/** Test-only: proves owned default budgets are disposed on every stream-death path. */
|
|
353
|
+
export function translatorLiveBudgetCountForTests(): number {
|
|
354
|
+
return liveBudgets.size;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** Test-only: proves no charge survives against a disposed budget (cancel race). */
|
|
358
|
+
export function translatorAggregateCurrentBytesForTests(): number {
|
|
359
|
+
return aggregateCurrentBytes;
|
|
360
|
+
}
|
|
361
|
+
|
|
318
362
|
/** Clears process-wide translator diagnostics and disposes leaked test budgets. */
|
|
319
363
|
export function resetTranslatorAggregateForTests(): void {
|
|
320
364
|
for (const budget of liveBudgets) budget.dispose();
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { OcxProviderConfig, UpstreamHttpVersion } from "../types";
|
|
2
|
+
|
|
3
|
+
/** True when a provider explicitly selected the HTTP/1.1 compatibility path. */
|
|
4
|
+
export function isPinnedHttp1(version: UpstreamHttpVersion | undefined): boolean {
|
|
5
|
+
return version === "http1.1" || version === "h1";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Bun's fetch accepts a non-standard `protocol` init to pin the HTTP version
|
|
10
|
+
* (BunFetchRequestInit.protocol). The DOM lib types do not include it, so the
|
|
11
|
+
* value is represented through a RequestInit cast at this shared boundary.
|
|
12
|
+
*/
|
|
13
|
+
const UPSTREAM_HTTP_VERSION_PROTOCOL: Record<Exclude<UpstreamHttpVersion, "auto">, string> = {
|
|
14
|
+
"http1.1": "http1.1",
|
|
15
|
+
h1: "h1",
|
|
16
|
+
http2: "http2",
|
|
17
|
+
h2: "h2",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Raised before I/O when a fixed Bun HTTP-version pin cannot apply to the target URL. */
|
|
21
|
+
export class UpstreamHttpVersionTargetError extends Error {
|
|
22
|
+
readonly code = "upstream_http_version_target";
|
|
23
|
+
|
|
24
|
+
constructor(readonly observedProtocol: string) {
|
|
25
|
+
super(`upstream HTTP version pin requires an HTTPS target (received ${observedProtocol})`);
|
|
26
|
+
this.name = "UpstreamHttpVersionTargetError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Attach Bun's `protocol` pin for one explicit version value. */
|
|
31
|
+
export function withUpstreamHttpVersionValue(
|
|
32
|
+
input: Parameters<typeof globalThis.fetch>[0],
|
|
33
|
+
init: RequestInit | undefined,
|
|
34
|
+
version: UpstreamHttpVersion | undefined,
|
|
35
|
+
): RequestInit | undefined {
|
|
36
|
+
if (!version || version === "auto") return init;
|
|
37
|
+
// Bun's protocol pin requires an https: target. An explicit fixed-version request must fail
|
|
38
|
+
// locally when the pin cannot be honored instead of silently negotiating another transport.
|
|
39
|
+
const target = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
40
|
+
let targetUrl: URL;
|
|
41
|
+
try {
|
|
42
|
+
targetUrl = new URL(target);
|
|
43
|
+
} catch {
|
|
44
|
+
throw new UpstreamHttpVersionTargetError("unparseable");
|
|
45
|
+
}
|
|
46
|
+
if (targetUrl.protocol !== "https:") throw new UpstreamHttpVersionTargetError(targetUrl.protocol);
|
|
47
|
+
return { ...(init ?? {}), protocol: UPSTREAM_HTTP_VERSION_PROTOCOL[version] } as RequestInit;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Attach Bun's `protocol` pin when the provider opted into a fixed HTTP version. */
|
|
51
|
+
export function withUpstreamHttpVersion(
|
|
52
|
+
input: Parameters<typeof globalThis.fetch>[0],
|
|
53
|
+
init: RequestInit | undefined,
|
|
54
|
+
provider: Pick<OcxProviderConfig, "upstreamHttpVersion">,
|
|
55
|
+
): RequestInit | undefined {
|
|
56
|
+
return withUpstreamHttpVersionValue(input, init, provider.upstreamHttpVersion);
|
|
57
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classification of upstream fetch rejections that occur before any request
|
|
3
|
+
* bytes can reach the origin: DNS resolution failure and TCP connect refusal.
|
|
4
|
+
*
|
|
5
|
+
* Issue #914: these failures are machine/network-wide, not account-specific —
|
|
6
|
+
* every Codex pool account shares the provider host, so rotating accounts
|
|
7
|
+
* cannot repair them. The transport layer previously mapped every non-timeout
|
|
8
|
+
* rejection to `connect_error`, and at `upstreamFailoverThreshold` that streak
|
|
9
|
+
* soft-avoided a healthy account and cleared thread affinity.
|
|
10
|
+
*
|
|
11
|
+
* Bun collapses DNS failure and TCP refusal into one label class
|
|
12
|
+
* (`ConnectionRefused` / `FailedToOpenSocket`, errno 0); Node's undici emits
|
|
13
|
+
* the classic `ECONNREFUSED` / `ENOTFOUND` / `EAI_AGAIN` / `ENETUNREACH` /
|
|
14
|
+
* `ENETDOWN` / `EHOSTUNREACH` shapes. Both shapes are matched here, on `code`
|
|
15
|
+
* values only, through a bounded cause chain. Message substrings are never
|
|
16
|
+
* trusted (a message-only match is a negative case), and labels that can also
|
|
17
|
+
* appear after the origin saw the credential — `ECONNRESET`, `EPIPE`, TLS
|
|
18
|
+
* errors, unknown shapes — deliberately stay outside the set.
|
|
19
|
+
*
|
|
20
|
+
* Classifier semantics extracted from PR #966 (Yuxin-Qiao) with attribution;
|
|
21
|
+
* sidecar blast radius intentionally not inherited.
|
|
22
|
+
*
|
|
23
|
+
* MUST stay a leaf module: imports nothing from server.ts or adapters.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { RequestPacingQueueOverloadError } from "../providers/request-pacing";
|
|
27
|
+
import { UpstreamRetryEvidenceError } from "./upstream-retry";
|
|
28
|
+
|
|
29
|
+
export const PRE_CONNECT_REACHABILITY_CODES = new Set([
|
|
30
|
+
// Bun: DNS failure and TCP refusal share this class.
|
|
31
|
+
"ConnectionRefused",
|
|
32
|
+
"FailedToOpenSocket",
|
|
33
|
+
// Node undici / classic Node shapes.
|
|
34
|
+
"ECONNREFUSED",
|
|
35
|
+
"ENOTFOUND",
|
|
36
|
+
"EAI_AGAIN",
|
|
37
|
+
"ENETUNREACH",
|
|
38
|
+
"ENETDOWN",
|
|
39
|
+
"EHOSTUNREACH",
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
/** Upper bound on how far `cause` chains are inspected. */
|
|
43
|
+
export const MAX_REACHABILITY_CAUSE_DEPTH = 3;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* True when the rejection (or a bounded `cause` of it) carries a proven
|
|
47
|
+
* pre-connection reachability code. Never matches message text.
|
|
48
|
+
*/
|
|
49
|
+
export function isPreConnectReachabilityError(err: unknown): boolean {
|
|
50
|
+
let current: unknown = err;
|
|
51
|
+
const seen = new Set<unknown>();
|
|
52
|
+
for (let depth = 0; depth < MAX_REACHABILITY_CAUSE_DEPTH; depth++) {
|
|
53
|
+
if (!(current instanceof Error) || seen.has(current)) return false;
|
|
54
|
+
seen.add(current);
|
|
55
|
+
const code = (current as { code?: unknown }).code;
|
|
56
|
+
if (typeof code === "string" && PRE_CONNECT_REACHABILITY_CODES.has(code)) return true;
|
|
57
|
+
current = current.cause;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type TransportFailureKind = "timeout" | "connect_neutral" | "connect_error";
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Shared transport rejection classification for Codex pool upstream sends.
|
|
66
|
+
* Timeouts keep their existing identity (account-transient); proven pre-connect
|
|
67
|
+
* reachability failures become account-neutral; everything else (ECONNRESET,
|
|
68
|
+
* EPIPE, TLS, unknown shapes) keeps the existing `connect_error`
|
|
69
|
+
* account-attributed behavior.
|
|
70
|
+
*/
|
|
71
|
+
export function classifyTransportFailureKind(err: unknown): TransportFailureKind {
|
|
72
|
+
// A local pacing admission failure happened before transport classification and must
|
|
73
|
+
// remain a client-visible overload, not account or host health evidence.
|
|
74
|
+
if (err instanceof RequestPacingQueueOverloadError) throw err;
|
|
75
|
+
const evidence = err instanceof UpstreamRetryEvidenceError ? err : undefined;
|
|
76
|
+
const rejection = evidence ? evidence.cause : err;
|
|
77
|
+
if (rejection instanceof Error && rejection.name === "TimeoutError") return "timeout";
|
|
78
|
+
if (isPreConnectReachabilityError(rejection)) {
|
|
79
|
+
// A transient upstream response (5xx) or a credential-visible connection
|
|
80
|
+
// reset before the rejection proves the host and credential path were
|
|
81
|
+
// reached: the failure is account-attributable, never the pre-connection
|
|
82
|
+
// neutral class (issue #914 review).
|
|
83
|
+
if (evidence && (evidence.transientStatuses.length > 0 || evidence.resetSeen)) return "connect_error";
|
|
84
|
+
return "connect_neutral";
|
|
85
|
+
}
|
|
86
|
+
return "connect_error";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Stable `code` carried by a transport rejection, when there is one. */
|
|
90
|
+
export function transportErrorCode(err: unknown): string | undefined {
|
|
91
|
+
const rejection = err instanceof UpstreamRetryEvidenceError ? err.cause : err;
|
|
92
|
+
if (!(rejection instanceof Error)) return undefined;
|
|
93
|
+
const code = (rejection as { code?: unknown }).code;
|
|
94
|
+
return typeof code === "string" && code !== "" ? code : undefined;
|
|
95
|
+
}
|