open-multi-agent-kit 0.98.3 → 0.98.5

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 (468) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +12 -2
  3. package/dist/cli/args.d.ts +1 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +14 -0
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/help.d.ts.map +1 -1
  8. package/dist/cli/help.js +3 -0
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/mcp-attach.d.ts +3 -3
  11. package/dist/cli/mcp-attach.d.ts.map +1 -1
  12. package/dist/cli/mcp-attach.js +5 -4
  13. package/dist/cli/mcp-attach.js.map +1 -1
  14. package/dist/cli/model-contract.d.ts +5 -0
  15. package/dist/cli/model-contract.d.ts.map +1 -0
  16. package/dist/cli/model-contract.js +45 -0
  17. package/dist/cli/model-contract.js.map +1 -0
  18. package/dist/commands/adaptorch-doctor-cli.d.ts.map +1 -1
  19. package/dist/commands/adaptorch-doctor-cli.js +32 -10
  20. package/dist/commands/adaptorch-doctor-cli.js.map +1 -1
  21. package/dist/commands/init-cli.d.ts +12 -0
  22. package/dist/commands/init-cli.d.ts.map +1 -0
  23. package/dist/commands/init-cli.js +76 -0
  24. package/dist/commands/init-cli.js.map +1 -0
  25. package/dist/commands/provider-sync-cli.d.ts +27 -0
  26. package/dist/commands/provider-sync-cli.d.ts.map +1 -0
  27. package/dist/commands/provider-sync-cli.js +177 -0
  28. package/dist/commands/provider-sync-cli.js.map +1 -0
  29. package/dist/commands/run-command.d.ts +9 -0
  30. package/dist/commands/run-command.d.ts.map +1 -0
  31. package/dist/commands/run-command.js +34 -0
  32. package/dist/commands/run-command.js.map +1 -0
  33. package/dist/commands/verified-run-cli.d.ts +6 -0
  34. package/dist/commands/verified-run-cli.d.ts.map +1 -0
  35. package/dist/commands/verified-run-cli.js +190 -0
  36. package/dist/commands/verified-run-cli.js.map +1 -0
  37. package/dist/core/active-skill-state.d.ts +7 -0
  38. package/dist/core/active-skill-state.d.ts.map +1 -0
  39. package/dist/core/active-skill-state.js +25 -0
  40. package/dist/core/active-skill-state.js.map +1 -0
  41. package/dist/core/agent-session-services.d.ts +10 -1
  42. package/dist/core/agent-session-services.d.ts.map +1 -1
  43. package/dist/core/agent-session-services.js +43 -0
  44. package/dist/core/agent-session-services.js.map +1 -1
  45. package/dist/core/agent-session.d.ts +7 -9
  46. package/dist/core/agent-session.d.ts.map +1 -1
  47. package/dist/core/agent-session.js +91 -126
  48. package/dist/core/agent-session.js.map +1 -1
  49. package/dist/core/codex-chatgpt-web-bridge.d.ts +99 -0
  50. package/dist/core/codex-chatgpt-web-bridge.d.ts.map +1 -0
  51. package/dist/core/codex-chatgpt-web-bridge.js +192 -0
  52. package/dist/core/codex-chatgpt-web-bridge.js.map +1 -0
  53. package/dist/core/codex-chatgpt-web-sync.d.ts +50 -0
  54. package/dist/core/codex-chatgpt-web-sync.d.ts.map +1 -0
  55. package/dist/core/codex-chatgpt-web-sync.js +119 -0
  56. package/dist/core/codex-chatgpt-web-sync.js.map +1 -0
  57. package/dist/core/compaction/compaction-headroom.d.ts +30 -0
  58. package/dist/core/compaction/compaction-headroom.d.ts.map +1 -0
  59. package/dist/core/compaction/compaction-headroom.js +48 -0
  60. package/dist/core/compaction/compaction-headroom.js.map +1 -0
  61. package/dist/core/compaction/compaction.d.ts +2 -29
  62. package/dist/core/compaction/compaction.d.ts.map +1 -1
  63. package/dist/core/compaction/compaction.js +19 -47
  64. package/dist/core/compaction/compaction.js.map +1 -1
  65. package/dist/core/compaction/overflow-retry-guard.d.ts +20 -0
  66. package/dist/core/compaction/overflow-retry-guard.d.ts.map +1 -0
  67. package/dist/core/compaction/overflow-retry-guard.js +38 -0
  68. package/dist/core/compaction/overflow-retry-guard.js.map +1 -0
  69. package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
  70. package/dist/core/context-budget-headroom-types.js +1 -1
  71. package/dist/core/context-budget-headroom-types.js.map +1 -1
  72. package/dist/core/context-budget-token-counter-types.d.ts +27 -0
  73. package/dist/core/context-budget-token-counter-types.d.ts.map +1 -0
  74. package/dist/core/context-budget-token-counter-types.js +2 -0
  75. package/dist/core/context-budget-token-counter-types.js.map +1 -0
  76. package/dist/core/context-budget-token-counter.d.ts +2 -26
  77. package/dist/core/context-budget-token-counter.d.ts.map +1 -1
  78. package/dist/core/context-budget-token-counter.js +2 -0
  79. package/dist/core/context-budget-token-counter.js.map +1 -1
  80. package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
  81. package/dist/core/context-budget-v2-scoring.js +14 -10
  82. package/dist/core/context-budget-v2-scoring.js.map +1 -1
  83. package/dist/core/domain-router.d.ts.map +1 -1
  84. package/dist/core/domain-router.js +1 -1
  85. package/dist/core/domain-router.js.map +1 -1
  86. package/dist/core/grok-harness-dispatch.d.ts +4 -0
  87. package/dist/core/grok-harness-dispatch.d.ts.map +1 -1
  88. package/dist/core/grok-harness-dispatch.js +20 -3
  89. package/dist/core/grok-harness-dispatch.js.map +1 -1
  90. package/dist/core/grok-harness.d.ts +15 -9
  91. package/dist/core/grok-harness.d.ts.map +1 -1
  92. package/dist/core/grok-harness.js +34 -8
  93. package/dist/core/grok-harness.js.map +1 -1
  94. package/dist/core/mcp/tools.d.ts +2 -2
  95. package/dist/core/mcp/tools.d.ts.map +1 -1
  96. package/dist/core/mcp/tools.js +29 -3
  97. package/dist/core/mcp/tools.js.map +1 -1
  98. package/dist/core/model-registry-schema.d.ts +1081 -0
  99. package/dist/core/model-registry-schema.d.ts.map +1 -0
  100. package/dist/core/model-registry-schema.js +148 -0
  101. package/dist/core/model-registry-schema.js.map +1 -0
  102. package/dist/core/model-registry.d.ts.map +1 -1
  103. package/dist/core/model-registry.js +1 -146
  104. package/dist/core/model-registry.js.map +1 -1
  105. package/dist/core/model-resolver.d.ts +2 -1
  106. package/dist/core/model-resolver.d.ts.map +1 -1
  107. package/dist/core/model-resolver.js +2 -1
  108. package/dist/core/model-resolver.js.map +1 -1
  109. package/dist/core/prompt-preset.d.ts +1 -1
  110. package/dist/core/prompt-preset.d.ts.map +1 -1
  111. package/dist/core/prompt-preset.js +16 -1
  112. package/dist/core/prompt-preset.js.map +1 -1
  113. package/dist/core/prompt-settlement.d.ts +4 -3
  114. package/dist/core/prompt-settlement.d.ts.map +1 -1
  115. package/dist/core/prompt-settlement.js.map +1 -1
  116. package/dist/core/provider-display-names.d.ts.map +1 -1
  117. package/dist/core/provider-display-names.js +1 -0
  118. package/dist/core/provider-display-names.js.map +1 -1
  119. package/dist/core/provider-resilience.d.ts +8 -0
  120. package/dist/core/provider-resilience.d.ts.map +1 -1
  121. package/dist/core/provider-resilience.js +20 -3
  122. package/dist/core/provider-resilience.js.map +1 -1
  123. package/dist/core/provider-usage-types.d.ts +42 -0
  124. package/dist/core/provider-usage-types.d.ts.map +1 -0
  125. package/dist/core/provider-usage-types.js +2 -0
  126. package/dist/core/provider-usage-types.js.map +1 -0
  127. package/dist/core/provider-usage.d.ts +2 -31
  128. package/dist/core/provider-usage.d.ts.map +1 -1
  129. package/dist/core/provider-usage.js +4 -0
  130. package/dist/core/provider-usage.js.map +1 -1
  131. package/dist/core/redaction.d.ts.map +1 -1
  132. package/dist/core/redaction.js +18 -2
  133. package/dist/core/redaction.js.map +1 -1
  134. package/dist/core/run-budget-policy.d.ts +18 -0
  135. package/dist/core/run-budget-policy.d.ts.map +1 -0
  136. package/dist/core/run-budget-policy.js +52 -0
  137. package/dist/core/run-budget-policy.js.map +1 -0
  138. package/dist/core/run-budget.d.ts +33 -0
  139. package/dist/core/run-budget.d.ts.map +1 -0
  140. package/dist/core/run-budget.js +89 -0
  141. package/dist/core/run-budget.js.map +1 -0
  142. package/dist/core/run-execution-api.d.ts +15 -0
  143. package/dist/core/run-execution-api.d.ts.map +1 -0
  144. package/dist/core/run-execution-api.js +7 -0
  145. package/dist/core/run-execution-api.js.map +1 -0
  146. package/dist/core/run-journal.d.ts.map +1 -1
  147. package/dist/core/run-journal.js +30 -208
  148. package/dist/core/run-journal.js.map +1 -1
  149. package/dist/core/sandbox/policy-merge.d.ts +6 -0
  150. package/dist/core/sandbox/policy-merge.d.ts.map +1 -0
  151. package/dist/core/sandbox/policy-merge.js +74 -0
  152. package/dist/core/sandbox/policy-merge.js.map +1 -0
  153. package/dist/core/sandbox/policy-paths.d.ts +4 -0
  154. package/dist/core/sandbox/policy-paths.d.ts.map +1 -0
  155. package/dist/core/sandbox/policy-paths.js +21 -0
  156. package/dist/core/sandbox/policy-paths.js.map +1 -0
  157. package/dist/core/sandbox/policy-types.d.ts +75 -0
  158. package/dist/core/sandbox/policy-types.d.ts.map +1 -0
  159. package/dist/core/sandbox/policy-types.js +2 -0
  160. package/dist/core/sandbox/policy-types.js.map +1 -0
  161. package/dist/core/sandbox/policy.d.ts +3 -77
  162. package/dist/core/sandbox/policy.d.ts.map +1 -1
  163. package/dist/core/sandbox/policy.js +3 -98
  164. package/dist/core/sandbox/policy.js.map +1 -1
  165. package/dist/core/sdk-provider-stream.d.ts +14 -0
  166. package/dist/core/sdk-provider-stream.d.ts.map +1 -0
  167. package/dist/core/sdk-provider-stream.js +38 -0
  168. package/dist/core/sdk-provider-stream.js.map +1 -0
  169. package/dist/core/sdk.d.ts +5 -1
  170. package/dist/core/sdk.d.ts.map +1 -1
  171. package/dist/core/sdk.js +17 -33
  172. package/dist/core/sdk.js.map +1 -1
  173. package/dist/core/session-bash-service.d.ts +2 -2
  174. package/dist/core/session-bash-service.d.ts.map +1 -1
  175. package/dist/core/session-bash-service.js +21 -9
  176. package/dist/core/session-bash-service.js.map +1 -1
  177. package/dist/core/session-failure-cause.d.ts.map +1 -1
  178. package/dist/core/session-failure-cause.js +15 -3
  179. package/dist/core/session-failure-cause.js.map +1 -1
  180. package/dist/core/session-prompt-lifecycle.d.ts +25 -0
  181. package/dist/core/session-prompt-lifecycle.d.ts.map +1 -0
  182. package/dist/core/session-prompt-lifecycle.js +87 -0
  183. package/dist/core/session-prompt-lifecycle.js.map +1 -0
  184. package/dist/core/session-run-budget.d.ts +28 -0
  185. package/dist/core/session-run-budget.d.ts.map +1 -0
  186. package/dist/core/session-run-budget.js +127 -0
  187. package/dist/core/session-run-budget.js.map +1 -0
  188. package/dist/core/session-run-termination.d.ts +18 -0
  189. package/dist/core/session-run-termination.d.ts.map +1 -0
  190. package/dist/core/session-run-termination.js +72 -0
  191. package/dist/core/session-run-termination.js.map +1 -0
  192. package/dist/core/session-termination-types.d.ts +97 -0
  193. package/dist/core/session-termination-types.d.ts.map +1 -0
  194. package/dist/core/session-termination-types.js +26 -0
  195. package/dist/core/session-termination-types.js.map +1 -0
  196. package/dist/core/session-termination.d.ts +3 -97
  197. package/dist/core/session-termination.d.ts.map +1 -1
  198. package/dist/core/session-termination.js +25 -29
  199. package/dist/core/session-termination.js.map +1 -1
  200. package/dist/core/skill-selector.d.ts +40 -0
  201. package/dist/core/skill-selector.d.ts.map +1 -0
  202. package/dist/core/skill-selector.js +102 -0
  203. package/dist/core/skill-selector.js.map +1 -0
  204. package/dist/core/slash-commands.d.ts.map +1 -1
  205. package/dist/core/slash-commands.js +1 -0
  206. package/dist/core/slash-commands.js.map +1 -1
  207. package/dist/core/subagent-lane-launcher.d.ts +3 -2
  208. package/dist/core/subagent-lane-launcher.d.ts.map +1 -1
  209. package/dist/core/subagent-lane-launcher.js +24 -13
  210. package/dist/core/subagent-lane-launcher.js.map +1 -1
  211. package/dist/core/tools/render-utils.d.ts.map +1 -1
  212. package/dist/core/tools/render-utils.js +2 -4
  213. package/dist/core/tools/render-utils.js.map +1 -1
  214. package/dist/core/turn-metrics-record.d.ts +55 -0
  215. package/dist/core/turn-metrics-record.d.ts.map +1 -0
  216. package/dist/core/turn-metrics-record.js +149 -0
  217. package/dist/core/turn-metrics-record.js.map +1 -0
  218. package/dist/core/turn-metrics.d.ts +4 -56
  219. package/dist/core/turn-metrics.d.ts.map +1 -1
  220. package/dist/core/turn-metrics.js +12 -60
  221. package/dist/core/turn-metrics.js.map +1 -1
  222. package/dist/core/verified-run/broker.d.ts +26 -0
  223. package/dist/core/verified-run/broker.d.ts.map +1 -0
  224. package/dist/core/verified-run/broker.js +210 -0
  225. package/dist/core/verified-run/broker.js.map +1 -0
  226. package/dist/core/verified-run/candidate.d.ts +24 -0
  227. package/dist/core/verified-run/candidate.d.ts.map +1 -0
  228. package/dist/core/verified-run/candidate.js +167 -0
  229. package/dist/core/verified-run/candidate.js.map +1 -0
  230. package/dist/core/verified-run/check-receipt.d.ts +18 -0
  231. package/dist/core/verified-run/check-receipt.d.ts.map +1 -0
  232. package/dist/core/verified-run/check-receipt.js +92 -0
  233. package/dist/core/verified-run/check-receipt.js.map +1 -0
  234. package/dist/core/verified-run/coordinator.d.ts +39 -0
  235. package/dist/core/verified-run/coordinator.d.ts.map +1 -0
  236. package/dist/core/verified-run/coordinator.js +190 -0
  237. package/dist/core/verified-run/coordinator.js.map +1 -0
  238. package/dist/core/verified-run/dag-candidates.d.ts +8 -0
  239. package/dist/core/verified-run/dag-candidates.d.ts.map +1 -0
  240. package/dist/core/verified-run/dag-candidates.js +65 -0
  241. package/dist/core/verified-run/dag-candidates.js.map +1 -0
  242. package/dist/core/verified-run/dag-phase.d.ts +7 -0
  243. package/dist/core/verified-run/dag-phase.d.ts.map +1 -0
  244. package/dist/core/verified-run/dag-phase.js +92 -0
  245. package/dist/core/verified-run/dag-phase.js.map +1 -0
  246. package/dist/core/verified-run/dag-projection.d.ts +7 -0
  247. package/dist/core/verified-run/dag-projection.d.ts.map +1 -0
  248. package/dist/core/verified-run/dag-projection.js +90 -0
  249. package/dist/core/verified-run/dag-projection.js.map +1 -0
  250. package/dist/core/verified-run/dag-recovery.d.ts +13 -0
  251. package/dist/core/verified-run/dag-recovery.d.ts.map +1 -0
  252. package/dist/core/verified-run/dag-recovery.js +98 -0
  253. package/dist/core/verified-run/dag-recovery.js.map +1 -0
  254. package/dist/core/verified-run/dag-retry-projection.d.ts +9 -0
  255. package/dist/core/verified-run/dag-retry-projection.d.ts.map +1 -0
  256. package/dist/core/verified-run/dag-retry-projection.js +43 -0
  257. package/dist/core/verified-run/dag-retry-projection.js.map +1 -0
  258. package/dist/core/verified-run/dag-types.d.ts +47 -0
  259. package/dist/core/verified-run/dag-types.d.ts.map +1 -0
  260. package/dist/core/verified-run/dag-types.js +2 -0
  261. package/dist/core/verified-run/dag-types.js.map +1 -0
  262. package/dist/core/verified-run/event-parser.d.ts +3 -0
  263. package/dist/core/verified-run/event-parser.d.ts.map +1 -0
  264. package/dist/core/verified-run/event-parser.js +153 -0
  265. package/dist/core/verified-run/event-parser.js.map +1 -0
  266. package/dist/core/verified-run/events.d.ts +4 -0
  267. package/dist/core/verified-run/events.d.ts.map +1 -0
  268. package/dist/core/verified-run/events.js +3 -0
  269. package/dist/core/verified-run/events.js.map +1 -0
  270. package/dist/core/verified-run/evidence-binding.d.ts +18 -0
  271. package/dist/core/verified-run/evidence-binding.d.ts.map +1 -0
  272. package/dist/core/verified-run/evidence-binding.js +81 -0
  273. package/dist/core/verified-run/evidence-binding.js.map +1 -0
  274. package/dist/core/verified-run/evidence.d.ts +27 -0
  275. package/dist/core/verified-run/evidence.d.ts.map +1 -0
  276. package/dist/core/verified-run/evidence.js +100 -0
  277. package/dist/core/verified-run/evidence.js.map +1 -0
  278. package/dist/core/verified-run/journal.d.ts +32 -0
  279. package/dist/core/verified-run/journal.d.ts.map +1 -0
  280. package/dist/core/verified-run/journal.js +103 -0
  281. package/dist/core/verified-run/journal.js.map +1 -0
  282. package/dist/core/verified-run/namespace-identity.d.ts +11 -0
  283. package/dist/core/verified-run/namespace-identity.d.ts.map +1 -0
  284. package/dist/core/verified-run/namespace-identity.js +77 -0
  285. package/dist/core/verified-run/namespace-identity.js.map +1 -0
  286. package/dist/core/verified-run/owned-execution.d.ts +21 -0
  287. package/dist/core/verified-run/owned-execution.d.ts.map +1 -0
  288. package/dist/core/verified-run/owned-execution.js +43 -0
  289. package/dist/core/verified-run/owned-execution.js.map +1 -0
  290. package/dist/core/verified-run/phase-context.d.ts +9 -0
  291. package/dist/core/verified-run/phase-context.d.ts.map +1 -0
  292. package/dist/core/verified-run/phase-context.js +2 -0
  293. package/dist/core/verified-run/phase-context.js.map +1 -0
  294. package/dist/core/verified-run/process-gate.d.ts +5 -0
  295. package/dist/core/verified-run/process-gate.d.ts.map +1 -0
  296. package/dist/core/verified-run/process-gate.js +31 -0
  297. package/dist/core/verified-run/process-gate.js.map +1 -0
  298. package/dist/core/verified-run/projection.d.ts +4 -0
  299. package/dist/core/verified-run/projection.d.ts.map +1 -0
  300. package/dist/core/verified-run/projection.js +219 -0
  301. package/dist/core/verified-run/projection.js.map +1 -0
  302. package/dist/core/verified-run/recovery-clock.d.ts +19 -0
  303. package/dist/core/verified-run/recovery-clock.d.ts.map +1 -0
  304. package/dist/core/verified-run/recovery-clock.js +69 -0
  305. package/dist/core/verified-run/recovery-clock.js.map +1 -0
  306. package/dist/core/verified-run/recovery-command.d.ts +11 -0
  307. package/dist/core/verified-run/recovery-command.d.ts.map +1 -0
  308. package/dist/core/verified-run/recovery-command.js +73 -0
  309. package/dist/core/verified-run/recovery-command.js.map +1 -0
  310. package/dist/core/verified-run/recovery-projection.d.ts +12 -0
  311. package/dist/core/verified-run/recovery-projection.d.ts.map +1 -0
  312. package/dist/core/verified-run/recovery-projection.js +88 -0
  313. package/dist/core/verified-run/recovery-projection.js.map +1 -0
  314. package/dist/core/verified-run/recovery.d.ts +12 -0
  315. package/dist/core/verified-run/recovery.d.ts.map +1 -0
  316. package/dist/core/verified-run/recovery.js +115 -0
  317. package/dist/core/verified-run/recovery.js.map +1 -0
  318. package/dist/core/verified-run/run-types.d.ts +103 -0
  319. package/dist/core/verified-run/run-types.d.ts.map +1 -0
  320. package/dist/core/verified-run/run-types.js +2 -0
  321. package/dist/core/verified-run/run-types.js.map +1 -0
  322. package/dist/core/verified-run/scripted-writer.d.ts +15 -0
  323. package/dist/core/verified-run/scripted-writer.d.ts.map +1 -0
  324. package/dist/core/verified-run/scripted-writer.js +94 -0
  325. package/dist/core/verified-run/scripted-writer.js.map +1 -0
  326. package/dist/core/verified-run/session-port.d.ts +27 -0
  327. package/dist/core/verified-run/session-port.d.ts.map +1 -0
  328. package/dist/core/verified-run/session-port.js +2 -0
  329. package/dist/core/verified-run/session-port.js.map +1 -0
  330. package/dist/core/verified-run/storage.d.ts +15 -0
  331. package/dist/core/verified-run/storage.d.ts.map +1 -0
  332. package/dist/core/verified-run/storage.js +96 -0
  333. package/dist/core/verified-run/storage.js.map +1 -0
  334. package/dist/core/verified-run/verification-phase.d.ts +5 -0
  335. package/dist/core/verified-run/verification-phase.d.ts.map +1 -0
  336. package/dist/core/verified-run/verification-phase.js +56 -0
  337. package/dist/core/verified-run/verification-phase.js.map +1 -0
  338. package/dist/core/verified-run/work-recovery.d.ts +8 -0
  339. package/dist/core/verified-run/work-recovery.d.ts.map +1 -0
  340. package/dist/core/verified-run/work-recovery.js +44 -0
  341. package/dist/core/verified-run/work-recovery.js.map +1 -0
  342. package/dist/core/verified-run/writer-completion.d.ts +4 -0
  343. package/dist/core/verified-run/writer-completion.d.ts.map +1 -0
  344. package/dist/core/verified-run/writer-completion.js +27 -0
  345. package/dist/core/verified-run/writer-completion.js.map +1 -0
  346. package/dist/core/verified-run/writer-phase.d.ts +8 -0
  347. package/dist/core/verified-run/writer-phase.d.ts.map +1 -0
  348. package/dist/core/verified-run/writer-phase.js +76 -0
  349. package/dist/core/verified-run/writer-phase.js.map +1 -0
  350. package/dist/core/verified-run/writer-projection.d.ts +11 -0
  351. package/dist/core/verified-run/writer-projection.d.ts.map +1 -0
  352. package/dist/core/verified-run/writer-projection.js +70 -0
  353. package/dist/core/verified-run/writer-projection.js.map +1 -0
  354. package/dist/core/verified-run/writer-recovery.d.ts +16 -0
  355. package/dist/core/verified-run/writer-recovery.d.ts.map +1 -0
  356. package/dist/core/verified-run/writer-recovery.js +107 -0
  357. package/dist/core/verified-run/writer-recovery.js.map +1 -0
  358. package/dist/core/workload-permit-pool.d.ts +1 -3
  359. package/dist/core/workload-permit-pool.d.ts.map +1 -1
  360. package/dist/core/workload-permit-pool.js +11 -11
  361. package/dist/core/workload-permit-pool.js.map +1 -1
  362. package/dist/guardrails/evidence-system.d.ts +1 -1
  363. package/dist/guardrails/evidence-system.d.ts.map +1 -1
  364. package/dist/guardrails/evidence-system.js +18 -29
  365. package/dist/guardrails/evidence-system.js.map +1 -1
  366. package/dist/guardrails/merge-gate-result.d.ts +4 -0
  367. package/dist/guardrails/merge-gate-result.d.ts.map +1 -0
  368. package/dist/guardrails/merge-gate-result.js +32 -0
  369. package/dist/guardrails/merge-gate-result.js.map +1 -0
  370. package/dist/index.d.ts +3 -2
  371. package/dist/index.d.ts.map +1 -1
  372. package/dist/index.js +2 -2
  373. package/dist/index.js.map +1 -1
  374. package/dist/main.d.ts.map +1 -1
  375. package/dist/main.js +17 -23
  376. package/dist/main.js.map +1 -1
  377. package/dist/modes/interactive/components/session-failure.d.ts +13 -0
  378. package/dist/modes/interactive/components/session-failure.d.ts.map +1 -0
  379. package/dist/modes/interactive/components/session-failure.js +81 -0
  380. package/dist/modes/interactive/components/session-failure.js.map +1 -0
  381. package/dist/modes/interactive/interactive-login-options.d.ts +27 -0
  382. package/dist/modes/interactive/interactive-login-options.d.ts.map +1 -0
  383. package/dist/modes/interactive/interactive-login-options.js +54 -0
  384. package/dist/modes/interactive/interactive-login-options.js.map +1 -0
  385. package/dist/modes/interactive/interactive-mode.d.ts +8 -4
  386. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  387. package/dist/modes/interactive/interactive-mode.js +183 -171
  388. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  389. package/dist/modes/interactive/interactive-resume-command.d.ts +3 -0
  390. package/dist/modes/interactive/interactive-resume-command.d.ts.map +1 -0
  391. package/dist/modes/interactive/interactive-resume-command.js +24 -0
  392. package/dist/modes/interactive/interactive-resume-command.js.map +1 -0
  393. package/dist/modes/interactive/interactive-tool-result.d.ts +32 -0
  394. package/dist/modes/interactive/interactive-tool-result.d.ts.map +1 -0
  395. package/dist/modes/interactive/interactive-tool-result.js +59 -0
  396. package/dist/modes/interactive/interactive-tool-result.js.map +1 -0
  397. package/dist/modes/interactive/tui-diagnostics.d.ts +36 -0
  398. package/dist/modes/interactive/tui-diagnostics.d.ts.map +1 -0
  399. package/dist/modes/interactive/tui-diagnostics.js +105 -0
  400. package/dist/modes/interactive/tui-diagnostics.js.map +1 -0
  401. package/dist/modes/interactive/tui-runtime-info.d.ts +20 -0
  402. package/dist/modes/interactive/tui-runtime-info.d.ts.map +1 -0
  403. package/dist/modes/interactive/tui-runtime-info.js +76 -0
  404. package/dist/modes/interactive/tui-runtime-info.js.map +1 -0
  405. package/dist/modes/print-mode.d.ts.map +1 -1
  406. package/dist/modes/print-mode.js +22 -0
  407. package/dist/modes/print-mode.js.map +1 -1
  408. package/dist/utils/clipboard-image.d.ts.map +1 -1
  409. package/dist/utils/clipboard-image.js +10 -60
  410. package/dist/utils/clipboard-image.js.map +1 -1
  411. package/dist/utils/terminal-links.d.ts +13 -0
  412. package/dist/utils/terminal-links.d.ts.map +1 -0
  413. package/dist/utils/terminal-links.js +68 -0
  414. package/dist/utils/terminal-links.js.map +1 -0
  415. package/dist/utils/windows-clipboard-image.d.ts +16 -0
  416. package/dist/utils/windows-clipboard-image.d.ts.map +1 -0
  417. package/dist/utils/windows-clipboard-image.js +87 -0
  418. package/dist/utils/windows-clipboard-image.js.map +1 -0
  419. package/docs/adaptorch-onboarding.md +80 -0
  420. package/docs/containerization.md +12 -0
  421. package/docs/context-files.md +97 -0
  422. package/docs/correctness-wall.md +8 -0
  423. package/docs/custom-provider.md +35 -0
  424. package/docs/development.md +28 -3
  425. package/docs/environment-variables.md +2 -0
  426. package/docs/grok-harness.md +4 -0
  427. package/docs/harness-boundaries.md +154 -0
  428. package/docs/harness-improvements.md +90 -0
  429. package/docs/json.md +19 -0
  430. package/docs/keybindings.md +2 -2
  431. package/docs/mcp.md +22 -2
  432. package/docs/metrics.md +66 -16
  433. package/docs/model-catalog-refresh.md +190 -0
  434. package/docs/model-contract.md +168 -0
  435. package/docs/models.md +21 -4
  436. package/docs/provider-resilience.md +5 -3
  437. package/docs/providers.md +68 -1
  438. package/docs/quickstart.md +4 -0
  439. package/docs/release-audit-0.98.4.md +112 -0
  440. package/docs/release-audit-0.98.5.md +84 -0
  441. package/docs/review-bundle-followup.md +195 -0
  442. package/docs/run-protocol.md +33 -1
  443. package/docs/runtime-algorithms.md +45 -0
  444. package/docs/sdk.md +204 -2
  445. package/docs/sessions.md +1 -1
  446. package/docs/settings.md +17 -1
  447. package/docs/skills.md +2 -0
  448. package/docs/tb21-audit.md +131 -0
  449. package/docs/terminal-setup.md +20 -0
  450. package/docs/usage.md +77 -1
  451. package/docs/verified-run-testing.md +417 -0
  452. package/docs/verified-run.md +388 -0
  453. package/docs/windows.md +48 -0
  454. package/examples/README.md +4 -0
  455. package/examples/context/AGENTS.md +64 -0
  456. package/examples/context/CLAUDE.md +17 -0
  457. package/examples/context/INTERNET.md +42 -0
  458. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  459. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  460. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  461. package/examples/extensions/gondolin/package-lock.json +2 -2
  462. package/examples/extensions/gondolin/package.json +1 -1
  463. package/examples/extensions/sandbox/package-lock.json +2 -2
  464. package/examples/extensions/sandbox/package.json +1 -1
  465. package/examples/extensions/with-deps/package-lock.json +2 -2
  466. package/examples/extensions/with-deps/package.json +1 -1
  467. package/npm-shrinkwrap.json +18 -18
  468. package/package.json +6 -6
