@stigmer/runner 3.15.0 → 3.15.2-dev.20260913223433

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 (518) hide show
  1. package/README.md +2 -4
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/__test-utils__/execution-record-fixture.d.ts +14 -0
  4. package/dist/__test-utils__/execution-record-fixture.js +4 -0
  5. package/dist/__test-utils__/execution-record-fixture.js.map +1 -1
  6. package/dist/__test-utils__/file-review-projection.d.ts +40 -0
  7. package/dist/__test-utils__/file-review-projection.js +136 -0
  8. package/dist/__test-utils__/file-review-projection.js.map +1 -0
  9. package/dist/__test-utils__/harness-contract/types.d.ts +23 -1
  10. package/dist/__test-utils__/harness-contract/types.js +15 -0
  11. package/dist/__test-utils__/harness-contract/types.js.map +1 -1
  12. package/dist/__test-utils__/hermetic-activity.d.ts +44 -12
  13. package/dist/__test-utils__/hermetic-activity.js +52 -14
  14. package/dist/__test-utils__/hermetic-activity.js.map +1 -1
  15. package/dist/__test-utils__/model-registry-fixture.d.ts +32 -7
  16. package/dist/__test-utils__/model-registry-fixture.js +31 -7
  17. package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
  18. package/dist/__test-utils__/turn-input-fixture.d.ts +1 -1
  19. package/dist/__test-utils__/turn-input-fixture.js +1 -1
  20. package/dist/__test-utils__/turn-input-fixture.js.map +1 -1
  21. package/dist/activities/call-agent.js +2 -2
  22. package/dist/activities/call-agent.js.map +1 -1
  23. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +45 -3
  24. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
  25. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +0 -9
  26. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +2 -41
  27. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
  28. package/dist/activities/execute-cursor/adapter.d.ts +7 -5
  29. package/dist/activities/execute-cursor/adapter.js +7 -5
  30. package/dist/activities/execute-cursor/adapter.js.map +1 -1
  31. package/dist/activities/execute-cursor/approval-policy.d.ts +5 -2
  32. package/dist/activities/execute-cursor/approval-policy.js +5 -2
  33. package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
  34. package/dist/activities/execute-cursor/approval-state.d.ts +16 -8
  35. package/dist/activities/execute-cursor/approval-state.js +6 -3
  36. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  37. package/dist/activities/execute-cursor/cas-observations.d.ts +14 -2
  38. package/dist/activities/execute-cursor/cas-observations.js +19 -2
  39. package/dist/activities/execute-cursor/cas-observations.js.map +1 -1
  40. package/dist/activities/execute-cursor/cursor-capabilities.d.ts +8 -4
  41. package/dist/activities/execute-cursor/cursor-capabilities.js +21 -4
  42. package/dist/activities/execute-cursor/cursor-capabilities.js.map +1 -1
  43. package/dist/activities/execute-cursor/message-translator.d.ts +12 -20
  44. package/dist/activities/execute-cursor/message-translator.js +19 -32
  45. package/dist/activities/execute-cursor/message-translator.js.map +1 -1
  46. package/dist/activities/execute-cursor/prompt-builder.d.ts +48 -58
  47. package/dist/activities/execute-cursor/prompt-builder.js +76 -95
  48. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  49. package/dist/activities/execute-cursor/turn-boundary.d.ts +27 -55
  50. package/dist/activities/execute-cursor/turn-boundary.js +30 -90
  51. package/dist/activities/execute-cursor/turn-boundary.js.map +1 -1
  52. package/dist/activities/execute-cursor/turn-settle.d.ts +11 -7
  53. package/dist/activities/execute-cursor/turn-settle.js +37 -49
  54. package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
  55. package/dist/activities/execute-cursor/turn-setup.d.ts +16 -24
  56. package/dist/activities/execute-cursor/turn-setup.js +29 -59
  57. package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
  58. package/dist/activities/execute-cursor/turn-stream.d.ts +3 -8
  59. package/dist/activities/execute-cursor/turn-stream.js +9 -22
  60. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  61. package/dist/activities/execute-cursor/turn.js +5 -1
  62. package/dist/activities/execute-cursor/turn.js.map +1 -1
  63. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +93 -0
  64. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +348 -0
  65. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js.map +1 -0
  66. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +170 -0
  67. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +225 -0
  68. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -0
  69. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.d.ts +24 -0
  70. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +41 -0
  71. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -0
  72. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +54 -0
  73. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +72 -0
  74. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -0
  75. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +187 -23
  76. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +238 -27
  77. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
  78. package/dist/activities/execute-deep-agent/adapter.d.ts +41 -0
  79. package/dist/activities/execute-deep-agent/adapter.js +78 -0
  80. package/dist/activities/execute-deep-agent/adapter.js.map +1 -0
  81. package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +8 -0
  82. package/dist/activities/execute-deep-agent/cas-capture-observer.js +9 -0
  83. package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -1
  84. package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +31 -0
  85. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +40 -0
  86. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js.map +1 -0
  87. package/dist/activities/execute-deep-agent/execution-state.d.ts +4 -11
  88. package/dist/activities/execute-deep-agent/execution-state.js +4 -12
  89. package/dist/activities/execute-deep-agent/execution-state.js.map +1 -1
  90. package/dist/activities/execute-deep-agent/hitl.d.ts +49 -47
  91. package/dist/activities/execute-deep-agent/hitl.js +77 -103
  92. package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
  93. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +51 -23
  94. package/dist/activities/execute-deep-agent/prompt-builder.js +115 -76
  95. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  96. package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +18 -32
  97. package/dist/activities/execute-deep-agent/status-builder-shared.js +10 -65
  98. package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
  99. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +10 -8
  100. package/dist/activities/execute-deep-agent/streaming-side-effects.js +12 -14
  101. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  102. package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +36 -13
  103. package/dist/activities/execute-deep-agent/subagent-tracker.js +76 -39
  104. package/dist/activities/execute-deep-agent/subagent-tracker.js.map +1 -1
  105. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -28
  106. package/dist/activities/execute-deep-agent/subagent-transformer.js +29 -82
  107. package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
  108. package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +3 -2
  109. package/dist/activities/execute-deep-agent/subagent-wiring.js +2 -2
  110. package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
  111. package/dist/activities/execute-deep-agent/turn-settle.d.ts +42 -0
  112. package/dist/activities/execute-deep-agent/turn-settle.js +146 -0
  113. package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -0
  114. package/dist/activities/execute-deep-agent/turn-setup.d.ts +212 -0
  115. package/dist/activities/execute-deep-agent/turn-setup.js +430 -0
  116. package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -0
  117. package/dist/activities/execute-deep-agent/turn-stream.d.ts +97 -0
  118. package/dist/activities/execute-deep-agent/turn-stream.js +242 -0
  119. package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -0
  120. package/dist/activities/execute-deep-agent/turn.d.ts +42 -0
  121. package/dist/activities/execute-deep-agent/turn.js +116 -0
  122. package/dist/activities/execute-deep-agent/turn.js.map +1 -0
  123. package/dist/activities/execute-deep-agent/v3-event-recorder.d.ts +6 -5
  124. package/dist/activities/execute-deep-agent/v3-event-recorder.js +6 -5
  125. package/dist/activities/execute-deep-agent/v3-event-recorder.js.map +1 -1
  126. package/dist/activities/execute-deep-agent/v3-events.d.ts +1 -1
  127. package/dist/activities/execute-deep-agent/v3-events.js +1 -1
  128. package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +60 -17
  129. package/dist/activities/execute-deep-agent/v3-status-builder.js +72 -57
  130. package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
  131. package/dist/harness/approval-decisions.d.ts +61 -0
  132. package/dist/harness/approval-decisions.js +102 -0
  133. package/dist/harness/approval-decisions.js.map +1 -0
  134. package/dist/harness/capabilities.d.ts +16 -5
  135. package/dist/harness/capabilities.js +9 -0
  136. package/dist/harness/capabilities.js.map +1 -1
  137. package/dist/harness/capture.d.ts +144 -0
  138. package/dist/harness/capture.js +219 -0
  139. package/dist/harness/capture.js.map +1 -0
  140. package/dist/harness/persist-chokepoint.d.ts +19 -4
  141. package/dist/harness/persist-chokepoint.js +17 -4
  142. package/dist/harness/persist-chokepoint.js.map +1 -1
  143. package/dist/harness/run-turn.js +115 -29
  144. package/dist/harness/run-turn.js.map +1 -1
  145. package/dist/harness/terminal-table.d.ts +40 -11
  146. package/dist/harness/terminal-table.js +58 -19
  147. package/dist/harness/terminal-table.js.map +1 -1
  148. package/dist/harness/turn-context.d.ts +141 -76
  149. package/dist/harness/turn-context.js +194 -99
  150. package/dist/harness/turn-context.js.map +1 -1
  151. package/dist/harness/types.d.ts +65 -12
  152. package/dist/harness/types.js +8 -2
  153. package/dist/harness/types.js.map +1 -1
  154. package/dist/harness-adapters.d.ts +12 -8
  155. package/dist/harness-adapters.js +17 -9
  156. package/dist/harness-adapters.js.map +1 -1
  157. package/dist/middleware/approval-gate.d.ts +1 -1
  158. package/dist/middleware/approval-gate.js +1 -1
  159. package/dist/middleware/approval-gate.js.map +1 -1
  160. package/dist/middleware/cost-advisory.d.ts +39 -0
  161. package/dist/middleware/{cost-cap.js → cost-advisory.js} +37 -55
  162. package/dist/middleware/cost-advisory.js.map +1 -0
  163. package/dist/middleware/index.d.ts +23 -12
  164. package/dist/middleware/index.js +26 -18
  165. package/dist/middleware/index.js.map +1 -1
  166. package/dist/middleware/types.d.ts +3 -2
  167. package/dist/runner-manager.js +1 -5
  168. package/dist/runner-manager.js.map +1 -1
  169. package/dist/runner.js +1 -5
  170. package/dist/runner.js.map +1 -1
  171. package/dist/shared/attachment-resolver.d.ts +41 -18
  172. package/dist/shared/attachment-resolver.js +178 -46
  173. package/dist/shared/attachment-resolver.js.map +1 -1
  174. package/dist/shared/attachment-zip.d.ts +85 -0
  175. package/dist/shared/attachment-zip.js +185 -0
  176. package/dist/shared/attachment-zip.js.map +1 -0
  177. package/dist/shared/connect-backfill.js +5 -4
  178. package/dist/shared/connect-backfill.js.map +1 -1
  179. package/dist/shared/cost-guard.d.ts +3 -3
  180. package/dist/shared/cost-guard.js +3 -3
  181. package/dist/shared/execution-status-writer.d.ts +5 -5
  182. package/dist/shared/execution-status-writer.js +5 -5
  183. package/dist/shared/extract-structured-output.d.ts +8 -7
  184. package/dist/shared/extract-structured-output.js +8 -7
  185. package/dist/shared/extract-structured-output.js.map +1 -1
  186. package/dist/shared/filereview/cas-progress.d.ts +1 -18
  187. package/dist/shared/filereview/cas-progress.js +4 -0
  188. package/dist/shared/filereview/cas-progress.js.map +1 -1
  189. package/dist/shared/filereview/cas-touched.d.ts +56 -0
  190. package/dist/shared/filereview/cas-touched.js +63 -0
  191. package/dist/shared/filereview/cas-touched.js.map +1 -0
  192. package/dist/shared/filereview/index.d.ts +2 -1
  193. package/dist/shared/filereview/index.js +2 -1
  194. package/dist/shared/filereview/index.js.map +1 -1
  195. package/dist/shared/mcp-resolver.d.ts +4 -5
  196. package/dist/shared/mcp-resolver.js +4 -5
  197. package/dist/shared/mcp-resolver.js.map +1 -1
  198. package/dist/shared/persist-decision.d.ts +3 -4
  199. package/dist/shared/persist-decision.js +3 -4
  200. package/dist/shared/persist-decision.js.map +1 -1
  201. package/dist/shared/plan-mode-permissions.d.ts +2 -2
  202. package/dist/shared/plan-mode-permissions.js +2 -2
  203. package/dist/shared/plan-mode-prompt.d.ts +1 -1
  204. package/dist/shared/plan-mode-prompt.js +1 -1
  205. package/dist/shared/prompt-sections.d.ts +163 -0
  206. package/dist/shared/prompt-sections.js +164 -0
  207. package/dist/shared/prompt-sections.js.map +1 -0
  208. package/dist/shared/skill-resolver.d.ts +4 -2
  209. package/dist/shared/skill-resolver.js +4 -2
  210. package/dist/shared/skill-resolver.js.map +1 -1
  211. package/dist/shared/tool-rounds.d.ts +32 -0
  212. package/dist/shared/tool-rounds.js +43 -0
  213. package/dist/shared/tool-rounds.js.map +1 -1
  214. package/dist/shared/tool-row.d.ts +34 -0
  215. package/dist/shared/tool-row.js +52 -0
  216. package/dist/shared/tool-row.js.map +1 -1
  217. package/dist/shared/workspace/session-provision.d.ts +3 -3
  218. package/dist/shared/workspace/session-provision.js +3 -3
  219. package/dist/shared/workspace/stigmer-link.d.ts +3 -3
  220. package/dist/shared/workspace/stigmer-link.js +3 -3
  221. package/dist/shared/workspace/writeback-coordinator.d.ts +15 -22
  222. package/dist/shared/workspace/writeback-coordinator.js +16 -53
  223. package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
  224. package/dist/tools/index.d.ts +1 -1
  225. package/dist/tools/index.js +1 -1
  226. package/package.json +4 -4
  227. package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +1 -0
  228. package/src/__test-utils__/execution-record-fixture.ts +18 -2
  229. package/src/__test-utils__/file-review-projection.ts +156 -0
  230. package/src/__test-utils__/git-workspace-fixture.ts +58 -0
  231. package/src/__test-utils__/harness-boot-order-child.ts +7 -2
  232. package/src/__test-utils__/harness-contract/contract.ts +33 -20
  233. package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
  234. package/src/__test-utils__/harness-contract/runtime-contract.ts +328 -10
  235. package/src/__test-utils__/harness-contract/scripted-adapter.ts +38 -6
  236. package/src/__test-utils__/harness-contract/types.ts +27 -3
  237. package/src/__test-utils__/hermetic-activity.ts +52 -14
  238. package/src/__test-utils__/model-registry-fixture.ts +32 -7
  239. package/src/__test-utils__/module-specifiers.ts +24 -0
  240. package/src/__test-utils__/turn-input-fixture.ts +2 -2
  241. package/src/__tests__/harness-boot-order.test.ts +9 -2
  242. package/src/activities/call-agent.ts +2 -2
  243. package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +47 -3
  244. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +2 -48
  245. package/src/activities/execute-cursor/__tests__/approval-decisions-agree.test.ts +1 -1
  246. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +6 -1
  247. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +52 -27
  248. package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +25 -0
  249. package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.everything.prompt.md +166 -0
  250. package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.plan-mode.prompt.md +186 -0
  251. package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.recovery.prompt.md +191 -0
  252. package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.reinvocation.prompt.md +13 -0
  253. package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.prefixed.prompt.md +32 -0
  254. package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.raw.prompt.md +1 -0
  255. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -2
  256. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-error-at-create.status.json +4 -0
  257. package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +24 -4
  258. package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +6 -5
  259. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +9 -0
  260. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +11 -8
  261. package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +229 -0
  262. package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +3 -3
  263. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +2 -2
  264. package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +78 -234
  265. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +1 -26
  266. package/src/activities/execute-cursor/adapter.ts +7 -5
  267. package/src/activities/execute-cursor/approval-policy.ts +5 -2
  268. package/src/activities/execute-cursor/approval-state.ts +16 -8
  269. package/src/activities/execute-cursor/cas-observations.ts +20 -2
  270. package/src/activities/execute-cursor/cursor-capabilities.ts +24 -5
  271. package/src/activities/execute-cursor/message-translator.ts +29 -41
  272. package/src/activities/execute-cursor/prompt-builder.ts +99 -157
  273. package/src/activities/execute-cursor/turn-boundary.ts +34 -140
  274. package/src/activities/execute-cursor/turn-settle.ts +37 -51
  275. package/src/activities/execute-cursor/turn-setup.ts +30 -70
  276. package/src/activities/execute-cursor/turn-stream.ts +8 -31
  277. package/src/activities/execute-cursor/turn.ts +5 -1
  278. package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +391 -0
  279. package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +313 -0
  280. package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +48 -0
  281. package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +85 -0
  282. package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +383 -36
  283. package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +76 -0
  284. package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +47 -0
  285. package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +80 -0
  286. package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +1 -1
  287. package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -2
  288. package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -3
  289. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.build-from-plan.prompt.md +172 -0
  290. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.everything.prompt.md +167 -0
  291. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.minimal.prompt.md +36 -0
  292. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.plan-mode.prompt.md +178 -0
  293. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.skills-below-threshold.prompt.md +158 -0
  294. package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +210 -0
  295. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +74 -0
  296. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +104 -0
  297. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +78 -0
  298. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +138 -0
  299. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +68 -0
  300. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +154 -0
  301. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +116 -0
  302. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +69 -0
  303. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +67 -0
  304. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/plain-turn.status.json +47 -0
  305. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +68 -0
  306. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +280 -0
  307. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +77 -0
  308. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +18 -0
  309. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +99 -0
  310. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +109 -0
  311. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +76 -0
  312. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.text-fallback.status.json +51 -0
  313. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tier2.status.json +51 -0
  314. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tool-strategy.status.json +56 -0
  315. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +90 -0
  316. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +70 -0
  317. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +71 -0
  318. package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +144 -0
  319. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +184 -0
  320. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +203 -0
  321. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +170 -0
  322. package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +175 -0
  323. package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +206 -0
  324. package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +142 -0
  325. package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +126 -0
  326. package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +139 -0
  327. package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +122 -0
  328. package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +236 -0
  329. package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +161 -0
  330. package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +157 -0
  331. package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +152 -0
  332. package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +50 -122
  333. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +8 -6
  334. package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +2 -2
  335. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +35 -35
  336. package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +247 -0
  337. package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +329 -0
  338. package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +1 -20
  339. package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +78 -0
  340. package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +1 -1
  341. package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +2 -2
  342. package/src/activities/execute-deep-agent/__tests__/subagent-tracker.test.ts +57 -31
  343. package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +19 -79
  344. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +15 -15
  345. package/src/activities/execute-deep-agent/__tests__/summarization-verification.test.ts +1 -1
  346. package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +391 -0
  347. package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +343 -38
  348. package/src/activities/execute-deep-agent/__tests__/vision-input.test.ts +5 -5
  349. package/src/activities/execute-deep-agent/adapter.ts +94 -0
  350. package/src/activities/execute-deep-agent/cas-capture-observer.ts +11 -0
  351. package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +43 -0
  352. package/src/activities/execute-deep-agent/execution-state.ts +4 -13
  353. package/src/activities/execute-deep-agent/hitl.ts +100 -123
  354. package/src/activities/execute-deep-agent/prompt-builder.ts +141 -115
  355. package/src/activities/execute-deep-agent/status-builder-shared.ts +21 -84
  356. package/src/activities/execute-deep-agent/streaming-side-effects.ts +13 -19
  357. package/src/activities/execute-deep-agent/subagent-tracker.ts +77 -42
  358. package/src/activities/execute-deep-agent/subagent-transformer.ts +46 -105
  359. package/src/activities/execute-deep-agent/subagent-wiring.ts +5 -4
  360. package/src/activities/execute-deep-agent/turn-settle.ts +164 -0
  361. package/src/activities/execute-deep-agent/turn-setup.ts +541 -0
  362. package/src/activities/execute-deep-agent/turn-stream.ts +313 -0
  363. package/src/activities/execute-deep-agent/turn.ts +131 -0
  364. package/src/activities/execute-deep-agent/v3-event-recorder.ts +6 -5
  365. package/src/activities/execute-deep-agent/v3-events.ts +1 -1
  366. package/src/activities/execute-deep-agent/v3-status-builder.ts +84 -63
  367. package/src/harness/__tests__/approval-decisions.test.ts +116 -0
  368. package/src/harness/__tests__/capture.test.ts +268 -0
  369. package/src/harness/__tests__/mount-skills.test.ts +169 -0
  370. package/src/harness/__tests__/persist-chokepoint.test.ts +38 -2
  371. package/src/harness/__tests__/run-turn-deterministic.test.ts +94 -0
  372. package/src/harness/__tests__/run-turn.test.ts +39 -23
  373. package/src/harness/__tests__/turn-attachments-vision.test.ts +80 -0
  374. package/src/harness/__tests__/turn-context.test.ts +73 -26
  375. package/src/harness/approval-decisions.ts +104 -0
  376. package/src/harness/capabilities.ts +16 -5
  377. package/src/harness/capture.ts +301 -0
  378. package/src/harness/persist-chokepoint.ts +24 -4
  379. package/src/harness/run-turn.ts +120 -29
  380. package/src/harness/terminal-table.ts +61 -20
  381. package/src/harness/turn-context.ts +208 -111
  382. package/src/harness/types.ts +66 -12
  383. package/src/harness-adapters.ts +17 -9
  384. package/src/middleware/__tests__/approval-gate.test.ts +1 -1
  385. package/src/middleware/__tests__/cost-advisory.test.ts +113 -0
  386. package/src/middleware/approval-gate.ts +2 -2
  387. package/src/middleware/{cost-cap.ts → cost-advisory.ts} +40 -70
  388. package/src/middleware/index.ts +29 -29
  389. package/src/middleware/types.ts +3 -2
  390. package/src/runner-manager.ts +0 -8
  391. package/src/runner.ts +0 -8
  392. package/src/shared/__tests__/attachment-naming.test.ts +6 -19
  393. package/src/shared/__tests__/attachment-resolver.test.ts +158 -8
  394. package/src/shared/__tests__/attachment-zip.test.ts +236 -0
  395. package/src/shared/__tests__/bedrock-adapter.test.ts +1 -1
  396. package/src/shared/__tests__/blueprint-resolver.test.ts +43 -0
  397. package/src/shared/__tests__/cost-guard.test.ts +3 -3
  398. package/src/shared/__tests__/prompt-sections.test.ts +167 -0
  399. package/src/shared/__tests__/secret-leak-scan.test.ts +2 -2
  400. package/src/{activities/execute-deep-agent → shared}/__tests__/stamp-flowed-rows.test.ts +33 -10
  401. package/src/shared/__tests__/vertex-adapter.test.ts +1 -1
  402. package/src/shared/attachment-resolver.ts +218 -55
  403. package/src/shared/attachment-zip.ts +281 -0
  404. package/src/shared/connect-backfill.ts +9 -11
  405. package/src/shared/cost-guard.ts +3 -3
  406. package/src/shared/execution-status-writer.ts +5 -5
  407. package/src/shared/extract-structured-output.ts +8 -7
  408. package/src/shared/filereview/__tests__/capture.test.ts +86 -4
  409. package/src/shared/filereview/__tests__/cas-progress.test.ts +2 -1
  410. package/src/shared/filereview/cas-progress.ts +5 -19
  411. package/src/shared/filereview/cas-touched.ts +89 -0
  412. package/src/shared/filereview/index.ts +4 -2
  413. package/src/shared/mcp-resolver.ts +4 -5
  414. package/src/shared/persist-decision.ts +3 -4
  415. package/src/shared/plan-mode-permissions.ts +2 -2
  416. package/src/shared/plan-mode-prompt.ts +1 -1
  417. package/src/shared/prompt-sections.ts +256 -0
  418. package/src/shared/skill-resolver.ts +4 -2
  419. package/src/shared/tool-rounds.ts +48 -0
  420. package/src/shared/tool-row.ts +61 -0
  421. package/src/shared/workspace/__tests__/stigmer-link.test.ts +7 -22
  422. package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +20 -61
  423. package/src/shared/workspace/session-provision.ts +3 -3
  424. package/src/shared/workspace/stigmer-link.ts +3 -3
  425. package/src/shared/workspace/writeback-coordinator.ts +16 -64
  426. package/src/tools/index.ts +1 -1
  427. package/dist/activities/execute-cursor/capture-flow.d.ts +0 -142
  428. package/dist/activities/execute-cursor/capture-flow.js +0 -285
  429. package/dist/activities/execute-cursor/capture-flow.js.map +0 -1
  430. package/dist/activities/execute-cursor/command-provenance.d.ts +0 -48
  431. package/dist/activities/execute-cursor/command-provenance.js +0 -38
  432. package/dist/activities/execute-cursor/command-provenance.js.map +0 -1
  433. package/dist/activities/execute-deep-agent/attachment-injector.d.ts +0 -134
  434. package/dist/activities/execute-deep-agent/attachment-injector.js +0 -386
  435. package/dist/activities/execute-deep-agent/attachment-injector.js.map +0 -1
  436. package/dist/activities/execute-deep-agent/command-provenance.d.ts +0 -61
  437. package/dist/activities/execute-deep-agent/command-provenance.js +0 -72
  438. package/dist/activities/execute-deep-agent/command-provenance.js.map +0 -1
  439. package/dist/activities/execute-deep-agent/environment.d.ts +0 -24
  440. package/dist/activities/execute-deep-agent/environment.js +0 -58
  441. package/dist/activities/execute-deep-agent/environment.js.map +0 -1
  442. package/dist/activities/execute-deep-agent/event-recorder.d.ts +0 -21
  443. package/dist/activities/execute-deep-agent/event-recorder.js +0 -67
  444. package/dist/activities/execute-deep-agent/event-recorder.js.map +0 -1
  445. package/dist/activities/execute-deep-agent/index.d.ts +0 -15
  446. package/dist/activities/execute-deep-agent/index.js +0 -918
  447. package/dist/activities/execute-deep-agent/index.js.map +0 -1
  448. package/dist/activities/execute-deep-agent/mcp-gate.d.ts +0 -28
  449. package/dist/activities/execute-deep-agent/mcp-gate.js +0 -22
  450. package/dist/activities/execute-deep-agent/mcp-gate.js.map +0 -1
  451. package/dist/activities/execute-deep-agent/post-stream.d.ts +0 -23
  452. package/dist/activities/execute-deep-agent/post-stream.js +0 -71
  453. package/dist/activities/execute-deep-agent/post-stream.js.map +0 -1
  454. package/dist/activities/execute-deep-agent/setup.d.ts +0 -136
  455. package/dist/activities/execute-deep-agent/setup.js +0 -767
  456. package/dist/activities/execute-deep-agent/setup.js.map +0 -1
  457. package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +0 -36
  458. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +0 -56
  459. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +0 -1
  460. package/dist/activities/execute-deep-agent/status-builder.d.ts +0 -93
  461. package/dist/activities/execute-deep-agent/status-builder.js +0 -365
  462. package/dist/activities/execute-deep-agent/status-builder.js.map +0 -1
  463. package/dist/activities/execute-deep-agent/streaming-terminal.d.ts +0 -21
  464. package/dist/activities/execute-deep-agent/streaming-terminal.js +0 -80
  465. package/dist/activities/execute-deep-agent/streaming-terminal.js.map +0 -1
  466. package/dist/activities/execute-deep-agent/streaming-v3.d.ts +0 -13
  467. package/dist/activities/execute-deep-agent/streaming-v3.js +0 -174
  468. package/dist/activities/execute-deep-agent/streaming-v3.js.map +0 -1
  469. package/dist/activities/execute-deep-agent/streaming.d.ts +0 -81
  470. package/dist/activities/execute-deep-agent/streaming.js +0 -160
  471. package/dist/activities/execute-deep-agent/streaming.js.map +0 -1
  472. package/dist/middleware/cost-cap.d.ts +0 -22
  473. package/dist/middleware/cost-cap.js.map +0 -1
  474. package/dist/middleware/graceful-stop.d.ts +0 -17
  475. package/dist/middleware/graceful-stop.js +0 -63
  476. package/dist/middleware/graceful-stop.js.map +0 -1
  477. package/dist/shared/skill-writer.d.ts +0 -75
  478. package/dist/shared/skill-writer.js +0 -207
  479. package/dist/shared/skill-writer.js.map +0 -1
  480. package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +0 -1032
  481. package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +0 -240
  482. package/src/activities/execute-cursor/__tests__/progress-substrate.test.ts +0 -169
  483. package/src/activities/execute-cursor/capture-flow.ts +0 -379
  484. package/src/activities/execute-cursor/command-provenance.ts +0 -73
  485. package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +0 -1161
  486. package/src/activities/execute-deep-agent/__tests__/command-provenance.test.ts +0 -252
  487. package/src/activities/execute-deep-agent/__tests__/environment.test.ts +0 -108
  488. package/src/activities/execute-deep-agent/__tests__/event-recorder.test.ts +0 -150
  489. package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +0 -226
  490. package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +0 -318
  491. package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +0 -351
  492. package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +0 -293
  493. package/src/activities/execute-deep-agent/__tests__/index.test.ts +0 -99
  494. package/src/activities/execute-deep-agent/__tests__/mcp-gate.test.ts +0 -44
  495. package/src/activities/execute-deep-agent/__tests__/post-stream.test.ts +0 -112
  496. package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +0 -358
  497. package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +0 -1933
  498. package/src/activities/execute-deep-agent/__tests__/streaming-terminal.test.ts +0 -40
  499. package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +0 -597
  500. package/src/activities/execute-deep-agent/__tests__/streaming.test.ts +0 -508
  501. package/src/activities/execute-deep-agent/attachment-injector.ts +0 -627
  502. package/src/activities/execute-deep-agent/command-provenance.ts +0 -102
  503. package/src/activities/execute-deep-agent/environment.ts +0 -76
  504. package/src/activities/execute-deep-agent/event-recorder.ts +0 -95
  505. package/src/activities/execute-deep-agent/index.ts +0 -1072
  506. package/src/activities/execute-deep-agent/mcp-gate.ts +0 -37
  507. package/src/activities/execute-deep-agent/post-stream.ts +0 -109
  508. package/src/activities/execute-deep-agent/setup.ts +0 -1081
  509. package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +0 -64
  510. package/src/activities/execute-deep-agent/status-builder.ts +0 -481
  511. package/src/activities/execute-deep-agent/streaming-terminal.ts +0 -106
  512. package/src/activities/execute-deep-agent/streaming-v3.ts +0 -277
  513. package/src/activities/execute-deep-agent/streaming.ts +0 -313
  514. package/src/middleware/__tests__/cost-cap.test.ts +0 -192
  515. package/src/middleware/__tests__/graceful-stop.test.ts +0 -105
  516. package/src/middleware/graceful-stop.ts +0 -86
  517. package/src/shared/__tests__/skill-writer.test.ts +0 -372
  518. package/src/shared/skill-writer.ts +0 -266
