@stigmer/runner 3.14.0 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/__test-utils__/execution-record-fixture.d.ts +47 -0
  3. package/dist/__test-utils__/execution-record-fixture.js +68 -0
  4. package/dist/__test-utils__/execution-record-fixture.js.map +1 -0
  5. package/dist/__test-utils__/harness-contract/types.d.ts +166 -0
  6. package/dist/__test-utils__/harness-contract/types.js +49 -0
  7. package/dist/__test-utils__/harness-contract/types.js.map +1 -0
  8. package/dist/__test-utils__/hermetic-activity.d.ts +268 -0
  9. package/dist/__test-utils__/hermetic-activity.js +385 -0
  10. package/dist/__test-utils__/hermetic-activity.js.map +1 -0
  11. package/dist/__test-utils__/mock-client.d.ts +13 -0
  12. package/dist/__test-utils__/mock-client.js +45 -0
  13. package/dist/__test-utils__/mock-client.js.map +1 -0
  14. package/dist/__test-utils__/mock-workspace.d.ts +15 -0
  15. package/dist/__test-utils__/mock-workspace.js +21 -0
  16. package/dist/__test-utils__/mock-workspace.js.map +1 -0
  17. package/dist/__test-utils__/model-registry-fixture.d.ts +56 -0
  18. package/dist/__test-utils__/model-registry-fixture.js +66 -0
  19. package/dist/__test-utils__/model-registry-fixture.js.map +1 -0
  20. package/dist/__test-utils__/turn-input-fixture.d.ts +60 -0
  21. package/dist/__test-utils__/turn-input-fixture.js +118 -0
  22. package/dist/__test-utils__/turn-input-fixture.js.map +1 -0
  23. package/dist/activities/execute-cursor/__test-utils__/contract-subject.d.ts +107 -0
  24. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +344 -0
  25. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -0
  26. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +20 -0
  27. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +58 -0
  28. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -1
  29. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +2 -32
  30. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -1
  31. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +148 -0
  32. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +257 -0
  33. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -0
  34. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +183 -0
  35. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +262 -0
  36. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -0
  37. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.d.ts +138 -0
  38. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js +191 -0
  39. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js.map +1 -0
  40. package/dist/activities/execute-cursor/adapter.d.ts +55 -0
  41. package/dist/activities/execute-cursor/adapter.js +119 -0
  42. package/dist/activities/execute-cursor/adapter.js.map +1 -0
  43. package/dist/activities/execute-cursor/approval-state.d.ts +2 -2
  44. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  45. package/dist/activities/execute-cursor/capture-flow.d.ts +15 -30
  46. package/dist/activities/execute-cursor/capture-flow.js +17 -26
  47. package/dist/activities/execute-cursor/capture-flow.js.map +1 -1
  48. package/dist/activities/execute-cursor/cursor-capabilities.d.ts +23 -0
  49. package/dist/activities/execute-cursor/cursor-capabilities.js +33 -0
  50. package/dist/activities/execute-cursor/cursor-capabilities.js.map +1 -0
  51. package/dist/activities/execute-cursor/cursor-mcp-config.d.ts +2 -2
  52. package/dist/activities/execute-cursor/cursor-mcp-config.js.map +1 -1
  53. package/dist/activities/execute-cursor/fetch-interceptor.d.ts +15 -25
  54. package/dist/activities/execute-cursor/fetch-interceptor.js +39 -33
  55. package/dist/activities/execute-cursor/fetch-interceptor.js.map +1 -1
  56. package/dist/activities/execute-cursor/http2-interceptor.d.ts +21 -17
  57. package/dist/activities/execute-cursor/http2-interceptor.js +38 -31
  58. package/dist/activities/execute-cursor/http2-interceptor.js.map +1 -1
  59. package/dist/activities/execute-cursor/message-translator.d.ts +28 -19
  60. package/dist/activities/execute-cursor/message-translator.js +76 -27
  61. package/dist/activities/execute-cursor/message-translator.js.map +1 -1
  62. package/dist/activities/execute-cursor/prompt-builder.d.ts +180 -11
  63. package/dist/activities/execute-cursor/prompt-builder.js +171 -0
  64. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  65. package/dist/activities/execute-cursor/rejection-capture.d.ts +3 -6
  66. package/dist/activities/execute-cursor/rejection-capture.js +5 -7
  67. package/dist/activities/execute-cursor/rejection-capture.js.map +1 -1
  68. package/dist/activities/execute-cursor/session-lifecycle.d.ts +1 -4
  69. package/dist/activities/execute-cursor/session-lifecycle.js.map +1 -1
  70. package/dist/activities/execute-cursor/turn-boundary.d.ts +1 -1
  71. package/dist/activities/execute-cursor/turn-settle.d.ts +49 -0
  72. package/dist/activities/execute-cursor/turn-settle.js +507 -0
  73. package/dist/activities/execute-cursor/turn-settle.js.map +1 -0
  74. package/dist/activities/execute-cursor/turn-setup.d.ts +173 -0
  75. package/dist/activities/execute-cursor/turn-setup.js +496 -0
  76. package/dist/activities/execute-cursor/turn-setup.js.map +1 -0
  77. package/dist/activities/execute-cursor/turn-stream.d.ts +41 -71
  78. package/dist/activities/execute-cursor/turn-stream.js +70 -135
  79. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  80. package/dist/activities/execute-cursor/turn.d.ts +43 -0
  81. package/dist/activities/execute-cursor/turn.js +222 -0
  82. package/dist/activities/execute-cursor/turn.js.map +1 -0
  83. package/dist/activities/execute-cursor/usage-pricing.d.ts +38 -0
  84. package/dist/activities/execute-cursor/usage-pricing.js +50 -0
  85. package/dist/activities/execute-cursor/usage-pricing.js.map +1 -0
  86. package/dist/activities/execute-deep-agent/approval-file-change.d.ts +1 -1
  87. package/dist/activities/execute-deep-agent/approval-file-change.js +1 -1
  88. package/dist/activities/execute-deep-agent/environment.js +1 -1
  89. package/dist/activities/execute-deep-agent/environment.js.map +1 -1
  90. package/dist/activities/execute-deep-agent/setup.js +3 -2
  91. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  92. package/dist/activities/hydrate-workflow-execution.js +1 -1
  93. package/dist/activities/hydrate-workflow-execution.js.map +1 -1
  94. package/dist/config.d.ts +15 -0
  95. package/dist/config.js.map +1 -1
  96. package/dist/harness/capabilities.d.ts +87 -0
  97. package/dist/harness/capabilities.js +37 -0
  98. package/dist/harness/capabilities.js.map +1 -0
  99. package/dist/harness/persist-chokepoint.d.ts +74 -0
  100. package/dist/harness/persist-chokepoint.js +88 -0
  101. package/dist/harness/persist-chokepoint.js.map +1 -0
  102. package/dist/harness/registry.d.ts +106 -0
  103. package/dist/harness/registry.js +168 -0
  104. package/dist/harness/registry.js.map +1 -0
  105. package/dist/harness/run-turn.d.ts +53 -0
  106. package/dist/harness/run-turn.js +528 -0
  107. package/dist/harness/run-turn.js.map +1 -0
  108. package/dist/harness/stop-controller.d.ts +50 -0
  109. package/dist/harness/stop-controller.js +62 -0
  110. package/dist/harness/stop-controller.js.map +1 -0
  111. package/dist/harness/terminal-table.d.ts +141 -0
  112. package/dist/harness/terminal-table.js +239 -0
  113. package/dist/harness/terminal-table.js.map +1 -0
  114. package/dist/harness/turn-context.d.ts +523 -0
  115. package/dist/harness/turn-context.js +739 -0
  116. package/dist/harness/turn-context.js.map +1 -0
  117. package/dist/harness/types.d.ts +448 -0
  118. package/dist/harness/types.js +64 -0
  119. package/dist/harness/types.js.map +1 -0
  120. package/dist/harness/usage-accumulator.d.ts +68 -0
  121. package/dist/harness/usage-accumulator.js +101 -0
  122. package/dist/harness/usage-accumulator.js.map +1 -0
  123. package/dist/harness-adapters.d.ts +27 -0
  124. package/dist/harness-adapters.js +28 -0
  125. package/dist/harness-adapters.js.map +1 -0
  126. package/dist/main.js.map +1 -1
  127. package/dist/runner-manager.js +64 -48
  128. package/dist/runner-manager.js.map +1 -1
  129. package/dist/runner.d.ts +5 -4
  130. package/dist/runner.js +65 -64
  131. package/dist/runner.js.map +1 -1
  132. package/dist/shared/approval-policy.d.ts +1 -1
  133. package/dist/shared/approval-policy.js.map +1 -1
  134. package/dist/{activities/execute-cursor → shared}/attachment-resolver.d.ts +2 -2
  135. package/dist/{activities/execute-cursor → shared}/attachment-resolver.js +5 -5
  136. package/dist/shared/attachment-resolver.js.map +1 -0
  137. package/dist/{activities/execute-cursor → shared}/blueprint-resolver.d.ts +7 -5
  138. package/dist/{activities/execute-cursor → shared}/blueprint-resolver.js +3 -63
  139. package/dist/shared/blueprint-resolver.js.map +1 -0
  140. package/dist/shared/channel-attachment.d.ts +1 -1
  141. package/dist/shared/channel-attachment.js.map +1 -1
  142. package/dist/{activities/execute-cursor → shared}/cost-guard.d.ts +5 -1
  143. package/dist/{activities/execute-cursor → shared}/cost-guard.js +5 -1
  144. package/dist/shared/cost-guard.js.map +1 -0
  145. package/dist/{activities/execute-cursor → shared}/env-resolver.d.ts +1 -1
  146. package/dist/shared/env-resolver.js.map +1 -0
  147. package/dist/{activities/execute-cursor → shared}/exact-apply.d.ts +1 -1
  148. package/dist/{activities/execute-cursor → shared}/exact-apply.js +6 -6
  149. package/dist/shared/exact-apply.js.map +1 -0
  150. package/dist/shared/execution-context.d.ts +35 -0
  151. package/dist/shared/execution-context.js +38 -0
  152. package/dist/shared/execution-context.js.map +1 -0
  153. package/dist/{activities/execute-cursor → shared}/extract-structured-output.d.ts +5 -2
  154. package/dist/{activities/execute-cursor → shared}/extract-structured-output.js +9 -6
  155. package/dist/shared/extract-structured-output.js.map +1 -0
  156. package/dist/shared/filereview/capture.d.ts +1 -1
  157. package/dist/shared/filereview/capture.js +1 -1
  158. package/dist/shared/heartbeat.d.ts +13 -11
  159. package/dist/shared/heartbeat.js +14 -29
  160. package/dist/shared/heartbeat.js.map +1 -1
  161. package/dist/{activities/execute-cursor → shared}/persist-decision.d.ts +5 -2
  162. package/dist/{activities/execute-cursor → shared}/persist-decision.js +4 -1
  163. package/dist/shared/persist-decision.js.map +1 -0
  164. package/dist/{activities/execute-cursor → shared}/skill-resolver.d.ts +16 -3
  165. package/dist/{activities/execute-cursor → shared}/skill-resolver.js +12 -4
  166. package/dist/shared/skill-resolver.js.map +1 -0
  167. package/dist/shared/subagent-rows.d.ts +21 -0
  168. package/dist/shared/subagent-rows.js +34 -0
  169. package/dist/shared/subagent-rows.js.map +1 -0
  170. package/dist/shared/worker-shutdown.d.ts +43 -16
  171. package/dist/shared/worker-shutdown.js +28 -13
  172. package/dist/shared/worker-shutdown.js.map +1 -1
  173. package/dist/shared/workspace/platform-dir.d.ts +1 -1
  174. package/dist/shared/workspace/platform-dir.js +1 -1
  175. package/dist/shared/workspace/session-provision.d.ts +54 -0
  176. package/dist/{activities/execute-cursor/workspace-provision.js → shared/workspace/session-provision.js} +15 -12
  177. package/dist/shared/workspace/session-provision.js.map +1 -0
  178. package/package.json +4 -4
  179. package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +325 -0
  180. package/src/__test-utils__/config-fixture.ts +63 -0
  181. package/src/__test-utils__/execution-record-fixture.ts +104 -0
  182. package/src/__test-utils__/harness-boot-order-child.ts +58 -0
  183. package/src/__test-utils__/harness-contract/contract.ts +581 -0
  184. package/src/__test-utils__/harness-contract/recording-sink.ts +107 -0
  185. package/src/__test-utils__/harness-contract/runtime-contract.ts +666 -0
  186. package/src/__test-utils__/harness-contract/scripted-adapter.ts +349 -0
  187. package/src/__test-utils__/harness-contract/types.ts +176 -0
  188. package/src/__test-utils__/hermetic-activity.ts +502 -0
  189. package/src/__test-utils__/model-registry-fixture.ts +72 -0
  190. package/src/__test-utils__/module-specifiers.ts +82 -0
  191. package/src/__test-utils__/proto-helpers.ts +25 -0
  192. package/src/__test-utils__/turn-input-fixture.ts +155 -0
  193. package/src/__tests__/harness-boot-order.test.ts +77 -0
  194. package/src/__tests__/harness-contract.test.ts +27 -0
  195. package/src/__tests__/runner-manager.test.ts +18 -0
  196. package/src/activities/execute-cursor/__test-utils__/__tests__/scripted-double.test.ts +102 -0
  197. package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +396 -0
  198. package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +66 -0
  199. package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +2 -43
  200. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +342 -0
  201. package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +368 -0
  202. package/src/activities/execute-cursor/__test-utils__/scripted-sdk.ts +254 -0
  203. package/src/activities/execute-cursor/__tests__/adapter-is-temporal-free.test.ts +45 -0
  204. package/src/activities/execute-cursor/__tests__/approval-decisions-agree.test.ts +90 -0
  205. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +7 -8
  206. package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +28 -1
  207. package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +1 -1
  208. package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +64 -6
  209. package/src/activities/execute-cursor/__tests__/hermetic/deny-and-retry.test.ts +228 -0
  210. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +180 -0
  211. package/src/activities/execute-cursor/__tests__/hermetic/goldens/cost-cap.status.json +53 -0
  212. package/src/activities/execute-cursor/__tests__/hermetic/goldens/deny-and-retry.turn1.status.json +55 -0
  213. package/src/activities/execute-cursor/__tests__/hermetic/goldens/deny-and-retry.turn2.status.json +77 -0
  214. package/src/activities/execute-cursor/__tests__/hermetic/goldens/file-review-capture.status.json +126 -0
  215. package/src/activities/execute-cursor/__tests__/hermetic/goldens/pause.status.json +40 -0
  216. package/src/activities/execute-cursor/__tests__/hermetic/goldens/plain-turn.status.json +48 -0
  217. package/src/activities/execute-cursor/__tests__/hermetic/goldens/platform-stop.status.json +56 -0
  218. package/src/activities/execute-cursor/__tests__/hermetic/goldens/recovery-fresh-agent.status.json +53 -0
  219. package/src/activities/execute-cursor/__tests__/hermetic/goldens/recovery-transport-timeout.status.json +48 -0
  220. package/src/activities/execute-cursor/__tests__/hermetic/goldens/resolution-error.status.json +18 -0
  221. package/src/activities/execute-cursor/__tests__/hermetic/goldens/run-cancelled.status.json +36 -0
  222. package/src/activities/execute-cursor/__tests__/hermetic/goldens/run-error-non-retryable.status.json +37 -0
  223. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-error-at-create.status.json +38 -0
  224. package/src/activities/execute-cursor/__tests__/hermetic/goldens/stall.status.json +59 -0
  225. package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-call.status.json +68 -0
  226. package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +79 -0
  227. package/src/activities/execute-cursor/__tests__/hermetic/goldens/worker-shutdown.status.json +42 -0
  228. package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +170 -0
  229. package/src/activities/execute-cursor/__tests__/hermetic/pause-vs-shutdown.test.ts +205 -0
  230. package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +171 -0
  231. package/src/activities/execute-cursor/__tests__/hermetic/recovery-fresh-agent.test.ts +156 -0
  232. package/src/activities/execute-cursor/__tests__/hermetic/run-wait-arms.test.ts +286 -0
  233. package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +332 -0
  234. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +193 -0
  235. package/src/activities/execute-cursor/__tests__/hermetic/tool-call.test.ts +137 -0
  236. package/src/activities/execute-cursor/__tests__/hermetic/unattributed-hook-block.test.ts +185 -0
  237. package/src/activities/execute-cursor/__tests__/hermetic/workspace-lock-timeout.test.ts +151 -0
  238. package/src/activities/execute-cursor/__tests__/http2-interceptor.test.ts +65 -35
  239. package/src/activities/execute-cursor/__tests__/message-translator.test.ts +0 -40
  240. package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +171 -0
  241. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +118 -243
  242. package/src/activities/execute-cursor/__tests__/usage-pricing.test.ts +84 -0
  243. package/src/activities/execute-cursor/adapter.ts +138 -0
  244. package/src/activities/execute-cursor/approval-state.ts +2 -2
  245. package/src/activities/execute-cursor/capture-flow.ts +18 -41
  246. package/src/activities/execute-cursor/cursor-capabilities.ts +35 -0
  247. package/src/activities/execute-cursor/cursor-mcp-config.ts +2 -2
  248. package/src/activities/execute-cursor/fetch-interceptor.ts +48 -45
  249. package/src/activities/execute-cursor/http2-interceptor.ts +47 -34
  250. package/src/activities/execute-cursor/message-translator.ts +90 -45
  251. package/src/activities/execute-cursor/prompt-builder.ts +327 -12
  252. package/src/activities/execute-cursor/rejection-capture.ts +5 -12
  253. package/src/activities/execute-cursor/session-lifecycle.ts +1 -5
  254. package/src/activities/execute-cursor/turn-boundary.ts +1 -1
  255. package/src/activities/execute-cursor/turn-settle.ts +601 -0
  256. package/src/activities/execute-cursor/turn-setup.ts +645 -0
  257. package/src/activities/execute-cursor/turn-stream.ts +84 -210
  258. package/src/activities/execute-cursor/turn.ts +240 -0
  259. package/src/activities/execute-cursor/usage-pricing.ts +67 -0
  260. package/src/activities/execute-deep-agent/approval-file-change.ts +1 -1
  261. package/src/activities/execute-deep-agent/environment.ts +1 -1
  262. package/src/activities/execute-deep-agent/setup.ts +3 -2
  263. package/src/activities/hydrate-workflow-execution.ts +1 -1
  264. package/src/config.ts +13 -0
  265. package/src/harness/__tests__/import-direction.test.ts +168 -0
  266. package/src/harness/__tests__/persist-chokepoint.test.ts +146 -0
  267. package/src/harness/__tests__/registry.test.ts +168 -0
  268. package/src/harness/__tests__/run-turn.test.ts +165 -0
  269. package/src/harness/__tests__/stop-controller.test.ts +49 -0
  270. package/src/harness/__tests__/turn-context.test.ts +178 -0
  271. package/src/harness/__tests__/usage-accumulator.test.ts +83 -0
  272. package/src/harness/capabilities.ts +92 -0
  273. package/src/harness/persist-chokepoint.ts +112 -0
  274. package/src/harness/registry.ts +197 -0
  275. package/src/harness/run-turn.ts +618 -0
  276. package/src/harness/stop-controller.ts +83 -0
  277. package/src/harness/terminal-table.ts +276 -0
  278. package/src/harness/turn-context.ts +1028 -0
  279. package/src/harness/types.ts +473 -0
  280. package/src/harness/usage-accumulator.ts +122 -0
  281. package/src/harness-adapters.ts +30 -0
  282. package/src/main.ts +1 -1
  283. package/src/runner-manager.ts +68 -61
  284. package/src/runner.ts +67 -78
  285. package/src/shared/__tests__/attachment-naming.test.ts +1 -1
  286. package/src/{activities/execute-cursor → shared}/__tests__/attachment-resolver.test.ts +6 -4
  287. package/src/{activities/execute-cursor → shared}/__tests__/cost-guard.test.ts +2 -1
  288. package/src/{activities/execute-cursor → shared}/__tests__/exact-apply.test.ts +3 -3
  289. package/src/{activities/execute-cursor → shared}/__tests__/extract-structured-output.test.ts +8 -8
  290. package/src/{activities/execute-cursor → shared}/__tests__/persist-decision.test.ts +4 -2
  291. package/src/{activities/execute-cursor → shared}/__tests__/skill-resolver.test.ts +1 -1
  292. package/src/shared/__tests__/subagent-rows.test.ts +34 -0
  293. package/src/shared/__tests__/worker-shutdown.test.ts +44 -39
  294. package/src/shared/approval-policy.ts +1 -1
  295. package/src/{activities/execute-cursor → shared}/attachment-resolver.ts +6 -6
  296. package/src/{activities/execute-cursor → shared}/blueprint-resolver.ts +7 -78
  297. package/src/shared/channel-attachment.ts +1 -1
  298. package/src/{activities/execute-cursor → shared}/cost-guard.ts +5 -1
  299. package/src/{activities/execute-cursor → shared}/env-resolver.ts +1 -1
  300. package/src/{activities/execute-cursor → shared}/exact-apply.ts +7 -7
  301. package/src/shared/execution-context.ts +45 -0
  302. package/src/{activities/execute-cursor → shared}/extract-structured-output.ts +10 -7
  303. package/src/shared/filereview/capture.ts +1 -1
  304. package/src/shared/heartbeat.ts +13 -33
  305. package/src/{activities/execute-cursor → shared}/persist-decision.ts +5 -2
  306. package/src/{activities/execute-cursor → shared}/skill-resolver.ts +20 -6
  307. package/src/shared/subagent-rows.ts +39 -0
  308. package/src/shared/worker-shutdown.ts +55 -25
  309. package/src/{activities/execute-cursor/__tests__/workspace-provision.test.ts → shared/workspace/__tests__/session-provision.test.ts} +25 -28
  310. package/src/shared/workspace/platform-dir.ts +1 -1
  311. package/src/{activities/execute-cursor/workspace-provision.ts → shared/workspace/session-provision.ts} +29 -16
  312. package/dist/activities/execute-cursor/attachment-resolver.js.map +0 -1
  313. package/dist/activities/execute-cursor/blueprint-resolver.js.map +0 -1
  314. package/dist/activities/execute-cursor/cost-guard.js.map +0 -1
  315. package/dist/activities/execute-cursor/env-resolver.js.map +0 -1
  316. package/dist/activities/execute-cursor/exact-apply.js.map +0 -1
  317. package/dist/activities/execute-cursor/extract-structured-output.js.map +0 -1
  318. package/dist/activities/execute-cursor/index.d.ts +0 -209
  319. package/dist/activities/execute-cursor/index.js +0 -2345
  320. package/dist/activities/execute-cursor/index.js.map +0 -1
  321. package/dist/activities/execute-cursor/persist-decision.js.map +0 -1
  322. package/dist/activities/execute-cursor/skill-resolver.js.map +0 -1
  323. package/dist/activities/execute-cursor/usage-accumulator.d.ts +0 -89
  324. package/dist/activities/execute-cursor/usage-accumulator.js +0 -121
  325. package/dist/activities/execute-cursor/usage-accumulator.js.map +0 -1
  326. package/dist/activities/execute-cursor/workspace-provision.d.ts +0 -42
  327. package/dist/activities/execute-cursor/workspace-provision.js.map +0 -1
  328. package/src/activities/execute-cursor/__tests__/usage-accumulator.test.ts +0 -164
  329. package/src/activities/execute-cursor/index.ts +0 -2876
  330. package/src/activities/execute-cursor/usage-accumulator.ts +0 -169
  331. /package/dist/{activities/execute-cursor → shared}/env-resolver.js +0 -0
