@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,298 @@
|
|
|
1
|
+
import { readBoundedResponseBody } from "../lib/bounded-body";
|
|
2
|
+
|
|
3
|
+
export class CodexWarmupError extends Error {
|
|
4
|
+
code: "http_status" | "missing_body" | "stream_failed" | "stream_incomplete" | "stream_error" | "stream_too_large" | "invalid_sse" | "no_terminal" | "transport";
|
|
5
|
+
status?: number;
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
code: CodexWarmupError["code"],
|
|
9
|
+
message = "Codex warmup failed",
|
|
10
|
+
options: { status?: number; cause?: unknown } = {},
|
|
11
|
+
) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "CodexWarmupError";
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.status = options.status;
|
|
16
|
+
if (options.cause !== undefined) this.cause = options.cause;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CodexWarmupOptions {
|
|
21
|
+
accessToken: string;
|
|
22
|
+
chatgptAccountId: string;
|
|
23
|
+
model?: string;
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
28
|
+
const DEFAULT_MODEL = "gpt-5.4-mini";
|
|
29
|
+
const FALLBACK_MODELS = ["gpt-5.5"];
|
|
30
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
31
|
+
const MAX_TIMEOUT_MS = 0x7fff_ffff;
|
|
32
|
+
const MAX_ERROR_BODY_BYTES = 2048;
|
|
33
|
+
const MAX_WARMUP_STREAM_BYTES = 1024 * 1024;
|
|
34
|
+
|
|
35
|
+
/** Bound and release an upstream error body without exposing provider-controlled text. */
|
|
36
|
+
async function drainErrorBody(res: Response, signal: AbortSignal): Promise<void> {
|
|
37
|
+
try {
|
|
38
|
+
await readBoundedResponseBody(res, {
|
|
39
|
+
signal,
|
|
40
|
+
maxBytes: MAX_ERROR_BODY_BYTES,
|
|
41
|
+
fatalUtf8: true,
|
|
42
|
+
});
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (signal.aborted) {
|
|
45
|
+
throw new CodexWarmupError("transport", "Codex warmup request failed", {
|
|
46
|
+
cause: error,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// The bounded reader owns cancellation for oversized, invalid, or stalled bodies.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function safeWarmupReason(err: unknown): string {
|
|
54
|
+
if (err instanceof CodexWarmupError) {
|
|
55
|
+
return err.status ? `${err.code}:${err.status}` : err.code;
|
|
56
|
+
}
|
|
57
|
+
return "transport";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function codexWarmupFailureReason(err: unknown): string {
|
|
61
|
+
return safeWarmupReason(err);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function eventTypeFromData(data: unknown): string | undefined {
|
|
65
|
+
if (!data || typeof data !== "object") return undefined;
|
|
66
|
+
const record = data as Record<string, unknown>;
|
|
67
|
+
return typeof record.type === "string" ? record.type : undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function parseSseFrame(frame: string): unknown | null {
|
|
71
|
+
const dataLines = frame
|
|
72
|
+
.split(/\r?\n/)
|
|
73
|
+
.filter(line => line.startsWith("data:"))
|
|
74
|
+
.map(line => line.slice(5).trimStart());
|
|
75
|
+
if (dataLines.length === 0) return null;
|
|
76
|
+
const data = dataLines.join("\n").trim();
|
|
77
|
+
if (!data || data === "[DONE]") return null;
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(data) as unknown;
|
|
80
|
+
} catch (err) {
|
|
81
|
+
throw new CodexWarmupError("invalid_sse", "Codex warmup received invalid SSE", { cause: err });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function drainWarmupSse(body: ReadableStream<Uint8Array>, signal: AbortSignal): Promise<void> {
|
|
86
|
+
const reader = body.getReader();
|
|
87
|
+
const decoder = new TextDecoder();
|
|
88
|
+
let buffer = new Uint8Array(Math.min(MAX_WARMUP_STREAM_BYTES, 64 * 1024));
|
|
89
|
+
let bufferedBytes = 0;
|
|
90
|
+
let scanOffset = 0;
|
|
91
|
+
let bytesRead = 0;
|
|
92
|
+
const abortError = () => new CodexWarmupError("transport", "Codex warmup request failed", {
|
|
93
|
+
cause: signal.reason,
|
|
94
|
+
});
|
|
95
|
+
const cancelReader = () => {
|
|
96
|
+
try {
|
|
97
|
+
void reader.cancel(signal.reason).catch(() => {});
|
|
98
|
+
} catch {
|
|
99
|
+
// Some custom stream implementations throw synchronously from cancel().
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
// Fetch implementations usually error the response body when their signal is
|
|
103
|
+
// aborted, but a ReadableStream is not intrinsically coupled to that signal.
|
|
104
|
+
// Race only the currently pending read against a removable abort listener;
|
|
105
|
+
// Bun 1.3 can leave read() parked until a custom source's cancel promise
|
|
106
|
+
// settles, while a shared never-settled race promise would retain one handler
|
|
107
|
+
// per chunk. Cancellation remains best-effort and is never awaited.
|
|
108
|
+
const readWithSignal = (): Promise<Awaited<ReturnType<typeof reader.read>>> => {
|
|
109
|
+
if (signal.aborted) {
|
|
110
|
+
cancelReader();
|
|
111
|
+
return Promise.reject(abortError());
|
|
112
|
+
}
|
|
113
|
+
const read = reader.read();
|
|
114
|
+
void read.catch(() => {});
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
let settled = false;
|
|
117
|
+
const finish = (action: () => void) => {
|
|
118
|
+
if (settled) return;
|
|
119
|
+
settled = true;
|
|
120
|
+
signal.removeEventListener("abort", onAbort);
|
|
121
|
+
action();
|
|
122
|
+
};
|
|
123
|
+
const onAbort = () => {
|
|
124
|
+
cancelReader();
|
|
125
|
+
finish(() => reject(abortError()));
|
|
126
|
+
};
|
|
127
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
128
|
+
if (signal.aborted) {
|
|
129
|
+
onAbort();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
read.then(
|
|
133
|
+
result => finish(() => resolve(result)),
|
|
134
|
+
error => finish(() => reject(error)),
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
const ensureCapacity = (requiredBytes: number) => {
|
|
139
|
+
if (requiredBytes <= buffer.byteLength) return;
|
|
140
|
+
const grown = new Uint8Array(Math.min(
|
|
141
|
+
MAX_WARMUP_STREAM_BYTES,
|
|
142
|
+
Math.max(requiredBytes, buffer.byteLength * 2),
|
|
143
|
+
));
|
|
144
|
+
grown.set(buffer.subarray(0, bufferedBytes));
|
|
145
|
+
buffer = grown;
|
|
146
|
+
};
|
|
147
|
+
const findFrameDelimiter = (start: number): { index: number; length: 2 | 3 | 4 } | undefined => {
|
|
148
|
+
for (let index = start; index < bufferedBytes - 1; index += 1) {
|
|
149
|
+
const firstLength = buffer[index] === 10
|
|
150
|
+
? 1
|
|
151
|
+
: buffer[index] === 13 && buffer[index + 1] === 10 ? 2 : 0;
|
|
152
|
+
if (firstLength === 0) continue;
|
|
153
|
+
const secondStart = index + firstLength;
|
|
154
|
+
const secondLength = buffer[secondStart] === 10
|
|
155
|
+
? 1
|
|
156
|
+
: buffer[secondStart] === 13 && buffer[secondStart + 1] === 10 ? 2 : 0;
|
|
157
|
+
if (secondLength > 0) return { index, length: (firstLength + secondLength) as 2 | 3 | 4 };
|
|
158
|
+
}
|
|
159
|
+
return undefined;
|
|
160
|
+
};
|
|
161
|
+
const acceptFrame = (frame: Uint8Array): boolean => {
|
|
162
|
+
const parsed = parseSseFrame(decoder.decode(frame));
|
|
163
|
+
const type = eventTypeFromData(parsed);
|
|
164
|
+
if (type === "response.completed") return true;
|
|
165
|
+
if (type === "response.failed") throw new CodexWarmupError("stream_failed");
|
|
166
|
+
if (type === "response.incomplete") throw new CodexWarmupError("stream_incomplete");
|
|
167
|
+
if (type === "error") throw new CodexWarmupError("stream_error");
|
|
168
|
+
return false;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
if (signal.aborted) throw abortError();
|
|
173
|
+
for (;;) {
|
|
174
|
+
const { done, value } = await readWithSignal();
|
|
175
|
+
if (signal.aborted) throw abortError();
|
|
176
|
+
if (done) break;
|
|
177
|
+
if (value.byteLength > MAX_WARMUP_STREAM_BYTES - bytesRead) {
|
|
178
|
+
throw new CodexWarmupError("stream_too_large", "Codex warmup stream exceeded the size limit");
|
|
179
|
+
}
|
|
180
|
+
bytesRead += value.byteLength;
|
|
181
|
+
ensureCapacity(bufferedBytes + value.byteLength);
|
|
182
|
+
buffer.set(value, bufferedBytes);
|
|
183
|
+
bufferedBytes += value.byteLength;
|
|
184
|
+
|
|
185
|
+
let consumedBytes = 0;
|
|
186
|
+
for (;;) {
|
|
187
|
+
const delimiter = findFrameDelimiter(scanOffset);
|
|
188
|
+
if (!delimiter) {
|
|
189
|
+
// A delimiter can start at most three bytes before the next chunk.
|
|
190
|
+
scanOffset = Math.max(consumedBytes, bufferedBytes - 3);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
if (acceptFrame(buffer.subarray(consumedBytes, delimiter.index))) return;
|
|
194
|
+
consumedBytes = delimiter.index + delimiter.length;
|
|
195
|
+
scanOffset = consumedBytes;
|
|
196
|
+
}
|
|
197
|
+
if (consumedBytes > 0) {
|
|
198
|
+
buffer.copyWithin(0, consumedBytes, bufferedBytes);
|
|
199
|
+
bufferedBytes -= consumedBytes;
|
|
200
|
+
scanOffset = Math.max(0, scanOffset - consumedBytes);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (bufferedBytes > 0 && acceptFrame(buffer.subarray(0, bufferedBytes))) return;
|
|
205
|
+
|
|
206
|
+
throw new CodexWarmupError("no_terminal", "Codex warmup ended before completion");
|
|
207
|
+
} finally {
|
|
208
|
+
try {
|
|
209
|
+
reader.releaseLock();
|
|
210
|
+
} catch {
|
|
211
|
+
// A hostile cancel promise may keep the final read locked after timeout.
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function tryWarmup(options: CodexWarmupOptions, model: string): Promise<void> {
|
|
217
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
218
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || timeoutMs > MAX_TIMEOUT_MS) {
|
|
219
|
+
throw new CodexWarmupError("transport", "Codex warmup request failed", {
|
|
220
|
+
cause: new RangeError("Codex warmup timeout is outside the supported range"),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
// Bun 1.3 can leave AbortSignal.timeout() dormant while a custom response
|
|
224
|
+
// stream has a pending read. A ref'ed timer and explicit controller make the
|
|
225
|
+
// same deadline cover response headers and the full success/error body.
|
|
226
|
+
const deadline = new AbortController();
|
|
227
|
+
const signal = deadline.signal;
|
|
228
|
+
const timer = setTimeout(() => {
|
|
229
|
+
deadline.abort(new DOMException("Codex warmup timed out", "TimeoutError"));
|
|
230
|
+
}, timeoutMs);
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
let res: Response;
|
|
234
|
+
try {
|
|
235
|
+
res = await fetch(CODEX_RESPONSES_URL, {
|
|
236
|
+
method: "POST",
|
|
237
|
+
headers: {
|
|
238
|
+
Authorization: `Bearer ${options.accessToken}`,
|
|
239
|
+
"ChatGPT-Account-Id": options.chatgptAccountId,
|
|
240
|
+
"Content-Type": "application/json",
|
|
241
|
+
},
|
|
242
|
+
body: JSON.stringify({
|
|
243
|
+
model,
|
|
244
|
+
instructions: "Reply with OK.",
|
|
245
|
+
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }],
|
|
246
|
+
stream: true,
|
|
247
|
+
store: false,
|
|
248
|
+
}),
|
|
249
|
+
signal,
|
|
250
|
+
});
|
|
251
|
+
} catch (err) {
|
|
252
|
+
throw new CodexWarmupError("transport", "Codex warmup request failed", { cause: err });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!res.ok) {
|
|
256
|
+
await drainErrorBody(res, signal);
|
|
257
|
+
throw new CodexWarmupError("http_status", "Codex warmup was rejected", {
|
|
258
|
+
status: res.status,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
const body = res.body;
|
|
262
|
+
if (!body) throw new CodexWarmupError("missing_body");
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
await drainWarmupSse(body, signal);
|
|
266
|
+
} finally {
|
|
267
|
+
try {
|
|
268
|
+
void body.cancel().catch(() => {});
|
|
269
|
+
} catch {
|
|
270
|
+
// Some custom stream implementations throw synchronously from cancel().
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
} finally {
|
|
274
|
+
clearTimeout(timer);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export async function warmCodexAccount(options: CodexWarmupOptions): Promise<void> {
|
|
279
|
+
const primaryModel = options.model?.trim() || DEFAULT_MODEL;
|
|
280
|
+
try {
|
|
281
|
+
await tryWarmup(options, primaryModel);
|
|
282
|
+
return;
|
|
283
|
+
} catch (err) {
|
|
284
|
+
// Retry with fallback models on 400 (model may not be available for this account).
|
|
285
|
+
if (!(err instanceof CodexWarmupError) || err.status !== 400) throw err;
|
|
286
|
+
let lastErr = err;
|
|
287
|
+
for (const fallback of FALLBACK_MODELS) {
|
|
288
|
+
if (fallback === primaryModel) continue;
|
|
289
|
+
try {
|
|
290
|
+
await tryWarmup(options, fallback);
|
|
291
|
+
return;
|
|
292
|
+
} catch (retryErr) {
|
|
293
|
+
if (retryErr instanceof CodexWarmupError) lastErr = retryErr;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
throw lastErr;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ServerWebSocket } from "bun";
|
|
2
|
+
import type { WsData } from "../server/ws-bridge";
|
|
3
|
+
import { createAdmissionGate, type AdmissionMetrics, type AdmissionReservation } from "../lib/admission";
|
|
4
|
+
|
|
5
|
+
export const MAX_TRACKED_CODEX_WEBSOCKETS = 128;
|
|
6
|
+
const websocketGate = createAdmissionGate("codex_websockets", MAX_TRACKED_CODEX_WEBSOCKETS);
|
|
7
|
+
|
|
8
|
+
export function tryReserveCodexWebSocket(): AdmissionReservation<ServerWebSocket<WsData>> | null {
|
|
9
|
+
const gateLease = websocketGate.tryAcquire();
|
|
10
|
+
if (!gateLease) return null;
|
|
11
|
+
let socket: ServerWebSocket<WsData> | undefined;
|
|
12
|
+
let active = true;
|
|
13
|
+
return {
|
|
14
|
+
bind(value) {
|
|
15
|
+
if (!active) return;
|
|
16
|
+
socket = value;
|
|
17
|
+
},
|
|
18
|
+
release() {
|
|
19
|
+
if (!active) return;
|
|
20
|
+
active = false;
|
|
21
|
+
socket = undefined;
|
|
22
|
+
gateLease.release();
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function codexWebSocketAdmissionMetrics(): AdmissionMetrics {
|
|
28
|
+
return websocketGate.metrics();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const socketsByAccount = new Map<string, Set<ServerWebSocket<WsData>>>();
|
|
32
|
+
|
|
33
|
+
function trackedAccountId(ws: ServerWebSocket<WsData>): string | null {
|
|
34
|
+
const ctx = ws.data.authContext;
|
|
35
|
+
return ctx?.kind === "pool" || ctx?.kind === "main-pool" ? ctx.accountId : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function addSocketForAccount(accountId: string | null, ws: ServerWebSocket<WsData>): void {
|
|
39
|
+
if (!accountId) return;
|
|
40
|
+
let sockets = socketsByAccount.get(accountId);
|
|
41
|
+
if (!sockets) {
|
|
42
|
+
sockets = new Set();
|
|
43
|
+
socketsByAccount.set(accountId, sockets);
|
|
44
|
+
}
|
|
45
|
+
sockets.add(ws);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function removeSocketForAccount(accountId: string | null, ws: ServerWebSocket<WsData>): void {
|
|
49
|
+
if (!accountId) return;
|
|
50
|
+
const sockets = socketsByAccount.get(accountId);
|
|
51
|
+
if (!sockets) return;
|
|
52
|
+
sockets.delete(ws);
|
|
53
|
+
if (sockets.size === 0) socketsByAccount.delete(accountId);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function registerCodexWebSocket(ws: ServerWebSocket<WsData>): void {
|
|
57
|
+
addSocketForAccount(trackedAccountId(ws), ws);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function unregisterCodexWebSocket(ws: ServerWebSocket<WsData>): void {
|
|
61
|
+
removeSocketForAccount(trackedAccountId(ws), ws);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function updateCodexWebSocketAuthContext(
|
|
65
|
+
ws: ServerWebSocket<WsData>,
|
|
66
|
+
authContext: WsData["authContext"],
|
|
67
|
+
): void {
|
|
68
|
+
const before = trackedAccountId(ws);
|
|
69
|
+
removeSocketForAccount(before, ws);
|
|
70
|
+
ws.data.authContext = authContext;
|
|
71
|
+
addSocketForAccount(trackedAccountId(ws), ws);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function invalidateCodexWebSocketsForAccount(accountId: string): number {
|
|
75
|
+
const sockets = socketsByAccount.get(accountId);
|
|
76
|
+
if (!sockets) return 0;
|
|
77
|
+
const snapshot = [...sockets];
|
|
78
|
+
socketsByAccount.delete(accountId);
|
|
79
|
+
for (const ws of snapshot) {
|
|
80
|
+
try {
|
|
81
|
+
ws.data.cancel?.();
|
|
82
|
+
} catch {
|
|
83
|
+
/* ignore cancel callbacks during invalidation */
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
ws.close(4001, "Codex account invalidated");
|
|
87
|
+
} catch {
|
|
88
|
+
/* socket may already be closing */
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return snapshot.length;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function getTrackedCodexWebSocketCountForAccount(accountId: string): number {
|
|
95
|
+
return socketsByAccount.get(accountId)?.size ?? 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function clearCodexWebSocketRegistry(): void {
|
|
99
|
+
socketsByAccount.clear();
|
|
100
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The witness a native Codex write is coordinated by.
|
|
3
|
+
*
|
|
4
|
+
* NOT an `AdmissionSnapshot`, and the difference is the whole reason this file
|
|
5
|
+
* exists. `admitCodexWrite` refuses before it constructs a snapshot unless
|
|
6
|
+
* ownership is `owned` (`admission.ts`), so every snapshot it produces carries
|
|
7
|
+
* `owned` — the field cannot hold `unknown` at all. Reusing that type here would
|
|
8
|
+
* have meant one of two untruths: either the call becomes gated on ownership,
|
|
9
|
+
* which refuses Codex injection on every Windows machine and on Linux without a
|
|
10
|
+
* reachable user bus, or a hand-built snapshot claims an admission that never
|
|
11
|
+
* happened.
|
|
12
|
+
*
|
|
13
|
+
* So this type authorizes THE WRITE, not the DECISION to write. Deciding whether
|
|
14
|
+
* a write should happen at all is admission's job and belongs to its own phase,
|
|
15
|
+
* behind the Windows definition-chain walk.
|
|
16
|
+
*
|
|
17
|
+
* What the id covers, and why it is the OUTPUT rather than the inputs: the bytes
|
|
18
|
+
* `injectCodexConfig` emits depend on the port, the resolved catalog path, the
|
|
19
|
+
* hostname, websocket mode, legacy mode and the managed sub-agent defaults. An
|
|
20
|
+
* enumeration of those was already incomplete once. Hashing the computed
|
|
21
|
+
* candidate bytes closes the class instead of the instance — an input that
|
|
22
|
+
* changes the output changes the id whether or not anyone remembered to list it.
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
|
|
26
|
+
export interface CodexWriteCandidate {
|
|
27
|
+
/** The exact string about to replace `config.toml`. */
|
|
28
|
+
readonly configBytes: string;
|
|
29
|
+
/** The exact string about to replace the profile. */
|
|
30
|
+
readonly profileBytes: string;
|
|
31
|
+
/** The RESOLVED catalog path, never the raw option. */
|
|
32
|
+
readonly catalogPath: string | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CodexWriteEvidence {
|
|
36
|
+
/** sha256 of the `config.toml` bytes this operation read as its input. */
|
|
37
|
+
readonly nativeInputIdentity: string;
|
|
38
|
+
/** Digest of the persisted OpenCodex config these bytes were derived from. */
|
|
39
|
+
readonly persistedIdentity: string;
|
|
40
|
+
/** Generation as observed before the lock; `present:false` means no coordinator yet. */
|
|
41
|
+
readonly generation: Readonly<{ present: boolean; value: number }>;
|
|
42
|
+
/** Content identity of the journal, or a stable marker for absence. */
|
|
43
|
+
readonly journalIdentity: string;
|
|
44
|
+
readonly canonicalTargets: Readonly<{
|
|
45
|
+
config: string;
|
|
46
|
+
profile: string;
|
|
47
|
+
journal: string;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface CodexWriteCoordination {
|
|
52
|
+
readonly candidate: CodexWriteCandidate;
|
|
53
|
+
readonly evidence: CodexWriteEvidence;
|
|
54
|
+
/**
|
|
55
|
+
* Recorded context, deliberately OUTSIDE `comparisonId`.
|
|
56
|
+
*
|
|
57
|
+
* The lock compares one id and nothing else, so a field that is copied rather
|
|
58
|
+
* than re-observed detects no drift by being in it — it would only match
|
|
59
|
+
* itself. Ownership is not re-observed under the lock (that would mean running
|
|
60
|
+
* a service-manager subprocess while N and C are held), so it travels as
|
|
61
|
+
* something the operation SAW, not as something the comparison PROVES.
|
|
62
|
+
*/
|
|
63
|
+
readonly observedOwnership: "owned" | "foreign" | "unknown";
|
|
64
|
+
readonly comparisonId: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Collapse a generation to one token.
|
|
69
|
+
*
|
|
70
|
+
* Absent and present-zero are the same authority — no cooperating write has
|
|
71
|
+
* committed — and they cannot be observed the same way: before the lock only
|
|
72
|
+
* absence is visible, and inside it only the committed zero is. Without this
|
|
73
|
+
* they never match and every first write refuses.
|
|
74
|
+
*/
|
|
75
|
+
function generationToken(generation: CodexWriteEvidence["generation"]): string {
|
|
76
|
+
const { present, value } = generation;
|
|
77
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
78
|
+
throw new TypeError(`A config generation must be a non-negative safe integer, got ${String(value)}.`);
|
|
79
|
+
}
|
|
80
|
+
return present && value > 0 ? `gen:${value}` : "gen:0";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function hashCodexWriteCoordination(
|
|
84
|
+
candidate: CodexWriteCandidate,
|
|
85
|
+
evidence: CodexWriteEvidence,
|
|
86
|
+
): string {
|
|
87
|
+
return createHash("sha256")
|
|
88
|
+
.update(JSON.stringify([
|
|
89
|
+
// The output, hashed directly. Two operations that intend different bytes
|
|
90
|
+
// cannot share an id, however they came to differ.
|
|
91
|
+
createHash("sha256").update(candidate.configBytes).digest("hex"),
|
|
92
|
+
createHash("sha256").update(candidate.profileBytes).digest("hex"),
|
|
93
|
+
candidate.catalogPath,
|
|
94
|
+
evidence.nativeInputIdentity,
|
|
95
|
+
evidence.persistedIdentity,
|
|
96
|
+
generationToken(evidence.generation),
|
|
97
|
+
evidence.journalIdentity,
|
|
98
|
+
evidence.canonicalTargets,
|
|
99
|
+
]))
|
|
100
|
+
.digest("hex");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function codexWriteCoordination(
|
|
104
|
+
candidate: CodexWriteCandidate,
|
|
105
|
+
evidence: CodexWriteEvidence,
|
|
106
|
+
observedOwnership: CodexWriteCoordination["observedOwnership"],
|
|
107
|
+
): CodexWriteCoordination {
|
|
108
|
+
return {
|
|
109
|
+
candidate,
|
|
110
|
+
evidence,
|
|
111
|
+
observedOwnership,
|
|
112
|
+
comparisonId: hashCodexWriteCoordination(candidate, evidence),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { classifyError, isCyberPolicyCode } from "../lib/errors";
|
|
2
|
+
import type { OcxComboTarget } from "../types";
|
|
3
|
+
import { targetKey } from "./types";
|
|
4
|
+
import {
|
|
5
|
+
captureConfigGeneration,
|
|
6
|
+
sweepExpiredOnWrite,
|
|
7
|
+
type GenerationContext,
|
|
8
|
+
} from "../lib/state-store-sweeper";
|
|
9
|
+
|
|
10
|
+
interface TargetCooldown {
|
|
11
|
+
cooldownUntil: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const DEFAULT_COOLDOWN_MS = 60_000;
|
|
15
|
+
const MAX_COOLDOWN_MS = 10 * 60_000;
|
|
16
|
+
|
|
17
|
+
/** Map<`${comboId}\0${provider/model}`, TargetCooldown> */
|
|
18
|
+
const targetCooldowns = new Map<string, TargetCooldown>();
|
|
19
|
+
let lastReconciledGeneration = 0;
|
|
20
|
+
let liveComboTargets = new Set<string>();
|
|
21
|
+
|
|
22
|
+
function cooldownMapKey(
|
|
23
|
+
comboId: string,
|
|
24
|
+
target: Pick<OcxComboTarget, "provider" | "model">,
|
|
25
|
+
): string {
|
|
26
|
+
return `${comboId}\0${targetKey(target)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function parseRetryAfterMs(
|
|
30
|
+
value: string | null | undefined,
|
|
31
|
+
now = Date.now(),
|
|
32
|
+
): number | undefined {
|
|
33
|
+
const text = value?.trim();
|
|
34
|
+
if (!text) return undefined;
|
|
35
|
+
if (/^\d+(?:\.\d+)?$/.test(text)) {
|
|
36
|
+
const seconds = Number(text);
|
|
37
|
+
if (Number.isFinite(seconds) && seconds > 0) {
|
|
38
|
+
return Math.min(Math.max(Math.ceil(seconds * 1000), 1), MAX_COOLDOWN_MS);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const timestamp = Date.parse(text);
|
|
42
|
+
if (!Number.isFinite(timestamp)) return undefined;
|
|
43
|
+
const delay = timestamp - now;
|
|
44
|
+
return delay > 0 ? Math.min(delay, MAX_COOLDOWN_MS) : undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isComboTargetInCooldown(
|
|
48
|
+
comboId: string,
|
|
49
|
+
target: Pick<OcxComboTarget, "provider" | "model">,
|
|
50
|
+
now = Date.now(),
|
|
51
|
+
): boolean {
|
|
52
|
+
const key = cooldownMapKey(comboId, target);
|
|
53
|
+
const entry = targetCooldowns.get(key);
|
|
54
|
+
if (!entry) return false;
|
|
55
|
+
if (entry.cooldownUntil <= now) {
|
|
56
|
+
targetCooldowns.delete(key);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function coolComboTarget(
|
|
63
|
+
comboId: string,
|
|
64
|
+
target: Pick<OcxComboTarget, "provider" | "model">,
|
|
65
|
+
options?: { retryAfter?: string | null; now?: number; cooldownMs?: number; writerGeneration?: number },
|
|
66
|
+
): void {
|
|
67
|
+
const now = options?.now ?? Date.now();
|
|
68
|
+
const writerGeneration = options?.writerGeneration ?? captureConfigGeneration();
|
|
69
|
+
const ownerKey = `${comboId}::${targetKey(target)}`;
|
|
70
|
+
if (writerGeneration < lastReconciledGeneration && !liveComboTargets.has(ownerKey)) return;
|
|
71
|
+
const cooldownMs = options?.cooldownMs
|
|
72
|
+
?? parseRetryAfterMs(options?.retryAfter, now)
|
|
73
|
+
?? DEFAULT_COOLDOWN_MS;
|
|
74
|
+
targetCooldowns.set(cooldownMapKey(comboId, target), {
|
|
75
|
+
cooldownUntil: now + Math.min(Math.max(cooldownMs, 1), MAX_COOLDOWN_MS),
|
|
76
|
+
});
|
|
77
|
+
sweepExpiredOnWrite(now);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function reconcileComboTargetCooldowns(context: GenerationContext): number {
|
|
81
|
+
if (context.generation <= lastReconciledGeneration) return 0;
|
|
82
|
+
liveComboTargets = new Set(context.comboTargets);
|
|
83
|
+
lastReconciledGeneration = context.generation;
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function sweepExpiredComboTargetCooldowns(now = Date.now()): number {
|
|
88
|
+
let removed = 0;
|
|
89
|
+
for (const [key, cooldown] of targetCooldowns) {
|
|
90
|
+
if (cooldown.cooldownUntil > now) continue;
|
|
91
|
+
targetCooldowns.delete(key);
|
|
92
|
+
removed += 1;
|
|
93
|
+
}
|
|
94
|
+
return removed;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function clearComboTargetCooldowns(comboId?: string): void {
|
|
98
|
+
if (comboId === undefined) {
|
|
99
|
+
targetCooldowns.clear();
|
|
100
|
+
liveComboTargets.clear();
|
|
101
|
+
lastReconciledGeneration = 0;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const prefix = `${comboId}\0`;
|
|
105
|
+
for (const key of targetCooldowns.keys()) {
|
|
106
|
+
if (key.startsWith(prefix)) targetCooldowns.delete(key);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type ComboFailureDecision = "hop" | "stop";
|
|
111
|
+
|
|
112
|
+
export function comboFailureDecision(
|
|
113
|
+
status: number,
|
|
114
|
+
message: string,
|
|
115
|
+
options?: { code?: string | null },
|
|
116
|
+
): ComboFailureDecision {
|
|
117
|
+
if (status === 499) return "stop";
|
|
118
|
+
if (message.toLowerCase().includes("origin_rejected")) return "stop";
|
|
119
|
+
// Cyber policy is a hard non-retryable refusal — honor structured code even when
|
|
120
|
+
// classificationText was truncated before the JSON code field.
|
|
121
|
+
if (isCyberPolicyCode(options?.code)) return "stop";
|
|
122
|
+
const error = classifyError(status, "upstream_error", message);
|
|
123
|
+
if (isCyberPolicyCode(error.code)) return "stop";
|
|
124
|
+
// A local input-admission refusal (#1524) says "this candidate cannot fit the request",
|
|
125
|
+
// not "the request is impossible": the next candidate may have a larger context window.
|
|
126
|
+
//
|
|
127
|
+
// This MUST be tested before the generic stop list below. Our own refusal message says
|
|
128
|
+
// "context window" -- that is what it refuses on -- and the classifier remaps that phrase,
|
|
129
|
+
// so checking the stop list first swallowed the signal and ended the chain. An UPSTREAM
|
|
130
|
+
// `context_length_exceeded` carries no admission code and still falls through to stop.
|
|
131
|
+
//
|
|
132
|
+
// Matched on the STRUCTURED code only, which classifyError now preserves for our own
|
|
133
|
+
// refusal. A raw substring test would additionally let any upstream override a terminal
|
|
134
|
+
// verdict by echoing the token in prose we do not control.
|
|
135
|
+
//
|
|
136
|
+
// Precise about what this is NOT: an upstream can still SET this code deliberately, since
|
|
137
|
+
// both extractors read the upstream error object. That is bounded rather than dangerous --
|
|
138
|
+
// an upstream already controls other hop signals (429, 5xx), and traversal is finite: policy
|
|
139
|
+
// tries each candidate once via `tried`, and combo excludes each attempted target. So this is
|
|
140
|
+
// structured-code-only, not provably local.
|
|
141
|
+
if (options?.code === "input_admission_refused" || error.code === "input_admission_refused") {
|
|
142
|
+
return "hop";
|
|
143
|
+
}
|
|
144
|
+
if (["origin_rejected", "context_length_exceeded", "invalid_request_error"].includes(error.code ?? "")) {
|
|
145
|
+
return "stop";
|
|
146
|
+
}
|
|
147
|
+
if ([401, 403, 404, 408, 429].includes(status) || status >= 500) return "hop";
|
|
148
|
+
if ([
|
|
149
|
+
"permission_denied",
|
|
150
|
+
"subscription_required",
|
|
151
|
+
"invalid_api_key",
|
|
152
|
+
"insufficient_quota",
|
|
153
|
+
"rate_limit_exceeded",
|
|
154
|
+
"server_is_overloaded",
|
|
155
|
+
"upstream_server_error",
|
|
156
|
+
].includes(error.code ?? "")) {
|
|
157
|
+
return "hop";
|
|
158
|
+
}
|
|
159
|
+
return "stop";
|
|
160
|
+
}
|