@stigmer/runner 3.15.3-dev.20260914121148 → 3.16.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 (592) hide show
  1. package/README.md +2 -1
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/__test-utils__/approval-contract/types.d.ts +1 -1
  4. package/dist/__test-utils__/hermetic-activity.d.ts +61 -7
  5. package/dist/__test-utils__/hermetic-activity.js +100 -9
  6. package/dist/__test-utils__/hermetic-activity.js.map +1 -1
  7. package/dist/__test-utils__/model-registry-fixture.d.ts +22 -3
  8. package/dist/__test-utils__/model-registry-fixture.js +22 -7
  9. package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
  10. package/dist/activities/execute-cursor/__test-utils__/contract-subject.d.ts +2 -2
  11. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +2 -2
  12. package/dist/activities/execute-cursor/__test-utils__/fold.d.ts +61 -0
  13. package/dist/activities/execute-cursor/__test-utils__/fold.js +94 -0
  14. package/dist/activities/execute-cursor/__test-utils__/fold.js.map +1 -0
  15. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +1 -1
  16. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -1
  17. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +47 -12
  18. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +41 -10
  19. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
  20. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +15 -6
  21. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +17 -4
  22. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -1
  23. package/dist/activities/execute-cursor/adapter.d.ts +4 -4
  24. package/dist/activities/execute-cursor/adapter.js +4 -4
  25. package/dist/activities/execute-cursor/approval-policy.d.ts +10 -10
  26. package/dist/activities/execute-cursor/approval-policy.js +12 -23
  27. package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
  28. package/dist/activities/execute-cursor/approval-state.d.ts +35 -1
  29. package/dist/activities/execute-cursor/approval-state.js +65 -0
  30. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  31. package/dist/activities/execute-cursor/{message-translator.d.ts → boundary-rows.d.ts} +45 -341
  32. package/dist/activities/execute-cursor/boundary-rows.js +1018 -0
  33. package/dist/activities/execute-cursor/boundary-rows.js.map +1 -0
  34. package/dist/activities/execute-cursor/cursor-capabilities.d.ts +1 -1
  35. package/dist/activities/execute-cursor/cursor-capabilities.js +1 -1
  36. package/dist/activities/execute-cursor/cursor-event-recorder.d.ts +33 -14
  37. package/dist/activities/execute-cursor/cursor-event-recorder.js +43 -21
  38. package/dist/activities/execute-cursor/cursor-event-recorder.js.map +1 -1
  39. package/dist/activities/execute-cursor/prompt-builder.d.ts +3 -3
  40. package/dist/activities/execute-cursor/prompt-builder.js +4 -4
  41. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  42. package/dist/activities/execute-cursor/service-tier.js +1 -2
  43. package/dist/activities/execute-cursor/service-tier.js.map +1 -1
  44. package/dist/activities/execute-cursor/sub-agent-steps.d.ts +79 -0
  45. package/dist/activities/execute-cursor/sub-agent-steps.js +180 -0
  46. package/dist/activities/execute-cursor/sub-agent-steps.js.map +1 -0
  47. package/dist/activities/execute-cursor/tool-result.d.ts +49 -0
  48. package/dist/activities/execute-cursor/tool-result.js +130 -0
  49. package/dist/activities/execute-cursor/tool-result.js.map +1 -0
  50. package/dist/activities/execute-cursor/translator.d.ts +178 -0
  51. package/dist/activities/execute-cursor/translator.js +485 -0
  52. package/dist/activities/execute-cursor/translator.js.map +1 -0
  53. package/dist/activities/execute-cursor/turn-boundary.d.ts +15 -5
  54. package/dist/activities/execute-cursor/turn-boundary.js +10 -14
  55. package/dist/activities/execute-cursor/turn-boundary.js.map +1 -1
  56. package/dist/activities/execute-cursor/turn-settle.d.ts +11 -7
  57. package/dist/activities/execute-cursor/turn-settle.js +47 -34
  58. package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
  59. package/dist/activities/execute-cursor/turn-setup.d.ts +2 -2
  60. package/dist/activities/execute-cursor/turn-setup.js +5 -5
  61. package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
  62. package/dist/activities/execute-cursor/turn-stream.d.ts +23 -14
  63. package/dist/activities/execute-cursor/turn-stream.js +41 -28
  64. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  65. package/dist/activities/execute-cursor/turn.d.ts +4 -4
  66. package/dist/activities/execute-cursor/turn.js +5 -5
  67. package/dist/activities/execute-cursor/usage-pricing.d.ts +2 -2
  68. package/dist/activities/execute-cursor/usage-pricing.js +2 -2
  69. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +2 -2
  70. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +2 -2
  71. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +16 -11
  72. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +22 -10
  73. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -1
  74. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +1 -1
  75. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -1
  76. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +2 -3
  77. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +2 -3
  78. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -1
  79. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +12 -3
  80. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +18 -4
  81. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
  82. package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.d.ts +10 -1
  83. package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js +24 -11
  84. package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js.map +1 -1
  85. package/dist/activities/execute-deep-agent/adapter.d.ts +1 -1
  86. package/dist/activities/execute-deep-agent/adapter.js +1 -1
  87. package/dist/activities/execute-deep-agent/approval-file-change.d.ts +19 -19
  88. package/dist/activities/execute-deep-agent/approval-file-change.js +16 -15
  89. package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -1
  90. package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +2 -2
  91. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +2 -2
  92. package/dist/activities/execute-deep-agent/hitl.d.ts +2 -2
  93. package/dist/activities/execute-deep-agent/hitl.js +2 -2
  94. package/dist/activities/execute-deep-agent/inline-publisher.d.ts +13 -3
  95. package/dist/activities/execute-deep-agent/inline-publisher.js +3 -3
  96. package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
  97. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +3 -3
  98. package/dist/activities/execute-deep-agent/prompt-builder.js +6 -7
  99. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  100. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +16 -8
  101. package/dist/activities/execute-deep-agent/streaming-side-effects.js +20 -33
  102. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  103. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +3 -3
  104. package/dist/activities/execute-deep-agent/subagent-transformer.js +1 -1
  105. package/dist/activities/execute-deep-agent/translator.d.ts +134 -0
  106. package/dist/activities/execute-deep-agent/translator.js +418 -0
  107. package/dist/activities/execute-deep-agent/translator.js.map +1 -0
  108. package/dist/activities/execute-deep-agent/turn-settle.d.ts +13 -7
  109. package/dist/activities/execute-deep-agent/turn-settle.js +34 -45
  110. package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -1
  111. package/dist/activities/execute-deep-agent/turn-setup.d.ts +8 -8
  112. package/dist/activities/execute-deep-agent/turn-setup.js +7 -7
  113. package/dist/activities/execute-deep-agent/turn-stream.d.ts +13 -10
  114. package/dist/activities/execute-deep-agent/turn-stream.js +44 -41
  115. package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -1
  116. package/dist/activities/execute-deep-agent/turn.d.ts +3 -3
  117. package/dist/activities/execute-deep-agent/turn.js +3 -3
  118. package/dist/activities/generate-session-subject.d.ts +25 -4
  119. package/dist/activities/generate-session-subject.js +17 -7
  120. package/dist/activities/generate-session-subject.js.map +1 -1
  121. package/dist/client/stigmer-client.d.ts +17 -6
  122. package/dist/client/stigmer-client.js +47 -14
  123. package/dist/client/stigmer-client.js.map +1 -1
  124. package/dist/config.d.ts +6 -3
  125. package/dist/config.js +4 -1
  126. package/dist/config.js.map +1 -1
  127. package/dist/harness/approval-decisions.d.ts +1 -1
  128. package/dist/harness/approval-decisions.js +1 -1
  129. package/dist/harness/capabilities.d.ts +21 -11
  130. package/dist/harness/capabilities.js +2 -2
  131. package/dist/harness/capture.d.ts +4 -4
  132. package/dist/harness/capture.js +4 -4
  133. package/dist/harness/persist-chokepoint.d.ts +3 -3
  134. package/dist/harness/persist-chokepoint.js +3 -3
  135. package/dist/harness/registry.d.ts +2 -2
  136. package/dist/harness/registry.js +2 -2
  137. package/dist/harness/run-turn.d.ts +6 -4
  138. package/dist/harness/run-turn.js +31 -9
  139. package/dist/harness/run-turn.js.map +1 -1
  140. package/dist/harness/stop-controller.d.ts +1 -1
  141. package/dist/harness/stop-controller.js +1 -1
  142. package/dist/harness/terminal-table.d.ts +7 -7
  143. package/dist/harness/terminal-table.js +7 -7
  144. package/dist/harness/transcript/builder.d.ts +182 -0
  145. package/dist/harness/transcript/builder.js +693 -0
  146. package/dist/harness/transcript/builder.js.map +1 -0
  147. package/dist/harness/transcript/events.d.ts +218 -0
  148. package/dist/harness/transcript/events.js +50 -0
  149. package/dist/harness/transcript/events.js.map +1 -0
  150. package/dist/harness/transcript/state.d.ts +110 -0
  151. package/dist/harness/transcript/state.js +148 -0
  152. package/dist/harness/transcript/state.js.map +1 -0
  153. package/dist/harness/turn-context.d.ts +54 -38
  154. package/dist/harness/turn-context.js +42 -43
  155. package/dist/harness/turn-context.js.map +1 -1
  156. package/dist/harness/types.d.ts +63 -22
  157. package/dist/harness/types.js +12 -11
  158. package/dist/harness/types.js.map +1 -1
  159. package/dist/harness/usage-accumulator.d.ts +2 -2
  160. package/dist/harness/usage-accumulator.js +2 -2
  161. package/dist/harness-adapters.d.ts +5 -4
  162. package/dist/harness-adapters.js +5 -4
  163. package/dist/harness-adapters.js.map +1 -1
  164. package/dist/interceptors/run-credential-activity.d.ts +28 -0
  165. package/dist/interceptors/run-credential-activity.js +56 -0
  166. package/dist/interceptors/run-credential-activity.js.map +1 -0
  167. package/dist/middleware/approval-gate.d.ts +6 -0
  168. package/dist/middleware/approval-gate.js +7 -57
  169. package/dist/middleware/approval-gate.js.map +1 -1
  170. package/dist/middleware/cost-advisory.d.ts +3 -3
  171. package/dist/middleware/cost-advisory.js +3 -3
  172. package/dist/middleware/index.d.ts +1 -1
  173. package/dist/middleware/index.js +1 -1
  174. package/dist/runner-manager.js +10 -4
  175. package/dist/runner-manager.js.map +1 -1
  176. package/dist/shared/activity-input.d.ts +9 -0
  177. package/dist/shared/activity-input.js +17 -0
  178. package/dist/shared/activity-input.js.map +1 -1
  179. package/dist/shared/approval-policy.d.ts +53 -0
  180. package/dist/shared/approval-policy.js +76 -0
  181. package/dist/shared/approval-policy.js.map +1 -1
  182. package/dist/shared/args-preview.d.ts +45 -22
  183. package/dist/shared/args-preview.js +51 -41
  184. package/dist/shared/args-preview.js.map +1 -1
  185. package/dist/shared/attachment-resolver.d.ts +4 -4
  186. package/dist/shared/attachment-resolver.js +4 -4
  187. package/dist/shared/attachment-zip.d.ts +2 -2
  188. package/dist/shared/attachment-zip.js +2 -2
  189. package/dist/shared/channel-attachment.d.ts +8 -5
  190. package/dist/shared/channel-attachment.js +10 -6
  191. package/dist/shared/channel-attachment.js.map +1 -1
  192. package/dist/shared/cost-guard.d.ts +2 -2
  193. package/dist/shared/cost-guard.js +2 -2
  194. package/dist/shared/exact-apply.d.ts +1 -1
  195. package/dist/shared/exact-apply.js +1 -1
  196. package/dist/shared/execution-context.d.ts +2 -2
  197. package/dist/shared/execution-context.js +2 -2
  198. package/dist/shared/extract-structured-output.d.ts +2 -2
  199. package/dist/shared/extract-structured-output.js +2 -2
  200. package/dist/shared/filereview/cas-touched.d.ts +2 -2
  201. package/dist/shared/filereview/cas-touched.js +2 -2
  202. package/dist/shared/filereview/progress.d.ts +13 -0
  203. package/dist/shared/filereview/progress.js +13 -0
  204. package/dist/shared/filereview/progress.js.map +1 -1
  205. package/dist/shared/heartbeat.d.ts +1 -1
  206. package/dist/shared/heartbeat.js +1 -1
  207. package/dist/shared/llm-backend.d.ts +0 -4
  208. package/dist/shared/llm-backend.js +0 -4
  209. package/dist/shared/llm-backend.js.map +1 -1
  210. package/dist/shared/memory-attachment.d.ts +16 -9
  211. package/dist/shared/memory-attachment.js +18 -10
  212. package/dist/shared/memory-attachment.js.map +1 -1
  213. package/dist/shared/model-error.d.ts +1 -2
  214. package/dist/shared/model-error.js +1 -2
  215. package/dist/shared/model-error.js.map +1 -1
  216. package/dist/shared/persist-decision.d.ts +18 -24
  217. package/dist/shared/persist-decision.js +19 -11
  218. package/dist/shared/persist-decision.js.map +1 -1
  219. package/dist/shared/prompt-sections.d.ts +4 -4
  220. package/dist/shared/prompt-sections.js +4 -4
  221. package/dist/shared/run-credential-store.d.ts +32 -0
  222. package/dist/shared/run-credential-store.js +39 -0
  223. package/dist/shared/run-credential-store.js.map +1 -0
  224. package/dist/shared/run-credential.d.ts +61 -0
  225. package/dist/shared/run-credential.js +68 -0
  226. package/dist/shared/run-credential.js.map +1 -0
  227. package/dist/shared/skill-resolver.d.ts +3 -3
  228. package/dist/shared/skill-resolver.js +3 -3
  229. package/dist/shared/status-offload.d.ts +1 -1
  230. package/dist/shared/status-offload.js +2 -2
  231. package/dist/shared/status-offload.js.map +1 -1
  232. package/dist/shared/streaming-scheduler.d.ts +2 -7
  233. package/dist/shared/streaming-scheduler.js +6 -14
  234. package/dist/shared/streaming-scheduler.js.map +1 -1
  235. package/dist/shared/subagent-rows.d.ts +2 -2
  236. package/dist/shared/subagent-rows.js +2 -2
  237. package/dist/shared/synthesized-attachment.d.ts +30 -3
  238. package/dist/shared/synthesized-attachment.js +26 -0
  239. package/dist/shared/synthesized-attachment.js.map +1 -1
  240. package/dist/shared/todos.d.ts +3 -3
  241. package/dist/shared/todos.js +3 -3
  242. package/dist/shared/tool-row.d.ts +19 -1
  243. package/dist/shared/tool-row.js +36 -2
  244. package/dist/shared/tool-row.js.map +1 -1
  245. package/dist/shared/worker-shutdown.d.ts +1 -1
  246. package/dist/shared/worker-shutdown.js +1 -1
  247. package/dist/shared/workspace/session-provision.d.ts +1 -1
  248. package/dist/shared/workspace/session-provision.js +1 -1
  249. package/dist/shared/workspace/sources/empty.d.ts +1 -1
  250. package/dist/shared/workspace/sources/empty.js +1 -2
  251. package/dist/shared/workspace/sources/empty.js.map +1 -1
  252. package/dist/shared/workspace/sources/git.js +4 -4
  253. package/dist/shared/workspace/sources/git.js.map +1 -1
  254. package/dist/shared/workspace/sources/local-path.js +6 -6
  255. package/dist/shared/workspace/sources/local-path.js.map +1 -1
  256. package/dist/shared/workspace/stigmer-link.d.ts +1 -1
  257. package/dist/shared/workspace/stigmer-link.js +1 -1
  258. package/dist/shared/workspace/types.d.ts +2 -5
  259. package/dist/shared/workspace/types.js +0 -6
  260. package/dist/shared/workspace/types.js.map +1 -1
  261. package/dist/shared/workspace/writeback-coordinator.d.ts +17 -4
  262. package/dist/shared/workspace/writeback-coordinator.js +7 -9
  263. package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
  264. package/dist/worker.js +10 -3
  265. package/dist/worker.js.map +1 -1
  266. package/dist/workflow-source.d.ts +10 -0
  267. package/dist/workflow-source.js +12 -0
  268. package/dist/workflow-source.js.map +1 -1
  269. package/dist/workflows/execute-from-execution.d.ts +15 -0
  270. package/dist/workflows/execute-from-execution.js +11 -0
  271. package/dist/workflows/execute-from-execution.js.map +1 -1
  272. package/dist/workflows/index.d.ts +5 -3
  273. package/dist/workflows/index.js +5 -3
  274. package/dist/workflows/index.js.map +1 -1
  275. package/dist/workflows/interceptors/run-credential.d.ts +34 -0
  276. package/dist/workflows/interceptors/run-credential.js +71 -0
  277. package/dist/workflows/interceptors/run-credential.js.map +1 -0
  278. package/package.json +4 -4
  279. package/src/__test-utils__/__tests__/execution-record-barrier.test.ts +70 -0
  280. package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +35 -1
  281. package/src/__test-utils__/approval-contract/contract.ts +1 -1
  282. package/src/__test-utils__/approval-contract/types.ts +1 -1
  283. package/src/__test-utils__/harness-boot-order-child.ts +1 -1
  284. package/src/__test-utils__/harness-contract/contract.ts +8 -8
  285. package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
  286. package/src/__test-utils__/harness-contract/runtime-contract.ts +52 -16
  287. package/src/__test-utils__/harness-contract/scripted-adapter.ts +47 -43
  288. package/src/__test-utils__/hermetic-activity.ts +113 -9
  289. package/src/__test-utils__/model-registry-fixture.ts +31 -7
  290. package/src/__test-utils__/module-specifiers.ts +32 -3
  291. package/src/__tests__/harness-boot-order.test.ts +1 -1
  292. package/src/__tests__/harness-contract.test.ts +1 -1
  293. package/src/__tests__/run-credential-e2e.test.ts +233 -0
  294. package/src/__tests__/run-credential-registration.test.ts +80 -0
  295. package/src/__tests__/runner-manager.test.ts +1 -1
  296. package/src/activities/__tests__/generate-session-subject-wire.test.ts +119 -0
  297. package/src/activities/execute-cursor/__test-utils__/__tests__/scripted-double.test.ts +1 -1
  298. package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +2 -2
  299. package/src/activities/execute-cursor/__test-utils__/fold.ts +116 -0
  300. package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +1 -1
  301. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +67 -17
  302. package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +19 -5
  303. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +2 -1
  304. package/src/activities/execute-cursor/__tests__/approval-state.test.ts +14 -21
  305. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +3 -3
  306. package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +2 -1
  307. package/src/activities/execute-cursor/__tests__/cursor-event-recorder.test.ts +142 -0
  308. package/src/activities/execute-cursor/__tests__/cursor-fetch-interceptor-bypass.test.ts +19 -8
  309. package/src/activities/execute-cursor/__tests__/cursor-sdk-auth-smoke.test.ts +10 -8
  310. package/src/activities/execute-cursor/__tests__/cursor-transcript-live.test.ts +298 -0
  311. package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +4 -2
  312. package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +1 -1
  313. package/src/activities/execute-cursor/__tests__/hermetic/deny-and-retry.test.ts +1 -1
  314. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -1
  315. package/src/activities/execute-cursor/__tests__/hermetic/goldens/mcp-tool-call.status.json +71 -0
  316. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-task-note.status.json +73 -0
  317. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.error-delta.status.json +78 -0
  318. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.output-first.status.json +72 -0
  319. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.status.json +72 -0
  320. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sub-agent-delegation.status.json +109 -0
  321. package/src/activities/execute-cursor/__tests__/hermetic/goldens/thinking-turn.status.json +78 -0
  322. package/src/activities/execute-cursor/__tests__/hermetic/goldens/todo-write.status.json +119 -0
  323. package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-error.status.json +68 -0
  324. package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-only-step.status.json +67 -0
  325. package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +1 -1
  326. package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +7 -7
  327. package/src/activities/execute-cursor/__tests__/hermetic/mcp-tool-call.test.ts +146 -0
  328. package/src/activities/execute-cursor/__tests__/hermetic/pause-vs-shutdown.test.ts +5 -5
  329. package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +2 -1
  330. package/src/activities/execute-cursor/__tests__/hermetic/run-wait-arms.test.ts +2 -1
  331. package/src/activities/execute-cursor/__tests__/hermetic/sdk-task-note.test.ts +122 -0
  332. package/src/activities/execute-cursor/__tests__/hermetic/shell-output-delta.test.ts +276 -0
  333. package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +8 -8
  334. package/src/activities/execute-cursor/__tests__/hermetic/sub-agent-delegation.test.ts +196 -0
  335. package/src/activities/execute-cursor/__tests__/hermetic/thinking-turn.test.ts +145 -0
  336. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +1 -1
  337. package/src/activities/execute-cursor/__tests__/hermetic/todo-write.test.ts +165 -0
  338. package/src/activities/execute-cursor/__tests__/hermetic/tool-call.test.ts +3 -3
  339. package/src/activities/execute-cursor/__tests__/hermetic/tool-error.test.ts +138 -0
  340. package/src/activities/execute-cursor/__tests__/hermetic/tool-only-step.test.ts +130 -0
  341. package/src/activities/execute-cursor/__tests__/hermetic/unattributed-hook-block.test.ts +13 -1
  342. package/src/activities/execute-cursor/__tests__/hermetic/workspace-lock-timeout.test.ts +2 -2
  343. package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +43 -60
  344. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +23 -28
  345. package/src/activities/execute-cursor/__tests__/hook-script.test.ts +2 -1
  346. package/src/activities/execute-cursor/__tests__/http2-interceptor.test.ts +1 -1
  347. package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +5 -5
  348. package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +11 -13
  349. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +8 -11
  350. package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +8 -22
  351. package/src/activities/execute-cursor/__tests__/translator.test.ts +594 -0
  352. package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +8 -5
  353. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +87 -63
  354. package/src/activities/execute-cursor/adapter.ts +4 -4
  355. package/src/activities/execute-cursor/approval-policy.ts +12 -24
  356. package/src/activities/execute-cursor/approval-state.ts +66 -1
  357. package/src/activities/execute-cursor/boundary-rows.ts +1132 -0
  358. package/src/activities/execute-cursor/cursor-capabilities.ts +1 -1
  359. package/src/activities/execute-cursor/cursor-event-recorder.ts +57 -28
  360. package/src/activities/execute-cursor/fetch-interceptor.ts +1 -1
  361. package/src/activities/execute-cursor/http2-interceptor.ts +1 -1
  362. package/src/activities/execute-cursor/prompt-builder.ts +6 -6
  363. package/src/activities/execute-cursor/service-tier.ts +1 -2
  364. package/src/activities/execute-cursor/sub-agent-steps.ts +188 -0
  365. package/src/activities/execute-cursor/tool-result.ts +128 -0
  366. package/src/activities/execute-cursor/translator.ts +541 -0
  367. package/src/activities/execute-cursor/turn-boundary.ts +21 -14
  368. package/src/activities/execute-cursor/turn-settle.ts +46 -34
  369. package/src/activities/execute-cursor/turn-setup.ts +5 -5
  370. package/src/activities/execute-cursor/turn-stream.ts +49 -46
  371. package/src/activities/execute-cursor/turn.ts +5 -5
  372. package/src/activities/execute-cursor/usage-pricing.ts +2 -2
  373. package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +2 -2
  374. package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +23 -11
  375. package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +1 -1
  376. package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +2 -3
  377. package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +25 -4
  378. package/src/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.ts +25 -11
  379. package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +1 -1
  380. package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +1 -1
  381. package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +6 -17
  382. package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +25 -8
  383. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +2 -7
  384. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +4 -14
  385. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +1 -5
  386. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +4 -7
  387. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +1 -5
  388. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +2 -9
  389. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +2 -9
  390. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +1 -4
  391. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +1 -4
  392. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +1 -4
  393. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +109 -76
  394. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +1 -5
  395. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +2 -10
  396. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +2 -10
  397. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +1 -5
  398. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +3 -6
  399. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +1 -4
  400. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +1 -4
  401. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/write-todos.status.json +92 -0
  402. package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +3 -3
  403. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +9 -5
  404. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +3 -3
  405. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +3 -3
  406. package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +7 -6
  407. package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +10 -11
  408. package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +4 -4
  409. package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +9 -8
  410. package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +21 -13
  411. package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +2 -2
  412. package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +7 -7
  413. package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +28 -12
  414. package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +21 -21
  415. package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +5 -5
  416. package/src/activities/execute-deep-agent/__tests__/hermetic/write-todos.test.ts +160 -0
  417. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +35 -30
  418. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +3 -4
  419. package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +12 -10
  420. package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +13 -3
  421. package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +26 -18
  422. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +1 -1
  423. package/src/activities/execute-deep-agent/__tests__/{subagent-tracker.test.ts → translator-subagents.test.ts} +76 -57
  424. package/src/activities/execute-deep-agent/__tests__/{v3-status-builder.test.ts → translator-through-builder.test.ts} +213 -299
  425. package/src/activities/execute-deep-agent/__tests__/translator.test.ts +505 -0
  426. package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +11 -29
  427. package/src/activities/execute-deep-agent/adapter.ts +1 -1
  428. package/src/activities/execute-deep-agent/approval-file-change.ts +20 -22
  429. package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +2 -2
  430. package/src/activities/execute-deep-agent/hitl.ts +2 -2
  431. package/src/activities/execute-deep-agent/inline-publisher.ts +16 -5
  432. package/src/activities/execute-deep-agent/prompt-builder.ts +6 -7
  433. package/src/activities/execute-deep-agent/streaming-side-effects.ts +22 -31
  434. package/src/activities/execute-deep-agent/subagent-transformer.ts +3 -3
  435. package/src/activities/execute-deep-agent/translator.ts +463 -0
  436. package/src/activities/execute-deep-agent/turn-settle.ts +35 -43
  437. package/src/activities/execute-deep-agent/turn-setup.ts +8 -8
  438. package/src/activities/execute-deep-agent/turn-stream.ts +53 -51
  439. package/src/activities/execute-deep-agent/turn.ts +3 -3
  440. package/src/activities/generate-session-subject.ts +29 -4
  441. package/src/client/__tests__/stigmer-client.test.ts +92 -0
  442. package/src/client/stigmer-client.ts +48 -14
  443. package/src/config.ts +6 -3
  444. package/src/harness/README.md +335 -0
  445. package/src/harness/__tests__/approval-decisions.test.ts +2 -2
  446. package/src/harness/__tests__/capture.test.ts +3 -3
  447. package/src/harness/__tests__/import-direction.test.ts +3 -2
  448. package/src/harness/__tests__/mount-skills.test.ts +4 -1
  449. package/src/harness/__tests__/readme-index.test.ts +339 -0
  450. package/src/harness/__tests__/run-turn.test.ts +131 -6
  451. package/src/harness/__tests__/transcript-is-engine-free.test.ts +83 -0
  452. package/src/harness/__tests__/transcript-writers.test.ts +191 -0
  453. package/src/harness/__tests__/turn-attachments-vision.test.ts +5 -2
  454. package/src/harness/__tests__/turn-context.test.ts +5 -5
  455. package/src/harness/approval-decisions.ts +1 -1
  456. package/src/harness/capabilities.ts +21 -11
  457. package/src/harness/capture.ts +4 -4
  458. package/src/harness/persist-chokepoint.ts +3 -3
  459. package/src/harness/registry.ts +2 -2
  460. package/src/harness/run-turn.ts +33 -9
  461. package/src/harness/stop-controller.ts +1 -1
  462. package/src/harness/terminal-table.ts +7 -7
  463. package/src/harness/transcript/__tests__/builder.test.ts +975 -0
  464. package/src/harness/transcript/__tests__/state.test.ts +143 -0
  465. package/src/harness/transcript/builder.ts +755 -0
  466. package/src/harness/transcript/events.ts +262 -0
  467. package/src/harness/transcript/state.ts +167 -0
  468. package/src/harness/turn-context.ts +61 -46
  469. package/src/harness/types.ts +64 -22
  470. package/src/harness/usage-accumulator.ts +2 -2
  471. package/src/harness-adapters.ts +5 -4
  472. package/src/interceptors/__tests__/run-credential-activity.test.ts +133 -0
  473. package/src/interceptors/run-credential-activity.ts +67 -0
  474. package/src/middleware/__tests__/cost-advisory.test.ts +2 -2
  475. package/src/middleware/approval-gate.ts +7 -74
  476. package/src/middleware/cost-advisory.ts +3 -3
  477. package/src/middleware/index.ts +1 -1
  478. package/src/runner-manager.ts +14 -4
  479. package/src/shared/__tests__/approval-policy.test.ts +45 -0
  480. package/src/shared/__tests__/args-preview.test.ts +40 -20
  481. package/src/shared/__tests__/attachment-naming.test.ts +1 -1
  482. package/src/shared/__tests__/attachment-resolver.test.ts +5 -5
  483. package/src/shared/__tests__/attachment-zip.test.ts +1 -1
  484. package/src/shared/__tests__/blueprint-resolver.test.ts +1 -1
  485. package/src/shared/__tests__/channel-attachment.test.ts +12 -0
  486. package/src/shared/__tests__/cost-guard.test.ts +1 -1
  487. package/src/shared/__tests__/exact-apply.test.ts +2 -1
  488. package/src/shared/__tests__/memory-attachment.test.ts +17 -0
  489. package/src/shared/__tests__/persist-decision.test.ts +19 -33
  490. package/src/shared/__tests__/prompt-sections.test.ts +1 -1
  491. package/src/shared/__tests__/run-credential.test.ts +111 -0
  492. package/src/shared/__tests__/stamp-flowed-rows.test.ts +1 -1
  493. package/src/shared/__tests__/status-offload.test.ts +1 -1
  494. package/src/shared/__tests__/streaming-scheduler.test.ts +8 -9
  495. package/src/shared/__tests__/todos.test.ts +1 -1
  496. package/src/shared/__tests__/tool-row.test.ts +1 -1
  497. package/src/shared/activity-input.ts +21 -0
  498. package/src/shared/approval-policy.ts +95 -0
  499. package/src/shared/args-preview.ts +51 -42
  500. package/src/shared/attachment-resolver.ts +4 -4
  501. package/src/shared/attachment-zip.ts +2 -2
  502. package/src/shared/channel-attachment.ts +14 -6
  503. package/src/shared/cost-guard.ts +2 -2
  504. package/src/shared/exact-apply.ts +1 -1
  505. package/src/shared/execution-context.ts +2 -2
  506. package/src/shared/extract-structured-output.ts +2 -2
  507. package/src/shared/filereview/__tests__/capture.test.ts +9 -5
  508. package/src/shared/filereview/__tests__/git-substrate.test.ts +4 -1
  509. package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +3 -3
  510. package/src/shared/filereview/cas-touched.ts +2 -2
  511. package/src/shared/filereview/progress.ts +13 -0
  512. package/src/shared/heartbeat.ts +1 -1
  513. package/src/shared/llm-backend.ts +0 -4
  514. package/src/shared/memory-attachment.ts +22 -10
  515. package/src/shared/model-error.ts +1 -2
  516. package/src/shared/persist-decision.ts +19 -28
  517. package/src/shared/prompt-sections.ts +4 -4
  518. package/src/shared/run-credential-store.ts +45 -0
  519. package/src/shared/run-credential.ts +70 -0
  520. package/src/shared/skill-resolver.ts +3 -3
  521. package/src/shared/status-offload.ts +2 -2
  522. package/src/shared/streaming-scheduler.ts +8 -13
  523. package/src/shared/subagent-rows.ts +2 -2
  524. package/src/shared/synthesized-attachment.ts +38 -3
  525. package/src/shared/todos.ts +3 -3
  526. package/src/shared/tool-row.ts +38 -2
  527. package/src/shared/worker-shutdown.ts +1 -1
  528. package/src/shared/workspace/__tests__/git-source.test.ts +3 -3
  529. package/src/shared/workspace/__tests__/provisioner.test.ts +5 -5
  530. package/src/shared/workspace/__tests__/session-provision.test.ts +2 -2
  531. package/src/shared/workspace/__tests__/writeback-coordinator.integration.test.ts +8 -9
  532. package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +25 -25
  533. package/src/shared/workspace/provisioner.ts +1 -1
  534. package/src/shared/workspace/session-provision.ts +1 -1
  535. package/src/shared/workspace/sources/empty.ts +2 -2
  536. package/src/shared/workspace/sources/git.ts +3 -4
  537. package/src/shared/workspace/sources/local-path.ts +5 -6
  538. package/src/shared/workspace/stigmer-link.ts +1 -1
  539. package/src/shared/workspace/types.ts +2 -5
  540. package/src/shared/workspace/writeback-coordinator.ts +23 -12
  541. package/src/worker.ts +15 -3
  542. package/src/workflow-source.ts +15 -0
  543. package/src/workflows/__tests__/run-credential-interceptor.test.ts +178 -0
  544. package/src/workflows/execute-from-execution.ts +17 -0
  545. package/src/workflows/index.ts +5 -3
  546. package/src/workflows/interceptors/run-credential.ts +101 -0
  547. package/dist/activities/execute-cursor/delta-enricher.d.ts +0 -87
  548. package/dist/activities/execute-cursor/delta-enricher.js +0 -265
  549. package/dist/activities/execute-cursor/delta-enricher.js.map +0 -1
  550. package/dist/activities/execute-cursor/message-translator.js +0 -2040
  551. package/dist/activities/execute-cursor/message-translator.js.map +0 -1
  552. package/dist/activities/execute-cursor/todo-tracker.d.ts +0 -40
  553. package/dist/activities/execute-cursor/todo-tracker.js +0 -80
  554. package/dist/activities/execute-cursor/todo-tracker.js.map +0 -1
  555. package/dist/activities/execute-deep-agent/execution-state.d.ts +0 -54
  556. package/dist/activities/execute-deep-agent/execution-state.js +0 -68
  557. package/dist/activities/execute-deep-agent/execution-state.js.map +0 -1
  558. package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +0 -65
  559. package/dist/activities/execute-deep-agent/status-builder-shared.js +0 -90
  560. package/dist/activities/execute-deep-agent/status-builder-shared.js.map +0 -1
  561. package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +0 -113
  562. package/dist/activities/execute-deep-agent/subagent-tracker.js +0 -400
  563. package/dist/activities/execute-deep-agent/subagent-tracker.js.map +0 -1
  564. package/dist/activities/execute-deep-agent/v3-events.d.ts +0 -102
  565. package/dist/activities/execute-deep-agent/v3-events.js +0 -35
  566. package/dist/activities/execute-deep-agent/v3-events.js.map +0 -1
  567. package/dist/activities/execute-deep-agent/v3-protocol-normalizer.d.ts +0 -15
  568. package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js +0 -235
  569. package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js.map +0 -1
  570. package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +0 -111
  571. package/dist/activities/execute-deep-agent/v3-status-builder.js +0 -457
  572. package/dist/activities/execute-deep-agent/v3-status-builder.js.map +0 -1
  573. package/dist/shared/execution-status-writer.d.ts +0 -31
  574. package/dist/shared/execution-status-writer.js +0 -43
  575. package/dist/shared/execution-status-writer.js.map +0 -1
  576. package/src/activities/execute-cursor/__tests__/delta-enricher.test.ts +0 -310
  577. package/src/activities/execute-cursor/__tests__/message-translator.test.ts +0 -1033
  578. package/src/activities/execute-cursor/__tests__/todo-tracker.test.ts +0 -498
  579. package/src/activities/execute-cursor/delta-enricher.ts +0 -307
  580. package/src/activities/execute-cursor/message-translator.ts +0 -2319
  581. package/src/activities/execute-cursor/todo-tracker.ts +0 -91
  582. package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -148
  583. package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -154
  584. package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +0 -47
  585. package/src/activities/execute-deep-agent/__tests__/v3-protocol-normalizer.test.ts +0 -324
  586. package/src/activities/execute-deep-agent/execution-state.ts +0 -78
  587. package/src/activities/execute-deep-agent/status-builder-shared.ts +0 -142
  588. package/src/activities/execute-deep-agent/subagent-tracker.ts +0 -479
  589. package/src/activities/execute-deep-agent/v3-events.ts +0 -153
  590. package/src/activities/execute-deep-agent/v3-protocol-normalizer.ts +0 -264
  591. package/src/activities/execute-deep-agent/v3-status-builder.ts +0 -565
  592. package/src/shared/execution-status-writer.ts +0 -56
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Hermetic golden: an UNGATED tool call that FAILS — a `read` the SDK streams
3
+ * as `running` and then `error` with the failure text as its result — through
4
+ * the whole `ExecuteCursor` activity.
5
+ *
6
+ * Invariant pinned: the error event folds onto the same row
7
+ * (the builder's `tool_error` upsert) as FAILED, with the failure text
8
+ * in `error` and `result` EMPTY, `completedAt` stamped, no approval fields (a
9
+ * read-only built-in is not gated, and the text is not Cursor's hook-block
10
+ * copy, so the boundary's #205 pass leaves it alone), and the turn still ends
11
+ * COMPLETED: a failed read is the model's problem to route around, not the
12
+ * turn's.
13
+ *
14
+ * Moved 2026-09-14 (#1097), one line: until then the accumulator
15
+ * wrote the failure text to BOTH `error` and `result`. Native writes `error`
16
+ * alone, as do this harness's own sub-agent rows, and every reader shows
17
+ * `error || result`; the duplicate's one behavioral reader was the twin
18
+ * collapse's `carriesOwnChange`, which took a hook-denied shell for a row
19
+ * carrying its own output. `startedAt` was already
20
+ * present because the `running` event came first.
21
+ *
22
+ * Why this net exists: the one FAILED row among the seventeen Cursor goldens
23
+ * is the gate's own case (`unattributed-hook-block`); the ordinary tool
24
+ * failure — the fold #1097 rewrote — had no end-to-end net.
25
+ *
26
+ * Regenerate ONLY after a deliberate behavior change:
27
+ * npx vitest run src/activities/execute-cursor/__tests__/hermetic -u
28
+ */
29
+
30
+ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
31
+ import { toJson } from "@bufbuild/protobuf";
32
+ import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
33
+ import { ExecutionPhase, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
34
+
35
+ vi.mock("@cursor/sdk", async () =>
36
+ (await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
37
+ );
38
+ vi.mock("../../../../client/stigmer-client.js", async () =>
39
+ (await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
40
+ );
41
+
42
+ import {
43
+ ScriptedClock,
44
+ createHermeticEnvironment,
45
+ type HermeticEnvironment,
46
+ } from "../../../../__test-utils__/hermetic-activity.js";
47
+ import { ScriptedCursorAgent, sdkEvents, step } from "../../__test-utils__/scripted-agent.js";
48
+ import {
49
+ FIXTURE,
50
+ SDK_CATALOG,
51
+ beginCursorScenario,
52
+ cursorExecutionRecord,
53
+ runCursorTurn,
54
+ } from "../../__test-utils__/hermetic-cursor.js";
55
+ import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
56
+
57
+ const AGENT_ID = "agent-hermetic-toolerror-0001";
58
+ const RUN_ID = "run-hermetic-toolerror-0001";
59
+ const CALL_ID = "call-hermetic-toolerror-read-0001";
60
+ const USER_MESSAGE = "What is in MISSING.md?";
61
+ const TEXT_BEFORE = "Let me read it.";
62
+ const READ_ARGS = { path: "MISSING.md" };
63
+ const READ_ERROR = "ENOENT: no such file or directory, open 'MISSING.md'";
64
+ const TEXT_AFTER = "There is no MISSING.md in this workspace.";
65
+
66
+ describe("ExecuteCursor hermetic — an ungated tool call that fails", () => {
67
+ let env: HermeticEnvironment;
68
+ let registry: ReturnType<typeof stubRegistryFetch>;
69
+ const clock = new ScriptedClock();
70
+
71
+ beforeAll(() => {
72
+ env = createHermeticEnvironment();
73
+ registry = stubRegistryFetch();
74
+ clock.install();
75
+ });
76
+
77
+ afterAll(() => {
78
+ clock.uninstall();
79
+ registry.restore();
80
+ env.dispose();
81
+ });
82
+
83
+ it("folds running -> error into one FAILED row carrying the failure text and completes", async () => {
84
+ // ── Arrange ──────────────────────────────────────────────────────────────
85
+ const ev = sdkEvents(AGENT_ID, RUN_ID);
86
+ const agent = new ScriptedCursorAgent({
87
+ agentId: AGENT_ID,
88
+ runIds: [RUN_ID],
89
+ observeStep: () => clock.tick(),
90
+ turns: [
91
+ [
92
+ step.event(ev.init()),
93
+ step.event(ev.assistant(TEXT_BEFORE)),
94
+ step.event(ev.toolCall(CALL_ID, "read", "running", READ_ARGS)),
95
+ step.event(ev.toolCall(CALL_ID, "read", "error", READ_ARGS, READ_ERROR)),
96
+ step.event(ev.assistant(TEXT_AFTER)),
97
+ step.turnEnded({ inputTokens: 1_900, outputTokens: 70, cacheReadTokens: 0, cacheWriteTokens: 0 }),
98
+ step.finished({ result: TEXT_AFTER, model: { id: FIXTURE.model, params: [] } }),
99
+ ],
100
+ ],
101
+ });
102
+ const record = cursorExecutionRecord({ message: USER_MESSAGE });
103
+ const scenario = beginCursorScenario({
104
+ env,
105
+ clock,
106
+ record,
107
+ sdk: { agents: [agent], catalog: SDK_CATALOG },
108
+ });
109
+
110
+ // ── Act ──────────────────────────────────────────────────────────────────
111
+ const invocation = await runCursorTurn(scenario);
112
+
113
+ // ── Assert: a failed read does not fail the turn ─────────────────────────
114
+ expect(invocation.outcome.kind).toBe("returned");
115
+ expect((invocation.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
116
+ expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
117
+
118
+ // ── Assert: the one FAILED row ───────────────────────────────────────────
119
+ const rows = record.toolCalls();
120
+ expect(rows, "running + error fold into ONE row").toHaveLength(1);
121
+ const row = rows[0];
122
+ expect(row.id).toBe(CALL_ID);
123
+ expect(row.status).toBe(ToolCallStatus.TOOL_CALL_FAILED);
124
+ expect(row.error, "the failure text is the error").toBe(READ_ERROR);
125
+ expect(row.result, "and only the error").toBe("");
126
+ expect(row.requiresApproval, "an ordinary failure is not an approval gate").toBe(false);
127
+ expect(row.approvalRequestedAt).toBe("");
128
+ expect(row.startedAt < row.completedAt).toBe(true);
129
+
130
+ // ── Assert: hermeticity ──────────────────────────────────────────────────
131
+ expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
132
+
133
+ // ── Assert: the golden ───────────────────────────────────────────────────
134
+ const final = record.lastFullStatus!;
135
+ const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
136
+ await expect(json).toMatchFileSnapshot("./goldens/tool-error.status.json");
137
+ });
138
+ });
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Hermetic golden: a turn whose FIRST event after `init` is a tool call — no
3
+ * assistant text proposed it — through the whole `ExecuteCursor` activity.
4
+ *
5
+ * Invariant pinned: a tool call with no AI message before it in
6
+ * its scope gets an EMPTY AI message created to carry its row
7
+ * (the builder's AI-message boundary), and the assistant text
8
+ * that follows the tool opens a new message. So: AI(`""` + the read row),
9
+ * AI(A1). This is the same shape the native builder produces for a model turn
10
+ * that proposes tools without text (`ensureAiMessageForToolCall`), which is
11
+ * why the canonical builder can serve both.
12
+ *
13
+ * Predicted for the swap to the shared builder (2026-09-14): NO move.
14
+ * The boundary rule ("the scope's current AI message, lazily created empty when
15
+ * the scope's latest model run had no text") produces exactly this transcript.
16
+ *
17
+ * Why this net exists: `tool-call.status.json` pins text→tool→text; nothing
18
+ * pinned a tool with no proposing text before this net.
19
+ *
20
+ * Regenerate ONLY after a deliberate behavior change:
21
+ * npx vitest run src/activities/execute-cursor/__tests__/hermetic -u
22
+ */
23
+
24
+ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
25
+ import { toJson } from "@bufbuild/protobuf";
26
+ import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
27
+ import { ExecutionPhase, MessageType, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
28
+
29
+ vi.mock("@cursor/sdk", async () =>
30
+ (await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
31
+ );
32
+ vi.mock("../../../../client/stigmer-client.js", async () =>
33
+ (await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
34
+ );
35
+
36
+ import {
37
+ ScriptedClock,
38
+ createHermeticEnvironment,
39
+ type HermeticEnvironment,
40
+ } from "../../../../__test-utils__/hermetic-activity.js";
41
+ import { ScriptedCursorAgent, sdkEvents, step } from "../../__test-utils__/scripted-agent.js";
42
+ import {
43
+ FIXTURE,
44
+ SDK_CATALOG,
45
+ beginCursorScenario,
46
+ cursorExecutionRecord,
47
+ runCursorTurn,
48
+ } from "../../__test-utils__/hermetic-cursor.js";
49
+ import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
50
+
51
+ const AGENT_ID = "agent-hermetic-toolonly-0001";
52
+ const RUN_ID = "run-hermetic-toolonly-0001";
53
+ const CALL_ID = "call-hermetic-toolonly-read-0001";
54
+ const USER_MESSAGE = "What is in README.md?";
55
+ const READ_ARGS = { path: "README.md" };
56
+ const READ_RESULT = "# Hermetic\n\nA fixture readme.\n";
57
+ const ASSISTANT_TEXT = "README.md holds a one-line fixture description.";
58
+
59
+ describe("ExecuteCursor hermetic — a tool call with no proposing text", () => {
60
+ let env: HermeticEnvironment;
61
+ let registry: ReturnType<typeof stubRegistryFetch>;
62
+ const clock = new ScriptedClock();
63
+
64
+ beforeAll(() => {
65
+ env = createHermeticEnvironment();
66
+ registry = stubRegistryFetch();
67
+ clock.install();
68
+ });
69
+
70
+ afterAll(() => {
71
+ clock.uninstall();
72
+ registry.restore();
73
+ env.dispose();
74
+ });
75
+
76
+ it("creates an empty AI message for the row and a new one for the text that follows", async () => {
77
+ // ── Arrange ──────────────────────────────────────────────────────────────
78
+ const ev = sdkEvents(AGENT_ID, RUN_ID);
79
+ const agent = new ScriptedCursorAgent({
80
+ agentId: AGENT_ID,
81
+ runIds: [RUN_ID],
82
+ observeStep: () => clock.tick(),
83
+ turns: [
84
+ [
85
+ step.event(ev.init()),
86
+ step.event(ev.toolCall(CALL_ID, "read", "running", READ_ARGS)),
87
+ step.event(ev.toolCall(CALL_ID, "read", "completed", READ_ARGS, READ_RESULT)),
88
+ step.event(ev.assistant(ASSISTANT_TEXT)),
89
+ step.turnEnded({ inputTokens: 1_800, outputTokens: 60, cacheReadTokens: 0, cacheWriteTokens: 0 }),
90
+ step.finished({ result: ASSISTANT_TEXT, model: { id: FIXTURE.model, params: [] } }),
91
+ ],
92
+ ],
93
+ });
94
+ const record = cursorExecutionRecord({ message: USER_MESSAGE });
95
+ const scenario = beginCursorScenario({
96
+ env,
97
+ clock,
98
+ record,
99
+ sdk: { agents: [agent], catalog: SDK_CATALOG },
100
+ });
101
+
102
+ // ── Act ──────────────────────────────────────────────────────────────────
103
+ const invocation = await runCursorTurn(scenario);
104
+
105
+ // ── Assert: outcome ──────────────────────────────────────────────────────
106
+ expect(invocation.outcome.kind).toBe("returned");
107
+ expect((invocation.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
108
+ expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
109
+
110
+ // ── Assert: the transcript ───────────────────────────────────────────────
111
+ const final = record.lastFullStatus!;
112
+ expect(
113
+ final.messages.map((m) => [m.type, m.content, m.toolCalls.map((tc) => tc.id)]),
114
+ "an empty AI message carries the row; the text after the tool is a new message",
115
+ ).toEqual([
116
+ [MessageType.MESSAGE_AI, "", [CALL_ID]],
117
+ [MessageType.MESSAGE_AI, ASSISTANT_TEXT, []],
118
+ ]);
119
+ const row = record.toolCalls()[0];
120
+ expect(row.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
121
+ expect(row.result).toBe(READ_RESULT);
122
+
123
+ // ── Assert: hermeticity ──────────────────────────────────────────────────
124
+ expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
125
+
126
+ // ── Assert: the golden ───────────────────────────────────────────────────
127
+ const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
128
+ await expect(json).toMatchFileSnapshot("./goldens/tool-only-step.status.json");
129
+ });
130
+ });
@@ -21,6 +21,18 @@
21
21
  * Engine disposition on this arm, today: the handle is `close()`d, not parked
22
22
  * (index.ts `enterUnattributedHookBlockFailure`).
23
23
  *
24
+ * Moved 2026-09-14 (#1097), one line: the FAILED row — built from
25
+ * a single `status: "error"` event with no `running` before it — carries
26
+ * `startedAt`, equal to its `completedAt`: the instant the runner first
27
+ * learned of the call. Until then a completed-only event left the field unset,
28
+ * the one field the canonical builder stamps at every row's creation. This
29
+ * is the only Cursor golden that scripts a completed-only event (the three
30
+ * moves predicted for that rule were all this one).
31
+ *
32
+ * Moved again the same day (#1097), one line: the FAILED row's
33
+ * `result` no longer duplicates its `error` — the hook-block text lives in
34
+ * `error` alone, which is the field the #205 detector reads.
35
+ *
24
36
  * Parent phase rows exercised beyond the earlier scenarios: the gate install's
25
37
  * foreign-hook detection (`workspace-setup.ts` `mergeHooks`); the FAILED
26
38
  * tool-call row built from a single `status: "error"` event; the boundary's
@@ -83,7 +95,7 @@ const USER_HOOKS_JSON =
83
95
  JSON.stringify({ version: 1, hooks: { preToolUse: [{ command: FOREIGN_HOOK_COMMAND }] } }, null, 2) + "\n";
84
96
  /**
85
97
  * The error text Cursor stamps on a hook-blocked tool call
86
- * (`message-translator.ts` HOOK_BLOCK_ERROR_MARKERS). The SDK has no structured
98
+ * (`boundary-rows.ts` HOOK_BLOCK_ERROR_MARKERS). The SDK has no structured
87
99
  * "denied by hook" signal; this text is the only stream-side trace.
88
100
  */
89
101
  const HOOK_BLOCK_ERROR = "blocked by a hook";
@@ -13,9 +13,9 @@
13
13
  * `Agent.create`, no `harness_state_id` bind, nothing streamed.
14
14
  *
15
15
  * Why this is the one terminal arm WITHOUT a file golden (owner ruling,
16
- * 2026-09-11, entry 20260911.03 M0): the wire copy embeds the absolute
16
+ * 2026-09-11, #1070): the wire copy embeds the absolute
17
17
  * workspace path, which is a per-run temp directory and differs between macOS
18
- * and CI. The S0 driver's rule is to control a volatile source at its origin
18
+ * and CI. The hermetic driver's rule (#1048) is to control a volatile source at its origin
19
19
  * or escalate — never redact — so this scenario pins every byte the path does
20
20
  * not touch with explicit assertions and builds the expected message with the
21
21
  * SAME error class over the real resolved path.
@@ -1,5 +1,9 @@
1
1
  /**
2
- * @regression file-hitl-phase0 — pins file-edit HITL fixes #1, #2, #3, #5 (see _projects/2026-06/20260630.01.file-change-hitl-redesign/tasks/T01_3_regression-manifest.md)
2
+ * @regression file-hitl-phase0 — pins four rules of the file-edit gate: one
3
+ * approval gate per turn (a distinct co-pending sibling defers to SKIPPED);
4
+ * decisions bind to the active turn's ledger, never a stale one; the executed
5
+ * capture supersedes the model's proposal; the hook-captured input is
6
+ * authoritative over the streamed snapshot.
3
7
  *
4
8
  * Unit tests for the Cursor-harness HITL denial-ledger flow.
5
9
  *
@@ -61,11 +65,12 @@ import {
61
65
  reconcileDeniedToolCalls,
62
66
  clearProvisionalPostDenialNarration,
63
67
  collapseRedundantToolCallTwins,
64
- toolCallIdentityToken,
65
68
  detectUnattributedHookBlocks,
66
69
  stampUnattendedSkippedToolCalls,
67
70
  HOOK_BLOCK_ERROR_MARKERS,
68
- } from "../message-translator.js";
71
+ } from "../boundary-rows.js";
72
+ import { builderOver } from "../__test-utils__/fold.js";
73
+ import { toolCallIdentityToken } from "../approval-state.js";
69
74
  import { mockWorkspaceBackend } from "../../../__test-utils__/mock-workspace.js";
70
75
  import type { WorkspaceBackend } from "../../../shared/workspace/types.js";
71
76
  import { generateHookScript } from "../hook-script.js";
@@ -240,7 +245,7 @@ describe("reconcileDeniedToolCalls", () => {
240
245
  });
241
246
  const messages = [aiMessageWith([tc])];
242
247
 
243
- const reconciled = await reconcileDeniedToolCalls(messages, [
248
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages), [
244
249
  { toolName: "Write", token: grantToken("write", "gated.txt") },
245
250
  ]);
246
251
 
@@ -279,7 +284,7 @@ describe("reconcileDeniedToolCalls", () => {
279
284
  ]);
280
285
 
281
286
  // MCP tools are keyed name-only (their name is consistent across layers).
282
- await reconcileDeniedToolCalls(messages, [
287
+ await reconcileDeniedToolCalls(messages, builderOver(messages), [
283
288
  { toolName: "apply_x", token: grantToken("apply_x", "") },
284
289
  ], policies);
285
290
 
@@ -302,7 +307,7 @@ describe("reconcileDeniedToolCalls", () => {
302
307
  });
303
308
  const messages = [aiMessageWith([denied, allowed])];
304
309
 
305
- const reconciled = await reconcileDeniedToolCalls(messages, [
310
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages), [
306
311
  { toolName: "Write", token: grantToken("write", "gated.txt") },
307
312
  ]);
308
313
 
@@ -323,7 +328,7 @@ describe("reconcileDeniedToolCalls", () => {
323
328
  const second = toolCall({ id: "c2", name: "edit", args: { path: "gated.txt" } });
324
329
  const messages = [aiMessageWith([first, second])];
325
330
 
326
- const reconciled = await reconcileDeniedToolCalls(messages, [
331
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages), [
327
332
  { toolName: "Write", token: grantToken("write", "gated.txt") },
328
333
  ]);
329
334
 
@@ -344,7 +349,7 @@ describe("reconcileDeniedToolCalls", () => {
344
349
  it("synthesizes a WAITING_APPROVAL tool call when a denial produced no stream event", async () => {
345
350
  const messages = [aiMessageWith([])];
346
351
 
347
- const reconciled = await reconcileDeniedToolCalls(messages, [
352
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages), [
348
353
  { toolName: "Shell", token: grantToken("shell", "rm -rf build") },
349
354
  ]);
350
355
 
@@ -363,7 +368,7 @@ describe("reconcileDeniedToolCalls", () => {
363
368
  it("is a no-op when the ledger is empty", async () => {
364
369
  const tc = toolCall({ id: "c1", status: ToolCallStatus.TOOL_CALL_COMPLETED });
365
370
  const messages = [aiMessageWith([tc])];
366
- expect(await reconcileDeniedToolCalls(messages, [])).toEqual([]);
371
+ expect(await reconcileDeniedToolCalls(messages, builderOver(messages), [])).toEqual([]);
367
372
  expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
368
373
  });
369
374
  });
@@ -391,8 +396,7 @@ describe("reconcileDeniedToolCalls — duplicate denial-twin collapse", () => {
391
396
  });
392
397
  const messages = [aiMessageWith([gate, twin])];
393
398
 
394
- const reconciled = await reconcileDeniedToolCalls(
395
- messages,
399
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
396
400
  [{ toolName: "Write", token: grantToken("write", "notes.md") }],
397
401
  undefined,
398
402
  rootBackend(),
@@ -432,8 +436,7 @@ describe("reconcileDeniedToolCalls — duplicate denial-twin collapse", () => {
432
436
  });
433
437
  const messages = [aiMessageWith([gate, twin])];
434
438
 
435
- const reconciled = await reconcileDeniedToolCalls(
436
- messages,
439
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
437
440
  [{ toolName: "Shell", token: grantToken("shell", "rm -rf build") }],
438
441
  undefined,
439
442
  rootBackend(),
@@ -470,8 +473,7 @@ describe("reconcileDeniedToolCalls — duplicate denial-twin collapse", () => {
470
473
  });
471
474
  const messages = [aiMessageWith([gateA, gateB])];
472
475
 
473
- const reconciled = await reconcileDeniedToolCalls(
474
- messages,
476
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
475
477
  [
476
478
  // The hook records the content-exact token for an edit (its primary token).
477
479
  { toolName: "Write", token: toolCallIdentityToken(gateA) },
@@ -508,8 +510,7 @@ describe("reconcileDeniedToolCalls — duplicate denial-twin collapse", () => {
508
510
  });
509
511
  const messages = [aiMessageWith([gate, twin])];
510
512
 
511
- await reconcileDeniedToolCalls(
512
- messages,
513
+ await reconcileDeniedToolCalls(messages, builderOver(messages),
513
514
  [{ toolName: "Write", token: grantToken("write", "notes.md") }],
514
515
  undefined,
515
516
  rootBackend(),
@@ -552,8 +553,7 @@ describe("reconcileDeniedToolCalls — one gate per turn (deny-only workaround)"
552
553
  });
553
554
  const messages = [aiMessageWith([edit, shell])];
554
555
 
555
- const reconciled = await reconcileDeniedToolCalls(
556
- messages,
556
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
557
557
  [
558
558
  { toolName: "Write", token: grantToken("write", "notes.md") },
559
559
  { toolName: "Shell", token: grantToken("shell", "cat > notes.md") },
@@ -595,8 +595,7 @@ describe("reconcileDeniedToolCalls — one gate per turn (deny-only workaround)"
595
595
  });
596
596
  const messages = [aiMessageWith([edit]), aiMessageWith([shell])];
597
597
 
598
- const reconciled = await reconcileDeniedToolCalls(
599
- messages,
598
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
600
599
  [
601
600
  { toolName: "Write", token: grantToken("write", "notes.md") },
602
601
  { toolName: "Shell", token: grantToken("shell", "cat > notes.md") },
@@ -631,8 +630,7 @@ describe("reconcileDeniedToolCalls — one gate per turn (deny-only workaround)"
631
630
  });
632
631
  const messages = [aiMessageWith([shell, edit])];
633
632
 
634
- const reconciled = await reconcileDeniedToolCalls(
635
- messages,
633
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
636
634
  [
637
635
  { toolName: "Shell", token: toolCallIdentityToken(shell) },
638
636
  { toolName: "Write", token: toolCallIdentityToken(edit) },
@@ -666,8 +664,7 @@ describe("reconcileDeniedToolCalls — one gate per turn (deny-only workaround)"
666
664
  });
667
665
  const messages = [aiMessageWith([mcp, edit])];
668
666
 
669
- const reconciled = await reconcileDeniedToolCalls(
670
- messages,
667
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
671
668
  [
672
669
  { toolName: "fetch", token: toolCallIdentityToken(mcp) },
673
670
  { toolName: "Write", token: toolCallIdentityToken(edit) },
@@ -692,8 +689,7 @@ describe("reconcileDeniedToolCalls — one gate per turn (deny-only workaround)"
692
689
  });
693
690
  const messages = [aiMessageWith([edit])];
694
691
 
695
- const reconciled = await reconcileDeniedToolCalls(
696
- messages,
692
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
697
693
  [{ toolName: "Write", token: grantToken("write", "notes.md") }],
698
694
  undefined,
699
695
  rootBackend(),
@@ -725,8 +721,7 @@ describe("reconcileDeniedToolCalls — one gate per turn (deny-only workaround)"
725
721
  });
726
722
  const messages = [aiMessageWith([edit, shell])];
727
723
 
728
- const reconciled = await reconcileDeniedToolCalls(
729
- messages,
724
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
730
725
  [
731
726
  { toolName: "Write", token: grantToken("write", "notes.md") },
732
727
  { toolName: "Shell", token: grantToken("shell", "cat > notes.md") },
@@ -801,8 +796,7 @@ describe("reconcileDeniedToolCalls — interrupted non-terminal rows (raced work
801
796
  ];
802
797
 
803
798
  // Only the anchor's denial is in the ledger — the workaround's raced it.
804
- const reconciled = await reconcileDeniedToolCalls(
805
- messages,
799
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
806
800
  [{ toolName: "Shell", token: toolCallIdentityToken(anchor) }],
807
801
  undefined,
808
802
  rootBackend(),
@@ -848,8 +842,7 @@ describe("reconcileDeniedToolCalls — interrupted non-terminal rows (raced work
848
842
  create(AgentMessageSchema, { type: MessageType.MESSAGE_AI, content: "trailing reaction" }),
849
843
  ];
850
844
 
851
- const reconciled = await reconcileDeniedToolCalls(
852
- messages,
845
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
853
846
  [{ toolName: "Shell", token: toolCallIdentityToken(anchor) }],
854
847
  undefined,
855
848
  rootBackend(),
@@ -1066,8 +1059,7 @@ describe("reconcileDeniedToolCalls — authoritative hook input overlay", () =>
1066
1059
  });
1067
1060
  const messages = [aiMessageWith([tc])];
1068
1061
 
1069
- await reconcileDeniedToolCalls(
1070
- messages,
1062
+ await reconcileDeniedToolCalls(messages, builderOver(messages),
1071
1063
  [{
1072
1064
  toolName: "Write",
1073
1065
  token: grantToken("write", "src/new.ts"),
@@ -1097,8 +1089,7 @@ describe("reconcileDeniedToolCalls — authoritative hook input overlay", () =>
1097
1089
  });
1098
1090
  const messages = [aiMessageWith([tc])];
1099
1091
 
1100
- await reconcileDeniedToolCalls(
1101
- messages,
1092
+ await reconcileDeniedToolCalls(messages, builderOver(messages),
1102
1093
  [{
1103
1094
  toolName: "Write",
1104
1095
  token: grantToken("write", ".env"),
@@ -1122,8 +1113,7 @@ describe("reconcileDeniedToolCalls — authoritative hook input overlay", () =>
1122
1113
  });
1123
1114
  const messages = [aiMessageWith([tc])];
1124
1115
 
1125
- await reconcileDeniedToolCalls(
1126
- messages,
1116
+ await reconcileDeniedToolCalls(messages, builderOver(messages),
1127
1117
  [{
1128
1118
  toolName: "StrReplace",
1129
1119
  token: grantToken("write", "src/app.ts"),
@@ -1147,8 +1137,7 @@ describe("reconcileDeniedToolCalls — authoritative hook input overlay", () =>
1147
1137
  });
1148
1138
  const messages = [aiMessageWith([tc])];
1149
1139
 
1150
- await reconcileDeniedToolCalls(
1151
- messages,
1140
+ await reconcileDeniedToolCalls(messages, builderOver(messages),
1152
1141
  [{
1153
1142
  toolName: "EditNotebook",
1154
1143
  token: grantToken("write", "nb.ipynb"),
@@ -1170,8 +1159,7 @@ describe("reconcileDeniedToolCalls — authoritative hook input overlay", () =>
1170
1159
  });
1171
1160
  const messages = [aiMessageWith([tc])];
1172
1161
 
1173
- await reconcileDeniedToolCalls(
1174
- messages,
1162
+ await reconcileDeniedToolCalls(messages, builderOver(messages),
1175
1163
  [{
1176
1164
  toolName: "Shell",
1177
1165
  token: grantToken("shell", "rm -rf build"),
@@ -1190,8 +1178,7 @@ describe("reconcileDeniedToolCalls — authoritative hook input overlay", () =>
1190
1178
  // proposed content from the captured input, not a bare {path}.
1191
1179
  const messages = [aiMessageWith([])];
1192
1180
 
1193
- const reconciled = await reconcileDeniedToolCalls(
1194
- messages,
1181
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
1195
1182
  [{
1196
1183
  toolName: "Write",
1197
1184
  token: grantToken("write", "ghost.md"),
@@ -1217,8 +1204,7 @@ describe("reconcileDeniedToolCalls — authoritative hook input overlay", () =>
1217
1204
  });
1218
1205
  const messages = [aiMessageWith([tc])];
1219
1206
 
1220
- await reconcileDeniedToolCalls(
1221
- messages,
1207
+ await reconcileDeniedToolCalls(messages, builderOver(messages),
1222
1208
  [{
1223
1209
  toolName: "Write",
1224
1210
  token: grantToken("write", "big.ts"),
@@ -1259,8 +1245,7 @@ describe("reconcileDeniedToolCalls — normalized-path fallback (abs/rel drift)"
1259
1245
  });
1260
1246
  const messages = [aiMessageWith([streamed])];
1261
1247
 
1262
- const reconciled = await reconcileDeniedToolCalls(
1263
- messages,
1248
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
1264
1249
  [{ toolName: "Write", token: grantToken("write", "/root/notes.md") }],
1265
1250
  undefined,
1266
1251
  rootBackend(),
@@ -1288,8 +1273,7 @@ describe("reconcileDeniedToolCalls — normalized-path fallback (abs/rel drift)"
1288
1273
  });
1289
1274
  const messages = [aiMessageWith([streamed])];
1290
1275
 
1291
- await reconcileDeniedToolCalls(
1292
- messages,
1276
+ await reconcileDeniedToolCalls(messages, builderOver(messages),
1293
1277
  [{ toolName: "Write", token: grantToken("write", "/root/src/app.ts") }],
1294
1278
  undefined,
1295
1279
  rootBackend(),
@@ -1304,8 +1288,7 @@ describe("reconcileDeniedToolCalls — normalized-path fallback (abs/rel drift)"
1304
1288
  // The genuine no-stream-event denial (rare) must still surface a gate.
1305
1289
  const messages = [aiMessageWith([])];
1306
1290
 
1307
- const reconciled = await reconcileDeniedToolCalls(
1308
- messages,
1291
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
1309
1292
  [{ toolName: "Write", token: grantToken("write", "/root/ghost.md") }],
1310
1293
  undefined,
1311
1294
  rootBackend(),
@@ -1327,8 +1310,7 @@ describe("reconcileDeniedToolCalls — normalized-path fallback (abs/rel drift)"
1327
1310
  });
1328
1311
  const messages = [aiMessageWith([streamed])];
1329
1312
 
1330
- const reconciled = await reconcileDeniedToolCalls(
1331
- messages,
1313
+ const reconciled = await reconcileDeniedToolCalls(messages, builderOver(messages),
1332
1314
  [{ toolName: "Write", token: grantToken("write", "/root/notes.md") }],
1333
1315
  undefined,
1334
1316
  rootBackend(),
@@ -1566,7 +1548,7 @@ describe("first-denial stop contract", () => {
1566
1548
  expect(messages.some((m) => m.content.includes("try the shell"))).toBe(false);
1567
1549
 
1568
1550
  // Phase 12 reconcile + redact on the stopped transcript yields the clean shape.
1569
- const denied = await reconcileDeniedToolCalls(messages, await readDenialLedger(ws));
1551
+ const denied = await reconcileDeniedToolCalls(messages, builderOver(messages), await readDenialLedger(ws));
1570
1552
  expect(denied).toHaveLength(1);
1571
1553
  expect(edit.status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
1572
1554
  const redacted = clearProvisionalPostDenialNarration(messages, denied);
@@ -1610,7 +1592,8 @@ describe("first-denial stop contract", () => {
1610
1592
  expect(cancelled).toBe(false);
1611
1593
  expect(consumed).toBe(3); // the whole turn is consumed
1612
1594
  // And the boundary reconcile manufactures no gate from it either.
1613
- const denied = await reconcileDeniedToolCalls([], await readDenialLedger(ws));
1595
+ const messages: AgentMessage[] = [];
1596
+ const denied = await reconcileDeniedToolCalls(messages, builderOver(messages), await readDenialLedger(ws));
1614
1597
  expect(denied).toEqual([]);
1615
1598
  });
1616
1599
 
@@ -1641,7 +1624,7 @@ describe("first-denial stop contract", () => {
1641
1624
 
1642
1625
  // Phase 12 on the stopped transcript: the anchor gates, and the one raced
1643
1626
  // reaction message is blanked by the trim backstop.
1644
- const denied = await reconcileDeniedToolCalls(messages, await readDenialLedger(ws), undefined, rootBackend());
1627
+ const denied = await reconcileDeniedToolCalls(messages, builderOver(messages), await readDenialLedger(ws), undefined, rootBackend());
1645
1628
  expect(denied).toHaveLength(1);
1646
1629
  expect(shell.status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
1647
1630
  const redacted = clearProvisionalPostDenialNarration(messages, denied);
@@ -1812,7 +1795,7 @@ describe("reconcileDeniedToolCalls — non-approval kinds never gate", () => {
1812
1795
  error: "blocked by a hook", args: { path: ".env" },
1813
1796
  });
1814
1797
  const messages = [aiMessageWith([secretRow])];
1815
- const denied = await reconcileDeniedToolCalls(messages, [
1798
+ const denied = await reconcileDeniedToolCalls(messages, builderOver(messages), [
1816
1799
  { toolName: "Write", token: toolCallIdentityToken(secretRow), kind: "secret" },
1817
1800
  { toolName: "Write", token: grantToken("write", "app.log"), kind: "capture-error" },
1818
1801
  { toolName: "Shell", token: grantToken("shell", "make"), kind: "fail-closed" },
@@ -1834,7 +1817,7 @@ describe("reconcileDeniedToolCalls — non-approval kinds never gate", () => {
1834
1817
  error: "blocked by a hook", args: { path: ".env" },
1835
1818
  });
1836
1819
  const messages = [aiMessageWith([gated, secretRow])];
1837
- const denied = await reconcileDeniedToolCalls(messages, [
1820
+ const denied = await reconcileDeniedToolCalls(messages, builderOver(messages), [
1838
1821
  // Ledger order puts the secret FIRST: the anchor must still be the first
1839
1822
  // APPROVAL entry, never a non-approval kind.
1840
1823
  { toolName: "Write", token: toolCallIdentityToken(secretRow), kind: "secret" },