@stigmer/runner 3.15.0 → 3.15.2-dev.20260913223433

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 (518) hide show
  1. package/README.md +2 -4
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/__test-utils__/execution-record-fixture.d.ts +14 -0
  4. package/dist/__test-utils__/execution-record-fixture.js +4 -0
  5. package/dist/__test-utils__/execution-record-fixture.js.map +1 -1
  6. package/dist/__test-utils__/file-review-projection.d.ts +40 -0
  7. package/dist/__test-utils__/file-review-projection.js +136 -0
  8. package/dist/__test-utils__/file-review-projection.js.map +1 -0
  9. package/dist/__test-utils__/harness-contract/types.d.ts +23 -1
  10. package/dist/__test-utils__/harness-contract/types.js +15 -0
  11. package/dist/__test-utils__/harness-contract/types.js.map +1 -1
  12. package/dist/__test-utils__/hermetic-activity.d.ts +44 -12
  13. package/dist/__test-utils__/hermetic-activity.js +52 -14
  14. package/dist/__test-utils__/hermetic-activity.js.map +1 -1
  15. package/dist/__test-utils__/model-registry-fixture.d.ts +32 -7
  16. package/dist/__test-utils__/model-registry-fixture.js +31 -7
  17. package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
  18. package/dist/__test-utils__/turn-input-fixture.d.ts +1 -1
  19. package/dist/__test-utils__/turn-input-fixture.js +1 -1
  20. package/dist/__test-utils__/turn-input-fixture.js.map +1 -1
  21. package/dist/activities/call-agent.js +2 -2
  22. package/dist/activities/call-agent.js.map +1 -1
  23. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +45 -3
  24. package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
  25. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +0 -9
  26. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +2 -41
  27. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
  28. package/dist/activities/execute-cursor/adapter.d.ts +7 -5
  29. package/dist/activities/execute-cursor/adapter.js +7 -5
  30. package/dist/activities/execute-cursor/adapter.js.map +1 -1
  31. package/dist/activities/execute-cursor/approval-policy.d.ts +5 -2
  32. package/dist/activities/execute-cursor/approval-policy.js +5 -2
  33. package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
  34. package/dist/activities/execute-cursor/approval-state.d.ts +16 -8
  35. package/dist/activities/execute-cursor/approval-state.js +6 -3
  36. package/dist/activities/execute-cursor/approval-state.js.map +1 -1
  37. package/dist/activities/execute-cursor/cas-observations.d.ts +14 -2
  38. package/dist/activities/execute-cursor/cas-observations.js +19 -2
  39. package/dist/activities/execute-cursor/cas-observations.js.map +1 -1
  40. package/dist/activities/execute-cursor/cursor-capabilities.d.ts +8 -4
  41. package/dist/activities/execute-cursor/cursor-capabilities.js +21 -4
  42. package/dist/activities/execute-cursor/cursor-capabilities.js.map +1 -1
  43. package/dist/activities/execute-cursor/message-translator.d.ts +12 -20
  44. package/dist/activities/execute-cursor/message-translator.js +19 -32
  45. package/dist/activities/execute-cursor/message-translator.js.map +1 -1
  46. package/dist/activities/execute-cursor/prompt-builder.d.ts +48 -58
  47. package/dist/activities/execute-cursor/prompt-builder.js +76 -95
  48. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  49. package/dist/activities/execute-cursor/turn-boundary.d.ts +27 -55
  50. package/dist/activities/execute-cursor/turn-boundary.js +30 -90
  51. package/dist/activities/execute-cursor/turn-boundary.js.map +1 -1
  52. package/dist/activities/execute-cursor/turn-settle.d.ts +11 -7
  53. package/dist/activities/execute-cursor/turn-settle.js +37 -49
  54. package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
  55. package/dist/activities/execute-cursor/turn-setup.d.ts +16 -24
  56. package/dist/activities/execute-cursor/turn-setup.js +29 -59
  57. package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
  58. package/dist/activities/execute-cursor/turn-stream.d.ts +3 -8
  59. package/dist/activities/execute-cursor/turn-stream.js +9 -22
  60. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  61. package/dist/activities/execute-cursor/turn.js +5 -1
  62. package/dist/activities/execute-cursor/turn.js.map +1 -1
  63. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +93 -0
  64. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +348 -0
  65. package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js.map +1 -0
  66. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +170 -0
  67. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +225 -0
  68. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -0
  69. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.d.ts +24 -0
  70. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +41 -0
  71. package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -0
  72. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +54 -0
  73. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +72 -0
  74. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -0
  75. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +187 -23
  76. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +238 -27
  77. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
  78. package/dist/activities/execute-deep-agent/adapter.d.ts +41 -0
  79. package/dist/activities/execute-deep-agent/adapter.js +78 -0
  80. package/dist/activities/execute-deep-agent/adapter.js.map +1 -0
  81. package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +8 -0
  82. package/dist/activities/execute-deep-agent/cas-capture-observer.js +9 -0
  83. package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -1
  84. package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +31 -0
  85. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +40 -0
  86. package/dist/activities/execute-deep-agent/deep-agent-capabilities.js.map +1 -0
  87. package/dist/activities/execute-deep-agent/execution-state.d.ts +4 -11
  88. package/dist/activities/execute-deep-agent/execution-state.js +4 -12
  89. package/dist/activities/execute-deep-agent/execution-state.js.map +1 -1
  90. package/dist/activities/execute-deep-agent/hitl.d.ts +49 -47
  91. package/dist/activities/execute-deep-agent/hitl.js +77 -103
  92. package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
  93. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +51 -23
  94. package/dist/activities/execute-deep-agent/prompt-builder.js +115 -76
  95. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  96. package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +18 -32
  97. package/dist/activities/execute-deep-agent/status-builder-shared.js +10 -65
  98. package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
  99. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +10 -8
  100. package/dist/activities/execute-deep-agent/streaming-side-effects.js +12 -14
  101. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  102. package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +36 -13
  103. package/dist/activities/execute-deep-agent/subagent-tracker.js +76 -39
  104. package/dist/activities/execute-deep-agent/subagent-tracker.js.map +1 -1
  105. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -28
  106. package/dist/activities/execute-deep-agent/subagent-transformer.js +29 -82
  107. package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
  108. package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +3 -2
  109. package/dist/activities/execute-deep-agent/subagent-wiring.js +2 -2
  110. package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
  111. package/dist/activities/execute-deep-agent/turn-settle.d.ts +42 -0
  112. package/dist/activities/execute-deep-agent/turn-settle.js +146 -0
  113. package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -0
  114. package/dist/activities/execute-deep-agent/turn-setup.d.ts +212 -0
  115. package/dist/activities/execute-deep-agent/turn-setup.js +430 -0
  116. package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -0
  117. package/dist/activities/execute-deep-agent/turn-stream.d.ts +97 -0
  118. package/dist/activities/execute-deep-agent/turn-stream.js +242 -0
  119. package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -0
  120. package/dist/activities/execute-deep-agent/turn.d.ts +42 -0
  121. package/dist/activities/execute-deep-agent/turn.js +116 -0
  122. package/dist/activities/execute-deep-agent/turn.js.map +1 -0
  123. package/dist/activities/execute-deep-agent/v3-event-recorder.d.ts +6 -5
  124. package/dist/activities/execute-deep-agent/v3-event-recorder.js +6 -5
  125. package/dist/activities/execute-deep-agent/v3-event-recorder.js.map +1 -1
  126. package/dist/activities/execute-deep-agent/v3-events.d.ts +1 -1
  127. package/dist/activities/execute-deep-agent/v3-events.js +1 -1
  128. package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +60 -17
  129. package/dist/activities/execute-deep-agent/v3-status-builder.js +72 -57
  130. package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
  131. package/dist/harness/approval-decisions.d.ts +61 -0
  132. package/dist/harness/approval-decisions.js +102 -0
  133. package/dist/harness/approval-decisions.js.map +1 -0
  134. package/dist/harness/capabilities.d.ts +16 -5
  135. package/dist/harness/capabilities.js +9 -0
  136. package/dist/harness/capabilities.js.map +1 -1
  137. package/dist/harness/capture.d.ts +144 -0
  138. package/dist/harness/capture.js +219 -0
  139. package/dist/harness/capture.js.map +1 -0
  140. package/dist/harness/persist-chokepoint.d.ts +19 -4
  141. package/dist/harness/persist-chokepoint.js +17 -4
  142. package/dist/harness/persist-chokepoint.js.map +1 -1
  143. package/dist/harness/run-turn.js +115 -29
  144. package/dist/harness/run-turn.js.map +1 -1
  145. package/dist/harness/terminal-table.d.ts +40 -11
  146. package/dist/harness/terminal-table.js +58 -19
  147. package/dist/harness/terminal-table.js.map +1 -1
  148. package/dist/harness/turn-context.d.ts +141 -76
  149. package/dist/harness/turn-context.js +194 -99
  150. package/dist/harness/turn-context.js.map +1 -1
  151. package/dist/harness/types.d.ts +65 -12
  152. package/dist/harness/types.js +8 -2
  153. package/dist/harness/types.js.map +1 -1
  154. package/dist/harness-adapters.d.ts +12 -8
  155. package/dist/harness-adapters.js +17 -9
  156. package/dist/harness-adapters.js.map +1 -1
  157. package/dist/middleware/approval-gate.d.ts +1 -1
  158. package/dist/middleware/approval-gate.js +1 -1
  159. package/dist/middleware/approval-gate.js.map +1 -1
  160. package/dist/middleware/cost-advisory.d.ts +39 -0
  161. package/dist/middleware/{cost-cap.js → cost-advisory.js} +37 -55
  162. package/dist/middleware/cost-advisory.js.map +1 -0
  163. package/dist/middleware/index.d.ts +23 -12
  164. package/dist/middleware/index.js +26 -18
  165. package/dist/middleware/index.js.map +1 -1
  166. package/dist/middleware/types.d.ts +3 -2
  167. package/dist/runner-manager.js +1 -5
  168. package/dist/runner-manager.js.map +1 -1
  169. package/dist/runner.js +1 -5
  170. package/dist/runner.js.map +1 -1
  171. package/dist/shared/attachment-resolver.d.ts +41 -18
  172. package/dist/shared/attachment-resolver.js +178 -46
  173. package/dist/shared/attachment-resolver.js.map +1 -1
  174. package/dist/shared/attachment-zip.d.ts +85 -0
  175. package/dist/shared/attachment-zip.js +185 -0
  176. package/dist/shared/attachment-zip.js.map +1 -0
  177. package/dist/shared/connect-backfill.js +5 -4
  178. package/dist/shared/connect-backfill.js.map +1 -1
  179. package/dist/shared/cost-guard.d.ts +3 -3
  180. package/dist/shared/cost-guard.js +3 -3
  181. package/dist/shared/execution-status-writer.d.ts +5 -5
  182. package/dist/shared/execution-status-writer.js +5 -5
  183. package/dist/shared/extract-structured-output.d.ts +8 -7
  184. package/dist/shared/extract-structured-output.js +8 -7
  185. package/dist/shared/extract-structured-output.js.map +1 -1
  186. package/dist/shared/filereview/cas-progress.d.ts +1 -18
  187. package/dist/shared/filereview/cas-progress.js +4 -0
  188. package/dist/shared/filereview/cas-progress.js.map +1 -1
  189. package/dist/shared/filereview/cas-touched.d.ts +56 -0
  190. package/dist/shared/filereview/cas-touched.js +63 -0
  191. package/dist/shared/filereview/cas-touched.js.map +1 -0
  192. package/dist/shared/filereview/index.d.ts +2 -1
  193. package/dist/shared/filereview/index.js +2 -1
  194. package/dist/shared/filereview/index.js.map +1 -1
  195. package/dist/shared/mcp-resolver.d.ts +4 -5
  196. package/dist/shared/mcp-resolver.js +4 -5
  197. package/dist/shared/mcp-resolver.js.map +1 -1
  198. package/dist/shared/persist-decision.d.ts +3 -4
  199. package/dist/shared/persist-decision.js +3 -4
  200. package/dist/shared/persist-decision.js.map +1 -1
  201. package/dist/shared/plan-mode-permissions.d.ts +2 -2
  202. package/dist/shared/plan-mode-permissions.js +2 -2
  203. package/dist/shared/plan-mode-prompt.d.ts +1 -1
  204. package/dist/shared/plan-mode-prompt.js +1 -1
  205. package/dist/shared/prompt-sections.d.ts +163 -0
  206. package/dist/shared/prompt-sections.js +164 -0
  207. package/dist/shared/prompt-sections.js.map +1 -0
  208. package/dist/shared/skill-resolver.d.ts +4 -2
  209. package/dist/shared/skill-resolver.js +4 -2
  210. package/dist/shared/skill-resolver.js.map +1 -1
  211. package/dist/shared/tool-rounds.d.ts +32 -0
  212. package/dist/shared/tool-rounds.js +43 -0
  213. package/dist/shared/tool-rounds.js.map +1 -1
  214. package/dist/shared/tool-row.d.ts +34 -0
  215. package/dist/shared/tool-row.js +52 -0
  216. package/dist/shared/tool-row.js.map +1 -1
  217. package/dist/shared/workspace/session-provision.d.ts +3 -3
  218. package/dist/shared/workspace/session-provision.js +3 -3
  219. package/dist/shared/workspace/stigmer-link.d.ts +3 -3
  220. package/dist/shared/workspace/stigmer-link.js +3 -3
  221. package/dist/shared/workspace/writeback-coordinator.d.ts +15 -22
  222. package/dist/shared/workspace/writeback-coordinator.js +16 -53
  223. package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
  224. package/dist/tools/index.d.ts +1 -1
  225. package/dist/tools/index.js +1 -1
  226. package/package.json +4 -4
  227. package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +1 -0
  228. package/src/__test-utils__/execution-record-fixture.ts +18 -2
  229. package/src/__test-utils__/file-review-projection.ts +156 -0
  230. package/src/__test-utils__/git-workspace-fixture.ts +58 -0
  231. package/src/__test-utils__/harness-boot-order-child.ts +7 -2
  232. package/src/__test-utils__/harness-contract/contract.ts +33 -20
  233. package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
  234. package/src/__test-utils__/harness-contract/runtime-contract.ts +328 -10
  235. package/src/__test-utils__/harness-contract/scripted-adapter.ts +38 -6
  236. package/src/__test-utils__/harness-contract/types.ts +27 -3
  237. package/src/__test-utils__/hermetic-activity.ts +52 -14
  238. package/src/__test-utils__/model-registry-fixture.ts +32 -7
  239. package/src/__test-utils__/module-specifiers.ts +24 -0
  240. package/src/__test-utils__/turn-input-fixture.ts +2 -2
  241. package/src/__tests__/harness-boot-order.test.ts +9 -2
  242. package/src/activities/call-agent.ts +2 -2
  243. package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +47 -3
  244. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +2 -48
  245. package/src/activities/execute-cursor/__tests__/approval-decisions-agree.test.ts +1 -1
  246. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +6 -1
  247. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +52 -27
  248. package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +25 -0
  249. package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.everything.prompt.md +166 -0
  250. package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.plan-mode.prompt.md +186 -0
  251. package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.recovery.prompt.md +191 -0
  252. package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.reinvocation.prompt.md +13 -0
  253. package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.prefixed.prompt.md +32 -0
  254. package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.raw.prompt.md +1 -0
  255. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -2
  256. package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-error-at-create.status.json +4 -0
  257. package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +24 -4
  258. package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +6 -5
  259. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +9 -0
  260. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +11 -8
  261. package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +229 -0
  262. package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +3 -3
  263. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +2 -2
  264. package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +78 -234
  265. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +1 -26
  266. package/src/activities/execute-cursor/adapter.ts +7 -5
  267. package/src/activities/execute-cursor/approval-policy.ts +5 -2
  268. package/src/activities/execute-cursor/approval-state.ts +16 -8
  269. package/src/activities/execute-cursor/cas-observations.ts +20 -2
  270. package/src/activities/execute-cursor/cursor-capabilities.ts +24 -5
  271. package/src/activities/execute-cursor/message-translator.ts +29 -41
  272. package/src/activities/execute-cursor/prompt-builder.ts +99 -157
  273. package/src/activities/execute-cursor/turn-boundary.ts +34 -140
  274. package/src/activities/execute-cursor/turn-settle.ts +37 -51
  275. package/src/activities/execute-cursor/turn-setup.ts +30 -70
  276. package/src/activities/execute-cursor/turn-stream.ts +8 -31
  277. package/src/activities/execute-cursor/turn.ts +5 -1
  278. package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +391 -0
  279. package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +313 -0
  280. package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +48 -0
  281. package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +85 -0
  282. package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +383 -36
  283. package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +76 -0
  284. package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +47 -0
  285. package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +80 -0
  286. package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +1 -1
  287. package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -2
  288. package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -3
  289. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.build-from-plan.prompt.md +172 -0
  290. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.everything.prompt.md +167 -0
  291. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.minimal.prompt.md +36 -0
  292. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.plan-mode.prompt.md +178 -0
  293. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.skills-below-threshold.prompt.md +158 -0
  294. package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +210 -0
  295. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +74 -0
  296. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +104 -0
  297. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +78 -0
  298. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +138 -0
  299. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +68 -0
  300. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +154 -0
  301. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +116 -0
  302. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +69 -0
  303. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +67 -0
  304. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/plain-turn.status.json +47 -0
  305. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +68 -0
  306. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +280 -0
  307. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +77 -0
  308. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +18 -0
  309. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +99 -0
  310. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +109 -0
  311. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +76 -0
  312. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.text-fallback.status.json +51 -0
  313. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tier2.status.json +51 -0
  314. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tool-strategy.status.json +56 -0
  315. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +90 -0
  316. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +70 -0
  317. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +71 -0
  318. package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +144 -0
  319. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +184 -0
  320. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +203 -0
  321. package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +170 -0
  322. package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +175 -0
  323. package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +206 -0
  324. package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +142 -0
  325. package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +126 -0
  326. package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +139 -0
  327. package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +122 -0
  328. package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +236 -0
  329. package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +161 -0
  330. package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +157 -0
  331. package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +152 -0
  332. package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +50 -122
  333. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +8 -6
  334. package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +2 -2
  335. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +35 -35
  336. package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +247 -0
  337. package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +329 -0
  338. package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +1 -20
  339. package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +78 -0
  340. package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +1 -1
  341. package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +2 -2
  342. package/src/activities/execute-deep-agent/__tests__/subagent-tracker.test.ts +57 -31
  343. package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +19 -79
  344. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +15 -15
  345. package/src/activities/execute-deep-agent/__tests__/summarization-verification.test.ts +1 -1
  346. package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +391 -0
  347. package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +343 -38
  348. package/src/activities/execute-deep-agent/__tests__/vision-input.test.ts +5 -5
  349. package/src/activities/execute-deep-agent/adapter.ts +94 -0
  350. package/src/activities/execute-deep-agent/cas-capture-observer.ts +11 -0
  351. package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +43 -0
  352. package/src/activities/execute-deep-agent/execution-state.ts +4 -13
  353. package/src/activities/execute-deep-agent/hitl.ts +100 -123
  354. package/src/activities/execute-deep-agent/prompt-builder.ts +141 -115
  355. package/src/activities/execute-deep-agent/status-builder-shared.ts +21 -84
  356. package/src/activities/execute-deep-agent/streaming-side-effects.ts +13 -19
  357. package/src/activities/execute-deep-agent/subagent-tracker.ts +77 -42
  358. package/src/activities/execute-deep-agent/subagent-transformer.ts +46 -105
  359. package/src/activities/execute-deep-agent/subagent-wiring.ts +5 -4
  360. package/src/activities/execute-deep-agent/turn-settle.ts +164 -0
  361. package/src/activities/execute-deep-agent/turn-setup.ts +541 -0
  362. package/src/activities/execute-deep-agent/turn-stream.ts +313 -0
  363. package/src/activities/execute-deep-agent/turn.ts +131 -0
  364. package/src/activities/execute-deep-agent/v3-event-recorder.ts +6 -5
  365. package/src/activities/execute-deep-agent/v3-events.ts +1 -1
  366. package/src/activities/execute-deep-agent/v3-status-builder.ts +84 -63
  367. package/src/harness/__tests__/approval-decisions.test.ts +116 -0
  368. package/src/harness/__tests__/capture.test.ts +268 -0
  369. package/src/harness/__tests__/mount-skills.test.ts +169 -0
  370. package/src/harness/__tests__/persist-chokepoint.test.ts +38 -2
  371. package/src/harness/__tests__/run-turn-deterministic.test.ts +94 -0
  372. package/src/harness/__tests__/run-turn.test.ts +39 -23
  373. package/src/harness/__tests__/turn-attachments-vision.test.ts +80 -0
  374. package/src/harness/__tests__/turn-context.test.ts +73 -26
  375. package/src/harness/approval-decisions.ts +104 -0
  376. package/src/harness/capabilities.ts +16 -5
  377. package/src/harness/capture.ts +301 -0
  378. package/src/harness/persist-chokepoint.ts +24 -4
  379. package/src/harness/run-turn.ts +120 -29
  380. package/src/harness/terminal-table.ts +61 -20
  381. package/src/harness/turn-context.ts +208 -111
  382. package/src/harness/types.ts +66 -12
  383. package/src/harness-adapters.ts +17 -9
  384. package/src/middleware/__tests__/approval-gate.test.ts +1 -1
  385. package/src/middleware/__tests__/cost-advisory.test.ts +113 -0
  386. package/src/middleware/approval-gate.ts +2 -2
  387. package/src/middleware/{cost-cap.ts → cost-advisory.ts} +40 -70
  388. package/src/middleware/index.ts +29 -29
  389. package/src/middleware/types.ts +3 -2
  390. package/src/runner-manager.ts +0 -8
  391. package/src/runner.ts +0 -8
  392. package/src/shared/__tests__/attachment-naming.test.ts +6 -19
  393. package/src/shared/__tests__/attachment-resolver.test.ts +158 -8
  394. package/src/shared/__tests__/attachment-zip.test.ts +236 -0
  395. package/src/shared/__tests__/bedrock-adapter.test.ts +1 -1
  396. package/src/shared/__tests__/blueprint-resolver.test.ts +43 -0
  397. package/src/shared/__tests__/cost-guard.test.ts +3 -3
  398. package/src/shared/__tests__/prompt-sections.test.ts +167 -0
  399. package/src/shared/__tests__/secret-leak-scan.test.ts +2 -2
  400. package/src/{activities/execute-deep-agent → shared}/__tests__/stamp-flowed-rows.test.ts +33 -10
  401. package/src/shared/__tests__/vertex-adapter.test.ts +1 -1
  402. package/src/shared/attachment-resolver.ts +218 -55
  403. package/src/shared/attachment-zip.ts +281 -0
  404. package/src/shared/connect-backfill.ts +9 -11
  405. package/src/shared/cost-guard.ts +3 -3
  406. package/src/shared/execution-status-writer.ts +5 -5
  407. package/src/shared/extract-structured-output.ts +8 -7
  408. package/src/shared/filereview/__tests__/capture.test.ts +86 -4
  409. package/src/shared/filereview/__tests__/cas-progress.test.ts +2 -1
  410. package/src/shared/filereview/cas-progress.ts +5 -19
  411. package/src/shared/filereview/cas-touched.ts +89 -0
  412. package/src/shared/filereview/index.ts +4 -2
  413. package/src/shared/mcp-resolver.ts +4 -5
  414. package/src/shared/persist-decision.ts +3 -4
  415. package/src/shared/plan-mode-permissions.ts +2 -2
  416. package/src/shared/plan-mode-prompt.ts +1 -1
  417. package/src/shared/prompt-sections.ts +256 -0
  418. package/src/shared/skill-resolver.ts +4 -2
  419. package/src/shared/tool-rounds.ts +48 -0
  420. package/src/shared/tool-row.ts +61 -0
  421. package/src/shared/workspace/__tests__/stigmer-link.test.ts +7 -22
  422. package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +20 -61
  423. package/src/shared/workspace/session-provision.ts +3 -3
  424. package/src/shared/workspace/stigmer-link.ts +3 -3
  425. package/src/shared/workspace/writeback-coordinator.ts +16 -64
  426. package/src/tools/index.ts +1 -1
  427. package/dist/activities/execute-cursor/capture-flow.d.ts +0 -142
  428. package/dist/activities/execute-cursor/capture-flow.js +0 -285
  429. package/dist/activities/execute-cursor/capture-flow.js.map +0 -1
  430. package/dist/activities/execute-cursor/command-provenance.d.ts +0 -48
  431. package/dist/activities/execute-cursor/command-provenance.js +0 -38
  432. package/dist/activities/execute-cursor/command-provenance.js.map +0 -1
  433. package/dist/activities/execute-deep-agent/attachment-injector.d.ts +0 -134
  434. package/dist/activities/execute-deep-agent/attachment-injector.js +0 -386
  435. package/dist/activities/execute-deep-agent/attachment-injector.js.map +0 -1
  436. package/dist/activities/execute-deep-agent/command-provenance.d.ts +0 -61
  437. package/dist/activities/execute-deep-agent/command-provenance.js +0 -72
  438. package/dist/activities/execute-deep-agent/command-provenance.js.map +0 -1
  439. package/dist/activities/execute-deep-agent/environment.d.ts +0 -24
  440. package/dist/activities/execute-deep-agent/environment.js +0 -58
  441. package/dist/activities/execute-deep-agent/environment.js.map +0 -1
  442. package/dist/activities/execute-deep-agent/event-recorder.d.ts +0 -21
  443. package/dist/activities/execute-deep-agent/event-recorder.js +0 -67
  444. package/dist/activities/execute-deep-agent/event-recorder.js.map +0 -1
  445. package/dist/activities/execute-deep-agent/index.d.ts +0 -15
  446. package/dist/activities/execute-deep-agent/index.js +0 -918
  447. package/dist/activities/execute-deep-agent/index.js.map +0 -1
  448. package/dist/activities/execute-deep-agent/mcp-gate.d.ts +0 -28
  449. package/dist/activities/execute-deep-agent/mcp-gate.js +0 -22
  450. package/dist/activities/execute-deep-agent/mcp-gate.js.map +0 -1
  451. package/dist/activities/execute-deep-agent/post-stream.d.ts +0 -23
  452. package/dist/activities/execute-deep-agent/post-stream.js +0 -71
  453. package/dist/activities/execute-deep-agent/post-stream.js.map +0 -1
  454. package/dist/activities/execute-deep-agent/setup.d.ts +0 -136
  455. package/dist/activities/execute-deep-agent/setup.js +0 -767
  456. package/dist/activities/execute-deep-agent/setup.js.map +0 -1
  457. package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +0 -36
  458. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +0 -56
  459. package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +0 -1
  460. package/dist/activities/execute-deep-agent/status-builder.d.ts +0 -93
  461. package/dist/activities/execute-deep-agent/status-builder.js +0 -365
  462. package/dist/activities/execute-deep-agent/status-builder.js.map +0 -1
  463. package/dist/activities/execute-deep-agent/streaming-terminal.d.ts +0 -21
  464. package/dist/activities/execute-deep-agent/streaming-terminal.js +0 -80
  465. package/dist/activities/execute-deep-agent/streaming-terminal.js.map +0 -1
  466. package/dist/activities/execute-deep-agent/streaming-v3.d.ts +0 -13
  467. package/dist/activities/execute-deep-agent/streaming-v3.js +0 -174
  468. package/dist/activities/execute-deep-agent/streaming-v3.js.map +0 -1
  469. package/dist/activities/execute-deep-agent/streaming.d.ts +0 -81
  470. package/dist/activities/execute-deep-agent/streaming.js +0 -160
  471. package/dist/activities/execute-deep-agent/streaming.js.map +0 -1
  472. package/dist/middleware/cost-cap.d.ts +0 -22
  473. package/dist/middleware/cost-cap.js.map +0 -1
  474. package/dist/middleware/graceful-stop.d.ts +0 -17
  475. package/dist/middleware/graceful-stop.js +0 -63
  476. package/dist/middleware/graceful-stop.js.map +0 -1
  477. package/dist/shared/skill-writer.d.ts +0 -75
  478. package/dist/shared/skill-writer.js +0 -207
  479. package/dist/shared/skill-writer.js.map +0 -1
  480. package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +0 -1032
  481. package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +0 -240
  482. package/src/activities/execute-cursor/__tests__/progress-substrate.test.ts +0 -169
  483. package/src/activities/execute-cursor/capture-flow.ts +0 -379
  484. package/src/activities/execute-cursor/command-provenance.ts +0 -73
  485. package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +0 -1161
  486. package/src/activities/execute-deep-agent/__tests__/command-provenance.test.ts +0 -252
  487. package/src/activities/execute-deep-agent/__tests__/environment.test.ts +0 -108
  488. package/src/activities/execute-deep-agent/__tests__/event-recorder.test.ts +0 -150
  489. package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +0 -226
  490. package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +0 -318
  491. package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +0 -351
  492. package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +0 -293
  493. package/src/activities/execute-deep-agent/__tests__/index.test.ts +0 -99
  494. package/src/activities/execute-deep-agent/__tests__/mcp-gate.test.ts +0 -44
  495. package/src/activities/execute-deep-agent/__tests__/post-stream.test.ts +0 -112
  496. package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +0 -358
  497. package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +0 -1933
  498. package/src/activities/execute-deep-agent/__tests__/streaming-terminal.test.ts +0 -40
  499. package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +0 -597
  500. package/src/activities/execute-deep-agent/__tests__/streaming.test.ts +0 -508
  501. package/src/activities/execute-deep-agent/attachment-injector.ts +0 -627
  502. package/src/activities/execute-deep-agent/command-provenance.ts +0 -102
  503. package/src/activities/execute-deep-agent/environment.ts +0 -76
  504. package/src/activities/execute-deep-agent/event-recorder.ts +0 -95
  505. package/src/activities/execute-deep-agent/index.ts +0 -1072
  506. package/src/activities/execute-deep-agent/mcp-gate.ts +0 -37
  507. package/src/activities/execute-deep-agent/post-stream.ts +0 -109
  508. package/src/activities/execute-deep-agent/setup.ts +0 -1081
  509. package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +0 -64
  510. package/src/activities/execute-deep-agent/status-builder.ts +0 -481
  511. package/src/activities/execute-deep-agent/streaming-terminal.ts +0 -106
  512. package/src/activities/execute-deep-agent/streaming-v3.ts +0 -277
  513. package/src/activities/execute-deep-agent/streaming.ts +0 -313
  514. package/src/middleware/__tests__/cost-cap.test.ts +0 -192
  515. package/src/middleware/__tests__/graceful-stop.test.ts +0 -105
  516. package/src/middleware/graceful-stop.ts +0 -86
  517. package/src/shared/__tests__/skill-writer.test.ts +0 -372
  518. package/src/shared/skill-writer.ts +0 -266