@@ -1,13 +1,33 @@
1
1
  /**
2
2
  * V3StatusBuilder — consumes StigmerRunEvents from the V3ProtocolNormalizer
3
- * and progressively builds the AgentExecutionStatus proto.
3
+ * and progressively builds the transcript half of the AgentExecutionStatus
4
+ * proto it is handed: messages, tool-call rows and their approval status,
5
+ * sub-agent rows, todos, artifacts, write-backs.
4
6
  *
5
- * Implements the same behavioral contract as the v2 StatusBuilder for all
6
- * 8 golden scenarios, using v3 event semantics (tool_call_id keying,
7
- * content-block-level deltas, explicit tool lifecycle).
7
+ * The one transcript builder of the native harness since S3 M2b: v3 event
8
+ * semantics (tool_call_id keying, content-block-level deltas, explicit tool
9
+ * lifecycle). Its predecessor, the v2 `StatusBuilder` over LangChain's
10
+ * `streamEvents` v2, was retired with the v2 stream (Q-S3-1); the eight
11
+ * golden sequences both once had to agree on are this builder's test's.
8
12
  *
9
- * Usage is accumulated on message_finish only (standalone usage events are
13
+ * What this builder deliberately does NOT write, and who does (the adapter
14
+ * contract's field ownership, `harness/types.ts` `TurnSink`): the phase,
15
+ * `startedAt` and `streamingUsage` are the turn runtime's. A gated tool
16
+ * start is reported as the {@link awaitingApproval} fact and the caller
17
+ * (`turn-stream.ts`) decides what that means for the turn; usage is reported
18
+ * raw through {@link V3StatusBuilderOptions.onUsage} and the caller prices
19
+ * it into the sink (until S3 M2a this builder flipped WAITING_FOR_APPROVAL
20
+ * itself and summed usage into `streamingUsage` — a second writer of each
21
+ * field beside the runtime's).
22
+ *
23
+ * Usage is reported on message_finish only (standalone usage events are
10
24
  * no-ops) to prevent double-counting — v3 emits both for the same turn.
25
+ * EVERY graph's usage is reported, the sub-agents' included (S3 M2b,
26
+ * Q-M2b-1): a sub-agent's `message_finish` fires the same hook before it is
27
+ * routed to the tracker, so the execution's usage — and the cost cap the
28
+ * runtime enforces over it — is the whole execution's spend. Until M2b the
29
+ * sub-agents' turns were discarded (S3 M2a finding F-M2a-12); the cost cap
30
+ * then lived inside the graph, where a middleware saw every model call.
11
31
  */
