@iislee/opencodex 2.11.0 → 2.31.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 +114 -19
- package/bin/ocx.mjs +164 -36
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-BF38heuV.js +104 -0
- package/gui/dist/assets/index-DMiI18Kv.css +1 -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 +19 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +376 -52
- package/src/adapters/base.ts +54 -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 +601 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +129 -6
- package/src/adapters/cursor/discovery.ts +61 -4
- package/src/adapters/cursor/effort-map.ts +27 -3
- 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 +174 -58
- package/src/adapters/cursor/live-transport.ts +609 -170
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- 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 +0 -1
- package/src/adapters/cursor/native-exec.ts +101 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +383 -65
- package/src/adapters/cursor/request-builder.ts +237 -31
- package/src/adapters/cursor/tool-definitions.ts +242 -12
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +28 -1
- package/src/adapters/cursor.ts +133 -7
- package/src/adapters/google-antigravity-replay.ts +558 -34
- package/src/adapters/google-antigravity-wire.ts +43 -10
- 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 +618 -74
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +10 -1
- package/src/adapters/kiro.ts +92 -54
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1311 -336
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +830 -56
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +104 -21
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +428 -61
- package/src/chat/inbound.ts +43 -19
- package/src/chat/outbound.ts +82 -26
- package/src/claude/agents-inject.ts +32 -9
- package/src/claude/context-windows.ts +21 -5
- 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 +70 -16
- package/src/cli/account-api.ts +35 -1
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +389 -2
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +30 -6
- package/src/cli/agent.ts +45 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +29 -4
- package/src/cli/claude.ts +103 -34
- 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 +593 -0
- package/src/cli/doctor.ts +315 -43
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +20 -271
- package/src/cli/index.ts +378 -519
- package/src/cli/init.ts +4 -17
- 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 +35 -2
- package/src/cli/models.ts +100 -14
- package/src/cli/observe.ts +92 -3
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/provider.ts +24 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +437 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +10 -3
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/v2.ts +105 -10
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1358 -21
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-priority.ts +83 -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 +493 -106
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +849 -242
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +345 -32
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +68 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +121 -30
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +529 -45
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +224 -30
- package/src/codex/catalog/provider-fetch.ts +1460 -134
- package/src/codex/catalog/sync.ts +1449 -186
- 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 +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -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/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +26 -20
- package/src/codex/history-provider.ts +231 -28
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1073 -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 +80 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +506 -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 +353 -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 +492 -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 +682 -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 +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +175 -13
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +505 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1009 -28
- package/src/codex/subagent-model-fallback.ts +350 -35
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +612 -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 +20 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +81 -9
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +1762 -140
- package/src/generated/compatibility-version.json +3164 -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 +152 -29
- package/src/images/plan.ts +23 -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.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -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/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +153 -9
- 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 +6 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +44 -2
- 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/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +4 -1
- 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/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 +202 -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 +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +398 -66
- package/src/oauth/key-providers.ts +16 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/local-token-detect.ts +11 -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/store.ts +119 -21
- package/src/oauth/token-guardian.ts +9 -3
- 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/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +22 -5
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +253 -6
- package/src/providers/fastwire.ts +501 -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 +1 -1
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -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 +29 -0
- package/src/providers/quota.ts +1319 -38
- package/src/providers/registry.ts +1429 -111
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +42 -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 +11 -4
- package/src/reasoning-effort.ts +49 -1
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +220 -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 +426 -0
- package/src/responses/schema.ts +7 -1
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +565 -27
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +366 -30
- package/src/routing/analytics.ts +378 -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 +145 -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 +267 -46
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +130 -56
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +159 -43
- 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 +865 -181
- package/src/server/lifecycle.ts +284 -13
- 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 +398 -103
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/combo-routes.ts +62 -24
- package/src/server/management/config-routes.ts +464 -51
- package/src/server/management/context.ts +80 -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 +101 -32
- package/src/server/management/model-routes.ts +189 -131
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +80 -4
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +764 -157
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +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 +288 -32
- 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 +115 -14
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +132 -5
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +236 -76
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log.ts +235 -22
- 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/collaboration.ts +204 -35
- package/src/server/responses/compact.ts +442 -55
- package/src/server/responses/core.ts +2872 -331
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +79 -4
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -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 +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -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 +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +292 -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 +39 -38
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1111 -90
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- 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 +845 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +104 -1236
- package/src/update/index.ts +32 -19
- package/src/update/job.ts +442 -67
- 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 +259 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +268 -16
- package/src/usage/log.ts +606 -41
- package/src/usage/summary.ts +177 -9
- 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 +9 -3
- 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 +88 -0
- package/src/web-search/executor.ts +11 -3
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +279 -50
- 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
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import type { OcxProviderConfig, RequestPacingRule } from "../types";
|
|
2
|
+
import type { GenerationContext } from "../lib/state-store-sweeper";
|
|
3
|
+
|
|
4
|
+
export const REQUEST_PACING_MAX_QUEUE_DEPTH = 256;
|
|
5
|
+
export const REQUEST_PACING_MAX_QUEUE_AGE_MS = 60_000;
|
|
6
|
+
|
|
7
|
+
let maxQueueDepth = REQUEST_PACING_MAX_QUEUE_DEPTH;
|
|
8
|
+
let maxQueueAgeMs = REQUEST_PACING_MAX_QUEUE_AGE_MS;
|
|
9
|
+
|
|
10
|
+
export type RequestPacingQueueOverloadReason = "queue_full" | "queue_expired";
|
|
11
|
+
|
|
12
|
+
export class RequestPacingQueueOverloadError extends Error {
|
|
13
|
+
constructor(
|
|
14
|
+
public readonly providerName: string,
|
|
15
|
+
public readonly reason: RequestPacingQueueOverloadReason,
|
|
16
|
+
public readonly retryAfterSeconds: number,
|
|
17
|
+
) {
|
|
18
|
+
super(reason === "queue_full"
|
|
19
|
+
? `request pacing queue for provider '${providerName}' is full`
|
|
20
|
+
: `request pacing queue for provider '${providerName}' exceeded the maximum queued age`);
|
|
21
|
+
this.name = "RequestPacingQueueOverloadError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class RequestPacingProviderRemovedError extends Error {
|
|
26
|
+
constructor(public readonly providerName: string) {
|
|
27
|
+
super(`request pacing provider '${providerName}' was removed`);
|
|
28
|
+
this.name = "RequestPacingProviderRemovedError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface Waiter {
|
|
33
|
+
modelId?: string;
|
|
34
|
+
providerIntervalMs: number;
|
|
35
|
+
modelIntervalMs: number;
|
|
36
|
+
queuedAt: number;
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
resolve: () => void;
|
|
39
|
+
reject: (reason: unknown) => void;
|
|
40
|
+
abort?: () => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface ProviderPacer {
|
|
44
|
+
queue: Waiter[];
|
|
45
|
+
providerNextStartAt: number;
|
|
46
|
+
modelNextStartAt: Map<string, number>;
|
|
47
|
+
timer?: unknown;
|
|
48
|
+
lastStartedAt?: number;
|
|
49
|
+
lastModelId?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface RequestPacingRuntime {
|
|
53
|
+
now: () => number;
|
|
54
|
+
setTimer: (callback: () => void, delayMs: number) => unknown;
|
|
55
|
+
clearTimer: (handle: unknown) => void;
|
|
56
|
+
enqueueMicrotask: (callback: () => void) => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ProviderRequestPacingStatus {
|
|
60
|
+
provider: string;
|
|
61
|
+
enabled: boolean;
|
|
62
|
+
queued: number;
|
|
63
|
+
nextSlotInMs: number;
|
|
64
|
+
lastStartedAt?: number;
|
|
65
|
+
lastModelId?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const pacers = new Map<string, ProviderPacer>();
|
|
69
|
+
const defaultRuntime: RequestPacingRuntime = {
|
|
70
|
+
now: Date.now,
|
|
71
|
+
setTimer: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
72
|
+
clearTimer: handle => clearTimeout(handle as ReturnType<typeof setTimeout>),
|
|
73
|
+
enqueueMicrotask: queueMicrotask,
|
|
74
|
+
};
|
|
75
|
+
let runtime = defaultRuntime;
|
|
76
|
+
let lastReconciledGeneration = 0;
|
|
77
|
+
|
|
78
|
+
function abortReason(signal: AbortSignal): unknown {
|
|
79
|
+
return signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizedInterval(rule: RequestPacingRule | undefined): number {
|
|
83
|
+
if (!rule) return 0;
|
|
84
|
+
const rpmInterval = typeof rule.requestsPerMinute === "number" && rule.requestsPerMinute > 0
|
|
85
|
+
? 60_000 / rule.requestsPerMinute
|
|
86
|
+
: 0;
|
|
87
|
+
const fixedInterval = typeof rule.minIntervalMs === "number" && rule.minIntervalMs > 0
|
|
88
|
+
? rule.minIntervalMs
|
|
89
|
+
: 0;
|
|
90
|
+
return Math.max(rpmInterval, fixedInterval);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function requestPacingIntervalMs(provider: OcxProviderConfig, modelId?: string): number {
|
|
94
|
+
const policy = provider.requestPacing;
|
|
95
|
+
if (!policy?.enabled) return 0;
|
|
96
|
+
const override = modelId ? policy.models?.[modelId] : undefined;
|
|
97
|
+
return Math.max(normalizedInterval(policy), normalizedInterval(override));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function requestPacingIntervals(provider: OcxProviderConfig, modelId?: string): {
|
|
101
|
+
providerIntervalMs: number;
|
|
102
|
+
modelIntervalMs: number;
|
|
103
|
+
} {
|
|
104
|
+
const policy = provider.requestPacing;
|
|
105
|
+
if (!policy?.enabled) return { providerIntervalMs: 0, modelIntervalMs: 0 };
|
|
106
|
+
return {
|
|
107
|
+
providerIntervalMs: normalizedInterval(policy),
|
|
108
|
+
modelIntervalMs: modelId ? normalizedInterval(policy.models?.[modelId]) : 0,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function waiterReadyAt(state: ProviderPacer, modelId: string | undefined): number {
|
|
113
|
+
return Math.max(
|
|
114
|
+
state.providerNextStartAt,
|
|
115
|
+
modelId ? (state.modelNextStartAt.get(modelId) ?? 0) : 0,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function pacingRetryAfterSeconds(state: ProviderPacer, modelId: string | undefined, now: number): number {
|
|
120
|
+
return Math.max(1, Math.ceil(Math.max(0, waiterReadyAt(state, modelId) - now) / 1000));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function rejectExpiredWaiters(providerName: string, state: ProviderPacer, now: number): void {
|
|
124
|
+
for (let index = state.queue.length - 1; index >= 0; index -= 1) {
|
|
125
|
+
const waiter = state.queue[index]!;
|
|
126
|
+
if (now - waiter.queuedAt < maxQueueAgeMs) continue;
|
|
127
|
+
state.queue.splice(index, 1);
|
|
128
|
+
if (waiter.abort) waiter.signal?.removeEventListener("abort", waiter.abort);
|
|
129
|
+
waiter.reject(new RequestPacingQueueOverloadError(
|
|
130
|
+
providerName,
|
|
131
|
+
"queue_expired",
|
|
132
|
+
pacingRetryAfterSeconds(state, waiter.modelId, now),
|
|
133
|
+
));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function removeProviderPacer(providerName: string, state: ProviderPacer): void {
|
|
138
|
+
if (state.timer) runtime.clearTimer(state.timer);
|
|
139
|
+
state.timer = undefined;
|
|
140
|
+
pacers.delete(providerName);
|
|
141
|
+
const waiters = state.queue.splice(0);
|
|
142
|
+
const error = new RequestPacingProviderRemovedError(providerName);
|
|
143
|
+
for (const waiter of waiters) {
|
|
144
|
+
if (waiter.abort) waiter.signal?.removeEventListener("abort", waiter.abort);
|
|
145
|
+
waiter.reject(error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function runQueue(providerName: string, state: ProviderPacer): void {
|
|
150
|
+
if (state.queue.length === 0) {
|
|
151
|
+
if (state.timer) runtime.clearTimer(state.timer);
|
|
152
|
+
state.timer = undefined;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (state.timer) return;
|
|
156
|
+
const now = runtime.now();
|
|
157
|
+
for (const [modelId, readyAt] of state.modelNextStartAt) {
|
|
158
|
+
if (readyAt <= now) state.modelNextStartAt.delete(modelId);
|
|
159
|
+
}
|
|
160
|
+
rejectExpiredWaiters(providerName, state, now);
|
|
161
|
+
if (state.queue.length === 0) return;
|
|
162
|
+
|
|
163
|
+
const providerReadyAt = Math.max(now, state.providerNextStartAt);
|
|
164
|
+
const waiterIndex = state.queue.findIndex(waiter => {
|
|
165
|
+
const modelReadyAt = waiter.modelId ? (state.modelNextStartAt.get(waiter.modelId) ?? 0) : 0;
|
|
166
|
+
return Math.max(providerReadyAt, modelReadyAt) <= now;
|
|
167
|
+
});
|
|
168
|
+
if (waiterIndex < 0) {
|
|
169
|
+
let earliestAt = Number.POSITIVE_INFINITY;
|
|
170
|
+
for (const waiter of state.queue) {
|
|
171
|
+
const modelReadyAt = waiter.modelId ? (state.modelNextStartAt.get(waiter.modelId) ?? 0) : 0;
|
|
172
|
+
const readyAt = Math.max(providerReadyAt, modelReadyAt);
|
|
173
|
+
const expiresAt = waiter.queuedAt + maxQueueAgeMs;
|
|
174
|
+
earliestAt = Math.min(earliestAt, readyAt, expiresAt);
|
|
175
|
+
}
|
|
176
|
+
const delayMs = Math.max(0, earliestAt - now);
|
|
177
|
+
state.timer = runtime.setTimer(() => {
|
|
178
|
+
state.timer = undefined;
|
|
179
|
+
runQueue(providerName, state);
|
|
180
|
+
}, delayMs);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const waiter = state.queue[waiterIndex]!;
|
|
184
|
+
state.queue.splice(waiterIndex, 1);
|
|
185
|
+
if (waiter.abort) waiter.signal?.removeEventListener("abort", waiter.abort);
|
|
186
|
+
const startedAt = runtime.now();
|
|
187
|
+
state.lastStartedAt = startedAt;
|
|
188
|
+
state.lastModelId = waiter.modelId;
|
|
189
|
+
state.providerNextStartAt = startedAt + waiter.providerIntervalMs;
|
|
190
|
+
if (waiter.modelId && waiter.modelIntervalMs > 0) {
|
|
191
|
+
state.modelNextStartAt.set(waiter.modelId, startedAt + waiter.modelIntervalMs);
|
|
192
|
+
}
|
|
193
|
+
waiter.resolve();
|
|
194
|
+
runtime.enqueueMicrotask(() => runQueue(providerName, state));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function waitForProviderRequestSlot(
|
|
198
|
+
providerName: string,
|
|
199
|
+
provider: OcxProviderConfig,
|
|
200
|
+
modelId?: string,
|
|
201
|
+
signal?: AbortSignal,
|
|
202
|
+
): Promise<void> {
|
|
203
|
+
const intervals = requestPacingIntervals(provider, modelId);
|
|
204
|
+
if (Math.max(intervals.providerIntervalMs, intervals.modelIntervalMs) <= 0) return;
|
|
205
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
206
|
+
|
|
207
|
+
const state = pacers.get(providerName) ?? {
|
|
208
|
+
queue: [], providerNextStartAt: 0, modelNextStartAt: new Map<string, number>(),
|
|
209
|
+
};
|
|
210
|
+
pacers.set(providerName, state);
|
|
211
|
+
|
|
212
|
+
// Give already-eligible or expired waiters a chance to leave before applying the
|
|
213
|
+
// admission bound to the newest request. This preserves FIFO-ish fairness while
|
|
214
|
+
// keeping the retained queue strictly bounded under burst load.
|
|
215
|
+
if (state.timer) {
|
|
216
|
+
runtime.clearTimer(state.timer);
|
|
217
|
+
state.timer = undefined;
|
|
218
|
+
}
|
|
219
|
+
runQueue(providerName, state);
|
|
220
|
+
if (state.queue.length >= maxQueueDepth) {
|
|
221
|
+
throw new RequestPacingQueueOverloadError(
|
|
222
|
+
providerName,
|
|
223
|
+
"queue_full",
|
|
224
|
+
pacingRetryAfterSeconds(state, modelId, runtime.now()),
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
await new Promise<void>((resolve, reject) => {
|
|
229
|
+
const waiter: Waiter = { modelId, ...intervals, queuedAt: runtime.now(), signal, resolve, reject };
|
|
230
|
+
waiter.abort = () => {
|
|
231
|
+
const index = state.queue.indexOf(waiter);
|
|
232
|
+
if (index >= 0) state.queue.splice(index, 1);
|
|
233
|
+
if (state.timer) {
|
|
234
|
+
runtime.clearTimer(state.timer);
|
|
235
|
+
state.timer = undefined;
|
|
236
|
+
}
|
|
237
|
+
reject(abortReason(signal!));
|
|
238
|
+
runQueue(providerName, state);
|
|
239
|
+
};
|
|
240
|
+
signal?.addEventListener("abort", waiter.abort, { once: true });
|
|
241
|
+
state.queue.push(waiter);
|
|
242
|
+
// Abort may race between the eager check above and listener registration.
|
|
243
|
+
if (signal?.aborted) {
|
|
244
|
+
waiter.abort();
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (state.timer) {
|
|
248
|
+
runtime.clearTimer(state.timer);
|
|
249
|
+
state.timer = undefined;
|
|
250
|
+
}
|
|
251
|
+
runQueue(providerName, state);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function providerRequestPacingStatus(
|
|
256
|
+
providerName: string,
|
|
257
|
+
provider: OcxProviderConfig,
|
|
258
|
+
now = runtime.now(),
|
|
259
|
+
): ProviderRequestPacingStatus {
|
|
260
|
+
const state = pacers.get(providerName);
|
|
261
|
+
let nextSlotAt = state?.providerNextStartAt ?? 0;
|
|
262
|
+
if (state && state.queue.length > 0) {
|
|
263
|
+
let earliestQueuedSlotAt = Number.POSITIVE_INFINITY;
|
|
264
|
+
for (const waiter of state.queue) {
|
|
265
|
+
earliestQueuedSlotAt = Math.min(earliestQueuedSlotAt, waiterReadyAt(state, waiter.modelId));
|
|
266
|
+
}
|
|
267
|
+
if (Number.isFinite(earliestQueuedSlotAt)) nextSlotAt = earliestQueuedSlotAt;
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
provider: providerName,
|
|
271
|
+
enabled: provider.requestPacing?.enabled === true,
|
|
272
|
+
queued: state?.queue.length ?? 0,
|
|
273
|
+
nextSlotInMs: Math.max(0, Math.ceil(nextSlotAt - now)),
|
|
274
|
+
...(state?.lastStartedAt !== undefined ? { lastStartedAt: state.lastStartedAt } : {}),
|
|
275
|
+
...(state?.lastModelId ? { lastModelId: state.lastModelId } : {}),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function setProviderRequestPacingLimitsForTest(limits: {
|
|
280
|
+
maxQueueDepth?: number;
|
|
281
|
+
maxQueueAgeMs?: number;
|
|
282
|
+
}): void {
|
|
283
|
+
if (limits.maxQueueDepth !== undefined) maxQueueDepth = limits.maxQueueDepth;
|
|
284
|
+
if (limits.maxQueueAgeMs !== undefined) maxQueueAgeMs = limits.maxQueueAgeMs;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function setProviderRequestPacingRuntimeForTest(nextRuntime: RequestPacingRuntime): void {
|
|
288
|
+
runtime = nextRuntime;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function reconcileProviderRequestPacing(context: GenerationContext): number {
|
|
292
|
+
if (context.generation <= lastReconciledGeneration) return 0;
|
|
293
|
+
let removed = 0;
|
|
294
|
+
for (const [providerName, state] of pacers) {
|
|
295
|
+
if (context.providerNames.has(providerName)) continue;
|
|
296
|
+
removeProviderPacer(providerName, state);
|
|
297
|
+
removed += 1;
|
|
298
|
+
}
|
|
299
|
+
lastReconciledGeneration = context.generation;
|
|
300
|
+
return removed;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function resetProviderRequestPacingForTest(): void {
|
|
304
|
+
for (const state of pacers.values()) if (state.timer) runtime.clearTimer(state.timer);
|
|
305
|
+
pacers.clear();
|
|
306
|
+
maxQueueDepth = REQUEST_PACING_MAX_QUEUE_DEPTH;
|
|
307
|
+
maxQueueAgeMs = REQUEST_PACING_MAX_QUEUE_AGE_MS;
|
|
308
|
+
runtime = defaultRuntime;
|
|
309
|
+
lastReconciledGeneration = 0;
|
|
310
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type { OcxProviderConfig } from "../types";
|
|
2
|
+
import { captureWireAdapterHardPins } from "../types";
|
|
3
|
+
import { isCanonicalOpenAiForwardProvider } from "./openai-tiers";
|
|
4
|
+
import {
|
|
5
|
+
getProviderRegistryEntry,
|
|
6
|
+
providerMatchesRegistryTransport,
|
|
7
|
+
registryModelServiceTierCapabilityApplies,
|
|
8
|
+
type InboundWire,
|
|
9
|
+
type ModelWireDefault,
|
|
10
|
+
} from "./registry";
|
|
11
|
+
import {
|
|
12
|
+
cloneFastWire,
|
|
13
|
+
resolveFastPolicy,
|
|
14
|
+
resolveProviderAuthTransport,
|
|
15
|
+
type FastPolicyAuthority,
|
|
16
|
+
type ResolvedFastPolicy,
|
|
17
|
+
} from "./fastwire";
|
|
18
|
+
|
|
19
|
+
/** OpenAI-compatible adapters that can carry the standard `service_tier` field. */
|
|
20
|
+
export const SERVICE_TIER_ADAPTERS = new Set(["openai-chat", "openai-responses"]);
|
|
21
|
+
|
|
22
|
+
/** @deprecated A1 evolves this snapshot into the complete FastPolicyAuthority. */
|
|
23
|
+
export type CapturedServiceTierAdapterAuthority = FastPolicyAuthority;
|
|
24
|
+
|
|
25
|
+
const capturedFastPolicyAuthorities = new WeakMap<object, FastPolicyAuthority>();
|
|
26
|
+
|
|
27
|
+
type ServiceTierCapabilityProvider = Pick<
|
|
28
|
+
OcxProviderConfig,
|
|
29
|
+
| "adapter"
|
|
30
|
+
| "supportsServiceTier"
|
|
31
|
+
| "modelSupportsServiceTier"
|
|
32
|
+
| "modelAdapters"
|
|
33
|
+
| "baseUrl"
|
|
34
|
+
| "authMode"
|
|
35
|
+
| "apiKeyTransport"
|
|
36
|
+
| "chatServiceTier"
|
|
37
|
+
| "fastWire"
|
|
38
|
+
>;
|
|
39
|
+
|
|
40
|
+
function cloneRegistryWireDefaults(
|
|
41
|
+
defaults: Readonly<Record<string, ModelWireDefault>> | undefined,
|
|
42
|
+
): Readonly<Record<string, ModelWireDefault>> {
|
|
43
|
+
if (!defaults) return Object.freeze({});
|
|
44
|
+
const clone: Record<string, ModelWireDefault> = {};
|
|
45
|
+
for (const [modelId, declaration] of Object.entries(defaults)) {
|
|
46
|
+
clone[modelId.trim().toLowerCase()] = typeof declaration === "string"
|
|
47
|
+
? declaration
|
|
48
|
+
: Object.freeze({
|
|
49
|
+
wire: declaration.wire,
|
|
50
|
+
inbound: Object.freeze([...declaration.inbound]),
|
|
51
|
+
...(declaration.authModes
|
|
52
|
+
? { authModes: Object.freeze([...declaration.authModes]) }
|
|
53
|
+
: {}),
|
|
54
|
+
...(declaration.forwardCallerServiceTier !== undefined
|
|
55
|
+
? { forwardCallerServiceTier: declaration.forwardCallerServiceTier }
|
|
56
|
+
: {}),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return Object.freeze(clone);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Capture every registry-owned input before an asynchronous catalog flight begins.
|
|
64
|
+
* The resolver itself is pure and never reads the live provider registry.
|
|
65
|
+
*/
|
|
66
|
+
function buildFastPolicyAuthority(
|
|
67
|
+
providerName: string,
|
|
68
|
+
provider: ServiceTierCapabilityProvider,
|
|
69
|
+
registryTransportMatch: boolean,
|
|
70
|
+
capabilityProvider: ServiceTierCapabilityProvider = provider,
|
|
71
|
+
): FastPolicyAuthority {
|
|
72
|
+
const registry = registryTransportMatch ? getProviderRegistryEntry(providerName) : undefined;
|
|
73
|
+
const authTransport = resolveProviderAuthTransport(
|
|
74
|
+
provider.adapter,
|
|
75
|
+
provider.authMode ?? registry?.authKind ?? "key",
|
|
76
|
+
provider.apiKeyTransport,
|
|
77
|
+
);
|
|
78
|
+
const keyAuthDefaults = registry?.allowKeyAuthOverride === true
|
|
79
|
+
&& (authTransport === "authorization_bearer" || authTransport === "x_api_key")
|
|
80
|
+
? registry.keyAuthServiceTier
|
|
81
|
+
: undefined;
|
|
82
|
+
const registryModelCapabilities = registry
|
|
83
|
+
&& registryModelServiceTierCapabilityApplies(registry, capabilityProvider)
|
|
84
|
+
? registry.modelSupportsServiceTier
|
|
85
|
+
: undefined;
|
|
86
|
+
const providerCapability = capabilityProvider.supportsServiceTier
|
|
87
|
+
?? keyAuthDefaults?.supportsServiceTier
|
|
88
|
+
?? registry?.supportsServiceTier;
|
|
89
|
+
const authority: FastPolicyAuthority = Object.freeze({
|
|
90
|
+
providerAdapter: provider.adapter,
|
|
91
|
+
providerAuthMode: provider.authMode ?? registry?.authKind ?? "key",
|
|
92
|
+
fastWireDeclaration: cloneFastWire(
|
|
93
|
+
provider.fastWire !== undefined ? provider.fastWire : registry?.fastWire,
|
|
94
|
+
{ freeze: true },
|
|
95
|
+
),
|
|
96
|
+
...(registry?.fastTierDescription !== undefined
|
|
97
|
+
? { fastTierDescription: registry.fastTierDescription }
|
|
98
|
+
: {}),
|
|
99
|
+
modelWireOverrideAllowed: !isCanonicalOpenAiForwardProvider(provider as OcxProviderConfig),
|
|
100
|
+
authTransport,
|
|
101
|
+
capability: Object.freeze({
|
|
102
|
+
...(providerCapability !== undefined ? { provider: providerCapability } : {}),
|
|
103
|
+
models: Object.freeze({
|
|
104
|
+
...(registryModelCapabilities ?? {}),
|
|
105
|
+
...(keyAuthDefaults?.modelSupportsServiceTier ?? {}),
|
|
106
|
+
...(capabilityProvider.modelSupportsServiceTier ?? {}),
|
|
107
|
+
}),
|
|
108
|
+
...(provider.chatServiceTier !== undefined
|
|
109
|
+
? { chatServiceTier: provider.chatServiceTier }
|
|
110
|
+
: keyAuthDefaults?.chatServiceTier !== undefined
|
|
111
|
+
? { chatServiceTier: keyAuthDefaults.chatServiceTier }
|
|
112
|
+
: {}),
|
|
113
|
+
}),
|
|
114
|
+
modelAdapters: Object.freeze({ ...(provider.modelAdapters ?? {}) }),
|
|
115
|
+
hardPins: captureWireAdapterHardPins(providerName),
|
|
116
|
+
registryWireDefaults: cloneRegistryWireDefaults(registry?.modelWireDefaults),
|
|
117
|
+
});
|
|
118
|
+
return authority;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function captureFastPolicyAuthority(
|
|
122
|
+
providerName: string,
|
|
123
|
+
provider: ServiceTierCapabilityProvider,
|
|
124
|
+
registryTransportMatch: boolean,
|
|
125
|
+
capabilityProvider: ServiceTierCapabilityProvider = provider,
|
|
126
|
+
): FastPolicyAuthority {
|
|
127
|
+
const authority = buildFastPolicyAuthority(
|
|
128
|
+
providerName,
|
|
129
|
+
provider,
|
|
130
|
+
registryTransportMatch,
|
|
131
|
+
capabilityProvider,
|
|
132
|
+
);
|
|
133
|
+
if (Object.isFrozen(provider)) capturedFastPolicyAuthorities.set(provider, authority);
|
|
134
|
+
return authority;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** @deprecated Use captureFastPolicyAuthority. The legacy inbound argument is now snapshot data. */
|
|
138
|
+
export function captureServiceTierAdapterAuthority(
|
|
139
|
+
providerName: string,
|
|
140
|
+
provider: ServiceTierCapabilityProvider,
|
|
141
|
+
registryTransportMatch: boolean,
|
|
142
|
+
_inbound: InboundWire = "responses",
|
|
143
|
+
): FastPolicyAuthority {
|
|
144
|
+
return captureFastPolicyAuthority(providerName, provider, registryTransportMatch);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function authorityForProvider(
|
|
148
|
+
provider: ServiceTierCapabilityProvider,
|
|
149
|
+
providerName?: string,
|
|
150
|
+
capabilityProvider?: ServiceTierCapabilityProvider,
|
|
151
|
+
): FastPolicyAuthority {
|
|
152
|
+
// Preserve the legacy no-name short circuit: serviceTierSupportForModel() used the
|
|
153
|
+
// provider adapter directly when no provider identity was available, so no configured
|
|
154
|
+
// override, hard pin, or registry default may participate on this path in A1.
|
|
155
|
+
if (providerName === undefined) {
|
|
156
|
+
const authority = buildFastPolicyAuthority("", provider, false, capabilityProvider ?? provider);
|
|
157
|
+
return Object.freeze({
|
|
158
|
+
...authority,
|
|
159
|
+
modelAdapters: Object.freeze({}),
|
|
160
|
+
hardPins: Object.freeze({}),
|
|
161
|
+
registryWireDefaults: Object.freeze({}),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const captured = capabilityProvider === undefined && Object.isFrozen(provider)
|
|
165
|
+
? capturedFastPolicyAuthorities.get(provider)
|
|
166
|
+
: undefined;
|
|
167
|
+
if (captured) return captured;
|
|
168
|
+
const registryTransportMatch = providerMatchesRegistryTransport(providerName, provider);
|
|
169
|
+
const authority = buildFastPolicyAuthority(
|
|
170
|
+
providerName,
|
|
171
|
+
provider,
|
|
172
|
+
registryTransportMatch,
|
|
173
|
+
capabilityProvider ?? provider,
|
|
174
|
+
);
|
|
175
|
+
// Frozen provider snapshots cannot drift, so repeated catalog/runtime projections may safely
|
|
176
|
+
// reuse the registry lookup and detached declaration maps. Mutable configs still rebuild.
|
|
177
|
+
if (Object.isFrozen(provider)) capturedFastPolicyAuthorities.set(provider, authority);
|
|
178
|
+
return authority;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Resolve the pure Fast policy for a provider/model pair. */
|
|
182
|
+
export function fastPolicyForModel(
|
|
183
|
+
provider: ServiceTierCapabilityProvider,
|
|
184
|
+
modelId: string,
|
|
185
|
+
providerName?: string,
|
|
186
|
+
inbound: InboundWire = "responses",
|
|
187
|
+
capabilityProvider?: ServiceTierCapabilityProvider,
|
|
188
|
+
): ResolvedFastPolicy {
|
|
189
|
+
return resolveFastPolicy(
|
|
190
|
+
authorityForProvider(provider, providerName, capabilityProvider),
|
|
191
|
+
modelId,
|
|
192
|
+
inbound,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Resolve the declared provider/model capability without applying wire availability.
|
|
198
|
+
* Kept as a public compatibility helper for callers that need the pure tri-state.
|
|
199
|
+
*/
|
|
200
|
+
export function supportsServiceTierForModel(
|
|
201
|
+
provider: Pick<OcxProviderConfig, "supportsServiceTier" | "modelSupportsServiceTier">,
|
|
202
|
+
modelId: string,
|
|
203
|
+
): boolean | undefined {
|
|
204
|
+
const authority: FastPolicyAuthority = {
|
|
205
|
+
providerAdapter: "openai-responses",
|
|
206
|
+
fastWireDeclaration: undefined,
|
|
207
|
+
modelWireOverrideAllowed: true,
|
|
208
|
+
authTransport: "authorization_bearer",
|
|
209
|
+
capability: {
|
|
210
|
+
...(provider.supportsServiceTier !== undefined ? { provider: provider.supportsServiceTier } : {}),
|
|
211
|
+
models: provider.modelSupportsServiceTier ?? {},
|
|
212
|
+
},
|
|
213
|
+
modelAdapters: {},
|
|
214
|
+
hardPins: {},
|
|
215
|
+
registryWireDefaults: {},
|
|
216
|
+
};
|
|
217
|
+
return resolveFastPolicy(authority, modelId).capability;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Whether a Chat route may forward an arbitrary caller tier rather than canonical Fast. */
|
|
221
|
+
export function canForwardForeignServiceTierForChatModel(
|
|
222
|
+
provider: Pick<OcxProviderConfig, "supportsServiceTier" | "modelSupportsServiceTier" | "chatServiceTier">,
|
|
223
|
+
modelId: string,
|
|
224
|
+
): boolean {
|
|
225
|
+
const capability = supportsServiceTierForModel(provider, modelId);
|
|
226
|
+
return capability !== false && provider.chatServiceTier === true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Final adapter selected by the Fast policy's four-level wire resolver. */
|
|
230
|
+
export function serviceTierAdapterForModel(
|
|
231
|
+
providerName: string,
|
|
232
|
+
provider: ServiceTierCapabilityProvider,
|
|
233
|
+
modelId: string,
|
|
234
|
+
inbound: InboundWire = "responses",
|
|
235
|
+
): string {
|
|
236
|
+
return fastPolicyForModel(provider, modelId, providerName, inbound).adapter;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** Whether the final provider/model pair can publish/send OpenAI service tiers. */
|
|
240
|
+
export function canForwardServiceTierForModel(
|
|
241
|
+
provider: ServiceTierCapabilityProvider,
|
|
242
|
+
modelId: string,
|
|
243
|
+
providerName?: string,
|
|
244
|
+
inbound: InboundWire = "responses",
|
|
245
|
+
): boolean {
|
|
246
|
+
return serviceTierSupportForModel(provider, modelId, providerName, inbound) === true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Compatibility projection for catalog, routing, and fingerprint consumers. The new
|
|
251
|
+
* resolver carries richer eligibility internally while preserving the old tri-state bytes.
|
|
252
|
+
*/
|
|
253
|
+
export function serviceTierSupportForModel(
|
|
254
|
+
provider: ServiceTierCapabilityProvider,
|
|
255
|
+
modelId: string,
|
|
256
|
+
providerName?: string,
|
|
257
|
+
inbound: InboundWire = "responses",
|
|
258
|
+
): boolean | undefined {
|
|
259
|
+
const policy = fastPolicyForModel(provider, modelId, providerName, inbound);
|
|
260
|
+
return serviceTierSupportFromPolicy(policy);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Compatibility projection shared by catalog, routing, and request logging. */
|
|
264
|
+
export function serviceTierSupportFromPolicy(
|
|
265
|
+
policy: Pick<ResolvedFastPolicy, "eligibility" | "adapter" | "forwardCallerTier">,
|
|
266
|
+
): boolean | undefined {
|
|
267
|
+
if (policy.eligibility === "eligible") return true;
|
|
268
|
+
if (policy.eligibility === "unclassified") {
|
|
269
|
+
// An unclassified route that cannot forward a caller tier has definitive negative
|
|
270
|
+
// evidence even when its adapter can normally serialize service_tier. This covers both
|
|
271
|
+
// Chat routes without chatServiceTier and a registry default that explicitly closes a
|
|
272
|
+
// subscription gateway. Generic unclassified Responses routes still project unknown.
|
|
273
|
+
if (!policy.forwardCallerTier) return false;
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
@@ -29,6 +29,20 @@ export function encodeRoutedModelId(id: string): string {
|
|
|
29
29
|
return id.includes("/") ? id.replaceAll("/", SLUG_ALIAS_SEPARATOR) : id;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* True when `modelId` shares a Codex-facing encoded form with a different known id.
|
|
34
|
+
* That collision is what makes `provider/openai-gpt-5.5` decode to native `openai-gpt-5.5`
|
|
35
|
+
* while a custom `openai/gpt-5.5` row is still visible.
|
|
36
|
+
*/
|
|
37
|
+
export function encodedModelIdCollides(modelId: string, knownIds: Iterable<string>): boolean {
|
|
38
|
+
const encoded = encodeRoutedModelId(modelId);
|
|
39
|
+
for (const id of knownIds) {
|
|
40
|
+
if (id === modelId) continue;
|
|
41
|
+
if (encodeRoutedModelId(id) === encoded) return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
/** Codex-facing routed slug: exactly one "/" — `<provider>/<encoded id>`. */
|
|
33
47
|
export function routedSlug(provider: string, id: string): string {
|
|
34
48
|
return `${provider}/${encodeRoutedModelId(id)}`;
|
|
@@ -51,17 +65,39 @@ export function decodeRoutedModelId(requested: string, knownIds: Iterable<string
|
|
|
51
65
|
return aliasMatch ?? requested;
|
|
52
66
|
}
|
|
53
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Decode a Codex-facing id, but fail when a custom slash id and another known id
|
|
70
|
+
* share the same encoded form. Write-time checks cannot cover a later live cache.
|
|
71
|
+
*/
|
|
72
|
+
export function decodeRoutedModelIdOrThrow(requested: string, knownIds: Iterable<string>): string {
|
|
73
|
+
const ids = [...knownIds];
|
|
74
|
+
const encodedRequested = encodeRoutedModelId(requested);
|
|
75
|
+
const matches = new Set<string>();
|
|
76
|
+
for (const id of ids) {
|
|
77
|
+
if (id === requested || encodeRoutedModelId(id) === encodedRequested) matches.add(id);
|
|
78
|
+
}
|
|
79
|
+
if (matches.size > 1) throw new Error(`ambiguous model id "${requested}"`);
|
|
80
|
+
return decodeRoutedModelId(requested, ids);
|
|
81
|
+
}
|
|
82
|
+
|
|
54
83
|
/** Does a stored config slug name this routed model, in either raw or encoded form? */
|
|
55
84
|
export function slugEquals(stored: string, provider: string, id: string): boolean {
|
|
56
85
|
return stored === `${provider}/${id}` || stored === routedSlug(provider, id);
|
|
57
86
|
}
|
|
58
87
|
|
|
88
|
+
/** Stable key for the exact equivalence relation used by catalog/config slug matching. */
|
|
89
|
+
export function slugEquivalenceKey(slug: string): string {
|
|
90
|
+
const slash = slug.indexOf("/");
|
|
91
|
+
return slash <= 0
|
|
92
|
+
? JSON.stringify(["exact", slug])
|
|
93
|
+
: JSON.stringify([
|
|
94
|
+
"routed",
|
|
95
|
+
slug.slice(0, slash),
|
|
96
|
+
encodeRoutedModelId(slug.slice(slash + 1)),
|
|
97
|
+
]);
|
|
98
|
+
}
|
|
99
|
+
|
|
59
100
|
/** Equivalence between two routed slugs regardless of raw/encoded mix. */
|
|
60
101
|
export function slugsEquivalent(a: string, b: string): boolean {
|
|
61
|
-
|
|
62
|
-
const pa = a.indexOf("/");
|
|
63
|
-
const pb = b.indexOf("/");
|
|
64
|
-
if (pa <= 0 || pb <= 0) return false;
|
|
65
|
-
if (a.slice(0, pa) !== b.slice(0, pb)) return false;
|
|
66
|
-
return encodeRoutedModelId(a.slice(pa + 1)) === encodeRoutedModelId(b.slice(pb + 1));
|
|
102
|
+
return a === b || slugEquivalenceKey(a) === slugEquivalenceKey(b);
|
|
67
103
|
}
|