@@ -54,8 +54,14 @@
54
54
  * - No persist cadence. The runtime's chokepoint is single-flight and
55
55
  * unconditional at settle; WHEN a streaming turn asks for a write is the
56
56
  * adapter's (`shared/persist-decision.ts` over its own dirty flags, since
57
- * what counts as a discrete change is engine knowledge), until S3 lifts
58
- * the file-review capture and can revisit with both loops in view.
57
+ * what counts as a discrete change is engine knowledge). Revisited with
58
+ * both loops in view at S3 M4 and kept (Q-S3-13): the cadence RULE is
59
+ * shared, the timing is the engine's.
60
+ * - No capture. The file-review capture is the runtime's whole
61
+ * (`harness/capture.ts`): the baseline before `runTurn`, the candidate
62
+ * after it, the stamp, the review decision. The one capture fact an
63
+ * adapter holds — what its engine observed touching CAS-owned paths — it
64
+ * binds through `TurnSink.bindCasObservations`.
59
65
  *
60
66
  * Module shape follows `shared/checkpointer/`: `types.ts`, `capabilities.ts`,
61
67
  * `registry.ts`, no barrel.
@@ -82,6 +88,7 @@ import type { EffectiveThinkingMode } from "../shared/thinking-mode.js";
82
88
  import type { SenderIdentity } from "../shared/sender-identity.js";
