@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,263 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type {
|
|
3
|
+
OcxClaudeDesktopAssignment,
|
|
4
|
+
OcxClaudeDesktopFamily,
|
|
5
|
+
OcxClaudeDesktopProfile,
|
|
6
|
+
} from "../types";
|
|
7
|
+
|
|
8
|
+
export const DESKTOP_FAMILIES = ["opus", "fable", "sonnet", "haiku"] as const;
|
|
9
|
+
export type DesktopFamily = OcxClaudeDesktopFamily;
|
|
10
|
+
export type DesktopProfile = OcxClaudeDesktopProfile;
|
|
11
|
+
|
|
12
|
+
export interface DesktopProfileModel {
|
|
13
|
+
route: string;
|
|
14
|
+
label: string;
|
|
15
|
+
contextWindow?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RenderedDesktopModel extends DesktopProfileModel {
|
|
19
|
+
name: string;
|
|
20
|
+
family: DesktopFamily;
|
|
21
|
+
isFamilyDefault: boolean;
|
|
22
|
+
supports1m: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const DATE_ALIAS = /^claude-opus-4-8-(2026\d{4})$/;
|
|
26
|
+
const DAY_COUNT_2026 = 365;
|
|
27
|
+
|
|
28
|
+
export class DesktopProfileError extends Error {
|
|
29
|
+
constructor(message: string, readonly path = "profile") {
|
|
30
|
+
super(`${path}: ${message}`);
|
|
31
|
+
this.name = "DesktopProfileError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function emptyDesktopProfile(): DesktopProfile {
|
|
36
|
+
return {
|
|
37
|
+
version: 1,
|
|
38
|
+
assignments: {},
|
|
39
|
+
defaults: { opus: null, fable: null, sonnet: null, haiku: null },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
44
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function assertExactKeys(value: Record<string, unknown>, keys: readonly string[], path: string): void {
|
|
48
|
+
const allowed = new Set(keys);
|
|
49
|
+
for (const key of Object.keys(value)) {
|
|
50
|
+
if (!allowed.has(key)) throw new DesktopProfileError(`unknown field "${key}"`, path);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Applied-state markers survive every profile rebuild.
|
|
56
|
+
*
|
|
57
|
+
* `parseDesktopProfile`, `reconcileDesktopProfile` and `moveDesktopRoute` each construct a
|
|
58
|
+
* fresh `{ version, assignments, defaults }`, and the management routes persist whatever they
|
|
59
|
+
* return. Without this carry-through, saving an assignment — or merely dragging a model to
|
|
60
|
+
* another family — would erase the fingerprint the apply route wrote, and the GUI would report
|
|
61
|
+
* "not applied" for a config that is applied on disk.
|
|
62
|
+
*/
|
|
63
|
+
function appliedMarkers(source: { appliedFingerprint?: unknown; appliedAt?: unknown }): {
|
|
64
|
+
appliedFingerprint?: string;
|
|
65
|
+
appliedAt?: string;
|
|
66
|
+
} {
|
|
67
|
+
return {
|
|
68
|
+
...(typeof source.appliedFingerprint === "string" ? { appliedFingerprint: source.appliedFingerprint } : {}),
|
|
69
|
+
...(typeof source.appliedAt === "string" ? { appliedAt: source.appliedAt } : {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isFamily(value: unknown): value is DesktopFamily {
|
|
74
|
+
return typeof value === "string" && (DESKTOP_FAMILIES as readonly string[]).includes(value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function routeModelId(route: string): string {
|
|
78
|
+
const slash = route.indexOf("/");
|
|
79
|
+
return slash >= 0 ? route.slice(slash + 1) : route;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isRealAnthropicRoute(route: string): boolean {
|
|
83
|
+
return route.startsWith("anthropic/claude-");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function validDateAlias(alias: string): boolean {
|
|
87
|
+
const match = DATE_ALIAS.exec(alias);
|
|
88
|
+
if (!match) return false;
|
|
89
|
+
const year = Number(match[1]!.slice(0, 4));
|
|
90
|
+
const month = Number(match[1]!.slice(4, 6));
|
|
91
|
+
const day = Number(match[1]!.slice(6, 8));
|
|
92
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
93
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function parseDesktopProfile(value: unknown): DesktopProfile {
|
|
97
|
+
if (!isPlainObject(value)) throw new DesktopProfileError("must be an object");
|
|
98
|
+
// `appliedFingerprint`/`appliedAt` are written back by the apply route once a profile
|
|
99
|
+
// reaches Claude Desktop (see server/management/agent-settings-routes.ts). Rejecting them
|
|
100
|
+
// here made every reload after the first apply fail with `unknown field`.
|
|
101
|
+
assertExactKeys(value, ["version", "assignments", "defaults", "appliedFingerprint", "appliedAt"], "profile");
|
|
102
|
+
if (value.version !== 1) throw new DesktopProfileError("version must be 1", "profile.version");
|
|
103
|
+
if (!isPlainObject(value.assignments)) throw new DesktopProfileError("must be an object", "profile.assignments");
|
|
104
|
+
if (!isPlainObject(value.defaults)) throw new DesktopProfileError("must be an object", "profile.defaults");
|
|
105
|
+
assertExactKeys(value.defaults, DESKTOP_FAMILIES, "profile.defaults");
|
|
106
|
+
if (value.appliedFingerprint !== undefined && typeof value.appliedFingerprint !== "string") {
|
|
107
|
+
throw new DesktopProfileError("must be a string", "profile.appliedFingerprint");
|
|
108
|
+
}
|
|
109
|
+
if (value.appliedAt !== undefined && typeof value.appliedAt !== "string") {
|
|
110
|
+
throw new DesktopProfileError("must be a string", "profile.appliedAt");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const assignments: Record<string, OcxClaudeDesktopAssignment> = {};
|
|
114
|
+
const aliases = new Set<string>();
|
|
115
|
+
for (const [route, raw] of Object.entries(value.assignments)) {
|
|
116
|
+
if (!route.trim() || !route.includes("/")) throw new DesktopProfileError("route must be provider/model", `profile.assignments.${route || "<empty>"}`);
|
|
117
|
+
if (!isPlainObject(raw)) throw new DesktopProfileError("must be an object", `profile.assignments.${route}`);
|
|
118
|
+
assertExactKeys(raw, ["family", "alias"], `profile.assignments.${route}`);
|
|
119
|
+
if (!isFamily(raw.family)) throw new DesktopProfileError("unknown family", `profile.assignments.${route}.family`);
|
|
120
|
+
if (typeof raw.alias !== "string" || !raw.alias) throw new DesktopProfileError("must be a non-empty string", `profile.assignments.${route}.alias`);
|
|
121
|
+
if (isRealAnthropicRoute(route)) {
|
|
122
|
+
if (raw.alias !== routeModelId(route)) throw new DesktopProfileError("real Anthropic routes must keep their exact model id", `profile.assignments.${route}.alias`);
|
|
123
|
+
} else if (!validDateAlias(raw.alias)) {
|
|
124
|
+
throw new DesktopProfileError("must be a valid claude-opus-4-8-2026MMDD alias", `profile.assignments.${route}.alias`);
|
|
125
|
+
}
|
|
126
|
+
if (aliases.has(raw.alias)) throw new DesktopProfileError(`duplicate alias "${raw.alias}"`, `profile.assignments.${route}.alias`);
|
|
127
|
+
aliases.add(raw.alias);
|
|
128
|
+
assignments[route] = { family: raw.family, alias: raw.alias };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const defaults = {} as DesktopProfile["defaults"];
|
|
132
|
+
for (const family of DESKTOP_FAMILIES) {
|
|
133
|
+
const route = value.defaults[family];
|
|
134
|
+
if (route !== null && typeof route !== "string") throw new DesktopProfileError("must be a route or null", `profile.defaults.${family}`);
|
|
135
|
+
const members = Object.keys(assignments).filter(key => assignments[key]!.family === family).sort();
|
|
136
|
+
if (members.length === 0) {
|
|
137
|
+
if (route !== null) throw new DesktopProfileError("must be null for an empty family", `profile.defaults.${family}`);
|
|
138
|
+
defaults[family] = null;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (typeof route !== "string" || !assignments[route] || assignments[route]!.family !== family) {
|
|
142
|
+
throw new DesktopProfileError("must reference a member of this family", `profile.defaults.${family}`);
|
|
143
|
+
}
|
|
144
|
+
defaults[family] = route;
|
|
145
|
+
}
|
|
146
|
+
return { version: 1, assignments, defaults, ...appliedMarkers(value) };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function dayOfYearAlias(dayIndex: number): string {
|
|
150
|
+
const date = new Date(Date.UTC(2026, 0, dayIndex + 1));
|
|
151
|
+
const y = date.getUTCFullYear();
|
|
152
|
+
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
153
|
+
const d = String(date.getUTCDate()).padStart(2, "0");
|
|
154
|
+
return `claude-opus-4-8-${y}${m}${d}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function routeStartDay(route: string): number {
|
|
158
|
+
return createHash("sha256").update(route).digest().readUInt32BE(0) % DAY_COUNT_2026;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function allocateAlias(route: string, used: Set<string>): string {
|
|
162
|
+
if (isRealAnthropicRoute(route)) return routeModelId(route);
|
|
163
|
+
const start = routeStartDay(route);
|
|
164
|
+
for (let offset = 0; offset < DAY_COUNT_2026; offset += 1) {
|
|
165
|
+
const alias = dayOfYearAlias((start + offset) % DAY_COUNT_2026);
|
|
166
|
+
if (!used.has(alias)) return alias;
|
|
167
|
+
}
|
|
168
|
+
throw new DesktopProfileError("all 365 encoded date slots are occupied", `profile.assignments.${route}.alias`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function reconcileDesktopProfile(
|
|
172
|
+
stored: unknown,
|
|
173
|
+
models: readonly DesktopProfileModel[],
|
|
174
|
+
): DesktopProfile {
|
|
175
|
+
const profile = stored === undefined || stored === null ? emptyDesktopProfile() : parseDesktopProfile(stored);
|
|
176
|
+
const assignments: DesktopProfile["assignments"] = Object.fromEntries(
|
|
177
|
+
Object.entries(profile.assignments).map(([route, assignment]) => [route, { ...assignment }]),
|
|
178
|
+
);
|
|
179
|
+
const used = new Set(Object.values(assignments).map(value => value.alias));
|
|
180
|
+
for (const model of [...models].sort((a, b) => a.route.localeCompare(b.route))) {
|
|
181
|
+
if (assignments[model.route]) continue;
|
|
182
|
+
const alias = allocateAlias(model.route, used);
|
|
183
|
+
used.add(alias);
|
|
184
|
+
assignments[model.route] = { family: "opus", alias };
|
|
185
|
+
}
|
|
186
|
+
const defaults = { ...profile.defaults };
|
|
187
|
+
for (const family of DESKTOP_FAMILIES) {
|
|
188
|
+
const members = Object.keys(assignments).filter(route => assignments[route]!.family === family).sort();
|
|
189
|
+
const current = defaults[family];
|
|
190
|
+
defaults[family] = current && assignments[current]?.family === family ? current : (members[0] ?? null);
|
|
191
|
+
}
|
|
192
|
+
return parseDesktopProfile({ version: 1, assignments, defaults, ...appliedMarkers(profile) });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function moveDesktopRoute(
|
|
196
|
+
profile: DesktopProfile,
|
|
197
|
+
route: string,
|
|
198
|
+
family: DesktopFamily,
|
|
199
|
+
makeDefault = false,
|
|
200
|
+
): DesktopProfile {
|
|
201
|
+
const parsed = parseDesktopProfile(profile);
|
|
202
|
+
const current = parsed.assignments[route];
|
|
203
|
+
if (!current) throw new DesktopProfileError("route is not assigned", `profile.assignments.${route}`);
|
|
204
|
+
const oldFamily = current.family;
|
|
205
|
+
if (oldFamily === family) {
|
|
206
|
+
return makeDefault ? setDesktopFamilyDefault(parsed, family, route) : parsed;
|
|
207
|
+
}
|
|
208
|
+
const assignments = { ...parsed.assignments, [route]: { ...current, family } };
|
|
209
|
+
const defaults = { ...parsed.defaults };
|
|
210
|
+
if (defaults[oldFamily] === route) {
|
|
211
|
+
defaults[oldFamily] = Object.keys(assignments).filter(key => key !== route && assignments[key]!.family === oldFamily).sort()[0] ?? null;
|
|
212
|
+
}
|
|
213
|
+
const destinationMembers = Object.keys(assignments).filter(key => assignments[key]!.family === family).sort();
|
|
214
|
+
if (makeDefault || !defaults[family] || assignments[defaults[family]!]?.family !== family) defaults[family] = route;
|
|
215
|
+
if (!defaults[family] && destinationMembers.length > 0) defaults[family] = destinationMembers[0]!;
|
|
216
|
+
return parseDesktopProfile({ version: 1, assignments, defaults, ...appliedMarkers(parsed) });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function setDesktopFamilyDefault(
|
|
220
|
+
profile: DesktopProfile,
|
|
221
|
+
family: DesktopFamily,
|
|
222
|
+
route: string | null,
|
|
223
|
+
): DesktopProfile {
|
|
224
|
+
const parsed = parseDesktopProfile(profile);
|
|
225
|
+
const members = Object.keys(parsed.assignments).filter(key => parsed.assignments[key]!.family === family);
|
|
226
|
+
if (route === null && members.length > 0) throw new DesktopProfileError("cannot clear a non-empty family default", `profile.defaults.${family}`);
|
|
227
|
+
if (route !== null && parsed.assignments[route]?.family !== family) throw new DesktopProfileError("route is not a member of this family", `profile.defaults.${family}`);
|
|
228
|
+
return parseDesktopProfile({ ...parsed, defaults: { ...parsed.defaults, [family]: route } });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function renderDesktopProfile(
|
|
232
|
+
profile: DesktopProfile,
|
|
233
|
+
models: readonly DesktopProfileModel[],
|
|
234
|
+
): RenderedDesktopModel[] {
|
|
235
|
+
const parsed = parseDesktopProfile(profile);
|
|
236
|
+
const modelByRoute = new Map(models.map(model => [model.route, model]));
|
|
237
|
+
const activeByFamily = new Map<DesktopFamily, string[]>();
|
|
238
|
+
for (const family of DESKTOP_FAMILIES) activeByFamily.set(family, []);
|
|
239
|
+
for (const [route, assignment] of Object.entries(parsed.assignments)) {
|
|
240
|
+
if (modelByRoute.has(route)) activeByFamily.get(assignment.family)!.push(route);
|
|
241
|
+
}
|
|
242
|
+
for (const routes of activeByFamily.values()) routes.sort();
|
|
243
|
+
const effectiveDefaults = {} as Record<DesktopFamily, string | null>;
|
|
244
|
+
for (const family of DESKTOP_FAMILIES) {
|
|
245
|
+
const active = activeByFamily.get(family)!;
|
|
246
|
+
const stored = parsed.defaults[family];
|
|
247
|
+
effectiveDefaults[family] = stored && active.includes(stored) ? stored : (active[0] ?? null);
|
|
248
|
+
}
|
|
249
|
+
const defaultOrder = DESKTOP_FAMILIES.map(family => effectiveDefaults[family]).filter((route): route is string => !!route);
|
|
250
|
+
const defaultSet = new Set(defaultOrder);
|
|
251
|
+
const rest = Object.keys(parsed.assignments).filter(route => modelByRoute.has(route) && !defaultSet.has(route)).sort();
|
|
252
|
+
return [...defaultOrder, ...rest].map(route => {
|
|
253
|
+
const model = modelByRoute.get(route)!;
|
|
254
|
+
const assignment = parsed.assignments[route]!;
|
|
255
|
+
return {
|
|
256
|
+
...model,
|
|
257
|
+
name: assignment.alias,
|
|
258
|
+
family: assignment.family,
|
|
259
|
+
isFamilyDefault: effectiveDefaults[assignment.family] === route,
|
|
260
|
+
supports1m: typeof model.contextWindow === "number" && model.contextWindow >= 1_000_000,
|
|
261
|
+
};
|
|
262
|
+
});
|
|
263
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code gateway-model cache writer (devlog 260712 030).
|
|
3
|
+
*
|
|
4
|
+
* Claude Code 2.1.207 refreshes ~/.claude/cache/gateway-models.json ONLY when it
|
|
5
|
+
* holds a credential (q5l(): `if(!ANTHROPIC_AUTH_TOKEN && !apiKey) return`). Our
|
|
6
|
+
* subscription-preserving launch deliberately sets no token, so the CLI can never
|
|
7
|
+
* refresh its picker list itself — it reads whatever cache exists. We therefore
|
|
8
|
+
* pre-write the cache in the exact on-disk schema the CLI uses:
|
|
9
|
+
* { baseUrl, fetchedAt, models: [{ id, display_name? }] } (mode 0600)
|
|
10
|
+
* mirroring its `/^(claude|anthropic)/i` usable-id filter. The picker validates
|
|
11
|
+
* only `baseUrl === ANTHROPIC_BASE_URL`, so a foreign base URL is simply ignored.
|
|
12
|
+
*/
|
|
13
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { loadServiceTokenFromFile, serviceApiTokenFilePath } from "../lib/service-secrets";
|
|
17
|
+
import type { OcxConfig } from "../types";
|
|
18
|
+
|
|
19
|
+
export interface GatewayModelRow {
|
|
20
|
+
id: string;
|
|
21
|
+
display_name?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface GatewayModelCacheRefreshOptions {
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
configDir?: string;
|
|
27
|
+
admissionConfig?: Pick<OcxConfig, "apiKeys">;
|
|
28
|
+
env?: NodeJS.ProcessEnv;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Claude Code config dir (CLAUDE_CONFIG_DIR override honored, like the CLI). */
|
|
32
|
+
export function claudeConfigDir(): string {
|
|
33
|
+
const custom = process.env.CLAUDE_CONFIG_DIR;
|
|
34
|
+
return custom && custom.length > 0 ? custom : join(homedir(), ".claude");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Write the cache file; returns its path or null (best-effort, never throws). */
|
|
38
|
+
export function writeGatewayModelCache(baseUrl: string, models: readonly GatewayModelRow[], configDir = claudeConfigDir()): string | null {
|
|
39
|
+
try {
|
|
40
|
+
// Mirror the CLI's usable-id filter so our file matches what it would cache.
|
|
41
|
+
const usable = models.filter(m => /^(claude|anthropic)/i.test(m.id));
|
|
42
|
+
const cacheDir = join(configDir, "cache");
|
|
43
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
44
|
+
const path = join(cacheDir, "gateway-models.json");
|
|
45
|
+
const payload = {
|
|
46
|
+
baseUrl,
|
|
47
|
+
fetchedAt: Date.now(),
|
|
48
|
+
models: usable.map(m => (m.display_name === undefined ? { id: m.id } : { id: m.id, display_name: m.display_name })),
|
|
49
|
+
};
|
|
50
|
+
writeFileSync(path, JSON.stringify(payload), { encoding: "utf8", mode: 0o600 });
|
|
51
|
+
return path;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Hardened service-token file, the same precedence `ocx opencode` uses. A service
|
|
59
|
+
* install writes the admission token to disk rather than the interactive environment,
|
|
60
|
+
* so an interactive `ocx claude` with neither env token nor configured key would
|
|
61
|
+
* otherwise still get a 401 and keep a stale picker list.
|
|
62
|
+
*/
|
|
63
|
+
function serviceFileToken(env: NodeJS.ProcessEnv): string | null {
|
|
64
|
+
const lookup = env.OCX_API_TOKEN_FILE?.trim()
|
|
65
|
+
? env
|
|
66
|
+
: { ...env, OCX_API_TOKEN_FILE: serviceApiTokenFilePath() };
|
|
67
|
+
return loadServiceTokenFromFile(lookup as Record<string, string | undefined>);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Fetch the anthropic-flavor /v1/models from the local proxy and write the cache. */
|
|
71
|
+
export async function refreshGatewayModelCacheFromProxy(
|
|
72
|
+
port: number,
|
|
73
|
+
options: GatewayModelCacheRefreshOptions = {},
|
|
74
|
+
): Promise<string | null> {
|
|
75
|
+
try {
|
|
76
|
+
const headers = new Headers({ "anthropic-version": "2023-06-01" });
|
|
77
|
+
// A wildcard/non-loopback listener requires data-plane admission even for a
|
|
78
|
+
// request sent to its local 127.0.0.1 address. Reuse the same dedicated
|
|
79
|
+
// credential domain as /v1/models admission; never place it in Authorization,
|
|
80
|
+
// which can belong to an upstream provider on other data-plane surfaces.
|
|
81
|
+
const envToken = (options.env ?? process.env).OPENCODEX_API_AUTH_TOKEN?.trim();
|
|
82
|
+
const configuredToken = options.admissionConfig?.apiKeys
|
|
83
|
+
?.find(entry => entry.key.trim().length > 0)
|
|
84
|
+
?.key.trim();
|
|
85
|
+
const admissionToken = envToken || serviceFileToken(options.env ?? process.env) || configuredToken;
|
|
86
|
+
if (admissionToken) headers.set("x-opencodex-api-key", admissionToken);
|
|
87
|
+
|
|
88
|
+
// ?ids=cli pins the readable claude-ocx id family deterministically (audit 051
|
|
89
|
+
// #5): the cache prewrite must not depend on UA sniffing.
|
|
90
|
+
const res = await fetch(`http://127.0.0.1:${port}/v1/models?limit=1000&ids=cli`, {
|
|
91
|
+
headers,
|
|
92
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 3_000),
|
|
93
|
+
});
|
|
94
|
+
if (!res.ok) return null;
|
|
95
|
+
const body = await res.json() as { data?: unknown };
|
|
96
|
+
if (!Array.isArray(body.data)) return null;
|
|
97
|
+
const models: GatewayModelRow[] = body.data
|
|
98
|
+
.filter(m => typeof m.id === "string" && (m.id as string).length > 0)
|
|
99
|
+
.map(m => ({
|
|
100
|
+
id: m.id as string,
|
|
101
|
+
display_name: typeof m.display_name === "string" ? m.display_name : undefined,
|
|
102
|
+
}));
|
|
103
|
+
return writeGatewayModelCache(`http://127.0.0.1:${port}`, models, options.configDir);
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude inbound debug capture (devlog/260711_claude_inbound/130 B1).
|
|
3
|
+
*
|
|
4
|
+
* Opt-in ring (last 20) of ALLOWLIST SCALARS from inbound Anthropic requests so the
|
|
5
|
+
* user can watch live what Claude Desktop/Code actually sends per effort-slider
|
|
6
|
+
* position (thinking.type / output_config.effort) and whether metadata.user_id
|
|
7
|
+
* exists (prompt-cache affinity, H1/H2).
|
|
8
|
+
*
|
|
9
|
+
* Privacy contract (audit 133 R1#6/R2#3): no prompt text, no raw objects, no stable
|
|
10
|
+
* hashes. The only identity-ish values are 8-char HMAC tags salted with a
|
|
11
|
+
* process-random key — comparable for equality WITHIN one proxy run, useless as a
|
|
12
|
+
* cross-run fingerprint. Capture is gated on the `claude` debug flag and the ring is
|
|
13
|
+
* cleared when the flag turns off.
|
|
14
|
+
*/
|
|
15
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
16
|
+
import { isClaudeDebugEnabled } from "../lib/debug-settings";
|
|
17
|
+
import { retainedUtf8Bytes, truncateRetainedUtf8 } from "../lib/admission";
|
|
18
|
+
import { enforceAppOwnedMemoryBudget } from "../lib/app-owned-memory";
|
|
19
|
+
|
|
20
|
+
export interface ClaudeInboundDebugEntry {
|
|
21
|
+
/** Monotonic capture id — unique even when several entries share Date.now(). */
|
|
22
|
+
id: number;
|
|
23
|
+
at: number;
|
|
24
|
+
endpoint: "messages" | "count_tokens";
|
|
25
|
+
model: string;
|
|
26
|
+
resolvedModel?: string;
|
|
27
|
+
stream?: boolean;
|
|
28
|
+
maxTokens?: number;
|
|
29
|
+
thinkingType?: string;
|
|
30
|
+
thinkingBudgetTokens?: number;
|
|
31
|
+
outputConfigEffort?: string;
|
|
32
|
+
metadataKeys?: string[];
|
|
33
|
+
metadataKeysDropped?: number;
|
|
34
|
+
rowTruncated?: true;
|
|
35
|
+
hasMetadataUserId: boolean;
|
|
36
|
+
hasSystem: boolean;
|
|
37
|
+
/** Raw anthropic-beta header (comma list) — carries context-1m / effort betas. */
|
|
38
|
+
anthropicBeta?: string;
|
|
39
|
+
/** Ephemeral equality tags (process-salted HMAC, 8 chars) — run-local identity only. */
|
|
40
|
+
userIdTag?: string;
|
|
41
|
+
systemTag?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const RING_LIMIT = 20;
|
|
45
|
+
const MAX_DIAGNOSTIC_VALUE_BYTES = 8 * 1024;
|
|
46
|
+
const MAX_CLAUDE_INBOUND_METADATA_KEYS = 64;
|
|
47
|
+
const MAX_CLAUDE_INBOUND_ROW_BYTES = 32 * 1024;
|
|
48
|
+
const ring: ClaudeInboundDebugEntry[] = [];
|
|
49
|
+
let ringBytes = 0;
|
|
50
|
+
const salt = randomBytes(16).toString("hex");
|
|
51
|
+
let lastEnabled = false;
|
|
52
|
+
let nextCaptureId = 0;
|
|
53
|
+
|
|
54
|
+
function tag(value: string): string {
|
|
55
|
+
return createHmac("sha256", salt).update(value).digest("hex").slice(0, 8);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type Rec = Record<string, unknown>;
|
|
59
|
+
|
|
60
|
+
function isRec(v: unknown): v is Rec {
|
|
61
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function systemText(system: unknown): string | undefined {
|
|
65
|
+
if (typeof system === "string") return system.length > 0 ? system : undefined;
|
|
66
|
+
if (Array.isArray(system)) {
|
|
67
|
+
const parts = system
|
|
68
|
+
.filter((b): b is Rec => isRec(b) && b.type === "text" && typeof b.text === "string")
|
|
69
|
+
.map(b => b.text as string);
|
|
70
|
+
return parts.length > 0 ? parts.join("\n\n") : undefined;
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function entryBytes(entry: ClaudeInboundDebugEntry): number {
|
|
76
|
+
return retainedUtf8Bytes(JSON.stringify(entry));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function removeOldest(): number {
|
|
80
|
+
const removed = ring.shift();
|
|
81
|
+
if (!removed) return 0;
|
|
82
|
+
const bytes = entryBytes(removed);
|
|
83
|
+
ringBytes -= bytes;
|
|
84
|
+
return bytes;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Record one inbound request. No-op (and ring flush) when the claude debug flag is off. */
|
|
88
|
+
export function captureClaudeInbound(
|
|
89
|
+
endpoint: "messages" | "count_tokens",
|
|
90
|
+
body: unknown,
|
|
91
|
+
resolvedModel?: string,
|
|
92
|
+
anthropicBeta?: string,
|
|
93
|
+
): void {
|
|
94
|
+
const enabled = isClaudeDebugEnabled();
|
|
95
|
+
if (!enabled) {
|
|
96
|
+
if (lastEnabled) clearClaudeInboundDebug(); // flag turned off: drop captured entries
|
|
97
|
+
lastEnabled = false;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
lastEnabled = true;
|
|
101
|
+
if (!isRec(body)) return;
|
|
102
|
+
const thinking = isRec(body.thinking) ? body.thinking : undefined;
|
|
103
|
+
const outputConfig = isRec(body.output_config) ? body.output_config : undefined;
|
|
104
|
+
const metadata = isRec(body.metadata) ? body.metadata : undefined;
|
|
105
|
+
const userId = metadata && typeof metadata.user_id === "string" ? metadata.user_id : undefined;
|
|
106
|
+
const system = systemText(body.system);
|
|
107
|
+
const metadataNames = metadata ? Object.keys(metadata) : [];
|
|
108
|
+
const entry: ClaudeInboundDebugEntry = {
|
|
109
|
+
id: ++nextCaptureId,
|
|
110
|
+
at: Date.now(),
|
|
111
|
+
endpoint,
|
|
112
|
+
model: truncateRetainedUtf8(typeof body.model === "string" ? body.model : "unknown", MAX_DIAGNOSTIC_VALUE_BYTES),
|
|
113
|
+
...(resolvedModel ? { resolvedModel: truncateRetainedUtf8(resolvedModel, MAX_DIAGNOSTIC_VALUE_BYTES) } : {}),
|
|
114
|
+
...(typeof body.stream === "boolean" ? { stream: body.stream } : {}),
|
|
115
|
+
...(typeof body.max_tokens === "number" ? { maxTokens: body.max_tokens } : {}),
|
|
116
|
+
...(thinking && typeof thinking.type === "string" ? { thinkingType: truncateRetainedUtf8(thinking.type, MAX_DIAGNOSTIC_VALUE_BYTES) } : {}),
|
|
117
|
+
...(thinking && typeof thinking.budget_tokens === "number" ? { thinkingBudgetTokens: thinking.budget_tokens } : {}),
|
|
118
|
+
...(outputConfig && typeof outputConfig.effort === "string" ? { outputConfigEffort: truncateRetainedUtf8(outputConfig.effort, MAX_DIAGNOSTIC_VALUE_BYTES) } : {}),
|
|
119
|
+
...(metadata ? { metadataKeys: metadataNames.slice(0, MAX_CLAUDE_INBOUND_METADATA_KEYS).map(key => truncateRetainedUtf8(key, MAX_DIAGNOSTIC_VALUE_BYTES)) } : {}),
|
|
120
|
+
...(metadataNames.length > MAX_CLAUDE_INBOUND_METADATA_KEYS ? { metadataKeysDropped: metadataNames.length - MAX_CLAUDE_INBOUND_METADATA_KEYS } : {}),
|
|
121
|
+
hasMetadataUserId: userId !== undefined,
|
|
122
|
+
hasSystem: system !== undefined,
|
|
123
|
+
...(anthropicBeta ? { anthropicBeta: truncateRetainedUtf8(anthropicBeta, MAX_DIAGNOSTIC_VALUE_BYTES) } : {}),
|
|
124
|
+
...(userId !== undefined ? { userIdTag: tag(userId) } : {}),
|
|
125
|
+
...(system !== undefined ? { systemTag: tag(system) } : {}),
|
|
126
|
+
};
|
|
127
|
+
while (entryBytes(entry) > MAX_CLAUDE_INBOUND_ROW_BYTES && entry.metadataKeys?.length) {
|
|
128
|
+
entry.metadataKeys.pop();
|
|
129
|
+
entry.metadataKeysDropped = (entry.metadataKeysDropped ?? 0) + 1;
|
|
130
|
+
entry.rowTruncated = true;
|
|
131
|
+
}
|
|
132
|
+
for (const key of ["anthropicBeta", "resolvedModel", "outputConfigEffort", "thinkingType"] as const) {
|
|
133
|
+
if (entryBytes(entry) <= MAX_CLAUDE_INBOUND_ROW_BYTES) break;
|
|
134
|
+
if (entry[key] !== undefined) {
|
|
135
|
+
delete entry[key];
|
|
136
|
+
entry.rowTruncated = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
ring.push(entry);
|
|
140
|
+
ringBytes += entryBytes(entry);
|
|
141
|
+
if (ring.length > RING_LIMIT) removeOldest();
|
|
142
|
+
enforceAppOwnedMemoryBudget();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Newest-first snapshot for /api/claude/inbound-debug. */
|
|
146
|
+
export function getClaudeInboundDebugEntries(): ClaudeInboundDebugEntry[] {
|
|
147
|
+
return [...ring].reverse();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Test isolation / explicit clear. */
|
|
151
|
+
export function clearClaudeInboundDebug(): void {
|
|
152
|
+
ring.length = 0;
|
|
153
|
+
ringBytes = 0;
|
|
154
|
+
lastEnabled = false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function claudeInboundDebugMetrics(): { entries: number; bytes: number; oldestAt: number | null } {
|
|
158
|
+
return { entries: ring.length, bytes: ringBytes, oldestAt: ring[0]?.at ?? null };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function evictOldestClaudeInboundForBudget(): number {
|
|
162
|
+
return removeOldest();
|
|
163
|
+
}
|