Commit Graph
213 Commits
Author SHA1 Message Date
Bowen Liang 8bfcf65d31 feat(client): count whole-session human inputs on the stats board 2026-09-12 01:24:46 +08:00
Bowen Liang 8d2ddeff55 feat(client): move the context-event tallies onto the events card's kind filters
The Context Stats card's inject/compaction/prune cells duplicated what the
events card already tracks. Drop the three cells (the grid falls to five:
turns, steps, tool calls, cache hit, cost) and show each tally inside the
matching kind-filter button instead, sourced from the same precomputed
counts the stats card used.
2026-09-12 00:35:30 +08:00
Bowen Liang b68a6d50ad fix(client): cite the official model IDs in the cost tooltip
The rates themselves check out against the official pricing pages (both
currencies, both families, and the Beijing peak windows), but the flash
row's label named deepseek-v4.1-flash — the model VERSION, not an API ID.
The page's current flash ID is deepseek-flash, with deepseek-v4-flash as
the accepted legacy ID, so the label and the tip's applies-to list now
cite those; the tip also drops the colloquial hardcoded phrasing for a
plain for-reference-only note.
2026-09-12 00:28:36 +08:00
Bowen Liang 02ae427c8e style(client): rename the zh answer-decode label to 模型输出 2026-09-12 00:13:02 +08:00
Bowen Liang 3ed80aa9e0 feat(client): the Context card swaps its image count for the chat line's cache hit
The images cell said little about the session; the eighth cell now shows
the whole-session prompt-side cache-hit rate off the official tokenUsage
projection — the same source and formula as the chat stats line under the
composer, truncated to one decimal (cacheHitPercent gains a precision
parameter; the two-decimal default keeps the request detail's figure).
2026-09-12 00:05:45 +08:00
Bowen Liang 423e9423eb refactor(client): split the Token card by composition around the chat-line total
The donut center now carries the whole-session billed total — the same
figure the harness chat stats line shows under the composer (uncached
input + cache read + cache write + output off the official tokenUsage
projection) — instead of the cache-hit share.

The slices answer WHAT the tokens are rather than how the provider
cached them: the six composition categories share the provider-reported
prompt-side total by the composition card's own estimated ratios (their
counts carry the ≈ marker), and the provider-exact output closes the
ring, so the parts equal the chat line's figure by construction.