12
32
 
13
33
  import { create, type JsonObject } from "@bufbuild/protobuf";
@@ -20,7 +40,6 @@ import type { AgentMessage, ToolCall } from "@stigmer/protos/ai/stigmer/agentic/
20
40
  import type { ExecutionArtifact } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
21
41
  import type { WorkspaceWriteBack } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/writeback_pb";
22
42
  import {
23
- ExecutionPhase,
24
43
  MessageType,
25
44
  ToolCallStatus,
26
45
  ToolKind,
@@ -31,48 +50,61 @@ import { applyTodoUpdate } from "../../shared/todos.js";
31
50
  import { ExecutionState } from "./execution-state.js";
32
51
  import { utcTimestamp } from "../../shared/status.js";
33
52
  import type { ExecutionStatusWriter } from "../../shared/execution-status-writer.js";
34
- import type { ApprovalPolicyProvider } from "./status-builder.js";
35
53
  import type { StigmerRunEvent, V3UsagePayload } from "./v3-events.js";
36
54
  import { namespaceDepth } from "./v3-events.js";
37
55
  import {
38
- UsageAccumulator,
39
56
  extractToolResultV3,
40
57
  sanitizeArgsPreview,
41
58
  stampApprovalProvenance,
59
+ type ApprovalPolicyProvider,
42
60
  } from "./status-builder-shared.js";
43
61
  import { SubAgentTracker } from "./subagent-tracker.js";
44
62
 
63
+ export interface V3StatusBuilderOptions {
64
+ /**
65
+ * Fired once per `message_finish` that carries usage — the parent graph's
66
+ * and every sub-agent's — with the payload exactly as the protocol
67
+ * delivered it. The builder never sums or prices: the caller accounts (the
68
+ * turn runtime through `TurnSink.reportUsage`, priced at the turn's model;
69
+ * a sub-agent on its own model is priced at the parent's rate, as the
70
+ * in-graph cost cap always did — per-sub-agent pricing is an S5 item).
71
+ */
72
+ readonly onUsage?: (usage: V3UsagePayload) => void;
73
+ }
74
+
45
75
  export class V3StatusBuilder implements ExecutionStatusWriter {
46
76
  readonly executionId: string;
47
77
  private readonly state: ExecutionState;
48
78
  private _forceNextUpdate = false;
79
+ private _awaitingApproval = false;
49
80
  private approvalProvider: ApprovalPolicyProvider | null = null;
50
- private readonly usageAccumulator: UsageAccumulator;
81
+ private readonly onUsage: ((usage: V3UsagePayload) => void) | undefined;
51
82
  private readonly subAgentTracker: SubAgentTracker;
52
83
 
53
84
  /** Progressive tool call arg accumulation keyed by callId. */
54
85
  private readonly toolArgBuffers = new Map<string, string>();
55
86
 
56
- constructor(executionId: string, initialStatus: AgentExecutionStatus) {
87
+ /**
88
+ * Builds INTO `status`, by reference: its `messages` and
89
+ * `subAgentExecutions` arrays are the ones indexed and pushed into, never
90
+ * replaced, so a caller that wraps the same status (the write-back
91
+ * coordinator's writer, the runtime's chokepoint) keeps seeing every row.
92
+ */
93
+ constructor(executionId: string, status: AgentExecutionStatus, options: V3StatusBuilderOptions = {}) {
57
94
  this.executionId = executionId;
58
- this.state = new ExecutionState(initialStatus);
59
-
60
- // Resume path: when constructed from a persisted transcript (status seeded
61
- // in index.ts on a durable-checkpoint resume), rebuild the tool-call index
62
- // so resumed tool_started/tool_finished events reconcile to the existing
63
- // calls instead of duplicating them. A first run carries no messages, so
64
- // this is a no-op.
65
- if (initialStatus.messages.length > 0) {
95
+ this.state = new ExecutionState(status);
96
+ this.onUsage = options.onUsage;
97
+
98
+ // Resume path: when constructed over a persisted transcript (seeded by the
99
+ // caller on a reinvocation), rebuild the tool-call index so resumed
100
+ // tool_started/tool_finished events reconcile to the existing calls
101
+ // instead of duplicating them. A first run carries no messages, so this
102
+ // is a no-op. The tracker does the same for the seeded sub-agent rows.
103
+ if (status.messages.length > 0) {
66
104
  this.state.rebuildToolCallIndex();
67
105
  }
68
106
 
69
- initialStatus.phase = ExecutionPhase.EXECUTION_IN_PROGRESS;
70
- if (!initialStatus.startedAt) {
71
- initialStatus.startedAt = utcTimestamp();
72
- }
73
-
74
- this.usageAccumulator = new UsageAccumulator();
75
- this.subAgentTracker = new SubAgentTracker();
107
+ this.subAgentTracker = new SubAgentTracker(status.subAgentExecutions);
76
108
  }
77
109
 
78
110
  setApprovalProvider(provider: ApprovalPolicyProvider): void {
@@ -83,6 +115,16 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
83
115
  return this.state.proto;
84
116
  }
85
117
 
118
+ /**
119
+ * True once a tool call this turn was gated and left WAITING_APPROVAL: the
120
+ * engine has interrupted (or will, at this step's end) and the turn should
121
+ * end awaiting a decision. A fact about the transcript, not a phase — the
122
+ * caller owns the phase.
123
+ */
124
+ get awaitingApproval(): boolean {
125
+ return this._awaitingApproval;
126
+ }
127
+
86
128
  get forceNextUpdate(): boolean {
87
129
  return this._forceNextUpdate;
88
130
  }
@@ -119,6 +161,9 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
119
161
  }
120
162
 
121
163
  if (this.subAgentTracker.isSubAgentNamespace(event.namespace)) {
164
+ // A sub-agent's spend is the execution's spend (Q-M2b-1): report it
165
+ // through the one hook before the tracker folds the message.
166
+ if (event.kind === "message_finish" && event.usage) this.onUsage?.(event.usage);
122
167
  this.subAgentTracker.routeEvent(event);
123
168
  return;
124
169
  }
@@ -219,7 +264,7 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
219
264
  }
220
265
 
221
266
  if (usage) {
222
- this.accumulateV3Usage(usage);
267
+ this.onUsage?.(usage);
223
268
  }
224
269
  }
225
270
 
@@ -257,8 +302,9 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
257
302
  namespace: string,
258
303
  ): void {
259
304
  // Resume reconciliation: the gated tool call already exists, seeded from the
260
- // persisted transcript of a prior invocation (seedStatusFromExecution in
261
- // index.ts). The durable checkpoint re-emits tool_started now that approval
305
+ // persisted transcript of a prior invocation (the runtime's
306
+ // `seedFromPersistedStatus`, `harness/turn-context.ts`). The durable
307
+ // checkpoint re-emits tool_started now that approval
262
308
  // is granted — flip the existing call to RUNNING in place rather than
263
309
  // appending a duplicate or re-triggering the approval gate. v3 keys by
264
310
  // tool_call_id, so this is an exact match (no name heuristics needed).
@@ -270,7 +316,6 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
270
316
  if (Object.keys(input).length > 0 && !existing.args) {
271
317
  existing.args = input as JsonObject;
272
318
  }
273
- this.state.toolStartTimes.set(callId, performance.now());
274
319
  this._forceNextUpdate = true;
275
320
  return;
276
321
  }
