@iislee/opencodex 2.11.0 → 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/README.md +114 -19
- package/bin/ocx.mjs +164 -36
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-BF38heuV.js +104 -0
- package/gui/dist/assets/index-DMiI18Kv.css +1 -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 +19 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +376 -52
- package/src/adapters/base.ts +54 -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 +601 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +129 -6
- package/src/adapters/cursor/discovery.ts +61 -4
- package/src/adapters/cursor/effort-map.ts +27 -3
- 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 +174 -58
- package/src/adapters/cursor/live-transport.ts +609 -170
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- 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 +0 -1
- package/src/adapters/cursor/native-exec.ts +101 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +383 -65
- package/src/adapters/cursor/request-builder.ts +237 -31
- package/src/adapters/cursor/tool-definitions.ts +242 -12
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +28 -1
- package/src/adapters/cursor.ts +133 -7
- package/src/adapters/google-antigravity-replay.ts +558 -34
- package/src/adapters/google-antigravity-wire.ts +43 -10
- 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 +618 -74
- package/src/adapters/identity.ts +39 -6
- 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 +10 -1
- package/src/adapters/kiro.ts +92 -54
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1311 -336
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +830 -56
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +104 -21
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +428 -61
- package/src/chat/inbound.ts +43 -19
- package/src/chat/outbound.ts +82 -26
- package/src/claude/agents-inject.ts +32 -9
- package/src/claude/context-windows.ts +21 -5
- 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 +70 -16
- package/src/cli/account-api.ts +35 -1
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +389 -2
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +30 -6
- package/src/cli/agent.ts +45 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +29 -4
- package/src/cli/claude.ts +103 -34
- 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 +593 -0
- package/src/cli/doctor.ts +315 -43
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +20 -271
- package/src/cli/index.ts +378 -519
- package/src/cli/init.ts +4 -17
- 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 +35 -2
- package/src/cli/models.ts +100 -14
- package/src/cli/observe.ts +92 -3
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/provider.ts +24 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +437 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +10 -3
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/v2.ts +105 -10
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1358 -21
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-priority.ts +83 -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 +493 -106
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +849 -242
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +345 -32
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +68 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +121 -30
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +529 -45
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +224 -30
- package/src/codex/catalog/provider-fetch.ts +1460 -134
- package/src/codex/catalog/sync.ts +1449 -186
- 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 +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/desired-state.ts +230 -0
- package/src/codex/features.ts +636 -39
- 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 +26 -20
- package/src/codex/history-provider.ts +231 -28
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1073 -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 +80 -0
- package/src/codex/journal.ts +66 -4
- 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 +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 +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 +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 +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +175 -13
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +505 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1009 -28
- package/src/codex/subagent-model-fallback.ts +350 -35
- package/src/codex/sync.ts +191 -2
- 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 +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +20 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +81 -9
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +1762 -140
- package/src/generated/compatibility-version.json +3164 -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 +152 -29
- package/src/images/plan.ts +23 -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.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/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +153 -9
- 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 +6 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +44 -2
- 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/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +4 -1
- 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/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 +202 -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 +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +398 -66
- package/src/oauth/key-providers.ts +16 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/local-token-detect.ts +11 -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/store.ts +119 -21
- package/src/oauth/token-guardian.ts +9 -3
- 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/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +22 -5
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +253 -6
- package/src/providers/fastwire.ts +501 -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 +1 -1
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -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 +29 -0
- package/src/providers/quota.ts +1319 -38
- package/src/providers/registry.ts +1429 -111
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +42 -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 +11 -4
- package/src/reasoning-effort.ts +49 -1
- package/src/responses/compaction.ts +26 -1
- 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 +220 -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 +426 -0
- package/src/responses/schema.ts +7 -1
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +565 -27
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +366 -30
- 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 +2 -29
- package/src/server/auth-cors.ts +267 -46
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +130 -56
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +159 -43
- 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 +865 -181
- package/src/server/lifecycle.ts +284 -13
- 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 +398 -103
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/combo-routes.ts +62 -24
- package/src/server/management/config-routes.ts +464 -51
- package/src/server/management/context.ts +80 -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 +101 -32
- package/src/server/management/model-routes.ts +189 -131
- 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 +80 -4
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +764 -157
- 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 +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 +288 -32
- 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 +115 -14
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +132 -5
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +236 -76
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log.ts +235 -22
- 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 +204 -35
- package/src/server/responses/compact.ts +442 -55
- package/src/server/responses/core.ts +2872 -331
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +79 -4
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -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 +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -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 +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 +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +292 -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 +39 -38
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1111 -90
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- 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 +845 -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 +104 -1236
- package/src/update/index.ts +32 -19
- package/src/update/job.ts +442 -67
- 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 +259 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +268 -16
- package/src/usage/log.ts +606 -41
- package/src/usage/summary.ts +177 -9
- 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 +9 -3
- 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 +88 -0
- package/src/web-search/executor.ts +11 -3
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +279 -50
- 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
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import type { ObservationEvent, ProtocolSubjectV1, RouteSubjectV1, TaskSubjectV1 } from "../events/types";
|
|
2
|
+
import { EVIDENCE_LAYERS, type ExecutionMode } from "../constants";
|
|
3
|
+
import type { SuiteManifestV1 } from "../conformance/suite-manifest";
|
|
4
|
+
import type { VerificationRole } from "../conformance/types";
|
|
5
|
+
import { isSha256Hex } from "../digest";
|
|
6
|
+
|
|
7
|
+
export interface VerificationEvaluation {
|
|
8
|
+
applicableRequiredScenarioIds: string[];
|
|
9
|
+
passingRequiredScenarioIds: string[];
|
|
10
|
+
missingRequiredScenarioIds: string[];
|
|
11
|
+
canVerify: boolean;
|
|
12
|
+
notes: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type LoadScenarioManifest = (digest: string) => Record<string, unknown> | null;
|
|
16
|
+
|
|
17
|
+
export interface ScenarioRequirements {
|
|
18
|
+
inboundProtocols?: string[];
|
|
19
|
+
upstreamProtocols?: string[];
|
|
20
|
+
surfaces?: string[];
|
|
21
|
+
requiredClaims?: string[];
|
|
22
|
+
requiredHarnessFeatures?: string[];
|
|
23
|
+
platforms?: string[];
|
|
24
|
+
routePreconditions?: string[];
|
|
25
|
+
freshness?: { maxAgeMs: number | null };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type LoadScenarioRequirements = (digest: string) => ScenarioRequirements | null;
|
|
29
|
+
|
|
30
|
+
/** Live-reserved scenarios are inapplicable in fixture-mode protocol conformance. */
|
|
31
|
+
export function isScenarioApplicable(
|
|
32
|
+
scenarioId: string,
|
|
33
|
+
executionMode: ExecutionMode,
|
|
34
|
+
evidenceLayer: string,
|
|
35
|
+
): boolean {
|
|
36
|
+
if (evidenceLayer === "protocol_conformance" && executionMode === "fixture") {
|
|
37
|
+
// The frozen CL-00 scenario schema has no explicit execution-mode field.
|
|
38
|
+
// Use an exact dot-delimited `live` segment rather than substring matching.
|
|
39
|
+
if (scenarioId.split(".").includes("live")) return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Match protocol subject fields against scenario requirement lists. */
|
|
45
|
+
function scenarioApplicableToRequirements(
|
|
46
|
+
requirements: ScenarioRequirements,
|
|
47
|
+
subject: ProtocolSubjectV1,
|
|
48
|
+
): boolean {
|
|
49
|
+
const inbound = requirements.inboundProtocols ?? [];
|
|
50
|
+
const upstream = requirements.upstreamProtocols ?? [];
|
|
51
|
+
const surfaces = requirements.surfaces ?? [];
|
|
52
|
+
return (
|
|
53
|
+
inbound.includes(subject.inboundProtocol) &&
|
|
54
|
+
upstream.includes(subject.upstreamProtocol) &&
|
|
55
|
+
surfaces.includes(subject.surface)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Applicability for live_route_compatibility using exact RouteSubjectV1 plus validated claim state. */
|
|
60
|
+
export function routeSubjectApplicableToRequirements(
|
|
61
|
+
requirements: ScenarioRequirements,
|
|
62
|
+
subject: RouteSubjectV1,
|
|
63
|
+
supportedClaims: readonly string[],
|
|
64
|
+
): boolean {
|
|
65
|
+
const inbound = requirements.inboundProtocols ?? [];
|
|
66
|
+
const upstream = requirements.upstreamProtocols ?? [];
|
|
67
|
+
const surfaces = requirements.surfaces ?? [];
|
|
68
|
+
const requiredClaims = requirements.requiredClaims ?? [];
|
|
69
|
+
const claimsOk = requiredClaims.every((claim) => supportedClaims.includes(claim));
|
|
70
|
+
return (
|
|
71
|
+
inbound.includes(subject.inboundProtocol) &&
|
|
72
|
+
upstream.includes(subject.upstreamProtocol) &&
|
|
73
|
+
surfaces.includes(subject.surface) &&
|
|
74
|
+
claimsOk
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Task-layer applicability against harness/platform/precondition state. */
|
|
79
|
+
export function taskSubjectApplicableToRequirements(
|
|
80
|
+
requirements: ScenarioRequirements,
|
|
81
|
+
capability: {
|
|
82
|
+
harnessFeatures: readonly string[];
|
|
83
|
+
platforms: readonly string[];
|
|
84
|
+
routePreconditions: readonly string[];
|
|
85
|
+
},
|
|
86
|
+
): boolean {
|
|
87
|
+
const platforms = requirements.platforms ?? ["*"];
|
|
88
|
+
const features = requirements.requiredHarnessFeatures ?? [];
|
|
89
|
+
const preconditions = requirements.routePreconditions ?? [];
|
|
90
|
+
const platformOk = platforms.includes("*")
|
|
91
|
+
|| capability.platforms.includes("*")
|
|
92
|
+
|| platforms.some((platform) => capability.platforms.includes(platform));
|
|
93
|
+
const featuresOk = features.every((feature) => capability.harnessFeatures.includes(feature));
|
|
94
|
+
const preconditionsOk = preconditions.every((item) => capability.routePreconditions.includes(item));
|
|
95
|
+
return platformOk && featuresOk && preconditionsOk;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Return true for non-negative integer values used in freshness parsing. */
|
|
99
|
+
function isNonNegativeInteger(value: unknown): value is number {
|
|
100
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Parse a freshness object from a scenario manifest fragment. */
|
|
104
|
+
function parseFreshness(value: unknown): { maxAgeMs: number | null } | null {
|
|
105
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
106
|
+
const maxAgeMs = (value as { maxAgeMs?: unknown }).maxAgeMs;
|
|
107
|
+
if (maxAgeMs === null) return { maxAgeMs: null };
|
|
108
|
+
if (!isNonNegativeInteger(maxAgeMs)) return null;
|
|
109
|
+
return { maxAgeMs };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Parse a string array requirement field from a manifest fragment. */
|
|
113
|
+
function parseStringArray(value: unknown): string[] | null {
|
|
114
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) return null;
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function scenarioContractFromManifest(
|
|
119
|
+
scenarioManifest: Record<string, unknown> | null,
|
|
120
|
+
): ScenarioRequirements | null {
|
|
121
|
+
if (!scenarioManifest) return null;
|
|
122
|
+
const req = scenarioManifest.requirements;
|
|
123
|
+
if (!req || typeof req !== "object" || Array.isArray(req)) return null;
|
|
124
|
+
const row = req as Record<string, unknown>;
|
|
125
|
+
const inboundProtocols = parseStringArray(row.inboundProtocols);
|
|
126
|
+
const upstreamProtocols = parseStringArray(row.upstreamProtocols);
|
|
127
|
+
const surfaces = parseStringArray(row.surfaces);
|
|
128
|
+
if (!inboundProtocols || !upstreamProtocols || !surfaces) return null;
|
|
129
|
+
const requiredClaims = row.requiredClaims === undefined ? [] : parseStringArray(row.requiredClaims);
|
|
130
|
+
if (!requiredClaims) return null;
|
|
131
|
+
const requiredHarnessFeatures = row.requiredHarnessFeatures === undefined
|
|
132
|
+
? []
|
|
133
|
+
: parseStringArray(row.requiredHarnessFeatures);
|
|
134
|
+
if (!requiredHarnessFeatures) return null;
|
|
135
|
+
const platforms = row.platforms === undefined ? ["*"] : parseStringArray(row.platforms);
|
|
136
|
+
if (!platforms) return null;
|
|
137
|
+
const routePreconditions = row.routePreconditions === undefined
|
|
138
|
+
? []
|
|
139
|
+
: parseStringArray(row.routePreconditions);
|
|
140
|
+
if (!routePreconditions) return null;
|
|
141
|
+
const freshness = parseFreshness(scenarioManifest.freshness);
|
|
142
|
+
if (!freshness) return null;
|
|
143
|
+
return {
|
|
144
|
+
inboundProtocols,
|
|
145
|
+
upstreamProtocols,
|
|
146
|
+
surfaces,
|
|
147
|
+
requiredClaims,
|
|
148
|
+
requiredHarnessFeatures,
|
|
149
|
+
platforms,
|
|
150
|
+
routePreconditions,
|
|
151
|
+
freshness,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function effectiveMaxAgeMs(
|
|
156
|
+
suiteMaxAgeMs: number | null,
|
|
157
|
+
scenarioMaxAgeMs: number | null,
|
|
158
|
+
): number | null {
|
|
159
|
+
if (suiteMaxAgeMs === null) return scenarioMaxAgeMs;
|
|
160
|
+
if (scenarioMaxAgeMs === null) return suiteMaxAgeMs;
|
|
161
|
+
return Math.min(suiteMaxAgeMs, scenarioMaxAgeMs);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function newestObservationByScenario(
|
|
165
|
+
observations: ObservationEvent[],
|
|
166
|
+
): Map<string, ObservationEvent> {
|
|
167
|
+
const byScenario = new Map<string, ObservationEvent>();
|
|
168
|
+
const ordered = [...observations].sort((a, b) => {
|
|
169
|
+
if (a.completedAt !== b.completedAt) return a.completedAt - b.completedAt;
|
|
170
|
+
return a.eventId < b.eventId ? -1 : a.eventId > b.eventId ? 1 : 0;
|
|
171
|
+
});
|
|
172
|
+
for (const obs of ordered) {
|
|
173
|
+
byScenario.set(obs.scenarioId, obs);
|
|
174
|
+
}
|
|
175
|
+
return byScenario;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Evaluate `all-applicable-required-pass-v1` per frozen CL-00 semantics.
|
|
180
|
+
* Positive VERIFIED requires a non-empty applicable required/control set and a
|
|
181
|
+
* current, fresh pass for every applicable required scenario and negative control.
|
|
182
|
+
*/
|
|
183
|
+
export function evaluateAllApplicableRequiredPassV1(
|
|
184
|
+
suiteManifest: SuiteManifestV1,
|
|
185
|
+
observations: ObservationEvent[],
|
|
186
|
+
executionMode: ExecutionMode,
|
|
187
|
+
opts: {
|
|
188
|
+
subject?: ProtocolSubjectV1 | RouteSubjectV1 | TaskSubjectV1;
|
|
189
|
+
/** For live projection this must come from validated current claim snapshots for subjectId. */
|
|
190
|
+
routeSupportedClaims?: readonly string[];
|
|
191
|
+
/** Fabric harness/platform/precondition state for task_effectiveness applicability. */
|
|
192
|
+
fabricCapability?: {
|
|
193
|
+
harnessFeatures: readonly string[];
|
|
194
|
+
platforms: readonly string[];
|
|
195
|
+
routePreconditions: readonly string[];
|
|
196
|
+
};
|
|
197
|
+
loadScenarioManifest?: LoadScenarioManifest;
|
|
198
|
+
loadScenarioRequirements?: LoadScenarioRequirements;
|
|
199
|
+
asOf?: number;
|
|
200
|
+
} = {},
|
|
201
|
+
): VerificationEvaluation {
|
|
202
|
+
const notes: string[] = [];
|
|
203
|
+
if (suiteManifest.verificationRule !== "all-applicable-required-pass-v1") {
|
|
204
|
+
return {
|
|
205
|
+
applicableRequiredScenarioIds: [],
|
|
206
|
+
passingRequiredScenarioIds: [],
|
|
207
|
+
missingRequiredScenarioIds: [],
|
|
208
|
+
canVerify: false,
|
|
209
|
+
notes: ["unsupported_verification_rule"],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (suiteManifest.evidenceLayer === "live_route_compatibility") {
|
|
213
|
+
if (opts.subject?.subjectKind !== "route") {
|
|
214
|
+
return { applicableRequiredScenarioIds: [], passingRequiredScenarioIds: [], missingRequiredScenarioIds: [], canVerify: false, notes: ["route_subject_required"] };
|
|
215
|
+
}
|
|
216
|
+
if (opts.routeSupportedClaims === undefined) {
|
|
217
|
+
return { applicableRequiredScenarioIds: [], passingRequiredScenarioIds: [], missingRequiredScenarioIds: [], canVerify: false, notes: ["route_claim_state_required"] };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (suiteManifest.evidenceLayer === "task_effectiveness") {
|
|
221
|
+
if (opts.subject?.subjectKind !== "task") {
|
|
222
|
+
return {
|
|
223
|
+
applicableRequiredScenarioIds: [],
|
|
224
|
+
passingRequiredScenarioIds: [],
|
|
225
|
+
missingRequiredScenarioIds: [],
|
|
226
|
+
canVerify: false,
|
|
227
|
+
notes: ["task_subject_required"],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (executionMode !== "fabric") {
|
|
231
|
+
return {
|
|
232
|
+
applicableRequiredScenarioIds: [],
|
|
233
|
+
passingRequiredScenarioIds: [],
|
|
234
|
+
missingRequiredScenarioIds: [],
|
|
235
|
+
canVerify: false,
|
|
236
|
+
notes: ["fabric_execution_mode_required"],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const requiredScenarios = suiteManifest.scenarios.filter(
|
|
242
|
+
(s) => s.role === "required" || s.role === "negative_control",
|
|
243
|
+
);
|
|
244
|
+
const applicableRequired: string[] = [];
|
|
245
|
+
const unavailableManifests: string[] = [];
|
|
246
|
+
const scenarioMaxAgeById = new Map<string, number | null>();
|
|
247
|
+
|
|
248
|
+
for (const s of requiredScenarios) {
|
|
249
|
+
if (!isScenarioApplicable(s.id, executionMode, suiteManifest.evidenceLayer)) continue;
|
|
250
|
+
|
|
251
|
+
let requirements: ScenarioRequirements | null = null;
|
|
252
|
+
const scenarioManifest = opts.loadScenarioManifest?.(s.manifestDigest) ?? null;
|
|
253
|
+
if (scenarioManifest) {
|
|
254
|
+
requirements = scenarioContractFromManifest(scenarioManifest);
|
|
255
|
+
if (!requirements) {
|
|
256
|
+
unavailableManifests.push(s.id);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
requirements = opts.loadScenarioRequirements?.(s.manifestDigest) ?? null;
|
|
261
|
+
if (!requirements || !requirements.freshness) {
|
|
262
|
+
unavailableManifests.push(s.id);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (suiteManifest.evidenceLayer === "protocol_conformance" && opts.subject?.subjectKind === "protocol") {
|
|
268
|
+
if (!scenarioApplicableToRequirements(requirements, opts.subject)) continue;
|
|
269
|
+
}
|
|
270
|
+
if (suiteManifest.evidenceLayer === "live_route_compatibility" && opts.subject?.subjectKind === "route") {
|
|
271
|
+
if (!routeSubjectApplicableToRequirements(requirements, opts.subject, opts.routeSupportedClaims!)) continue;
|
|
272
|
+
}
|
|
273
|
+
if (suiteManifest.evidenceLayer === "task_effectiveness" && opts.subject?.subjectKind === "task") {
|
|
274
|
+
const capability = opts.fabricCapability ?? {
|
|
275
|
+
harnessFeatures: ["fabric-scratch-v1"],
|
|
276
|
+
platforms: [process.platform, "*"],
|
|
277
|
+
routePreconditions: ["exact-route-subject"],
|
|
278
|
+
};
|
|
279
|
+
if (!taskSubjectApplicableToRequirements(requirements, capability)) continue;
|
|
280
|
+
}
|
|
281
|
+
applicableRequired.push(s.id);
|
|
282
|
+
scenarioMaxAgeById.set(s.id, requirements.freshness?.maxAgeMs ?? null);
|
|
283
|
+
}
|
|
284
|
+
applicableRequired.sort();
|
|
285
|
+
|
|
286
|
+
if (unavailableManifests.length > 0) {
|
|
287
|
+
return {
|
|
288
|
+
applicableRequiredScenarioIds: applicableRequired,
|
|
289
|
+
passingRequiredScenarioIds: [],
|
|
290
|
+
missingRequiredScenarioIds: [...new Set([...applicableRequired, ...unavailableManifests])].sort(),
|
|
291
|
+
canVerify: false,
|
|
292
|
+
notes: unavailableManifests.map((id) => `scenario_manifest_unavailable:${id}`),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (applicableRequired.length === 0) {
|
|
297
|
+
return {
|
|
298
|
+
applicableRequiredScenarioIds: [],
|
|
299
|
+
passingRequiredScenarioIds: [],
|
|
300
|
+
missingRequiredScenarioIds: [],
|
|
301
|
+
canVerify: false,
|
|
302
|
+
notes: ["empty_applicable_required_set"],
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const newest = newestObservationByScenario(observations);
|
|
307
|
+
const passing: string[] = [];
|
|
308
|
+
const missing: string[] = [];
|
|
309
|
+
const asOf = opts.asOf ?? observations.reduce((max, obs) => Math.max(max, obs.completedAt), 0);
|
|
310
|
+
|
|
311
|
+
for (const scenarioId of applicableRequired) {
|
|
312
|
+
const scenarioRef = requiredScenarios.find((s) => s.id === scenarioId)!;
|
|
313
|
+
const obs = newest.get(scenarioId);
|
|
314
|
+
if (!obs) {
|
|
315
|
+
missing.push(scenarioId);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (obs.scenarioManifestDigest !== scenarioRef.manifestDigest) {
|
|
319
|
+
missing.push(scenarioId);
|
|
320
|
+
notes.push(`digest_mismatch:${scenarioId}`);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const maxAgeMs = effectiveMaxAgeMs(
|
|
324
|
+
suiteManifest.freshness.maxAgeMs,
|
|
325
|
+
scenarioMaxAgeById.get(scenarioId) ?? null,
|
|
326
|
+
);
|
|
327
|
+
if (maxAgeMs !== null && asOf - obs.completedAt > maxAgeMs) {
|
|
328
|
+
missing.push(scenarioId);
|
|
329
|
+
notes.push(`stale_observation:${scenarioId}`);
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (obs.outcome !== "pass") {
|
|
333
|
+
missing.push(scenarioId);
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
passing.push(scenarioId);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
applicableRequiredScenarioIds: applicableRequired,
|
|
341
|
+
passingRequiredScenarioIds: passing,
|
|
342
|
+
missingRequiredScenarioIds: missing,
|
|
343
|
+
canVerify: missing.length === 0 && passing.length === applicableRequired.length,
|
|
344
|
+
notes,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function requireNonEmptyString(value: unknown): string | null {
|
|
349
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export function parseSuiteManifestFromArtifact(parsed: unknown): SuiteManifestV1 | null {
|
|
353
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
354
|
+
const raw = parsed as Record<string, unknown>;
|
|
355
|
+
if (raw.schemaVersion !== 1) return null;
|
|
356
|
+
|
|
357
|
+
const id = requireNonEmptyString(raw.id);
|
|
358
|
+
const version = requireNonEmptyString(raw.version);
|
|
359
|
+
const capability = requireNonEmptyString(raw.capability);
|
|
360
|
+
const assertionDslVersion = requireNonEmptyString(raw.assertionDslVersion);
|
|
361
|
+
const evidenceSchemaVersion = requireNonEmptyString(raw.evidenceSchemaVersion);
|
|
362
|
+
const contradictionRule = requireNonEmptyString(raw.contradictionRule);
|
|
363
|
+
const verificationRule = requireNonEmptyString(raw.verificationRule);
|
|
364
|
+
if (
|
|
365
|
+
!id || !version || !capability || !assertionDslVersion ||
|
|
366
|
+
!evidenceSchemaVersion || !contradictionRule ||
|
|
367
|
+
verificationRule !== "all-applicable-required-pass-v1"
|
|
368
|
+
) return null;
|
|
369
|
+
if (
|
|
370
|
+
typeof raw.evidenceLayer !== "string" ||
|
|
371
|
+
!(EVIDENCE_LAYERS as readonly string[]).includes(raw.evidenceLayer)
|
|
372
|
+
) return null;
|
|
373
|
+
const freshness = parseFreshness(raw.freshness);
|
|
374
|
+
if (!freshness || !Array.isArray(raw.scenarios)) return null;
|
|
375
|
+
|
|
376
|
+
const roles = new Set<VerificationRole>(["required", "supplemental", "negative_control"]);
|
|
377
|
+
const seenScenarioIds = new Set<string>();
|
|
378
|
+
const scenarios: SuiteManifestV1["scenarios"] = [];
|
|
379
|
+
for (const s of raw.scenarios) {
|
|
380
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) return null;
|
|
381
|
+
const row = s as Record<string, unknown>;
|
|
382
|
+
const scenarioId = requireNonEmptyString(row.id);
|
|
383
|
+
const scenarioVersion = requireNonEmptyString(row.version);
|
|
384
|
+
const manifestDigest = requireNonEmptyString(row.manifestDigest);
|
|
385
|
+
if (
|
|
386
|
+
!scenarioId || !scenarioVersion || !manifestDigest || !isSha256Hex(manifestDigest) ||
|
|
387
|
+
typeof row.role !== "string" || !roles.has(row.role as VerificationRole) ||
|
|
388
|
+
seenScenarioIds.has(scenarioId)
|
|
389
|
+
) return null;
|
|
390
|
+
seenScenarioIds.add(scenarioId);
|
|
391
|
+
scenarios.push({
|
|
392
|
+
id: scenarioId,
|
|
393
|
+
version: scenarioVersion,
|
|
394
|
+
role: row.role as VerificationRole,
|
|
395
|
+
manifestDigest,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
schemaVersion: 1,
|
|
401
|
+
id,
|
|
402
|
+
version,
|
|
403
|
+
evidenceLayer: raw.evidenceLayer,
|
|
404
|
+
capability,
|
|
405
|
+
assertionDslVersion,
|
|
406
|
+
evidenceSchemaVersion,
|
|
407
|
+
freshness,
|
|
408
|
+
contradictionRule,
|
|
409
|
+
scenarios,
|
|
410
|
+
verificationRule,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { jcsStringify } from "../digest";
|
|
2
|
+
import { publicEvidenceId } from "./ids";
|
|
3
|
+
import {
|
|
4
|
+
PUBLIC_EVIDENCE_BUNDLE_SCHEMA_VERSION,
|
|
5
|
+
PUBLIC_EXPORT_POLICY_VERSION,
|
|
6
|
+
type PublicArtifactV1,
|
|
7
|
+
type PublicEvidenceBundleUnsignedV1,
|
|
8
|
+
type PublicEvidenceRecordV1,
|
|
9
|
+
type PublicPublisherV1,
|
|
10
|
+
} from "./types";
|
|
11
|
+
import { PublicEvidenceValidationError, validatePublicEvidenceRecord } from "./validate";
|
|
12
|
+
|
|
13
|
+
export const MAX_PUBLIC_BUNDLE_BYTES = 2 * 1024 * 1024;
|
|
14
|
+
export const MAX_PUBLIC_BUNDLE_RECORDS = 256;
|
|
15
|
+
export const MAX_PUBLIC_BUNDLE_ARTIFACTS = 16;
|
|
16
|
+
export const MAX_PUBLIC_ARTIFACT_BYTES = 256 * 1024;
|
|
17
|
+
export const MAX_PUBLIC_ARTIFACT_BYTES_TOTAL = 1024 * 1024;
|
|
18
|
+
|
|
19
|
+
export interface PublicEvidenceContentInput {
|
|
20
|
+
records: PublicEvidenceRecordV1[];
|
|
21
|
+
artifacts: PublicArtifactV1[];
|
|
22
|
+
createdDayUtc: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface BuildPublicEvidenceBundleInput extends PublicEvidenceContentInput {
|
|
26
|
+
publisher: PublicPublisherV1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Deterministic, locale-independent code-unit ordering for canonical identity. */
|
|
30
|
+
function compareCanonicalId(a: string, b: string): number {
|
|
31
|
+
if (a < b) return -1;
|
|
32
|
+
if (a > b) return 1;
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function utcDay(value: string): string {
|
|
37
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
38
|
+
throw new PublicEvidenceValidationError("invalid_day", "createdDayUtc must be YYYY-MM-DD");
|
|
39
|
+
}
|
|
40
|
+
const parsed = new Date(`${value}T00:00:00.000Z`);
|
|
41
|
+
if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value) {
|
|
42
|
+
throw new PublicEvidenceValidationError("invalid_day", "createdDayUtc must be a real UTC day");
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function validatePublisher(publisher: PublicPublisherV1): PublicPublisherV1 {
|
|
48
|
+
const raw = publisher as unknown as Record<string, unknown>;
|
|
49
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
50
|
+
throw new PublicEvidenceValidationError("invalid_publisher", "publisher must be an object");
|
|
51
|
+
}
|
|
52
|
+
const keys = Object.keys(raw);
|
|
53
|
+
if (keys.some((key) => !["algorithm", "keyId", "publicKey"].includes(key))) {
|
|
54
|
+
throw new PublicEvidenceValidationError("unknown_field", "publisher contains unknown fields");
|
|
55
|
+
}
|
|
56
|
+
if (publisher.algorithm !== "ed25519") {
|
|
57
|
+
throw new PublicEvidenceValidationError("unsupported_algorithm", "publisher must use ed25519");
|
|
58
|
+
}
|
|
59
|
+
if (!/^[0-9a-f]{64}$/.test(publisher.keyId)) {
|
|
60
|
+
throw new PublicEvidenceValidationError("invalid_publisher", "publisher.keyId must be sha256 hex");
|
|
61
|
+
}
|
|
62
|
+
if (typeof publisher.publicKey !== "string" || publisher.publicKey.length === 0 || publisher.publicKey.length > 1024) {
|
|
63
|
+
throw new PublicEvidenceValidationError("invalid_publisher", "publisher.publicKey is invalid");
|
|
64
|
+
}
|
|
65
|
+
const publicKeyBytes = Buffer.from(publisher.publicKey, "base64");
|
|
66
|
+
if (publicKeyBytes.byteLength === 0 || publicKeyBytes.toString("base64") !== publisher.publicKey) {
|
|
67
|
+
throw new PublicEvidenceValidationError("invalid_publisher", "publisher.publicKey must use canonical base64");
|
|
68
|
+
}
|
|
69
|
+
const expectedKeyId = publicEvidenceId("publisher_key", {
|
|
70
|
+
algorithm: publisher.algorithm,
|
|
71
|
+
publicKey: publisher.publicKey,
|
|
72
|
+
});
|
|
73
|
+
if (publisher.keyId !== expectedKeyId) {
|
|
74
|
+
throw new PublicEvidenceValidationError("publisher_key_id_mismatch", "publisher.keyId does not match public key");
|
|
75
|
+
}
|
|
76
|
+
return { algorithm: "ed25519", keyId: publisher.keyId, publicKey: publisher.publicKey };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function validateArtifacts(artifacts: PublicArtifactV1[]): PublicArtifactV1[] {
|
|
80
|
+
if (!Array.isArray(artifacts) || artifacts.length > MAX_PUBLIC_BUNDLE_ARTIFACTS) {
|
|
81
|
+
throw new PublicEvidenceValidationError("array_too_large", `artifacts exceeds ${MAX_PUBLIC_BUNDLE_ARTIFACTS}`);
|
|
82
|
+
}
|
|
83
|
+
let aggregate = 0;
|
|
84
|
+
const ids = new Set<string>();
|
|
85
|
+
return artifacts.map((artifact, index) => {
|
|
86
|
+
const raw = artifact as unknown as Record<string, unknown>;
|
|
87
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
88
|
+
throw new PublicEvidenceValidationError("invalid_artifact", `artifacts[${index}] must be an object`);
|
|
89
|
+
}
|
|
90
|
+
if (Object.keys(raw).some((key) => !["artifactId", "artifactClass", "mediaType", "byteCount", "contentBase64"].includes(key))) {
|
|
91
|
+
throw new PublicEvidenceValidationError("unknown_field", `artifacts[${index}] contains unknown fields`);
|
|
92
|
+
}
|
|
93
|
+
if (!/^[0-9a-f]{64}$/.test(artifact.artifactId)) {
|
|
94
|
+
throw new PublicEvidenceValidationError("invalid_artifact", `artifacts[${index}].artifactId is invalid`);
|
|
95
|
+
}
|
|
96
|
+
if (typeof artifact.artifactClass !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:+-]{0,255}$/.test(artifact.artifactClass)) {
|
|
97
|
+
throw new PublicEvidenceValidationError("invalid_artifact", `artifacts[${index}].artifactClass is invalid`);
|
|
98
|
+
}
|
|
99
|
+
if (typeof artifact.mediaType !== "string" || artifact.mediaType.length === 0 || artifact.mediaType.length > 256) {
|
|
100
|
+
throw new PublicEvidenceValidationError("invalid_artifact", `artifacts[${index}].mediaType is invalid`);
|
|
101
|
+
}
|
|
102
|
+
if (!Number.isInteger(artifact.byteCount) || artifact.byteCount < 0 || artifact.byteCount > MAX_PUBLIC_ARTIFACT_BYTES) {
|
|
103
|
+
throw new PublicEvidenceValidationError("artifact_too_large", `artifacts[${index}].byteCount is invalid`);
|
|
104
|
+
}
|
|
105
|
+
let bytes: Buffer;
|
|
106
|
+
try {
|
|
107
|
+
bytes = Buffer.from(artifact.contentBase64, "base64");
|
|
108
|
+
} catch {
|
|
109
|
+
throw new PublicEvidenceValidationError("invalid_artifact", `artifacts[${index}].contentBase64 is invalid`);
|
|
110
|
+
}
|
|
111
|
+
if (bytes.byteLength !== artifact.byteCount || bytes.toString("base64") !== artifact.contentBase64) {
|
|
112
|
+
throw new PublicEvidenceValidationError("invalid_artifact", `artifacts[${index}] byte count or base64 is non-canonical`);
|
|
113
|
+
}
|
|
114
|
+
const expectedArtifactId = publicEvidenceId("artifact", {
|
|
115
|
+
artifactClass: artifact.artifactClass,
|
|
116
|
+
mediaType: artifact.mediaType,
|
|
117
|
+
byteCount: artifact.byteCount,
|
|
118
|
+
contentBase64: artifact.contentBase64,
|
|
119
|
+
});
|
|
120
|
+
if (artifact.artifactId !== expectedArtifactId) {
|
|
121
|
+
throw new PublicEvidenceValidationError("artifact_id_mismatch", `artifacts[${index}].artifactId mismatch`);
|
|
122
|
+
}
|
|
123
|
+
aggregate += artifact.byteCount;
|
|
124
|
+
if (aggregate > MAX_PUBLIC_ARTIFACT_BYTES_TOTAL) {
|
|
125
|
+
throw new PublicEvidenceValidationError("artifact_aggregate_too_large", "artifact aggregate exceeds 1 MiB");
|
|
126
|
+
}
|
|
127
|
+
if (ids.has(artifact.artifactId)) {
|
|
128
|
+
throw new PublicEvidenceValidationError("duplicate_id", "artifacts contains duplicate ids");
|
|
129
|
+
}
|
|
130
|
+
ids.add(artifact.artifactId);
|
|
131
|
+
return { ...artifact };
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Validate all publisher-independent bundle content before any signing-key state is touched. */
|
|
136
|
+
export function normalizePublicEvidenceContent(input: PublicEvidenceContentInput): PublicEvidenceContentInput {
|
|
137
|
+
if (!Array.isArray(input.records) || input.records.length > MAX_PUBLIC_BUNDLE_RECORDS) {
|
|
138
|
+
throw new PublicEvidenceValidationError("array_too_large", `records exceeds ${MAX_PUBLIC_BUNDLE_RECORDS}`);
|
|
139
|
+
}
|
|
140
|
+
const records = input.records
|
|
141
|
+
.map(validatePublicEvidenceRecord)
|
|
142
|
+
.sort((a, b) => compareCanonicalId(a.recordId, b.recordId));
|
|
143
|
+
if (new Set(records.map((record) => record.recordId)).size !== records.length) {
|
|
144
|
+
throw new PublicEvidenceValidationError("duplicate_id", "records contains duplicate ids");
|
|
145
|
+
}
|
|
146
|
+
const artifacts = validateArtifacts(input.artifacts)
|
|
147
|
+
.sort((a, b) => compareCanonicalId(a.artifactId, b.artifactId));
|
|
148
|
+
const artifactIds = new Set(artifacts.map((artifact) => artifact.artifactId));
|
|
149
|
+
for (const record of records) {
|
|
150
|
+
for (const artifactId of record.artifactRefs ?? []) {
|
|
151
|
+
if (!artifactIds.has(artifactId)) {
|
|
152
|
+
throw new PublicEvidenceValidationError("artifact_ref_missing", `record ${record.recordId} references a missing public artifact`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { records, artifacts, createdDayUtc: utcDay(input.createdDayUtc) };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function canonicalPublicEvidenceContent(
|
|
160
|
+
input: PublicEvidenceContentInput,
|
|
161
|
+
): { canonical: boolean; normalized: PublicEvidenceContentInput } {
|
|
162
|
+
const normalized = normalizePublicEvidenceContent(input);
|
|
163
|
+
const canonical = input.records.length === normalized.records.length
|
|
164
|
+
&& input.artifacts.length === normalized.artifacts.length
|
|
165
|
+
&& input.records.every((record, index) => record.recordId === normalized.records[index]!.recordId)
|
|
166
|
+
&& input.artifacts.every((artifact, index) => artifact.artifactId === normalized.artifacts[index]!.artifactId);
|
|
167
|
+
return { canonical, normalized };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function hasCanonicalPublicEvidenceOrder(input: PublicEvidenceContentInput): boolean {
|
|
171
|
+
return canonicalPublicEvidenceContent(input).canonical;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function buildFromNormalizedContent(
|
|
175
|
+
normalized: PublicEvidenceContentInput,
|
|
176
|
+
publisherInput: PublicPublisherV1,
|
|
177
|
+
): PublicEvidenceBundleUnsignedV1 {
|
|
178
|
+
const publisher = validatePublisher(publisherInput);
|
|
179
|
+
const content = {
|
|
180
|
+
schemaVersion: PUBLIC_EVIDENCE_BUNDLE_SCHEMA_VERSION,
|
|
181
|
+
exportPolicyVersion: PUBLIC_EXPORT_POLICY_VERSION,
|
|
182
|
+
createdDayUtc: normalized.createdDayUtc,
|
|
183
|
+
publisher,
|
|
184
|
+
records: normalized.records,
|
|
185
|
+
artifacts: normalized.artifacts,
|
|
186
|
+
};
|
|
187
|
+
const bundleId = publicEvidenceId("bundle", content);
|
|
188
|
+
const bundleDigest = publicEvidenceId("bundle_digest", { ...content, bundleId });
|
|
189
|
+
const bundle: PublicEvidenceBundleUnsignedV1 = { ...content, bundleId, bundleDigest };
|
|
190
|
+
if (new TextEncoder().encode(jcsStringify(bundle)).byteLength > MAX_PUBLIC_BUNDLE_BYTES) {
|
|
191
|
+
throw new PublicEvidenceValidationError("bundle_too_large", "public bundle exceeds 2 MiB");
|
|
192
|
+
}
|
|
193
|
+
return bundle;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function buildPublicEvidenceBundle(input: BuildPublicEvidenceBundleInput): PublicEvidenceBundleUnsignedV1 {
|
|
197
|
+
return buildFromNormalizedContent(normalizePublicEvidenceContent(input), input.publisher);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function expectedPublicBundleIdentityFromNormalized(
|
|
201
|
+
normalized: PublicEvidenceContentInput,
|
|
202
|
+
publisher: PublicPublisherV1,
|
|
203
|
+
): { bundleId: string; bundleDigest: string } {
|
|
204
|
+
const rebuilt = buildFromNormalizedContent(normalized, publisher);
|
|
205
|
+
return { bundleId: rebuilt.bundleId, bundleDigest: rebuilt.bundleDigest };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function expectedPublicBundleIdentity(bundle: PublicEvidenceBundleUnsignedV1): { bundleId: string; bundleDigest: string } {
|
|
209
|
+
return expectedPublicBundleIdentityFromNormalized(
|
|
210
|
+
normalizePublicEvidenceContent({
|
|
211
|
+
records: bundle.records,
|
|
212
|
+
artifacts: bundle.artifacts,
|
|
213
|
+
createdDayUtc: bundle.createdDayUtc,
|
|
214
|
+
}),
|
|
215
|
+
bundle.publisher,
|
|
216
|
+
);
|
|
217
|
+
}
|