@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
@@ -1333,6 +1333,11 @@ var AgentLoopGraph = class extends import_graph3.Graph {
1333
1333
  chat.append("assistant", response.content, {
1334
1334
  toolCalls: response.toolCalls
1335
1335
  });
1336
+ if (capReached && hasToolCalls) {
1337
+ for (const tc of response.toolCalls) {
1338
+ chat.appendToolResult(tc.id, "[tool call denied: maxTurns reached]");
1339
+ }
1340
+ }
1336
1341
  });
1337
1342
  },
1338
1343
  { describeKind: "effect" }
@@ -1354,6 +1359,45 @@ var AgentLoopGraph = class extends import_graph3.Graph {
1354
1359
  },
1355
1360
  { describeKind: "effect" }
1356
1361
  );
1362
+ const effFullDeny = gatedToolCallsNode !== toolCallsRaw ? (0, import_core9.node)(
1363
+ [toolCallsRaw, gatedToolCallsNode],
1364
+ (batchData) => {
1365
+ if (latestAborted) return;
1366
+ const rawBatch = batchData[0];
1367
+ const gatedBatch = batchData[1];
1368
+ const rawCalls = rawBatch != null && rawBatch.length > 0 ? rawBatch.at(-1) : null;
1369
+ if (rawCalls == null || rawCalls.length === 0) return;
1370
+ const gatedCalls = gatedBatch != null && gatedBatch.length > 0 ? gatedBatch.at(-1) : null;
1371
+ const allowedIds = gatedCalls === null ? null : new Set(gatedCalls.map((c) => c.id));
1372
+ const denied = allowedIds === null ? rawCalls : rawCalls.filter((c) => !allowedIds.has(c.id));
1373
+ if (denied.length === 0) return;
1374
+ const isFullDeny = gatedCalls === null;
1375
+ (0, import_core9.batch)(() => {
1376
+ if (isFullDeny) {
1377
+ statusNode.emit(latestTurn >= maxTurns ? "done" : "thinking");
1378
+ }
1379
+ for (const c of denied)
1380
+ chat.appendToolResult(c.id, "[tool call denied by interceptor]");
1381
+ });
1382
+ },
1383
+ {
1384
+ name: "fullDenyRecovery",
1385
+ describeKind: "effect",
1386
+ meta: aiMeta("agent_full_deny_recovery"),
1387
+ // MUST be explicit: the core `node()` default is
1388
+ // `partial: false` (node.ts `opts.partial ?? false`),
1389
+ // and `gatedToolCallsNode` only ever emits RESOLVED on the
1390
+ // full-deny path (never DATA/terminal). Spec R2.7.0
1391
+ // (DS-2.7.A, 2026-05-19) holds the `partial: false`
1392
+ // first-run gate until every dep has contributed real
1393
+ // DATA, so a `partial: false` effFullDeny would hold the
1394
+ // fn FOREVER. Spec R2.7.2 = `partial: true` disables the
1395
+ // gate; the fn body's `denied`-subtraction guard above
1396
+ // covers the SENTINEL slot per the R2.7.2 author
1397
+ // contract.
1398
+ partial: true
1399
+ }
1400
+ ) : null;
1357
1401
  let latestStatus = statusNode.cache ?? "idle";
