@cesarus777/opencode-cgp 0.2.3 → 0.3.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/tui.js CHANGED
@@ -1,174 +1,33 @@
1
1
  // @bun
2
- // src/rpc.ts
3
- import { Rpc } from "@opencode/plugin/rpc";
4
- var count = {
5
- type: "integer",
6
- minimum: 0,
7
- maximum: Number.MAX_SAFE_INTEGER
8
- };
9
- var text = { type: "string", minLength: 1, maxLength: 512 };
10
- var enumeration = (...values) => ({ type: "string", enum: values });
11
- var object = (properties, required = Object.keys(properties)) => ({
12
- type: "object",
13
- properties,
14
- required,
15
- additionalProperties: false
16
- });
17
- var array = (items, maxItems = 128) => ({
18
- type: "array",
19
- items,
20
- maxItems
21
- });
22
- var measurement = object({
23
- tokens: count,
24
- at: count,
25
- model: text,
26
- requestId: text,
27
- provenance: enumeration("provider-normalized", "serialized-estimate"),
28
- confidence: enumeration("reported", "approximate"),
29
- scope: enumeration("completed-request", "prepared-request")
30
- }, ["tokens", "at", "model", "provenance", "confidence", "scope"]);
31
- var row = object({
32
- sessionId: text,
33
- revision: count,
34
- observedAt: count,
35
- parentId: text,
36
- agent: text,
37
- model: text,
38
- measurement,
39
- limits: object({ context: count, input: count, output: count }, [
40
- "context",
41
- "output"
42
- ]),
43
- capacityTokens: count,
44
- outputReserveTokens: count,
45
- peakTokens: count,
46
- policy: object({
47
- id: text,
48
- configured: object({
49
- type: enumeration("tokens", "ratio", "remaining"),
50
- value: { type: "number", minimum: 0 }
51
- }),
52
- triggerTokens: { type: "number" },
53
- comparison: enumeration(">=", ">"),
54
- reason: enumeration("missing-usage", "model-changed", "invalid-usage", "stale-usage", "missing-model-budget", "incompatible-model-budget", "threshold-crossed", "below-threshold"),
55
- crossed: { type: "boolean" }
56
- }, ["id", "configured", "comparison", "reason", "crossed"]),
57
- compaction: object({
58
- phase: enumeration("idle", "requesting", "accepted", "running", "awaiting-measurement", "blocked", "uncertain"),
59
- count,
60
- armed: { type: "boolean" },
61
- blockedReason: enumeration("session-removed", "compaction-failed", "reclaim-unverifiable", "insufficient-reclaim", "completion-event-timeout", "admission-outcome-unknown", "restart-needs-lifecycle-evidence"),
62
- lastAttempt: count,
63
- lastCompleted: count,
64
- cooldownUntil: count
65
- }, ["phase", "armed"]),
66
- cumulative: object({
67
- input: count,
68
- output: count,
69
- reasoning: count,
70
- cache: object({ read: count, write: count })
71
- }),
72
- cumulativeScope: enumeration("session", "unknown"),
73
- lastObservedActive: { type: "boolean" }
74
- }, [
75
- "sessionId",
76
- "revision",
77
- "observedAt",
78
- "policy",
79
- "compaction",
80
- "cumulativeScope",
81
- "lastObservedActive"
82
- ]);
83
- var batchFields = {
84
- schemaVersion: { const: 1 },
85
- epoch: text,
86
- sequence: count,
87
- generatedAt: count,
88
- health: object({
89
- enabled: { type: "boolean" },
90
- mode: enumeration("observe", "enforce"),
91
- enforcement: enumeration("observing", "available", "suspended", "disabled"),
92
- reasons: array(enumeration("event-stream-gap", "persistence-unavailable", "capacity-exceeded", "model-metadata-unavailable"), 8),
93
- staleMs: count,
94
- maxSessions: count
95
- }),
96
- upserts: array(row),
97
- removed: array(object({
98
- sessionId: text,
99
- revision: count,
100
- reason: enumeration("deleted", "evicted")
101
- }))
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
102
17
  };
