dsh-context 0.66.1 → 0.67.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/lib/index.d.ts CHANGED
@@ -4,31 +4,21 @@ import "@deepseek-ai/dsh-session";
4
4
  import z$1 from "@deepseek-ai/schemastery";
5
5
  import { Context } from "@deepseek-ai/cordis";
6
6
  //#region src/host/activity.d.ts
7
- /** The persisted fold state (the registry's `stateSchema` contract). */
8
7
  interface ActivityState {
9
8
  days: Record<string, ActivityDay>;
10
- /** The route in force (last `request/header`'s config wins) — prices each settlement. */
9
+ /** The route in force (the last `request/header`'s config wins) — prices each settlement. */
11
10
  model?: string;
12
11
  provider?: string;
13
12
  /** The open step's start instant (armed by `step/start`, cleared by `step/end`). */
14
13
  stepStart?: number;
15
- /**
16
- * Pending `skill`-tool call ids (armed by `tool/call`, consumed by the
17
- * pairing `tool/result`). The wrapper match alone is not trusted: a `read`
18
- * of source text quoting `<skill_content name="…">` (this very file's
19
- * comments, say) must not book a load — the timeline fold's pairing
20
- * authority (fold.ts) applies here too. Bounded at
21
- * {@link MAX_PENDING_SKILL_CALLS}; a never-answered call's id drops oldest
22
- * first.
23
- */
14
+ /** Pending `skill`-tool call ids (armed by `tool/call`, consumed by the pairing `tool/result`):
15
+ * the wrapper match alone is not trusted, or a `read` of source text quoting
16
+ * `<skill_content name="…">` would book a load. Bounded at {@link MAX_PENDING_SKILL_CALLS}. */
24
17
  skillCalls?: string[];
25
18
  }
26
19
  //#endregion
27
20
  //#region src/host/headers.d.ts
