@stigmer/runner 3.15.3-dev.20260914100347 → 3.15.3-dev.20260916211208

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 (549) hide show
  1. package/README.md +1 -0
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/__test-utils__/approval-contract/types.d.ts +1 -1
  4. package/dist/__test-utils__/hermetic-activity.d.ts +61 -7
  5. package/dist/__test-utils__/hermetic-activity.js +100 -9
  6. package/dist/__test-utils__/hermetic-activity.js.map +1 -1
  7. package/dist/__test-utils__/model-registry-fixture.d.ts +22 -3
  8. package/dist/__test-utils__/model-registry-fixture.js +22 -7
  9. package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
  10. package/dist/activities/execute-cursor/__test-utils__/contract-subject.d.ts +2 -2
  11. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +14 -11
  12. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
  13. package/dist/activities/execute-cursor/__test-utils__/fold.d.ts +61 -0
  14. package/dist/activities/execute-cursor/__test-utils__/fold.js +94 -0
  15. package/dist/activities/execute-cursor/__test-utils__/fold.js.map +1 -0
  16. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +1 -1
  17. package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -1
  18. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +52 -14
  19. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +46 -12
  20. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
  21. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +21 -28
  22. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +27 -11
  23. package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -1
  24. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.d.ts +70 -26
  25. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js +82 -32
  26. package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js.map +1 -1
  27. package/dist/activities/execute-cursor/adapter.d.ts +8 -7
  28. package/dist/activities/execute-cursor/adapter.js +13 -9
  29. package/dist/activities/execute-cursor/adapter.js.map +1 -1
  30. package/dist/activities/execute-cursor/approval-policy.d.ts +10 -10
  31. package/dist/activities/execute-cursor/approval-policy.js +12 -23
  32. package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
  33. package/dist/activities/execute-cursor/approval-state.d.ts +35 -1
  34. package/dist/activities/execute-cursor/approval-state.js +65 -0
  35. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  36. package/dist/activities/execute-cursor/{message-translator.d.ts → boundary-rows.d.ts} +45 -341
  37. package/dist/activities/execute-cursor/boundary-rows.js +1018 -0
  38. package/dist/activities/execute-cursor/boundary-rows.js.map +1 -0
  39. package/dist/activities/execute-cursor/cursor-capabilities.d.ts +1 -1
  40. package/dist/activities/execute-cursor/cursor-capabilities.js +1 -1
  41. package/dist/activities/execute-cursor/cursor-event-recorder.d.ts +33 -14
  42. package/dist/activities/execute-cursor/cursor-event-recorder.js +43 -21
  43. package/dist/activities/execute-cursor/cursor-event-recorder.js.map +1 -1
  44. package/dist/activities/execute-cursor/prompt-builder.d.ts +3 -3
  45. package/dist/activities/execute-cursor/prompt-builder.js +4 -4
  46. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  47. package/dist/activities/execute-cursor/sdk-warmup.d.ts +15 -6
  48. package/dist/activities/execute-cursor/sdk-warmup.js +15 -6
  49. package/dist/activities/execute-cursor/sdk-warmup.js.map +1 -1
  50. package/dist/activities/execute-cursor/service-tier.js +1 -2
  51. package/dist/activities/execute-cursor/service-tier.js.map +1 -1
  52. package/dist/activities/execute-cursor/session-lifecycle.d.ts +38 -59
  53. package/dist/activities/execute-cursor/session-lifecycle.js +59 -96
  54. package/dist/activities/execute-cursor/session-lifecycle.js.map +1 -1
  55. package/dist/activities/execute-cursor/session-store.d.ts +100 -0
  56. package/dist/activities/execute-cursor/session-store.js +157 -0
  57. package/dist/activities/execute-cursor/session-store.js.map +1 -0
  58. package/dist/activities/execute-cursor/sub-agent-steps.d.ts +79 -0
  59. package/dist/activities/execute-cursor/sub-agent-steps.js +180 -0
  60. package/dist/activities/execute-cursor/sub-agent-steps.js.map +1 -0
  61. package/dist/activities/execute-cursor/tool-result.d.ts +49 -0
  62. package/dist/activities/execute-cursor/tool-result.js +130 -0
  63. package/dist/activities/execute-cursor/tool-result.js.map +1 -0
  64. package/dist/activities/execute-cursor/translator.d.ts +178 -0
  65. package/dist/activities/execute-cursor/translator.js +485 -0
  66. package/dist/activities/execute-cursor/translator.js.map +1 -0
  67. package/dist/activities/execute-cursor/turn-boundary.d.ts +15 -5
  68. package/dist/activities/execute-cursor/turn-boundary.js +10 -14
  69. package/dist/activities/execute-cursor/turn-boundary.js.map +1 -1
  70. package/dist/activities/execute-cursor/turn-settle.d.ts +11 -7
  71. package/dist/activities/execute-cursor/turn-settle.js +47 -34
  72. package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
  73. package/dist/activities/execute-cursor/turn-setup.d.ts +2 -2
  74. package/dist/activities/execute-cursor/turn-setup.js +5 -5
  75. package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
  76. package/dist/activities/execute-cursor/turn-stream.d.ts +23 -14
  77. package/dist/activities/execute-cursor/turn-stream.js +41 -28
  78. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  79. package/dist/activities/execute-cursor/turn.d.ts +4 -4
  80. package/dist/activities/execute-cursor/turn.js +5 -5
  81. package/dist/activities/execute-cursor/usage-pricing.d.ts +2 -2
  82. package/dist/activities/execute-cursor/usage-pricing.js +2 -2
  83. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +2 -2
  84. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +2 -2
  85. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +16 -11
  86. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +22 -10
  87. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -1
  88. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +1 -1
  89. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -1
  90. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +2 -3
  91. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +2 -3
  92. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -1
  93. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +12 -3
  94. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +18 -4
  95. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
  96. package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.d.ts +10 -1
  97. package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js +24 -11
  98. package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js.map +1 -1
  99. package/dist/activities/execute-deep-agent/adapter.d.ts +1 -1
  100. package/dist/activities/execute-deep-agent/adapter.js +1 -1
  101. package/dist/activities/execute-deep-agent/approval-file-change.d.ts +19 -19
  102. package/dist/activities/execute-deep-agent/approval-file-change.js +16 -15
  103. package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -1
  104. package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +2 -2
  105. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +2 -2
  106. package/dist/activities/execute-deep-agent/hitl.d.ts +2 -2
  107. package/dist/activities/execute-deep-agent/hitl.js +2 -2
  108. package/dist/activities/execute-deep-agent/inline-publisher.d.ts +13 -3
  109. package/dist/activities/execute-deep-agent/inline-publisher.js +3 -3
  110. package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
  111. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +3 -3
  112. package/dist/activities/execute-deep-agent/prompt-builder.js +6 -7
  113. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  114. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +16 -8
  115. package/dist/activities/execute-deep-agent/streaming-side-effects.js +20 -33
  116. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  117. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +3 -3
  118. package/dist/activities/execute-deep-agent/subagent-transformer.js +1 -1
  119. package/dist/activities/execute-deep-agent/translator.d.ts +134 -0
  120. package/dist/activities/execute-deep-agent/translator.js +418 -0
  121. package/dist/activities/execute-deep-agent/translator.js.map +1 -0
  122. package/dist/activities/execute-deep-agent/turn-settle.d.ts +13 -7
  123. package/dist/activities/execute-deep-agent/turn-settle.js +34 -45
  124. package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -1
  125. package/dist/activities/execute-deep-agent/turn-setup.d.ts +8 -8
  126. package/dist/activities/execute-deep-agent/turn-setup.js +7 -7
  127. package/dist/activities/execute-deep-agent/turn-stream.d.ts +13 -10
  128. package/dist/activities/execute-deep-agent/turn-stream.js +44 -41
  129. package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -1
  130. package/dist/activities/execute-deep-agent/turn.d.ts +3 -3
  131. package/dist/activities/execute-deep-agent/turn.js +3 -3
  132. package/dist/config.d.ts +2 -2
  133. package/dist/harness/approval-decisions.d.ts +1 -1
  134. package/dist/harness/approval-decisions.js +1 -1
  135. package/dist/harness/capabilities.d.ts +21 -11
  136. package/dist/harness/capabilities.js +2 -2
  137. package/dist/harness/capture.d.ts +4 -4
  138. package/dist/harness/capture.js +4 -4
  139. package/dist/harness/persist-chokepoint.d.ts +3 -3
  140. package/dist/harness/persist-chokepoint.js +3 -3
  141. package/dist/harness/registry.d.ts +2 -2
  142. package/dist/harness/registry.js +2 -2
  143. package/dist/harness/run-turn.d.ts +6 -4
  144. package/dist/harness/run-turn.js +31 -9
  145. package/dist/harness/run-turn.js.map +1 -1
  146. package/dist/harness/stop-controller.d.ts +1 -1
  147. package/dist/harness/stop-controller.js +1 -1
  148. package/dist/harness/terminal-table.d.ts +7 -7
  149. package/dist/harness/terminal-table.js +7 -7
  150. package/dist/harness/transcript/builder.d.ts +182 -0
  151. package/dist/harness/transcript/builder.js +693 -0
  152. package/dist/harness/transcript/builder.js.map +1 -0
  153. package/dist/harness/transcript/events.d.ts +218 -0
  154. package/dist/harness/transcript/events.js +50 -0
  155. package/dist/harness/transcript/events.js.map +1 -0
  156. package/dist/harness/transcript/state.d.ts +110 -0
  157. package/dist/harness/transcript/state.js +148 -0
  158. package/dist/harness/transcript/state.js.map +1 -0
  159. package/dist/harness/turn-context.d.ts +54 -38
  160. package/dist/harness/turn-context.js +42 -43
  161. package/dist/harness/turn-context.js.map +1 -1
  162. package/dist/harness/types.d.ts +63 -22
  163. package/dist/harness/types.js +12 -11
  164. package/dist/harness/types.js.map +1 -1
  165. package/dist/harness/usage-accumulator.d.ts +2 -2
  166. package/dist/harness/usage-accumulator.js +2 -2
  167. package/dist/harness-adapters.d.ts +5 -4
  168. package/dist/harness-adapters.js +5 -4
  169. package/dist/harness-adapters.js.map +1 -1
  170. package/dist/middleware/approval-gate.d.ts +6 -0
  171. package/dist/middleware/approval-gate.js +7 -57
  172. package/dist/middleware/approval-gate.js.map +1 -1
  173. package/dist/middleware/cost-advisory.d.ts +3 -3
  174. package/dist/middleware/cost-advisory.js +3 -3
  175. package/dist/middleware/index.d.ts +1 -1
  176. package/dist/middleware/index.js +1 -1
  177. package/dist/runner-manager.js +1 -1
  178. package/dist/runner-manager.js.map +1 -1
  179. package/dist/shared/approval-policy.d.ts +53 -0
  180. package/dist/shared/approval-policy.js +76 -0
  181. package/dist/shared/approval-policy.js.map +1 -1
  182. package/dist/shared/args-preview.d.ts +45 -22
  183. package/dist/shared/args-preview.js +51 -41
  184. package/dist/shared/args-preview.js.map +1 -1
  185. package/dist/shared/attachment-resolver.d.ts +4 -4
  186. package/dist/shared/attachment-resolver.js +4 -4
  187. package/dist/shared/attachment-vision.d.ts +2 -1
  188. package/dist/shared/attachment-vision.js.map +1 -1
  189. package/dist/shared/attachment-zip.d.ts +2 -2
  190. package/dist/shared/attachment-zip.js +2 -2
  191. package/dist/shared/cost-guard.d.ts +2 -2
  192. package/dist/shared/cost-guard.js +2 -2
  193. package/dist/shared/exact-apply.d.ts +1 -1
  194. package/dist/shared/exact-apply.js +1 -1
  195. package/dist/shared/execution-context.d.ts +2 -2
  196. package/dist/shared/execution-context.js +2 -2
  197. package/dist/shared/extract-structured-output.d.ts +2 -2
  198. package/dist/shared/extract-structured-output.js +2 -2
  199. package/dist/shared/filereview/cas-touched.d.ts +2 -2
  200. package/dist/shared/filereview/cas-touched.js +2 -2
  201. package/dist/shared/heartbeat.d.ts +1 -1
  202. package/dist/shared/heartbeat.js +1 -1
  203. package/dist/shared/llm-backend.d.ts +0 -4
  204. package/dist/shared/llm-backend.js +0 -4
  205. package/dist/shared/llm-backend.js.map +1 -1
  206. package/dist/shared/model-error.d.ts +1 -2
  207. package/dist/shared/model-error.js +1 -2
  208. package/dist/shared/model-error.js.map +1 -1
  209. package/dist/shared/persist-decision.d.ts +18 -24
  210. package/dist/shared/persist-decision.js +19 -11
  211. package/dist/shared/persist-decision.js.map +1 -1
  212. package/dist/shared/prompt-sections.d.ts +4 -4
  213. package/dist/shared/prompt-sections.js +4 -4
  214. package/dist/shared/skill-resolver.d.ts +3 -3
  215. package/dist/shared/skill-resolver.js +3 -3
  216. package/dist/shared/status-offload.d.ts +1 -1
  217. package/dist/shared/status-offload.js +2 -2
  218. package/dist/shared/status-offload.js.map +1 -1
  219. package/dist/shared/streaming-scheduler.d.ts +2 -7
  220. package/dist/shared/streaming-scheduler.js +6 -14
  221. package/dist/shared/streaming-scheduler.js.map +1 -1
  222. package/dist/shared/subagent-rows.d.ts +2 -2
  223. package/dist/shared/subagent-rows.js +2 -2
  224. package/dist/shared/todos.d.ts +3 -3
  225. package/dist/shared/todos.js +3 -3
  226. package/dist/shared/tool-row.d.ts +19 -1
  227. package/dist/shared/tool-row.js +36 -2
  228. package/dist/shared/tool-row.js.map +1 -1
  229. package/dist/shared/worker-shutdown.d.ts +1 -1
  230. package/dist/shared/worker-shutdown.js +1 -1
  231. package/dist/shared/workspace/session-provision.d.ts +1 -1
  232. package/dist/shared/workspace/session-provision.js +1 -1
  233. package/dist/shared/workspace/sources/empty.d.ts +1 -1
  234. package/dist/shared/workspace/sources/empty.js +1 -2
  235. package/dist/shared/workspace/sources/empty.js.map +1 -1
  236. package/dist/shared/workspace/sources/git.js +4 -4
  237. package/dist/shared/workspace/sources/git.js.map +1 -1
  238. package/dist/shared/workspace/sources/local-path.js +6 -6
  239. package/dist/shared/workspace/sources/local-path.js.map +1 -1
  240. package/dist/shared/workspace/stigmer-link.d.ts +1 -1
  241. package/dist/shared/workspace/stigmer-link.js +1 -1
  242. package/dist/shared/workspace/types.d.ts +2 -5
  243. package/dist/shared/workspace/types.js +0 -6
  244. package/dist/shared/workspace/types.js.map +1 -1
  245. package/dist/shared/workspace/writeback-coordinator.d.ts +17 -4
  246. package/dist/shared/workspace/writeback-coordinator.js +7 -9
  247. package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
  248. package/package.json +5 -5
  249. package/src/__test-utils__/__tests__/execution-record-barrier.test.ts +70 -0
  250. package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +35 -1
  251. package/src/__test-utils__/approval-contract/contract.ts +1 -1
  252. package/src/__test-utils__/approval-contract/types.ts +1 -1
  253. package/src/__test-utils__/harness-boot-order-child.ts +1 -1
  254. package/src/__test-utils__/harness-contract/contract.ts +8 -8
  255. package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
  256. package/src/__test-utils__/harness-contract/runtime-contract.ts +52 -16
  257. package/src/__test-utils__/harness-contract/scripted-adapter.ts +47 -43
  258. package/src/__test-utils__/hermetic-activity.ts +113 -9
  259. package/src/__test-utils__/model-registry-fixture.ts +31 -7
  260. package/src/__test-utils__/module-specifiers.ts +32 -3
  261. package/src/__tests__/harness-boot-order.test.ts +1 -1
  262. package/src/__tests__/harness-contract.test.ts +1 -1
  263. package/src/__tests__/runner-manager.test.ts +1 -1
  264. package/src/activities/execute-cursor/__test-utils__/__tests__/scripted-double.test.ts +32 -8
  265. package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +12 -10
  266. package/src/activities/execute-cursor/__test-utils__/fold.ts +116 -0
  267. package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +1 -1
  268. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +72 -19
  269. package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +35 -29
  270. package/src/activities/execute-cursor/__test-utils__/scripted-sdk.ts +94 -38
  271. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +2 -1
  272. package/src/activities/execute-cursor/__tests__/approval-state.test.ts +14 -21
  273. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +3 -3
  274. package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +2 -1
  275. package/src/activities/execute-cursor/__tests__/cursor-baseurl-routing.test.ts +10 -8
  276. package/src/activities/execute-cursor/__tests__/cursor-event-recorder.test.ts +142 -0
  277. package/src/activities/execute-cursor/__tests__/cursor-fetch-interceptor-bypass.test.ts +24 -12
  278. package/src/activities/execute-cursor/__tests__/cursor-generate-image-live.test.ts +7 -2
  279. package/src/activities/execute-cursor/__tests__/cursor-hook-protocol-live.test.ts +163 -0
  280. package/src/activities/execute-cursor/__tests__/cursor-sdk-auth-smoke.test.ts +23 -20
  281. package/src/activities/execute-cursor/__tests__/cursor-transcript-live.test.ts +298 -0
  282. package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +4 -2
  283. package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +1 -1
  284. package/src/activities/execute-cursor/__tests__/hermetic/deny-and-retry.test.ts +5 -2
  285. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +5 -2
  286. package/src/activities/execute-cursor/__tests__/hermetic/goldens/mcp-tool-call.status.json +71 -0
  287. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-task-note.status.json +73 -0
  288. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.error-delta.status.json +78 -0
  289. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.output-first.status.json +72 -0
  290. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.status.json +72 -0
  291. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sub-agent-delegation.status.json +109 -0
  292. package/src/activities/execute-cursor/__tests__/hermetic/goldens/thinking-turn.status.json +78 -0
  293. package/src/activities/execute-cursor/__tests__/hermetic/goldens/todo-write.status.json +119 -0
  294. package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-error.status.json +68 -0
  295. package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-only-step.status.json +67 -0
  296. package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +1 -1
  297. package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +10 -7
  298. package/src/activities/execute-cursor/__tests__/hermetic/mcp-tool-call.test.ts +146 -0
  299. package/src/activities/execute-cursor/__tests__/hermetic/pause-vs-shutdown.test.ts +8 -5
  300. package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +6 -2
  301. package/src/activities/execute-cursor/__tests__/hermetic/recovery-fresh-agent.test.ts +4 -1
  302. package/src/activities/execute-cursor/__tests__/hermetic/run-wait-arms.test.ts +6 -2
  303. package/src/activities/execute-cursor/__tests__/hermetic/sdk-task-note.test.ts +122 -0
  304. package/src/activities/execute-cursor/__tests__/hermetic/shell-output-delta.test.ts +276 -0
  305. package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +12 -9
  306. package/src/activities/execute-cursor/__tests__/hermetic/sub-agent-delegation.test.ts +196 -0
  307. package/src/activities/execute-cursor/__tests__/hermetic/thinking-turn.test.ts +145 -0
  308. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +4 -1
  309. package/src/activities/execute-cursor/__tests__/hermetic/todo-write.test.ts +165 -0
  310. package/src/activities/execute-cursor/__tests__/hermetic/tool-call.test.ts +7 -4
  311. package/src/activities/execute-cursor/__tests__/hermetic/tool-error.test.ts +138 -0
  312. package/src/activities/execute-cursor/__tests__/hermetic/tool-only-step.test.ts +130 -0
  313. package/src/activities/execute-cursor/__tests__/hermetic/unattributed-hook-block.test.ts +17 -2
  314. package/src/activities/execute-cursor/__tests__/hermetic/workspace-lock-timeout.test.ts +5 -2
  315. package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +43 -60
  316. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +23 -28
  317. package/src/activities/execute-cursor/__tests__/hook-script.test.ts +2 -1
  318. package/src/activities/execute-cursor/__tests__/http2-interceptor.test.ts +1 -1
  319. package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +5 -5
  320. package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +11 -13
  321. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +8 -11
  322. package/src/activities/execute-cursor/__tests__/session-lifecycle.test.ts +86 -58
  323. package/src/activities/execute-cursor/__tests__/session-store.test.ts +220 -0
  324. package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +8 -22
  325. package/src/activities/execute-cursor/__tests__/translator.test.ts +594 -0
  326. package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +8 -5
  327. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +87 -63
  328. package/src/activities/execute-cursor/adapter.ts +13 -9
  329. package/src/activities/execute-cursor/approval-policy.ts +12 -24
  330. package/src/activities/execute-cursor/approval-state.ts +66 -1
  331. package/src/activities/execute-cursor/boundary-rows.ts +1132 -0
  332. package/src/activities/execute-cursor/cursor-capabilities.ts +1 -1
  333. package/src/activities/execute-cursor/cursor-event-recorder.ts +57 -28
  334. package/src/activities/execute-cursor/fetch-interceptor.ts +1 -1
  335. package/src/activities/execute-cursor/http2-interceptor.ts +1 -1
  336. package/src/activities/execute-cursor/prompt-builder.ts +6 -6
  337. package/src/activities/execute-cursor/sdk-warmup.ts +15 -6
  338. package/src/activities/execute-cursor/service-tier.ts +1 -2
  339. package/src/activities/execute-cursor/session-lifecycle.ts +63 -109
  340. package/src/activities/execute-cursor/session-store.ts +187 -0
  341. package/src/activities/execute-cursor/sub-agent-steps.ts +188 -0
  342. package/src/activities/execute-cursor/tool-result.ts +128 -0
  343. package/src/activities/execute-cursor/translator.ts +541 -0
  344. package/src/activities/execute-cursor/turn-boundary.ts +21 -14
  345. package/src/activities/execute-cursor/turn-settle.ts +46 -34
  346. package/src/activities/execute-cursor/turn-setup.ts +5 -5
  347. package/src/activities/execute-cursor/turn-stream.ts +49 -46
  348. package/src/activities/execute-cursor/turn.ts +5 -5
  349. package/src/activities/execute-cursor/usage-pricing.ts +2 -2
  350. package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +2 -2
  351. package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +23 -11
  352. package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +1 -1
  353. package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +2 -3
  354. package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +25 -4
  355. package/src/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.ts +25 -11
  356. package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +1 -1
  357. package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +1 -1
  358. package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +6 -17
  359. package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +25 -8
  360. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +2 -7
  361. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +4 -14
  362. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +1 -5
  363. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +4 -7
  364. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +1 -5
  365. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +2 -9
  366. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +2 -9
  367. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +1 -4
  368. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +1 -4
  369. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +1 -4
  370. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +109 -76
  371. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +1 -5
  372. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +2 -10
  373. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +2 -10
  374. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +1 -5
  375. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +3 -6
  376. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +1 -4
  377. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +1 -4
  378. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/write-todos.status.json +92 -0
  379. package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +3 -3
  380. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +9 -5
  381. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +3 -3
  382. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +3 -3
  383. package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +7 -6
  384. package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +10 -11
  385. package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +4 -4
  386. package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +9 -8
  387. package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +21 -13
  388. package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +2 -2
  389. package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +7 -7
  390. package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +28 -12
  391. package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +21 -21
  392. package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +5 -5
  393. package/src/activities/execute-deep-agent/__tests__/hermetic/write-todos.test.ts +160 -0
  394. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +35 -30
  395. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +3 -4
  396. package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +12 -10
  397. package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +13 -3
  398. package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +26 -18
  399. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +1 -1
  400. package/src/activities/execute-deep-agent/__tests__/{subagent-tracker.test.ts → translator-subagents.test.ts} +76 -57
  401. package/src/activities/execute-deep-agent/__tests__/{v3-status-builder.test.ts → translator-through-builder.test.ts} +213 -299
  402. package/src/activities/execute-deep-agent/__tests__/translator.test.ts +505 -0
  403. package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +11 -29
  404. package/src/activities/execute-deep-agent/adapter.ts +1 -1
  405. package/src/activities/execute-deep-agent/approval-file-change.ts +20 -22
  406. package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +2 -2
  407. package/src/activities/execute-deep-agent/hitl.ts +2 -2
  408. package/src/activities/execute-deep-agent/inline-publisher.ts +16 -5
  409. package/src/activities/execute-deep-agent/prompt-builder.ts +6 -7
  410. package/src/activities/execute-deep-agent/streaming-side-effects.ts +22 -31
  411. package/src/activities/execute-deep-agent/subagent-transformer.ts +3 -3
  412. package/src/activities/execute-deep-agent/translator.ts +463 -0
  413. package/src/activities/execute-deep-agent/turn-settle.ts +35 -43
  414. package/src/activities/execute-deep-agent/turn-setup.ts +8 -8
  415. package/src/activities/execute-deep-agent/turn-stream.ts +53 -51
  416. package/src/activities/execute-deep-agent/turn.ts +3 -3
  417. package/src/config.ts +2 -2
  418. package/src/harness/README.md +335 -0
  419. package/src/harness/__tests__/approval-decisions.test.ts +2 -2
  420. package/src/harness/__tests__/capture.test.ts +3 -3
  421. package/src/harness/__tests__/import-direction.test.ts +3 -2
  422. package/src/harness/__tests__/mount-skills.test.ts +4 -1
  423. package/src/harness/__tests__/readme-index.test.ts +339 -0
  424. package/src/harness/__tests__/run-turn.test.ts +131 -6
  425. package/src/harness/__tests__/transcript-is-engine-free.test.ts +83 -0
  426. package/src/harness/__tests__/transcript-writers.test.ts +191 -0
  427. package/src/harness/__tests__/turn-attachments-vision.test.ts +5 -2
  428. package/src/harness/__tests__/turn-context.test.ts +5 -5
  429. package/src/harness/approval-decisions.ts +1 -1
  430. package/src/harness/capabilities.ts +21 -11
  431. package/src/harness/capture.ts +4 -4
  432. package/src/harness/persist-chokepoint.ts +3 -3
  433. package/src/harness/registry.ts +2 -2
  434. package/src/harness/run-turn.ts +33 -9
  435. package/src/harness/stop-controller.ts +1 -1
  436. package/src/harness/terminal-table.ts +7 -7
  437. package/src/harness/transcript/__tests__/builder.test.ts +975 -0
  438. package/src/harness/transcript/__tests__/state.test.ts +143 -0
  439. package/src/harness/transcript/builder.ts +755 -0
  440. package/src/harness/transcript/events.ts +262 -0
  441. package/src/harness/transcript/state.ts +167 -0
  442. package/src/harness/turn-context.ts +61 -46
  443. package/src/harness/types.ts +64 -22
  444. package/src/harness/usage-accumulator.ts +2 -2
  445. package/src/harness-adapters.ts +5 -4
  446. package/src/middleware/__tests__/cost-advisory.test.ts +2 -2
  447. package/src/middleware/approval-gate.ts +7 -74
  448. package/src/middleware/cost-advisory.ts +3 -3
  449. package/src/middleware/index.ts +1 -1
  450. package/src/runner-manager.ts +1 -1
  451. package/src/shared/__tests__/approval-policy.test.ts +45 -0
  452. package/src/shared/__tests__/args-preview.test.ts +40 -20
  453. package/src/shared/__tests__/attachment-naming.test.ts +1 -1
  454. package/src/shared/__tests__/attachment-resolver.test.ts +5 -5
  455. package/src/shared/__tests__/attachment-zip.test.ts +1 -1
  456. package/src/shared/__tests__/blueprint-resolver.test.ts +1 -1
  457. package/src/shared/__tests__/cost-guard.test.ts +1 -1
  458. package/src/shared/__tests__/exact-apply.test.ts +2 -1
  459. package/src/shared/__tests__/persist-decision.test.ts +19 -33
  460. package/src/shared/__tests__/prompt-sections.test.ts +1 -1
  461. package/src/shared/__tests__/stamp-flowed-rows.test.ts +1 -1
  462. package/src/shared/__tests__/status-offload.test.ts +1 -1
  463. package/src/shared/__tests__/streaming-scheduler.test.ts +8 -9
  464. package/src/shared/__tests__/todos.test.ts +1 -1
  465. package/src/shared/__tests__/tool-row.test.ts +1 -1
  466. package/src/shared/approval-policy.ts +95 -0
  467. package/src/shared/args-preview.ts +51 -42
  468. package/src/shared/attachment-resolver.ts +4 -4
  469. package/src/shared/attachment-vision.ts +2 -1
  470. package/src/shared/attachment-zip.ts +2 -2
  471. package/src/shared/cost-guard.ts +2 -2
  472. package/src/shared/exact-apply.ts +1 -1
  473. package/src/shared/execution-context.ts +2 -2
  474. package/src/shared/extract-structured-output.ts +2 -2
  475. package/src/shared/filereview/__tests__/capture.test.ts +9 -5
  476. package/src/shared/filereview/__tests__/git-substrate.test.ts +4 -1
  477. package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +3 -3
  478. package/src/shared/filereview/cas-touched.ts +2 -2
  479. package/src/shared/heartbeat.ts +1 -1
  480. package/src/shared/llm-backend.ts +0 -4
  481. package/src/shared/model-error.ts +1 -2
  482. package/src/shared/persist-decision.ts +19 -28
  483. package/src/shared/prompt-sections.ts +4 -4
  484. package/src/shared/skill-resolver.ts +3 -3
  485. package/src/shared/status-offload.ts +2 -2
  486. package/src/shared/streaming-scheduler.ts +8 -13
  487. package/src/shared/subagent-rows.ts +2 -2
  488. package/src/shared/todos.ts +3 -3
  489. package/src/shared/tool-row.ts +38 -2
  490. package/src/shared/worker-shutdown.ts +1 -1
  491. package/src/shared/workspace/__tests__/git-source.test.ts +3 -3
  492. package/src/shared/workspace/__tests__/provisioner.test.ts +5 -5
  493. package/src/shared/workspace/__tests__/session-provision.test.ts +2 -2
  494. package/src/shared/workspace/__tests__/writeback-coordinator.integration.test.ts +8 -9
  495. package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +25 -25
  496. package/src/shared/workspace/provisioner.ts +1 -1
  497. package/src/shared/workspace/session-provision.ts +1 -1
  498. package/src/shared/workspace/sources/empty.ts +2 -2
  499. package/src/shared/workspace/sources/git.ts +3 -4
  500. package/src/shared/workspace/sources/local-path.ts +5 -6
  501. package/src/shared/workspace/stigmer-link.ts +1 -1
  502. package/src/shared/workspace/types.ts +2 -5
  503. package/src/shared/workspace/writeback-coordinator.ts +23 -12
  504. package/dist/activities/execute-cursor/delta-enricher.d.ts +0 -87
  505. package/dist/activities/execute-cursor/delta-enricher.js +0 -265
  506. package/dist/activities/execute-cursor/delta-enricher.js.map +0 -1
  507. package/dist/activities/execute-cursor/message-translator.js +0 -2040
  508. package/dist/activities/execute-cursor/message-translator.js.map +0 -1
  509. package/dist/activities/execute-cursor/todo-tracker.d.ts +0 -40
  510. package/dist/activities/execute-cursor/todo-tracker.js +0 -80
  511. package/dist/activities/execute-cursor/todo-tracker.js.map +0 -1
  512. package/dist/activities/execute-deep-agent/execution-state.d.ts +0 -54
  513. package/dist/activities/execute-deep-agent/execution-state.js +0 -68
  514. package/dist/activities/execute-deep-agent/execution-state.js.map +0 -1
  515. package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +0 -65
  516. package/dist/activities/execute-deep-agent/status-builder-shared.js +0 -90
  517. package/dist/activities/execute-deep-agent/status-builder-shared.js.map +0 -1
  518. package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +0 -113
  519. package/dist/activities/execute-deep-agent/subagent-tracker.js +0 -400
  520. package/dist/activities/execute-deep-agent/subagent-tracker.js.map +0 -1
  521. package/dist/activities/execute-deep-agent/v3-events.d.ts +0 -102
  522. package/dist/activities/execute-deep-agent/v3-events.js +0 -35
  523. package/dist/activities/execute-deep-agent/v3-events.js.map +0 -1
  524. package/dist/activities/execute-deep-agent/v3-protocol-normalizer.d.ts +0 -15
  525. package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js +0 -235
  526. package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js.map +0 -1
  527. package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +0 -111
  528. package/dist/activities/execute-deep-agent/v3-status-builder.js +0 -457
  529. package/dist/activities/execute-deep-agent/v3-status-builder.js.map +0 -1
  530. package/dist/shared/execution-status-writer.d.ts +0 -31
  531. package/dist/shared/execution-status-writer.js +0 -43
  532. package/dist/shared/execution-status-writer.js.map +0 -1
  533. package/src/activities/execute-cursor/__tests__/delta-enricher.test.ts +0 -310
  534. package/src/activities/execute-cursor/__tests__/message-translator.test.ts +0 -1033
  535. package/src/activities/execute-cursor/__tests__/todo-tracker.test.ts +0 -498
  536. package/src/activities/execute-cursor/delta-enricher.ts +0 -307
  537. package/src/activities/execute-cursor/message-translator.ts +0 -2319
  538. package/src/activities/execute-cursor/todo-tracker.ts +0 -91
  539. package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -148
  540. package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -154
  541. package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +0 -47
  542. package/src/activities/execute-deep-agent/__tests__/v3-protocol-normalizer.test.ts +0 -324
  543. package/src/activities/execute-deep-agent/execution-state.ts +0 -78
  544. package/src/activities/execute-deep-agent/status-builder-shared.ts +0 -142
  545. package/src/activities/execute-deep-agent/subagent-tracker.ts +0 -479
  546. package/src/activities/execute-deep-agent/v3-events.ts +0 -153
  547. package/src/activities/execute-deep-agent/v3-protocol-normalizer.ts +0 -264
  548. package/src/activities/execute-deep-agent/v3-status-builder.ts +0 -565
  549. package/src/shared/execution-status-writer.ts +0 -56
