@cesarus777/opencode-cgp 0.0.0-stage → 0.1.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/core.js ADDED
@@ -0,0 +1,826 @@
1
+ // @bun
2
+ // src/types.ts
3
+ class NoopBudgetController {
4
+ evaluate(_input) {
5
+ return { type: "continue" };
6
+ }
7
+ }
8
+ // src/config.ts
9
+ function object(value, path) {
10
+ if (!value || typeof value !== "object" || Array.isArray(value))
11
+ throw new Error(`${path}: expected object`);
12
+ return value;
13
+ }
14
+ function keys(o, allowed, path) {
15
+ for (const key of Object.keys(o))
16
+ if (!allowed.includes(key))
17
+ throw new Error(`${path}.${key}: unknown option`);
18
+ }
19
+ function number(v, fallback, path, min = 0) {
20
+ if (v === undefined)
21
+ return fallback;
22
+ if (typeof v !== "number" || !Number.isSafeInteger(v) || v < min)
23
+ throw new Error(`${path}: expected integer >= ${min}`);
24
+ return v;
25
+ }
26
+ function bool(v, fallback, path) {
27
+ if (v === undefined)
28
+ return fallback;
29
+ if (typeof v !== "boolean")
30
+ throw new Error(`${path}: expected boolean`);
31
+ return v;
32
+ }
33
+ function selector(v, path) {
34
+ if (typeof v !== "string" || !v.length || v.length > 256 || /[\[\]{}\\]/.test(v))
35
+ throw new Error(`${path}: expected selector (only * and ? wildcards)`);
36
+ return v;
37
+ }
38
+ function threshold(v, path) {
39
+ const o = object(v, path);
40
+ keys(o, ["type", "value"], path);
41
+ if (o.type !== "tokens" && o.type !== "ratio" && o.type !== "remaining")
42
+ throw new Error(`${path}.type: invalid threshold`);
43
+ if (typeof o.value !== "number" || !Number.isFinite(o.value) || o.value <= 0 || (o.type === "ratio" ? o.value > 1 : !Number.isSafeInteger(o.value)))
44
+ throw new Error(`${path}.value: invalid threshold`);
45
+ return { type: o.type, value: o.value };
46
+ }
47
+ function parseConfig(value = {}) {
48
+ const o = object(value, "options");
49
+ keys(o, ["enabled", "mode", "defaults", "policies", "safety", "telemetry"], "options");
50
+ if (o.mode !== undefined && o.mode !== "observe" && o.mode !== "enforce")
51
+ throw new Error("mode: expected observe or enforce");
52
+ const d = object(o.defaults ?? {}, "defaults");
53
+ keys(d, ["threshold"], "defaults");
54
+ const s = object(o.safety ?? {}, "safety");
55
+ keys(s, [
56
+ "cooldownMs",
57
+ "minReclaimTokens",
58
+ "maxConsecutiveFailures",
59
+ "pendingTimeoutMs",
60
+ "staleMs",
61
+ "maxSessions",
62
+ "sessionTtlMs",
63
+ "outputReserveTokens"
64
+ ], "safety");
65
+ const t = object(o.telemetry ?? {}, "telemetry");
66
+ keys(t, ["enabled", "jsonl", "path", "maxBytes"], "telemetry");
67
+ if (t.path !== undefined && (typeof t.path !== "string" || !t.path.startsWith("/")))
68
+ throw new Error("telemetry.path: expected absolute path");
69
+ if (o.policies !== undefined && (!Array.isArray(o.policies) || o.policies.length > 256))
70
+ throw new Error("policies: expected array of at most 256 policies");
71
+ const policies = (o.policies ?? []).map((v, i) => {
72
+ const p = object(v, `policies[${i}]`);
73
+ keys(p, ["match", "threshold"], `policies[${i}]`);
74
+ const m = object(p.match, "match");
75
+ keys(m, ["role", "model", "agent"], "match");
76
+ if (!Object.keys(m).length)
77
+ throw new Error("match: at least one selector required");
78
+ const match = {};
79
+ if (m.role !== undefined) {
80
+ if (m.role !== "main" && m.role !== "subagent")
81
+ throw new Error("match.role: invalid");
82
+ match.role = m.role;
83
+ }
84
+ if (m.model !== undefined) {
85
+ match.model = selector(m.model, "match.model");
86
+ if (!match.model.includes("/"))
87
+ throw new Error("match.model: use provider/model");
88
+ }
89
+ if (m.agent !== undefined)
90
+ match.agent = selector(m.agent, "match.agent");
91
+ return { match, threshold: threshold(p.threshold, "threshold") };
92
+ });
93
+ const config = {
94
+ enabled: bool(o.enabled, true, "enabled"),
95
+ mode: o.mode ?? "observe",
96
+ defaults: {
97
+ threshold: threshold(d.threshold ?? { type: "tokens", value: 131072 }, "defaults.threshold")
98
+ },
99
+ policies,
100
+ safety: {
101
+ cooldownMs: number(s.cooldownMs, 60000, "cooldownMs"),
102
+ minReclaimTokens: number(s.minReclaimTokens, 8192, "minReclaimTokens"),
103
+ maxConsecutiveFailures: number(s.maxConsecutiveFailures, 3, "maxConsecutiveFailures", 1),
104
+ pendingTimeoutMs: number(s.pendingTimeoutMs, 300000, "pendingTimeoutMs", 1),
105
+ staleMs: number(s.staleMs, 300000, "staleMs", 1),
106
+ maxSessions: number(s.maxSessions, 1024, "maxSessions", 1),
107
+ sessionTtlMs: number(s.sessionTtlMs, 86400000, "sessionTtlMs", 1),
108
+ ...s.outputReserveTokens === undefined ? {} : {
109
+ outputReserveTokens: number(s.outputReserveTokens, 0, "outputReserveTokens")
110
+ }
111
+ },
112
+ telemetry: {
113
+ enabled: bool(t.enabled, true, "telemetry.enabled"),
114
+ jsonl: bool(t.jsonl, true, "telemetry.jsonl"),
115
+ maxBytes: number(t.maxBytes, 8 * 1024 * 1024, "maxBytes", 4096),
116
+ ...typeof t.path === "string" ? { path: t.path } : {}
117
+ }
118
+ };
119
+ for (const policy of [config.defaults, ...config.policies]) {
120
+ if (policy.threshold.type === "tokens" && config.safety.minReclaimTokens >= policy.threshold.value)
121
+ throw new Error("minReclaimTokens must be smaller than each absolute threshold");
122
+ }
123
+ if (!config.telemetry.jsonl && config.telemetry.path)
124
+ throw new Error("telemetry.path requires jsonl");
125
+ return config;
126
+ }
127
+ // src/policy.ts
128
+ function glob(pattern, text) {
129
+ let p = 0, t = 0, star = -1, retry = 0;
130
+ while (t < text.length) {
131
+ if (pattern[p] === "?" || pattern[p] === text[t]) {
132
+ p++;
133
+ t++;
134
+ } else if (pattern[p] === "*") {
135
+ star = p++;
136
+ retry = t;
137
+ } else if (star >= 0) {
138
+ p = star + 1;
139
+ t = ++retry;
140
+ } else
141
+ return false;
142
+ }
143
+ while (pattern[p] === "*")
144
+ p++;
145
+ return p === pattern.length;
146
+ }
147
+
148
+ class PolicyResolver {
149
+ config;
150
+ constructor(config) {
151
+ this.config = config;
152
+ }
153
+ resolve(identity) {
154
+ let result = {
155
+ threshold: this.config.defaults.threshold,
156
+ policy: "defaults"
157
+ };
158
+ this.config.policies.forEach((p, i) => {
159
+ const m = p.match;
160
+ if (m.role && m.role !== (identity.parentId ? "subagent" : "main"))
161
+ return;
162
+ if (m.agent && (!identity.agent || !glob(m.agent, identity.agent)))
163
+ return;
164
+ if (m.model && (!identity.model || !glob(m.model, identity.model)))
165
+ return;
166
+ result = { threshold: p.threshold, policy: `policies[${i}]` };
167
+ });
168
+ return result;
169
+ }
170
+ }
171
+ function usableInput(limits, reserve) {
172
+ if (!limits)
173
+ return;
174
+ const valid = (n) => n !== undefined && Number.isFinite(n) && n > 0;
175
+ const output = reserve ?? (valid(limits.output) ? limits.output : undefined);
176
+ const contextBudget = valid(limits.context) && output !== undefined ? Math.max(0, limits.context - output) : undefined;
177
+ if (valid(limits.input))
178
+ return contextBudget === undefined ? limits.input : Math.min(limits.input, contextBudget);
179
+ return contextBudget;
180
+ }
181
+ function evaluateThreshold(threshold2, measurement, limits, now, staleMs, model, policy, reserve) {
182
+ const budget = usableInput(limits, reserve);
183
+ const base = {
184
+ crossed: false,
185
+ reason: "missing-usage",
186
+ policy,
187
+ ...budget === undefined ? {} : { budget }
188
+ };
189
+ const trigger = threshold2.type === "tokens" ? threshold2.value : budget === undefined ? undefined : threshold2.type === "ratio" ? Math.ceil(budget * threshold2.value) : budget - threshold2.value;
190
+ if (trigger !== undefined)
191
+ base.threshold = trigger;
192
+ if (!measurement)
193
+ return base;
194
+ if (model !== measurement.model)
195
+ return { ...base, reason: "model-changed" };
196
+ if (!Number.isFinite(measurement.tokens) || measurement.tokens < 0)
197
+ return { ...base, reason: "invalid-usage" };
198
+ if (now - measurement.at > staleMs || measurement.at > now)
199
+ return { ...base, reason: "stale-usage" };
200
+ if (budget !== undefined && budget > 0)
201
+ base.percentage = measurement.tokens / budget * 100;
202
+ if (trigger === undefined)
203
+ return { ...base, reason: "missing-model-budget" };
204
+ if (trigger <= 0)
205
+ return { ...base, reason: "incompatible-model-budget" };
206
+ const crossed = threshold2.type === "remaining" ? measurement.tokens > trigger : measurement.tokens >= trigger;
207
+ return {
208
+ ...base,
209
+ crossed,
210
+ reason: crossed ? "threshold-crossed" : "below-threshold"
211
+ };
212
+ }
213
+ // src/monitor.ts
214
+ function promptTokens(t) {
215
+ const fields = [t.input, t.output, t.reasoning, t.cache.read, t.cache.write];
216
+ if (fields.some((x) => !Number.isSafeInteger(x) || x < 0))
217
+ return;
218
+ const total = t.input + t.cache.read + t.cache.write;
219
+ return total > 0 && Number.isSafeInteger(total) ? total : undefined;
220
+ }
221
+
222
+ class ContextMonitor {
223
+ config;
224
+ scheduler;
225
+ sink;
226
+ now;
227
+ budget;
228
+ sessions = new Map;
229
+ resolver;
230
+ constructor(config, scheduler, sink, now = Date.now, budget = new NoopBudgetController) {
231
+ this.config = config;
232
+ this.scheduler = scheduler;
233
+ this.sink = sink;
234
+ this.now = now;
235
+ this.budget = budget;
236
+ this.resolver = new PolicyResolver(config);
237
+ }
238
+ touch(identity, limits) {
239
+ let s = this.sessions.get(identity.sessionId);
240
+ if (!s) {
241
+ this.cleanup();
242
+ if (this.sessions.size >= this.config.safety.maxSessions) {
243
+ this.sink.emit({
244
+ time: this.now(),
245
+ type: "capacity-exceeded",
246
+ detail: "New session not monitored; increase safety.maxSessions"
247
+ });
248
+ return;
249
+ }
250
+ s = {
251
+ ...identity,
252
+ lastSeen: this.now(),
253
+ sequence: -1,
254
+ active: false,
255
+ usage: {
256
+ cumulativeScope: "unknown",
257
+ peak: 0,
258
+ modelRequests: 0,
259
+ toolCalls: 0,
260
+ countersSince: this.now()
261
+ },
262
+ compaction: {
263
+ phase: "idle",
264
+ armed: true,
265
+ count: 0,
266
+ failures: 0,
267
+ consecutiveFailures: 0
268
+ }
269
+ };
270
+ this.sessions.set(identity.sessionId, s);
271
+ }
272
+ if (identity.model && identity.model !== s.model) {
273
+ delete s.usage.current;
274
+ delete s.limits;
275
+ }
276
+ Object.assign(s, identity);
277
+ s.lastSeen = this.now();
278
+ if (limits)
279
+ s.limits = { ...limits };
280
+ return s;
281
+ }
282
+ accept(s, sequence) {
283
+ if (sequence <= s.sequence)
284
+ return false;
285
+ s.sequence = sequence;
286
+ s.lastSeen = this.now();
287
+ return true;
288
+ }
289
+ measure(s, measurement, cumulative) {
290
+ if (s.usage.current && measurement.at < s.usage.current.at)
291
+ return;
292
+ s.usage.current = measurement;
293
+ s.usage.peak = Math.max(s.usage.peak, measurement.tokens);
294
+ if (cumulative) {
295
+ s.usage.cumulative = cumulative;
296
+ s.usage.cumulativeScope = "session";
297
+ }
298
+ this.evaluate(s);
299
+ }
300
+ evaluate(s) {
301
+ const p = this.resolver.resolve(s);
302
+ s.decision = evaluateThreshold(p.threshold, s.usage.current, s.limits, this.now(), this.config.safety.staleMs, s.model, p.policy, this.config.safety.outputReserveTokens);
303
+ this.scheduler.measured(s);
304
+ const action = this.budget.evaluate({
305
+ session: s,
306
+ now: this.now(),
307
+ elapsedMs: this.now() - s.usage.countersSince
308
+ });
309
+ if (action.type !== "continue")
310
+ this.sink.emit({
311
+ time: this.now(),
312
+ type: `budget.${action.type}`,
313
+ session: s,
314
+ detail: "Advisory extension; only context policies execute in this release"
315
+ });
316
+ this.sink.emit({
317
+ time: this.now(),
318
+ type: this.config.mode === "observe" && s.decision.crossed ? "would-compact" : "usage",
319
+ session: s
320
+ });
321
+ this.scheduler.consider(s);
322
+ }
323
+ remove(id) {
324
+ const s = this.sessions.get(id);
325
+ if (s) {
326
+ s.active = false;
327
+ s.compaction.phase = "blocked";
328
+ s.compaction.blockedReason = "session-removed";
329
+ this.sink.emit({ time: this.now(), type: "session.removed", session: s });
330
+ this.sessions.delete(id);
331
+ }
332
+ }
333
+ cleanup() {
334
+ for (const [id, s] of this.sessions) {
335
+ this.scheduler.tick(s);
336
+ if (!s.active && ["idle", "blocked"].includes(s.compaction.phase) && this.now() - s.lastSeen > this.config.safety.sessionTtlMs)
337
+ this.remove(id);
338
+ }
339
+ }
340
+ async getUsage(id) {
341
+ const s = this.sessions.get(id);
342
+ if (!s)
343
+ throw new Error("Unknown session");
344
+ return structuredClone(s.usage);
345
+ }
346
+ }
347
+ // src/scheduler.ts
348
+ class CompactionScheduler {
349
+ config;
350
+ port;
351
+ sink;
352
+ now;
353
+ id;
354
+ stopped = false;
355
+ jobs = new Set;
356
+ constructor(config, port, sink, now = Date.now, id = () => `msg_${crypto.randomUUID().replaceAll("-", "")}`) {
357
+ this.config = config;
358
+ this.port = port;
359
+ this.sink = sink;
360
+ this.now = now;
361
+ this.id = id;
362
+ }
363
+ consider(s) {
364
+ if (this.stopped || !this.config.enabled || this.config.mode !== "enforce" || !s.decision?.crossed)
365
+ return;
366
+ const c = s.compaction, time = this.now();
367
+ if (c.phase !== "idle" || !c.armed || c.consecutiveFailures >= this.config.safety.maxConsecutiveFailures)
368
+ return;
369
+ if (c.lastAttempt !== undefined && time - c.lastAttempt < this.config.safety.cooldownMs)
370
+ return;
371
+ c.phase = "requesting";
372
+ c.lastAttempt = time;
373
+ c.requestId = this.id();
374
+ c.before = s.usage.current?.tokens;
375
+ c.beforeModel = s.model;
376
+ c.beforeConfidence = s.usage.current?.confidence;
377
+ c.armed = false;
378
+ this.emit("compaction.requesting", s);
379
+ const requestId = c.requestId;
380
+ const job = new Promise((resolve) => setTimeout(resolve, 0)).then(async () => {
381
+ if (this.stopped || c.phase !== "requesting")
382
+ return;
383
+ try {
384
+ await this.port.compact(s.sessionId, requestId);
385
+ if (c.phase === "requesting")
386
+ c.phase = "accepted";
387
+ this.emit("compaction.accepted", s);
388
+ } catch {
389
+ if (c.phase === "requesting") {
390
+ c.phase = "uncertain";
391
+ c.blockedReason = "admission-outcome-unknown";
392
+ c.failures++;
393
+ c.consecutiveFailures++;
394
+ this.emit("compaction.uncertain", s);
395
+ }
396
+ }
397
+ }).finally(() => this.jobs.delete(job));
398
+ this.jobs.add(job);
399
+ }
400
+ lifecycle(s, event, at) {
401
+ const c = s.compaction;
402
+ if (event === "started") {
403
+ if (c.phase !== "requesting" && c.phase !== "accepted") {
404
+ c.before = s.usage.current?.tokens;
405
+ c.beforeModel = s.model;
406
+ c.beforeConfidence = s.usage.current?.confidence;
407
+ }
408
+ c.phase = "running";
409
+ c.armed = false;
410
+ c.lastAttempt = at;
411
+ } else if (event === "ended") {
412
+ c.phase = "awaiting-measurement";
413
+ c.lastCompleted = at;
414
+ c.count++;
415
+ c.consecutiveFailures = 0;
416
+ delete s.usage.current;
417
+ } else {
418
+ c.failures++;
419
+ c.consecutiveFailures++;
420
+ c.lastAttempt = at;
421
+ c.armed = true;
422
+ c.phase = c.consecutiveFailures >= this.config.safety.maxConsecutiveFailures ? "blocked" : "idle";
423
+ c.blockedReason = "compaction-failed";
424
+ delete s.usage.current;
425
+ }
426
+ this.emit(`compaction.${event}`, s);
427
+ }
428
+ measured(s) {
429
+ const c = s.compaction, m = s.usage.current;
430
+ if (!m)
431
+ return;
432
+ if (c.phase === "awaiting-measurement") {
433
+ if (m.confidence !== "reported" || m.at < (c.lastCompleted ?? 0))
434
+ return;
435
+ if (c.beforeModel !== m.model || c.before === undefined || c.beforeConfidence !== "reported") {
436
+ c.phase = "blocked";
437
+ c.blockedReason = "reclaim-unverifiable";
438
+ } else if (c.before - m.tokens < this.config.safety.minReclaimTokens) {
439
+ c.phase = "blocked";
440
+ c.blockedReason = "insufficient-reclaim";
441
+ } else {
442
+ c.phase = "idle";
443
+ c.armed = false;
444
+ delete c.blockedReason;
445
+ }
446
+ }
447
+ const resetAt = (s.decision?.threshold ?? 0) - this.config.safety.minReclaimTokens;
448
+ if (m.tokens <= resetAt && (c.phase === "idle" || c.phase === "blocked" && c.blockedReason !== "compaction-failed")) {
449
+ c.armed = true;
450
+ c.phase = "idle";
451
+ delete c.blockedReason;
452
+ }
453
+ }
454
+ tick(s) {
455
+ const c = s.compaction;
456
+ if ((c.phase === "accepted" || c.phase === "running" || c.phase === "requesting") && this.now() - (c.lastAttempt ?? this.now()) > this.config.safety.pendingTimeoutMs) {
457
+ c.phase = "uncertain";
458
+ c.blockedReason = "completion-event-timeout";
459
+ this.emit("compaction.uncertain", s);
460
+ }
461
+ }
462
+ emit(type, session) {
463
+ this.sink.emit({ type, time: this.now(), session });
464
+ }
465
+ async flush() {
466
+ await Promise.all(this.jobs);
467
+ }
468
+ stop() {
469
+ this.stopped = true;
470
+ }
471
+ }
472
+ // src/estimate.ts
473
+ function estimate(value, maxChars = 2000000) {
474
+ let chars = 0, nodes = 0;
475
+ function visit(v, depth) {
476
+ if (++nodes > 1e5 || depth > 30 || chars > maxChars)
477
+ return false;
478
+ if (typeof v === "string")
479
+ chars += v.length;
480
+ else if (typeof v === "number" || typeof v === "boolean")
481
+ chars += 8;
482
+ else if (Array.isArray(v)) {
483
+ for (const item of v)
484
+ if (!visit(item, depth + 1))
485
+ return false;
486
+ } else if (v && typeof v === "object") {
487
+ if (ArrayBuffer.isView(v) || v instanceof ArrayBuffer)
488
+ return false;
489
+ for (const [key, item] of Object.entries(v)) {
490
+ chars += key.length + 4;
491
+ if (!visit(item, depth + 1))
492
+ return false;
493
+ }
494
+ }
495
+ return chars <= maxChars;
496
+ }
497
+ return visit(value, 0) ? Math.ceil(chars / 3) : undefined;
498
+ }
499
+ // src/snapshot.ts
500
+ function project(s, config, now, revision = 0) {
501
+ const p = new PolicyResolver(config).resolve(s);
502
+ const d = evaluateThreshold(p.threshold, s.usage.current, s.limits, now, config.safety.staleMs, s.model, p.policy, config.safety.outputReserveTokens);
503
+ const c = s.compaction;
504
+ return freezeSnapshot({
505
+ sessionId: s.sessionId,
506
+ revision,
507
+ observedAt: s.lastSeen,
508
+ ...s.parentId ? { parentId: s.parentId } : {},
509
+ ...s.agent ? { agent: s.agent } : {},
510
+ ...s.model ? { model: s.model } : {},
511
+ ...s.usage.current ? {
512
+ measurement: {
513
+ tokens: s.usage.current.tokens,
514
+ at: s.usage.current.at,
515
+ model: s.usage.current.model,
516
+ provenance: s.usage.current.provenance,
517
+ confidence: s.usage.current.confidence,
518
+ scope: s.usage.current.scope,
519
+ ...s.usage.current.requestId ? { requestId: s.usage.current.requestId } : {}
520
+ }
521
+ } : {},
522
+ ...s.limits ? {
523
+ limits: {
524
+ context: s.limits.context,
525
+ output: s.limits.output,
526
+ ...s.limits.input === undefined ? {} : { input: s.limits.input }
527
+ }
528
+ } : {},
529
+ ...d.budget === undefined ? {} : { capacityTokens: d.budget },
530
+ ...config.safety.outputReserveTokens === undefined && !s.limits ? {} : {
531
+ outputReserveTokens: config.safety.outputReserveTokens ?? s.limits?.output ?? 0
532
+ },
533
+ peakTokens: s.usage.peak,
534
+ policy: {
535
+ id: p.policy,
536
+ configured: { type: p.threshold.type, value: p.threshold.value },
537
+ ...d.threshold === undefined ? {} : { triggerTokens: d.threshold },
538
+ comparison: p.threshold.type === "remaining" ? ">" : ">=",
539
+ reason: d.reason,
540
+ crossed: d.crossed
541
+ },
542
+ compaction: {
543
+ phase: c.phase,
544
+ count: c.count,
545
+ armed: c.armed,
546
+ ...c.blockedReason ? { blockedReason: c.blockedReason } : {},
547
+ ...c.lastAttempt === undefined ? {} : {
548
+ lastAttempt: c.lastAttempt,
549
+ cooldownUntil: c.lastAttempt + config.safety.cooldownMs
550
+ },
551
+ ...c.lastCompleted === undefined ? {} : { lastCompleted: c.lastCompleted }
552
+ },
553
+ ...s.usage.cumulative ? {
554
+ cumulative: {
555
+ input: s.usage.cumulative.input,
556
+ output: s.usage.cumulative.output,
557
+ reasoning: s.usage.cumulative.reasoning,
558
+ cache: {
559
+ read: s.usage.cumulative.cache.read,
560
+ write: s.usage.cumulative.cache.write
561
+ }
562
+ }
563
+ } : {},
564
+ cumulativeScope: s.usage.cumulativeScope,
565
+ lastObservedActive: s.active
566
+ });
567
+ }
568
+
569
+ class SnapshotStore {
570
+ config;
571
+ sessions;
572
+ now;
573
+ epoch;
574
+ sequence = 0;
575
+ revision = 0;
576
+ rows = new Map;
577
+ deleted = new Set;
578
+ dirty = new Set;
579
+ listeners = new Set;
580
+ healthKey = "";
581
+ reasons = new Set;
582
+ constructor(config, sessions, now = Date.now, epoch = crypto.randomUUID()) {
583
+ this.config = config;
584
+ this.sessions = sessions;
585
+ this.now = now;
586
+ this.epoch = epoch;
587
+ }
588
+ health() {
589
+ return {
590
+ enabled: this.config.enabled,
591
+ mode: this.config.mode,
592
+ enforcement: !this.config.enabled ? "disabled" : this.reasons.has("event-stream-gap") || this.reasons.has("persistence-unavailable") ? "suspended" : this.config.mode === "observe" ? "observing" : "available",
593
+ reasons: [...this.reasons].sort(),
594
+ staleMs: this.config.safety.staleMs,
595
+ maxSessions: this.config.safety.maxSessions
596
+ };
597
+ }
598
+ degrade(reason) {
599
+ this.reasons.add(reason);
600
+ }
601
+ recover(reason) {
602
+ this.reasons.delete(reason);
603
+ }
604
+ mark(id) {
605
+ if (this.dirty.size < this.config.safety.maxSessions)
606
+ this.dirty.add(id);
607
+ }
608
+ markAll() {
609
+ for (const id of this.sessions().keys())
610
+ this.mark(id);
611
+ }
612
+ remove(id) {
613
+ if (this.rows.has(id))
614
+ this.deleted.add(id);
615
+ }
616
+ listen(fn) {
617
+ this.listeners.add(fn);
618
+ return () => {
619
+ this.listeners.delete(fn);
620
+ };
621
+ }
622
+ batch(upserts, removed) {
623
+ return {
624
+ schemaVersion: 1,
625
+ epoch: this.epoch,
626
+ sequence: this.sequence,
627
+ generatedAt: this.now(),
628
+ health: this.health(),
629
+ upserts,
630
+ removed
631
+ };
632
+ }
633
+ flush() {
634
+ const upserts = [], removed = [];
635
+ const resident = this.sessions();
636
+ for (const [id, old] of this.rows)
637
+ if (old.measurement && old.policy.reason !== "stale-usage" && this.now() - old.measurement.at > this.config.safety.staleMs)
638
+ this.mark(id);
639
+ for (const id of resident.keys())
640
+ if (!this.rows.has(id))
641
+ this.mark(id);
642
+ for (const id of this.dirty) {
643
+ const s = resident.get(id);
644
+ if (!s)
645
+ continue;
646
+ const next = project(s, this.config, this.now());
647
+ const old = this.rows.get(id);
648
+ if (old && JSON.stringify({ ...old, revision: 0 }) === JSON.stringify(next))
649
+ continue;
650
+ const row = freezeSnapshot({ ...next, revision: ++this.revision });
651
+ this.rows.set(id, row);
652
+ upserts.push(row);
653
+ }
654
+ for (const id of this.rows.keys())
655
+ if (!resident.has(id)) {
656
+ this.rows.delete(id);
657
+ removed.push({
658
+ sessionId: id,
659
+ revision: ++this.revision,
660
+ reason: this.deleted.has(id) ? "deleted" : "evicted"
661
+ });
662
+ }
663
+ this.deleted.clear();
664
+ this.dirty.clear();
665
+ const healthKey = JSON.stringify(this.health()), changed = healthKey !== this.healthKey;
666
+ this.healthKey = healthKey;
667
+ const changes = [
668
+ ...upserts.map((row) => ({ kind: "upsert", row })),
669
+ ...removed.map((row) => ({ kind: "remove", row }))
670
+ ];
671
+ for (let i = 0;i < Math.max(changes.length, changed ? 1 : 0); i += 128) {
672
+ this.sequence++;
673
+ const slice = changes.slice(i, i + 128);
674
+ const batch = this.batch(slice.flatMap((change) => change.kind === "upsert" ? [change.row] : []), slice.flatMap((change) => change.kind === "remove" ? [change.row] : []));
675
+ for (const fn of this.listeners) {
676
+ try {
677
+ fn(structuredClone(batch));
678
+ } catch {
679
+ }
680
+ }
681
+ }
682
+ }
683
+ read(input) {
684
+ this.flush();
685
+ const ids = [...new Set(input.sessionIds)];
686
+ return structuredClone({
687
+ ...this.batch(ids.flatMap((id) => {
688
+ const row = this.rows.get(id);
689
+ return row ? [row] : [];
690
+ }), []),
691
+ unavailable: ids.filter((id) => !this.rows.has(id)).map((sessionId) => ({ sessionId, reason: "not-monitored" }))
692
+ });
693
+ }
694
+ }
695
+ function freezeSnapshot(row) {
696
+ if (row.measurement)
697
+ Object.freeze(row.measurement);
698
+ if (row.limits)
699
+ Object.freeze(row.limits);
700
+ Object.freeze(row.policy.configured);
701
+ Object.freeze(row.policy);
702
+ Object.freeze(row.compaction);
703
+ if (row.cumulative) {
704
+ Object.freeze(row.cumulative.cache);
705
+ Object.freeze(row.cumulative);
706
+ }
707
+ return Object.freeze(row);
708
+ }
709
+ // src/family.ts
710
+ function metric(snapshot, now, staleMs) {
711
+ const m = snapshot?.measurement;
712
+ const freshness = !m ? "missing" : m.model !== snapshot?.model || m.at > now ? "invalid" : now - m.at > staleMs ? "stale" : "fresh";
713
+ const t = snapshot?.policy.triggerTokens;
714
+ const valid = freshness === "fresh" && m !== undefined;
715
+ const pressureRatio = valid && t !== undefined && t > 0 ? m.tokens / t : undefined;
716
+ const crossed = valid && t !== undefined && t > 0 && (snapshot?.policy.comparison === ">" ? m.tokens > t : m.tokens >= t);
717
+ return {
718
+ freshness,
719
+ pressure: pressureRatio === undefined ? "unknown" : crossed ? "crossed" : pressureRatio >= 0.9 ? "near" : "normal",
720
+ ...pressureRatio === undefined ? {} : { pressureRatio },
721
+ ...valid && snapshot?.capacityTokens && snapshot.capacityTokens > 0 ? { utilizationPercent: m.tokens / snapshot.capacityTokens * 100 } : {},
722
+ ...valid && t !== undefined ? { triggerDeltaTokens: t - m.tokens } : {}
723
+ };
724
+ }
725
+ function familyRows(nodes, snapshots, current, now, staleMs, expanded = new Set, limit = 1024, timing, stableOrder = []) {
726
+ const byId = new Map(nodes.map((n) => [n.sessionId, n]));
727
+ const children = new Map;
728
+ for (const n of byId.values()) {
729
+ const parent = n.parentId && byId.has(n.parentId) ? n.parentId : "";
730
+ const list = children.get(parent) ?? [];
731
+ list.push(n);
732
+ children.set(parent, list);
733
+ }
734
+ const metrics = new Map([...byId.keys()].map((id) => [
735
+ id,
736
+ metric(snapshots.get(id), timing?.(id).now ?? now, timing?.(id).staleMs ?? staleMs)
737
+ ]));
738
+ const protectedIds = new Set;
739
+ let ancestor = byId.get(current);
740
+ while (ancestor && !protectedIds.has(ancestor.sessionId)) {
741
+ protectedIds.add(ancestor.sessionId);
742
+ ancestor = ancestor.parentId ? byId.get(ancestor.parentId) : undefined;
743
+ }
744
+ const scores = new Map, sensitive = new Map, sizes = new Map;
745
+ const calculate = (n, seen) => {
746
+ if (seen.has(n.sessionId))
747
+ return -1;
748
+ if (scores.has(n.sessionId))
749
+ return scores.get(n.sessionId) ?? -1;
750
+ const path = new Set(seen).add(n.sessionId), m = metrics.get(n.sessionId);
751
+ let score = m?.pressureRatio ?? -1, size = 1;
752
+ let keep = protectedIds.has(n.sessionId) || n.status === "running" || m?.pressure === "crossed" || ["blocked", "uncertain"].includes(snapshots.get(n.sessionId)?.compaction.phase ?? "");
753
+ for (const child of children.get(n.sessionId) ?? []) {
754
+ score = Math.max(score, calculate(child, path));
755
+ size += sizes.get(child.sessionId) ?? 0;
756
+ keep ||= sensitive.get(child.sessionId) ?? false;
757
+ }
758
+ scores.set(n.sessionId, score);
759
+ sizes.set(n.sessionId, size);
760
+ sensitive.set(n.sessionId, keep);
761
+ return score;
762
+ };
763
+ for (const n of byId.values())
764
+ calculate(n, new Set);
765
+ const visited = new Set, rows = [];
766
+ const visit = (n, depth) => {
767
+ if (visited.has(n.sessionId) || rows.length >= limit || depth > 64)
768
+ return;
769
+ visited.add(n.sessionId);
770
+ const snapshot = snapshots.get(n.sessionId), m = metrics.get(n.sessionId) ?? metric(undefined, now, staleMs);
771
+ const collapsed = n.status === "completed" && !sensitive.get(n.sessionId) && !expanded.has(n.sessionId);
772
+ rows.push({
773
+ node: n,
774
+ ...snapshot ? { snapshot } : {},
775
+ depth,
776
+ current: n.sessionId === current,
777
+ ...m,
778
+ hidden: collapsed ? (sizes.get(n.sessionId) ?? 1) - 1 : 0
779
+ });
780
+ if (collapsed) {
781
+ const pending = [...children.get(n.sessionId) ?? []];
782
+ while (pending.length) {
783
+ const child = pending.pop();
784
+ if (!child || visited.has(child.sessionId))
785
+ continue;
786
+ visited.add(child.sessionId);
787
+ pending.push(...children.get(child.sessionId) ?? []);
788
+ }
789
+ }
790
+ if (!collapsed)
791
+ for (const child of sorted(children.get(n.sessionId) ?? []))
792
+ visit(child, depth + 1);
793
+ };
794
+ const ranks = new Map(stableOrder.map((id, i) => [id, i]));
795
+ const sorted = (items) => [...items].sort((a, b) => (stableOrder.length ? (ranks.get(a.sessionId) ?? Infinity) - (ranks.get(b.sessionId) ?? Infinity) : 0) || (scores.get(b.sessionId) ?? -1) - (scores.get(a.sessionId) ?? -1) || a.sessionId.localeCompare(b.sessionId));
796
+ for (const n of sorted(children.get("") ?? []))
797
+ visit(n, 0);
798
+ for (const n of byId.values())
799
+ if (!visited.has(n.sessionId) && rows.length < limit)
800
+ visit(n, 0);
801
+ return rows;
802
+ }
803
+ function executionStatus(known, nativeRunning, evidence) {
804
+ if (nativeRunning || evidence.startedAt !== undefined && evidence.startedAt > (evidence.terminalAt ?? -1))
805
+ return "running";
806
+ if (evidence.outcome && (evidence.terminalAt ?? -1) >= (evidence.startedAt ?? -1))
807
+ return evidence.outcome === "succeeded" ? "completed" : evidence.outcome === "failed" ? "failed" : "unknown";
808
+ return known ? "idle" : "unknown";
809
+ }
810
+ export {
811
+ usableInput,
812
+ promptTokens,
813
+ project,
814
+ parseConfig,
815
+ metric,
816
+ glob,
817
+ familyRows,
818
+ executionStatus,
819
+ evaluateThreshold,
820
+ estimate,
821
+ SnapshotStore,
822
+ PolicyResolver,
823
+ NoopBudgetController,
824
+ ContextMonitor,
825
+ CompactionScheduler
826
+ };