@stigmer/runner 3.0.9-dev.20260615153829 → 3.1.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 (329) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/__test-utils__/approval-contract/types.d.ts +174 -0
  3. package/dist/__test-utils__/approval-contract/types.js +24 -0
  4. package/dist/__test-utils__/approval-contract/types.js.map +1 -0
  5. package/dist/activities/call-agent-status.d.ts +19 -1
  6. package/dist/activities/call-agent-status.js +64 -5
  7. package/dist/activities/call-agent-status.js.map +1 -1
  8. package/dist/activities/call-llm.js +19 -53
  9. package/dist/activities/call-llm.js.map +1 -1
  10. package/dist/activities/classify-tool-approvals.d.ts +24 -0
  11. package/dist/activities/classify-tool-approvals.js +69 -17
  12. package/dist/activities/classify-tool-approvals.js.map +1 -1
  13. package/dist/activities/discover-mcp-server.d.ts +7 -0
  14. package/dist/activities/discover-mcp-server.js +11 -1
  15. package/dist/activities/discover-mcp-server.js.map +1 -1
  16. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +140 -0
  17. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +130 -0
  18. package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -0
  19. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.d.ts +18 -0
  20. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +123 -0
  21. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -0
  22. package/dist/activities/execute-cursor/approval-policy.d.ts +17 -61
  23. package/dist/activities/execute-cursor/approval-policy.js +8 -120
  24. package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
  25. package/dist/activities/execute-cursor/approval-state.d.ts +219 -9
  26. package/dist/activities/execute-cursor/approval-state.js +233 -17
  27. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  28. package/dist/activities/execute-cursor/capture-flow.d.ts +127 -0
  29. package/dist/activities/execute-cursor/capture-flow.js +234 -0
  30. package/dist/activities/execute-cursor/capture-flow.js.map +1 -0
  31. package/dist/activities/execute-cursor/cas-observations.d.ts +96 -0
  32. package/dist/activities/execute-cursor/cas-observations.js +184 -0
  33. package/dist/activities/execute-cursor/cas-observations.js.map +1 -0
  34. package/dist/activities/execute-cursor/command-provenance.d.ts +62 -0
  35. package/dist/activities/execute-cursor/command-provenance.js +128 -0
  36. package/dist/activities/execute-cursor/command-provenance.js.map +1 -0
  37. package/dist/activities/execute-cursor/exact-apply.d.ts +110 -0
  38. package/dist/activities/execute-cursor/exact-apply.js +204 -0
  39. package/dist/activities/execute-cursor/exact-apply.js.map +1 -0
  40. package/dist/activities/execute-cursor/hook-script.d.ts +53 -24
  41. package/dist/activities/execute-cursor/hook-script.js +310 -47
  42. package/dist/activities/execute-cursor/hook-script.js.map +1 -1
  43. package/dist/activities/execute-cursor/index.d.ts +8 -1
  44. package/dist/activities/execute-cursor/index.js +500 -64
  45. package/dist/activities/execute-cursor/index.js.map +1 -1
  46. package/dist/activities/execute-cursor/message-translator.d.ts +258 -11
  47. package/dist/activities/execute-cursor/message-translator.js +836 -90
  48. package/dist/activities/execute-cursor/message-translator.js.map +1 -1
  49. package/dist/activities/execute-cursor/prompt-builder.d.ts +20 -23
  50. package/dist/activities/execute-cursor/prompt-builder.js +74 -15
  51. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  52. package/dist/activities/execute-cursor/workspace-setup.d.ts +17 -0
  53. package/dist/activities/execute-cursor/workspace-setup.js +212 -33
  54. package/dist/activities/execute-cursor/workspace-setup.js.map +1 -1
  55. package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.d.ts +19 -0
  56. package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js +143 -0
  57. package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js.map +1 -0
  58. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +88 -0
  59. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +81 -0
  60. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -0
  61. package/dist/activities/execute-deep-agent/approval-file-change.d.ts +47 -0
  62. package/dist/activities/execute-deep-agent/approval-file-change.js +68 -0
  63. package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -0
  64. package/dist/activities/execute-deep-agent/attachment-injector.d.ts +8 -1
  65. package/dist/activities/execute-deep-agent/attachment-injector.js +7 -7
  66. package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
  67. package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +42 -0
  68. package/dist/activities/execute-deep-agent/cas-capture-backend.js +47 -0
  69. package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -0
  70. package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +79 -0
  71. package/dist/activities/execute-deep-agent/cas-capture-observer.js +112 -0
  72. package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -0
  73. package/dist/activities/execute-deep-agent/hitl.d.ts +16 -3
  74. package/dist/activities/execute-deep-agent/hitl.js +11 -3
  75. package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
  76. package/dist/activities/execute-deep-agent/index.d.ts +2 -1
  77. package/dist/activities/execute-deep-agent/index.js +393 -33
  78. package/dist/activities/execute-deep-agent/index.js.map +1 -1
  79. package/dist/activities/execute-deep-agent/inline-publisher.d.ts +7 -1
  80. package/dist/activities/execute-deep-agent/inline-publisher.js +23 -2
  81. package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
  82. package/dist/activities/execute-deep-agent/setup.d.ts +53 -2
  83. package/dist/activities/execute-deep-agent/setup.js +149 -92
  84. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  85. package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +36 -0
  86. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +56 -0
  87. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +1 -0
  88. package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +34 -1
  89. package/dist/activities/execute-deep-agent/status-builder-shared.js +26 -25
  90. package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
  91. package/dist/activities/execute-deep-agent/status-builder.d.ts +17 -5
  92. package/dist/activities/execute-deep-agent/status-builder.js +44 -3
  93. package/dist/activities/execute-deep-agent/status-builder.js.map +1 -1
  94. package/dist/activities/execute-deep-agent/streaming-side-effects.js +2 -19
  95. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  96. package/dist/activities/execute-deep-agent/streaming.js +3 -15
  97. package/dist/activities/execute-deep-agent/streaming.js.map +1 -1
  98. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -7
  99. package/dist/activities/execute-deep-agent/subagent-transformer.js +23 -7
  100. package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
  101. package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +30 -3
  102. package/dist/activities/execute-deep-agent/subagent-wiring.js +29 -3
  103. package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
  104. package/dist/activities/execute-deep-agent/v3-status-builder.js +32 -2
  105. package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
  106. package/dist/claimcheck/payload-codec.js +9 -5
  107. package/dist/claimcheck/payload-codec.js.map +1 -1
  108. package/dist/client/stigmer-client.d.ts +2 -0
  109. package/dist/client/stigmer-client.js +2 -0
  110. package/dist/client/stigmer-client.js.map +1 -1
  111. package/dist/middleware/approval-gate.d.ts +85 -4
  112. package/dist/middleware/approval-gate.js +165 -38
  113. package/dist/middleware/approval-gate.js.map +1 -1
  114. package/dist/middleware/types.d.ts +2 -5
  115. package/dist/shared/activity-input.d.ts +43 -0
  116. package/dist/shared/activity-input.js +17 -0
  117. package/dist/shared/activity-input.js.map +1 -0
  118. package/dist/shared/approval-canonicalize.d.ts +19 -0
  119. package/dist/shared/approval-canonicalize.js +119 -0
  120. package/dist/shared/approval-canonicalize.js.map +1 -0
  121. package/dist/shared/approval-fingerprint.d.ts +106 -0
  122. package/dist/shared/approval-fingerprint.js +113 -0
  123. package/dist/shared/approval-fingerprint.js.map +1 -0
  124. package/dist/shared/approval-policy.d.ts +182 -12
  125. package/dist/shared/approval-policy.js +213 -27
  126. package/dist/shared/approval-policy.js.map +1 -1
  127. package/dist/shared/args-preview.d.ts +52 -0
  128. package/dist/shared/args-preview.js +93 -0
  129. package/dist/shared/args-preview.js.map +1 -0
  130. package/dist/shared/artifact-storage.d.ts +19 -1
  131. package/dist/shared/artifact-storage.js +48 -11
  132. package/dist/shared/artifact-storage.js.map +1 -1
  133. package/dist/shared/file-change.d.ts +44 -0
  134. package/dist/shared/file-change.js +57 -0
  135. package/dist/shared/file-change.js.map +1 -0
  136. package/dist/shared/file-tools.d.ts +107 -0
  137. package/dist/shared/file-tools.js +168 -0
  138. package/dist/shared/file-tools.js.map +1 -0
  139. package/dist/shared/filereview/capture.d.ts +202 -0
  140. package/dist/shared/filereview/capture.js +498 -0
  141. package/dist/shared/filereview/capture.js.map +1 -0
  142. package/dist/shared/filereview/cas-substrate.d.ts +190 -0
  143. package/dist/shared/filereview/cas-substrate.js +284 -0
  144. package/dist/shared/filereview/cas-substrate.js.map +1 -0
  145. package/dist/shared/filereview/digest.d.ts +40 -0
  146. package/dist/shared/filereview/digest.js +66 -0
  147. package/dist/shared/filereview/digest.js.map +1 -0
  148. package/dist/shared/filereview/events.d.ts +170 -0
  149. package/dist/shared/filereview/events.js +298 -0
  150. package/dist/shared/filereview/events.js.map +1 -0
  151. package/dist/shared/filereview/git-substrate.d.ts +175 -0
  152. package/dist/shared/filereview/git-substrate.js +439 -0
  153. package/dist/shared/filereview/git-substrate.js.map +1 -0
  154. package/dist/shared/filereview/index.d.ts +11 -0
  155. package/dist/shared/filereview/index.js +12 -0
  156. package/dist/shared/filereview/index.js.map +1 -0
  157. package/dist/shared/filereview/secret-paths.d.ts +63 -0
  158. package/dist/shared/filereview/secret-paths.js +105 -0
  159. package/dist/shared/filereview/secret-paths.js.map +1 -0
  160. package/dist/shared/fingerprint-secret.d.ts +26 -0
  161. package/dist/shared/fingerprint-secret.js +47 -0
  162. package/dist/shared/fingerprint-secret.js.map +1 -0
  163. package/dist/shared/model-client.d.ts +51 -0
  164. package/dist/shared/model-client.js +77 -0
  165. package/dist/shared/model-client.js.map +1 -0
  166. package/dist/shared/plan-artifact.js +0 -2
  167. package/dist/shared/plan-artifact.js.map +1 -1
  168. package/dist/shared/status-offload.d.ts +83 -9
  169. package/dist/shared/status-offload.js +399 -79
  170. package/dist/shared/status-offload.js.map +1 -1
  171. package/dist/shared/status.js +14 -1
  172. package/dist/shared/status.js.map +1 -1
  173. package/dist/shared/tool-kind.d.ts +19 -0
  174. package/dist/shared/tool-kind.js +13 -0
  175. package/dist/shared/tool-kind.js.map +1 -1
  176. package/dist/shared/tool-row.d.ts +88 -0
  177. package/dist/shared/tool-row.js +127 -0
  178. package/dist/shared/tool-row.js.map +1 -0
  179. package/dist/shared/workspace/platform-dir.d.ts +25 -0
  180. package/dist/shared/workspace/platform-dir.js +38 -2
  181. package/dist/shared/workspace/platform-dir.js.map +1 -1
  182. package/dist/workflows/call-agent-orchestrator.js +56 -7
  183. package/dist/workflows/call-agent-orchestrator.js.map +1 -1
  184. package/dist/workflows/connect-mcp-server.d.ts +50 -0
  185. package/dist/workflows/connect-mcp-server.js +136 -15
  186. package/dist/workflows/connect-mcp-server.js.map +1 -1
  187. package/dist/workflows/types.d.ts +8 -0
  188. package/package.json +2 -2
  189. package/src/__test-utils__/approval-contract/contract.ts +224 -0
  190. package/src/__test-utils__/approval-contract/types.ts +179 -0
  191. package/src/__test-utils__/fake-artifact-storage.ts +72 -0
  192. package/src/__tests__/approval-gateway-contract.test.ts +29 -0
  193. package/src/__tests__/claimcheck-codec.test.ts +16 -53
  194. package/src/__tests__/golden-e2e.test.ts +2 -0
  195. package/src/__tests__/runner-token-coordinator.test.ts +3 -3
  196. package/src/activities/__tests__/call-agent-status.test.ts +135 -0
  197. package/src/activities/__tests__/call-llm.test.ts +1 -1
  198. package/src/activities/__tests__/classify-tool-approvals.test.ts +208 -1
  199. package/src/activities/__tests__/discover-mcp-server.test.ts +30 -0
  200. package/src/activities/__tests__/workflow-event-activities.test.ts +2 -1
  201. package/src/activities/call-agent-status.ts +74 -4
  202. package/src/activities/call-llm.ts +18 -63
  203. package/src/activities/classify-tool-approvals.ts +101 -19
  204. package/src/activities/discover-mcp-server.ts +29 -1
  205. package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +216 -0
  206. package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +148 -0
  207. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +41 -9
  208. package/src/activities/execute-cursor/__tests__/approval-state.test.ts +292 -0
  209. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +68 -1
  210. package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +1005 -0
  211. package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +187 -0
  212. package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +97 -0
  213. package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +240 -0
  214. package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +203 -0
  215. package/src/activities/execute-cursor/__tests__/exact-apply.test.ts +375 -0
  216. package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +1294 -24
  217. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +446 -0
  218. package/src/activities/execute-cursor/__tests__/hook-script.test.ts +384 -110
  219. package/src/activities/execute-cursor/__tests__/message-translator.test.ts +171 -25
  220. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +189 -0
  221. package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +44 -23
  222. package/src/activities/execute-cursor/__tests__/workspace-setup.test.ts +190 -10
  223. package/src/activities/execute-cursor/approval-policy.ts +28 -159
  224. package/src/activities/execute-cursor/approval-state.ts +366 -18
  225. package/src/activities/execute-cursor/capture-flow.ts +323 -0
  226. package/src/activities/execute-cursor/cas-observations.ts +204 -0
  227. package/src/activities/execute-cursor/command-provenance.ts +168 -0
  228. package/src/activities/execute-cursor/exact-apply.ts +253 -0
  229. package/src/activities/execute-cursor/hook-script.ts +317 -51
  230. package/src/activities/execute-cursor/index.ts +575 -67
  231. package/src/activities/execute-cursor/message-translator.ts +963 -89
  232. package/src/activities/execute-cursor/prompt-builder.ts +80 -14
  233. package/src/activities/execute-cursor/workspace-setup.ts +257 -42
  234. package/src/activities/execute-deep-agent/__test-utils__/gateway-substrate.ts +180 -0
  235. package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +134 -0
  236. package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +84 -0
  237. package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +11 -24
  238. package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +64 -0
  239. package/src/activities/execute-deep-agent/__tests__/cas-capture-observer.test.ts +163 -0
  240. package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +8 -5
  241. package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +342 -0
  242. package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +284 -0
  243. package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +14 -36
  244. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +31 -13
  245. package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +349 -0
  246. package/src/activities/execute-deep-agent/__tests__/stamp-flowed-rows.test.ts +119 -0
  247. package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +91 -10
  248. package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +9 -9
  249. package/src/activities/execute-deep-agent/__tests__/subagent-approval-propagation.test.ts +160 -0
  250. package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +213 -0
  251. package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +3 -6
  252. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +84 -1
  253. package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +4 -1
  254. package/src/activities/execute-deep-agent/approval-file-change.ts +80 -0
  255. package/src/activities/execute-deep-agent/attachment-injector.ts +20 -11
  256. package/src/activities/execute-deep-agent/cas-capture-backend.ts +66 -0
  257. package/src/activities/execute-deep-agent/cas-capture-observer.ts +125 -0
  258. package/src/activities/execute-deep-agent/hitl.ts +23 -7
  259. package/src/activities/execute-deep-agent/index.ts +463 -40
  260. package/src/activities/execute-deep-agent/inline-publisher.ts +27 -4
  261. package/src/activities/execute-deep-agent/setup.ts +223 -125
  262. package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +64 -0
  263. package/src/activities/execute-deep-agent/status-builder-shared.ts +62 -23
  264. package/src/activities/execute-deep-agent/status-builder.ts +63 -8
  265. package/src/activities/execute-deep-agent/streaming-side-effects.ts +2 -16
  266. package/src/activities/execute-deep-agent/streaming.ts +3 -13
  267. package/src/activities/execute-deep-agent/subagent-transformer.ts +53 -13
  268. package/src/activities/execute-deep-agent/subagent-wiring.ts +50 -3
  269. package/src/activities/execute-deep-agent/v3-status-builder.ts +36 -2
  270. package/src/claimcheck/payload-codec.ts +8 -8
  271. package/src/client/stigmer-client.ts +9 -1
  272. package/src/middleware/__tests__/approval-gate.test.ts +488 -4
  273. package/src/middleware/approval-gate.ts +247 -38
  274. package/src/middleware/types.ts +5 -5
  275. package/src/shared/__tests__/activity-input.test.ts +78 -0
  276. package/src/shared/__tests__/approval-canonicalize.test.ts +106 -0
  277. package/src/shared/__tests__/approval-fingerprint.test.ts +115 -0
  278. package/src/shared/__tests__/approval-policy.test.ts +274 -40
  279. package/src/shared/__tests__/args-preview.test.ts +78 -0
  280. package/src/shared/__tests__/artifact-storage-extended.test.ts +62 -10
  281. package/src/shared/__tests__/artifact-storage.test.ts +123 -11
  282. package/src/shared/__tests__/file-change.test.ts +85 -0
  283. package/src/shared/__tests__/file-tools.test.ts +90 -0
  284. package/src/shared/__tests__/fingerprint-secret.test.ts +51 -0
  285. package/src/shared/__tests__/lease-scope-corpus.test.ts +56 -0
  286. package/src/shared/__tests__/model-client.test.ts +162 -0
  287. package/src/shared/__tests__/plan-artifact.test.ts +11 -26
  288. package/src/shared/__tests__/policy-source-corpus.test.ts +58 -0
  289. package/src/shared/__tests__/status-offload.test.ts +573 -16
  290. package/src/shared/__tests__/status.test.ts +4 -5
  291. package/src/shared/__tests__/tool-kind.test.ts +24 -1
  292. package/src/shared/__tests__/tool-row.test.ts +221 -0
  293. package/src/shared/activity-input.ts +57 -0
  294. package/src/shared/approval-canonicalize.ts +159 -0
  295. package/src/shared/approval-fingerprint.ts +148 -0
  296. package/src/shared/approval-policy.ts +303 -27
  297. package/src/shared/args-preview.ts +98 -0
  298. package/src/shared/artifact-storage.ts +62 -11
  299. package/src/shared/checkpointer/__tests__/http-saver.test.ts +1 -2
  300. package/src/shared/file-change.ts +64 -0
  301. package/src/shared/file-tools.ts +169 -0
  302. package/src/shared/filereview/__tests__/capture.test.ts +856 -0
  303. package/src/shared/filereview/__tests__/cas-substrate.test.ts +404 -0
  304. package/src/shared/filereview/__tests__/digest.test.ts +100 -0
  305. package/src/shared/filereview/__tests__/events.test.ts +245 -0
  306. package/src/shared/filereview/__tests__/git-substrate.test.ts +362 -0
  307. package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +286 -0
  308. package/src/shared/filereview/__tests__/secret-paths.test.ts +121 -0
  309. package/src/shared/filereview/capture.ts +727 -0
  310. package/src/shared/filereview/cas-substrate.ts +401 -0
  311. package/src/shared/filereview/digest.ts +83 -0
  312. package/src/shared/filereview/events.ts +449 -0
  313. package/src/shared/filereview/git-substrate.ts +555 -0
  314. package/src/shared/filereview/index.ts +60 -0
  315. package/src/shared/filereview/secret-paths.ts +121 -0
  316. package/src/shared/fingerprint-secret.ts +53 -0
  317. package/src/shared/model-client.ts +122 -0
  318. package/src/shared/plan-artifact.ts +0 -2
  319. package/src/shared/status-offload.ts +433 -77
  320. package/src/shared/status.ts +13 -0
  321. package/src/shared/tool-kind.ts +33 -0
  322. package/src/shared/tool-row.ts +135 -0
  323. package/src/shared/workspace/platform-dir.ts +41 -2
  324. package/src/workflow-engine/__tests__/golden-execution.test.ts +35 -18
  325. package/src/workflow-engine/__tests__/tasks/try.test.ts +1 -1
  326. package/src/workflows/__tests__/connect-mcp-server.test.ts +304 -29
  327. package/src/workflows/call-agent-orchestrator.ts +53 -6
  328. package/src/workflows/connect-mcp-server.ts +179 -24
  329. package/src/workflows/types.ts +8 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Approved-command turn provenance (DD-28): the runner-owned qualification for
