harnery 0.43.0 → 0.45.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 (323) hide show
  1. package/README.md +3 -3
  2. package/dist/commander.js +1 -0
  3. package/dist/commands/agents.d.ts.map +1 -1
  4. package/dist/commands/agents.js +41 -48
  5. package/dist/commands/browse.d.ts.map +1 -1
  6. package/dist/commands/browse.js +38 -11
  7. package/dist/commands/diagnostics.d.ts.map +1 -1
  8. package/dist/commands/diagnostics.js +10 -0
  9. package/dist/commands/doctor.d.ts +12 -0
  10. package/dist/commands/doctor.d.ts.map +1 -1
  11. package/dist/commands/doctor.js +22 -7
  12. package/dist/commands/files.d.ts +2 -2
  13. package/dist/commands/files.d.ts.map +1 -1
  14. package/dist/commands/files.js +7 -4
  15. package/dist/commands/governor.js +22 -10
  16. package/dist/commands/inbox.d.ts +1 -1
  17. package/dist/commands/inbox.d.ts.map +1 -1
  18. package/dist/commands/inbox.js +55 -2
  19. package/dist/commands/rm.d.ts.map +1 -1
  20. package/dist/commands/rm.js +28 -4
  21. package/dist/commands/servers.d.ts.map +1 -1
  22. package/dist/commands/servers.js +26 -17
  23. package/dist/commands/supervisor.d.ts.map +1 -1
  24. package/dist/commands/supervisor.js +7 -2
  25. package/dist/commands/toc.d.ts +14 -0
  26. package/dist/commands/toc.d.ts.map +1 -1
  27. package/dist/commands/toc.js +7 -4
  28. package/dist/commands/tunnel.d.ts +15 -0
  29. package/dist/commands/tunnel.d.ts.map +1 -1
  30. package/dist/commands/tunnel.js +453 -153
  31. package/dist/commands/turn-rollback.d.ts +5 -0
  32. package/dist/commands/turn-rollback.d.ts.map +1 -0
  33. package/dist/commands/turn-rollback.js +76 -0
  34. package/dist/commands/workflow.d.ts.map +1 -1
  35. package/dist/commands/workflow.js +68 -30
  36. package/dist/core/agents/coord-client.d.ts.map +1 -1
  37. package/dist/core/agents/coord-client.js +18 -0
  38. package/dist/core/agents/live-authority-v3.d.ts +1 -0
  39. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  40. package/dist/core/agents/live-authority-v3.js +11 -0
  41. package/dist/core/agents/live-lifecycle-v3.d.ts +4 -2
  42. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
  43. package/dist/core/agents/live-lifecycle-v3.js +28 -7
  44. package/dist/core/agents/mailbox.d.ts +9 -8
  45. package/dist/core/agents/mailbox.d.ts.map +1 -1
  46. package/dist/core/agents/mailbox.js +24 -17
  47. package/dist/core/agents/render/prompt-context.d.ts +2 -0
  48. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  49. package/dist/core/agents/render/prompt-context.js +8 -12
  50. package/dist/core/agents/render/session-context.d.ts +2 -8
  51. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  52. package/dist/core/agents/render/session-context.js +7 -30
  53. package/dist/core/agents/rules/stop-hook.js +3 -3
  54. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  55. package/dist/core/agents/state/heartbeat-writer.js +25 -14
  56. package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
  57. package/dist/core/agents/state/live-coordination-writer.js +5 -0
  58. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  59. package/dist/core/agents/state/pidmap.js +5 -0
  60. package/dist/core/agents/state/proc-start.d.ts +19 -1
  61. package/dist/core/agents/state/proc-start.d.ts.map +1 -1
  62. package/dist/core/agents/state/proc-start.js +46 -1
  63. package/dist/core/agents/state/process-handle.d.ts +26 -0
  64. package/dist/core/agents/state/process-handle.d.ts.map +1 -0
  65. package/dist/core/agents/state/process-handle.js +121 -0
  66. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  67. package/dist/core/agents/state/stale-sweep.js +180 -70
  68. package/dist/core/agents/state/unchanged-file.d.ts +44 -0
  69. package/dist/core/agents/state/unchanged-file.d.ts.map +1 -0
  70. package/dist/core/agents/state/unchanged-file.js +390 -0
  71. package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
  72. package/dist/core/artifacts/delivery-card.js +2 -1
  73. package/dist/core/context/index.d.ts +22 -10
  74. package/dist/core/context/index.d.ts.map +1 -1
  75. package/dist/core/context/index.js +381 -119
  76. package/dist/core/context/observation.d.ts +26 -0
  77. package/dist/core/context/observation.d.ts.map +1 -0
  78. package/dist/core/context/observation.js +11 -0
  79. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  80. package/dist/core/diagnostics/bundle.js +25 -14
  81. package/dist/core/diagnostics/path-identity.d.ts +3 -0
  82. package/dist/core/diagnostics/path-identity.d.ts.map +1 -0
  83. package/dist/core/diagnostics/path-identity.js +12 -0
  84. package/dist/core/diagnostics/sanitize.d.ts.map +1 -1
  85. package/dist/core/diagnostics/sanitize.js +4 -3
  86. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  87. package/dist/core/events/v3/authority-outbox.js +29 -0
  88. package/dist/core/events/v3/generation-custody.d.ts +5 -0
  89. package/dist/core/events/v3/generation-custody.d.ts.map +1 -0
  90. package/dist/core/events/v3/generation-custody.js +50 -0
  91. package/dist/core/events/v3/producers/coordination-recorder.d.ts +1 -0
  92. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  93. package/dist/core/events/v3/producers/coordination-recorder.js +7 -0
  94. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  95. package/dist/core/events/v3/producers/recorder.js +77 -64
  96. package/dist/core/governor/planning.d.ts.map +1 -1
  97. package/dist/core/governor/planning.js +14 -1
  98. package/dist/core/governor/service-read.d.ts +10 -2
  99. package/dist/core/governor/service-read.d.ts.map +1 -1
  100. package/dist/core/governor/service-read.js +105 -22
  101. package/dist/core/governor/service.d.ts +20 -2
  102. package/dist/core/governor/service.d.ts.map +1 -1
  103. package/dist/core/governor/service.js +72 -47
  104. package/dist/core/hooks/cli.js +33 -5
  105. package/dist/core/inbox/contract.d.ts +2 -1
  106. package/dist/core/inbox/contract.d.ts.map +1 -1
  107. package/dist/core/inbox/index.d.ts +2 -0
  108. package/dist/core/inbox/index.d.ts.map +1 -1
  109. package/dist/core/inbox/index.js +2 -0
  110. package/dist/core/inbox/peer-messages.d.ts +37 -0
  111. package/dist/core/inbox/peer-messages.d.ts.map +1 -0
  112. package/dist/core/inbox/peer-messages.js +72 -0
  113. package/dist/core/inbox/service.d.ts +8 -1
  114. package/dist/core/inbox/service.d.ts.map +1 -1
  115. package/dist/core/inbox/service.js +67 -21
  116. package/dist/core/inbox/targets.d.ts +22 -0
  117. package/dist/core/inbox/targets.d.ts.map +1 -0
  118. package/dist/core/inbox/targets.js +108 -0
  119. package/dist/core/inbox/watcher.js +7 -3
  120. package/dist/core/journal/index.d.ts.map +1 -1
  121. package/dist/core/journal/index.js +17 -2
  122. package/dist/core/resources/contract.d.ts +3 -0
  123. package/dist/core/resources/contract.d.ts.map +1 -1
  124. package/dist/core/resources/service-status.d.ts +14 -1
  125. package/dist/core/resources/service-status.d.ts.map +1 -1
  126. package/dist/core/resources/service-status.js +23 -13
  127. package/dist/core/resources/service.d.ts +7 -13
  128. package/dist/core/resources/service.d.ts.map +1 -1
  129. package/dist/core/resources/service.js +34 -91
  130. package/dist/core/resources/status.d.ts +2 -5
  131. package/dist/core/resources/status.d.ts.map +1 -1
  132. package/dist/core/resources/status.js +36 -7
  133. package/dist/core/semantic/service.d.ts.map +1 -1
  134. package/dist/core/semantic/service.js +2 -9
  135. package/dist/core/servers/index.d.ts +34 -22
  136. package/dist/core/servers/index.d.ts.map +1 -1
  137. package/dist/core/servers/index.js +297 -144
  138. package/dist/core/servers/tunnels.d.ts +7 -7
  139. package/dist/core/servers/tunnels.d.ts.map +1 -1
  140. package/dist/core/servers/tunnels.js +47 -10
  141. package/dist/core/supervisor/contract.d.ts +12 -0
  142. package/dist/core/supervisor/contract.d.ts.map +1 -1
  143. package/dist/core/supervisor/explanations.d.ts.map +1 -1
  144. package/dist/core/supervisor/explanations.js +2 -1
  145. package/dist/core/supervisor/findings.d.ts.map +1 -1
  146. package/dist/core/supervisor/findings.js +54 -9
  147. package/dist/core/supervisor/service.d.ts +7 -1
  148. package/dist/core/supervisor/service.d.ts.map +1 -1
  149. package/dist/core/supervisor/service.js +59 -29
  150. package/dist/core/supervisor/services.d.ts.map +1 -1
  151. package/dist/core/supervisor/services.js +22 -1
  152. package/dist/core/supervisor/status.d.ts +17 -2
  153. package/dist/core/supervisor/status.d.ts.map +1 -1
  154. package/dist/core/supervisor/status.js +70 -12
  155. package/dist/core/supervisor/timeline.d.ts.map +1 -1
  156. package/dist/core/supervisor/timeline.js +2 -1
  157. package/dist/core/turn-rollback/content.d.ts +53 -0
  158. package/dist/core/turn-rollback/content.d.ts.map +1 -0
  159. package/dist/core/turn-rollback/content.js +396 -0
  160. package/dist/core/turn-rollback/index.d.ts +72 -0
  161. package/dist/core/turn-rollback/index.d.ts.map +1 -0
  162. package/dist/core/turn-rollback/index.js +443 -0
  163. package/dist/core/work/state.js +7 -7
  164. package/dist/core/workflow/engine.d.ts.map +1 -1
  165. package/dist/core/workflow/engine.js +105 -32
  166. package/dist/core/workflow/failure-evidence.d.ts +16 -0
  167. package/dist/core/workflow/failure-evidence.d.ts.map +1 -0
  168. package/dist/core/workflow/failure-evidence.js +114 -0
  169. package/dist/core/workflow/index.d.ts +5 -3
  170. package/dist/core/workflow/index.d.ts.map +1 -1
  171. package/dist/core/workflow/index.js +3 -2
  172. package/dist/core/workflow/live-session-v3.d.ts +6 -0
  173. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  174. package/dist/core/workflow/live-session-v3.js +126 -5
  175. package/dist/core/workflow/proof.d.ts +5 -5
  176. package/dist/core/workflow/proof.d.ts.map +1 -1
  177. package/dist/core/workflow/proof.js +115 -20
  178. package/dist/core/workflow/recovery-audit.d.ts +30 -0
  179. package/dist/core/workflow/recovery-audit.d.ts.map +1 -0
  180. package/dist/core/workflow/recovery-audit.js +116 -0
  181. package/dist/core/workflow/run-state.d.ts +20 -1
  182. package/dist/core/workflow/run-state.d.ts.map +1 -1
  183. package/dist/core/workflow/run-state.js +141 -32
  184. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  185. package/dist/core/workflow/spawn-claude.js +19 -0
  186. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  187. package/dist/core/workflow/spawn-codex.js +13 -0
  188. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  189. package/dist/core/workflow/spawn-cursor.js +19 -0
  190. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
  191. package/dist/core/workflow/spawn-opencode.js +14 -0
  192. package/dist/core/workflow/types.d.ts +64 -17
  193. package/dist/core/workflow/types.d.ts.map +1 -1
  194. package/dist/core/workflow/workspaces/index.d.ts +0 -1
  195. package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
  196. package/dist/core/workflow/workspaces/index.js +0 -1
  197. package/dist/core/workflow/workspaces/leases.d.ts +3 -1
  198. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  199. package/dist/core/workflow/workspaces/leases.js +30 -16
  200. package/dist/core/workflow/workspaces/local-git.d.ts +6 -2
  201. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  202. package/dist/core/workflow/workspaces/local-git.js +328 -8
  203. package/dist/core/workflow/workspaces/ownership.d.ts +11 -0
  204. package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -0
  205. package/dist/core/workflow/workspaces/ownership.js +194 -0
  206. package/dist/core/workflow/workspaces/reclaim.d.ts +40 -0
  207. package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
  208. package/dist/core/workflow/workspaces/reclaim.js +14 -0
  209. package/dist/core/workflow/workspaces/recovery.d.ts +2 -0
  210. package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
  211. package/dist/core/workflow/workspaces/recovery.js +42 -10
  212. package/dist/core/workflow/workspaces/state.d.ts +1 -0
  213. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  214. package/dist/core/workflow/workspaces/state.js +21 -1
  215. package/dist/lib/browser/windows-chrome.d.ts +100 -12
  216. package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
  217. package/dist/lib/browser/windows-chrome.js +262 -73
  218. package/dist/lib/docs-links.d.ts.map +1 -1
  219. package/dist/lib/docs-links.js +45 -2
  220. package/dist/lib/guarded-remove.d.ts +7 -0
  221. package/dist/lib/guarded-remove.d.ts.map +1 -1
  222. package/dist/lib/guarded-remove.js +106 -12
  223. package/dist/lib/identities/assume.d.ts.map +1 -1
  224. package/dist/lib/identities/assume.js +4 -0
  225. package/dist/lib/tunnel/state.d.ts +13 -6
  226. package/dist/lib/tunnel/state.d.ts.map +1 -1
  227. package/dist/lib/tunnel/state.js +58 -12
  228. package/dist/lib/zip/index.d.ts +41 -0
  229. package/dist/lib/zip/index.d.ts.map +1 -0
  230. package/dist/lib/zip/index.js +398 -0
  231. package/package.json +12 -1
  232. package/src/commander.ts +10 -0
  233. package/src/commands/agents.ts +54 -63
  234. package/src/commands/browse.ts +50 -14
  235. package/src/commands/diagnostics.ts +10 -0
  236. package/src/commands/doctor.ts +26 -7
  237. package/src/commands/files.ts +13 -5
  238. package/src/commands/governor.ts +23 -10
  239. package/src/commands/inbox.ts +52 -2
  240. package/src/commands/rm.ts +43 -4
  241. package/src/commands/servers.ts +27 -26
  242. package/src/commands/supervisor.ts +7 -2
  243. package/src/commands/toc.ts +14 -6
  244. package/src/commands/tunnel.ts +533 -166
  245. package/src/commands/turn-rollback.ts +111 -0
  246. package/src/commands/workflow.ts +89 -30
  247. package/src/core/agents/coord-client.ts +20 -0
  248. package/src/core/agents/live-authority-v3.ts +17 -0
  249. package/src/core/agents/live-lifecycle-v3.ts +52 -14
  250. package/src/core/agents/mailbox.ts +26 -17
  251. package/src/core/agents/render/prompt-context.ts +10 -12
  252. package/src/core/agents/render/session-context.ts +9 -32
  253. package/src/core/agents/rules/stop-hook.ts +3 -3
  254. package/src/core/agents/state/heartbeat-writer.ts +29 -12
  255. package/src/core/agents/state/live-coordination-writer.ts +29 -0
  256. package/src/core/agents/state/pidmap.ts +13 -0
  257. package/src/core/agents/state/proc-start.ts +62 -2
  258. package/src/core/agents/state/process-handle.ts +165 -0
  259. package/src/core/agents/state/stale-sweep.ts +247 -82
  260. package/src/core/agents/state/unchanged-file.ts +456 -0
  261. package/src/core/artifacts/delivery-card.ts +7 -1
  262. package/src/core/context/index.ts +446 -136
  263. package/src/core/context/observation.ts +29 -0
  264. package/src/core/diagnostics/bundle.ts +23 -18
  265. package/src/core/diagnostics/path-identity.ts +15 -0
  266. package/src/core/diagnostics/sanitize.ts +4 -3
  267. package/src/core/events/v3/authority-outbox.ts +56 -0
  268. package/src/core/events/v3/generation-custody.ts +59 -0
  269. package/src/core/events/v3/producers/coordination-recorder.ts +15 -0
  270. package/src/core/events/v3/producers/recorder.ts +94 -68
  271. package/src/core/governor/planning.ts +20 -8
  272. package/src/core/governor/service-read.ts +128 -20
  273. package/src/core/governor/service.ts +119 -42
  274. package/src/core/hooks/cli.ts +33 -5
  275. package/src/core/inbox/contract.ts +2 -1
  276. package/src/core/inbox/index.ts +2 -0
  277. package/src/core/inbox/peer-messages.ts +94 -0
  278. package/src/core/inbox/service.ts +88 -22
  279. package/src/core/inbox/targets.ts +148 -0
  280. package/src/core/inbox/watcher.ts +8 -9
  281. package/src/core/journal/index.ts +19 -2
  282. package/src/core/resources/contract.ts +4 -0
  283. package/src/core/resources/service-status.ts +35 -12
  284. package/src/core/resources/service.ts +57 -95
  285. package/src/core/resources/status.ts +57 -8
  286. package/src/core/semantic/service.ts +2 -8
  287. package/src/core/servers/index.ts +376 -141
  288. package/src/core/servers/tunnels.ts +57 -13
  289. package/src/core/supervisor/contract.ts +13 -0
  290. package/src/core/supervisor/explanations.ts +4 -1
  291. package/src/core/supervisor/findings.ts +68 -10
  292. package/src/core/supervisor/service.ts +87 -26
  293. package/src/core/supervisor/services.ts +26 -8
  294. package/src/core/supervisor/status.ts +89 -12
  295. package/src/core/supervisor/timeline.ts +2 -1
  296. package/src/core/turn-rollback/content.ts +499 -0
  297. package/src/core/turn-rollback/index.ts +580 -0
  298. package/src/core/work/state.ts +7 -7
  299. package/src/core/workflow/engine.ts +131 -49
  300. package/src/core/workflow/failure-evidence.ts +141 -0
  301. package/src/core/workflow/index.ts +12 -2
  302. package/src/core/workflow/live-session-v3.ts +146 -5
  303. package/src/core/workflow/proof.ts +134 -26
  304. package/src/core/workflow/recovery-audit.ts +181 -0
  305. package/src/core/workflow/run-state.ts +165 -34
  306. package/src/core/workflow/spawn-claude.ts +18 -0
  307. package/src/core/workflow/spawn-codex.ts +13 -0
  308. package/src/core/workflow/spawn-cursor.ts +18 -0
  309. package/src/core/workflow/spawn-opencode.ts +14 -0
  310. package/src/core/workflow/types.ts +51 -17
  311. package/src/core/workflow/workspaces/index.ts +0 -1
  312. package/src/core/workflow/workspaces/leases.ts +48 -17
  313. package/src/core/workflow/workspaces/local-git.ts +388 -7
  314. package/src/core/workflow/workspaces/ownership.ts +232 -0
  315. package/src/core/workflow/workspaces/reclaim.ts +62 -0
  316. package/src/core/workflow/workspaces/recovery.ts +45 -10
  317. package/src/core/workflow/workspaces/state.ts +20 -0
  318. package/src/lib/browser/windows-chrome.ts +346 -86
  319. package/src/lib/docs-links.ts +49 -2
  320. package/src/lib/guarded-remove.ts +132 -14
  321. package/src/lib/identities/assume.ts +4 -0
  322. package/src/lib/tunnel/state.ts +70 -15
  323. package/src/lib/zip/index.ts +483 -0
