@cesarus777/opencode-cgp 0.2.5 → 0.3.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.
- package/CHANGELOG.md +19 -0
- package/README.md +56 -5
- package/dist/cli.js +3498 -1029
- package/dist/core.js +311 -102
- package/dist/index.js +873 -836
- package/dist/rpc.js +275 -246
- package/dist/tui.js +679 -627
- package/dist/types/clihelp.d.ts +2 -0
- package/dist/types/core.d.ts +1 -0
- package/dist/types/dashboard/command.d.ts +5 -0
- package/dist/types/dashboard/connection.d.ts +36 -0
- package/dist/types/dashboard/controller.d.ts +57 -0
- package/dist/types/dashboard/inventory.d.ts +17 -0
- package/dist/types/dashboard/port.d.ts +41 -0
- package/dist/types/dashboard/terminal.d.ts +36 -0
- package/dist/types/dashboard/terminalview.d.ts +15 -0
- package/dist/types/dashboard/view.d.ts +83 -0
- package/dist/types/modelanalytics.d.ts +60 -0
- package/dist/types/modelcli.d.ts +1 -0
- package/dist/types/tui/contextchart.d.ts +2 -1
- package/package.json +6 -7
package/dist/tui.js
CHANGED
|
@@ -1,174 +1,33 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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) {
|
|
@@ -427,131 +441,6 @@ function executionStatus(known, nativeRunning, evidence) {
|
|
|
427
441
|
return known ? "idle" : "unknown";
|
|
428
442
|
}
|
|
429
443
|
|
|
430
|
-
// src/tui/contextchart.ts
|
|
431
|
-
function updateContextTrace(previous, row2) {
|
|
432
|
-
const snapshot = row2?.snapshot;
|
|
433
|
-
const sessionId = row2?.node.sessionId ?? "";
|
|
434
|
-
const sameSession = previous?.sessionId === sessionId;
|
|
435
|
-
const model = snapshot?.model ?? row2?.node.model ?? (sameSession ? previous?.model : undefined);
|
|
436
|
-
const capacity = snapshot?.capacityTokens ?? (sameSession && model === previous?.model ? previous?.capacity : undefined);
|
|
437
|
-
const compatible = sameSession && previous?.model === model && previous.capacity === capacity;
|
|
438
|
-
const samples = compatible ? previous.samples : [];
|
|
439
|
-
const completed = snapshot?.compaction.lastCompleted ?? (compatible ? previous.lastCompleted : undefined);
|
|
440
|
-
let pending = compatible ? previous.pendingCompleted : undefined;
|
|
441
|
-
if (completed !== undefined && completed !== previous?.lastCompleted)
|
|
442
|
-
pending = completed;
|
|
443
|
-
const measurement2 = snapshot?.measurement;
|
|
444
|
-
const valid2 = (row2?.freshness === "fresh" || row2?.freshness === "stale") && measurement2 && Number.isFinite(measurement2.tokens) && measurement2.tokens >= 0;
|
|
445
|
-
const at = measurement2?.at ?? snapshot?.observedAt;
|
|
446
|
-
let next = samples;
|
|
447
|
-
if (at !== undefined && Number.isFinite(at)) {
|
|
448
|
-
const last = samples.at(-1);
|
|
449
|
-
if (!last || at >= last.at) {
|
|
450
|
-
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");
|
|
451
|
-
const waiting = pending !== undefined && !reset;
|
|
452
|
-
const tokens = valid2 && !waiting ? Math.max(reset ? 0 : last?.tokens ?? 0, measurement2.tokens) : last?.tokens;
|
|
453
|
-
const held = tokens !== undefined && (!valid2 || waiting || tokens !== measurement2.tokens);
|
|
454
|
-
const sample = {
|
|
455
|
-
at,
|
|
456
|
-
...tokens === undefined ? {} : { tokens },
|
|
457
|
-
...held ? { held: true } : {},
|
|
458
|
-
...reset || at === last?.at && last.compacted ? { compacted: true } : {}
|
|
459
|
-
};
|
|
460
|
-
if (reset)
|
|
461
|
-
pending = undefined;
|
|
462
|
-
if (!last || at > last.at) {
|
|
463
|
-
const bridge = last?.tokens !== undefined && at - last.at > 2500 ? [{ at: at - 1, tokens: last.tokens, held: true }] : [];
|
|
464
|
-
next = [...samples, ...bridge, sample].slice(-96);
|
|
465
|
-
} else if (sample.tokens !== last.tokens || sample.held !== last.held || sample.compacted !== last.compacted) {
|
|
466
|
-
next = [...samples.slice(0, -1), sample];
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
return {
|
|
471
|
-
sessionId,
|
|
472
|
-
model,
|
|
473
|
-
capacity,
|
|
474
|
-
samples: next,
|
|
475
|
-
...completed === undefined ? {} : { lastCompleted: completed },
|
|
476
|
-
...pending === undefined ? {} : { pendingCompleted: pending }
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
class ContextTraceStore {
|
|
481
|
-
traces = new Map;
|
|
482
|
-
update(row2) {
|
|
483
|
-
if (!row2)
|
|
484
|
-
return updateContextTrace(undefined, undefined);
|
|
485
|
-
const id = row2.node.sessionId;
|
|
486
|
-
const trace = updateContextTrace(this.traces.get(id), row2);
|
|
487
|
-
this.traces.delete(id);
|
|
488
|
-
this.traces.set(id, trace);
|
|
489
|
-
if (this.traces.size > 128) {
|
|
490
|
-
const oldest = this.traces.keys().next().value;
|
|
491
|
-
if (oldest !== undefined)
|
|
492
|
-
this.traces.delete(oldest);
|
|
493
|
-
}
|
|
494
|
-
return trace;
|
|
495
|
-
}
|
|
496
|
-
clear() {
|
|
497
|
-
this.traces.clear();
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
function contextChart(samples, capacity, trigger, width, now) {
|
|
501
|
-
if (capacity === undefined || !Number.isFinite(capacity) || capacity <= 0 || width < 4)
|
|
502
|
-
return [];
|
|
503
|
-
const height = 8;
|
|
504
|
-
const gutter = width >= 16 ? 4 : 0;
|
|
505
|
-
const size = Math.max(1, Math.min(96, width - gutter - 2));
|
|
506
|
-
const last = samples.at(-1);
|
|
507
|
-
const tail = now !== undefined && last?.tokens !== undefined && now - last.at > 2500 ? [{ at: now, tokens: last.tokens, held: true }] : [];
|
|
508
|
-
const values = [...samples, ...tail].slice(-size);
|
|
509
|
-
const offset = size - values.length;
|
|
510
|
-
const thresholdRow = trigger !== undefined && Number.isFinite(trigger) && trigger >= 0 && trigger <= capacity ? Math.round((1 - trigger / capacity) * (height - 1)) : -1;
|
|
511
|
-
const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
512
|
-
const lines = Array.from({ length: height }, (_, row2) => {
|
|
513
|
-
const threshold = row2 === thresholdRow;
|
|
514
|
-
const axis = gutter ? (row2 === 0 ? "100%" : row2 === height - 1 ? "0%" : "").padStart(gutter) : "";
|
|
515
|
-
const plot = Array.from({ length: size }, (_2, col) => {
|
|
516
|
-
const sample = values[col - offset];
|
|
517
|
-
const value = sample?.tokens;
|
|
518
|
-
const fill = value === undefined ? 0 : Math.max(0, Math.min(8, Math.ceil((Math.min(value / capacity, 1) * height - (height - row2 - 1)) * 8)));
|
|
519
|
-
if (sample?.held && value !== undefined) {
|
|
520
|
-
const top = Math.min(height - 1, Math.max(0, height - Math.ceil(Math.min(value / capacity, 1) * height)));
|
|
521
|
-
if (row2 === top)
|
|
522
|
-
return "\u2508";
|
|
523
|
-
if (fill > 0)
|
|
524
|
-
return "\u2591";
|
|
525
|
-
}
|
|
526
|
-
if (threshold)
|
|
527
|
-
return fill > 0 ? "\u2542" : "\u2504";
|
|
528
|
-
if (value === 0 && row2 === height - 1)
|
|
529
|
-
return "\u2581";
|
|
530
|
-
return blocks[fill] ?? " ";
|
|
531
|
-
});
|
|
532
|
-
const line = `${axis}\u2502${plot.join("")}\u2502`;
|
|
533
|
-
if (!values.some((sample) => sample.held))
|
|
534
|
-
return { line, threshold };
|
|
535
|
-
const segments = [{ text: `${axis}\u2502`, held: false }];
|
|
536
|
-
for (let col = 0;col < plot.length; col++) {
|
|
537
|
-
const text2 = plot[col] ?? " ";
|
|
538
|
-
const held = values[col - offset]?.held === true;
|
|
539
|
-
const previous = segments.at(-1);
|
|
540
|
-
if (previous?.held === held)
|
|
541
|
-
segments[segments.length - 1] = { text: previous.text + text2, held };
|
|
542
|
-
else
|
|
543
|
-
segments.push({ text: text2, held });
|
|
544
|
-
}
|
|
545
|
-
segments.push({ text: "\u2502", held: false });
|
|
546
|
-
return { line, threshold, segments };
|
|
547
|
-
});
|
|
548
|
-
lines.push({
|
|
549
|
-
line: `${" ".repeat(gutter)}\u2514${"\u2500".repeat(size)}\u2518`,
|
|
550
|
-
threshold: false
|
|
551
|
-
});
|
|
552
|
-
return lines;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
444
|
// src/tui/transport.ts
|
|
556
445
|
class SnapshotClient {
|
|
557
446
|
transport;
|
|
@@ -800,30 +689,407 @@ class SnapshotClient {
|
|
|
800
689
|
});
|
|
801
690
|
}
|
|
802
691
|
}
|
|
803
|
-
stop() {
|
|
804
|
-
this.generation++;
|
|
805
|
-
this.controller?.abort();
|
|
806
|
-
this.controller = undefined;
|
|
807
|
-
clearInterval(this.refreshTimer);
|
|
808
|
-
clearTimeout(this.retryTimer);
|
|
809
|
-
this.reading = undefined;
|
|
810
|
-
this.buffered = [];
|
|
811
|
-
this.overflow = false;
|
|
812
|
-
this.refreshAgain = false;
|
|
692
|
+
stop() {
|
|
693
|
+
this.generation++;
|
|
694
|
+
this.controller?.abort();
|
|
695
|
+
this.controller = undefined;
|
|
696
|
+
clearInterval(this.refreshTimer);
|
|
697
|
+
clearTimeout(this.retryTimer);
|
|
698
|
+
this.reading = undefined;
|
|
699
|
+
this.buffered = [];
|
|
700
|
+
this.overflow = false;
|
|
701
|
+
this.refreshAgain = false;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
function abortable(promise, signal) {
|
|
705
|
+
return new Promise((resolve, reject) => {
|
|
706
|
+
const abort = () => reject(new Error("Snapshot request cancelled"));
|
|
707
|
+
if (signal.aborted) {
|
|
708
|
+
abort();
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
712
|
+
promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
var init_transport = __esm(() => {
|
|
716
|
+
init_rpc();
|
|
717
|
+
});
|
|
718
|
+
|
|
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))
|
|
740
|
+
break;
|
|
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 = "";
|
|
800
|
+
}
|
|
801
|
+
for (const { segment } of segmenter.segment((part ? " " : "") + word)) {
|
|
802
|
+
if (part && cells(part + segment) > width) {
|
|
803
|
+
result.push(part);
|
|
804
|
+
part = "";
|
|
805
|
+
}
|
|
806
|
+
part += cells(segment) <= width ? segment : "?";
|
|
807
|
+
}
|
|
808
|
+
}
|
|
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();
|
|
813
1026
|
}
|
|
814
1027
|
}
|
|
815
|
-
function
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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 });
|
|
821
1072
|
}
|
|
822
|
-
|
|
823
|
-
|
|
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
|
+
]
|
|
824
1086
|
});
|
|
1087
|
+
return lines;
|
|
825
1088
|
}
|
|
826
1089
|
|
|
1090
|
+
// src/tui/state.ts
|
|
1091
|
+
init_transport();
|
|
1092
|
+
|
|
827
1093
|
// src/tui/inventory.ts
|
|
828
1094
|
async function discoverFamily(client, currentId, signal, cached, maxNodes = 1024, previous) {
|
|
829
1095
|
const sessions = new Map(previous?.sessions);
|
|
@@ -1231,6 +1497,7 @@ function sameLocation(a, b) {
|
|
|
1231
1497
|
}
|
|
1232
1498
|
|
|
1233
1499
|
// src/tui/sidebar.tsx
|
|
1500
|
+
init_format();
|
|
1234
1501
|
import { effect as _$effect2 } from "@opentui/solid";
|
|
1235
1502
|
import { createComponent as _$createComponent2 } from "@opentui/solid";
|
|
1236
1503
|
import { insert as _$insert2 } from "@opentui/solid";
|
|
@@ -1241,121 +1508,8 @@ import { setProp as _$setProp2 } from "@opentui/solid";
|
|
|
1241
1508
|
import { createElement as _$createElement2 } from "@opentui/solid";
|
|
1242
1509
|
import { For as For2, createEffect as createEffect2, createMemo as createMemo2, onCleanup, Show as Show2 } from "solid-js";
|
|
1243
1510
|
|
|
1244
|
-
// src/tui/format.ts
|
|
1245
|
-
function quantity(n) {
|
|
1246
|
-
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);
|
|
1247
|
-
}
|
|
1248
|
-
var segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
1249
|
-
function cells(value) {
|
|
1250
|
-
return [...segmenter.segment(value)].reduce((n, { segment }) => n + graphemeWidth(segment), 0);
|
|
1251
|
-
}
|
|
1252
|
-
function graphemeWidth(s) {
|
|
1253
|
-
const cp = s.codePointAt(0) ?? 0;
|
|
1254
|
-
if (/^[\p{Mark}\u200d\ufe0f]+$/u.test(s))
|
|
1255
|
-
return 0;
|
|
1256
|
-
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;
|
|
1257
|
-
}
|
|
1258
|
-
function clip(value, width) {
|
|
1259
|
-
const clean = sanitize(value);
|
|
1260
|
-
if (cells(clean) <= width)
|
|
1261
|
-
return clean;
|
|
1262
|
-
let result = "", used = 0;
|
|
1263
|
-
for (const { segment } of segmenter.segment(clean)) {
|
|
1264
|
-
const size = graphemeWidth(segment);
|
|
1265
|
-
if (used + size > Math.max(0, width - 3))
|
|
1266
|
-
break;
|
|
1267
|
-
result += segment;
|
|
1268
|
-
used += size;
|
|
1269
|
-
}
|
|
1270
|
-
return width < 3 ? ".".repeat(Math.max(0, width)) : result + "...";
|
|
1271
|
-
}
|
|
1272
|
-
var statuses = {
|
|
1273
|
-
running: "RUN",
|
|
1274
|
-
idle: "IDLE",
|
|
1275
|
-
completed: "DONE",
|
|
1276
|
-
failed: "FAIL",
|
|
1277
|
-
unknown: "?"
|
|
1278
|
-
};
|
|
1279
|
-
function rowLines(row2, width) {
|
|
1280
|
-
const s = row2.snapshot, m = s?.measurement;
|
|
1281
|
-
const prefix = row2.current ? "* " : row2.depth > 5 ? ".../" : " ".repeat(Math.min(row2.depth, 5)) + "|-";
|
|
1282
|
-
const percent = row2.utilizationPercent === undefined ? "?" : `${row2.utilizationPercent.toFixed(1)}%`;
|
|
1283
|
-
const warning = row2.freshness === "stale" ? " STALE" : row2.pressure === "crossed" ? " !! crossed" : row2.pressure === "near" ? " ! near" : "";
|
|
1284
|
-
const suffix = ` ${statuses[row2.node.status]} ${m?.confidence === "approximate" ? "~" : ""}${percent}${warning}`;
|
|
1285
|
-
const label = clip(`${prefix}${row2.node.agent ?? row2.node.sessionId}`, Math.max(1, width - cells(suffix)));
|
|
1286
|
-
return [
|
|
1287
|
-
clip(`${label}${suffix}`, width),
|
|
1288
|
-
...performanceLines(row2, width),
|
|
1289
|
-
...wrap(`${quantity(m?.tokens)}/${quantity(s?.capacityTokens)} input; to ${quantity(row2.triggerDeltaTokens)}`, width),
|
|
1290
|
-
...s && s.compaction.phase !== "idle" ? wrap(`compact: ${s.compaction.phase}`, width) : [],
|
|
1291
|
-
...row2.node.interrupted ? [clip("interrupted", width)] : [],
|
|
1292
|
-
...row2.hidden ? wrap(`+ ${row2.hidden} completed descendants`, width) : []
|
|
1293
|
-
];
|
|
1294
|
-
}
|
|
1295
|
-
function detailLines(row2, width, expanded = false, ageMs = 0) {
|
|
1296
|
-
const s = row2?.snapshot, m = s?.measurement;
|
|
1297
|
-
if (!row2)
|
|
1298
|
-
return wrap("Loading session family...", width);
|
|
1299
|
-
if (!s)
|
|
1300
|
-
return [
|
|
1301
|
-
...wrap(`${row2.node.agent ?? row2.node.sessionId} / ${row2.node.model ?? "?"}`, width),
|
|
1302
|
-
...wrap("Guard: not monitored / unavailable", width)
|
|
1303
|
-
];
|
|
1304
|
-
const threshold = s.policy.configured;
|
|
1305
|
-
const headroom = row2.triggerDeltaTokens;
|
|
1306
|
-
const lines = [
|
|
1307
|
-
`${s.agent ?? "?"} / ${s.model ?? "?"}`,
|
|
1308
|
-
...performanceLines(row2, width),
|
|
1309
|
-
`${m?.confidence === "approximate" ? "~" : ""}${quantity(m?.tokens)} / ${quantity(s.capacityTokens)} input`,
|
|
1310
|
-
`${row2.utilizationPercent?.toFixed(1) ?? "?"}% ${row2.pressure === "crossed" ? "!! crossed" : row2.pressure === "near" ? "! near" : row2.freshness.toUpperCase()}`,
|
|
1311
|
-
`Trigger ${threshold.type === "ratio" ? `${threshold.value * 100}%` : `${threshold.value} ${threshold.type}`} (${s.policy.comparison})`,
|
|
1312
|
-
`Resolved trigger ${s.policy.triggerTokens ?? "?"}`,
|
|
1313
|
-
`Until trigger ${quantity(headroom)}`,
|
|
1314
|
-
`Compact: ${s.compaction.phase}`,
|
|
1315
|
-
`${s.compaction.armed ? "armed" : "disarmed"}${s.compaction.blockedReason ? ` / ${s.compaction.blockedReason}` : ""}`,
|
|
1316
|
-
m ? `${m.confidence} ${m.scope}` : "measurement unknown",
|
|
1317
|
-
m ? `${Math.floor(Math.max(0, ageMs) / 1000)}s ago; ${row2.freshness}` : ""
|
|
1318
|
-
];
|
|
1319
|
-
if (expanded)
|
|
1320
|
-
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)}`);
|
|
1321
|
-
return lines.filter(Boolean).flatMap((line) => wrap(line, width));
|
|
1322
|
-
}
|
|
1323
|
-
function sanitize(value) {
|
|
1324
|
-
return value.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "").replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\x00-\x1f\x7f-\x9f]/g, "");
|
|
1325
|
-
}
|
|
1326
|
-
function wrap(value, width) {
|
|
1327
|
-
const result = [];
|
|
1328
|
-
let part = "";
|
|
1329
|
-
for (const word of sanitize(value).split(" ")) {
|
|
1330
|
-
if (part && cells(part + " " + word) > width) {
|
|
1331
|
-
result.push(part);
|
|
1332
|
-
part = "";
|
|
1333
|
-
}
|
|
1334
|
-
for (const { segment } of segmenter.segment((part ? " " : "") + word)) {
|
|
1335
|
-
if (part && cells(part + segment) > width) {
|
|
1336
|
-
result.push(part);
|
|
1337
|
-
part = "";
|
|
1338
|
-
}
|
|
1339
|
-
part += cells(segment) <= width ? segment : "?";
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
if (part)
|
|
1343
|
-
result.push(part);
|
|
1344
|
-
return result;
|
|
1345
|
-
}
|
|
1346
|
-
function performanceLines(row2, width) {
|
|
1347
|
-
const p = row2.snapshot?.performance;
|
|
1348
|
-
if (!p)
|
|
1349
|
-
return [];
|
|
1350
|
-
const value = (n) => n === undefined ? "?" : n.toFixed(1);
|
|
1351
|
-
return [
|
|
1352
|
-
...wrap(`TPS ${p.status === "waiting" ? "waiting" : `${p.status === "streaming" ? "~" : ""}${value(p.liveTps)}`} \xB7 ${p.status}${p.partial ? " \xB7 partial" : ""}`, width),
|
|
1353
|
-
...wrap(`Avg model ${value(p.averageModelTps)} \xB7 Avg session ${value(p.averageSessionTps)}`, width),
|
|
1354
|
-
...wrap(`TTFT ${p.ttftMs === undefined ? "?" : `~${(p.ttftMs / 1000).toFixed(2)}s`}${p.latestRequestTps === undefined ? "" : ` \xB7 Last ${value(p.latestRequestTps)} t/s`}`, width)
|
|
1355
|
-
];
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
1511
|
// src/tui/compact.tsx
|
|
1512
|
+
init_format();
|
|
1359
1513
|
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
1360
1514
|
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
1361
1515
|
import { setProp as _$setProp } from "@opentui/solid";
|
|
@@ -1367,6 +1521,7 @@ import { createComponent as _$createComponent } from "@opentui/solid";
|
|
|
1367
1521
|
import { For, Index, Show, createEffect, createMemo, createSignal as createSignal2 } from "solid-js";
|
|
1368
1522
|
|
|
1369
1523
|
// src/tui/compactformat.ts
|
|
1524
|
+
init_format();
|
|
1370
1525
|
function tokens(value) {
|
|
1371
1526
|
if (value === undefined || !Number.isFinite(value))
|
|
1372
1527
|
return "?";
|
|
@@ -1418,7 +1573,7 @@ function mainSections(row2, width, age, samples, chartCapacity) {
|
|
|
1418
1573
|
...samples?.some((sample) => sample.held) || m && age > 2500 ? [
|
|
1419
1574
|
{
|
|
1420
1575
|
tone: "measurement",
|
|
1421
|
-
lines: wrap("
|
|
1576
|
+
lines: wrap("muted held \xB7 last known context", width)
|
|
1422
1577
|
}
|
|
1423
1578
|
] : [],
|
|
1424
1579
|
{
|
|
@@ -1668,7 +1823,7 @@ function CompactSidebar(props) {
|
|
|
1668
1823
|
var _el$14 = _$createElement("span");
|
|
1669
1824
|
_$insert(_el$14, () => part.text);
|
|
1670
1825
|
_$effect((_$p) => _$setProp(_el$14, "style", {
|
|
1671
|
-
fg: part.held ? colors().muted :
|
|
1826
|
+
fg: part.tone === "axis" || part.tone === "held" ? colors().muted : part.tone === "threshold" ? colors().feedback.warning.base : current()?.freshness === "fresh" ? colors().base : colors().muted
|
|
1672
1827
|
}, _$p));
|
|
1673
1828
|
return _el$14;
|
|
1674
1829
|
})()
|
|
@@ -1910,7 +2065,7 @@ function GuardView(props) {
|
|
|
1910
2065
|
var _el$10 = _$createElement2("span");
|
|
1911
2066
|
_$insert2(_el$10, () => part.text);
|
|
1912
2067
|
_$effect2((_$p) => _$setProp2(_el$10, "style", {
|
|
1913
|
-
fg: part.held ? props.context.theme.text.muted :
|
|
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
|
|
1914
2069
|
}, _$p));
|
|
1915
2070
|
return _el$10;
|
|
1916
2071
|
})()
|
|
@@ -1923,7 +2078,7 @@ function GuardView(props) {
|
|
|
1923
2078
|
}), _el$6);
|
|
1924
2079
|
_$insert2(_el$, _$createComponent2(For2, {
|
|
1925
2080
|
get each() {
|
|
1926
|
-
return wrap("Recent context samples \u2192 now \xB7 \u2504 trigger \xB7
|
|
2081
|
+
return wrap("Recent context samples \u2192 now \xB7 \u2504 trigger \xB7 muted held", props.width);
|
|
1927
2082
|
},
|
|
1928
2083
|
children: (line) => (() => {
|
|
1929
2084
|
var _el$11 = _$createElement2("text");
|
|
@@ -2035,6 +2190,8 @@ import { createElement as _$createElement4 } from "@opentui/solid";
|
|
|
2035
2190
|
import { createSignal as createSignal4, createMemo as createMemo3, createEffect as createEffect4, onCleanup as onCleanup3, Show as Show3, For as For4 } from "solid-js";
|
|
2036
2191
|
|
|
2037
2192
|
// src/tui/charts.tsx
|
|
2193
|
+
init_chartformat();
|
|
2194
|
+
init_format();
|
|
2038
2195
|
import { effect as _$effect3 } from "@opentui/solid";
|
|
2039
2196
|
import { insertNode as _$insertNode3 } from "@opentui/solid";
|
|
2040
2197
|
import { insert as _$insert3 } from "@opentui/solid";
|
|
@@ -2042,113 +2199,6 @@ import { createComponent as _$createComponent3 } from "@opentui/solid";
|
|
|
2042
2199
|
import { setProp as _$setProp3 } from "@opentui/solid";
|
|
2043
2200
|
import { createElement as _$createElement3 } from "@opentui/solid";
|
|
2044
2201
|
import { createEffect as createEffect3, createSignal as createSignal3, onCleanup as onCleanup2, For as For3 } from "solid-js";
|
|
2045
|
-
|
|
2046
|
-
// src/tui/chartformat.ts
|
|
2047
|
-
function columns2(points, from, to, width, gaps = []) {
|
|
2048
|
-
const size = Math.max(1, Math.min(120, Math.floor(width))), values = Array.from({ length: size });
|
|
2049
|
-
const column = (time) => Math.max(0, Math.min(size - 1, Math.floor((time - from) / Math.max(1, to - from) * (size - 1))));
|
|
2050
|
-
let min = Infinity, max = -Infinity;
|
|
2051
|
-
for (const point of points) {
|
|
2052
|
-
if (point.time < from || point.time > to || point.value === undefined || !Number.isFinite(point.value) || point.value < 0)
|
|
2053
|
-
continue;
|
|
2054
|
-
min = Math.min(min, point.value);
|
|
2055
|
-
max = Math.max(max, point.value);
|
|
2056
|
-
const i = column(point.time);
|
|
2057
|
-
values[i] = Math.max(values[i] ?? -Infinity, point.value);
|
|
2058
|
-
}
|
|
2059
|
-
for (const time of gaps)
|
|
2060
|
-
if (time >= from && time <= to)
|
|
2061
|
-
values[column(time)] = undefined;
|
|
2062
|
-
return { values, column, min, max };
|
|
2063
|
-
}
|
|
2064
|
-
function sparkline(points, from, to, width, gaps = []) {
|
|
2065
|
-
const { values, column, min, max } = columns2(points, from, to, width, gaps);
|
|
2066
|
-
const bars = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
2067
|
-
const chars = values.map((v) => v === undefined ? " " : bars[Math.min(7, Math.floor(v / Math.max(1, max) * 7))] ?? "\u2581");
|
|
2068
|
-
for (const time of gaps)
|
|
2069
|
-
if (time >= from && time <= to)
|
|
2070
|
-
chars[column(time)] = "\u2506";
|
|
2071
|
-
return { line: chars.join(""), ...min === Infinity ? {} : { min, max } };
|
|
2072
|
-
}
|
|
2073
|
-
var chartMetrics = [
|
|
2074
|
-
{ label: "Live TPS ~", unit: "t/s", key: "liveTps", source: "points" },
|
|
2075
|
-
{ label: "Avg model", unit: "t/s", key: "averageModelTps", source: "points" },
|
|
2076
|
-
{
|
|
2077
|
-
label: "Avg session",
|
|
2078
|
-
unit: "t/s",
|
|
2079
|
-
key: "averageSessionTps",
|
|
2080
|
-
source: "points"
|
|
2081
|
-
},
|
|
2082
|
-
{ label: "TTFT ~", unit: "s", key: "ttftMs", source: "requests" },
|
|
2083
|
-
{ label: "Request throughput", unit: "t/s", key: "tps", source: "requests" },
|
|
2084
|
-
{
|
|
2085
|
-
label: "Cumulative output",
|
|
2086
|
-
unit: "tokens",
|
|
2087
|
-
key: "sessionOutputTokens",
|
|
2088
|
-
source: "points"
|
|
2089
|
-
}
|
|
2090
|
-
];
|
|
2091
|
-
function metricPlot(points, from, to, width, gaps = []) {
|
|
2092
|
-
const label = (value) => value >= 1e6 ? `${(value / 1e6).toFixed(1)}M` : value >= 1000 ? `${(value / 1000).toFixed(1)}k` : value.toFixed(1);
|
|
2093
|
-
const peak = Math.max(0, ...points.filter((p) => p.time >= from && p.time <= to && Number.isFinite(p.value)).map((p) => p.value ?? 0));
|
|
2094
|
-
const scale = Math.max(1, peak);
|
|
2095
|
-
const gutter = width >= 20 ? Math.max(label(scale).length, 3) : 0;
|
|
2096
|
-
const { values, column } = columns2(points, from, to, Math.max(1, width - gutter - 2), gaps);
|
|
2097
|
-
const gapColumns = new Set(gaps.filter((time) => time >= from && time <= to).map(column));
|
|
2098
|
-
const height = 6;
|
|
2099
|
-
const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
2100
|
-
const lines = Array.from({ length: height }, (_, row2) => {
|
|
2101
|
-
const axis = gutter ? (row2 === 0 ? label(scale) : row2 === height - 1 ? "0.0" : "").padStart(gutter) : "";
|
|
2102
|
-
return `${axis}\u2502${values.map((value, i) => {
|
|
2103
|
-
if (gapColumns.has(i))
|
|
2104
|
-
return "\u2506";
|
|
2105
|
-
if (value === undefined)
|
|
2106
|
-
return " ";
|
|
2107
|
-
const fill = Math.max(0, Math.min(8, Math.ceil((value / scale * height - (height - row2 - 1)) * 8)));
|
|
2108
|
-
return value === 0 && row2 === height - 1 ? "\u2581" : blocks[fill];
|
|
2109
|
-
}).join("")}\u2502`;
|
|
2110
|
-
});
|
|
2111
|
-
lines.push(`${" ".repeat(gutter)}\u2514${"\u2500".repeat(values.length)}\u2518`);
|
|
2112
|
-
const start = new Date(from).toISOString().slice(11, 16), end = new Date(to).toISOString().slice(11, 16);
|
|
2113
|
-
const axisWidth = values.length + 2;
|
|
2114
|
-
if (axisWidth >= 11)
|
|
2115
|
-
lines.push(`${" ".repeat(gutter)}${start}${" ".repeat(axisWidth - 10)}${end}`);
|
|
2116
|
-
return lines;
|
|
2117
|
-
}
|
|
2118
|
-
function historyChartLines(result, width, from, to, metricIndex = 0) {
|
|
2119
|
-
if (!result.enabled)
|
|
2120
|
-
return wrap("History recording is disabled on this server.", width);
|
|
2121
|
-
const series = result.data.series[0];
|
|
2122
|
-
if (!series)
|
|
2123
|
-
return wrap("No retained measurements in this window.", width);
|
|
2124
|
-
const gaps = result.data.gaps.map((g) => g.time);
|
|
2125
|
-
const observations = (key) => series.points.map((p) => ({
|
|
2126
|
-
time: p.time,
|
|
2127
|
-
...p[key] === undefined ? {} : { value: p[key] }
|
|
2128
|
-
}));
|
|
2129
|
-
const requests = (key) => series.requests.map((p) => ({
|
|
2130
|
-
time: p.time,
|
|
2131
|
-
...p[key] === undefined ? {} : { value: key === "ttftMs" ? p[key] / 1000 : p[key] }
|
|
2132
|
-
}));
|
|
2133
|
-
const metric2 = chartMetrics[metricIndex] ?? chartMetrics[0];
|
|
2134
|
-
const points = metric2.source === "points" ? observations(metric2.key) : requests(metric2.key);
|
|
2135
|
-
const plotted = sparkline(points, from, to, width - 2, gaps);
|
|
2136
|
-
return [
|
|
2137
|
-
...wrap(`\u2039 ${metric2.label} \u203A \xB7 ${metricIndex + 1}/${chartMetrics.length}`, width),
|
|
2138
|
-
...wrap(plotted.min === undefined ? "No measurements for this metric." : `${plotted.min.toFixed(1)}\u2013${plotted.max?.toFixed(1)} ${metric2.unit} \xB7 observed range`, width),
|
|
2139
|
-
"",
|
|
2140
|
-
...metricPlot(points, from, to, width, gaps),
|
|
2141
|
-
"",
|
|
2142
|
-
...wrap("UTC time \u2192 \xB7 peaks per column", width),
|
|
2143
|
-
...wrap("blank = no sample \xB7 \u2506 = gap", width),
|
|
2144
|
-
...wrap(`${new Date(from).toISOString()} \u2192 ${new Date(to).toISOString()}`, width),
|
|
2145
|
-
...series.points.some((p) => p.partial) ? wrap("Partial timing coverage; averages include only matched model durations.", width) : [],
|
|
2146
|
-
...result.truncated ? wrap("Downsampled/bounded history; some older requests or gap markers may be omitted.", width) : [],
|
|
2147
|
-
...result.metadata.malformedRecords ? wrap(`${result.metadata.malformedRecords} malformed/partial records skipped.`, width) : []
|
|
2148
|
-
];
|
|
2149
|
-
}
|
|
2150
|
-
|
|
2151
|
-
// src/tui/charts.tsx
|
|
2152
2202
|
function HistoryCharts(props) {
|
|
2153
2203
|
const [result, setResult] = createSignal3();
|
|
2154
2204
|
const [status, setStatus] = createSignal3("Loading retained history...");
|
|
@@ -2243,6 +2293,8 @@ function HistoryCharts(props) {
|
|
|
2243
2293
|
}
|
|
2244
2294
|
|
|
2245
2295
|
// src/tui/panel.tsx
|
|
2296
|
+
init_chartformat();
|
|
2297
|
+
init_format();
|
|
2246
2298
|
function GuardPanel(props) {
|
|
2247
2299
|
const [showCharts, setShowCharts] = createSignal4(false);
|
|
2248
2300
|
const [metricIndex, setMetricIndex] = createSignal4(0);
|