103
- var readSchema = object({
104
- schemaVersion: { const: 1 },
105
- sessionIds: { ...array(text), uniqueItems: true }
106
- });
107
- var batchSchema = object(batchFields);
108
- var resultSchema = object({
109
- ...batchFields,
110
- unavailable: array(object({ sessionId: text, reason: enumeration("not-monitored") }))
111
- });
112
- var monitorRpc = Rpc.define({
113
- id: "context-guard.monitor.v1",
114
- methods: { read: { input: readSchema, output: resultSchema } },
115
- events: { changed: { schema: batchSchema } }
116
- });
117
- var nonnegative = { type: "number", minimum: 0 };
118
- var performanceSchema = object({
119
- observedSince: count,
120
- at: count,
121
- sessionCreatedAt: count,
122
- sessionOutputTokens: count,
123
- status: enumeration("waiting", "streaming", "idle", "unavailable"),
124
- partial: { type: "boolean" },
125
- liveTps: nonnegative,
126
- averageModelTps: nonnegative,
127
- averageSessionTps: nonnegative,
128
- ttftMs: nonnegative,
129
- latestRequestTps: nonnegative,
130
- requestId: text,
131
- requestAgent: text,
132
- requestModel: text,
133
- requestOutputTokens: count,
134
- requestStartedAt: count,
135
- firstOutputAt: count,
136
- streamEndedAt: count,
137
- estimatedOutputTokens: nonnegative,
138
- modelOutputTokens: nonnegative,
139
- modelDurationMs: nonnegative,
140
- completedRequests: count,
141
- tokenSource: enumeration("estimated", "reported", "unavailable")
142
- }, [
143
- "sessionCreatedAt",
144
- "observedSince",
145
- "at",
146
- "status",
147
- "partial",
148
- "estimatedOutputTokens",
149
- "modelOutputTokens",
150
- "modelDurationMs",
151
- "completedRequests",
152
- "tokenSource"
153
- ]);
154
- var performanceRow = {
155
- ...row,
156
- properties: { ...row.properties, performance: performanceSchema }
18
+ var __export = (target, all) => {
19
+ for (var name in all)
20
+ __defProp(target, name, {
21
+ get: all[name],
22
+ enumerable: true,
23
+ configurable: true,
24
+ set: (newValue) => all[name] = () => newValue
25
+ });
157
26
  };
158
- var performanceBatchSchema = object({
159
- ...batchFields,
160
- upserts: array(performanceRow)
161
- });
162
- var performanceResultSchema = object({
163
- ...batchFields,
164
- upserts: array(performanceRow),
165
- unavailable: resultSchema.properties.unavailable ?? {}
166
- });
167
- var performanceRpc = Rpc.define({
168
- id: "context-guard.monitor.v2",
169
- methods: { read: { input: readSchema, output: performanceResultSchema } },
170
- events: { changed: { schema: performanceBatchSchema } }
171
- });
27
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
28
+
29
+ // src/rpc.ts
30
+ import { Rpc } from "@opencode/plugin/rpc";
172
31
  function isPerformanceBatch(value) {
173
32
  return valid(value, performanceBatchSchema) && uniqueRows(value);
174
33
  }
@@ -178,84 +37,6 @@ function isPerformanceResult(value) {
178
37
  function isPerformanceSnapshot(value) {
179
38
  return valid(value, performanceSchema);
180
39
  }
181
- var chartPointSchema = object({
182
- time: count,
183
- agent: text,
184
- model: text,
185
- breakBefore: { type: "boolean" },
186
- partial: { type: "boolean" },
187
- liveTps: nonnegative,
188
- averageModelTps: nonnegative,
189
- averageSessionTps: nonnegative,
190
- sessionOutputTokens: count
191
- }, ["time", "breakBefore", "partial"]);
192
- var chartRequestSchema = object({
193
- time: count,
194
- requestId: text,
195
- started: count,
196
- agent: text,
197
- model: text,
198
- ttftMs: nonnegative,
199
- outputTokens: count,
200
- durationMs: nonnegative,
201
- tps: nonnegative,
202
- partial: { type: "boolean" }
203
- }, ["time", "requestId", "started", "partial"]);
204
- var historyReadSchema = object({
205
- schemaVersion: { const: 1 },
206
- sessionId: text,
207
- from: count,
208
- to: count,
209
- maxPoints: { type: "integer", minimum: 16, maximum: 500 }
210
- });
211
- var historyResultSchema = object({
212
- schemaVersion: { const: 1 },
213
- generatedAt: count,
214
- enabled: { type: "boolean" },
215
- truncated: { type: "boolean" },
216
- metadata: object({
217
- schema: { const: 1 },
218
- retainedFrom: count,
219
- retainedBytes: count,
220
- skippedBytes: count,
221
- malformedRecords: count,
222
- completeness: enumeration("bounded-retention")
223
- }, [
224
- "schema",
225
- "retainedBytes",
226
- "skippedBytes",
227
- "malformedRecords",
228
- "completeness"
229
- ]),
230
- data: object({
231
- schema: { const: 1 },
232
- omittedSessions: count,
233
- omittedGaps: count,
234
- maxPoints: count,
235
- gaps: array(object({ time: count, sessionId: text, reason: text }), 128),
236
- series: array(object({
237
- sessionId: text,
238
- parentId: text,
239
- agents: array(text, 1024),
240
- models: array(text, 1024),
241
- points: array(chartPointSchema, 500),
242
- requests: array(chartRequestSchema, 500),
243
- originalPoints: count
244
- }, [
245
- "sessionId",
246
- "agents",
247
- "models",
248
- "points",
249
- "requests",
250
- "originalPoints"
251
- ]), 1)
252
- }, ["schema", "omittedSessions", "maxPoints", "gaps", "series"])
253
- });
254
- var historyRpc = Rpc.define({
255
- id: "context-guard.history.v1",
256
- methods: { read: { input: historyReadSchema, output: historyResultSchema } },
257
- events: {}
258
- });
259
40
  function isHistoryRead(value) {
260
41
  return valid(value, historyReadSchema) && record(value) && typeof value.from === "number" && typeof value.to === "number" && value.from <= value.to && value.to - value.from <= 7 * 86400000;
261
42
  }
@@ -310,19 +91,252 @@ function uniqueRows(value) {
310
91
  const ids = [...value.upserts, ...value.removed].map((v) => record(v) ? v.sessionId : undefined);
311
92
  return ids.length <= 128 && new Set(ids).size === ids.length;
312
93
  }
313
-
314
- // src/tui.tsx
315
- import { effect as _$effect5 } from "@opentui/solid";
316
- import { createTextNode as _$createTextNode3 } from "@opentui/solid";
317
- import { insertNode as _$insertNode5 } from "@opentui/solid";
318
- import { setProp as _$setProp5 } from "@opentui/solid";
319
- import { createElement as _$createElement5 } from "@opentui/solid";
320
- import { createComponent as _$createComponent5 } from "@opentui/solid";
321
- import { Plugin } from "@opencode/plugin/tui";
322
- import { Show as Show4 } from "solid-js";
323
-
324
- // src/tui/state.ts
325
- import { createSignal } from "solid-js";
94
+ var count, text, enumeration = (...values) => ({ type: "string", enum: values }), object = (properties, required = Object.keys(properties)) => ({
95
+ type: "object",
96
+ properties,
97
+ required,
98
+ additionalProperties: false
99
+ }), array = (items, maxItems = 128) => ({
100
+ type: "array",
101
+ items,
102
+ maxItems
103
+ }), measurement, row, batchFields, readSchema, batchSchema, resultSchema, monitorRpc, nonnegative, performanceSchema, performanceRow, performanceBatchSchema, performanceResultSchema, performanceRpc, chartPointSchema, chartRequestSchema, historyReadSchema, historyResultSchema, historyRpc;
104
+ var init_rpc = __esm(() => {
105
+ count = {
106
+ type: "integer",
107
+ minimum: 0,
108
+ maximum: Number.MAX_SAFE_INTEGER
109
+ };
110
+ text = { type: "string", minLength: 1, maxLength: 512 };
111
+ measurement = object({
112
+ tokens: count,
113
+ at: count,
114
+ model: text,
115
+ requestId: text,
116
+ provenance: enumeration("provider-normalized", "serialized-estimate"),
117
+ confidence: enumeration("reported", "approximate"),
118
+ scope: enumeration("completed-request", "prepared-request")
119
+ }, ["tokens", "at", "model", "provenance", "confidence", "scope"]);
120
+ row = object({
121
+ sessionId: text,
122
+ revision: count,
123
+ observedAt: count,
124
+ parentId: text,
125
+ agent: text,
126
+ model: text,
127
+ measurement,
128
+ limits: object({ context: count, input: count, output: count }, [
129
+ "context",
130
+ "output"
131
+ ]),
132
+ capacityTokens: count,
133
+ outputReserveTokens: count,
134
+ peakTokens: count,
135
+ policy: object({
136
+ id: text,
137
+ configured: object({
138
+ type: enumeration("tokens", "ratio", "remaining"),
139
+ value: { type: "number", minimum: 0 }
140
+ }),
141
+ triggerTokens: { type: "number" },
142
+ comparison: enumeration(">=", ">"),
143
+ reason: enumeration("missing-usage", "model-changed", "invalid-usage", "stale-usage", "missing-model-budget", "incompatible-model-budget", "threshold-crossed", "below-threshold"),
144
+ crossed: { type: "boolean" }
145
+ }, ["id", "configured", "comparison", "reason", "crossed"]),
146
+ compaction: object({
147
+ phase: enumeration("idle", "requesting", "accepted", "running", "awaiting-measurement", "blocked", "uncertain"),
148
+ count,
149
+ armed: { type: "boolean" },
150
+ blockedReason: enumeration("session-removed", "compaction-failed", "reclaim-unverifiable", "insufficient-reclaim", "completion-event-timeout", "admission-outcome-unknown", "restart-needs-lifecycle-evidence"),
151
+ lastAttempt: count,
152
+ lastCompleted: count,
153
+ cooldownUntil: count
154
+ }, ["phase", "armed"]),
155
+ cumulative: object({
156
+ input: count,
157
+ output: count,
158
+ reasoning: count,
159
+ cache: object({ read: count, write: count })
160
+ }),
161
+ cumulativeScope: enumeration("session", "unknown"),
162
+ lastObservedActive: { type: "boolean" }
163
+ }, [
164
+ "sessionId",
165
+ "revision",
166
+ "observedAt",
167
+ "policy",
168
+ "compaction",
169
+ "cumulativeScope",
170
+ "lastObservedActive"
171
+ ]);
172
+ batchFields = {
173
+ schemaVersion: { const: 1 },
174
+ epoch: text,
175
+ sequence: count,
176
+ generatedAt: count,
177
+ health: object({
178
+ enabled: { type: "boolean" },
179
+ mode: enumeration("observe", "enforce"),
180
+ enforcement: enumeration("observing", "available", "suspended", "disabled"),
181
+ reasons: array(enumeration("event-stream-gap", "persistence-unavailable", "capacity-exceeded", "model-metadata-unavailable"), 8),
182
+ staleMs: count,
183
+ maxSessions: count
184
+ }),
185
+ upserts: array(row),
186
+ removed: array(object({
187
+ sessionId: text,
188
+ revision: count,
189
+ reason: enumeration("deleted", "evicted")
190
+ }))
191
+ };
192
+ readSchema = object({
193
+ schemaVersion: { const: 1 },
194
+ sessionIds: { ...array(text), uniqueItems: true }
195
+ });
196
+ batchSchema = object(batchFields);
197
+ resultSchema = object({
198
+ ...batchFields,
199
+ unavailable: array(object({ sessionId: text, reason: enumeration("not-monitored") }))
200
+ });
201
+ monitorRpc = Rpc.define({
202
+ id: "context-guard.monitor.v1",
203
+ methods: { read: { input: readSchema, output: resultSchema } },
204
+ events: { changed: { schema: batchSchema } }
205
+ });
206
+ nonnegative = { type: "number", minimum: 0 };
207
+ performanceSchema = object({
208
+ observedSince: count,
209
+ at: count,
210
+ sessionCreatedAt: count,
211
+ sessionOutputTokens: count,
212
+ status: enumeration("waiting", "streaming", "idle", "unavailable"),
213
+ partial: { type: "boolean" },
214
+ liveTps: nonnegative,
215
+ averageModelTps: nonnegative,
216
+ averageSessionTps: nonnegative,
217
+ ttftMs: nonnegative,
218
+ latestRequestTps: nonnegative,
219
+ requestId: text,
220
+ requestAgent: text,
221
+ requestModel: text,
222
+ requestOutputTokens: count,
223
+ requestStartedAt: count,
224
+ firstOutputAt: count,
225
+ streamEndedAt: count,
226
+ estimatedOutputTokens: nonnegative,
227
+ modelOutputTokens: nonnegative,
228
+ modelDurationMs: nonnegative,
229
+ completedRequests: count,
230
+ tokenSource: enumeration("estimated", "reported", "unavailable")
231
+ }, [
232
+ "sessionCreatedAt",
233
+ "observedSince",
234
+ "at",
235
+ "status",
236
+ "partial",
237
+ "estimatedOutputTokens",
238
+ "modelOutputTokens",
239
+ "modelDurationMs",
240
+ "completedRequests",
241
+ "tokenSource"
242
+ ]);
243
+ performanceRow = {
244
+ ...row,
245
+ properties: { ...row.properties, performance: performanceSchema }
246
+ };
247
+ performanceBatchSchema = object({
248
+ ...batchFields,
249
+ upserts: array(performanceRow)
250
+ });
251
+ performanceResultSchema = object({
252
+ ...batchFields,
253
+ upserts: array(performanceRow),
254
+ unavailable: resultSchema.properties.unavailable ?? {}
255
+ });
256
+ performanceRpc = Rpc.define({
257
+ id: "context-guard.monitor.v2",
258
+ methods: { read: { input: readSchema, output: performanceResultSchema } },
259
+ events: { changed: { schema: performanceBatchSchema } }
260
+ });
261
+ chartPointSchema = object({
262
+ time: count,
263
+ agent: text,
264
+ model: text,
265
+ breakBefore: { type: "boolean" },
266
+ partial: { type: "boolean" },
267
+ liveTps: nonnegative,
268
+ averageModelTps: nonnegative,
269
+ averageSessionTps: nonnegative,
270
+ sessionOutputTokens: count
271
+ }, ["time", "breakBefore", "partial"]);
272
+ chartRequestSchema = object({
273
+ time: count,
274
+ requestId: text,
275
+ started: count,
276
+ agent: text,
277
+ model: text,
278
+ ttftMs: nonnegative,
279
+ outputTokens: count,
280
+ durationMs: nonnegative,
281
+ tps: nonnegative,
282
+ partial: { type: "boolean" }
283
+ }, ["time", "requestId", "started", "partial"]);
284
+ historyReadSchema = object({
285
+ schemaVersion: { const: 1 },
286
+ sessionId: text,
287
+ from: count,
288
+ to: count,
289
+ maxPoints: { type: "integer", minimum: 16, maximum: 500 }
290
+ });
291
+ historyResultSchema = object({
292
+ schemaVersion: { const: 1 },
293
+ generatedAt: count,
294
+ enabled: { type: "boolean" },
295
+ truncated: { type: "boolean" },
296
+ metadata: object({
297
+ schema: { const: 1 },
298
+ retainedFrom: count,
299
+ retainedBytes: count,
300
+ skippedBytes: count,
301
+ malformedRecords: count,
302
+ completeness: enumeration("bounded-retention")
303
+ }, [
304
+ "schema",
305
+ "retainedBytes",
306
+ "skippedBytes",
307
+ "malformedRecords",
308
+ "completeness"
309
+ ]),
310
+ data: object({
311
+ schema: { const: 1 },
312
+ omittedSessions: count,
313
+ omittedGaps: count,
314
+ maxPoints: count,
315
+ gaps: array(object({ time: count, sessionId: text, reason: text }), 128),
316
+ series: array(object({
317
+ sessionId: text,
318
+ parentId: text,
319
+ agents: array(text, 1024),
320
+ models: array(text, 1024),
321
+ points: array(chartPointSchema, 500),
322
+ requests: array(chartRequestSchema, 500),
323
+ originalPoints: count
324
+ }, [
325
+ "sessionId",
326
+ "agents",
327
+ "models",
328
+ "points",
329
+ "requests",
330
+ "originalPoints"
331
+ ]), 1)
332
+ }, ["schema", "omittedSessions", "maxPoints", "gaps", "series"])
333
+ });
334
+ historyRpc = Rpc.define({
335
+ id: "context-guard.history.v1",
336
+ methods: { read: { input: historyReadSchema, output: historyResultSchema } },
337
+ events: {}
338
+ });
339
+ });
326
340
 
327
341
  // src/family.ts
328
342
  function metric(snapshot, now, staleMs) {
@@ -698,112 +712,490 @@ function abortable(promise, signal) {
698
712
  promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
699
713
  });
700
714
  }
715
+ var init_transport = __esm(() => {
716
+ init_rpc();
717
+ });
701
718
 
702
- // src/tui/inventory.ts
703
- async function discoverFamily(client, currentId, signal, cached, maxNodes = 1024, previous) {
704
- const sessions = new Map(previous?.sessions);
705
- let damaged = previous?.damaged ?? false, pages = 0, rootId = previous?.rootId ?? currentId;
706
- const get = async (id) => cached(id) ?? client.session.get({ sessionID: id }, { signal });
707
- if (!previous) {
708
- let current = await get(currentId);
709
- for (let depth = 0;depth <= 64; depth++) {
710
- if (signal.aborted)
711
- throw signal.reason;
712
- if (sessions.has(current.id) || sessions.size >= maxNodes) {
713
- damaged = true;
714
- break;
715
- }
716
- sessions.set(current.id, current);
717
- rootId = current.id;
718
- if (!current.parentID)
719
- break;
720
- if (depth === 64) {
721
- damaged = true;
722
- break;
723
- }
724
- try {
725
- current = await get(current.parentID);
726
- } catch {
727
- if (signal.aborted)
728
- throw signal.reason;
729
- damaged = true;
730
- break;
731
- }
732
- }
733
- }
734
- const queue = previous ? [...previous.pending] : [{ id: rootId, depth: 0, cursors: [] }];
735
- const scanned = new Set(previous?.scanned);
736
- while (queue.length && pages < 100) {
737
- const next = queue.shift();
738
- if (!next)
719
+ // src/tui/format.ts
720
+ function quantity(n) {
721
+ return n === undefined || !Number.isFinite(n) ? "?" : Math.abs(n) >= 1e6 ? `${(n / 1e6).toFixed(1)}m` : Math.abs(n) >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
722
+ }
723
+ function cells(value) {
724
+ return [...segmenter.segment(value)].reduce((n, { segment }) => n + graphemeWidth(segment), 0);
725
+ }
726
+ function graphemeWidth(s) {
727
+ const cp = s.codePointAt(0) ?? 0;
728
+ if (/^[\p{Mark}\u200d\ufe0f]+$/u.test(s))
729
+ return 0;
730
+ return /[\p{Extended_Pictographic}\p{Regional_Indicator}\u20e3]/u.test(s) || cp >= 4352 && (cp <= 4447 || cp >= 9001 && cp <= 9002 || cp >= 11904 && cp <= 42191 || cp >= 44032 && cp <= 55203 || cp >= 63744 && cp <= 64255 || cp >= 65040 && cp <= 65135 || cp >= 65281 && cp <= 65376 || cp >= 131072) ? 2 : 1;
731
+ }
732
+ function clip(value, width) {
733
+ const clean = sanitize(value);
734
+ if (cells(clean) <= width)
735
+ return clean;
736
+ let result = "", used = 0;
737
+ for (const { segment } of segmenter.segment(clean)) {
738
+ const size = graphemeWidth(segment);
739
+ if (used + size > Math.max(0, width - 3))
739
740
  break;
740
- if (!next.cursor && scanned.has(next.id)) {
741
- damaged = true;
742
- continue;
741
+ result += segment;
742
+ used += size;
743
+ }
744
+ return width < 3 ? ".".repeat(Math.max(0, width)) : result + "...";
745
+ }
746
+ function rowLines(row2, width) {
747
+ const s = row2.snapshot, m = s?.measurement;
748
+ const prefix = row2.current ? "* " : row2.depth > 5 ? ".../" : " ".repeat(Math.min(row2.depth, 5)) + "|-";
749
+ const percent = row2.utilizationPercent === undefined ? "?" : `${row2.utilizationPercent.toFixed(1)}%`;
750
+ const warning = row2.freshness === "stale" ? " STALE" : row2.pressure === "crossed" ? " !! crossed" : row2.pressure === "near" ? " ! near" : "";
751
+ const suffix = ` ${statuses[row2.node.status]} ${m?.confidence === "approximate" ? "~" : ""}${percent}${warning}`;
752
+ const label = clip(`${prefix}${row2.node.agent ?? row2.node.sessionId}`, Math.max(1, width - cells(suffix)));
753
+ return [
754
+ clip(`${label}${suffix}`, width),
755
+ ...performanceLines(row2, width),
756
+ ...wrap(`${quantity(m?.tokens)}/${quantity(s?.capacityTokens)} input; to ${quantity(row2.triggerDeltaTokens)}`, width),
757
+ ...s && s.compaction.phase !== "idle" ? wrap(`compact: ${s.compaction.phase}`, width) : [],
758
+ ...row2.node.interrupted ? [clip("interrupted", width)] : [],
759
+ ...row2.hidden ? wrap(`+ ${row2.hidden} completed descendants`, width) : []
760
+ ];
761
+ }
762
+ function detailLines(row2, width, expanded = false, ageMs = 0) {
763
+ const s = row2?.snapshot, m = s?.measurement;
764
+ if (!row2)
765
+ return wrap("Loading session family...", width);
766
+ if (!s)
767
+ return [
768
+ ...wrap(`${row2.node.agent ?? row2.node.sessionId} / ${row2.node.model ?? "?"}`, width),
769
+ ...wrap("Guard: not monitored / unavailable", width)
770
+ ];
771
+ const threshold = s.policy.configured;
772
+ const headroom = row2.triggerDeltaTokens;
773
+ const lines = [
774
+ `${s.agent ?? "?"} / ${s.model ?? "?"}`,
775
+ ...performanceLines(row2, width),
776
+ `${m?.confidence === "approximate" ? "~" : ""}${quantity(m?.tokens)} / ${quantity(s.capacityTokens)} input`,
777
+ `${row2.utilizationPercent?.toFixed(1) ?? "?"}% ${row2.pressure === "crossed" ? "!! crossed" : row2.pressure === "near" ? "! near" : row2.freshness.toUpperCase()}`,
778
+ `Trigger ${threshold.type === "ratio" ? `${threshold.value * 100}%` : `${threshold.value} ${threshold.type}`} (${s.policy.comparison})`,
779
+ `Resolved trigger ${s.policy.triggerTokens ?? "?"}`,
780
+ `Until trigger ${quantity(headroom)}`,
781
+ `Compact: ${s.compaction.phase}`,
782
+ `${s.compaction.armed ? "armed" : "disarmed"}${s.compaction.blockedReason ? ` / ${s.compaction.blockedReason}` : ""}`,
783
+ m ? `${m.confidence} ${m.scope}` : "measurement unknown",
784
+ m ? `${Math.floor(Math.max(0, ageMs) / 1000)}s ago; ${row2.freshness}` : ""
785
+ ];
786
+ if (expanded)
787
+ lines.push(`Policy ${s.policy.id}: ${s.policy.reason}`, `Limits in/context/out ${quantity(s.limits?.input)}/${quantity(s.limits?.context)}/${quantity(s.limits?.output)}`, `Output reserve ${quantity(s.outputReserveTokens)}`, `Provenance ${m?.provenance ?? "?"}`, `Cooldown until ${s.compaction.cooldownUntil ?? "?"}`, `Cumulative (${s.cumulativeScope})`, `Input ${quantity(s.cumulative?.input)} output ${quantity(s.cumulative?.output)}`, `Reasoning ${quantity(s.cumulative?.reasoning)}`, `Cache read ${quantity(s.cumulative?.cache.read)} write ${quantity(s.cumulative?.cache.write)}`);
788
+ return lines.filter(Boolean).flatMap((line) => wrap(line, width));
789
+ }
790
+ function sanitize(value) {
791
+ return value.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "").replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\x00-\x1f\x7f-\x9f]/g, "");
792
+ }
793
+ function wrap(value, width) {
794
+ const result = [];
795
+ let part = "";
796
+ for (const word of sanitize(value).split(" ")) {
797
+ if (part && cells(part + " " + word) > width) {
798
+ result.push(part);
799
+ part = "";
743
800
  }
744
- if (signal.aborted)
745
- throw signal.reason;
746
- try {
747
- pages++;
748
- const response = await client.session.list({
749
- parentID: next.id,
750
- limit: 100,
751
- order: "desc",
752
- ...next.cursor ? { cursor: next.cursor } : {}
753
- }, { signal });
754
- let capped = false;
755
- for (const child of response.data) {
756
- if (child.parentID !== next.id || child.id === next.id) {
757
- damaged = true;
758
- continue;
759
- }
760
- if (!sessions.has(child.id) && sessions.size >= maxNodes) {
761
- capped = true;
762
- break;
763
- }
764
- sessions.set(child.id, child);
765
- if (!scanned.has(child.id) && !queue.some((branch) => branch.id === child.id)) {
766
- if (next.depth < 64)
767
- queue.push({ id: child.id, depth: next.depth + 1, cursors: [] });
768
- else
769
- damaged = true;
770
- }
771
- }
772
- if (capped) {
773
- queue.unshift(next);
774
- break;
801
+ for (const { segment } of segmenter.segment((part ? " " : "") + word)) {
802
+ if (part && cells(part + segment) > width) {
803
+ result.push(part);
804
+ part = "";
775
805
  }
776
- const cursor = response.cursor?.next;
777
- if (cursor) {
778
- if (next.cursors.includes(cursor) || cursor === next.cursor)
779
- damaged = true;
780
- else
781
- queue.unshift({
782
- ...next,
783
- cursor,
784
- cursors: [...next.cursors, cursor]
785
- });
786
- } else
787
- scanned.add(next.id);
788
- } catch {
789
- if (signal.aborted)
790
- throw signal.reason;
791
- damaged = true;
806
+ part += cells(segment) <= width ? segment : "?";
792
807
  }
793
808
  }
794
- return {
795
- sessions,
796
- complete: !damaged && !queue.length,
797
- rootId,
798
- pages: (previous?.pages ?? 0) + pages,
799
- pending: queue,
800
- scanned,
809
+ if (part)
810
+ result.push(part);
811
+ return result;
812
+ }
813
+ function performanceLines(row2, width) {
814
+ const p = row2.snapshot?.performance;
815
+ if (!p)
816
+ return [];
817
+ const value = (n) => n === undefined ? "?" : n.toFixed(1);
818
+ return [
819
+ ...wrap(`TPS ${p.status === "waiting" ? "waiting" : `${p.status === "streaming" ? "~" : ""}${value(p.liveTps)}`} \xB7 ${p.status}${p.partial ? " \xB7 partial" : ""}`, width),
820
+ ...wrap(`Avg model ${value(p.averageModelTps)} \xB7 Avg session ${value(p.averageSessionTps)}`, width),
821
+ ...wrap(`TTFT ${p.ttftMs === undefined ? "?" : `~${(p.ttftMs / 1000).toFixed(2)}s`}${p.latestRequestTps === undefined ? "" : ` \xB7 Last ${value(p.latestRequestTps)} t/s`}`, width)
822
+ ];
823
+ }
824
+ var segmenter, statuses;
825
+ var init_format = __esm(() => {
826
+ segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
827
+ statuses = {
828
+ running: "RUN",
829
+ idle: "IDLE",
830
+ completed: "DONE",
831
+ failed: "FAIL",
832
+ unknown: "?"
833
+ };
834
+ });
835
+
836
+ // src/tui/chartformat.ts
837
+ function columns2(points, from, to, width, gaps = []) {
838
+ const size = Math.max(1, Math.min(120, Math.floor(width))), values = Array.from({ length: size });
839
+ const column = (time) => Math.max(0, Math.min(size - 1, Math.floor((time - from) / Math.max(1, to - from) * (size - 1))));
840
+ let min = Infinity, max = -Infinity;
841
+ for (const point of points) {
842
+ if (point.time < from || point.time > to || point.value === undefined || !Number.isFinite(point.value) || point.value < 0)
843
+ continue;
844
+ min = Math.min(min, point.value);
845
+ max = Math.max(max, point.value);
846
+ const i = column(point.time);
847
+ values[i] = Math.max(values[i] ?? -Infinity, point.value);
848
+ }
849
+ for (const time of gaps)
850
+ if (time >= from && time <= to)
851
+ values[column(time)] = undefined;
852
+ return { values, column, min, max };
853
+ }
854
+ function sparkline(points, from, to, width, gaps = []) {
855
+ const { values, column, min, max } = columns2(points, from, to, width, gaps);
856
+ const bars = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
857
+ const chars = values.map((v) => v === undefined ? " " : bars[Math.min(7, Math.floor(v / Math.max(1, max) * 7))] ?? "\u2581");
858
+ for (const time of gaps)
859
+ if (time >= from && time <= to)
860
+ chars[column(time)] = "\u2506";
861
+ return { line: chars.join(""), ...min === Infinity ? {} : { min, max } };
862
+ }
863
+ function metricPlot(points, from, to, width, gaps = []) {
864
+ const label = (value) => value >= 1e6 ? `${(value / 1e6).toFixed(1)}M` : value >= 1000 ? `${(value / 1000).toFixed(1)}k` : value.toFixed(1);
865
+ const peak = Math.max(0, ...points.filter((p) => p.time >= from && p.time <= to && Number.isFinite(p.value)).map((p) => p.value ?? 0));
866
+ const scale = Math.max(1, peak);
867
+ const gutter = width >= 20 ? Math.max(label(scale).length, 3) : 0;
868
+ const { values, column } = columns2(points, from, to, Math.max(1, width - gutter - 2), gaps);
869
+ const gapColumns = new Set(gaps.filter((time) => time >= from && time <= to).map(column));
870
+ const height = 6;
871
+ const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
872
+ const lines = Array.from({ length: height }, (_, row2) => {
873
+ const axis = gutter ? (row2 === 0 ? label(scale) : row2 === height - 1 ? "0.0" : "").padStart(gutter) : "";
874
+ return `${axis}\u2502${values.map((value, i) => {
875
+ if (gapColumns.has(i))
876
+ return "\u2506";
877
+ if (value === undefined)
878
+ return " ";
879
+ const fill = Math.max(0, Math.min(8, Math.ceil((value / scale * height - (height - row2 - 1)) * 8)));
880
+ return value === 0 && row2 === height - 1 ? "\u2581" : blocks[fill];
881
+ }).join("")}\u2502`;
882
+ });
883
+ lines.push(`${" ".repeat(gutter)}\u2514${"\u2500".repeat(values.length)}\u2518`);
884
+ const start = new Date(from).toISOString().slice(11, 16), end = new Date(to).toISOString().slice(11, 16);
885
+ const axisWidth = values.length + 2;
886
+ if (axisWidth >= 11)
887
+ lines.push(`${" ".repeat(gutter)}${start}${" ".repeat(axisWidth - 10)}${end}`);
888
+ return lines;
889
+ }
890
+ function historyChartLines(result, width, from, to, metricIndex = 0) {
891
+ if (!result.enabled)
892
+ return wrap("History recording is disabled on this server.", width);
893
+ const series = result.data.series[0];
894
+ if (!series)
895
+ return wrap("No retained measurements in this window.", width);
896
+ const gaps = result.data.gaps.map((g) => g.time);
897
+ const observations = (key) => series.points.map((p) => ({
898
+ time: p.time,
899
+ ...p[key] === undefined ? {} : { value: p[key] }
900
+ }));
901
+ const requests = (key) => series.requests.map((p) => ({
902
+ time: p.time,
903
+ ...p[key] === undefined ? {} : { value: key === "ttftMs" ? p[key] / 1000 : p[key] }
904
+ }));
905
+ const metric2 = chartMetrics[metricIndex] ?? chartMetrics[0];
906
+ const points = metric2.source === "points" ? observations(metric2.key) : requests(metric2.key);
907
+ const plotted = sparkline(points, from, to, width - 2, gaps);
908
+ return [
909
+ ...wrap(`\u2039 ${metric2.label} \u203A \xB7 ${metricIndex + 1}/${chartMetrics.length}`, width),
910
+ ...wrap(plotted.min === undefined ? "No measurements for this metric." : `${plotted.min.toFixed(1)}\u2013${plotted.max?.toFixed(1)} ${metric2.unit} \xB7 observed range`, width),
911
+ "",
912
+ ...metricPlot(points, from, to, width, gaps),
913
+ "",
914
+ ...wrap("UTC time \u2192 \xB7 peaks per column", width),
915
+ ...wrap("blank = no sample \xB7 \u2506 = gap", width),
916
+ ...wrap(`${new Date(from).toISOString()} \u2192 ${new Date(to).toISOString()}`, width),
917
+ ...series.points.some((p) => p.partial) ? wrap("Partial timing coverage; averages include only matched model durations.", width) : [],
918
+ ...result.truncated ? wrap("Downsampled/bounded history; some older requests or gap markers may be omitted.", width) : [],
919
+ ...result.metadata.malformedRecords ? wrap(`${result.metadata.malformedRecords} malformed/partial records skipped.`, width) : []
920
+ ];
921
+ }
922
+ var chartMetrics;
923
+ var init_chartformat = __esm(() => {
924
+ init_format();
925
+ chartMetrics = [
926
+ { label: "Live TPS ~", unit: "t/s", key: "liveTps", source: "points" },
927
+ { label: "Avg model", unit: "t/s", key: "averageModelTps", source: "points" },
928
+ {
929
+ label: "Avg session",
930
+ unit: "t/s",
931
+ key: "averageSessionTps",
932
+ source: "points"
933
+ },
934
+ { label: "TTFT ~", unit: "s", key: "ttftMs", source: "requests" },
935
+ { label: "Request throughput", unit: "t/s", key: "tps", source: "requests" },
936
+ {
937
+ label: "Cumulative output",
938
+ unit: "tokens",
939
+ key: "sessionOutputTokens",
940
+ source: "points"
941
+ }
942
+ ];
943
+ });
944
+
945
+ // src/tui.tsx
946
+ import { effect as _$effect5 } from "@opentui/solid";
947
+ import { createTextNode as _$createTextNode3 } from "@opentui/solid";
948
+ import { insertNode as _$insertNode5 } from "@opentui/solid";
949
+ import { setProp as _$setProp5 } from "@opentui/solid";
950
+ import { createElement as _$createElement5 } from "@opentui/solid";
951
+ import { createComponent as _$createComponent5 } from "@opentui/solid";
952
+ import { Plugin } from "@opencode/plugin/tui";
953
+ import { Show as Show4 } from "solid-js";
954
+
955
+ // src/tui/state.ts
956
+ init_rpc();
957
+ import { createSignal } from "solid-js";
958
+
959
+ // src/tui/contextchart.ts
960
+ function updateContextTrace(previous, row2) {
961
+ const snapshot = row2?.snapshot;
962
+ const sessionId = row2?.node.sessionId ?? "";
963
+ const sameSession = previous?.sessionId === sessionId;
964
+ const model = snapshot?.model ?? row2?.node.model ?? (sameSession ? previous?.model : undefined);
965
+ const capacity = snapshot?.capacityTokens ?? (sameSession && model === previous?.model ? previous?.capacity : undefined);
966
+ const compatible = sameSession && previous?.model === model && previous.capacity === capacity;
967
+ const samples = compatible ? previous.samples : [];
968
+ const completed = snapshot?.compaction.lastCompleted ?? (compatible ? previous.lastCompleted : undefined);
969
+ let pending = compatible ? previous.pendingCompleted : undefined;
970
+ if (completed !== undefined && completed !== previous?.lastCompleted)
971
+ pending = completed;
972
+ const measurement2 = snapshot?.measurement;
973
+ const valid2 = (row2?.freshness === "fresh" || row2?.freshness === "stale") && measurement2 && Number.isFinite(measurement2.tokens) && measurement2.tokens >= 0;
974
+ const at = measurement2?.at ?? snapshot?.observedAt;
975
+ let next = samples;
976
+ if (at !== undefined && Number.isFinite(at)) {
977
+ const last = samples.at(-1);
978
+ if (!last || at >= last.at) {
979
+ const reset = valid2 && (at === last?.at && last.compacted === true || pending !== undefined && measurement2.at >= pending && measurement2.confidence === "reported" && measurement2.scope === "completed-request" && snapshot?.compaction.phase !== "running");
980
+ const waiting = pending !== undefined && !reset;
981
+ const tokens = valid2 && !waiting ? Math.max(reset ? 0 : last?.tokens ?? 0, measurement2.tokens) : last?.tokens;
982
+ const held = tokens !== undefined && (!valid2 || waiting || tokens !== measurement2.tokens);
983
+ const sample = {
984
+ at,
985
+ ...tokens === undefined ? {} : { tokens },
986
+ ...held ? { held: true } : {},
987
+ ...reset || at === last?.at && last.compacted ? { compacted: true } : {}
988
+ };
989
+ if (reset)
990
+ pending = undefined;
991
+ if (!last || at > last.at && sample.tokens !== last.tokens) {
992
+ next = [...samples, sample].slice(-96);
993
+ } else if (sample.at !== last.at || sample.tokens !== last.tokens || sample.held !== last.held || sample.compacted !== last.compacted) {
994
+ next = [...samples.slice(0, -1), sample];
995
+ }
996
+ }
997
+ }
998
+ return {
999
+ sessionId,
1000
+ model,
1001
+ capacity,
1002
+ samples: next,
1003
+ ...completed === undefined ? {} : { lastCompleted: completed },
1004
+ ...pending === undefined ? {} : { pendingCompleted: pending }
1005
+ };
1006
+ }
1007
+
1008
+ class ContextTraceStore {
1009
+ traces = new Map;
1010
+ update(row2) {
1011
+ if (!row2)
1012
+ return updateContextTrace(undefined, undefined);
1013
+ const id = row2.node.sessionId;
1014
+ const trace = updateContextTrace(this.traces.get(id), row2);
1015
+ this.traces.delete(id);
1016
+ this.traces.set(id, trace);
1017
+ if (this.traces.size > 128) {
1018
+ const oldest = this.traces.keys().next().value;
1019
+ if (oldest !== undefined)
1020
+ this.traces.delete(oldest);
1021
+ }
1022
+ return trace;
1023
+ }
1024
+ clear() {
1025
+ this.traces.clear();
1026
+ }
1027
+ }
1028
+ function contextChart(samples, capacity, trigger, width, now) {
1029
+ if (capacity === undefined || !Number.isFinite(capacity) || capacity <= 0 || width < 4)
1030
+ return [];
1031
+ const height = 8;
1032
+ const gutter = width >= 16 ? 4 : 0;
1033
+ const size = Math.max(1, Math.min(96, width - gutter - 2));
1034
+ const last = samples.at(-1);
1035
+ const paused = now !== undefined && last?.tokens !== undefined && now - last.at > 2500;
1036
+ const values = samples.slice(-size).map((sample, index, visible) => paused && index === visible.length - 1 ? { ...sample, held: true } : sample);
1037
+ const offset = size - values.length;
1038
+ const thresholdRow = trigger !== undefined && Number.isFinite(trigger) && trigger >= 0 && trigger <= capacity ? Math.round((1 - trigger / capacity) * (height - 1)) : -1;
1039
+ const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
1040
+ const lines = Array.from({ length: height }, (_, row2) => {
1041
+ const threshold = row2 === thresholdRow;
1042
+ const axis = gutter ? (row2 === 0 ? "100%" : row2 === height - 1 ? "0%" : "").padStart(gutter) : "";
1043
+ const plot = Array.from({ length: size }, (_2, col) => {
1044
+ const sample = values[col - offset];
1045
+ const value = sample?.tokens;
1046
+ const fill = value === undefined ? 0 : Math.max(0, Math.min(8, Math.ceil((Math.min(value / capacity, 1) * height - (height - row2 - 1)) * 8)));
1047
+ const tone = sample?.held ? "held" : "value";
1048
+ if (fill > 0)
1049
+ return { text: blocks[fill] ?? " ", tone };
1050
+ if (threshold)
1051
+ return { text: "\u2504", tone: "threshold" };
1052
+ if (value === 0 && row2 === height - 1)
1053
+ return { text: "\u2581", tone };
1054
+ return { text: " ", tone: "axis" };
1055
+ });
1056
+ const line = `${axis}\u2502${plot.map((cell) => cell.text).join("")}\u2502`;
1057
+ const segments = [
1058
+ { text: `${axis}\u2502`, held: false, tone: "axis" }
1059
+ ];
1060
+ for (const cell of plot) {
1061
+ const { text: text2, tone } = cell;
1062
+ const held = tone === "held";
1063
+ const previous = segments.at(-1);
1064
+ if (previous?.tone === tone)
1065
+ segments[segments.length - 1] = {
1066
+ text: previous.text + text2,
1067
+ held,
1068
+ tone
1069
+ };
1070
+ else
1071
+ segments.push({ text: text2, held, tone });
1072
+ }
1073
+ segments.push({ text: "\u2502", held: false, tone: "axis" });
1074
+ return { line, threshold, segments };
1075
+ });
1076
+ lines.push({
1077
+ line: `${" ".repeat(gutter)}\u2514${"\u2500".repeat(size)}\u2518`,
1078
+ threshold: false,
1079
+ segments: [
1080
+ {
1081
+ text: `${" ".repeat(gutter)}\u2514${"\u2500".repeat(size)}\u2518`,
1082
+ held: false,
1083
+ tone: "axis"
1084
+ }
1085
+ ]
1086
+ });
1087
+ return lines;
1088
+ }
1089
+
1090
+ // src/tui/state.ts
1091
+ init_transport();
1092
+
1093
+ // src/tui/inventory.ts
1094
+ async function discoverFamily(client, currentId, signal, cached, maxNodes = 1024, previous) {
1095
+ const sessions = new Map(previous?.sessions);
1096
+ let damaged = previous?.damaged ?? false, pages = 0, rootId = previous?.rootId ?? currentId;
1097
+ const get = async (id) => cached(id) ?? client.session.get({ sessionID: id }, { signal });
1098
+ if (!previous) {
1099
+ let current = await get(currentId);
1100
+ for (let depth = 0;depth <= 64; depth++) {
1101
+ if (signal.aborted)
1102
+ throw signal.reason;
1103
+ if (sessions.has(current.id) || sessions.size >= maxNodes) {
1104
+ damaged = true;
1105
+ break;
1106
+ }
1107
+ sessions.set(current.id, current);
1108
+ rootId = current.id;
1109
+ if (!current.parentID)
1110
+ break;
1111
+ if (depth === 64) {
1112
+ damaged = true;
1113
+ break;
1114
+ }
1115
+ try {
1116
+ current = await get(current.parentID);
1117
+ } catch {
1118
+ if (signal.aborted)
1119
+ throw signal.reason;
1120
+ damaged = true;
1121
+ break;
1122
+ }
1123
+ }
1124
+ }
1125
+ const queue = previous ? [...previous.pending] : [{ id: rootId, depth: 0, cursors: [] }];
1126
+ const scanned = new Set(previous?.scanned);
1127
+ while (queue.length && pages < 100) {
1128
+ const next = queue.shift();
1129
+ if (!next)
1130
+ break;
1131
+ if (!next.cursor && scanned.has(next.id)) {
1132
+ damaged = true;
1133
+ continue;
1134
+ }
1135
+ if (signal.aborted)
1136
+ throw signal.reason;
1137
+ try {
1138
+ pages++;
1139
+ const response = await client.session.list({
1140
+ parentID: next.id,
1141
+ limit: 100,
1142
+ order: "desc",
1143
+ ...next.cursor ? { cursor: next.cursor } : {}
1144
+ }, { signal });
1145
+ let capped = false;
1146
+ for (const child of response.data) {
1147
+ if (child.parentID !== next.id || child.id === next.id) {
1148
+ damaged = true;
1149
+ continue;
1150
+ }
1151
+ if (!sessions.has(child.id) && sessions.size >= maxNodes) {
1152
+ capped = true;
1153
+ break;
1154
+ }
1155
+ sessions.set(child.id, child);
1156
+ if (!scanned.has(child.id) && !queue.some((branch) => branch.id === child.id)) {
1157
+ if (next.depth < 64)
1158
+ queue.push({ id: child.id, depth: next.depth + 1, cursors: [] });
1159
+ else
1160
+ damaged = true;
1161
+ }
1162
+ }
1163
+ if (capped) {
1164
+ queue.unshift(next);
1165
+ break;
1166
+ }
1167
+ const cursor = response.cursor?.next;
1168
+ if (cursor) {
1169
+ if (next.cursors.includes(cursor) || cursor === next.cursor)
1170
+ damaged = true;
1171
+ else
1172
+ queue.unshift({
1173
+ ...next,
1174
+ cursor,
1175
+ cursors: [...next.cursors, cursor]
1176
+ });
1177
+ } else
1178
+ scanned.add(next.id);
1179
+ } catch {
1180
+ if (signal.aborted)
1181
+ throw signal.reason;
1182
+ damaged = true;
1183
+ }
1184
+ }
1185
+ return {
1186
+ sessions,
1187
+ complete: !damaged && !queue.length,
1188
+ rootId,
1189
+ pages: (previous?.pages ?? 0) + pages,
1190
+ pending: queue,
1191
+ scanned,
801
1192
  damaged
802
1193
  };
803
1194
  }
804
1195
 
805
1196
  // src/tui/state.ts
806
1197
  function createGuardState(context) {
1198
+ const contextTraces = new ContextTraceStore;
807
1199
  const [version, setVersion] = createSignal(0);
808
1200
  const notify = () => {
809
1201
  setVersion((n) => n + 1);
@@ -986,301 +1378,138 @@ function createGuardState(context) {
986
1378
  },
987
1379
  release() {
988
1380
  consumers.delete(token);
989
- if (consumers.size)
990
- select();
991
- else
992
- stop();
993
- }
994
- };
995
- }
996
- function stop() {
997
- controller?.abort();
998
- scanGeneration++;
999
- scanning = false;
1000
- for (const c of clients.values())
1001
- c.stop();
1002
- clients.clear();
1003
- clearTimeout(expiryTimer);
1004
- clearInterval(ageTimer);
1005
- clearInterval(scanTimer);
1006
- clearTimeout(eventTimer);
1007
- unlisten?.();
1008
- unlisten = undefined;
1009
- inventory = undefined;
1010
- evidence.clear();
1011
- currentId = "";
1012
- }
1013
- function rows(stableOrder = [], allDescendants = false) {
1014
- version();
1015
- const snapshots = new Map([...clients.values()].flatMap((c2) => [...c2.rows]));
1016
- const nodes = [...inventory?.sessions.values() ?? []].map((original) => {
1017
- const s = context.data.session.get(original.id) ?? original;
1018
- const observed = evidence.get(s.id) ?? {};
1019
- const metadataAt = s.time.idle ?? 0;
1020
- const e = s.outcome && metadataAt >= (observed.terminalAt ?? 0) ? { ...observed, outcome: s.outcome, terminalAt: metadataAt } : observed;
1021
- return {
1022
- sessionId: s.id,
1023
- ...s.parentID ? { parentId: s.parentID } : {},
1024
- ...s.agent ? { agent: s.agent } : {},
1025
- ...s.model ? { model: `${s.model.providerID}/${s.model.id}` } : {},
1026
- ...e.outcome === "interrupted" ? { interrupted: true } : {},
1027
- status: executionStatus(true, context.data.session.status(s.id) === "running", e)
1028
- };
1029
- });
1030
- const c = [...clients.values()].find((c2) => c2.rows.has(currentId)) ?? clients.values().next().value;
1031
- return familyRows(nodes, snapshots, currentId, c?.now() ?? Date.now(), c?.health?.staleMs ?? 300000, allDescendants ? new Set(nodes.map((node) => node.sessionId)) : expanded, maxNodes, (id) => {
1032
- const session = inventory?.sessions.get(id);
1033
- const client = session ? clients.get(locationKey(session.location)) : undefined;
1034
- return {
1035
- now: client?.now() ?? Date.now(),
1036
- staleMs: client?.health?.staleMs ?? 300000
1037
- };
1038
- }, stableOrder);
1039
- }
1040
- return {
1041
- version,
1042
- acquire,
1043
- async history(id, from, to, signal) {
1044
- const info = inventory?.sessions.get(id);
1045
- if (!info)
1046
- throw new Error("Session location unavailable");
1047
- const result = await context.client.rpc(historyRpc).read({ schemaVersion: 1, sessionId: id, from, to, maxPoints: 240 }, { location: info.location, signal });
1048
- if (!isHistoryResult(result))
1049
- throw new Error("Incompatible history service");
1050
- return result;
1051
- },
1052
- rows,
1053
- dispose: stop,
1054
- age(id) {
1055
- version();
1056
- const c = [...clients.values()].find((c2) => c2.rows.has(id));
1057
- const m = c?.rows.get(id)?.measurement;
1058
- return c && m ? c.now() - m.at : 0;
1059
- },
1060
- health() {
1061
- version();
1062
- return [...clients.values()].find((c) => c.rows.has(currentId))?.health ?? clients.values().next().value?.health;
1063
- },
1064
- connection() {
1065
- version();
1066
- return clients.size ? [...clients.values()].map((c) => c.connection).find((c) => c !== "live") ?? "live" : scanFailed ? "unavailable" : "loading";
1067
- },
1068
- complete() {
1069
- version();
1070
- return inventory?.complete ?? false;
1071
- },
1072
- refresh() {
1073
- scan();
1074
- for (const c of clients.values())
1075
- c.refresh();
1076
- },
1077
- loadMore() {
1078
- maxNodes = Math.min(2048, maxNodes + 1024);
1079
- scan(true);
1080
- },
1081
- expand(id) {
1082
- if (expanded.has(id))
1083
- expanded.delete(id);
1084
- else if (expanded.size < 1024)
1085
- expanded.add(id);
1086
- savePreferences((draft) => {
1087
- draft.expanded = [...expanded];
1088
- }).catch(() => {
1089
- });
1090
- notify();
1091
- }
1092
- };
1093
- }
1094
- function locationKey(location) {
1095
- return JSON.stringify([location.directory, location.workspaceID ?? null]);
1096
- }
1097
- function sameLocation(a, b) {
1098
- if (a.workspaceID && b.workspaceID)
1099
- return a.workspaceID === b.workspaceID;
1100
- return a.directory === b.directory;
1101
- }
1102
-
1103
- // src/tui/sidebar.tsx
1104
- import { effect as _$effect2 } from "@opentui/solid";
1105
- import { createComponent as _$createComponent2 } from "@opentui/solid";
1106
- import { insert as _$insert2 } from "@opentui/solid";
1107
- import { createTextNode as _$createTextNode2 } from "@opentui/solid";
1108
- import { insertNode as _$insertNode2 } from "@opentui/solid";
1109
- import { memo as _$memo2 } from "@opentui/solid";
1110
- import { setProp as _$setProp2 } from "@opentui/solid";
1111
- import { createElement as _$createElement2 } from "@opentui/solid";
1112
- import { For as For2, createEffect as createEffect2, createMemo as createMemo2, onCleanup, Show as Show2 } from "solid-js";
1113
-
1114
- // src/tui/format.ts
1115
- function quantity(n) {
1116
- return n === undefined || !Number.isFinite(n) ? "?" : Math.abs(n) >= 1e6 ? `${(n / 1e6).toFixed(1)}m` : Math.abs(n) >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
1117
- }
1118
- var segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
1119
- function cells(value) {
1120
- return [...segmenter.segment(value)].reduce((n, { segment }) => n + graphemeWidth(segment), 0);
1121
- }
1122
- function graphemeWidth(s) {
1123
- const cp = s.codePointAt(0) ?? 0;
1124
- if (/^[\p{Mark}\u200d\ufe0f]+$/u.test(s))
1125
- return 0;
1126
- return /[\p{Extended_Pictographic}\p{Regional_Indicator}\u20e3]/u.test(s) || cp >= 4352 && (cp <= 4447 || cp >= 9001 && cp <= 9002 || cp >= 11904 && cp <= 42191 || cp >= 44032 && cp <= 55203 || cp >= 63744 && cp <= 64255 || cp >= 65040 && cp <= 65135 || cp >= 65281 && cp <= 65376 || cp >= 131072) ? 2 : 1;
1127
- }
1128
- function clip(value, width) {
1129
- const clean = sanitize(value);
1130
- if (cells(clean) <= width)
1131
- return clean;
1132
- let result = "", used = 0;
1133
- for (const { segment } of segmenter.segment(clean)) {
1134
- const size = graphemeWidth(segment);
1135
- if (used + size > Math.max(0, width - 3))
1136
- break;
1137
- result += segment;
1138
- used += size;
1139
- }
1140
- return width < 3 ? ".".repeat(Math.max(0, width)) : result + "...";
1141
- }
1142
- var statuses = {
1143
- running: "RUN",
1144
- idle: "IDLE",
1145
- completed: "DONE",
1146
- failed: "FAIL",
1147
- unknown: "?"
1148
- };
1149
- function rowLines(row2, width) {
1150
- const s = row2.snapshot, m = s?.measurement;
1151
- const prefix = row2.current ? "* " : row2.depth > 5 ? ".../" : " ".repeat(Math.min(row2.depth, 5)) + "|-";
1152
- const percent = row2.utilizationPercent === undefined ? "?" : `${row2.utilizationPercent.toFixed(1)}%`;
1153
- const warning = row2.freshness === "stale" ? " STALE" : row2.pressure === "crossed" ? " !! crossed" : row2.pressure === "near" ? " ! near" : "";
1154
- const suffix = ` ${statuses[row2.node.status]} ${m?.confidence === "approximate" ? "~" : ""}${percent}${warning}`;
1155
- const label = clip(`${prefix}${row2.node.agent ?? row2.node.sessionId}`, Math.max(1, width - cells(suffix)));
1156
- return [
1157
- clip(`${label}${suffix}`, width),
1158
- ...performanceLines(row2, width),
1159
- ...wrap(`${quantity(m?.tokens)}/${quantity(s?.capacityTokens)} input; to ${quantity(row2.triggerDeltaTokens)}`, width),
1160
- ...s && s.compaction.phase !== "idle" ? wrap(`compact: ${s.compaction.phase}`, width) : [],
1161
- ...row2.node.interrupted ? [clip("interrupted", width)] : [],
1162
- ...row2.hidden ? wrap(`+ ${row2.hidden} completed descendants`, width) : []
1163
- ];
1164
- }
1165
- function detailLines(row2, width, expanded = false, ageMs = 0) {
1166
- const s = row2?.snapshot, m = s?.measurement;
1167
- if (!row2)
1168
- return wrap("Loading session family...", width);
1169
- if (!s)
1170
- return [
1171
- ...wrap(`${row2.node.agent ?? row2.node.sessionId} / ${row2.node.model ?? "?"}`, width),
1172
- ...wrap("Guard: not monitored / unavailable", width)
1173
- ];
1174
- const threshold = s.policy.configured;
1175
- const headroom = row2.triggerDeltaTokens;
1176
- const lines = [
1177
- `${s.agent ?? "?"} / ${s.model ?? "?"}`,
1178
- ...performanceLines(row2, width),
1179
- `${m?.confidence === "approximate" ? "~" : ""}${quantity(m?.tokens)} / ${quantity(s.capacityTokens)} input`,
1180
- `${row2.utilizationPercent?.toFixed(1) ?? "?"}% ${row2.pressure === "crossed" ? "!! crossed" : row2.pressure === "near" ? "! near" : row2.freshness.toUpperCase()}`,
1181
- `Trigger ${threshold.type === "ratio" ? `${threshold.value * 100}%` : `${threshold.value} ${threshold.type}`} (${s.policy.comparison})`,
1182
- `Resolved trigger ${s.policy.triggerTokens ?? "?"}`,
1183
- `Until trigger ${quantity(headroom)}`,
1184
- `Compact: ${s.compaction.phase}`,
1185
- `${s.compaction.armed ? "armed" : "disarmed"}${s.compaction.blockedReason ? ` / ${s.compaction.blockedReason}` : ""}`,
1186
- m ? `${m.confidence} ${m.scope}` : "measurement unknown",
1187
- m ? `${Math.floor(Math.max(0, ageMs) / 1000)}s ago; ${row2.freshness}` : ""
1188
- ];
1189
- if (expanded)
1190
- lines.push(`Policy ${s.policy.id}: ${s.policy.reason}`, `Limits in/context/out ${quantity(s.limits?.input)}/${quantity(s.limits?.context)}/${quantity(s.limits?.output)}`, `Output reserve ${quantity(s.outputReserveTokens)}`, `Provenance ${m?.provenance ?? "?"}`, `Cooldown until ${s.compaction.cooldownUntil ?? "?"}`, `Cumulative (${s.cumulativeScope})`, `Input ${quantity(s.cumulative?.input)} output ${quantity(s.cumulative?.output)}`, `Reasoning ${quantity(s.cumulative?.reasoning)}`, `Cache read ${quantity(s.cumulative?.cache.read)} write ${quantity(s.cumulative?.cache.write)}`);
1191
- return lines.filter(Boolean).flatMap((line) => wrap(line, width));
1192
- }
1193
- function sanitize(value) {
1194
- return value.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "").replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\x00-\x1f\x7f-\x9f]/g, "");
1195
- }
1196
- function wrap(value, width) {
1197
- const result = [];
1198
- let part = "";
1199
- for (const word of sanitize(value).split(" ")) {
1200
- if (part && cells(part + " " + word) > width) {
1201
- result.push(part);
1202
- part = "";
1203
- }
1204
- for (const { segment } of segmenter.segment((part ? " " : "") + word)) {
1205
- if (part && cells(part + segment) > width) {
1206
- result.push(part);
1207
- part = "";
1208
- }
1209
- part += cells(segment) <= width ? segment : "?";
1210
- }
1211
- }
1212
- if (part)
1213
- result.push(part);
1214
- return result;
1215
- }
1216
- function performanceLines(row2, width) {
1217
- const p = row2.snapshot?.performance;
1218
- if (!p)
1219
- return [];
1220
- const value = (n) => n === undefined ? "?" : n.toFixed(1);
1221
- return [
1222
- ...wrap(`TPS ${p.status === "waiting" ? "waiting" : `${p.status === "streaming" ? "~" : ""}${value(p.liveTps)}`} \xB7 ${p.status}${p.partial ? " \xB7 partial" : ""}`, width),
1223
- ...wrap(`Avg model ${value(p.averageModelTps)} \xB7 Avg session ${value(p.averageSessionTps)}`, width),
1224
- ...wrap(`TTFT ${p.ttftMs === undefined ? "?" : `~${(p.ttftMs / 1000).toFixed(2)}s`}${p.latestRequestTps === undefined ? "" : ` \xB7 Last ${value(p.latestRequestTps)} t/s`}`, width)
1225
- ];
1226
- }
1227
-
1228
- // src/tui/contextchart.ts
1229
- function updateContextTrace(previous, row2) {
1230
- const snapshot = row2?.snapshot;
1231
- const model = snapshot?.model ?? row2?.node.model;
1232
- const capacity = snapshot?.capacityTokens;
1233
- const sessionId = row2?.node.sessionId ?? "";
1234
- const samples = previous?.sessionId === sessionId && previous.model === model && previous.capacity === capacity ? previous.samples : [];
1235
- const measurement2 = snapshot?.measurement;
1236
- const valid2 = (row2?.freshness === "fresh" || row2?.freshness === "stale") && measurement2 && Number.isFinite(measurement2.tokens) && measurement2.tokens >= 0;
1237
- const at = measurement2?.at ?? snapshot?.observedAt;
1238
- let next = samples;
1239
- if (at !== undefined && Number.isFinite(at)) {
1240
- const sample = {
1241
- at,
1242
- ...valid2 ? { tokens: measurement2.tokens } : {}
1381
+ if (consumers.size)
1382
+ select();
1383
+ else
1384
+ stop();
1385
+ }
1243
1386
  };
1244
- const last = samples.at(-1);
1245
- if (!last || at > last.at)
1246
- next = [...samples, sample].slice(-96);
1247
- else if (at === last.at && sample.tokens !== last.tokens)
1248
- next = [...samples.slice(0, -1), sample];
1249
1387
  }
