@stigmer/runner 3.15.3-dev.20260914100347 → 3.15.3-dev.20260916211208

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 (549) hide show
  1. package/README.md +1 -0
  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 +14 -11
  12. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
  13. package/dist/activities/execute-cursor/__test-utils__/fold.d.ts +61 -0
  14. package/dist/activities/execute-cursor/__test-utils__/fold.js +94 -0
  15. package/dist/activities/execute-cursor/__test-utils__/fold.js.map +1 -0
  16. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +1 -1
  17. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -1
  18. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +52 -14
  19. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +46 -12
  20. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
  21. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +21 -28
  22. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +27 -11
  23. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -1
  24. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.d.ts +70 -26
  25. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js +82 -32
  26. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js.map +1 -1
  27. package/dist/activities/execute-cursor/adapter.d.ts +8 -7
  28. package/dist/activities/execute-cursor/adapter.js +13 -9
  29. package/dist/activities/execute-cursor/adapter.js.map +1 -1
  30. package/dist/activities/execute-cursor/approval-policy.d.ts +10 -10
  31. package/dist/activities/execute-cursor/approval-policy.js +12 -23
  32. package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
  33. package/dist/activities/execute-cursor/approval-state.d.ts +35 -1
  34. package/dist/activities/execute-cursor/approval-state.js +65 -0
  35. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  36. package/dist/activities/execute-cursor/{message-translator.d.ts → boundary-rows.d.ts} +45 -341
  37. package/dist/activities/execute-cursor/boundary-rows.js +1018 -0
  38. package/dist/activities/execute-cursor/boundary-rows.js.map +1 -0
  39. package/dist/activities/execute-cursor/cursor-capabilities.d.ts +1 -1
  40. package/dist/activities/execute-cursor/cursor-capabilities.js +1 -1
  41. package/dist/activities/execute-cursor/cursor-event-recorder.d.ts +33 -14
  42. package/dist/activities/execute-cursor/cursor-event-recorder.js +43 -21
  43. package/dist/activities/execute-cursor/cursor-event-recorder.js.map +1 -1
  44. package/dist/activities/execute-cursor/prompt-builder.d.ts +3 -3
  45. package/dist/activities/execute-cursor/prompt-builder.js +4 -4
  46. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  47. package/dist/activities/execute-cursor/sdk-warmup.d.ts +15 -6
  48. package/dist/activities/execute-cursor/sdk-warmup.js +15 -6
  49. package/dist/activities/execute-cursor/sdk-warmup.js.map +1 -1
  50. package/dist/activities/execute-cursor/service-tier.js +1 -2
  51. package/dist/activities/execute-cursor/service-tier.js.map +1 -1
  52. package/dist/activities/execute-cursor/session-lifecycle.d.ts +38 -59
  53. package/dist/activities/execute-cursor/session-lifecycle.js +59 -96
  54. package/dist/activities/execute-cursor/session-lifecycle.js.map +1 -1
  55. package/dist/activities/execute-cursor/session-store.d.ts +100 -0
  56. package/dist/activities/execute-cursor/session-store.js +157 -0
  57. package/dist/activities/execute-cursor/session-store.js.map +1 -0
  58. package/dist/activities/execute-cursor/sub-agent-steps.d.ts +79 -0
  59. package/dist/activities/execute-cursor/sub-agent-steps.js +180 -0
  60. package/dist/activities/execute-cursor/sub-agent-steps.js.map +1 -0
  61. package/dist/activities/execute-cursor/tool-result.d.ts +49 -0
  62. package/dist/activities/execute-cursor/tool-result.js +130 -0
  63. package/dist/activities/execute-cursor/tool-result.js.map +1 -0
  64. package/dist/activities/execute-cursor/translator.d.ts +178 -0
  65. package/dist/activities/execute-cursor/translator.js +485 -0
  66. package/dist/activities/execute-cursor/translator.js.map +1 -0
  67. package/dist/activities/execute-cursor/turn-boundary.d.ts +15 -5
  68. package/dist/activities/execute-cursor/turn-boundary.js +10 -14
  69. package/dist/activities/execute-cursor/turn-boundary.js.map +1 -1
  70. package/dist/activities/execute-cursor/turn-settle.d.ts +11 -7
  71. package/dist/activities/execute-cursor/turn-settle.js +47 -34
  72. package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
  73. package/dist/activities/execute-cursor/turn-setup.d.ts +2 -2
  74. package/dist/activities/execute-cursor/turn-setup.js +5 -5
  75. package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
  76. package/dist/activities/execute-cursor/turn-stream.d.ts +23 -14
  77. package/dist/activities/execute-cursor/turn-stream.js +41 -28
  78. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  79. package/dist/activities/execute-cursor/turn.d.ts +4 -4
  80. package/dist/activities/execute-cursor/turn.js +5 -5
  81. package/dist/activities/execute-cursor/usage-pricing.d.ts +2 -2
  82. package/dist/activities/execute-cursor/usage-pricing.js +2 -2
  83. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +2 -2
  84. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +2 -2
  85. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +16 -11
  86. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +22 -10
  87. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -1
  88. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +1 -1
  89. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -1
  90. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +2 -3
  91. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +2 -3
  92. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -1
  93. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +12 -3
  94. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +18 -4
  95. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
  96. package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.d.ts +10 -1
  97. package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js +24 -11
  98. package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js.map +1 -1
  99. package/dist/activities/execute-deep-agent/adapter.d.ts +1 -1
  100. package/dist/activities/execute-deep-agent/adapter.js +1 -1
  101. package/dist/activities/execute-deep-agent/approval-file-change.d.ts +19 -19
  102. package/dist/activities/execute-deep-agent/approval-file-change.js +16 -15
  103. package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -1
  104. package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +2 -2
  105. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +2 -2
  106. package/dist/activities/execute-deep-agent/hitl.d.ts +2 -2
  107. package/dist/activities/execute-deep-agent/hitl.js +2 -2
  108. package/dist/activities/execute-deep-agent/inline-publisher.d.ts +13 -3
  109. package/dist/activities/execute-deep-agent/inline-publisher.js +3 -3
  110. package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
  111. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +3 -3
  112. package/dist/activities/execute-deep-agent/prompt-builder.js +6 -7
  113. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  114. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +16 -8
  115. package/dist/activities/execute-deep-agent/streaming-side-effects.js +20 -33
  116. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  117. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +3 -3
  118. package/dist/activities/execute-deep-agent/subagent-transformer.js +1 -1
  119. package/dist/activities/execute-deep-agent/translator.d.ts +134 -0
  120. package/dist/activities/execute-deep-agent/translator.js +418 -0
  121. package/dist/activities/execute-deep-agent/translator.js.map +1 -0
  122. package/dist/activities/execute-deep-agent/turn-settle.d.ts +13 -7
  123. package/dist/activities/execute-deep-agent/turn-settle.js +34 -45
  124. package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -1
  125. package/dist/activities/execute-deep-agent/turn-setup.d.ts +8 -8
  126. package/dist/activities/execute-deep-agent/turn-setup.js +7 -7
  127. package/dist/activities/execute-deep-agent/turn-stream.d.ts +13 -10
  128. package/dist/activities/execute-deep-agent/turn-stream.js +44 -41
  129. package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -1
  130. package/dist/activities/execute-deep-agent/turn.d.ts +3 -3
  131. package/dist/activities/execute-deep-agent/turn.js +3 -3
  132. package/dist/config.d.ts +2 -2
  133. package/dist/harness/approval-decisions.d.ts +1 -1
  134. package/dist/harness/approval-decisions.js +1 -1
  135. package/dist/harness/capabilities.d.ts +21 -11
  136. package/dist/harness/capabilities.js +2 -2
  137. package/dist/harness/capture.d.ts +4 -4
  138. package/dist/harness/capture.js +4 -4
  139. package/dist/harness/persist-chokepoint.d.ts +3 -3
  140. package/dist/harness/persist-chokepoint.js +3 -3
  141. package/dist/harness/registry.d.ts +2 -2
  142. package/dist/harness/registry.js +2 -2
  143. package/dist/harness/run-turn.d.ts +6 -4
  144. package/dist/harness/run-turn.js +31 -9
  145. package/dist/harness/run-turn.js.map +1 -1
  146. package/dist/harness/stop-controller.d.ts +1 -1
  147. package/dist/harness/stop-controller.js +1 -1
  148. package/dist/harness/terminal-table.d.ts +7 -7
  149. package/dist/harness/terminal-table.js +7 -7
  150. package/dist/harness/transcript/builder.d.ts +182 -0
  151. package/dist/harness/transcript/builder.js +693 -0
  152. package/dist/harness/transcript/builder.js.map +1 -0
  153. package/dist/harness/transcript/events.d.ts +218 -0
  154. package/dist/harness/transcript/events.js +50 -0
  155. package/dist/harness/transcript/events.js.map +1 -0
  156. package/dist/harness/transcript/state.d.ts +110 -0
  157. package/dist/harness/transcript/state.js +148 -0
  158. package/dist/harness/transcript/state.js.map +1 -0
  159. package/dist/harness/turn-context.d.ts +54 -38
  160. package/dist/harness/turn-context.js +42 -43
  161. package/dist/harness/turn-context.js.map +1 -1
  162. package/dist/harness/types.d.ts +63 -22
  163. package/dist/harness/types.js +12 -11
  164. package/dist/harness/types.js.map +1 -1
  165. package/dist/harness/usage-accumulator.d.ts +2 -2
  166. package/dist/harness/usage-accumulator.js +2 -2
  167. package/dist/harness-adapters.d.ts +5 -4
  168. package/dist/harness-adapters.js +5 -4
  169. package/dist/harness-adapters.js.map +1 -1
  170. package/dist/middleware/approval-gate.d.ts +6 -0
  171. package/dist/middleware/approval-gate.js +7 -57
  172. package/dist/middleware/approval-gate.js.map +1 -1
  173. package/dist/middleware/cost-advisory.d.ts +3 -3
  174. package/dist/middleware/cost-advisory.js +3 -3
  175. package/dist/middleware/index.d.ts +1 -1
  176. package/dist/middleware/index.js +1 -1
  177. package/dist/runner-manager.js +1 -1
  178. package/dist/runner-manager.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-vision.d.ts +2 -1
  188. package/dist/shared/attachment-vision.js.map +1 -1
  189. package/dist/shared/attachment-zip.d.ts +2 -2
  190. package/dist/shared/attachment-zip.js +2 -2
  191. package/dist/shared/cost-guard.d.ts +2 -2
  192. package/dist/shared/cost-guard.js +2 -2
  193. package/dist/shared/exact-apply.d.ts +1 -1
  194. package/dist/shared/exact-apply.js +1 -1
  195. package/dist/shared/execution-context.d.ts +2 -2
  196. package/dist/shared/execution-context.js +2 -2
  197. package/dist/shared/extract-structured-output.d.ts +2 -2
  198. package/dist/shared/extract-structured-output.js +2 -2
  199. package/dist/shared/filereview/cas-touched.d.ts +2 -2
  200. package/dist/shared/filereview/cas-touched.js +2 -2
  201. package/dist/shared/heartbeat.d.ts +1 -1
  202. package/dist/shared/heartbeat.js +1 -1
  203. package/dist/shared/llm-backend.d.ts +0 -4
  204. package/dist/shared/llm-backend.js +0 -4
  205. package/dist/shared/llm-backend.js.map +1 -1
  206. package/dist/shared/model-error.d.ts +1 -2
  207. package/dist/shared/model-error.js +1 -2
  208. package/dist/shared/model-error.js.map +1 -1
  209. package/dist/shared/persist-decision.d.ts +18 -24
  210. package/dist/shared/persist-decision.js +19 -11
  211. package/dist/shared/persist-decision.js.map +1 -1
  212. package/dist/shared/prompt-sections.d.ts +4 -4
  213. package/dist/shared/prompt-sections.js +4 -4
  214. package/dist/shared/skill-resolver.d.ts +3 -3
  215. package/dist/shared/skill-resolver.js +3 -3
  216. package/dist/shared/status-offload.d.ts +1 -1
  217. package/dist/shared/status-offload.js +2 -2
  218. package/dist/shared/status-offload.js.map +1 -1
  219. package/dist/shared/streaming-scheduler.d.ts +2 -7
  220. package/dist/shared/streaming-scheduler.js +6 -14
  221. package/dist/shared/streaming-scheduler.js.map +1 -1
  222. package/dist/shared/subagent-rows.d.ts +2 -2
  223. package/dist/shared/subagent-rows.js +2 -2
  224. package/dist/shared/todos.d.ts +3 -3
  225. package/dist/shared/todos.js +3 -3
  226. package/dist/shared/tool-row.d.ts +19 -1
  227. package/dist/shared/tool-row.js +36 -2
  228. package/dist/shared/tool-row.js.map +1 -1
  229. package/dist/shared/worker-shutdown.d.ts +1 -1
  230. package/dist/shared/worker-shutdown.js +1 -1
  231. package/dist/shared/workspace/session-provision.d.ts +1 -1
  232. package/dist/shared/workspace/session-provision.js +1 -1
  233. package/dist/shared/workspace/sources/empty.d.ts +1 -1
  234. package/dist/shared/workspace/sources/empty.js +1 -2
  235. package/dist/shared/workspace/sources/empty.js.map +1 -1
  236. package/dist/shared/workspace/sources/git.js +4 -4
  237. package/dist/shared/workspace/sources/git.js.map +1 -1
  238. package/dist/shared/workspace/sources/local-path.js +6 -6
  239. package/dist/shared/workspace/sources/local-path.js.map +1 -1
  240. package/dist/shared/workspace/stigmer-link.d.ts +1 -1
  241. package/dist/shared/workspace/stigmer-link.js +1 -1
  242. package/dist/shared/workspace/types.d.ts +2 -5
  243. package/dist/shared/workspace/types.js +0 -6
  244. package/dist/shared/workspace/types.js.map +1 -1
  245. package/dist/shared/workspace/writeback-coordinator.d.ts +17 -4
  246. package/dist/shared/workspace/writeback-coordinator.js +7 -9
  247. package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
  248. package/package.json +5 -5
  249. package/src/__test-utils__/__tests__/execution-record-barrier.test.ts +70 -0
  250. package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +35 -1
  251. package/src/__test-utils__/approval-contract/contract.ts +1 -1
  252. package/src/__test-utils__/approval-contract/types.ts +1 -1
  253. package/src/__test-utils__/harness-boot-order-child.ts +1 -1
  254. package/src/__test-utils__/harness-contract/contract.ts +8 -8
  255. package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
  256. package/src/__test-utils__/harness-contract/runtime-contract.ts +52 -16
  257. package/src/__test-utils__/harness-contract/scripted-adapter.ts +47 -43
  258. package/src/__test-utils__/hermetic-activity.ts +113 -9
  259. package/src/__test-utils__/model-registry-fixture.ts +31 -7
  260. package/src/__test-utils__/module-specifiers.ts +32 -3
  261. package/src/__tests__/harness-boot-order.test.ts +1 -1
  262. package/src/__tests__/harness-contract.test.ts +1 -1
  263. package/src/__tests__/runner-manager.test.ts +1 -1
  264. package/src/activities/execute-cursor/__test-utils__/__tests__/scripted-double.test.ts +32 -8
  265. package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +12 -10
  266. package/src/activities/execute-cursor/__test-utils__/fold.ts +116 -0
  267. package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +1 -1
  268. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +72 -19
  269. package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +35 -29
  270. package/src/activities/execute-cursor/__test-utils__/scripted-sdk.ts +94 -38
  271. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +2 -1
  272. package/src/activities/execute-cursor/__tests__/approval-state.test.ts +14 -21
  273. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +3 -3
  274. package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +2 -1
  275. package/src/activities/execute-cursor/__tests__/cursor-baseurl-routing.test.ts +10 -8
  276. package/src/activities/execute-cursor/__tests__/cursor-event-recorder.test.ts +142 -0
  277. package/src/activities/execute-cursor/__tests__/cursor-fetch-interceptor-bypass.test.ts +24 -12
  278. package/src/activities/execute-cursor/__tests__/cursor-generate-image-live.test.ts +7 -2
  279. package/src/activities/execute-cursor/__tests__/cursor-hook-protocol-live.test.ts +163 -0
  280. package/src/activities/execute-cursor/__tests__/cursor-sdk-auth-smoke.test.ts +23 -20
  281. package/src/activities/execute-cursor/__tests__/cursor-transcript-live.test.ts +298 -0
  282. package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +4 -2
  283. package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +1 -1
  284. package/src/activities/execute-cursor/__tests__/hermetic/deny-and-retry.test.ts +5 -2
  285. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +5 -2
  286. package/src/activities/execute-cursor/__tests__/hermetic/goldens/mcp-tool-call.status.json +71 -0
  287. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-task-note.status.json +73 -0
  288. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.error-delta.status.json +78 -0
  289. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.output-first.status.json +72 -0
  290. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.status.json +72 -0
  291. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sub-agent-delegation.status.json +109 -0
  292. package/src/activities/execute-cursor/__tests__/hermetic/goldens/thinking-turn.status.json +78 -0
  293. package/src/activities/execute-cursor/__tests__/hermetic/goldens/todo-write.status.json +119 -0
  294. package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-error.status.json +68 -0
  295. package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-only-step.status.json +67 -0
  296. package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +1 -1
  297. package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +10 -7
  298. package/src/activities/execute-cursor/__tests__/hermetic/mcp-tool-call.test.ts +146 -0
  299. package/src/activities/execute-cursor/__tests__/hermetic/pause-vs-shutdown.test.ts +8 -5
  300. package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +6 -2
  301. package/src/activities/execute-cursor/__tests__/hermetic/recovery-fresh-agent.test.ts +4 -1
  302. package/src/activities/execute-cursor/__tests__/hermetic/run-wait-arms.test.ts +6 -2
  303. package/src/activities/execute-cursor/__tests__/hermetic/sdk-task-note.test.ts +122 -0
  304. package/src/activities/execute-cursor/__tests__/hermetic/shell-output-delta.test.ts +276 -0
  305. package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +12 -9
  306. package/src/activities/execute-cursor/__tests__/hermetic/sub-agent-delegation.test.ts +196 -0
  307. package/src/activities/execute-cursor/__tests__/hermetic/thinking-turn.test.ts +145 -0
  308. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +4 -1
  309. package/src/activities/execute-cursor/__tests__/hermetic/todo-write.test.ts +165 -0
  310. package/src/activities/execute-cursor/__tests__/hermetic/tool-call.test.ts +7 -4
  311. package/src/activities/execute-cursor/__tests__/hermetic/tool-error.test.ts +138 -0
  312. package/src/activities/execute-cursor/__tests__/hermetic/tool-only-step.test.ts +130 -0
  313. package/src/activities/execute-cursor/__tests__/hermetic/unattributed-hook-block.test.ts +17 -2
  314. package/src/activities/execute-cursor/__tests__/hermetic/workspace-lock-timeout.test.ts +5 -2
  315. package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +43 -60
  316. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +23 -28
  317. package/src/activities/execute-cursor/__tests__/hook-script.test.ts +2 -1
  318. package/src/activities/execute-cursor/__tests__/http2-interceptor.test.ts +1 -1
  319. package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +5 -5
  320. package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +11 -13
  321. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +8 -11
  322. package/src/activities/execute-cursor/__tests__/session-lifecycle.test.ts +86 -58
  323. package/src/activities/execute-cursor/__tests__/session-store.test.ts +220 -0
  324. package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +8 -22
  325. package/src/activities/execute-cursor/__tests__/translator.test.ts +594 -0
  326. package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +8 -5
  327. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +87 -63
  328. package/src/activities/execute-cursor/adapter.ts +13 -9
  329. package/src/activities/execute-cursor/approval-policy.ts +12 -24
  330. package/src/activities/execute-cursor/approval-state.ts +66 -1
  331. package/src/activities/execute-cursor/boundary-rows.ts +1132 -0
  332. package/src/activities/execute-cursor/cursor-capabilities.ts +1 -1
  333. package/src/activities/execute-cursor/cursor-event-recorder.ts +57 -28
  334. package/src/activities/execute-cursor/fetch-interceptor.ts +1 -1
  335. package/src/activities/execute-cursor/http2-interceptor.ts +1 -1
  336. package/src/activities/execute-cursor/prompt-builder.ts +6 -6
  337. package/src/activities/execute-cursor/sdk-warmup.ts +15 -6
  338. package/src/activities/execute-cursor/service-tier.ts +1 -2
  339. package/src/activities/execute-cursor/session-lifecycle.ts +63 -109
  340. package/src/activities/execute-cursor/session-store.ts +187 -0
  341. package/src/activities/execute-cursor/sub-agent-steps.ts +188 -0
  342. package/src/activities/execute-cursor/tool-result.ts +128 -0
  343. package/src/activities/execute-cursor/translator.ts +541 -0
  344. package/src/activities/execute-cursor/turn-boundary.ts +21 -14
  345. package/src/activities/execute-cursor/turn-settle.ts +46 -34
  346. package/src/activities/execute-cursor/turn-setup.ts +5 -5
  347. package/src/activities/execute-cursor/turn-stream.ts +49 -46
  348. package/src/activities/execute-cursor/turn.ts +5 -5
  349. package/src/activities/execute-cursor/usage-pricing.ts +2 -2
  350. package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +2 -2
  351. package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +23 -11
  352. package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +1 -1
  353. package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +2 -3
  354. package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +25 -4
  355. package/src/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.ts +25 -11
  356. package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +1 -1
  357. package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +1 -1
  358. package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +6 -17
  359. package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +25 -8
  360. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +2 -7
  361. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +4 -14
  362. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +1 -5
  363. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +4 -7
  364. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +1 -5
  365. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +2 -9
  366. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +2 -9
  367. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +1 -4
  368. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +1 -4
  369. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +1 -4
  370. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +109 -76
  371. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +1 -5
  372. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +2 -10
  373. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +2 -10
  374. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +1 -5
  375. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +3 -6
  376. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +1 -4
  377. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +1 -4
  378. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/write-todos.status.json +92 -0
  379. package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +3 -3
  380. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +9 -5
  381. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +3 -3
  382. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +3 -3
  383. package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +7 -6
  384. package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +10 -11
  385. package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +4 -4
  386. package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +9 -8
  387. package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +21 -13
  388. package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +2 -2
  389. package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +7 -7
  390. package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +28 -12
  391. package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +21 -21
  392. package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +5 -5
  393. package/src/activities/execute-deep-agent/__tests__/hermetic/write-todos.test.ts +160 -0
  394. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +35 -30
  395. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +3 -4
  396. package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +12 -10
  397. package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +13 -3
  398. package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +26 -18
  399. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +1 -1
  400. package/src/activities/execute-deep-agent/__tests__/{subagent-tracker.test.ts → translator-subagents.test.ts} +76 -57
  401. package/src/activities/execute-deep-agent/__tests__/{v3-status-builder.test.ts → translator-through-builder.test.ts} +213 -299
  402. package/src/activities/execute-deep-agent/__tests__/translator.test.ts +505 -0
  403. package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +11 -29
  404. package/src/activities/execute-deep-agent/adapter.ts +1 -1
  405. package/src/activities/execute-deep-agent/approval-file-change.ts +20 -22
  406. package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +2 -2
  407. package/src/activities/execute-deep-agent/hitl.ts +2 -2
  408. package/src/activities/execute-deep-agent/inline-publisher.ts +16 -5
  409. package/src/activities/execute-deep-agent/prompt-builder.ts +6 -7
  410. package/src/activities/execute-deep-agent/streaming-side-effects.ts +22 -31
  411. package/src/activities/execute-deep-agent/subagent-transformer.ts +3 -3
  412. package/src/activities/execute-deep-agent/translator.ts +463 -0
  413. package/src/activities/execute-deep-agent/turn-settle.ts +35 -43
  414. package/src/activities/execute-deep-agent/turn-setup.ts +8 -8
  415. package/src/activities/execute-deep-agent/turn-stream.ts +53 -51
  416. package/src/activities/execute-deep-agent/turn.ts +3 -3
  417. package/src/config.ts +2 -2
  418. package/src/harness/README.md +335 -0
  419. package/src/harness/__tests__/approval-decisions.test.ts +2 -2
  420. package/src/harness/__tests__/capture.test.ts +3 -3
  421. package/src/harness/__tests__/import-direction.test.ts +3 -2
  422. package/src/harness/__tests__/mount-skills.test.ts +4 -1
  423. package/src/harness/__tests__/readme-index.test.ts +339 -0
  424. package/src/harness/__tests__/run-turn.test.ts +131 -6
  425. package/src/harness/__tests__/transcript-is-engine-free.test.ts +83 -0
  426. package/src/harness/__tests__/transcript-writers.test.ts +191 -0
  427. package/src/harness/__tests__/turn-attachments-vision.test.ts +5 -2
  428. package/src/harness/__tests__/turn-context.test.ts +5 -5
  429. package/src/harness/approval-decisions.ts +1 -1
  430. package/src/harness/capabilities.ts +21 -11
  431. package/src/harness/capture.ts +4 -4
  432. package/src/harness/persist-chokepoint.ts +3 -3
  433. package/src/harness/registry.ts +2 -2
  434. package/src/harness/run-turn.ts +33 -9
  435. package/src/harness/stop-controller.ts +1 -1
  436. package/src/harness/terminal-table.ts +7 -7
  437. package/src/harness/transcript/__tests__/builder.test.ts +975 -0
  438. package/src/harness/transcript/__tests__/state.test.ts +143 -0
  439. package/src/harness/transcript/builder.ts +755 -0
  440. package/src/harness/transcript/events.ts +262 -0
  441. package/src/harness/transcript/state.ts +167 -0
  442. package/src/harness/turn-context.ts +61 -46
  443. package/src/harness/types.ts +64 -22
  444. package/src/harness/usage-accumulator.ts +2 -2
  445. package/src/harness-adapters.ts +5 -4
  446. package/src/middleware/__tests__/cost-advisory.test.ts +2 -2
  447. package/src/middleware/approval-gate.ts +7 -74
  448. package/src/middleware/cost-advisory.ts +3 -3
  449. package/src/middleware/index.ts +1 -1
  450. package/src/runner-manager.ts +1 -1
  451. package/src/shared/__tests__/approval-policy.test.ts +45 -0
  452. package/src/shared/__tests__/args-preview.test.ts +40 -20
  453. package/src/shared/__tests__/attachment-naming.test.ts +1 -1
  454. package/src/shared/__tests__/attachment-resolver.test.ts +5 -5
  455. package/src/shared/__tests__/attachment-zip.test.ts +1 -1
  456. package/src/shared/__tests__/blueprint-resolver.test.ts +1 -1
  457. package/src/shared/__tests__/cost-guard.test.ts +1 -1
  458. package/src/shared/__tests__/exact-apply.test.ts +2 -1
  459. package/src/shared/__tests__/persist-decision.test.ts +19 -33
  460. package/src/shared/__tests__/prompt-sections.test.ts +1 -1
  461. package/src/shared/__tests__/stamp-flowed-rows.test.ts +1 -1
  462. package/src/shared/__tests__/status-offload.test.ts +1 -1
  463. package/src/shared/__tests__/streaming-scheduler.test.ts +8 -9
  464. package/src/shared/__tests__/todos.test.ts +1 -1
  465. package/src/shared/__tests__/tool-row.test.ts +1 -1
  466. package/src/shared/approval-policy.ts +95 -0
  467. package/src/shared/args-preview.ts +51 -42
  468. package/src/shared/attachment-resolver.ts +4 -4
  469. package/src/shared/attachment-vision.ts +2 -1
  470. package/src/shared/attachment-zip.ts +2 -2
  471. package/src/shared/cost-guard.ts +2 -2
  472. package/src/shared/exact-apply.ts +1 -1
  473. package/src/shared/execution-context.ts +2 -2
  474. package/src/shared/extract-structured-output.ts +2 -2
  475. package/src/shared/filereview/__tests__/capture.test.ts +9 -5
  476. package/src/shared/filereview/__tests__/git-substrate.test.ts +4 -1
  477. package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +3 -3
  478. package/src/shared/filereview/cas-touched.ts +2 -2
  479. package/src/shared/heartbeat.ts +1 -1
  480. package/src/shared/llm-backend.ts +0 -4
  481. package/src/shared/model-error.ts +1 -2
  482. package/src/shared/persist-decision.ts +19 -28
  483. package/src/shared/prompt-sections.ts +4 -4
  484. package/src/shared/skill-resolver.ts +3 -3
  485. package/src/shared/status-offload.ts +2 -2
  486. package/src/shared/streaming-scheduler.ts +8 -13
  487. package/src/shared/subagent-rows.ts +2 -2
  488. package/src/shared/todos.ts +3 -3
  489. package/src/shared/tool-row.ts +38 -2
  490. package/src/shared/worker-shutdown.ts +1 -1
  491. package/src/shared/workspace/__tests__/git-source.test.ts +3 -3
  492. package/src/shared/workspace/__tests__/provisioner.test.ts +5 -5
  493. package/src/shared/workspace/__tests__/session-provision.test.ts +2 -2
  494. package/src/shared/workspace/__tests__/writeback-coordinator.integration.test.ts +8 -9
  495. package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +25 -25
  496. package/src/shared/workspace/provisioner.ts +1 -1
  497. package/src/shared/workspace/session-provision.ts +1 -1
  498. package/src/shared/workspace/sources/empty.ts +2 -2
  499. package/src/shared/workspace/sources/git.ts +3 -4
  500. package/src/shared/workspace/sources/local-path.ts +5 -6
  501. package/src/shared/workspace/stigmer-link.ts +1 -1
  502. package/src/shared/workspace/types.ts +2 -5
  503. package/src/shared/workspace/writeback-coordinator.ts +23 -12
  504. package/dist/activities/execute-cursor/delta-enricher.d.ts +0 -87
  505. package/dist/activities/execute-cursor/delta-enricher.js +0 -265
  506. package/dist/activities/execute-cursor/delta-enricher.js.map +0 -1
  507. package/dist/activities/execute-cursor/message-translator.js +0 -2040
  508. package/dist/activities/execute-cursor/message-translator.js.map +0 -1
  509. package/dist/activities/execute-cursor/todo-tracker.d.ts +0 -40
  510. package/dist/activities/execute-cursor/todo-tracker.js +0 -80
  511. package/dist/activities/execute-cursor/todo-tracker.js.map +0 -1
  512. package/dist/activities/execute-deep-agent/execution-state.d.ts +0 -54
  513. package/dist/activities/execute-deep-agent/execution-state.js +0 -68
  514. package/dist/activities/execute-deep-agent/execution-state.js.map +0 -1
  515. package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +0 -65
  516. package/dist/activities/execute-deep-agent/status-builder-shared.js +0 -90
  517. package/dist/activities/execute-deep-agent/status-builder-shared.js.map +0 -1
  518. package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +0 -113
  519. package/dist/activities/execute-deep-agent/subagent-tracker.js +0 -400
  520. package/dist/activities/execute-deep-agent/subagent-tracker.js.map +0 -1
  521. package/dist/activities/execute-deep-agent/v3-events.d.ts +0 -102
  522. package/dist/activities/execute-deep-agent/v3-events.js +0 -35
  523. package/dist/activities/execute-deep-agent/v3-events.js.map +0 -1
  524. package/dist/activities/execute-deep-agent/v3-protocol-normalizer.d.ts +0 -15
  525. package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js +0 -235
  526. package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js.map +0 -1
  527. package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +0 -111
  528. package/dist/activities/execute-deep-agent/v3-status-builder.js +0 -457
  529. package/dist/activities/execute-deep-agent/v3-status-builder.js.map +0 -1
  530. package/dist/shared/execution-status-writer.d.ts +0 -31
  531. package/dist/shared/execution-status-writer.js +0 -43
  532. package/dist/shared/execution-status-writer.js.map +0 -1
  533. package/src/activities/execute-cursor/__tests__/delta-enricher.test.ts +0 -310
  534. package/src/activities/execute-cursor/__tests__/message-translator.test.ts +0 -1033
  535. package/src/activities/execute-cursor/__tests__/todo-tracker.test.ts +0 -498
  536. package/src/activities/execute-cursor/delta-enricher.ts +0 -307
  537. package/src/activities/execute-cursor/message-translator.ts +0 -2319
  538. package/src/activities/execute-cursor/todo-tracker.ts +0 -91
  539. package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -148
  540. package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -154
  541. package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +0 -47
  542. package/src/activities/execute-deep-agent/__tests__/v3-protocol-normalizer.test.ts +0 -324
  543. package/src/activities/execute-deep-agent/execution-state.ts +0 -78
  544. package/src/activities/execute-deep-agent/status-builder-shared.ts +0 -142
  545. package/src/activities/execute-deep-agent/subagent-tracker.ts +0 -479
  546. package/src/activities/execute-deep-agent/v3-events.ts +0 -153
  547. package/src/activities/execute-deep-agent/v3-protocol-normalizer.ts +0 -264
  548. package/src/activities/execute-deep-agent/v3-status-builder.ts +0 -565
  549. package/src/shared/execution-status-writer.ts +0 -56
