@remodex/rmx 1.0.2
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 +91 -0
- package/LICENSE +21 -0
- package/README.md +242 -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 +584 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-CZqebSPQ.css +1 -0
- package/gui/dist/assets/index-CkETtt7P.js +71 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/fonts/google-sans-cyrillic.woff2 +0 -0
- package/gui/dist/fonts/google-sans-latin.woff2 +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 +118 -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.ts +1205 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +83 -0
- package/src/adapters/client-fingerprint.ts +59 -0
- package/src/adapters/command-code.ts +453 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/cursor-errors.ts +165 -0
- package/src/adapters/cursor/discovery.ts +276 -0
- package/src/adapters/cursor/effort-map.ts +139 -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/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +153 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1235 -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 +59 -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 +548 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +604 -0
- package/src/adapters/cursor/protobuf-events.ts +735 -0
- package/src/adapters/cursor/protobuf-request.ts +719 -0
- package/src/adapters/cursor/request-builder.ts +280 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +621 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +57 -0
- package/src/adapters/cursor/types.ts +59 -0
- package/src/adapters/cursor.ts +196 -0
- package/src/adapters/google-antigravity-replay.ts +520 -0
- package/src/adapters/google-antigravity-wire.ts +140 -0
- package/src/adapters/google-errors.ts +85 -0
- package/src/adapters/google-http.ts +100 -0
- package/src/adapters/google-tool-schema.ts +173 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +859 -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 +104 -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 +1924 -0
- package/src/adapters/mimo-free.ts +263 -0
- package/src/adapters/openai-chat.ts +1265 -0
- package/src/adapters/openai-responses.ts +1309 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-catalog-nudge.ts +71 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/android-remote/assets.ts +218 -0
- package/src/android-remote/attachments.ts +168 -0
- package/src/android-remote/auth.ts +237 -0
- package/src/android-remote/cloudflare-provisioning.ts +409 -0
- package/src/android-remote/cloudflare-secret.ts +106 -0
- package/src/android-remote/cloudflare-tunnel.ts +488 -0
- package/src/android-remote/cloudflared.ts +286 -0
- package/src/android-remote/codex-app-server.ts +565 -0
- package/src/android-remote/desktop-history-page.ts +936 -0
- package/src/android-remote/desktop-ipc.ts +3643 -0
- package/src/android-remote/desktop-ownership-store.ts +98 -0
- package/src/android-remote/desktop-project-registration.ts +129 -0
- package/src/android-remote/desktop-session-stream.ts +1110 -0
- package/src/android-remote/desktop-workspace-state.ts +443 -0
- package/src/android-remote/file-change-parser.ts +112 -0
- package/src/android-remote/gateway.ts +9108 -0
- package/src/android-remote/mutation-store.ts +299 -0
- package/src/android-remote/projection.ts +1780 -0
- package/src/android-remote/queued-turn-store.ts +248 -0
- package/src/android-remote/session-command-recovery.ts +1384 -0
- package/src/android-remote/store.ts +466 -0
- package/src/android-remote/thread-reconciliation.ts +133 -0
- package/src/android-remote/thread-source-paths.ts +307 -0
- package/src/android-remote/thread-stream.ts +546 -0
- package/src/android-remote/turn-activity.ts +235 -0
- package/src/android-remote/user-message-identity.ts +94 -0
- package/src/bridge.ts +1793 -0
- package/src/chat/inbound.ts +295 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +267 -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 +189 -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 +601 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +70 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +519 -0
- package/src/claude/model-info.ts +154 -0
- package/src/claude/outbound.ts +898 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +296 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +476 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +297 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +184 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-desktop.ts +211 -0
- package/src/cli/claude.ts +302 -0
- package/src/cli/codex-shim-autorestore.ts +45 -0
- package/src/cli/codex-shim-readiness.ts +69 -0
- package/src/cli/combo.ts +124 -0
- package/src/cli/config-command.ts +183 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/desktop-first-run.ts +25 -0
- package/src/cli/doctor.ts +1022 -0
- package/src/cli/export-command.ts +201 -0
- package/src/cli/help.ts +370 -0
- package/src/cli/index.ts +1565 -0
- package/src/cli/init.ts +224 -0
- package/src/cli/integrations.ts +225 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/internal-dispatch.ts +35 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/models-runtime.ts +224 -0
- package/src/cli/models.ts +340 -0
- package/src/cli/observe.ts +170 -0
- package/src/cli/opencode.ts +587 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +476 -0
- package/src/cli/ready.ts +301 -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 +321 -0
- package/src/cli/system-command.ts +196 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +205 -0
- package/src/cli/v2.ts +200 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1109 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +34 -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 +517 -0
- package/src/codex/account-usability.ts +40 -0
- package/src/codex/admission.ts +263 -0
- package/src/codex/app-server-processes.ts +799 -0
- package/src/codex/auth-api.ts +2098 -0
- package/src/codex/auth-collision.ts +107 -0
- package/src/codex/auth-context.ts +480 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +471 -0
- package/src/codex/catalog/bundled.ts +533 -0
- package/src/codex/catalog/effort.ts +432 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +287 -0
- package/src/codex/catalog/native-models.ts +7 -0
- package/src/codex/catalog/parsing.ts +503 -0
- package/src/codex/catalog/provider-fetch.ts +2267 -0
- package/src/codex/catalog/sync.ts +1606 -0
- package/src/codex/catalog-admission.ts +197 -0
- package/src/codex/catalog-refresh-status.ts +87 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +15 -0
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +593 -0
- package/src/codex/convergence.ts +580 -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/desktop-client-processes.ts +521 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1091 -0
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +347 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +115 -0
- package/src/codex/history-provider.ts +1075 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +204 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +257 -0
- package/src/codex/inject.ts +1857 -0
- package/src/codex/injected-marker.ts +79 -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 +105 -0
- package/src/codex/journal.ts +172 -0
- package/src/codex/main-account-cache.ts +56 -0
- package/src/codex/main-account.ts +40 -0
- package/src/codex/management-convergence.ts +114 -0
- package/src/codex/model-cache.ts +267 -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 +167 -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 +348 -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 +691 -0
- package/src/codex/paths.ts +78 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +426 -0
- package/src/codex/prompt-journal.ts +311 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/provider-adoption.ts +242 -0
- package/src/codex/quota-rejection.ts +224 -0
- package/src/codex/quota.ts +494 -0
- package/src/codex/refresh.ts +60 -0
- package/src/codex/routing.ts +1855 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +1215 -0
- package/src/codex/subagent-defaults.ts +557 -0
- package/src/codex/subagent-model-fallback.ts +560 -0
- package/src/codex/sync.ts +238 -0
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +374 -0
- package/src/codex/warmup.ts +192 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +140 -0
- package/src/combos/index.ts +44 -0
- package/src/combos/request.ts +64 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +392 -0
- package/src/config.ts +3270 -0
- package/src/generated/model-metadata.ts +144 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +540 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +127 -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 +922 -0
- package/src/images/plan.ts +133 -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 +151 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +108 -0
- package/src/integrations/serialize.ts +235 -0
- package/src/integrations/state.ts +290 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer.ts +492 -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 +173 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +242 -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 +127 -0
- package/src/lib/config-ownership.ts +438 -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 +316 -0
- package/src/lib/errors.ts +364 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/open-url.ts +25 -0
- package/src/lib/pinned-http.ts +182 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-environment.ts +470 -0
- package/src/lib/provider-outbound.ts +203 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +510 -0
- package/src/lib/remodex-home.ts +616 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/service-secrets.ts +178 -0
- package/src/lib/shadow-call.ts +54 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +109 -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 +98 -0
- package/src/lib/token-estimate.ts +69 -0
- package/src/lib/translator-budget.ts +366 -0
- package/src/lib/upstream-reachability.ts +91 -0
- package/src/lib/upstream-retry.ts +508 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-elevation.ts +705 -0
- package/src/lib/windows-secret-acl.ts +817 -0
- package/src/lib/windows-user-principal.ts +283 -0
- package/src/lib/winsw.ts +402 -0
- package/src/model-sources.ts +73 -0
- package/src/oauth/anthropic-routing.ts +594 -0
- package/src/oauth/anthropic.ts +177 -0
- package/src/oauth/callback-server.ts +294 -0
- package/src/oauth/chatgpt.ts +150 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +231 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +230 -0
- package/src/oauth/health.ts +443 -0
- package/src/oauth/index.ts +1280 -0
- package/src/oauth/key-providers.ts +128 -0
- package/src/oauth/kimi.ts +213 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +121 -0
- package/src/oauth/log.ts +48 -0
- package/src/oauth/login-cli.ts +163 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +655 -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 +317 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +64 -0
- package/src/providers/codex-capacity.ts +288 -0
- package/src/providers/command-code-efforts.ts +85 -0
- package/src/providers/context-cap.ts +73 -0
- package/src/providers/derive.ts +451 -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 +361 -0
- package/src/providers/openai-sidecar.ts +235 -0
- package/src/providers/openai-tier-startup.ts +27 -0
- package/src/providers/openai-tiers.ts +301 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +179 -0
- package/src/providers/quota.ts +1942 -0
- package/src/providers/reasoning-capabilities.ts +336 -0
- package/src/providers/registry.ts +2375 -0
- package/src/providers/slug-codec.ts +74 -0
- package/src/providers/xai-transport.ts +149 -0
- package/src/reasoning-effort.ts +243 -0
- package/src/responses/compaction.ts +124 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/parser.ts +714 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +106 -0
- package/src/responses/schema.ts +159 -0
- package/src/responses/spill-store.ts +431 -0
- package/src/responses/state.ts +1039 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/router.ts +802 -0
- package/src/routing/analytics.ts +377 -0
- package/src/routing/capability.ts +205 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +444 -0
- package/src/routing/health.ts +401 -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 +424 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +686 -0
- package/src/server/adapter-resolve.ts +83 -0
- package/src/server/auth-cors.ts +606 -0
- package/src/server/chat-completions.ts +379 -0
- package/src/server/claude-messages.ts +980 -0
- package/src/server/effort-policy.ts +251 -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 +485 -0
- package/src/server/index.ts +1633 -0
- package/src/server/lifecycle.ts +482 -0
- package/src/server/live.ts +609 -0
- package/src/server/management/agent-settings-routes.ts +1180 -0
- package/src/server/management/android-remote-routes.ts +390 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +167 -0
- package/src/server/management/body.ts +35 -0
- package/src/server/management/combo-routes.ts +244 -0
- package/src/server/management/config-routes.ts +602 -0
- package/src/server/management/context.ts +88 -0
- package/src/server/management/integration-routes.ts +538 -0
- package/src/server/management/logs-usage-routes.ts +516 -0
- package/src/server/management/model-routes.ts +519 -0
- package/src/server/management/model-rows.ts +143 -0
- package/src/server/management/native-integration-routes.ts +781 -0
- package/src/server/management/oauth-account-routes.ts +573 -0
- package/src/server/management/provider-routes.ts +781 -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 +384 -0
- package/src/server/management/shared.ts +277 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +433 -0
- package/src/server/management/system-routes.ts +141 -0
- package/src/server/management/usage-summary-cache.ts +86 -0
- package/src/server/management-api.ts +269 -0
- package/src/server/management-auth.ts +353 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +326 -0
- package/src/server/proxy-stop.ts +92 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1179 -0
- package/src/server/request-decompress.ts +132 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1072 -0
- package/src/server/responses/collaboration.ts +409 -0
- package/src/server/responses/compact.ts +710 -0
- package/src/server/responses/core.ts +3561 -0
- package/src/server/responses/encrypted-payload.ts +308 -0
- package/src/server/responses/fetch-helpers.ts +171 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/policy-fallback.ts +152 -0
- package/src/server/responses/terminal-guard.ts +230 -0
- package/src/server/responses/upstream-error.ts +48 -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 +52 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses.ts +10 -0
- package/src/server/search.ts +181 -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 +308 -0
- package/src/server/startup-health-cache.ts +119 -0
- package/src/server/system-env.ts +418 -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 +470 -0
- package/src/service-manager-probe.ts +824 -0
- package/src/service.ts +3011 -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 +738 -0
- package/src/types.ts +1531 -0
- package/src/update/badge.ts +72 -0
- package/src/update/desktop-release.ts +1620 -0
- package/src/update/index.ts +402 -0
- package/src/update/job.ts +1906 -0
- package/src/update/notify.ts +261 -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/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +283 -0
- package/src/usage/log.ts +695 -0
- package/src/usage/summary.ts +585 -0
- package/src/usage/totals.ts +14 -0
- package/src/vision/anthropic-describe.ts +185 -0
- package/src/vision/describe.ts +127 -0
- package/src/vision/index.ts +558 -0
- package/src/vision/reasoning.ts +55 -0
- package/src/web-search/anthropic-executor.ts +189 -0
- package/src/web-search/executor.ts +105 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/index.ts +196 -0
- package/src/web-search/loop.ts +791 -0
- package/src/web-search/parse.ts +235 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/synthetic-tool.ts +47 -0
|
@@ -0,0 +1,377 @@
|
|
|
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, PersistedUsageAttempt } 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
|
+
]);
|
|
123
|
+
|
|
124
|
+
function percentile(sorted: number[], p: number): number | undefined {
|
|
125
|
+
if (sorted.length === 0) return undefined;
|
|
126
|
+
const index = Math.max(0, Math.ceil((p / 100) * sorted.length) - 1);
|
|
127
|
+
return sorted[Math.min(index, sorted.length - 1)];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function classifyRow(row: ScannedRow): "success" | "failure" | "cancelled" {
|
|
131
|
+
if (row.closeReason === "client_cancel" || row.status === 499) return "cancelled";
|
|
132
|
+
if (row.terminalStatus === "incomplete") return "failure";
|
|
133
|
+
if (row.terminalStatus && row.terminalStatus !== "completed") return "failure";
|
|
134
|
+
if (row.status >= 400) return "failure";
|
|
135
|
+
return "success";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function parseEntry(rowJson: string): PersistedUsageEntry | null {
|
|
139
|
+
try {
|
|
140
|
+
const parsed = JSON.parse(rowJson) as PersistedUsageEntry;
|
|
141
|
+
return parsed && typeof parsed === "object" && typeof parsed.requestId === "string" ? parsed : null;
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function attemptsOf(entry: PersistedUsageEntry | null): PersistedUsageAttempt[] | undefined {
|
|
148
|
+
return entry?.attempts;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function cooldownTriggering(entry: PersistedUsageEntry | null, status: number): boolean {
|
|
152
|
+
if (status === 429) return true;
|
|
153
|
+
const attempts = attemptsOf(entry) ?? [];
|
|
154
|
+
return attempts.some(attempt => attempt.recoveryKinds.some(kind => COOLDOWN_RECOVERY_KINDS.has(kind)));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function successCostUsd(
|
|
158
|
+
row: Pick<ScannedRow, "provider" | "model">,
|
|
159
|
+
entry: PersistedUsageEntry,
|
|
160
|
+
): number | null {
|
|
161
|
+
if (!entry.usage) return null;
|
|
162
|
+
const estimate = estimateRequestCost({
|
|
163
|
+
provider: row.provider,
|
|
164
|
+
model: row.model,
|
|
165
|
+
usage: entry.usage,
|
|
166
|
+
usageStatus: entry.usageStatus,
|
|
167
|
+
serviceTier: serviceTierContext(entry),
|
|
168
|
+
});
|
|
169
|
+
return estimate ? estimate.cost.total : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function computeRoutingAnalytics(
|
|
173
|
+
filters: RoutingAnalyticsFilters,
|
|
174
|
+
options: { maxRows?: number } = {},
|
|
175
|
+
): Promise<RoutingAnalyticsResult> {
|
|
176
|
+
await openRequestHistoryIndex();
|
|
177
|
+
const handle = requestHistoryDb();
|
|
178
|
+
const maxRows = Math.min(
|
|
179
|
+
Math.max(1, Math.trunc(options.maxRows ?? ANALYTICS_MAX_ROWS)),
|
|
180
|
+
ANALYTICS_MAX_ROWS,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const where: string[] = [];
|
|
184
|
+
const values: Array<string | number> = [];
|
|
185
|
+
const add = (clause: string, value: string | number) => {
|
|
186
|
+
where.push(clause);
|
|
187
|
+
values.push(value);
|
|
188
|
+
};
|
|
189
|
+
if (filters.provider !== undefined) add("provider = ?", filters.provider);
|
|
190
|
+
if (filters.model !== undefined) add("model = ?", filters.model);
|
|
191
|
+
if (filters.profileId !== undefined) add("profile_id = ?", filters.profileId);
|
|
192
|
+
if (filters.surface !== undefined) add("surface = ?", filters.surface);
|
|
193
|
+
if (filters.from !== undefined) add("timestamp >= ?", filters.from);
|
|
194
|
+
if (filters.to !== undefined) add("timestamp <= ?", filters.to);
|
|
195
|
+
const whereSql = where.length > 0 ? ` WHERE ${where.join(" AND ")}` : "";
|
|
196
|
+
|
|
197
|
+
const rows = handle.query(
|
|
198
|
+
`SELECT provider, model, api_key_id AS apiKeyId, profile_id AS profileId,
|
|
199
|
+
profile_revision AS profileRevision, status,
|
|
200
|
+
duration_ms AS durationMs, first_output_ms AS firstOutputMs,
|
|
201
|
+
close_reason AS closeReason, terminal_status AS terminalStatus,
|
|
202
|
+
usage_status AS usageStatus, usage_json AS usageJson,
|
|
203
|
+
attempt_count AS attemptCount, fallback, row_json AS rowJson
|
|
204
|
+
FROM requests${whereSql} ORDER BY timestamp DESC LIMIT ?`,
|
|
205
|
+
).all(...values, maxRows + 1) as ScannedRow[];
|
|
206
|
+
|
|
207
|
+
const scanned = rows.slice(0, maxRows);
|
|
208
|
+
const historyTruncated = rows.length > maxRows;
|
|
209
|
+
|
|
210
|
+
let successes = 0;
|
|
211
|
+
let failures = 0;
|
|
212
|
+
let cancelled = 0;
|
|
213
|
+
let fallbacks = 0;
|
|
214
|
+
let totalAttempts = 0;
|
|
215
|
+
let incompleteStreams = 0;
|
|
216
|
+
let cooldownFailures = 0;
|
|
217
|
+
let usageReported = 0;
|
|
218
|
+
const durations: number[] = [];
|
|
219
|
+
const firstOutputs: number[] = [];
|
|
220
|
+
let costTotalUsd = 0;
|
|
221
|
+
let costCount = 0;
|
|
222
|
+
|
|
223
|
+
const byKey = new Map<string, Bucket>();
|
|
224
|
+
const byProfile = new Map<string, AnalyticsProfileRow>();
|
|
225
|
+
|
|
226
|
+
for (const row of scanned) {
|
|
227
|
+
const kind = classifyRow(row);
|
|
228
|
+
if (kind === "success") successes += 1;
|
|
229
|
+
else if (kind === "failure") failures += 1;
|
|
230
|
+
else cancelled += 1;
|
|
231
|
+
if (row.fallback === 1) fallbacks += 1;
|
|
232
|
+
totalAttempts += row.attemptCount;
|
|
233
|
+
if (row.terminalStatus === "incomplete") incompleteStreams += 1;
|
|
234
|
+
durations.push(row.durationMs);
|
|
235
|
+
if (row.firstOutputMs !== null && row.firstOutputMs !== undefined && row.firstOutputMs >= 0) {
|
|
236
|
+
firstOutputs.push(row.firstOutputMs);
|
|
237
|
+
}
|
|
238
|
+
if (row.usageStatus !== "unreported") usageReported += 1;
|
|
239
|
+
|
|
240
|
+
let rowCostUsd: number | null = null;
|
|
241
|
+
if (kind === "success") {
|
|
242
|
+
const entry = parseEntry(row.rowJson);
|
|
243
|
+
if (entry) {
|
|
244
|
+
rowCostUsd = successCostUsd(row, entry);
|
|
245
|
+
if (rowCostUsd !== null) {
|
|
246
|
+
costTotalUsd += rowCostUsd;
|
|
247
|
+
costCount += 1;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (kind === "failure") {
|
|
253
|
+
const failureEntry = parseEntry(row.rowJson);
|
|
254
|
+
if (cooldownTriggering(failureEntry, row.status)) cooldownFailures += 1;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const key = `${row.provider}\0${row.model}\0${row.apiKeyId ?? ""}\0${row.profileId ?? ""}`;
|
|
258
|
+
let bucket: Bucket | undefined = byKey.get(key);
|
|
259
|
+
if (!bucket) {
|
|
260
|
+
bucket = {
|
|
261
|
+
provider: row.provider,
|
|
262
|
+
model: row.model,
|
|
263
|
+
...(row.apiKeyId ? { accountRef: row.apiKeyId } : {}),
|
|
264
|
+
...(row.profileId ? { profileId: row.profileId } : {}),
|
|
265
|
+
requests: 0,
|
|
266
|
+
successes: 0,
|
|
267
|
+
failures: 0,
|
|
268
|
+
cancelled: 0,
|
|
269
|
+
successRate: null,
|
|
270
|
+
durations: [],
|
|
271
|
+
costUsdSum: 0,
|
|
272
|
+
costRows: 0,
|
|
273
|
+
};
|
|
274
|
+
byKey.set(key, bucket);
|
|
275
|
+
}
|
|
276
|
+
bucket.requests += 1;
|
|
277
|
+
if (kind === "success") bucket.successes += 1;
|
|
278
|
+
else if (kind === "failure") bucket.failures += 1;
|
|
279
|
+
else bucket.cancelled += 1;
|
|
280
|
+
bucket.durations.push(row.durationMs);
|
|
281
|
+
if (rowCostUsd !== null) {
|
|
282
|
+
bucket.costUsdSum += rowCostUsd;
|
|
283
|
+
bucket.costRows += 1;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (row.profileId) {
|
|
287
|
+
let profile = byProfile.get(row.profileId);
|
|
288
|
+
if (!profile) {
|
|
289
|
+
profile = {
|
|
290
|
+
profileId: row.profileId,
|
|
291
|
+
...(row.profileRevision ? { profileRevision: row.profileRevision } : {}),
|
|
292
|
+
requests: 0,
|
|
293
|
+
successes: 0,
|
|
294
|
+
failures: 0,
|
|
295
|
+
fallbacks: 0,
|
|
296
|
+
successRate: null,
|
|
297
|
+
};
|
|
298
|
+
byProfile.set(row.profileId, profile);
|
|
299
|
+
}
|
|
300
|
+
profile.requests += 1;
|
|
301
|
+
if (kind === "success") profile.successes += 1;
|
|
302
|
+
else if (kind === "failure") profile.failures += 1;
|
|
303
|
+
if (row.fallback === 1) profile.fallbacks += 1;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
durations.sort((a, b) => a - b);
|
|
308
|
+
firstOutputs.sort((a, b) => a - b);
|
|
309
|
+
const total = scanned.length;
|
|
310
|
+
const rate = (count: number): number | null => (total > 0 ? count / total : null);
|
|
311
|
+
|
|
312
|
+
const breakdown: AnalyticsBreakdownRow[] = [...byKey.values()].map(bucket => {
|
|
313
|
+
const sorted = bucket.durations.sort((a, b) => a - b);
|
|
314
|
+
const p50DurationMs = percentile(sorted, 50);
|
|
315
|
+
return {
|
|
316
|
+
provider: bucket.provider,
|
|
317
|
+
model: bucket.model,
|
|
318
|
+
...(bucket.accountRef ? { accountRef: bucket.accountRef } : {}),
|
|
319
|
+
...(bucket.profileId ? { profileId: bucket.profileId } : {}),
|
|
320
|
+
requests: bucket.requests,
|
|
321
|
+
successes: bucket.successes,
|
|
322
|
+
failures: bucket.failures,
|
|
323
|
+
cancelled: bucket.cancelled,
|
|
324
|
+
successRate: bucket.requests > 0 ? bucket.successes / bucket.requests : null,
|
|
325
|
+
...(p50DurationMs !== undefined ? { p50DurationMs } : {}),
|
|
326
|
+
...(bucket.requests > 0
|
|
327
|
+
? { estimatedCostUsdPerSuccessfulRequest: bucket.costRows > 0
|
|
328
|
+
? bucket.costUsdSum / bucket.costRows
|
|
329
|
+
: null }
|
|
330
|
+
: {}),
|
|
331
|
+
};
|
|
332
|
+
}).sort((a, b) => b.requests - a.requests);
|
|
333
|
+
|
|
334
|
+
const profileBreakdown: AnalyticsProfileRow[] = [...byProfile.values()].map(profile => ({
|
|
335
|
+
...profile,
|
|
336
|
+
successRate: profile.requests > 0 ? profile.successes / profile.requests : null,
|
|
337
|
+
})).sort((a, b) => b.requests - a.requests);
|
|
338
|
+
|
|
339
|
+
const confidence: AnalyticsConfidence | null = total === 0
|
|
340
|
+
? null
|
|
341
|
+
: total >= 100 ? "high" : total >= 20 ? "medium" : "low";
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
generatedAt: Date.now(),
|
|
345
|
+
totalRequests: total,
|
|
346
|
+
scannedRows: scanned.length,
|
|
347
|
+
historyTruncated,
|
|
348
|
+
confidence,
|
|
349
|
+
successRate: rate(successes),
|
|
350
|
+
failureRate: rate(failures),
|
|
351
|
+
cancelledRate: rate(cancelled),
|
|
352
|
+
fallbackRate: rate(fallbacks),
|
|
353
|
+
totalAttempts,
|
|
354
|
+
averageAttemptsPerRequest: total > 0 ? totalAttempts / total : null,
|
|
355
|
+
incompleteStreamRate: rate(incompleteStreams),
|
|
356
|
+
cooldownTriggeringFailures: cooldownFailures,
|
|
357
|
+
durationMs: {
|
|
358
|
+
...(percentile(durations, 50) !== undefined ? { p50: percentile(durations, 50) } : {}),
|
|
359
|
+
...(percentile(durations, 95) !== undefined ? { p95: percentile(durations, 95) } : {}),
|
|
360
|
+
...(percentile(durations, 99) !== undefined ? { p99: percentile(durations, 99) } : {}),
|
|
361
|
+
sampleCount: durations.length,
|
|
362
|
+
},
|
|
363
|
+
firstOutputMs: {
|
|
364
|
+
...(percentile(firstOutputs, 50) !== undefined ? { p50: percentile(firstOutputs, 50) } : {}),
|
|
365
|
+
...(percentile(firstOutputs, 95) !== undefined ? { p95: percentile(firstOutputs, 95) } : {}),
|
|
366
|
+
...(percentile(firstOutputs, 99) !== undefined ? { p99: percentile(firstOutputs, 99) } : {}),
|
|
367
|
+
sampleCount: firstOutputs.length,
|
|
368
|
+
coverage: total > 0 ? firstOutputs.length / total : null,
|
|
369
|
+
},
|
|
370
|
+
estimatedCostUsdPerSuccessfulRequest: costCount > 0 ? costTotalUsd / costCount : null,
|
|
371
|
+
estimatedCostUsdTotalSuccessful: costCount > 0 ? costTotalUsd : null,
|
|
372
|
+
usageCoverage: total > 0 ? usageReported / total : null,
|
|
373
|
+
priceCoverage: successes > 0 ? costCount / successes : null,
|
|
374
|
+
breakdown,
|
|
375
|
+
profileBreakdown,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
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 type { OcxConfig } from "../types";
|
|
14
|
+
import { isCanonicalOpenAiForwardProvider } from "../providers/openai-tiers";
|
|
15
|
+
import { PROVIDER_REGISTRY } from "../providers/registry";
|
|
16
|
+
import {
|
|
17
|
+
nativeInputModalities,
|
|
18
|
+
nativeOpenAiContextWindow,
|
|
19
|
+
nativeParallelToolCalls,
|
|
20
|
+
nativeReasoningEfforts,
|
|
21
|
+
} from "../codex/catalog/metadata";
|
|
22
|
+
import { readCatalog, readCodexCatalogPath } from "../codex/catalog/parsing";
|
|
23
|
+
import { statSync } from "node:fs";
|
|
24
|
+
import type { RouteCapabilityEvidence } from "./trace";
|
|
25
|
+
|
|
26
|
+
type CatalogModelRow = {
|
|
27
|
+
provider: string;
|
|
28
|
+
id: string;
|
|
29
|
+
contextWindow?: number;
|
|
30
|
+
inputModalities?: string[];
|
|
31
|
+
reasoningEfforts?: string[];
|
|
32
|
+
capabilities?: string[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Catalog rows memoized by path + mtime: the cached Codex catalog is stable
|
|
37
|
+
* between refreshes, and re-reading/parsing the whole file per candidate on
|
|
38
|
+
* the request path would multiply a synchronous disk + JSON cost by the
|
|
39
|
+
* profile candidate count for every policy-routed request.
|
|
40
|
+
*/
|
|
41
|
+
let catalogCache: { path: string; mtimeMs: number; rows: CatalogModelRow[] } | null = null;
|
|
42
|
+
|
|
43
|
+
function cachedCatalogModels(): CatalogModelRow[] {
|
|
44
|
+
try {
|
|
45
|
+
const path = readCodexCatalogPath();
|
|
46
|
+
const mtimeMs = statSync(path).mtimeMs;
|
|
47
|
+
if (catalogCache && catalogCache.path === path && catalogCache.mtimeMs === mtimeMs) {
|
|
48
|
+
return catalogCache.rows;
|
|
49
|
+
}
|
|
50
|
+
const catalog = readCatalog(path);
|
|
51
|
+
const models = catalog?.models;
|
|
52
|
+
if (!Array.isArray(models)) return [];
|
|
53
|
+
const rows = models
|
|
54
|
+
.filter((model): model is Record<string, unknown> & { id: string; provider: string } =>
|
|
55
|
+
typeof model === "object" && model !== null && typeof model.id === "string" && typeof model.provider === "string")
|
|
56
|
+
.map(model => ({
|
|
57
|
+
provider: model.provider,
|
|
58
|
+
id: model.id,
|
|
59
|
+
...(typeof model.contextWindow === "number" ? { contextWindow: model.contextWindow } : {}),
|
|
60
|
+
...(Array.isArray(model.inputModalities)
|
|
61
|
+
? { inputModalities: model.inputModalities.filter((value): value is string => typeof value === "string") }
|
|
62
|
+
: {}),
|
|
63
|
+
...(Array.isArray(model.reasoningEfforts)
|
|
64
|
+
? { reasoningEfforts: model.reasoningEfforts.filter((value): value is string => typeof value === "string") }
|
|
65
|
+
: {}),
|
|
66
|
+
...(Array.isArray(model.capabilities)
|
|
67
|
+
? { capabilities: model.capabilities.filter((value): value is string => typeof value === "string") }
|
|
68
|
+
: {}),
|
|
69
|
+
}));
|
|
70
|
+
catalogCache = { path, mtimeMs, rows };
|
|
71
|
+
return rows;
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Classify a hostname for locality evidence. `URL.hostname` keeps IPv6
|
|
79
|
+
* literals bracketed (`[::1]`), so strip the brackets before matching.
|
|
80
|
+
* Anything not positively local or private stays unknown: "unknown is not
|
|
81
|
+
* zero", so an unrecognized host must never assert `remoteAllowed`.
|
|
82
|
+
*/
|
|
83
|
+
function classifyHostname(hostname: string): "local" | "private" | null {
|
|
84
|
+
const host = hostname.trim().toLowerCase().replace(/\.$/, "").replace(/^\[|\]$/g, "");
|
|
85
|
+
if (host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0") return "local";
|
|
86
|
+
if (host === "::1" || /^127\./.test(host)) return "local";
|
|
87
|
+
if (/^10\./.test(host)
|
|
88
|
+
|| /^192\.168\./.test(host)
|
|
89
|
+
|| /^169\.254\./.test(host)
|
|
90
|
+
|| /^172\.(1[6-9]|2\d|3[01])\./.test(host)
|
|
91
|
+
|| /^f[cd][0-9a-f]{2}:/.test(host)
|
|
92
|
+
|| /^fe80:/.test(host)
|
|
93
|
+
|| /^::ffff:(?:10\.|127\.|192\.168\.|169\.254\.|172\.(?:1[6-9]|2\d|3[01])\.)/.test(host)) {
|
|
94
|
+
return "private";
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Adapters whose upstream protocol supports function/tool calling. Mirrors
|
|
101
|
+
* the adapter ids the resolver accepts (including the `azure` alias for
|
|
102
|
+
* `azure-openai`); `kiro` and `mimo-free` send/delegate tool calls.
|
|
103
|
+
*/
|
|
104
|
+
const TOOL_CAPABLE_ADAPTERS = new Set([
|
|
105
|
+
"openai-chat",
|
|
106
|
+
"openai-responses",
|
|
107
|
+
"anthropic",
|
|
108
|
+
"cursor",
|
|
109
|
+
"google",
|
|
110
|
+
"azure-openai",
|
|
111
|
+
"azure",
|
|
112
|
+
"kiro",
|
|
113
|
+
"mimo-free",
|
|
114
|
+
"command-code",
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
function localRemoteEvidence(baseUrl: string | undefined): Pick<RouteCapabilityEvidence, "localOnly" | "remoteAllowed"> {
|
|
118
|
+
if (typeof baseUrl !== "string" || baseUrl.length === 0) return {};
|
|
119
|
+
try {
|
|
120
|
+
const hostname = new URL(baseUrl).hostname;
|
|
121
|
+
if (!hostname) return {};
|
|
122
|
+
const kind = classifyHostname(hostname);
|
|
123
|
+
if (kind === null) return {};
|
|
124
|
+
// Both booleans are emitted once classified: definitive negative evidence,
|
|
125
|
+
// so a local host cannot satisfy `require.remoteAllowed` (or vice versa)
|
|
126
|
+
// under `unknownEvidence.capability: "allow"`/`"penalize"`.
|
|
127
|
+
return kind === "local" || kind === "private"
|
|
128
|
+
? { localOnly: true, remoteAllowed: false }
|
|
129
|
+
: { remoteAllowed: true, localOnly: false };
|
|
130
|
+
} catch {
|
|
131
|
+
return {};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Assemble canonical capability evidence for one `provider/model` candidate.
|
|
137
|
+
* Sources (in priority order): provider config maps, provider registry hints,
|
|
138
|
+
* cached Codex catalog row, native-model metadata.
|
|
139
|
+
*/
|
|
140
|
+
export function candidateCapabilityEvidence(
|
|
141
|
+
config: OcxConfig,
|
|
142
|
+
providerName: string,
|
|
143
|
+
modelId: string,
|
|
144
|
+
): RouteCapabilityEvidence {
|
|
145
|
+
const provider = config.providers[providerName];
|
|
146
|
+
const registryEntry = PROVIDER_REGISTRY.find(entry => entry.id === providerName);
|
|
147
|
+
const catalogRow = cachedCatalogModels().find(model => model.provider === providerName && model.id === modelId);
|
|
148
|
+
const isNative = providerName === "openai" && !modelId.includes("/");
|
|
149
|
+
|
|
150
|
+
const contextWindow = provider?.modelContextWindows?.[modelId]
|
|
151
|
+
?? provider?.contextWindow
|
|
152
|
+
?? registryEntry?.modelContextWindows?.[modelId]
|
|
153
|
+
?? catalogRow?.contextWindow
|
|
154
|
+
?? (isNative ? nativeOpenAiContextWindow(modelId) : undefined);
|
|
155
|
+
|
|
156
|
+
const modalities = provider?.modelInputModalities?.[modelId]
|
|
157
|
+
?? registryEntry?.modelInputModalities?.[modelId]
|
|
158
|
+
?? catalogRow?.inputModalities
|
|
159
|
+
?? (isNative ? nativeInputModalities(modelId) : undefined);
|
|
160
|
+
const image = Array.isArray(modalities)
|
|
161
|
+
? modalities.includes("image")
|
|
162
|
+
: undefined;
|
|
163
|
+
|
|
164
|
+
const capabilities = catalogRow?.capabilities ?? [];
|
|
165
|
+
// The catalog `capabilities` list is a positive per-model signal; a row
|
|
166
|
+
// without "tools" is treated as unknown, never as a negative. Without a
|
|
167
|
+
// catalog row the adapter protocol itself is the signal: tool-capable
|
|
168
|
+
// adapters run single tool calls even when the parallel-call opt-in is
|
|
169
|
+
// unset or false. `parallelToolCalls` stays a positive provider-level
|
|
170
|
+
// override.
|
|
171
|
+
const tools = capabilities.includes("tools")
|
|
172
|
+
|| isNative
|
|
173
|
+
|| (catalogRow === undefined && provider !== undefined && TOOL_CAPABLE_ADAPTERS.has(provider.adapter))
|
|
174
|
+
|| provider?.parallelToolCalls === true
|
|
175
|
+
|| undefined;
|
|
176
|
+
|
|
177
|
+
const reasoningEfforts = provider?.modelReasoningEfforts?.[modelId]
|
|
178
|
+
?? registryEntry?.modelReasoningEfforts?.[modelId]
|
|
179
|
+
?? catalogRow?.reasoningEfforts
|
|
180
|
+
?? (isNative ? nativeReasoningEfforts(modelId) : undefined);
|
|
181
|
+
|
|
182
|
+
const tierSupport = provider?.supportsServiceTier
|
|
183
|
+
?? registryEntry?.supportsServiceTier;
|
|
184
|
+
const serviceTier = tierSupport === true
|
|
185
|
+
? "supported"
|
|
186
|
+
: tierSupport === false ? "unsupported" : "unknown";
|
|
187
|
+
|
|
188
|
+
const localRemote = localRemoteEvidence(provider?.baseUrl);
|
|
189
|
+
// Only emit a definitive encryptedCodexTasks value when the provider is
|
|
190
|
+
// present. An absent/unconfigured provider must stay unknown so
|
|
191
|
+
// require.encryptedCodexTasks does not fail closed on missing config.
|
|
192
|
+
const encryptedCodexTasks = provider === undefined
|
|
193
|
+
? undefined
|
|
194
|
+
: isCanonicalOpenAiForwardProvider(provider);
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
...(typeof contextWindow === "number" ? { contextWindow } : {}),
|
|
198
|
+
...(typeof image === "boolean" ? { image } : {}),
|
|
199
|
+
...(typeof tools === "boolean" ? { tools } : {}),
|
|
200
|
+
...(reasoningEfforts !== undefined && reasoningEfforts.length > 0 ? { reasoningEfforts } : {}),
|
|
201
|
+
...(serviceTier !== "unknown" ? { serviceTier } : {}),
|
|
202
|
+
...localRemote,
|
|
203
|
+
...(typeof encryptedCodexTasks === "boolean" ? { encryptedCodexTasks } : {}),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost-aware policy scoring and limits (RI-08).
|
|
3
|
+
*
|
|
4
|
+
* Reuses the canonical price/cost normalization (`src/usage/cost.ts`).
|
|
5
|
+
* Evidence distinguishes estimated vs authoritative usage and registry vs
|
|
6
|
+
* expected prices. Unknown prices stay unknown (never free); the profile's
|
|
7
|
+
* `unknownEvidence.cost` policy decides how unknown evidence is handled.
|
|
8
|
+
*
|
|
9
|
+
* No billing, invoicing, or hidden budgets - a hard per-request ceiling
|
|
10
|
+
* (`limits.maxEstimatedCostUsd`) is evaluated deterministically.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { OcxUsage } from "../types";
|
|
14
|
+
import type { UsageStatus } from "../usage/log";
|
|
15
|
+
import { estimateRequestCost, type ServiceTierInput } from "../usage/cost";
|
|
16
|
+
import type { RouteCostEvidence } from "./trace";
|
|
17
|
+
|
|
18
|
+
/** Reference cost (USD) for the relative cost score when no limit is set. */
|
|
19
|
+
export const COST_SCORE_REFERENCE_USD = 1.0;
|
|
20
|
+
|
|
21
|
+
export interface CostEvidenceInput {
|
|
22
|
+
provider: string;
|
|
23
|
+
model: string;
|
|
24
|
+
usage?: OcxUsage;
|
|
25
|
+
usageStatus?: UsageStatus;
|
|
26
|
+
serviceTier?: ServiceTierInput;
|
|
27
|
+
limitUsd?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Assemble cost evidence from the canonical price model. Returns unknown-ish
|
|
32
|
+
* evidence (`incomplete: true`, no estimate) when usage or a price is
|
|
33
|
+
* missing - never a fabricated zero.
|
|
34
|
+
*/
|
|
35
|
+
export function costEvidenceForCandidate(input: CostEvidenceInput): RouteCostEvidence {
|
|
36
|
+
const limitUsd = input.limitUsd;
|
|
37
|
+
if (!input.usage) {
|
|
38
|
+
return {
|
|
39
|
+
...(limitUsd !== undefined ? { limitUsd } : {}),
|
|
40
|
+
incomplete: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const estimate = estimateRequestCost({
|
|
44
|
+
provider: input.provider,
|
|
45
|
+
model: input.model,
|
|
46
|
+
usage: input.usage,
|
|
47
|
+
usageStatus: input.usageStatus ?? "estimated",
|
|
48
|
+
...(input.serviceTier ? { serviceTier: input.serviceTier } : {}),
|
|
49
|
+
});
|
|
50
|
+
if (!estimate) {
|
|
51
|
+
return {
|
|
52
|
+
...(limitUsd !== undefined ? { limitUsd } : {}),
|
|
53
|
+
incomplete: true,
|
|
54
|
+
priceSource: "unmatched",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const priceSource = estimate.price?.source ?? "registry";
|
|
58
|
+
return {
|
|
59
|
+
estimatedUsd: estimate.cost.total,
|
|
60
|
+
priceSource,
|
|
61
|
+
incomplete: estimate.estimated || priceSource === "expected",
|
|
62
|
+
...(limitUsd !== undefined ? { limitUsd } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Deterministic cost score in [0,1]: cheaper is better, relative to
|
|
68
|
+
* `COST_SCORE_REFERENCE_USD` (or the profile limit when set). Unknown
|
|
69
|
+
* estimates return null so the profile's unknownEvidence policy applies.
|
|
70
|
+
*/
|
|
71
|
+
export function costScore(evidence: RouteCostEvidence | undefined): number | null {
|
|
72
|
+
if (evidence?.estimatedUsd === undefined || !Number.isFinite(evidence.estimatedUsd)) return null;
|
|
73
|
+
const reference = typeof evidence.limitUsd === "number" && evidence.limitUsd > 0
|
|
74
|
+
? evidence.limitUsd
|
|
75
|
+
: COST_SCORE_REFERENCE_USD;
|
|
76
|
+
return Math.max(0, Math.min(1, 1 - evidence.estimatedUsd / reference));
|
|
77
|
+
}
|