@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
@@ -5,14 +5,18 @@
5
5
  * sub-agent tracking, the first-denial early stop — and the loop's side of the adapter contract: progress reported per event
6
6
  * and per delta, usage reported priced, the persist awaited, and the runtime's
7
7
  * stop signal honoured at every event boundary and inside a blocked pull by
8
- * cancelling the SDK run. Since S2 M3 the loop no longer decides WHY it
8
+ * cancelling the SDK run. Since #1070 the loop no longer decides WHY it
9
9
  * stopped (stall, cost cap, pause, platform stop are the runtime's evidence;
10
10
  * `harness/__tests__/run-turn.test.ts` proves that table); it reports
11
11
  * `completed`, `first-denial` or `interrupted`.
12
12
  *
13
13
  * The loop is driven with a structural `mockRun` (an async-iterable `.stream()`)
14
14
  * and the kit's `RecordingTurnSink`, so it runs without the live Cursor SDK or
15
- * Temporal — mirroring the deep-agent `streaming.test.ts` pattern.
15
+ * Temporal — mirroring the deep-agent `streaming.test.ts` pattern. The
16
+ * transcript side is the REAL pair the loop runs in production — the Cursor
17
+ * translator into the shared `TranscriptBuilder` over `sink.status` (#1097;
18
+ * until then a stubbed accumulator counted calls) — so what these arms read
19
+ * back are rows, not mock invocations.
16
20
  */
17
21
 
18
22
  import { describe, it, expect, vi, afterEach } from "vitest";
@@ -20,7 +24,11 @@ import { mkdtemp, writeFile, rm } from "node:fs/promises";
20
24
  import { tmpdir } from "node:os";
21
25
  import { join } from "node:path";
22
26
  import type { SDKMessage } from "@cursor/sdk";
