@cesarus777/opencode-cgp 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -2
  3. package/dist/cli.js +1411 -0
  4. package/dist/core.js +1247 -0
  5. package/dist/index.js +2565 -0
  6. package/dist/rpc.js +332 -0
  7. package/dist/tui.js +2286 -0
  8. package/dist/types/adapter.d.ts +20 -0
  9. package/dist/types/chartreport.d.ts +14 -0
  10. package/dist/types/charts.d.ts +54 -0
  11. package/dist/types/config.d.ts +2 -0
  12. package/dist/types/core.d.ts +10 -0
  13. package/dist/types/estimate.d.ts +1 -0
  14. package/dist/types/family.d.ts +39 -0
  15. package/dist/types/history.d.ts +59 -0
  16. package/dist/types/historycharts.d.ts +17 -0
  17. package/dist/types/historyserver.d.ts +7 -0
  18. package/dist/types/index.d.ts +3 -0
  19. package/dist/types/monitor.d.ts +20 -0
  20. package/dist/types/performance.d.ts +57 -0
  21. package/dist/types/policy.d.ts +12 -0
  22. package/dist/types/recovery.d.ts +22 -0
  23. package/dist/types/rpc.d.ts +129 -0
  24. package/dist/types/scheduler.d.ts +18 -0
  25. package/dist/types/snapshot.d.ts +98 -0
  26. package/dist/types/snapshotserver.d.ts +7 -0
  27. package/dist/types/telemetry.d.ts +17 -0
  28. package/dist/types/tui/chartformat.d.ts +11 -0
  29. package/dist/types/tui/charts.d.ts +10 -0
  30. package/dist/types/tui/compact.d.ts +10 -0
  31. package/dist/types/tui/compactformat.d.ts +28 -0
  32. package/dist/types/tui/format.d.ts +8 -0
  33. package/dist/types/tui/inventory.d.ts +18 -0
  34. package/dist/types/tui/panel.d.ts +8 -0
  35. package/dist/types/tui/sidebar.d.ts +13 -0
  36. package/dist/types/tui/state.d.ts +30 -0
  37. package/dist/types/tui/transport.d.ts +40 -0
  38. package/dist/types/tui.d.ts +3 -0
  39. package/dist/types/types.d.ts +153 -0
  40. package/package.json +110 -4
