@stigmer/runner 3.14.1 → 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 (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
@@ -0,0 +1,739 @@
1
+ /**
2
+ * The turn context — everything the runtime resolves for one agent turn
3
+ * before any harness runs, as small named phases producing one typed record.
4
+ *
5
+ * A turn begins with a dozen reads and provisions that have nothing to do
6
+ * with the engine that will run it: the execution and its session, the
7
+ * agent blueprint, the environment, the workspace and its lock, what the
8
+ * previous turn left waiting for approval, the MCP servers and their
9
+ * approval policies, the attachments, the standing context. Until S2 M2
10
+ * that sequence lived inline in the Cursor orchestrator
11
+ * (`activities/execute-cursor/index.ts` `executeCursorInner`, phases 1 to
12
+ * 9c); the native orchestrator carried a second copy in its `performSetup`.
13
+ * This module is the one copy. Each phase is a function that reads its
14
+ * inputs, reports its progress label, heartbeats once on entry, and returns
15
+ * a typed slice of the contract's `TurnInput`; {@link resolveTurnContext}
16
+ * sequences them, and the adapter runs its own harness-specific steps after
17
+ * the whole record exists.
18
+ *
19
+ * Shape, chosen against `execute-deep-agent/setup.ts`: a typed record built
20
+ * from small functions (the `SetupResult` mold), never one 775-line
21
+ * `performSetup`. The record is the adapter contract's `TurnInput`
22
+ * (`types.ts`): every phase returns a named slice of it, and
23
+ * {@link resolveTurnContext} composes the whole. There is no wider
24
+ * "runtime-private" record beside it — the lock release and the write-back
25
+ * coordinator a phase produces are the caller's `TurnFrame` resources, set
26
+ * the instant they exist so a `finally` sees them on every path (M2 had a
27
+ * `TurnContext extends TurnInput` for them; once the groups lifted, nothing
28
+ * read the extension after composition, so M3 deleted it).
29
+ *
30
+ * Three rules every phase obeys:
31
+ *
32
+ * - It never persists and never returns a slim status. A phase that can end
33
+ * the turn (the lock, the reinvocation reconcile) returns a
34
+ * {@link TurnSettlement} naming WHAT was decided; the caller writes the
35
+ * status, persists once and returns. The lock golden pins that persist as
36
+ * the turn's one and only, which is why the rule is structural.
37
+ * - It imports nothing from `src/activities/` (pinned by
38
+ * `__tests__/import-direction.test.ts`). A fact only the harness knows
39
+ * (its file-review identity, its vision profile) arrives through
40
+ * `HarnessCapabilities`.
41
+ * - Its heartbeat pulse and its cold-start timing marks are its own: the
42
+ * step name it enters under is the mark name it emits, one vocabulary for
43
+ * "where in setup are we" in Temporal heartbeat details and the
44
+ * `execution_setup` log (Q-S2-10).
45
+ *
46
+ * What is deliberately NOT here, and where it is instead:
47
+ *
48
+ * - Cursor's catalog validation of the requested model (`resolveModelId`),
49
+ * the pricing preload and the MCP env pre-flight warning stay in the
50
+ * Cursor adapter for S2: each reads a module in the adapter column of the
51
+ * entry's disposition table, and the runtime carries the REQUESTED model
52
+ * name so every harness validates against its own catalog (S2 M2 gate,
53
+ * Q-M2-1). The skill mount was in that list until M3b found it reads the
54
+ * control plane, which only the runtime holds; it is {@link mountSkills}.
55
+ * - The approval grants, the HITL gate, the capture baseline pin and the
56
+ * Cursor agent itself (phases 5c, 7, 8a, 9) are the adapter's.
57
+ * - The memory selection is a memoized thunk, not a value: whether a prompt
58
+ * carries standing context depends on how the ENGINE resolved (a resumed
59
+ * agent already holds it), which the runtime learns only after the
60
+ * adapter runs. The runtime prepares the selection once; the adapter
61
+ * pulls it when its prompt needs it.
62
+ *
63
+ * Every `index.ts` line reference below was read from `2c06bc7ea` on
64
+ * 2026-09-11, before the orchestrator moved onto this module.
65
+ */
66
+ import { CancelledFailure } from "@temporalio/activity";
67
+ import { clone } from "@bufbuild/protobuf";
68
+ import { AgentMessageSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
69
+ import { ApprovalAction, FileChangeSetStatus, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
70
+ import { resolveBlueprint } from "../shared/blueprint-resolver.js";
71
+ import { resolveExecutionEnv } from "../shared/env-resolver.js";
72
+ import { provisionSessionWorkspace } from "../shared/workspace/session-provision.js";
73
+ import { WriteBackCoordinator } from "../shared/workspace/writeback-coordinator.js";
74
+ import { statusProtoWriter } from "../shared/execution-status-writer.js";
75
+ import { isGitWorkTree } from "../shared/filereview/git-substrate.js";
76
+ import { applyCaptureDecisions, deriveCaptureMode } from "../shared/filereview/capture.js";
77
+ import { casBlobReader } from "../shared/filereview/cas-substrate.js";
78
+ import { acquireWorkspaceLock, WorkspaceLockCancelledError, WorkspaceLockTimeoutError, } from "../shared/workspace/workspace-lock.js";
79
+ import { LocalWorkspaceBackend } from "../shared/workspace/local-backend.js";
80
+ import { resolveMcpServers } from "../shared/mcp-resolver.js";
81
+ import { resolveMcpTransportPosture } from "../shared/mcp-transport-guard.js";
82
+ import { injectCallerIdentityEnv, resolveCallerIdentity } from "../shared/caller-identity.js";
83
+ import { backfillMcpServersIfNeeded } from "../shared/connect-backfill.js";
84
+ import { discoverChannelMessaging, synthesizeChannelAttachment } from "../shared/channel-attachment.js";
85
+ import { readChannelConversationId, synthesizeConversationAttachment } from "../shared/conversation-attachment.js";
86
+ import { synthesizeMemoryAttachment } from "../shared/memory-attachment.js";
87
+ import { injectSynthesizedAttachment } from "../shared/synthesized-attachment.js";
88
+ import { deriveActiveLeases, mergeApprovalPolicies } from "../shared/approval-policy.js";
89
+ import { resolveAttachments } from "../shared/attachment-resolver.js";
90
+ import { resolveSkills } from "../shared/skill-resolver.js";
91
+ import { VisionBudget } from "../shared/attachment-vision.js";
92
+ import { getModelVisionCapability } from "../shared/model-registry.js";
93
+ import { applyApprovedWholeFileWrites } from "../shared/exact-apply.js";
94
+ import { resolveEffectiveServiceTier } from "../shared/service-tier.js";
95
+ import { resolveEffectiveThinkingMode } from "../shared/thinking-mode.js";
96
+ import { readContextBridge } from "../shared/context-bridge.js";
97
+ import { readSenderIdentity } from "../shared/sender-identity.js";
98
+ import { readSessionContext } from "../shared/session-context.js";
99
+ import { readDeclaredPreferences } from "../shared/declared-preferences.js";
100
+ import { readConversationCatchup } from "../shared/conversation-catchup.js";
101
+ import { selectRecalledFacts } from "../shared/memory-retrieval.js";
102
+ // ---------------------------------------------------------------------------
103
+ // Pure helpers (exported for their tests and for the adapter kit)
104
+ // ---------------------------------------------------------------------------
105
+ /**
106
+ * Whether the engine already holds state for this execution, so this
107
+ * invocation resumes rather than begins. One function keyed on how the
108
+ * harness mints its state id (Q-S2-8):
109
+ *
110
+ * - `engine-minted` (Cursor: the agent id): the runtime knows nothing until
111
+ * the adapter binds an id, so a non-empty `threadId` IS the evidence.
112
+ * Correct today because a pure-reconcile turn does not advance `turnSeq`
113
+ * and the legacy positional wire shape yields 0, so `turnSeq` cannot be
114
+ * the signal (`index.ts` L402-407).
115
+ * - `deterministic` (native: the runtime-minted thread id): the id exists
116
+ * on the FIRST turn too, so the evidence is the persisted transcript
117
+ * (pending approvals, DECIDED sets). That arm lands in S3 with the native
118
+ * adapter; until then this function refuses rather than guess.
119
+ */
120
+ export function isReinvocation(stateIdSource, input) {
121
+ switch (stateIdSource) {
122
+ case "engine-minted":
123
+ return input.threadId !== "";
124
+ case "deterministic":
125
+ throw new Error("isReinvocation: the deterministic arm lands with the native adapter (S3); no S2 harness mints its state id deterministically");
126
+ default: {
127
+ const exhaustive = stateIdSource;
128
+ throw new Error(`isReinvocation: unknown state id source ${String(exhaustive)}`);
129
+ }
130
+ }
131
+ }
132
+ /**
133
+ * The runtime's one reader of approval decisions: every top-level tool-call
134
+ * row still WAITING_APPROVAL whose `approvalAction` the server has set,
135
+ * keyed by tool-call id. Derived from the status on every invocation, never
136
+ * stored, so it cannot drift from the rows. The same rows, in the same
137
+ * order, that the Cursor adapter's `reconstructAdjudicatedApprovals` reads
138
+ * for its own facts (the pending-approval protos and content digests); a
139
+ * test pins the two readers' agreement.
140
+ */
141
+ export function approvalDecisionsOf(status) {
142
+ const decisions = new Map();
143
+ for (const message of status.messages) {
144
+ for (const row of message.toolCalls) {
145
+ if (row.status !== ToolCallStatus.TOOL_CALL_WAITING_APPROVAL)
146
+ continue;
147
+ if (row.approvalAction === ApprovalAction.UNSPECIFIED)
148
+ continue;
149
+ decisions.set(row.id, row.approvalAction);
150
+ }
151
+ }
152
+ return decisions;
153
+ }
154
+ /**
155
+ * How a reinvocation proceeds, decided once from the facts (`index.ts`
156
+ * L547-618): a REJECT of an irreversible action fails the turn; any other
157
+ * adjudicated decision runs the agent (the approved shell/MCP proceeds and
158
+ * may produce further edits); a resume that reconciled file review and
159
+ * decided nothing else is complete, because keeping or discarding a file
160
+ * never re-prompts the agent (Cursor-like). `undefined` means "run the
161
+ * agent".
162
+ */
163
+ export function decideReinvocation(facts) {
164
+ if (facts.approvalDecisions.size > 0) {
165
+ const hasReject = [...facts.approvalDecisions.values()].some((a) => a === ApprovalAction.REJECT);
166
+ return hasReject ? { kind: "rejected-by-user" } : undefined;
167
+ }
168
+ if (facts.reconciledFileReview) {
169
+ return {
170
+ kind: "file-review-resolved",
171
+ failed: facts.fileReviewFailed,
172
+ failureDetail: facts.fileReviewFailureDetail,
173
+ discardedPaths: facts.discardedPaths,
174
+ };
175
+ }
176
+ return undefined;
177
+ }
178
+ /**
179
+ * Seed an in-progress status from the persisted execution on a durable
180
+ * resume (HITL approval, pause/resume, transient recovery) so the upcoming
181
+ * turn APPENDS onto prior history instead of replacing it.
182
+ *
183
+ * Why it is required: a resumed engine re-issues the previously gated tool
184
+ * calls with brand-new call ids. Without seeding, the harness's accumulator
185
+ * would rebuild the transcript from empty and emit a status that drops the
186
+ * already-committed tool-call ids. The backend's append-only-at-identity
187
+ * guard rejects any non-terminal update that drops a committed tool-call id,
188
+ * so the resumed progress would never persist: the run stalls in
189
+ * WAITING_FOR_APPROVAL with no pending approvals and the workflow watchdog
190
+ * fails it. Seeding makes the resume status a strict superset; the re-runs
191
+ * are then reconciled in place onto these seeded calls by canonical identity
192
+ * inside the accumulator.
193
+ *
194
+ * The persisted protos are cloned so the input execution stays immutable.
195
+ * Messages are pushed into `status.messages` (which the accumulator wraps by
196
+ * reference) BEFORE the accumulator is constructed. Sub-agent executions are
197
+ * NOT seeded here: the Cursor accumulator owns `status.subAgentExecutions`
198
+ * and overwrites it on every flush, so the adapter clones them itself from
199
+ * `input.execution.status` under the same "left a transcript" test.
200
+ *
201
+ * Moved from `execute-cursor/index.ts` `seedCursorTranscriptFromExecution`
202
+ * (L2468-2478); the native twin is `execute-deep-agent/index.ts`
203
+ * `seedStatusFromExecution` (S3 retires it).
204
+ */
205
+ export function seedTranscriptFromExecution(status, execution) {
206
+ const persisted = execution.status;
207
+ if (!persisted || persisted.messages.length === 0)
208
+ return;
209
+ for (const message of persisted.messages) {
210
+ status.messages.push(clone(AgentMessageSchema, message));
211
+ }
212
+ }
213
+ // ---------------------------------------------------------------------------
214
+ // The phases, in the order the turn runs them
215
+ // ---------------------------------------------------------------------------
216
+ /** Phase 1 (`index.ts` L325-330): hydrate the execution from the control plane. */
217
+ export async function fetchExecution(deps) {
218
+ deps.enterPhase("fetch_execution");
219
+ await deps.reportProgress("Fetching execution");
220
+ const execution = await deps.client.getExecution(deps.input.executionId);
221
+ const spec = execution.spec;
222
+ deps.timing.mark("fetch_execution");
223
+ return { execution, spec, sessionId: spec.sessionId };
224
+ }
225
+ /** Phase 2 (`index.ts` L332-336): load the session and resolve the full agent blueprint through it. */
226
+ export async function resolveAgentBlueprint(deps, sessionId) {
227
+ deps.enterPhase("resolve_blueprint");
228
+ await deps.reportProgress("Resolving agent blueprint");
229
+ const session = await deps.client.getSession(sessionId);
230
+ const blueprint = await resolveBlueprint(deps.client, session);
231
+ deps.timing.mark("resolve_blueprint");
232
+ return { session, blueprint };
233
+ }
234
+ /** Phase 2b (`index.ts` L338-343): the execution environment (MCP server credentials). */
235
+ export async function resolveEnvironment(deps) {
236
+ deps.enterPhase("resolve_environment");
237
+ await deps.reportProgress("Resolving environment");
238
+ const { envVars, secretKeys } = await resolveExecutionEnv(deps.client, deps.input.executionId);
239
+ deps.timing.mark("resolve_environment");
240
+ return { envVars, secretKeys };
241
+ }
242
+ /**
243
+ * Phase 2c, first half (`index.ts` L351-408): provision the workspace (clone
244
+ * git repos / mount local paths) so the LOCAL agent operates on the actual
245
+ * repo, wire the git write-back coordinator, and decide the capture posture.
246
+ *
247
+ * Git provisioning is idempotent across multi-turn and HITL reinvocations.
248
+ * The write-back coordinator pushes the session's APPROVED tree to the
249
+ * session branch (stigmer/<session-id>) and keeps one PR open; finalize runs
250
+ * at exactly two seams (the pure file-review resume and terminal
251
+ * completion), never mid-turn, because the working tree is speculative until
252
+ * reviewed. Non-eligible workspaces (local paths, no credentials) make it a
253
+ * no-op coordinator, here `null`.
254
+ *
255
+ * Apply-then-review is the universal file-review model (Slice 2c): a git work
256
+ * tree captures per-file from the git diff at the turn boundary; a NON-git
257
+ * workspace captures every write via the path-scoped CAS substrate, which
258
+ * needs artifact storage to persist blobs, and falls back to the classic
259
+ * deny-gate without it. `gitWorkspace` selects the substrate; `captureMode`
260
+ * says whether file edits flow at all.
261
+ *
262
+ * The change-set id is minted from the workflow-threaded turn index so it is
263
+ * stable across a Temporal retry (idempotent ledger authoring) and unique per
264
+ * turn; a "wasted" id on a pure-reconcile resume (which never authors a
265
+ * baseline) is harmless.
266
+ */
267
+ export async function provisionWorkspace(deps, args) {
268
+ deps.enterPhase("provision_workspace");
269
+ await deps.reportProgress("Provisioning workspace");
270
+ const provision = await provisionSessionWorkspace(deps.config, args.session, args.envVars, args.sessionId);
271
+ deps.timing.mark("provision_workspace");
272
+ const writeback = provision.provisionResults.length > 0
273
+ ? new WriteBackCoordinator({
274
+ statusWriter: statusProtoWriter(deps.status),
275
+ executionId: deps.input.executionId,
276
+ sessionId: args.sessionId,
277
+ githubToken: args.envVars.GITHUB_TOKEN ?? "",
278
+ provisionResults: provision.provisionResults,
279
+ workspaceEntries: args.session.spec?.workspaceEntries ?? [],
280
+ workspaceBackend: provision.workspaceBackend,
281
+ })
282
+ : null;
283
+ const primaryDir = provision.workspaceDirs[0];
284
+ const gitWorkspace = primaryDir ? await isGitWorkTree(primaryDir) : false;
285
+ const captureMode = deriveCaptureMode(primaryDir, gitWorkspace, !!deps.artifactStorage);
286
+ const changeSetId = `${deps.input.executionId}:${deps.input.turnSeq}`;
287
+ return {
288
+ workspace: { dirs: provision.workspaceDirs, primaryDir, gitWorkspace, captureMode, changeSetId, provision },
289
+ writeback,
290
+ };
291
+ }
292
+ /**
293
+ * Phase 2c, second half (`index.ts` L410-449): serialize this turn against
294
+ * every other execution sharing the primary working tree. Sessions declaring
295
+ * the same localPath (or the shared runner root) resolve to ONE directory,
296
+ * and an unserialized concurrent write lands inside this turn's
297
+ * baseline→candidate window, misattributing another session's file to this
298
+ * turn's review. Acquired before ANY tree mutation (decision reconcile, gate
299
+ * install, agent writes, capture); while another turn holds the lock this
300
+ * surfaces a visible waiting state and heartbeats; a cancel aborts the wait
301
+ * at once.
302
+ *
303
+ * Its own phase so the release handle reaches the caller the instant the
304
+ * lock exists: nothing runs between acquisition and hand-over, so nothing
305
+ * can throw and leave a held lock the caller's `finally` never sees.
306
+ */
307
+ export async function acquireWorkspaceTurnLock(deps, primaryDir) {
308
+ deps.enterPhase("acquire_workspace_lock");
309
+ let release;
310
+ if (primaryDir) {
311
+ try {
312
+ release = await acquireWorkspaceLock(primaryDir, {
313
+ onWaiting: () => deps.reportProgress("Waiting for workspace — in use by another session"),
314
+ heartbeat: deps.heartbeat,
315
+ signal: deps.signal,
316
+ timeoutMs: deps.config.workspaceLockTimeoutMs,
317
+ });
318
+ }
319
+ catch (lockErr) {
320
+ if (lockErr instanceof WorkspaceLockCancelledError) {
321
+ throw new CancelledFailure("Activity cancelled while waiting for the workspace lock");
322
+ }
323
+ if (lockErr instanceof WorkspaceLockTimeoutError) {
324
+ return { kind: "settled", settlement: { kind: "workspace-lock-timeout", error: lockErr } };
325
+ }
326
+ throw lockErr;
327
+ }
328
+ }
329
+ deps.timing.mark("acquire_workspace_lock");
330
+ return { kind: "acquired", release };
331
+ }
332
+ /**
333
+ * `index.ts` L451-461: set OTel baggage so downstream calls carry execution
334
+ * context. Best-effort; tracing not initialized is a silent skip. Runs after
335
+ * the lock, as it always has.
336
+ */
337
+ export async function bindTelemetryBaggage(deps, args) {
338
+ try {
339
+ const { setBaggage, BAGGAGE_EXECUTION_ID, BAGGAGE_SESSION_ID, BAGGAGE_ORG_ID } = await import("../otel.js");
340
+ await setBaggage({
341
+ [BAGGAGE_EXECUTION_ID]: deps.input.executionId,
342
+ [BAGGAGE_SESSION_ID]: args.sessionId,
343
+ [BAGGAGE_ORG_ID]: args.session.metadata?.org ?? "",
344
+ });
345
+ }
346
+ catch {
347
+ // Tracing not initialized — silently skip.
348
+ }
349
+ }
350
+ /**
351
+ * Phase 3 (`index.ts` L475-619): what the previous invocation left, and
352
+ * whether this one runs the agent at all.
353
+ *
354
+ * On a reinvocation, first seed the in-progress status from the persisted
355
+ * execution ({@link seedTranscriptFromExecution}), BEFORE the harness's
356
+ * accumulator wraps `status.messages`. Then the file-review reconcile (the
357
+ * dual-source half): every change set the server projected as DECIDED is
358
+ * reconciled from the ledger decisions and the pinned git refs (approved
359
+ * kept at their "after" bytes, rejected snapped back to baseline, all
360
+ * uncommitted, hash-verified) through the shared reconcile under the
361
+ * harness's {@link FileReviewIdentity}. Independent of tool approvals: a
362
+ * single turn can carry BOTH a DECIDED file change set AND an approved
363
+ * shell/MCP action. Tool approvals (shell / MCP / gitignored writes) still
364
+ * resolve from the message transcript, the deny-gate path, read here as
365
+ * {@link approvalDecisionsOf} after the reconcile, at the same point the
366
+ * orchestrator always read them.
367
+ *
368
+ * The outcome is {@link decideReinvocation}'s: a settlement the caller
369
+ * writes and persists, or the facts the turn proceeds with.
370
+ */
371
+ export async function reconcileReinvocation(deps, args) {
372
+ deps.enterPhase("reconcile_reinvocation");
373
+ const reinvoked = isReinvocation(args.stateIdSource, deps.input);
374
+ let reconciledFileReview = false;
375
+ let fileReviewFailed = false;
376
+ let fileReviewFailureDetail = "";
377
+ const discardedPaths = [];
378
+ if (reinvoked) {
379
+ const existingStatus = args.execution.status;
380
+ seedTranscriptFromExecution(deps.status, args.execution);
381
+ if (args.workspace.captureMode && args.workspace.primaryDir) {
382
+ const decidedSets = (existingStatus?.fileChangeSets ?? []).filter((cs) => cs.status === FileChangeSetStatus.DECIDED);
383
+ for (const changeSet of decidedSets) {
384
+ const capResult = await applyCaptureDecisions({
385
+ status: deps.status,
386
+ gitRoot: args.workspace.primaryDir,
387
+ executionId: deps.input.executionId,
388
+ changeSet,
389
+ harnessId: args.fileReview.harnessId,
390
+ excludePaths: args.fileReview.excludePaths,
391
+ // Thread the CAS store so CAS-captured files in the change set
392
+ // reconcile from the durable manifest (approved after-blobs written,
393
+ // rejected snapped back). In a non-git workspace this is the ONLY
394
+ // reconcile; in a git tree it composes with the git-ref reconcile.
395
+ storage: deps.artifactStorage,
396
+ readBlob: deps.artifactStorage ? casBlobReader(deps.artifactStorage) : undefined,
397
+ gitWorkspace: args.workspace.gitWorkspace,
398
+ });
399
+ if (!capResult.isCaptureTurn)
400
+ continue;
401
+ reconciledFileReview = true;
402
+ if (capResult.failed) {
403
+ fileReviewFailed = true;
404
+ fileReviewFailureDetail = capResult.failureDetail ?? "file review reconcile failed";
405
+ }
406
+ if (capResult.hadReject)
407
+ discardedPaths.push(...capResult.rejectedPaths);
408
+ }
409
+ }
410
+ }
411
+ const approvalDecisions = approvalDecisionsOf(deps.status);
412
+ const settlement = decideReinvocation({
413
+ approvalDecisions,
414
+ reconciledFileReview,
415
+ fileReviewFailed,
416
+ fileReviewFailureDetail,
417
+ discardedPaths,
418
+ });
419
+ if (settlement)
420
+ return { kind: "settled", settlement };
421
+ return { kind: "ready", reinvocation: { isReinvocation: reinvoked, approvalDecisions } };
422
+ }
423
+ /**
424
+ * Phases 4 to 4b (`index.ts` L620-770): the tool surface.
425
+ *
426
+ * The MCP-bound env map (and ONLY it, never the agent process env) carries
427
+ * the reserved caller-identity keys, so a server that declares them in
428
+ * spec.env can template the platform-verified caller into its headers;
429
+ * filterEnvToDeclaredKeys keeps every other server blind. The resolved list
430
+ * then mutates through the Connect backfill and three synthesized
431
+ * attachments, deliberately AFTER resolve + backfill so the backfill's
432
+ * destructiveHint tightener can never force-gate an attachment's tools:
433
+ *
434
+ * - channel messaging (DD-006 D7/D8): the discovery read IS the attachment
435
+ * decision, and every failure mode degrades to honest absence;
436
+ * - conversation participation (channel-conversations DD-008 D-c): the
437
+ * channel-id session label IS the decision, a free local read;
438
+ * - memory capture (DD-005 D1): the recall snapshot's enabled bit IS the
439
+ * decision, server-stamped at execution create.
440
+ *
441
+ * Their credential story (DD-006 D4): the exchanged scoped runner token
442
+ * authenticates the discovery reads per call; the exchange is opportunistic
443
+ * (every consumer degrades to an empty answer by contract, and the server
444
+ * refuses the ambient fallback safely), so a failed exchange must not kill
445
+ * the run, unlike the env read, where secrets are load-bearing.
446
+ *
447
+ * Approval policies merge last, from all layers, with two bypasses shared
448
+ * with the native harness (`shared/approval-policy.ts` `ActiveLeases`): the
449
+ * pre-armed spec.auto_approve_all is the one whole-run global bypass; an
450
+ * interactive APPROVE_ALL grants a run-lifetime lease scoped to that
451
+ * action's class. Layer-3 overrides ride each resolved server from its
452
+ * merged usage (issue #349). The harness projects its SDK config from
453
+ * `servers` after this returns, exactly once, so every mutation is visible
454
+ * by construction.
455
+ */
456
+ export async function resolveMcpServersAndPolicies(deps, args) {
457
+ const { client, config } = deps;
458
+ const { executionId } = deps.input;
459
+ const { execution, session, sessionId, blueprint, environment } = args;
460
+ deps.enterPhase("resolve_mcp_servers");
461
+ await deps.reportProgress("Resolving MCP servers");
462
+ const transportPosture = resolveMcpTransportPosture(config.mode);
463
+ const mcpEnvVars = injectCallerIdentityEnv(environment.envVars, resolveCallerIdentity(blueprint.sessionSpec.metadata, session.status?.audit?.specAudit?.createdBy), sessionId);
464
+ let servers = (await resolveMcpServers(client, blueprint.mergedMcpServerUsages, mcpEnvVars, transportPosture)).resolvedServers;
465
+ deps.timing.mark("resolve_mcp_servers");
466
+ // Phase 4a: Connect backfill for undiscovered MCP servers.
467
+ const sessionOrg = session.metadata?.org ?? "";
468
+ servers = await backfillMcpServersIfNeeded(client, servers, blueprint.mergedMcpServerUsages, mcpEnvVars, sessionOrg, transportPosture, deps.heartbeat, environment.secretKeys);
469
+ deps.timing.mark("backfill_mcp");
470
+ let exchangedRunnerToken;
471
+ try {
472
+ exchangedRunnerToken =
473
+ await client.acquireScopedRunnerToken({ agentExecutionId: executionId });
474
+ }
475
+ catch (err) {
476
+ console.warn("[turn-context] Scoped-token exchange failed for attachment/discovery " +
477
+ `reads; degrading to the ambient credential: ${err instanceof Error ? err.message : err}`);
478
+ }
479
+ const attachmentCredential = exchangedRunnerToken
480
+ ?? config.stigmerTokenRef?.current
481
+ ?? config.stigmerToken;
482
+ const attachmentEndpoints = {
483
+ bridgeEndpoint: config.mcpBridgeEndpoint,
484
+ credential: attachmentCredential,
485
+ backendEndpoint: config.stigmerBackendEndpoint,
486
+ };
487
+ // Phase 4a2: the channel messaging attachment.
488
+ const channelMessaging = await discoverChannelMessaging(client, exchangedRunnerToken);
489
+ if (channelMessaging.length > 0) {
490
+ const attachment = synthesizeChannelAttachment(channelMessaging, attachmentEndpoints);
491
+ if (attachment) {
492
+ servers = injectSynthesizedAttachment(servers, attachment, "channel messaging");
493
+ }
494
+ }
495
+ // Phase 4a4: the conversation participation attachment. HTTP-only:
496
+ // synthesize answers undefined with no bridge endpoint by design.
497
+ const conversationAttachment = synthesizeConversationAttachment(readChannelConversationId(session.metadata?.labels), attachmentEndpoints);
498
+ if (conversationAttachment) {
499
+ servers = injectSynthesizedAttachment(servers, conversationAttachment, "conversation participation");
500
+ }
501
+ // Phase 4a5: the memory capture attachment. The capture context is
502
+ // attribution the server verifies or trusts per edition; the subject is
503
+ // never threaded, it derives from the credential.
504
+ const memoryAttachment = synthesizeMemoryAttachment(execution.spec?.recalledMemories, {
505
+ org: session.metadata?.org ?? "",
506
+ agentId: blueprint.agent.metadata?.id ?? "",
507
+ sessionId,
508
+ agentExecutionId: executionId,
509
+ }, attachmentEndpoints);
510
+ if (memoryAttachment) {
511
+ servers = injectSynthesizedAttachment(servers, memoryAttachment, "memory capture");
512
+ }
513
+ // Phase 4b: merge approval policies from all layers.
514
+ const leases = deriveActiveLeases(execution);
515
+ const policies = mergeApprovalPolicies(servers, leases);
516
+ return { servers, channelMessaging, leases, policies };
517
+ }
518
+ /**
519
+ * Phase 5 (`index.ts` L493-501, the orchestrator's position between the
520
+ * tool surface and the attachments): mount the merged skills under the
521
+ * session's platform dir, reachable from the workspace through its
522
+ * `.stigmer` link, and return what a prompt renders (`shared/skill-
523
+ * resolver.ts`). Reads the control plane, which is why it is the runtime's
524
+ * and not a harness's; each harness places the returned metadata in its own
525
+ * prompt shape. The `.stigmer` link this and the attachment phase create is
526
+ * removed in the runtime's finally, after the harness's own teardown.
527
+ */
528
+ export async function mountSkills(deps, args) {
529
+ deps.enterPhase("resolve_skills");
530
+ await deps.reportProgress("Resolving skills");
531
+ const skills = await resolveSkills(deps.client, args.blueprint.mergedSkillRefs, {
532
+ sessionId: args.sessionId,
533
+ primaryWorkspaceDir: args.primaryDir,
534
+ });
535
+ deps.timing.mark("resolve_skills");
536
+ return skills;
537
+ }
538
+ /**
539
+ * Phase 5b (`index.ts` L803-838): resolve the turn's attachments, fail-hard
540
+ * (they are explicit user inputs; see `shared/attachment-resolver.ts`).
541
+ * Downloads by storage key through the same artifact storage resolved for
542
+ * status offload. The vision budget rides along so image attachments are
543
+ * selected for inline delivery while their bytes are already in hand
544
+ * (`shared/attachment-vision.ts` owns all policy); it carries the requested
545
+ * model's registry vision capability, looked up from the raw
546
+ * executionConfig name, because full model validation isn't needed for
547
+ * this and ""/"default" (the Auto pool) resolves to unknown, which the
548
+ * policy treats as sighted. The vision profile is the harness's (each
549
+ * engine accepts different image shapes), so it arrives as an argument.
550
+ */
551
+ export async function resolveTurnAttachments(deps, args) {
552
+ deps.enterPhase("resolve_attachments");
553
+ const visionBudget = new VisionBudget(args.visionProfile, {
554
+ modelVision: await getModelVisionCapability(args.spec.executionConfig?.modelName ?? ""),
555
+ });
556
+ const results = await resolveAttachments(args.spec.attachments, {
557
+ sessionId: args.sessionId,
558
+ primaryWorkspaceDir: args.primaryDir,
559
+ mode: deps.config.mode,
560
+ storage: deps.artifactStorage,
561
+ visionBudget,
562
+ });
563
+ // Vision facts, derived once from the single resolution result: the
564
+ // images the model will see inline (in attachment order) and the ones
565
+ // that degraded to path-only, disclosed in the prompt.
566
+ const visionImages = results.flatMap((a) => (a.vision ? [a.vision] : []));
567
+ const visionNotViewable = results.flatMap((a) => a.visionDegraded ? [{ path: a.relativePath, reason: a.visionDegraded }] : []);
568
+ if (visionImages.length > 0 || visionNotViewable.length > 0) {
569
+ console.log(`[attachment-vision] execution=${deps.input.executionId} inline=${visionImages.length} ` +
570
+ `(${visionImages.reduce((n, v) => n + v.byteSize, 0)} bytes) ` +
571
+ `degraded=${JSON.stringify(visionNotViewable.map((d) => `${d.path}:${d.reason}`))}`);
572
+ }
573
+ deps.timing.mark("resolve_attachments");
574
+ return { results, visionImages, visionNotViewable };
575
+ }
576
+ /**
577
+ * Phase 5b3 (`index.ts` L849-867): exact-apply approved whole-file writes
578
+ * (HITL "what you approve is what gets applied"). A deny-only harness
579
+ * reinvokes the model, which regenerates content, so a resource grant alone
580
+ * cannot guarantee the bytes that land match the bytes the user approved.
581
+ * The runner therefore writes the EXACT approved whole-file content itself
582
+ * and marks those tool calls COMPLETED; the harness then issues NO grant for
583
+ * them, so any FURTHER change the model makes to those files is re-gated.
584
+ * Hunk edits / shell / MCP stay on the grant + reinvocation path, and every
585
+ * uncertain case degrades to that path, so this can never corrupt a file
586
+ * (see `shared/exact-apply.ts`).
587
+ *
588
+ * Scoped OUT of capture mode, which does not reinvoke the model for file
589
+ * edits: it applies the exact captured bytes itself in the reconcile.
590
+ *
591
+ * The caller persists when the returned set is non-empty (the applied state
592
+ * must be durable before the continuation runs, and the UI reflects it at
593
+ * once); this phase, like every phase, never persists.
594
+ */
595
+ export async function applyApprovedWrites(deps, args) {
596
+ const { workspace, reinvocation } = args;
597
+ if (workspace.captureMode || !reinvocation.isReinvocation || reinvocation.approvalDecisions.size === 0) {
598
+ return new Set();
599
+ }
600
+ deps.enterPhase("apply_approved_writes");
601
+ return applyApprovedWholeFileWrites({
602
+ messages: deps.status.messages,
603
+ workspaceBackend: new LocalWorkspaceBackend(workspace.primaryDir),
604
+ workspaceDirs: workspace.dirs,
605
+ executionId: deps.input.executionId,
606
+ });
607
+ }
608
+ /**
609
+ * Phase 6, the harness-agnostic half (`index.ts` L998, L1005-1006): the
610
+ * requested model name as the execution states it, and the effective service
611
+ * tier and thinking mode. UNSPECIFIED → STANDARD (#357) and UNSPECIFIED →
612
+ * DISABLED (#772) resolve here and nowhere else; every upstream layer
613
+ * preserves the caller's raw enum values. Validating the NAME against a
614
+ * catalog is the harness's (Cursor's `resolveModelId`), so it is not here.
615
+ */
616
+ export function resolveModelPreferences(spec) {
617
+ return {
618
+ requested: spec.executionConfig?.modelName || "default",
619
+ serviceTier: resolveEffectiveServiceTier(spec.executionConfig?.serviceTier),
620
+ thinkingMode: resolveEffectiveThinkingMode(spec.executionConfig?.thinkingMode),
621
+ };
622
+ }
623
+ /** Phase 9b (`index.ts` L1183-1184): the structured-output schema the execution asks for, if any. */
624
+ export function structuredOutputSchemaOf(spec) {
625
+ return spec.executionConfig?.structuredOutputSchema;
626
+ }
627
+ /**
628
+ * Phase 9c (`index.ts` L1186-1216, and the metadata reads at L1240-1245):
629
+ * the standing context, read once, and the memory selection prepared once.
630
+ *
631
+ * Selection runs against the frozen first message's semantics: above the
632
+ * activation threshold it picks top-k for spec.message, otherwise (and on
633
+ * any failure) it injects the full candidate set. The outcome report is
634
+ * stamped on the turn's status ONCE, picked up by the next persist; a
635
+ * re-invocation replays the report already persisted on the execution
636
+ * rather than re-selecting (the written-once rule). Memoized because a
637
+ * resumed-agent primary send carries no memories, but a mid-send
638
+ * poisoned-handle failure rebuilds on a FRESH agent whose recovery prompt
639
+ * does, so every memory-carrying build site awaits this lazily.
640
+ */
641
+ export function resolveStandingContext(deps, args) {
642
+ const { execution, spec, blueprint } = args;
643
+ let memorySelection;
644
+ const selectRecalledMemories = () => {
645
+ memorySelection ??= selectRecalledFacts(spec.recalledMemories, spec.message, {
646
+ proxyEndpoint: deps.config.proxyEndpoint,
647
+ stigmerToken: deps.config.stigmerToken,
648
+ executionId: deps.input.executionId,
649
+ priorReport: execution.status?.recalledMemoriesReport,
650
+ }).then((selection) => {
651
+ if (selection.report !== undefined) {
652
+ deps.status.recalledMemoriesReport = selection.report;
653
+ }
654
+ return selection.content;
655
+ });
656
+ return memorySelection;
657
+ };
658
+ return {
659
+ contextBridge: readContextBridge(blueprint.sessionSpec.metadata),
660
+ senderIdentity: readSenderIdentity(blueprint.sessionSpec.metadata),
661
+ sessionContext: readSessionContext(blueprint.sessionSpec.metadata),
662
+ declaredPreferences: readDeclaredPreferences(spec.declaredPreferences),
663
+ conversationCatchup: readConversationCatchup(spec.conversationCatchup),
664
+ selectRecalledMemories,
665
+ };
666
+ }
667
+ /**
668
+ * Run the twelve phases in order and compose the turn's `TurnInput`, or
669
+ * stop at the first settlement.
670
+ *
671
+ * The order is the orchestrator's, with its harness-specific steps gone
672
+ * (they run inside `adapter.runTurn`, after this returns): the tool surface,
673
+ * the skills, the attachments, the exact-apply, then the reads that need no
674
+ * network.
675
+ *
676
+ * The one persist a phase requires — the applied writes must be durable
677
+ * before the continuation runs — goes through the caller's chokepoint
678
+ * (`persist`), so the rule that phases never persist holds here too.
679
+ */
680
+ export async function resolveTurnContext(deps, capabilities, frame, persist) {
681
+ const { execution, spec, sessionId } = await fetchExecution(deps);
682
+ const { session, blueprint } = await resolveAgentBlueprint(deps, sessionId);
683
+ const environment = await resolveEnvironment(deps);
684
+ const { workspace, writeback } = await provisionWorkspace(deps, { session, sessionId, envVars: environment.envVars });
685
+ frame.primaryDir = workspace.primaryDir;
686
+ frame.writeback = writeback;
687
+ const lock = await acquireWorkspaceTurnLock(deps, workspace.primaryDir);
688
+ if (lock.kind === "settled")
689
+ return { kind: "settled", settlement: lock.settlement };
690
+ frame.releaseWorkspaceLock = lock.release;
691
+ await bindTelemetryBaggage(deps, { session, sessionId });
692
+ const reinvoked = await reconcileReinvocation(deps, {
693
+ stateIdSource: capabilities.stateIdSource,
694
+ execution,
695
+ workspace,
696
+ fileReview: capabilities.fileReview,
697
+ });
698
+ if (reinvoked.kind === "settled")
699
+ return { kind: "settled", settlement: reinvoked.settlement };
700
+ const mcp = await resolveMcpServersAndPolicies(deps, { execution, session, sessionId, blueprint, environment });
701
+ const skills = await mountSkills(deps, { blueprint, sessionId, primaryDir: workspace.primaryDir });
702
+ const attachments = await resolveTurnAttachments(deps, {
703
+ spec,
704
+ sessionId,
705
+ primaryDir: workspace.primaryDir,
706
+ visionProfile: capabilities.visionProfile,
707
+ });
708
+ const appliedToolCallIds = await applyApprovedWrites(deps, { workspace, reinvocation: reinvoked.reinvocation });
709
+ if (appliedToolCallIds.size > 0) {
710
+ // Persist the applied writes (tool calls now COMPLETED with the approved
711
+ // diff) before reinvocation, so the applied state is durable even if the
712
+ // continuation fails, and the UI reflects it immediately.
713
+ await persist();
714
+ }
715
+ return {
716
+ kind: "ready",
717
+ input: {
718
+ executionId: deps.input.executionId,
719
+ threadId: deps.input.threadId,
720
+ turnSeq: deps.input.turnSeq,
721
+ sessionId,
722
+ approvalDecisions: reinvoked.reinvocation.approvalDecisions,
723
+ execution,
724
+ session,
725
+ blueprint,
726
+ environment,
727
+ workspace,
728
+ mcp,
729
+ skills,
730
+ attachments,
731
+ appliedToolCallIds,
732
+ model: resolveModelPreferences(spec),
733
+ structuredOutputSchema: structuredOutputSchemaOf(spec),
734
+ standing: resolveStandingContext(deps, { execution, spec, blueprint }),
735
+ artifactStorage: deps.artifactStorage,
736
+ },
737
+ };
738
+ }
739
+ //# sourceMappingURL=turn-context.js.map