@stigmer/runner 3.15.1 → 3.15.2-dev.20260913223433

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 (518) hide show
  1. package/README.md +2 -4
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/__test-utils__/execution-record-fixture.d.ts +14 -0
  4. package/dist/__test-utils__/execution-record-fixture.js +4 -0
  5. package/dist/__test-utils__/execution-record-fixture.js.map +1 -1
  6. package/dist/__test-utils__/file-review-projection.d.ts +40 -0
  7. package/dist/__test-utils__/file-review-projection.js +136 -0
  8. package/dist/__test-utils__/file-review-projection.js.map +1 -0
  9. package/dist/__test-utils__/harness-contract/types.d.ts +23 -1
  10. package/dist/__test-utils__/harness-contract/types.js +15 -0
  11. package/dist/__test-utils__/harness-contract/types.js.map +1 -1
  12. package/dist/__test-utils__/hermetic-activity.d.ts +44 -12
  13. package/dist/__test-utils__/hermetic-activity.js +52 -14
  14. package/dist/__test-utils__/hermetic-activity.js.map +1 -1
  15. package/dist/__test-utils__/model-registry-fixture.d.ts +32 -7
  16. package/dist/__test-utils__/model-registry-fixture.js +31 -7
  17. package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
  18. package/dist/__test-utils__/turn-input-fixture.d.ts +1 -1
  19. package/dist/__test-utils__/turn-input-fixture.js +1 -1
  20. package/dist/__test-utils__/turn-input-fixture.js.map +1 -1
  21. package/dist/activities/call-agent.js +2 -2
  22. package/dist/activities/call-agent.js.map +1 -1
  23. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +45 -3
  24. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
  25. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +0 -9
  26. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +2 -41
  27. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
  28. package/dist/activities/execute-cursor/adapter.d.ts +7 -5
  29. package/dist/activities/execute-cursor/adapter.js +7 -5
  30. package/dist/activities/execute-cursor/adapter.js.map +1 -1
  31. package/dist/activities/execute-cursor/approval-policy.d.ts +5 -2
  32. package/dist/activities/execute-cursor/approval-policy.js +5 -2
  33. package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
  34. package/dist/activities/execute-cursor/approval-state.d.ts +16 -8
  35. package/dist/activities/execute-cursor/approval-state.js +6 -3
  36. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  37. package/dist/activities/execute-cursor/cas-observations.d.ts +14 -2
  38. package/dist/activities/execute-cursor/cas-observations.js +19 -2
  39. package/dist/activities/execute-cursor/cas-observations.js.map +1 -1
  40. package/dist/activities/execute-cursor/cursor-capabilities.d.ts +8 -4
  41. package/dist/activities/execute-cursor/cursor-capabilities.js +21 -4
  42. package/dist/activities/execute-cursor/cursor-capabilities.js.map +1 -1
  43. package/dist/activities/execute-cursor/message-translator.d.ts +12 -20
  44. package/dist/activities/execute-cursor/message-translator.js +19 -32
  45. package/dist/activities/execute-cursor/message-translator.js.map +1 -1
  46. package/dist/activities/execute-cursor/prompt-builder.d.ts +48 -58
  47. package/dist/activities/execute-cursor/prompt-builder.js +76 -95
  48. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  49. package/dist/activities/execute-cursor/turn-boundary.d.ts +27 -55
  50. package/dist/activities/execute-cursor/turn-boundary.js +30 -90
  51. package/dist/activities/execute-cursor/turn-boundary.js.map +1 -1
  52. package/dist/activities/execute-cursor/turn-settle.d.ts +11 -7
  53. package/dist/activities/execute-cursor/turn-settle.js +37 -49
  54. package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
  55. package/dist/activities/execute-cursor/turn-setup.d.ts +16 -24
  56. package/dist/activities/execute-cursor/turn-setup.js +29 -59
  57. package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
  58. package/dist/activities/execute-cursor/turn-stream.d.ts +3 -8
  59. package/dist/activities/execute-cursor/turn-stream.js +9 -22
  60. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  61. package/dist/activities/execute-cursor/turn.js +5 -1
  62. package/dist/activities/execute-cursor/turn.js.map +1 -1
  63. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +93 -0
  64. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +348 -0
  65. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js.map +1 -0
  66. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +170 -0
  67. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +225 -0
  68. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -0
  69. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.d.ts +24 -0
  70. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +41 -0
  71. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -0
  72. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +54 -0
  73. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +72 -0
  74. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -0
  75. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +187 -23
  76. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +238 -27
  77. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
  78. package/dist/activities/execute-deep-agent/adapter.d.ts +41 -0
  79. package/dist/activities/execute-deep-agent/adapter.js +78 -0
  80. package/dist/activities/execute-deep-agent/adapter.js.map +1 -0
  81. package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +8 -0
  82. package/dist/activities/execute-deep-agent/cas-capture-observer.js +9 -0
  83. package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -1
  84. package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +31 -0
  85. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +40 -0
  86. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js.map +1 -0
  87. package/dist/activities/execute-deep-agent/execution-state.d.ts +4 -11
  88. package/dist/activities/execute-deep-agent/execution-state.js +4 -12
  89. package/dist/activities/execute-deep-agent/execution-state.js.map +1 -1
  90. package/dist/activities/execute-deep-agent/hitl.d.ts +49 -47
  91. package/dist/activities/execute-deep-agent/hitl.js +77 -103
  92. package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
  93. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +51 -23
  94. package/dist/activities/execute-deep-agent/prompt-builder.js +115 -76
  95. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  96. package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +18 -32
  97. package/dist/activities/execute-deep-agent/status-builder-shared.js +10 -65
  98. package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
  99. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +10 -8
  100. package/dist/activities/execute-deep-agent/streaming-side-effects.js +12 -14
  101. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  102. package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +36 -13
  103. package/dist/activities/execute-deep-agent/subagent-tracker.js +76 -39
  104. package/dist/activities/execute-deep-agent/subagent-tracker.js.map +1 -1
  105. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -28
  106. package/dist/activities/execute-deep-agent/subagent-transformer.js +29 -82
  107. package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
  108. package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +3 -2
  109. package/dist/activities/execute-deep-agent/subagent-wiring.js +2 -2
  110. package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
  111. package/dist/activities/execute-deep-agent/turn-settle.d.ts +42 -0
  112. package/dist/activities/execute-deep-agent/turn-settle.js +146 -0
  113. package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -0
  114. package/dist/activities/execute-deep-agent/turn-setup.d.ts +212 -0
  115. package/dist/activities/execute-deep-agent/turn-setup.js +430 -0
  116. package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -0
  117. package/dist/activities/execute-deep-agent/turn-stream.d.ts +97 -0
  118. package/dist/activities/execute-deep-agent/turn-stream.js +242 -0
  119. package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -0
  120. package/dist/activities/execute-deep-agent/turn.d.ts +42 -0
  121. package/dist/activities/execute-deep-agent/turn.js +116 -0
  122. package/dist/activities/execute-deep-agent/turn.js.map +1 -0
  123. package/dist/activities/execute-deep-agent/v3-event-recorder.d.ts +6 -5
  124. package/dist/activities/execute-deep-agent/v3-event-recorder.js +6 -5
  125. package/dist/activities/execute-deep-agent/v3-event-recorder.js.map +1 -1
  126. package/dist/activities/execute-deep-agent/v3-events.d.ts +1 -1
  127. package/dist/activities/execute-deep-agent/v3-events.js +1 -1
  128. package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +60 -17
  129. package/dist/activities/execute-deep-agent/v3-status-builder.js +72 -57
  130. package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
  131. package/dist/harness/approval-decisions.d.ts +61 -0
  132. package/dist/harness/approval-decisions.js +102 -0
  133. package/dist/harness/approval-decisions.js.map +1 -0
  134. package/dist/harness/capabilities.d.ts +16 -5
  135. package/dist/harness/capabilities.js +9 -0
  136. package/dist/harness/capabilities.js.map +1 -1
  137. package/dist/harness/capture.d.ts +144 -0
  138. package/dist/harness/capture.js +219 -0
  139. package/dist/harness/capture.js.map +1 -0
  140. package/dist/harness/persist-chokepoint.d.ts +19 -4
  141. package/dist/harness/persist-chokepoint.js +17 -4
  142. package/dist/harness/persist-chokepoint.js.map +1 -1
  143. package/dist/harness/run-turn.js +115 -29
  144. package/dist/harness/run-turn.js.map +1 -1
  145. package/dist/harness/terminal-table.d.ts +40 -11
  146. package/dist/harness/terminal-table.js +58 -19
  147. package/dist/harness/terminal-table.js.map +1 -1
  148. package/dist/harness/turn-context.d.ts +141 -76
  149. package/dist/harness/turn-context.js +194 -99
  150. package/dist/harness/turn-context.js.map +1 -1
  151. package/dist/harness/types.d.ts +65 -12
  152. package/dist/harness/types.js +8 -2
  153. package/dist/harness/types.js.map +1 -1
  154. package/dist/harness-adapters.d.ts +12 -8
  155. package/dist/harness-adapters.js +17 -9
  156. package/dist/harness-adapters.js.map +1 -1
  157. package/dist/middleware/approval-gate.d.ts +1 -1
  158. package/dist/middleware/approval-gate.js +1 -1
  159. package/dist/middleware/approval-gate.js.map +1 -1
  160. package/dist/middleware/cost-advisory.d.ts +39 -0
  161. package/dist/middleware/{cost-cap.js → cost-advisory.js} +37 -55
  162. package/dist/middleware/cost-advisory.js.map +1 -0
  163. package/dist/middleware/index.d.ts +23 -12
  164. package/dist/middleware/index.js +26 -18
  165. package/dist/middleware/index.js.map +1 -1
  166. package/dist/middleware/types.d.ts +3 -2
  167. package/dist/runner-manager.js +1 -5
  168. package/dist/runner-manager.js.map +1 -1
  169. package/dist/runner.js +1 -5
  170. package/dist/runner.js.map +1 -1
  171. package/dist/shared/attachment-resolver.d.ts +41 -18
  172. package/dist/shared/attachment-resolver.js +178 -46
  173. package/dist/shared/attachment-resolver.js.map +1 -1
  174. package/dist/shared/attachment-zip.d.ts +85 -0
  175. package/dist/shared/attachment-zip.js +185 -0
  176. package/dist/shared/attachment-zip.js.map +1 -0
  177. package/dist/shared/connect-backfill.js +5 -4
  178. package/dist/shared/connect-backfill.js.map +1 -1
  179. package/dist/shared/cost-guard.d.ts +3 -3
  180. package/dist/shared/cost-guard.js +3 -3
  181. package/dist/shared/execution-status-writer.d.ts +5 -5
  182. package/dist/shared/execution-status-writer.js +5 -5
  183. package/dist/shared/extract-structured-output.d.ts +8 -7
  184. package/dist/shared/extract-structured-output.js +8 -7
  185. package/dist/shared/extract-structured-output.js.map +1 -1
  186. package/dist/shared/filereview/cas-progress.d.ts +1 -18
  187. package/dist/shared/filereview/cas-progress.js +4 -0
  188. package/dist/shared/filereview/cas-progress.js.map +1 -1
  189. package/dist/shared/filereview/cas-touched.d.ts +56 -0
  190. package/dist/shared/filereview/cas-touched.js +63 -0
  191. package/dist/shared/filereview/cas-touched.js.map +1 -0
  192. package/dist/shared/filereview/index.d.ts +2 -1
  193. package/dist/shared/filereview/index.js +2 -1
  194. package/dist/shared/filereview/index.js.map +1 -1
  195. package/dist/shared/mcp-resolver.d.ts +4 -5
  196. package/dist/shared/mcp-resolver.js +4 -5
  197. package/dist/shared/mcp-resolver.js.map +1 -1
  198. package/dist/shared/persist-decision.d.ts +3 -4
  199. package/dist/shared/persist-decision.js +3 -4
  200. package/dist/shared/persist-decision.js.map +1 -1
  201. package/dist/shared/plan-mode-permissions.d.ts +2 -2
  202. package/dist/shared/plan-mode-permissions.js +2 -2
  203. package/dist/shared/plan-mode-prompt.d.ts +1 -1
  204. package/dist/shared/plan-mode-prompt.js +1 -1
  205. package/dist/shared/prompt-sections.d.ts +163 -0
  206. package/dist/shared/prompt-sections.js +164 -0
  207. package/dist/shared/prompt-sections.js.map +1 -0
  208. package/dist/shared/skill-resolver.d.ts +4 -2
  209. package/dist/shared/skill-resolver.js +4 -2
  210. package/dist/shared/skill-resolver.js.map +1 -1
  211. package/dist/shared/tool-rounds.d.ts +32 -0
  212. package/dist/shared/tool-rounds.js +43 -0
  213. package/dist/shared/tool-rounds.js.map +1 -1
  214. package/dist/shared/tool-row.d.ts +34 -0
  215. package/dist/shared/tool-row.js +52 -0
  216. package/dist/shared/tool-row.js.map +1 -1
  217. package/dist/shared/workspace/session-provision.d.ts +3 -3
  218. package/dist/shared/workspace/session-provision.js +3 -3
  219. package/dist/shared/workspace/stigmer-link.d.ts +3 -3
  220. package/dist/shared/workspace/stigmer-link.js +3 -3
  221. package/dist/shared/workspace/writeback-coordinator.d.ts +15 -22
  222. package/dist/shared/workspace/writeback-coordinator.js +16 -53
  223. package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
  224. package/dist/tools/index.d.ts +1 -1
  225. package/dist/tools/index.js +1 -1
  226. package/package.json +4 -4
  227. package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +1 -0
  228. package/src/__test-utils__/execution-record-fixture.ts +18 -2
  229. package/src/__test-utils__/file-review-projection.ts +156 -0
  230. package/src/__test-utils__/git-workspace-fixture.ts +58 -0
  231. package/src/__test-utils__/harness-boot-order-child.ts +7 -2
  232. package/src/__test-utils__/harness-contract/contract.ts +33 -20
  233. package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
  234. package/src/__test-utils__/harness-contract/runtime-contract.ts +328 -10
  235. package/src/__test-utils__/harness-contract/scripted-adapter.ts +38 -6
  236. package/src/__test-utils__/harness-contract/types.ts +27 -3
  237. package/src/__test-utils__/hermetic-activity.ts +52 -14
  238. package/src/__test-utils__/model-registry-fixture.ts +32 -7
  239. package/src/__test-utils__/module-specifiers.ts +24 -0
  240. package/src/__test-utils__/turn-input-fixture.ts +2 -2
  241. package/src/__tests__/harness-boot-order.test.ts +9 -2
  242. package/src/activities/call-agent.ts +2 -2
  243. package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +47 -3
  244. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +2 -48
  245. package/src/activities/execute-cursor/__tests__/approval-decisions-agree.test.ts +1 -1
  246. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +6 -1
  247. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +52 -27
  248. package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +25 -0
  249. package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.everything.prompt.md +166 -0
  250. package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.plan-mode.prompt.md +186 -0
  251. package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.recovery.prompt.md +191 -0
  252. package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.reinvocation.prompt.md +13 -0
  253. package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.prefixed.prompt.md +32 -0
  254. package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.raw.prompt.md +1 -0
  255. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -2
  256. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-error-at-create.status.json +4 -0
  257. package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +24 -4
  258. package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +6 -5
  259. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +9 -0
  260. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +11 -8
  261. package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +229 -0
  262. package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +3 -3
  263. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +2 -2
  264. package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +78 -234
  265. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +1 -26
  266. package/src/activities/execute-cursor/adapter.ts +7 -5
  267. package/src/activities/execute-cursor/approval-policy.ts +5 -2
  268. package/src/activities/execute-cursor/approval-state.ts +16 -8
  269. package/src/activities/execute-cursor/cas-observations.ts +20 -2
  270. package/src/activities/execute-cursor/cursor-capabilities.ts +24 -5
  271. package/src/activities/execute-cursor/message-translator.ts +29 -41
  272. package/src/activities/execute-cursor/prompt-builder.ts +99 -157
  273. package/src/activities/execute-cursor/turn-boundary.ts +34 -140
  274. package/src/activities/execute-cursor/turn-settle.ts +37 -51
  275. package/src/activities/execute-cursor/turn-setup.ts +30 -70
  276. package/src/activities/execute-cursor/turn-stream.ts +8 -31
  277. package/src/activities/execute-cursor/turn.ts +5 -1
  278. package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +391 -0
  279. package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +313 -0
  280. package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +48 -0
  281. package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +85 -0
  282. package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +383 -36
  283. package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +76 -0
  284. package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +47 -0
  285. package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +80 -0
  286. package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +1 -1
  287. package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -2
  288. package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -3
  289. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.build-from-plan.prompt.md +172 -0
  290. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.everything.prompt.md +167 -0
  291. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.minimal.prompt.md +36 -0
  292. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.plan-mode.prompt.md +178 -0
  293. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.skills-below-threshold.prompt.md +158 -0
  294. package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +210 -0
  295. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +74 -0
  296. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +104 -0
  297. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +78 -0
  298. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +138 -0
  299. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +68 -0
  300. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +154 -0
  301. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +116 -0
  302. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +69 -0
  303. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +67 -0
  304. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/plain-turn.status.json +47 -0
  305. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +68 -0
  306. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +280 -0
  307. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +77 -0
  308. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +18 -0
  309. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +99 -0
  310. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +109 -0
  311. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +76 -0
  312. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.text-fallback.status.json +51 -0
  313. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tier2.status.json +51 -0
  314. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tool-strategy.status.json +56 -0
  315. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +90 -0
  316. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +70 -0
  317. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +71 -0
  318. package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +144 -0
  319. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +184 -0
  320. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +203 -0
  321. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +170 -0
  322. package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +175 -0
  323. package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +206 -0
  324. package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +142 -0
  325. package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +126 -0
  326. package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +139 -0
  327. package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +122 -0
  328. package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +236 -0
  329. package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +161 -0
  330. package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +157 -0
  331. package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +152 -0
  332. package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +50 -122
  333. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +8 -6
  334. package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +2 -2
  335. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +35 -35
  336. package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +247 -0
  337. package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +329 -0
  338. package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +1 -20
  339. package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +78 -0
  340. package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +1 -1
  341. package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +2 -2
  342. package/src/activities/execute-deep-agent/__tests__/subagent-tracker.test.ts +57 -31
  343. package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +19 -79
  344. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +15 -15
  345. package/src/activities/execute-deep-agent/__tests__/summarization-verification.test.ts +1 -1
  346. package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +391 -0
  347. package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +343 -38
  348. package/src/activities/execute-deep-agent/__tests__/vision-input.test.ts +5 -5
  349. package/src/activities/execute-deep-agent/adapter.ts +94 -0
  350. package/src/activities/execute-deep-agent/cas-capture-observer.ts +11 -0
  351. package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +43 -0
  352. package/src/activities/execute-deep-agent/execution-state.ts +4 -13
  353. package/src/activities/execute-deep-agent/hitl.ts +100 -123
  354. package/src/activities/execute-deep-agent/prompt-builder.ts +141 -115
  355. package/src/activities/execute-deep-agent/status-builder-shared.ts +21 -84
  356. package/src/activities/execute-deep-agent/streaming-side-effects.ts +13 -19
  357. package/src/activities/execute-deep-agent/subagent-tracker.ts +77 -42
  358. package/src/activities/execute-deep-agent/subagent-transformer.ts +46 -105
  359. package/src/activities/execute-deep-agent/subagent-wiring.ts +5 -4
  360. package/src/activities/execute-deep-agent/turn-settle.ts +164 -0
  361. package/src/activities/execute-deep-agent/turn-setup.ts +541 -0
  362. package/src/activities/execute-deep-agent/turn-stream.ts +313 -0
  363. package/src/activities/execute-deep-agent/turn.ts +131 -0
  364. package/src/activities/execute-deep-agent/v3-event-recorder.ts +6 -5
  365. package/src/activities/execute-deep-agent/v3-events.ts +1 -1
  366. package/src/activities/execute-deep-agent/v3-status-builder.ts +84 -63
  367. package/src/harness/__tests__/approval-decisions.test.ts +116 -0
  368. package/src/harness/__tests__/capture.test.ts +268 -0
  369. package/src/harness/__tests__/mount-skills.test.ts +169 -0
  370. package/src/harness/__tests__/persist-chokepoint.test.ts +38 -2
  371. package/src/harness/__tests__/run-turn-deterministic.test.ts +94 -0
  372. package/src/harness/__tests__/run-turn.test.ts +39 -23
  373. package/src/harness/__tests__/turn-attachments-vision.test.ts +80 -0
  374. package/src/harness/__tests__/turn-context.test.ts +73 -26
  375. package/src/harness/approval-decisions.ts +104 -0
  376. package/src/harness/capabilities.ts +16 -5
  377. package/src/harness/capture.ts +301 -0
  378. package/src/harness/persist-chokepoint.ts +24 -4
  379. package/src/harness/run-turn.ts +120 -29
  380. package/src/harness/terminal-table.ts +61 -20
  381. package/src/harness/turn-context.ts +208 -111
  382. package/src/harness/types.ts +66 -12
  383. package/src/harness-adapters.ts +17 -9
  384. package/src/middleware/__tests__/approval-gate.test.ts +1 -1
  385. package/src/middleware/__tests__/cost-advisory.test.ts +113 -0
  386. package/src/middleware/approval-gate.ts +2 -2
  387. package/src/middleware/{cost-cap.ts → cost-advisory.ts} +40 -70
  388. package/src/middleware/index.ts +29 -29
  389. package/src/middleware/types.ts +3 -2
  390. package/src/runner-manager.ts +0 -8
  391. package/src/runner.ts +0 -8
  392. package/src/shared/__tests__/attachment-naming.test.ts +6 -19
  393. package/src/shared/__tests__/attachment-resolver.test.ts +158 -8
  394. package/src/shared/__tests__/attachment-zip.test.ts +236 -0
  395. package/src/shared/__tests__/bedrock-adapter.test.ts +1 -1
  396. package/src/shared/__tests__/blueprint-resolver.test.ts +43 -0
  397. package/src/shared/__tests__/cost-guard.test.ts +3 -3
  398. package/src/shared/__tests__/prompt-sections.test.ts +167 -0
  399. package/src/shared/__tests__/secret-leak-scan.test.ts +2 -2
  400. package/src/{activities/execute-deep-agent → shared}/__tests__/stamp-flowed-rows.test.ts +33 -10
  401. package/src/shared/__tests__/vertex-adapter.test.ts +1 -1
  402. package/src/shared/attachment-resolver.ts +218 -55
  403. package/src/shared/attachment-zip.ts +281 -0
  404. package/src/shared/connect-backfill.ts +9 -11
  405. package/src/shared/cost-guard.ts +3 -3
  406. package/src/shared/execution-status-writer.ts +5 -5
  407. package/src/shared/extract-structured-output.ts +8 -7
  408. package/src/shared/filereview/__tests__/capture.test.ts +86 -4
  409. package/src/shared/filereview/__tests__/cas-progress.test.ts +2 -1
  410. package/src/shared/filereview/cas-progress.ts +5 -19
  411. package/src/shared/filereview/cas-touched.ts +89 -0
  412. package/src/shared/filereview/index.ts +4 -2
  413. package/src/shared/mcp-resolver.ts +4 -5
  414. package/src/shared/persist-decision.ts +3 -4
  415. package/src/shared/plan-mode-permissions.ts +2 -2
  416. package/src/shared/plan-mode-prompt.ts +1 -1
  417. package/src/shared/prompt-sections.ts +256 -0
  418. package/src/shared/skill-resolver.ts +4 -2
  419. package/src/shared/tool-rounds.ts +48 -0
  420. package/src/shared/tool-row.ts +61 -0
  421. package/src/shared/workspace/__tests__/stigmer-link.test.ts +7 -22
  422. package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +20 -61
  423. package/src/shared/workspace/session-provision.ts +3 -3
  424. package/src/shared/workspace/stigmer-link.ts +3 -3
  425. package/src/shared/workspace/writeback-coordinator.ts +16 -64
  426. package/src/tools/index.ts +1 -1
  427. package/dist/activities/execute-cursor/capture-flow.d.ts +0 -142
  428. package/dist/activities/execute-cursor/capture-flow.js +0 -285
  429. package/dist/activities/execute-cursor/capture-flow.js.map +0 -1
  430. package/dist/activities/execute-cursor/command-provenance.d.ts +0 -48
  431. package/dist/activities/execute-cursor/command-provenance.js +0 -38
  432. package/dist/activities/execute-cursor/command-provenance.js.map +0 -1
  433. package/dist/activities/execute-deep-agent/attachment-injector.d.ts +0 -134
  434. package/dist/activities/execute-deep-agent/attachment-injector.js +0 -386
  435. package/dist/activities/execute-deep-agent/attachment-injector.js.map +0 -1
  436. package/dist/activities/execute-deep-agent/command-provenance.d.ts +0 -61
  437. package/dist/activities/execute-deep-agent/command-provenance.js +0 -72
  438. package/dist/activities/execute-deep-agent/command-provenance.js.map +0 -1
  439. package/dist/activities/execute-deep-agent/environment.d.ts +0 -24
  440. package/dist/activities/execute-deep-agent/environment.js +0 -58
  441. package/dist/activities/execute-deep-agent/environment.js.map +0 -1
  442. package/dist/activities/execute-deep-agent/event-recorder.d.ts +0 -21
  443. package/dist/activities/execute-deep-agent/event-recorder.js +0 -67
  444. package/dist/activities/execute-deep-agent/event-recorder.js.map +0 -1
  445. package/dist/activities/execute-deep-agent/index.d.ts +0 -15
  446. package/dist/activities/execute-deep-agent/index.js +0 -918
  447. package/dist/activities/execute-deep-agent/index.js.map +0 -1
  448. package/dist/activities/execute-deep-agent/mcp-gate.d.ts +0 -28
  449. package/dist/activities/execute-deep-agent/mcp-gate.js +0 -22
  450. package/dist/activities/execute-deep-agent/mcp-gate.js.map +0 -1
  451. package/dist/activities/execute-deep-agent/post-stream.d.ts +0 -23
  452. package/dist/activities/execute-deep-agent/post-stream.js +0 -71
  453. package/dist/activities/execute-deep-agent/post-stream.js.map +0 -1
  454. package/dist/activities/execute-deep-agent/setup.d.ts +0 -136
  455. package/dist/activities/execute-deep-agent/setup.js +0 -767
  456. package/dist/activities/execute-deep-agent/setup.js.map +0 -1
  457. package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +0 -36
  458. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +0 -56
  459. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +0 -1
  460. package/dist/activities/execute-deep-agent/status-builder.d.ts +0 -93
  461. package/dist/activities/execute-deep-agent/status-builder.js +0 -365
  462. package/dist/activities/execute-deep-agent/status-builder.js.map +0 -1
  463. package/dist/activities/execute-deep-agent/streaming-terminal.d.ts +0 -21
  464. package/dist/activities/execute-deep-agent/streaming-terminal.js +0 -80
  465. package/dist/activities/execute-deep-agent/streaming-terminal.js.map +0 -1
  466. package/dist/activities/execute-deep-agent/streaming-v3.d.ts +0 -13
  467. package/dist/activities/execute-deep-agent/streaming-v3.js +0 -174
  468. package/dist/activities/execute-deep-agent/streaming-v3.js.map +0 -1
  469. package/dist/activities/execute-deep-agent/streaming.d.ts +0 -81
  470. package/dist/activities/execute-deep-agent/streaming.js +0 -160
  471. package/dist/activities/execute-deep-agent/streaming.js.map +0 -1
  472. package/dist/middleware/cost-cap.d.ts +0 -22
  473. package/dist/middleware/cost-cap.js.map +0 -1
  474. package/dist/middleware/graceful-stop.d.ts +0 -17
  475. package/dist/middleware/graceful-stop.js +0 -63
  476. package/dist/middleware/graceful-stop.js.map +0 -1
  477. package/dist/shared/skill-writer.d.ts +0 -75
  478. package/dist/shared/skill-writer.js +0 -207
  479. package/dist/shared/skill-writer.js.map +0 -1
  480. package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +0 -1032
  481. package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +0 -240
  482. package/src/activities/execute-cursor/__tests__/progress-substrate.test.ts +0 -169
  483. package/src/activities/execute-cursor/capture-flow.ts +0 -379
  484. package/src/activities/execute-cursor/command-provenance.ts +0 -73
  485. package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +0 -1161
  486. package/src/activities/execute-deep-agent/__tests__/command-provenance.test.ts +0 -252
  487. package/src/activities/execute-deep-agent/__tests__/environment.test.ts +0 -108
  488. package/src/activities/execute-deep-agent/__tests__/event-recorder.test.ts +0 -150
  489. package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +0 -226
  490. package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +0 -318
  491. package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +0 -351
  492. package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +0 -293
  493. package/src/activities/execute-deep-agent/__tests__/index.test.ts +0 -99
  494. package/src/activities/execute-deep-agent/__tests__/mcp-gate.test.ts +0 -44
  495. package/src/activities/execute-deep-agent/__tests__/post-stream.test.ts +0 -112
  496. package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +0 -358
  497. package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +0 -1933
  498. package/src/activities/execute-deep-agent/__tests__/streaming-terminal.test.ts +0 -40
  499. package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +0 -597
  500. package/src/activities/execute-deep-agent/__tests__/streaming.test.ts +0 -508
  501. package/src/activities/execute-deep-agent/attachment-injector.ts +0 -627
  502. package/src/activities/execute-deep-agent/command-provenance.ts +0 -102
  503. package/src/activities/execute-deep-agent/environment.ts +0 -76
  504. package/src/activities/execute-deep-agent/event-recorder.ts +0 -95
  505. package/src/activities/execute-deep-agent/index.ts +0 -1072
  506. package/src/activities/execute-deep-agent/mcp-gate.ts +0 -37
  507. package/src/activities/execute-deep-agent/post-stream.ts +0 -109
  508. package/src/activities/execute-deep-agent/setup.ts +0 -1081
  509. package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +0 -64
  510. package/src/activities/execute-deep-agent/status-builder.ts +0 -481
  511. package/src/activities/execute-deep-agent/streaming-terminal.ts +0 -106
  512. package/src/activities/execute-deep-agent/streaming-v3.ts +0 -277
  513. package/src/activities/execute-deep-agent/streaming.ts +0 -313
  514. package/src/middleware/__tests__/cost-cap.test.ts +0 -192
  515. package/src/middleware/__tests__/graceful-stop.test.ts +0 -105
  516. package/src/middleware/graceful-stop.ts +0 -86
  517. package/src/shared/__tests__/skill-writer.test.ts +0 -372
  518. package/src/shared/skill-writer.ts +0 -266
