@stigmer/runner 3.14.1 → 3.15.1

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 (320) 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 +28 -5
  9. package/dist/__test-utils__/hermetic-activity.js +23 -7
  10. package/dist/__test-utils__/hermetic-activity.js.map +1 -1
  11. package/dist/__test-utils__/mock-workspace.d.ts +15 -0
  12. package/dist/__test-utils__/mock-workspace.js +21 -0
  13. package/dist/__test-utils__/mock-workspace.js.map +1 -0
  14. package/dist/__test-utils__/model-registry-fixture.d.ts +56 -0
  15. package/dist/__test-utils__/model-registry-fixture.js +66 -0
  16. package/dist/__test-utils__/model-registry-fixture.js.map +1 -0
  17. package/dist/__test-utils__/turn-input-fixture.d.ts +60 -0
  18. package/dist/__test-utils__/turn-input-fixture.js +118 -0
  19. package/dist/__test-utils__/turn-input-fixture.js.map +1 -0
  20. package/dist/activities/execute-cursor/__test-utils__/contract-subject.d.ts +107 -0
  21. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +344 -0
  22. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -0
  23. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +20 -0
  24. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +58 -0
  25. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -1
  26. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +2 -32
  27. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -1
  28. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +38 -62
  29. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +47 -121
  30. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
  31. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +17 -1
  32. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +27 -4
  33. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -1
  34. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.d.ts +52 -11
  35. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js +66 -7
  36. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js.map +1 -1
  37. package/dist/activities/execute-cursor/adapter.d.ts +55 -0
  38. package/dist/activities/execute-cursor/adapter.js +119 -0
  39. package/dist/activities/execute-cursor/adapter.js.map +1 -0
  40. package/dist/activities/execute-cursor/approval-state.d.ts +2 -2
  41. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  42. package/dist/activities/execute-cursor/capture-flow.d.ts +15 -30
  43. package/dist/activities/execute-cursor/capture-flow.js +17 -26
  44. package/dist/activities/execute-cursor/capture-flow.js.map +1 -1
  45. package/dist/activities/execute-cursor/cursor-capabilities.d.ts +23 -0
  46. package/dist/activities/execute-cursor/cursor-capabilities.js +33 -0
  47. package/dist/activities/execute-cursor/cursor-capabilities.js.map +1 -0
  48. package/dist/activities/execute-cursor/cursor-mcp-config.d.ts +2 -2
  49. package/dist/activities/execute-cursor/cursor-mcp-config.js.map +1 -1
  50. package/dist/activities/execute-cursor/fetch-interceptor.d.ts +15 -25
  51. package/dist/activities/execute-cursor/fetch-interceptor.js +39 -33
  52. package/dist/activities/execute-cursor/fetch-interceptor.js.map +1 -1
  53. package/dist/activities/execute-cursor/http2-interceptor.d.ts +21 -17
  54. package/dist/activities/execute-cursor/http2-interceptor.js +38 -31
  55. package/dist/activities/execute-cursor/http2-interceptor.js.map +1 -1
  56. package/dist/activities/execute-cursor/message-translator.d.ts +28 -19
  57. package/dist/activities/execute-cursor/message-translator.js +76 -27
  58. package/dist/activities/execute-cursor/message-translator.js.map +1 -1
  59. package/dist/activities/execute-cursor/prompt-builder.d.ts +180 -11
  60. package/dist/activities/execute-cursor/prompt-builder.js +171 -0
  61. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  62. package/dist/activities/execute-cursor/rejection-capture.d.ts +3 -6
  63. package/dist/activities/execute-cursor/rejection-capture.js +5 -7
  64. package/dist/activities/execute-cursor/rejection-capture.js.map +1 -1
  65. package/dist/activities/execute-cursor/session-lifecycle.d.ts +1 -4
  66. package/dist/activities/execute-cursor/session-lifecycle.js.map +1 -1
  67. package/dist/activities/execute-cursor/turn-boundary.d.ts +1 -1
  68. package/dist/activities/execute-cursor/turn-settle.d.ts +49 -0
  69. package/dist/activities/execute-cursor/turn-settle.js +507 -0
  70. package/dist/activities/execute-cursor/turn-settle.js.map +1 -0
  71. package/dist/activities/execute-cursor/turn-setup.d.ts +173 -0
  72. package/dist/activities/execute-cursor/turn-setup.js +496 -0
  73. package/dist/activities/execute-cursor/turn-setup.js.map +1 -0
  74. package/dist/activities/execute-cursor/turn-stream.d.ts +41 -71
  75. package/dist/activities/execute-cursor/turn-stream.js +70 -135
  76. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  77. package/dist/activities/execute-cursor/turn.d.ts +43 -0
  78. package/dist/activities/execute-cursor/turn.js +222 -0
  79. package/dist/activities/execute-cursor/turn.js.map +1 -0
  80. package/dist/activities/execute-cursor/usage-pricing.d.ts +38 -0
  81. package/dist/activities/execute-cursor/usage-pricing.js +50 -0
  82. package/dist/activities/execute-cursor/usage-pricing.js.map +1 -0
  83. package/dist/activities/execute-deep-agent/approval-file-change.d.ts +1 -1
  84. package/dist/activities/execute-deep-agent/approval-file-change.js +1 -1
  85. package/dist/activities/execute-deep-agent/environment.js +1 -1
  86. package/dist/activities/execute-deep-agent/environment.js.map +1 -1
  87. package/dist/activities/execute-deep-agent/setup.js +3 -2
  88. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  89. package/dist/activities/hydrate-workflow-execution.js +1 -1
  90. package/dist/activities/hydrate-workflow-execution.js.map +1 -1
  91. package/dist/config.d.ts +15 -0
  92. package/dist/config.js.map +1 -1
  93. package/dist/harness/capabilities.d.ts +16 -0
  94. package/dist/harness/capabilities.js +1 -0
  95. package/dist/harness/capabilities.js.map +1 -1
  96. package/dist/harness/persist-chokepoint.d.ts +74 -0
  97. package/dist/harness/persist-chokepoint.js +88 -0
  98. package/dist/harness/persist-chokepoint.js.map +1 -0
  99. package/dist/harness/registry.d.ts +44 -5
  100. package/dist/harness/registry.js +61 -5
  101. package/dist/harness/registry.js.map +1 -1
  102. package/dist/harness/run-turn.d.ts +53 -0
  103. package/dist/harness/run-turn.js +528 -0
  104. package/dist/harness/run-turn.js.map +1 -0
  105. package/dist/harness/stop-controller.d.ts +50 -0
  106. package/dist/harness/stop-controller.js +62 -0
  107. package/dist/harness/stop-controller.js.map +1 -0
  108. package/dist/harness/terminal-table.d.ts +141 -0
  109. package/dist/harness/terminal-table.js +239 -0
  110. package/dist/harness/terminal-table.js.map +1 -0
  111. package/dist/harness/turn-context.d.ts +523 -0
  112. package/dist/harness/turn-context.js +739 -0
  113. package/dist/harness/turn-context.js.map +1 -0
  114. package/dist/harness/types.d.ts +232 -52
  115. package/dist/harness/types.js +17 -8
  116. package/dist/harness/types.js.map +1 -1
  117. package/dist/harness/usage-accumulator.d.ts +68 -0
  118. package/dist/harness/usage-accumulator.js +101 -0
  119. package/dist/harness/usage-accumulator.js.map +1 -0
  120. package/dist/harness-adapters.d.ts +27 -0
  121. package/dist/harness-adapters.js +28 -0
  122. package/dist/harness-adapters.js.map +1 -0
  123. package/dist/main.js.map +1 -1
  124. package/dist/runner-manager.js +64 -48
  125. package/dist/runner-manager.js.map +1 -1
  126. package/dist/runner.d.ts +5 -4
  127. package/dist/runner.js +65 -64
  128. package/dist/runner.js.map +1 -1
  129. package/dist/shared/approval-policy.d.ts +1 -1
  130. package/dist/shared/approval-policy.js.map +1 -1
  131. package/dist/{activities/execute-cursor → shared}/attachment-resolver.d.ts +2 -2
  132. package/dist/{activities/execute-cursor → shared}/attachment-resolver.js +5 -5
  133. package/dist/shared/attachment-resolver.js.map +1 -0
  134. package/dist/{activities/execute-cursor → shared}/blueprint-resolver.d.ts +7 -5
  135. package/dist/{activities/execute-cursor → shared}/blueprint-resolver.js +3 -63
  136. package/dist/shared/blueprint-resolver.js.map +1 -0
  137. package/dist/shared/channel-attachment.d.ts +1 -1
  138. package/dist/shared/channel-attachment.js.map +1 -1
  139. package/dist/{activities/execute-cursor → shared}/cost-guard.d.ts +5 -1
  140. package/dist/{activities/execute-cursor → shared}/cost-guard.js +5 -1
  141. package/dist/shared/cost-guard.js.map +1 -0
  142. package/dist/{activities/execute-cursor → shared}/env-resolver.d.ts +1 -1
  143. package/dist/shared/env-resolver.js.map +1 -0
  144. package/dist/{activities/execute-cursor → shared}/exact-apply.d.ts +1 -1
  145. package/dist/{activities/execute-cursor → shared}/exact-apply.js +6 -6
  146. package/dist/shared/exact-apply.js.map +1 -0
  147. package/dist/shared/execution-context.d.ts +35 -0
  148. package/dist/shared/execution-context.js +38 -0
  149. package/dist/shared/execution-context.js.map +1 -0
  150. package/dist/{activities/execute-cursor → shared}/extract-structured-output.d.ts +5 -2
  151. package/dist/{activities/execute-cursor → shared}/extract-structured-output.js +9 -6
  152. package/dist/shared/extract-structured-output.js.map +1 -0
  153. package/dist/shared/filereview/capture.d.ts +1 -1
  154. package/dist/shared/filereview/capture.js +1 -1
  155. package/dist/shared/heartbeat.d.ts +13 -11
  156. package/dist/shared/heartbeat.js +14 -29
  157. package/dist/shared/heartbeat.js.map +1 -1
  158. package/dist/{activities/execute-cursor → shared}/persist-decision.d.ts +5 -2
  159. package/dist/{activities/execute-cursor → shared}/persist-decision.js +4 -1
  160. package/dist/shared/persist-decision.js.map +1 -0
  161. package/dist/{activities/execute-cursor → shared}/skill-resolver.d.ts +16 -3
  162. package/dist/{activities/execute-cursor → shared}/skill-resolver.js +12 -4
  163. package/dist/shared/skill-resolver.js.map +1 -0
  164. package/dist/shared/subagent-rows.d.ts +21 -0
  165. package/dist/shared/subagent-rows.js +34 -0
  166. package/dist/shared/subagent-rows.js.map +1 -0
  167. package/dist/shared/worker-shutdown.d.ts +43 -16
  168. package/dist/shared/worker-shutdown.js +28 -13
  169. package/dist/shared/worker-shutdown.js.map +1 -1
  170. package/dist/shared/workspace/platform-dir.d.ts +1 -1
  171. package/dist/shared/workspace/platform-dir.js +1 -1
  172. package/dist/shared/workspace/session-provision.d.ts +54 -0
  173. package/dist/{activities/execute-cursor/workspace-provision.js → shared/workspace/session-provision.js} +15 -12
  174. package/dist/shared/workspace/session-provision.js.map +1 -0
  175. package/package.json +4 -4
  176. package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +110 -14
  177. package/src/__test-utils__/execution-record-fixture.ts +104 -0
  178. package/src/__test-utils__/harness-boot-order-child.ts +58 -0
  179. package/src/__test-utils__/harness-contract/contract.ts +128 -83
  180. package/src/__test-utils__/harness-contract/recording-sink.ts +15 -4
  181. package/src/__test-utils__/harness-contract/runtime-contract.ts +666 -0
  182. package/src/__test-utils__/harness-contract/scripted-adapter.ts +92 -32
  183. package/src/__test-utils__/harness-contract/types.ts +85 -9
  184. package/src/__test-utils__/hermetic-activity.ts +33 -8
  185. package/src/__test-utils__/model-registry-fixture.ts +72 -0
  186. package/src/__test-utils__/module-specifiers.ts +82 -0
  187. package/src/__test-utils__/turn-input-fixture.ts +155 -0
  188. package/src/__tests__/harness-boot-order.test.ts +77 -0
  189. package/src/__tests__/harness-contract.test.ts +7 -5
  190. package/src/__tests__/runner-manager.test.ts +18 -0
  191. package/src/activities/execute-cursor/__test-utils__/__tests__/scripted-double.test.ts +102 -0
  192. package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +396 -0
  193. package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +66 -0
  194. package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +2 -43
  195. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +65 -145
  196. package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +30 -4
  197. package/src/activities/execute-cursor/__test-utils__/scripted-sdk.ts +96 -8
  198. package/src/activities/execute-cursor/__tests__/adapter-is-temporal-free.test.ts +45 -0
  199. package/src/activities/execute-cursor/__tests__/approval-decisions-agree.test.ts +90 -0
  200. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +7 -8
  201. package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +28 -1
  202. package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +1 -1
  203. package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +64 -6
  204. package/src/activities/execute-cursor/__tests__/hermetic/deny-and-retry.test.ts +1 -1
  205. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -1
  206. package/src/activities/execute-cursor/__tests__/hermetic/goldens/cost-cap.status.json +53 -0
  207. package/src/activities/execute-cursor/__tests__/hermetic/goldens/pause.status.json +1 -6
  208. package/src/activities/execute-cursor/__tests__/hermetic/goldens/platform-stop.status.json +56 -0
  209. package/src/activities/execute-cursor/__tests__/hermetic/goldens/recovery-transport-timeout.status.json +48 -0
  210. package/src/activities/execute-cursor/__tests__/hermetic/goldens/resolution-error.status.json +18 -0
  211. package/src/activities/execute-cursor/__tests__/hermetic/goldens/run-cancelled.status.json +36 -0
  212. package/src/activities/execute-cursor/__tests__/hermetic/goldens/run-error-non-retryable.status.json +37 -0
  213. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-error-at-create.status.json +38 -0
  214. package/src/activities/execute-cursor/__tests__/hermetic/goldens/stall.status.json +59 -0
  215. package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +79 -0
  216. package/src/activities/execute-cursor/__tests__/hermetic/goldens/worker-shutdown.status.json +2 -7
  217. package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +170 -0
  218. package/src/activities/execute-cursor/__tests__/hermetic/pause-vs-shutdown.test.ts +22 -18
  219. package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +1 -1
  220. package/src/activities/execute-cursor/__tests__/hermetic/recovery-fresh-agent.test.ts +1 -1
  221. package/src/activities/execute-cursor/__tests__/hermetic/run-wait-arms.test.ts +286 -0
  222. package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +332 -0
  223. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +193 -0
  224. package/src/activities/execute-cursor/__tests__/hermetic/tool-call.test.ts +1 -1
  225. package/src/activities/execute-cursor/__tests__/hermetic/unattributed-hook-block.test.ts +185 -0
  226. package/src/activities/execute-cursor/__tests__/hermetic/workspace-lock-timeout.test.ts +151 -0
  227. package/src/activities/execute-cursor/__tests__/http2-interceptor.test.ts +65 -35
  228. package/src/activities/execute-cursor/__tests__/message-translator.test.ts +0 -40
  229. package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +171 -0
  230. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +118 -243
  231. package/src/activities/execute-cursor/__tests__/usage-pricing.test.ts +84 -0
  232. package/src/activities/execute-cursor/adapter.ts +138 -0
  233. package/src/activities/execute-cursor/approval-state.ts +2 -2
  234. package/src/activities/execute-cursor/capture-flow.ts +18 -41
  235. package/src/activities/execute-cursor/cursor-capabilities.ts +35 -0
  236. package/src/activities/execute-cursor/cursor-mcp-config.ts +2 -2
  237. package/src/activities/execute-cursor/fetch-interceptor.ts +48 -45
  238. package/src/activities/execute-cursor/http2-interceptor.ts +47 -34
  239. package/src/activities/execute-cursor/message-translator.ts +90 -45
  240. package/src/activities/execute-cursor/prompt-builder.ts +327 -12
  241. package/src/activities/execute-cursor/rejection-capture.ts +5 -12
  242. package/src/activities/execute-cursor/session-lifecycle.ts +1 -5
  243. package/src/activities/execute-cursor/turn-boundary.ts +1 -1
  244. package/src/activities/execute-cursor/turn-settle.ts +601 -0
  245. package/src/activities/execute-cursor/turn-setup.ts +645 -0
  246. package/src/activities/execute-cursor/turn-stream.ts +84 -210
  247. package/src/activities/execute-cursor/turn.ts +240 -0
  248. package/src/activities/execute-cursor/usage-pricing.ts +67 -0
  249. package/src/activities/execute-deep-agent/approval-file-change.ts +1 -1
  250. package/src/activities/execute-deep-agent/environment.ts +1 -1
  251. package/src/activities/execute-deep-agent/setup.ts +3 -2
  252. package/src/activities/hydrate-workflow-execution.ts +1 -1
  253. package/src/config.ts +13 -0
  254. package/src/harness/__tests__/import-direction.test.ts +168 -0
  255. package/src/harness/__tests__/persist-chokepoint.test.ts +146 -0
  256. package/src/harness/__tests__/registry.test.ts +1 -0
  257. package/src/harness/__tests__/run-turn.test.ts +165 -0
  258. package/src/harness/__tests__/stop-controller.test.ts +49 -0
  259. package/src/harness/__tests__/turn-context.test.ts +178 -0
  260. package/src/harness/__tests__/usage-accumulator.test.ts +83 -0
  261. package/src/harness/capabilities.ts +17 -0
  262. package/src/harness/persist-chokepoint.ts +112 -0
  263. package/src/harness/registry.ts +79 -5
  264. package/src/harness/run-turn.ts +618 -0
  265. package/src/harness/stop-controller.ts +83 -0
  266. package/src/harness/terminal-table.ts +276 -0
  267. package/src/harness/turn-context.ts +1028 -0
  268. package/src/harness/types.ts +248 -53
  269. package/src/harness/usage-accumulator.ts +122 -0
  270. package/src/harness-adapters.ts +30 -0
  271. package/src/main.ts +1 -1
  272. package/src/runner-manager.ts +68 -61
  273. package/src/runner.ts +67 -78
  274. package/src/shared/__tests__/attachment-naming.test.ts +1 -1
  275. package/src/{activities/execute-cursor → shared}/__tests__/attachment-resolver.test.ts +6 -4
  276. package/src/{activities/execute-cursor → shared}/__tests__/cost-guard.test.ts +2 -1
  277. package/src/{activities/execute-cursor → shared}/__tests__/exact-apply.test.ts +3 -3
  278. package/src/{activities/execute-cursor → shared}/__tests__/extract-structured-output.test.ts +8 -8
  279. package/src/{activities/execute-cursor → shared}/__tests__/persist-decision.test.ts +4 -2
  280. package/src/{activities/execute-cursor → shared}/__tests__/skill-resolver.test.ts +1 -1
  281. package/src/shared/__tests__/subagent-rows.test.ts +34 -0
  282. package/src/shared/__tests__/worker-shutdown.test.ts +44 -39
  283. package/src/shared/approval-policy.ts +1 -1
  284. package/src/{activities/execute-cursor → shared}/attachment-resolver.ts +6 -6
  285. package/src/{activities/execute-cursor → shared}/blueprint-resolver.ts +7 -78
  286. package/src/shared/channel-attachment.ts +1 -1
  287. package/src/{activities/execute-cursor → shared}/cost-guard.ts +5 -1
  288. package/src/{activities/execute-cursor → shared}/env-resolver.ts +1 -1
  289. package/src/{activities/execute-cursor → shared}/exact-apply.ts +7 -7
  290. package/src/shared/execution-context.ts +45 -0
  291. package/src/{activities/execute-cursor → shared}/extract-structured-output.ts +10 -7
  292. package/src/shared/filereview/capture.ts +1 -1
  293. package/src/shared/heartbeat.ts +13 -33
  294. package/src/{activities/execute-cursor → shared}/persist-decision.ts +5 -2
  295. package/src/{activities/execute-cursor → shared}/skill-resolver.ts +20 -6
  296. package/src/shared/subagent-rows.ts +39 -0
  297. package/src/shared/worker-shutdown.ts +55 -25
  298. package/src/{activities/execute-cursor/__tests__/workspace-provision.test.ts → shared/workspace/__tests__/session-provision.test.ts} +25 -28
  299. package/src/shared/workspace/platform-dir.ts +1 -1
  300. package/src/{activities/execute-cursor/workspace-provision.ts → shared/workspace/session-provision.ts} +29 -16
  301. package/dist/activities/execute-cursor/attachment-resolver.js.map +0 -1
  302. package/dist/activities/execute-cursor/blueprint-resolver.js.map +0 -1
  303. package/dist/activities/execute-cursor/cost-guard.js.map +0 -1
  304. package/dist/activities/execute-cursor/env-resolver.js.map +0 -1
  305. package/dist/activities/execute-cursor/exact-apply.js.map +0 -1
  306. package/dist/activities/execute-cursor/extract-structured-output.js.map +0 -1
  307. package/dist/activities/execute-cursor/index.d.ts +0 -209
  308. package/dist/activities/execute-cursor/index.js +0 -2345
  309. package/dist/activities/execute-cursor/index.js.map +0 -1
  310. package/dist/activities/execute-cursor/persist-decision.js.map +0 -1
  311. package/dist/activities/execute-cursor/skill-resolver.js.map +0 -1
  312. package/dist/activities/execute-cursor/usage-accumulator.d.ts +0 -89
  313. package/dist/activities/execute-cursor/usage-accumulator.js +0 -121
  314. package/dist/activities/execute-cursor/usage-accumulator.js.map +0 -1
  315. package/dist/activities/execute-cursor/workspace-provision.d.ts +0 -42
  316. package/dist/activities/execute-cursor/workspace-provision.js.map +0 -1
  317. package/src/activities/execute-cursor/__tests__/usage-accumulator.test.ts +0 -164
  318. package/src/activities/execute-cursor/index.ts +0 -2876
  319. package/src/activities/execute-cursor/usage-accumulator.ts +0 -169
  320. /package/dist/{activities/execute-cursor → shared}/env-resolver.js +0 -0
