@graphrefly/graphrefly 0.20.0 → 0.22.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 (97) hide show
  1. package/README.md +27 -8
  2. package/dist/chunk-44HD4BTA.js +47 -0
  3. package/dist/chunk-44HD4BTA.js.map +1 -0
  4. package/dist/chunk-7TAQJHQV.js +103 -0
  5. package/dist/chunk-7TAQJHQV.js.map +1 -0
  6. package/dist/chunk-BLD3IFYF.js +6827 -0
  7. package/dist/chunk-BLD3IFYF.js.map +1 -0
  8. package/dist/{chunk-IAPLC4NR.js → chunk-EQUZ5NLD.js} +34 -45
  9. package/dist/chunk-EQUZ5NLD.js.map +1 -0
  10. package/dist/{chunk-OOA2UTXF.js → chunk-IR3KMOLX.js} +358 -128
  11. package/dist/chunk-IR3KMOLX.js.map +1 -0
  12. package/dist/{chunk-5PSVTDNZ.js → chunk-MQBQOFDS.js} +20 -11
  13. package/dist/chunk-MQBQOFDS.js.map +1 -0
  14. package/dist/chunk-NXC35KC5.js +2417 -0
  15. package/dist/chunk-NXC35KC5.js.map +1 -0
  16. package/dist/chunk-QA3RP5NH.js +2234 -0
  17. package/dist/chunk-QA3RP5NH.js.map +1 -0
  18. package/dist/chunk-RHI3GHZW.js +115 -0
  19. package/dist/chunk-RHI3GHZW.js.map +1 -0
  20. package/dist/{chunk-2L5J6RPM.js → chunk-TH6COGOP.js} +15 -26
  21. package/dist/chunk-TH6COGOP.js.map +1 -0
  22. package/dist/compat/nestjs/index.cjs +3366 -2259
  23. package/dist/compat/nestjs/index.cjs.map +1 -1
  24. package/dist/compat/nestjs/index.d.cts +6 -4
  25. package/dist/compat/nestjs/index.d.ts +6 -4
  26. package/dist/compat/nestjs/index.js +8 -8
  27. package/dist/core/index.cjs +1611 -1218
  28. package/dist/core/index.cjs.map +1 -1
  29. package/dist/core/index.d.cts +3 -2
  30. package/dist/core/index.d.ts +3 -2
  31. package/dist/core/index.js +37 -34
  32. package/dist/extra/index.cjs +7726 -6470
  33. package/dist/extra/index.cjs.map +1 -1
  34. package/dist/extra/index.d.cts +4 -4
  35. package/dist/extra/index.d.ts +4 -4
  36. package/dist/extra/index.js +57 -30
  37. package/dist/graph/index.cjs +3107 -2216
  38. package/dist/graph/index.cjs.map +1 -1
  39. package/dist/graph/index.d.cts +5 -3
  40. package/dist/graph/index.d.ts +5 -3
  41. package/dist/graph/index.js +24 -11
  42. package/dist/graph-DFr0diXB.d.ts +1128 -0
  43. package/dist/graph-ab1yPwIB.d.cts +1128 -0
  44. package/dist/{index-8a605sg9.d.ts → index-BHm3Ba5q.d.ts} +2 -2
  45. package/dist/{index-SFzE_KTa.d.cts → index-BbYZma8G.d.ts} +1697 -586
  46. package/dist/{index-DuN3bhtm.d.ts → index-BvWfZCTt.d.cts} +1697 -586
  47. package/dist/index-C9z6rU9P.d.cts +388 -0
  48. package/dist/{index-BjtlNirP.d.cts → index-D36MAQ3f.d.ts} +4 -4
  49. package/dist/{index-VHA43cGP.d.cts → index-DLE1Sp-L.d.cts} +2 -2
  50. package/dist/{index-CgSiUouz.d.ts → index-DrJq9B1T.d.cts} +4 -4
  51. package/dist/index-DsGxLfwL.d.ts +315 -0
  52. package/dist/index-Dy04P4W3.d.cts +315 -0
  53. package/dist/index-HdJx_BjO.d.ts +388 -0
  54. package/dist/index.cjs +9919 -7900
  55. package/dist/index.cjs.map +1 -1
  56. package/dist/index.d.cts +415 -42
  57. package/dist/index.d.ts +415 -42
  58. package/dist/index.js +1064 -639
  59. package/dist/index.js.map +1 -1
  60. package/dist/meta--fr9sxRM.d.cts +41 -0
  61. package/dist/meta-n3FoVWML.d.ts +41 -0
  62. package/dist/node-C5UD5MGq.d.cts +1146 -0
  63. package/dist/node-C5UD5MGq.d.ts +1146 -0
  64. package/dist/{observable-DcBwQY7t.d.ts → observable-CQRBtEbq.d.ts} +1 -1
  65. package/dist/{observable-C8Kx_O6k.d.cts → observable-DWydVy5b.d.cts} +1 -1
  66. package/dist/patterns/reactive-layout/index.cjs +3102 -2132
  67. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  68. package/dist/patterns/reactive-layout/index.d.cts +5 -3
  69. package/dist/patterns/reactive-layout/index.d.ts +5 -3
  70. package/dist/patterns/reactive-layout/index.js +5 -4
  71. package/dist/storage-Bew05Xy6.d.cts +182 -0
  72. package/dist/storage-C9fZfMfM.d.ts +182 -0
  73. package/package.json +2 -1
  74. package/dist/chunk-2L5J6RPM.js.map +0 -1
  75. package/dist/chunk-3N2Y6PCR.js +0 -2117
  76. package/dist/chunk-3N2Y6PCR.js.map +0 -1
  77. package/dist/chunk-5PSVTDNZ.js.map +0 -1
  78. package/dist/chunk-BJAOEU4D.js +0 -6269
  79. package/dist/chunk-BJAOEU4D.js.map +0 -1
  80. package/dist/chunk-IAPLC4NR.js.map +0 -1
  81. package/dist/chunk-OOA2UTXF.js.map +0 -1
  82. package/dist/chunk-PGEU5MEH.js +0 -162
  83. package/dist/chunk-PGEU5MEH.js.map +0 -1
  84. package/dist/chunk-R2LPZIY2.js +0 -111
  85. package/dist/chunk-R2LPZIY2.js.map +0 -1
  86. package/dist/chunk-WZ2Z2CRV.js +0 -32
  87. package/dist/chunk-WZ2Z2CRV.js.map +0 -1
  88. package/dist/chunk-XYL3GLB3.js +0 -1631
  89. package/dist/chunk-XYL3GLB3.js.map +0 -1
  90. package/dist/graph-KsTe57nI.d.cts +0 -750
  91. package/dist/graph-mILUUqW8.d.ts +0 -750
  92. package/dist/index-B2SvPEbc.d.ts +0 -257
  93. package/dist/index-BHfg_Ez3.d.ts +0 -629
  94. package/dist/index-Bc_diYYJ.d.cts +0 -629
  95. package/dist/index-UudxGnzc.d.cts +0 -257
  96. package/dist/meta-BnG7XAaE.d.cts +0 -778
  97. package/dist/meta-BnG7XAaE.d.ts +0 -778
package/dist/index.js CHANGED
@@ -1,20 +1,19 @@
1
1
  import {
2
2
  CircuitOpenError,
3
- DictCheckpointAdapter,
4
- FileCheckpointAdapter,
5
- MemoryCheckpointAdapter,
6
3
  NS_PER_MS,
7
4
  NS_PER_SEC,
8
- SqliteCheckpointAdapter,
5
+ NativeIndexBackend,
6
+ NativeListBackend,
7
+ NativeMapBackend,
8
+ NativePubSubBackend,
9
+ RateLimiterOverflowError,
9
10
  TimeoutError,
10
11
  audit,
11
12
  buffer,
12
13
  bufferCount,
13
14
  bufferTime,
14
- cache,
15
15
  cascadingCache,
16
16
  catchError,
17
- checkpointNodeValue,
18
17
  checkpointToRedis,
19
18
  checkpointToS3,
20
19
  circuitBreaker,
@@ -24,19 +23,24 @@ import {
24
23
  concatMap,
25
24
  constant,
26
25
  createTransport,
26
+ csvRows,
27
27
  debounce,
28
28
  debounceTime,
29
29
  decorrelatedJitter,
30
30
  delay,
31
31
  deserializeError,
32
+ dictStorage,
32
33
  distill,
33
34
  distinctUntilChanged,
34
35
  elementAt,
35
36
  exhaustMap,
36
37
  exponential,
38
+ externalBundle,
39
+ externalProducer,
37
40
  extra_exports,
38
41
  fallback,
39
42
  fibonacci,
43
+ fileStorage,
40
44
  filter,
41
45
  find,
42
46
  first,
@@ -46,6 +50,8 @@ import {
46
50
  fromDrizzle,
47
51
  fromGitHook,
48
52
  fromHTTP,
53
+ fromHTTPPoll,
54
+ fromHTTPStream,
49
55
  fromIDBRequest,
50
56
  fromIDBTransaction,
51
57
  fromKafka,
@@ -59,19 +65,25 @@ import {
59
65
  fromPulsar,
60
66
  fromRabbitMQ,
61
67
  fromRedisStream,
68
+ fromSSE,
62
69
  fromSqlite,
70
+ fromSqliteCursor,
63
71
  fromStatsD,
64
72
  fromSyslog,
65
73
  fromWebSocket,
74
+ fromWebSocketReconnect,
66
75
  fromWebhook,
76
+ indexedDbStorage,
67
77
  interval,
68
78
  last,
69
79
  linear,
70
80
  lru,
71
81
  map,
82
+ memoryStorage,
72
83
  merge,
73
84
  mergeMap,
74
85
  nameToSignal,
86
+ ndjsonRows,
75
87
  pairwise,
76
88
  parsePrometheusText,
77
89
  parseStatsD,
@@ -83,20 +95,19 @@ import {
83
95
  reactiveIndex,
84
96
  reactiveList,
85
97
  reactiveMap,
98
+ reactiveSink,
86
99
  reduce,
87
100
  repeat,
88
101
  rescue,
89
102
  resolveBackoffPreset,
90
- restoreGraphCheckpoint,
91
- restoreGraphCheckpointIndexedDb,
92
103
  retry,
104
+ retrySource,
93
105
  sample,
94
- saveGraphCheckpoint,
95
- saveGraphCheckpointIndexedDb,
96
106
  scan,
97
107
  serializeError,
98
108
  signalToName,
99
109
  skip,
110
+ sqliteStorage,
100
111
  switchMap,
101
112
  take,
102
113
  takeUntil,
@@ -104,11 +115,11 @@ import {
104
115
  tap,
105
116
  throttle,
106
117
  throttleTime,
107
- tieredStorage,
108
118
  timeout,
109
119
  toCSV,
110
120
  toClickHouse,
111
121
  toFile,
122
+ toHTTP,
112
123
  toKafka,
113
124
  toLoki,
114
125
  toMongo,
@@ -116,14 +127,15 @@ import {
116
127
  toPostgres,
117
128
  toPulsar,
118
129
  toRabbitMQ,
130
+ toReadableStream,
119
131
  toRedisStream,
120
132
  toS3,
121
133
  toSSE,
134
+ toSSEBytes,
122
135
  toSqlite,
123
136
  toTempo,
124
137
  toWebSocket,
125
138
  tokenBucket,
126
- tokenTracker,
127
139
  valve,
128
140
  verifiable,
129
141
  window,
@@ -136,27 +148,22 @@ import {
136
148
  workerBridge,
137
149
  workerSelf,
138
150
  zip
139
- } from "./chunk-BJAOEU4D.js";
151
+ } from "./chunk-BLD3IFYF.js";
140
152
  import {
141
153
  cqrs_exports,
142
154
  domainMeta,
143
155
  nestjs_exports,
144
- trackingKey
145
- } from "./chunk-5PSVTDNZ.js";
156
+ trackingKey,
157
+ tryIncrementBounded
158
+ } from "./chunk-MQBQOFDS.js";
146
159
  import {
147
- DEFAULT_DOWN,
148
- bridge,
149
160
  core_exports
150
- } from "./chunk-PGEU5MEH.js";
161
+ } from "./chunk-RHI3GHZW.js";
151
162
  import {
152
- JsonCodec,
153
- createDagCborCodec,
154
- createDagCborZstdCodec,
155
- graph_exports,
156
- negotiateCodec,
157
- replayWAL
158
- } from "./chunk-R2LPZIY2.js";
163
+ graph_exports
164
+ } from "./chunk-44HD4BTA.js";
159
165
  import {
166
+ NativeLogBackend,
160
167
  cached,
161
168
  createWatermarkController,
162
169
  empty,
@@ -174,7 +181,6 @@ import {
174
181
  fromTimer,
175
182
  globToRegExp,
176
183
  keepalive,
177
- logSlice,
178
184
  matchesAnyPattern,
179
185
  matchesCron,
180
186
  never,
@@ -188,72 +194,89 @@ import {
188
194
  throwError,
189
195
  toArray,
190
196
  toObservable
191
- } from "./chunk-OOA2UTXF.js";
192
- import {
193
- ResettableTimer
194
- } from "./chunk-WZ2Z2CRV.js";
197
+ } from "./chunk-IR3KMOLX.js";
195
198
  import {
196
199
  analyzeAndMeasure,
197
200
  computeLineBreaks,
198
201
  reactive_layout_exports
199
- } from "./chunk-IAPLC4NR.js";
202
+ } from "./chunk-EQUZ5NLD.js";
200
203
  import {
201
204
  GRAPH_META_SEGMENT,
202
205
  Graph,
206
+ OVERHEAD,
207
+ SIZEOF_SYMBOL,
208
+ diffForWAL,
203
209
  graphProfile,
204
210
  reachable,
205
211
  sizeof
206
- } from "./chunk-3N2Y6PCR.js";
212
+ } from "./chunk-NXC35KC5.js";
207
213
  import {
208
214
  describeNode,
209
215
  resolveDescribeFields
210
- } from "./chunk-2L5J6RPM.js";
216
+ } from "./chunk-TH6COGOP.js";
217
+ import {
218
+ ResettableTimer
219
+ } from "./chunk-7TAQJHQV.js";
211
220
  import {
212
- CLEANUP_RESULT,
213
221
  COMPLETE,
222
+ COMPLETE_MSG,
223
+ COMPLETE_ONLY_BATCH,
214
224
  DATA,
215
225
  DEFAULT_ACTOR,
216
226
  DIRTY,
217
- DynamicNodeImpl,
227
+ DIRTY_MSG,
228
+ DIRTY_ONLY_BATCH,
229
+ ENVELOPE_VERSION,
218
230
  ERROR,
231
+ GraphReFlyConfig,
219
232
  GuardDenied,
220
233
  INVALIDATE,
234
+ INVALIDATE_MSG,
235
+ INVALIDATE_ONLY_BATCH,
236
+ JsonCodec,
237
+ NodeImpl,
221
238
  PAUSE,
222
239
  RESOLVED,
240
+ RESOLVED_MSG,
241
+ RESOLVED_ONLY_BATCH,
223
242
  RESUME,
224
243
  START,
244
+ START_MSG,
225
245
  TEARDOWN,
246
+ TEARDOWN_MSG,
247
+ TEARDOWN_ONLY_BATCH,
226
248
  __export,
227
249
  accessHintForGuard,
228
250
  advanceVersion,
251
+ autoTrackNode,
229
252
  batch,
230
- cleanupResult,
253
+ configure,
254
+ createDagCborCodec,
255
+ createDagCborZstdCodec,
231
256
  createVersioning,
257
+ decodeEnvelope,
258
+ defaultConfig,
232
259
  defaultHash,
233
260
  derived,
234
261
  downWithBatch,
235
262
  dynamicNode,
236
263
  effect,
264
+ encodeEnvelope,
237
265
  isBatching,
238
- isKnownMessageType,
239
- isLocalOnly,
240
- isPhase2Message,
241
- isTerminalMessage,
242
266
  isV1,
243
- knownMessageTypes,
244
- messageTier,
245
267
  monotonicNs,
246
268
  node,
247
269
  normalizeActor,
248
- partitionForBatch,
249
270
  pipe,
250
271
  policy,
251
272
  policyFromRules,
252
273
  producer,
253
- propagatesToMeta,
274
+ registerBuiltinCodecs,
275
+ registerBuiltins,
276
+ replayWAL,
254
277
  state,
255
278
  wallClockNs
256
- } from "./chunk-XYL3GLB3.js";
279
+ } from "./chunk-QA3RP5NH.js";
257
280
 
258
281
  // src/compat/index.ts
259
282
  var compat_exports = {};
@@ -285,7 +308,7 @@ function atom(initialOrRead, writeOrOptions, options) {
285
308
  return createPrimitiveAtom(initialOrRead, writeOrOptions);
286
309
  }
