@yansigit/opencodex 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/LICENSE +21 -0
- package/README.md +303 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +587 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
- package/gui/dist/assets/index-CGoDO3uO.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +2 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +108 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +1327 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +121 -0
- package/src/adapters/client-fingerprint.ts +65 -0
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +288 -0
- package/src/adapters/cursor/discovery.ts +333 -0
- package/src/adapters/cursor/effort-map.ts +151 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +250 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -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/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +269 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1653 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +76 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +332 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +547 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +663 -0
- package/src/adapters/cursor/protobuf-events.ts +1381 -0
- package/src/adapters/cursor/protobuf-request.ts +1032 -0
- package/src/adapters/cursor/request-builder.ts +461 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +735 -0
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +79 -0
- package/src/adapters/cursor/types.ts +79 -0
- package/src/adapters/cursor.ts +322 -0
- package/src/adapters/google-antigravity-hosts.ts +48 -0
- package/src/adapters/google-antigravity-replay.ts +827 -0
- package/src/adapters/google-antigravity-tools.ts +105 -0
- package/src/adapters/google-antigravity-wire.ts +141 -0
- package/src/adapters/google-errors.ts +92 -0
- package/src/adapters/google-http.ts +460 -0
- package/src/adapters/google-tool-schema.ts +238 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +1377 -0
- package/src/adapters/identity.ts +77 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +208 -0
- package/src/adapters/kiro-events.ts +197 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +112 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +224 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1936 -0
- package/src/adapters/mimo-free.ts +280 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1980 -0
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1911 -0
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +154 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +1986 -0
- package/src/chat/inbound.ts +319 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +266 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +205 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +615 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +107 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +578 -0
- package/src/claude/model-info.ts +174 -0
- package/src/claude/outbound.ts +926 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +302 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +737 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +299 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +290 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +213 -0
- package/src/cli/claude.ts +355 -0
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +47 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +127 -0
- package/src/cli/config-command.ts +209 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/dispatch.ts +585 -0
- package/src/cli/doctor.ts +1202 -0
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +213 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +973 -0
- package/src/cli/init.ts +211 -0
- package/src/cli/integrations.ts +260 -0
- package/src/cli/interactive-confirm.ts +133 -0
- 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 +245 -0
- package/src/cli/models.ts +422 -0
- package/src/cli/observe.ts +206 -0
- package/src/cli/opencode.ts +588 -0
- package/src/cli/provider-replit.ts +232 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +492 -0
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +422 -0
- package/src/cli/replit-gateway-key-input.ts +138 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +328 -0
- package/src/cli/star-prompt.ts +211 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +328 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +199 -0
- package/src/cli/v2.ts +268 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1704 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +47 -0
- package/src/codex/account-lifecycle.ts +172 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +195 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +544 -0
- package/src/codex/account-usability.ts +43 -0
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/agent-roles-sync.ts +225 -0
- package/src/codex/agent-roles.ts +238 -0
- package/src/codex/app-server-processes.ts +1143 -0
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +2147 -0
- package/src/codex/auth-collision.ts +109 -0
- package/src/codex/auth-context.ts +665 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +436 -0
- package/src/codex/catalog/bundled.ts +549 -0
- package/src/codex/catalog/effort.ts +446 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +664 -0
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +650 -0
- package/src/codex/catalog/provider-fetch.ts +2064 -0
- package/src/codex/catalog/sync.ts +1883 -0
- 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 +14 -0
- 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/data/upstream-models.json +830 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1566 -0
- 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 +108 -0
- package/src/codex/history-provider.ts +979 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1733 -0
- package/src/codex/injected-marker.ts +106 -0
- 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 +225 -0
- 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 +56 -0
- package/src/codex/main-account.ts +68 -0
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +273 -0
- 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 +144 -0
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +425 -0
- 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 +573 -0
- package/src/codex/refresh.ts +62 -0
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +1888 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +2170 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +784 -0
- package/src/codex/sync.ts +319 -0
- 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 +298 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +160 -0
- package/src/combos/index.ts +45 -0
- package/src/combos/request.ts +94 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +398 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +4041 -0
- package/src/fork/register.ts +3 -0
- package/src/generated/compatibility-version.json +3116 -0
- package/src/generated/model-metadata.ts +106 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +530 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +955 -0
- package/src/images/plan.ts +143 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- 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/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +195 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +346 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +184 -0
- package/src/lib/bun-stream-caps.ts +130 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +364 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +380 -0
- package/src/lib/errors.ts +406 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -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/open-url.ts +25 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +270 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-outbound.ts +210 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +521 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +61 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +119 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +86 -0
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +400 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +392 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-atomic-replace.ts +156 -0
- package/src/lib/windows-elevation.ts +773 -0
- package/src/lib/windows-secret-acl.ts +854 -0
- 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 +403 -0
- 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-routing.ts +594 -0
- package/src/oauth/anthropic.ts +188 -0
- package/src/oauth/antigravity-routing.ts +151 -0
- package/src/oauth/callback-server.ts +300 -0
- package/src/oauth/chatgpt.ts +161 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +252 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +262 -0
- package/src/oauth/health.ts +407 -0
- package/src/oauth/index.ts +1504 -0
- package/src/oauth/key-providers.ts +124 -0
- package/src/oauth/kimi.ts +227 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +130 -0
- package/src/oauth/log.ts +50 -0
- package/src/oauth/login-cli.ts +223 -0
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +728 -0
- package/src/oauth/token-guardian.ts +309 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +695 -0
- package/src/providers/antigravity-quota.ts +216 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +74 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +82 -0
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +586 -0
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +187 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +271 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +449 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +243 -0
- package/src/providers/openai-tier-startup.ts +56 -0
- package/src/providers/openai-tiers.ts +423 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +185 -0
- package/src/providers/quota.ts +2345 -0
- package/src/providers/registry.ts +2918 -0
- package/src/providers/replit/constants.ts +27 -0
- package/src/providers/replit/derive.ts +85 -0
- package/src/providers/replit/headers.ts +28 -0
- package/src/providers/replit/origin.ts +55 -0
- package/src/providers/replit/pair-install-response.ts +72 -0
- package/src/providers/replit/probe.ts +199 -0
- package/src/providers/replit/setup.ts +350 -0
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +103 -0
- 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 +148 -0
- package/src/reasoning-effort.ts +183 -0
- package/src/responses/compaction.ts +142 -0
- 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 +838 -0
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +165 -0
- package/src/responses/spill-store.ts +459 -0
- package/src/responses/state.ts +1433 -0
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +761 -0
- 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 +53 -0
- package/src/server/auth-cors.ts +751 -0
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +442 -0
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +1030 -0
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +190 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +152 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +568 -0
- package/src/server/index.ts +1813 -0
- package/src/server/lifecycle.ts +498 -0
- package/src/server/live.ts +717 -0
- 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 +1433 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +193 -0
- package/src/server/management/body.ts +41 -0
- package/src/server/management/combo-routes.ts +263 -0
- package/src/server/management/config-routes.ts +835 -0
- package/src/server/management/context.ts +113 -0
- 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 +586 -0
- package/src/server/management/model-routes.ts +560 -0
- 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 +637 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +1033 -0
- package/src/server/management/replit-provider-routes.ts +86 -0
- 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 +385 -0
- package/src/server/management/shared.ts +286 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- 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 +435 -0
- package/src/server/management/system-routes.ts +194 -0
- package/src/server/management/usage-summary-cache.ts +94 -0
- 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 +314 -0
- package/src/server/management-auth.ts +482 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +328 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1209 -0
- package/src/server/request-decompress.ts +239 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1259 -0
- 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 +551 -0
- package/src/server/responses/compact.ts +771 -0
- package/src/server/responses/core.ts +5389 -0
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +331 -0
- package/src/server/responses/fetch-helpers.ts +232 -0
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +78 -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 +251 -0
- package/src/server/responses/upstream-error.ts +53 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +272 -0
- 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 +25 -0
- package/src/server/search.ts +201 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +263 -0
- package/src/server/startup-action-control.ts +315 -0
- package/src/server/startup-health-cache.ts +131 -0
- package/src/server/system-env.ts +484 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +472 -0
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +3575 -0
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +364 -0
- package/src/tray/windows.ts +757 -0
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +876 -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 +106 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +415 -0
- package/src/update/job.ts +1887 -0
- package/src/update/notify.ts +263 -0
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +625 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +416 -0
- package/src/usage/log.ts +1223 -0
- package/src/usage/summary.ts +753 -0
- package/src/usage/totals.ts +14 -0
- 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 +189 -0
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +131 -0
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +681 -0
- 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 +195 -0
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +113 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +331 -0
- package/src/web-search/loop.ts +896 -0
- package/src/web-search/parse.ts +315 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/synthetic-tool.ts +47 -0
- package/src/web-search/xai-executor.ts +219 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing-profile management API (RI-04).
|
|
3
|
+
*
|
|
4
|
+
* - `GET /api/routing-profiles` - normalized profiles with revisions
|
|
5
|
+
* - `PUT /api/routing-profiles` - create or replace one validated profile
|
|
6
|
+
* - `DELETE /api/routing-profiles?id=<id>` - remove one profile
|
|
7
|
+
* - `POST /api/routing-profiles/dry-run` - deterministic dry-run evaluation
|
|
8
|
+
* (never dispatches an upstream request)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
getRoutingProfile,
|
|
13
|
+
listRoutingProfileIds,
|
|
14
|
+
normalizeRoutingProfile,
|
|
15
|
+
policyPublicModelId,
|
|
16
|
+
routingProfileIssues,
|
|
17
|
+
} from "../../routing/profile";
|
|
18
|
+
import { evaluatePolicyProfile, type PolicyCandidateEvidence, type PolicyRequestEvidence } from "../../routing/evaluator";
|
|
19
|
+
import { assemblePolicyCandidateEvidence } from "../../routing/compatibility/assemble";
|
|
20
|
+
import { activateLab, labActivationRequired } from "../../lib/lab-activation";
|
|
21
|
+
import { quotaEvidenceForCandidate } from "../../routing/quota";
|
|
22
|
+
import { routedProviderConfig } from "../../router";
|
|
23
|
+
import { saveConfigPreservingClaudeCode, getConfigDir } from "../../config";
|
|
24
|
+
import { reconcileLiveStateStores } from "../../lib/state-store-registrations";
|
|
25
|
+
import { isPlainRecord } from "./shared";
|
|
26
|
+
import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
|
|
27
|
+
import { jsonResponse } from "../auth-cors";
|
|
28
|
+
import type { ManagementContext } from "./context";
|
|
29
|
+
import type { OcxConfig, OcxRoutingProfileConfig } from "../../types";
|
|
30
|
+
|
|
31
|
+
function profileDto(config: Parameters<typeof getRoutingProfile>[0], id: string): Record<string, unknown> | null {
|
|
32
|
+
const profile = getRoutingProfile(config, id);
|
|
33
|
+
if (!profile) return null;
|
|
34
|
+
return {
|
|
35
|
+
id,
|
|
36
|
+
alias: profile.alias,
|
|
37
|
+
model: policyPublicModelId(id, profile),
|
|
38
|
+
revision: profile.revision,
|
|
39
|
+
candidates: profile.candidates,
|
|
40
|
+
require: profile.require,
|
|
41
|
+
optimize: profile.optimize,
|
|
42
|
+
limits: profile.limits,
|
|
43
|
+
unknownEvidence: profile.unknownEvidence,
|
|
44
|
+
...(profile.compatibility ? { compatibility: profile.compatibility } : {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseEvidence(raw: unknown): { evidence: PolicyRequestEvidence; ok: boolean } {
|
|
49
|
+
// Absent evidence is empty evidence, mirroring the absent-candidates case.
|
|
50
|
+
if (raw === undefined) return { evidence: {}, ok: true };
|
|
51
|
+
if (!isPlainRecord(raw)) return { evidence: {}, ok: false };
|
|
52
|
+
const record = raw as Record<string, unknown>;
|
|
53
|
+
const evidence: PolicyRequestEvidence = {};
|
|
54
|
+
if (typeof record.contextWindow === "number" && Number.isFinite(record.contextWindow) && record.contextWindow >= 0) {
|
|
55
|
+
evidence.contextWindow = record.contextWindow;
|
|
56
|
+
}
|
|
57
|
+
for (const key of ["toolsRequired", "imageInputRequired", "structuredOutputRequired", "encryptedCodexTask"] as const) {
|
|
58
|
+
if (typeof record[key] === "boolean") evidence[key] = record[key];
|
|
59
|
+
}
|
|
60
|
+
if (typeof record.reasoningEffort === "string") evidence.reasoningEffort = record.reasoningEffort;
|
|
61
|
+
if (typeof record.serviceTier === "string") evidence.serviceTier = record.serviceTier;
|
|
62
|
+
return { evidence, ok: true };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseCandidateEvidence(raw: unknown): PolicyCandidateEvidence[] | null {
|
|
66
|
+
if (!Array.isArray(raw)) return null;
|
|
67
|
+
const out: PolicyCandidateEvidence[] = [];
|
|
68
|
+
for (const item of raw) {
|
|
69
|
+
if (!isPlainRecord(item)) return null;
|
|
70
|
+
const provider = item.provider;
|
|
71
|
+
const model = item.model;
|
|
72
|
+
if (typeof provider !== "string" || typeof model !== "string") return null;
|
|
73
|
+
out.push({
|
|
74
|
+
provider,
|
|
75
|
+
model,
|
|
76
|
+
...(typeof item.accountRef === "string" ? { accountRef: item.accountRef } : {}),
|
|
77
|
+
...(typeof item.codexAccountId === "string" ? { codexAccountId: item.codexAccountId } : {}),
|
|
78
|
+
// Dry-run evidence is caller-supplied and echoed back in the result as
|
|
79
|
+
// given; the trace's candidate rows carry only score/exclusions, which
|
|
80
|
+
// the trace builder bounds. Structural casts keep the API permissive.
|
|
81
|
+
...(isPlainRecord(item.capability) ? { capability: item.capability as unknown as PolicyCandidateEvidence["capability"] } : {}),
|
|
82
|
+
...(isPlainRecord(item.health) ? { health: item.health as unknown as PolicyCandidateEvidence["health"] } : {}),
|
|
83
|
+
...(isPlainRecord(item.quota) ? { quota: item.quota as unknown as PolicyCandidateEvidence["quota"] } : {}),
|
|
84
|
+
...(isPlainRecord(item.cost) ? { cost: item.cost as unknown as PolicyCandidateEvidence["cost"] } : {}),
|
|
85
|
+
// Derive account-scoped quota evidence from the documented refs when the
|
|
86
|
+
// caller does not supply an explicit quota object, so a dry-run following
|
|
87
|
+
// the documented shape reports the same cached account quota as routing.
|
|
88
|
+
...(item.quota === undefined && typeof item.codexAccountId === "string"
|
|
89
|
+
? { quota: quotaEvidenceForCandidate({ provider, model, codexAccountId: item.codexAccountId }) }
|
|
90
|
+
: {}),
|
|
91
|
+
...(item.quota === undefined && typeof item.accountRef === "string" && typeof item.codexAccountId !== "string"
|
|
92
|
+
? { quota: quotaEvidenceForCandidate({ provider, model, accountRef: item.accountRef }) }
|
|
93
|
+
: {}),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function assembleCandidateEvidence(
|
|
100
|
+
config: OcxConfig,
|
|
101
|
+
profile: NonNullable<ReturnType<typeof getRoutingProfile>>,
|
|
102
|
+
now: number,
|
|
103
|
+
): PolicyCandidateEvidence[] {
|
|
104
|
+
return assemblePolicyCandidateEvidence(config, profile, now, {
|
|
105
|
+
routedProviderConfig,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function storedProfile(
|
|
110
|
+
id: string,
|
|
111
|
+
raw: OcxRoutingProfileConfig,
|
|
112
|
+
): OcxRoutingProfileConfig {
|
|
113
|
+
const normalized = normalizeRoutingProfile(id, raw);
|
|
114
|
+
const {
|
|
115
|
+
id: _id,
|
|
116
|
+
revision: _revision,
|
|
117
|
+
alias,
|
|
118
|
+
...profile
|
|
119
|
+
} = normalized;
|
|
120
|
+
return alias === null ? profile : { ...profile, alias };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Rewrite config references from one public model id to another (alias change
|
|
125
|
+
* on update). Mirrors the /api/combos migration: model-valued config that
|
|
126
|
+
* still names the old alias must follow it, or requests fall through to
|
|
127
|
+
* ordinary routing and send the obsolete alias upstream.
|
|
128
|
+
*/
|
|
129
|
+
/**
|
|
130
|
+
* Detect a modelMap key collision that the alias migration would silently
|
|
131
|
+
* resolve by dropping one mapping: the map already contains the new public
|
|
132
|
+
* model as a key with a different target than the old-alias key's target.
|
|
133
|
+
*/
|
|
134
|
+
function modelMapMigrationCollision(
|
|
135
|
+
config: OcxConfig,
|
|
136
|
+
oldPublicModel: string,
|
|
137
|
+
newPublicModel: string,
|
|
138
|
+
): string | null {
|
|
139
|
+
const map = config.claudeCode?.modelMap;
|
|
140
|
+
if (!map) return null;
|
|
141
|
+
if (oldPublicModel === newPublicModel) return null;
|
|
142
|
+
const oldTarget = map[oldPublicModel];
|
|
143
|
+
if (oldTarget === undefined) return null;
|
|
144
|
+
const newTarget = map[newPublicModel];
|
|
145
|
+
if (newTarget === undefined) return null;
|
|
146
|
+
if (oldTarget === newTarget) return null;
|
|
147
|
+
return `modelMap already maps \"${newPublicModel}\" to \"${newTarget}\"; renaming \"${oldPublicModel}\" (→ \"${newTarget}\") would drop one mapping. Resolve the conflict and retry.`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Rewrite config references from one public model id to another (alias change
|
|
152
|
+
* on update). Mirrors the /api/combos migration: model-valued config that
|
|
153
|
+
* still names the old alias must follow it, or requests fall through to
|
|
154
|
+
* ordinary routing and send the obsolete alias upstream.
|
|
155
|
+
*/
|
|
156
|
+
function migrateProfileModelReferences(
|
|
157
|
+
config: OcxConfig,
|
|
158
|
+
oldPublicModel: string,
|
|
159
|
+
newPublicModel: string,
|
|
160
|
+
): boolean {
|
|
161
|
+
if (oldPublicModel === newPublicModel) return false;
|
|
162
|
+
const migrateReference = (model: string): string => (
|
|
163
|
+
model === oldPublicModel ? newPublicModel : model
|
|
164
|
+
);
|
|
165
|
+
let shouldSyncClaudeAgentDefs = false;
|
|
166
|
+
const migrateAgentReference = (model: string): string => {
|
|
167
|
+
const migrated = migrateReference(model);
|
|
168
|
+
if (migrated !== model) shouldSyncClaudeAgentDefs = true;
|
|
169
|
+
return migrated;
|
|
170
|
+
};
|
|
171
|
+
const migrateReferences = (models: string[]): string[] => [
|
|
172
|
+
...new Set(models.map(migrateReference)),
|
|
173
|
+
];
|
|
174
|
+
if (config.disabledModels) {
|
|
175
|
+
config.disabledModels = migrateReferences(config.disabledModels);
|
|
176
|
+
}
|
|
177
|
+
if (config.subagentModels) {
|
|
178
|
+
config.subagentModels = [...new Set(config.subagentModels.map(migrateAgentReference))];
|
|
179
|
+
}
|
|
180
|
+
if (config.subagentRoles) {
|
|
181
|
+
for (const role of config.subagentRoles) {
|
|
182
|
+
role.model = migrateAgentReference(role.model);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (config.subagentModelFallback) {
|
|
186
|
+
config.subagentModelFallback = [...new Set(config.subagentModelFallback.map(migrateAgentReference))];
|
|
187
|
+
}
|
|
188
|
+
if (config.injectionModel && config.injectionModel === oldPublicModel) {
|
|
189
|
+
config.injectionModel = newPublicModel;
|
|
190
|
+
}
|
|
191
|
+
if (config.shadowCallIntercept?.model && config.shadowCallIntercept.model === oldPublicModel) {
|
|
192
|
+
config.shadowCallIntercept = { ...config.shadowCallIntercept, model: newPublicModel };
|
|
193
|
+
}
|
|
194
|
+
if (config.claudeCode) {
|
|
195
|
+
const claudeCode = { ...config.claudeCode };
|
|
196
|
+
for (const field of ["model", "smallFastModel"] as const) {
|
|
197
|
+
if (claudeCode[field]) claudeCode[field] = migrateAgentReference(claudeCode[field]);
|
|
198
|
+
}
|
|
199
|
+
if (claudeCode.tierModels) {
|
|
200
|
+
claudeCode.tierModels = Object.fromEntries(
|
|
201
|
+
Object.entries(claudeCode.tierModels).map(([tier, model]) => [tier, migrateAgentReference(model)]),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
if (claudeCode.modelMap) {
|
|
205
|
+
// Keys are the inbound ids matched for reroute (src/claude/inbound.ts);
|
|
206
|
+
// an old-alias key must follow the rename or that request stops intercepting.
|
|
207
|
+
claudeCode.modelMap = Object.fromEntries(
|
|
208
|
+
Object.entries(claudeCode.modelMap).map(([source, model]) => [
|
|
209
|
+
migrateAgentReference(source),
|
|
210
|
+
migrateAgentReference(model),
|
|
211
|
+
]),
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
config.claudeCode = claudeCode;
|
|
215
|
+
}
|
|
216
|
+
return shouldSyncClaudeAgentDefs;
|
|
217
|
+
}
|
|
218
|
+
export async function handleRoutingProfileRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
219
|
+
const { req, url, config, deps, convergeCodexCatalog, syncClaudeAgentDefsBestEffort } = ctx;
|
|
220
|
+
|
|
221
|
+
if (url.pathname === "/api/routing-profiles" && req.method === "GET") {
|
|
222
|
+
const profiles = listRoutingProfileIds(config).map(id => profileDto(config, id)).filter(
|
|
223
|
+
(profile): profile is Record<string, unknown> => profile !== null,
|
|
224
|
+
);
|
|
225
|
+
return jsonResponse({ profiles }, 200, req, config);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (url.pathname === "/api/routing-profiles" && req.method === "PUT") {
|
|
229
|
+
let rawBody: unknown;
|
|
230
|
+
try {
|
|
231
|
+
rawBody = await readManagementJsonBody(req);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
rethrowManagementBodyTooLarge(error);
|
|
234
|
+
return jsonResponse({ error: "invalid JSON body" }, 400, req, config);
|
|
235
|
+
}
|
|
236
|
+
if (!isPlainRecord(rawBody)) {
|
|
237
|
+
return jsonResponse({ error: "request body must be an object" }, 400, req, config);
|
|
238
|
+
}
|
|
239
|
+
const body = rawBody as Record<string, unknown>;
|
|
240
|
+
const id = typeof body.id === "string" ? body.id.trim() : "";
|
|
241
|
+
if (!id) {
|
|
242
|
+
return jsonResponse({ error: { code: "missing_profile_id", message: "id is required" } }, 400, req, config);
|
|
243
|
+
}
|
|
244
|
+
const mode = body.mode === "create" || body.mode === "update" ? body.mode : null;
|
|
245
|
+
if (!mode) {
|
|
246
|
+
return jsonResponse({ error: { code: "invalid_profile_mode", message: "mode must be create or update" } }, 400, req, config);
|
|
247
|
+
}
|
|
248
|
+
const exists = Object.hasOwn(config.routingProfiles ?? {}, id);
|
|
249
|
+
if (mode === "create" && exists) {
|
|
250
|
+
return jsonResponse({ error: { code: "profile_exists", message: `routing profile already exists: ${id}` } }, 409, req, config);
|
|
251
|
+
}
|
|
252
|
+
if (mode === "update" && !exists) {
|
|
253
|
+
return jsonResponse({ error: { code: "unknown_profile", message: `unknown routing profile: ${id}` } }, 404, req, config);
|
|
254
|
+
}
|
|
255
|
+
if (mode === "update") {
|
|
256
|
+
const expectedRevision = typeof body.expectedRevision === "string" && body.expectedRevision.trim()
|
|
257
|
+
? body.expectedRevision.trim()
|
|
258
|
+
: undefined;
|
|
259
|
+
if (expectedRevision) {
|
|
260
|
+
const current = getRoutingProfile(config, id);
|
|
261
|
+
if (current && current.revision !== expectedRevision) {
|
|
262
|
+
return jsonResponse({
|
|
263
|
+
error: {
|
|
264
|
+
code: "profile_revision_conflict",
|
|
265
|
+
message: `routing profile ${id} changed since it was loaded; reload and retry`,
|
|
266
|
+
currentRevision: current.revision,
|
|
267
|
+
},
|
|
268
|
+
}, 409, req, config);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const issues = routingProfileIssues(id, body.profile, config, { excludeProfileId: id });
|
|
273
|
+
if (issues.length > 0) {
|
|
274
|
+
return jsonResponse({
|
|
275
|
+
error: {
|
|
276
|
+
code: "invalid_profile",
|
|
277
|
+
message: issues[0]!.message,
|
|
278
|
+
issues,
|
|
279
|
+
},
|
|
280
|
+
}, 400, req, config);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const previousProfile = mode === "update" ? getRoutingProfile(config, id) : undefined;
|
|
284
|
+
if (mode === "update" && previousProfile) {
|
|
285
|
+
const oldPublicModel = policyPublicModelId(id, previousProfile);
|
|
286
|
+
const newProfile = normalizeRoutingProfile(id, body.profile as OcxRoutingProfileConfig);
|
|
287
|
+
const newPublicModel = policyPublicModelId(id, newProfile);
|
|
288
|
+
const collision = modelMapMigrationCollision(config, oldPublicModel, newPublicModel);
|
|
289
|
+
if (collision) {
|
|
290
|
+
return jsonResponse({
|
|
291
|
+
error: { code: "alias_reference_conflict", message: collision },
|
|
292
|
+
}, 409, req, config);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const nextProfiles = { ...(config.routingProfiles ?? {}) };
|
|
296
|
+
nextProfiles[id] = storedProfile(id, body.profile as OcxRoutingProfileConfig);
|
|
297
|
+
config.routingProfiles = nextProfiles;
|
|
298
|
+
// Creating the first profile on a process started profile-less must install the
|
|
299
|
+
// compatibility provider now; activation is synchronous and idempotent per configDir.
|
|
300
|
+
if (labActivationRequired(config, getConfigDir())) activateLab(config, getConfigDir());
|
|
301
|
+
// An alias change on update renames the public model id; rewrite config
|
|
302
|
+
// references (disabledModels, subagentModels, injectionModel,
|
|
303
|
+
// shadowCallIntercept, claudeCode) so they follow the new alias.
|
|
304
|
+
let shouldSyncClaudeAgentDefs = false;
|
|
305
|
+
if (previousProfile) {
|
|
306
|
+
const oldPublicModel = policyPublicModelId(id, previousProfile);
|
|
307
|
+
const newPublicModel = policyPublicModelId(id, getRoutingProfile(config, id)!);
|
|
308
|
+
shouldSyncClaudeAgentDefs = migrateProfileModelReferences(config, oldPublicModel, newPublicModel);
|
|
309
|
+
}
|
|
310
|
+
const save = deps.saveConfigPreservingClaudeCode ?? saveConfigPreservingClaudeCode;
|
|
311
|
+
save(config);
|
|
312
|
+
reconcileLiveStateStores();
|
|
313
|
+
const catalogRefresh = await convergeCodexCatalog();
|
|
314
|
+
if (shouldSyncClaudeAgentDefs) await syncClaudeAgentDefsBestEffort();
|
|
315
|
+
const profile = profileDto(config, id)!;
|
|
316
|
+
return jsonResponse({
|
|
317
|
+
success: true,
|
|
318
|
+
id,
|
|
319
|
+
model: profile.model,
|
|
320
|
+
profile,
|
|
321
|
+
catalogRefresh,
|
|
322
|
+
}, 200, req, config);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (url.pathname === "/api/routing-profiles" && req.method === "DELETE") {
|
|
326
|
+
const id = url.searchParams.get("id")?.trim();
|
|
327
|
+
if (!id) {
|
|
328
|
+
return jsonResponse({ error: "id query param is required" }, 400, req, config);
|
|
329
|
+
}
|
|
330
|
+
if (!Object.hasOwn(config.routingProfiles ?? {}, id)) {
|
|
331
|
+
return jsonResponse({ error: "unknown routing profile" }, 404, req, config);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const nextProfiles = { ...(config.routingProfiles ?? {}) };
|
|
335
|
+
delete nextProfiles[id];
|
|
336
|
+
if (Object.keys(nextProfiles).length > 0) config.routingProfiles = nextProfiles;
|
|
337
|
+
else delete config.routingProfiles;
|
|
338
|
+
const saveConfigPreservingClaudeCodeSafe = deps.saveConfigPreservingClaudeCode ?? saveConfigPreservingClaudeCode;
|
|
339
|
+
saveConfigPreservingClaudeCodeSafe(config);
|
|
340
|
+
reconcileLiveStateStores();
|
|
341
|
+
const catalogRefresh = await convergeCodexCatalog();
|
|
342
|
+
return jsonResponse({ success: true, id, catalogRefresh }, 200, req, config);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (url.pathname === "/api/routing-profiles/dry-run" && req.method === "POST") {
|
|
346
|
+
let rawBody: unknown;
|
|
347
|
+
try { rawBody = await readManagementJsonBody(req); } catch (error) {
|
|
348
|
+
rethrowManagementBodyTooLarge(error);
|
|
349
|
+
return jsonResponse({ error: "invalid JSON body" }, 400, req, config);
|
|
350
|
+
}
|
|
351
|
+
if (!isPlainRecord(rawBody)) {
|
|
352
|
+
return jsonResponse({ error: "request body must be an object" }, 400, req, config);
|
|
353
|
+
}
|
|
354
|
+
const body = rawBody as Record<string, unknown>;
|
|
355
|
+
const profile = typeof body.profile === "string" ? body.profile.trim() : "";
|
|
356
|
+
if (!profile) {
|
|
357
|
+
return jsonResponse({ error: { code: "missing_profile", message: "profile is required" } }, 400, req, config);
|
|
358
|
+
}
|
|
359
|
+
const resolvedProfile = getRoutingProfile(config, profile);
|
|
360
|
+
if (!resolvedProfile) {
|
|
361
|
+
return jsonResponse({ error: { code: "unknown_profile", message: `unknown routing profile: ${profile}` } }, 404, req, config);
|
|
362
|
+
}
|
|
363
|
+
const { evidence, ok } = parseEvidence(body.evidence);
|
|
364
|
+
if (!ok) {
|
|
365
|
+
return jsonResponse({ error: { code: "invalid_evidence", message: "evidence must be an object" } }, 400, req, config);
|
|
366
|
+
}
|
|
367
|
+
// One clock read for both assembly and evaluation keeps freshness, health,
|
|
368
|
+
// and trace timestamps mutually consistent with the production router.
|
|
369
|
+
const now = Date.now();
|
|
370
|
+
// R3-1: dry-run assembles candidate evidence independently of the startup gate, so an
|
|
371
|
+
// operator preview on a process started without profiles would silently omit
|
|
372
|
+
// compatibility evidence and disagree with production. Activate first.
|
|
373
|
+
if (labActivationRequired(config, getConfigDir())) activateLab(config, getConfigDir());
|
|
374
|
+
const candidateEvidence = body.candidates === undefined
|
|
375
|
+
? assembleCandidateEvidence(config, resolvedProfile, now)
|
|
376
|
+
: parseCandidateEvidence(body.candidates);
|
|
377
|
+
if (candidateEvidence === null) {
|
|
378
|
+
return jsonResponse({ error: { code: "invalid_candidates", message: "candidates must be an array of evidence objects" } }, 400, req, config);
|
|
379
|
+
}
|
|
380
|
+
const result = evaluatePolicyProfile(config, profile, evidence, candidateEvidence, now);
|
|
381
|
+
return jsonResponse(result, 200, req, config);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import type { CatalogModel } from "../../codex/catalog";
|
|
4
|
+
import { catalogModelSlug, invalidateCodexModelsCache, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_SUBAGENT_MODELS,
|
|
7
|
+
codexAutoStartEnabled,
|
|
8
|
+
hasOwnProvider,
|
|
9
|
+
isValidProviderName,
|
|
10
|
+
multiAgentGuidanceEnabled,
|
|
11
|
+
providerBaseUrlConfigError,
|
|
12
|
+
providerHeadersConfigError,
|
|
13
|
+
saveConfigPreservingClaudeCode,
|
|
14
|
+
} from "../../config";
|
|
15
|
+
import {
|
|
16
|
+
clearLoginState,
|
|
17
|
+
getLoginStatus,
|
|
18
|
+
isPublicOAuthProvider,
|
|
19
|
+
listOAuthProviders,
|
|
20
|
+
startLoginFlow,
|
|
21
|
+
submitManualLoginCode,
|
|
22
|
+
upsertOAuthProvider,
|
|
23
|
+
} from "../../oauth";
|
|
24
|
+
import { removeCredential } from "../../oauth/store";
|
|
25
|
+
import { providerDestinationResolvedError } from "../../lib/destination-policy";
|
|
26
|
+
import { enrichProviderFromCatalog, listKeyLoginProviders } from "../../oauth/key-providers";
|
|
27
|
+
import { deriveProviderPresets } from "../../providers/derive";
|
|
28
|
+
import { providerCodexAccountMode } from "../../providers/registry";
|
|
29
|
+
import { routedSlug, slugEquals } from "../../providers/slug-codec";
|
|
30
|
+
import { clearProviderQuotaCache, fetchProviderQuotaReports } from "../../providers/quota";
|
|
31
|
+
import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
32
|
+
import { clearThreadAccountMap } from "../../codex/routing";
|
|
33
|
+
import { primeCodexPoolQuotas } from "../../codex/auth-api";
|
|
34
|
+
import { DEFAULT_PROVIDER_CONTEXT_CAP, globalContextCapValue, providerContextCap, providerContextCaps, setAllProviderContextCaps, setGlobalContextCapValue, setProviderContextCap } from "../../providers/context-cap";
|
|
35
|
+
import { resolveCodexHomeDir } from "../../codex/home";
|
|
36
|
+
import { readUsageEntries } from "../../usage/log";
|
|
37
|
+
import { getUsageDebugLogEntries } from "../../usage/debug";
|
|
38
|
+
import { parseRange, parseUsageSurface, summarizeUsage } from "../../usage/summary";
|
|
39
|
+
import { stripCodexRuntimeProviderFields } from "../../codex/auth-context";
|
|
40
|
+
import { getProviderRegistryEntry, providerMatchesRegistryTransport } from "../../providers/registry";
|
|
41
|
+
import { getDebugLogEntries } from "../../lib/debug-log-buffer";
|
|
42
|
+
import { getInjectionDebugLogEntries } from "../../lib/injection-debug-log";
|
|
43
|
+
import {
|
|
44
|
+
clearDebugSettings,
|
|
45
|
+
clearDebugSetting,
|
|
46
|
+
getDebugSettings,
|
|
47
|
+
setDebugSettings,
|
|
48
|
+
type DebugFlag,
|
|
49
|
+
} from "../../lib/debug-settings";
|
|
50
|
+
import type { OcxClaudeCodeConfig, OcxClaudeDesktopProfile, OcxConfig, OcxCustomModel, OcxProviderConfig } from "../../types";
|
|
51
|
+
import type { DesktopProfileModel } from "../../claude/desktop-profile";
|
|
52
|
+
import { drainAndShutdown } from "../lifecycle";
|
|
53
|
+
import { filterRequestLogs, getRequestLogEntries, type RequestLogEntry } from "../request-log";
|
|
54
|
+
import { estimateComboCost, estimateRequestCost, serviceTierContext, normalizeCostTokens, tokensPerSecond } from "../../usage/cost";
|
|
55
|
+
import type { PersistedUsageAttempt } from "../../usage/log";
|
|
56
|
+
import { isAllowedRequestOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO } from "../auth-cors";
|
|
57
|
+
import { applySystemEnvToggle } from "../system-env";
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
export function isPlainRecord(v: unknown): v is Record<string, unknown> {
|
|
61
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function parseDebugLogQuery(url: URL): { after: number; limit: number } {
|
|
65
|
+
const after = Number(url.searchParams.get("after") ?? url.searchParams.get("since") ?? "0");
|
|
66
|
+
const limit = Number(url.searchParams.get("limit") ?? "500");
|
|
67
|
+
return {
|
|
68
|
+
after: Number.isFinite(after) && after > 0 ? after : 0,
|
|
69
|
+
limit: Number.isFinite(limit) && limit > 0 ? Math.min(limit, 2000) : 500,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---- /api/logs display metrics (devlog/_plan/260720_toks_speed_price_columns/020) ----
|
|
74
|
+
// Derived at response time only; NEVER persisted to the request log or usage.jsonl.
|
|
75
|
+
|
|
76
|
+
export type MetricUnavailableReason =
|
|
77
|
+
| "usage_missing" | "usage_unsupported" | "output_missing" | "invalid_duration"
|
|
78
|
+
| "price_unmatched" | "invalid_cache_breakdown"
|
|
79
|
+
| "invalid_usage" | "combo_attempt_unavailable";
|
|
80
|
+
|
|
81
|
+
export type TokPerSecondResult =
|
|
82
|
+
| { kind: "value"; value: number; estimated: boolean }
|
|
83
|
+
| { kind: "unavailable"; reason: MetricUnavailableReason };
|
|
84
|
+
|
|
85
|
+
export type CostEstimateReason =
|
|
86
|
+
| "usage_estimated"
|
|
87
|
+
| "cache_detail_missing"
|
|
88
|
+
| "expected_price_overlay"
|
|
89
|
+
| "provider_cost_overlay"
|
|
90
|
+
| "priority_lower_bound";
|
|
91
|
+
|
|
92
|
+
export type CostResult =
|
|
93
|
+
| { kind: "value"; estimate: NonNullable<ReturnType<typeof estimateRequestCost>>; estimateReasons: CostEstimateReason[] }
|
|
94
|
+
| { kind: "unavailable"; reason: MetricUnavailableReason };
|
|
95
|
+
|
|
96
|
+
export type MetricSource = Pick<RequestLogEntry, "provider" | "model" | "durationMs" | "usageStatus" | "usage" | "requestedServiceTier" | "configuredServiceTier" | "responseServiceTier" | "tierOutcome"> & {
|
|
97
|
+
attempts?: readonly PersistedUsageAttempt[];
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export function tokPerSecondResult(entry: Pick<MetricSource, "durationMs" | "usageStatus" | "usage">): TokPerSecondResult {
|
|
101
|
+
if (!entry.usage) return { kind: "unavailable", reason: "usage_missing" };
|
|
102
|
+
if (entry.usageStatus === "unsupported") return { kind: "unavailable", reason: "usage_unsupported" };
|
|
103
|
+
const value = tokensPerSecond(entry.usage.outputTokens, entry.durationMs);
|
|
104
|
+
if (value === null) {
|
|
105
|
+
return {
|
|
106
|
+
kind: "unavailable",
|
|
107
|
+
reason: entry.usage.outputTokens <= 0 ? "output_missing" : "invalid_duration",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return { kind: "value", value, estimated: entry.usageStatus === "estimated" || entry.usage.estimated === true };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function unavailableCostReason(entry: MetricSource): MetricUnavailableReason {
|
|
114
|
+
// Normalizer-first classification: the landed normalizer recovers legacy
|
|
115
|
+
// cachedInputTokens=read+write rows via retry, so a raw read+write>input
|
|
116
|
+
// pre-check would misclassify recoverable rows (020 audit blocker #2).
|
|
117
|
+
if (!entry.usage && !entry.attempts?.length) return "usage_missing";
|
|
118
|
+
if (entry.usageStatus === "unsupported") return "usage_unsupported";
|
|
119
|
+
if (entry.attempts?.length) return "combo_attempt_unavailable";
|
|
120
|
+
if (!entry.usage) return "usage_missing";
|
|
121
|
+
if (!normalizeCostTokens(entry.usage)) {
|
|
122
|
+
const effectiveRead = entry.usage.cacheReadInputTokens ?? entry.usage.cachedInputTokens ?? 0;
|
|
123
|
+
const effectiveWrite = entry.usage.cacheCreationInputTokens ?? 0;
|
|
124
|
+
const finite = [entry.usage.inputTokens, entry.usage.outputTokens, effectiveRead, effectiveWrite]
|
|
125
|
+
.every(v => Number.isFinite(v) && v >= 0);
|
|
126
|
+
return finite ? "invalid_cache_breakdown" : "invalid_usage";
|
|
127
|
+
}
|
|
128
|
+
return "price_unmatched";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Display-time cost estimate for one log entry (or its attempt list), including the reasons that qualify the estimate. */
|
|
132
|
+
export function costResult(entry: MetricSource): CostResult {
|
|
133
|
+
const tier = serviceTierContext(entry);
|
|
134
|
+
const estimate = entry.attempts?.length
|
|
135
|
+
? estimateComboCost(entry.attempts, undefined, tier)
|
|
136
|
+
: estimateRequestCost({ provider: entry.provider, model: entry.model, usage: entry.usage, usageStatus: entry.usageStatus, serviceTier: tier });
|
|
137
|
+
if (!estimate) return { kind: "unavailable", reason: unavailableCostReason(entry) };
|
|
138
|
+
const estimateReasons = [
|
|
139
|
+
entry.usageStatus === "estimated" || entry.usage?.estimated ? "usage_estimated" as const : undefined,
|
|
140
|
+
entry.usage && entry.usage.cachedInputTokens === undefined
|
|
141
|
+
&& entry.usage.cacheReadInputTokens === undefined
|
|
142
|
+
&& entry.usage.cacheCreationInputTokens === undefined ? "cache_detail_missing" as const : undefined,
|
|
143
|
+
estimate.price?.source === "expected" || estimate.attempts?.some(a => a.price.source === "expected")
|
|
144
|
+
? "expected_price_overlay" as const : undefined,
|
|
145
|
+
estimate.price?.source === "user" || estimate.attempts?.some(a => a.price.source === "user")
|
|
146
|
+
? "provider_cost_overlay" as const : undefined,
|
|
147
|
+
estimate.priorityLowerBound ? "priority_lower_bound" as const : undefined,
|
|
148
|
+
].filter((reason): reason is CostEstimateReason => reason !== undefined);
|
|
149
|
+
return { kind: "value", estimate, estimateReasons };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function requestLogDto(entry: RequestLogEntry): Record<string, unknown> {
|
|
153
|
+
return {
|
|
154
|
+
...entry,
|
|
155
|
+
displayMetrics: {
|
|
156
|
+
tokPerSecond: tokPerSecondResult(entry),
|
|
157
|
+
cost: costResult(entry),
|
|
158
|
+
},
|
|
159
|
+
...(entry.attempts?.length
|
|
160
|
+
? {
|
|
161
|
+
attempts: entry.attempts.map(attempt => ({
|
|
162
|
+
...attempt,
|
|
163
|
+
displayMetrics: {
|
|
164
|
+
tokPerSecond: tokPerSecondResult(attempt),
|
|
165
|
+
cost: costResult({ ...attempt, attempts: undefined, requestedServiceTier: entry.requestedServiceTier, configuredServiceTier: entry.configuredServiceTier, responseServiceTier: entry.responseServiceTier }),
|
|
166
|
+
},
|
|
167
|
+
})),
|
|
168
|
+
}
|
|
169
|
+
: {}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Live routed-provider models for the proxy's /api/* and /v1/models endpoints. Delegates to the
|
|
175
|
+
* canonical, TTL-cached `gatherRoutedModels` (single source of truth) — so the GUI/codex endpoints
|
|
176
|
+
* share the same fetch, the same per-provider cache (dedups Codex's frequent /v1/models polling),
|
|
177
|
+
* and the same stale fallback when a provider blips, instead of a parallel uncached copy.
|
|
178
|
+
*/
|
|
179
|
+
export async function fetchAllModels(config: OcxConfig): Promise<CatalogModel[]> {
|
|
180
|
+
const { gatherRoutedModels } = await import("../../codex/catalog");
|
|
181
|
+
return gatherRoutedModels(config);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface GrokCandidateModel {
|
|
185
|
+
id: string;
|
|
186
|
+
contextWindow?: number;
|
|
187
|
+
native: boolean;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The model list `syncGrokConfig` would inject, BEFORE the user's exclusions. The Grok
|
|
192
|
+
* page needs this to show a switch for a model the user has already excluded — such a
|
|
193
|
+
* model is absent from the fence, so readGrokStatus alone could never list it. Built
|
|
194
|
+
* from the same two sources as the sync so the two can never disagree.
|
|
195
|
+
*/
|
|
196
|
+
export async function fetchGrokCandidateModels(config: OcxConfig): Promise<GrokCandidateModel[]> {
|
|
197
|
+
const { filterCatalogVisibleModels, nativeContextLimits, nativeOpenAiContextWindow, visibleNativeSlugs } = await import("../../codex/catalog");
|
|
198
|
+
const routed = filterCatalogVisibleModels(await fetchAllModels(config), config);
|
|
199
|
+
return [
|
|
200
|
+
...visibleNativeSlugs(config).map(id => {
|
|
201
|
+
const contextWindow = nativeOpenAiContextWindow(id, nativeContextLimits(config));
|
|
202
|
+
return { id, native: true, ...(contextWindow !== undefined ? { contextWindow } : {}) };
|
|
203
|
+
}),
|
|
204
|
+
...routed.map(m => ({
|
|
205
|
+
id: m.alias ?? `${m.provider}/${m.id}`,
|
|
206
|
+
native: false,
|
|
207
|
+
...(m.contextWindow !== undefined ? { contextWindow: m.contextWindow } : {}),
|
|
208
|
+
})),
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function stripRegistryOnlyStaticHeaders(name: string, provider: OcxProviderConfig): OcxProviderConfig {
|
|
213
|
+
const entry = providerMatchesRegistryTransport(name, provider) ? getProviderRegistryEntry(name) : undefined;
|
|
214
|
+
if (!entry?.staticHeaders || !provider.headers) return provider;
|
|
215
|
+
const headerEntries = Object.entries(provider.headers);
|
|
216
|
+
const staticEntries = Object.entries(entry.staticHeaders);
|
|
217
|
+
if (headerEntries.length !== staticEntries.length) return provider;
|
|
218
|
+
const matchesRegistryStaticHeaders = staticEntries.every(([key, value]) => provider.headers?.[key] === value);
|
|
219
|
+
if (!matchesRegistryStaticHeaders) return provider;
|
|
220
|
+
const { headers: _headers, ...rest } = provider;
|
|
221
|
+
return rest;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Shared Desktop profile DTO builder for the management API and CLI. */
|
|
225
|
+
export async function buildClaudeDesktopState(config: OcxConfig, stored?: OcxClaudeDesktopProfile) {
|
|
226
|
+
const { filterCatalogVisibleModels, nativeContextLimits, nativeOpenAiContextWindow, desktopVisibleNativeSlugs } = await import("../../codex/catalog");
|
|
227
|
+
const { DESKTOP_SUPPORTS_1M_THRESHOLD } = await import("../../claude/desktop-3p");
|
|
228
|
+
const { reconcileDesktopProfile, renderDesktopProfile } = await import("../../claude/desktop-profile");
|
|
229
|
+
const routed = filterCatalogVisibleModels(await fetchAllModels(config), config);
|
|
230
|
+
const profileModels: DesktopProfileModel[] = [
|
|
231
|
+
// Native rows carry their real context window from the same accessor the Grok sync
|
|
232
|
+
// uses — otherwise Sol's 372k and gpt-5.5's 272k render as blank on Desktop.
|
|
233
|
+
...desktopVisibleNativeSlugs(config).map(id => {
|
|
234
|
+
const contextWindow = nativeOpenAiContextWindow(id, nativeContextLimits(config));
|
|
235
|
+
return { route: `native/${id}`, label: `${id} (native)`,
|
|
236
|
+
...(contextWindow !== undefined ? { contextWindow } : {}) };
|
|
237
|
+
}),
|
|
238
|
+
...routed.map(model => ({
|
|
239
|
+
route: `${model.provider}/${model.id}`,
|
|
240
|
+
label: `${model.id} (${model.provider})`,
|
|
241
|
+
...(typeof model.contextWindow === "number" ? { contextWindow: model.contextWindow } : {}),
|
|
242
|
+
})),
|
|
243
|
+
];
|
|
244
|
+
const profile = reconcileDesktopProfile(stored ?? config.claudeCode?.desktopProfile, profileModels);
|
|
245
|
+
if (config.claudeCode?.desktopNativeModels === false) {
|
|
246
|
+
for (const route of Object.keys(profile.assignments)) {
|
|
247
|
+
if (route.startsWith("native/")) delete profile.assignments[route];
|
|
248
|
+
}
|
|
249
|
+
for (const family of ["opus", "fable", "sonnet", "haiku"] as const) {
|
|
250
|
+
const current = profile.defaults[family];
|
|
251
|
+
if (current?.startsWith("native/")) {
|
|
252
|
+
profile.defaults[family] = Object.keys(profile.assignments)
|
|
253
|
+
.filter(route => profile.assignments[route]?.family === family)
|
|
254
|
+
.sort()[0] ?? null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const available = new Set(profileModels.map(model => model.route));
|
|
259
|
+
const modelByRoute = new Map(profileModels.map(model => [model.route, model]));
|
|
260
|
+
// Effort support: routed models with a non-empty reasoningEfforts ladder support effort;
|
|
261
|
+
// native models always support it (Anthropic native effort).
|
|
262
|
+
const effortByRoute = new Map<string, boolean>();
|
|
263
|
+
for (const m of routed) {
|
|
264
|
+
effortByRoute.set(`${m.provider}/${m.id}`, Array.isArray(m.reasoningEfforts) && m.reasoningEfforts.length > 0);
|
|
265
|
+
}
|
|
266
|
+
for (const id of desktopVisibleNativeSlugs(config)) {
|
|
267
|
+
effortByRoute.set(`native/${id}`, true);
|
|
268
|
+
}
|
|
269
|
+
const models = Object.keys(profile.assignments).sort().map(route => ({
|
|
270
|
+
route,
|
|
271
|
+
label: modelByRoute.get(route)?.label ?? route,
|
|
272
|
+
available: available.has(route),
|
|
273
|
+
...(modelByRoute.get(route)?.contextWindow ? { contextWindow: modelByRoute.get(route)!.contextWindow } : {}),
|
|
274
|
+
effortSupported: effortByRoute.get(route) ?? false,
|
|
275
|
+
// Read-only view of the 1M capability the written Desktop config already emits,
|
|
276
|
+
// derived from the SAME threshold so the dashboard chip can never disagree.
|
|
277
|
+
supports1m: (modelByRoute.get(route)?.contextWindow ?? 0) >= DESKTOP_SUPPORTS_1M_THRESHOLD,
|
|
278
|
+
assignment: profile.assignments[route]!,
|
|
279
|
+
}));
|
|
280
|
+
return {
|
|
281
|
+
profile,
|
|
282
|
+
models,
|
|
283
|
+
rendered: renderDesktopProfile(profile, profileModels),
|
|
284
|
+
port: config.port,
|
|
285
|
+
};
|
|
286
|
+
}
|