@@ -1,1072 +0,0 @@
1
- /**
2
- * ExecuteDeepAgent activity — runs a Stigmer deep agent via LangGraph.
3
- *
4
- * Signature matches ExecuteGraphton (Python): (executionId, threadId) → status.
5
- * The slim-payload pattern is preserved: input is just IDs, output is a slim
6
- * AgentExecutionStatus proto.
7
- *
8
- * Phase 3b-iii: full middleware stack + artifact storage, inline publishing,
9
- * incremental git writeback, and post-stream safety net.
10
- */
11
-
12
- import { readFile } from "node:fs/promises";
13
- import { join } from "node:path";
14
- import { Context, CancelledFailure } from "@temporalio/activity";
15
- import { create, clone, type JsonObject } from "@bufbuild/protobuf";
16
- import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
17
- import type { AgentExecution, AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
18
- import { AgentMessageSchema, ToolCallSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
19
- import { ExecutionPhase, FileCaptureClass, FileChangeSetStatus, InteractionMode, MessageType, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
20
- import { activityStarted, activityFinished } from "../../idle-watchdog.js";
21
- import { normalizeActivityInput, type ExecuteActivityInput } from "../../shared/activity-input.js";
22
- import { persistStatus, reportSetupProgress, slimStatus, utcTimestamp } from "../../shared/status.js";
23
- import {
24
- acquireWorkspaceLock,
25
- WorkspaceLockCancelledError,
26
- WorkspaceLockTimeoutError,
27
- type ReleaseWorkspaceLock,
28
- } from "../../shared/workspace/workspace-lock.js";
29
- import {
30
- ensureStigmerSymlink,
31
- removeStigmerSymlink,
32
- } from "../../shared/workspace/stigmer-link.js";
33
- import type { ToolOutputOffloadContext } from "../../shared/status-offload.js";
34
- import { publishPlanArtifact } from "../../shared/plan-artifact.js";
35
- import { classifyTool } from "../../shared/tool-kind.js";
36
- import {
37
- POLICY_ENGINE_VERSION,
38
- toProtoPolicySource,
39
- type PolicySource,
40
- } from "../../shared/approval-policy.js";
41
- import type { Config } from "../../config.js";
42
- import { StigmerClient } from "../../client/stigmer-client.js";
43
- import { performSetup, type SetupResult } from "./setup.js";
44
- import { streamExecution, type StreamResult } from "./streaming.js";
45
- import { loadStreamingConfig } from "../../shared/streaming-scheduler.js";
46
- import { StatusBuilder } from "./status-builder.js";
47
- import { InlinePublisher } from "./inline-publisher.js";
48
- import { WriteBackCoordinator } from "../../shared/workspace/writeback-coordinator.js";
49
- import { processPostStream } from "./post-stream.js";
50
- import { resolveResumeInput, reconcileNonExecutingDecisions, reconcileUnattendedSkips, type GraphStateSnapshot } from "./hitl.js";
51
- import { captureApprovalArtifacts } from "./approval-file-change.js";
52
- import {
53
- applyCaptureDecisions,
54
- captureBaselineToLedger,
55
- captureCandidateToLedger,
56
- } from "../../shared/filereview/capture.js";
57
- import {
58
- captureFileChangeProgress,
59
- createGitProgressSubstrate,
60
- createHybridProgressSubstrate,
61
- newProgressCaptureState,
62
- type ProgressSubstrate,
63
- } from "../../shared/filereview/progress.js";
64
- import {
65
- createCasProgressSubstrate,
66
- type CasTouchedSnapshot,
67
- } from "../../shared/filereview/cas-progress.js";
68
- import { hasCandidateCaptured } from "../../shared/filereview/events.js";
69
- import { casBlobReader, type CasPathCapture } from "../../shared/filereview/cas-substrate.js";
70
- import { partitionIgnoredPathsBySecret } from "../../shared/filereview/secret-paths.js";
71
- import type { CasCaptureObserver } from "./cas-capture-observer.js";
72
- import {
73
- collectSettledToolCallIds,
74
- collectSubAgentToolCallIds,
75
- withholdSecretContentFromMessages,
76
- } from "../../shared/tool-row.js";
77
- import { stampFlowedFileEditRows, stampFlowedSubAgentFileEditRows } from "./stamp-flowed-rows.js";
78
- import { deriveTurnCommandProvenance } from "./command-provenance.js";
79
- import { describeExecutionError } from "../../shared/model-error.js";
80
- import { inferProvider, type LlmProvider } from "../../shared/llm-proxy.js";
81
- import { getShutdownSignalForQueue } from "../../shared/worker-shutdown.js";
82
-
83
- /** The harness id stamped on the deep-agent's file-review ledger events. */
84
- const DEEP_AGENT_HARNESS_ID = "deep-agent";
85
-
86
- /**
87
- * The worker-shutdown terminal status (#776). A shutdown is not a pause: the
88
- * execution will not resume on this worker, so persisting PAUSED would strand
89
- * the user with a lie. The copy is byte-identical to the Cursor harness's
90
- * worker-shutdown branch so every downstream status.error consumer (the Go and
91
- * Java workflow fallbacks, the cloud channel decision table) keys on ONE shape.
92
- */
93
- function buildWorkerShutdownStatus(): AgentExecutionStatus {
94
- return create(AgentExecutionStatusSchema, {
95
- phase: ExecutionPhase.EXECUTION_FAILED,
96
- error: "Execution interrupted: runner worker was shut down. Retry or resume.",
97
- completedAt: utcTimestamp(),
98
- messages: [
99
- create(AgentMessageSchema, {
100
- type: MessageType.MESSAGE_SYSTEM,
101
- content: "Execution interrupted: the runner worker was shut down while the agent was still running. You can retry or resume.",
102
- timestamp: utcTimestamp(),
103
- }),
104
- ],
105
- });
106
- }
107
-
108
- /**
109
- * Best-effort provider inference for error-message wording. inferProvider
110
- * throws on unrecognized names; an error path must never throw over a label.
111
- */
112
- function tryInferProvider(modelName: string): LlmProvider | undefined {
113
- try {
114
- return inferProvider(modelName);
115
- } catch {
116
- return undefined;
117
- }
118
- }
119
-
120
- export function createDeepAgentActivities(config: Config) {
121
- const client = new StigmerClient({
122
- endpoint: config.stigmerBackendEndpoint,
123
- token: config.stigmerToken,
124
- tokenRef: config.stigmerTokenRef,
125
- runnerTokenRef: config.stigmerRunnerTokenRef,
126
- });
127
-
128
- const streamingConfig = loadStreamingConfig();
129
-
130
- return {
131
- // Accepts the new typed object OR the legacy positional args (transitional
132
- // dual-shape so the runner can deploy before the control planes — see
133
- // shared/activity-input.ts). Drop the positional arm once both control
134
- // planes send the object.
135
- ExecuteDeepAgent: async (
136
- arg0: ExecuteActivityInput | string,
137
- arg1?: string,
138
- ): Promise<unknown> => {
139
- const { executionId, threadId, turnSeq } = normalizeActivityInput(arg0, arg1);
140
- activityStarted();
141
- // Worker-shutdown classification channel (#776): the pause paths below
142
- // consult this signal to distinguish "my worker is draining" (SIGTERM,
143
- // desktop quit) from "the orchestrator cancelled me" (a real user
144
- // pause). See shared/worker-shutdown.ts for the ownership contract.
145
- const shutdownSignal = getShutdownSignalForQueue(Context.current().info.taskQueue);
146
- let setup: SetupResult | null = null;
147
- // Exclusive turn lock on the workspace working tree — held across the
148
- // entire tree-mutating window (decision reconcile, agent writes,
149
- // candidate capture, write-back) and released in the finally. Mirrors
150
- // the Cursor harness; see shared/workspace/workspace-lock.ts.
151
- let releaseWorkspaceLock: ReleaseWorkspaceLock | undefined;
152
-
153
- try {
154
- console.log(`[ExecuteDeepAgent] Started for execution ${executionId}`);
155
-
156
- setup = await performSetup({ config, client, executionId, threadId });
157
-
158
- // Single offload context for every persist in this execution: spill
159
- // oversized tool outputs (e.g. computer-use screenshots) to artifact
160
- // storage so the UI can render them, and keep the status under the
161
- // gRPC cap. Threaded into the streaming loop and reused for the
162
- // terminal persists below so the guard is never skipped. Undefined when
163
- // there is no artifact store (proxy misconfig): offload is then disabled,
164
- // but persistStatus still enforces the aggregate size cap, so persistence
165
- // can never silently blow past the gRPC limit.
166
- const statusOffload: ToolOutputOffloadContext | undefined = setup.artifactStorage
167
- ? { artifactStorage: setup.artifactStorage, executionId }
168
- : undefined;
169
-
170
- // Read the graph checkpoint once, to resolve how a pending approval
171
- // interrupt continues (Command(resume) vs a fresh replay). Reading once
172
- // avoids a redundant round-trip on the durable (http) saver.
173
- const graphState: GraphStateSnapshot = await setup.agentGraph.getState(setup.langgraphConfig);
174
-
175
- // Seed from the persisted transcript whenever the execution already has
176
- // committed history, so streamed deltas append onto it instead of
177
- // replacing it. This holds for every re-invocation — durable resume and
178
- // memory-checkpointer replay alike — and is a no-op on a first run.
179
- const initialStatus = shouldSeedFromPersistedTranscript(setup.execution)
180
- ? seedStatusFromExecution(setup.execution)
181
- : create(AgentExecutionStatusSchema, {});
182
-
183
- // The semantic retriever's injection outcome (DD-008 D5), computed at
184
- // prompt build in setup Step 8. Stamped here — before the first
185
- // persist — because setup predates this status object; the server's
186
- // presence-guarded merge preserves it across report-less writes. On a
187
- // re-invocation the seeded status already carries the (replayed)
188
- // report, so this stamp is idempotent by construction.
189
- if (setup.recalledMemoriesReport !== undefined) {
190
- initialStatus.recalledMemoriesReport = setup.recalledMemoriesReport;
191
- }
192
-
193
- const statusBuilder = new StatusBuilder(executionId, initialStatus);
194
-
195
- statusBuilder.setApprovalProvider({
196
- policies: setup.approvalPolicies,
197
- toolServerMap: setup.toolServerMap,
198
- leasedCategories: setup.leasedCategories,
199
- globalBypass: setup.globalBypass,
200
- unattended: setup.unattended,
201
- });
202
-
203
- const resume = resolveResumeInput(setup.execution, graphState);
204
-
205
- // Not an approval resume -> setup.langgraphInput, the single
206
- // construction site of the turn's user message (string or multimodal
207
- // content blocks when the turn carries inline images).
208
- const effectiveInput = resume.isResumeFromApproval
209
- ? resume.graphInput
210
- : setup.langgraphInput;
211
-
212
- const inlinePublisher = new InlinePublisher({
213
- workspaceBackend: setup.workspaceBackend,
214
- artifactStorage: setup.artifactStorage,
215
- statusWriter: statusBuilder,
216
- executionId,
217
- });
218
-
219
- const workspaceEntries = setup.session.spec?.workspaceEntries ?? [];
220
- const writebackCoordinator = setup.provisionResults.length > 0
221
- ? new WriteBackCoordinator({
222
- statusWriter: statusBuilder,
223
- executionId,
224
- // Branch/PR are session-scoped: every turn of this session
225
- // appends commits to the same stigmer/<session-id> branch.
226
- sessionId: setup.session.metadata?.id ?? "",
227
- // The PR API token, plumbed from the resolved execution env —
228
- // gitMetadata.repoUrl is token-stripped by construction, so the
229
- // coordinator can never recover it from provisioning state.
230
- githubToken: setup.mergedEnvVars.GITHUB_TOKEN ?? "",
231
- provisionResults: setup.provisionResults,
232
- workspaceEntries: workspaceEntries as any,
233
- workspaceBackend: setup.workspaceBackend,
234
- })
235
- : null;
236
-
237
- // Apply-then-review capture identity + substrate root for this turn.
238
- const gitRoot = setup.workspaceBackend.rootDir;
239
- const changeSetId = `${executionId}:${turnSeq}`;
240
-
241
- // Serialize this turn against every other execution sharing this
242
- // working tree — an unserialized concurrent write lands inside this
243
- // turn's baseline→candidate window and gets misattributed to this
244
- // turn's review. Acquired before ANY tree mutation below (decision
245
- // reconcile, agent writes, candidate capture, write-back); waiting
246
- // surfaces a visible state and heartbeats, and a cancel aborts the
247
- // wait immediately. Mirrors the Cursor harness wiring exactly.
248
- try {
249
- releaseWorkspaceLock = await acquireWorkspaceLock(gitRoot, {
250
- onWaiting: () => reportSetupProgress(
251
- client, executionId, "Waiting for workspace — in use by another session",
252
- ),
253
- heartbeat: () => Context.current().heartbeat(),
254
- signal: Context.current().cancellationSignal,
255
- timeoutMs: config.workspaceLockTimeoutMs,
256
- });
257
- } catch (lockErr) {
258
- if (lockErr instanceof WorkspaceLockCancelledError) {
259
- throw new CancelledFailure("Activity cancelled while waiting for the workspace lock");
260
- }
261
- if (lockErr instanceof WorkspaceLockTimeoutError) {
262
- const failedStatus = create(AgentExecutionStatusSchema, {
263
- phase: ExecutionPhase.EXECUTION_FAILED,
264
- error: lockErr.message,
265
- completedAt: utcTimestamp(),
266
- messages: [
267
- create(AgentMessageSchema, {
268
- type: MessageType.MESSAGE_SYSTEM,
269
- content: `Execution failed: ${lockErr.message}`,
270
- timestamp: utcTimestamp(),
271
- }),
272
- ],
273
- });
274
- await persistStatus(client, executionId, failedStatus, { offload: statusOffload });
275
- return slimStatus(failedStatus);
276
- }
277
- throw lockErr;
278
- }
279
-
280
- // Bridge the workspace to the session's platform dir so the agent's
281
- // file tools can read platform-mounted content (`.stigmer/inputs/…`
282
- // attachments incl. the approved plan, `.stigmer/skills/…`) — the
283
- // deepagents FilesystemBackend resolves paths against the workspace
284
- // root and knows nothing of the LocalWorkspaceBackend's `.stigmer`
285
- // routing. Two ordering invariants (see shared/workspace/stigmer-link.ts):
286
- // - AFTER the lock: the link is a tree mutation; sessions sharing one
287
- // attached directory must not re-point it under a running turn.
288
- // - BEFORE the baseline capture below: present in both the baseline
289
- // and candidate trees, the link cancels out of the file-review diff
290
- // even when the workspace's git excludes do not list `.stigmer`.
291
- // Removed in cleanup() (the finally below), before the lock releases.
292
- if (setup.workspaceBackend.platformDir) {
293
- await ensureStigmerSymlink(gitRoot, setup.workspaceBackend.platformDir);
294
- }
295
-
296
- // (1) Capture-mode resume — reconcile any DECIDED change set FIRST (this
297
- // drops the per-execution refs), before the next baseline re-pins them
298
- // (refs are executionId-keyed). Then (2) short-circuit a pure file-review
299
- // resume — the agent already finished in a prior segment, so finalize with
300
- // NO model re-invocation. A mixed turn (a tool gate is also pending) falls
301
- // through to resume the graph for the gated tool.
302
- if (setup.captureMode) {
303
- const decidedSets = (setup.execution.status?.fileChangeSets ?? []).filter(
304
- (cs) => cs.status === FileChangeSetStatus.DECIDED,
305
- );
306
- let reconciledAny = false;
307
- let reconcileFailed = false;
308
- let reconcileFailureDetail = "";
309
- // The CAS blob reader reconciles ignored-file decisions from the durable
310
- // manifest (git-only turns have no manifest, so the CAS branch no-ops).
311
- // Undefined with no store: this block still runs for a git workspace
312
- // whose captureMode is true without storage, but such a turn captures no
313
- // CAS files (captureIgnored is off), so its change set carries no CAS ref
314
- // and applyCaptureDecisions never needs the reader.
315
- const casReadBlob = setup.artifactStorage
316
- ? casBlobReader(setup.artifactStorage)
317
- : undefined;
318
- for (const changeSet of decidedSets) {
319
- const capResult = await applyCaptureDecisions({
320
- status: initialStatus,
321
- gitRoot,
322
- executionId,
323
- changeSet,
324
- harnessId: DEEP_AGENT_HARNESS_ID,
325
- storage: setup.artifactStorage,
326
- readBlob: casReadBlob,
327
- gitWorkspace: setup.gitWorkspace,
328
- });
329
- if (!capResult.isCaptureTurn) continue;
330
- reconciledAny = true;
331
- if (capResult.failed) {
332
- reconcileFailed = true;
333
- reconcileFailureDetail =
334
- capResult.failureDetail ?? "file review reconcile failed";
335
- }
336
- }
337
-
338
- if (reconciledAny) {
339
- if (reconcileFailed) {
340
- // What-you-approve-is-what-applies failed: nothing was applied.
341
- initialStatus.phase = ExecutionPhase.EXECUTION_FAILED;
342
- initialStatus.error = `File review reconcile failed: ${reconcileFailureDetail}`;
343
- initialStatus.completedAt = utcTimestamp();
344
- await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
345
- return slimStatus(initialStatus);
346
- }
347
- if (!hasPendingToolApprovals(setup.execution)) {
348
- // Pure file review: the agent's final answer + outputs already rode
349
- // the prior segment's persisted status (seeded into initialStatus).
350
- // Complete now and push only the approved tree — NO model re-run.
351
- //
352
- // The discriminator is the PERSISTED transcript, not the live graph
353
- // checkpoint: it must behave identically across checkpointer
354
- // backends, but the live checkpoint does not — the memory backend
355
- // (opt-in, tests) recreates it empty each invocation while the
356
- // durable backends (sqlite local / http cloud) preserve it, so a
357
- // graphState-based check would diverge and could wrongly fire for a
358
- // mixed turn. A mixed turn leaves WAITING_APPROVAL tool rows in the
359
- // transcript; a pure file-review turn leaves none.
360
- initialStatus.phase = ExecutionPhase.EXECUTION_COMPLETED;
361
- initialStatus.completedAt = utcTimestamp();
362
- if (writebackCoordinator) {
363
- await processCaptureWriteback(writebackCoordinator, executionId);
364
- }
365
- await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
366
- // Surface the agent's final outputs (computed in the prior segment,
367
- // carried on the seeded status) the same way a normal completion does.
368
- const slim = slimStatus(initialStatus) as Record<string, unknown>;
369
- const lastAi = [...initialStatus.messages]
370
- .reverse()
371
- .find((m) => m.type === MessageType.MESSAGE_AI);
372
- if (lastAi?.content) slim.final_text = lastAi.content;
373
- if (initialStatus.structuredOutput !== undefined) {
374
- slim.structured = initialStatus.structuredOutput;
375
- }
376
- return slim;
377
- }
378
- // else: mixed turn — fall through; the graph resumes for the gated
379
- // tool and a new baseline is taken below for the continuation segment.
380
- }
381
- }
382
-
383
- // Turn start (capture mode): pin the pre-turn tree + author BASELINE. Taken
384
- // AFTER any reconcile above and AFTER performSetup's workspace writes and
385
- // the `.stigmer` symlink above, so the baseline absorbs the reconciled
386
- // state and the runner-owned files (the symlink then cancels out of the
387
- // baseline→candidate diff).
388
- let captureBaselineTree = "";
389
- // Snapshot the sub-agent tool-call ids that exist BEFORE this turn's
390
- // stream, so the turn-boundary stamp scopes itself to sub-agent rows
391
- // created this turn (empty here by construction — this harness never
392
- // seeds prior sub-agents — but computed, not assumed, so it stays correct
393
- // if that ever changes; see collectSubAgentToolCallIds).
394
- const priorSubAgentToolCallIds = collectSubAgentToolCallIds(initialStatus.subAgentExecutions);
395
- // Snapshot the top-level tool-call ids already SETTLED before this turn's
396
- // stream, so the approved-command provenance (DD-28) scopes itself to THIS
397
- // turn's executed commands by identity. The deep-agent's approved shell
398
- // executes in place at its seeded position, so the Cursor positional scope
399
- // would miss it — see execute-deep-agent/command-provenance.ts.
400
- const priorSettledToolCallIds = collectSettledToolCallIds(initialStatus.messages);
401
- if (setup.captureMode) {
402
- captureBaselineTree = await captureBaselineToLedger({
403
- status: initialStatus,
404
- gitRoot,
405
- executionId,
406
- changeSetId,
407
- harnessId: DEEP_AGENT_HARNESS_ID,
408
- gitWorkspace: setup.gitWorkspace,
409
- });
410
- }
411
-
412
- // Per-turn state + substrate for mid-run live capture (DD-32 / DD-33). The
413
- // floor lives in progressState; the substrate is chosen for this turn's
414
- // workspace shape and owns its own short-circuit cache. An atomic snapshot
415
- // of the shared observer feeds the CAS/HYBRID substrates — copied
416
- // synchronously so a concurrent sub-agent write cannot mutate it mid-read.
417
- const progressState = newProgressCaptureState();
418
- const casObserver = setup.casObserver;
419
- const readObserverTouched = (): CasTouchedSnapshot => ({
420
- before: new Map(casObserver.before),
421
- blockedSecretPaths: new Set(casObserver.blockedSecretPaths),
422
- });
423
- // Git tree -> hybrid (numstat for tracked + observer for gitignored);
424
- // non-git -> cas over the observer (no baseline tree needed). Undefined
425
- // outside capture mode (writes are deny-gated, nothing is captured).
426
- const progressSubstrate: ProgressSubstrate | undefined = !setup.captureMode
427
- ? undefined
428
- : setup.gitWorkspace
429
- ? captureBaselineTree
430
- ? createHybridProgressSubstrate(
431
- createGitProgressSubstrate({
432
- workspaceRoot: gitRoot,
433
- executionId,
434
- baselineTree: captureBaselineTree,
435
- }),
436
- createCasProgressSubstrate({ workspaceRoot: gitRoot, read: readObserverTouched }),
437
- )
438
- : undefined
439
- : createCasProgressSubstrate({ workspaceRoot: gitRoot, read: readObserverTouched });
440
-
441
- const cancellationSignal = Context.current().cancellationSignal;
442
-
443
- const result: StreamResult = await streamExecution({
444
- agentGraph: setup.agentGraph,
445
- langgraphInput: effectiveInput as Record<string, unknown>,
446
- langgraphConfig: setup.langgraphConfig,
447
- executionId,
448
- client,
449
- initialStatus,
450
- streamingConfig,
451
- offload: statusOffload,
452
- gracefulStop: setup.gracefulStop,
453
- inlinePublisher,
454
- // Capture mode: never push speculative edits during the turn. Writeback
455
- // is deferred until the approved tree is reconciled (post-decision).
456
- writebackCoordinator: setup.captureMode ? undefined : (writebackCoordinator ?? undefined),
457
- heartbeatFn: (details) => Context.current().heartbeat(details),
458
- isCancelledFn: () => cancellationSignal.aborted,
459
- approvalProvider: {
460
- policies: setup.approvalPolicies,
461
- toolServerMap: setup.toolServerMap,
462
- leasedCategories: setup.leasedCategories,
463
- globalBypass: setup.globalBypass,
464
- unattended: setup.unattended,
465
- },
466
- streamVersion: setup.streamVersion,
467
- // Mid-run live capture (DD-32 / DD-33): attach file_change_progress
468
- // before each scheduled persist, throttled by the floor inside
469
- // captureFileChangeProgress. The substrate (git / non-git CAS / hybrid)
470
- // was chosen for this turn above; the deep-agent's only runner-owned
471
- // tree entry is the `.stigmer` symlink, created before the baseline
472
- // so it appears identically in every capture and cancels out of the
473
- // diff — the git slice therefore needs no excludePaths, matching its
474
- // turn-boundary candidate capture.
475
- beforePersist: async (status) => {
476
- if (!progressSubstrate) return;
477
- await captureFileChangeProgress({
478
- status,
479
- changeSetId,
480
- substrate: progressSubstrate,
481
- state: progressState,
482
- });
483
- },
484
- });
485
-
486
- await processPostStream({
487
- status: initialStatus,
488
- inlinePublisher,
489
- // Capture mode: suppress the post-stream writeback finalize — the edits
490
- // on disk are still speculative until reviewed. Writeback runs only on
491
- // the approved tree (processCaptureWriteback, after reconcile).
492
- writebackCoordinator: setup.captureMode ? null : writebackCoordinator,
493
- pendingPublishPromises: result.pendingPublishPromises,
494
- pendingWritebackPromises: result.pendingWritebackPromises,
495
- executionId,
496
- });
497
-
498
- // Never-persist-secret backstop (DD-26 #2): withhold content from any
499
- // built-in write row targeting a secret-like path, across the top-level
500
- // and sub-agent transcripts, before ANY persist below. On the deny-gate a
501
- // secret write is hard-blocked (a COMPLETED row) or — under
502
- // auto_approve_all, where no gate is installed — flowed; either way the
503
- // streamed row still carries `args` from handleToolStarted, and the
504
- // capture-mode stamping pass (which performs the same scrub) does not run
505
- // here. Idempotent / a no-op in capture mode. This single post-stream
506
- // sweep is the one choke point that precedes every downstream persist.
507
- withholdSecretContentFromMessages(
508
- initialStatus.messages,
509
- initialStatus.subAgentExecutions,
510
- );
511
-
512
- // Terminalize any tool call the user SKIPPED or REJECTED at the gate.
513
- // These decisions never run the tool, so the resumed stream leaves the
514
- // seeded WAITING_APPROVAL row untouched (the gate returns a ToolMessage
515
- // with no on_tool_start on http; the memory replay never re-drives the
516
- // gate). Folding the recorded decision into a terminal status here makes
517
- // reject/skip resolve identically on both checkpointers — see
518
- // reconcileNonExecutingDecisions. Runs before the WAITING-detection and
519
- // completion persists below so the terminal status is what is persisted.
520
- reconcileNonExecutingDecisions(initialStatus);
521
-
522
- // Terminalize every tool call the gate auto-skipped under UNATTENDED
523
- // approval mode (DD-014): the skip has no human decision behind it, so
524
- // reconcileNonExecutingDecisions cannot see it — this sibling folds the
525
- // gate's registry into terminal SKIPPED rows with UNATTENDED_SKIP
526
- // provenance, whatever transient status the stream left behind. No-op
527
- // for interactive executions (empty registry).
528
- reconcileUnattendedSkips(initialStatus, setup.unattendedSkips);
529
-
530
- // Turn boundary (capture mode): capture the candidate change set from the
531
- // git diff and author CANDIDATE_CAPTURED, then stamp the flowed file-edit
532
- // rows with the change set id — they stay visible in place as
533
- // observational records while file_change_sets remains the single
534
- // decision surface. Skipped on an abnormal terminal (pause / stop /
535
- // recursion limit) — there is no review to open. `fileReviewPending`
536
- // then composes with any tool gate below.
537
- let fileReviewPending = false;
538
- const abnormalTerminal =
539
- !!result.terminalStatus &&
540
- initialStatus.phase !== ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL;
541
- if (setup.captureMode && !abnormalTerminal) {
542
- // Compose the CAS-captured paths (before-bytes from the observer,
543
- // after-bytes re-read from disk) and the secret-blocked paths into the
544
- // change set. The substrate class labels their provenance:
545
- // GIT_IGNORED_CAPTURED for a git tree's ignored paths, NON_GIT_CAS for a
546
- // non-git workspace (where these ARE the whole change set). In a git turn
547
- // they compose with the git-tracked diff into one hybrid set; in a non-git
548
- // turn they are the entire (CAS-only) set.
549
- const casCaptureClass = setup.gitWorkspace
550
- ? FileCaptureClass.GIT_IGNORED_CAPTURED
551
- : FileCaptureClass.NON_GIT_CAS;
552
- const { casCaptures, unreviewablePaths } = await buildCasTurnCaptures(
553
- setup.casObserver,
554
- gitRoot,
555
- casCaptureClass,
556
- );
557
- // Approved-command turn facts (DD-28): when every mutation-capable call
558
- // this turn was a consented shell command, attach the provenance so the
559
- // backend can verify the cited consent rows and auto-keep the set
560
- // instead of arming a second review gate. Fail-closed: any non-qualifying
561
- // turn attaches nothing and reviews manually exactly as before. Attached
562
- // only when captureCandidateToLedger actually authors a CANDIDATE.
563
- const commandProvenance = deriveTurnCommandProvenance({
564
- status: initialStatus,
565
- priorSettledToolCallIds,
566
- priorSubAgentToolCallIds,
567
- globalBypass: setup.globalBypass,
568
- });
569
- if (commandProvenance) {
570
- console.log(
571
- `[ExecuteDeepAgent] capture: turn qualifies for approved-command auto-keep ` +
572
- `(consent rows: ${commandProvenance.consentToolCallIds.join(",") || "(auto_approve_all)"}); ` +
573
- `attaching provenance to candidate (execution=${executionId})`,
574
- );
575
- }
576
- await captureCandidateToLedger({
577
- status: initialStatus,
578
- gitRoot,
579
- executionId,
580
- changeSetId,
581
- baselineTree: captureBaselineTree,
582
- harnessId: DEEP_AGENT_HARNESS_ID,
583
- casCaptures,
584
- storage: setup.artifactStorage,
585
- unreviewablePaths,
586
- unreviewableCaptureClass: casCaptureClass,
587
- gitWorkspace: setup.gitWorkspace,
588
- commandProvenance,
589
- });
590
- // Review is pending iff a CANDIDATE was actually authored (the seam
591
- // drops no-op captures), so a turn that only touched-then-unchanged an
592
- // ignored file does not open an empty review. Stamping is gated on the
593
- // same signal: a row must never reference a change set that does not
594
- // exist.
595
- fileReviewPending = hasCandidateCaptured(initialStatus, changeSetId);
596
- if (fileReviewPending) {
597
- stampFlowedFileEditRows(initialStatus.messages, changeSetId);
598
- stampFlowedSubAgentFileEditRows(
599
- initialStatus.subAgentExecutions,
600
- changeSetId,
601
- priorSubAgentToolCallIds,
602
- );
603
- }
604
- }
605
-
606
- if (result.terminalStatus) {
607
- if (initialStatus.phase === ExecutionPhase.EXECUTION_PAUSED) {
608
- // The stream loop marks PAUSED for ANY platform cancellation; a
609
- // worker shutdown is not a pause and must not persist as one (#776).
610
- if (shutdownSignal?.aborted) {
611
- console.log(`[ExecuteDeepAgent] Cancelled (worker shutdown) for execution ${executionId}: events=${result.eventsProcessed}`);
612
- await persistStatus(client, executionId, buildWorkerShutdownStatus(), { offload: statusOffload }).catch(() => {});
613
- throw new CancelledFailure("Activity cancelled (worker shutdown, not user pause)");
614
- }
615
- await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
616
- console.log(`[ExecuteDeepAgent] Paused for execution ${executionId}: events=${result.eventsProcessed}`);
617
- throw new CancelledFailure("Activity paused by orchestrator");
618
- }
619
- // Capture mode: a tool gate set WAITING during the stream while file
620
- // edits also flowed this turn. The CANDIDATE event + stamped file-edit
621
- // rows post-date the stream's slim, so persist + return the current
622
- // status (both review surfaces — the file change set and the tool gate —
623
- // are now pending). `!abnormalTerminal` means the capture block ran.
624
- if (setup.captureMode && !abnormalTerminal) {
625
- await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
626
- return slimStatus(initialStatus);
627
- }
628
- return result.terminalStatus;
629
- }
630
-
631
- if (!setup.globalBypass) {
632
- const postStreamGraphState = await setup.agentGraph.getState(setup.langgraphConfig);
633
- const graphMessages = (postStreamGraphState.values as { messages?: unknown }).messages;
634
- const aiMessages = Array.isArray(graphMessages) ? graphMessages : [];
635
- const pendingInterrupts = detectPendingInterrupts(postStreamGraphState);
636
-
637
- if (pendingInterrupts.length > 0) {
638
- console.log(
639
- `[ExecuteDeepAgent] Detected ${pendingInterrupts.length} pending interrupt(s) ` +
640
- `for execution ${executionId} — setting WAITING_FOR_APPROVAL`,
641
- );
642
- initialStatus.phase = ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL;
643
-
644
- const aiMsg = create(AgentMessageSchema, {
645
- type: MessageType.MESSAGE_AI,
646
- content: "",
647
- timestamp: utcTimestamp(),
648
- isStreaming: false,
649
- });
650
- for (const intr of pendingInterrupts) {
651
- const toolCall = create(ToolCallSchema, {
652
- id: intr.toolCallId,
653
- name: intr.toolName,
654
- status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
655
- requiresApproval: true,
656
- approvalMessage: intr.message,
657
- approvalRequestedAt: utcTimestamp(),
658
- mcpServerSlug: intr.mcpServerSlug,
659
- startedAt: utcTimestamp(),
660
- toolKind: classifyTool(intr.toolName, intr.mcpServerSlug),
661
- approvalPolicySource: toProtoPolicySource(intr.policySource),
662
- policyEngineVersion: intr.policySource ? POLICY_ENGINE_VERSION : "",
663
- });
664
-
665
- // Capture a sanitized args preview AND the redacted args object
666
- // while the graph is paused, so the approval UI renders the
667
- // proposed change before the tool runs — and the row header can
668
- // extract its filename-first path from `args` like every other
669
- // row (issue #754: a placeholder without args rendered a
670
- // pathless "Write" header). Args are correlated from the
671
- // AI-message tool call in graph state (the single source of
672
- // truth).
673
- const { argsPreview, args } = captureApprovalArtifacts({
674
- toolCallId: intr.toolCallId,
675
- messages: aiMessages,
676
- });
677
- if (argsPreview) toolCall.argsPreview = argsPreview;
678
- if (args) toolCall.args = args as typeof toolCall.args;
679
-
680
- aiMsg.toolCalls.push(toolCall);
681
- }
682
- initialStatus.messages.push(aiMsg);
683
-
684
- await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
685
- return slimStatus(initialStatus);
686
- }
687
- }
688
-
689
- // Capture mode: file edits flowed but no tool gate fired — open file
690
- // review instead of completing. The agent's final answer + outputs are
691
- // still computed and persisted below, so the pure-file-review resume can
692
- // complete from the seeded status with no model re-invocation.
693
- const completeNow = !fileReviewPending;
694
- initialStatus.phase = completeNow
695
- ? ExecutionPhase.EXECUTION_COMPLETED
696
- : ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL;
697
- if (completeNow) {
698
- initialStatus.completedAt = utcTimestamp();
699
- }
700
-
701
- let structuredOutput: JsonObject | undefined;
702
- let finalText: string | undefined;
703
-
704
- const lastAiMsg = [...initialStatus.messages]
705
- .reverse()
706
- .find(m => m.type === MessageType.MESSAGE_AI);
707
- if (lastAiMsg) {
708
- finalText = lastAiMsg.content;
709
- }
710
-
711
- if (setup.hasStructuredOutput) {
712
- // Primary source: deepagents' structuredResponse surfaced via the v3
713
- // run.output. This is the reliable path when the graph populates it.
714
- const sr = result.runOutput?.structuredResponse;
715
- if (sr != null && typeof sr === "object" && !Array.isArray(sr)) {
716
- structuredOutput = sr as JsonObject;
717
- } else if (sr !== undefined) {
718
- console.warn(
719
- `[ExecuteDeepAgent] structuredResponse is not a plain object ` +
720
- `for execution ${executionId}: type=${typeof sr}`,
721
- );
722
- }
723
-
724
- // Fallback: extract JSON from the agent's final text message. The v3
725
- // streaming path does not surface deepagents' structuredResponse
726
- // (hasStructuredResponse=false), so we recover structured output by
727
- // parsing the agent's final response (JSON / code-fence / last-brace).
728
- if (structuredOutput === undefined && finalText) {
729
- const { extractJsonFromText } = await import("../../shared/extract-json.js");
730
- const extracted = extractJsonFromText(finalText);
731
- if (extracted != null && typeof extracted === "object" && !Array.isArray(extracted)) {
732
- structuredOutput = extracted as JsonObject;
733
- console.log(
734
- `[ExecuteDeepAgent] structured output extracted from final text ` +
735
- `for execution ${executionId}: finalTextLength=${finalText.length}`,
736
- );
737
- }
738
- }
739
-
740
- if (structuredOutput !== undefined) {
741
- initialStatus.structuredOutput = structuredOutput;
742
- }
743
- }
744
-
745
- // Plan mode: the agent's final message is the plan. Publish it as a
746
- // first-class plan artifact (named from the plan's title) so the UI can
747
- // render a reviewable Plan card and a follow-up Implement run can
748
- // reference it. Read-only mode produces no file to auto-publish, so
749
- // this is the only artifact path.
750
- // Requires artifact storage; with none (proxy misconfig) the plan text
751
- // still lives in the final message — only the reviewable artifact is
752
- // skipped (mirrors Cursor's storage-guarded plan publish).
753
- if (
754
- setup.execution.spec?.executionConfig?.interactionMode === InteractionMode.PLAN &&
755
- finalText &&
756
- setup.artifactStorage
757
- ) {
758
- await publishPlanArtifact({
759
- status: initialStatus,
760
- executionId,
761
- planText: finalText,
762
- artifactStorage: setup.artifactStorage,
763
- });
764
- }
765
-
766
- // Capture mode: push the approved tree exactly once, on terminal
767
- // completion — never the speculative mid-turn edits.
768
- if (setup.captureMode && completeNow && writebackCoordinator) {
769
- await processCaptureWriteback(writebackCoordinator, executionId);
770
- }
771
-
772
- await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
773
-
774
- console.log(
775
- `[ExecuteDeepAgent] ${completeNow ? "Completed" : "Awaiting file review for"} ` +
776
- `execution ${executionId}: ` +
777
- `events=${result.eventsProcessed}, ` +
778
- `messages=${initialStatus.messages.length}, ` +
779
- `artifacts=${initialStatus.artifacts.length}, ` +
780
- `writebacks=${initialStatus.workspaceWriteBacks.length}, ` +
781
- `hasStructuredOutput=${structuredOutput !== undefined}`,
782
- );
783
-
784
- // A WAITING (file-review) return carries no final outputs yet — those are
785
- // surfaced when the pure-file-review resume completes from this persisted
786
- // status. The COMPLETED return enriches the slim with final text/outputs.
787
- if (!completeNow) {
788
- return slimStatus(initialStatus);
789
- }
790
-
791
- const slim = slimStatus(initialStatus) as Record<string, unknown>;
792
- if (finalText !== undefined) {
793
- slim.final_text = finalText;
794
- }
795
- if (structuredOutput !== undefined) {
796
- slim.structured = structuredOutput;
797
- }
798
- return slim;
799
-
800
- } catch (err: unknown) {
801
- if (err instanceof CancelledFailure) {
802
- // Worker shutdown is infrastructure failure, not pause — the caught
803
- // CancelledFailure is itself the interruption evidence (#776).
804
- if (shutdownSignal?.aborted) {
805
- console.log(`[ExecuteDeepAgent] Cancelled (worker shutdown) for execution ${executionId}`);
806
- await persistStatus(client, executionId, buildWorkerShutdownStatus()).catch(() => {});
807
- throw err;
808
- }
809
- console.log(`[ExecuteDeepAgent] Cancelled (pause) for execution ${executionId}`);
810
- const pausedStatus = create(AgentExecutionStatusSchema, {
811
- phase: ExecutionPhase.EXECUTION_PAUSED,
812
- });
813
- await persistStatus(client, executionId, pausedStatus).catch(() => {});
814
- throw err;
815
- }
816
-
817
- if (Context.current().cancellationSignal.aborted) {
818
- if (shutdownSignal?.aborted) {
819
- console.log(`[ExecuteDeepAgent] Error during worker-shutdown cancellation for ${executionId}: ${err}`);
820
- await persistStatus(client, executionId, buildWorkerShutdownStatus()).catch(() => {});
821
- throw new CancelledFailure("Activity cancelled (worker shutdown, not user pause)");
822
- }
823
- console.log(`[ExecuteDeepAgent] Error during cancellation for ${executionId}, treating as pause: ${err}`);
824
- const pausedStatus = create(AgentExecutionStatusSchema, {
825
- phase: ExecutionPhase.EXECUTION_PAUSED,
826
- });
827
- await persistStatus(client, executionId, pausedStatus).catch(() => {});
828
- throw new CancelledFailure("Activity paused by orchestrator (error during cancellation)");
829
- }
830
-
831
- // Classify before formatting: model-call failures get stable codes
832
- // and platform-vs-user attribution (LangChain's MiddlewareError
833
- // wrapper is unwrapped to the root SDK error); non-model failures
834
- // keep the root error's own identity. See shared/model-error.ts.
835
- const { errorType, errorMessage } = describeExecutionError(err, {
836
- proxyMode: !!config.proxyEndpoint,
837
- modelId: setup?.modelName,
838
- provider: setup ? tryInferProvider(setup.modelName) : undefined,
839
- });
840
-
841
- console.error(
842
- `[ExecuteDeepAgent] Failed for execution ${executionId}: ` +
843
- `[${errorType}] ${errorMessage}`,
844
- );
845
-
846
- const failedStatus = create(AgentExecutionStatusSchema, {
847
- phase: ExecutionPhase.EXECUTION_FAILED,
848
- error: `Execution failed: [${errorType}] ${errorMessage}`,
849
- completedAt: utcTimestamp(),
850
- messages: [
851
- create(AgentMessageSchema, {
852
- type: MessageType.MESSAGE_SYSTEM,
853
- content: `Error: [${errorType}] ${errorMessage}`,
854
- timestamp: utcTimestamp(),
855
- }),
856
- ],
857
- });
858
-
859
- await persistStatus(client, executionId, failedStatus);
860
- return slimStatus(failedStatus);
861
-
862
- } finally {
863
- await cleanup(setup);
864
- // Release the workspace turn lock LAST — the next queued turn must
865
- // not baseline until every mutation of this one has landed.
866
- // Idempotent and non-throwing (see workspace-lock.ts).
867
- await releaseWorkspaceLock?.();
868
- activityFinished();
869
- }
870
- },
871
- };
872
- }
873
-
874
- /** A pending LangGraph approval interrupt, normalized from the graph checkpoint. */
875
- interface PendingInterrupt {
876
- readonly toolCallId: string;
877
- readonly toolName: string;
878
- readonly mcpServerSlug: string;
879
- readonly message: string;
880
- /** Gate provenance carried through the interrupt; undefined → UNSPECIFIED. */
881
- readonly policySource: PolicySource | undefined;
882
- }
883
-
884
- /**
885
- * Normalize the graph checkpoint's un-resumed interrupts into pending approvals.
886
- * Used both before streaming (to detect whether a resume is a pure file review)
887
- * and after (to seed the WAITING_FOR_APPROVAL tool rows).
888
- */
889
- function detectPendingInterrupts(graphState: GraphStateSnapshot): PendingInterrupt[] {
890
- const tasks = (graphState as {
891
- tasks?: readonly {
892
- interrupts?: readonly { value: Record<string, unknown>; resumeValue?: unknown }[];
893
- }[];
894
- }).tasks;
895
- return (
896
- tasks?.flatMap((task) =>
897
- (task.interrupts ?? [])
898
- .filter((intr) => intr.resumeValue === undefined)
899
- .map((intr) => {
900
- const val = intr.value as Record<string, unknown>;
901
- return {
902
- toolCallId: (val?.tool_call_id as string) ?? "",
903
- toolName: (val?.tool_name as string) ?? "",
904
- mcpServerSlug: (val?.mcp_server_slug as string) ?? "",
905
- message: (val?.message as string) ?? "",
906
- policySource: (val?.policy_source as PolicySource) || undefined,
907
- };
908
- }),
909
- ) ?? []
910
- );
911
- }
912
-
913
- /**
914
- * Whether the persisted transcript holds any tool call awaiting approval — the
915
- * checkpointer-independent signal that a resume must drive a tool gate (resume
916
- * the graph), not just a file review. Scans root + sub-agent messages. A tool
917
- * keeps `WAITING_APPROVAL` after the user decides (the decision rides
918
- * `approval_action`) until the runner reconciles it, so this is true for a mixed
919
- * turn's resume and false for a pure file-review turn.
920
- */
921
- function hasPendingToolApprovals(execution: AgentExecution): boolean {
922
- const status = execution.status;
923
- if (!status) return false;
924
- const anyWaiting = (msgs: { toolCalls: { status: ToolCallStatus }[] }[]): boolean =>
925
- msgs.some((m) =>
926
- m.toolCalls.some((tc) => tc.status === ToolCallStatus.TOOL_CALL_WAITING_APPROVAL),
927
- );
928
- if (anyWaiting(status.messages)) return true;
929
- return status.subAgentExecutions.some((sa) => anyWaiting(sa.messages));
930
- }
931
-
932
- /**
933
- * Assemble the turn's CAS captures and secret-blocked paths from the shared
934
- * observer (the pre-turn bytes recorded by the parent AND every sub-agent CAS
935
- * backend, plus the gate's secret-blocked set).
936
- *
937
- * For each observed CAS-owned path the after-bytes are re-read from disk (the
938
- * authoritative net result of the turn; `null` when the file is gone). A path
939
- * that is secret-like is diverted to `unreviewablePaths` and its before-bytes are
940
- * dropped — the fail-closed backstop that holds even under the global bypass,
941
- * where the gate did not run to block it up front. The result composes into the
942
- * change set in {@link captureCandidateToLedger}.
943
- *
944
- * `captureClass` is the turn's CAS substrate class — GIT_IGNORED_CAPTURED for a
945
- * git work tree's ignored paths, NON_GIT_CAS for a non-git workspace (where these
946
- * ARE the whole change set) — so each captured path carries its true provenance.
947
- */
948
- async function buildCasTurnCaptures(
949
- observer: CasCaptureObserver,
950
- workspaceRoot: string,
951
- captureClass: FileCaptureClass,
952
- ): Promise<{ casCaptures: CasPathCapture[]; unreviewablePaths: string[] }> {
953
- // The secret partition (pure, corpus-lockable) decides what may be captured;
954
- // this shell only performs the IO for the capturable set. The backstop that
955
- // withholds a secret observed under the global bypass lives in the partition.
956
- const { capturablePaths, unreviewablePaths } = partitionIgnoredPathsBySecret(
957
- observer.before.keys(),
958
- observer.blockedSecretPaths,
959
- );
960
- const casCaptures: CasPathCapture[] = [];
961
- for (const relPath of capturablePaths) {
962
- const after = await readFileOrNull(join(workspaceRoot, relPath));
963
- casCaptures.push({
964
- path: relPath,
965
- before: observer.before.get(relPath) ?? null,
966
- after,
967
- captureClass,
968
- });
969
- }
970
- return { casCaptures, unreviewablePaths: [...unreviewablePaths] };
971
- }
972
-
973
- /** Raw bytes of a file, or `null` when it does not exist (a DELETE). */
974
- async function readFileOrNull(absolutePath: string): Promise<Uint8Array | null> {
975
- try {
976
- return await readFile(absolutePath);
977
- } catch {
978
- return null;
979
- }
980
- }
981
-
982
- /**
983
- * Push the reconciled (approved) tree exactly once, on terminal completion in
984
- * capture mode. The coordinator's {@link WriteBackCoordinator.finalize} commits +
985
- * pushes whatever uncommitted changes remain — which, post-reconcile, is exactly
986
- * the approved content (rejected files were already snapped back to baseline).
987
- * Speculative mid-turn edits never reach here (writeback is suppressed during the
988
- * turn). Fire-and-forget by the coordinator's own contract (errors logged).
989
- */
990
- async function processCaptureWriteback(
991
- writebackCoordinator: WriteBackCoordinator,
992
- executionId: string,
993
- ): Promise<void> {
994
- await writebackCoordinator.finalize();
995
- console.log(`[ExecuteDeepAgent] capture writeback finalized for execution ${executionId}`);
996
- }
997
-
998
- /**
999
- * Whether to seed the initial status from the persisted execution transcript.
1000
- *
1001
- * The Temporal workflow re-invokes ExecuteDeepAgent with the same thread_id on
1002
- * every continuation — HITL approval, pause/resume, and transient recovery. The
1003
- * runner owns the transcript and the server enforces it as append-only at
1004
- * identity, so any continuation must build ON the committed history rather than
1005
- * rebuilding from an empty proto and replacing it.
1006
- *
1007
- * The decision is therefore driven solely by whether the server already holds
1008
- * committed history for this execution — NOT by the live graph checkpoint, which
1009
- * the checkpointer backends leave in different states for the same continuation:
1010
- * - durable (sqlite, the OSS local / desktop default; and http in cloud): the
1011
- * checkpoint survives, the graph resumes via Command(resume), and
1012
- * streamEvents re-emits only post-checkpoint events;
1013
- * - memory (ephemeral, opt-in — used by tests): the checkpoint is recreated
1014
- * empty every invocation, so the graph REPLAYS from scratch and the blind
1015
- * FIFO turns advance one gate at a time.
1016
- * Keying on the live checkpoint skipped seeding on that memory-replay path, so a
1017
- * second sequential gate emitted a transcript holding only the new gate, dropped
1018
- * the first gate's committed tool-call id, and tripped the server's append-only
1019
- * guard — the silent sequential-gate skip. Seeding whenever persisted history
1020
- * exists covers both paths; a re-emitted prior turn reconciles in place by
1021
- * tool-call id (StatusBuilder.rebuildToolCallIndex), so seeding never
1022
- * duplicates. False on a first run (no persisted messages), preserving the
1023
- * original start-from-empty behavior.
1024
- */
1025
- function shouldSeedFromPersistedTranscript(execution: AgentExecution): boolean {
1026
- return (execution.status?.messages.length ?? 0) > 0;
1027
- }
1028
-
1029
- /**
1030
- * Seed a fresh status from the persisted execution transcript so streamed
1031
- * deltas append onto existing history rather than replacing it. The clone keeps
1032
- * the persisted proto immutable. Terminal fields are cleared because the resumed
1033
- * run is in progress again; the StatusBuilder constructor re-sets the phase.
1034
- */
1035
- function seedStatusFromExecution(execution: AgentExecution): AgentExecutionStatus {
1036
- const seeded = clone(AgentExecutionStatusSchema, execution.status!);
1037
- seeded.completedAt = "";
1038
- seeded.error = "";
1039
- return seeded;
1040
- }
1041
-
1042
- async function cleanup(setup: SetupResult | null): Promise<void> {
1043
- if (!setup) return;
1044
-
1045
- if (setup.mcpConnection) {
1046
- try {
1047
- await setup.mcpConnection.client.close();
1048
- } catch (err) {
1049
- console.warn("[ExecuteDeepAgent] MCP connection cleanup failed:", err);
1050
- }
1051
- }
1052
-
1053
- // Close the checkpointer's backing resources. Only the durable sqlite saver
1054
- // holds an OS handle (an open DB file); memory/http savers have no close(),
1055
- // so this is duck-typed and best-effort.
1056
- const closable = setup.checkpointer as { close?: () => void } | undefined;
1057
- if (closable && typeof closable.close === "function") {
1058
- try {
1059
- closable.close();
1060
- } catch (err) {
1061
- console.warn("[ExecuteDeepAgent] Checkpointer cleanup failed:", err);
1062
- }
1063
- }
1064
-
1065
- // Drop the workspace→platform `.stigmer` symlink so an attached repo is
1066
- // left untouched between turns (issue #173 semantics; a multi-turn session
1067
- // recreates it next turn). Runs in the activity finally, BEFORE the
1068
- // workspace lock releases, so the next queued turn baselines a clean tree.
1069
- // Best-effort and symlink-only: a real `.stigmer` directory is never removed.
1070
- await removeStigmerSymlink(setup.workspaceBackend.rootDir);
1071
- }
1072
-