287
310
  function pull(n) {
288
- let val = n.get();
311
+ let val = n.cache;
289
312
  let err;
290
313
  const unsub = n.subscribe((msgs) => {
291
314
  for (const [t, v] of msgs) {
@@ -305,15 +328,19 @@ function createPrimitiveAtom(initial, options) {
305
328
  });
306
329
  return {
307
330
  get: () => {
308
- if (n.status === "disconnected") {
331
+ if (n.status === "sentinel") {
309
332
  return pull(n);
310
333
  }
311
- return n.get();
334
+ return n.cache;
312
335
  },
313
- set: (value) => n.down([[DATA, value]]),
336
+ // Use `n.emit` (not raw `n.down`) so writes go through the framed
337
+ // emit pipeline: bundle() auto-prefixes DIRTY (diamond-safe wave
338
+ // coordination) and the equals check folds same-value writes to
339
+ // RESOLVED (no spurious subscriber fires).
340
+ set: (value) => n.emit(value),
314
341
  update: (fn) => {
315
- const current = n.status === "disconnected" ? pull(n) : n.get();
316
- n.down([[DATA, fn(current)]]);
342
+ const current = n.status === "sentinel" ? pull(n) : n.cache;
343
+ n.emit(fn(current));
317
344
  },
318
345
  subscribe: (cb) => {
319
346
  let initial2 = true;
@@ -334,13 +361,13 @@ function createPrimitiveAtom(initial, options) {
334
361
  };
335
362
  }
336
363
  function createDerivedAtom(read, write, options) {
337
- const n = dynamicNode(
338
- (get) => read((a) => {
364
+ const n = autoTrackNode(
365
+ (track) => read((a) => {
339
366
  const dn = a._node;
340
- if (dn.status === "disconnected") {
367
+ if (dn.status === "sentinel") {
341
368
  pull(dn);
342
369
  }
343
- return get(dn);
370
+ return track(dn);
344
371
  }),
345
372
  {
346
373
  ...options,
@@ -350,10 +377,10 @@ function createDerivedAtom(read, write, options) {
350
377
  );
351
378
  const result = {
352
379
  get: () => {
353
- if (n.status === "disconnected") {
380
+ if (n.status === "sentinel") {
354
381
  return pull(n);
355
382
  }
356
- return n.get();
383
+ return n.cache;
357
384
  },
358
385
  subscribe: (cb) => {
359
386
  let initial = true;
@@ -378,7 +405,7 @@ function createDerivedAtom(read, write, options) {
378
405
  const writable = result;
379
406
  writable.set = (value) => write(getFn, setFn, value);
380
407
  writable.update = (fn) => {
381
- const current = n.status === "disconnected" ? pull(n) : n.get();
408
+ const current = n.status === "sentinel" ? pull(n) : n.cache;
382
409
  return write(getFn, setFn, fn(current));
383
410
  };
384
411
  return writable;
@@ -451,7 +478,7 @@ function createStore(node2, extra = {}) {
451
478
  return store;
452
479
  }
453
480
  function pull2(n) {
454
- let val = n.get();
481
+ let val = n.cache;
455
482
  let err;
456
483
  const unsub = n.subscribe((msgs) => {
457
484
  for (const [t, v] of msgs) {
@@ -464,10 +491,10 @@ function pull2(n) {
464
491
  return val;
465
492
  }
466
493
  function getVal(n) {
467
- if (n.status === "disconnected") {
494
+ if (n.status === "sentinel") {
468
495
  return pull2(n);
469
496
  }
470
- return n.get();
497
+ return n.cache;
471
498
  }
472
499
  function atom2(initial) {
473
500
  const n = state(initial, {
@@ -475,19 +502,26 @@ function atom2(initial) {
475
502
  resetOnTeardown: true
476
503
  });
477
504
  return createStore(n, {
478
- set: (value) => n.down([[DATA, value]])
505
+ // `n.emit` routes through the framed pipeline: `bundle()` auto-
506
+ // prefixes `[DIRTY]` (diamond-safe wave coordination) and the
507
+ // node's `equals` (default `Object.is`) folds same-value writes
508
+ // into `RESOLVED`. Matches nanostores' documented Object.is
509
+ // dedup semantics without any custom check.
510
+ set: (value) => n.emit(value)
479
511
  });
480
512
  }
481
513
  function computed(stores, fn) {
482
514
  const storeArray = Array.isArray(stores) ? stores : [stores];
515
+ const depNodes = storeArray.map((s) => s._node);
483
516
  const n = dynamicNode(
484
- (get) => {
517
+ depNodes,
518
+ (track) => {
485
519
  const vals = storeArray.map((s) => {
486
520
  const node2 = s._node;
487
- if (node2.status === "disconnected") {
521
+ if (node2.status === "sentinel") {
488
522
  pull2(node2);
489
523
  }
490
- return get(node2);
524
+ return track(node2);
491
525
  });
492
526
  return fn(...vals);
493
527
  },
@@ -506,10 +540,14 @@ function map2(initial) {
506
540
  equals: () => false
507
541
  });
508
542
  return createStore(n, {
509
- set: (value) => n.down([[DATA, value]]),
543
+ // `map`'s state node is configured with `equals: () => false`
544
+ // above, so every `emit` produces DATA (even for same-key same-
545
+ // value sets). The `emit` path is still required for diamond
546
+ // coordination (`[DIRTY]` auto-prefix via `bundle()`).
547
+ set: (value) => n.emit(value),
510
548
  setKey: (key, value) => {
511
549
  const current = getVal(n);
512
- n.down([[DATA, { ...current, [key]: value }]]);
550
+ n.emit({ ...current, [key]: value });
513
551
  }
514
552
  });
515
553
  }
@@ -570,8 +608,8 @@ function useSubscribe(node2) {
570
608
  unsub();
571
609
  };
572
610
  },
573
- () => node2.get(),
574
- () => node2.get()
611
+ () => node2.cache,
612
+ () => node2.cache
575
613
  // Server snapshot
576
614
  );
577
615
  }
@@ -591,12 +629,12 @@ function useSubscribeRecord(keysNode, factory) {
591
629
  const store = useMemo(() => {
592
630
  const computeSnap = () => {
593
631
  const snap = {};
594
- const keys = keysNode.get() ?? [];
632
+ const keys = keysNode.cache ?? [];
595
633
  for (const key of keys) {
596
634
  const nodes = factoryRef.current(key);
597
635
  const values = {};
598
636
  for (const field of Object.keys(nodes)) {
599
- values[field] = nodes[field].get();
637
+ values[field] = nodes[field].cache;
600
638
  }
601
639
  snap[key] = values;
602
640
  }
@@ -627,10 +665,10 @@ function useSubscribeRecord(keysNode, factory) {
627
665
  if (!disposed) onStoreChange();
628
666
  };
629
667
  const keysUnsub = keysNode.subscribe((msgs) => {
630
- const hasSettled = msgs.some((m) => messageTier(m[0]) >= 3);
631
- if (!disposed && hasSettled) sync(keysNode.get() ?? []);
668
+ const hasSettled = msgs.some((m) => m[0] === DATA || m[0] === RESOLVED);
669
+ if (!disposed && hasSettled) sync(keysNode.cache ?? []);
632
670
  });
633
- sync(keysNode.get() ?? []);
671
+ sync(keysNode.cache ?? []);
634
672
  return () => {
635
673
  disposed = true;
636
674
  keysUnsub();
@@ -650,7 +688,7 @@ __export(signals_exports, {
650
688
  });
651
689
  var trackingStack = [];
652
690
  function pull3(n) {
653
- let val = n.get();
691
+ let val = n.cache;
654
692
  const unsub = n.subscribe((msgs) => {
655
693
  for (const [t, v] of msgs) {
656
694
  if (t === DATA) val = v;
@@ -674,15 +712,15 @@ var SignalState = class {
674
712
  get() {
675
713
  const tracker = trackingStack[trackingStack.length - 1];
676
714
  if (tracker) {
677
- if (this._node.status === "disconnected") {
715
+ if (this._node.status === "sentinel") {
678
716
  pull3(this._node);
679
717
  }
680
718
  return tracker(this._node);
681
719
  }
682
- if (this._node.status === "disconnected") {
720
+ if (this._node.status === "sentinel") {
683
721
  return pull3(this._node);
684
722
  }
685
- return this._node.get();
723
+ return this._node.cache;
686
724
  }
687
725
  set(value) {
688
726
  if (this._equals(this.get(), value)) return;
@@ -695,9 +733,9 @@ var SignalComputed = class {
695
733
  /** @internal */
696
734
  _node;
697
735
  constructor(computation, opts) {
698
- this._node = dynamicNode(
699
- (get) => {
700
- trackingStack.push(get);
736
+ this._node = autoTrackNode(
737
+ (track) => {
738
+ trackingStack.push(track);
701
739
  try {
702
740
  return computation();
703
741
  } finally {
@@ -715,15 +753,15 @@ var SignalComputed = class {
715
753
  get() {
716
754
  const tracker = trackingStack[trackingStack.length - 1];
717
755
  if (tracker) {
718
- if (this._node.status === "disconnected") {
756
+ if (this._node.status === "sentinel") {
719
757
  pull3(this._node);
720
758
  }
721
759
  return tracker(this._node);
722
760
  }
723
- if (this._node.status === "disconnected") {
761
+ if (this._node.status === "sentinel") {
724
762
  return pull3(this._node);
725
763
  }
726
- return this._node.get();
764
+ return this._node.cache;
727
765
  }
728
766
  };
729
767
  var Signal = {
@@ -767,9 +805,9 @@ __export(solid_exports, {
767
805
  });
768
806
  import { createSignal, getOwner, onCleanup } from "solid-js";
769
807
  function useSubscribe2(node2) {
770
- const [value, setValue] = createSignal(node2.get(), { equals: false });
808
+ const [value, setValue] = createSignal(node2.cache, { equals: false });
771
809
  const unsub = node2.subscribe(() => {
772
- setValue(() => node2.get());
810
+ setValue(() => node2.cache);
773
811
  });
774
812
  if (getOwner()) {
775
813
  onCleanup(() => unsub());
@@ -796,11 +834,11 @@ function useSubscribeRecord2(keysNode, factory) {
796
834
  };
797
835
  const buildSnapshot = () => {
798
836
  const snap = {};
799
- for (const key of keysNode.get() ?? []) {
837
+ for (const key of keysNode.cache ?? []) {
800
838
  const nodes = factory(key);
801
839
  const values = {};
802
840
  for (const field of Object.keys(nodes)) {
803
- values[field] = nodes[field].get();
841
+ values[field] = nodes[field].cache;
804
842
  }
805
843
  snap[key] = values;
806
844
  }
@@ -820,11 +858,11 @@ function useSubscribeRecord2(keysNode, factory) {
820
858
  setValue(() => buildSnapshot());
821
859
  };
822
860
  const keysUnsub = keysNode.subscribe((msgs) => {
823
- if (msgs.some((m) => messageTier(m[0]) >= 3)) {
824
- sync(keysNode.get() ?? []);
861
+ if (msgs.some((m) => m[0] === DATA || m[0] === RESOLVED)) {
862
+ sync(keysNode.cache ?? []);
825
863
  }
826
864
  });
827
- sync(keysNode.get() ?? []);
865
+ sync(keysNode.cache ?? []);
828
866
  if (getOwner()) {
829
867
  onCleanup(() => {
830
868
  keysUnsub();
@@ -849,9 +887,9 @@ function useSubscribe3(node2) {
849
887
  return {
850
888
  subscribe(run) {
851
889
  const unsub = node2.subscribe(() => {
852
- run(node2.get());
890
+ run(node2.cache);
853
891
  });
854
- run(node2.get());
892
+ run(node2.cache);
855
893
  return unsub;
856
894
  }
857
895
  };
@@ -860,16 +898,16 @@ function useStore3(node2) {
860
898
  return {
861
899
  subscribe(run) {
862
900
  const unsub = node2.subscribe(() => {
863
- run(node2.get());
901
+ run(node2.cache);
864
902
  });
865
- run(node2.get());
903
+ run(node2.cache);
866
904
  return unsub;
867
905
  },
868
906
  set(value) {
869
907
  node2.down([[DIRTY], [DATA, value]]);
870
908
  },
871
909
  update(updater) {
872
- const next = updater(node2.get());
910
+ const next = updater(node2.cache);
873
911
  node2.down([[DIRTY], [DATA, next]]);
874
912
  }
875
913
  };
@@ -884,11 +922,11 @@ function useSubscribeRecord3(keysNode, factory) {
884
922
  };
885
923
  const buildSnapshot = () => {
886
924
  const snap = {};
887
- for (const key of keysNode.get() ?? []) {
925
+ for (const key of keysNode.cache ?? []) {
888
926
  const nodes = factory(key);
889
927
  const values = {};
890
928
  for (const field of Object.keys(nodes)) {
891
- values[field] = nodes[field].get();
929
+ values[field] = nodes[field].cache;
892
930
  }
893
931
  snap[key] = values;
894
932
  }
@@ -908,11 +946,11 @@ function useSubscribeRecord3(keysNode, factory) {
908
946
  run(buildSnapshot());
909
947
  };
910
948
  const keysUnsub = keysNode.subscribe((msgs) => {
911
- if (msgs.some((m) => messageTier(m[0]) >= 3)) {
912
- sync(keysNode.get() ?? []);
949
+ if (msgs.some((m) => m[0] === DATA || m[0] === RESOLVED)) {
950
+ sync(keysNode.cache ?? []);
913
951
  }
914
952
  });
915
- sync(keysNode.get() ?? []);
953
+ sync(keysNode.cache ?? []);
916
954
  return () => {
917
955
  keysUnsub();
918
956
  cleanupEntries();
@@ -938,9 +976,9 @@ import {
938
976
  watch
939
977
  } from "vue";
940
978
  function useSubscribe4(node2) {
941
- const ref = shallowRef(node2.get());
979
+ const ref = shallowRef(node2.cache);
942
980
  const unsub = node2.subscribe(() => {
943
- ref.value = node2.get();
981
+ ref.value = node2.cache;
944
982
  });
945
983
  if (getCurrentScope()) {
946
984
  onScopeDispose(() => unsub());
@@ -952,9 +990,9 @@ function useSubscribe4(node2) {
952
990
  return readonly(ref);
953
991
  }
954
992
  function useStore4(node2) {
955
- const inner = shallowRef(node2.get());
993
+ const inner = shallowRef(node2.cache);
956
994
  const unsub = node2.subscribe(() => {
957
- inner.value = node2.get();
995
+ inner.value = node2.cache;
958
996
  });
959
997
  if (getCurrentScope()) {
960
998
  onScopeDispose(() => unsub());
@@ -1000,9 +1038,9 @@ function useSubscribeRecord4(keys, factory) {
1000
1038
  const subs = [];
1001
1039
  for (const field of fields) {
1002
1040
  const node2 = nodes[field];
1003
- values[field] = node2.get();
1041
+ values[field] = node2.cache;
1004
1042
  const unsub = node2.subscribe(() => {
1005
- values[field] = node2.get();
1043
+ values[field] = node2.cache;
1006
1044
  scheduleBatch();
1007
1045
  });
1008
1046
  subs.push(unsub);
@@ -1041,16 +1079,20 @@ var zustand_exports = {};
1041
1079
  __export(zustand_exports, {
1042
1080
  create: () => create
1043
1081
  });
1082
+ var alwaysDiffer = () => false;
1044
1083
  function create(initializer) {
1045
1084
  const g = new Graph("zustand");
1046
- const s = state(void 0, { name: "state" });
1085
+ const s = state(void 0, {
1086
+ name: "state",
1087
+ equals: alwaysDiffer
1088
+ });
1047
1089
  g.add("state", s);
1048
- const getState = () => s.get();
1090
+ const getState = () => s.cache;
1049
1091
  const setState = (partial, replace) => {
1050
1092
  const prev = getState();
1051
1093
  const next = typeof partial === "function" ? partial(prev) : partial;
1052
1094
  const nextState = replace ? next : { ...prev, ...next };
1053
- s.down([[DATA, nextState]]);
1095
+ s.emit(nextState);
1054
1096
  };
1055
1097
  const api = {
1056
1098
  getState,
@@ -1075,7 +1117,7 @@ function create(initializer) {
1075
1117
  destroy: g.destroy.bind(g)
1076
1118
  };
1077
1119
  const initialValue = initializer(setState, getState, api);
1078
- s.down([[DATA, initialValue]]);
1120
+ s.emit(initialValue);
1079
1121
  return Object.assign(g, api);
1080
1122
  }
1081
1123
 
@@ -1105,6 +1147,7 @@ __export(ai_exports, {
1105
1147
  agentLoop: () => agentLoop,
1106
1148
  agentMemory: () => agentMemory,
1107
1149
  chatStream: () => chatStream,
1150
+ contentGate: () => contentGate,
1108
1151
  costMeterExtractor: () => costMeterExtractor,
1109
1152
  fromLLM: () => fromLLM,
1110
1153
  gatedStream: () => gatedStream,
@@ -1115,6 +1158,7 @@ __export(ai_exports, {
1115
1158
  llmConsolidator: () => llmConsolidator,
1116
1159
  llmExtractor: () => llmExtractor,
1117
1160
  promptNode: () => promptNode,
1161
+ redactor: () => redactor,
1118
1162
  streamExtractor: () => streamExtractor,
1119
1163
  streamingPromptNode: () => streamingPromptNode,
1120
1164
  suggestStrategy: () => suggestStrategy,
@@ -1149,10 +1193,10 @@ function copyMap(m) {
1149
1193
  return new Map(m);
1150
1194
  }
1151
1195
  function readMap(node2) {
1152
- return node2.get() ?? /* @__PURE__ */ new Map();
1196
+ return node2.cache ?? /* @__PURE__ */ new Map();
1153
1197
  }
1154
1198
  function readArray(node2) {
1155
- return node2.get() ?? [];
1199
+ return node2.cache ?? [];
1156
1200
  }
1157
1201
  function cosineSimilarity(a, b) {
1158
1202
  const n = Math.max(a.length, b.length);
@@ -1282,8 +1326,6 @@ function collection(name, opts = {}) {
1282
1326
  graph.add("items", items);
1283
1327
  graph.add("ranked", ranked);
1284
1328
  graph.add("size", size);
1285
- graph.connect("items", "ranked");
1286
- graph.connect("items", "size");
1287
1329
  function effective(entry, now) {
1288
1330
  const ageSeconds = (now - entry.lastAccessNs) / 1e9;
1289
1331
  return decay(entry.baseScore, ageSeconds, decayRate, minScore);
@@ -1441,7 +1483,6 @@ function knowledgeGraph(name) {
1441
1483
  graph.add("entities", entities);
1442
1484
  graph.add("edges", edges);
1443
1485
  graph.add("adjacency", adjacency);
1444
- graph.connect("edges", "adjacency");
1445
1486
  function commitEntities(next) {
1446
1487
  entities.down([[DATA, next]]);
1447
1488
  }
@@ -1505,11 +1546,13 @@ var messaging_exports = {};
1505
1546
  __export(messaging_exports, {
1506
1547
  JobFlowGraph: () => JobFlowGraph,
1507
1548
  JobQueueGraph: () => JobQueueGraph,
1549
+ MessagingHubGraph: () => MessagingHubGraph,
1508
1550
  SubscriptionGraph: () => SubscriptionGraph,
1509
1551
  TopicBridgeGraph: () => TopicBridgeGraph,
1510
1552
  TopicGraph: () => TopicGraph,
1511
1553
  jobFlow: () => jobFlow,
1512
1554
  jobQueue: () => jobQueue,
1555
+ messagingHub: () => messagingHub,
1513
1556
  subscription: () => subscription,
1514
1557
  topic: () => topic,
1515
1558
  topicBridge: () => topicBridge
@@ -1527,7 +1570,24 @@ function messagingMeta(kind, extra) {
1527
1570
  var TopicGraph = class extends Graph {
1528
1571
  _log;
1529
1572
  events;
1573
+ /**
1574
+ * Most recently published value, or `null` when the topic has no entries
1575
+ * yet. Spec §5.12 reserves `undefined` as the protocol-internal "never
1576
+ * sent DATA" sentinel — `null` is the idiomatic "empty / no value" signal
1577
+ * for domain nodes. F7.
1578
+ *
1579
+ * **Caveat when `T` itself includes `null`** (e.g., `topic<number | null>`):
1580
+ * `latest === null` is ambiguous — it could mean "no publish yet" OR "a
1581
+ * `null` value was published". Use {@link hasLatest} to disambiguate, or
1582
+ * observe {@link events} directly and track length yourself.
1583
+ */
1530
1584
  latest;
1585
+ /**
1586
+ * Reactive `true` once the topic has at least one published entry.
1587
+ * Disambiguates "`null` never published" from "`null` was published" when
1588
+ * `T` includes `null`.
1589
+ */
1590
+ hasLatest;
1531
1591
  constructor(name, opts = {}) {
1532
1592
  super(name, opts.graph);
1533
1593
  this._log = reactiveLog([], { name: "events", maxSize: opts.retainedLimit });
@@ -1537,40 +1597,62 @@ var TopicGraph = class extends Graph {
1537
1597
  [this.events],
1538
1598
  ([snapshot]) => {
1539
1599
  const entries = snapshot;
1540
- return entries.length === 0 ? void 0 : entries[entries.length - 1];
1600
+ return entries.length === 0 ? null : entries[entries.length - 1];
1541
1601
  },
1542
1602
  {
1543
1603
  name: "latest",
1544
1604
  describeKind: "derived",
1545
- meta: messagingMeta("topic_latest"),
1546
- initial: void 0
1605
+ meta: messagingMeta("topic_latest")
1547
1606
  }
1548
1607
  );
1549
1608
  this.add("latest", this.latest);
1550
- this.connect("events", "latest");
1551
1609
  this.addDisposer(keepalive(this.latest));
1610
+ this.hasLatest = derived(
1611
+ [this.events],
1612
+ ([snapshot]) => snapshot.length > 0,
1613
+ {
1614
+ name: "hasLatest",
1615
+ describeKind: "derived",
1616
+ meta: messagingMeta("topic_has_latest")
1617
+ }
1618
+ );
1619
+ this.add("hasLatest", this.hasLatest);
1620
+ this.addDisposer(keepalive(this.hasLatest));
1621
+ this.addDisposer(() => {
1622
+ this.events.down([[COMPLETE]]);
1623
+ });
1624
+ this.addDisposer(() => this._log.disposeAllViews());
1552
1625
  }
1553
1626
  publish(value) {
1554
1627
  this._log.append(value);
1555
1628
  }
1556
1629
  retained() {
1557
- return this.events.get();
1630
+ return this.events.cache;
1558
1631
  }
1559
1632
  };
1560
1633
  var SubscriptionGraph = class extends Graph {
1561
1634
  source;
1562
1635
  cursor;
1563
1636
  available;
1637
+ /**
1638
+ * Reference to the upstream topic graph. Intentionally NOT mounted
1639
+ * under this subscription: a subscription is a VIEW over an
1640
+ * externally-owned topic. Double-mounting (e.g. hub-owned topic +
1641
+ * sub-mount here) would make either-side teardown leave the other
1642
+ * holding a dead reference. Node-level `derived([topicEvents], …)`
1643
+ * still wires the data dependency across graph boundaries. D1(e).
1644
+ */
1645
+ topic;
1564
1646
  constructor(name, topicGraph, opts = {}) {
1565
1647
  super(name, opts.graph);
1566
1648
  const initialCursor = requireNonNegativeInt(opts.cursor ?? 0, "subscription cursor");
1567
- this.mount("topic", topicGraph);
1649
+ this.topic = topicGraph;
1568
1650
  const topicEvents = topicGraph.events;
1569
1651
  this.source = derived([topicEvents], ([snapshot]) => snapshot, {
1570
1652
  name: "source",
1571
1653
  describeKind: "derived",
1572
1654
  meta: messagingMeta("subscription_source"),
1573
- initial: topicEvents.get()
1655
+ initial: topicEvents.cache
1574
1656
  });
1575
1657
  this.add("source", this.source);
1576
1658
  this.cursor = state(initialCursor, {
@@ -1594,23 +1676,20 @@ var SubscriptionGraph = class extends Graph {
1594
1676
  }
1595
1677
  );
1596
1678
  this.add("available", this.available);
1597
- this.connect("topic::events", "source");
1598
- this.connect("source", "available");
1599
- this.connect("cursor", "available");
1600
1679
  this.addDisposer(keepalive(this.source));
1601
1680
  this.addDisposer(keepalive(this.available));
1602
1681
  }
1603
1682
  ack(count) {
1604
- const available = this.available.get();
1683
+ const available = this.available.cache;
1605
1684
  const requested = count === void 0 ? available.length : requireNonNegativeInt(count, "subscription ack count");
1606
1685
  const step = Math.min(requested, available.length);
1607
- if (step <= 0) return this.cursor.get();
1608
- const next = this.cursor.get() + step;
1609
- this.cursor.down([[DATA, next]]);
1686
+ if (step <= 0) return this.cursor.cache;
1687
+ const next = this.cursor.cache + step;
1688
+ this.cursor.emit(next);
1610
1689
  return next;
1611
1690
  }
1612
1691
  pull(limit, opts = {}) {
1613
- const available = this.available.get();
1692
+ const available = this.available.cache;
1614
1693
  const max = limit === void 0 ? available.length : requireNonNegativeInt(limit, "subscription pull limit");
1615
1694
  const out = available.slice(0, max);
1616
1695
  if (opts.ack && out.length > 0) this.ack(out.length);
@@ -1639,7 +1718,6 @@ var JobQueueGraph = class extends Graph {
1639
1718
  initial: 0
1640
1719
  });
1641
1720
  this.add("depth", this.depth);
1642
- this.connect("pending", "depth");
1643
1721
  this.addDisposer(keepalive(this.depth));
1644
1722
  }
1645
1723
  enqueue(payload, opts = {}) {
@@ -1663,7 +1741,7 @@ var JobQueueGraph = class extends Graph {
1663
1741
  if (max === 0) return [];
1664
1742
  const out = [];
1665
1743
  while (out.length < max) {
1666
- const ids = this.pending.get();
1744
+ const ids = this.pending.cache;
1667
1745
  if (ids.length === 0) break;
1668
1746
  const id = this._pending.pop(0);
1669
1747
  const job = this._jobs.get(id);
@@ -1732,7 +1810,6 @@ var JobFlowGraph = class extends Graph {
1732
1810
  }
1733
1811
  );
1734
1812
  this.add("completedCount", this.completedCount);
1735
- this.connect("completed", "completedCount");
1736
1813
  this.addDisposer(keepalive(this.completedCount));
1737
1814
  const maxPerPump = Math.max(
1738
1815
  1,
@@ -1772,7 +1849,6 @@ var JobFlowGraph = class extends Graph {
1772
1849
  }
1773
1850
  );
1774
1851
  this.add(`pump_${stage}`, pump);
1775
- this.connect(`${stage}::pending`, `pump_${stage}`);
1776
1852
  this.addDisposer(keepalive(pump));
1777
1853
  }
1778
1854
  }
@@ -1788,7 +1864,7 @@ var JobFlowGraph = class extends Graph {
1788
1864
  return this.queue(this._stageNames[0]).enqueue(payload, opts);
1789
1865
  }
1790
1866
  retainedCompleted() {
1791
- return this.completed.get();
1867
+ return this.completed.cache;
1792
1868
  }
1793
1869
  };
1794
1870
  var TopicBridgeGraph = class extends Graph {
@@ -1826,7 +1902,7 @@ var TopicBridgeGraph = class extends Graph {
1826
1902
  bridged += 1;
1827
1903
  }
1828
1904
  if (bridged > 0) {
1829
- const current = this.bridgedCount.get();
1905
+ const current = this.bridgedCount.cache;
1830
1906
  this.bridgedCount.down([[DATA, current + bridged]]);
1831
1907
  }
1832
1908
  },
@@ -1837,13 +1913,112 @@ var TopicBridgeGraph = class extends Graph {
1837
1913
  }
1838
1914
  );
1839
1915
  this.add("pump", pump);
1840
- this.connect("subscription::available", "pump");
1841
1916
  this.addDisposer(keepalive(pump));
1842
1917
  }
1843
1918
  };
1919
+ var MessagingHubGraph = class extends Graph {
1920
+ _topics = /* @__PURE__ */ new Map();
1921
+ _version = 0;
1922
+ _defaultTopicOptions;
1923
+ constructor(name, opts = {}) {
1924
+ super(name, opts.graph);
1925
+ this._defaultTopicOptions = { ...opts.defaultTopicOptions ?? {} };
1926
+ }
1927
+ /** Monotonic counter advancing on topic create/remove. */
1928
+ get version() {
1929
+ return this._version;
1930
+ }
1931
+ /** Number of topics currently in the hub. */
1932
+ get size() {
1933
+ return this._topics.size;
1934
+ }
1935
+ /** Checks topic existence without creating. */
1936
+ has(name) {
1937
+ return this._topics.has(name);
1938
+ }
1939
+ /** Iterator over topic names. */
1940
+ topicNames() {
1941
+ return this._topics.keys();
1942
+ }
1943
+ /**
1944
+ * Returns the {@link TopicGraph} for `name`, creating lazily on first call.
1945
+ * Subsequent calls with the same name return the same instance (options on
1946
+ * repeat calls are ignored — the topic is already configured).
1947
+ */
1948
+ topic(name, opts) {
1949
+ let t = this._topics.get(name);
1950
+ if (t === void 0) {
1951
+ const effective = { ...this._defaultTopicOptions, ...opts ?? {} };
1952
+ t = new TopicGraph(name, effective);
1953
+ this._topics.set(name, t);
1954
+ this.mount(name, t);
1955
+ this._version += 1;
1956
+ }
1957
+ return t;
1958
+ }
1959
+ /**
1960
+ * Publishes a value to the topic, lazily creating it on first publish.
1961
+ *
1962
+ * **Late-subscriber caveat:** the topic is created lazily, so subscribers
1963
+ * that attach AFTER a publish only see the retained window (governed by
1964
+ * `retainedLimit` on `TopicOptions` / `defaultTopicOptions`). If
1965
+ * `retainedLimit === 0` is set explicitly, early publishes are
1966
+ * effectively dropped — prefer an unset `retainedLimit` (unbounded
1967
+ * retention) or subscribe before publishing when late-subscribers matter.
1968
+ */
1969
+ publish(name, value) {
1970
+ this.topic(name).publish(value);
1971
+ }
1972
+ /**
1973
+ * Bulk publish — issues all publishes inside one outer batch. New topics
1974
+ * are created on demand. No-op if `entries` yields nothing.
1975
+ *
1976
+ * **Iterable consumption (F6):** `entries` is consumed once (single-pass)
1977
+ * INSIDE the batch frame. If the iterator throws mid-way, the batch is
1978
+ * discarded and NO publishes are visible to subscribers (all-or-nothing).
1979
+ * Pass an array or `Set` for multi-shot callers.
1980
+ */
1981
+ publishMany(entries) {
1982
+ batch(() => {
1983
+ for (const [name, value] of entries) {
1984
+ this.topic(name).publish(value);
1985
+ }
1986
+ });
1987
+ }
1988
+ /**
1989
+ * Creates a {@link SubscriptionGraph} over a named topic. The topic is
1990
+ * lazily created if missing. Subscription lifecycle is owned by the caller —
1991
+ * the hub does NOT mount the subscription.
1992
+ *
1993
+ * @param subName - Local name for the subscription graph.
1994
+ * @param topicName - Hub topic to subscribe to.
1995
+ * @param opts - `SubscriptionOptions` (initial cursor, etc.).
1996
+ */
1997
+ subscribe(subName, topicName, opts) {
1998
+ const t = this.topic(topicName);
1999
+ return new SubscriptionGraph(subName, t, opts);
2000
+ }
2001
+ /**
2002
+ * Unmounts and tears down the topic's graph. Returns `true` if the topic
2003
+ * existed. Subscribers receive `TEARDOWN` via {@link Graph.remove}.
2004
+ */
2005
+ removeTopic(name) {
2006
+ if (!this._topics.has(name)) return false;
2007
+ try {
2008
+ this.remove(name);
2009
+ } finally {
2010
+ this._topics.delete(name);
2011
+ this._version += 1;
2012
+ }
2013
+ return true;
2014
+ }
2015
+ };
1844
2016
  function topic(name, opts) {
1845
2017
  return new TopicGraph(name, opts);
1846
2018
  }
2019
+ function messagingHub(name, opts) {
2020
+ return new MessagingHubGraph(name, opts);
2021
+ }
1847
2022
  function subscription(name, topicGraph, opts) {
1848
2023
  return new SubscriptionGraph(name, topicGraph, opts);
1849
2024
  }
@@ -1900,9 +2075,7 @@ function findRegisteredNodePath(graph, target) {
1900
2075
  }
1901
2076
  function registerStep(graph, name, step, depPaths) {
1902
2077
  graph.add(name, step);
1903
- for (const path of depPaths) {
1904
- graph.connect(path, name);
1905
- }
2078
+ void depPaths;
1906
2079
  }
1907
2080
  function baseMeta(kind, meta) {
1908
2081
  return domainMeta("orchestration", kind, meta);
@@ -1931,9 +2104,16 @@ function task(graph, name, run, opts) {
1931
2104
  const depRefs = opts?.deps ?? [];
1932
2105
  const deps = depRefs.map((dep) => resolveDep(graph, dep));
1933
2106
  const { deps: _deps, ...nodeOpts } = opts ?? {};
2107
+ const wrapped = (batchData, actions, ctx) => {
2108
+ const data = batchData.map(
2109
+ (batch2, i) => batch2 != null && batch2.length > 0 ? batch2.at(-1) : ctx.prevData[i]
2110
+ );
2111
+ actions.emit(run(data, ctx));
2112
+ return void 0;
2113
+ };
1934
2114
  const step = node(
1935
2115
  deps.map((d) => d.node),
1936
- run,
2116
+ wrapped,
1937
2117
  {
1938
2118
  ...nodeOpts,
1939
2119
  name,
@@ -1951,7 +2131,7 @@ function task(graph, name, run, opts) {
1951
2131
  }
1952
2132
  function branch(graph, name, source, predicate, opts) {
1953
2133
  const src = resolveDep(graph, source);
1954
- const step = node(
2134
+ const step = derived(
1955
2135
  [src.node],
1956
2136
  ([value]) => ({
1957
2137
  branch: predicate(value) ? "then" : "else",
@@ -1960,7 +2140,6 @@ function branch(graph, name, source, predicate, opts) {
1960
2140
  {
1961
2141
  ...opts,
1962
2142
  name,
1963
- describeKind: "derived",
1964
2143
  meta: baseMeta("branch", opts?.meta)
1965
2144
  }
1966
2145
  );
@@ -1972,18 +2151,28 @@ function valve2(graph, name, source, control, opts) {
1972
2151
  const ctrl = resolveDep(graph, control);
1973
2152
  const step = node(
1974
2153
  [src.node, ctrl.node],
1975
- (_deps, actions) => {
1976
- const opened = ctrl.node.get();
1977
- if (!opened) {
2154
+ (batchData, actions, ctx) => {
2155
+ const batch0 = batchData[0];
2156
+ const batch1 = batchData[1];
2157
+ const ctrlVal = batch1 != null && batch1.length > 0 ? batch1.at(-1) : ctx.prevData[1];
2158
+ if (batch0 == null || batch0.length === 0) {
2159
+ if (batch1 != null && batch1.length > 0 && ctrlVal && ctx.prevData[0] !== void 0) {
2160
+ actions.emit(ctx.prevData[0]);
2161
+ } else {
2162
+ actions.down([[RESOLVED]]);
2163
+ }
2164
+ return;
2165
+ }
2166
+ if (!ctrlVal) {
1978
2167
  actions.down([[RESOLVED]]);
1979
- return void 0;
2168
+ return;
1980
2169
  }
1981
- return src.node.get();
2170
+ for (const v of batch0) actions.emit(v);
1982
2171
  },
1983
2172
  {
1984
2173
  ...opts,
1985
2174
  name,
1986
- describeKind: "operator",
2175
+ describeKind: "derived",
1987
2176
  meta: baseMeta("valve", opts?.meta)
1988
2177
  }
1989
2178
  );
@@ -2001,17 +2190,28 @@ function approval(graph, name, source, approver, opts) {
2001
2190
  const isApproved = opts?.isApproved ?? ((value) => Boolean(value));
2002
2191
  const step = node(
2003
2192
  [src.node, ctrl.node],
2004
- (_deps, actions) => {
2005
- if (!isApproved(ctrl.node.get())) {
2193
+ (batchData, actions, ctx) => {
2194
+ const batch0 = batchData[0];
2195
+ const batch1 = batchData[1];
2196
+ const ctrlVal = batch1 != null && batch1.length > 0 ? batch1.at(-1) : ctx.prevData[1];
2197
+ if (ctrlVal === void 0 || !isApproved(ctrlVal)) {
2006
2198
  actions.down([[RESOLVED]]);
2007
- return void 0;
2199
+ return;
2008
2200
  }
2009
- return src.node.get();
2201
+ if (batch0 == null || batch0.length === 0) {
2202
+ if (batch1 != null && batch1.length > 0 && ctx.prevData[0] !== void 0) {
2203
+ actions.emit(ctx.prevData[0]);
2204
+ } else {
2205
+ actions.down([[RESOLVED]]);
2206
+ }
2207
+ return;
2208
+ }
2209
+ for (const v of batch0) actions.emit(v);
2010
2210
  },
2011
2211
  {
2012
2212
  ...opts,
2013
2213
  name,
2014
- describeKind: "operator",
2214
+ describeKind: "derived",
2015
2215
  meta: baseMeta("approval", opts?.meta)
2016
2216
  }
2017
2217
  );
@@ -2032,12 +2232,17 @@ function gate(graph, name, source, opts) {
2032
2232
  const src = resolveDep(graph, source);
2033
2233
  const pendingNode = state([], { name: "pending", equals: () => false });
2034
2234
  const isOpenNode = state(startOpen, { name: "isOpen" });
2035
- const countNode = node([pendingNode], ([arr]) => arr.length, {
2036
- name: "count",
2037
- describeKind: "derived"
2235
+ const countNode = derived([pendingNode], ([arr]) => arr.length, {
2236
+ name: "count"
2038
2237
  });
2039
2238
  let queue = [];
2040
2239
  let torn = false;
2240
+ let latestIsOpen = startOpen;
2241
+ const isOpenUnsub = isOpenNode.subscribe((msgs) => {
2242
+ for (const m of msgs) {
2243
+ if (m[0] === DATA) latestIsOpen = m[1];
2244
+ }
2245
+ });
2041
2246
  function syncPending() {
2042
2247
  pendingNode.down([[DATA, [...queue]]]);
2043
2248
  }
@@ -2054,38 +2259,37 @@ function gate(graph, name, source, opts) {
2054
2259
  function guardTorn(method) {
2055
2260
  if (torn) throw new Error(`gate: ${method}() called after gate was torn down`);
2056
2261
  }
2057
- const output = node([src.node], () => void 0, {
2058
- name,
2059
- describeKind: "operator",
2060
- meta: baseMeta("gate", opts?.meta),
2061
- onMessage(msg, _depIndex, actions) {
2062
- if (msg[0] === DATA) {
2063
- if (isOpenNode.get()) {
2064
- actions.emit(msg[1]);
2065
- } else {
2066
- enqueue(msg[1]);
2067
- actions.down([[RESOLVED]]);
2068
- }
2069
- return true;
2070
- }
2071
- if (msg[0] === TEARDOWN) {
2262
+ const output = node(
2263
+ [src.node],
2264
+ (batchData, actions, ctx) => {
2265
+ const terminal = ctx.terminalDeps[0];
2266
+ if (terminal !== void 0) {
2072
2267
  torn = true;
2073
2268
  queue = [];
2074
2269
  syncPending();
2075
- actions.down([msg]);
2076
- return true;
2270
+ actions.down(terminal === true ? [[COMPLETE]] : [[ERROR, terminal]]);
2271
+ return;
2077
2272
  }
2078
- if (msg[0] === COMPLETE || msg[0] === ERROR) {
2079
- torn = true;
2080
- queue = [];
2081
- syncPending();
2082
- actions.down([msg]);
2083
- return true;
2273
+ const batch0 = batchData[0];
2274
+ if (batch0 == null || batch0.length === 0) {
2275
+ actions.down([[RESOLVED]]);
2276
+ return;
2084
2277
  }
2085
- actions.down([msg]);
2086
- return true;
2278
+ for (const v of batch0) {
2279
+ if (latestIsOpen) {
2280
+ actions.emit(v);
2281
+ } else {
2282
+ enqueue(v);
2283
+ actions.down([[RESOLVED]]);
2284
+ }
2285
+ }
2286
+ },
2287
+ {
2288
+ name,
2289
+ describeKind: "derived",
2290
+ meta: baseMeta("gate", opts?.meta)
2087
2291
  }
2088
- });
2292
+ );
2089
2293
  const controller = {
2090
2294
  node: output,
2091
2295
  pending: pendingNode,
@@ -2114,12 +2318,14 @@ function gate(graph, name, source, opts) {
2114
2318
  },
2115
2319
  open() {
2116
2320
  guardTorn("open");
2117
- isOpenNode.down([[DATA, true]]);
2118
- const items = dequeue(queue.length);
2119
- for (const item of items) {
2120
- if (torn) break;
2121
- output.down([[DATA, item]]);
2122
- }
2321
+ batch(() => {
2322
+ isOpenNode.down([[DATA, true]]);
2323
+ const items = dequeue(queue.length);
2324
+ for (const item of items) {
2325
+ if (torn) break;
2326
+ output.down([[DATA, item]]);
2327
+ }
2328
+ });
2123
2329
  },
2124
2330
  close() {
2125
2331
  guardTorn("close");
@@ -2127,58 +2333,65 @@ function gate(graph, name, source, opts) {
2127
2333
  }
2128
2334
  };
2129
2335
  graph.addDisposer(countNode.subscribe(() => void 0));
2336
+ graph.addDisposer(isOpenUnsub);
2130
2337
  registerStep(graph, name, output, src.path ? [src.path] : []);
2131
2338
  const internal = new Graph(`${name}_state`);
2132
2339
  internal.add("pending", pendingNode);
2133
2340
  internal.add("isOpen", isOpenNode);
2134
2341
  internal.add("count", countNode);
2135
- internal.connect("pending", "count");
2136
2342
  graph.mount(`${name}_state`, internal);
2137
2343
  return controller;
2138
2344
  }
2139
2345
  function forEach2(graph, name, source, run, opts) {
2140
2346
  const src = resolveDep(graph, source);
2141
2347
  let terminated = false;
2142
- const step = node([src.node], () => void 0, {
2143
- ...opts,
2144
- name,
2145
- describeKind: "effect",
2146
- completeWhenDepsComplete: false,
2147
- meta: baseMeta("forEach", opts?.meta),
2148
- onMessage(msg, depIndex, actions) {
2149
- if (terminated) return true;
2150
- if (depIndex !== 0) {
2151
- actions.down([msg]);
2152
- if (msg[0] === COMPLETE || msg[0] === ERROR) terminated = true;
2153
- return true;
2348
+ const step = node(
2349
+ [src.node],
2350
+ (batchData, actions, ctx) => {
2351
+ if (terminated) {
2352
+ actions.down([[RESOLVED]]);
2353
+ return;
2354
+ }
2355
+ const terminal = ctx.terminalDeps[0];
2356
+ if (terminal !== void 0) {
2357
+ terminated = true;
2358
+ actions.down(terminal === true ? [[COMPLETE]] : [[ERROR, terminal]]);
2359
+ return;
2154
2360
  }
2155
- if (msg[0] === DATA) {
2361
+ const batch0 = batchData[0];
2362
+ if (batch0 == null || batch0.length === 0) {
2363
+ actions.down([[RESOLVED]]);
2364
+ return;
2365
+ }
2366
+ for (const v of batch0) {
2156
2367
  try {
2157
- run(msg[1], actions);
2158
- actions.down([msg]);
2368
+ run(v, actions);
2159
2369
  } catch (err) {
2160
2370
  terminated = true;
2161
2371
  actions.down([[ERROR, err]]);
2372
+ return;
2162
2373
  }
2163
- return true;
2164
2374
  }
2165
- actions.down([msg]);
2166
- if (msg[0] === COMPLETE || msg[0] === ERROR) terminated = true;
2167
- return true;
2375
+ },
2376
+ {
2377
+ ...opts,
2378
+ name,
2379
+ describeKind: "effect",
2380
+ completeWhenDepsComplete: false,
2381
+ meta: baseMeta("forEach", opts?.meta)
2168
2382
  }
2169
- });
2383
+ );
2170
2384
  registerStep(graph, name, step, src.path ? [src.path] : []);
2171
2385
  return step;
2172
2386
  }
2173
2387
  function join(graph, name, deps, opts) {
2174
2388
  const resolved = deps.map((dep) => resolveDep(graph, dep));
2175
- const step = node(
2389
+ const step = derived(
2176
2390
  resolved.map((d) => d.node),
2177
2391
  (values) => values,
2178
2392
  {
2179
2393
  ...opts,
2180
2394
  name,
2181
- describeKind: "derived",
2182
2395
  meta: baseMeta("join", opts?.meta)
2183
2396
  }
2184
2397
  );
@@ -2197,14 +2410,16 @@ function loop(graph, name, source, iterate, opts) {
2197
2410
  const staticIterations = typeof iterRef === "number" ? iterRef : void 0;
2198
2411
  const step = node(
2199
2412
  iterDep ? [src.node, iterDep.node] : [src.node],
2200
- (_deps, actions) => {
2201
- let current = src.node.get();
2202
- const rawCount = staticIterations ?? iterDep?.node.get() ?? 1;
2413
+ (depValues, actions, ctx) => {
2414
+ const batch0 = depValues[0];
2415
+ let current = batch0 != null && batch0.length > 0 ? batch0.at(-1) : ctx.prevData[0];
2416
+ const batch1 = iterDep ? depValues[1] : void 0;
2417
+ const rawCount = staticIterations ?? (iterDep ? batch1 != null && batch1.length > 0 ? batch1.at(-1) : ctx.prevData[1] : 1);
2203
2418
  const count = coerceLoopIterations(rawCount);
2204
2419
  for (let i = 0; i < count; i += 1) {
2205
2420
  current = iterate(current, i, actions);
2206
2421
  }
2207
- return current;
2422
+ actions.emit(current);
2208
2423
  },
2209
2424
  {
2210
2425
  ...opts,
@@ -2260,7 +2475,7 @@ function wait(graph, name, source, ms, opts) {
2260
2475
  for (const id of timers) clearTimeout(id);
2261
2476
  timers.clear();
2262
2477
  }
2263
- const srcVal = src.node.get();
2478
+ const srcVal = src.node.cache;
2264
2479
  const initialOpt = srcVal !== void 0 ? { initial: srcVal } : {};
2265
2480
  const step = node(
2266
2481
  [],
@@ -2305,46 +2520,50 @@ function wait(graph, name, source, ms, opts) {
2305
2520
  ...opts,
2306
2521
  name,
2307
2522
  ...initialOpt,
2308
- describeKind: "operator",
2523
+ describeKind: "derived",
2309
2524
  completeWhenDepsComplete: false,
2310
2525
  meta: baseMeta("wait", opts?.meta)
2311
2526
  }
2312
2527
  );
2313
2528
  graph.add(name, step);
2314
- if (src.path) {
2315
- try {
2316
- graph.connect(src.path, name);
2317
- } catch (e) {
2318
- if (!(e instanceof Error && /dep|edge|connect/i.test(e.message))) throw e;
2319
- }
2320
- }
2321
2529
  return step;
2322
2530
  }
2323
2531
  function onFailure(graph, name, source, recover, opts) {
2324
2532
  const src = resolveDep(graph, source);
2325
2533
  let terminated = false;
2326
- const step = node([src.node], () => void 0, {
2327
- ...opts,
2328
- name,
2329
- describeKind: "operator",
2330
- completeWhenDepsComplete: false,
2331
- meta: baseMeta("onFailure", opts?.meta),
2332
- onMessage(msg, _depIndex, actions) {
2333
- if (terminated) return true;
2334
- if (msg[0] === ERROR) {
2335
- try {
2336
- actions.emit(recover(msg[1], actions));
2337
- } catch (err) {
2338
- terminated = true;
2339
- actions.down([[ERROR, err]]);
2534
+ const step = node(
2535
+ [],
2536
+ (_data, actions) => {
2537
+ const unsub = src.node.subscribe((msgs) => {
2538
+ for (const msg of msgs) {
2539
+ if (terminated) return;
2540
+ if (msg[0] === ERROR) {
2541
+ try {
2542
+ actions.emit(recover(msg[1], actions));
2543
+ } catch (err) {
2544
+ terminated = true;
2545
+ actions.down([[ERROR, err]]);
2546
+ }
2547
+ } else {
2548
+ actions.down([msg]);
2549
+ if (msg[0] === COMPLETE) terminated = true;
2550
+ }
2340
2551
  }
2341
- return true;
2342
- }
2343
- actions.down([msg]);
2344
- if (msg[0] === COMPLETE) terminated = true;
2345
- return true;
2552
+ });
2553
+ return () => unsub();
2554
+ },
2555
+ {
2556
+ ...opts,
2557
+ name,
2558
+ describeKind: "derived",
2559
+ completeWhenDepsComplete: false,
2560
+ // onFailure handles errors via manual subscription (recover callback).
2561
+ // Disable auto-propagation so dep-channel ERROR doesn't terminate this
2562
+ // node before the recover callback has a chance to emit a replacement value.
2563
+ errorWhenDepsError: false,
2564
+ meta: baseMeta("onFailure", opts?.meta)
2346
2565
  }
2347
- });
2566
+ );
2348
2567
  registerStep(graph, name, step, src.path ? [src.path] : []);
2349
2568
  return step;
2350
2569
  }
@@ -2357,7 +2576,7 @@ function isPromiseLike(x) {
2357
2576
  return x != null && typeof x.then === "function";
2358
2577
  }
2359
2578
  function isNodeLike(x) {
2360
- return typeof x === "object" && x !== null && "subscribe" in x && typeof x.subscribe === "function" && "get" in x && typeof x.get === "function";
2579
+ return typeof x === "object" && x !== null && "subscribe" in x && typeof x.subscribe === "function" && "cache" in x;
2361
2580
  }
2362
2581
  function isAsyncIterableLike(x) {
2363
2582
  return x != null && typeof x === "object" && Symbol.asyncIterator in x && typeof x[Symbol.asyncIterator] === "function";
@@ -2365,7 +2584,7 @@ function isAsyncIterableLike(x) {
2365
2584
  var DEFAULT_TIMEOUT_MS = 3e4;
2366
2585
  function firstDataFromNode(resolved, opts) {
2367
2586
  if (resolved.status === "settled") {
2368
- const immediate = resolved.get();
2587
+ const immediate = resolved.cache;
2369
2588
  if (immediate !== void 0) {
2370
2589
  return Promise.resolve(immediate);
2371
2590
  }
@@ -2505,43 +2724,94 @@ function streamExtractor(streamTopic, extractFn, opts) {
2505
2724
  {
2506
2725
  name: opts?.name ?? "extractor",
2507
2726
  describeKind: "derived",
2508
- initial: null
2727
+ initial: null,
2728
+ meta: aiMeta("stream_extractor"),
2729
+ ...opts?.equals ? { equals: opts.equals } : {}
2509
2730
  }
2510
2731
  );
2511
2732
  }
2733
+ var keywordFlagsEqual = (a, b) => {
2734
+ if (a === b) return true;
2735
+ if (a == null || b == null) return a === b;
2736
+ if (a.length !== b.length) return false;
2737
+ for (let i = 0; i < a.length; i++) {
2738
+ const x = a[i];
2739
+ const y = b[i];
2740
+ if (x.label !== y.label || x.pattern !== y.pattern || x.match !== y.match || x.position !== y.position) {
2741
+ return false;
2742
+ }
2743
+ }
2744
+ return true;
2745
+ };
2512
2746
  function keywordFlagExtractor(streamTopic, opts) {
2747
+ const maxPatternLength = opts.maxPatternLength ?? 128;
2513
2748
  return derived(
2514
2749
  [streamTopic.latest],
2515
- ([chunk]) => {
2750
+ ([chunk], ctx) => {
2516
2751
  if (chunk == null) return [];
2517
2752
  const accumulated = chunk.accumulated;
2518
- const flags = [];
2753
+ if (!("flags" in ctx.store)) {
2754
+ ctx.store.flags = [];
2755
+ ctx.store.scannedTo = 0;
2756
+ }
2757
+ const flags = ctx.store.flags;
2758
+ const scannedTo = ctx.store.scannedTo;
2759
+ const startOffset = Math.max(0, scannedTo - maxPatternLength);
2760
+ const region = accumulated.slice(startOffset);
2761
+ let added = false;
2519
2762
  for (const { pattern, label } of opts.patterns) {
2520
2763
  const re = new RegExp(pattern.source, `${pattern.flags.replace("g", "")}g`);
2521
- for (const m of accumulated.matchAll(re)) {
2522
- flags.push({ label, pattern, match: m[0], position: m.index });
2764
+ for (const m of region.matchAll(re)) {
2765
+ const pos = startOffset + m.index;
2766
+ if (pos + m[0].length <= scannedTo) continue;
2767
+ flags.push({ label, pattern, match: m[0], position: pos });
2768
+ added = true;
2523
2769
  }
2524
2770
  }
2525
- return flags;
2771
+ ctx.store.scannedTo = accumulated.length;
2772
+ return added ? [...flags] : flags.slice();
2526
2773
  },
2527
2774
  {
2528
2775
  name: opts.name ?? "keyword-flag-extractor",
2529
2776
  describeKind: "derived",
2530
- initial: []
2777
+ initial: [],
2778
+ meta: aiMeta("keyword_flag_extractor"),
2779
+ equals: keywordFlagsEqual
2531
2780
  }
2532
2781
  );
2533
2782
  }
2783
+ var toolCallsEqual = (a, b) => {
2784
+ if (a === b) return true;
2785
+ if (a == null || b == null) return a === b;
2786
+ if (a.length !== b.length) return false;
2787
+ for (let i = 0; i < a.length; i++) {
2788
+ const x = a[i];
2789
+ const y = b[i];
2790
+ if (x.startIndex !== y.startIndex || x.name !== y.name || x.raw !== y.raw) {
2791
+ return false;
2792
+ }
2793
+ }
2794
+ return true;
2795
+ };
2534
2796
  function toolCallExtractor(streamTopic, opts) {
2535
2797
  return derived(
2536
2798
  [streamTopic.latest],
2537
- ([chunk]) => {
2799
+ ([chunk], ctx) => {
2538
2800
  if (chunk == null) return [];
2539
2801
  const accumulated = chunk.accumulated;
2540
- const calls = [];
2541
- let i = 0;
2802
+ if (!("calls" in ctx.store)) {
2803
+ ctx.store.calls = [];
2804
+ ctx.store.scanFrom = 0;
2805
+ }
2806
+ const calls = ctx.store.calls;
2807
+ let i = ctx.store.scanFrom;
2808
+ let added = false;
2542
2809
  while (i < accumulated.length) {
2543
2810
  const start = accumulated.indexOf("{", i);
2544
- if (start === -1) break;
2811
+ if (start === -1) {
2812
+ ctx.store.scanFrom = accumulated.length;
2813
+ break;
2814
+ }
2545
2815
  let depth = 0;
2546
2816
  let end = -1;
2547
2817
  let inString = false;
@@ -2565,7 +2835,10 @@ function toolCallExtractor(streamTopic, opts) {
2565
2835
  }
2566
2836
  }
2567
2837
  }
2568
- if (end === -1) break;
2838
+ if (end === -1) {
2839
+ ctx.store.scanFrom = start;
2840
+ break;
2841
+ }
2569
2842
  const raw = accumulated.slice(start, end + 1);
2570
2843
  try {
2571
2844
  const parsed = JSON.parse(raw);
@@ -2576,20 +2849,28 @@ function toolCallExtractor(streamTopic, opts) {
2576
2849
  raw,
2577
2850
  startIndex: start
2578
2851
  });
2852
+ added = true;
2579
2853
  }
2580
2854
  } catch {
2581
2855
  }
2582
2856
  i = end + 1;
2857
+ ctx.store.scanFrom = i;
2583
2858
  }
2584
- return calls;
2859
+ return added ? [...calls] : calls.slice();
2585
2860
  },
2586
2861
  {
2587
2862
  name: opts?.name ?? "tool-call-extractor",
2588
2863
  describeKind: "derived",
2589
- initial: []
2864
+ initial: [],
2865
+ meta: aiMeta("tool_call_extractor"),
2866
+ equals: toolCallsEqual
2590
2867
  }
2591
2868
  );
2592
2869
  }
2870
+ var costMeterEqual = (a, b) => {
2871
+ if (a === b) return true;
2872
+ return a.chunkCount === b.chunkCount && a.charCount === b.charCount && a.estimatedTokens === b.estimatedTokens;
2873
+ };
2593
2874
  function costMeterExtractor(streamTopic, opts) {
2594
2875
  const charsPerToken = opts?.charsPerToken ?? 4;
2595
2876
  return derived(
@@ -2607,8 +2888,57 @@ function costMeterExtractor(streamTopic, opts) {
2607
2888
  {
2608
2889
  name: opts?.name ?? "cost-meter",
2609
2890
  describeKind: "derived",
2610
- initial: { chunkCount: 0, charCount: 0, estimatedTokens: 0 }
2891
+ initial: { chunkCount: 0, charCount: 0, estimatedTokens: 0 },
2892
+ meta: aiMeta("cost_meter_extractor"),
2893
+ equals: costMeterEqual
2894
+ }
2895
+ );
2896
+ }
2897
+ function redactor(streamTopic, patterns, replaceFn, opts) {
2898
+ const replace = replaceFn ?? (() => "[REDACTED]");
2899
+ function sanitize(text) {
2900
+ let result = text;
2901
+ for (const pat of patterns) {
2902
+ const global = pat.global ? pat : new RegExp(pat.source, `${pat.flags}g`);
2903
+ result = result.replace(global, (m) => replace(m, pat));
2611
2904
  }
2905
+ return result;
2906
+ }
2907
+ return derived(
2908
+ [streamTopic.latest],
2909
+ ([chunk]) => {
2910
+ if (chunk == null) {
2911
+ return { source: "", token: "", accumulated: "", index: -1 };
2912
+ }
2913
+ const c = chunk;
2914
+ const sanitizedAccumulated = sanitize(c.accumulated);
2915
+ const sanitizedToken = sanitize(c.token);
2916
+ return {
2917
+ source: c.source,
2918
+ token: sanitizedToken,
2919
+ accumulated: sanitizedAccumulated,
2920
+ index: c.index
2921
+ };
2922
+ },
2923
+ { name: opts?.name ?? "redactor" }
2924
+ );
2925
+ }
2926
+ function contentGate(streamTopic, classifier, threshold, opts) {
2927
+ const hardThreshold = threshold * (opts?.hardMultiplier ?? 1.5);
2928
+ const isNodeClassifier = typeof classifier !== "function";
2929
+ const deps = [streamTopic.latest];
2930
+ if (isNodeClassifier) deps.push(classifier);
2931
+ return derived(
2932
+ deps,
2933
+ (values) => {
2934
+ const chunk = values[0];
2935
+ if (chunk == null) return "allow";
2936
+ const score = isNodeClassifier ? values[1] ?? 0 : classifier(chunk.accumulated);
2937
+ if (score >= hardThreshold) return "block";
2938
+ if (score >= threshold) return "review";
2939
+ return "allow";
2940
+ },
2941
+ { name: opts?.name ?? "content-gate", initial: "allow" }
2612
2942
  );
2613
2943
  }
2614
2944
  function gatedStream(graph, name, adapter, deps, prompt, opts) {
@@ -2712,7 +3042,7 @@ function promptNode(adapter, deps, prompt, opts) {
2712
3042
  const format = opts?.format ?? "text";
2713
3043
  const retries = opts?.retries ?? 0;
2714
3044
  const useCache = opts?.cache ?? false;
2715
- const cache2 = useCache ? /* @__PURE__ */ new Map() : null;
3045
+ const cache = useCache ? /* @__PURE__ */ new Map() : null;
2716
3046
  const messagesNode = derived(
2717
3047
  deps,
2718
3048
  (values) => {
@@ -2735,8 +3065,8 @@ function promptNode(adapter, deps, prompt, opts) {
2735
3065
  return state(null);
2736
3066
  }
2737
3067
  const cacheKey = useCache ? JSON.stringify(msgs.map((m) => [m.role, m.content])) : "";
2738
- if (cache2?.has(cacheKey)) {
2739
- return state(cache2.get(cacheKey));
3068
+ if (cache?.has(cacheKey)) {
3069
+ return state(cache.get(cacheKey));
2740
3070
  }
2741
3071
  async function attempt(remaining) {
2742
3072
  try {
@@ -2749,8 +3079,8 @@ function promptNode(adapter, deps, prompt, opts) {
2749
3079
  });
2750
3080
  if (input && typeof input.then === "function") {
2751
3081
  input.then(resolve, reject);
2752
- } else if (input && typeof input.get === "function") {
2753
- resolve(input.get());
3082
+ } else if (input && typeof input.subscribe === "function") {
3083
+ resolve(input.cache);
2754
3084
  } else {
2755
3085
  resolve(input);
2756
3086
  }
@@ -2762,7 +3092,7 @@ function promptNode(adapter, deps, prompt, opts) {
2762
3092
  } else {
2763
3093
  parsed = content;
2764
3094
  }
2765
- cache2?.set(cacheKey, parsed);
3095
+ cache?.set(cacheKey, parsed);
2766
3096
  return parsed;
2767
3097
  } catch (err) {
2768
3098
  if (remaining > 0) return attempt(remaining - 1);
@@ -2790,17 +3120,15 @@ var ChatStreamGraph = class extends Graph {
2790
3120
  [this.messages],
2791
3121
  ([snapshot]) => {
2792
3122
  const entries = snapshot;
2793
- return entries.length === 0 ? void 0 : entries[entries.length - 1];
3123
+ return entries.length === 0 ? null : entries[entries.length - 1];
2794
3124
  },
2795
3125
  {
2796
3126
  name: "latest",
2797
3127
  describeKind: "derived",
2798
- meta: aiMeta("chat_latest"),
2799
- initial: void 0
3128
+ meta: aiMeta("chat_latest")
2800
3129
  }
2801
3130
  );
2802
3131
  this.add("latest", this.latest);
2803
- this.connect("messages", "latest");
2804
3132
  this.addDisposer(keepalive(this.latest));
2805
3133
  this.messageCount = derived(
2806
3134
  [this.messages],
@@ -2813,7 +3141,6 @@ var ChatStreamGraph = class extends Graph {
2813
3141
  }
2814
3142
  );
2815
3143
  this.add("messageCount", this.messageCount);
2816
- this.connect("messages", "messageCount");
2817
3144
  this.addDisposer(keepalive(this.messageCount));
2818
3145
  }
2819
3146
  append(role, content, extra) {
@@ -2826,7 +3153,7 @@ var ChatStreamGraph = class extends Graph {
2826
3153
  this._log.clear();
2827
3154
  }
2828
3155
  allMessages() {
2829
- return this.messages.get();
3156
+ return this.messages.cache;
2830
3157
  }
2831
3158
  };
2832
3159
  function chatStream(name, opts) {
@@ -2854,31 +3181,30 @@ var ToolRegistryGraph = class extends Graph {
2854
3181
  }
2855
3182
  );
2856
3183
  this.add("schemas", this.schemas);
2857
- this.connect("definitions", "schemas");
2858
3184
  this.addDisposer(keepalive(this.schemas));
2859
3185
  }
2860
3186
  register(tool) {
2861
- const current = this.definitions.get();
3187
+ const current = this.definitions.cache;
2862
3188
  const next = new Map(current);
2863
3189
  next.set(tool.name, tool);
2864
3190
  this.definitions.down([[DATA, next]]);
2865
3191
  }
2866
3192
  unregister(name) {
2867
- const current = this.definitions.get();
3193
+ const current = this.definitions.cache;
2868
3194
  if (!current.has(name)) return;
2869
3195
  const next = new Map(current);
2870
3196
  next.delete(name);
2871
3197
  this.definitions.down([[DATA, next]]);
2872
3198
  }
2873
3199
  async execute(name, args) {
2874
- const defs = this.definitions.get();
3200
+ const defs = this.definitions.cache;
2875
3201
  const tool = defs.get(name);
2876
3202
  if (!tool) throw new Error(`toolRegistry: unknown tool "${name}"`);
2877
3203
  const raw = tool.handler(args);
2878
3204
  return resolveToolHandlerResult(raw);
2879
3205
  }
2880
3206
  getDefinition(name) {
2881
- return this.definitions.get().get(name);
3207
+ return this.definitions.cache?.get(name);
2882
3208
  }
2883
3209
  };
2884
3210
  function toolRegistry(name, opts) {
@@ -2913,7 +3239,7 @@ function llmExtractor(systemPrompt, opts) {
2913
3239
  })
2914
3240
  }
2915
3241
  ];
2916
- return producer((_deps, actions) => {
3242
+ return producer((actions) => {
2917
3243
  let active = true;
2918
3244
  const result = opts.adapter.invoke(messages, {
2919
3245
  model: opts.model,
@@ -2963,7 +3289,7 @@ function llmConsolidator(systemPrompt, opts) {
2963
3289
  { role: "system", content: systemPrompt },
2964
3290
  { role: "user", content: JSON.stringify({ memories: entriesArray }) }
2965
3291
  ];
2966
- return producer((_deps, actions) => {
3292
+ return producer((actions) => {
2967
3293
  let active = true;
2968
3294
  const result = opts.adapter.invoke(messages, {
2969
3295
  model: opts.model,
@@ -3078,8 +3404,6 @@ function agentMemory(name, source, opts) {
3078
3404
  graph.add("store", distillBundle.store.entries);
3079
3405
  graph.add("compact", distillBundle.compact);
3080
3406
  graph.add("size", distillBundle.size);
3081
- graph.connect("store", "compact");
3082
- graph.connect("store", "size");
3083
3407
  let vectors = null;
3084
3408
  if (opts.vectorDimensions && opts.vectorDimensions > 0 && opts.embedFn) {
3085
3409
  vectors = vectorIndex({ dimension: opts.vectorDimensions });
@@ -3102,7 +3426,7 @@ function agentMemory(name, source, opts) {
3102
3426
  const permanentKeys = /* @__PURE__ */ new Set();
3103
3427
  const tierOf = (key) => {
3104
3428
  if (permanentKeys.has(key)) return "permanent";
3105
- const storeMap = extractStoreMap(distillBundle.store.entries.get());
3429
+ const storeMap = extractStoreMap(distillBundle.store.entries.cache);
3106
3430
  if (storeMap.has(key)) return "active";
3107
3431
  return "archived";
3108
3432
  };
@@ -3161,10 +3485,10 @@ function agentMemory(name, source, opts) {
3161
3485
  });
3162
3486
  keepaliveSubs.push(tierClassifier.subscribe(() => void 0));
3163
3487
  let archiveHandle = null;
3164
- if (tiersOpts.archiveAdapter) {
3165
- archiveHandle = graph.autoCheckpoint(
3166
- tiersOpts.archiveAdapter,
3167
- tiersOpts.archiveCheckpointOptions
3488
+ if (tiersOpts.archiveTier) {
3489
+ archiveHandle = graph.attachStorage(
3490
+ [tiersOpts.archiveTier],
3491
+ tiersOpts.archiveStorageOptions ?? {}
3168
3492
  );
3169
3493
  }
3170
3494
  memoryTiersBundle = {
@@ -3210,12 +3534,14 @@ function agentMemory(name, source, opts) {
3210
3534
  const budget = opts.budget ?? 2e3;
3211
3535
  const costFn = opts.cost;
3212
3536
  const scoreFn = opts.score;
3213
- const queryInput = state(null, {
3214
- name: "retrievalQuery",
3215
- describeKind: "state"
3216
- });
3217
- graph.add("retrievalQuery", queryInput);
3218
3537
  const contextNode = opts.context ? fromAny(opts.context) : state(null);
3538
+ const retrievalOutput = state([], {
3539
+ name: "retrieval",
3540
+ describeKind: "state",
3541
+ meta: aiMeta("retrieval_pipeline")
3542
+ });
3543
+ graph.add("retrieval", retrievalOutput);
3544
+ retrievalNode = retrievalOutput;
3219
3545
  const traceState = state(null, {
3220
3546
  name: "retrievalTrace",
3221
3547
  describeKind: "state",
@@ -3223,97 +3549,81 @@ function agentMemory(name, source, opts) {
3223
3549
  });
3224
3550
  graph.add("retrievalTrace", traceState);
3225
3551
  retrievalTraceNode = traceState;
3226
- const storeNode = distillBundle.store.entries;
3227
- let lastTrace = null;
3228
- const retrievalDerived = derived(
3229
- [queryInput, storeNode, contextNode],
3230
- ([query, snapshot, ctx]) => {
3231
- if (!query) return [];
3232
- const q = query;
3233
- const storeMap = extractStoreMap(snapshot);
3234
- const candidateMap = /* @__PURE__ */ new Map();
3235
- let vectorCandidates = [];
3236
- if (vectors && q.vector) {
3237
- vectorCandidates = vectors.search(q.vector, topK);
3238
- for (const vc of vectorCandidates) {
3239
- const mem = storeMap.get(vc.id);
3240
- if (mem) {
3241
- candidateMap.set(vc.id, { value: mem, sources: /* @__PURE__ */ new Set(["vector"]) });
3242
- }
3552
+ retrieveFn = (query) => {
3553
+ const storeMap = extractStoreMap(distillBundle.store.entries.cache);
3554
+ const ctx = contextNode.cache;
3555
+ const candidateMap = /* @__PURE__ */ new Map();
3556
+ let vectorCandidates = [];
3557
+ if (vectors && query.vector) {
3558
+ vectorCandidates = vectors.search(query.vector, topK);
3559
+ for (const vc of vectorCandidates) {
3560
+ const mem = storeMap.get(vc.id);
3561
+ if (mem) {
3562
+ candidateMap.set(vc.id, { value: mem, sources: /* @__PURE__ */ new Set(["vector"]) });
3243
3563
  }
3244
3564
  }
3245
- const graphExpanded = [];
3246
- if (kg) {
3247
- const seedIds = [...q.entityIds ?? [], ...[...candidateMap.keys()]];
3248
- const visited = /* @__PURE__ */ new Set();
3249
- let frontier = seedIds;
3250
- for (let depth = 0; depth < graphDepth; depth++) {
3251
- const nextFrontier = [];
3252
- for (const id of frontier) {
3253
- if (visited.has(id)) continue;
3254
- visited.add(id);
3255
- const related = kg.related(id);
3256
- for (const edge of related) {
3257
- const targetId = edge.to;
3258
- if (!visited.has(targetId)) {
3259
- nextFrontier.push(targetId);
3260
- const mem = storeMap.get(targetId);
3261
- if (mem) {
3262
- const existing = candidateMap.get(targetId);
3263
- if (existing) {
3264
- existing.sources.add("graph");
3265
- } else {
3266
- candidateMap.set(targetId, { value: mem, sources: /* @__PURE__ */ new Set(["graph"]) });
3267
- }
3268
- graphExpanded.push(targetId);
3565
+ }
3566
+ const graphExpanded = [];
3567
+ if (kg) {
3568
+ const seedIds = [...query.entityIds ?? [], ...[...candidateMap.keys()]];
3569
+ const visited = /* @__PURE__ */ new Set();
3570
+ let frontier = seedIds;
3571
+ for (let depth = 0; depth < graphDepth; depth++) {
3572
+ const nextFrontier = [];
3573
+ for (const id of frontier) {
3574
+ if (visited.has(id)) continue;
3575
+ visited.add(id);
3576
+ const related = kg.related(id);
3577
+ for (const edge of related) {
3578
+ const targetId = edge.to;
3579
+ if (!visited.has(targetId)) {
3580
+ nextFrontier.push(targetId);
3581
+ const mem = storeMap.get(targetId);
3582
+ if (mem) {
3583
+ const existing = candidateMap.get(targetId);
3584
+ if (existing) {
3585
+ existing.sources.add("graph");
3586
+ } else {
3587
+ candidateMap.set(targetId, { value: mem, sources: /* @__PURE__ */ new Set(["graph"]) });
3269
3588
  }
3589
+ graphExpanded.push(targetId);
3270
3590
  }
3271
3591
  }
3272
3592
  }
3273
- frontier = nextFrontier;
3274
- }
3275
- }
3276
- for (const [key, mem] of storeMap) {
3277
- if (!candidateMap.has(key)) {
3278
- candidateMap.set(key, { value: mem, sources: /* @__PURE__ */ new Set(["store"]) });
3279
3593
  }
3594
+ frontier = nextFrontier;
3280
3595
  }
3281
- const ranked = [];
3282
- for (const [key, { value, sources }] of candidateMap) {
3283
- const score = scoreFn(value, ctx);
3284
- ranked.push({ key, value, score, sources: [...sources] });
3285
- }
3286
- ranked.sort((a, b) => b.score - a.score);
3287
- const packed = [];
3288
- let usedBudget = 0;
3289
- for (const entry of ranked) {
3290
- const c = costFn(entry.value);
3291
- if (usedBudget + c > budget && packed.length > 0) break;
3292
- packed.push(entry);
3293
- usedBudget += c;
3596
+ }
3597
+ for (const [key, mem] of storeMap) {
3598
+ if (!candidateMap.has(key)) {
3599
+ candidateMap.set(key, { value: mem, sources: /* @__PURE__ */ new Set(["store"]) });
3294
3600
  }
3295
- lastTrace = { vectorCandidates, graphExpanded, ranked, packed };
3296
- return packed;
3297
- },
3298
- {
3299
- name: "retrieval",
3300
- describeKind: "derived",
3301
- meta: aiMeta("retrieval_pipeline"),
3302
- initial: []
3303
3601
  }
3304
- );
3305
- graph.add("retrieval", retrievalDerived);
3306
- graph.connect("retrievalQuery", "retrieval");
3307
- graph.connect("store", "retrieval");
3308
- keepaliveSubs.push(retrievalDerived.subscribe(() => void 0));
3309
- retrievalNode = retrievalDerived;
3310
- retrieveFn = (query) => {
3311
- queryInput.down([[DATA, query]]);
3312
- const result = retrievalDerived.get();
3313
- if (lastTrace) {
3314
- traceState.down([[DATA, lastTrace]]);
3602
+ const ranked = [];
3603
+ for (const [key, { value, sources }] of candidateMap) {
3604
+ const score = scoreFn(value, ctx);
3605
+ ranked.push({ key, value, score, sources: [...sources] });
3315
3606
  }
3316
- return result;
3607
+ ranked.sort((a, b) => b.score - a.score);
3608
+ const packed = [];
3609
+ let usedBudget = 0;
3610
+ for (const entry of ranked) {
3611
+ const c = costFn(entry.value);
3612
+ if (usedBudget + c > budget && packed.length > 0) break;
3613
+ packed.push(entry);
3614
+ usedBudget += c;
3615
+ }
3616
+ const trace = {
3617
+ vectorCandidates,
3618
+ graphExpanded,
3619
+ ranked,
3620
+ packed
3621
+ };
3622
+ batch(() => {
3623
+ retrievalOutput.down([[DATA, packed]]);
3624
+ traceState.down([[DATA, trace]]);
3625
+ });
3626
+ return packed;
3317
3627
  };
3318
3628
  }
3319
3629
  graph.addDisposer(() => {
@@ -3417,7 +3727,7 @@ var AgentLoopGraph = class extends Graph {
3417
3727
  this._statusState.down([[DATA, "thinking"]]);
3418
3728
  });
3419
3729
  const msgs = this.chat.allMessages();
3420
- const toolSchemas = this.tools.schemas.get() ?? [];
3730
+ const toolSchemas = this.tools.schemas.cache ?? [];
3421
3731
  const response = await this._invokeLLM(msgs, toolSchemas, signal);
3422
3732
  if (signal.aborted) throw new Error("agentLoop: aborted");
3423
3733
  this.lastResponse.down([[DATA, response]]);
@@ -3452,7 +3762,7 @@ var AgentLoopGraph = class extends Graph {
3452
3762
  this._statusState.down([[DATA, "done"]]);
3453
3763
  this._running = false;
3454
3764
  this._abortController = null;
3455
- return this.lastResponse.get();
3765
+ return this.lastResponse.cache;
3456
3766
  } catch (err) {
3457
3767
  this._statusState.down([[DATA, "error"]]);
3458
3768
  this._running = false;
@@ -3948,7 +4258,7 @@ function demoShell(opts) {
3948
4258
  ([ref, _tick]) => {
3949
4259
  const demo = ref;
3950
4260
  if (!demo) return "";
3951
- return demo.toMermaid();
4261
+ return demo.describe({ format: "mermaid" });
3952
4262
  },
3953
4263
  { name: "graph/mermaid" }
3954
4264
  );
@@ -4004,7 +4314,6 @@ function demoShell(opts) {
4004
4314
  cb(line);
4005
4315
  });
4006
4316
  g.add("highlight/apply-code-scroll", applyCodeScroll);
4007
- g.connect("highlight/code-scroll", "highlight/apply-code-scroll");
4008
4317
  }
4009
4318
  if (onHighlight?.visual) {
4010
4319
  const cb = onHighlight.visual;
@@ -4012,7 +4321,6 @@ function demoShell(opts) {
4012
4321
  cb(selector);
4013
4322
  });
4014
4323
  g.add("highlight/apply-visual", applyVisual);
4015
- g.connect("highlight/visual", "highlight/apply-visual");
4016
4324
  }
4017
4325
  if (onHighlight?.graph) {
4018
4326
  const cb = onHighlight.graph;
@@ -4020,7 +4328,6 @@ function demoShell(opts) {
4020
4328
  cb(nodeId);
4021
4329
  });
4022
4330
  g.add("highlight/apply-graph", applyGraph);
4023
- g.connect("highlight/graph", "highlight/apply-graph");
4024
4331
  }
4025
4332
  const inspectSelected = state(null, {
4026
4333
  name: "inspect/selected-node"
@@ -4036,7 +4343,7 @@ function demoShell(opts) {
4036
4343
  try {
4037
4344
  const nd = demo.resolve(p);
4038
4345
  const nodeDesc = describeNode(nd, standardFields);
4039
- return { path: p, ...nodeDesc, value: nd.get() };
4346
+ return { path: p, ...nodeDesc, value: nd.cache };
4040
4347
  } catch {
4041
4348
  return null;
4042
4349
  }
@@ -4060,7 +4367,7 @@ function demoShell(opts) {
4060
4367
  [metaDebug, demoGraphTick],
4061
4368
  ([debug, _tick]) => {
4062
4369
  if (!debug) return "";
4063
- return g.toMermaid();
4370
+ return g.describe({ format: "mermaid" });
4064
4371
  },
4065
4372
  { name: "meta/shell-mermaid" }
4066
4373
  );
@@ -4126,35 +4433,7 @@ function demoShell(opts) {
4126
4433
  g.add("layout/graph-labels", graphLabels);
4127
4434
  g.add("layout/code-lines", codeLines);
4128
4435
  g.add("layout/side-width-hint", sideWidthHint);
4129
- g.connect("graph/describe", "layout/graph-labels");
4130
- g.connect("layout/code-text", "layout/code-lines");
4131
- g.connect("pane/side-width", "layout/code-lines");
4132
- g.connect("layout/graph-labels", "layout/side-width-hint");
4133
- }
4134
- g.connect("pane/main-ratio", "pane/main-width");
4135
- g.connect("viewport/width", "pane/main-width");
4136
- g.connect("pane/fullscreen", "pane/main-width");
4137
- g.connect("pane/main-width", "pane/side-width");
4138
- g.connect("viewport/width", "pane/side-width");
4139
- g.connect("pane/fullscreen", "pane/side-width");
4140
- g.connect("pane/side-split", "pane/graph-height-ratio");
4141
- g.connect("pane/fullscreen", "pane/graph-height-ratio");
4142
- g.connect("pane/graph-height-ratio", "pane/code-height-ratio");
4143
- g.connect("pane/fullscreen", "pane/code-height-ratio");
4144
- g.connect("demo/graph-ref", "graph/mermaid");
4145
- g.connect("demo/graph-tick", "graph/mermaid");
4146
- g.connect("demo/graph-ref", "graph/describe");
4147
- g.connect("demo/graph-tick", "graph/describe");
4148
- g.connect("hover/target", "highlight/code-scroll");
4149
- g.connect("hover/target", "highlight/visual");
4150
- g.connect("hover/target", "highlight/graph");
4151
- g.connect("inspect/selected-node", "inspect/node-detail");
4152
- g.connect("demo/graph-ref", "inspect/node-detail");
4153
- g.connect("demo/graph-tick", "inspect/node-detail");
4154
- g.connect("demo/graph-ref", "inspect/trace-log");
4155
- g.connect("demo/graph-tick", "inspect/trace-log");
4156
- g.connect("meta/debug", "meta/shell-mermaid");
4157
- g.connect("demo/graph-tick", "meta/shell-mermaid");
4436
+ }
4158
4437
  let tickCounter = 0;
4159
4438
  return {
4160
4439
  graph: g,
@@ -4239,14 +4518,14 @@ function _addBranch(graph, source, rulesNode, rule) {
4239
4518
  let sourcePhase2 = false;
4240
4519
  let sourceValue = _noValue;
4241
4520
  let pendingDirty = false;
4521
+ let latestRules = rulesNode.cache ?? [];
4242
4522
  function resolve(actions) {
4243
4523
  if (sourcePhase2) {
4244
4524
  sourcePhase2 = false;
4245
4525
  const value = sourceValue;
4246
4526
  sourceValue = _noValue;
4247
4527
  if (value !== _noValue) {
4248
- const currentRules = rulesNode.get();
4249
- const currentRule = currentRules.find((r) => r.name === rule.name);
4528
+ const currentRule = latestRules.find((r) => r.name === rule.name);
4250
4529
  let matches = false;
4251
4530
  try {
4252
4531
  matches = currentRule?.classify(value) ?? false;
@@ -4272,55 +4551,75 @@ function _addBranch(graph, source, rulesNode, rule) {
4272
4551
  }
4273
4552
  }
4274
4553
  }
4275
- const filterNode = node([source, rulesNode], () => void 0, {
4276
- describeKind: "operator",
4277
- meta: baseMeta2("stratify_branch", { branch: rule.name }),
4278
- onMessage(msg, depIndex, actions) {
4279
- const t = msg[0];
4280
- if (t === DIRTY) {
4281
- if (depIndex === 0) {
4282
- sourceDirty = true;
4283
- pendingDirty = true;
4284
- } else {
4285
- rulesDirty = true;
4554
+ const filterNode = node(
4555
+ [],
4556
+ (_data, filterActions) => {
4557
+ const srcUnsub = source.subscribe((msgs) => {
4558
+ for (const msg of msgs) {
4559
+ _handleStratifyMessage(msg, 0, filterActions);
4286
4560
  }
4287
- return true;
4288
- }
4289
- if (t === DATA || t === RESOLVED) {
4290
- if (depIndex === 0) {
4291
- sourceDirty = false;
4292
- sourcePhase2 = true;
4293
- sourceValue = t === DATA ? msg[1] : _noValue;
4294
- } else {
4295
- rulesDirty = false;
4561
+ });
4562
+ const rulesUnsub = rulesNode.subscribe((msgs) => {
4563
+ for (const msg of msgs) {
4564
+ _handleStratifyMessage(msg, 1, filterActions);
4296
4565
  }
4297
- if (sourceDirty || rulesDirty) return true;
4298
- resolve(actions);
4299
- return true;
4566
+ });
4567
+ return () => {
4568
+ srcUnsub();
4569
+ rulesUnsub();
4570
+ };
4571
+ },
4572
+ {
4573
+ describeKind: "derived",
4574
+ meta: baseMeta2("stratify_branch", { branch: rule.name }),
4575
+ completeWhenDepsComplete: false
4576
+ }
4577
+ );
4578
+ function _handleStratifyMessage(msg, depIndex, actions) {
4579
+ const t = msg[0];
4580
+ if (t === DIRTY) {
4581
+ if (depIndex === 0) {
4582
+ sourceDirty = true;
4583
+ pendingDirty = true;
4584
+ } else {
4585
+ rulesDirty = true;
4300
4586
  }
4301
- if (t === COMPLETE || t === ERROR || t === TEARDOWN) {
4587
+ return true;
4588
+ }
4589
+ if (t === DATA || t === RESOLVED) {
4590
+ if (depIndex === 0) {
4302
4591
  sourceDirty = false;
4303
- rulesDirty = false;
4304
- sourcePhase2 = false;
4305
- sourceValue = _noValue;
4306
- pendingDirty = false;
4307
- if (depIndex === 0) {
4308
- actions.down([msg]);
4592
+ sourcePhase2 = true;
4593
+ sourceValue = t === DATA ? msg[1] : _noValue;
4594
+ } else {
4595
+ if (t === DATA) {
4596
+ latestRules = msg[1];
4309
4597
  }
4310
- return true;
4598
+ rulesDirty = false;
4311
4599
  }
4312
- if (depIndex === 1) return true;
4313
- return false;
4314
- },
4315
- completeWhenDepsComplete: false
4316
- });
4600
+ if (sourceDirty || rulesDirty) return true;
4601
+ resolve(actions);
4602
+ return true;
4603
+ }
4604
+ if (t === COMPLETE || t === ERROR || t === TEARDOWN) {
4605
+ sourceDirty = false;
4606
+ rulesDirty = false;
4607
+ sourcePhase2 = false;
4608
+ sourceValue = _noValue;
4609
+ pendingDirty = false;
4610
+ if (depIndex === 0) {
4611
+ actions.down([msg]);
4612
+ }
4613
+ return true;
4614
+ }
4615
+ if (depIndex === 1) return true;
4616
+ return false;
4617
+ }
4317
4618
  graph.add(branchName, filterNode);
4318
- graph.connect("source", branchName);
4319
4619
  if (rule.ops) {
4320
4620
  const transformed = rule.ops(filterNode);
4321
4621
  const transformedName = `branch/${rule.name}/out`;
4322
4622
  graph.add(transformedName, transformed);
4323
- graph.connect(branchName, transformedName);
4324
4623
  }
4325
4624
  }
4326
4625
  function funnel(name, sources, stages, opts) {
@@ -4349,12 +4648,14 @@ function funnel(name, sources, stages, opts) {
4349
4648
  const stageInputPath = `${stage.name}::input`;
4350
4649
  const stageInput = g.resolve(stageInputPath);
4351
4650
  const bridgeName = `__bridge_${prevOutputPath}\u2192${stage.name}_input`;
4352
- const br = bridge(prevNode, stageInput, {
4353
- name: bridgeName,
4354
- down: DEFAULT_DOWN.filter((t) => t !== TEARDOWN)
4355
- });
4651
+ const br = effect(
4652
+ [prevNode],
4653
+ ([data]) => {
4654
+ stageInput.down([[DATA, data]]);
4655
+ },
4656
+ { name: bridgeName }
4657
+ );
4356
4658
  g.add(bridgeName, br);
4357
- g.connect(prevOutputPath, bridgeName);
4358
4659
  g.addDisposer(keepalive(br));
4359
4660
  prevOutputPath = `${stage.name}::output`;
4360
4661
  }
@@ -4374,39 +4675,41 @@ function feedback(graph, condition, reentry, opts) {
4374
4675
  const condNode = graph.resolve(condition);
4375
4676
  const reentryNode = graph.resolve(reentry);
4376
4677
  const feedbackEffectName = `__feedback_effect_${condition}`;
4377
- const feedbackEffect = node([condNode], void 0, {
4378
- name: feedbackEffectName,
4379
- describeKind: "effect",
4380
- meta: {
4381
- ...baseMeta2("feedback_effect", {
4382
- feedbackFrom: condition,
4383
- feedbackTo: reentry
4384
- }),
4385
- _internal: true
4678
+ const feedbackEffect = node(
4679
+ [],
4680
+ (_data, _feedbackActions) => {
4681
+ const unsub = condNode.subscribe((msgs) => {
4682
+ for (const msg of msgs) {
4683
+ const t = msg[0];
4684
+ if (t === DATA) {
4685
+ const condValue = msg[1];
4686
+ if (condValue == null) return;
4687
+ batch(() => {
4688
+ if (tryIncrementBounded(counter, maxIter)) {
4689
+ reentryNode.down([[DATA, condValue]]);
4690
+ }
4691
+ });
4692
+ } else if (t === COMPLETE || t === ERROR) {
4693
+ const terminal = t === ERROR && msg.length > 1 ? [ERROR, msg[1]] : [t];
4694
+ counter.down([terminal]);
4695
+ }
4696
+ }
4697
+ });
4698
+ return () => unsub();
4386
4699
  },
4387
- onMessage(msg, _depIndex, _actions) {
4388
- const t = msg[0];
4389
- if (t === DATA) {
4390
- const currentCount = counter.get();
4391
- if (currentCount >= maxIter) return true;
4392
- const condValue = msg[1];
4393
- if (condValue == null) return true;
4394
- batch(() => {
4395
- counter.down([[DATA, currentCount + 1]]);
4396
- reentryNode.down([[DATA, condValue]]);
4397
- });
4398
- return true;
4399
- }
4400
- if (t === COMPLETE || t === ERROR) {
4401
- const terminal = t === ERROR && msg.length > 1 ? [ERROR, msg[1]] : [t];
4402
- counter.down([terminal]);
4403
- return true;
4700
+ {
4701
+ name: feedbackEffectName,
4702
+ describeKind: "effect",
4703
+ meta: {
4704
+ ...baseMeta2("feedback_effect", {
4705
+ feedbackFrom: condition,
4706
+ feedbackTo: reentry
4707
+ }),
4708
+ _internal: true
4404
4709
  }
4405
- return false;
4406
4710
  }
4407
- });
4711
+ );
4408
4712
  graph.add(feedbackEffectName, feedbackEffect);
4409
- graph.connect(condition, feedbackEffectName);
4410
4713
  graph.addDisposer(keepalive(feedbackEffect));
4411
4714
  return graph;
4412
4715
  }
@@ -4418,8 +4721,9 @@ function budgetGate(source, constraints, opts) {
4418
4721
  let paused = false;
4419
4722
  let pendingResolved = false;
4420
4723
  const lockId = /* @__PURE__ */ Symbol("budget-gate");
4724
+ const latestValues = new Array(constraints.length);
4421
4725
  function checkBudget() {
4422
- return constraints.every((c) => c.check(c.node.get()));
4726
+ return constraints.every((c, i) => c.check(latestValues[i]));
4423
4727
  }
4424
4728
  function flushBuffer(actions) {
4425
4729
  while (buffer2.length > 0 && checkBudget()) {
@@ -4432,78 +4736,103 @@ function budgetGate(source, constraints, opts) {
4432
4736
  actions.down([[RESOLVED]]);
4433
4737
  }
4434
4738
  }
4435
- return node(allDeps, () => void 0, {
4436
- ...opts,
4437
- describeKind: "operator",
4438
- meta: baseMeta2("budget_gate", opts?.meta),
4439
- onMessage(msg, depIndex, actions) {
4440
- const t = msg[0];
4441
- if (depIndex === 0) {
4442
- if (t === DATA) {
4443
- if (checkBudget() && buffer2.length === 0) {
4444
- actions.emit(msg[1]);
4445
- } else {
4446
- buffer2.push(msg[1]);
4447
- if (!paused) {
4448
- paused = true;
4449
- actions.up([[PAUSE, lockId]]);
4739
+ return node(
4740
+ [],
4741
+ (_data, gateActions) => {
4742
+ for (let i = 0; i < constraints.length; i++) {
4743
+ latestValues[i] = constraints[i].node.cache;
4744
+ }
4745
+ const unsubs = [];
4746
+ for (let depIdx = 0; depIdx < allDeps.length; depIdx++) {
4747
+ const dep = allDeps[depIdx];
4748
+ unsubs.push(
4749
+ dep.subscribe((msgs) => {
4750
+ for (const msg of msgs) {
4751
+ _handleBudgetMessage(msg, depIdx, gateActions);
4450
4752
  }
4451
- }
4452
- return true;
4453
- }
4454
- if (t === DIRTY) {
4455
- actions.down([[DIRTY]]);
4456
- return true;
4457
- }
4458
- if (t === RESOLVED) {
4459
- if (buffer2.length === 0) {
4460
- actions.down([[RESOLVED]]);
4461
- } else {
4462
- pendingResolved = true;
4463
- }
4464
- return true;
4465
- }
4466
- if (t === COMPLETE || t === ERROR) {
4467
- for (const item of buffer2) {
4468
- actions.emit(item);
4469
- }
4470
- buffer2 = [];
4471
- pendingResolved = false;
4472
- if (paused) {
4473
- paused = false;
4474
- actions.up([[RESUME, lockId]]);
4475
- }
4476
- actions.down([msg]);
4477
- return true;
4478
- }
4479
- return false;
4753
+ })
4754
+ );
4480
4755
  }
4481
- if (t === DATA || t === RESOLVED) {
4482
- if (checkBudget() && buffer2.length > 0) {
4483
- flushBuffer(actions);
4484
- if (buffer2.length === 0 && paused) {
4485
- paused = false;
4486
- actions.up([[RESUME, lockId]]);
4756
+ return () => {
4757
+ for (const u of unsubs) u();
4758
+ };
4759
+ },
4760
+ {
4761
+ ...opts,
4762
+ describeKind: "derived",
4763
+ meta: baseMeta2("budget_gate", opts?.meta)
4764
+ }
4765
+ );
4766
+ function _handleBudgetMessage(msg, depIndex, actions) {
4767
+ const t = msg[0];
4768
+ if (depIndex === 0) {
4769
+ if (t === DATA) {
4770
+ if (checkBudget() && buffer2.length === 0) {
4771
+ actions.emit(msg[1]);
4772
+ } else {
4773
+ buffer2.push(msg[1]);
4774
+ if (!paused) {
4775
+ paused = true;
4776
+ actions.up([[PAUSE, lockId]]);
4487
4777
  }
4488
- } else if (!checkBudget() && !paused && buffer2.length > 0) {
4489
- paused = true;
4490
- actions.up([[PAUSE, lockId]]);
4491
4778
  }
4492
4779
  return true;
4493
4780
  }
4494
4781
  if (t === DIRTY) {
4782
+ actions.down([[DIRTY]]);
4495
4783
  return true;
4496
4784
  }
4497
- if (t === ERROR) {
4498
- actions.down([msg]);
4785
+ if (t === RESOLVED) {
4786
+ if (buffer2.length === 0) {
4787
+ actions.down([[RESOLVED]]);
4788
+ } else {
4789
+ pendingResolved = true;
4790
+ }
4499
4791
  return true;
4500
4792
  }
4501
- if (t === COMPLETE) {
4793
+ if (t === COMPLETE || t === ERROR) {
4794
+ for (const item of buffer2) {
4795
+ actions.emit(item);
4796
+ }
4797
+ buffer2 = [];
4798
+ pendingResolved = false;
4799
+ if (paused) {
4800
+ paused = false;
4801
+ actions.up([[RESUME, lockId]]);
4802
+ }
4803
+ actions.down([msg]);
4502
4804
  return true;
4503
4805
  }
4504
4806
  return false;
4505
4807
  }
4506
- });
4808
+ if (t === DATA) {
4809
+ latestValues[depIndex - 1] = msg[1];
4810
+ }
4811
+ if (t === DATA || t === RESOLVED) {
4812
+ if (checkBudget() && buffer2.length > 0) {
4813
+ flushBuffer(actions);
4814
+ if (buffer2.length === 0 && paused) {
4815
+ paused = false;
4816
+ actions.up([[RESUME, lockId]]);
4817
+ }
4818
+ } else if (!checkBudget() && !paused && buffer2.length > 0) {
4819
+ paused = true;
4820
+ actions.up([[PAUSE, lockId]]);
4821
+ }
4822
+ return true;
4823
+ }
4824
+ if (t === DIRTY) {
4825
+ return true;
4826
+ }
4827
+ if (t === ERROR) {
4828
+ actions.down([msg]);
4829
+ return true;
4830
+ }
4831
+ if (t === COMPLETE) {
4832
+ return true;
4833
+ }
4834
+ return false;
4835
+ }
4507
4836
  }
4508
4837
  function scorer(sources, weights, opts) {
4509
4838
  if (sources.length === 0) throw new RangeError("scorer requires at least one source");
@@ -4535,7 +4864,11 @@ function scorer(sources, weights, opts) {
4535
4864
  };
4536
4865
  },
4537
4866
  {
4538
- ...opts,
4867
+ ...opts ? {
4868
+ equals: opts.equals,
4869
+ resubscribable: opts.resubscribable,
4870
+ resetOnTeardown: opts.resetOnTeardown
4871
+ } : {},
4539
4872
  describeKind: "derived",
4540
4873
  meta: baseMeta2("scorer", opts?.meta)
4541
4874
  }
@@ -4623,12 +4956,6 @@ function observabilityGraph(name, opts) {
4623
4956
  }
4624
4957
  );
4625
4958
  g.add("correlate", correlateNode);
4626
- for (const b of branches) {
4627
- try {
4628
- g.connect(`stratify::branch/${b.name}`, "correlate");
4629
- } catch {
4630
- }
4631
- }
4632
4959
  const sloCheckFn = opts.sloCheck ?? (() => ({ pass: true }));
4633
4960
  const sloValue = derived([correlateNode], (vals) => vals[0], {
4634
4961
  meta: baseMeta3("observability", { stage: "slo_value" })
@@ -4638,8 +4965,6 @@ function observabilityGraph(name, opts) {
4638
4965
  });
4639
4966
  g.add("slo_value", sloValue);
4640
4967
  g.add("slo_verified", sloVerified);
4641
- g.connect("correlate", "slo_value");
4642
- g.connect("slo_value", "slo_verified");
4643
4968
  const weightValues = opts.weights ?? branches.map(() => 1);
4644
4969
  const signalNodes = branchNodes.map(
4645
4970
  (bn) => derived([bn], (vals) => vals[0] != null ? 1 : 0)
@@ -4665,8 +4990,6 @@ function observabilityGraph(name, opts) {
4665
4990
  }
4666
4991
  );
4667
4992
  g.add("output", output);
4668
- g.connect("alerts", "output");
4669
- g.connect("slo_verified", "output");
4670
4993
  const fbReentry = state(null, {
4671
4994
  meta: baseMeta3("observability", { stage: "feedback_reentry" })
4672
4995
  });
@@ -4683,7 +5006,6 @@ function observabilityGraph(name, opts) {
4683
5006
  }
4684
5007
  );
4685
5008
  g.add("feedback_condition", fbCondition);
4686
- g.connect("slo_verified", "feedback_condition");
4687
5009
  feedback(g, "feedback_condition", "feedback_reentry", {
4688
5010
  maxIterations: opts.maxFeedbackIterations ?? 5
4689
5011
  });
@@ -4705,7 +5027,6 @@ function issueTrackerGraph(name, opts) {
4705
5027
  meta: baseMeta3("issue_tracker", { stage: "extract" })
4706
5028
  });
4707
5029
  g.add("extract", extractNode);
4708
- g.connect("source", "extract");
4709
5030
  const verifyFn = opts.verify ?? (() => ({ valid: true }));
4710
5031
  const verifyNode = derived(
4711
5032
  [extractNode],
@@ -4718,7 +5039,6 @@ function issueTrackerGraph(name, opts) {
4718
5039
  }
4719
5040
  );
4720
5041
  g.add("verify", verifyNode);
4721
- g.connect("extract", "verify");
4722
5042
  const knownPatterns = state([], {
4723
5043
  meta: baseMeta3("issue_tracker", { stage: "known_patterns" })
4724
5044
  });
@@ -4734,8 +5054,6 @@ function issueTrackerGraph(name, opts) {
4734
5054
  { meta: baseMeta3("issue_tracker", { stage: "regression" }) }
4735
5055
  );
4736
5056
  g.add("regression", regressionNode);
4737
- g.connect("extract", "regression");
4738
- g.connect("known_patterns", "regression");
4739
5057
  const severitySignal = derived([extractNode], (vals) => {
4740
5058
  const issue = vals[0];
4741
5059
  return issue?.severity ?? 0;
@@ -4762,9 +5080,6 @@ function issueTrackerGraph(name, opts) {
4762
5080
  { meta: baseMeta3("issue_tracker", { stage: "output" }) }
4763
5081
  );
4764
5082
  g.add("output", output);
4765
- g.connect("verify", "output");
4766
- g.connect("regression", "output");
4767
- g.connect("priority", "output");
4768
5083
  const fbReentry = state(null, {
4769
5084
  meta: baseMeta3("issue_tracker", { stage: "feedback_reentry" })
4770
5085
  });
@@ -4784,7 +5099,6 @@ function issueTrackerGraph(name, opts) {
4784
5099
  }
4785
5100
  );
4786
5101
  g.add("feedback_condition", fbCondition);
4787
- g.connect("verify", "feedback_condition");
4788
5102
  feedback(g, "feedback_condition", "feedback_reentry", {
4789
5103
  maxIterations: opts.maxFeedbackIterations ?? 3
4790
5104
  });
@@ -4803,7 +5117,6 @@ function contentModerationGraph(name, opts) {
4803
5117
  meta: baseMeta3("content_moderation", { stage: "classify" })
4804
5118
  });
4805
5119
  g.add("classify", classifyNode);
4806
- g.connect("source", "classify");
4807
5120
  const strat = stratify("stratify", classifyNode, [
4808
5121
  { name: "safe", classify: (v) => v.label === "safe" },
4809
5122
  { name: "review", classify: (v) => v.label === "review" },
@@ -4831,7 +5144,6 @@ function contentModerationGraph(name, opts) {
4831
5144
  g.add("__review_accumulator", reviewAccumulator);
4832
5145
  g.addDisposer(keepalive(reviewAccumulator));
4833
5146
  try {
4834
- g.connect("stratify::branch/review", "__review_accumulator");
4835
5147
  } catch {
4836
5148
  }
4837
5149
  const policy2 = state(
@@ -4872,8 +5184,6 @@ function contentModerationGraph(name, opts) {
4872
5184
  { meta: baseMeta3("content_moderation", { stage: "output" }) }
4873
5185
  );
4874
5186
  g.add("output", output);
4875
- g.connect("classify", "output");
4876
- g.connect("priority", "output");
4877
5187
  const fbCondition = derived(
4878
5188
  [reviewLog.entries, policy2],
4879
5189
  (vals) => {
@@ -4910,7 +5220,6 @@ function dataQualityGraph(name, opts) {
4910
5220
  { meta: baseMeta3("data_quality", { stage: "validate" }) }
4911
5221
  );
4912
5222
  g.add("validate", validateNode);
4913
- g.connect("source", "validate");
4914
5223
  const detectAnomalyFn = opts.detectAnomaly ?? ((record) => ({
4915
5224
  anomaly: false,
4916
5225
  score: 0,
@@ -4922,7 +5231,6 @@ function dataQualityGraph(name, opts) {
4922
5231
  { meta: baseMeta3("data_quality", { stage: "anomaly" }) }
4923
5232
  );
4924
5233
  g.add("anomaly", anomalyNode);
4925
- g.connect("source", "anomaly");
4926
5234
  const baseline = state(null, {
4927
5235
  meta: baseMeta3("data_quality", {
4928
5236
  stage: "baseline",
@@ -4939,7 +5247,6 @@ function dataQualityGraph(name, opts) {
4939
5247
  }
4940
5248
  });
4941
5249
  g.add("__baseline_updater", baselineUpdater);
4942
- g.connect("validate", "__baseline_updater");
4943
5250
  keepalive(baselineUpdater);
4944
5251
  const detectDriftFn = opts.detectDrift ?? (() => ({ drift: false }));
4945
5252
  const driftNode = derived(
@@ -4948,8 +5255,6 @@ function dataQualityGraph(name, opts) {
4948
5255
  { meta: baseMeta3("data_quality", { stage: "drift" }) }
4949
5256
  );
4950
5257
  g.add("drift", driftNode);
4951
- g.connect("source", "drift");
4952
- g.connect("baseline", "drift");
4953
5258
  const suggestFn = opts.suggest ?? (() => null);
4954
5259
  const remediateNode = derived(
4955
5260
  [validateNode, anomalyNode],
@@ -4960,8 +5265,6 @@ function dataQualityGraph(name, opts) {
4960
5265
  { meta: baseMeta3("data_quality", { stage: "remediate" }) }
4961
5266
  );
4962
5267
  g.add("remediate", remediateNode);
4963
- g.connect("validate", "remediate");
4964
- g.connect("anomaly", "remediate");
4965
5268
  const output = derived(
4966
5269
  [validateNode, anomalyNode, driftNode, remediateNode],
4967
5270
  (vals) => ({
@@ -4973,10 +5276,6 @@ function dataQualityGraph(name, opts) {
4973
5276
  { meta: baseMeta3("data_quality", { stage: "output" }) }
4974
5277
  );
4975
5278
  g.add("output", output);
4976
- g.connect("validate", "output");
4977
- g.connect("anomaly", "output");
4978
- g.connect("drift", "output");
4979
- g.connect("remediate", "output");
4980
5279
  const validationRules = state([], {
4981
5280
  meta: baseMeta3("data_quality", { stage: "validation_rules" })
4982
5281
  });
@@ -4993,7 +5292,6 @@ function dataQualityGraph(name, opts) {
4993
5292
  }
4994
5293
  );
4995
5294
  g.add("feedback_condition", fbCondition);
4996
- g.connect("anomaly", "feedback_condition");
4997
5295
  feedback(g, "feedback_condition", "validation_rules", {
4998
5296
  maxIterations: opts.maxFeedbackIterations ?? 3
4999
5297
  });
@@ -5519,20 +5817,6 @@ ${catalogValidation.errors.join("\n")}`
5519
5817
  } catch {
5520
5818
  }
5521
5819
  }
5522
- for (const [name, raw] of Object.entries(spec.nodes)) {
5523
- if (raw.type === "template") continue;
5524
- const n = raw;
5525
- for (const dep of n.deps ?? []) {
5526
- try {
5527
- g.connect(dep, name);
5528
- } catch (err) {
5529
- const msg = err instanceof Error ? err.message : "";
5530
- if (!msg.includes("constructor deps") && !msg.includes("already")) {
5531
- throw err;
5532
- }
5533
- }
5534
- }
5535
- }
5536
5820
  for (const fb of spec.feedback ?? []) {
5537
5821
  feedback(g, fb.from, fb.to, {
5538
5822
  maxIterations: fb.maxIterations
@@ -6030,12 +6314,17 @@ __export(harness_exports, {
6030
6314
  DEFAULT_SEVERITY_WEIGHTS: () => DEFAULT_SEVERITY_WEIGHTS,
6031
6315
  HarnessGraph: () => HarnessGraph,
6032
6316
  QUEUE_NAMES: () => QUEUE_NAMES,
6317
+ affectedTaskFilter: () => affectedTaskFilter,
6318
+ beforeAfterCompare: () => beforeAfterCompare,
6319
+ codeChangeBridge: () => codeChangeBridge,
6033
6320
  createIntakeBridge: () => createIntakeBridge,
6034
6321
  defaultErrorClassifier: () => defaultErrorClassifier,
6035
6322
  evalIntakeBridge: () => evalIntakeBridge,
6323
+ evalSource: () => evalSource,
6036
6324
  harnessLoop: () => harnessLoop,
6037
6325
  harnessProfile: () => harnessProfile,
6038
6326
  harnessTrace: () => harnessTrace,
6327
+ notifyEffect: () => notifyEffect,
6039
6328
  priorityScore: () => priorityScore,
6040
6329
  strategyKey: () => strategyKey,
6041
6330
  strategyModel: () => strategyModel
@@ -6055,10 +6344,10 @@ function createIntakeBridge(source, intakeTopic, parser, opts) {
6055
6344
  { name: opts?.name ?? "intake-bridge" }
6056
6345
  );
6057
6346
  }
6058
- function evalIntakeBridge(evalSource, intakeTopic, opts) {
6347
+ function evalIntakeBridge(evalSource2, intakeTopic, opts) {
6059
6348
  const defaultSeverity = opts?.defaultSeverity ?? "medium";
6060
6349
  return effect(
6061
- [evalSource],
6350
+ [evalSource2],
6062
6351
  ([results]) => {
6063
6352
  if (results == null) return;
6064
6353
  const runs = Array.isArray(results) ? results : [results];
@@ -6095,6 +6384,110 @@ function evalIntakeBridge(evalSource, intakeTopic, opts) {
6095
6384
  { name: opts?.name ?? "eval-intake-bridge" }
6096
6385
  );
6097
6386
  }
6387
+ function evalSource(trigger2, runner) {
6388
+ return switchMap(trigger2, () => fromAny(runner()));
6389
+ }
6390
+ function beforeAfterCompare(before, after) {
6391
+ return derived(
6392
+ [before, after],
6393
+ ([b, a]) => {
6394
+ const bRes = b;
6395
+ const aRes = a;
6396
+ const beforeMap = new Map(bRes.tasks.map((t) => [t.task_id, t]));
6397
+ const afterMap = new Map(aRes.tasks.map((t) => [t.task_id, t]));
6398
+ const allIds = /* @__PURE__ */ new Set([...beforeMap.keys(), ...afterMap.keys()]);
6399
+ const taskDeltas = [];
6400
+ const newFailures = [];
6401
+ const resolved = [];
6402
+ for (const id of allIds) {
6403
+ const bt = beforeMap.get(id);
6404
+ const at = afterMap.get(id);
6405
+ const beforeValid = bt?.valid ?? false;
6406
+ const afterValid = at?.valid ?? false;
6407
+ const beforeScore = bt?.judge_scores ? bt.judge_scores.filter((s) => s.pass).length : void 0;
6408
+ const afterScore = at?.judge_scores ? at.judge_scores.filter((s) => s.pass).length : void 0;
6409
+ const scoreDiff = beforeScore !== void 0 && afterScore !== void 0 ? afterScore - beforeScore : void 0;
6410
+ taskDeltas.push({ taskId: id, before: beforeValid, after: afterValid, scoreDiff });
6411
+ if (beforeValid && !afterValid) newFailures.push(id);
6412
+ if (!beforeValid && afterValid) resolved.push(id);
6413
+ }
6414
+ return {
6415
+ newFailures,
6416
+ resolved,
6417
+ taskDeltas,
6418
+ overallImproved: resolved.length > newFailures.length
6419
+ };
6420
+ },
6421
+ { name: "eval-delta" }
6422
+ );
6423
+ }
6424
+ function affectedTaskFilter(issues, fullTaskSet) {
6425
+ const taskSetNode = fullTaskSet == null ? null : Array.isArray(fullTaskSet) ? state(fullTaskSet) : fullTaskSet;
6426
+ const deps = [issues];
6427
+ if (taskSetNode) deps.push(taskSetNode);
6428
+ return derived(
6429
+ deps,
6430
+ (values) => {
6431
+ const items = values[0];
6432
+ const all = taskSetNode ? new Set(values[1]) : null;
6433
+ const affected = /* @__PURE__ */ new Set();
6434
+ for (const item of items) {
6435
+ for (const id of item.affectsEvalTasks ?? []) {
6436
+ if (all == null || all.has(id)) affected.add(id);
6437
+ }
6438
+ }
6439
+ return [...affected].sort();
6440
+ },
6441
+ { name: "affected-task-filter" }
6442
+ );
6443
+ }
6444
+ function codeChangeBridge(source, intakeTopic, parser, opts) {
6445
+ const defaultSeverity = opts?.defaultSeverity ?? "high";
6446
+ function defaultParser(change) {
6447
+ const items = [];
6448
+ for (const err of change.lintErrors ?? []) {
6449
+ items.push({
6450
+ source: "code-change",
6451
+ summary: `Lint: ${err.rule} in ${err.file}:${err.line}`,
6452
+ evidence: err.message,
6453
+ affectsAreas: [err.file],
6454
+ severity: defaultSeverity
6455
+ });
6456
+ }
6457
+ for (const fail of change.testFailures ?? []) {
6458
+ items.push({
6459
+ source: "test",
6460
+ summary: `Test failure: ${fail.testId}`,
6461
+ evidence: fail.message,
6462
+ affectsAreas: [fail.file],
6463
+ affectsEvalTasks: [fail.testId],
6464
+ severity: defaultSeverity
6465
+ });
6466
+ }
6467
+ return items;
6468
+ }
6469
+ const resolve = parser ?? defaultParser;
6470
+ return effect(
6471
+ [source],
6472
+ ([change]) => {
6473
+ if (change == null) return;
6474
+ for (const item of resolve(change)) {
6475
+ intakeTopic.publish(item);
6476
+ }
6477
+ },
6478
+ { name: opts?.name ?? "code-change-bridge" }
6479
+ );
6480
+ }
6481
+ function notifyEffect(topic2, transport, opts) {
6482
+ return effect(
6483
+ [topic2.latest],
6484
+ ([item]) => {
6485
+ if (item == null) return;
6486
+ void transport(item);
6487
+ },
6488
+ { name: opts?.name ?? "notify-effect" }
6489
+ );
6490
+ }
6098
6491
 
6099
6492
  // src/patterns/harness/types.ts
6100
6493
  var QUEUE_NAMES = [
@@ -6314,6 +6707,7 @@ function harnessLoop(name, opts) {
6314
6707
  }
6315
6708
  const routerInput = withLatestFrom(triageNode, triageInput);
6316
6709
  const router = effect([routerInput], ([pair]) => {
6710
+ if (pair == null) return;
6317
6711
  const [classification, triagePair] = pair;
6318
6712
  if (!classification || !classification.route) return;
6319
6713
  const intakeItem = triagePair?.[0];
@@ -6359,32 +6753,43 @@ function harnessLoop(name, opts) {
6359
6753
  retries: 1
6360
6754
  }
6361
6755
  );
6756
+ const executeContextNode = withLatestFrom(
6757
+ executeNode,
6758
+ executeInput
6759
+ );
6362
6760
  const verifyResults = new TopicGraph("verify-results", { retainedLimit });
6363
6761
  const verifyNode = promptNode(
6364
6762
  adapter,
6365
- [executeNode, executeInput],
6366
- opts.verifyPrompt ?? ((execution, item) => DEFAULT_VERIFY_PROMPT.replace("{{execution}}", JSON.stringify(execution)).replace(
6367
- "{{item}}",
6368
- JSON.stringify(item)
6369
- )),
6763
+ [executeContextNode],
6764
+ opts.verifyPrompt ?? ((ctxPair) => {
6765
+ const [execution, item] = ctxPair;
6766
+ return DEFAULT_VERIFY_PROMPT.replace("{{execution}}", JSON.stringify(execution)).replace(
6767
+ "{{item}}",
6768
+ JSON.stringify(item)
6769
+ );
6770
+ }),
6370
6771
  {
6371
6772
  name: "verify",
6372
6773
  format: "json",
6373
6774
  retries: 1
6374
6775
  }
6375
6776
  );
6376
- const verifyWithExec = withLatestFrom(verifyNode, executeNode);
6377
6777
  const verifyContext = withLatestFrom(
6378
- verifyWithExec,
6379
- executeInput
6778
+ verifyNode,
6779
+ executeContextNode
6380
6780
  );
6381
6781
  const maxReingestions = opts.maxReingestions ?? 1;
6382
6782
  const maxTotalRetries = Math.min(opts.maxTotalRetries ?? maxRetries * 10, 100);
6383
6783
  const maxTotalReingestions = Math.min(opts.maxTotalReingestions ?? maxReingestions * 10, 100);
6384
6784
  const totalRetries = state(0);
6385
6785
  const totalReingestions = state(0);
6386
- const fastRetry = effect([verifyContext], ([ctx]) => {
6387
- const [[vo, execRaw], item] = ctx;
6786
+ const fastRetry = node([verifyContext], (batchData, _actions) => {
6787
+ const batch2 = batchData[0];
6788
+ if (batch2 == null || batch2.length === 0) return;
6789
+ const ctxVal = batch2[batch2.length - 1];
6790
+ if (ctxVal == null) return;
6791
+ const [vo, execCtx] = ctxVal;
6792
+ const [execRaw, item] = execCtx ?? [null, null];
6388
6793
  if (!vo || !item) return;
6389
6794
  const exec = {
6390
6795
  item,
@@ -6409,8 +6814,7 @@ function harnessLoop(name, opts) {
6409
6814
  detail: vr.findings.join("; ")
6410
6815
  });
6411
6816
  const itemRetries = item._retries ?? 0;
6412
- if (errClass === "self-correctable" && itemRetries < maxRetries && (totalRetries.get() ?? 0) < maxTotalRetries) {
6413
- totalRetries.down([[DIRTY], [DATA, (totalRetries.get() ?? 0) + 1]]);
6817
+ if (errClass === "self-correctable" && itemRetries < maxRetries && tryIncrementBounded(totalRetries, maxTotalRetries)) {
6414
6818
  const key = trackingKey(item);
6415
6819
  const retryItem = {
6416
6820
  ...item,
@@ -6424,8 +6828,7 @@ function harnessLoop(name, opts) {
6424
6828
  verifyResults.publish(vr);
6425
6829
  const key = trackingKey(item);
6426
6830
  const itemReingestions = item._reingestions ?? 0;
6427
- if (itemReingestions < maxReingestions && (totalReingestions.get() ?? 0) < maxTotalReingestions) {
6428
- totalReingestions.down([[DIRTY], [DATA, (totalReingestions.get() ?? 0) + 1]]);
6831
+ if (itemReingestions < maxReingestions && tryIncrementBounded(totalReingestions, maxTotalReingestions)) {
6429
6832
  intake.publish({
6430
6833
  source: "eval",
6431
6834
  summary: `Verification failed for: ${key}`,
@@ -6478,9 +6881,9 @@ function harnessProfile(harness, opts) {
6478
6881
  return {
6479
6882
  ...base,
6480
6883
  queueDepths,
6481
- strategyEntries: harness.strategy.node.get()?.size ?? 0,
6482
- totalRetries: harness.totalRetries.get() ?? 0,
6483
- totalReingestions: harness.totalReingestions.get() ?? 0
6884
+ strategyEntries: harness.strategy.node.cache?.size ?? 0,
6885
+ totalRetries: harness.totalRetries.cache ?? 0,
6886
+ totalReingestions: harness.totalReingestions.cache ?? 0
6484
6887
  };
6485
6888
  }
6486
6889
 
@@ -6587,56 +6990,69 @@ function truncate(s, max) {
6587
6990
  // src/index.ts
6588
6991
  var version = "0.0.0";
6589
6992
  export {
6590
- CLEANUP_RESULT,
6591
6993
  COMPLETE,
6994
+ COMPLETE_MSG,
6995
+ COMPLETE_ONLY_BATCH,
6592
6996
  CircuitOpenError,
6593
6997
  DATA,
6594
6998
  DEFAULT_ACTOR,
6595
- DEFAULT_DOWN,
6596
6999
  DIRTY,
6597
- DictCheckpointAdapter,
6598
- DynamicNodeImpl,
7000
+ DIRTY_MSG,
7001
+ DIRTY_ONLY_BATCH,
7002
+ ENVELOPE_VERSION,
6599
7003
  ERROR,
6600
- FileCheckpointAdapter,
6601
7004
  GRAPH_META_SEGMENT,
6602
7005
  Graph,
7006
+ GraphReFlyConfig,
6603
7007
  GuardDenied,
6604
7008
  INVALIDATE,
7009
+ INVALIDATE_MSG,
7010
+ INVALIDATE_ONLY_BATCH,
6605
7011
  JsonCodec,
6606
- MemoryCheckpointAdapter,
6607
7012
  NS_PER_MS,
6608
7013
  NS_PER_SEC,
7014
+ NativeIndexBackend,
7015
+ NativeListBackend,
7016
+ NativeLogBackend,
7017
+ NativeMapBackend,
7018
+ NativePubSubBackend,
7019
+ NodeImpl,
6609
7020
  PAUSE,
6610
7021
  RESOLVED,
7022
+ RESOLVED_MSG,
7023
+ RESOLVED_ONLY_BATCH,
6611
7024
  RESUME,
7025
+ RateLimiterOverflowError,
6612
7026
  ResettableTimer,
7027
+ OVERHEAD as SIZEOF_OVERHEAD,
7028
+ SIZEOF_SYMBOL,
6613
7029
  START,
6614
- SqliteCheckpointAdapter,
7030
+ START_MSG,
6615
7031
  TEARDOWN,
7032
+ TEARDOWN_MSG,
7033
+ TEARDOWN_ONLY_BATCH,
6616
7034
  TimeoutError,
6617
7035
  accessHintForGuard,
6618
7036
  advanceVersion,
6619
7037
  ai_exports as ai,
6620
7038
  audit,
7039
+ autoTrackNode,
6621
7040
  batch,
6622
- bridge,
6623
7041
  buffer,
6624
7042
  bufferCount,
6625
7043
  bufferTime,
6626
- cache,
6627
7044
  cached,
6628
7045
  cascadingCache,
6629
7046
  catchError,
6630
- checkpointNodeValue,
6631
7047
  checkpointToRedis,
6632
7048
  checkpointToS3,
6633
7049
  circuitBreaker,
6634
- cleanupResult,
6635
7050
  combine,
6636
7051
  combineLatest,
6637
7052
  compat_exports as compat,
6638
7053
  concat,
6639
7054
  concatMap,
7055
+ configure,
6640
7056
  constant,
6641
7057
  core_exports as core,
6642
7058
  cqrs_exports as cqrs,
@@ -6645,14 +7061,19 @@ export {
6645
7061
  createTransport,
6646
7062
  createVersioning,
6647
7063
  createWatermarkController,
7064
+ csvRows,
6648
7065
  debounce,
6649
7066
  debounceTime,
7067
+ decodeEnvelope,
6650
7068
  decorrelatedJitter,
7069
+ defaultConfig,
6651
7070
  defaultHash,
6652
7071
  delay,
6653
7072
  demo_shell_exports as demoShell,
6654
7073
  derived,
6655
7074
  deserializeError,
7075
+ dictStorage,
7076
+ diffForWAL,
6656
7077
  distill,
6657
7078
  distinctUntilChanged,
6658
7079
  domain_templates_exports as domainTemplates,
@@ -6661,12 +7082,16 @@ export {
6661
7082
  effect,
6662
7083
  elementAt,
6663
7084
  empty,
7085
+ encodeEnvelope,
6664
7086
  escapeRegexChar,
6665
7087
  exhaustMap,
6666
7088
  exponential,
7089
+ externalBundle,
7090
+ externalProducer,
6667
7091
  extra_exports as extra,
6668
7092
  fallback,
6669
7093
  fibonacci,
7094
+ fileStorage,
6670
7095
  filter,
6671
7096
  find,
6672
7097
  first,
@@ -6684,6 +7109,8 @@ export {
6684
7109
  fromFSWatch,
6685
7110
  fromGitHook,
6686
7111
  fromHTTP,
7112
+ fromHTTPPoll,
7113
+ fromHTTPStream,
6687
7114
  fromIDBRequest,
6688
7115
  fromIDBTransaction,
6689
7116
  fromIter,
@@ -6699,44 +7126,42 @@ export {
6699
7126
  fromPulsar,
6700
7127
  fromRabbitMQ,
6701
7128
  fromRedisStream,
7129
+ fromSSE,
6702
7130
  fromSqlite,
7131
+ fromSqliteCursor,
6703
7132
  fromStatsD,
6704
7133
  fromSyslog,
6705
7134
  fromTimer,
6706
7135
  fromWebSocket,
7136
+ fromWebSocketReconnect,
6707
7137
  fromWebhook,
6708
7138
  globToRegExp,
6709
7139
  graph_exports as graph,
6710
7140
  graphProfile,
6711
7141
  graphspec_exports as graphspec,
6712
7142
  harness_exports as harness,
7143
+ indexedDbStorage,
6713
7144
  interval,
6714
7145
  isBatching,
6715
- isKnownMessageType,
6716
- isLocalOnly,
6717
- isPhase2Message,
6718
- isTerminalMessage,
6719
7146
  isV1,
6720
7147
  jotai_exports as jotai,
6721
7148
  keepalive,
6722
- knownMessageTypes,
6723
7149
  last,
6724
7150
  reactive_layout_exports as layout,
6725
7151
  linear,
6726
- logSlice,
6727
7152
  lru,
6728
7153
  map,
6729
7154
  matchesAnyPattern,
6730
7155
  matchesCron,
6731
7156
  memory_exports as memory,
7157
+ memoryStorage,
6732
7158
  merge,
6733
7159
  mergeMap,
6734
- messageTier,
6735
7160
  messaging_exports as messaging,
6736
7161
  monotonicNs,
6737
7162
  nameToSignal,
6738
7163
  nanostores_exports as nanostores,
6739
- negotiateCodec,
7164
+ ndjsonRows,
6740
7165
  nestjs_exports as nestjs,
6741
7166
  never,
6742
7167
  node,
@@ -6748,14 +7173,12 @@ export {
6748
7173
  parsePrometheusText,
6749
7174
  parseStatsD,
6750
7175
  parseSyslog,
6751
- partitionForBatch,
6752
7176
  patterns_exports as patterns,
6753
7177
  pausable,
6754
7178
  pipe,
6755
7179
  policy,
6756
7180
  policyFromRules,
6757
7181
  producer,
6758
- propagatesToMeta,
6759
7182
  pubsub,
6760
7183
  race,
6761
7184
  rateLimiter,
@@ -6766,20 +7189,20 @@ export {
6766
7189
  reactiveList,
6767
7190
  reactiveLog,
6768
7191
  reactiveMap,
7192
+ reactiveSink,
6769
7193
  reduce,
6770
7194
  reduction_exports as reduction,
7195
+ registerBuiltinCodecs,
7196
+ registerBuiltins,
6771
7197
  repeat,
6772
7198
  replay,
6773
7199
  replayWAL,
6774
7200
  rescue,
6775
7201
  resolveBackoffPreset,
6776
7202
  resolveDescribeFields,
6777
- restoreGraphCheckpoint,
6778
- restoreGraphCheckpointIndexedDb,
6779
7203
  retry,
7204
+ retrySource,
6780
7205
  sample,
6781
- saveGraphCheckpoint,
6782
- saveGraphCheckpointIndexedDb,
6783
7206
  scan,
6784
7207
  serializeError,
6785
7208
  share,
@@ -6789,6 +7212,7 @@ export {
6789
7212
  sizeof,
6790
7213
  skip,
6791
7214
  solid_exports as solid,
7215
+ sqliteStorage,
6792
7216
  state,
6793
7217
  svelte_exports as svelte,
6794
7218
  switchMap,
@@ -6799,12 +7223,12 @@ export {
6799
7223
  throttle,
6800
7224
  throttleTime,
6801
7225
  throwError,
6802
- tieredStorage,
6803
7226
  timeout,
6804
7227
  toArray,
6805
7228
  toCSV,
6806
7229
  toClickHouse,
6807
7230
  toFile,
7231
+ toHTTP,
6808
7232
  toKafka,
6809
7233
  toLoki,
6810
7234
  toMongo,
@@ -6813,14 +7237,15 @@ export {
6813
7237
  toPostgres,
6814
7238
  toPulsar,
6815
7239
  toRabbitMQ,
7240
+ toReadableStream,
6816
7241
  toRedisStream,
6817
7242
  toS3,
6818
7243
  toSSE,
7244
+ toSSEBytes,
6819
7245
  toSqlite,
6820
7246
  toTempo,
6821
7247
  toWebSocket,
6822
7248
  tokenBucket,
6823
- tokenTracker,
6824
7249
  valve,
6825
7250
  verifiable,
6826
7251
  version,