28
- /**
29
- * One stored tool: the v1 row shape carried the producer description and the
30
- * raw schema; folds since the #37 slim-down append metadata-only entries.
31
- */
21
+ /** One stored tool: v1 rows carried the producer description and raw schema; later folds append metadata-only. */
32
22
  interface StoredHeaderTool {
33
23
  name: string;
34
24
  tokens: number;
@@ -36,7 +26,7 @@ interface StoredHeaderTool {
36
26
  plugin?: string;
37
27
  schema?: unknown;
38
28
  }
39
- /** One stored epoch: v1 rows carried `system`; folds since carry `systemTokens`. */
29
+ /** One stored epoch: v1 rows carried `system`; later folds carry `systemTokens`. */
40
30
  interface StoredHeaderRecord {
41
31
  seq: number;
42
32
  time: number;
@@ -48,271 +38,113 @@ interface HeadersState {
48
38
  headers: StoredHeaderRecord[];
49
39
  }
50
40
  //#endregion
51
- //#region src/host/fold.d.ts
52
- /**
53
- * History retention bounds (configurable since 0.11 — see config.ts; these
54
- * are the defaults' values). The fold keeps per-STEP request records; once the
55
- * newest run count exceeds `maxKeptTurns`, the timeline is trimmed to the
56
- * most recent whole TURN runs (never cutting a turn in half), so turn
57
- * granularity can always show the full recent turn range instead of a
58
- * step-count fragment. The turn-run trim runs whenever the cap is crossed
59
- * (not only when the raw step bound is), so the bounded state stays at the
60
- * newest ~`maxKeptTurns` turns deterministically as a live log grows.
61
- */
41
+ //#region src/host/foldState.d.ts
62
42
  interface TimelineState {
63
43
  /** Model-visible surface, newest last. */
64
44
  surface: SurfaceNode[];
65
45
  sums: Record<Category, number>;
66
46
  systemTokens: number;
67
- /**
68
- * The live system-prompt nodes, oldest first — a log's `system/message`
69
- * surface nodes. `systemTokens` is the LAST entry with tokens > 0 (the
70
- * harness's own "last nonempty surviving system" rule), so an empty dormant
71
- * node keeps its position without clearing the prompt. Bounded by
72
- * SYSTEM_NODES_MAX. ABSENT on rows folded before this field existed — the
73
- * wire then serves no `systems` and the client falls back to the header
74
- * epoch's own envelope figure.
75
- */
47
+ /** The live system-prompt nodes, oldest first. `systemTokens` is the LAST entry with tokens > 0 (the harness's
48
+ * own "last nonempty surviving system" rule), so an empty dormant node keeps its position without clearing the
49
+ * prompt. Bounded by SYSTEM_NODES_MAX. Absent on rows folded before this field existed; the client then falls
50
+ * back to the header epoch's own envelope figure. */
76
51
  systems?: SystemPromptNode[];
77
52
  toolsTokens: number;
78
- /**
79
- * The projection-cache precondition is plain JSON: a property whose value
80
- * is `undefined` makes the whole checkpoint unserializable
81
- * (`snapshotJsonValue` rejects it), which fails EVERY cache write for the
82
- * session — including the `title` projection row that powers the session
83
- * list after a restart. Optional fields therefore use absent properties
84
- * (`model`/`provider`/`lastModel`/`contextWindow` are simply not set until
85
- * a value is known) instead of `undefined`-valued ones. Reads via
86
- * `state.model` are identical for both shapes (`undefined` on miss).
87
- */
53
+ /** The projection cache requires plain JSON: a property whose value is `undefined` makes the whole checkpoint
54
+ * unserializable, failing EVERY cache write for the session — including the `title` row that powers the session
55
+ * list after a restart. Optional fields therefore stay absent until a value is known, never `undefined`-valued. */
88
56
  model?: string;
89
57
  provider?: string;
90
58
  lastModel?: string;
91
59
  contextWindow?: number;
92
60
  requests: RequestRecord[];
93
- /**
94
- * The number of turn runs in `requests` (a run = consecutive records
95
- * sharing one `turn`). Maintained incrementally at the single append site
96
- * and recomputed when a trim replaces the array, so the retention trim's
97
- * cap check stays O(1) per event instead of walking every retained
98
- * request. Absent on rows folded before the field existed — the first
99
- * post-restore event recomputes it once (the same additive-optional shape
100
- * as `detailRev`).
101
- */
61
+ /** The number of turn runs in `requests` (a run = consecutive records sharing one `turn`). Maintained
62
+ * incrementally at the single append site and recomputed when a trim replaces the array, so the retention
63
+ * trim's cap check stays O(1) per event. Absent on rows folded before the field existed. */
102
64
  turnRuns?: number;
103
65
  events: ContextEventRecord[];
104
- /**
105
- * Recently removed surface nodes (stamped COPIES carrying `gone`), in
106
- * removal order. Feeds the Context browser's per-step reconstruction.
107
- * Bounded two ways in trimState: capped to `maxArchiveNodes`, and pruned
108
- * to removals after the oldest retained request (older removals can only
109
- * serve steps the requests trim already forgot).
110
- */
66
+ /** Recently removed surface nodes (stamped COPIES carrying `gone`), in removal order. Bounded two ways in
67
+ * trimState: capped to `maxArchiveNodes`, and pruned to removals after the oldest retained request. */
111
68
  archived: SurfaceNode[];
112
- /**
113
- * Session-cost raw material: cumulative billed-token totals per
114
- * (provider, model), split into pricing periods for DeepSeek (see
115
- * SessionCostUsage / CostModelUsage). Running totals — never trimmed, so
116
- * the estimate always covers the COMPLETE session log even after the
117
- * request/event retention bounds cut in. Absent until a usage-reporting
118
- * request with a known model folds.
119
- */
69
+ /** Cumulative billed-token totals per (provider, model), split into pricing periods for DeepSeek. Never trimmed, so
70
+ * the estimate covers the COMPLETE session log. */
120
71
  cost?: SessionCostUsage;
121
- /**
122
- * Whole-session human-input tally (see Snapshot.humanInputs): every
123
- * non-injection `user/message` plus every answered `ask_user_question`
124
- * result. Running total — never trimmed, like `cost`/`timing`. Absent until
125
- * the first human input folds.
126
- */
72
+ /** Whole-session human-input tally (see Snapshot.humanInputs); never trimmed, like `cost`. */
127
73
  humanInputs?: number;
128
- /**
129
- * Whole-session answers tally (see Snapshot.answers): one per assistant
130
- * message carrying a non-blank text block — a step that only dispatched
131
- * tool calls (or projected to no message at all) is work, not a reply.
132
- * Running total — never trimmed, like `humanInputs`. Absent until the first
133
- * textual reply folds.
134
- */
74
+ /** Whole-session answers tally (see Snapshot.answers); never trimmed, like `humanInputs`. */
135
75
  answers?: number;
136
- /**
137
- * The user's newest own message as a bounded one-line preview (the surface
138
- * node's first-text line). Last-wins — a newer human input replaces it, a
139
- * text-less one (images only) keeps the previous line. Additive-optional
140
- * like the 0.47 fields: cached rows folded before the field existed read
141
- * without it and the card just hides its preview line.
142
- */
76
+ /** The user's newest own message as a bounded one-line preview. Last-wins: a text-less input (images only) keeps the previous line. */
143
77
  lastUser?: string;
144
78
  archiveFloor?: number;
145
- /**
146
- * The detail collections' revision marker (see ContextTimelineDetail):
147
- * bumped by every fold that mutates the request records, context events,
148
- * live surface, or the removed-node archive — the slim wire head carries
149
- * it so an open tab knows its fetched detail went stale. Absent until the
150
- * first detail fold (undefined reads as 0; never materialize an
151
- * `undefined`-valued property — the plain-JSON precondition above).
152
- */
79
+ /** Bumped by every fold that mutates the request records, context events, live surface, or removed-node
80
+ * archive; the slim wire head carries it so an open tab knows its fetched detail went stale. */
153
81
  detailRev?: number;
154
- /**
155
- * Whole-session timing totals (see TimingTotals) — running sums over the
156
- * COMPLETE session log, like `cost`. Absent until the first step or tool
157
- * lifecycle folds in; created once and cloned-on-touch afterwards (the
158
- * object is shared with the persisted previous state — see `ensure`).
159
- */
82
+ /** Whole-session timing totals (see TimingTotals), running sums over the COMPLETE session log like `cost`. */
160
83
  timing?: TimingTotals;
161
- /**
162
- * The open step's start instant, armed by `step/start` and consumed by the
163
- * `assistant/message` (TTFT/generation split) and `step/end` (wall time)
164
- * that follow it; the message's embedded stream stamps `firstToken` —
165
- * absent when the stream carried none (an aborted step), which leaves that
166
- * call's model time unattributed. One slot, not a map: steps are sequential
167
- * in the log, so the newest `step/start` is the one those events close — a
168
- * hostile interleaved log degrades to skipped durations, never to
169
- * unbounded state. Same arm/remove lifecycle as `pendingShadowedSeqs`.
170
- */
84
+ /** The open step's start instant, armed by `step/start` and consumed by the `assistant/message` (TTFT/generation
85
+ * split) and `step/end` (wall time) that follow. One slot, not a map: steps are sequential in the log, so the
86
+ * newest `step/start` is the one those events close — a hostile interleaved log degrades to skipped durations. */
171
87
  stepStart?: {
172
88
  time: number;
173
89
  firstToken?: number;
174
90
  };
175
- /**
176
- * The open step's CLOSED user-wait intervals (real log instants), pushed
177
- * replace-style as each wait settles: an approval decision (its asked →
178
- * decided pair closes via `stepApprovals`) and an `ask_user_question`
179
- * call's whole tool window (its body is the answer wait itself, in both
180
- * the blocking and the timed schema's foreground phase). `step/end`
181
- * subtracts their clamped union from the step window to price the
182
- * request's `activeMs`, then DELETES the slot. Same arm/remove lifecycle
183
- * as `stepStart` — never written in place, so no CloneKey covers it.
184
- */
91
+ /** The open step's CLOSED user-wait intervals (real log instants), pushed replace-style as each wait settles: an
92
+ * approval decision, or an `ask_user_question` call's whole tool window. `step/end` subtracts their clamped
93
+ * union from the step window to price the request's `activeMs`, then deletes the slot. */
185
94
  stepWaits?: {
186
95
  start: number;
187
96
  end: number;
188
97
  }[];
189
- /**
190
- * The open step's PENDING approval waits (request id → its `approval/asked`
191
- * instant), armed only while a step is open and disarmed by the matching
192
- * `approval/decided`. A crash mid-decision leaves the pair dangling —
193
- * resume repair appends no synthetic decision — so `step/end` closes the
194
- * survivors at its own instant. One record, not a map: pending approvals
195
- * are a handful at most, and the rebuild-on-change copy stays trivial.
196
- * Deleted at `step/start` (a superseded step's leftovers) and `step/end`.
197
- */
98
+ /** The open step's PENDING approval waits (request id → its `approval/asked` instant), disarmed by the matching
99
+ * `approval/decided`. A crash mid-decision leaves the pair dangling, so `step/end` closes the survivors at its
100
+ * own instant. Deleted at `step/start` (a superseded step's leftovers) and `step/end`. */
198
101
  stepApprovals?: Record<string, number>;
199
- /**
200
- * The seq of the request record committed by the open step's
201
- * `assistant/message` — the back-pointer `step/end` stamps `activeMs`
202
- * onto. It stays the requests TAIL until then (nothing pushes between a
203
- * step's message and its end, and the turn trim only drops the oldest),
204
- * so the stamp is an O(1) tail write guarded by the seq. Same arm/remove
205
- * lifecycle as `stepStart`.
206
- */
102
+ /** The seq of the request record committed by the open step's `assistant/message` — the back-pointer `step/end`
103
+ * stamps `activeMs` onto. Nothing pushes between a step's message and its end, so the record is the requests
104
+ * tail and the stamp is an O(1) tail write guarded by this seq. */
207
105
  stepRequestSeq?: number;
208
- /**
209
- * The timing strip's painted spans (shared/types.ts TimingSpan): every
210
- * completed step's time slices in log order, stamped with their REAL
211
- * instants — the TTFT wait, the decode blocks in stream order, the tool-run
212
- * windows, and the in-step residue. The client packs them gapless, each
213
- * band's width its share of the cumulative active time (idle time BETWEEN
214
- * steps carries no span and takes no track). Bounded by TIMING_SPANS_MAX
215
- * (newest tail kept). Flushed per step at `step/end` from the `stepSpans`
216
- * accumulator.
217
- */
106
+ /** The timing strip's painted spans: every completed step's time slices in log order, stamped with their REAL
107
+ * instants — the TTFT wait, the decode blocks in stream order, the tool-run windows, and the in-step residue.
108
+ * Idle time BETWEEN steps carries no span. Bounded by TIMING_SPANS_MAX (newest tail kept). */
218
109
  spans: TimingSpan[];
219
- /**
220
- * The open step's accumulated painted spans: the assistant message pushes
221
- * the model slices (TTFT + the decode blocks), each paired tool result its
222
- * run window; `step/end` clamps them into the step window, de-overlaps
223
- * first-wins (parallel runs paint their union), fills the holes with the
224
- * residue kind, and flushes the tiling into `spans`. Absent until the first
225
- * in-step span; DELETED at the flush (the plain-JSON precondition). Same
226
- * arm/remove lifecycle as `stepStart` — never written in place, always
227
- * replaced, so no CloneKey covers it.
228
- */
110
+ /** The open step's accumulated painted spans; `step/end` clamps them into the step window, de-overlaps
111
+ * first-wins (parallel runs paint their union), fills the holes with the residue kind, and flushes the tiling
112
+ * into `spans`. Deleted at the flush (the plain-JSON precondition). */
229
113
  stepSpans?: TimingSpan[];
230
- /**
231
- * Tool callId → the call's name, start instant, and raw arguments, armed by
232
- * `tool/call` and DELETED when its `tool/result` folds in (one result per
233
- * call, in log order) — the map stays at pending-call size instead of
234
- * growing for the session's whole lifetime (it is persisted state,
235
- * shallow-copied by every fold step). The start instant prices the call's
236
- * duration into `timing.toolsMs` when the result arrives; the raw arguments
237
- * feed the file-op derivation (shared/fileOps.ts) at that same moment.
238
- */
114
+ /** Tool callId → the call's name, start instant, and raw arguments, armed by `tool/call` and DELETED when its
115
+ * `tool/result` folds in — the map stays at pending-call size instead of growing for the session's lifetime. */
239
116
  callNames: Record<string, {
240
117
  name: string;
241
118
  start: number;
242
119
  argsRaw?: string;
243
120
  }>;
244
- /**
245
- * Seq list of the surface nodes the next replacement will shadow, armed by
246
- * the metering event (`compaction/summary` | `compaction/prune`) and
247
- * consumed by the replacement that must follow it synchronously. The
248
- * producer's shadow price covers exactly these seqs — which can differ
249
- * from the replacement's declared range (pruned replacement nodes keep
250
- * their own seqs, beyond the range end) — so removal must follow the seqs.
251
- * Absent until armed, and REMOVED (not set to `undefined`) when consumed,
252
- * to keep the state plain JSON for the projection cache.
253
- */
121
+ /** Seq list of the surface nodes the next replacement will shadow, armed by the metering event
122
+ * (`compaction/summary` | `compaction/prune`) and consumed by the replacement that must follow it
123
+ * synchronously. The producer's shadow price covers exactly these seqs, which can differ from the
124
+ * replacement's declared range (pruned replacement nodes keep their own seqs, beyond the range end). */
254
125
  pendingShadowedSeqs?: number[];
255
- /**
256
- * The seq of the compaction/prune event that armed `pendingShadowedSeqs` —
257
- * the shadowed path rewrites that event's `tokens` from the gross shadow
258
- * price to the NET freed amount (removed nodes minus the synchronous
259
- * replacement), so the row matches the drop the trend chart shows. Same
260
- * arm/remove lifecycle as `pendingShadowedSeqs`.
261
- */
126
+ /** The seq of the `compaction/prune` event that armed `pendingShadowedSeqs`. The shadowed path rewrites that
127
+ * event's `tokens` from the gross shadow price to the NET freed amount, so the row matches the drop the trend chart shows. */
262
128
  pendingShadowEventSeq?: number;
263
- /**
264
- * The fold-derived file-operation log (the File Activity card's raw
265
- * material, shared/fileOps.ts): one record per executed file op, appended
266
- * in log order — at `tool/result` (the armed call's arguments + the
267
- * result's meta) and at a run_code result's flush of its nested
268
- * dispatches. Bounded by `maxFileOps`; the trim stamps `fileOpsFloor`.
269
- */
129
+ /** The fold-derived file-operation log: one record per executed file op, bounded by `maxFileOps`; the trim stamps `fileOpsFloor`. */
270
130
  fileOps: FileOpRecord[];
271
131
  /** The newest dropped op's seq (the card's coverage floor for the served op log). */
272
132
  fileOpsFloor?: number;
273
- /**
274
- * Nested Code-Mode ops buffered by their top run_code call id until the
275
- * parent's result folds (the dispatch events land BEFORE it, and the ops'
276
- * locate target is that result's seq). Flushed (and the key deleted) when
277
- * the result with that callId folds; absent until the first dispatch books
278
- * an op. Bounded by PENDING_CODE_OPS_MAX — a hostile log that never
279
- * settles a run_code cannot grow it.
280
- */
133
+ /** Nested Code-Mode ops buffered by their top run_code call id until the parent's result folds (the dispatch
134
+ * events land BEFORE it). Bounded by PENDING_CODE_OPS_MAX, so a hostile log that never settles a run_code cannot grow it. */
281
135
  pendingCodeOps?: Record<string, FileOpRecord[]>;
282
136
  }
283
137
  //#endregion
284
138
  //#region src/shared/types.d.ts
285
139
  declare module '@deepseek-ai/dsh-session-projection/types' {
286
140
  interface SessionProjectionMap {
287
- /**
288
- * The plugin's context timeline: current composition, counters, and the
289
- * headline anchor. Since the split generation the wire value is the SLIM
290
- * head (every delivery channel — session.list rows, control baselines,
291
- * push frames — carries it whole); the per-request history, context
292
- * events, and the surface/archive collections ride the on-demand detail
293
- * channel ({@link ContextTimelineDetail}, host/detail.ts). Channel-less
294
- * hosts keep the inline generation (the collections stay in the value).
295
- * Key absence = the plugin's host half is not composed.
296
- */
141
+ /** The plugin's timeline: slim head on the split generation, inline value on channel-less
142
+ * hosts; key absence = the host half is not composed. */
297
143
  contextTimeline: ContextTimeline;
298
- /**
299
- * The request-header CONTENT epochs (full system prompt + tool schemas)
300
- * behind the timeline's envelope figures. A separate unit so the hot
301
- * `contextTimeline` value stays lean: headers change rarely, so this
302
- * value (and its pushes) change only when a `request/header` lands.
303
- * The Context browser card reads it to show the actual prompt/schema
304
- * content of a picked step (key absence = older host: tokens only).
305
- */
144
+ /** Request-header content epochs — a separate unit so the hot timeline value stays lean;
145
+ * key absence = an older host (tokens only). */
306
146
  contextHeaders: ContextHeaders;
307
- /**
308
- * The per-day activity ledger (billed tokens + completed requests keyed
309
- * by local day) behind the Context Dashboard's heatmap — the timeline's
310
- * "current snapshot" cannot draw a per-day chart, so the overview reads
311
- * this off every session-list row's projection column. Tiny (one small
312
- * record per day, retention-capped), so riding every list row costs
313
- * nothing next to the timeline head. Key absence = older host: the
314
- * heatmap degrades to its empty note.
315
- */
147
+ /** The per-day activity ledger behind the Context Insights heatmap; key absence = an older host (empty heatmap). */
316
148
  contextActivity: ContextActivity;
317
149
  }
318
150
  interface SessionProjectionStateMap {
@@ -321,148 +153,88 @@ declare module '@deepseek-ai/dsh-session-projection/types' {
321
153
  contextActivity: ActivityState;
322
154
  }
323
155
  }
324
- /**
325
- * The priced surface buckets. `skill` carries every skill-machinery content
326
- * the harness injects (issue #66): the `<available_skills>` catalog digest,
327
- * a user-explicit `/name` invocation's instructions message, and the content
328
- * a `skill`-tool load returns (modeled as a tool result by the harness).
329
- */
156
+ /** The priced surface buckets. `skill` carries the skill-machinery content the harness injects:
157
+ * the `<available_skills>` digest, a `/name` invocation's instructions, and a `skill`-tool load. */
330
158
  type Category = 'user' | 'inject' | 'skill' | 'assistant' | 'tool';
331
- /**
332
- * One live system-prompt node (Snapshot.systems) — the harness models the
333
- * system prompt as a surface node, so its TEXT is fetched on demand from the
334
- * `system/message` event at `seq`. `tokens` is the node's heuristic
335
- * price (0 for a dormant empty node, which the harness reads as "no system
336
- * prompt"); the effective figure is the LAST node with `tokens > 0`.
337
- */
159
+ /** One live system-prompt node; the harness models the prompt as a surface node, so its TEXT is
160
+ * fetched from the `system/message` event at `seq`. The effective figure is the LAST node with `tokens > 0`. */
338
161
  interface SystemPromptNode {
339
162
  seq: number;
340
163
  time: number;
341
164
  tokens: number;
342
165
  }
343
- /**
344
- * The stats board's count figures, precomputed host-side over the RETAINED
345
- * request/event records (the same set the detail payload serves). Carried by
346
- * the split-generation wire head so the board — and the Agent card's
347
- * per-session request tally — never need the collections themselves.
348
- * `steps` doubles as the retained request-record count.
349
- */
166
+ /** Counts precomputed over the RETAINED request/event records; `steps` is the retained request-record count. */
350
167
  interface TimelineCounts {
351
168
  turns: number;
352
169
  steps: number;
353
170
  injects: number;
354
171
  compactions: number;
355
172
  prunes: number;
356
- /**
357
- * Distinct skills loaded this session — the unique names among the retained
358
- * inject events the fold tagged `sub: 'skill'` (a user-explicit `/name`
359
- * invocation's instructions or a `skill`-tool load's content). The
360
- * `<available_skills>` catalog digest is injected context without a name
361
- * and never counts. Additive-optional: a head served without it (a partial
362
- * record re-proved at the client's boundary) reads as zero.
363
- */
173
+ /** Distinct names among the retained events tagged `sub: 'skill'`; the `<available_skills>`
174
+ * digest has no name and never counts. Absent = 0. */
364
175
  skills?: number;
365
176
  }
366
- /**
367
- * The newest retained request record's billing summary — the headline's
368
- * derived-occupancy anchor (`prompt + surface movement since`), carried by
369
- * the split-generation wire head so the headline never needs the request
370
- * records themselves.
371
- */
177
+ /** The newest retained request record's billing summary. */
372
178
  interface TimelineLast {
373
179
  seq: number;
374
180
  total: number;
375
181
  prompt?: number;
376
182
  }
377
- /** One day's ledger entry in the `contextActivity` projection. */
378
183
  interface ActivityDay {
379
- /**
380
- * Billed tokens folded from provider-reported usage that day (prompt-side
381
- * input + cache read/write + output). Requests without a usage settlement
382
- * count only toward `requests` — a fabricated 0 never understates the day.
383
- */
184
+ /** Billed tokens from provider-reported usage that day (input + cache read/write + output); a
185
+ * settlement-less request counts only toward `requests`. */
384
186
  tokens: number;
385
187
  /** Completed model calls (assistant settlements) that day. */
386
188
  requests: number;
387
- /**
388
- * The day's billed buckets keyed provider → model → pricing period — the
389
- * same SessionCostUsage raw material the timeline's session-cost totals
390
- * carry, scoped to the day the requests INITIATED in, so the client prices
391
- * each day off the same model-price book as the KPI band. ADDITIVE-OPTIONAL:
392
- * absent on rows folded before the field existed (older plugin builds), on
393
- * days whose settlements predate any model header, and on settlements the
394
- * provider left unmetered — the usage chart degrades to tokens-only bars.
395
- */
189
+ /** The day's billed buckets keyed provider → model → pricing period, scoped to the day the
190
+ * requests INITIATED in. Absent = tokens-only bars. */
396
191
  cost?: SessionCostUsage;
397
- /**
398
- * The day's skill loads, skill name → its tally (`n` loads, `last` load
399
- * instant in epoch ms): both durable load gestures — a user-explicit
400
- * `/name` invocation's `skill-invocation` injection and a `skill`-tool
401
- * load's `<skill_content>` result. ADDITIVE-OPTIONAL: absent on rows folded
402
- * before the field existed and on days without a load — the Insights page's
403
- * skill card treats a missing record as an empty day.
404
- */
192
+ /** Skill name → `{ n: loads, last: last-load instant in epoch ms }`; absent = an empty day. */
405
193
  skills?: Record<string, {
406
194
  n: number;
407
195
  last: number;
408
196
  }>;
409
197
  }
410
- /**
411
- * The per-session daily activity ledger (`contextActivity` wire value):
412
- * day key (`YYYY-MM-DD`, host-local — see shared/days.ts) → that day's
413
- * billed volume, retention-capped by the fold. The Context Dashboard merges
414
- * every listed session's ledger into its activity heatmap.
415
- */
198
+ /** The per-session daily ledger: host-local day key (`YYYY-MM-DD`, shared/days.ts) → that day's
199
+ * billed volume; the Context Insights page merges every listed session's ledger. */
416
200
  interface ContextActivity {
417
201
  days: Record<string, ActivityDay>;
418
202
  }
419
- /**
420
- * One available skill's catalog metadata, served by the plugin's
421
- * `/api/dsh-context/skills` route (host/skills.ts) off the harness's skill
422
- * registry: the Insights page's skill card joins it by name onto the load
423
- * tallies to show what a skill IS (description, origin, on-disk path). Every
424
- * field but `name` is best-effort — a registry that omits one still serves
425
- * the rest, and the card renders the tally alone when the route is absent.
426
- */
203
+ /** One available skill's catalog metadata, served by `/api/dsh-context/skills` (host/skills.ts); every field but `name` is best-effort. */
427
204
  interface SkillInfo {
428
205
  /** Kebab-case identifier — the join key onto `ActivityDay.skills`. */
429
206
  name: string;
430
- /** The registry's routing description (may be empty). */
431
207
  description: string;
432
208
  /** Absolute instruction file path, when the provider is filesystem-backed. */
433
209
  path?: string;
434
210
  /** Discovery source bucket (e.g. 'project-agents', 'user-dsh', 'bundled'). */
435
211
  source?: string;
436
212
  }
437
- /**
438
- * The per-user display-preference vocabulary of the `dsh-context` entry —
439
- * the ONE declaration both halves share: the Host's entry Config schema
440
- * carries the fields as `.volatile()` preferences (host/config.ts — the
441
- * Config-form generation serves them live on the Plugins page), the Client
442
- * binds the form and edits fields by name (client/settings.ts). Type-only,
443
- * so both bundles erase it.
444
- */
213
+ /** The display-preference vocabulary of the `dsh-context` entry — the ONE declaration shared by
214
+ * the Host Config schema (host/config.ts) and the Client's settings form (client/settings.ts). */
445
215
  type DefaultGranularity = 'step' | 'turn';
446
216
  type DefaultTrendMode = 'total' | 'delta';
447
- /** File Activity row order: most operations first, most-recently-touched first, or path ascending. */
217
+ /** The browser delta baseline: against the immediately preceding record, or the previous turn's last step. */
218
+ type DefaultDeltaBase = 'step' | 'turn';
448
219
  type DefaultFileSort = 'count' | 'latest' | 'path';
449
- /** Tool-definition row order: largest schema first, most call hits first, or name ascending. */
450
220
  type DefaultToolSort = 'size' | 'count' | 'name';
451
- /** Where the Context view is offered: the conversation tab, the right Sidebar, or both. */
452
221
  type DefaultPlacement = 'all' | 'tab' | 'sidebar';
453
- /** Whether the Context Insights panel's sidebar entry is offered at all. */
454
222
  type InsightsEntry = 'show' | 'hide';
455
- /** Whether the trend chart mounts with the per-request duration curve overlaid. */
456
223
  type DefaultDurationCurve = 'show' | 'hide';
224
+ interface PluginSettings {
225
+ defaultPlacement: DefaultPlacement;
226
+ defaultGranularity: DefaultGranularity;
227
+ defaultTrendMode: DefaultTrendMode;
228
+ defaultDeltaBase: DefaultDeltaBase;
229
+ defaultToolSort: DefaultToolSort;
230
+ defaultFileSort: DefaultFileSort;
231
+ insightsEntry: InsightsEntry;
232
+ defaultDurationCurve: DefaultDurationCurve;
233
+ }
457
234
  interface Snapshot {
458
235
  ok: boolean;
459
- /**
460
- * The host's baseline-gate record, present ONLY when the running harness
461
- * is below the plugin's supported baseline: the host then folds nothing
462
- * and every figure in this snapshot is zero/empty. The client keeps
463
- * rendering the (blank) cards and pops the upgrade gate modal naming
464
- * `current` (the detected harness version) and `minimum` (the baseline).
465
- */
236
+ /** Present ONLY when the running harness is below the supported baseline: the host folds
237
+ * nothing (all figures zero) and the client shows the upgrade gate. */
466
238
  unsupported?: {
467
239
  current: string;
468
240
  minimum: string;
@@ -480,144 +252,57 @@ interface Snapshot {
480
252
  tool: number;
481
253
  total: number;
482
254
  };
483
- /**
484
- * Image blocks live in the CURRENT context (user uploads plus tool-result
485
- * images, nested blocks included) — the sum over the live surface nodes'
486
- * `imgs`, so compaction/prune shrink it. Absent from older hosts; clients
487
- * treat absence as zero.
488
- */
255
+ /** Image blocks in the CURRENT context (uploads plus nested tool-result images), summed over live surface nodes. Absent = 0. */
489
256
  images?: number;
490
- /**
491
- * Tool calls whose result is live in the CURRENT context (one `tool/result`
492
- * folds to one `tool` surface node). Calls still in flight and results
493
- * compacted/pruned out of the surface are not counted. Absent from older
494
- * hosts; clients treat absence as zero.
495
- */
257
+ /** Tool calls whose result is live (a `skill`-tool node keeps counting); in-flight and
258
+ * compacted/pruned-out calls are excluded. Absent = 0. */
496
259
  toolCalls?: number;
497
- /**
498
- * Whole-session human-input tally: every non-injection `user/message`
499
- * (the user's own messages) plus every answered `ask_user_question`
500
- * result (one per answer submission). A running total over the COMPLETE
501
- * log — turns the retained window no longer holds still count. Absent
502
- * from older hosts; clients treat absence as zero.
503
- */
260
+ /** Whole-session tally of non-injection `user/message` plus answered `ask_user_question` — a
261
+ * running total over the COMPLETE log. Absent = 0. */
504
262
  humanInputs?: number;
505
- /**
506
- * Whole-session answers tally: one per assistant message carrying a
507
- * non-blank text block — steps that only dispatched tool calls are not
508
- * replies. Running total over the complete log, like `humanInputs`.
509
- * Additive-optional — absent from rows folded before the field existed;
510
- * clients treat absence as zero.
511
- */
263
+ /** Whole-session replies: one per assistant message carrying non-blank text (tool-only steps are not replies). Absent = 0. */
512
264
  answers?: number;
513
- /**
514
- * The user's newest own message as a one-line bounded preview (first text
515
- * block, whitespace collapsed, ~80 chars): the session cards' footer line.
516
- * Additive-optional — absent from rows folded before the field existed
517
- * (older hosts, idle sessions' cached rows); clients hide the line then.
518
- */
265
+ /** The newest own message as a one-line preview (first text block, whitespace collapsed, 80 chars max). Absent = hidden. */
519
266
  lastUser?: string;
520
- /**
521
- * Split-generation head fields — present exactly when the host serves the
522
- * SLIM head (the heavy collections moved to the on-demand detail channel,
523
- * host/detail.ts) and absent on the inline generation (older or
524
- * channel-less hosts serve the collections in place). `detailRev` is the
525
- * detail's revision marker: it bumps whenever the detail collections
526
- * change, so an open tab refetches on the push alone.
527
- */
267
+ /** Split-generation head fields — present exactly when the host serves the slim head;
268
+ * `detailRev` bumps whenever the detail collections change. */
528
269
  counts?: TimelineCounts;
529
270
  last?: TimelineLast;
530
271
  detailRev?: number;
531
- /**
532
- * The per-request history / context-event collections. On the split
533
- * generation these stay ABSENT from the wire value (every session.list row,
534
- * control baseline, and push frame would carry them whole otherwise); the
535
- * client fills them from the detail channel ({@link ContextTimelineDetail}).
536
- */
272
+ /** The slim head serves these EMPTY (the collections ride the detail channel); the client swaps in the detail payload's collections. */
537
273
  requests: RequestRecord[];
538
274
  events: ContextEventRecord[];
539
- /**
540
- * Cumulative session-cost raw material (per-provider, per-model billed
541
- * token totals — see SessionCostUsage). Absent until a request with a
542
- * known model reports usage.
543
- */
275
+ /** Cumulative session-cost raw material (SessionCostUsage); absent until a request with a known model reports usage. */
544
276
  cost?: SessionCostUsage;
545
- /**
546
- * Whole-session timing totals (see TimingTotals). Absent until the first
547
- * step lifecycle completes in the log (older plugin builds never folded
548
- * one — clients treat absence as an empty timing card).
549
- */
277
+ /** Whole-session timing totals (TimingTotals). Absent until the first step lifecycle completes. */
550
278
  timing?: TimingTotals;
551
- /**
552
- * The live system-prompt nodes, oldest first — the browser's per-step source
553
- * for the System section. Absent on rows folded before this field existed
554
- * (older plugin builds; the client then falls back to the header epoch's
555
- * own `systemTokens`, the legacy wire shape those builds served).
556
- */
279
+ /** The live system-prompt nodes, oldest first. Absent on rows folded before the field existed
280
+ * — the client then reads the header epoch's `systemTokens`. */
557
281
  systems?: SystemPromptNode[];
558
- /**
559
- * The served live surface: the newest `maxNodes` tail PLUS every live inject node older than the tail (injections land first and are
560
- * few,
561
- * so they are pinned). Seq-ordered, oldest first.
562
- */
282
+ /** The served live surface: the newest `maxNodes` tail PLUS every live inject/skill node older
283
+ * than the tail (they land first and are pinned). Seq-ordered, oldest first. */
563
284
  nodes: SurfaceNode[];
564
- /** Live nodes not served (the overflow beyond `maxNodes`, minus pinned injects — see `nodes`). */
285
+ /** Live nodes not served: the overflow beyond `maxNodes` minus the pinned inject/skill nodes. */
565
286
  droppedNodes: number;
566
- /**
567
- * Recently REMOVED surface nodes (compaction/prune shadows), each stamped
568
- * with `gone` (the replacing event's seq). Together with `nodes` this lets
569
- * the Context browser reconstruct the assembled surface of any retained
570
- * step: alive at request R = seq < R.seq && (gone undefined || gone > R.seq).
571
- */
287
+ /** Recently REMOVED surface nodes (compaction/prune shadows) stamped with `gone` (the replacing
288
+ * event's seq); a node belongs to request R when `seq < R.seq && (gone undefined || gone > R.seq)`. */
572
289
  archive: SurfaceNode[];
573
- /**
574
- * Coverage floor of the served live `nodes`: the newest seq among the
575
- * `droppedNodes` live nodes not served. Present only when droppedNodes > 0.
576
- */
290
+ /** Newest seq among the `droppedNodes` nodes not served; present only when droppedNodes > 0. */
577
291
  surfaceFloor?: number;
578
- /**
579
- * Coverage floor of `archive`: the newest `gone` among archive entries the
580
- * retention bounds dropped. Steps with seq < archiveFloor may miss removed
581
- * nodes (the browser shows the reconstruction as approximate).
582
- */
292
+ /** Newest `gone` among retention-dropped archive entries; steps below it may miss removed nodes. */
583
293
  archiveFloor?: number;
584
- /**
585
- * The fold-derived file-operation log and its trim floor — present on the
586
- * INLINE wire value (channel-less hosts) and on the detail payload
587
- * (ContextTimelineDetail), absent from the slim head (they ride the detail
588
- * channel there).
589
- */
294
+ /** The fold-derived file-op log and trim floor: the INLINE value and detail payload, never the slim head. */
590
295
  fileOps?: FileOpRecord[];
591
296
  fileOpsFloor?: number;
592
- /**
593
- * The timing strip's painted spans (see TimingSpan) — present on the INLINE
594
- * wire value and on the detail payload, absent from the slim head (they ride
595
- * the detail channel there) and from rows folded before the collection
596
- * existed (older hosts; the client then shows no strip).
597
- */
297
+ /** The timing strip's painted spans: INLINE value and detail payload only; absent on rows folded before the collection existed. */
598
298
  spans?: TimingSpan[];
599
299
  }
600
- /**
601
- * The on-demand DETAIL payload of the split `contextTimeline` generation —
602
- * the heavy collections (per-request records, context events, the served
603
- * surface window, and the removed-node archive) that the slim wire head no
604
- * longer carries through every delivery channel. The host serves it off the
605
- * live fold state at the `/dsh-context` `detail` endpoint (host/detail.ts);
606
- * `rev` mirrors the head's `detailRev` at build time and acts as the
607
- * client's latest-wins cursor.
608
- */
300
+ /** The on-demand DETAIL payload of the split generation: the heavy collections the slim head no
301
+ * longer carries (host/detail.ts). `rev` mirrors the head's `detailRev` and is the client's latest-wins cursor. */
609
302
  interface ContextTimelineDetail {
610
303
  rev: number;
611
- /**
612
- * The slim wire head at the SAME fold cut as the collections: the
613
- * composition scalars (`current`), the window/model envelope, and the
614
- * precomputed counts. Sessions listed cold (never attached since the
615
- * requesting unit last changed) carry no `contextTimeline` projection row
616
- * for the browser's list reads, so the Agent network card fetches this
617
- * head per node to render their composition rings. The host always serves
618
- * it; optional so a payload missing it still serves the collections (the
619
- * detail cards) and only the ring composition degrades.
620
- */
304
+ /** The slim head at the SAME fold cut as the collections — a session listed cold has no
305
+ * `contextTimeline` row, so the Agent network ring reads this. Optional. */
621
306
  head?: ContextTimeline;
622
307
  requests: RequestRecord[];
623
308
  events: ContextEventRecord[];
@@ -626,37 +311,16 @@ interface ContextTimelineDetail {
626
311
  archive: SurfaceNode[];
627
312
  surfaceFloor?: number;
628
313
  archiveFloor?: number;
629
- /**
630
- * The fold-derived file-operation log (shared/fileOps.ts): one record per
631
- * executed file op, newest-retained, covering the full log (never
632
- * window-bound like the client-side join derivation it replaces on this
633
- * generation). Code-Mode nested dispatches book ops located on their
634
- * parent run_code result (`parent`).
635
- */
314
+ /** The file-op log (shared/fileOps.ts) over the FULL log; Code-Mode nested dispatches book on their parent run_code result (`parent`). */
636
315
  fileOps?: FileOpRecord[];
637
316
  /** The newest dropped op's seq when the op log trimmed (coverage honesty, same family as archiveFloor). */
638
317
  fileOpsFloor?: number;
639
- /**
640
- * The timing strip's painted spans (see TimingSpan): the completed steps'
641
- * time slices in log order, positioned by their real instants. Additive-
642
- * optional: a payload folded before the collection existed (older hosts)
643
- * serves without it, and the timing card shows no strip then.
644
- */
318
+ /** The completed steps' time slices in log order, positioned by their real instants. Absent = no strip. */
645
319
  spans?: TimingSpan[];
646
320
  }
647
- /**
648
- * One executed file operation (a settled file-tool call with a resolved
649
- * target), folded host-side from the durable tool lifecycle: the call's
650
- * name+arguments (`tool/call`), the result's presentation meta and error
651
- * (`tool/result`), or a nested Code-Mode settle (`tool/ptc-dispatch`,
652
- * located on its parent run_code result via `parent` + `program`).
653
- *
654
- * `gone` is NOT host-stamped: the client joins it from the detail's archive
655
- * at render time (the op's result node leaving the live surface marks where
656
- * its content is still viewable). Line deltas are estimates read off the
657
- * call ARGUMENTS (an edit's old/new strings, a write's content), never off
658
- * result payloads.
659
- */
321
+ /** One executed file operation, folded host-side from the durable tool lifecycle (`tool/call` +
322
+ * `tool/result`, or a nested `tool/ptc-dispatch`); line deltas are estimates read off the call
323
+ * ARGUMENTS. `gone` is client-joined, not host-stamped. */
660
324
  interface FileOpRecord {
661
325
  seq: number;
662
326
  /** The op's file; for a pathless search the searched PATTERN (`pattern: true`). */
@@ -667,7 +331,7 @@ interface FileOpRecord {
667
331
  err: boolean;
668
332
  added: number;
669
333
  removed: number;
670
- /** What was searched for, when a search named both a path and a pattern. */
334
+ /** The searched pattern, with the include filter when one narrowed the call. */
671
335
  detail?: string;
672
336
  /** Meta-attributed search op only: matched lines the result reported for this file. */
673
337
  hits?: number;
@@ -688,143 +352,71 @@ interface FileOpRecord {
688
352
  /** Client-joined archive stamp (see the type note); absent on the wire. */
689
353
  gone?: number;
690
354
  }
691
- /**
692
- * The `contextTimeline` projection's whole value — the same snapshot the Client has always rendered. `ok` is always `true` here (a
693
- * delivered projection is by definition available); kept for wire compatibility with the snapshot shape.
694
- */
355
+ /** The `contextTimeline` projection's whole value; `ok` is always `true` here (kept for wire compatibility). */
695
356
  type ContextTimeline = Snapshot;
696
- /**
697
- * One painted span of the timing card's session-time strip (the card's bottom
698
- * band): a completed step's time slice — the TTFT wait, one decode block (in
699
- * stream order), one tool-run window, or an in-step residue gap — folded
700
- * host-side at `step/end`. The instants are the slice's REAL occurrence times
701
- * (the hover tip reads them); the strip packs the spans gapless in log order,
702
- * each band's width its share of the session's cumulative active time (idle
703
- * time BETWEEN steps carries no span and takes no track). `end` > `start`
704
- * always (the fold drops zero/negative slices).
705
- */
357
+ /** One painted span of the timing card's session-time strip (a step's TTFT wait, decode block,
358
+ * tool-run window, or residue gap). `start`/`end` are the slice's REAL instants; the strip packs
359
+ * them gapless in log order, and `end` > `start` always. */
706
360
  interface TimingSpan {
707
- /** The slice kind — the timing card's own slice vocabulary (same keys, same colors). */
361
+ /** The timing card's own slice vocabulary (same keys, same colors). */
708
362
  kind: 'ttft' | 'reasoning' | 'text' | 'toolarg' | 'tools' | 'other';
709
363
  start: number;
710
364
  end: number;
711
365
  }
712
- /**
713
- * Cumulative billed-token totals for one pricing bucket of the session-cost
714
- * estimate (host-folded, never trimmed — running totals over the COMPLETE
715
- * session log, immune to the request/event retention bounds).
716
- */
366
+ /** One pricing bucket's cumulative billed tokens: a running total over the COMPLETE session log, never trimmed. */
717
367
  interface CostBucketTotals {
718
368
  uncached: number;
719
369
  cacheRead: number;
720
370
  cacheWrite: number;
721
371
  output: number;
722
372
  }
723
- /**
724
- * One completed tool name's whole-session call tally behind the timing
725
- * card's top-tools ranking (running totals, never trimmed).
726
- */
727
373
  interface ToolTimingTotals {
728
374
  calls: number;
729
375
  ms: number;
730
376
  }
731
377
  /**
732
- * Whole-session timing totals, host-folded from the durable `step/start` /
733
- * `step/end` / `tool/call` / `tool/result` lifecycle plus the model call's
734
- * first token (running totals over the COMPLETE session log — the same
735
- * never-trimmed framing as `cost`). The first token comes from the call's
736
- * own embedded stream (`assistant/message.data.stream` /
737
- * `assistant/attempt.data.stream`), matching the harness's own
738
- * session-stats fold. Durations are wall-clock milliseconds: `wallMs` sums
739
- * whole steps, `ttftMs` the step-start → decode-start slice (the model wait)
740
- * and `genMs` the decode-start → assistant-message slice (the generation) —
741
- * both only over calls whose stream carried a token delta, `toolsMs` the sum
742
- * of per-call tool durations (parallel calls each count, so it can overlap).
743
- * The decode window opens at the first OBSERVABLE instant — the first token,
744
- * or an earlier `block-start` marker the token packing could not stamp (a
745
- * redacted reasoning block leaves no chunk behind): anchoring at the token
746
- * would charge the marker-tiled window to the wait as well, double-counting
747
- * it past 100% in the legend. Absent until the first step lifecycle
748
- * completes in the log.
378
+ * Whole-session timing totals, host-folded from the durable step/tool lifecycle plus the call's
379
+ * first token (its embedded `assistant/message.data.stream`); running totals over the COMPLETE log.
749
380
  *
750
- * The generation window itself splits by WHAT was being decoded, off the
751
- * stream's `block-start` framing (`blockType`): `reasoningMs` (the model's
752
- * thinking), `textMs` (the answer text), and `toolArgMs` (the tool-call
753
- * arguments). Each marker owns the interval up to the next one (the last one
754
- * up to the assistant message), so the three tile the marker span — exactly
755
- * `genMs` when the first marker leads the first token (the window opens
756
- * there); when the first marker sits marginally PAST the token the lead-in
757
- * gap stays inside `genMs` unattributed, so the buckets never exceed it.
758
- * They are ADDITIVE-OPTIONAL: cached projection rows written before
759
- * the split carry `genMs` without them, so the card falls back to the
760
- * un-split shape instead of the cache row being discarded (the
761
- * stateVersion-15 rationale in host/timeline.ts).
381
+ * Milliseconds: `wallMs` sums whole steps; `ttftMs` the step-start → decode-start slice and `genMs`
382
+ * the decode-start → assistant-message slice (only over calls whose stream carried a token delta);
383
+ * `toolsMs` sums per-call tool durations (parallel calls each count). The decode window opens at the
384
+ * first OBSERVABLE instant — the first token, or an earlier `block-start` marker (a redacted
385
+ * reasoning block leaves no chunk): anchoring at the token would double-count the marker-tiled
386
+ * window past 100%.
387
+ *
388
+ * The generation split comes off the stream's `block-start` framing (`blockType`): `reasoningMs` /
389
+ * `textMs` / `toolArgMs` and their block counts, each marker owning the interval to the next.
390
+ * `speedTokens`/`speedMs` are the harness-parity throughput seat (first-token → assistant-message,
391
+ * calls carrying both); `speedMs` KEEPS the first-token anchor. `calls` counts folded assistant
392
+ * messages, `toolCalls` folded call/result pairs.
762
393
  */
763
394
  interface TimingTotals {
764
- /** Summed wall time of completed steps (the session's active time). */
765
395
  wallMs: number;
766
- /** Summed step-start → decode-start time (the model wait, TTFT). */
767
396
  ttftMs: number;
768
- /** Summed decode-start → assistant-message time (the generation). */
769
397
  genMs: number;
770
- /** Reasoning-decode slice of `genMs` (the model's thinking). */
771
398
  reasoningMs?: number;
772
- /** Counted reasoning-decode blocks (the Thinking slice's tally). */
773
399
  reasoningBlocks?: number;
774
- /** Answer-text decode slice of `genMs`. */
775
400
  textMs?: number;
776
- /** Counted answer-text decode blocks (the Answer slice's tally). */
777
401
  textBlocks?: number;
778
- /** Tool-call-argument decode slice of `genMs`. */
779
402
  toolArgMs?: number;
780
- /**
781
- * Counted tool-call-argument decode blocks — one per tool call whose
782
- * arguments the stream decoded (the Tool args slice's tally). The counts are
783
- * ADDITIVE-OPTIONAL like their spans: a row cached before they existed (or a
784
- * call whose stream framed no blocks) carries none, and the card qualifies
785
- * only what was actually counted.
786
- */
787
403
  toolArgBlocks?: number;
788
- /**
789
- * The decode-throughput seat, paired exactly as the harness's own
790
- * session-stats fold pairs them: `speedTokens` sums provider-reported
791
- * output tokens and `speedMs` the first-token → assistant-message
792
- * windows, over the calls that carried BOTH a first-token stamp and a
793
- * usage report — a subset of `genMs`, which counts every token-stamped
794
- * call regardless of usage. `speedMs` KEEPS the first-token anchor (not
795
- * the marker-aware decode start): it is the harness-parity figure.
796
- * Additive-optional: cached rows written before
797
- * the seat existed lack them, and the card falls back to no chip.
798
- */
799
404
  speedTokens?: number;
800
405
  speedMs?: number;
801
- /** Completed model calls (assistant messages folded). */
802
406
  calls: number;
803
- /** Summed per-call durations of completed tool calls. */
804
407
  toolsMs: number;
805
- /** Completed tool calls (call/result pairs folded). */
806
408
  toolCalls: number;
807
409
  /** Per-tool-name tallies behind the timing card's ranking (bounded). */
808
410
  tools: Record<string, ToolTimingTotals>;
809
411
  }
810
- /**
811
- * One billed model's cumulative totals split by pricing period. Providers
812
- * without period-based pricing book everything under `peak` (the list-price
813
- * period); DeepSeek splits at fold time — peak windows bill at list price,
814
- * off-peak (all other hours) at half.
815
- */
412
+ /** One billed model's totals split by pricing period: providers without period pricing book
413
+ * everything under `peak`; DeepSeek bills `peak` at list price and `off` at half. */
816
414
  interface CostModelUsage {
817
415
  peak?: CostBucketTotals;
818
416
  off?: CostBucketTotals;
819
417
  }
820
- /**
821
- * The session-cost estimate's raw material: cumulative provider-reported
822
- * billed-token totals, keyed by the request envelope's DSH provider id (''
823
- * when a log carries none) and then by its model id — the exact (provider,
824
- * model) faces the Client's model-price book resolves (the models.dev
825
- * registry, client/modelPrices.ts). Running totals per key; absent until a
826
- * request with a known model reports usage.
827
- */
418
+ /** The session-cost raw material: cumulative provider-reported billed tokens keyed by the request
419
+ * envelope's DSH provider id ('' when none) then model id — the faces the Client's models.dev price book resolves. */
828
420
  interface SessionCostUsage {
829
421
  [provider: string]: {
830
422
  [model: string]: CostModelUsage;
@@ -838,21 +430,11 @@ interface SurfaceNode {
838
430
  tokens: number;
839
431
  /** Image blocks inside this node's message (absent when zero). */
840
432
  imgs?: number;
841
- /**
842
- * Removal marker, present only on `archive` entries: the seq of the
843
- * replacement surface event that shadowed this node (compaction/prune).
844
- * The node is part of the assembled context of every request with
845
- * seq > this node.seq and seq < gone.
846
- */
433
+ /** Set only on `archive` entries: the seq of the replacement event that shadowed this node. */
847
434
  gone?: number;
848
435
  form?: string;
849
- /**
850
- * The producer identity the matching inject event names (host pricing.ts
851
- * `injectionSourceName`: the plugin id, the reconciled instruction files,
852
- * or the durable kind). Stamped on injection nodes alongside the event, so
853
- * the browser rows label them the way the events card does; absent when the
854
- * source carries no readable identity or the node predates the stamp.
855
- */
436
+ /** The producer identity the matching inject event names (host pricing.ts
437
+ * `injectionSourceName`); absent when unreadable or predating the stamp. */
856
438
  name?: string;
857
439
  text?: string;
858
440
  tool?: string;
@@ -874,42 +456,21 @@ interface RequestRecord {
874
456
  tool: number;
875
457
  total: number;
876
458
  prompt?: number;
877
- /**
878
- * Skill-machinery tokens of this request (the `skill` composition
879
- * category — catalog digests, invocation instructions, `skill`-tool
880
- * loads). Always written by the current fold; absent on rows folded
881
- * before the category existed (read as 0).
882
- */
459
+ /** Skill-machinery tokens of this request (the `skill` composition category); absent on rows
460
+ * folded before the category existed (read as 0). */
883
461
  skill?: number;
884
- /**
885
- * Billed cache-read (served) prompt tokens of this request — the
886
- * hit-rate numerator against `prompt` (input + cacheRead + cacheWrite).
887
- * Absent on older hosts / usage-less requests; zero is a real value.
888
- */
462
+ /** Billed cache-read (served) prompt tokens — the hit-rate numerator against `prompt`; absent
463
+ * on older hosts, and zero is a real value. */
889
464
  cacheRead?: number;
890
465
  output?: number;
891
- /**
892
- * The request's step ACTIVE milliseconds (`step/start` → `step/end`: the
893
- * model call plus its tool runs) minus the in-step user waits — approval
894
- * decisions and `ask_user_question` answer windows — the trend chart's
895
- * duration overlay. Stamped at `step/end` onto the step's committed record;
896
- * absent while that step is still open (the live tail) or when the step
897
- * closed without one (an unpaired/hostile log).
898
- */
466
+ /** The step's ACTIVE ms (`step/start` → `step/end` minus in-step user waits: approval decisions
467
+ * and `ask_user_question`), stamped at `step/end`; absent while the step is open. */
899
468
  activeMs?: number;
900
- /**
901
- * Turn-mode aggregate marker, set by the Client's aggregateByTurn (one bar
902
- * per turn shows its LAST step's record). The Host never sets it.
903
- */
469
+ /** Turn-mode aggregate marker, set by the Client's aggregateByTurn (the Host never sets it). */
904
470
  stepCount?: number;
905
- /**
906
- * Delta-mode signed net change, set by the Client's deltaOf (only present
907
- * on the delta-transformed records the TrendChart plots). The Host never
908
- * sets it.
909
- */
471
+ /** Delta-mode signed net change, set by the Client's deltaOf (the Host never sets it). */
910
472
  net?: number;
911
473
  }
912
- /** A notable context event (compaction, prune, injection, model switch). */
913
474
  interface ContextEventRecord {
914
475
  seq: number;
915
476
  time: number;
@@ -919,7 +480,6 @@ interface ContextEventRecord {
919
480
  count?: number;
920
481
  sub?: string;
921
482
  name?: string;
922
- /** One-line producer account (notice-form summary), shown after the name. */
923
483
  detail?: string;
924
484
  from?: string;
925
485
  to?: string;
@@ -934,26 +494,13 @@ interface ContextEventRecord {
934
494
  interface HeaderTool {
935
495
  name: string;
936
496
  tokens: number;
937
- /**
938
- * The registering plugin's label, when attribution is known: the host's
939
- * best-effort attribution (`mcp:<server>` for MCP tools, or the pinned
940
- * first-party package map), or a `plugin` field carried by the raw header
941
- * entry — no supported-baseline harness path writes one, but the read
942
- * stays defensive for foreign/newer producers. `UNKNOWN_TOOL_SOURCE` marks
943
- * a tool whose provider predates the attribution hook; absent means
944
- * nothing is known and the browser shows no tag.
945
- */
497
+ /** The registering plugin's label when known: `mcp:<server>` for MCP tools, or the pinned
498
+ * first-party map; `UNKNOWN_TOOL_SOURCE` marks a boot-time tool, absent = no tag. */
946
499
  plugin?: string;
947
500
  }
948
- /**
949
- * One request-header epoch's METADATA: the epoch boundaries and token prices
950
- * in force from this event's seq until the next epoch. The epoch CONTENT
951
- * (full system prompt text, tool descriptions/schemas) is not projected —
952
- * every session.list row, control baseline, push frame, and projection-cache
953
- * checkpoint would otherwise carry it per session × epoch. The client
954
- * fetches one epoch's content on demand (a seq-anchored history read off
955
- * `seq`) as a {@link HeaderEpochContent}.
956
- */
501
+ /** One request-header epoch's METADATA (epoch boundaries and token prices, in force from this seq
502
+ * until the next). The CONTENT is deliberately not projected — every list row and push frame would
503
+ * carry it — and is fetched on demand as a {@link HeaderEpochContent}. */
957
504
  interface HeaderRecord {
958
505
  seq: number;
959
506
  time: number;
@@ -969,61 +516,34 @@ interface ContextHeaders {
969
516
  interface PlatformBalanceEntry {
970
517
  /** The ISO code the platform reported (`CNY` / `USD`). */
971
518
  currency: string;
972
- /** Total available funds: `granted` + `toppedUp`. Derived here rather than read
973
- * from the platform's own `total_balance`, which rounds independently of its
974
- * parts and can land a cent away from what the breakdown beside it shows. */
519
+ /** `granted` + `toppedUp`, derived rather than read from the platform's `total_balance`, which rounds independently of its parts. */
975
520
  total: number;
976
521
  /** The not-expired granted (gift) balance. */
977
522
  granted: number;
978
- /** The topped-up balance. */
979
523
  toppedUp: number;
980
524
  }
981
- /**
982
- * The DeepSeek open-platform balance, served by the plugin's
983
- * `/api/dsh-context/balance` fetch route (host/balance.ts). `null` on the
984
- * wire — and nothing rendered client-side — whenever the platform is not
985
- * configured or the read fails: the capsule only ever shows a live figure.
986
- */
525
+ /** The DeepSeek open-platform balance, served by `/api/dsh-context/balance`; `null` on the wire
526
+ * (nothing rendered) when unconfigured or the read fails. */
987
527
  interface PlatformBalance {
988
- /** Whether the platform reports the balance sufficient for API calls. */
989
528
  isAvailable: boolean;
990
529
  /** One entry per currency the account holds; at least one. */
991
530
  balances: PlatformBalanceEntry[];
992
531
  }
993
532
  //#endregion
994
533
  //#region src/host/config.d.ts
995
- export interface Config {
996
- /** Cap on kept per-step request records (the hard step backstop). */
997
- maxRequestSteps?: number;
534
+ declare const DEFAULT_BOUNDS: {
535
+ maxRequestSteps: number;
998
536
  /** Newest whole-turn window kept; trimming crosses whole turns, never mid-turn. */
999
- maxKeptTurns?: number;
1000
- maxEvents?: number;
1001
- /**
1002
- * Served surface nodes (newest carry the signal; live inject nodes are pinned — they land first and are few). Deliberately generous:
1003
- * auto-compaction keeps healthy surfaces far below it, so the browser effectively lists every live node; the bound is a
1004
- * pathological-session backstop (each push ships the whole value, ~150B/node).
1005
- */
1006
- maxNodes?: number;
1007
- /** Removed (shadowed) surface nodes kept for per-step reconstruction. */
1008
- maxArchiveNodes?: number;
1009
- /** Fold-derived file-operation records kept (the File Activity card's raw material). */
1010
- maxFileOps?: number;
1011
- /** Where the Context view is offered (the conversation tab, the right Sidebar, or both). */
1012
- defaultPlacement?: DefaultPlacement;
1013
- /** Timeline default granularity (per step or per turn). */
1014
- defaultGranularity?: DefaultGranularity;
1015
- /** Timeline default trend mode (total or delta). */
1016
- defaultTrendMode?: DefaultTrendMode;
1017
- /** Tool-definition row default order. */
1018
- defaultToolSort?: DefaultToolSort;
1019
- /** File Activity row default order. */
1020
- defaultFileSort?: DefaultFileSort;
1021
- /** Whether the Context Insights panel's sidebar entry is offered. */
1022
- insightsEntry?: InsightsEntry;
1023
- /** Whether the trend chart mounts with the duration curve overlaid. */
1024
- defaultDurationCurve?: DefaultDurationCurve;
1025
- }
1026
- /** The cordis `Config` validator and the Config-form generation's served schema. */
537
+ maxKeptTurns: number;
538
+ maxEvents: number;
539
+ /** Pathological-session backstop only: each push ships the whole value (~150B/node). */
540
+ maxNodes: number;
541
+ maxArchiveNodes: number;
542
+ maxFileOps: number;
543
+ };
544
+ /** The resolved fold bounds; derived from {@link DEFAULT_BOUNDS} so the two cannot drift. */
545
+ type FoldBounds = typeof DEFAULT_BOUNDS;
546
+ export type Config = Partial<FoldBounds> & Partial<PluginSettings>;
1027
547
  export declare const Config: z$1<Schemastery.ObjectS<NoInfer<{
1028
548
  maxRequestSteps: z$1<number, number, "defined">;
1029
549
  maxKeptTurns: z$1<number, number, "defined">;