@sema-agent/server 1.323.0 โ 2.0.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/dist/approval-hmac.d.ts +40 -0
- package/dist/approval-hmac.js +67 -0
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +72 -4
- package/dist/audit.d.ts +26 -0
- package/dist/audit.js +40 -1
- package/dist/auth-bridge.d.ts +10 -0
- package/dist/auth-bridge.js +47 -4
- package/dist/auth-keys.d.ts +19 -0
- package/dist/auth-keys.js +13 -0
- package/dist/bake-runner/main.d.ts +5 -0
- package/dist/bake-runner/main.js +43 -4
- package/dist/bake-runner/protocol.d.ts +57 -0
- package/dist/bake-runner/protocol.js +58 -3
- package/dist/bake-runner/runner.d.ts +54 -0
- package/dist/bake-runner/runner.js +87 -5
- package/dist/bench/l8/artifact.d.ts +63 -0
- package/dist/bench/l8/artifact.js +61 -2
- package/dist/bench/l8/escape.d.ts +36 -0
- package/dist/bench/l8/escape.js +25 -0
- package/dist/bench/l8/index.d.ts +14 -0
- package/dist/bench/l8/index.js +14 -0
- package/dist/bench/l8/probes.d.ts +74 -0
- package/dist/bench/l8/probes.js +60 -0
- package/dist/bench/l8/run-probes.d.ts +43 -0
- package/dist/bench/l8/run-probes.js +45 -2
- package/dist/bench/s1/arms.d.ts +126 -0
- package/dist/bench/s1/arms.js +188 -13
- package/dist/bench/s1/live-deps.d.ts +66 -0
- package/dist/bench/s1/live-deps.js +249 -11
- package/dist/bench/s1/oracle.d.ts +49 -0
- package/dist/bench/s1/oracle.js +36 -1
- package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
- package/dist/bench/s1/repair-oracle-adapter.js +30 -0
- package/dist/bench/s1/reviewer.d.ts +38 -0
- package/dist/bench/s1/reviewer.js +34 -0
- package/dist/bench/s1/row.d.ts +77 -0
- package/dist/bench/s1/row.js +62 -0
- package/dist/bench/s1/run-firm.d.ts +47 -0
- package/dist/bench/s1/run-firm.js +70 -4
- package/dist/bench/s1/runner-ctx.d.ts +71 -0
- package/dist/bench/s1/runner-ctx.js +36 -0
- package/dist/bench/s1/tasks.d.ts +108 -0
- package/dist/bench/s1/tasks.js +115 -0
- package/dist/boot-reclaim.d.ts +19 -0
- package/dist/boot-reclaim.js +20 -0
- package/dist/brain.d.ts +25 -0
- package/dist/brain.js +61 -1
- package/dist/budget.d.ts +66 -0
- package/dist/budget.js +128 -4
- package/dist/capabilities/builtin-tools.d.ts +3 -0
- package/dist/capabilities/builtin-tools.js +3 -0
- package/dist/capabilities/center-plugins.d.ts +17 -1
- package/dist/capabilities/center-plugins.js +45 -4
- package/dist/capabilities/center-prompts.d.ts +46 -0
- package/dist/capabilities/center-prompts.js +57 -3
- package/dist/capabilities/code-review-council.d.ts +28 -0
- package/dist/capabilities/code-review-council.js +40 -7
- package/dist/capabilities/collab-wire.d.ts +2 -0
- package/dist/capabilities/collab-workflows.d.ts +7 -0
- package/dist/capabilities/collab-workflows.js +44 -1
- package/dist/capabilities/oa-tools.d.ts +6 -0
- package/dist/capabilities/oa-tools.js +10 -0
- package/dist/capabilities/prompt.d.ts +14 -0
- package/dist/capabilities/prompt.js +14 -0
- package/dist/capabilities/prompts/code-review.d.ts +14 -0
- package/dist/capabilities/prompts/code-review.js +14 -0
- package/dist/capabilities/prompts/identity.d.ts +6 -0
- package/dist/capabilities/prompts/identity.js +6 -0
- package/dist/capabilities/prompts/team.d.ts +4 -0
- package/dist/capabilities/prompts/team.js +4 -0
- package/dist/capabilities/repo-tools.d.ts +11 -0
- package/dist/capabilities/repo-tools.js +8 -0
- package/dist/capabilities/sandbox-file-send.d.ts +95 -0
- package/dist/capabilities/sandbox-file-send.js +91 -3
- package/dist/capabilities/scenarios.d.ts +130 -0
- package/dist/capabilities/scenarios.js +180 -7
- package/dist/capabilities/select-environment-tool.d.ts +7 -0
- package/dist/capabilities/select-environment-tool.js +30 -0
- package/dist/capabilities/send-user-file-tool.d.ts +33 -0
- package/dist/capabilities/send-user-file-tool.js +65 -1
- package/dist/capabilities/skills.d.ts +29 -0
- package/dist/capabilities/skills.js +13 -2
- package/dist/capabilities/team.d.ts +22 -0
- package/dist/capabilities/team.js +22 -2
- package/dist/capabilities/tool-defer.d.ts +26 -0
- package/dist/capabilities/tool-defer.js +3 -0
- package/dist/config-center/apply-effective.d.ts +65 -0
- package/dist/config-center/apply-effective.js +204 -12
- package/dist/config-center/http-client.d.ts +34 -0
- package/dist/config-center/http-client.js +68 -0
- package/dist/config-center/restart-signal.d.ts +22 -0
- package/dist/config-center/restart-signal.js +39 -0
- package/dist/config-center/skills-mcp.d.ts +20 -1
- package/dist/config-center/skills-mcp.js +42 -7
- package/dist/config-center/types.d.ts +92 -1
- package/dist/config-lkg.d.ts +12 -0
- package/dist/config-lkg.js +41 -0
- package/dist/config-provider.d.ts +78 -0
- package/dist/config-provider.js +165 -2
- package/dist/config-types.d.ts +546 -0
- package/dist/config.d.ts +64 -0
- package/dist/config.js +455 -60
- package/dist/degenerate-instrument.d.ts +38 -0
- package/dist/degenerate-instrument.js +55 -3
- package/dist/elicitation.d.ts +39 -0
- package/dist/elicitation.js +87 -3
- package/dist/env-facts.d.ts +61 -0
- package/dist/env-facts.js +73 -7
- package/dist/fleet/fleet-bus.d.ts +185 -0
- package/dist/fleet/fleet-bus.js +333 -27
- package/dist/fleet/subagent-tail-bus.d.ts +11 -0
- package/dist/fleet/subagent-tail-bus.js +33 -2
- package/dist/fleet-client.d.ts +38 -0
- package/dist/fleet-client.js +44 -9
- package/dist/fleet-lease.d.ts +46 -0
- package/dist/fleet-lease.js +73 -5
- package/dist/hooks/branch-transcript.d.ts +8 -0
- package/dist/hooks/branch-transcript.js +33 -0
- package/dist/hooks/cc-stop-prompt.d.ts +42 -0
- package/dist/hooks/cc-stop-prompt.js +43 -1
- package/dist/hooks/hook-llm.d.ts +15 -0
- package/dist/hooks/hook-llm.js +47 -0
- package/dist/hooks/hook-runner.d.ts +89 -0
- package/dist/hooks/hook-runner.js +327 -18
- package/dist/http/idempotency.d.ts +31 -0
- package/dist/http/idempotency.js +34 -0
- package/dist/http/principal-gate.d.ts +17 -0
- package/dist/http/principal-gate.js +35 -3
- package/dist/http/route-ctx.d.ts +36 -0
- package/dist/http/routes/approvals-assistant.d.ts +25 -0
- package/dist/http/routes/approvals-assistant.js +253 -23
- package/dist/http/routes/attachments.js +22 -4
- package/dist/http/routes/capabilities.d.ts +7 -0
- package/dist/http/routes/capabilities.js +202 -1
- package/dist/http/routes/fleet.d.ts +6 -0
- package/dist/http/routes/fleet.js +94 -5
- package/dist/http/routes/images.d.ts +17 -0
- package/dist/http/routes/images.js +222 -14
- package/dist/http/routes/leader.d.ts +7 -0
- package/dist/http/routes/leader.js +10 -0
- package/dist/http/routes/memory-policy.d.ts +6 -0
- package/dist/http/routes/memory-policy.js +38 -6
- package/dist/http/routes/notify-wake.d.ts +10 -0
- package/dist/http/routes/notify-wake.js +19 -3
- package/dist/http/routes/observability.d.ts +6 -0
- package/dist/http/routes/observability.js +19 -2
- package/dist/http/routes/runs.d.ts +15 -0
- package/dist/http/routes/runs.js +464 -41
- package/dist/http/routes/session-sync.d.ts +15 -0
- package/dist/http/routes/session-sync.js +229 -19
- package/dist/http/routes/sessions-list.d.ts +6 -0
- package/dist/http/routes/sessions-list.js +35 -4
- package/dist/http/routes/sessions.d.ts +14 -0
- package/dist/http/routes/sessions.js +176 -31
- package/dist/http/routes/side-query.d.ts +8 -0
- package/dist/http/routes/side-query.js +26 -1
- package/dist/http/routes/tasks.d.ts +18 -0
- package/dist/http/routes/tasks.js +501 -38
- package/dist/http/routes/trace-usage.d.ts +10 -0
- package/dist/http/routes/trace-usage.js +66 -14
- package/dist/http/routes/workflows.d.ts +12 -0
- package/dist/http/routes/workflows.js +180 -19
- package/dist/http/run-meta.d.ts +11 -0
- package/dist/http/run-meta.js +6 -0
- package/dist/http/send.d.ts +19 -0
- package/dist/http/send.js +11 -0
- package/dist/http/server.d.ts +255 -0
- package/dist/http/server.js +921 -75
- package/dist/http/sse-log.d.ts +15 -0
- package/dist/http/sse-log.js +16 -0
- package/dist/http/tar.d.ts +9 -0
- package/dist/http/tar.js +21 -5
- package/dist/http/wire-gate.d.ts +9 -0
- package/dist/http/wire-gate.js +9 -0
- package/dist/http/wire-types.d.ts +187 -0
- package/dist/http/workspace-content.d.ts +8 -0
- package/dist/http/workspace-content.js +10 -0
- package/dist/images/bake-validate.d.ts +63 -0
- package/dist/images/bake-validate.js +71 -1
- package/dist/images/manifest.d.ts +19 -0
- package/dist/images/manifest.js +6 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +10 -5
- package/dist/key-resolver.d.ts +23 -0
- package/dist/key-resolver.js +27 -2
- package/dist/leader/diffout.d.ts +23 -0
- package/dist/leader/diffout.js +8 -1
- package/dist/leader/diffup.d.ts +32 -0
- package/dist/leader/diffup.js +43 -0
- package/dist/leader/endpoint.d.ts +20 -0
- package/dist/leader/endpoint.js +25 -2
- package/dist/leader/fanout.d.ts +60 -0
- package/dist/leader/fanout.js +29 -3
- package/dist/leader/grader-env-factory.d.ts +74 -0
- package/dist/leader/grader-env-factory.js +62 -3
- package/dist/leader/leader.d.ts +97 -0
- package/dist/leader/leader.js +151 -4
- package/dist/leader/merge.d.ts +94 -0
- package/dist/leader/merge.js +112 -9
- package/dist/leader/planner.d.ts +52 -0
- package/dist/leader/planner.js +47 -3
- package/dist/leader/repair-oracle.d.ts +61 -0
- package/dist/leader/repair-oracle.js +59 -1
- package/dist/leader/repair-wire.d.ts +78 -0
- package/dist/leader/repair-wire.js +79 -2
- package/dist/leader/wire.d.ts +79 -0
- package/dist/leader/wire.js +255 -8
- package/dist/lsp/e2b-bridge.d.ts +16 -0
- package/dist/lsp/e2b-bridge.js +32 -4
- package/dist/lsp/e2b-manager.d.ts +10 -0
- package/dist/lsp/e2b-manager.js +18 -2
- package/dist/lsp/lsp-frames.d.ts +12 -0
- package/dist/lsp/lsp-frames.js +12 -0
- package/dist/lsp/manager.d.ts +12 -0
- package/dist/lsp/manager.js +35 -2
- package/dist/lsp/types.d.ts +10 -0
- package/dist/lsp/ws-transport.d.ts +12 -0
- package/dist/lsp/ws-transport.js +53 -5
- package/dist/lsp-evict.d.ts +14 -0
- package/dist/lsp-evict.js +15 -1
- package/dist/main.js +2132 -96
- package/dist/memory-export.d.ts +1 -0
- package/dist/memory-export.js +4 -0
- package/dist/memory-scope.d.ts +49 -0
- package/dist/memory-scope.js +87 -4
- package/dist/memory-sync-client.d.ts +24 -0
- package/dist/memory-sync-client.js +41 -2
- package/dist/memory-sync.d.ts +23 -0
- package/dist/memory-sync.js +70 -1
- package/dist/model-select.d.ts +39 -0
- package/dist/model-select.js +42 -1
- package/dist/observability/cost-quota.d.ts +20 -0
- package/dist/observability/cost-quota.js +4 -0
- package/dist/observability/cost-taxonomy.d.ts +45 -0
- package/dist/observability/cost-taxonomy.js +34 -0
- package/dist/observability/logger.d.ts +1 -0
- package/dist/observability/logger.js +6 -0
- package/dist/observability/metrics.d.ts +13 -0
- package/dist/observability/metrics.js +63 -0
- package/dist/observability/otel-exporter.d.ts +9 -0
- package/dist/observability/otel-exporter.js +11 -3
- package/dist/observability/principal-context.d.ts +9 -0
- package/dist/observability/principal-context.js +9 -0
- package/dist/observability/prompt-manifest.d.ts +37 -0
- package/dist/observability/prompt-manifest.js +35 -1
- package/dist/observability/rate-limit.d.ts +10 -0
- package/dist/observability/rate-limit.js +1 -0
- package/dist/observability/tool-trace.d.ts +24 -0
- package/dist/observability/tool-trace.js +41 -1
- package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
- package/dist/orchestration/hardened-vm-runner.js +119 -4
- package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
- package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
- package/dist/orchestration/hardened-vm-worker.js +27 -0
- package/dist/orchestration/subagent-steer.d.ts +44 -0
- package/dist/orchestration/subagent-steer.js +45 -1
- package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
- package/dist/orchestration/workflow-agent-steer.js +80 -1
- package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
- package/dist/orchestration/workflow-completion-inbox.js +283 -32
- package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
- package/dist/orchestration/workflow-notify-journal.js +187 -13
- package/dist/parked-decide.d.ts +61 -0
- package/dist/parked-decide.js +58 -1
- package/dist/per-task-image.d.ts +54 -0
- package/dist/per-task-image.js +41 -0
- package/dist/plan-cache-probe.d.ts +35 -0
- package/dist/plan-cache-probe.js +18 -3
- package/dist/plugins/approval-exemption-store.d.ts +7 -0
- package/dist/plugins/approval-exemption-store.js +28 -2
- package/dist/plugins/approval-store-sql.d.ts +64 -0
- package/dist/plugins/approval-store-sql.js +32 -0
- package/dist/plugins/background-agent-store-sql.d.ts +55 -0
- package/dist/plugins/background-agent-store-sql.js +91 -4
- package/dist/plugins/background-shell-support.d.ts +98 -0
- package/dist/plugins/background-shell-support.js +117 -13
- package/dist/plugins/blob-backend.d.ts +104 -0
- package/dist/plugins/blob-backend.js +163 -6
- package/dist/plugins/breaker-state-sql.d.ts +65 -1
- package/dist/plugins/breaker-state-sql.js +46 -8
- package/dist/plugins/caching-session-store.d.ts +42 -0
- package/dist/plugins/caching-session-store.js +63 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
- package/dist/plugins/checkpoint-store-sql.js +239 -11
- package/dist/plugins/file-outcome-sink.d.ts +4 -0
- package/dist/plugins/file-outcome-sink.js +9 -0
- package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
- package/dist/plugins/file-resume-anchor-store.js +43 -4
- package/dist/plugins/file-run-store.d.ts +90 -0
- package/dist/plugins/file-run-store.js +145 -13
- package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
- package/dist/plugins/file-snapshot-store-sql.js +178 -10
- package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
- package/dist/plugins/file-workflow-journal-store.js +10 -0
- package/dist/plugins/fork-routing-session-store.d.ts +47 -0
- package/dist/plugins/fork-routing-session-store.js +51 -3
- package/dist/plugins/host-platform.d.ts +69 -0
- package/dist/plugins/host-platform.js +81 -2
- package/dist/plugins/image-bake-store-sql.d.ts +136 -1
- package/dist/plugins/image-bake-store-sql.js +241 -7
- package/dist/plugins/image-index-sql.d.ts +46 -0
- package/dist/plugins/image-index-sql.js +113 -4
- package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
- package/dist/plugins/k8s-bg-scripts.js +88 -4
- package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
- package/dist/plugins/k8s-exec-protocol.js +41 -0
- package/dist/plugins/local-checkpoint-store.d.ts +40 -0
- package/dist/plugins/local-checkpoint-store.js +86 -7
- package/dist/plugins/local-session-store.d.ts +157 -0
- package/dist/plugins/local-session-store.js +289 -34
- package/dist/plugins/local-task-attachment-store.js +14 -2
- package/dist/plugins/mailbox-store-sql.d.ts +38 -0
- package/dist/plugins/mailbox-store-sql.js +39 -6
- package/dist/plugins/memory-engine-pg.d.ts +30 -0
- package/dist/plugins/memory-engine-pg.js +122 -4
- package/dist/plugins/memory-engine-tidb.d.ts +26 -0
- package/dist/plugins/memory-engine-tidb.js +115 -5
- package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
- package/dist/plugins/memory-engine-vector-util.js +5 -0
- package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
- package/dist/plugins/memory-resume-anchor-store.js +17 -0
- package/dist/plugins/memory-run-store.d.ts +63 -0
- package/dist/plugins/memory-run-store.js +73 -8
- package/dist/plugins/memory-session-policy-store.d.ts +17 -0
- package/dist/plugins/memory-session-policy-store.js +17 -0
- package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
- package/dist/plugins/memory-sync-store-pg.js +45 -2
- package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
- package/dist/plugins/memory-sync-store-tidb.js +31 -1
- package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
- package/dist/plugins/outcome-ledger-sql.js +97 -3
- package/dist/plugins/pg-approval-store.d.ts +7 -0
- package/dist/plugins/pg-approval-store.js +7 -0
- package/dist/plugins/pg-breaker-state.d.ts +6 -0
- package/dist/plugins/pg-breaker-state.js +6 -0
- package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
- package/dist/plugins/pg-checkpoint-store.js +8 -0
- package/dist/plugins/pg-cost-quota.d.ts +17 -0
- package/dist/plugins/pg-cost-quota.js +6 -0
- package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/pg-file-snapshot-store.js +6 -0
- package/dist/plugins/pg-image-bake.d.ts +9 -0
- package/dist/plugins/pg-image-bake.js +9 -0
- package/dist/plugins/pg-image-index.d.ts +9 -0
- package/dist/plugins/pg-image-index.js +9 -0
- package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
- package/dist/plugins/pg-outcome-ledger.js +9 -0
- package/dist/plugins/pg-pool.d.ts +50 -0
- package/dist/plugins/pg-pool.js +78 -0
- package/dist/plugins/pg-query.d.ts +11 -0
- package/dist/plugins/pg-rate-limiter.d.ts +23 -0
- package/dist/plugins/pg-rate-limiter.js +12 -2
- package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/pg-resume-anchor-store.js +5 -0
- package/dist/plugins/pg-run-store.d.ts +7 -0
- package/dist/plugins/pg-run-store.js +7 -0
- package/dist/plugins/pg-safe-json.d.ts +13 -0
- package/dist/plugins/pg-safe-json.js +40 -4
- package/dist/plugins/pg-session-policy-store.d.ts +5 -0
- package/dist/plugins/pg-session-policy-store.js +5 -0
- package/dist/plugins/pg-session-storage.d.ts +72 -0
- package/dist/plugins/pg-session-storage.js +152 -20
- package/dist/plugins/pg-session-store.d.ts +10 -0
- package/dist/plugins/pg-session-store.js +10 -0
- package/dist/plugins/pg-tool-result-store.d.ts +7 -0
- package/dist/plugins/pg-tool-result-store.js +7 -0
- package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-journal-store.js +7 -0
- package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-run-store.js +7 -0
- package/dist/plugins/posix-shell-fs.d.ts +3 -0
- package/dist/plugins/posix-shell-fs.js +31 -1
- package/dist/plugins/remote-env-adb.d.ts +25 -0
- package/dist/plugins/remote-env-adb.js +95 -12
- package/dist/plugins/remote-env-e2b.d.ts +146 -0
- package/dist/plugins/remote-env-e2b.js +371 -40
- package/dist/plugins/remote-env-file-error.d.ts +34 -0
- package/dist/plugins/remote-env-file-error.js +37 -0
- package/dist/plugins/remote-env-host.d.ts +151 -0
- package/dist/plugins/remote-env-host.js +468 -70
- package/dist/plugins/remote-env-k8s.d.ts +135 -0
- package/dist/plugins/remote-env-k8s.js +318 -32
- package/dist/plugins/remote-env-local-docker.d.ts +65 -0
- package/dist/plugins/remote-env-local-docker.js +149 -24
- package/dist/plugins/remote-env-ssh.d.ts +35 -0
- package/dist/plugins/remote-env-ssh.js +117 -20
- package/dist/plugins/remote-scratchpad.d.ts +38 -0
- package/dist/plugins/remote-scratchpad.js +32 -2
- package/dist/plugins/remote-shell.d.ts +22 -0
- package/dist/plugins/remote-shell.js +27 -1
- package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
- package/dist/plugins/resume-anchor-store-sql.js +11 -0
- package/dist/plugins/roster-store-sql.d.ts +27 -0
- package/dist/plugins/roster-store-sql.js +54 -0
- package/dist/plugins/run-store-sql.d.ts +194 -0
- package/dist/plugins/run-store-sql.js +210 -9
- package/dist/plugins/s3-presign.d.ts +20 -0
- package/dist/plugins/s3-presign.js +49 -2
- package/dist/plugins/scheduler-support.d.ts +12 -0
- package/dist/plugins/scheduler-support.js +75 -3
- package/dist/plugins/send-file-ledger.d.ts +15 -0
- package/dist/plugins/send-file-ledger.js +53 -4
- package/dist/plugins/send-user-file.d.ts +77 -0
- package/dist/plugins/send-user-file.js +92 -4
- package/dist/plugins/session-policy-store-sql.d.ts +15 -0
- package/dist/plugins/session-policy-store-sql.js +75 -3
- package/dist/plugins/session-store.d.ts +45 -0
- package/dist/plugins/session-store.js +51 -0
- package/dist/plugins/sql-driver.d.ts +77 -0
- package/dist/plugins/sql-driver.js +4 -0
- package/dist/plugins/sql-escape.d.ts +8 -0
- package/dist/plugins/sql-escape.js +8 -0
- package/dist/plugins/sql-row-helpers.d.ts +25 -0
- package/dist/plugins/sql-row-helpers.js +25 -0
- package/dist/plugins/store-backend.d.ts +103 -0
- package/dist/plugins/store-backend.js +120 -27
- package/dist/plugins/store-contracts.d.ts +62 -0
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +60 -0
- package/dist/plugins/task-attachment-store.js +49 -3
- package/dist/plugins/task-list-store-sql.d.ts +25 -0
- package/dist/plugins/task-list-store-sql.js +21 -0
- package/dist/plugins/tidb-approval-store.d.ts +6 -0
- package/dist/plugins/tidb-approval-store.js +6 -0
- package/dist/plugins/tidb-breaker-state.d.ts +5 -0
- package/dist/plugins/tidb-breaker-state.js +5 -0
- package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
- package/dist/plugins/tidb-checkpoint-store.js +7 -0
- package/dist/plugins/tidb-cost-quota.d.ts +6 -0
- package/dist/plugins/tidb-cost-quota.js +1 -0
- package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/tidb-file-snapshot-store.js +6 -0
- package/dist/plugins/tidb-image-bake.d.ts +8 -0
- package/dist/plugins/tidb-image-bake.js +8 -0
- package/dist/plugins/tidb-image-index.d.ts +8 -0
- package/dist/plugins/tidb-image-index.js +8 -0
- package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
- package/dist/plugins/tidb-outcome-ledger.js +10 -0
- package/dist/plugins/tidb-pool.d.ts +56 -0
- package/dist/plugins/tidb-pool.js +181 -1
- package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
- package/dist/plugins/tidb-rate-limiter.js +5 -2
- package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/tidb-resume-anchor-store.js +5 -0
- package/dist/plugins/tidb-run-store.d.ts +7 -0
- package/dist/plugins/tidb-run-store.js +7 -0
- package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
- package/dist/plugins/tidb-session-policy-store.js +5 -0
- package/dist/plugins/tidb-session-storage.d.ts +43 -0
- package/dist/plugins/tidb-session-storage.js +62 -3
- package/dist/plugins/tidb-session-store.d.ts +142 -0
- package/dist/plugins/tidb-session-store.js +264 -16
- package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
- package/dist/plugins/tidb-tool-result-store.js +7 -0
- package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/tidb-workflow-journal-store.js +7 -0
- package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
- package/dist/plugins/tidb-workflow-run-store.js +8 -0
- package/dist/plugins/tool-result-store-sql.d.ts +58 -0
- package/dist/plugins/tool-result-store-sql.js +38 -2
- package/dist/plugins/web-search.d.ts +35 -0
- package/dist/plugins/web-search.js +45 -9
- package/dist/plugins/workflow-journal-limits.d.ts +10 -0
- package/dist/plugins/workflow-journal-limits.js +10 -0
- package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
- package/dist/plugins/workflow-journal-store-sql.js +58 -2
- package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
- package/dist/plugins/workflow-run-store-sql.js +84 -6
- package/dist/plugins/worktree-isolation.d.ts +45 -0
- package/dist/plugins/worktree-isolation.js +105 -6
- package/dist/plugins/write-behind-counter.d.ts +56 -1
- package/dist/plugins/write-behind-counter.js +53 -14
- package/dist/principal-jwt.d.ts +24 -0
- package/dist/principal-jwt.js +60 -5
- package/dist/project-memory.d.ts +22 -0
- package/dist/project-memory.js +144 -15
- package/dist/prompts-domain-validate.d.ts +28 -0
- package/dist/prompts-domain-validate.js +42 -1
- package/dist/question.d.ts +33 -0
- package/dist/question.js +69 -2
- package/dist/resource-suspend.d.ts +21 -0
- package/dist/resource-suspend.js +16 -0
- package/dist/router/route-orchestration.d.ts +78 -0
- package/dist/router/route-orchestration.js +77 -0
- package/dist/run-local.d.ts +32 -0
- package/dist/run-local.js +172 -8
- package/dist/runs.d.ts +189 -4
- package/dist/runs.js +465 -24
- package/dist/runtime-caps-resolver.d.ts +106 -0
- package/dist/runtime-caps-resolver.js +89 -4
- package/dist/runtime-governance.d.ts +84 -0
- package/dist/runtime-governance.js +127 -1
- package/dist/sandbox-pkg-source.d.ts +23 -0
- package/dist/sandbox-pkg-source.js +37 -0
- package/dist/sealed-key.d.ts +54 -0
- package/dist/sealed-key.js +68 -3
- package/dist/security.d.ts +108 -0
- package/dist/security.js +145 -6
- package/dist/sema-registry.d.ts +21 -0
- package/dist/sema-registry.js +21 -0
- package/dist/session-leaf-bus.d.ts +31 -0
- package/dist/session-leaf-bus.js +32 -0
- package/dist/session-sync-content.d.ts +46 -0
- package/dist/session-sync-content.js +47 -1
- package/dist/session-sync-kernel.d.ts +89 -0
- package/dist/session-sync-kernel.js +48 -3
- package/dist/session-sync.d.ts +125 -0
- package/dist/session-sync.js +181 -8
- package/dist/session-titler.d.ts +26 -0
- package/dist/session-titler.js +41 -7
- package/dist/session-watch.d.ts +53 -1
- package/dist/session-watch.js +73 -9
- package/dist/sighup-idle.d.ts +30 -0
- package/dist/sighup-idle.js +12 -1
- package/dist/spec-fields.d.ts +91 -3
- package/dist/spec-fields.js +116 -4
- package/dist/task-cwd.d.ts +50 -0
- package/dist/task-cwd.js +68 -1
- package/dist/task-mcp.d.ts +47 -0
- package/dist/task-mcp.js +29 -0
- package/dist/task-settings.d.ts +175 -0
- package/dist/task-settings.js +226 -11
- package/dist/task-workflow.d.ts +67 -0
- package/dist/task-workflow.js +75 -8
- package/dist/tool-approval.d.ts +91 -0
- package/dist/tool-approval.js +215 -0
- package/dist/trace/artifacts.d.ts +9 -0
- package/dist/trace/artifacts.js +30 -5
- package/dist/trace/core-keyset-guard.d.ts +18 -0
- package/dist/trace/ledger-sink.d.ts +43 -0
- package/dist/trace/ledger-sink.js +64 -5
- package/dist/trace/project.d.ts +133 -0
- package/dist/trace/project.js +189 -2
- package/dist/trace/redact.d.ts +19 -0
- package/dist/trace/redact.js +66 -9
- package/dist/usage-analytics.d.ts +26 -0
- package/dist/usage-analytics.js +26 -3
- package/package.json +1 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** The claim image-api returns: the VETTED argv (operator free-text never reaches the runner) + the per-bake
|
|
2
|
+
* ingest secret minted at claim (so a stale/rogue runner can't inject frames into another bake โ ยงP2.4e). */
|
|
1
3
|
export interface ClaimedBake {
|
|
2
4
|
bakeId: string;
|
|
3
5
|
argv: string[];
|
|
@@ -5,22 +7,34 @@ export interface ClaimedBake {
|
|
|
5
7
|
dryRun: boolean;
|
|
6
8
|
logs: boolean;
|
|
7
9
|
ingestSecret: string;
|
|
10
|
+
/** Denormalized so the runner can pick the profile-aware deadline without a second fetch. */
|
|
8
11
|
profile?: string | null;
|
|
9
12
|
bands?: string[] | null;
|
|
10
13
|
}
|
|
14
|
+
/** The ingest/heartbeat response โ image-api echoes whether a cancel was requested so the runner kills the group. */
|
|
11
15
|
export interface IngestAck {
|
|
12
16
|
accepted: boolean;
|
|
17
|
+
/** The durable cancel flag (ยงP2.11) โ polled off EVERY ingest/heartbeat response (โคheartbeat-interval lag). */
|
|
13
18
|
cancelRequested?: boolean;
|
|
19
|
+
/** False when the lease was lost/stolen (the runner aborts + kills the child โ it no longer owns the build). */
|
|
14
20
|
leaseValid?: boolean;
|
|
15
21
|
}
|
|
22
|
+
/** A spawned build.sh child โ abstracted so a test can drive stdout/stderr/exit without a real process. The handle
|
|
23
|
+
* exposes the process-GROUP id (negative pid) so a kill signals the WHOLE group (the setsid-detached tree). */
|
|
16
24
|
export interface ChildHandle {
|
|
25
|
+
/** Negated pgid for `process.kill(-pgid, sig)` โ the group kill that actually stops a detached build (ยงP2.11). */
|
|
17
26
|
readonly pgid: number;
|
|
27
|
+
/** Resolves with the child's exit code (or 124-style on a signal) once it exits. */
|
|
18
28
|
readonly exited: Promise<{
|
|
19
29
|
code: number | null;
|
|
20
30
|
signal: string | null;
|
|
21
31
|
}>;
|
|
32
|
+
/** Send a signal to the process GROUP (SIGTERM then SIGKILL on the cancel/deadline path). */
|
|
22
33
|
killGroup(signal: NodeJS.Signals): void;
|
|
23
34
|
}
|
|
35
|
+
/** Spawn `build.sh <argv> --json` detached in its own process group; stdout/stderr are delivered LINE-BY-LINE via
|
|
36
|
+
* the callbacks (the impl splits on newlines). The runner feeds ONLY stdout lines through the JSON parser; stderr
|
|
37
|
+
* is the failure tail (ยงP2.5) โ buffered, surfaced on a non-zero exit, never JSON.parsed. */
|
|
24
38
|
export interface ChildSpawner {
|
|
25
39
|
spawn(opts: {
|
|
26
40
|
argv: string[];
|
|
@@ -29,15 +43,27 @@ export interface ChildSpawner {
|
|
|
29
43
|
onStderrLine(line: string): void;
|
|
30
44
|
}): ChildHandle;
|
|
31
45
|
}
|
|
46
|
+
/** The image-api HTTP client (claim/ingest) โ injected so a test drives the control plane without a server. */
|
|
32
47
|
export interface ImageApiClient {
|
|
48
|
+
/** Long-poll for + claim the next queued bake; null when none is claimable right now. */
|
|
33
49
|
claimNext(): Promise<ClaimedBake | null>;
|
|
50
|
+
/** POST one frame to `โฆ/ingest` (carries the per-bake ingest secret header); returns the ack (cancel/lease). */
|
|
34
51
|
ingest(bakeId: string, ingestSecret: string, frame: Record<string, unknown>): Promise<IngestAck>;
|
|
52
|
+
/** Heartbeat the lease forward (also surfaces cancel/lease state) โ distinct from a frame ingest. */
|
|
35
53
|
heartbeat(bakeId: string, ingestSecret: string): Promise<IngestAck>;
|
|
36
54
|
}
|
|
55
|
+
/** Host-side ops the runner needs that are NOT image-api calls (fs + a thin docker-push wrapper for the big-blob
|
|
56
|
+
* retry โ NOT a build.sh --replay). All injected so the orchestration is testable off build-host. */
|
|
37
57
|
export interface HostOps {
|
|
58
|
+
/** Read the manifest BODY off the build-host FS (build.sh's done carries only a path โ a cat/read is mandatory, ยงP2.10). */
|
|
38
59
|
readManifest(path: string): Promise<unknown>;
|
|
60
|
+
/** `df -P -BG /data` output for the pre-flight disk guard (ยงP2.7); the runner parses free GiB out of it. */
|
|
39
61
|
dfData(): Promise<string>;
|
|
62
|
+
/** Checkout the recipe at RECIPE_REF so build.sh's git rev-parse gitsha is REAL (ยงP2.12); returns the cwd. */
|
|
40
63
|
checkoutRecipe(ref: string): Promise<string>;
|
|
64
|
+
/** The thin `docker push` big-blob retry wrapper (ยงP2.12) โ retries a flaky registry layer push (born on SWR โ
|
|
65
|
+
* deprecated; pool now docker.io/claybobby) WITHOUT re-running the
|
|
66
|
+
* whole build (build.sh has no push-only re-entry; --replay would re-bake + hit the dup-check). Best-effort. */
|
|
41
67
|
dockerPushRetry?(tag: string, attempts: number): Promise<{
|
|
42
68
|
ok: boolean;
|
|
43
69
|
digest: string | null;
|
|
@@ -58,10 +84,17 @@ export interface BakeRunnerOpts {
|
|
|
58
84
|
host: HostOps;
|
|
59
85
|
clock: Clock;
|
|
60
86
|
log: BakeRunnerLogger;
|
|
87
|
+
/** The git ref build.sh's recipe checkout is pinned to (RECIPE_REF) โ replay-sufficient gitsha (ยงP2.12). */
|
|
61
88
|
recipeRef: string;
|
|
89
|
+
/** The absolute path to build.sh inside the recipe checkout (e.g. e2b-template/dev-sandbox/build.sh). */
|
|
62
90
|
buildShPath: string;
|
|
91
|
+
/** Lease heartbeat cadence (default 30s) โ must stay well under image-api's BAKE_LEASE_MS (90s) so a healthy
|
|
92
|
+
* build never has its lease reaped (ยงP2.7). The docker-build progress tick + cancel poll ride the same cadence. */
|
|
63
93
|
heartbeatMs?: number;
|
|
94
|
+
/** Idle poll backoff when no bake is claimable (default 5s). */
|
|
64
95
|
idlePollMs?: number;
|
|
96
|
+
/** Minimum free GiB on /data at claim; below this the bake fails fast (507-class) rather than dying mid-build and
|
|
97
|
+
* leaving a half-layer (k3s + docker containerd both on /data โ a fill triggers kubelet GC eviction โ ยงP2.7). */
|
|
65
98
|
minFreeGb?: number;
|
|
66
99
|
}
|
|
67
100
|
export declare class BakeRunner {
|
|
@@ -71,15 +104,36 @@ export declare class BakeRunner {
|
|
|
71
104
|
private readonly minFreeGb;
|
|
72
105
|
private stopped;
|
|
73
106
|
constructor(opts: BakeRunnerOpts);
|
|
107
|
+
/** Cooperative shutdown โ finishes the in-flight bake's current frame, then exits the claim loop. */
|
|
74
108
|
stop(): void;
|
|
109
|
+
/** The long-poll loop: claim โ run โ release โ poll next. Single-flight is enforced DB-side (the claim CAS), so a
|
|
110
|
+
* second runner replica simply never wins a claim. Runs until `stop()`. */
|
|
75
111
|
loop(): Promise<void>;
|
|
112
|
+
/** Run a single CLAIMED bake end-to-end. Owns the lineOrd counter, the heartbeat/cancel/deadline timers, the
|
|
113
|
+
* child process group, the stdoutโingest pump, and the terminal done synthesis. */
|
|
76
114
|
runOne(bake: ClaimedBake): Promise<void>;
|
|
115
|
+
/** Forward build.sh's own `done` line โ but FIRST read the manifest BODY off disk and attach it so image-api can
|
|
116
|
+
* auto-register (build.sh's done carries only a build-host path, NOT the body โ ยงP2.10). A manifest read failure is
|
|
117
|
+
* non-fatal: the done still forwards (the digest is on the line); image-api then registers indexId:null partial.
|
|
118
|
+
*
|
|
119
|
+
* BIG-BLOB PUSH RETRY (ยงP2.12): build.sh dies exit 13 on a flaky `docker push`. The retry is a THIN `docker push`
|
|
120
|
+
* wrapper on the host (NOT build.sh --replay โ that re-bakes + hits the dup-check exit 10). On a push-fail done
|
|
121
|
+
* for a `--push` bake with a known tag, the runner retries the push; on success it rewrites the done to COMPLETE
|
|
122
|
+
* with the recovered post-push digest (no manifest on disk โ build.sh dies before the manifest write โ so image-
|
|
123
|
+
* api registers the built-and-pushed-but-unregistered partial, which the P3 reconciliation tick finalizes). */
|
|
77
124
|
private forwardDone;
|
|
125
|
+
/** Kill the process GROUP: SIGTERM, then SIGKILL shortly after (a SIGKILL mid-docker-build can leave dangling
|
|
126
|
+
* layers; the dup-check + lockhash prevent a poisoned re-bake, and a startup workdir sweep reclaims them โ ยงP2.11). */
|
|
78
127
|
private killGroup;
|
|
79
128
|
private stopSupervisor;
|
|
129
|
+
/** Pre-flight free-GiB read; null (fail-open) on a df parse/exec miss โ a df hiccup must not block a bake (the
|
|
130
|
+
* reaper + a mid-build fill guard still protect the host). */
|
|
80
131
|
private freeGbOrNull;
|
|
132
|
+
/** Ingest one frame, swallowing transient errors (the buffered-retry of unsent lines is the real-runner's job;
|
|
133
|
+
* image-api's uq_line makes a re-send a no-op, so a dropped frame is recoverable, never a crash). */
|
|
81
134
|
private safeIngest;
|
|
82
135
|
}
|
|
136
|
+
/** Allocate the next per-bake lineOrd (monotonic from 1) โ the at-least-once ingest dedupe key (ยงP2.10). */
|
|
83
137
|
export declare function nextOrd(ord: {
|
|
84
138
|
n: number;
|
|
85
139
|
}): number;
|
|
@@ -1,8 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bake-runner core (IMAGE-API-DESIGN.md ยงP2.0/ยงP2.12) โ the privileged build-host daemon that actually runs `build.sh`.
|
|
3
|
+
*
|
|
4
|
+
* image-api (the unprivileged controller) holds ZERO build credentials; this runner is the ONE privileged,
|
|
5
|
+
* non-customer-facing tier (node-pinned build-host, replicas=1, docker.sock + ctr + /root/.docker mounted). It:
|
|
6
|
+
* 1. long-polls image-api for a queued bake and CLAIMS it (single-flight via the DB lease โ image-api's job);
|
|
7
|
+
* 2. checks out the recipe at the deployed RECIPE_REF (so build.sh's git rev-parse is REAL โ ยงP2.12);
|
|
8
|
+
* 3. spawns `build.sh <VETTED argv> --json` as a CHILD IN ITS OWN PROCESS GROUP (setsid/detached) so a cancel
|
|
9
|
+
* can SIGTERM/SIGKILL the whole group โ channel-close does NOT kill a build (ยงP2.11);
|
|
10
|
+
* 4. forwards each STDOUT line to `โฆ/ingest` with an incrementing lineOrd (at-least-once dedupe โ ยงP2.10);
|
|
11
|
+
* 5. heartbeats the lease (+ polls cancel_requested off the ingest/heartbeat response) every ~30s;
|
|
12
|
+
* 6. on cancel/deadline kills the process GROUP and synthesizes the terminal `done` (build.sh emits none โ ยงP2.11);
|
|
13
|
+
* 7. on the `done` line reads the manifest BODY off disk and forwards it so image-api can auto-register (ยงP2.10).
|
|
14
|
+
*
|
|
15
|
+
* This file is the I/O-injected core: the HTTP client, the child spawner, the fs reader, the clock and the logger
|
|
16
|
+
* are all dependencies, so the orchestration is structurally testable here while `main.ts` wires the real ones (the
|
|
17
|
+
* runner can only be exercised live on build-host). The lineโframe mapping itself lives PURE in ./protocol.ts.
|
|
18
|
+
*/
|
|
1
19
|
import { resolve as resolvePath } from "node:path";
|
|
2
20
|
import { lineToFrame, dockerBuildTickFrame, synthDoneFrame, exitDoneFrame, deadlineMsForProfile, parseDfFreeGb, parseBuildShLine, manifestPathFromLine, } from "./protocol.js";
|
|
3
21
|
const DEFAULT_HEARTBEAT_MS = 30_000;
|
|
4
22
|
const DEFAULT_IDLE_POLL_MS = 5_000;
|
|
5
23
|
const DEFAULT_MIN_FREE_GB = 20;
|
|
24
|
+
/** A high, fixed lineOrd for the loop-level catch terminal (runOne owns the per-bake counter, so its current value
|
|
25
|
+
* is out of scope here) โ far above any realistic in-bake line count so it never collides on uq_line. */
|
|
6
26
|
const TERMINAL_FALLBACK_ORD = 2_000_000_000;
|
|
7
27
|
export class BakeRunner {
|
|
8
28
|
o;
|
|
@@ -16,9 +36,12 @@ export class BakeRunner {
|
|
|
16
36
|
this.idlePollMs = opts.idlePollMs ?? DEFAULT_IDLE_POLL_MS;
|
|
17
37
|
this.minFreeGb = opts.minFreeGb ?? DEFAULT_MIN_FREE_GB;
|
|
18
38
|
}
|
|
39
|
+
/** Cooperative shutdown โ finishes the in-flight bake's current frame, then exits the claim loop. */
|
|
19
40
|
stop() {
|
|
20
41
|
this.stopped = true;
|
|
21
42
|
}
|
|
43
|
+
/** The long-poll loop: claim โ run โ release โ poll next. Single-flight is enforced DB-side (the claim CAS), so a
|
|
44
|
+
* second runner replica simply never wins a claim. Runs until `stop()`. */
|
|
22
45
|
async loop() {
|
|
23
46
|
while (!this.stopped) {
|
|
24
47
|
let claimed = null;
|
|
@@ -36,18 +59,28 @@ export class BakeRunner {
|
|
|
36
59
|
await this.runOne(claimed);
|
|
37
60
|
}
|
|
38
61
|
catch (err) {
|
|
62
|
+
// A bake that threw out of runOne (an unexpected host/API failure) โ best-effort terminal so readers settle.
|
|
63
|
+
// The lineOrd here is unknown (runOne owns the counter), so use a NULL-equivalent high ordinal that won't
|
|
64
|
+
// collide with an in-flight frame's uq_line (image-api still ++seq's it; the reaper backstops a true miss).
|
|
39
65
|
this.o.log.error("bake_run_error", { bakeId: claimed.bakeId, err: String(err) });
|
|
40
66
|
await this.safeIngest(claimed, exitDoneFrame(TERMINAL_FALLBACK_ORD, 1, String(err)));
|
|
41
67
|
}
|
|
42
68
|
}
|
|
43
69
|
}
|
|
70
|
+
/** Run a single CLAIMED bake end-to-end. Owns the lineOrd counter, the heartbeat/cancel/deadline timers, the
|
|
71
|
+
* child process group, the stdoutโingest pump, and the terminal done synthesis. */
|
|
44
72
|
async runOne(bake) {
|
|
45
|
-
const ord = { n: 0 };
|
|
73
|
+
const ord = { n: 0 }; // the monotonic lineOrd allocator (at-least-once dedupe key โ ยงP2.10)
|
|
46
74
|
this.o.log.info("bake_claimed", { bakeId: bake.bakeId, push: bake.push, dryRun: bake.dryRun, logs: bake.logs });
|
|
75
|
+
// M2: serialize the stdoutโingest pump. A burst chunk (bufferLines calls onStdoutLine per newline synchronously)
|
|
76
|
+
// + the progress tick were fired as un-awaited `void safeIngest`, so N POSTs raced concurrently โ image-api's
|
|
77
|
+
// non-atomic seq alloc then lost a frame on a (bake_id,seq) PK collision. Chain each non-terminal ingest so they
|
|
78
|
+
// POST one-at-a-time in lineOrd order, and AWAIT the chain before runOne returns (alongside the done forward).
|
|
47
79
|
let pump = Promise.resolve();
|
|
48
80
|
const enqueueIngest = (frame) => {
|
|
49
81
|
pump = pump.then(() => this.safeIngest(bake, frame));
|
|
50
82
|
};
|
|
83
|
+
// โโ Pre-flight disk guard (ยงP2.7): below the floor, fail FAST rather than dying mid-build with a half-layer.
|
|
51
84
|
if (!bake.dryRun) {
|
|
52
85
|
const freeGb = await this.freeGbOrNull();
|
|
53
86
|
if (freeGb !== null && freeGb < this.minFreeGb) {
|
|
@@ -63,6 +96,7 @@ export class BakeRunner {
|
|
|
63
96
|
return;
|
|
64
97
|
}
|
|
65
98
|
}
|
|
99
|
+
// โโ Recipe checkout at the pinned ref so build.sh's git rev-parse gitsha is REAL (ยงP2.12).
|
|
66
100
|
let cwd;
|
|
67
101
|
try {
|
|
68
102
|
cwd = await this.o.host.checkoutRecipe(this.o.recipeRef);
|
|
@@ -71,16 +105,24 @@ export class BakeRunner {
|
|
|
71
105
|
await this.safeIngest(bake, exitDoneFrame(nextOrd(ord), 1, `recipe checkout failed: ${String(err)}`));
|
|
72
106
|
return;
|
|
73
107
|
}
|
|
108
|
+
// โโ Spawn build.sh in its OWN PROCESS GROUP (so a cancel/deadline kill signals the whole detached tree).
|
|
74
109
|
const stderrTail = [];
|
|
75
110
|
let lastManifestPath = null;
|
|
76
|
-
let lastImageTag = null;
|
|
111
|
+
let lastImageTag = null; // the built tag โ the push-retry target (ยงP2.12 big-blob retry)
|
|
77
112
|
let sawDone = false;
|
|
113
|
+
// build.sh's `done` triggers an ASYNC tail (read the manifest body / the push retry, then ingest) โ the child
|
|
114
|
+
// can exit the instant after printing it. It is chained ONTO `pump` (below), so `await pump` before runOne
|
|
115
|
+
// returns also awaits this pending forward (the lease isn't released before the auto-register input lands).
|
|
116
|
+
// ๐ด Resolve buildShPath against the recipe CHECKOUT cwd, NOT via PATH: node/bun's spawn resolves a BARE or
|
|
117
|
+
// RELATIVE argv[0] through $PATH (a bare `build.sh` silently ENOENTโexit-127 footgun โ cost an hour live), so
|
|
118
|
+
// path.resolve(cwd, buildShPath) pins it to the checkout. An absolute buildShPath is returned unchanged.
|
|
78
119
|
const buildShAbs = resolvePath(cwd, this.o.buildShPath);
|
|
79
120
|
const buildArgv = [buildShAbs, ...bake.argv, "--json"];
|
|
80
121
|
const child = this.o.spawner.spawn({
|
|
81
122
|
argv: buildArgv,
|
|
82
123
|
cwd,
|
|
83
124
|
onStdoutLine: (raw) => {
|
|
125
|
+
// Forward EVERY stdout line through the pure mapper; remember the manifest path for the auto-register read.
|
|
84
126
|
const ev = parseBuildShLine(raw);
|
|
85
127
|
if (ev && ev.event === "manifest" && typeof ev.path === "string") {
|
|
86
128
|
lastManifestPath = ev.path;
|
|
@@ -90,39 +132,47 @@ export class BakeRunner {
|
|
|
90
132
|
}
|
|
91
133
|
if (ev && ev.event === "done") {
|
|
92
134
|
sawDone = true;
|
|
135
|
+
// The done line needs the manifest BODY attached before ingest (auto-register reads it off disk โ ยงP2.10).
|
|
136
|
+
// M2: chain it ON the serialized pump so the terminal `done` is forwarded STRICTLY AFTER every queued
|
|
137
|
+
// non-terminal frame (the SSE log's last event), in lineOrd order โ never racing ahead of them.
|
|
93
138
|
pump = pump.then(() => this.forwardDone(bake, ord, ev, lastManifestPath, lastImageTag));
|
|
94
139
|
return;
|
|
95
140
|
}
|
|
96
141
|
enqueueIngest(lineToFrame(raw, nextOrd(ord)));
|
|
97
142
|
},
|
|
143
|
+
// stderr is the failure tail (`tail -40 build.log >&2`), NOT JSON โ buffer it, surface on a non-zero exit.
|
|
98
144
|
onStderrLine: (raw) => {
|
|
99
145
|
if (stderrTail.length < 80)
|
|
100
146
|
stderrTail.push(raw);
|
|
101
147
|
},
|
|
102
148
|
});
|
|
149
|
+
// โโ The heartbeat / cancel-poll / docker-build-progress / deadline supervisor, racing the child exit.
|
|
103
150
|
const deadlineMs = deadlineMsForProfile(bake.profile ?? "custom", bake.bands ?? null);
|
|
104
151
|
const startedAt = this.o.clock.now();
|
|
105
152
|
let killedFor = null;
|
|
106
|
-
let childExited = false;
|
|
153
|
+
let childExited = false; // set when the child resolves so the supervisor's next tick stops (no leaked heartbeats)
|
|
107
154
|
const supervisor = (async () => {
|
|
155
|
+
// eslint-disable-next-line no-constant-condition
|
|
108
156
|
while (true) {
|
|
109
157
|
await this.o.clock.sleep(this.heartbeatMs);
|
|
110
158
|
if (childExited || killedFor)
|
|
111
|
-
return;
|
|
159
|
+
return; // the child finished, or we already killed โ stop heartbeating
|
|
112
160
|
const elapsed = this.o.clock.now() - startedAt;
|
|
161
|
+
// Deadline first (a hung build past its profile budget โ kill the group, synthesize FAILED 124).
|
|
113
162
|
if (elapsed >= deadlineMs) {
|
|
114
163
|
killedFor = "timeout";
|
|
115
164
|
this.o.log.warn("bake_deadline_kill", { bakeId: bake.bakeId, elapsedMs: elapsed, deadlineMs });
|
|
116
165
|
this.killGroup(child);
|
|
117
166
|
return;
|
|
118
167
|
}
|
|
168
|
+
// Heartbeat the lease + read back the cancel flag (the durable cross-replica cancel โ ยงP2.11).
|
|
119
169
|
let ack;
|
|
120
170
|
try {
|
|
121
171
|
ack = await this.o.api.heartbeat(bake.bakeId, bake.ingestSecret);
|
|
122
172
|
}
|
|
123
173
|
catch (err) {
|
|
124
174
|
this.o.log.warn("bake_heartbeat_error", { bakeId: bake.bakeId, err: String(err) });
|
|
125
|
-
continue;
|
|
175
|
+
continue; // a transient heartbeat miss is not fatal; the reaper backstops a truly-lost runner.
|
|
126
176
|
}
|
|
127
177
|
if (ack.cancelRequested) {
|
|
128
178
|
killedFor = "cancelled";
|
|
@@ -131,30 +181,50 @@ export class BakeRunner {
|
|
|
131
181
|
return;
|
|
132
182
|
}
|
|
133
183
|
if (ack.leaseValid === false) {
|
|
184
|
+
// The lease was stolen (image-api reaped us as stale) โ abort; we no longer own this build.
|
|
134
185
|
killedFor = "timeout";
|
|
135
186
|
this.o.log.warn("bake_lease_lost", { bakeId: bake.bakeId });
|
|
136
187
|
this.killGroup(child);
|
|
137
188
|
return;
|
|
138
189
|
}
|
|
190
|
+
// No cancel, no deadline: emit the docker-build progress tick so a resuming reader sees motion (logs off).
|
|
139
191
|
enqueueIngest(dockerBuildTickFrame(nextOrd(ord), Math.floor(elapsed / 1000)));
|
|
140
192
|
}
|
|
141
193
|
})();
|
|
142
194
|
const exit = await child.exited;
|
|
143
195
|
childExited = true;
|
|
144
196
|
this.stopSupervisor(supervisor);
|
|
197
|
+
// M2: drain the serialized pump BEFORE synthesizing a terminal. The pump carries every non-terminal frame AND
|
|
198
|
+
// (when build.sh printed its own done) the chained `forwardDone` โ so by here the real `done` has already been
|
|
199
|
+
// forwarded LAST in lineOrd order. Draining first guarantees the lease isn't released before the auto-register
|
|
200
|
+
// input (manifest body) is in image-api's hands.
|
|
145
201
|
await pump.catch(() => undefined);
|
|
202
|
+
// โโ Terminal done synthesis. build.sh has NO SIGTERM trap, so a killed child emits no done โ we synthesize it.
|
|
146
203
|
if (killedFor) {
|
|
147
204
|
await this.safeIngest(bake, synthDoneFrame(nextOrd(ord), killedFor));
|
|
148
205
|
return;
|
|
149
206
|
}
|
|
150
207
|
if (sawDone) {
|
|
208
|
+
// build.sh printed its own done โ already forwarded (manifest-body read / push retry) via the drained pump.
|
|
151
209
|
return;
|
|
152
210
|
}
|
|
211
|
+
// The child exited WITHOUT a done line (a crash/SIGKILL before build.sh's die() trap) โ backstop a FAILED done
|
|
212
|
+
// carrying the exit code + the stderr failure tail (image-api redacts it on store).
|
|
153
213
|
const code = exit.code ?? (exit.signal ? 137 : 1);
|
|
154
214
|
await this.safeIngest(bake, exitDoneFrame(nextOrd(ord), code, stderrTail.join("\n")));
|
|
155
215
|
}
|
|
216
|
+
/** Forward build.sh's own `done` line โ but FIRST read the manifest BODY off disk and attach it so image-api can
|
|
217
|
+
* auto-register (build.sh's done carries only a build-host path, NOT the body โ ยงP2.10). A manifest read failure is
|
|
218
|
+
* non-fatal: the done still forwards (the digest is on the line); image-api then registers indexId:null partial.
|
|
219
|
+
*
|
|
220
|
+
* BIG-BLOB PUSH RETRY (ยงP2.12): build.sh dies exit 13 on a flaky `docker push`. The retry is a THIN `docker push`
|
|
221
|
+
* wrapper on the host (NOT build.sh --replay โ that re-bakes + hits the dup-check exit 10). On a push-fail done
|
|
222
|
+
* for a `--push` bake with a known tag, the runner retries the push; on success it rewrites the done to COMPLETE
|
|
223
|
+
* with the recovered post-push digest (no manifest on disk โ build.sh dies before the manifest write โ so image-
|
|
224
|
+
* api registers the built-and-pushed-but-unregistered partial, which the P3 reconciliation tick finalizes). */
|
|
156
225
|
async forwardDone(bake, ord, doneLine, lastManifestPath, lastImageTag) {
|
|
157
226
|
let line = doneLine;
|
|
227
|
+
// Push-fail (exit 13) recovery: retry the big-blob push out-of-band before declaring the bake failed.
|
|
158
228
|
if (line.status === "FAILED" &&
|
|
159
229
|
line.exitCode === 13 &&
|
|
160
230
|
bake.push &&
|
|
@@ -164,6 +234,7 @@ export class BakeRunner {
|
|
|
164
234
|
try {
|
|
165
235
|
const r = await this.o.host.dockerPushRetry(lastImageTag, 3);
|
|
166
236
|
if (r.ok) {
|
|
237
|
+
// Recovered: rewrite the terminal to COMPLETE with the post-push RepoDigest (design ยง6 runtime ref).
|
|
167
238
|
line = { ...line, status: "COMPLETE", exitCode: 0, error: null, digest: r.digest ?? line.digest, pushed: true };
|
|
168
239
|
this.o.log.info("bake_push_recovered", { bakeId: bake.bakeId, tag: lastImageTag });
|
|
169
240
|
}
|
|
@@ -181,28 +252,36 @@ export class BakeRunner {
|
|
|
181
252
|
}
|
|
182
253
|
catch (err) {
|
|
183
254
|
this.o.log.warn("bake_manifest_read_failed", { bakeId: bake.bakeId, path, err: String(err) });
|
|
255
|
+
// leave manifestBody unset โ image-api registers the built-but-unregistered partial (digest, indexId:null).
|
|
184
256
|
}
|
|
185
257
|
}
|
|
186
258
|
}
|
|
187
259
|
await this.safeIngest(bake, frame);
|
|
188
260
|
}
|
|
261
|
+
/** Kill the process GROUP: SIGTERM, then SIGKILL shortly after (a SIGKILL mid-docker-build can leave dangling
|
|
262
|
+
* layers; the dup-check + lockhash prevent a poisoned re-bake, and a startup workdir sweep reclaims them โ ยงP2.11). */
|
|
189
263
|
killGroup(child) {
|
|
190
264
|
try {
|
|
191
265
|
child.killGroup("SIGTERM");
|
|
192
266
|
}
|
|
193
267
|
catch {
|
|
268
|
+
/* the group may already be gone */
|
|
194
269
|
}
|
|
195
270
|
void this.o.clock.sleep(5_000).then(() => {
|
|
196
271
|
try {
|
|
197
272
|
child.killGroup("SIGKILL");
|
|
198
273
|
}
|
|
199
274
|
catch {
|
|
275
|
+
/* already dead */
|
|
200
276
|
}
|
|
201
277
|
});
|
|
202
278
|
}
|
|
203
279
|
stopSupervisor(p) {
|
|
280
|
+
// The supervisor loop exits on its own once killedFor is set or the child exits; this just swallows its result.
|
|
204
281
|
void p.catch(() => undefined);
|
|
205
282
|
}
|
|
283
|
+
/** Pre-flight free-GiB read; null (fail-open) on a df parse/exec miss โ a df hiccup must not block a bake (the
|
|
284
|
+
* reaper + a mid-build fill guard still protect the host). */
|
|
206
285
|
async freeGbOrNull() {
|
|
207
286
|
try {
|
|
208
287
|
return parseDfFreeGb(await this.o.host.dfData());
|
|
@@ -211,6 +290,8 @@ export class BakeRunner {
|
|
|
211
290
|
return null;
|
|
212
291
|
}
|
|
213
292
|
}
|
|
293
|
+
/** Ingest one frame, swallowing transient errors (the buffered-retry of unsent lines is the real-runner's job;
|
|
294
|
+
* image-api's uq_line makes a re-send a no-op, so a dropped frame is recoverable, never a crash). */
|
|
214
295
|
async safeIngest(bake, frame) {
|
|
215
296
|
try {
|
|
216
297
|
return await this.o.api.ingest(bake.bakeId, bake.ingestSecret, frame);
|
|
@@ -221,6 +302,7 @@ export class BakeRunner {
|
|
|
221
302
|
}
|
|
222
303
|
}
|
|
223
304
|
}
|
|
305
|
+
/** Allocate the next per-bake lineOrd (monotonic from 1) โ the at-least-once ingest dedupe key (ยงP2.10). */
|
|
224
306
|
export function nextOrd(ord) {
|
|
225
307
|
ord.n += 1;
|
|
226
308
|
return ord.n;
|
|
@@ -1,38 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L8 G-c artifact โ the signed deploy-authenticity evidence the design/93 ยง5.2 G-c aggregator requires
|
|
3
|
+
* (SPEC-RSI-L7-L8 ยงA.3). The artifact is canonical-JSON serialized and ed25519-signed so the GO aggregator can
|
|
4
|
+
* verify it was produced by a real L8 run and link it to a commit + adapter build + boundary.
|
|
5
|
+
*
|
|
6
|
+
* ๐ด SIGNING IS PROVENANCE, NOT AUTHENTICITY-OF-RESULT (SPEC ยงA.3 RISK-4): a valid signature only proves "this
|
|
7
|
+
* run, with this key, produced these probe VALUES"; it does NOT prove the probes hit a real boundary โ that is
|
|
8
|
+
* the P0 fail-loud + the external collector's job. The aggregator MUST check `gC.satisfied` AND
|
|
9
|
+
* `boundary.isolationClassVerified` AND `p1Mode !== "adapter-RO-seam-absent"`, never just "signature valid".
|
|
10
|
+
*
|
|
11
|
+
* ๐ด ED25519, NOT HMAC: a shared HMAC secret is forgeable by anyone who holds it; ed25519 is asymmetric
|
|
12
|
+
* โ the private key signs (control-plane held, env L8_SIGNER_KEY_B64), the PUBLIC key (shipped to the aggregator)
|
|
13
|
+
* only verifies. node:crypto's ed25519 is built-in (no new dep).
|
|
14
|
+
*
|
|
15
|
+
* ๐ด TIMESTAMPS ARE INJECTED, NOT `Date.now()` (deterministic-test requirement): `buildArtifact` takes
|
|
16
|
+
* `producedAt` from the caller (the live runner passes a real ISO string; the unit test passes a fixed one), so
|
|
17
|
+
* the canonical JSON is byte-stable in tests.
|
|
18
|
+
*/
|
|
1
19
|
import { type KeyObject } from "node:crypto";
|
|
2
20
|
import { type L8SchemaVersion, type L8Escape } from "./escape.js";
|
|
3
21
|
import type { Backend, P1Mode } from "./probes.js";
|
|
4
22
|
export interface L8Boundary {
|
|
5
23
|
provisioned: boolean;
|
|
24
|
+
/** What the adapter SELF-DECLARES (capabilities.isolation) โ recorded but never load-bearing. */
|
|
6
25
|
isolationClassClaimed: "out_of_process" | "in_process_probe_only";
|
|
26
|
+
/** What L8 PROVED (P0+P2) โ the load-bearing field. */
|
|
7
27
|
isolationClassVerified: boolean;
|
|
28
|
+
/** kata only; null+reason on e2b (RISK-7). */
|
|
8
29
|
guestKernel: string | null;
|
|
9
30
|
hostKernel: string | null;
|
|
10
31
|
runtimeClass: string;
|
|
32
|
+
/** P1 sanity: escalation failed because CAP_SYS_ADMIN is gone, not because we silently had it. */
|
|
11
33
|
capSysAdminAbsent: boolean;
|
|
12
34
|
}
|
|
13
35
|
export interface ProbeRecord {
|
|
14
36
|
id: string;
|
|
15
37
|
name: string;
|
|
38
|
+
/** The verdict for this probe. `null` โ the probe is in an honest PENDING state (blocked on a cross-team dep). */
|
|
16
39
|
passed: boolean | null;
|
|
40
|
+
/** Honest pending flag โ set when the probe could not run for real (the blocker is in `pendingReason`). */
|
|
17
41
|
pending: boolean;
|
|
18
42
|
pendingReason?: string;
|
|
43
|
+
/** P1-specific honest mode (only on the immutable-mount probe). */
|
|
19
44
|
p1Mode?: P1Mode;
|
|
45
|
+
/** Per-probe structured observation fields (kept open so each probe records what it asserts). */
|
|
20
46
|
observations?: Record<string, unknown>;
|
|
47
|
+
/** The command(s) the probe issued (for audit; the live runner fills these). */
|
|
21
48
|
commands?: string[];
|
|
49
|
+
/** The INDEPENDENT observer's result for this probe (e.g. collector nonce log / inode check). */
|
|
22
50
|
independentObserver?: Record<string, unknown>;
|
|
23
51
|
}
|
|
24
52
|
export interface L8GcResult {
|
|
53
|
+
/** G-c = no escapes โง isolationClassVerified โง every probe passed (none pending/failed) โง p1Mode present. */
|
|
25
54
|
satisfied: boolean;
|
|
26
55
|
reason: string;
|
|
27
56
|
}
|
|
28
57
|
export interface L8Signature {
|
|
29
58
|
alg: "ed25519";
|
|
30
59
|
keyId: string;
|
|
60
|
+
/** base64 detached signature over the canonical JSON of the artifact MINUS the `signature` field. */
|
|
31
61
|
sig: string;
|
|
32
62
|
}
|
|
63
|
+
/** The l8.v1 artifact. `signature` is absent until {@link signArtifact}. */
|
|
33
64
|
export interface L8DeployContractArtifact {
|
|
34
65
|
schemaVersion: L8SchemaVersion;
|
|
35
66
|
kind: "deploy-contract-authenticity";
|
|
67
|
+
/** Injected ISO timestamp โ NOT Date.now() (deterministic-test requirement). */
|
|
36
68
|
producedAt: string;
|
|
37
69
|
serviceCommit: string;
|
|
38
70
|
coreVersion: string;
|
|
@@ -41,6 +73,7 @@ export interface L8DeployContractArtifact {
|
|
|
41
73
|
probes: ProbeRecord[];
|
|
42
74
|
escapes: L8Escape[];
|
|
43
75
|
gC: L8GcResult;
|
|
76
|
+
/** Content hash linking the artifact body (everything except `signature`) โ the thing the signature covers. */
|
|
44
77
|
contentHash: string;
|
|
45
78
|
signature?: L8Signature;
|
|
46
79
|
}
|
|
@@ -51,12 +84,42 @@ export interface BuildArtifactMeta {
|
|
|
51
84
|
backend: Backend;
|
|
52
85
|
boundary: L8Boundary;
|
|
53
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Canonical JSON: object keys sorted recursively, no insignificant whitespace, arrays preserve order. Stable
|
|
89
|
+
* across re-serialization so the signature/contentHash are deterministic. Rejects non-finite numbers (NaN/Inf
|
|
90
|
+
* would serialize as `null` and silently corrupt the canonical form).
|
|
91
|
+
*/
|
|
54
92
|
export declare function canonicalJson(value: unknown): string;
|
|
93
|
+
/** sha256 of the canonical body โ links the signature to the exact body bytes. */
|
|
55
94
|
export declare function computeContentHash(a: L8DeployContractArtifact): string;
|
|
95
|
+
/**
|
|
96
|
+
* Fold G-c (SPEC ยงA.3): satisfied iff
|
|
97
|
+
* - no escapes, AND
|
|
98
|
+
* - boundary.provisioned AND boundary.isolationClassVerified, AND
|
|
99
|
+
* - every probe passed (NONE pending, NONE failed), AND
|
|
100
|
+
* - the immutable-mount probe's p1Mode is NOT "adapter-RO-seam-absent".
|
|
101
|
+
* G-c is necessary-non-sufficient: any pending/failed probe โ NO-GO. This is what keeps tier-ON NO-GO honest.
|
|
102
|
+
*/
|
|
56
103
|
export declare function foldGc(boundary: L8Boundary, probes: ProbeRecord[], escapes: L8Escape[]): L8GcResult;
|
|
104
|
+
/** Build the artifact body + contentHash. Pure: timestamps injected, gC folded. No signature yet. */
|
|
57
105
|
export declare function buildArtifact(meta: BuildArtifactMeta, probes: ProbeRecord[], escapes: L8Escape[]): L8DeployContractArtifact;
|
|
106
|
+
/** Load an ed25519 private key from a base64 PKCS8 DER (the L8_SIGNER_KEY_B64 control-plane secret). */
|
|
58
107
|
export declare function loadPrivateKeyB64(b64: string): KeyObject;
|
|
108
|
+
/** Load an ed25519 public key from a base64 SPKI DER (the key shipped to the aggregator). */
|
|
59
109
|
export declare function loadPublicKeyB64(b64: string): KeyObject;
|
|
110
|
+
/**
|
|
111
|
+
* Sign the artifact: recompute the contentHash (defends against a body mutated after build), sign the canonical
|
|
112
|
+
* body with ed25519 (`sign(null, msg, key)` โ ed25519 takes no separate digest algorithm), attach the detached
|
|
113
|
+
* base64 signature. Returns a NEW artifact (does not mutate the input).
|
|
114
|
+
*/
|
|
60
115
|
export declare function signArtifact(artifact: L8DeployContractArtifact, privateKey: KeyObject, keyId: string): L8DeployContractArtifact;
|
|
116
|
+
/**
|
|
117
|
+
* Verify an artifact: recompute the contentHash over the body and check it matches the stored one (tamper of any
|
|
118
|
+
* body field flips the hash), then ed25519-verify the detached signature over the canonical body. Returns false
|
|
119
|
+
* (never throws) on a missing/garbage signature, a content-hash mismatch, or a bad signature.
|
|
120
|
+
*
|
|
121
|
+
* ๐ด A `true` here means INTEGRITY/PROVENANCE only โ the caller MUST still gate on gC.satisfied AND
|
|
122
|
+
* boundary.isolationClassVerified AND p1Mode (see file header). Verification alone is NOT a GO.
|
|
123
|
+
*/
|
|
61
124
|
export declare function verifyArtifact(artifact: L8DeployContractArtifact, publicKey: KeyObject): boolean;
|
|
62
125
|
//# sourceMappingURL=artifact.d.ts.map
|
|
@@ -1,5 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L8 G-c artifact โ the signed deploy-authenticity evidence the design/93 ยง5.2 G-c aggregator requires
|
|
3
|
+
* (SPEC-RSI-L7-L8 ยงA.3). The artifact is canonical-JSON serialized and ed25519-signed so the GO aggregator can
|
|
4
|
+
* verify it was produced by a real L8 run and link it to a commit + adapter build + boundary.
|
|
5
|
+
*
|
|
6
|
+
* ๐ด SIGNING IS PROVENANCE, NOT AUTHENTICITY-OF-RESULT (SPEC ยงA.3 RISK-4): a valid signature only proves "this
|
|
7
|
+
* run, with this key, produced these probe VALUES"; it does NOT prove the probes hit a real boundary โ that is
|
|
8
|
+
* the P0 fail-loud + the external collector's job. The aggregator MUST check `gC.satisfied` AND
|
|
9
|
+
* `boundary.isolationClassVerified` AND `p1Mode !== "adapter-RO-seam-absent"`, never just "signature valid".
|
|
10
|
+
*
|
|
11
|
+
* ๐ด ED25519, NOT HMAC: a shared HMAC secret is forgeable by anyone who holds it; ed25519 is asymmetric
|
|
12
|
+
* โ the private key signs (control-plane held, env L8_SIGNER_KEY_B64), the PUBLIC key (shipped to the aggregator)
|
|
13
|
+
* only verifies. node:crypto's ed25519 is built-in (no new dep).
|
|
14
|
+
*
|
|
15
|
+
* ๐ด TIMESTAMPS ARE INJECTED, NOT `Date.now()` (deterministic-test requirement): `buildArtifact` takes
|
|
16
|
+
* `producedAt` from the caller (the live runner passes a real ISO string; the unit test passes a fixed one), so
|
|
17
|
+
* the canonical JSON is byte-stable in tests.
|
|
18
|
+
*/
|
|
1
19
|
import { createPrivateKey, createPublicKey, sign as cryptoSign, verify as cryptoVerify } from "node:crypto";
|
|
2
20
|
import { L8_SCHEMA_VERSION } from "./escape.js";
|
|
21
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ canonical JSON โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
22
|
+
/**
|
|
23
|
+
* Canonical JSON: object keys sorted recursively, no insignificant whitespace, arrays preserve order. Stable
|
|
24
|
+
* across re-serialization so the signature/contentHash are deterministic. Rejects non-finite numbers (NaN/Inf
|
|
25
|
+
* would serialize as `null` and silently corrupt the canonical form).
|
|
26
|
+
*/
|
|
3
27
|
export function canonicalJson(value) {
|
|
4
28
|
return serialize(value);
|
|
5
29
|
}
|
|
@@ -21,7 +45,7 @@ function serialize(v) {
|
|
|
21
45
|
if (t === "object") {
|
|
22
46
|
const obj = v;
|
|
23
47
|
const keys = Object.keys(obj)
|
|
24
|
-
.filter((k) => obj[k] !== undefined)
|
|
48
|
+
.filter((k) => obj[k] !== undefined) // omit undefined fields (matches JSON.stringify object semantics)
|
|
25
49
|
.sort();
|
|
26
50
|
return `{${keys.map((k) => `${JSON.stringify(k)}:${serialize(obj[k])}`).join(",")}}`;
|
|
27
51
|
}
|
|
@@ -29,14 +53,26 @@ function serialize(v) {
|
|
|
29
53
|
throw new Error("canonicalJson: bigint is not serializable");
|
|
30
54
|
throw new Error(`canonicalJson: unsupported type ${t}`);
|
|
31
55
|
}
|
|
56
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ content hash โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
32
57
|
import { createHash } from "node:crypto";
|
|
58
|
+
/** The body that the signature + contentHash cover = the artifact WITHOUT `signature` and WITHOUT `contentHash`. */
|
|
33
59
|
function signableBody(a) {
|
|
34
60
|
const { signature: _sig, contentHash: _ch, ...rest } = a;
|
|
35
61
|
return rest;
|
|
36
62
|
}
|
|
63
|
+
/** sha256 of the canonical body โ links the signature to the exact body bytes. */
|
|
37
64
|
export function computeContentHash(a) {
|
|
38
65
|
return createHash("sha256").update(canonicalJson(signableBody(a)), "utf8").digest("hex");
|
|
39
66
|
}
|
|
67
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ gC fold (pure) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
68
|
+
/**
|
|
69
|
+
* Fold G-c (SPEC ยงA.3): satisfied iff
|
|
70
|
+
* - no escapes, AND
|
|
71
|
+
* - boundary.provisioned AND boundary.isolationClassVerified, AND
|
|
72
|
+
* - every probe passed (NONE pending, NONE failed), AND
|
|
73
|
+
* - the immutable-mount probe's p1Mode is NOT "adapter-RO-seam-absent".
|
|
74
|
+
* G-c is necessary-non-sufficient: any pending/failed probe โ NO-GO. This is what keeps tier-ON NO-GO honest.
|
|
75
|
+
*/
|
|
40
76
|
export function foldGc(boundary, probes, escapes) {
|
|
41
77
|
const reasons = [];
|
|
42
78
|
if (escapes.length > 0)
|
|
@@ -45,6 +81,9 @@ export function foldGc(boundary, probes, escapes) {
|
|
|
45
81
|
reasons.push("boundary not provisioned");
|
|
46
82
|
if (!boundary.isolationClassVerified)
|
|
47
83
|
reasons.push("isolationClass not verified (P0/P2 โ possible runc downgrade)");
|
|
84
|
+
// ๐ด (review HIGH) require the FULL canonical probe set PRESENT before satisfied can be true โ else an empty or
|
|
85
|
+
// partial probe array silently satisfies G-c (a GO with NO evidence; foldGc(boundary,[],[]) used to return
|
|
86
|
+
// satisfied:true). Each required id must appear exactly once. Mirrors PROBE_MATRIX ids (P0..P4).
|
|
48
87
|
const REQUIRED_PROBE_IDS = ["P0", "P1", "P2", "P3", "P4"];
|
|
49
88
|
const idCounts = new Map();
|
|
50
89
|
for (const p of probes)
|
|
@@ -61,6 +100,8 @@ export function foldGc(boundary, probes, escapes) {
|
|
|
61
100
|
const failedProbes = probes.filter((p) => !p.pending && p.passed !== true);
|
|
62
101
|
if (failedProbes.length > 0)
|
|
63
102
|
reasons.push(`failed/unproven probe(s): ${failedProbes.map((p) => p.id).join(", ")}`);
|
|
103
|
+
// wq64gmm5e: key on the constrained `id` (every other gate above keys on id) โ a free-form `name` drift must
|
|
104
|
+
// never fail this seam-absent guard OPEN into a fake-green GO.
|
|
64
105
|
const p1 = probes.find((p) => p.id === "P1");
|
|
65
106
|
if (p1 && p1.p1Mode === "adapter-RO-seam-absent") {
|
|
66
107
|
reasons.push("P1 ran in adapter-RO-seam-absent mode (necessary-non-sufficient for the grader contract)");
|
|
@@ -73,6 +114,8 @@ export function foldGc(boundary, probes, escapes) {
|
|
|
73
114
|
: `G-c NOT satisfied: ${reasons.join(" | ")}`,
|
|
74
115
|
};
|
|
75
116
|
}
|
|
117
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ build (pure) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
118
|
+
/** Build the artifact body + contentHash. Pure: timestamps injected, gC folded. No signature yet. */
|
|
76
119
|
export function buildArtifact(meta, probes, escapes) {
|
|
77
120
|
const gC = foldGc(meta.boundary, probes, escapes);
|
|
78
121
|
const draft = {
|
|
@@ -86,17 +129,25 @@ export function buildArtifact(meta, probes, escapes) {
|
|
|
86
129
|
probes,
|
|
87
130
|
escapes,
|
|
88
131
|
gC,
|
|
89
|
-
contentHash: "",
|
|
132
|
+
contentHash: "", // filled below
|
|
90
133
|
};
|
|
91
134
|
draft.contentHash = computeContentHash(draft);
|
|
92
135
|
return draft;
|
|
93
136
|
}
|
|
137
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ ed25519 sign / verify โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
138
|
+
/** Load an ed25519 private key from a base64 PKCS8 DER (the L8_SIGNER_KEY_B64 control-plane secret). */
|
|
94
139
|
export function loadPrivateKeyB64(b64) {
|
|
95
140
|
return createPrivateKey({ key: Buffer.from(b64, "base64"), format: "der", type: "pkcs8" });
|
|
96
141
|
}
|
|
142
|
+
/** Load an ed25519 public key from a base64 SPKI DER (the key shipped to the aggregator). */
|
|
97
143
|
export function loadPublicKeyB64(b64) {
|
|
98
144
|
return createPublicKey({ key: Buffer.from(b64, "base64"), format: "der", type: "spki" });
|
|
99
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Sign the artifact: recompute the contentHash (defends against a body mutated after build), sign the canonical
|
|
148
|
+
* body with ed25519 (`sign(null, msg, key)` โ ed25519 takes no separate digest algorithm), attach the detached
|
|
149
|
+
* base64 signature. Returns a NEW artifact (does not mutate the input).
|
|
150
|
+
*/
|
|
100
151
|
export function signArtifact(artifact, privateKey, keyId) {
|
|
101
152
|
const withHash = { ...artifact, signature: undefined, contentHash: "" };
|
|
102
153
|
withHash.contentHash = computeContentHash(withHash);
|
|
@@ -104,6 +155,14 @@ export function signArtifact(artifact, privateKey, keyId) {
|
|
|
104
155
|
const sig = cryptoSign(null, msg, privateKey);
|
|
105
156
|
return { ...withHash, signature: { alg: "ed25519", keyId, sig: sig.toString("base64") } };
|
|
106
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Verify an artifact: recompute the contentHash over the body and check it matches the stored one (tamper of any
|
|
160
|
+
* body field flips the hash), then ed25519-verify the detached signature over the canonical body. Returns false
|
|
161
|
+
* (never throws) on a missing/garbage signature, a content-hash mismatch, or a bad signature.
|
|
162
|
+
*
|
|
163
|
+
* ๐ด A `true` here means INTEGRITY/PROVENANCE only โ the caller MUST still gate on gC.satisfied AND
|
|
164
|
+
* boundary.isolationClassVerified AND p1Mode (see file header). Verification alone is NOT a GO.
|
|
165
|
+
*/
|
|
107
166
|
export function verifyArtifact(artifact, publicKey) {
|
|
108
167
|
const sig = artifact.signature;
|
|
109
168
|
if (!sig || sig.alg !== "ed25519" || typeof sig.sig !== "string")
|