@@ -0,0 +1,349 @@
1
+ /**
2
+ * The scripted harness adapter — the reference implementation of
3
+ * `HarnessAdapter`, driven by a `TurnScenario` per turn instead of a vendor
4
+ * SDK.
5
+ *
6
+ * Two jobs. It is the fake the contract kit runs against under every
7
+ * capability combination, so the kit is proven before any real adapter
8
+ * implements the contract. And it is the TEMPLATE a future harness author
9
+ * reads first: every rule of the contract appears here as the smallest code
10
+ * that honours it, with the reason beside it. Its measured line count is the
11
+ * program's "lines a new harness must write" data point.
12
+ *
13
+ * The insight this fake makes visible: above the contract line, the two real
14
+ * pause primitives are indistinguishable. Whether the engine checkpoints at
15
+ * the gate (`interrupt`) or a hook denies the tool and the run is cancelled
16
+ * (`deny-and-retry`), the turn ends `awaiting_approval` with a WAITING row on
17
+ * the status, and the next invocation carries the decision. How the engine
18
+ * RESUMES is the adapter's business, below the line, and the runtime never
19
+ * sees it. So this fake takes `pausePrimitive` as an option and does not
20
+ * branch on it — and the kit runs it under both to prove the kit does not
21
+ * branch on it either.
22
+ *
23
+ * What is deliberately simple here and would be real work in an adapter:
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 —
27
+ * settling with an outcome and never rejecting, stopping at every step
28
+ * boundary and inside a hang, binding before the first persist, executing an
29
+ * approval exactly once, refusing to resume a state it never minted — because
30
+ * those are what the kit measures.
31
+ *
32
+ * What this fake deliberately does NOT do, because it is not an adapter's
33
+ * job: carry a REJECTed or SKIPped row to its terminal status. The decision
34
+ * is on the row (the server's field) and the transition follows from it with
35
+ * no engine knowledge, so it belongs to the runtime — one writer per field,
36
+ * as `approvalDecisionsOf` is the runtime's one reader (S2 M4, Q-M4-1; the
37
+ * runtime's arm lands in S3). An adapter's whole duty for a non-executing
38
+ * decision is to not execute.
39
+ *
40
+ * Scenarios are arranged PER SESSION (the engine is per session in every real
41
+ * harness; a subject arranges that session's engine), and arranging replaces
42
+ * what that session had not yet played. A `runTurn` with no scenario arranged
43
+ * for its session is a test bug and throws (the same rule as the scripted
44
+ * `@cursor/sdk` agent's `send()` with no script left); every other exit is a
45
+ * `TurnOutcome`.
46
+ */
47
+
48
+ import { ApprovalAction, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
49
+ import type { ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
50
+
51
+ import type { Config } from "../../config.js";
52
+ import type { HarnessCapabilities, PausePrimitive, StateIdSource } from "../../harness/capabilities.js";
53
+ import type { HarnessAdapter, TurnInput, TurnOutcome, TurnSink } from "../../harness/types.js";
54
+ import { DEEP_AGENT_VISION_PROFILE } from "../../shared/attachment-vision.js";
55
+ import type { ProposedAction } from "../approval-contract/types.js";
56
+ import { testConfig } from "../config-fixture.js";
57
+ import { aiMessage, findToolCallRow, toolCall, waitingToolCall } from "../proto-helpers.js";
58
+ import type { EngineView, HarnessContractSubject, ScenarioStep, TurnScenario } from "./types.js";
59
+
60
+ export interface ScriptedHarnessOptions {
61
+ /** Diagnostic name; defaults to a name that says which primitives this instance runs under. */
62
+ readonly name?: string;
63
+ readonly pausePrimitive: PausePrimitive;
64
+ readonly stateIdSource: StateIdSource;
65
+ /**
66
+ * The subject's `Config` over `testConfig()`'s inert defaults. The fake reads
67
+ * none of it; the RUNTIME does when the subject runs through the real
68
+ * activity (the workspace root it provisions under, the task queue the
69
+ * drain signal is keyed by).
70
+ */
71
+ readonly config?: Partial<Config>;
72
+ }
73
+
74
+ /**
75
+ * Resolves when the signal aborts; resolves at once if it already has. Parks
76
+ * on the abort EVENT, never on a timer, so a hanging turn holds nothing that
77
+ * could keep a process alive — the property the runtime's heartbeat relies
78
+ * on when it declares a stalled turn dead.
79
+ */
80
+ function whenAborted(signal: AbortSignal): Promise<void> {
81
+ if (signal.aborted) return Promise.resolve();
82
+ return new Promise((resolve) => signal.addEventListener("abort", () => resolve(), { once: true }));
83
+ }
84
+
85
+ /** A proposal already carried to a terminal row has been settled by an earlier invocation. */
86
+ function isSettled(row: ToolCall | undefined): boolean {
87
+ return row !== undefined
88
+ && (row.status === ToolCallStatus.TOOL_CALL_COMPLETED || row.status === ToolCallStatus.TOOL_CALL_SKIPPED);
89
+ }
90
+
91
+ export class ScriptedHarnessAdapter implements HarnessAdapter {
92
+ readonly name: string;
93
+ readonly capabilities: HarnessCapabilities;
94
+
95
+ /** The next turn each session plays; arranging a session replaces its entry. */
96
+ private readonly arranged = new Map<string, TurnScenario>();
97
+ private readonly mintedStateIds = new Set<string>();
98
+ private readonly executions = new Map<string, number>();
99
+ private readonly hangWaiters: Array<() => void> = [];
100
+ private mintCounter = 0;
101
+
102
+ constructor(options: ScriptedHarnessOptions) {
103
+ this.name = options.name ?? `scripted(${options.pausePrimitive}, ${options.stateIdSource})`;
104
+ this.capabilities = {
105
+ pausePrimitive: options.pausePrimitive,
106
+ stateIdSource: options.stateIdSource,
107
+ systemPrompt: true,
108
+ subAgents: false,
109
+ toolRestriction: true,
110
+ visionProfile: DEEP_AGENT_VISION_PROFILE,
111
+ fileReview: { harnessId: "scripted", excludePaths: [] },
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Resolves the next time a turn parks on a `hang` step — for a test that
117
+ * stands where the runtime's watchdog stands and needs to know the engine
118
+ * is silent before it advances the clock. Never on a timer.
119
+ */
120
+ whenHanging(): Promise<void> {
121
+ return new Promise((resolve) => this.hangWaiters.push(resolve));
122
+ }
123
+
124
+ // ── Engine controls (the subject's side of the kit seam) ──────────────────
125
+
126
+ /**
127
+ * What the session's next `runTurn` plays. The rest of the view is not
128
+ * needed here: this fake reads its decisions from `input.approvalDecisions`
129
+ * and settles a repeated proposal from the row itself, so it has nothing to
130
+ * decide before the turn.
131
+ */
132
+ arrange(turn: TurnScenario, view: Pick<EngineView, "sessionId">): void {
133
+ this.arranged.set(view.sessionId, turn);
134
+ }
135
+
136
+ executionCount(toolCallId: string): number {
137
+ return this.executions.get(toolCallId) ?? 0;
138
+ }
139
+
140
+ // ── HarnessAdapter ────────────────────────────────────────────────────────
141
+
142
+ /** Nothing to install: a real adapter imports its SDK lazily and installs its transport here. */
143
+ async boot(_config: Config): Promise<void> {}
144
+
145
+ /** Nothing held: a real adapter closes every engine it still has parked. */
146
+ async shutdown(): Promise<void> {}
147
+
148
+ /** Nothing is parked per session here; a real adapter releases the engine it parked for `sessionId`. */
149
+ async releaseSession(_sessionId: string): Promise<void> {}
150
+
151
+ async runTurn(input: TurnInput, sink: TurnSink): Promise<TurnOutcome> {
152
+ const turn = this.arranged.get(input.sessionId);
153
+ if (!turn) throw new Error(`${this.name}: runTurn called with no scenario arranged for session '${input.sessionId}' (test bug)`);
154
+ this.arranged.delete(input.sessionId);
155
+
156
+ // The signal may be aborted before the turn is entered; do no work then.
157
+ if (sink.stopSignal.aborted) return { kind: "interrupted" };
158
+
159
+ const resumed = await this.resolveEngineState(input, sink);
160
+ if (resumed.kind !== "ok") return resumed.outcome;
161
+
162
+ for (const step of turn) {
163
+ // Stop at every step boundary — the Cursor loop's per-event isCancelled().
164
+ if (sink.stopSignal.aborted) return { kind: "interrupted" };
165
+ const ended = await this.play(step, input, sink);
166
+ if (ended) return ended;
167
+ }
168
+ return { kind: "completed" };
169
+ }
170
+
171
+ /**
172
+ * Create or resume the engine, per `stateIdSource`. An engine-minted id is
173
+ * bound BEFORE the turn proceeds so a crash mid-turn still resumes; a
174
+ * resume of an id this engine never minted fails the way `Agent.resume` of
175
+ * an unknown id fails, so a runtime that threads the wrong id is caught. A
176
+ * deterministic engine's id is the runtime's; nothing to bind.
177
+ */
178
+ private async resolveEngineState(
179
+ input: TurnInput,
180
+ sink: TurnSink,
181
+ ): Promise<{ kind: "ok" } | { kind: "ended"; outcome: TurnOutcome }> {
182
+ if (this.capabilities.stateIdSource === "deterministic") return { kind: "ok" };
183
+
184
+ if (input.threadId === "") {
185
+ const minted = `${this.name}#state-${++this.mintCounter}`;
186
+ this.mintedStateIds.add(minted);
187
+ try {
188
+ await sink.bindHarnessState(minted);
189
+ } catch (err) {
190
+ return {
191
+ kind: "ended",
192
+ outcome: { kind: "failed", surface: "internal", message: `${this.name}: could not bind engine state`, cause: err },
193
+ };
194
+ }
195
+ return { kind: "ok" };
196
+ }
197
+
198
+ if (!this.mintedStateIds.has(input.threadId)) {
199
+ return {
200
+ kind: "ended",
201
+ outcome: { kind: "failed", surface: "internal", message: `${this.name}: no engine state '${input.threadId}' to resume` },
202
+ };
203
+ }
204
+ return { kind: "ok" };
205
+ }
206
+
207
+ /** Play one step; returns the outcome that ends the turn, or undefined to continue. */
208
+ private async play(step: ScenarioStep, input: TurnInput, sink: TurnSink): Promise<TurnOutcome | undefined> {
209
+ switch (step.kind) {
210
+ case "say": {
211
+ sink.status.messages.push(aiMessage(step.text));
212
+ sink.recordActivity();
213
+ // Awaited, as the Cursor loop awaits its own: a platform STOP the
214
+ // runtime reads from this write aborts the signal before the next
215
+ // step boundary sees it (the contract's "MAY await for ordering").
216
+ await sink.requestPersist();
217
+ return undefined;
218
+ }
219
+ case "propose":
220
+ return this.propose(step.toolCallId, step.action, input, sink);
221
+ case "read": {
222
+ // Ungated: the row lands COMPLETED at once, the effect counts as run,
223
+ // and the persist is awaited like `say`'s — a real engine's tool call
224
+ // is the discrete event its loop flushes on.
225
+ const message = aiMessage("");
226
+ const row = toolCall(step.toolCallId, "read", ToolCallStatus.TOOL_CALL_COMPLETED);
227
+ row.result = `contents of ${step.path}`;
228
+ message.toolCalls.push(row);
229
+ sink.status.messages.push(message);
230
+ this.executions.set(step.toolCallId, this.executionCount(step.toolCallId) + 1);
231
+ sink.recordActivity("read");
232
+ await sink.requestPersist();
233
+ return undefined;
234
+ }
235
+ case "usage": {
236
+ sink.reportUsage(step.delta);
237
+ sink.recordActivity();
238
+ return undefined;
239
+ }
240
+ case "hang": {
241
+ for (const waiter of this.hangWaiters.splice(0)) waiter();
242
+ await whenAborted(sink.stopSignal);
243
+ return { kind: "interrupted" };
244
+ }
245
+ case "fail":
246
+ return { kind: "failed", surface: step.surface, message: step.message };
247
+ case "cancelled":
248
+ return { kind: "cancelled" };
249
+ default: {
250
+ const exhaustive: never = step;
251
+ throw new Error(`${this.name}: unknown scenario step ${JSON.stringify(exhaustive)}`);
252
+ }
253
+ }
254
+ }
255
+
256
+ /**
257
+ * The gate, above the contract line. The status is the single source of
258
+ * truth for whether the effect ran: a row already carried to COMPLETED or
259
+ * SKIPPED by an earlier invocation is settled and the step is a no-op, which
260
+ * is what makes "reinvoked twice with the same decision, still once" hold
261
+ * without a second ledger.
262
+ */
263
+ private propose(
264
+ toolCallId: string,
265
+ action: ProposedAction,
266
+ input: TurnInput,
267
+ sink: TurnSink,
268
+ ): TurnOutcome | undefined {
269
+ const existing = findToolCallRow(sink.status, toolCallId);
270
+ if (isSettled(existing)) return undefined;
271
+
272
+ const decision = input.approvalDecisions.get(toolCallId) ?? ApprovalAction.UNSPECIFIED;
273
+ switch (decision) {
274
+ case ApprovalAction.UNSPECIFIED: {
275
+ // Propose: surface the gated call as a WAITING row and stop the turn.
276
+ // Never write a second row for the same id on a resumed turn.
277
+ if (!existing) {
278
+ const message = aiMessage("");
279
+ message.toolCalls.push(waitingToolCall(toolCallId, action.kind, `Approve ${action.kind} ${action.resource}?`));
280
+ sink.status.messages.push(message);
281
+ }
282
+ sink.recordActivity();
283
+ sink.requestPersist();
284
+ return { kind: "awaiting_approval" };
285
+ }
286
+ case ApprovalAction.APPROVE:
287
+ case ApprovalAction.APPROVE_ALL: {
288
+ this.executions.set(toolCallId, this.executionCount(toolCallId) + 1);
289
+ this.completeRow(existing, toolCallId, action, sink);
290
+ return undefined;
291
+ }
292
+ case ApprovalAction.SKIP:
293
+ case ApprovalAction.REJECT: {
294
+ // Not executed, and the row is left as the runtime handed it (see
295
+ // the header): the engine simply moves on.
296
+ sink.recordActivity();
297
+ return undefined;
298
+ }
299
+ default: {
300
+ const exhaustive: never = decision;
301
+ throw new Error(`${this.name}: unknown approval action ${String(exhaustive)}`);
302
+ }
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Report the executed action on its row, as a real engine's completion
308
+ * event does. The row normally exists (the runtime seeded the status with
309
+ * last turn's WAITING row); a decision with no row is a runtime that decided
310
+ * out of band, and the adapter still records what happened rather than
311
+ * losing the fact.
312
+ */
313
+ private completeRow(existing: ToolCall | undefined, toolCallId: string, action: ProposedAction, sink: TurnSink): void {
314
+ if (existing) {
315
+ existing.status = ToolCallStatus.TOOL_CALL_COMPLETED;
316
+ } else {
317
+ const message = aiMessage("");
318
+ const row = waitingToolCall(toolCallId, action.kind, "");
319
+ row.status = ToolCallStatus.TOOL_CALL_COMPLETED;
320
+ message.toolCalls.push(row);
321
+ sink.status.messages.push(message);
322
+ }
323
+ sink.recordActivity();
324
+ sink.requestPersist();
325
+ }
326
+ }
327
+
328
+ /** The scripted adapter as a kit subject: the adapter IS its own engine, so the seam is a thin view over it. */
329
+ export interface ScriptedSubject extends HarnessContractSubject {
330
+ readonly adapter: ScriptedHarnessAdapter;
331
+ }
332
+
333
+ /**
334
+ * The fake fills the `deep-agent` row in S2 — the one row the runtime does
335
+ * not yet serve for real (the native adapter lands in S3, and takes the row
336
+ * over from the fake in the runtime half then).
337
+ */
338
+ export function scriptedSubject(options: ScriptedHarnessOptions): ScriptedSubject {
339
+ const adapter = new ScriptedHarnessAdapter(options);
340
+ return {
341
+ name: adapter.name,
342
+ harness: "deep-agent",
343
+ adapter,
344
+ config: testConfig(options.config),
345
+ arrange: (turn, view) => adapter.arrange(turn, view),
346
+ whenHanging: () => adapter.whenHanging(),
347
+ executionCount: (toolCallId) => adapter.executionCount(toolCallId),
348
+ };
349
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * The test-facing seam of the harness contract kit.
3
+ *
4
+ * `HarnessAdapter.runTurn(input, sink)` gives a test no way to make the
5
+ * engine DO anything: propose a gated action, emit usage, hang, fail. The
6
+ * approval-contract kit met the same problem with `GatewaySubstrate` — a
7
+ * small interface each real substrate adapts so one invariant catalog can
8
+ * drive all of them. This is that seam for harnesses: a
9
+ * {@link HarnessContractSubject} owns the ENGINE (what the next turn will
10
+ * do), and the kit owns everything the runtime would own (the sink, the
11
+ * `TurnInput`, the state id threading, the reinvocation).
12
+ *
13
+ * The scenario vocabulary is deliberately small and engine-neutral: it says
14
+ * what the kit needs to PROVOKE, never how an engine behaves. The scripted
15
+ * fake consumes it directly; a real harness's subject translates it onto that
16
+ * harness's own double (the Cursor subject onto the scripted `@cursor/sdk`
17
+ * agent of `execute-cursor/__test-utils__/`). One vocabulary, not one per
18
+ * engine, so the kit cannot drift from what the fake can do.
19
+ *
20
+ * Gated actions reuse `ProposedAction` from `approval-contract/types.ts`, the
21
+ * taxonomy-free action every enforcement substrate already translates, so the
22
+ * two kits speak of the same logical side effect and neither restates the
23
+ * HITL taxonomy.
24
+ */
25
+
26
+ import type { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
27
+
28
+ import type { Config } from "../../config.js";
29
+ import type { HarnessName } from "../../harness/registry.js";
30
+ import type { FailureSurface, HarnessAdapter, UsageDelta } from "../../harness/types.js";
31
+ import type { ProposedAction } from "../approval-contract/types.js";
32
+ import type { TurnInputFixtureOverrides } from "../turn-input-fixture.js";
33
+
34
+ /**
35
+ * One thing the engine does during a turn. A turn plays its steps in order
36
+ * and ends `completed` when none is left, unless a step ends it first.
37
+ *
38
+ * - `say`: the engine emits assistant text (a transcript row).
39
+ * - `propose`: the engine reaches a gated side effect identified by
40
+ * `toolCallId`. Undecided → the turn ends `awaiting_approval` and later
41
+ * steps do not run; APPROVE → the effect runs once and the turn continues;
42
+ * REJECT / SKIP → the effect never runs and the turn continues. WHICH
43
+ * kinds a harness gates is the harness's policy and the workspace's
44
+ * posture: a `shell` is gated by every harness in every posture; a `write`
45
+ * is gated only outside apply-then-review capture (a git tree, or artifact
46
+ * storage), so an arm that must gate on every subject proposes a `shell`.
47
+ * - `read`: the engine performs an UNGATED read of `path` — a tool-call row
48
+ * that runs at once, never pauses, and is the discrete event every harness
49
+ * flushes a persist on (the lever an arm pulls when it needs the control
50
+ * plane to answer a mid-turn persist).
51
+ * - `usage`: the engine reports one turn's token counts.
52
+ * - `hang`: the engine makes no further progress until told to stop. The
53
+ * step that the stop-signal invariants and the runtime's stall watchdog
54
+ * are built around; it must be parked on the signal, never on a timer.
55
+ * - `fail`: the engine fails with a message the adapter can name; the turn
56
+ * ends `failed` on the given surface (`engine` unless the scenario says
57
+ * otherwise — the runtime's three failure copies are its own invariant).
58
+ * - `cancelled`: the engine ends its own run cancelled with nothing to
59
+ * wait for (an SDK-side cancel); the turn ends `cancelled`.
60
+ */
61
+ export type ScenarioStep =
62
+ | { readonly kind: "say"; readonly text: string }
63
+ | { readonly kind: "propose"; readonly toolCallId: string; readonly action: ProposedAction }
64
+ | { readonly kind: "read"; readonly toolCallId: string; readonly path: string }
65
+ | { readonly kind: "usage"; readonly delta: UsageDelta }
66
+ | { readonly kind: "hang" }
67
+ | { readonly kind: "fail"; readonly message: string; readonly surface: FailureSurface }
68
+ | { readonly kind: "cancelled" };
69
+
70
+ /** One turn's worth of engine behaviour. */
71
+ export type TurnScenario = readonly ScenarioStep[];
72
+
73
+ /** Step builders — the vocabulary a kit invariant reads as. */
74
+ export const scenario = {
75
+ say(text: string): ScenarioStep {
76
+ return { kind: "say", text };
77
+ },
78
+ propose(toolCallId: string, action: ProposedAction): ScenarioStep {
79
+ return { kind: "propose", toolCallId, action };
80
+ },
81
+ read(toolCallId: string, path: string): ScenarioStep {
82
+ return { kind: "read", toolCallId, path };
83
+ },
84
+ usage(delta: UsageDelta): ScenarioStep {
85
+ return { kind: "usage", delta };
86
+ },
87
+ hang(): ScenarioStep {
88
+ return { kind: "hang" };
89
+ },
90
+ fail(message: string, surface: FailureSurface = "engine"): ScenarioStep {
91
+ return { kind: "fail", message, surface };
92
+ },
93
+ cancelled(): ScenarioStep {
94
+ return { kind: "cancelled" };
95
+ },
96
+ } as const;
97
+
98
+ /**
99
+ * What the engine can know about the turn it is about to play, handed to the
100
+ * subject with every {@link HarnessContractSubject.arrange}: which execution
101
+ * and session it serves (a real engine is per session — its handle is parked
102
+ * under the session, its workspace and hook state live under the session —
103
+ * so a subject arranges THAT session's engine, never "the next turn"), and
104
+ * the approval decisions the prompt will carry (the runtime's
105
+ * `approvalDecisionsOf` over the persisted status — the one reader both
106
+ * drivers call). A subject whose engine cannot re-run a settled action, or
107
+ * whose model is told not to perform a skipped one, reads its decision here
108
+ * instead of parsing the prompt for it.
109
+ */
110
+ export interface EngineView {
111
+ readonly executionId: string;
112
+ readonly sessionId: string;
113
+ readonly approvalDecisions: ReadonlyMap<string, ApprovalAction>;
114
+ }
115
+
116
+ /**
117
+ * What a subject may say about the record its engine is handed, laid UNDER
118
+ * the driver's own facts: the five it threads (`executionId`, `threadId`,
119
+ * `turnSeq`, `sessionId`, `approvalDecisions`) and the persisted status it
120
+ * seeds are the kit's bookkeeping and never a subject's to state.
121
+ */
122
+ export type SubjectInputOverrides = Omit<
123
+ TurnInputFixtureOverrides,
124
+ "executionId" | "threadId" | "turnSeq" | "sessionId" | "approvalDecisions" | "persistedStatus"
125
+ >;
126
+
127
+ /**
128
+ * One adapter under test, with the engine controls the kit needs. Adapters
129
+ * translate scenarios into their own double's drives and report what the
130
+ * double observed; they never reimplement contract behaviour.
131
+ *
132
+ * The kit owns the adapter's lifetime: it boots the adapter before the first
133
+ * turn and shuts it down after the last (the registry boots every adapter
134
+ * before the worker polls, and a real adapter refuses a turn before boot).
135
+ * The exported assertion functions therefore REQUIRE a booted adapter.
136
+ */
137
+ export interface HarnessContractSubject {
138
+ /** Stable name, used in suite titles and every assertion message. */
139
+ readonly name: string;
140
+ /** The registry row this adapter fills; the runtime half selects the activity by it. */
141
+ readonly harness: HarnessName;
142
+ /** The adapter under test — the real implementation of the contract. */
143
+ readonly adapter: HarnessAdapter;
144
+ /** What `adapter.boot` needs. The subject knows its adapter's fields; the kit does not. */
145
+ readonly config: Config;
146
+ /**
147
+ * Arrange the engine so that the NEXT `runTurn` for `view.sessionId` plays
148
+ * `turn` — REPLACING whatever was arranged for that session and not yet
149
+ * played (a turn interrupted before its engine ran leaves no stale script
150
+ * behind). Called by the kit before every `runTurn`, including a
151
+ * reinvocation (the engine re-reaches the same gated call on resume, so the
152
+ * same proposal is arranged again).
153
+ */
154
+ arrange(turn: TurnScenario, view: EngineView): void;
155
+ /**
156
+ * What this engine needs the record to say beyond the driver's facts (the
157
+ * model the adapter validates, the workspace its gate installs into). A
158
+ * subject whose engine reads nothing of the record omits it.
159
+ */
160
+ inputOverrides?(view: EngineView): SubjectInputOverrides;
161
+ /**
162
+ * Resolves the next time a turn parks on a `hang` step — the moment the
163
+ * kit stops a turn from the outside, and the moment the runtime half
164
+ * delivers a cancellation, a drain or the clock tick that trips the stall
165
+ * watchdog. A real engine reaches its hang only after real setup (file
166
+ * writes, catalog fetches); a stop delivered any earlier would land in that
167
+ * setup and prove nothing about the hang. Never a timer.
168
+ */
169
+ whenHanging(): Promise<void>;
170
+ /**
171
+ * How many times the side effect behind `toolCallId` actually ran, as the
172
+ * subject's double observed it. The safety-critical observable; zero for an
173
+ * id the engine never reached.
174
+ */
175
+ executionCount(toolCallId: string): number;
176
+ }