dsh-context 0.7.3 → 0.9.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/README.md +4 -0
- package/lib/client.js +284 -439
- package/lib/index.js +301 -194
- package/package.json +9 -2
package/lib/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/host/timeline.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
1
4
|
// src/host/pricing.ts
|
|
2
5
|
var CHARS_PER_TOKEN = 4;
|
|
3
6
|
var BLOCK_OVERHEAD = 4;
|
|
@@ -64,6 +67,8 @@ function isInjection(source) {
|
|
|
64
67
|
// src/host/fold.ts
|
|
65
68
|
var MAX_REQUEST_STEPS = 1500;
|
|
66
69
|
var MAX_KEPT_TURNS = 300;
|
|
70
|
+
var MAX_EVENTS = 400;
|
|
71
|
+
var MAX_NODES = 200;
|
|
67
72
|
function trimToLastTurns(requests, maxTurns) {
|
|
68
73
|
let runs = 0;
|
|
69
74
|
let start = requests.length;
|
|
@@ -79,27 +84,43 @@ function trimToLastTurns(requests, maxTurns) {
|
|
|
79
84
|
}
|
|
80
85
|
return requests.slice(start);
|
|
81
86
|
}
|
|
82
|
-
function
|
|
87
|
+
function countTurnRuns(requests) {
|
|
88
|
+
let runs = 0;
|
|
89
|
+
let prevTurn;
|
|
90
|
+
for (const r of requests) {
|
|
91
|
+
if (r.turn !== prevTurn) {
|
|
92
|
+
runs++;
|
|
93
|
+
prevTurn = r.turn;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return runs;
|
|
97
|
+
}
|
|
98
|
+
function trimState(st) {
|
|
99
|
+
if (countTurnRuns(st.requests) > MAX_KEPT_TURNS) {
|
|
100
|
+
st.requests = trimToLastTurns(st.requests, MAX_KEPT_TURNS);
|
|
101
|
+
}
|
|
102
|
+
if (st.requests.length > MAX_REQUEST_STEPS) {
|
|
103
|
+
st.requests = st.requests.slice(-MAX_REQUEST_STEPS);
|
|
104
|
+
}
|
|
105
|
+
if (st.events.length > MAX_EVENTS) st.events = st.events.slice(-MAX_EVENTS);
|
|
106
|
+
}
|
|
107
|
+
function createTimelineState() {
|
|
83
108
|
return {
|
|
84
|
-
n: 0,
|
|
85
|
-
// number of log events already folded
|
|
86
109
|
surface: [],
|
|
87
|
-
// { seq, cat, tokens, form?, text?, tool?, err?, skill?, calls? }
|
|
88
110
|
sums: { user: 0, inject: 0, assistant: 0, tool: 0 },
|
|
89
111
|
systemTokens: 0,
|
|
90
112
|
toolsTokens: 0,
|
|
91
113
|
toolList: [],
|
|
92
|
-
// { name, tokens }
|
|
93
114
|
model: void 0,
|
|
94
115
|
provider: void 0,
|
|
95
116
|
lastModel: void 0,
|
|
96
117
|
contextWindow: void 0,
|
|
118
|
+
pressureTokens: void 0,
|
|
119
|
+
sampledSurfaceTokens: void 0,
|
|
120
|
+
occupancyWindow: void 0,
|
|
97
121
|
requests: [],
|
|
98
|
-
// one entry per answered model call
|
|
99
122
|
events: [],
|
|
100
|
-
// notable context events (structured; the Client labels them)
|
|
101
123
|
callNames: {}
|
|
102
|
-
// callId -> tool name
|
|
103
124
|
};
|
|
104
125
|
}
|
|
105
126
|
function categoryOf(type, message) {
|
|
@@ -129,9 +150,12 @@ function applySurface(st, ev, type, data, message) {
|
|
|
129
150
|
if (names.length > 0) node.calls = names.slice(0, 3);
|
|
130
151
|
}
|
|
131
152
|
} else if (type === "tool/result") {
|
|
153
|
+
const srcId = source?.callId;
|
|
154
|
+
const srcName = typeof srcId === "string" ? st.callNames[srcId] : void 0;
|
|
132
155
|
const block = message?.content?.[0];
|
|
133
|
-
const
|
|
134
|
-
if (
|
|
156
|
+
const blockId = block?.toolCallId;
|
|
157
|
+
if (srcName) node.tool = srcName;
|
|
158
|
+
else if (typeof blockId === "string") node.tool = st.callNames[blockId];
|
|
135
159
|
if (data?.error) node.err = true;
|
|
136
160
|
} else if (source?.kind === "skill-invocation") {
|
|
137
161
|
node.skill = typeof source.name === "string" ? source.name : "?";
|
|
@@ -147,8 +171,22 @@ function applySurface(st, ev, type, data, message) {
|
|
|
147
171
|
const utext = firstText(message?.content);
|
|
148
172
|
if (utext !== "") node.text = utext;
|
|
149
173
|
}
|
|
174
|
+
const shadowedSeqs = st.pendingShadowedSeqs;
|
|
175
|
+
st.pendingShadowedSeqs = void 0;
|
|
150
176
|
const op = ev.surfaceOp;
|
|
151
177
|
if (op !== null && typeof op === "object" && op.op === "replace") {
|
|
178
|
+
if (Array.isArray(shadowedSeqs) && shadowedSeqs.length > 0) {
|
|
179
|
+
const shadowed = new Set(shadowedSeqs);
|
|
180
|
+
const kept = [];
|
|
181
|
+
for (const n of st.surface) {
|
|
182
|
+
if (shadowed.has(n.seq)) st.sums[n.cat] -= n.tokens;
|
|
183
|
+
else kept.push(n);
|
|
184
|
+
}
|
|
185
|
+
st.surface = kept;
|
|
186
|
+
st.sums[cat] += node.tokens;
|
|
187
|
+
st.surface.push(node);
|
|
188
|
+
return node;
|
|
189
|
+
}
|
|
152
190
|
let si = -1;
|
|
153
191
|
let ei = -1;
|
|
154
192
|
for (let i = 0; i < st.surface.length; i++) {
|
|
@@ -169,149 +207,189 @@ function applySurface(st, ev, type, data, message) {
|
|
|
169
207
|
st.sums[cat] += node.tokens;
|
|
170
208
|
return node;
|
|
171
209
|
}
|
|
172
|
-
function
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
seq: ev.seq,
|
|
210
|
-
time: ev.time,
|
|
211
|
-
kind: "inject",
|
|
212
|
-
form: source.form || "context",
|
|
213
|
-
tokens: node.tokens
|
|
214
|
-
};
|
|
215
|
-
if (source.kind === "skill-invocation") {
|
|
216
|
-
rec.sub = "skill";
|
|
217
|
-
rec.name = typeof source.name === "string" ? source.name : "?";
|
|
218
|
-
} else if (typeof source.plugin === "string" && source.plugin !== "") {
|
|
219
|
-
rec.name = source.plugin;
|
|
220
|
-
}
|
|
221
|
-
st.events.push(rec);
|
|
222
|
-
}
|
|
223
|
-
break;
|
|
210
|
+
function pressureOf(usage) {
|
|
211
|
+
if (usage === void 0 || typeof usage.inputTokens !== "number") return void 0;
|
|
212
|
+
return usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
|
|
213
|
+
}
|
|
214
|
+
function sampleUsage(st, usage) {
|
|
215
|
+
const pressureTokens = pressureOf(usage);
|
|
216
|
+
if (pressureTokens === void 0) return;
|
|
217
|
+
st.pressureTokens = pressureTokens;
|
|
218
|
+
st.sampledSurfaceTokens = st.sums.user + st.sums.inject + st.sums.assistant + st.sums.tool;
|
|
219
|
+
}
|
|
220
|
+
function applyTimeline(state, event) {
|
|
221
|
+
let st;
|
|
222
|
+
const ensure = () => st ??= {
|
|
223
|
+
...state,
|
|
224
|
+
surface: [...state.surface],
|
|
225
|
+
sums: { ...state.sums },
|
|
226
|
+
toolList: [...state.toolList],
|
|
227
|
+
requests: [...state.requests],
|
|
228
|
+
events: [...state.events],
|
|
229
|
+
callNames: { ...state.callNames }
|
|
230
|
+
};
|
|
231
|
+
const data = event.data;
|
|
232
|
+
switch (event.type) {
|
|
233
|
+
case "request/header": {
|
|
234
|
+
const header = data?.header ?? {};
|
|
235
|
+
const tools = Array.isArray(header.tools) ? header.tools : [];
|
|
236
|
+
const s = ensure();
|
|
237
|
+
s.toolList = tools.map((t) => ({
|
|
238
|
+
name: typeof t.name === "string" ? t.name : "?",
|
|
239
|
+
tokens: estimateToolSchema(t)
|
|
240
|
+
}));
|
|
241
|
+
s.toolsTokens = estimateToolsTotal(tools);
|
|
242
|
+
s.systemTokens = estimateSystem(header.system);
|
|
243
|
+
if (header.config && typeof header.config.model === "string") s.model = header.config.model;
|
|
244
|
+
if (header.config && typeof header.config.provider === "string") s.provider = header.config.provider;
|
|
245
|
+
if (data?.reason === "change" && s.model && s.lastModel && s.model !== s.lastModel) {
|
|
246
|
+
s.events.push({ seq: event.seq, time: event.time, kind: "model", from: s.lastModel, to: s.model });
|
|
224
247
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
248
|
+
if (s.model) s.lastModel = s.model;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
case "request/context": {
|
|
252
|
+
const s = ensure();
|
|
253
|
+
if (data && typeof data.contextWindow === "number") s.contextWindow = data.contextWindow;
|
|
254
|
+
if (data && typeof data.model === "string") s.model = data.model;
|
|
255
|
+
if (data && typeof data.provider === "string") s.provider = data.provider;
|
|
256
|
+
s.occupancyWindow = data && typeof data.contextWindow === "number" ? data.contextWindow : void 0;
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
case "tool/call": {
|
|
260
|
+
if (data && data.callId !== void 0 && typeof data.name === "string") {
|
|
261
|
+
const s = ensure();
|
|
262
|
+
s.callNames[String(data.callId)] = data.name;
|
|
229
263
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
total
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
case "user/message": {
|
|
267
|
+
const msg = data;
|
|
268
|
+
const s = ensure();
|
|
269
|
+
const node = applySurface(s, event, event.type, data, msg);
|
|
270
|
+
const source = msg?.source;
|
|
271
|
+
if (isInjection(source)) {
|
|
272
|
+
const rec = {
|
|
273
|
+
seq: event.seq,
|
|
274
|
+
time: event.time,
|
|
275
|
+
kind: "inject",
|
|
276
|
+
form: source.form || "context",
|
|
277
|
+
tokens: node.tokens
|
|
245
278
|
};
|
|
246
|
-
if (
|
|
247
|
-
|
|
248
|
-
|
|
279
|
+
if (source.kind === "skill-invocation") {
|
|
280
|
+
rec.sub = "skill";
|
|
281
|
+
rec.name = typeof source.name === "string" ? source.name : "?";
|
|
282
|
+
} else if (typeof source.plugin === "string" && source.plugin !== "") {
|
|
283
|
+
rec.name = source.plugin;
|
|
249
284
|
}
|
|
250
|
-
|
|
251
|
-
const asstMsg = data?.message ?? null;
|
|
252
|
-
applySurface(st, ev, ev.type, data, asstMsg);
|
|
253
|
-
break;
|
|
285
|
+
s.events.push(rec);
|
|
254
286
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
});
|
|
271
|
-
break;
|
|
272
|
-
default:
|
|
273
|
-
break;
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
case "tool/result": {
|
|
290
|
+
const toolMsg = data?.message ?? null;
|
|
291
|
+
const s = ensure();
|
|
292
|
+
applySurface(s, event, event.type, data, toolMsg);
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
case "assistant/chunk": {
|
|
296
|
+
const chunk = data?.chunk;
|
|
297
|
+
if (chunk !== void 0 && chunk.type === "usage") {
|
|
298
|
+
const s = ensure();
|
|
299
|
+
sampleUsage(s, chunk.usage);
|
|
300
|
+
}
|
|
301
|
+
break;
|
|
274
302
|
}
|
|
303
|
+
case "assistant/message": {
|
|
304
|
+
const usage = data?.usage;
|
|
305
|
+
const s = ensure();
|
|
306
|
+
sampleUsage(s, usage);
|
|
307
|
+
const total = s.systemTokens + s.toolsTokens + s.sums.user + s.sums.inject + s.sums.assistant + s.sums.tool;
|
|
308
|
+
const record = {
|
|
309
|
+
turn: data && typeof data.turn === "number" ? data.turn : void 0,
|
|
310
|
+
step: data && typeof data.step === "number" ? data.step : void 0,
|
|
311
|
+
time: event.time,
|
|
312
|
+
seq: event.seq,
|
|
313
|
+
system: s.systemTokens,
|
|
314
|
+
tools: s.toolsTokens,
|
|
315
|
+
user: s.sums.user,
|
|
316
|
+
inject: s.sums.inject,
|
|
317
|
+
assistant: s.sums.assistant,
|
|
318
|
+
tool: s.sums.tool,
|
|
319
|
+
total
|
|
320
|
+
};
|
|
321
|
+
if (usage && typeof usage.inputTokens === "number") {
|
|
322
|
+
record.prompt = usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
|
|
323
|
+
if (typeof usage.outputTokens === "number") record.output = usage.outputTokens;
|
|
324
|
+
}
|
|
325
|
+
s.requests.push(record);
|
|
326
|
+
const asstMsg = data?.message ?? null;
|
|
327
|
+
applySurface(s, event, event.type, data, asstMsg);
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
case "compaction/summary":
|
|
331
|
+
case "compaction/prune": {
|
|
332
|
+
const s = ensure();
|
|
333
|
+
if (data && Array.isArray(data.shadowedSeqs)) {
|
|
334
|
+
s.pendingShadowedSeqs = data.shadowedSeqs.filter((x) => typeof x === "number");
|
|
335
|
+
}
|
|
336
|
+
s.events.push({
|
|
337
|
+
seq: event.seq,
|
|
338
|
+
time: event.time,
|
|
339
|
+
kind: event.type === "compaction/summary" ? "compaction" : "prune",
|
|
340
|
+
tokens: data && typeof data.shadowedTokenCount === "number" ? data.shadowedTokenCount : 0,
|
|
341
|
+
...event.type === "compaction/summary" && data && Array.isArray(data.shadowedSeqs) ? { count: data.shadowedSeqs.length } : {}
|
|
342
|
+
});
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
default:
|
|
346
|
+
return state;
|
|
275
347
|
}
|
|
276
|
-
st
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (st.requests.length > MAX_REQUEST_STEPS) st.requests = st.requests.slice(-MAX_REQUEST_STEPS);
|
|
348
|
+
if (st !== void 0) {
|
|
349
|
+
trimState(st);
|
|
350
|
+
return st;
|
|
280
351
|
}
|
|
281
|
-
|
|
352
|
+
return state;
|
|
282
353
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const surfaceTotal = st.sums.user + st.sums.inject + st.sums.assistant + st.sums.tool;
|
|
354
|
+
function buildTimelineView(state) {
|
|
355
|
+
const surfaceTotal = state.sums.user + state.sums.inject + state.sums.assistant + state.sums.tool;
|
|
356
|
+
const projectedTokens = state.pressureTokens !== void 0 && state.sampledSurfaceTokens !== void 0 ? Math.max(0, state.pressureTokens + surfaceTotal - state.sampledSurfaceTokens) : void 0;
|
|
287
357
|
const result = {
|
|
288
358
|
ok: true,
|
|
289
|
-
model:
|
|
290
|
-
provider:
|
|
291
|
-
contextWindow:
|
|
359
|
+
model: state.model,
|
|
360
|
+
provider: state.provider,
|
|
361
|
+
contextWindow: state.contextWindow,
|
|
292
362
|
current: {
|
|
293
|
-
system:
|
|
294
|
-
tools:
|
|
295
|
-
user:
|
|
296
|
-
inject:
|
|
297
|
-
assistant:
|
|
298
|
-
tool:
|
|
299
|
-
total: surfaceTotal +
|
|
363
|
+
system: state.systemTokens,
|
|
364
|
+
tools: state.toolsTokens,
|
|
365
|
+
user: state.sums.user,
|
|
366
|
+
inject: state.sums.inject,
|
|
367
|
+
assistant: state.sums.assistant,
|
|
368
|
+
tool: state.sums.tool,
|
|
369
|
+
total: surfaceTotal + state.systemTokens + state.toolsTokens
|
|
300
370
|
},
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
371
|
+
occupancy: {
|
|
372
|
+
...state.pressureTokens === void 0 ? {} : { pressureTokens: state.pressureTokens },
|
|
373
|
+
surfaceTokens: surfaceTotal,
|
|
374
|
+
...state.sampledSurfaceTokens === void 0 ? {} : { sampledSurfaceTokens: state.sampledSurfaceTokens },
|
|
375
|
+
...projectedTokens === void 0 ? {} : { projectedTokens },
|
|
376
|
+
...state.occupancyWindow === void 0 ? {} : { contextWindow: state.occupancyWindow }
|
|
377
|
+
},
|
|
378
|
+
toolList: state.toolList,
|
|
379
|
+
requests: state.requests.map((r) => ({ ...r })),
|
|
380
|
+
events: state.events.map((e) => ({ ...e })),
|
|
304
381
|
nodes: [],
|
|
305
382
|
droppedNodes: 0
|
|
306
383
|
};
|
|
307
|
-
|
|
308
|
-
result.
|
|
309
|
-
|
|
384
|
+
result.droppedNodes = Math.max(0, state.surface.length - MAX_NODES);
|
|
385
|
+
result.nodes = state.surface.slice(-MAX_NODES);
|
|
386
|
+
const requests = result.requests;
|
|
387
|
+
const events = result.events;
|
|
310
388
|
let ri = 0;
|
|
311
|
-
for (const ev of
|
|
312
|
-
while (ri <
|
|
313
|
-
const next =
|
|
314
|
-
const prev = ri > 0 ?
|
|
389
|
+
for (const ev of events) {
|
|
390
|
+
while (ri < requests.length && requests[ri].seq <= ev.seq) ri++;
|
|
391
|
+
const next = requests[ri];
|
|
392
|
+
const prev = ri > 0 ? requests[ri - 1] : void 0;
|
|
315
393
|
if (next !== void 0 && typeof next.turn === "number" && typeof next.step === "number") {
|
|
316
394
|
ev.turn = next.turn;
|
|
317
395
|
ev.step = next.step;
|
|
@@ -323,68 +401,97 @@ function buildResult(st) {
|
|
|
323
401
|
}
|
|
324
402
|
return result;
|
|
325
403
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
404
|
+
|
|
405
|
+
// src/host/timeline.ts
|
|
406
|
+
var surfaceNodeSchema = z.object({
|
|
407
|
+
seq: z.number().int().nonnegative(),
|
|
408
|
+
time: z.number().optional(),
|
|
409
|
+
cat: z.enum(["user", "inject", "assistant", "tool"]),
|
|
410
|
+
tokens: z.number().int().nonnegative(),
|
|
411
|
+
form: z.string().optional(),
|
|
412
|
+
text: z.string().optional(),
|
|
413
|
+
tool: z.string().optional(),
|
|
414
|
+
err: z.boolean().optional(),
|
|
415
|
+
skill: z.string().optional(),
|
|
416
|
+
calls: z.array(z.string()).optional()
|
|
417
|
+
}).strict();
|
|
418
|
+
var requestRecordSchema = z.object({
|
|
419
|
+
turn: z.number().optional(),
|
|
420
|
+
step: z.number().optional(),
|
|
421
|
+
time: z.number(),
|
|
422
|
+
seq: z.number(),
|
|
423
|
+
system: z.number().int().nonnegative(),
|
|
424
|
+
tools: z.number().int().nonnegative(),
|
|
425
|
+
user: z.number().int().nonnegative(),
|
|
426
|
+
inject: z.number().int().nonnegative(),
|
|
427
|
+
assistant: z.number().int().nonnegative(),
|
|
428
|
+
tool: z.number().int().nonnegative(),
|
|
429
|
+
total: z.number().int().nonnegative(),
|
|
430
|
+
prompt: z.number().int().nonnegative().optional(),
|
|
431
|
+
output: z.number().int().nonnegative().optional(),
|
|
432
|
+
stepCount: z.number().int().positive().optional()
|
|
433
|
+
}).strict();
|
|
434
|
+
var contextEventSchema = z.object({
|
|
435
|
+
seq: z.number(),
|
|
436
|
+
time: z.number(),
|
|
437
|
+
kind: z.enum(["compaction", "prune", "inject", "model"]),
|
|
438
|
+
form: z.string().optional(),
|
|
439
|
+
tokens: z.number().optional(),
|
|
440
|
+
count: z.number().optional(),
|
|
441
|
+
sub: z.string().optional(),
|
|
442
|
+
name: z.string().optional(),
|
|
443
|
+
from: z.string().optional(),
|
|
444
|
+
to: z.string().optional(),
|
|
445
|
+
fromTurn: z.number().optional(),
|
|
446
|
+
fromStep: z.number().optional(),
|
|
447
|
+
turn: z.number().optional(),
|
|
448
|
+
step: z.number().optional()
|
|
449
|
+
}).strict();
|
|
450
|
+
var currentSchema = z.object({
|
|
451
|
+
system: z.number().int().nonnegative(),
|
|
452
|
+
tools: z.number().int().nonnegative(),
|
|
453
|
+
user: z.number().int().nonnegative(),
|
|
454
|
+
inject: z.number().int().nonnegative(),
|
|
455
|
+
assistant: z.number().int().nonnegative(),
|
|
456
|
+
tool: z.number().int().nonnegative(),
|
|
457
|
+
total: z.number().int().nonnegative()
|
|
458
|
+
}).strict();
|
|
459
|
+
var occupancySchema = z.object({
|
|
460
|
+
pressureTokens: z.number().int().nonnegative().optional(),
|
|
461
|
+
surfaceTokens: z.number().int().nonnegative(),
|
|
462
|
+
sampledSurfaceTokens: z.number().int().nonnegative().optional(),
|
|
463
|
+
projectedTokens: z.number().int().nonnegative().optional(),
|
|
464
|
+
contextWindow: z.number().int().positive().optional()
|
|
465
|
+
}).strict();
|
|
466
|
+
var contextTimelineSchema = z.object({
|
|
467
|
+
ok: z.literal(true),
|
|
468
|
+
model: z.string().optional(),
|
|
469
|
+
provider: z.string().optional(),
|
|
470
|
+
contextWindow: z.number().optional(),
|
|
471
|
+
current: currentSchema,
|
|
472
|
+
occupancy: occupancySchema.optional(),
|
|
473
|
+
toolList: z.array(z.object({ name: z.string(), tokens: z.number().int().nonnegative() }).strict()),
|
|
474
|
+
requests: z.array(requestRecordSchema),
|
|
475
|
+
events: z.array(contextEventSchema),
|
|
476
|
+
nodes: z.array(surfaceNodeSchema),
|
|
477
|
+
droppedNodes: z.number().int().nonnegative()
|
|
478
|
+
}).strict();
|
|
479
|
+
var contextTimelineDefinition = {
|
|
480
|
+
key: "contextTimeline",
|
|
481
|
+
schema: contextTimelineSchema,
|
|
482
|
+
init: () => createTimelineState(),
|
|
483
|
+
apply: (state, event) => applyTimeline(state, event),
|
|
484
|
+
view: (state) => buildTimelineView(state),
|
|
485
|
+
stateVersion: 1
|
|
486
|
+
};
|
|
354
487
|
|
|
355
488
|
// src/host/index.ts
|
|
356
489
|
var name = "dsh-context";
|
|
357
|
-
var inject = ["
|
|
490
|
+
var inject = ["sessionProjections"];
|
|
358
491
|
function apply(ctx) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
"/dsh-context",
|
|
363
|
-
async (endpoint, payload) => {
|
|
364
|
-
try {
|
|
365
|
-
if (endpoint !== "snapshot") {
|
|
366
|
-
return { ok: false, error: { code: "internal", message: `unknown endpoint: ${endpoint}`, details: {} } };
|
|
367
|
-
}
|
|
368
|
-
const sessionId = payload !== null && typeof payload === "object" ? payload.sessionId : void 0;
|
|
369
|
-
if (typeof sessionId !== "string" || sessionId === "") {
|
|
370
|
-
return { ok: false, error: { code: "internal", message: "missing sessionId", details: {} } };
|
|
371
|
-
}
|
|
372
|
-
const value = await computeSnapshot(ctx, states, sessionId);
|
|
373
|
-
return { ok: true, value };
|
|
374
|
-
} catch (err) {
|
|
375
|
-
return {
|
|
376
|
-
ok: false,
|
|
377
|
-
error: {
|
|
378
|
-
code: "internal",
|
|
379
|
-
message: err instanceof Error ? err.message : String(err),
|
|
380
|
-
details: {}
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
{ authority: "trusted-host" }
|
|
386
|
-
);
|
|
387
|
-
}, "dsh-context: rpc channel");
|
|
492
|
+
ctx.inject(["sessionProjections"], (projectionCtx) => {
|
|
493
|
+
projectionCtx.sessionProjections.register(contextTimelineDefinition);
|
|
494
|
+
});
|
|
388
495
|
}
|
|
389
496
|
export {
|
|
390
497
|
apply,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Context insight panel for DeepSeek Harness: see what the model's context window is made of and how it evolves — composition, per-request history, compactions, injections, and model switches.",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Context insight panel for DeepSeek Harness: see what the model's context window is made of and how it evolves — composition, per-request history, compactions, injections, and model switches. Streamed through the harness's session-projection pipeline.",
|
|
5
5
|
"author": "bowenliang123",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -56,8 +56,15 @@
|
|
|
56
56
|
"ui"
|
|
57
57
|
],
|
|
58
58
|
"license": "Apache-2.0",
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"zod": "^4.4.3"
|
|
61
|
+
},
|
|
59
62
|
"devDependencies": {
|
|
60
63
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.6",
|
|
65
|
+
"@deepseek-ai/dsh-session": "0.1.0-rc.6",
|
|
66
|
+
"@deepseek-ai/dsh-session-projection": "0.1.0-rc.6",
|
|
67
|
+
"@deepseek-ai/dsh-token-meter": "0.1.0-rc.6",
|
|
61
68
|
"@types/react": "^18.3.31",
|
|
62
69
|
"esbuild": "^0.28.2",
|
|
63
70
|
"husky": "^9.1.7",
|