@@ -37,7 +37,8 @@ import { freezeWorkflowAttemptContext } from "./attempt-context.js";
37
37
  import { probeBilling } from "./billing.js";
38
38
  import { stableDigest } from "./durable-record.js";
39
39
  import { evidencePreflightError } from "./evidence-preflight.js";
40
- import { endWorkflowChildSessionV3, startWorkflowChildSessionV3 } from "./live-session-v3.js";
40
+ import { canRetryFailedSpawn, observeSpawnFailure, resolveFailureEvidence, } from "./failure-evidence.js";
41
+ import { endWorkflowChildSessionV3, recordWorkflowChildTerminalFailureV3, startWorkflowChildSessionV3, } from "./live-session-v3.js";
41
42
  import { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, writeWorkflowProof, } from "./proof.js";
42
43
  import { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
43
44
  import { assertProjectionWithinWorkspace, resolveGitGrantRoots } from "./sandbox-projection.js";
@@ -310,7 +311,12 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
310
311
  durationMs,
311
312
  transcriptPath,
312
313
  before: resumeState.manifest.repository_before,
313
- after: resumeState.manifest.repository_before,
314
+ after: {
315
+ cwd: frozenBinding.active_root,
316
+ observation: "unavailable",
317
+ observation_detail: "Workspace reattachment failed; no trusted after-run snapshot was observed.",
318
+ dirty_paths: [],
319
+ },
314
320
  agents: [],
315
321
  evidence: [],
316
322
  diagnosticAdmission: initialDiagnosticAdmissionProof(diagnosticAdmissionConfig),
@@ -476,6 +482,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
476
482
  let policySeq = 0;
477
483
  const billingProbed = new Map();
478
484
  const agentProofs = new Map();
485
+ let terminalRecordingError;
479
486
  const evidenceRecords = [];
480
487
  const policyDecisions = new Map(history?.policyDecisions.map((decision) => [decision.id, decision]));
481
488
  const acceptanceIds = new Set(meta.acceptance.map((criterion) => criterion.id));
@@ -858,6 +865,51 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
858
865
  },
