@iislee/opencodex 2.11.0 → 2.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/README.md +117 -22
- package/bin/ocx.mjs +245 -88
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +21 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +475 -62
- package/src/adapters/base.ts +82 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +637 -0
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +308 -0
- package/src/adapters/cursor/cursor-errors.ts +144 -6
- package/src/adapters/cursor/discovery.ts +122 -14
- package/src/adapters/cursor/effort-map.ts +106 -4
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +180 -59
- package/src/adapters/cursor/live-transport.ts +623 -170
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -3
- package/src/adapters/cursor/native-exec.ts +121 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +447 -75
- package/src/adapters/cursor/request-builder.ts +271 -35
- package/src/adapters/cursor/tool-definitions.ts +252 -12
- package/src/adapters/cursor/tool-result-normalize.ts +115 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +48 -1
- package/src/adapters/cursor.ts +336 -39
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +713 -41
- package/src/adapters/google-antigravity-wire.ts +48 -10
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +742 -86
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +65 -17
- package/src/adapters/kiro.ts +270 -73
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1377 -344
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1164 -64
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +105 -21
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +186 -0
- package/src/bridge.ts +512 -92
- package/src/chat/inbound.ts +57 -20
- package/src/chat/outbound.ts +97 -33
- package/src/claude/agents-inject.ts +40 -10
- package/src/claude/context-windows.ts +37 -14
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +88 -21
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +150 -19
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +648 -28
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +97 -19
- package/src/cli/agent.ts +53 -2
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +60 -15
- package/src/cli/claude.ts +129 -35
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +821 -0
- package/src/cli/doctor.ts +440 -56
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +34 -274
- package/src/cli/index.ts +417 -528
- package/src/cli/init.ts +5 -17
- package/src/cli/inspect.ts +230 -0
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +130 -2
- package/src/cli/models.ts +118 -16
- package/src/cli/observe.ts +123 -11
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +34 -3
- package/src/cli/provider.ts +25 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +476 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +57 -10
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +51 -4
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +137 -18
- package/src/cli/version-skew.ts +46 -0
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1442 -23
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +35 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +84 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +570 -107
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +893 -246
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +348 -32
- package/src/codex/autostart-health.ts +24 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +86 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +138 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +555 -45
- package/src/codex/catalog/native-models.ts +75 -0
- package/src/codex/catalog/parsing.ts +269 -30
- package/src/codex/catalog/provider-fetch.ts +1626 -144
- package/src/codex/catalog/sync.ts +1576 -187
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +386 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +676 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +436 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +30 -24
- package/src/codex/history-provider.ts +1016 -235
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +223 -0
- package/src/codex/inject-coordination.ts +373 -0
- package/src/codex/inject.ts +1114 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +81 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +524 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +360 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +622 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +675 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +1313 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +264 -21
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +514 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1065 -31
- package/src/codex/subagent-model-fallback.ts +437 -43
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +720 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +47 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +91 -9
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +1713 -709
- package/src/generated/compatibility-version.json +3324 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +157 -33
- package/src/images/plan.ts +24 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +155 -0
- package/src/integrations/registry.ts +166 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +433 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +715 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +167 -11
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +26 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +80 -5
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +6 -2
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +253 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +30 -4
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +462 -71
- package/src/oauth/key-providers.ts +25 -0
- package/src/oauth/kimi.ts +25 -3
- package/src/oauth/kiro.ts +95 -6
- package/src/oauth/local-token-detect.ts +13 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +133 -26
- package/src/oauth/token-guardian.ts +9 -3
- package/src/oauth/types.ts +15 -0
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +176 -0
- package/src/providers/context-cap.ts +26 -8
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +281 -6
- package/src/providers/fastwire.ts +507 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +35 -2
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +30 -0
- package/src/providers/quota.ts +1379 -40
- package/src/providers/registry.ts +1545 -112
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +94 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +32 -4
- package/src/reasoning-effort.ts +68 -3
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +384 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +435 -0
- package/src/responses/parser.ts +225 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +504 -0
- package/src/responses/schema.ts +12 -2
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +659 -31
- package/src/responses/thought-signature-replay.ts +364 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +405 -31
- package/src/routing/analytics.ts +379 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +155 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +289 -48
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +154 -72
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +445 -0
- package/src/server/claude-messages.ts +160 -44
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +1021 -190
- package/src/server/lifecycle.ts +336 -14
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +433 -116
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +64 -25
- package/src/server/management/config-routes.ts +511 -51
- package/src/server/management/context.ts +89 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +127 -52
- package/src/server/management/model-routes.ts +461 -133
- package/src/server/management/model-rows.ts +167 -0
- package/src/server/management/native-integration-routes.ts +770 -0
- package/src/server/management/oauth-account-routes.ts +90 -8
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +836 -159
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +289 -33
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +126 -23
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +161 -10
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +221 -54
- package/src/server/relay.ts +479 -87
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +298 -31
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +239 -36
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +444 -72
- package/src/server/responses/core.ts +3579 -436
- package/src/server/responses/empty-completion-guard.ts +333 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +89 -99
- package/src/server/responses/input-admission.ts +187 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +343 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +381 -0
- package/src/server/responses-custom-tool-repair.ts +363 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +363 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +376 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +319 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +53 -40
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1153 -94
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +911 -0
- package/src/types/provider.ts +614 -0
- package/src/types/request.ts +397 -0
- package/src/types/tools.ts +236 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +107 -1237
- package/src/update/index.ts +37 -23
- package/src/update/job.ts +445 -68
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +266 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +287 -16
- package/src/usage/log.ts +666 -50
- package/src/usage/summary.ts +388 -17
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +27 -16
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +119 -0
- package/src/web-search/executor.ts +21 -6
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +284 -54
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source-backed routing analytics (RI-03).
|
|
3
|
+
*
|
|
4
|
+
* All metrics derive from the rebuildable request-history index
|
|
5
|
+
* (`routing-history.sqlite`), never from repeated full JSONL scans. The
|
|
6
|
+
* analysis is read-only: no routing decision, profile, or weight changes here
|
|
7
|
+
* (ADR-10 - no automatic self-tuning).
|
|
8
|
+
*
|
|
9
|
+
* Bounds: at most ANALYTICS_MAX_ROWS matching rows are analyzed per call; a
|
|
10
|
+
* larger population sets `historyTruncated: true` so readers never mistake a
|
|
11
|
+
* sample for the full history.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { PersistedUsageEntry } from "../usage/log";
|
|
15
|
+
import { estimateRequestCost, serviceTierContext } from "../usage/cost";
|
|
16
|
+
import { openRequestHistoryIndex, requestHistoryDb } from "./history/indexer";
|
|
17
|
+
|
|
18
|
+
export const ANALYTICS_MAX_ROWS = 50_000;
|
|
19
|
+
/** Default row cap for the management API (full cap remains available via `limit`). */
|
|
20
|
+
export const ANALYTICS_API_DEFAULT_ROWS = 5_000;
|
|
21
|
+
|
|
22
|
+
export interface RoutingAnalyticsFilters {
|
|
23
|
+
provider?: string;
|
|
24
|
+
model?: string;
|
|
25
|
+
profileId?: string;
|
|
26
|
+
surface?: string;
|
|
27
|
+
from?: number;
|
|
28
|
+
to?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type AnalyticsConfidence = "high" | "medium" | "low";
|
|
32
|
+
|
|
33
|
+
export interface AnalyticsBreakdownRow {
|
|
34
|
+
provider: string;
|
|
35
|
+
model: string;
|
|
36
|
+
accountRef?: string;
|
|
37
|
+
profileId?: string;
|
|
38
|
+
requests: number;
|
|
39
|
+
successes: number;
|
|
40
|
+
failures: number;
|
|
41
|
+
cancelled: number;
|
|
42
|
+
successRate: number | null;
|
|
43
|
+
p50DurationMs?: number;
|
|
44
|
+
estimatedCostUsdPerSuccessfulRequest?: number | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AnalyticsProfileRow {
|
|
48
|
+
profileId: string;
|
|
49
|
+
profileRevision?: string;
|
|
50
|
+
requests: number;
|
|
51
|
+
successes: number;
|
|
52
|
+
failures: number;
|
|
53
|
+
fallbacks: number;
|
|
54
|
+
successRate: number | null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface RoutingAnalyticsResult {
|
|
58
|
+
generatedAt: number;
|
|
59
|
+
totalRequests: number;
|
|
60
|
+
scannedRows: number;
|
|
61
|
+
historyTruncated: boolean;
|
|
62
|
+
confidence: AnalyticsConfidence | null;
|
|
63
|
+
successRate: number | null;
|
|
64
|
+
failureRate: number | null;
|
|
65
|
+
cancelledRate: number | null;
|
|
66
|
+
fallbackRate: number | null;
|
|
67
|
+
totalAttempts: number;
|
|
68
|
+
averageAttemptsPerRequest: number | null;
|
|
69
|
+
incompleteStreamRate: number | null;
|
|
70
|
+
cooldownTriggeringFailures: number;
|
|
71
|
+
durationMs: {
|
|
72
|
+
p50?: number;
|
|
73
|
+
p95?: number;
|
|
74
|
+
p99?: number;
|
|
75
|
+
sampleCount: number;
|
|
76
|
+
};
|
|
77
|
+
firstOutputMs: {
|
|
78
|
+
p50?: number;
|
|
79
|
+
p95?: number;
|
|
80
|
+
p99?: number;
|
|
81
|
+
sampleCount: number;
|
|
82
|
+
/** Share of scanned requests with a TTFT measurement (0..1). */
|
|
83
|
+
coverage: number | null;
|
|
84
|
+
};
|
|
85
|
+
estimatedCostUsdPerSuccessfulRequest: number | null;
|
|
86
|
+
estimatedCostUsdTotalSuccessful: number | null;
|
|
87
|
+
usageCoverage: number | null;
|
|
88
|
+
priceCoverage: number | null;
|
|
89
|
+
breakdown: AnalyticsBreakdownRow[];
|
|
90
|
+
profileBreakdown: AnalyticsProfileRow[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface ScannedRow {
|
|
94
|
+
provider: string;
|
|
95
|
+
model: string;
|
|
96
|
+
apiKeyId?: string | null;
|
|
97
|
+
profileId?: string | null;
|
|
98
|
+
profileRevision?: string | null;
|
|
99
|
+
status: number;
|
|
100
|
+
durationMs: number;
|
|
101
|
+
firstOutputMs?: number | null;
|
|
102
|
+
closeReason?: string | null;
|
|
103
|
+
terminalStatus?: string | null;
|
|
104
|
+
usageStatus: string;
|
|
105
|
+
usageJson?: string | null;
|
|
106
|
+
attemptCount: number;
|
|
107
|
+
fallback: number;
|
|
108
|
+
rowJson: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface Bucket extends AnalyticsBreakdownRow {
|
|
112
|
+
durations: number[];
|
|
113
|
+
costUsdSum: number;
|
|
114
|
+
costRows: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const COOLDOWN_RECOVERY_KINDS = new Set([
|
|
118
|
+
"rate-limit-429",
|
|
119
|
+
"key-429",
|
|
120
|
+
"oauth-401",
|
|
121
|
+
"anthropic-oauth-429",
|
|
122
|
+
"oauth-account-429",
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
function percentile(sorted: number[], p: number): number | undefined {
|
|
126
|
+
if (sorted.length === 0) return undefined;
|
|
127
|
+
const index = Math.max(0, Math.ceil((p / 100) * sorted.length) - 1);
|
|
128
|
+
return sorted[Math.min(index, sorted.length - 1)];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function classifyRow(row: ScannedRow): "success" | "failure" | "cancelled" {
|
|
132
|
+
if (row.closeReason === "client_cancel" || row.status === 499) return "cancelled";
|
|
133
|
+
if (row.terminalStatus === "incomplete") return "failure";
|
|
134
|
+
if (row.terminalStatus && row.terminalStatus !== "completed") return "failure";
|
|
135
|
+
if (row.status >= 400) return "failure";
|
|
136
|
+
return "success";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function parseEntry(rowJson: string): PersistedUsageEntry | null {
|
|
140
|
+
try {
|
|
141
|
+
const parsed = JSON.parse(rowJson) as PersistedUsageEntry;
|
|
142
|
+
return parsed && typeof parsed === "object" && typeof parsed.requestId === "string" ? parsed : null;
|
|
143
|
+
} catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function cooldownTriggering(entry: PersistedUsageEntry | null, status: number): boolean {
|
|
149
|
+
if (status === 429) return true;
|
|
150
|
+
if (!Array.isArray(entry?.attempts)) return false;
|
|
151
|
+
return entry.attempts.some((attempt: unknown) => {
|
|
152
|
+
if (!attempt || typeof attempt !== "object") return false;
|
|
153
|
+
const recoveryKinds = (attempt as { recoveryKinds?: unknown }).recoveryKinds;
|
|
154
|
+
return Array.isArray(recoveryKinds)
|
|
155
|
+
&& recoveryKinds.some(kind => typeof kind === "string" && COOLDOWN_RECOVERY_KINDS.has(kind));
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function successCostUsd(
|
|
160
|
+
row: Pick<ScannedRow, "provider" | "model">,
|
|
161
|
+
entry: PersistedUsageEntry,
|
|
162
|
+
): number | null {
|
|
163
|
+
if (!entry.usage) return null;
|
|
164
|
+
const estimate = estimateRequestCost({
|
|
165
|
+
provider: row.provider,
|
|
166
|
+
model: row.model,
|
|
167
|
+
usage: entry.usage,
|
|
168
|
+
usageStatus: entry.usageStatus,
|
|
169
|
+
serviceTier: serviceTierContext(entry),
|
|
170
|
+
});
|
|
171
|
+
return estimate ? estimate.cost.total : null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function computeRoutingAnalytics(
|
|
175
|
+
filters: RoutingAnalyticsFilters,
|
|
176
|
+
options: { maxRows?: number } = {},
|
|
177
|
+
): Promise<RoutingAnalyticsResult> {
|
|
178
|
+
await openRequestHistoryIndex();
|
|
179
|
+
const handle = requestHistoryDb();
|
|
180
|
+
const maxRows = Math.min(
|
|
181
|
+
Math.max(1, Math.trunc(options.maxRows ?? ANALYTICS_MAX_ROWS)),
|
|
182
|
+
ANALYTICS_MAX_ROWS,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const where: string[] = [];
|
|
186
|
+
const values: Array<string | number> = [];
|
|
187
|
+
const add = (clause: string, value: string | number) => {
|
|
188
|
+
where.push(clause);
|
|
189
|
+
values.push(value);
|
|
190
|
+
};
|
|
191
|
+
if (filters.provider !== undefined) add("provider = ?", filters.provider);
|
|
192
|
+
if (filters.model !== undefined) add("model = ?", filters.model);
|
|
193
|
+
if (filters.profileId !== undefined) add("profile_id = ?", filters.profileId);
|
|
194
|
+
if (filters.surface !== undefined) add("surface = ?", filters.surface);
|
|
195
|
+
if (filters.from !== undefined) add("timestamp >= ?", filters.from);
|
|
196
|
+
if (filters.to !== undefined) add("timestamp <= ?", filters.to);
|
|
197
|
+
const whereSql = where.length > 0 ? ` WHERE ${where.join(" AND ")}` : "";
|
|
198
|
+
|
|
199
|
+
const rows = handle.query(
|
|
200
|
+
`SELECT provider, model, api_key_id AS apiKeyId, profile_id AS profileId,
|
|
201
|
+
profile_revision AS profileRevision, status,
|
|
202
|
+
duration_ms AS durationMs, first_output_ms AS firstOutputMs,
|
|
203
|
+
close_reason AS closeReason, terminal_status AS terminalStatus,
|
|
204
|
+
usage_status AS usageStatus, usage_json AS usageJson,
|
|
205
|
+
attempt_count AS attemptCount, fallback, row_json AS rowJson
|
|
206
|
+
FROM requests${whereSql} ORDER BY timestamp DESC LIMIT ?`,
|
|
207
|
+
).all(...values, maxRows + 1) as ScannedRow[];
|
|
208
|
+
|
|
209
|
+
const scanned = rows.slice(0, maxRows);
|
|
210
|
+
const historyTruncated = rows.length > maxRows;
|
|
211
|
+
|
|
212
|
+
let successes = 0;
|
|
213
|
+
let failures = 0;
|
|
214
|
+
let cancelled = 0;
|
|
215
|
+
let fallbacks = 0;
|
|
216
|
+
let totalAttempts = 0;
|
|
217
|
+
let incompleteStreams = 0;
|
|
218
|
+
let cooldownFailures = 0;
|
|
219
|
+
let usageReported = 0;
|
|
220
|
+
const durations: number[] = [];
|
|
221
|
+
const firstOutputs: number[] = [];
|
|
222
|
+
let costTotalUsd = 0;
|
|
223
|
+
let costCount = 0;
|
|
224
|
+
|
|
225
|
+
const byKey = new Map<string, Bucket>();
|
|
226
|
+
const byProfile = new Map<string, AnalyticsProfileRow>();
|
|
227
|
+
|
|
228
|
+
for (const row of scanned) {
|
|
229
|
+
const kind = classifyRow(row);
|
|
230
|
+
if (kind === "success") successes += 1;
|
|
231
|
+
else if (kind === "failure") failures += 1;
|
|
232
|
+
else cancelled += 1;
|
|
233
|
+
if (row.fallback === 1) fallbacks += 1;
|
|
234
|
+
totalAttempts += row.attemptCount;
|
|
235
|
+
if (row.terminalStatus === "incomplete") incompleteStreams += 1;
|
|
236
|
+
durations.push(row.durationMs);
|
|
237
|
+
if (row.firstOutputMs !== null && row.firstOutputMs !== undefined && row.firstOutputMs >= 0) {
|
|
238
|
+
firstOutputs.push(row.firstOutputMs);
|
|
239
|
+
}
|
|
240
|
+
if (row.usageStatus !== "unreported") usageReported += 1;
|
|
241
|
+
|
|
242
|
+
let rowCostUsd: number | null = null;
|
|
243
|
+
if (kind === "success") {
|
|
244
|
+
const entry = parseEntry(row.rowJson);
|
|
245
|
+
if (entry) {
|
|
246
|
+
rowCostUsd = successCostUsd(row, entry);
|
|
247
|
+
if (rowCostUsd !== null) {
|
|
248
|
+
costTotalUsd += rowCostUsd;
|
|
249
|
+
costCount += 1;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (kind === "failure") {
|
|
255
|
+
const failureEntry = parseEntry(row.rowJson);
|
|
256
|
+
if (cooldownTriggering(failureEntry, row.status)) cooldownFailures += 1;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const key = `${row.provider}\0${row.model}\0${row.apiKeyId ?? ""}\0${row.profileId ?? ""}`;
|
|
260
|
+
let bucket: Bucket | undefined = byKey.get(key);
|
|
261
|
+
if (!bucket) {
|
|
262
|
+
bucket = {
|
|
263
|
+
provider: row.provider,
|
|
264
|
+
model: row.model,
|
|
265
|
+
...(row.apiKeyId ? { accountRef: row.apiKeyId } : {}),
|
|
266
|
+
...(row.profileId ? { profileId: row.profileId } : {}),
|
|
267
|
+
requests: 0,
|
|
268
|
+
successes: 0,
|
|
269
|
+
failures: 0,
|
|
270
|
+
cancelled: 0,
|
|
271
|
+
successRate: null,
|
|
272
|
+
durations: [],
|
|
273
|
+
costUsdSum: 0,
|
|
274
|
+
costRows: 0,
|
|
275
|
+
};
|
|
276
|
+
byKey.set(key, bucket);
|
|
277
|
+
}
|
|
278
|
+
bucket.requests += 1;
|
|
279
|
+
if (kind === "success") bucket.successes += 1;
|
|
280
|
+
else if (kind === "failure") bucket.failures += 1;
|
|
281
|
+
else bucket.cancelled += 1;
|
|
282
|
+
bucket.durations.push(row.durationMs);
|
|
283
|
+
if (rowCostUsd !== null) {
|
|
284
|
+
bucket.costUsdSum += rowCostUsd;
|
|
285
|
+
bucket.costRows += 1;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (row.profileId) {
|
|
289
|
+
let profile = byProfile.get(row.profileId);
|
|
290
|
+
if (!profile) {
|
|
291
|
+
profile = {
|
|
292
|
+
profileId: row.profileId,
|
|
293
|
+
...(row.profileRevision ? { profileRevision: row.profileRevision } : {}),
|
|
294
|
+
requests: 0,
|
|
295
|
+
successes: 0,
|
|
296
|
+
failures: 0,
|
|
297
|
+
fallbacks: 0,
|
|
298
|
+
successRate: null,
|
|
299
|
+
};
|
|
300
|
+
byProfile.set(row.profileId, profile);
|
|
301
|
+
}
|
|
302
|
+
profile.requests += 1;
|
|
303
|
+
if (kind === "success") profile.successes += 1;
|
|
304
|
+
else if (kind === "failure") profile.failures += 1;
|
|
305
|
+
if (row.fallback === 1) profile.fallbacks += 1;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
durations.sort((a, b) => a - b);
|
|
310
|
+
firstOutputs.sort((a, b) => a - b);
|
|
311
|
+
const total = scanned.length;
|
|
312
|
+
const rate = (count: number): number | null => (total > 0 ? count / total : null);
|
|
313
|
+
|
|
314
|
+
const breakdown: AnalyticsBreakdownRow[] = [...byKey.values()].map(bucket => {
|
|
315
|
+
const sorted = bucket.durations.sort((a, b) => a - b);
|
|
316
|
+
const p50DurationMs = percentile(sorted, 50);
|
|
317
|
+
return {
|
|
318
|
+
provider: bucket.provider,
|
|
319
|
+
model: bucket.model,
|
|
320
|
+
...(bucket.accountRef ? { accountRef: bucket.accountRef } : {}),
|
|
321
|
+
...(bucket.profileId ? { profileId: bucket.profileId } : {}),
|
|
322
|
+
requests: bucket.requests,
|
|
323
|
+
successes: bucket.successes,
|
|
324
|
+
failures: bucket.failures,
|
|
325
|
+
cancelled: bucket.cancelled,
|
|
326
|
+
successRate: bucket.requests > 0 ? bucket.successes / bucket.requests : null,
|
|
327
|
+
...(p50DurationMs !== undefined ? { p50DurationMs } : {}),
|
|
328
|
+
...(bucket.requests > 0
|
|
329
|
+
? { estimatedCostUsdPerSuccessfulRequest: bucket.costRows > 0
|
|
330
|
+
? bucket.costUsdSum / bucket.costRows
|
|
331
|
+
: null }
|
|
332
|
+
: {}),
|
|
333
|
+
};
|
|
334
|
+
}).sort((a, b) => b.requests - a.requests);
|
|
335
|
+
|
|
336
|
+
const profileBreakdown: AnalyticsProfileRow[] = [...byProfile.values()].map(profile => ({
|
|
337
|
+
...profile,
|
|
338
|
+
successRate: profile.requests > 0 ? profile.successes / profile.requests : null,
|
|
339
|
+
})).sort((a, b) => b.requests - a.requests);
|
|
340
|
+
|
|
341
|
+
const confidence: AnalyticsConfidence | null = total === 0
|
|
342
|
+
? null
|
|
343
|
+
: total >= 100 ? "high" : total >= 20 ? "medium" : "low";
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
generatedAt: Date.now(),
|
|
347
|
+
totalRequests: total,
|
|
348
|
+
scannedRows: scanned.length,
|
|
349
|
+
historyTruncated,
|
|
350
|
+
confidence,
|
|
351
|
+
successRate: rate(successes),
|
|
352
|
+
failureRate: rate(failures),
|
|
353
|
+
cancelledRate: rate(cancelled),
|
|
354
|
+
fallbackRate: rate(fallbacks),
|
|
355
|
+
totalAttempts,
|
|
356
|
+
averageAttemptsPerRequest: total > 0 ? totalAttempts / total : null,
|
|
357
|
+
incompleteStreamRate: rate(incompleteStreams),
|
|
358
|
+
cooldownTriggeringFailures: cooldownFailures,
|
|
359
|
+
durationMs: {
|
|
360
|
+
...(percentile(durations, 50) !== undefined ? { p50: percentile(durations, 50) } : {}),
|
|
361
|
+
...(percentile(durations, 95) !== undefined ? { p95: percentile(durations, 95) } : {}),
|
|
362
|
+
...(percentile(durations, 99) !== undefined ? { p99: percentile(durations, 99) } : {}),
|
|
363
|
+
sampleCount: durations.length,
|
|
364
|
+
},
|
|
365
|
+
firstOutputMs: {
|
|
366
|
+
...(percentile(firstOutputs, 50) !== undefined ? { p50: percentile(firstOutputs, 50) } : {}),
|
|
367
|
+
...(percentile(firstOutputs, 95) !== undefined ? { p95: percentile(firstOutputs, 95) } : {}),
|
|
368
|
+
...(percentile(firstOutputs, 99) !== undefined ? { p99: percentile(firstOutputs, 99) } : {}),
|
|
369
|
+
sampleCount: firstOutputs.length,
|
|
370
|
+
coverage: total > 0 ? firstOutputs.length / total : null,
|
|
371
|
+
},
|
|
372
|
+
estimatedCostUsdPerSuccessfulRequest: costCount > 0 ? costTotalUsd / costCount : null,
|
|
373
|
+
estimatedCostUsdTotalSuccessful: costCount > 0 ? costTotalUsd : null,
|
|
374
|
+
usageCoverage: total > 0 ? usageReported / total : null,
|
|
375
|
+
priceCoverage: successes > 0 ? costCount / successes : null,
|
|
376
|
+
breakdown,
|
|
377
|
+
profileBreakdown,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Candidate capability evidence for policy routing (RI-05).
|
|
3
|
+
*
|
|
4
|
+
* Evidence comes from canonical local sources only - provider config maps,
|
|
5
|
+
* the provider registry, the cached Codex catalog file, and the native-model
|
|
6
|
+
* metadata helpers. No live network fetch happens at routing time.
|
|
7
|
+
*
|
|
8
|
+
* "Unknown is not zero": any dimension without canonical evidence stays
|
|
9
|
+
* `undefined` (unknown) and the profile's `unknownEvidence` policy decides
|
|
10
|
+
* how that affects eligibility.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { modelInList, type OcxConfig } from "../types";
|
|
14
|
+
import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../providers/openai-tiers";
|
|
15
|
+
import { serviceTierSupportForModel } from "../providers/service-tier";
|
|
16
|
+
import { PROVIDER_REGISTRY } from "../providers/registry";
|
|
17
|
+
import {
|
|
18
|
+
nativeInputModalities,
|
|
19
|
+
nativeContextLimits, nativeOpenAiContextWindow,
|
|
20
|
+
nativeParallelToolCalls,
|
|
21
|
+
nativeReasoningEfforts,
|
|
22
|
+
} from "../codex/catalog/metadata";
|
|
23
|
+
import { readCatalog, readCodexCatalogPath } from "../codex/catalog/parsing";
|
|
24
|
+
import { modelRecordValue } from "../reasoning-effort";
|
|
25
|
+
import { statSync } from "node:fs";
|
|
26
|
+
import type { RouteCapabilityEvidence } from "./trace";
|
|
27
|
+
|
|
28
|
+
type CatalogModelRow = {
|
|
29
|
+
/** Exact provider/native-id identity, from the provenance block. */
|
|
30
|
+
provider: string;
|
|
31
|
+
id: string;
|
|
32
|
+
/** Only values a real source asserted; never a strict-parser default. */
|
|
33
|
+
contextWindow?: number;
|
|
34
|
+
inputModalities?: string[];
|
|
35
|
+
capabilities?: string[];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Catalog rows memoized by path + mtime: the cached Codex catalog is stable
|
|
40
|
+
* between refreshes, and re-reading/parsing the whole file per candidate on
|
|
41
|
+
* the request path would multiply a synchronous disk + JSON cost by the
|
|
42
|
+
* profile candidate count for every policy-routed request.
|
|
43
|
+
*/
|
|
44
|
+
let catalogCache: { path: string; mtimeMs: number; rows: CatalogModelRow[] } | null = null;
|
|
45
|
+
|
|
46
|
+
function cachedCatalogModels(): CatalogModelRow[] {
|
|
47
|
+
try {
|
|
48
|
+
const path = readCodexCatalogPath();
|
|
49
|
+
const mtimeMs = statSync(path).mtimeMs;
|
|
50
|
+
if (catalogCache && catalogCache.path === path && catalogCache.mtimeMs === mtimeMs) {
|
|
51
|
+
return catalogCache.rows;
|
|
52
|
+
}
|
|
53
|
+
const catalog = readCatalog(path);
|
|
54
|
+
const models = catalog?.models;
|
|
55
|
+
if (!Array.isArray(models)) return [];
|
|
56
|
+
// Read ONLY `opencodex_capability_provenance` (written by
|
|
57
|
+
// applyCatalogModelMetadata). The row's own `context_window` and
|
|
58
|
+
// `input_modalities` always exist because ensureStrictCatalogFields fills them
|
|
59
|
+
// with compatibility defaults for Codex's strict parser, so reading them would
|
|
60
|
+
// turn "nobody asserted anything" into a confident `image: false` and a
|
|
61
|
+
// fabricated 128000 — the opposite of this module's contract. A row without
|
|
62
|
+
// provenance contributes nothing.
|
|
63
|
+
const rows = models.flatMap((model): CatalogModelRow[] => {
|
|
64
|
+
if (typeof model !== "object" || model === null) return [];
|
|
65
|
+
const provenance = (model as Record<string, unknown>).opencodex_capability_provenance;
|
|
66
|
+
if (typeof provenance !== "object" || provenance === null) return [];
|
|
67
|
+
const source = provenance as Record<string, unknown>;
|
|
68
|
+
if (typeof source.provider !== "string" || typeof source.model_id !== "string") return [];
|
|
69
|
+
return [{
|
|
70
|
+
provider: source.provider,
|
|
71
|
+
id: source.model_id,
|
|
72
|
+
...(typeof source.context_window === "number" && source.context_window > 0
|
|
73
|
+
? { contextWindow: source.context_window }
|
|
74
|
+
: {}),
|
|
75
|
+
...(Array.isArray(source.input_modalities)
|
|
76
|
+
? { inputModalities: source.input_modalities.filter((value): value is string => typeof value === "string") }
|
|
77
|
+
: {}),
|
|
78
|
+
...(Array.isArray(source.capabilities)
|
|
79
|
+
? { capabilities: source.capabilities.filter((value): value is string => typeof value === "string") }
|
|
80
|
+
: {}),
|
|
81
|
+
}];
|
|
82
|
+
});
|
|
83
|
+
catalogCache = { path, mtimeMs, rows };
|
|
84
|
+
return rows;
|
|
85
|
+
} catch {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Classify a hostname for locality evidence. `URL.hostname` keeps IPv6
|
|
92
|
+
* literals bracketed (`[::1]`), so strip the brackets before matching.
|
|
93
|
+
* Anything not positively local or private stays unknown: "unknown is not
|
|
94
|
+
* zero", so an unrecognized host must never assert `remoteAllowed`.
|
|
95
|
+
*/
|
|
96
|
+
function classifyHostname(hostname: string): "local" | "private" | null {
|
|
97
|
+
const host = hostname.trim().toLowerCase().replace(/\.$/, "").replace(/^\[|\]$/g, "");
|
|
98
|
+
if (host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0") return "local";
|
|
99
|
+
if (host === "::1" || /^127\./.test(host)) return "local";
|
|
100
|
+
if (/^10\./.test(host)
|
|
101
|
+
|| /^192\.168\./.test(host)
|
|
102
|
+
|| /^169\.254\./.test(host)
|
|
103
|
+
|| /^172\.(1[6-9]|2\d|3[01])\./.test(host)
|
|
104
|
+
|| /^f[cd][0-9a-f]{2}:/.test(host)
|
|
105
|
+
|| /^fe80:/.test(host)
|
|
106
|
+
|| /^::ffff:(?:10\.|127\.|192\.168\.|169\.254\.|172\.(?:1[6-9]|2\d|3[01])\.)/.test(host)) {
|
|
107
|
+
return "private";
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Adapters whose upstream protocol supports function/tool calling. Mirrors
|
|
114
|
+
* the adapter ids the resolver accepts (including the `azure` alias for
|
|
115
|
+
* `azure-openai`); `kiro` and `mimo-free` send/delegate tool calls.
|
|
116
|
+
*/
|
|
117
|
+
const TOOL_CAPABLE_ADAPTERS = new Set([
|
|
118
|
+
"openai-chat",
|
|
119
|
+
"openai-responses",
|
|
120
|
+
"anthropic",
|
|
121
|
+
"cursor",
|
|
122
|
+
"google",
|
|
123
|
+
"azure-openai",
|
|
124
|
+
"azure",
|
|
125
|
+
"kiro",
|
|
126
|
+
"mimo-free",
|
|
127
|
+
"command-code",
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
function localRemoteEvidence(baseUrl: string | undefined): Pick<RouteCapabilityEvidence, "localOnly" | "remoteAllowed"> {
|
|
131
|
+
if (typeof baseUrl !== "string" || baseUrl.length === 0) return {};
|
|
132
|
+
try {
|
|
133
|
+
const hostname = new URL(baseUrl).hostname;
|
|
134
|
+
if (!hostname) return {};
|
|
135
|
+
const kind = classifyHostname(hostname);
|
|
136
|
+
if (kind === null) return {};
|
|
137
|
+
// Both booleans are emitted once classified: definitive negative evidence,
|
|
138
|
+
// so a local host cannot satisfy `require.remoteAllowed` (or vice versa)
|
|
139
|
+
// under `unknownEvidence.capability: "allow"`/`"penalize"`.
|
|
140
|
+
return kind === "local" || kind === "private"
|
|
141
|
+
? { localOnly: true, remoteAllowed: false }
|
|
142
|
+
: { remoteAllowed: true, localOnly: false };
|
|
143
|
+
} catch {
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Assemble canonical capability evidence for one `provider/model` candidate.
|
|
150
|
+
* Sources (in priority order): provider config maps, provider registry hints,
|
|
151
|
+
* cached Codex catalog row, native-model metadata.
|
|
152
|
+
*/
|
|
153
|
+
export function candidateCapabilityEvidence(
|
|
154
|
+
config: OcxConfig,
|
|
155
|
+
providerName: string,
|
|
156
|
+
modelId: string,
|
|
157
|
+
): RouteCapabilityEvidence {
|
|
158
|
+
const provider = config.providers[providerName];
|
|
159
|
+
const registryEntry = PROVIDER_REGISTRY.find(entry => entry.id === providerName);
|
|
160
|
+
const catalogRow = cachedCatalogModels().find(model => model.provider === providerName && model.id === modelId);
|
|
161
|
+
const isNative = providerName === OPENAI_CODEX_PROVIDER_ID && !modelId.includes("/");
|
|
162
|
+
|
|
163
|
+
// `modelRecordValue`, not a bare lookup: every runtime reader of these three maps
|
|
164
|
+
// resolves them that way, so a `gpt-oss` entry covers `gpt-oss:120b`. Reading raw
|
|
165
|
+
// made the evidence disagree with the resolver it claims to describe — and for the
|
|
166
|
+
// window it did not even degrade to unknown, it fell through to the provider-wide
|
|
167
|
+
// value, which is a definite wrong answer rather than an absent one.
|
|
168
|
+
const rawContextWindow = modelRecordValue(provider?.modelContextWindows, modelId)
|
|
169
|
+
?? provider?.contextWindow
|
|
170
|
+
?? modelRecordValue(registryEntry?.modelContextWindows, modelId)
|
|
171
|
+
?? catalogRow?.contextWindow
|
|
172
|
+
?? (isNative ? nativeOpenAiContextWindow(modelId, nativeContextLimits(config)) : undefined);
|
|
173
|
+
// Native rows go through the accessor (raise-to-ceiling + opt-in). Routed rows keep
|
|
174
|
+
// the raw value; a provider cap on openai must not invent a window they do not have.
|
|
175
|
+
const contextWindow = isNative
|
|
176
|
+
? (nativeOpenAiContextWindow(modelId, nativeContextLimits(config)) ?? rawContextWindow)
|
|
177
|
+
: rawContextWindow;
|
|
178
|
+
|
|
179
|
+
// `noVisionModels` is checked before the modality chain because that is the order
|
|
180
|
+
// `isModelTextOnly` uses: it matches the no-vision list and returns true before it
|
|
181
|
+
// ever reads `modelInputModalities` (`src/vision/index.ts:32`). So a `gpt-oss`
|
|
182
|
+
// no-vision entry beats an exact `gpt-oss:120b` entry that lists "image", and
|
|
183
|
+
// deriving `image` from the modality chain alone reported vision on a model the
|
|
184
|
+
// runtime refuses it for. That matters more here than on the CLI surface fixed in
|
|
185
|
+
// #2086: routing *acts* on this evidence, so it would select the candidate for image
|
|
186
|
+
// work that execution then rejects.
|
|
187
|
+
const noVision = modelInList(provider?.noVisionModels, modelId);
|
|
188
|
+
const modalities = noVision
|
|
189
|
+
? ["text"]
|
|
190
|
+
: (modelRecordValue(provider?.modelInputModalities, modelId)
|
|
191
|
+
?? modelRecordValue(registryEntry?.modelInputModalities, modelId)
|
|
192
|
+
?? catalogRow?.inputModalities
|
|
193
|
+
?? (isNative ? nativeInputModalities(modelId) : undefined));
|
|
194
|
+
const image = Array.isArray(modalities)
|
|
195
|
+
? modalities.includes("image")
|
|
196
|
+
: undefined;
|
|
197
|
+
|
|
198
|
+
const capabilities = catalogRow?.capabilities ?? [];
|
|
199
|
+
// The catalog `capabilities` list is a positive per-model signal; a row
|
|
200
|
+
// without "tools" is treated as unknown, never as a negative. Without a
|
|
201
|
+
// catalog row the adapter protocol itself is the signal: tool-capable
|
|
202
|
+
// adapters run single tool calls even when the parallel-call opt-in is
|
|
203
|
+
// unset or false. `parallelToolCalls` stays a positive provider-level
|
|
204
|
+
// override.
|
|
205
|
+
const tools = capabilities.includes("tools")
|
|
206
|
+
|| isNative
|
|
207
|
+
// The adapter protocol is positive evidence on its own. This was once gated
|
|
208
|
+
// on `catalogRow === undefined`, which was only safe while the catalog lookup
|
|
209
|
+
// never matched anything: once it matches, a row that simply does not
|
|
210
|
+
// enumerate "tools" would silently revoke tool support for every openai-chat
|
|
211
|
+
// and anthropic candidate.
|
|
212
|
+
|| (provider !== undefined && TOOL_CAPABLE_ADAPTERS.has(provider.adapter))
|
|
213
|
+
|| provider?.parallelToolCalls === true
|
|
214
|
+
|| undefined;
|
|
215
|
+
|
|
216
|
+
const reasoningEfforts = modelRecordValue(provider?.modelReasoningEfforts, modelId)
|
|
217
|
+
?? modelRecordValue(registryEntry?.modelReasoningEfforts, modelId)
|
|
218
|
+
?? (isNative ? nativeReasoningEfforts(modelId) : undefined);
|
|
219
|
+
|
|
220
|
+
const tierSupport = provider
|
|
221
|
+
? serviceTierSupportForModel(provider, modelId, providerName)
|
|
222
|
+
: registryEntry ? serviceTierSupportForModel(registryEntry, modelId, providerName) : undefined;
|
|
223
|
+
const serviceTier = tierSupport === true
|
|
224
|
+
? "supported"
|
|
225
|
+
: tierSupport === false ? "unsupported" : "unknown";
|
|
226
|
+
|
|
227
|
+
const localRemote = localRemoteEvidence(provider?.baseUrl);
|
|
228
|
+
// Only emit a definitive encryptedCodexTasks value when the provider is
|
|
229
|
+
// present. An absent/unconfigured provider must stay unknown so
|
|
230
|
+
// require.encryptedCodexTasks does not fail closed on missing config.
|
|
231
|
+
const encryptedCodexTasks = provider === undefined
|
|
232
|
+
? undefined
|
|
233
|
+
: isCanonicalOpenAiForwardProvider(provider);
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
...(typeof contextWindow === "number" ? { contextWindow } : {}),
|
|
237
|
+
...(typeof image === "boolean" ? { image } : {}),
|
|
238
|
+
...(typeof tools === "boolean" ? { tools } : {}),
|
|
239
|
+
...(reasoningEfforts !== undefined && reasoningEfforts.length > 0 ? { reasoningEfforts } : {}),
|
|
240
|
+
...(serviceTier !== "unknown" ? { serviceTier } : {}),
|
|
241
|
+
...localRemote,
|
|
242
|
+
...(typeof encryptedCodexTasks === "boolean" ? { encryptedCodexTasks } : {}),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { OcxConfig, OcxProviderConfig } from "../../types";
|
|
2
|
+
import { candidateCapabilityEvidence } from "../capability";
|
|
3
|
+
import { costEvidenceForCandidate } from "../cost";
|
|
4
|
+
import type { PolicyCandidateEvidence } from "../evaluator";
|
|
5
|
+
import { policyCandidateHealthEvidence } from "../health";
|
|
6
|
+
import type { NormalizedRoutingProfile } from "../profile";
|
|
7
|
+
import { quotaEvidenceForCandidate } from "../quota";
|
|
8
|
+
import { resolveCompatibilityEvidenceProvider, type CoreEvidenceOptions } from "./provider-slot";
|
|
9
|
+
|
|
10
|
+
export type RoutedProviderResolver = (
|
|
11
|
+
providerName: string,
|
|
12
|
+
provider: OcxProviderConfig,
|
|
13
|
+
) => OcxProviderConfig;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options the core assembler needs. Provider-specific test seams (subject resolution,
|
|
17
|
+
* catalog and projection loading) belong to the compatibility provider, not here -- keeping
|
|
18
|
+
* them out is what stops the core assembler from naming Lab-backed contracts.
|
|
19
|
+
*
|
|
20
|
+
* The provider reads its own seams off the same object, so callers may still pass a
|
|
21
|
+
* `LabCompatibilityProviderOptions`; that type extends this one.
|
|
22
|
+
*/
|
|
23
|
+
export type AssemblePolicyEvidenceOptions = CoreEvidenceOptions;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Assemble production policy candidate evidence including compatibility snapshots.
|
|
27
|
+
* Compatibility work is completely skipped for legacy/no-requirement profiles.
|
|
28
|
+
* Active compatibility profiles use one catalog snapshot and one bounded SQLite
|
|
29
|
+
* read for all candidate subject IDs.
|
|
30
|
+
*/
|
|
31
|
+
export function assemblePolicyCandidateEvidence(
|
|
32
|
+
config: OcxConfig,
|
|
33
|
+
profile: NormalizedRoutingProfile,
|
|
34
|
+
now: number,
|
|
35
|
+
options: AssemblePolicyEvidenceOptions,
|
|
36
|
+
): PolicyCandidateEvidence[] {
|
|
37
|
+
const compatibilityPolicy = profile.compatibility;
|
|
38
|
+
const hasCompatibilityRequirements = Boolean(
|
|
39
|
+
compatibilityPolicy && compatibilityPolicy.requiredSuites.length > 0,
|
|
40
|
+
);
|
|
41
|
+
// Compatibility evidence is supplied by an opt-in subsystem. With no provider registered
|
|
42
|
+
// -- every install without compatibility-gated profiles -- the evaluator sees no
|
|
43
|
+
// compatibility evidence and scores on capability, health, quota, and cost exactly as it
|
|
44
|
+
// did before compatibility policy existed.
|
|
45
|
+
const compatibilityProvider = hasCompatibilityRequirements && compatibilityPolicy
|
|
46
|
+
? resolveCompatibilityEvidenceProvider()
|
|
47
|
+
: null;
|
|
48
|
+
const compatibilityByCandidate = compatibilityProvider && compatibilityPolicy
|
|
49
|
+
? compatibilityProvider(config, profile, compatibilityPolicy, options)
|
|
50
|
+
: null;
|
|
51
|
+
|
|
52
|
+
return profile.candidates.map(candidate => {
|
|
53
|
+
const key = `${candidate.provider}/${candidate.model}`;
|
|
54
|
+
const compatibility = compatibilityByCandidate?.get(key);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
provider: candidate.provider,
|
|
58
|
+
model: candidate.model,
|
|
59
|
+
capability: candidateCapabilityEvidence(config, candidate.provider, candidate.model),
|
|
60
|
+
health: policyCandidateHealthEvidence(config, candidate, now),
|
|
61
|
+
quota: quotaEvidenceForCandidate({
|
|
62
|
+
provider: candidate.provider,
|
|
63
|
+
model: candidate.model,
|
|
64
|
+
}),
|
|
65
|
+
cost: costEvidenceForCandidate({
|
|
66
|
+
provider: candidate.provider,
|
|
67
|
+
model: candidate.model,
|
|
68
|
+
limitUsd: profile.limits.maxEstimatedCostUsd,
|
|
69
|
+
}),
|
|
70
|
+
...(compatibility ? { compatibility } : {}),
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
}
|