@graphrefly/graphrefly 0.47.1 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/dist/base/composition/index.cjs +28 -19
  2. package/dist/base/composition/index.cjs.map +1 -1
  3. package/dist/base/composition/index.d.cts +14 -5
  4. package/dist/base/composition/index.d.ts +14 -5
  5. package/dist/base/composition/index.js +9 -9
  6. package/dist/base/index.cjs +294 -164
  7. package/dist/base/index.cjs.map +1 -1
  8. package/dist/base/index.d.cts +2 -2
  9. package/dist/base/index.d.ts +2 -2
  10. package/dist/base/index.js +77 -72
  11. package/dist/base/io/index.cjs +145 -85
  12. package/dist/base/io/index.cjs.map +1 -1
  13. package/dist/base/io/index.d.cts +32 -5
  14. package/dist/base/io/index.d.ts +32 -5
  15. package/dist/base/io/index.js +5 -5
  16. package/dist/base/mutation/index.cjs +21 -0
  17. package/dist/base/mutation/index.cjs.map +1 -1
  18. package/dist/base/mutation/index.d.cts +23 -1
  19. package/dist/base/mutation/index.d.ts +23 -1
  20. package/dist/base/mutation/index.js +3 -1
  21. package/dist/base/sources/browser/index.cjs +18 -12
  22. package/dist/base/sources/browser/index.cjs.map +1 -1
  23. package/dist/base/sources/browser/index.d.cts +20 -2
  24. package/dist/base/sources/browser/index.d.ts +20 -2
  25. package/dist/base/sources/browser/index.js +18 -12
  26. package/dist/base/sources/browser/index.js.map +1 -1
  27. package/dist/base/sources/event/index.cjs +29 -1
  28. package/dist/base/sources/event/index.cjs.map +1 -1
  29. package/dist/base/sources/event/index.d.cts +67 -3
  30. package/dist/base/sources/event/index.d.ts +67 -3
  31. package/dist/base/sources/event/index.js +5 -2
  32. package/dist/base/sources/index.cjs +96 -50
  33. package/dist/base/sources/index.cjs.map +1 -1
  34. package/dist/base/sources/index.d.cts +1 -1
  35. package/dist/base/sources/index.d.ts +1 -1
  36. package/dist/base/sources/index.js +7 -4
  37. package/dist/base/sources/node/index.cjs +43 -37
  38. package/dist/base/sources/node/index.cjs.map +1 -1
  39. package/dist/base/sources/node/index.js +43 -37
  40. package/dist/base/sources/node/index.js.map +1 -1
  41. package/dist/{chunk-J5WFUEO4.js → chunk-23MAWVOJ.js} +3 -3
  42. package/dist/{chunk-YXCPV26R.js → chunk-3REMCHSS.js} +39 -27
  43. package/dist/chunk-3REMCHSS.js.map +1 -0
  44. package/dist/{chunk-CEVNQ74M.js → chunk-3YGXPUHW.js} +2 -2
  45. package/dist/{chunk-CEVNQ74M.js.map → chunk-3YGXPUHW.js.map} +1 -1
  46. package/dist/{chunk-EVYY4X5A.js → chunk-46X2EFQH.js} +16 -5
  47. package/dist/chunk-46X2EFQH.js.map +1 -0
  48. package/dist/{chunk-NY2PYHNC.js → chunk-5UY3PNFY.js} +12 -5
  49. package/dist/chunk-5UY3PNFY.js.map +1 -0
  50. package/dist/{chunk-RGMTUZCL.js → chunk-65OM4XLQ.js} +50 -4
  51. package/dist/chunk-65OM4XLQ.js.map +1 -0
  52. package/dist/{chunk-3PSLNJDU.js → chunk-6DQYBIHW.js} +314 -49
  53. package/dist/chunk-6DQYBIHW.js.map +1 -0
  54. package/dist/{chunk-LDCSZ72P.js → chunk-6YBER5UP.js} +3 -3
  55. package/dist/{chunk-LDCSZ72P.js.map → chunk-6YBER5UP.js.map} +1 -1
  56. package/dist/{chunk-7EGRP2VX.js → chunk-7BULJTL6.js} +2 -2
  57. package/dist/{chunk-7EGRP2VX.js.map → chunk-7BULJTL6.js.map} +1 -1
  58. package/dist/{chunk-VLAGJZSL.js → chunk-7T7WLEPM.js} +25 -4
  59. package/dist/chunk-7T7WLEPM.js.map +1 -0
  60. package/dist/{chunk-PKPO3JTZ.js → chunk-AQAKDE7F.js} +29 -11
  61. package/dist/chunk-AQAKDE7F.js.map +1 -0
  62. package/dist/{chunk-2OB3CEJS.js → chunk-B5Y5GPD5.js} +2 -2
  63. package/dist/{chunk-BXGZFGZ4.js → chunk-C5QD5DQX.js} +22 -1
  64. package/dist/chunk-C5QD5DQX.js.map +1 -0
  65. package/dist/{chunk-4XCHZRUJ.js → chunk-D5YGR4TP.js} +58 -7
  66. package/dist/chunk-D5YGR4TP.js.map +1 -0
  67. package/dist/{chunk-NPRP3MCV.js → chunk-DHDCOOJU.js} +2 -2
  68. package/dist/chunk-DHDCOOJU.js.map +1 -0
  69. package/dist/{chunk-MTTRCEJT.js → chunk-DVTDF5OI.js} +2 -2
  70. package/dist/{chunk-SOOKUYVM.js → chunk-F7EKHR32.js} +13 -9
  71. package/dist/chunk-F7EKHR32.js.map +1 -0
  72. package/dist/{chunk-A7KV5UK4.js → chunk-G7H6PN7P.js} +2 -2
  73. package/dist/{chunk-OCUDSN63.js → chunk-GGKHHG5Y.js} +110 -64
  74. package/dist/chunk-GGKHHG5Y.js.map +1 -0
  75. package/dist/{chunk-RAGGHLCV.js → chunk-GUNIRPEJ.js} +8 -6
  76. package/dist/{chunk-RAGGHLCV.js.map → chunk-GUNIRPEJ.js.map} +1 -1
  77. package/dist/{chunk-YJ4U2D2C.js → chunk-J5TBZFBD.js} +9 -7
  78. package/dist/chunk-J5TBZFBD.js.map +1 -0
  79. package/dist/{chunk-Y52CS6YA.js → chunk-JA67ZQG2.js} +2 -2
  80. package/dist/{chunk-Y52CS6YA.js.map → chunk-JA67ZQG2.js.map} +1 -1
  81. package/dist/{chunk-U225SKB4.js → chunk-K4ZYJ4EM.js} +569 -424
  82. package/dist/chunk-K4ZYJ4EM.js.map +1 -0
  83. package/dist/{chunk-Z4YXAUDN.js → chunk-KUFXLAEY.js} +11 -7
  84. package/dist/{chunk-Z4YXAUDN.js.map → chunk-KUFXLAEY.js.map} +1 -1
  85. package/dist/{chunk-IHTWQEDR.js → chunk-LTSI7ULC.js} +3 -3
  86. package/dist/{chunk-IHTWQEDR.js.map → chunk-LTSI7ULC.js.map} +1 -1
  87. package/dist/{chunk-DKNHAICT.js → chunk-MMHGYX44.js} +25 -9
  88. package/dist/chunk-MMHGYX44.js.map +1 -0
  89. package/dist/{chunk-K7PDZYQE.js → chunk-MQMTRKY3.js} +129 -50
  90. package/dist/chunk-MQMTRKY3.js.map +1 -0
  91. package/dist/{chunk-42FQ27MQ.js → chunk-MTODGQBR.js} +44 -179
  92. package/dist/chunk-MTODGQBR.js.map +1 -0
  93. package/dist/{chunk-O3MT7DYI.js → chunk-N6MNJNHB.js} +2 -2
  94. package/dist/{chunk-FVINAAKA.js → chunk-NBK6QQMG.js} +14 -13
  95. package/dist/{chunk-FVINAAKA.js.map → chunk-NBK6QQMG.js.map} +1 -1
  96. package/dist/{chunk-DM4OMPWK.js → chunk-NSA5K5G2.js} +2 -2
  97. package/dist/{chunk-MLTPJMH6.js → chunk-QQYULEZL.js} +2 -2
  98. package/dist/chunk-QSW4DFKE.js +31 -0
  99. package/dist/chunk-QSW4DFKE.js.map +1 -0
  100. package/dist/{chunk-PZWISPIQ.js → chunk-S7HN5FHL.js} +17 -11
  101. package/dist/chunk-S7HN5FHL.js.map +1 -0
  102. package/dist/{chunk-4S53H2KR.js → chunk-SUNCHMML.js} +2 -2
  103. package/dist/{chunk-4GYMCUDZ.js → chunk-T2U6N3FV.js} +7 -7
  104. package/dist/{chunk-RJOG4IJU.js → chunk-T5URUIIY.js} +50 -35
  105. package/dist/chunk-T5URUIIY.js.map +1 -0
  106. package/dist/{chunk-B4AKFXGE.js → chunk-TPTZZV25.js} +6 -6
  107. package/dist/chunk-TPTZZV25.js.map +1 -0
  108. package/dist/{chunk-BU3SEFA5.js → chunk-V46JWFGV.js} +7 -6
  109. package/dist/chunk-V46JWFGV.js.map +1 -0
  110. package/dist/{chunk-IJRR6YAI.js → chunk-VLDRAMP7.js} +18 -12
  111. package/dist/chunk-VLDRAMP7.js.map +1 -0
  112. package/dist/{chunk-6XZYT4SW.js → chunk-X6ESZDR6.js} +8 -9
  113. package/dist/chunk-X6ESZDR6.js.map +1 -0
  114. package/dist/{chunk-E5OZPDIW.js → chunk-X7BA5PWG.js} +7 -5
  115. package/dist/chunk-X7BA5PWG.js.map +1 -0
  116. package/dist/{chunk-CXANAIZU.js → chunk-XEWV254I.js} +3 -3
  117. package/dist/{chunk-CXANAIZU.js.map → chunk-XEWV254I.js.map} +1 -1
  118. package/dist/{chunk-V4Y3TM7U.js → chunk-YBJVKMTM.js} +38 -16
  119. package/dist/chunk-YBJVKMTM.js.map +1 -0
  120. package/dist/{chunk-7ADWWI2T.js → chunk-ZW32BPXV.js} +17 -6
  121. package/dist/chunk-ZW32BPXV.js.map +1 -0
  122. package/dist/compat/index.cjs +52 -5
  123. package/dist/compat/index.cjs.map +1 -1
  124. package/dist/compat/index.d.cts +1 -1
  125. package/dist/compat/index.d.ts +1 -1
  126. package/dist/compat/index.js +7 -7
  127. package/dist/compat/nestjs/index.cjs +52 -5
  128. package/dist/compat/nestjs/index.cjs.map +1 -1
  129. package/dist/compat/nestjs/index.d.cts +1 -1
  130. package/dist/compat/nestjs/index.d.ts +1 -1
  131. package/dist/compat/nestjs/index.js +4 -4
  132. package/dist/{fallback-Bx46zqky.d.cts → fallback-BROR6ZhO.d.cts} +1 -1
  133. package/dist/{fallback-pIWW8A2d.d.ts → fallback-DO80aM_3.d.ts} +1 -1
  134. package/dist/{index-B_p8tnvf.d.cts → index-D1z3XcF9.d.cts} +1 -0
  135. package/dist/{index-_HDSmPyp.d.ts → index-DZ6yua0Q.d.ts} +1 -0
  136. package/dist/index.cjs +2387 -1707
  137. package/dist/index.cjs.map +1 -1
  138. package/dist/index.d.cts +10 -10
  139. package/dist/index.d.ts +10 -10
  140. package/dist/index.js +173 -150
  141. package/dist/index.js.map +1 -1
  142. package/dist/presets/ai/index.cjs +88 -26
  143. package/dist/presets/ai/index.cjs.map +1 -1
  144. package/dist/presets/ai/index.js +14 -14
  145. package/dist/presets/harness/index.cjs +183 -51
  146. package/dist/presets/harness/index.cjs.map +1 -1
  147. package/dist/presets/harness/index.d.cts +15 -5
  148. package/dist/presets/harness/index.d.ts +15 -5
  149. package/dist/presets/harness/index.js +26 -26
  150. package/dist/presets/index.cjs +298 -101
  151. package/dist/presets/index.cjs.map +1 -1
  152. package/dist/presets/index.d.cts +2 -2
  153. package/dist/presets/index.d.ts +2 -2
  154. package/dist/presets/index.js +49 -49
  155. package/dist/presets/inspect/index.cjs +63 -14
  156. package/dist/presets/inspect/index.cjs.map +1 -1
  157. package/dist/presets/inspect/index.d.cts +1 -1
  158. package/dist/presets/inspect/index.d.ts +1 -1
  159. package/dist/presets/inspect/index.js +6 -6
  160. package/dist/presets/resilience/index.cjs +64 -44
  161. package/dist/presets/resilience/index.cjs.map +1 -1
  162. package/dist/presets/resilience/index.d.cts +12 -8
  163. package/dist/presets/resilience/index.d.ts +12 -8
  164. package/dist/presets/resilience/index.js +6 -6
  165. package/dist/{rate-limiter-DpVbSYdH.d.cts → rate-limiter-DC26FM8J.d.cts} +10 -1
  166. package/dist/{rate-limiter-CEALq4N1.d.ts → rate-limiter-DyWpwpQP.d.ts} +10 -1
  167. package/dist/{reactive-layout-fswlBUvX.d.ts → reactive-layout-BBBWH0V_.d.cts} +85 -4
  168. package/dist/{reactive-layout-fswlBUvX.d.cts → reactive-layout-BBBWH0V_.d.ts} +85 -4
  169. package/dist/solutions/index.cjs +239 -92
  170. package/dist/solutions/index.cjs.map +1 -1
  171. package/dist/solutions/index.d.cts +2 -2
  172. package/dist/solutions/index.d.ts +2 -2
  173. package/dist/solutions/index.js +32 -32
  174. package/dist/{spawnable-5mDY501F.d.cts → spawnable-B2IlW60f.d.cts} +23 -2
  175. package/dist/{spawnable-D3lR0oQu.d.ts → spawnable-tttFz2Nh.d.ts} +23 -2
  176. package/dist/testing/index.cjs +94 -0
  177. package/dist/testing/index.cjs.map +1 -0
  178. package/dist/testing/index.d.cts +59 -0
  179. package/dist/testing/index.d.ts +59 -0
  180. package/dist/testing/index.js +73 -0
  181. package/dist/testing/index.js.map +1 -0
  182. package/dist/{timeout-U5O4ESK3.js → timeout-BEABACRP.js} +2 -2
  183. package/dist/utils/ai/browser.cjs.map +1 -1
  184. package/dist/utils/ai/browser.d.cts +2 -2
  185. package/dist/utils/ai/browser.d.ts +2 -2
  186. package/dist/utils/ai/browser.js +10 -10
  187. package/dist/utils/ai/browser.js.map +1 -1
  188. package/dist/utils/ai/index.cjs +291 -191
  189. package/dist/utils/ai/index.cjs.map +1 -1
  190. package/dist/utils/ai/index.d.cts +108 -12
  191. package/dist/utils/ai/index.d.ts +108 -12
  192. package/dist/utils/ai/index.js +23 -21
  193. package/dist/utils/ai/node.cjs.map +1 -1
  194. package/dist/utils/ai/node.d.cts +5 -5
  195. package/dist/utils/ai/node.d.ts +5 -5
  196. package/dist/utils/ai/node.js +3 -3
  197. package/dist/utils/ai/node.js.map +1 -1
  198. package/dist/utils/cqrs/index.cjs +29 -3
  199. package/dist/utils/cqrs/index.cjs.map +1 -1
  200. package/dist/utils/cqrs/index.d.cts +12 -7
  201. package/dist/utils/cqrs/index.d.ts +12 -7
  202. package/dist/utils/cqrs/index.js +2 -2
  203. package/dist/utils/demo-shell/index.cjs +45 -19
  204. package/dist/utils/demo-shell/index.cjs.map +1 -1
  205. package/dist/utils/demo-shell/index.d.cts +1 -1
  206. package/dist/utils/demo-shell/index.d.ts +1 -1
  207. package/dist/utils/demo-shell/index.js +2 -2
  208. package/dist/utils/domain-templates/index.cjs +1 -1
  209. package/dist/utils/domain-templates/index.cjs.map +1 -1
  210. package/dist/utils/domain-templates/index.js +3 -3
  211. package/dist/utils/graphspec/index.cjs +1 -1
  212. package/dist/utils/graphspec/index.cjs.map +1 -1
  213. package/dist/utils/graphspec/index.js +3 -3
  214. package/dist/utils/harness/index.cjs +16 -10
  215. package/dist/utils/harness/index.cjs.map +1 -1
  216. package/dist/utils/harness/index.js +1 -1
  217. package/dist/utils/index.cjs +1692 -1192
  218. package/dist/utils/index.cjs.map +1 -1
  219. package/dist/utils/index.d.cts +7 -7
  220. package/dist/utils/index.d.ts +7 -7
  221. package/dist/utils/index.js +77 -59
  222. package/dist/utils/inspect/index.cjs +52 -4
  223. package/dist/utils/inspect/index.cjs.map +1 -1
  224. package/dist/utils/inspect/index.d.cts +32 -3
  225. package/dist/utils/inspect/index.d.ts +32 -3
  226. package/dist/utils/inspect/index.js +4 -4
  227. package/dist/utils/job-queue/index.cjs +46 -9
  228. package/dist/utils/job-queue/index.cjs.map +1 -1
  229. package/dist/utils/job-queue/index.d.cts +33 -3
  230. package/dist/utils/job-queue/index.d.ts +33 -3
  231. package/dist/utils/job-queue/index.js +2 -2
  232. package/dist/utils/memory/index.cjs +570 -425
  233. package/dist/utils/memory/index.cjs.map +1 -1
  234. package/dist/utils/memory/index.d.cts +261 -33
  235. package/dist/utils/memory/index.d.ts +261 -33
  236. package/dist/utils/memory/index.js +10 -2
  237. package/dist/utils/messaging/index.cjs.map +1 -1
  238. package/dist/utils/messaging/index.d.cts +4 -3
  239. package/dist/utils/messaging/index.d.ts +4 -3
  240. package/dist/utils/messaging/index.js +2 -2
  241. package/dist/utils/orchestration/index.cjs +14 -3
  242. package/dist/utils/orchestration/index.cjs.map +1 -1
  243. package/dist/utils/orchestration/index.js +3 -3
  244. package/dist/utils/process/index.cjs +32 -2
  245. package/dist/utils/process/index.cjs.map +1 -1
  246. package/dist/utils/process/index.d.cts +4 -3
  247. package/dist/utils/process/index.d.ts +4 -3
  248. package/dist/utils/process/index.js +3 -3
  249. package/dist/utils/reactive-layout/index.cjs +184 -55
  250. package/dist/utils/reactive-layout/index.cjs.map +1 -1
  251. package/dist/utils/reactive-layout/index.d.cts +128 -3
  252. package/dist/utils/reactive-layout/index.d.ts +128 -3
  253. package/dist/utils/reactive-layout/index.js +16 -8
  254. package/dist/utils/reduction/index.cjs +1 -1
  255. package/dist/utils/reduction/index.cjs.map +1 -1
  256. package/dist/utils/reduction/index.js +2 -2
  257. package/dist/utils/resilience/index.cjs +64 -43
  258. package/dist/utils/resilience/index.cjs.map +1 -1
  259. package/dist/utils/resilience/index.d.cts +1 -1
  260. package/dist/utils/resilience/index.d.ts +1 -1
  261. package/dist/utils/resilience/index.js +5 -5
  262. package/dist/utils/surface/index.cjs +1 -1
  263. package/dist/utils/surface/index.cjs.map +1 -1
  264. package/dist/utils/surface/index.js +4 -4
  265. package/package.json +15 -3
  266. package/dist/chunk-3PSLNJDU.js.map +0 -1
  267. package/dist/chunk-42FQ27MQ.js.map +0 -1
  268. package/dist/chunk-4XCHZRUJ.js.map +0 -1
  269. package/dist/chunk-6XZYT4SW.js.map +0 -1
  270. package/dist/chunk-7ADWWI2T.js.map +0 -1
  271. package/dist/chunk-B4AKFXGE.js.map +0 -1
  272. package/dist/chunk-BU3SEFA5.js.map +0 -1
  273. package/dist/chunk-BXGZFGZ4.js.map +0 -1
  274. package/dist/chunk-DKNHAICT.js.map +0 -1
  275. package/dist/chunk-E5OZPDIW.js.map +0 -1
  276. package/dist/chunk-EVYY4X5A.js.map +0 -1
  277. package/dist/chunk-IJRR6YAI.js.map +0 -1
  278. package/dist/chunk-K7PDZYQE.js.map +0 -1
  279. package/dist/chunk-NPRP3MCV.js.map +0 -1
  280. package/dist/chunk-NY2PYHNC.js.map +0 -1
  281. package/dist/chunk-OCUDSN63.js.map +0 -1
  282. package/dist/chunk-PKPO3JTZ.js.map +0 -1
  283. package/dist/chunk-PZWISPIQ.js.map +0 -1
  284. package/dist/chunk-RGMTUZCL.js.map +0 -1
  285. package/dist/chunk-RJOG4IJU.js.map +0 -1
  286. package/dist/chunk-SOOKUYVM.js.map +0 -1
  287. package/dist/chunk-U225SKB4.js.map +0 -1
  288. package/dist/chunk-V4Y3TM7U.js.map +0 -1
  289. package/dist/chunk-VLAGJZSL.js.map +0 -1
  290. package/dist/chunk-W2BOPXTI.js +0 -1
  291. package/dist/chunk-YJ4U2D2C.js.map +0 -1
  292. package/dist/chunk-YXCPV26R.js.map +0 -1
  293. package/dist/timeout-U5O4ESK3.js.map +0 -1
  294. /package/dist/{chunk-J5WFUEO4.js.map → chunk-23MAWVOJ.js.map} +0 -0
  295. /package/dist/{chunk-2OB3CEJS.js.map → chunk-B5Y5GPD5.js.map} +0 -0
  296. /package/dist/{chunk-MTTRCEJT.js.map → chunk-DVTDF5OI.js.map} +0 -0
  297. /package/dist/{chunk-A7KV5UK4.js.map → chunk-G7H6PN7P.js.map} +0 -0
  298. /package/dist/{chunk-O3MT7DYI.js.map → chunk-N6MNJNHB.js.map} +0 -0
  299. /package/dist/{chunk-DM4OMPWK.js.map → chunk-NSA5K5G2.js.map} +0 -0
  300. /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
  301. /package/dist/{chunk-4S53H2KR.js.map → chunk-SUNCHMML.js.map} +0 -0
  302. /package/dist/{chunk-4GYMCUDZ.js.map → chunk-T2U6N3FV.js.map} +0 -0
  303. /package/dist/{chunk-W2BOPXTI.js.map → timeout-BEABACRP.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/job-queue/index.ts"],"sourcesContent":["/**\n * Job queue patterns (roadmap §4.2).\n *\n * Queue / flow primitives modeled as graph factories:\n * - `jobQueue()` — claim/ack/nack workflow with reactive depth.\n * - `jobFlow()` — multi-stage queue chain.\n *\n * Topic / subscription / hub primitives live in `patterns/messaging`.\n */\n\nimport {\n\tbatch,\n\tDATA,\n\tERROR,\n\ttype Node,\n\tnode,\n\tplaceholderArgs,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport type { AppendLogStorageTier } from \"@graphrefly/pure-ts/extra\";\nimport {\n\tfromAny,\n\tkeepalive,\n\ttype NodeInput,\n\ttype ReactiveLogBundle,\n\treactiveList,\n\treactiveLog,\n\treactiveMap,\n} from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport {\n\ttype BaseAuditRecord,\n\tbumpCursor,\n\tcreateAuditLog,\n\tmutate,\n\tregisterCursor,\n} from \"../../base/mutation/index.js\";\n\nconst DEFAULT_MAX_PER_PUMP = 256;\nconst DEFAULT_COMPLETED_RETAINED_LIMIT = 1024;\n\nfunction requireNonNegativeInt(value: number, label: string): number {\n\tif (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {\n\t\tthrow new Error(`${label} must be a non-negative integer`);\n\t}\n\treturn value;\n}\n\nfunction jobQueueMeta(kind: string, extra?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"job_queue\", kind, extra);\n}\n\nexport type JobState = \"queued\" | \"inflight\";\n\nexport type JobEnvelope<T> = {\n\tid: string;\n\tpayload: T;\n\tattempts: number;\n\tmetadata: Readonly<Record<string, unknown>>;\n\tstate: JobState;\n};\n\n/** Audit record for a job-queue mutation (Audit 2 cross-cutting). */\nexport type JobEventAction = \"enqueue\" | \"claim\" | \"ack\" | \"nack\" | \"remove\";\n\nexport interface JobEvent<T = unknown> extends BaseAuditRecord {\n\treadonly action: JobEventAction;\n\treadonly id: string;\n\treadonly attempts?: number;\n\treadonly payload?: T;\n}\n\n/** Recommended `keyOf` for keyed-storage adapters (Audit 2 #7). */\nexport const jobEventKeyOf = <T>(e: JobEvent<T>): string => e.action;\n\nexport type JobQueueOptions = {\n\tgraph?: GraphOptions;\n};\n\nexport class JobQueueGraph<T> extends Graph {\n\tprivate readonly _pending;\n\tprivate readonly _jobs;\n\tprivate readonly _seqCursor: Node<number>;\n\treadonly pending: Node<readonly string[]>;\n\treadonly jobs: Node<ReadonlyMap<string, JobEnvelope<T>>>;\n\treadonly depth: Node<number>;\n\t/** Audit log of every queue mutation (Audit 2). */\n\treadonly events: ReactiveLogBundle<JobEvent<T>>;\n\t/** Alias for {@link JobQueueGraph.events} — Audit 2 `.audit` duplication. */\n\treadonly audit: ReactiveLogBundle<JobEvent<T>>;\n\n\t// Tier 8 / COMPOSITION-GUIDE §35: mutate wrappers for the four\n\t// single-record mutation methods. Assigned in the constructor (NOT via\n\t// class-field initializers) because field initializers run before the\n\t// constructor body — `this.events` and `this._seqCursor` aren't ready yet.\n\t// `claim` stays inline because it emits one record per claimed job.\n\tprivate readonly _enqueueImpl: (\n\t\tpayload: T,\n\t\topts: { id?: string; metadata?: Record<string, unknown> },\n\t) => string;\n\tprivate readonly _ackImpl: (id: string, job: JobEnvelope<T>) => void;\n\tprivate readonly _nackImpl: (id: string, job: JobEnvelope<T>, requeue: boolean) => void;\n\tprivate readonly _removeByIdImpl: (id: string, job: JobEnvelope<T>) => void;\n\n\tconstructor(name: string, opts: JobQueueOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\tthis._pending = reactiveList<string>([], { name: \"pending\" });\n\t\tthis._jobs = reactiveMap<string, JobEnvelope<T>>({ name: \"jobs\" });\n\t\tthis.pending = this._pending.items;\n\t\tthis.jobs = this._jobs.entries;\n\t\tthis.add(this.pending, { name: \"pending\" });\n\t\tthis.add(this.jobs, { name: \"jobs\" });\n\t\tthis.depth = node(\n\t\t\t[this.pending],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tactions.emit((data[0] as readonly string[]).length);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"depth\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: jobQueueMeta(\"queue_depth\"),\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t);\n\t\tthis.add(this.depth, { name: \"depth\" });\n\t\tthis.addDisposer(keepalive(this.depth));\n\n\t\tthis.events = createAuditLog<JobEvent<T>>({\n\t\t\tname: \"events\",\n\t\t\tretainedLimit: 1024,\n\t\t\tgraph: this,\n\t\t});\n\t\tthis.audit = this.events;\n\t\tthis._seqCursor = registerCursor(this, \"seq\", 0);\n\n\t\t// `freeze: false` everywhere because the payload may be large and\n\t\t// per-mutation cost matters on hot paths. mutate bumps `seq` via\n\t\t// the registered cursor BEFORE the action runs, so action bodies that\n\t\t// need the just-bumped value (e.g. enqueue's auto-id) read\n\t\t// `this._seqCursor.cache`.\n\t\tthis._enqueueImpl = mutate<\n\t\t\t[T, { id?: string; metadata?: Record<string, unknown> }],\n\t\t\tstring,\n\t\t\tJobEvent<T>\n\t\t>(\n\t\t\t(payload, enqueueOpts): string => {\n\t\t\t\tconst seq = this._seqCursor.cache as number;\n\t\t\t\tconst id = enqueueOpts.id ?? `${this.name}-${seq}`;\n\t\t\t\tif (this._jobs.get(id) !== undefined) {\n\t\t\t\t\tthrow new Error(`jobQueue(\"${this.name}\"): duplicate job id \"${id}\"`);\n\t\t\t\t}\n\t\t\t\tconst job: JobEnvelope<T> = {\n\t\t\t\t\tid,\n\t\t\t\t\tpayload,\n\t\t\t\t\tattempts: 0,\n\t\t\t\t\tmetadata: Object.freeze({ ...(enqueueOpts.metadata ?? {}) }),\n\t\t\t\t\tstate: \"queued\",\n\t\t\t\t};\n\t\t\t\tthis._jobs.set(id, job);\n\t\t\t\tthis._pending.append(id);\n\t\t\t\treturn id;\n\t\t\t},\n\t\t\t{\n\t\t\t\tframe: \"inline\",\n\t\t\t\tlog: this.events,\n\t\t\t\tseq: this._seqCursor,\n\t\t\t\tfreeze: false,\n\t\t\t\tonSuccessRecord: ([payload], id, { t_ns, seq }) => ({\n\t\t\t\t\taction: \"enqueue\",\n\t\t\t\t\tid,\n\t\t\t\t\tpayload,\n\t\t\t\t\tt_ns,\n\t\t\t\t\tseq: seq ?? 0,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\n\t\tthis._ackImpl = mutate<[string, JobEnvelope<T>], void, JobEvent<T>>(\n\t\t\t(id, _job): void => {\n\t\t\t\tthis._jobs.delete(id);\n\t\t\t},\n\t\t\t{\n\t\t\t\tframe: \"inline\",\n\t\t\t\tlog: this.events,\n\t\t\t\tseq: this._seqCursor,\n\t\t\t\tfreeze: false,\n\t\t\t\tonSuccessRecord: ([id, job], _r, { t_ns, seq }) => ({\n\t\t\t\t\taction: \"ack\",\n\t\t\t\t\tid,\n\t\t\t\t\tattempts: job.attempts,\n\t\t\t\t\tt_ns,\n\t\t\t\t\tseq: seq ?? 0,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\n\t\tthis._nackImpl = mutate<[string, JobEnvelope<T>, boolean], void, JobEvent<T>>(\n\t\t\t(id, job, requeue): void => {\n\t\t\t\tif (requeue) {\n\t\t\t\t\tthis._jobs.set(id, { ...job, state: \"queued\" });\n\t\t\t\t\tthis._pending.append(id);\n\t\t\t\t} else {\n\t\t\t\t\tthis._jobs.delete(id);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tframe: \"inline\",\n\t\t\t\tlog: this.events,\n\t\t\t\tseq: this._seqCursor,\n\t\t\t\tfreeze: false,\n\t\t\t\tonSuccessRecord: ([id, job], _r, { t_ns, seq }) => ({\n\t\t\t\t\taction: \"nack\",\n\t\t\t\t\tid,\n\t\t\t\t\tattempts: job.attempts,\n\t\t\t\t\tt_ns,\n\t\t\t\t\tseq: seq ?? 0,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\n\t\tthis._removeByIdImpl = mutate<[string, JobEnvelope<T>], void, JobEvent<T>>(\n\t\t\t(id, job): void => {\n\t\t\t\tif (job.state === \"queued\") {\n\t\t\t\t\tconst pending = this.pending.cache as readonly string[];\n\t\t\t\t\tconst idx = pending.indexOf(id);\n\t\t\t\t\tif (idx >= 0) this._pending.pop(idx);\n\t\t\t\t}\n\t\t\t\tthis._jobs.delete(id);\n\t\t\t},\n\t\t\t{\n\t\t\t\tframe: \"inline\",\n\t\t\t\tlog: this.events,\n\t\t\t\tseq: this._seqCursor,\n\t\t\t\tfreeze: false,\n\t\t\t\tonSuccessRecord: ([id, job], _r, { t_ns, seq }) => ({\n\t\t\t\t\taction: \"remove\",\n\t\t\t\t\tid,\n\t\t\t\t\tattempts: job.attempts,\n\t\t\t\t\tt_ns,\n\t\t\t\t\tseq: seq ?? 0,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Wire append-log storage tiers (Audit 4). Returns a disposer.\n\t *\n\t * Named `attachEventStorage` to avoid colliding with {@link Graph.attachSnapshotStorage}.\n\t */\n\tattachEventStorage(tiers: readonly AppendLogStorageTier<JobEvent<T>>[]): () => void {\n\t\treturn this.events.attachStorage(tiers);\n\t}\n\n\tenqueue(payload: T, opts: { id?: string; metadata?: Record<string, unknown> } = {}): string {\n\t\treturn this._enqueueImpl(payload, opts);\n\t}\n\n\tclaim(limit = 1): readonly JobEnvelope<T>[] {\n\t\tconst max = requireNonNegativeInt(limit, \"job queue claim limit\");\n\t\tif (max === 0) return [];\n\t\tconst out: JobEnvelope<T>[] = [];\n\t\twhile (out.length < max) {\n\t\t\tconst ids = this.pending.cache as readonly string[];\n\t\t\tif (ids.length === 0) break;\n\t\t\tconst id = this._pending.pop(0);\n\t\t\tconst job = this._jobs.get(id);\n\t\t\tif (!job || job.state !== \"queued\") continue;\n\t\t\tconst inflight: JobEnvelope<T> = {\n\t\t\t\t...job,\n\t\t\t\tstate: \"inflight\",\n\t\t\t\tattempts: job.attempts + 1,\n\t\t\t};\n\t\t\tthis._jobs.set(id, inflight);\n\t\t\tout.push(inflight);\n\t\t\t// claim emits one audit record per claimed job; mutate wraps a\n\t\t\t// single call → single record, so claim stays inline and bumps the\n\t\t\t// cursor directly via the shared `bumpCursor` helper.\n\t\t\tthis.events.append({\n\t\t\t\taction: \"claim\",\n\t\t\t\tid,\n\t\t\t\tattempts: inflight.attempts,\n\t\t\t\tt_ns: wallClockNs(),\n\t\t\t\tseq: bumpCursor(this._seqCursor),\n\t\t\t});\n\t\t}\n\t\treturn out;\n\t}\n\n\tack(id: string): boolean {\n\t\tconst job = this._jobs.get(id);\n\t\tif (!job || job.state !== \"inflight\") return false;\n\t\tthis._ackImpl(id, job);\n\t\treturn true;\n\t}\n\n\tnack(id: string, opts: { requeue?: boolean } = {}): boolean {\n\t\tconst job = this._jobs.get(id);\n\t\tif (!job || job.state !== \"inflight\") return false;\n\t\tthis._nackImpl(id, job, opts.requeue ?? true);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Remove a job by id regardless of its current state. Returns `true` if\n\t * the job existed and was removed, `false` if no job has this id.\n\t *\n\t * `ack` only works on inflight; `nack` only works on inflight.\n\t * `removeById` is the state-agnostic escape hatch — useful for\n\t * audit/observability layers that enqueue but never claim, and need to\n\t * finalize a job when an external decision (e.g. harness verify\n\t * outcome) resolves it. Distinct name from the inherited\n\t * {@link Graph.remove}, which removes a mounted child subgraph by path.\n\t *\n\t * When the job is in `queued` state, its id is also pulled from the\n\t * `pending` list — depth + pending snapshot stay consistent.\n\t */\n\tremoveById(id: string): boolean {\n\t\tconst job = this._jobs.get(id);\n\t\tif (!job) return false;\n\t\tthis._removeByIdImpl(id, job);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Subscribe to a Node and enqueue each DATA payload into this queue.\n\t * Returns a disposer that stops consuming when called.\n\t *\n\t * Used internally by {@link JobFlowGraph} for stage-to-stage wiring but\n\t * also useful for users wiring an external source into a job queue.\n\t *\n\t * @param source - Node whose DATA values are enqueued.\n\t * @param opts - Optional enqueue options (id generator, metadata prefix).\n\t */\n\tconsumeFrom(\n\t\tsource: Node<T>,\n\t\topts?: {\n\t\t\tmetadata?: Record<string, unknown>;\n\t\t},\n\t): () => void {\n\t\treturn source.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\tconst payload = m[1] as T;\n\t\t\t\tthis.enqueue(payload, opts ? { metadata: opts.metadata } : undefined);\n\t\t\t}\n\t\t});\n\t}\n}\n\n// ── StageDef ─────────────────────────────────────────────────────────────\n\n/**\n * Work function for a job flow stage. Receives the full job envelope and\n * an optional per-claim options object carrying an `AbortSignal`; returns\n * a `NodeInput<T>` — raw value (sync), Promise (async), or Node (composed\n * pipeline). `fromAny` coerces any of these shapes.\n *\n * On error / rejection: the stage nacks the job (no requeue by default).\n *\n * **Per-claim signal (Tier 6.5 2.5b, 2026-04-29).** The pump mints an\n * `AbortController` per claim and supplies its `signal` via `opts`. The\n * signal aborts when (a) the result node settles (first DATA / first\n * ERROR — auto-cleanup after the pump captures), OR (b) the pump itself\n * tears down (e.g. parent Graph `destroy()`). User-supplied work fns that\n * do long-running async (HTTP, LLM streams, evaluator subgraphs) can\n * forward this signal into `fetch({ signal })`, `adapter.invoke({ signal\n * })`, etc. for cooperative cancellation. Sync work fns ignore `opts` —\n * the second arg is optional, no behavior change for legacy callers.\n *\n * Mirrors the `LLMInvokeOptions.signal` / `apply(item, { signal })` /\n * tool-handler `(args, { signal })` precedents — same shape across the\n * library's user-callback boundaries.\n */\nexport type WorkFn<T> = (job: JobEnvelope<T>, opts?: { signal: AbortSignal }) => NodeInput<T>;\n\n/**\n * Stage definition for {@link JobFlowGraph}. Either a bare name string\n * (no work hook, pure pass-through) or a full definition object.\n */\nexport type StageDef<T> =\n\t| string\n\t| {\n\t\t\tname: string;\n\t\t\twork?: WorkFn<T>;\n\t\t\thandlerVersion?: { id: string; version: string | number };\n\t\t\t/**\n\t\t\t * Per-stage cap on `claim → work → ack` cycles per pump tick.\n\t\t\t * Overrides {@link JobFlowOptions.maxPerPump} for this stage. Useful\n\t\t\t * when stages have asymmetric cost (e.g. an LLM-execute stage capped\n\t\t\t * at 4 concurrent calls while a cheap verify stage runs unbounded).\n\t\t\t *\n\t\t\t * Falls back to the top-level `JobFlowOptions.maxPerPump`, which in\n\t\t\t * turn falls back to `DEFAULT_MAX_PER_PUMP` (256).\n\t\t\t */\n\t\t\tmaxPerPump?: number;\n\t\t\t/**\n\t\t\t * Per-stage cap on TOTAL concurrent inflight claims (Tier 6.5 3.1,\n\t\t\t * 2026-04-29). Distinct from {@link maxPerPump}: `maxPerPump` caps\n\t\t\t * claims per pump tick, while `maxInflight` caps the number of\n\t\t\t * unsettled in-flight claims at any moment across all ticks. Use\n\t\t\t * for rigorous LLM cost ceilings (e.g. \"no more than 4 concurrent\n\t\t\t * adapter.invoke calls regardless of pending depth\").\n\t\t\t *\n\t\t\t * When set, the stage mounts an internal `state(0)` counter as a\n\t\t\t * pump dep so the pump re-fires on each settle — pending items\n\t\t\t * resume claiming as soon as inflight drops below the cap.\n\t\t\t *\n\t\t\t * Unset (default): unbounded inflight, gated only by `maxPerPump`.\n\t\t\t */\n\t\t\tmaxInflight?: number;\n\t };\n\nexport type JobFlowOptions<T = unknown> = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Stage definitions. Each stage is either a bare name string (pure\n\t * pass-through) or a `{ name, work?, handlerVersion?, maxPerPump? }` object.\n\t *\n\t * For back-compat, `string[]` values behave as stages with no work hook.\n\t */\n\tstages?: readonly StageDef<T>[];\n\t/**\n\t * Default cap on claims per pump tick across all stages. Per-stage\n\t * overrides can be set on each {@link StageDef.maxPerPump}; if neither is\n\t * set, falls back to `DEFAULT_MAX_PER_PUMP` (256).\n\t */\n\tmaxPerPump?: number;\n};\n\nexport class JobFlowGraph<T> extends Graph {\n\tprivate readonly _stageNames: readonly string[];\n\tprivate readonly _stageWorkFns: ReadonlyMap<string, WorkFn<T>>;\n\tprivate readonly _queues = new Map<string, JobQueueGraph<T>>();\n\tprivate readonly _completed;\n\treadonly completed: Node<readonly JobEnvelope<T>[]>;\n\treadonly completedCount: Node<number>;\n\n\tconstructor(name: string, opts: JobFlowOptions<T> = {}) {\n\t\tsuper(name, opts.graph);\n\n\t\t// Normalise stage definitions.\n\t\tconst rawStages = opts.stages ?? ([\"incoming\", \"processing\", \"done\"] as readonly StageDef<T>[]);\n\t\tconst stageNames: string[] = [];\n\t\tconst stageWorkFns = new Map<string, WorkFn<T>>();\n\t\tconst stageMaxPerPump = new Map<string, number>();\n\t\tconst stageMaxInflight = new Map<string, number>();\n\n\t\tfor (const raw of rawStages) {\n\t\t\tconst stageName = typeof raw === \"string\" ? raw.trim() : raw.name.trim();\n\t\t\tif (typeof raw !== \"string\" && raw.work) {\n\t\t\t\tstageWorkFns.set(stageName, raw.work);\n\t\t\t}\n\t\t\tif (typeof raw !== \"string\" && raw.maxPerPump != null) {\n\t\t\t\tstageMaxPerPump.set(\n\t\t\t\t\tstageName,\n\t\t\t\t\tMath.max(\n\t\t\t\t\t\t1,\n\t\t\t\t\t\trequireNonNegativeInt(raw.maxPerPump, `job flow stage \"${stageName}\" maxPerPump`),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (typeof raw !== \"string\" && raw.maxInflight != null) {\n\t\t\t\tstageMaxInflight.set(\n\t\t\t\t\tstageName,\n\t\t\t\t\tMath.max(\n\t\t\t\t\t\t1,\n\t\t\t\t\t\trequireNonNegativeInt(raw.maxInflight, `job flow stage \"${stageName}\" maxInflight`),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tstageNames.push(stageName);\n\t\t}\n\n\t\tif (stageNames.length < 2) {\n\t\t\tthrow new Error(`jobFlow(\"${name}\"): requires at least 2 stages`);\n\t\t}\n\t\tconst unique = new Set(stageNames);\n\t\tif (unique.size !== stageNames.length) {\n\t\t\tthrow new Error(`jobFlow(\"${name}\"): stage names must be unique`);\n\t\t}\n\t\tthis._stageNames = Object.freeze([...stageNames]);\n\t\tthis._stageWorkFns = stageWorkFns;\n\n\t\tfor (const stage of this._stageNames) {\n\t\t\tconst q = jobQueue<T>(`${name}-${stage}`);\n\t\t\tthis._queues.set(stage, q);\n\t\t\tthis.mount(stage, q);\n\t\t}\n\n\t\tthis._completed = reactiveLog<JobEnvelope<T>>([], {\n\t\t\tname: \"completed\",\n\t\t\tmaxSize: DEFAULT_COMPLETED_RETAINED_LIMIT,\n\t\t});\n\t\tthis.completed = this._completed.entries;\n\t\tthis.add(this.completed, { name: \"completed\" });\n\t\tthis.completedCount = node(\n\t\t\t[this.completed],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tactions.emit((data[0] as readonly JobEnvelope<T>[]).length);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"completedCount\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: jobQueueMeta(\"job_flow_completed_count\"),\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t);\n\t\tthis.add(this.completedCount, { name: \"completedCount\" });\n\t\tthis.addDisposer(keepalive(this.completedCount));\n\n\t\tconst defaultMaxPerPump = Math.max(\n\t\t\t1,\n\t\t\trequireNonNegativeInt(opts.maxPerPump ?? DEFAULT_MAX_PER_PUMP, \"job flow maxPerPump\"),\n\t\t);\n\n\t\t// Wire up per-stage pumps.\n\t\tfor (let i = 0; i < this._stageNames.length; i += 1) {\n\t\t\tconst stage = this._stageNames[i] as string;\n\t\t\tconst current = this.queue(stage);\n\t\t\tconst next =\n\t\t\t\ti + 1 < this._stageNames.length ? this.queue(this._stageNames[i + 1] as string) : null;\n\t\t\tconst workFn = this._stageWorkFns.get(stage);\n\t\t\t// Per-stage `maxPerPump` override falls back to the top-level cap.\n\t\t\t// Captured per stage so each pump's loop sees its own resolved limit.\n\t\t\tconst stagePerPump = stageMaxPerPump.get(stage) ?? defaultMaxPerPump;\n\t\t\tconst stageMaxInflightCap = stageMaxInflight.get(stage);\n\t\t\t// When `maxInflight` is set, mount a state(0) counter on the graph\n\t\t\t// and wire it as an extra pump dep — settles `inflightCounter.emit`\n\t\t\t// re-fire the pump so pending items resume claiming after each\n\t\t\t// settle (without a counter, the pump only fires on `pending`\n\t\t\t// changes, which `ack` does not affect → maxInflight at saturation\n\t\t\t// would deadlock the queue).\n\t\t\t// qa F-D (Tier 5 /qa pass, 2026-04-29): mount under `__inflight__/`\n\t\t\t// internal namespace so the counter cannot collide with a user-named\n\t\t\t// stage (e.g. `inflight_my-stage`). Matches the EH-16\n\t\t\t// `__processManagers__/<name>` convention (COMPOSITION-GUIDE §38 —\n\t\t\t// internal infrastructure paths use the `__` prefix).\n\t\t\tconst inflightCounter =\n\t\t\t\tstageMaxInflightCap !== undefined\n\t\t\t\t\t? node<number>([], { name: `__inflight__/${stage}`, initial: 0 })\n\t\t\t\t\t: null;\n\t\t\tif (inflightCounter) {\n\t\t\t\tthis.add(inflightCounter, { name: `__inflight__/${stage}` });\n\t\t\t}\n\n\t\t\t// `isTerminal` marks the last stage — completed jobs go into\n\t\t\t// `_completed` log instead of a next queue.\n\t\t\tconst isTerminal = next === null;\n\n\t\t\tif (workFn) {\n\t\t\t\t// ── Stage with work hook ──────────────────────────────────────\n\t\t\t\t// Pump effect: claim one job, run work(job), forward result on\n\t\t\t\t// success; nack on failure.\n\t\t\t\t// Per B.3 lock: effects ARE sanctioned for side-effects.\n\t\t\t\t// `fromAny` bridges sync value / Promise / Node → Node<T>.\n\t\t\t\t//\n\t\t\t\t// **Inflight teardown drain (Tier 6.5 2.5a, 2026-04-29).** Each\n\t\t\t\t// claim mints a per-claim `AbortController` and tracks the\n\t\t\t\t// `(unsub, ac)` pair in a `ctx.store.inflight` Set. The\n\t\t\t\t// per-claim signal is supplied to the work fn via the optional\n\t\t\t\t// second-arg `{ signal }` (mirrors `LLMInvokeOptions.signal` /\n\t\t\t\t// `apply(item, {signal})` / tool-handler precedents). On the\n\t\t\t\t// pump's `deactivate` hook (parent Graph TEARDOWN cascade —\n\t\t\t\t// e.g. `harness.destroy()`), every inflight entry is aborted +\n\t\t\t\t// unsubscribed so user-supplied async work (LLM streams, eval\n\t\t\t\t// HTTP calls, refineLoop iterations) gets cooperative\n\t\t\t\t// cancellation instead of leaking past the harness lifetime.\n\t\t\t\ttype InflightEntry = { unsub: () => void; ac: AbortController };\n\t\t\t\ttype InflightStore = { entries: Set<InflightEntry>; terminated: boolean };\n\t\t\t\tconst pumpDeps: Node[] =\n\t\t\t\t\tinflightCounter != null ? [current.pending, inflightCounter] : [current.pending];\n\t\t\t\tconst pump = node<unknown>(\n\t\t\t\t\tpumpDeps,\n\t\t\t\t\t(_data, _actions, ctx) => {\n\t\t\t\t\t\tif (!(\"inflight\" in ctx.store)) {\n\t\t\t\t\t\t\tctx.store.inflight = {\n\t\t\t\t\t\t\t\tentries: new Set<InflightEntry>(),\n\t\t\t\t\t\t\t\tterminated: false,\n\t\t\t\t\t\t\t} satisfies InflightStore;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst inflightStore = ctx.store.inflight as InflightStore;\n\t\t\t\t\t\tconst inflight = inflightStore.entries;\n\t\t\t\t\t\tlet processed = 0;\n\t\t\t\t\t\twhile (processed < stagePerPump) {\n\t\t\t\t\t\t\t// 3.1 maxInflight gate: cap concurrent inflight across pump\n\t\t\t\t\t\t\t// ticks. The inflightCounter (mounted as a pump dep) re-fires\n\t\t\t\t\t\t\t// the pump when a settle decrements it, so pending items\n\t\t\t\t\t\t\t// resume claiming when capacity frees up.\n\t\t\t\t\t\t\tif (stageMaxInflightCap !== undefined && inflight.size >= stageMaxInflightCap) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst claims = current.claim(1);\n\t\t\t\t\t\t\tif (claims.length === 0) break;\n\t\t\t\t\t\t\tconst job = claims[0] as JobEnvelope<T>;\n\t\t\t\t\t\t\tif (!job) break;\n\n\t\t\t\t\t\t\t// Build the updated path accumulator.\n\t\t\t\t\t\t\tconst prevPath = (job.metadata.job_flow_path as readonly string[] | undefined) ?? [];\n\t\t\t\t\t\t\tconst newPath = [...prevPath, stage];\n\n\t\t\t\t\t\t\tconst ac = new AbortController();\n\t\t\t\t\t\t\tconst entry: InflightEntry = { unsub: () => undefined, ac };\n\t\t\t\t\t\t\tinflight.add(entry);\n\t\t\t\t\t\t\tinflightCounter?.emit(inflight.size);\n\n\t\t\t\t\t\t\tlet result: NodeInput<T>;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tresult = workFn(job, { signal: ac.signal });\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Sync throw → nack no-requeue.\n\t\t\t\t\t\t\t\tinflight.delete(entry);\n\t\t\t\t\t\t\t\tinflightCounter?.emit(inflight.size);\n\t\t\t\t\t\t\t\tcurrent.nack(job.id, { requeue: false });\n\t\t\t\t\t\t\t\tprocessed += 1;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Coerce to Node<T> via fromAny.\n\t\t\t\t\t\t\tconst resultNode = fromAny<T>(result);\n\n\t\t\t\t\t\t\t// M8: Subscribe once to the result node; on DATA forward; on ERROR nack.\n\t\t\t\t\t\t\t// Use `let unsub` + TDZ guard (same pattern as toPromise) so sync\n\t\t\t\t\t\t\t// DATA delivery inside subscribe() doesn't hit TDZ on `unsub`.\n\t\t\t\t\t\t\tlet settled = false;\n\t\t\t\t\t\t\tlet unsub: (() => void) | undefined;\n\t\t\t\t\t\t\tconst cleanupSub = (): void => {\n\t\t\t\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\t\t\t\tunsub();\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tPromise.resolve().then(() => unsub?.());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tinflight.delete(entry);\n\t\t\t\t\t\t\t\t// qa F-F (Tier 5 /qa pass, 2026-04-29): skip the counter\n\t\t\t\t\t\t\t\t// emit after teardown — the counter Node is itself in the\n\t\t\t\t\t\t\t\t// cascade. Late ERROR/DATA arriving via the deferred\n\t\t\t\t\t\t\t\t// `Promise.resolve().then(unsub)` path could otherwise emit\n\t\t\t\t\t\t\t\t// on a torn-down node.\n\t\t\t\t\t\t\t\tif (!inflightStore.terminated) {\n\t\t\t\t\t\t\t\t\tinflightCounter?.emit(inflight.size);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tunsub = resultNode.subscribe((msgs) => {\n\t\t\t\t\t\t\t\tif (settled) return;\n\t\t\t\t\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\t\t\t\t\tcleanupSub();\n\t\t\t\t\t\t\t\t\t\tconst newPayload = m[1] as T;\n\t\t\t\t\t\t\t\t\t\tconst newMetadata = {\n\t\t\t\t\t\t\t\t\t\t\t...job.metadata,\n\t\t\t\t\t\t\t\t\t\t\tjob_flow_path: newPath,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\tif (isTerminal) {\n\t\t\t\t\t\t\t\t\t\t\tconst completedJob: JobEnvelope<T> = {\n\t\t\t\t\t\t\t\t\t\t\t\t...job,\n\t\t\t\t\t\t\t\t\t\t\t\tpayload: newPayload,\n\t\t\t\t\t\t\t\t\t\t\t\tmetadata: Object.freeze(newMetadata),\n\t\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\t\t\t\t\t\t\tcurrent.ack(job.id);\n\t\t\t\t\t\t\t\t\t\t\t\tthis._completed.append(completedJob);\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\t\t\t\t\t\t\tcurrent.ack(job.id);\n\t\t\t\t\t\t\t\t\t\t\t\t(next as JobQueueGraph<T>).enqueue(newPayload, {\n\t\t\t\t\t\t\t\t\t\t\t\t\tmetadata: newMetadata,\n\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t} else if (m[0] === ERROR) {\n\t\t\t\t\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\t\t\t\t\tcleanupSub();\n\t\t\t\t\t\t\t\t\t\tcurrent.nack(job.id, { requeue: false });\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tentry.unsub = () => unsub?.();\n\n\t\t\t\t\t\t\tprocessed += 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\t\t\t// qa F-F: set terminated BEFORE draining so any\n\t\t\t\t\t\t\t\t// `cleanupSub` racing via the deferred-microtask path\n\t\t\t\t\t\t\t\t// (`Promise.resolve().then(() => unsub?.())`) sees\n\t\t\t\t\t\t\t\t// `terminated === true` and skips its `inflightCounter.emit`.\n\t\t\t\t\t\t\t\tinflightStore.terminated = true;\n\t\t\t\t\t\t\t\tfor (const e of inflight) {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\te.ac.abort();\n\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\t// best-effort\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\te.unsub();\n\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\t// best-effort\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tinflight.clear();\n\t\t\t\t\t\t\t\t// Lock 6.D (Phase 13.6.B): drop the `inflight` key\n\t\t\t\t\t\t\t\t// so the next activation re-initializes a fresh\n\t\t\t\t\t\t\t\t// `InflightStore` with `terminated: false`. Without\n\t\t\t\t\t\t\t\t// this, post-flip preserve-by-default keeps the\n\t\t\t\t\t\t\t\t// stale `terminated: true` flag and silently\n\t\t\t\t\t\t\t\t// suppresses inflight-counter emits forever.\n\t\t\t\t\t\t\t\tdelete ctx.store.inflight;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tmeta: jobQueueMeta(\"job_flow_pump\", { stage, has_work: true }),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tthis.addDisposer(keepalive(pump));\n\t\t\t} else {\n\t\t\t\t// ── Stage without work hook (pass-through ferry) ──────────────\n\t\t\t\t// Claim, accumulate path, forward to next stage or completed.\n\t\t\t\tconst pump = this.effect(\n\t\t\t\t\t`pump_${stage}`,\n\t\t\t\t\t[`${stage}::pending`],\n\t\t\t\t\t() => {\n\t\t\t\t\t\tlet moved = 0;\n\t\t\t\t\t\twhile (moved < stagePerPump) {\n\t\t\t\t\t\t\tconst claim = current.claim(1);\n\t\t\t\t\t\t\tif (claim.length === 0) break;\n\t\t\t\t\t\t\tconst job = claim[0] as JobEnvelope<T>;\n\t\t\t\t\t\t\tif (!job) break;\n\n\t\t\t\t\t\t\tconst prevPath = (job.metadata.job_flow_path as readonly string[] | undefined) ?? [];\n\t\t\t\t\t\t\tconst newPath = [...prevPath, stage];\n\t\t\t\t\t\t\tconst newMetadata = { ...job.metadata, job_flow_path: newPath };\n\n\t\t\t\t\t\t\tif (isTerminal) {\n\t\t\t\t\t\t\t\tconst completedJob: JobEnvelope<T> = {\n\t\t\t\t\t\t\t\t\t...job,\n\t\t\t\t\t\t\t\t\tmetadata: Object.freeze(newMetadata),\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\t\t\t\tcurrent.ack(job.id);\n\t\t\t\t\t\t\t\t\tthis._completed.append(completedJob);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\t\t\t\tcurrent.ack(job.id);\n\t\t\t\t\t\t\t\t\t(next as JobQueueGraph<T>).enqueue(job.payload, {\n\t\t\t\t\t\t\t\t\t\tmetadata: newMetadata,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmoved += 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tmeta: jobQueueMeta(\"job_flow_pump\", { stage, has_work: false }),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tthis.addDisposer(keepalive(pump));\n\t\t\t}\n\t\t}\n\t}\n\n\tstages(): readonly string[] {\n\t\treturn this._stageNames;\n\t}\n\n\tqueue(stage: string): JobQueueGraph<T> {\n\t\tconst q = this._queues.get(stage);\n\t\tif (!q) throw new Error(`jobFlow(\"${this.name}\"): unknown stage \"${stage}\"`);\n\t\treturn q;\n\t}\n\n\tenqueue(payload: T, opts: { id?: string; metadata?: Record<string, unknown> } = {}): string {\n\t\treturn this.queue(this._stageNames[0] as string).enqueue(payload, opts);\n\t}\n\n\tretainedCompleted(): readonly JobEnvelope<T>[] {\n\t\treturn this.completed.cache as readonly JobEnvelope<T>[];\n\t}\n}\n\n/**\n * Creates a Pulsar-inspired job queue graph with claim/ack/nack workflow.\n */\nexport function jobQueue<T>(name: string, opts?: JobQueueOptions): JobQueueGraph<T> {\n\treturn new JobQueueGraph<T>(name, opts);\n}\n\n/**\n * Creates an autonomous multi-stage queue chain graph.\n */\nexport function jobFlow<T>(name: string, opts?: JobFlowOptions<T>): JobFlowGraph<T> {\n\tconst g = new JobFlowGraph<T>(name, opts);\n\t// Tier 1.5.3 Phase 2.5 (DG1=B): tag the Graph with its constructing\n\t// factory so `describe()` surfaces provenance. Route through\n\t// `placeholderArgs` since `stages[].work` is a function and\n\t// `opts.graph` may carry non-JSON fields.\n\tconst { factory: _f, factoryArgs: _fa, ...tagArgs } = (opts ?? {}) as Record<string, unknown>;\n\tg.tagFactory(\"jobFlow\", placeholderArgs(tagArgs));\n\treturn g;\n}\n"],"mappings":"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","names":["batch"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/harness/actuator-executor.ts","../src/utils/harness/auto-solidify.ts","../src/utils/harness/bridge.ts"],"sourcesContent":["/**\n * actuatorExecutor — bridge a side-effecting actuator into the harness\n * EXECUTE work fn.\n *\n * `refineExecutor` covers the artifact-typed case (refine a candidate\n * `T` against an evaluator); `actuatorExecutor` covers the side-effecting\n * case (write a catalog entry, mutate a template registry, edit a doc on\n * disk). The user's `apply` callback owns the side effect; the executor\n * wraps it in the per-claim lifecycle:\n *\n * 1. **One DATA per claim.** The producer captures the first DATA from\n * the bridged `apply` result, emits a {@link HarnessJobPayload} with\n * `execution` filled in, and completes. Subsequent inner DATAs are\n * ignored.\n * 2. **Cancel-on-teardown.** When the JobFlow pump unsubscribes (after\n * capturing first DATA, or on graph teardown), the producer's cleanup\n * fires `ac.abort()` which propagates into `apply`'s `signal`.\n * 3. **Errors surfaced as failure payload.** A thrown / ERROR result is\n * mapped via `onError` into a `failure`-outcome `ExecuteOutput` so the\n * dispatch effect can route the item rather than silently dropping it.\n *\n * **What `apply` may return.** Anything `fromAny` accepts: `Promise<R>`,\n * `Node<R>`, `AsyncIterable<R>`, `Iterable<R>`, or a synchronous `R`.\n * `Promise<R>` is the typical shape (`writeFile`, `fetch`, `db.execute`).\n *\n * **Pairing with `evalVerifier`.** `ExecuteOutput.artifact` is set to\n * the actuation record; an `evalVerifier<R>` whose `extractArtifact`\n * returns the record (or the post-apply world state) closes EXECUTE →\n * VERIFY with consistent typing end-to-end.\n *\n * @module\n */\n\nimport { COMPLETE, DATA, ERROR, type Messages, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { fromAny, type NodeInput } from \"@graphrefly/pure-ts/extra\";\nimport type { JobEnvelope } from \"../job-queue/index.js\";\n\nimport type { ExecuteOutput, HarnessExecutor, HarnessJobPayload, TriagedItem } from \"./types.js\";\n\n/**\n * What an actuator's `apply` may return. Mirrors `NodeInput<R>` plus a\n * raw `R` for synchronous side effects.\n */\nexport type ActuatorResult<R> = NodeInput<R>;\n\n/** Configuration for {@link actuatorExecutor}. */\nexport interface ActuatorExecutorConfig<R> {\n\t/**\n\t * Apply the side effect for this triaged item. Receives the abort\n\t * signal — actuators that own real I/O should thread `signal` into\n\t * `fetch`, `fs.writeFile`, child-process kills, etc. so that the\n\t * pump's teardown actually cancels in-flight work.\n\t *\n\t * The first DATA emitted by the bridged result wins; later DATAs are\n\t * discarded. ERROR (or a synchronous throw) is mapped via `onError`.\n\t */\n\tapply: (item: TriagedItem, opts: { signal: AbortSignal }) => ActuatorResult<R>;\n\n\t/**\n\t * Optional gate — when provided and returning `false`, the actuator\n\t * is skipped and the executor emits an `ExecuteOutput` with\n\t * `outcome: \"failure\"`. Use to route interventions the actuator can't\n\t * handle into the failure path.\n\t */\n\tshouldApply?: (item: TriagedItem) => boolean;\n\n\t/** Detail string for the skip path. Default: includes intervention name. */\n\tskipDetail?: (item: TriagedItem) => string;\n\n\t/**\n\t * Map a successfully-applied actuation record into an `ExecuteOutput<R>`.\n\t */\n\ttoOutput?: (record: R, item: TriagedItem) => ExecuteOutput<R>;\n\n\t/**\n\t * Map a thrown / ERROR result into an `ExecuteOutput<R>`.\n\t */\n\tonError?: (err: unknown, item: TriagedItem) => ExecuteOutput<R>;\n\n\t/** Node name prefix for `describe()` introspection. */\n\tname?: string;\n}\n\nfunction defaultToOutput<R>(record: R, item: TriagedItem): ExecuteOutput<R> {\n\treturn {\n\t\toutcome: \"success\",\n\t\tdetail: `actuator applied ${item.intervention} for ${truncate(item.summary)}`,\n\t\tartifact: record,\n\t};\n}\n\nfunction defaultOnError<R>(err: unknown, item: TriagedItem): ExecuteOutput<R> {\n\tconst message = err instanceof Error ? err.message : String(err);\n\treturn {\n\t\toutcome: \"failure\",\n\t\tdetail: `actuator threw on ${item.intervention}: ${message}`,\n\t};\n}\n\nfunction defaultSkipDetail(item: TriagedItem): string {\n\treturn `actuator skipped ${item.intervention} (shouldApply returned false)`;\n}\n\nfunction truncate(s: string, max = 80): string {\n\treturn s.length <= max ? s : `${s.slice(0, max - 1)}…`;\n}\n\n/**\n * Build a {@link HarnessExecutor} backed by a side-effecting actuator.\n *\n * @example File-system actuator that writes a catalog entry and emits the diff.\n * ```ts\n * const harness = harnessLoop(\"repair\", {\n * adapter,\n * executor: actuatorExecutor<CatalogPatch>({\n * async apply(item, { signal }) {\n * const patch = patchFromItem(item);\n * await fs.writeFile(patch.path, patch.contents, { signal });\n * return patch;\n * },\n * shouldApply: (item) => item.intervention === \"catalog-fn\",\n * }),\n * verifier: evalVerifier<CatalogPatch>({ ... }),\n * });\n * ```\n */\nexport function actuatorExecutor<R>(config: ActuatorExecutorConfig<R>): HarnessExecutor<R> {\n\tconst name = config.name ?? \"actuator-executor\";\n\tconst toOutput = config.toOutput ?? defaultToOutput<R>;\n\tconst onError = config.onError ?? defaultOnError<R>;\n\tconst skipDetail = config.skipDetail ?? defaultSkipDetail;\n\n\treturn (job: JobEnvelope<HarnessJobPayload<R>>, opts) => {\n\t\tconst item = job.payload.item;\n\n\t\tif (config.shouldApply && !config.shouldApply(item)) {\n\t\t\t// Synchronous failure payload — return as a plain object;\n\t\t\t// `fromAny` accepts the bare value and emits one DATA.\n\t\t\treturn {\n\t\t\t\t...job.payload,\n\t\t\t\texecution: { item, outcome: \"failure\", detail: skipDetail(item) },\n\t\t\t} satisfies HarnessJobPayload<R>;\n\t\t}\n\n\t\treturn node<HarnessJobPayload<R>>(\n\t\t\t[],\n\t\t\t(_data, actions) => {\n\t\t\t\tconst ac = new AbortController();\n\t\t\t\t// Link pump-supplied signal (Tier 6.5 2.5b): parent abort\n\t\t\t\t// (e.g. `harness.destroy()`) cascades into the inner AC and\n\t\t\t\t// thus into `apply(item, { signal })` + `fromAny({ signal })`.\n\t\t\t\tconst parentSignal = opts?.signal;\n\t\t\t\tlet unlinkParent: () => void = () => undefined;\n\t\t\t\tif (parentSignal) {\n\t\t\t\t\tif (parentSignal.aborted) {\n\t\t\t\t\t\tac.abort();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst onParentAbort = (): void => ac.abort();\n\t\t\t\t\t\tparentSignal.addEventListener(\"abort\", onParentAbort, { once: true });\n\t\t\t\t\t\tunlinkParent = () => parentSignal.removeEventListener(\"abort\", onParentAbort);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlet captured = false;\n\t\t\t\tlet unsub: (() => void) | null = null;\n\t\t\t\tconst emitOnce = (out: ExecuteOutput<R>): void => {\n\t\t\t\t\tif (captured) return;\n\t\t\t\t\tcaptured = true;\n\t\t\t\t\tactions.down([\n\t\t\t\t\t\t[DATA, { ...job.payload, execution: { item, ...out } }],\n\t\t\t\t\t\t[COMPLETE],\n\t\t\t\t\t] satisfies Messages);\n\t\t\t\t\tunsub?.();\n\t\t\t\t\tunsub = null;\n\t\t\t\t};\n\t\t\t\tlet inner: Node<R>;\n\t\t\t\ttry {\n\t\t\t\t\tconst rawResult = config.apply(item, { signal: ac.signal });\n\t\t\t\t\tinner = fromAny<R>(rawResult, { signal: ac.signal });\n\t\t\t\t} catch (err) {\n\t\t\t\t\temitOnce(onError(err, item));\n\t\t\t\t\treturn () => {\n\t\t\t\t\t\tunlinkParent();\n\t\t\t\t\t\tac.abort();\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tunsub = inner.subscribe((batch) => {\n\t\t\t\t\tfor (const m of batch) {\n\t\t\t\t\t\tif (captured) return;\n\t\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\t\temitOnce(toOutput(m[1] as R, item));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\t\t\temitOnce(onError(m[1], item));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\t\t\temitOnce(onError(new Error(\"actuator inner completed without emitting DATA\"), item));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\t// Sync DATA delivery (cached state / `fromAny` over a sync value):\n\t\t\t\t// the callback ran reentrantly before `unsub` was assigned, so\n\t\t\t\t// `emitOnce`'s `unsub?.()` was a no-op. Drop the upstream subscription\n\t\t\t\t// now that we have the handle. Without this, the inner stays\n\t\t\t\t// subscribed until producer teardown — leaks at high volume.\n\t\t\t\tif (captured && unsub) {\n\t\t\t\t\tunsub();\n\t\t\t\t\tunsub = null;\n\t\t\t\t}\n\t\t\t\treturn () => {\n\t\t\t\t\tunlinkParent();\n\t\t\t\t\tac.abort();\n\t\t\t\t\tunsub?.();\n\t\t\t\t\tunsub = null;\n\t\t\t\t};\n\t\t\t},\n\t\t\t{ name: `${name}/inner`, describeKind: \"producer\" },\n\t\t);\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// dispatchActuator\n// ---------------------------------------------------------------------------\n\n/**\n * Apply callback shape consumed by {@link dispatchActuator}. Same shape as\n * {@link ActuatorExecutorConfig.apply}.\n */\nexport type ActuatorApplyFn<R> = (\n\titem: TriagedItem,\n\topts: { signal: AbortSignal },\n) => ActuatorResult<R>;\n\n/** Configuration for {@link dispatchActuator}. */\nexport interface DispatchActuatorConfig<R> {\n\t/**\n\t * Per-intervention apply callbacks. Keyed by `TriagedItem.intervention`.\n\t * Items whose intervention is not in `routes` fall through to `default`\n\t * (when set) or emit a skip-failure `ExecuteOutput`.\n\t */\n\troutes: Readonly<Partial<Record<TriagedItem[\"intervention\"], ActuatorApplyFn<R>>>>;\n\t/** Fallback apply callback for items whose intervention is not in `routes`. */\n\tdefault?: ActuatorApplyFn<R>;\n\t/** Node name prefix for `describe()` introspection. */\n\tname?: string;\n}\n\n/**\n * Multi-intervention actuator — dispatches each `TriagedItem` to one of\n * several `apply` callbacks based on `item.intervention`.\n *\n * Internally builds a single `actuatorExecutor` whose `apply` resolves the\n * intervention → callback at call-time. Items with no matching route and no\n * `default` emit a skip-failure with detail\n * `\"no route for intervention 'X'\"`.\n */\nexport function dispatchActuator<R>(config: DispatchActuatorConfig<R>): HarnessExecutor<R> {\n\tconst name = config.name ?? \"dispatch-actuator\";\n\tconst defaultFn = config.default ?? null;\n\tconst hasDefault = defaultFn != null;\n\treturn actuatorExecutor<R>({\n\t\tapply: (item, opts) => {\n\t\t\tconst fn = Object.hasOwn(config.routes, item.intervention)\n\t\t\t\t? config.routes[item.intervention]!\n\t\t\t\t: defaultFn;\n\t\t\tif (!fn) {\n\t\t\t\tthrow new Error(`dispatchActuator: no route for intervention '${item.intervention}'`);\n\t\t\t}\n\t\t\treturn fn(item, opts);\n\t\t},\n\t\tshouldApply: (item) => Object.hasOwn(config.routes, item.intervention) || hasDefault,\n\t\tskipDetail: (item) => `no route for intervention '${item.intervention}'`,\n\t\tname,\n\t});\n}\n","/**\n * autoSolidify — promote successful VERIFY runs into a durable artifact\n * (catalog entry, skill, template, doc edit, …).\n *\n * Closes the dogfood retrospective loop: when the harness's VERIFY\n * stage reports `verified: true`, the validated intervention should\n * become an authoring artifact the next loop run can rely on. This\n * primitive is the generic substrate — pass a `write` callback that\n * does the actual promotion (e.g. `overlay.upsertTemplate` for the\n * dogfood catalog overlay; `fs.writeFile` for a doc edit; `ctx.skill`\n * for a Hermes-style skill registry).\n *\n * @example Wire the catalog overlay as the solidify target.\n * ```ts\n * const solidified = autoSolidify({\n * verifyResults: harness.verifyResults.latest,\n * extract: (vr) => vr.execution.artifact ?? null,\n * write: (entry, vr) => overlay.upsertFn(`learned-${vr.item.summary}`, entry),\n * });\n * solidified.subscribe(() => {}); // keep alive for log\n * ```\n *\n * **Why a node and not just an effect.** The returned `Node<R>` emits\n * each promoted artifact, so callers can pipe solidifications through\n * the standard reactive surface (`describe()`, `observe()`, replay\n * buffers) instead of side-channel logging. An audit / dashboard that\n * wants \"what was learned this run?\" subscribes to the returned node;\n * the `write` callback owns the durable side effect.\n *\n * **Idempotency is the caller's responsibility.** The primitive\n * promotes every `verified: true` wave that passes the predicate. If\n * the harness re-verifies the same item (e.g. via reingestion), the\n * `write` callback is invoked again. Wrap your write fn with a\n * dedup-by-key guard if your target store would otherwise bloat. The\n * inner `seen` set inside this factory is intentionally absent — the\n * harness already retains via topic logs and the user may want\n * re-promotion semantics that are domain-specific.\n *\n * @module\n */\n\nimport { COMPLETE, DATA, ERROR, type Messages, type Node, node } from \"@graphrefly/pure-ts/core\";\n\nimport type { VerifyResult } from \"./types.js\";\n\n/**\n * Configuration for {@link autoSolidify}.\n *\n * `R` is the artifact type the upstream EXECUTE stage produced (and\n * `evalVerifier` carries through `execution.artifact`). `T` is the\n * promotion shape — what `write` consumes and what the returned node\n * emits. Often `T = R`, but they diverge when the actuator's raw\n * artifact needs a transform before storing (e.g. wrap a `CatalogPatch`\n * into a `CatalogEntry` with effectiveness metadata).\n */\nexport interface AutoSolidifyConfig<R, T = R> {\n\t/** Reactive verify-result stream. Typically `harness.verifyResults.latest`. */\n\tverifyResults: Node<VerifyResult<R> | null>;\n\t/**\n\t * Pull the value-to-promote out of a verified VerifyResult.\n\t * Default: `(vr) => vr.execution.artifact as T | null`. Return `null`\n\t * to skip a particular VerifyResult even when `verified: true` (e.g.\n\t * an LLM-default executor produces no artifact and there's nothing to\n\t * solidify).\n\t */\n\textract?: (vr: VerifyResult<R>) => T | null;\n\t/**\n\t * Optional gate beyond `verified === true`. When provided, the\n\t * primitive only promotes when this returns `true`. Default: pass\n\t * everything verified.\n\t *\n\t * Useful predicates:\n\t * - `(vr) => vr.item.intervention === \"catalog-fn\"` — only catalog work.\n\t * - `(vr) => (vr.findings ?? []).every(f => !/regression/i.test(f))` —\n\t * skip even-passes that mention regressions.\n\t */\n\tpredicate?: (vr: VerifyResult<R>) => boolean;\n\t/**\n\t * Promote — usually a side effect (write to overlay, fs, KG, etc.).\n\t * Receives the extracted artifact AND the originating VerifyResult so\n\t * the writer can use any context it needs (item summary, eval task\n\t * IDs, finding text, …) when shaping the durable record.\n\t */\n\twrite: (artifact: T, vr: VerifyResult<R>) => void;\n\t/** Node name for `describe()` introspection. Default `\"auto-solidify\"`. */\n\tname?: string;\n}\n\n/**\n * Build a `Node<T>` that subscribes to `verifyResults`, filters to\n * verified passes that produced an extractable artifact, runs `write`,\n * and emits the artifact. Use the returned node as a subscription\n * point for audit / dashboard / log pipelines.\n *\n * **Terminal-on-error semantics.** A throw from `predicate`, `extract`,\n * or `write` surfaces as `[[ERROR]]` on the returned node and\n * **terminates** it — the upstream subscription tears down and no\n * further DATA is emitted. This matches the spec's terminal-frame\n * contract for ERROR. If you want the solidify node to stay live\n * across user-callback throws, wrap your callbacks with try/catch\n * internally and emit a sentinel value or no-op on failure. A future\n * non-terminal `errors: Node<unknown>` companion may surface failures\n * without terminating the success stream — flagged as a follow-up.\n *\n * @returns A `Node<T>` that emits one DATA per promoted artifact.\n * Stays live as long as `verifyResults` is live AND no user callback\n * has thrown.\n */\nexport function autoSolidify<R, T = R>(config: AutoSolidifyConfig<R, T>): Node<T> {\n\tconst name = config.name ?? \"auto-solidify\";\n\tconst extract =\n\t\tconfig.extract ?? ((vr: VerifyResult<R>) => (vr.execution.artifact ?? null) as T | null);\n\tconst predicate = config.predicate ?? (() => true);\n\n\treturn node<T>(\n\t\t[],\n\t\t(_data, actions) => {\n\t\t\tlet unsub: (() => void) | null = null;\n\t\t\tlet terminated = false;\n\t\t\tconst tearDown = (): void => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tterminated = true;\n\t\t\t\tunsub?.();\n\t\t\t\tunsub = null;\n\t\t\t};\n\t\t\tconst emitTerminalError = (err: unknown): void => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t\ttearDown();\n\t\t\t};\n\t\t\tunsub = config.verifyResults.subscribe((batch) => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tfor (const m of batch) {\n\t\t\t\t\tif (terminated) return;\n\t\t\t\t\tif (m[0] !== DATA) {\n\t\t\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\t\t\t// Upstream verifyResults completed (rare; harness destroy).\n\t\t\t\t\t\t\t// Forward COMPLETE and tear down — solidify is terminal too.\n\t\t\t\t\t\t\tactions.down([[COMPLETE]] satisfies Messages);\n\t\t\t\t\t\t\ttearDown();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst vr = m[1] as VerifyResult<R> | null;\n\t\t\t\t\tif (vr == null) continue;\n\t\t\t\t\tif (!vr.verified) continue;\n\t\t\t\t\t// User callbacks (predicate / extract / write) are isolated\n\t\t\t\t\t// in try/catch so a throw lands as a single terminal ERROR\n\t\t\t\t\t// rather than propagating into the upstream emitter where\n\t\t\t\t\t// it would skip later messages in the same batch and leave\n\t\t\t\t\t// the solidify node un-terminated.\n\t\t\t\t\tlet pass: boolean;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tpass = predicate(vr);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\temitTerminalError(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!pass) continue;\n\t\t\t\t\tlet artifact: T | null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tartifact = extract(vr);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\temitTerminalError(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (artifact == null) continue;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconfig.write(artifact, vr);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\temitTerminalError(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tactions.down([[DATA, artifact]] satisfies Messages);\n\t\t\t\t}\n\t\t\t});\n\t\t\t// If `subscribe` fired terminally during the call (push-on-subscribe\n\t\t\t// of an already-COMPLETE upstream), `tearDown()` ran inside the\n\t\t\t// callback before `unsub` was assigned, so the unsub is still\n\t\t\t// dangling. Drop it now if we're already terminated.\n\t\t\tif (terminated && unsub) {\n\t\t\t\tunsub();\n\t\t\t\tunsub = null;\n\t\t\t}\n\t\t\treturn () => {\n\t\t\t\ttearDown();\n\t\t\t};\n\t\t},\n\t\t{ name, describeKind: \"producer\" },\n\t);\n}\n","/**\n * Harness bridge factories (roadmap §9.0).\n *\n * Intake bridges, eval source wrapper, before/after comparison,\n * affected-task filter, code-change bridge, and notification effect.\n * All are compositions of existing primitives — no new abstractions.\n *\n * @module\n */\n\nimport { type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { fromAny, switchMap } from \"@graphrefly/pure-ts/extra\";\nimport type { Graph } from \"@graphrefly/pure-ts/graph\";\nimport type { TopicGraph } from \"../messaging/index.js\";\n\nimport type { IntakeItem, Severity, TriagedItem } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Generic intake bridge\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createIntakeBridge}. */\nexport interface CreateIntakeBridgeOptions<T> {\n\t/** Graph to register the effect node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Reactive node emitting domain-specific data. */\n\tsource: Node<T>;\n\t/** TopicGraph to publish IntakeItem entries to. */\n\tintakeTopic: TopicGraph<IntakeItem>;\n\t/** Converts source data into IntakeItem[]. Return empty array to skip. */\n\tparser: (value: T) => IntakeItem[];\n\t/** Effect-node name (default `\"intake-bridge\"`). */\n\tname?: string;\n}\n\n/**\n * Generic source→intake bridge factory.\n *\n * Watches a source node for new values, passes each through a user-supplied\n * `parser` that produces zero or more `IntakeItem`s, and publishes them to\n * the given intake topic.\n *\n * This is the generalized pattern behind {@link evalIntakeBridge}. Use it for\n * CI results, test failures, Slack messages, monitoring alerts, or any domain\n * where structured results should flow into a harness loop.\n *\n * The effect node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n *\n * @returns The effect node (for lifecycle management).\n */\nexport function createIntakeBridge<T>(opts: CreateIntakeBridgeOptions<T>): Node<unknown> {\n\tconst { graph, source, intakeTopic, parser, name = \"intake-bridge\" } = opts;\n\tconst eff = node(\n\t\t[source as Node<unknown>],\n\t\t(batchData, _actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst value = data[0];\n\t\t\tif (value === undefined) return;\n\t\t\tconst items = parser(value as T);\n\t\t\tfor (const item of items) {\n\t\t\t\tintakeTopic.publish(item);\n\t\t\t}\n\t\t},\n\t\t{ describeKind: \"effect\" },\n\t);\n\tgraph.add(eff, { name });\n\treturn eff;\n}\n\n// ---------------------------------------------------------------------------\n// Generic eval result shape\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal eval result shape accepted by the bridge.\n *\n * TS eval runner uses `EvalRun` from `evals/lib/types.ts` which is a superset\n * of this shape. The bridge only reads what it needs.\n */\nexport interface EvalRunResult {\n\trun_id: string;\n\tmodel: string;\n\ttasks: EvalTaskResult[];\n}\n\nexport interface EvalTaskResult {\n\ttask_id: string;\n\tvalid: boolean;\n\tjudge_scores?: EvalJudgeScore[];\n}\n\nexport interface EvalJudgeScore {\n\tclaim: string;\n\tpass: boolean;\n\treasoning: string;\n}\n\n// ---------------------------------------------------------------------------\n// Bridge factory\n// ---------------------------------------------------------------------------\n\nexport interface EvalIntakeBridgeOptions {\n\t/** Graph to register the effect node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Node emitting EvalRunResult (or EvalRunResult[]). */\n\tsource: Node<EvalRunResult | EvalRunResult[]>;\n\t/** TopicGraph to publish IntakeItem entries to. */\n\tintakeTopic: TopicGraph<IntakeItem>;\n\t/** Effect-node name (default `\"eval-intake-bridge\"`). */\n\tname?: string;\n\t/** Minimum severity for eval-sourced items (default `\"medium\"`). */\n\tdefaultSeverity?: Severity;\n}\n\n/**\n * Create an effect node that watches an eval results source and publishes\n * per-criterion findings to an intake topic.\n *\n * Each failing judge criterion produces a separate IntakeItem — not one\n * item per task. This gives the triage stage granular findings to classify.\n *\n * The effect node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n *\n * @returns The effect node (for lifecycle management).\n */\nexport function evalIntakeBridge(opts: EvalIntakeBridgeOptions): Node<unknown> {\n\tconst {\n\t\tgraph,\n\t\tsource,\n\t\tintakeTopic,\n\t\tname = \"eval-intake-bridge\",\n\t\tdefaultSeverity = \"medium\",\n\t} = opts;\n\n\tconst eff = node(\n\t\t[source as Node<unknown>],\n\t\t(batchData, _actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst results = data[0];\n\t\t\tif (results === undefined) return;\n\t\t\tconst runs = Array.isArray(results)\n\t\t\t\t? (results as EvalRunResult[])\n\t\t\t\t: [results as EvalRunResult];\n\n\t\t\tfor (const run of runs) {\n\t\t\t\tfor (const task of run.tasks) {\n\t\t\t\t\t// Only process tasks with failures\n\t\t\t\t\tif (task.valid && task.judge_scores?.every((s) => s.pass)) continue;\n\n\t\t\t\t\t// Task-level validity failure (no judge scores or overall invalid)\n\t\t\t\t\tif (!task.valid && (!task.judge_scores || task.judge_scores.length === 0)) {\n\t\t\t\t\t\tintakeTopic.publish({\n\t\t\t\t\t\t\tsource: \"eval\",\n\t\t\t\t\t\t\tsummary: `Task ${task.task_id} invalid (model: ${run.model})`,\n\t\t\t\t\t\t\tevidence: `Run ${run.run_id}: task produced invalid output`,\n\t\t\t\t\t\t\taffectsAreas: [\"graphspec\"],\n\t\t\t\t\t\t\taffectsEvalTasks: [task.task_id],\n\t\t\t\t\t\t\tseverity: defaultSeverity,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Per-criterion findings\n\t\t\t\t\tif (task.judge_scores) {\n\t\t\t\t\t\tfor (const score of task.judge_scores) {\n\t\t\t\t\t\t\tif (score.pass) continue;\n\t\t\t\t\t\t\tintakeTopic.publish({\n\t\t\t\t\t\t\t\tsource: \"eval\",\n\t\t\t\t\t\t\t\tsummary: `${task.task_id}: ${score.claim} (model: ${run.model})`,\n\t\t\t\t\t\t\t\tevidence: score.reasoning,\n\t\t\t\t\t\t\t\taffectsAreas: [\"graphspec\"],\n\t\t\t\t\t\t\t\taffectsEvalTasks: [task.task_id],\n\t\t\t\t\t\t\t\tseverity: defaultSeverity,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{ describeKind: \"effect\" },\n\t);\n\tgraph.add(eff, { name });\n\treturn eff;\n}\n\n// ---------------------------------------------------------------------------\n// Composition A: Eval-driven improvement loop\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap any eval runner as a reactive producer node.\n *\n * When `trigger` emits, calls `runner()` and emits the result downstream.\n * Uses `switchMap` + `fromAny` — the async boundary stays in the source\n * layer (spec §5.10). A new trigger cancels any in-flight run.\n *\n * Pure transform via operator composition — does not construct an\n * effect/derived node, so no `graph` parameter is needed.\n *\n * ```ts\n * const trigger = state(0); // bump to trigger a new run\n * const results = evalSource(trigger, () => runEvals(config));\n * results.subscribe(msgs => { ... });\n * trigger.emit(1); // fires the runner\n * ```\n *\n * @param trigger - Any node; each new DATA emission fires the runner.\n * @param runner - Returns an EvalRunResult (or promise of one).\n */\nexport function evalSource<T extends EvalRunResult>(\n\ttrigger: Node<unknown>,\n\trunner: () => T | Promise<T>,\n): Node<T> {\n\treturn switchMap(trigger, () => fromAny(runner()) as Node<T>);\n}\n\n// ---------------------------------------------------------------------------\n\n/** Per-task delta produced by {@link beforeAfterCompare}. */\nexport interface EvalTaskDelta {\n\ttaskId: string;\n\tbefore: boolean;\n\tafter: boolean;\n\t/** Score-level diff (after − before), undefined if no scores present. */\n\tscoreDiff?: number;\n}\n\n/** Output of {@link beforeAfterCompare}. */\nexport interface EvalDelta {\n\t/** Task IDs that newly fail in `after` (were passing in `before`). */\n\tnewFailures: string[];\n\t/** Task IDs that now pass in `after` (were failing in `before`). */\n\tresolved: string[];\n\t/** Full per-task breakdown. */\n\ttaskDeltas: EvalTaskDelta[];\n\t/** True when net resolutions > net failures. */\n\toverallImproved: boolean;\n}\n\n/** Options for {@link beforeAfterCompare}. */\nexport interface BeforeAfterCompareOptions {\n\t/** Graph to register the derived node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Node holding the baseline eval result. */\n\tbefore: Node<EvalRunResult>;\n\t/** Node holding the new eval result. */\n\tafter: Node<EvalRunResult>;\n\t/** Derived-node name (default `\"eval-delta\"`). */\n\tname?: string;\n}\n\n/**\n * Derived node that computes before/after eval deltas.\n *\n * Pure computation: no LLM, no async. Compares per-task validity and\n * pass counts between two `EvalRunResult` snapshots.\n *\n * The derived node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n */\nexport function beforeAfterCompare(opts: BeforeAfterCompareOptions): Node<EvalDelta> {\n\tconst { graph, before, after, name = \"eval-delta\" } = opts;\n\tconst der = node<EvalDelta>(\n\t\t[before as Node<unknown>, after as Node<unknown>],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst bRes = data[0] as EvalRunResult;\n\t\t\tconst aRes = data[1] as EvalRunResult;\n\n\t\t\tconst beforeMap = new Map<string, EvalTaskResult>(bRes.tasks.map((t) => [t.task_id, t]));\n\t\t\tconst afterMap = new Map<string, EvalTaskResult>(aRes.tasks.map((t) => [t.task_id, t]));\n\n\t\t\tconst allIds = new Set([...beforeMap.keys(), ...afterMap.keys()]);\n\t\t\tconst taskDeltas: EvalTaskDelta[] = [];\n\t\t\tconst newFailures: string[] = [];\n\t\t\tconst resolved: string[] = [];\n\n\t\t\tfor (const id of allIds) {\n\t\t\t\tconst bt = beforeMap.get(id);\n\t\t\t\tconst at = afterMap.get(id);\n\t\t\t\tconst beforeValid = bt?.valid ?? false;\n\t\t\t\tconst afterValid = at?.valid ?? false;\n\n\t\t\t\tconst beforeScore = bt?.judge_scores\n\t\t\t\t\t? bt.judge_scores.filter((s) => s.pass).length\n\t\t\t\t\t: undefined;\n\t\t\t\tconst afterScore = at?.judge_scores\n\t\t\t\t\t? at.judge_scores.filter((s) => s.pass).length\n\t\t\t\t\t: undefined;\n\t\t\t\tconst scoreDiff =\n\t\t\t\t\tbeforeScore !== undefined && afterScore !== undefined\n\t\t\t\t\t\t? afterScore - beforeScore\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\ttaskDeltas.push({ taskId: id, before: beforeValid, after: afterValid, scoreDiff });\n\t\t\t\tif (beforeValid && !afterValid) newFailures.push(id);\n\t\t\t\tif (!beforeValid && afterValid) resolved.push(id);\n\t\t\t}\n\n\t\t\tactions.emit({\n\t\t\t\tnewFailures,\n\t\t\t\tresolved,\n\t\t\t\ttaskDeltas,\n\t\t\t\toverallImproved: resolved.length > newFailures.length,\n\t\t\t});\n\t\t},\n\t\t{ describeKind: \"derived\" },\n\t);\n\tgraph.add(der as Node<unknown>, { name });\n\treturn der;\n}\n\n// ---------------------------------------------------------------------------\n\n/** Options for {@link affectedTaskFilter}. */\nexport interface AffectedTaskFilterOptions {\n\t/** Graph to register the derived node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Node holding the current list of triaged items. */\n\tissues: Node<readonly TriagedItem[]>;\n\t/**\n\t * Optional node (or plain array) of all known task IDs.\n\t * When provided, output is the intersection.\n\t */\n\tfullTaskSet?: Node<readonly string[]> | readonly string[];\n\t/** Derived-node name (default `\"affected-task-filter\"`). */\n\tname?: string;\n}\n\n/**\n * Derived node that selects which eval task IDs to re-run.\n *\n * Collects `affectsEvalTasks` from all triaged items, deduplicates, then\n * optionally intersects with `fullTaskSet`. Returns a sorted array of IDs.\n *\n * Use this to avoid re-running the full eval suite after each fix: only the\n * tasks that the triaged items claim to affect are returned.\n *\n * The derived node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n */\nexport function affectedTaskFilter(opts: AffectedTaskFilterOptions): Node<string[]> {\n\tconst { graph, issues, fullTaskSet, name = \"affected-task-filter\" } = opts;\n\n\tlet taskSetNode: Node<unknown> | null = null;\n\tif (fullTaskSet != null) {\n\t\tif (Array.isArray(fullTaskSet)) {\n\t\t\t// Static-array form: register the inline state node so it appears\n\t\t\t// in `describe()`/`explain()` walks (EC8 — qa 2026-04-30).\n\t\t\tconst inlineSet = node([], { initial: fullTaskSet as readonly string[] });\n\t\t\tgraph.add(inlineSet, { name: `${name}/fullTaskSet` });\n\t\t\ttaskSetNode = inlineSet as Node<unknown>;\n\t\t} else {\n\t\t\t// User-supplied Node — owned by the caller's graph; don't re-add.\n\t\t\ttaskSetNode = fullTaskSet as Node<unknown>;\n\t\t}\n\t}\n\n\tconst deps: Node<unknown>[] = [issues as Node<unknown>];\n\tif (taskSetNode) deps.push(taskSetNode);\n\n\tconst der = node<string[]>(\n\t\tdeps,\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst items = data[0] as readonly TriagedItem[];\n\t\t\tconst all = taskSetNode ? new Set(data[1] as readonly string[]) : null;\n\n\t\t\tconst affected = new Set<string>();\n\t\t\tfor (const item of items) {\n\t\t\t\tfor (const id of item.affectsEvalTasks ?? []) {\n\t\t\t\t\tif (all == null || all.has(id)) affected.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tactions.emit([...affected].sort());\n\t\t},\n\t\t{ describeKind: \"derived\" },\n\t);\n\tgraph.add(der as Node<unknown>, { name });\n\treturn der;\n}\n\n// ---------------------------------------------------------------------------\n// Composition D: Quality gate (CI/CD)\n// ---------------------------------------------------------------------------\n\n/** A single lint error emitted by a CI tool. */\nexport interface LintError {\n\tfile: string;\n\tline: number;\n\tcol: number;\n\trule: string;\n\tmessage: string;\n}\n\n/** A single test failure emitted by a test runner. */\nexport interface TestFailure {\n\ttestId: string;\n\tfile: string;\n\tmessage: string;\n}\n\n/** Structured code-change / CI event. */\nexport interface CodeChange {\n\t/** Files touched by the change. */\n\tfiles: string[];\n\tlintErrors?: LintError[];\n\ttestFailures?: TestFailure[];\n}\n\n/** Options for {@link codeChangeBridge}. */\nexport interface CodeChangeBridgeOptions {\n\t/** Graph to register the effect node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** Node emitting CodeChange events. */\n\tsource: Node<CodeChange>;\n\t/** TopicGraph to publish IntakeItem entries to. */\n\tintakeTopic: TopicGraph<IntakeItem>;\n\t/** Optional custom parser (overrides default). */\n\tparser?: (change: CodeChange) => IntakeItem[];\n\t/** Effect-node name (default `\"code-change-bridge\"`). */\n\tname?: string;\n\t/** Default severity for generated IntakeItems (default `\"high\"`). */\n\tdefaultSeverity?: Severity;\n}\n\n/**\n * Intake bridge for code-change / CI events.\n *\n * Watches a source node for `CodeChange` events and publishes one\n * `IntakeItem` per lint error and per test failure to the intake topic.\n * Pass a custom `parser` to override the default mapping.\n *\n * The effect node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n */\nexport function codeChangeBridge(opts: CodeChangeBridgeOptions): Node<unknown> {\n\tconst {\n\t\tgraph,\n\t\tsource,\n\t\tintakeTopic,\n\t\tparser,\n\t\tname = \"code-change-bridge\",\n\t\tdefaultSeverity = \"high\",\n\t} = opts;\n\n\tfunction defaultParser(change: CodeChange): IntakeItem[] {\n\t\tconst items: IntakeItem[] = [];\n\t\tfor (const err of change.lintErrors ?? []) {\n\t\t\titems.push({\n\t\t\t\tsource: \"code-change\",\n\t\t\t\tsummary: `Lint: ${err.rule} in ${err.file}:${err.line}`,\n\t\t\t\tevidence: err.message,\n\t\t\t\taffectsAreas: [err.file],\n\t\t\t\tseverity: defaultSeverity,\n\t\t\t});\n\t\t}\n\t\tfor (const fail of change.testFailures ?? []) {\n\t\t\titems.push({\n\t\t\t\tsource: \"test\",\n\t\t\t\tsummary: `Test failure: ${fail.testId}`,\n\t\t\t\tevidence: fail.message,\n\t\t\t\taffectsAreas: [fail.file],\n\t\t\t\taffectsEvalTasks: [fail.testId],\n\t\t\t\tseverity: defaultSeverity,\n\t\t\t});\n\t\t}\n\t\treturn items;\n\t}\n\n\tconst resolve = parser ?? defaultParser;\n\n\tconst eff = node(\n\t\t[source as Node<unknown>],\n\t\t(batchData, _actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst change = data[0];\n\t\t\tif (change === undefined) return;\n\t\t\tfor (const item of resolve(change as CodeChange)) {\n\t\t\t\tintakeTopic.publish(item);\n\t\t\t}\n\t\t},\n\t\t{ describeKind: \"effect\" },\n\t);\n\tgraph.add(eff, { name });\n\treturn eff;\n}\n\n// ---------------------------------------------------------------------------\n\n/** Transport function for {@link notifyEffect}. Sync or async. */\nexport type NotifyTransport<T> = (item: T) => void | Promise<void>;\n\n/** Options for {@link notifyEffect}. */\nexport interface NotifyEffectOptions<T> {\n\t/** Graph to register the effect node on (B.1 narrow-waist visibility). */\n\tgraph: Graph;\n\t/** TopicGraph whose latest entry triggers the notification. */\n\ttopic: TopicGraph<T>;\n\t/** Called with each new item. May return a Promise. */\n\ttransport: NotifyTransport<T>;\n\t/** Effect-node name (default `\"notify-effect\"`). */\n\tname?: string;\n}\n\n/**\n * Effect node that sends each new topic entry to an external channel.\n *\n * The `transport` function is called for every item published to `topic`.\n * Async transports are bridged via `fromAny` (spec §5.10 compliant).\n *\n * Typical use: Slack webhook, GitHub PR comment, email notification, etc.\n * The factory provides reactive wiring; the transport supplies domain logic.\n *\n * The effect node is registered on the supplied `graph` so it appears in\n * `describe()` and is owned by the graph's lifecycle.\n *\n * ```ts\n * notifyEffect({ graph, topic: alertQueue, transport: async (item) => {\n * await fetch(SLACK_WEBHOOK, { method: 'POST', body: JSON.stringify({ text: item.summary }) });\n * }});\n * ```\n */\nexport function notifyEffect<T>(opts: NotifyEffectOptions<T>): Node<unknown> {\n\tconst { graph, topic, transport, name = \"notify-effect\" } = opts;\n\t// SENTINEL contract on `topic.latest` (COMPOSITION-GUIDE §1a + spec §5.12):\n\t// - `topic.publish(undefined)` is rejected at the publish boundary, so\n\t// `undefined` is exclusively the protocol SENTINEL on the read side.\n\t// - `topic.latest` stays SENTINEL on empty (no eager DATA emission), so\n\t// the partial-false first-run gate holds this fn until the first publish.\n\t// - Legit `null` DATA (when `T` includes `null`) reaches `transport`;\n\t// user transports must handle `null` themselves per v5.\n\t// The `=== undefined` guard below is defense-in-depth for any future\n\t// empty-batch wave where `prevData[0]` is still SENTINEL — in normal flow\n\t// the first-run gate has already filtered the empty case.\n\tconst eff = node(\n\t\t[topic.latest as Node<unknown>],\n\t\t(batchData, _actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst item = data[0];\n\t\t\tif (item === undefined) return;\n\t\t\t// transport is a side effect (webhook, Slack, email). Async transports\n\t\t\t// are fire-and-forget — the Promise result does not feed back into the\n\t\t\t// graph. Suppress unhandled-rejection noise by voiding the return.\n\t\t\tvoid transport(item as T);\n\t\t},\n\t\t{ describeKind: \"effect\" },\n\t);\n\tgraph.add(eff, { name });\n\treturn eff;\n}\n"],"mappings":";AAiCA,SAAS,UAAU,MAAM,OAAiC,YAAY;AACtE,SAAS,eAA+B;AAiDxC,SAAS,gBAAmB,QAAW,MAAqC;AAC3E,SAAO;AAAA,IACN,SAAS;AAAA,IACT,QAAQ,oBAAoB,KAAK,YAAY,QAAQ,SAAS,KAAK,OAAO,CAAC;AAAA,IAC3E,UAAU;AAAA,EACX;AACD;AAEA,SAAS,eAAkB,KAAc,MAAqC;AAC7E,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,SAAO;AAAA,IACN,SAAS;AAAA,IACT,QAAQ,qBAAqB,KAAK,YAAY,KAAK,OAAO;AAAA,EAC3D;AACD;AAEA,SAAS,kBAAkB,MAA2B;AACrD,SAAO,oBAAoB,KAAK,YAAY;AAC7C;AAEA,SAAS,SAAS,GAAW,MAAM,IAAY;AAC9C,SAAO,EAAE,UAAU,MAAM,IAAI,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AACpD;AAqBO,SAAS,iBAAoB,QAAuD;AAC1F,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,UAAU,OAAO,WAAW;AAClC,QAAM,aAAa,OAAO,cAAc;AAExC,SAAO,CAAC,KAAwC,SAAS;AACxD,UAAM,OAAO,IAAI,QAAQ;AAEzB,QAAI,OAAO,eAAe,CAAC,OAAO,YAAY,IAAI,GAAG;AAGpD,aAAO;AAAA,QACN,GAAG,IAAI;AAAA,QACP,WAAW,EAAE,MAAM,SAAS,WAAW,QAAQ,WAAW,IAAI,EAAE;AAAA,MACjE;AAAA,IACD;AAEA,WAAO;AAAA,MACN,CAAC;AAAA,MACD,CAAC,OAAO,YAAY;AACnB,cAAM,KAAK,IAAI,gBAAgB;AAI/B,cAAM,eAAe,MAAM;AAC3B,YAAI,eAA2B,MAAM;AACrC,YAAI,cAAc;AACjB,cAAI,aAAa,SAAS;AACzB,eAAG,MAAM;AAAA,UACV,OAAO;AACN,kBAAM,gBAAgB,MAAY,GAAG,MAAM;AAC3C,yBAAa,iBAAiB,SAAS,eAAe,EAAE,MAAM,KAAK,CAAC;AACpE,2BAAe,MAAM,aAAa,oBAAoB,SAAS,aAAa;AAAA,UAC7E;AAAA,QACD;AACA,YAAI,WAAW;AACf,YAAI,QAA6B;AACjC,cAAM,WAAW,CAAC,QAAgC;AACjD,cAAI,SAAU;AACd,qBAAW;AACX,kBAAQ,KAAK;AAAA,YACZ,CAAC,MAAM,EAAE,GAAG,IAAI,SAAS,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,CAAC;AAAA,YACtD,CAAC,QAAQ;AAAA,UACV,CAAoB;AACpB,kBAAQ;AACR,kBAAQ;AAAA,QACT;AACA,YAAI;AACJ,YAAI;AACH,gBAAM,YAAY,OAAO,MAAM,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;AAC1D,kBAAQ,QAAW,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC;AAAA,QACpD,SAAS,KAAK;AACb,mBAAS,QAAQ,KAAK,IAAI,CAAC;AAC3B,iBAAO,MAAM;AACZ,yBAAa;AACb,eAAG,MAAM;AAAA,UACV;AAAA,QACD;AACA,gBAAQ,MAAM,UAAU,CAAC,UAAU;AAClC,qBAAW,KAAK,OAAO;AACtB,gBAAI,SAAU;AACd,gBAAI,EAAE,CAAC,MAAM,MAAM;AAClB,uBAAS,SAAS,EAAE,CAAC,GAAQ,IAAI,CAAC;AAClC;AAAA,YACD;AACA,gBAAI,EAAE,CAAC,MAAM,OAAO;AACnB,uBAAS,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC;AAC5B;AAAA,YACD;AACA,gBAAI,EAAE,CAAC,MAAM,UAAU;AACtB,uBAAS,QAAQ,IAAI,MAAM,gDAAgD,GAAG,IAAI,CAAC;AACnF;AAAA,YACD;AAAA,UACD;AAAA,QACD,CAAC;AAMD,YAAI,YAAY,OAAO;AACtB,gBAAM;AACN,kBAAQ;AAAA,QACT;AACA,eAAO,MAAM;AACZ,uBAAa;AACb,aAAG,MAAM;AACT,kBAAQ;AACR,kBAAQ;AAAA,QACT;AAAA,MACD;AAAA,MACA,EAAE,MAAM,GAAG,IAAI,UAAU,cAAc,WAAW;AAAA,IACnD;AAAA,EACD;AACD;AAsCO,SAAS,iBAAoB,QAAuD;AAC1F,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,YAAY,OAAO,WAAW;AACpC,QAAM,aAAa,aAAa;AAChC,SAAO,iBAAoB;AAAA,IAC1B,OAAO,CAAC,MAAM,SAAS;AACtB,YAAM,KAAK,OAAO,OAAO,OAAO,QAAQ,KAAK,YAAY,IACtD,OAAO,OAAO,KAAK,YAAY,IAC/B;AACH,UAAI,CAAC,IAAI;AACR,cAAM,IAAI,MAAM,gDAAgD,KAAK,YAAY,GAAG;AAAA,MACrF;AACA,aAAO,GAAG,MAAM,IAAI;AAAA,IACrB;AAAA,IACA,aAAa,CAAC,SAAS,OAAO,OAAO,OAAO,QAAQ,KAAK,YAAY,KAAK;AAAA,IAC1E,YAAY,CAAC,SAAS,8BAA8B,KAAK,YAAY;AAAA,IACrE;AAAA,EACD,CAAC;AACF;;;AC5OA,SAAS,YAAAA,WAAU,QAAAC,OAAM,SAAAC,QAAiC,QAAAC,aAAY;AAmE/D,SAAS,aAAuB,QAA2C;AACjF,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,UACL,OAAO,YAAY,CAAC,OAAyB,GAAG,UAAU,YAAY;AACvE,QAAM,YAAY,OAAO,cAAc,MAAM;AAE7C,SAAOA;AAAA,IACN,CAAC;AAAA,IACD,CAAC,OAAO,YAAY;AACnB,UAAI,QAA6B;AACjC,UAAI,aAAa;AACjB,YAAM,WAAW,MAAY;AAC5B,YAAI,WAAY;AAChB,qBAAa;AACb,gBAAQ;AACR,gBAAQ;AAAA,MACT;AACA,YAAM,oBAAoB,CAAC,QAAuB;AACjD,YAAI,WAAY;AAChB,gBAAQ,KAAK,CAAC,CAACD,QAAO,GAAG,CAAC,CAAoB;AAC9C,iBAAS;AAAA,MACV;AACA,cAAQ,OAAO,cAAc,UAAU,CAAC,UAAU;AACjD,YAAI,WAAY;AAChB,mBAAW,KAAK,OAAO;AACtB,cAAI,WAAY;AAChB,cAAI,EAAE,CAAC,MAAMD,OAAM;AAClB,gBAAI,EAAE,CAAC,MAAMD,WAAU;AAGtB,sBAAQ,KAAK,CAAC,CAACA,SAAQ,CAAC,CAAoB;AAC5C,uBAAS;AACT;AAAA,YACD;AACA;AAAA,UACD;AACA,gBAAM,KAAK,EAAE,CAAC;AACd,cAAI,MAAM,KAAM;AAChB,cAAI,CAAC,GAAG,SAAU;AAMlB,cAAI;AACJ,cAAI;AACH,mBAAO,UAAU,EAAE;AAAA,UACpB,SAAS,KAAK;AACb,8BAAkB,GAAG;AACrB;AAAA,UACD;AACA,cAAI,CAAC,KAAM;AACX,cAAI;AACJ,cAAI;AACH,uBAAW,QAAQ,EAAE;AAAA,UACtB,SAAS,KAAK;AACb,8BAAkB,GAAG;AACrB;AAAA,UACD;AACA,cAAI,YAAY,KAAM;AACtB,cAAI;AACH,mBAAO,MAAM,UAAU,EAAE;AAAA,UAC1B,SAAS,KAAK;AACb,8BAAkB,GAAG;AACrB;AAAA,UACD;AACA,kBAAQ,KAAK,CAAC,CAACC,OAAM,QAAQ,CAAC,CAAoB;AAAA,QACnD;AAAA,MACD,CAAC;AAKD,UAAI,cAAc,OAAO;AACxB,cAAM;AACN,gBAAQ;AAAA,MACT;AACA,aAAO,MAAM;AACZ,iBAAS;AAAA,MACV;AAAA,IACD;AAAA,IACA,EAAE,MAAM,cAAc,WAAW;AAAA,EAClC;AACD;;;ACrLA,SAAoB,QAAAG,aAAY;AAChC,SAAS,WAAAC,UAAS,iBAAiB;AAwC5B,SAAS,mBAAsB,MAAmD;AACxF,QAAM,EAAE,OAAO,QAAQ,aAAa,QAAQ,OAAO,gBAAgB,IAAI;AACvE,QAAM,MAAMD;AAAA,IACX,CAAC,MAAuB;AAAA,IACxB,CAAC,WAAW,UAAU,QAAQ;AAC7B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,QAAQ,KAAK,CAAC;AACpB,UAAI,UAAU,OAAW;AACzB,YAAM,QAAQ,OAAO,KAAU;AAC/B,iBAAW,QAAQ,OAAO;AACzB,oBAAY,QAAQ,IAAI;AAAA,MACzB;AAAA,IACD;AAAA,IACA,EAAE,cAAc,SAAS;AAAA,EAC1B;AACA,QAAM,IAAI,KAAK,EAAE,KAAK,CAAC;AACvB,SAAO;AACR;AA2DO,SAAS,iBAAiB,MAA8C;AAC9E,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,kBAAkB;AAAA,EACnB,IAAI;AAEJ,QAAM,MAAMA;AAAA,IACX,CAAC,MAAuB;AAAA,IACxB,CAAC,WAAW,UAAU,QAAQ;AAC7B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,UAAU,KAAK,CAAC;AACtB,UAAI,YAAY,OAAW;AAC3B,YAAM,OAAO,MAAM,QAAQ,OAAO,IAC9B,UACD,CAAC,OAAwB;AAE5B,iBAAW,OAAO,MAAM;AACvB,mBAAW,QAAQ,IAAI,OAAO;AAE7B,cAAI,KAAK,SAAS,KAAK,cAAc,MAAM,CAAC,MAAM,EAAE,IAAI,EAAG;AAG3D,cAAI,CAAC,KAAK,UAAU,CAAC,KAAK,gBAAgB,KAAK,aAAa,WAAW,IAAI;AAC1E,wBAAY,QAAQ;AAAA,cACnB,QAAQ;AAAA,cACR,SAAS,QAAQ,KAAK,OAAO,oBAAoB,IAAI,KAAK;AAAA,cAC1D,UAAU,OAAO,IAAI,MAAM;AAAA,cAC3B,cAAc,CAAC,WAAW;AAAA,cAC1B,kBAAkB,CAAC,KAAK,OAAO;AAAA,cAC/B,UAAU;AAAA,YACX,CAAC;AACD;AAAA,UACD;AAGA,cAAI,KAAK,cAAc;AACtB,uBAAW,SAAS,KAAK,cAAc;AACtC,kBAAI,MAAM,KAAM;AAChB,0BAAY,QAAQ;AAAA,gBACnB,QAAQ;AAAA,gBACR,SAAS,GAAG,KAAK,OAAO,KAAK,MAAM,KAAK,YAAY,IAAI,KAAK;AAAA,gBAC7D,UAAU,MAAM;AAAA,gBAChB,cAAc,CAAC,WAAW;AAAA,gBAC1B,kBAAkB,CAAC,KAAK,OAAO;AAAA,gBAC/B,UAAU;AAAA,cACX,CAAC;AAAA,YACF;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA,EAAE,cAAc,SAAS;AAAA,EAC1B;AACA,QAAM,IAAI,KAAK,EAAE,KAAK,CAAC;AACvB,SAAO;AACR;AA0BO,SAAS,WACf,SACA,QACU;AACV,SAAO,UAAU,SAAS,MAAMC,SAAQ,OAAO,CAAC,CAAY;AAC7D;AA8CO,SAAS,mBAAmB,MAAkD;AACpF,QAAM,EAAE,OAAO,QAAQ,OAAO,OAAO,aAAa,IAAI;AACtD,QAAM,MAAMD;AAAA,IACX,CAAC,QAAyB,KAAsB;AAAA,IAChD,CAAC,WAAW,SAAS,QAAQ;AAC5B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,OAAO,KAAK,CAAC;AACnB,YAAM,OAAO,KAAK,CAAC;AAEnB,YAAM,YAAY,IAAI,IAA4B,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;AACvF,YAAM,WAAW,IAAI,IAA4B,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;AAEtF,YAAM,SAAS,oBAAI,IAAI,CAAC,GAAG,UAAU,KAAK,GAAG,GAAG,SAAS,KAAK,CAAC,CAAC;AAChE,YAAM,aAA8B,CAAC;AACrC,YAAM,cAAwB,CAAC;AAC/B,YAAM,WAAqB,CAAC;AAE5B,iBAAW,MAAM,QAAQ;AACxB,cAAM,KAAK,UAAU,IAAI,EAAE;AAC3B,cAAM,KAAK,SAAS,IAAI,EAAE;AAC1B,cAAM,cAAc,IAAI,SAAS;AACjC,cAAM,aAAa,IAAI,SAAS;AAEhC,cAAM,cAAc,IAAI,eACrB,GAAG,aAAa,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,SACtC;AACH,cAAM,aAAa,IAAI,eACpB,GAAG,aAAa,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,SACtC;AACH,cAAM,YACL,gBAAgB,UAAa,eAAe,SACzC,aAAa,cACb;AAEJ,mBAAW,KAAK,EAAE,QAAQ,IAAI,QAAQ,aAAa,OAAO,YAAY,UAAU,CAAC;AACjF,YAAI,eAAe,CAAC,WAAY,aAAY,KAAK,EAAE;AACnD,YAAI,CAAC,eAAe,WAAY,UAAS,KAAK,EAAE;AAAA,MACjD;AAEA,cAAQ,KAAK;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA,iBAAiB,SAAS,SAAS,YAAY;AAAA,MAChD,CAAC;AAAA,IACF;AAAA,IACA,EAAE,cAAc,UAAU;AAAA,EAC3B;AACA,QAAM,IAAI,KAAsB,EAAE,KAAK,CAAC;AACxC,SAAO;AACR;AA+BO,SAAS,mBAAmB,MAAiD;AACnF,QAAM,EAAE,OAAO,QAAQ,aAAa,OAAO,uBAAuB,IAAI;AAEtE,MAAI,cAAoC;AACxC,MAAI,eAAe,MAAM;AACxB,QAAI,MAAM,QAAQ,WAAW,GAAG;AAG/B,YAAM,YAAYA,MAAK,CAAC,GAAG,EAAE,SAAS,YAAiC,CAAC;AACxE,YAAM,IAAI,WAAW,EAAE,MAAM,GAAG,IAAI,eAAe,CAAC;AACpD,oBAAc;AAAA,IACf,OAAO;AAEN,oBAAc;AAAA,IACf;AAAA,EACD;AAEA,QAAM,OAAwB,CAAC,MAAuB;AACtD,MAAI,YAAa,MAAK,KAAK,WAAW;AAEtC,QAAM,MAAMA;AAAA,IACX;AAAA,IACA,CAAC,WAAW,SAAS,QAAQ;AAC5B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,QAAQ,KAAK,CAAC;AACpB,YAAM,MAAM,cAAc,IAAI,IAAI,KAAK,CAAC,CAAsB,IAAI;AAElE,YAAM,WAAW,oBAAI,IAAY;AACjC,iBAAW,QAAQ,OAAO;AACzB,mBAAW,MAAM,KAAK,oBAAoB,CAAC,GAAG;AAC7C,cAAI,OAAO,QAAQ,IAAI,IAAI,EAAE,EAAG,UAAS,IAAI,EAAE;AAAA,QAChD;AAAA,MACD;AACA,cAAQ,KAAK,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC;AAAA,IAClC;AAAA,IACA,EAAE,cAAc,UAAU;AAAA,EAC3B;AACA,QAAM,IAAI,KAAsB,EAAE,KAAK,CAAC;AACxC,SAAO;AACR;AAwDO,SAAS,iBAAiB,MAA8C;AAC9E,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,kBAAkB;AAAA,EACnB,IAAI;AAEJ,WAAS,cAAc,QAAkC;AACxD,UAAM,QAAsB,CAAC;AAC7B,eAAW,OAAO,OAAO,cAAc,CAAC,GAAG;AAC1C,YAAM,KAAK;AAAA,QACV,QAAQ;AAAA,QACR,SAAS,SAAS,IAAI,IAAI,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,QACrD,UAAU,IAAI;AAAA,QACd,cAAc,CAAC,IAAI,IAAI;AAAA,QACvB,UAAU;AAAA,MACX,CAAC;AAAA,IACF;AACA,eAAW,QAAQ,OAAO,gBAAgB,CAAC,GAAG;AAC7C,YAAM,KAAK;AAAA,QACV,QAAQ;AAAA,QACR,SAAS,iBAAiB,KAAK,MAAM;AAAA,QACrC,UAAU,KAAK;AAAA,QACf,cAAc,CAAC,KAAK,IAAI;AAAA,QACxB,kBAAkB,CAAC,KAAK,MAAM;AAAA,QAC9B,UAAU;AAAA,MACX,CAAC;AAAA,IACF;AACA,WAAO;AAAA,EACR;AAEA,QAAM,UAAU,UAAU;AAE1B,QAAM,MAAMA;AAAA,IACX,CAAC,MAAuB;AAAA,IACxB,CAAC,WAAW,UAAU,QAAQ;AAC7B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,SAAS,KAAK,CAAC;AACrB,UAAI,WAAW,OAAW;AAC1B,iBAAW,QAAQ,QAAQ,MAAoB,GAAG;AACjD,oBAAY,QAAQ,IAAI;AAAA,MACzB;AAAA,IACD;AAAA,IACA,EAAE,cAAc,SAAS;AAAA,EAC1B;AACA,QAAM,IAAI,KAAK,EAAE,KAAK,CAAC;AACvB,SAAO;AACR;AAqCO,SAAS,aAAgB,MAA6C;AAC5E,QAAM,EAAE,OAAO,OAAO,WAAW,OAAO,gBAAgB,IAAI;AAW5D,QAAM,MAAMA;AAAA,IACX,CAAC,MAAM,MAAuB;AAAA,IAC9B,CAAC,WAAW,UAAU,QAAQ;AAC7B,YAAM,OAAO,UAAU;AAAA,QAAI,CAAC,OAAO,MAClC,SAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,EAAE,IAAI,IAAI,SAAS,CAAC;AAAA,MAClE;AACA,YAAM,OAAO,KAAK,CAAC;AACnB,UAAI,SAAS,OAAW;AAIxB,WAAK,UAAU,IAAS;AAAA,IACzB;AAAA,IACA,EAAE,cAAc,SAAS;AAAA,EAC1B;AACA,QAAM,IAAI,KAAK,EAAE,KAAK,CAAC;AACvB,SAAO;AACR;","names":["COMPLETE","DATA","ERROR","node","node","fromAny"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/presets/ai/agent-loop.ts","../src/presets/ai/agent.ts","../src/presets/ai/agents.ts"],"sourcesContent":["/**\n * Reactive agent loop — autonomous multi-turn LLM agent with tool execution.\n */\n\nexport type AgentLoopStatus = \"idle\" | \"thinking\" | \"acting\" | \"done\" | \"error\";\n\nimport {\n\tbatch,\n\tDATA,\n\tERROR,\n\tINVALIDATE,\n\ttype Node,\n\tnode,\n\tnode as nodeFactory,\n\tplaceholderArgs,\n\tRESOLVED,\n} from \"@graphrefly/pure-ts/core\";\nimport { fromAny, keepalive, switchMap } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\nimport { awaitSettled } from \"../../base/sources/settled.js\";\nimport { aiMeta } from \"../../utils/ai/_internal.js\";\nimport type {\n\tChatMessage,\n\tLLMAdapter,\n\tLLMResponse,\n\tToolCall,\n\tToolDefinition,\n} from \"../../utils/ai/adapters/core/types.js\";\nimport { type ChatStreamGraph, chatStream } from \"../../utils/ai/agents/chat-stream.js\";\nimport { type ToolResult, toolExecution } from \"../../utils/ai/agents/tool-execution.js\";\nimport { type ToolRegistryGraph, toolRegistry } from \"../../utils/ai/agents/tool-registry.js\";\n\nexport type { ToolResult } from \"../../utils/ai/agents/tool-execution.js\";\n\n// ---------------------------------------------------------------------------\n// agentLoop\n// ---------------------------------------------------------------------------\n\nexport type AgentLoopOptions = {\n\tgraph?: GraphOptions;\n\tadapter: LLMAdapter;\n\ttools?: readonly ToolDefinition[];\n\tsystemPrompt?: string;\n\tmaxTurns?: number;\n\tstopWhen?: (response: LLMResponse) => boolean;\n\tonToolCall?: (call: ToolCall) => void;\n\tmaxMessages?: number;\n\tmodel?: string;\n\ttemperature?: number;\n\tmaxTokens?: number;\n\t/**\n\t * Reactive tool-call splice (COMPOSITION-GUIDE §31 \"interception is security\").\n\t * When set, the raw `toolCalls` node is piped through this transform before\n\t * reaching the executor. The transform is a pure reactive composition —\n\t * `(calls: Node<readonly ToolCall[]>) => Node<readonly ToolCall[]>` — so the\n\t * gate is visible in `describe()` / `explain()` as a real edge (no hidden\n\t * imperative wraps; §24).\n\t *\n\t * Typical uses:\n\t * - **Filter / block** — `derived([calls, policy], ([raw, p]) => raw.filter(p))`\n\t * - **Throttle / debounce** — `throttle(calls, windowMs)`\n\t * - **Human-in-the-loop approval** — pipe through a `gate` controller so\n\t * calls wait for human approval before reaching the executor.\n\t *\n\t * The public `agent.toolCalls` node surfaces the POST-intercept stream, so\n\t * audit / telemetry consumers see what the executor actually runs. The raw\n\t * pre-intercept stream is not exposed — tests that need it should run\n\t * without `interceptToolCalls` set (the identity case).\n\t */\n\tinterceptToolCalls?: (calls: Node<readonly ToolCall[]>) => Node<readonly ToolCall[]>;\n};\n\n/**\n * Reactive agent loop.\n *\n * The loop is a reactive state machine wired entirely from graph primitives:\n * `chat.messages` + `tools.schemas` + gating state feed a `promptInput`\n * derived; `switchMap` turns non-null inputs into an LLM invocation via\n * `fromAny(adapter.invoke(...))`. The LLM response drives chat writes and\n * status transitions via effects. Tool calls flow through a reactive\n * executor (`retrySource` + `rescue`) that retries once on error and\n * surfaces terminal errors as JSON-shaped `ToolResult` payloads for the\n * LLM to react to.\n *\n * **No imperative control flow inside the reactive layer** (spec §5.8-5.12):\n * no `while` loops, no manual `await adapter.invoke`, no polling.\n * `agent.run()` is a thin `awaitSettled` bridge so callers can still `await`\n * the loop if they want a Promise.\n *\n * Public surface:\n * - `chat` / `tools` — subgraphs (imperative `append` at boundary, reactive `executeReactive` for tool invocation)\n * - `status` / `turn` / `aborted` — state nodes with explicit initials\n * - `lastResponse` / `toolCalls` / `toolResults` — reactive outputs (SENTINEL until first emission; callers use `awaitSettled` / `subscribe`)\n * - `run(userMessage?, signal?)` — optional user append + Promise bridge\n * - `abort()` — imperative abort shim; flips `aborted` state\n *\n * **Lifecycle: single-mount.** `AgentLoopGraph` instances expect to be\n * constructed once and used until `destroy()`. The internal closure mirrors\n * (`latestTurn` / `latestAborted` / `latestStatus`) are wired by\n * subscribe-and-capture at construction time; their `addDisposer`-registered\n * subscriptions are torn down on subgraph unmount or `destroy()`. After\n * teardown the mirrors freeze at their last value, so re-using a destroyed\n * instance — calling `run()` again, or remounting under a new parent —\n * would silently feed stale mirror data into reactive fn bodies. If you\n * need to \"reset\" an agent, build a fresh `AgentLoopGraph` instance instead\n * of recycling.\n */\nexport class AgentLoopGraph extends Graph {\n\treadonly chat: ChatStreamGraph;\n\treadonly tools: ToolRegistryGraph;\n\n\t/** Current agent status. `initial: \"idle\"` — always has a real value. */\n\treadonly status: Node<AgentLoopStatus>;\n\t/** Turn count (completed LLM invocations this run). `initial: 0`. */\n\treadonly turn: Node<number>;\n\t/** Aborted flag; flipped by `abort()` or external `AbortSignal`. `initial: false`. */\n\treadonly aborted: Node<boolean>;\n\n\t/**\n\t * Most recent LLM response. State-backed mirror driven by the response\n\t * effect. **Stays SENTINEL** (`cache === undefined`, no DATA emitted)\n\t * until the first real response — bridge subscribers see no spurious\n\t * push-on-subscribe DATA. After a real response, holds the latest\n\t * `LLMResponse`. Reset between `run()` calls via `[[INVALIDATE]]` (clears\n\t * cache back to SENTINEL) so a second run with a pre-aborted signal\n\t * cannot leak the prior run's response. Bridge with\n\t * `awaitSettled(lastResponse)` for the first DATA as a Promise; consumers\n\t * inside reactive fns gate on `ctx.prevData[i] === undefined`.\n\t */\n\treadonly lastResponse: Node<LLMResponse>;\n\t/** Tool-call batch emitted by the most recent LLM response. SENTINEL. */\n\treadonly toolCalls: Node<readonly ToolCall[]>;\n\t/** Tool-result batch (one entry per call) after reactive execution. SENTINEL. */\n\treadonly toolResults: Node<readonly ToolResult[]>;\n\n\tprivate readonly _terminalResult: Node<LLMResponse>;\n\tprivate readonly _disposeRunWiring: () => void;\n\t/** Guards against overlapping `run()` calls. */\n\tprivate _running = false;\n\t/**\n\t * Abort controller for the currently-running `adapter.invoke`. Minted per\n\t * switchMap project; aborted when the reactive `aborted` node flips true\n\t * OR when the caller's external `AbortSignal` fires. Threaded into\n\t * `adapter.invoke({ signal })` AND `fromAny(promise, { signal })`, so the\n\t * reactive layer sees ERROR when the wire call is cancelled.\n\t */\n\tprivate _currentAbortController: AbortController | null = null;\n\n\tconstructor(name: string, opts: AgentLoopOptions) {\n\t\tsuper(name, opts.graph);\n\n\t\t// Mount chat subgraph\n\t\tthis.chat = chatStream(`${name}-chat`, { maxMessages: opts.maxMessages });\n\t\tthis.mount(\"chat\", this.chat);\n\n\t\t// Mount tool registry subgraph\n\t\tthis.tools = toolRegistry(`${name}-tools`);\n\t\tthis.mount(\"tools\", this.tools);\n\n\t\tif (opts.tools) {\n\t\t\tfor (const tool of opts.tools) {\n\t\t\t\tthis.tools.register(tool);\n\t\t\t}\n\t\t}\n\n\t\t// --- State nodes (always have a real value; explicit initials) ---\n\t\tthis.status = node<AgentLoopStatus>([], {\n\t\t\t...{\n\t\t\t\tname: \"status\",\n\t\t\t\tdescribeKind: \"state\",\n\t\t\t\tmeta: aiMeta(\"agent_status\"),\n\t\t\t},\n\t\t\tinitial: \"idle\",\n\t\t});\n\t\tthis.add(this.status, { name: \"status\" });\n\n\t\tthis.turn = node<number>([], {\n\t\t\t...{\n\t\t\t\tname: \"turn\",\n\t\t\t\tdescribeKind: \"state\",\n\t\t\t\tmeta: aiMeta(\"agent_turn_count\"),\n\t\t\t},\n\t\t\tinitial: 0,\n\t\t});\n\t\tthis.add(this.turn, { name: \"turn\" });\n\n\t\tthis.aborted = node<boolean>([], {\n\t\t\t...{\n\t\t\t\tname: \"aborted\",\n\t\t\t\tdescribeKind: \"state\",\n\t\t\t\tmeta: aiMeta(\"agent_aborted\"),\n\t\t\t},\n\t\t\tinitial: false,\n\t\t});\n\t\tthis.add(this.aborted, { name: \"aborted\" });\n\n\t\t// --- Reactive pipeline ---\n\t\t//\n\t\t// **Read pattern (Phase 12 D1 lock + F2 fix 2026-05-01).** State\n\t\t// scratchpad held on this `AgentLoopGraph` (turn / aborted / chat /\n\t\t// tools) is read inside reactive fn bodies via either:\n\t\t// (a) `data[i]` / `ctx.prevData[i]` for declared deps, OR\n\t\t// (b) sole-owner `.cache` reads on subgraph-mounted state Nodes\n\t\t// (chat / tools mounted as subgraphs of this Graph; the agent\n\t\t// is the sole owner; promptInput / effResponse / effResults\n\t\t// live in the same enclosing constructor scope — sanctioned\n\t\t// form per Phase 12 D1 read-pattern lock).\n\t\t//\n\t\t// Closure mirrors (`latestTurn` / `latestAborted` / `latestStatus`)\n\t\t// are kept ONLY for fields where (b) doesn't simplify the call site\n\t\t// — turn / aborted / status are state Nodes registered on `this`\n\t\t// directly (not subgraphs), and the closure form keeps\n\t\t// effResponse's batch logic readable. They are CORRECT but are NOT\n\t\t// safe under nested drains (the subscribe handler may not have run\n\t\t// yet when a downstream fn reads the closure). For nested-drain-\n\t\t// reachable reads (promptInput, called via `agentLoop.run` from\n\t\t// inside another graph's reactive subscribe handler), prefer (a) or\n\t\t// (b) over the closure mirror. F2 root cause: chat.messages.cache\n\t\t// is updated DURING the DATA-settle phase (before subscribers\n\t\t// run), so `.cache` reads are always at least as fresh as closure\n\t\t// mirrors. See `optimizations.md` \"Phase 13 design-session inputs\n\t\t// from §13.M lock-test\" F2.\n\t\t//\n\t\t// **Pattern note on `latestTurn` staleness under in-batch reads.**\n\t\t// Effect 1 emits `turnNode.emit(next)` inside its batch; Effect 2\n\t\t// reads `latestTurn` on the following wave (after toolResults\n\t\t// settle). Because batch drain is FIFO, `turnSub`'s handler runs\n\t\t// before Effect 2's next wave fires, so `latestTurn` is up-to-date\n\t\t// by the time Effect 2 reads it. This invariant is stable as long\n\t\t// as `turnNode.emit` remains inside Effect 1's batch — a future\n\t\t// refactor that un-batches the emit would regress silently.\n\t\tlet latestTurn = 0;\n\t\tconst turnSub = this.turn.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) if (m[0] === DATA) latestTurn = m[1] as number;\n\t\t});\n\t\tlet latestAborted = false;\n\t\tconst abortedSub = this.aborted.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) if (m[0] === DATA) latestAborted = m[1] as boolean;\n\t\t});\n\n\t\tconst adapter = opts.adapter;\n\t\tconst systemPrompt = opts.systemPrompt;\n\t\tconst model = opts.model;\n\t\tconst temperature = opts.temperature;\n\t\tconst maxTokens = opts.maxTokens;\n\t\tconst maxTurns = opts.maxTurns ?? 10;\n\t\tconst stopWhen = opts.stopWhen;\n\n\t\t// Capture `this` for closures that don't bind `this`.\n\t\tconst chat = this.chat;\n\t\tconst tools = this.tools;\n\t\tconst statusNode = this.status;\n\t\tconst turnNode = this.turn;\n\t\tconst abortedNode = this.aborted;\n\n\t\t// promptInput: STATUS is the only reactive trigger — chat.messages,\n\t\t// tools.schemas, turn, aborted are sampled via closure-held mirrors\n\t\t// (all populated by subscribe-and-capture above). This prevents the\n\t\t// classic feedback cycle (COMPOSITION-GUIDE §7): if chat.messageCount\n\t\t// were a reactive dep here, effect 1's `chat.append` would trigger a\n\t\t// promptInput wave, which under effect-1's batch would see status\n\t\t// STILL \"thinking\" (pre-drain) and fire a spurious LLM invocation.\n\t\t// By gating only on status, chat writes don't re-trigger — only\n\t\t// explicit status transitions do.\n\t\tconst promptInput: Node<InvokeInput> = nodeFactory<InvokeInput>(\n\t\t\t[statusNode],\n\t\t\t(data, actions, ctx) => {\n\t\t\t\tconst stat = readLatest<AgentLoopStatus>(data, ctx.prevData, 0, \"idle\");\n\t\t\t\tif (stat !== \"thinking\" || latestAborted || latestTurn >= maxTurns) {\n\t\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// F2 fix (2026-05-01): under nested drains, the closure mirrors\n\t\t\t\t// `latestMessages` / `latestSchemas` lag the actual node state —\n\t\t\t\t// messagesSub / schemasSub subscribers may not have run yet when\n\t\t\t\t// promptInput fires (the drain processes status's downstream\n\t\t\t\t// before chat.messages's subscribers fire, even though\n\t\t\t\t// chat.messages.cache is already settled). Read `.cache`\n\t\t\t\t// directly per Phase 12 D1 read-pattern lock: chat / tools are\n\t\t\t\t// mounted as subgraphs of this AgentLoopGraph (sole owner) and\n\t\t\t\t// promptInput is a reactive reader in the same enclosing\n\t\t\t\t// constructor scope — sanctioned `.cache` form. See\n\t\t\t\t// `optimizations.md` \"Phase 13 design-session inputs from §13.M\n\t\t\t\t// lock-test\" F2.\n\t\t\t\tconst messages = (this.chat.messages.cache as readonly ChatMessage[] | undefined) ?? [];\n\t\t\t\tif (messages.length === 0) {\n\t\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst schemas = (this.tools.schemas.cache as readonly ToolDefinition[] | undefined) ?? [];\n\t\t\t\tactions.emit({ messages, tools: schemas });\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"promptInput\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: aiMeta(\"agent_prompt_input\", {\n\t\t\t\t\t// State this fn body samples beyond its declared `statusNode`\n\t\t\t\t\t// dep. `aborted` / `turn` come from §28 closure mirrors\n\t\t\t\t\t// (`latestAborted` / `latestTurn`); `chat.messages` /\n\t\t\t\t\t// `tools.schemas` come from sole-owner `.cache` reads\n\t\t\t\t\t// (Phase 12 D1 lock + F2 fix; see comment block above).\n\t\t\t\t\t// Listed here so inspection tooling can surface fold-in\n\t\t\t\t\t// state without grepping source.\n\t\t\t\t\tclosureReads: [\"aborted\", \"turn\", \"chat.messages\", \"tools.schemas\"],\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\n\t\tconst llmResponse: Node<LLMResponse> = switchMap(\n\t\t\tpromptInput,\n\t\t\t(input) => {\n\t\t\t\tconst controller = new AbortController();\n\t\t\t\tthis._currentAbortController = controller;\n\t\t\t\tif (latestAborted) {\n\t\t\t\t\tcontroller.abort(new Error(\"agentLoop: aborted\"));\n\t\t\t\t}\n\t\t\t\t// Wave A Unit B-CC fix: drop the `Promise.resolve(adapter.invoke(...))`\n\t\t\t\t// wrapper. `adapter.invoke` returns a `NodeInput<LLMResponse>`\n\t\t\t\t// (Promise | Node | raw). `fromAny` already handles all three\n\t\t\t\t// shapes; the manual `Promise.resolve` wrapper would force a\n\t\t\t\t// Node-returning adapter into an extra microtask hop and lose\n\t\t\t\t// reactivity (see Unit 11 + Unit 1 for the parallel cleanup).\n\t\t\t\treturn fromAny(\n\t\t\t\t\tadapter.invoke(input.messages, {\n\t\t\t\t\t\ttools: input.tools.length > 0 ? input.tools : undefined,\n\t\t\t\t\t\tsystemPrompt,\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\ttemperature,\n\t\t\t\t\t\tmaxTokens,\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t}),\n\t\t\t\t\t{ signal: controller.signal },\n\t\t\t\t);\n\t\t\t},\n\t\t\t{ equals: () => false },\n\t\t);\n\n\t\t// State mirror for `lastResponse` — exists for **cross-run reset\n\t\t// semantics**, NOT the §32 mid-wave hazard.\n\t\t//\n\t\t// Why: `llmResponse` is a switchMap output; its cache persists across\n\t\t// `run()` calls (switchMap's output node has no built-in reset path —\n\t\t// the cache stays at the last DATA the inner emitted). A second\n\t\t// `run()` with a pre-aborted signal would otherwise have\n\t\t// `_terminalResult` evaluate `stat=done` (driven by `effAbort`) +\n\t\t// `resp=<prior run's response>` (cached on `llmResponse`) and resolve\n\t\t// the Promise with stale data instead of rejecting with AbortError.\n\t\t// The mirror is **reset via `[[INVALIDATE]]`** in `run()`'s reset\n\t\t// batch — INVALIDATE clears `_cached` back to `undefined` (SENTINEL)\n\t\t// AND clears the `prevData` slot on every dependent (`_terminalResult`,\n\t\t// `toolCallsRaw`), so the abort path correctly emits\n\t\t// `[[ERROR, AbortError]]` from terminalResult's `stat=\"done\" &&\n\t\t// prevData[lastResponse] === undefined → ERROR` guard.\n\t\t//\n\t\t// **No `T | null` placeholder.** Per `feedback_use_prevdata_for_sentinel`\n\t\t// + COMPOSITION-GUIDE §1a, the SENTINEL state IS the \"never sent\n\t\t// real DATA yet\" signal. An eager `initial: null` would push `[null]`\n\t\t// to every fresh subscriber — a footgun for bridge subscribers that\n\t\t// would otherwise need `if (resp == null) continue` guards. Stay\n\t\t// SENTINEL; consumers detect \"no response yet\" via\n\t\t// `ctx.prevData[i] === undefined` (or `cache === undefined` outside\n\t\t// reactive fns). F9 lock-test (`multi-agent-example.test.ts` test 5)\n\t\t// pins this invariant.\n\t\t//\n\t\t// What this does NOT solve: the §32 mid-wave \"stale peer-read\"\n\t\t// hazard. Investigation (2026-04-25) confirmed `_dirtyDepCount`\n\t\t// gating in `_maybeRunFnOnSettlement` already prevents that — when\n\t\t// `effResponse`'s nested batch fires `status=\"done\"` mid-iteration,\n\t\t// terminal's status dep settles but its `llmResponse` (or mirror)\n\t\t// dep is still DIRTY from Phase 1, so the fn does not run until\n\t\t// Phase 2 visits both deps. Verified by fast-check invariant `#12b\n\t\t// nested-drain-peer-consistency-compound` and by the multi-turn\n\t\t// `executes tool calls and loops` test passing under either dep\n\t\t// shape (`[statusNode, llmResponse]` or `[statusNode, lastResponseState]`).\n\t\t//\n\t\t// Verified by: QA C3 regression tests (`run() with pre-aborted\n\t\t// signal rejects AbortError` and `second run() with pre-aborted\n\t\t// signal rejects AbortError (no stale response leak)`) — both\n\t\t// fail when `_terminalResult` is rewired to depend on `llmResponse`\n\t\t// directly. See COMPOSITION-GUIDE §32 (cross-wave reset reframe).\n\t\tconst lastResponseState = node<LLMResponse>([], {\n\t\t\tname: \"lastResponse\",\n\t\t\tdescribeKind: \"state\",\n\t\t\tmeta: aiMeta(\"agent_last_response\"),\n\t\t});\n\t\tthis.lastResponse = lastResponseState;\n\n\t\t// toolCalls: raw node that emits DATA only when status === \"acting\" and\n\t\t// the current response has tool calls. Otherwise emits RESOLVED. Using\n\t\t// DATA([]) for the idle case would cause switchMap(toolCalls) to\n\t\t// re-dispatch its inner (creating a fresh node([], { initial: [] }) source whose\n\t\t// emissions re-trigger effects downstream). RESOLVED keeps the inner\n\t\t// alive and lets upstream waves pass through without re-dispatch.\n\t\t// Inner raw tool-call stream — name `toolCallsRaw` so the post-intercept\n\t\t// public surface (`this.toolCalls`) is unambiguous in `describe()`.\n\t\t// QA-fix: previously the inner was named `\"toolCalls\"`, which collided\n\t\t// with `this.toolCalls` if the user-supplied interceptor returned a\n\t\t// wrapper that internally retained a reference to this raw node —\n\t\t// `describe()` would render two distinct nodes both labeled `\"toolCalls\"`.\n\t\tconst toolCallsRaw = nodeFactory<readonly ToolCall[]>(\n\t\t\t[lastResponseState, statusNode],\n\t\t\t(data, actions, ctx) => {\n\t\t\t\t// SENTINEL guard: `lastResponseState` stays `undefined` (cache\n\t\t\t\t// + prevData) until the first real response. `readLatest`'s\n\t\t\t\t// fallback returns `undefined` here so the no-DATA branch is\n\t\t\t\t// indistinguishable from the protocol SENTINEL — both gate to\n\t\t\t\t// RESOLVED. Per `feedback_use_prevdata_for_sentinel`.\n\t\t\t\tconst resp = readLatest<LLMResponse | undefined>(data, ctx.prevData, 0, undefined);\n\t\t\t\tconst stat = readLatest<AgentLoopStatus>(data, ctx.prevData, 1, \"idle\");\n\t\t\t\tif (stat !== \"acting\") {\n\t\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst calls = resp?.toolCalls;\n\t\t\t\tif (calls == null || calls.length === 0) {\n\t\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tactions.emit(calls);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"toolCallsRaw\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: aiMeta(\"agent_tool_calls_raw\"),\n\t\t\t},\n\t\t);\n\t\t// Reactive splice (D9 / COMPOSITION-GUIDE §31). When `interceptToolCalls`\n\t\t// is set, the raw tool-call stream is transformed in the graph — the\n\t\t// executor sees the gated stream, and `agent.toolCalls` surfaces the\n\t\t// post-intercept view so audit / telemetry match reality.\n\t\tconst gatedToolCallsNode = opts.interceptToolCalls\n\t\t\t? opts.interceptToolCalls(toolCallsRaw)\n\t\t\t: toolCallsRaw;\n\t\tthis.toolCalls = gatedToolCallsNode;\n\n\t\t// Delegate per-call fan-out + retry + rescue to the `toolExecution`\n\t\t// primitive. `toolCallsRaw` already gates empty batches to RESOLVED,\n\t\t// so `toolExecution`'s \"non-empty batch only\" contract is satisfied\n\t\t// upstream. `retryCount: 1` matches the pre-extraction behaviour\n\t\t// (one retry after first failure = 2 attempts total).\n\t\tconst toolResultsNode: Node<readonly ToolResult[]> = toolExecution({\n\t\t\ttoolCalls: gatedToolCallsNode,\n\t\t\ttools,\n\t\t\tretryCount: 1,\n\t\t});\n\t\tthis.toolResults = toolResultsNode;\n\n\t\t// --- State-machine effects ---\n\t\t// Effect 1: LLM response landed → write lastResponse mirror + chat,\n\t\t// transition status, increment turn. Emission ORDER inside the batch\n\t\t// matters (drain is FIFO under any outer-batch depth):\n\t\t// 1. `lastResponseState.emit(response)` FIRST — so when the drain\n\t\t// fires the status=done wave later in the queue, `_terminalResult`'s\n\t\t// dep on `lastResponseState` has already been updated.\n\t\t// 2. `statusNode.emit(nextStatus)` — drives state machine.\n\t\t// 3. `turnNode.emit(next)` — counter.\n\t\t// 4. `chat.append(...)` LAST — chat.messageCount wave now sees the\n\t\t// new status (so `promptInput` gates correctly).\n\t\t// Without (1) first, `_terminalResult` reads stale `prevData` for\n\t\t// lastResponse when status transitions synchronously during drain.\n\t\t//\n\t\t// **Invariant independence from outer batch depth.** `downWithBatch`\n\t\t// preserves FIFO drain order regardless of nesting — whether the\n\t\t// outer batch is at depth 0 (common: Promise microtask) or depth >0\n\t\t// (user-composed `batch()` scope around `agent.run()`), the emissions\n\t\t// above drain in the order they were enqueued. The state-mirror\n\t\t// pattern holds in both cases.\n\t\t//\n\t\t// **Abort guard (C2 defense-in-depth).** If the `aborted` state has\n\t\t// flipped true between `adapter.invoke`'s Promise resolution and this\n\t\t// effect firing (micro-race), bail out so we don't append to chat or\n\t\t// execute tool calls for an abandoned run. The controller.abort() in\n\t\t// effAbort also fires the signal, which causes `fromAny` to emit\n\t\t// ERROR — but that ERROR propagation arrives in a separate wave, so\n\t\t// this guard covers the \"Promise already resolved before abort hit\n\t\t// the controller\" case.\n\t\tconst effResponse = node(\n\t\t\t[llmResponse],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tif (latestAborted) return;\n\t\t\t\tconst response = data[0] as LLMResponse;\n\t\t\t\tconst next = latestTurn + 1;\n\t\t\t\tconst hasToolCalls = response.toolCalls != null && response.toolCalls.length > 0;\n\t\t\t\tconst naturalStop =\n\t\t\t\t\tresponse.finishReason === \"end_turn\" &&\n\t\t\t\t\t(!response.toolCalls || response.toolCalls.length === 0);\n\t\t\t\tconst customStop = stopWhen?.(response) === true;\n\t\t\t\tconst capReached = next >= maxTurns;\n\t\t\t\tconst nextStatus: AgentLoopStatus =\n\t\t\t\t\tcustomStop || naturalStop || !hasToolCalls || capReached ? \"done\" : \"acting\";\n\t\t\t\tbatch(() => {\n\t\t\t\t\tlastResponseState.emit(response);\n\t\t\t\t\tstatusNode.emit(nextStatus);\n\t\t\t\t\tturnNode.emit(next);\n\t\t\t\t\tchat.append(\"assistant\", response.content, {\n\t\t\t\t\t\ttoolCalls: response.toolCalls,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\t{ describeKind: \"effect\" },\n\t\t);\n\n\t\t// Effect 2: Tool results landed → append to chat, transition to\n\t\t// thinking (or done if turn cap reached). Same ordering discipline —\n\t\t// status emits before chat mutations. Abort guard mirrors effResponse.\n\t\tconst effResults = node(\n\t\t\t[toolResultsNode],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tif (latestAborted) return;\n\t\t\t\tconst arr = data[0] as readonly ToolResult[];\n\t\t\t\tif (arr.length === 0) return;\n\t\t\t\tconst nextStatus: AgentLoopStatus = latestTurn >= maxTurns ? \"done\" : \"thinking\";\n\t\t\t\tbatch(() => {\n\t\t\t\t\tstatusNode.emit(nextStatus);\n\t\t\t\t\tfor (const r of arr) chat.appendToolResult(r.id, r.content);\n\t\t\t\t});\n\t\t\t},\n\t\t\t{ describeKind: \"effect\" },\n\t\t);\n\n\t\t// Effect 3: external abort → cancel in-flight wire call + terminal status.\n\t\t// Aborting the controller causes the switchMap inner's `fromAny` to\n\t\t// emit ERROR (signal-bound), which tears down the subscription. The\n\t\t// `status=\"done\"` emit drives `_terminalResult` to resolve `run()`'s\n\t\t// Promise (via AbortError when `resp == null`, see C3).\n\t\t//\n\t\t// Unit 4 Q5: status guard — if status is already \"done\" (the natural-\n\t\t// completion path raced the abort), skip the redundant emit so the\n\t\t// status-node event log isn't polluted with a trailing duplicate.\n\t\t// Closure-mirror `latestStatus` keeps the comparison synchronous and\n\t\t// P3-compliant (closure read, not `.cache` read — see §28). Seeded\n\t\t// from `statusNode.cache` to match the §28 factory-time-seed pattern\n\t\t// that `latestTurn` / `latestAborted` use — the literal `\"idle\"` would\n\t\t// silently drift if the constructor initial value ever changed.\n\t\tlet latestStatus: AgentLoopStatus = (statusNode.cache as AgentLoopStatus | undefined) ?? \"idle\";\n\t\tconst statusSub = statusNode.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) if (m[0] === DATA) latestStatus = m[1] as AgentLoopStatus;\n\t\t});\n\t\tconst effAbort = node(\n\t\t\t[abortedNode],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tif (data[0] === true) {\n\t\t\t\t\tthis._currentAbortController?.abort(new Error(\"agentLoop: aborted\"));\n\t\t\t\t\tif (latestStatus !== \"done\") statusNode.emit(\"done\");\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ describeKind: \"effect\" },\n\t\t);\n\n\t\t// Keepalive so the pipeline stays activated even without external\n\t\t// subscribers. Callers don't need to subscribe to `llmResponse` /\n\t\t// `toolResults` for the loop to run.\n\t\tconst kaResponse = keepalive(effResponse);\n\t\tconst kaResults = keepalive(effResults);\n\t\tconst kaAbort = keepalive(effAbort);\n\n\t\t// terminalResult emits the final `LLMResponse` on each \"done\"\n\t\t// transition. The old compound `{response, runVersion}` shape existed\n\t\t// to let a re-entrant caller's `awaitSettled` predicate filter out\n\t\t// the PREVIOUS run's cached DATA; that job now belongs to\n\t\t// `awaitSettled({skipCurrent: true})` (extra/sources.ts) which\n\t\t// ignores the initial push-on-subscribe DATA and resolves only on\n\t\t// fresh post-subscribe emissions. Retiring the stamp removes a\n\t\t// closure-held counter and a per-emission object allocation from\n\t\t// the hot path.\n\t\t//\n\t\t// C3 (abort-before-response) post-SENTINEL: when `stat === \"done\"` but\n\t\t// `lastResponseState` is SENTINEL (no DATA ever delivered for this\n\t\t// run — `prevData[1] === undefined`, equivalently `resp === undefined`\n\t\t// after `readLatest`'s fallback), emit `ERROR(AbortError)` so the\n\t\t// awaiting Promise rejects instead of hanging on a RESOLVED. The\n\t\t// SENTINEL state is restored at every `run()` boundary by\n\t\t// `lastResponse.down([[INVALIDATE]])` (clears `_cached` AND clears\n\t\t// each dependent's `prevData` slot for this dep), so a second run\n\t\t// after a successful first run still detects the abort cleanly —\n\t\t// no stale `prevData` leak.\n\t\tthis._terminalResult = nodeFactory<LLMResponse>(\n\t\t\t[statusNode, lastResponseState],\n\t\t\t(data, actions, ctx) => {\n\t\t\t\tconst stat = readLatest<AgentLoopStatus>(data, ctx.prevData, 0, \"idle\");\n\t\t\t\tconst resp = readLatest<LLMResponse | undefined>(data, ctx.prevData, 1, undefined);\n\t\t\t\tif (stat === \"done\") {\n\t\t\t\t\tif (resp !== undefined) {\n\t\t\t\t\t\tactions.emit(resp);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst err = new Error(\"agentLoop: aborted\") as Error & { name: string };\n\t\t\t\t\terr.name = \"AbortError\";\n\t\t\t\t\tactions.down([[ERROR, err]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (stat === \"error\") {\n\t\t\t\t\tactions.down([[ERROR, new Error(\"agentLoop: errored\")]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"terminalResult\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: aiMeta(\"agent_terminal_result\"),\n\t\t\t\t// `lastResponseState` is SENTINEL until the first real response\n\t\t\t\t// arrives — without `partial: true`, the spec §2.7 first-run\n\t\t\t\t// gate would block this fn from ever firing on the abort-\n\t\t\t\t// before-response path (`status → \"done\"` while `lastResponse`\n\t\t\t\t// has never delivered DATA). The fn explicitly handles the\n\t\t\t\t// SENTINEL case (`resp === undefined → ERROR(AbortError)`),\n\t\t\t\t// so partial-fire is safe by design.\n\t\t\t\tpartial: true,\n\t\t\t},\n\t\t);\n\t\t// Wave B-CC Q2/C: register intermediate pipeline nodes so consumers\n\t\t// can `observe(path)` them by name (e.g. `agent.observe(\"promptInput\")`).\n\t\t// They were already visible in `describe()` via dep traversal, but not\n\t\t// path-addressable. Tools using the `observe`-by-path API now work.\n\t\t//\n\t\t// QA-fix (#5 stability): registrations live AFTER ALL dependent nodes\n\t\t// are constructed (`promptInput → llmResponse → effResponse →\n\t\t// lastResponseState → toolCallsRaw → toolResultsNode → effResults →\n\t\t// effAbort → _terminalResult`). Topology event-stream consumers\n\t\t// subscribed at construction time now see registrations in an order\n\t\t// where every edge between two registered nodes is already valid —\n\t\t// no transient partial graph slipping through to live mermaid / d2\n\t\t// renderers.\n\t\tthis.add(promptInput as Node<unknown>, { name: \"promptInput\" });\n\t\tthis.add(llmResponse as Node<unknown>, { name: \"llmResponse\" });\n\t\tthis.add(this.lastResponse as Node<unknown>, { name: \"lastResponse\" });\n\t\t// When no interceptor is configured, `this.toolCalls === toolCallsRaw` —\n\t\t// registering the same instance under two names trips the per-graph\n\t\t// `_nodeToName` collision check. Register the raw under `toolCalls`\n\t\t// directly in that case; otherwise register both (raw + post-intercept).\n\t\tif (this.toolCalls === toolCallsRaw) {\n\t\t\tthis.add(this.toolCalls as Node<unknown>, { name: \"toolCalls\" });\n\t\t} else {\n\t\t\tthis.add(toolCallsRaw as Node<unknown>, { name: \"toolCallsRaw\" });\n\t\t\tthis.add(this.toolCalls as Node<unknown>, { name: \"toolCalls\" });\n\t\t}\n\t\tthis.add(toolResultsNode as Node<unknown>, { name: \"toolResults\" });\n\t\tthis.add(this._terminalResult as Node<unknown>, { name: \"terminalResult\" });\n\n\t\t// Register subscriptions via `addDisposer` so they tear down on\n\t\t// subgraph unmount (not just explicit `destroy()`). A caller that\n\t\t// unmounts the AgentLoopGraph from its parent via `graph.remove(...)`\n\t\t// would otherwise keep `turnSub` / `abortedSub` live against dead state.\n\t\tthis.addDisposer(turnSub);\n\t\tthis.addDisposer(abortedSub);\n\t\tthis.addDisposer(statusSub);\n\t\tthis.addDisposer(kaResponse);\n\t\tthis.addDisposer(kaResults);\n\t\tthis.addDisposer(kaAbort);\n\t\tthis._disposeRunWiring = (): void => {\n\t\t\t// addDisposer takes care of teardown; this shim stays for the\n\t\t\t// `destroy()` override's idempotency contract (safe no-op if the\n\t\t\t// disposers already fired).\n\t\t};\n\t}\n\n\t/**\n\t * Bridge to `Promise<LLMResponse>` over the reactive pipeline.\n\t *\n\t * - If `userMessage` is provided, appends it as a user message and\n\t * transitions status to `\"thinking\"` to kick the loop.\n\t * - If `signal` is provided, binds it to the reactive `aborted` node\n\t * AND threads into `adapter.invoke({ signal })` so the wire call can\n\t * cancel mid-flight. The reactive `aborted` state + effect 3 guarantee\n\t * that even an adapter that ignores `signal` will stop emitting into\n\t * the agent graph.\n\t * - Resolves when `status === \"done\"` with the final LLM response.\n\t * Rejects with `AbortError` when the abort signal fires pre-response.\n\t * Rejects with the stage error when `status === \"error\"`.\n\t *\n\t * **Concurrency:** `run()` refuses to overlap with a pending call on the\n\t * same agent. Attempting to call `run()` while a previous `run()` is\n\t * still in-flight throws a `RangeError` immediately. Stale-resolution\n\t * safety is provided by `awaitSettled({skipCurrent: true})`, which\n\t * ignores the cached initial DATA from any previous run and resolves\n\t * only on a fresh post-subscribe emission of `_terminalResult`.\n\t */\n\tasync run(userMessage?: string, signal?: AbortSignal): Promise<LLMResponse | null> {\n\t\tif (this._running) {\n\t\t\tthrow new RangeError(\n\t\t\t\t`agentLoop \"${this.name}\": run() called while a previous run() is still pending — await the previous run before starting another, or call abort() first`,\n\t\t\t);\n\t\t}\n\t\tthis._running = true;\n\n\t\tlet offAbort: (() => void) | undefined;\n\t\ttry {\n\t\t\t// Reset per-run state. `lastResponse` MUST be cleared here —\n\t\t\t// without it, `_terminalResult` would read the prior run's\n\t\t\t// cached response during a second `run()` with a pre-aborted\n\t\t\t// signal: `effAbort` drives `status → \"done\"`, `_terminalResult`\n\t\t\t// evaluates `stat=\"done\"` + `resp=<prior respA>` and emits DATA\n\t\t\t// as a fresh post-subscribe signal → `awaitSettled` resolves\n\t\t\t// with the stale response instead of rejecting with AbortError.\n\t\t\t// The C3 `stat=done && resp===undefined → ERROR` guard in\n\t\t\t// `_terminalResult` is only correct once the reset clears the\n\t\t\t// cache.\n\t\t\t//\n\t\t\t// **SENTINEL reset via plain `[[INVALIDATE]]`** (DS-13.5.A,\n\t\t\t// 2026-05-01). Pre-DS-13.5.A this required a paired\n\t\t\t// `[[INVALIDATE], [RESOLVED]]` because INVALIDATE alone left\n\t\t\t// dependents wedged in DIRTY. With INVALIDATE settling the wave\n\t\t\t// (decrementing `_dirtyDepCount` like RESOLVED) and clearing\n\t\t\t// `_cached` + each dependent's `prevData[lastResponse]` slot\n\t\t\t// back to `undefined`, a single emission restores SENTINEL state\n\t\t\t// AND lets dependents fire on the next status transition.\n\t\t\t// `lastResponse` is a `Node<LLMResponse>` with no `initial` —\n\t\t\t// it stays SENTINEL until the first real response; resetting\n\t\t\t// via `emit(null)` would push a `null` DATA placeholder (the\n\t\t\t// F9 trap), which is why INVALIDATE rather than emit is used.\n\t\t\tbatch(() => {\n\t\t\t\tthis.lastResponse.down([[INVALIDATE]]);\n\t\t\t\tthis.turn.emit(0);\n\t\t\t\tthis.aborted.emit(false);\n\t\t\t\tthis.status.emit(\"idle\");\n\t\t\t});\n\t\t\tif (userMessage != null) this.chat.append(\"user\", userMessage);\n\n\t\t\t// Subscribe to `_terminalResult` BEFORE transitioning to\n\t\t\t// \"thinking\" — otherwise a synchronous adapter (mock tests,\n\t\t\t// offline stubs) would drain status → done → DATA on\n\t\t\t// `_terminalResult` before `awaitSettled` had a chance to\n\t\t\t// subscribe, and `skipCurrent: true` would swallow the only\n\t\t\t// DATA this run will produce. `awaitSettled` / `firstWhere`\n\t\t\t// subscribes synchronously during the `async` function's\n\t\t\t// initial execution slice, so calling it before the kick\n\t\t\t// guarantees the subscription is in place when the pipeline\n\t\t\t// starts draining.\n\t\t\t//\n\t\t\t// `skipCurrent: true` still matters: on the second `run()`\n\t\t\t// call `_terminalResult` holds cached DATA from the prior run,\n\t\t\t// and push-on-subscribe would resolve immediately with that\n\t\t\t// stale value without the skip.\n\t\t\tconst resultPromise = awaitSettled(this._terminalResult, { skipCurrent: true });\n\n\t\t\tif (signal != null) {\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tthis.aborted.emit(true);\n\t\t\t\t} else {\n\t\t\t\t\tconst listener = (): void => this.aborted.emit(true);\n\t\t\t\t\tsignal.addEventListener(\"abort\", listener, { once: true });\n\t\t\t\t\toffAbort = (): void => signal.removeEventListener(\"abort\", listener);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Kick — transition to \"thinking\" fires promptInput → llmResponse.\n\t\t\t// Skip the kick when the signal was already aborted: `effAbort`\n\t\t\t// has driven `status → \"done\"` above, and a trailing\n\t\t\t// `thinking` emit would produce a non-monotonic `idle → done →\n\t\t\t// thinking` sequence in the status-event log for no reactive\n\t\t\t// benefit (promptInput gates on `!latestAborted` anyway).\n\t\t\tif (signal?.aborted !== true) {\n\t\t\t\tthis.status.emit(\"thinking\");\n\t\t\t}\n\n\t\t\treturn await resultPromise;\n\t\t} finally {\n\t\t\toffAbort?.();\n\t\t\tthis._running = false;\n\t\t\tthis._currentAbortController = null;\n\t\t}\n\t}\n\n\t/**\n\t * Flip the reactive `aborted` state. Equivalent to setting an external\n\t * `AbortSignal` — the pipeline observes and transitions to `\"done\"`.\n\t */\n\tabort(): void {\n\t\tthis.aborted.emit(true);\n\t}\n\n\toverride destroy(): void {\n\t\ttry {\n\t\t\tthis._disposeRunWiring();\n\t\t} catch {\n\t\t\t/* best-effort: disposing keepalives shouldn't block destroy */\n\t\t}\n\t\tsuper.destroy();\n\t}\n}\n\n/**\n * Read the latest value for dep `i` inside a raw-`node()` fn body.\n *\n * Checks `batchData[i]` first (this-wave DATA from the dep), falls back to\n * `ctx.prevData[i]` (last DATA from prior waves), and finally to `fallback`\n * when the dep has never emitted (SENTINEL). Matches the unwrap semantics\n * `derived`'s sugar applies, so raw nodes can read deps uniformly.\n *\n * @internal\n */\nfunction readLatest<T>(\n\tbatchData: readonly (readonly unknown[] | undefined)[],\n\tprevData: readonly unknown[],\n\tindex: number,\n\tfallback: T,\n): T {\n\tconst batch = batchData[index];\n\tif (batch != null && batch.length > 0) return batch[batch.length - 1] as T;\n\tconst prev = prevData[index];\n\treturn (prev !== undefined ? prev : fallback) as T;\n}\n\n/** @internal Shape of the LLM invocation input — constructed inside `promptInput`. */\ninterface InvokeInput {\n\treadonly messages: readonly ChatMessage[];\n\treadonly tools: readonly ToolDefinition[];\n}\n\nexport function agentLoop(name: string, opts: AgentLoopOptions): AgentLoopGraph {\n\tconst g = new AgentLoopGraph(name, opts);\n\t// Tier 1.5.3 Phase 2.5 (DG1=B): tag the Graph with its constructing\n\t// factory so `describe()` exposes provenance. Opts include non-JSON\n\t// fields (`adapter`, `tools`, `stopWhen`, `onToolCall`,\n\t// `interceptToolCalls`, etc.) so route through `placeholderArgs`\n\t// (DG2=ii).\n\tg.tagFactory(\"agentLoop\", placeholderArgs(opts as unknown as Record<string, unknown>));\n\treturn g;\n}\n","/**\n * Phase 13.G — `AgentBundle<TIn, TOut>` interface + `class AgentGraph extends Graph`.\n *\n * Source: `archive/docs/SESSION-multi-agent-gap-analysis.md` G1 lock B.\n *\n * Composes the existing substrate (`agentLoop`, `toolRegistry`,\n * `agentMemory`) into a typed inbox/outbox subgraph that other parts of a\n * multi-agent system can wire to. Sibling preset `agent()` (in\n * `./agents.ts`) is the ergonomic factory; this file is the contract.\n *\n * **Cross-cut #1 lock (no `agent.run()`):** caller-side runtime entry is\n * `bundle.in.emit(input)` + `awaitSettled(bundle.out)`. The legacy\n * `agentLoop.run()` is still available on `bundle.graph.loop` for\n * single-shot Promise-bridge use cases, but `agent()` does NOT expose a\n * `run()` method on the bundle.\n *\n * **Memory partition default:** private memory per agent (each `agent(...)`\n * call creates its own `AgentMemoryGraph` if none passed). Pass an explicit\n * shared instance for §29 handoff context-transfer.\n */\n\nimport { batch, DATA, INVALIDATE, type Node, node, RESOLVED } from \"@graphrefly/pure-ts/core\";\nimport { keepalive } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\nimport { aiMeta } from \"../../utils/ai/_internal.js\";\nimport type {\n\tInputTokens,\n\tLLMAdapter,\n\tLLMResponse,\n\tOutputTokens,\n\tTokenUsage,\n\tToolDefinition,\n} from \"../../utils/ai/adapters/core/types.js\";\nimport {\n\ttype SubscriptionGraph,\n\tsubscription,\n\ttype TopicGraph,\n\ttopic,\n} from \"../../utils/messaging/index.js\";\nimport { type AgentLoopGraph, agentLoop } from \"./agent-loop.js\";\nimport type { AgentMemoryGraph } from \"./agent-memory.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Lifecycle status of an {@link AgentGraph}.\n *\n * - `idle` — no input has been received since construction or last reset.\n * - `running` — inputs are flowing through the underlying agentLoop\n * (collapses the loop's `thinking` + `acting` substates so consumers\n * don't have to model the tool-call inner loop).\n * - `verifying` — verifier subgraph is in flight (reserved; lights up when\n * the verifier slot is added in a future wave per G7 recipe).\n * - `done` — the most recent input has settled with a verified response.\n * - `error` — the loop or verifier produced a terminal error.\n *\n * **Note (Phase 13.G, 2026-05-01):** v1 of `agent()` has no built-in\n * verifier slot — `verifying` is reserved but never produced. When a\n * verifier consumer surfaces, this enum widens (non-breaking type\n * widening; existing consumers see the same `idle | running | done | error`\n * subset).\n */\nexport type AgentStatus = \"idle\" | \"running\" | \"verifying\" | \"done\" | \"error\";\n\n/**\n * Aggregated cost for an agent's run, surfaced as a `Node<CostState>` on\n * the bundle. **Wraps the canonical {@link TokenUsage}** so consumers get\n * the full provider-disaggregated token classes (cache-read /\n * cache-write-5m / cache-write-1h / audio / image / video / tool-use /\n * reasoning / prediction-accepted / prediction-rejected / extensions /\n * auxiliary non-token costs / raw escape-hatch) without losing fidelity\n * for downstream pricing. USD conversion is a downstream `derived` over\n * `usage`.\n *\n * - `usage` — accumulated {@link TokenUsage} across all turns of the\n * current input.\n * - `turns` — number of completed agentLoop iterations (LLM invocations).\n *\n * **Counter scope:** resets to {@link ZERO_COST} on each new `bundle.in`\n * emit (per-input cost rather than per-agent-lifetime). Sum across multiple\n * inputs by snapshotting `cost` at `done` and accumulating externally —\n * a per-lifetime cost is a downstream `scan` over this.\n *\n * **Helpers.** Use `sumInputTokens(usage)` / `sumOutputTokens(usage)` from\n * `@graphrefly/graphrefly-ts` to flatten to scalars when the caller wants\n * a single number.\n */\nexport interface CostState {\n\treadonly usage: TokenUsage;\n\treadonly turns: number;\n}\n\nconst EMPTY_INPUT: InputTokens = Object.freeze({ regular: 0 });\nconst EMPTY_OUTPUT: OutputTokens = Object.freeze({ regular: 0 });\nconst EMPTY_USAGE: TokenUsage = Object.freeze({ input: EMPTY_INPUT, output: EMPTY_OUTPUT });\n\n/** Empty cost. Used as the initial value and the per-input reset baseline. */\nexport const ZERO_COST: CostState = Object.freeze({ usage: EMPTY_USAGE, turns: 0 });\n\n// ---------------------------------------------------------------------------\n// TokenUsage accumulator\n// ---------------------------------------------------------------------------\n\nfunction addOptional(a: number | undefined, b: number | undefined): number | undefined {\n\tif (a == null && b == null) return undefined;\n\treturn (a ?? 0) + (b ?? 0);\n}\n\nfunction addExtensions(\n\ta: Record<string, number> | undefined,\n\tb: Record<string, number> | undefined,\n): Record<string, number> | undefined {\n\tif (a == null && b == null) return undefined;\n\tconst out: Record<string, number> = { ...(a ?? {}) };\n\tfor (const [k, v] of Object.entries(b ?? {})) {\n\t\tout[k] = (out[k] ?? 0) + v;\n\t}\n\treturn out;\n}\n\n/**\n * Accumulates two {@link TokenUsage} snapshots. All field classes are\n * summed; optional fields propagate as `undefined` when absent from both\n * sides, otherwise treated as 0 for the missing side. `auxiliary` and\n * `extensions` merge by key. `raw` is dropped — it's a per-call escape\n * hatch, not summable.\n *\n * @category extra\n */\nexport function addUsage(a: TokenUsage, b: TokenUsage): TokenUsage {\n\tconst out: TokenUsage = {\n\t\tinput: {\n\t\t\tregular: a.input.regular + b.input.regular,\n\t\t\t...(addOptional(a.input.cacheRead, b.input.cacheRead) !== undefined && {\n\t\t\t\tcacheRead: addOptional(a.input.cacheRead, b.input.cacheRead) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.input.cacheWrite5m, b.input.cacheWrite5m) !== undefined && {\n\t\t\t\tcacheWrite5m: addOptional(a.input.cacheWrite5m, b.input.cacheWrite5m) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.input.cacheWrite1h, b.input.cacheWrite1h) !== undefined && {\n\t\t\t\tcacheWrite1h: addOptional(a.input.cacheWrite1h, b.input.cacheWrite1h) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.input.cacheWriteOther, b.input.cacheWriteOther) !== undefined && {\n\t\t\t\tcacheWriteOther: addOptional(a.input.cacheWriteOther, b.input.cacheWriteOther) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.input.audio, b.input.audio) !== undefined && {\n\t\t\t\taudio: addOptional(a.input.audio, b.input.audio) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.input.image, b.input.image) !== undefined && {\n\t\t\t\timage: addOptional(a.input.image, b.input.image) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.input.video, b.input.video) !== undefined && {\n\t\t\t\tvideo: addOptional(a.input.video, b.input.video) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.input.toolUse, b.input.toolUse) !== undefined && {\n\t\t\t\ttoolUse: addOptional(a.input.toolUse, b.input.toolUse) as number,\n\t\t\t}),\n\t\t\t...(addExtensions(a.input.extensions, b.input.extensions) !== undefined && {\n\t\t\t\textensions: addExtensions(a.input.extensions, b.input.extensions) as Record<string, number>,\n\t\t\t}),\n\t\t},\n\t\toutput: {\n\t\t\tregular: a.output.regular + b.output.regular,\n\t\t\t...(addOptional(a.output.reasoning, b.output.reasoning) !== undefined && {\n\t\t\t\treasoning: addOptional(a.output.reasoning, b.output.reasoning) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.output.audio, b.output.audio) !== undefined && {\n\t\t\t\taudio: addOptional(a.output.audio, b.output.audio) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.output.predictionAccepted, b.output.predictionAccepted) !== undefined && {\n\t\t\t\tpredictionAccepted: addOptional(\n\t\t\t\t\ta.output.predictionAccepted,\n\t\t\t\t\tb.output.predictionAccepted,\n\t\t\t\t) as number,\n\t\t\t}),\n\t\t\t...(addOptional(a.output.predictionRejected, b.output.predictionRejected) !== undefined && {\n\t\t\t\tpredictionRejected: addOptional(\n\t\t\t\t\ta.output.predictionRejected,\n\t\t\t\t\tb.output.predictionRejected,\n\t\t\t\t) as number,\n\t\t\t}),\n\t\t\t...(addExtensions(a.output.extensions, b.output.extensions) !== undefined && {\n\t\t\t\textensions: addExtensions(a.output.extensions, b.output.extensions) as Record<\n\t\t\t\t\tstring,\n\t\t\t\t\tnumber\n\t\t\t\t>,\n\t\t\t}),\n\t\t},\n\t\t...(addExtensions(a.auxiliary, b.auxiliary) !== undefined && {\n\t\t\tauxiliary: addExtensions(a.auxiliary, b.auxiliary) as Record<string, number>,\n\t\t}),\n\t};\n\treturn out;\n}\n\n/**\n * Spec for {@link agent} (in `./agents.ts`). Required fields are minimal —\n * `name` and `adapter` cover the common case where the input is a string\n * and the output is the raw `LLMResponse`. Optional fields shape the\n * agent's behavior:\n *\n * - **Mappers** (`inMapper` / `outMapper`) translate between caller-typed\n * `TIn` / `TOut` and the loop's internal `string` / `LLMResponse`. Default\n * identity mappers are wired automatically when `TIn` extends `string`\n * and `TOut` extends `LLMResponse`.\n * - **`tools`** is a reactive `NodeInput<readonly ToolDefinition[]>` —\n * `agent()` subscribes and reconciles the underlying `toolRegistry`'s\n * registrations on each emit. Static-array form is also accepted.\n * - **`memory`** is an explicit `AgentMemoryGraph` instance for shared\n * memory across agents (§29 handoff context transfer). Default: private\n * memory per agent (each `agent()` call mints its own).\n * - **`maxIterations`** caps the underlying agentLoop's tool-call inner\n * loop. Default 10 (matches `agentLoop`).\n * - **Verifier slot** is intentionally not in v1 — G7 reframe locks it as\n * a caller-composed recipe. When a real consumer surfaces, a\n * `verifier?: (out: Node<TOut>) => NodeInput<VerifierResult>` field\n * lands here additively.\n */\nexport interface AgentSpec<TIn, TOut> {\n\t/** Local mount name when wired to a parent graph. Required. */\n\treadonly name: string;\n\t/** LLM adapter for the underlying agentLoop. Required. */\n\treadonly adapter: LLMAdapter;\n\t/** Optional system prompt. Static today; reactive widening pending. */\n\treadonly systemPrompt?: string;\n\t/**\n\t * Optional reactive tool list. When a Node, the agent subscribes and\n\t * reconciles the underlying `toolRegistry` registrations on each emit\n\t * (additions registered, removals unregistered). When a static array,\n\t * tools are registered once at construction.\n\t */\n\treadonly tools?: Node<readonly ToolDefinition[]> | readonly ToolDefinition[];\n\t/**\n\t * Optional shared memory. Default: private (agent mints its own\n\t * `AgentMemoryGraph` if needed; not yet wired into the loop's chat —\n\t * that wiring is a separate follow-up). Pass an explicit instance to\n\t * share memory across agents for §29 handoff context transfer.\n\t */\n\treadonly memory?: AgentMemoryGraph<unknown>;\n\t/**\n\t * Maps caller-typed input → string for the underlying chat. Defaults to\n\t * identity when `TIn extends string`; required otherwise.\n\t */\n\treadonly inMapper?: (input: TIn) => string;\n\t/**\n\t * Maps the agentLoop's `LLMResponse` → caller-typed output. Defaults to\n\t * identity when `TOut extends LLMResponse`; required otherwise.\n\t */\n\treadonly outMapper?: (response: LLMResponse) => TOut;\n\t/** Caps tool-call inner-loop iterations. Default 10. */\n\treadonly maxIterations?: number;\n\t/** Escape hatch for non-core fields. Surfaced in `describe()` via meta. */\n\treadonly meta?: Record<string, unknown>;\n}\n\n/**\n * Public contract for an agent — typed inbox/outbox + lifecycle / cost\n * observables + the underlying graph for inspection / mounting.\n *\n * **Reactive entry:** caller writes to `in` (e.g. `bundle.in.emit(input)`).\n * The agent reactively kicks the underlying loop and produces `out`.\n *\n * **Reactive exit:** caller reads `out` via `subscribe` (continuous) or\n * `awaitSettled(out)` (single-shot). Both `in` and `out` stay SENTINEL\n * (`cache === undefined`) until the first real emission — no `null`\n * push-on-subscribe trap (per `feedback_use_prevdata_for_sentinel`).\n *\n * **Cross-graph wiring:** the bundle's `graph` is mountable under any\n * parent via `parent.mount(name, bundle.graph)`. After mount, the bundle's\n * Nodes are reachable through both the bundle reference (direct) and via\n * `parent.node(\"<name>::out\")` etc. (qualified path).\n */\nexport interface AgentBundle<TIn, TOut> {\n\treadonly in: Node<TIn>;\n\treadonly out: Node<TOut>;\n\treadonly status: Node<AgentStatus>;\n\treadonly cost: Node<CostState>;\n\treadonly graph: AgentGraph<TIn, TOut>;\n}\n\n// ---------------------------------------------------------------------------\n// AgentGraph\n// ---------------------------------------------------------------------------\n\nconst TERMINAL_STATUSES = new Set<AgentStatus>([\"done\", \"error\"]);\n\n/**\n * Graph subclass implementing {@link AgentBundle}. Mounts an inner\n * {@link AgentLoopGraph} at `loop/`; `in` / `out` / `status` / `cost`\n * surface the bundle contract as top-level nodes.\n *\n * Construction is internal — use the {@link agent} factory in\n * `./agents.ts` for normal use. Direct `new AgentGraph(name, spec)` is\n * supported for callers that want full control over mount order.\n *\n * **Topology:**\n * ```\n * <name>\n * ├── loop (AgentLoopGraph subgraph)\n * │ ├── chat\n * │ ├── tools\n * │ ├── status / turn / aborted / lastResponse / ...\n * ├── in (Node<TIn>, SENTINEL until first emit)\n * ├── out (Node<TOut>, SENTINEL until first response)\n * ├── status (Node<AgentStatus>, mirror of loop.status)\n * └── cost (Node<CostState>)\n * ```\n *\n * **Lifecycle:**\n * - On `in` emit: `inMapper` projects to `string`; appended to\n * `loop.chat`; loop status reset (`turn=0`, `aborted=false`,\n * `status=\"thinking\"`); per-input cost counters reset to zero.\n * - On `loop.lastResponse` emit: cost rolls forward; `out` emits\n * `outMapper(response)`.\n * - On `loop.status=\"done\"`: agent's status emits `\"done\"`.\n * - On `loop.status=\"error\"` (or any ERROR propagation): agent's status\n * emits `\"error\"`.\n */\nexport class AgentGraph<TIn, TOut> extends Graph {\n\t/** The agent's typed inbox. Writable; `in.emit(value)` kicks the loop. */\n\treadonly in: Node<TIn>;\n\t/** The agent's typed outbox. SENTINEL until first response. */\n\treadonly out: Node<TOut>;\n\t/** Lifecycle status (translated from the underlying loop's substates). */\n\treadonly status: Node<AgentStatus>;\n\t/** Cumulative cost for the current / most-recent input. */\n\treadonly cost: Node<CostState>;\n\t/** The underlying agentLoop — exposed for inspection / advanced wiring. */\n\treadonly loop: AgentLoopGraph;\n\t/** Optional shared memory subgraph (mounted at `memory/` if provided). */\n\treadonly memory: AgentMemoryGraph<unknown> | null;\n\n\tconstructor(spec: AgentSpec<TIn, TOut>, opts?: GraphOptions) {\n\t\tsuper(spec.name, opts);\n\n\t\t// --- 1. Mount the agentLoop subgraph. ------------------------------\n\t\tconst initialTools = Array.isArray(spec.tools)\n\t\t\t? (spec.tools as readonly ToolDefinition[])\n\t\t\t: undefined;\n\t\tthis.loop = agentLoop(`${spec.name}-loop`, {\n\t\t\tadapter: spec.adapter,\n\t\t\t...(spec.systemPrompt != null ? { systemPrompt: spec.systemPrompt } : {}),\n\t\t\t...(initialTools != null ? { tools: initialTools } : {}),\n\t\t\t...(spec.maxIterations != null ? { maxTurns: spec.maxIterations } : {}),\n\t\t});\n\t\tthis.mount(\"loop\", this.loop);\n\n\t\t// --- 2. Reactive tools subscription (if Node-form). ----------------\n\t\t// agentLoop's tools are static-array at construction; we reconcile\n\t\t// dynamically by subscribing to the user's reactive Node and\n\t\t// register/unregister against the inner toolRegistry.\n\t\tif (spec.tools != null && !Array.isArray(spec.tools)) {\n\t\t\tconst toolsNode = spec.tools as Node<readonly ToolDefinition[]>;\n\t\t\tconst registered = new Set<string>();\n\t\t\tconst unsubTools = toolsNode.subscribe((msgs) => {\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\tconst next = m[1] as readonly ToolDefinition[];\n\t\t\t\t\tconst nextNames = new Set(next.map((t) => t.name));\n\t\t\t\t\t// Unregister missing.\n\t\t\t\t\tfor (const name of registered) {\n\t\t\t\t\t\tif (!nextNames.has(name)) {\n\t\t\t\t\t\t\tthis.loop.tools.unregister(name);\n\t\t\t\t\t\t\tregistered.delete(name);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Register new (idempotent guard via local tracking).\n\t\t\t\t\tfor (const tool of next) {\n\t\t\t\t\t\tif (!registered.has(tool.name)) {\n\t\t\t\t\t\t\tthis.loop.tools.register(tool);\n\t\t\t\t\t\t\tregistered.add(tool.name);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.addDisposer(unsubTools);\n\t\t}\n\n\t\t// --- 3. Optional shared memory subgraph (passed through). ----------\n\t\t// v1: memory is mounted but NOT yet wired into the loop's chat — the\n\t\t// chat-context-from-memory glue is a separate follow-up. Mounting it\n\t\t// here gives the bundle a stable surface for §29 handoff (callers\n\t\t// can pass the SAME instance to multiple agents for shared memory).\n\t\tthis.memory = spec.memory ?? null;\n\t\tif (this.memory != null) {\n\t\t\tthis.mount(\"memory\", this.memory);\n\t\t}\n\n\t\t// --- 4. `in` — the typed inbox. ------------------------------------\n\t\t// SENTINEL until first emit; `equals: () => false` so re-emitting the\n\t\t// same value still kicks (no spurious dedup of repeat inputs).\n\t\tthis.in = node<TIn>([], {\n\t\t\tname: \"in\",\n\t\t\tdescribeKind: \"state\",\n\t\t\tmeta: aiMeta(\"agent_in\"),\n\t\t\tequals: () => false,\n\t\t});\n\t\tthis.add(this.in, { name: \"in\" });\n\n\t\t// --- 5. `cost` — per-input token counters. -------------------------\n\t\tconst costNode = node<CostState>([], {\n\t\t\tname: \"cost\",\n\t\t\tdescribeKind: \"state\",\n\t\t\tmeta: aiMeta(\"agent_cost\"),\n\t\t\tinitial: ZERO_COST,\n\t\t});\n\t\tthis.add(costNode, { name: \"cost\" });\n\t\tthis.cost = costNode;\n\n\t\t// --- 6. `out` — the typed outbox. ----------------------------------\n\t\t// Derived from `loop.lastResponse`. SENTINEL while `loop.lastResponse`\n\t\t// has never emitted a real response (F9 fix: the loop now stays\n\t\t// SENTINEL too — no more eager `null` placeholder), so the SENTINEL\n\t\t// detector inside the fn is `prevData[0] === undefined`. Between\n\t\t// runs, `loop.lastResponse.down([[INVALIDATE]])` clears that\n\t\t// `prevData` slot back to undefined, so this derived correctly gates\n\t\t// to RESOLVED on the next status=\"idle\" wave.\n\t\tconst outMapper = spec.outMapper ?? defaultOutMapper<TOut>();\n\t\tconst outNode = node<TOut>(\n\t\t\t[this.loop.lastResponse],\n\t\t\t(data, a, ctx) => {\n\t\t\t\tconst batch0 = data[0];\n\t\t\t\tconst resp =\n\t\t\t\t\tbatch0 != null && batch0.length > 0\n\t\t\t\t\t\t? (batch0.at(-1) as LLMResponse | undefined)\n\t\t\t\t\t\t: (ctx.prevData[0] as LLMResponse | undefined);\n\t\t\t\tif (resp === undefined) {\n\t\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ta.emit(outMapper(resp));\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"out\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: aiMeta(\"agent_out\"),\n\t\t\t\t// Each in.emit may produce a structurally-equal response (e.g.\n\t\t\t\t// from a deterministic adapter) — disable framework dedup so\n\t\t\t\t// repeat emits propagate and `awaitSettled({skipCurrent:true})`\n\t\t\t\t// sees them. Callers can wrap with `distinctUntilChanged` if\n\t\t\t\t// they want change-only semantics.\n\t\t\t\tequals: () => false,\n\t\t\t},\n\t\t);\n\t\tthis.add(outNode, { name: \"out\" });\n\t\tthis.out = outNode;\n\n\t\t// --- 7. `status` — translated from loop.status. --------------------\n\t\t// Mirror via §32 pattern: a state node downstream consumers depend on,\n\t\t// reset and updated by an effect listening to loop.status.\n\t\tconst statusNode = node<AgentStatus>([], {\n\t\t\tname: \"status\",\n\t\t\tdescribeKind: \"state\",\n\t\t\tmeta: aiMeta(\"agent_status\"),\n\t\t\tinitial: \"idle\",\n\t\t});\n\t\tthis.add(statusNode, { name: \"status\" });\n\t\tthis.status = statusNode;\n\n\t\tconst statusMirrorEff = node(\n\t\t\t[this.loop.status],\n\t\t\t(data, _a, ctx) => {\n\t\t\t\tconst batch0 = data[0];\n\t\t\t\tconst loopStatus =\n\t\t\t\t\tbatch0 != null && batch0.length > 0\n\t\t\t\t\t\t? (batch0.at(-1) as string)\n\t\t\t\t\t\t: ((ctx.prevData[0] as string | undefined) ?? \"idle\");\n\t\t\t\tconst next: AgentStatus =\n\t\t\t\t\tloopStatus === \"idle\"\n\t\t\t\t\t\t? \"idle\"\n\t\t\t\t\t\t: loopStatus === \"thinking\" || loopStatus === \"acting\"\n\t\t\t\t\t\t\t? \"running\"\n\t\t\t\t\t\t\t: loopStatus === \"done\"\n\t\t\t\t\t\t\t\t? \"done\"\n\t\t\t\t\t\t\t\t: loopStatus === \"error\"\n\t\t\t\t\t\t\t\t\t? \"error\"\n\t\t\t\t\t\t\t\t\t: \"idle\";\n\t\t\t\tif (statusNode.cache !== next) statusNode.emit(next);\n\t\t\t},\n\t\t\t{ describeKind: \"effect\", meta: aiMeta(\"agent_status_mirror\") },\n\t\t);\n\t\tthis.addDisposer(keepalive(statusMirrorEff));\n\n\t\t// --- 8. Cost-rollup effect. ---------------------------------------\n\t\t// Rolls forward on each loop.lastResponse emission. Reads\n\t\t// loop.turn.cache for the iteration count (sole-owner-reactive-reader\n\t\t// per Phase 12 D1 lock — loop is mounted as a subgraph of this Graph).\n\t\t// SENTINEL gate: `prevData[0] === undefined` means no response has\n\t\t// ever been delivered for this run (post-INVALIDATE reset between\n\t\t// runs), so the rollup short-circuits.\n\t\tconst costEff = node(\n\t\t\t[this.loop.lastResponse],\n\t\t\t(data, _a, ctx) => {\n\t\t\t\tconst batch0 = data[0];\n\t\t\t\tconst resp =\n\t\t\t\t\tbatch0 != null && batch0.length > 0\n\t\t\t\t\t\t? (batch0.at(-1) as LLMResponse | undefined)\n\t\t\t\t\t\t: (ctx.prevData[0] as LLMResponse | undefined);\n\t\t\t\tif (resp === undefined) return;\n\t\t\t\tconst prev = (costNode.cache as CostState | undefined) ?? ZERO_COST;\n\t\t\t\tconst turns = (this.loop.turn.cache as number | undefined) ?? prev.turns;\n\t\t\t\tconst next: CostState = {\n\t\t\t\t\tusage: resp.usage != null ? addUsage(prev.usage, resp.usage) : prev.usage,\n\t\t\t\t\tturns,\n\t\t\t\t};\n\t\t\t\tcostNode.emit(next);\n\t\t\t},\n\t\t\t{ describeKind: \"effect\", meta: aiMeta(\"agent_cost_rollup\") },\n\t\t);\n\t\tthis.addDisposer(keepalive(costEff));\n\n\t\t// --- 9. `in` → input queue → drain → kick the loop. ----------------\n\t\t// Phase 13.G/H + /qa N1(b) lock (2026-05-01): bundle.in is a\n\t\t// writable surface, but kicks are queued through an internal\n\t\t// hub-style topic + cursor subscription. Out-of-the-box queueing —\n\t\t// caller fires `in.emit(x)` while the agent is mid-run; the input\n\t\t// is parked on the queue and picked up when the loop returns to\n\t\t// `idle` / `done` / `error`. No mid-run reset / cost-leak hazard\n\t\t// (which the prior raw `in.subscribe → kick` path had).\n\t\t//\n\t\t// Topology:\n\t\t// `in` (state Node, writable) → `inputBridge` (subscribe →\n\t\t// publish) → `inputTopic` (TopicGraph<TIn>) → `inputSub`\n\t\t// (SubscriptionGraph<TIn>) → `drainEffect` (effect on\n\t\t// [inputSub.available, loop.status]) → loop kick.\n\t\tconst inMapper = spec.inMapper ?? defaultInMapper<TIn>();\n\t\tconst inputTopic: TopicGraph<TIn> = topic<TIn>(\"input-topic\");\n\t\tthis.mount(\"input-topic\", inputTopic);\n\t\tconst inputSub: SubscriptionGraph<TIn> = subscription<TIn>(\"input-sub\", inputTopic, {\n\t\t\tfrom: \"now\",\n\t\t});\n\t\tthis.mount(\"input-sub\", inputSub);\n\n\t\t// Bridge: `in.emit(x)` publishes to the topic. Validates the\n\t\t// caller-supplied input via `inMapper` at the boundary so the\n\t\t// type error surfaces in the caller's stack frame (not later\n\t\t// during drain). Per the F9 SENTINEL trap, `in` cache is\n\t\t// `undefined` until the first emit; push-on-subscribe delivers\n\t\t// nothing.\n\t\tconst inputBridge = this.in.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\tconst input = m[1] as TIn;\n\t\t\t\t// Boundary type-check: throws if TIn is not string and no\n\t\t\t\t// inMapper supplied. Better here than at drain time so the\n\t\t\t\t// caller's `in.emit(...)` raises synchronously.\n\t\t\t\tinMapper(input);\n\t\t\t\tinputTopic.publish(input);\n\t\t\t}\n\t\t});\n\t\tthis.addDisposer(inputBridge);\n\n\t\t// Drain effect: when an input is pending AND the loop is ready\n\t\t// (`idle` / `done` / `error`), pull one and kick. Re-entrancy\n\t\t// guard via `loop.status` — `thinking` / `acting` skip.\n\t\tconst drainEffect = node(\n\t\t\t[inputSub.available, this.loop.status],\n\t\t\t(data, _a, ctx) => {\n\t\t\t\tconst availBatch = data[0];\n\t\t\t\tconst statusBatch = data[1];\n\t\t\t\tconst avail =\n\t\t\t\t\t(availBatch != null && availBatch.length > 0\n\t\t\t\t\t\t? (availBatch.at(-1) as readonly TIn[])\n\t\t\t\t\t\t: ((ctx.prevData[0] as readonly TIn[] | undefined) ?? [])) ?? [];\n\t\t\t\tconst stat =\n\t\t\t\t\t(statusBatch != null && statusBatch.length > 0\n\t\t\t\t\t\t? (statusBatch.at(-1) as string)\n\t\t\t\t\t\t: ((ctx.prevData[1] as string | undefined) ?? \"idle\")) ?? \"idle\";\n\t\t\t\tif (avail.length === 0) return;\n\t\t\t\tif (stat === \"thinking\" || stat === \"acting\") return;\n\t\t\t\tconst result = inputSub.pullAndAck(1);\n\t\t\t\tif (result.items.length === 0) return;\n\t\t\t\tconst input = result.items[0] as TIn;\n\t\t\t\tconst userMsg = inMapper(input);\n\t\t\t\tbatch(() => {\n\t\t\t\t\t// Reset per-input accumulators so cost/turns don't include\n\t\t\t\t\t// the previous input. `lastResponse` is reset via plain\n\t\t\t\t\t// `[[INVALIDATE]]` — under DS-13.5.A INVALIDATE both clears\n\t\t\t\t\t// `_cached` AND settles the consuming wave (decrements\n\t\t\t\t\t// `_dirtyDepCount` like RESOLVED), so dependents like\n\t\t\t\t\t// `out` / `costEff` fire on the next status transition\n\t\t\t\t\t// without staying wedged in DIRTY. Pre-DS-13.5.A this used\n\t\t\t\t\t// the `[[INVALIDATE], [RESOLVED]]` paired-reset workaround.\n\t\t\t\t\tthis.loop.lastResponse.down([[INVALIDATE]]);\n\t\t\t\t\tthis.loop.turn.emit(0);\n\t\t\t\t\tthis.loop.aborted.emit(false);\n\t\t\t\t\tcostNode.emit(ZERO_COST);\n\t\t\t\t\tthis.loop.chat.append(\"user\", userMsg);\n\t\t\t\t\tthis.loop.status.emit(\"thinking\");\n\t\t\t\t});\n\t\t\t},\n\t\t\t{ describeKind: \"effect\", meta: aiMeta(\"agent_input_drain\") },\n\t\t);\n\t\tthis.addDisposer(keepalive(drainEffect));\n\n\t\t// `out` and `status` keepalives are unnecessary because the cost /\n\t\t// status effects above already activate `loop.lastResponse` and\n\t\t// `loop.status` — the derived `out` reads from a kept-alive source.\n\t\t// We do keep `out` alive explicitly so `awaitSettled(bundle.out,\n\t\t// { skipCurrent: true })` works even when no other consumer\n\t\t// subscribes between input and response.\n\t\tthis.addDisposer(keepalive(this.out));\n\n\t\t// Surface in describe.\n\t\tvoid TERMINAL_STATUSES;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Default mappers\n// ---------------------------------------------------------------------------\n\n/**\n * Default `inMapper` for `TIn extends string`. Asserts the runtime type at\n * the boundary so callers who omit `inMapper` for a non-string `TIn` get a\n * clear error rather than a silent passthrough.\n */\nfunction defaultInMapper<TIn>(): (input: TIn) => string {\n\treturn (input) => {\n\t\tif (typeof input !== \"string\") {\n\t\t\tthrow new TypeError(\n\t\t\t\t`agent: inMapper is required when TIn is not a string (got ${typeof input}). Pass spec.inMapper.`,\n\t\t\t);\n\t\t}\n\t\treturn input;\n\t};\n}\n\n/**\n * Default `outMapper` for `TOut extends LLMResponse`. Asserts the response\n * shape at the boundary; callers with a non-LLMResponse `TOut` must\n * provide `outMapper`.\n */\nfunction defaultOutMapper<TOut>(): (response: LLMResponse) => TOut {\n\treturn (response) => response as unknown as TOut;\n}\n","/**\n * Phase 13.H — `agent(spec)` preset + `presetRegistry` sugar.\n *\n * Source: `archive/docs/SESSION-multi-agent-gap-analysis.md` G1 + G2.\n *\n * `agent()` is the ergonomic factory — given a parent Graph and an\n * `AgentSpec`, mints an `AgentGraph`, mounts it under the parent at\n * `spec.name`, and returns the `AgentBundle` contract.\n *\n * `presetRegistry()` is thin sugar over `reactiveMap` — a typed reactive\n * map of `<id, preset>`. Pairs with `materialize` (Phase 13.C) for\n * dynamic preset selection: callers store specs / factories / configs in\n * the registry and `materialize` mounts the matching one based on a\n * routing key.\n *\n * **Cross-cut #1 lock:** no `agent.run()` imperative sugar — caller-side\n * runtime is `bundle.in.emit(input)` + `awaitSettled(bundle.out)`.\n */\n\nimport { type ReactiveMapBundle, reactiveMap } from \"@graphrefly/pure-ts/extra\";\nimport type { Graph } from \"@graphrefly/pure-ts/graph\";\nimport type { LLMResponse } from \"../../utils/ai/adapters/core/types.js\";\nimport { type AgentBundle, AgentGraph, type AgentSpec } from \"./agent.js\";\n\n// ---------------------------------------------------------------------------\n// agent() factory\n// ---------------------------------------------------------------------------\n\n/**\n * Mints an {@link AgentGraph} from `spec`, mounts it under `parent` at\n * `spec.name`, and returns the {@link AgentBundle} contract.\n *\n * **Default type parameters.** When called without explicit type params,\n * `TIn` defaults to `string` and `TOut` to `LLMResponse` — the common\n * case where the caller writes a user message and reads the raw response.\n * Custom types require both `inMapper` and `outMapper` in the spec; the\n * default mappers throw at runtime if `TIn` / `TOut` aren't string /\n * LLMResponse.\n *\n * **Memory partition default.** Each `agent()` call mints its own\n * `AgentMemoryGraph` if `spec.memory` is omitted (private memory; the\n * common case). Pass an explicit shared instance — e.g.\n * `agent(parent, { ..., memory: sharedMemory })` for two agents — to\n * implement §29 handoff context transfer.\n *\n * **Reactive entry / exit:**\n * - `bundle.in.emit(input)` kicks the loop reactively (no imperative\n * `.run()` per cross-cut #1 lock).\n * - `awaitSettled(bundle.out, { skipCurrent: true })` resolves on the\n * first response after the kick. `skipCurrent` matters for the second\n * call onward — `out` caches the prior response.\n *\n * **Mounting.** The factory mounts under `parent.mount(spec.name, ...)`.\n * The slot name must be free on `parent` at construction time. To keep\n * the agent unmounted, construct `new AgentGraph(spec)` directly.\n *\n * @example\n * ```ts\n * import { agent, awaitSettled, Graph } from \"@graphrefly/graphrefly-ts\";\n *\n * const parent = new Graph(\"parent\");\n * const a = agent(parent, {\n * name: \"researcher\",\n * adapter: openaiAdapter,\n * systemPrompt: \"Research the user's question carefully.\",\n * });\n * a.in.emit(\"What's the capital of France?\");\n * const resp = await awaitSettled(a.out, { skipCurrent: true });\n * ```\n *\n * @category patterns\n */\nexport function agent<TIn = string, TOut = LLMResponse>(\n\tparent: Graph,\n\tspec: AgentSpec<TIn, TOut>,\n): AgentBundle<TIn, TOut> {\n\tconst graph = new AgentGraph<TIn, TOut>(spec);\n\tparent.mount(spec.name, graph);\n\treturn {\n\t\tin: graph.in,\n\t\tout: graph.out,\n\t\tstatus: graph.status,\n\t\tcost: graph.cost,\n\t\tgraph,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// presetRegistry()\n// ---------------------------------------------------------------------------\n\n/**\n * The bundle returned by {@link presetRegistry}. Wraps a `reactiveMap`\n * with imperative `put` / `remove` shortcuts and exposes the underlying\n * `registry` for direct reactive consumption (`.entries` is a\n * `Node<ReadonlyMap<string, TPreset>>`).\n *\n * Use the `registry.entries` Node directly with {@link materialize} (Phase\n * 13.C) — pass it as the `factories` argument when `TPreset` is itself\n * a `() => Graph` factory thunk, or transform via `derived` when\n * `TPreset` is a richer spec type that needs a `spec → factory` adapter.\n */\nexport interface PresetRegistryBundle<TPreset> {\n\t/**\n\t * The underlying reactive map. `registry.entries` is the\n\t * `Node<ReadonlyMap<string, TPreset>>` — pass directly to\n\t * {@link materialize} when preset shape matches the factories arg.\n\t */\n\treadonly registry: ReactiveMapBundle<string, TPreset>;\n\t/** Imperative add / replace. Always emits a fresh snapshot. */\n\tput(id: string, preset: TPreset): void;\n\t/** Imperative remove. Returns `true` if the id was present. */\n\tremove(id: string): boolean;\n}\n\n/**\n * Thin sugar over `reactiveMap` — a typed registry of `<id, preset>` for\n * agent / strategy / persona / skill catalogs.\n *\n * **Generic over preset shape.** `TPreset` is open — could be an\n * {@link AgentSpec}, a `() => Graph` factory thunk, a static config\n * object, or anything else. Decoupled from `agent()` so the same primitive\n * powers harnessLoop strategy registries, pipelineGraph stage catalogs,\n * etc.\n *\n * **Composes with `materialize`.** When `TPreset` is a `() => Graph`\n * factory, pass `registry.entries` directly to\n * {@link materialize} as the `factories` argument. When `TPreset` is a\n * spec, transform via `derived` to build a `Map<id, () => Graph>` adapter:\n *\n * ```ts\n * const presets = presetRegistry<AgentSpec<string, LLMResponse>>();\n * presets.put(\"researcher\", { name: \"researcher\", adapter, systemPrompt: \"...\" });\n *\n * // Adapter: spec → factory.\n * const factories = derived(\n * [presets.registry.entries],\n * ([m]) => new Map(\n * [...m].map(([id, spec]) => [id, () => new AgentGraph(spec)]),\n * ),\n * );\n * const slot = materialize(activeKey, factories, parent);\n * ```\n *\n * @param initial - Optional initial entries.\n * @returns {@link PresetRegistryBundle}.\n *\n * @category patterns\n */\nexport function presetRegistry<TPreset>(\n\tinitial?: ReadonlyMap<string, TPreset>,\n): PresetRegistryBundle<TPreset> {\n\tconst registry = reactiveMap<string, TPreset>({ name: \"presetRegistry\" });\n\tif (initial != null) {\n\t\tfor (const [id, preset] of initial) {\n\t\t\tregistry.set(id, preset);\n\t\t}\n\t}\n\treturn {\n\t\tregistry,\n\t\tput(id, preset) {\n\t\t\tregistry.set(id, preset);\n\t\t},\n\t\tremove(id) {\n\t\t\tif (!registry.has(id)) return false;\n\t\t\tregistry.delete(id);\n\t\t\treturn true;\n\t\t},\n\t};\n}\n"],"mappings":"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","names":["batch","batch","DATA","INVALIDATE","node","RESOLVED","keepalive","Graph","Graph","DATA","node","RESOLVED","keepalive","batch","INVALIDATE"]}