@graphrefly/graphrefly 0.47.2 → 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 (266) hide show
  1. package/dist/base/composition/index.cjs +4 -3
  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 +8 -8
  6. package/dist/base/index.cjs +152 -78
  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 +75 -70
  11. package/dist/base/io/index.cjs +31 -17
  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 +1 -1
  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 +5 -3
  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 +5 -3
  26. package/dist/base/sources/browser/index.js.map +1 -1
  27. package/dist/base/sources/event/index.cjs +28 -0
  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 +4 -1
  32. package/dist/base/sources/index.cjs +75 -37
  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 +5 -2
  37. package/dist/{chunk-R6ZCSXKX.js → chunk-23MAWVOJ.js} +3 -3
  38. package/dist/{chunk-MS3WPRJR.js → chunk-3REMCHSS.js} +6 -6
  39. package/dist/chunk-3REMCHSS.js.map +1 -0
  40. package/dist/{chunk-CEVNQ74M.js → chunk-3YGXPUHW.js} +2 -2
  41. package/dist/{chunk-CEVNQ74M.js.map → chunk-3YGXPUHW.js.map} +1 -1
  42. package/dist/{chunk-6ZLCPUXS.js → chunk-46X2EFQH.js} +15 -4
  43. package/dist/chunk-46X2EFQH.js.map +1 -0
  44. package/dist/{chunk-NY2PYHNC.js → chunk-5UY3PNFY.js} +12 -5
  45. package/dist/chunk-5UY3PNFY.js.map +1 -0
  46. package/dist/{chunk-FQSQONOU.js → chunk-65OM4XLQ.js} +49 -3
  47. package/dist/chunk-65OM4XLQ.js.map +1 -0
  48. package/dist/{chunk-3PSLNJDU.js → chunk-6DQYBIHW.js} +314 -49
  49. package/dist/chunk-6DQYBIHW.js.map +1 -0
  50. package/dist/{chunk-LDCSZ72P.js → chunk-6YBER5UP.js} +3 -3
  51. package/dist/{chunk-LDCSZ72P.js.map → chunk-6YBER5UP.js.map} +1 -1
  52. package/dist/{chunk-3O3NKZJW.js → chunk-7T7WLEPM.js} +24 -3
  53. package/dist/chunk-7T7WLEPM.js.map +1 -0
  54. package/dist/{chunk-PKPO3JTZ.js → chunk-AQAKDE7F.js} +29 -11
  55. package/dist/chunk-AQAKDE7F.js.map +1 -0
  56. package/dist/{chunk-6MRSX3YK.js → chunk-B5Y5GPD5.js} +2 -2
  57. package/dist/{chunk-BXGZFGZ4.js → chunk-C5QD5DQX.js} +22 -1
  58. package/dist/chunk-C5QD5DQX.js.map +1 -0
  59. package/dist/{chunk-4XCHZRUJ.js → chunk-D5YGR4TP.js} +58 -7
  60. package/dist/chunk-D5YGR4TP.js.map +1 -0
  61. package/dist/{chunk-NPRP3MCV.js → chunk-DHDCOOJU.js} +2 -2
  62. package/dist/chunk-DHDCOOJU.js.map +1 -0
  63. package/dist/{chunk-VP3TIUDF.js → chunk-DVTDF5OI.js} +2 -2
  64. package/dist/{chunk-OXD5LFQP.js → chunk-G7H6PN7P.js} +2 -2
  65. package/dist/{chunk-EL5VHUGK.js → chunk-GGKHHG5Y.js} +32 -18
  66. package/dist/chunk-GGKHHG5Y.js.map +1 -0
  67. package/dist/{chunk-446I4EGD.js → chunk-J5TBZFBD.js} +2 -2
  68. package/dist/{chunk-7AVQIGF6.js → chunk-K4ZYJ4EM.js} +554 -460
  69. package/dist/chunk-K4ZYJ4EM.js.map +1 -0
  70. package/dist/{chunk-QFE5BQH7.js → chunk-LTSI7ULC.js} +2 -2
  71. package/dist/{chunk-5GVURVIG.js → chunk-MMHGYX44.js} +12 -2
  72. package/dist/{chunk-5GVURVIG.js.map → chunk-MMHGYX44.js.map} +1 -1
  73. package/dist/{chunk-KRFGO5QH.js → chunk-MQMTRKY3.js} +118 -43
  74. package/dist/chunk-MQMTRKY3.js.map +1 -0
  75. package/dist/{chunk-42FQ27MQ.js → chunk-MTODGQBR.js} +44 -179
  76. package/dist/chunk-MTODGQBR.js.map +1 -0
  77. package/dist/{chunk-FVINAAKA.js → chunk-NBK6QQMG.js} +14 -13
  78. package/dist/{chunk-FVINAAKA.js.map → chunk-NBK6QQMG.js.map} +1 -1
  79. package/dist/{chunk-KNU73RZW.js → chunk-NSA5K5G2.js} +2 -2
  80. package/dist/{chunk-MLTPJMH6.js → chunk-QQYULEZL.js} +2 -2
  81. package/dist/chunk-QSW4DFKE.js +31 -0
  82. package/dist/chunk-QSW4DFKE.js.map +1 -0
  83. package/dist/{chunk-VAZXUK6G.js → chunk-SUNCHMML.js} +2 -2
  84. package/dist/{chunk-EP4WVQLX.js → chunk-T2U6N3FV.js} +6 -6
  85. package/dist/{chunk-T7SP3EYR.js → chunk-T5URUIIY.js} +33 -24
  86. package/dist/chunk-T5URUIIY.js.map +1 -0
  87. package/dist/{chunk-VNXAF2KE.js → chunk-TPTZZV25.js} +6 -6
  88. package/dist/chunk-TPTZZV25.js.map +1 -0
  89. package/dist/{chunk-IOJDYUA7.js → chunk-V46JWFGV.js} +6 -5
  90. package/dist/chunk-V46JWFGV.js.map +1 -0
  91. package/dist/{chunk-WGDEBIP4.js → chunk-X6ESZDR6.js} +5 -6
  92. package/dist/chunk-X6ESZDR6.js.map +1 -0
  93. package/dist/{chunk-N65E26UL.js → chunk-XEWV254I.js} +2 -2
  94. package/dist/{chunk-N65E26UL.js.map → chunk-XEWV254I.js.map} +1 -1
  95. package/dist/{chunk-PTWADEH3.js → chunk-YBJVKMTM.js} +34 -14
  96. package/dist/chunk-YBJVKMTM.js.map +1 -0
  97. package/dist/{chunk-DDTS7F5O.js → chunk-ZW32BPXV.js} +12 -3
  98. package/dist/chunk-ZW32BPXV.js.map +1 -0
  99. package/dist/compat/index.cjs +51 -4
  100. package/dist/compat/index.cjs.map +1 -1
  101. package/dist/compat/index.d.cts +1 -1
  102. package/dist/compat/index.d.ts +1 -1
  103. package/dist/compat/index.js +6 -6
  104. package/dist/compat/nestjs/index.cjs +51 -4
  105. package/dist/compat/nestjs/index.cjs.map +1 -1
  106. package/dist/compat/nestjs/index.d.cts +1 -1
  107. package/dist/compat/nestjs/index.d.ts +1 -1
  108. package/dist/compat/nestjs/index.js +3 -3
  109. package/dist/{fallback-Bx46zqky.d.cts → fallback-BROR6ZhO.d.cts} +1 -1
  110. package/dist/{fallback-pIWW8A2d.d.ts → fallback-DO80aM_3.d.ts} +1 -1
  111. package/dist/{index-B_p8tnvf.d.cts → index-D1z3XcF9.d.cts} +1 -0
  112. package/dist/{index-_HDSmPyp.d.ts → index-DZ6yua0Q.d.ts} +1 -0
  113. package/dist/index.cjs +2215 -1676
  114. package/dist/index.cjs.map +1 -1
  115. package/dist/index.d.cts +10 -10
  116. package/dist/index.d.ts +10 -10
  117. package/dist/index.js +169 -146
  118. package/dist/index.js.map +1 -1
  119. package/dist/presets/ai/index.cjs +46 -0
  120. package/dist/presets/ai/index.cjs.map +1 -1
  121. package/dist/presets/ai/index.js +12 -12
  122. package/dist/presets/harness/index.cjs +130 -18
  123. package/dist/presets/harness/index.cjs.map +1 -1
  124. package/dist/presets/harness/index.d.cts +15 -5
  125. package/dist/presets/harness/index.d.ts +15 -5
  126. package/dist/presets/harness/index.js +22 -22
  127. package/dist/presets/index.cjs +222 -53
  128. package/dist/presets/index.cjs.map +1 -1
  129. package/dist/presets/index.d.cts +2 -2
  130. package/dist/presets/index.d.ts +2 -2
  131. package/dist/presets/index.js +45 -45
  132. package/dist/presets/inspect/index.cjs +63 -14
  133. package/dist/presets/inspect/index.cjs.map +1 -1
  134. package/dist/presets/inspect/index.d.cts +1 -1
  135. package/dist/presets/inspect/index.d.ts +1 -1
  136. package/dist/presets/inspect/index.js +6 -6
  137. package/dist/presets/resilience/index.cjs +29 -21
  138. package/dist/presets/resilience/index.cjs.map +1 -1
  139. package/dist/presets/resilience/index.d.cts +12 -8
  140. package/dist/presets/resilience/index.d.ts +12 -8
  141. package/dist/presets/resilience/index.js +3 -3
  142. package/dist/{rate-limiter-DpVbSYdH.d.cts → rate-limiter-DC26FM8J.d.cts} +10 -1
  143. package/dist/{rate-limiter-CEALq4N1.d.ts → rate-limiter-DyWpwpQP.d.ts} +10 -1
  144. package/dist/{reactive-layout-fswlBUvX.d.ts → reactive-layout-BBBWH0V_.d.cts} +85 -4
  145. package/dist/{reactive-layout-fswlBUvX.d.cts → reactive-layout-BBBWH0V_.d.ts} +85 -4
  146. package/dist/solutions/index.cjs +168 -47
  147. package/dist/solutions/index.cjs.map +1 -1
  148. package/dist/solutions/index.d.cts +2 -2
  149. package/dist/solutions/index.d.ts +2 -2
  150. package/dist/solutions/index.js +28 -28
  151. package/dist/{spawnable-5mDY501F.d.cts → spawnable-B2IlW60f.d.cts} +23 -2
  152. package/dist/{spawnable-D3lR0oQu.d.ts → spawnable-tttFz2Nh.d.ts} +23 -2
  153. package/dist/testing/index.cjs +94 -0
  154. package/dist/testing/index.cjs.map +1 -0
  155. package/dist/testing/index.d.cts +59 -0
  156. package/dist/testing/index.d.ts +59 -0
  157. package/dist/testing/index.js +73 -0
  158. package/dist/testing/index.js.map +1 -0
  159. package/dist/utils/ai/browser.cjs.map +1 -1
  160. package/dist/utils/ai/browser.d.cts +2 -2
  161. package/dist/utils/ai/browser.d.ts +2 -2
  162. package/dist/utils/ai/browser.js +6 -6
  163. package/dist/utils/ai/browser.js.map +1 -1
  164. package/dist/utils/ai/index.cjs +250 -166
  165. package/dist/utils/ai/index.cjs.map +1 -1
  166. package/dist/utils/ai/index.d.cts +108 -12
  167. package/dist/utils/ai/index.d.ts +108 -12
  168. package/dist/utils/ai/index.js +21 -19
  169. package/dist/utils/ai/node.cjs.map +1 -1
  170. package/dist/utils/ai/node.d.cts +5 -5
  171. package/dist/utils/ai/node.d.ts +5 -5
  172. package/dist/utils/ai/node.js +2 -2
  173. package/dist/utils/ai/node.js.map +1 -1
  174. package/dist/utils/cqrs/index.cjs +29 -3
  175. package/dist/utils/cqrs/index.cjs.map +1 -1
  176. package/dist/utils/cqrs/index.d.cts +12 -7
  177. package/dist/utils/cqrs/index.d.ts +12 -7
  178. package/dist/utils/cqrs/index.js +2 -2
  179. package/dist/utils/demo-shell/index.cjs +45 -19
  180. package/dist/utils/demo-shell/index.cjs.map +1 -1
  181. package/dist/utils/demo-shell/index.d.cts +1 -1
  182. package/dist/utils/demo-shell/index.d.ts +1 -1
  183. package/dist/utils/demo-shell/index.js +2 -2
  184. package/dist/utils/domain-templates/index.cjs.map +1 -1
  185. package/dist/utils/domain-templates/index.js +3 -3
  186. package/dist/utils/graphspec/index.cjs.map +1 -1
  187. package/dist/utils/graphspec/index.js +3 -3
  188. package/dist/utils/index.cjs +1642 -1225
  189. package/dist/utils/index.cjs.map +1 -1
  190. package/dist/utils/index.d.cts +7 -7
  191. package/dist/utils/index.d.ts +7 -7
  192. package/dist/utils/index.js +72 -54
  193. package/dist/utils/inspect/index.cjs +52 -4
  194. package/dist/utils/inspect/index.cjs.map +1 -1
  195. package/dist/utils/inspect/index.d.cts +32 -3
  196. package/dist/utils/inspect/index.d.ts +32 -3
  197. package/dist/utils/inspect/index.js +4 -4
  198. package/dist/utils/job-queue/index.cjs +46 -9
  199. package/dist/utils/job-queue/index.cjs.map +1 -1
  200. package/dist/utils/job-queue/index.d.cts +33 -3
  201. package/dist/utils/job-queue/index.d.ts +33 -3
  202. package/dist/utils/job-queue/index.js +2 -2
  203. package/dist/utils/memory/index.cjs +556 -462
  204. package/dist/utils/memory/index.cjs.map +1 -1
  205. package/dist/utils/memory/index.d.cts +203 -24
  206. package/dist/utils/memory/index.d.ts +203 -24
  207. package/dist/utils/memory/index.js +10 -2
  208. package/dist/utils/messaging/index.cjs.map +1 -1
  209. package/dist/utils/messaging/index.d.cts +4 -3
  210. package/dist/utils/messaging/index.d.ts +4 -3
  211. package/dist/utils/messaging/index.js +2 -2
  212. package/dist/utils/orchestration/index.cjs +9 -0
  213. package/dist/utils/orchestration/index.cjs.map +1 -1
  214. package/dist/utils/orchestration/index.js +3 -3
  215. package/dist/utils/process/index.cjs +32 -2
  216. package/dist/utils/process/index.cjs.map +1 -1
  217. package/dist/utils/process/index.d.cts +4 -3
  218. package/dist/utils/process/index.d.ts +4 -3
  219. package/dist/utils/process/index.js +2 -2
  220. package/dist/utils/reactive-layout/index.cjs +184 -55
  221. package/dist/utils/reactive-layout/index.cjs.map +1 -1
  222. package/dist/utils/reactive-layout/index.d.cts +128 -3
  223. package/dist/utils/reactive-layout/index.d.ts +128 -3
  224. package/dist/utils/reactive-layout/index.js +16 -8
  225. package/dist/utils/reduction/index.cjs.map +1 -1
  226. package/dist/utils/reduction/index.js +2 -2
  227. package/dist/utils/resilience/index.cjs +29 -20
  228. package/dist/utils/resilience/index.cjs.map +1 -1
  229. package/dist/utils/resilience/index.d.cts +1 -1
  230. package/dist/utils/resilience/index.d.ts +1 -1
  231. package/dist/utils/resilience/index.js +2 -2
  232. package/dist/utils/surface/index.cjs.map +1 -1
  233. package/dist/utils/surface/index.js +4 -4
  234. package/package.json +15 -3
  235. package/dist/chunk-3O3NKZJW.js.map +0 -1
  236. package/dist/chunk-3PSLNJDU.js.map +0 -1
  237. package/dist/chunk-42FQ27MQ.js.map +0 -1
  238. package/dist/chunk-4XCHZRUJ.js.map +0 -1
  239. package/dist/chunk-6ZLCPUXS.js.map +0 -1
  240. package/dist/chunk-7AVQIGF6.js.map +0 -1
  241. package/dist/chunk-BXGZFGZ4.js.map +0 -1
  242. package/dist/chunk-DDTS7F5O.js.map +0 -1
  243. package/dist/chunk-EL5VHUGK.js.map +0 -1
  244. package/dist/chunk-FQSQONOU.js.map +0 -1
  245. package/dist/chunk-IOJDYUA7.js.map +0 -1
  246. package/dist/chunk-KRFGO5QH.js.map +0 -1
  247. package/dist/chunk-MS3WPRJR.js.map +0 -1
  248. package/dist/chunk-NPRP3MCV.js.map +0 -1
  249. package/dist/chunk-NY2PYHNC.js.map +0 -1
  250. package/dist/chunk-PKPO3JTZ.js.map +0 -1
  251. package/dist/chunk-PTWADEH3.js.map +0 -1
  252. package/dist/chunk-T7SP3EYR.js.map +0 -1
  253. package/dist/chunk-VNXAF2KE.js.map +0 -1
  254. package/dist/chunk-W2BOPXTI.js +0 -1
  255. package/dist/chunk-W2BOPXTI.js.map +0 -1
  256. package/dist/chunk-WGDEBIP4.js.map +0 -1
  257. /package/dist/{chunk-R6ZCSXKX.js.map → chunk-23MAWVOJ.js.map} +0 -0
  258. /package/dist/{chunk-6MRSX3YK.js.map → chunk-B5Y5GPD5.js.map} +0 -0
  259. /package/dist/{chunk-VP3TIUDF.js.map → chunk-DVTDF5OI.js.map} +0 -0
  260. /package/dist/{chunk-OXD5LFQP.js.map → chunk-G7H6PN7P.js.map} +0 -0
  261. /package/dist/{chunk-446I4EGD.js.map → chunk-J5TBZFBD.js.map} +0 -0
  262. /package/dist/{chunk-QFE5BQH7.js.map → chunk-LTSI7ULC.js.map} +0 -0
  263. /package/dist/{chunk-KNU73RZW.js.map → chunk-NSA5K5G2.js.map} +0 -0
  264. /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
  265. /package/dist/{chunk-VAZXUK6G.js.map → chunk-SUNCHMML.js.map} +0 -0
  266. /package/dist/{chunk-EP4WVQLX.js.map → chunk-T2U6N3FV.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/ai/agents/chat-stream.ts","../src/utils/ai/_internal.ts","../src/utils/ai/agents/tool-execution.ts","../src/utils/ai/agents/tool-registry.ts","../src/utils/ai/memory/memory-composers.ts","../src/utils/ai/prompts/prompt-node.ts","../src/utils/ai/prompts/prompt-call.ts"],"sourcesContent":["import { type Node, node, RESOLVED } from \"@graphrefly/pure-ts/core\";\nimport { keepalive, type ReactiveLogBundle, reactiveLog } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\nimport { aiMeta } from \"../_internal.js\";\nimport type { ChatMessage } from \"../adapters/core/types.js\";\n\n// ---------------------------------------------------------------------------\n// chatStream\n// ---------------------------------------------------------------------------\n\nexport type ChatStreamOptions = {\n\tgraph?: GraphOptions;\n\tmaxMessages?: number;\n};\n\nexport class ChatStreamGraph extends Graph {\n\tprivate readonly _log: ReactiveLogBundle<ChatMessage>;\n\treadonly messages: Node<readonly ChatMessage[]>;\n\t/**\n\t * Most recently appended message. Stays in the protocol SENTINEL state\n\t * (`cache === undefined`, no DATA emitted) until the first append, then\n\t * tracks the latest entry. Per COMPOSITION-GUIDE §1a, the SENTINEL is\n\t * the canonical \"no value yet\" signal — consumers detect empty via\n\t * `data[i] === undefined` inside reactive fns or `latest.cache === undefined`\n\t * outside. No `T | null` placeholder, no `hasLatest` companion.\n\t */\n\treadonly latest: Node<ChatMessage>;\n\treadonly messageCount: Node<number>;\n\n\tconstructor(name: string, opts: ChatStreamOptions = {}) {\n\t\tsuper(name, opts.graph);\n\n\t\tthis._log = reactiveLog<ChatMessage>([], {\n\t\t\tname: \"messages\",\n\t\t\tmaxSize: opts.maxMessages,\n\t\t});\n\t\tthis.messages = this._log.entries;\n\t\tthis.add(this.messages, { name: \"messages\" });\n\n\t\t// SENTINEL on empty (COMPOSITION-GUIDE §1a): return `[]` for\n\t\t// RESOLVED-only on empty stream, `[T]` to emit DATA. `latest.cache`\n\t\t// stays `undefined` until the first append.\n\t\tthis.latest = node<ChatMessage>(\n\t\t\t[this.messages],\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\tconst entries = data[0] as readonly ChatMessage[];\n\t\t\t\tif (entries.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(entries[entries.length - 1] as ChatMessage);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"latest\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: aiMeta(\"chat_latest\"),\n\t\t\t},\n\t\t);\n\t\tthis.add(this.latest, { name: \"latest\" });\n\t\tthis.addDisposer(keepalive(this.latest));\n\n\t\tthis.messageCount = node<number>(\n\t\t\t[this.messages],\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 ChatMessage[]).length);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"messageCount\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: aiMeta(\"chat_message_count\"),\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t);\n\t\tthis.add(this.messageCount, { name: \"messageCount\" });\n\t\tthis.addDisposer(keepalive(this.messageCount));\n\t}\n\n\tappend(role: ChatMessage[\"role\"], content: string, extra?: Partial<ChatMessage>): void {\n\t\tthis._log.append({ role, content, ...extra });\n\t}\n\n\tappendToolResult(callId: string, content: string): void {\n\t\tthis._log.append({ role: \"tool\", content, toolCallId: callId });\n\t}\n\n\tclear(): void {\n\t\tthis._log.clear();\n\t}\n\n\tallMessages(): readonly ChatMessage[] {\n\t\treturn this.messages.cache as readonly ChatMessage[];\n\t}\n}\n\nexport function chatStream(name: string, opts?: ChatStreamOptions): ChatStreamGraph {\n\treturn new ChatStreamGraph(name, opts);\n}\n","/**\n * @internal — shared helpers for the AI pattern modules.\n *\n * NOT part of the public API. Consumers reach public symbols through\n * `@graphrefly/graphrefly/patterns/ai` (the barrel).\n *\n * @module\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\ttype Messages,\n\ttype Node,\n\tnode,\n\tResettableTimer,\n} from \"@graphrefly/pure-ts/core\";\nimport { fromAny, type NodeInput } from \"@graphrefly/pure-ts/extra\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport type {\n\tChatMessage,\n\tLLMAdapter,\n\tLLMInvokeOptions,\n\tLLMResponse,\n} from \"./adapters/core/types.js\";\n\nexport function aiMeta(kind: string, extra?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"ai\", kind, extra);\n}\n\nexport function isPromiseLike(x: unknown): x is PromiseLike<unknown> {\n\treturn x != null && typeof (x as PromiseLike<unknown>).then === \"function\";\n}\n\nexport function isNodeLike(x: unknown): x is Node<unknown> {\n\treturn (\n\t\ttypeof x === \"object\" &&\n\t\tx !== null &&\n\t\t\"subscribe\" in x &&\n\t\ttypeof (x as Node<unknown>).subscribe === \"function\" &&\n\t\t\"cache\" in x\n\t);\n}\n\nexport function isAsyncIterableLike(x: unknown): x is AsyncIterable<unknown> {\n\treturn (\n\t\tx != null &&\n\t\ttypeof x === \"object\" &&\n\t\tSymbol.asyncIterator in x &&\n\t\ttypeof (x as AsyncIterable<unknown>)[Symbol.asyncIterator] === \"function\"\n\t);\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/** First settled `DATA` from a `Node` (do not pass plain strings — `fromAny` would iterate chars). */\nexport function firstDataFromNode(\n\tresolved: Node<unknown>,\n\topts?: { timeoutMs?: number },\n): Promise<unknown> {\n\tif ((resolved as { status?: string }).status === \"settled\") {\n\t\tconst immediate = resolved.cache;\n\t\tif (immediate !== undefined) {\n\t\t\treturn Promise.resolve(immediate);\n\t\t}\n\t}\n\tconst timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = new ResettableTimer();\n\t\tconst unsub = resolved.subscribe((messages) => {\n\t\t\tfor (const msg of messages) {\n\t\t\t\tif (msg[0] === DATA) {\n\t\t\t\t\ttimer.cancel();\n\t\t\t\t\tunsub();\n\t\t\t\t\tresolve(msg[1]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (msg[0] === ERROR) {\n\t\t\t\t\ttimer.cancel();\n\t\t\t\t\tunsub();\n\t\t\t\t\treject(msg[1]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (msg[0] === COMPLETE) {\n\t\t\t\t\ttimer.cancel();\n\t\t\t\t\tunsub();\n\t\t\t\t\treject(new Error(\"firstDataFromNode: completed without producing a value\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttimer.start(timeoutMs, () => {\n\t\t\tunsub();\n\t\t\treject(new Error(`firstDataFromNode: timed out after ${timeoutMs}ms`));\n\t\t});\n\t});\n}\n\n/** Await Promise-likes, then resolve `Node` / async-iterable inputs via `fromAny` + first `DATA`. */\nexport async function resolveToolHandlerResult(value: unknown): Promise<unknown> {\n\tif (isPromiseLike(value)) {\n\t\treturn resolveToolHandlerResult(await value);\n\t}\n\tif (isNodeLike(value)) {\n\t\treturn firstDataFromNode(value);\n\t}\n\tif (isAsyncIterableLike(value)) {\n\t\treturn firstDataFromNode(fromAny(value as NodeInput<unknown>));\n\t}\n\treturn value;\n}\n\n/** Strip markdown code fences, handling trailing commentary after closing fence. */\nexport function stripFences(text: string): string {\n\tconst match = text.match(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```[\\s\\S]*$/);\n\treturn match ? match[1]! : text;\n}\n\n/**\n * Bridge-layer failure kind reported to {@link OneShotLlmCallConfig.onFailure}.\n *\n * - `\"throw\"` — synchronous throw from `adapter.invoke()`.\n * - `\"error\"` — `[ERROR, value]` message on the bridged Node.\n * - `\"complete\"` — the bridged Node closed without emitting DATA.\n * - `\"onSuccess-threw\"` — `onSuccess(resp)` itself threw (uncaught parse /\n * builder error). Caller's `onFailure` decides the failure-payload shape.\n */\nexport type OneShotLlmFailureKind = \"throw\" | \"error\" | \"complete\" | \"onSuccess-threw\";\n\n/** Configuration for {@link _oneShotLlmCall}. */\nexport interface OneShotLlmCallConfig<T> {\n\t/**\n\t * Build the success payload from the adapter's first DATA message.\n\t * MAY throw — the helper catches and routes through `onFailure(kind:\n\t * \"onSuccess-threw\", err)` so callers don't need their own try/catch.\n\t */\n\tonSuccess: (resp: LLMResponse) => T;\n\t/**\n\t * Build a failure payload when the bridge layer reports any of the\n\t * {@link OneShotLlmFailureKind} categories. Caller chooses the detail\n\t * string format and any error-class metadata.\n\t */\n\tonFailure: (kind: OneShotLlmFailureKind, err: unknown) => T;\n\t/**\n\t * Forwarded to `adapter.invoke(messages, opts)` — `signal` is set\n\t * by the helper from the producer's AbortController and CANNOT be\n\t * overridden here (cancellation is a hard contract of this helper).\n\t */\n\tinvokeOpts?: Omit<LLMInvokeOptions, \"signal\">;\n\t/**\n\t * Optional parent abort signal (e.g. JobFlow pump's per-claim signal).\n\t * When the parent aborts, the helper aborts its inner AbortController —\n\t * so `adapter.invoke({ signal })` and `fromAny({ signal })` see the\n\t * cascade and cancel in-flight work. Pump-driven harness teardown\n\t * (`harness.destroy()`) propagates through this hook (Tier 6.5 2.5b).\n\t */\n\tparentSignal?: AbortSignal;\n}\n\n/**\n * Internal — one-shot bridge from `adapter.invoke()` (a `NodeInput<LLMResponse>`)\n * into a producer that emits exactly one DATA + COMPLETE.\n *\n * **Why this exists.** The harness's `defaultLlmExecutor` and\n * `defaultLlmVerifier` (Tier 6.5 C2) both call `adapter.invoke()` once\n * per claimed JobFlow job and need to:\n * 1. Subscribe to the bridged Node, capture the first DATA, parse, emit\n * a domain payload.\n * 2. Map adapter throws / ERROR / COMPLETE-without-DATA to a domain\n * failure payload (rather than nack the JobFlow claim).\n * 3. Thread `signal: ac.signal` into BOTH `adapter.invoke()` (via\n * `LLMInvokeOptions.signal`) and `fromAny()` (covers Node-shaped\n * invokeResults) so teardown actually aborts in-flight HTTP work.\n * 4. Tear down the inner subscription cleanly when DATA captures or\n * when the producer is unsubscribed.\n *\n * Pre-extraction this body was duplicated ~80 LOC across the two default\n * bridges; symmetric fixes had to land twice (qa F1 / F2 / F5). This\n * helper centralizes the producer body so future bridge-layer fixes apply\n * once.\n *\n * **Not part of the public API.** Callers in `patterns/ai/_internal.ts`'s\n * import surface (the harness defaults today) use this; user code should\n * use `promptNode` for cross-wave reactive transforms or call\n * `adapter.invoke()` directly.\n */\nexport function _oneShotLlmCall<T>(\n\tadapter: LLMAdapter,\n\tmessages: readonly ChatMessage[],\n\tconfig: OneShotLlmCallConfig<T>,\n): NodeInput<T> {\n\treturn node<T>(\n\t\t(_data, actions) => {\n\t\t\tconst ac = new AbortController();\n\t\t\t// Link parent signal (e.g. pump per-claim signal) so cascading\n\t\t\t// teardown propagates: parent abort → inner ac.abort → adapter +\n\t\t\t// fromAny cancel.\n\t\t\tconst parentSignal = config.parentSignal;\n\t\t\tlet unlinkParent: () => void = () => undefined;\n\t\t\tif (parentSignal) {\n\t\t\t\tif (parentSignal.aborted) {\n\t\t\t\t\tac.abort();\n\t\t\t\t} else {\n\t\t\t\t\tconst onParentAbort = (): void => ac.abort();\n\t\t\t\t\tparentSignal.addEventListener(\"abort\", onParentAbort, { once: true });\n\t\t\t\t\tunlinkParent = () => parentSignal.removeEventListener(\"abort\", onParentAbort);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlet captured = false;\n\t\t\tlet unsub: (() => void) | null = null;\n\t\t\tconst emitOnce = (value: T): void => {\n\t\t\t\tif (captured) return;\n\t\t\t\tcaptured = true;\n\t\t\t\tactions.down([[DATA, value], [COMPLETE]] satisfies Messages);\n\t\t\t\tunsub?.();\n\t\t\t\tunsub = null;\n\t\t\t};\n\t\t\tlet invokeResult: NodeInput<LLMResponse>;\n\t\t\ttry {\n\t\t\t\tinvokeResult = adapter.invoke(messages, { ...config.invokeOpts, signal: ac.signal });\n\t\t\t} catch (err) {\n\t\t\t\temitOnce(config.onFailure(\"throw\", err));\n\t\t\t\treturn {\n\t\t\t\t\tonDeactivation: () => {\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}\n\t\t\tconst callNode = fromAny<LLMResponse>(invokeResult, { signal: ac.signal });\n\t\t\tunsub = callNode.subscribe((batch) => {\n\t\t\t\tfor (const m of batch) {\n\t\t\t\t\tif (captured) return;\n\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\temitOnce(config.onSuccess(m[1] as LLMResponse));\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\temitOnce(config.onFailure(\"onSuccess-threw\", err));\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\t\temitOnce(config.onFailure(\"error\", m[1]));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\t\t// COMPLETE without prior DATA — without this arm the JobFlow\n\t\t\t\t\t\t// pump's claim would stall (qa F1 regression). Helper handles\n\t\t\t\t\t\t// for ALL callers; defaults can't regress.\n\t\t\t\t\t\temitOnce(config.onFailure(\"complete\", undefined));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\t// Sync DATA delivery (cached state / `fromAny` over a sync value):\n\t\t\t// the callback ran reentrantly before `unsub` was assigned, so the\n\t\t\t// `unsub?.()` call inside `emitOnce` was a no-op. Drop the upstream\n\t\t\t// subscription now that we have the handle.\n\t\t\tif (captured && unsub) {\n\t\t\t\tunsub();\n\t\t\t\tunsub = null;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\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},\n\t\t{ describeKind: \"producer\" },\n\t);\n}\n","/**\n * `toolExecution` — reactive per-tool-call executor with retry + rescue.\n *\n * Lifted from the inlined `executeToolReactively` helper inside `agent-loop.ts`\n * so it can be consumed standalone by any caller with a reactive `toolCalls`\n * batch — not just `agentLoop`. The shape is: one input `Node<readonly\n * ToolCall[]>` + a `ToolRegistryGraph` → one output `Node<readonly\n * ToolResult[]>`. Each call maps to a per-call `retrySource(executeReactive)`\n * → optional `rescue` chain that emits the handler result on success, or a\n * JSON-wrapped `{ error }` payload on terminal failure so the LLM can see the\n * error as tool output and decide whether to try again via another tool call.\n *\n * **Cancellation.** `executeReactive` mints a per-call `AbortController` and\n * threads its signal into the handler call. When `switchMap` supersedes the\n * inner (a fresh `toolCalls` batch arrives) or the outer graph tears down,\n * the per-call node unsubscribes and `ac.abort()` fires. Signal-aware\n * handlers (`fetch(url, {signal})`, child-process kill, DB cancel) actually\n * stop in-flight work; handlers that ignore the signal still complete to\n * their original termination, but their result is discarded.\n *\n * @module\n */\n\nimport { type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { rescue, switchMap } from \"@graphrefly/pure-ts/extra\";\nimport { retry } from \"../../../base/resilience/retry.js\";\nimport type { ToolCall } from \"../adapters/core/types.js\";\nimport type { ToolRegistryGraph } from \"./tool-registry.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A single tool execution outcome: `{id, content}` where content is a JSON string. */\nexport interface ToolResult {\n\treadonly id: string;\n\treadonly content: string;\n}\n\nexport type ToolExecutionOptions = {\n\t/**\n\t * Reactive tool-call batch. Each non-empty emission triggers a fresh\n\t * per-call execution fan-out; superseding emissions cancel the prior fan.\n\t */\n\ttoolCalls: Node<readonly ToolCall[]>;\n\t/** Registry that resolves tool name → handler. */\n\ttools: ToolRegistryGraph;\n\t/**\n\t * Retry count per individual tool call. `retrySource({count: N})` retries\n\t * up to N times on error (N retries = N+1 total attempts). Default: 1.\n\t */\n\tretryCount?: number;\n\t/**\n\t * How to surface a terminal error after retries are exhausted.\n\t * - `\"rescue\"` (default): emit `{id, content: JSON.stringify({error})}`\n\t * so the LLM sees the failure as structured tool output and can decide\n\t * how to react. Sibling calls in the same batch continue to their own\n\t * completion; one call's failure does not affect the others.\n\t * - `\"propagate\"`: let the ERROR propagate downstream. **Blast radius:**\n\t * the per-batch `derived` join auto-errors when any per-call node\n\t * terminates with ERROR, so one call's failure discards every sibling's\n\t * DATA (even ones that already settled with a valid ToolResult). Use\n\t * `\"propagate\"` only when a single tool failure should be fatal for the\n\t * whole batch; prefer `\"rescue\"` when you want the LLM to see partial\n\t * results plus per-call error markers.\n\t */\n\tonError?: \"rescue\" | \"propagate\";\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Reactive executor for a batch of LLM tool calls.\n *\n * Each DATA emission on `toolCalls` dispatches a fresh per-call fan-out: for\n * every call in the batch, construct a `retrySource(fromAny(tools.execute(\n * name, args)))` node, optionally `rescue` it into a JSON error shape, and\n * join the results via a `derived` whose first-run gate waits for every call\n * to settle before emitting the batch. Empty batches (`calls.length === 0`)\n * are a caller-side invariant violation (the upstream gate should emit\n * RESOLVED for empty batches, not DATA) and trigger a loud error — callers\n * that want to accept empty batches should upstream-filter them first.\n *\n * Reference-equality + content-equality dedup is applied to the output batch\n * so duplicate re-emissions from a completing retrySource don't propagate.\n *\n * @param opts - `{ toolCalls, tools, retryCount?, onError? }`.\n * @returns `Node<readonly ToolResult[]>` — one ToolResult per input ToolCall.\n */\nexport function toolExecution(opts: ToolExecutionOptions): Node<readonly ToolResult[]> {\n\tconst { toolCalls, tools } = opts;\n\tconst retryCount = opts.retryCount ?? 1;\n\tconst onError = opts.onError ?? \"rescue\";\n\n\tconst batchEquals = (a: readonly ToolResult[], b: readonly ToolResult[]): boolean => {\n\t\tif (a === b) return true;\n\t\tif (a.length !== b.length) return false;\n\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\tconst ai = a[i];\n\t\t\tconst bi = b[i];\n\t\t\tif (ai?.id !== bi?.id) return false;\n\t\t\tif (ai?.content !== bi?.content) return false;\n\t\t}\n\t\treturn true;\n\t};\n\n\treturn switchMap<readonly ToolCall[], readonly ToolResult[]>(toolCalls, (calls) => {\n\t\tif (calls == null || calls.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t\"toolExecution: received an empty tool-call batch as DATA — callers must upstream-filter empty batches (emit RESOLVED) so switchMap is only dispatched for non-empty batches.\",\n\t\t\t);\n\t\t}\n\t\tconst perCall = calls.map((call) => executeOne(call, tools, retryCount, onError));\n\t\t// `executeOne` returns `Node<ToolResult>` in both \"rescue\" and\n\t\t// \"propagate\" modes (the rescue handler builds a `ToolResult`\n\t\t// shape; the success `derived` builds one directly). The join\n\t\t// just forwards the per-call values — no shape coercion needed.\n\t\treturn node(\n\t\t\tperCall,\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 as readonly ToolResult[]);\n\t\t\t},\n\t\t\t{ describeKind: \"derived\", name: \"toolExecution::batch\", equals: batchEquals },\n\t\t);\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/**\n * Per-call reactive executor. `retrySource` re-invokes the factory on ERROR\n * (each attempt mints a fresh `executeReactive` node, which in turn mints a\n * fresh `AbortController` and handler invocation). `executeReactive` itself\n * handles synchronous handler throws — they surface as `[[ERROR, err]]`\n * inside the producer, so `retrySource`'s reactive ERROR path fires\n * consistently regardless of handler shape. No `Promise.resolve().then(...)`\n * thunk needed — the reactive path is end-to-end.\n *\n * Handlers that return a plain string are surfaced as-is; anything else is\n * `JSON.stringify`'d so LLMs that parse tool results can roundtrip\n * structured data without surprise quoting.\n */\nfunction executeOne(\n\tcall: ToolCall,\n\ttools: ToolRegistryGraph,\n\tretryCount: number,\n\tonError: \"rescue\" | \"propagate\",\n): Node<ToolResult> {\n\tconst attempted: Node<unknown> = retry(() => tools.executeReactive(call.name, call.arguments), {\n\t\tcount: retryCount,\n\t}).node;\n\tconst onSuccess = node<ToolResult>(\n\t\t[attempted],\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 val = data[0];\n\t\t\tactions.emit({\n\t\t\t\tid: call.id,\n\t\t\t\tcontent: typeof val === \"string\" ? val : JSON.stringify(val),\n\t\t\t});\n\t\t},\n\t\t{ describeKind: \"derived\" },\n\t);\n\tif (onError === \"propagate\") return onSuccess;\n\treturn rescue(onSuccess, (err) => ({\n\t\tid: call.id,\n\t\tcontent: JSON.stringify({ error: String(err) }),\n\t}));\n}\n","import { ERROR, type Messages, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { fromAsyncIter, fromPromise, keepalive, reactiveMap } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\nimport { aiMeta, isNodeLike } from \"../_internal.js\";\nimport type { ToolDefinition } from \"../adapters/core/types.js\";\n\n// ---------------------------------------------------------------------------\n// toolRegistry\n// ---------------------------------------------------------------------------\n\nexport type ToolRegistryOptions = {\n\tgraph?: GraphOptions;\n};\n\n/**\n * `ToolRegistryGraph` — name-keyed registry of {@link ToolDefinition}s.\n *\n * **Reactive-only execution.** The only execution path is\n * {@link executeReactive}, which returns a `Node<unknown>` for the handler\n * result. Composing factories (`toolExecution`, `agentLoop`) consume it\n * directly inside `retrySource` / `switchMap` chains. There is intentionally\n * no imperative `execute()` Promise method — the registry was originally a\n * dual-boundary class (imperative + reactive) and the imperative path was\n * the only thing in the codebase bridging through `Promise.resolve().then()`\n * to feed `fromAny`. Removing it left every consumer on a single\n * reactive-all-the-way path with real abort propagation.\n *\n * For non-reactive callers (debug scripts, one-shot tests), bridge with\n * `awaitSettled(toolRegistry.executeReactive(name, args))`.\n *\n * **Wave A Unit 6 refactor:** internal storage migrated from `state<Map>`\n * (O(N) Map-copy per mutation) to `ReactiveMapBundle<string, ToolDefinition>`\n * (O(1) mutations + version counter).\n */\nexport class ToolRegistryGraph extends Graph {\n\treadonly definitions: Node<ReadonlyMap<string, ToolDefinition>>;\n\treadonly schemas: Node<readonly ToolDefinition[]>;\n\tprivate readonly _bundle: ReturnType<typeof reactiveMap<string, ToolDefinition>>;\n\n\tconstructor(name: string, opts: ToolRegistryOptions = {}) {\n\t\tsuper(name, opts.graph);\n\n\t\tthis._bundle = reactiveMap<string, ToolDefinition>({\n\t\t\tname: \"definitions\",\n\t\t});\n\t\tthis.definitions = this._bundle.entries;\n\t\tthis.add(this.definitions, { name: \"definitions\" });\n\n\t\tthis.schemas = node<readonly ToolDefinition[]>(\n\t\t\t[this.definitions],\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\tconst defs = data[0];\n\t\t\t\tactions.emit([...((defs ?? new Map()) as ReadonlyMap<string, ToolDefinition>).values()]);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"schemas\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: aiMeta(\"tool_schemas\"),\n\t\t\t\tinitial: [],\n\t\t\t},\n\t\t);\n\t\tthis.add(this.schemas, { name: \"schemas\" });\n\t\tthis.addDisposer(keepalive(this.schemas));\n\t}\n\n\tregister(tool: ToolDefinition): void {\n\t\tthis._bundle.set(tool.name, tool);\n\t}\n\n\tunregister(name: string): void {\n\t\tthis._bundle.delete(name);\n\t}\n\n\t/**\n\t * Reactive execution — returns a `Node<unknown>` that emits the handler\n\t * result. The returned node is a `producer` that:\n\t *\n\t * 1. Mints a per-call `AbortController` whose `signal` is threaded into\n\t * the handler call AND into `fromAny` (so a `fromPromise` /\n\t * `fromAsyncIter` inner abandons cleanly when the consumer\n\t * unsubscribes).\n\t * 2. Runs `tool.handler(args, {signal})` inside a try/catch — a\n\t * synchronous throw surfaces as `[[ERROR, err]]` downstream instead\n\t * of escaping the producer.\n\t * 3. Forwards every message from the inner `fromAny` chain to the\n\t * producer's outputs.\n\t * 4. On teardown (subscriber count drops to zero, e.g. `switchMap`\n\t * supersede) calls `ac.abort()` and unsubscribes the inner.\n\t * Signal-aware handlers (e.g. `fetch(url, {signal})`) actually stop.\n\t *\n\t * Each call mints a fresh node tied to a fresh `handler(args, ...)`\n\t * invocation — call `executeReactive` again for repeated invocations.\n\t *\n\t * @throws `Error` synchronously when `name` is not registered (no node is\n\t * constructed — the caller gets a pre-wiring failure rather than a\n\t * silent ERROR wave on an empty graph).\n\t */\n\texecuteReactive(name: string, args: Record<string, unknown>): Node<unknown> {\n\t\tconst tool = this._bundle.get(name);\n\t\tif (!tool) throw new Error(`toolRegistry: unknown tool \"${name}\"`);\n\t\treturn node<unknown>(\n\t\t\t[],\n\t\t\t(_data, actions) => {\n\t\t\t\tconst ac = new AbortController();\n\t\t\t\tlet inner: Node<unknown>;\n\t\t\t\ttry {\n\t\t\t\t\tconst raw = tool.handler(args, { signal: ac.signal });\n\t\t\t\t\tinner = handlerResultToNode(raw, ac.signal);\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// Synchronous throw from handler → ERROR. Producer cleanup\n\t\t\t\t\t// still aborts the controller for symmetry (no-op if no\n\t\t\t\t\t// signal listeners attached).\n\t\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\t\tac.abort();\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tconst unsub = inner.subscribe((batch) => {\n\t\t\t\t\tactions.down(batch as Messages);\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\tac.abort();\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: `executeReactive::${name}`,\n\t\t\t\tdescribeKind: \"producer\",\n\t\t\t\tmeta: aiMeta(\"tool_execute_reactive\"),\n\t\t\t},\n\t\t);\n\t}\n\n\tgetDefinition(name: string): ToolDefinition | undefined {\n\t\t// Pure read via the snapshot cache — avoids the bundle's\n\t\t// `wrapMutation` path (which would run the version-bump check and\n\t\t// any configured retention eviction on every lookup). Safe because\n\t\t// `getDefinition` is a boundary API, not a reactive fn body.\n\t\treturn this._bundle.entries.cache?.get(name);\n\t}\n}\n\nexport function toolRegistry(name: string, opts?: ToolRegistryOptions): ToolRegistryGraph {\n\treturn new ToolRegistryGraph(name, opts);\n}\n\n/**\n * Coerce a tool handler return value into a `Node<unknown>`.\n *\n * Differs from `fromAny` by treating **strings, arrays, plain iterables, and\n * scalar objects as single DATA values** rather than iterating them. A tool\n * handler that returns `\"hello world\"` should surface as one `DATA(\"hello\n * world\")`, not 11 `DATA` events of single characters; an array `[1, 2, 3]`\n * should surface as `DATA([1, 2, 3])`, not three separate emissions.\n *\n * Reactive shapes (Node, Promise, AsyncIterable) are unwrapped as expected.\n *\n * @internal\n */\nfunction handlerResultToNode(raw: unknown, signal: AbortSignal): Node<unknown> {\n\tif (isNodeLike(raw)) {\n\t\treturn raw as Node<unknown>;\n\t}\n\tif (raw != null && typeof (raw as PromiseLike<unknown>).then === \"function\") {\n\t\treturn fromPromise(raw as PromiseLike<unknown>, { signal });\n\t}\n\tif (raw != null && typeof raw === \"object\" && Symbol.asyncIterator in (raw as object)) {\n\t\treturn fromAsyncIter(raw as AsyncIterable<unknown>, { signal });\n\t}\n\t// String, number, boolean, null, undefined, plain object, array,\n\t// sync iterable — treat as a single DATA value via a resolved Promise so\n\t// `fromPromise`'s scalar-DATA-emit + COMPLETE semantics match the\n\t// pre-refactor `tools.execute` behavior (which always wrapped via async).\n\treturn fromPromise(Promise.resolve(raw), { signal });\n}\n","// ---------------------------------------------------------------------------\n// memory composers — Unit 7 C-factoring (2026-04-23 doc decision).\n//\n// Each composer attaches one capability (vectors, KG, tiers, retrieval) to a\n// `DistillBundle`. `agentMemory` continues to ship as the ergonomic sugar\n// over the full pipeline; power users who want a subset call these factories\n// directly.\n//\n// Class B audit (2026-04-30): the composers were migrated from\n// bundle-returning factories to **Graph subclasses** so they participate in\n// `describe()` / `destroy()` like every other Phase 4+ Graph (mirrors\n// `AuditTrailGraph`, `PolicyGateGraph`, `CqrsGraph`). The factory functions\n// remain as ergonomic constructors (`memoryWithVectors(opts) → MemoryWithVectorsGraph`).\n//\n// Tier 4.1 B + 4.3 B (2026-04-29): `memoryWithTiers` is the construction site\n// for the distill bundle when tiers are configured (`reactiveMap.retention`\n// wired at construction eliminates the §7 feedback cycle the prior\n// `tierClassifier` effect carried). `permanentKeys` and `entryCreatedAtNs`\n// are reactive maps mounted on the graph (not closure state) so\n// `describe()`/`explain()` can walk to the inputs that fed an archival\n// decision.\n// ---------------------------------------------------------------------------\n\nimport { batch, DATA, monotonicNs, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport type { StorageHandle } from \"@graphrefly/pure-ts/extra\";\nimport {\n\tfromAny,\n\tkeepalive,\n\ttype NodeInput,\n\ttype ReactiveMapBundle,\n\ttype ReactiveMapRetention,\n\treactiveMap,\n} from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\nimport {\n\ttype DistillBundle,\n\ttype DistillOptions,\n\tdistill,\n\ttype Extraction,\n} from \"../../../base/composition/distill.js\";\nimport { decay } from \"../../../base/utils/decay.js\";\nimport {\n\tcollection,\n\tcosineSimilarity,\n\ttype KnowledgeEdge,\n\ttype KnowledgeGraph,\n\tknowledgeGraph,\n\ttype VectorIndexGraph,\n\ttype VectorRecord,\n\ttype VectorSearchResult,\n\tvectorIndex,\n} from \"../../memory/index.js\";\nimport { aiMeta } from \"../_internal.js\";\nimport type { RetrievalEntry, RetrievalQuery, RetrievalTrace } from \"./retrieval.js\";\nimport {\n\tDEFAULT_DECAY_RATE,\n\ttype MemoryTier,\n\ttype MemoryTiersBundle,\n\ttype MemoryTiersOptions,\n} from \"./tiers.js\";\n\n// Tier 4.7 (Wave AM Unit 5 carry): the pre-rebuild defensive `extractStoreMap`\n// helper (runtime `instanceof Map` check before casting) was deleted in favor\n// of a typed `as` cast at each callsite. The upstream `ReactiveMapBundle`\n// always emits a real Map on the live emit path; non-Map snapshots only\n// surface on `Graph.restore` from a codec that round-tripped Map → JSON →\n// plain object (handled in `extra/composite.ts:mapFromSnapshot` for distill\n// internals). Empty map is the canonical \"no entries yet\" value —\n// `node([], { initial: undefined })` would stall a derived/effect's first-run gate.\n//\n// qa F3 (deferred): the typed cast lies if upstream contract ever breaks\n// (e.g. `entries` emits a non-Map non-undefined value). Failure mode is a\n// TypeError at iteration instead of a silent empty-map fallback —\n// deliberate trade-off, surfacing real upstream-contract violations beats\n// hiding them. Upstream-narrowing follow-up filed in `docs/optimizations.md`\n// under \"Tier 4.7 follow-up — narrow `ReactiveMapBundle.entries` callback typing\".\n\n// ---------------------------------------------------------------------------\n// memoryWithVectors\n// ---------------------------------------------------------------------------\n\nexport interface MemoryWithVectorsOptions<TMem> {\n\t/** Optional Graph identity — passed through to the underlying `Graph` ctor. */\n\tgraph?: GraphOptions;\n\t/** Subgraph name. Default: `\"memory-vectors\"`. */\n\tname?: string;\n\t/** The substrate distill store to index. */\n\tstore: DistillBundle<TMem>;\n\t/** Embedding dimension. Must match the `embedFn` output length. */\n\tdimension: number;\n\t/** Extract an embedding vector for a memory entry. */\n\tembedFn: (mem: TMem) => readonly number[] | undefined;\n}\n\n/**\n * Graph subclass that attaches a vector index to a `DistillBundle`. The inner\n * `VectorIndexGraph` is mounted at `\"vectorIndex\"`; an internal effect\n * subscribes to the substrate store and re-indexes on every change.\n *\n * Mirrors `AuditTrailGraph` / `PolicyGateGraph` shape — fully self-contained,\n * teardown via the Graph's `destroy()` cascade.\n */\nexport class MemoryWithVectorsGraph<TMem> extends Graph {\n\treadonly vectors: VectorIndexGraph<TMem>;\n\n\tconstructor(opts: MemoryWithVectorsOptions<TMem>) {\n\t\tsuper(opts.name ?? \"memory-vectors\", opts.graph);\n\t\tthis.vectors = vectorIndex<TMem>({ dimension: opts.dimension });\n\t\tthis.mount(\"vectorIndex\", this.vectors);\n\n\t\tconst embedFn = opts.embedFn;\n\t\tconst vectorsRef = this.vectors;\n\n\t\t// Indexer effect — subscribes to the substrate's store entries, upserts\n\t\t// vectors. Pure side-effect; restricted `effect` fn (no emit/down).\n\t\t// Cross-graph dep on `opts.store.store.entries` is fine — the substrate\n\t\t// is the upstream wired in by the parent factory.\n\t\tconst indexer = node(\n\t\t\t[opts.store.store.entries],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst data = batchData.map((b, i) =>\n\t\t\t\t\tb != null && b.length > 0 ? b.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst storeMap =\n\t\t\t\t\t(data[0] as ReadonlyMap<string, TMem> | undefined) ?? new Map<string, TMem>();\n\t\t\t\tfor (const [key, mem] of storeMap) {\n\t\t\t\t\tconst vec = embedFn(mem);\n\t\t\t\t\tif (vec) vectorsRef.upsert(key, vec, mem);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ name: \"indexer\", describeKind: \"effect\" },\n\t\t);\n\t\tthis.add(indexer, { name: \"indexer\" });\n\t\tthis.addDisposer(keepalive(indexer));\n\t}\n}\n\n/**\n * Attach a vector index to a `DistillBundle`. Indexes every entry in the\n * store as it changes. Returns the `MemoryWithVectorsGraph` whose `vectors`\n * field exposes the underlying `VectorIndexGraph`.\n *\n * Teardown is handled by `Graph.destroy()` — typically inherited via\n * mounting the result on a parent graph (see `agentMemory`).\n */\nexport function memoryWithVectors<TMem>(\n\topts: MemoryWithVectorsOptions<TMem>,\n): MemoryWithVectorsGraph<TMem> {\n\treturn new MemoryWithVectorsGraph<TMem>(opts);\n}\n\n// ---------------------------------------------------------------------------\n// memoryWithKG\n// ---------------------------------------------------------------------------\n\nexport interface MemoryWithKGOptions<TMem> {\n\t/** Optional Graph identity. */\n\tgraph?: GraphOptions;\n\t/** Subgraph name. Default: `\"memory-kg\"`. */\n\tname?: string;\n\t/** The substrate distill store to index. */\n\tstore: DistillBundle<TMem>;\n\t/** Inner KnowledgeGraph name. Default: `${name}-kg`. */\n\tkgName?: string;\n\t/**\n\t * Mount path within this Graph for the KnowledgeGraph. Default:\n\t * `\"knowledge-kg\"` (B5c — symmetric with the outer `knowledge` mount so\n\t * describe paths render `knowledge::knowledge-kg::*`).\n\t */\n\tmountPath?: string;\n\t/**\n\t * Extract entities + relations for a memory entry. Omit to mount an empty\n\t * KG without an indexer effect — caller upserts entities / relations\n\t * directly on the `kg` field.\n\t */\n\tentityFn?: (\n\t\tkey: string,\n\t\tmem: TMem,\n\t) =>\n\t\t| {\n\t\t\t\tentities?: Array<{ id: string; value: unknown }>;\n\t\t\t\trelations?: Array<{ from: string; to: string; relation: string; weight?: number }>;\n\t\t }\n\t\t| undefined;\n}\n\n/**\n * Graph subclass that attaches a knowledge graph alongside a `DistillBundle`.\n * Mounts the inner `KnowledgeGraph` at `mountPath` (default `\"knowledge-kg\"`); when\n * `entityFn` is provided, an indexer effect populates entities/relations on\n * every store change.\n */\nexport class MemoryWithKGGraph<TMem> extends Graph {\n\treadonly kg: KnowledgeGraph<unknown, string>;\n\n\tconstructor(opts: MemoryWithKGOptions<TMem>) {\n\t\tconst name = opts.name ?? \"memory-kg\";\n\t\tsuper(name, opts.graph);\n\t\tconst kgName = opts.kgName ?? `${name}-kg`;\n\t\tconst mountPath = opts.mountPath ?? \"knowledge-kg\";\n\t\tthis.kg = knowledgeGraph<unknown, string>(kgName);\n\t\tthis.mount(mountPath, this.kg);\n\n\t\tif (!opts.entityFn) return;\n\t\tconst entityFn = opts.entityFn;\n\t\tconst kgRef = this.kg;\n\t\tconst indexer = node(\n\t\t\t[opts.store.store.entries],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst data = batchData.map((b, i) =>\n\t\t\t\t\tb != null && b.length > 0 ? b.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst storeMap =\n\t\t\t\t\t(data[0] as ReadonlyMap<string, TMem> | undefined) ?? new Map<string, TMem>();\n\t\t\t\tfor (const [key, mem] of storeMap) {\n\t\t\t\t\tconst extracted = entityFn(key, mem);\n\t\t\t\t\tif (!extracted) continue;\n\t\t\t\t\tfor (const ent of extracted.entities ?? []) {\n\t\t\t\t\t\tkgRef.upsertEntity(ent.id, ent.value);\n\t\t\t\t\t}\n\t\t\t\t\tfor (const rel of extracted.relations ?? []) {\n\t\t\t\t\t\tkgRef.link(rel.from, rel.to, rel.relation, rel.weight);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ name: \"indexer\", describeKind: \"effect\" },\n\t\t);\n\t\tthis.add(indexer, { name: \"indexer\" });\n\t\tthis.addDisposer(keepalive(indexer));\n\t}\n}\n\n/**\n * Attach a knowledge graph alongside a `DistillBundle`. Returns the\n * `MemoryWithKGGraph` whose `kg` field exposes the inner `KnowledgeGraph`.\n */\nexport function memoryWithKG<TMem>(opts: MemoryWithKGOptions<TMem>): MemoryWithKGGraph<TMem> {\n\treturn new MemoryWithKGGraph<TMem>(opts);\n}\n\n// ---------------------------------------------------------------------------\n// memoryWithTiers\n// ---------------------------------------------------------------------------\n\n/**\n * Full options for {@link memoryWithTiers} (Tier 4.1 B + 4.3 B refactor,\n * 2026-04-29). Combines tier-policy options with the distill-side options\n * needed to construct the underlying store — `memoryWithTiers` is the\n * **construction site** for the distill bundle so it can wire\n * `reactiveMap.retention` into the store at construction (eliminating the\n * §7 feedback cycle the previous `tierClassifier` effect carried).\n */\nexport type MemoryWithTiersOptions<TRaw, TMem> = MemoryTiersOptions<TMem> &\n\tOmit<DistillOptions<TMem>, \"mapOptions\" | \"score\" | \"context\"> & {\n\t\t/** Optional Graph identity. */\n\t\tgraph?: GraphOptions;\n\t\t/** Subgraph name. Default: `\"memory-tiers\"`. */\n\t\tname?: string;\n\t\t/** Raw source feeding distill. */\n\t\tsource: NodeInput<TRaw>;\n\t\t/** Reactive extraction wiring (same shape as `distill`). */\n\t\textractFn: (\n\t\t\traw: Node<TRaw>,\n\t\t\texisting: Node<ReadonlyMap<string, TMem>>,\n\t\t) => NodeInput<Extraction<TMem>>;\n\t\t/** Score function — same signature as `agentMemory.score`. */\n\t\tscore: (mem: TMem, context: unknown) => number;\n\t\t/** Optional reactive context node (passed to `score`). */\n\t\tcontext?: NodeInput<unknown>;\n\t};\n\n/**\n * Graph subclass attaching 3-tier storage (active / archived / permanent) to\n * a fresh distill store, wiring `reactiveMap.retention` at construction so\n * archival happens synchronously inside the substrate's mutation pipeline\n * (no §7 feedback cycle). Promotes `permanentKeys` and `entryCreatedAtNs` to\n * reactive maps registered on this graph (Tier 4.3 B — Unit 7 Q3) so\n * `describe()` / `explain()` can walk to \"why was X archived?\".\n *\n * Public-face fields:\n * - `store` — the distill bundle (construction site, exposed for downstream\n * composers).\n * - `tiers` — tier classification + permanent promotion handles.\n * - `compact`, `size` — alias for `store.compact` / `store.size` (registered\n * under their canonical names so `describe()` keys match `agentMemory`'s\n * pre-migration layout).\n */\nexport class MemoryWithTiersGraph<TRaw, TMem> extends Graph {\n\treadonly store: DistillBundle<TMem>;\n\treadonly tiers: MemoryTiersBundle<TMem>;\n\treadonly compact: Node<Array<{ key: string; value: TMem; score: number }>>;\n\treadonly size: Node<number>;\n\treadonly permanent: ReturnType<typeof collection<TMem>>;\n\treadonly permanentKeys: ReactiveMapBundle<string, true>;\n\treadonly entryCreatedAtNs: ReactiveMapBundle<string, number>;\n\n\tconstructor(opts: MemoryWithTiersOptions<TRaw, TMem>) {\n\t\tsuper(opts.name ?? \"memory-tiers\", opts.graph);\n\n\t\tconst decayRate = opts.decayRate ?? DEFAULT_DECAY_RATE;\n\t\tconst maxActive = opts.maxActive ?? 1000;\n\t\tconst archiveThreshold = opts.archiveThreshold ?? 0.1;\n\t\tconst permanentFilter = opts.permanentFilter ?? (() => false);\n\n\t\t// Tier 2.3 fold: `lightCollection` was merged into\n\t\t// `collection({ranked: false})`. The unified factory returns a Graph (not\n\t\t// a detached bundle), so it's mounted as a subgraph for `describe()`.\n\t\tthis.permanent = collection<TMem>(\"permanent\", { ranked: false });\n\t\tthis.mount(\"permanent\", this.permanent);\n\n\t\t// 4.3 B (Unit 7 Q3, 2026-04-29): closure-state promotion. `permanentKeys`\n\t\t// and `entryCreatedAtNs` are reactive maps registered on this graph so\n\t\t// `describe()` can walk to them and `explain()` can trace the inputs\n\t\t// that fed an archival decision.\n\t\tthis.permanentKeys = reactiveMap<string, true>({ name: \"permanentKeys\" });\n\t\tthis.add(this.permanentKeys.entries, { name: \"permanentKeys\" });\n\t\tthis.entryCreatedAtNs = reactiveMap<string, number>({ name: \"entryCreatedAtNs\" });\n\t\tthis.add(this.entryCreatedAtNs.entries, { name: \"entryCreatedAtNs\" });\n\n\t\t// Closure-mirror for ctx (§28 factory-time seed). `score(mem, ctx)` runs\n\t\t// inside `retention.score` which is invoked synchronously from store\n\t\t// mutations — no reactive dep on contextNode there. The mirror keeps\n\t\t// `latestCtx` current via subscribe.\n\t\t//\n\t\t// Topology visibility: the local-default branch registers the context\n\t\t// state node so it appears in `describe()`. The user-supplied-Node\n\t\t// branch deliberately leaves the node unregistered — `fromAny` returns\n\t\t// the caller's owned Node, which is owned by their graph; mounting it\n\t\t// here would corrupt cross-graph ownership.\n\t\tlet contextNode: Node<unknown>;\n\t\tif (opts.context) {\n\t\t\tcontextNode = fromAny(opts.context);\n\t\t} else {\n\t\t\tcontextNode = node<unknown>([], { initial: null });\n\t\t\tthis.add(contextNode, { name: \"context\" });\n\t\t}\n\t\tlet latestCtx: unknown = contextNode.cache;\n\t\tconst ctxUnsub = contextNode.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) if (m[0] === DATA) latestCtx = m[1];\n\t\t});\n\t\tthis.addDisposer(ctxUnsub);\n\n\t\tconst permanentKeysRef = this.permanentKeys;\n\t\tconst entryCreatedAtNsRef = this.entryCreatedAtNs;\n\t\tconst score = opts.score;\n\n\t\t// Build retention. `score` runs synchronously inside store mutations.\n\t\t// Permanent matches return Infinity to bypass eviction.\n\t\t//\n\t\t// DS-13.5.F (2026-05-01): `score` is read-only against\n\t\t// `entryCreatedAtNs` — the first-write side-effect was extracted into\n\t\t// the `entryCreatedAtNs/sync` effect below. Race window for the very\n\t\t// first call on a new key is mitigated by the `?? nowNs` fallback\n\t\t// (yields ageSeconds = 0, i.e. fresh-decay), and the sync effect\n\t\t// populates the map after the wave settles so subsequent score calls\n\t\t// see the persisted timestamp.\n\t\tconst retention: ReactiveMapRetention<string, TMem> = {\n\t\t\tscore: (key, value) => {\n\t\t\t\tif (permanentFilter(key, value)) return Number.POSITIVE_INFINITY;\n\t\t\t\tif (permanentKeysRef.has(key)) return Number.POSITIVE_INFINITY;\n\t\t\t\tconst nowNs = monotonicNs();\n\t\t\t\tconst createdNs = entryCreatedAtNsRef.get(key) ?? nowNs;\n\t\t\t\tconst ageSeconds = Number(nowNs - createdNs) / 1e9;\n\t\t\t\treturn decay(score(value, latestCtx), ageSeconds, decayRate);\n\t\t\t},\n\t\t\tarchiveThreshold,\n\t\t\tmaxSize: maxActive,\n\t\t};\n\n\t\t// Construct distill with retention wired into mapOptions.\n\t\tthis.store = distill<TRaw, TMem>(opts.source, opts.extractFn, {\n\t\t\tscore: opts.score,\n\t\t\tcost: opts.cost,\n\t\t\t...(opts.budget !== undefined ? { budget: opts.budget } : {}),\n\t\t\t...(opts.evict !== undefined ? { evict: opts.evict } : {}),\n\t\t\t...(opts.consolidate !== undefined ? { consolidate: opts.consolidate } : {}),\n\t\t\t...(opts.consolidateTrigger !== undefined\n\t\t\t\t? { consolidateTrigger: opts.consolidateTrigger }\n\t\t\t\t: {}),\n\t\t\t...(opts.context !== undefined ? { context: opts.context } : {}),\n\t\t\tmapOptions: { retention },\n\t\t});\n\n\t\t// Register the distill bundle's exposed nodes under their canonical\n\t\t// names so consumers (and `describe()`) see the same shape as the\n\t\t// pre-migration top-level surface on `agentMemory`.\n\t\tthis.add(this.store.store.entries, { name: \"store\" });\n\t\tthis.compact = this.store.compact;\n\t\tthis.add(this.compact, { name: \"compact\" });\n\t\tthis.size = this.store.size;\n\t\tthis.add(this.size, { name: \"size\" });\n\n\t\tconst storeRef = this.store;\n\t\tconst tierOf = (key: string): MemoryTier => {\n\t\t\tif (permanentKeysRef.has(key)) return \"permanent\";\n\t\t\tconst m =\n\t\t\t\t(storeRef.store.entries.cache as ReadonlyMap<string, TMem> | undefined) ??\n\t\t\t\tnew Map<string, TMem>();\n\t\t\tif (m.has(key)) return \"active\";\n\t\t\treturn \"archived\";\n\t\t};\n\t\tconst permanentRef = this.permanent;\n\t\tconst markPermanent = (key: string, value: TMem): void => {\n\t\t\tpermanentKeysRef.set(key, true);\n\t\t\tpermanentRef.upsert(key, value);\n\t\t};\n\n\t\t// DS-13.5.F (2026-05-01): first-write of `entryCreatedAtNs[key]` runs\n\t\t// here (extracted from `retention.score` to keep score pure). Reads\n\t\t// `store.store.entries`, writes `entryCreatedAtNs` — distinct nodes,\n\t\t// no §7 feedback cycle. Idempotent: re-emissions for already-tracked\n\t\t// keys skip via `entryCreatedAtNsRef.has(key)`.\n\t\tconst syncCreatedAt = node(\n\t\t\t[this.store.store.entries],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst data = batchData.map((b, i) =>\n\t\t\t\t\tb != null && b.length > 0 ? b.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst map = (data[0] as ReadonlyMap<string, TMem> | undefined) ?? new Map<string, TMem>();\n\t\t\t\tconst nowNs = monotonicNs();\n\t\t\t\tconst toAdd: string[] = [];\n\t\t\t\tfor (const key of map.keys()) {\n\t\t\t\t\tif (!entryCreatedAtNsRef.has(key)) toAdd.push(key);\n\t\t\t\t}\n\t\t\t\tif (toAdd.length > 0) {\n\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\tfor (const key of toAdd) entryCreatedAtNsRef.set(key, nowNs);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ name: \"entryCreatedAtNs/sync\", describeKind: \"effect\" },\n\t\t);\n\t\tthis.add(syncCreatedAt, { name: \"entryCreatedAtNs/sync\" });\n\t\tthis.addDisposer(keepalive(syncCreatedAt));\n\n\t\t// GC entryCreatedAtNs entries that no longer exist in the active store.\n\t\t// (Adds happen via the syncCreatedAt effect above; removals piggyback\n\t\t// on the store-snapshot subscriber here so the map stays in sync.)\n\t\tconst entriesUnsub = this.store.store.entries.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\tconst map = m[1] as ReadonlyMap<string, TMem>;\n\t\t\t\tconst created = entryCreatedAtNsRef.entries.cache as\n\t\t\t\t\t| ReadonlyMap<string, number>\n\t\t\t\t\t| undefined;\n\t\t\t\tif (created == null) continue;\n\t\t\t\tconst toDelete: string[] = [];\n\t\t\t\tfor (const key of created.keys()) {\n\t\t\t\t\tif (!map.has(key)) toDelete.push(key);\n\t\t\t\t}\n\t\t\t\tif (toDelete.length > 0) {\n\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\tfor (const key of toDelete) entryCreatedAtNsRef.delete(key);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.addDisposer(entriesUnsub);\n\n\t\t// Permanent-promotion effect. Writes to `permanent` collection +\n\t\t// `permanentKeys` (NOT to the active store), so no §7 cycle: the effect's\n\t\t// dep is `store.store.entries`, but it doesn't write back to that node.\n\t\tconst promoter = node(\n\t\t\t[this.store.store.entries],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst data = batchData.map((b, i) =>\n\t\t\t\t\tb != null && b.length > 0 ? b.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst map = (data[0] as ReadonlyMap<string, TMem> | undefined) ?? new Map<string, TMem>();\n\t\t\t\tfor (const [key, mem] of map) {\n\t\t\t\t\tif (permanentKeysRef.has(key)) continue;\n\t\t\t\t\tif (permanentFilter(key, mem)) {\n\t\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\t\tmarkPermanent(key, mem);\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\t{ name: \"promoter\", describeKind: \"effect\" },\n\t\t);\n\t\tthis.add(promoter, { name: \"promoter\" });\n\t\tthis.addDisposer(keepalive(promoter));\n\n\t\tlet archiveHandle: StorageHandle | null = null;\n\t\tif (opts.archiveTier) {\n\t\t\tarchiveHandle = this.attachSnapshotStorage(\n\t\t\t\t[{ snapshot: opts.archiveTier }],\n\t\t\t\topts.archiveStorageOptions ?? {},\n\t\t\t);\n\t\t\tthis.addDisposer(() => archiveHandle?.dispose());\n\t\t}\n\n\t\tthis.tiers = {\n\t\t\tpermanent: this.permanent,\n\t\t\tactiveEntries: this.store.store.entries,\n\t\t\tarchiveHandle,\n\t\t\ttierOf,\n\t\t\tmarkPermanent,\n\t\t};\n\t}\n}\n\n/**\n * Attach 3-tier storage (active / archived / permanent) over a fresh distill\n * store. Returns a `MemoryWithTiersGraph` whose `store` and `tiers` fields\n * mirror the previous bundle shape.\n *\n * **API shape** (Class B audit, 2026-04-30 — breaking change vs.\n * pre-migration): the factory takes a single opts bag including `source`\n * and `extractFn`. The bundle is exposed as `result.store` for downstream\n * composers (vectors / KG / retrieval).\n *\n * - `permanentFilter`-matching entries score `Infinity` in retention →\n * never archived. Independent permanent-promotion effect upserts them\n * into the `permanent` collection.\n * - Below-threshold entries → retention archives synchronously.\n * - Over-`maxActive` entries → retention's `maxSize` evicts lowest-scored.\n */\nexport function memoryWithTiers<TRaw, TMem>(\n\topts: MemoryWithTiersOptions<TRaw, TMem>,\n): MemoryWithTiersGraph<TRaw, TMem> {\n\treturn new MemoryWithTiersGraph<TRaw, TMem>(opts);\n}\n\n// ---------------------------------------------------------------------------\n// memoryRetrieval\n// ---------------------------------------------------------------------------\n\nexport interface MemoryRetrievalOptions<TMem> {\n\t/** Optional Graph identity. */\n\tgraph?: GraphOptions;\n\t/** Subgraph name. Default: `\"memory-retrieval\"`. */\n\tname?: string;\n\t/** The substrate distill store. */\n\tstore: DistillBundle<TMem>;\n\t/** Optional vector index for similarity search. */\n\tvectors?: VectorIndexGraph<TMem> | null;\n\t/** Optional knowledge graph for entity-relation expansion. */\n\tkg?: KnowledgeGraph<unknown, string> | null;\n\t/** Score function (same shape as `agentMemory.score`). */\n\tscore: (mem: TMem, context: unknown) => number;\n\t/** Cost function for budget packing. */\n\tcost: (mem: TMem) => number;\n\t/** Token / cost budget. Default 2000. */\n\tbudget?: number;\n\t/** Top-K vector candidates. Default 20. */\n\ttopK?: number;\n\t/** KG expansion depth in hops. Default 1. */\n\tgraphDepth?: number;\n\t/** Hierarchical-context boost weight. Default 0. */\n\tcontextWeight?: number;\n\t/** Hierarchical-context accessor for entries. */\n\tcontextOf?: (mem: TMem) => readonly string[] | undefined;\n\t/** Optional reactive context node (passed to `score`). */\n\tcontext?: NodeInput<unknown>;\n}\n\nfunction sharedPrefixDepth(\n\tq: readonly string[] | undefined,\n\te: readonly string[] | undefined,\n): number {\n\tif (!q || !e) return 0;\n\tconst n = Math.min(q.length, e.length);\n\tlet i = 0;\n\twhile (i < n && q[i] === e[i]) i++;\n\treturn i;\n}\n\n// QA-fix: element-wise reference-equality dedup so subscribers don't wake\n// up when an identical packed array lands (runRetrieval allocates a new\n// outer array reference every call).\nconst packedEquals = <T>(a: readonly T[], b: readonly T[]): boolean => {\n\tif (a === b) return true;\n\tif (a.length !== b.length) return false;\n\tfor (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n\treturn true;\n};\n\n/**\n * Graph subclass that builds the retrieval pipeline (vector + KG + budget\n * packing) over a `DistillBundle` and optional vectors / kg substrates.\n *\n * **C1 rework (2026-04-30):** retrieval is reactive-only. Each\n * `retrieveReactive(input)` call constructs its own per-input subgraph\n * mounted at `retrieve_${id}` with named nodes `context`, `result`, and\n * `projection`. Subgraphs register their own scoped disposers so teardown\n * is local to the per-call mount.\n *\n * **QA F-9 (2026-04-30):** the shared `retrieval` / `retrievalTrace`\n * state-node mirrors are dropped — they were last-writer-wins under\n * concurrent `retrieveReactive(...)` calls. Consumers must subscribe to\n * the per-call `projection` node directly. One-shot consumers use\n * `awaitSettled(retrieveReactive(input))`.\n *\n * **QA F-6 (2026-04-30):** the per-call `result` derived declares\n * `vectors.entries` / `kg.adjacencyOut` / `kg.adjacencyIn` as deps when\n * configured, so a vector upsert / KG mutation re-runs retrieval even\n * when the query / context / store-snapshot are unchanged. Resolves the\n * §28 closure-mirror gap where these `.cache` reads were undeclared.\n */\nexport class MemoryRetrievalGraph<TMem> extends Graph {\n\tprivate readonly _store: DistillBundle<TMem>;\n\tprivate readonly _vectors: VectorIndexGraph<TMem> | null;\n\tprivate readonly _kg: KnowledgeGraph<unknown, string> | null;\n\tprivate readonly _opts: MemoryRetrievalOptions<TMem>;\n\tprivate readonly _contextNode: Node<unknown>;\n\tprivate readonly _topK: number;\n\tprivate readonly _graphDepth: number;\n\tprivate readonly _budget: number;\n\tprivate readonly _contextWeight: number;\n\tprivate _retrieveSeq = 0;\n\n\tconstructor(opts: MemoryRetrievalOptions<TMem>) {\n\t\tsuper(opts.name ?? \"memory-retrieval\", opts.graph);\n\n\t\tthis._store = opts.store;\n\t\tthis._vectors = opts.vectors ?? null;\n\t\tthis._kg = opts.kg ?? null;\n\t\tthis._opts = opts;\n\t\tthis._topK = opts.topK ?? 20;\n\t\tthis._graphDepth = opts.graphDepth ?? 1;\n\t\tthis._budget = opts.budget ?? 2000;\n\t\tthis._contextWeight = opts.contextWeight ?? 0;\n\t\t// DS-13.5.C: synthesized branch (no `opts.context` supplied) registers\n\t\t// on this graph as `_context` so describe()/explain() can walk to it.\n\t\t// User-supplied branch stays unregistered — `fromAny` returns the\n\t\t// caller's owned Node, which is owned by their graph; mounting it\n\t\t// here would corrupt cross-graph ownership (mirrors MemoryWithTiers's\n\t\t// context-branch policy).\n\t\tif (opts.context) {\n\t\t\tthis._contextNode = fromAny(opts.context);\n\t\t} else {\n\t\t\tthis._contextNode = this.state<unknown>(\"_context\", null);\n\t\t}\n\t}\n\n\tprivate _runRetrieval(\n\t\tstoreMap: ReadonlyMap<string, TMem>,\n\t\tctx: unknown,\n\t\tquery: RetrievalQuery,\n\t): { packed: RetrievalEntry<TMem>[]; trace: RetrievalTrace<TMem> } {\n\t\tconst opts = this._opts;\n\t\tconst candidateMap = new Map<\n\t\t\tstring,\n\t\t\t{ value: TMem; sources: Set<\"vector\" | \"graph\" | \"store\"> }\n\t\t>();\n\n\t\tlet vectorCandidates: VectorSearchResult<TMem>[] = [];\n\t\tif (this._vectors && query.vector) {\n\t\t\t// Wave A migrated `vectorIndex` to a reactive-only read API\n\t\t\t// (`searchNode`); inline the equivalent flat-cosine snapshot scan\n\t\t\t// here since `_runRetrieval` is sync and `searchNode` is async-shaped.\n\t\t\t// `patterns/ai/memory/` is queued for its own audit per the Wave A\n\t\t\t// session doc § D.1.\n\t\t\tconst q = query.vector;\n\t\t\tconst snapshot = this._vectors.entries.cache as\n\t\t\t\t| ReadonlyMap<string, VectorRecord<TMem>>\n\t\t\t\t| undefined;\n\t\t\tif (snapshot && snapshot.size > 0 && this._topK > 0) {\n\t\t\t\tconst scored = [...snapshot.values()]\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(row): VectorSearchResult<TMem> => ({\n\t\t\t\t\t\t\tid: row.id,\n\t\t\t\t\t\t\tscore: cosineSimilarity(q, row.vector),\n\t\t\t\t\t\t\t...(row.meta !== undefined ? { meta: row.meta } : {}),\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.sort((a, b) => b.score - a.score)\n\t\t\t\t\t.slice(0, this._topK);\n\t\t\t\tvectorCandidates = scored;\n\t\t\t\tfor (const vc of vectorCandidates) {\n\t\t\t\t\tconst mem = storeMap.get(vc.id);\n\t\t\t\t\tif (mem) candidateMap.set(vc.id, { value: mem, sources: new Set([\"vector\"]) });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst graphExpanded: string[] = [];\n\t\tif (this._kg) {\n\t\t\t// Wave A migrated `knowledgeGraph` to a reactive-only `relatedNode`\n\t\t\t// API; inline the equivalent adjacency-snapshot scan here for the\n\t\t\t// sync expansion. `adjacencyOut` / `adjacencyIn` are kept warm by\n\t\t\t// the kg's own internal keepalive disposers, so `.cache` is always\n\t\t\t// populated post-construction.\n\t\t\tconst adjOut = this._kg.adjacencyOut.cache as\n\t\t\t\t| ReadonlyMap<string, readonly KnowledgeEdge<string>[]>\n\t\t\t\t| undefined;\n\t\t\tconst adjIn = this._kg.adjacencyIn.cache as\n\t\t\t\t| ReadonlyMap<string, readonly KnowledgeEdge<string>[]>\n\t\t\t\t| undefined;\n\t\t\tconst seedIds = [...(query.entityIds ?? []), ...[...candidateMap.keys()]];\n\t\t\tconst visited = new Set<string>();\n\t\t\tlet frontier = seedIds;\n\t\t\tfor (let depth = 0; depth < this._graphDepth; depth++) {\n\t\t\t\tconst nextFrontier: string[] = [];\n\t\t\t\tfor (const id of frontier) {\n\t\t\t\t\tif (visited.has(id)) continue;\n\t\t\t\t\tvisited.add(id);\n\t\t\t\t\tconst outEdges = adjOut?.get(id) ?? [];\n\t\t\t\t\tconst inEdges = adjIn?.get(id) ?? [];\n\t\t\t\t\tfor (const edge of outEdges) {\n\t\t\t\t\t\tconst targetId = edge.to;\n\t\t\t\t\t\tif (!visited.has(targetId)) {\n\t\t\t\t\t\t\tnextFrontier.push(targetId);\n\t\t\t\t\t\t\tconst mem = storeMap.get(targetId);\n\t\t\t\t\t\t\tif (mem) {\n\t\t\t\t\t\t\t\tconst existing = candidateMap.get(targetId);\n\t\t\t\t\t\t\t\tif (existing) existing.sources.add(\"graph\");\n\t\t\t\t\t\t\t\telse candidateMap.set(targetId, { value: mem, sources: new Set([\"graph\"]) });\n\t\t\t\t\t\t\t\tgraphExpanded.push(targetId);\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// Inbound edges: traverse to the `from` side. Match the\n\t\t\t\t\t// previous `kg.related(id)` semantics, which returned both\n\t\t\t\t\t// `from === id` and `to === id` matches.\n\t\t\t\t\tfor (const edge of inEdges) {\n\t\t\t\t\t\tconst targetId = edge.from;\n\t\t\t\t\t\tif (!visited.has(targetId)) {\n\t\t\t\t\t\t\tnextFrontier.push(targetId);\n\t\t\t\t\t\t\tconst mem = storeMap.get(targetId);\n\t\t\t\t\t\t\tif (mem) {\n\t\t\t\t\t\t\t\tconst existing = candidateMap.get(targetId);\n\t\t\t\t\t\t\t\tif (existing) existing.sources.add(\"graph\");\n\t\t\t\t\t\t\t\telse candidateMap.set(targetId, { value: mem, sources: new Set([\"graph\"]) });\n\t\t\t\t\t\t\t\tgraphExpanded.push(targetId);\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\tfrontier = nextFrontier;\n\t\t\t}\n\t\t}\n\t\tfor (const [key, mem] of storeMap) {\n\t\t\tif (!candidateMap.has(key)) {\n\t\t\t\tcandidateMap.set(key, { value: mem, sources: new Set([\"store\"]) });\n\t\t\t}\n\t\t}\n\n\t\tconst qDepth = query.context?.length ?? 0;\n\t\tconst ranked: RetrievalEntry<TMem>[] = [];\n\t\tfor (const [key, { value, sources }] of candidateMap) {\n\t\t\tconst entryContext = opts.contextOf ? opts.contextOf(value) : undefined;\n\t\t\tlet score = opts.score(value, ctx);\n\t\t\tif (this._contextWeight > 0 && qDepth > 0) {\n\t\t\t\tconst shared = sharedPrefixDepth(query.context, entryContext);\n\t\t\t\tif (shared > 0) score = score * (1 + (this._contextWeight * shared) / qDepth);\n\t\t\t}\n\t\t\tconst entry: RetrievalEntry<TMem> = entryContext\n\t\t\t\t? { key, value, score, sources: [...sources], context: entryContext }\n\t\t\t\t: { key, value, score, sources: [...sources] };\n\t\t\tranked.push(entry);\n\t\t}\n\t\tranked.sort((a, b) => b.score - a.score);\n\n\t\tconst packed: RetrievalEntry<TMem>[] = [];\n\t\tlet usedBudget = 0;\n\t\tfor (const entry of ranked) {\n\t\t\tconst c = opts.cost(entry.value);\n\t\t\tif (usedBudget + c > this._budget && packed.length > 0) break;\n\t\t\tpacked.push(entry);\n\t\t\tusedBudget += c;\n\t\t}\n\n\t\treturn { packed, trace: { vectorCandidates, graphExpanded, ranked, packed } };\n\t}\n\n\t/**\n\t * Reactive consumer API — chain into the graph.\n\t *\n\t * Each call constructs its own per-input subgraph mounted at\n\t * `retrieve_${id}` (auto-incrementing within this MemoryRetrievalGraph\n\t * instance) with named nodes:\n\t *\n\t * - `context` — `fromAny(queryInput)` projection (so the input node is\n\t * visible to `describe()` even when callers pass a raw value).\n\t * - `result` — pure derived `{ packed, trace }`.\n\t * - `projection` — the packed-array node returned to the caller.\n\t *\n\t * `result` declares the substrate's `store.entries`, the optional\n\t * `context` Node, the local `context` projection, and (when configured)\n\t * `vectors.entries` / `kg.adjacencyOut` / `kg.adjacencyIn` as deps —\n\t * so vector upserts and KG mutations re-trigger retrieval even when\n\t * the input is unchanged.\n\t *\n\t * **Lifecycle contract (DS-13.5.C, 2026-05-01).** The per-call subgraph\n\t * stays mounted while the returned `projection` has at least one\n\t * subscriber. When the last subscriber unsubscribes, projection's\n\t * `deactivate` cleanup hook fires (canonical \"last unsubscribe\" signal\n\t * via the existing `NodeFnCleanup.onDeactivation` protocol), which calls\n\t * `parent.remove(retrieve_${id})` and tears the per-call topology\n\t * down via TEARDOWN cascade (post-DS-13.5.A Q16, COMPLETE auto-precedes).\n\t *\n\t * **Single-shot lifecycle.** This auto-unmount is keyed to the FIRST\n\t * last-unsubscribe event — projection is non-resubscribable from the\n\t * caller's perspective. Callers who need to subscribe / unsubscribe /\n\t * re-subscribe should hold a long-lived subscription externally (e.g.\n\t * `keepalive(projection)`) or call `retrieveReactive(...)` again to\n\t * mount a fresh per-call subgraph.\n\t *\n\t * **Caller obligation.** Either subscribe to `projection` (and\n\t * eventually unsubscribe to trigger cleanup) OR drop the returned\n\t * reference without subscribing — in the no-subscribe case the\n\t * subgraph is dormant (no compute fires) and a parent `destroy()`\n\t * cascade reclaims it. Holding `projection` without subscribing AND\n\t * without ever destroying the parent is the leak case the JSDoc above\n\t * the C1 rework covers.\n\t *\n\t * One-shot callers use `awaitSettled(retrieveReactive(input))`.\n\t */\n\tretrieveReactive(\n\t\tqueryInput: NodeInput<RetrievalQuery | null>,\n\t): Node<ReadonlyArray<RetrievalEntry<TMem>>> {\n\t\tconst id = ++this._retrieveSeq;\n\t\tconst segment = `retrieve_${id}`;\n\n\t\t// Per-call subgraph — owns the wiring, the keepalive, and the\n\t\t// teardown. Mounted on `this` so it's visible in `describe()` and\n\t\t// reachable via `${parent}::retrieve_${id}::result` etc.\n\t\tconst sub = new Graph(segment);\n\n\t\t// Wrap the input as a local pass-through so the per-call subgraph\n\t\t// shows the query source in `describe()` regardless of where the\n\t\t// caller's node lives in the broader topology. `fromAny` returns\n\t\t// the original Node when given a Node, otherwise wraps a\n\t\t// value/promise into a producer.\n\t\t//\n\t\t// DS-13.5.C: registered via `sub.derived(...)` (Graph helper) for\n\t\t// equals plumbing + automatic registration; replaces the prior raw\n\t\t// `node([inputNode], fn) + sub.add(...)` shape.\n\t\tconst inputNode = fromAny(queryInput);\n\t\tconst localContext = sub.derived<RetrievalQuery | null>(\n\t\t\t\"context\",\n\t\t\t[inputNode],\n\t\t\t(batchData, ctx) => {\n\t\t\t\tconst data = batchData.map((b, i) =>\n\t\t\t\t\tb != null && b.length > 0 ? b.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\treturn [(data[0] as RetrievalQuery | null) ?? null];\n\t\t\t},\n\t\t\t{\n\t\t\t\tmeta: aiMeta(\"retrieval_query_input\"),\n\t\t\t\tinitial: null,\n\t\t\t},\n\t\t);\n\n\t\t// /qa F-6 (2026-04-30): declare vectors / kg substrate Node refs as\n\t\t// deps so vector upserts / KG mutations re-trigger retrieval even\n\t\t// when query / context / store snapshots are unchanged. The\n\t\t// `_runRetrieval` body reads `.cache` from these substrates; before\n\t\t// this fix those reads were undeclared §28 closure-mirrors.\n\t\tconst resultDeps: (string | Node<unknown>)[] = [\n\t\t\tthis._store.store.entries,\n\t\t\tthis._contextNode,\n\t\t\tlocalContext,\n\t\t];\n\t\tif (this._vectors) resultDeps.push(this._vectors.entries as Node<unknown>);\n\t\tif (this._kg) {\n\t\t\tresultDeps.push(this._kg.adjacencyOut as Node<unknown>);\n\t\t\tresultDeps.push(this._kg.adjacencyIn as Node<unknown>);\n\t\t}\n\n\t\t// DS-13.5.C: migrated to `sub.derived(...)` for equals plumbing +\n\t\t// automatic registration.\n\t\tconst result = sub.derived<{\n\t\t\tpacked: ReadonlyArray<RetrievalEntry<TMem>>;\n\t\t\ttrace: RetrievalTrace<TMem> | null;\n\t\t}>(\n\t\t\t\"result\",\n\t\t\tresultDeps,\n\t\t\t(batchData, ctx) => {\n\t\t\t\tconst data = batchData.map((b, i) =>\n\t\t\t\t\tb != null && b.length > 0 ? b.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst query = data[2];\n\t\t\t\tif (query == null) {\n\t\t\t\t\treturn [{ packed: [] as ReadonlyArray<RetrievalEntry<TMem>>, trace: null }];\n\t\t\t\t}\n\t\t\t\tconst storeMap =\n\t\t\t\t\t(data[0] as ReadonlyMap<string, TMem> | undefined) ?? new Map<string, TMem>();\n\t\t\t\tconst { packed, trace } = this._runRetrieval(storeMap, data[1], query as RetrievalQuery);\n\t\t\t\treturn [{ packed, trace }];\n\t\t\t},\n\t\t\t{\n\t\t\t\tmeta: aiMeta(\"retrieval_reactive_result\"),\n\t\t\t\tinitial: { packed: [] as ReadonlyArray<RetrievalEntry<TMem>>, trace: null },\n\t\t\t},\n\t\t);\n\n\t\t// DS-13.5.C: projection stays as raw `node()` (not `sub.derived`)\n\t\t// because the keepalive disposer is wired via the fn's\n\t\t// `NodeFnCleanup.onDeactivation` hook — projection's cleanup-on-last-\n\t\t// unsubscribe is what drives `parent.remove(segment)`. The Graph\n\t\t// `.derived()` helper drops the cleanup return, so the raw form\n\t\t// is required here. `equals: packedEquals` is preserved verbatim.\n\t\tconst projection = node<ReadonlyArray<RetrievalEntry<TMem>>>(\n\t\t\t[result],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst data = batchData.map((b, i) =>\n\t\t\t\t\tb != null && b.length > 0 ? b.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tactions.emit((data[0] as { packed: ReadonlyArray<RetrievalEntry<TMem>> }).packed);\n\t\t\t\treturn {\n\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\t// Auto-unmount on last unsubscribe (DS-13.5.C).\n\t\t\t\t\t\t// Idempotent: try/catch covers the case where the\n\t\t\t\t\t\t// segment was already removed (e.g. parent destroy\n\t\t\t\t\t\t// cascade ran first, or the caller called remove()\n\t\t\t\t\t\t// manually).\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tthis.remove(segment);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* best-effort cleanup */\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\t{\n\t\t\t\tname: \"projection\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: aiMeta(\"retrieval_reactive\"),\n\t\t\t\tinitial: [] as ReadonlyArray<RetrievalEntry<TMem>>,\n\t\t\t\tequals: packedEquals,\n\t\t\t},\n\t\t);\n\t\tsub.add(projection, { name: \"projection\" });\n\n\t\tthis.mount(segment, sub);\n\t\treturn projection;\n\t}\n}\n\n/**\n * Build the retrieval pipeline (vector + KG + budget packing) over a\n * `DistillBundle` and optional `vectors` / `kg` substrates. Returns a\n * `MemoryRetrievalGraph` exposing `retrieval` / `retrievalTrace` reactive\n * state and the `retrieveReactive(input)` consumer method.\n */\nexport function memoryRetrieval<TMem>(\n\topts: MemoryRetrievalOptions<TMem>,\n): MemoryRetrievalGraph<TMem> {\n\treturn new MemoryRetrievalGraph<TMem>(opts);\n}\n","/**\n * `promptNode` — universal LLM transform as a reactive derived node.\n *\n * The shape: `deps → messagesNode (derived) → switchMap → response (producer) → output`.\n * Each upstream wave is one LLM call; superseding waves cancel the in-flight\n * call via the abort signal threaded through `nodeSignal(opts.abort)`.\n *\n * The producer-shape on the inner is load-bearing: it emits exactly one DATA\n * + COMPLETE per wave, so the outer switchMap sees one DATA per wave (matches\n * the `HarnessExecutor` contract). A `node([response], (batchData, actions, ctx) => {\n * const data = ...; actions.emit(parse(data[0]));\n * }, { describeKind: \"derived\" })` would have its\n * own first-run / push-on-subscribe semantics that can leak a transient null\n * before the real response arrives — observed and reverted in an earlier\n * attempt; see SESSION-ai-harness-module-review.md line 3654 for context.\n * Locked as path (b) producer-based by Session C (2026-04-27); inner-node\n * naming aligned to `prompt_node::response` per the C+D widening (2026-04-30).\n *\n * **Retry / replay-cache.** Stack middleware on the adapter:\n *\n * ```ts\n * import { withRetry, withReplayCache } from \"@graphrefly/graphrefly/patterns/ai\";\n *\n * const adapter = withRetry(\n * withReplayCache(baseAdapter, { keyFn: (ctx) => ctx.messages[0].content }),\n * { count: 3, backoff: 200 },\n * );\n * const result = promptNode(adapter, [input], (q) => q);\n * ```\n *\n * `promptNode` no longer ships `retries` / `cache` options — they duplicated\n * middleware already at the adapter layer.\n *\n * **Cross-wave cache (COMPOSITION-GUIDE §32).** The switchMap output cache\n * survives across new outer DATAs — `promptNode`'s cached value persists\n * until the next wave fully resolves. Consumers that need to distinguish\n * \"fresh value for THIS session\" from \"stale cache from a prior session\"\n * (e.g. `agentLoop` resetting on new `run()`) must add a `node([])` mirror\n * at their session boundary and depend on the mirror, not the `promptNode`\n * output directly. `promptNode` itself stays primitive — it does not\n * embed a state-mirror.\n *\n * @module\n */\n\nimport { COMPLETE, DATA, ERROR, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { fromAny, type NodeInput, switchMap } from \"@graphrefly/pure-ts/extra\";\nimport { nodeSignal } from \"../../../base/sources/settled.js\";\nimport { aiMeta, stripFences } from \"../_internal.js\";\nimport type {\n\tChatMessage,\n\tLLMAdapter,\n\tLLMInvokeOptions,\n\tLLMResponse,\n\tToolDefinition,\n} from \"../adapters/core/types.js\";\n\nexport type PromptNodeOptions = {\n\tname?: string;\n\tmodel?: string;\n\ttemperature?: number;\n\tmaxTokens?: number;\n\t/**\n\t * Output format:\n\t * - `\"text\"` (default) — emit the response content as a string.\n\t * - `\"json\"` — `JSON.parse` the content (markdown fences stripped).\n\t * - `\"raw\"` — emit the full {@link LLMResponse} object (subsumes the\n\t * pre-Tier-2.3 `fromLLM` shape; use this when you need `usage` /\n\t * `toolCalls` / `finishReason` alongside `content`).\n\t */\n\tformat?: \"text\" | \"json\" | \"raw\";\n\t/**\n\t * Reactive tool definitions forwarded to the adapter. Pair with\n\t * `format: \"raw\"` (or read `toolCalls` from a downstream parser) when\n\t * tool-calling is in scope.\n\t *\n\t * **Reactive declared edge** (DF12, Tier 7): `tools` is a `Node` so the\n\t * tools list participates in `describe()` topology and `explain()` causal\n\t * chains. The tools Node is added to `messagesNode`'s declared deps —\n\t * tools changes re-invoke the LLM (treated as a new call envelope).\n\t * Wrap with `distinctUntilChanged` upstream if your tool selector emits\n\t * noisy duplicates that would otherwise spam the adapter. See\n\t * COMPOSITION-GUIDE §31 (Dynamic tool selection) for the canonical\n\t * `toolSelector` pattern that produces this Node.\n\t *\n\t * **Activation note:** since `tools` is a real declared dep, `messagesNode`\n\t * waits for the tools Node to DATA at least once before firing\n\t * (push-on-subscribe SENTINEL gate). Pass a `node<ToolDefinition[]>([], { initial: [] })`\n\t * if you want immediate activation with no tools, or the latest published\n\t * `toolSelector.tools` Node.\n\t */\n\ttools?: Node<readonly ToolDefinition[]>;\n\t/**\n\t * Optional system prompt. Forwarded via `opts.systemPrompt` to the adapter\n\t * only — never pushed as a `{role:\"system\"}` message (avoiding the\n\t * double-send class of bug where adapters that normalize both shapes end\n\t * up with two system entries).\n\t */\n\tsystemPrompt?: string;\n\t/**\n\t * Optional reactive abort signal. When the node emits `true`, the in-flight\n\t * `adapter.invoke()` call is cancelled via `AbortController.abort()`.\n\t * Threaded through `nodeSignal(abort)` — a one-shot bridge. Useful inside\n\t * agent state machines where a separate `aborted` state should cancel the\n\t * current LLM call without superseding via switchMap.\n\t */\n\tabort?: Node<boolean>;\n\tmeta?: Record<string, unknown>;\n};\n\n/** Extract text content from an LLM response, handling various response shapes. */\nfunction extractContent(resp: unknown): string {\n\tif (resp != null && typeof resp === \"object\" && \"content\" in resp) {\n\t\treturn String((resp as LLMResponse).content);\n\t}\n\tif (typeof resp === \"string\") return resp;\n\treturn String(resp);\n}\n\nfunction previewContent(text: string, max = 200): string {\n\tif (text.length <= max) return text;\n\treturn `${text.slice(0, max)}…`;\n}\n\n/**\n * Universal LLM transform: wraps a prompt template + model adapter into a reactive derived node.\n * Re-invokes the LLM whenever any dep changes. Suitable for triage, QA, hypothesis, parity, etc.\n *\n * **Topology** (visible in `describe()`):\n * ```\n * <deps...>, [tools?] → <name>::messages (derived, meta.ai = prompt_node::messages)\n * <name>::messages → <name>::output (switchMap product, meta.ai = prompt_node::output)\n * per-wave inner: <name>::response (producer, meta.ai = prompt_node::response)\n * ```\n * When `opts.tools` is supplied, the tools `Node` is appended to\n * `messagesNode`'s declared deps so it appears as a real edge in `describe()`\n * / `explain()` (DF12, Tier 7).\n *\n * **No-input semantics** (matches the codebase-wide SENTINEL convention):\n * - **Initial no-input** (no real input has ever arrived) — emits nothing.\n * Outer cache stays `undefined`; `subscribe` consumers see no DATA event.\n * Use this to keep downstream gating clean: a `withLatestFrom`-paired\n * trigger won't fire until the LLM has actually produced something.\n * - **Mid-flow no-input** (input dropped to nullish after at least one\n * real LLM call) — emits `null` as a domain \"input went away\" signal.\n * Downstream consumers can distinguish \"haven't started\" from \"input\n * gone.\"\n *\n * **Retries / caching:** stack `withRetry` / `withReplayCache` middleware on the\n * `adapter` argument — `promptNode` no longer ships its own duplicated retry /\n * cache loops (pre-1.0 cleanup, see review session 1).\n *\n * @param adapter - LLM adapter (provider-agnostic). Wrap with `withRetry` /\n * `withReplayCache` middleware for transient-error tolerance\n * or replay caching.\n * @param deps - Input nodes whose values feed the prompt.\n * @param prompt - Static string or template function receiving dep values.\n * @param opts - Optional configuration.\n * @returns `Node` emitting LLM responses (string or parsed JSON).\n */\n// Overload 1: `format: \"raw\"` constrains the emit type to `LLMResponse | null`\n// (the full adapter response, with `usage` / `toolCalls` / `finishReason`).\n// Subsumes the pre-Tier-2.3 `fromLLM` shape.\nexport function promptNode(\n\tadapter: LLMAdapter,\n\tdeps: readonly Node<unknown>[],\n\tprompt: string | ((...depValues: unknown[]) => string),\n\topts: PromptNodeOptions & { format: \"raw\" },\n): Node<LLMResponse | null>;\n// Overload 2: `format: \"text\" | \"json\"` (default text) — emit-type is the\n// caller's `T` (defaults to `string`). For `\"json\"` callers typically pass\n// the parsed shape (e.g. `promptNode<MyShape>(...)`).\nexport function promptNode<T = string>(\n\tadapter: LLMAdapter,\n\tdeps: readonly Node<unknown>[],\n\tprompt: string | ((...depValues: unknown[]) => string),\n\topts?: Omit<PromptNodeOptions, \"format\"> & { format?: \"text\" | \"json\" },\n): Node<T | null>;\nexport function promptNode<T = string>(\n\tadapter: LLMAdapter,\n\tdeps: readonly Node<unknown>[],\n\tprompt: string | ((...depValues: unknown[]) => string),\n\topts?: PromptNodeOptions,\n): Node<T | null> {\n\tconst format = opts?.format ?? \"text\";\n\tconst baseName = opts?.name ?? \"prompt_node\";\n\n\t// qa A8: tools without `format: \"raw\"` is a footgun — adapter receives\n\t// the tool definitions and may produce `toolCalls`, but the emit path\n\t// only extracts `content`. Warn at construction; downstream parsers\n\t// reading `toolCalls` from a custom `format: \"raw\"` consumer pattern\n\t// can ignore by setting `format: \"raw\"` (intent now matches behavior).\n\tif (opts?.tools !== undefined && format !== \"raw\") {\n\t\tconsole.warn(\n\t\t\t\"promptNode: `tools` is set but `format !== 'raw'`. \" +\n\t\t\t\t\"Tool calls in the response will be silently dropped — set \" +\n\t\t\t\t\"`format: 'raw'` to receive the full LLMResponse with `toolCalls`.\",\n\t\t);\n\t}\n\n\t// SENTINEL semantics rely on the universal first-run gate + standard\n\t// prevData semantics (undefined = SENTINEL, any other value = DATA seen):\n\t// - **Initial no-input** (no dep has ever DATA'd, so prevData is\n\t// undefined across the board): the `derived`'s first-run gate blocks\n\t// `messagesNode`'s fn entirely. It never emits, switchMap never\n\t// fires, outer cache stays `undefined`.\n\t// - **Mid-flow no-input** (deps previously DATA'd then went nullish):\n\t// fn runs, returns `[]`, switchMap dispatches the `node([], { initial: null })`\n\t// branch → outer emits `null` as the domain \"input went away\" signal.\n\t// No `initial: []` and no closure flag — `prevData === undefined` is\n\t// already the sentinel marker, and the gate already enforces \"don't fire\n\t// fn until every dep has DATA'd at least once.\"\n\t//\n\t// DF12: when `opts.tools` is a Node, it's appended to `messagesNode`'s\n\t// declared deps. The fn slices values into user-deps + tools, and emits\n\t// an envelope `{ messages, tools }` so switchMap's per-wave inner can\n\t// read the latest tools via the reactive edge instead of a closure.\n\ttype Envelope = {\n\t\tmessages: readonly ChatMessage[];\n\t\ttools: readonly ToolDefinition[] | undefined;\n\t};\n\tconst userDepsLength = deps.length;\n\tconst allDeps: readonly Node<unknown>[] =\n\t\topts?.tools !== undefined ? [...deps, opts.tools as Node<unknown>] : deps;\n\tconst messagesNode = node<Envelope>(\n\t\tallDeps 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 userValues = data.slice(0, userDepsLength);\n\t\t\tconst toolsValue =\n\t\t\t\topts?.tools !== undefined\n\t\t\t\t\t? (data[userDepsLength] as readonly ToolDefinition[] | undefined)\n\t\t\t\t\t: undefined;\n\t\t\t// Dep-level null guard (composition guide §8): if any USER dep is\n\t\t\t// nullish, emit empty messages → switchMap emits null (mid-flow\n\t\t\t// drop-out). The tools dep can legitimately be empty `[]`; only\n\t\t\t// user deps gate the call.\n\t\t\tif (userValues.some((v) => v == null)) {\n\t\t\t\tactions.emit({ messages: [], tools: toolsValue });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst text = typeof prompt === \"string\" ? prompt : prompt(...userValues);\n\t\t\tif (!text) {\n\t\t\t\tactions.emit({ messages: [], tools: toolsValue });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// systemPrompt forwarded through invoke opts only (no double-send).\n\t\t\tactions.emit({\n\t\t\t\tmessages: [{ role: \"user\" as const, content: text }],\n\t\t\t\ttools: toolsValue,\n\t\t\t});\n\t\t},\n\t\t{\n\t\t\tname: `${baseName}::messages`,\n\t\t\tmeta: aiMeta(\"prompt_node::messages\"),\n\t\t},\n\t);\n\n\tconst result = switchMap<Envelope, T | null>(\n\t\tmessagesNode,\n\t\t(envelope) => {\n\t\t\tconst { messages: msgs, tools } = envelope;\n\t\t\tif (!msgs || msgs.length === 0) {\n\t\t\t\treturn node<T | null>([], { initial: null }) as NodeInput<T | null>;\n\t\t\t}\n\n\t\t\t// Producer ensures exactly one DATA + COMPLETE per wave; switchMap\n\t\t\t// sees one DATA, the harness's \"one emission per wave\" contract is\n\t\t\t// honored. Earlier attempts using a derived node leaked\n\t\t\t// transient nulls via the derived's first-run gate.\n\t\t\treturn node<T | null>(\n\t\t\t\t(_data, actions) => {\n\t\t\t\t\tlet done = false;\n\t\t\t\t\tlet cancelled = false;\n\t\t\t\t\tlet abortDispose: (() => void) | undefined;\n\n\t\t\t\t\tconst invokeOpts: LLMInvokeOptions = {\n\t\t\t\t\t\tmodel: opts?.model,\n\t\t\t\t\t\ttemperature: opts?.temperature,\n\t\t\t\t\t\tmaxTokens: opts?.maxTokens,\n\t\t\t\t\t\tsystemPrompt: opts?.systemPrompt,\n\t\t\t\t\t\t...(tools !== undefined ? { tools } : {}),\n\t\t\t\t\t};\n\t\t\t\t\tif (opts?.abort) {\n\t\t\t\t\t\tconst sig = nodeSignal(opts.abort);\n\t\t\t\t\t\tinvokeOpts.signal = sig.signal;\n\t\t\t\t\t\tabortDispose = sig.dispose;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet invokeResult: NodeInput<LLMResponse>;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tinvokeResult = adapter.invoke(msgs, invokeOpts);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tdone = true;\n\t\t\t\t\t\tactions.down([[ERROR, err]]);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\t\t\tabortDispose?.();\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tconst callNode = fromAny(invokeResult);\n\n\t\t\t\t\tconst sub = callNode.subscribe((batch) => {\n\t\t\t\t\t\tif (cancelled || done) return;\n\t\t\t\t\t\tfor (const msg of batch) {\n\t\t\t\t\t\t\t// F-11: re-check `cancelled` (and `done`) at the top of\n\t\t\t\t\t\t\t// each per-message iteration so a teardown / abort that\n\t\t\t\t\t\t\t// fires synchronously between messages stops processing\n\t\t\t\t\t\t\t// further batched messages immediately.\n\t\t\t\t\t\t\tif (cancelled || done) return;\n\t\t\t\t\t\t\tif (msg[0] === DATA) {\n\t\t\t\t\t\t\t\tconst resp = msg[1] as LLMResponse;\n\t\t\t\t\t\t\t\t// `format: \"raw\"` bypasses parsing — emit the full\n\t\t\t\t\t\t\t\t// LLMResponse object (subsumes the pre-Tier-2.3 `fromLLM`\n\t\t\t\t\t\t\t\t// output shape).\n\t\t\t\t\t\t\t\tif (format === \"raw\") {\n\t\t\t\t\t\t\t\t\tactions.emit(resp as unknown as T);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// F-12: cache the extracted content once on the\n\t\t\t\t\t\t\t\t\t// parse-failure path so we don't call\n\t\t\t\t\t\t\t\t\t// `extractContent(resp)` twice (once for parsing,\n\t\t\t\t\t\t\t\t\t// once for the error-message preview).\n\t\t\t\t\t\t\t\t\tlet content: string;\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tcontent = extractContent(resp);\n\t\t\t\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t\t\t\t// extractContent itself failed — propagate as\n\t\t\t\t\t\t\t\t\t\t// an ERROR with a generic raw-extraction message.\n\t\t\t\t\t\t\t\t\t\tconst wrapped = new Error(\n\t\t\t\t\t\t\t\t\t\t\t`promptNode: failed to extract content from LLM response: ${\n\t\t\t\t\t\t\t\t\t\t\t\t(err as Error).message\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\t// F-7: dispose abort hook on terminal-error\n\t\t\t\t\t\t\t\t\t\t// branches so we don't retain the AbortController\n\t\t\t\t\t\t\t\t\t\t// after the wave terminates. Idempotent.\n\t\t\t\t\t\t\t\t\t\tabortDispose?.();\n\t\t\t\t\t\t\t\t\t\tabortDispose = undefined;\n\t\t\t\t\t\t\t\t\t\tdone = true;\n\t\t\t\t\t\t\t\t\t\tactions.down([[ERROR, wrapped]]);\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\ttry {\n\t\t\t\t\t\t\t\t\t\tconst parsed: T =\n\t\t\t\t\t\t\t\t\t\t\tformat === \"json\"\n\t\t\t\t\t\t\t\t\t\t\t\t? (JSON.parse(stripFences(content)) as T)\n\t\t\t\t\t\t\t\t\t\t\t\t: (content as unknown as T);\n\t\t\t\t\t\t\t\t\t\tactions.emit(parsed);\n\t\t\t\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t\t\t\tconst wrapped = new Error(\n\t\t\t\t\t\t\t\t\t\t\t`promptNode: failed to parse LLM response as JSON: ${\n\t\t\t\t\t\t\t\t\t\t\t\t(err as Error).message\n\t\t\t\t\t\t\t\t\t\t\t}\\n Raw content (first 200 chars): ${previewContent(content)}`,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t// F-7: dispose abort hook on parse-error\n\t\t\t\t\t\t\t\t\t\t// terminal branch.\n\t\t\t\t\t\t\t\t\t\tabortDispose?.();\n\t\t\t\t\t\t\t\t\t\tabortDispose = undefined;\n\t\t\t\t\t\t\t\t\t\tdone = true;\n\t\t\t\t\t\t\t\t\t\tactions.down([[ERROR, wrapped]]);\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} else if (msg[0] === ERROR) {\n\t\t\t\t\t\t\t\t// F-7: dispose abort hook on terminal ERROR branch.\n\t\t\t\t\t\t\t\tabortDispose?.();\n\t\t\t\t\t\t\t\tabortDispose = undefined;\n\t\t\t\t\t\t\t\tdone = true;\n\t\t\t\t\t\t\t\tactions.down([[ERROR, msg[1]]]);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t} else if (msg[0] === COMPLETE) {\n\t\t\t\t\t\t\t\t// Adapter completed — propagate. emit() above already\n\t\t\t\t\t\t\t\t// queued the parsed value so the wave carries DATA + COMPLETE.\n\t\t\t\t\t\t\t\t// F-7: dispose abort hook on terminal COMPLETE branch.\n\t\t\t\t\t\t\t\tabortDispose?.();\n\t\t\t\t\t\t\t\tabortDispose = undefined;\n\t\t\t\t\t\t\t\tdone = true;\n\t\t\t\t\t\t\t\tactions.down([[COMPLETE]]);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Spec §1.3.6 forward-unknown — DIRTY/RESOLVED/INVALIDATE/\n\t\t\t\t\t\t\t\t// PAUSE/RESUME etc. should propagate so downstream caches /\n\t\t\t\t\t\t\t\t// flow-control hooks aren't starved. Re-typed `as never`\n\t\t\t\t\t\t\t\t// because the call's NodeInput<LLMResponse> message tuple\n\t\t\t\t\t\t\t\t// is wider than the unbound `T` projection.\n\t\t\t\t\t\t\t\tactions.down([msg as never]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\t\tcancelled = true;\n\t\t\t\t\t\t\tsub();\n\t\t\t\t\t\t\t// F-7: cleanup callback's abortDispose call is idempotent —\n\t\t\t\t\t\t\t// the terminal-branch dispose above sets `abortDispose =\n\t\t\t\t\t\t\t// undefined` so this is a no-op when terminal-fired.\n\t\t\t\t\t\t\tabortDispose?.();\n\t\t\t\t\t\t\tabortDispose = undefined;\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdescribeKind: \"producer\",\n\t\t\t\t\tname: `${baseName}::response`,\n\t\t\t\t\tmeta: aiMeta(\"prompt_node::response\"),\n\t\t\t\t},\n\t\t\t) as NodeInput<T | null>;\n\t\t},\n\t\t{\n\t\t\tname: `${baseName}::output`,\n\t\t\tmeta: opts?.meta\n\t\t\t\t? { ...aiMeta(\"prompt_node::output\"), ...opts.meta }\n\t\t\t\t: aiMeta(\"prompt_node::output\"),\n\t\t},\n\t);\n\n\treturn result;\n}\n","// ---------------------------------------------------------------------------\n// promptCall — public single-shot LLM JSON helper (Tier 4.6 Wave AM Unit 4\n// promotion from the previously-internal `llmJsonCall` in\n// `patterns/ai/memory/llm-memory.ts`).\n//\n// Wraps {@link promptNode} for the common \"one-shot LLM JSON call per input\"\n// shape: a per-call `node([], { initial: input })` is wrapped, the prompt builder runs against\n// it, and the returned `NodeInput<TOut>` slots into reactive callbacks like\n// `distill`'s `extractFn` / `consolidateFn`. Inherits markdown-fence stripping\n// and content-preview parse errors from `promptNode({format: \"json\"})`.\n//\n// `llmExtractor` / `llmConsolidator` are now thin wrappers over `promptCall`.\n// ---------------------------------------------------------------------------\n\nimport { node } from \"@graphrefly/pure-ts/core\";\nimport type { NodeInput } from \"@graphrefly/pure-ts/extra\";\nimport type { Extraction } from \"../../../base/composition/distill.js\";\nimport type { LLMAdapter } from \"../adapters/core/types.js\";\nimport { promptNode } from \"./prompt-node.js\";\n\n/** Options accepted by {@link promptCall}, {@link llmExtractor}, and {@link llmConsolidator}. */\nexport type PromptCallOptions = {\n\tadapter: LLMAdapter;\n\tmodel?: string;\n\ttemperature?: number;\n\tmaxTokens?: number;\n\t/**\n\t * Optional name forwarded to the underlying `promptNode` (used as the\n\t * `<name>::messages` / `<name>::response` / `<name>::output` path prefix).\n\t * Defaults differ per call site so multiple `promptCall`s wired into the\n\t * same graph don't collide on `prompt_node::output`.\n\t */\n\tname?: string;\n};\n\n/**\n * Build a one-shot LLM JSON-call factory: each invocation wraps `input` in a\n * fresh `node([], { initial: input })`, delegates to `promptNode({format: \"json\"})`, and\n * returns a `NodeInput<TOut>` that the caller plugs into `distill` /\n * `agentLoop` / any reactive composition that accepts `NodeInput`.\n *\n * **Per-call lifecycle.** The returned `NodeInput<TOut>` is a producer that\n * emits exactly one `DATA` per upstream input (per Tier 1.2 Session C lock —\n * `promptNode` guarantees one DATA per wave). When the consumer's switchMap\n * supersedes it, the per-call `node([], { initial: input })` and the inner `prompt_node::response`\n * tear down together.\n *\n * @param systemPrompt - System message sent on every call.\n * @param buildUserContent - Per-input user-content builder (must be JSON-stringifiable).\n * @param opts - Adapter + model/temperature/maxTokens + optional name prefix.\n * @param defaultName - Path-prefix fallback when `opts.name` is omitted.\n * @returns Factory `(input: TIn) => NodeInput<TOut>`.\n *\n * @category patterns\n */\nexport function promptCall<TIn, TOut>(\n\tsystemPrompt: string,\n\tbuildUserContent: (input: TIn) => string,\n\topts: PromptCallOptions,\n\tdefaultName: string,\n): (input: TIn) => NodeInput<TOut> {\n\tconst name = opts.name ?? defaultName;\n\treturn (input: TIn) => {\n\t\t// One-shot node([], { initial: input }) per call — switchMap teardown inside the\n\t\t// consumer (e.g. distill) reclaims the node when the next upstream\n\t\t// arrives or the bundle disposes.\n\t\tconst inputState = node<TIn>([], { initial: input });\n\t\treturn promptNode<TOut>(\n\t\t\topts.adapter,\n\t\t\t[inputState as never],\n\t\t\t(value: unknown) => buildUserContent(value as TIn),\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tformat: \"json\",\n\t\t\t\tsystemPrompt,\n\t\t\t\tmodel: opts.model,\n\t\t\t\ttemperature: opts.temperature ?? 0,\n\t\t\t\tmaxTokens: opts.maxTokens,\n\t\t\t},\n\t\t) as NodeInput<TOut>;\n\t};\n}\n\n/** Options accepted by {@link llmExtractor} and {@link llmConsolidator}. */\nexport type LLMExtractorOptions = PromptCallOptions & {\n\t/**\n\t * Cap the dedup-hint slice of `existingKeys` passed to the LLM. Larger\n\t * stores ship more keys (better dedup recall) at the cost of prompt size.\n\t * Default 100. Set to `Infinity` to forward every key.\n\t */\n\tmaxExistingKeys?: number;\n};\n\n/** Alias for backward compatibility. */\nexport type LLMConsolidatorOptions = LLMExtractorOptions;\n\n/**\n * Returns an `extractFn` callback for `distill()` that invokes an LLM to\n * extract structured memories from raw input.\n *\n * The system prompt should instruct the LLM to return JSON matching\n * `Extraction<TMem>` shape: `{ upsert: [{ key, value }], remove?: [key] }`.\n *\n * Built on `promptNode({format: \"json\"})` — inherits markdown-fence stripping\n * and content-preview parse errors. Stack `withRetry` on the adapter for\n * transient-error tolerance (see `patterns/ai/adapters/middleware/retry.ts`).\n */\nexport function llmExtractor<TRaw, TMem>(\n\tsystemPrompt: string,\n\topts: LLMExtractorOptions,\n): (raw: TRaw, existing: ReadonlyMap<string, TMem>) => NodeInput<Extraction<TMem>> {\n\tconst cap = opts.maxExistingKeys ?? 100;\n\tconst call = promptCall<{ raw: TRaw; existingKeys: string[] }, Extraction<TMem>>(\n\t\tsystemPrompt,\n\t\t(input) => JSON.stringify({ input: input.raw, existingKeys: input.existingKeys }),\n\t\topts,\n\t\t\"llmExtractor\",\n\t);\n\treturn (raw: TRaw, existing: ReadonlyMap<string, TMem>) => {\n\t\tconst existingKeys =\n\t\t\tcap === Number.POSITIVE_INFINITY ? [...existing.keys()] : [...existing.keys()].slice(0, cap);\n\t\treturn call({ raw, existingKeys });\n\t};\n}\n\n/**\n * Returns a `consolidateFn` callback for `distill()` that invokes an LLM to\n * cluster and merge related memories.\n */\nexport function llmConsolidator<TMem>(\n\tsystemPrompt: string,\n\topts: LLMConsolidatorOptions,\n): (entries: ReadonlyMap<string, TMem>) => NodeInput<Extraction<TMem>> {\n\tconst call = promptCall<readonly { key: string; value: TMem }[], Extraction<TMem>>(\n\t\tsystemPrompt,\n\t\t(memories) => JSON.stringify({ memories }),\n\t\topts,\n\t\t\"llmConsolidator\",\n\t);\n\treturn (entries: ReadonlyMap<string, TMem>) => {\n\t\tconst memories = [...entries.entries()].map(([key, value]) => ({ key, value }));\n\t\treturn call(memories);\n\t};\n}\n"],"mappings":"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","names":["node","batch","node","batch","node","node","batch","ERROR","node","keepalive","Graph","Graph","node","batch","keepalive","ERROR","DATA","node","fromAny","keepalive","reactiveMap","Graph","Graph","node","keepalive","reactiveMap","fromAny","DATA","COMPLETE","DATA","ERROR","node","fromAny","switchMap","node","batch","switchMap","ERROR","fromAny","DATA","COMPLETE","node","node"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/messaging/audit-records.ts","../src/utils/messaging/message.ts","../src/utils/messaging/index.ts"],"sourcesContent":["/**\n * Messaging audit-record schemas (DS-13.5.E, locked 2026-05-01 alt A).\n *\n * Per-site discriminated-union audit records for the four messaging mutation\n * sites that route through `mutate`:\n *\n * - {@link TopicPublishRecord} — `Topic.publish`\n * - {@link SubscriptionAckRecord} — `Subscription.ack`\n * - {@link SubscriptionPullAndAckRecord} — `Subscription.pullAndAck`\n * - {@link HubRemoveTopicRecord} — `Hub.removeTopic`\n *\n * **Opt-in usage.** None of the four mutation sites enable an audit log by\n * default — caller wires audit visibility by composing `mutate` with\n * an audit `ReactiveLogBundle<R>` of the matching record type and an\n * `onSuccess`/`onFailure` builder:\n *\n * ```ts\n * import { createAuditLog, mutate } from \"@graphrefly/graphrefly/extra\";\n * import {\n * type TopicPublishRecord,\n * topicPublishKeyOf,\n * } from \"@graphrefly/graphrefly/patterns/messaging\";\n *\n * const audit = createAuditLog<TopicPublishRecord>({ name: \"publishes\" });\n * const publish = mutate(\n * (item: MyMessage) => topic.publish(item),\n * {\n * frame: \"inline\",\n * log: audit,\n * onSuccessRecord: ([item], _r, m) => ({\n * t_ns: m.t_ns,\n * seq: m.seq,\n * kind: \"topic.publish\",\n * topicName: topic.name,\n * itemKey: keyOf(item),\n * }),\n * },\n * );\n * ```\n *\n * **Stability.** The `kind` discriminator strings are pre-1.0 stable;\n * renaming downstream breaks external auditors.\n *\n * **Composability.** All four records extend {@link BaseAuditRecord} so they\n * carry the cross-cutting `t_ns` / `seq?` / `handlerVersion?` fields stamped\n * by `mutate` (Audit 2 / Audit 5).\n *\n * **Per-record keyOf.** Each record exports a recommended `keyOf` for\n * keyed-storage adapters (Rule G.27-keyOf-recommended) — partition the audit\n * log by the most natural identity (`topicName::itemKey`,\n * `subscriptionId::cursor`, etc.).\n *\n * **Hub.addTopic deferred.** No `HubAddTopicRecord` ships now; the lazy\n * topic-creation site has no caller signal asking for an audit record.\n * Re-add when a consumer surfaces.\n *\n * @category patterns\n * @module patterns/messaging/audit-records\n */\n\nimport type { BaseAuditRecord } from \"../../base/mutation/index.js\";\n\n// ── Topic.publish ────────────────────────────────────────────────────────\n\n/**\n * Audit record for a single {@link TopicGraph.publish} call.\n *\n * - `topicName` — the topic the publish targeted.\n * - `itemKey` — caller-supplied identity for the published item (typically\n * the result of the topic's own `keyOf` derivation, when one exists).\n */\nexport interface TopicPublishRecord extends BaseAuditRecord {\n\treadonly kind: \"topic.publish\";\n\treadonly topicName: string;\n\treadonly itemKey: string;\n}\n\n/**\n * Recommended `keyOf` for {@link TopicPublishRecord} — formats as\n * `${topicName}::${itemKey}` for keyed-storage partitioning. Caller may\n * override per Rule G.27-keyOf-recommended.\n */\nexport const topicPublishKeyOf = (r: TopicPublishRecord): string => `${r.topicName}::${r.itemKey}`;\n\n// ── Subscription.ack ─────────────────────────────────────────────────────\n\n/**\n * Audit record for a single {@link SubscriptionGraph.ack} call.\n *\n * - `subscriptionId` — the subscription the ack advanced.\n * - `cursor` — the post-ack cursor position.\n */\nexport interface SubscriptionAckRecord extends BaseAuditRecord {\n\treadonly kind: \"subscription.ack\";\n\treadonly subscriptionId: string;\n\treadonly cursor: number;\n}\n\n/**\n * Recommended `keyOf` for {@link SubscriptionAckRecord} — formats as\n * `${subscriptionId}::${cursor}`.\n */\nexport const subscriptionAckKeyOf = (r: SubscriptionAckRecord): string =>\n\t`${r.subscriptionId}::${r.cursor}`;\n\n// ── Subscription.pullAndAck ──────────────────────────────────────────────\n\n/**\n * Audit record for a single {@link SubscriptionGraph.pullAndAck} call.\n *\n * - `subscriptionId` — the subscription the pullAndAck advanced.\n * - `cursor` — the post-pullAndAck cursor position.\n * - `itemCount` — number of items returned to the caller in this call.\n */\nexport interface SubscriptionPullAndAckRecord extends BaseAuditRecord {\n\treadonly kind: \"subscription.pullAndAck\";\n\treadonly subscriptionId: string;\n\treadonly cursor: number;\n\treadonly itemCount: number;\n}\n\n/**\n * Recommended `keyOf` for {@link SubscriptionPullAndAckRecord} — formats as\n * `${subscriptionId}::${cursor}` (identical shape to ack records so the\n * combined audit-log partitioning matches a per-cursor-frame view).\n */\nexport const subscriptionPullAndAckKeyOf = (r: SubscriptionPullAndAckRecord): string =>\n\t`${r.subscriptionId}::${r.cursor}`;\n\n// ── Hub.removeTopic ──────────────────────────────────────────────────────\n\n/**\n * Audit record for a single {@link MessagingHubGraph.removeTopic} call.\n *\n * - `topicName` — the topic that was unmounted from the hub.\n */\nexport interface HubRemoveTopicRecord extends BaseAuditRecord {\n\treadonly kind: \"hub.removeTopic\";\n\treadonly topicName: string;\n}\n\n/**\n * Recommended `keyOf` for {@link HubRemoveTopicRecord} — the topic name itself\n * is already the natural identity.\n */\nexport const hubRemoveTopicKeyOf = (r: HubRemoveTopicRecord): string => r.topicName;\n\n// ── Discriminated-union convenience ──────────────────────────────────────\n\n/**\n * Discriminated union over every messaging audit record. Useful for callers\n * that aggregate records from multiple sites into one log; switch on\n * `record.kind` to narrow.\n */\nexport type MessagingAuditRecord =\n\t| TopicPublishRecord\n\t| SubscriptionAckRecord\n\t| SubscriptionPullAndAckRecord\n\t| HubRemoveTopicRecord;\n","/**\n * Standard `TopicMessage<T>` envelope for hub topics + well-known topic name\n * constants (Phase 13.B; spec source: archive/docs/SESSION-human-llm-intervention-primitives.md\n * §6 + archive/docs/SESSION-multi-agent-gap-analysis.md §6 cross-cut).\n *\n * `TopicMessage<T>` is the **recommended** wrapper for cross-agent / cross-graph\n * topic payloads — it carries identity, schema, deadline, and correlation\n * metadata alongside the typed `payload`. It is NOT a required protocol\n * type; raw `topic<T>` continues to work for in-process payloads where the\n * envelope fields would be noise.\n *\n * Use the envelope when:\n * - Two or more graphs (or human + LLM consumers) communicate over a topic\n * and need a stable wire shape — `correlationId` is the join key, `schema`\n * gates payload validation, `expiresAt` enables TTL enforcement.\n * - A topic carries multiple payload kinds and consumers need to discriminate\n * without parsing structurally.\n *\n * The standard well-known topic constants below are **conventions** — string\n * literals callers can pass to `messagingHub().topic(NAME)` to get a\n * predictable lookup. The hub does not enforce any topic to actually exist;\n * topics are still lazy on first access.\n */\n\n// ---------------------------------------------------------------------------\n// JSON Schema — minimal local type\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal JSON Schema shape, scoped to what `TopicMessage<T>` validates against.\n * Locked DS-13.B (2026-04-30): zero-dep posture, structural shape only — no\n * full validator shipped. Callers that want full validation supply their own\n * (e.g. `ajv`, `zod`, `valibot`) and read `Message.schema` as the rule\n * source. The shape covers the JSON-Schema-7 subset that hub-topic payload\n * descriptions actually need:\n * - `type` / `properties` / `required` / `additionalProperties` for objects.\n * - `items` for arrays.\n * - `enum` / `const` for value constraints.\n * - `$ref` / `definitions` for shared sub-schemas.\n *\n * If a concrete consumer needs a richer shape (oneOf, allOf, format, etc.),\n * extend this type — it's a structural contract, not a tagged union, so\n * additive fields don't break existing producers.\n */\nexport interface JsonSchema {\n\treadonly type?:\n\t\t| \"string\"\n\t\t| \"number\"\n\t\t| \"integer\"\n\t\t| \"boolean\"\n\t\t| \"object\"\n\t\t| \"array\"\n\t\t| \"null\"\n\t\t| readonly (\"string\" | \"number\" | \"integer\" | \"boolean\" | \"object\" | \"array\" | \"null\")[];\n\treadonly properties?: Readonly<Record<string, JsonSchema>>;\n\treadonly required?: readonly string[];\n\treadonly additionalProperties?: boolean | JsonSchema;\n\treadonly items?: JsonSchema | readonly JsonSchema[];\n\treadonly enum?: readonly unknown[];\n\treadonly const?: unknown;\n\treadonly $ref?: string;\n\treadonly definitions?: Readonly<Record<string, JsonSchema>>;\n\treadonly description?: string;\n\treadonly title?: string;\n}\n\n// ---------------------------------------------------------------------------\n// TopicMessage<T> envelope\n// ---------------------------------------------------------------------------\n\n/**\n * Recommended envelope for hub topic payloads. Carries identity, optional\n * schema reference, optional expiry, optional correlation, and the typed\n * payload itself.\n *\n * - `id` — globally-unique identifier for this message instance. Producers\n * mint it (UUID, ULID, hash, etc.); consumers use it for deduplication and\n * trace correlation. **Required.**\n * - `schema` — optional structural description of `payload`. Validators\n * (caller-supplied) read this to gate or shape consumption. Writers MAY\n * include the schema inline for self-describing topics, or omit when the\n * payload type is statically known to all consumers.\n * - `expiresAt` — ISO 8601 timestamp; consumers SHOULD drop / fallback past\n * this point. Substrate enforcement is via composition (`timeout(source,\n * ms)` + `fallback`), not a hub-level rule.\n * - `correlationId` — links related messages across topics (request /\n * response pairs, conversation threads, multi-agent handoffs). Producers\n * propagate it; consumers filter / group on it.\n * - `payload` — the typed body. Type parameter `T` is the consumer-agreed\n * shape; the envelope adds metadata around it without coupling consumers\n * to a concrete payload type.\n *\n * Reactive composition with the envelope:\n *\n * ```ts\n * const requests = hub.topic<TopicMessage<RequestBody>>(PROMPTS_TOPIC);\n * const responses = hub.topic<TopicMessage<ResponseBody>>(RESPONSES_TOPIC);\n *\n * // Filter responses to one correlation\n * const myResponse = derived([responses.latest], ([msg]) =>\n * msg?.correlationId === requestId ? [msg.payload] : [],\n * );\n * ```\n */\nexport interface TopicMessage<T> {\n\treadonly id: string;\n\treadonly schema?: JsonSchema;\n\treadonly expiresAt?: string;\n\treadonly correlationId?: string;\n\treadonly payload: T;\n}\n\n// ---------------------------------------------------------------------------\n// Standard topic name constants\n// ---------------------------------------------------------------------------\n\n/**\n * Well-known topic name for human / LLM prompts directed at the harness.\n * Example payload: `TopicMessage<{ prompt: string; context?: object }>`.\n *\n * Co-locked with {@link RESPONSES_TOPIC} per the human-LLM intervention\n * session §6 #4 (paired request / response convention).\n */\nexport const PROMPTS_TOPIC = \"prompts\";\n\n/**\n * Well-known topic name for responses to {@link PROMPTS_TOPIC} entries.\n * Producers pair the response to its prompt via `correlationId`. Example\n * payload: `TopicMessage<{ content: string; finishReason?: string }>`.\n */\nexport const RESPONSES_TOPIC = \"responses\";\n\n/**\n * Well-known topic name for out-of-band injections — runtime overrides /\n * hot-fixes / human nudges that bypass the normal request flow. Example\n * payload: `TopicMessage<{ kind: \"context-patch\" | \"policy-override\" | ...;\n * data: unknown }>`. Per-injection consumers decide how (and whether) to\n * apply.\n */\nexport const INJECTIONS_TOPIC = \"injections\";\n\n/**\n * Well-known topic name for items the harness deferred for later attention\n * (parked queue, follow-up tracker, \"I'll get back to this\"). Producer is\n * usually the harness itself; consumer is a tracker / dashboard / human.\n * Example payload: `TopicMessage<{ reason: string; original: unknown }>`.\n */\nexport const DEFERRED_TOPIC = \"deferred\";\n\n/**\n * Well-known topic name for spawn requests (Phase 13.I `spawnable()`\n * surface). Producer emits a `TopicMessage<SpawnRequest>` to request a child\n * agent / subgraph; consumer is the materializer that mints the slot.\n * Example payload: `TopicMessage<{ presetId: string; taskInput: unknown;\n * depth?: number }>`. `correlationId` links the spawn to its parent\n * conversation; `expiresAt` enforces TTL on long-lived requests.\n */\nexport const SPAWNS_TOPIC = \"spawns\";\n\n/**\n * DS-14.6.A D-B3 — well-known topic for the dynamic `actorPool()` shared\n * tagged-context pool. Actors publish `ContextEntry` here; per-actor views\n * render their own compressed slice.\n */\nexport const CONTEXT_TOPIC = \"context\";\n\n/**\n * DS-14.6.A D-B3 — well-known topic for the `actorPool()` shared todo list.\n * Any actor may `enqueueTodo`; actors pull assigned todos via their cursor.\n */\nexport const TODOS_TOPIC = \"todos\";\n\n/**\n * Tuple of all well-known topic constants — useful for \"register all\n * standard topics on a hub\" patterns and for compile-time exhaustiveness\n * checks.\n */\nexport const STANDARD_TOPICS = [\n\tPROMPTS_TOPIC,\n\tRESPONSES_TOPIC,\n\tINJECTIONS_TOPIC,\n\tDEFERRED_TOPIC,\n\tSPAWNS_TOPIC,\n\tCONTEXT_TOPIC,\n\tTODOS_TOPIC,\n] as const;\n\n/**\n * Union of all well-known topic name string literals.\n */\nexport type StandardTopic = (typeof STANDARD_TOPICS)[number];\n","/**\n * Messaging patterns (roadmap §4.2).\n *\n * Pulsar-inspired messaging primitives modeled as graph factories:\n * - `topic()` for append-only topic streams with a retained window.\n * - `subscription()` for cursor-based consumers.\n * - `topicBridge()` for autonomous topic-to-topic relay.\n * - `messagingHub()` for a lazy topic registry.\n *\n * Plus the Phase 13.B standard `Message<T>` envelope and well-known topic\n * name constants ({@link PROMPTS_TOPIC} / {@link RESPONSES_TOPIC} /\n * {@link INJECTIONS_TOPIC} / {@link DEFERRED_TOPIC} / {@link SPAWNS_TOPIC})\n * — recommended (not enforced) wire shape for cross-graph topic payloads.\n *\n * Job queue / job flow primitives live in `patterns/job-queue` — they are a\n * distinct domain that happens to share reactive-log / reactive-map\n * infrastructure with topics.\n */\n\nexport {\n\ttype HubRemoveTopicRecord,\n\thubRemoveTopicKeyOf,\n\ttype MessagingAuditRecord,\n\ttype SubscriptionAckRecord,\n\ttype SubscriptionPullAndAckRecord,\n\tsubscriptionAckKeyOf,\n\tsubscriptionPullAndAckKeyOf,\n\ttype TopicPublishRecord,\n\ttopicPublishKeyOf,\n} from \"./audit-records.js\";\nexport {\n\tCONTEXT_TOPIC,\n\tDEFERRED_TOPIC,\n\tINJECTIONS_TOPIC,\n\ttype JsonSchema,\n\tPROMPTS_TOPIC,\n\tRESPONSES_TOPIC,\n\tSPAWNS_TOPIC,\n\tSTANDARD_TOPICS,\n\ttype StandardTopic,\n\tTODOS_TOPIC,\n\ttype TopicMessage,\n} from \"./message.js\";\n\nimport { batch, COMPLETE, DATA, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { keepalive, type ReactiveLogBundle, reactiveLog } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport { mutate } from \"../../base/mutation/index.js\";\n\nconst DEFAULT_MAX_PER_PUMP = 256;\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 messagingMeta(kind: string, extra?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"messaging\", kind, extra);\n}\n\nexport type TopicOptions = {\n\tgraph?: GraphOptions;\n\t/** Bounded retention; default 1024 per cross-cutting policy (Audit 2/4). */\n\tretainedLimit?: number;\n};\n\nconst DEFAULT_TOPIC_RETAINED_LIMIT = 1024;\n\nexport class TopicGraph<T> extends Graph {\n\tprivate readonly _log;\n\tprivate readonly _publishImpl: (value: T) => void;\n\treadonly events: Node<readonly T[]>;\n\t/**\n\t * Most recently published value. Stays in the protocol SENTINEL state\n\t * (`cache === undefined`, no DATA emitted) until the first publish, then\n\t * tracks the latest entry. Spec §5.12 reserves `undefined` as the\n\t * \"never sent DATA\" sentinel — and `TopicGraph.publish(undefined)` is\n\t * rejected — so `cache === undefined` unambiguously signals \"empty topic\"\n\t * even when `T` itself includes `null` (i.e., `topic<number | null>`).\n\t *\n\t * **Within a reactive fn:** detect the empty-topic case via\n\t * `ctx.prevData[i] === undefined` for the dep slot holding `topic.latest`,\n\t * or check `latest.cache === undefined` outside reactive code. No\n\t * separate `hasLatest` companion needed — the SENTINEL is the answer.\n\t */\n\treadonly latest: Node<T>;\n\n\tconstructor(name: string, opts: TopicOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\tthis._log = reactiveLog<T>([], {\n\t\t\tname: \"events\",\n\t\t\tmaxSize: opts.retainedLimit ?? DEFAULT_TOPIC_RETAINED_LIMIT,\n\t\t});\n\t\tthis.events = this._log.entries;\n\t\tthis.add(this.events, { name: \"events\" });\n\t\t// `this.derived(\"latest\", [\"events\"], …)` is expressible after the\n\t\t// 2026-04-30 self-resolve fix in `Graph._resolveFromSegments` — a\n\t\t// single-segment path matching the graph's own name (e.g.\n\t\t// `topic(\"events\").resolve(\"events\")`) no longer collapses to empty\n\t\t// and falls through to local-node lookup. Replaces the prior\n\t\t// `node([events], …) + this.add(...)` workaround.\n\t\t//\n\t\t// SENTINEL on empty: returning `[]` here yields a RESOLVED-only wave\n\t\t// (no DATA), so `latest.cache` stays `undefined` until the first\n\t\t// publish. `TopicGraph.publish(undefined)` is rejected (line below),\n\t\t// so `undefined` cache is unambiguously \"empty topic\" even when `T`\n\t\t// itself includes `null`. Drops the prior `hasLatest` companion as\n\t\t// redundant.\n\t\tthis.latest = this.derived<T>(\n\t\t\t\"latest\",\n\t\t\t[\"events\"],\n\t\t\t(batchData, 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\tconst entries = data[0] as readonly T[];\n\t\t\t\treturn entries.length === 0 ? [] : [entries[entries.length - 1] as T];\n\t\t\t},\n\t\t\t{ meta: messagingMeta(\"topic_latest\") },\n\t\t);\n\t\tthis.addDisposer(keepalive(this.latest));\n\n\t\t// D1(a): on teardown, propagate COMPLETE on `events` so downstream\n\t\t// derived chains (including any externally-held SubscriptionGraph\n\t\t// sources) see the termination via their `terminalDeps` and can stop\n\t\t// serving stale caches. Tier-3 terminal per spec §2.2.\n\t\t//\n\t\t// EC16 (verified 2026-04-30): the COMPLETE-then-disposeAllViews order\n\t\t// is intentional. COMPLETE propagates SYNCHRONOUSLY through every\n\t\t// subscriber (cursor views, derived chains) so they self-unsubscribe\n\t\t// in their terminal handler before `disposeAllViews` runs. Swapping\n\t\t// the order would clear view caches before subscribers receive the\n\t\t// terminal — strictly worse. Reading `.cache` outside a reactive fn\n\t\t// across teardown is an anti-pattern (spec §5.12) and not a use case\n\t\t// this ordering needs to preserve.\n\t\tthis.addDisposer(() => {\n\t\t\tthis.events.down([[COMPLETE]]);\n\t\t});\n\t\t// P9: release any memoized tail/slice view keepalives held by the log.\n\t\t// TopicGraph itself doesn't call log.tail/slice, but plugins may have\n\t\t// attached views via `_log` — defensive (typical reactive subscribers\n\t\t// have already unsubscribed in their COMPLETE handler above).\n\t\tthis.addDisposer(() => this._log.disposeAllViews());\n\n\t\t// Tier 8 / COMPOSITION-GUIDE §35: route publish through `mutate`\n\t\t// for centralized freeze + re-throw semantics. No audit log surface\n\t\t// (per Tier 8 γ-0): the topic's `events` log already records every\n\t\t// successful publish, so a separate audit Node would be redundant.\n\t\t// `freeze: false` because topic payloads can be large and per-publish\n\t\t// cost matters on hot paths.\n\t\tthis._publishImpl = mutate<[T], void, never>(\n\t\t\t(value): void => {\n\t\t\t\tthis._log.append(value);\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\t}\n\n\tpublish(value: T): void {\n\t\t// SENTINEL alignment (Wave B.1 Unit 11 lock): `undefined` is the\n\t\t// protocol-level \"never sent DATA\" sentinel — refusing it here\n\t\t// preserves `lastValue: Node<T | undefined>` semantics.\n\t\tif (value === undefined) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`TopicGraph \"${this.name}\": publish(undefined) is not allowed (spec §5.12 SENTINEL).`,\n\t\t\t);\n\t\t}\n\t\tthis._publishImpl(value);\n\t}\n\n\t/**\n\t * Wire one or more append-log storage tiers (Audit 4). Each tier receives\n\t * appended events per wave; rollback honors the wave-as-transaction model.\n\t *\n\t * Named `attachEventStorage` (not `attachStorage`) to avoid colliding with\n\t * the inherited {@link Graph.attachSnapshotStorage} which takes the\n\t * paired `AttachSnapshotTierPair[]` shape (Phase 14.6) — distinct\n\t * concerns, distinct surfaces.\n\t *\n\t * @returns Disposer.\n\t */\n\tattachEventStorage(\n\t\ttiers: readonly import(\"@graphrefly/pure-ts/extra\").AppendLogStorageTier<T>[],\n\t): () => void {\n\t\treturn this._log.attachStorage(tiers);\n\t}\n\n\tretained(): readonly T[] {\n\t\treturn this.events.cache as readonly T[];\n\t}\n\n\t/** Internal log bundle — used by TopicBridgeGraph for `attach`. */\n\tget _logBundle() {\n\t\treturn this._log;\n\t}\n}\n\nexport type SubscriptionOptions = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Starting cursor position.\n\t * @deprecated Use `from` instead.\n\t */\n\tcursor?: number;\n\t/**\n\t * Starting position for the subscription.\n\t * - `\"retained\"` (default) — cursor starts at 0; consumer sees all retained history.\n\t * - `\"now\"` — cursor starts at current topic length; consumer ignores history.\n\t * - `number` — explicit cursor position.\n\t */\n\tfrom?: \"now\" | \"retained\" | number;\n\t/**\n\t * When this signal node emits DATA, the subscription auto-advances cursor\n\t * to current `available.length`. Useful for \"ack everything when X happens\"\n\t * patterns. The reactive edge `advanceOn → cursor` is visible in `explain()`.\n\t */\n\tadvanceOn?: Node<unknown>;\n};\n\n/** Result of {@link SubscriptionGraph.pullAndAck}. */\nexport type PullAndAckResult<T> = {\n\titems: readonly T[];\n\tcursor: number;\n};\n\nexport class SubscriptionGraph<T> extends Graph {\n\treadonly cursor: Node<number>;\n\treadonly available: Node<readonly T[]>;\n\t/**\n\t * Reference to the upstream topic graph. Intentionally NOT mounted\n\t * under this subscription: a subscription is a VIEW over an\n\t * externally-owned topic. Double-mounting (e.g. hub-owned topic +\n\t * sub-mount here) would make either-side teardown leave the other\n\t * holding a dead reference. Node-level `derived([topicEvents], …)`\n\t * still wires the data dependency across graph boundaries. D1(e).\n\t */\n\treadonly topic: TopicGraph<T>;\n\n\tprivate _disposed = false;\n\tprivate readonly _ackImpl: (count: number | undefined) => number;\n\tprivate readonly _pullAndAckImpl: (limit: number | undefined) => PullAndAckResult<T>;\n\n\tconstructor(name: string, topicGraph: TopicGraph<T>, opts: SubscriptionOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\tthis.topic = topicGraph;\n\n\t\t// Resolve initial cursor from `from` option, falling back to legacy `cursor` option.\n\t\tlet initialCursor: number;\n\t\tif (opts.from !== undefined) {\n\t\t\tif (opts.from === \"retained\") {\n\t\t\t\tinitialCursor = 0;\n\t\t\t} else if (opts.from === \"now\") {\n\t\t\t\t// §28 sanctioned factory-time boundary read.\n\t\t\t\tinitialCursor = (topicGraph.events.cache as readonly T[]).length;\n\t\t\t} else {\n\t\t\t\tinitialCursor = requireNonNegativeInt(opts.from, \"subscription from\");\n\t\t\t}\n\t\t} else {\n\t\t\tinitialCursor = requireNonNegativeInt(opts.cursor ?? 0, \"subscription cursor\");\n\t\t}\n\n\t\tthis.cursor = this.state<number>(\"cursor\", initialCursor, {\n\t\t\tmeta: messagingMeta(\"subscription_cursor\"),\n\t\t});\n\n\t\t// B.1 Unit 12 lock: `available` depends directly on topic.events + cursor\n\t\t// via `view({ kind: \"fromCursor\" })`. No `source` passthrough node —\n\t\t// describe shows `topic::events → available` (cross-graph edge) and\n\t\t// `cursor → available` (local edge). One fewer node per subscription.\n\t\tthis.available = topicGraph._logBundle.view({ kind: \"fromCursor\", cursor: this.cursor });\n\t\tthis.add(this.available, { name: \"available\" });\n\t\tthis.addDisposer(keepalive(this.available));\n\n\t\t// Optional reactive auto-advance: when `advanceOn` emits a NEW DATA\n\t\t// (after construction), cursor advances by `available.length` atomically.\n\t\t// Edge visible in describe: advancePump depends on advanceOn.\n\t\t// `_advanceInitialized` guards against the initial push-on-subscribe fire\n\t\t// that would advance cursor before the user has a chance to read.\n\t\tif (opts.advanceOn !== undefined) {\n\t\t\tconst advanceOn = opts.advanceOn;\n\t\t\tlet advanceInitialized = false;\n\t\t\tconst advancePump = node<unknown>(\n\t\t\t\t[advanceOn],\n\t\t\t\t() => {\n\t\t\t\t\t// Skip the initial push-on-subscribe wave.\n\t\t\t\t\tif (!advanceInitialized) {\n\t\t\t\t\t\tadvanceInitialized = true;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (this._disposed) return;\n\t\t\t\t\tconst avail = this.available.cache as readonly T[];\n\t\t\t\t\tif (avail.length === 0) return;\n\t\t\t\t\tconst next = (this.cursor.cache as number) + avail.length;\n\t\t\t\t\tthis.cursor.emit(next);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"advancePump\",\n\t\t\t\t\tdescribeKind: \"effect\",\n\t\t\t\t\tmeta: messagingMeta(\"subscription_advance_pump\"),\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.add(advancePump, { name: \"advancePump\" });\n\t\t\tthis.addDisposer(keepalive(advancePump));\n\t\t}\n\n\t\t// Tier 8 / COMPOSITION-GUIDE §35: route ack + pullAndAck through\n\t\t// `mutate` for centralized freeze + re-throw semantics. No audit\n\t\t// log surface (per Tier 8 γ-0): the cursor's own emission stream already\n\t\t// records every advance, so a separate audit Node would be redundant.\n\t\t// `freeze: false` because count/limit are simple numbers; freezing is\n\t\t// pointless overhead. Disposed-checks stay outside the wrapper so a\n\t\t// no-op call doesn't unnecessarily run the wrapper.\n\t\tthis._ackImpl = mutate<[number | undefined], number, never>(\n\t\t\t(count): number => {\n\t\t\t\tconst available = this.available.cache as readonly T[];\n\t\t\t\tconst requested =\n\t\t\t\t\tcount === undefined\n\t\t\t\t\t\t? available.length\n\t\t\t\t\t\t: requireNonNegativeInt(count, \"subscription ack count\");\n\t\t\t\tconst step = Math.min(requested, available.length);\n\t\t\t\tif (step <= 0) return this.cursor.cache as number;\n\t\t\t\tconst next = (this.cursor.cache as number) + step;\n\t\t\t\t// F8: use emit() so the pipeline auto-prefixes DIRTY, runs equals\n\t\t\t\t// substitution, and produces a proper two-phase wave (the raw\n\t\t\t\t// `down([[DATA, next]])` path bypassed those contracts).\n\t\t\t\tthis.cursor.emit(next);\n\t\t\t\treturn next;\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\n\t\tthis._pullAndAckImpl = mutate<[number | undefined], PullAndAckResult<T>, never>(\n\t\t\t(limit): PullAndAckResult<T> => {\n\t\t\t\tconst available = this.available.cache as readonly T[];\n\t\t\t\tconst max =\n\t\t\t\t\tlimit === undefined\n\t\t\t\t\t\t? available.length\n\t\t\t\t\t\t: requireNonNegativeInt(limit, \"subscription pullAndAck limit\");\n\t\t\t\tconst items = available.slice(0, max);\n\t\t\t\tif (items.length === 0) return { items, cursor: this.cursor.cache as number };\n\t\t\t\tconst next = (this.cursor.cache as number) + items.length;\n\t\t\t\tthis.cursor.emit(next);\n\t\t\t\treturn { items, cursor: next };\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\t}\n\n\tack(count?: number): number {\n\t\tif (this._disposed) return this.cursor.cache as number;\n\t\treturn this._ackImpl(count);\n\t}\n\n\tpull(limit?: number): readonly T[] {\n\t\tif (this._disposed) return [];\n\t\tconst available = this.available.cache as readonly T[];\n\t\tconst max =\n\t\t\tlimit === undefined\n\t\t\t\t? available.length\n\t\t\t\t: requireNonNegativeInt(limit, \"subscription pull limit\");\n\t\treturn available.slice(0, max);\n\t}\n\n\t/**\n\t * Atomic pull-and-acknowledge. Returns `{ items, cursor }` where `cursor`\n\t * is the new cursor position after advancing. Under single-threaded JS the\n\t * snapshot and advance are atomic; PY callers use a per-subscription Lock.\n\t *\n\t * Replaces `pull(limit, { ack: true })`.\n\t */\n\tpullAndAck(limit?: number): PullAndAckResult<T> {\n\t\tif (this._disposed) return { items: [], cursor: this.cursor.cache as number };\n\t\treturn this._pullAndAckImpl(limit);\n\t}\n\n\t/**\n\t * Release internal subscriptions and mark the subscription torn-down.\n\t * Subsequent `pull`, `pullAndAck`, `ack` return empty / current cursor.\n\t * Emits COMPLETE on `cursor` so derived consumers (e.g. `available`) see\n\t * the termination signal. Also drains `addDisposer` callbacks (including\n\t * the `keepalive(advancePump)` subscription) so no keepalive leak occurs.\n\t */\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\t\tthis._disposed = true;\n\t\tthis.cursor.down([[COMPLETE]]);\n\t\t// m4: drain addDisposer callbacks to release the keepalive subscription.\n\t\tthis.destroy();\n\t}\n}\n\nexport type TopicBridgeOptions<TIn, TOut> = {\n\tgraph?: GraphOptions;\n\tcursor?: number;\n\tmaxPerPump?: number;\n\t/**\n\t * Optional transform/filter applied to each item before republishing.\n\t *\n\t * **At-most-once with silent drop:** when `map` returns `undefined`, the\n\t * input is consumed from the source cursor but NOT republished. Filtered\n\t * items are not retained for retry. If you need filter-with-retry\n\t * semantics, do the filtering in a downstream subscription on the bridged\n\t * output rather than in the `map` function.\n\t */\n\tmap?: (value: TIn) => TOut | undefined;\n};\n\nexport class TopicBridgeGraph<TIn, TOut = TIn> extends Graph {\n\tprivate readonly _sourceSub;\n\treadonly bridgedCount: Node<number>;\n\t/**\n\t * Emits each mapped batch as DATA — gives downstream observers a reactive\n\t * stream of bridged values. Also the link target for `target._log.attach`.\n\t */\n\treadonly output: Node<readonly TOut[]>;\n\n\tconstructor(\n\t\tname: string,\n\t\tsourceTopic: TopicGraph<TIn>,\n\t\ttargetTopic: TopicGraph<TOut>,\n\t\topts: TopicBridgeOptions<TIn, TOut> = {},\n\t) {\n\t\tsuper(name, opts.graph);\n\t\tthis._sourceSub = subscription<TIn>(`${name}-subscription`, sourceTopic, {\n\t\t\tcursor: opts.cursor,\n\t\t});\n\t\tthis.mount(\"subscription\", this._sourceSub);\n\n\t\tconst maxPerPump = Math.max(\n\t\t\t1,\n\t\t\trequireNonNegativeInt(opts.maxPerPump ?? DEFAULT_MAX_PER_PUMP, \"topic bridge maxPerPump\"),\n\t\t);\n\t\tconst mapValue = opts.map ?? ((value: TIn) => value as unknown as TOut);\n\n\t\t// Reactive output node: derives a mapped batch from `available`.\n\t\t// §24 compliant — output is a real derived edge, visible in describe.\n\t\t// Replaces imperative publish loop. Items where mapValue returns undefined\n\t\t// are filtered out (opt-out / filter).\n\t\tthis.output = node<readonly TOut[]>(\n\t\t\t[this._sourceSub.available],\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\tconst arr = data[0] as readonly TIn[];\n\t\t\t\tconst outBatch: TOut[] = [];\n\t\t\t\tconst take = Math.min(arr.length, maxPerPump);\n\t\t\t\tfor (let i = 0; i < take; i++) {\n\t\t\t\t\tconst mapped = mapValue(arr[i] as TIn);\n\t\t\t\t\tif (mapped !== undefined) outBatch.push(mapped);\n\t\t\t\t}\n\t\t\t\tactions.emit(outBatch);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"output\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: messagingMeta(\"topic_bridge_output\", { targetRef: targetTopic.name }),\n\t\t\t\tinitial: [],\n\t\t\t},\n\t\t);\n\t\tthis.add(this.output, { name: \"output\" });\n\t\tthis.addDisposer(keepalive(this.output));\n\n\t\t// bridgedCount: state node accumulating total bridged items.\n\t\t// Updated by ackPump after each batch — edge visible via ackPump dep on output.\n\t\tthis.bridgedCount = this.state<number>(\"bridgedCount\", 0, {\n\t\t\tmeta: messagingMeta(\"topic_bridge_count\"),\n\t\t});\n\t\tthis.addDisposer(keepalive(this.bridgedCount));\n\n\t\t// ackPump: effect that advances the subscription cursor and updates\n\t\t// bridgedCount after each batch. Runs after `output` settles.\n\t\t// Captures refs to `this.output`, `this._sourceSub`, `this.bridgedCount`\n\t\t// to avoid `this` inside the fn body.\n\t\tconst outputRef = this.output;\n\t\tconst subRef = this._sourceSub;\n\t\tconst countRef = this.bridgedCount;\n\t\tconst ackPump = this.effect(\n\t\t\t\"ackPump\",\n\t\t\t[\"output\"],\n\t\t\t() => {\n\t\t\t\tconst outBatch = outputRef.cache as readonly TOut[];\n\t\t\t\tif (outBatch.length === 0) return;\n\t\t\t\tconst availLen = (subRef.available.cache as readonly TIn[]).length;\n\t\t\t\tconst toAck = Math.min(availLen, maxPerPump);\n\t\t\t\tif (toAck > 0) {\n\t\t\t\t\tsubRef.ack(toAck);\n\t\t\t\t\tconst prev = (countRef.cache as number) ?? 0;\n\t\t\t\t\tcountRef.emit(prev + outBatch.length);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tmeta: messagingMeta(\"topic_bridge_ack_pump\"),\n\t\t\t},\n\t\t);\n\t\tthis.addDisposer(keepalive(ackPump));\n\n\t\t// Wire output into target topic's log reactively.\n\t\t// _attachArrayToLog subscribes to output and publishes each item to targetTopic.\n\t\t// Teardown: disposer runs before mount teardown.\n\t\tconst detach = _attachArrayToLog(this.output, targetTopic);\n\t\tthis.addDisposer(detach);\n\t}\n}\n\n/**\n * Attaches each element of an array-valued Node to a TopicGraph's log.\n * Every DATA emission on `source` appends all items in the array to `targetTopic`.\n * Returns a disposer.\n */\nfunction _attachArrayToLog<T>(source: Node<readonly T[]>, targetTopic: TopicGraph<T>): () => void {\n\treturn source.subscribe((msgs) => {\n\t\tfor (const m of msgs) {\n\t\t\tif (m[0] !== DATA) continue;\n\t\t\tconst arr = m[1] as readonly T[];\n\t\t\tif (arr.length === 0) continue;\n\t\t\tbatch(() => {\n\t\t\t\tfor (const v of arr) targetTopic.publish(v);\n\t\t\t});\n\t\t}\n\t});\n}\n\n// ── TopicRegistry ─────────────────────────────────────────────────────────\n\n/**\n * Private pure data structure managing a named set of {@link TopicGraph}\n * instances. Extracted from {@link MessagingHubGraph} for separation of\n * concerns (B.2 Unit 14 lock: D — split into TopicRegistry + facade).\n *\n * Reusable if other domain consumers (e.g. cqrs.eventLogs) want a shared\n * topic registry later.\n *\n * @internal\n */\nexport class TopicRegistry {\n\tprivate readonly _map = new Map<string, TopicGraph<unknown>>();\n\t/** Reactive monotonic version counter. Advances on topic create/remove. */\n\treadonly version: Node<number>;\n\n\tconstructor(versionNode: Node<number>) {\n\t\tthis.version = versionNode;\n\t}\n\n\tget size(): number {\n\t\treturn this._map.size;\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this._map.has(name);\n\t}\n\n\tget<T>(name: string): TopicGraph<T> | undefined {\n\t\treturn this._map.get(name) as TopicGraph<T> | undefined;\n\t}\n\n\tset<T>(name: string, t: TopicGraph<T>): void {\n\t\tthis._map.set(name, t as TopicGraph<unknown>);\n\t}\n\n\tdelete(name: string): boolean {\n\t\treturn this._map.delete(name);\n\t}\n\n\tkeys(): IterableIterator<string> {\n\t\treturn this._map.keys();\n\t}\n}\n\n// ── MessagingHubGraph ─────────────────────────────────────────────────────\n\nexport type MessagingHubOptions = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Default `TopicOptions` applied to every topic created via `topic(name)`\n\t * without explicit options. Per-call opts override. Default: `{}`\n\t * (unbounded retention per topic unless `retainedLimit` is set per call).\n\t */\n\tdefaultTopicOptions?: TopicOptions;\n};\n\n/**\n * Lazy Pulsar-inspired topic registry. Manages a named set of {@link TopicGraph}\n * instances with retention + cursor semantics. Topics are created on first\n * access; `removeTopic(name)` unmounts and tears down via {@link Graph.remove}.\n *\n * Internally delegates to {@link TopicRegistry} for topic map management\n * (B.2 Unit 14 lock: D facade split).\n *\n * **Relationship to `pubsub()` in `src/extra/pubsub.ts`:** `pubsub` is a\n * lightweight last-value state hub (no retention, no cursors). `MessagingHubGraph`\n * is the full messaging hub — retained message logs, cursor-based subscriptions,\n * and pattern-layer lifecycle management.\n *\n * @category patterns\n */\nexport class MessagingHubGraph extends Graph {\n\tprivate readonly _registry: TopicRegistry;\n\t/** Reactive monotonic version counter — advances on topic create/remove. */\n\treadonly version: Node<number>;\n\tprivate readonly _defaultTopicOptions: TopicOptions;\n\tprivate readonly _removeTopicImpl: (name: string) => void;\n\n\tconstructor(name: string, opts: MessagingHubOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\t// B.2 Unit 14 lock: promote _version → version: Node<number>.\n\t\tconst versionNode = this.state<number>(\"version\", 0, {\n\t\t\tmeta: messagingMeta(\"hub_version\"),\n\t\t});\n\t\tthis.version = versionNode;\n\t\tthis._registry = new TopicRegistry(versionNode);\n\t\t// P8: shallow-copy caller-provided defaults so post-construction\n\t\t// mutations by the caller don't leak into every future `topic()` call.\n\t\tthis._defaultTopicOptions = { ...(opts.defaultTopicOptions ?? {}) };\n\n\t\t// Tier 8 / COMPOSITION-GUIDE §35: route the registry-delete branch of\n\t\t// `removeTopic` through `mutate` for centralized re-throw\n\t\t// semantics. No audit log surface (per Tier 8 γ-0).\n\t\t// `freeze: false` because the only arg is a string name (freeze pointless).\n\t\t// **Closure-state caveat (γ-4):** the inner `try/finally` mutates\n\t\t// `_registry` (a `Map`) and emits the version bump. mutate has no\n\t\t// `batch()` frame, so reactive emissions are NOT rolled back on throw —\n\t\t// and even if it did, `Map.delete` on closure state is invisible to the\n\t\t// batch and can't be unwound. The pre-existing try/finally on\n\t\t// `Graph.remove` is what guarantees registry/version converge to a\n\t\t// consistent state when `remove()` throws; `mutate` adds nothing\n\t\t// to that contract beyond the re-throw.\n\t\tthis._removeTopicImpl = mutate<[string], void, never>(\n\t\t\t(topicName): void => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.remove(topicName); // unmounts, drops edges, tears down\n\t\t\t\t} finally {\n\t\t\t\t\tthis._registry.delete(topicName);\n\t\t\t\t\tconst cur = (this.version.cache as number) ?? 0;\n\t\t\t\t\tthis.version.emit(cur + 1);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\t}\n\n\t/** Number of topics currently in the hub. */\n\tget size(): number {\n\t\treturn this._registry.size;\n\t}\n\n\t/** Checks topic existence without creating. */\n\thas(name: string): boolean {\n\t\treturn this._registry.has(name);\n\t}\n\n\t/** Iterator over topic names. */\n\ttopicNames(): IterableIterator<string> {\n\t\treturn this._registry.keys();\n\t}\n\n\t/**\n\t * Returns the {@link TopicGraph} for `name`, creating lazily on first call.\n\t * Subsequent calls with the same name return the same instance (options on\n\t * repeat calls are ignored — the topic is already configured).\n\t */\n\ttopic<T = unknown>(name: string, opts?: TopicOptions): TopicGraph<T> {\n\t\tlet t = this._registry.get<T>(name);\n\t\tif (t === undefined) {\n\t\t\tconst effective: TopicOptions = { ...this._defaultTopicOptions, ...(opts ?? {}) };\n\t\t\tt = new TopicGraph<T>(name, effective);\n\t\t\tthis._registry.set(name, t);\n\t\t\tthis.mount(name, t);\n\t\t\tconst cur = (this.version.cache as number) ?? 0;\n\t\t\tthis.version.emit(cur + 1);\n\t\t}\n\t\treturn t;\n\t}\n\n\t/**\n\t * Publishes a value to the topic, lazily creating it on first publish.\n\t *\n\t * **Late-subscriber caveat:** the topic is created lazily, so subscribers\n\t * that attach AFTER a publish only see the retained window (governed by\n\t * `retainedLimit` on `TopicOptions` / `defaultTopicOptions`). If\n\t * `retainedLimit === 0` is set explicitly, early publishes are\n\t * effectively dropped — prefer an unset `retainedLimit` (unbounded\n\t * retention) or subscribe before publishing when late-subscribers matter.\n\t */\n\tpublish<T = unknown>(name: string, value: T): void {\n\t\tthis.topic<T>(name).publish(value);\n\t}\n\n\t/**\n\t * Bulk publish — issues all publishes inside one outer batch. New topics\n\t * are created on demand. No-op if `entries` yields nothing.\n\t *\n\t * **Iterable consumption (F6):** `entries` is consumed once (single-pass)\n\t * INSIDE the batch frame. If the iterator throws mid-way, the batch is\n\t * discarded and NO publishes are visible to subscribers (all-or-nothing).\n\t * Pass an array or `Set` for multi-shot callers.\n\t */\n\tpublishMany(entries: Iterable<[string, unknown]>): void {\n\t\t// P2: iterate inside batch — no `[...entries]` materialization so large\n\t\t// / infinite iterables don't OOM, and iterator throws are contained.\n\t\tbatch(() => {\n\t\t\tfor (const [name, value] of entries) {\n\t\t\t\tthis.topic(name).publish(value);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Creates a {@link SubscriptionGraph} over a named topic. The topic is\n\t * lazily created if missing. Subscription lifecycle is owned by the caller —\n\t * the hub does NOT mount the subscription.\n\t *\n\t * @param subName - Local name for the subscription graph.\n\t * @param topicName - Hub topic to subscribe to.\n\t * @param opts - `SubscriptionOptions` (initial cursor, etc.).\n\t */\n\tsubscribe<T = unknown>(\n\t\tsubName: string,\n\t\ttopicName: string,\n\t\topts?: SubscriptionOptions,\n\t): SubscriptionGraph<T> {\n\t\tconst t = this.topic<T>(topicName);\n\t\treturn new SubscriptionGraph<T>(subName, t, opts);\n\t}\n\n\t/**\n\t * Unmounts and tears down the topic's graph. Returns `true` if the topic\n\t * existed. Subscribers receive `TEARDOWN` via {@link Graph.remove}.\n\t *\n\t * **Closure-state caveat:** the registry mutation (`_registry.delete`) and\n\t * version bump happen in a `try/finally`, so registry/version converge to\n\t * a consistent state even when {@link Graph.remove} throws. `mutate`\n\t * does not roll back this mutation on throw — `Map.delete` on closure\n\t * state is invisible to any batch frame. The pre-existing try/finally is\n\t * load-bearing for that invariant.\n\t */\n\tremoveTopic(name: string): boolean {\n\t\tif (!this._registry.has(name)) return false;\n\t\t// P1 / P3: Graph.remove first — if it throws, `_registry` must NOT still\n\t\t// hold the broken half-disposed topic (otherwise the next\n\t\t// `hub.topic(name)` returns the corrupted reference). The `try/finally`\n\t\t// inside `_removeTopicImpl`'s action body preserves that invariant.\n\t\tthis._removeTopicImpl(name);\n\t\treturn true;\n\t}\n}\n\n/**\n * Creates a Pulsar-inspired topic graph (append-only retained stream + latest value).\n */\nexport function topic<T>(name: string, opts?: TopicOptions): TopicGraph<T> {\n\treturn new TopicGraph<T>(name, opts);\n}\n\n/**\n * Creates a lazy Pulsar-inspired messaging hub. Topics are created on first access\n * via `hub.topic(name)`; `hub.publish(name, value)` shortcuts through the registry.\n *\n * @example\n * ```ts\n * import { messagingHub } from \"@graphrefly/graphrefly/patterns/messaging\";\n *\n * const hub = messagingHub(\"main\", { defaultTopicOptions: { retainedLimit: 256 } });\n * hub.publish(\"orders\", { id: 1 });\n * hub.publishMany([[\"shipments\", { id: 1 }], [\"orders\", { id: 2 }]]);\n * const sub = hub.subscribe(\"orders-worker\", \"orders\", { cursor: 0 });\n * ```\n */\nexport function messagingHub(name: string, opts?: MessagingHubOptions): MessagingHubGraph {\n\treturn new MessagingHubGraph(name, opts);\n}\n\n/**\n * Creates a cursor-based subscription graph over a topic.\n */\nexport function subscription<T>(\n\tname: string,\n\ttopicGraph: TopicGraph<T>,\n\topts?: SubscriptionOptions,\n): SubscriptionGraph<T> {\n\treturn new SubscriptionGraph<T>(name, topicGraph, opts);\n}\n\n/**\n * Creates an autonomous cursor-based topic relay graph.\n *\n * When `opts.map` is provided, items where `map` returns `undefined` are\n * consumed from the source cursor but NOT republished (at-most-once with\n * silent drop). For filter-with-retry semantics, apply the filter in a\n * downstream subscription on the bridge's `output` node instead.\n */\nexport function topicBridge<TIn, TOut = TIn>(\n\tname: string,\n\tsourceTopic: TopicGraph<TIn>,\n\ttargetTopic: TopicGraph<TOut>,\n\topts?: TopicBridgeOptions<TIn, TOut>,\n): TopicBridgeGraph<TIn, TOut> {\n\treturn new TopicBridgeGraph<TIn, TOut>(name, sourceTopic, targetTopic, opts);\n}\n\n// ── LogProjector ──────────────────────────────────────────────────────────\n//\n// Promotion 2 (memo:Re Story 6.4 back-derivation, design-review-locked\n// 2026-05-16). A cursor-driven projector over a log/topic where a per-item\n// sink can poison-fail. memo:Re hand-rolled `createProjectorCursor` and got\n// the failure mode wrong: a bare `catch { break; }` conflated a *transient*\n// condition (a native feature not yet available → retry later) with a\n// *poison* entry (will never project) → a permanent head-of-line block of\n// every newer entry. The fix is a real, observable, subscribable dead-letter\n// topic + a typed failure policy.\n//\n// Built ON `subscription()` (TopicGraph source) / the bundle's `fromCursor`\n// view (ReactiveLogBundle source) — the same hardened cursor machinery\n// `SubscriptionGraph` itself uses — so the consumer never hand-rolls cursor\n// persistence/durability (the Med \"durability skew\" + parse-leniency findings\n// dissolve). Scope is deliberately bounded: `halt | deadLetter` only, NO\n// programmatic retry/backoff (compose a downstream subscription on\n// `deadLetter` for that — avoids the §44 wrap-imperative-as-primitive trap).\n\nexport type ProjectorPoisonPolicy = \"halt\" | \"deadLetter\";\n\n/** A poison entry routed to {@link LogProjectorGraph.deadLetter}. */\nexport type DeadLetterEntry<T> = {\n\treadonly item: T;\n\t/** `Error.message` (or `String(thrown)`) from the failing `sink`. */\n\treadonly error: string;\n\t/** Absolute 0-based log position of the poisoned item. */\n\treadonly cursorPos: number;\n};\n\nexport type LogProjectorOptions<T> = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Per-item side-effecting projection.\n\t *\n\t * **Transient-vs-poison contract (read this).** The projector cannot tell\n\t * \"this will project later\" from \"this will never project\" — only the sink\n\t * knows. So the contract is:\n\t * - **Return normally** (sync return, or a resolved Promise) → the item is\n\t * *handled*; the cursor advances. Use this for the success path AND for\n\t * any transient/skip condition the sink wants to no-op (e.g. an optional\n\t * native feature not yet available — return without throwing; the entry\n\t * is simply considered done for this projector).\n\t * - **Throw / reject** → the item is *poison*; the {@link onPoison} policy\n\t * applies. Do NOT throw for transient conditions or the item is\n\t * dead-lettered (or halts the stream).\n\t */\n\treadonly sink: (item: T) => void | Promise<void>;\n\t/**\n\t * Behaviour when `sink` throws/rejects on an item (poison):\n\t * - `\"halt\"` (default) — stop projecting at the poison item; the cursor\n\t * does NOT advance past it (head-of-line stop). `position` stalls — that\n\t * is the observable signal. No retry/backoff is built in.\n\t * - `\"deadLetter\"` — publish `{ item, error, cursorPos }` to the\n\t * {@link LogProjectorGraph.deadLetter} topic and advance past it, so\n\t * newer entries still project.\n\t */\n\treadonly onPoison?: ProjectorPoisonPolicy;\n\t/**\n\t * Initial cursor position. `\"retained\"` (default) projects all history;\n\t * `\"now\"` skips existing entries (project only future appends); a number\n\t * starts at that absolute index.\n\t */\n\treadonly from?: \"retained\" | \"now\" | number;\n\t/** Retained window for the `deadLetter` topic. Default 1024. */\n\treadonly deadLetterRetainedLimit?: number;\n};\n\n/**\n * Cursor-driven projector over a {@link TopicGraph} or {@link ReactiveLogBundle}.\n *\n * Topology (mounted on the returned graph):\n * - `subscription` (TopicGraph source only) — the hardened\n * {@link SubscriptionGraph} cursor; or a local `cursor` state + the\n * bundle's `fromCursor` view (ReactiveLogBundle source).\n * - `drain` — an `effect` that, on every not-yet-projected wave, schedules a\n * serialized async pass that calls `sink` per item (mirrors the\n * `SubscriptionGraph.ackPump` / `TopicBridge.ackPump` effect precedent +\n * memo:Re's `inFlight` chain — one wave processed at a time).\n * - `deadLetter` — a real {@link TopicGraph} (NOT a callback): poison entries\n * are observable in `describe()` and subscribable, instead of memo:Re's\n * silent `break`.\n *\n * **No imperative reads.** Observe `position` (cursor) / subscribe to\n * `deadLetter`. `idle()` is a test-only await convenience.\n *\n * @category patterns\n */\nexport class LogProjectorGraph<T> extends Graph {\n\t/** Reactive count of fully-projected entries (the cursor; read-only). */\n\treadonly position: Node<number>;\n\t/**\n\t * Poison entries (populated when `onPoison: \"deadLetter\"`). A real topic —\n\t * subscribable + visible in `describe()`.\n\t */\n\treadonly deadLetter: TopicGraph<DeadLetterEntry<T>>;\n\tprivate _inFlight: Promise<void> = Promise.resolve();\n\n\tconstructor(\n\t\tname: string,\n\t\tsource: TopicGraph<T> | ReactiveLogBundle<T>,\n\t\topts: LogProjectorOptions<T>,\n\t) {\n\t\tsuper(name, opts.graph);\n\t\tconst onPoison: ProjectorPoisonPolicy = opts.onPoison ?? \"halt\";\n\t\tconst sink = opts.sink;\n\n\t\tconst dl = new TopicGraph<DeadLetterEntry<T>>(`${name}_dead_letter`, {\n\t\t\tretainedLimit: opts.deadLetterRetainedLimit ?? DEFAULT_TOPIC_RETAINED_LIMIT,\n\t\t});\n\t\tthis.mount(\"deadLetter\", dl);\n\t\tthis.deadLetter = dl;\n\n\t\t// Uniform cursor surface over either source kind. A TopicGraph reuses\n\t\t// the hardened SubscriptionGraph cursor; a ReactiveLogBundle uses a\n\t\t// local state cursor + the bundle's `fromCursor` view — the very\n\t\t// machinery SubscriptionGraph is itself built on.\n\t\tlet available: Node<readonly T[]>;\n\t\tlet cursorBase: () => number;\n\t\tlet advance: (n: number) => void;\n\n\t\tif (source instanceof TopicGraph) {\n\t\t\tconst sub = new SubscriptionGraph<T>(`${name}_subscription`, source, {\n\t\t\t\tfrom: opts.from ?? \"retained\",\n\t\t\t});\n\t\t\tthis.mount(\"subscription\", sub);\n\t\t\tavailable = sub.available;\n\t\t\tthis.position = sub.cursor;\n\t\t\tcursorBase = () => sub.cursor.cache as number;\n\t\t\tadvance = (n) => {\n\t\t\t\tif (n > 0) sub.ack(n);\n\t\t\t};\n\t\t} else {\n\t\t\tconst log = source;\n\t\t\tlet initialCursor: number;\n\t\t\tif (opts.from === \"now\") {\n\t\t\t\tinitialCursor = log.size;\n\t\t\t} else if (typeof opts.from === \"number\") {\n\t\t\t\tinitialCursor = requireNonNegativeInt(opts.from, \"logProjector from\");\n\t\t\t} else {\n\t\t\t\tinitialCursor = 0; // \"retained\"\n\t\t\t}\n\t\t\tconst cursor = this.state<number>(\"cursor\", initialCursor, {\n\t\t\t\tmeta: messagingMeta(\"log_projector_cursor\"),\n\t\t\t});\n\t\t\tthis.position = cursor;\n\t\t\tcursorBase = () => cursor.cache as number;\n\t\t\tavailable = log.view({ kind: \"fromCursor\", cursor });\n\t\t\tadvance = (n) => {\n\t\t\t\tif (n > 0) cursor.emit((cursor.cache as number) + n);\n\t\t\t};\n\t\t}\n\n\t\t// `halt` is a HARD LATCH (QA-C): on the first poison under `onPoison:\n\t\t// \"halt\"`, `sink` has been invoked exactly once on the poison item;\n\t\t// the projector then freezes — no further `sink` calls, no rescheduled\n\t\t// drains — so a later unrelated append cannot re-invoke the (possibly\n\t\t// side-effecting, non-idempotent) sink on the poison. The stalled\n\t\t// `position` + frozen stream IS the observable signal. v1 has no retry\n\t\t// (compose downstream of `deadLetter` if you need that).\n\t\tlet halted = false;\n\n\t\t// Serialized async drain. One wave processed at a time (the inFlight\n\t\t// chain) so an async `sink` cannot interleave; the cursor is advanced\n\t\t// ONCE per pass after the captured snapshot is processed (mirrors\n\t\t// memo:Re's `runPump` + the ackPump effect precedent).\n\t\tconst runDrain = async (): Promise<void> => {\n\t\t\tif (halted) return;\n\t\t\tconst snapshot = (available.cache as readonly T[] | undefined) ?? [];\n\t\t\tif (snapshot.length === 0) return;\n\t\t\tlet consumed = 0;\n\t\t\tfor (let i = 0; i < snapshot.length; i += 1) {\n\t\t\t\tconst item = snapshot[i] as T;\n\t\t\t\ttry {\n\t\t\t\t\tawait sink(item);\n\t\t\t\t\tconsumed += 1;\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst error = e instanceof Error ? e.message : String(e);\n\t\t\t\t\tif (onPoison === \"deadLetter\") {\n\t\t\t\t\t\tdl.publish({ item, error, cursorPos: cursorBase() + consumed });\n\t\t\t\t\t\tconsumed += 1; // advance past the poison\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// \"halt\" — latch and stop here; do NOT advance past the\n\t\t\t\t\t// poison, do NOT re-invoke `sink` on any later wave.\n\t\t\t\t\thalted = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (consumed > 0) advance(consumed);\n\t\t};\n\t\tconst schedule = (): void => {\n\t\t\tif (halted) return; // latched — no further drains\n\t\t\tthis._inFlight = this._inFlight.then(runDrain, runDrain);\n\t\t};\n\n\t\t// Effect: every wave that exposes not-yet-projected entries schedules a\n\t\t// drain. Side-effecting (sink / cursor advance / dead-letter publish) →\n\t\t// an `effect` node, not a pure derived (COMPOSITION-GUIDE §35; exact\n\t\t// `SubscriptionGraph.advancePump` precedent — never calls `emit` on its\n\t\t// own node, kept warm via `keepalive`).\n\t\tconst drain = node<unknown>(\n\t\t\t[available],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst b = batchData[0];\n\t\t\t\tconst snap = (b != null && b.length > 0 ? b.at(-1) : ctx.prevData[0]) as\n\t\t\t\t\t| readonly T[]\n\t\t\t\t\t| undefined;\n\t\t\t\tif (snap && snap.length > 0) schedule();\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"drain\",\n\t\t\t\tdescribeKind: \"effect\",\n\t\t\t\tmeta: messagingMeta(\"log_projector_drain\"),\n\t\t\t},\n\t\t);\n\t\tthis.add(drain, { name: \"drain\" });\n\t\tthis.addDisposer(keepalive(drain));\n\t}\n\n\t/**\n\t * Await any in-flight drain pass. **Test convenience only** — the canonical\n\t * reactive observable is {@link LogProjectorGraph.position}.\n\t */\n\tidle(): Promise<void> {\n\t\treturn this._inFlight;\n\t}\n}\n\n/**\n * Creates a cursor-driven log/topic projector with a typed poison-failure\n * policy and an observable dead-letter topic.\n *\n * @example\n * ```ts\n * import { logProjector, topic } from \"@graphrefly/graphrefly\";\n *\n * const events = topic<Doc>(\"docs\");\n * const proj = logProjector(\"indexer\", events, {\n * sink: async (doc) => { await index(doc); }, // throw ⇒ poison\n * onPoison: \"deadLetter\",\n * });\n * proj.deadLetter.events.subscribe(/* observe poison *​/);\n * ```\n *\n * @remarks\n * **Use an UNBOUNDED source for durable / long-lived projection.** The cursor\n * is an absolute index; the underlying `fromCursor` view slices the source's\n * *current* entries array. A `TopicGraph` with a `retainedLimit` (or a\n * `ReactiveLogBundle` with `maxSize`) trims its head, so an absolute cursor\n * past the retained window reads the wrong offset (skips entries or stalls).\n * This is inherited `subscription()` / `fromCursor` behaviour, not specific to\n * `logProjector` — but it matters here because projection is typically\n * long-lived. For unbounded projection pass a source with NO `retainedLimit` /\n * `maxSize` (memo:Re's `changesetLog` is unbounded ✓).\n *\n * @category patterns\n */\nexport function logProjector<T>(\n\tname: string,\n\tsource: TopicGraph<T> | ReactiveLogBundle<T>,\n\topts: LogProjectorOptions<T>,\n): LogProjectorGraph<T> {\n\treturn new LogProjectorGraph<T>(name, source, opts);\n}\n"],"mappings":"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","names":["batch"]}