@@ -304,7 +349,8 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
304
349
 
305
350
  // Stamp authorization provenance in the same spot as tool_kind, for every
306
351
  // observed tool call (gated or auto-approved). A gated call is re-seeded on
307
- // reinvocation (index.ts) with the same source carried through the interrupt.
352
+ // reinvocation (the runtime's wide seed) with the same source carried
353
+ // through the interrupt.
308
354
  stampApprovalProvenance(tc, this.approvalProvider);
309
355
 
310
356
  if (approvalReq.requiresApproval) {
@@ -317,12 +363,11 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
317
363
  tc.argsPreview = argsPreview;
318
364
  }
319
365
 
320
- this.state.proto.phase = ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL;
366
+ this._awaitingApproval = true;
321
367
  }
322
368
 
323
369
  parentMsg.toolCalls.push(tc);
324
370
  this.state.toolCalls.set(callId, tc);
325
- this.state.toolStartTimes.set(callId, performance.now());
326
371
 
327
372
  this._forceNextUpdate = true;
328
373
  }
@@ -339,7 +384,6 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
339
384
  tc.result = extractToolResultV3(output);
340
385
  tc.completedAt = utcTimestamp();
341
386
  tc.isStreaming = false;
342
- this.state.toolStartTimes.delete(callId);
343
387
  this.toolArgBuffers.delete(callId);