package/dist/core.js ADDED
@@ -0,0 +1,1247 @@
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, [
50
+ "enabled",
51
+ "mode",
52
+ "defaults",
53
+ "policies",
54
+ "safety",
55
+ "telemetry",
56
+ "metrics",
57
+ "history"
58
+ ], "options");
59
+ if (o.mode !== undefined && o.mode !== "observe" && o.mode !== "enforce")
60
+ throw new Error("mode: expected observe or enforce");
61
+ const d = object(o.defaults ?? {}, "defaults");
62
+ keys(d, ["threshold"], "defaults");
63
+ const s = object(o.safety ?? {}, "safety");
64
+ keys(s, [
65
+ "cooldownMs",
66
+ "minReclaimTokens",
67
+ "maxConsecutiveFailures",
68
+ "pendingTimeoutMs",
69
+ "staleMs",
70
+ "maxSessions",
71
+ "sessionTtlMs",
72
+ "outputReserveTokens"
73
+ ], "safety");
74
+ const metrics = object(o.metrics ?? {}, "metrics");
75
+ keys(metrics, ["enabled", "sampleIntervalMs", "rollingWindowMs"], "metrics");
76
+ const history = object(o.history ?? {}, "history");
77
+ keys(history, ["enabled", "retentionDays", "maxBytes", "path"], "history");
78
+ if (history.path !== undefined && (typeof history.path !== "string" || !history.path.startsWith("/")))
79
+ throw new Error("history.path: expected absolute directory path");
80
+ const t = object(o.telemetry ?? {}, "telemetry");
81
+ keys(t, ["enabled", "jsonl", "path", "maxBytes"], "telemetry");
82
+ if (t.path !== undefined && (typeof t.path !== "string" || !t.path.startsWith("/")))
83
+ throw new Error("telemetry.path: expected absolute path");
84
+ if (o.policies !== undefined && (!Array.isArray(o.policies) || o.policies.length > 256))
85
+ throw new Error("policies: expected array of at most 256 policies");
86
+ const policies = (o.policies ?? []).map((v, i) => {
87
+ const p = object(v, `policies[${i}]`);
88
+ keys(p, ["match", "threshold"], `policies[${i}]`);
89
+ const m = object(p.match, "match");
90
+ keys(m, ["role", "model", "agent"], "match");
91
+ if (!Object.keys(m).length)
92
+ throw new Error("match: at least one selector required");
93
+ const match = {};
94
+ if (m.role !== undefined) {
95
+ if (m.role !== "main" && m.role !== "subagent")
96
+ throw new Error("match.role: invalid");
97
+ match.role = m.role;
98
+ }
99
+ if (m.model !== undefined) {
100
+ match.model = selector(m.model, "match.model");
101
+ if (!match.model.includes("/"))
102
+ throw new Error("match.model: use provider/model");
103
+ }
104
+ if (m.agent !== undefined)
105
+ match.agent = selector(m.agent, "match.agent");
106
+ return { match, threshold: threshold(p.threshold, "threshold") };
107
+ });
108
+ const config = {
109
+ enabled: bool(o.enabled, true, "enabled"),
110
+ mode: o.mode ?? "observe",
111
+ defaults: {
112
+ threshold: threshold(d.threshold ?? { type: "tokens", value: 131072 }, "defaults.threshold")
113
+ },
114
+ policies,
115
+ safety: {
116
+ cooldownMs: number(s.cooldownMs, 60000, "cooldownMs"),
117
+ minReclaimTokens: number(s.minReclaimTokens, 8192, "minReclaimTokens"),
118
+ maxConsecutiveFailures: number(s.maxConsecutiveFailures, 3, "maxConsecutiveFailures", 1),
119
+ pendingTimeoutMs: number(s.pendingTimeoutMs, 300000, "pendingTimeoutMs", 1),
120
+ staleMs: number(s.staleMs, 300000, "staleMs", 1),
121
+ maxSessions: number(s.maxSessions, 1024, "maxSessions", 1),
122
+ sessionTtlMs: number(s.sessionTtlMs, 86400000, "sessionTtlMs", 1),
123
+ ...s.outputReserveTokens === undefined ? {} : {
124
+ outputReserveTokens: number(s.outputReserveTokens, 0, "outputReserveTokens")
125
+ }
126
+ },
127
+ metrics: {
128
+ enabled: bool(metrics.enabled, true, "metrics.enabled"),
129
+ sampleIntervalMs: number(metrics.sampleIntervalMs, 500, "metrics.sampleIntervalMs", 100),
130
+ rollingWindowMs: number(metrics.rollingWindowMs, 2000, "metrics.rollingWindowMs", 100)
131
+ },
132
+ history: {
133
+ enabled: bool(history.enabled, true, "history.enabled"),
134
+ retentionDays: number(history.retentionDays, 7, "history.retentionDays", 1),
135
+ maxBytes: number(history.maxBytes, 64 * 1024 * 1024, "history.maxBytes", 4096),
136
+ ...typeof history.path === "string" ? { path: history.path } : {}
137
+ },
138
+ telemetry: {
139
+ enabled: bool(t.enabled, true, "telemetry.enabled"),
140
+ jsonl: bool(t.jsonl, true, "telemetry.jsonl"),
141
+ maxBytes: number(t.maxBytes, 8 * 1024 * 1024, "maxBytes", 4096),
142
+ ...typeof t.path === "string" ? { path: t.path } : {}
143
+ }
144
+ };
145
+ if (config.metrics.rollingWindowMs < config.metrics.sampleIntervalMs || config.metrics.rollingWindowMs > 60000)
146
+ throw new Error("metrics.rollingWindowMs must be >= sampleIntervalMs and <= 60000");
147
+ for (const policy of [config.defaults, ...config.policies]) {
148
+ if (policy.threshold.type === "tokens" && config.safety.minReclaimTokens >= policy.threshold.value)
149
+ throw new Error("minReclaimTokens must be smaller than each absolute threshold");
150
+ }
151
+ if (!config.telemetry.jsonl && config.telemetry.path)
152
+ throw new Error("telemetry.path requires jsonl");
153
+ return config;
154
+ }
155
+ // src/policy.ts
156
+ function glob(pattern, text) {
157
+ let p = 0, t = 0, star = -1, retry = 0;
158
+ while (t < text.length) {
159
+ if (pattern[p] === "?" || pattern[p] === text[t]) {
160
+ p++;
161
+ t++;
162
+ } else if (pattern[p] === "*") {
163
+ star = p++;
164
+ retry = t;
165
+ } else if (star >= 0) {
166
+ p = star + 1;
167
+ t = ++retry;
168
+ } else
169
+ return false;
170
+ }
171
+ while (pattern[p] === "*")
172
+ p++;
173
+ return p === pattern.length;
174
+ }
175
+
176
+ class PolicyResolver {
177
+ config;
178
+ constructor(config) {
179
+ this.config = config;
180
+ }
181
+ resolve(identity) {
182
+ let result = {
183
+ threshold: this.config.defaults.threshold,
184
+ policy: "defaults"
185
+ };
186
+ this.config.policies.forEach((p, i) => {
187
+ const m = p.match;
188
+ if (m.role && m.role !== (identity.parentId ? "subagent" : "main"))
189
+ return;
190
+ if (m.agent && (!identity.agent || !glob(m.agent, identity.agent)))
191
+ return;
192
+ if (m.model && (!identity.model || !glob(m.model, identity.model)))
193
+ return;
194
+ result = { threshold: p.threshold, policy: `policies[${i}]` };
195
+ });
196
+ return result;
197
+ }
198
+ }
199
+ function usableInput(limits, reserve) {
200
+ if (!limits)
201
+ return;
202
+ const valid = (n) => n !== undefined && Number.isFinite(n) && n > 0;
203
+ const output = reserve ?? (valid(limits.output) ? limits.output : undefined);
204
+ const contextBudget = valid(limits.context) && output !== undefined ? Math.max(0, limits.context - output) : undefined;
205
+ if (valid(limits.input))
206
+ return contextBudget === undefined ? limits.input : Math.min(limits.input, contextBudget);
207
+ return contextBudget;
208
+ }
209
+ function evaluateThreshold(threshold2, measurement, limits, now, staleMs, model, policy, reserve) {
210
+ const budget = usableInput(limits, reserve);
211
+ const base = {
212
+ crossed: false,
213
+ reason: "missing-usage",
214
+ policy,
215
+ ...budget === undefined ? {} : { budget }
216
+ };
217
+ const trigger = threshold2.type === "tokens" ? threshold2.value : budget === undefined ? undefined : threshold2.type === "ratio" ? Math.ceil(budget * threshold2.value) : budget - threshold2.value;
218
+ if (trigger !== undefined)
219
+ base.threshold = trigger;
220
+ if (!measurement)
221
+ return base;
222
+ if (model !== measurement.model)
223
+ return { ...base, reason: "model-changed" };
224
+ if (!Number.isFinite(measurement.tokens) || measurement.tokens < 0)
225
+ return { ...base, reason: "invalid-usage" };
226
+ if (now - measurement.at > staleMs || measurement.at > now)
227
+ return { ...base, reason: "stale-usage" };
228
+ if (budget !== undefined && budget > 0)
229
+ base.percentage = measurement.tokens / budget * 100;
230
+ if (trigger === undefined)
231
+ return { ...base, reason: "missing-model-budget" };
232
+ if (trigger <= 0)
233
+ return { ...base, reason: "incompatible-model-budget" };
234
+ const crossed = threshold2.type === "remaining" ? measurement.tokens > trigger : measurement.tokens >= trigger;
235
+ return {
236
+ ...base,
237
+ crossed,
238
+ reason: crossed ? "threshold-crossed" : "below-threshold"
239
+ };
240
+ }
241
+ // src/monitor.ts
242
+ function promptTokens(t) {
243
+ const fields = [t.input, t.output, t.reasoning, t.cache.read, t.cache.write];
244
+ if (fields.some((x) => !Number.isSafeInteger(x) || x < 0))
245
+ return;
246
+ const total = t.input + t.cache.read + t.cache.write;
247
+ return total > 0 && Number.isSafeInteger(total) ? total : undefined;
248
+ }
249
+
250
+ class ContextMonitor {
251
+ config;
252
+ scheduler;
253
+ sink;
254
+ now;
255
+ budget;
256
+ sessions = new Map;
257
+ resolver;
258
+ constructor(config, scheduler, sink, now = Date.now, budget = new NoopBudgetController) {
259
+ this.config = config;
260
+ this.scheduler = scheduler;
261
+ this.sink = sink;
262
+ this.now = now;
263
+ this.budget = budget;
264
+ this.resolver = new PolicyResolver(config);
265
+ }
266
+ touch(identity, limits) {
267
+ let s = this.sessions.get(identity.sessionId);
268
+ if (!s) {
269
+ this.cleanup();
270
+ if (this.sessions.size >= this.config.safety.maxSessions) {
271
+ this.sink.emit({
272
+ time: this.now(),
273
+ type: "capacity-exceeded",
274
+ detail: "New session not monitored; increase safety.maxSessions"
275
+ });
276
+ return;
277
+ }
278
+ s = {
279
+ ...identity,
280
+ lastSeen: this.now(),
281
+ sequence: -1,
282
+ active: false,
283
+ usage: {
284
+ cumulativeScope: "unknown",
285
+ peak: 0,
286
+ modelRequests: 0,
287
+ toolCalls: 0,
288
+ countersSince: this.now()
289
+ },
290
+ compaction: {
291
+ phase: "idle",
292
+ armed: true,
293
+ count: 0,
294
+ failures: 0,
295
+ consecutiveFailures: 0
296
+ }
297
+ };
298
+ this.sessions.set(identity.sessionId, s);
299
+ }
300
+ if (identity.model && identity.model !== s.model) {
301
+ delete s.usage.current;
302
+ delete s.limits;
303
+ }
304
+ Object.assign(s, identity);
305
+ s.lastSeen = this.now();
306
+ if (limits)
307
+ s.limits = { ...limits };
308
+ return s;
309
+ }
310
+ accept(s, sequence) {
311
+ if (sequence <= s.sequence)
312
+ return false;
313
+ s.sequence = sequence;
314
+ s.lastSeen = this.now();
315
+ return true;
316
+ }
317
+ measure(s, measurement, cumulative) {
318
+ if (s.usage.current && measurement.at < s.usage.current.at)
319
+ return;
320
+ s.usage.current = measurement;
321
+ s.usage.peak = Math.max(s.usage.peak, measurement.tokens);
322
+ if (cumulative) {
323
+ s.usage.cumulative = cumulative;
324
+ s.usage.cumulativeScope = "session";
325
+ }
326
+ this.evaluate(s);
327
+ }
328
+ evaluate(s) {
329
+ const p = this.resolver.resolve(s);
330
+ s.decision = evaluateThreshold(p.threshold, s.usage.current, s.limits, this.now(), this.config.safety.staleMs, s.model, p.policy, this.config.safety.outputReserveTokens);
331
+ this.scheduler.measured(s);
332
+ const action = this.budget.evaluate({
333
+ session: s,
334
+ now: this.now(),
335
+ elapsedMs: this.now() - s.usage.countersSince
336
+ });
337
+ if (action.type !== "continue")
338
+ this.sink.emit({
339
+ time: this.now(),
340
+ type: `budget.${action.type}`,
341
+ session: s,
342
+ detail: "Advisory extension; only context policies execute in this release"
343
+ });
344
+ this.sink.emit({
345
+ time: this.now(),
346
+ type: this.config.mode === "observe" && s.decision.crossed ? "would-compact" : "usage",
347
+ session: s
348
+ });
349
+ this.scheduler.consider(s);
350
+ }
351
+ remove(id) {
352
+ const s = this.sessions.get(id);
353
+ if (s) {
354
+ s.active = false;
355
+ s.compaction.phase = "blocked";
356
+ s.compaction.blockedReason = "session-removed";
357
+ this.sink.emit({ time: this.now(), type: "session.removed", session: s });
358
+ this.sessions.delete(id);
359
+ }
360
+ }
361
+ cleanup() {
362
+ for (const [id, s] of this.sessions) {
363
+ this.scheduler.tick(s);
364
+ if (!s.active && ["idle", "blocked"].includes(s.compaction.phase) && this.now() - s.lastSeen > this.config.safety.sessionTtlMs)
365
+ this.remove(id);
366
+ }
367
+ }
368
+ async getUsage(id) {
369
+ const s = this.sessions.get(id);
370
+ if (!s)
371
+ throw new Error("Unknown session");
372
+ return structuredClone(s.usage);
373
+ }
374
+ }
375
+ // src/scheduler.ts
376
+ class CompactionScheduler {
377
+ config;
378
+ port;
379
+ sink;
380
+ now;
381
+ id;
382
+ stopped = false;
383
+ jobs = new Set;
384
+ constructor(config, port, sink, now = Date.now, id = () => `msg_${crypto.randomUUID().replaceAll("-", "")}`) {
385
+ this.config = config;
386
+ this.port = port;
387
+ this.sink = sink;
388
+ this.now = now;
389
+ this.id = id;
390
+ }
391
+ consider(s) {
392
+ if (this.stopped || !this.config.enabled || this.config.mode !== "enforce" || !s.decision?.crossed)
393
+ return;
394
+ const c = s.compaction, time = this.now();
395
+ if (c.phase !== "idle" || !c.armed || c.consecutiveFailures >= this.config.safety.maxConsecutiveFailures)
396
+ return;
397
+ if (c.lastAttempt !== undefined && time - c.lastAttempt < this.config.safety.cooldownMs)
398
+ return;
399
+ c.phase = "requesting";
400
+ c.lastAttempt = time;
401
+ c.requestId = this.id();
402
+ c.before = s.usage.current?.tokens;
403
+ c.beforeModel = s.model;
404
+ c.beforeConfidence = s.usage.current?.confidence;
405
+ c.armed = false;
406
+ this.emit("compaction.requesting", s);
407
+ const requestId = c.requestId;
408
+ const job = new Promise((resolve) => setTimeout(resolve, 0)).then(async () => {
409
+ if (this.stopped || c.phase !== "requesting")
410
+ return;
411
+ try {
412
+ await this.port.compact(s.sessionId, requestId);
413
+ if (c.phase === "requesting")
414
+ c.phase = "accepted";
415
+ this.emit("compaction.accepted", s);
416
+ } catch {
417
+ if (c.phase === "requesting") {
418
+ c.phase = "uncertain";
419
+ c.blockedReason = "admission-outcome-unknown";
420
+ c.failures++;
421
+ c.consecutiveFailures++;
422
+ this.emit("compaction.uncertain", s);
423
+ }
424
+ }
425
+ }).finally(() => this.jobs.delete(job));
426
+ this.jobs.add(job);
427
+ }
428
+ lifecycle(s, event, at) {
429
+ const c = s.compaction;
430
+ if (event === "started") {
431
+ if (c.phase !== "requesting" && c.phase !== "accepted") {
432
+ c.before = s.usage.current?.tokens;
433
+ c.beforeModel = s.model;
434
+ c.beforeConfidence = s.usage.current?.confidence;
435
+ }
436
+ c.phase = "running";
437
+ c.armed = false;
438
+ c.lastAttempt = at;
439
+ } else if (event === "ended") {
440
+ c.phase = "awaiting-measurement";
441
+ c.lastCompleted = at;
442
+ c.count++;
443
+ c.consecutiveFailures = 0;
444
+ delete s.usage.current;
445
+ } else {
446
+ c.failures++;
447
+ c.consecutiveFailures++;
448
+ c.lastAttempt = at;
449
+ c.armed = true;
450
+ c.phase = c.consecutiveFailures >= this.config.safety.maxConsecutiveFailures ? "blocked" : "idle";
451
+ c.blockedReason = "compaction-failed";
452
+ delete s.usage.current;
453
+ }
454
+ this.emit(`compaction.${event}`, s);
455
+ }
456
+ measured(s) {
457
+ const c = s.compaction, m = s.usage.current;
458
+ if (!m)
459
+ return;
460
+ if (c.phase === "awaiting-measurement") {
461
+ if (m.confidence !== "reported" || m.at < (c.lastCompleted ?? 0))
462
+ return;
463
+ if (c.beforeModel !== m.model || c.before === undefined || c.beforeConfidence !== "reported") {
464
+ c.phase = "blocked";
465
+ c.blockedReason = "reclaim-unverifiable";
466
+ } else if (c.before - m.tokens < this.config.safety.minReclaimTokens) {
467
+ c.phase = "blocked";
468
+ c.blockedReason = "insufficient-reclaim";
469
+ } else {
470
+ c.phase = "idle";
471
+ c.armed = false;
472
+ delete c.blockedReason;
473
+ }
474
+ }
475
+ const resetAt = (s.decision?.threshold ?? 0) - this.config.safety.minReclaimTokens;
476
+ if (m.tokens <= resetAt && (c.phase === "idle" || c.phase === "blocked" && c.blockedReason !== "compaction-failed")) {
477
+ c.armed = true;
478
+ c.phase = "idle";
479
+ delete c.blockedReason;
480
+ }
481
+ }
482
+ tick(s) {
483
+ const c = s.compaction;
484
+ if ((c.phase === "accepted" || c.phase === "running" || c.phase === "requesting") && this.now() - (c.lastAttempt ?? this.now()) > this.config.safety.pendingTimeoutMs) {
485
+ c.phase = "uncertain";
486
+ c.blockedReason = "completion-event-timeout";
487
+ this.emit("compaction.uncertain", s);
488
+ }
489
+ }
490
+ emit(type, session) {
491
+ this.sink.emit({ type, time: this.now(), session });
492
+ }
493
+ async flush() {
494
+ await Promise.all(this.jobs);
495
+ }
496
+ stop() {
497
+ this.stopped = true;
498
+ }
499
+ }
500
+ // src/estimate.ts
501
+ function estimate(value, maxChars = 2000000) {
502
+ let chars = 0, nodes = 0;
503
+ function visit(v, depth) {
504
+ if (++nodes > 1e5 || depth > 30 || chars > maxChars)
505
+ return false;
506
+ if (typeof v === "string")
507
+ chars += v.length;
508
+ else if (typeof v === "number" || typeof v === "boolean")
509
+ chars += 8;
510
+ else if (Array.isArray(v)) {
511
+ for (const item of v)
512
+ if (!visit(item, depth + 1))
513
+ return false;
514
+ } else if (v && typeof v === "object") {
515
+ if (ArrayBuffer.isView(v) || v instanceof ArrayBuffer)
516
+ return false;
517
+ for (const [key, item] of Object.entries(v)) {
518
+ chars += key.length + 4;
519
+ if (!visit(item, depth + 1))
520
+ return false;
521
+ }
522
+ }
523
+ return chars <= maxChars;
524
+ }
525
+ return visit(value, 0) ? Math.ceil(chars / 3) : undefined;
526
+ }
527
+ // src/snapshot.ts
528
+ function project(s, config, now, revision = 0) {
529
+ const p = new PolicyResolver(config).resolve(s);
530
+ const d = evaluateThreshold(p.threshold, s.usage.current, s.limits, now, config.safety.staleMs, s.model, p.policy, config.safety.outputReserveTokens);
531
+ const c = s.compaction;
532
+ return freezeSnapshot({
533
+ sessionId: s.sessionId,
534
+ revision,
535
+ observedAt: s.lastSeen,
536
+ ...s.parentId ? { parentId: s.parentId } : {},
537
+ ...s.agent ? { agent: s.agent } : {},
538
+ ...s.model ? { model: s.model } : {},
539
+ ...s.usage.current ? {
540
+ measurement: {
541
+ tokens: s.usage.current.tokens,
542
+ at: s.usage.current.at,
543
+ model: s.usage.current.model,
544
+ provenance: s.usage.current.provenance,
545
+ confidence: s.usage.current.confidence,
546
+ scope: s.usage.current.scope,
547
+ ...s.usage.current.requestId ? { requestId: s.usage.current.requestId } : {}
548
+ }
549
+ } : {},
550
+ ...s.limits ? {
551
+ limits: {
552
+ context: s.limits.context,
553
+ output: s.limits.output,
554
+ ...s.limits.input === undefined ? {} : { input: s.limits.input }
555
+ }
556
+ } : {},
557
+ ...d.budget === undefined ? {} : { capacityTokens: d.budget },
558
+ ...config.safety.outputReserveTokens === undefined && !s.limits ? {} : {
559
+ outputReserveTokens: config.safety.outputReserveTokens ?? s.limits?.output ?? 0
560
+ },
561
+ peakTokens: s.usage.peak,
562
+ policy: {
563
+ id: p.policy,
564
+ configured: { type: p.threshold.type, value: p.threshold.value },
565
+ ...d.threshold === undefined ? {} : { triggerTokens: d.threshold },
566
+ comparison: p.threshold.type === "remaining" ? ">" : ">=",
567
+ reason: d.reason,
568
+ crossed: d.crossed
569
+ },
570
+ compaction: {
571
+ phase: c.phase,
572
+ count: c.count,
573
+ armed: c.armed,
574
+ ...c.blockedReason ? { blockedReason: c.blockedReason } : {},
575
+ ...c.lastAttempt === undefined ? {} : {
576
+ lastAttempt: c.lastAttempt,
577
+ cooldownUntil: c.lastAttempt + config.safety.cooldownMs
578
+ },
579
+ ...c.lastCompleted === undefined ? {} : { lastCompleted: c.lastCompleted }
580
+ },
581
+ ...s.usage.cumulative ? {
582
+ cumulative: {
583
+ input: s.usage.cumulative.input,
584
+ output: s.usage.cumulative.output,
585
+ reasoning: s.usage.cumulative.reasoning,
586
+ cache: {
587
+ read: s.usage.cumulative.cache.read,
588
+ write: s.usage.cumulative.cache.write
589
+ }
590
+ }
591
+ } : {},
592
+ cumulativeScope: s.usage.cumulativeScope,
593
+ lastObservedActive: s.active
594
+ });
595
+ }
596
+
597
+ class SnapshotStore {
598
+ config;
599
+ sessions;
600
+ now;
601
+ performanceSnapshot;
602
+ epoch;
603
+ sequence = 0;
604
+ revision = 0;
605
+ rows = new Map;
606
+ deleted = new Set;
607
+ dirty = new Set;
608
+ listeners = new Set;
609
+ healthKey = "";
610
+ reasons = new Set;
611
+ constructor(config, sessions, now = Date.now, epoch = crypto.randomUUID(), performanceSnapshot) {
612
+ this.config = config;
613
+ this.sessions = sessions;
614
+ this.now = now;
615
+ this.performanceSnapshot = performanceSnapshot;
616
+ this.epoch = epoch;
617
+ }
618
+ health() {
619
+ return {
620
+ enabled: this.config.enabled,
621
+ mode: this.config.mode,
622
+ enforcement: !this.config.enabled ? "disabled" : this.reasons.has("event-stream-gap") || this.reasons.has("persistence-unavailable") ? "suspended" : this.config.mode === "observe" ? "observing" : "available",
623
+ reasons: [...this.reasons].sort(),
624
+ staleMs: this.config.safety.staleMs,
625
+ maxSessions: this.config.safety.maxSessions
626
+ };
627
+ }
628
+ degrade(reason) {
629
+ this.reasons.add(reason);
630
+ }
631
+ recover(reason) {
632
+ this.reasons.delete(reason);
633
+ }
634
+ mark(id) {
635
+ if (this.dirty.size < this.config.safety.maxSessions)
636
+ this.dirty.add(id);
637
+ }
638
+ markAll() {
639
+ for (const id of this.sessions().keys())
640
+ this.mark(id);
641
+ }
642
+ remove(id) {
643
+ if (this.rows.has(id))
644
+ this.deleted.add(id);
645
+ }
646
+ listen(fn) {
647
+ this.listeners.add(fn);
648
+ return () => {
649
+ this.listeners.delete(fn);
650
+ };
651
+ }
652
+ batch(upserts, removed) {
653
+ return {
654
+ schemaVersion: 1,
655
+ epoch: this.epoch,
656
+ sequence: this.sequence,
657
+ generatedAt: this.now(),
658
+ health: this.health(),
659
+ upserts,
660
+ removed
661
+ };
662
+ }
663
+ flush() {
664
+ const upserts = [], removed = [];
665
+ const resident = this.sessions();
666
+ for (const [id, old] of this.rows)
667
+ if (old.measurement && old.policy.reason !== "stale-usage" && this.now() - old.measurement.at > this.config.safety.staleMs)
668
+ this.mark(id);
669
+ for (const id of resident.keys())
670
+ if (!this.rows.has(id))
671
+ this.mark(id);
672
+ for (const id of this.dirty) {
673
+ const s = resident.get(id);
674
+ if (!s)
675
+ continue;
676
+ const performance = this.performanceSnapshot?.(id);
677
+ const next = freezeSnapshot({
678
+ ...project(s, this.config, this.now()),
679
+ ...performance ? { performance } : {}
680
+ });
681
+ const old = this.rows.get(id);
682
+ if (old && JSON.stringify({ ...old, revision: 0 }) === JSON.stringify(next))
683
+ continue;
684
+ const row = freezeSnapshot({ ...next, revision: ++this.revision });
685
+ this.rows.set(id, row);
686
+ upserts.push(row);
687
+ }
688
+ for (const id of this.rows.keys())
689
+ if (!resident.has(id)) {
690
+ this.rows.delete(id);
691
+ removed.push({
692
+ sessionId: id,
693
+ revision: ++this.revision,
694
+ reason: this.deleted.has(id) ? "deleted" : "evicted"
695
+ });
696
+ }
697
+ this.deleted.clear();
698
+ this.dirty.clear();
699
+ const healthKey = JSON.stringify(this.health()), changed = healthKey !== this.healthKey;
700
+ this.healthKey = healthKey;
701
+ const changes = [
702
+ ...upserts.map((row) => ({ kind: "upsert", row })),
703
+ ...removed.map((row) => ({ kind: "remove", row }))
704
+ ];
705
+ for (let i = 0;i < Math.max(changes.length, changed ? 1 : 0); i += 128) {
706
+ this.sequence++;
707
+ const slice = changes.slice(i, i + 128);
708
+ const batch = this.batch(slice.flatMap((change) => change.kind === "upsert" ? [change.row] : []), slice.flatMap((change) => change.kind === "remove" ? [change.row] : []));
709
+ for (const fn of this.listeners) {
710
+ try {
711
+ fn(structuredClone(batch));
712
+ } catch {
713
+ }
714
+ }
715
+ }
716
+ }
717
+ read(input) {
718
+ this.flush();
719
+ const ids = [...new Set(input.sessionIds)];
720
+ return structuredClone({
721
+ ...this.batch(ids.flatMap((id) => {
722
+ const row = this.rows.get(id);
723
+ return row ? [row] : [];
724
+ }), []),
725
+ unavailable: ids.filter((id) => !this.rows.has(id)).map((sessionId) => ({ sessionId, reason: "not-monitored" }))
726
+ });
727
+ }
728
+ }
729
+ function freezeSnapshot(row) {
730
+ if (row.performance)
731
+ Object.freeze(row.performance);
732
+ if (row.measurement)
733
+ Object.freeze(row.measurement);
734
+ if (row.limits)
735
+ Object.freeze(row.limits);
736
+ Object.freeze(row.policy.configured);
737
+ Object.freeze(row.policy);
738
+ Object.freeze(row.compaction);
739
+ if (row.cumulative) {
740
+ Object.freeze(row.cumulative.cache);
741
+ Object.freeze(row.cumulative);
742
+ }
743
+ return Object.freeze(row);
744
+ }
745
+ // src/family.ts
746
+ function metric(snapshot, now, staleMs) {
747
+ const m = snapshot?.measurement;
748
+ const freshness = !m ? "missing" : m.model !== snapshot?.model || m.at > now ? "invalid" : now - m.at > staleMs ? "stale" : "fresh";
749
+ const t = snapshot?.policy.triggerTokens;
750
+ const valid = freshness === "fresh" && m !== undefined;
751
+ const pressureRatio = valid && t !== undefined && t > 0 ? m.tokens / t : undefined;
752
+ const crossed = valid && t !== undefined && t > 0 && (snapshot?.policy.comparison === ">" ? m.tokens > t : m.tokens >= t);
753
+ return {
754
+ freshness,
755
+ pressure: pressureRatio === undefined ? "unknown" : crossed ? "crossed" : pressureRatio >= 0.9 ? "near" : "normal",
756
+ ...pressureRatio === undefined ? {} : { pressureRatio },
757
+ ...valid && snapshot?.capacityTokens && snapshot.capacityTokens > 0 ? { utilizationPercent: m.tokens / snapshot.capacityTokens * 100 } : {},
758
+ ...valid && t !== undefined ? { triggerDeltaTokens: t - m.tokens } : {}
759
+ };
760
+ }
761
+ function familyRows(nodes, snapshots, current, now, staleMs, expanded = new Set, limit = 1024, timing, stableOrder = []) {
762
+ const byId = new Map(nodes.map((n) => [n.sessionId, n]));
763
+ const children = new Map;
764
+ for (const n of byId.values()) {
765
+ const parent = n.parentId && byId.has(n.parentId) ? n.parentId : "";
766
+ const list = children.get(parent) ?? [];
767
+ list.push(n);
768
+ children.set(parent, list);
769
+ }
770
+ const metrics = new Map([...byId.keys()].map((id) => [
771
+ id,
772
+ metric(snapshots.get(id), timing?.(id).now ?? now, timing?.(id).staleMs ?? staleMs)
773
+ ]));
774
+ const protectedIds = new Set;
775
+ let ancestor = byId.get(current);
776
+ while (ancestor && !protectedIds.has(ancestor.sessionId)) {
777
+ protectedIds.add(ancestor.sessionId);
778
+ ancestor = ancestor.parentId ? byId.get(ancestor.parentId) : undefined;
779
+ }
780
+ const scores = new Map, sensitive = new Map, sizes = new Map;
781
+ const calculate = (n, seen) => {
782
+ if (seen.has(n.sessionId))
783
+ return -1;
784
+ if (scores.has(n.sessionId))
785
+ return scores.get(n.sessionId) ?? -1;
786
+ const path = new Set(seen).add(n.sessionId), m = metrics.get(n.sessionId);
787
+ let score = m?.pressureRatio ?? -1, size = 1;
788
+ let keep = protectedIds.has(n.sessionId) || n.status === "running" || m?.pressure === "crossed" || ["blocked", "uncertain"].includes(snapshots.get(n.sessionId)?.compaction.phase ?? "");
789
+ for (const child of children.get(n.sessionId) ?? []) {
790
+ score = Math.max(score, calculate(child, path));
791
+ size += sizes.get(child.sessionId) ?? 0;
792
+ keep ||= sensitive.get(child.sessionId) ?? false;
793
+ }
794
+ scores.set(n.sessionId, score);
795
+ sizes.set(n.sessionId, size);
796
+ sensitive.set(n.sessionId, keep);
797
+ return score;
798
+ };
799
+ for (const n of byId.values())
800
+ calculate(n, new Set);
801
+ const visited = new Set, rows = [];
802
+ const visit = (n, depth) => {
803
+ if (visited.has(n.sessionId) || rows.length >= limit || depth > 64)
804
+ return;
805
+ visited.add(n.sessionId);
806
+ const snapshot = snapshots.get(n.sessionId), m = metrics.get(n.sessionId) ?? metric(undefined, now, staleMs);
807
+ const collapsed = n.status === "completed" && !sensitive.get(n.sessionId) && !expanded.has(n.sessionId);
808
+ rows.push({
809
+ node: n,
810
+ ...snapshot ? { snapshot } : {},
811
+ depth,
812
+ current: n.sessionId === current,
813
+ ...m,
814
+ hidden: collapsed ? (sizes.get(n.sessionId) ?? 1) - 1 : 0
815
+ });
816
+ if (collapsed) {
817
+ const pending = [...children.get(n.sessionId) ?? []];
818
+ while (pending.length) {
819
+ const child = pending.pop();
820
+ if (!child || visited.has(child.sessionId))
821
+ continue;
822
+ visited.add(child.sessionId);
823
+ pending.push(...children.get(child.sessionId) ?? []);
824
+ }
825
+ }
826
+ if (!collapsed)
827
+ for (const child of sorted(children.get(n.sessionId) ?? []))
828
+ visit(child, depth + 1);
829
+ };
830
+ const ranks = new Map(stableOrder.map((id, i) => [id, i]));
831
+ 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));
832
+ for (const n of sorted(children.get("") ?? []))
833
+ visit(n, 0);
834
+ for (const n of byId.values())
835
+ if (!visited.has(n.sessionId) && rows.length < limit)
836
+ visit(n, 0);
837
+ return rows;
838
+ }
839
+ function executionStatus(known, nativeRunning, evidence) {
840
+ if (nativeRunning || evidence.startedAt !== undefined && evidence.startedAt > (evidence.terminalAt ?? -1))
841
+ return "running";
842
+ if (evidence.outcome && (evidence.terminalAt ?? -1) >= (evidence.startedAt ?? -1))
843
+ return evidence.outcome === "succeeded" ? "completed" : evidence.outcome === "failed" ? "failed" : "unknown";
844
+ return known ? "idle" : "unknown";
845
+ }
846
+ // src/performance.ts
847
+ var valid = (n) => Number.isFinite(n) && n >= 0;
848
+ var rate = (tokens, ms) => valid(tokens) && ms > 0 ? tokens * 1000 / ms : undefined;
849
+ function outputTokens(tokens) {
850
+ const n = tokens.output + tokens.reasoning;
851
+ return Number.isSafeInteger(tokens.output) && tokens.output >= 0 && Number.isSafeInteger(tokens.reasoning) && tokens.reasoning >= 0 && Number.isSafeInteger(n) ? n : undefined;
852
+ }
853
+
854
+ class PerformanceTracker {
855
+ maxSessions;
856
+ windowMs;
857
+ now;
858
+ rows = new Map;
859
+ constructor(maxSessions, windowMs = 2000, now = Date.now) {
860
+ this.maxSessions = maxSessions;
861
+ this.windowMs = windowMs;
862
+ this.now = now;
863
+ }
864
+ observe(id, created, cumulative) {
865
+ let row = this.rows.get(id);
866
+ if (!row) {
867
+ if (this.rows.size >= this.maxSessions)
868
+ return;
869
+ row = {
870
+ observedSince: this.now(),
871
+ created,
872
+ partial: cumulative !== 0,
873
+ output: 0,
874
+ duration: 0,
875
+ completed: 0,
876
+ sequence: -1
877
+ };
878
+ this.rows.set(id, row);
879
+ }
880
+ if (valid(created))
881
+ row.created = created;
882
+ if (cumulative !== undefined && valid(cumulative))
883
+ row.cumulative = cumulative;
884
+ }
885
+ accept(id, sequence) {
886
+ const row = this.rows.get(id);
887
+ if (!row || sequence <= row.sequence)
888
+ return false;
889
+ row.sequence = sequence;
890
+ return true;
891
+ }
892
+ start(id, requestId, agent, model, started) {
893
+ const row = this.rows.get(id);
894
+ if (!row || !valid(started))
895
+ return;
896
+ const old = row.request;
897
+ if (old?.id === requestId && old.started === started)
898
+ return;
899
+ if (old && !old.settled)
900
+ row.partial = true;
901
+ row.request = {
902
+ id: requestId,
903
+ agent,
904
+ model,
905
+ started,
906
+ chars: 0,
907
+ settled: false,
908
+ events: new Set,
909
+ buckets: new Map
910
+ };
911
+ }
912
+ delta(id, requestId, eventId, at, characters) {
913
+ const row = this.rows.get(id), r = row?.request;
914
+ if (!row || !r || r.id !== requestId || r.settled || r.ended !== undefined || !valid(at) || at < r.started || !valid(characters) || !characters || r.events.has(eventId))
915
+ return;
916
+ if (r.events.size >= 256)
917
+ r.events.delete(r.events.values().next().value ?? "");
918
+ r.events.add(eventId);
919
+ r.first = r.first === undefined ? at : Math.min(r.first, at);
920
+ r.chars += characters;
921
+ const bucket = Math.floor(at / 100) * 100;
922
+ r.buckets.set(bucket, (r.buckets.get(bucket) ?? 0) + characters);
923
+ this.prune(r, Math.max(at, this.now()));
924
+ while (r.buckets.size > Math.ceil(this.windowMs / 100) + 2) {
925
+ r.buckets.delete(Math.min(...r.buckets.keys()));
926
+ }
927
+ }
928
+ streamed(id, requestId, at) {
929
+ const row = this.rows.get(id), r = row?.request;
930
+ if (!row || !r || r.id !== requestId || r.settled || r.ended !== undefined)
931
+ return;
932
+ if (!valid(at) || at <= r.started || r.first !== undefined && at < r.first) {
933
+ row.partial = true;
934
+ return;
935
+ }
936
+ r.ended = at;
937
+ }
938
+ settle(id, requestId, tokens) {
939
+ const row = this.rows.get(id), r = row?.request;
940
+ if (!row)
941
+ return;
942
+ if (!r || r.id !== requestId) {
943
+ row.partial = true;
944
+ return;
945
+ }
946
+ if (r.settled)
947
+ return;
948
+ r.settled = true;
949
+ if (tokens !== undefined && valid(tokens))
950
+ r.output = tokens;
951
+ if (tokens !== undefined && valid(tokens) && r.ended !== undefined) {
952
+ row.output += tokens;
953
+ row.duration += r.ended - r.started;
954
+ row.completed++;
955
+ } else
956
+ row.partial = true;
957
+ const snapshot = this.snapshot(id);
958
+ if (snapshot) {
959
+ const { latestRequestTps: _previousRate, ...current } = snapshot;
960
+ row.latest = {
961
+ ...current,
962
+ tokenSource: tokens === undefined ? "unavailable" : "reported",
963
+ ...tokens !== undefined && r.ended !== undefined ? { latestRequestTps: tokens * 1000 / (r.ended - r.started) } : {}
964
+ };
965
+ }
966
+ r.events.clear();
967
+ r.buckets.clear();
968
+ }
969
+ gap(id) {
970
+ for (const [key, row] of this.rows) {
971
+ if (id !== undefined && key !== id)
972
+ continue;
973
+ row.partial = true;
974
+ delete row.request;
975
+ }
976
+ }
977
+ prune(r, at) {
978
+ for (const time of r.buckets.keys())
979
+ if (time < at - this.windowMs)
980
+ r.buckets.delete(time);
981
+ }
982
+ snapshot(id) {
983
+ const row = this.rows.get(id);
984
+ if (!row)
985
+ return;
986
+ const at = this.now(), r = row.request;
987
+ const active = r && !r.settled && r.ended === undefined;
988
+ if (r)
989
+ this.prune(r, at);
990
+ const live = active ? rate([...r.buckets.values()].reduce((a, b) => a + b, 0) / 4, Math.min(this.windowMs, at - r.started)) : 0;
991
+ const averageModelTps = rate(row.output, row.duration);
992
+ const averageSessionTps = row.cumulative === undefined ? undefined : rate(row.cumulative, at - row.created);
993
+ return {
994
+ observedSince: row.observedSince,
995
+ at,
996
+ sessionCreatedAt: row.created,
997
+ ...row.cumulative === undefined ? {} : { sessionOutputTokens: row.cumulative },
998
+ status: active ? r.first === undefined ? "waiting" : "streaming" : !r && row.partial ? "unavailable" : "idle",
999
+ partial: row.partial,
1000
+ ...live === undefined || !r && row.partial ? {} : { liveTps: live },
1001
+ ...averageModelTps === undefined ? {} : { averageModelTps },
1002
+ ...averageSessionTps === undefined ? {} : { averageSessionTps },
1003
+ ...r ? {
1004
+ requestId: r.id,
1005
+ requestAgent: r.agent,
1006
+ requestModel: r.model,
1007
+ requestStartedAt: r.started,
1008
+ ...r.output === undefined ? {} : { requestOutputTokens: r.output },
1009
+ ...r.first === undefined ? {} : { firstOutputAt: r.first, ttftMs: r.first - r.started },
1010
+ ...r.ended === undefined ? {} : { streamEndedAt: r.ended }
1011
+ } : row.latest ? {
1012
+ ...row.latest.ttftMs === undefined ? {} : { ttftMs: row.latest.ttftMs }
1013
+ } : {},
1014
+ ...row.latest?.latestRequestTps === undefined ? {} : { latestRequestTps: row.latest.latestRequestTps },
1015
+ estimatedOutputTokens: r ? r.chars / 4 : 0,
1016
+ modelOutputTokens: row.output,
1017
+ modelDurationMs: row.duration,
1018
+ completedRequests: row.completed,
1019
+ tokenSource: r && !r.settled ? "estimated" : row.latest?.tokenSource ?? "unavailable"
1020
+ };
1021
+ }
1022
+ checkpoint() {
1023
+ return [...this.rows].map(([sessionId, r]) => ({
1024
+ sessionId,
1025
+ observedSince: r.observedSince,
1026
+ created: r.created,
1027
+ output: r.output,
1028
+ duration: r.duration,
1029
+ completed: r.completed,
1030
+ sequence: r.sequence
1031
+ }));
1032
+ }
1033
+ restore(value) {
1034
+ if (!Array.isArray(value))
1035
+ return;
1036
+ for (const item of value.slice(0, this.maxSessions)) {
1037
+ if (!item || typeof item !== "object")
1038
+ continue;
1039
+ const r = item;
1040
+ if (typeof r.sessionId !== "string" || !r.sessionId.length || r.sessionId.length > 512 || ![r.observedSince, r.created, r.output, r.duration, r.completed].every((n) => typeof n === "number" && Number.isSafeInteger(n) && n >= 0) || typeof r.sequence !== "number" || !Number.isSafeInteger(r.sequence) || r.sequence < -1)
1041
+ continue;
1042
+ const { observedSince, created, output, duration, completed, sequence } = r;
1043
+ if (typeof observedSince !== "number" || typeof created !== "number" || typeof output !== "number" || typeof duration !== "number" || typeof completed !== "number")
1044
+ continue;
1045
+ this.rows.set(r.sessionId, {
1046
+ observedSince,
1047
+ created,
1048
+ output,
1049
+ duration,
1050
+ completed,
1051
+ sequence,
1052
+ partial: true
1053
+ });
1054
+ }
1055
+ }
1056
+ remove(id) {
1057
+ this.rows.delete(id);
1058
+ }
1059
+ ids() {
1060
+ return this.rows.keys();
1061
+ }
1062
+ }
1063
+ // src/charts.ts
1064
+ var rateFields = [
1065
+ "liveTps",
1066
+ "averageModelTps",
1067
+ "averageSessionTps",
1068
+ "sessionOutputTokens"
1069
+ ];
1070
+ function downsamplePoints(points, limit) {
1071
+ if (!Number.isSafeInteger(limit) || limit < 16 || limit > 5000)
1072
+ throw new Error("maxPoints must be an integer from 16 to 5000");
1073
+ if (points.length <= limit)
1074
+ return [...points];
1075
+ const buckets = Math.max(1, Math.floor(limit / (2 + rateFields.length * 2)));
1076
+ const selected = new Set;
1077
+ for (let b = 0;b < buckets; b++) {
1078
+ const start = Math.floor(b * points.length / buckets), end = Math.floor((b + 1) * points.length / buckets);
1079
+ selected.add(start);
1080
+ selected.add(end - 1);
1081
+ for (const field of rateFields) {
1082
+ let low, high;
1083
+ for (let i = start;i < end; i++) {
1084
+ const value = points[i]?.[field];
1085
+ if (value === undefined)
1086
+ continue;
1087
+ if (low === undefined || value < (points[low]?.[field] ?? Infinity))
1088
+ low = i;
1089
+ if (high === undefined || value > (points[high]?.[field] ?? -Infinity))
1090
+ high = i;
1091
+ }
1092
+ if (low !== undefined)
1093
+ selected.add(low);
1094
+ if (high !== undefined)
1095
+ selected.add(high);
1096
+ }
1097
+ }
1098
+ let previous = -1;
1099
+ return [...selected].sort((a, b) => a - b).flatMap((index) => {
1100
+ const point = points[index];
1101
+ if (!point)
1102
+ return [];
1103
+ const skippedGap = points.slice(previous + 1, index + 1).some((p) => p.breakBefore);
1104
+ previous = index;
1105
+ return [{ ...point, breakBefore: point.breakBefore || skippedGap }];
1106
+ });
1107
+ }
1108
+ function buildChartData(records, options = {}) {
1109
+ const maxPoints = options.maxPoints ?? 1000, maxSessions = options.maxSessions ?? 128;
1110
+ if (!Number.isSafeInteger(maxSessions) || maxSessions < 1 || maxSessions > 1024)
1111
+ throw new Error("maxSessions must be from 1 to 1024");
1112
+ downsamplePoints([], maxPoints);
1113
+ const sorted = [...records].sort((a, b) => a.time - b.time || a.recordId.localeCompare(b.recordId));
1114
+ const lastSeen = new Map;
1115
+ for (const r of sorted)
1116
+ if (r.performance && (!options.sessionId || r.sessionId === options.sessionId))
1117
+ lastSeen.set(r.sessionId, r.time);
1118
+ const sessions = [...lastSeen].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
1119
+ const selected = new Set(sessions.slice(0, maxSessions).map(([id]) => id));
1120
+ const series = new Map, gaps = [];
1121
+ const seen = new Set, requestSeen = new Set;
1122
+ const pendingGaps = new Set;
1123
+ const observedSince = new Map;
1124
+ for (const r of sorted) {
1125
+ if (seen.has(r.recordId))
1126
+ continue;
1127
+ seen.add(r.recordId);
1128
+ if (r.type === "gap") {
1129
+ if (r.sessionId === "__history__" || selected.has(r.sessionId)) {
1130
+ gaps.push({
1131
+ time: r.time,
1132
+ sessionId: r.sessionId,
1133
+ reason: r.event ?? "observation-gap"
1134
+ });
1135
+ if (r.sessionId === "__history__")
1136
+ for (const id of selected)
1137
+ pendingGaps.add(id);
1138
+ else
1139
+ pendingGaps.add(r.sessionId);
1140
+ }
1141
+ continue;
1142
+ }
1143
+ const p = r.performance;
1144
+ if (!p || !selected.has(r.sessionId))
1145
+ continue;
1146
+ let s = series.get(r.sessionId);
1147
+ if (!s) {
1148
+ s = {
1149
+ sessionId: r.sessionId,
1150
+ ...r.parentId ? { parentId: r.parentId } : {},
1151
+ agents: [],
1152
+ models: [],
1153
+ points: [],
1154
+ requests: [],
1155
+ originalPoints: 0
1156
+ };
1157
+ series.set(r.sessionId, s);
1158
+ }
1159
+ const agent = p.requestAgent ?? r.agent, model = p.requestModel ?? r.model;
1160
+ if (agent && !s.agents.includes(agent))
1161
+ s.agents.push(agent);
1162
+ if (model && !s.models.includes(model))
1163
+ s.models.push(model);
1164
+ const previous = s.points.at(-1);
1165
+ const restart = observedSince.has(r.sessionId) && observedSince.get(r.sessionId) !== p.observedSince;
1166
+ const reset = previous?.sessionOutputTokens !== undefined && p.sessionOutputTokens !== undefined && p.sessionOutputTokens < previous.sessionOutputTokens;
1167
+ const discontinuity = pendingGaps.has(r.sessionId) || restart || reset || !previous || previous.partial !== p.partial || previous.liveTps === undefined || p.liveTps === undefined || r.time - previous.time > 2500 || previous.agent !== agent || previous.model !== model;
1168
+ const point = {
1169
+ time: r.time,
1170
+ ...agent ? { agent } : {},
1171
+ ...model ? { model } : {},
1172
+ breakBefore: discontinuity,
1173
+ partial: p.partial,
1174
+ ...p.liveTps === undefined ? {} : { liveTps: p.liveTps },
1175
+ ...p.averageModelTps === undefined ? {} : { averageModelTps: p.averageModelTps },
1176
+ ...p.averageSessionTps === undefined ? {} : { averageSessionTps: p.averageSessionTps },
1177
+ ...p.sessionOutputTokens === undefined ? {} : { sessionOutputTokens: p.sessionOutputTokens }
1178
+ };
1179
+ if (previous?.time === point.time)
1180
+ s.points[s.points.length - 1] = {
1181
+ ...point,
1182
+ breakBefore: point.breakBefore || previous.breakBefore
1183
+ };
1184
+ else
1185
+ s.points.push(point);
1186
+ observedSince.set(r.sessionId, p.observedSince);
1187
+ pendingGaps.delete(r.sessionId);
1188
+ if (r.type === "request" && p.requestId !== undefined && p.requestStartedAt !== undefined) {
1189
+ const key = JSON.stringify([
1190
+ r.sessionId,
1191
+ p.requestId,
1192
+ p.requestStartedAt
1193
+ ]);
1194
+ if (requestSeen.has(key))
1195
+ continue;
1196
+ requestSeen.add(key);
1197
+ const duration = p.streamEndedAt === undefined ? undefined : p.streamEndedAt - p.requestStartedAt;
1198
+ const validDuration = duration !== undefined && duration > 0;
1199
+ const output = p.tokenSource === "reported" ? p.requestOutputTokens : undefined;
1200
+ s.requests.push({
1201
+ time: r.time,
1202
+ requestId: p.requestId,
1203
+ started: p.requestStartedAt,
1204
+ ...agent ? { agent } : {},
1205
+ ...model ? { model } : {},
1206
+ partial: p.partial,
1207
+ ...p.ttftMs === undefined ? {} : { ttftMs: p.ttftMs },
1208
+ ...output === undefined ? {} : { outputTokens: output },
1209
+ ...validDuration ? { durationMs: duration } : {},
1210
+ ...output !== undefined && validDuration ? { tps: output * 1000 / duration } : {}
1211
+ });
1212
+ }
1213
+ }
1214
+ return {
1215
+ schema: 1,
1216
+ series: [...series.values()].map((s) => ({
1217
+ ...s,
1218
+ originalPoints: s.points.length,
1219
+ points: downsamplePoints(s.points, maxPoints)
1220
+ })),
1221
+ gaps: gaps.slice(-1024),
1222
+ omittedGaps: Math.max(0, gaps.length - 1024),
1223
+ omittedSessions: Math.max(0, sessions.length - selected.size),
1224
+ maxPoints
1225
+ };
1226
+ }
1227
+ export {
1228
+ usableInput,
1229
+ promptTokens,
1230
+ project,
1231
+ parseConfig,
1232
+ outputTokens,
1233
+ metric,
1234
+ glob,
1235
+ familyRows,
1236
+ executionStatus,
1237
+ evaluateThreshold,
1238
+ estimate,
1239
+ downsamplePoints,
1240
+ buildChartData,
1241
+ SnapshotStore,
1242
+ PolicyResolver,
1243
+ PerformanceTracker,
1244
+ NoopBudgetController,
1245
+ ContextMonitor,
1246
+ CompactionScheduler
1247
+ };