@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,234 @@
1
+ /**
2
+ * Cursor adapter over the harness-agnostic capture-mode orchestration
3
+ * ({@link ../../shared/filereview/capture.js}).
4
+ *
5
+ * The substrate (git snapshot/restore) and the orchestration (baseline/candidate/
6
+ * reconcile authoring) live in `shared/filereview/` so the Cursor and deep-agent
7
+ * harnesses author IDENTICAL ledger entries. This adapter binds the Cursor
8
+ * specifics:
9
+ * - `harnessId = "cursor"` (stamped on the BASELINE payload the projection reads);
10
+ * - the Cursor gate files written into the workspace, excluded from the captured
11
+ * diff (`CURSOR_RUNNER_OWNED_PATHS`);
12
+ * - stamping the streamed file-edit transcript rows that flowed this turn with
13
+ * the change set id (`stampFlowedFileEditRows`) — the rows stay visible as
14
+ * observational/audit records at their transcript positions, while
15
+ * `file_change_sets` remains the single DECISION surface.
16
+ *
17
+ * The three turn-boundary functions keep their original signatures so the
18
+ * activity wiring (index.ts) and the cutover tests are unchanged.
19
+ */
20
+ import { readFile } from "node:fs/promises";
21
+ import { join } from "node:path";
22
+ import { FileCaptureClass } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
23
+ import { approvalCategory } from "./approval-policy.js";
24
+ import { toolIdentity, primaryToken } from "./approval-state.js";
25
+ import { readCasObservations } from "./cas-observations.js";
26
+ import { contentDigest } from "../../shared/file-tools.js";
27
+ import { isToolCallRowHidden, stampFileEditRow } from "../../shared/tool-row.js";
28
+ import { applyCaptureDecisions as sharedApplyCaptureDecisions, captureBaselineToLedger as sharedCaptureBaselineToLedger, captureCandidateToLedger as sharedCaptureCandidateToLedger, } from "../../shared/filereview/capture.js";
29
+ import { hasCandidateCaptured } from "../../shared/filereview/events.js";
30
+ import { partitionIgnoredPathsBySecret } from "../../shared/filereview/secret-paths.js";
31
+ import { casBlobReader } from "../../shared/filereview/cas-substrate.js";
32
+ /** The harness id the projection reads from the BASELINE payload (load-bearing). */
33
+ const HARNESS_ID = "cursor";
34
+ /**
35
+ * Workspace-relative paths the Cursor gate writes into the repo. They are excluded
36
+ * from capture so a turn's diff never shows the gate's own machinery. (The
37
+ * workspace-scoped gate dir and SDK state live under `~/.stigmer` / the
38
+ * git-excluded `.stigmer`, so they need no exclusion here.)
39
+ */
40
+ const CURSOR_RUNNER_OWNED_PATHS = [
41
+ ".cursor/hooks.json",
42
+ ".cursor/rules/stigmer-tool-approval.mdc",
43
+ ];
44
+ // `deriveCaptureMode` is the single capture-vs-deny-gate decision, now shared by
45
+ // BOTH harnesses from `shared/filereview/capture.ts`. Re-exported here so existing
46
+ // Cursor imports (index.ts, the deny-gate-exact-apply seam test) keep resolving
47
+ // from this adapter.
48
+ export { deriveCaptureMode } from "../../shared/filereview/capture.js";
49
+ /**
50
+ * Turn start: pin the pre-turn working tree behind the baseline ref and author
51
+ * BASELINE_CAPTURED (harnessId "cursor"). Returns the baseline tree sha for the
52
+ * turn-end diff.
53
+ */
54
+ export function captureBaselineToLedger(opts) {
55
+ return sharedCaptureBaselineToLedger({
56
+ ...opts,
57
+ harnessId: HARNESS_ID,
58
+ excludePaths: CURSOR_RUNNER_OWNED_PATHS,
59
+ });
60
+ }
61
+ /**
62
+ * Turn end: capture the change set + author CANDIDATE_CAPTURED, then stamp the
63
+ * streamed file-edit rows that flowed this turn with the change set id — the
64
+ * rows stay visible in place as observational records while `file_change_sets`
65
+ * remains the single decision surface. The working tree is LEFT applied
66
+ * (Cursor parity).
67
+ *
68
+ * The change set is HYBRID (git tree) or CAS-only (non-git): the hook-staged
69
+ * writes from the cas-observations sidecar this turn ({@link readCasObservations})
70
+ * — the Cursor analog of deep-agent's `buildCasTurnCaptures` — composed with the
71
+ * git-tracked diff when `gitWorkspace`. Non-secret staged paths become
72
+ * `GIT_IGNORED_CAPTURED` (git tree) or `NON_GIT_CAS` (non-git) CAS captures
73
+ * (before-bytes from the sidecar, after-bytes re-read from disk);
74
+ * secret-blocked paths become content-less `DIFF_UNREVIEWABLE` entries. The
75
+ * boundary re-runs {@link partitionIgnoredPathsBySecret} as a fail-closed
76
+ * backstop, so a secret that ever slipped into the captured set still has its
77
+ * bytes withheld from durable storage.
78
+ *
79
+ * `deniedTokens` are the identities the hook gated this turn (shell/MCP, or a
80
+ * gitignored delete). A streamed file-edit row whose identity is in that set is
81
+ * left for the deny-gate reconcile path — it did NOT flow. A flowed gitignored
82
+ * write is NOT in that set (the hook allowed it), so it is stamped like any
83
+ * other flowed edit and its captured delta surfaces as a CAS entry in the set.
84
+ *
85
+ * `hitlDir`/`storage` are omitted only by callers with no artifact storage
86
+ * (captureIgnored off); the CAS half is then skipped and this is a git-only
87
+ * capture exactly as before.
88
+ *
89
+ * Mutates `messages` and `status` in place. Returns the captured git changes.
90
+ */
91
+ export async function captureTurnToLedger(opts) {
92
+ const { status, gitRoot, executionId, changeSetId, baselineTree, messages, deniedTokens, hitlDir, storage, priorSubAgentToolCallIds, commandProvenance } = opts;
93
+ const gitWorkspace = opts.gitWorkspace ?? true;
94
+ // The CAS substrate class for this turn's staged writes: gitignored paths in a
95
+ // git tree, all touched paths in a non-git workspace.
96
+ const casCaptureClass = gitWorkspace
97
+ ? FileCaptureClass.GIT_IGNORED_CAPTURED
98
+ : FileCaptureClass.NON_GIT_CAS;
99
+ const { casCaptures, unreviewablePaths } = await buildCasTurnCaptures(gitRoot, hitlDir, storage, casCaptureClass);
100
+ const changes = await sharedCaptureCandidateToLedger({
101
+ status,
102
+ gitRoot,
103
+ executionId,
104
+ changeSetId,
105
+ baselineTree,
106
+ harnessId: HARNESS_ID,
107
+ excludePaths: CURSOR_RUNNER_OWNED_PATHS,
108
+ casCaptures,
109
+ storage,
110
+ unreviewablePaths,
111
+ unreviewableCaptureClass: casCaptureClass,
112
+ gitWorkspace,
113
+ commandProvenance,
114
+ });
115
+ // Observational rows: the reviewable diff lives on the file_review ledger
116
+ // (projected to file_change_sets); the streamed file-edit rows that flowed
117
+ // this turn stay visible in place, stamped with the change set id so clients
118
+ // badge them and anchor the decision surface. Denied (gitignored-delete/
119
+ // shell) rows stay on the deny-gate path. Gated on an authored CANDIDATE —
120
+ // a no-op turn (every edit reverted before the boundary) authors no event,
121
+ // and a row must never reference a change set that does not exist.
122
+ if (hasCandidateCaptured(status, changeSetId)) {
123
+ stampFlowedFileEditRows(messages, deniedTokens, changeSetId);
124
+ // Sub-agent edit rows fold their files into the SAME parent turn set, so they
125
+ // carry the same change set id. Walked separately (they live under
126
+ // subAgentExecutions, not the top-level transcript) and scoped to this turn
127
+ // via the pre-turn snapshot of seeded sub-agent tool-call ids.
128
+ for (const sa of status.subAgentExecutions) {
129
+ stampFlowedFileEditRows(sa.messages, deniedTokens, changeSetId, priorSubAgentToolCallIds);
130
+ }
131
+ }
132
+ return changes;
133
+ }
134
+ /**
135
+ * Compose this turn's CAS captures from the sidecar the hook staged, mirroring
136
+ * deep-agent's `buildCasTurnCaptures`: for each non-secret observed path, the
137
+ * before-bytes come from the sidecar and the after-bytes are re-read from disk now
138
+ * (`null` = the file was removed after the write). Secret-blocked paths are
139
+ * returned as `unreviewablePaths`. The secret partition is re-run as a
140
+ * fail-closed backstop over the observed set. `captureClass` labels each captured
141
+ * path's provenance (GIT_IGNORED_CAPTURED for a git tree's ignored paths,
142
+ * NON_GIT_CAS for a non-git workspace).
143
+ *
144
+ * Returns empty when the harness has no storage (captureIgnored off) or the
145
+ * sidecar is empty — leaving the shared capture unchanged.
146
+ */
147
+ async function buildCasTurnCaptures(gitRoot, hitlDir, storage, captureClass) {
148
+ if (!hitlDir || !storage)
149
+ return { casCaptures: [], unreviewablePaths: [] };
150
+ const { captured, secretPaths } = await readCasObservations(hitlDir);
151
+ const beforeByPath = new Map(captured.map((c) => [c.path, c.before]));
152
+ const { capturablePaths, unreviewablePaths } = partitionIgnoredPathsBySecret(beforeByPath.keys(), new Set(secretPaths));
153
+ const casCaptures = [];
154
+ for (const relPath of capturablePaths) {
155
+ const after = await readFileOrNull(join(gitRoot, relPath));
156
+ casCaptures.push({
157
+ path: relPath,
158
+ before: beforeByPath.get(relPath) ?? null,
159
+ after,
160
+ captureClass,
161
+ });
162
+ }
163
+ return { casCaptures, unreviewablePaths: [...unreviewablePaths] };
164
+ }
165
+ /** Raw bytes of a file, or `null` when it does not exist (an ADD-then-removed). */
166
+ async function readFileOrNull(absolutePath) {
167
+ try {
168
+ return await readFile(absolutePath);
169
+ }
170
+ catch {
171
+ return null;
172
+ }
173
+ }
174
+ /**
175
+ * Resume: reconcile the working tree to the change set's DECIDED decisions
176
+ * (keep approved "after" bytes, snap rejected/undecided back to baseline,
177
+ * hash-verified), author RECONCILED / FAILED, and release the refs. Delegates to
178
+ * the shared orchestration with the Cursor harness id + exclude paths.
179
+ *
180
+ * `storage` threads the CAS blob store + reader so gitignored (CAS) files in the
181
+ * change set reconcile from the durable manifest — approved after-blobs written
182
+ * (hash-verified), rejected files snapped back to their before-blobs. Omit for a
183
+ * git-only harness/turn; the shared CAS branch is then skipped. The read path is
184
+ * the same one `exact-apply` already uses (`ArtifactStorage.download`).
185
+ */
186
+ export function applyCaptureDecisions(opts) {
187
+ const { storage, ...rest } = opts;
188
+ return sharedApplyCaptureDecisions({
189
+ ...rest,
190
+ harnessId: HARNESS_ID,
191
+ excludePaths: CURSOR_RUNNER_OWNED_PATHS,
192
+ storage,
193
+ readBlob: storage ? casBlobReader(storage) : undefined,
194
+ });
195
+ }
196
+ // ---------------------------------------------------------------------------
197
+ // Cursor-specific presentation
198
+ // ---------------------------------------------------------------------------
199
+ /**
200
+ * Stamp every streamed file-edit row (category write/delete) that flowed this
201
+ * turn with the change set id, so the row stays visible as an observational
202
+ * record and clients can badge it / anchor the decision surface. Skips:
203
+ * - already-stamped rows — the stamp is the idempotency AND cross-turn guard:
204
+ * a resume seeds prior turns' rows into this transcript, and re-stamping
205
+ * them with this turn's id would mis-attribute them ({@link stampFileEditRow});
206
+ * - legacy hidden rows (sessions persisted before stamping existed) — they
207
+ * belong to an earlier turn's change set, not this one;
208
+ * - denied identities (the deny-gate reconcile path owns those rows);
209
+ * - tool-call ids in `skipToolCallIds` — used for sub-agent rows, which lack
210
+ * the already-stamped/hidden shields the top-level transcript has, to scope
211
+ * the stamp to rows created this turn (the seeded prior sub-agents' ids).
212
+ */
213
+ function stampFlowedFileEditRows(messages, deniedTokens, changeSetId, skipToolCallIds) {
214
+ for (const msg of messages) {
215
+ for (const tc of msg.toolCalls) {
216
+ if (tc.fileChangeSetId)
217
+ continue;
218
+ if (skipToolCallIds?.has(tc.id))
219
+ continue;
220
+ if (isToolCallRowHidden(tc))
221
+ continue;
222
+ const category = approvalCategory(tc.name);
223
+ if (category !== "write" && category !== "delete")
224
+ continue;
225
+ const args = (tc.args ?? {});
226
+ const id = toolIdentity(tc.name, tc.mcpServerSlug, args);
227
+ const token = primaryToken(id.key, id.salient, contentDigest(args));
228
+ if (deniedTokens.has(token))
229
+ continue; // denied -> reconcile path owns it
230
+ stampFileEditRow(tc, changeSetId);
231
+ }
232
+ }
233
+ }
234
+ //# sourceMappingURL=capture-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-flow.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/capture-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The Cursor harness's disk-backed observer for gitignored file writes — the
3
+ * out-of-process analog of the deep-agent `CasCaptureFilesystemBackend`
4
+ * ({@link ../execute-deep-agent/cas-capture-backend.js}).
5
+ *
6
+ * Deep-agent runs the agent in-process, so it records a gitignored file's
7
+ * pre-turn bytes in an in-memory `CasBeforeMap` at the mutation point (before
8
+ * `super.write`). Cursor runs the agent as an EXTERNAL CLI subprocess whose only
9
+ * pre-write interception is the generated preToolUse bash hook — and each hook
10
+ * invocation is a FRESH, EPHEMERAL process that cannot hold in-memory state
11
+ * across writes. So Cursor's before-map must be DISK-BACKED: the hook stages each
12
+ * first-touched gitignored path into a per-turn "cas-observations" sidecar, and
13
+ * the runner reads it back at the turn boundary.
14
+ *
15
+ * The sidecar lives under the session HITL directory
16
+ * (`~/.stigmer/sessions/{id}/hitl/cas-observations/`), OUTSIDE the user's
17
+ * workspace — so staged before-bytes and secret markers never land in the repo,
18
+ * cannot pollute the captured git diff, and are never pushed. It is reset every
19
+ * turn (alongside the denial ledger) and consumed within the same activity
20
+ * invocation that produced it; resume reconciles from the durable CAS manifest,
21
+ * never this scratch. A Temporal retry re-runs the reset and re-observes.
22
+ *
23
+ * ONE MODULE OWNS THE ON-DISK FORMAT. Both sides live here so they cannot drift:
24
+ * - the READER ({@link readCasObservations}) the runner boundary calls;
25
+ * - the WRITER-SCRIPT GENERATOR ({@link buildObservationStagingScript}) whose
26
+ * output the hook embeds and runs on the runner's own Node binary. The writer
27
+ * classifies secrets from the SAME {@link isSecretLikePath} pattern arrays the
28
+ * boundary backstop uses (`shared/filereview/secret-paths.ts`), reconstructed
29
+ * as byte-identical `RegExp`s — no bash-regex, no divergence.
30
+ *
31
+ * Each observed path becomes one entry:
32
+ * - `captured` — a non-secret gitignored write/edit: `{path, kind, existed}`
33
+ * metadata plus a `.blob` of the pre-turn bytes (omitted for an ADD, where the
34
+ * file did not exist). First-touch-wins via atomic exclusive-create, so
35
+ * repeated edits to one path keep the true pre-turn before.
36
+ * - `secret` — a secret-like gitignored path the hook hard-blocked (denied,
37
+ * nothing written): `{path, kind}` only. The boundary authors it as a
38
+ * content-less `DIFF_UNREVIEWABLE` change; its bytes never reach storage.
39
+ *
40
+ * @since File-Change HITL Redesign (Cursor CAS parity — DD-18)
41
+ */
42
+ /** Sidecar directory name under the session HITL dir. */
43
+ export declare const CAS_OBSERVATIONS_DIRNAME = "cas-observations";
44
+ /** Absolute path of the per-turn cas-observations sidecar dir. */
45
+ export declare function casObservationsDir(hitlDir: string): string;
46
+ /**
47
+ * Truncate the cas-observations sidecar to empty for a fresh turn, returning its
48
+ * path. Called every turn alongside {@link resetDenialLedger} so the boundary
49
+ * only ever reads observations produced by the current run — deterministic across
50
+ * HITL reinvocations and Temporal activity retries.
51
+ */
52
+ export declare function resetCasObservations(hitlDir: string): Promise<string>;
53
+ /** One non-secret gitignored path the hook staged this turn. */
54
+ export interface CasObservationCapture {
55
+ /** Workspace-root-relative path. */
56
+ readonly path: string;
57
+ /** Pre-turn bytes; `null` when the file did not exist before (an ADD). */
58
+ readonly before: Uint8Array | null;
59
+ }
60
+ /** The turn's staged gitignored observations, split by the hook's secret gate. */
61
+ export interface CasObservations {
62
+ /** Non-secret paths to capture into CAS (before-bytes in hand). */
63
+ readonly captured: CasObservationCapture[];
64
+ /** Secret-like paths the hook hard-blocked (path-only; bytes withheld). */
65
+ readonly secretPaths: string[];
66
+ }
67
+ /**
68
+ * Read the cas-observations sidecar the hook wrote during the turn. Missing dir →
69
+ * no observations. Malformed/partial entries are tolerated and skipped (the hook
70
+ * writes the blob before the metadata marker, so a present marker implies a
71
+ * complete blob; a marker whose blob is unexpectedly missing is skipped rather
72
+ * than mis-captured). Entries are returned in a deterministic (sorted) order.
73
+ */
74
+ export declare function readCasObservations(hitlDir: string): Promise<CasObservations>;
75
+ /**
76
+ * Build the standalone Node.js script the hook runs to observe a single
77
+ * gitignored write — the disk-backed mirror of `CasCaptureFilesystemBackend`'s
78
+ * `recordBefore` plus the DD-E secret gate.
79
+ *
80
+ * Invoked as `printf %s "$SALIENT" | node -e '<this>' <workspaceRoot> <obsDir>`
81
+ * — the (arbitrary) salient path arrives on stdin so no argv escaping is needed;
82
+ * the two absolute paths ride argv. Prints one token on stdout for the hook:
83
+ * - `captured` — non-secret: staged the pre-turn bytes (first-touch-wins), the
84
+ * hook then ALLOWS the write to flow;
85
+ * - `secret` — secret-like: recorded a content-less marker, the hook then
86
+ * DENIES with the security message (nothing is written);
87
+ * - `error` — the path escaped the workspace or a filesystem error occurred,
88
+ * the hook then fails closed (deny).
89
+ *
90
+ * The secret classifier is generated from the SAME
91
+ * {@link SECRET_BASENAME_PATTERNS}/{@link SECRET_PATH_PATTERNS} the boundary
92
+ * backstop uses, so the hook-side and boundary-side classifications agree by
93
+ * construction (a table test locks the equivalence). Authored as a
94
+ * single-quoted-bash string, so the JS must contain no single quotes.
95
+ */
96
+ export declare function buildObservationStagingScript(): string;
@@ -0,0 +1,184 @@
1
+ /**
2
+ * The Cursor harness's disk-backed observer for gitignored file writes — the
3
+ * out-of-process analog of the deep-agent `CasCaptureFilesystemBackend`
4
+ * ({@link ../execute-deep-agent/cas-capture-backend.js}).
5
+ *
6
+ * Deep-agent runs the agent in-process, so it records a gitignored file's
7
+ * pre-turn bytes in an in-memory `CasBeforeMap` at the mutation point (before
8
+ * `super.write`). Cursor runs the agent as an EXTERNAL CLI subprocess whose only
9
+ * pre-write interception is the generated preToolUse bash hook — and each hook
10
+ * invocation is a FRESH, EPHEMERAL process that cannot hold in-memory state
11
+ * across writes. So Cursor's before-map must be DISK-BACKED: the hook stages each
12
+ * first-touched gitignored path into a per-turn "cas-observations" sidecar, and
13
+ * the runner reads it back at the turn boundary.
14
+ *
15
+ * The sidecar lives under the session HITL directory
16
+ * (`~/.stigmer/sessions/{id}/hitl/cas-observations/`), OUTSIDE the user's
17
+ * workspace — so staged before-bytes and secret markers never land in the repo,
18
+ * cannot pollute the captured git diff, and are never pushed. It is reset every
19
+ * turn (alongside the denial ledger) and consumed within the same activity
20
+ * invocation that produced it; resume reconciles from the durable CAS manifest,
21
+ * never this scratch. A Temporal retry re-runs the reset and re-observes.
22
+ *
23
+ * ONE MODULE OWNS THE ON-DISK FORMAT. Both sides live here so they cannot drift:
24
+ * - the READER ({@link readCasObservations}) the runner boundary calls;
25
+ * - the WRITER-SCRIPT GENERATOR ({@link buildObservationStagingScript}) whose
26
+ * output the hook embeds and runs on the runner's own Node binary. The writer
27
+ * classifies secrets from the SAME {@link isSecretLikePath} pattern arrays the
28
+ * boundary backstop uses (`shared/filereview/secret-paths.ts`), reconstructed
29
+ * as byte-identical `RegExp`s — no bash-regex, no divergence.
30
+ *
31
+ * Each observed path becomes one entry:
32
+ * - `captured` — a non-secret gitignored write/edit: `{path, kind, existed}`
33
+ * metadata plus a `.blob` of the pre-turn bytes (omitted for an ADD, where the
34
+ * file did not exist). First-touch-wins via atomic exclusive-create, so
35
+ * repeated edits to one path keep the true pre-turn before.
36
+ * - `secret` — a secret-like gitignored path the hook hard-blocked (denied,
37
+ * nothing written): `{path, kind}` only. The boundary authors it as a
38
+ * content-less `DIFF_UNREVIEWABLE` change; its bytes never reach storage.
39
+ *
40
+ * @since File-Change HITL Redesign (Cursor CAS parity — DD-18)
41
+ */
42
+ import { readFile, readdir, rm, mkdir } from "node:fs/promises";
43
+ import { join } from "node:path";
44
+ import { SECRET_BASENAME_PATTERNS, SECRET_PATH_PATTERNS } from "../../shared/filereview/secret-paths.js";
45
+ /** Sidecar directory name under the session HITL dir. */
46
+ export const CAS_OBSERVATIONS_DIRNAME = "cas-observations";
47
+ /** Suffix of the per-path metadata file (also the atomic first-touch marker). */
48
+ const META_SUFFIX = ".meta.json";
49
+ /** Suffix of the per-path pre-turn bytes blob (present only for a MODIFY). */
50
+ const BLOB_SUFFIX = ".blob";
51
+ /** Absolute path of the per-turn cas-observations sidecar dir. */
52
+ export function casObservationsDir(hitlDir) {
53
+ return join(hitlDir, CAS_OBSERVATIONS_DIRNAME);
54
+ }
55
+ /**
56
+ * Truncate the cas-observations sidecar to empty for a fresh turn, returning its
57
+ * path. Called every turn alongside {@link resetDenialLedger} so the boundary
58
+ * only ever reads observations produced by the current run — deterministic across
59
+ * HITL reinvocations and Temporal activity retries.
60
+ */
61
+ export async function resetCasObservations(hitlDir) {
62
+ const dir = casObservationsDir(hitlDir);
63
+ await rm(dir, { recursive: true, force: true });
64
+ await mkdir(dir, { recursive: true });
65
+ return dir;
66
+ }
67
+ /**
68
+ * Read the cas-observations sidecar the hook wrote during the turn. Missing dir →
69
+ * no observations. Malformed/partial entries are tolerated and skipped (the hook
70
+ * writes the blob before the metadata marker, so a present marker implies a
71
+ * complete blob; a marker whose blob is unexpectedly missing is skipped rather
72
+ * than mis-captured). Entries are returned in a deterministic (sorted) order.
73
+ */
74
+ export async function readCasObservations(hitlDir) {
75
+ const dir = casObservationsDir(hitlDir);
76
+ let names;
77
+ try {
78
+ names = await readdir(dir);
79
+ }
80
+ catch {
81
+ return { captured: [], secretPaths: [] };
82
+ }
83
+ const captured = [];
84
+ const secretPaths = [];
85
+ for (const name of names.sort()) {
86
+ if (!name.endsWith(META_SUFFIX))
87
+ continue;
88
+ let meta;
89
+ try {
90
+ meta = JSON.parse(await readFile(join(dir, name), "utf-8"));
91
+ }
92
+ catch {
93
+ continue; // partial/garbled marker — tolerate
94
+ }
95
+ if (!meta || typeof meta.path !== "string" || !meta.path)
96
+ continue;
97
+ if (meta.kind === "secret") {
98
+ secretPaths.push(meta.path);
99
+ continue;
100
+ }
101
+ if (meta.kind === "captured") {
102
+ let before = null;
103
+ if (meta.existed === true) {
104
+ const blobPath = join(dir, name.slice(0, -META_SUFFIX.length) + BLOB_SUFFIX);
105
+ try {
106
+ before = await readFile(blobPath);
107
+ }
108
+ catch {
109
+ continue; // marker present but blob missing (mid-hook crash) — skip
110
+ }
111
+ }
112
+ captured.push({ path: meta.path, before });
113
+ }
114
+ }
115
+ return { captured, secretPaths };
116
+ }
117
+ /**
118
+ * Emit a JS `RegExp` constructor byte-identical to a source pattern, safe to
119
+ * embed inside a single-quoted bash string (JSON.stringify emits double quotes;
120
+ * these patterns contain no single quotes).
121
+ */
122
+ function regexLiteral(re) {
123
+ return `new RegExp(${JSON.stringify(re.source)},${JSON.stringify(re.flags)})`;
124
+ }
125
+ /**
126
+ * Build the standalone Node.js script the hook runs to observe a single
127
+ * gitignored write — the disk-backed mirror of `CasCaptureFilesystemBackend`'s
128
+ * `recordBefore` plus the DD-E secret gate.
129
+ *
130
+ * Invoked as `printf %s "$SALIENT" | node -e '<this>' <workspaceRoot> <obsDir>`
131
+ * — the (arbitrary) salient path arrives on stdin so no argv escaping is needed;
132
+ * the two absolute paths ride argv. Prints one token on stdout for the hook:
133
+ * - `captured` — non-secret: staged the pre-turn bytes (first-touch-wins), the
134
+ * hook then ALLOWS the write to flow;
135
+ * - `secret` — secret-like: recorded a content-less marker, the hook then
136
+ * DENIES with the security message (nothing is written);
137
+ * - `error` — the path escaped the workspace or a filesystem error occurred,
138
+ * the hook then fails closed (deny).
139
+ *
140
+ * The secret classifier is generated from the SAME
141
+ * {@link SECRET_BASENAME_PATTERNS}/{@link SECRET_PATH_PATTERNS} the boundary
142
+ * backstop uses, so the hook-side and boundary-side classifications agree by
143
+ * construction (a table test locks the equivalence). Authored as a
144
+ * single-quoted-bash string, so the JS must contain no single quotes.
145
+ */
146
+ export function buildObservationStagingScript() {
147
+ const base = SECRET_BASENAME_PATTERNS.map(regexLiteral).join(",");
148
+ const pathPatterns = SECRET_PATH_PATTERNS.map(regexLiteral).join(",");
149
+ const metaSuffix = JSON.stringify(META_SUFFIX);
150
+ const blobSuffix = JSON.stringify(BLOB_SUFFIX);
151
+ return [
152
+ `const fs=require("fs");const path=require("path");const crypto=require("crypto");`,
153
+ `const wsRoot=process.argv[1]||"";const obsDir=process.argv[2]||"";`,
154
+ `let salient="";try{salient=fs.readFileSync(0,"utf8");}catch(e){salient="";}`,
155
+ `if(!salient||!obsDir){process.stdout.write("error");process.exit(0);}`,
156
+ // Workspace-relative key, git-consistent: an absolute salient stays put, a
157
+ // relative one resolves against the workspace root. A path that escapes the
158
+ // workspace is treated as an error (fail closed) — it should never be flagged
159
+ // gitignored by `git check-ignore -C <root>` in the first place.
160
+ `const abs=path.resolve(wsRoot,salient);`,
161
+ `let rel=path.relative(wsRoot,abs).split(path.sep).join("/");`,
162
+ `if(!rel||rel===".."||rel.startsWith("../")){process.stdout.write("error");process.exit(0);}`,
163
+ // --- secret classifier: byte-identical mirror of isSecretLikePath ---
164
+ `const baseName=(p)=>{const n=p.replace(/\\\\/g,"/");const i=n.lastIndexOf("/");return i>=0?n.slice(i+1):n;};`,
165
+ `const BASE=[${base}];const PATHP=[${pathPatterns}];`,
166
+ `const isSecret=(p)=>{if(!p)return true;const nm=p.replace(/\\\\/g,"/");const b=baseName(nm).toLowerCase();for(const r of BASE){if(r.test(b))return true;}const lp=nm.toLowerCase();for(const r of PATHP){if(r.test(lp))return true;}return false;};`,
167
+ `const key=crypto.createHash("sha256").update(rel,"utf8").digest("hex");`,
168
+ `const metaPath=path.join(obsDir,key+${metaSuffix});const blobPath=path.join(obsDir,key+${blobSuffix});`,
169
+ `try{fs.mkdirSync(obsDir,{recursive:true});}catch(e){}`,
170
+ // Secret: record a content-less marker (idempotent) and signal a hard-block.
171
+ `if(isSecret(rel)){try{fs.writeFileSync(metaPath,JSON.stringify({path:rel,kind:"secret"}),{flag:"wx"});}catch(e){}process.stdout.write("secret");process.exit(0);}`,
172
+ // Non-secret: first-touch-wins. A prior marker means this path is already
173
+ // observed (a later edit) — leave the true pre-turn before intact.
174
+ `if(fs.existsSync(metaPath)){process.stdout.write("captured");process.exit(0);}`,
175
+ // Read the pre-turn bytes NOW (the write has not applied — this is preToolUse
176
+ // and the first touch). Missing file = an ADD (before is null).
177
+ `let existed=false;let before=null;try{before=fs.readFileSync(abs);existed=true;}catch(e){existed=false;}`,
178
+ // Write the blob BEFORE the marker, so a present marker always implies a
179
+ // complete blob. The wx marker is the atomic commit + first-touch lock.
180
+ `try{if(existed){fs.writeFileSync(blobPath,before);}fs.writeFileSync(metaPath,JSON.stringify({path:rel,kind:"captured",existed:existed}),{flag:"wx"});}catch(e){if(e&&e.code==="EEXIST"){process.stdout.write("captured");process.exit(0);}process.stdout.write("error");process.exit(0);}`,
181
+ `process.stdout.write("captured");`,
182
+ ].join("");
183
+ }
184
+ //# sourceMappingURL=cas-observations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cas-observations.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/cas-observations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
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
+ import type { AgentMessage } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
28
+ import type { TurnCommandProvenance } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
29
+ export interface CommandProvenanceInputs {
30
+ /**
31
+ * The full transcript at capture time. Turn scoping is positional: messages
32
+ * from index {@link turnStartIndex} onward were produced by THIS turn's
33
+ * stream; everything before was seeded from prior turns. Lease consent rows
34
+ * (APPROVE_ALL) are looked up across the WHOLE transcript — a lease is
35
+ * run-lifetime, its granting row usually lives in a prior turn.
36
+ */
37
+ readonly messages: readonly AgentMessage[];
38
+ /** Index of the first message created by this turn's stream. */
39
+ readonly turnStartIndex: number;
40
+ /**
41
+ * Identity tokens the hook denied THIS turn (the denial ledger). A denied
42
+ * shell row never executed — it is this turn's pending gate, not a mutation
43
+ * source — so it neither needs consent nor disqualifies.
44
+ */
45
+ readonly deniedTokens: ReadonlySet<string>;
46
+ /**
47
+ * Grant token → the tool-call id of the approval row that minted the grant
48
+ * (the row carrying the server-authored approval_action). An executed shell
49
+ * command whose identity token is a key here was authorized by that
50
+ * per-command approval.
51
+ */
52
+ readonly grantTokenToConsentId: ReadonlyMap<string, string>;
53
+ /** True when the pre-armed spec.auto_approve_all bypassed the gate. */
54
+ readonly globalBypass: boolean;
55
+ }
56
+ /**
57
+ * Derive the {@link TurnCommandProvenance} for the turn, or undefined when the
58
+ * turn does not qualify (any non-shell mutation-capable call, any unknown tool,
59
+ * any executed command without a provable consent source, or no executed
60
+ * consented command at all).
61
+ */
62
+ export declare function deriveTurnCommandProvenance(inputs: CommandProvenanceInputs): TurnCommandProvenance | undefined;