344
388
 
345
389
  // Project a completed to-do write into status.todos. deepagents' write_todos
@@ -364,7 +408,6 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
364
408
  tc.error = message;
365
409
  tc.completedAt = utcTimestamp();
366
410
  tc.isStreaming = false;
367
- this.state.toolStartTimes.delete(callId);
368
411
  this.toolArgBuffers.delete(callId);
369
412
 
370
413
  this._forceNextUpdate = true;
@@ -498,21 +541,6 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
498
541
  return msg;
499
542
  }
500
543
 
501
- // ── Usage ──────────────────────────────────────────────────────────
502
-
503
- private accumulateV3Usage(usage: V3UsagePayload): void {
504
- const meta: Record<string, unknown> = {
505
- input_tokens: usage.input_tokens,
506
- output_tokens: usage.output_tokens,
507
- };
508
- if (usage.input_token_details) {
509
- meta.cache_read_input_tokens = usage.input_token_details.cache_read;
510
- meta.cache_creation_input_tokens = usage.input_token_details.cache_creation;
511
- }
512
- this.usageAccumulator.accumulate(meta);
513
- this.state.proto.streamingUsage = this.usageAccumulator.toProto();
514
- }
515
-
516
544
  // ── Sub-Agent Integration ──────────────────────────────────────────
