@iislee/opencodex 2.11.0 → 2.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/README.md +117 -22
- package/bin/ocx.mjs +245 -88
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +21 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +475 -62
- package/src/adapters/base.ts +82 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +637 -0
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +308 -0
- package/src/adapters/cursor/cursor-errors.ts +144 -6
- package/src/adapters/cursor/discovery.ts +122 -14
- package/src/adapters/cursor/effort-map.ts +106 -4
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +180 -59
- package/src/adapters/cursor/live-transport.ts +623 -170
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -3
- package/src/adapters/cursor/native-exec.ts +121 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +447 -75
- package/src/adapters/cursor/request-builder.ts +271 -35
- package/src/adapters/cursor/tool-definitions.ts +252 -12
- package/src/adapters/cursor/tool-result-normalize.ts +115 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +48 -1
- package/src/adapters/cursor.ts +336 -39
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +713 -41
- package/src/adapters/google-antigravity-wire.ts +48 -10
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +742 -86
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +65 -17
- package/src/adapters/kiro.ts +270 -73
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1377 -344
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1164 -64
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +105 -21
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +186 -0
- package/src/bridge.ts +512 -92
- package/src/chat/inbound.ts +57 -20
- package/src/chat/outbound.ts +97 -33
- package/src/claude/agents-inject.ts +40 -10
- package/src/claude/context-windows.ts +37 -14
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +88 -21
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +150 -19
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +648 -28
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +97 -19
- package/src/cli/agent.ts +53 -2
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +60 -15
- package/src/cli/claude.ts +129 -35
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +821 -0
- package/src/cli/doctor.ts +440 -56
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +34 -274
- package/src/cli/index.ts +417 -528
- package/src/cli/init.ts +5 -17
- package/src/cli/inspect.ts +230 -0
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +130 -2
- package/src/cli/models.ts +118 -16
- package/src/cli/observe.ts +123 -11
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +34 -3
- package/src/cli/provider.ts +25 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +476 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +57 -10
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +51 -4
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +137 -18
- package/src/cli/version-skew.ts +46 -0
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1442 -23
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +35 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +84 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +570 -107
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +893 -246
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +348 -32
- package/src/codex/autostart-health.ts +24 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +86 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +138 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +555 -45
- package/src/codex/catalog/native-models.ts +75 -0
- package/src/codex/catalog/parsing.ts +269 -30
- package/src/codex/catalog/provider-fetch.ts +1626 -144
- package/src/codex/catalog/sync.ts +1576 -187
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +386 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +676 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +436 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +30 -24
- package/src/codex/history-provider.ts +1016 -235
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +223 -0
- package/src/codex/inject-coordination.ts +373 -0
- package/src/codex/inject.ts +1114 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +81 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +524 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +360 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +622 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +675 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +1313 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +264 -21
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +514 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1065 -31
- package/src/codex/subagent-model-fallback.ts +437 -43
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +720 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +47 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +91 -9
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +1713 -709
- package/src/generated/compatibility-version.json +3324 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +157 -33
- package/src/images/plan.ts +24 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +155 -0
- package/src/integrations/registry.ts +166 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +433 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +715 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +167 -11
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +26 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +80 -5
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +6 -2
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +253 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +30 -4
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +462 -71
- package/src/oauth/key-providers.ts +25 -0
- package/src/oauth/kimi.ts +25 -3
- package/src/oauth/kiro.ts +95 -6
- package/src/oauth/local-token-detect.ts +13 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +133 -26
- package/src/oauth/token-guardian.ts +9 -3
- package/src/oauth/types.ts +15 -0
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +176 -0
- package/src/providers/context-cap.ts +26 -8
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +281 -6
- package/src/providers/fastwire.ts +507 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +35 -2
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +30 -0
- package/src/providers/quota.ts +1379 -40
- package/src/providers/registry.ts +1545 -112
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +94 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +32 -4
- package/src/reasoning-effort.ts +68 -3
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +384 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +435 -0
- package/src/responses/parser.ts +225 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +504 -0
- package/src/responses/schema.ts +12 -2
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +659 -31
- package/src/responses/thought-signature-replay.ts +364 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +405 -31
- package/src/routing/analytics.ts +379 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +155 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +289 -48
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +154 -72
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +445 -0
- package/src/server/claude-messages.ts +160 -44
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +1021 -190
- package/src/server/lifecycle.ts +336 -14
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +433 -116
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +64 -25
- package/src/server/management/config-routes.ts +511 -51
- package/src/server/management/context.ts +89 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +127 -52
- package/src/server/management/model-routes.ts +461 -133
- package/src/server/management/model-rows.ts +167 -0
- package/src/server/management/native-integration-routes.ts +770 -0
- package/src/server/management/oauth-account-routes.ts +90 -8
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +836 -159
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +289 -33
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +126 -23
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +161 -10
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +221 -54
- package/src/server/relay.ts +479 -87
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +298 -31
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +239 -36
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +444 -72
- package/src/server/responses/core.ts +3579 -436
- package/src/server/responses/empty-completion-guard.ts +333 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +89 -99
- package/src/server/responses/input-admission.ts +187 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +343 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +381 -0
- package/src/server/responses-custom-tool-repair.ts +363 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +363 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +376 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +319 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +53 -40
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1153 -94
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +911 -0
- package/src/types/provider.ts +614 -0
- package/src/types/request.ts +397 -0
- package/src/types/tools.ts +236 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +107 -1237
- package/src/update/index.ts +37 -23
- package/src/update/job.ts +445 -68
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +266 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +287 -16
- package/src/usage/log.ts +666 -50
- package/src/usage/summary.ts +388 -17
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +27 -16
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +119 -0
- package/src/web-search/executor.ts +21 -6
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +284 -54
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -130,6 +130,50 @@ function registryAllowsPrivateNetwork(name: string): boolean {
|
|
|
130
130
|
return getProviderRegistryEntry(name)?.allowPrivateNetworkByDefault === true;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
/**
|
|
134
|
+
* OAuth registry entries that opt into `allowBaseUrlOverride` send bearer credentials to a
|
|
135
|
+
* user-configured endpoint (review findings, PR #2109 / PR #2110): a cleartext `http:`
|
|
136
|
+
* override would expose the OAuth token on the wire. `https:` is therefore required for
|
|
137
|
+
* every non-local destination. Loopback/localhost/private relays keep working over
|
|
138
|
+
* `http:` because they already sit behind the explicit `allowPrivateNetwork` opt-in
|
|
139
|
+
* enforced by {@link providerDestinationConfigError}. Keyed/local providers (Ollama,
|
|
140
|
+
* vLLM, LM Studio, LiteLLM, Moonshot, Qwen, Alibaba) are untouched: they are not
|
|
141
|
+
* `authKind: "oauth"`, so this check never fires for them.
|
|
142
|
+
*/
|
|
143
|
+
function registrySendsOAuthToOverriddenBaseUrl(name: string): boolean {
|
|
144
|
+
const entry = getProviderRegistryEntry(name);
|
|
145
|
+
return entry?.authKind === "oauth" && entry.allowBaseUrlOverride === true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function providerSecureTransportConfigError(
|
|
149
|
+
name: string,
|
|
150
|
+
provider: Pick<OcxProviderConfig, "baseUrl" | "allowPrivateNetwork">,
|
|
151
|
+
): string | null {
|
|
152
|
+
if (!registrySendsOAuthToOverriddenBaseUrl(name)) return null;
|
|
153
|
+
let parsed: URL;
|
|
154
|
+
try {
|
|
155
|
+
parsed = new URL(provider.baseUrl.trim());
|
|
156
|
+
} catch {
|
|
157
|
+
return null; // invalid URLs are providerBaseUrlConfigError's concern
|
|
158
|
+
}
|
|
159
|
+
if (parsed.protocol !== "http:") return null;
|
|
160
|
+
const assessment = assessDestination(provider.baseUrl);
|
|
161
|
+
// Classify FIRST, then consult the opt-in. `allowPrivateNetwork` says "this destination is
|
|
162
|
+
// intentionally local", which is a statement about the address, not a waiver of transport
|
|
163
|
+
// security — reading it before classification let `http://attacker.example` with the opt-in
|
|
164
|
+
// set carry an OAuth bearer in cleartext to a public host.
|
|
165
|
+
if (!assessment) return null;
|
|
166
|
+
const local = assessment.kind === "localhost"
|
|
167
|
+
|| assessment.kind === "loopback"
|
|
168
|
+
|| assessment.kind === "private";
|
|
169
|
+
if (local && providerAllowsPrivateNetwork(name, provider)) {
|
|
170
|
+
// A genuinely local relay over http stays reachable through the explicit opt-in; the
|
|
171
|
+
// private-network gate still governs whether it may be reached at all.
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return "baseUrl must use https: this provider sends OAuth credentials to its endpoint, and http is allowed only for loopback/private relays";
|
|
175
|
+
}
|
|
176
|
+
|
|
133
177
|
/**
|
|
134
178
|
* Whether a provider may reach loopback/private addresses.
|
|
135
179
|
*
|
|
@@ -150,6 +194,8 @@ export function providerAllowsPrivateNetwork(
|
|
|
150
194
|
}
|
|
151
195
|
|
|
152
196
|
export function providerDestinationConfigError(name: string, provider: Pick<OcxProviderConfig, "baseUrl" | "allowPrivateNetwork">): string | null {
|
|
197
|
+
const secureTransportError = providerSecureTransportConfigError(name, provider);
|
|
198
|
+
if (secureTransportError) return secureTransportError;
|
|
153
199
|
const assessment = assessDestination(provider.baseUrl);
|
|
154
200
|
if (!assessment) return null;
|
|
155
201
|
if (assessment.kind === "public" || assessment.kind === "hostname") return null;
|
|
@@ -241,10 +287,18 @@ export function assessUrlDestination(url: string): UrlDestinationAssessment | nu
|
|
|
241
287
|
* Returns the validated addresses so direct callers can pin the connect peer and
|
|
242
288
|
* avoid a second, rebindable resolution. DNS failures remain fail-closed here;
|
|
243
289
|
* the provider proxy wrapper alone may recognize that typed failure and degrade.
|
|
290
|
+
*
|
|
291
|
+
* `allowBenchmarkAddresses` is an explicit outbound-only opt-in for Clash/Surge/
|
|
292
|
+
* Mihomo fake-IP DNS (IANA benchmark space 198.18.0.0/15, credit #1748): a hostname
|
|
293
|
+
* answer in that range is accepted without marking the destination private, so the
|
|
294
|
+
* caller can keep the hostname on its configured HTTP(S) proxy path. It applies to
|
|
295
|
+
* resolved answers only — a literal 198.18.x URL still rejects — and mixed answers
|
|
296
|
+
* that include any other non-public address still fail. Callers that do not pass it
|
|
297
|
+
* (image and Lab fetch) keep rejecting benchmark space.
|
|
244
298
|
*/
|
|
245
299
|
export async function resolvePublicAddresses(
|
|
246
300
|
url: string,
|
|
247
|
-
options?: string | { context?: string; allowPrivateNetwork?: boolean },
|
|
301
|
+
options?: string | { context?: string; allowPrivateNetwork?: boolean; allowBenchmarkAddresses?: boolean },
|
|
248
302
|
): Promise<{
|
|
249
303
|
hostname: string;
|
|
250
304
|
addresses: { address: string; family: number }[];
|
|
@@ -254,6 +308,7 @@ export async function resolvePublicAddresses(
|
|
|
254
308
|
? `${options.trim() || "image"} URL`
|
|
255
309
|
: options?.context?.trim() || "image URL";
|
|
256
310
|
const privateNetworkAllowed = typeof options === "object" && options?.allowPrivateNetwork === true;
|
|
311
|
+
const benchmarkAllowed = typeof options === "object" && options?.allowBenchmarkAddresses === true;
|
|
257
312
|
let hostname: string;
|
|
258
313
|
try {
|
|
259
314
|
hostname = normalizeHostname(new URL(url.trim()).hostname);
|
|
@@ -294,6 +349,14 @@ export async function resolvePublicAddresses(
|
|
|
294
349
|
const ipKind = isIP(address) || (family === 4 || family === 6 ? family : 0);
|
|
295
350
|
const assessment = ipKind === 4 ? classifyIpv4(address) : ipKind === 6 ? classifyIpv6(normalizeHostname(address)) : null;
|
|
296
351
|
if (!assessment || assessment.kind !== "public") {
|
|
352
|
+
// Hostname → 198.18.0.0/15 under the explicit opt-in is Clash/Surge/Mihomo
|
|
353
|
+
// fake-IP DNS, not a LAN provider. Accept it without allowPrivateNetwork and
|
|
354
|
+
// do not mark the destination private, so the caller's HTTP(S)_PROXY path
|
|
355
|
+
// still applies (credit #1748).
|
|
356
|
+
if (benchmarkAllowed && assessment?.kind === "private" && assessment.detail === "benchmark address") {
|
|
357
|
+
validatedAddresses.push({ address, family: ipKind === 4 || ipKind === 6 ? ipKind : (family || 4) });
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
297
360
|
const allowedPrivateAddress = privateNetworkAllowed
|
|
298
361
|
&& assessment
|
|
299
362
|
&& (assessment.kind === "loopback" || assessment.kind === "private");
|
|
@@ -314,3 +377,4 @@ export async function resolvePublicAddresses(
|
|
|
314
377
|
export async function assertUrlResolvesPublic(url: string): Promise<void> {
|
|
315
378
|
await resolvePublicAddresses(url);
|
|
316
379
|
}
|
|
380
|
+
|
package/src/lib/errors.ts
CHANGED
|
@@ -6,11 +6,18 @@ export interface OcxErrorPayload {
|
|
|
6
6
|
|
|
7
7
|
/** OpenAI / Codex hard block for high-risk cybersecurity activity (HTTP 400 or mid-stream). */
|
|
8
8
|
export const CYBER_POLICY_ERROR_CODE = "cyber_policy";
|
|
9
|
+
export const CYBER_POLICY_FALLBACK_MESSAGE = "Request blocked by the upstream cybersecurity policy.";
|
|
9
10
|
|
|
10
11
|
export function isCyberPolicyCode(code: string | null | undefined): boolean {
|
|
11
12
|
return code === CYBER_POLICY_ERROR_CODE;
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
/** Preserve a structured upstream error type; otherwise use the dedicated policy identity. */
|
|
16
|
+
export function cyberPolicyErrorType(type: string | null | undefined): string {
|
|
17
|
+
const trimmed = typeof type === "string" ? type.trim() : "";
|
|
18
|
+
return trimmed || CYBER_POLICY_ERROR_CODE;
|
|
19
|
+
}
|
|
20
|
+
|
|
14
21
|
/**
|
|
15
22
|
* Detect OpenAI cyber-policy refusals from message text when structured `code` was stripped.
|
|
16
23
|
* Matches Codex fallback copy and Cursor/API agent wording (session evidence 2026-07-24).
|
|
@@ -43,6 +50,25 @@ function isSubscriptionGateMessage(text: string): boolean {
|
|
|
43
50
|
);
|
|
44
51
|
}
|
|
45
52
|
|
|
53
|
+
function isLocalAclHardeningMessage(text: string): boolean {
|
|
54
|
+
const secretPathHardening = text.includes("secret path") && (
|
|
55
|
+
text.includes("acl") ||
|
|
56
|
+
text.includes("harden") ||
|
|
57
|
+
text.includes("permission") ||
|
|
58
|
+
text.includes("access denied") ||
|
|
59
|
+
text.includes("inheritance")
|
|
60
|
+
);
|
|
61
|
+
return (
|
|
62
|
+
text.includes("icacls") ||
|
|
63
|
+
text.includes("acl hardening") ||
|
|
64
|
+
text.includes("ntfs") ||
|
|
65
|
+
secretPathHardening ||
|
|
66
|
+
text.includes("inheritance:r") ||
|
|
67
|
+
/\bwindows\b.*\bacl\b/.test(text) ||
|
|
68
|
+
text.includes("/grant:r")
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
function isAuthenticationMessage(text: string): boolean {
|
|
47
73
|
const accessDeniedWithCredentialCue = (
|
|
48
74
|
text.includes("access denied") ||
|
|
@@ -120,9 +146,20 @@ export function classifyError(status: number, type: string, message: string): Oc
|
|
|
120
146
|
return { message, type: "invalid_request_error", code: "client_closed_request" };
|
|
121
147
|
}
|
|
122
148
|
// Codex only shows the dedicated cyber UI when error.code === "cyber_policy".
|
|
123
|
-
//
|
|
149
|
+
// The public wire does not establish invalid_request_error as the canonical type, so
|
|
150
|
+
// message-only classification keeps the dedicated identity instead of inventing one.
|
|
151
|
+
// Structured callers re-apply their real upstream type with cyberPolicyErrorType().
|
|
124
152
|
if (type === CYBER_POLICY_ERROR_CODE || isCyberPolicyMessage(text)) {
|
|
125
|
-
return { message, type:
|
|
153
|
+
return { message, type: CYBER_POLICY_ERROR_CODE, code: CYBER_POLICY_ERROR_CODE };
|
|
154
|
+
}
|
|
155
|
+
// A LOCAL preflight refusal keeps its own code (#1524). The message necessarily says
|
|
156
|
+
// "context window" -- that is what it is refusing on -- so the generic remap below would
|
|
157
|
+
// rewrite it to `context_length_exceeded` and make it indistinguishable from an UPSTREAM
|
|
158
|
+
// verdict. The two need opposite fallback handling: ours means "this candidate does not
|
|
159
|
+
// fit", theirs means "the request is impossible", so collapsing them ended the chain at
|
|
160
|
+
// the first candidate that was merely too small.
|
|
161
|
+
if (type === "input_admission_refused") {
|
|
162
|
+
return { message, type: "invalid_request_error", code: "input_admission_refused" };
|
|
126
163
|
}
|
|
127
164
|
if (
|
|
128
165
|
text.includes("context_length_exceeded") ||
|
|
@@ -134,11 +171,16 @@ export function classifyError(status: number, type: string, message: string): Oc
|
|
|
134
171
|
return { message, type: "invalid_request_error", code: "context_length_exceeded" };
|
|
135
172
|
}
|
|
136
173
|
// "Cursor resource limit exceeded" is emitted only for explicit request-size overflow
|
|
137
|
-
// details (isCursorRequestTooLargeDetail in cursor-errors.ts);
|
|
138
|
-
//
|
|
174
|
+
// details (isCursorRequestTooLargeDetail in cursor-errors.ts); "Cursor context limit
|
|
175
|
+
// exceeded" is the bare payload-overflow shape (isCursorZeroTokenResourceExhausted);
|
|
176
|
+
// quota-style resource exhaustion arrives as "Cursor rate limit exceeded" and falls
|
|
177
|
+
// through to 429 below.
|
|
139
178
|
if (text.includes("cursor resource limit exceeded")) {
|
|
140
179
|
return { message, type: "invalid_request_error", code: "tool_catalog_too_large" };
|
|
141
180
|
}
|
|
181
|
+
if (text.includes("cursor context limit exceeded")) {
|
|
182
|
+
return { message, type: "invalid_request_error", code: "context_length_exceeded" };
|
|
183
|
+
}
|
|
142
184
|
// The Cursor adapter's classified rate-limit prefix is authoritative: its DETAIL may echo
|
|
143
185
|
// quota wording ("... quota exhausted") that would otherwise hit the insufficient_quota
|
|
144
186
|
// branch below and break the planned retry-with-backoff contract (WP3 review blocker 1).
|
|
@@ -170,6 +212,11 @@ export function classifyError(status: number, type: string, message: string): Oc
|
|
|
170
212
|
if (type === "origin_rejected") {
|
|
171
213
|
return { message, type: "invalid_request_error", code: "origin_rejected" };
|
|
172
214
|
}
|
|
215
|
+
// Local ACL setup failures can contain provider-like auth wording (for example
|
|
216
|
+
// "access denied" or "authentication") but represent unavailable infrastructure.
|
|
217
|
+
if (status === 503 && isLocalAclHardeningMessage(text)) {
|
|
218
|
+
return { message, type: "server_error", code: "upstream_server_error" };
|
|
219
|
+
}
|
|
173
220
|
// HTTP 401 and explicit auth failures are authoritative even when provider text
|
|
174
221
|
// also advertises an upgrade or subscription.
|
|
175
222
|
if (
|
|
@@ -273,6 +320,7 @@ export function inferHttpStatusFromAdapterMessage(message: string): number {
|
|
|
273
320
|
// See classifyError: this prefix now only means explicit request-size overflow (400);
|
|
274
321
|
// quota-style Cursor resource exhaustion carries the rate-limit prefix and maps to 429.
|
|
275
322
|
if (lower.includes("cursor resource limit exceeded")) return 400;
|
|
323
|
+
if (lower.includes("cursor context limit exceeded")) return 400;
|
|
276
324
|
if (
|
|
277
325
|
lower.includes("resource_exhausted") ||
|
|
278
326
|
lower.includes("resource exhausted") ||
|
|
@@ -280,10 +328,19 @@ export function inferHttpStatusFromAdapterMessage(message: string): number {
|
|
|
280
328
|
lower.includes("too many requests") ||
|
|
281
329
|
lower.includes("throttling")
|
|
282
330
|
) return 429;
|
|
331
|
+
// Local Windows filesystem hardening is infrastructure, not provider authentication.
|
|
332
|
+
// Keep this ahead of auth/permission and timeout keyword inference.
|
|
333
|
+
if (isLocalAclHardeningMessage(lower)) return 503;
|
|
283
334
|
// Strong authentication signals win when a message contains mixed auth and
|
|
284
335
|
// subscription/permission wording.
|
|
285
336
|
if (isAuthenticationMessage(lower)) return 401;
|
|
286
337
|
if (isSubscriptionGateMessage(lower) || isPermissionMessage(lower)) return 403;
|
|
338
|
+
// Same precedence rule as classifyCursorError: an explicit gRPC FAILED_PRECONDITION is a
|
|
339
|
+
// structured, deterministic rejection, so it outranks the overload keywords that routinely
|
|
340
|
+
// appear beside it ("failed_precondition: model unavailable for this plan"). Without this,
|
|
341
|
+
// the message matched "unavailable" and returned a retryable 503, so clients kept retrying
|
|
342
|
+
// a rejection that can never succeed.
|
|
343
|
+
if (lower.includes("failed_precondition") || lower.includes("failed precondition")) return 400;
|
|
287
344
|
if (
|
|
288
345
|
lower.includes("unavailable") ||
|
|
289
346
|
lower.includes("overloaded") ||
|
|
@@ -359,6 +416,24 @@ export function httpStatusFromTerminalError(error: {
|
|
|
359
416
|
if (message && isClientClosedMessage(message)) return 499;
|
|
360
417
|
if (error.type === "invalid_request_error") return 400;
|
|
361
418
|
if (error.type === "proxy_error") return 500;
|
|
362
|
-
|
|
419
|
+
// A structured server class must not be downgraded to a CLIENT error by message wording.
|
|
420
|
+
// classifyError assigns `server_error` + `upstream_server_error` to every 5xx it sees, so
|
|
421
|
+
// the class is authoritative about blame: the upstream failed, the caller did not send a
|
|
422
|
+
// bad request. What it is NOT authoritative about is which server status fits — a stall is
|
|
423
|
+
// genuinely 504 and an overload genuinely 503, and flattening those to 502 discards
|
|
424
|
+
// information both the log surface and the retry policy read. So message inference still
|
|
425
|
+
// chooses the specific status, and only a client-error verdict is overridden.
|
|
426
|
+
//
|
|
427
|
+
// The override is deliberately narrowed to 400 alone. 429, 499, 401 and 403 are all
|
|
428
|
+
// actionable signals the caller routes on — retry-after, client cancellation, re-auth,
|
|
429
|
+
// entitlement — and overriding them would trade one kind of misreport for another. 400 is
|
|
430
|
+
// the single verdict that both blames the caller and stops the retry, which is the failure
|
|
431
|
+
// being fixed: an upstream 500 whose text happens to contain "malformed" or "invalid
|
|
432
|
+
// request" used to return 400, so Claude Code stopped retrying a retryable failure.
|
|
433
|
+
const structuredServerClass = error.type === "server_error" || error.code === "upstream_server_error";
|
|
434
|
+
if (message) {
|
|
435
|
+
const inferred = inferHttpStatusFromAdapterMessage(message);
|
|
436
|
+
return structuredServerClass && inferred === 400 ? 502 : inferred;
|
|
437
|
+
}
|
|
363
438
|
return 502;
|
|
364
439
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TrustedFabricPatchExecutor } from "../lab/fabric/types";
|
|
2
|
+
import { isHostIssuedFabricPatchExecutor } from "./fabric-task-host";
|
|
3
|
+
|
|
4
|
+
/** Trusted exact-route fabric patch executor recognition facade. */
|
|
5
|
+
export function isTrustedFabricPatchExecutor(value: unknown): value is TrustedFabricPatchExecutor {
|
|
6
|
+
return isHostIssuedFabricPatchExecutor(value);
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { FabricPatchExecutor, FabricPatchExecutorInput, TrustedFabricPatchExecutor } from "../lab/fabric/types";
|
|
2
|
+
|
|
3
|
+
const HOST_ISSUED_EXECUTORS = new WeakSet<object>();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Trusted host integration boundary for CL-07 exact-route fabric patch production.
|
|
7
|
+
*
|
|
8
|
+
* @internal host integration only
|
|
9
|
+
*/
|
|
10
|
+
export function createHostIssuedFabricPatchExecutor(
|
|
11
|
+
executorModulePath: string,
|
|
12
|
+
execute: FabricPatchExecutor,
|
|
13
|
+
): TrustedFabricPatchExecutor {
|
|
14
|
+
const capability: TrustedFabricPatchExecutor = Object.freeze({
|
|
15
|
+
executorModulePath,
|
|
16
|
+
execute: (input: FabricPatchExecutorInput) => Promise.resolve(execute(input)),
|
|
17
|
+
});
|
|
18
|
+
HOST_ISSUED_EXECUTORS.add(capability);
|
|
19
|
+
return capability;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Internal recognition check consumed by the read-only authority facade. */
|
|
23
|
+
export function isHostIssuedFabricPatchExecutor(value: unknown): value is TrustedFabricPatchExecutor {
|
|
24
|
+
if (typeof value !== "object" || value === null || !HOST_ISSUED_EXECUTORS.has(value as object)) return false;
|
|
25
|
+
const capability = value as TrustedFabricPatchExecutor;
|
|
26
|
+
return typeof capability.executorModulePath === "string"
|
|
27
|
+
&& capability.executorModulePath.length > 0
|
|
28
|
+
&& typeof capability.execute === "function";
|
|
29
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compatibility Lab activation.
|
|
3
|
+
*
|
|
4
|
+
* The proxy core does not import Lab. This module is the seam that does, and the startup
|
|
5
|
+
* composition root calls it only when the install actually uses Lab, so a user with no
|
|
6
|
+
* routing profile and no automation executes no Lab code and starts no Lab timer.
|
|
7
|
+
*
|
|
8
|
+
* Activation is SYNCHRONOUS by design. Three audit rounds established that a deferred
|
|
9
|
+
* activation window is unpatchable: `routeModelInternal` is sync, and so is the
|
|
10
|
+
* subagent-fallback chain that calls `routeModel`, so those callers have nowhere to await.
|
|
11
|
+
* During such a window a policy alias would be silently dropped from a fallback chain and
|
|
12
|
+
* the subagent would run on a different model than the operator configured. Registration
|
|
13
|
+
* therefore completes before `startServer` returns, inside the same synchronous turn as
|
|
14
|
+
* `Bun.serve`, so no request can observe an unregistered slot.
|
|
15
|
+
*
|
|
16
|
+
* See devlog/_fin/260814_lab_core_decoupling/080_activation_is_synchronous.md
|
|
17
|
+
*
|
|
18
|
+
* Startup degrades, explicit operator action reports. This asymmetry is deliberate: an
|
|
19
|
+
* invalid automation config disables automation with a warning here, but the management
|
|
20
|
+
* API and CLI paths that start a scheduler leave `LabAutomationError` to surface (the
|
|
21
|
+
* management route maps it to a 400). Someone who just toggled automation should see the
|
|
22
|
+
* validation error; someone merely starting the proxy should not lose unrelated traffic.
|
|
23
|
+
*
|
|
24
|
+
* @internal host integration only
|
|
25
|
+
*/
|
|
26
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
27
|
+
import { dirname, join } from "node:path";
|
|
28
|
+
import { labAutomationPolicyPath } from "../lab/paths";
|
|
29
|
+
import type { OcxConfig } from "../types";
|
|
30
|
+
import { LabAutomationError } from "../lab/automation/types";
|
|
31
|
+
import { registerLabPassiveRouteLinker } from "./lab-passive-linker-registration";
|
|
32
|
+
import { registerCurrentServerResourceCleanup } from "./server-resource-ownership";
|
|
33
|
+
import { setCompatibilityEvidenceProvider } from "../routing/compatibility/provider-slot";
|
|
34
|
+
import { labCompatibilityEvidenceProvider } from "../routing/compatibility/lab-evidence-provider";
|
|
35
|
+
import {
|
|
36
|
+
setLabAutomationDispatchDeps,
|
|
37
|
+
startLabAutomationScheduler,
|
|
38
|
+
} from "../lab/automation/orchestrator";
|
|
39
|
+
import { createProductionLabRouteExecutor } from "./lab-live-route-production";
|
|
40
|
+
|
|
41
|
+
interface LabRuntimeBinding {
|
|
42
|
+
release(): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface LabActivationRecord {
|
|
46
|
+
staticDetach: Array<() => void>;
|
|
47
|
+
runtime: LabRuntimeBinding | null;
|
|
48
|
+
seenRuntimeConfigs: WeakSet<OcxConfig>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Activation records keyed by configDir, so one process can own several configs. */
|
|
52
|
+
const activated = new Map<string, LabActivationRecord>();
|
|
53
|
+
|
|
54
|
+
const activationKey = (configDir?: string): string => configDir ?? "";
|
|
55
|
+
|
|
56
|
+
function readJsonIfPresent(path: string): unknown {
|
|
57
|
+
try {
|
|
58
|
+
if (!existsSync(path)) return null;
|
|
59
|
+
return JSON.parse(readFileSync(path, "utf8")) as unknown;
|
|
60
|
+
} catch {
|
|
61
|
+
// A malformed or unreadable file means "not enabled": this detector must never throw
|
|
62
|
+
// during startup, and must never import Lab persistence to answer the question.
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* True when Lab automation is enabled on disk.
|
|
69
|
+
*
|
|
70
|
+
* Mirrors `loadLabAutomationConfig` precedence deliberately: the current authority is the
|
|
71
|
+
* combined `automation-config.json`, with `automation-policy.json` as the legacy fallback.
|
|
72
|
+
* Reading only the legacy file would miss every install that enabled automation through
|
|
73
|
+
* the current dashboard.
|
|
74
|
+
*/
|
|
75
|
+
export function labAutomationEnabledOnDisk(configDir?: string): boolean {
|
|
76
|
+
const legacyPath = labAutomationPolicyPath(configDir);
|
|
77
|
+
const combined = readJsonIfPresent(join(dirname(legacyPath), "automation-config.json"));
|
|
78
|
+
if (combined && typeof combined === "object") {
|
|
79
|
+
const policy = (combined as { policy?: unknown }).policy;
|
|
80
|
+
if (policy && typeof policy === "object") {
|
|
81
|
+
return (policy as { enabled?: unknown }).enabled === true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const legacy = readJsonIfPresent(legacyPath);
|
|
85
|
+
if (legacy && typeof legacy === "object") {
|
|
86
|
+
return (legacy as { enabled?: unknown }).enabled === true;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** True when this install actually uses Lab: any routing profile, or automation enabled. */
|
|
92
|
+
export function labActivationRequired(config: OcxConfig, configDir?: string): boolean {
|
|
93
|
+
if (Object.keys(config.routingProfiles ?? {}).length > 0) return true;
|
|
94
|
+
return labAutomationEnabledOnDisk(configDir);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function startAutomationIfEnabled(configDir?: string): void {
|
|
98
|
+
if (!labAutomationEnabledOnDisk(configDir)) return;
|
|
99
|
+
try {
|
|
100
|
+
startLabAutomationScheduler(configDir);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
// Neither a malformed automation file nor a busy state lock may take the proxy down
|
|
103
|
+
// at startup. Lab automation stays off for this run; routing, evidence, and every
|
|
104
|
+
// other subsystem keep working.
|
|
105
|
+
//
|
|
106
|
+
// The two causes get different messages because they need different actions, and a
|
|
107
|
+
// lock-contention failure reported as "invalid config" sends the operator to fix a
|
|
108
|
+
// file that is fine. Contention can also stall startup by up to the 5s lock wait.
|
|
109
|
+
const code = err instanceof LabAutomationError ? err.code : null;
|
|
110
|
+
if (code === "state_lock_busy" || code === "state_lock_failed") {
|
|
111
|
+
console.warn(
|
|
112
|
+
"[lab] Lab automation did not start: another process holds the automation state lock."
|
|
113
|
+
+ " Automation stays off for this run and will be retried on the next start.",
|
|
114
|
+
);
|
|
115
|
+
} else {
|
|
116
|
+
console.warn(
|
|
117
|
+
"[lab] Lab automation is disabled for this run because its configuration could not be"
|
|
118
|
+
+ " loaded:",
|
|
119
|
+
err instanceof Error ? err.message : err,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function installLabAutomationRuntime(
|
|
126
|
+
record: LabActivationRecord,
|
|
127
|
+
config: OcxConfig,
|
|
128
|
+
configDir?: string,
|
|
129
|
+
): void {
|
|
130
|
+
const previous = record.runtime;
|
|
131
|
+
const routeExecutor = createProductionLabRouteExecutor({ configDir, loadConfig: () => config });
|
|
132
|
+
const releaseDispatchDeps = setLabAutomationDispatchDeps({
|
|
133
|
+
configDir,
|
|
134
|
+
loadConfig: () => config,
|
|
135
|
+
routeExecutor,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
let released = false;
|
|
139
|
+
let detachOwnerCleanup = () => {};
|
|
140
|
+
const binding: LabRuntimeBinding = {
|
|
141
|
+
release() {
|
|
142
|
+
if (released) return;
|
|
143
|
+
released = true;
|
|
144
|
+
detachOwnerCleanup();
|
|
145
|
+
releaseDispatchDeps();
|
|
146
|
+
if (record.runtime === binding) record.runtime = null;
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
// `setLabAutomationDispatchDeps` already registers its own owner cleanup. This second
|
|
150
|
+
// receipt only keeps the activation record in sync with that owner-scoped lifetime, so a
|
|
151
|
+
// later same-process server can see that the static Lab slots survived but CL-08 authority
|
|
152
|
+
// did not. The release is idempotent, so cleanup order does not matter.
|
|
153
|
+
detachOwnerCleanup = registerCurrentServerResourceCleanup(binding.release);
|
|
154
|
+
|
|
155
|
+
// Install the successor before releasing the predecessor. The dispatcher token check then
|
|
156
|
+
// makes the predecessor release a no-op for the successor scheduler/authority.
|
|
157
|
+
record.runtime = binding;
|
|
158
|
+
record.seenRuntimeConfigs.add(config);
|
|
159
|
+
previous?.release();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Register Lab into the core slots. Static activation is idempotent per configDir. The
|
|
164
|
+
* server-owned CL-08 runtime binding is refreshed when its prior owner ended or when a new
|
|
165
|
+
* server instance arrives with a config object that has not owned this activation before.
|
|
166
|
+
*/
|
|
167
|
+
export function activateLab(config: OcxConfig, configDir?: string): void {
|
|
168
|
+
const key = activationKey(configDir);
|
|
169
|
+
const existing = activated.get(key);
|
|
170
|
+
if (existing) {
|
|
171
|
+
// A released predecessor leaves the static Lab slots resident but removes dispatch
|
|
172
|
+
// authority and its scheduler. A live successor uses a fresh config object. Rebind in
|
|
173
|
+
// either case, but never let an older already-seen server steal authority back from a
|
|
174
|
+
// newer successor merely because it receives another management request.
|
|
175
|
+
if (existing.runtime === null || !existing.seenRuntimeConfigs.has(config)) {
|
|
176
|
+
installLabAutomationRuntime(existing, config, configDir);
|
|
177
|
+
startAutomationIfEnabled(configDir);
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// INVARIANT: static activation is all-or-nothing and reason-independent. Every static
|
|
183
|
+
// slot is registered here regardless of WHY activation was required, so automation-only
|
|
184
|
+
// activation still installs the compatibility provider a later profile needs.
|
|
185
|
+
const record: LabActivationRecord = {
|
|
186
|
+
staticDetach: [],
|
|
187
|
+
runtime: null,
|
|
188
|
+
seenRuntimeConfigs: new WeakSet<OcxConfig>(),
|
|
189
|
+
};
|
|
190
|
+
record.staticDetach.push(registerLabPassiveRouteLinker(configDir));
|
|
191
|
+
record.staticDetach.push(setCompatibilityEvidenceProvider(labCompatibilityEvidenceProvider));
|
|
192
|
+
installLabAutomationRuntime(record, config, configDir);
|
|
193
|
+
|
|
194
|
+
// Record the activation BEFORE the scheduler start. startLabAutomationScheduler runs the
|
|
195
|
+
// full automation normalizer, which throws on any field violation, and this call sits on
|
|
196
|
+
// the startup path of every install that has a routing profile. Storing the record first
|
|
197
|
+
// means a throw cannot orphan the detach receipts and leave slots registered with no
|
|
198
|
+
// activation record, which would let a later activateLab register them a second time.
|
|
199
|
+
activated.set(key, record);
|
|
200
|
+
startAutomationIfEnabled(configDir);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** True when this configDir has been activated. */
|
|
204
|
+
export function isLabActivated(configDir?: string): boolean {
|
|
205
|
+
return activated.has(activationKey(configDir));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Test-only teardown. Deactivation is deliberately NOT a production path: tearing an
|
|
210
|
+
* activation down mid-run would finalize in-flight automation as ineligible rather than
|
|
211
|
+
* cancelled, and the orchestrator's shutdown signal is process-global. An install that
|
|
212
|
+
* creates then deletes a profile keeps Lab resident until restart, which does not affect
|
|
213
|
+
* users who never opted in.
|
|
214
|
+
*/
|
|
215
|
+
export function resetLabActivationForTests(): void {
|
|
216
|
+
for (const [key, record] of [...activated]) {
|
|
217
|
+
activated.delete(key);
|
|
218
|
+
try { record.runtime?.release(); } catch { /* teardown is best-effort */ }
|
|
219
|
+
for (const release of [...record.staticDetach].reverse()) {
|
|
220
|
+
try { release(); } catch { /* teardown is best-effort */ }
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TrustedLabRouteExecutor } from "../lab/live/types";
|
|
2
|
+
import { isHostIssuedLabRouteExecutor } from "./lab-live-host";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Read-only trust check used by the Lab executor.
|
|
6
|
+
*
|
|
7
|
+
* Capability issuance intentionally does not live in this authority module: callers cannot
|
|
8
|
+
* self-attest sandbox boundary names here. Only the dedicated trusted host integration may
|
|
9
|
+
* construct and register an evidence-eligible route executor.
|
|
10
|
+
*/
|
|
11
|
+
export function isTrustedLabRouteExecutor(value: unknown): value is TrustedLabRouteExecutor {
|
|
12
|
+
return isHostIssuedLabRouteExecutor(value);
|
|
13
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { LabRouteExecutor, TrustedLabRouteExecutor } from "../lab/live/types";
|
|
2
|
+
import { REQUIRED_LAB_SANDBOX_BOUNDARIES } from "../lab/live/types";
|
|
3
|
+
|
|
4
|
+
const HOST_ISSUED_EXECUTORS = new WeakSet<object>();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Trusted host integration boundary for CL-03 exact-route execution.
|
|
8
|
+
*
|
|
9
|
+
* This module is intentionally not re-exported from the Lab public surface. The host owns
|
|
10
|
+
* enforcement of every mandatory sandbox boundary and therefore supplies no caller-controlled
|
|
11
|
+
* boundary list. Adapters/providers receive only the resulting opaque capability.
|
|
12
|
+
*
|
|
13
|
+
* @internal host integration only
|
|
14
|
+
*/
|
|
15
|
+
export function createHostIssuedLabRouteExecutor(execute: LabRouteExecutor): TrustedLabRouteExecutor {
|
|
16
|
+
const capability: TrustedLabRouteExecutor = Object.freeze({
|
|
17
|
+
execute,
|
|
18
|
+
enforcedBoundaries: Object.freeze([...REQUIRED_LAB_SANDBOX_BOUNDARIES]),
|
|
19
|
+
});
|
|
20
|
+
HOST_ISSUED_EXECUTORS.add(capability);
|
|
21
|
+
return capability;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Internal recognition check consumed by the read-only authority facade. */
|
|
25
|
+
export function isHostIssuedLabRouteExecutor(value: unknown): value is TrustedLabRouteExecutor {
|
|
26
|
+
if (typeof value !== "object" || value === null || !HOST_ISSUED_EXECUTORS.has(value as object)) return false;
|
|
27
|
+
const capability = value as TrustedLabRouteExecutor;
|
|
28
|
+
return capability.enforcedBoundaries.length === REQUIRED_LAB_SANDBOX_BOUNDARIES.length
|
|
29
|
+
&& REQUIRED_LAB_SANDBOX_BOUNDARIES.every((boundary, index) => capability.enforcedBoundaries[index] === boundary);
|
|
30
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { PinnedHttpError, pinnedHttpGet, pinnedHttpPost } from "./pinned-http";
|
|
2
|
+
import { TransportError } from "../lab/live/transport";
|
|
3
|
+
import type { LabCredentialLeaseV1, LabPinnedSender } from "../lab/live/types";
|
|
4
|
+
|
|
5
|
+
/** Only response metadata required by current live assertions crosses into Lab. */
|
|
6
|
+
const LAB_RESPONSE_HEADER_ALLOWLIST = ["content-type"] as const;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Trusted credential/transport owner. Secret headers exist only in this non-Lab module and are
|
|
10
|
+
* consumed directly by the pinned HTTP primitive; they are never returned to Lab code.
|
|
11
|
+
*/
|
|
12
|
+
export function createLabAuthorizedPinnedSender(
|
|
13
|
+
authorize: (lease: LabCredentialLeaseV1) => Promise<HeadersInit> | HeadersInit,
|
|
14
|
+
): LabPinnedSender {
|
|
15
|
+
return async (lease, destination, pinned, request, signal, limits) => {
|
|
16
|
+
const headers = await authorize(lease);
|
|
17
|
+
const url = `${destination.scheme}://${destination.host}:${destination.port}${destination.basePath}${request.path}`;
|
|
18
|
+
const options = {
|
|
19
|
+
headers,
|
|
20
|
+
maxBytes: limits.maxOutputBytes,
|
|
21
|
+
connectTimeoutMs: limits.connectTimeoutMs,
|
|
22
|
+
firstByteTimeoutMs: limits.firstByteTimeoutMs,
|
|
23
|
+
inactivityTimeoutMs: limits.inactivityTimeoutMs,
|
|
24
|
+
rejectUnauthorized: true,
|
|
25
|
+
context: "Lab provider response",
|
|
26
|
+
};
|
|
27
|
+
let response: Response;
|
|
28
|
+
let body: string;
|
|
29
|
+
try {
|
|
30
|
+
response = request.method === "POST"
|
|
31
|
+
? await pinnedHttpPost(url, pinned, request.body ?? "", signal, options)
|
|
32
|
+
: await pinnedHttpGet(url, pinned, signal, options);
|
|
33
|
+
body = await response.text();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (error instanceof PinnedHttpError) {
|
|
36
|
+
switch (error.code) {
|
|
37
|
+
case "connect_timeout":
|
|
38
|
+
throw new TransportError("connect_timeout", "pinned provider connection timed out");
|
|
39
|
+
case "first_byte_timeout":
|
|
40
|
+
throw new TransportError("first_byte_timeout", "pinned provider first byte timed out");
|
|
41
|
+
case "inactivity_timeout":
|
|
42
|
+
throw new TransportError("inactivity_timeout", "pinned provider response stalled");
|
|
43
|
+
case "output_byte_limit":
|
|
44
|
+
throw new TransportError("output_byte_limit", "pinned provider response exceeded byte budget");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
const responseHeaders: Record<string, string> = {};
|
|
50
|
+
for (const headerName of LAB_RESPONSE_HEADER_ALLOWLIST) {
|
|
51
|
+
const value = response.headers.get(headerName);
|
|
52
|
+
if (value !== null) responseHeaders[headerName] = value;
|
|
53
|
+
}
|
|
54
|
+
return { status: response.status, headers: responseHeaders, body };
|
|
55
|
+
};
|
|
56
|
+
}
|