@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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Modelled after src/codex/routing.ts cooldown logic but scoped to plain API-key pools.
|
|
10
10
|
*/
|
|
11
11
|
import { saveConfigPreservingClaudeCode } from "../config";
|
|
12
|
-
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
12
|
+
import type { OcxConfig, OcxProviderConfig, RateLimitRetryPolicy } from "../types";
|
|
13
13
|
import { resolveProviderTransport, type OcxProviderTransport } from "./xai-transport";
|
|
14
14
|
import { sweepExpiredOnWrite } from "../lib/state-store-sweeper";
|
|
15
15
|
|
|
@@ -22,6 +22,18 @@ interface KeyCooldown {
|
|
|
22
22
|
const DEFAULT_COOLDOWN_MS = 60_000;
|
|
23
23
|
const MAX_COOLDOWN_MS = 10 * 60_000; // cap at 10 min for api-key rotation
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Default same-target 429 retry policy used when a provider opts in via a bare
|
|
27
|
+
* `retryOn429: {}` (presence = opt-in with these defaults).
|
|
28
|
+
*/
|
|
29
|
+
const DEFAULT_RATE_LIMIT_RETRY = {
|
|
30
|
+
enabled: true,
|
|
31
|
+
attempts: 3,
|
|
32
|
+
intervalMs: 5_000,
|
|
33
|
+
maxIntervalMs: 60_000,
|
|
34
|
+
respectRetryAfter: true,
|
|
35
|
+
} as const satisfies Required<RateLimitRetryPolicy>;
|
|
36
|
+
|
|
25
37
|
/** Map<`${providerName}\0${keyId}`, KeyCooldown> */
|
|
26
38
|
const keyCooldowns = new Map<string, KeyCooldown>();
|
|
27
39
|
|
|
@@ -29,21 +41,32 @@ function cooldownKey(providerName: string, keyId: string): string {
|
|
|
29
41
|
return `${providerName}\0${keyId}`;
|
|
30
42
|
}
|
|
31
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Parse an upstream `Retry-After` header: numeric seconds (including `0`) or an HTTP-date.
|
|
46
|
+
* Returns a bounded delay in ms (1..MAX_COOLDOWN_MS), or undefined when the value is
|
|
47
|
+
* malformed. An HTTP-date already in the past yields an immediate (1 ms) retry.
|
|
48
|
+
*/
|
|
32
49
|
function parseRetryAfterMs(value: string | null | undefined, now = Date.now()): number | undefined {
|
|
33
50
|
const text = value?.trim();
|
|
34
51
|
if (!text) return undefined;
|
|
35
52
|
if (/^\d+(?:\.\d+)?$/.test(text)) {
|
|
36
53
|
const seconds = Number(text);
|
|
37
|
-
if (Number.isFinite(seconds) && seconds
|
|
54
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
38
55
|
return Math.min(Math.max(Math.ceil(seconds * 1000), 1), MAX_COOLDOWN_MS);
|
|
39
56
|
}
|
|
40
57
|
}
|
|
41
58
|
const timestamp = Date.parse(text);
|
|
42
59
|
if (!Number.isFinite(timestamp)) return undefined;
|
|
43
60
|
const delay = timestamp - now;
|
|
44
|
-
|
|
61
|
+
// A valid HTTP-date whose retry time has already passed is an immediate retry, exactly like
|
|
62
|
+
// numeric `Retry-After: 0` — never a malformed-header fallback to the fixed interval.
|
|
63
|
+
return Math.min(Math.max(delay, 1), MAX_COOLDOWN_MS);
|
|
45
64
|
}
|
|
46
65
|
|
|
66
|
+
/**
|
|
67
|
+
* True while the given key is inside its 429 cooldown window (lazily evicting the entry once the
|
|
68
|
+
* window expires). Used to skip keys that the upstream just rate-limited during failover.
|
|
69
|
+
*/
|
|
47
70
|
function isKeyInCooldown(providerName: string, keyId: string, now = Date.now()): boolean {
|
|
48
71
|
const entry = keyCooldowns.get(cooldownKey(providerName, keyId));
|
|
49
72
|
if (!entry) return false;
|
|
@@ -65,6 +88,51 @@ export function hasKeyPoolFailover(provider: OcxProviderConfig): boolean {
|
|
|
65
88
|
return (provider.apiKeyPool?.length ?? 0) >= 2;
|
|
66
89
|
}
|
|
67
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Normalize a provider's `retryOn429` policy, or return null when the knob is absent,
|
|
93
|
+
* explicitly disabled, or the provider is not key-auth (OAuth/forward credentials must not be
|
|
94
|
+
* replayed on the same token, forward passthrough never reaches the recovery loop anyway, and
|
|
95
|
+
* local runtimes have no remote key to preserve). The returned policy is fully defaulted so
|
|
96
|
+
* callers never re-check fields.
|
|
97
|
+
*/
|
|
98
|
+
export function rateLimitRetryPolicyFor(
|
|
99
|
+
provider: Pick<OcxProviderConfig, "retryOn429" | "authMode">,
|
|
100
|
+
): Required<RateLimitRetryPolicy> | null {
|
|
101
|
+
const policy = provider.retryOn429;
|
|
102
|
+
if (!policy || policy.enabled === false) return null;
|
|
103
|
+
// Fail closed: only explicit key auth or the documented omitted-default (undefined == key for
|
|
104
|
+
// custom API-key providers) may use same-key replays. OAuth/forward are never replayed on the
|
|
105
|
+
// same token, local runtimes have no remote key to preserve, and unknown/custom values are
|
|
106
|
+
// rejected rather than guessed at.
|
|
107
|
+
if (provider.authMode !== undefined && provider.authMode !== "key") return null;
|
|
108
|
+
return {
|
|
109
|
+
enabled: policy.enabled ?? DEFAULT_RATE_LIMIT_RETRY.enabled,
|
|
110
|
+
attempts: policy.attempts ?? DEFAULT_RATE_LIMIT_RETRY.attempts,
|
|
111
|
+
intervalMs: policy.intervalMs ?? DEFAULT_RATE_LIMIT_RETRY.intervalMs,
|
|
112
|
+
maxIntervalMs: policy.maxIntervalMs ?? DEFAULT_RATE_LIMIT_RETRY.maxIntervalMs,
|
|
113
|
+
respectRetryAfter: policy.respectRetryAfter ?? DEFAULT_RATE_LIMIT_RETRY.respectRetryAfter,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Wait before the next same-target replay: upstream Retry-After (seconds or HTTP-date) when
|
|
119
|
+
* `respectRetryAfter` is on and the header parses, capped at `maxIntervalMs`; otherwise the
|
|
120
|
+
* fixed `intervalMs`, also capped at `maxIntervalMs` (a single wait never exceeds the cap).
|
|
121
|
+
* Malformed headers fall back to the fixed interval.
|
|
122
|
+
*/
|
|
123
|
+
export function rateLimitRetryDelayMs(
|
|
124
|
+
policy: Required<RateLimitRetryPolicy>,
|
|
125
|
+
retryAfterHeader: string | null | undefined,
|
|
126
|
+
now = Date.now(),
|
|
127
|
+
): number {
|
|
128
|
+
const raw = retryAfterHeader?.trim();
|
|
129
|
+
if (policy.respectRetryAfter && raw) {
|
|
130
|
+
const parsed = parseRetryAfterMs(raw, now);
|
|
131
|
+
if (parsed !== undefined) return Math.min(parsed, policy.maxIntervalMs);
|
|
132
|
+
}
|
|
133
|
+
return Math.min(policy.intervalMs, policy.maxIntervalMs);
|
|
134
|
+
}
|
|
135
|
+
|
|
68
136
|
/**
|
|
69
137
|
* Record a 429 for the current key and attempt to switch to the next available one.
|
|
70
138
|
*
|
package/src/providers/label.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CODEX_ACCOUNT_LOG_LABEL_RE } from "../codex/account-label";
|
|
2
2
|
|
|
3
|
-
function canonicalUsageProviderLabel(provider: string): string {
|
|
3
|
+
export function canonicalUsageProviderLabel(provider: string): string {
|
|
4
4
|
return provider === "chatgpt" || provider === "openai-multi" ? "openai" : provider;
|
|
5
5
|
}
|
|
6
6
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Hard process-wide limits shared by all live model-discovery parsers. */
|
|
2
|
+
export const MODEL_DISCOVERY_MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
3
|
+
export const MODEL_DISCOVERY_MAX_MODELS = 2_000;
|
|
4
|
+
export const MODEL_DISCOVERY_MAX_MODEL_ID_LENGTH = 1_024;
|
|
5
|
+
|
|
6
|
+
const MODEL_DISCOVERY_MODEL_ID_CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/;
|
|
7
|
+
|
|
8
|
+
/** Reject model IDs that cannot safely be published as callable catalog selectors. */
|
|
9
|
+
export function isValidModelDiscoveryModelId(value: unknown): value is string {
|
|
10
|
+
if (typeof value !== "string") return false;
|
|
11
|
+
const normalized = value.trim();
|
|
12
|
+
return Boolean(normalized)
|
|
13
|
+
&& normalized === value
|
|
14
|
+
&& normalized.length <= MODEL_DISCOVERY_MAX_MODEL_ID_LENGTH
|
|
15
|
+
&& !MODEL_DISCOVERY_MODEL_ID_CONTROL_CHARS.test(normalized);
|
|
16
|
+
}
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import type { OcxProviderConfig } from "../types";
|
|
2
|
+
import {
|
|
3
|
+
isValidModelDiscoveryModelId,
|
|
4
|
+
MODEL_DISCOVERY_MAX_MODEL_ID_LENGTH,
|
|
5
|
+
MODEL_DISCOVERY_MAX_MODELS,
|
|
6
|
+
MODEL_DISCOVERY_MAX_RESPONSE_BYTES,
|
|
7
|
+
} from "./model-discovery-limits";
|
|
8
|
+
export {
|
|
9
|
+
isValidModelDiscoveryModelId,
|
|
10
|
+
MODEL_DISCOVERY_MAX_MODEL_ID_LENGTH,
|
|
11
|
+
MODEL_DISCOVERY_MAX_MODELS,
|
|
12
|
+
MODEL_DISCOVERY_MAX_RESPONSE_BYTES,
|
|
13
|
+
} from "./model-discovery-limits";
|
|
2
14
|
import {
|
|
3
15
|
getProviderRegistryEntry,
|
|
4
16
|
providerMatchesRegistryTransport,
|
|
17
|
+
registryEntryForProviderDestination,
|
|
5
18
|
type ProviderModelDiscoveryFilter,
|
|
6
19
|
type ProviderModelDiscoveryPredicate,
|
|
7
20
|
type ProviderModelDiscoveryScalar,
|
|
8
21
|
type ProviderModelDiscoverySpec,
|
|
9
22
|
} from "./registry";
|
|
10
23
|
|
|
11
|
-
/** Hard process-wide limits. Registry entries may lower, but never raise, these ceilings. */
|
|
12
|
-
export const MODEL_DISCOVERY_MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
13
|
-
export const MODEL_DISCOVERY_MAX_MODELS = 2_000;
|
|
14
|
-
export const MODEL_DISCOVERY_MAX_MODEL_ID_LENGTH = 1_024;
|
|
15
24
|
const MODEL_DISCOVERY_MAX_FILTER_VALUES = 256;
|
|
16
25
|
const MODEL_DISCOVERY_MAX_FILTER_STRING_LENGTH = 1_024;
|
|
17
|
-
const MODEL_DISCOVERY_MODEL_ID_CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/;
|
|
18
26
|
|
|
19
27
|
export interface ResolvedProviderModelDiscovery {
|
|
20
28
|
spec?: ProviderModelDiscoverySpec;
|
|
@@ -91,7 +99,12 @@ export function providerModelDiscoverySpecError(spec: ProviderModelDiscoverySpec
|
|
|
91
99
|
return "discovery path must be a query-free relative/origin path";
|
|
92
100
|
}
|
|
93
101
|
if (path.includes("\\")) return "discovery path must use forward slashes";
|
|
94
|
-
|
|
102
|
+
const segments = path.split("/");
|
|
103
|
+
if (segments.some((segment, index) => {
|
|
104
|
+
const decoded = segment.replace(/%2e/gi, ".");
|
|
105
|
+
if (decoded !== "..") return false;
|
|
106
|
+
return index !== 0 || segments.filter(s => s.replace(/%2e/gi, ".") === "..").length !== 1;
|
|
107
|
+
})) {
|
|
95
108
|
return "discovery path must not contain parent-directory segments";
|
|
96
109
|
}
|
|
97
110
|
}
|
|
@@ -124,9 +137,14 @@ export function resolveProviderModelDiscovery(
|
|
|
124
137
|
providerName: string,
|
|
125
138
|
provider: Pick<OcxProviderConfig, "baseUrl" | "adapter"> & Partial<Pick<OcxProviderConfig, "authMode">>,
|
|
126
139
|
): ResolvedProviderModelDiscovery {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
// The dashboard permits a canonical preset to be saved under a different name. Recover its
|
|
141
|
+
// registry-owned discovery policy by transport in that case. The destination helper is limited
|
|
142
|
+
// to exact fixed-key baseUrl + adapter matches, so custom endpoints, OAuth rows, templates, and
|
|
143
|
+
// overridable destinations cannot acquire another provider's discovery URL or filter.
|
|
144
|
+
const namedEntry = getProviderRegistryEntry(providerName);
|
|
145
|
+
const entry = namedEntry
|
|
146
|
+
? (providerMatchesRegistryTransport(providerName, provider) ? namedEntry : undefined)
|
|
147
|
+
: registryEntryForProviderDestination(provider);
|
|
130
148
|
const spec = entry?.modelDiscovery;
|
|
131
149
|
return {
|
|
132
150
|
...(spec ? { spec } : {}),
|
|
@@ -313,12 +331,83 @@ export function extractModelEnvelopeRows(
|
|
|
313
331
|
}
|
|
314
332
|
|
|
315
333
|
/** Validate, bound, deduplicate, and declaratively filter OpenAI `{data:[...]}` or top-level arrays (Together `#617`). */
|
|
334
|
+
/**
|
|
335
|
+
* Metadata a sibling `models[]` array may contribute to an ALREADY-ADMITTED
|
|
336
|
+
* `data[]` row (#1797).
|
|
337
|
+
*
|
|
338
|
+
* llama.cpp serves a dual-envelope body: an Ollama-style `models[]` array
|
|
339
|
+
* carrying `capabilities` alongside the OpenAI-style `data[]` array carrying
|
|
340
|
+
* `meta`, so the two halves of one model's metadata never meet and a server
|
|
341
|
+
* that truthfully advertises "multimodal" produced an image-blind row.
|
|
342
|
+
*
|
|
343
|
+
* Two boundaries make this safe, and both were added after review found the
|
|
344
|
+
* first attempt unsound:
|
|
345
|
+
*
|
|
346
|
+
* 1. It runs AFTER admission filtering. Enriching first let a sibling supply
|
|
347
|
+
* the exact field a provider filter requires — reproduced against the real
|
|
348
|
+
* Chutes policy, where a row lacking `supported_features: ["tools"]` was
|
|
349
|
+
* admitted once a same-id sibling provided it. Enrichment may change what is
|
|
350
|
+
* KNOWN about a model, never WHICH models are published.
|
|
351
|
+
* 2. Only the capability keys #1797 needs are copied. A blanket "fill every
|
|
352
|
+
* absent key" made the untrusted `models[]` array a way into any field the
|
|
353
|
+
* pipeline consumes.
|
|
354
|
+
*
|
|
355
|
+
* The list deliberately EXCLUDES `supported_features` and `features`, even
|
|
356
|
+
* though both are capability-shaped: they are the two keys real provider
|
|
357
|
+
* filters test (`registry.ts:1591` requires `supported_features` to contain
|
|
358
|
+
* "tools"; `registry.ts:1883` tests `features.tool_use`). Ordering already
|
|
359
|
+
* prevents a sibling from flipping an admission verdict, but a key that is
|
|
360
|
+
* both enrichable and filter-relevant is one refactor away from becoming a
|
|
361
|
+
* bypass again. #1797 does not need them.
|
|
362
|
+
*/
|
|
363
|
+
const SIBLING_ENRICHABLE_KEYS = new Set(["capabilities", "modalities", "input_modalities"]);
|
|
364
|
+
|
|
365
|
+
type SiblingIndex = Map<string, Record<string, unknown> | null>;
|
|
366
|
+
|
|
367
|
+
function buildSiblingIndex(value: unknown, limit: number): SiblingIndex | null {
|
|
368
|
+
const record = plainObject(value);
|
|
369
|
+
const sibling = record?.models;
|
|
370
|
+
if (!Array.isArray(sibling) || sibling.length === 0 || sibling.length > limit) return null;
|
|
371
|
+
|
|
372
|
+
const byId: SiblingIndex = new Map();
|
|
373
|
+
for (const raw of sibling) {
|
|
374
|
+
const entry = plainObject(raw);
|
|
375
|
+
if (!entry) continue;
|
|
376
|
+
for (const key of ["id", "model", "name"]) {
|
|
377
|
+
const id = entry[key];
|
|
378
|
+
if (typeof id !== "string" || id.length === 0) continue;
|
|
379
|
+
// `null` marks an ambiguous id: two sibling entries claim it, so neither
|
|
380
|
+
// can be attributed with confidence and both are ignored.
|
|
381
|
+
byId.set(id, byId.has(id) && byId.get(id) !== entry ? null : entry);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return byId.size > 0 ? byId : null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function enrichAdmittedModel(item: ProviderModelsApiItem, siblings: SiblingIndex): ProviderModelsApiItem {
|
|
388
|
+
const extra = siblings.get(item.id);
|
|
389
|
+
if (!extra) return item;
|
|
390
|
+
let merged: Record<string, unknown> | null = null;
|
|
391
|
+
for (const key of SIBLING_ENRICHABLE_KEYS) {
|
|
392
|
+
if (!(key in extra) || key in item) continue;
|
|
393
|
+
merged ??= { ...(item as Record<string, unknown>) };
|
|
394
|
+
merged[key] = extra[key];
|
|
395
|
+
}
|
|
396
|
+
return (merged ?? item) as ProviderModelsApiItem;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function plainObject(value: unknown): Record<string, unknown> | null {
|
|
400
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
401
|
+
? value as Record<string, unknown>
|
|
402
|
+
: null;
|
|
403
|
+
}
|
|
316
404
|
export function extractProviderModelItems(
|
|
317
405
|
value: unknown,
|
|
318
406
|
discovery: ResolvedProviderModelDiscovery,
|
|
319
407
|
): ProviderModelItemsResult {
|
|
320
408
|
const limit = positiveIntegerAtMost(discovery.maxModels, MODEL_DISCOVERY_MAX_MODELS);
|
|
321
409
|
let data: unknown[];
|
|
410
|
+
let siblings: SiblingIndex | null = null;
|
|
322
411
|
if (Array.isArray(value)) {
|
|
323
412
|
// Together-style top-level /models arrays. Catalog discovery must not treat a stray
|
|
324
413
|
// `models` key on openai-chat responses as valid — only `data` envelopes or top-level arrays.
|
|
@@ -328,6 +417,7 @@ export function extractProviderModelItems(
|
|
|
328
417
|
const envelope = extractModelEnvelopeRows(value, discovery.maxModels, ["data"]);
|
|
329
418
|
if (!envelope.ok) return envelope;
|
|
330
419
|
data = envelope.rows;
|
|
420
|
+
siblings = buildSiblingIndex(value, limit);
|
|
331
421
|
}
|
|
332
422
|
|
|
333
423
|
const items: ProviderModelsApiItem[] = [];
|
|
@@ -337,20 +427,23 @@ export function extractProviderModelItems(
|
|
|
337
427
|
return { ok: false, reason: "invalid_shape" };
|
|
338
428
|
}
|
|
339
429
|
const id = (raw as { id?: unknown }).id;
|
|
340
|
-
if (
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|| MODEL_DISCOVERY_MODEL_ID_CONTROL_CHARS.test(normalizedId)
|
|
347
|
-
) {
|
|
348
|
-
return { ok: false, reason: "invalid_shape" };
|
|
430
|
+
if (!isValidModelDiscoveryModelId(id)) return { ok: false, reason: "invalid_shape" };
|
|
431
|
+
const prefix = discovery.spec?.stripIdPrefix;
|
|
432
|
+
let finalId = id;
|
|
433
|
+
if (prefix && finalId.startsWith(prefix)) {
|
|
434
|
+
finalId = finalId.slice(prefix.length);
|
|
435
|
+
if (!isValidModelDiscoveryModelId(finalId)) continue;
|
|
349
436
|
}
|
|
350
|
-
const item = raw as ProviderModelsApiItem;
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
437
|
+
const item = finalId === id ? raw as ProviderModelsApiItem : { ...(raw as ProviderModelsApiItem), id: finalId };
|
|
438
|
+
// Admission is decided on the ORIGINAL `data[]` row, before any sibling
|
|
439
|
+
// enrichment. Merging first let a `models[]` entry supply the very field a
|
|
440
|
+
// provider filter requires — reproduced against the real Chutes policy,
|
|
441
|
+
// where a row lacking `supported_features: ["tools"]` was admitted once a
|
|
442
|
+
// same-id sibling provided it. Enrichment must never change WHICH models
|
|
443
|
+
// are published, only what is known about an already-admitted one.
|
|
444
|
+
if (!providerModelMatchesDiscoveryFilter(item, discovery.spec?.filter) || seen.has(finalId)) continue;
|
|
445
|
+
seen.add(finalId);
|
|
446
|
+
items.push(siblings ? enrichAdmittedModel(item, siblings) : item);
|
|
354
447
|
}
|
|
355
448
|
return { ok: true, items, rawCount: data.length };
|
|
356
449
|
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// Registry model renames do not reach a saved provider config on their own.
|
|
2
|
+
//
|
|
3
|
+
// `reconcileOAuthProviders` refuses to touch a row whose `authMode` is not
|
|
4
|
+
// `oauth` (src/oauth/index.ts), and `enrichProviderFromRegistry` is fill-only by
|
|
5
|
+
// design: it backfills a MISSING field and never rewrites a present one, so a
|
|
6
|
+
// user's hand-edited model list survives an upgrade. Both postures are correct.
|
|
7
|
+
// Their gap is the case where the registry did not ADD a model but RENAMED one:
|
|
8
|
+
// the saved row keeps a retired id forever, the supported id never appears, and
|
|
9
|
+
// the capability metadata stays keyed to an id the vendor is taking offline
|
|
10
|
+
// (issue #1610 — `qwen3.8-max-preview` persisted through the `qwen3.8-max`
|
|
11
|
+
// rename in six separate fields, including a reasoning ladder that had since
|
|
12
|
+
// diverged from the registry's).
|
|
13
|
+
//
|
|
14
|
+
// This migration is deliberately NOT general reconciliation. It rewrites exactly
|
|
15
|
+
// one thing: an id this file declares retired, on a provider that still carries
|
|
16
|
+
// the registry's transport, and only when the registry currently seeds the
|
|
17
|
+
// replacement. Everything else in the row is left alone.
|
|
18
|
+
|
|
19
|
+
import { PROVIDER_REGISTRY } from "./registry";
|
|
20
|
+
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
21
|
+
|
|
22
|
+
export interface ModelRename {
|
|
23
|
+
/** Registry provider id whose saved rows may carry the retired model id. */
|
|
24
|
+
provider: string;
|
|
25
|
+
from: string;
|
|
26
|
+
to: string;
|
|
27
|
+
/** Why the vendor retired it, for the startup warning and future readers. */
|
|
28
|
+
reason: string;
|
|
29
|
+
/**
|
|
30
|
+
* Drop the retired key from `modelReasoningEffortMap` instead of renaming it.
|
|
31
|
+
*
|
|
32
|
+
* Renaming preserves the VALUE, which is right for records whose values describe the
|
|
33
|
+
* model — a context window or an effort ladder survives a rename — and wrong for the
|
|
34
|
+
* one record whose values are themselves wire ids. An effort map saved as
|
|
35
|
+
* `high -> gemini-3.6-flash-high` would keep naming a dead wire id under the new key,
|
|
36
|
+
* and the adapter maps effort BEFORE resolving CCA routing, so that value arrives as
|
|
37
|
+
* an unrecognised effort and silently degrades to the default tier.
|
|
38
|
+
*
|
|
39
|
+
* Only that one record is dropped. Emptying the others would be worse than the bug:
|
|
40
|
+
* catalog enrichment treats an existing `{}` as "already populated" and will not
|
|
41
|
+
* restore the registry's records, so the migrated user would keep routing correctly
|
|
42
|
+
* but lose the reasoning picker entirely.
|
|
43
|
+
*/
|
|
44
|
+
dropReasoningEffortMap?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Renames already applied to `PROVIDER_REGISTRY`. An entry stays here after the
|
|
49
|
+
* registry moves on: it is what repairs configs saved before that move. Removing
|
|
50
|
+
* one strands every config that has not started since the rename shipped.
|
|
51
|
+
*/
|
|
52
|
+
export const MODEL_RENAMES: readonly ModelRename[] = [
|
|
53
|
+
{
|
|
54
|
+
provider: "alibaba-token-plan",
|
|
55
|
+
from: "qwen3.8-max-preview",
|
|
56
|
+
to: "qwen3.8-max",
|
|
57
|
+
reason: "Alibaba shipped Qwen3.8-Max as stable and documents the preview endpoint as liable to be taken offline once preview concludes",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
provider: "alibaba-token-plan-intl",
|
|
61
|
+
from: "qwen3.8-max-preview",
|
|
62
|
+
to: "qwen3.8-max",
|
|
63
|
+
reason: "Alibaba shipped Qwen3.8-Max as stable and documents the preview endpoint as liable to be taken offline once preview concludes",
|
|
64
|
+
},
|
|
65
|
+
// Antigravity Flash generations. Google takes the previous Flash model off Cloud Code
|
|
66
|
+
// Assist almost immediately when the next ships, so a saved 3.6 (or older 3.5) id is a
|
|
67
|
+
// dead selection rather than a merely outdated one. Routing already redirects these ids
|
|
68
|
+
// at request time; this migration repairs the saved config so the picker, the allowlist
|
|
69
|
+
// and the capability maps stop naming a model the backend no longer serves.
|
|
70
|
+
...(["gemini-3.6-flash", "gemini-3.6-flash-low", "gemini-3.6-flash-medium", "gemini-3.6-flash-high",
|
|
71
|
+
"gemini-3.5-flash-extra-low", "gemini-3.5-flash-low", "gemini-3.5-flash-mid", "gemini-3.5-flash-high",
|
|
72
|
+
"gemini-3-flash-agent"] as const).map(from => ({
|
|
73
|
+
provider: "google-antigravity",
|
|
74
|
+
from,
|
|
75
|
+
to: "gemini-3.7-flash",
|
|
76
|
+
reason: "Google retires the previous Antigravity Flash generation from Cloud Code Assist when its successor ships, so the saved id no longer resolves to a live model",
|
|
77
|
+
// The retired Flash tiers were wire ids, so any saved per-model record keyed by one
|
|
78
|
+
// may also hold one as a value. 3.7 expresses tiers as thinkingLevel names instead.
|
|
79
|
+
dropReasoningEffortMap: true,
|
|
80
|
+
})),
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
/** Provider fields that key metadata by model id. */
|
|
84
|
+
const MODEL_KEYED_RECORDS = [
|
|
85
|
+
"modelContextWindows",
|
|
86
|
+
"modelMaxOutputTokens",
|
|
87
|
+
"modelInputModalities",
|
|
88
|
+
"modelReasoningEfforts",
|
|
89
|
+
"modelDefaultReasoningEfforts",
|
|
90
|
+
"modelReasoningEffortMap",
|
|
91
|
+
] as const;
|
|
92
|
+
|
|
93
|
+
/** Provider fields that are flat lists of model ids. */
|
|
94
|
+
const MODEL_ID_LISTS = [
|
|
95
|
+
"models",
|
|
96
|
+
// A retired id left here is worse than a stale label: `filterCatalogModels` treats
|
|
97
|
+
// `selectedModels` as an exact-match allowlist, so a user who allowlisted only the
|
|
98
|
+
// retired model gets NO replacement row at all — the model silently vanishes from
|
|
99
|
+
// their catalog instead of being renamed. OAuth reconciliation does not cover this
|
|
100
|
+
// field, so the rename has to.
|
|
101
|
+
"selectedModels",
|
|
102
|
+
"noVisionModels",
|
|
103
|
+
"noReasoningModels",
|
|
104
|
+
"noTemperatureModels",
|
|
105
|
+
"noTopPModels",
|
|
106
|
+
"noPenaltyModels",
|
|
107
|
+
"autoToolChoiceOnlyModels",
|
|
108
|
+
"preserveReasoningContentModels",
|
|
109
|
+
"thinkingBudgetModels",
|
|
110
|
+
"directReasoningEffortModels",
|
|
111
|
+
] as const;
|
|
112
|
+
|
|
113
|
+
function renameInList(value: unknown, from: string, to: string): string[] | null {
|
|
114
|
+
if (!Array.isArray(value) || !value.includes(from)) return null;
|
|
115
|
+
const seen = new Set<string>();
|
|
116
|
+
const next: string[] = [];
|
|
117
|
+
// Rename in place to preserve ordering, and collapse a duplicate if the target
|
|
118
|
+
// id was already present alongside the retired one.
|
|
119
|
+
for (const entry of value) {
|
|
120
|
+
if (typeof entry !== "string") continue;
|
|
121
|
+
const mapped = entry === from ? to : entry;
|
|
122
|
+
if (seen.has(mapped)) continue;
|
|
123
|
+
seen.add(mapped);
|
|
124
|
+
next.push(mapped);
|
|
125
|
+
}
|
|
126
|
+
return next;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function renameInRecord(value: unknown, from: string, to: string): Record<string, unknown> | null {
|
|
130
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
131
|
+
const record = value as Record<string, unknown>;
|
|
132
|
+
if (!(from in record)) return null;
|
|
133
|
+
const next: Record<string, unknown> = {};
|
|
134
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
135
|
+
const mapped = key === from ? to : key;
|
|
136
|
+
if (mapped in next) continue;
|
|
137
|
+
// An explicit entry already saved under the new id is the newer intent.
|
|
138
|
+
next[mapped] = key === from && to in record ? record[to] : entry;
|
|
139
|
+
}
|
|
140
|
+
return next;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Drop the retired key entirely, leaving any entry already saved under the new id. */
|
|
144
|
+
function dropFromRecord(value: unknown, from: string): Record<string, unknown> | null {
|
|
145
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
146
|
+
const record = value as Record<string, unknown>;
|
|
147
|
+
if (!(from in record)) return null;
|
|
148
|
+
const next: Record<string, unknown> = {};
|
|
149
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
150
|
+
if (key === from) continue;
|
|
151
|
+
next[key] = entry;
|
|
152
|
+
}
|
|
153
|
+
return next;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* `provider/model` rows in the top-level `disabledModels` list.
|
|
158
|
+
*
|
|
159
|
+
* The retired row is DROPPED rather than renamed: carrying its disabled state to
|
|
160
|
+
* the new id would hide the supported model behind a toggle the user set for a
|
|
161
|
+
* different model. An existing row for the new id is left untouched.
|
|
162
|
+
*/
|
|
163
|
+
function renameDisabledModels(config: OcxConfig, rename: ModelRename): boolean {
|
|
164
|
+
const list = config.disabledModels;
|
|
165
|
+
if (!Array.isArray(list)) return false;
|
|
166
|
+
const retired = `${rename.provider}/${rename.from}`;
|
|
167
|
+
if (!list.includes(retired)) return false;
|
|
168
|
+
config.disabledModels = list.filter(entry => entry !== retired);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Only migrate a row that still points at the registry's own endpoint. A user who
|
|
174
|
+
* repointed `baseUrl` at a different vendor owns their model ids.
|
|
175
|
+
*/
|
|
176
|
+
function providerStillMatchesRegistry(name: string, prov: OcxProviderConfig): boolean {
|
|
177
|
+
const entry = PROVIDER_REGISTRY.find(row => row.id === name);
|
|
178
|
+
if (!entry) return false;
|
|
179
|
+
if (!prov.baseUrl || !entry.baseUrl) return true;
|
|
180
|
+
const choices = entry.baseUrlChoices?.map(choice => choice.baseUrl) ?? [];
|
|
181
|
+
const known = [entry.baseUrl, ...choices]
|
|
182
|
+
.filter((url): url is string => typeof url === "string")
|
|
183
|
+
.map(url => url.replace(/\/+$/, ""));
|
|
184
|
+
return known.includes(prov.baseUrl.replace(/\/+$/, ""));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Guard against a stale rename: only apply when the registry actually seeds `to`. */
|
|
188
|
+
function registrySeedsTarget(rename: ModelRename): boolean {
|
|
189
|
+
const entry = PROVIDER_REGISTRY.find(row => row.id === rename.provider);
|
|
190
|
+
return !!entry?.models?.includes(rename.to);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface ModelRenameProjection {
|
|
194
|
+
config: OcxConfig;
|
|
195
|
+
changed: boolean;
|
|
196
|
+
warnings: string[];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Pure projection: apply every applicable rename and report what changed. The
|
|
201
|
+
* caller decides whether to persist.
|
|
202
|
+
*/
|
|
203
|
+
export function projectModelRenames(
|
|
204
|
+
config: OcxConfig,
|
|
205
|
+
renames: readonly ModelRename[] = MODEL_RENAMES,
|
|
206
|
+
): ModelRenameProjection {
|
|
207
|
+
const warnings: string[] = [];
|
|
208
|
+
let changed = false;
|
|
209
|
+
|
|
210
|
+
for (const rename of renames) {
|
|
211
|
+
const prov = config.providers?.[rename.provider];
|
|
212
|
+
if (!prov) continue;
|
|
213
|
+
if (!registrySeedsTarget(rename)) {
|
|
214
|
+
warnings.push(
|
|
215
|
+
`registry no longer seeds "${rename.to}" for "${rename.provider}"; skipping the `
|
|
216
|
+
+ `"${rename.from}" rename rather than writing an id the registry does not know.`,
|
|
217
|
+
);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (!providerStillMatchesRegistry(rename.provider, prov)) continue;
|
|
221
|
+
|
|
222
|
+
// Provider config is a closed interface, so index through one unknown-cast
|
|
223
|
+
// view rather than casting at each assignment.
|
|
224
|
+
const row = prov as unknown as Record<string, unknown>;
|
|
225
|
+
let touched = false;
|
|
226
|
+
for (const field of MODEL_ID_LISTS) {
|
|
227
|
+
const next = renameInList(row[field], rename.from, rename.to);
|
|
228
|
+
if (!next) continue;
|
|
229
|
+
row[field] = next;
|
|
230
|
+
touched = true;
|
|
231
|
+
}
|
|
232
|
+
for (const field of MODEL_KEYED_RECORDS) {
|
|
233
|
+
const next = rename.dropReasoningEffortMap && field === "modelReasoningEffortMap"
|
|
234
|
+
? dropFromRecord(row[field], rename.from)
|
|
235
|
+
: renameInRecord(row[field], rename.from, rename.to);
|
|
236
|
+
if (!next) continue;
|
|
237
|
+
row[field] = next;
|
|
238
|
+
touched = true;
|
|
239
|
+
}
|
|
240
|
+
if (prov.defaultModel === rename.from) {
|
|
241
|
+
prov.defaultModel = rename.to;
|
|
242
|
+
touched = true;
|
|
243
|
+
}
|
|
244
|
+
if (renameDisabledModels(config, rename)) touched = true;
|
|
245
|
+
|
|
246
|
+
if (touched) {
|
|
247
|
+
changed = true;
|
|
248
|
+
warnings.push(
|
|
249
|
+
`renamed "${rename.provider}/${rename.from}" to "${rename.to}" in the saved config: ${rename.reason}.`,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return { config, changed, warnings };
|
|
255
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { saveConfig } from "../config";
|
|
2
|
+
import { projectModelRenames } from "./model-rename-migration";
|
|
3
|
+
import type { OcxConfig } from "../types";
|
|
4
|
+
|
|
5
|
+
export interface ModelRenameStartupDeps {
|
|
6
|
+
project: typeof projectModelRenames;
|
|
7
|
+
save: (config: OcxConfig) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Apply registry model renames to the saved config at startup (issue #1610).
|
|
12
|
+
*
|
|
13
|
+
* No backup is taken, unlike the OpenAI tier and Alibaba region migrations: those
|
|
14
|
+
* rewrite credentials and provider identity, where a bad projection is not
|
|
15
|
+
* recoverable from the config alone. This one only rewrites model ids that the
|
|
16
|
+
* registry itself no longer seeds, and the pre-migration value is a string this
|
|
17
|
+
* file still names, so the change is reversible by hand.
|
|
18
|
+
*/
|
|
19
|
+
export function runModelRenameStartupMigration(
|
|
20
|
+
config: OcxConfig,
|
|
21
|
+
deps: ModelRenameStartupDeps = { project: projectModelRenames, save: saveConfig },
|
|
22
|
+
): OcxConfig {
|
|
23
|
+
const projection = deps.project(config);
|
|
24
|
+
for (const warning of projection.warnings) console.warn(`[model-rename-migration] ${warning}`);
|
|
25
|
+
if (!projection.changed) return projection.config;
|
|
26
|
+
deps.save(projection.config);
|
|
27
|
+
return projection.config;
|
|
28
|
+
}
|