517
545
 
518
546
  /**
@@ -525,20 +553,13 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
525
553
  }
526
554
 
527
555
  /**
528
- * Sync sub-agent executions into the proto for persistence.
529
- * Called by the streaming orchestrator before each persist.
530
- */
531
- syncSubAgentExecutions(): void {
532
- if (this.subAgentTracker.hasExecutions()) {
533
- this.state.proto.subAgentExecutions = this.subAgentTracker.getExecutions();
534
- }
535
- }
536
-
537
- /**
538
- * Cancel all in-progress sub-agents (called on parent cancellation).
556
+ * Clear the streaming flag on every sub-agent message still marked as
557
+ * streaming what a turn that stopped mid-delegation owes the transcript
558
+ * beside the CANCELLED status the caller stamps on the row itself
559
+ * (`shared/subagent-rows.ts` `cancelInProgressSubAgentProtos`, the one
560
+ * home of that transition for every harness).
539
561
  */
540
- cancelSubAgents(): void {
541
- this.subAgentTracker.cancelAll();
542
- this.syncSubAgentExecutions();
562
+ finalizeSubAgentStreaming(): void {
563
+ this.subAgentTracker.finalizeAllStreaming();
543
564
  }
544
565
  }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * `harness/approval-decisions.ts` — the runtime's one settler of a decision
