The TS port passed the event ENVELOPE (data) instead of the model-visible
message (data.message) for assistant/message and tool/result — every such
node was priced at a flat 4 tokens (role framing only), so tool-heavy and
assistant-rich sessions were massively undercounted in the Current
composition. This is why the overview (20.4k) disagreed with the real
usage. Mirrors dsh's deriveEventMessage exactly now; empty-content
assistant messages price 0; tools total uses dsh's whole-array price; the
overview additionally shows the last provider-reported actual prompt
alongside the estimate.
The fold capped the timeline at a step count, so long sessions lost
older turns entirely in turn granularity. When the step bound is
exceeded, the timeline is now trimmed to the newest 300 WHOLE turn runs
(1500-step backstop for pathological many-step turns) — a turn is never
cut in half, and turn mode always shows the full recent turn range.
The harness session log itself has no event cap, so this was the only
limiter.
The Current composition stacked bar and the legend below share one hover
state: hovering a segment brightens it and highlights its legend chip
(bold text + ring on the color dot), and hovering a chip highlights the
matching segment. The mini bar in the request detail is unaffected (no
legend there); the tooltip keeps working.
Turn aggregates no longer stretch to their step span — every bar keeps
the fixed 14px column in both granularities, and the turn strip blocks
match (uniform in turn mode, step-spanning in step mode), so bars and
blocks stay aligned 1:1.
The Messages list renders newest-first by reversing the seq-ordered
surface; that ordering is now pinned by a test. Each node row additionally
shows the source event's timestamp when the host provides one — the host
now stamps every surface node with ev.time (no new data source, just
passing the existing event time through).
A turn aggregate in the detail panel is now labeled 'T{t} · N steps'
with a 'last step' tag instead of a bare step number, making it clear the
breakdown shown is the turn's final step (the record the bar carries).
Tooltips on turn bars use the same step-count wording; step records are
unchanged.
The strip's min-width clamp silently widened single-bar turn blocks
(14px -> 16px), shifting every following block out of column. Removed the
clamp, and turn-mode bars now span their steps' columns: bars and strip
blocks share the same span math, so they align by construction in both
granularities (step records carry stepCount, which also feeds the strip).
On first mount (and on session switch, via a sessionId key remount) the
chart scrolls to the right edge so the latest steps are visible
immediately; afterwards it only sticks to the end while the user is near
it, so scrolling back into earlier turns/steps is never fought. The edge
fades follow the scroll position as before. The render test's fake React
now keeps useRef stable across renders (like React), which is what the
effect needs to see the container.
The chart sliced to the newest 80 requests, so anything older was never
rendered. All requests the host provides (capped at 160) are now rendered
and reachable by horizontal scrolling; the scrollbar is thicker and
higher-contrast, edge fades appear whenever more history sits beyond the
viewport, and the hint mentions the sideways scroll.
A Step/Turn segmented control in the trend card header switches the chart
granularity. Turn mode collapses each turn into a single bar showing its
LAST step's record (the context state when the turn finished); the turn
strip, hover linking, and detail panel all follow the aggregated bars.
Each turn is now a colored block whose width spans exactly its bars'
columns (same grid math), so the partition lines up with the steps above.
Consecutive turns cycle a palette; the T-label sits on the block with a
tooltip fallback for narrow single-bar turns. Hover highlighting
(bidirectional with the bars) is unchanged.
The turn tick row is now an interactive strip: hovering a turn block
highlights that turn's bars in the chart (tinted columns), and hovering a
bar highlights its turn block — both follow one shared hover-only state,
so nothing glows by default. Leaving the strip or the chart clears it.
The Current composition stacked bar (and the request-detail mini bar)
now shows a theme-native tooltip on hover per segment — category, token
count, and share of the total — positioned at the pointer and clamped
inside the bar. Replaces the native title attribute; the wrapper keeps
the tooltip outside the rounded/clipped stack.
Hovering a trend bar links the detail panel to that request immediately
(hover wins over the pinned selection, which takes over again when the
pointer leaves; both fall back to the newest request). The hovered bar
gets a dashed outline, and the chart clears the preview on mouse leave.
- src/host.ts: typed fold model (Snapshot/RequestRecord/SurfaceNode/
ContextEventRecord), local drift-free service contracts for
connection/sessions/sessionQuery via declare module augmentation
- src/client.cts: typed client bundle (React types via @types/react,
locale/slots/connection contracts, typed component props); CJS-flavored
so esbuild keeps require/module.exports despite type:module
- tsconfig.json: strict, noEmit, verbatimModuleSyntax (runtime-free of
imports in the client bundle is enforced by the type system)
- scripts/build.mjs: esbuild transpile for both halves, loader wrap and
smoke checks unchanged; npm run typecheck added to the test suite
- note: @deepseek-ai/* service type packages publish broken dependency
chains on npm (dsh-paths 404), so local contracts replace them
- bars use a fixed 14px column width, so sparse histories no longer
stretch and dense histories scroll horizontally instead of compressing
- the chart sticks to the newest bars unless the user scrolls away
- a turn tick row under the bars groups consecutive requests of the same
T into one labeled range (T1 spans its bars with a connector line)
- test-client now renders the view tree with a stateful fake React and
asserts bar count, scroll container, and turn-range geometry