859
866
  });
860
867
  agentProof.access = access;
868
+ let childGenerationId;
869
+ let recordAgentEnd;
870
+ const finalizeChild = () => {
871
+ try {
872
+ if (childGenerationId)
873
+ endWorkflowChildSessionV3({
874
+ coordRoot: opts.coordRoot,
875
+ instanceId: childInstanceId(runId, id),
876
+ runId,
877
+ agentId: id,
878
+ adapter,
879
+ cleanExit: agentProof.status === "succeeded",
880
+ expectedGenerationId: childGenerationId,
881
+ });
882
+ // Result publication is the resume-cache boundary, after canonical custody.
883
+ recordAgentEnd?.();
884
+ }
885
+ catch (error) {
886
+ let reason = `V3 terminal recording failed: ${error.message}`;
887
+ try {
888
+ if (childGenerationId)
889
+ recordWorkflowChildTerminalFailureV3({
890
+ coordRoot: opts.coordRoot,
891
+ instanceId: childInstanceId(runId, id),
892
+ runId,
893
+ agentId: id,
894
+ adapter,
895
+ expectedGenerationId: childGenerationId,
896
+ reason,
897
+ });
898
+ }
899
+ catch (visibilityError) {
900
+ reason += `; blocked lifecycle recording failed: ${visibilityError.message}`;
901
+ }
902
+ const failure = new Error(agentProof.error ? `${agentProof.error}; ${reason}` : reason, {
903
+ cause: error,
904
+ });
905
+ terminalRecordingError ??= failure;
906
+ agentProof.status = "failed";
907
+ agentProof.error = failure.message;
908
+ transcript("agent.failed", { id, key, error: failure.message });
909
+ log(`[${name}] ${failure.message}`);
910
+ throw failure;
911
+ }
912
+ };
861
913
  await acquire();