3
+ * that never runs the tool. These arms came with the function from the
4
+ * native harness's `hitl.ts` (`reconcileNonExecutingDecisions`, S3 M1 /
5
+ * M2a): SKIP and REJECT settle to SKIPPED (REJECT with the pinned reason),
6
+ * APPROVE and APPROVE_ALL are left to the engine's own events, sub-agent
7
+ * transcripts are settled alike, and an undecided row is untouched. The
8
+ * reader half (`approvalDecisionsOf`) is pinned against the Cursor adapter's
9
+ * own reader in `execute-cursor/__tests__/approval-decisions-agree.test.ts`
10
+ * and exercised through `turn-context.test.ts`.
11
+ */
12
+
13
+ import { describe, it, expect } from "vitest";
14
+ import { create } from "@bufbuild/protobuf";
15
+ import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
16
+ import { AgentMessageSchema, ToolCallSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
17
+ import { SubAgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
18
+ import { ApprovalAction, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
19
+ import { REJECTED_BY_USER_ERROR, terminalizeNonExecutingDecisions } from "../approval-decisions.js";
20
+
21
+ describe("terminalizeNonExecutingDecisions", () => {
22
+ function statusWith(
23
+ toolCalls: Array<{ id: string; status: ToolCallStatus; approvalAction: ApprovalAction }>,
24
+ ) {
25
+ return create(AgentExecutionStatusSchema, {
26
+ messages: [
27
+ create(AgentMessageSchema, {
28
+ toolCalls: toolCalls.map(tc =>
29
+ create(ToolCallSchema, {
30
+ id: tc.id,
31
+ name: "test_tool",
32
+ status: tc.status,
33
+ approvalAction: tc.approvalAction,
34
+ }),
35
+ ),
36
+ }),
37
+ ],
38
+ });
39
+ }
40
+
41
+ it("terminalizes a REJECT decision to SKIPPED with the pinned reason (does not FAIL)", () => {
42
+ const status = statusWith([{
43
+ id: "call-reject",
44
+ status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
45
+ approvalAction: ApprovalAction.REJECT,
46
+ }]);
47
+
48
+ terminalizeNonExecutingDecisions(status);
49
+
50
+ const tc = status.messages[0].toolCalls[0];
51
+ expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_SKIPPED);
52
+ expect(tc.error).toBe(REJECTED_BY_USER_ERROR);
53
+ // The REJECT decision is preserved so the audit trail stays honest.
54
+ expect(tc.approvalAction).toBe(ApprovalAction.REJECT);
55
+ });
56
+
57
+ it("terminalizes a SKIP decision to SKIPPED (fixes the stuck-WAITING for skip too)", () => {
58
+ const status = statusWith([{
59
+ id: "call-skip",
60
+ status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
61
+ approvalAction: ApprovalAction.SKIP,
62
+ }]);
63
+
64
+ terminalizeNonExecutingDecisions(status);
65
+
66
+ expect(status.messages[0].toolCalls[0].status).toBe(ToolCallStatus.TOOL_CALL_SKIPPED);
67
+ });
68
+
69
+ it("leaves APPROVE / APPROVE_ALL untouched (the tool executes)", () => {
70
+ const status = statusWith([
71
+ { id: "call-approve", status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL, approvalAction: ApprovalAction.APPROVE },
72
+ { id: "call-approve-all", status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL, approvalAction: ApprovalAction.APPROVE_ALL },
73
+ ]);
74
+
75
+ terminalizeNonExecutingDecisions(status);
76
+
77
+ // Still WAITING — real tool events (not this reconciler) terminalize them.
78
+ expect(status.messages[0].toolCalls[0].status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
79
+ expect(status.messages[0].toolCalls[1].status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
80
+ });
81
+
82
+ it("settles sub-agent transcripts too", () => {
83
+ const status = create(AgentExecutionStatusSchema, {
84
+ subAgentExecutions: [
85
+ create(SubAgentExecutionSchema, {
86
+ id: "sub-1",
87
+ messages: [
88
+ create(AgentMessageSchema, {
89
+ toolCalls: [create(ToolCallSchema, {
90
+ id: "sub-call-skip",
91
+ name: "test_tool",
92
+ status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
93
+ approvalAction: ApprovalAction.SKIP,
94
+ })],
95
+ }),
96
+ ],
97
+ }),
98
+ ],
99
+ });
100
+
101
+ terminalizeNonExecutingDecisions(status);
102
+
103
+ expect(status.subAgentExecutions[0].messages[0].toolCalls[0].status).toBe(ToolCallStatus.TOOL_CALL_SKIPPED);
104
+ });
105
+
106
+ it("is a no-op when there are no decided tool calls", () => {
107
+ const status = statusWith([{
108
+ id: "call-pending",
109
+ status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
110
+ approvalAction: ApprovalAction.UNSPECIFIED,
111
+ }]);
112
+
113
+ expect(() => terminalizeNonExecutingDecisions(status)).not.toThrow();
114
+ expect(status.messages[0].toolCalls[0].status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
115
+ });
116
+ });
@@ -0,0 +1,268 @@
1
+ /**
2
+ * The runtime's capture (`harness/capture.ts`) over a REAL git work tree,
3
+ * without any engine: the pin, the candidate, the stamp, the provenance and
4
+ * the review decision, each as the runtime performs it between the
5
+ * resolution phases and the outcome table.
6
+ *
7
+ * What is asserted here and nowhere else are the two rules the lift made
8
+ * one for every harness (S3 M4, Q-M4-5, Q-M4-6):
9
+ *
10
+ * - the stamp reads the ROW's status: a COMPLETED write row created this
11
+ * turn is badged, a FAILED or WAITING one is not, a seeded prior-turn row
12
+ * keeps its own change set;
13
+ * - the provenance scopes by id-novelty and consents by the row's own
14
+ * approval: the approved command that executed ON ITS SEEDED ROW (message
15
+ * 0, the shape both engines produce — F-M4-P3) qualifies the turn, where
16
+ * a positional scope missed it.
17
+ *
18
+ * The ledger shape itself is `shared/filereview/capture.ts`'s and is pinned
19
+ * by its own tests and the two harnesses' `file-review-capture` goldens; the
20
+ * end-to-end pause and reconcile are the kit's runtime arm. The git fixture
21
+ * is the shared one every file-review scenario stands on.
22
+ */
23
+
24
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
25
+ import { tmpdir } from "node:os";
26
+ import { join } from "node:path";
27
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
28
+ import { create, type JsonObject } from "@bufbuild/protobuf";
29
+ import { AgentExecutionStatusSchema, type AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
30
+ import { AgentMessageSchema, ToolCallSchema, type ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
31
+ import { SubAgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
32
+ import { ApprovalAction, FileChangeKind, MessageType, SnapshotKind, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
33
+
34
+ import { makeInMemoryArtifactStorage } from "../../__test-utils__/fake-artifact-storage.js";
35
+ import { initGitWorkspace } from "../../__test-utils__/git-workspace-fixture.js";
36
+ import { mockWorkspaceBackend } from "../../__test-utils__/mock-workspace.js";
37
+ import type { FileReviewIdentity } from "../capabilities.js";
38
+ import { captureCandidate, deriveCommandProvenance, pinCaptureBaseline } from "../capture.js";
39
+ import type { TurnWorkspace } from "../types.js";
40
+
41
+ const EXECUTION_ID = "aex-capture-unit";
42
+ const FILE_REVIEW: FileReviewIdentity = { harnessId: "unit", excludePaths: [".unit/scratch.json"] };
43
+
44
+ function workspaceOver(root: string, turnSeq = 0): TurnWorkspace {
45
+ return {
46
+ dirs: [root],
47
+ primaryDir: root,
48
+ gitWorkspace: true,
49
+ captureMode: true,
50
+ changeSetId: `${EXECUTION_ID}:${turnSeq}`,
51
+ provision: { workspaceDirs: [root], provisionResults: [], workspaceBackend: mockWorkspaceBackend() },
52
+ };
53
+ }
54
+
55
+ /** The three row facts these arms vary: the args (a path for the stamp), the server's verdict, a prior stamp. */
56
+ interface RowFacts {
57
+ readonly args?: JsonObject;
58
+ readonly approvalAction?: ApprovalAction;
59
+ readonly fileChangeSetId?: string;
60
+ }
61
+
62
+ function row(id: string, name: string, status: ToolCallStatus, extra: RowFacts = {}): ToolCall {
63
+ return create(ToolCallSchema, { id, name, status, ...extra });
64
+ }
65
+
66
+ function messageWith(...toolCalls: ToolCall[]) {
67
+ return create(AgentMessageSchema, { type: MessageType.MESSAGE_AI, toolCalls });
68
+ }
69
+
70
+ describe("harness/capture over a git work tree", () => {
71
+ let root: string;
72
+ let status: AgentExecutionStatus;
73
+
74
+ beforeEach(() => {
75
+ root = mkdtempSync(join(tmpdir(), "stigmer-capture-unit-"));
76
+ initGitWorkspace(root, { "notes.md": "one\n", "keep.md": "keep\n" });
77
+ status = create(AgentExecutionStatusSchema, {});
78
+ });
79
+
80
+ afterEach(() => {
81
+ rmSync(root, { recursive: true, force: true });
82
+ });
83
+
84
+ it("outside capture mode there is nothing to pin", async () => {
85
+ const capture = await pinCaptureBaseline({
86
+ status,
87
+ executionId: EXECUTION_ID,
88
+ workspace: { ...workspaceOver(root), captureMode: false },
89
+ fileReview: FILE_REVIEW,
90
+ artifactStorage: undefined,
91
+ });
92
+ expect(capture).toBeUndefined();
93
+ expect(status.fileReviewEventStream).toBeUndefined();
94
+ });
95
+
96
+ it("pins the baseline, captures the net change after the turn, stamps only the COMPLETED write rows, and reports the review pending", async () => {
97
+ const workspace = workspaceOver(root);
98
+ const capture = (await pinCaptureBaseline({ status, executionId: EXECUTION_ID, workspace, fileReview: FILE_REVIEW, artifactStorage: undefined }))!;
99
+ expect(capture.baselineTree).toMatch(/^[0-9a-f]{40}$/);
100
+ expect(status.fileReviewEventStream?.events.map((e) => e.payload.case)).toEqual(["baselineCaptured"]);
101
+
102
+ // The engine's turn: one edit flowed, one edit failed, one write is still
103
+ // waiting at a gate (a deny-and-retry harness's denied row); an excluded
104
+ // path written by the harness itself must never reach the diff.
105
+ writeFileSync(join(root, "notes.md"), "one\ntwo\n");
106
+ mkdirSync(join(root, ".unit"));
107
+ writeFileSync(join(root, ".unit/scratch.json"), "{}");
108
+ status.messages.push(
109
+ messageWith(
110
+ row("tc-flowed", "write_file", ToolCallStatus.TOOL_CALL_COMPLETED, { args: { file_path: "notes.md", content: "one\ntwo\n" } }),
111
+ row("tc-failed", "write_file", ToolCallStatus.TOOL_CALL_FAILED, { args: { file_path: "other.md" } }),
112
+ row("tc-denied", "write_file", ToolCallStatus.TOOL_CALL_WAITING_APPROVAL, { args: { file_path: ".env" } }),
113
+ ),
114
+ );
115
+
116
+ const pending = await captureCandidate({
117
+ status,
118
+ executionId: EXECUTION_ID,
119
+ workspace,
120
+ fileReview: FILE_REVIEW,
121
+ artifactStorage: undefined,
122
+ capture,
123
+ globalBypass: false,
124
+ });
125
+
126
+ expect(pending).toBe(true);
127
+ const events = status.fileReviewEventStream?.events ?? [];
128
+ expect(events.map((e) => e.payload.case)).toEqual(["baselineCaptured", "candidateCaptured"]);
129
+ const candidate = events[1]!.payload;
130
+ if (candidate.case !== "candidateCaptured") throw new Error("unreachable");
131
+ expect(candidate.value.changes.map((c) => c.pathAfter), "the excluded path never enters the diff").toEqual(["notes.md"]);
132
+ expect(candidate.value.candidateSnapshot?.kind).toBe(SnapshotKind.GIT_TREE_REF);
133
+ expect(candidate.value.commandProvenance, "a file-tool turn never qualifies for auto-keep").toBeUndefined();
134
+
135
+ const byId = new Map(status.messages.flatMap((m) => m.toolCalls).map((tc) => [tc.id, tc.fileChangeSetId]));
136
+ expect(byId.get("tc-flowed"), "the COMPLETED write is badged").toBe(workspace.changeSetId);
137
+ expect(byId.get("tc-failed"), "a FAILED write did not land").toBe("");
138
+ expect(byId.get("tc-denied"), "a WAITING write did not land").toBe("");
139
+ });
140
+
141
+ it("a turn that changed nothing authors no candidate and pends no review", async () => {
142
+ const workspace = workspaceOver(root);
143
+ const capture = (await pinCaptureBaseline({ status, executionId: EXECUTION_ID, workspace, fileReview: FILE_REVIEW, artifactStorage: undefined }))!;
144
+ const pending = await captureCandidate({ status, executionId: EXECUTION_ID, workspace, fileReview: FILE_REVIEW, artifactStorage: undefined, capture, globalBypass: false });
145
+ expect(pending).toBe(false);
146
+ expect(status.fileReviewEventStream?.events.map((e) => e.payload.case)).toEqual(["baselineCaptured"]);
147
+ });
148
+
149
+ it("a turn that changed nothing on the tree stamps no row, even a COMPLETED write whose edit was reverted before the boundary", async () => {
150
+ const workspace = workspaceOver(root);
151
+ const capture = (await pinCaptureBaseline({ status, executionId: EXECUTION_ID, workspace, fileReview: FILE_REVIEW, artifactStorage: undefined }))!;
152
+ status.messages.push(messageWith(row("tc-reverted", "write_file", ToolCallStatus.TOOL_CALL_COMPLETED, { args: { file_path: "notes.md" } })));
153
+ // The engine wrote and then wrote the original back: no net change.
154
+ writeFileSync(join(root, "notes.md"), "one\n");
155
+
156
+ const pending = await captureCandidate({ status, executionId: EXECUTION_ID, workspace, fileReview: FILE_REVIEW, artifactStorage: undefined, capture, globalBypass: false });
157
+
158
+ expect(pending).toBe(false);
159
+ expect(status.messages[0]!.toolCalls[0]!.fileChangeSetId, "a row must never reference a change set that does not exist").toBe("");
160
+ });
161
+
162
+ it("the progress substrate follows the workspace shape: git-only without storage surfaces the tracked change; CAS reads what the adapter bound", async () => {
163
+ const gitOnly = (await pinCaptureBaseline({ status, executionId: EXECUTION_ID, workspace: workspaceOver(root), fileReview: FILE_REVIEW, artifactStorage: undefined }))!;
164
+ writeFileSync(join(root, "notes.md"), "one\ntwo\n");
165
+ const tracked = await gitOnly.progress.substrate.capture();
166
+ expect(tracked.changed).toBe(true);
167
+ expect(tracked.delta.entries.map((e) => e.pathAfter)).toEqual(["notes.md"]);
168
+
169
+ // A non-git workspace with storage: the CAS slice alone, over the reader
170
+ // the adapter binds; unbound it reads nothing observed.
171
+ const casRoot = mkdtempSync(join(tmpdir(), "stigmer-capture-cas-"));
172
+ try {
173
+ const casWorkspace: TurnWorkspace = { ...workspaceOver(casRoot), gitWorkspace: false };
174
+ const cas = (await pinCaptureBaseline({
175
+ status: create(AgentExecutionStatusSchema, {}),
176
+ executionId: EXECUTION_ID,
177
+ workspace: casWorkspace,
178
+ fileReview: FILE_REVIEW,
179
+ artifactStorage: makeInMemoryArtifactStorage().storage,
180
+ }))!;
181
+ expect(cas.baselineTree, "a non-git baseline is the CAS manifest, not a tree").toBe("");
182
+ writeFileSync(join(casRoot, "scratch.log"), "line\n");
183
+ expect((await cas.progress.substrate.capture()).delta.entries, "nothing bound, nothing observed").toEqual([]);
184
+ cas.casObservations = () => ({ before: new Map([["scratch.log", null]]), blockedSecretPaths: new Set() });
185
+ const observed = await cas.progress.substrate.capture();
186
+ expect(observed.delta.entries.map((e) => [e.pathAfter, e.kind])).toEqual([["scratch.log", FileChangeKind.ADD]]);
187
+ } finally {
188
+ rmSync(casRoot, { recursive: true, force: true });
189
+ }
190
+ });
191
+
192
+ it("scopes the stamp to this turn: a seeded prior-turn row keeps its change set, a sub-agent row created this turn takes the parent's", async () => {
193
+ // The seed a reinvocation carries: a prior turn's flowed row and a prior sub-agent row.
194
+ status.messages.push(messageWith(row("tc-prior", "write_file", ToolCallStatus.TOOL_CALL_COMPLETED, { fileChangeSetId: `${EXECUTION_ID}:0` })));
195
+ status.subAgentExecutions.push(
196
+ create(SubAgentExecutionSchema, { id: "sa-prior", name: "editor", messages: [messageWith(row("sa-tc-prior", "write_file", ToolCallStatus.TOOL_CALL_COMPLETED))] }),
197
+ );
198
+ const workspace = workspaceOver(root, 1);
199
+ const capture = (await pinCaptureBaseline({ status, executionId: EXECUTION_ID, workspace, fileReview: FILE_REVIEW, artifactStorage: undefined }))!;
200
+
201
+ writeFileSync(join(root, "keep.md"), "kept and changed\n");
202
+ status.subAgentExecutions[0]!.messages.push(messageWith(row("sa-tc-new", "write_file", ToolCallStatus.TOOL_CALL_COMPLETED)));
203
+
204
+ await captureCandidate({ status, executionId: EXECUTION_ID, workspace, fileReview: FILE_REVIEW, artifactStorage: undefined, capture, globalBypass: false });
205
+
206
+ expect(status.messages[0]!.toolCalls[0]!.fileChangeSetId, "the prior turn's row is untouched").toBe(`${EXECUTION_ID}:0`);
207
+ const sub = status.subAgentExecutions[0]!.messages.flatMap((m) => m.toolCalls);
208
+ expect(sub.find((tc) => tc.id === "sa-tc-prior")!.fileChangeSetId, "the prior sub-agent row is untouched").toBe("");
209
+ expect(sub.find((tc) => tc.id === "sa-tc-new")!.fileChangeSetId, "this turn's sub-agent row takes the parent set").toBe(`${EXECUTION_ID}:1`);
210
+ });
211
+ });
212
+
213
+ describe("deriveCommandProvenance — one rule for both engines (F-M4-P3)", () => {
214
+ const seededShell = (status: ToolCallStatus, approvalAction: ApprovalAction) =>
215
+ row("tc-shell", "shell", status, { approvalAction, args: { command: "make generate" } });
216
+
217
+ it("qualifies the approved command that executed ON ITS SEEDED ROW — the shape a positional scope missed", () => {
218
+ // Turn 1 left the gate row WAITING at message 0; the reinvocation
219
+ // reconciled the re-attempt onto it: same id, same position, COMPLETED,
220
+ // the server's APPROVE preserved. It had NOT settled before this turn.
221
+ const status = create(AgentExecutionStatusSchema, {
222
+ messages: [
223
+ messageWith(seededShell(ToolCallStatus.TOOL_CALL_COMPLETED, ApprovalAction.APPROVE)),
224
+ create(AgentMessageSchema, { type: MessageType.MESSAGE_AI, content: "Generated." }),
225
+ ],
226
+ });
227
+ const provenance = deriveCommandProvenance(status, { priorSettledToolCallIds: new Set(), priorSubAgentToolCallIds: new Set() }, false);
228
+ expect(provenance?.consentToolCallIds).toEqual(["tc-shell"]);
229
+ expect(provenance?.authorizedByAutoApproveAll).toBe(false);
230
+ });
231
+
232
+ it("the gate turn itself does not qualify: the proposed command is WAITING, not executed", () => {
233
+ const status = create(AgentExecutionStatusSchema, {
234
+ messages: [messageWith(seededShell(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL, ApprovalAction.UNSPECIFIED))],
235
+ });
236
+ expect(deriveCommandProvenance(status, { priorSettledToolCallIds: new Set(), priorSubAgentToolCallIds: new Set() }, false)).toBeUndefined();
237
+ });
238
+
239
+ it("a command settled before this turn is prior history and cannot qualify or disqualify it", () => {
240
+ const status = create(AgentExecutionStatusSchema, {
241
+ messages: [
242
+ messageWith(seededShell(ToolCallStatus.TOOL_CALL_COMPLETED, ApprovalAction.APPROVE)),
243
+ messageWith(row("tc-read", "read_file", ToolCallStatus.TOOL_CALL_COMPLETED)),
244
+ ],
245
+ });
246
+ expect(
247
+ deriveCommandProvenance(status, { priorSettledToolCallIds: new Set(["tc-shell"]), priorSubAgentToolCallIds: new Set() }, false),
248
+ "no executed consented command THIS turn",
249
+ ).toBeUndefined();
250
+ });
251
+
252
+ it("any sub-agent activity this turn fails closed (DD-28 D1)", () => {
253
+ const status = create(AgentExecutionStatusSchema, {
254
+ messages: [messageWith(seededShell(ToolCallStatus.TOOL_CALL_COMPLETED, ApprovalAction.APPROVE))],
255
+ subAgentExecutions: [create(SubAgentExecutionSchema, { id: "sa", messages: [messageWith(row("sa-tc", "write_file", ToolCallStatus.TOOL_CALL_COMPLETED))] })],
256
+ });
257
+ expect(deriveCommandProvenance(status, { priorSettledToolCallIds: new Set(), priorSubAgentToolCallIds: new Set() }, false)).toBeUndefined();
258
+ });
259
+
260
+ it("under the global bypass an executed unconsented command is attributed to auto_approve_all", () => {
261
+ const status = create(AgentExecutionStatusSchema, {
262
+ messages: [messageWith(row("tc-shell", "shell", ToolCallStatus.TOOL_CALL_COMPLETED, { args: { command: "make" } }))],
263
+ });
264
+ const provenance = deriveCommandProvenance(status, { priorSettledToolCallIds: new Set(), priorSubAgentToolCallIds: new Set() }, true);
265
+ expect(provenance?.authorizedByAutoApproveAll).toBe(true);
266
+ expect(provenance?.consentToolCallIds).toEqual([]);
267
+ });
268
+ });