The cache-read/cache-write/uncached bucket view and its four locale
keys are gone; tokens.total is new.
2026-09-11 23:26:36 +08:00
Bowen Liang 296324cc70 fix(client): remount trend bars on granularity switch so the entrance rise replays
A turn aggregate IS its last step's record (the same seq), so with a
plain seq key a step ↔ turn switch reused those bars' DOM nodes — the
entrance animation had already finished on them and CSS never replays an
animation on a reused node, leaving every turn's last step bar popping
in unanimated. Keying ChartBar by seq + granularity forces a remount;
mode (Total/Delta) switches already remount through the stack ↔ arms
structural change.
2026-09-11 22:14:54 +08:00
Bowen Liang d5dc784292 feat(client): ease-in entrance animations for the context charts
The donut slices sweep in clockwise from 12 o'clock, the composition
stacked bar grows open segment by segment, and the trend chart bars rise
from their baseline — all staggered in layout order, transform-only (no
layout thrash), running once per mount and disabled under
prefers-reduced-motion (the harness's own idiom).

- the stagger caps (trend bars 20, stacked pieces 8) so long logs and
  long DNA band lists still settle in well under a second
- the auto-compaction reserve band stays unanimated: as an overlay its
  growth would smear the stripes, and it reads as chrome, not data
- covers the tab, the /context modal and the right Sidebar, which all
  render the same chart components
2026-09-11 22:13:56 +08:00
Bowen Liang 08def011a0 fix(client): price deepseek-v4-pro at its own V4 Pro list rates
DeepSeek has ended the routing window: deepseek-v4-pro (DeepSeek-V4-Pro-0813)
now bills at its own published table instead of riding the V4.1 Flash triple —
USD peak hit $0.044 / miss $1.32 / out $3.96, CNY peak ¥0.3 / ¥9 / ¥27,
off-peak half price. Flash rates are unchanged.
2026-09-11 21:54:21 +08:00
bowenliang123 97ae8ff2ee feat(client): preview File Activity files in the right Sidebar
Clicking a file name in the File Activity card now opens the file's preview
tab in the right Sidebar — the shipped document-preview viewer, reached the
same way the built-in Files sidebar reaches it — on harnesses that serve that
column (dsh 0.1.5-rc.1+). The address is built with the harness's own
browser-safe `fileAddressFor`, inlined by the client bundle exactly as the
Sidebar's file types inline it, so a path under the workspace collapses to the
session-relative spelling and every route to one file settles on one tab.

The capability is OPTIONAL by contract, like the tab registration itself: the
opener reads `ctx.sidebarRight.openResource` through the runtime boundary and
re-proves it per call, and reports whether the column took the address. An
absent column, a face that refuses the address, or an unencodable log path
falls through to the pre-existing system-open behavior, so an older line is
fully functional and nothing becomes an inert click. Pattern rows and
directory targets are never previewable.

Adds the `@deepseek-ai/dsh-util-workspace-path` dev dependency (itself
browser-safe), allows it in the client bundle purity gate, pins the
resource-navigation seam in the compat matrix, and covers the address builder,
the card's preview-then-fallback click, and the optional opener per baseline.
2026-09-11 18:45:28 +08:00
bowenliang123 65e5f0efd4 fix(host): probe the running module tree before the profiles mirror
The baseline gate resolved the harness version from the healed
`$DSH_HOME/profiles/node_modules` mirror first, but that mirror is only as
fresh as the last CLI profile boot: it is rewritten by whichever installation
last healed it, and a packaged desktop host never heals it at all. A leftover
global CLI could therefore pin the mirror below the baseline and gate a
healthy desktop harness — an upgrade modal plus fallback (unfolded) projection
units on every launch (issue #59).

Resolve through the module tree the plugin is actually bound to instead: its
own ESM resolution (`import.meta.resolve`, the same pipeline its runtime
imports use, so an embedding shell that redirects plugin imports — the
packaged desktop's ASAR resolver bridge — redirects the probe too) of the
library packages it imports. A witness that lands inside this package's own
tree is the plugin's dependency closure (a `link:`-installed dev checkout's
pinned devDependencies), never the harness, so it is discarded. The home
mirror stays as the fallback for a dev checkout and for a running anchor that
cannot answer; the CLI package is still probed only through it.

Fail-open is unchanged: any unresolvable package, non-file URL, or unreadable
manifest degrades to `undefined`, which never trips the gate.
2026-09-11 16:18:47 +08:00
bowenliang123 be3f598959 style(client): lead the placement menu with 全部 and shorten the tab option to 标签页 / Tab 2026-09-11 14:06:55 +08:00
bowenliang123 5322d1e6d4 fix(client): fail the placement open to 全部 on unreadable or unpersisted config
A config problem must never leave a Context entry hidden:

- a scope value the plugin cannot understand (present but invalid)
  degrades the placement to `all` instead of stranding the previously
  chosen one — the read path now distinguishes invalid from missing,
  and only a genuinely absent field (older Host half) keeps the
  current state;
- a placement write that fails to persist no longer keeps the
  optimistic echo in-session: unless the scope's truth itself carries
  a valid placement, the rollback degrades to `all` (other fields
  keep the existing keep-the-echo behavior).
2026-09-11 14:02:35 +08:00
bowenliang123 a4add07664 feat: add a placement preference (tab / sidebar / both) for the Context view
Closes #58: the Context panel hung off two registrations at once (the
conversation tab and the right-Sidebar guide entry), with no way to keep
just one. A new per-user `defaultPlacement` preference — the first row of
the Plugin configuration card — picks who carries the view: the
conversation tab, the right Sidebar, or both (the default, so existing
deployments see no change).

- shared: `DefaultPlacement` vocabulary (`all` | `tab` | `sidebar`) on the
  settings namespace; the Host schema gains a loose `defaultPlacement`
  field so a stale persisted value degrades to `all`
- client: `placement.ts` mounts each registration through its own factory
  and, on a preference flip, disposes exactly what the new value drops
  (the harness's own dynamic-mount idiom); `watchSidebarContextTab`
  returns its deferred-inject disposer so the sidebar mount can be taken
  down and put back; the settings card leads with the placement row
2026-09-11 13:44:21 +08:00
Bowen Liang 189609874b style(client): shorten the actual label and right-align the estimate pair 2026-09-11 01:03:19 +08:00
Bowen Liang 61bb138ddc feat(client): pair the live surface's estimate with the freshest actual prompt 2026-09-11 01:03:01 +08:00
Bowen Liang 8f7f01a9f9 feat(client): drop the preview badge from the browser meta row 2026-09-11 01:02:42 +08:00
Bowen Liang 037a2e80e7 feat(client): part the donut slices on a hairline divider
A ring with a neighbour on each side now gives half a 0.5-unit gap off
both ends of every arc (1 unit ≈ 1% of the circumference, so ~1px on the
96px card ring), letting the card's own background draw the line between
neighbouring colours. A lone painted slice keeps its full ring — a gap
there reads as a nick — and the cut never takes more than a quarter per
end, so a sliver like the 0.2% overhead wedge never vanishes whole.
2026-09-10 20:19:14 +08:00
bowenliang123 8212526d6c feat(client): brand the sidebar Context tab with the document emblem
Bundle the colourful document sheet as the plugin's own icon and fill the
right-Sidebar tab type's two glyph seats with it, the way the shipped files
type does: the guide capsule's `icon`, and a new `sidebar.right.pane.tab.title`
registration that draws the emblem beside the chip label.

The chip's close control sits out of flow over the title's last 14px and an
active chip fades the 16px before it, so the shipped files chip stays readable
only by its short label. The label now carries a trailing gutter so that fade
lands past the text instead of on the last glyphs.

Registration turns rollback-safe (each disposer is tracked) so a partial
failure unwinds cleanly, and the obsolete primitives-icon resilience spec goes
with the icon read it guarded.
2026-09-10 17:32:40 +08:00
bowenliang123 1a8163a752 refactor(client): follow the 0.1.5-rc.1 guide-entry contract
`SidebarRightGuideEntry` carries `order`, `title`, and an OPTIONAL
`description` again at dsh 0.1.5-rc.1 — it was required at 0.1.5-alpha.1,
dropped at 0.1.5-alpha.2, and restored optional now. The plugin's guide
capsule re-adds its `description` thunk and the `sidebar.guideDescription`
dictionary keys, so the right Sidebar's guide entry renders the one-line
subtitle the shipped Files type does.

The mirrored face marks the thunk optional: a line whose guide body ignores
it simply goes without, so the same contribution stays valid across the
whole 0.1.5 line.
2026-09-10 11:37:21 +08:00
bowenliang123 86fb240ce3 compat: move the V3 baseline to dsh 0.1.5-rc.1
The supported V3 line follows the newest release: the baseline becomes
v0.1.5-rc.1 (tag, vendored session package) and the declared release map
names 0.1.5-rc.1. The release workflow fetches the new tag, and the
disposable-profile install/uninstall was verified for real against the
published 0.1.5-rc.1 CLI (install OK -> 1 composed row -> uninstall OK ->
0 rows).

At 0.1.5-rc.1 `SidebarRightGuideEntry` regained its optional `description`
line, so the guideEntry probe pins order/title/description/icon and the
now-pointless "absent" assertion plumbing is dropped.

Every support/capability label states the 0.1.5-rc.1+ floor;
`0.1.5-alpha.x+` remains only as the V3 LOG-generation label (the format
first appeared at 0.1.5-alpha.1). The docs record the verified matrix and
the seam movement across alpha.1/alpha.2/rc.1.
2026-09-10 11:37:04 +08:00
bowenliang123 489864d63a feat(cost): price the deepseek-flash model name at the Flash rates
The family matcher required a `v4` marker, so `deepseek-flash` requests
went unpriced. Accept `deepseek` as an alternative marker — proxy
spellings like `deepseek/deepseek-flash` land too, while foreign
flash-named models (gemini-2.0-flash) still match no family. The stats
board's price-list label and both locales' cost tooltip name the new
model beside deepseek-v4.1-flash.
2026-09-10 11:22:42 +08:00
bowenliang123 d5b4247654 feat(client): copy a rich section's raw text from the browser
Every rich detail section (system prompt, tool schema descriptions,
messages, tool results, compaction summaries, and the same cards inside
the /context modal) gains an icon-only copy control beside the Raw/MD
switch. It copies the section's exact SOURCE text, never the rendered
markdown, via the harness's own writeClipboard primitive; success flips
the glyph to a check for a beat, a rejected write claims no success, and
a repeat click inside the window is a no-op — the shipped code-block
control's behavior. Both icons and the writer are platform-module
exports present on all three supported baselines.

The added control tipped the head's trailing group out of the clipped
section card at the narrowest pane, so rich-text heads now fold that
group onto a second line under width pressure. The wrap is scoped to
those heads: the call-name head must stay one line (long MCP names
ellipsize instead).
2026-09-10 11:12:13 +08:00
Bowen Liang b857d56db3 fix(client): drop zero-time slices from the timing legend
A slice that never happened still rendered as a row — "0.0%" plus a count
borrowed from elsewhere on the card — which reads as "this ran 323 times in
no time". The ring has always skipped zero arcs; now the legend matches it,
so the card shrinks to the slices that actually carry time.

Zero slices are common: across 307 local sessions, reasoning is zero in
23.5%, answer text in 14.3%, tool args in 13.7%, and tools in 14.3% (a
one-turn "hi" session collapses from six rows to two). Overhead is the
residual and effectively never zero, so the change bites the decode and
tool rows. Every slice is a share of the wall total, so dropping rows never
distorts the remaining percentages.
2026-09-10 02:23:23 +08:00
Bowen Liang e0532936fe feat(client): render the generation split and stop borrowing call counts
The Timing card's model slice expands into Thinking / Answer / Tool args
whenever the host served the split; an older cached row (or a log whose
stream carried no block markers) keeps the single LLM Gen row, so nothing
degrades. The ring shares the generation window across the buckets in card
order, clamped exactly as tools already were.

The decode rows carry no call-count qualifier: they count decode BLOCKS,
and one model call emits zero to many of them (a single call routinely
requests several tools; most calls emit no reasoning at all), so the
session's call count was a false per-slice tally. Measured on a real
236-call session: 236 calls produced 302 tool-call blocks, and 1078 of
3037 calls emitted no reasoning. TTFT and tools keep their counts — those
happen exactly once per call/run.

`timingOf` narrows the new optional scalars without materializing absent
ones, and the fast path applies the same non-negative gate as the slow
path so a hostile bucket cannot slip through.
2026-09-10 02:22:43 +08:00
Bowen Liang 635afceb83 feat(host): split the model generation window by decode block
The timing totals priced a model call as TTFT + one opaque generation
window, so thinking, answer text, and tool-argument decoding were
indistinguishable. Fold the stream's `block-start` framing into three new
additive-optional buckets — `reasoningMs`, `textMs`, `toolArgMs` — that
tile the window's marker span.

Both log generations feed the same split: a V0 log's `assistant/chunk`
markers open and close spans in the step slot; a V2+ log carries no chunk
events, so `decodeSpansOfStream` reads them off the embedded settlement
stream. The split is priced only when the window was (an unstamped call's
model time stays unattributed wholesale), and a zero span stays absent so
pre-split-shaped state gains no dead properties.

The fields are additive-optional, so `stateVersion` stays at 15 and
cached rows keep parsing — a bump would discard them and orphan the key
for idle sessions (the #37 regression).

Measured over 305 local sessions: reasoning 31.3% of active time, tool
args 15.3%, answer text 3.5% — the answer is 1/9th of what the model
spends decoding.
2026-09-10 02:21:45 +08:00
Bowen Liang 6bdd6bdf59 fix(client): give the sidebar Context panel the tab's main-row layout
The right Sidebar's Context panel split its three main cards into three
columns, so a pane too narrow for three folded into a ragged 2+1: a hole
under the composition card and the trend card alone on its own row. The
conversation tab read differently, from the same component.

Drop the per-host arrangement: both hosts now use one two-column row
(composition over trend in the left column, the browser beside it and
stretched to the pair's height). The panel's row simply folds to a single
column at the shared 360px floor instead of splitting across two rows.
The head row is untouched, so the panel still shows its two donut cards.
2026-09-10 01:37:25 +08:00
Bowen Liang 47c470043f feat(client): render timing durations in compact locale-free units 2026-09-10 00:24:05 +08:00
Bowen Liang 50e89301cd fix(client): reprice the session-cost estimate for the 2026-09-10 Flash rates
DeepSeek reprices the Flash series effective 2026-09-10 12:00 BJT
(off-peak per 1M tokens: hit ¥0.02/$0.003, miss ¥1/$0.15, out ¥4/$0.6;
peak at 2x). Between the V4.1 Flash launch and the V4.1 Pro release
every Pro request is routed to V4.1 Flash and billed at the Flash
rates, so pro shares the flash triple until V4.1 Pro is priced — swap
that one reference when it ships. The stats-board tooltip names the
flash row deepseek-v4.1-flash, and both locales note the interim
routing. Fold logic is untouched: the substring family match already
lands v4.1 spellings (new fixture), and the peak windows are
unchanged. Logs spanning the cutover estimate entirely at the new
table — the cell is a reference estimate by contract.
2026-09-09 23:41:31 +08:00
Bowen Liang 5f2498e973 refactor(client): follow the 0.1.5-alpha.2 guide-entry contract
SidebarRightGuideEntry now carries only `order`, `title`, and `icon`,
and the guide page renders a glyph + title capsule, so the plugin's
contribution drops its description thunk and the now-unused
`sidebar.guideDescription` dictionary keys. The mirrored face and the
sidebar's version labels state the supported 0.1.5-alpha.2+ floor.
2026-09-09 23:03:25 +08:00
Bowen Liang a4bd0d40f9 compat: move the V3 baseline to dsh 0.1.5-alpha.2
The supported V3 line follows the newest release: the baseline becomes
v0.1.5-alpha.2 (tag, vendored session package) and the declared release
map names 0.1.5-alpha.2. The release workflow fetches the new tag.

The alpha.2 client seams are pinned in the matrix: the conversation
surface moved under the keyed `main` panel (`main.conversation`) while
the plugin's `conversation.view` seat keeps its name, and
`SidebarRightGuideEntry` dropped its description line. tests/baselines.ts
therefore gains a guideEntry probe (the fields present plus the dropped
copy field asserted absent).

Every support/capability label states the 0.1.5-alpha.2+ floor;
`0.1.5-alpha.x+` remains only as the V3 LOG-generation label. The docs
record the verified matrix, the two moved seams, and the install row
that still waits on the CLI's npm publication (the `alpha` dist-tag
lags the release).
2026-09-09 23:03:10 +08:00
Bowen Liang 8978db50c9 feat(client): put the Context browser before the trend in the sidebar panel 2026-09-09 02:47:24 +08:00
Bowen Liang b1c0f46d3c feat(client): drop two head cards from the sidebar Context panel 2026-09-09 02:45:50 +08:00
Bowen Liang ade57b2544 test(host): pin the envelope system list between folds 2026-09-09 02:43:35 +08:00
Bowen Liang 52c3b0f824 fix(client): refuse a partially numeric systems list on the fast path 2026-09-09 02:43:15 +08:00
Bowen Liang b93b0e24f9 feat(client): add the Context tab to the right Sidebar 2026-09-09 02:43:04 +08:00
Bowen Liang f16f034e63 test(compat): add the v0.1.3-alpha.2 and v0.1.5-alpha.1 baselines
Each line carries its own durable-event vocabulary (assistant/chunk and tool/code-dispatch are V0/V2-only; system/message and tool/ptc-dispatch are V3-only), so Baseline.foldEventTypes replaces the single global list and a matrix test asserts the union still covers every fold case. The release workflow fetches both new tags before pnpm test.
2026-09-09 02:09:49 +08:00
Bowen Liang 6819fa7fe6 feat(compat): fold the V2 and V3 session-log generations alongside V0
The supported range (dsh 0.1.2-rc.1+) spans three durable-log generations, and the fold only read the V0 spellings — so on 0.1.5-alpha.1 sessions the system prompt priced 0, TTFT/generation stayed 0, and startSeq/endSeq replacements appended instead of replacing.

Read SHAPES, never a detected version (the probe can be wrong: a healed profile mirror may name another release than the running harness):

- system/message surface nodes feed systemTokens plus a bounded systems list (the harness's "last nonempty surviving system" rule), replacing the V0/V2 request/header.header.system envelope figure; the browser resolves the prompt in force per step and fetches its text from either event shape.
- The first token comes from the embedded assistant stream (assistant/message.data.stream / assistant/attempt.data.stream) when the log carries no V0 assistant/chunk deltas, mirroring dsh-llm's assistantStreamFirstTokenTime.
- Replacement endpoints accept both startSeq/endSeq and start/end.
- tool/ptc-dispatch books nested file ops alongside tool/code-dispatch.

Statistics were differentially verified against the harness's OWN projection values on real V0, V3-native, and V3-migrated logs: system/tools/message tokens, request and turn counts, TTFT, generation, tool time, and every billed cost bucket match exactly.

The new state fields are additive-optional, so stateVersion stays 15: a bump would orphan the key for idle sessions (the #37 regression).
2026-09-09 02:09:38 +08:00
Bowen Liang 7c081433c4 fix(client): remove the chat stats-line jump affordance
The hover-underline/click jump the plugin wired onto the harness chat
stats row conflicts with the reworked stats-line detail view shipping
in dsh 0.1.5. Drop the composer-dock entry, its styles, i18n keys, and
tests wholesale - the stats line renders untouched again.

Closes #53.
2026-09-09 01:19:26 +08:00
Bowen Liang d3227ff3c2 fix(client): stop a chart edge swipe from navigating the browser history
A trackpad swipe past either edge of the trend chart's horizontal scroller
chained into the page and the browser read it as its history gesture, so a
few scrolls in the Context tab could leave the whole app for another page in
the tab's history (back at the left edge, forward at the right).

Two layers, matching what the harness already does for its own horizontal
scrollers: `overscroll-behavior-x: contain` on the chart scroller and the
agent graph's stage stops the chaining (Chromium/Firefox), and overscroll.ts
cancels the horizontal-dominant wheel the scroller cannot consume at either
boundary for WebKit, which still navigates on horizontal overscroll with the
property set (WebKit bug 240183). Vertical-dominant gestures are untouched,
so a wheel over the chart still scrolls the page.
2026-09-08 19:55:40 +08:00
Bowen Liang c3ffbed94c feat(client): rescale the Context Trend bars to the visible range
A title-row Adaptive switch redraws the chart against the bars currently
visible in the scroller instead of the whole retained log: the axis follows
the scroll and pane resizes, so the first step's system prompt + tool schemas
(a spike far off screen) no longer flattens every bar the reader is looking
at. Per-bar totals scale total mode, the diverging arms scale delta mode, and
the window reads the exact 14px column + 2px gap grid, so a column parked in
the gap never joins it.

The switch reuses the Context Browser's DNA-toggle chrome and stays
mount-local (no settings write); off keeps the whole-log scale. A window
carrying no figure of its own — an empty history, a zero-width pane, or an
all-zero delta window — falls back to that scale, and unchanged maxima
re-render nothing.
2026-09-08 19:49:10 +08:00
Bowen Liang 14ecf44ce4 refactor(client): drop the Context Trend card's subtitle hint
The "✂ marks compaction/prune, Step/Turn switches granularity" caption
restated controls that are already visible in the card's own title row,
so it only crowded the header. The subtitle span now renders solely for
the transient focus state ("Showing {cat} only"), and the unused
trend.hint key leaves both dictionaries.
2026-09-08 19:35:41 +08:00
Bowen Liang 40a054b99b fix(client): guard the DNA bar against a stale hover key
A snapshot push can drop the band under a resting pointer (compaction,
tail slide out of the served window) without a mouseleave, and the dead
key exact-matched nothing: the whole strip sat dimmed with nothing lit
— the very state the open-category pin's breakdown gate rules out.
Honor a band hover only while its key names a rendered band, so the
mirror/pin fallbacks resume underneath.

Also correct the hover-link comment: DNA hovers stay on the bar and
never report back to the overview (the link is one-way in DNA mode,
as the existing test already pins).
2026-09-08 05:25:32 +08:00
Bowen Liang 481783de4e feat(client): keep the open category lit in the browser's composition bar
The browser's accordion already focuses the Context trend chart on the
open category; the composition bar now holds the same highlight as a
resting state — category mode lights the category's segment, DNA mode
lights the category's bands as a group (the parts' group key). A
pointer hover overrides the pin and the pin resumes on leave; the pin
drops while a previewed/picked step holds nothing for the category, so
the bar never reads all-dimmed with nothing lit. No tooltip floats for
the pin: category mode mounts no tip slot, and the group-keyed pin
exact-matches no DNA band.
2026-09-08 00:09:55 +08:00
Bowen Liang 568d837f80 feat(client): add a DNA mode to the Context browser's composition bar
A title-row toggle redraws the composition bar as one band per context
item — the system prompt, every tool schema, every surface message — in
prompt order (the header prefix first, then the message flow by seq),
the order the model reads the context and the order items joined it.
The header epoch's own seq is bookkeeping (dsh logs request/header at
dispatch of the first request using it), never an ordering key: a
mid-session refresh must not park the schemas behind the tool results
they describe.

Bands floor at 0.35% of the occupied region so a tiny item stays a
hoverable filament (tooltips still report true shares); hovering names
the item with its time, a mirrored category hover lights the category's
bands as a group, and clicking a band opens its category + element row
below, scrolling it into view.
2026-09-07 23:58:34 +08:00
Bowen Liang d52c43ae18 fix(client): plot raw fold figures in the trend chart so constant categories stay flat
The trend bars multiplied every category by a per-request anchor factor
(provider prompt / heuristic total). That factor drifts with the heuristic's
ratio error as the context composition changes, so categories that never
change — the system prompt, tool schemas — plotted as steadily growing
(1668→1789 on a real log whose fold value is a constant 1851), and the delta
mode invented non-zero changes for them. The same screen already shows the
raw figures everywhere else (step detail, browser, overview), and dsh's own
token-meter breakdown projection serves un-rescaled heuristics per request,
so drop the anchor: segments, axis, tooltip and the focused view all plot the
fold's figures directly. Provider usage stays visible in the step detail's
Actual Prompt / Output / Cache chips.
2026-09-07 22:49:55 +08:00
bowenliang123 3a404b696e fix(client): keep the browser's on-demand fetchers reactive to the history face
A mount could race the declared inject (a watch rebuild remounts the
Context tab and the /context modal before the injected fiber re-fires),
and the fetchers snapshot the absent face once — the browser then stuck
on the static degradation for its whole lifetime, with no details
renderable in any category. The face now lives behind a
subscribe+useSyncExternalStore seat (useHistoryFace), and both
components rebuild their fetchers when it lands or is revoked, so an
open section auto-fetches without any user action. The /context modal
also gains the session image loader the tab already wires, so
attachment cards stop degrading to metadata-only there.
2026-09-07 15:38:40 +08:00
bowenliang123 ba074e078e fix(host): backfill pre-step message ids so stripped messages cannot brick sessions
A durable `user/message` event lacking a non-empty string `data.id` passes
the runtime append unchecked but makes the harness refuse the whole session
at its next load (`... lacks an identified message`, issue #51). The archived
wrap-up notice carried a rebuild signature (role-first key order, `id` and
`summary` stripped) that no shipped harness producer emits, so the strip
happens upstream of the append, outside both audited codebases.

Guard the durability boundary from the plugin side: a prepended
`agent/pre-step` listener runs last in the waterfall and mints a `dshctx-`
prefixed id for any decision message that would persist unidentified,
leaving everything else — including the decision's reference identity —
verbatim, and failing open on hostile entries. The seam is pinned per
baseline (the event dispatch, the user/message append that consumes it,
and the prepend option) in the real-code matrix.
2026-09-07 15:12:33 +08:00
bowenliang123 bcbbc3367f feat(client): focus the trend chart bars on the browser's open category
Expanding a category in the Context browser now plots every trend bar on
that category alone: the axis rescales to the category's own max (provider
anchor preserved) in both total and delta modes, the hover tip names the
category, and the card subtitle reads "Showing {cat} only" until the
category collapses. The /context modal keeps its unfocused chart.
2026-09-07 13:45:41 +08:00
bowenliang123 1eb10e64e2 refactor(client): drop the T prefix from turn strip labels
The strip label is now the bare turn number (the block's hover title
keeps "T{n}"). Without the prefix, one/two-digit turns fit the 10px
base font at the fixed 16px column pitch with no shrink; the uniform
shrink now kicks in from three-digit turns (7px) and floors at 6px for
four-digit ones.
2026-09-07 12:39:15 +08:00