1250
- return { sessionId, model, capacity, samples: next };
1388
+ function stop() {
1389
+ controller?.abort();
1390
+ scanGeneration++;
1391
+ scanning = false;
1392
+ for (const c of clients.values())
1393
+ c.stop();
1394
+ clients.clear();
1395
+ clearTimeout(expiryTimer);
1396
+ clearInterval(ageTimer);
1397
+ clearInterval(scanTimer);
1398
+ clearTimeout(eventTimer);
1399
+ unlisten?.();
1400
+ unlisten = undefined;
1401
+ inventory = undefined;
1402
+ evidence.clear();
1403
+ currentId = "";
1404
+ }
1405
+ function rows(stableOrder = [], allDescendants = false) {
1406
+ version();
1407
+ const snapshots = new Map([...clients.values()].flatMap((c2) => [...c2.rows]));
1408
+ const nodes = [...inventory?.sessions.values() ?? []].map((original) => {
1409
+ const s = context.data.session.get(original.id) ?? original;
1410
+ const observed = evidence.get(s.id) ?? {};
1411
+ const metadataAt = s.time.idle ?? 0;
1412
+ const e = s.outcome && metadataAt >= (observed.terminalAt ?? 0) ? { ...observed, outcome: s.outcome, terminalAt: metadataAt } : observed;
1413
+ return {
1414
+ sessionId: s.id,
1415
+ ...s.parentID ? { parentId: s.parentID } : {},
1416
+ ...s.agent ? { agent: s.agent } : {},
1417
+ ...s.model ? { model: `${s.model.providerID}/${s.model.id}` } : {},
1418
+ ...e.outcome === "interrupted" ? { interrupted: true } : {},
1419
+ status: executionStatus(true, context.data.session.status(s.id) === "running", e)
1420
+ };
1421
+ });
1422
+ const c = [...clients.values()].find((c2) => c2.rows.has(currentId)) ?? clients.values().next().value;
1423
+ return familyRows(nodes, snapshots, currentId, c?.now() ?? Date.now(), c?.health?.staleMs ?? 300000, allDescendants ? new Set(nodes.map((node) => node.sessionId)) : expanded, maxNodes, (id) => {
1424
+ const session = inventory?.sessions.get(id);
1425
+ const client = session ? clients.get(locationKey(session.location)) : undefined;
1426
+ return {
1427
+ now: client?.now() ?? Date.now(),
1428
+ staleMs: client?.health?.staleMs ?? 300000
1429
+ };
1430
+ }, stableOrder);
1431
+ }
1432
+ return {
1433
+ version,
1434
+ acquire,
1435
+ async history(id, from, to, signal) {
1436
+ const info = inventory?.sessions.get(id);
1437
+ if (!info)
1438
+ throw new Error("Session location unavailable");
1439
+ const result = await context.client.rpc(historyRpc).read({ schemaVersion: 1, sessionId: id, from, to, maxPoints: 240 }, { location: info.location, signal });
1440
+ if (!isHistoryResult(result))
1441
+ throw new Error("Incompatible history service");
1442
+ return result;
1443
+ },
1444
+ rows,
1445
+ contextTrace: (row2) => contextTraces.update(row2),
1446
+ dispose() {
1447
+ stop();
1448
+ contextTraces.clear();
1449
+ },
1450
+ age(id) {
1451
+ version();
1452
+ const c = [...clients.values()].find((c2) => c2.rows.has(id));
1453
+ const m = c?.rows.get(id)?.measurement;
1454
+ return c && m ? c.now() - m.at : 0;
1455
+ },
1456
+ health() {
1457
+ version();
1458
+ return [...clients.values()].find((c) => c.rows.has(currentId))?.health ?? clients.values().next().value?.health;
1459
+ },
1460
+ connection() {
1461
+ version();
1462
+ return clients.size ? [...clients.values()].map((c) => c.connection).find((c) => c !== "live") ?? "live" : scanFailed ? "unavailable" : "loading";
1463
+ },
1464
+ complete() {
1465
+ version();
1466
+ return inventory?.complete ?? false;
1467
+ },
1468
+ refresh() {
1469
+ scan();
1470
+ for (const c of clients.values())
1471
+ c.refresh();
1472
+ },
1473
+ loadMore() {
1474
+ maxNodes = Math.min(2048, maxNodes + 1024);
1475
+ scan(true);
1476
+ },
1477
+ expand(id) {
1478
+ if (expanded.has(id))
1479
+ expanded.delete(id);
1480
+ else if (expanded.size < 1024)
1481
+ expanded.add(id);
1482
+ savePreferences((draft) => {
1483
+ draft.expanded = [...expanded];
1484
+ }).catch(() => {
1485
+ });
1486
+ notify();
1487
+ }
1488
+ };
1251
1489
  }
