Files
deepseek-harness/packages/session/session-stats
imccyu 92f9e79fac perf(session-projection): serve session-list rows from a summary key set
A Session-list row no longer carries every registered client view: the three
units whose values a list row never renders (`turnOutline`, `sessionStats`,
and `todos`) are excluded, which removes the whole-log turn outline and the
figure and todo payloads from every row. An opened Session is unchanged —
`session/follow`, the `session/control` baseline, and `session.projections`
still serve every projected key.

The exclusion is one type-level mark plus one registration interlock:
`SessionProjectionSummaryExcluded<T>` marks a map entry, and
`ProjectionDefinition.wire` requires `summary: false` exactly on a marked key
and rejects it everywhere else, so the mark and the declaration cannot drift.
A nullable entry keeps `null` outside the wrapper, so `todos` stays
`TodoItem[] | null`. `summaryKeys()` returns the derived set, stable until a
registration or its disposal changes the key table, and the existing
key-taking read faces (`cachedSnapshot`, `viewCheckpoint`) accept that set
directly, so summaries need no separate read face and no per-read key copy.
`packages/api/session-controller/src/list.ts` passes the set to the live and
cold cached faces; `cachedPredecessorTitle` is untouched.

Client types only: `SessionSummary.projectionValues` narrows to
`SessionProjectionSummaryValues`, `SessionProjectionSnapshot.values` keeps
every view value with the mark stripped, and no wire field or client name
changed. The cache, subsystem page, and package README document the summary
rule and the two read faces.
2026-10-18 22:13:18 +08:00
..
2026-10-17 15:10:31 +08:00

description, kind
description kind
Whole-log conversation counts and wall times for clients and maintainers choosing, composing, or debugging the sessionStats projection unit. package-reference

@deepseek-ai/dsh-session-stats

English | 中文

Summary

This package gives clients whole-session turn and step counts plus LLM, tool, first-token, and decode wall times through the public sessionStats value. The figures come from the complete durable log, so paging and compaction do not change them. Use it when a client must display consistent conversation statistics across reloads and reduced history. When whole-session statistics are unavailable, clients can use window-scoped counting instead.

Table of Contents


Use this package

Mount the plugin beside the session store and the projection registry when clients should display whole-session conversation figures that survive paging and compaction. The unit registers only when the registry is present.

Composition

- name: '@deepseek-ai/dsh-session'
- name: '@deepseek-ai/dsh-session-projection'
- name: '@deepseek-ai/dsh-session-stats'

What the figures mean

Field Meaning
turns Distinct turns with at least one closed step; rejected or empty turns are uncounted
steps Closed steps — completed, failed, cancelled, and max-tokens steps all count
llmMs Summed model wall time over steps that assembled a message
toolMs Summed matched tool/call → tool/result wall time
ttftMs / ttftSteps Summed first-token latency and the steps carrying it
decodeMs / decodeTokens Summed decode wall time and provider output tokens over usage-reporting steps

Every field is 0 until its first contributing event; the composed registry always serves the key, so clients read the value rather than key presence. Clients render whole-log figures through the projection seam's snapshot and change feed; the reference consumer is the web chat stats strip, whose window fold mirrors these field names as its no-unit fallback.

Failures and recovery

The unit is inert without the projection registry: inject keeps the fiber pending and nothing registers, so other assemblies lack the sessionStats key. Unmounting the plugin removes the key, because registrations are effects on the mounting fiber. A crash-interrupted step counts after the session reloads, when crash recovery appends its synthetic step/end.


Understand the implementation

Implementation internals — click to expand

This section explains the fold behind the figures; the observable behavior is fully covered in Use this package.

Design concept

The unit is a pure fold over committed session events: step/end is the counted step event because the agent loop appends exactly one per entered step in a finally, so completed, failed, cancelled, and max-tokens steps all land one. Counting assembled assistant messages instead would overcount max-tokens usage-host messages (empty content, excluded from the surface) and undercount cancelled steps (aborted before the message assembles). The wall-time folds mirror the client window fold field by field.

Source map

File Role
src/index.ts Plugin entry: inject, unit registration on the mounting fiber
src/projection.ts The fold: state shape, per-event transitions, wire view
src/types.ts One home of the sessionStats projection-key declaration and field types

Data model

The fold state holds the eight totals plus in-flight boundaries: lastTurn (turn of the last counted step/end), openStep (the open step's boundary facts, closed by its assistant/message), and pendingCalls (tool dispatch times by callId). The wire view is a strict subset — the eight totals — so the persisted-cache state schema extends the view schema with the boundary fields.

Fold rules

  • Uninteresting events return the same state reference; the registry's Object.is gate keeps the change feed quiet.
  • First-token latency records the first non-empty delta chunk and survives an in-step llm/retry.
  • Decode time and tokens accrue only over steps carrying both a first token and a valid provider usage report; malformed usage is ignored like the window fold guards node usage.
  • Tool time pairs tool/call → tool/result by callId; unresolved calls are dropped at turn/end because results land within their turn, and a callId colliding with an Object prototype name reads as unmatched.

Further Exploration

Read these pages when the unit's contract is not enough. They move from the registry that drives units to adjacent session packages.


Model Experience

None, as the sessionStats unit folds already-logged step boundaries into a client-facing read model and registers nothing model-facing.

KV Cache effect

None; the package never assembles or sends provider requests.

Known Limitations and Deferred Work

These limits define what the figures describe and when the unit is absent. They are current package constraints.

  • Steps count work attempted, not visible output — a step that failed before producing visible content still closes with step/end and counts; a step interrupted by a crash counts after the session reloads, when crash recovery appends its synthetic step/end.
  • A cancelled step is counted but untimed — no assistant message assembles, so its partial stream time enters no wall-time figure; a max-tokens usage-host message conversely contributes model time the surface does not show.
  • Counts are log-scoped, not surface-scoped — steps whose messages were later compacted away stay counted; the figures describe the whole session, not the current model-visible surface.
  • Mounted only where the projection registry is composed — other assemblies serve no sessionStats key, and their consumers fall back to window-scoped counting.

Dev Note

Working context for maintainers — click to expand

None.