83
89
  import type { DeclaredPreferencesContent } from "../shared/declared-preferences.js";
84
90
  import type { RecalledMemoriesContent } from "../shared/recalled-memories.js";
91
+ import type { CasTouchedReader } from "../shared/filereview/cas-touched.js";
85
92
  import type { HarnessCapabilities } from "./capabilities.js";
86
93
 
87
94
  /**
@@ -196,6 +203,21 @@ export interface TurnMcp {
196
203
  readonly policies: ReadonlyMap<string, MergedToolPolicy>;
197
204
  }
198
205
 
206
+ /**
207
+ * The mounted skills (phase 5), each under the session's platform dir and
208
+ * reachable from the workspace through its `.stigmer` link; the harness
209
+ * renders them into its prompt shapes. Resolved per OWNER, because a
210
+ * sub-agent's skills are its own prompt's, not the root's: the root's are
211
+ * `blueprint.mergedSkillRefs` (agent plus session), a sub-agent's are its
212
+ * `skillRefs` on the blueprint, keyed by the sub-agent's name (the key its
213
+ * compiler uses). A sub-agent with no skills has no entry. Empty for a
214
+ * harness whose `capabilities.subAgents` is false.
215
+ */
216
+ export interface TurnSkills {
217
+ readonly root: readonly SkillMetadata[];
218
+ readonly bySubAgent: ReadonlyMap<string, readonly SkillMetadata[]>;
219
+ }
220
+
199
221
  /** The turn's explicit inputs (phase 5b), resolved into the workspace with the vision facts derived once. */
