dsh-context 0.60.0 → 0.62.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 +1 -1
- package/lib/client.js +540 -59
- package/lib/index.d.ts +52 -0
- package/lib/index.js +127 -4
- package/package.json +3 -2
package/lib/index.d.ts
CHANGED
|
@@ -157,6 +157,28 @@ interface TimelineState {
|
|
|
157
157
|
time: number;
|
|
158
158
|
firstToken?: number;
|
|
159
159
|
};
|
|
160
|
+
/**
|
|
161
|
+
* The timing strip's painted spans (shared/types.ts TimingSpan): every
|
|
162
|
+
* completed step's time slices in log order, stamped with their REAL
|
|
163
|
+
* instants — the TTFT wait, the decode blocks in stream order, the tool-run
|
|
164
|
+
* windows, and the in-step residue. The client packs them gapless, each
|
|
165
|
+
* band's width its share of the cumulative active time (idle time BETWEEN
|
|
166
|
+
* steps carries no span and takes no track). Bounded by TIMING_SPANS_MAX
|
|
167
|
+
* (newest tail kept). Flushed per step at `step/end` from the `stepSpans`
|
|
168
|
+
* accumulator.
|
|
169
|
+
*/
|
|
170
|
+
spans: TimingSpan[];
|
|
171
|
+
/**
|
|
172
|
+
* The open step's accumulated painted spans: the assistant message pushes
|
|
173
|
+
* the model slices (TTFT + the decode blocks), each paired tool result its
|
|
174
|
+
* run window; `step/end` clamps them into the step window, de-overlaps
|
|
175
|
+
* first-wins (parallel runs paint their union), fills the holes with the
|
|
176
|
+
* residue kind, and flushes the tiling into `spans`. Absent until the first
|
|
177
|
+
* in-step span; DELETED at the flush (the plain-JSON precondition). Same
|
|
178
|
+
* arm/remove lifecycle as `stepStart` — never written in place, always
|
|
179
|
+
* replaced, so no CloneKey covers it.
|
|
180
|
+
*/
|
|
181
|
+
stepSpans?: TimingSpan[];
|
|
160
182
|
/**
|
|
161
183
|
* Tool callId → the call's name, start instant, and raw arguments, armed by
|
|
162
184
|
* `tool/call` and DELETED when its `tool/result` folds in (one result per
|
|
@@ -469,6 +491,13 @@ interface Snapshot {
|
|
|
469
491
|
*/
|
|
470
492
|
fileOps?: FileOpRecord[];
|
|
471
493
|
fileOpsFloor?: number;
|
|
494
|
+
/**
|
|
495
|
+
* The timing strip's painted spans (see TimingSpan) — present on the INLINE
|
|
496
|
+
* wire value and on the detail payload, absent from the slim head (they ride
|
|
497
|
+
* the detail channel there) and from rows folded before the collection
|
|
498
|
+
* existed (older hosts; the client then shows no strip).
|
|
499
|
+
*/
|
|
500
|
+
spans?: TimingSpan[];
|
|
472
501
|
}
|
|
473
502
|
/**
|
|
474
503
|
* The on-demand DETAIL payload of the split `contextTimeline` generation —
|
|
@@ -509,6 +538,13 @@ interface ContextTimelineDetail {
|
|
|
509
538
|
fileOps?: FileOpRecord[];
|
|
510
539
|
/** The newest dropped op's seq when the op log trimmed (coverage honesty, same family as archiveFloor). */
|
|
511
540
|
fileOpsFloor?: number;
|
|
541
|
+
/**
|
|
542
|
+
* The timing strip's painted spans (see TimingSpan): the completed steps'
|
|
543
|
+
* time slices in log order, positioned by their real instants. Additive-
|
|
544
|
+
* optional: a payload folded before the collection existed (older hosts)
|
|
545
|
+
* serves without it, and the timing card shows no strip then.
|
|
546
|
+
*/
|
|
547
|
+
spans?: TimingSpan[];
|
|
512
548
|
}
|
|
513
549
|
/**
|
|
514
550
|
* One executed file operation (a settled file-tool call with a resolved
|
|
@@ -559,6 +595,22 @@ interface FileOpRecord {
|
|
|
559
595
|
* delivered projection is by definition available); kept for wire compatibility with the snapshot shape.
|
|
560
596
|
*/
|
|
561
597
|
type ContextTimeline = Snapshot;
|
|
598
|
+
/**
|
|
599
|
+
* One painted span of the timing card's session-time strip (the card's bottom
|
|
600
|
+
* band): a completed step's time slice — the TTFT wait, one decode block (in
|
|
601
|
+
* stream order), one tool-run window, or an in-step residue gap — folded
|
|
602
|
+
* host-side at `step/end`. The instants are the slice's REAL occurrence times
|
|
603
|
+
* (the hover tip reads them); the strip packs the spans gapless in log order,
|
|
604
|
+
* each band's width its share of the session's cumulative active time (idle
|
|
605
|
+
* time BETWEEN steps carries no span and takes no track). `end` > `start`
|
|
606
|
+
* always (the fold drops zero/negative slices).
|
|
607
|
+
*/
|
|
608
|
+
interface TimingSpan {
|
|
609
|
+
/** The slice kind — the timing card's own slice vocabulary (same keys, same colors). */
|
|
610
|
+
kind: 'ttft' | 'reasoning' | 'text' | 'toolarg' | 'tools' | 'other';
|
|
611
|
+
start: number;
|
|
612
|
+
end: number;
|
|
613
|
+
}
|
|
562
614
|
/**
|
|
563
615
|
* Cumulative billed-token totals for one pricing bucket of the session-cost
|
|
564
616
|
* estimate (host-folded, never trimmed — running totals over the COMPLETE
|
package/lib/index.js
CHANGED
|
@@ -414,6 +414,43 @@ function decodeTallyOfStream(stream, endTime) {
|
|
|
414
414
|
return tally;
|
|
415
415
|
}
|
|
416
416
|
/**
|
|
417
|
+
* The ORDERED decode spans of one embedded assistant stream — the same
|
|
418
|
+
* ownership rule as {@link decodeTallyOfStream} (each `block-start` marker
|
|
419
|
+
* owns the interval up to the next one, the last one up to `endTime`), but
|
|
420
|
+
* keeping the sequence: the timing strip paints the decode window in stream
|
|
421
|
+
* order. Total over untrusted input, mirroring the tally: a malformed record
|
|
422
|
+
* is skipped whole, an unknown `blockType` leaves its interval unspanned
|
|
423
|
+
* (the caller's residue fill owns it), a zero/negative-length span drops
|
|
424
|
+
* out, and a stream with no marker (or not an array) yields none.
|
|
425
|
+
*/
|
|
426
|
+
function decodeSpansOfStream(stream, endTime) {
|
|
427
|
+
const spans = [];
|
|
428
|
+
if (!Array.isArray(stream) || !Number.isFinite(endTime)) return spans;
|
|
429
|
+
let kind;
|
|
430
|
+
let since = 0;
|
|
431
|
+
const close = (to) => {
|
|
432
|
+
if (kind !== void 0 && to > since) spans.push({
|
|
433
|
+
kind,
|
|
434
|
+
start: since,
|
|
435
|
+
end: to
|
|
436
|
+
});
|
|
437
|
+
};
|
|
438
|
+
for (const record of stream) {
|
|
439
|
+
if (record === null || typeof record !== "object") continue;
|
|
440
|
+
const r = record;
|
|
441
|
+
if (r.type !== "chunk" || r.chunk === null || typeof r.chunk !== "object") continue;
|
|
442
|
+
const chunk = r.chunk;
|
|
443
|
+
if (chunk.type !== "block-start") continue;
|
|
444
|
+
const time = r.time;
|
|
445
|
+
if (typeof time !== "number" || !Number.isFinite(time)) continue;
|
|
446
|
+
close(time);
|
|
447
|
+
kind = decodeKindOfBlock(chunk.blockType);
|
|
448
|
+
since = time;
|
|
449
|
+
}
|
|
450
|
+
close(endTime);
|
|
451
|
+
return spans;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
417
454
|
* The first token's instant inside one PACKED delta run (`text-chunks` /
|
|
418
455
|
* `reasoning-chunks` / `tool-call-chunks`): the run's base time plus the
|
|
419
456
|
* accumulated inter-member deltas, taken at the first qualifying member —
|
|
@@ -758,6 +795,7 @@ function trimState(st, bounds) {
|
|
|
758
795
|
st.turnRuns = countTurnRuns(st.requests);
|
|
759
796
|
}
|
|
760
797
|
if (st.events.length > bounds.maxEvents) st.events = st.events.slice(-bounds.maxEvents);
|
|
798
|
+
if (st.spans.length > TIMING_SPANS_MAX) st.spans = st.spans.slice(-TIMING_SPANS_MAX);
|
|
761
799
|
if (st.fileOps.length > bounds.maxFileOps) {
|
|
762
800
|
const drop = st.fileOps.length - bounds.maxFileOps;
|
|
763
801
|
st.fileOpsFloor = Math.max(st.fileOpsFloor ?? 0, st.fileOps[drop - 1].seq);
|
|
@@ -775,6 +813,13 @@ function trimState(st, bounds) {
|
|
|
775
813
|
}
|
|
776
814
|
}
|
|
777
815
|
}
|
|
816
|
+
/**
|
|
817
|
+
* Bound on the timing strip's painted spans (TimelineState.spans): a count
|
|
818
|
+
* cap, newest tail kept — the same hardcoded-cap family as SYSTEM_NODES_MAX.
|
|
819
|
+
* At ~6–8 spans per step the cap covers a few hundred recent steps, far past
|
|
820
|
+
* the strip's pixel resolution on any real pane.
|
|
821
|
+
*/
|
|
822
|
+
const TIMING_SPANS_MAX = 2e3;
|
|
778
823
|
function createTimelineState() {
|
|
779
824
|
return {
|
|
780
825
|
surface: [],
|
|
@@ -792,7 +837,8 @@ function createTimelineState() {
|
|
|
792
837
|
events: [],
|
|
793
838
|
archived: [],
|
|
794
839
|
callNames: {},
|
|
795
|
-
fileOps: []
|
|
840
|
+
fileOps: [],
|
|
841
|
+
spans: []
|
|
796
842
|
};
|
|
797
843
|
}
|
|
798
844
|
function categoryOf(type, message) {
|
|
@@ -969,6 +1015,11 @@ function applySurface(st, ev, type, data, message) {
|
|
|
969
1015
|
timing.toolsMs += dur;
|
|
970
1016
|
timing.toolCalls += 1;
|
|
971
1017
|
bumpToolTotals(timing, toolEntry.name, dur);
|
|
1018
|
+
if (st.stepStart !== void 0) st.stepSpans = [...st.stepSpans ?? [], {
|
|
1019
|
+
kind: "tools",
|
|
1020
|
+
start: toolEntry.start,
|
|
1021
|
+
end: ev.time
|
|
1022
|
+
}];
|
|
972
1023
|
}
|
|
973
1024
|
if (typeof srcId === "string" || typeof blockId === "string") {
|
|
974
1025
|
const kept = {};
|
|
@@ -1345,6 +1396,7 @@ function applyTimeline(state, event, bounds) {
|
|
|
1345
1396
|
case "step/start": {
|
|
1346
1397
|
const s = ensure([]);
|
|
1347
1398
|
s.stepStart = { time: event.time };
|
|
1399
|
+
delete s.stepSpans;
|
|
1348
1400
|
break;
|
|
1349
1401
|
}
|
|
1350
1402
|
case "step/end": {
|
|
@@ -1352,7 +1404,43 @@ function applyTimeline(state, event, bounds) {
|
|
|
1352
1404
|
if (start === void 0) return state;
|
|
1353
1405
|
const s = ensure(["timing"]);
|
|
1354
1406
|
ensureTiming(s).wallMs += durOf(start.time, event.time);
|
|
1407
|
+
const painted = [];
|
|
1408
|
+
for (const span of state.stepSpans ?? []) {
|
|
1409
|
+
const from = Math.max(start.time, span.start);
|
|
1410
|
+
const to = Math.min(event.time, span.end);
|
|
1411
|
+
if (to > from) painted.push({
|
|
1412
|
+
kind: span.kind,
|
|
1413
|
+
start: from,
|
|
1414
|
+
end: to
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
painted.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
1418
|
+
const flushed = [];
|
|
1419
|
+
let cursor = start.time;
|
|
1420
|
+
for (const span of painted) {
|
|
1421
|
+
const from = Math.max(cursor, span.start);
|
|
1422
|
+
if (span.end <= from) continue;
|
|
1423
|
+
if (from > cursor) flushed.push({
|
|
1424
|
+
kind: "other",
|
|
1425
|
+
start: cursor,
|
|
1426
|
+
end: from
|
|
1427
|
+
});
|
|
1428
|
+
flushed.push({
|
|
1429
|
+
kind: span.kind,
|
|
1430
|
+
start: from,
|
|
1431
|
+
end: span.end
|
|
1432
|
+
});
|
|
1433
|
+
cursor = span.end;
|
|
1434
|
+
}
|
|
1435
|
+
if (event.time > cursor) flushed.push({
|
|
1436
|
+
kind: "other",
|
|
1437
|
+
start: cursor,
|
|
1438
|
+
end: event.time
|
|
1439
|
+
});
|
|
1440
|
+
s.spans = [...state.spans, ...flushed];
|
|
1355
1441
|
delete s.stepStart;
|
|
1442
|
+
delete s.stepSpans;
|
|
1443
|
+
bumpDetailRev(s);
|
|
1356
1444
|
break;
|
|
1357
1445
|
}
|
|
1358
1446
|
case "user/message": {
|
|
@@ -1506,6 +1594,24 @@ function applyTimeline(state, event, bounds) {
|
|
|
1506
1594
|
if (firstToken !== void 0) {
|
|
1507
1595
|
timing.ttftMs += durOf(stepStart.time, firstToken);
|
|
1508
1596
|
timing.genMs += durOf(firstToken, event.time);
|
|
1597
|
+
const decodeBlocks = decodeSpansOfStream(data?.stream, event.time);
|
|
1598
|
+
let decodeStart = firstToken;
|
|
1599
|
+
for (const block of decodeBlocks) decodeStart = Math.min(decodeStart, block.start);
|
|
1600
|
+
const modelSpans = [{
|
|
1601
|
+
kind: "ttft",
|
|
1602
|
+
start: stepStart.time,
|
|
1603
|
+
end: decodeStart
|
|
1604
|
+
}];
|
|
1605
|
+
for (const block of decodeBlocks) {
|
|
1606
|
+
const to = Math.min(event.time, block.end);
|
|
1607
|
+
if (to <= block.start) continue;
|
|
1608
|
+
modelSpans.push({
|
|
1609
|
+
kind: block.kind,
|
|
1610
|
+
start: modelSpans.length === 1 ? decodeStart : block.start,
|
|
1611
|
+
end: to
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
s.stepSpans = [...s.stepSpans ?? [], ...modelSpans];
|
|
1509
1615
|
if (output !== null) {
|
|
1510
1616
|
timing.speedMs = (timing.speedMs ?? 0) + durOf(firstToken, event.time);
|
|
1511
1617
|
timing.speedTokens = (timing.speedTokens ?? 0) + output;
|
|
@@ -1641,7 +1747,8 @@ function detailCollectionsOf(state, bounds) {
|
|
|
1641
1747
|
droppedNodes: 0,
|
|
1642
1748
|
archive: state.archived.map((n) => ({ ...n })),
|
|
1643
1749
|
fileOps: state.fileOps.map((o) => ({ ...o })),
|
|
1644
|
-
...state.fileOpsFloor !== void 0 ? { fileOpsFloor: state.fileOpsFloor } : {}
|
|
1750
|
+
...state.fileOpsFloor !== void 0 ? { fileOpsFloor: state.fileOpsFloor } : {},
|
|
1751
|
+
spans: state.spans.map((s) => ({ ...s }))
|
|
1645
1752
|
};
|
|
1646
1753
|
const overflowCount = Math.max(0, state.surface.length - bounds.maxNodes);
|
|
1647
1754
|
const overflow = state.surface.slice(0, overflowCount);
|
|
@@ -3103,6 +3210,19 @@ const fileOpSchema = z.object({
|
|
|
3103
3210
|
pattern: z.literal(true).optional(),
|
|
3104
3211
|
gone: z.number().int().nonnegative().optional()
|
|
3105
3212
|
}).strict();
|
|
3213
|
+
/** One painted span of the timing strip (shared/types.ts TimingSpan). */
|
|
3214
|
+
const timingSpanSchema = z.object({
|
|
3215
|
+
kind: z.enum([
|
|
3216
|
+
"ttft",
|
|
3217
|
+
"reasoning",
|
|
3218
|
+
"text",
|
|
3219
|
+
"toolarg",
|
|
3220
|
+
"tools",
|
|
3221
|
+
"other"
|
|
3222
|
+
]),
|
|
3223
|
+
start: z.number(),
|
|
3224
|
+
end: z.number()
|
|
3225
|
+
}).strict();
|
|
3106
3226
|
const currentSchema = z.object({
|
|
3107
3227
|
system: z.number().int().nonnegative(),
|
|
3108
3228
|
tools: z.number().int().nonnegative(),
|
|
@@ -3198,7 +3318,8 @@ const contextTimelineSchema = z.object({
|
|
|
3198
3318
|
surfaceFloor: z.number().int().nonnegative().optional(),
|
|
3199
3319
|
archiveFloor: z.number().int().nonnegative().optional(),
|
|
3200
3320
|
fileOps: z.array(fileOpSchema).optional(),
|
|
3201
|
-
fileOpsFloor: z.number().int().nonnegative().optional()
|
|
3321
|
+
fileOpsFloor: z.number().int().nonnegative().optional(),
|
|
3322
|
+
spans: z.array(timingSpanSchema).optional()
|
|
3202
3323
|
}).strict();
|
|
3203
3324
|
/**
|
|
3204
3325
|
* The persisted fold-state schema (the registry's `stateSchema`
|
|
@@ -3245,6 +3366,8 @@ const timelineStateSchema = z.object({
|
|
|
3245
3366
|
detailRev: z.number().int().nonnegative().optional(),
|
|
3246
3367
|
fileOps: z.array(fileOpSchema),
|
|
3247
3368
|
fileOpsFloor: z.number().int().nonnegative().optional(),
|
|
3369
|
+
spans: z.array(timingSpanSchema),
|
|
3370
|
+
stepSpans: z.array(timingSpanSchema).optional(),
|
|
3248
3371
|
pendingCodeOps: z.record(z.string(), z.array(fileOpSchema)).optional()
|
|
3249
3372
|
});
|
|
3250
3373
|
/**
|
|
@@ -3292,7 +3415,7 @@ function createContextTimelineDefinition(config, slim) {
|
|
|
3292
3415
|
},
|
|
3293
3416
|
init: () => createTimelineState(),
|
|
3294
3417
|
apply: (state, event) => applyTimeline(state, event, bounds),
|
|
3295
|
-
stateVersion:
|
|
3418
|
+
stateVersion: 23
|
|
3296
3419
|
};
|
|
3297
3420
|
}
|
|
3298
3421
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.62.0",
|
|
4
4
|
"description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
|
|
5
5
|
"icon": "icon.svg",
|
|
6
6
|
"author": "bowenliang123",
|
|
@@ -69,7 +69,8 @@
|
|
|
69
69
|
"compatibility": {
|
|
70
70
|
"dshReleases": {
|
|
71
71
|
"0.1.5-rc.1": "compatible",
|
|
72
|
-
"0.1.7-rc.2": "compatible"
|
|
72
|
+
"0.1.7-rc.2": "compatible",
|
|
73
|
+
"0.2.0-rc.2": "compatible"
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
},
|