@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.
Files changed (539) hide show
  1. package/dist/approval-hmac.d.ts +40 -0
  2. package/dist/approval-hmac.js +67 -0
  3. package/dist/approval.d.ts +31 -0
  4. package/dist/approval.js +72 -4
  5. package/dist/audit.d.ts +26 -0
  6. package/dist/audit.js +40 -1
  7. package/dist/auth-bridge.d.ts +10 -0
  8. package/dist/auth-bridge.js +47 -4
  9. package/dist/auth-keys.d.ts +19 -0
  10. package/dist/auth-keys.js +13 -0
  11. package/dist/bake-runner/main.d.ts +5 -0
  12. package/dist/bake-runner/main.js +43 -4
  13. package/dist/bake-runner/protocol.d.ts +57 -0
  14. package/dist/bake-runner/protocol.js +58 -3
  15. package/dist/bake-runner/runner.d.ts +54 -0
  16. package/dist/bake-runner/runner.js +87 -5
  17. package/dist/bench/l8/artifact.d.ts +63 -0
  18. package/dist/bench/l8/artifact.js +61 -2
  19. package/dist/bench/l8/escape.d.ts +36 -0
  20. package/dist/bench/l8/escape.js +25 -0
  21. package/dist/bench/l8/index.d.ts +14 -0
  22. package/dist/bench/l8/index.js +14 -0
  23. package/dist/bench/l8/probes.d.ts +74 -0
  24. package/dist/bench/l8/probes.js +60 -0
  25. package/dist/bench/l8/run-probes.d.ts +43 -0
  26. package/dist/bench/l8/run-probes.js +45 -2
  27. package/dist/bench/s1/arms.d.ts +126 -0
  28. package/dist/bench/s1/arms.js +188 -13
  29. package/dist/bench/s1/live-deps.d.ts +66 -0
  30. package/dist/bench/s1/live-deps.js +249 -11
  31. package/dist/bench/s1/oracle.d.ts +49 -0
  32. package/dist/bench/s1/oracle.js +36 -1
  33. package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
  34. package/dist/bench/s1/repair-oracle-adapter.js +30 -0
  35. package/dist/bench/s1/reviewer.d.ts +38 -0
  36. package/dist/bench/s1/reviewer.js +34 -0
  37. package/dist/bench/s1/row.d.ts +77 -0
  38. package/dist/bench/s1/row.js +62 -0
  39. package/dist/bench/s1/run-firm.d.ts +47 -0
  40. package/dist/bench/s1/run-firm.js +70 -4
  41. package/dist/bench/s1/runner-ctx.d.ts +71 -0
  42. package/dist/bench/s1/runner-ctx.js +36 -0
  43. package/dist/bench/s1/tasks.d.ts +108 -0
  44. package/dist/bench/s1/tasks.js +115 -0
  45. package/dist/boot-reclaim.d.ts +19 -0
  46. package/dist/boot-reclaim.js +20 -0
  47. package/dist/brain.d.ts +25 -0
  48. package/dist/brain.js +61 -1
  49. package/dist/budget.d.ts +66 -0
  50. package/dist/budget.js +128 -4
  51. package/dist/capabilities/builtin-tools.d.ts +3 -0
  52. package/dist/capabilities/builtin-tools.js +3 -0
  53. package/dist/capabilities/center-plugins.d.ts +17 -1
  54. package/dist/capabilities/center-plugins.js +45 -4
  55. package/dist/capabilities/center-prompts.d.ts +46 -0
  56. package/dist/capabilities/center-prompts.js +57 -3
  57. package/dist/capabilities/code-review-council.d.ts +28 -0
  58. package/dist/capabilities/code-review-council.js +40 -7
  59. package/dist/capabilities/collab-wire.d.ts +2 -0
  60. package/dist/capabilities/collab-workflows.d.ts +7 -0
  61. package/dist/capabilities/collab-workflows.js +44 -1
  62. package/dist/capabilities/oa-tools.d.ts +6 -0
  63. package/dist/capabilities/oa-tools.js +10 -0
  64. package/dist/capabilities/prompt.d.ts +14 -0
  65. package/dist/capabilities/prompt.js +14 -0
  66. package/dist/capabilities/prompts/code-review.d.ts +14 -0
  67. package/dist/capabilities/prompts/code-review.js +14 -0
  68. package/dist/capabilities/prompts/identity.d.ts +6 -0
  69. package/dist/capabilities/prompts/identity.js +6 -0
  70. package/dist/capabilities/prompts/team.d.ts +4 -0
  71. package/dist/capabilities/prompts/team.js +4 -0
  72. package/dist/capabilities/repo-tools.d.ts +11 -0
  73. package/dist/capabilities/repo-tools.js +8 -0
  74. package/dist/capabilities/sandbox-file-send.d.ts +95 -0
  75. package/dist/capabilities/sandbox-file-send.js +91 -3
  76. package/dist/capabilities/scenarios.d.ts +130 -0
  77. package/dist/capabilities/scenarios.js +180 -7
  78. package/dist/capabilities/select-environment-tool.d.ts +7 -0
  79. package/dist/capabilities/select-environment-tool.js +30 -0
  80. package/dist/capabilities/send-user-file-tool.d.ts +33 -0
  81. package/dist/capabilities/send-user-file-tool.js +65 -1
  82. package/dist/capabilities/skills.d.ts +29 -0
  83. package/dist/capabilities/skills.js +13 -2
  84. package/dist/capabilities/team.d.ts +22 -0
  85. package/dist/capabilities/team.js +22 -2
  86. package/dist/capabilities/tool-defer.d.ts +26 -0
  87. package/dist/capabilities/tool-defer.js +3 -0
  88. package/dist/config-center/apply-effective.d.ts +65 -0
  89. package/dist/config-center/apply-effective.js +204 -12
  90. package/dist/config-center/http-client.d.ts +34 -0
  91. package/dist/config-center/http-client.js +68 -0
  92. package/dist/config-center/restart-signal.d.ts +22 -0
  93. package/dist/config-center/restart-signal.js +39 -0
  94. package/dist/config-center/skills-mcp.d.ts +20 -1
  95. package/dist/config-center/skills-mcp.js +42 -7
  96. package/dist/config-center/types.d.ts +92 -1
  97. package/dist/config-lkg.d.ts +12 -0
  98. package/dist/config-lkg.js +41 -0
  99. package/dist/config-provider.d.ts +78 -0
  100. package/dist/config-provider.js +165 -2
  101. package/dist/config-types.d.ts +546 -0
  102. package/dist/config.d.ts +64 -0
  103. package/dist/config.js +455 -60
  104. package/dist/degenerate-instrument.d.ts +38 -0
  105. package/dist/degenerate-instrument.js +55 -3
  106. package/dist/elicitation.d.ts +39 -0
  107. package/dist/elicitation.js +87 -3
  108. package/dist/env-facts.d.ts +61 -0
  109. package/dist/env-facts.js +73 -7
  110. package/dist/fleet/fleet-bus.d.ts +185 -0
  111. package/dist/fleet/fleet-bus.js +333 -27
  112. package/dist/fleet/subagent-tail-bus.d.ts +11 -0
  113. package/dist/fleet/subagent-tail-bus.js +33 -2
  114. package/dist/fleet-client.d.ts +38 -0
  115. package/dist/fleet-client.js +44 -9
  116. package/dist/fleet-lease.d.ts +46 -0
  117. package/dist/fleet-lease.js +73 -5
  118. package/dist/hooks/branch-transcript.d.ts +8 -0
  119. package/dist/hooks/branch-transcript.js +33 -0
  120. package/dist/hooks/cc-stop-prompt.d.ts +42 -0
  121. package/dist/hooks/cc-stop-prompt.js +43 -1
  122. package/dist/hooks/hook-llm.d.ts +15 -0
  123. package/dist/hooks/hook-llm.js +47 -0
  124. package/dist/hooks/hook-runner.d.ts +89 -0
  125. package/dist/hooks/hook-runner.js +327 -18
  126. package/dist/http/idempotency.d.ts +31 -0
  127. package/dist/http/idempotency.js +34 -0
  128. package/dist/http/principal-gate.d.ts +17 -0
  129. package/dist/http/principal-gate.js +35 -3
  130. package/dist/http/route-ctx.d.ts +36 -0
  131. package/dist/http/routes/approvals-assistant.d.ts +25 -0
  132. package/dist/http/routes/approvals-assistant.js +253 -23
  133. package/dist/http/routes/attachments.js +22 -4
  134. package/dist/http/routes/capabilities.d.ts +7 -0
  135. package/dist/http/routes/capabilities.js +202 -1
  136. package/dist/http/routes/fleet.d.ts +6 -0
  137. package/dist/http/routes/fleet.js +94 -5
  138. package/dist/http/routes/images.d.ts +17 -0
  139. package/dist/http/routes/images.js +222 -14
  140. package/dist/http/routes/leader.d.ts +7 -0
  141. package/dist/http/routes/leader.js +10 -0
  142. package/dist/http/routes/memory-policy.d.ts +6 -0
  143. package/dist/http/routes/memory-policy.js +38 -6
  144. package/dist/http/routes/notify-wake.d.ts +10 -0
  145. package/dist/http/routes/notify-wake.js +19 -3
  146. package/dist/http/routes/observability.d.ts +6 -0
  147. package/dist/http/routes/observability.js +19 -2
  148. package/dist/http/routes/runs.d.ts +15 -0
  149. package/dist/http/routes/runs.js +464 -41
  150. package/dist/http/routes/session-sync.d.ts +15 -0
  151. package/dist/http/routes/session-sync.js +229 -19
  152. package/dist/http/routes/sessions-list.d.ts +6 -0
  153. package/dist/http/routes/sessions-list.js +35 -4
  154. package/dist/http/routes/sessions.d.ts +14 -0
  155. package/dist/http/routes/sessions.js +176 -31
  156. package/dist/http/routes/side-query.d.ts +8 -0
  157. package/dist/http/routes/side-query.js +26 -1
  158. package/dist/http/routes/tasks.d.ts +18 -0
  159. package/dist/http/routes/tasks.js +501 -38
  160. package/dist/http/routes/trace-usage.d.ts +10 -0
  161. package/dist/http/routes/trace-usage.js +66 -14
  162. package/dist/http/routes/workflows.d.ts +12 -0
  163. package/dist/http/routes/workflows.js +180 -19
  164. package/dist/http/run-meta.d.ts +11 -0
  165. package/dist/http/run-meta.js +6 -0
  166. package/dist/http/send.d.ts +19 -0
  167. package/dist/http/send.js +11 -0
  168. package/dist/http/server.d.ts +255 -0
  169. package/dist/http/server.js +921 -75
  170. package/dist/http/sse-log.d.ts +15 -0
  171. package/dist/http/sse-log.js +16 -0
  172. package/dist/http/tar.d.ts +9 -0
  173. package/dist/http/tar.js +21 -5
  174. package/dist/http/wire-gate.d.ts +9 -0
  175. package/dist/http/wire-gate.js +9 -0
  176. package/dist/http/wire-types.d.ts +187 -0
  177. package/dist/http/workspace-content.d.ts +8 -0
  178. package/dist/http/workspace-content.js +10 -0
  179. package/dist/images/bake-validate.d.ts +63 -0
  180. package/dist/images/bake-validate.js +71 -1
  181. package/dist/images/manifest.d.ts +19 -0
  182. package/dist/images/manifest.js +6 -1
  183. package/dist/index.d.ts +1 -5
  184. package/dist/index.js +10 -5
  185. package/dist/key-resolver.d.ts +23 -0
  186. package/dist/key-resolver.js +27 -2
  187. package/dist/leader/diffout.d.ts +23 -0
  188. package/dist/leader/diffout.js +8 -1
  189. package/dist/leader/diffup.d.ts +32 -0
  190. package/dist/leader/diffup.js +43 -0
  191. package/dist/leader/endpoint.d.ts +20 -0
  192. package/dist/leader/endpoint.js +25 -2
  193. package/dist/leader/fanout.d.ts +60 -0
  194. package/dist/leader/fanout.js +29 -3
  195. package/dist/leader/grader-env-factory.d.ts +74 -0
  196. package/dist/leader/grader-env-factory.js +62 -3
  197. package/dist/leader/leader.d.ts +97 -0
  198. package/dist/leader/leader.js +151 -4
  199. package/dist/leader/merge.d.ts +94 -0
  200. package/dist/leader/merge.js +112 -9
  201. package/dist/leader/planner.d.ts +52 -0
  202. package/dist/leader/planner.js +47 -3
  203. package/dist/leader/repair-oracle.d.ts +61 -0
  204. package/dist/leader/repair-oracle.js +59 -1
  205. package/dist/leader/repair-wire.d.ts +78 -0
  206. package/dist/leader/repair-wire.js +79 -2
  207. package/dist/leader/wire.d.ts +79 -0
  208. package/dist/leader/wire.js +255 -8
  209. package/dist/lsp/e2b-bridge.d.ts +16 -0
  210. package/dist/lsp/e2b-bridge.js +32 -4
  211. package/dist/lsp/e2b-manager.d.ts +10 -0
  212. package/dist/lsp/e2b-manager.js +18 -2
  213. package/dist/lsp/lsp-frames.d.ts +12 -0
  214. package/dist/lsp/lsp-frames.js +12 -0
  215. package/dist/lsp/manager.d.ts +12 -0
  216. package/dist/lsp/manager.js +35 -2
  217. package/dist/lsp/types.d.ts +10 -0
  218. package/dist/lsp/ws-transport.d.ts +12 -0
  219. package/dist/lsp/ws-transport.js +53 -5
  220. package/dist/lsp-evict.d.ts +14 -0
  221. package/dist/lsp-evict.js +15 -1
  222. package/dist/main.js +2132 -96
  223. package/dist/memory-export.d.ts +1 -0
  224. package/dist/memory-export.js +4 -0
  225. package/dist/memory-scope.d.ts +49 -0
  226. package/dist/memory-scope.js +87 -4
  227. package/dist/memory-sync-client.d.ts +24 -0
  228. package/dist/memory-sync-client.js +41 -2
  229. package/dist/memory-sync.d.ts +23 -0
  230. package/dist/memory-sync.js +70 -1
  231. package/dist/model-select.d.ts +39 -0
  232. package/dist/model-select.js +42 -1
  233. package/dist/observability/cost-quota.d.ts +20 -0
  234. package/dist/observability/cost-quota.js +4 -0
  235. package/dist/observability/cost-taxonomy.d.ts +45 -0
  236. package/dist/observability/cost-taxonomy.js +34 -0
  237. package/dist/observability/logger.d.ts +1 -0
  238. package/dist/observability/logger.js +6 -0
  239. package/dist/observability/metrics.d.ts +13 -0
  240. package/dist/observability/metrics.js +63 -0
  241. package/dist/observability/otel-exporter.d.ts +9 -0
  242. package/dist/observability/otel-exporter.js +11 -3
  243. package/dist/observability/principal-context.d.ts +9 -0
  244. package/dist/observability/principal-context.js +9 -0
  245. package/dist/observability/prompt-manifest.d.ts +37 -0
  246. package/dist/observability/prompt-manifest.js +35 -1
  247. package/dist/observability/rate-limit.d.ts +10 -0
  248. package/dist/observability/rate-limit.js +1 -0
  249. package/dist/observability/tool-trace.d.ts +24 -0
  250. package/dist/observability/tool-trace.js +41 -1
  251. package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
  252. package/dist/orchestration/hardened-vm-runner.js +119 -4
  253. package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
  254. package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
  255. package/dist/orchestration/hardened-vm-worker.js +27 -0
  256. package/dist/orchestration/subagent-steer.d.ts +44 -0
  257. package/dist/orchestration/subagent-steer.js +45 -1
  258. package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
  259. package/dist/orchestration/workflow-agent-steer.js +80 -1
  260. package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
  261. package/dist/orchestration/workflow-completion-inbox.js +283 -32
  262. package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
  263. package/dist/orchestration/workflow-notify-journal.js +187 -13
  264. package/dist/parked-decide.d.ts +61 -0
  265. package/dist/parked-decide.js +58 -1
  266. package/dist/per-task-image.d.ts +54 -0
  267. package/dist/per-task-image.js +41 -0
  268. package/dist/plan-cache-probe.d.ts +35 -0
  269. package/dist/plan-cache-probe.js +18 -3
  270. package/dist/plugins/approval-exemption-store.d.ts +7 -0
  271. package/dist/plugins/approval-exemption-store.js +28 -2
  272. package/dist/plugins/approval-store-sql.d.ts +64 -0
  273. package/dist/plugins/approval-store-sql.js +32 -0
  274. package/dist/plugins/background-agent-store-sql.d.ts +55 -0
  275. package/dist/plugins/background-agent-store-sql.js +91 -4
  276. package/dist/plugins/background-shell-support.d.ts +98 -0
  277. package/dist/plugins/background-shell-support.js +117 -13
  278. package/dist/plugins/blob-backend.d.ts +104 -0
  279. package/dist/plugins/blob-backend.js +163 -6
  280. package/dist/plugins/breaker-state-sql.d.ts +65 -1
  281. package/dist/plugins/breaker-state-sql.js +46 -8
  282. package/dist/plugins/caching-session-store.d.ts +42 -0
  283. package/dist/plugins/caching-session-store.js +63 -3
  284. package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
  285. package/dist/plugins/checkpoint-store-sql.js +239 -11
  286. package/dist/plugins/file-outcome-sink.d.ts +4 -0
  287. package/dist/plugins/file-outcome-sink.js +9 -0
  288. package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
  289. package/dist/plugins/file-resume-anchor-store.js +43 -4
  290. package/dist/plugins/file-run-store.d.ts +90 -0
  291. package/dist/plugins/file-run-store.js +145 -13
  292. package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
  293. package/dist/plugins/file-snapshot-store-sql.js +178 -10
  294. package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
  295. package/dist/plugins/file-workflow-journal-store.js +10 -0
  296. package/dist/plugins/fork-routing-session-store.d.ts +47 -0
  297. package/dist/plugins/fork-routing-session-store.js +51 -3
  298. package/dist/plugins/host-platform.d.ts +69 -0
  299. package/dist/plugins/host-platform.js +81 -2
  300. package/dist/plugins/image-bake-store-sql.d.ts +136 -1
  301. package/dist/plugins/image-bake-store-sql.js +241 -7
  302. package/dist/plugins/image-index-sql.d.ts +46 -0
  303. package/dist/plugins/image-index-sql.js +113 -4
  304. package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
  305. package/dist/plugins/k8s-bg-scripts.js +88 -4
  306. package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
  307. package/dist/plugins/k8s-exec-protocol.js +41 -0
  308. package/dist/plugins/local-checkpoint-store.d.ts +40 -0
  309. package/dist/plugins/local-checkpoint-store.js +86 -7
  310. package/dist/plugins/local-session-store.d.ts +157 -0
  311. package/dist/plugins/local-session-store.js +289 -34
  312. package/dist/plugins/local-task-attachment-store.js +14 -2
  313. package/dist/plugins/mailbox-store-sql.d.ts +38 -0
  314. package/dist/plugins/mailbox-store-sql.js +39 -6
  315. package/dist/plugins/memory-engine-pg.d.ts +30 -0
  316. package/dist/plugins/memory-engine-pg.js +122 -4
  317. package/dist/plugins/memory-engine-tidb.d.ts +26 -0
  318. package/dist/plugins/memory-engine-tidb.js +115 -5
  319. package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
  320. package/dist/plugins/memory-engine-vector-util.js +5 -0
  321. package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
  322. package/dist/plugins/memory-resume-anchor-store.js +17 -0
  323. package/dist/plugins/memory-run-store.d.ts +63 -0
  324. package/dist/plugins/memory-run-store.js +73 -8
  325. package/dist/plugins/memory-session-policy-store.d.ts +17 -0
  326. package/dist/plugins/memory-session-policy-store.js +17 -0
  327. package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
  328. package/dist/plugins/memory-sync-store-pg.js +45 -2
  329. package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
  330. package/dist/plugins/memory-sync-store-tidb.js +31 -1
  331. package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
  332. package/dist/plugins/outcome-ledger-sql.js +97 -3
  333. package/dist/plugins/pg-approval-store.d.ts +7 -0
  334. package/dist/plugins/pg-approval-store.js +7 -0
  335. package/dist/plugins/pg-breaker-state.d.ts +6 -0
  336. package/dist/plugins/pg-breaker-state.js +6 -0
  337. package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
  338. package/dist/plugins/pg-checkpoint-store.js +8 -0
  339. package/dist/plugins/pg-cost-quota.d.ts +17 -0
  340. package/dist/plugins/pg-cost-quota.js +6 -0
  341. package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
  342. package/dist/plugins/pg-file-snapshot-store.js +6 -0
  343. package/dist/plugins/pg-image-bake.d.ts +9 -0
  344. package/dist/plugins/pg-image-bake.js +9 -0
  345. package/dist/plugins/pg-image-index.d.ts +9 -0
  346. package/dist/plugins/pg-image-index.js +9 -0
  347. package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
  348. package/dist/plugins/pg-outcome-ledger.js +9 -0
  349. package/dist/plugins/pg-pool.d.ts +50 -0
  350. package/dist/plugins/pg-pool.js +78 -0
  351. package/dist/plugins/pg-query.d.ts +11 -0
  352. package/dist/plugins/pg-rate-limiter.d.ts +23 -0
  353. package/dist/plugins/pg-rate-limiter.js +12 -2
  354. package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
  355. package/dist/plugins/pg-resume-anchor-store.js +5 -0
  356. package/dist/plugins/pg-run-store.d.ts +7 -0
  357. package/dist/plugins/pg-run-store.js +7 -0
  358. package/dist/plugins/pg-safe-json.d.ts +13 -0
  359. package/dist/plugins/pg-safe-json.js +40 -4
  360. package/dist/plugins/pg-session-policy-store.d.ts +5 -0
  361. package/dist/plugins/pg-session-policy-store.js +5 -0
  362. package/dist/plugins/pg-session-storage.d.ts +72 -0
  363. package/dist/plugins/pg-session-storage.js +152 -20
  364. package/dist/plugins/pg-session-store.d.ts +10 -0
  365. package/dist/plugins/pg-session-store.js +10 -0
  366. package/dist/plugins/pg-tool-result-store.d.ts +7 -0
  367. package/dist/plugins/pg-tool-result-store.js +7 -0
  368. package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
  369. package/dist/plugins/pg-workflow-journal-store.js +7 -0
  370. package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
  371. package/dist/plugins/pg-workflow-run-store.js +7 -0
  372. package/dist/plugins/posix-shell-fs.d.ts +3 -0
  373. package/dist/plugins/posix-shell-fs.js +31 -1
  374. package/dist/plugins/remote-env-adb.d.ts +25 -0
  375. package/dist/plugins/remote-env-adb.js +95 -12
  376. package/dist/plugins/remote-env-e2b.d.ts +146 -0
  377. package/dist/plugins/remote-env-e2b.js +371 -40
  378. package/dist/plugins/remote-env-file-error.d.ts +34 -0
  379. package/dist/plugins/remote-env-file-error.js +37 -0
  380. package/dist/plugins/remote-env-host.d.ts +151 -0
  381. package/dist/plugins/remote-env-host.js +468 -70
  382. package/dist/plugins/remote-env-k8s.d.ts +135 -0
  383. package/dist/plugins/remote-env-k8s.js +318 -32
  384. package/dist/plugins/remote-env-local-docker.d.ts +65 -0
  385. package/dist/plugins/remote-env-local-docker.js +149 -24
  386. package/dist/plugins/remote-env-ssh.d.ts +35 -0
  387. package/dist/plugins/remote-env-ssh.js +117 -20
  388. package/dist/plugins/remote-scratchpad.d.ts +38 -0
  389. package/dist/plugins/remote-scratchpad.js +32 -2
  390. package/dist/plugins/remote-shell.d.ts +22 -0
  391. package/dist/plugins/remote-shell.js +27 -1
  392. package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
  393. package/dist/plugins/resume-anchor-store-sql.js +11 -0
  394. package/dist/plugins/roster-store-sql.d.ts +27 -0
  395. package/dist/plugins/roster-store-sql.js +54 -0
  396. package/dist/plugins/run-store-sql.d.ts +194 -0
  397. package/dist/plugins/run-store-sql.js +210 -9
  398. package/dist/plugins/s3-presign.d.ts +20 -0
  399. package/dist/plugins/s3-presign.js +49 -2
  400. package/dist/plugins/scheduler-support.d.ts +12 -0
  401. package/dist/plugins/scheduler-support.js +75 -3
  402. package/dist/plugins/send-file-ledger.d.ts +15 -0
  403. package/dist/plugins/send-file-ledger.js +53 -4
  404. package/dist/plugins/send-user-file.d.ts +77 -0
  405. package/dist/plugins/send-user-file.js +92 -4
  406. package/dist/plugins/session-policy-store-sql.d.ts +15 -0
  407. package/dist/plugins/session-policy-store-sql.js +75 -3
  408. package/dist/plugins/session-store.d.ts +45 -0
  409. package/dist/plugins/session-store.js +51 -0
  410. package/dist/plugins/sql-driver.d.ts +77 -0
  411. package/dist/plugins/sql-driver.js +4 -0
  412. package/dist/plugins/sql-escape.d.ts +8 -0
  413. package/dist/plugins/sql-escape.js +8 -0
  414. package/dist/plugins/sql-row-helpers.d.ts +25 -0
  415. package/dist/plugins/sql-row-helpers.js +25 -0
  416. package/dist/plugins/store-backend.d.ts +103 -0
  417. package/dist/plugins/store-backend.js +120 -27
  418. package/dist/plugins/store-contracts.d.ts +62 -0
  419. package/dist/plugins/store-contracts.js +3 -0
  420. package/dist/plugins/task-attachment-store.d.ts +60 -0
  421. package/dist/plugins/task-attachment-store.js +49 -3
  422. package/dist/plugins/task-list-store-sql.d.ts +25 -0
  423. package/dist/plugins/task-list-store-sql.js +21 -0
  424. package/dist/plugins/tidb-approval-store.d.ts +6 -0
  425. package/dist/plugins/tidb-approval-store.js +6 -0
  426. package/dist/plugins/tidb-breaker-state.d.ts +5 -0
  427. package/dist/plugins/tidb-breaker-state.js +5 -0
  428. package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
  429. package/dist/plugins/tidb-checkpoint-store.js +7 -0
  430. package/dist/plugins/tidb-cost-quota.d.ts +6 -0
  431. package/dist/plugins/tidb-cost-quota.js +1 -0
  432. package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
  433. package/dist/plugins/tidb-file-snapshot-store.js +6 -0
  434. package/dist/plugins/tidb-image-bake.d.ts +8 -0
  435. package/dist/plugins/tidb-image-bake.js +8 -0
  436. package/dist/plugins/tidb-image-index.d.ts +8 -0
  437. package/dist/plugins/tidb-image-index.js +8 -0
  438. package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
  439. package/dist/plugins/tidb-outcome-ledger.js +10 -0
  440. package/dist/plugins/tidb-pool.d.ts +56 -0
  441. package/dist/plugins/tidb-pool.js +181 -1
  442. package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
  443. package/dist/plugins/tidb-rate-limiter.js +5 -2
  444. package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
  445. package/dist/plugins/tidb-resume-anchor-store.js +5 -0
  446. package/dist/plugins/tidb-run-store.d.ts +7 -0
  447. package/dist/plugins/tidb-run-store.js +7 -0
  448. package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
  449. package/dist/plugins/tidb-session-policy-store.js +5 -0
  450. package/dist/plugins/tidb-session-storage.d.ts +43 -0
  451. package/dist/plugins/tidb-session-storage.js +62 -3
  452. package/dist/plugins/tidb-session-store.d.ts +142 -0
  453. package/dist/plugins/tidb-session-store.js +264 -16
  454. package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
  455. package/dist/plugins/tidb-tool-result-store.js +7 -0
  456. package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
  457. package/dist/plugins/tidb-workflow-journal-store.js +7 -0
  458. package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
  459. package/dist/plugins/tidb-workflow-run-store.js +8 -0
  460. package/dist/plugins/tool-result-store-sql.d.ts +58 -0
  461. package/dist/plugins/tool-result-store-sql.js +38 -2
  462. package/dist/plugins/web-search.d.ts +35 -0
  463. package/dist/plugins/web-search.js +45 -9
  464. package/dist/plugins/workflow-journal-limits.d.ts +10 -0
  465. package/dist/plugins/workflow-journal-limits.js +10 -0
  466. package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
  467. package/dist/plugins/workflow-journal-store-sql.js +58 -2
  468. package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
  469. package/dist/plugins/workflow-run-store-sql.js +84 -6
  470. package/dist/plugins/worktree-isolation.d.ts +45 -0
  471. package/dist/plugins/worktree-isolation.js +105 -6
  472. package/dist/plugins/write-behind-counter.d.ts +56 -1
  473. package/dist/plugins/write-behind-counter.js +53 -14
  474. package/dist/principal-jwt.d.ts +24 -0
  475. package/dist/principal-jwt.js +60 -5
  476. package/dist/project-memory.d.ts +22 -0
  477. package/dist/project-memory.js +144 -15
  478. package/dist/prompts-domain-validate.d.ts +28 -0
  479. package/dist/prompts-domain-validate.js +42 -1
  480. package/dist/question.d.ts +33 -0
  481. package/dist/question.js +69 -2
  482. package/dist/resource-suspend.d.ts +21 -0
  483. package/dist/resource-suspend.js +16 -0
  484. package/dist/router/route-orchestration.d.ts +78 -0
  485. package/dist/router/route-orchestration.js +77 -0
  486. package/dist/run-local.d.ts +32 -0
  487. package/dist/run-local.js +172 -8
  488. package/dist/runs.d.ts +189 -4
  489. package/dist/runs.js +465 -24
  490. package/dist/runtime-caps-resolver.d.ts +106 -0
  491. package/dist/runtime-caps-resolver.js +89 -4
  492. package/dist/runtime-governance.d.ts +84 -0
  493. package/dist/runtime-governance.js +127 -1
  494. package/dist/sandbox-pkg-source.d.ts +23 -0
  495. package/dist/sandbox-pkg-source.js +37 -0
  496. package/dist/sealed-key.d.ts +54 -0
  497. package/dist/sealed-key.js +68 -3
  498. package/dist/security.d.ts +108 -0
  499. package/dist/security.js +145 -6
  500. package/dist/sema-registry.d.ts +21 -0
  501. package/dist/sema-registry.js +21 -0
  502. package/dist/session-leaf-bus.d.ts +31 -0
  503. package/dist/session-leaf-bus.js +32 -0
  504. package/dist/session-sync-content.d.ts +46 -0
  505. package/dist/session-sync-content.js +47 -1
  506. package/dist/session-sync-kernel.d.ts +89 -0
  507. package/dist/session-sync-kernel.js +48 -3
  508. package/dist/session-sync.d.ts +125 -0
  509. package/dist/session-sync.js +181 -8
  510. package/dist/session-titler.d.ts +26 -0
  511. package/dist/session-titler.js +41 -7
  512. package/dist/session-watch.d.ts +53 -1
  513. package/dist/session-watch.js +73 -9
  514. package/dist/sighup-idle.d.ts +30 -0
  515. package/dist/sighup-idle.js +12 -1
  516. package/dist/spec-fields.d.ts +91 -3
  517. package/dist/spec-fields.js +116 -4
  518. package/dist/task-cwd.d.ts +50 -0
  519. package/dist/task-cwd.js +68 -1
  520. package/dist/task-mcp.d.ts +47 -0
  521. package/dist/task-mcp.js +29 -0
  522. package/dist/task-settings.d.ts +175 -0
  523. package/dist/task-settings.js +226 -11
  524. package/dist/task-workflow.d.ts +67 -0
  525. package/dist/task-workflow.js +75 -8
  526. package/dist/tool-approval.d.ts +91 -0
  527. package/dist/tool-approval.js +215 -0
  528. package/dist/trace/artifacts.d.ts +9 -0
  529. package/dist/trace/artifacts.js +30 -5
  530. package/dist/trace/core-keyset-guard.d.ts +18 -0
  531. package/dist/trace/ledger-sink.d.ts +43 -0
  532. package/dist/trace/ledger-sink.js +64 -5
  533. package/dist/trace/project.d.ts +133 -0
  534. package/dist/trace/project.js +189 -2
  535. package/dist/trace/redact.d.ts +19 -0
  536. package/dist/trace/redact.js +66 -9
  537. package/dist/usage-analytics.d.ts +26 -0
  538. package/dist/usage-analytics.js +26 -3
  539. 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")