200
222
  export interface TurnAttachments {
201
223
  readonly results: readonly ResolvedAttachment[];
@@ -245,9 +267,13 @@ export interface TurnStandingContext {
245
267
  * `engine-minted` harness's first turn (nothing minted yet) and the id the
246
268
  * adapter bound through {@link TurnSink.bindHarnessState} on every later
247
269
  * invocation; the runtime-minted id on every turn of a `deterministic`
248
- * harness. An adapter derives create-vs-resume from it and its own state
249
- * through `turn-context.ts`'s `isReinvocation`; the contract carries no flag
250
- * because the two harnesses would derive it differently.
270
+ * harness. An adapter derives create-vs-resume for its ENGINE from it and its
271
+ * own state; the runtime derives its own reinvocation fact (the seed, the
272
+ * reconcile) in `turn-context.ts`'s `isReinvocation`, one arm per
273
+ * `stateIdSource`. The contract carries no flag because the two readings
274
+ * answer different questions ("does the engine hold state for this session"
275
+ * vs "does this execution have a transcript") and one name over two meanings
276
+ * would be the divergence a contract exists to prevent.
251
277
  *
252
278
  * Adapter-only facts (the Cursor mode, the service-tier params, the seeded
253
279
  * sub-agent rows) are read by the adapter from these records, never resolved
@@ -278,8 +304,7 @@ export interface TurnInput extends NormalizedActivityInput {
278
304
  readonly environment: TurnEnvironment;
279
305
  readonly workspace: TurnWorkspace;
280
306
  readonly mcp: TurnMcp;
281
- /** The mounted skills (phase 5): each under the session's platform dir, reachable from the workspace through its `.stigmer` link; the harness renders them into its prompt. */
282
- readonly skills: readonly SkillMetadata[];
307
+ readonly skills: TurnSkills;
283
308
  readonly attachments: TurnAttachments;
284
309
  /** Approved whole-file writes the runtime applied itself this turn (exact-apply); the harness issues no grant for them. */
285
310
  readonly appliedToolCallIds: ReadonlySet<string>;
@@ -304,11 +329,10 @@ export interface TurnInput extends NormalizedActivityInput {
304
329
  * the adapter appends the engine's transcript rows (assistant messages,
305
330
  * tool-call rows and their approval status, sub-agent rows, todos); the
306
331
  * runtime writes the phase, the terminal system messages, `streamingUsage`,
307
- * artifacts, write-backs and the file-review projection. An adapter never
308
- * writes a phase or a terminal copy: those are Temporal semantics the
309
- * runtime owns once. (The file-review boundary itself is the adapter's until
310
- * S3 lifts both harnesses' captures together; `execute-cursor/adapter.ts`
311
- * says so.)
332
+ * artifacts, write-backs, `fileChangeProgress`, the file-review ledger
333
+ * events and the change-set stamp on a flowed edit row (S3 M4,
334
+ * `harness/capture.ts`). An adapter never writes a phase or a terminal copy:
335
+ * those are Temporal semantics the runtime owns once.
312
336
  */
313
337
  export interface TurnSink {
314
338
  /**
@@ -398,6 +422,26 @@ export interface TurnSink {
398
422
  * that error and executes nothing further.
399
423
  */
400
424
  bindHarnessState(harnessStateId: string): Promise<void>;
425
+
426
+ /**
427
+ * What the engine observed touching CAS-owned paths this turn — gitignored
428
+ * paths in a git tree, every path in a non-git one: the pre-turn bytes of
429
+ * each first-touched path and the paths the gate blocked as secrets
430
+ * (`shared/filereview/cas-touched.ts`). The one fact about a turn's file
431
+ * changes the runtime cannot read from the tree: the pre-edit bytes exist
432
+ * only at mutation time, in whatever sits between the engine and the disk
433
+ * (the deep-agent's filesystem backend and gate, the Cursor hook's
434
+ * sidecar). The runtime reads the AFTER bytes itself and owns everything
435
+ * else of the capture (`harness/capture.ts`): the baseline, the progress,
436
+ * the candidate, the stamp, the review decision.
437
+ *
438
+ * Bound once, before the first persist, as `bindHarnessState` is; the
439
+ * runtime pulls it at each progress refresh and at the turn boundary, so
440
+ * it must return an atomic snapshot (copy live state before any await). A
441
+ * harness that observes nothing binds nothing and gets git-only capture.
442
+ * Never throws.
443
+ */
444
+ bindCasObservations(read: CasTouchedReader): void;
401
445
  }
402
446
 
403
447
  /**
@@ -460,6 +504,15 @@ export type FailureSurface = "engine" | "actionable" | "internal";
460
504
  * takes, `cause` is for the log. The runtime persists FAILED and RETURNS
461
505
  * (Temporal does not retry a returned activity; re-running the same
462
506
  * prompt would fail the same way).
507
+ * - `tool_call_limit`: the engine exhausted the tool-round budget the
508
+ * execution set (`max_tool_rounds`, `shared/tool-rounds.ts`) and stopped
509
+ * at a step boundary with its work checkpointed. Not `failed`: the
510
+ * platform deliberately stopped the run, and the conversation continues
511
+ * on the next message. The runtime writes TERMINATED with the cross-repo
512
+ * prefix and the user copy, and RETURNS (a retry would spend the same
513
+ * budget again). Every surveyed SDK ends a turn on a budget with a
514
+ * distinct outcome (Claude's `maxTurns` subtype, Codex's turn reason), so
515
+ * this is a cross-harness fact, not an engine quirk.
463
516
  * - `interrupted`: `sink.stopSignal` aborted and the adapter stopped. WHY it
464
517
  * aborted is the runtime's evidence (its watchdog, its accounting, its
465
518
  * chokepoint, the cancellation it was delivered), so no reason rides here;
@@ -469,5 +522,6 @@ export type TurnOutcome =
469
522
  | { readonly kind: "completed" }
470
523
  | { readonly kind: "cancelled" }
471
524
  | { readonly kind: "awaiting_approval" }
525
+ | { readonly kind: "tool_call_limit" }
472
526
  | { readonly kind: "failed"; readonly message: string; readonly surface: FailureSurface; readonly cause?: unknown }
473
527
  | { readonly kind: "interrupted" };
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The harnesses this worker serves: one registry row per adapter, in boot
3
3
  * order. The ONE place an adapter's factory is named outside its own
4
- * directory.
4
+ * directory; both composition roots read this table and nothing else to
5
+ * learn which harnesses exist.
5
6
  *
6
7
  * Lives at the source root, beside the composition roots that read it, and
7
8
  * not in `harness/registry.ts`: a row imports its adapter from
@@ -11,20 +12,27 @@
11
12
  *
12
13
  * ORDER IS LOAD-BEARING (`registry.ts` `bootHarnesses`): the Cursor
13
14
  * adapter's interceptors must patch `node:http2` before anything dials the
14
- * control plane, so it boots first. The native deep-agent harness joins this
15
- * table at S3 of the harness runtime program; until then both roots import
16
- * its activities directly beside this table's.
15
+ * control plane, so it boots first. The native deep-agent adapter boots
16
+ * second; its `boot` registers the deepagents harness profiles and loads
17
+ * LangChain, both after the patch (S3 M2b, 2026-09-13; until then the roots
18
+ * imported the native orchestrator directly beside this table).
17
19
  *
18
20
  * THIS MODULE'S STATIC GRAPH MUST STAY CONNECT- AND SDK-FREE. The roots
19
21
  * import it BEFORE they boot the harnesses, and the Cursor adapter's boot is
20
22
  * where `node:http2` is patched and `@cursor/sdk` first loaded; an adapter
21
- * factory that imported its SDK statically would defeat both. An adapter
22
- * named here loads its vendor SDK inside `boot` (`adapter.ts` shows the
23
- * shape). `__tests__/harness-boot-order.test.ts` boots a fresh process
24
- * through this module and fails if the graph regresses.
23
+ * factory that imported its SDK statically would defeat both. Every adapter
24
+ * named here loads its vendor SDK inside `boot` (`execute-cursor/adapter.ts`
25
+ * and `execute-deep-agent/adapter.ts` show the shape; each has a fence that
26
+ * walks its static graph). `__tests__/harness-boot-order.test.ts` boots a
27
+ * fresh process through this module — both rows — and fails if the graph
28
+ * regresses.
25
29
  */
26
30
 
27
31
  import { createCursorAdapter } from "./activities/execute-cursor/adapter.js";
32
+ import { createDeepAgentAdapter } from "./activities/execute-deep-agent/adapter.js";
28
33
  import type { HarnessRow } from "./harness/registry.js";
29
34
 
30
- export const HARNESS_ADAPTERS: readonly HarnessRow[] = [{ harness: "cursor", adapter: createCursorAdapter() }];
35
+ export const HARNESS_ADAPTERS: readonly HarnessRow[] = [
36
+ { harness: "cursor", adapter: createCursorAdapter() },
37
+ { harness: "deep-agent", adapter: createDeepAgentAdapter() },
38
+ ];
@@ -48,7 +48,7 @@ describe("ApprovalGateMiddleware", () => {
48
48
  });
49
49
 
50
50
  // The global pre-arm (spec.auto_approve_all) is no longer the gate's concern:
51
- // setup.ts simply does not install the gate when it is set. The gate is always
51
+ // turn-setup.ts simply does not install the gate when it is set. The gate is always
52
52
  // active once built; scoped leases (below) decide what it lets through.
53
53
 
54
54
  it("auto-approves a built-in whose category holds a run-lifetime lease", async () => {
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The cost advisory: prices each model call off `usage_metadata`, keeps one
3
+ * running total the sub-agent views share, and warns the model ONCE at the
4
+ * configured share of `max_cost_usd`. Nothing here caps: the enforcement is
5
+ * the turn runtime's (`shared/__tests__/cost-guard.test.ts`,
6
+ * `harness/__tests__/run-turn.test.ts`'s cost-cap arm). Until S3 M2b this
7
+ * file was `cost-cap.test.ts` and also pinned the in-graph tool block and
8
+ * the "exceeded" message — retired with that half (Q-S3-3, Q-M2b-9).
9
+ */
10
+
11
+ import { describe, it, expect } from "vitest";
12
+ import { AIMessage } from "@langchain/core/messages";
13
+ import { createCostAdvisoryMiddleware } from "../cost-advisory.js";
14
+
15
+ function stateWithUsage(inputTokens: number, outputTokens: number, cacheRead = 0): Record<string, unknown> {
16
+ const aiMsg = new AIMessage({ content: "response" });
17
+ (aiMsg as unknown as Record<string, unknown>).usage_metadata = {
18
+ input_tokens: inputTokens,
19
+ output_tokens: outputTokens,
20
+ input_token_details: { cache_read: cacheRead },
21
+ };
22
+ return { messages: [aiMsg] };
23
+ }
24
+
25
+ const BASE_CONFIG = {
26
+ maxCostUsd: 1.0,
27
+ inputPricePerMillion: 3.0,
28
+ outputPricePerMillion: 15.0,
29
+ cacheReadPricePerMillion: 0.3,
30
+ warningPct: 80,
31
+ };
32
+
33
+ function warningOf(result: unknown): string | undefined {
34
+ const messages = (result as { messages?: Array<{ content: unknown }> } | undefined)?.messages;
35
+ return messages?.[0] ? String(messages[0].content) : undefined;
36
+ }
37
+
38
+ describe("CostAdvisoryMiddleware", () => {
39
+ it("prices a model call from usage_metadata", () => {
40
+ const mw = createCostAdvisoryMiddleware(BASE_CONFIG);
41
+ mw.afterModel!(stateWithUsage(1000, 500), {});
42
+ // (1000 * 3 + 500 * 15) / 1_000_000 = 0.0105
43
+ expect(mw.runningCost).toBeCloseTo(0.0105, 4);
44
+ });
45
+
46
+ it("prices cache reads at their own rate when configured", () => {
47
+ const mw = createCostAdvisoryMiddleware(BASE_CONFIG);
48
+ mw.afterModel!(stateWithUsage(1000, 500, 600), {});
49
+ // regular input 400 * 3 + cache 600 * 0.3 + output 500 * 15 = 8,880 / 1M
50
+ expect(mw.runningCost).toBeCloseTo(0.00888, 4);
51
+ });
52
+
53
+ it("accumulates across model calls", () => {
54
+ const mw = createCostAdvisoryMiddleware(BASE_CONFIG);
55
+ mw.afterModel!(stateWithUsage(1000, 500), {});
56
+ mw.afterModel!(stateWithUsage(2000, 1000), {});
57
+ expect(mw.runningCost).toBeCloseTo(0.0315, 4);
58
+ });
59
+
60
+ it("skips a message without usage_metadata", () => {
61
+ const mw = createCostAdvisoryMiddleware(BASE_CONFIG);
62
+ mw.afterModel!({ messages: [new AIMessage({ content: "no usage" })] }, {});
63
+ expect(mw.runningCost).toBe(0);
64
+ });
65
+
66
+ it("warns once at the warning share of the cap, and never blocks a tool", () => {
67
+ const mw = createCostAdvisoryMiddleware({ ...BASE_CONFIG, maxCostUsd: 0.01, warningPct: 80 });
68
+ // $0.0105 crosses 80% of $0.01.
69
+ const first = warningOf(mw.afterModel!(stateWithUsage(1000, 500), {}));
70
+ expect(first).toContain("Budget warning");
71
+ expect(first).toContain("$0.01");
72
+ // Far past the cap: still no second message, and no tool hook exists to block with.
73
+ const second = mw.afterModel!(stateWithUsage(100_000, 50_000), {});
74
+ expect(second).toBeUndefined();
75
+ expect(mw.wrapToolCall, "the advisory has no power over tool calls; the runtime enforces the cap").toBeUndefined();
76
+ });
77
+
78
+ it("resets its total on beforeAgent", () => {
79
+ const mw = createCostAdvisoryMiddleware(BASE_CONFIG);
80
+ mw.afterModel!(stateWithUsage(1000, 500), {});
81
+ mw.beforeAgent!({}, {});
82
+ expect(mw.runningCost).toBe(0);
83
+ });
84
+
85
+ it("rejects a non-positive cap and a warning share outside 50–95", () => {
86
+ expect(() => createCostAdvisoryMiddleware({ ...BASE_CONFIG, maxCostUsd: 0 })).toThrow("maxCostUsd");
87
+ expect(() => createCostAdvisoryMiddleware({ ...BASE_CONFIG, maxCostUsd: -1 })).toThrow("maxCostUsd");
88
+ expect(() => createCostAdvisoryMiddleware({ ...BASE_CONFIG, warningPct: 49 })).toThrow("warningPct");
89
+ expect(() => createCostAdvisoryMiddleware({ ...BASE_CONFIG, warningPct: 96 })).toThrow("warningPct");
90
+ });
91
+
92
+ describe("forSubAgent", () => {
93
+ it("advances the parent's running total", () => {
94
+ const parent = createCostAdvisoryMiddleware(BASE_CONFIG);
95
+ parent.forSubAgent().afterModel!(stateWithUsage(1000, 500), {});
96
+ expect(parent.runningCost).toBeCloseTo(0.0105, 4);
97
+ });
98
+
99
+ it("does not reset the total when the sub-agent starts", () => {
100
+ const parent = createCostAdvisoryMiddleware(BASE_CONFIG);
101
+ parent.afterModel!(stateWithUsage(1000, 500), {});
102
+ const child = parent.forSubAgent();
103
+ expect(child.beforeAgent, "the view has no beforeAgent on purpose").toBeUndefined();
104
+ expect(parent.runningCost).toBeCloseTo(0.0105, 4);
105
+ });
106
+
107
+ it("warns inside the sub-agent when its call crosses the parent's threshold", () => {
108
+ const parent = createCostAdvisoryMiddleware({ ...BASE_CONFIG, maxCostUsd: 0.01 });
109
+ const warning = warningOf(parent.forSubAgent().afterModel!(stateWithUsage(1000, 500), {}));
110
+ expect(warning).toContain("Budget warning");
111
+ });
112
+ });
113
+ });
@@ -109,7 +109,7 @@ export interface ApprovalGateConfig {
109
109
  * gitignored path stays on the interrupt gate exactly as before.
110
110
  *
111
111
  * True only for gates whose backend a CAS observer wraps: the parent gate
112
- * always (setup.ts), and — since DD-19 — sub-agent gates too, because
112
+ * always (turn-setup.ts), and — since DD-19 — sub-agent gates too, because
113
113
  * `compileSubagents` gives every sub-agent a CAS-observing backend wired to
114
114
  * the SAME shared observer and `buildSubAgentMiddleware` then inherits this
115
115
  * config verbatim. A gate over an UNOBSERVED backend must keep this false:
@@ -199,7 +199,7 @@ export function createApprovalGateMiddleware(
199
199
  const leasedCategories = config.leasedCategories ?? EMPTY_CATEGORY_SET;
200
200
 
201
201
  // No global-bypass early return: a pre-armed spec.auto_approve_all means the
202
- // gate is never even installed (setup.ts builds this config only when not
202
+ // gate is never even installed (turn-setup.ts builds this config only when not
203
203
  // global). Scoped leases keep the gate active so non-leased actions still gate.
204
204
 
205
205
  return {
@@ -1,29 +1,46 @@
1
1
  /**
2
- * Cost cap middleware for agent execution budget enforcement.
2
+ * Cost advisory middleware: warns the model once as the execution's estimated
3
+ * spend nears `max_cost_usd`, so it can wrap up before the cap.
3
4
  *
4
- * Tracks running estimated cost of LLM calls and enforces a
5
- * configurable cost ceiling:
5
+ * It ADVISES; it does not cap. The one enforcement of `max_cost_usd` is the
6
+ * turn runtime's (`harness/run-turn.ts` over `shared/cost-guard.ts`): every
7
+ * usage delta the adapter reports moves the runtime's estimate, and when the
8
+ * estimate crosses the cap the runtime stops the turn and settles TERMINATED
9
+ * with the cost-limit copy. The graph learns of the stop through its abort
10
+ * signal, the same way it learns of a platform STOP or a stall. This is the
11
+ * shape `execution-budget.ts` already has beside LangGraph's `recursionLimit`:
12
+ * the middleware warns inside the graph, the engine's own limit enforces.
6
13
  *
7
- * afterModel extracts usage_metadata from the latest AIMessage,
8
- * computes incremental cost, accumulates a running total. Injects
9
- * a warning SystemMessage at ~80% and a termination message at 100%.
14
+ * Until S3 M2b (Q-S3-3) this module was `cost-cap.ts` and did both: it also
15
+ * blocked every tool call once the running cost passed the cap and gave the
16
+ * model one tool-free round to summarize. That second enforcement lived
17
+ * inside the graph, where the runtime could not see it, and a platform STOP
18
+ * had a sibling (`graceful-stop.ts`, deleted with it) that let the run spend
19
+ * a summary round after the platform said stop. Both went when the runtime
20
+ * became the one place a turn is stopped (harness runtime program, S3).
10
21
  *
11
- * wrapToolCallblocks all tool execution when the budget is exceeded,
12
- * giving the model one final tool-free round to produce a summary.
22
+ * afterModelreads `usage_metadata` off the latest AIMessage, prices the
23
+ * call at the parent's rates (a sub-agent on its own model is priced the
24
+ * same way the enforcement prices it; per-sub-agent pricing is an S5 item),
25
+ * accumulates the running total, and injects the warning SystemMessage once
26
+ * at `warningPct` of the cap.
13
27
  *
14
- * Only injected when maxCostUsd > 0 is explicitly configured.
28
+ * forSubAgent a view that shares the running total, so a sub-agent's calls
29
+ * advance the same figure the parent's warning reads.
30
+ *
31
+ * Only built when `max_cost_usd > 0` is explicitly configured.
15
32
  */
16
33
 
17
- import { ToolMessage, SystemMessage } from "@langchain/core/messages";
18
- import type { StigmerMiddleware, CostCapConfig } from "./types.js";
34
+ import { SystemMessage } from "@langchain/core/messages";
35
+ import type { StigmerMiddleware, CostAdvisoryConfig } from "./types.js";
19
36
 
20
37
  const DEFAULT_WARNING_PCT = 80;
21
38
  const MIN_WARNING_PCT = 50;
22
39
  const MAX_WARNING_PCT = 95;
23
40
 
24
- export interface CostCapMiddleware extends StigmerMiddleware {
41
+ export interface CostAdvisoryMiddleware extends StigmerMiddleware {
42
+ /** The running estimate this advisory has priced, in USD. */
25
43
  readonly runningCost: number;
26
- readonly exceeded: boolean;
27
44
  forSubAgent(): StigmerMiddleware;
28
45
  }
29
46
 
@@ -49,7 +66,7 @@ function extractUsage(aiMessage: Record<string, unknown>): {
49
66
  return { totalInput, output, cacheRead };
50
67
  }
51
68
 
52
- export function createCostCapMiddleware(config: CostCapConfig): CostCapMiddleware {
69
+ export function createCostAdvisoryMiddleware(config: CostAdvisoryConfig): CostAdvisoryMiddleware {
53
70
  const {
54
71
  maxCostUsd,
55
72
  inputPricePerMillion,
@@ -65,7 +82,6 @@ export function createCostCapMiddleware(config: CostCapConfig): CostCapMiddlewar
65
82
 
66
83
  let runningCost = 0;
67
84
  let warned = false;
68
- let exceeded = false;
69
85
  let modelCallCount = 0;
70
86
 
71
87
  function computeCallCost(totalInput: number, output: number, cacheRead: number): number {
@@ -99,21 +115,7 @@ export function createCostCapMiddleware(config: CostCapConfig): CostCapMiddlewar
99
115
  });
100
116
  }
101
117
 
102
- function createExceededMessage(): SystemMessage {
103
- return new SystemMessage({
104
- content:
105
- `Budget exceeded: This execution has consumed ` +
106
- `$${runningCost.toFixed(4)}, exceeding the ` +
107
- `$${maxCostUsd.toFixed(2)} budget. ` +
108
- `All tool calls are now blocked. ` +
109
- `Respond with a summary of what you accomplished and ` +
110
- `what work remains so the user can continue.`,
111
- });
112
- }
113
-
114
118
  function processAfterModel(state: Record<string, unknown>): { messages: SystemMessage[] } | void {
115
- if (exceeded) return;
116
-
117
119
  const messages = (state.messages ?? []) as unknown[];
118
120
  let lastAi: Record<string, unknown> | null = null;
119
121
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -128,57 +130,28 @@ export function createCostCapMiddleware(config: CostCapConfig): CostCapMiddlewar
128
130
  const { totalInput, output, cacheRead } = extractUsage(lastAi);
129
131
  if (totalInput === 0 && output === 0) return;
130
132
 
131
- const callCost = computeCallCost(totalInput, output, cacheRead);
132
- runningCost += callCost;
133
+ runningCost += computeCallCost(totalInput, output, cacheRead);
133
134
  modelCallCount++;
134
135
 
135
136
  const warningThreshold = maxCostUsd * warningPct / 100;
136
-
137
- if (runningCost >= maxCostUsd) {
138
- exceeded = true;
139
- console.warn(
140
- `[CostCap] EXCEEDED: $${runningCost.toFixed(4)} >= $${maxCostUsd.toFixed(2)} ` +
141
- `after ${modelCallCount} calls. Tool execution will be blocked.`,
142
- );
143
- return { messages: [createExceededMessage()] };
144
- }
145
-
146
137
  if (!warned && runningCost >= warningThreshold) {
147
138
  warned = true;
148
139
  console.warn(
149
- `[CostCap] WARNING: $${runningCost.toFixed(4)} >= $${warningThreshold.toFixed(4)} ` +
140
+ `[CostAdvisory] WARNING: $${runningCost.toFixed(4)} >= $${warningThreshold.toFixed(4)} ` +
150
141
  `(${warningPct}% of $${maxCostUsd.toFixed(2)}) after ${modelCallCount} calls.`,
151
142
  );
152
143
  return { messages: [createWarningMessage()] };
153
144
  }
154
145
  }
155
146
 
156
- async function processWrapToolCall(
157
- request: Parameters<NonNullable<StigmerMiddleware["wrapToolCall"]>>[0],
158
- handler: Parameters<NonNullable<StigmerMiddleware["wrapToolCall"]>>[1],
159
- ) {
160
- if (exceeded) {
161
- return new ToolMessage({
162
- content:
163
- `[Budget exceeded: tool execution blocked. ` +
164
- `Summarize your progress for the user.]`,
165
- tool_call_id: request.toolCall.id,
166
- name: request.toolCall.name,
167
- });
168
- }
169
- return await handler(request);
170
- }
171
-
172
- const middleware: CostCapMiddleware = {
173
- name: "CostCapMiddleware",
147
+ const middleware: CostAdvisoryMiddleware = {
148
+ name: "CostAdvisoryMiddleware",
174
149
 
175
150
  get runningCost() { return runningCost; },
176
- get exceeded() { return exceeded; },
177
151
 
178
152
  beforeAgent() {
179
153
  runningCost = 0;
180
154
  warned = false;
181
- exceeded = false;
182
155
  modelCallCount = 0;
183
156
  },
184
157
 
@@ -186,25 +159,22 @@ export function createCostCapMiddleware(config: CostCapConfig): CostCapMiddlewar
186
159
  return processAfterModel(state);
187
160
  },
188
161
 
189
- wrapToolCall: processWrapToolCall,
190
-
191
162
  afterAgent() {
192
163
  const pctUsed = maxCostUsd > 0
193
164
  ? (runningCost / maxCostUsd * 100)
194
165
  : 0;
195
166
  console.log(
196
- `[CostCap] Summary: $${runningCost.toFixed(4)} of $${maxCostUsd.toFixed(2)} ` +
197
- `(~${pctUsed.toFixed(0)}%) across ${modelCallCount} calls, ` +
198
- `warned=${warned}, exceeded=${exceeded}`,
167
+ `[CostAdvisory] Summary: $${runningCost.toFixed(4)} of $${maxCostUsd.toFixed(2)} ` +
168
+ `(~${pctUsed.toFixed(0)}%) across ${modelCallCount} calls, warned=${warned}`,
199
169
  );
200
170
  },
201
171
 
202
172
  forSubAgent(): StigmerMiddleware {
203
173
  return {
204
- name: "CostCapSubAgentView",
205
- // Sub-agent view does NOT reset state on beforeAgent
174
+ name: "CostAdvisorySubAgentView",
175
+ // The view does NOT reset the running total on beforeAgent: a
176
+ // sub-agent's calls advance the parent's figure.
206
177
  afterModel(state) { return processAfterModel(state); },
207
- wrapToolCall: processWrapToolCall,
208
178
  };
209
179
  },
210
180
  };
@@ -1,43 +1,53 @@
1
1
  /**
2
- * Middleware stack factory.
2
+ * Middleware stack factory for the native harness's root graph.
3
3
  *
4
- * Assembles the ordered middleware array from a MiddlewareStackConfig.
5
- * The composition order matches the Python create_deep_agent injection:
4
+ * Assembles the ordered middleware array from a MiddlewareStackConfig. The
5
+ * order is load-bearing and reads top to bottom:
6
6
  *
7
7
  * 0. Path normalization (conditional: only on permission-rule-bearing
8
8
  * graphs — FIRST, so every downstream middleware observes canonical
9
9
  * workspace-absolute paths)
10
10
  * 1. Loop detection (always)
11
- * 2. Execution budget (always)
11
+ * 2. Execution budget (always — advises at ~80% of the tool-round budget
12
+ * that LangGraph's `recursionLimit` enforces)
12
13
  * 3. Tool intent (always — bind-time shell schema extension, issue #276)
13
14
  * 4. Tool truncation (always)
14
- * 5. Graceful stop (always, inert until activated)
15
- * 6. Cost cap (conditional: only when maxCostUsd > 0)
15
+ * 5. Approval gate (conditional: absent under the global bypass)
16
+ * 6. Cost advisory (conditional: only when maxCostUsd > 0 — advises at
17
+ * ~80% of the cap that the turn runtime enforces)
16
18
  * 7. Error hints (always)
17
19
  * 8. OTel spans (always, no-op when OTel not configured)
20
+ *
21
+ * Every middleware here either shapes the graph's tool surface or ADVISES
22
+ * the model; none of them stops the run. A stop — the platform's, the
23
+ * cost cap's, a stall's — is the turn runtime's, delivered to the graph
24
+ * through its abort signal (harness runtime program, S3 Q-S3-3; until M2b a
25
+ * graceful-stop middleware and the cost cap's tool block were second stops
26
+ * inside the graph that the runtime could not see).
27
+ *
28
+ * The sub-agent stack is `execute-deep-agent/subagent-wiring.ts`'s: the same
29
+ * order over the parent's shared instances where one exists (the advisory's
30
+ * running total), fresh ones otherwise.
18
31
  */
19
32
 
20
33
  import type { StigmerMiddleware, MiddlewareStackConfig } from "./types.js";
21
- import type { GracefulStopMiddleware } from "./graceful-stop.js";
22
34
  import { createPathNormalizationMiddleware } from "./path-normalization.js";
23
35
  import { createLoopDetectionMiddleware } from "./loop-detection.js";
24
36
  import { createExecutionBudgetMiddleware } from "./execution-budget.js";
25
37
  import { createToolIntentMiddleware } from "./tool-intent.js";
26
38
  import { createToolTruncationMiddleware } from "./tool-truncation.js";
27
- import { createGracefulStopMiddleware } from "./graceful-stop.js";
28
39
  import { createApprovalGateMiddleware } from "./approval-gate.js";
29
- import { createCostCapMiddleware, type CostCapMiddleware } from "./cost-cap.js";
40
+ import { createCostAdvisoryMiddleware, type CostAdvisoryMiddleware } from "./cost-advisory.js";
30
41
  import { createErrorHintsMiddleware } from "./error-hints.js";
31
42
  import { createOtelSpansMiddleware } from "./otel-spans.js";
32
43
 
33
- export type { GracefulStopMiddleware } from "./graceful-stop.js";
34
- export type { CostCapMiddleware } from "./cost-cap.js";
44
+ export type { CostAdvisoryMiddleware } from "./cost-advisory.js";
35
45
  export type { StigmerMiddleware, MiddlewareStackConfig } from "./types.js";
36
46
 
37
47
  export interface MiddlewareStackResult {
38
48
  readonly middleware: StigmerMiddleware[];
39
- readonly gracefulStop: GracefulStopMiddleware;
40
- readonly costCap: CostCapMiddleware | null;
49
+ /** The advisory's instance, for the sub-agent stacks to share its running total; null when no cap is configured. */
50
+ readonly costAdvisory: CostAdvisoryMiddleware | null;
41
51
  }
42
52
 
43
53
  export function buildMiddlewareStack(
@@ -50,32 +60,22 @@ export function buildMiddlewareStack(
50
60
  }
51
61
 
52
62
  stack.push(createLoopDetectionMiddleware(config.loopDetection));
53
-
54
63
  stack.push(createExecutionBudgetMiddleware(config.executionBudget));
55
-
56
64
  stack.push(createToolIntentMiddleware());
57
-
58
65
  stack.push(createToolTruncationMiddleware(config.toolTruncation));
59
66
 
60
- const gracefulStop = createGracefulStopMiddleware();
61
- stack.push(gracefulStop);
62
-
63
67
  if (config.approvalGate) {
64
68
  stack.push(createApprovalGateMiddleware(config.approvalGate));
65
69
  }
66
70
 
67
- if (config.costCap && config.costCap.maxCostUsd > 0) {
68
- const costCapMiddleware = createCostCapMiddleware(config.costCap);
69
- stack.push(costCapMiddleware);
70
-
71
- stack.push(createErrorHintsMiddleware());
72
- stack.push(createOtelSpansMiddleware(config.otelSpans));
73
-
74
- return { middleware: stack, gracefulStop, costCap: costCapMiddleware };
75
- }
71
+ const costAdvisory =
72
+ config.costAdvisory && config.costAdvisory.maxCostUsd > 0
73
+ ? createCostAdvisoryMiddleware(config.costAdvisory)
74
+ : null;
75
+ if (costAdvisory) stack.push(costAdvisory);
76
76
 
77
77
  stack.push(createErrorHintsMiddleware());
78
78
  stack.push(createOtelSpansMiddleware(config.otelSpans));
79
79
 
80
- return { middleware: stack, gracefulStop, costCap: null };
80
+ return { middleware: stack, costAdvisory };
81
81
  }
@@ -82,7 +82,8 @@ export interface ToolTruncationConfig {
82
82
  readonly onTruncation?: (toolName: string, charsTruncated: number) => void;
83
83
  }
84
84
 
85
- export interface CostCapConfig {
85
+ /** The cap the advisory warns toward; the turn runtime enforces it (`shared/cost-guard.ts`). */
86
+ export interface CostAdvisoryConfig {
86
87
  readonly maxCostUsd: number;
87
88
  readonly inputPricePerMillion: number;
88
89
  readonly outputPricePerMillion: number;
@@ -113,7 +114,7 @@ export interface MiddlewareStackConfig {
113
114
  readonly loopDetection?: Partial<LoopDetectionConfig>;
114
115
  readonly executionBudget?: Partial<ExecutionBudgetConfig>;
115
116
  readonly toolTruncation?: Partial<ToolTruncationConfig>;
116
- readonly costCap?: CostCapConfig | null;
117
+ readonly costAdvisory?: CostAdvisoryConfig | null;
117
118
  readonly otelSpans?: Partial<OtelSpansConfig>;
118
119
  readonly approvalGate?: ApprovalGateConfig | null;
119
120
  /**