1252
- function contextChart(samples, capacity, trigger, width) {
1253
- if (capacity === undefined || !Number.isFinite(capacity) || capacity <= 0 || width < 4)
1254
- return [];
1255
- const height = 8;
1256
- const gutter = width >= 16 ? 4 : 0;
1257
- const size = Math.max(1, Math.min(96, width - gutter - 2));
1258
- const values = samples.slice(-size);
1259
- const offset = size - values.length;
1260
- const thresholdRow = trigger !== undefined && Number.isFinite(trigger) && trigger >= 0 && trigger <= capacity ? Math.round((1 - trigger / capacity) * (height - 1)) : -1;
1261
- const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
1262
- const lines = Array.from({ length: height }, (_, row2) => {
1263
- const threshold = row2 === thresholdRow;
1264
- const axis = gutter ? (row2 === 0 ? "100%" : row2 === height - 1 ? "0%" : "").padStart(gutter) : "";
1265
- const plot = Array.from({ length: size }, (_2, col) => {
1266
- const value = values[col - offset]?.tokens;
1267
- const fill = value === undefined ? 0 : Math.max(0, Math.min(8, Math.ceil((Math.min(value / capacity, 1) * height - (height - row2 - 1)) * 8)));
1268
- if (threshold)
1269
- return fill > 0 ? "\u2542" : "\u2504";
1270
- if (value === 0 && row2 === height - 1)
1271
- return "\u2581";
1272
- return blocks[fill] ?? " ";
1273
- }).join("");
1274
- return { line: `${axis}\u2502${plot}\u2502`, threshold };
1275
- });
1276
- lines.push({
1277
- line: `${" ".repeat(gutter)}\u2514${"\u2500".repeat(size)}\u2518`,
1278
- threshold: false
1279
- });
1280
- return lines;
1490
+ function locationKey(location) {
1491
+ return JSON.stringify([location.directory, location.workspaceID ?? null]);
1492
+ }
1493
+ function sameLocation(a, b) {
1494
+ if (a.workspaceID && b.workspaceID)
1495
+ return a.workspaceID === b.workspaceID;
1496
+ return a.directory === b.directory;
1281
1497
  }
1282
1498
 
1499
+ // src/tui/sidebar.tsx
1500
+ init_format();
1501
+ import { effect as _$effect2 } from "@opentui/solid";
1502
+ import { createComponent as _$createComponent2 } from "@opentui/solid";
1503
+ import { insert as _$insert2 } from "@opentui/solid";
1504
+ import { createTextNode as _$createTextNode2 } from "@opentui/solid";
1505
+ import { insertNode as _$insertNode2 } from "@opentui/solid";
1506
+ import { memo as _$memo2 } from "@opentui/solid";
1507
+ import { setProp as _$setProp2 } from "@opentui/solid";
1508
+ import { createElement as _$createElement2 } from "@opentui/solid";
1509
+ import { For as For2, createEffect as createEffect2, createMemo as createMemo2, onCleanup, Show as Show2 } from "solid-js";
1510
+
1283
1511
  // src/tui/compact.tsx
1512
+ init_format();
1284
1513
  import { createTextNode as _$createTextNode } from "@opentui/solid";
1285
1514
  import { insertNode as _$insertNode } from "@opentui/solid";
1286
1515
  import { setProp as _$setProp } from "@opentui/solid";
@@ -1292,6 +1521,7 @@ import { createComponent as _$createComponent } from "@opentui/solid";
1292
1521
  import { For, Index, Show, createEffect, createMemo, createSignal as createSignal2 } from "solid-js";
1293
1522
 
1294
1523
  // src/tui/compactformat.ts
1524
+ init_format();
1295
1525
  function tokens(value) {
1296
1526
  if (value === undefined || !Number.isFinite(value))
1297
1527
  return "?";
@@ -1314,7 +1544,7 @@ function progress(ratio, width) {
1314
1544
  const filled = valid2 ? Math.round(Math.min(1, ratio) * size) : 0;
1315
1545
  return clip("\u2588".repeat(filled) + "\u2591".repeat(size - filled) + " " + label, width);
1316
1546
  }
1317
- function mainSections(row2, width, age, samples) {
1547
+ function mainSections(row2, width, age, samples, chartCapacity) {
1318
1548
  if (!row2)
1319
1549
  return [{ tone: "base", lines: wrap("Loading session family...", width) }];
1320
1550
  const s = row2.snapshot, m = s?.measurement;
@@ -1335,10 +1565,17 @@ function mainSections(row2, width, age, samples) {
1335
1565
  ...columns("Context", `${tokens(measured)} / ${tokens(s?.capacityTokens)}`, width)
1336
1566
  ]
1337
1567
  },
1338
- ...contextChart(samples ?? (m && valid2 ? [{ at: m.at, tokens: m.tokens }] : []), s?.capacityTokens, s?.policy.triggerTokens, width).map(({ line, threshold }) => ({
1568
+ ...contextChart(samples ?? (m && valid2 ? [{ at: m.at, tokens: m.tokens }] : []), s?.capacityTokens ?? chartCapacity, s?.policy.triggerTokens, width, m ? m.at + age : s?.observedAt === undefined ? undefined : s.observedAt + age).map(({ line, threshold, segments }) => ({
1339
1569
  tone: threshold ? "threshold" : "pressure",
1340
- lines: [line]
1570
+ lines: [line],
1571
+ ...segments ? { segments } : {}
1341
1572
  })),
1573
+ ...samples?.some((sample) => sample.held) || m && age > 2500 ? [
1574
+ {
1575
+ tone: "measurement",
1576
+ lines: wrap("muted held \xB7 last known context", width)
1577
+ }
1578
+ ] : [],
1342
1579
  {
1343
1580
  tone: "base",
1344
1581
  lines: wrap(`${ratio(s?.capacityTokens) === undefined ? "?" : Math.round((ratio(s?.capacityTokens) ?? 0) * 100)}% used \xB7 recent samples \u2192 now`, width)
@@ -1472,7 +1709,7 @@ function CompactSidebar(props) {
1472
1709
  setCollapsed(new Set);
1473
1710
  });
1474
1711
  const current = createMemo(() => props.rows.find((r) => r.node.sessionId === props.sessionID));
1475
- const trace = createMemo((previous) => updateContextTrace(previous, current()));
1712
+ const trace = createMemo(() => props.state.contextTrace(current()));
1476
1713
  const tree = createMemo(() => subagentTree(props.rows, props.sessionID, collapsed(), hideCompleted(), byPressure()));
1477
1714
  const entries = createMemo(() => new Map(tree().visible.map((entry) => [entry.row.node.sessionId, entry])));
1478
1715
  const toggle = (id) => setCollapsed((previous) => {
@@ -1538,7 +1775,7 @@ function CompactSidebar(props) {
1538
1775
  const head = cells(`\u2500\u2500 ${title} `) > width() ? title : `\u2500\u2500 ${title} `;
1539
1776
  return clip(head, width()) + "\u2500".repeat(Math.max(0, width() - cells(head)));
1540
1777
  };
1541
- const notices = createMemo(() => [...props.context.app.version !== "2.0.29" ? wrap(`Runtime ${props.context.app.version}: unverified`, width()) : [], ...props.state.connection() !== "live" ? wrap(`Connection: ${props.state.connection()}`, width()) : [], ...props.state.health()?.mode === "observe" ? ["Observe mode"] : [], ...props.state.health()?.enforcement === "disabled" || props.state.health()?.enforcement === "suspended" ? wrap(`Guard: ${props.state.health()?.enforcement}`, width()) : [], ...(props.state.health()?.reasons ?? []).flatMap((reason) => wrap(reason, width()))]);
1778
+ const notices = createMemo(() => [...!["2.0.29", "2.1.2"].includes(props.context.app.version) ? wrap(`Runtime ${props.context.app.version}: unverified`, width()) : [], ...props.state.connection() !== "live" ? wrap(`Connection: ${props.state.connection()}`, width()) : [], ...props.state.health()?.mode === "observe" ? ["Observe mode"] : [], ...props.state.health()?.enforcement === "disabled" || props.state.health()?.enforcement === "suspended" ? wrap(`Guard: ${props.state.health()?.enforcement}`, width()) : [], ...(props.state.health()?.reasons ?? []).flatMap((reason) => wrap(reason, width()))]);
1542
1779
  return (() => {
1543
1780
  var _el$2 = _$createElement("box"), _el$3 = _$createElement("text"), _el$5 = _$createElement("text"), _el$6 = _$createElement("text"), _el$8 = _$createElement("text"), _el$9 = _$createElement("text"), _el$0 = _$createElement("text"), _el$1 = _$createElement("text"), _el$11 = _$createElement("text"), _el$12 = _$createElement("text");
1544
1781
  _$insertNode(_el$2, _el$3);
@@ -1562,7 +1799,7 @@ function CompactSidebar(props) {
1562
1799
  });
1563
1800
  _$insert(_el$2, _$createComponent(Index, {
1564
1801
  get each() {
1565
- return mainSections(current(), width(), props.state.age(props.sessionID), trace().samples);
1802
+ return mainSections(current(), width(), props.state.age(props.sessionID), trace().samples, trace().capacity);
1566
1803
  },
1567
1804
  children: (section2) => _$createComponent(Index, {
1568
1805
  get each() {
@@ -1570,7 +1807,29 @@ function CompactSidebar(props) {
1570
1807
  },
1571
1808
  children: (line) => (() => {
1572
1809
  var _el$13 = _$createElement("text");
1573
- _$insert(_el$13, () => line() || " ");
1810
+ _$insert(_el$13, _$createComponent(Show, {
1811
+ get when() {
1812
+ return section2().segments;
1813
+ },
1814
+ get fallback() {
1815
+ return line() || " ";
1816
+ },
1817
+ get children() {
1818
+ return _$createComponent(For, {
1819
+ get each() {
1820
+ return section2().segments;
1821
+ },
1822
+ children: (part) => (() => {
1823
+ var _el$14 = _$createElement("span");
1824
+ _$insert(_el$14, () => part.text);
1825
+ _$effect((_$p) => _$setProp(_el$14, "style", {
1826
+ fg: part.tone === "axis" || part.tone === "held" ? colors().muted : part.tone === "threshold" ? colors().feedback.warning.base : current()?.freshness === "fresh" ? colors().base : colors().muted
1827
+ }, _$p));
1828
+ return _el$14;
1829
+ })()
1830
+ });
1831
+ }
1832
+ }));
1574
1833
  _$effect((_$p) => _$setProp(_el$13, "fg", section2().tone === "execution" ? executionColor(current()) : section2().tone === "threshold" ? colors().feedback.warning.base : section2().tone === "pressure" ? pressureColor(current()) : section2().tone === "measurement" ? current()?.freshness === "fresh" ? colors().muted : colors().feedback.warning.base : colors().base, _$p));
1575
1834
  return _el$13;
1576
1835
  })()
@@ -1581,10 +1840,10 @@ function CompactSidebar(props) {
1581
1840
  return notices();
1582
1841
  },
1583
1842
  children: (line) => (() => {
1584
- var _el$14 = _$createElement("text");
1585
- _$insert(_el$14, line);
1586
- _$effect((_$p) => _$setProp(_el$14, "fg", colors().feedback.warning.base, _$p));
1587
- return _el$14;
1843
+ var _el$15 = _$createElement("text");
1844
+ _$insert(_el$15, line);
1845
+ _$effect((_$p) => _$setProp(_el$15, "fg", colors().feedback.warning.base, _$p));
1846
+ return _el$15;
1588
1847
  })()
1589
1848
  }), _el$3);
1590
1849
  _$insertNode(_el$3, _$createTextNode(` `));
@@ -1610,60 +1869,60 @@ function CompactSidebar(props) {
1610
1869
  return row2();
1611
1870
  },
1612
1871
  children: (item) => (() => {
1613
- var _el$15 = _$createElement("box"), _el$16 = _$createElement("box"), _el$18 = _$createElement("text"), _el$19 = _$createElement("span"), _el$20 = _$createElement("span"), _el$21 = _$createElement("text");
1614
- _$insertNode(_el$15, _el$16);
1615
- _$insertNode(_el$15, _el$21);
1616
- _$setProp(_el$15, "flexDirection", "column");
1617
- _$setProp(_el$15, "flexShrink", 0);
1618
- _$insertNode(_el$16, _el$18);
1619
- _$setProp(_el$16, "flexDirection", "row");
1872
+ var _el$16 = _$createElement("box"), _el$17 = _$createElement("box"), _el$19 = _$createElement("text"), _el$20 = _$createElement("span"), _el$21 = _$createElement("span"), _el$22 = _$createElement("text");
1873
+ _$insertNode(_el$16, _el$17);
1874
+ _$insertNode(_el$16, _el$22);
1875
+ _$setProp(_el$16, "flexDirection", "column");
1620
1876
  _$setProp(_el$16, "flexShrink", 0);
1621
- _$insert(_el$16, _$createComponent(Show, {
1877
+ _$insertNode(_el$17, _el$19);
1878
+ _$setProp(_el$17, "flexDirection", "row");
1879
+ _$setProp(_el$17, "flexShrink", 0);
1880
+ _$insert(_el$17, _$createComponent(Show, {
1622
1881
  get when() {
1623
1882
  return prefix();
1624
1883
  },
1625
1884
  get children() {
1626
- var _el$17 = _$createElement("text");
1627
- _$setProp(_el$17, "onMouseDown", () => toggle(id));
1628
- _$insert(_el$17, prefix);
1629
- _$effect((_$p) => _$setProp(_el$17, "fg", colors().muted, _$p));
1630
- return _el$17;
1885
+ var _el$18 = _$createElement("text");
1886
+ _$setProp(_el$18, "onMouseDown", () => toggle(id));
1887
+ _$insert(_el$18, prefix);
1888
+ _$effect((_$p) => _$setProp(_el$18, "fg", colors().muted, _$p));
1889
+ return _el$18;
1631
1890
  }
1632
- }), _el$18);
1633
- _$insertNode(_el$18, _el$19);
1634
- _$insertNode(_el$18, _el$20);
1635
- _$setProp(_el$18, "onMouseDown", () => props.context.ui.router.navigate({
1891
+ }), _el$19);
1892
+ _$insertNode(_el$19, _el$20);
1893
+ _$insertNode(_el$19, _el$21);
1894
+ _$setProp(_el$19, "onMouseDown", () => props.context.ui.router.navigate({
1636
1895
  type: "session",
1637
1896
  sessionID: id
1638
1897
  }));
1639
- _$insert(_el$19, () => agentHeading(item(), nameWidth()).slice(0, 1));
1640
- _$insert(_el$20, () => agentHeading(item(), nameWidth()).slice(1));
1641
- _$insert(_el$15, _$createComponent(Index, {
1898
+ _$insert(_el$20, () => agentHeading(item(), nameWidth()).slice(0, 1));
1899
+ _$insert(_el$21, () => agentHeading(item(), nameWidth()).slice(1));
1900
+ _$insert(_el$16, _$createComponent(Index, {
1642
1901
  get each() {
1643
1902
  return agentLines(item(), Math.max(1, width() - cells(indent()) - 2));
1644
1903
  },
1645
1904
  children: (line) => (() => {
1646
- var _el$23 = _$createElement("text"), _el$24 = _$createTextNode(` `);
1647
- _$insertNode(_el$23, _el$24);
1648
- _$insert(_el$23, indent, _el$24);
1649
- _$insert(_el$23, line, null);
1650
- _$effect((_$p) => _$setProp(_el$23, "fg", rowColor(item()), _$p));
1651
- return _el$23;
1905
+ var _el$24 = _$createElement("text"), _el$25 = _$createTextNode(` `);
1906
+ _$insertNode(_el$24, _el$25);
1907
+ _$insert(_el$24, indent, _el$25);
1908
+ _$insert(_el$24, line, null);
1909
+ _$effect((_$p) => _$setProp(_el$24, "fg", rowColor(item()), _$p));
1910
+ return _el$24;
1652
1911
  })()
1653
- }), _el$21);
1654
- _$insertNode(_el$21, _$createTextNode(` `));
1912
+ }), _el$22);
1913
+ _$insertNode(_el$22, _$createTextNode(` `));
1655
1914
  _$effect((_p$) => {
1656
1915
  var _v$0 = rowColor(item()), _v$1 = {
1657
1916
  fg: executionColor(item())
1658
1917
  };
1659
- _v$0 !== _p$.e && (_p$.e = _$setProp(_el$18, "fg", _v$0, _p$.e));
1660
- _v$1 !== _p$.t && (_p$.t = _$setProp(_el$19, "style", _v$1, _p$.t));
1918
+ _v$0 !== _p$.e && (_p$.e = _$setProp(_el$19, "fg", _v$0, _p$.e));
1919
+ _v$1 !== _p$.t && (_p$.t = _$setProp(_el$20, "style", _v$1, _p$.t));
1661
1920
  return _p$;
1662
1921
  }, {
1663
1922
  e: undefined,
1664
1923
  t: undefined
1665
1924
  });
1666
- return _el$15;
1925
+ return _el$16;
1667
1926
  })()
1668
1927
  });
1669
1928
  }
@@ -1717,8 +1976,8 @@ function GuardView(props) {
1717
1976
  onCleanup(() => consumer.release());
1718
1977
  const rows = createMemo2(() => props.family ?? props.state.rows([], !props.expanded));
1719
1978
  const current = createMemo2(() => rows().find((row2) => row2.node.sessionId === props.sessionID));
1720
- const trace = createMemo2((previous) => updateContextTrace(previous, props.expanded ? current() : undefined));
1721
- const chart = createMemo2(() => contextChart(trace().samples, current()?.snapshot?.capacityTokens, current()?.snapshot?.policy.triggerTokens, props.width));
1979
+ const trace = createMemo2(() => props.state.contextTrace(props.expanded ? current() : undefined));
1980
+ const chart = createMemo2(() => contextChart(trace().samples, current()?.snapshot?.capacityTokens ?? trace().capacity, current()?.snapshot?.policy.triggerTokens, props.width, current()?.snapshot?.measurement ? (current()?.snapshot?.measurement?.at ?? 0) + props.state.age(props.sessionID) : undefined));
1722
1981
  const detail = createMemo2(() => {
1723
1982
  const lines = detailLines(current(), props.width, props.expanded, props.state.age(props.sessionID));
1724
1983
  if (props.expanded) {
@@ -1780,8 +2039,8 @@ function GuardView(props) {
1780
2039
  _$insertNode2(_el$2, _el$3);
1781
2040
  _$insert2(_el$2, () => props.state.health()?.mode ?? "?", null);
1782
2041
  _$insert2(_el$4, (() => {
1783
- var _c$ = _$memo2(() => props.context.app.version === "2.0.29");
1784
- return () => _c$() ? "" : clip(`Runtime ${props.context.app.version}: unverified (2.0.29 target)`, props.width);
2042
+ var _c$ = _$memo2(() => !!["2.0.29", "2.1.2"].includes(props.context.app.version));
2043
+ return () => _c$() ? "" : clip(`Runtime ${props.context.app.version}: unverified (tested: 2.0.29, 2.1.2)`, props.width);
1785
2044
  })());
1786
2045
  _$insert2(_el$5, () => clip(`${props.state.connection()} / ${props.state.health()?.enforcement ?? "?"}`, props.width));
1787
2046
  _$insert2(_el$, _$createComponent2(For2, {
@@ -1790,20 +2049,42 @@ function GuardView(props) {
1790
2049
  },
1791
2050
  children: (item) => (() => {
1792
2051
  var _el$1 = _$createElement2("text");
1793
- _$insert2(_el$1, () => item.line);
2052
+ _$insert2(_el$1, _$createComponent2(Show2, {
2053
+ get when() {
2054
+ return item.segments;
2055
+ },
2056
+ get fallback() {
2057
+ return item.line;
2058
+ },
2059
+ get children() {
2060
+ return _$createComponent2(For2, {
2061
+ get each() {
2062
+ return item.segments;
2063
+ },
2064
+ children: (part) => (() => {
2065
+ var _el$10 = _$createElement2("span");
2066
+ _$insert2(_el$10, () => part.text);
2067
+ _$effect2((_$p) => _$setProp2(_el$10, "style", {
2068
+ fg: part.tone === "axis" || part.tone === "held" ? props.context.theme.text.muted : part.tone === "threshold" ? props.context.theme.text.feedback.warning.base : current()?.freshness === "fresh" ? props.context.theme.text.base : props.context.theme.text.muted
2069
+ }, _$p));
2070
+ return _el$10;
2071
+ })()
2072
+ });
2073
+ }
2074
+ }));
1794
2075
  _$effect2((_$p) => _$setProp2(_el$1, "fg", item.threshold ? props.context.theme.text.feedback.warning.base : props.context.theme.text.muted, _$p));
1795
2076
  return _el$1;
1796
2077
  })()
1797
2078
  }), _el$6);
1798
2079
  _$insert2(_el$, _$createComponent2(For2, {
1799
2080
  get each() {
1800
- return wrap("Recent context samples \u2192 now \xB7 \u2504 trigger", props.width);
2081
+ return wrap("Recent context samples \u2192 now \xB7 \u2504 trigger \xB7 muted held", props.width);
1801
2082
  },
1802
2083
  children: (line) => (() => {
1803
- var _el$10 = _$createElement2("text");
1804
- _$insert2(_el$10, line);
1805
- _$effect2((_$p) => _$setProp2(_el$10, "fg", props.context.theme.text.muted, _$p));
1806
- return _el$10;
2084
+ var _el$11 = _$createElement2("text");
2085
+ _$insert2(_el$11, line);
2086
+ _$effect2((_$p) => _$setProp2(_el$11, "fg", props.context.theme.text.muted, _$p));
2087
+ return _el$11;
1807
2088
  })()
1808
2089
  }), _el$6);
1809
2090
  _$insert2(_el$, _$createComponent2(For2, {
@@ -1811,10 +2092,10 @@ function GuardView(props) {
1811
2092
  return detail();
1812
2093
  },
1813
2094
  children: (line) => (() => {
1814
- var _el$11 = _$createElement2("text");
1815
- _$insert2(_el$11, line);
1816
- _$effect2((_$p) => _$setProp2(_el$11, "fg", props.context.theme.text.base, _$p));
1817
- return _el$11;
2095
+ var _el$12 = _$createElement2("text");
2096
+ _$insert2(_el$12, line);
2097
+ _$effect2((_$p) => _$setProp2(_el$12, "fg", props.context.theme.text.base, _$p));
2098
+ return _el$12;
1818
2099
  })()
1819
2100
  }), _el$6);
1820
2101
  _$insert2(_el$, _$createComponent2(For2, {
@@ -1822,10 +2103,10 @@ function GuardView(props) {
1822
2103
  return props.state.health()?.reasons ?? [];
1823
2104
  },
1824
2105
  children: (reason) => (() => {
1825
- var _el$12 = _$createElement2("text");
1826
- _$insert2(_el$12, () => clip(reason, props.width));
1827
- _$effect2((_$p) => _$setProp2(_el$12, "fg", props.context.theme.text.feedback.warning.base, _$p));
1828
- return _el$12;
2106
+ var _el$13 = _$createElement2("text");
2107
+ _$insert2(_el$13, () => clip(reason, props.width));
2108
+ _$effect2((_$p) => _$setProp2(_el$13, "fg", props.context.theme.text.feedback.warning.base, _$p));
2109
+ return _el$13;
1829
2110
  })()
1830
2111
  }), _el$6);
1831
2112
  _$insert2(_el$6, (() => {
@@ -1841,9 +2122,9 @@ function GuardView(props) {
1841
2122
  return visible();
1842
2123
  },
1843
2124
  children: (row2) => (() => {
1844
- var _el$13 = _$createElement2("box");
1845
- _$setProp2(_el$13, "flexDirection", "column");
1846
- _$setProp2(_el$13, "onMouseDown", () => {
2125
+ var _el$14 = _$createElement2("box");
2126
+ _$setProp2(_el$14, "flexDirection", "column");
2127
+ _$setProp2(_el$14, "onMouseDown", () => {
1847
2128
  if (row2.hidden)
1848
2129
  props.state.expand(row2.node.sessionId);
1849
2130
  else {
@@ -1855,19 +2136,19 @@ function GuardView(props) {
1855
2136
  });
1856
2137
  }
1857
2138
  });
1858
- _$insert2(_el$13, _$createComponent2(For2, {
2139
+ _$insert2(_el$14, _$createComponent2(For2, {
1859
2140
  get each() {
1860
2141
  return rowLines(row2, Math.max(1, props.width - (props.expanded ? 2 : 0))).map((line) => props.expanded ? `${row2.node.sessionId === props.selectedId ? ">" : " "} ${line}` : line);
1861
2142
  },
1862
2143
  children: (line) => (() => {
1863
- var _el$14 = _$createElement2("text");
1864
- _$insert2(_el$14, line);
1865
- _$effect2((_$p) => _$setProp2(_el$14, "fg", row2.pressure === "crossed" ? props.context.theme.text.feedback.error.base : row2.pressure === "near" ? props.context.theme.text.feedback.warning.base : props.context.theme.text.base, _$p));
1866
- return _el$14;
2144
+ var _el$15 = _$createElement2("text");
2145
+ _$insert2(_el$15, line);
2146
+ _$effect2((_$p) => _$setProp2(_el$15, "fg", row2.pressure === "crossed" ? props.context.theme.text.feedback.error.base : row2.pressure === "near" ? props.context.theme.text.feedback.warning.base : props.context.theme.text.base, _$p));
2147
+ return _el$15;
1867
2148
  })()
1868
2149
  }));
1869
- _$effect2((_$p) => _$setProp2(_el$13, "id", `context-guard.row.${row2.node.sessionId}`, _$p));
1870
- return _el$13;
2150
+ _$effect2((_$p) => _$setProp2(_el$14, "id", `context-guard.row.${row2.node.sessionId}`, _$p));
2151
+ return _el$14;
1871
2152
  })()
1872
2153
  }), _el$0);
1873
2154
  _$setProp2(_el$0, "onMouseDown", () => props.context.ui.panel.open("context-guard.monitor"));
@@ -1909,6 +2190,8 @@ import { createElement as _$createElement4 } from "@opentui/solid";
1909
2190
  import { createSignal as createSignal4, createMemo as createMemo3, createEffect as createEffect4, onCleanup as onCleanup3, Show as Show3, For as For4 } from "solid-js";
1910
2191
 
1911
2192
  // src/tui/charts.tsx
2193
+ init_chartformat();
2194
+ init_format();
1912
2195
  import { effect as _$effect3 } from "@opentui/solid";
1913
2196
  import { insertNode as _$insertNode3 } from "@opentui/solid";
1914
2197
  import { insert as _$insert3 } from "@opentui/solid";
@@ -1916,113 +2199,6 @@ import { createComponent as _$createComponent3 } from "@opentui/solid";
1916
2199
  import { setProp as _$setProp3 } from "@opentui/solid";
1917
2200
  import { createElement as _$createElement3 } from "@opentui/solid";
1918
2201
  import { createEffect as createEffect3, createSignal as createSignal3, onCleanup as onCleanup2, For as For3 } from "solid-js";
1919
-
1920
- // src/tui/chartformat.ts
1921
- function columns2(points, from, to, width, gaps = []) {
1922
- const size = Math.max(1, Math.min(120, Math.floor(width))), values = Array.from({ length: size });
1923
- const column = (time) => Math.max(0, Math.min(size - 1, Math.floor((time - from) / Math.max(1, to - from) * (size - 1))));
1924
- let min = Infinity, max = -Infinity;
1925
- for (const point of points) {
1926
- if (point.time < from || point.time > to || point.value === undefined || !Number.isFinite(point.value) || point.value < 0)
1927
- continue;
1928
- min = Math.min(min, point.value);
1929
- max = Math.max(max, point.value);
1930
- const i = column(point.time);
1931
- values[i] = Math.max(values[i] ?? -Infinity, point.value);
1932
- }
1933
- for (const time of gaps)
1934
- if (time >= from && time <= to)
1935
- values[column(time)] = undefined;
1936
- return { values, column, min, max };
1937
- }
1938
- function sparkline(points, from, to, width, gaps = []) {
1939
- const { values, column, min, max } = columns2(points, from, to, width, gaps);
1940
- const bars = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
1941
- const chars = values.map((v) => v === undefined ? " " : bars[Math.min(7, Math.floor(v / Math.max(1, max) * 7))] ?? "\u2581");
1942
- for (const time of gaps)
1943
- if (time >= from && time <= to)
1944
- chars[column(time)] = "\u2506";
1945
- return { line: chars.join(""), ...min === Infinity ? {} : { min, max } };
1946
- }
1947
- var chartMetrics = [
1948
- { label: "Live TPS ~", unit: "t/s", key: "liveTps", source: "points" },
1949
- { label: "Avg model", unit: "t/s", key: "averageModelTps", source: "points" },
1950
- {
1951
- label: "Avg session",
1952
- unit: "t/s",
1953
- key: "averageSessionTps",
1954
- source: "points"
1955
- },
1956
- { label: "TTFT ~", unit: "s", key: "ttftMs", source: "requests" },
1957
- { label: "Request throughput", unit: "t/s", key: "tps", source: "requests" },
1958
- {
1959
- label: "Cumulative output",
1960
- unit: "tokens",
1961
- key: "sessionOutputTokens",
1962
- source: "points"
1963
- }
1964
- ];
1965
- function metricPlot(points, from, to, width, gaps = []) {
1966
- const label = (value) => value >= 1e6 ? `${(value / 1e6).toFixed(1)}M` : value >= 1000 ? `${(value / 1000).toFixed(1)}k` : value.toFixed(1);
1967
- const peak = Math.max(0, ...points.filter((p) => p.time >= from && p.time <= to && Number.isFinite(p.value)).map((p) => p.value ?? 0));
1968
- const scale = Math.max(1, peak);
1969
- const gutter = width >= 20 ? Math.max(label(scale).length, 3) : 0;
1970
- const { values, column } = columns2(points, from, to, Math.max(1, width - gutter - 2), gaps);
1971
- const gapColumns = new Set(gaps.filter((time) => time >= from && time <= to).map(column));
1972
- const height = 6;
1973
- const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
1974
- const lines = Array.from({ length: height }, (_, row2) => {
1975
- const axis = gutter ? (row2 === 0 ? label(scale) : row2 === height - 1 ? "0.0" : "").padStart(gutter) : "";
1976
- return `${axis}\u2502${values.map((value, i) => {
1977
- if (gapColumns.has(i))
1978
- return "\u2506";
1979
- if (value === undefined)
1980
- return " ";
1981
- const fill = Math.max(0, Math.min(8, Math.ceil((value / scale * height - (height - row2 - 1)) * 8)));
1982
- return value === 0 && row2 === height - 1 ? "\u2581" : blocks[fill];
1983
- }).join("")}\u2502`;
1984
- });
1985
- lines.push(`${" ".repeat(gutter)}\u2514${"\u2500".repeat(values.length)}\u2518`);
1986
- const start = new Date(from).toISOString().slice(11, 16), end = new Date(to).toISOString().slice(11, 16);
1987
- const axisWidth = values.length + 2;
1988
- if (axisWidth >= 11)
1989
- lines.push(`${" ".repeat(gutter)}${start}${" ".repeat(axisWidth - 10)}${end}`);
1990
- return lines;
1991
- }
1992
- function historyChartLines(result, width, from, to, metricIndex = 0) {
1993
- if (!result.enabled)
1994
- return wrap("History recording is disabled on this server.", width);
1995
- const series = result.data.series[0];
1996
- if (!series)
1997
- return wrap("No retained measurements in this window.", width);
1998
- const gaps = result.data.gaps.map((g) => g.time);
1999
- const observations = (key) => series.points.map((p) => ({
2000
- time: p.time,
2001
- ...p[key] === undefined ? {} : { value: p[key] }
2002
- }));
2003
- const requests = (key) => series.requests.map((p) => ({
2004
- time: p.time,
2005
- ...p[key] === undefined ? {} : { value: key === "ttftMs" ? p[key] / 1000 : p[key] }
2006
- }));
2007
- const metric2 = chartMetrics[metricIndex] ?? chartMetrics[0];
2008
- const points = metric2.source === "points" ? observations(metric2.key) : requests(metric2.key);
2009
- const plotted = sparkline(points, from, to, width - 2, gaps);
2010
- return [
2011
- ...wrap(`\u2039 ${metric2.label} \u203A \xB7 ${metricIndex + 1}/${chartMetrics.length}`, width),
2012
- ...wrap(plotted.min === undefined ? "No measurements for this metric." : `${plotted.min.toFixed(1)}\u2013${plotted.max?.toFixed(1)} ${metric2.unit} \xB7 observed range`, width),
2013
- "",
2014
- ...metricPlot(points, from, to, width, gaps),
2015
- "",
2016
- ...wrap("UTC time \u2192 \xB7 peaks per column", width),
2017
- ...wrap("blank = no sample \xB7 \u2506 = gap", width),
2018
- ...wrap(`${new Date(from).toISOString()} \u2192 ${new Date(to).toISOString()}`, width),
2019
- ...series.points.some((p) => p.partial) ? wrap("Partial timing coverage; averages include only matched model durations.", width) : [],
2020
- ...result.truncated ? wrap("Downsampled/bounded history; some older requests or gap markers may be omitted.", width) : [],
2021
- ...result.metadata.malformedRecords ? wrap(`${result.metadata.malformedRecords} malformed/partial records skipped.`, width) : []
2022
- ];
2023
- }
2024
-
2025
- // src/tui/charts.tsx
2026
2202
  function HistoryCharts(props) {
2027
2203
  const [result, setResult] = createSignal3();
2028
2204
  const [status, setStatus] = createSignal3("Loading retained history...");
@@ -2117,10 +2293,13 @@ function HistoryCharts(props) {
2117
2293
  }
2118
2294
 
2119
2295
  // src/tui/panel.tsx
2296
+ init_chartformat();
2297
+ init_format();
2120
2298
  function GuardPanel(props) {
2121
2299
  const [showCharts, setShowCharts] = createSignal4(false);
2122
2300
  const [metricIndex, setMetricIndex] = createSignal4(0);
2123
- const width = () => Math.max(4, props.input.width - 4);
2301
+ const [viewportWidth, setViewportWidth] = createSignal4();
2302
+ const width = () => Math.max(4, Math.min(props.input.width - 4, viewportWidth() ?? Infinity));
2124
2303
  const windows = [300000, 3600000, 86400000, 7 * 86400000];
2125
2304
  const [windowIndex, setWindowIndex] = createSignal4(1);
2126
2305
  const [chartRefresh, setChartRefresh] = createSignal4(0);
@@ -2276,6 +2455,11 @@ function GuardPanel(props) {
2276
2455
  _$use((value) => {
2277
2456
  scroll = value;
2278
2457
  }, _el$2);
2458
+ _$setProp4(_el$2, "viewportOptions", {
2459
+ onSizeChange: function() {
2460
+ setViewportWidth(this.width);
2461
+ }
2462
+ });
2279
2463
  _$setProp4(_el$2, "flexGrow", 1);
2280
2464
  _$setProp4(_el$2, "minHeight", 0);
2281
2465
  _$insertNode4(_el$3, _el$4);