@@ -13,6 +13,7 @@ import {
13
13
  resolveApprovalMessage,
14
14
  deriveActiveLeases,
15
15
  resolveApprovalProvenance,
16
+ resolveToolApproval,
16
17
  toProtoPolicySource,
17
18
  type ActiveLeases,
18
19
  type MergedToolPolicy,
@@ -510,6 +511,50 @@ describe("resolveApprovalProvenance", () => {
510
511
  // cases assert the mapping is total and that undefined collapses to UNSPECIFIED.
511
512
  // ---------------------------------------------------------------------------
512
513
 
514
+ describe("resolveToolApproval — THE gate decision, shared by the gate and the translators", () => {
515
+ const NO_CATEGORIES: ReadonlySet<ToolApprovalCategory> = new Set();
516
+ const gated: ReadonlyMap<string, MergedToolPolicy> = new Map([
517
+ ["github/create_issue", { toolName: "create_issue", mcpServerSlug: "github", requiresApproval: true, approvalMessage: "Create issue '{{args.title}}'?", source: "agent_override" }],
518
+ ]);
519
+
520
+ it("an MCP tool with a gating policy waits, with the policy's message resolved and its layer as source", () => {
521
+ expect(resolveToolApproval("create_issue", "github", { title: "Fix crash" }, gated, NO_CATEGORIES)).toEqual({
522
+ requiresApproval: true,
523
+ message: "Create issue 'Fix crash'?",
524
+ source: "agent_override",
525
+ });
526
+ });
527
+
528
+ it("an MCP tool absent from the policy map runs — the classifier cleared it (fail-open), source classifier_default", () => {
529
+ expect(resolveToolApproval("list_issues", "github", {}, gated, NO_CATEGORIES)).toEqual({
530
+ requiresApproval: false,
531
+ message: "",
532
+ source: "classifier_default",
533
+ });
534
+ });
535
+
536
+ it("a mutating built-in waits with its category's message (fail-closed), source builtin_category", () => {
537
+ expect(resolveToolApproval("execute", "", { command: "rm -rf build" }, new Map(), NO_CATEGORIES)).toEqual({
538
+ requiresApproval: true,
539
+ message: "Run command: rm -rf build",
540
+ source: "builtin_category",
541
+ });
542
+ });
543
+
544
+ it("a mutating built-in whose category is leased runs, source approval_lease", () => {
545
+ expect(resolveToolApproval("execute", "", { command: "ls" }, new Map(), new Set<ToolApprovalCategory>(["shell"]))).toEqual({
546
+ requiresApproval: false,
547
+ message: "",
548
+ source: "approval_lease",
549
+ });
550
+ });
551
+
552
+ it("a read-only or unclassified built-in runs (fail-open)", () => {
553
+ expect(resolveToolApproval("read_file", "", { path: "/x" }, new Map(), NO_CATEGORIES).requiresApproval).toBe(false);
554
+ expect(resolveToolApproval("think", "", {}, new Map(), NO_CATEGORIES).requiresApproval).toBe(false);
555
+ });
556
+ });
557
+
513
558
  describe("toProtoPolicySource", () => {
514
559
  it("maps undefined (no governing layer) to UNSPECIFIED", () => {
515
560
  expect(toProtoPolicySource(undefined)).toBe(ApprovalPolicySource.UNSPECIFIED);
@@ -1,38 +1,46 @@
1
1
  /**
2
- * Unit tests for the shared args-preview sanitizers.
2
+ * Unit tests for the shared args-preview builder — the ONE preview rule every
3
+ * tool row carries since #1097.
3
4
  *
4
5
  * The Cursor gate path depends on {@link buildElidedArgsPreview} producing a
5
6
  * SMALL, ALWAYS-VALID, salient-preserving preview: the resumed turn re-parses it
6
7
  * to rebuild an approval grant's identity, and the persist-time size backstop
7
8
  * would replace an oversized preview with an unparseable marker. These pin those
8
9
  * invariants so a large write can never silently break the resume round-trip.
10
+ * (The native harness's whole-string-truncating `sanitizeArgsPreview` and its
11
+ * two arms went with it: a preview that could truncate to invalid JSON has no
12
+ * caller left.)
9
13
  */
10
14
 
11
15
  import { describe, it, expect } from "vitest";
12
- import {
13
- buildElidedArgsPreview,
14
- sanitizeArgsPreview,
15
- MAX_ARGS_PREVIEW_LENGTH,
16
- } from "../args-preview.js";
16
+ import { buildElidedArgsPreview, redactSensitiveArgs, SALIENT_ARG_FIELDS } from "../args-preview.js";
17
17
 
18
- describe("sanitizeArgsPreview (native)", () => {
19
- it("redacts sensitive keys", () => {
20
- const out = JSON.parse(sanitizeArgsPreview({ api_key: "sk-123", path: "a.txt" }));
21
- expect(out.api_key).toBe("[REDACTED]");
22
- expect(out.path).toBe("a.txt");
18
+ describe("SALIENT_ARG_FIELDS", () => {
19
+ it("spans both harnesses' shapes for a file and a shell command, in priority order", () => {
20
+ expect([...SALIENT_ARG_FIELDS]).toEqual(["file_path", "path", "target_notebook", "command"]);
23
21
  });
22
+ });
24
23
 
25
- it("truncates the whole string past the cap", () => {
26
- const preview = sanitizeArgsPreview({ contents: "x".repeat(MAX_ARGS_PREVIEW_LENGTH * 2) });
27
- expect(preview.length).toBeLessThanOrEqual(MAX_ARGS_PREVIEW_LENGTH + 1);
28
- expect(preview.endsWith("…")).toBe(true);
24
+ describe("redactSensitiveArgs", () => {
25
+ it("redacts secret keys case-insensitively and keeps everything else verbatim", () => {
26
+ expect(redactSensitiveArgs({ Api_Key: "sk-123", path: "a.txt", n: 1 })).toEqual({ Api_Key: "[REDACTED]", path: "a.txt", n: 1 });
29
27
  });
30
28
  });
31
29
 
32
- describe("buildElidedArgsPreview (cursor gate)", () => {
33
- const SALIENT = ["file_path", "path", "target_notebook", "command"] as const;
30
+ describe("buildElidedArgsPreview", () => {
31
+ const SALIENT = SALIENT_ARG_FIELDS;
32
+
33
+ it("is exactly JSON.stringify(args) for short, secret-free args (what every native row now carries)", () => {
34
+ expect(buildElidedArgsPreview({ path: "/x", n: 2 }, SALIENT)).toBe(JSON.stringify({ path: "/x", n: 2 }));
35
+ });
34
36
 
35
- it("stays valid JSON and small for a large write, eliding the content value", () => {
37
+ it("yields an empty string for unserializable args instead of throwing", () => {
38
+ const cyclic: Record<string, unknown> = {};
39
+ cyclic.self = cyclic;
40
+ expect(buildElidedArgsPreview(cyclic, SALIENT)).toBe("");
41
+ });
42
+
43
+ it("stays valid JSON and small for a large write, leaving the content value out", () => {
36
44
  const content = "line\n".repeat(100_000); // ~500 KB
37
45
  const preview = buildElidedArgsPreview(
38
46
  { path: "src/big.ts", contents: content },
@@ -43,9 +51,21 @@ describe("buildElidedArgsPreview (cursor gate)", () => {
43
51
  const parsed = JSON.parse(preview) as Record<string, unknown>;
44
52
  // Bounded — nowhere near the raw content size.
45
53
  expect(preview.length).toBeLessThan(1_000);
46
- // The salient path survives verbatim (grant identity), content is elided.
54
+ // The salient path survives verbatim (grant identity); the content is not
55
+ // in the preview at all — never an in-band marker a reader could mistake
56
+ // for the file (stigmer#1107): a surface that needs it reads the row's args.
47
57
  expect(parsed.path).toBe("src/big.ts");
48
- expect(parsed.contents).toBe(`[${content.length} chars]`);
58
+ expect("contents" in parsed).toBe(false);
59
+ });
60
+
61
+ it("a value at the cap is carried whole; one character over is left out", () => {
62
+ const atCap = "a".repeat(200);
63
+ const overCap = "a".repeat(201);
64
+ const parsed = JSON.parse(
65
+ buildElidedArgsPreview({ path: "x", note: atCap, essay: overCap }, SALIENT),
66
+ ) as Record<string, unknown>;
67
+ expect(parsed.note).toBe(atCap);
68
+ expect("essay" in parsed).toBe(false);
49
69
  });
50
70
 
51
71
  it("never elides a salient field, even a very long shell command", () => {
@@ -129,7 +129,7 @@ describe("cross-harness naming parity", () => {
129
129
  makeAttachment("Makefile", "attachments/01D/Makefile"),
130
130
  ];
131
131
 
132
- // One attachment pipeline serves both harnesses since S3 M2b (Q-S3-14);
132
+ // One attachment pipeline serves both harnesses since #1096;
133
133
  // the names it lands are the ones the React SDK's table above predicts.
134
134
  const resolved = await resolveAttachments(attachments, {
135
135
  sessionId,
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Tests for the attachment resolver — the turn runtime's attachment phase,
3
- * the one pipeline for both harnesses since S3 M2b (the native twin,
3
+ * the one pipeline for both harnesses since #1096 (the native twin,
4
4
  * `attachment-injector.ts`, retired then; its ZIP guards moved to
5
- * `attachment-zip.ts` at S3 M1).
5
+ * `attachment-zip.ts` in the same PR).
6
6
  *
7
7
  * The load-bearing behaviors: storage-key attachments materialize under the
8
8
  * platform inputs dir (the universal path — every server-created attachment
9
9
  * carries a storage key), the workspace `.stigmer` symlink exists even when
10
10
  * the agent has no skills, and any attachment that cannot be materialized
11
11
  * fails the resolution loudly (the silent-skip regression behind "plan file
12
- * wasn't found"). Since S3 M1 (Q-S3-14, Q-M1-2) the resolver is the one
12
+ * wasn't found"). Since #1096 the resolver is the one
13
13
  * attachment pipeline for every harness, so it also pins the two behaviors
14
14
  * lifted from the native injector: `extract` archives land under their mount
15
15
  * directory through the shared #567 guards, and an explicit `mountPath` is
@@ -279,7 +279,7 @@ describe("resolveAttachments", () => {
279
279
 
280
280
  // ── Explicit mountPath: honoured inside the inputs namespace, refused outside ──
281
281
 
282
- describe("mountPath (S3 M1, Q-M1-2)", () => {
282
+ describe("mountPath", () => {
283
283
  it("places a file at its explicit `inputs/…` path, in every spelling the producers use", async () => {
284
284
  const { storage } = makeInMemoryArtifactStorage();
285
285
  await storage.upload("k/a", Buffer.from("A"), "text/plain");
@@ -364,7 +364,7 @@ describe("resolveAttachments", () => {
364
364
 
365
365
  // ── Archives: `extract` lands the entries under the mount directory ──────
366
366
 
367
- describe("extract (S3 M1, Q-S3-14)", () => {
367
+ describe("extract", () => {
368
368
  const archive = () =>
369
369
  Buffer.from(
370
370
  buildZip([
@@ -3,7 +3,7 @@
3
3
  * (`shared/attachment-zip.ts`): the manifest checks (paths, traversal, null
4
4
  * bytes, duplicates, methods, emptiness, count and declared-size caps) and
5
5
  * the central-directory parsing that issue #567 made authoritative. Moved
6
- * from the native injector's test file at S3 M1 with the code; every
6
+ * from the native injector's test file in #1096 with the code; every
7
7
  * archive comes from the shared real-shape builder
8
8
  * (@stigmer/zip-structure/testing). The write of an admitted entry is each
9
9
  * caller's and is pinned in that caller's tests (`attachment-resolver.test.ts`).
@@ -4,7 +4,7 @@
4
4
  * (`resolveAgentBlueprint`). Union, deduplicated by slug, the session's ref
5
5
  * winning a collision (it may pin a different version).
6
6
  *
7
- * Carried from `skill-writer.test.ts` at S3 M2b, when the native
7
+ * Carried from `skill-writer.test.ts` in #1096, when the native
8
8
  * orchestrator's byte-twin of this function was deleted with its module;
9
9
  * until then the runtime's copy was tested only through the twin.
10
10
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Unit tests for the max_cost_usd hard-stop guard (cost-guard.ts), which the
3
- * Cursor activity calls today and the turn runtime takes over in S2 M3.
3
+ * Cursor activity called until the turn runtime took it over in #1070.
4
4
  *
5
5
  * The guard is the cursor-side analog of the native cost-cap middleware
6
6
  * (middleware/cost-cap.ts): same proto semantics (0/unset = no cap), same
@@ -1,5 +1,6 @@
1
1
  /**
2
- * @regression file-hitl-phase0 — pins file-edit HITL fix #4 (see _projects/2026-06/20260630.01.file-change-hitl-redesign/tasks/T01_3_regression-manifest.md)
2
+ * @regression file-hitl-phase0 — pins exact-apply: the runner applies the exact
3
+ * approved whole-file bytes, never model-regenerated ones.
3
4
  *
4
5
  * Unit tests for resume-time exact-apply (the HITL "what you approve is what gets
5
6
  * applied" guarantee for the Cursor deny-only harness).
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * Unit tests for the streaming persist decision (issue #179), the shared
3
- * cadence rule the Cursor stream loop calls today and the turn runtime
4
- * takes over in S2 M3.
3
+ * cadence rule both adapters' stream loops read.
5
4
  *
6
- * Validates the converged cadence: discrete force-flush signals persist
5
+ * Validates the converged cadence: a discrete transcript change persists
7
6
  * immediately, while high-frequency token deltas ride the shared
8
7
  * StreamingUpdateScheduler's time cadence (rather than the old, time-blind
9
- * `eventCount % 20` gate that starved short turns).
8
+ * `eventCount % 20` gate that starved short turns). The signal is the one
9
+ * transcript builder's `dirty` flag (since #1097); until then three
10
+ * Cursor-named flags, two of them constant `false` on both loops.
10
11
  */
11
12
 
12
13
  import { describe, it, expect } from "vitest";
@@ -22,65 +23,50 @@ const CONFIG: StreamingConfig = {
22
23
  burstThreshold: 50,
23
24
  };
24
25
 
25
- const NO_FORCE = {
26
- deltaEnricherDirty: false,
27
- todosDirty: false,
28
- contentDirty: false,
29
- } as const;
30
-
31
26
  describe("shouldPersistStreamingStatus", () => {
32
27
  it("persists on the first event via the scheduler (kills the opaque-wait at turn start)", () => {
33
28
  const scheduler = new StreamingUpdateScheduler(CONFIG, 0);
34
- expect(shouldPersistStreamingStatus(NO_FORCE, scheduler, 1, 0)).toBe(true);
29
+ expect(shouldPersistStreamingStatus(false, scheduler, 1, 0)).toBe(true);
35
30
  });
36
31
 
37
- it("does not persist a sub-500ms token delta with no force signal", () => {
32
+ it("does not persist a sub-500ms token delta while the transcript is clean", () => {
38
33
  const scheduler = new StreamingUpdateScheduler(CONFIG, 0);
39
34
  scheduler.markUpdateSent(1, 0); // first flush already sent
40
35
 
41
36
  // 100ms later, one more delta event — too soon for the time cadence.
42
- expect(shouldPersistStreamingStatus(NO_FORCE, scheduler, 2, 100)).toBe(false);
37
+ expect(shouldPersistStreamingStatus(false, scheduler, 2, 100)).toBe(false);
43
38
  });
44
39
 
45
40
  it("persists a token delta once the 500ms cadence elapses", () => {
46
41
  const scheduler = new StreamingUpdateScheduler(CONFIG, 0);
47
42
  scheduler.markUpdateSent(1, 0);
48
43
 
49
- expect(shouldPersistStreamingStatus(NO_FORCE, scheduler, 2, 500)).toBe(true);
44
+ expect(shouldPersistStreamingStatus(false, scheduler, 2, 500)).toBe(true);
50
45
  });
51
46
 
52
- it.each([
53
- ["contentDirty (tool call / sub-agent)", { ...NO_FORCE, contentDirty: true }],
54
- ["todosDirty", { ...NO_FORCE, todosDirty: true }],
55
- ["deltaEnricherDirty (shell output)", { ...NO_FORCE, deltaEnricherDirty: true }],
56
- ])("force-flushes immediately on %s, bypassing the time cadence", (_label, signals) => {
47
+ it("flushes immediately when the transcript is dirty, bypassing the time cadence", () => {
57
48
  const scheduler = new StreamingUpdateScheduler(CONFIG, 0);
58
49
  scheduler.markUpdateSent(1, 0);
59
50
 
60
51
  // Only 1ms after the last flush: the scheduler alone would say no...
61
- expect(shouldPersistStreamingStatus(NO_FORCE, scheduler, 2, 1)).toBe(false);
62
- // ...but any discrete force signal flushes now.
63
- expect(shouldPersistStreamingStatus(signals, scheduler, 2, 1)).toBe(true);
52
+ expect(shouldPersistStreamingStatus(false, scheduler, 2, 1)).toBe(false);
53
+ // ...but a discrete transcript change flushes now.
54
+ expect(shouldPersistStreamingStatus(true, scheduler, 2, 1)).toBe(true);
64
55
  });
65
56
 
66
57
  // The #179 starvation scenario: a short turn of < 20 events. Under the old
67
58
  // `eventCount % 20` gate, a tool call appearing at event 3 (and completing at
68
59
  // event 4) would never be persisted until the unconditional final flush. With
69
- // the converged decision, the tool lifecycle force-flushes the instant it
70
- // happens, regardless of how few events the turn has.
60
+ // the converged decision, the tool lifecycle flushes the instant it happens,
61
+ // regardless of how few events the turn has.
71
62
  it("surfaces a tool call on a short (<20 event) turn", () => {
72
63
  const scheduler = new StreamingUpdateScheduler(CONFIG, 0);
73
64
  const flushes: number[] = [];
74
65
  let clock = 0;
75
66
 
76
67
  // Helper mirroring the loop: decide, then mark both clocks on a flush.
77
- const tick = (eventCount: number, contentDirty: boolean) => {
78
- const persist = shouldPersistStreamingStatus(
79
- { ...NO_FORCE, contentDirty },
80
- scheduler,
81
- eventCount,
82
- clock,
83
- );
68
+ const tick = (eventCount: number, transcriptDirty: boolean) => {
69
+ const persist = shouldPersistStreamingStatus(transcriptDirty, scheduler, eventCount, clock);
84
70
  if (persist) {
85
71
  flushes.push(eventCount);
86
72
  scheduler.markUpdateSent(eventCount, clock);
@@ -90,8 +76,8 @@ describe("shouldPersistStreamingStatus", () => {
90
76
 
91
77
  tick(1, false); // assistant delta — first-event flush
92
78
  tick(2, false); // thinking delta — within 500ms, no flush
93
- tick(3, true); // tool call starts — force-flush
94
- tick(4, true); // tool call completes — force-flush
79
+ tick(3, true); // tool call starts — the transcript is dirty, flush
80
+ tick(4, true); // tool call completes — dirty again, flush
95
81
  tick(5, false); // closing assistant delta — within 500ms, no flush
96
82
 
97
83
  // The tool lifecycle (events 3 and 4) is observed live; event 1 is the
@@ -2,7 +2,7 @@
2
2
  * The shared prompt glue (`shared/prompt-sections.ts`): the standing order,
3
3
  * the input-files lines, the skill selection and the also-available sentence
4
4
  * (the sentence's arms came from `skill-writer.test.ts`, retired with its
5
- * module at S3 M5). What each harness renders AROUND these is pinned whole by
5
+ * module in #1096). What each harness renders AROUND these is pinned whole by
6
6
  * the two builders' prompt goldens (`execute-deep-agent/__tests__/
7
7
  * prompt-goldens.test.ts`, `execute-cursor/__tests__/prompt-goldens.test.ts`);
8
8
  * this file pins the glue's own contract, harness-free.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Unit tests for the shared turn-boundary flowed-row stamping
3
3
  * (`shared/tool-row.ts` `stampFlowedFileEditRows` /
4
- * `stampFlowedSubAgentFileEditRows`; the deep-agent's copy until S3 M4), with
4
+ * `stampFlowedSubAgentFileEditRows`; the deep-agent's copy until #1096), with
5
5
  * emphasis on the sub-agent path added by the DD-24 follow-up: sub-agent edit
6
6
  * rows fold into the parent turn's change set, so they carry the parent change
7
7
  * set id, scoped to this turn by tool-call-id novelty (they lack the
@@ -342,7 +342,7 @@ describe("offloadOversizedToolOutputs", () => {
342
342
  await offloadOversizedToolOutputs(status, ctx);
343
343
  expect(uploads).toHaveLength(1);
344
344
 
345
- // Simulate mergeToolCallEvent re-inflating the inline result with the SAME
345
+ // Simulate the builder's finish upsert re-inflating the inline result with the SAME
346
346
  // bytes on a subsequent persist; the ref's content hash still matches.
347
347
  status.messages[0].toolCalls[0].result = result;
348
348
  await offloadOversizedToolOutputs(status, ctx);
@@ -1,7 +1,6 @@
1
1
  import { describe, it, expect, vi, afterEach } from "vitest";
2
2
  import {
3
3
  StreamingUpdateScheduler,
4
- UpdateReason,
5
4
  loadStreamingConfig,
6
5
  type StreamingConfig,
7
6
  } from "../streaming-scheduler.js";
@@ -17,7 +16,7 @@ describe("StreamingUpdateScheduler", () => {
17
16
  it("always sends on first event", () => {
18
17
  const s = new StreamingUpdateScheduler(BASE_CONFIG, 0);
19
18
  expect(s.shouldSendUpdate(1, 0)).toBe(true);
20
- expect(s.updateReason).toBe(UpdateReason.FIRST_UPDATE);
19
+ expect(s.updateReason).toBe("first_update");
21
20
  });
22
21
 
23
22
  it("does not send if zero events processed", () => {
@@ -32,7 +31,7 @@ describe("StreamingUpdateScheduler", () => {
32
31
  s.markUpdateSent(0, 0);
33
32
 
34
33
  expect(s.shouldSendUpdate(1, 500)).toBe(true);
35
- expect(s.updateReason).toBe(UpdateReason.TIME_THRESHOLD);
34
+ expect(s.updateReason).toBe("time_threshold");
36
35
  });
37
36
 
38
37
  it("does not send before minInterval", () => {
@@ -40,7 +39,7 @@ describe("StreamingUpdateScheduler", () => {
40
39
  s.markUpdateSent(0, 0);
41
40
 
42
41
  expect(s.shouldSendUpdate(1, 499)).toBe(false);
43
- expect(s.updateReason).toBe(UpdateReason.NONE);
42
+ expect(s.updateReason).toBe("none");
44
43
  });
45
44
 
46
45
  it("does not send after minInterval with zero new events", () => {
@@ -57,7 +56,7 @@ describe("StreamingUpdateScheduler", () => {
57
56
  s.markUpdateSent(0, 0);
58
57
 
59
58
  expect(s.shouldSendUpdate(50, 100)).toBe(true);
60
- expect(s.updateReason).toBe(UpdateReason.BURST_PROTECTION);
59
+ expect(s.updateReason).toBe("burst_protection");
61
60
  });
62
61
 
63
62
  it("does not send at threshold - 1", () => {
@@ -74,7 +73,7 @@ describe("StreamingUpdateScheduler", () => {
74
73
  s.markUpdateSent(10, 0);
75
74
 
76
75
  expect(s.shouldSendUpdate(10, 5000)).toBe(true);
77
- expect(s.updateReason).toBe(UpdateReason.KEEPALIVE);
76
+ expect(s.updateReason).toBe("keepalive");
78
77
  });
79
78
 
80
79
  it("does not send keepalive before maxInterval", () => {
@@ -91,7 +90,7 @@ describe("StreamingUpdateScheduler", () => {
91
90
  s.markUpdateSent(10, 1000);
92
91
 
93
92
  expect(s.shouldSendUpdate(11, 1500)).toBe(true);
94
- expect(s.updateReason).toBe(UpdateReason.TIME_THRESHOLD);
93
+ expect(s.updateReason).toBe("time_threshold");
95
94
 
96
95
  s.markUpdateSent(11, 1500);
97
96
 
@@ -125,7 +124,7 @@ describe("StreamingUpdateScheduler", () => {
125
124
  const s = new StreamingUpdateScheduler(BASE_CONFIG, 0);
126
125
  // First check with event and enough time — should be FIRST_UPDATE, not TIME_THRESHOLD
127
126
  expect(s.shouldSendUpdate(1, 600)).toBe(true);
128
- expect(s.updateReason).toBe(UpdateReason.FIRST_UPDATE);
127
+ expect(s.updateReason).toBe("first_update");
129
128
  });
130
129
 
131
130
  it("time_threshold takes priority over burst_protection", () => {
@@ -134,7 +133,7 @@ describe("StreamingUpdateScheduler", () => {
134
133
 
135
134
  // Both time and burst conditions met — time wins
136
135
  expect(s.shouldSendUpdate(50, 500)).toBe(true);
137
- expect(s.updateReason).toBe(UpdateReason.TIME_THRESHOLD);
136
+ expect(s.updateReason).toBe("time_threshold");
138
137
  });
139
138
  });
140
139
  });
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Unit tests for applyTodoUpdate — the harness-agnostic to-do map projection
3
- * shared by the Cursor TodoTracker and the native v2/v3 status builders.
3
+ * the one transcript builder projects a todo write through, for both harnesses.
4
4
  *
5
5
  * These pin the exact mapping contract (status coercion, id synthesis,
6
6
  * created_at preservation, merge vs. full-replace, clear-on-empty, defensive
@@ -382,7 +382,7 @@ describe("collectSettledToolCallIds", () => {
382
382
  // scoping them as this-turn would mis-attribute a dead call to the
383
383
  // resuming turn's change set. (The Cursor monotonic guard makes the
384
384
  // opposite call for the same status so a replayed event can still advance
385
- // the row; see isTerminalToolStatus in message-translator.ts.)
385
+ // the row; see isSettled in harness/transcript/builder.ts.)
386
386
  const ids = collectSettledToolCallIds([
387
387
  msgWith("tc-interrupted", ToolCallStatus.TOOL_CALL_INTERRUPTED),
388
388
  ]);
@@ -466,3 +466,98 @@ export function resolveApprovalMessage(
466
466
  return JSON.stringify(value);
467
467
  });
468
468
  }
469
+
470
+ // ── The gate's decision ─────────────────────────────────────────────
471
+
472
+ /**
473
+ * THE approval-message template per mutating built-in category — the one
474
+ * table both harnesses word their approval cards from (since #1097).
475
+ * Keyed by category (not raw tool name) so every alias of a mutation renders
476
+ * one message; placeholders resolve against the stream arg shape
477
+ * (`path`/`command`; `resolveApprovalMessage`). The native gate decides with
478
+ * it (`resolveToolApproval`), the Cursor translator words its `gate` from it
479
+ * (`execute-cursor/approval-policy.ts` `getBuiltInApprovalMessage`), and the
480
+ * Cursor boundary resolves a denied call's message through the same path.
481
+ *
482
+ * Until then the Cursor harness carried a table of its own and the two differed
483
+ * by a word — `shell` read "Run command" there and "Execute command" here.
484
+ * Unified on the plainer word: a user reads "Run command" and knows
485
+ * what is being asked. Known and deliberately left (#1112):
486
+ * `write` resolves `{{args.path}}`, while deepagents' file tools send
487
+ * `file_path`, so a gated native write reads "Write file: <unknown>".
488
+ */
489
+ export const CATEGORY_APPROVAL_MESSAGE: Record<ToolApprovalCategory, string> = {
490
+ write: "Write file: {{args.path}}",
491
+ delete: "Delete: {{args.path}}",
492
+ shell: "Run command: {{args.command}}",
493
+ };
494
+
495
+ /** What the gate decides for one call: whether it waits, the message the card shows, and which layer said so. */
496
+ export interface ApprovalRequirement {
497
+ readonly requiresApproval: boolean;
498
+ readonly message: string;
499
+ /** Which policy layer determined this verdict — stamped on the shadow receipt. */
500
+ readonly source: PolicySource;
501
+ }
502
+
503
+ /**
504
+ * THE approval decision for one tool call — the same function the native gate
505
+ * (`middleware/approval-gate.ts`) interrupts on and the native translator
506
+ * reports as `tool_started.gate`, so the row can never disagree with the gate
507
+ * about whether a call waits (since #1097; until then the transcript
508
+ * builder carried its own weaker copy that gated MCP tools only). Its
509
+ * read-side twin is {@link resolveApprovalProvenance}, which answers "which
510
+ * layer governs this call" for EVERY call, gated or not.
511
+ *
512
+ * MCP tools stay governed by the connect-flow classifier + four-level policy
513
+ * chain. The policy map carries only the tools that REQUIRE approval, so an
514
+ * absent entry means the classifier already auto-approved it — fail-OPEN is
515
+ * correct here (a fail-closed default would re-gate everything the classifier
516
+ * cleared). A server-scoped lease surfaces as an absent entry too: the leased
517
+ * server's tools are dropped from `policies` upstream (`mergeApprovalPolicies`),
518
+ * so a lease-cleared MCP tool takes the same auto-approve path (its receipt
519
+ * therefore reads classifier_default rather than approval_lease; a faithful
520
+ * per-server lease provenance would need the lease set threaded here and is
521
+ * deferred with the rest of the persisted-receipt work).
522
+ *
523
+ * Built-in/platform tools: gate exactly the mutating categories through the
524
+ * shared classifier — fail-CLOSED for the mutating set (write/edit/delete/shell
525
+ * require approval), fail-open for read-only and unclassified built-ins. A
526
+ * run-lifetime category lease (the user chose "approve all <category>" earlier
527
+ * in this run) auto-approves every built-in of that category for the rest of
528
+ * the run — the scoped successor to auto-approve-all.
529
+ */
530
+ export function resolveToolApproval(
531
+ toolName: string,
532
+ serverSlug: string,
533
+ args: Record<string, unknown>,
534
+ policies: ReadonlyMap<string, MergedToolPolicy>,
535
+ leasedCategories: ReadonlySet<ToolApprovalCategory>,
536
+ ): ApprovalRequirement {
537
+ if (serverSlug) {
538
+ const key = `${serverSlug}/${toolName}`;
539
+ const policy = policies.get(key);
540
+ if (policy) {
541
+ return {
542
+ requiresApproval: policy.requiresApproval,
543
+ message: resolveApprovalMessage(policy.approvalMessage, toolName, args),
544
+ source: policy.source,
545
+ };
546
+ }
547
+ return { requiresApproval: false, message: "", source: "classifier_default" };
548
+ }
549
+
550
+ const category = toolApprovalCategory(toolName);
551
+ if (category) {
552
+ if (leasedCategories.has(category)) {
553
+ return { requiresApproval: false, message: "", source: "approval_lease" };
554
+ }
555
+ return {
556
+ requiresApproval: true,
557
+ message: resolveApprovalMessage(CATEGORY_APPROVAL_MESSAGE[category], toolName, args),
558
+ source: "builtin_category",
559
+ };
560
+ }
561
+
562
+ return { requiresApproval: false, message: "", source: "builtin_category" };
563
+ }