@@ -30,13 +30,14 @@
30
30
  * - approvalMessage: from the policy, with placeholder resolution
31
31
  */
32
32
 
33
- import { create } from "@bufbuild/protobuf";
33
+ import { clone, create } from "@bufbuild/protobuf";
34
34
  import type { JsonObject } from "@bufbuild/protobuf";
35
+ import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
35
36
  import { AgentMessageSchema, ToolCallSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
36
37
  import type { AgentMessage, ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
37
38
  import { SubAgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
38
39
  import type { SubAgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
39
- import { ApprovalPolicySource, MessageType, ToolCallStatus, SubAgentStatus, ToolKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
40
+ import { ApprovalAction, ApprovalPolicySource, MessageType, ToolCallStatus, SubAgentStatus, ToolKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
40
41
  import type { SDKMessage } from "@cursor/sdk";
41
42
  import type { MergedToolPolicy } from "./approval-policy.js";
42
43
  import { lookupMcpToolPolicy, resolveApprovalMessage, builtInRequiresApproval, getBuiltInApprovalMessage, SALIENT_ARG_FIELDS } from "./approval-policy.js";
@@ -55,6 +56,7 @@ import {
55
56
  type DeniedLedgerEntry,
56
57
  } from "./approval-state.js";
57
58
  import { utcTimestamp } from "../../shared/status.js";
59
+ import { cancelInProgressSubAgentProtos } from "../../shared/subagent-rows.js";
58
60
  import { hideToolCallRow, isToolCallRowHidden } from "../../shared/tool-row.js";
59
61
  import { classifyTool, toolApprovalCategory, type ToolApprovalCategory } from "../../shared/tool-kind.js";
60
62
  import { resolveWorkspacePath } from "../../shared/file-change.js";
@@ -192,7 +194,7 @@ function translateToolCall(event: Extract<SDKMessage, { type: "tool_call" }>): A
192
194
  */
193
195
  export function buildToolCallProto(
194
196
  event: Extract<SDKMessage, { type: "tool_call" }>,
195
- mergedPolicies?: Map<string, MergedToolPolicy>,
197
+ mergedPolicies?: ReadonlyMap<string, MergedToolPolicy>,
196
198
  provenance?: ApprovalProvenanceContext,
197
199
  ): ToolCall {
198
200
  const status = mapToolCallStatus(event.status);
@@ -714,7 +716,7 @@ function interpretSubAgentToolResult(result: unknown): {
714
716
  * Options for creating a MessageAccumulator with policy awareness.
715
717
  */
716
718
  export interface MessageAccumulatorOptions {
717
- mergedPolicies?: Map<string, MergedToolPolicy>;
719
+ mergedPolicies?: ReadonlyMap<string, MergedToolPolicy>;
718
720
  /**
719
721
  * Run-scoped approval context (global bypass + active leases) so reconstructed
720
722
  * tool calls carry their authorization provenance. Omitted in unit tests that
@@ -729,13 +731,28 @@ export interface MessageAccumulatorOptions {
729
731
  workspaceRoot?: string;
730
732
  /**
731
733
  * Sub-agent executions carried over from the persisted transcript on a
732
- * durable resume (see seedCursorTranscriptFromExecution in index.ts). The
733
- * accumulator re-registers them so a sub-agent's resumed lifecycle updates
734
- * merge onto the seeded row instead of producing a duplicate, and so the row
735
- * survives the round-trip rather than being dropped from the rebuilt status.
736
- * Empty on a first run.
734
+ * durable resume ({@link seededSubAgentsOf}). The accumulator re-registers
735
+ * them so a sub-agent's resumed lifecycle updates merge onto the seeded row
736
+ * instead of producing a duplicate, and so the row survives the round-trip
737
+ * rather than being dropped from the rebuilt status. Empty on a first run.
737
738
  */
738
- seededSubAgents?: SubAgentExecution[];
739
+ seededSubAgents?: readonly SubAgentExecution[];
740
+ }
741
+
742
+ /**
743
+ * The sub-agent rows a resumed turn re-registers, cloned from the persisted
744
+ * execution so the input stays immutable. This accumulator OWNS
745
+ * `status.subAgentExecutions` and overwrites it on every flush, which is why
746
+ * the runtime's transcript seeding (`harness/turn-context.ts`
747
+ * `seedTranscriptFromExecution`) leaves the sub-agents to this harness: they
748
+ * are re-registered here, never pushed onto the status. Same "left a
749
+ * transcript" test as the seeding, so a resume that seeded messages always
750
+ * seeds their sub-agents too.
751
+ */
752
+ export function seededSubAgentsOf(execution: AgentExecution): SubAgentExecution[] {
753
+ const persisted = execution.status;
754
+ if (!persisted || persisted.messages.length === 0) return [];
755
+ return persisted.subAgentExecutions.map((sub) => clone(SubAgentExecutionSchema, sub));
739
756
  }
740
757
 
741
758
  /**
@@ -765,31 +782,6 @@ export interface MessageAccumulatorOptions {
765
782
  * Task (sub-agent) tool calls additionally produce SubAgentExecution
766
783
  * protos, accessible via the subAgentExecutions getter.
767
784
  */
768
- /**
769
- * Transition any non-terminal sub-agent (IN_PROGRESS or PENDING) in the given
770
- * proto array to CANCELLED with a completion timestamp, in place.
771
- *
772
- * Operates directly on the status array (not the accumulator) because the
773
- * Cursor cancellation exception unwinds out of the streaming loop into the
774
- * activity's catch block, where the MessageAccumulator is out of scope. Returns
775
- * true if any sub-agent changed.
776
- */
777
- export function cancelInProgressSubAgentProtos(
778
- subAgents: SubAgentExecution[],
779
- ): boolean {
780
- let changed = false;
781
- for (const sub of subAgents) {
782
- if (
783
- sub.status === SubAgentStatus.SUB_AGENT_IN_PROGRESS ||
784
- sub.status === SubAgentStatus.SUB_AGENT_PENDING
785
- ) {
786
- sub.status = SubAgentStatus.SUB_AGENT_CANCELLED;
787
- sub.completedAt = utcTimestamp();
788
- changed = true;
789
- }
790
- }
791
- return changed;
792
- }
793
785
 
794
786
  export class MessageAccumulator {
795
787
  private readonly messages: AgentMessage[];
@@ -797,7 +789,7 @@ export class MessageAccumulator {
797
789
  private activeThinkingByRunId = new Map<string, AgentMessage>();
798
790
  private readonly _subAgentExecutions: SubAgentExecution[] = [];
799
791
  private readonly subAgentMap = new Map<string, SubAgentExecution>();
800
- private readonly mergedPolicies?: Map<string, MergedToolPolicy>;
792
+ private readonly mergedPolicies?: ReadonlyMap<string, MergedToolPolicy>;
801
793
  private readonly provenance?: ApprovalProvenanceContext;
802
794
  private readonly workspaceRoot?: string;
803
795
  private readonly toolCallIndex = new Map<string, ToolCall>();
@@ -974,12 +966,21 @@ export class MessageAccumulator {
974
966
  * pending call with the same identity naturally reconciles onto the next one.
975
967
  * Tool calls created during this turn are not WAITING_APPROVAL until the
976
968
  * post-stream denial reconciliation runs, so they can never be matched here.
969
+ *
970
+ * Only a row whose decision EXECUTES is resumable: the re-issue after an
971
+ * APPROVE is the approved act itself, landing on its row. A row the user
972
+ * declined (SKIP / REJECT) is a closed gate — a later same-identity call is a
973
+ * NEW act the model was told not to perform, and it gets its own row so the
974
+ * hook's denial gates it afresh instead of landing on the declined row and
975
+ * inheriting its decision (S2 M4 finding F9, the accumulator's half; the
976
+ * denial overlay's half is {@link isAdjudicatedRow}).
977
977
  */
978
978
  private findResumableSeededToolCall(candidate: ToolCall): ToolCall | undefined {
979
979
  const wanted = toolCallIdentityToken(candidate);
980
980
  for (const tc of this.toolCallIndex.values()) {
981
981
  if (
982
982
  tc.status === ToolCallStatus.TOOL_CALL_WAITING_APPROVAL &&
983
+ !isDeclinedRow(tc) &&
983
984
  toolCallIdentityToken(tc) === wanted
984
985
  ) {
985
986
  return tc;
@@ -1241,7 +1242,7 @@ export class MessageAccumulator {
1241
1242
  export async function reconcileDeniedToolCalls(
1242
1243
  messages: AgentMessage[],
1243
1244
  ledger: DeniedLedgerEntry[],
1244
- mergedPolicies?: Map<string, MergedToolPolicy>,
1245
+ mergedPolicies?: ReadonlyMap<string, MergedToolPolicy>,
1245
1246
  workspaceBackend?: WorkspaceBackend,
1246
1247
  ): Promise<ToolCall[]> {
1247
1248
  // Defense-in-depth: only APPROVAL-kind denials may become approval gates.
@@ -1281,10 +1282,13 @@ export async function reconcileDeniedToolCalls(
1281
1282
  // byte-for-byte (the path form agreed on both sides). The anchor resource
1282
1283
  // re-attempted within the turn shares one token and collapses to a single
1283
1284
  // approval (the first match is the keeper; same-identity twins are blanked
1284
- // by collapseRedundantToolCallTwins below).
1285
+ // by collapseRedundantToolCallTwins below). A row an earlier invocation's
1286
+ // gate already settled is never the home of THIS turn's denial
1287
+ // (isAdjudicatedRow) — the search moves past it to this turn's attempt.
1285
1288
  for (const msg of messages) {
1286
1289
  if (anchorMatched) break;
1287
1290
  for (const tc of msg.toolCalls) {
1291
+ if (isAdjudicatedRow(tc)) continue;
1288
1292
  if (toolCallIdentityToken(tc) !== anchorToken) continue;
1289
1293
  overlayDeniedStreamCall(tc, anchorInput, mergedPolicies);
1290
1294
  matchedCalls.add(tc);
@@ -1415,7 +1419,7 @@ export async function reconcileDeniedToolCalls(
1415
1419
  function overlayDeniedStreamCall(
1416
1420
  tc: ToolCall,
1417
1421
  input: Record<string, unknown> | undefined,
1418
- mergedPolicies: Map<string, MergedToolPolicy> | undefined,
1422
+ mergedPolicies: ReadonlyMap<string, MergedToolPolicy> | undefined,
1419
1423
  ): void {
1420
1424
  markWaitingApproval(tc, mergedPolicies);
1421
1425
  applyGateInput(tc, input);
@@ -1466,6 +1470,42 @@ function isAlreadyCollapsed(tc: ToolCall): boolean {
1466
1470
  );
1467
1471
  }
1468
1472
 
1473
+ /**
1474
+ * Whether the server has already adjudicated this row — the user decided its
1475
+ * gate (`approval_action`, the server's field; the runner never writes it).
1476
+ *
1477
+ * Such a row belongs to a gate that is CLOSED: approved and executed, or
1478
+ * declined, in an earlier invocation of this execution. No reconciliation of
1479
+ * the CURRENT turn may rewrite it — not the denial overlay (a new denial of the
1480
+ * same identity is a NEW act and gets its own row), not the twin collapse (the
1481
+ * executed row is the transcript's record). Before this rule the overlay matched
1482
+ * a denial to the FIRST same-identity row in the whole transcript, flipped a
1483
+ * completed, approved row back to WAITING_APPROVAL with its APPROVE still on
1484
+ * it, and the next invocation read that as the decision for the new, undecided
1485
+ * proposal — an approval bleeding to a later identical act (S2 M4 finding F9,
1486
+ * found by the harness contract kit's invariant 3 against this adapter).
1487
+ */
1488
+ function isAdjudicatedRow(tc: ToolCall): boolean {
1489
+ return tc.approvalAction !== ApprovalAction.UNSPECIFIED;
1490
+ }
1491
+
1492
+ /** Adjudicated with a decision that does NOT execute: the user declined this gate. */
1493
+ function isDeclinedRow(tc: ToolCall): boolean {
1494
+ switch (tc.approvalAction) {
1495
+ case ApprovalAction.SKIP:
1496
+ case ApprovalAction.REJECT:
1497
+ return true;
1498
+ case ApprovalAction.UNSPECIFIED:
1499
+ case ApprovalAction.APPROVE:
1500
+ case ApprovalAction.APPROVE_ALL:
1501
+ return false;
1502
+ default: {
1503
+ const exhaustive: never = tc.approvalAction;
1504
+ throw new Error(`isDeclinedRow: unknown approval action ${String(exhaustive)}`);
1505
+ }
1506
+ }
1507
+ }
1508
+
1469
1509
  /**
1470
1510
  * Whether a tool call carries a change/output of its own — the signal that it is
1471
1511
  * authoritative for its resource rather than a redundant denial/cancel twin.
@@ -1556,8 +1596,11 @@ export function collapseRedundantToolCallTwins(messages: AgentMessage[]): number
1556
1596
  if (group.length < 2) continue; // a lone call is never a twin
1557
1597
 
1558
1598
  // Keepers carry authoritative state: a change/output of their own
1559
- // (carriesOwnChange) or the approval gate itself. For a file mutation the gate
1560
- // is the sole authoritative row (the row carries no diff review lives in the
1599
+ // (carriesOwnChange), the approval gate itself, or a gate an earlier
1600
+ // invocation already settled (isAdjudicatedRowthe transcript's record of
1601
+ // what the user decided and what ran; a same-identity act in a later turn
1602
+ // is a new act, never this row's twin). For a file mutation the gate is the
1603
+ // sole authoritative row (the row carries no diff — review lives in the
1561
1604
  // ledger, or the row IS the no-storage deny-gate), so a denied write's
1562
1605
  // same-identity siblings collapse onto it; a shell/MCP twin keeps every
1563
1606
  // distinct run with output.
@@ -1565,6 +1608,7 @@ export function collapseRedundantToolCallTwins(messages: AgentMessage[]): number
1565
1608
  group.filter(
1566
1609
  (tc) =>
1567
1610
  carriesOwnChange(tc) ||
1611
+ isAdjudicatedRow(tc) ||
1568
1612
  tc.status === ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
1569
1613
  ),
1570
1614
  );
@@ -1613,6 +1657,7 @@ function collapseNonAnchorDenials(
1613
1657
  let collapsed = 0;
1614
1658
  for (const msg of messages) {
1615
1659
  for (const tc of msg.toolCalls) {
1660
+ if (isAdjudicatedRow(tc)) continue;
1616
1661
  const token = toolCallIdentityToken(tc);
1617
1662
  if (token === anchorToken) continue;
1618
1663
  if (!deniedTokens.has(token)) continue;
@@ -1714,7 +1759,7 @@ function findUnmatchedStreamCallByNormalizedSalient(
1714
1759
  ): ToolCall | undefined {
1715
1760
  for (const msg of messages) {
1716
1761
  for (const tc of msg.toolCalls) {
1717
- if (matchedCalls.has(tc)) continue;
1762
+ if (matchedCalls.has(tc) || isAdjudicatedRow(tc)) continue;
1718
1763
  const id = toolIdentity(tc.name, tc.mcpServerSlug, toolCallArgs(tc));
1719
1764
  if (normalizedFileSalient(id.key, id.salient, workspaceRoot) === wanted) {
1720
1765
  return tc;
@@ -2189,7 +2234,7 @@ function toolCallArgs(tc: ToolCall): Record<string, unknown> {
2189
2234
  */
2190
2235
  function markWaitingApproval(
2191
2236
  tc: ToolCall,
2192
- mergedPolicies?: Map<string, MergedToolPolicy>,
2237
+ mergedPolicies?: ReadonlyMap<string, MergedToolPolicy>,
2193
2238
  ): void {
2194
2239
  tc.status = ToolCallStatus.TOOL_CALL_WAITING_APPROVAL;
2195
2240
  tc.requiresApproval = true;
@@ -2209,7 +2254,7 @@ function synthesizeWaitingApprovalToolCall(
2209
2254
  salient: string,
2210
2255
  digest: string,
2211
2256
  token: string,
2212
- mergedPolicies?: Map<string, MergedToolPolicy>,
2257
+ mergedPolicies?: ReadonlyMap<string, MergedToolPolicy>,
2213
2258
  ): ToolCall {
2214
2259
  const tc = create(ToolCallSchema, {
2215
2260
  id: `approval:${token}`,
@@ -2255,7 +2300,7 @@ function resolveDeniedApprovalMessage(
2255
2300
  name: string,
2256
2301
  mcpServerSlug: string,
2257
2302
  args: Record<string, unknown>,
2258
- mergedPolicies?: Map<string, MergedToolPolicy>,
2303
+ mergedPolicies?: ReadonlyMap<string, MergedToolPolicy>,
2259
2304
  ): string {
2260
2305
  if (mergedPolicies && mcpServerSlug) {
2261
2306
  const policy = lookupMcpToolPolicy(name, mcpServerSlug, mergedPolicies);
@@ -48,6 +48,8 @@ import {
48
48
  type DownloadUrlKind,
49
49
  } from "../../shared/attachment-download-urls.js";
50
50
  import { formatTurnRecoveryText } from "./turn-recovery.js";
51
+ import type { SkillMetadata } from "../../shared/skill-resolver.js";
52
+ import type { AgentResolution, AgentResolutionReason } from "./session-lifecycle.js";
51
53
  import { PLAN_MODE_DIRECTIVE } from "../../shared/plan-mode-prompt.js";
52
54
  import {
53
55
  buildImplementPlanDirective,
@@ -66,12 +68,6 @@ const RUNNER_PATH_MARKERS = [
66
68
  "/dist/main.js",
67
69
  ] as const;
68
70
 
69
- export interface SkillMetadata {
70
- name: string;
71
- description: string;
72
- path: string;
73
- }
74
-
75
71
  /**
76
72
  * Vision facts about this turn's attachments, rendered inside the
77
73
  * input-files section: which images the model can see inline (in send
@@ -80,7 +76,7 @@ export interface SkillMetadata {
80
76
  */
81
77
  export interface VisionPromptInfo {
82
78
  inlineFilenames: string[];
83
- notViewable: NotViewableEntry[];
79
+ notViewable: readonly NotViewableEntry[];
84
80
  }
85
81
 
86
82
  /**
@@ -104,13 +100,13 @@ export interface AttachmentPromptEntry {
104
100
  export interface EnhancedPromptOptions {
105
101
  instructions: string;
106
102
  userMessage: string;
107
- skills: SkillMetadata[];
103
+ skills: readonly SkillMetadata[];
108
104
  /**
109
105
  * Serving proactive channels + their approved templates — rendered as
110
106
  * the `<available_channel_templates>` section (proactive-messaging
111
107
  * DD-003 D5) beside the synthesized send_channel_message tool.
112
108
  */
113
- channelMessaging?: ChannelMessagingInfo[];
109
+ channelMessaging?: readonly ChannelMessagingInfo[];
114
110
  subAgents: SubAgent[];
115
111
  workspaceDirs: string[];
116
112
  workspaceFileRefs: string[];
@@ -326,7 +322,7 @@ export function buildEnhancedPrompt(options: EnhancedPromptOptions): string {
326
322
  */
327
323
  export function buildReinvocationPrompt(
328
324
  pendingApprovals: PendingApproval[],
329
- approvalDecisions: Map<string, ApprovalAction>,
325
+ approvalDecisions: ReadonlyMap<string, ApprovalAction>,
330
326
  appliedToolCallIds: ReadonlySet<string> = new Set(),
331
327
  ): string {
332
328
  const approved: string[] = [];
@@ -415,7 +411,7 @@ export function buildHitlRecoveryPrompt(
415
411
  recovery: {
416
412
  turnDigest: string | undefined;
417
413
  pendingApprovals: PendingApproval[];
418
- approvalDecisions: Map<string, ApprovalAction>;
414
+ approvalDecisions: ReadonlyMap<string, ApprovalAction>;
419
415
  appliedToolCallIds?: ReadonlySet<string>;
420
416
  },
421
417
  ): string {
@@ -508,7 +504,7 @@ export function formatConversationCatchupSection(digest: string): string {
508
504
  return `<conversation_catchup>\n${formatConversationCatchupText(digest)}\n</conversation_catchup>`;
509
505
  }
510
506
 
511
- export function formatSkillsSection(skills: SkillMetadata[]): string {
507
+ export function formatSkillsSection(skills: readonly SkillMetadata[]): string {
512
508
  const entries = skills.map(
513
509
  (s) => `- **${s.name}**: ${s.description}\n Path: \`${s.path}\``,
514
510
  );
@@ -810,3 +806,322 @@ export function buildToolApprovalRuleFile(): string {
810
806
  "",
811
807
  ].join("\n");
812
808
  }
809
+
810
+ // ---------------------------------------------------------------------------
811
+ // Prompt selection — which of the four shapes a turn sends, from how the
812
+ // agent resolved and whether the turn carries adjudicated approvals. Moved
813
+ // from `index.ts` at S2 M3b, body unchanged; `__tests__/build-prompt.test.ts`
814
+ // pins every branch.
815
+ // ---------------------------------------------------------------------------
816
+
817
+ export interface BuildPromptInput {
818
+ resolution: AgentResolution;
819
+ /** Empty when the turn carries no adjudicated approvals (see isHitlReinvocation). */
820
+ approvalDecisions: ReadonlyMap<string, ApprovalAction>;
821
+ instructions: string;
822
+ userMessage: string;
823
+ skills: readonly SkillMetadata[];
824
+ /**
825
+ * Serving proactive channels + their templates (the DD-006 D2
826
+ * discovery read) — the `<available_channel_templates>` section.
827
+ */
828
+ channelMessaging?: readonly ChannelMessagingInfo[];
829
+ subAgents: SubAgent[];
830
+ workspaceDirs: string[];
831
+ workspaceFileRefs: string[];
832
+ /**
833
+ * This turn's resolved attachments for the `<input_files>` section —
834
+ * final paths plus duplicate-rename disclosure (attachment-resolver.ts).
835
+ */
836
+ attachments: AttachmentPromptEntry[];
837
+ /**
838
+ * Vision facts for the input-files section (T04): which attachments the
839
+ * model sees inline and which degraded to path-only. PER-TURN like the
840
+ * catchup — it rides both the enhanced prompt and a resumed turn's prefix.
841
+ */
842
+ vision?: VisionPromptInfo;
843
+ /**
844
+ * What kind of URL the turn's storage backend mints (issue #532) — keys
845
+ * the input-files hand-off wording. Sourced from the resolved
846
+ * artifactStorage's self-description; absent when no storage resolved.
847
+ */
848
+ downloadUrlKind?: DownloadUrlKind;
849
+ pendingApprovals: PendingApproval[];
850
+ /**
851
+ * Approved whole-file writes the runner already applied itself (exact-apply).
852
+ * The reinvocation prompt marks these as done so the model does not redo them;
853
+ * the remaining approved actions are the ones it must still carry out.
854
+ */
855
+ appliedToolCallIds?: ReadonlySet<string>;
856
+ interactionMode?: InteractionMode;
857
+ /**
858
+ * The execution is a Build-from-plan turn (spec.execution_config
859
+ * .build_from_plan): both prompt paths carry the implement-plan directive.
860
+ */
861
+ buildFromPlan?: boolean;
862
+ /**
863
+ * Rollover context bridge from `SessionSpec.metadata` (cloud DD-013).
864
+ * Only the enhanced-prompt path consumes it — a resumed agent's native
865
+ * context IS the previous conversation, so it needs no bridge.
866
+ */
867
+ contextBridge?: string;
868
+ /**
869
+ * Channel sender identity from `SessionSpec.metadata`. Like the bridge,
870
+ * only the enhanced-prompt path consumes it — a resumed agent's native
871
+ * context already carries it from the session's first turn.
872
+ */
873
+ senderIdentity?: SenderIdentity;
874
+ /**
875
+ * Embedder-supplied session context from `SessionSpec.metadata`. Like
876
+ * the bridge, only the enhanced-prompt path consumes it — a resumed
877
+ * agent's native context already carries it from the session's first
878
+ * turn.
879
+ */
880
+ sessionContext?: string;
881
+ /**
882
+ * Platform-declared standing preferences from the execution spec's
883
+ * `declared_preferences` (stigmer/stigmer#293). Like the bridge, only
884
+ * the enhanced-prompt path consumes it — deliberately frozen per Cursor
885
+ * session (DD-002 D3): the first turn delivers it into the agent's own
886
+ * conversation store, and repeating it on resumed turns would bloat the
887
+ * store with identical content.
888
+ */
889
+ declaredPreferences?: DeclaredPreferencesContent;
890
+ /**
891
+ * The subject's confirmed memories from the execution spec's
892
+ * `recalled_memories` (stigmer/stigmer#293 Phase 2, DD-006). Like the
893
+ * preferences, only the enhanced-prompt path consumes it — deliberately
894
+ * frozen per Cursor session (DD-002 D3, inherited by DD-006 D4): the
895
+ * first turn delivers it into the agent's own conversation store, and
896
+ * repeating it on resumed turns would bloat the store with identical
897
+ * content.
898
+ */
899
+ recalledMemories?: RecalledMemoriesContent;
900
+ /**
901
+ * Conversation catchup from the execution spec's `conversation_catchup`
902
+ * (cloud DD-006): what happened on the channel conversation that the
903
+ * agent has not seen. PER-TURN, so unlike the standing values
904
+ * above it rides BOTH prompt paths — the enhanced prompt and a resumed
905
+ * turn's prefix (the `interaction_mode` shape). Handback lands
906
+ * mid-session on a resumed agent: the resumed path is the one that
907
+ * matters. Once delivered, the digest persists in the agent's own
908
+ * conversation store; the next turn's field is composed fresh and is
909
+ * usually blank.
910
+ */
911
+ conversationCatchup?: string;
912
+ /**
913
+ * The turn's recorded transcript rendered as digest lines
914
+ * (turn-recovery.ts), composed from `status.messages` at the call site.
915
+ * Consumed ONLY by the HITL-recovery shape — a fresh agent that replaced
916
+ * a lost one mid-HITL needs the story of the work it no longer remembers
917
+ * (issue #366); every other shape either has native context or no prior
918
+ * work to tell.
919
+ */
920
+ turnRecoveryDigest?: string;
921
+ }
922
+
923
+ /**
924
+ * Select and build the appropriate prompt based on resolution reason and
925
+ * HITL state.
926
+ *
927
+ * Conversation continuation is carried entirely by the Cursor SDK's native
928
+ * agent state (the local SQLite store persisted on the durable workspace
929
+ * volume, or cloud server-side state) — there is no separate continuation
930
+ * store. The prompt therefore depends only on how the agent was resolved:
931
+ *
932
+ * 1. HITL reinvocation, -> buildReinvocationPrompt (approval decisions
933
+ * resumed agent only; the resumed agent's native context
934
+ * carries the prior conversation)
935
+ * 2. HITL reinvocation, -> buildHitlRecoveryPrompt (full context +
936
+ * fresh agent after the turn's recorded transcript + decisions;
937
+ * resume failure the replacement agent's conversation is
938
+ * empty, and both fresh-agent crossings —
939
+ * resolution-time resume failure and mid-send
940
+ * poisoned-handle recovery — land here by
941
+ * keying on the reason, issue #366)
942
+ * 3. resumed_successfully -> raw userMessage (native context carries it)
943
+ * 4. first execution / fresh -> buildEnhancedPrompt (full instructions +
944
+ * agent after resume failure skills; no prior conversation to inherit)
945
+ */
946
+ /**
947
+ * Whether this activity invocation is a HITL re-invocation — the turn resumes
948
+ * an agent purely to convey approval decisions, carrying NO user message.
949
+ * Discriminates the two surfaces that depend on the agent already holding
950
+ * this turn's content natively — the prompt shape (below) and the primary
951
+ * send's vision payload — but never alone: both pair it with
952
+ * `resolution.reason`, because a FRESH agent mid-HITL holds nothing and
953
+ * needs the full re-delivery (issue #366).
954
+ */
955
+ export function isHitlReinvocation(
956
+ approvalDecisions: ReadonlyMap<string, ApprovalAction>,
957
+ ): boolean {
958
+ return approvalDecisions.size > 0;
959
+ }
960
+
961
+ /**
962
+ * Whether the PRIMARY send delivers the turn's vision payload. The invariant
963
+ * is "images accompany the user's turn message, wherever the conversation
964
+ * does not already hold them" — so the only send that skips them is a HITL
965
+ * re-invocation of a successfully RESUMED agent, whose native conversation
966
+ * carries the images from the original send. A fresh agent mid-HITL
967
+ * (resolution-time resume failure — issue #366's vision corollary) holds
968
+ * nothing and needs the re-delivery. The mid-send recovery retries always
969
+ * run on a fresh agent, so their send sites carry the payload
970
+ * unconditionally rather than consulting this.
971
+ */
972
+ export function primarySendCarriesImages(
973
+ approvalDecisions: ReadonlyMap<string, ApprovalAction>,
974
+ reason: AgentResolutionReason,
975
+ ): boolean {
976
+ return !(isHitlReinvocation(approvalDecisions) && reason === "resumed_successfully");
977
+ }
978
+
979
+ /**
980
+ * Whether a prompt built for this resolution carries the STANDING context —
981
+ * instructions, skills, declared preferences, recalled memories, session
982
+ * context. Exactly one resolution shape does not: a successfully RESUMED
983
+ * agent, whose native conversation already holds the first message's
984
+ * context (both its prompt shapes — the raw follow-up and the
985
+ * decisions-only HITL reinvocation — send no standing sections).
986
+ *
987
+ * The named authority for that routing property (the
988
+ * primarySendCarriesImages idiom): buildPrompt's internal routing and the
989
+ * activity's standing-context preparation (e.g. the memory selection gate)
990
+ * both consult THIS predicate, so the two can never drift.
991
+ */
992
+ export function promptCarriesStandingContext(reason: AgentResolutionReason): boolean {
993
+ return reason !== "resumed_successfully";
994
+ }
995
+
996
+ /**
997
+ * Append the structured-output contract to a prompt when the execution
998
+ * requests one. A per-turn directive (the buildFromPlan rule): it must ride
999
+ * every prompt this turn sends — the primary send AND the poisoned-handle
1000
+ * recovery rebuild, which previously lost it (issue #366 ride-along).
1001
+ */
1002
+ export function appendStructuredOutputDirective(
1003
+ basePrompt: string,
1004
+ schema: Record<string, unknown> | undefined,
1005
+ ): string {
1006
+ if (!schema) return basePrompt;
1007
+ const schemaStr = JSON.stringify(schema, null, 2);
1008
+ return basePrompt + `\n\n---\nCRITICAL OUTPUT REQUIREMENT:\nYour final response MUST be a single valid JSON object (no markdown, no commentary, no code fences) that matches this schema:\n${schemaStr}\n\nRespond with ONLY the JSON object. Nothing else.`;
1009
+ }
1010
+
1011
+ export function buildPrompt(input: BuildPromptInput): string {
1012
+ const {
1013
+ resolution,
1014
+ approvalDecisions,
1015
+ instructions,
1016
+ userMessage,
1017
+ skills,
1018
+ subAgents,
1019
+ workspaceDirs,
1020
+ workspaceFileRefs,
1021
+ attachments,
1022
+ interactionMode,
1023
+ buildFromPlan,
1024
+ conversationCatchup,
1025
+ } = input;
1026
+
1027
+ // HITL reinvocation: the decisions-only prompt is correct ONLY while the
1028
+ // agent's native context still carries the prior conversation — which only
1029
+ // resumed_successfully guarantees. Any other reason means a fresh agent
1030
+ // mid-HITL (in practice created_after_resume_failure: the stored handle
1031
+ // failed to resume, or a poisoned handle was replaced mid-send), which
1032
+ // gets the full recovery shape instead — enhanced context + the turn's
1033
+ // recorded transcript + the same decisions — because the bare decisions on
1034
+ // an empty conversation strand the agent with instructions and no story,
1035
+ // and the session inherits that amnesia permanently (issue #366).
1036
+ if (isHitlReinvocation(approvalDecisions)) {
1037
+ if (promptCarriesStandingContext(resolution.reason)) {
1038
+ return buildHitlRecoveryPrompt(
1039
+ {
1040
+ instructions,
1041
+ userMessage,
1042
+ skills,
1043
+ channelMessaging: input.channelMessaging ?? [],
1044
+ subAgents,
1045
+ workspaceDirs,
1046
+ workspaceFileRefs,
1047
+ attachments,
1048
+ vision: input.vision,
1049
+ downloadUrlKind: input.downloadUrlKind,
1050
+ interactionMode,
1051
+ buildFromPlan,
1052
+ contextBridge: input.contextBridge,
1053
+ senderIdentity: input.senderIdentity,
1054
+ sessionContext: input.sessionContext,
1055
+ declaredPreferences: input.declaredPreferences,
1056
+ recalledMemories: input.recalledMemories,
1057
+ conversationCatchup,
1058
+ },
1059
+ {
1060
+ turnDigest: input.turnRecoveryDigest,
1061
+ pendingApprovals: input.pendingApprovals,
1062
+ approvalDecisions,
1063
+ appliedToolCallIds: input.appliedToolCallIds,
1064
+ },
1065
+ );
1066
+ }
1067
+ return buildReinvocationPrompt(
1068
+ input.pendingApprovals,
1069
+ approvalDecisions,
1070
+ input.appliedToolCallIds,
1071
+ );
1072
+ }
1073
+
1074
+ // A successfully resumed agent carries its own conversation context via the
1075
+ // SDK's native store — send the raw user message with no preamble. The
1076
+ // exceptions are the per-EXECUTION values, which never inherit from the
1077
+ // session's first turn: the interaction-mode prefix (a follow-up can switch
1078
+ // Agent→Plan mid-session, and for Cursor the prompt is the only plan-mode
1079
+ // enforcement), the implement-plan directive (the build turn is usually a
1080
+ // follow-up on a resumed agent), THIS turn's attachments (spec.attachments
1081
+ // is per-execution — a file sent on a follow-up turn materializes for this
1082
+ // turn and would otherwise never be announced at all), and the conversation
1083
+ // catchup (handback ALWAYS lands mid-session on a resumed agent — this
1084
+ // prefix is the property the metadata lane structurally cannot deliver,
1085
+ // cloud DD-006). Catchup last: it is context, and context sits closest to
1086
+ // the task (the enhanced prompt's own ordering doctrine); the input files
1087
+ // precede it because they are this turn's payload, not background.
1088
+ if (!promptCarriesStandingContext(resolution.reason)) {
1089
+ const prefixes = [
1090
+ formatInteractionModePrefix(interactionMode),
1091
+ formatImplementPlanSection(buildFromPlan, attachments),
1092
+ attachments.length > 0
1093
+ ? formatInputFiles(attachments, input.vision, input.downloadUrlKind)
1094
+ : undefined,
1095
+ conversationCatchup !== undefined
1096
+ ? formatConversationCatchupSection(conversationCatchup)
1097
+ : undefined,
1098
+ ].filter((p): p is string => p !== undefined);
1099
+ return prefixes.length > 0
1100
+ ? [...prefixes, userMessage].join("\n\n")
1101
+ : userMessage;
1102
+ }
1103
+
1104
+ // First execution, or a fresh agent created after a resume failure: there is
1105
+ // no prior conversation to inherit, so start a new turn with full context —
1106
+ // including the rollover bridge, when the session carries one.
1107
+ return buildEnhancedPrompt({
1108
+ instructions,
1109
+ userMessage,
1110
+ skills,
1111
+ channelMessaging: input.channelMessaging ?? [],
1112
+ subAgents,
1113
+ workspaceDirs,
1114
+ workspaceFileRefs,
1115
+ attachments,
1116
+ vision: input.vision,
1117
+ downloadUrlKind: input.downloadUrlKind,
1118
+ interactionMode,
1119
+ buildFromPlan,
1120
+ contextBridge: input.contextBridge,
1121
+ senderIdentity: input.senderIdentity,
1122
+ sessionContext: input.sessionContext,
1123
+ declaredPreferences: input.declaredPreferences,
1124
+ recalledMemories: input.recalledMemories,
1125
+ conversationCatchup,
1126
+ });
1127
+ }