862
914
  try {
863
915
  await ensureDiagnosticAdmissionObserved();
@@ -878,7 +930,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
878
930
  // Headless children do not reliably fire adapter hooks. The engine owns
879
931
  // their canonical V3 start so every dispatched child has an auditable
880
932
  // generation before it begins work.
881
- startWorkflowChildSessionV3({
933
+ const child = startWorkflowChildSessionV3({
882
934
  coordRoot: opts.coordRoot,
883
935
  instanceId: childInstanceId(runId, id),
884
936
  runId,
@@ -887,9 +939,13 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
887
939
  label,
888
940
  model: agentOpts.model,
889
941
  });
942
+ if (!child.v3_generation_id)
943
+ throw new Error("workflow_child_v3_generation_missing");
944
+ childGenerationId = child.v3_generation_id;
890
945
  let attemptPrompt = dispatchPrompt;
891
946
  let last = null;
892
947
  let agentCostUsd = 0;
948
+ let informativeAttempt;
893
949
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
894
950
  last = await spawner({
895
951
  prompt: attemptPrompt,
@@ -911,30 +967,45 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
911
967
  agentCostUsd += last.costUsd ?? 0;
912
968
  costUsd += last.costUsd ?? 0;
913
969
  if (!last.ok) {
914
- transcript("agent.attempt_failed", { id, attempt, error: last.error });
915
- // ADR 0046: an environment failure (the binary was absent) cannot be
916
- // helped by retrying an unchanged environment, so stop the in-agent
917
- // retry too — not just the outer attempt/replan budget. An upstream
918
- // refusal keeps retrying here: the vendor may recover mid-loop.
919
- if (last.class === "environment")
970
+ const observed = last.failureEvidence === undefined
971
+ ? observeSpawnFailure({}, last.text, "final_message")
972
+ : resolveFailureEvidence(last.failureEvidence);
973
+ agentProof.failure_evidence = informativeAttempt
974
+ ? {
975
+ ...observed,
976
+ effect: { state: "unknown" },
977
+ partial_output: informativeAttempt.partial_output,
978
+ }
979
+ : observed;
980
+ transcript("agent.attempt_failed", {
981
+ id,
982
+ attempt,
983
+ error: last.error,
984
+ failure_evidence: observed,
985
+ result_digest: digestResult(last.text, "text"),
986
+ text: last.text,
987
+ });
988
+ if (!canRetryFailedSpawn({ ...last, failureEvidence: agentProof.failure_evidence }))
920
989
  break;
921
- continue; // spawn-level failure: retry with the original prompt
990
+ continue;
922
991
  }
992
+ informativeAttempt = observeSpawnFailure({}, last.text, "final_message");
923
993
  if (!agentOpts.schema) {
924
994
  agentProof.status = "succeeded";
925
995
  agentProof.cost_usd =
926
996
  agentCostUsd > 0 || last.costUsd !== undefined ? agentCostUsd : undefined;
927
997
  agentProof.session_id = last.sessionId;
928
998
  agentProof.result = digestResult(last.text, "text");
929
- transcriptAgentEnd({
999
+ const completed = last;
1000
+ recordAgentEnd = () => transcriptAgentEnd({
930
1001
  id,
931
1002
  key,
932
1003
  attempts: attempt,
933
- cost_usd: last.costUsd,
1004
+ cost_usd: completed.costUsd,
934
1005
  total_cost_usd: agentCostUsd,
935
- duration_ms: last.durationMs,
936
- session_id: last.sessionId,
937
- }, "text", last.text);
1006
+ duration_ms: completed.durationMs,
1007
+ session_id: completed.sessionId,
1008
+ }, "text", completed.text);
938
1009
  return last.text;
939
1010
  }
940
1011
  const parsed = parseStageOutput(last.text);
@@ -947,18 +1018,26 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
947
1018
  agentCostUsd > 0 || last.costUsd !== undefined ? agentCostUsd : undefined;
948
1019
  agentProof.session_id = last.sessionId;
949
1020
  agentProof.result = digestResult(parsed.value, "json");
950
- transcriptAgentEnd({
1021
+ const completed = last;
1022
+ recordAgentEnd = () => transcriptAgentEnd({
951
1023
  id,
952
1024
  key,
953
1025
  attempts: attempt,
954
- cost_usd: last.costUsd,
1026
+ cost_usd: completed.costUsd,
955
1027
  total_cost_usd: agentCostUsd,
956
- duration_ms: last.durationMs,
957
- session_id: last.sessionId,
1028
+ duration_ms: completed.durationMs,
1029
+ session_id: completed.sessionId,
958
1030
  }, "json", parsed.value);
959
1031
  return parsed.value;
960
1032
  }
961
- transcript("agent.schema_retry", { id, attempt, problems });
1033
+ agentProof.failure_evidence = informativeAttempt;
1034
+ transcript("agent.schema_retry", {
1035
+ id,
1036
+ attempt,
1037
+ problems,
1038
+ rejected_text: last.text,
1039
+ result_digest: digestResult(last.text, "text"),
1040
+ });
962
1041
  // Keep the original cache identity while making the actual output
963
1042
  // contract explicit. A full bounded copy of the rejected value lets the
964
1043
  // child repair omissions instead of reconstructing an unseen object.
@@ -993,21 +1072,13 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
993
1072
  }
994
1073
  finally {
995
1074
  reservedCostUsd = Math.max(0, reservedCostUsd - reservedForDispatch);
996
- // Record the canonical terminal before removing the disposable cache.
997
1075
  try {
998
- endWorkflowChildSessionV3({
999
- coordRoot: opts.coordRoot,
1000
- instanceId: childInstanceId(runId, id),
1001
- runId,
1002
- agentId: id,
1003
- adapter,
1004
- cleanExit: agentProof.status === "succeeded",
1005
- });
1076
+ finalizeChild();
1006
1077
  }
1007
- catch (error) {
1008
- log(`[${name}] V3 terminal recording failed: ${error.message}`);
1078
+ finally {
1079
+ // A slot is disposable; unresolved custody remains visible and failed.
1080
+ release();
1009
1081
  }
1010
- release();
1011
1082
  }
1012
1083
  };
1013
1084
  const parallel = async (thunks) => {
@@ -1105,6 +1176,8 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
1105
1176
  if (!workflow)
1106
1177
  throw new Error("workflow module initialization did not produce an executable");
1107
1178
  const result = await workflow(ctx);
1179
+ if (terminalRecordingError)
1180
+ throw terminalRecordingError;
1108
1181
  const endedAt = new Date().toISOString();
1109
1182
  const durationMs = Date.now() - t0;
1110
1183
  const workspaceAttestation = workspaceBinding
@@ -0,0 +1,16 @@
1
+ import type { AdapterRawResult } from "../adapters/types.js";
2
+ import type { SpawnFailureEvidence, SpawnOutputSource, SpawnResult } from "./types.js";
3
+ export declare function unknownFailureEvidence(): SpawnFailureEvidence;
4
+ /** Built-in subprocess adapters observe output and process-start failures.
5
+ * Their current vendor envelopes do not prove effects or native refusal.
6
+ * In particular, blank output and 429/5xx statuses leave both unknown. */
7
+ export declare function observeSpawnFailure(raw: Pick<AdapterRawResult, "spawnErrno" | "timedOut"> & Partial<Pick<AdapterRawResult, "stdout" | "resultFileText">>, text: string, source: SpawnOutputSource): SpawnFailureEvidence;
8
+ export declare function isSpawnFailureEvidence(value: unknown): value is SpawnFailureEvidence;
9
+ /** Missing observations are unknown; malformed positive claims are rejected.
10
+ * This is also used when reading persisted workflow proof. */
11
+ export declare function resolveFailureEvidence(value: unknown): SpawnFailureEvidence;
12
+ export declare function failureEvidenceAllowsUncharged(value: unknown): boolean;
13
+ /** A provider retry requires positive no-effect evidence. A missing binary is
14
+ * known not to have started, but retrying that unchanged environment cannot help. */
15
+ export declare function canRetryFailedSpawn(result: SpawnResult): boolean;
16
+ //# sourceMappingURL=failure-evidence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure-evidence.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/failure-evidence.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEvF,wBAAgB,sBAAsB,IAAI,oBAAoB,CAM7D;AAED;;0EAE0E;AAC1E,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,UAAU,CAAC,GACpD,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,gBAAgB,CAAC,CAAC,EAC9D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,iBAAiB,GACxB,oBAAoB,CAsBtB;AA+DD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAQpF;AAED;8DAC8D;AAC9D,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAI3E;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOtE;AAED;qFACqF;AACrF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAMhE"}
@@ -0,0 +1,114 @@
1
+ import { createHash } from "node:crypto";
2
+ export function unknownFailureEvidence() {
3
+ return {
4
+ effect: { state: "unknown" },
5
+ refusal: { state: "unknown" },
6
+ partial_output: { state: "unknown" },
7
+ };
8
+ }
9
+ /** Built-in subprocess adapters observe output and process-start failures.
10
+ * Their current vendor envelopes do not prove effects or native refusal.
11
+ * In particular, blank output and 429/5xx statuses leave both unknown. */
12
+ export function observeSpawnFailure(raw, text, source) {
13
+ const bytes = Buffer.byteLength(text);
14
+ return {
15
+ effect: raw.spawnErrno === "ENOENT" &&
16
+ !raw.timedOut &&
17
+ bytes === 0 &&
18
+ !raw.stdout?.trim() &&
19
+ !raw.resultFileText?.trim()
20
+ ? { state: "none", source: "spawn_errno", reference: "ENOENT" }
21
+ : { state: "unknown" },
22
+ refusal: { state: "unknown" },
23
+ partial_output: bytes > 0
24
+ ? {
25
+ state: "present",
26
+ source,
27
+ bytes,
28
+ sha256: createHash("sha256").update(text).digest("hex"),
29
+ }
30
+ : { state: "absent", source },
31
+ };
32
+ }
33
+ function record(value) {
34
+ return typeof value === "object" && value !== null && !Array.isArray(value);
35
+ }
36
+ function keys(value, expected) {
37
+ return (Object.keys(value).length === expected.length &&
38
+ expected.every((key) => Object.hasOwn(value, key)));
39
+ }
40
+ function reference(value) {
41
+ return typeof value === "string" && value.trim().length > 0 && value.length <= 512;
42
+ }
43
+ function validEffect(value) {
44
+ if (!record(value))
45
+ return false;
46
+ if (value.state === "unknown")
47
+ return keys(value, ["state"]);
48
+ if (!keys(value, ["state", "source", "reference"]))
49
+ return false;
50
+ if (value.state === "none" && value.source === "spawn_errno") {
51
+ return value.reference === "ENOENT";
52
+ }
53
+ return ((value.state === "none" || value.state === "observed") &&
54
+ value.source === "adapter_evidence" &&
55
+ reference(value.reference));
56
+ }
57
+ function validRefusal(value) {
58
+ if (!record(value))
59
+ return false;
60
+ if (value.state === "unknown")
61
+ return keys(value, ["state"]);
62
+ return (keys(value, ["state", "source", "reference"]) &&
63
+ value.state === "refused" &&
64
+ value.source === "vendor_envelope" &&
65
+ reference(value.reference));
66
+ }
67
+ function validOutput(value) {
68
+ if (!record(value))
69
+ return false;
70
+ if (value.state === "unknown")
71
+ return keys(value, ["state"]);
72
+ if (value.source !== "final_message" &&
73
+ value.source !== "vendor_envelope" &&
74
+ value.source !== "stdout") {
75
+ return false;
76
+ }
77
+ if (value.state === "absent")
78
+ return keys(value, ["state", "source"]);
79
+ return (value.state === "present" &&
80
+ keys(value, ["state", "source", "bytes", "sha256"]) &&
81
+ Number.isSafeInteger(value.bytes) &&
82
+ value.bytes > 0 &&
83
+ typeof value.sha256 === "string" &&
84
+ /^[a-f0-9]{64}$/.test(value.sha256));
85
+ }
86
+ export function isSpawnFailureEvidence(value) {
87
+ return (record(value) &&
88
+ keys(value, ["effect", "refusal", "partial_output"]) &&
89
+ validEffect(value.effect) &&
90
+ validRefusal(value.refusal) &&
91
+ validOutput(value.partial_output));
92
+ }
93
+ /** Missing observations are unknown; malformed positive claims are rejected.
94
+ * This is also used when reading persisted workflow proof. */
95
+ export function resolveFailureEvidence(value) {
96
+ if (value === undefined)
97
+ return unknownFailureEvidence();
98
+ if (!isSpawnFailureEvidence(value))
99
+ throw new Error("invalid spawn failure evidence");
100
+ return value;
101
+ }
102
+ export function failureEvidenceAllowsUncharged(value) {
103
+ const evidence = resolveFailureEvidence(value);
104
+ return (evidence.effect.state === "none" &&
105
+ evidence.refusal.state !== "refused" &&
106
+ evidence.partial_output.state === "absent");
107
+ }
108
+ /** A provider retry requires positive no-effect evidence. A missing binary is
109
+ * known not to have started, but retrying that unchanged environment cannot help. */
110
+ export function canRetryFailedSpawn(result) {
111
+ return (!result.ok &&
112
+ result.class !== "environment" &&
113
+ failureEvidenceAllowsUncharged(result.failureEvidence));
114
+ }
@@ -4,11 +4,13 @@ export { assertWorkflowRunId, createWorkflowApproval, listWorkflowApprovals, rea
4
4
  export { freezeWorkflowAttemptContext, isCanonicalWorkflowAttemptContext, normalizeWorkflowAttemptContext, } from "./attempt-context.js";
5
5
  export { runWorkflow, WorkflowParkedError, WorkflowRunError } from "./engine.js";
6
6
  export { buildWorkflowProof, createEvidenceRecord, deriveRunFailureClass, digestResult, normalizeWorkflowMeta, readWorkflowProof, renderWorkflowProof, rollupAcceptance, writeWorkflowProof, } from "./proof.js";
7
- export type { WorkflowRunManifest } from "./run-state.js";
8
- export { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, readWorkflowRunManifest, WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION, workflowRunManifestPath, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
7
+ export type { WorkflowRecoveryAuthorization, WorkflowRecoveryIntent, WorkflowRecoveryRecord, } from "./recovery-audit.js";
8
+ export { beginWorkflowRecovery, readWorkflowRecoveryLedger } from "./recovery-audit.js";
9
+ export type { WorkflowResumeRecoveryOptions, WorkflowResumeRecoveryResult, WorkflowRunManifest, } from "./run-state.js";
10
+ export { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, readWorkflowRunManifest, recoverWorkflowResumeLease, WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION, workflowRunManifestPath, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
9
11
  export { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
10
12
  export type { AcceptanceCriterion, AcceptanceResult, AcceptanceStatus, AcceptanceSummary, AdapterEvidenceCapability, AdapterEvidenceCoverage, AdapterName, AgentOpts, EngineOpts, EvidenceKind, EvidenceSource, EvidenceStatus, ResultDigest, RunReport, Spawner, SpawnFailureClass, SpawnRequest, SpawnResult, StageSchema, WorkflowAgentProof, WorkflowApprovalMode, WorkflowAttemptContext, WorkflowAttemptFailureCause, WorkflowAttemptPriorContext, WorkflowAttemptUnresolvedCriterion, WorkflowContext, WorkflowDiagnosticAdmissionConfig, WorkflowDiagnosticAdmissionObservation, WorkflowDiagnosticAdmissionProof, WorkflowEvidenceInput, WorkflowEvidenceRecord, WorkflowMeta, WorkflowModule, WorkflowPolicyProof, WorkflowProof, WorkflowProofUnknown, WorkflowRepoEvidence, WorkflowRepoSnapshot, WorkflowRunStatus, WorkflowSpecialistProfile, WorkflowWorkContext, } from "./types.js";
11
13
  export { WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION, WORKFLOW_DIAGNOSTIC_ADMISSION_SCHEMA_VERSION, WORKFLOW_PROOF_SCHEMA_VERSION, WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION, } from "./types.js";
12
14
  export type { ApplyIntegrationInput, AuthorizedIntegrationPlan, AuthorizedProviderIntegrationInput, CancelWorkspaceInput, CleanupWorkspaceInput, FilesystemIdentity, IntegrationApplyAttempt, IntegrationApplyReceipt, IntegrationAuthorization, IntegrationPlan, IntegrationReviewRecord, IntegrationTarget, LocalGitWorktreeProviderOptions, PrepareIntegrationInput, ProviderIntegrationInput, ProviderIntegrationPreview, ProviderIntegrationResult, ProviderUnknown, ProviderUnsupportedReason, RepositoryBinding, ValidatedFilesystemPath, WorkflowWorkspaceInspection, WorkflowWorkspaceSelection, WorkflowWorkspaceStatus, WorkflowWorkspaceVerification, WorkspaceAllocationRequest, WorkspaceAttestation, WorkspaceBinding, WorkspaceBoundExecutionEvidence, WorkspaceCancellationReceipt, WorkspaceCancellationResult, WorkspaceCleanupAttempt, WorkspaceCleanupIntent, WorkspaceCleanupMode, WorkspaceCleanupReceipt, WorkspaceCleanupResult, WorkspaceCompatibilityExecutionEvidence, WorkspaceExecutionEvidence, WorkspaceIntegrationState, WorkspaceIsolation, WorkspaceLifecycleProjection, WorkspaceLifecycleProjectionInput, WorkspaceLifecycleState, WorkspaceObservation, WorkspaceOwner, WorkspaceProbeInput, WorkspaceProbeResult, WorkspaceProofOutcome, WorkspaceProvider, WorkspaceProviderCapabilities, WorkspaceProviderRef, WorkspaceResourceState, } from "./workspaces/index.js";
13
- export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, IntegrationPrepareParkedError, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, prepareReclaim, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
15
+ export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, IntegrationPrepareParkedError, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
14
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4CAA4C,EAC5C,KAAK,uCAAuC,EAC5C,kCAAkC,EAClC,KAAK,kCAAkC,GACxC,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,2BAA2B,EAC3B,4BAA4B,EAC5B,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,4BAA4B,EAC5B,iCAAiC,EACjC,+BAA+B,GAChC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,6BAA6B,EAC7B,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACxF,YAAY,EACV,6BAA6B,EAC7B,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,6BAA6B,EAC7B,uBAAuB,EACvB,0BAA0B,EAC1B,oCAAoC,EACpC,uBAAuB,EACvB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,WAAW,EACX,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,YAAY,EACZ,SAAS,EACT,OAAO,EACP,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAC3B,kCAAkC,EAClC,eAAe,EACf,iCAAiC,EACjC,sCAAsC,EACtC,gCAAgC,EAChC,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,uCAAuC,EACvC,4CAA4C,EAC5C,6BAA6B,EAC7B,oCAAoC,GACrC,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,kCAAkC,EAClC,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,eAAe,EACf,uBAAuB,EACvB,iBAAiB,EACjB,+BAA+B,EAC/B,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,EAC1B,yBAAyB,EACzB,eAAe,EACf,yBAAyB,EACzB,iBAAiB,EACjB,uBAAuB,EACvB,2BAA2B,EAC3B,0BAA0B,EAC1B,uBAAuB,EACvB,6BAA6B,EAC7B,0BAA0B,EAC1B,oBAAoB,EACpB,gBAAgB,EAChB,+BAA+B,EAC/B,4BAA4B,EAC5B,2BAA2B,EAC3B,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,EACvB,sBAAsB,EACtB,uCAAuC,EACvC,0BAA0B,EAC1B,yBAAyB,EACzB,kBAAkB,EAClB,4BAA4B,EAC5B,iCAAiC,EACjC,uBAAuB,EACvB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,6BAA6B,EAC7B,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAChB,kCAAkC,EAClC,eAAe,EACf,gBAAgB,EAChB,8BAA8B,EAC9B,wBAAwB,EACxB,6BAA6B,EAC7B,wBAAwB,EACxB,yBAAyB,EACzB,gCAAgC,EAChC,kBAAkB,EAClB,6BAA6B,EAC7B,2BAA2B,EAC3B,6BAA6B,EAC7B,0BAA0B,EAC1B,gCAAgC,EAChC,gCAAgC,GACjC,MAAM,uBAAuB,CAAC"}
@@ -3,7 +3,8 @@ export { assertWorkflowRunId, createWorkflowApproval, listWorkflowApprovals, rea
3
3
  export { freezeWorkflowAttemptContext, isCanonicalWorkflowAttemptContext, normalizeWorkflowAttemptContext, } from "./attempt-context.js";
4
4
  export { runWorkflow, WorkflowParkedError, WorkflowRunError } from "./engine.js";
5
5
  export { buildWorkflowProof, createEvidenceRecord, deriveRunFailureClass, digestResult, normalizeWorkflowMeta, readWorkflowProof, renderWorkflowProof, rollupAcceptance, writeWorkflowProof, } from "./proof.js";
6
- export { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, readWorkflowRunManifest, WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION, workflowRunManifestPath, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
6
+ export { beginWorkflowRecovery, readWorkflowRecoveryLedger } from "./recovery-audit.js";
7
+ export { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, readWorkflowRunManifest, recoverWorkflowResumeLease, WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION, workflowRunManifestPath, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
7
8
  export { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
8
9
  export { WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION, WORKFLOW_DIAGNOSTIC_ADMISSION_SCHEMA_VERSION, WORKFLOW_PROOF_SCHEMA_VERSION, WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION, } from "./types.js";
9
- export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, IntegrationPrepareParkedError, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, prepareReclaim, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
10
+ export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, IntegrationPrepareParkedError, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
@@ -14,6 +14,12 @@ export declare function startWorkflowChildSessionV3(input: WorkflowChildSessionV
14
14
  /** Record the authoritative terminal before removing the disposable coordination cache. */
15
15
  export declare function endWorkflowChildSessionV3(input: WorkflowChildSessionV3Input & {
16
16
  cleanExit: boolean;
17
+ expectedGenerationId: string;
18
+ }): void;
19
+ /** Persist unresolved terminal custody only for the generation this dispatch owns. */
20
+ export declare function recordWorkflowChildTerminalFailureV3(input: WorkflowChildSessionV3Input & {
21
+ expectedGenerationId: string;
22
+ reason: string;
17
23
  }): void;
18
24
  export { stableScopeId } from "./scope-id.js";
19
25
  //# sourceMappingURL=live-session-v3.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"live-session-v3.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/live-session-v3.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,SAAS,EAEf,MAAM,qCAAqC,CAAC;AAY7C,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,8FAA8F;AAC9F,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,2BAA2B,GAAG,SAAS,CAiDzF;AAED,2FAA2F;AAC3F,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,2BAA2B,GAAG;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,GAC1D,IAAI,CAmBN;AAgBD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"live-session-v3.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/live-session-v3.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,SAAS,EAGf,MAAM,qCAAqC,CAAC;AAoB7C,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,8FAA8F;AAC9F,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,2BAA2B,GAAG,SAAS,CAqDzF;AAED,2FAA2F;AAC3F,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,2BAA2B,GAAG;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,GACxF,IAAI,CAIN;AA0CD,sFAAsF;AACtF,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,2BAA2B,GAAG;IAAE,oBAAoB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpF,IAAI,CAcN;AA8FD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
@@ -1,13 +1,21 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { existsSync, linkSync, readdirSync, readFileSync, renameSync, unlinkSync } from "node:fs";
3
+ import { basename, dirname } from "node:path";
1
4
  import { adapterFromPlatform } from "../adapter.js";
2
- import { recordLiveTaskChangeV3 } from "../agents/live-authority-v3.js";
5
+ import { recordLiveLifecycleChangeV3, recordLiveTaskChangeV3, } from "../agents/live-authority-v3.js";
3
6
  import { endSessionExplicitV3 } from "../agents/session-finalizer-v3.js";
4
- import { clearCoordinationCache, setIdentityCache, } from "../agents/state/heartbeat-writer.js";
7
+ import { heartbeatPath } from "../agents/state/heartbeat-reader.js";
8
+ import { readHeartbeat, setIdentityCache, } from "../agents/state/heartbeat-writer.js";
5
9
  import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
6
10
  import { ensureLiveCoordinationHeartbeat } from "../agents/state/live-coordination-writer.js";
7
11
  import { assignName } from "../agents/state/names.js";
8
12
  import { coordFreshnessSeconds } from "../config.js";
9
13
  import { ensureEventLedgerV3 } from "../events/v3/bootstrap.js";
10
- import { recordLiveHookSignalV3, resolveLiveEventLedgerRouteV3, } from "../events/v3/live-routing.js";
14
+ import { readCoordinationViewV3, requireAuthoritySafeCoordinationViewV3, } from "../events/v3/coordination-view.js";
15
+ import { withGenerationCustodyV3 } from "../events/v3/generation-custody.js";
16
+ import { liveInstanceIdV3, recordLiveHookSignalV3, resolveLiveEventLedgerRouteV3, } from "../events/v3/live-routing.js";
17
+ import { readHookProducerStateV3 } from "../events/v3/producers/recorder.js";
18
+ import { fsyncParentDirectory } from "./durable-record.js";
11
19
  import { stableScopeId } from "./scope-id.js";
12
20
  /** Start a headless workflow child through the same canonical V3 producer as native hooks. */
13
21
  export function startWorkflowChildSessionV3(input) {
@@ -54,13 +62,38 @@ export function startWorkflowChildSessionV3(input) {
54
62
  const projected = readLiveCoordinationRow(input.coordRoot, input.instanceId);
55
63
  if (!projected)
56
64
  throw new Error("workflow_child_v3_projection_missing");
65
+ const own = readHookProducerStateV3(input.coordRoot, adapter, sessionId);
66
+ if (!own || own.terminal || own.generation_id !== projected.v3_generation_id) {
67
+ throw new Error("workflow_child_v3_start_generation_changed");
68
+ }
57
69
  return projected;
58
70
  }
59
71
  /** Record the authoritative terminal before removing the disposable coordination cache. */
60
72
  export function endWorkflowChildSessionV3(input) {
73
+ withGenerationCustodyV3(input.coordRoot, [liveInstanceIdV3(input.instanceId)], () => endExpectedChild(input));
74
+ }
75
+ function endExpectedChild(input) {
61
76
  requireV3Route(input.coordRoot);
77
+ const view = requireAuthoritySafeCoordinationViewV3(readCoordinationViewV3(input.coordRoot));
78
+ const instanceId = liveInstanceIdV3(input.instanceId);
79
+ const current = view.instances[instanceId];
80
+ const terminal = view.terminal_generations[input.expectedGenerationId];
81
+ if (terminal?.instance_id === instanceId)
82
+ recoverHeldCaches(input);
83
+ if (current && current.generation_id !== input.expectedGenerationId) {
84
+ throw new Error("workflow_child_v3_generation_changed");
85
+ }
86
+ if (!current) {
87
+ if (!terminal || terminal.instance_id !== instanceId)
88
+ throw new Error("workflow_child_v3_generation_missing");
89
+ if (terminal.terminal?.outcome !== (input.cleanExit ? "succeeded" : "failed")) {
90
+ throw new Error("workflow_child_v3_terminal_outcome_changed");
91
+ }
92
+ clearExpectedCache(input);
93
+ return;
94
+ }
62
95
  const row = readLiveCoordinationRow(input.coordRoot, input.instanceId);
63
- if (!row?.v3_instance_id || !row.v3_generation_id) {
96
+ if (!row?.v3_instance_id || row.v3_generation_id !== input.expectedGenerationId) {
64
97
  throw new Error("workflow_child_v3_generation_missing");
65
98
  }
66
99
  const result = endSessionExplicitV3({
@@ -73,7 +106,95 @@ export function endWorkflowChildSessionV3(input) {
73
106
  if (result.state !== "recorded" && result.state !== "already_ended") {
74
107
  throw new Error(`workflow_child_v3_end_failed:${result.state}${"reason" in result ? `:${result.reason}` : ""}`);
75
108
  }
76
- clearCoordinationCache(input.coordRoot, input.instanceId);
109
+ clearExpectedCache(input);
110
+ }
111
+ /** Persist unresolved terminal custody only for the generation this dispatch owns. */
112
+ export function recordWorkflowChildTerminalFailureV3(input) {
113
+ withGenerationCustodyV3(input.coordRoot, [liveInstanceIdV3(input.instanceId)], () => {
114
+ const row = readLiveCoordinationRow(input.coordRoot, input.instanceId);
115
+ if (!row || row.v3_generation_id !== input.expectedGenerationId)
116
+ return;
117
+ recordLiveLifecycleChangeV3({
118
+ coordRoot: input.coordRoot,
119
+ owner: input.instanceId,
120
+ nativeSessionId: input.sessionId ?? input.instanceId,
121
+ adapter: workflowAdapter(input.adapter),
122
+ expectedGenerationId: input.expectedGenerationId,
123
+ state: "blocked",
124
+ reason: input.reason,
125
+ });
126
+ });
127
+ }
128
+ function heldPrefix(input) {
129
+ if (!/^gen_[a-zA-Z0-9._-]{1,128}$/.test(input.expectedGenerationId))
130
+ throw new Error("workflow_child_v3_generation_invalid");
131
+ return `${basename(heartbeatPath(input.coordRoot, input.instanceId))}.terminal-${input.expectedGenerationId}-`;
132
+ }
133
+ function recoverHeldCaches(input) {
134
+ const path = heartbeatPath(input.coordRoot, input.instanceId);
135
+ if (!existsSync(dirname(path)))
136
+ return;
137
+ const prefix = heldPrefix(input);
138
+ for (const name of readdirSync(dirname(path)).filter((value) => value.startsWith(prefix))) {
139
+ finishHeldCache(`${dirname(path)}/${name}`, path, input.expectedGenerationId);
140
+ }
141
+ }
142
+ function clearExpectedCache(input) {
143
+ recoverHeldCaches(input);
144
+ if (readHeartbeat(input.coordRoot, input.instanceId)?.v3_generation_id !==
145
+ input.expectedGenerationId)
146
+ return;
147
+ const path = heartbeatPath(input.coordRoot, input.instanceId);
148
+ const held = `${dirname(path)}/${heldPrefix(input)}${randomUUID()}`;
149
+ try {
150
+ // A unique held name pins the actual bytes atomically. Checking before
151
+ // unlink alone could consume a replacement published at this boundary.
152
+ renameSync(path, held);
153
+ fsyncParentDirectory(path);
154
+ }
155
+ catch (error) {
156
+ if (error.code === "ENOENT")
157
+ return;
158
+ throw error;
159
+ }
160
+ finishHeldCache(held, path, input.expectedGenerationId);
161
+ }
162
+ function finishHeldCache(held, path, expectedGenerationId) {
163
+ let removed = false;
164
+ let cleanupFailed = false;
165
+ let cleanupError;
166
+ try {
167
+ const cache = JSON.parse(readFileSync(held, "utf8"));
168
+ if (cache.v3_generation_id === expectedGenerationId) {
169
+ unlinkSync(held);
170
+ fsyncParentDirectory(held);
171
+ removed = true;
172
+ }
173
+ }
174
+ catch (error) {
175
+ cleanupFailed = true;
176
+ cleanupError = error;
177
+ }
178
+ if (!removed) {
179
+ try {
180
+ // Never overwrite a later publication. Keep the held evidence when
181
+ // another cache already owns the live name.
182
+ linkSync(held, path);
183
+ fsyncParentDirectory(path);
184
+ unlinkSync(held);
185
+ fsyncParentDirectory(held);
186
+ }
187
+ catch (error) {
188
+ if (error.code !== "EEXIST") {
189
+ if (cleanupFailed) {
190
+ throw new AggregateError([cleanupError, error], `workflow_child_v3_cache_cleanup_and_restore_failed: ${String(cleanupError)}; restoration: ${String(error)}`);
191
+ }
192
+ throw error;
193
+ }
194
+ }
195
+ }
196
+ if (cleanupFailed)
197
+ throw cleanupError;
77
198
  }
78
199
  function requireV3Route(coordRoot) {
79
200
  let route = resolveLiveEventLedgerRouteV3(coordRoot);
@@ -1,4 +1,4 @@
1
- import type { RepoSnapshot } from "../context/index.js";
1
+ import { type RepoSnapshot } from "../context/observation.js";
2
2
  import type { NormalizedPolicy, PolicyDecision, PolicyIsolation, PolicyNetworkAccess } from "../policy/index.js";
3
3
  import type { AcceptanceCriterion, AcceptanceResult, AcceptanceSummary, AdapterAttestationCitation, AdapterEvidenceCapability, ResultDigest, SpawnFailureClass, WorkflowAgentProof, WorkflowAttemptContext, WorkflowDiagnosticAdmissionProof, WorkflowEvidenceInput, WorkflowEvidenceRecord, WorkflowMeta, WorkflowProof, WorkflowSandboxProjectionEvidence, WorkflowWorkContext } from "./types.js";
4
4
  import type { WorkspaceAttestation, WorkspaceBinding, WorkspaceCompatibilityExecutionEvidence } from "./workspaces/index.js";
@@ -72,12 +72,12 @@ export declare function digestResult(value: unknown, kind?: "text" | "json"): Re
72
72
  * attempt was informative about the work — charge it. This also keeps a
73
73
  * resumed run whose earlier segment did real work from being written off by
74
74
  * a later environment failure.
75
- * 3. Otherwise, among the failed agents, environment wins over upstream: a
76
- * missing binary means nothing ran at all, and it is the operator-chosen
77
- * hard stop.
75
+ * 3. Every participant must have failed with positive no-effect evidence,
76
+ * observed absent output and no refusal. A class alone is insufficient.
77
+ * Among those eligible failures, environment is the hard stop.
78
78
  * 4. Anything else is undefined ⇒ a charged work failure, exactly as today.
79
79
  */
80
- export declare function deriveRunFailureClass(status: "succeeded" | "failed", agents: readonly Pick<WorkflowAgentProof, "status" | "class">[]): SpawnFailureClass | undefined;
80
+ export declare function deriveRunFailureClass(status: "succeeded" | "failed", agents: readonly Pick<WorkflowAgentProof, "status" | "class" | "failure_evidence">[]): SpawnFailureClass | undefined;
81
81
  export declare function buildWorkflowProof(input: BuildWorkflowProofInput): WorkflowProof;
82
82
  export declare function writeWorkflowProof(path: string, proof: WorkflowProof): void;
83
83
  export declare function readWorkflowProof(coordRoot: string, runId: string): WorkflowProof;