@@ -1,13 +1,34 @@
1
+ /**
2
+ * The native translator's integration arms: raw LangGraph v3 protocol events
3
+ * through `DeepAgentTranslator` and then `TranscriptBuilder`, end to end —
4
+ * the exact path `turn-stream.ts`'s loop takes — asserting the transcript
5
+ * that comes out. Every sequence here is the wire's real shape (the fixtures
6
+ * default to LangGraph's namespaces), so these arms prove that the
7
+ * translator's reading of the engine and the builder's rules compose into
8
+ * the goldens' shapes.
9
+ *
10
+ * What is NOT here: the builder's own rules, driven with
11
+ * `TranscriptEvent`s directly, live in
12
+ * `harness/transcript/__tests__/builder.test.ts`; the translator's own
13
+ * reading of the wire in `translator.test.ts`. This file began as the
14
+ * builder's unit suite when the builder was the native adapter's
15
+ * (`v3-status-builder.test.ts`, #1096) and became what it always was in
16
+ * truth in #1097.
17
+ *
18
+ * In the adapter's folder because the fixtures and the translator are
19
+ * `activities/` modules the direction fence keeps out of `harness/`.
20
+ */
21
+
1
22
  import { describe, it, expect, beforeEach } from "vitest";
2
23
  import { create } from "@bufbuild/protobuf";
