@graphrefly/graphrefly 0.26.0 → 0.27.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.
- package/dist/ai-CaR_912Q.d.cts +1033 -0
- package/dist/ai-WlRltJV7.d.ts +1033 -0
- package/dist/audit-ClmqGOCx.d.cts +245 -0
- package/dist/audit-DRlSzBu9.d.ts +245 -0
- package/dist/{chunk-JYXEWPH4.js → chunk-APFNLIRG.js} +2 -2
- package/dist/chunk-AT5LKYNL.js +395 -0
- package/dist/chunk-AT5LKYNL.js.map +1 -0
- package/dist/{chunk-AMCG74RZ.js → chunk-BQ6RQQFF.js} +215 -2128
- package/dist/chunk-BQ6RQQFF.js.map +1 -0
- package/dist/{chunk-IZYUSJC7.js → chunk-DST5DKZS.js} +6 -4
- package/dist/{chunk-IZYUSJC7.js.map → chunk-DST5DKZS.js.map} +1 -1
- package/dist/{chunk-LCE3GF5P.js → chunk-GTE6PWRZ.js} +2 -2
- package/dist/{chunk-RB6QPHJ7.js → chunk-J2VBW3DZ.js} +2 -93
- package/dist/chunk-J2VBW3DZ.js.map +1 -0
- package/dist/{chunk-FQMKGR6L.js → chunk-JWBCY4NC.js} +3 -3
- package/dist/chunk-K2AUJHVP.js +2251 -0
- package/dist/chunk-K2AUJHVP.js.map +1 -0
- package/dist/chunk-NC6S43JJ.js +456 -0
- package/dist/chunk-NC6S43JJ.js.map +1 -0
- package/dist/chunk-OFVJBJXR.js +98 -0
- package/dist/chunk-OFVJBJXR.js.map +1 -0
- package/dist/{chunk-6LDQFTYS.js → chunk-OU5CQKNW.js} +2 -2
- package/dist/{chunk-THTWHNU4.js → chunk-PF7GRZMW.js} +5 -5
- package/dist/chunk-RNHBMHKA.js +1665 -0
- package/dist/chunk-RNHBMHKA.js.map +1 -0
- package/dist/{chunk-SN4YWWYO.js → chunk-WBZOVTYK.js} +11 -11
- package/dist/chunk-WKNUIZOY.js +354 -0
- package/dist/chunk-WKNUIZOY.js.map +1 -0
- package/dist/{chunk-ZQMEI34O.js → chunk-X3VMZYBT.js} +3 -3
- package/dist/chunk-X5R3GL6H.js +525 -0
- package/dist/chunk-X5R3GL6H.js.map +1 -0
- package/dist/compat/index.d.cts +14 -14
- package/dist/compat/index.d.ts +14 -14
- package/dist/compat/index.js +17 -16
- package/dist/compat/jotai/index.d.cts +2 -2
- package/dist/compat/jotai/index.d.ts +2 -2
- package/dist/compat/nanostores/index.d.cts +2 -2
- package/dist/compat/nanostores/index.d.ts +2 -2
- package/dist/compat/nestjs/index.d.cts +6 -6
- package/dist/compat/nestjs/index.d.ts +6 -6
- package/dist/compat/nestjs/index.js +7 -6
- package/dist/compat/react/index.d.cts +2 -2
- package/dist/compat/react/index.d.ts +2 -2
- package/dist/compat/solid/index.d.cts +2 -2
- package/dist/compat/solid/index.d.ts +2 -2
- package/dist/compat/svelte/index.d.cts +2 -2
- package/dist/compat/svelte/index.d.ts +2 -2
- package/dist/compat/vue/index.d.cts +2 -2
- package/dist/compat/vue/index.d.ts +2 -2
- package/dist/compat/zustand/index.d.cts +5 -5
- package/dist/compat/zustand/index.d.ts +5 -5
- package/dist/compat/zustand/index.js +3 -3
- package/dist/composite-C7PcQvcs.d.cts +303 -0
- package/dist/composite-aUCvjZVR.d.ts +303 -0
- package/dist/core/index.d.cts +4 -3
- package/dist/core/index.d.ts +4 -3
- package/dist/{demo-shell-DEp-nMTl.d.ts → demo-shell-BDkOptd6.d.ts} +2 -2
- package/dist/{demo-shell-26p5fVxn.d.cts → demo-shell-Crid1WdR.d.cts} +2 -2
- package/dist/extra/index.d.cts +5 -4
- package/dist/extra/index.d.ts +5 -4
- package/dist/extra/index.js +68 -62
- package/dist/extra/sources.d.cts +1 -1
- package/dist/extra/sources.d.ts +1 -1
- package/dist/graph/index.d.cts +5 -5
- package/dist/graph/index.d.ts +5 -5
- package/dist/graph/index.js +3 -3
- package/dist/{graph-DQ69XU0g.d.ts → graph-CCwGKLCm.d.ts} +4 -4
- package/dist/{graph-6tZ5jEzr.d.cts → graph-DNCrvZSn.d.cts} +4 -4
- package/dist/{index-qldRdbQw.d.ts → index-3lsddbbS.d.ts} +1 -1
- package/dist/{index-Bxb5ZYc9.d.cts → index-B1tloyhO.d.cts} +1 -1
- package/dist/{index-eJ6T_qGM.d.ts → index-B6D3QNSA.d.ts} +2 -2
- package/dist/{index-B4MP_8V_.d.cts → index-B6EhDnjH.d.cts} +1 -1
- package/dist/{index-CmnuOibw.d.ts → index-B9B7_HEY.d.ts} +1 -1
- package/dist/{index-BeIdBfcb.d.cts → index-BHlKbUwO.d.cts} +16 -315
- package/dist/{index-CuYwdKO-.d.ts → index-BPVt8kqc.d.ts} +3 -3
- package/dist/index-BaSM3aYt.d.ts +195 -0
- package/dist/{index-BjI6ty9z.d.ts → index-BuEoe-Qu.d.ts} +9 -9
- package/dist/{index-DdD5MVDL.d.ts → index-BwfLUNw4.d.ts} +16 -315
- package/dist/{index-QBpffFW-.d.cts → index-ByQxazQJ.d.cts} +1 -1
- package/dist/{index-xdGjv0nO.d.ts → index-C0svESO4.d.ts} +1 -1
- package/dist/{index-BW1z3BN9.d.ts → index-C8oil6M6.d.ts} +3 -3
- package/dist/{index-C8mdwMXc.d.cts → index-CI3DprxP.d.cts} +3 -3
- package/dist/{index-CUwyr1Kk.d.cts → index-CO8uBlUh.d.cts} +2 -2
- package/dist/{index-DrISNAOm.d.ts → index-CxFrXH4m.d.ts} +1 -1
- package/dist/{index-BEfE8H_G.d.cts → index-D8wS_PeY.d.cts} +9 -9
- package/dist/{index-CUyrtuOf.d.cts → index-DO_6JN9Z.d.cts} +1 -1
- package/dist/index-DVGiGFGT.d.cts +195 -0
- package/dist/{index-DFhjO4Gg.d.cts → index-DYme44FM.d.cts} +1 -1
- package/dist/{index-_oMEWlDq.d.cts → index-DlLp-2Xn.d.cts} +3 -3
- package/dist/{index-CPgZ5wRl.d.ts → index-Dzk2hrlR.d.ts} +1 -1
- package/dist/{index-Bd_fwmLf.d.cts → index-VHqptjhu.d.cts} +1 -1
- package/dist/{index-CDAjUFIv.d.ts → index-VdHQMPy1.d.ts} +1 -1
- package/dist/{index-B_IP40nB.d.cts → index-Xi3u0HCQ.d.cts} +1 -1
- package/dist/{index-BYOHF0zP.d.ts → index-wEn0eFe8.d.ts} +1 -1
- package/dist/index.d.cts +35 -1692
- package/dist/index.d.ts +35 -1692
- package/dist/index.js +307 -3731
- package/dist/index.js.map +1 -1
- package/dist/memory-C6Z2tGpC.d.cts +139 -0
- package/dist/memory-li6FL5RM.d.ts +139 -0
- package/dist/messaging-Gt4LPbyA.d.cts +269 -0
- package/dist/messaging-XDoYablx.d.ts +269 -0
- package/dist/{meta-C0-8XW6Q.d.cts → meta-BxCA7rcr.d.cts} +1 -1
- package/dist/{meta-BGqSZ7mt.d.ts → meta-CbznRPYJ.d.ts} +1 -1
- package/dist/{node-C_IBuvX2.d.cts → node-BmerH3kS.d.cts} +1 -1
- package/dist/{node-C_IBuvX2.d.ts → node-BmerH3kS.d.ts} +1 -1
- package/dist/{observable-DCk45RH5.d.ts → observable-BgGUwcqp.d.ts} +1 -1
- package/dist/{observable-Crr1jgzx.d.cts → observable-DJt_AxzQ.d.cts} +1 -1
- package/dist/patterns/ai.cjs +7930 -0
- package/dist/patterns/ai.cjs.map +1 -0
- package/dist/patterns/ai.d.cts +10 -0
- package/dist/patterns/ai.d.ts +10 -0
- package/dist/patterns/ai.js +71 -0
- package/dist/patterns/ai.js.map +1 -0
- package/dist/patterns/audit.cjs +5805 -0
- package/dist/patterns/audit.cjs.map +1 -0
- package/dist/patterns/audit.d.cts +6 -0
- package/dist/patterns/audit.d.ts +6 -0
- package/dist/patterns/audit.js +29 -0
- package/dist/patterns/audit.js.map +1 -0
- package/dist/patterns/demo-shell.d.cts +6 -6
- package/dist/patterns/demo-shell.d.ts +6 -6
- package/dist/patterns/demo-shell.js +4 -4
- package/dist/patterns/memory.cjs +5283 -0
- package/dist/patterns/memory.cjs.map +1 -0
- package/dist/patterns/memory.d.cts +5 -0
- package/dist/patterns/memory.d.ts +5 -0
- package/dist/patterns/memory.js +20 -0
- package/dist/patterns/memory.js.map +1 -0
- package/dist/patterns/reactive-layout/index.d.cts +6 -6
- package/dist/patterns/reactive-layout/index.d.ts +6 -6
- package/dist/patterns/reactive-layout/index.js +4 -4
- package/dist/{reactive-layout-BaOQefHu.d.cts → reactive-layout-MQP--J3F.d.cts} +2 -2
- package/dist/{reactive-layout-D9gejYXE.d.ts → reactive-layout-u5Ulnqag.d.ts} +2 -2
- package/dist/{storage-BMycWEh2.d.ts → storage-CMjUUuxn.d.ts} +2 -2
- package/dist/{storage-DiqWHzVI.d.cts → storage-DdWlZo6U.d.cts} +2 -2
- package/dist/sugar-CCOxXK1e.d.ts +201 -0
- package/dist/sugar-D02n5JjF.d.cts +201 -0
- package/package.json +32 -2
- package/dist/chunk-AMCG74RZ.js.map +0 -1
- package/dist/chunk-RB6QPHJ7.js.map +0 -1
- package/dist/index-C0ZXMaXO.d.cts +0 -392
- package/dist/index-CY2TljO4.d.ts +0 -392
- /package/dist/{chunk-JYXEWPH4.js.map → chunk-APFNLIRG.js.map} +0 -0
- /package/dist/{chunk-LCE3GF5P.js.map → chunk-GTE6PWRZ.js.map} +0 -0
- /package/dist/{chunk-FQMKGR6L.js.map → chunk-JWBCY4NC.js.map} +0 -0
- /package/dist/{chunk-6LDQFTYS.js.map → chunk-OU5CQKNW.js.map} +0 -0
- /package/dist/{chunk-THTWHNU4.js.map → chunk-PF7GRZMW.js.map} +0 -0
- /package/dist/{chunk-SN4YWWYO.js.map → chunk-WBZOVTYK.js.map} +0 -0
- /package/dist/{chunk-ZQMEI34O.js.map → chunk-X3VMZYBT.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import {
|
|
2
2
|
reactive_layout_exports
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-X3VMZYBT.js";
|
|
4
|
+
import {
|
|
5
|
+
demo_shell_exports
|
|
6
|
+
} from "./chunk-JWBCY4NC.js";
|
|
7
|
+
import "./chunk-GTE6PWRZ.js";
|
|
4
8
|
import {
|
|
5
9
|
compat_exports,
|
|
6
10
|
signals_exports
|
|
7
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-WBZOVTYK.js";
|
|
12
|
+
import {
|
|
13
|
+
vue_exports
|
|
14
|
+
} from "./chunk-MJ2NKQQL.js";
|
|
15
|
+
import {
|
|
16
|
+
zustand_exports
|
|
17
|
+
} from "./chunk-APFNLIRG.js";
|
|
18
|
+
import {
|
|
19
|
+
jotai_exports
|
|
20
|
+
} from "./chunk-XGPU467M.js";
|
|
8
21
|
import {
|
|
9
22
|
nanostores_exports
|
|
10
23
|
} from "./chunk-N6UR7YVY.js";
|
|
11
24
|
import {
|
|
12
25
|
cqrs_exports,
|
|
13
26
|
nestjs_exports
|
|
14
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-DST5DKZS.js";
|
|
15
28
|
import {
|
|
16
29
|
react_exports
|
|
17
30
|
} from "./chunk-J22W6HV3.js";
|
|
@@ -21,59 +34,25 @@ import {
|
|
|
21
34
|
import {
|
|
22
35
|
svelte_exports
|
|
23
36
|
} from "./chunk-OHISZPOJ.js";
|
|
24
|
-
import {
|
|
25
|
-
vue_exports
|
|
26
|
-
} from "./chunk-MJ2NKQQL.js";
|
|
27
|
-
import {
|
|
28
|
-
zustand_exports
|
|
29
|
-
} from "./chunk-JYXEWPH4.js";
|
|
30
37
|
import {
|
|
31
38
|
CircuitOpenError,
|
|
32
|
-
NS_PER_MS,
|
|
33
|
-
NS_PER_SEC,
|
|
34
39
|
NativeIndexBackend,
|
|
35
|
-
NativeListBackend,
|
|
36
|
-
NativeMapBackend,
|
|
37
40
|
NativePubSubBackend,
|
|
38
41
|
RateLimiterOverflowError,
|
|
39
42
|
TimeoutError,
|
|
40
|
-
audit,
|
|
41
|
-
buffer,
|
|
42
|
-
bufferCount,
|
|
43
|
-
bufferTime,
|
|
44
43
|
cascadingCache,
|
|
45
|
-
catchError,
|
|
46
44
|
checkpointToRedis,
|
|
47
45
|
checkpointToS3,
|
|
48
46
|
circuitBreaker,
|
|
49
|
-
combine,
|
|
50
|
-
combineLatest,
|
|
51
|
-
concat,
|
|
52
|
-
concatMap,
|
|
53
|
-
constant,
|
|
54
47
|
createTransport,
|
|
55
48
|
csvRows,
|
|
56
|
-
debounce,
|
|
57
|
-
debounceTime,
|
|
58
|
-
decorrelatedJitter,
|
|
59
|
-
delay,
|
|
60
49
|
deserializeError,
|
|
61
50
|
dictStorage,
|
|
62
|
-
distill,
|
|
63
|
-
distinctUntilChanged,
|
|
64
|
-
elementAt,
|
|
65
|
-
exhaustMap,
|
|
66
|
-
exponential,
|
|
67
51
|
externalBundle,
|
|
68
52
|
externalProducer,
|
|
69
53
|
extra_exports,
|
|
70
54
|
fallback,
|
|
71
|
-
fibonacci,
|
|
72
55
|
fileStorage,
|
|
73
|
-
filter,
|
|
74
|
-
find,
|
|
75
|
-
first,
|
|
76
|
-
flatMap,
|
|
77
56
|
fromCSV,
|
|
78
57
|
fromClickHouseWatch,
|
|
79
58
|
fromDrizzle,
|
|
@@ -104,47 +83,22 @@ import {
|
|
|
104
83
|
fromWebSocketReconnect,
|
|
105
84
|
fromWebhook,
|
|
106
85
|
indexedDbStorage,
|
|
107
|
-
interval,
|
|
108
|
-
last,
|
|
109
|
-
linear,
|
|
110
86
|
lru,
|
|
111
|
-
map,
|
|
112
87
|
memoryStorage,
|
|
113
|
-
merge,
|
|
114
|
-
mergeMap,
|
|
115
88
|
nameToSignal,
|
|
116
89
|
ndjsonRows,
|
|
117
|
-
pairwise,
|
|
118
90
|
parsePrometheusText,
|
|
119
91
|
parseStatsD,
|
|
120
92
|
parseSyslog,
|
|
121
|
-
pausable,
|
|
122
93
|
pubsub,
|
|
123
|
-
race,
|
|
124
94
|
rateLimiter,
|
|
125
95
|
reactiveIndex,
|
|
126
|
-
reactiveList,
|
|
127
|
-
reactiveMap,
|
|
128
96
|
reactiveSink,
|
|
129
|
-
reduce,
|
|
130
|
-
repeat,
|
|
131
|
-
rescue,
|
|
132
|
-
resolveBackoffPreset,
|
|
133
97
|
retry,
|
|
134
98
|
retrySource,
|
|
135
|
-
sample,
|
|
136
|
-
scan,
|
|
137
99
|
serializeError,
|
|
138
100
|
signalToName,
|
|
139
|
-
skip,
|
|
140
101
|
sqliteStorage,
|
|
141
|
-
switchMap,
|
|
142
|
-
take,
|
|
143
|
-
takeUntil,
|
|
144
|
-
takeWhile,
|
|
145
|
-
tap,
|
|
146
|
-
throttle,
|
|
147
|
-
throttleTime,
|
|
148
102
|
timeout,
|
|
149
103
|
toCSV,
|
|
150
104
|
toClickHouse,
|
|
@@ -166,3633 +120,255 @@ import {
|
|
|
166
120
|
toTempo,
|
|
167
121
|
toWebSocket,
|
|
168
122
|
tokenBucket,
|
|
123
|
+
withBreaker,
|
|
124
|
+
withStatus,
|
|
125
|
+
workerBridge,
|
|
126
|
+
workerSelf
|
|
127
|
+
} from "./chunk-BQ6RQQFF.js";
|
|
128
|
+
import {
|
|
129
|
+
createWatermarkController,
|
|
130
|
+
toObservable
|
|
131
|
+
} from "./chunk-OFVJBJXR.js";
|
|
132
|
+
import {
|
|
133
|
+
ai_exports,
|
|
134
|
+
gate,
|
|
135
|
+
orchestration_exports,
|
|
136
|
+
promptNode
|
|
137
|
+
} from "./chunk-K2AUJHVP.js";
|
|
138
|
+
import {
|
|
139
|
+
decay,
|
|
140
|
+
memory_exports
|
|
141
|
+
} from "./chunk-AT5LKYNL.js";
|
|
142
|
+
import {
|
|
143
|
+
NS_PER_MS,
|
|
144
|
+
NS_PER_SEC,
|
|
145
|
+
audit,
|
|
146
|
+
buffer,
|
|
147
|
+
bufferCount,
|
|
148
|
+
bufferTime,
|
|
149
|
+
catchError,
|
|
150
|
+
combine,
|
|
151
|
+
combineLatest,
|
|
152
|
+
concat,
|
|
153
|
+
concatMap,
|
|
154
|
+
constant,
|
|
155
|
+
debounce,
|
|
156
|
+
debounceTime,
|
|
157
|
+
decorrelatedJitter,
|
|
158
|
+
delay,
|
|
159
|
+
distill,
|
|
160
|
+
distinctUntilChanged,
|
|
161
|
+
elementAt,
|
|
162
|
+
exhaustMap,
|
|
163
|
+
exponential,
|
|
164
|
+
fibonacci,
|
|
165
|
+
filter,
|
|
166
|
+
find,
|
|
167
|
+
first,
|
|
168
|
+
flatMap,
|
|
169
|
+
interval,
|
|
170
|
+
last,
|
|
171
|
+
linear,
|
|
172
|
+
map,
|
|
173
|
+
merge,
|
|
174
|
+
mergeMap,
|
|
175
|
+
pairwise,
|
|
176
|
+
pausable,
|
|
177
|
+
race,
|
|
178
|
+
reduce,
|
|
179
|
+
repeat,
|
|
180
|
+
rescue,
|
|
181
|
+
resolveBackoffPreset,
|
|
182
|
+
sample,
|
|
183
|
+
scan,
|
|
184
|
+
skip,
|
|
185
|
+
switchMap,
|
|
186
|
+
take,
|
|
187
|
+
takeUntil,
|
|
188
|
+
takeWhile,
|
|
189
|
+
tap,
|
|
190
|
+
throttle,
|
|
191
|
+
throttleTime,
|
|
169
192
|
valve,
|
|
170
193
|
verifiable,
|
|
171
194
|
window,
|
|
172
195
|
windowCount,
|
|
173
196
|
windowTime,
|
|
174
|
-
withBreaker,
|
|
175
197
|
withLatestFrom,
|
|
176
198
|
withMaxAttempts,
|
|
177
|
-
withStatus,
|
|
178
|
-
workerBridge,
|
|
179
|
-
workerSelf,
|
|
180
199
|
zip
|
|
181
|
-
} from "./chunk-
|
|
182
|
-
import {
|
|
183
|
-
NativeLogBackend,
|
|
184
|
-
createWatermarkController,
|
|
185
|
-
reactiveLog,
|
|
186
|
-
toObservable
|
|
187
|
-
} from "./chunk-RB6QPHJ7.js";
|
|
188
|
-
import {
|
|
189
|
-
core_exports
|
|
190
|
-
} from "./chunk-3ZWCKRHX.js";
|
|
200
|
+
} from "./chunk-RNHBMHKA.js";
|
|
191
201
|
import {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
202
|
+
audit_exports,
|
|
203
|
+
policyEnforcer,
|
|
204
|
+
reactiveExplainPath
|
|
205
|
+
} from "./chunk-NC6S43JJ.js";
|
|
195
206
|
import {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
207
|
+
TopicGraph,
|
|
208
|
+
messaging_exports
|
|
209
|
+
} from "./chunk-X5R3GL6H.js";
|
|
199
210
|
import {
|
|
200
211
|
domainMeta,
|
|
201
212
|
trackingKey,
|
|
202
213
|
tryIncrementBounded
|
|
203
214
|
} from "./chunk-JSCT3CR4.js";
|
|
204
215
|
import {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
escapeRegexChar,
|
|
208
|
-
firstValueFrom,
|
|
209
|
-
firstWhere,
|
|
210
|
-
forEach,
|
|
211
|
-
fromAny,
|
|
212
|
-
fromAsyncIter,
|
|
213
|
-
fromCron,
|
|
214
|
-
fromEvent,
|
|
215
|
-
fromIter,
|
|
216
|
-
fromPromise,
|
|
217
|
-
fromRaf,
|
|
218
|
-
fromTimer,
|
|
219
|
-
globToRegExp,
|
|
220
|
-
keepalive,
|
|
221
|
-
matchesAnyPattern,
|
|
222
|
-
matchesCron,
|
|
223
|
-
never,
|
|
224
|
-
of,
|
|
225
|
-
parseCron,
|
|
226
|
-
reactiveCounter,
|
|
227
|
-
replay,
|
|
228
|
-
share,
|
|
229
|
-
shareReplay,
|
|
230
|
-
throwError,
|
|
231
|
-
toArray
|
|
232
|
-
} from "./chunk-BVZYTZ5H.js";
|
|
233
|
-
import {
|
|
234
|
-
GRAPH_META_SEGMENT,
|
|
235
|
-
Graph,
|
|
236
|
-
OVERHEAD,
|
|
237
|
-
SIZEOF_SYMBOL,
|
|
238
|
-
SNAPSHOT_VERSION,
|
|
239
|
-
diffForWAL,
|
|
240
|
-
explainPath,
|
|
241
|
-
graphProfile,
|
|
242
|
-
reachable,
|
|
243
|
-
sizeof
|
|
244
|
-
} from "./chunk-THTWHNU4.js";
|
|
245
|
-
import {
|
|
246
|
-
ResettableTimer
|
|
247
|
-
} from "./chunk-7TAQJHQV.js";
|
|
248
|
-
import {
|
|
249
|
-
resolveDescribeFields
|
|
250
|
-
} from "./chunk-VYPWMZ6H.js";
|
|
216
|
+
core_exports
|
|
217
|
+
} from "./chunk-3ZWCKRHX.js";
|
|
251
218
|
import {
|
|
252
|
-
|
|
253
|
-
|
|
219
|
+
NativeListBackend,
|
|
220
|
+
NativeMapBackend,
|
|
221
|
+
reactiveList,
|
|
222
|
+
reactiveMap
|
|
223
|
+
} from "./chunk-WKNUIZOY.js";
|
|
254
224
|
import {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
GuardDenied,
|
|
259
|
-
JsonCodec,
|
|
260
|
-
NodeImpl,
|
|
261
|
-
accessHintForGuard,
|
|
262
|
-
advanceVersion,
|
|
263
|
-
autoTrackNode,
|
|
264
|
-
batch,
|
|
265
|
-
configure,
|
|
266
|
-
createDagCborCodec,
|
|
267
|
-
createDagCborZstdCodec,
|
|
268
|
-
createVersioning,
|
|
269
|
-
decodeEnvelope,
|
|
270
|
-
defaultConfig,
|
|
271
|
-
defaultHash,
|
|
272
|
-
derived,
|
|
273
|
-
downWithBatch,
|
|
274
|
-
dynamicNode,
|
|
275
|
-
effect,
|
|
276
|
-
encodeEnvelope,
|
|
277
|
-
isBatching,
|
|
278
|
-
isV1,
|
|
279
|
-
monotonicNs,
|
|
280
|
-
node,
|
|
281
|
-
normalizeActor,
|
|
282
|
-
pipe,
|
|
283
|
-
policy,
|
|
284
|
-
policyFromRules,
|
|
285
|
-
producer,
|
|
286
|
-
registerBuiltinCodecs,
|
|
287
|
-
registerBuiltins,
|
|
288
|
-
replayWAL,
|
|
289
|
-
state,
|
|
290
|
-
wallClockNs
|
|
291
|
-
} from "./chunk-PHOUUNK7.js";
|
|
225
|
+
NativeLogBackend,
|
|
226
|
+
reactiveLog
|
|
227
|
+
} from "./chunk-J2VBW3DZ.js";
|
|
292
228
|
import {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
function readArray(node2) {
|
|
407
|
-
return node2.cache ?? [];
|
|
408
|
-
}
|
|
409
|
-
function cosineSimilarity(a, b) {
|
|
410
|
-
const n = Math.max(a.length, b.length);
|
|
411
|
-
let dot = 0;
|
|
412
|
-
let na = 0;
|
|
413
|
-
let nb = 0;
|
|
414
|
-
for (let i = 0; i < n; i += 1) {
|
|
415
|
-
const av = a[i] ?? 0;
|
|
416
|
-
const bv = b[i] ?? 0;
|
|
417
|
-
dot += av * bv;
|
|
418
|
-
na += av * av;
|
|
419
|
-
nb += bv * bv;
|
|
420
|
-
}
|
|
421
|
-
if (na === 0 || nb === 0) return 0;
|
|
422
|
-
return dot / Math.sqrt(na * nb);
|
|
423
|
-
}
|
|
424
|
-
function lightCollection(opts = {}) {
|
|
425
|
-
const maxSize = opts.maxSize;
|
|
426
|
-
const policy2 = opts.policy ?? "fifo";
|
|
427
|
-
assertMaxSize(maxSize);
|
|
428
|
-
const entries = state(/* @__PURE__ */ new Map(), {
|
|
429
|
-
name: opts.name,
|
|
430
|
-
describeKind: "state"
|
|
431
|
-
});
|
|
432
|
-
function evictIfNeeded(next) {
|
|
433
|
-
if (maxSize === void 0) return;
|
|
434
|
-
while (next.size > maxSize) {
|
|
435
|
-
let victim;
|
|
436
|
-
for (const entry of next.values()) {
|
|
437
|
-
if (!victim) {
|
|
438
|
-
victim = entry;
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
const lhs = policy2 === "lru" ? entry.lastAccessNs : entry.createdAtNs;
|
|
442
|
-
const rhs = policy2 === "lru" ? victim.lastAccessNs : victim.createdAtNs;
|
|
443
|
-
if (lhs < rhs) victim = entry;
|
|
444
|
-
}
|
|
445
|
-
if (!victim) break;
|
|
446
|
-
next.delete(victim.id);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
function commit(next) {
|
|
450
|
-
entries.down([[DATA, next]]);
|
|
451
|
-
}
|
|
452
|
-
return {
|
|
453
|
-
entries,
|
|
454
|
-
upsert(id, value) {
|
|
455
|
-
const now = monotonicNs();
|
|
456
|
-
const current = readMap(entries);
|
|
457
|
-
const prev = current.get(id);
|
|
458
|
-
const next = copyMap(current);
|
|
459
|
-
next.set(id, {
|
|
460
|
-
id,
|
|
461
|
-
value,
|
|
462
|
-
createdAtNs: prev?.createdAtNs ?? now,
|
|
463
|
-
lastAccessNs: now
|
|
464
|
-
});
|
|
465
|
-
evictIfNeeded(next);
|
|
466
|
-
commit(next);
|
|
467
|
-
},
|
|
468
|
-
remove(id) {
|
|
469
|
-
const next = copyMap(readMap(entries));
|
|
470
|
-
if (!next.delete(id)) return;
|
|
471
|
-
commit(next);
|
|
472
|
-
},
|
|
473
|
-
clear() {
|
|
474
|
-
if (readMap(entries).size === 0) return;
|
|
475
|
-
commit(/* @__PURE__ */ new Map());
|
|
476
|
-
},
|
|
477
|
-
get(id) {
|
|
478
|
-
const current = readMap(entries);
|
|
479
|
-
const found = current.get(id);
|
|
480
|
-
if (!found) return void 0;
|
|
481
|
-
if (policy2 === "lru") {
|
|
482
|
-
const now = monotonicNs();
|
|
483
|
-
const next = copyMap(current);
|
|
484
|
-
next.set(id, { ...found, lastAccessNs: now });
|
|
485
|
-
commit(next);
|
|
486
|
-
}
|
|
487
|
-
return found.value;
|
|
488
|
-
},
|
|
489
|
-
has(id) {
|
|
490
|
-
return readMap(entries).has(id);
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
function collection(name, opts = {}) {
|
|
495
|
-
const maxSize = opts.maxSize;
|
|
496
|
-
const policy2 = opts.policy ?? "lru";
|
|
497
|
-
const decayRate = opts.decayRate ?? 0;
|
|
498
|
-
const minScore = opts.minScore ?? 0;
|
|
499
|
-
const scoreFn = opts.score ?? (() => 1);
|
|
500
|
-
assertMaxSize(maxSize);
|
|
501
|
-
const graph = new Graph(name);
|
|
502
|
-
const items = state(/* @__PURE__ */ new Map(), {
|
|
503
|
-
name: "items",
|
|
504
|
-
describeKind: "state"
|
|
505
|
-
});
|
|
506
|
-
const ranked = derived(
|
|
507
|
-
[items],
|
|
508
|
-
([snapshot]) => {
|
|
509
|
-
const typed = snapshot ?? /* @__PURE__ */ new Map();
|
|
510
|
-
const now = monotonicNs();
|
|
511
|
-
const out2 = [...typed.values()].map((entry) => {
|
|
512
|
-
const ageSeconds = (now - entry.lastAccessNs) / 1e9;
|
|
513
|
-
return {
|
|
514
|
-
...entry,
|
|
515
|
-
score: decay(entry.baseScore, ageSeconds, decayRate, minScore)
|
|
516
|
-
};
|
|
517
|
-
});
|
|
518
|
-
out2.sort((a, b) => b.score - a.score || b.lastAccessNs - a.lastAccessNs);
|
|
519
|
-
return out2;
|
|
520
|
-
},
|
|
521
|
-
{ name: "ranked", describeKind: "derived" }
|
|
522
|
-
);
|
|
523
|
-
const size = derived(
|
|
524
|
-
[items],
|
|
525
|
-
([snapshot]) => (snapshot ?? /* @__PURE__ */ new Map()).size,
|
|
526
|
-
{
|
|
527
|
-
name: "size",
|
|
528
|
-
describeKind: "derived",
|
|
529
|
-
initial: 0
|
|
530
|
-
}
|
|
531
|
-
);
|
|
532
|
-
void ranked.subscribe(() => void 0);
|
|
533
|
-
void size.subscribe(() => void 0);
|
|
534
|
-
graph.add("items", items);
|
|
535
|
-
graph.add("ranked", ranked);
|
|
536
|
-
graph.add("size", size);
|
|
537
|
-
function effective(entry, now) {
|
|
538
|
-
const ageSeconds = (now - entry.lastAccessNs) / 1e9;
|
|
539
|
-
return decay(entry.baseScore, ageSeconds, decayRate, minScore);
|
|
540
|
-
}
|
|
541
|
-
function evictIfNeeded(next) {
|
|
542
|
-
if (maxSize === void 0) return;
|
|
543
|
-
while (next.size > maxSize) {
|
|
544
|
-
const now = monotonicNs();
|
|
545
|
-
let victim;
|
|
546
|
-
let victimScore = Number.POSITIVE_INFINITY;
|
|
547
|
-
for (const entry of next.values()) {
|
|
548
|
-
const score = effective(entry, now);
|
|
549
|
-
if (score < victimScore) {
|
|
550
|
-
victim = entry;
|
|
551
|
-
victimScore = score;
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
if (score === victimScore && victim) {
|
|
555
|
-
const lhs = policy2 === "lru" ? entry.lastAccessNs : entry.createdAtNs;
|
|
556
|
-
const rhs = policy2 === "lru" ? victim.lastAccessNs : victim.createdAtNs;
|
|
557
|
-
if (lhs < rhs) victim = entry;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
if (!victim) break;
|
|
561
|
-
next.delete(victim.id);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
function commit(next) {
|
|
565
|
-
items.down([[DATA, next]]);
|
|
566
|
-
}
|
|
567
|
-
const out = Object.assign(graph, {
|
|
568
|
-
upsert(id, value, upsertOpts) {
|
|
569
|
-
const now = monotonicNs();
|
|
570
|
-
const current = readMap(items);
|
|
571
|
-
const prev = current.get(id);
|
|
572
|
-
const baseScore = upsertOpts?.score ?? scoreFn(value);
|
|
573
|
-
const next = copyMap(current);
|
|
574
|
-
next.set(id, {
|
|
575
|
-
id,
|
|
576
|
-
value,
|
|
577
|
-
baseScore,
|
|
578
|
-
createdAtNs: prev?.createdAtNs ?? now,
|
|
579
|
-
lastAccessNs: now
|
|
580
|
-
});
|
|
581
|
-
evictIfNeeded(next);
|
|
582
|
-
commit(next);
|
|
583
|
-
},
|
|
584
|
-
remove(id) {
|
|
585
|
-
const next = copyMap(readMap(items));
|
|
586
|
-
if (!next.delete(id)) return;
|
|
587
|
-
commit(next);
|
|
588
|
-
},
|
|
589
|
-
clear() {
|
|
590
|
-
if (readMap(items).size === 0) return;
|
|
591
|
-
commit(/* @__PURE__ */ new Map());
|
|
592
|
-
},
|
|
593
|
-
getItem(id) {
|
|
594
|
-
const current = readMap(items);
|
|
595
|
-
const found = current.get(id);
|
|
596
|
-
if (!found) return void 0;
|
|
597
|
-
if (policy2 === "lru") {
|
|
598
|
-
const next = copyMap(current);
|
|
599
|
-
next.set(id, { ...found, lastAccessNs: monotonicNs() });
|
|
600
|
-
commit(next);
|
|
601
|
-
}
|
|
602
|
-
return found;
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
return out;
|
|
606
|
-
}
|
|
607
|
-
function vectorIndex(opts = {}) {
|
|
608
|
-
const backend = opts.backend ?? "flat";
|
|
609
|
-
const dimension = opts.dimension;
|
|
610
|
-
let hnsw;
|
|
611
|
-
if (backend === "hnsw") {
|
|
612
|
-
hnsw = opts.hnswFactory?.();
|
|
613
|
-
if (!hnsw) {
|
|
614
|
-
throw new Error(
|
|
615
|
-
'vectorIndex backend "hnsw" requires an optional dependency adapter; install your HNSW package and provide `hnswFactory`.'
|
|
616
|
-
);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
const entries = state(/* @__PURE__ */ new Map(), {
|
|
620
|
-
describeKind: "state",
|
|
621
|
-
name: "vector-index"
|
|
622
|
-
});
|
|
623
|
-
function assertDimension(vector) {
|
|
624
|
-
if (dimension !== void 0 && vector.length !== dimension) {
|
|
625
|
-
throw new RangeError(
|
|
626
|
-
`vector dimension mismatch: expected ${dimension}, got ${vector.length}`
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
function commit(next) {
|
|
631
|
-
entries.down([[DATA, next]]);
|
|
632
|
-
}
|
|
633
|
-
return {
|
|
634
|
-
backend,
|
|
635
|
-
entries,
|
|
636
|
-
upsert(id, vector, meta) {
|
|
637
|
-
assertDimension(vector);
|
|
638
|
-
const next = copyMap(readMap(entries));
|
|
639
|
-
next.set(id, { id, vector: [...vector], meta });
|
|
640
|
-
if (backend === "hnsw") hnsw.upsert(id, vector, meta);
|
|
641
|
-
commit(next);
|
|
642
|
-
},
|
|
643
|
-
remove(id) {
|
|
644
|
-
const next = copyMap(readMap(entries));
|
|
645
|
-
if (!next.delete(id)) return;
|
|
646
|
-
if (backend === "hnsw") hnsw.remove(id);
|
|
647
|
-
commit(next);
|
|
648
|
-
},
|
|
649
|
-
clear() {
|
|
650
|
-
if (readMap(entries).size === 0) return;
|
|
651
|
-
if (backend === "hnsw") hnsw.clear();
|
|
652
|
-
commit(/* @__PURE__ */ new Map());
|
|
653
|
-
},
|
|
654
|
-
search(query, k = 5) {
|
|
655
|
-
assertDimension(query);
|
|
656
|
-
if (k <= 0) return [];
|
|
657
|
-
if (backend === "hnsw") return hnsw.search(query, k);
|
|
658
|
-
const ranked = [...readMap(entries).values()].map((row) => ({
|
|
659
|
-
id: row.id,
|
|
660
|
-
score: cosineSimilarity(query, row.vector),
|
|
661
|
-
meta: row.meta
|
|
662
|
-
})).sort((a, b) => b.score - a.score).slice(0, k);
|
|
663
|
-
return ranked;
|
|
664
|
-
}
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
function knowledgeGraph(name) {
|
|
668
|
-
const graph = new Graph(name);
|
|
669
|
-
const entities = state(/* @__PURE__ */ new Map(), {
|
|
670
|
-
name: "entities",
|
|
671
|
-
describeKind: "state"
|
|
672
|
-
});
|
|
673
|
-
const edges = state([], {
|
|
674
|
-
name: "edges",
|
|
675
|
-
describeKind: "state"
|
|
676
|
-
});
|
|
677
|
-
const adjacency = derived(
|
|
678
|
-
[edges],
|
|
679
|
-
([rows]) => {
|
|
680
|
-
const typed = rows ?? [];
|
|
681
|
-
const out2 = /* @__PURE__ */ new Map();
|
|
682
|
-
for (const edge of typed) {
|
|
683
|
-
const prev = out2.get(edge.from) ?? [];
|
|
684
|
-
out2.set(edge.from, Object.freeze([...prev, edge]));
|
|
685
|
-
}
|
|
686
|
-
return out2;
|
|
687
|
-
},
|
|
688
|
-
{ name: "adjacency", describeKind: "derived", initial: /* @__PURE__ */ new Map() }
|
|
689
|
-
);
|
|
690
|
-
void adjacency.subscribe(() => void 0);
|
|
691
|
-
graph.add("entities", entities);
|
|
692
|
-
graph.add("edges", edges);
|
|
693
|
-
graph.add("adjacency", adjacency);
|
|
694
|
-
function commitEntities(next) {
|
|
695
|
-
entities.down([[DATA, next]]);
|
|
696
|
-
}
|
|
697
|
-
function commitEdges(next) {
|
|
698
|
-
edges.down([[DATA, next]]);
|
|
699
|
-
}
|
|
700
|
-
const out = Object.assign(graph, {
|
|
701
|
-
upsertEntity(id, value) {
|
|
702
|
-
const next = copyMap(readMap(entities));
|
|
703
|
-
next.set(id, value);
|
|
704
|
-
commitEntities(next);
|
|
705
|
-
},
|
|
706
|
-
removeEntity(id) {
|
|
707
|
-
const nextEntities = copyMap(readMap(entities));
|
|
708
|
-
const existed = nextEntities.delete(id);
|
|
709
|
-
const currentEdges = readArray(edges);
|
|
710
|
-
const nextEdges = currentEdges.filter((edge) => edge.from !== id && edge.to !== id);
|
|
711
|
-
if (!existed && nextEdges.length === currentEdges.length) return;
|
|
712
|
-
commitEntities(nextEntities);
|
|
713
|
-
commitEdges(nextEdges);
|
|
714
|
-
},
|
|
715
|
-
link(from, to, relation, weight = 1) {
|
|
716
|
-
const key = `${from}\0${to}\0${relation}`;
|
|
717
|
-
const currentEdges = readArray(edges);
|
|
718
|
-
const existing = new Set(
|
|
719
|
-
currentEdges.map((edge) => `${edge.from}\0${edge.to}\0${edge.relation}`)
|
|
720
|
-
);
|
|
721
|
-
const next = [...currentEdges];
|
|
722
|
-
if (existing.has(key)) {
|
|
723
|
-
for (let i = 0; i < next.length; i += 1) {
|
|
724
|
-
const edge = next[i];
|
|
725
|
-
if (edge.from === from && edge.to === to && edge.relation === relation) {
|
|
726
|
-
next[i] = { ...edge, weight };
|
|
727
|
-
break;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
} else {
|
|
731
|
-
next.push({ from, to, relation, weight });
|
|
732
|
-
}
|
|
733
|
-
commitEdges(next);
|
|
734
|
-
},
|
|
735
|
-
unlink(from, to, relation) {
|
|
736
|
-
const currentEdges = readArray(edges);
|
|
737
|
-
const next = currentEdges.filter(
|
|
738
|
-
(edge) => relation === void 0 ? !(edge.from === from && edge.to === to) : !(edge.from === from && edge.to === to && edge.relation === relation)
|
|
739
|
-
);
|
|
740
|
-
if (next.length === currentEdges.length) return;
|
|
741
|
-
commitEdges(next);
|
|
742
|
-
},
|
|
743
|
-
related(id, relation) {
|
|
744
|
-
return readArray(edges).filter(
|
|
745
|
-
(edge) => (edge.from === id || edge.to === id) && (relation === void 0 || edge.relation === relation)
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
return out;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
// src/patterns/messaging.ts
|
|
753
|
-
var messaging_exports = {};
|
|
754
|
-
__export(messaging_exports, {
|
|
755
|
-
JobFlowGraph: () => JobFlowGraph,
|
|
756
|
-
JobQueueGraph: () => JobQueueGraph,
|
|
757
|
-
MessagingHubGraph: () => MessagingHubGraph,
|
|
758
|
-
SubscriptionGraph: () => SubscriptionGraph,
|
|
759
|
-
TopicBridgeGraph: () => TopicBridgeGraph,
|
|
760
|
-
TopicGraph: () => TopicGraph,
|
|
761
|
-
jobFlow: () => jobFlow,
|
|
762
|
-
jobQueue: () => jobQueue,
|
|
763
|
-
messagingHub: () => messagingHub,
|
|
764
|
-
subscription: () => subscription,
|
|
765
|
-
topic: () => topic,
|
|
766
|
-
topicBridge: () => topicBridge
|
|
767
|
-
});
|
|
768
|
-
var DEFAULT_MAX_PER_PUMP = 2147483647;
|
|
769
|
-
function requireNonNegativeInt(value, label) {
|
|
770
|
-
if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
|
|
771
|
-
throw new Error(`${label} must be a non-negative integer`);
|
|
772
|
-
}
|
|
773
|
-
return value;
|
|
774
|
-
}
|
|
775
|
-
function messagingMeta(kind, extra) {
|
|
776
|
-
return domainMeta("messaging", kind, extra);
|
|
777
|
-
}
|
|
778
|
-
var TopicGraph = class extends Graph {
|
|
779
|
-
_log;
|
|
780
|
-
events;
|
|
781
|
-
/**
|
|
782
|
-
* Most recently published value, or `null` when the topic has no entries
|
|
783
|
-
* yet. Spec §5.12 reserves `undefined` as the protocol-internal "never
|
|
784
|
-
* sent DATA" sentinel — `null` is the idiomatic "empty / no value" signal
|
|
785
|
-
* for domain nodes. F7.
|
|
786
|
-
*
|
|
787
|
-
* **Caveat when `T` itself includes `null`** (e.g., `topic<number | null>`):
|
|
788
|
-
* `latest === null` is ambiguous — it could mean "no publish yet" OR "a
|
|
789
|
-
* `null` value was published". Use {@link hasLatest} to disambiguate, or
|
|
790
|
-
* observe {@link events} directly and track length yourself.
|
|
791
|
-
*/
|
|
792
|
-
latest;
|
|
793
|
-
/**
|
|
794
|
-
* Reactive `true` once the topic has at least one published entry.
|
|
795
|
-
* Disambiguates "`null` never published" from "`null` was published" when
|
|
796
|
-
* `T` includes `null`.
|
|
797
|
-
*/
|
|
798
|
-
hasLatest;
|
|
799
|
-
constructor(name, opts = {}) {
|
|
800
|
-
super(name, opts.graph);
|
|
801
|
-
this._log = reactiveLog([], { name: "events", maxSize: opts.retainedLimit });
|
|
802
|
-
this.events = this._log.entries;
|
|
803
|
-
this.add("events", this.events);
|
|
804
|
-
this.latest = derived(
|
|
805
|
-
[this.events],
|
|
806
|
-
([snapshot]) => {
|
|
807
|
-
const entries = snapshot;
|
|
808
|
-
return entries.length === 0 ? null : entries[entries.length - 1];
|
|
809
|
-
},
|
|
810
|
-
{
|
|
811
|
-
name: "latest",
|
|
812
|
-
describeKind: "derived",
|
|
813
|
-
meta: messagingMeta("topic_latest")
|
|
814
|
-
}
|
|
815
|
-
);
|
|
816
|
-
this.add("latest", this.latest);
|
|
817
|
-
this.addDisposer(keepalive(this.latest));
|
|
818
|
-
this.hasLatest = derived(
|
|
819
|
-
[this.events],
|
|
820
|
-
([snapshot]) => snapshot.length > 0,
|
|
821
|
-
{
|
|
822
|
-
name: "hasLatest",
|
|
823
|
-
describeKind: "derived",
|
|
824
|
-
meta: messagingMeta("topic_has_latest")
|
|
825
|
-
}
|
|
826
|
-
);
|
|
827
|
-
this.add("hasLatest", this.hasLatest);
|
|
828
|
-
this.addDisposer(keepalive(this.hasLatest));
|
|
829
|
-
this.addDisposer(() => {
|
|
830
|
-
this.events.down([[COMPLETE]]);
|
|
831
|
-
});
|
|
832
|
-
this.addDisposer(() => this._log.disposeAllViews());
|
|
833
|
-
}
|
|
834
|
-
publish(value) {
|
|
835
|
-
this._log.append(value);
|
|
836
|
-
}
|
|
837
|
-
retained() {
|
|
838
|
-
return this.events.cache;
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
var SubscriptionGraph = class extends Graph {
|
|
842
|
-
source;
|
|
843
|
-
cursor;
|
|
844
|
-
available;
|
|
845
|
-
/**
|
|
846
|
-
* Reference to the upstream topic graph. Intentionally NOT mounted
|
|
847
|
-
* under this subscription: a subscription is a VIEW over an
|
|
848
|
-
* externally-owned topic. Double-mounting (e.g. hub-owned topic +
|
|
849
|
-
* sub-mount here) would make either-side teardown leave the other
|
|
850
|
-
* holding a dead reference. Node-level `derived([topicEvents], …)`
|
|
851
|
-
* still wires the data dependency across graph boundaries. D1(e).
|
|
852
|
-
*/
|
|
853
|
-
topic;
|
|
854
|
-
constructor(name, topicGraph, opts = {}) {
|
|
855
|
-
super(name, opts.graph);
|
|
856
|
-
const initialCursor = requireNonNegativeInt(opts.cursor ?? 0, "subscription cursor");
|
|
857
|
-
this.topic = topicGraph;
|
|
858
|
-
const topicEvents = topicGraph.events;
|
|
859
|
-
this.source = derived([topicEvents], ([snapshot]) => snapshot, {
|
|
860
|
-
name: "source",
|
|
861
|
-
describeKind: "derived",
|
|
862
|
-
meta: messagingMeta("subscription_source"),
|
|
863
|
-
initial: topicEvents.cache
|
|
864
|
-
});
|
|
865
|
-
this.add("source", this.source);
|
|
866
|
-
this.cursor = state(initialCursor, {
|
|
867
|
-
name: "cursor",
|
|
868
|
-
describeKind: "state",
|
|
869
|
-
meta: messagingMeta("subscription_cursor")
|
|
870
|
-
});
|
|
871
|
-
this.add("cursor", this.cursor);
|
|
872
|
-
this.available = derived(
|
|
873
|
-
[this.source, this.cursor],
|
|
874
|
-
([sourceSnapshot, cursor]) => {
|
|
875
|
-
const entries = sourceSnapshot;
|
|
876
|
-
const start = Math.max(0, Math.trunc(cursor ?? 0));
|
|
877
|
-
return entries.slice(start);
|
|
878
|
-
},
|
|
879
|
-
{
|
|
880
|
-
name: "available",
|
|
881
|
-
describeKind: "derived",
|
|
882
|
-
meta: messagingMeta("subscription_available"),
|
|
883
|
-
initial: []
|
|
884
|
-
}
|
|
885
|
-
);
|
|
886
|
-
this.add("available", this.available);
|
|
887
|
-
this.addDisposer(keepalive(this.source));
|
|
888
|
-
this.addDisposer(keepalive(this.available));
|
|
889
|
-
}
|
|
890
|
-
ack(count) {
|
|
891
|
-
const available = this.available.cache;
|
|
892
|
-
const requested = count === void 0 ? available.length : requireNonNegativeInt(count, "subscription ack count");
|
|
893
|
-
const step = Math.min(requested, available.length);
|
|
894
|
-
if (step <= 0) return this.cursor.cache;
|
|
895
|
-
const next = this.cursor.cache + step;
|
|
896
|
-
this.cursor.emit(next);
|
|
897
|
-
return next;
|
|
898
|
-
}
|
|
899
|
-
pull(limit, opts = {}) {
|
|
900
|
-
const available = this.available.cache;
|
|
901
|
-
const max = limit === void 0 ? available.length : requireNonNegativeInt(limit, "subscription pull limit");
|
|
902
|
-
const out = available.slice(0, max);
|
|
903
|
-
if (opts.ack && out.length > 0) this.ack(out.length);
|
|
904
|
-
return out;
|
|
905
|
-
}
|
|
906
|
-
};
|
|
907
|
-
var JobQueueGraph = class extends Graph {
|
|
908
|
-
_pending;
|
|
909
|
-
_jobs;
|
|
910
|
-
_seq = 0;
|
|
911
|
-
pending;
|
|
912
|
-
jobs;
|
|
913
|
-
depth;
|
|
914
|
-
constructor(name, opts = {}) {
|
|
915
|
-
super(name, opts.graph);
|
|
916
|
-
this._pending = reactiveList([], { name: "pending" });
|
|
917
|
-
this._jobs = reactiveMap({ name: "jobs" });
|
|
918
|
-
this.pending = this._pending.items;
|
|
919
|
-
this.jobs = this._jobs.entries;
|
|
920
|
-
this.add("pending", this.pending);
|
|
921
|
-
this.add("jobs", this.jobs);
|
|
922
|
-
this.depth = derived([this.pending], ([snapshot]) => snapshot.length, {
|
|
923
|
-
name: "depth",
|
|
924
|
-
describeKind: "derived",
|
|
925
|
-
meta: messagingMeta("queue_depth"),
|
|
926
|
-
initial: 0
|
|
927
|
-
});
|
|
928
|
-
this.add("depth", this.depth);
|
|
929
|
-
this.addDisposer(keepalive(this.depth));
|
|
930
|
-
}
|
|
931
|
-
enqueue(payload, opts = {}) {
|
|
932
|
-
const id = opts.id ?? `${this.name}-${++this._seq}`;
|
|
933
|
-
if (this._jobs.get(id) !== void 0) {
|
|
934
|
-
throw new Error(`jobQueue("${this.name}"): duplicate job id "${id}"`);
|
|
935
|
-
}
|
|
936
|
-
const job = {
|
|
937
|
-
id,
|
|
938
|
-
payload,
|
|
939
|
-
attempts: 0,
|
|
940
|
-
metadata: Object.freeze({ ...opts.metadata ?? {} }),
|
|
941
|
-
state: "queued"
|
|
942
|
-
};
|
|
943
|
-
this._jobs.set(id, job);
|
|
944
|
-
this._pending.append(id);
|
|
945
|
-
return id;
|
|
946
|
-
}
|
|
947
|
-
claim(limit = 1) {
|
|
948
|
-
const max = requireNonNegativeInt(limit, "job queue claim limit");
|
|
949
|
-
if (max === 0) return [];
|
|
950
|
-
const out = [];
|
|
951
|
-
while (out.length < max) {
|
|
952
|
-
const ids = this.pending.cache;
|
|
953
|
-
if (ids.length === 0) break;
|
|
954
|
-
const id = this._pending.pop(0);
|
|
955
|
-
const job = this._jobs.get(id);
|
|
956
|
-
if (!job || job.state !== "queued") continue;
|
|
957
|
-
const inflight = {
|
|
958
|
-
...job,
|
|
959
|
-
state: "inflight",
|
|
960
|
-
attempts: job.attempts + 1
|
|
961
|
-
};
|
|
962
|
-
this._jobs.set(id, inflight);
|
|
963
|
-
out.push(inflight);
|
|
964
|
-
}
|
|
965
|
-
return out;
|
|
966
|
-
}
|
|
967
|
-
ack(id) {
|
|
968
|
-
const job = this._jobs.get(id);
|
|
969
|
-
if (!job || job.state !== "inflight") return false;
|
|
970
|
-
this._jobs.delete(id);
|
|
971
|
-
return true;
|
|
972
|
-
}
|
|
973
|
-
nack(id, opts = {}) {
|
|
974
|
-
const job = this._jobs.get(id);
|
|
975
|
-
if (!job || job.state !== "inflight") return false;
|
|
976
|
-
if (opts.requeue ?? true) {
|
|
977
|
-
this._jobs.set(id, { ...job, state: "queued" });
|
|
978
|
-
this._pending.append(id);
|
|
979
|
-
return true;
|
|
980
|
-
}
|
|
981
|
-
this._jobs.delete(id);
|
|
982
|
-
return true;
|
|
983
|
-
}
|
|
984
|
-
};
|
|
985
|
-
var JobFlowGraph = class extends Graph {
|
|
986
|
-
_stageNames;
|
|
987
|
-
_queues = /* @__PURE__ */ new Map();
|
|
988
|
-
_completed;
|
|
989
|
-
completed;
|
|
990
|
-
completedCount;
|
|
991
|
-
constructor(name, opts = {}) {
|
|
992
|
-
super(name, opts.graph);
|
|
993
|
-
const stages = (opts.stages ?? ["incoming", "processing", "done"]).map((v) => v.trim());
|
|
994
|
-
if (stages.length < 2) {
|
|
995
|
-
throw new Error(`jobFlow("${name}"): requires at least 2 stages`);
|
|
996
|
-
}
|
|
997
|
-
const unique = new Set(stages);
|
|
998
|
-
if (unique.size !== stages.length) {
|
|
999
|
-
throw new Error(`jobFlow("${name}"): stage names must be unique`);
|
|
1000
|
-
}
|
|
1001
|
-
this._stageNames = Object.freeze([...stages]);
|
|
1002
|
-
for (const stage of this._stageNames) {
|
|
1003
|
-
const q = jobQueue(`${name}-${stage}`);
|
|
1004
|
-
this._queues.set(stage, q);
|
|
1005
|
-
this.mount(stage, q);
|
|
1006
|
-
}
|
|
1007
|
-
this._completed = reactiveLog([], { name: "completed" });
|
|
1008
|
-
this.completed = this._completed.entries;
|
|
1009
|
-
this.add("completed", this.completed);
|
|
1010
|
-
this.completedCount = derived(
|
|
1011
|
-
[this.completed],
|
|
1012
|
-
([snapshot]) => snapshot.length,
|
|
1013
|
-
{
|
|
1014
|
-
name: "completedCount",
|
|
1015
|
-
describeKind: "derived",
|
|
1016
|
-
meta: messagingMeta("job_flow_completed_count"),
|
|
1017
|
-
initial: 0
|
|
1018
|
-
}
|
|
1019
|
-
);
|
|
1020
|
-
this.add("completedCount", this.completedCount);
|
|
1021
|
-
this.addDisposer(keepalive(this.completedCount));
|
|
1022
|
-
const maxPerPump = Math.max(
|
|
1023
|
-
1,
|
|
1024
|
-
requireNonNegativeInt(opts.maxPerPump ?? DEFAULT_MAX_PER_PUMP, "job flow maxPerPump")
|
|
1025
|
-
);
|
|
1026
|
-
for (let i = 0; i < this._stageNames.length; i += 1) {
|
|
1027
|
-
const stage = this._stageNames[i];
|
|
1028
|
-
const current = this.queue(stage);
|
|
1029
|
-
const next = i + 1 < this._stageNames.length ? this.queue(this._stageNames[i + 1]) : null;
|
|
1030
|
-
const pump = node(
|
|
1031
|
-
[current.pending],
|
|
1032
|
-
() => {
|
|
1033
|
-
let moved = 0;
|
|
1034
|
-
while (moved < maxPerPump) {
|
|
1035
|
-
const claim = current.claim(1);
|
|
1036
|
-
if (claim.length === 0) break;
|
|
1037
|
-
const job = claim[0];
|
|
1038
|
-
if (!job) break;
|
|
1039
|
-
if (next) {
|
|
1040
|
-
next.enqueue(job.payload, {
|
|
1041
|
-
metadata: {
|
|
1042
|
-
...job.metadata,
|
|
1043
|
-
job_flow_from: stage
|
|
1044
|
-
}
|
|
1045
|
-
});
|
|
1046
|
-
} else {
|
|
1047
|
-
this._completed.append(job);
|
|
1048
|
-
}
|
|
1049
|
-
current.ack(job.id);
|
|
1050
|
-
moved += 1;
|
|
1051
|
-
}
|
|
1052
|
-
},
|
|
1053
|
-
{
|
|
1054
|
-
name: `pump_${stage}`,
|
|
1055
|
-
describeKind: "effect",
|
|
1056
|
-
meta: messagingMeta("job_flow_pump")
|
|
1057
|
-
}
|
|
1058
|
-
);
|
|
1059
|
-
this.add(`pump_${stage}`, pump);
|
|
1060
|
-
this.addDisposer(keepalive(pump));
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
stages() {
|
|
1064
|
-
return this._stageNames;
|
|
1065
|
-
}
|
|
1066
|
-
queue(stage) {
|
|
1067
|
-
const q = this._queues.get(stage);
|
|
1068
|
-
if (!q) throw new Error(`jobFlow("${this.name}"): unknown stage "${stage}"`);
|
|
1069
|
-
return q;
|
|
1070
|
-
}
|
|
1071
|
-
enqueue(payload, opts = {}) {
|
|
1072
|
-
return this.queue(this._stageNames[0]).enqueue(payload, opts);
|
|
1073
|
-
}
|
|
1074
|
-
retainedCompleted() {
|
|
1075
|
-
return this.completed.cache;
|
|
1076
|
-
}
|
|
1077
|
-
};
|
|
1078
|
-
var TopicBridgeGraph = class extends Graph {
|
|
1079
|
-
_sourceSub;
|
|
1080
|
-
_target;
|
|
1081
|
-
bridgedCount;
|
|
1082
|
-
constructor(name, sourceTopic, targetTopic, opts = {}) {
|
|
1083
|
-
super(name, opts.graph);
|
|
1084
|
-
this._sourceSub = subscription(`${name}-subscription`, sourceTopic, {
|
|
1085
|
-
cursor: opts.cursor
|
|
1086
|
-
});
|
|
1087
|
-
this._target = targetTopic;
|
|
1088
|
-
this.mount("subscription", this._sourceSub);
|
|
1089
|
-
this.bridgedCount = state(0, {
|
|
1090
|
-
name: "bridgedCount",
|
|
1091
|
-
describeKind: "state",
|
|
1092
|
-
meta: messagingMeta("topic_bridge_count")
|
|
1093
|
-
});
|
|
1094
|
-
this.add("bridgedCount", this.bridgedCount);
|
|
1095
|
-
const maxPerPump = Math.max(
|
|
1096
|
-
1,
|
|
1097
|
-
requireNonNegativeInt(opts.maxPerPump ?? DEFAULT_MAX_PER_PUMP, "topic bridge maxPerPump")
|
|
1098
|
-
);
|
|
1099
|
-
const mapValue = opts.map ?? ((value) => value);
|
|
1100
|
-
const pump = node(
|
|
1101
|
-
[this._sourceSub.available],
|
|
1102
|
-
() => {
|
|
1103
|
-
const available = this._sourceSub.pull(maxPerPump, { ack: true });
|
|
1104
|
-
if (available.length === 0) return;
|
|
1105
|
-
let bridged = 0;
|
|
1106
|
-
for (const value of available) {
|
|
1107
|
-
const mapped = mapValue(value);
|
|
1108
|
-
if (mapped === void 0) continue;
|
|
1109
|
-
this._target.publish(mapped);
|
|
1110
|
-
bridged += 1;
|
|
1111
|
-
}
|
|
1112
|
-
if (bridged > 0) {
|
|
1113
|
-
const current = this.bridgedCount.cache;
|
|
1114
|
-
this.bridgedCount.down([[DATA, current + bridged]]);
|
|
1115
|
-
}
|
|
1116
|
-
},
|
|
1117
|
-
{
|
|
1118
|
-
name: "pump",
|
|
1119
|
-
describeKind: "effect",
|
|
1120
|
-
meta: messagingMeta("topic_bridge_pump")
|
|
1121
|
-
}
|
|
1122
|
-
);
|
|
1123
|
-
this.add("pump", pump);
|
|
1124
|
-
this.addDisposer(keepalive(pump));
|
|
1125
|
-
}
|
|
1126
|
-
};
|
|
1127
|
-
var MessagingHubGraph = class extends Graph {
|
|
1128
|
-
_topics = /* @__PURE__ */ new Map();
|
|
1129
|
-
_version = 0;
|
|
1130
|
-
_defaultTopicOptions;
|
|
1131
|
-
constructor(name, opts = {}) {
|
|
1132
|
-
super(name, opts.graph);
|
|
1133
|
-
this._defaultTopicOptions = { ...opts.defaultTopicOptions ?? {} };
|
|
1134
|
-
}
|
|
1135
|
-
/** Monotonic counter advancing on topic create/remove. */
|
|
1136
|
-
get version() {
|
|
1137
|
-
return this._version;
|
|
1138
|
-
}
|
|
1139
|
-
/** Number of topics currently in the hub. */
|
|
1140
|
-
get size() {
|
|
1141
|
-
return this._topics.size;
|
|
1142
|
-
}
|
|
1143
|
-
/** Checks topic existence without creating. */
|
|
1144
|
-
has(name) {
|
|
1145
|
-
return this._topics.has(name);
|
|
1146
|
-
}
|
|
1147
|
-
/** Iterator over topic names. */
|
|
1148
|
-
topicNames() {
|
|
1149
|
-
return this._topics.keys();
|
|
1150
|
-
}
|
|
1151
|
-
/**
|
|
1152
|
-
* Returns the {@link TopicGraph} for `name`, creating lazily on first call.
|
|
1153
|
-
* Subsequent calls with the same name return the same instance (options on
|
|
1154
|
-
* repeat calls are ignored — the topic is already configured).
|
|
1155
|
-
*/
|
|
1156
|
-
topic(name, opts) {
|
|
1157
|
-
let t = this._topics.get(name);
|
|
1158
|
-
if (t === void 0) {
|
|
1159
|
-
const effective = { ...this._defaultTopicOptions, ...opts ?? {} };
|
|
1160
|
-
t = new TopicGraph(name, effective);
|
|
1161
|
-
this._topics.set(name, t);
|
|
1162
|
-
this.mount(name, t);
|
|
1163
|
-
this._version += 1;
|
|
1164
|
-
}
|
|
1165
|
-
return t;
|
|
1166
|
-
}
|
|
1167
|
-
/**
|
|
1168
|
-
* Publishes a value to the topic, lazily creating it on first publish.
|
|
1169
|
-
*
|
|
1170
|
-
* **Late-subscriber caveat:** the topic is created lazily, so subscribers
|
|
1171
|
-
* that attach AFTER a publish only see the retained window (governed by
|
|
1172
|
-
* `retainedLimit` on `TopicOptions` / `defaultTopicOptions`). If
|
|
1173
|
-
* `retainedLimit === 0` is set explicitly, early publishes are
|
|
1174
|
-
* effectively dropped — prefer an unset `retainedLimit` (unbounded
|
|
1175
|
-
* retention) or subscribe before publishing when late-subscribers matter.
|
|
1176
|
-
*/
|
|
1177
|
-
publish(name, value) {
|
|
1178
|
-
this.topic(name).publish(value);
|
|
1179
|
-
}
|
|
1180
|
-
/**
|
|
1181
|
-
* Bulk publish — issues all publishes inside one outer batch. New topics
|
|
1182
|
-
* are created on demand. No-op if `entries` yields nothing.
|
|
1183
|
-
*
|
|
1184
|
-
* **Iterable consumption (F6):** `entries` is consumed once (single-pass)
|
|
1185
|
-
* INSIDE the batch frame. If the iterator throws mid-way, the batch is
|
|
1186
|
-
* discarded and NO publishes are visible to subscribers (all-or-nothing).
|
|
1187
|
-
* Pass an array or `Set` for multi-shot callers.
|
|
1188
|
-
*/
|
|
1189
|
-
publishMany(entries) {
|
|
1190
|
-
batch(() => {
|
|
1191
|
-
for (const [name, value] of entries) {
|
|
1192
|
-
this.topic(name).publish(value);
|
|
1193
|
-
}
|
|
1194
|
-
});
|
|
1195
|
-
}
|
|
1196
|
-
/**
|
|
1197
|
-
* Creates a {@link SubscriptionGraph} over a named topic. The topic is
|
|
1198
|
-
* lazily created if missing. Subscription lifecycle is owned by the caller —
|
|
1199
|
-
* the hub does NOT mount the subscription.
|
|
1200
|
-
*
|
|
1201
|
-
* @param subName - Local name for the subscription graph.
|
|
1202
|
-
* @param topicName - Hub topic to subscribe to.
|
|
1203
|
-
* @param opts - `SubscriptionOptions` (initial cursor, etc.).
|
|
1204
|
-
*/
|
|
1205
|
-
subscribe(subName, topicName, opts) {
|
|
1206
|
-
const t = this.topic(topicName);
|
|
1207
|
-
return new SubscriptionGraph(subName, t, opts);
|
|
1208
|
-
}
|
|
1209
|
-
/**
|
|
1210
|
-
* Unmounts and tears down the topic's graph. Returns `true` if the topic
|
|
1211
|
-
* existed. Subscribers receive `TEARDOWN` via {@link Graph.remove}.
|
|
1212
|
-
*/
|
|
1213
|
-
removeTopic(name) {
|
|
1214
|
-
if (!this._topics.has(name)) return false;
|
|
1215
|
-
try {
|
|
1216
|
-
this.remove(name);
|
|
1217
|
-
} finally {
|
|
1218
|
-
this._topics.delete(name);
|
|
1219
|
-
this._version += 1;
|
|
1220
|
-
}
|
|
1221
|
-
return true;
|
|
1222
|
-
}
|
|
1223
|
-
};
|
|
1224
|
-
function topic(name, opts) {
|
|
1225
|
-
return new TopicGraph(name, opts);
|
|
1226
|
-
}
|
|
1227
|
-
function messagingHub(name, opts) {
|
|
1228
|
-
return new MessagingHubGraph(name, opts);
|
|
1229
|
-
}
|
|
1230
|
-
function subscription(name, topicGraph, opts) {
|
|
1231
|
-
return new SubscriptionGraph(name, topicGraph, opts);
|
|
1232
|
-
}
|
|
1233
|
-
function jobQueue(name, opts) {
|
|
1234
|
-
return new JobQueueGraph(name, opts);
|
|
1235
|
-
}
|
|
1236
|
-
function jobFlow(name, opts) {
|
|
1237
|
-
return new JobFlowGraph(name, opts);
|
|
1238
|
-
}
|
|
1239
|
-
function topicBridge(name, sourceTopic, targetTopic, opts) {
|
|
1240
|
-
return new TopicBridgeGraph(name, sourceTopic, targetTopic, opts);
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
// src/patterns/orchestration.ts
|
|
1244
|
-
var orchestration_exports = {};
|
|
1245
|
-
__export(orchestration_exports, {
|
|
1246
|
-
approval: () => approval,
|
|
1247
|
-
branch: () => branch,
|
|
1248
|
-
forEach: () => forEach2,
|
|
1249
|
-
gate: () => gate,
|
|
1250
|
-
join: () => join,
|
|
1251
|
-
loop: () => loop,
|
|
1252
|
-
onFailure: () => onFailure,
|
|
1253
|
-
pipeline: () => pipeline,
|
|
1254
|
-
sensor: () => sensor,
|
|
1255
|
-
subPipeline: () => subPipeline,
|
|
1256
|
-
task: () => task,
|
|
1257
|
-
valve: () => valve2,
|
|
1258
|
-
wait: () => wait
|
|
1259
|
-
});
|
|
1260
|
-
function resolveDep(graph, dep) {
|
|
1261
|
-
if (typeof dep === "string") {
|
|
1262
|
-
return { node: graph.resolve(dep), path: dep };
|
|
1263
|
-
}
|
|
1264
|
-
const path = findRegisteredNodePath(graph, dep);
|
|
1265
|
-
if (!path) {
|
|
1266
|
-
throw new Error(
|
|
1267
|
-
"orchestration dep node must already be registered in the graph so explicit edges can be recorded; pass a string path or register the node first"
|
|
1268
|
-
);
|
|
1269
|
-
}
|
|
1270
|
-
return { node: dep, path };
|
|
1271
|
-
}
|
|
1272
|
-
function findRegisteredNodePath(graph, target) {
|
|
1273
|
-
const described = graph.describe();
|
|
1274
|
-
const metaSegment = `::${GRAPH_META_SEGMENT}::`;
|
|
1275
|
-
for (const path of Object.keys(described.nodes).sort()) {
|
|
1276
|
-
if (path.includes(metaSegment)) continue;
|
|
1277
|
-
try {
|
|
1278
|
-
if (graph.resolve(path) === target) return path;
|
|
1279
|
-
} catch {
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
return void 0;
|
|
1283
|
-
}
|
|
1284
|
-
function registerStep(graph, name, step, depPaths) {
|
|
1285
|
-
graph.add(name, step);
|
|
1286
|
-
void depPaths;
|
|
1287
|
-
}
|
|
1288
|
-
function baseMeta(kind, meta) {
|
|
1289
|
-
return domainMeta("orchestration", kind, meta);
|
|
1290
|
-
}
|
|
1291
|
-
function coerceLoopIterations(raw) {
|
|
1292
|
-
const parseString = (value) => {
|
|
1293
|
-
const trimmed = value.trim();
|
|
1294
|
-
if (trimmed.length === 0) return 0;
|
|
1295
|
-
return Number(trimmed);
|
|
1296
|
-
};
|
|
1297
|
-
let parsed;
|
|
1298
|
-
if (typeof raw === "string") {
|
|
1299
|
-
parsed = parseString(raw);
|
|
1300
|
-
} else if (raw === null) {
|
|
1301
|
-
parsed = 0;
|
|
1302
|
-
} else {
|
|
1303
|
-
parsed = Number(raw);
|
|
1304
|
-
}
|
|
1305
|
-
if (!Number.isFinite(parsed)) return 1;
|
|
1306
|
-
return Math.max(0, Math.trunc(parsed));
|
|
1307
|
-
}
|
|
1308
|
-
function pipeline(name, opts) {
|
|
1309
|
-
return new Graph(name, opts);
|
|
1310
|
-
}
|
|
1311
|
-
function task(graph, name, run, opts) {
|
|
1312
|
-
const depRefs = opts?.deps ?? [];
|
|
1313
|
-
const deps = depRefs.map((dep) => resolveDep(graph, dep));
|
|
1314
|
-
const { deps: _deps, ...nodeOpts } = opts ?? {};
|
|
1315
|
-
const wrapped = (batchData, actions, ctx) => {
|
|
1316
|
-
const data = batchData.map(
|
|
1317
|
-
(batch2, i) => batch2 != null && batch2.length > 0 ? batch2.at(-1) : ctx.prevData[i]
|
|
1318
|
-
);
|
|
1319
|
-
actions.emit(run(data, ctx));
|
|
1320
|
-
return void 0;
|
|
1321
|
-
};
|
|
1322
|
-
const step = node(
|
|
1323
|
-
deps.map((d) => d.node),
|
|
1324
|
-
wrapped,
|
|
1325
|
-
{
|
|
1326
|
-
...nodeOpts,
|
|
1327
|
-
name,
|
|
1328
|
-
describeKind: "derived",
|
|
1329
|
-
meta: baseMeta("task", opts?.meta)
|
|
1330
|
-
}
|
|
1331
|
-
);
|
|
1332
|
-
registerStep(
|
|
1333
|
-
graph,
|
|
1334
|
-
name,
|
|
1335
|
-
step,
|
|
1336
|
-
deps.flatMap((d) => d.path ? [d.path] : [])
|
|
1337
|
-
);
|
|
1338
|
-
return step;
|
|
1339
|
-
}
|
|
1340
|
-
function branch(graph, name, source, predicate, opts) {
|
|
1341
|
-
const src = resolveDep(graph, source);
|
|
1342
|
-
const step = derived(
|
|
1343
|
-
[src.node],
|
|
1344
|
-
([value]) => ({
|
|
1345
|
-
branch: predicate(value) ? "then" : "else",
|
|
1346
|
-
value
|
|
1347
|
-
}),
|
|
1348
|
-
{
|
|
1349
|
-
...opts,
|
|
1350
|
-
name,
|
|
1351
|
-
meta: baseMeta("branch", opts?.meta)
|
|
1352
|
-
}
|
|
1353
|
-
);
|
|
1354
|
-
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
1355
|
-
return step;
|
|
1356
|
-
}
|
|
1357
|
-
function valve2(graph, name, source, control, opts) {
|
|
1358
|
-
const src = resolveDep(graph, source);
|
|
1359
|
-
const ctrl = resolveDep(graph, control);
|
|
1360
|
-
const step = node(
|
|
1361
|
-
[src.node, ctrl.node],
|
|
1362
|
-
(batchData, actions, ctx) => {
|
|
1363
|
-
const batch0 = batchData[0];
|
|
1364
|
-
const batch1 = batchData[1];
|
|
1365
|
-
const ctrlVal = batch1 != null && batch1.length > 0 ? batch1.at(-1) : ctx.prevData[1];
|
|
1366
|
-
if (batch0 == null || batch0.length === 0) {
|
|
1367
|
-
if (batch1 != null && batch1.length > 0 && ctrlVal && ctx.prevData[0] !== void 0) {
|
|
1368
|
-
actions.emit(ctx.prevData[0]);
|
|
1369
|
-
} else {
|
|
1370
|
-
actions.down([[RESOLVED]]);
|
|
1371
|
-
}
|
|
1372
|
-
return;
|
|
1373
|
-
}
|
|
1374
|
-
if (!ctrlVal) {
|
|
1375
|
-
actions.down([[RESOLVED]]);
|
|
1376
|
-
return;
|
|
1377
|
-
}
|
|
1378
|
-
for (const v of batch0) actions.emit(v);
|
|
1379
|
-
},
|
|
1380
|
-
{
|
|
1381
|
-
...opts,
|
|
1382
|
-
name,
|
|
1383
|
-
describeKind: "derived",
|
|
1384
|
-
meta: baseMeta("valve", opts?.meta)
|
|
1385
|
-
}
|
|
1386
|
-
);
|
|
1387
|
-
registerStep(
|
|
1388
|
-
graph,
|
|
1389
|
-
name,
|
|
1390
|
-
step,
|
|
1391
|
-
[src.path, ctrl.path].filter((v) => typeof v === "string")
|
|
1392
|
-
);
|
|
1393
|
-
return step;
|
|
1394
|
-
}
|
|
1395
|
-
function approval(graph, name, source, approver, opts) {
|
|
1396
|
-
const src = resolveDep(graph, source);
|
|
1397
|
-
const ctrl = resolveDep(graph, approver);
|
|
1398
|
-
const isApproved = opts?.isApproved ?? ((value) => Boolean(value));
|
|
1399
|
-
const step = node(
|
|
1400
|
-
[src.node, ctrl.node],
|
|
1401
|
-
(batchData, actions, ctx) => {
|
|
1402
|
-
const batch0 = batchData[0];
|
|
1403
|
-
const batch1 = batchData[1];
|
|
1404
|
-
const ctrlVal = batch1 != null && batch1.length > 0 ? batch1.at(-1) : ctx.prevData[1];
|
|
1405
|
-
if (ctrlVal === void 0 || !isApproved(ctrlVal)) {
|
|
1406
|
-
actions.down([[RESOLVED]]);
|
|
1407
|
-
return;
|
|
1408
|
-
}
|
|
1409
|
-
if (batch0 == null || batch0.length === 0) {
|
|
1410
|
-
if (batch1 != null && batch1.length > 0 && ctx.prevData[0] !== void 0) {
|
|
1411
|
-
actions.emit(ctx.prevData[0]);
|
|
1412
|
-
} else {
|
|
1413
|
-
actions.down([[RESOLVED]]);
|
|
1414
|
-
}
|
|
1415
|
-
return;
|
|
1416
|
-
}
|
|
1417
|
-
for (const v of batch0) actions.emit(v);
|
|
1418
|
-
},
|
|
1419
|
-
{
|
|
1420
|
-
...opts,
|
|
1421
|
-
name,
|
|
1422
|
-
describeKind: "derived",
|
|
1423
|
-
meta: baseMeta("approval", opts?.meta)
|
|
1424
|
-
}
|
|
1425
|
-
);
|
|
1426
|
-
registerStep(
|
|
1427
|
-
graph,
|
|
1428
|
-
name,
|
|
1429
|
-
step,
|
|
1430
|
-
[src.path, ctrl.path].filter((v) => typeof v === "string")
|
|
1431
|
-
);
|
|
1432
|
-
return step;
|
|
1433
|
-
}
|
|
1434
|
-
function gate(graph, name, source, opts) {
|
|
1435
|
-
const maxPending = opts?.maxPending ?? Infinity;
|
|
1436
|
-
if (maxPending < 1 && maxPending !== Infinity) {
|
|
1437
|
-
throw new RangeError("gate: maxPending must be >= 1");
|
|
1438
|
-
}
|
|
1439
|
-
const startOpen = opts?.startOpen ?? false;
|
|
1440
|
-
const src = resolveDep(graph, source);
|
|
1441
|
-
const pendingNode = state([], { name: "pending", equals: () => false });
|
|
1442
|
-
const isOpenNode = state(startOpen, { name: "isOpen" });
|
|
1443
|
-
const countNode = derived([pendingNode], ([arr]) => arr.length, {
|
|
1444
|
-
name: "count"
|
|
1445
|
-
});
|
|
1446
|
-
let queue = [];
|
|
1447
|
-
let torn = false;
|
|
1448
|
-
let latestIsOpen = startOpen;
|
|
1449
|
-
const isOpenUnsub = isOpenNode.subscribe((msgs) => {
|
|
1450
|
-
for (const m of msgs) {
|
|
1451
|
-
if (m[0] === DATA) latestIsOpen = m[1];
|
|
1452
|
-
}
|
|
1453
|
-
});
|
|
1454
|
-
function syncPending() {
|
|
1455
|
-
pendingNode.down([[DATA, [...queue]]]);
|
|
1456
|
-
}
|
|
1457
|
-
function enqueue(value) {
|
|
1458
|
-
queue.push(value);
|
|
1459
|
-
if (queue.length > maxPending) queue.shift();
|
|
1460
|
-
syncPending();
|
|
1461
|
-
}
|
|
1462
|
-
function dequeue(n) {
|
|
1463
|
-
const items = queue.splice(0, n);
|
|
1464
|
-
syncPending();
|
|
1465
|
-
return items;
|
|
1466
|
-
}
|
|
1467
|
-
function guardTorn(method) {
|
|
1468
|
-
if (torn) throw new Error(`gate: ${method}() called after gate was torn down`);
|
|
1469
|
-
}
|
|
1470
|
-
const output = node(
|
|
1471
|
-
[src.node],
|
|
1472
|
-
(batchData, actions, ctx) => {
|
|
1473
|
-
const terminal = ctx.terminalDeps[0];
|
|
1474
|
-
if (terminal !== void 0) {
|
|
1475
|
-
torn = true;
|
|
1476
|
-
queue = [];
|
|
1477
|
-
syncPending();
|
|
1478
|
-
actions.down(terminal === true ? [[COMPLETE]] : [[ERROR, terminal]]);
|
|
1479
|
-
return;
|
|
1480
|
-
}
|
|
1481
|
-
const batch0 = batchData[0];
|
|
1482
|
-
if (batch0 == null || batch0.length === 0) {
|
|
1483
|
-
actions.down([[RESOLVED]]);
|
|
1484
|
-
return;
|
|
1485
|
-
}
|
|
1486
|
-
for (const v of batch0) {
|
|
1487
|
-
if (latestIsOpen) {
|
|
1488
|
-
actions.emit(v);
|
|
1489
|
-
} else {
|
|
1490
|
-
enqueue(v);
|
|
1491
|
-
actions.down([[RESOLVED]]);
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
},
|
|
1495
|
-
{
|
|
1496
|
-
name,
|
|
1497
|
-
describeKind: "derived",
|
|
1498
|
-
meta: baseMeta("gate", opts?.meta)
|
|
1499
|
-
}
|
|
1500
|
-
);
|
|
1501
|
-
const controller = {
|
|
1502
|
-
node: output,
|
|
1503
|
-
pending: pendingNode,
|
|
1504
|
-
count: countNode,
|
|
1505
|
-
isOpen: isOpenNode,
|
|
1506
|
-
approve(count = 1) {
|
|
1507
|
-
guardTorn("approve");
|
|
1508
|
-
const items = dequeue(count);
|
|
1509
|
-
for (const item of items) {
|
|
1510
|
-
if (torn) break;
|
|
1511
|
-
output.down([[DATA, item]]);
|
|
1512
|
-
}
|
|
1513
|
-
},
|
|
1514
|
-
reject(count = 1) {
|
|
1515
|
-
guardTorn("reject");
|
|
1516
|
-
dequeue(count);
|
|
1517
|
-
},
|
|
1518
|
-
modify(fn, count = 1) {
|
|
1519
|
-
guardTorn("modify");
|
|
1520
|
-
const snapshot = [...queue];
|
|
1521
|
-
const items = dequeue(count);
|
|
1522
|
-
for (let i = 0; i < items.length; i++) {
|
|
1523
|
-
if (torn) break;
|
|
1524
|
-
output.down([[DATA, fn(items[i], i, snapshot)]]);
|
|
1525
|
-
}
|
|
1526
|
-
},
|
|
1527
|
-
open() {
|
|
1528
|
-
guardTorn("open");
|
|
1529
|
-
batch(() => {
|
|
1530
|
-
isOpenNode.down([[DATA, true]]);
|
|
1531
|
-
const items = dequeue(queue.length);
|
|
1532
|
-
for (const item of items) {
|
|
1533
|
-
if (torn) break;
|
|
1534
|
-
output.down([[DATA, item]]);
|
|
1535
|
-
}
|
|
1536
|
-
});
|
|
1537
|
-
},
|
|
1538
|
-
close() {
|
|
1539
|
-
guardTorn("close");
|
|
1540
|
-
isOpenNode.down([[DATA, false]]);
|
|
1541
|
-
}
|
|
1542
|
-
};
|
|
1543
|
-
graph.addDisposer(countNode.subscribe(() => void 0));
|
|
1544
|
-
graph.addDisposer(isOpenUnsub);
|
|
1545
|
-
registerStep(graph, name, output, src.path ? [src.path] : []);
|
|
1546
|
-
const internal = new Graph(`${name}_state`);
|
|
1547
|
-
internal.add("pending", pendingNode);
|
|
1548
|
-
internal.add("isOpen", isOpenNode);
|
|
1549
|
-
internal.add("count", countNode);
|
|
1550
|
-
graph.mount(`${name}_state`, internal);
|
|
1551
|
-
return controller;
|
|
1552
|
-
}
|
|
1553
|
-
function forEach2(graph, name, source, run, opts) {
|
|
1554
|
-
const src = resolveDep(graph, source);
|
|
1555
|
-
let terminated = false;
|
|
1556
|
-
const step = node(
|
|
1557
|
-
[src.node],
|
|
1558
|
-
(batchData, actions, ctx) => {
|
|
1559
|
-
if (terminated) {
|
|
1560
|
-
actions.down([[RESOLVED]]);
|
|
1561
|
-
return;
|
|
1562
|
-
}
|
|
1563
|
-
const terminal = ctx.terminalDeps[0];
|
|
1564
|
-
if (terminal !== void 0) {
|
|
1565
|
-
terminated = true;
|
|
1566
|
-
actions.down(terminal === true ? [[COMPLETE]] : [[ERROR, terminal]]);
|
|
1567
|
-
return;
|
|
1568
|
-
}
|
|
1569
|
-
const batch0 = batchData[0];
|
|
1570
|
-
if (batch0 == null || batch0.length === 0) {
|
|
1571
|
-
actions.down([[RESOLVED]]);
|
|
1572
|
-
return;
|
|
1573
|
-
}
|
|
1574
|
-
for (const v of batch0) {
|
|
1575
|
-
try {
|
|
1576
|
-
run(v, actions);
|
|
1577
|
-
} catch (err) {
|
|
1578
|
-
terminated = true;
|
|
1579
|
-
actions.down([[ERROR, err]]);
|
|
1580
|
-
return;
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
},
|
|
1584
|
-
{
|
|
1585
|
-
...opts,
|
|
1586
|
-
name,
|
|
1587
|
-
describeKind: "effect",
|
|
1588
|
-
completeWhenDepsComplete: false,
|
|
1589
|
-
meta: baseMeta("forEach", opts?.meta)
|
|
1590
|
-
}
|
|
1591
|
-
);
|
|
1592
|
-
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
1593
|
-
return step;
|
|
1594
|
-
}
|
|
1595
|
-
function join(graph, name, deps, opts) {
|
|
1596
|
-
const resolved = deps.map((dep) => resolveDep(graph, dep));
|
|
1597
|
-
const step = derived(
|
|
1598
|
-
resolved.map((d) => d.node),
|
|
1599
|
-
(values) => values,
|
|
1600
|
-
{
|
|
1601
|
-
...opts,
|
|
1602
|
-
name,
|
|
1603
|
-
meta: baseMeta("join", opts?.meta)
|
|
1604
|
-
}
|
|
1605
|
-
);
|
|
1606
|
-
registerStep(
|
|
1607
|
-
graph,
|
|
1608
|
-
name,
|
|
1609
|
-
step,
|
|
1610
|
-
resolved.flatMap((d) => d.path ? [d.path] : [])
|
|
1611
|
-
);
|
|
1612
|
-
return step;
|
|
1613
|
-
}
|
|
1614
|
-
function loop(graph, name, source, iterate, opts) {
|
|
1615
|
-
const src = resolveDep(graph, source);
|
|
1616
|
-
const iterRef = opts?.iterations;
|
|
1617
|
-
const iterDep = typeof iterRef === "number" || iterRef === void 0 ? void 0 : resolveDep(graph, iterRef);
|
|
1618
|
-
const staticIterations = typeof iterRef === "number" ? iterRef : void 0;
|
|
1619
|
-
const step = node(
|
|
1620
|
-
iterDep ? [src.node, iterDep.node] : [src.node],
|
|
1621
|
-
(depValues, actions, ctx) => {
|
|
1622
|
-
const batch0 = depValues[0];
|
|
1623
|
-
let current = batch0 != null && batch0.length > 0 ? batch0.at(-1) : ctx.prevData[0];
|
|
1624
|
-
const batch1 = iterDep ? depValues[1] : void 0;
|
|
1625
|
-
const rawCount = staticIterations ?? (iterDep ? batch1 != null && batch1.length > 0 ? batch1.at(-1) : ctx.prevData[1] : 1);
|
|
1626
|
-
const count = coerceLoopIterations(rawCount);
|
|
1627
|
-
for (let i = 0; i < count; i += 1) {
|
|
1628
|
-
current = iterate(current, i, actions);
|
|
1629
|
-
}
|
|
1630
|
-
actions.emit(current);
|
|
1631
|
-
},
|
|
1632
|
-
{
|
|
1633
|
-
...opts,
|
|
1634
|
-
name,
|
|
1635
|
-
describeKind: "derived",
|
|
1636
|
-
meta: baseMeta("loop", opts?.meta)
|
|
1637
|
-
}
|
|
1638
|
-
);
|
|
1639
|
-
registerStep(
|
|
1640
|
-
graph,
|
|
1641
|
-
name,
|
|
1642
|
-
step,
|
|
1643
|
-
[src.path, iterDep?.path].filter((v) => typeof v === "string")
|
|
1644
|
-
);
|
|
1645
|
-
return step;
|
|
1646
|
-
}
|
|
1647
|
-
function subPipeline(graph, name, childOrBuild, opts) {
|
|
1648
|
-
const child = childOrBuild instanceof Graph ? childOrBuild : pipeline(name, opts);
|
|
1649
|
-
if (typeof childOrBuild === "function") {
|
|
1650
|
-
childOrBuild(child);
|
|
1651
|
-
}
|
|
1652
|
-
graph.mount(name, child);
|
|
1653
|
-
return child;
|
|
1654
|
-
}
|
|
1655
|
-
function sensor(graph, name, initial, opts) {
|
|
1656
|
-
const source = node([], () => void 0, {
|
|
1657
|
-
...opts,
|
|
1658
|
-
name,
|
|
1659
|
-
initial,
|
|
1660
|
-
describeKind: "producer",
|
|
1661
|
-
meta: baseMeta("sensor", opts?.meta)
|
|
1662
|
-
});
|
|
1663
|
-
registerStep(graph, name, source, []);
|
|
1664
|
-
return {
|
|
1665
|
-
node: source,
|
|
1666
|
-
push(value) {
|
|
1667
|
-
source.down([[DATA, value]]);
|
|
1668
|
-
},
|
|
1669
|
-
error(err) {
|
|
1670
|
-
source.down([[ERROR, err]]);
|
|
1671
|
-
},
|
|
1672
|
-
complete() {
|
|
1673
|
-
source.down([[COMPLETE]]);
|
|
1674
|
-
}
|
|
1675
|
-
};
|
|
1676
|
-
}
|
|
1677
|
-
function wait(graph, name, source, ms, opts) {
|
|
1678
|
-
const src = resolveDep(graph, source);
|
|
1679
|
-
const timers = /* @__PURE__ */ new Set();
|
|
1680
|
-
let terminated = false;
|
|
1681
|
-
let completed = false;
|
|
1682
|
-
function clearAllTimers() {
|
|
1683
|
-
for (const id of timers) clearTimeout(id);
|
|
1684
|
-
timers.clear();
|
|
1685
|
-
}
|
|
1686
|
-
const srcVal = src.node.cache;
|
|
1687
|
-
const initialOpt = srcVal !== void 0 ? { initial: srcVal } : {};
|
|
1688
|
-
const step = node(
|
|
1689
|
-
[],
|
|
1690
|
-
(_deps, actions) => {
|
|
1691
|
-
clearAllTimers();
|
|
1692
|
-
terminated = false;
|
|
1693
|
-
completed = false;
|
|
1694
|
-
const unsub = src.node.subscribe((msgs) => {
|
|
1695
|
-
for (const msg of msgs) {
|
|
1696
|
-
if (terminated) return;
|
|
1697
|
-
if (msg[0] === DATA) {
|
|
1698
|
-
const id = setTimeout(() => {
|
|
1699
|
-
timers.delete(id);
|
|
1700
|
-
actions.down([msg]);
|
|
1701
|
-
if (completed && timers.size === 0) {
|
|
1702
|
-
actions.down([[COMPLETE]]);
|
|
1703
|
-
}
|
|
1704
|
-
}, ms);
|
|
1705
|
-
timers.add(id);
|
|
1706
|
-
} else if (msg[0] === COMPLETE) {
|
|
1707
|
-
terminated = true;
|
|
1708
|
-
completed = true;
|
|
1709
|
-
if (timers.size === 0) {
|
|
1710
|
-
actions.down([[COMPLETE]]);
|
|
1711
|
-
}
|
|
1712
|
-
} else if (msg[0] === ERROR) {
|
|
1713
|
-
terminated = true;
|
|
1714
|
-
clearAllTimers();
|
|
1715
|
-
actions.down([msg]);
|
|
1716
|
-
} else {
|
|
1717
|
-
actions.down([msg]);
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
});
|
|
1721
|
-
return () => {
|
|
1722
|
-
unsub();
|
|
1723
|
-
clearAllTimers();
|
|
1724
|
-
terminated = true;
|
|
1725
|
-
};
|
|
1726
|
-
},
|
|
1727
|
-
{
|
|
1728
|
-
...opts,
|
|
1729
|
-
name,
|
|
1730
|
-
...initialOpt,
|
|
1731
|
-
describeKind: "derived",
|
|
1732
|
-
completeWhenDepsComplete: false,
|
|
1733
|
-
meta: baseMeta("wait", opts?.meta)
|
|
1734
|
-
}
|
|
1735
|
-
);
|
|
1736
|
-
graph.add(name, step);
|
|
1737
|
-
return step;
|
|
1738
|
-
}
|
|
1739
|
-
function onFailure(graph, name, source, recover, opts) {
|
|
1740
|
-
const src = resolveDep(graph, source);
|
|
1741
|
-
let terminated = false;
|
|
1742
|
-
const step = node(
|
|
1743
|
-
[],
|
|
1744
|
-
(_data, actions) => {
|
|
1745
|
-
const unsub = src.node.subscribe((msgs) => {
|
|
1746
|
-
for (const msg of msgs) {
|
|
1747
|
-
if (terminated) return;
|
|
1748
|
-
if (msg[0] === ERROR) {
|
|
1749
|
-
try {
|
|
1750
|
-
actions.emit(recover(msg[1], actions));
|
|
1751
|
-
} catch (err) {
|
|
1752
|
-
terminated = true;
|
|
1753
|
-
actions.down([[ERROR, err]]);
|
|
1754
|
-
}
|
|
1755
|
-
} else {
|
|
1756
|
-
actions.down([msg]);
|
|
1757
|
-
if (msg[0] === COMPLETE) terminated = true;
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
});
|
|
1761
|
-
return () => unsub();
|
|
1762
|
-
},
|
|
1763
|
-
{
|
|
1764
|
-
...opts,
|
|
1765
|
-
name,
|
|
1766
|
-
describeKind: "derived",
|
|
1767
|
-
completeWhenDepsComplete: false,
|
|
1768
|
-
// onFailure handles errors via manual subscription (recover callback).
|
|
1769
|
-
// Disable auto-propagation so dep-channel ERROR doesn't terminate this
|
|
1770
|
-
// node before the recover callback has a chance to emit a replacement value.
|
|
1771
|
-
errorWhenDepsError: false,
|
|
1772
|
-
meta: baseMeta("onFailure", opts?.meta)
|
|
1773
|
-
}
|
|
1774
|
-
);
|
|
1775
|
-
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
1776
|
-
return step;
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
// src/patterns/ai.ts
|
|
1780
|
-
function aiMeta(kind, extra) {
|
|
1781
|
-
return domainMeta("ai", kind, extra);
|
|
1782
|
-
}
|
|
1783
|
-
function isPromiseLike(x) {
|
|
1784
|
-
return x != null && typeof x.then === "function";
|
|
1785
|
-
}
|
|
1786
|
-
function isNodeLike(x) {
|
|
1787
|
-
return typeof x === "object" && x !== null && "subscribe" in x && typeof x.subscribe === "function" && "cache" in x;
|
|
1788
|
-
}
|
|
1789
|
-
function isAsyncIterableLike(x) {
|
|
1790
|
-
return x != null && typeof x === "object" && Symbol.asyncIterator in x && typeof x[Symbol.asyncIterator] === "function";
|
|
1791
|
-
}
|
|
1792
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
1793
|
-
function firstDataFromNode(resolved, opts) {
|
|
1794
|
-
if (resolved.status === "settled") {
|
|
1795
|
-
const immediate = resolved.cache;
|
|
1796
|
-
if (immediate !== void 0) {
|
|
1797
|
-
return Promise.resolve(immediate);
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1801
|
-
return new Promise((resolve, reject) => {
|
|
1802
|
-
const timer = new ResettableTimer();
|
|
1803
|
-
const unsub = resolved.subscribe((messages) => {
|
|
1804
|
-
for (const msg of messages) {
|
|
1805
|
-
if (msg[0] === DATA) {
|
|
1806
|
-
timer.cancel();
|
|
1807
|
-
unsub();
|
|
1808
|
-
resolve(msg[1]);
|
|
1809
|
-
return;
|
|
1810
|
-
}
|
|
1811
|
-
if (msg[0] === ERROR) {
|
|
1812
|
-
timer.cancel();
|
|
1813
|
-
unsub();
|
|
1814
|
-
reject(msg[1]);
|
|
1815
|
-
return;
|
|
1816
|
-
}
|
|
1817
|
-
if (msg[0] === COMPLETE) {
|
|
1818
|
-
timer.cancel();
|
|
1819
|
-
unsub();
|
|
1820
|
-
reject(new Error("firstDataFromNode: completed without producing a value"));
|
|
1821
|
-
return;
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
});
|
|
1825
|
-
timer.start(timeoutMs, () => {
|
|
1826
|
-
unsub();
|
|
1827
|
-
reject(new Error(`firstDataFromNode: timed out after ${timeoutMs}ms`));
|
|
1828
|
-
});
|
|
1829
|
-
});
|
|
1830
|
-
}
|
|
1831
|
-
async function resolveToolHandlerResult(value) {
|
|
1832
|
-
if (isPromiseLike(value)) {
|
|
1833
|
-
return resolveToolHandlerResult(await value);
|
|
1834
|
-
}
|
|
1835
|
-
if (isNodeLike(value)) {
|
|
1836
|
-
return firstDataFromNode(value);
|
|
1837
|
-
}
|
|
1838
|
-
if (isAsyncIterableLike(value)) {
|
|
1839
|
-
return firstDataFromNode(fromAny(value));
|
|
1840
|
-
}
|
|
1841
|
-
return value;
|
|
1842
|
-
}
|
|
1843
|
-
function fromLLM(adapter, messages, opts) {
|
|
1844
|
-
const msgsNode = fromAny(messages);
|
|
1845
|
-
const result = switchMap(msgsNode, (msgs) => {
|
|
1846
|
-
if (!msgs || msgs.length === 0) {
|
|
1847
|
-
return state(null);
|
|
1848
|
-
}
|
|
1849
|
-
const tools = opts?.tools;
|
|
1850
|
-
return adapter.invoke(msgs, {
|
|
1851
|
-
model: opts?.model,
|
|
1852
|
-
temperature: opts?.temperature,
|
|
1853
|
-
maxTokens: opts?.maxTokens,
|
|
1854
|
-
tools,
|
|
1855
|
-
systemPrompt: opts?.systemPrompt
|
|
1856
|
-
});
|
|
1857
|
-
});
|
|
1858
|
-
return result;
|
|
1859
|
-
}
|
|
1860
|
-
function streamingPromptNode(adapter, deps, prompt, opts) {
|
|
1861
|
-
const sourceName = opts?.name ?? "llm";
|
|
1862
|
-
const format = opts?.format ?? "text";
|
|
1863
|
-
const streamTopic = topic(`${sourceName}/stream`);
|
|
1864
|
-
const messagesNode = derived(deps, (values) => {
|
|
1865
|
-
if (values.some((v) => v == null)) return [];
|
|
1866
|
-
const text = typeof prompt === "string" ? prompt : prompt(...values);
|
|
1867
|
-
if (!text) return [];
|
|
1868
|
-
const msgs = [];
|
|
1869
|
-
if (opts?.systemPrompt) msgs.push({ role: "system", content: opts.systemPrompt });
|
|
1870
|
-
msgs.push({ role: "user", content: text });
|
|
1871
|
-
return msgs;
|
|
1872
|
-
});
|
|
1873
|
-
const output = switchMap(messagesNode, (msgs) => {
|
|
1874
|
-
const chatMsgs = msgs;
|
|
1875
|
-
if (!chatMsgs || chatMsgs.length === 0) {
|
|
1876
|
-
return state(null);
|
|
1877
|
-
}
|
|
1878
|
-
const ac = new AbortController();
|
|
1879
|
-
async function* pumpAndCollect() {
|
|
1880
|
-
let accumulated = "";
|
|
1881
|
-
let index = 0;
|
|
1882
|
-
try {
|
|
1883
|
-
for await (const token of adapter.stream(chatMsgs, {
|
|
1884
|
-
model: opts?.model,
|
|
1885
|
-
temperature: opts?.temperature,
|
|
1886
|
-
maxTokens: opts?.maxTokens,
|
|
1887
|
-
systemPrompt: opts?.systemPrompt,
|
|
1888
|
-
signal: ac.signal
|
|
1889
|
-
})) {
|
|
1890
|
-
accumulated += token;
|
|
1891
|
-
streamTopic.publish({
|
|
1892
|
-
source: sourceName,
|
|
1893
|
-
token,
|
|
1894
|
-
accumulated,
|
|
1895
|
-
index: index++
|
|
1896
|
-
});
|
|
1897
|
-
}
|
|
1898
|
-
let result;
|
|
1899
|
-
if (format === "json") {
|
|
1900
|
-
try {
|
|
1901
|
-
result = JSON.parse(stripFences(accumulated));
|
|
1902
|
-
} catch {
|
|
1903
|
-
result = null;
|
|
1904
|
-
}
|
|
1905
|
-
} else {
|
|
1906
|
-
result = accumulated;
|
|
1907
|
-
}
|
|
1908
|
-
yield result;
|
|
1909
|
-
} finally {
|
|
1910
|
-
ac.abort();
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
return fromAny(pumpAndCollect());
|
|
1914
|
-
});
|
|
1915
|
-
const unsub = keepalive(output);
|
|
1916
|
-
return {
|
|
1917
|
-
output,
|
|
1918
|
-
stream: streamTopic,
|
|
1919
|
-
dispose: () => {
|
|
1920
|
-
unsub();
|
|
1921
|
-
streamTopic.destroy();
|
|
1922
|
-
}
|
|
1923
|
-
};
|
|
1924
|
-
}
|
|
1925
|
-
function streamExtractor(streamTopic, extractFn, opts) {
|
|
1926
|
-
return derived(
|
|
1927
|
-
[streamTopic.latest],
|
|
1928
|
-
([chunk]) => {
|
|
1929
|
-
if (chunk == null) return null;
|
|
1930
|
-
return extractFn(chunk.accumulated);
|
|
1931
|
-
},
|
|
1932
|
-
{
|
|
1933
|
-
name: opts?.name ?? "extractor",
|
|
1934
|
-
describeKind: "derived",
|
|
1935
|
-
initial: null,
|
|
1936
|
-
meta: aiMeta("stream_extractor"),
|
|
1937
|
-
...opts?.equals ? { equals: opts.equals } : {}
|
|
1938
|
-
}
|
|
1939
|
-
);
|
|
1940
|
-
}
|
|
1941
|
-
var keywordFlagsEqual = (a, b) => {
|
|
1942
|
-
if (a === b) return true;
|
|
1943
|
-
if (a == null || b == null) return a === b;
|
|
1944
|
-
if (a.length !== b.length) return false;
|
|
1945
|
-
for (let i = 0; i < a.length; i++) {
|
|
1946
|
-
const x = a[i];
|
|
1947
|
-
const y = b[i];
|
|
1948
|
-
if (x.label !== y.label || x.pattern !== y.pattern || x.match !== y.match || x.position !== y.position) {
|
|
1949
|
-
return false;
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
return true;
|
|
1953
|
-
};
|
|
1954
|
-
function keywordFlagExtractor(streamTopic, opts) {
|
|
1955
|
-
const maxPatternLength = opts.maxPatternLength ?? 128;
|
|
1956
|
-
return derived(
|
|
1957
|
-
[streamTopic.latest],
|
|
1958
|
-
([chunk], ctx) => {
|
|
1959
|
-
if (chunk == null) return [];
|
|
1960
|
-
const accumulated = chunk.accumulated;
|
|
1961
|
-
if (!("flags" in ctx.store)) {
|
|
1962
|
-
ctx.store.flags = [];
|
|
1963
|
-
ctx.store.scannedTo = 0;
|
|
1964
|
-
}
|
|
1965
|
-
const flags = ctx.store.flags;
|
|
1966
|
-
const scannedTo = ctx.store.scannedTo;
|
|
1967
|
-
const startOffset = Math.max(0, scannedTo - maxPatternLength);
|
|
1968
|
-
const region = accumulated.slice(startOffset);
|
|
1969
|
-
let added = false;
|
|
1970
|
-
for (const { pattern, label } of opts.patterns) {
|
|
1971
|
-
const re = new RegExp(pattern.source, `${pattern.flags.replace("g", "")}g`);
|
|
1972
|
-
for (const m of region.matchAll(re)) {
|
|
1973
|
-
const pos = startOffset + m.index;
|
|
1974
|
-
if (pos + m[0].length <= scannedTo) continue;
|
|
1975
|
-
flags.push({ label, pattern, match: m[0], position: pos });
|
|
1976
|
-
added = true;
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
ctx.store.scannedTo = accumulated.length;
|
|
1980
|
-
return added ? [...flags] : flags.slice();
|
|
1981
|
-
},
|
|
1982
|
-
{
|
|
1983
|
-
name: opts.name ?? "keyword-flag-extractor",
|
|
1984
|
-
describeKind: "derived",
|
|
1985
|
-
initial: [],
|
|
1986
|
-
meta: aiMeta("keyword_flag_extractor"),
|
|
1987
|
-
equals: keywordFlagsEqual
|
|
1988
|
-
}
|
|
1989
|
-
);
|
|
1990
|
-
}
|
|
1991
|
-
var toolCallsEqual = (a, b) => {
|
|
1992
|
-
if (a === b) return true;
|
|
1993
|
-
if (a == null || b == null) return a === b;
|
|
1994
|
-
if (a.length !== b.length) return false;
|
|
1995
|
-
for (let i = 0; i < a.length; i++) {
|
|
1996
|
-
const x = a[i];
|
|
1997
|
-
const y = b[i];
|
|
1998
|
-
if (x.startIndex !== y.startIndex || x.name !== y.name || x.raw !== y.raw) {
|
|
1999
|
-
return false;
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
return true;
|
|
2003
|
-
};
|
|
2004
|
-
function toolCallExtractor(streamTopic, opts) {
|
|
2005
|
-
return derived(
|
|
2006
|
-
[streamTopic.latest],
|
|
2007
|
-
([chunk], ctx) => {
|
|
2008
|
-
if (chunk == null) return [];
|
|
2009
|
-
const accumulated = chunk.accumulated;
|
|
2010
|
-
if (!("calls" in ctx.store)) {
|
|
2011
|
-
ctx.store.calls = [];
|
|
2012
|
-
ctx.store.scanFrom = 0;
|
|
2013
|
-
}
|
|
2014
|
-
const calls = ctx.store.calls;
|
|
2015
|
-
let i = ctx.store.scanFrom;
|
|
2016
|
-
let added = false;
|
|
2017
|
-
while (i < accumulated.length) {
|
|
2018
|
-
const start = accumulated.indexOf("{", i);
|
|
2019
|
-
if (start === -1) {
|
|
2020
|
-
ctx.store.scanFrom = accumulated.length;
|
|
2021
|
-
break;
|
|
2022
|
-
}
|
|
2023
|
-
let depth = 0;
|
|
2024
|
-
let end = -1;
|
|
2025
|
-
let inString = false;
|
|
2026
|
-
for (let j = start; j < accumulated.length; j++) {
|
|
2027
|
-
const ch = accumulated[j];
|
|
2028
|
-
if (inString) {
|
|
2029
|
-
if (ch === "\\" && j + 1 < accumulated.length) {
|
|
2030
|
-
j++;
|
|
2031
|
-
} else if (ch === '"') {
|
|
2032
|
-
inString = false;
|
|
2033
|
-
}
|
|
2034
|
-
} else if (ch === '"') {
|
|
2035
|
-
inString = true;
|
|
2036
|
-
} else if (ch === "{") {
|
|
2037
|
-
depth++;
|
|
2038
|
-
} else if (ch === "}") {
|
|
2039
|
-
depth--;
|
|
2040
|
-
if (depth === 0) {
|
|
2041
|
-
end = j;
|
|
2042
|
-
break;
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
if (end === -1) {
|
|
2047
|
-
ctx.store.scanFrom = start;
|
|
2048
|
-
break;
|
|
2049
|
-
}
|
|
2050
|
-
const raw = accumulated.slice(start, end + 1);
|
|
2051
|
-
try {
|
|
2052
|
-
const parsed = JSON.parse(raw);
|
|
2053
|
-
if (typeof parsed.name === "string" && parsed.arguments != null && typeof parsed.arguments === "object") {
|
|
2054
|
-
calls.push({
|
|
2055
|
-
name: parsed.name,
|
|
2056
|
-
arguments: parsed.arguments,
|
|
2057
|
-
raw,
|
|
2058
|
-
startIndex: start
|
|
2059
|
-
});
|
|
2060
|
-
added = true;
|
|
2061
|
-
}
|
|
2062
|
-
} catch {
|
|
2063
|
-
}
|
|
2064
|
-
i = end + 1;
|
|
2065
|
-
ctx.store.scanFrom = i;
|
|
2066
|
-
}
|
|
2067
|
-
return added ? [...calls] : calls.slice();
|
|
2068
|
-
},
|
|
2069
|
-
{
|
|
2070
|
-
name: opts?.name ?? "tool-call-extractor",
|
|
2071
|
-
describeKind: "derived",
|
|
2072
|
-
initial: [],
|
|
2073
|
-
meta: aiMeta("tool_call_extractor"),
|
|
2074
|
-
equals: toolCallsEqual
|
|
2075
|
-
}
|
|
2076
|
-
);
|
|
2077
|
-
}
|
|
2078
|
-
var costMeterEqual = (a, b) => {
|
|
2079
|
-
if (a === b) return true;
|
|
2080
|
-
return a.chunkCount === b.chunkCount && a.charCount === b.charCount && a.estimatedTokens === b.estimatedTokens;
|
|
2081
|
-
};
|
|
2082
|
-
function costMeterExtractor(streamTopic, opts) {
|
|
2083
|
-
const charsPerToken = opts?.charsPerToken ?? 4;
|
|
2084
|
-
return derived(
|
|
2085
|
-
[streamTopic.latest],
|
|
2086
|
-
([chunk]) => {
|
|
2087
|
-
if (chunk == null) return { chunkCount: 0, charCount: 0, estimatedTokens: 0 };
|
|
2088
|
-
const c = chunk;
|
|
2089
|
-
const charCount = c.accumulated.length;
|
|
2090
|
-
return {
|
|
2091
|
-
chunkCount: c.index + 1,
|
|
2092
|
-
charCount,
|
|
2093
|
-
estimatedTokens: Math.ceil(charCount / charsPerToken)
|
|
2094
|
-
};
|
|
2095
|
-
},
|
|
2096
|
-
{
|
|
2097
|
-
name: opts?.name ?? "cost-meter",
|
|
2098
|
-
describeKind: "derived",
|
|
2099
|
-
initial: { chunkCount: 0, charCount: 0, estimatedTokens: 0 },
|
|
2100
|
-
meta: aiMeta("cost_meter_extractor"),
|
|
2101
|
-
equals: costMeterEqual
|
|
2102
|
-
}
|
|
2103
|
-
);
|
|
2104
|
-
}
|
|
2105
|
-
function redactor(streamTopic, patterns, replaceFn, opts) {
|
|
2106
|
-
const replace = replaceFn ?? (() => "[REDACTED]");
|
|
2107
|
-
function sanitize(text) {
|
|
2108
|
-
let result = text;
|
|
2109
|
-
for (const pat of patterns) {
|
|
2110
|
-
const global = pat.global ? pat : new RegExp(pat.source, `${pat.flags}g`);
|
|
2111
|
-
result = result.replace(global, (m) => replace(m, pat));
|
|
2112
|
-
}
|
|
2113
|
-
return result;
|
|
2114
|
-
}
|
|
2115
|
-
return derived(
|
|
2116
|
-
[streamTopic.latest],
|
|
2117
|
-
([chunk]) => {
|
|
2118
|
-
if (chunk == null) {
|
|
2119
|
-
return { source: "", token: "", accumulated: "", index: -1 };
|
|
2120
|
-
}
|
|
2121
|
-
const c = chunk;
|
|
2122
|
-
const sanitizedAccumulated = sanitize(c.accumulated);
|
|
2123
|
-
const sanitizedToken = sanitize(c.token);
|
|
2124
|
-
return {
|
|
2125
|
-
source: c.source,
|
|
2126
|
-
token: sanitizedToken,
|
|
2127
|
-
accumulated: sanitizedAccumulated,
|
|
2128
|
-
index: c.index
|
|
2129
|
-
};
|
|
2130
|
-
},
|
|
2131
|
-
{ name: opts?.name ?? "redactor" }
|
|
2132
|
-
);
|
|
2133
|
-
}
|
|
2134
|
-
function contentGate(streamTopic, classifier, threshold, opts) {
|
|
2135
|
-
const hardThreshold = threshold * (opts?.hardMultiplier ?? 1.5);
|
|
2136
|
-
const isNodeClassifier = typeof classifier !== "function";
|
|
2137
|
-
const deps = [streamTopic.latest];
|
|
2138
|
-
if (isNodeClassifier) deps.push(classifier);
|
|
2139
|
-
return derived(
|
|
2140
|
-
deps,
|
|
2141
|
-
(values) => {
|
|
2142
|
-
const chunk = values[0];
|
|
2143
|
-
if (chunk == null) return "allow";
|
|
2144
|
-
const score = isNodeClassifier ? values[1] ?? 0 : classifier(chunk.accumulated);
|
|
2145
|
-
if (score >= hardThreshold) return "block";
|
|
2146
|
-
if (score >= threshold) return "review";
|
|
2147
|
-
return "allow";
|
|
2148
|
-
},
|
|
2149
|
-
{ name: opts?.name ?? "content-gate", initial: "allow" }
|
|
2150
|
-
);
|
|
2151
|
-
}
|
|
2152
|
-
function gatedStream(graph, name, adapter, deps, prompt, opts) {
|
|
2153
|
-
const cancelSignal = state(0, { name: `${name}/cancel` });
|
|
2154
|
-
let cancelCounter = 0;
|
|
2155
|
-
const allDeps = [...deps, cancelSignal];
|
|
2156
|
-
const sourceName = opts?.name ?? name;
|
|
2157
|
-
const format = opts?.format ?? "text";
|
|
2158
|
-
const streamTopic = topic(`${sourceName}/stream`);
|
|
2159
|
-
const messagesNode = derived(allDeps, (values) => {
|
|
2160
|
-
const depValues = values.slice(0, -1);
|
|
2161
|
-
if (depValues.some((v) => v == null)) return [];
|
|
2162
|
-
const text = typeof prompt === "string" ? prompt : prompt(...depValues);
|
|
2163
|
-
if (!text) return [];
|
|
2164
|
-
const msgs = [];
|
|
2165
|
-
if (opts?.systemPrompt) msgs.push({ role: "system", content: opts.systemPrompt });
|
|
2166
|
-
msgs.push({ role: "user", content: text });
|
|
2167
|
-
return msgs;
|
|
2168
|
-
});
|
|
2169
|
-
const output = switchMap(messagesNode, (msgs) => {
|
|
2170
|
-
const chatMsgs = msgs;
|
|
2171
|
-
if (!chatMsgs || chatMsgs.length === 0) {
|
|
2172
|
-
return state(null);
|
|
2173
|
-
}
|
|
2174
|
-
const ac = new AbortController();
|
|
2175
|
-
async function* pumpAndCollect() {
|
|
2176
|
-
let accumulated = "";
|
|
2177
|
-
let index = 0;
|
|
2178
|
-
try {
|
|
2179
|
-
for await (const token of adapter.stream(chatMsgs, {
|
|
2180
|
-
model: opts?.model,
|
|
2181
|
-
temperature: opts?.temperature,
|
|
2182
|
-
maxTokens: opts?.maxTokens,
|
|
2183
|
-
systemPrompt: opts?.systemPrompt,
|
|
2184
|
-
signal: ac.signal
|
|
2185
|
-
})) {
|
|
2186
|
-
accumulated += token;
|
|
2187
|
-
streamTopic.publish({
|
|
2188
|
-
source: sourceName,
|
|
2189
|
-
token,
|
|
2190
|
-
accumulated,
|
|
2191
|
-
index: index++
|
|
2192
|
-
});
|
|
2193
|
-
}
|
|
2194
|
-
let result;
|
|
2195
|
-
if (format === "json") {
|
|
2196
|
-
try {
|
|
2197
|
-
result = JSON.parse(stripFences(accumulated));
|
|
2198
|
-
} catch {
|
|
2199
|
-
result = null;
|
|
2200
|
-
}
|
|
2201
|
-
} else {
|
|
2202
|
-
result = accumulated;
|
|
2203
|
-
}
|
|
2204
|
-
yield result;
|
|
2205
|
-
} finally {
|
|
2206
|
-
ac.abort();
|
|
2207
|
-
}
|
|
2208
|
-
}
|
|
2209
|
-
return fromAny(pumpAndCollect());
|
|
2210
|
-
});
|
|
2211
|
-
const unsub = keepalive(output);
|
|
2212
|
-
const nonNullOutput = derived(
|
|
2213
|
-
[output],
|
|
2214
|
-
([v]) => {
|
|
2215
|
-
if (v == null) return void 0;
|
|
2216
|
-
return v;
|
|
2217
|
-
},
|
|
2218
|
-
{
|
|
2219
|
-
name: `${name}/filter`
|
|
2220
|
-
}
|
|
2221
|
-
);
|
|
2222
|
-
graph.add(`${name}/raw`, nonNullOutput);
|
|
2223
|
-
const gateCtrl = gate(graph, `${name}/gate`, `${name}/raw`, opts?.gate);
|
|
2224
|
-
const originalReject = gateCtrl.reject.bind(gateCtrl);
|
|
2225
|
-
const gateWithAbort = {
|
|
2226
|
-
...gateCtrl,
|
|
2227
|
-
reject(count = 1) {
|
|
2228
|
-
originalReject(count);
|
|
2229
|
-
cancelSignal.down([[DATA, ++cancelCounter]]);
|
|
2230
|
-
}
|
|
2231
|
-
};
|
|
2232
|
-
return {
|
|
2233
|
-
output: gateCtrl.node,
|
|
2234
|
-
stream: streamTopic,
|
|
2235
|
-
gate: gateWithAbort,
|
|
2236
|
-
dispose: () => {
|
|
2237
|
-
unsub();
|
|
2238
|
-
streamTopic.destroy();
|
|
2239
|
-
}
|
|
2240
|
-
};
|
|
2241
|
-
}
|
|
2242
|
-
function extractContent(resp) {
|
|
2243
|
-
if (resp != null && typeof resp === "object" && "content" in resp) {
|
|
2244
|
-
return String(resp.content);
|
|
2245
|
-
}
|
|
2246
|
-
if (typeof resp === "string") return resp;
|
|
2247
|
-
return String(resp);
|
|
2248
|
-
}
|
|
2249
|
-
function promptNode(adapter, deps, prompt, opts) {
|
|
2250
|
-
const format = opts?.format ?? "text";
|
|
2251
|
-
const retries = opts?.retries ?? 0;
|
|
2252
|
-
const useCache = opts?.cache ?? false;
|
|
2253
|
-
const cache = useCache ? /* @__PURE__ */ new Map() : null;
|
|
2254
|
-
const messagesNode = derived(
|
|
2255
|
-
deps,
|
|
2256
|
-
(values) => {
|
|
2257
|
-
if (values.some((v) => v == null)) return [];
|
|
2258
|
-
const text = typeof prompt === "string" ? prompt : prompt(...values);
|
|
2259
|
-
if (!text) return [];
|
|
2260
|
-
const msgs = [];
|
|
2261
|
-
if (opts?.systemPrompt) msgs.push({ role: "system", content: opts.systemPrompt });
|
|
2262
|
-
msgs.push({ role: "user", content: text });
|
|
2263
|
-
return msgs;
|
|
2264
|
-
},
|
|
2265
|
-
{
|
|
2266
|
-
name: opts?.name ? `${opts.name}::messages` : "prompt_node::messages",
|
|
2267
|
-
meta: aiMeta("prompt_node"),
|
|
2268
|
-
initial: []
|
|
2269
|
-
}
|
|
2270
|
-
);
|
|
2271
|
-
const result = switchMap(messagesNode, (msgs) => {
|
|
2272
|
-
if (!msgs || msgs.length === 0) {
|
|
2273
|
-
return state(null);
|
|
2274
|
-
}
|
|
2275
|
-
const cacheKey = useCache ? JSON.stringify(msgs.map((m) => [m.role, m.content])) : "";
|
|
2276
|
-
if (cache?.has(cacheKey)) {
|
|
2277
|
-
return state(cache.get(cacheKey));
|
|
2278
|
-
}
|
|
2279
|
-
async function attempt(remaining) {
|
|
2280
|
-
try {
|
|
2281
|
-
const resp = await new Promise((resolve, reject) => {
|
|
2282
|
-
const input = adapter.invoke(msgs, {
|
|
2283
|
-
model: opts?.model,
|
|
2284
|
-
temperature: opts?.temperature,
|
|
2285
|
-
maxTokens: opts?.maxTokens,
|
|
2286
|
-
systemPrompt: opts?.systemPrompt
|
|
2287
|
-
});
|
|
2288
|
-
if (input && typeof input.then === "function") {
|
|
2289
|
-
input.then(resolve, reject);
|
|
2290
|
-
} else if (input && typeof input.subscribe === "function") {
|
|
2291
|
-
resolve(input.cache);
|
|
2292
|
-
} else {
|
|
2293
|
-
resolve(input);
|
|
2294
|
-
}
|
|
2295
|
-
});
|
|
2296
|
-
const content = extractContent(resp);
|
|
2297
|
-
let parsed;
|
|
2298
|
-
if (format === "json") {
|
|
2299
|
-
parsed = JSON.parse(stripFences(content));
|
|
2300
|
-
} else {
|
|
2301
|
-
parsed = content;
|
|
2302
|
-
}
|
|
2303
|
-
cache?.set(cacheKey, parsed);
|
|
2304
|
-
return parsed;
|
|
2305
|
-
} catch (err) {
|
|
2306
|
-
if (remaining > 0) return attempt(remaining - 1);
|
|
2307
|
-
throw err;
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
return attempt(retries);
|
|
2311
|
-
});
|
|
2312
|
-
return result;
|
|
2313
|
-
}
|
|
2314
|
-
var ChatStreamGraph = class extends Graph {
|
|
2315
|
-
_log;
|
|
2316
|
-
messages;
|
|
2317
|
-
latest;
|
|
2318
|
-
messageCount;
|
|
2319
|
-
constructor(name, opts = {}) {
|
|
2320
|
-
super(name, opts.graph);
|
|
2321
|
-
this._log = reactiveLog([], {
|
|
2322
|
-
name: "messages",
|
|
2323
|
-
maxSize: opts.maxMessages
|
|
2324
|
-
});
|
|
2325
|
-
this.messages = this._log.entries;
|
|
2326
|
-
this.add("messages", this.messages);
|
|
2327
|
-
this.latest = derived(
|
|
2328
|
-
[this.messages],
|
|
2329
|
-
([snapshot]) => {
|
|
2330
|
-
const entries = snapshot;
|
|
2331
|
-
return entries.length === 0 ? null : entries[entries.length - 1];
|
|
2332
|
-
},
|
|
2333
|
-
{
|
|
2334
|
-
name: "latest",
|
|
2335
|
-
describeKind: "derived",
|
|
2336
|
-
meta: aiMeta("chat_latest")
|
|
2337
|
-
}
|
|
2338
|
-
);
|
|
2339
|
-
this.add("latest", this.latest);
|
|
2340
|
-
this.addDisposer(keepalive(this.latest));
|
|
2341
|
-
this.messageCount = derived(
|
|
2342
|
-
[this.messages],
|
|
2343
|
-
([snapshot]) => snapshot.length,
|
|
2344
|
-
{
|
|
2345
|
-
name: "messageCount",
|
|
2346
|
-
describeKind: "derived",
|
|
2347
|
-
meta: aiMeta("chat_message_count"),
|
|
2348
|
-
initial: 0
|
|
2349
|
-
}
|
|
2350
|
-
);
|
|
2351
|
-
this.add("messageCount", this.messageCount);
|
|
2352
|
-
this.addDisposer(keepalive(this.messageCount));
|
|
2353
|
-
}
|
|
2354
|
-
append(role, content, extra) {
|
|
2355
|
-
this._log.append({ role, content, ...extra });
|
|
2356
|
-
}
|
|
2357
|
-
appendToolResult(callId, content) {
|
|
2358
|
-
this._log.append({ role: "tool", content, toolCallId: callId });
|
|
2359
|
-
}
|
|
2360
|
-
clear() {
|
|
2361
|
-
this._log.clear();
|
|
2362
|
-
}
|
|
2363
|
-
allMessages() {
|
|
2364
|
-
return this.messages.cache;
|
|
2365
|
-
}
|
|
2366
|
-
};
|
|
2367
|
-
function chatStream(name, opts) {
|
|
2368
|
-
return new ChatStreamGraph(name, opts);
|
|
2369
|
-
}
|
|
2370
|
-
var ToolRegistryGraph = class extends Graph {
|
|
2371
|
-
definitions;
|
|
2372
|
-
schemas;
|
|
2373
|
-
constructor(name, opts = {}) {
|
|
2374
|
-
super(name, opts.graph);
|
|
2375
|
-
this.definitions = state(/* @__PURE__ */ new Map(), {
|
|
2376
|
-
name: "definitions",
|
|
2377
|
-
describeKind: "state",
|
|
2378
|
-
meta: aiMeta("tool_definitions")
|
|
2379
|
-
});
|
|
2380
|
-
this.add("definitions", this.definitions);
|
|
2381
|
-
this.schemas = derived(
|
|
2382
|
-
[this.definitions],
|
|
2383
|
-
([defs]) => [...(defs ?? /* @__PURE__ */ new Map()).values()],
|
|
2384
|
-
{
|
|
2385
|
-
name: "schemas",
|
|
2386
|
-
describeKind: "derived",
|
|
2387
|
-
meta: aiMeta("tool_schemas"),
|
|
2388
|
-
initial: []
|
|
2389
|
-
}
|
|
2390
|
-
);
|
|
2391
|
-
this.add("schemas", this.schemas);
|
|
2392
|
-
this.addDisposer(keepalive(this.schemas));
|
|
2393
|
-
}
|
|
2394
|
-
register(tool) {
|
|
2395
|
-
const current = this.definitions.cache;
|
|
2396
|
-
const next = new Map(current);
|
|
2397
|
-
next.set(tool.name, tool);
|
|
2398
|
-
this.definitions.down([[DATA, next]]);
|
|
2399
|
-
}
|
|
2400
|
-
unregister(name) {
|
|
2401
|
-
const current = this.definitions.cache;
|
|
2402
|
-
if (!current.has(name)) return;
|
|
2403
|
-
const next = new Map(current);
|
|
2404
|
-
next.delete(name);
|
|
2405
|
-
this.definitions.down([[DATA, next]]);
|
|
2406
|
-
}
|
|
2407
|
-
async execute(name, args) {
|
|
2408
|
-
const defs = this.definitions.cache;
|
|
2409
|
-
const tool = defs.get(name);
|
|
2410
|
-
if (!tool) throw new Error(`toolRegistry: unknown tool "${name}"`);
|
|
2411
|
-
const raw = tool.handler(args);
|
|
2412
|
-
return resolveToolHandlerResult(raw);
|
|
2413
|
-
}
|
|
2414
|
-
getDefinition(name) {
|
|
2415
|
-
return this.definitions.cache?.get(name);
|
|
2416
|
-
}
|
|
2417
|
-
};
|
|
2418
|
-
function toolRegistry(name, opts) {
|
|
2419
|
-
return new ToolRegistryGraph(name, opts);
|
|
2420
|
-
}
|
|
2421
|
-
function systemPromptBuilder(sections, opts) {
|
|
2422
|
-
const separator = opts?.separator ?? "\n\n";
|
|
2423
|
-
const sectionNodes = sections.map((s) => typeof s === "string" ? state(s) : fromAny(s));
|
|
2424
|
-
const prompt = derived(
|
|
2425
|
-
sectionNodes,
|
|
2426
|
-
(values) => values.filter((v) => v != null && v !== "").join(separator),
|
|
2427
|
-
{
|
|
2428
|
-
name: opts?.name ?? "systemPrompt",
|
|
2429
|
-
describeKind: "derived",
|
|
2430
|
-
meta: aiMeta("system_prompt"),
|
|
2431
|
-
initial: ""
|
|
2432
|
-
}
|
|
2433
|
-
);
|
|
2434
|
-
const unsub = keepalive(prompt);
|
|
2435
|
-
return Object.assign(prompt, { dispose: unsub });
|
|
2436
|
-
}
|
|
2437
|
-
function llmExtractor(systemPrompt, opts) {
|
|
2438
|
-
return (raw, existing) => {
|
|
2439
|
-
const existingKeys = [...existing.keys()].slice(0, 100);
|
|
2440
|
-
const messages = [
|
|
2441
|
-
{ role: "system", content: systemPrompt },
|
|
2442
|
-
{
|
|
2443
|
-
role: "user",
|
|
2444
|
-
content: JSON.stringify({
|
|
2445
|
-
input: raw,
|
|
2446
|
-
existingKeys
|
|
2447
|
-
})
|
|
2448
|
-
}
|
|
2449
|
-
];
|
|
2450
|
-
return producer((actions) => {
|
|
2451
|
-
let active = true;
|
|
2452
|
-
const result = opts.adapter.invoke(messages, {
|
|
2453
|
-
model: opts.model,
|
|
2454
|
-
temperature: opts.temperature ?? 0,
|
|
2455
|
-
maxTokens: opts.maxTokens
|
|
2456
|
-
});
|
|
2457
|
-
const resolved = fromAny(result);
|
|
2458
|
-
const unsub = resolved.subscribe((msgs) => {
|
|
2459
|
-
if (!active) return;
|
|
2460
|
-
let done = false;
|
|
2461
|
-
for (const msg of msgs) {
|
|
2462
|
-
if (done) break;
|
|
2463
|
-
if (msg[0] === DATA) {
|
|
2464
|
-
const response = msg[1];
|
|
2465
|
-
try {
|
|
2466
|
-
const parsed = JSON.parse(response.content);
|
|
2467
|
-
actions.emit(parsed);
|
|
2468
|
-
actions.down([[COMPLETE]]);
|
|
2469
|
-
} catch {
|
|
2470
|
-
actions.down([
|
|
2471
|
-
[ERROR, new Error("llmExtractor: failed to parse LLM response as JSON")]
|
|
2472
|
-
]);
|
|
2473
|
-
}
|
|
2474
|
-
done = true;
|
|
2475
|
-
} else if (msg[0] === ERROR) {
|
|
2476
|
-
actions.down([[ERROR, msg[1]]]);
|
|
2477
|
-
done = true;
|
|
2478
|
-
} else if (msg[0] === COMPLETE) {
|
|
2479
|
-
actions.down([[COMPLETE]]);
|
|
2480
|
-
done = true;
|
|
2481
|
-
} else {
|
|
2482
|
-
actions.down([[msg[0], msg[1]]]);
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
|
-
});
|
|
2486
|
-
return () => {
|
|
2487
|
-
unsub();
|
|
2488
|
-
active = false;
|
|
2489
|
-
};
|
|
2490
|
-
});
|
|
2491
|
-
};
|
|
2492
|
-
}
|
|
2493
|
-
function llmConsolidator(systemPrompt, opts) {
|
|
2494
|
-
return (entries) => {
|
|
2495
|
-
const entriesArray = [...entries.entries()].map(([key, value]) => ({ key, value }));
|
|
2496
|
-
const messages = [
|
|
2497
|
-
{ role: "system", content: systemPrompt },
|
|
2498
|
-
{ role: "user", content: JSON.stringify({ memories: entriesArray }) }
|
|
2499
|
-
];
|
|
2500
|
-
return producer((actions) => {
|
|
2501
|
-
let active = true;
|
|
2502
|
-
const result = opts.adapter.invoke(messages, {
|
|
2503
|
-
model: opts.model,
|
|
2504
|
-
temperature: opts.temperature ?? 0,
|
|
2505
|
-
maxTokens: opts.maxTokens
|
|
2506
|
-
});
|
|
2507
|
-
const resolved = fromAny(result);
|
|
2508
|
-
const unsub = resolved.subscribe((msgs) => {
|
|
2509
|
-
if (!active) return;
|
|
2510
|
-
let done = false;
|
|
2511
|
-
for (const msg of msgs) {
|
|
2512
|
-
if (done) break;
|
|
2513
|
-
if (msg[0] === DATA) {
|
|
2514
|
-
const response = msg[1];
|
|
2515
|
-
try {
|
|
2516
|
-
const parsed = JSON.parse(response.content);
|
|
2517
|
-
actions.emit(parsed);
|
|
2518
|
-
actions.down([[COMPLETE]]);
|
|
2519
|
-
} catch {
|
|
2520
|
-
actions.down([
|
|
2521
|
-
[ERROR, new Error("llmConsolidator: failed to parse LLM response as JSON")]
|
|
2522
|
-
]);
|
|
2523
|
-
}
|
|
2524
|
-
done = true;
|
|
2525
|
-
} else if (msg[0] === ERROR) {
|
|
2526
|
-
actions.down([[ERROR, msg[1]]]);
|
|
2527
|
-
done = true;
|
|
2528
|
-
} else if (msg[0] === COMPLETE) {
|
|
2529
|
-
actions.down([[COMPLETE]]);
|
|
2530
|
-
done = true;
|
|
2531
|
-
} else {
|
|
2532
|
-
actions.down([[msg[0], msg[1]]]);
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
});
|
|
2536
|
-
return () => {
|
|
2537
|
-
unsub();
|
|
2538
|
-
active = false;
|
|
2539
|
-
};
|
|
2540
|
-
});
|
|
2541
|
-
};
|
|
2542
|
-
}
|
|
2543
|
-
function defaultAdmissionScorer(_raw) {
|
|
2544
|
-
return { persistence: 0.5, structure: 0.5, personalValue: 0.5 };
|
|
2545
|
-
}
|
|
2546
|
-
function admissionFilter3D(opts = {}) {
|
|
2547
|
-
const scoreFn = opts.scoreFn ?? defaultAdmissionScorer;
|
|
2548
|
-
const pThresh = opts.persistenceThreshold ?? 0.3;
|
|
2549
|
-
const pvThresh = opts.personalValueThreshold ?? 0.3;
|
|
2550
|
-
const reqStructured = opts.requireStructured ?? false;
|
|
2551
|
-
return (raw) => {
|
|
2552
|
-
const scores = scoreFn(raw);
|
|
2553
|
-
if (scores.persistence < pThresh) return false;
|
|
2554
|
-
if (scores.personalValue < pvThresh) return false;
|
|
2555
|
-
if (reqStructured && scores.structure <= 0) return false;
|
|
2556
|
-
return true;
|
|
2557
|
-
};
|
|
2558
|
-
}
|
|
2559
|
-
var DEFAULT_DECAY_RATE = Math.LN2 / (7 * 86400);
|
|
2560
|
-
function extractStoreMap(snapshot) {
|
|
2561
|
-
if (snapshot instanceof Map) return snapshot;
|
|
2562
|
-
return /* @__PURE__ */ new Map();
|
|
2563
|
-
}
|
|
2564
|
-
function agentMemory(name, source, opts) {
|
|
2565
|
-
const graph = new Graph(name, opts.graph);
|
|
2566
|
-
const keepaliveSubs = [];
|
|
2567
|
-
let rawExtractFn;
|
|
2568
|
-
if (opts.extractFn) {
|
|
2569
|
-
rawExtractFn = opts.extractFn;
|
|
2570
|
-
} else if (opts.adapter && opts.extractPrompt) {
|
|
2571
|
-
rawExtractFn = llmExtractor(opts.extractPrompt, { adapter: opts.adapter });
|
|
2572
|
-
} else {
|
|
2573
|
-
throw new Error("agentMemory: provide either extractFn or adapter + extractPrompt");
|
|
2574
|
-
}
|
|
2575
|
-
const extractFn = (raw, existing) => {
|
|
2576
|
-
if (raw == null) return { upsert: [] };
|
|
2577
|
-
return rawExtractFn(raw, existing);
|
|
2578
|
-
};
|
|
2579
|
-
let filteredSource = source;
|
|
2580
|
-
if (opts.admissionFilter) {
|
|
2581
|
-
const srcNode = fromAny(source);
|
|
2582
|
-
const filter2 = opts.admissionFilter;
|
|
2583
|
-
filteredSource = derived(
|
|
2584
|
-
[srcNode],
|
|
2585
|
-
([raw]) => {
|
|
2586
|
-
if (filter2(raw)) return raw;
|
|
2587
|
-
return void 0;
|
|
2588
|
-
},
|
|
2589
|
-
{ name: "admissionFilter", describeKind: "derived" }
|
|
2590
|
-
);
|
|
2591
|
-
}
|
|
2592
|
-
let consolidateFn;
|
|
2593
|
-
if (opts.consolidateFn) {
|
|
2594
|
-
consolidateFn = opts.consolidateFn;
|
|
2595
|
-
} else if (opts.adapter && opts.consolidatePrompt) {
|
|
2596
|
-
consolidateFn = llmConsolidator(opts.consolidatePrompt, { adapter: opts.adapter });
|
|
2597
|
-
}
|
|
2598
|
-
let consolidateTrigger = opts.consolidateTrigger;
|
|
2599
|
-
if (!consolidateTrigger && consolidateFn && opts.reflection?.enabled !== false) {
|
|
2600
|
-
const interval2 = opts.reflection?.interval ?? 3e5;
|
|
2601
|
-
consolidateTrigger = fromTimer(interval2, { period: interval2 });
|
|
2602
|
-
}
|
|
2603
|
-
const distillOpts = {
|
|
2604
|
-
score: opts.score,
|
|
2605
|
-
cost: opts.cost,
|
|
2606
|
-
budget: opts.budget ?? 2e3,
|
|
2607
|
-
context: opts.context,
|
|
2608
|
-
consolidate: consolidateFn,
|
|
2609
|
-
consolidateTrigger
|
|
2610
|
-
};
|
|
2611
|
-
const distillBundle = distill(filteredSource, extractFn, distillOpts);
|
|
2612
|
-
graph.add("store", distillBundle.store.entries);
|
|
2613
|
-
graph.add("compact", distillBundle.compact);
|
|
2614
|
-
graph.add("size", distillBundle.size);
|
|
2615
|
-
let vectors = null;
|
|
2616
|
-
if (opts.vectorDimensions && opts.vectorDimensions > 0 && opts.embedFn) {
|
|
2617
|
-
vectors = vectorIndex({ dimension: opts.vectorDimensions });
|
|
2618
|
-
graph.add("vectorIndex", vectors.entries);
|
|
2619
|
-
}
|
|
2620
|
-
let kg = null;
|
|
2621
|
-
if (opts.enableKnowledgeGraph) {
|
|
2622
|
-
kg = knowledgeGraph(`${name}-kg`);
|
|
2623
|
-
graph.mount("kg", kg);
|
|
2624
|
-
}
|
|
2625
|
-
let memoryTiersBundle = null;
|
|
2626
|
-
if (opts.tiers) {
|
|
2627
|
-
const tiersOpts = opts.tiers;
|
|
2628
|
-
const decayRate = tiersOpts.decayRate ?? DEFAULT_DECAY_RATE;
|
|
2629
|
-
const maxActive = tiersOpts.maxActive ?? 1e3;
|
|
2630
|
-
const archiveThreshold = tiersOpts.archiveThreshold ?? 0.1;
|
|
2631
|
-
const permanentFilter = tiersOpts.permanentFilter ?? (() => false);
|
|
2632
|
-
const permanent = lightCollection({ name: "permanent" });
|
|
2633
|
-
graph.add("permanent", permanent.entries);
|
|
2634
|
-
const permanentKeys = /* @__PURE__ */ new Set();
|
|
2635
|
-
const tierOf = (key) => {
|
|
2636
|
-
if (permanentKeys.has(key)) return "permanent";
|
|
2637
|
-
const storeMap = extractStoreMap(distillBundle.store.entries.cache);
|
|
2638
|
-
if (storeMap.has(key)) return "active";
|
|
2639
|
-
return "archived";
|
|
2640
|
-
};
|
|
2641
|
-
const markPermanent = (key, value) => {
|
|
2642
|
-
permanentKeys.add(key);
|
|
2643
|
-
permanent.upsert(key, value);
|
|
2644
|
-
};
|
|
2645
|
-
const entryCreatedAtNs = /* @__PURE__ */ new Map();
|
|
2646
|
-
const storeNode = distillBundle.store.entries;
|
|
2647
|
-
const contextNode = opts.context ? fromAny(opts.context) : state(null);
|
|
2648
|
-
const tierClassifier = effect([storeNode, contextNode], ([snapshot, ctx]) => {
|
|
2649
|
-
const storeMap = extractStoreMap(snapshot);
|
|
2650
|
-
const nowNs = monotonicNs();
|
|
2651
|
-
const toArchive = [];
|
|
2652
|
-
const toPermanent = [];
|
|
2653
|
-
for (const [key, mem] of storeMap) {
|
|
2654
|
-
if (!entryCreatedAtNs.has(key)) {
|
|
2655
|
-
entryCreatedAtNs.set(key, nowNs);
|
|
2656
|
-
}
|
|
2657
|
-
if (permanentFilter(key, mem)) {
|
|
2658
|
-
toPermanent.push({ key, value: mem });
|
|
2659
|
-
continue;
|
|
2660
|
-
}
|
|
2661
|
-
const baseScore = opts.score(mem, ctx);
|
|
2662
|
-
const createdNs = entryCreatedAtNs.get(key) ?? nowNs;
|
|
2663
|
-
const ageSeconds = Number(nowNs - createdNs) / 1e9;
|
|
2664
|
-
const decayed = decay(baseScore, ageSeconds, decayRate);
|
|
2665
|
-
if (decayed < archiveThreshold) {
|
|
2666
|
-
toArchive.push(key);
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
for (const key of entryCreatedAtNs.keys()) {
|
|
2670
|
-
if (!storeMap.has(key)) entryCreatedAtNs.delete(key);
|
|
2671
|
-
}
|
|
2672
|
-
for (const { key, value } of toPermanent) {
|
|
2673
|
-
if (!permanentKeys.has(key)) {
|
|
2674
|
-
markPermanent(key, value);
|
|
2675
|
-
}
|
|
2676
|
-
}
|
|
2677
|
-
const activeCount = storeMap.size - permanentKeys.size;
|
|
2678
|
-
if (activeCount > maxActive) {
|
|
2679
|
-
const scored = [...storeMap.entries()].filter(([k]) => !permanentKeys.has(k)).map(([k, m]) => ({ key: k, score: opts.score(m, ctx) })).sort((a, b) => a.score - b.score);
|
|
2680
|
-
const excess = activeCount - maxActive;
|
|
2681
|
-
for (let i = 0; i < excess && i < scored.length; i++) {
|
|
2682
|
-
const sk = scored[i].key;
|
|
2683
|
-
if (!toArchive.includes(sk)) toArchive.push(sk);
|
|
2684
|
-
}
|
|
2685
|
-
}
|
|
2686
|
-
if (toArchive.length > 0) {
|
|
2687
|
-
batch(() => {
|
|
2688
|
-
for (const key of toArchive) {
|
|
2689
|
-
distillBundle.store.delete(key);
|
|
2690
|
-
}
|
|
2691
|
-
});
|
|
2692
|
-
}
|
|
2693
|
-
});
|
|
2694
|
-
keepaliveSubs.push(tierClassifier.subscribe(() => void 0));
|
|
2695
|
-
let archiveHandle = null;
|
|
2696
|
-
if (tiersOpts.archiveTier) {
|
|
2697
|
-
archiveHandle = graph.attachStorage(
|
|
2698
|
-
[tiersOpts.archiveTier],
|
|
2699
|
-
tiersOpts.archiveStorageOptions ?? {}
|
|
2700
|
-
);
|
|
2701
|
-
}
|
|
2702
|
-
memoryTiersBundle = {
|
|
2703
|
-
permanent,
|
|
2704
|
-
activeEntries: storeNode,
|
|
2705
|
-
archiveHandle,
|
|
2706
|
-
tierOf,
|
|
2707
|
-
markPermanent
|
|
2708
|
-
};
|
|
2709
|
-
}
|
|
2710
|
-
if (vectors || kg) {
|
|
2711
|
-
const embedFn = opts.embedFn;
|
|
2712
|
-
const entityFn = opts.entityFn;
|
|
2713
|
-
const storeNode = distillBundle.store.entries;
|
|
2714
|
-
const indexer = effect([storeNode], ([snapshot]) => {
|
|
2715
|
-
const storeMap = extractStoreMap(snapshot);
|
|
2716
|
-
for (const [key, mem] of storeMap) {
|
|
2717
|
-
if (vectors && embedFn) {
|
|
2718
|
-
const vec = embedFn(mem);
|
|
2719
|
-
if (vec) vectors.upsert(key, vec, mem);
|
|
2720
|
-
}
|
|
2721
|
-
if (kg && entityFn) {
|
|
2722
|
-
const extracted = entityFn(key, mem);
|
|
2723
|
-
if (extracted) {
|
|
2724
|
-
for (const ent of extracted.entities ?? []) {
|
|
2725
|
-
kg.upsertEntity(ent.id, ent.value);
|
|
2726
|
-
}
|
|
2727
|
-
for (const rel of extracted.relations ?? []) {
|
|
2728
|
-
kg.link(rel.from, rel.to, rel.relation, rel.weight);
|
|
2729
|
-
}
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
});
|
|
2734
|
-
keepaliveSubs.push(indexer.subscribe(() => void 0));
|
|
2735
|
-
}
|
|
2736
|
-
let retrievalNode = null;
|
|
2737
|
-
let retrievalTraceNode = null;
|
|
2738
|
-
let retrieveFn = null;
|
|
2739
|
-
if (vectors || kg) {
|
|
2740
|
-
const topK = opts.retrieval?.topK ?? 20;
|
|
2741
|
-
const graphDepth = opts.retrieval?.graphDepth ?? 1;
|
|
2742
|
-
const budget = opts.budget ?? 2e3;
|
|
2743
|
-
const costFn = opts.cost;
|
|
2744
|
-
const scoreFn = opts.score;
|
|
2745
|
-
const contextNode = opts.context ? fromAny(opts.context) : state(null);
|
|
2746
|
-
const retrievalOutput = state([], {
|
|
2747
|
-
name: "retrieval",
|
|
2748
|
-
describeKind: "state",
|
|
2749
|
-
meta: aiMeta("retrieval_pipeline")
|
|
2750
|
-
});
|
|
2751
|
-
graph.add("retrieval", retrievalOutput);
|
|
2752
|
-
retrievalNode = retrievalOutput;
|
|
2753
|
-
const traceState = state(null, {
|
|
2754
|
-
name: "retrievalTrace",
|
|
2755
|
-
describeKind: "state",
|
|
2756
|
-
meta: aiMeta("retrieval_trace")
|
|
2757
|
-
});
|
|
2758
|
-
graph.add("retrievalTrace", traceState);
|
|
2759
|
-
retrievalTraceNode = traceState;
|
|
2760
|
-
retrieveFn = (query) => {
|
|
2761
|
-
const storeMap = extractStoreMap(distillBundle.store.entries.cache);
|
|
2762
|
-
const ctx = contextNode.cache;
|
|
2763
|
-
const candidateMap = /* @__PURE__ */ new Map();
|
|
2764
|
-
let vectorCandidates = [];
|
|
2765
|
-
if (vectors && query.vector) {
|
|
2766
|
-
vectorCandidates = vectors.search(query.vector, topK);
|
|
2767
|
-
for (const vc of vectorCandidates) {
|
|
2768
|
-
const mem = storeMap.get(vc.id);
|
|
2769
|
-
if (mem) {
|
|
2770
|
-
candidateMap.set(vc.id, { value: mem, sources: /* @__PURE__ */ new Set(["vector"]) });
|
|
2771
|
-
}
|
|
2772
|
-
}
|
|
2773
|
-
}
|
|
2774
|
-
const graphExpanded = [];
|
|
2775
|
-
if (kg) {
|
|
2776
|
-
const seedIds = [...query.entityIds ?? [], ...[...candidateMap.keys()]];
|
|
2777
|
-
const visited = /* @__PURE__ */ new Set();
|
|
2778
|
-
let frontier = seedIds;
|
|
2779
|
-
for (let depth = 0; depth < graphDepth; depth++) {
|
|
2780
|
-
const nextFrontier = [];
|
|
2781
|
-
for (const id of frontier) {
|
|
2782
|
-
if (visited.has(id)) continue;
|
|
2783
|
-
visited.add(id);
|
|
2784
|
-
const related = kg.related(id);
|
|
2785
|
-
for (const edge of related) {
|
|
2786
|
-
const targetId = edge.to;
|
|
2787
|
-
if (!visited.has(targetId)) {
|
|
2788
|
-
nextFrontier.push(targetId);
|
|
2789
|
-
const mem = storeMap.get(targetId);
|
|
2790
|
-
if (mem) {
|
|
2791
|
-
const existing = candidateMap.get(targetId);
|
|
2792
|
-
if (existing) {
|
|
2793
|
-
existing.sources.add("graph");
|
|
2794
|
-
} else {
|
|
2795
|
-
candidateMap.set(targetId, { value: mem, sources: /* @__PURE__ */ new Set(["graph"]) });
|
|
2796
|
-
}
|
|
2797
|
-
graphExpanded.push(targetId);
|
|
2798
|
-
}
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
frontier = nextFrontier;
|
|
2803
|
-
}
|
|
2804
|
-
}
|
|
2805
|
-
for (const [key, mem] of storeMap) {
|
|
2806
|
-
if (!candidateMap.has(key)) {
|
|
2807
|
-
candidateMap.set(key, { value: mem, sources: /* @__PURE__ */ new Set(["store"]) });
|
|
2808
|
-
}
|
|
2809
|
-
}
|
|
2810
|
-
const ranked = [];
|
|
2811
|
-
for (const [key, { value, sources }] of candidateMap) {
|
|
2812
|
-
const score = scoreFn(value, ctx);
|
|
2813
|
-
ranked.push({ key, value, score, sources: [...sources] });
|
|
2814
|
-
}
|
|
2815
|
-
ranked.sort((a, b) => b.score - a.score);
|
|
2816
|
-
const packed = [];
|
|
2817
|
-
let usedBudget = 0;
|
|
2818
|
-
for (const entry of ranked) {
|
|
2819
|
-
const c = costFn(entry.value);
|
|
2820
|
-
if (usedBudget + c > budget && packed.length > 0) break;
|
|
2821
|
-
packed.push(entry);
|
|
2822
|
-
usedBudget += c;
|
|
2823
|
-
}
|
|
2824
|
-
const trace = {
|
|
2825
|
-
vectorCandidates,
|
|
2826
|
-
graphExpanded,
|
|
2827
|
-
ranked,
|
|
2828
|
-
packed
|
|
2829
|
-
};
|
|
2830
|
-
batch(() => {
|
|
2831
|
-
retrievalOutput.down([[DATA, packed]]);
|
|
2832
|
-
traceState.down([[DATA, trace]]);
|
|
2833
|
-
});
|
|
2834
|
-
return packed;
|
|
2835
|
-
};
|
|
2836
|
-
}
|
|
2837
|
-
graph.addDisposer(() => {
|
|
2838
|
-
for (const unsub of keepaliveSubs) unsub();
|
|
2839
|
-
keepaliveSubs.length = 0;
|
|
2840
|
-
});
|
|
2841
|
-
return Object.assign(graph, {
|
|
2842
|
-
distillBundle,
|
|
2843
|
-
compact: distillBundle.compact,
|
|
2844
|
-
size: distillBundle.size,
|
|
2845
|
-
vectors,
|
|
2846
|
-
kg,
|
|
2847
|
-
memoryTiers: memoryTiersBundle,
|
|
2848
|
-
retrieval: retrievalNode,
|
|
2849
|
-
retrievalTrace: retrievalTraceNode,
|
|
2850
|
-
retrieve: retrieveFn
|
|
2851
|
-
});
|
|
2852
|
-
}
|
|
2853
|
-
var AgentLoopGraph = class extends Graph {
|
|
2854
|
-
chat;
|
|
2855
|
-
tools;
|
|
2856
|
-
status;
|
|
2857
|
-
turnCount;
|
|
2858
|
-
lastResponse;
|
|
2859
|
-
_statusState;
|
|
2860
|
-
_turnCountState;
|
|
2861
|
-
_adapter;
|
|
2862
|
-
_maxTurns;
|
|
2863
|
-
_stopWhen;
|
|
2864
|
-
_onToolCall;
|
|
2865
|
-
_systemPrompt;
|
|
2866
|
-
_model;
|
|
2867
|
-
_temperature;
|
|
2868
|
-
_maxTokens;
|
|
2869
|
-
_running = false;
|
|
2870
|
-
_abortController = null;
|
|
2871
|
-
constructor(name, opts) {
|
|
2872
|
-
super(name, opts.graph);
|
|
2873
|
-
this._adapter = opts.adapter;
|
|
2874
|
-
this._maxTurns = opts.maxTurns ?? 10;
|
|
2875
|
-
this._stopWhen = opts.stopWhen;
|
|
2876
|
-
this._onToolCall = opts.onToolCall;
|
|
2877
|
-
this._systemPrompt = opts.systemPrompt;
|
|
2878
|
-
this._model = opts.model;
|
|
2879
|
-
this._temperature = opts.temperature;
|
|
2880
|
-
this._maxTokens = opts.maxTokens;
|
|
2881
|
-
this.chat = chatStream(`${name}-chat`, { maxMessages: opts.maxMessages });
|
|
2882
|
-
this.mount("chat", this.chat);
|
|
2883
|
-
this.tools = toolRegistry(`${name}-tools`);
|
|
2884
|
-
this.mount("tools", this.tools);
|
|
2885
|
-
if (opts.tools) {
|
|
2886
|
-
for (const tool of opts.tools) {
|
|
2887
|
-
this.tools.register(tool);
|
|
2888
|
-
}
|
|
2889
|
-
}
|
|
2890
|
-
this._statusState = state("idle", {
|
|
2891
|
-
name: "status",
|
|
2892
|
-
describeKind: "state",
|
|
2893
|
-
meta: aiMeta("agent_status")
|
|
2894
|
-
});
|
|
2895
|
-
this.status = this._statusState;
|
|
2896
|
-
this.add("status", this.status);
|
|
2897
|
-
this._turnCountState = state(0, {
|
|
2898
|
-
name: "turnCount",
|
|
2899
|
-
describeKind: "state",
|
|
2900
|
-
meta: aiMeta("agent_turn_count")
|
|
2901
|
-
});
|
|
2902
|
-
this.turnCount = this._turnCountState;
|
|
2903
|
-
this.add("turnCount", this.turnCount);
|
|
2904
|
-
this.lastResponse = state(null, {
|
|
2905
|
-
name: "lastResponse",
|
|
2906
|
-
describeKind: "state",
|
|
2907
|
-
meta: aiMeta("agent_last_response")
|
|
2908
|
-
});
|
|
2909
|
-
this.add("lastResponse", this.lastResponse);
|
|
2910
|
-
}
|
|
2911
|
-
/**
|
|
2912
|
-
* Start the agent loop with a user message. The loop runs reactively:
|
|
2913
|
-
* think (LLM call) → act (tool execution) → repeat until done.
|
|
2914
|
-
*
|
|
2915
|
-
* Messages accumulate across calls. Call `chat.clear()` before `run()`
|
|
2916
|
-
* to reset conversation history.
|
|
2917
|
-
*/
|
|
2918
|
-
async run(userMessage) {
|
|
2919
|
-
if (this._running) throw new Error("agentLoop: already running");
|
|
2920
|
-
this._running = true;
|
|
2921
|
-
this._abortController = new AbortController();
|
|
2922
|
-
const { signal } = this._abortController;
|
|
2923
|
-
batch(() => {
|
|
2924
|
-
this._statusState.down([[DATA, "idle"]]);
|
|
2925
|
-
this._turnCountState.down([[DATA, 0]]);
|
|
2926
|
-
});
|
|
2927
|
-
this.chat.append("user", userMessage);
|
|
2928
|
-
try {
|
|
2929
|
-
let turns = 0;
|
|
2930
|
-
while (turns < this._maxTurns) {
|
|
2931
|
-
if (signal.aborted) throw new Error("agentLoop: aborted");
|
|
2932
|
-
turns++;
|
|
2933
|
-
batch(() => {
|
|
2934
|
-
this._turnCountState.down([[DATA, turns]]);
|
|
2935
|
-
this._statusState.down([[DATA, "thinking"]]);
|
|
2936
|
-
});
|
|
2937
|
-
const msgs = this.chat.allMessages();
|
|
2938
|
-
const toolSchemas = this.tools.schemas.cache ?? [];
|
|
2939
|
-
const response = await this._invokeLLM(msgs, toolSchemas, signal);
|
|
2940
|
-
if (signal.aborted) throw new Error("agentLoop: aborted");
|
|
2941
|
-
this.lastResponse.down([[DATA, response]]);
|
|
2942
|
-
this.chat.append("assistant", response.content, {
|
|
2943
|
-
toolCalls: response.toolCalls
|
|
2944
|
-
});
|
|
2945
|
-
if (this._shouldStop(response)) {
|
|
2946
|
-
this._statusState.down([[DATA, "done"]]);
|
|
2947
|
-
this._running = false;
|
|
2948
|
-
this._abortController = null;
|
|
2949
|
-
return response;
|
|
2950
|
-
}
|
|
2951
|
-
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
2952
|
-
this._statusState.down([[DATA, "acting"]]);
|
|
2953
|
-
for (const call of response.toolCalls) {
|
|
2954
|
-
if (signal.aborted) throw new Error("agentLoop: aborted");
|
|
2955
|
-
this._onToolCall?.(call);
|
|
2956
|
-
try {
|
|
2957
|
-
const result = await this.tools.execute(call.name, call.arguments);
|
|
2958
|
-
this.chat.appendToolResult(call.id, JSON.stringify(result));
|
|
2959
|
-
} catch (err) {
|
|
2960
|
-
this.chat.appendToolResult(call.id, JSON.stringify({ error: String(err) }));
|
|
2961
|
-
}
|
|
2962
|
-
}
|
|
2963
|
-
} else {
|
|
2964
|
-
this._statusState.down([[DATA, "done"]]);
|
|
2965
|
-
this._running = false;
|
|
2966
|
-
this._abortController = null;
|
|
2967
|
-
return response;
|
|
2968
|
-
}
|
|
2969
|
-
}
|
|
2970
|
-
this._statusState.down([[DATA, "done"]]);
|
|
2971
|
-
this._running = false;
|
|
2972
|
-
this._abortController = null;
|
|
2973
|
-
return this.lastResponse.cache;
|
|
2974
|
-
} catch (err) {
|
|
2975
|
-
this._statusState.down([[DATA, "error"]]);
|
|
2976
|
-
this._running = false;
|
|
2977
|
-
this._abortController = null;
|
|
2978
|
-
throw err;
|
|
2979
|
-
}
|
|
2980
|
-
}
|
|
2981
|
-
async _invokeLLM(msgs, tools, signal) {
|
|
2982
|
-
const result = this._adapter.invoke(msgs, {
|
|
2983
|
-
tools: tools.length > 0 ? tools : void 0,
|
|
2984
|
-
systemPrompt: this._systemPrompt,
|
|
2985
|
-
model: this._model,
|
|
2986
|
-
temperature: this._temperature,
|
|
2987
|
-
maxTokens: this._maxTokens,
|
|
2988
|
-
signal
|
|
2989
|
-
});
|
|
2990
|
-
if (result == null) {
|
|
2991
|
-
throw new Error("_invokeLLM: adapter.invoke() returned null or undefined");
|
|
2992
|
-
}
|
|
2993
|
-
if (typeof result === "string") {
|
|
2994
|
-
throw new Error("_invokeLLM: adapter.invoke() returned a string, expected LLMResponse");
|
|
2995
|
-
}
|
|
2996
|
-
if (typeof result === "object" && "content" in result && !("subscribe" in result) && !("then" in result)) {
|
|
2997
|
-
return result;
|
|
2998
|
-
}
|
|
2999
|
-
if (isPromiseLike(result)) {
|
|
3000
|
-
const awaited = await result;
|
|
3001
|
-
if (typeof awaited === "object" && awaited !== null && "content" in awaited && !("subscribe" in awaited)) {
|
|
3002
|
-
return awaited;
|
|
3003
|
-
}
|
|
3004
|
-
return firstDataFromNode(fromAny(awaited));
|
|
3005
|
-
}
|
|
3006
|
-
return firstDataFromNode(fromAny(result));
|
|
3007
|
-
}
|
|
3008
|
-
_shouldStop(response) {
|
|
3009
|
-
if (response.finishReason === "end_turn" && (!response.toolCalls || response.toolCalls.length === 0))
|
|
3010
|
-
return true;
|
|
3011
|
-
if (this._stopWhen?.(response)) return true;
|
|
3012
|
-
return false;
|
|
3013
|
-
}
|
|
3014
|
-
destroy() {
|
|
3015
|
-
if (this._abortController) {
|
|
3016
|
-
this._abortController.abort();
|
|
3017
|
-
this._abortController = null;
|
|
3018
|
-
}
|
|
3019
|
-
this._running = false;
|
|
3020
|
-
super.destroy();
|
|
3021
|
-
}
|
|
3022
|
-
};
|
|
3023
|
-
function agentLoop(name, opts) {
|
|
3024
|
-
return new AgentLoopGraph(name, opts);
|
|
3025
|
-
}
|
|
3026
|
-
function metaToJsonSchema(meta) {
|
|
3027
|
-
const schema = {};
|
|
3028
|
-
const metaType = meta.type;
|
|
3029
|
-
if (metaType === "enum" && Array.isArray(meta.values)) {
|
|
3030
|
-
schema.type = "string";
|
|
3031
|
-
schema.enum = meta.values;
|
|
3032
|
-
} else if (metaType === "integer") {
|
|
3033
|
-
schema.type = "integer";
|
|
3034
|
-
} else if (metaType === "number") {
|
|
3035
|
-
schema.type = "number";
|
|
3036
|
-
} else if (metaType === "boolean") {
|
|
3037
|
-
schema.type = "boolean";
|
|
3038
|
-
} else if (metaType === "string") {
|
|
3039
|
-
schema.type = "string";
|
|
3040
|
-
} else {
|
|
3041
|
-
schema.type = ["string", "number", "boolean"];
|
|
3042
|
-
}
|
|
3043
|
-
if (Array.isArray(meta.range) && meta.range.length === 2) {
|
|
3044
|
-
schema.minimum = meta.range[0];
|
|
3045
|
-
schema.maximum = meta.range[1];
|
|
3046
|
-
}
|
|
3047
|
-
if (typeof meta.format === "string") {
|
|
3048
|
-
schema.description = `Format: ${meta.format}`;
|
|
3049
|
-
}
|
|
3050
|
-
if (typeof meta.unit === "string") {
|
|
3051
|
-
if (schema.description) {
|
|
3052
|
-
schema.description += ` (${meta.unit})`;
|
|
3053
|
-
} else {
|
|
3054
|
-
schema.description = `Unit: ${meta.unit}`;
|
|
3055
|
-
}
|
|
3056
|
-
}
|
|
3057
|
-
return schema;
|
|
3058
|
-
}
|
|
3059
|
-
function knobsAsTools(graph, actor) {
|
|
3060
|
-
const described = graph.describe({ actor, detail: "full" });
|
|
3061
|
-
const openai = [];
|
|
3062
|
-
const mcp = [];
|
|
3063
|
-
const definitions = [];
|
|
3064
|
-
for (const [path, node2] of Object.entries(described.nodes)) {
|
|
3065
|
-
if (node2.type !== "state") continue;
|
|
3066
|
-
if (path.includes("::__meta__::")) continue;
|
|
3067
|
-
if (node2.status === "completed" || node2.status === "errored") continue;
|
|
3068
|
-
const meta = node2.meta ?? {};
|
|
3069
|
-
const access = meta.access;
|
|
3070
|
-
if (access === "human" || access === "system") continue;
|
|
3071
|
-
const description = meta.description ?? `Set the value of ${path}`;
|
|
3072
|
-
const valueSchema = metaToJsonSchema(meta);
|
|
3073
|
-
const parameterSchema = {
|
|
3074
|
-
type: "object",
|
|
3075
|
-
required: ["value"],
|
|
3076
|
-
properties: {
|
|
3077
|
-
value: valueSchema
|
|
3078
|
-
},
|
|
3079
|
-
additionalProperties: false
|
|
3080
|
-
};
|
|
3081
|
-
const sanitizedName = path.replace(/::/g, "__");
|
|
3082
|
-
openai.push({
|
|
3083
|
-
type: "function",
|
|
3084
|
-
function: {
|
|
3085
|
-
name: sanitizedName,
|
|
3086
|
-
description,
|
|
3087
|
-
parameters: parameterSchema
|
|
3088
|
-
}
|
|
3089
|
-
});
|
|
3090
|
-
mcp.push({
|
|
3091
|
-
name: path,
|
|
3092
|
-
description,
|
|
3093
|
-
inputSchema: parameterSchema
|
|
3094
|
-
});
|
|
3095
|
-
const graphRef = graph;
|
|
3096
|
-
const actorRef = actor;
|
|
3097
|
-
const nv = node2.v;
|
|
3098
|
-
definitions.push({
|
|
3099
|
-
name: path,
|
|
3100
|
-
description,
|
|
3101
|
-
parameters: parameterSchema,
|
|
3102
|
-
handler(args) {
|
|
3103
|
-
graphRef.set(path, args.value, actorRef ? { actor: actorRef } : void 0);
|
|
3104
|
-
return args.value;
|
|
3105
|
-
},
|
|
3106
|
-
...nv != null ? { version: { id: nv.id, version: nv.version } } : {}
|
|
3107
|
-
});
|
|
3108
|
-
}
|
|
3109
|
-
return { openai, mcp, definitions };
|
|
3110
|
-
}
|
|
3111
|
-
function gaugesAsContext(graph, actor, options) {
|
|
3112
|
-
const described = graph.describe({ actor, detail: "full" });
|
|
3113
|
-
const groupByTags = options?.groupByTags ?? true;
|
|
3114
|
-
const separator = options?.separator ?? "\n";
|
|
3115
|
-
const entries = [];
|
|
3116
|
-
const sinceVersion = options?.sinceVersion;
|
|
3117
|
-
for (const [path, node2] of Object.entries(described.nodes)) {
|
|
3118
|
-
const meta = node2.meta ?? {};
|
|
3119
|
-
const desc = meta.description;
|
|
3120
|
-
const format = meta.format;
|
|
3121
|
-
if (!desc && !format) continue;
|
|
3122
|
-
if (sinceVersion != null && node2.v != null) {
|
|
3123
|
-
const lastSeen = sinceVersion.get(path);
|
|
3124
|
-
if (lastSeen != null && lastSeen.id === node2.v.id && node2.v.version <= lastSeen.version)
|
|
3125
|
-
continue;
|
|
3126
|
-
}
|
|
3127
|
-
const label = desc ?? path;
|
|
3128
|
-
const value = node2.value;
|
|
3129
|
-
const unit = meta.unit;
|
|
3130
|
-
let formatted;
|
|
3131
|
-
if (format === "currency" && typeof value === "number") {
|
|
3132
|
-
formatted = `$${value.toFixed(2)}`;
|
|
3133
|
-
} else if (format === "percentage" && typeof value === "number") {
|
|
3134
|
-
formatted = `${(value * 100).toFixed(1)}%`;
|
|
3135
|
-
} else if (value === void 0 || value === null) {
|
|
3136
|
-
formatted = "(no value)";
|
|
3137
|
-
} else {
|
|
3138
|
-
formatted = String(value);
|
|
3139
|
-
}
|
|
3140
|
-
if (unit && format !== "currency" && format !== "percentage") {
|
|
3141
|
-
formatted = `${formatted} ${unit}`;
|
|
3142
|
-
}
|
|
3143
|
-
entries.push({ path, description: label, formatted });
|
|
3144
|
-
}
|
|
3145
|
-
if (entries.length === 0) return "";
|
|
3146
|
-
if (groupByTags) {
|
|
3147
|
-
const tagGroups = /* @__PURE__ */ new Map();
|
|
3148
|
-
const ungrouped = [];
|
|
3149
|
-
for (const entry of entries) {
|
|
3150
|
-
const node2 = described.nodes[entry.path];
|
|
3151
|
-
const tags = node2.meta?.tags;
|
|
3152
|
-
if (tags && tags.length > 0) {
|
|
3153
|
-
const tag = tags[0];
|
|
3154
|
-
let group = tagGroups.get(tag);
|
|
3155
|
-
if (!group) {
|
|
3156
|
-
group = [];
|
|
3157
|
-
tagGroups.set(tag, group);
|
|
3158
|
-
}
|
|
3159
|
-
group.push(entry);
|
|
3160
|
-
} else {
|
|
3161
|
-
ungrouped.push(entry);
|
|
3162
|
-
}
|
|
3163
|
-
}
|
|
3164
|
-
if (tagGroups.size === 0) {
|
|
3165
|
-
return entries.map((e) => `- ${e.description}: ${e.formatted}`).join(separator);
|
|
3166
|
-
}
|
|
3167
|
-
const sections = [];
|
|
3168
|
-
for (const [tag, group] of [...tagGroups.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
3169
|
-
sections.push(
|
|
3170
|
-
`[${tag}]${separator}${group.map((e) => `- ${e.description}: ${e.formatted}`).join(separator)}`
|
|
3171
|
-
);
|
|
3172
|
-
}
|
|
3173
|
-
if (ungrouped.length > 0) {
|
|
3174
|
-
sections.push(ungrouped.map((e) => `- ${e.description}: ${e.formatted}`).join(separator));
|
|
3175
|
-
}
|
|
3176
|
-
return sections.join(separator + separator);
|
|
3177
|
-
}
|
|
3178
|
-
return entries.map((e) => `- ${e.description}: ${e.formatted}`).join(separator);
|
|
3179
|
-
}
|
|
3180
|
-
var VALID_NODE_TYPES = /* @__PURE__ */ new Set(["state", "derived", "producer", "operator", "effect"]);
|
|
3181
|
-
function validateGraphDef(def) {
|
|
3182
|
-
const errors = [];
|
|
3183
|
-
if (def == null || typeof def !== "object") {
|
|
3184
|
-
return { valid: false, errors: ["Definition must be a non-null object"] };
|
|
3185
|
-
}
|
|
3186
|
-
const d = def;
|
|
3187
|
-
if (typeof d.name !== "string" || d.name.length === 0) {
|
|
3188
|
-
errors.push("Missing or empty 'name' field");
|
|
3189
|
-
}
|
|
3190
|
-
if (d.nodes == null || typeof d.nodes !== "object" || Array.isArray(d.nodes)) {
|
|
3191
|
-
errors.push("Missing or invalid 'nodes' field (must be an object)");
|
|
3192
|
-
return { valid: false, errors };
|
|
3193
|
-
}
|
|
3194
|
-
const nodeNames = new Set(Object.keys(d.nodes));
|
|
3195
|
-
for (const [name, raw] of Object.entries(d.nodes)) {
|
|
3196
|
-
if (raw == null || typeof raw !== "object") {
|
|
3197
|
-
errors.push(`Node "${name}": must be an object`);
|
|
3198
|
-
continue;
|
|
3199
|
-
}
|
|
3200
|
-
const node2 = raw;
|
|
3201
|
-
if (typeof node2.type !== "string" || !VALID_NODE_TYPES.has(node2.type)) {
|
|
3202
|
-
errors.push(
|
|
3203
|
-
`Node "${name}": invalid type "${String(node2.type)}" (expected: ${[...VALID_NODE_TYPES].join(", ")})`
|
|
3204
|
-
);
|
|
3205
|
-
}
|
|
3206
|
-
if (Array.isArray(node2.deps)) {
|
|
3207
|
-
for (const dep of node2.deps) {
|
|
3208
|
-
if (typeof dep === "string" && !nodeNames.has(dep)) {
|
|
3209
|
-
errors.push(`Node "${name}": dep "${dep}" does not reference an existing node`);
|
|
3210
|
-
}
|
|
3211
|
-
}
|
|
3212
|
-
}
|
|
3213
|
-
}
|
|
3214
|
-
if (!Array.isArray(d.edges)) {
|
|
3215
|
-
if (d.edges !== void 0) {
|
|
3216
|
-
errors.push("'edges' must be an array");
|
|
3217
|
-
}
|
|
3218
|
-
} else {
|
|
3219
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3220
|
-
for (let i = 0; i < d.edges.length; i++) {
|
|
3221
|
-
const edge = d.edges[i];
|
|
3222
|
-
if (edge == null || typeof edge !== "object") {
|
|
3223
|
-
errors.push(`Edge [${i}]: must be an object`);
|
|
3224
|
-
continue;
|
|
3225
|
-
}
|
|
3226
|
-
const e = edge;
|
|
3227
|
-
if (typeof e.from !== "string" || !nodeNames.has(e.from)) {
|
|
3228
|
-
errors.push(`Edge [${i}]: 'from' "${String(e.from)}" does not reference an existing node`);
|
|
3229
|
-
}
|
|
3230
|
-
if (typeof e.to !== "string" || !nodeNames.has(e.to)) {
|
|
3231
|
-
errors.push(`Edge [${i}]: 'to' "${String(e.to)}" does not reference an existing node`);
|
|
3232
|
-
}
|
|
3233
|
-
const key = `${e.from}->${e.to}`;
|
|
3234
|
-
if (seen.has(key)) {
|
|
3235
|
-
errors.push(`Edge [${i}]: duplicate edge ${key}`);
|
|
3236
|
-
}
|
|
3237
|
-
seen.add(key);
|
|
3238
|
-
}
|
|
3239
|
-
}
|
|
3240
|
-
return { valid: errors.length === 0, errors };
|
|
3241
|
-
}
|
|
3242
|
-
function stripFences(text) {
|
|
3243
|
-
const match = text.match(/^```(?:json)?\s*([\s\S]*?)\s*```[\s\S]*$/);
|
|
3244
|
-
return match ? match[1] : text;
|
|
3245
|
-
}
|
|
3246
|
-
var GRAPH_FROM_SPEC_SYSTEM_PROMPT = `You are a graph architect for GraphReFly, a reactive graph protocol.
|
|
3247
|
-
|
|
3248
|
-
Given a natural-language description, produce a JSON graph definition with this structure:
|
|
3249
|
-
|
|
3250
|
-
{
|
|
3251
|
-
"name": "<graph_name>",
|
|
3252
|
-
"nodes": {
|
|
3253
|
-
"<node_name>": {
|
|
3254
|
-
"type": "state" | "derived" | "producer" | "operator" | "effect",
|
|
3255
|
-
"value": <initial_value_or_null>,
|
|
3256
|
-
"deps": ["<dep_node_name>", ...],
|
|
3257
|
-
"meta": {
|
|
3258
|
-
"description": "<human-readable purpose>",
|
|
3259
|
-
"type": "string" | "number" | "boolean" | "integer" | "enum",
|
|
3260
|
-
"range": [min, max],
|
|
3261
|
-
"values": ["a", "b"],
|
|
3262
|
-
"format": "currency" | "percentage" | "status",
|
|
3263
|
-
"access": "human" | "llm" | "both" | "system",
|
|
3264
|
-
"unit": "<unit>",
|
|
3265
|
-
"tags": ["<tag>"]
|
|
3266
|
-
}
|
|
3267
|
-
}
|
|
3268
|
-
},
|
|
3269
|
-
"edges": [
|
|
3270
|
-
{ "from": "<source_node>", "to": "<target_node>" }
|
|
3271
|
-
]
|
|
3272
|
-
}
|
|
3273
|
-
|
|
3274
|
-
Rules:
|
|
3275
|
-
- "state" nodes have no deps and hold user/LLM-writable values (knobs).
|
|
3276
|
-
- "derived" nodes have deps and compute from them.
|
|
3277
|
-
- "effect" nodes have deps but produce side effects (no return value).
|
|
3278
|
-
- "producer" nodes have no deps but generate values asynchronously.
|
|
3279
|
-
- Edges wire output of one node as input to another. They must match deps.
|
|
3280
|
-
- meta.description is required for every node.
|
|
3281
|
-
- Return ONLY valid JSON, no markdown fences or commentary.`;
|
|
3282
|
-
async function graphFromSpec(naturalLanguage, adapter, opts) {
|
|
3283
|
-
const systemPrompt = opts?.systemPromptExtra ? `${GRAPH_FROM_SPEC_SYSTEM_PROMPT}
|
|
3284
|
-
|
|
3285
|
-
${opts.systemPromptExtra}` : GRAPH_FROM_SPEC_SYSTEM_PROMPT;
|
|
3286
|
-
const messages = [
|
|
3287
|
-
{ role: "system", content: systemPrompt },
|
|
3288
|
-
{ role: "user", content: naturalLanguage }
|
|
3289
|
-
];
|
|
3290
|
-
const rawResult = adapter.invoke(messages, {
|
|
3291
|
-
model: opts?.model,
|
|
3292
|
-
temperature: opts?.temperature ?? 0,
|
|
3293
|
-
maxTokens: opts?.maxTokens
|
|
3294
|
-
});
|
|
3295
|
-
const response = await resolveToolHandlerResult(rawResult);
|
|
3296
|
-
let content = response.content.trim();
|
|
3297
|
-
if (content.startsWith("```")) {
|
|
3298
|
-
content = stripFences(content);
|
|
3299
|
-
}
|
|
3300
|
-
let parsed;
|
|
3301
|
-
try {
|
|
3302
|
-
parsed = JSON.parse(content);
|
|
3303
|
-
} catch {
|
|
3304
|
-
throw new Error(`graphFromSpec: LLM response is not valid JSON: ${content.slice(0, 200)}`);
|
|
3305
|
-
}
|
|
3306
|
-
const validation = validateGraphDef(parsed);
|
|
3307
|
-
if (!validation.valid) {
|
|
3308
|
-
throw new Error(`graphFromSpec: invalid graph definition:
|
|
3309
|
-
${validation.errors.join("\n")}`);
|
|
3310
|
-
}
|
|
3311
|
-
const def = parsed;
|
|
3312
|
-
if (def.version === void 0) def.version = 1;
|
|
3313
|
-
if (!Array.isArray(def.subgraphs)) def.subgraphs = [];
|
|
3314
|
-
return Graph.fromSnapshot(def, opts?.build);
|
|
3315
|
-
}
|
|
3316
|
-
var SUGGEST_STRATEGY_SYSTEM_PROMPT = `You are a reactive graph optimizer for GraphReFly.
|
|
3317
|
-
|
|
3318
|
-
Given a graph's current structure (from describe()) and a problem statement, suggest topology and parameter changes to solve the problem.
|
|
3319
|
-
|
|
3320
|
-
Return ONLY valid JSON with this structure:
|
|
3321
|
-
{
|
|
3322
|
-
"summary": "<one-line summary of the strategy>",
|
|
3323
|
-
"reasoning": "<explanation of why these changes help>",
|
|
3324
|
-
"operations": [
|
|
3325
|
-
{ "type": "add_node", "name": "<name>", "nodeType": "state|derived|effect|producer|operator", "meta": {...}, "initial": <value> },
|
|
3326
|
-
{ "type": "remove_node", "name": "<name>" },
|
|
3327
|
-
{ "type": "connect", "from": "<source>", "to": "<target>" },
|
|
3328
|
-
{ "type": "disconnect", "from": "<source>", "to": "<target>" },
|
|
3329
|
-
{ "type": "set_value", "name": "<name>", "value": <new_value> },
|
|
3330
|
-
{ "type": "update_meta", "name": "<name>", "key": "<meta_key>", "value": <new_value> }
|
|
3331
|
-
]
|
|
3332
|
-
}
|
|
3333
|
-
|
|
3334
|
-
Rules:
|
|
3335
|
-
- Only suggest operations that reference existing nodes (for remove/disconnect/set_value/update_meta) or new nodes you define (for add_node).
|
|
3336
|
-
- Keep changes minimal \u2014 prefer the smallest set of operations that solves the problem.
|
|
3337
|
-
- Return ONLY valid JSON, no markdown fences or commentary.`;
|
|
3338
|
-
async function suggestStrategy(graph, problem, adapter, opts) {
|
|
3339
|
-
const { expand: _, ...described } = graph.describe({ actor: opts?.actor, detail: "standard" });
|
|
3340
|
-
const messages = [
|
|
3341
|
-
{ role: "system", content: SUGGEST_STRATEGY_SYSTEM_PROMPT },
|
|
3342
|
-
{
|
|
3343
|
-
role: "user",
|
|
3344
|
-
content: JSON.stringify({
|
|
3345
|
-
graph: described,
|
|
3346
|
-
problem
|
|
3347
|
-
})
|
|
3348
|
-
}
|
|
3349
|
-
];
|
|
3350
|
-
const rawResult = adapter.invoke(messages, {
|
|
3351
|
-
model: opts?.model,
|
|
3352
|
-
temperature: opts?.temperature ?? 0,
|
|
3353
|
-
maxTokens: opts?.maxTokens
|
|
3354
|
-
});
|
|
3355
|
-
const response = await resolveToolHandlerResult(rawResult);
|
|
3356
|
-
let content = response.content.trim();
|
|
3357
|
-
if (content.startsWith("```")) {
|
|
3358
|
-
content = content.replace(/^```(?:json)?\s*/, "").replace(/\s*```$/, "");
|
|
3359
|
-
}
|
|
3360
|
-
let parsed;
|
|
3361
|
-
try {
|
|
3362
|
-
parsed = JSON.parse(content);
|
|
3363
|
-
} catch {
|
|
3364
|
-
throw new Error(`suggestStrategy: LLM response is not valid JSON: ${content.slice(0, 200)}`);
|
|
3365
|
-
}
|
|
3366
|
-
const plan = parsed;
|
|
3367
|
-
if (typeof plan.summary !== "string") {
|
|
3368
|
-
throw new Error("suggestStrategy: missing 'summary' in response");
|
|
3369
|
-
}
|
|
3370
|
-
if (typeof plan.reasoning !== "string") {
|
|
3371
|
-
throw new Error("suggestStrategy: missing 'reasoning' in response");
|
|
3372
|
-
}
|
|
3373
|
-
if (!Array.isArray(plan.operations)) {
|
|
3374
|
-
throw new Error("suggestStrategy: missing 'operations' array in response");
|
|
3375
|
-
}
|
|
3376
|
-
return {
|
|
3377
|
-
summary: plan.summary,
|
|
3378
|
-
reasoning: plan.reasoning,
|
|
3379
|
-
operations: plan.operations
|
|
3380
|
-
};
|
|
3381
|
-
}
|
|
229
|
+
cached,
|
|
230
|
+
empty,
|
|
231
|
+
escapeRegexChar,
|
|
232
|
+
firstValueFrom,
|
|
233
|
+
firstWhere,
|
|
234
|
+
forEach,
|
|
235
|
+
fromAny,
|
|
236
|
+
fromAsyncIter,
|
|
237
|
+
fromCron,
|
|
238
|
+
fromEvent,
|
|
239
|
+
fromIter,
|
|
240
|
+
fromPromise,
|
|
241
|
+
fromRaf,
|
|
242
|
+
fromTimer,
|
|
243
|
+
globToRegExp,
|
|
244
|
+
keepalive,
|
|
245
|
+
matchesAnyPattern,
|
|
246
|
+
matchesCron,
|
|
247
|
+
never,
|
|
248
|
+
of,
|
|
249
|
+
parseCron,
|
|
250
|
+
reactiveCounter,
|
|
251
|
+
replay,
|
|
252
|
+
share,
|
|
253
|
+
shareReplay,
|
|
254
|
+
throwError,
|
|
255
|
+
toArray
|
|
256
|
+
} from "./chunk-BVZYTZ5H.js";
|
|
257
|
+
import {
|
|
258
|
+
graph_exports,
|
|
259
|
+
watchTopologyTree
|
|
260
|
+
} from "./chunk-OU5CQKNW.js";
|
|
261
|
+
import {
|
|
262
|
+
GRAPH_META_SEGMENT,
|
|
263
|
+
Graph,
|
|
264
|
+
OVERHEAD,
|
|
265
|
+
SIZEOF_SYMBOL,
|
|
266
|
+
SNAPSHOT_VERSION,
|
|
267
|
+
diffForWAL,
|
|
268
|
+
explainPath,
|
|
269
|
+
graphProfile,
|
|
270
|
+
reachable,
|
|
271
|
+
sizeof
|
|
272
|
+
} from "./chunk-PF7GRZMW.js";
|
|
273
|
+
import {
|
|
274
|
+
resolveDescribeFields
|
|
275
|
+
} from "./chunk-VYPWMZ6H.js";
|
|
276
|
+
import {
|
|
277
|
+
ResettableTimer
|
|
278
|
+
} from "./chunk-7TAQJHQV.js";
|
|
279
|
+
import {
|
|
280
|
+
DEFAULT_ACTOR,
|
|
281
|
+
ENVELOPE_VERSION,
|
|
282
|
+
GraphReFlyConfig,
|
|
283
|
+
GuardDenied,
|
|
284
|
+
JsonCodec,
|
|
285
|
+
NodeImpl,
|
|
286
|
+
accessHintForGuard,
|
|
287
|
+
advanceVersion,
|
|
288
|
+
autoTrackNode,
|
|
289
|
+
batch,
|
|
290
|
+
configure,
|
|
291
|
+
createDagCborCodec,
|
|
292
|
+
createDagCborZstdCodec,
|
|
293
|
+
createVersioning,
|
|
294
|
+
decodeEnvelope,
|
|
295
|
+
defaultConfig,
|
|
296
|
+
defaultHash,
|
|
297
|
+
derived,
|
|
298
|
+
downWithBatch,
|
|
299
|
+
dynamicNode,
|
|
300
|
+
effect,
|
|
301
|
+
encodeEnvelope,
|
|
302
|
+
isBatching,
|
|
303
|
+
isV1,
|
|
304
|
+
monotonicNs,
|
|
305
|
+
node,
|
|
306
|
+
normalizeActor,
|
|
307
|
+
pipe,
|
|
308
|
+
policy,
|
|
309
|
+
policyFromRules,
|
|
310
|
+
producer,
|
|
311
|
+
registerBuiltinCodecs,
|
|
312
|
+
registerBuiltins,
|
|
313
|
+
replayWAL,
|
|
314
|
+
state,
|
|
315
|
+
wallClockNs
|
|
316
|
+
} from "./chunk-PHOUUNK7.js";
|
|
317
|
+
import {
|
|
318
|
+
COMPLETE,
|
|
319
|
+
COMPLETE_MSG,
|
|
320
|
+
COMPLETE_ONLY_BATCH,
|
|
321
|
+
DATA,
|
|
322
|
+
DIRTY,
|
|
323
|
+
DIRTY_MSG,
|
|
324
|
+
DIRTY_ONLY_BATCH,
|
|
325
|
+
ERROR,
|
|
326
|
+
INVALIDATE,
|
|
327
|
+
INVALIDATE_MSG,
|
|
328
|
+
INVALIDATE_ONLY_BATCH,
|
|
329
|
+
PAUSE,
|
|
330
|
+
RESOLVED,
|
|
331
|
+
RESOLVED_MSG,
|
|
332
|
+
RESOLVED_ONLY_BATCH,
|
|
333
|
+
RESUME,
|
|
334
|
+
START,
|
|
335
|
+
START_MSG,
|
|
336
|
+
TEARDOWN,
|
|
337
|
+
TEARDOWN_MSG,
|
|
338
|
+
TEARDOWN_ONLY_BATCH,
|
|
339
|
+
__export
|
|
340
|
+
} from "./chunk-SX52TAR4.js";
|
|
3382
341
|
|
|
3383
|
-
// src/patterns/
|
|
3384
|
-
var
|
|
3385
|
-
__export(
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
342
|
+
// src/patterns/index.ts
|
|
343
|
+
var patterns_exports = {};
|
|
344
|
+
__export(patterns_exports, {
|
|
345
|
+
SNAPSHOT_WIRE_VERSION: () => SNAPSHOT_WIRE_VERSION,
|
|
346
|
+
SurfaceError: () => SurfaceError,
|
|
347
|
+
accountability: () => audit_exports,
|
|
348
|
+
ai: () => ai_exports,
|
|
349
|
+
asSurfaceError: () => asSurfaceError,
|
|
350
|
+
cqrs: () => cqrs_exports,
|
|
351
|
+
createGraph: () => createGraph,
|
|
352
|
+
deleteSnapshot: () => deleteSnapshot,
|
|
353
|
+
demoShell: () => demo_shell_exports,
|
|
354
|
+
diffSnapshots: () => diffSnapshots,
|
|
355
|
+
domainTemplates: () => domain_templates_exports,
|
|
356
|
+
graphspec: () => graphspec_exports,
|
|
357
|
+
guarded: () => guarded_execution_exports,
|
|
358
|
+
harness: () => harness_exports,
|
|
359
|
+
layout: () => reactive_layout_exports,
|
|
360
|
+
lens: () => lens_exports,
|
|
361
|
+
listSnapshots: () => listSnapshots,
|
|
362
|
+
memory: () => memory_exports,
|
|
363
|
+
messaging: () => messaging_exports,
|
|
364
|
+
orchestration: () => orchestration_exports,
|
|
365
|
+
reduction: () => reduction_exports,
|
|
366
|
+
resilientPipeline: () => resilient_pipeline_exports,
|
|
367
|
+
restoreSnapshot: () => restoreSnapshot,
|
|
368
|
+
runReduction: () => runReduction,
|
|
369
|
+
saveSnapshot: () => saveSnapshot,
|
|
370
|
+
surface: () => surface_exports
|
|
3392
371
|
});
|
|
3393
|
-
function auditMeta(kind, extra) {
|
|
3394
|
-
return domainMeta("audit", kind, extra);
|
|
3395
|
-
}
|
|
3396
|
-
var DEFAULT_INCLUDE_TYPES = /* @__PURE__ */ new Set([
|
|
3397
|
-
"data",
|
|
3398
|
-
"error",
|
|
3399
|
-
"complete",
|
|
3400
|
-
"teardown"
|
|
3401
|
-
]);
|
|
3402
|
-
var AuditTrailGraph = class extends Graph {
|
|
3403
|
-
entries;
|
|
3404
|
-
count;
|
|
3405
|
-
_log;
|
|
3406
|
-
_target;
|
|
3407
|
-
constructor(target, opts) {
|
|
3408
|
-
super(opts.name ?? `${target.name}_audit`, opts.graph);
|
|
3409
|
-
this._target = target;
|
|
3410
|
-
this._log = reactiveLog([], {
|
|
3411
|
-
name: "entries",
|
|
3412
|
-
...opts.maxSize != null ? { maxSize: opts.maxSize } : {}
|
|
3413
|
-
});
|
|
3414
|
-
this.entries = this._log.entries;
|
|
3415
|
-
this.add("entries", this.entries);
|
|
3416
|
-
this.count = derived(
|
|
3417
|
-
[this.entries],
|
|
3418
|
-
([snapshot]) => snapshot.length,
|
|
3419
|
-
{ name: "count", describeKind: "derived", meta: auditMeta("count") }
|
|
3420
|
-
);
|
|
3421
|
-
this.add("count", this.count);
|
|
3422
|
-
this.addDisposer(keepalive(this.count));
|
|
3423
|
-
const includeTypes = opts.includeTypes != null ? new Set(opts.includeTypes) : DEFAULT_INCLUDE_TYPES;
|
|
3424
|
-
const filter2 = opts.filter;
|
|
3425
|
-
let seq = 0;
|
|
3426
|
-
const handle = target.observe({ timeline: true, structured: true });
|
|
3427
|
-
const offEvent = handle.onEvent((event) => {
|
|
3428
|
-
if (event.type === "derived") return;
|
|
3429
|
-
const type = event.type;
|
|
3430
|
-
if (!includeTypes.has(type)) return;
|
|
3431
|
-
const path = event.path ?? "";
|
|
3432
|
-
const entry = {
|
|
3433
|
-
seq: seq++,
|
|
3434
|
-
timestamp_ns: event.timestamp_ns ?? monotonicNs(),
|
|
3435
|
-
wall_clock_ns: wallClockNs(),
|
|
3436
|
-
path,
|
|
3437
|
-
type
|
|
3438
|
-
};
|
|
3439
|
-
const node2 = path ? safeNode(target, path) : void 0;
|
|
3440
|
-
const lastMutation = node2?.lastMutation;
|
|
3441
|
-
if (lastMutation != null) entry.actor = lastMutation.actor;
|
|
3442
|
-
if (type === "data") entry.value = event.data;
|
|
3443
|
-
if (type === "error") entry.error = event.data;
|
|
3444
|
-
const reason = path ? safeAnnotation(target, path) : void 0;
|
|
3445
|
-
if (reason != null) entry.reason = reason;
|
|
3446
|
-
if (filter2 != null && !filter2(entry)) return;
|
|
3447
|
-
this._log.append(entry);
|
|
3448
|
-
});
|
|
3449
|
-
this.addDisposer(() => {
|
|
3450
|
-
offEvent();
|
|
3451
|
-
handle.dispose();
|
|
3452
|
-
});
|
|
3453
|
-
this.addDisposer(() => this._log.disposeAllViews());
|
|
3454
|
-
}
|
|
3455
|
-
/** All entries currently in the ring (snapshot). */
|
|
3456
|
-
all() {
|
|
3457
|
-
return this.entries.cache ?? [];
|
|
3458
|
-
}
|
|
3459
|
-
/** Entries matching `path`. Order preserved. */
|
|
3460
|
-
byNode(path) {
|
|
3461
|
-
return this.all().filter((e) => e.path === path);
|
|
3462
|
-
}
|
|
3463
|
-
/** Entries whose `actor.id` matches. Use `byActorType` for type filtering. */
|
|
3464
|
-
byActor(actorId) {
|
|
3465
|
-
return this.all().filter((e) => e.actor?.id === actorId);
|
|
3466
|
-
}
|
|
3467
|
-
/** Entries whose `actor.type` matches (e.g. `"llm"`, `"human"`). */
|
|
3468
|
-
byActorType(type) {
|
|
3469
|
-
return this.all().filter((e) => e.actor?.type === type);
|
|
3470
|
-
}
|
|
3471
|
-
/**
|
|
3472
|
-
* Entries with `timestamp_ns` in `[start_ns, end_ns)` (end exclusive).
|
|
3473
|
-
* Omit `end_ns` to query open-ended.
|
|
3474
|
-
*/
|
|
3475
|
-
byTimeRange(start_ns, end_ns) {
|
|
3476
|
-
return this.all().filter((e) => {
|
|
3477
|
-
if (e.timestamp_ns < start_ns) return false;
|
|
3478
|
-
if (end_ns != null && e.timestamp_ns >= end_ns) return false;
|
|
3479
|
-
return true;
|
|
3480
|
-
});
|
|
3481
|
-
}
|
|
3482
|
-
/** Reference to the audited graph (escape hatch for tooling). */
|
|
3483
|
-
get target() {
|
|
3484
|
-
return this._target;
|
|
3485
|
-
}
|
|
3486
|
-
};
|
|
3487
|
-
function auditTrail(target, opts = {}) {
|
|
3488
|
-
return new AuditTrailGraph(target, opts);
|
|
3489
|
-
}
|
|
3490
|
-
var PolicyEnforcerGraph = class extends Graph {
|
|
3491
|
-
policies;
|
|
3492
|
-
violations;
|
|
3493
|
-
violationCount;
|
|
3494
|
-
_target;
|
|
3495
|
-
_mode;
|
|
3496
|
-
_currentGuard;
|
|
3497
|
-
constructor(target, policies, opts) {
|
|
3498
|
-
super(opts.name ?? `${target.name}_policy`, opts.graph);
|
|
3499
|
-
this._target = target;
|
|
3500
|
-
this._mode = opts.mode ?? "audit";
|
|
3501
|
-
const policiesNode = isNode(policies) ? policies : state(policies, { name: "policies" });
|
|
3502
|
-
this.policies = policiesNode;
|
|
3503
|
-
this.add("policies", this.policies);
|
|
3504
|
-
this.violations = new TopicGraph("violations", {
|
|
3505
|
-
retainedLimit: opts.violationsLimit ?? 1e3
|
|
3506
|
-
});
|
|
3507
|
-
this.mount("violations", this.violations);
|
|
3508
|
-
this.violationCount = derived(
|
|
3509
|
-
[this.violations.events],
|
|
3510
|
-
([snapshot]) => snapshot.length,
|
|
3511
|
-
{
|
|
3512
|
-
name: "violationCount",
|
|
3513
|
-
describeKind: "derived",
|
|
3514
|
-
meta: auditMeta("policy_violation_count")
|
|
3515
|
-
}
|
|
3516
|
-
);
|
|
3517
|
-
this.add("violationCount", this.violationCount);
|
|
3518
|
-
this.addDisposer(keepalive(this.violationCount));
|
|
3519
|
-
const initialRules = policiesNode.cache ?? [];
|
|
3520
|
-
let latestRules = initialRules;
|
|
3521
|
-
this._currentGuard = policyFromRules(latestRules);
|
|
3522
|
-
const offPolicies = policiesNode.subscribe((msgs) => {
|
|
3523
|
-
for (const m of msgs) {
|
|
3524
|
-
if (m[0] === DATA) {
|
|
3525
|
-
latestRules = m[1] ?? [];
|
|
3526
|
-
this._currentGuard = policyFromRules(latestRules);
|
|
3527
|
-
}
|
|
3528
|
-
}
|
|
3529
|
-
});
|
|
3530
|
-
this.addDisposer(offPolicies);
|
|
3531
|
-
const paths = opts.paths != null ? [...opts.paths] : collectPaths(target);
|
|
3532
|
-
if (this._mode === "enforce") {
|
|
3533
|
-
const restorers = /* @__PURE__ */ new Map();
|
|
3534
|
-
const wrapAndPush = (path) => {
|
|
3535
|
-
if (restorers.has(path)) return;
|
|
3536
|
-
const node2 = safeNode(target, path);
|
|
3537
|
-
if (!(node2 instanceof NodeImpl)) return;
|
|
3538
|
-
const pathGuard = (actor, action) => {
|
|
3539
|
-
const ok = this._currentGuard(actor, action);
|
|
3540
|
-
if (!ok) {
|
|
3541
|
-
this._publishViolation(actor, action, path, "blocked");
|
|
3542
|
-
}
|
|
3543
|
-
return ok;
|
|
3544
|
-
};
|
|
3545
|
-
restorers.set(path, node2._pushGuard(pathGuard));
|
|
3546
|
-
};
|
|
3547
|
-
for (const path of paths) wrapAndPush(path);
|
|
3548
|
-
if (opts.paths == null) {
|
|
3549
|
-
const offTopology = watchTopologyTree(target, (event, emitter, prefix) => {
|
|
3550
|
-
if (event.kind === "added") {
|
|
3551
|
-
if (event.nodeKind === "node") {
|
|
3552
|
-
wrapAndPush(`${prefix}${event.name}`);
|
|
3553
|
-
} else {
|
|
3554
|
-
const child = emitter._mounts.get(event.name);
|
|
3555
|
-
if (!(child instanceof Graph)) return;
|
|
3556
|
-
const mountPrefix = `${prefix}${event.name}::`;
|
|
3557
|
-
const localPaths = collectPaths(child);
|
|
3558
|
-
for (const localPath of localPaths) {
|
|
3559
|
-
wrapAndPush(
|
|
3560
|
-
localPath === "" ? `${prefix}${event.name}` : `${mountPrefix}${localPath}`
|
|
3561
|
-
);
|
|
3562
|
-
}
|
|
3563
|
-
}
|
|
3564
|
-
} else if (event.kind === "removed") {
|
|
3565
|
-
if (event.nodeKind === "node") {
|
|
3566
|
-
const qp = `${prefix}${event.name}`;
|
|
3567
|
-
const r = restorers.get(qp);
|
|
3568
|
-
if (r != null) {
|
|
3569
|
-
r();
|
|
3570
|
-
restorers.delete(qp);
|
|
3571
|
-
}
|
|
3572
|
-
} else {
|
|
3573
|
-
const mountQp = `${prefix}${event.name}`;
|
|
3574
|
-
const mountPrefix = `${mountQp}::`;
|
|
3575
|
-
for (const [p, r] of restorers) {
|
|
3576
|
-
if (p === mountQp || p.startsWith(mountPrefix)) {
|
|
3577
|
-
r();
|
|
3578
|
-
restorers.delete(p);
|
|
3579
|
-
}
|
|
3580
|
-
}
|
|
3581
|
-
}
|
|
3582
|
-
}
|
|
3583
|
-
});
|
|
3584
|
-
this.addDisposer(offTopology);
|
|
3585
|
-
} else {
|
|
3586
|
-
const offCleanup = target.topology.subscribe((msgs) => {
|
|
3587
|
-
for (const m of msgs) {
|
|
3588
|
-
if (m[0] !== DATA) continue;
|
|
3589
|
-
const event = m[1];
|
|
3590
|
-
if (event.kind !== "removed" || event.nodeKind !== "node") continue;
|
|
3591
|
-
const r = restorers.get(event.name);
|
|
3592
|
-
if (r != null) {
|
|
3593
|
-
r();
|
|
3594
|
-
restorers.delete(event.name);
|
|
3595
|
-
}
|
|
3596
|
-
}
|
|
3597
|
-
});
|
|
3598
|
-
this.addDisposer(offCleanup);
|
|
3599
|
-
}
|
|
3600
|
-
this.addDisposer(() => {
|
|
3601
|
-
for (const r of restorers.values()) r();
|
|
3602
|
-
restorers.clear();
|
|
3603
|
-
});
|
|
3604
|
-
} else {
|
|
3605
|
-
const handle = target.observe({ timeline: true, structured: true });
|
|
3606
|
-
const off = handle.onEvent((event) => {
|
|
3607
|
-
if (event.type !== "data" && event.type !== "error") return;
|
|
3608
|
-
const path = event.path ?? "";
|
|
3609
|
-
if (!path) return;
|
|
3610
|
-
if (opts.paths != null && !opts.paths.includes(path)) return;
|
|
3611
|
-
const node2 = safeNode(target, path);
|
|
3612
|
-
const lastMutation = node2?.lastMutation;
|
|
3613
|
-
if (lastMutation == null) return;
|
|
3614
|
-
const action = "write";
|
|
3615
|
-
if (this._currentGuard(lastMutation.actor, action)) return;
|
|
3616
|
-
this._publishViolation(lastMutation.actor, action, path, "observed");
|
|
3617
|
-
});
|
|
3618
|
-
this.addDisposer(() => {
|
|
3619
|
-
off();
|
|
3620
|
-
handle.dispose();
|
|
3621
|
-
});
|
|
3622
|
-
}
|
|
3623
|
-
}
|
|
3624
|
-
_publishViolation(actor, action, path, result) {
|
|
3625
|
-
this.violations.publish({
|
|
3626
|
-
timestamp_ns: monotonicNs(),
|
|
3627
|
-
wall_clock_ns: wallClockNs(),
|
|
3628
|
-
path,
|
|
3629
|
-
actor,
|
|
3630
|
-
action,
|
|
3631
|
-
mode: this._mode,
|
|
3632
|
-
result
|
|
3633
|
-
});
|
|
3634
|
-
}
|
|
3635
|
-
/** Snapshot of recorded violations. */
|
|
3636
|
-
all() {
|
|
3637
|
-
return this.violations.retained();
|
|
3638
|
-
}
|
|
3639
|
-
get mode() {
|
|
3640
|
-
return this._mode;
|
|
3641
|
-
}
|
|
3642
|
-
get target() {
|
|
3643
|
-
return this._target;
|
|
3644
|
-
}
|
|
3645
|
-
};
|
|
3646
|
-
function policyEnforcer(target, policies, opts = {}) {
|
|
3647
|
-
return new PolicyEnforcerGraph(target, policies, opts);
|
|
3648
|
-
}
|
|
3649
|
-
function reactiveExplainPath(target, from, to, opts) {
|
|
3650
|
-
let v = 0;
|
|
3651
|
-
const version2 = state(v, { name: "explain_version" });
|
|
3652
|
-
const handle = target.observe({ timeline: true, structured: true });
|
|
3653
|
-
const off = handle.onEvent((event) => {
|
|
3654
|
-
const t = event.type;
|
|
3655
|
-
if (t !== "data" && t !== "error" && t !== "complete" && t !== "teardown") return;
|
|
3656
|
-
v += 1;
|
|
3657
|
-
version2.emit(v);
|
|
3658
|
-
});
|
|
3659
|
-
const explainOpts = {
|
|
3660
|
-
...opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {},
|
|
3661
|
-
...opts?.findCycle === true ? { findCycle: true } : {}
|
|
3662
|
-
};
|
|
3663
|
-
const node2 = derived([version2], () => target.explain(from, to, explainOpts), {
|
|
3664
|
-
name: opts?.name ?? "explain",
|
|
3665
|
-
describeKind: "derived",
|
|
3666
|
-
equals: (a, b) => a.found === b.found && a.reason === b.reason && a.steps.length === b.steps.length && causalStepsEqual(a.steps, b.steps),
|
|
3667
|
-
meta: auditMeta("explain_path", { from, to })
|
|
3668
|
-
});
|
|
3669
|
-
const stopKeepalive = keepalive(node2);
|
|
3670
|
-
return {
|
|
3671
|
-
node: node2,
|
|
3672
|
-
dispose() {
|
|
3673
|
-
off();
|
|
3674
|
-
handle.dispose();
|
|
3675
|
-
stopKeepalive();
|
|
3676
|
-
}
|
|
3677
|
-
};
|
|
3678
|
-
}
|
|
3679
|
-
function causalStepsEqual(a, b) {
|
|
3680
|
-
for (let i = 0; i < a.length; i++) {
|
|
3681
|
-
const x = a[i];
|
|
3682
|
-
const y = b[i];
|
|
3683
|
-
if (x.path !== y.path) return false;
|
|
3684
|
-
if (x.type !== y.type) return false;
|
|
3685
|
-
if (x.status !== y.status) return false;
|
|
3686
|
-
if (x.hop !== y.hop) return false;
|
|
3687
|
-
if (x.dep_index !== y.dep_index) return false;
|
|
3688
|
-
if (x.reason !== y.reason) return false;
|
|
3689
|
-
if (x.value !== y.value) return false;
|
|
3690
|
-
if (x.lastMutation !== y.lastMutation) return false;
|
|
3691
|
-
const xv = x.v;
|
|
3692
|
-
const yv = y.v;
|
|
3693
|
-
if (xv !== yv) {
|
|
3694
|
-
if (xv == null || yv == null) return false;
|
|
3695
|
-
if (xv.id !== yv.id || xv.version !== yv.version) return false;
|
|
3696
|
-
}
|
|
3697
|
-
}
|
|
3698
|
-
return true;
|
|
3699
|
-
}
|
|
3700
|
-
function complianceSnapshot(target, opts = {}) {
|
|
3701
|
-
const result = {
|
|
3702
|
-
format_version: 1,
|
|
3703
|
-
timestamp_ns: monotonicNs(),
|
|
3704
|
-
wall_clock_ns: wallClockNs(),
|
|
3705
|
-
graph: target.snapshot()
|
|
3706
|
-
};
|
|
3707
|
-
if (opts.actor != null) result.actor = opts.actor;
|
|
3708
|
-
if (opts.audit != null) {
|
|
3709
|
-
const entries = [...opts.audit.all()];
|
|
3710
|
-
result.audit = { count: entries.length, entries };
|
|
3711
|
-
}
|
|
3712
|
-
if (opts.policies != null) {
|
|
3713
|
-
const rules = opts.policies.policies.cache ?? [];
|
|
3714
|
-
result.policies = {
|
|
3715
|
-
mode: opts.policies.mode,
|
|
3716
|
-
rules,
|
|
3717
|
-
violations: [...opts.policies.all()]
|
|
3718
|
-
};
|
|
3719
|
-
}
|
|
3720
|
-
const fingerprint = computeFingerprint(result);
|
|
3721
|
-
return { ...result, fingerprint };
|
|
3722
|
-
}
|
|
3723
|
-
function isNode(x) {
|
|
3724
|
-
return typeof x === "object" && x !== null && "subscribe" in x;
|
|
3725
|
-
}
|
|
3726
|
-
function safeNode(target, path) {
|
|
3727
|
-
try {
|
|
3728
|
-
return target.node(path);
|
|
3729
|
-
} catch {
|
|
3730
|
-
return void 0;
|
|
3731
|
-
}
|
|
3732
|
-
}
|
|
3733
|
-
function safeAnnotation(target, path) {
|
|
3734
|
-
try {
|
|
3735
|
-
return target.annotation(path);
|
|
3736
|
-
} catch {
|
|
3737
|
-
return void 0;
|
|
3738
|
-
}
|
|
3739
|
-
}
|
|
3740
|
-
function collectPaths(target) {
|
|
3741
|
-
const described = target.describe({ detail: "minimal" });
|
|
3742
|
-
return Object.keys(described.nodes);
|
|
3743
|
-
}
|
|
3744
|
-
function computeFingerprint(value) {
|
|
3745
|
-
return defaultHash(JSON.stringify(canonicalize(value)));
|
|
3746
|
-
}
|
|
3747
|
-
function canonicalize(value) {
|
|
3748
|
-
const stack = /* @__PURE__ */ new Set();
|
|
3749
|
-
const walk = (v) => {
|
|
3750
|
-
if (v === void 0) return { __undefined: true };
|
|
3751
|
-
if (v === null) return null;
|
|
3752
|
-
const t = typeof v;
|
|
3753
|
-
if (t === "bigint") return { __bigint: v.toString() };
|
|
3754
|
-
if (t !== "object") return v;
|
|
3755
|
-
const obj = v;
|
|
3756
|
-
if (stack.has(obj)) return { __circular: true };
|
|
3757
|
-
stack.add(obj);
|
|
3758
|
-
try {
|
|
3759
|
-
if (Array.isArray(obj)) {
|
|
3760
|
-
return obj.map(walk);
|
|
3761
|
-
}
|
|
3762
|
-
if (obj instanceof Date) {
|
|
3763
|
-
return { __date: obj.toISOString() };
|
|
3764
|
-
}
|
|
3765
|
-
if (obj instanceof RegExp) {
|
|
3766
|
-
return { __regexp: { source: obj.source, flags: obj.flags } };
|
|
3767
|
-
}
|
|
3768
|
-
if (obj instanceof Map) {
|
|
3769
|
-
const entries = [...obj.entries()].map(([k, mv]) => [
|
|
3770
|
-
walk(k),
|
|
3771
|
-
walk(mv)
|
|
3772
|
-
]);
|
|
3773
|
-
return { __map: entries };
|
|
3774
|
-
}
|
|
3775
|
-
if (obj instanceof Set) {
|
|
3776
|
-
const items = [...obj].map(walk);
|
|
3777
|
-
return { __set: items };
|
|
3778
|
-
}
|
|
3779
|
-
if (ArrayBuffer.isView(obj)) {
|
|
3780
|
-
const ta = obj;
|
|
3781
|
-
const arr = new Array(ta.length);
|
|
3782
|
-
for (let i = 0; i < ta.length; i++) arr[i] = ta[i] ?? 0;
|
|
3783
|
-
return { __typed_array: { ctor: obj.constructor.name, data: arr } };
|
|
3784
|
-
}
|
|
3785
|
-
const out = {};
|
|
3786
|
-
for (const k of Object.keys(obj).sort()) {
|
|
3787
|
-
out[k] = walk(obj[k]);
|
|
3788
|
-
}
|
|
3789
|
-
return out;
|
|
3790
|
-
} finally {
|
|
3791
|
-
stack.delete(obj);
|
|
3792
|
-
}
|
|
3793
|
-
};
|
|
3794
|
-
return walk(value);
|
|
3795
|
-
}
|
|
3796
372
|
|
|
3797
373
|
// src/patterns/domain-templates.ts
|
|
3798
374
|
var domain_templates_exports = {};
|
|
@@ -3813,14 +389,14 @@ __export(reduction_exports, {
|
|
|
3813
389
|
scorer: () => scorer,
|
|
3814
390
|
stratify: () => stratify
|
|
3815
391
|
});
|
|
3816
|
-
function
|
|
392
|
+
function baseMeta(kind, meta) {
|
|
3817
393
|
return domainMeta("reduction", kind, meta);
|
|
3818
394
|
}
|
|
3819
395
|
function stratify(name, source, rules, opts) {
|
|
3820
396
|
const g = new Graph(name, opts);
|
|
3821
397
|
g.add("source", source);
|
|
3822
398
|
const rulesNode = state(rules, {
|
|
3823
|
-
meta:
|
|
399
|
+
meta: baseMeta("stratify_rules")
|
|
3824
400
|
});
|
|
3825
401
|
g.add("rules", rulesNode);
|
|
3826
402
|
for (const rule of rules) {
|
|
@@ -3889,7 +465,7 @@ function _addBranch(graph, source, rulesNode, rule) {
|
|
|
3889
465
|
},
|
|
3890
466
|
{
|
|
3891
467
|
describeKind: "derived",
|
|
3892
|
-
meta:
|
|
468
|
+
meta: baseMeta("stratify_branch", { branch: rule.name }),
|
|
3893
469
|
completeWhenDepsComplete: false
|
|
3894
470
|
}
|
|
3895
471
|
);
|
|
@@ -3983,7 +559,7 @@ function feedback(graph, condition, reentry, opts) {
|
|
|
3983
559
|
const maxIter = opts?.maxIterations ?? 10;
|
|
3984
560
|
const counterName = `__feedback_${condition}`;
|
|
3985
561
|
const counter = state(0, {
|
|
3986
|
-
meta:
|
|
562
|
+
meta: baseMeta("feedback_counter", {
|
|
3987
563
|
maxIterations: maxIter,
|
|
3988
564
|
feedbackFrom: condition,
|
|
3989
565
|
feedbackTo: reentry
|
|
@@ -4019,7 +595,7 @@ function feedback(graph, condition, reentry, opts) {
|
|
|
4019
595
|
name: feedbackEffectName,
|
|
4020
596
|
describeKind: "effect",
|
|
4021
597
|
meta: {
|
|
4022
|
-
...
|
|
598
|
+
...baseMeta("feedback_effect", {
|
|
4023
599
|
feedbackFrom: condition,
|
|
4024
600
|
feedbackTo: reentry
|
|
4025
601
|
}),
|
|
@@ -4078,7 +654,7 @@ function budgetGate(source, constraints, opts) {
|
|
|
4078
654
|
{
|
|
4079
655
|
...opts,
|
|
4080
656
|
describeKind: "derived",
|
|
4081
|
-
meta:
|
|
657
|
+
meta: baseMeta("budget_gate", opts?.meta)
|
|
4082
658
|
}
|
|
4083
659
|
);
|
|
4084
660
|
function _handleBudgetMessage(msg, depIndex, actions) {
|
|
@@ -4188,7 +764,7 @@ function scorer(sources, weights, opts) {
|
|
|
4188
764
|
resetOnTeardown: opts.resetOnTeardown
|
|
4189
765
|
} : {},
|
|
4190
766
|
describeKind: "derived",
|
|
4191
|
-
meta:
|
|
767
|
+
meta: baseMeta("scorer", opts?.meta)
|
|
4192
768
|
}
|
|
4193
769
|
);
|
|
4194
770
|
}
|
|
@@ -4239,7 +815,7 @@ function effectivenessTracker(opts) {
|
|
|
4239
815
|
}
|
|
4240
816
|
|
|
4241
817
|
// src/patterns/domain-templates.ts
|
|
4242
|
-
function
|
|
818
|
+
function baseMeta2(kind, extra) {
|
|
4243
819
|
return domainMeta("domain_template", kind, extra);
|
|
4244
820
|
}
|
|
4245
821
|
function observabilityGraph(name, opts) {
|
|
@@ -4270,16 +846,16 @@ function observabilityGraph(name, opts) {
|
|
|
4270
846
|
branchNodes,
|
|
4271
847
|
(vals) => correlateFn(vals),
|
|
4272
848
|
{
|
|
4273
|
-
meta:
|
|
849
|
+
meta: baseMeta2("observability", { stage: "correlate" })
|
|
4274
850
|
}
|
|
4275
851
|
);
|
|
4276
852
|
g.add("correlate", correlateNode);
|
|
4277
853
|
const sloCheckFn = opts.sloCheck ?? (() => ({ pass: true }));
|
|
4278
854
|
const sloValue = derived([correlateNode], (vals) => vals[0], {
|
|
4279
|
-
meta:
|
|
855
|
+
meta: baseMeta2("observability", { stage: "slo_value" })
|
|
4280
856
|
});
|
|
4281
857
|
const sloVerified = derived([sloValue], (vals) => sloCheckFn(vals[0]), {
|
|
4282
|
-
meta:
|
|
858
|
+
meta: baseMeta2("observability", { stage: "slo_verified" })
|
|
4283
859
|
});
|
|
4284
860
|
g.add("slo_value", sloValue);
|
|
4285
861
|
g.add("slo_verified", sloVerified);
|
|
@@ -4304,12 +880,12 @@ function observabilityGraph(name, opts) {
|
|
|
4304
880
|
slo: vals[1]
|
|
4305
881
|
}),
|
|
4306
882
|
{
|
|
4307
|
-
meta:
|
|
883
|
+
meta: baseMeta2("observability", { stage: "output" })
|
|
4308
884
|
}
|
|
4309
885
|
);
|
|
4310
886
|
g.add("output", output);
|
|
4311
887
|
const fbReentry = state(null, {
|
|
4312
|
-
meta:
|
|
888
|
+
meta: baseMeta2("observability", { stage: "feedback_reentry" })
|
|
4313
889
|
});
|
|
4314
890
|
g.add("feedback_reentry", fbReentry);
|
|
4315
891
|
const fbCondition = derived(
|
|
@@ -4320,7 +896,7 @@ function observabilityGraph(name, opts) {
|
|
|
4320
896
|
return null;
|
|
4321
897
|
},
|
|
4322
898
|
{
|
|
4323
|
-
meta:
|
|
899
|
+
meta: baseMeta2("observability", { stage: "feedback_condition" })
|
|
4324
900
|
}
|
|
4325
901
|
);
|
|
4326
902
|
g.add("feedback_condition", fbCondition);
|
|
@@ -4342,7 +918,7 @@ function issueTrackerGraph(name, opts) {
|
|
|
4342
918
|
});
|
|
4343
919
|
const extractFn = opts.extract ?? defaultExtract;
|
|
4344
920
|
const extractNode = derived([opts.source], (vals) => extractFn(vals[0]), {
|
|
4345
|
-
meta:
|
|
921
|
+
meta: baseMeta2("issue_tracker", { stage: "extract" })
|
|
4346
922
|
});
|
|
4347
923
|
g.add("extract", extractNode);
|
|
4348
924
|
const verifyFn = opts.verify ?? (() => ({ valid: true }));
|
|
@@ -4353,12 +929,12 @@ function issueTrackerGraph(name, opts) {
|
|
|
4353
929
|
return { issue, verification: verifyFn(issue) };
|
|
4354
930
|
},
|
|
4355
931
|
{
|
|
4356
|
-
meta:
|
|
932
|
+
meta: baseMeta2("issue_tracker", { stage: "verify" })
|
|
4357
933
|
}
|
|
4358
934
|
);
|
|
4359
935
|
g.add("verify", verifyNode);
|
|
4360
936
|
const knownPatterns = state([], {
|
|
4361
|
-
meta:
|
|
937
|
+
meta: baseMeta2("issue_tracker", { stage: "known_patterns" })
|
|
4362
938
|
});
|
|
4363
939
|
g.add("known_patterns", knownPatterns);
|
|
4364
940
|
const detectFn = opts.detectRegression ?? (() => ({ regression: false }));
|
|
@@ -4369,7 +945,7 @@ function issueTrackerGraph(name, opts) {
|
|
|
4369
945
|
const known = vals[1];
|
|
4370
946
|
return { issue, regression: detectFn(issue, known) };
|
|
4371
947
|
},
|
|
4372
|
-
{ meta:
|
|
948
|
+
{ meta: baseMeta2("issue_tracker", { stage: "regression" }) }
|
|
4373
949
|
);
|
|
4374
950
|
g.add("regression", regressionNode);
|
|
4375
951
|
const severitySignal = derived([extractNode], (vals) => {
|
|
@@ -4395,11 +971,11 @@ function issueTrackerGraph(name, opts) {
|
|
|
4395
971
|
regression: vals[1],
|
|
4396
972
|
priority: vals[2]
|
|
4397
973
|
}),
|
|
4398
|
-
{ meta:
|
|
974
|
+
{ meta: baseMeta2("issue_tracker", { stage: "output" }) }
|
|
4399
975
|
);
|
|
4400
976
|
g.add("output", output);
|
|
4401
977
|
const fbReentry = state(null, {
|
|
4402
|
-
meta:
|
|
978
|
+
meta: baseMeta2("issue_tracker", { stage: "feedback_reentry" })
|
|
4403
979
|
});
|
|
4404
980
|
g.add("feedback_reentry", fbReentry);
|
|
4405
981
|
const fbCondition = derived(
|
|
@@ -4413,7 +989,7 @@ function issueTrackerGraph(name, opts) {
|
|
|
4413
989
|
return null;
|
|
4414
990
|
},
|
|
4415
991
|
{
|
|
4416
|
-
meta:
|
|
992
|
+
meta: baseMeta2("issue_tracker", { stage: "feedback_condition" })
|
|
4417
993
|
}
|
|
4418
994
|
);
|
|
4419
995
|
g.add("feedback_condition", fbCondition);
|
|
@@ -4432,7 +1008,7 @@ function contentModerationGraph(name, opts) {
|
|
|
4432
1008
|
});
|
|
4433
1009
|
const classifyFn = opts.classify ?? defaultClassify;
|
|
4434
1010
|
const classifyNode = derived([opts.source], (vals) => classifyFn(vals[0]), {
|
|
4435
|
-
meta:
|
|
1011
|
+
meta: baseMeta2("content_moderation", { stage: "classify" })
|
|
4436
1012
|
});
|
|
4437
1013
|
g.add("classify", classifyNode);
|
|
4438
1014
|
const strat = stratify("stratify", classifyNode, [
|
|
@@ -4467,7 +1043,7 @@ function contentModerationGraph(name, opts) {
|
|
|
4467
1043
|
const policy2 = state(
|
|
4468
1044
|
{},
|
|
4469
1045
|
{
|
|
4470
|
-
meta:
|
|
1046
|
+
meta: baseMeta2("content_moderation", {
|
|
4471
1047
|
stage: "policy",
|
|
4472
1048
|
access: "both",
|
|
4473
1049
|
description: "Moderation policy rules \u2014 updated via feedback"
|
|
@@ -4499,7 +1075,7 @@ function contentModerationGraph(name, opts) {
|
|
|
4499
1075
|
classification: vals[0],
|
|
4500
1076
|
priority: vals[1]
|
|
4501
1077
|
}),
|
|
4502
|
-
{ meta:
|
|
1078
|
+
{ meta: baseMeta2("content_moderation", { stage: "output" }) }
|
|
4503
1079
|
);
|
|
4504
1080
|
g.add("output", output);
|
|
4505
1081
|
const fbCondition = derived(
|
|
@@ -4515,7 +1091,7 @@ function contentModerationGraph(name, opts) {
|
|
|
4515
1091
|
return null;
|
|
4516
1092
|
},
|
|
4517
1093
|
{
|
|
4518
|
-
meta:
|
|
1094
|
+
meta: baseMeta2("content_moderation", { stage: "feedback_condition" })
|
|
4519
1095
|
}
|
|
4520
1096
|
);
|
|
4521
1097
|
g.add("feedback_condition", fbCondition);
|
|
@@ -4535,7 +1111,7 @@ function dataQualityGraph(name, opts) {
|
|
|
4535
1111
|
const validateNode = derived(
|
|
4536
1112
|
[opts.source],
|
|
4537
1113
|
(vals) => vals[0] != null ? validateFn(vals[0]) : void 0,
|
|
4538
|
-
{ meta:
|
|
1114
|
+
{ meta: baseMeta2("data_quality", { stage: "validate" }) }
|
|
4539
1115
|
);
|
|
4540
1116
|
g.add("validate", validateNode);
|
|
4541
1117
|
const detectAnomalyFn = opts.detectAnomaly ?? ((record) => ({
|
|
@@ -4546,11 +1122,11 @@ function dataQualityGraph(name, opts) {
|
|
|
4546
1122
|
const anomalyNode = derived(
|
|
4547
1123
|
[opts.source],
|
|
4548
1124
|
(vals) => vals[0] != null ? detectAnomalyFn(vals[0]) : void 0,
|
|
4549
|
-
{ meta:
|
|
1125
|
+
{ meta: baseMeta2("data_quality", { stage: "anomaly" }) }
|
|
4550
1126
|
);
|
|
4551
1127
|
g.add("anomaly", anomalyNode);
|
|
4552
1128
|
const baseline = state(null, {
|
|
4553
|
-
meta:
|
|
1129
|
+
meta: baseMeta2("data_quality", {
|
|
4554
1130
|
stage: "baseline",
|
|
4555
1131
|
description: "Rolling baseline for drift detection"
|
|
4556
1132
|
})
|
|
@@ -4570,7 +1146,7 @@ function dataQualityGraph(name, opts) {
|
|
|
4570
1146
|
const driftNode = derived(
|
|
4571
1147
|
[opts.source, baseline],
|
|
4572
1148
|
(vals) => detectDriftFn(vals[0], vals[1]),
|
|
4573
|
-
{ meta:
|
|
1149
|
+
{ meta: baseMeta2("data_quality", { stage: "drift" }) }
|
|
4574
1150
|
);
|
|
4575
1151
|
g.add("drift", driftNode);
|
|
4576
1152
|
const suggestFn = opts.suggest ?? (() => null);
|
|
@@ -4580,7 +1156,7 @@ function dataQualityGraph(name, opts) {
|
|
|
4580
1156
|
validation: vals[0],
|
|
4581
1157
|
anomaly: vals[1]
|
|
4582
1158
|
}),
|
|
4583
|
-
{ meta:
|
|
1159
|
+
{ meta: baseMeta2("data_quality", { stage: "remediate" }) }
|
|
4584
1160
|
);
|
|
4585
1161
|
g.add("remediate", remediateNode);
|
|
4586
1162
|
const output = derived(
|
|
@@ -4591,11 +1167,11 @@ function dataQualityGraph(name, opts) {
|
|
|
4591
1167
|
drift: vals[2],
|
|
4592
1168
|
remediation: vals[3]
|
|
4593
1169
|
}),
|
|
4594
|
-
{ meta:
|
|
1170
|
+
{ meta: baseMeta2("data_quality", { stage: "output" }) }
|
|
4595
1171
|
);
|
|
4596
1172
|
g.add("output", output);
|
|
4597
1173
|
const validationRules = state([], {
|
|
4598
|
-
meta:
|
|
1174
|
+
meta: baseMeta2("data_quality", { stage: "validation_rules" })
|
|
4599
1175
|
});
|
|
4600
1176
|
g.add("validation_rules", validationRules);
|
|
4601
1177
|
const fbCondition = derived(
|
|
@@ -4606,7 +1182,7 @@ function dataQualityGraph(name, opts) {
|
|
|
4606
1182
|
return null;
|
|
4607
1183
|
},
|
|
4608
1184
|
{
|
|
4609
|
-
meta:
|
|
1185
|
+
meta: baseMeta2("data_quality", { stage: "feedback_condition" })
|
|
4610
1186
|
}
|
|
4611
1187
|
);
|
|
4612
1188
|
g.add("feedback_condition", fbCondition);
|
|
@@ -4793,7 +1369,7 @@ function levenshtein(a, b) {
|
|
|
4793
1369
|
}
|
|
4794
1370
|
return dp[m][n];
|
|
4795
1371
|
}
|
|
4796
|
-
var
|
|
1372
|
+
var VALID_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
4797
1373
|
"state",
|
|
4798
1374
|
"producer",
|
|
4799
1375
|
"derived",
|
|
@@ -4880,9 +1456,9 @@ function validateSpec(spec) {
|
|
|
4880
1456
|
continue;
|
|
4881
1457
|
}
|
|
4882
1458
|
const n = raw;
|
|
4883
|
-
if (typeof n.type !== "string" || !
|
|
1459
|
+
if (typeof n.type !== "string" || !VALID_NODE_TYPES.has(n.type)) {
|
|
4884
1460
|
errors.push(
|
|
4885
|
-
`Node "${name}": invalid type "${String(n.type)}" (expected: ${[...
|
|
1461
|
+
`Node "${name}": invalid type "${String(n.type)}" (expected: ${[...VALID_NODE_TYPES].join(", ")})`
|
|
4886
1462
|
);
|
|
4887
1463
|
continue;
|
|
4888
1464
|
}
|
|
@@ -5507,7 +2083,7 @@ Rules:
|
|
|
5507
2083
|
- Use "feedback" for bounded cycles where a derived value writes back to a state node.
|
|
5508
2084
|
- meta.description is required for every node.
|
|
5509
2085
|
- Return ONLY valid JSON, no markdown fences or commentary.`;
|
|
5510
|
-
function
|
|
2086
|
+
function stripFences(text) {
|
|
5511
2087
|
const match = text.match(/^```(?:json)?\s*([\s\S]*?)\s*```[\s\S]*$/);
|
|
5512
2088
|
return match ? match[1] : text;
|
|
5513
2089
|
}
|
|
@@ -5537,7 +2113,7 @@ ${opts.systemPromptExtra}`;
|
|
|
5537
2113
|
const response = await rawResult;
|
|
5538
2114
|
let content = response.content.trim();
|
|
5539
2115
|
if (content.startsWith("```")) {
|
|
5540
|
-
content =
|
|
2116
|
+
content = stripFences(content);
|
|
5541
2117
|
}
|
|
5542
2118
|
let parsed;
|
|
5543
2119
|
try {
|
|
@@ -5608,7 +2184,7 @@ Return the complete modified GraphSpec as JSON.`
|
|
|
5608
2184
|
const response = await rawResult;
|
|
5609
2185
|
let content = response.content.trim();
|
|
5610
2186
|
if (content.startsWith("```")) {
|
|
5611
|
-
content =
|
|
2187
|
+
content = stripFences(content);
|
|
5612
2188
|
}
|
|
5613
2189
|
let parsed;
|
|
5614
2190
|
try {
|
|
@@ -5686,7 +2262,7 @@ function guardedExecution(target, opts) {
|
|
|
5686
2262
|
// src/patterns/harness/index.ts
|
|
5687
2263
|
var harness_exports = {};
|
|
5688
2264
|
__export(harness_exports, {
|
|
5689
|
-
DEFAULT_DECAY_RATE: () =>
|
|
2265
|
+
DEFAULT_DECAY_RATE: () => DEFAULT_DECAY_RATE,
|
|
5690
2266
|
DEFAULT_QUEUE_CONFIGS: () => DEFAULT_QUEUE_CONFIGS,
|
|
5691
2267
|
DEFAULT_SEVERITY_WEIGHTS: () => DEFAULT_SEVERITY_WEIGHTS,
|
|
5692
2268
|
HarnessGraph: () => HarnessGraph,
|
|
@@ -5729,28 +2305,28 @@ function evalIntakeBridge(evalSource2, intakeTopic, opts) {
|
|
|
5729
2305
|
if (results == null) return;
|
|
5730
2306
|
const runs = Array.isArray(results) ? results : [results];
|
|
5731
2307
|
for (const run of runs) {
|
|
5732
|
-
for (const
|
|
5733
|
-
if (
|
|
5734
|
-
if (!
|
|
2308
|
+
for (const task of run.tasks) {
|
|
2309
|
+
if (task.valid && task.judge_scores?.every((s) => s.pass)) continue;
|
|
2310
|
+
if (!task.valid && (!task.judge_scores || task.judge_scores.length === 0)) {
|
|
5735
2311
|
intakeTopic.publish({
|
|
5736
2312
|
source: "eval",
|
|
5737
|
-
summary: `Task ${
|
|
2313
|
+
summary: `Task ${task.task_id} invalid (model: ${run.model})`,
|
|
5738
2314
|
evidence: `Run ${run.run_id}: task produced invalid output`,
|
|
5739
2315
|
affectsAreas: ["graphspec"],
|
|
5740
|
-
affectsEvalTasks: [
|
|
2316
|
+
affectsEvalTasks: [task.task_id],
|
|
5741
2317
|
severity: defaultSeverity
|
|
5742
2318
|
});
|
|
5743
2319
|
continue;
|
|
5744
2320
|
}
|
|
5745
|
-
if (
|
|
5746
|
-
for (const score of
|
|
2321
|
+
if (task.judge_scores) {
|
|
2322
|
+
for (const score of task.judge_scores) {
|
|
5747
2323
|
if (score.pass) continue;
|
|
5748
2324
|
intakeTopic.publish({
|
|
5749
2325
|
source: "eval",
|
|
5750
|
-
summary: `${
|
|
2326
|
+
summary: `${task.task_id}: ${score.claim} (model: ${run.model})`,
|
|
5751
2327
|
evidence: score.reasoning,
|
|
5752
2328
|
affectsAreas: ["graphspec"],
|
|
5753
|
-
affectsEvalTasks: [
|
|
2329
|
+
affectsEvalTasks: [task.task_id],
|
|
5754
2330
|
severity: defaultSeverity
|
|
5755
2331
|
});
|
|
5756
2332
|
}
|
|
@@ -5855,9 +2431,9 @@ function codeChangeBridge(source, intakeTopic, parser, opts) {
|
|
|
5855
2431
|
{ name: opts?.name ?? "code-change-bridge" }
|
|
5856
2432
|
);
|
|
5857
2433
|
}
|
|
5858
|
-
function notifyEffect(
|
|
2434
|
+
function notifyEffect(topic, transport, opts) {
|
|
5859
2435
|
return effect(
|
|
5860
|
-
[
|
|
2436
|
+
[topic.latest],
|
|
5861
2437
|
([item]) => {
|
|
5862
2438
|
if (item == null) return;
|
|
5863
2439
|
void transport(item);
|
|
@@ -5889,7 +2465,7 @@ var DEFAULT_SEVERITY_WEIGHTS = {
|
|
|
5889
2465
|
medium: 40,
|
|
5890
2466
|
low: 10
|
|
5891
2467
|
};
|
|
5892
|
-
var
|
|
2468
|
+
var DEFAULT_DECAY_RATE = Math.LN2 / (7 * 24 * 3600);
|
|
5893
2469
|
var DEFAULT_QUEUE_CONFIGS = {
|
|
5894
2470
|
"auto-fix": { gated: false },
|
|
5895
2471
|
"needs-decision": { gated: true },
|
|
@@ -5945,7 +2521,7 @@ function strategyModel() {
|
|
|
5945
2521
|
}
|
|
5946
2522
|
function priorityScore(item, strategy, lastInteractionNs, urgency, signals) {
|
|
5947
2523
|
const severityWeights = { ...DEFAULT_SEVERITY_WEIGHTS, ...signals?.severityWeights };
|
|
5948
|
-
const decayRate = signals?.decayRate ??
|
|
2524
|
+
const decayRate = signals?.decayRate ?? DEFAULT_DECAY_RATE;
|
|
5949
2525
|
const effectivenessThreshold = signals?.effectivenessThreshold ?? 0.7;
|
|
5950
2526
|
const effectivenessBoost = signals?.effectivenessBoost ?? 15;
|
|
5951
2527
|
const deps = [item, strategy, lastInteractionNs];
|
|
@@ -6089,8 +2665,8 @@ function harnessLoop(name, opts) {
|
|
|
6089
2665
|
if (!classification || !classification.route) return;
|
|
6090
2666
|
const intakeItem = triagePair?.[0];
|
|
6091
2667
|
const merged = { ...intakeItem, ...classification };
|
|
6092
|
-
const
|
|
6093
|
-
if (
|
|
2668
|
+
const topic = queueTopics.get(merged.route);
|
|
2669
|
+
if (topic) topic.publish(merged);
|
|
6094
2670
|
});
|
|
6095
2671
|
const routerUnsub = router.subscribe(() => {
|
|
6096
2672
|
});
|
|
@@ -6098,9 +2674,9 @@ function harnessLoop(name, opts) {
|
|
|
6098
2674
|
const gateControllers = /* @__PURE__ */ new Map();
|
|
6099
2675
|
for (const route of QUEUE_NAMES) {
|
|
6100
2676
|
const config = queueConfigs.get(route);
|
|
6101
|
-
const
|
|
2677
|
+
const topic = queueTopics.get(route);
|
|
6102
2678
|
if (config.gated) {
|
|
6103
|
-
gateGraph.add(`${route}/source`,
|
|
2679
|
+
gateGraph.add(`${route}/source`, topic.latest);
|
|
6104
2680
|
const ctrl = gate(gateGraph, `${route}/gate`, `${route}/source`, {
|
|
6105
2681
|
maxPending: config.maxPending,
|
|
6106
2682
|
startOpen: config.startOpen
|
|
@@ -6239,8 +2815,8 @@ function harnessLoop(name, opts) {
|
|
|
6239
2815
|
harness.add("verify", verifyNode);
|
|
6240
2816
|
harness.add("strategy", strategy.node);
|
|
6241
2817
|
harness.mount("intake", intake);
|
|
6242
|
-
for (const [route,
|
|
6243
|
-
harness.mount(`queue/${route}`,
|
|
2818
|
+
for (const [route, topic] of queueTopics) {
|
|
2819
|
+
harness.mount(`queue/${route}`, topic);
|
|
6244
2820
|
}
|
|
6245
2821
|
harness.mount("gates", gateGraph);
|
|
6246
2822
|
harness.mount("retry-input", retryTopic);
|
|
@@ -6252,8 +2828,8 @@ function harnessLoop(name, opts) {
|
|
|
6252
2828
|
function harnessProfile(harness, opts) {
|
|
6253
2829
|
const base = graphProfile(harness, opts);
|
|
6254
2830
|
const queueDepths = {};
|
|
6255
|
-
for (const [route,
|
|
6256
|
-
queueDepths[route] =
|
|
2831
|
+
for (const [route, topic] of harness.queues) {
|
|
2832
|
+
queueDepths[route] = topic.retained().length;
|
|
6257
2833
|
}
|
|
6258
2834
|
return {
|
|
6259
2835
|
...base,
|
|
@@ -6746,11 +3322,11 @@ ${catalogValidation.errors.join("\n")}`,
|
|
|
6746
3322
|
}
|
|
6747
3323
|
|
|
6748
3324
|
// src/patterns/surface/reduce.ts
|
|
6749
|
-
var
|
|
3325
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
6750
3326
|
async function runReduction(spec, input, opts) {
|
|
6751
3327
|
const inputPath = opts?.inputPath ?? "input";
|
|
6752
3328
|
const outputPath = opts?.outputPath ?? "output";
|
|
6753
|
-
const timeoutMs = opts?.timeoutMs ??
|
|
3329
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
6754
3330
|
const graph = createGraph(spec, { catalog: opts?.catalog });
|
|
6755
3331
|
let outputNode;
|
|
6756
3332
|
try {
|