3
+ * the auto-keep policy.
4
+ *
5
+ * A turn whose ONLY mutation source was shell commands the human had already
6
+ * authorized should not re-gate its file effects at the turn-boundary review —
7
+ * the user consented to the command, and the command's file effects are the
8
+ * consented outcome. The server cannot derive this itself (tool calls carry no
9
+ * turn marker), so the runner — the only component that owns turn scoping —
10
+ * derives the facts here and attaches them to the CANDIDATE_CAPTURED event as
11
+ * {@link TurnCommandProvenance}.
12
+ *
13
+ * TRUST BOUNDARY. This module asserts turn FACTS (which tools ran this turn),
14
+ * the same trust level as the captured bytes themselves. It never asserts
15
+ * CONSENT: `consentToolCallIds` merely POINT at transcript rows whose
16
+ * `approval_action` was authored by the server's SubmitApproval (and is
17
+ * preserved against runner writes) — the backend re-verifies every claimed row
18
+ * against that server-owned record before authoring the policy decision, so a
19
+ * runner cannot mint authorization it was never given.
20
+ *
21
+ * FAIL-CLOSED. Every uncertainty disqualifies (returns undefined → the set
22
+ * reviews manually, exactly as before DD-28): a file-tool call, an MCP tool, a
23
+ * sub-agent delegation, an unrecognized tool name, or an executed shell command
24
+ * with no provable consent source. Being conservative here costs only an extra
25
+ * review; being permissive would silently waive one.
26
+ */
27
+
28
+ import { create } from "@bufbuild/protobuf";
29
+ import type { AgentMessage } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
30
+ import { TurnCommandProvenanceSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
31
+ import type { TurnCommandProvenance } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
32
+ import { ApprovalAction, ToolKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
33
+ import { classifyTool, toolApprovalCategory } from "../../shared/tool-kind.js";
34
+ import { isToolCallRowHidden } from "../../shared/tool-row.js";
35
+ import { toolCallIdentityToken } from "./message-translator.js";
36
+
37
+ /**
38
+ * Tool kinds that can never mutate the workspace: their presence in a turn is
39
+ * irrelevant to change-set provenance. Everything OUTSIDE this set is either a
40
+ * consent-mapped shell command or a disqualifier — never silently ignored.
41
+ */
42
+ const NON_MUTATING_KINDS: ReadonlySet<ToolKind> = new Set([
43
+ ToolKind.FILE_READ,
44
+ ToolKind.SEARCH,
45
+ ToolKind.LIST,
46
+ ToolKind.FETCH,
47
+ ToolKind.WEB_SEARCH,
48
+ ToolKind.THINK,
49
+ ToolKind.TODO,
50
+ ]);
51
+
52
+ export interface CommandProvenanceInputs {
53
+ /**
54
+ * The full transcript at capture time. Turn scoping is positional: messages
55
+ * from index {@link turnStartIndex} onward were produced by THIS turn's
56
+ * stream; everything before was seeded from prior turns. Lease consent rows
57
+ * (APPROVE_ALL) are looked up across the WHOLE transcript — a lease is
58
+ * run-lifetime, its granting row usually lives in a prior turn.
59
+ */
60
+ readonly messages: readonly AgentMessage[];
61
+ /** Index of the first message created by this turn's stream. */
62
+ readonly turnStartIndex: number;
63
+ /**
64
+ * Identity tokens the hook denied THIS turn (the denial ledger). A denied
65
+ * shell row never executed — it is this turn's pending gate, not a mutation
66
+ * source — so it neither needs consent nor disqualifies.
67
+ */
68
+ readonly deniedTokens: ReadonlySet<string>;
69
+ /**
70
+ * Grant token → the tool-call id of the approval row that minted the grant
71
+ * (the row carrying the server-authored approval_action). An executed shell
72
+ * command whose identity token is a key here was authorized by that
73
+ * per-command approval.
74
+ */
75
+ readonly grantTokenToConsentId: ReadonlyMap<string, string>;
76
+ /** True when the pre-armed spec.auto_approve_all bypassed the gate. */
77
+ readonly globalBypass: boolean;
78
+ }
79
+
80
+ /**
81
+ * Derive the {@link TurnCommandProvenance} for the turn, or undefined when the
82
+ * turn does not qualify (any non-shell mutation-capable call, any unknown tool,
83
+ * any executed command without a provable consent source, or no executed
84
+ * consented command at all).
85
+ */
86
+ export function deriveTurnCommandProvenance(
87
+ inputs: CommandProvenanceInputs,
88
+ ): TurnCommandProvenance | undefined {
89
+ const { messages, turnStartIndex, deniedTokens, grantTokenToConsentId, globalBypass } = inputs;
90
+
91
+ const consentIds = new Set<string>();
92
+ let authorizedByAutoApproveAll = false;
93
+ let executedCommandCount = 0;
94
+
95
+ for (let i = turnStartIndex; i < messages.length; i++) {
96
+ for (const tc of messages[i].toolCalls) {
97
+ // A hidden row is a collapsed twin/reaction — it never executed.
98
+ if (isToolCallRowHidden(tc)) continue;
99
+
100
+ const kind = classifyTool(tc.name, tc.mcpServerSlug);
101
+ if (NON_MUTATING_KINDS.has(kind)) continue;
102
+
103
+ if (kind !== ToolKind.SHELL) {
104
+ // A file tool (mixed turn), a sub-agent (unattributable mutations), an
105
+ // MCP tool (opaque side effects), or an unrecognized name — the turn's
106
+ // mutations cannot be attributed to consented commands. Fail closed.
107
+ return undefined;
108
+ }
109
+
110
+ const token = toolCallIdentityToken(tc);
111
+ if (deniedTokens.has(token)) {
112
+ // This turn's pending gate — denied by the hook, never executed.
113
+ continue;
114
+ }
115
+ executedCommandCount++;
116
+
117
+ const consentId = grantTokenToConsentId.get(token);
118
+ if (consentId) {
119
+ consentIds.add(consentId);
120
+ continue;
121
+ }
122
+ const leaseConsentId = findLeaseConsentId(messages, tc.name);
123
+ if (leaseConsentId) {
124
+ consentIds.add(leaseConsentId);
125
+ continue;
126
+ }
127
+ if (globalBypass) {
128
+ authorizedByAutoApproveAll = true;
129
+ continue;
130
+ }
131
+ // An executed shell command with no grant, no lease, and no bypass should
132
+ // be impossible (the hook gates every un-consented shell); if it ever
133
+ // happens, the honest answer is a manual review, not a waived one.
134
+ return undefined;
135
+ }
136
+ }
137
+
138
+ // A turn that executed no consented command has nothing to attribute the
139
+ // change set to — whatever changed came from somewhere else. Manual review.
140
+ if (executedCommandCount === 0) return undefined;
141
+
142
+ return create(TurnCommandProvenanceSchema, {
143
+ consentToolCallIds: [...consentIds],
144
+ authorizedByAutoApproveAll,
145
+ });
146
+ }
147
+
148
+ /**
149
+ * Find the transcript row whose APPROVE_ALL authored the run-lifetime lease
150
+ * covering `toolName`'s category — the consent row a lease-executed command
151
+ * cites. Searched across the whole transcript (a lease usually originates in a
152
+ * prior turn); the first (earliest) APPROVE_ALL of the category is the lease's
153
+ * origin. Returns undefined when no lease row exists for the category.
154
+ */
155
+ function findLeaseConsentId(
156
+ messages: readonly AgentMessage[],
157
+ toolName: string,
158
+ ): string | undefined {
159
+ const category = toolApprovalCategory(toolName);
160
+ if (!category) return undefined;
161
+ for (const msg of messages) {
162
+ for (const tc of msg.toolCalls) {
163
+ if (tc.approvalAction !== ApprovalAction.APPROVE_ALL) continue;
164
+ if (toolApprovalCategory(tc.name) === category) return tc.id;
165
+ }
166
+ }
167
+ return undefined;
168
+ }
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Resume-time exact-apply for approved whole-file writes (Cursor deny-only HITL).
3
+ *
4
+ * THE GUARANTEE THIS RESTORES
5
+ * ---------------------------
6
+ * A user must get "what you approve is what gets applied" — the contract the
7
+ * native LangGraph harness and Cursor's own IDE honor by pausing at the exact
8
+ * tool call and applying its exact args on approval. The Cursor deny-only
9
+ * harness cannot pause: it denies the tool, grants the RESOURCE (`write\n<path>`),
10
+ * and reinvokes the model, which REGENERATES content from scratch. So the bytes
11
+ * that land can differ from the bytes the gate showed (observed in production: a
12
+ * gate previewing one edit, an applied file carrying more).
13
+ *
14
+ * The fix turns an approved WHOLE-FILE write into a runner-executed action: the
15
+ * runner writes the EXACT approved bytes itself, marks the tool COMPLETED, and —
16
+ * crucially — issues NO resource grant for it. Any FURTHER write the model makes
17
+ * to that file on reinvocation is therefore re-gated, so the user sees and
18
+ * approves every change. This module owns the apply AND the grant-exclusion
19
+ * derivation ({@link excludeAppliedFromGrants}, the "issues NO grant" half); the
20
+ * caller wires the resulting grants and the prompt split (so the model is told
21
+ * the write is already applied and does not redo it).
22
+ *
23
+ * SCOPE — whole-file writes only. A hunk edit (`old_string`/`new_string`) is left
24
+ * on the grant + reinvocation path: applying a hunk would require locating the
25
+ * fragment in the file, which would make the runner a second source of truth for
26
+ * the edit result. Shell/MCP grants are already command-/name-specific, so their
27
+ * approved==applied property is already tight.
28
+ *
29
+ * SOURCE OF TRUTH — the approved whole-file bytes and target path are read from
30
+ * the gated tool call's `args` (the authoritative proposed content the deny-gate
31
+ * stamped from the hook input; see execute-cursor/message-translator.ts
32
+ * `applyGateInput`). This is the single copy — there is no separate captured
33
+ * `file_changes` mirror — so "what was shown == what is applied" holds by
34
+ * construction. (Phase 5 Slice 4 removed the redundant `ToolCall.file_changes`
35
+ * copy; `args` was always its source.)
36
+ *
37
+ * SAFETY — exact-apply writes ONLY a fully-resolved body. It never writes a
38
+ * truncated preview or the elision marker (which would silently corrupt the
39
+ * file); an unresolvable body or any write failure degrades to the existing
40
+ * grant + reinvocation path. Corruption is never an option — degradation to the
41
+ * prior behavior is.
42
+ *
43
+ * TESTING — the apply + grant-exclusion + re-gating composition is proven
44
+ * end-to-end (real workspace backend + the real deny-oracle hook) in
45
+ * `__tests__/deny-gate-exact-apply.test.ts`. That deterministic runner test is
46
+ * the achievable substitute for a pure-Go offline e2e, which is structurally
47
+ * infeasible (there is no offline Cursor agent driver — see DD-23).
48
+ */
49
+
50
+ import { isAbsolute, relative, resolve } from "node:path";
51
+ import {
52
+ ApprovalAction,
53
+ ToolCallStatus,
54
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
55
+ import type {
56
+ AgentMessage,
57
+ ToolCall,
58
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
59
+ import type { PendingApproval } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
60
+ import { ELISION_MARKER } from "../../shared/status-offload.js";
61
+ import { extractFilePath, extractWriteContent } from "../../shared/file-tools.js";
62
+ import { resolveWorkspacePath } from "../../shared/file-change.js";
63
+ import type { WorkspaceBackend } from "../../shared/workspace/types.js";
64
+ import { utcTimestamp } from "../../shared/status.js";
65
+
66
+ /** Options for {@link applyApprovedWholeFileWrites}. */
67
+ export interface ExactApplyOptions {
68
+ /** The (seeded) transcript whose approved tool calls are applied in place. */
69
+ readonly messages: AgentMessage[];
70
+ /** Workspace the files live in (local FS for OSS, sandbox in cloud). */
71
+ readonly workspaceBackend: WorkspaceBackend;
72
+ /** Configured workspace roots; a write is refused outside all of them. */
73
+ readonly workspaceDirs: readonly string[];
74
+ /** For structured logs. */
75
+ readonly executionId: string;
76
+ }
77
+
78
+ /**
79
+ * Apply every APPROVED whole-file write in `messages` to disk with its EXACT
80
+ * approved bytes, marking each applied tool call COMPLETED in place.
81
+ *
82
+ * Returns the set of tool-call ids that were exact-applied, so the caller can
83
+ * (a) exclude their resources from the approval grants — the key to re-gating a
84
+ * later write — and (b) describe them as "already applied" in the reinvocation
85
+ * prompt rather than asking the model to redo them.
86
+ *
87
+ * Idempotent: a call already COMPLETED (a Temporal activity retry re-runs this
88
+ * phase) is skipped, never written twice. Every uncertain case (no resolvable
89
+ * body, out-of-workspace target, write failure) is SKIPPED, leaving the call
90
+ * WAITING_APPROVAL so the existing grant + reinvocation path handles it — the
91
+ * conservative fallback that can never corrupt a file.
92
+ */
93
+ export async function applyApprovedWholeFileWrites(
94
+ opts: ExactApplyOptions,
95
+ ): Promise<Set<string>> {
96
+ const applied = new Set<string>();
97
+
98
+ for (const msg of opts.messages) {
99
+ for (const tc of msg.toolCalls) {
100
+ if (!isApprovedWholeFileWrite(tc)) continue;
101
+
102
+ const args = argsRecord(tc);
103
+ const rawPath = extractFilePath(args);
104
+ if (!rawPath) {
105
+ // A whole-file write always carries a path arg; a missing one is
106
+ // unexpected — do not guess a path, fall back.
107
+ logSkip(opts.executionId, tc, "no file path in tool args");
108
+ continue;
109
+ }
110
+ // Resolve against the workspace root with the same convention the gate used
111
+ // (Cursor: real paths, not virtual), so the applied path matches the one the
112
+ // user approved.
113
+ const { absolutePath: target } = resolveWorkspacePath(
114
+ rawPath,
115
+ opts.workspaceBackend.rootDir,
116
+ /* virtualRoot */ false,
117
+ );
118
+ if (!isWithinWorkspace(target, opts.workspaceDirs)) {
119
+ logSkip(opts.executionId, tc, `target outside workspace: ${target}`);
120
+ continue;
121
+ }
122
+
123
+ const content = resolveApprovedWholeFileContent(tc);
124
+ if (content === null) {
125
+ logSkip(opts.executionId, tc, "exact approved bytes unresolvable");
126
+ continue;
127
+ }
128
+
129
+ try {
130
+ await opts.workspaceBackend.writeFile(target, content);
131
+ } catch (err) {
132
+ logSkip(
133
+ opts.executionId,
134
+ tc,
135
+ `write failed: ${err instanceof Error ? err.message : String(err)}`,
136
+ );
137
+ continue;
138
+ }
139
+
140
+ // The tool is now genuinely done: the runner applied exactly what the user
141
+ // approved (the same `args` bytes the gate showed). Mark COMPLETED in place
142
+ // (same id — the backend's append-only-at-identity guard accepts a status
143
+ // change). The approval_action is preserved as the audit trail; the status
144
+ // flip removes it from the server's pending_approvals projection (which
145
+ // keys on WAITING_APPROVAL).
146
+ tc.status = ToolCallStatus.TOOL_CALL_COMPLETED;
147
+ if (!tc.completedAt) tc.completedAt = utcTimestamp();
148
+ tc.error = "";
149
+ applied.add(tc.id);
150
+ console.log(
151
+ `ExecuteCursor exact-apply: wrote approved bytes to ${target} ` +
152
+ `(tool=${tc.id}); no resource grant issued, so a further change ` +
153
+ `re-gates. execution=${opts.executionId}`,
154
+ );
155
+ }
156
+ }
157
+
158
+ return applied;
159
+ }
160
+
161
+ /**
162
+ * The adjudicated approvals MINUS the ones the runner already exact-applied — the
163
+ * set the caller turns into resource grants for the resumed agent.
164
+ *
165
+ * This is the "issues NO grant for an applied write" half of the guarantee: an
166
+ * exact-applied whole-file write is intentionally NOT granted, so if the
167
+ * reinvoked model writes to that same file again it is re-gated and the user
168
+ * reviews the new change (the deny-gate's "what you approve is what gets applied"
169
+ * property). Non-applied approvals (hunk edits, shell, MCP, and every reject /
170
+ * skip) pass through unchanged — they still need their grant to flow on resume.
171
+ *
172
+ * Pure and order-independent. Load-bearing because the caller computes the
173
+ * adjudicated set ONCE (before {@link applyApprovedWholeFileWrites} flips the
174
+ * applied calls to COMPLETED) and never re-derives it, so this exclusion is the
175
+ * only thing that keeps an applied write out of the grants.
176
+ */
177
+ export function excludeAppliedFromGrants(
178
+ adjudicatedApprovals: readonly PendingApproval[],
179
+ appliedToolCallIds: ReadonlySet<string>,
180
+ ): PendingApproval[] {
181
+ return adjudicatedApprovals.filter((pa) => !appliedToolCallIds.has(pa.toolCallId));
182
+ }
183
+
184
+ /**
185
+ * The exact approved bytes for a whole-file write, or `null` when they cannot be
186
+ * recovered (in which case the caller must fall back, never write).
187
+ *
188
+ * The bytes come from the gated call's `args` whole-file content — the single
189
+ * authoritative copy of the proposed body (the deny-gate stamped it from the
190
+ * hook input; see {@link isApprovedWholeFileWrite}). `args` is never offloaded to
191
+ * a ref (only dropped wholesale by the aggregate size backstop), so when present
192
+ * it is intact.
193
+ *
194
+ * It NEVER returns the elision marker: that is a lossy stand-in and writing it
195
+ * would corrupt the file. An `args` body equal to the marker is treated as
196
+ * unresolved, and the caller degrades to grant + reinvocation.
197
+ */
198
+ export function resolveApprovedWholeFileContent(tc: ToolCall): string | null {
199
+ const content = extractWriteContent(argsRecord(tc));
200
+ if (content !== null && content !== ELISION_MARKER) return content;
201
+ return null;
202
+ }
203
+
204
+ /**
205
+ * Whether `tc` is an approved, still-pending WHOLE-FILE write eligible for
206
+ * exact-apply. APPROVE and APPROVE_ALL both approve the clicked tool (the latter
207
+ * also leases the class, handled separately by the caller). A SKIP/REJECT, an
208
+ * edit-family call (`old_string`/`new_string`, which carries no whole-file body),
209
+ * and an already-applied (COMPLETED) call are all excluded.
210
+ *
211
+ * The whole-file predicate is `extractWriteContent(args) !== null` — the exact
212
+ * condition the removed field-22 capture used to classify a change WHOLE_FILE
213
+ * (a whole-file body present) vs HUNK_ONLY (an edit fragment). Reading it from
214
+ * `args` keeps eligibility on the single source of truth.
215
+ */
216
+ function isApprovedWholeFileWrite(tc: ToolCall): boolean {
217
+ if (tc.status !== ToolCallStatus.TOOL_CALL_WAITING_APPROVAL) return false;
218
+ if (
219
+ tc.approvalAction !== ApprovalAction.APPROVE &&
220
+ tc.approvalAction !== ApprovalAction.APPROVE_ALL
221
+ ) {
222
+ return false;
223
+ }
224
+ return extractWriteContent(argsRecord(tc)) !== null;
225
+ }
226
+
227
+ /** The gated tool call's structured args as a plain record (empty when absent). */
228
+ function argsRecord(tc: ToolCall): Record<string, unknown> {
229
+ return tc.args && typeof tc.args === "object"
230
+ ? (tc.args as Record<string, unknown>)
231
+ : {};
232
+ }
233
+
234
+ /** Whether `absTarget` resolves inside one of the configured workspace roots. */
235
+ function isWithinWorkspace(
236
+ absTarget: string,
237
+ workspaceDirs: readonly string[],
238
+ ): boolean {
239
+ const target = resolve(absTarget);
240
+ return workspaceDirs.some((dir) => {
241
+ const root = resolve(dir);
242
+ if (target === root) return true;
243
+ const rel = relative(root, target);
244
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
245
+ });
246
+ }
247
+
248
+ function logSkip(executionId: string, tc: ToolCall, reason: string): void {
249
+ console.log(
250
+ `ExecuteCursor exact-apply skipped (falling back to grant+reinvocation): ` +
251
+ `tool=${tc.id} reason="${reason}" execution=${executionId}`,
252
+ );
253
+ }