1358
1402
  const statusSub = statusNode.subscribe((msgs) => {
1359
1403
  for (const m of msgs) if (m[0] === import_core9.DATA) latestStatus = m[1];
@@ -1373,6 +1417,7 @@ var AgentLoopGraph = class extends import_graph3.Graph {
1373
1417
  );
1374
1418
  const kaResponse = (0, import_extra5.keepalive)(effResponse);
1375
1419
  const kaResults = (0, import_extra5.keepalive)(effResults);
1420
+ const kaFullDeny = effFullDeny ? (0, import_extra5.keepalive)(effFullDeny) : null;
1376
1421
  const kaAbort = (0, import_extra5.keepalive)(effAbort);
1377
1422
  this._terminalResult = (0, import_core9.node)(
1378
1423
  [statusNode, lastResponseState],
@@ -1425,6 +1470,7 @@ var AgentLoopGraph = class extends import_graph3.Graph {
1425
1470
  this.addDisposer(statusSub);
1426
1471
  this.addDisposer(kaResponse);
1427
1472
  this.addDisposer(kaResults);
1473
+ if (kaFullDeny) this.addDisposer(kaFullDeny);
1428
1474
  this.addDisposer(kaAbort);
1429
1475
  this._disposeRunWiring = () => {
1430
1476
  };
@@ -1724,6 +1770,26 @@ function createAuditLog(opts) {
1724
1770
  }
1725
1771
  return log;
1726
1772
  }
1773
+ function readonlyAuditLog(log) {
1774
+ return Object.freeze({
1775
+ get entries() {
1776
+ return log.entries;
1777
+ },
1778
+ get size() {
1779
+ return log.size;
1780
+ },
1781
+ get lastValue() {
1782
+ return log.lastValue;
1783
+ },
1784
+ get mutationLog() {
1785
+ return log.mutationLog;
1786
+ },
1787
+ at: log.at.bind(log),
1788
+ withLatest: log.withLatest.bind(log),
1789
+ view: log.view.bind(log),
1790
+ scan: log.scan.bind(log)
1791
+ });
1792
+ }
1727
1793
  function deepFreeze(value) {
1728
1794
  if (value === null || typeof value !== "object" || Object.isFrozen(value)) return value;
1729
1795
  for (const k of Object.keys(value)) {
@@ -4129,31 +4195,40 @@ var RATE_LIMITER_INITIAL_STATE = Object.freeze({
4129
4195
  pendingCount: 0,
4130
4196
  paused: false
4131
4197
  });
4198
+ function validateRateLimiterOpts(o) {
4199
+ if (o.maxEvents <= 0) throw new RangeError("maxEvents must be > 0");
4200
+ if (o.windowNs <= 0) throw new RangeError("windowNs must be > 0");
4201
+ if (o.maxBuffer === void 0) {
4202
+ throw new RangeError(
4203
+ "rateLimiter requires explicit maxBuffer (use Infinity to opt in to unbounded)"
4204
+ );
4205
+ }
4206
+ const isUnbounded = o.maxBuffer === Infinity;
4207
+ if (!isUnbounded && (!Number.isInteger(o.maxBuffer) || o.maxBuffer < 1)) {
4208
+ throw new RangeError("maxBuffer must be a positive integer (or Infinity for unbounded)");
4209
+ }
4210
+ }
4211
+ function resolveInitialReactiveOpts(optsNode) {
4212
+ const cached = optsNode.cache;
4213
+ if (cached === void 0) {
4214
+ throw new RangeError(
4215
+ "rateLimiter: reactive (Node-form) opts must carry a cached value at construction \u2014 seed the opts Node with `initial`. Mode (bounded vs unbounded) and the initial cap are LOCKED from `.cache` at construction and the swap handler rejects mode toggles; an un-seeded Node would silently lock bounded `maxBuffer: 1` (D4)."
4216
+ );
4217
+ }
4218
+ return cached;
4219
+ }
4132
4220
  function rateLimiter(source, opts) {
4133
4221
  const isReactive = isNode(opts);
4134
- if (!isReactive) {
4135
- const o = opts;
4136
- if (o.maxEvents <= 0) throw new RangeError("maxEvents must be > 0");
4137
- if (o.windowNs <= 0) throw new RangeError("windowNs must be > 0");
4138
- if (o.maxBuffer === void 0) {
4139
- throw new RangeError(
4140
- "rateLimiter requires explicit maxBuffer (use Infinity to opt in to unbounded)"
4141
- );
4142
- }
4143
- const isUnbounded0 = o.maxBuffer === Infinity;
4144
- if (!isUnbounded0 && (!Number.isInteger(o.maxBuffer) || o.maxBuffer < 1)) {
4145
- throw new RangeError("maxBuffer must be a positive integer (or Infinity for unbounded)");
4146
- }
4147
- }
4148
- const initialOpts = isReactive ? opts.cache : opts;
4149
- const initialMaxBuffer = initialOpts?.maxBuffer;
4222
+ const initialOpts = isReactive ? resolveInitialReactiveOpts(opts) : opts;
4223
+ validateRateLimiterOpts(initialOpts);
4224
+ const initialMaxBuffer = initialOpts.maxBuffer;
4150
4225
  const isUnbounded = initialMaxBuffer === Infinity;
4151
4226
  const out = (0, import_core22.node)(
4152
4227
  (_data, a) => {
4153
- let maxEvents = initialOpts?.maxEvents ?? 1;
4154
- let windowNs = initialOpts?.windowNs ?? NS_PER_SEC;
4155
- let maxBuffer = initialMaxBuffer ?? 1;
4156
- let onOverflow = initialOpts?.onOverflow ?? "drop-newest";
4228
+ let maxEvents = initialOpts.maxEvents;
4229
+ let windowNs = initialOpts.windowNs;
4230
+ let maxBuffer = initialMaxBuffer;
4231
+ let onOverflow = initialOpts.onOverflow ?? "drop-newest";
4157
4232
  let refillPerSec = maxEvents * NS_PER_SEC / windowNs;
4158
4233
  let tokenTimeNs = NS_PER_SEC / refillPerSec;
4159
4234
  let bucket = tokenBucket(maxEvents, refillPerSec);
@@ -4982,6 +5057,10 @@ var PipelineGraph = class extends import_graph9.Graph {
4982
5057
  {
4983
5058
  name,
4984
5059
  describeKind: "derived",
5060
+ // Spec §2.7 R2.7.1 (DS-2.7.A). fn must fire on
5061
+ // upstream-COMPLETE/ERROR-only-without-DATA so the
5062
+ // teardown-decision record + downstream terminal forward run.
5063
+ terminalAsRealInput: true,
4985
5064
  meta: meta("approval_gate", opts.meta)
4986
5065
  }
4987
5066
  );
@@ -5199,6 +5278,11 @@ var PipelineGraph = class extends import_graph9.Graph {
5199
5278
  describeKind: "derived",
5200
5279
  completeWhenDepsComplete: opts.completeWhenDepsComplete ?? !(mode === "completed" || mode === "terminal"),
5201
5280
  errorWhenDepsError: !(mode === "errored" || mode === "terminal"),
5281
+ // Spec §2.7 R2.7.1 (DS-2.7.A). `catch` exists to fire on a
5282
+ // source terminal — its whole job is `recover(cause, …)` on a
5283
+ // terminal-only wave. Without this opt-in the gate holds and
5284
+ // the recover branch never runs.
5285
+ terminalAsRealInput: true,
5202
5286
  meta: meta("catch", opts.meta)
5203
5287
  }
5204
5288
  );
@@ -5401,7 +5485,10 @@ var JobQueueGraph = class extends import_graph11.Graph {
5401
5485
  depth;
5402
5486
  /** Audit log of every queue mutation (Audit 2). */
5403
5487
  events;
5404
- /** Alias for {@link JobQueueGraph.events} — Audit 2 `.audit` duplication. */
5488
+ /**
5489
+ * Read-only view of {@link JobQueueGraph.events} — Audit 2 `.audit`
5490
+ * duplication; M7 (cannot mutate the canonical log via the alias).
5491
+ */
5405
5492
  audit;
5406
5493
  // Tier 8 / COMPOSITION-GUIDE §35: mutate wrappers for the four
5407
5494
  // single-record mutation methods. Assigned in the constructor (NOT via
@@ -5442,7 +5529,7 @@ var JobQueueGraph = class extends import_graph11.Graph {
5442
5529
  retainedLimit: 1024,
5443
5530
  graph: this
5444
5531
  });
5445
- this.audit = this.events;
5532
+ this.audit = readonlyAuditLog(this.events);
5446
5533
  this._seqCursor = registerCursor(this, "seq", 0);
5447
5534
  this._enqueueImpl = mutate(
5448
5535
  (payload, enqueueOpts) => {
@@ -5495,7 +5582,7 @@ var JobQueueGraph = class extends import_graph11.Graph {
5495
5582
  }
5496
5583
  );
5497
5584
  this._nackImpl = mutate(
5498
- (id, job, requeue) => {
5585
+ (id, job, requeue, _error) => {
5499
5586
  if (requeue) {
5500
5587
  this._jobs.set(id, { ...job, state: "queued" });
5501
5588
  this._pending.append(id);
@@ -5508,12 +5595,16 @@ var JobQueueGraph = class extends import_graph11.Graph {
5508
5595
  log: this.events,
5509
5596
  seq: this._seqCursor,
5510
5597
  freeze: false,
5511
- onSuccessRecord: ([id, job], _r, { t_ns, seq }) => ({
5598
+ onSuccessRecord: ([id, job, requeue, error], _r, { t_ns, seq }) => ({
5512
5599
  action: "nack",
5513
5600
  id,
5514
5601
  attempts: job.attempts,
5515
5602
  t_ns,
5516
- seq: seq ?? 0
5603
+ seq: seq ?? 0,
5604
+ // F-CATCH D-3: surface the failure cause on the no-requeue
5605
+ // (terminal-failure) nack only. A requeue nack is a retry,
5606
+ // not a failure, so it carries no error.
5607
+ ...!requeue && error !== void 0 ? { error } : {}
5517
5608
  })
5518
5609
  }
5519
5610
  );
@@ -5585,10 +5676,20 @@ var JobQueueGraph = class extends import_graph11.Graph {
5585
5676
  this._ackImpl(id, job);
5586
5677
  return true;
5587
5678
  }
5679
+ /**
5680
+ * Negatively-acknowledge an inflight job.
5681
+ *
5682
+ * @param opts.requeue - `true` (default) returns the job to the queue for
5683
+ * retry; `false` drops it permanently (terminal failure).
5684
+ * @param opts.error - Optional failure cause for a `requeue: false` nack.
5685
+ * Recorded on the emitted `"nack"` {@link JobEvent} as `error` so the
5686
+ * reason a job failed is recoverable from the event stream (F-CATCH
5687
+ * D-3). Ignored when `requeue` is `true` (a retry is not a failure).
5688
+ */
5588
5689
  nack(id, opts = {}) {
5589
5690
  const job = this._jobs.get(id);
5590
5691
  if (!job || job.state !== "inflight") return false;
5591
- this._nackImpl(id, job, opts.requeue ?? true);
5692
+ this._nackImpl(id, job, opts.requeue ?? true, opts.error);
5592
5693
  return true;
5593
5694
  }
5594
5695
  /**
@@ -5754,10 +5855,10 @@ var JobFlowGraph = class extends import_graph11.Graph {
5754
5855
  let result;
5755
5856
  try {
5756
5857
  result = workFn(job, { signal: ac.signal });
5757
- } catch {
5858
+ } catch (err) {
5758
5859
  inflight.delete(entry);
5759
5860
  inflightCounter?.emit(inflight.size);
5760
- current.nack(job.id, { requeue: false });
5861
+ current.nack(job.id, { requeue: false, error: err });
5761
5862
  processed += 1;
5762
5863
  continue;
5763
5864
  }
@@ -5808,7 +5909,7 @@ var JobFlowGraph = class extends import_graph11.Graph {
5808
5909
  } else if (m[0] === import_core26.ERROR) {
5809
5910
  settled = true;
5810
5911
  cleanupSub();
5811
- current.nack(job.id, { requeue: false });
5912
+ current.nack(job.id, { requeue: false, error: m[1] });
5812
5913
  return;
5813
5914
  }
5814
5915
  }
@@ -6064,7 +6165,18 @@ var HarnessGraph = class extends import_graph12.Graph {
6064
6165
  * (use {@link unrouted}), and the internal `triage-output` fan-in.
6065
6166
  */
6066
6167
  queueTopics;
6067
- /** Strategy model — `auditedSuccessTracker` keyed by `StrategyKey`. */
6168
+ /**
6169
+ * Strategy model — `auditedSuccessTracker` keyed by `StrategyKey`.
6170
+ *
6171
+ * **Ownership (EC10/EC15).** Owned by the harness: it is mounted as a
6172
+ * child subgraph (`harness.mount("strategy", strategy)`), so its disposal
6173
+ * cascades from `harness.destroy()` via the mount lifecycle. Do **not**
6174
+ * call `strategy.destroy()` independently — the harness's `triage-input`
6175
+ * node and (when `opts.priority` is set) `buildPriorityScores` hold
6176
+ * cross-graph deps on `strategy.entries`; destroying the strategy out of
6177
+ * band staleness those nodes while the rest of the loop keeps running.
6178
+ * Read/subscribe freely; let the harness own the lifecycle.
6179
+ */
6068
6180
  strategy;
6069
6181
  /** Global retry count across all items (circuit breaker). Reactive — subscribable. */
6070
6182
  totalRetries;
@@ -6526,6 +6638,16 @@ var import_core28 = require("@graphrefly/pure-ts/core");
6526
6638
  var import_extra19 = require("@graphrefly/pure-ts/extra");
6527
6639
  var import_graph13 = require("@graphrefly/pure-ts/graph");
6528
6640
  var RefineLoopGraph = class extends import_graph13.Graph {
6641
+ /**
6642
+ * Best candidate so far. **SENTINEL until the first iteration settles** —
6643
+ * `loop.best.cache` is `undefined` (not `null`) before any iteration
6644
+ * produces a best, and a degenerate empty-candidate iteration leaves the
6645
+ * prior best in place rather than wiping it. Consumers guard with
6646
+ * `=== undefined` (spec §3 SENTINEL), not `== null`. (Anti-pattern sweep
6647
+ * 2026-05-18: dropped the `initial: null` eager-placeholder; `null` is
6648
+ * reserved for the per-iteration {@link Iteration.best} data field where an
6649
+ * empty batch is a valid domain value.)
6650
+ */
6529
6651
  best;
6530
6652
  /**
6531
6653
  * Best score so far. Pseudo-private (`_score`) to avoid colliding with any
@@ -6576,8 +6698,7 @@ var RefineLoopGraph = class extends import_graph13.Graph {
6576
6698
  });
6577
6699
  this.add(strategyNode, { name: "strategy" });
6578
6700
  const lastFeedbackState = (0, import_core28.node)([], {
6579
- name: "lastFeedback",
6580
- initial: null
6701
+ name: "lastFeedback"
6581
6702
  });
6582
6703
  this.add(lastFeedbackState, { name: "lastFeedback" });
6583
6704
  const prevCandidatesState = (0, import_core28.node)([], {
@@ -6596,7 +6717,7 @@ var RefineLoopGraph = class extends import_graph13.Graph {
6596
6717
  // append-style; reactive consumers want every push
6597
6718
  });
6598
6719
  this.add(historyState, { name: "history" });
6599
- const bestState = (0, import_core28.node)([], { name: "best", initial: null });
6720
+ const bestState = (0, import_core28.node)([], { name: "best" });
6600
6721
  this.add(bestState, { name: "best" });
6601
6722
  const scoreState = (0, import_core28.node)([], { name: "score", initial: Number.NEGATIVE_INFINITY });
6602
6723
  this.add(scoreState, { name: "score" });
@@ -6620,7 +6741,7 @@ var RefineLoopGraph = class extends import_graph13.Graph {
6620
6741
  this.decide = hubDecideTopic;
6621
6742
  this._pauseState = pauseState;
6622
6743
  let latestStrategy = initialStrategy;
6623
- let latestFeedback = null;
6744
+ let latestFeedback;
6624
6745
  let latestPrevCandidates = [];
6625
6746
  this.addDisposer(
6626
6747
  strategyNode.subscribe((msgs) => {
@@ -6641,7 +6762,7 @@ var RefineLoopGraph = class extends import_graph13.Graph {
6641
6762
  iterationTrigger,
6642
6763
  (iter) => {
6643
6764
  const strat = latestStrategy;
6644
- const isSeed = iter === 0 || latestFeedback == null;
6765
+ const isSeed = iter === 0 || latestFeedback === void 0;
6645
6766
  return (0, import_core28.node)(
6646
6767
  [],
6647
6768
  (_data, actions) => {
@@ -7033,7 +7154,7 @@ var RefineLoopGraph = class extends import_graph13.Graph {
7033
7154
  decision = "paused";
7034
7155
  }
7035
7156
  (0, import_core28.batch)(() => {
7036
- bestState.emit(best);
7157
+ if (best !== null) bestState.emit(best);
7037
7158
  scoreState.emit(fb.score);
7038
7159
  historyState.emit(nextHistory);
7039
7160
  budgetState.emit(nextBudget);
@@ -7925,11 +8046,12 @@ var GuardedExecutionGraph = class extends import_graph17.Graph {
7925
8046
  this.addDisposer((0, import_extra24.keepalive)(this.scope));
7926
8047
  const scopedHandle = target.describe({
7927
8048
  reactive: true,
7928
- // F8 (Tier 5.2): `_actorNode` is `Node<Actor | null>`. The cast is
7929
- // safe at runtime per the `resolveActorOption` null-tolerance
7930
- // contract `null` / `undefined` cache is treated as "no scoping"
7931
- // (full visibility). See `scopedDescribeNode` for the matching
7932
- // per-call site and graph.ts § "Cache-undefined semantics".
8049
+ // F8 (resolved 2026-05-18): `GraphDescribeOptions.actor` is now
8050
+ // `Actor | Node<Actor | null>`, so `_actorNode` (a
8051
+ // `Node<Actor | null>`) passes without a cast. A `null`/`undefined`
8052
+ // cache resolves to "no scoping" (full visibility) per
8053
+ // `resolveActorOption`. See graph.ts § "Cache-undefined/null
8054
+ // semantics".
7933
8055
  actor: this._actorNode,
7934
8056
  reactiveName: "scopedDescribe"
7935
8057
  });
@@ -7975,11 +8097,11 @@ var GuardedExecutionGraph = class extends import_graph17.Graph {
7975
8097
  const actorNode = actorOverride == null ? this._actorNode : isNode4(actorOverride) ? actorOverride : (0, import_core32.node)([], { name: "actor_override", initial: actorOverride });
7976
8098
  const handle = this._target.describe({
7977
8099
  reactive: true,
7978
- // `_actorNode` is `Node<Actor | null>`. The `as Node<Actor>` cast is
7979
- // safe at runtime: `_describeReactive` resolves the actor via
7980
- // `resolveActorOption`, which treats `null`/`undefined` cache as
7981
- // "no scoping" (full visibility). Documented in graph.ts §
7982
- // "Cache-undefined semantics."
8100
+ // F8 (resolved 2026-05-18): `actor` accepts `Node<Actor | null>`
8101
+ // directly. `actorNode` is `_actorNode` (`Node<Actor | null>`) or
8102
+ // an override (`Node<Actor>`/`Node<Actor | null>`); both assign
8103
+ // without a cast. `_describeReactive` resolves via
8104
+ // `resolveActorOption` (null/undefined cache → no scoping).
7983
8105
  actor: actorNode,
7984
8106
  ...opts ?? {}
7985
8107
  });
@@ -8080,7 +8202,6 @@ var ResilientPipelineGraph = class extends import_graph18.Graph {
8080
8202
  } else {
8081
8203
  const rateOpts = {
8082
8204
  ...opts.rateLimit,
8083
- maxBuffer: opts.rateLimit.maxBuffer ?? Infinity,
8084
8205
  meta: domainMeta("resilient", "rate-limit")
8085
8206
  };
8086
8207
  const bundle = rateLimiter(current, rateOpts);