@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
@@ -0,0 +1,70 @@
1
+ /**
2
+ * `ExecutionRecord.whenToolCallsSettled` — the barrier the hermetic scripted
3
+ * model waits on before it ticks (since #1097): resolves once every named row is
4
+ * persisted settled, across the root and the sub-agent transcripts; resolves
5
+ * at once when there is nothing to wait for; fails naming the open ids rather
6
+ * than hanging when no persist ever carries them.
7
+ */
8
+ import { describe, expect, it } from "vitest";
9
+ import { create } from "@bufbuild/protobuf";
10
+ import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
11
+ import { ExecutionPhase, MessageType, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
12
+ import { deepAgentExecutionRecord } from "../../activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js";
13
+
14
+ function statusWith(rows: Array<{ id: string; status: ToolCallStatus; subAgent?: boolean }>) {
15
+ const message = (ids: typeof rows) => ({
16
+ type: MessageType.MESSAGE_AI,
17
+ content: "",
18
+ toolCalls: ids.map((r) => ({ id: r.id, name: "probe", status: r.status })),
19
+ });
20
+ return create(AgentExecutionStatusSchema, {
21
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
22
+ messages: [message(rows.filter((r) => !r.subAgent))],
23
+ subAgentExecutions: rows.some((r) => r.subAgent)
24
+ ? [{ id: "task-1", name: "helper", messages: [message(rows.filter((r) => r.subAgent))] }]
25
+ : [],
26
+ });
27
+ }
28
+
29
+ describe("ExecutionRecord.whenToolCallsSettled", () => {
30
+ it("resolves at once for no ids, and for ids already persisted settled", async () => {
31
+ const record = deepAgentExecutionRecord({ message: "go" });
32
+ await expect(record.whenToolCallsSettled([])).resolves.toBeUndefined();
33
+
34
+ record.applyStatusUpdate(statusWith([{ id: "c-1", status: ToolCallStatus.TOOL_CALL_COMPLETED }]));
35
+ await expect(record.whenToolCallsSettled(["c-1"])).resolves.toBeUndefined();
36
+ });
37
+
38
+ it("waits through a RUNNING persist and releases on the persist that settles the row", async () => {
39
+ const record = deepAgentExecutionRecord({ message: "go" });
40
+ let released = false;
41
+ const barrier = record.whenToolCallsSettled(["c-1"]).then(() => {
42
+ released = true;
43
+ });
44
+
45
+ record.applyStatusUpdate(statusWith([{ id: "c-1", status: ToolCallStatus.TOOL_CALL_RUNNING }]));
46
+ await Promise.resolve();
47
+ expect(released, "a RUNNING row is not settled").toBe(false);
48
+
49
+ record.applyStatusUpdate(statusWith([{ id: "c-1", status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL }]));
50
+ await barrier;
51
+ expect(released).toBe(true);
52
+ });
53
+
54
+ it("finds a sub-agent's rows in its own transcript", async () => {
55
+ const record = deepAgentExecutionRecord({ message: "go" });
56
+ record.applyStatusUpdate(
57
+ statusWith([
58
+ { id: "task-1", status: ToolCallStatus.TOOL_CALL_RUNNING },
59
+ { id: "sub-1", status: ToolCallStatus.TOOL_CALL_COMPLETED, subAgent: true },
60
+ ]),
61
+ );
62
+ await expect(record.whenToolCallsSettled(["sub-1"])).resolves.toBeUndefined();
63
+ });
64
+
65
+ it("fails naming the ids still open when no persist ever settles them, instead of hanging", async () => {
66
+ const record = deepAgentExecutionRecord({ message: "go" });
67
+ record.applyStatusUpdate(statusWith([{ id: "c-1", status: ToolCallStatus.TOOL_CALL_RUNNING }]));
68
+ await expect(record.whenToolCallsSettled(["c-1", "c-2"], 20)).rejects.toThrow(/c-1, c-2 never settled/);
69
+ });
70
+ });
@@ -27,7 +27,9 @@
27
27
 
28
28
  import { describe, it, expect } from "vitest";
29
29
  import { CancelledFailure } from "@temporalio/activity";
30
+ import { create } from "@bufbuild/protobuf";
30
31
  import { ApprovalAction, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
32
+ import { SubAgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
31
33
 
32
34
  import type { HarnessAdapter, TurnInput, TurnOutcome, TurnSink } from "../../harness/types.js";
33
35
  import {
@@ -41,11 +43,12 @@ import {
41
43
  assertStopSignalInterrupts,
42
44
  assertUsageReachesSinkAsDeltas,
43
45
  } from "../harness-contract/contract.js";
46
+ import { assertTranscriptStreamsNothing } from "../harness-contract/runtime-contract.js";
44
47
  import { scriptedSubject } from "../harness-contract/scripted-adapter.js";
45
48
  import type { ScriptedSubject } from "../harness-contract/scripted-adapter.js";
46
49
  import { scenario } from "../harness-contract/types.js";
47
50
  import type { EngineView, HarnessContractSubject, TurnScenario } from "../harness-contract/types.js";
48
- import { findToolCallRow } from "../proto-helpers.js";
51
+ import { aiMessage, emptyStatus, findToolCallRow, toolCall } from "../proto-helpers.js";
49
52
 
50
53
  /** Short enough to keep this file fast; long enough that an honest microtask settle never trips it. */
51
54
  const SHORT_BOUND_MS = 100;
@@ -75,6 +78,7 @@ function adapterOver(inner: HarnessAdapter, name: string, patch: Partial<Harness
75
78
  function sinkOver(inner: TurnSink, patch: Partial<TurnSink>): TurnSink {
76
79
  return {
77
80
  status: inner.status,
81
+ transcript: inner.transcript,
78
82
  stopSignal: inner.stopSignal,
79
83
  setupTiming: inner.setupTiming,
80
84
  requestPersist: () => inner.requestPersist(),
@@ -324,3 +328,33 @@ describe("harness contract kit self-check — the driver stands in for the runti
324
328
  expect(turn.sink.status.messages.map((m) => m.content), "the stop landed in the hang, after the setup step and before the step after it").toEqual(["setting up"]);
325
329
  });
326
330
  });
331
+
332
+ // ── The runtime half's transcript fact ──────────────────────────────────────
333
+
334
+ describe("harness contract kit self-check — the settled-transcript assertion can fail", () => {
335
+ it("passes a transcript with every flag off", () => {
336
+ const status = emptyStatus();
337
+ const message = aiMessage("done");
338
+ message.toolCalls.push(toolCall("t-1", "read", ToolCallStatus.TOOL_CALL_COMPLETED));
339
+ status.messages.push(message);
340
+ expect(() => assertTranscriptStreamsNothing("honest", status)).not.toThrow();
341
+ });
342
+
343
+ it("names a message left streaming", () => {
344
+ const status = emptyStatus();
345
+ const message = aiMessage("still typing");
346
+ message.isStreaming = true;
347
+ status.messages.push(message);
348
+ expect(() => assertTranscriptStreamsNothing("broken:streams", status)).toThrow(/broken:streams: a settled turn's transcript is still streaming at messages\[0\]/);
349
+ });
350
+
351
+ it("names a tool-call row left streaming, inside a sub-agent's transcript", () => {
352
+ const status = emptyStatus();
353
+ const row = toolCall("t-2", "shell");
354
+ row.isStreaming = true;
355
+ const message = aiMessage("");
356
+ message.toolCalls.push(row);
357
+ status.subAgentExecutions.push(create(SubAgentExecutionSchema, { id: "sub-1", messages: [message] }));
358
+ expect(() => assertTranscriptStreamsNothing("broken:streams", status)).toThrow(/subAgentExecutions\[sub-1\]\.messages\[0\]\.toolCalls\[t-2\]/);
359
+ });
360
+ });
@@ -90,7 +90,7 @@ export function describeGatewayContract(substrate: GatewaySubstrate): void {
90
90
  // effect is always auditable to the policy layer that governed it. Gated to
91
91
  // substrates that decide the source at the gate (the deep-agent gate); the
92
92
  // Cursor substrate projects provenance at reconstruction time and is covered
93
- // by the message-translator + corpus suites.
93
+ // by the translator + boundary-rows + corpus suites.
94
94
  if (substrate.capabilities.surfacesGatePolicySource) {
95
95
  it("tags every gated side effect with a non-UNSPECIFIED policy source", async () => {
96
96
  for (const action of [WRITE_A, SHELL, DELETE]) {
@@ -137,7 +137,7 @@ export interface SubstrateCapabilities {
137
137
  * and stamps the source at interrupt time (`true`); the Cursor substrate's
138
138
  * provenance is a reconstruction-time projection over the persisted tool call,
139
139
  * not a property of the hook's deny decision, so it is `false` here and is
140
- * covered instead by the message-translator and corpus suites.
140
+ * covered instead by the translator, boundary-rows and corpus suites.
141
141
  */
142
142
  readonly surfacesGatePolicySource: boolean;
143
143
  }
@@ -15,7 +15,7 @@
15
15
  * a bound token ref. Success means the pre-boot graph was connect-free,
16
16
  * both interceptors installed, `assertHttp2ConnectPatched` passed, the
17
17
  * Cursor SDK slice loaded from inside its `boot`, and the native
18
- * deep-agent adapter (the table's second row since S3 M2b) registered its
18
+ * deep-agent adapter (the table's second row since #1096) registered its
19
19
  * profiles and loaded LangChain after the patch. That neither engine is
20
20
  * on the table's STATIC graph is each adapter's own fence
21
21
  * (`adapter-graph-is-sdk-free.test.ts`); this child proves the boot
@@ -38,7 +38,7 @@
38
38
  * is carried to COMPLETED; with REJECT or SKIP → never executes and the
39
39
  * turn continues (the row's SKIPPED status is the runtime's to write,
40
40
  * `harness/approval-decisions.ts`, proven in the runtime half — the
41
- * driver stands in for that write between turns, Q-M1-3); reinvoked again
41
+ * driver stands in for that write between turns); reinvoked again
42
42
  * after the approval → still once, never re-gated.
43
43
  * 4. Aborting `stopSignal` mid-hang settles `runTurn` as `interrupted` within
44
44
  * {@link INTERRUPT_SETTLE_BOUND_MS} and nothing after the stop executes or
@@ -48,8 +48,8 @@
48
48
  * executes on the resumed turn is the pause primitive's business — an
49
49
  * interrupt engine completes the pending task before its model speaks,
50
50
  * a deny-and-retry engine re-reaches it whenever its model does — so it
51
- * is below the contract line (S3 M3 Q-M3-2; each harness's own kit file
52
- * observes its order).
51
+ * is below the contract line (each harness's own kit file observes its
52
+ * order).
53
53
  * 5. Usage reaches the sink as non-negative deltas summing to what the engine
54
54
  * emitted.
55
55
  * 6. Capability and behaviour agree on the state id: an `engine-minted`
@@ -154,7 +154,7 @@ export class ExecutionDriver {
154
154
 
155
155
  /** Start a turn without awaiting it, for invariants about turns in flight. */
156
156
  begin(turn: TurnScenario, options: TurnOptions = {}): TurnInFlight {
157
- const sink = options.sink ?? new RecordingTurnSink({ status: this.seedStatus() });
157
+ const sink = options.sink ?? new RecordingTurnSink({ status: this.seedStatus(), executionId: this.executionId });
158
158
  // The engine's view and the record's copy both derive from the status
159
159
  // this turn folds into, so a prepared sink's status is honoured too.
160
160
  const view: EngineView = {
@@ -297,7 +297,7 @@ export async function assertEveryExitIsAnOutcome(subject: HarnessContractSubject
297
297
  driver.recordTurn(sink);
298
298
  }
299
299
 
300
- const preAborted = new RecordingTurnSink({ status: driver.seedStatus() });
300
+ const preAborted = new RecordingTurnSink({ status: driver.seedStatus(), executionId: driver.executionId });
301
301
  preAborted.abort("kit: aborted before the turn");
302
302
  const { settled } = driver.begin([scenario.say("never")], { sink: preAborted });
303
303
  await settleAsOutcome(subject, settleWithinBound(subject, settled, "under a pre-aborted signal", boundMs), "under a pre-aborted signal");
@@ -335,8 +335,8 @@ export async function assertProposalIsWaitingAndUnexecuted(subject: HarnessContr
335
335
  * turn continues — the row's terminal status is NOT asserted here because
336
336
  * it is not the adapter's to write (the decision is the server's field and
337
337
  * the WAITING → SKIPPED transition follows from it with no engine knowledge,
338
- * so it is the runtime's — `terminalizeNonExecutingDecisions`, landed at
339
- * S3 M1 and pinned by the runtime half's REJECT / SKIP arms; S2 M4 Q-M4-1).
338
+ * so it is the runtime's — `terminalizeNonExecutingDecisions`, landed in
339
+ * #1096 and pinned by the runtime half's REJECT / SKIP arms).
340
340
  * The reinvocation sees a CLONE of the persisted status, as it would from
341
341
  * the server.
342
342
  */
@@ -391,7 +391,7 @@ export async function assertStopSignalInterrupts(subject: HarnessContractSubject
391
391
  expect(findToolCallRow(hanging.sink.status, id), `${subject.name}: nothing after the stop may even reach the gate — the proposal after the hang has no row`).toBeUndefined();
392
392
  driver.recordTurn(hanging.sink);
393
393
 
394
- const preAborted = new RecordingTurnSink({ status: driver.seedStatus() });
394
+ const preAborted = new RecordingTurnSink({ status: driver.seedStatus(), executionId: driver.executionId });
395
395
  preAborted.abort("kit: aborted before the turn");
396
396
  const early = driver.begin([scenario.propose(id, WRITE_ALPHA)], { sink: preAborted });
397
397
  const earlyOutcome = await settleWithinBound(subject, early.settled, "under a pre-aborted signal", boundMs);
@@ -20,6 +20,7 @@
20
20
 
21
21
  import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
22
22
 
23
+ import { TranscriptBuilder } from "../../harness/transcript/builder.js";
23
24
  import type { TurnSink, UsageDelta } from "../../harness/types.js";
24
25
  import type { CasTouchedReader } from "../../shared/filereview/cas-touched.js";
25
26
  import { TimingRecorder } from "../../shared/cold-start-timing.js";
@@ -35,14 +36,22 @@ export type SinkEvent =
35
36
  | { readonly kind: "cas-observations" };
36
37
 
37
38
  export interface RecordingTurnSinkOptions {
38
- /** The status this turn folds into; a fresh empty one when omitted. */
39
+ /**
40
+ * The status this turn folds into; a fresh empty one when omitted. A
41
+ * reinvocation's clone arrives already carrying last turn's rows, and the
42
+ * builder indexes them as the runtime's would after its seed.
43
+ */
39
44
  readonly status?: AgentExecutionStatus;
45
+ /** The execution the builder names in its log lines; the kit's driver passes its own. */
46
+ readonly executionId?: string;
40
47
  /** When set, `bindHarnessState` rejects with this error (a failed session write). */
41
48
  readonly bindRejectsWith?: Error;
42
49
  }
43
50
 
44
51
  export class RecordingTurnSink implements TurnSink {
45
52
  readonly status: AgentExecutionStatus;
53
+ /** The one builder over `status`, as the runtime hands every adapter; the fake creates its rows through it. */
54
+ readonly transcript: TranscriptBuilder;
46
55
  readonly stopSignal: AbortSignal;
47
56
  readonly setupTiming = new TimingRecorder();
48
57
 
@@ -52,6 +61,7 @@ export class RecordingTurnSink implements TurnSink {
52
61
 
53
62
  constructor(options: RecordingTurnSinkOptions = {}) {
54
63
  this.status = options.status ?? emptyStatus();
64
+ this.transcript = new TranscriptBuilder(options.executionId ?? "kit-exec", this.status);
55
65
  this.stopSignal = this.controller.signal;
56
66
  this.bindRejectsWith = options.bindRejectsWith;
57
67
  }
@@ -19,7 +19,9 @@
19
19
  * the facts the runtime owes on it: the terminal phase, the copy
20
20
  * (`terminal-table.ts` `TERMINAL_COPY`, byte-pinned), `status.error`,
21
21
  * `completedAt`, "the step after never runs", the whole-activity heartbeat,
22
- * one bind for an engine-minted harness, the slim return. What is NOT
22
+ * one bind for an engine-minted harness, the slim return, and — on every
23
+ * arm, whatever the outcome — a transcript that streams nothing once the
24
+ * turn has settled ({@link assertTranscriptStreamsNothing}). What is NOT
23
25
  * asserted here is engine cadence — how many IN_PROGRESS persists precede
24
26
  * the WAITING write, the exact setup labels beyond the runtime's own, the
25
27
  * exact price a vendor's table yields — because those are a subject's facts;
@@ -216,13 +218,47 @@ export class RuntimeExecutionDriver {
216
218
  }
217
219
  }
218
220
 
219
- /** The final full status the record holds; an arm that persisted nothing is a failure, named. */
221
+ /**
222
+ * The final full status the record holds, checked for the one fact every
223
+ * settled turn owes its transcript before the arm reads anything else. An
224
+ * arm that persisted nothing is a failure, named.
225
+ */
220
226
  function finalStatusOf(harness: RuntimeContractHarness, driver: RuntimeExecutionDriver): AgentExecutionStatus {
221
227
  const final = driver.record.lastFullStatus;
222
228
  if (!final) throw new Error(`${harness.subject.name}: the runtime persisted no full status`);
229
+ assertTranscriptStreamsNothing(harness.subject.name, final);
223
230
  return final;
224
231
  }
225
232
 
233
+ /**
234
+ * No streaming flag survives a settled turn — on any message, any tool-call
235
+ * row, in the root transcript or a sub-agent's — however the turn ended. A
236
+ * flag left on is a spinner the console never stops. The runtime closes
237
+ * them once, after `runTurn` returns, on every path (`harness/run-turn.ts`),
238
+ * so the fact holds for every subject and every outcome, and every arm
239
+ * checks it through {@link finalStatusOf}. Before the runtime owned that
240
+ * close, this fired on both real adapters' internal-failure arm: a turn that
241
+ * failed before its settle left its last message streaming. Exported so the
242
+ * kit's self-check can hand it a status that still streams and prove it
243
+ * fires.
244
+ */
245
+ export function assertTranscriptStreamsNothing(subjectName: string, status: AgentExecutionStatus): void {
246
+ const streaming: string[] = [];
247
+ const sweep = (scope: string, messages: AgentExecutionStatus["messages"]): void => {
248
+ messages.forEach((m, i) => {
249
+ if (m.isStreaming) streaming.push(`${scope}messages[${i}]`);
250
+ for (const tc of m.toolCalls) {
251
+ if (tc.isStreaming) streaming.push(`${scope}messages[${i}].toolCalls[${tc.id}]`);
252
+ }
253
+ });
254
+ };
255
+ sweep("", status.messages);
256
+ for (const sub of status.subAgentExecutions) sweep(`subAgentExecutions[${sub.id}].`, sub.messages);
257
+ if (streaming.length > 0) {
258
+ throw new Error(`${subjectName}: a settled turn's transcript is still streaming at ${streaming.join(", ")}`);
259
+ }
260
+ }
261
+
226
262
  /** What every arm hands back so a subject's file can add its own facts. */
227
263
  export interface RuntimeArmResult {
228
264
  readonly driver: RuntimeExecutionDriver;
@@ -357,11 +393,11 @@ export async function assertFileReviewRoundTrip(harness: RuntimeContractHarness,
357
393
  expect(rows.get(keepId)?.fileChangeSetId, `${subject.name}: a flowed row is badged with the change set`).toBe(changeSetId);
358
394
  expect(rows.get(dropId)?.fileChangeSetId).toBe(changeSetId);
359
395
  expect(rows.get(keepId)?.status, `${subject.name}: a flowed row is COMPLETED, never gated`).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
360
- // The strip is the runtime's, attached on its writes (Q-M4-7). Its COUNT is
396
+ // The strip is the runtime's, attached on its writes. Its COUNT is
361
397
  // the capture floor's: a real engine's first persist precedes its first
362
398
  // write and both writes land within one floor interval, so the pause can
363
- // honestly read "0 files" beside a two-file candidate (F-M4-3, recorded
364
- // for S5: whether the terminal write should bypass the floor). The arm
399
+ // honestly read "0 files" beside a two-file candidate (#1114: whether the
400
+ // terminal write should bypass the floor). The arm
365
401
  // asserts the ownership, not the floor.
366
402
  expect(afterCapture.fileChangeProgress?.changeSetId, `${subject.name}: the runtime carries this turn's strip`).toBe(changeSetId);
367
403
  expect(afterCapture.completedAt, `${subject.name}: a review pause is not complete`).toBe("");
@@ -374,7 +410,7 @@ export async function assertFileReviewRoundTrip(harness: RuntimeContractHarness,
374
410
  const second = await driver.turn([]);
375
411
  expect(slimOf(subject, second).phase, `${subject.name}: a pure file-review resume completes without the engine`).toBe("EXECUTION_COMPLETED");
376
412
  // The kept file is the engine-not-re-run witness: a subject that counts
377
- // executions by reading its ledger off disk (the native one, Q-M3-4) reads
413
+ // executions by reading its ledger off disk (the native one) reads
378
414
  // the DISCARDED file's count as 0 now, because the reconcile just restored
379
415
  // that file to its baseline — which is the reconcile working, not the
380
416
  // engine forgetting.
@@ -398,7 +434,7 @@ export async function assertFileReviewRoundTrip(harness: RuntimeContractHarness,
398
434
  * settles the row SKIPPED (a REJECT row carries `error: "Rejected by user"`),
399
435
  * so no WAITING row survives on a finished execution. This is the proto
400
436
  * contract as of stigmer#197 and the runtime's own write
401
- * (`harness/approval-decisions.ts`); before S3 M1 the runtime FAILED a
437
+ * (`harness/approval-decisions.ts`); before #1096 the runtime FAILED a
402
438
  * REJECTed reinvocation before any engine ran.
403
439
  */
404
440
  export async function assertNonExecutingDecisionSettlesSkipped(
@@ -440,14 +476,14 @@ export async function assertNonExecutingDecisionSettlesSkipped(
440
476
  * the next message), `error` starting with the cross-repo prefix Stigmer
441
477
  * Cloud's channel delivery matches on, the user copy as the one system row,
442
478
  * `completedAt` stamped, RETURNED (a retry would spend the same budget), and
443
- * the step after the limit never processed. Before S3 M1 the native harness
444
- * RETURNED this terminal without persisting it (M0 finding F-M0-4); the
479
+ * the step after the limit never processed. Before #1096 the native harness
480
+ * RETURNED this terminal without persisting it; the
445
481
  * runtime's arm is the one persisted terminal.
446
482
  *
447
483
  * The execution SETS the budget it exhausts: the record carries the smallest
448
484
  * valid `max_tool_rounds` (an unbounded execution has no limit to reach — on
449
485
  * native `resolveRecursionLimit(0)` is unlimited, and the first run of this
450
- * arm against it spun until the test timed out; S3 M3 F-M3-3). The fake
486
+ * arm against it spun until the test timed out). The fake
451
487
  * exhausts whatever budget it is given; a real engine spins its cheapest
452
488
  * ungated tool until the graph stops it.
453
489
  */
@@ -625,12 +661,12 @@ const STALL_CLOCK_INTERVAL_MS = 25;
625
661
  * park is reported while the chunks the engine produced before parking are
626
662
  * still in its event pipeline ahead of the adapter's loop, so activity marks
627
663
  * land AFTER a single tick and a frozen clock reads idle 0 from then on
628
- * (S3 M3 F-M3-5, measured on the native adapter: 35 marks at the park, 37
664
+ * (measured on the native adapter: 35 marks at the park, 37
629
665
  * within 10 ms). A pull-based double's park implies everything before it
630
666
  * was seen; a real stall is wall time passing while the engine is silent,
631
667
  * whichever shape the engine has. The interval advances a SCRIPTED clock —
632
668
  * the kit's rule against timers is about stopping a turn on a guess, which
633
- * this never does (Q-M3-7).
669
+ * this never does.
634
670
  */
635
671
  export async function assertStallFailsTheTurn(harness: RuntimeContractHarness, label = "rt-stall"): Promise<RuntimeArmResult> {
636
672
  const { subject, clock } = harness;
@@ -692,7 +728,7 @@ export async function assertCostCapTerminates(harness: RuntimeContractHarness, l
692
728
  /**
693
729
  * A platform STOP answered to a mid-turn persist: the turn completes early
694
730
  * with the platform-stop row and no error, and the step after the STOP never
695
- * runs — the awaited `requestPersist` at work (Q-M3-2). The persist the
731
+ * runs — the awaited `requestPersist` at work. The persist the
696
732
  * control plane answers is the one carrying a completed `read`: the discrete
697
733
  * event every harness flushes on (the Cursor golden's lever), where a bare
698
734
  * assistant message may ride an engine's cadence to the settle persist, too
@@ -786,7 +822,7 @@ export async function assertFailedSurfaceWritesItsCopy(harness: RuntimeContractH
786
822
 
787
823
  /**
788
824
  * A rejected `bindHarnessState` (the session write refused) ends the turn
789
- * FAILED on the internal surface with nothing executed (Q-S2-11, R3).
825
+ * FAILED on the internal surface with nothing executed.
790
826
  * Engine-minted harnesses only: a deterministic one never binds.
791
827
  */
792
828
  export async function assertRejectedBindFails(harness: RuntimeContractHarness, label = "rt-bind-rejects"): Promise<RuntimeArmResult> {
@@ -884,7 +920,7 @@ export interface RuntimeContractOptions {
884
920
  * to wait for (`scenario.cancelled()`, an SDK-side cancel); the arm is
885
921
  * registered SKIPPED otherwise. On by default; the native subject turns it
886
922
  * off (no LangGraph path ends a turn `cancelled` — a stop is the runtime's
887
- * and settles `interrupted`; S3 M3 F-M3-2).
923
+ * and settles `interrupted`).
888
924
  */
889
925
  readonly engineCancel?: boolean;
890
926
  }
@@ -968,7 +1004,7 @@ export function describeHarnessRuntimeContract(harness: RuntimeContractHarness,
968
1004
  await assertFailedSurfaceWritesItsCopy(harness, surface, FAILURE_MESSAGES[surface]);
969
1005
  });
970
1006
  }
971
- it.skipIf(subject.adapter.capabilities.stateIdSource !== "engine-minted")("a rejected bindHarnessState ends the turn failed with nothing executed (Q-S2-11)", async () => {
1007
+ it.skipIf(subject.adapter.capabilities.stateIdSource !== "engine-minted")("a rejected bindHarnessState ends the turn failed with nothing executed", async () => {
972
1008
  clock.reset();
973
1009
  await assertRejectedBindFails(harness);
974
1010
  });
@@ -22,8 +22,12 @@
22
22
  *
23
23
  * What is deliberately simple here and would be real work in an adapter:
24
24
  * "executing" a side effect is incrementing a counter; the engine state id is
25
- * a counter too; the transcript rows are built with the shared proto
26
- * factories. What is NOT simplified is the contract behaviour itself —
25
+ * a counter too; there is no engine event to translate, so each step emits
26
+ * the canonical `TranscriptEvent`s a translator would (a run for a `say`, a
27
+ * start and a finish for a tool, a proposal for a gate) straight into
28
+ * `sink.transcript` — the one way any harness creates a transcript row, and
29
+ * the whole of what a real adapter's translator adds on top of this file.
30
+ * What is NOT simplified is the contract behaviour itself —
27
31
  * settling with an outcome and never rejecting, stopping at every step
28
32
  * boundary and inside a hang, binding before the first persist, executing an
29
33
  * approval exactly once, refusing to resume a state it never minted — because
@@ -34,8 +38,8 @@
34
38
  * is on the row (the server's field) and the transition follows from it with
35
39
  * no engine knowledge, so it belongs to the runtime — one writer per field,
36
40
  * as `approvalDecisionsOf` is the runtime's one reader and
37
- * `terminalizeNonExecutingDecisions` its one writer (S2 M4, Q-M4-1; landed at
38
- * S3 M1, `harness/approval-decisions.ts`). An adapter's whole duty for a
41
+ * `terminalizeNonExecutingDecisions` its one writer (landed in #1096,
42
+ * `harness/approval-decisions.ts`). An adapter's whole duty for a
39
43
  * non-executing decision is to not execute.
40
44
  *
41
45
  * Scenarios are arranged PER SESSION (the engine is per session in every real
@@ -57,7 +61,7 @@ import type { HarnessAdapter, TurnInput, TurnOutcome, TurnSink } from "../../har
57
61
  import { DEEP_AGENT_VISION_PROFILE } from "../../shared/attachment-vision.js";
58
62
  import type { ProposedAction } from "../approval-contract/types.js";
59
63
  import { testConfig } from "../config-fixture.js";
60
- import { aiMessage, findToolCallRow, toolCall, waitingToolCall } from "../proto-helpers.js";
64
+ import { findToolCallRow } from "../proto-helpers.js";
61
65
  import type { EngineView, HarnessContractSubject, ScenarioStep, TurnScenario } from "./types.js";
62
66
 
63
67
  export interface ScriptedHarnessOptions {
@@ -101,6 +105,8 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
101
105
  private readonly executions = new Map<string, number>();
102
106
  private readonly hangWaiters: Array<() => void> = [];
103
107
  private mintCounter = 0;
108
+ /** One `runId` per `say`, as an engine mints one per model run; the builder keys a message on it. */
109
+ private runCounter = 0;
104
110
 
105
111
  constructor(options: ScriptedHarnessOptions) {
106
112
  this.name = options.name ?? `scripted(${options.pausePrimitive}, ${options.stateIdSource})`;
@@ -211,7 +217,12 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
211
217
  private async play(step: ScenarioStep, input: TurnInput, sink: TurnSink): Promise<TurnOutcome | undefined> {
212
218
  switch (step.kind) {
213
219
  case "say": {
214
- sink.status.messages.push(aiMessage(step.text));
220
+ // One model run: start, its text, finish — the shape every
221
+ // translator emits for an assistant message.
222
+ const runId = `run-${++this.runCounter}`;
223
+ sink.transcript.apply({ kind: "message_start", runId });
224
+ sink.transcript.apply({ kind: "text_delta", runId, text: step.text });
225
+ sink.transcript.apply({ kind: "message_finish", runId });
215
226
  sink.recordActivity();
216
227
  // Awaited, as the Cursor loop awaits its own: a platform STOP the
217
228
  // runtime reads from this write aborts the signal before the next
@@ -223,14 +234,12 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
223
234
  if (step.flows) return this.flowingWrite(step.toolCallId, step.action, input, sink);
224
235
  return this.propose(step.toolCallId, step.action, input, sink);
225
236
  case "read": {
226
- // Ungated: the row lands COMPLETED at once, the effect counts as run,
227
- // and the persist is awaited like `say`'s — a real engine's tool call
228
- // is the discrete event its loop flushes on.
229
- const message = aiMessage("");
230
- const row = toolCall(step.toolCallId, "read", ToolCallStatus.TOOL_CALL_COMPLETED);
231
- row.result = `contents of ${step.path}`;
232
- message.toolCalls.push(row);
233
- sink.status.messages.push(message);
237
+ // Ungated: the row starts and finishes at once, the effect counts as
238
+ // run, and the persist is awaited like `say`'s — a real engine's tool
239
+ // call is the discrete event its loop flushes on. The builder puts
240
+ // the row on the message whose text preceded it.
241
+ sink.transcript.apply({ kind: "tool_started", callId: step.toolCallId, name: "read", input: { path: step.path }, mcpServerSlug: "" });
242
+ sink.transcript.apply({ kind: "tool_finished", callId: step.toolCallId, result: `contents of ${step.path}` });
234
243
  this.executions.set(step.toolCallId, this.executionCount(step.toolCallId) + 1);
235
244
  sink.recordActivity("read");
236
245
  await sink.requestPersist();
@@ -279,12 +288,18 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
279
288
  switch (decision) {
280
289
  case ApprovalAction.UNSPECIFIED: {
281
290
  // Propose: surface the gated call as a WAITING row and stop the turn.
282
- // Never write a second row for the same id on a resumed turn.
283
- if (!existing) {
284
- const message = aiMessage("");
285
- message.toolCalls.push(waitingToolCall(toolCallId, action.kind, `Approve ${action.kind} ${action.resource}?`));
286
- sink.status.messages.push(message);
287
- }
291
+ // `approval_proposed` is an upsert by id — a resumed turn that
292
+ // re-proposes reopens the seeded row, never writes a second one. No
293
+ // `args` on purpose: a `write` carrying whole-file content would make
294
+ // the runtime's exact-apply eligible on APPROVE, and this fake's
295
+ // execution count would then measure a path the engine never ran.
296
+ sink.transcript.apply({
297
+ kind: "approval_proposed",
298
+ callId: toolCallId,
299
+ name: action.kind,
300
+ mcpServerSlug: "",
301
+ message: `Approve ${action.kind} ${action.resource}?`,
302
+ });
288
303
  sink.recordActivity();
289
304
  sink.requestPersist();
290
305
  return { kind: "awaiting_approval" };
@@ -292,7 +307,7 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
292
307
  case ApprovalAction.APPROVE:
293
308
  case ApprovalAction.APPROVE_ALL: {
294
309
  this.executions.set(toolCallId, this.executionCount(toolCallId) + 1);
295
- this.completeRow(existing, toolCallId, action, sink);
310
+ this.completeRow(toolCallId, action, sink);
296
311
  return undefined;
297
312
  }
298
313
  case ApprovalAction.SKIP:
@@ -321,11 +336,8 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
321
336
  const file = join(input.workspace.primaryDir, action.resource);
322
337
  await mkdir(dirname(file), { recursive: true });
323
338
  await writeFile(file, `${toolCallId}\n`);
324
- const message = aiMessage("");
325
- const row = toolCall(toolCallId, "write", ToolCallStatus.TOOL_CALL_COMPLETED);
326
- row.args = { path: action.resource };
327
- message.toolCalls.push(row);
328
- sink.status.messages.push(message);
339
+ sink.transcript.apply({ kind: "tool_started", callId: toolCallId, name: "write", input: { path: action.resource }, mcpServerSlug: "" });
340
+ sink.transcript.apply({ kind: "tool_finished", callId: toolCallId, result: "" });
329
341
  this.executions.set(toolCallId, this.executionCount(toolCallId) + 1);
330
342
  sink.recordActivity("write");
331
343
  await sink.requestPersist();
@@ -333,22 +345,14 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
333
345
  }
334
346
 
335
347
  /**
336
- * Report the executed action on its row, as a real engine's completion
337
- * event does. The row normally exists (the runtime seeded the status with
338
- * last turn's WAITING row); a decision with no row is a runtime that decided
339
- * out of band, and the adapter still records what happened rather than
340
- * losing the fact.
348
+ * Report the executed action on its row, as a real engine's re-issue does:
349
+ * a start (which flips the seeded WAITING row to RUNNING — or creates the
350
+ * row when a runtime decided out of band and no row exists, so the fact is
351
+ * recorded rather than lost) and a finish.
341
352
  */
342
- private completeRow(existing: ToolCall | undefined, toolCallId: string, action: ProposedAction, sink: TurnSink): void {
343
- if (existing) {
344
- existing.status = ToolCallStatus.TOOL_CALL_COMPLETED;
345
- } else {
346
- const message = aiMessage("");
347
- const row = waitingToolCall(toolCallId, action.kind, "");
348
- row.status = ToolCallStatus.TOOL_CALL_COMPLETED;
349
- message.toolCalls.push(row);
350
- sink.status.messages.push(message);
351
- }
353
+ private completeRow(toolCallId: string, action: ProposedAction, sink: TurnSink): void {
354
+ sink.transcript.apply({ kind: "tool_started", callId: toolCallId, name: action.kind, input: {}, mcpServerSlug: "" });
355
+ sink.transcript.apply({ kind: "tool_finished", callId: toolCallId, result: "" });
352
356
  sink.recordActivity();
353
357
  sink.requestPersist();
354
358
  }
@@ -360,8 +364,8 @@ export interface ScriptedSubject extends HarnessContractSubject {
360
364
  }
361
365
 
362
366
  /**
363
- * The fake declares the `deep-agent` row: in S2 the runtime served that row
364
- * through this fake alone; since S3 M3 the real native adapter takes it over
367
+ * The fake declares the `deep-agent` row: in #1070 the runtime served that row
368
+ * through this fake alone; since #1096 the real native adapter takes it over
365
369
  * in the runtime half
366
370
  * (`execute-deep-agent/__test-utils__/contract-subject.ts`), and the fake
367
371
  * keeps declaring it so the kit's self-check runs a full subject under both