27
+ import { ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
23
28
  import { RecordingTurnSink } from "../../../__test-utils__/harness-contract/recording-sink.js";
29
+ import type { TranscriptBuilder } from "../../../harness/transcript/builder.js";
30
+ import { sdkEvents } from "../__test-utils__/scripted-agent.js";
31
+ import { CursorTranslator } from "../translator.js";
24
32
  import {
25
33
  consumeCursorTurnStream,
26
34
  makeCursorTurnOnDelta,
@@ -30,10 +38,11 @@ import {
30
38
  type TurnStreamState,
31
39
  } from "../turn-stream.js";
32
40
 
33
- // A stream event is only inspected by the loop for `type`/`name`/`status`/
34
- // `message`; the accumulator (stubbed) owns the rest, so a minimal cast is safe.
35
- function ev(obj: Record<string, unknown>): SDKMessage {
36
- return obj as unknown as SDKMessage;
41
+ /** The SDK's real event shapes, bound to one run, so the translator folds them as production would. */
42
+ const sdk = sdkEvents("agent-test", "run-test");
43
+ /** A `status` event whose `message` is deliberately NOT a string — the oss#299 shape the loop must not crash on. */
44
+ function statusWithStructuredMessage(): SDKMessage {
45
+ return { type: "status", agent_id: "agent-test", run_id: "run-test", status: "ERROR", message: { code: 14 } } as unknown as SDKMessage;
37
46
  }
38
47
 
39
48
  async function* asyncIter(events: SDKMessage[]): AsyncIterable<SDKMessage> {
@@ -52,27 +61,8 @@ function mockRun(events: SDKMessage[]): MockRun {
52
61
  };
53
62
  }
54
63
 
55
- function stubAccumulator(overrides: Record<string, unknown> = {}) {
56
- return {
57
- processEvent: vi.fn(),
58
- trackSubAgentExecution: vi.fn(),
59
- finalize: vi.fn(),
60
- markPersisted: vi.fn(),
61
- cancelInProgressSubAgents: vi.fn(),
62
- isDirty: true,
63
- subAgentExecutions: [],
64
- ...overrides,
65
- };
66
- }
67
-
68
- function stubEnricher() {
69
- return {
70
- processDelta: vi.fn(),
71
- applyEnrichments: vi.fn(),
72
- finalize: vi.fn(),
73
- markPersisted: vi.fn(),
74
- isDirty: false,
75
- };
64
+ function translatorFor(sink: RecordingTurnSink): CursorTranslator {
65
+ return new CursorTranslator({ policies: new Map(), leases: { global: false, categories: new Set() }, seeded: sink.status.messages });
76
66
  }
77
67
 
78
68
  /** Prices nothing: hands the counts back as a delta with a stated cost, so the sink sees a priced delta. */
@@ -84,71 +74,104 @@ interface BuiltDeps {
84
74
  deps: CursorTurnStreamDeps;
85
75
  state: TurnStreamState;
86
76
  sink: RecordingTurnSink;
87
- accumulator: ReturnType<typeof stubAccumulator>;
77
+ transcript: TranscriptBuilder;
88
78
  }
89
79
 
90
80
  function buildDeps(overrides: Partial<CursorTurnStreamDeps> = {}): BuiltDeps {
91
81
  const state = overrides.state ?? newTurnStreamState();
92
82
  const sink = (overrides.sink as RecordingTurnSink | undefined) ?? new RecordingTurnSink();
93
- const accumulator = (overrides.accumulator as unknown as ReturnType<typeof stubAccumulator>) ?? stubAccumulator();
83
+ // The builder is the sink's, as the runtime hands it (#1097).
84
+ const transcript = sink.transcript;
94
85
 
95
86
  const deps = {
96
87
  // TurnOnDeltaDeps
97
88
  sink,
98
89
  usagePricer: stubUsagePricer(),
99
- deltaEnricher: stubEnricher(),
90
+ translator: translatorFor(sink),
91
+ eventRecorder: undefined,
100
92
  promptEstimatedTokens: 100,
101
93
  executionId: "exec-test",
102
94
  state,
103
95
  // CursorTurnStreamDeps
104
- accumulator,
105
- todoTracker: { processEvent: vi.fn(), markPersisted: vi.fn(), isDirty: false },
106
- eventRecorder: undefined,
107
- // contentDirty (accumulator.isDirty) forces the persist, so shouldSendUpdate
108
- // is never consulted; markUpdateSent must still exist.
96
+ // A tool row's start forces the persist (the builder's flag), so a text-only
97
+ // stream rides this scheduler, which never says yes; markUpdateSent must exist.
109
98
  scheduler: { shouldSendUpdate: () => false, markUpdateSent: vi.fn() },
110
99
  hitlDir: undefined,
111
100
  ...overrides,
112
101
  } as unknown as CursorTurnStreamDeps;
113
102
 
114
- return { deps, state, sink, accumulator };
103
+ return { deps, state, sink, transcript };
104
+ }
105
+
106
+ /** Every root row the loop folded into the sink's status. */
107
+ function rows(sink: RecordingTurnSink) {
108
+ return sink.status.messages.flatMap((m) => m.toolCalls);
115
109
  }
116
110
 
117
111
  describe("consumeCursorTurnStream", () => {
118
- it("processes every event, persists live through the sink, and returns 'completed' on a natural end", async () => {
119
- const { deps, state, sink, accumulator } = buildDeps();
112
+ it("folds every event into the sink's status through the translator and the builder, persists live, and returns 'completed' on a natural end", async () => {
113
+ const { deps, state, sink } = buildDeps();
120
114
 
121
- const reason = await consumeCursorTurnStream(mockRun([ev({ type: "assistant" }), ev({ type: "assistant" })]), deps);
115
+ const reason = await consumeCursorTurnStream(
116
+ mockRun([sdk.assistant("Reading."), sdk.toolCall("c1", "read", "running", { path: "a" }), sdk.toolCall("c1", "read", "completed", { path: "a" }, "text")]),
117
+ deps,
118
+ );
122
119
 
123
120
  expect(reason).toBe("completed");
124
- expect(accumulator.processEvent).toHaveBeenCalledTimes(2);
121
+ expect(sink.status.messages.map((m) => [m.content, m.toolCalls.map((tc) => tc.id)])).toEqual([["Reading.", ["c1"]]]);
122
+ expect(rows(sink)[0].result).toBe("text");
125
123
  // The #1 retry gap: the bare loop never persisted mid-stream. This proves the
126
- // shared loop persists live — through the runtime's chokepoint.
127
- expect(sink.persistRequests).toBeGreaterThan(0);
128
- expect(state.eventCount).toBe(2);
124
+ // shared loop persists live — through the runtime's chokepoint — on the
125
+ // builder's discrete-change flag (a row's start and its finish).
126
+ expect(sink.persistRequests).toBe(2);
127
+ expect(sink.transcript.dirty, "the flag is cleared after each persist").toBe(false);
128
+ expect(state.eventCount).toBe(3);
129
+ });
130
+
131
+ it("drains the delta channel's queued facts after the stream event, never before it — and the delta's completion instant stands", async () => {
132
+ const { deps, sink } = buildDeps();
133
+ const onDelta = makeCursorTurnOnDelta(deps);
134
+ const withDeltaBetween: StreamableRun = {
135
+ stream: async function* () {
136
+ yield sdk.toolCall("sh-1", "shell", "running", { command: "make" });
137
+ onDelta({ update: { type: "shell-output-delta", event: { callId: "sh-1", type: "stdout", data: "building\n" } } });
138
+ onDelta({ update: { type: "tool-call-completed", callId: "sh-1", modelCallId: "m", toolCall: { type: "shell", args: { command: "make" }, result: { status: "success", value: { exitCode: 0, signal: "", stdout: "building\n", stderr: "", executionTime: 1 } } } } });
139
+ yield sdk.assistant("Built.");
140
+ },
141
+ supports: () => true,
142
+ cancel: async () => {},
143
+ };
144
+
145
+ await consumeCursorTurnStream(withDeltaBetween, deps);
146
+
147
+ const row = rows(sink)[0];
148
+ expect(row.result, "the output reached the row at the next stream event").toBe("building\n");
149
+ expect(row.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
150
+ expect(row.completedAt, "stamped at the delta's observed instant").not.toBe("");
129
151
  });
130
152
 
131
153
  it("reports progress per event, naming the tool of a tool_call event (the stall copy's `last tool`)", async () => {
132
154
  const { deps, sink } = buildDeps();
133
155
 
134
- await consumeCursorTurnStream(mockRun([ev({ type: "assistant" }), ev({ type: "tool_call", name: "shell" })]), deps);
156
+ await consumeCursorTurnStream(mockRun([sdk.assistant("Go."), sdk.toolCall("c1", "shell", "running", { command: "ls" })]), deps);
135
157
 
136
158
  const marks = sink.events.filter((e) => e.kind === "activity");
137
159
  expect(marks.map((m) => (m.kind === "activity" ? m.detail : undefined))).toEqual([undefined, "shell"]);
138
160
  });
139
161
 
140
162
  it("tracks a sub-agent delegation (the 'task' tool call) — dropped by the old bare retry loop", async () => {
141
- const { deps, accumulator } = buildDeps();
163
+ const { deps, sink } = buildDeps();
142
164
 
143
- await consumeCursorTurnStream(mockRun([ev({ type: "tool_call", name: "task" })]), deps);
165
+ await consumeCursorTurnStream(mockRun([sdk.toolCall("t1", "task", "running", { subagentType: "helper", description: "Look", prompt: "Look at it" })]), deps);
144
166
 
145
- expect(accumulator.trackSubAgentExecution).toHaveBeenCalledTimes(1);
167
+ expect(sink.status.subAgentExecutions.map((s) => [s.id, s.name])).toEqual([["t1", "helper"]]);
168
+ expect(rows(sink).map((tc) => tc.id)).toEqual(["t1"]);
146
169
  });
147
170
 
148
171
  it("captures a stream ERROR status message onto state.streamErrorMessage", async () => {
149
172
  const { deps, state } = buildDeps();
150
173
 
151
- await consumeCursorTurnStream(mockRun([ev({ type: "status", status: "ERROR", message: "boom" })]), deps);
174
+ await consumeCursorTurnStream(mockRun([sdk.status("ERROR", "boom")]), deps);
152
175
 
153
176
  expect(state.streamErrorMessage).toBe("boom");
154
177
  });
@@ -158,7 +181,7 @@ describe("consumeCursorTurnStream", () => {
158
181
  // crash classifyText downstream (.toLowerCase() on a non-string).
159
182
  const { deps, state } = buildDeps();
160
183
 
161
- await consumeCursorTurnStream(mockRun([ev({ type: "status", status: "ERROR", message: { code: 14 } })]), deps);
184
+ await consumeCursorTurnStream(mockRun([statusWithStructuredMessage()]), deps);
162
185
 
163
186
  expect(state.streamErrorMessage).toBeUndefined();
164
187
  });
@@ -174,7 +197,7 @@ describe("consumeCursorTurnStream", () => {
174
197
  hitlDir = await mkdtemp(join(tmpdir(), "turn-stream-denial-"));
175
198
  await writeFile(join(hitlDir, "denials.jsonl"), JSON.stringify({ toolName: "shell", token: "tok-1" }) + "\n", "utf-8");
176
199
  const { deps, state } = buildDeps({ hitlDir });
177
- const run = mockRun([ev({ type: "tool_call", name: "shell" })]);
200
+ const run = mockRun([sdk.toolCall("c1", "shell", "running", { command: "rm -rf build" })]);
178
201
 
179
202
  const reason = await consumeCursorTurnStream(run, deps);
180
203
 
@@ -189,7 +212,7 @@ describe("consumeCursorTurnStream", () => {
189
212
  await writeFile(join(hitlDir, "denials.jsonl"), "", "utf-8");
190
213
  const { deps, state } = buildDeps({ hitlDir });
191
214
 
192
- const reason = await consumeCursorTurnStream(mockRun([ev({ type: "tool_call", name: "shell" })]), deps);
215
+ const reason = await consumeCursorTurnStream(mockRun([sdk.toolCall("c1", "shell", "running", { command: "ls" })]), deps);
193
216
 
194
217
  expect(reason).toBe("completed");
195
218
  expect(state.firstDenialDetected).toBe(false);
@@ -200,25 +223,25 @@ describe("consumeCursorTurnStream", () => {
200
223
  it("a signal aborted before the stream starts: the run is cancelled and nothing is processed", async () => {
201
224
  const sink = new RecordingTurnSink();
202
225
  sink.abort("cost-cap");
203
- const { deps, accumulator } = buildDeps({ sink });
204
- const run = mockRun([ev({ type: "assistant" })]);
226
+ const { deps } = buildDeps({ sink });
227
+ const run = mockRun([sdk.assistant("never folded")]);
205
228
 
206
229
  const reason = await consumeCursorTurnStream(run, deps);
207
230
 
208
231
  expect(reason).toBe("interrupted");
209
232
  expect(run.cancel).toHaveBeenCalledTimes(1);
210
- expect(accumulator.processEvent).not.toHaveBeenCalled();
233
+ expect(sink.status.messages).toHaveLength(0);
211
234
  });
212
235
 
213
236
  it("an abort between events: the run is cancelled at once and the next event is never processed", async () => {
214
237
  const sink = new RecordingTurnSink();
215
- const { deps, accumulator } = buildDeps({ sink });
238
+ const { deps } = buildDeps({ sink });
216
239
  // The realistic shape: the runtime aborts (a turn-ended delta crossed the
217
240
  // cap, a STOP came back from a persist) while the loop is between events.
218
241
  async function* gen(): AsyncIterable<SDKMessage> {
219
- yield ev({ type: "assistant" });
242
+ yield sdk.assistant("first");
220
243
  sink.abort("platform-stop");
221
- yield ev({ type: "assistant" });
244
+ yield sdk.assistant(" second");
222
245
  }
223
246
  const run: MockRun = { stream: () => gen(), supports: () => true, cancel: vi.fn(async () => {}) };
224
247
 
@@ -226,8 +249,8 @@ describe("consumeCursorTurnStream", () => {
226
249
 
227
250
  expect(reason).toBe("interrupted");
228
251
  expect(run.cancel).toHaveBeenCalledTimes(1);
229
- // Event 1 was processed; event 2 hit the boundary check.
230
- expect(accumulator.processEvent).toHaveBeenCalledTimes(1);
252
+ // Event 1 was folded; event 2 hit the boundary check.
253
+ expect(sink.status.messages.map((m) => m.content)).toEqual(["first"]);
231
254
  });
232
255
 
233
256
  it("an abort while the pull is blocked (a wedged stream): the cancel is what unblocks it", async () => {
@@ -238,7 +261,7 @@ describe("consumeCursorTurnStream", () => {
238
261
  // watchdog aborts.
239
262
  let unblock: (() => void) | undefined;
240
263
  async function* gen(): AsyncIterable<SDKMessage> {
241
- yield ev({ type: "assistant" });
264
+ yield sdk.assistant("first");
242
265
  await new Promise<void>((resolve) => {
243
266
  unblock = resolve;
244
267
  });
@@ -266,7 +289,7 @@ describe("consumeCursorTurnStream", () => {
266
289
  // The break exits the for-await, which invokes the iterator's return();
267
290
  // a cancelled SDK run can reject there. The catch must exempt a stopped
268
291
  // turn's teardown exactly like the first-denial teardown.
269
- const events = [ev({ type: "assistant" })];
292
+ const events = [sdk.assistant("first")];
270
293
  let i = 0;
271
294
  const run: MockRun = {
272
295
  stream: () => ({
@@ -293,7 +316,7 @@ describe("consumeCursorTurnStream", () => {
293
316
  it("rethrows a genuine stream failure when nothing stopped the turn", async () => {
294
317
  const { deps } = buildDeps();
295
318
  async function* gen(): AsyncIterable<SDKMessage> {
296
- yield ev({ type: "assistant" });
319
+ yield sdk.assistant("first");
297
320
  throw new Error("transport reset");
298
321
  }
299
322
  const run: MockRun = { stream: () => gen(), supports: () => true, cancel: vi.fn(async () => {}) };
@@ -311,7 +334,8 @@ describe("makeCursorTurnOnDelta", () => {
311
334
  const onDelta = makeCursorTurnOnDelta({
312
335
  sink,
313
336
  usagePricer: usagePricer as never,
314
- deltaEnricher: stubEnricher() as never,
337
+ translator: translatorFor(sink),
338
+ eventRecorder: undefined,
315
339
  promptEstimatedTokens: 10,
316
340
  executionId: "e",
317
341
  state,
@@ -5,9 +5,10 @@
5
5
  * module boots, releases, and delegates.
6
6
  *
7
7
  * What this adapter owns: the proxy interceptors and the parked-agent cache
8
- * (worker lifetime), the parked agent per session (session lifetime), and one
9
- * engine turn (`runTurn`, through `turn.ts`). Everything else about a turn is
10
- * the runtime's (`harness/run-turn.ts`).
8
+ * (worker lifetime), the parked agent and the SDK store per session (session
9
+ * lifetime; `agent-session-cache.ts`, `session-store.ts`), and one engine turn
10
+ * (`runTurn`, through `turn.ts`). Everything else about a turn is the
11
+ * runtime's (`harness/run-turn.ts`).
11
12
  *
12
13
  * THIS MODULE'S STATIC GRAPH IS SDK-FREE, ON PURPOSE. The composition roots
13
14
  * import it (through `src/harness-adapters.ts`) BEFORE they boot it, and
@@ -27,7 +28,7 @@
27
28
  * The interceptors read the proxy credential from `Config.proxyTokenRef` per
28
29
  * request; which credential that is (the static root's control-plane token,
29
30
  * the manager's minted runner token) is the root's decision, not this
30
- * module's (Q-S2-7).
31
+ * module's.
31
32
  *
32
33
  * What this adapter never does: throw out of `runTurn` (an SDK failure is
33
34
  * `failed` with the classifier's sentence; an unexpected exception is
@@ -39,14 +40,14 @@
39
40
  *
40
41
  * The turn boundary (`turn-boundary.ts`) runs inside `turn.ts` and reads
41
42
  * this harness's deny-and-retry evidence — the denial ledger — into WAITING
42
- * rows and settled rows. The file-review capture is NOT here since S3 M4:
43
+ * rows and settled rows. The file-review capture is NOT here since #1096:
43
44
  * the runtime pins the baseline before this adapter runs and captures the
44
45
  * whole turn's tree after it returns (`harness/capture.ts`); what this
45
46
  * adapter contributes is the hook sidecar it binds as the runtime's CAS
46
47
  * observations (`turn-setup.ts` `installGate`).
47
48
  *
48
- * Extracted from `index.ts` `createCursorActivities` at S2 M3b; split from
49
- * the turn body at S2 M5. The seventeen hermetic goldens under
49
+ * Extracted from `index.ts` `createCursorActivities` in #1070, which also
50
+ * split it from the turn body. The seventeen hermetic goldens under
50
51
  * `__tests__/hermetic/` pin the result byte for byte.
51
52
  */
52
53
 
@@ -57,6 +58,7 @@ import { closeAllCachedAgents, evictSessionAgent } from "./agent-session-cache.j
57
58
  import { CURSOR_CAPABILITIES } from "./cursor-capabilities.js";
58
59
  import { installFetchInterceptor } from "./fetch-interceptor.js";
59
60
  import { assertHttp2ConnectPatched, installHttp2Interceptor } from "./http2-interceptor.js";
61
+ import { releaseAllSessionStores, releaseSessionStore } from "./session-store.js";
60
62
  import type { CursorAdapterConfig } from "./turn-setup.js";
61
63
 
62
64
  /** The config slice this adapter reads per turn, taken at `boot`; a whole `Config` satisfies it. */
@@ -120,14 +122,16 @@ export function createCursorAdapter(): HarnessAdapter {
120
122
  booted = { config: resolveCursorConfig(bootConfig), runCursorTurn };
121
123
  },
122
124
 
123
- /** Close every parked agent (their executor leases and MCP subprocesses). */
125
+ /** Close every parked agent (their executor leases and MCP subprocesses), then every session's SDK store. */
124
126
  async shutdown(): Promise<void> {
125
127
  closeAllCachedAgents();
128
+ await releaseAllSessionStores();
126
129
  },
127
130
 
128
- /** The session is done on this host: close the agent parked for it, if any (#215). */
131
+ /** The session is done on this host: close the agent parked for it, if any (#215), then its SDK store (#1053). */
129
132
  async releaseSession(sessionId: string): Promise<void> {
130
133
  evictSessionAgent(sessionId);
134
+ await releaseSessionStore(sessionId);
131
135
  },
132
136
 
133
137
  async runTurn(input: TurnInput, sink: TurnSink): Promise<TurnOutcome> {
@@ -29,6 +29,7 @@ export {
29
29
  resolveApprovalMessage,
30
30
  POLICY_ENGINE_VERSION,
31
31
  } from "../../shared/approval-policy.js";
32
+ import { CATEGORY_APPROVAL_MESSAGE } from "../../shared/approval-policy.js";
32
33
  export type { MergedToolPolicy, PolicySource } from "../../shared/approval-policy.js";
33
34
 
34
35
  /**
@@ -72,30 +73,15 @@ export type ApprovalCategory = ToolApprovalCategory;
72
73
  export const approvalCategory = toolApprovalCategory;
73
74
 
74
75
  /**
75
- * Human-readable approval-message template per canonical category. Keyed by
76
- * category (not raw tool name) so a denial surfaced from either taxonomy renders
77
- * the same message. Placeholders resolve against the tool args via
78
- * {@link resolveApprovalMessage}; `{{args.path}}` and `{{args.command}}` are the
79
- * stream-side field names (the runner builds the approval surface from the
80
- * streamed tool call, whose args use `path`/`command`).
76
+ * The salient argument fields — the resource a built-in acts on — are the
77
+ * platform's one list, `shared/args-preview.ts` `SALIENT_ARG_FIELDS` (moved
78
+ * there in #1097: the transcript builder previews every row over it for
79
+ * both harnesses). Re-exported here for this harness's identity code and the
80
+ * generated hook script, which inject it so the runner and the hook never
81
+ * disagree on which field to match.
81
82
  */
82
- const CATEGORY_APPROVAL_MESSAGE: Record<ApprovalCategory, string> = {
83
- write: "Write file: {{args.path}}",
84
- delete: "Delete: {{args.path}}",
85
- shell: "Run command: {{args.command}}",
86
- };
87
-
88
- /**
89
- * Top-level tool-argument fields, in priority order, that identify the specific
90
- * resource a built-in tool acts on. The list deliberately spans BOTH taxonomies'
91
- * arg shapes: the hook input names a file `file_path` and the stream names it
92
- * `path`; both name a shell command `command`. Extracting the same resource
93
- * VALUE on both sides (the absolute path / the command string) is what lets the
94
- * hook-recorded denial token equal the stream-computed token. Authored here once
95
- * and injected into the generated preToolUse hook script so the runner and the
96
- * hook never disagree on which field to match.
97
- */
98
- export const SALIENT_ARG_FIELDS = ["file_path", "path", "target_notebook", "command"] as const;
83
+ export { SALIENT_ARG_FIELDS } from "../../shared/args-preview.js";
84
+ import { SALIENT_ARG_FIELDS } from "../../shared/args-preview.js";
99
85
 
100
86
  /**
101
87
  * Check whether a built-in (non-MCP) Cursor tool requires user approval.
@@ -152,7 +138,9 @@ export function getBuiltInGatedCategories(): Array<[string, ApprovalCategory]> {
152
138
  * Approval-message template for a gated built-in tool (either taxonomy), or
153
139
  * undefined when the tool is not gated. Resolved via {@link approvalCategory}
154
140
  * so stream-side names (`edit`/`shell`/`delete`) and hook-side names
155
- * (`Write`/`Shell`/`Delete`) both map to the same template. Callers resolve the
141
+ * (`Write`/`Shell`/`Delete`) both map to the same template — the platform's
142
+ * ONE table, `shared/approval-policy.ts` `CATEGORY_APPROVAL_MESSAGE`, shared
143
+ * with the native gate since #1097. Callers resolve the
156
144
  * placeholders against the tool args via resolveApprovalMessage.
157
145
  */
158
146
  export function getBuiltInApprovalMessage(toolName: string): string | undefined {
@@ -87,7 +87,7 @@ import { create } from "@bufbuild/protobuf";
87
87
  import { ApprovalAction, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
88
88
  import { PendingApprovalSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
89
89
  import type { PendingApproval } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
90
- import type { AgentMessage } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
90
+ import type { AgentMessage, ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
91
91
  import type { MergedToolPolicy, ApprovalCategory } from "./approval-policy.js";
92
92
  import { extractArgKey, approvalCategory, POLICY_ENGINE_VERSION } from "./approval-policy.js";
93
93
  import { contentDigest } from "../../shared/file-tools.js";
@@ -286,6 +286,71 @@ export function primaryToken(key: string, salient: string, contentDigest: string
286
286
  return contentDigest ? contentToken(key, salient, contentDigest) : grantToken(key, salient);
287
287
  }
288
288
 
289
+ /**
290
+ * Compute a streamed tool call's identity token in the same canonical space the
291
+ * preToolUse hook records denials in (see {@link toolIdentity} / primaryToken).
292
+ * The token keys on the cross-taxonomy category + salient resource PLUS, for a
293
+ * file edit/write, the {@link contentDigest} of the edit content — so a stream
294
+ * `edit` correlates to the hook's `Write` deny for the same path AND content,
295
+ * and an approval of one edit does not match a DIFFERENT edit to the same file.
296
+ *
297
+ * The digest is read from the persisted `approval_content_digest` field when
298
+ * present (a seeded gate carries it, stable even if `args` was elided), and is
299
+ * recomputed from the call's args otherwise (a freshly-streamed call). For a
300
+ * shell/delete/MCP call (no content) it falls back to the coarse token, exactly
301
+ * as before — so those identities are unchanged.
302
+ *
303
+ * Exported so the resume-grant round-trip can be locked against it: the grant a
304
+ * resume mints for an approved tool (buildApprovalGrants -> primaryToken) must
305
+ * equal THIS denial/overlay identity, or the re-issued call is re-gated forever
306
+ * (the dual-path drift the approval-state round-trip suite guards against).
307
+ */
308
+ export function toolCallIdentityToken(tc: ToolCall): string {
309
+ const id = toolIdentity(tc.name, tc.mcpServerSlug, toolCallArgs(tc));
310
+ const digest = tc.approvalContentDigest || contentDigest(toolCallArgs(tc));
311
+ return primaryToken(id.key, id.salient, digest);
312
+ }
313
+
314
+ /**
315
+ * Decode a primary token back into its (key, salient, digest) for the synthesis
316
+ * fallback. The token is `base64(key \n salient)` (coarse) or
317
+ * `base64(key \n salient \n digest)` (content-exact); the digest is the optional
318
+ * third segment. salient never contains a newline (a path or shell command), so
319
+ * splitting on the first two newlines is unambiguous.
320
+ */
321
+ export function decodeIdentityToken(
322
+ token: string,
323
+ ): { key: string; salient: string; digest: string } | undefined {
324
+ try {
325
+ const decoded = Buffer.from(token, "base64").toString("utf-8");
326
+ const first = decoded.indexOf("\n");
327
+ if (first < 0) return undefined;
328
+ const key = decoded.slice(0, first);
329
+ const rest = decoded.slice(first + 1);
330
+ const second = rest.indexOf("\n");
331
+ if (second < 0) return { key, salient: rest, digest: "" };
332
+ return { key, salient: rest.slice(0, second), digest: rest.slice(second + 1) };
333
+ } catch {
334
+ return undefined;
335
+ }
336
+ }
337
+
338
+ /** Best-effort args record for a tool call (proto struct, else parsed preview). */
339
+ export function toolCallArgs(tc: ToolCall): Record<string, unknown> {
340
+ if (tc.args && typeof tc.args === "object") {
341
+ return tc.args as Record<string, unknown>;
342
+ }
343
+ if (tc.argsPreview) {
344
+ try {
345
+ const parsed = JSON.parse(tc.argsPreview);
346
+ if (parsed && typeof parsed === "object") return parsed as Record<string, unknown>;
347
+ } catch {
348
+ // fall through
349
+ }
350
+ }
351
+ return {};
352
+ }
353
+
289
354
  /**
290
355
  * The shared HMAC coarse fingerprint of an approval grant.
291
356
  *