@@ -12,50 +12,15 @@
12
12
  * a failed write is dropped, never surfaced as a turn error.
13
13
  *
14
14
  * Privacy: records counts, durations, ids, and error *classes*. It never
15
- * records prompt text, tool arguments, tool output, or file contents. Tool
16
- * error strings are truncated and kept only to distinguish failure modes.
15
+ * records prompt text, tool arguments, tool output, or file contents. New
16
+ * records retain bounded error classes only; raw failure text is never serialized.
17
17
  */
18
18
  import fs from "node:fs";
19
19
  import path from "node:path";
20
- import { parseRuntimeProvenance } from "./runtime-provenance.js";
21
- export const TURN_METRICS_SCHEMA_VERSION = "omk-turn-metrics-1";
20
+ import { buildTurnMetricRecord, parseTurnMetricRecord, } from "./turn-metrics-record.js";
21
+ export * from "./turn-metrics-record.js";
22
22
  /** Rotate once the active file passes this size. */
23
23
  export const DEFAULT_MAX_METRICS_BYTES = 8 * 1024 * 1024;
24
- /** Cap on a retained error string. Enough to classify, too short to carry a payload. */
25
- export const MAX_ERROR_CHARS = 200;
26
- function truncateError(value) {
27
- if (value === undefined)
28
- return undefined;
29
- const collapsed = value.replace(/\s+/gu, " ").trim();
30
- if (collapsed.length === 0)
31
- return undefined;
32
- return collapsed.length > MAX_ERROR_CHARS ? `${collapsed.slice(0, MAX_ERROR_CHARS)}\u2026` : collapsed;
33
- }
34
- /**
35
- * Build the record written for one turn. Pure: no clock, no filesystem, so the
36
- * derived fields can be asserted directly.
37
- */
38
- export function buildTurnMetricRecord(input) {
39
- const toolCalls = (input.toolCalls ?? []).map((call) => ({
40
- name: call.name,
41
- durationMs: Math.max(0, Math.round(call.durationMs)),
42
- ok: call.ok,
43
- ...(call.ok ? {} : { error: truncateError(call.error) }),
44
- }));
45
- const runtimeProvenance = input.runtimeProvenance === undefined ? undefined : parseRuntimeProvenance(input.runtimeProvenance);
46
- // Spread the raw input minus its projection: a validated projection is re-added
47
- // below, an invalid one is dropped rather than persisted.
48
- const { runtimeProvenance: _rawProvenance, ...rest } = input;
49
- return {
50
- ...rest,
51
- schemaVersion: TURN_METRICS_SCHEMA_VERSION,
52
- durationMs: Math.max(0, input.endedAtEpochMs - input.startedAtEpochMs),
53
- toolCalls,
54
- toolCallCount: toolCalls.length,
55
- toolFailureCount: toolCalls.filter((call) => !call.ok).length,
56
- ...(runtimeProvenance === null || runtimeProvenance === undefined ? {} : { runtimeProvenance }),
57
- };
58
- }
59
24
  /**
60
25
  * Append-only JSONL sink. Writes are synchronous and best-effort: metrics must
61
26
  * never be the reason a turn fails, so every filesystem error is swallowed and
@@ -91,9 +56,14 @@ export class TurnMetricsSink {
91
56
  this.dropped++;
92
57
  return false;
93
58
  }
59
+ const bytes = Buffer.byteLength(line, "utf8");
60
+ if (bytes > this.maxBytes) {
61
+ this.dropped++;
62
+ return false;
63
+ }
94
64
  try {
95
65
  fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
96
- this.rotateIfNeeded(Buffer.byteLength(line, "utf8"));
66
+ this.rotateIfNeeded(bytes);
97
67
  fs.appendFileSync(this.filePath, line, { encoding: "utf8", mode: 0o600 });
98
68
  this.written++;
99
69
  return true;
@@ -128,23 +98,6 @@ function percentile(sorted, fraction) {
128
98
  const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(fraction * sorted.length) - 1));
129
99
  return sorted[index];
130
100
  }
131
- function isRecord(value) {
132
- return typeof value === "object" && value !== null && !Array.isArray(value);
133
- }
134
- function parseRecord(line) {
135
- let parsed;
136
- try {
137
- parsed = JSON.parse(line);
138
- }
139
- catch {
140
- return undefined;
141
- }
142
- if (!isRecord(parsed) || parsed.schemaVersion !== TURN_METRICS_SCHEMA_VERSION)
143
- return undefined;
144
- if (typeof parsed.turnIndex !== "number" || typeof parsed.durationMs !== "number")
145
- return undefined;
146
- return parsed;
147
- }
148
101
  /**
149
102
  * Aggregate JSONL lines into a summary. Malformed lines are counted rather than
150
103
  * discarded silently, so a truncated tail is visible instead of pretending the
@@ -157,7 +110,7 @@ export function summarizeTurnMetrics(lines) {
157
110
  const trimmed = line.trim();
158
111
  if (trimmed.length === 0)
159
112
  continue;
160
- const record = parseRecord(trimmed);
113
+ const record = parseTurnMetricRecord(trimmed);
161
114
  if (record)
162
115
  records.push(record);
163
116
  else
@@ -196,8 +149,7 @@ export function summarizeTurnMetrics(lines) {
196
149
  if (record.contextCache.planHit)
197
150
  planHits++;
198
151
  }
199
- // Re-validate provenance at read time: a tampered projection never counts.
200
- const provenance = record.runtimeProvenance === undefined ? undefined : parseRuntimeProvenance(record.runtimeProvenance);
152
+ const provenance = record.runtimeProvenance;
201
153
  if (provenance !== undefined &&
202
154
  provenance !== null &&
203
155
  provenance.responseModel !== null &&
@@ -1 +1 @@
1
- {"version":3,"file":"turn-metrics.js","sourceRoot":"","sources":["../../src/core/turn-metrics.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Per-turn runtime metrics.\n *\n * Before this, `telemetry.ts` covered install opt-in and nothing else, and\n * `run-journal.ts` recorded a hash-chained *integrity* log (run started /\n * finished / tool timeout). Neither could answer the questions that actually\n * drive harness work: which tools fail, how long they take, what a turn costs,\n * and whether the context cache is doing anything.\n *\n * Shape: one JSON object per line under `<cwd>/.omk/metrics/turns.jsonl`.\n * Append-only, bounded by size with a single rotation, and never load-bearing —\n * a failed write is dropped, never surfaced as a turn error.\n *\n * Privacy: records counts, durations, ids, and error *classes*. It never\n * records prompt text, tool arguments, tool output, or file contents. Tool\n * error strings are truncated and kept only to distinguish failure modes.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parseRuntimeProvenance, type RuntimeProvenance } from \"./runtime-provenance.ts\";\n\nexport const TURN_METRICS_SCHEMA_VERSION = \"omk-turn-metrics-1\" as const;\n/** Rotate once the active file passes this size. */\nexport const DEFAULT_MAX_METRICS_BYTES = 8 * 1024 * 1024;\n/** Cap on a retained error string. Enough to classify, too short to carry a payload. */\nexport const MAX_ERROR_CHARS = 200;\n\nexport interface ToolCallMetric {\n\treadonly name: string;\n\treadonly durationMs: number;\n\treadonly ok: boolean;\n\t/** Truncated failure text, present only when `ok` is false. */\n\treadonly error?: string;\n}\n\nexport interface TurnUsageMetric {\n\treadonly input: number;\n\treadonly output: number;\n\treadonly cacheRead: number;\n\treadonly cacheWrite: number;\n\treadonly costUsd: number;\n}\n\nexport interface TurnMetricInput {\n\treadonly sessionId: string;\n\treadonly turnIndex: number;\n\treadonly provider?: string;\n\treadonly model?: string;\n\treadonly startedAtEpochMs: number;\n\treadonly endedAtEpochMs: number;\n\t/** Time to the first streamed assistant chunk, when observed. */\n\treadonly timeToFirstChunkMs?: number;\n\treadonly usage?: TurnUsageMetric;\n\treadonly stopReason?: string;\n\treadonly toolCalls?: readonly ToolCallMetric[];\n\t/** Whether compaction ran during this turn. */\n\treadonly compacted?: boolean;\n\t/** Provider failover attempts made during this turn. */\n\treadonly failovers?: number;\n\t/** Context-budget cache outcome for the turn's prompt build. */\n\treadonly contextCache?: { readonly planHit: boolean; readonly hits: number; readonly misses: number };\n\t/** Requested/selected/response model and thinking projection. Advisory metadata only. */\n\treadonly runtimeProvenance?: RuntimeProvenance;\n}\n\nexport interface TurnMetricRecord extends TurnMetricInput {\n\treadonly schemaVersion: typeof TURN_METRICS_SCHEMA_VERSION;\n\treadonly durationMs: number;\n\treadonly toolCallCount: number;\n\treadonly toolFailureCount: number;\n}\n\nfunction truncateError(value: string | undefined): string | undefined {\n\tif (value === undefined) return undefined;\n\tconst collapsed = value.replace(/\\s+/gu, \" \").trim();\n\tif (collapsed.length === 0) return undefined;\n\treturn collapsed.length > MAX_ERROR_CHARS ? `${collapsed.slice(0, MAX_ERROR_CHARS)}\\u2026` : collapsed;\n}\n\n/**\n * Build the record written for one turn. Pure: no clock, no filesystem, so the\n * derived fields can be asserted directly.\n */\nexport function buildTurnMetricRecord(input: TurnMetricInput): TurnMetricRecord {\n\tconst toolCalls = (input.toolCalls ?? []).map((call) => ({\n\t\tname: call.name,\n\t\tdurationMs: Math.max(0, Math.round(call.durationMs)),\n\t\tok: call.ok,\n\t\t...(call.ok ? {} : { error: truncateError(call.error) }),\n\t}));\n\tconst runtimeProvenance =\n\t\tinput.runtimeProvenance === undefined ? undefined : parseRuntimeProvenance(input.runtimeProvenance);\n\t// Spread the raw input minus its projection: a validated projection is re-added\n\t// below, an invalid one is dropped rather than persisted.\n\tconst { runtimeProvenance: _rawProvenance, ...rest } = input;\n\treturn {\n\t\t...rest,\n\t\tschemaVersion: TURN_METRICS_SCHEMA_VERSION,\n\t\tdurationMs: Math.max(0, input.endedAtEpochMs - input.startedAtEpochMs),\n\t\ttoolCalls,\n\t\ttoolCallCount: toolCalls.length,\n\t\ttoolFailureCount: toolCalls.filter((call) => !call.ok).length,\n\t\t...(runtimeProvenance === null || runtimeProvenance === undefined ? {} : { runtimeProvenance }),\n\t};\n}\n\nexport interface TurnMetricsSinkOptions {\n\treadonly dir: string;\n\treadonly maxBytes?: number;\n\treadonly fileName?: string;\n}\n\n/**\n * Append-only JSONL sink. Writes are synchronous and best-effort: metrics must\n * never be the reason a turn fails, so every filesystem error is swallowed and\n * counted instead of thrown.\n */\nexport class TurnMetricsSink {\n\tprivate readonly filePath: string;\n\tprivate readonly maxBytes: number;\n\tprivate dropped = 0;\n\tprivate written = 0;\n\n\tconstructor(options: TurnMetricsSinkOptions) {\n\t\tthis.filePath = path.join(options.dir, options.fileName ?? \"turns.jsonl\");\n\t\tthis.maxBytes = Math.max(1024, options.maxBytes ?? DEFAULT_MAX_METRICS_BYTES);\n\t}\n\n\tget path(): string {\n\t\treturn this.filePath;\n\t}\n\n\t/** Records written successfully this process. */\n\tget writtenCount(): number {\n\t\treturn this.written;\n\t}\n\n\t/** Records lost to a filesystem error this process. */\n\tget droppedCount(): number {\n\t\treturn this.dropped;\n\t}\n\n\t/** Append one turn. Returns `true` when the line reached disk. */\n\trecord(input: TurnMetricInput): boolean {\n\t\tlet line: string;\n\t\ttry {\n\t\t\tline = `${JSON.stringify(buildTurnMetricRecord(input))}\\n`;\n\t\t} catch {\n\t\t\tthis.dropped++;\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tfs.mkdirSync(path.dirname(this.filePath), { recursive: true });\n\t\t\tthis.rotateIfNeeded(Buffer.byteLength(line, \"utf8\"));\n\t\t\tfs.appendFileSync(this.filePath, line, { encoding: \"utf8\", mode: 0o600 });\n\t\t\tthis.written++;\n\t\t\treturn true;\n\t\t} catch {\n\t\t\tthis.dropped++;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate rotateIfNeeded(incomingBytes: number): void {\n\t\tlet size: number;\n\t\ttry {\n\t\t\tsize = fs.statSync(this.filePath).size;\n\t\t} catch {\n\t\t\treturn; // No file yet.\n\t\t}\n\t\tif (size + incomingBytes <= this.maxBytes) return;\n\t\ttry {\n\t\t\t// Single generation: the previous file is replaced, so disk use stays bounded at 2x.\n\t\t\tfs.renameSync(this.filePath, `${this.filePath}.1`);\n\t\t} catch {\n\t\t\t// Rotation failure is not fatal; the next append still succeeds.\n\t\t}\n\t}\n}\n\nexport interface ToolAggregate {\n\treadonly name: string;\n\treadonly calls: number;\n\treadonly failures: number;\n\treadonly failureRate: number;\n\treadonly p50Ms: number;\n\treadonly p95Ms: number;\n\treadonly totalMs: number;\n}\n\nexport interface TurnMetricsSummary {\n\treadonly turns: number;\n\treadonly sessions: number;\n\treadonly models: readonly string[];\n\treadonly totalCostUsd: number;\n\treadonly totals: TurnUsageMetric;\n\treadonly turnDurationP50Ms: number;\n\treadonly turnDurationP95Ms: number;\n\treadonly timeToFirstChunkP50Ms?: number;\n\treadonly cacheReadShare: number;\n\treadonly compactions: number;\n\treadonly failovers: number;\n\treadonly contextCachePlanHitRate?: number;\n\t/** Turns where a valid projection's response model differs from the selected model. */\n\treadonly provenanceMismatchTurns: number;\n\treadonly tools: readonly ToolAggregate[];\n\t/** Lines that could not be parsed as a metric record. */\n\treadonly malformedLines: number;\n}\n\nfunction percentile(sorted: readonly number[], fraction: number): number {\n\tif (sorted.length === 0) return 0;\n\tconst index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(fraction * sorted.length) - 1));\n\treturn sorted[index];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseRecord(line: string): TurnMetricRecord | undefined {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!isRecord(parsed) || parsed.schemaVersion !== TURN_METRICS_SCHEMA_VERSION) return undefined;\n\tif (typeof parsed.turnIndex !== \"number\" || typeof parsed.durationMs !== \"number\") return undefined;\n\treturn parsed as unknown as TurnMetricRecord;\n}\n\n/**\n * Aggregate JSONL lines into a summary. Malformed lines are counted rather than\n * discarded silently, so a truncated tail is visible instead of pretending the\n * data is complete.\n */\nexport function summarizeTurnMetrics(lines: readonly string[]): TurnMetricsSummary {\n\tconst records: TurnMetricRecord[] = [];\n\tlet malformedLines = 0;\n\tfor (const line of lines) {\n\t\tconst trimmed = line.trim();\n\t\tif (trimmed.length === 0) continue;\n\t\tconst record = parseRecord(trimmed);\n\t\tif (record) records.push(record);\n\t\telse malformedLines++;\n\t}\n\n\tconst sessions = new Set<string>();\n\tconst models = new Set<string>();\n\tconst totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, costUsd: 0 };\n\tconst turnDurations: number[] = [];\n\tconst firstChunkTimes: number[] = [];\n\tconst toolStats = new Map<string, { durations: number[]; failures: number }>();\n\tlet compactions = 0;\n\tlet failovers = 0;\n\tlet planHits = 0;\n\tlet planObservations = 0;\n\tlet provenanceMismatchTurns = 0;\n\n\tfor (const record of records) {\n\t\tsessions.add(record.sessionId);\n\t\tif (record.model) models.add(record.provider ? `${record.provider}/${record.model}` : record.model);\n\t\tif (record.usage) {\n\t\t\ttotals.input += record.usage.input;\n\t\t\ttotals.output += record.usage.output;\n\t\t\ttotals.cacheRead += record.usage.cacheRead;\n\t\t\ttotals.cacheWrite += record.usage.cacheWrite;\n\t\t\ttotals.costUsd += record.usage.costUsd;\n\t\t}\n\t\tturnDurations.push(record.durationMs);\n\t\tif (typeof record.timeToFirstChunkMs === \"number\") firstChunkTimes.push(record.timeToFirstChunkMs);\n\t\tif (record.compacted) compactions++;\n\t\tfailovers += record.failovers ?? 0;\n\t\tif (record.contextCache) {\n\t\t\tplanObservations++;\n\t\t\tif (record.contextCache.planHit) planHits++;\n\t\t}\n\t\t// Re-validate provenance at read time: a tampered projection never counts.\n\t\tconst provenance =\n\t\t\trecord.runtimeProvenance === undefined ? undefined : parseRuntimeProvenance(record.runtimeProvenance);\n\t\tif (\n\t\t\tprovenance !== undefined &&\n\t\t\tprovenance !== null &&\n\t\t\tprovenance.responseModel !== null &&\n\t\t\tprovenance.responseModel !== provenance.selectedModel\n\t\t) {\n\t\t\tprovenanceMismatchTurns++;\n\t\t}\n\t\tfor (const call of record.toolCalls ?? []) {\n\t\t\tconst stats = toolStats.get(call.name) ?? { durations: [], failures: 0 };\n\t\t\tstats.durations.push(call.durationMs);\n\t\t\tif (!call.ok) stats.failures++;\n\t\t\ttoolStats.set(call.name, stats);\n\t\t}\n\t}\n\n\tturnDurations.sort((a, b) => a - b);\n\tfirstChunkTimes.sort((a, b) => a - b);\n\n\tconst tools: ToolAggregate[] = [...toolStats.entries()]\n\t\t.map(([name, stats]) => {\n\t\t\tconst sorted = [...stats.durations].sort((a, b) => a - b);\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\tcalls: sorted.length,\n\t\t\t\tfailures: stats.failures,\n\t\t\t\tfailureRate: sorted.length === 0 ? 0 : stats.failures / sorted.length,\n\t\t\t\tp50Ms: percentile(sorted, 0.5),\n\t\t\t\tp95Ms: percentile(sorted, 0.95),\n\t\t\t\ttotalMs: sorted.reduce((sum, value) => sum + value, 0),\n\t\t\t};\n\t\t})\n\t\t.sort((a, b) => b.totalMs - a.totalMs || a.name.localeCompare(b.name));\n\n\tconst promptSideTotal = totals.input + totals.cacheRead;\n\treturn {\n\t\tturns: records.length,\n\t\tsessions: sessions.size,\n\t\tmodels: [...models].sort(),\n\t\ttotalCostUsd: totals.costUsd,\n\t\ttotals,\n\t\tturnDurationP50Ms: percentile(turnDurations, 0.5),\n\t\tturnDurationP95Ms: percentile(turnDurations, 0.95),\n\t\ttimeToFirstChunkP50Ms: firstChunkTimes.length > 0 ? percentile(firstChunkTimes, 0.5) : undefined,\n\t\tcacheReadShare: promptSideTotal === 0 ? 0 : totals.cacheRead / promptSideTotal,\n\t\tcompactions,\n\t\tfailovers,\n\t\tcontextCachePlanHitRate: planObservations === 0 ? undefined : planHits / planObservations,\n\t\tprovenanceMismatchTurns,\n\t\ttools,\n\t\tmalformedLines,\n\t};\n}\n\n/** Read and summarize a metrics file. A missing file summarizes as zero turns. */\nexport function summarizeTurnMetricsFile(filePath: string): TurnMetricsSummary {\n\tlet raw: string;\n\ttry {\n\t\traw = fs.readFileSync(filePath, \"utf8\");\n\t} catch {\n\t\treturn summarizeTurnMetrics([]);\n\t}\n\treturn summarizeTurnMetrics(raw.split(\"\\n\"));\n}\n"]}
1
+ {"version":3,"file":"turn-metrics.js","sourceRoot":"","sources":["../../src/core/turn-metrics.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Per-turn runtime metrics.\n *\n * Before this, `telemetry.ts` covered install opt-in and nothing else, and\n * `run-journal.ts` recorded a hash-chained *integrity* log (run started /\n * finished / tool timeout). Neither could answer the questions that actually\n * drive harness work: which tools fail, how long they take, what a turn costs,\n * and whether the context cache is doing anything.\n *\n * Shape: one JSON object per line under `<cwd>/.omk/metrics/turns.jsonl`.\n * Append-only, bounded by size with a single rotation, and never load-bearing —\n * a failed write is dropped, never surfaced as a turn error.\n *\n * Privacy: records counts, durations, ids, and error *classes*. It never\n * records prompt text, tool arguments, tool output, or file contents. New\n * records retain bounded error classes only; raw failure text is never serialized.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n\tbuildTurnMetricRecord,\n\tparseTurnMetricRecord,\n\ttype TurnMetricInput,\n\ttype TurnMetricRecord,\n\ttype TurnUsageMetric,\n} from \"./turn-metrics-record.ts\";\n\nexport * from \"./turn-metrics-record.ts\";\n/** Rotate once the active file passes this size. */\nexport const DEFAULT_MAX_METRICS_BYTES = 8 * 1024 * 1024;\nexport interface TurnMetricsSinkOptions {\n\treadonly dir: string;\n\treadonly maxBytes?: number;\n\treadonly fileName?: string;\n}\n\n/**\n * Append-only JSONL sink. Writes are synchronous and best-effort: metrics must\n * never be the reason a turn fails, so every filesystem error is swallowed and\n * counted instead of thrown.\n */\nexport class TurnMetricsSink {\n\tprivate readonly filePath: string;\n\tprivate readonly maxBytes: number;\n\tprivate dropped = 0;\n\tprivate written = 0;\n\n\tconstructor(options: TurnMetricsSinkOptions) {\n\t\tthis.filePath = path.join(options.dir, options.fileName ?? \"turns.jsonl\");\n\t\tthis.maxBytes = Math.max(1024, options.maxBytes ?? DEFAULT_MAX_METRICS_BYTES);\n\t}\n\n\tget path(): string {\n\t\treturn this.filePath;\n\t}\n\n\t/** Records written successfully this process. */\n\tget writtenCount(): number {\n\t\treturn this.written;\n\t}\n\n\t/** Records lost to a filesystem error this process. */\n\tget droppedCount(): number {\n\t\treturn this.dropped;\n\t}\n\n\t/** Append one turn. Returns `true` when the line reached disk. */\n\trecord(input: TurnMetricInput): boolean {\n\t\tlet line: string;\n\t\ttry {\n\t\t\tline = `${JSON.stringify(buildTurnMetricRecord(input))}\\n`;\n\t\t} catch {\n\t\t\tthis.dropped++;\n\t\t\treturn false;\n\t\t}\n\t\tconst bytes = Buffer.byteLength(line, \"utf8\");\n\t\tif (bytes > this.maxBytes) {\n\t\t\tthis.dropped++;\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tfs.mkdirSync(path.dirname(this.filePath), { recursive: true });\n\t\t\tthis.rotateIfNeeded(bytes);\n\t\t\tfs.appendFileSync(this.filePath, line, { encoding: \"utf8\", mode: 0o600 });\n\t\t\tthis.written++;\n\t\t\treturn true;\n\t\t} catch {\n\t\t\tthis.dropped++;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate rotateIfNeeded(incomingBytes: number): void {\n\t\tlet size: number;\n\t\ttry {\n\t\t\tsize = fs.statSync(this.filePath).size;\n\t\t} catch {\n\t\t\treturn; // No file yet.\n\t\t}\n\t\tif (size + incomingBytes <= this.maxBytes) return;\n\t\ttry {\n\t\t\t// Single generation: the previous file is replaced, so disk use stays bounded at 2x.\n\t\t\tfs.renameSync(this.filePath, `${this.filePath}.1`);\n\t\t} catch {\n\t\t\t// Rotation failure is not fatal; the next append still succeeds.\n\t\t}\n\t}\n}\n\nexport interface ToolAggregate {\n\treadonly name: string;\n\treadonly calls: number;\n\treadonly failures: number;\n\treadonly failureRate: number;\n\treadonly p50Ms: number;\n\treadonly p95Ms: number;\n\treadonly totalMs: number;\n}\n\nexport interface TurnMetricsSummary {\n\treadonly turns: number;\n\treadonly sessions: number;\n\treadonly models: readonly string[];\n\treadonly totalCostUsd: number;\n\treadonly totals: TurnUsageMetric;\n\treadonly turnDurationP50Ms: number;\n\treadonly turnDurationP95Ms: number;\n\treadonly timeToFirstChunkP50Ms?: number;\n\treadonly cacheReadShare: number;\n\treadonly compactions: number;\n\treadonly failovers: number;\n\treadonly contextCachePlanHitRate?: number;\n\t/** Turns where a valid projection's response model differs from the selected model. */\n\treadonly provenanceMismatchTurns: number;\n\treadonly tools: readonly ToolAggregate[];\n\t/** Lines that could not be parsed as a metric record. */\n\treadonly malformedLines: number;\n}\n\nfunction percentile(sorted: readonly number[], fraction: number): number {\n\tif (sorted.length === 0) return 0;\n\tconst index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(fraction * sorted.length) - 1));\n\treturn sorted[index];\n}\n\n/**\n * Aggregate JSONL lines into a summary. Malformed lines are counted rather than\n * discarded silently, so a truncated tail is visible instead of pretending the\n * data is complete.\n */\nexport function summarizeTurnMetrics(lines: readonly string[]): TurnMetricsSummary {\n\tconst records: TurnMetricRecord[] = [];\n\tlet malformedLines = 0;\n\tfor (const line of lines) {\n\t\tconst trimmed = line.trim();\n\t\tif (trimmed.length === 0) continue;\n\t\tconst record = parseTurnMetricRecord(trimmed);\n\t\tif (record) records.push(record);\n\t\telse malformedLines++;\n\t}\n\n\tconst sessions = new Set<string>();\n\tconst models = new Set<string>();\n\tconst totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, costUsd: 0 };\n\tconst turnDurations: number[] = [];\n\tconst firstChunkTimes: number[] = [];\n\tconst toolStats = new Map<string, { durations: number[]; failures: number }>();\n\tlet compactions = 0;\n\tlet failovers = 0;\n\tlet planHits = 0;\n\tlet planObservations = 0;\n\tlet provenanceMismatchTurns = 0;\n\n\tfor (const record of records) {\n\t\tsessions.add(record.sessionId);\n\t\tif (record.model) models.add(record.provider ? `${record.provider}/${record.model}` : record.model);\n\t\tif (record.usage) {\n\t\t\ttotals.input += record.usage.input;\n\t\t\ttotals.output += record.usage.output;\n\t\t\ttotals.cacheRead += record.usage.cacheRead;\n\t\t\ttotals.cacheWrite += record.usage.cacheWrite;\n\t\t\ttotals.costUsd += record.usage.costUsd;\n\t\t}\n\t\tturnDurations.push(record.durationMs);\n\t\tif (typeof record.timeToFirstChunkMs === \"number\") firstChunkTimes.push(record.timeToFirstChunkMs);\n\t\tif (record.compacted) compactions++;\n\t\tfailovers += record.failovers ?? 0;\n\t\tif (record.contextCache) {\n\t\t\tplanObservations++;\n\t\t\tif (record.contextCache.planHit) planHits++;\n\t\t}\n\t\tconst provenance = record.runtimeProvenance;\n\t\tif (\n\t\t\tprovenance !== undefined &&\n\t\t\tprovenance !== null &&\n\t\t\tprovenance.responseModel !== null &&\n\t\t\tprovenance.responseModel !== provenance.selectedModel\n\t\t) {\n\t\t\tprovenanceMismatchTurns++;\n\t\t}\n\t\tfor (const call of record.toolCalls ?? []) {\n\t\t\tconst stats = toolStats.get(call.name) ?? { durations: [], failures: 0 };\n\t\t\tstats.durations.push(call.durationMs);\n\t\t\tif (!call.ok) stats.failures++;\n\t\t\ttoolStats.set(call.name, stats);\n\t\t}\n\t}\n\n\tturnDurations.sort((a, b) => a - b);\n\tfirstChunkTimes.sort((a, b) => a - b);\n\n\tconst tools: ToolAggregate[] = [...toolStats.entries()]\n\t\t.map(([name, stats]) => {\n\t\t\tconst sorted = [...stats.durations].sort((a, b) => a - b);\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\tcalls: sorted.length,\n\t\t\t\tfailures: stats.failures,\n\t\t\t\tfailureRate: sorted.length === 0 ? 0 : stats.failures / sorted.length,\n\t\t\t\tp50Ms: percentile(sorted, 0.5),\n\t\t\t\tp95Ms: percentile(sorted, 0.95),\n\t\t\t\ttotalMs: sorted.reduce((sum, value) => sum + value, 0),\n\t\t\t};\n\t\t})\n\t\t.sort((a, b) => b.totalMs - a.totalMs || a.name.localeCompare(b.name));\n\n\tconst promptSideTotal = totals.input + totals.cacheRead;\n\treturn {\n\t\tturns: records.length,\n\t\tsessions: sessions.size,\n\t\tmodels: [...models].sort(),\n\t\ttotalCostUsd: totals.costUsd,\n\t\ttotals,\n\t\tturnDurationP50Ms: percentile(turnDurations, 0.5),\n\t\tturnDurationP95Ms: percentile(turnDurations, 0.95),\n\t\ttimeToFirstChunkP50Ms: firstChunkTimes.length > 0 ? percentile(firstChunkTimes, 0.5) : undefined,\n\t\tcacheReadShare: promptSideTotal === 0 ? 0 : totals.cacheRead / promptSideTotal,\n\t\tcompactions,\n\t\tfailovers,\n\t\tcontextCachePlanHitRate: planObservations === 0 ? undefined : planHits / planObservations,\n\t\tprovenanceMismatchTurns,\n\t\ttools,\n\t\tmalformedLines,\n\t};\n}\n\n/** Read and summarize a metrics file. A missing file summarizes as zero turns. */\nexport function summarizeTurnMetricsFile(filePath: string): TurnMetricsSummary {\n\tlet raw: string;\n\ttry {\n\t\traw = fs.readFileSync(filePath, \"utf8\");\n\t} catch {\n\t\treturn summarizeTurnMetrics([]);\n\t}\n\treturn summarizeTurnMetrics(raw.split(\"\\n\"));\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { RunContract } from "omk-protocol";
2
+ import type { NamespaceIdentity } from "./namespace-identity.ts";
3
+ export interface SandboxExecution {
4
+ readonly workspace: string;
5
+ readonly argv: readonly string[];
6
+ readonly writable: boolean;
7
+ readonly timeoutMs: number;
8
+ readonly cleanupMs: number;
9
+ readonly maxOutputBytes: number;
10
+ readonly signal?: AbortSignal;
11
+ readonly onReady?: (identity: NamespaceIdentity) => void | Promise<void>;
12
+ }
13
+ export interface SandboxOutcome {
14
+ readonly stdout: Buffer;
15
+ readonly stderr: Buffer;
16
+ readonly exitCode: number | null;
17
+ readonly failure: string | null;
18
+ readonly startedAt: string;
19
+ readonly finishedAt: string;
20
+ readonly durationMs: number;
21
+ }
22
+ export declare function commandEnvironmentDigest(contract: RunContract, driver?: "legacy" | "gated-v1"): string;
23
+ export declare function probeVerifiedSandbox(): void;
24
+ /** The only process dispatch port; close means the PID namespace and its inherited pipes have drained. */
25
+ export declare function executeSandbox(request: SandboxExecution): Promise<SandboxOutcome>;
26
+ //# sourceMappingURL=broker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/broker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAsCjE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE;AACD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,GAAE,QAAQ,GAAG,UAAqB,GAAG,MAAM,CA0ChH;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAS3C;AAED,0GAA0G;AAC1G,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAqGvF","sourcesContent":["import { spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport { release } from \"node:os\";\nimport { performance } from \"node:perf_hooks\";\nimport type { RunContract } from \"omk-protocol\";\nimport type { NamespaceIdentity } from \"./namespace-identity.ts\";\nimport { identityFromSandboxInfo, PROCESS_GATE_ARGV } from \"./process-gate.ts\";\nimport { digestBytes, digestObject, VerifiedRunError } from \"./storage.ts\";\n\nconst BWRAP = \"/usr/bin/bwrap\";\nconst SYSTEM_ARGS = [\n\t\"--unshare-all\",\n\t\"--die-with-parent\",\n\t\"--new-session\",\n\t\"--cap-drop\",\n\t\"ALL\",\n\t\"--clearenv\",\n\t\"--ro-bind\",\n\t\"/usr\",\n\t\"/usr\",\n\t\"--symlink\",\n\t\"usr/bin\",\n\t\"/bin\",\n\t\"--symlink\",\n\t\"usr/lib\",\n\t\"/lib\",\n\t\"--symlink\",\n\t\"usr/lib64\",\n\t\"/lib64\",\n\t\"--proc\",\n\t\"/proc\",\n\t\"--dev\",\n\t\"/dev\",\n\t\"--tmpfs\",\n\t\"/tmp\",\n\t\"--setenv\",\n\t\"PATH\",\n\t\"/usr/bin:/bin\",\n\t\"--setenv\",\n\t\"LANG\",\n\t\"C.UTF-8\",\n] as const;\n\nexport interface SandboxExecution {\n\treadonly workspace: string;\n\treadonly argv: readonly string[];\n\treadonly writable: boolean;\n\treadonly timeoutMs: number;\n\treadonly cleanupMs: number;\n\treadonly maxOutputBytes: number;\n\treadonly signal?: AbortSignal;\n\treadonly onReady?: (identity: NamespaceIdentity) => void | Promise<void>;\n}\nexport interface SandboxOutcome {\n\treadonly stdout: Buffer;\n\treadonly stderr: Buffer;\n\treadonly exitCode: number | null;\n\treadonly failure: string | null;\n\treadonly startedAt: string;\n\treadonly finishedAt: string;\n\treadonly durationMs: number;\n}\n\nexport function commandEnvironmentDigest(contract: RunContract, driver: \"legacy\" | \"gated-v1\" = \"legacy\"): string {\n\tif (process.platform !== \"linux\" || !existsSync(BWRAP)) throw new VerifiedRunError(\"unsupported\");\n\tlet writers: readonly (readonly string[])[];\n\tswitch (contract.profile) {\n\t\tcase \"linux-command-v1\":\n\t\t\twriters = [contract.writer];\n\t\t\tbreak;\n\t\tcase \"linux-scripted-agent-v1\":\n\t\t\twriters = contract.writer.steps;\n\t\t\tbreak;\n\t\tcase \"linux-command-dag-v1\":\n\t\t\twriters = contract.writer.tasks.flatMap((task) => task.attempts);\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = contract;\n\t\t\tthrow new VerifiedRunError(String(exhaustive));\n\t\t}\n\t}\n\tconst binaries = [\n\t\t...new Set([\n\t\t\tBWRAP,\n\t\t\t...writers.map((argv) => argv[0]),\n\t\t\t...contract.checks.map((check) => check.argv[0]),\n\t\t\t...(driver === \"gated-v1\" ? [PROCESS_GATE_ARGV[0]] : []),\n\t\t]),\n\t];\n\tconst materials = binaries.map((binary) => {\n\t\tif (!binary || (!binary.startsWith(\"/usr/bin/\") && !binary.startsWith(\"/bin/\")))\n\t\t\tthrow new VerifiedRunError(\"unsupported_executable\");\n\t\tconst path = realpathSync(binary);\n\t\tif (!path.startsWith(\"/usr/\")) throw new VerifiedRunError(\"unsupported_executable\");\n\t\treturn { path, digest: digestBytes(readFileSync(path)) };\n\t});\n\treturn digestObject({\n\t\tprofile: contract.profile,\n\t\tplatform: process.platform,\n\t\tarchitecture: process.arch,\n\t\tkernel: release(),\n\t\tsandbox: SYSTEM_ARGS,\n\t\t...(driver === \"gated-v1\" ? { gate: PROCESS_GATE_ARGV, infoFd: 3 } : {}),\n\t\tbinaries: materials,\n\t});\n}\n\nexport function probeVerifiedSandbox(): void {\n\tif (process.platform !== \"linux\" || !existsSync(BWRAP)) throw new VerifiedRunError(\"unsupported\");\n\tconst probe = spawnSync(BWRAP, [...SYSTEM_ARGS, \"--\", \"/bin/true\"], {\n\t\tenv: {},\n\t\ttimeout: 3000,\n\t\tmaxBuffer: 4096,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t});\n\tif (probe.error || probe.status !== 0) throw new VerifiedRunError(\"unsupported\");\n}\n\n/** The only process dispatch port; close means the PID namespace and its inherited pipes have drained. */\nexport async function executeSandbox(request: SandboxExecution): Promise<SandboxOutcome> {\n\tif (request.signal?.aborted) throw new VerifiedRunError(\"cancelled\");\n\tif (request.timeoutMs <= 0) throw new VerifiedRunError(\"deadline\");\n\tconst started = performance.now();\n\tconst startedAt = new Date().toISOString();\n\tconst argv = [\n\t\t...SYSTEM_ARGS,\n\t\trequest.writable ? \"--bind\" : \"--ro-bind\",\n\t\trequest.workspace,\n\t\t\"/workspace\",\n\t\t\"--chdir\",\n\t\t\"/workspace\",\n\t\t...(request.onReady ? [\"--info-fd\", \"3\", \"--\", ...PROCESS_GATE_ARGV] : [\"--\"]),\n\t\t...request.argv,\n\t];\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = request.onReady\n\t\t\t? spawn(BWRAP, argv, { env: {}, stdio: [\"pipe\", \"pipe\", \"pipe\", \"pipe\"] })\n\t\t\t: spawn(BWRAP, argv, { env: {}, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\tlet gateFailed = false;\n\t\tlet gateError: unknown;\n\t\tconst stdout: Buffer[] = [];\n\t\tconst stderr: Buffer[] = [];\n\t\tlet size = 0;\n\t\tlet failure: string | null = null;\n\t\tlet cleanup: ReturnType<typeof setTimeout> | undefined;\n\t\tlet finished = false;\n\t\tconst stop = (reason: string): void => {\n\t\t\tif (finished || failure) return;\n\t\t\tfailure = reason;\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t\tcleanup = setTimeout(() => reject(new VerifiedRunError(\"unsettled\")), request.cleanupMs);\n\t\t};\n\t\tconst deadline = setTimeout(() => stop(\"deadline\"), request.timeoutMs);\n\t\tconst abort = (): void => stop(\"cancelled\");\n\t\trequest.signal?.addEventListener(\"abort\", abort, { once: true });\n\t\tif (request.signal?.aborted) abort();\n\t\tconst collect = (target: Buffer[], chunk: Buffer): void => {\n\t\t\tif (failure) return;\n\t\t\tsize += chunk.length;\n\t\t\tif (size > request.maxOutputBytes) {\n\t\t\t\tstop(\"output_limit\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttarget.push(chunk);\n\t\t};\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => collect(stdout, chunk));\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => collect(stderr, chunk));\n\t\tif (request.onReady) {\n\t\t\tconst info = child.stdio[3];\n\t\t\tlet metadata = \"\";\n\t\t\tif (!info || !child.stdin) {\n\t\t\t\tgateFailed = true;\n\t\t\t\tgateError = new VerifiedRunError(\"process_identity\");\n\t\t\t\tstop(\"process_identity\");\n\t\t\t} else {\n\t\t\t\tchild.stdin.on(\"error\", () => stop(\"execution_failed\"));\n\t\t\t\tinfo.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tmetadata += chunk.toString(\"utf8\");\n\t\t\t\t\tif (metadata.length > 4096) stop(\"process_identity\");\n\t\t\t\t});\n\t\t\t\tinfo.once(\"end\", async () => {\n\t\t\t\t\tif (finished || failure) return;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait request.onReady?.(identityFromSandboxInfo(metadata));\n\t\t\t\t\t\tif (performance.now() - started >= request.timeoutMs || request.signal?.aborted)\n\t\t\t\t\t\t\tstop(request.signal?.aborted ? \"cancelled\" : \"deadline\");\n\t\t\t\t\t\tif (!finished && !failure) child.stdin?.end(\"go\\n\");\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tgateFailed = true;\n\t\t\t\t\t\tgateError = error;\n\t\t\t\t\t\tstop(\"process_identity\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tchild.once(\"error\", () => {\n\t\t\tfailure = \"execution_failed\";\n\t\t});\n\t\tchild.once(\"close\", (exitCode) => {\n\t\t\tfinished = true;\n\t\t\tclearTimeout(deadline);\n\t\t\tclearTimeout(cleanup);\n\t\t\trequest.signal?.removeEventListener(\"abort\", abort);\n\t\t\tif (gateFailed) {\n\t\t\t\treject(gateError);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst durationMs = Math.max(0, Math.round(performance.now() - started));\n\t\t\tresolve({\n\t\t\t\tstdout: Buffer.concat(stdout),\n\t\t\t\tstderr: Buffer.concat(stderr),\n\t\t\t\texitCode,\n\t\t\t\tfailure: failure ?? (exitCode === 0 ? null : \"execution_failed\"),\n\t\t\t\tstartedAt,\n\t\t\t\t// Project the finish instant from the start's wall reference and monotonic elapsed time.\n\t\t\t\tfinishedAt: new Date(Date.parse(startedAt) + durationMs).toISOString(),\n\t\t\t\tdurationMs,\n\t\t\t});\n\t\t});\n\t});\n}\n"]}
@@ -0,0 +1,210 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
3
+ import { release } from "node:os";
4
+ import { performance } from "node:perf_hooks";
5
+ import { identityFromSandboxInfo, PROCESS_GATE_ARGV } from "./process-gate.js";
6
+ import { digestBytes, digestObject, VerifiedRunError } from "./storage.js";
7
+ const BWRAP = "/usr/bin/bwrap";
8
+ const SYSTEM_ARGS = [
9
+ "--unshare-all",
10
+ "--die-with-parent",
11
+ "--new-session",
12
+ "--cap-drop",
13
+ "ALL",
14
+ "--clearenv",
15
+ "--ro-bind",
16
+ "/usr",
17
+ "/usr",
18
+ "--symlink",
19
+ "usr/bin",
20
+ "/bin",
21
+ "--symlink",
22
+ "usr/lib",
23
+ "/lib",
24
+ "--symlink",
25
+ "usr/lib64",
26
+ "/lib64",
27
+ "--proc",
28
+ "/proc",
29
+ "--dev",
30
+ "/dev",
31
+ "--tmpfs",
32
+ "/tmp",
33
+ "--setenv",
34
+ "PATH",
35
+ "/usr/bin:/bin",
36
+ "--setenv",
37
+ "LANG",
38
+ "C.UTF-8",
39
+ ];
40
+ export function commandEnvironmentDigest(contract, driver = "legacy") {
41
+ if (process.platform !== "linux" || !existsSync(BWRAP))
42
+ throw new VerifiedRunError("unsupported");
43
+ let writers;
44
+ switch (contract.profile) {
45
+ case "linux-command-v1":
46
+ writers = [contract.writer];
47
+ break;
48
+ case "linux-scripted-agent-v1":
49
+ writers = contract.writer.steps;
50
+ break;
51
+ case "linux-command-dag-v1":
52
+ writers = contract.writer.tasks.flatMap((task) => task.attempts);
53
+ break;
54
+ default: {
55
+ const exhaustive = contract;
56
+ throw new VerifiedRunError(String(exhaustive));
57
+ }
58
+ }
59
+ const binaries = [
60
+ ...new Set([
61
+ BWRAP,
62
+ ...writers.map((argv) => argv[0]),
63
+ ...contract.checks.map((check) => check.argv[0]),
64
+ ...(driver === "gated-v1" ? [PROCESS_GATE_ARGV[0]] : []),
65
+ ]),
66
+ ];
67
+ const materials = binaries.map((binary) => {
68
+ if (!binary || (!binary.startsWith("/usr/bin/") && !binary.startsWith("/bin/")))
69
+ throw new VerifiedRunError("unsupported_executable");
70
+ const path = realpathSync(binary);
71
+ if (!path.startsWith("/usr/"))
72
+ throw new VerifiedRunError("unsupported_executable");
73
+ return { path, digest: digestBytes(readFileSync(path)) };
74
+ });
75
+ return digestObject({
76
+ profile: contract.profile,
77
+ platform: process.platform,
78
+ architecture: process.arch,
79
+ kernel: release(),
80
+ sandbox: SYSTEM_ARGS,
81
+ ...(driver === "gated-v1" ? { gate: PROCESS_GATE_ARGV, infoFd: 3 } : {}),
82
+ binaries: materials,
83
+ });
84
+ }
85
+ export function probeVerifiedSandbox() {
86
+ if (process.platform !== "linux" || !existsSync(BWRAP))
87
+ throw new VerifiedRunError("unsupported");
88
+ const probe = spawnSync(BWRAP, [...SYSTEM_ARGS, "--", "/bin/true"], {
89
+ env: {},
90
+ timeout: 3000,
91
+ maxBuffer: 4096,
92
+ stdio: ["ignore", "pipe", "pipe"],
93
+ });
94
+ if (probe.error || probe.status !== 0)
95
+ throw new VerifiedRunError("unsupported");
96
+ }
97
+ /** The only process dispatch port; close means the PID namespace and its inherited pipes have drained. */
98
+ export async function executeSandbox(request) {
99
+ if (request.signal?.aborted)
100
+ throw new VerifiedRunError("cancelled");
101
+ if (request.timeoutMs <= 0)
102
+ throw new VerifiedRunError("deadline");
103
+ const started = performance.now();
104
+ const startedAt = new Date().toISOString();
105
+ const argv = [
106
+ ...SYSTEM_ARGS,
107
+ request.writable ? "--bind" : "--ro-bind",
108
+ request.workspace,
109
+ "/workspace",
110
+ "--chdir",
111
+ "/workspace",
112
+ ...(request.onReady ? ["--info-fd", "3", "--", ...PROCESS_GATE_ARGV] : ["--"]),
113
+ ...request.argv,
114
+ ];
115
+ return new Promise((resolve, reject) => {
116
+ const child = request.onReady
117
+ ? spawn(BWRAP, argv, { env: {}, stdio: ["pipe", "pipe", "pipe", "pipe"] })
118
+ : spawn(BWRAP, argv, { env: {}, stdio: ["ignore", "pipe", "pipe"] });
119
+ let gateFailed = false;
120
+ let gateError;
121
+ const stdout = [];
122
+ const stderr = [];
123
+ let size = 0;
124
+ let failure = null;
125
+ let cleanup;
126
+ let finished = false;
127
+ const stop = (reason) => {
128
+ if (finished || failure)
129
+ return;
130
+ failure = reason;
131
+ child.kill("SIGKILL");
132
+ cleanup = setTimeout(() => reject(new VerifiedRunError("unsettled")), request.cleanupMs);
133
+ };
134
+ const deadline = setTimeout(() => stop("deadline"), request.timeoutMs);
135
+ const abort = () => stop("cancelled");
136
+ request.signal?.addEventListener("abort", abort, { once: true });
137
+ if (request.signal?.aborted)
138
+ abort();
139
+ const collect = (target, chunk) => {
140
+ if (failure)
141
+ return;
142
+ size += chunk.length;
143
+ if (size > request.maxOutputBytes) {
144
+ stop("output_limit");
145
+ return;
146
+ }
147
+ target.push(chunk);
148
+ };
149
+ child.stdout?.on("data", (chunk) => collect(stdout, chunk));
150
+ child.stderr?.on("data", (chunk) => collect(stderr, chunk));
151
+ if (request.onReady) {
152
+ const info = child.stdio[3];
153
+ let metadata = "";
154
+ if (!info || !child.stdin) {
155
+ gateFailed = true;
156
+ gateError = new VerifiedRunError("process_identity");
157
+ stop("process_identity");
158
+ }
159
+ else {
160
+ child.stdin.on("error", () => stop("execution_failed"));
161
+ info.on("data", (chunk) => {
162
+ metadata += chunk.toString("utf8");
163
+ if (metadata.length > 4096)
164
+ stop("process_identity");
165
+ });
166
+ info.once("end", async () => {
167
+ if (finished || failure)
168
+ return;
169
+ try {
170
+ await request.onReady?.(identityFromSandboxInfo(metadata));
171
+ if (performance.now() - started >= request.timeoutMs || request.signal?.aborted)
172
+ stop(request.signal?.aborted ? "cancelled" : "deadline");
173
+ if (!finished && !failure)
174
+ child.stdin?.end("go\n");
175
+ }
176
+ catch (error) {
177
+ gateFailed = true;
178
+ gateError = error;
179
+ stop("process_identity");
180
+ }
181
+ });
182
+ }
183
+ }
184
+ child.once("error", () => {
185
+ failure = "execution_failed";
186
+ });
187
+ child.once("close", (exitCode) => {
188
+ finished = true;
189
+ clearTimeout(deadline);
190
+ clearTimeout(cleanup);
191
+ request.signal?.removeEventListener("abort", abort);
192
+ if (gateFailed) {
193
+ reject(gateError);
194
+ return;
195
+ }
196
+ const durationMs = Math.max(0, Math.round(performance.now() - started));
197
+ resolve({
198
+ stdout: Buffer.concat(stdout),
199
+ stderr: Buffer.concat(stderr),
200
+ exitCode,
201
+ failure: failure ?? (exitCode === 0 ? null : "execution_failed"),
202
+ startedAt,
203
+ // Project the finish instant from the start's wall reference and monotonic elapsed time.
204
+ finishedAt: new Date(Date.parse(startedAt) + durationMs).toISOString(),
205
+ durationMs,
206
+ });
207
+ });
208
+ });
209
+ }
210
+ //# sourceMappingURL=broker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker.js","sourceRoot":"","sources":["../../../src/core/verified-run/broker.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport { release } from \"node:os\";\nimport { performance } from \"node:perf_hooks\";\nimport type { RunContract } from \"omk-protocol\";\nimport type { NamespaceIdentity } from \"./namespace-identity.ts\";\nimport { identityFromSandboxInfo, PROCESS_GATE_ARGV } from \"./process-gate.ts\";\nimport { digestBytes, digestObject, VerifiedRunError } from \"./storage.ts\";\n\nconst BWRAP = \"/usr/bin/bwrap\";\nconst SYSTEM_ARGS = [\n\t\"--unshare-all\",\n\t\"--die-with-parent\",\n\t\"--new-session\",\n\t\"--cap-drop\",\n\t\"ALL\",\n\t\"--clearenv\",\n\t\"--ro-bind\",\n\t\"/usr\",\n\t\"/usr\",\n\t\"--symlink\",\n\t\"usr/bin\",\n\t\"/bin\",\n\t\"--symlink\",\n\t\"usr/lib\",\n\t\"/lib\",\n\t\"--symlink\",\n\t\"usr/lib64\",\n\t\"/lib64\",\n\t\"--proc\",\n\t\"/proc\",\n\t\"--dev\",\n\t\"/dev\",\n\t\"--tmpfs\",\n\t\"/tmp\",\n\t\"--setenv\",\n\t\"PATH\",\n\t\"/usr/bin:/bin\",\n\t\"--setenv\",\n\t\"LANG\",\n\t\"C.UTF-8\",\n] as const;\n\nexport interface SandboxExecution {\n\treadonly workspace: string;\n\treadonly argv: readonly string[];\n\treadonly writable: boolean;\n\treadonly timeoutMs: number;\n\treadonly cleanupMs: number;\n\treadonly maxOutputBytes: number;\n\treadonly signal?: AbortSignal;\n\treadonly onReady?: (identity: NamespaceIdentity) => void | Promise<void>;\n}\nexport interface SandboxOutcome {\n\treadonly stdout: Buffer;\n\treadonly stderr: Buffer;\n\treadonly exitCode: number | null;\n\treadonly failure: string | null;\n\treadonly startedAt: string;\n\treadonly finishedAt: string;\n\treadonly durationMs: number;\n}\n\nexport function commandEnvironmentDigest(contract: RunContract, driver: \"legacy\" | \"gated-v1\" = \"legacy\"): string {\n\tif (process.platform !== \"linux\" || !existsSync(BWRAP)) throw new VerifiedRunError(\"unsupported\");\n\tlet writers: readonly (readonly string[])[];\n\tswitch (contract.profile) {\n\t\tcase \"linux-command-v1\":\n\t\t\twriters = [contract.writer];\n\t\t\tbreak;\n\t\tcase \"linux-scripted-agent-v1\":\n\t\t\twriters = contract.writer.steps;\n\t\t\tbreak;\n\t\tcase \"linux-command-dag-v1\":\n\t\t\twriters = contract.writer.tasks.flatMap((task) => task.attempts);\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = contract;\n\t\t\tthrow new VerifiedRunError(String(exhaustive));\n\t\t}\n\t}\n\tconst binaries = [\n\t\t...new Set([\n\t\t\tBWRAP,\n\t\t\t...writers.map((argv) => argv[0]),\n\t\t\t...contract.checks.map((check) => check.argv[0]),\n\t\t\t...(driver === \"gated-v1\" ? [PROCESS_GATE_ARGV[0]] : []),\n\t\t]),\n\t];\n\tconst materials = binaries.map((binary) => {\n\t\tif (!binary || (!binary.startsWith(\"/usr/bin/\") && !binary.startsWith(\"/bin/\")))\n\t\t\tthrow new VerifiedRunError(\"unsupported_executable\");\n\t\tconst path = realpathSync(binary);\n\t\tif (!path.startsWith(\"/usr/\")) throw new VerifiedRunError(\"unsupported_executable\");\n\t\treturn { path, digest: digestBytes(readFileSync(path)) };\n\t});\n\treturn digestObject({\n\t\tprofile: contract.profile,\n\t\tplatform: process.platform,\n\t\tarchitecture: process.arch,\n\t\tkernel: release(),\n\t\tsandbox: SYSTEM_ARGS,\n\t\t...(driver === \"gated-v1\" ? { gate: PROCESS_GATE_ARGV, infoFd: 3 } : {}),\n\t\tbinaries: materials,\n\t});\n}\n\nexport function probeVerifiedSandbox(): void {\n\tif (process.platform !== \"linux\" || !existsSync(BWRAP)) throw new VerifiedRunError(\"unsupported\");\n\tconst probe = spawnSync(BWRAP, [...SYSTEM_ARGS, \"--\", \"/bin/true\"], {\n\t\tenv: {},\n\t\ttimeout: 3000,\n\t\tmaxBuffer: 4096,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t});\n\tif (probe.error || probe.status !== 0) throw new VerifiedRunError(\"unsupported\");\n}\n\n/** The only process dispatch port; close means the PID namespace and its inherited pipes have drained. */\nexport async function executeSandbox(request: SandboxExecution): Promise<SandboxOutcome> {\n\tif (request.signal?.aborted) throw new VerifiedRunError(\"cancelled\");\n\tif (request.timeoutMs <= 0) throw new VerifiedRunError(\"deadline\");\n\tconst started = performance.now();\n\tconst startedAt = new Date().toISOString();\n\tconst argv = [\n\t\t...SYSTEM_ARGS,\n\t\trequest.writable ? \"--bind\" : \"--ro-bind\",\n\t\trequest.workspace,\n\t\t\"/workspace\",\n\t\t\"--chdir\",\n\t\t\"/workspace\",\n\t\t...(request.onReady ? [\"--info-fd\", \"3\", \"--\", ...PROCESS_GATE_ARGV] : [\"--\"]),\n\t\t...request.argv,\n\t];\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = request.onReady\n\t\t\t? spawn(BWRAP, argv, { env: {}, stdio: [\"pipe\", \"pipe\", \"pipe\", \"pipe\"] })\n\t\t\t: spawn(BWRAP, argv, { env: {}, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\tlet gateFailed = false;\n\t\tlet gateError: unknown;\n\t\tconst stdout: Buffer[] = [];\n\t\tconst stderr: Buffer[] = [];\n\t\tlet size = 0;\n\t\tlet failure: string | null = null;\n\t\tlet cleanup: ReturnType<typeof setTimeout> | undefined;\n\t\tlet finished = false;\n\t\tconst stop = (reason: string): void => {\n\t\t\tif (finished || failure) return;\n\t\t\tfailure = reason;\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t\tcleanup = setTimeout(() => reject(new VerifiedRunError(\"unsettled\")), request.cleanupMs);\n\t\t};\n\t\tconst deadline = setTimeout(() => stop(\"deadline\"), request.timeoutMs);\n\t\tconst abort = (): void => stop(\"cancelled\");\n\t\trequest.signal?.addEventListener(\"abort\", abort, { once: true });\n\t\tif (request.signal?.aborted) abort();\n\t\tconst collect = (target: Buffer[], chunk: Buffer): void => {\n\t\t\tif (failure) return;\n\t\t\tsize += chunk.length;\n\t\t\tif (size > request.maxOutputBytes) {\n\t\t\t\tstop(\"output_limit\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttarget.push(chunk);\n\t\t};\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => collect(stdout, chunk));\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => collect(stderr, chunk));\n\t\tif (request.onReady) {\n\t\t\tconst info = child.stdio[3];\n\t\t\tlet metadata = \"\";\n\t\t\tif (!info || !child.stdin) {\n\t\t\t\tgateFailed = true;\n\t\t\t\tgateError = new VerifiedRunError(\"process_identity\");\n\t\t\t\tstop(\"process_identity\");\n\t\t\t} else {\n\t\t\t\tchild.stdin.on(\"error\", () => stop(\"execution_failed\"));\n\t\t\t\tinfo.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tmetadata += chunk.toString(\"utf8\");\n\t\t\t\t\tif (metadata.length > 4096) stop(\"process_identity\");\n\t\t\t\t});\n\t\t\t\tinfo.once(\"end\", async () => {\n\t\t\t\t\tif (finished || failure) return;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait request.onReady?.(identityFromSandboxInfo(metadata));\n\t\t\t\t\t\tif (performance.now() - started >= request.timeoutMs || request.signal?.aborted)\n\t\t\t\t\t\t\tstop(request.signal?.aborted ? \"cancelled\" : \"deadline\");\n\t\t\t\t\t\tif (!finished && !failure) child.stdin?.end(\"go\\n\");\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tgateFailed = true;\n\t\t\t\t\t\tgateError = error;\n\t\t\t\t\t\tstop(\"process_identity\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tchild.once(\"error\", () => {\n\t\t\tfailure = \"execution_failed\";\n\t\t});\n\t\tchild.once(\"close\", (exitCode) => {\n\t\t\tfinished = true;\n\t\t\tclearTimeout(deadline);\n\t\t\tclearTimeout(cleanup);\n\t\t\trequest.signal?.removeEventListener(\"abort\", abort);\n\t\t\tif (gateFailed) {\n\t\t\t\treject(gateError);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst durationMs = Math.max(0, Math.round(performance.now() - started));\n\t\t\tresolve({\n\t\t\t\tstdout: Buffer.concat(stdout),\n\t\t\t\tstderr: Buffer.concat(stderr),\n\t\t\t\texitCode,\n\t\t\t\tfailure: failure ?? (exitCode === 0 ? null : \"execution_failed\"),\n\t\t\t\tstartedAt,\n\t\t\t\t// Project the finish instant from the start's wall reference and monotonic elapsed time.\n\t\t\t\tfinishedAt: new Date(Date.parse(startedAt) + durationMs).toISOString(),\n\t\t\t\tdurationMs,\n\t\t\t});\n\t\t});\n\t});\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { RunPhaseBudget } from "omk-protocol";
2
+ export interface CandidateFile {
3
+ readonly path: string;
4
+ readonly mode: number;
5
+ readonly digest: string;
6
+ readonly size: number;
7
+ }
8
+ export interface CandidateManifest {
9
+ readonly version: 1;
10
+ readonly directories: readonly string[];
11
+ readonly files: readonly CandidateFile[];
12
+ }
13
+ export interface CandidateSnapshot {
14
+ readonly manifest: CandidateManifest;
15
+ readonly digest: string;
16
+ readonly contents: ReadonlyMap<string, Buffer>;
17
+ }
18
+ /** Entire regular-file tree, including dot/untracked files; .git/.omk metadata is excluded by this profile. */
19
+ export declare function captureCandidate(root: string, limits: RunPhaseBudget): CandidateSnapshot;
20
+ export declare function materializeCandidate(snapshot: CandidateSnapshot, target: string): void;
21
+ export declare function assertCandidateScope(base: CandidateManifest, candidate: CandidateManifest, writable: readonly string[]): void;
22
+ export declare function storeCandidate(snapshot: CandidateSnapshot, runPath: string): void;
23
+ export declare function loadCandidate(runPath: string, digest: string, limits: RunPhaseBudget): CandidateSnapshot;
24
+ //# sourceMappingURL=candidate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"candidate.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/candidate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAYnD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;CACzC;AACD,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAYD,+GAA+G;AAC/G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,iBAAiB,CAsCxF;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAStF;AAED,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,iBAAiB,EACvB,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,EAAE,SAAS,MAAM,EAAE,GACzB,IAAI,CAWN;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAGjF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,iBAAiB,CAyExG","sourcesContent":["import { chmodSync, lstatSync, mkdirSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { RunPhaseBudget } from \"omk-protocol\";\nimport {\n\tassertDirectory,\n\tdigestBytes,\n\tdigestObject,\n\tpublishBytes,\n\tpublishObject,\n\treadJson,\n\treadRegularFile,\n\tVerifiedRunError,\n} from \"./storage.ts\";\n\nexport interface CandidateFile {\n\treadonly path: string;\n\treadonly mode: number;\n\treadonly digest: string;\n\treadonly size: number;\n}\nexport interface CandidateManifest {\n\treadonly version: 1;\n\treadonly directories: readonly string[];\n\treadonly files: readonly CandidateFile[];\n}\nexport interface CandidateSnapshot {\n\treadonly manifest: CandidateManifest;\n\treadonly digest: string;\n\treadonly contents: ReadonlyMap<string, Buffer>;\n}\n\nfunction safePath(path: string): void {\n\tif (\n\t\t!path ||\n\t\t/[\\\\\\u0000-\\u001f\\u007f]/.test(path) ||\n\t\tpath.split(\"/\").some((part) => [\"\", \".\", \"..\", \".git\", \".omk\"].includes(part))\n\t) {\n\t\tthrow new VerifiedRunError(\"file_type\");\n\t}\n}\n\n/** Entire regular-file tree, including dot/untracked files; .git/.omk metadata is excluded by this profile. */\nexport function captureCandidate(root: string, limits: RunPhaseBudget): CandidateSnapshot {\n\tassertDirectory(root);\n\tconst files: CandidateFile[] = [];\n\tconst directories: string[] = [];\n\tconst contents = new Map<string, Buffer>();\n\tlet bytesUsed = 0;\n\tconst visit = (prefix: string): void => {\n\t\tconst names = readdirSync(join(root, prefix), { encoding: \"buffer\" })\n\t\t\t.map((name) => new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: true }).decode(name))\n\t\t\t.sort();\n\t\tfor (const name of names) {\n\t\t\tif (!prefix && (name === \".git\" || name === \".omk\")) continue;\n\t\t\tconst path = prefix ? `${prefix}/${name}` : name;\n\t\t\tsafePath(path);\n\t\t\tif (files.length + directories.length >= limits.maxFiles) throw new VerifiedRunError(\"storage_limit\");\n\t\t\tconst absolute = join(root, path);\n\t\t\tconst stat = lstatSync(absolute);\n\t\t\tif (stat.isDirectory()) {\n\t\t\t\tif ((stat.mode & 0o7777) !== 0o755) throw new VerifiedRunError(\"file_type\");\n\t\t\t\tdirectories.push(path);\n\t\t\t\tvisit(path);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!stat.isFile() || stat.nlink !== 1 || (stat.mode & 0o7000) !== 0) throw new VerifiedRunError(\"file_type\");\n\t\t\tconst bytes = readRegularFile(absolute, limits.maxBytes - bytesUsed);\n\t\t\tbytesUsed += bytes.length;\n\t\t\tconst digest = digestBytes(bytes);\n\t\t\tcontents.set(digest, bytes);\n\t\t\tfiles.push(Object.freeze({ path, mode: stat.mode & 0o777, digest, size: bytes.length }));\n\t\t}\n\t};\n\tvisit(\"\");\n\tconst manifest: CandidateManifest = Object.freeze({\n\t\tversion: 1,\n\t\tdirectories: Object.freeze(directories),\n\t\tfiles: Object.freeze(files),\n\t});\n\treturn { manifest, digest: digestObject(manifest), contents };\n}\n\nexport function materializeCandidate(snapshot: CandidateSnapshot, target: string): void {\n\tmkdirSync(target, { mode: 0o700 });\n\tfor (const directory of snapshot.manifest.directories) mkdirSync(join(target, directory), { mode: 0o755 });\n\tfor (const file of snapshot.manifest.files) {\n\t\tconst bytes = snapshot.contents.get(file.digest);\n\t\tif (!bytes) throw new VerifiedRunError(\"integrity\");\n\t\tpublishBytes(join(target, file.path), bytes);\n\t\tchmodSync(join(target, file.path), file.mode);\n\t}\n}\n\nexport function assertCandidateScope(\n\tbase: CandidateManifest,\n\tcandidate: CandidateManifest,\n\twritable: readonly string[],\n): void {\n\tconst before = new Map(base.files.map((file) => [file.path, digestObject(file)]));\n\tconst after = new Map(candidate.files.map((file) => [file.path, digestObject(file)]));\n\tfor (const directory of base.directories) before.set(`${directory}/`, \"directory\");\n\tfor (const directory of candidate.directories) after.set(`${directory}/`, \"directory\");\n\tfor (const path of new Set([...before.keys(), ...after.keys()])) {\n\t\tif (before.get(path) === after.get(path)) continue;\n\t\tconst normalized = path.endsWith(\"/\") ? path.slice(0, -1) : path;\n\t\tif (!writable.some((scope) => normalized === scope || normalized.startsWith(`${scope}/`)))\n\t\t\tthrow new VerifiedRunError(\"scope_changed\");\n\t}\n}\n\nexport function storeCandidate(snapshot: CandidateSnapshot, runPath: string): void {\n\tfor (const [digest, bytes] of snapshot.contents) publishBytes(join(runPath, \"blobs\", digest), bytes);\n\tpublishObject(join(runPath, \"candidates\", `${snapshot.digest}.json`), snapshot.manifest);\n}\n\nexport function loadCandidate(runPath: string, digest: string, limits: RunPhaseBudget): CandidateSnapshot {\n\tif (!/^[a-f0-9]{64}$/.test(digest)) throw new VerifiedRunError(\"integrity\");\n\tconst raw = readJson(join(runPath, \"candidates\", `${digest}.json`), 4194304);\n\tif (\n\t\tdigestObject(raw) !== digest ||\n\t\ttypeof raw !== \"object\" ||\n\t\traw === null ||\n\t\t!(\"version\" in raw) ||\n\t\traw.version !== 1 ||\n\t\t!(\"files\" in raw) ||\n\t\t!Array.isArray(raw.files) ||\n\t\t!(\"directories\" in raw) ||\n\t\t!Array.isArray(raw.directories)\n\t) {\n\t\tthrow new VerifiedRunError(\"integrity\");\n\t}\n\tif (raw.files.length + raw.directories.length > limits.maxFiles) throw new VerifiedRunError(\"integrity\");\n\tconst files: CandidateFile[] = [];\n\tconst directories: string[] = [];\n\tconst paths = new Set<string>();\n\tlet total = 0;\n\tfor (const directory of raw.directories) {\n\t\tif (typeof directory !== \"string\") throw new VerifiedRunError(\"integrity\");\n\t\tsafePath(directory);\n\t\tif (paths.has(directory)) throw new VerifiedRunError(\"integrity\");\n\t\tpaths.add(directory);\n\t\tdirectories.push(directory);\n\t}\n\tconst contents = new Map<string, Buffer>();\n\tconst rawFiles: readonly unknown[] = raw.files;\n\tfor (const file of rawFiles) {\n\t\tif (\n\t\t\ttypeof file !== \"object\" ||\n\t\t\tfile === null ||\n\t\t\t!(\"path\" in file) ||\n\t\t\ttypeof file.path !== \"string\" ||\n\t\t\t!(\"digest\" in file) ||\n\t\t\ttypeof file.digest !== \"string\" ||\n\t\t\t!/^[a-f0-9]{64}$/.test(file.digest) ||\n\t\t\t!(\"mode\" in file) ||\n\t\t\ttypeof file.mode !== \"number\" ||\n\t\t\t!Number.isSafeInteger(file.mode) ||\n\t\t\tfile.mode < 0 ||\n\t\t\tfile.mode > 0o777 ||\n\t\t\t!(\"size\" in file) ||\n\t\t\ttypeof file.size !== \"number\" ||\n\t\t\t!Number.isSafeInteger(file.size) ||\n\t\t\tfile.size < 0\n\t\t)\n\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\tsafePath(file.path);\n\t\tif (paths.has(file.path)) throw new VerifiedRunError(\"integrity\");\n\t\tpaths.add(file.path);\n\t\ttotal += file.size;\n\t\tif (total > limits.maxBytes) throw new VerifiedRunError(\"integrity\");\n\t\tlet bytes: Buffer;\n\t\ttry {\n\t\t\tbytes = readRegularFile(join(runPath, \"blobs\", file.digest), file.size);\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) throw new VerifiedRunError(\"integrity\");\n\t\t\tthrow error;\n\t\t}\n\t\tif (bytes.length !== file.size || digestBytes(bytes) !== file.digest) throw new VerifiedRunError(\"integrity\");\n\t\tcontents.set(file.digest, bytes);\n\t\tfiles.push(Object.freeze({ path: file.path, mode: file.mode, digest: file.digest, size: file.size }));\n\t}\n\tconst manifest: CandidateManifest = Object.freeze({\n\t\tversion: 1,\n\t\tdirectories: Object.freeze(directories),\n\t\tfiles: Object.freeze(files),\n\t});\n\tif (digestObject(manifest) !== digest) throw new VerifiedRunError(\"integrity\");\n\treturn { manifest, digest, contents };\n}\n"]}