@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
@@ -42,6 +42,7 @@ describe("ConnectMcpServerWorkflow", () => {
42
42
  previousToolsFingerprint: "",
43
43
  previousToolApprovals: [],
44
44
  newToolsFingerprint: "abc123def456",
45
+ previousTools: [],
45
46
  };
46
47
 
47
48
  const classifyResult: ToolApprovalResult[] = [
@@ -93,21 +94,21 @@ describe("ConnectMcpServerWorkflow", () => {
93
94
  });
94
95
 
95
96
  // ─────────────────────────────────────────────────────────────────────────
96
- // Fingerprint short-circuit
97
+ // Incremental classification (content-addressed reuse)
97
98
  // ─────────────────────────────────────────────────────────────────────────
98
99
 
99
- describe("fingerprint short-circuit", () => {
100
- it("reuses previous approvals when fingerprint unchanged", async () => {
100
+ describe("incremental classification", () => {
101
+ it("reuses prior decisions and skips the LLM when all tools are unchanged", async () => {
101
102
  const { connectMcpServer } = await import("../connect-mcp-server.js");
102
103
 
103
- const fingerprint = "deadbeef12345678";
104
+ const search = { name: "search", description: "Search", inputSchema: null };
105
+ const del = { name: "delete_repo", description: "Delete", inputSchema: null };
104
106
  mockDiscoverActivity.mockResolvedValue(
105
107
  makeDiscoveryResult({
106
- tools: [{ name: "search", description: "Search", inputSchema: null }],
107
- newToolsFingerprint: fingerprint,
108
- previousToolsFingerprint: fingerprint,
108
+ tools: [search, del],
109
+ previousTools: [search, del],
109
110
  previousToolApprovals: [
110
- { toolName: "search", requiresApproval: false, message: "" },
111
+ { toolName: "delete_repo", requiresApproval: true, message: "Delete repo" },
111
112
  ],
112
113
  }),
113
114
  );
@@ -115,73 +116,346 @@ describe("ConnectMcpServerWorkflow", () => {
115
116
  const result = await connectMcpServer({ mcp_server_id: "mcp-cached" });
116
117
 
117
118
  expect(mockClassifyActivity).not.toHaveBeenCalled();
119
+ // The gated tool is carried forward; the unchanged un-gated tool stays absent.
118
120
  expect(result.tool_approvals).toEqual([
119
- { tool_name: "search", requires_approval: false, message: "" },
121
+ { tool_name: "delete_repo", requires_approval: true, message: "Delete repo" },
120
122
  ]);
121
123
  });
122
124
 
123
- it("runs classify when fingerprint matches but no previous approvals", async () => {
125
+ it("classifies all tools on first connect (no previous tools)", async () => {
124
126
  const { connectMcpServer } = await import("../connect-mcp-server.js");
125
127
 
126
- const fingerprint = "deadbeef12345678";
127
128
  mockDiscoverActivity.mockResolvedValue(
128
129
  makeDiscoveryResult({
129
- tools: [{ name: "search", description: "Search", inputSchema: null }],
130
- newToolsFingerprint: fingerprint,
131
- previousToolsFingerprint: fingerprint,
130
+ tools: [
131
+ { name: "search", description: "Search", inputSchema: null },
132
+ { name: "create", description: "Create", inputSchema: null },
133
+ ],
134
+ previousTools: [],
132
135
  previousToolApprovals: [],
133
136
  }),
134
137
  );
135
138
  mockClassifyActivity.mockResolvedValue([]);
136
139
 
137
- await connectMcpServer({ mcp_server_id: "mcp-no-prev" });
140
+ await connectMcpServer({ mcp_server_id: "mcp-first" });
138
141
 
139
142
  expect(mockClassifyActivity).toHaveBeenCalledOnce();
143
+ expect(mockClassifyActivity.mock.calls[0][0].tools).toHaveLength(2);
140
144
  });
141
145
 
142
- it("runs classify when fingerprints differ", async () => {
146
+ it("classifies only the newly added tool and merges with carried-forward decisions", async () => {
143
147
  const { connectMcpServer } = await import("../connect-mcp-server.js");
144
148
 
149
+ const known = { name: "search", description: "Search", inputSchema: null };
150
+ const gated = { name: "delete_repo", description: "Delete", inputSchema: null };
151
+ const added = { name: "create_pr", description: "Create PR", inputSchema: null };
145
152
  mockDiscoverActivity.mockResolvedValue(
146
153
  makeDiscoveryResult({
147
- tools: [{ name: "new_tool", description: "New", inputSchema: null }],
148
- newToolsFingerprint: "new-fingerprint",
149
- previousToolsFingerprint: "old-fingerprint",
154
+ tools: [known, gated, added],
155
+ previousTools: [known, gated],
150
156
  previousToolApprovals: [
151
- { toolName: "old_tool", requiresApproval: true, message: "Approve" },
157
+ { toolName: "delete_repo", requiresApproval: true, message: "Delete repo" },
152
158
  ],
153
159
  }),
154
160
  );
155
161
  mockClassifyActivity.mockResolvedValue([
156
- { tool_name: "new_tool", requires_approval: true, message: "Approve new" },
162
+ { tool_name: "create_pr", requires_approval: true, message: "Create PR" },
163
+ ]);
164
+
165
+ const result = await connectMcpServer({ mcp_server_id: "mcp-added" });
166
+
167
+ expect(mockClassifyActivity).toHaveBeenCalledOnce();
168
+ expect(mockClassifyActivity.mock.calls[0][0].tools).toEqual([
169
+ { name: "create_pr", description: "Create PR", input_schema: null },
170
+ ]);
171
+ expect(result.tool_approvals).toEqual([
172
+ { tool_name: "delete_repo", requires_approval: true, message: "Delete repo" },
173
+ { tool_name: "create_pr", requires_approval: true, message: "Create PR" },
174
+ ]);
175
+ });
176
+
177
+ it("re-classifies a tool whose definition changed (same name, new schema)", async () => {
178
+ const { connectMcpServer } = await import("../connect-mcp-server.js");
179
+
180
+ mockDiscoverActivity.mockResolvedValue(
181
+ makeDiscoveryResult({
182
+ tools: [{ name: "run", description: "Run", inputSchema: { properties: { cmd: {} } } }],
183
+ previousTools: [{ name: "run", description: "Run", inputSchema: null }],
184
+ previousToolApprovals: [],
185
+ }),
186
+ );
187
+ mockClassifyActivity.mockResolvedValue([
188
+ { tool_name: "run", requires_approval: true, message: "Run {{args.cmd}}" },
157
189
  ]);
158
190
 
159
191
  const result = await connectMcpServer({ mcp_server_id: "mcp-changed" });
160
192
 
161
193
  expect(mockClassifyActivity).toHaveBeenCalledOnce();
194
+ expect(mockClassifyActivity.mock.calls[0][0].tools).toHaveLength(1);
162
195
  expect(result.tool_approvals).toEqual([
163
- { tool_name: "new_tool", requires_approval: true, message: "Approve new" },
196
+ { tool_name: "run", requires_approval: true, message: "Run {{args.cmd}}" },
164
197
  ]);
165
198
  });
166
199
 
167
- it("runs classify when new fingerprint is empty (no tools)", async () => {
200
+ it("drops a previously-gated tool that no longer exists", async () => {
168
201
  const { connectMcpServer } = await import("../connect-mcp-server.js");
169
202
 
203
+ const kept = { name: "search", description: "Search", inputSchema: null };
170
204
  mockDiscoverActivity.mockResolvedValue(
171
205
  makeDiscoveryResult({
172
- tools: [],
173
- newToolsFingerprint: "",
174
- previousToolsFingerprint: "",
206
+ tools: [kept],
207
+ previousTools: [kept, { name: "removed", description: "Gone", inputSchema: null }],
175
208
  previousToolApprovals: [
176
- { toolName: "old", requiresApproval: true, message: "Approve" },
209
+ { toolName: "removed", requiresApproval: true, message: "Approve removed" },
210
+ ],
211
+ }),
212
+ );
213
+
214
+ const result = await connectMcpServer({ mcp_server_id: "mcp-removed" });
215
+
216
+ expect(mockClassifyActivity).not.toHaveBeenCalled();
217
+ expect(result.tool_approvals).toEqual([]);
218
+ });
219
+ });
220
+
221
+ // ─────────────────────────────────────────────────────────────────────────
222
+ // planIncrementalClassification — pure helper
223
+ // ─────────────────────────────────────────────────────────────────────────
224
+
225
+ describe("planIncrementalClassification", () => {
226
+ it("treats every tool as new when there is no previous state", async () => {
227
+ const { planIncrementalClassification } = await import("../connect-mcp-server.js");
228
+ const tools = [
229
+ { name: "a", description: "A", inputSchema: null },
230
+ { name: "b", description: "B", inputSchema: null },
231
+ ];
232
+ const { toolsToClassify, carriedForward } = planIncrementalClassification([], [], tools);
233
+ expect(toolsToClassify).toEqual(tools);
234
+ expect(carriedForward).toEqual([]);
235
+ });
236
+
237
+ it("carries forward a gated decision for an unchanged tool", async () => {
238
+ const { planIncrementalClassification } = await import("../connect-mcp-server.js");
239
+ const tool = { name: "delete_repo", description: "Delete", inputSchema: null };
240
+ const { toolsToClassify, carriedForward } = planIncrementalClassification(
241
+ [tool],
242
+ [{ toolName: "delete_repo", requiresApproval: true, message: "Delete repo" }],
243
+ [tool],
244
+ );
245
+ expect(toolsToClassify).toEqual([]);
246
+ expect(carriedForward).toEqual([
247
+ { tool_name: "delete_repo", requires_approval: true, message: "Delete repo" },
248
+ ]);
249
+ });
250
+
251
+ it("emits nothing for an unchanged tool that was not gated", async () => {
252
+ const { planIncrementalClassification } = await import("../connect-mcp-server.js");
253
+ const tool = { name: "search", description: "Search", inputSchema: null };
254
+ const { toolsToClassify, carriedForward } = planIncrementalClassification([tool], [], [tool]);
255
+ expect(toolsToClassify).toEqual([]);
256
+ expect(carriedForward).toEqual([]);
257
+ });
258
+
259
+ it("flags a tool for classification when its definition changed", async () => {
260
+ const { planIncrementalClassification } = await import("../connect-mcp-server.js");
261
+ const prev = { name: "run", description: "Run", inputSchema: null };
262
+ const next = { name: "run", description: "Run", inputSchema: { properties: { cmd: {} } } };
263
+ const { toolsToClassify, carriedForward } = planIncrementalClassification(
264
+ [prev],
265
+ [{ toolName: "run", requiresApproval: true, message: "old" }],
266
+ [next],
267
+ );
268
+ expect(toolsToClassify).toEqual([next]);
269
+ // The changed tool is NOT carried forward — it will be re-classified.
270
+ expect(carriedForward).toEqual([]);
271
+ });
272
+
273
+ it("partitions a mix of unchanged, changed, added, and removed tools", async () => {
274
+ const { planIncrementalClassification } = await import("../connect-mcp-server.js");
275
+ const unchanged = { name: "search", description: "Search", inputSchema: null };
276
+ const changedPrev = { name: "edit", description: "Edit", inputSchema: null };
277
+ const changedNext = { name: "edit", description: "Edit a file", inputSchema: null };
278
+ const added = { name: "create", description: "Create", inputSchema: null };
279
+ const removed = { name: "remove", description: "Remove", inputSchema: null };
280
+
281
+ const { toolsToClassify, carriedForward } = planIncrementalClassification(
282
+ [unchanged, changedPrev, removed],
283
+ [
284
+ { toolName: "search", requiresApproval: true, message: "Search msg" },
285
+ { toolName: "remove", requiresApproval: true, message: "Remove msg" },
286
+ ],
287
+ [unchanged, changedNext, added],
288
+ );
289
+
290
+ expect(toolsToClassify).toEqual([changedNext, added]);
291
+ expect(carriedForward).toEqual([
292
+ { tool_name: "search", requires_approval: true, message: "Search msg" },
293
+ ]);
294
+ });
295
+ });
296
+
297
+ // ─────────────────────────────────────────────────────────────────────────
298
+ // applyDestructiveHintTightener — pure helper (annotations tighten only)
299
+ // ─────────────────────────────────────────────────────────────────────────
300
+
301
+ describe("applyDestructiveHintTightener", () => {
302
+ it("force-gates a tool the classifier left un-gated when destructiveHint=true", async () => {
303
+ const { applyDestructiveHintTightener } = await import("../connect-mcp-server.js");
304
+
305
+ const gated: ToolApprovalResult[] = [];
306
+ const tools = [
307
+ { name: "wipe_db", description: "Wipe", inputSchema: null, annotations: { destructiveHint: true } },
308
+ ];
309
+
310
+ const { tightened, addedCount } = applyDestructiveHintTightener(gated, tools);
311
+
312
+ expect(addedCount).toBe(1);
313
+ expect(tightened).toEqual([
314
+ {
315
+ tool_name: "wipe_db",
316
+ requires_approval: true,
317
+ message: "Execute wipe_db",
318
+ from_destructive_hint: true,
319
+ },
320
+ ]);
321
+ });
322
+
323
+ it("does not duplicate a tool that is already gated", async () => {
324
+ const { applyDestructiveHintTightener } = await import("../connect-mcp-server.js");
325
+
326
+ const gated: ToolApprovalResult[] = [
327
+ { tool_name: "wipe_db", requires_approval: true, message: "Wipe {{args.db}}" },
328
+ ];
329
+ const tools = [
330
+ { name: "wipe_db", description: "Wipe", inputSchema: null, annotations: { destructiveHint: true } },
331
+ ];
332
+
333
+ const { tightened, addedCount } = applyDestructiveHintTightener(gated, tools);
334
+
335
+ expect(addedCount).toBe(0);
336
+ // The richer classifier message is preserved — no clobbering.
337
+ expect(tightened).toEqual(gated);
338
+ });
339
+
340
+ it("NEVER relaxes: a spoofed readOnlyHint on a destructive tool stays/forces gated", async () => {
341
+ const { applyDestructiveHintTightener } = await import("../connect-mcp-server.js");
342
+
343
+ // A malicious server marks a destructive tool readOnly to dodge approval.
344
+ const tools = [
345
+ {
346
+ name: "delete_all",
347
+ description: "Deletes everything",
348
+ inputSchema: null,
349
+ annotations: { readOnlyHint: true, destructiveHint: true },
350
+ },
351
+ ];
352
+
353
+ const { tightened, addedCount } = applyDestructiveHintTightener([], tools);
354
+
355
+ // readOnlyHint is ignored; destructiveHint force-gates it.
356
+ expect(addedCount).toBe(1);
357
+ expect(tightened[0]).toEqual({
358
+ tool_name: "delete_all",
359
+ requires_approval: true,
360
+ message: "Execute delete_all",
361
+ from_destructive_hint: true,
362
+ });
363
+ });
364
+
365
+ it("readOnlyHint alone never un-gates a classifier-gated tool", async () => {
366
+ const { applyDestructiveHintTightener } = await import("../connect-mcp-server.js");
367
+
368
+ const gated: ToolApprovalResult[] = [
369
+ { tool_name: "send_money", requires_approval: true, message: "Send {{args.amount}}" },
370
+ ];
371
+ const tools = [
372
+ { name: "send_money", description: "Transfer", inputSchema: null, annotations: { readOnlyHint: true } },
373
+ ];
374
+
375
+ const { tightened, addedCount } = applyDestructiveHintTightener(gated, tools);
376
+
377
+ expect(addedCount).toBe(0);
378
+ expect(tightened).toEqual(gated);
379
+ });
380
+
381
+ it("leaves tools without annotations or with destructiveHint!=true untouched", async () => {
382
+ const { applyDestructiveHintTightener } = await import("../connect-mcp-server.js");
383
+
384
+ const tools = [
385
+ { name: "read_file", description: "Read", inputSchema: null, annotations: { destructiveHint: false } },
386
+ { name: "list_dir", description: "List", inputSchema: null, annotations: null },
387
+ { name: "stat", description: "Stat", inputSchema: null },
388
+ ];
389
+
390
+ const { tightened, addedCount } = applyDestructiveHintTightener([], tools);
391
+
392
+ expect(addedCount).toBe(0);
393
+ expect(tightened).toEqual([]);
394
+ });
395
+ });
396
+
397
+ describe("connectMcpServer — destructiveHint tightener integration", () => {
398
+ it("force-gates a destructive-annotated tool the classifier auto-approved", async () => {
399
+ const { connectMcpServer } = await import("../connect-mcp-server.js");
400
+
401
+ mockDiscoverActivity.mockResolvedValue(
402
+ makeDiscoveryResult({
403
+ tools: [
404
+ { name: "search", description: "Search", inputSchema: null },
405
+ {
406
+ name: "purge_cache",
407
+ description: "Purge",
408
+ inputSchema: null,
409
+ annotations: { destructiveHint: true },
410
+ },
177
411
  ],
178
412
  }),
179
413
  );
414
+ // Classifier auto-approves both (returns no gated tools).
180
415
  mockClassifyActivity.mockResolvedValue([]);
181
416
 
182
- await connectMcpServer({ mcp_server_id: "mcp-empty" });
417
+ const result = await connectMcpServer({ mcp_server_id: "mcp-destructive" });
183
418
 
184
- expect(mockClassifyActivity).toHaveBeenCalledOnce();
419
+ expect(result.tool_approvals).toEqual([
420
+ {
421
+ tool_name: "purge_cache",
422
+ requires_approval: true,
423
+ message: "Execute purge_cache",
424
+ from_destructive_hint: true,
425
+ },
426
+ ]);
427
+ });
428
+
429
+ it("re-asserts gating for a carried-forward tool the server flips to destructive", async () => {
430
+ const { connectMcpServer } = await import("../connect-mcp-server.js");
431
+
432
+ // Unchanged tool (reuse path, classifier skipped) that the server now
433
+ // annotates destructive on the live connect.
434
+ const tool = {
435
+ name: "rotate_keys",
436
+ description: "Rotate",
437
+ inputSchema: null,
438
+ annotations: { destructiveHint: true },
439
+ };
440
+ mockDiscoverActivity.mockResolvedValue(
441
+ makeDiscoveryResult({
442
+ tools: [tool],
443
+ previousTools: [{ name: "rotate_keys", description: "Rotate", inputSchema: null }],
444
+ previousToolApprovals: [],
445
+ }),
446
+ );
447
+
448
+ const result = await connectMcpServer({ mcp_server_id: "mcp-flip" });
449
+
450
+ expect(mockClassifyActivity).not.toHaveBeenCalled();
451
+ expect(result.tool_approvals).toEqual([
452
+ {
453
+ tool_name: "rotate_keys",
454
+ requires_approval: true,
455
+ message: "Execute rotate_keys",
456
+ from_destructive_hint: true,
457
+ },
458
+ ]);
185
459
  });
186
460
  });
187
461
 
@@ -355,5 +629,6 @@ function makeDiscoveryResult(
355
629
  previousToolsFingerprint: overrides.previousToolsFingerprint ?? "",
356
630
  previousToolApprovals: overrides.previousToolApprovals ?? [],
357
631
  newToolsFingerprint: overrides.newToolsFingerprint ?? "new-fp-default",
632
+ previousTools: overrides.previousTools ?? [],
358
633
  };
359
634
  }
@@ -169,6 +169,39 @@ export async function orchestrateAgentCall(
169
169
  activityDone = true;
170
170
  });
171
171
 
172
+ // Last-written file-review reference for this child, as a deterministic key, so
173
+ // we write pending_file_reviews only when the child's AWAITING_REVIEW set changes
174
+ // (avoids status-write storms). undefined = never written yet.
175
+ let lastFileReviewKey: string | undefined;
176
+
177
+ // Poll-derive the child's file-review gate and surface it (reference-only) on the
178
+ // parent. No new Temporal signal: the orchestrator already holds the child open
179
+ // (async-completion) and reads its status here on the same cadence as progress.
180
+ async function syncFileReviews(childId: string): Promise<void> {
181
+ let ids: string[];
182
+ try {
183
+ ids = await statusProxy.GetAwaitingFileReviewChangeSetIds(childId);
184
+ } catch (err) {
185
+ log.warn("Failed to derive child file-review state (non-fatal)", {
186
+ error: String(err),
187
+ taskName: input.taskName,
188
+ });
189
+ return;
190
+ }
191
+ // Deterministic change key (sorted): a stable comparison across replays.
192
+ const key = [...ids].sort().join(",");
193
+ if (key === lastFileReviewKey) return;
194
+ lastFileReviewKey = key;
195
+ try {
196
+ await statusProxy.UpdateWorkflowFileReviewStatus(input.workflowExecutionId, childId, ids);
197
+ } catch (err) {
198
+ log.warn("Failed to update workflow file-review status (non-fatal)", {
199
+ error: String(err),
200
+ taskName: input.taskName,
201
+ });
202
+ }
203
+ }
204
+
172
205
  while (!activityDone) {
173
206
  // Wait for a signal, activity completion, or periodic timeout for progress polling.
174
207
  // condition() returns false on timeout, true when the predicate became true.
@@ -183,6 +216,7 @@ export async function orchestrateAgentCall(
183
216
  if (childExecId && !initialProgressEmitted) {
184
217
  initialProgressEmitted = true;
185
218
  await emitProgress(input, childExecId, null);
219
+ await syncFileReviews(childExecId);
186
220
  }
187
221
 
188
222
  // Periodic progress: on timeout, poll the child execution for live data
@@ -199,6 +233,7 @@ export async function orchestrateAgentCall(
199
233
  if (progress) {
200
234
  await emitProgress(input, childExecId, progress);
201
235
  }
236
+ await syncFileReviews(childExecId);
202
237
  }
203
238
 
204
239
  // Handle HITL approval notification
@@ -228,12 +263,24 @@ export async function orchestrateAgentCall(
228
263
  await emitProgress(input, childExecId, null);
229
264
  }
230
265
 
231
- try {
232
- await statusProxy.ClearWorkflowApprovalStatus(input.workflowExecutionId);
233
- } catch (clearErr) {
234
- log.warn("Failed to clear workflow approval status (non-fatal)", {
235
- error: String(clearErr),
236
- });
266
+ // Scoped clears for this child only (per-child merge) — a completing child must
267
+ // not wipe a parallel sibling's still-pending gate. Skipped if the child never
268
+ // started (nothing was ever surfaced for it).
269
+ if (childExecId) {
270
+ try {
271
+ await statusProxy.ClearWorkflowApprovalStatus(input.workflowExecutionId, childExecId);
272
+ } catch (clearErr) {
273
+ log.warn("Failed to clear workflow approval status (non-fatal)", {
274
+ error: String(clearErr),
275
+ });
276
+ }
277
+ try {
278
+ await statusProxy.UpdateWorkflowFileReviewStatus(input.workflowExecutionId, childExecId, []);
279
+ } catch (clearErr) {
280
+ log.warn("Failed to clear workflow file-review status (non-fatal)", {
281
+ error: String(clearErr),
282
+ });
283
+ }
237
284
  }
238
285
 
239
286
  if (activityError) {