oh-my-super-agent 0.1.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/agents/advisor.md +15 -0
- package/.agents/agents/conductor.md +32 -0
- package/.agents/agents/dreamer.md +13 -0
- package/.agents/agents/executor.md +17 -0
- package/.agents/agents/grader.md +15 -0
- package/.agents/config/endurance-policy.json +8 -0
- package/.agents/config/oms-config.json +119 -0
- package/.agents/config/release-policy.json +12 -0
- package/.agents/config/secret-scan-policy.json +7 -0
- package/.agents/config/verification-plan.json +33 -0
- package/.agents/registry.json +11 -0
- package/.agents/rules/constitution.md +7 -0
- package/.agents/skills/conductor/SKILL.md +23 -0
- package/.agents/skills/dream/SKILL.md +14 -0
- package/.agents/skills/executor/SKILL.md +13 -0
- package/.agents/skills/oms/SKILL.md +52 -0
- package/.agents/skills/quota-aware/SKILL.md +12 -0
- package/.agents/skills/schedule/SKILL.md +13 -0
- package/.agents/skills/team-protocol/SKILL.md +20 -0
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/src/audit/run-ledger.d.ts +183 -0
- package/dist/src/audit/run-ledger.js +1655 -0
- package/dist/src/audit/run-ledger.js.map +1 -0
- package/dist/src/cli.d.ts +61 -0
- package/dist/src/cli.js +2096 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/github-queue-probe.d.ts +26 -0
- package/dist/src/commands/github-queue-probe.js +51 -0
- package/dist/src/commands/github-queue-probe.js.map +1 -0
- package/dist/src/commands/trace-fixture.d.ts +20 -0
- package/dist/src/commands/trace-fixture.js +72 -0
- package/dist/src/commands/trace-fixture.js.map +1 -0
- package/dist/src/conductor/activity.d.ts +46 -0
- package/dist/src/conductor/activity.js +224 -0
- package/dist/src/conductor/activity.js.map +1 -0
- package/dist/src/conductor/bridge.d.ts +53 -0
- package/dist/src/conductor/bridge.js +652 -0
- package/dist/src/conductor/bridge.js.map +1 -0
- package/dist/src/conductor/notifications.d.ts +59 -0
- package/dist/src/conductor/notifications.js +381 -0
- package/dist/src/conductor/notifications.js.map +1 -0
- package/dist/src/conductor/recovery.d.ts +29 -0
- package/dist/src/conductor/recovery.js +198 -0
- package/dist/src/conductor/recovery.js.map +1 -0
- package/dist/src/conductor/start.d.ts +73 -0
- package/dist/src/conductor/start.js +519 -0
- package/dist/src/conductor/start.js.map +1 -0
- package/dist/src/conductor/waiter.d.ts +88 -0
- package/dist/src/conductor/waiter.js +427 -0
- package/dist/src/conductor/waiter.js.map +1 -0
- package/dist/src/config.d.ts +9 -0
- package/dist/src/config.js +40 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/dispatch/advice-source.d.ts +61 -0
- package/dist/src/dispatch/advice-source.js +198 -0
- package/dist/src/dispatch/advice-source.js.map +1 -0
- package/dist/src/dispatch/advice-store.d.ts +41 -0
- package/dist/src/dispatch/advice-store.js +201 -0
- package/dist/src/dispatch/advice-store.js.map +1 -0
- package/dist/src/dispatch/advice.d.ts +34 -0
- package/dist/src/dispatch/advice.js +371 -0
- package/dist/src/dispatch/advice.js.map +1 -0
- package/dist/src/dispatch/authority.d.ts +124 -0
- package/dist/src/dispatch/authority.js +1202 -0
- package/dist/src/dispatch/authority.js.map +1 -0
- package/dist/src/dispatch/intent.d.ts +66 -0
- package/dist/src/dispatch/intent.js +71 -0
- package/dist/src/dispatch/intent.js.map +1 -0
- package/dist/src/dispatch/runtime-authority.d.ts +42 -0
- package/dist/src/dispatch/runtime-authority.js +52 -0
- package/dist/src/dispatch/runtime-authority.js.map +1 -0
- package/dist/src/doctor.d.ts +20 -0
- package/dist/src/doctor.js +85 -0
- package/dist/src/doctor.js.map +1 -0
- package/dist/src/domain/records.d.ts +44 -0
- package/dist/src/domain/records.js +138 -0
- package/dist/src/domain/records.js.map +1 -0
- package/dist/src/domain/role-policy.d.ts +67 -0
- package/dist/src/domain/role-policy.js +547 -0
- package/dist/src/domain/role-policy.js.map +1 -0
- package/dist/src/events/bus.d.ts +32 -0
- package/dist/src/events/bus.js +115 -0
- package/dist/src/events/bus.js.map +1 -0
- package/dist/src/events/kinds.d.ts +126 -0
- package/dist/src/events/kinds.js +14 -0
- package/dist/src/events/kinds.js.map +1 -0
- package/dist/src/evidence/immutable-file.d.ts +12 -0
- package/dist/src/evidence/immutable-file.js +141 -0
- package/dist/src/evidence/immutable-file.js.map +1 -0
- package/dist/src/evidence/store.d.ts +125 -0
- package/dist/src/evidence/store.js +1189 -0
- package/dist/src/evidence/store.js.map +1 -0
- package/dist/src/evidence/verdict.d.ts +17 -0
- package/dist/src/evidence/verdict.js +23 -0
- package/dist/src/evidence/verdict.js.map +1 -0
- package/dist/src/generation/agent.d.ts +5 -0
- package/dist/src/generation/agent.js +34 -0
- package/dist/src/generation/agent.js.map +1 -0
- package/dist/src/generation/frontmatter.d.ts +7 -0
- package/dist/src/generation/frontmatter.js +33 -0
- package/dist/src/generation/frontmatter.js.map +1 -0
- package/dist/src/github/adapter.d.ts +44 -0
- package/dist/src/github/adapter.js +592 -0
- package/dist/src/github/adapter.js.map +1 -0
- package/dist/src/github/dispatch-eligibility.d.ts +9 -0
- package/dist/src/github/dispatch-eligibility.js +94 -0
- package/dist/src/github/dispatch-eligibility.js.map +1 -0
- package/dist/src/github/projector.d.ts +23 -0
- package/dist/src/github/projector.js +181 -0
- package/dist/src/github/projector.js.map +1 -0
- package/dist/src/github/review.d.ts +39 -0
- package/dist/src/github/review.js +1450 -0
- package/dist/src/github/review.js.map +1 -0
- package/dist/src/github/sync.d.ts +21 -0
- package/dist/src/github/sync.js +518 -0
- package/dist/src/github/sync.js.map +1 -0
- package/dist/src/github/types.d.ts +134 -0
- package/dist/src/github/types.js +11 -0
- package/dist/src/github/types.js.map +1 -0
- package/dist/src/goals/artifact-store.d.ts +31 -0
- package/dist/src/goals/artifact-store.js +241 -0
- package/dist/src/goals/artifact-store.js.map +1 -0
- package/dist/src/goals/authority.d.ts +112 -0
- package/dist/src/goals/authority.js +621 -0
- package/dist/src/goals/authority.js.map +1 -0
- package/dist/src/goals/native-snapshot.d.ts +12 -0
- package/dist/src/goals/native-snapshot.js +53 -0
- package/dist/src/goals/native-snapshot.js.map +1 -0
- package/dist/src/goals/runtime-authority.d.ts +7 -0
- package/dist/src/goals/runtime-authority.js +20 -0
- package/dist/src/goals/runtime-authority.js.map +1 -0
- package/dist/src/health/release-assessment.d.ts +11 -0
- package/dist/src/health/release-assessment.js +83 -0
- package/dist/src/health/release-assessment.js.map +1 -0
- package/dist/src/health/release-policy.d.ts +16 -0
- package/dist/src/health/release-policy.js +40 -0
- package/dist/src/health/release-policy.js.map +1 -0
- package/dist/src/hooks/native.d.ts +30 -0
- package/dist/src/hooks/native.js +495 -0
- package/dist/src/hooks/native.js.map +1 -0
- package/dist/src/hooks/provider-runtime.d.ts +55 -0
- package/dist/src/hooks/provider-runtime.js +479 -0
- package/dist/src/hooks/provider-runtime.js.map +1 -0
- package/dist/src/hooks/provider.d.ts +102 -0
- package/dist/src/hooks/provider.js +358 -0
- package/dist/src/hooks/provider.js.map +1 -0
- package/dist/src/live/contact-journal.d.ts +73 -0
- package/dist/src/live/contact-journal.js +439 -0
- package/dist/src/live/contact-journal.js.map +1 -0
- package/dist/src/live-probes/gh-adapter.d.ts +28 -0
- package/dist/src/live-probes/gh-adapter.js +188 -0
- package/dist/src/live-probes/gh-adapter.js.map +1 -0
- package/dist/src/live-probes/github-queue.d.ts +1 -0
- package/dist/src/live-probes/github-queue.js +2 -0
- package/dist/src/live-probes/github-queue.js.map +1 -0
- package/dist/src/notifications/slack-request.d.ts +13 -0
- package/dist/src/notifications/slack-request.js +40 -0
- package/dist/src/notifications/slack-request.js.map +1 -0
- package/dist/src/outbox/processor.d.ts +27 -0
- package/dist/src/outbox/processor.js +63 -0
- package/dist/src/outbox/processor.js.map +1 -0
- package/dist/src/outbox/store.d.ts +158 -0
- package/dist/src/outbox/store.js +834 -0
- package/dist/src/outbox/store.js.map +1 -0
- package/dist/src/package-root.d.ts +1 -0
- package/dist/src/package-root.js +26 -0
- package/dist/src/package-root.js.map +1 -0
- package/dist/src/project.d.ts +12 -0
- package/dist/src/project.js +60 -0
- package/dist/src/project.js.map +1 -0
- package/dist/src/projection/generated-file.d.ts +15 -0
- package/dist/src/projection/generated-file.js +72 -0
- package/dist/src/projection/generated-file.js.map +1 -0
- package/dist/src/projection/project-workspace.d.ts +11 -0
- package/dist/src/projection/project-workspace.js +94 -0
- package/dist/src/projection/project-workspace.js.map +1 -0
- package/dist/src/projection/registry.d.ts +15 -0
- package/dist/src/projection/registry.js +59 -0
- package/dist/src/projection/registry.js.map +1 -0
- package/dist/src/projection/safe-path.d.ts +4 -0
- package/dist/src/projection/safe-path.js +106 -0
- package/dist/src/projection/safe-path.js.map +1 -0
- package/dist/src/providers/claude-interactive.d.ts +38 -0
- package/dist/src/providers/claude-interactive.js +287 -0
- package/dist/src/providers/claude-interactive.js.map +1 -0
- package/dist/src/providers/claude-structured-bridge.d.ts +32 -0
- package/dist/src/providers/claude-structured-bridge.js +215 -0
- package/dist/src/providers/claude-structured-bridge.js.map +1 -0
- package/dist/src/providers/claude-structured-completion.d.ts +23 -0
- package/dist/src/providers/claude-structured-completion.js +229 -0
- package/dist/src/providers/claude-structured-completion.js.map +1 -0
- package/dist/src/providers/claude-structured-pane.d.ts +23 -0
- package/dist/src/providers/claude-structured-pane.js +156 -0
- package/dist/src/providers/claude-structured-pane.js.map +1 -0
- package/dist/src/providers/claude-structured-store.d.ts +48 -0
- package/dist/src/providers/claude-structured-store.js +212 -0
- package/dist/src/providers/claude-structured-store.js.map +1 -0
- package/dist/src/providers/claude-structured.d.ts +56 -0
- package/dist/src/providers/claude-structured.js +250 -0
- package/dist/src/providers/claude-structured.js.map +1 -0
- package/dist/src/providers/codex-exec.d.ts +29 -0
- package/dist/src/providers/codex-exec.js +127 -0
- package/dist/src/providers/codex-exec.js.map +1 -0
- package/dist/src/providers/grading.d.ts +2 -0
- package/dist/src/providers/grading.js +20 -0
- package/dist/src/providers/grading.js.map +1 -0
- package/dist/src/providers/native-dispatch.d.ts +24 -0
- package/dist/src/providers/native-dispatch.js +34 -0
- package/dist/src/providers/native-dispatch.js.map +1 -0
- package/dist/src/providers/preflight.d.ts +65 -0
- package/dist/src/providers/preflight.js +256 -0
- package/dist/src/providers/preflight.js.map +1 -0
- package/dist/src/providers/process.d.ts +18 -0
- package/dist/src/providers/process.js +21 -0
- package/dist/src/providers/process.js.map +1 -0
- package/dist/src/queue/model.d.ts +37 -0
- package/dist/src/queue/model.js +51 -0
- package/dist/src/queue/model.js.map +1 -0
- package/dist/src/queue/service.d.ts +1 -0
- package/dist/src/queue/service.js +2 -0
- package/dist/src/queue/service.js.map +1 -0
- package/dist/src/reflections/store.d.ts +21 -0
- package/dist/src/reflections/store.js +60 -0
- package/dist/src/reflections/store.js.map +1 -0
- package/dist/src/reports/daily-trust.d.ts +11 -0
- package/dist/src/reports/daily-trust.js +98 -0
- package/dist/src/reports/daily-trust.js.map +1 -0
- package/dist/src/runs/controller.d.ts +187 -0
- package/dist/src/runs/controller.js +1182 -0
- package/dist/src/runs/controller.js.map +1 -0
- package/dist/src/runtime/composition.d.ts +9 -0
- package/dist/src/runtime/composition.js +35 -0
- package/dist/src/runtime/composition.js.map +1 -0
- package/dist/src/runtime/endurance-authorities.d.ts +44 -0
- package/dist/src/runtime/endurance-authorities.js +101 -0
- package/dist/src/runtime/endurance-authorities.js.map +1 -0
- package/dist/src/runtime/endurance-policy.d.ts +12 -0
- package/dist/src/runtime/endurance-policy.js +13 -0
- package/dist/src/runtime/endurance-policy.js.map +1 -0
- package/dist/src/runtime/lock.d.ts +63 -0
- package/dist/src/runtime/lock.js +577 -0
- package/dist/src/runtime/lock.js.map +1 -0
- package/dist/src/runtime/process-identity.d.ts +10 -0
- package/dist/src/runtime/process-identity.js +43 -0
- package/dist/src/runtime/process-identity.js.map +1 -0
- package/dist/src/runtime/quota.d.ts +130 -0
- package/dist/src/runtime/quota.js +590 -0
- package/dist/src/runtime/quota.js.map +1 -0
- package/dist/src/runtime/record-store.d.ts +45 -0
- package/dist/src/runtime/record-store.js +272 -0
- package/dist/src/runtime/record-store.js.map +1 -0
- package/dist/src/runtime/schema.d.ts +28 -0
- package/dist/src/runtime/schema.js +194 -0
- package/dist/src/runtime/schema.js.map +1 -0
- package/dist/src/runtime/stable-id.d.ts +3 -0
- package/dist/src/runtime/stable-id.js +27 -0
- package/dist/src/runtime/stable-id.js.map +1 -0
- package/dist/src/runtime/system-clock.d.ts +8 -0
- package/dist/src/runtime/system-clock.js +35 -0
- package/dist/src/runtime/system-clock.js.map +1 -0
- package/dist/src/schedule/service-plan.d.ts +58 -0
- package/dist/src/schedule/service-plan.js +327 -0
- package/dist/src/schedule/service-plan.js.map +1 -0
- package/dist/src/schedule/user-installation.d.ts +62 -0
- package/dist/src/schedule/user-installation.js +701 -0
- package/dist/src/schedule/user-installation.js.map +1 -0
- package/dist/src/schedule/user-plan.d.ts +47 -0
- package/dist/src/schedule/user-plan.js +286 -0
- package/dist/src/schedule/user-plan.js.map +1 -0
- package/dist/src/scheduler/minute.d.ts +31 -0
- package/dist/src/scheduler/minute.js +441 -0
- package/dist/src/scheduler/minute.js.map +1 -0
- package/dist/src/schemas/registry.d.ts +10 -0
- package/dist/src/schemas/registry.js +36 -0
- package/dist/src/schemas/registry.js.map +1 -0
- package/dist/src/security/secret-scan.d.ts +50 -0
- package/dist/src/security/secret-scan.js +384 -0
- package/dist/src/security/secret-scan.js.map +1 -0
- package/dist/src/sessions/registry.d.ts +89 -0
- package/dist/src/sessions/registry.js +443 -0
- package/dist/src/sessions/registry.js.map +1 -0
- package/dist/src/state.d.ts +27 -0
- package/dist/src/state.js +235 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/status/hud.d.ts +37 -0
- package/dist/src/status/hud.js +88 -0
- package/dist/src/status/hud.js.map +1 -0
- package/dist/src/team/plan.d.ts +31 -0
- package/dist/src/team/plan.js +184 -0
- package/dist/src/team/plan.js.map +1 -0
- package/dist/src/team/projection.d.ts +24 -0
- package/dist/src/team/projection.js +79 -0
- package/dist/src/team/projection.js.map +1 -0
- package/dist/src/tickets/admission.d.ts +34 -0
- package/dist/src/tickets/admission.js +169 -0
- package/dist/src/tickets/admission.js.map +1 -0
- package/dist/src/tickets/materializer.d.ts +1 -0
- package/dist/src/tickets/materializer.js +2 -0
- package/dist/src/tickets/materializer.js.map +1 -0
- package/dist/src/tickets/runtime-authority.d.ts +32 -0
- package/dist/src/tickets/runtime-authority.js +46 -0
- package/dist/src/tickets/runtime-authority.js.map +1 -0
- package/dist/src/tickets/store.d.ts +221 -0
- package/dist/src/tickets/store.js +2025 -0
- package/dist/src/tickets/store.js.map +1 -0
- package/dist/src/tracer/one-ticket.d.ts +58 -0
- package/dist/src/tracer/one-ticket.js +578 -0
- package/dist/src/tracer/one-ticket.js.map +1 -0
- package/dist/src/trust/assessment.d.ts +24 -0
- package/dist/src/trust/assessment.js +124 -0
- package/dist/src/trust/assessment.js.map +1 -0
- package/dist/src/trust/runs.d.ts +13 -0
- package/dist/src/trust/runs.js +46 -0
- package/dist/src/trust/runs.js.map +1 -0
- package/dist/src/types.d.ts +6 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/fs.d.ts +13 -0
- package/dist/src/utils/fs.js +188 -0
- package/dist/src/utils/fs.js.map +1 -0
- package/dist/src/verification/authority.d.ts +28 -0
- package/dist/src/verification/authority.js +153 -0
- package/dist/src/verification/authority.js.map +1 -0
- package/dist/src/verification/grade-source.d.ts +17 -0
- package/dist/src/verification/grade-source.js +30 -0
- package/dist/src/verification/grade-source.js.map +1 -0
- package/dist/src/verification/plan.d.ts +20 -0
- package/dist/src/verification/plan.js +128 -0
- package/dist/src/verification/plan.js.map +1 -0
- package/dist/src/verification/protected-runner.d.ts +50 -0
- package/dist/src/verification/protected-runner.js +171 -0
- package/dist/src/verification/protected-runner.js.map +1 -0
- package/dist/src/verification/result-gate-store.d.ts +72 -0
- package/dist/src/verification/result-gate-store.js +193 -0
- package/dist/src/verification/result-gate-store.js.map +1 -0
- package/dist/src/verification/result-gate.d.ts +41 -0
- package/dist/src/verification/result-gate.js +731 -0
- package/dist/src/verification/result-gate.js.map +1 -0
- package/dist/src/verification/runner.d.ts +38 -0
- package/dist/src/verification/runner.js +143 -0
- package/dist/src/verification/runner.js.map +1 -0
- package/dist/src/verification/sandbox.d.ts +16 -0
- package/dist/src/verification/sandbox.js +199 -0
- package/dist/src/verification/sandbox.js.map +1 -0
- package/dist/src/verify.d.ts +8 -0
- package/dist/src/verify.js +35 -0
- package/dist/src/verify.js.map +1 -0
- package/dist/src/watchdog/reconcile.d.ts +42 -0
- package/dist/src/watchdog/reconcile.js +440 -0
- package/dist/src/watchdog/reconcile.js.map +1 -0
- package/dist/src/workers/bridge.d.ts +35 -0
- package/dist/src/workers/bridge.js +426 -0
- package/dist/src/workers/bridge.js.map +1 -0
- package/dist/src/workers/delivery.d.ts +31 -0
- package/dist/src/workers/delivery.js +876 -0
- package/dist/src/workers/delivery.js.map +1 -0
- package/dist/src/workers/panes.d.ts +32 -0
- package/dist/src/workers/panes.js +212 -0
- package/dist/src/workers/panes.js.map +1 -0
- package/dist/src/workers/registry.d.ts +80 -0
- package/dist/src/workers/registry.js +351 -0
- package/dist/src/workers/registry.js.map +1 -0
- package/dist/src/workers/runtime.d.ts +44 -0
- package/dist/src/workers/runtime.js +21 -0
- package/dist/src/workers/runtime.js.map +1 -0
- package/dist/src/worktrees/manager.d.ts +35 -0
- package/dist/src/worktrees/manager.js +315 -0
- package/dist/src/worktrees/manager.js.map +1 -0
- package/docs/ARCHITECTURE.md +323 -0
- package/docs/PLAN.md +216 -0
- package/docs/REFERENCES.md +42 -0
- package/docs/ROADMAP.md +327 -0
- package/docs/SECURITY.md +242 -0
- package/docs/adr/0001-tracer-first-file-backed-certification.md +41 -0
- package/docs/adr/0002-conductor-first-typed-dispatch.md +102 -0
- package/docs/adr/0003-mandatory-pre-dispatch-advice.md +59 -0
- package/docs/compatibility/claude-code-2.1.207.md +25 -0
- package/loop/contract.md +102 -0
- package/package.json +54 -0
- package/schemas/advisor-evidence.schema.json +54 -0
- package/schemas/advisor-output.schema.json +31 -0
- package/schemas/conductor-activity-payload.schema.json +38 -0
- package/schemas/conductor-decision.schema.json +29 -0
- package/schemas/conductor-dispatch-intent.schema.json +71 -0
- package/schemas/conductor-dispatch-preparation.schema.json +46 -0
- package/schemas/conductor-dispatch-proposal.schema.json +82 -0
- package/schemas/conductor-goal-proposal.schema.json +30 -0
- package/schemas/conductor-manage-worker.schema.json +17 -0
- package/schemas/conductor-notification-payload.schema.json +39 -0
- package/schemas/conductor-process-result.schema.json +14 -0
- package/schemas/conductor-route.schema.json +51 -0
- package/schemas/conductor-start-team.schema.json +16 -0
- package/schemas/conductor-waiter-payload.schema.json +68 -0
- package/schemas/deterministic-check.schema.json +107 -0
- package/schemas/dispatch-acknowledgement.schema.json +15 -0
- package/schemas/dispatch-advice-payload.schema.json +64 -0
- package/schemas/dispatch-payload.schema.json +131 -0
- package/schemas/dispatch-result.schema.json +28 -0
- package/schemas/endurance-policy.schema.json +16 -0
- package/schemas/events/dispatch-delivery-requested.schema.json +17 -0
- package/schemas/events/github-observed.schema.json +28 -0
- package/schemas/events/native-hook-observed.schema.json +35 -0
- package/schemas/events/provider-observed.schema.json +64 -0
- package/schemas/events/run-heartbeat-due.schema.json +10 -0
- package/schemas/events/slack-notification-requested.schema.json +14 -0
- package/schemas/events/structured-role-completed.schema.json +54 -0
- package/schemas/events/ticket-resume-due.schema.json +28 -0
- package/schemas/events/watchdog-lease-expired.schema.json +14 -0
- package/schemas/events/watchdog-outbox-stalled.schema.json +12 -0
- package/schemas/events/watchdog-session-stalled.schema.json +11 -0
- package/schemas/evidence-record.schema.json +37 -0
- package/schemas/executor-result.schema.json +17 -0
- package/schemas/github-observation.schema.json +33 -0
- package/schemas/github-watermark.schema.json +66 -0
- package/schemas/goal-artifact.schema.json +19 -0
- package/schemas/goal-create-input.schema.json +14 -0
- package/schemas/goal-executor-result.schema.json +18 -0
- package/schemas/goal-payload.schema.json +95 -0
- package/schemas/grader-output.schema.json +33 -0
- package/schemas/lease-expiry.schema.json +19 -0
- package/schemas/live-disclosure.schema.json +16 -0
- package/schemas/live-outcome.schema.json +52 -0
- package/schemas/native-goal-snapshot.schema.json +27 -0
- package/schemas/provider-observed.schema.json +64 -0
- package/schemas/quota-observation.schema.json +72 -0
- package/schemas/reflection-evidence.schema.json +61 -0
- package/schemas/reflection.schema.json +13 -0
- package/schemas/regression-request.schema.json +54 -0
- package/schemas/release-assessment.schema.json +34 -0
- package/schemas/release-policy.schema.json +30 -0
- package/schemas/result-gate-payload.schema.json +244 -0
- package/schemas/review-delivery-payload.schema.json +42 -0
- package/schemas/role-policy.schema.json +111 -0
- package/schemas/run-abort-input.schema.json +11 -0
- package/schemas/run-completion-proof.schema.json +62 -0
- package/schemas/run-ledger-entry.schema.json +78 -0
- package/schemas/run-manifest.schema.json +74 -0
- package/schemas/run-start-intent.schema.json +74 -0
- package/schemas/runtime-record.schema.json +111 -0
- package/schemas/service-plan.schema.json +35 -0
- package/schemas/session-payload.schema.json +80 -0
- package/schemas/structured-role-read-request.schema.json +14 -0
- package/schemas/structured-role-request.schema.json +44 -0
- package/schemas/structured-role-result.schema.json +38 -0
- package/schemas/structured-role-submit-result.schema.json +15 -0
- package/schemas/team-checkpoint.schema.json +42 -0
- package/schemas/team-plan.schema.json +30 -0
- package/schemas/ticket-content.schema.json +38 -0
- package/schemas/ticket-queue-payload.schema.json +191 -0
- package/schemas/trust-report.schema.json +68 -0
- package/schemas/trust-run.schema.json +47 -0
- package/schemas/ultragoal-checkpoint.schema.json +49 -0
- package/schemas/user-schedule-installation-v1.schema.json +32 -0
- package/schemas/user-schedule-installation.schema.json +38 -0
- package/schemas/user-schedule-plan.schema.json +39 -0
- package/schemas/verification-plan-evidence.schema.json +37 -0
- package/schemas/worker-acknowledge-dispatch.schema.json +12 -0
- package/schemas/worker-payload.schema.json +47 -0
- package/schemas/worker-read-dispatch.schema.json +11 -0
- package/schemas/worker-record-dispatch-result.schema.json +14 -0
|
@@ -0,0 +1,1189 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { StringDecoder } from "node:string_decoder";
|
|
5
|
+
import { assertOpaqueId, assertRecordId } from "../domain/records.js";
|
|
6
|
+
import { buildGithubIssueListRequest, normalizeGithubIssueTarget, parseGithubIssueListResult, } from "../live-probes/gh-adapter.js";
|
|
7
|
+
import { findPackageRoot } from "../package-root.js";
|
|
8
|
+
import { runProcess } from "../providers/process.js";
|
|
9
|
+
import { createStableId } from "../runtime/stable-id.js";
|
|
10
|
+
import { assertSchema, loadJsonSchema } from "../runtime/schema.js";
|
|
11
|
+
import { assertTicketMaterializer, assertTicketMaterializerEvidenceStore, } from "../tickets/materializer.js";
|
|
12
|
+
import { deriveGitHubTicketIdentity, } from "../tickets/store.js";
|
|
13
|
+
import { assessTrust } from "../trust/assessment.js";
|
|
14
|
+
import { errorMessage, isRecord, lstatIfExists } from "../utils/fs.js";
|
|
15
|
+
import { parseVerificationPlan } from "../verification/plan.js";
|
|
16
|
+
import { assertProtectedVerificationRun, } from "../verification/protected-runner.js";
|
|
17
|
+
import { captureProtectedGradeSource, } from "../verification/grade-source.js";
|
|
18
|
+
import { protectedAdvisorProvenanceIdentity, } from "../dispatch/advice-source.js";
|
|
19
|
+
import { assertFileInside, openStableDirectory, readImmutableFile, writeImmutableFile, } from "./immutable-file.js";
|
|
20
|
+
const MAX_EVIDENCE_BYTES = 4 * 1024 * 1024;
|
|
21
|
+
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
22
|
+
const TIMESTAMP_PATTERN = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.([0-9]{1,3}))?Z$/;
|
|
23
|
+
const EVIDENCE_CONTRACTS = {
|
|
24
|
+
"verification-plan": { producers: ["oms.conductor", "oms.system"], schemaFile: "verification-plan-evidence.schema.json" },
|
|
25
|
+
"executor-result": { producers: ["oms.ticket-executor"], schemaFile: "executor-result.schema.json" },
|
|
26
|
+
"goal-executor-result": { producers: ["oms.goal-long-executor"], schemaFile: "goal-executor-result.schema.json" },
|
|
27
|
+
"lease-expiry": { producers: ["oms.watchdog"], schemaFile: "lease-expiry.schema.json" },
|
|
28
|
+
"deterministic-check": { producers: ["oms.verifier"], schemaFile: "deterministic-check.schema.json" },
|
|
29
|
+
"advisor-output": { producers: ["oms.exec-advisor"], schemaFile: "advisor-evidence.schema.json" },
|
|
30
|
+
"grader-output": { producers: ["oms.grader.codex", "oms.grader.claude"], schemaFile: "grader-output.schema.json" },
|
|
31
|
+
"live-disclosure": { producers: ["oms.system"], schemaFile: "live-disclosure.schema.json" },
|
|
32
|
+
"live-outcome": { producers: ["oms.system"], schemaFile: "live-outcome.schema.json" },
|
|
33
|
+
"trust-run": { producers: ["oms.system"], schemaFile: "trust-run.schema.json" },
|
|
34
|
+
"reflection": { producers: ["oms.bounded-dreamer"], schemaFile: "reflection-evidence.schema.json" },
|
|
35
|
+
"trust-report": { producers: ["oms.system"], schemaFile: "trust-report.schema.json" },
|
|
36
|
+
};
|
|
37
|
+
const EVIDENCE_STORE_INSTANCES = new WeakSet();
|
|
38
|
+
const PROTECTED_RESULT_GATE_WRITER = Symbol("OMS protected result-gate evidence writer");
|
|
39
|
+
const PROTECTED_RESULT_GATE_WRITERS = new WeakSet();
|
|
40
|
+
const PROTECTED_ADVISOR_WRITER = Symbol("OMS protected advisor evidence writer");
|
|
41
|
+
const PROTECTED_ADVISOR_WRITERS = new WeakSet();
|
|
42
|
+
const TICKET_BOUND_EVIDENCE_KINDS = new Set(["trust-run", "reflection"]);
|
|
43
|
+
const GITHUB_PROBE_EVIDENCE_KINDS = new Set(["live-disclosure", "live-outcome"]);
|
|
44
|
+
const EVIDENCE_STORE_INTERNALS = new WeakMap();
|
|
45
|
+
export function assertEvidenceStore(value) {
|
|
46
|
+
if (typeof value !== "object" || value === null || !EVIDENCE_STORE_INSTANCES.has(value)) {
|
|
47
|
+
throw new Error("ticket lifecycle evidence requires a real OMS EvidenceStore");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function registeredEvidenceKinds() {
|
|
51
|
+
return Object.keys(EVIDENCE_CONTRACTS);
|
|
52
|
+
}
|
|
53
|
+
export function createEvidenceStore(options) {
|
|
54
|
+
const root = openStableDirectory(options.evidenceRoot, "evidence root");
|
|
55
|
+
const packageRoot = options.packageRoot ?? findPackageRoot();
|
|
56
|
+
const envelopeSchema = loadJsonSchema(join(packageRoot, "schemas", "evidence-record.schema.json"));
|
|
57
|
+
const contracts = loadContracts(packageRoot);
|
|
58
|
+
function validate(value) {
|
|
59
|
+
return validateEvidenceRecord(value, envelopeSchema, contracts);
|
|
60
|
+
}
|
|
61
|
+
function readStored(ref) {
|
|
62
|
+
validateEvidenceReference(ref);
|
|
63
|
+
const path = join(root.path, ref.file_name);
|
|
64
|
+
assertFileInside(root, path);
|
|
65
|
+
const bytes = readImmutableFile(path, MAX_EVIDENCE_BYTES);
|
|
66
|
+
if (sha256(bytes) !== ref.sha256)
|
|
67
|
+
throw new Error(`evidence digest mismatch: ${ref.evidence_id}`);
|
|
68
|
+
const record = validate(parseJson(bytes));
|
|
69
|
+
if (record.evidence_id !== ref.evidence_id)
|
|
70
|
+
throw new Error("stored evidence has the wrong evidence id");
|
|
71
|
+
validateEvidenceLinks(record);
|
|
72
|
+
return record;
|
|
73
|
+
}
|
|
74
|
+
function listStoredReferences(kind) {
|
|
75
|
+
root.assertUnchanged();
|
|
76
|
+
const refs = [];
|
|
77
|
+
for (const entry of readdirSync(root.path, { withFileTypes: true }).sort((left, right) => compareText(left.name, right.name))) {
|
|
78
|
+
if (!entry.name.endsWith(".json"))
|
|
79
|
+
continue;
|
|
80
|
+
if (kind === "trust-run" && !/^trust-run-[a-f0-9]{48}\.json$/.test(entry.name))
|
|
81
|
+
continue;
|
|
82
|
+
if (!entry.isFile())
|
|
83
|
+
throw new Error(`evidence entry is not a regular file: ${entry.name}`);
|
|
84
|
+
const evidenceId = entry.name.slice(0, -".json".length);
|
|
85
|
+
assertOpaqueId(evidenceId, "evidence filename id");
|
|
86
|
+
const bytes = readImmutableFile(join(root.path, entry.name), MAX_EVIDENCE_BYTES);
|
|
87
|
+
const ref = { evidence_id: evidenceId, sha256: sha256(bytes), file_name: entry.name };
|
|
88
|
+
const record = readStored(ref);
|
|
89
|
+
if (kind === undefined || record.evidence_kind === kind)
|
|
90
|
+
refs.push(ref);
|
|
91
|
+
}
|
|
92
|
+
return refs;
|
|
93
|
+
}
|
|
94
|
+
function validateEvidenceLinks(record) {
|
|
95
|
+
if (record.evidence_kind === "live-outcome") {
|
|
96
|
+
const reference = record.payload.disclosure_evidence;
|
|
97
|
+
if (reference.evidence_id === record.evidence_id)
|
|
98
|
+
throw new Error("live outcome cannot reference itself");
|
|
99
|
+
const disclosure = readStored(reference);
|
|
100
|
+
if (disclosure.evidence_kind !== "live-disclosure") {
|
|
101
|
+
throw new Error("live outcome disclosure reference has the wrong evidence kind");
|
|
102
|
+
}
|
|
103
|
+
if (disclosure.payload.run_id !== record.payload.run_id) {
|
|
104
|
+
throw new Error("live outcome run id does not match its disclosure");
|
|
105
|
+
}
|
|
106
|
+
if (disclosure.payload.certification !== record.payload.certification) {
|
|
107
|
+
throw new Error("live outcome certification does not match its disclosure");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (record.evidence_kind === "grader-output") {
|
|
111
|
+
const reference = record.payload.deterministic_evidence;
|
|
112
|
+
if (reference.evidence_id === record.evidence_id)
|
|
113
|
+
throw new Error("grader evidence cannot reference itself");
|
|
114
|
+
const deterministic = readStored(reference);
|
|
115
|
+
if (deterministic.evidence_kind !== "deterministic-check") {
|
|
116
|
+
throw new Error("grader deterministic reference has the wrong evidence kind");
|
|
117
|
+
}
|
|
118
|
+
if (deterministic.ticket_id !== record.ticket_id) {
|
|
119
|
+
throw new Error("grader deterministic reference belongs to a different ticket");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (record.evidence_kind === "trust-run")
|
|
123
|
+
validateTrustRunLinks(record, readStored);
|
|
124
|
+
if (record.evidence_kind === "reflection")
|
|
125
|
+
validateReflectionLinks(record, readStored);
|
|
126
|
+
if (record.evidence_kind === "trust-report") {
|
|
127
|
+
validateTrustReportLinks(record, readStored, () => listStoredReferences("trust-run"));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function prepareWrite(record, authority) {
|
|
131
|
+
if (authority !== "result-gate"
|
|
132
|
+
&& record.evidence_kind === "deterministic-check"
|
|
133
|
+
&& isRecord(record.payload)
|
|
134
|
+
&& record.payload.certification === "live") {
|
|
135
|
+
throw new Error("live deterministic evidence requires the protected result-gate writer");
|
|
136
|
+
}
|
|
137
|
+
const normalized = validate(record);
|
|
138
|
+
if (normalized.evidence_kind === "advisor-output" && authority !== "advisor") {
|
|
139
|
+
throw new Error("advisor-output evidence requires the protected advisor writer");
|
|
140
|
+
}
|
|
141
|
+
if (TICKET_BOUND_EVIDENCE_KINDS.has(normalized.evidence_kind) && authority !== "ticket-bound") {
|
|
142
|
+
throw new Error(`${normalized.evidence_kind} evidence requires the canonical ticket-bound writer`);
|
|
143
|
+
}
|
|
144
|
+
if (GITHUB_PROBE_EVIDENCE_KINDS.has(normalized.evidence_kind) && authority !== "github-probe") {
|
|
145
|
+
throw new Error(`${normalized.evidence_kind} evidence requires the canonical GitHub-probe writer`);
|
|
146
|
+
}
|
|
147
|
+
if (normalized.evidence_kind === "deterministic-check"
|
|
148
|
+
&& normalized.payload.certification === "live"
|
|
149
|
+
&& authority !== "result-gate") {
|
|
150
|
+
throw new Error("live deterministic evidence requires the protected result-gate writer");
|
|
151
|
+
}
|
|
152
|
+
if (normalized.evidence_kind === "deterministic-check"
|
|
153
|
+
&& normalized.payload.certification !== "live"
|
|
154
|
+
&& authority === "result-gate") {
|
|
155
|
+
throw new Error("protected result-gate writer accepts only live deterministic evidence");
|
|
156
|
+
}
|
|
157
|
+
if (normalized.evidence_kind === "grader-output") {
|
|
158
|
+
const deterministic = readStored(normalized.payload.deterministic_evidence);
|
|
159
|
+
const resultGateGrade = deterministic.payload.certification === "live";
|
|
160
|
+
if (resultGateGrade !== (authority === "result-gate")) {
|
|
161
|
+
throw new Error(resultGateGrade
|
|
162
|
+
? "grader evidence for a live check requires the protected result-gate writer"
|
|
163
|
+
: "protected result-gate writer requires a live deterministic check");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
validateEvidenceLinks(normalized);
|
|
167
|
+
const bytes = canonicalJson(normalized);
|
|
168
|
+
if (bytes.byteLength > MAX_EVIDENCE_BYTES)
|
|
169
|
+
throw new Error(`evidence record exceeds ${MAX_EVIDENCE_BYTES} bytes`);
|
|
170
|
+
const ref = {
|
|
171
|
+
evidence_id: normalized.evidence_id,
|
|
172
|
+
sha256: sha256(bytes),
|
|
173
|
+
file_name: `${normalized.evidence_id}.json`,
|
|
174
|
+
};
|
|
175
|
+
const path = join(root.path, ref.file_name);
|
|
176
|
+
assertFileInside(root, path);
|
|
177
|
+
return { normalized, bytes, ref, path };
|
|
178
|
+
}
|
|
179
|
+
function writeRecord(record, authority) {
|
|
180
|
+
const prepared = prepareWrite(record, authority);
|
|
181
|
+
const status = writeImmutableFile(prepared.path, prepared.bytes);
|
|
182
|
+
return { status, ref: prepared.ref, record: readStored(prepared.ref) };
|
|
183
|
+
}
|
|
184
|
+
const store = {
|
|
185
|
+
write(record) {
|
|
186
|
+
return writeRecord(record, "generic");
|
|
187
|
+
},
|
|
188
|
+
read(ref) {
|
|
189
|
+
return readStored(ref);
|
|
190
|
+
},
|
|
191
|
+
references(kind) {
|
|
192
|
+
return listStoredReferences(kind);
|
|
193
|
+
},
|
|
194
|
+
absolutePath(ref) {
|
|
195
|
+
readStored(ref);
|
|
196
|
+
return join(root.path, ref.file_name);
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
Object.freeze(store);
|
|
200
|
+
EVIDENCE_STORE_INSTANCES.add(store);
|
|
201
|
+
EVIDENCE_STORE_INTERNALS.set(store, {
|
|
202
|
+
writeTicketBound(record) {
|
|
203
|
+
return writeRecord(record, "ticket-bound");
|
|
204
|
+
},
|
|
205
|
+
replayTicketBound(record) {
|
|
206
|
+
const prepared = prepareWrite(record, "ticket-bound");
|
|
207
|
+
const entry = lstatIfExists(prepared.path);
|
|
208
|
+
if (!entry)
|
|
209
|
+
return undefined;
|
|
210
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
211
|
+
throw new Error("ticket-bound evidence path is unsafe");
|
|
212
|
+
return writeRecord(record, "ticket-bound");
|
|
213
|
+
},
|
|
214
|
+
validateGithubProbe(record) {
|
|
215
|
+
return prepareWrite(record, "github-probe").normalized;
|
|
216
|
+
},
|
|
217
|
+
writeGithubProbe(record) {
|
|
218
|
+
return writeRecord(record, "github-probe");
|
|
219
|
+
},
|
|
220
|
+
githubProbeEvidenceExists(evidenceId) {
|
|
221
|
+
const validatedId = assertOpaqueId(evidenceId, "GitHub-probe evidence id");
|
|
222
|
+
const path = join(root.path, `${validatedId}.json`);
|
|
223
|
+
assertFileInside(root, path);
|
|
224
|
+
const entry = lstatIfExists(path);
|
|
225
|
+
if (!entry)
|
|
226
|
+
return false;
|
|
227
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
228
|
+
throw new Error("GitHub-probe evidence path is unsafe");
|
|
229
|
+
return true;
|
|
230
|
+
},
|
|
231
|
+
writeResultGate(record) {
|
|
232
|
+
return writeRecord(record, "result-gate");
|
|
233
|
+
},
|
|
234
|
+
writeAdvisor(record) {
|
|
235
|
+
return writeRecord(record, "advisor");
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
return store;
|
|
239
|
+
}
|
|
240
|
+
/** Branded writer used only by the protected verification/result-gate authority. */
|
|
241
|
+
export function createProtectedResultGateEvidenceWriter(evidenceStore) {
|
|
242
|
+
assertEvidenceStore(evidenceStore);
|
|
243
|
+
const internal = EVIDENCE_STORE_INTERNALS.get(evidenceStore);
|
|
244
|
+
if (!internal)
|
|
245
|
+
throw new Error("protected result-gate evidence authority is unavailable");
|
|
246
|
+
const writer = Object.freeze({
|
|
247
|
+
[PROTECTED_RESULT_GATE_WRITER]: true,
|
|
248
|
+
writeDeterministic(options) {
|
|
249
|
+
assertProtectedVerificationRun(options.run);
|
|
250
|
+
const run = options.run;
|
|
251
|
+
return internal.writeResultGate({
|
|
252
|
+
schema_version: 1,
|
|
253
|
+
evidence_id: assertOpaqueId(options.evidenceId, "deterministic evidence id"),
|
|
254
|
+
evidence_kind: "deterministic-check",
|
|
255
|
+
ticket_id: run.binding.ticket_id,
|
|
256
|
+
producer_role: "oms.verifier",
|
|
257
|
+
created_at: options.createdAt,
|
|
258
|
+
payload: {
|
|
259
|
+
schema_version: 1,
|
|
260
|
+
certification: "live",
|
|
261
|
+
policy_version: run.policy_version,
|
|
262
|
+
policy_digest: run.policy_digest,
|
|
263
|
+
plan_digest: run.verification.plan_digest,
|
|
264
|
+
attempt: run.binding.attempt,
|
|
265
|
+
lease_token: run.binding.lease_token,
|
|
266
|
+
binding: structuredClone(run.binding),
|
|
267
|
+
preflight: structuredClone(run.preflight),
|
|
268
|
+
postflight: structuredClone(run.postflight),
|
|
269
|
+
passed: run.verification.passed,
|
|
270
|
+
failed_step: run.verification.failed_step,
|
|
271
|
+
steps: run.verification.steps.map((step) => ({ ...step })),
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
},
|
|
275
|
+
writeGrade(record, source) {
|
|
276
|
+
if (record.evidence_kind !== "grader-output") {
|
|
277
|
+
throw new Error("protected result-gate grade writer accepts only grader-output evidence");
|
|
278
|
+
}
|
|
279
|
+
const identity = captureProtectedGradeSource(source);
|
|
280
|
+
if (record.producer_role !== identity.role_id
|
|
281
|
+
|| record.payload.provider_request_id !== identity.request_id
|
|
282
|
+
|| record.payload.grader_engine !== identity.engine
|
|
283
|
+
|| record.payload.fresh_session_id !== identity.fresh_session_id
|
|
284
|
+
|| !canonicalJson(stripProtectedGradeMetadata(record.payload)).equals(canonicalJson(identity.output)))
|
|
285
|
+
throw new Error("protected grade evidence does not match its fresh provider result");
|
|
286
|
+
return internal.writeResultGate(structuredClone(record));
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
PROTECTED_RESULT_GATE_WRITERS.add(writer);
|
|
290
|
+
return writer;
|
|
291
|
+
}
|
|
292
|
+
function stripProtectedGradeMetadata(payload) {
|
|
293
|
+
const result = structuredClone(payload);
|
|
294
|
+
delete result.provider_request_id;
|
|
295
|
+
delete result.grader_engine;
|
|
296
|
+
delete result.fresh_session_id;
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
export function assertProtectedResultGateEvidenceWriter(value) {
|
|
300
|
+
if (typeof value !== "object" || value === null || !PROTECTED_RESULT_GATE_WRITERS.has(value)) {
|
|
301
|
+
throw new Error("result-gate evidence requires a real OMS protected writer");
|
|
302
|
+
}
|
|
303
|
+
if (!Object.isFrozen(value))
|
|
304
|
+
throw new Error("protected result-gate writer lost its frozen boundary");
|
|
305
|
+
}
|
|
306
|
+
/** Branded writer used only by the protected pre-dispatch advisor authority. */
|
|
307
|
+
export function createProtectedAdvisorEvidenceWriter(evidenceStore) {
|
|
308
|
+
assertEvidenceStore(evidenceStore);
|
|
309
|
+
const internal = EVIDENCE_STORE_INTERNALS.get(evidenceStore);
|
|
310
|
+
if (!internal)
|
|
311
|
+
throw new Error("protected advisor evidence authority is unavailable");
|
|
312
|
+
const writer = Object.freeze({
|
|
313
|
+
[PROTECTED_ADVISOR_WRITER]: true,
|
|
314
|
+
writeAdvice(options, provenance) {
|
|
315
|
+
const identity = protectedAdvisorProvenanceIdentity(provenance);
|
|
316
|
+
return internal.writeAdvisor({
|
|
317
|
+
schema_version: 1,
|
|
318
|
+
evidence_id: identity.evidence_id,
|
|
319
|
+
evidence_kind: "advisor-output",
|
|
320
|
+
ticket_id: identity.ticket_id,
|
|
321
|
+
producer_role: identity.role_id,
|
|
322
|
+
created_at: options.createdAt,
|
|
323
|
+
payload: {
|
|
324
|
+
...structuredClone(identity.output),
|
|
325
|
+
provider_request_id: identity.request_id,
|
|
326
|
+
fresh_session_id: identity.fresh_session_id,
|
|
327
|
+
contact_reference: structuredClone(identity.contact_reference),
|
|
328
|
+
advice_authorization_id: identity.advice_authorization_id,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
PROTECTED_ADVISOR_WRITERS.add(writer);
|
|
334
|
+
return writer;
|
|
335
|
+
}
|
|
336
|
+
export function assertProtectedAdvisorEvidenceWriter(value) {
|
|
337
|
+
if (typeof value !== "object" || value === null || !PROTECTED_ADVISOR_WRITERS.has(value)) {
|
|
338
|
+
throw new Error("advisor evidence requires a real OMS protected writer");
|
|
339
|
+
}
|
|
340
|
+
if (!Object.isFrozen(value))
|
|
341
|
+
throw new Error("protected advisor writer lost its frozen boundary");
|
|
342
|
+
}
|
|
343
|
+
export function writeTicketBoundEvidence(options) {
|
|
344
|
+
const evidenceStore = options.evidenceStore;
|
|
345
|
+
const ticketMaterializer = options.ticketMaterializer;
|
|
346
|
+
const record = structuredClone(options.record);
|
|
347
|
+
assertEvidenceStore(evidenceStore);
|
|
348
|
+
assertTicketMaterializer(ticketMaterializer);
|
|
349
|
+
const internal = EVIDENCE_STORE_INTERNALS.get(evidenceStore);
|
|
350
|
+
if (!internal)
|
|
351
|
+
throw new Error("ticket-bound evidence store authority is unavailable");
|
|
352
|
+
if (!TICKET_BOUND_EVIDENCE_KINDS.has(record.evidence_kind)) {
|
|
353
|
+
throw new Error("ticket-bound writer accepts only trust-run or reflection evidence");
|
|
354
|
+
}
|
|
355
|
+
const replay = internal.replayTicketBound(record);
|
|
356
|
+
if (replay)
|
|
357
|
+
return replay;
|
|
358
|
+
try {
|
|
359
|
+
validateCanonicalTicketEvidence(evidenceStore, ticketMaterializer, record);
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
const concurrentReplay = internal.replayTicketBound(record);
|
|
363
|
+
if (concurrentReplay)
|
|
364
|
+
return concurrentReplay;
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
367
|
+
return internal.writeTicketBound(record);
|
|
368
|
+
}
|
|
369
|
+
export class GithubQueueProbeError extends Error {
|
|
370
|
+
phase;
|
|
371
|
+
contacted;
|
|
372
|
+
outcomeEvidence;
|
|
373
|
+
constructor(options) {
|
|
374
|
+
super(`GitHub queue probe failed during ${options.phase}`, { cause: options.cause });
|
|
375
|
+
this.name = "GithubQueueProbeError";
|
|
376
|
+
this.phase = options.phase;
|
|
377
|
+
this.contacted = options.contacted;
|
|
378
|
+
this.outcomeEvidence = options.outcomeEvidence;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/** Certifying path: owns the canonical process, clock, materializer, and evidence writes. */
|
|
382
|
+
export function runGithubQueueProbe(options) {
|
|
383
|
+
return executeGithubQueueProbe(captureGithubQueueProbeOptions(options), "live", { ...process.env });
|
|
384
|
+
}
|
|
385
|
+
/** Offline fixture path: still uses runProcess, but every record is permanently non-certifying. */
|
|
386
|
+
export function runNonCertifyingGithubQueueProbeFixture(options) {
|
|
387
|
+
const env = { ...options.env };
|
|
388
|
+
return executeGithubQueueProbe(captureGithubQueueProbeOptions(options), "non-certifying", env);
|
|
389
|
+
}
|
|
390
|
+
function captureGithubQueueProbeOptions(options) {
|
|
391
|
+
return {
|
|
392
|
+
runId: options.runId,
|
|
393
|
+
repository: options.repository,
|
|
394
|
+
readyLabel: options.readyLabel,
|
|
395
|
+
limit: options.limit,
|
|
396
|
+
confirmLive: options.confirmLive,
|
|
397
|
+
cwd: options.cwd,
|
|
398
|
+
evidenceStore: options.evidenceStore,
|
|
399
|
+
materializer: options.materializer,
|
|
400
|
+
emitDisclosure: options.emitDisclosure,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
function executeGithubQueueProbe(options, certification, env) {
|
|
404
|
+
const runId = assertOpaqueId(options.runId, "GitHub probe run id");
|
|
405
|
+
const target = normalizeGithubIssueTarget({
|
|
406
|
+
repository: options.repository,
|
|
407
|
+
...(options.readyLabel === undefined ? {} : { readyLabel: options.readyLabel }),
|
|
408
|
+
...(options.limit === undefined ? {} : { limit: options.limit }),
|
|
409
|
+
});
|
|
410
|
+
const evidenceStore = options.evidenceStore;
|
|
411
|
+
const ticketMaterializer = options.materializer;
|
|
412
|
+
const emitDisclosure = options.emitDisclosure;
|
|
413
|
+
const cwd = options.cwd;
|
|
414
|
+
const effectiveCertification = options.confirmLive
|
|
415
|
+
? certification
|
|
416
|
+
: "non-certifying";
|
|
417
|
+
const disclosure = Object.freeze({
|
|
418
|
+
schema_version: 1,
|
|
419
|
+
certification: effectiveCertification,
|
|
420
|
+
run_id: runId,
|
|
421
|
+
contact_surface: "github-api-via-gh",
|
|
422
|
+
expected_spend: "none",
|
|
423
|
+
operation: "read-ready-issues",
|
|
424
|
+
repository: target.repository,
|
|
425
|
+
ready_label: target.readyLabel,
|
|
426
|
+
issue_limit: target.limit,
|
|
427
|
+
});
|
|
428
|
+
if (!options.confirmLive)
|
|
429
|
+
return { contacted: false, disclosure, syncResults: [] };
|
|
430
|
+
assertEvidenceStore(evidenceStore);
|
|
431
|
+
assertTicketMaterializer(ticketMaterializer);
|
|
432
|
+
assertTicketMaterializerEvidenceStore(ticketMaterializer, evidenceStore);
|
|
433
|
+
const internal = EVIDENCE_STORE_INTERNALS.get(evidenceStore);
|
|
434
|
+
if (!internal)
|
|
435
|
+
throw new Error("GitHub-probe evidence store authority is unavailable");
|
|
436
|
+
const request = buildGithubIssueListRequest({ target, cwd, env });
|
|
437
|
+
const disclosureId = createStableId("gh-disclosure", runId);
|
|
438
|
+
if (internal.githubProbeEvidenceExists(disclosureId)) {
|
|
439
|
+
throw new GithubQueueProbeError({
|
|
440
|
+
phase: "evidence",
|
|
441
|
+
contacted: false,
|
|
442
|
+
cause: new Error(`GitHub probe run id was already used: ${runId}`),
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
let disclosureWrite;
|
|
446
|
+
try {
|
|
447
|
+
disclosureWrite = internal.writeGithubProbe({
|
|
448
|
+
schema_version: 1,
|
|
449
|
+
evidence_id: disclosureId,
|
|
450
|
+
evidence_kind: "live-disclosure",
|
|
451
|
+
ticket_id: null,
|
|
452
|
+
producer_role: "oms.system",
|
|
453
|
+
created_at: new Date().toISOString(),
|
|
454
|
+
payload: { ...disclosure },
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
catch (error) {
|
|
458
|
+
if (internal.githubProbeEvidenceExists(disclosureId)) {
|
|
459
|
+
throw new GithubQueueProbeError({
|
|
460
|
+
phase: "evidence",
|
|
461
|
+
contacted: false,
|
|
462
|
+
cause: new Error(`GitHub probe run id was already used: ${runId}`),
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
throw error;
|
|
466
|
+
}
|
|
467
|
+
if (disclosureWrite.status !== "created") {
|
|
468
|
+
throw new GithubQueueProbeError({
|
|
469
|
+
phase: "evidence",
|
|
470
|
+
contacted: false,
|
|
471
|
+
cause: new Error(`GitHub probe run id was already used: ${runId}`),
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
const session = {
|
|
475
|
+
certification: effectiveCertification,
|
|
476
|
+
runId,
|
|
477
|
+
target,
|
|
478
|
+
disclosure,
|
|
479
|
+
disclosureEvidence: disclosureWrite.ref,
|
|
480
|
+
issues: [],
|
|
481
|
+
receipts: new Map(),
|
|
482
|
+
outcomeAttempted: false,
|
|
483
|
+
};
|
|
484
|
+
try {
|
|
485
|
+
emitDisclosure(deepFreeze(structuredClone(disclosure)), deepFreeze(structuredClone(session.disclosureEvidence)));
|
|
486
|
+
}
|
|
487
|
+
catch (error) {
|
|
488
|
+
const outcomeEvidence = writeGithubProbeFailure({
|
|
489
|
+
internal,
|
|
490
|
+
evidenceStore,
|
|
491
|
+
ticketMaterializer,
|
|
492
|
+
session,
|
|
493
|
+
phase: "evidence",
|
|
494
|
+
error,
|
|
495
|
+
});
|
|
496
|
+
throw new GithubQueueProbeError({ phase: "evidence", contacted: false, outcomeEvidence, cause: error });
|
|
497
|
+
}
|
|
498
|
+
try {
|
|
499
|
+
session.issues.push(...parseGithubIssueListResult(runProcess(request), target));
|
|
500
|
+
}
|
|
501
|
+
catch (error) {
|
|
502
|
+
const outcomeEvidence = writeGithubProbeFailure({
|
|
503
|
+
internal,
|
|
504
|
+
evidenceStore,
|
|
505
|
+
ticketMaterializer,
|
|
506
|
+
session,
|
|
507
|
+
phase: "contact",
|
|
508
|
+
error,
|
|
509
|
+
});
|
|
510
|
+
throw new GithubQueueProbeError({ phase: "contact", contacted: true, outcomeEvidence, cause: error });
|
|
511
|
+
}
|
|
512
|
+
const syncResults = [];
|
|
513
|
+
for (const issue of session.issues) {
|
|
514
|
+
try {
|
|
515
|
+
const input = deepFreeze(toGithubTicketInput(target.repository, issue));
|
|
516
|
+
const result = ticketMaterializer.syncGitHub(input);
|
|
517
|
+
const readBack = ticketMaterializer.read(deriveGitHubTicketIdentity(input).ticket_id);
|
|
518
|
+
const receipt = validateGithubSyncResult(input, result, readBack);
|
|
519
|
+
session.receipts.set(receipt.ticket_id, receipt);
|
|
520
|
+
syncResults.push(deepFreeze(structuredClone(result)));
|
|
521
|
+
}
|
|
522
|
+
catch (error) {
|
|
523
|
+
const outcomeEvidence = writeGithubProbeFailure({
|
|
524
|
+
internal,
|
|
525
|
+
evidenceStore,
|
|
526
|
+
ticketMaterializer,
|
|
527
|
+
session,
|
|
528
|
+
phase: "sync",
|
|
529
|
+
error,
|
|
530
|
+
});
|
|
531
|
+
throw new GithubQueueProbeError({ phase: "sync", contacted: true, outcomeEvidence, cause: error });
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
let outcomeEvidence;
|
|
535
|
+
try {
|
|
536
|
+
outcomeEvidence = writeGithubProbeOutcome({
|
|
537
|
+
internal,
|
|
538
|
+
evidenceStore,
|
|
539
|
+
ticketMaterializer,
|
|
540
|
+
session,
|
|
541
|
+
status: "success",
|
|
542
|
+
failedPhase: null,
|
|
543
|
+
error: null,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
throw new GithubQueueProbeError({ phase: "evidence", contacted: true, cause: error });
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
contacted: true,
|
|
551
|
+
disclosure,
|
|
552
|
+
disclosureEvidence: session.disclosureEvidence,
|
|
553
|
+
outcomeEvidence,
|
|
554
|
+
syncResults: Object.freeze(syncResults),
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function writeGithubProbeFailure(options) {
|
|
558
|
+
try {
|
|
559
|
+
return writeGithubProbeOutcome({
|
|
560
|
+
internal: options.internal,
|
|
561
|
+
evidenceStore: options.evidenceStore,
|
|
562
|
+
ticketMaterializer: options.ticketMaterializer,
|
|
563
|
+
session: options.session,
|
|
564
|
+
status: "failure",
|
|
565
|
+
failedPhase: options.phase,
|
|
566
|
+
error: sanitizeLiveOutcomeError(errorMessage(options.error)),
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
catch (outcomeError) {
|
|
570
|
+
throw new GithubQueueProbeError({
|
|
571
|
+
phase: "evidence",
|
|
572
|
+
contacted: options.phase !== "evidence",
|
|
573
|
+
cause: outcomeError,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function writeGithubProbeOutcome(options) {
|
|
578
|
+
if (options.session.outcomeAttempted)
|
|
579
|
+
throw new Error("GitHub probe already attempted its terminal outcome");
|
|
580
|
+
options.session.outcomeAttempted = true;
|
|
581
|
+
const receipts = sortedGithubReceipts([...options.session.receipts.values()]);
|
|
582
|
+
const record = {
|
|
583
|
+
schema_version: 1,
|
|
584
|
+
evidence_id: createStableId("gh-outcome", options.session.runId),
|
|
585
|
+
evidence_kind: "live-outcome",
|
|
586
|
+
ticket_id: null,
|
|
587
|
+
producer_role: "oms.system",
|
|
588
|
+
created_at: new Date().toISOString(),
|
|
589
|
+
payload: {
|
|
590
|
+
schema_version: 1,
|
|
591
|
+
certification: options.session.certification,
|
|
592
|
+
run_id: options.session.runId,
|
|
593
|
+
disclosure_evidence: options.session.disclosureEvidence,
|
|
594
|
+
status: options.status,
|
|
595
|
+
failed_phase: options.failedPhase,
|
|
596
|
+
error: options.error,
|
|
597
|
+
issue_count: options.session.issues.length,
|
|
598
|
+
synced_count: receipts.length,
|
|
599
|
+
synced_tickets: receipts,
|
|
600
|
+
},
|
|
601
|
+
};
|
|
602
|
+
const normalized = options.internal.validateGithubProbe(record);
|
|
603
|
+
validateCanonicalGithubProbeOutcome(options.evidenceStore, options.ticketMaterializer, options.session, normalized);
|
|
604
|
+
const written = options.internal.writeGithubProbe(normalized);
|
|
605
|
+
if (written.status !== "created")
|
|
606
|
+
throw new Error("GitHub probe outcome already exists");
|
|
607
|
+
return written.ref;
|
|
608
|
+
}
|
|
609
|
+
function validateGithubSyncResult(input, result, readBack) {
|
|
610
|
+
const identity = deriveGitHubTicketIdentity(input);
|
|
611
|
+
if (!isRecord(result) || !["created", "refreshed", "unchanged"].includes(String(result.status))) {
|
|
612
|
+
throw new Error("GitHub ticket sync returned an invalid status");
|
|
613
|
+
}
|
|
614
|
+
if (!isRecord(result.ref) || !isRecord(result.record) || !isRecord(result.record.payload)) {
|
|
615
|
+
throw new Error("GitHub ticket sync returned an invalid materialization receipt");
|
|
616
|
+
}
|
|
617
|
+
if (result.ref.ticket_id !== identity.ticket_id || result.record.record_id !== identity.ticket_id) {
|
|
618
|
+
throw new Error("GitHub ticket sync receipt belongs to a different ticket");
|
|
619
|
+
}
|
|
620
|
+
if (result.record.payload.source_ref_sha256 !== identity.source_ref_sha256
|
|
621
|
+
|| result.record.payload.content_sha256 !== result.ref.sha256
|
|
622
|
+
|| result.record.payload.content_file !== result.ref.file_name
|
|
623
|
+
|| result.ref.file_name !== `${identity.ticket_id}.${result.ref.sha256}.json`) {
|
|
624
|
+
throw new Error("GitHub ticket sync receipt does not match canonical content evidence");
|
|
625
|
+
}
|
|
626
|
+
if (!Number.isSafeInteger(result.record.version) || result.record.version < 0) {
|
|
627
|
+
throw new Error("GitHub ticket sync receipt has invalid queue state");
|
|
628
|
+
}
|
|
629
|
+
if (JSON.stringify(result.record) !== JSON.stringify(readBack)) {
|
|
630
|
+
throw new Error("GitHub ticket sync receipt does not match authoritative read-back");
|
|
631
|
+
}
|
|
632
|
+
return Object.freeze({
|
|
633
|
+
ticket_id: identity.ticket_id,
|
|
634
|
+
content_sha256: result.ref.sha256,
|
|
635
|
+
content_file: result.ref.file_name,
|
|
636
|
+
record_version: result.record.version,
|
|
637
|
+
sync_status: result.status,
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
function sortedGithubReceipts(receipts) {
|
|
641
|
+
return receipts
|
|
642
|
+
.map((receipt) => ({ ...receipt }))
|
|
643
|
+
.sort((left, right) => left.ticket_id.localeCompare(right.ticket_id));
|
|
644
|
+
}
|
|
645
|
+
function toGithubTicketInput(repository, issue) {
|
|
646
|
+
return {
|
|
647
|
+
repository,
|
|
648
|
+
issue_node_id: issue.id,
|
|
649
|
+
title: issue.title,
|
|
650
|
+
body: issue.body,
|
|
651
|
+
labels: [...issue.labels],
|
|
652
|
+
url: issue.url,
|
|
653
|
+
updated_at: issue.updatedAt,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
function deepFreeze(value, seen = new Set()) {
|
|
657
|
+
if (typeof value !== "object" || value === null || seen.has(value))
|
|
658
|
+
return value;
|
|
659
|
+
seen.add(value);
|
|
660
|
+
for (const child of Object.values(value))
|
|
661
|
+
deepFreeze(child, seen);
|
|
662
|
+
return Object.freeze(value);
|
|
663
|
+
}
|
|
664
|
+
function loadContracts(packageRoot) {
|
|
665
|
+
const contracts = new Map();
|
|
666
|
+
for (const kind of registeredEvidenceKinds()) {
|
|
667
|
+
const definition = EVIDENCE_CONTRACTS[kind];
|
|
668
|
+
contracts.set(kind, {
|
|
669
|
+
...definition,
|
|
670
|
+
schema: loadJsonSchema(join(packageRoot, "schemas", definition.schemaFile)),
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
return contracts;
|
|
674
|
+
}
|
|
675
|
+
function validateEvidenceRecord(value, envelopeSchema, contracts) {
|
|
676
|
+
assertJsonValue(value, "evidence record");
|
|
677
|
+
assertSchema(value, envelopeSchema, "evidence record");
|
|
678
|
+
const record = value;
|
|
679
|
+
assertOpaqueId(record.evidence_id, "evidence id");
|
|
680
|
+
if (record.ticket_id !== null)
|
|
681
|
+
assertRecordId(record.ticket_id, "evidence ticket id");
|
|
682
|
+
timestampEpoch(record.created_at, "evidence created_at");
|
|
683
|
+
const contract = contracts.get(record.evidence_kind);
|
|
684
|
+
if (!contract)
|
|
685
|
+
throw new Error(`unregistered evidence kind: ${String(record.evidence_kind)}`);
|
|
686
|
+
if (!contract.producers.includes(record.producer_role)) {
|
|
687
|
+
throw new Error(`producer ${record.producer_role} is not allowed for ${record.evidence_kind}`);
|
|
688
|
+
}
|
|
689
|
+
assertSchema(record.payload, contract.schema, `${record.evidence_kind} payload`);
|
|
690
|
+
if ([
|
|
691
|
+
"verification-plan",
|
|
692
|
+
"executor-result",
|
|
693
|
+
"goal-executor-result",
|
|
694
|
+
"lease-expiry",
|
|
695
|
+
"deterministic-check",
|
|
696
|
+
"advisor-output",
|
|
697
|
+
"grader-output",
|
|
698
|
+
"trust-run",
|
|
699
|
+
"reflection",
|
|
700
|
+
].includes(record.evidence_kind) && record.ticket_id === null) {
|
|
701
|
+
throw new Error(`${record.evidence_kind} requires a ticket id`);
|
|
702
|
+
}
|
|
703
|
+
if (record.evidence_kind === "verification-plan")
|
|
704
|
+
validateVerificationPlan(record.payload);
|
|
705
|
+
if (record.evidence_kind === "deterministic-check")
|
|
706
|
+
validateDeterministicCheck(record.payload);
|
|
707
|
+
if (record.evidence_kind === "deterministic-check"
|
|
708
|
+
&& record.payload.certification === "live"
|
|
709
|
+
&& record.payload.binding.ticket_id !== record.ticket_id) {
|
|
710
|
+
throw new Error("live deterministic check ticket binding does not match its evidence envelope");
|
|
711
|
+
}
|
|
712
|
+
if (record.evidence_kind === "executor-result") {
|
|
713
|
+
if (record.payload.ticket_id !== record.ticket_id)
|
|
714
|
+
throw new Error("executor evidence ticket id mismatch");
|
|
715
|
+
}
|
|
716
|
+
if (record.evidence_kind === "lease-expiry") {
|
|
717
|
+
if (record.payload.ticket_id !== record.ticket_id)
|
|
718
|
+
throw new Error("lease expiry evidence ticket id mismatch");
|
|
719
|
+
if (record.evidence_id !== createStableId("lease-expiry", record.ticket_id, String(record.payload.attempt), record.payload.lease_token))
|
|
720
|
+
throw new Error("lease expiry evidence id does not match its ticket lease");
|
|
721
|
+
}
|
|
722
|
+
if (["live-disclosure", "live-outcome", "trust-report"].includes(record.evidence_kind) && record.ticket_id !== null) {
|
|
723
|
+
throw new Error(`${record.evidence_kind} must not claim a ticket id`);
|
|
724
|
+
}
|
|
725
|
+
if (record.evidence_kind === "live-disclosure") {
|
|
726
|
+
if (record.evidence_id !== createStableId("gh-disclosure", record.payload.run_id)) {
|
|
727
|
+
throw new Error("live disclosure evidence id does not match its run id");
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (record.evidence_kind === "live-outcome")
|
|
731
|
+
validateLiveOutcome(record.payload);
|
|
732
|
+
if (record.evidence_kind === "live-outcome") {
|
|
733
|
+
if (record.evidence_id !== createStableId("gh-outcome", record.payload.run_id)) {
|
|
734
|
+
throw new Error("live outcome evidence id does not match its run id");
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return record;
|
|
738
|
+
}
|
|
739
|
+
function validateVerificationPlan(payload) {
|
|
740
|
+
let shippedPlan;
|
|
741
|
+
try {
|
|
742
|
+
shippedPlan = parseVerificationPlan(payload.plan, "verification plan evidence");
|
|
743
|
+
}
|
|
744
|
+
catch (error) {
|
|
745
|
+
throw new Error("verification plan does not match the exact shipped plan", { cause: error });
|
|
746
|
+
}
|
|
747
|
+
const digest = sha256(Buffer.from(JSON.stringify(shippedPlan), "utf8"));
|
|
748
|
+
if (payload.digest !== digest)
|
|
749
|
+
throw new Error("verification plan digest does not match its canonical plan");
|
|
750
|
+
}
|
|
751
|
+
function validateDeterministicCheck(payload) {
|
|
752
|
+
const expectedOrder = ["typecheck", "test", "build", "offline-verify"];
|
|
753
|
+
const steps = payload.steps;
|
|
754
|
+
if (steps.length < 1 || steps.length > expectedOrder.length)
|
|
755
|
+
throw new Error("deterministic check has invalid step count");
|
|
756
|
+
for (let index = 0; index < steps.length; index += 1) {
|
|
757
|
+
const step = steps[index];
|
|
758
|
+
if (!step || step.id !== expectedOrder[index])
|
|
759
|
+
throw new Error("deterministic check steps are out of order");
|
|
760
|
+
const expectedPassed = step.status === 0 && step.signal === null && step.error === null;
|
|
761
|
+
if (step.passed !== expectedPassed)
|
|
762
|
+
throw new Error(`deterministic check step ${String(step.id)} is inconsistent`);
|
|
763
|
+
if (index < steps.length - 1 && step.passed !== true) {
|
|
764
|
+
throw new Error("deterministic check continued after a failed step");
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const last = steps.at(-1);
|
|
768
|
+
if (!last)
|
|
769
|
+
throw new Error("deterministic check has no steps");
|
|
770
|
+
const allPassed = steps.length === expectedOrder.length && steps.every((step) => step.passed === true);
|
|
771
|
+
if (payload.passed !== allPassed)
|
|
772
|
+
throw new Error("deterministic check overall result is inconsistent");
|
|
773
|
+
if (allPassed) {
|
|
774
|
+
if (payload.failed_step !== null)
|
|
775
|
+
throw new Error("passing deterministic check must not name a failed step");
|
|
776
|
+
}
|
|
777
|
+
else if (last.passed !== false || payload.failed_step !== last.id) {
|
|
778
|
+
throw new Error("failed deterministic check must identify its terminal failed step");
|
|
779
|
+
}
|
|
780
|
+
if (payload.certification === "live")
|
|
781
|
+
validateLiveDeterministicBinding(payload);
|
|
782
|
+
}
|
|
783
|
+
function validateLiveDeterministicBinding(payload) {
|
|
784
|
+
if (!isRecord(payload.binding) || !isRecord(payload.preflight) || !isRecord(payload.postflight)) {
|
|
785
|
+
throw new Error("live deterministic check requires exact binding and pre/post worktree proof");
|
|
786
|
+
}
|
|
787
|
+
const binding = payload.binding;
|
|
788
|
+
const expectedExecutorEngine = binding.executor_role === "oms.ticket-executor" ? "claude" : "codex";
|
|
789
|
+
if (binding.attempt !== payload.attempt
|
|
790
|
+
|| binding.lease_token !== payload.lease_token
|
|
791
|
+
|| binding.worktree_root !== payload.preflight.worktree_root
|
|
792
|
+
|| binding.worktree_root !== payload.postflight.worktree_root
|
|
793
|
+
|| binding.worktree_branch !== payload.preflight.worktree_branch
|
|
794
|
+
|| binding.worktree_branch !== payload.postflight.worktree_branch
|
|
795
|
+
|| binding.expected_commit !== payload.preflight.head_commit
|
|
796
|
+
|| binding.expected_commit !== payload.postflight.head_commit
|
|
797
|
+
|| payload.preflight.clean !== true
|
|
798
|
+
|| payload.postflight.clean !== true
|
|
799
|
+
|| binding.executor_engine !== expectedExecutorEngine) {
|
|
800
|
+
throw new Error("live deterministic check binding does not match its worktree proof");
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function validateLiveOutcome(payload) {
|
|
804
|
+
const issueCount = payload.issue_count;
|
|
805
|
+
const syncedCount = payload.synced_count;
|
|
806
|
+
const syncedTickets = payload.synced_tickets;
|
|
807
|
+
if (syncedCount > issueCount)
|
|
808
|
+
throw new Error("live outcome synced_count exceeds issue_count");
|
|
809
|
+
if (syncedTickets.length !== syncedCount)
|
|
810
|
+
throw new Error("live outcome synced ticket receipts do not match synced_count");
|
|
811
|
+
const ticketIds = syncedTickets.map((ticket) => ticket.ticket_id);
|
|
812
|
+
if (new Set(ticketIds).size !== ticketIds.length)
|
|
813
|
+
throw new Error("live outcome contains duplicate ticket receipts");
|
|
814
|
+
if (JSON.stringify(ticketIds) !== JSON.stringify([...ticketIds].sort())) {
|
|
815
|
+
throw new Error("live outcome ticket receipts must be sorted by ticket id");
|
|
816
|
+
}
|
|
817
|
+
for (const ticket of syncedTickets) {
|
|
818
|
+
const ticketId = ticket.ticket_id;
|
|
819
|
+
if (ticket.content_file !== `${ticketId}.${String(ticket.content_sha256)}.json`) {
|
|
820
|
+
throw new Error("live outcome ticket receipt content filename is inconsistent");
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
if (payload.status === "success") {
|
|
824
|
+
if (payload.failed_phase !== null || payload.error !== null || syncedCount !== issueCount) {
|
|
825
|
+
throw new Error("successful live outcome is inconsistent");
|
|
826
|
+
}
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
if (typeof payload.failed_phase !== "string" || typeof payload.error !== "string" || payload.error.length === 0) {
|
|
830
|
+
throw new Error("failed live outcome requires failed_phase and error");
|
|
831
|
+
}
|
|
832
|
+
if (sanitizeLiveOutcomeError(payload.error) !== payload.error) {
|
|
833
|
+
throw new Error("live outcome error contains unsanitized secret material");
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
function validateTrustRunLinks(record, readStored) {
|
|
837
|
+
const deterministicReference = record.payload.deterministic_evidence;
|
|
838
|
+
if (deterministicReference.evidence_id === record.evidence_id) {
|
|
839
|
+
throw new Error("trust run cannot reference itself");
|
|
840
|
+
}
|
|
841
|
+
const deterministic = readStored(deterministicReference);
|
|
842
|
+
if (deterministic.evidence_kind !== "deterministic-check") {
|
|
843
|
+
throw new Error("trust run deterministic reference has the wrong evidence kind");
|
|
844
|
+
}
|
|
845
|
+
if (deterministic.payload.certification !== "live") {
|
|
846
|
+
throw new Error("trust run requires protected live deterministic evidence");
|
|
847
|
+
}
|
|
848
|
+
if (deterministic.ticket_id !== record.ticket_id) {
|
|
849
|
+
throw new Error("trust run deterministic reference belongs to a different ticket");
|
|
850
|
+
}
|
|
851
|
+
if (record.payload.ticket_id !== record.ticket_id)
|
|
852
|
+
throw new Error("trust run ticket id mismatch");
|
|
853
|
+
if (record.payload.run_id !== record.evidence_id)
|
|
854
|
+
throw new Error("trust run id does not match its evidence id");
|
|
855
|
+
if (record.payload.attempt !== deterministic.payload.attempt) {
|
|
856
|
+
throw new Error("trust run attempt does not match its deterministic check");
|
|
857
|
+
}
|
|
858
|
+
if (record.payload.deterministic_passed !== deterministic.payload.passed) {
|
|
859
|
+
throw new Error("trust run verdict does not match its deterministic check");
|
|
860
|
+
}
|
|
861
|
+
if (record.payload.gate_digest !== deterministic.payload.plan_digest) {
|
|
862
|
+
throw new Error("trust run gate digest does not match its deterministic check");
|
|
863
|
+
}
|
|
864
|
+
if (record.payload.policy_version !== deterministic.payload.policy_version) {
|
|
865
|
+
throw new Error("trust run policy version does not match its deterministic check");
|
|
866
|
+
}
|
|
867
|
+
if (record.payload.policy_digest !== deterministic.payload.policy_digest) {
|
|
868
|
+
throw new Error("trust run policy digest does not match its deterministic check");
|
|
869
|
+
}
|
|
870
|
+
if (record.created_at !== deterministic.created_at) {
|
|
871
|
+
throw new Error("trust run timestamp does not match its deterministic check");
|
|
872
|
+
}
|
|
873
|
+
const expectedId = createStableId("trust-run", deterministicReference.evidence_id, deterministicReference.sha256);
|
|
874
|
+
if (record.evidence_id !== expectedId)
|
|
875
|
+
throw new Error("trust run id does not match its deterministic check");
|
|
876
|
+
}
|
|
877
|
+
function validateCanonicalTicketEvidence(evidenceStore, ticketMaterializer, record) {
|
|
878
|
+
const deterministicReference = record.payload.deterministic_evidence;
|
|
879
|
+
const deterministic = evidenceStore.read(deterministicReference);
|
|
880
|
+
if (deterministic.evidence_kind !== "deterministic-check" || deterministic.ticket_id === null) {
|
|
881
|
+
throw new Error("ticket-bound evidence requires a deterministic ticket check");
|
|
882
|
+
}
|
|
883
|
+
if (record.evidence_kind === "trust-run" && deterministic.payload.certification !== "live") {
|
|
884
|
+
throw new Error("trust run requires protected live deterministic evidence");
|
|
885
|
+
}
|
|
886
|
+
const ticket = ticketMaterializer.read(deterministic.ticket_id);
|
|
887
|
+
const passed = deterministic.payload.passed === true;
|
|
888
|
+
const validState = record.evidence_kind === "trust-run" && passed
|
|
889
|
+
? ticket.state === "completed"
|
|
890
|
+
: ticket.state === "retryable" || ticket.state === "blocked";
|
|
891
|
+
if (!validState) {
|
|
892
|
+
throw new Error(`${record.evidence_kind} is not attached to the required canonical ticket state`);
|
|
893
|
+
}
|
|
894
|
+
if (!sameEvidenceReference(deterministicReference, ticket.payload.verification_evidence)) {
|
|
895
|
+
throw new Error(`${record.evidence_kind} deterministic evidence is not the ticket's authoritative verification evidence`);
|
|
896
|
+
}
|
|
897
|
+
if (ticket.attempt !== deterministic.payload.attempt
|
|
898
|
+
|| ticket.payload.policy_version !== deterministic.payload.policy_version
|
|
899
|
+
|| ticket.payload.policy_digest !== deterministic.payload.policy_digest
|
|
900
|
+
|| ticket.payload.gate_digest !== deterministic.payload.plan_digest) {
|
|
901
|
+
throw new Error(`${record.evidence_kind} identity does not match the canonical ticket policy, gate, or attempt`);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
function validateCanonicalGithubProbeOutcome(evidenceStore, ticketMaterializer, session, record) {
|
|
905
|
+
const disclosure = evidenceStore.read(session.disclosureEvidence);
|
|
906
|
+
if (disclosure.evidence_kind !== "live-disclosure"
|
|
907
|
+
|| JSON.stringify(disclosure.payload) !== JSON.stringify(session.disclosure)
|
|
908
|
+
|| disclosure.payload.repository !== session.target.repository
|
|
909
|
+
|| disclosure.payload.ready_label !== session.target.readyLabel
|
|
910
|
+
|| disclosure.payload.issue_limit !== session.target.limit
|
|
911
|
+
|| disclosure.payload.certification !== session.certification) {
|
|
912
|
+
throw new Error("GitHub-probe disclosure does not match the captured run target");
|
|
913
|
+
}
|
|
914
|
+
if (record.payload.run_id !== session.runId
|
|
915
|
+
|| record.payload.certification !== session.certification
|
|
916
|
+
|| record.payload.issue_count !== session.issues.length
|
|
917
|
+
|| !sameEvidenceReference(session.disclosureEvidence, record.payload.disclosure_evidence)) {
|
|
918
|
+
throw new Error("GitHub-probe outcome does not match the captured run identity");
|
|
919
|
+
}
|
|
920
|
+
const receipts = record.payload.synced_tickets;
|
|
921
|
+
const expectedReceipts = sortedGithubReceipts([...session.receipts.values()]);
|
|
922
|
+
if (JSON.stringify(receipts) !== JSON.stringify(expectedReceipts)) {
|
|
923
|
+
throw new Error("GitHub-probe outcome receipts do not match the captured run session");
|
|
924
|
+
}
|
|
925
|
+
const contactedTicketIds = new Set(session.issues.map((issue) => deriveGitHubTicketIdentity({ issue_node_id: issue.id }).ticket_id));
|
|
926
|
+
for (const receipt of receipts) {
|
|
927
|
+
const ticketId = receipt.ticket_id;
|
|
928
|
+
if (!contactedTicketIds.has(ticketId)) {
|
|
929
|
+
throw new Error("GitHub-probe outcome contains an old or unrelated canonical receipt");
|
|
930
|
+
}
|
|
931
|
+
const contentSha256 = receipt.content_sha256;
|
|
932
|
+
const contentFile = receipt.content_file;
|
|
933
|
+
const ticket = ticketMaterializer.read(ticketId);
|
|
934
|
+
if (ticket.version !== receipt.record_version
|
|
935
|
+
|| ticket.payload.ticket_id !== ticketId
|
|
936
|
+
|| ticket.payload.source !== "github"
|
|
937
|
+
|| ticket.payload.content_sha256 !== contentSha256
|
|
938
|
+
|| ticket.payload.content_file !== contentFile) {
|
|
939
|
+
throw new Error("GitHub-probe receipt does not match the authoritative ticket record");
|
|
940
|
+
}
|
|
941
|
+
const hasSyncOutcome = ticket.payload.sync_outcomes.some((outcome) => outcome.status === receipt.sync_status
|
|
942
|
+
&& outcome.content_sha256 === contentSha256
|
|
943
|
+
&& outcome.content_file === contentFile);
|
|
944
|
+
if (!hasSyncOutcome) {
|
|
945
|
+
throw new Error("GitHub-probe receipt does not match authoritative sync history");
|
|
946
|
+
}
|
|
947
|
+
const content = ticketMaterializer.readContent({
|
|
948
|
+
ticket_id: ticketId,
|
|
949
|
+
sha256: contentSha256,
|
|
950
|
+
file_name: contentFile,
|
|
951
|
+
});
|
|
952
|
+
if (content.ticket_id !== ticketId || content.source !== "github") {
|
|
953
|
+
throw new Error("GitHub-probe receipt does not resolve to canonical GitHub ticket content");
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
function validateReflectionLinks(record, readStored) {
|
|
958
|
+
const deterministicReference = record.payload.deterministic_evidence;
|
|
959
|
+
const failureReference = record.payload.failure_evidence;
|
|
960
|
+
if (deterministicReference.evidence_id === record.evidence_id || failureReference.evidence_id === record.evidence_id) {
|
|
961
|
+
throw new Error("reflection cannot reference itself");
|
|
962
|
+
}
|
|
963
|
+
const deterministic = readStored(deterministicReference);
|
|
964
|
+
const failure = readStored(failureReference);
|
|
965
|
+
if (deterministic.evidence_kind !== "deterministic-check") {
|
|
966
|
+
throw new Error("reflection deterministic reference has the wrong evidence kind");
|
|
967
|
+
}
|
|
968
|
+
if (deterministic.ticket_id !== record.ticket_id) {
|
|
969
|
+
throw new Error("reflection deterministic reference belongs to a different ticket");
|
|
970
|
+
}
|
|
971
|
+
if (record.payload.ticket_id !== record.ticket_id)
|
|
972
|
+
throw new Error("reflection ticket id mismatch");
|
|
973
|
+
if (failure.ticket_id !== record.ticket_id) {
|
|
974
|
+
throw new Error("reflection failure reference belongs to a different ticket");
|
|
975
|
+
}
|
|
976
|
+
const deterministicFailure = failure.evidence_kind === "deterministic-check"
|
|
977
|
+
&& sameEvidenceReference(failureReference, deterministicReference)
|
|
978
|
+
&& deterministic.payload.passed === false;
|
|
979
|
+
const gradeFailure = failure.evidence_kind === "grader-output"
|
|
980
|
+
&& failure.payload.verdict === "fail"
|
|
981
|
+
&& sameEvidenceReference(failure.payload.deterministic_evidence, deterministicReference);
|
|
982
|
+
if (!deterministicFailure && !gradeFailure) {
|
|
983
|
+
throw new Error("reflection requires a failed deterministic check or failed grade");
|
|
984
|
+
}
|
|
985
|
+
if (record.payload.attempt !== deterministic.payload.attempt) {
|
|
986
|
+
throw new Error("reflection attempt does not match its failed deterministic check");
|
|
987
|
+
}
|
|
988
|
+
if (record.created_at !== failure.created_at) {
|
|
989
|
+
throw new Error("reflection timestamp does not match its failure evidence");
|
|
990
|
+
}
|
|
991
|
+
const expectedId = createStableId("reflection", failureReference.evidence_id, failureReference.sha256);
|
|
992
|
+
if (record.evidence_id !== expectedId)
|
|
993
|
+
throw new Error("reflection id does not match its failure evidence");
|
|
994
|
+
}
|
|
995
|
+
function validateTrustReportLinks(record, readStored, allTrustRunReferences) {
|
|
996
|
+
const utcDate = record.payload.utc_date;
|
|
997
|
+
const closedAt = closedAtForUtcDate(utcDate);
|
|
998
|
+
if (record.payload.closed_at !== closedAt || record.created_at !== closedAt) {
|
|
999
|
+
throw new Error("trust report timestamp does not match its closed UTC date");
|
|
1000
|
+
}
|
|
1001
|
+
if (record.evidence_id !== createStableId("trust-report", utcDate, record.payload.policy_digest, record.payload.gate_digest)) {
|
|
1002
|
+
throw new Error("trust report id does not match its date, policy, and gate identity");
|
|
1003
|
+
}
|
|
1004
|
+
const references = record.payload.trust_run_evidence;
|
|
1005
|
+
const sorted = [...references].sort(compareEvidenceReferences);
|
|
1006
|
+
if (references.some((reference, index) => !sameEvidenceReference(reference, sorted[index]))) {
|
|
1007
|
+
throw new Error("trust report references are not sorted");
|
|
1008
|
+
}
|
|
1009
|
+
const seen = new Set();
|
|
1010
|
+
const runs = [];
|
|
1011
|
+
const startEpoch = timestampEpoch(`${utcDate}T00:00:00.000Z`, "trust report opened_at");
|
|
1012
|
+
const closeEpoch = timestampEpoch(closedAt, "trust report closed_at");
|
|
1013
|
+
if (Date.now() < closeEpoch)
|
|
1014
|
+
throw new Error("trust report requires a closed UTC date");
|
|
1015
|
+
for (const reference of references) {
|
|
1016
|
+
if (seen.has(reference.evidence_id))
|
|
1017
|
+
throw new Error(`duplicate trust run reference: ${reference.evidence_id}`);
|
|
1018
|
+
seen.add(reference.evidence_id);
|
|
1019
|
+
const trustRun = readStored(reference);
|
|
1020
|
+
if (trustRun.evidence_kind !== "trust-run") {
|
|
1021
|
+
throw new Error("trust report reference has the wrong evidence kind");
|
|
1022
|
+
}
|
|
1023
|
+
const runEpoch = timestampEpoch(trustRun.created_at, "trust run created_at");
|
|
1024
|
+
if (runEpoch < startEpoch || runEpoch >= closeEpoch) {
|
|
1025
|
+
throw new Error("trust report includes evidence outside its explicit UTC day");
|
|
1026
|
+
}
|
|
1027
|
+
runs.push({
|
|
1028
|
+
run_id: trustRun.payload.run_id,
|
|
1029
|
+
policy_version: trustRun.payload.policy_version,
|
|
1030
|
+
policy_digest: trustRun.payload.policy_digest,
|
|
1031
|
+
gate_digest: trustRun.payload.gate_digest,
|
|
1032
|
+
deterministic_passed: trustRun.payload.deterministic_passed,
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
const expectedReferences = allTrustRunReferences()
|
|
1036
|
+
.filter((reference) => {
|
|
1037
|
+
const createdEpoch = timestampEpoch(readStored(reference).created_at, "trust run created_at");
|
|
1038
|
+
return createdEpoch >= startEpoch && createdEpoch < closeEpoch;
|
|
1039
|
+
})
|
|
1040
|
+
.sort(compareEvidenceReferences);
|
|
1041
|
+
if (references.length !== expectedReferences.length
|
|
1042
|
+
|| references.some((reference, index) => !sameEvidenceReference(reference, expectedReferences[index]))) {
|
|
1043
|
+
throw new Error("trust report does not contain the complete explicit UTC day evidence set");
|
|
1044
|
+
}
|
|
1045
|
+
const expected = assessTrust({
|
|
1046
|
+
policy_version: record.payload.policy_version,
|
|
1047
|
+
policy_digest: record.payload.policy_digest,
|
|
1048
|
+
gate_digest: record.payload.gate_digest,
|
|
1049
|
+
runs,
|
|
1050
|
+
});
|
|
1051
|
+
const actual = record.payload.assessment;
|
|
1052
|
+
for (const key of [
|
|
1053
|
+
"status",
|
|
1054
|
+
"matching_runs",
|
|
1055
|
+
"passed_runs",
|
|
1056
|
+
"pass_rate_basis_points",
|
|
1057
|
+
"minimum_matching_runs",
|
|
1058
|
+
"required_pass_rate_basis_points",
|
|
1059
|
+
]) {
|
|
1060
|
+
if (actual[key] !== expected[key])
|
|
1061
|
+
throw new Error(`trust report assessment ${key} is inconsistent`);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
function compareEvidenceReferences(left, right) {
|
|
1065
|
+
return compareText(left.evidence_id, right.evidence_id)
|
|
1066
|
+
|| compareText(left.sha256, right.sha256)
|
|
1067
|
+
|| compareText(left.file_name, right.file_name);
|
|
1068
|
+
}
|
|
1069
|
+
function compareText(left, right) {
|
|
1070
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
1071
|
+
}
|
|
1072
|
+
function sameEvidenceReference(left, right) {
|
|
1073
|
+
return right !== undefined && right !== null
|
|
1074
|
+
&& left.evidence_id === right.evidence_id
|
|
1075
|
+
&& left.sha256 === right.sha256
|
|
1076
|
+
&& left.file_name === right.file_name;
|
|
1077
|
+
}
|
|
1078
|
+
function closedAtForUtcDate(value) {
|
|
1079
|
+
const match = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(value);
|
|
1080
|
+
if (!match)
|
|
1081
|
+
throw new Error("invalid trust report UTC date");
|
|
1082
|
+
const year = Number(match[1]);
|
|
1083
|
+
const month = Number(match[2]);
|
|
1084
|
+
const day = Number(match[3]);
|
|
1085
|
+
const start = new Date(Date.UTC(year, month - 1, day));
|
|
1086
|
+
if (start.getUTCFullYear() !== year || start.getUTCMonth() !== month - 1 || start.getUTCDate() !== day) {
|
|
1087
|
+
throw new Error("invalid trust report UTC date");
|
|
1088
|
+
}
|
|
1089
|
+
return new Date(start.getTime() + 24 * 60 * 60 * 1_000).toISOString();
|
|
1090
|
+
}
|
|
1091
|
+
const LIVE_ERROR_SECRET_PATTERNS = [
|
|
1092
|
+
/\bAuthorization\s*:\s*(?:Bearer|token)\s+\S+/giu,
|
|
1093
|
+
/\bBearer\s+[A-Za-z0-9._~+/-]+=*/giu,
|
|
1094
|
+
/\bgithub_pat_[A-Za-z0-9_]{8,}\b/giu,
|
|
1095
|
+
/\bgh[opusr]_[A-Za-z0-9_]{8,}\b/giu,
|
|
1096
|
+
/\b(?:GH_TOKEN|GITHUB_TOKEN)=\S+/giu,
|
|
1097
|
+
/\b(?:sk|key|token)-[A-Za-z0-9_-]{8,}\b/gu,
|
|
1098
|
+
/\b[A-Z][A-Z0-9_]*(?:API_KEY|TOKEN|SECRET|PASSWORD)=\S+/gu,
|
|
1099
|
+
];
|
|
1100
|
+
export function sanitizeLiveOutcomeError(value) {
|
|
1101
|
+
let sanitized = value;
|
|
1102
|
+
for (const pattern of LIVE_ERROR_SECRET_PATTERNS)
|
|
1103
|
+
sanitized = sanitized.replace(pattern, "[REDACTED]");
|
|
1104
|
+
const bytes = Buffer.from(sanitized, "utf8");
|
|
1105
|
+
if (bytes.byteLength <= 4096)
|
|
1106
|
+
return sanitized;
|
|
1107
|
+
return new StringDecoder("utf8").write(bytes.subarray(0, 4096));
|
|
1108
|
+
}
|
|
1109
|
+
function validateEvidenceReference(ref) {
|
|
1110
|
+
const evidenceId = assertOpaqueId(ref.evidence_id, "evidence id");
|
|
1111
|
+
if (!SHA256_PATTERN.test(ref.sha256))
|
|
1112
|
+
throw new Error("invalid evidence digest");
|
|
1113
|
+
if (ref.file_name !== `${evidenceId}.json`)
|
|
1114
|
+
throw new Error("evidence filename does not match its id");
|
|
1115
|
+
}
|
|
1116
|
+
function assertJsonValue(value, path, ancestors = new Set()) {
|
|
1117
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
1118
|
+
return;
|
|
1119
|
+
if (typeof value === "number") {
|
|
1120
|
+
if (!Number.isFinite(value))
|
|
1121
|
+
throw new Error(`${path} contains a non-finite number`);
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (typeof value !== "object")
|
|
1125
|
+
throw new Error(`${path} contains a non-JSON value`);
|
|
1126
|
+
if (ancestors.has(value))
|
|
1127
|
+
throw new Error(`${path} contains a cycle`);
|
|
1128
|
+
ancestors.add(value);
|
|
1129
|
+
try {
|
|
1130
|
+
if (Array.isArray(value)) {
|
|
1131
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1132
|
+
if (!(index in value))
|
|
1133
|
+
throw new Error(`${path}[${index}] contains a non-JSON array hole`);
|
|
1134
|
+
assertJsonValue(value[index], `${path}[${index}]`, ancestors);
|
|
1135
|
+
}
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
if (!isRecord(value) || (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)) {
|
|
1139
|
+
throw new Error(`${path} contains a non-JSON object`);
|
|
1140
|
+
}
|
|
1141
|
+
if (Object.getOwnPropertySymbols(value).length > 0)
|
|
1142
|
+
throw new Error(`${path} contains a symbol key`);
|
|
1143
|
+
for (const [key, entry] of Object.entries(value))
|
|
1144
|
+
assertJsonValue(entry, `${path}.${key}`, ancestors);
|
|
1145
|
+
}
|
|
1146
|
+
finally {
|
|
1147
|
+
ancestors.delete(value);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
function timestampEpoch(value, label) {
|
|
1151
|
+
if (typeof value !== "string")
|
|
1152
|
+
throw new Error(`invalid ${label}`);
|
|
1153
|
+
const match = TIMESTAMP_PATTERN.exec(value);
|
|
1154
|
+
if (!match)
|
|
1155
|
+
throw new Error(`invalid ${label}`);
|
|
1156
|
+
const [, yearRaw, monthRaw, dayRaw, hourRaw, minuteRaw, secondRaw, fractionRaw = ""] = match;
|
|
1157
|
+
const year = Number(yearRaw);
|
|
1158
|
+
const month = Number(monthRaw);
|
|
1159
|
+
const day = Number(dayRaw);
|
|
1160
|
+
const hour = Number(hourRaw);
|
|
1161
|
+
const minute = Number(minuteRaw);
|
|
1162
|
+
const second = Number(secondRaw);
|
|
1163
|
+
const millisecond = Number(fractionRaw.padEnd(3, "0").slice(0, 3));
|
|
1164
|
+
const date = new Date(Date.UTC(year, month - 1, day, hour, minute, second, millisecond));
|
|
1165
|
+
if (date.getUTCFullYear() !== year
|
|
1166
|
+
|| date.getUTCMonth() !== month - 1
|
|
1167
|
+
|| date.getUTCDate() !== day
|
|
1168
|
+
|| date.getUTCHours() !== hour
|
|
1169
|
+
|| date.getUTCMinutes() !== minute
|
|
1170
|
+
|| date.getUTCSeconds() !== second) {
|
|
1171
|
+
throw new Error(`invalid ${label}`);
|
|
1172
|
+
}
|
|
1173
|
+
return date.getTime();
|
|
1174
|
+
}
|
|
1175
|
+
function canonicalJson(value) {
|
|
1176
|
+
return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
1177
|
+
}
|
|
1178
|
+
function parseJson(bytes) {
|
|
1179
|
+
try {
|
|
1180
|
+
return JSON.parse(bytes.toString("utf8"));
|
|
1181
|
+
}
|
|
1182
|
+
catch (error) {
|
|
1183
|
+
throw new Error(`invalid JSON in evidence record: ${error instanceof Error ? error.message : String(error)}`);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
function sha256(bytes) {
|
|
1187
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
1188
|
+
}
|
|
1189
|
+
//# sourceMappingURL=store.js.map
|