@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
@@ -14,7 +14,7 @@
14
14
  * and driven by the framework's own activity environment. Both harnesses now
15
15
  * run on it (`execute-cursor/__test-utils__/hermetic-cursor.ts`,
16
16
  * `execute-deep-agent/__test-utils__/hermetic-deep-agent.ts`), and the earlier
17
- * native tests were re-homed onto the native driver (S3 M0), their assertions
17
+ * native tests were re-homed onto the native driver (#1096), their assertions
18
18
  * carried into `execute-deep-agent/__tests__/hermetic/`.
19
19
  *
20
20
  * What is generic here and what is not: everything an activity touches that is
@@ -55,11 +55,22 @@
55
55
  * Determinism: the clock is faked for `Date` ONLY (`vi.useFakeTimers({ toFake:
56
56
  * ["Date"] })`) and TICKS — the harness double advances it a fixed quantum per
57
57
  * script step through {@link ScriptedClock}. Frozen time would make
58
- * `delta-enricher.ts`'s persist debounce (`Date.now() - lastPersistTime`) never
58
+ * the streaming scheduler's cadence (`Date.now()` against its last send) never
59
59
  * elapse and silently skip the mid-stream persist path production always runs;
60
60
  * a ticking clock runs the same path and lands on the same instants every run.
61
61
  * Timers stay real so the periodic heartbeat and the stall watchdog behave.
62
62
  *
63
+ * The live posture (#1097's live-run gate): a `CURSOR_API_KEY`-gated live
64
+ * instrument keeps all three substitutions above — the real `Context`, the
65
+ * record behind the client, the temp environment — and drops only the
66
+ * harness's SDK double, so the REAL activity runs against the REAL vendor SDK
67
+ * with everything the activity persists still readable from the record.
68
+ * Two things differ from a hermetic run and both are the caller's explicit
69
+ * choice: no `ScriptedClock` is installed (the real SDK is not ticked, and a
70
+ * frozen clock would skip the persist cadence production runs), and the
71
+ * environment may name an `eventRecordingDir` so the run leaves a recording.
72
+ * The harness's driver names the entry (`beginLiveCursorScenario`).
73
+ *
63
74
  * Not in scope: this module never edits a production module and never
64
75
  * normalizes output. If a golden is not byte-stable, the volatile source is
65
76
  * controlled at its origin or the surprise is escalated — never redacted here.
@@ -151,6 +162,8 @@ export class ExecutionRecord {
151
162
  private readonly controlSignal: (status: AgentExecutionStatus) => ExecutionControlSignal;
152
163
  /** Every `updateStatus` payload, in order, snapshotted at write time. */
153
164
  readonly persisted: AgentExecutionStatus[] = [];
165
+ /** Fired after every full-status `applyStatusUpdate`; {@link whenToolCallsSettled} subscribes here. */
166
+ private readonly persistListeners = new Set<() => void>();
154
167
  /** Setup-progress labels reported before the stream started, in order. */
155
168
  readonly setupProgress: string[] = [];
156
169
  /**
@@ -205,6 +218,78 @@ export class ExecutionRecord {
205
218
  return this.execution.status?.messages.flatMap((m) => m.toolCalls) ?? [];
206
219
  }
207
220
 
221
+ /**
222
+ * Resolves once every one of `ids` is a SETTLED row (not RUNNING) in a status
223
+ * the activity has persisted — root messages and every sub-agent's alike.
224
+ * Resolves at once for an empty list or when they already are.
225
+ *
226
+ * This is the barrier a real model gives the runtime for free and a
227
+ * scripted one does not: a model that takes 300 ms to answer never starts
228
+ * its next turn before the loop has folded and persisted the tool results
229
+ * it is answering, while `ScriptedModel` answers instantly and LangGraph's
230
+ * producer can run a whole turn ahead of the consumer while the consumer
231
+ * awaits one mid-stream persist (on a git workspace that persist shells out
232
+ * for `file_change_progress`). The stamps the consumer writes at apply time
233
+ * then land on either side of the scripted clock's next tick depending on
234
+ * I/O timing — a golden that flips between `:01` and `:02` on the day. The
235
+ * driver awaits this before it ticks (`hermetic-deep-agent.ts`), so every
236
+ * stamp of turn N precedes turn N+1's instant, run after run.
237
+ *
238
+ * Why "settled" and not "seen": the row's `completedAt` is stamped when the
239
+ * tool's end event is applied, and the producer calls the next model turn
240
+ * right after emitting that event, so a barrier on the row merely existing
241
+ * would still race the completion stamp. The persist that carries the
242
+ * settled row always exists: a tool's finish forces one (the builder's
243
+ * force flag), and a turn followed by another turn always has a tool call.
244
+ *
245
+ * Bounded by real time (timers are real under the scripted clock): a
246
+ * scenario that breaks the fact above — a persist that never carries the
247
+ * row — fails naming the ids still open, never hangs.
248
+ */
249
+ whenToolCallsSettled(ids: readonly string[], timeoutMs = 10_000): Promise<void> {
250
+ const unsettled = (): string[] => {
251
+ const settled = this.settledToolCallIds();
252
+ return ids.filter((id) => !settled.has(id));
253
+ };
254
+ if (unsettled().length === 0) return Promise.resolve();
255
+
256
+ return new Promise<void>((resolve, reject) => {
257
+ const timer = setTimeout(() => {
258
+ this.persistListeners.delete(check);
259
+ reject(
260
+ new Error(
261
+ `ExecutionRecord: tool call(s) ${unsettled().join(", ")} never settled in a persisted status ` +
262
+ `within ${timeoutMs} ms (${this.persisted.length} persists seen). A model turn was asked to ` +
263
+ `wait for the results it answers; the loop never persisted them — a scenario or runtime bug.`,
264
+ ),
265
+ );
266
+ }, timeoutMs);
267
+ const check = (): void => {
268
+ if (unsettled().length > 0) return;
269
+ clearTimeout(timer);
270
+ this.persistListeners.delete(check);
271
+ resolve();
272
+ };
273
+ this.persistListeners.add(check);
274
+ });
275
+ }
276
+
277
+ /** The ids of every row not RUNNING in the held status, across the root and every sub-agent transcript. */
278
+ private settledToolCallIds(): Set<string> {
279
+ const out = new Set<string>();
280
+ const status = this.execution.status;
281
+ if (!status) return out;
282
+ const transcripts = [status.messages, ...status.subAgentExecutions.map((s) => s.messages)];
283
+ for (const messages of transcripts) {
284
+ for (const message of messages) {
285
+ for (const tc of message.toolCalls) {
286
+ if (tc.status !== ToolCallStatus.TOOL_CALL_RUNNING) out.add(tc.id);
287
+ }
288
+ }
289
+ }
290
+ return out;
291
+ }
292
+
208
293
  /** The tool calls currently paused for a decision. */
209
294
  waitingToolCalls(): ToolCall[] {
210
295
  return this.toolCalls().filter((tc) => tc.status === ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
@@ -254,6 +339,7 @@ export class ExecutionRecord {
254
339
  return ExecutionControlSignal.UNSPECIFIED;
255
340
  }
256
341
  this.execution.status = clone(AgentExecutionStatusSchema, snapshot);
342
+ for (const listener of [...this.persistListeners]) listener();
257
343
  return this.controlSignal(snapshot);
258
344
  }
259
345
 
@@ -343,14 +429,30 @@ export function hermeticStigmerClientModule(): { StigmerClient: new () => Stigme
343
429
  * (an absent store flips capture off — a different code path).
344
430
  * - `STIGMER_RUNNER_HITL_SECRET`: the one randomness source on the activity
345
431
  * path, pinned so grant tokens and fingerprints are byte-stable.
346
- * - `CURSOR_EVENT_RECORD_DIR` cleared: never write recordings from a test.
432
+ * - `CURSOR_EVENT_RECORD_DIR` cleared: never write recordings from a test —
433
+ * unless the caller names an `eventRecordingDir`, the live instruments'
434
+ * opt-in (below), in which case this environment SETS it. Either way the
435
+ * variable has one owner here, and dispose restores it.
347
436
  */
348
437
  const PINNED_ENV = {
349
438
  ARTIFACT_STORAGE_TYPE: "local",
350
439
  STIGMER_RUNNER_HITL_SECRET: "hermetic-fixture-secret-do-not-use-in-production",
351
440
  } as const;
352
441
 
353
- const CLEARED_ENV = ["CURSOR_EVENT_RECORD_DIR", "STIGMER_PROXY_ENDPOINT", "STIGMER_CLOUD_API_URL"] as const;
442
+ const CLEARED_ENV = ["STIGMER_PROXY_ENDPOINT", "STIGMER_CLOUD_API_URL"] as const;
443
+
444
+ /** The one name the Cursor recorder is switched on by (`turn-settle.ts` reads it). */
445
+ const CURSOR_EVENT_RECORD_DIR = "CURSOR_EVENT_RECORD_DIR";
446
+
447
+ export interface HermeticEnvironmentOptions {
448
+ /**
449
+ * Where the Cursor event recorder writes this run's raw SDK recording
450
+ * (`<dir>/<executionId>.cursor-events.jsonl`). Named ONLY by a live
451
+ * instrument, whose point is a recording of the real SDK; a hermetic
452
+ * scenario leaves it unset and the variable is cleared for the run.
453
+ */
454
+ readonly eventRecordingDir?: string;
455
+ }
354
456
 
355
457
  export interface HermeticEnvironment {
356
458
  /** The temp `HOME` (the runner's `~/.stigmer` lands under it). */
@@ -368,7 +470,7 @@ export interface HermeticEnvironment {
368
470
  * `beforeAll`) — the fingerprint secret is memoized per process on first use,
369
471
  * and vitest isolates files in forks, so per-file is the natural unit.
370
472
  */
371
- export function createHermeticEnvironment(): HermeticEnvironment {
473
+ export function createHermeticEnvironment(options: HermeticEnvironmentOptions = {}): HermeticEnvironment {
372
474
  const root = mkdtempSync(join(tmpdir(), "stigmer-hermetic-"));
373
475
  const home = join(root, "home");
374
476
  const workspaceRootDir = join(root, "workspaces");
@@ -384,6 +486,7 @@ export function createHermeticEnvironment(): HermeticEnvironment {
384
486
  set("LOCAL_ARTIFACT_PATH", artifactPath);
385
487
  for (const [k, v] of Object.entries(PINNED_ENV)) set(k, v);
386
488
  for (const k of CLEARED_ENV) set(k, undefined);
489
+ set(CURSOR_EVENT_RECORD_DIR, options.eventRecordingDir);
387
490
 
388
491
  let disposed = false;
389
492
  return {
@@ -411,15 +514,16 @@ export function createHermeticEnvironment(): HermeticEnvironment {
411
514
  * advance together; timers stay real (the periodic heartbeat, the stall
412
515
  * watchdog, `withTimeout` all need them). The harness double calls
413
516
  * {@link ScriptedClock.tick} once per script step, so every clock-based
414
- * decision on the activity path — status timestamps, the enricher's persist
415
- * debounce, cache TTLs (`Date`); the streaming scheduler's persist cadence
517
+ * decision on the activity path — status timestamps, the builder's row
518
+ * instants, cache TTLs (`Date`); the streaming scheduler's persist cadence
416
519
  * (`performance.now()`) — sees the same instants run after run and runs the
417
520
  * same branches production runs.
418
521
  *
419
- * Why both (S3 M5, Q-M5-7; F-M2a-20): until M5 only `Date` was faked, so the
522
+ * Why both (since #1096): until then only `Date` was faked, so the
420
523
  * scheduler paced persists on REAL elapsed time and `fileChangeProgress`,
421
524
  * which rides persists, could gain or lose a capture under load — a golden
422
- * flake seen twice across S3. One clock, two faces: `tick` advances through
525
+ * flake seen twice while the native adapter moved onto the runtime. One clock,
526
+ * two faces: `tick` advances through
423
527
  * the fake clock's own `tick` (`vi.advanceTimersByTime`), which moves both
424
528
  * `Date` and `performance.now()`; `vi.setSystemTime` would move `Date` alone
425
529
  * (fake-timers keeps `performance` monotonic across a system-time jump), so
@@ -23,6 +23,15 @@
23
23
  * never consulted and adding an entry is byte-invisible to the other
24
24
  * harness's goldens (verified when the native entry landed: Cursor's
25
25
  * seventeen unchanged).
26
+ *
27
+ * The live posture (`stubRegistryFetch({ live: true })`): a live instrument
28
+ * runs the real activity against the REAL `@cursor/sdk`, whose cloud-api
29
+ * client (`Cursor.models.list`, the user-key token exchange) goes through
30
+ * global `fetch` — only its Connect RPC stream rides connect-node. Such a run
31
+ * keeps this document for the registry (pricing and tier stay pinned; nothing
32
+ * a live turn proves lives there) and lets every other URL reach the network
33
+ * through the `fetch` that was global before the stub. The refusal stays the
34
+ * default: a hermetic run that grows a network dependency must still fail.
26
35
  */
27
36
 
28
37
  import { vi } from "vitest";
@@ -76,21 +85,36 @@ export const REGISTRY_DOCUMENT = {
76
85
  ],
77
86
  } as const;
78
87
 
88
+ export interface StubRegistryFetchOptions {
89
+ /**
90
+ * The live posture (header): every URL that is not the registry reaches the
91
+ * network through the pre-stub `fetch`. Default false — refuse, and fail the
92
+ * run.
93
+ */
94
+ readonly live?: boolean;
95
+ }
96
+
79
97
  /**
80
- * Stub `fetch` to answer the registry document and refuse everything else.
81
- * Returns the URLs fetched, for the "no other network" assertion.
98
+ * Stub `fetch` to answer the registry document and — by default — refuse
99
+ * everything else. Returns the URLs fetched, for the "no other network"
100
+ * assertion (and, in the live posture, for the record of what a real turn
101
+ * dialled).
82
102
  */
83
- export function stubRegistryFetch(): { readonly urls: string[]; restore(): void } {
103
+ export function stubRegistryFetch(options: StubRegistryFetchOptions = {}): { readonly urls: string[]; restore(): void } {
84
104
  const urls: string[] = [];
105
+ const networkFetch = globalThis.fetch;
85
106
  vi.stubGlobal(
86
107
  "fetch",
87
- vi.fn(async (input: string | URL | Request) => {
108
+ vi.fn(async (input: string | URL | Request, init?: RequestInit) => {
88
109
  const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
89
110
  urls.push(url);
90
- if (!url.includes("/model-registry")) {
91
- throw new Error(`hermetic run attempted a non-registry network call: ${url}`);
111
+ if (url.includes("/model-registry")) {
112
+ return { ok: true, status: 200, json: async () => REGISTRY_DOCUMENT } as unknown as Response;
113
+ }
114
+ if (options.live) {
115
+ return networkFetch(input, init);
92
116
  }
93
- return { ok: true, status: 200, json: async () => REGISTRY_DOCUMENT } as unknown as Response;
117
+ throw new Error(`hermetic run attempted a non-registry network call: ${url}`);
94
118
  }),
95
119
  );
96
120
  return { urls, restore: () => vi.unstubAllGlobals() };
@@ -2,11 +2,14 @@
2
2
  * The module specifiers a TypeScript file names, read off its syntax tree —
3
3
  * the primitive behind the runner's import fences.
4
4
  *
5
- * Two fences stand on it: `src/harness/` and production `src/shared/` never
5
+ * Three fences stand on it: `src/harness/` and production `src/shared/` never
6
6
  * import `src/activities/` (`harness/__tests__/import-direction.test.ts`,
7
- * the rule the turn runtime is written under), and the Cursor adapter never
7
+ * the rule the turn runtime is written under); the Cursor adapter never
8
8
  * imports `@temporalio/*` (`execute-cursor/__tests__/adapter-is-temporal-
9
- * free.test.ts`, what lets the contract kit run it outside an activity).
9
+ * free.test.ts`, what lets the contract kit run it outside an activity); and
10
+ * `src/harness/transcript/` names no package outside a short allow-list
11
+ * (`harness/__tests__/transcript-is-engine-free.test.ts`, what keeps the one
12
+ * transcript builder engine-neutral).
10
13
  *
11
14
  * A syntax tree, not a regex, so a path quoted in a header comment cannot
12
15
  * trip a fence, and no import form can slip past one: static, type-only,
@@ -84,6 +87,32 @@ export function packageImports(filePath: string, source: string, pkg: string): s
84
87
  return moduleSpecifiers(filePath, source).filter((specifier) => specifier === pkg || specifier.startsWith(`${pkg}/`));
85
88
  }
86
89
 
90
+ /**
91
+ * The packages `source` names — every specifier that is neither a relative
92
+ * path nor a `node:` builtin, reduced to its package (`@scope/name` or
93
+ * `name`, the subpath dropped), deduplicated, in first-appearance order.
94
+ *
95
+ * What an ALLOW-list fence compares against, where `packageImports` serves a
96
+ * deny: a deny-list must be extended for every SDK that does not exist yet,
97
+ * an allow-list refuses it on the day it is written.
98
+ */
99
+ export function packageNames(filePath: string, source: string): string[] {
100
+ const names = new Set<string>();
101
+ for (const specifier of moduleSpecifiers(filePath, source)) {
102
+ if (specifier.startsWith(".") || specifier.startsWith("node:")) continue;
103
+ names.add(packageName(specifier));
104
+ }
105
+ return [...names];
106
+ }
107
+
108
+ /** `@scope/name/sub/path` → `@scope/name`; `name/sub/path` → `name`. */
109
+ function packageName(specifier: string): string {
110
+ const parts = specifier.split("/");
111
+ const [first, second] = parts;
112
+ if (first === undefined) return specifier;
113
+ return first.startsWith("@") && second !== undefined ? `${first}/${second}` : first;
114
+ }
115
+
87
116
  /** Every `.ts` file under `dir`, sorted, skipping the named directories at any depth. */
88
117
  export function typeScriptFilesUnder(dir: string, skipDirectories: ReadonlySet<string>): string[] {
89
118
  const files: string[] = [];
@@ -20,7 +20,7 @@
20
20
  * `adapter.ts`'s graph, or a static client import on the registry's, fails
21
21
  * the first arm; a `boot` that stopped asserting fails the second.
22
22
  *
23
- * The table has two rows since S3 M2b (2026-09-13): the child boots the
23
+ * The table has two rows since #1096 (2026-09-13): the child boots the
24
24
  * Cursor adapter and then the native deep-agent adapter, whose `boot`
25
25
  * registers the deepagents profiles and loads LangChain — after the patch,
26
26
  * because it is the second row. A `boot` that reordered the rows, or a
@@ -13,7 +13,7 @@
13
13
  * halves in its own hermetic file, where the mocks its double needs can be
14
14
  * hoisted: the Cursor adapter does so in
15
15
  * `activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts`
16
- * (S2 M4), and the fake's runtime-side half runs in
16
+ * (since #1070), and the fake's runtime-side half runs in
17
17
  * `harness/__tests__/run-turn.test.ts`.
18
18
  */
19
19
 
@@ -33,7 +33,7 @@ describe("mapManagerOptionsToConfig", () => {
33
33
  expect(config.checkpointerType).toBe("http");
34
34
  });
35
35
 
36
- it("binds the proxy credential to the runner-token ref, never the control-plane ref (Q-S2-7)", () => {
36
+ it("binds the proxy credential to the runner-token ref, never the control-plane ref", () => {
37
37
  const tokenRef = { current: "control-plane-token" };
38
38
  const runnerTokenRef = { current: "control-plane-token" };
39
39
  const config = mapManagerOptionsToConfig(
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The scripted `@cursor/sdk` double's own arms — the surface the harness
3
- * contract kit's Cursor subject drives, added at S2 M4 beside the golden
3
+ * contract kit's Cursor subject drives, added in #1070 beside the golden
4
4
  * scenarios' declared-up-front surface (which the seventeen goldens prove).
5
5
  *
6
6
  * - `arrangeNextTurn` writes the slot the NEXT `send()` consumes, replaces a
@@ -9,17 +9,20 @@
9
9
  * - `nextRunId` names the run the next send will be given, so a script can be
10
10
  * built with matching event ids before the send happens.
11
11
  * - `agentForWorkspaceRef` hands `Agent.create` the agent declared for the
12
- * session the create names (via `platform.workspaceRef`), consulted before
13
- * the ordered list and registered for `Agent.resume` by id; a create with no
14
- * session, or a session the resolver does not know, falls through to the
15
- * list as before.
12
+ * session the create names (via the `workspaceRef` of the scripted store it
13
+ * passes as `local.store`), consulted before the ordered list and
14
+ * registered for `Agent.resume` by id; a create with no store, or a session
15
+ * the resolver does not know, falls through to the list as before.
16
+ * - `ScriptedLocalAgentStore.open` (the `@cursor/sdk/sqlite` double) records
17
+ * every open and dispose on the bound SDK, so a scenario can assert the
18
+ * adapter's session-store lifetime.
16
19
  */
17
20
 
18
21
  import { describe, it, expect } from "vitest";
19
22
  import type { AgentOptions, SDKMessage } from "@cursor/sdk";
20
23
 
21
24
  import { ScriptedCursorAgent, defaultRunId, sdkEvents, step } from "../scripted-agent.js";
22
- import { ScriptedCursorSdk } from "../scripted-sdk.js";
25
+ import { ScriptedCursorSdk, ScriptedLocalAgentStore, bindScriptedSdk } from "../scripted-sdk.js";
23
26
 
24
27
  /** An agent whose declared turns each play the given events and finish. */
25
28
  function agent(id: string, turns: SDKMessage[][] = []): ScriptedCursorAgent {
@@ -76,8 +79,14 @@ describe("ScriptedCursorAgent.nextRunId", () => {
76
79
  });
77
80
 
78
81
  describe("ScriptedCursorSdk.agentForWorkspaceRef", () => {
82
+ // The scripted store carries only the members the runner touches, not the
83
+ // SDK's whole `LocalAgentStore`, hence the cast — the same one the mocked
84
+ // module makes when it stands in for `@cursor/sdk/sqlite`.
79
85
  const createFor = (workspaceRef: string | undefined): AgentOptions =>
80
- ({ apiKey: "k", ...(workspaceRef !== undefined ? { platform: { workspaceRef, stateRoot: "/tmp/x" } } : {}) }) as unknown as AgentOptions;
86
+ ({
87
+ apiKey: "k",
88
+ local: workspaceRef !== undefined ? { store: new ScriptedLocalAgentStore(workspaceRef, "/tmp/x") } : undefined,
89
+ }) as unknown as AgentOptions;
81
90
 
82
91
  it("hands out the session's agent before the ordered list and registers it for resume", () => {
83
92
  const forSession = agent("session-agent");
@@ -94,9 +103,24 @@ describe("ScriptedCursorSdk.agentForWorkspaceRef", () => {
94
103
  expect(sdk.resolutions.map((r) => `${r.kind}:${r.agentId}`)).toEqual(["create:session-agent", "resume:session-agent", "create:listed-agent"]);
95
104
  });
96
105
 
97
- it("a create without a platform ref falls through to the ordered list", () => {
106
+ it("a create without a store falls through to the ordered list", () => {
98
107
  const listed = agent("listed-only");
99
108
  const sdk = new ScriptedCursorSdk({ agents: [listed], catalog: [], agentForWorkspaceRef: () => agent("never") });
100
109
  expect(sdk.create(createFor(undefined))).toBe(listed);
101
110
  });
102
111
  });
112
+
113
+ describe("ScriptedLocalAgentStore (the @cursor/sdk/sqlite double)", () => {
114
+ it("open hands out a store carrying the ref and root asked for, and records opens and disposes on the bound SDK", async () => {
115
+ const sdk = new ScriptedCursorSdk({ agents: [], catalog: [] });
116
+ bindScriptedSdk(sdk);
117
+
118
+ const store = await ScriptedLocalAgentStore.open({ workspaceRef: "stigmer-session:ses-9", stateRoot: "/tmp/ses-9" });
119
+ expect(store.workspaceRef).toBe("stigmer-session:ses-9");
120
+ expect(store.stateRoot).toBe("/tmp/ses-9");
121
+ expect(sdk.stores).toEqual([store]);
122
+
123
+ await store.dispose();
124
+ expect(store.disposeCalls).toBe(1);
125
+ });
126
+ });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The Cursor harness as a `HarnessContractSubject` — the REAL `createCursorAdapter()`
3
3
  * driven through the harness contract kit, with the kit's engine-neutral
4
- * scenario vocabulary translated onto S0's scripted `@cursor/sdk` double.
4
+ * scenario vocabulary translated onto the scripted `@cursor/sdk` double (#1048).
5
5
  *
6
6
  * The sibling of `gateway-substrate.ts` (the Cursor side of the approval
7
7
  * contract kit): the kit owns the invariants and the runtime stand-in, this
@@ -59,7 +59,7 @@
59
59
  * Every script that does not end in `fail`, `cancelled` or `hang` closes with
60
60
  * `finished({ result: <last say> })` so `run.wait()` has an answer.
61
61
  *
62
- * ── Two modelling decisions (S2 M4, Q-M4-4) ─────────────────────────────────
62
+ * ── Two modelling decisions (#1070) ─────────────────────────────────────────
63
63
  * A `propose` for an id this subject has already seen the hook ALLOW emits
64
64
  * nothing: the real SDK does not re-run a completed tool on resume — the parked
65
65
  * agent's conversation already holds the tool's result. A `propose` whose id
@@ -95,7 +95,7 @@ import type {
95
95
  } from "../../../__test-utils__/harness-contract/types.js";
96
96
  import type { HermeticEnvironment } from "../../../__test-utils__/hermetic-activity.js";
97
97
  import { createCursorAdapter } from "../adapter.js";
98
- import { resolvePlatformOptions } from "../session-lifecycle.js";
98
+ import { resolveSessionStoreLocation } from "../session-store.js";
99
99
  import { hookInputFor, streamArgsFor, STREAM_NAME, type GatedActionKind } from "./cursor-hook-harness.js";
100
100
  import { FIXTURE, SDK_CATALOG, hermeticCursorConfig, runWorkspaceHook } from "./hermetic-cursor.js";
101
101
  import { ScriptedCursorAgent, sdkEvents, step, type ScriptStep, type ScriptedRun } from "./scripted-agent.js";
@@ -216,9 +216,9 @@ class CursorSubject implements CursorContractSubject {
216
216
  private mintAgent(sessionId: string): ScriptedCursorAgent {
217
217
  const agent = new ScriptedCursorAgent({ agentId: `agent-kit-${++this.mintCounter}`, turns: [] });
218
218
  this.agents.set(sessionId, agent);
219
- // The adapter asks the SDK to create an agent for the session under this
220
- // ref; the double answers with this agent.
221
- const { workspaceRef } = resolvePlatformOptions(sessionId, this.config.workspaceRootDir);
219
+ // The adapter asks the SDK to create an agent for the session over a store
220
+ // opened under this ref; the double answers with this agent.
221
+ const { workspaceRef } = resolveSessionStoreLocation(sessionId, this.config.workspaceRootDir);
222
222
  this.agentsByWorkspaceRef.set(workspaceRef, agent);
223
223
  return agent;
224
224
  }
@@ -248,11 +248,13 @@ class CursorSubject implements CursorContractSubject {
248
248
  break;
249
249
  case "usage":
250
250
  script.push(
251
+ // The kit's delta leaves a count optional; the SDK's `turn-ended`
252
+ // states all four, and an absent count is zero, as the pricer reads it.
251
253
  step.turnEnded({
252
- inputTokens: s.delta.inputTokens,
253
- outputTokens: s.delta.outputTokens,
254
- cacheReadTokens: s.delta.cacheReadTokens,
255
- cacheWriteTokens: s.delta.cacheWriteTokens,
254
+ inputTokens: s.delta.inputTokens ?? 0,
255
+ outputTokens: s.delta.outputTokens ?? 0,
256
+ cacheReadTokens: s.delta.cacheReadTokens ?? 0,
257
+ cacheWriteTokens: s.delta.cacheWriteTokens ?? 0,
256
258
  }),
257
259
  );
258
260
  break;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Drive Cursor SDK events through the REAL pair the harness runs in production
3
+ * — `CursorTranslator` into `TranscriptBuilder` over an `AgentExecutionStatus`
4
+ * — and hand back what they built. The unit-test seam that replaced
5
+ * `new <accumulator>(messages, options)` in #1097 (the accumulator folded
6
+ * rows itself; now the translator emits events and the shared builder folds
7
+ * them), so the arms that read a row's shape after a sequence of SDK events
8
+ * keep their shape while the module under them changed.
9
+ *
10
+ * The same shape as the native translator's local `feedAll` in
11
+ * `execute-deep-agent/__tests__/translator-through-builder.test.ts`, shared
12
+ * here because seven Cursor test files read through it.
13
+ *
14
+ * Deltas follow the loop's discipline (`turn-stream.ts`): `observeDelta`
15
+ * queues, and the queue is drained into the builder after the NEXT stream
16
+ * event — or at {@link CursorFold.drain} for a caller that wants them applied
17
+ * now, the settle's own last drain before `finalize()`.
18
+ */
19
+
20
+ import { create } from "@bufbuild/protobuf";
21
+ import type { InteractionUpdate, SDKMessage } from "@cursor/sdk";
22
+ import { AgentExecutionStatusSchema, type AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
23
+ import type { AgentMessage, ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
24
+ import { TranscriptBuilder } from "../../../harness/transcript/builder.js";
25
+ import { CursorTranslator, type CursorTranslatorOptions } from "../translator.js";
26
+
27
+ export interface CursorFoldOptions {
28
+ /** The status to build into; a fresh empty one when omitted. Its `messages` are the translator's seed. */
29
+ readonly status?: AgentExecutionStatus;
30
+ /**
31
+ * An existing `messages` array to build into BY REFERENCE (a test that
32
+ * hand-built its seeded rows and keeps asserting on the same objects). Wins
33
+ * over `status`'s own array.
34
+ */
35
+ readonly messages?: AgentMessage[];
36
+ readonly policies?: CursorTranslatorOptions["policies"];
37
+ readonly leases?: CursorTranslatorOptions["leases"];
38
+ }
39
+
40
+ export class CursorFold {
41
+ readonly status: AgentExecutionStatus;
42
+ readonly translator: CursorTranslator;
43
+ readonly builder: TranscriptBuilder;
44
+
45
+ constructor(options: CursorFoldOptions = {}) {
46
+ this.status = options.status ?? create(AgentExecutionStatusSchema, {});
47
+ if (options.messages) this.status.messages = options.messages;
48
+ this.translator = new CursorTranslator({
49
+ policies: options.policies ?? new Map(),
50
+ leases: options.leases ?? { global: false, categories: new Set() },
51
+ seeded: this.status.messages,
52
+ });
53
+ this.builder = new TranscriptBuilder("exec-fold", this.status);
54
+ }
55
+
56
+ /** One stream event, translated and folded, then the deltas queued so far — the loop's order. */
57
+ event(event: SDKMessage): this {
58
+ for (const e of this.translator.translate(event)) this.builder.apply(e);
59
+ this.drain();
60
+ return this;
61
+ }
62
+
63
+ events(...events: SDKMessage[]): this {
64
+ for (const e of events) this.event(e);
65
+ return this;
66
+ }
67
+
68
+ /** One delta, queued — applied at the next {@link event} or {@link drain}. */
69
+ delta(update: InteractionUpdate): this {
70
+ this.translator.observeDelta(update);
71
+ return this;
72
+ }
73
+
74
+ drain(): this {
75
+ for (const e of this.translator.drainDeltas()) this.builder.apply(e);
76
+ return this;
77
+ }
78
+
79
+ finalize(): this {
80
+ this.drain();
81
+ this.builder.finalize();
82
+ return this;
83
+ }
84
+
85
+ get messages(): AgentMessage[] {
86
+ return this.status.messages;
87
+ }
88
+
89
+ /** Every root row, in transcript order. */
90
+ rows(): ToolCall[] {
91
+ return this.status.messages.flatMap((m) => m.toolCalls);
92
+ }
93
+
94
+ row(callId: string): ToolCall {
95
+ const found = this.rows().find((tc) => tc.id === callId);
96
+ if (!found) throw new Error(`CursorFold: no row ${callId}`);
97
+ return found;
98
+ }
99
+ }
100
+
101
+ /** Fold a sequence of stream events into a fresh transcript; the common one-liner. */
102
+ export function foldCursorEvents(events: readonly SDKMessage[], options: CursorFoldOptions = {}): CursorFold {
103
+ return new CursorFold(options).events(...events);
104
+ }
105
+
106
+ /**
107
+ * A builder over an existing `messages` array, kept BY REFERENCE (the status
108
+ * is given the array itself, never a copy), so a boundary test that built its
109
+ * rows by hand can hand them to `reconcileDeniedToolCalls` and keep asserting
110
+ * on the same objects.
111
+ */
112
+ export function builderOver(messages: AgentMessage[], executionId = "exec-boundary"): TranscriptBuilder {
113
+ const status = create(AgentExecutionStatusSchema, {});
114
+ status.messages = messages;
115
+ return new TranscriptBuilder(executionId, status);
116
+ }
@@ -80,7 +80,7 @@ export function createCursorSubstrate(): GatewaySubstrate {
80
80
  enforcesExactContent: true,
81
81
  appliesRunLifetimeLease: true,
82
82
  // The Cursor hook's deny decision does not carry approval_policy_source;
83
- // provenance is projected at message-reconstruction time (message-translator)
83
+ // provenance is projected at translation time (the Cursor translator)
84
84
  // and asserted by the corpus + resolveApprovalProvenance suites instead.
85
85
  surfacesGatePolicySource: false,
86
86
  },