3
- import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
4
- import { ApprovalAction, ExecutionPhase, MessageType, ToolCallStatus, ToolKind, TodoStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
24
+ import { type AgentExecutionStatus, AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
25
+ import { ApprovalAction, ApprovalPolicySource, ExecutionPhase, MessageType, ToolCallStatus, ToolKind, TodoStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
5
26
  import { AgentMessageSchema, ToolCallSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
6
27
  import { ExecutionArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
7
28
  import { WorkspaceWriteBackSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/writeback_pb";
8
- import { V3StatusBuilder } from "../v3-status-builder.js";
9
- import { normalize } from "../v3-protocol-normalizer.js";
10
- import type { ApprovalPolicyProvider } from "../status-builder-shared.js";
29
+ import { TranscriptBuilder } from "../../../harness/transcript/builder.js";
30
+ import { DeepAgentTranslator } from "../translator.js";
31
+ import type { DeepAgentGateState } from "../turn-setup.js";
11
32
  import type { MergedToolPolicy } from "../../../shared/approval-policy.js";
12
33
  import {
13
34
  resetSeq,
@@ -20,69 +41,52 @@ import {
20
41
  makeToolError,
21
42
  makeToolCallArgDelta,
22
43
  makeToolOutputDelta,
23
- makeUsageEvent,
24
44
  } from "../__test-utils__/v3-event-fixtures.js";
25
45
  import type { V3ProtocolEvent } from "../v3-event-recorder.js";
26
46
 
27
47
  /**
28
- * What the builder REPORTED through `onUsage`, summed by the test the way an
29
- * accountant would (the builder itself sums nothing and writes no
30
- * `streamingUsage`; the runtime does, priced, through the sink).
48
+ * A builder over an empty status. Usage never reaches the builder:
49
+ * the loop reads it off the wire through `usageOf` and prices it into the
50
+ * sink — `turn-stream.test.ts` pins that; `translator.test.ts` pins what
51
+ * `usageOf` reads. The `usage:` fields the sequences below still
52
+ * carry are the wire's real shape and are ignored by everything here.
31
53
  */
32
- interface ReportedUsage {
33
- inputTokens: number;
34
- outputTokens: number;
35
- cacheReadTokens: number;
36
- cacheWriteTokens: number;
37
- turnCount: number;
54
+ /** A builder and the status it builds into: the test writes through `sb` and reads `status`, as production reads `TurnSink.status`. */
55
+ function makeBuilder(): { sb: TranscriptBuilder; status: AgentExecutionStatus } {
56
+ const status = create(AgentExecutionStatusSchema, {});
57
+ return { sb: new TranscriptBuilder("exec-test", status), status };
38
58
  }
39
59
 
40
- const reportedByBuilder = new WeakMap<V3StatusBuilder, ReportedUsage>();
41
-
42
- function makeBuilder(): V3StatusBuilder {
43
- const reported: ReportedUsage = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, turnCount: 0 };
44
- const sb = new V3StatusBuilder("exec-test", create(AgentExecutionStatusSchema, {}), {
45
- onUsage: (usage) => {
46
- reported.inputTokens += usage.input_tokens ?? 0;
47
- reported.outputTokens += usage.output_tokens ?? 0;
48
- reported.cacheReadTokens += usage.input_token_details?.cache_read ?? 0;
49
- reported.cacheWriteTokens += usage.input_token_details?.cache_creation ?? 0;
50
- reported.turnCount += 1;
51
- },
52
- });
53
- reportedByBuilder.set(sb, reported);
54
- return sb;
55
- }
56
-
57
- function reportedUsage(sb: V3StatusBuilder): ReportedUsage {
58
- return reportedByBuilder.get(sb)!;
59
- }
60
-
61
- function feedAll(sb: V3StatusBuilder, events: V3ProtocolEvent[]): void {
60
+ /**
61
+ * Feed raw v3 events through the translator (over the given gate posture, or
62
+ * none) into the builder — the production path, `turn-stream.ts`'s loop.
63
+ */
64
+ function feedAll(sb: TranscriptBuilder, events: V3ProtocolEvent[], gate: DeepAgentGateState | null = null): void {
65
+ const translator = new DeepAgentTranslator(gate);
62
66
  for (const raw of events) {
63
- for (const e of normalize(raw)) {
64
- sb.processEvent(e);
67
+ for (const e of translator.translate(raw)) {
68
+ sb.apply(e);
65
69
  }
66
70
  }
67
71
  }
68
72
 
69
73
  beforeEach(() => resetSeq());
70
74
 
71
- describe("V3StatusBuilder", () => {
75
+ describe("the native translator through the builder", () => {
72
76
 
73
77
  // ── Initialization ───────────────────────────────────────────────
74
78
 
75
79
  describe("initialization", () => {
76
- it("writes neither phase nor startedAt (the turn runtime owns both; S3 M2a, Q-M2a-7)", () => {
77
- const sb = makeBuilder();
78
- expect(sb.currentStatus.phase).toBe(ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED);
79
- expect(sb.currentStatus.startedAt).toBe("");
80
+ it("writes neither phase nor startedAt (the turn runtime owns both)", () => {
81
+ const { sb, status } = makeBuilder();
82
+ expect(status.phase).toBe(ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED);
83
+ expect(status.startedAt).toBe("");
80
84
  expect(sb.awaitingApproval).toBe(false);
81
85
  });
82
86
 
83
- it("starts with forceNextUpdate = false", () => {
84
- const sb = makeBuilder();
85
- expect(sb.forceNextUpdate).toBe(false);
87
+ it("starts with dirty = false", () => {
88
+ const { sb } = makeBuilder();
89
+ expect(sb.dirty).toBe(false);
86
90
  });
87
91
  });
88
92
 
@@ -90,14 +94,14 @@ describe("V3StatusBuilder", () => {
90
94
  // GOLDEN SEQUENCES
91
95
  //
92
96
  // Same proto assertions as v2 StatusBuilder golden tests, but input
93
- // is v3 ProtocolEvent sequences fed through the normalizer.
97
+ // is v3 ProtocolEvent sequences fed through the translator.
94
98
  // ═══════════════════════════════════════════════════════════════════
95
99
 
96
100
  describe("golden sequences", () => {
97
101
 
98
102
  describe("plain chat — 2-turn text-only conversation", () => {
99
- it("produces 2 AI messages with accumulated usage", () => {
100
- const sb = makeBuilder();
103
+ it("produces 2 AI messages, one per run, each closed by its finish", () => {
104
+ const { sb, status } = makeBuilder();
101
105
  feedAll(sb, [
102
106
  makeMessageStart("run-1"),
103
107
  makeTextDelta("run-1", "Hello, "),
@@ -114,7 +118,6 @@ describe("V3StatusBuilder", () => {
114
118
  }),
115
119
  ]);
116
120
 
117
- const status = sb.currentStatus;
118
121
 
119
122
  expect(status.messages).toHaveLength(2);
120
123
  expect(status.messages[0].type).toBe(MessageType.MESSAGE_AI);
@@ -126,17 +129,13 @@ describe("V3StatusBuilder", () => {
126
129
  expect(status.messages[1].content).toBe("Here is more detail.");
127
130
  expect(status.messages[1].isStreaming).toBe(false);
128
131
 
129
- const usage = reportedUsage(sb);
130
- expect(usage.inputTokens).toBe(130);
131
- expect(usage.outputTokens).toBe(20);
132
- expect(usage.turnCount).toBe(2);
133
- expect(status.streamingUsage, "the runtime owns streamingUsage; the builder reports").toBeUndefined();
132
+ expect(status.streamingUsage, "the runtime owns streamingUsage; the builder never touches it").toBeUndefined();
134
133
  });
135
134
  });
136
135
 
137
136
  describe("Anthropic thinking + text", () => {
138
137
  it("produces THINKING then AI messages with correct turn boundaries", () => {
139
- const sb = makeBuilder();
138
+ const { sb, status } = makeBuilder();
140
139
  feedAll(sb, [
141
140
  makeMessageStart("run-1"),
142
141
  makeReasoningDelta("run-1", "Let me analyze "),
@@ -157,13 +156,14 @@ describe("V3StatusBuilder", () => {
157
156
  }),
158
157
  ]);
159
158
 
160
- const status = sb.currentStatus;
161
159
 
162
160
  expect(status.messages).toHaveLength(3);
163
161
 
164
162
  expect(status.messages[0].type).toBe(MessageType.MESSAGE_THINKING);
165
163
  expect(status.messages[0].content).toBe("Let me analyze this problem carefully.");
166
- expect(status.messages[0].isStreaming).toBe(true);
164
+ // The run's finish closes its thinking with its text (since #1097);
165
+ // until then the THINKING row spun until finalize.
166
+ expect(status.messages[0].isStreaming).toBe(false);
167
167
 
168
168
  expect(status.messages[1].type).toBe(MessageType.MESSAGE_AI);
169
169
  expect(status.messages[1].content).toBe("Based on my analysis, here is the answer.");
@@ -172,18 +172,12 @@ describe("V3StatusBuilder", () => {
172
172
  expect(status.messages[2].type).toBe(MessageType.MESSAGE_AI);
173
173
  expect(status.messages[2].content).toBe("Let me elaborate.");
174
174
  expect(status.messages[2].isStreaming).toBe(false);
175
-
176
- const usage = reportedUsage(sb);
177
- expect(usage.inputTokens).toBe(500);
178
- expect(usage.outputTokens).toBe(120);
179
- expect(usage.cacheReadTokens).toBe(50);
180
- expect(usage.turnCount).toBe(2);
181
175
  });
182
176
  });
183
177
 
184
178
  describe("single tool call — ReAct pattern", () => {
185
179
  it("produces AI message with tool call followed by response", () => {
186
- const sb = makeBuilder();
180
+ const { sb, status } = makeBuilder();
187
181
  feedAll(sb, [
188
182
  makeMessageStart("run-1"),
189
183
  makeTextDelta("run-1", "I'll read the file for you."),
@@ -199,7 +193,6 @@ describe("V3StatusBuilder", () => {
199
193
  }),
200
194
  ]);
201
195
 
202
- const status = sb.currentStatus;
203
196
 
204
197
  expect(status.messages).toHaveLength(2);
205
198
 
@@ -224,17 +217,12 @@ describe("V3StatusBuilder", () => {
224
217
  expect(secondMsg.type).toBe(MessageType.MESSAGE_AI);
225
218
  expect(secondMsg.content).toBe("The file contains a main function that logs 'hello'.");
226
219
  expect(secondMsg.toolCalls).toHaveLength(0);
227
-
228
- const usage = reportedUsage(sb);
229
- expect(usage.inputTokens).toBe(300);
230
- expect(usage.outputTokens).toBe(35);
231
- expect(usage.turnCount).toBe(2);
232
220
  });
233
221
  });
234
222
 
235
223
  describe("tool error — failed tool call", () => {
236
224
  it("marks tool as FAILED with error string", () => {
237
- const sb = makeBuilder();
225
+ const { sb, status } = makeBuilder();
238
226
  feedAll(sb, [
239
227
  makeMessageStart("run-1"),
240
228
  makeTextDelta("run-1", "I'll write to the file."),
@@ -250,7 +238,6 @@ describe("V3StatusBuilder", () => {
250
238
  }),
251
239
  ]);
252
240
 
253
- const status = sb.currentStatus;
254
241
 
255
242
  expect(status.messages).toHaveLength(2);
256
243
 
@@ -265,7 +252,7 @@ describe("V3StatusBuilder", () => {
265
252
 
266
253
  describe("multi-tool concurrent", () => {
267
254
  it("handles two parallel tools on one message with out-of-order completion", () => {
268
- const sb = makeBuilder();
255
+ const { sb, status } = makeBuilder();
269
256
  feedAll(sb, [
270
257
  makeMessageStart("run-1"),
271
258
  makeTextDelta("run-1", "I'll look up both files."),
@@ -279,7 +266,6 @@ describe("V3StatusBuilder", () => {
279
266
  makeToolFinished("toolu_1", "content of a"),
280
267
  ]);
281
268
 
282
- const status = sb.currentStatus;
283
269
 
284
270
  const msg = status.messages[0];
285
271
  expect(msg.toolCalls).toHaveLength(2);
@@ -293,10 +279,16 @@ describe("V3StatusBuilder", () => {
293
279
  });
294
280
  });
295
281
 
296
- describe("HITL approval gate", () => {
297
- it("sets WAITING_FOR_APPROVAL on tool with approval policy", () => {
298
- const sb = makeBuilder();
299
- sb.setApprovalProvider({
282
+ describe("a gated MCP tool that STARTED", () => {
283
+ // On native a held call never starts: LangGraph's interrupt() runs
284
+ // before the tool handler, so no tool_started arrives and the gate's
285
+ // hold reaches the transcript as approval_proposed from the post-stream
286
+ // seed. A tool_started that does arrive is the engine's word that
287
+ // the call was authorized, so the row is RUNNING and attributed — never
288
+ // WAITING at creation, whatever the policy says.
289
+ it("is RUNNING and attributed to its server; nothing waits", () => {
290
+ const { sb, status } = makeBuilder();
291
+ const gate = gateWith({
300
292
  policies: new Map([
301
293
  ["my-server/dangerous_tool", {
302
294
  toolName: "dangerous_tool",
@@ -307,8 +299,7 @@ describe("V3StatusBuilder", () => {
307
299
  }],
308
300
  ]),
309
301
  toolServerMap: new Map([["dangerous_tool", "my-server"]]),
310
- globalBypass: false,
311
- } as ApprovalPolicyProvider);
302
+ });
312
303
 
313
304
  feedAll(sb, [
314
305
  makeMessageStart("run-1"),
@@ -318,67 +309,23 @@ describe("V3StatusBuilder", () => {
318
309
  usage: { input_tokens: 100, output_tokens: 10 },
319
310
  }),
320
311
  makeToolStarted("toolu_1", "dangerous_tool", { path: "/etc/shadow" }),
321
- ]);
312
+ ], gate);
322
313
 
323
- const status = sb.currentStatus;
324
- expect(sb.awaitingApproval, "a fact for the caller, never a phase write").toBe(true);
314
+ expect(sb.awaitingApproval, "no tool_started parks a row").toBe(false);
325
315
  expect(status.phase).toBe(ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED);
326
316
 
327
317
  const tc = status.messages[0].toolCalls[0];
328
- expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
329
- expect(tc.requiresApproval).toBe(true);
330
- expect(tc.approvalMessage).toBe("This tool will modify /etc/shadow");
318
+ expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
319
+ expect(tc.requiresApproval).toBe(false);
331
320
  expect(tc.mcpServerSlug).toBe("my-server");
332
- expect(tc.approvalRequestedAt).toBeTruthy();
333
- expect(tc.argsPreview).toBeTruthy();
334
- });
335
- });
336
-
337
- describe("usage accumulation — 3-turn with cache", () => {
338
- it("accumulates input/output/cache tokens across turns", () => {
339
- const sb = makeBuilder();
340
- feedAll(sb, [
341
- makeMessageStart("run-1"),
342
- makeTextDelta("run-1", "Turn 1"),
343
- makeMessageFinish("run-1", {
344
- usage: {
345
- input_tokens: 100,
346
- output_tokens: 20,
347
- input_token_details: { cache_creation: 80 },
348
- },
349
- }),
350
- makeMessageStart("run-2"),
351
- makeTextDelta("run-2", "Turn 2"),
352
- makeMessageFinish("run-2", {
353
- usage: {
354
- input_tokens: 200,
355
- output_tokens: 30,
356
- input_token_details: { cache_read: 50 },
357
- },
358
- }),
359
- makeMessageStart("run-3"),
360
- makeTextDelta("run-3", "Turn 3"),
361
- makeMessageFinish("run-3", {
362
- usage: {
363
- input_tokens: 300,
364
- output_tokens: 10,
365
- input_token_details: { cache_read: 100 },
366
- },
367
- }),
368
- ]);
369
-
370
- const usage = reportedUsage(sb);
371
- expect(usage.inputTokens).toBe(600);
372
- expect(usage.outputTokens).toBe(60);
373
- expect(usage.cacheWriteTokens).toBe(80);
374
- expect(usage.cacheReadTokens).toBe(150);
375
- expect(usage.turnCount).toBe(3);
321
+ expect(tc.approvalPolicySource, "the policy's layer still attributes the row").toBe(ApprovalPolicySource.CLASSIFIER_DEFAULT);
322
+ expect(tc.approvalRequestedAt).toBe("");
376
323
  });
377
324
  });
378
325
 
379
326
  describe("namespace isolation — parent + subagent", () => {
380
327
  it("creates separate message trees per namespace", () => {
381
- const sb = makeBuilder();
328
+ const { sb, status } = makeBuilder();
382
329
  const subNs = ["subagent:worker-1"];
383
330
 
384
331
  feedAll(sb, [
@@ -412,7 +359,6 @@ describe("V3StatusBuilder", () => {
412
359
  }),
413
360
  ]);
414
361
 
415
- const status = sb.currentStatus;
416
362
 
417
363
  expect(status.messages).toHaveLength(4);
418
364
 
@@ -432,11 +378,6 @@ describe("V3StatusBuilder", () => {
432
378
 
433
379
  expect(status.messages[3].type).toBe(MessageType.MESSAGE_AI);
434
380
  expect(status.messages[3].content).toBe("The research is complete.");
435
-
436
- const usage = reportedUsage(sb);
437
- expect(usage.inputTokens).toBe(650);
438
- expect(usage.outputTokens).toBe(50);
439
- expect(usage.turnCount).toBe(3);
440
381
  });
441
382
  });
442
383
  });
@@ -445,7 +386,7 @@ describe("V3StatusBuilder", () => {
445
386
 
446
387
  describe("v3-specific", () => {
447
388
  it("keys tool calls by callId, not runId", () => {
448
- const sb = makeBuilder();
389
+ const { sb, status } = makeBuilder();
449
390
  feedAll(sb, [
450
391
  makeMessageStart("run-1"),
451
392
  makeTextDelta("run-1", "Reading file."),
@@ -454,29 +395,12 @@ describe("V3StatusBuilder", () => {
454
395
  makeToolFinished("toolu_abc123", "contents"),
455
396
  ]);
456
397
 
457
- const tc = sb.currentStatus.messages[0].toolCalls[0];
398
+ const tc = status.messages[0].toolCalls[0];
458
399
  expect(tc.id).toBe("toolu_abc123");
459
400
  });
460
401
 
461
- it("does not double-count usage from standalone usage events", () => {
462
- const sb = makeBuilder();
463
- feedAll(sb, [
464
- makeMessageStart("run-1"),
465
- makeTextDelta("run-1", "hi"),
466
- makeUsageEvent("run-1", { input_tokens: 50, output_tokens: 10 }),
467
- makeMessageFinish("run-1", {
468
- usage: { input_tokens: 50, output_tokens: 10 },
469
- }),
470
- ]);
471
-
472
- const usage = reportedUsage(sb);
473
- expect(usage.inputTokens).toBe(50);
474
- expect(usage.outputTokens).toBe(10);
475
- expect(usage.turnCount).toBe(1);
476
- });
477
-
478
- it("accumulates tool_call_arg_delta into ToolCall.args", () => {
479
- const sb = makeBuilder();
402
+ it("accumulates tool_arg_delta into ToolCall.args", () => {
403
+ const { sb, status } = makeBuilder();
480
404
  feedAll(sb, [
481
405
  makeMessageStart("run-1"),
482
406
  makeTextDelta("run-1", "Calling tool."),
@@ -487,12 +411,12 @@ describe("V3StatusBuilder", () => {
487
411
  makeToolFinished("toolu_1", "ok"),
488
412
  ]);
489
413
 
490
- const tc = sb.currentStatus.messages[0].toolCalls[0];
414
+ const tc = status.messages[0].toolCalls[0];
491
415
  expect(tc.args).toEqual({ thought: "test" });
492
416
  });
493
417
 
494
418
  it("handles tool_output_delta by accumulating into result", () => {
495
- const sb = makeBuilder();
419
+ const { sb, status } = makeBuilder();
496
420
  feedAll(sb, [
497
421
  makeMessageStart("run-1"),
498
422
  makeTextDelta("run-1", "Running."),
@@ -503,32 +427,32 @@ describe("V3StatusBuilder", () => {
503
427
  makeToolFinished("toolu_1", " final"),
504
428
  ]);
505
429
 
506
- const tc = sb.currentStatus.messages[0].toolCalls[0];
430
+ const tc = status.messages[0].toolCalls[0];
507
431
  expect(tc.result).toBe(" final");
508
432
  });
509
433
 
510
- it("sets forceNextUpdate on tool start and tool finish", () => {
511
- const sb = makeBuilder();
434
+ it("sets dirty on tool start and tool finish", () => {
435
+ const { sb } = makeBuilder();
512
436
 
513
437
  feedAll(sb, [
514
438
  makeMessageStart("run-1"),
515
439
  makeTextDelta("run-1", "x"),
516
440
  makeMessageFinish("run-1", { usage: { input_tokens: 1, output_tokens: 1 } }),
517
441
  ]);
518
- expect(sb.forceNextUpdate).toBe(false);
442
+ expect(sb.dirty).toBe(false);
519
443
 
520
444
  feedAll(sb, [makeToolStarted("toolu_1", "read", {})]);
521
- expect(sb.forceNextUpdate).toBe(true);
522
- sb.clearForceFlag();
445
+ expect(sb.dirty).toBe(true);
446
+ sb.markPersisted();
523
447
 
524
448
  feedAll(sb, [makeToolFinished("toolu_1", "done")]);
525
- expect(sb.forceNextUpdate).toBe(true);
449
+ expect(sb.dirty).toBe(true);
526
450
  });
527
451
 
528
452
  it("ignores tool-finished for unknown callId", () => {
529
- const sb = makeBuilder();
453
+ const { sb, status } = makeBuilder();
530
454
  feedAll(sb, [makeToolFinished("unknown_toolu", "some result")]);
531
- expect(sb.currentStatus.messages).toHaveLength(0);
455
+ expect(status.messages).toHaveLength(0);
532
456
  });
533
457
  });
534
458
 
@@ -539,7 +463,7 @@ describe("V3StatusBuilder", () => {
539
463
 
540
464
  describe("todo extraction", () => {
541
465
  it("projects a completed write_todos into status.todos", () => {
542
- const sb = makeBuilder();
466
+ const { sb, status } = makeBuilder();
543
467
  feedAll(sb, [
544
468
  makeMessageStart("run-1"),
545
469
  makeTextDelta("run-1", "Planning the build."),
@@ -553,17 +477,17 @@ describe("V3StatusBuilder", () => {
553
477
  makeToolFinished("todo-1", "Todos updated"),
554
478
  ]);
555
479
 
556
- const todos = sb.currentStatus.todos;
480
+ const todos = status.todos;
557
481
  expect(Object.keys(todos)).toEqual(["todo-0", "todo-1"]);
558
482
  expect(todos["todo-0"].content).toBe("Step one");
559
483
  expect(todos["todo-0"].status).toBe(TodoStatus.TODO_IN_PROGRESS);
560
484
  expect(todos["todo-1"].content).toBe("Step two");
561
485
  expect(todos["todo-1"].status).toBe(TodoStatus.TODO_PENDING);
562
- expect(sb.forceNextUpdate).toBe(true);
486
+ expect(sb.dirty).toBe(true);
563
487
  });
564
488
 
565
489
  it("keeps the write_todos ToolCall in messages stamped ToolKind.TODO", () => {
566
- const sb = makeBuilder();
490
+ const { sb, status } = makeBuilder();
567
491
  feedAll(sb, [
568
492
  makeToolStarted("todo-1", "write_todos", {
569
493
  todos: [{ content: "Step one", status: "pending" }],
@@ -571,28 +495,28 @@ describe("V3StatusBuilder", () => {
571
495
  makeToolFinished("todo-1", "Todos updated"),
572
496
  ]);
573
497
 
574
- const toolCalls = sb.currentStatus.messages.flatMap((m) => m.toolCalls);
498
+ const toolCalls = status.messages.flatMap((m) => m.toolCalls);
575
499
  const tc = toolCalls.find((t) => t.name === "write_todos");
576
500
  expect(tc).toBeDefined();
577
501
  expect(tc!.toolKind).toBe(ToolKind.TODO);
578
502
  });
579
503
 
580
504
  it("does not project until the call completes", () => {
581
- const sb = makeBuilder();
505
+ const { sb, status } = makeBuilder();
582
506
  feedAll(sb, [
583
507
  makeToolStarted("todo-1", "write_todos", {
584
508
  todos: [{ content: "Step one", status: "pending" }],
585
509
  }),
586
510
  ]);
587
511
  // Tool started but not finished — the state Command has not run yet.
588
- expect(Object.keys(sb.currentStatus.todos)).toHaveLength(0);
512
+ expect(Object.keys(status.todos)).toHaveLength(0);
589
513
 
590
514
  feedAll(sb, [makeToolFinished("todo-1", "Todos updated")]);
591
- expect(Object.keys(sb.currentStatus.todos)).toHaveLength(1);
515
+ expect(Object.keys(status.todos)).toHaveLength(1);
592
516
  });
593
517
 
594
518
  it("full-replaces the map on a subsequent write_todos", () => {
595
- const sb = makeBuilder();
519
+ const { sb, status } = makeBuilder();
596
520
  feedAll(sb, [
597
521
  makeToolStarted("todo-1", "write_todos", {
598
522
  todos: [
@@ -607,37 +531,33 @@ describe("V3StatusBuilder", () => {
607
531
  makeToolFinished("todo-2", "ok"),
608
532
  ]);
609
533
 
610
- const todos = sb.currentStatus.todos;
534
+ const todos = status.todos;
611
535
  expect(Object.keys(todos)).toEqual(["todo-0"]);
612
536
  expect(todos["todo-0"].content).toBe("Step two");
613
537
  expect(todos["todo-0"].status).toBe(TodoStatus.TODO_COMPLETED);
614
538
  });
615
539
 
616
- it("reports a sub-agent's usage through the same hook as the parent's (S3 M2b, Q-M2b-1)", () => {
617
- const sb = makeBuilder();
540
+ it("routes a sub-agent's message to its own row, never the parent's", () => {
541
+ const { sb, status } = makeBuilder();
618
542
  const subNs = ["tools:task-1", "model_request:sub-run"];
619
543
  feedAll(sb, [
620
- ...[makeMessageStart("parent-1"), makeTextDelta("parent-1", "Delegating."), makeMessageFinish("parent-1", { usage: { input_tokens: 1_000, output_tokens: 10 } })],
544
+ ...[makeMessageStart("parent-1"), makeTextDelta("parent-1", "Delegating."), makeMessageFinish("parent-1")],
621
545
  makeToolStarted("task-1", "task", { subagent_type: "worker", description: "delegate" }),
622
546
  makeMessageStart("sub-run", { namespace: subNs }),
623
547
  makeTextDelta("sub-run", "Working on it.", { namespace: subNs }),
624
- makeMessageFinish("sub-run", { namespace: subNs, usage: { input_tokens: 600, output_tokens: 12 } }),
548
+ makeMessageFinish("sub-run", { namespace: subNs }),
625
549
  ]);
626
550
 
627
- const usage = reportedUsage(sb);
628
- expect(usage.inputTokens, "the parent's 1,000 and the sub-agent's 600").toBe(1_600);
629
- expect(usage.outputTokens).toBe(22);
630
- expect(usage.turnCount).toBe(2);
631
- // The transcript still routes the sub-agent's message to its own row, never the parent's.
632
- expect(sb.currentStatus.messages.map((m) => m.content)).toEqual(["Delegating."]);
633
- expect(sb.currentStatus.subAgentExecutions).toHaveLength(1);
551
+ expect(status.messages.map((m) => m.content)).toEqual(["Delegating."]);
552
+ expect(status.subAgentExecutions).toHaveLength(1);
553
+ expect(status.subAgentExecutions[0].messages.map((m) => m.content)).toEqual(["Working on it."]);
634
554
  });
635
555
 
636
556
  it("does not project a sub-agent's write_todos into parent status.todos", () => {
637
- const sb = makeBuilder();
557
+ const { sb, status } = makeBuilder();
638
558
  const subNs = ["tools:task-1", "tools:sub-todo"];
639
559
  feedAll(sb, [
640
- // Register a sub-agent so its namespace routes to the SubAgentTracker.
560
+ // Open a sub-agent so its namespace routes to the sub-agent's own transcript scope.
641
561
  makeToolStarted("task-1", "task", {
642
562
  subagent_type: "worker",
643
563
  description: "delegate",
@@ -652,20 +572,29 @@ describe("V3StatusBuilder", () => {
652
572
  makeToolFinished("sub-todo", "ok", { namespace: subNs }),
653
573
  ]);
654
574
 
655
- expect(Object.keys(sb.currentStatus.todos)).toHaveLength(0);
575
+ expect(Object.keys(status.todos)).toHaveLength(0);
656
576
  });
657
577
  });
658
578
  // ═══════════════════════════════════════════════════════════════════
659
579
  // Carried from the v2 StatusBuilder's suite when that builder retired
660
- // with the v2 stream (S3 M2b, Q-S3-1 / Q-M2b-5): every rule below is the
580
+ // with the v2 stream (#1096): every rule below is the
661
581
  // builder's own — how a policy decides a row's status, what the row
662
582
  // carries, how a seeded row is resolved, how artifacts and write-backs
663
583
  // are upserted — and had no v3 twin. The sanitizer's own rules
664
584
  // (redaction, truncation) are `shared/__tests__/args-preview.test.ts`'s.
665
585
  // ═══════════════════════════════════════════════════════════════════
666
586
 
667
- function providerWith(overrides: Partial<ApprovalPolicyProvider> = {}): ApprovalPolicyProvider {
668
- return { policies: new Map(), toolServerMap: new Map(), globalBypass: false, ...overrides };
587
+ /** A gate posture for the translator: no policies, no leases, attended, not bypassed — unless overridden. */
588
+ function gateWith(overrides: Partial<DeepAgentGateState> = {}): DeepAgentGateState {
589
+ return {
590
+ policies: new Map(),
591
+ toolServerMap: new Map(),
592
+ leasedCategories: new Set(),
593
+ globalBypass: false,
594
+ unattended: false,
595
+ unattendedSkips: new Set(),
596
+ ...overrides,
597
+ };
669
598
  }
670
599
 
671
600
  function policyFor(serverSlug: string, toolName: string, message = "Approve?"): [string, MergedToolPolicy] {
@@ -675,119 +604,104 @@ describe("V3StatusBuilder", () => {
675
604
  ];
676
605
  }
677
606
 
678
- /** A text turn, then one tool start, on a builder with the given provider. */
679
- function firstToolCallUnder(provider: ApprovalPolicyProvider, toolName: string, input: Record<string, unknown> = {}) {
680
- const sb = makeBuilder();
681
- sb.setApprovalProvider(provider);
607
+ /** A text turn, then one tool start, through a translator over the given gate posture. */
608
+ function firstToolCallUnder(gate: DeepAgentGateState, toolName: string, input: Record<string, unknown> = {}) {
609
+ const { sb, status } = makeBuilder();
682
610
  feedAll(sb, [
683
611
  makeMessageStart("run-1"),
684
612
  makeTextDelta("run-1", "Working."),
685
613
  makeMessageFinish("run-1", { usage: { input_tokens: 10, output_tokens: 5 } }),
686
614
  makeToolStarted("toolu_1", toolName, input),
687
- ]);
688
- return { sb, tc: sb.currentStatus.messages[0].toolCalls[0] };
615
+ ], gate);
616
+ return { sb, tc: status.messages[0].toolCalls[0] };
689
617
  }
690
618
 
691
- describe("approval provider: what decides a row's status", () => {
692
- it("a policy that requires approval leaves the row WAITING_APPROVAL with its message and server", () => {
619
+ describe("attribution and provenance on tool_started", () => {
620
+ // The translator answers WHICH server a tool belongs to and WHICH policy
621
+ // layer governs it; it never answers whether the gate holds the call —
622
+ // on native a held call never starts (the interrupt precedes the
623
+ // handler), so every started call is RUNNING and nothing here waits.
624
+ // The decision itself, `resolveToolApproval`, has its own arms in
625
+ // `shared/__tests__/approval-policy.test.ts`; the gate's use of it in
626
+ // `middleware/__tests__/approval-gate.test.ts`.
627
+
628
+ it("an MCP tool with a gating policy STILL runs when it started, attributed to its server and the policy's layer", () => {
693
629
  const [key, policy] = policyFor("github", "create_issue");
694
630
  const { sb, tc } = firstToolCallUnder(
695
- providerWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["create_issue", "github"]]) }),
631
+ gateWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["create_issue", "github"]]) }),
696
632
  "create_issue",
697
633
  { title: "Bug fix" },
698
634
  );
699
- expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
700
- expect(tc.requiresApproval).toBe(true);
701
- expect(tc.approvalMessage).toBe("Approve?");
702
- expect(tc.mcpServerSlug).toBe("github");
703
- expect(tc.approvalRequestedAt, "stamped when approval is required").toContain("T");
704
- expect(sb.awaitingApproval).toBe(true);
705
- });
706
-
707
- it("the global bypass (spec.auto_approve_all) runs a gated tool without approval", () => {
708
- const [key, policy] = policyFor("github", "delete_repo");
709
- const { sb, tc } = firstToolCallUnder(
710
- providerWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["delete_repo", "github"]]), globalBypass: true }),
711
- "delete_repo",
712
- );
713
635
  expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
714
636
  expect(tc.requiresApproval).toBe(false);
637
+ expect(tc.mcpServerSlug).toBe("github");
638
+ expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.CLASSIFIER_DEFAULT);
639
+ expect(tc.policyEngineVersion).toBe("phase-7");
715
640
  expect(sb.awaitingApproval).toBe(false);
716
641
  });
717
642
 
718
- it("the global bypass still attributes the row to its MCP server", () => {
643
+ it("the global bypass (spec.auto_approve_all) attributes every row to it", () => {
644
+ const [key, policy] = policyFor("github", "delete_repo");
719
645
  const { tc } = firstToolCallUnder(
720
- providerWith({ toolServerMap: new Map([["echo", "test-mcp-server"]]), globalBypass: true }),
721
- "echo",
722
- { input: "hello" },
646
+ gateWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["delete_repo", "github"]]), globalBypass: true }),
647
+ "delete_repo",
723
648
  );
724
649
  expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
725
- expect(tc.mcpServerSlug).toBe("test-mcp-server");
650
+ expect(tc.mcpServerSlug, "the bypass still attributes the row to its MCP server").toBe("github");
651
+ expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.AUTO_APPROVE_ALL);
726
652
  });
727
653
 
728
- it("a tool with no policy runs, attributed to its server", () => {
729
- const { tc } = firstToolCallUnder(providerWith({ toolServerMap: new Map([["read", "filesystem"]]) }), "read");
730
- expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
654
+ it("an MCP tool with no policy is attributed to its server, classifier_default", () => {
655
+ const { tc } = firstToolCallUnder(gateWith({ toolServerMap: new Map([["read", "filesystem"]]) }), "read");
731
656
  expect(tc.mcpServerSlug).toBe("filesystem");
657
+ expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.CLASSIFIER_DEFAULT);
732
658
  });
733
659
 
734
- it("a policy whose tool is not in the server map does not gate (the policy key needs the server)", () => {
660
+ it("a tool the server map does not know carries no server (the policy key needs the server)", () => {
735
661
  const [key, policy] = policyFor("github", "create_pr");
736
- const { tc } = firstToolCallUnder(providerWith({ policies: new Map([[key, policy]]), toolServerMap: new Map() }), "create_pr");
737
- expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
662
+ const { tc } = firstToolCallUnder(gateWith({ policies: new Map([[key, policy]]), toolServerMap: new Map() }), "create_pr");
663
+ expect(tc.mcpServerSlug).toBe("");
664
+ expect(tc.approvalPolicySource, "an unknown built-in is governed by no layer").toBe(ApprovalPolicySource.UNSPECIFIED);
665
+ expect(tc.policyEngineVersion).toBe("");
738
666
  });
739
667
 
740
- it("resolves {{args.field}} placeholders in the approval message", () => {
741
- const [key, policy] = policyFor("github", "create_issue", "Create issue '{{args.title}}' in {{args.repo}}?");
742
- const { tc } = firstToolCallUnder(
743
- providerWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["create_issue", "github"]]) }),
744
- "create_issue",
745
- { title: "Fix crash", repo: "stigmer/stigmer" },
746
- );
747
- expect(tc.approvalMessage).toBe("Create issue 'Fix crash' in stigmer/stigmer?");
668
+ it("a mutating built-in that started is attributed builtin_category — the gate let it flow (capture mode, or a resume)", () => {
669
+ const { sb, tc } = firstToolCallUnder(gateWith(), "execute", { command: "rm -rf build" });
670
+ expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
671
+ expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.BUILTIN_CATEGORY);
672
+ expect(sb.awaitingApproval).toBe(false);
748
673
  });
749
- });
750
674
 
751
- describe("args preview: when the builder writes one", () => {
752
- it("a gated row carries a sanitized preview; an ungated row carries none", () => {
753
- const [key, policy] = policyFor("db", "connect");
754
- const gated = firstToolCallUnder(
755
- providerWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["connect", "db"]]) }),
756
- "connect",
757
- { host: "localhost", password: "super-secret" },
758
- );
759
- const preview = JSON.parse(gated.tc.argsPreview) as Record<string, string>;
760
- expect(preview.host).toBe("localhost");
761
- expect(preview.password, "the shared sanitizer redacts secret keys").toBe("[REDACTED]");
675
+ it("a leased built-in category is attributed to its lease", () => {
676
+ const { tc } = firstToolCallUnder(gateWith({ leasedCategories: new Set(["shell"]) }), "execute", { command: "ls" });
677
+ expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.APPROVAL_LEASE);
678
+ });
762
679
 
763
- const ungated = firstToolCallUnder(providerWith(), "read", { path: "/foo" });
764
- expect(ungated.tc.argsPreview).toBe("");
680
+ it("a read-only built-in is governed by no layer: UNSPECIFIED, no engine version", () => {
681
+ const { tc } = firstToolCallUnder(gateWith(), "read_file", { path: "/x" });
682
+ expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.UNSPECIFIED);
683
+ expect(tc.policyEngineVersion).toBe("");
765
684
  });
766
685
 
767
- it("an unserializable input leaves the preview empty instead of failing the row", () => {
768
- const [key, policy] = policyFor("api", "call");
769
- const circular: Record<string, unknown> = {};
770
- circular.self = circular;
771
- const { tc } = firstToolCallUnder(
772
- providerWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["call", "api"]]) }),
773
- "call",
774
- circular,
775
- );
776
- expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
777
- expect(tc.argsPreview).toBe("");
686
+ it("with no gate posture at all (a turn the unit arms drive bare) nothing is attributed", () => {
687
+ const { sb, status } = makeBuilder();
688
+ feedAll(sb, [makeToolStarted("toolu_1", "execute", { command: "ls" })]);
689
+ const tc = status.messages[0].toolCalls[0];
690
+ expect(tc.mcpServerSlug).toBe("");
691
+ expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.UNSPECIFIED);
778
692
  });
779
693
  });
780
694
 
781
695
  describe("tool rows without a preceding text turn, and interleaving", () => {
782
696
  it("a tool start with no prior text creates the AI message that carries it", () => {
783
- const sb = makeBuilder();
697
+ const { sb, status } = makeBuilder();
784
698
  feedAll(sb, [makeToolStarted("orphan", "read", {})]);
785
- expect(sb.currentStatus.messages).toHaveLength(1);
786
- expect(sb.currentStatus.messages[0].toolCalls[0].name).toBe("read");
699
+ expect(status.messages).toHaveLength(1);
700
+ expect(status.messages[0].toolCalls[0].name).toBe("read");
787
701
  });
788
702
 
789
703
  it("interleaved start/finish pairs settle each row independently", () => {
790
- const sb = makeBuilder();
704
+ const { sb, status } = makeBuilder();
791
705
  feedAll(sb, [
792
706
  makeMessageStart("run-1"),
793
707
  makeTextDelta("run-1", "working"),
@@ -797,29 +711,29 @@ describe("V3StatusBuilder", () => {
797
711
  makeToolStarted("toolu_b", "write", {}),
798
712
  makeToolFinished("toolu_b", "done"),
799
713
  ]);
800
- const rows = sb.currentStatus.messages.flatMap((m) => m.toolCalls);
714
+ const rows = status.messages.flatMap((m) => m.toolCalls);
801
715
  expect(rows.map((r) => r.status)).toEqual([ToolCallStatus.TOOL_CALL_COMPLETED, ToolCallStatus.TOOL_CALL_COMPLETED]);
802
716
  });
803
717
  });
804
718
 
805
719
  describe("a handler that throws", () => {
806
- it("is logged with the execution, the event kind and its seq, and never escapes processEvent", () => {
807
- const sb = makeBuilder();
720
+ it("is logged with the execution and the event kind, and never escapes apply", () => {
721
+ const { sb } = makeBuilder();
808
722
  const errors: string[] = [];
809
723
  const original = console.error;
810
724
  console.error = (msg: unknown) => { errors.push(String(msg)); };
811
725
  try {
812
- const poisoned = normalize(makeToolFinished("toolu_x", "ok"))[0]!;
813
- // A getter that throws where the handler reads the output.
814
- const event = { ...poisoned, get output(): unknown { throw new Error("property access failed"); } };
815
- expect(() => sb.processEvent(event as typeof poisoned)).not.toThrow();
726
+ const poisoned = new DeepAgentTranslator(null).translate(makeToolFinished("toolu_x", "ok"))[0]!;
727
+ // A getter that throws where the handler reads the result.
728
+ const event = { ...poisoned, get result(): string { throw new Error("property access failed"); } };
729
+ expect(() => sb.apply(event as typeof poisoned)).not.toThrow();
816
730
  } finally {
817
731
  console.error = original;
818
732
  }
819
733
  expect(errors).toHaveLength(1);
820
734
  expect(errors[0]).toContain("exec-test");
821
735
  expect(errors[0]).toContain("kind=tool_finished");
822
- expect(errors[0]).toMatch(/seq=\d+/);
736
+ expect(errors[0]).toContain("property access failed");
823
737
  });
824
738
  });
825
739
 
@@ -846,9 +760,9 @@ describe("V3StatusBuilder", () => {
846
760
  }),
847
761
  ],
848
762
  });
849
- const sb = new V3StatusBuilder("exec-resume", status);
763
+ const sb = new TranscriptBuilder("exec-resume", status);
850
764
  const [key, policy] = policyFor("my-server", "dangerous_tool", "Execute dangerous_tool");
851
- sb.setApprovalProvider(providerWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["dangerous_tool", "my-server"]]) }));
765
+ const gate = gateWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["dangerous_tool", "my-server"]]) });
852
766
 
853
767
  // The durable checkpoint re-emits the SAME tool_call_id now that approval is granted.
854
768
  feedAll(sb, [
@@ -857,60 +771,60 @@ describe("V3StatusBuilder", () => {
857
771
  makeMessageStart("llm-after"),
858
772
  makeTextDelta("llm-after", "All done."),
859
773
  makeMessageFinish("llm-after", { usage: { input_tokens: 1, output_tokens: 1 } }),
860
- ]);
774
+ ], gate);
861
775
 
862
- const gated = sb.currentStatus.messages.flatMap((m) => m.toolCalls).filter((tc) => tc.id === GATED_ID);
776
+ const gated = status.messages.flatMap((m) => m.toolCalls).filter((tc) => tc.id === GATED_ID);
863
777
  expect(gated).toHaveLength(1);
864
778
  expect(gated[0].status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
865
779
  expect(gated[0].result).toContain("tool executed ok");
866
780
  expect(gated[0].args, "the resumed start fills the args the seed lacked").toEqual({ target: "prod" });
867
- const allText = sb.currentStatus.messages.map((m) => m.content).join("\n");
781
+ const allText = status.messages.map((m) => m.content).join("\n");
868
782
  expect(allText).toContain("I'll call the gated tool.");
869
783
  expect(allText).toContain("All done.");
870
784
  expect(sb.awaitingApproval, "a resumed tool never re-gates").toBe(false);
871
785
  });
872
786
  });
873
787
 
874
- describe("artifacts and write-backs (the ExecutionStatusWriter face)", () => {
788
+ describe("artifacts and write-backs", () => {
875
789
  const artifact = (sandboxPath: string, contentHash: string) =>
876
790
  create(ExecutionArtifactSchema, { sandboxPath, contentHash, storageKey: `k/${contentHash}` });
877
791
 
878
792
  it("appends a new artifact and forces the next persist", () => {
879
- const sb = makeBuilder();
793
+ const { sb, status } = makeBuilder();
880
794
  sb.addArtifact(artifact("out/a.txt", "h1"));
881
- expect(sb.currentStatus.artifacts).toHaveLength(1);
882
- expect(sb.forceNextUpdate).toBe(true);
795
+ expect(status.artifacts).toHaveLength(1);
796
+ expect(sb.dirty).toBe(true);
883
797
  });
884
798
 
885
799
  it("deduplicates by sandboxPath: the same contentHash is a no-op that does not force a persist", () => {
886
- const sb = makeBuilder();
800
+ const { sb, status } = makeBuilder();
887
801
  sb.addArtifact(artifact("out/a.txt", "h1"));
888
- sb.clearForceFlag();
802
+ sb.markPersisted();
889
803
  sb.addArtifact(artifact("out/a.txt", "h1"));
890
- expect(sb.currentStatus.artifacts).toHaveLength(1);
891
- expect(sb.forceNextUpdate).toBe(false);
804
+ expect(status.artifacts).toHaveLength(1);
805
+ expect(sb.dirty).toBe(false);
892
806
  });
893
807
 
894
808
  it("replaces the artifact in place when its contentHash changes", () => {
895
- const sb = makeBuilder();
809
+ const { sb, status } = makeBuilder();
896
810
  sb.addArtifact(artifact("out/a.txt", "h1"));
897
- sb.clearForceFlag();
811
+ sb.markPersisted();
898
812
  sb.addArtifact(artifact("out/a.txt", "h2"));
899
- expect(sb.currentStatus.artifacts).toHaveLength(1);
900
- expect(sb.currentStatus.artifacts[0].contentHash).toBe("h2");
901
- expect(sb.forceNextUpdate).toBe(true);
813
+ expect(status.artifacts).toHaveLength(1);
814
+ expect(status.artifacts[0].contentHash).toBe("h2");
815
+ expect(sb.dirty).toBe(true);
902
816
  });
903
817
 
904
818
  it("upserts write-backs by workspaceEntryName, tracking entries independently", () => {
905
- const sb = makeBuilder();
819
+ const { sb, status } = makeBuilder();
906
820
  sb.addWriteBack(create(WorkspaceWriteBackSchema, { workspaceEntryName: "repo-a", branchName: "stigmer/s1" }));
907
821
  sb.addWriteBack(create(WorkspaceWriteBackSchema, { workspaceEntryName: "repo-b", branchName: "stigmer/s1" }));
908
- sb.clearForceFlag();
822
+ sb.markPersisted();
909
823
  sb.addWriteBack(create(WorkspaceWriteBackSchema, { workspaceEntryName: "repo-a", branchName: "stigmer/s1", pullRequestUrl: "https://example/pr/1" }));
910
- const backs = sb.currentStatus.workspaceWriteBacks;
824
+ const backs = status.workspaceWriteBacks;
911
825
  expect(backs.map((b) => b.workspaceEntryName)).toEqual(["repo-a", "repo-b"]);
912
826
  expect(backs[0].pullRequestUrl).toBe("https://example/pr/1");
913
- expect(sb.forceNextUpdate).toBe(true);
827
+ expect(sb.dirty).toBe(true);
914
828
  });
915
829
  });
916
830
  });