Commit Graph
387 Commits
Author SHA1 Message Date
bowenliang123 d7ac0e7cd8 fix(balance): lead the breakdown with the topped-up line
Read total / topped-up / granted, so the top-up figure sits before the
granted one. The spec's exact three-line assertions follow in both locales.
2026-09-20 21:26:04 +08:00
bowenliang123 4b40427c1e fix(balance): show the capsule tooltip at once
Replace the native `title` on the balance pill — browser-throttled to about
a second — with the harness Tooltip primitive, the same zero-delay seam the
heatmap cells and the assistant jump action already use. The multi-line
breakdown keeps its shape through the bubble's `pre-line`, and an
`aria-label` carries the same text for assistive tech.

The spec drives the bubble through real hover events instead of reading
`title`, and covers the leave that drops it.
2026-09-20 21:25:27 +08:00
bowenliang123 fa43340a7b feat(client): open the platform usage console from the balance capsule
The Context Dashboard's balance pill becomes a link to
platform.deepseek.com/usage — the console page behind the figure it
shows — opened in a new tab with noreferrer, the same external-link
shape the Plugin Info rows use. Hover underlines the figure as the
click affordance; the read, degradation, and tooltip behavior are
unchanged.
2026-09-20 11:25:46 +08:00
bowenliang123 95f187d308 feat: show the DeepSeek platform balance in the Context Dashboard
The dashboard header gains a capsule with the DeepSeek open-platform
balance (issue #80). The host mounts a /api/dsh-context/balance route
through the same Connection exact-route registry as the detail and
backfill routes, resolves the connection facts per request the way
llm-deepseek serves its own requests (its llm-deepseek settings section
for the credential ref and endpoint override, the credentials service
for the key), and reads /user/balance behind a short TTL cache — the
key never leaves the host process.

Every face is optional and runtime-proved: a deployment without the
DeepSeek provider, without a resolvable credential, without the route
registry, or whose platform read fails (network, non-2xx, malformed
payload, hostile entries dropped whole) answers a typed null, and the
capsule renders nothing — no pill, no spinner, no error state. The
entry follows the display locale (zh → CNY) with the account's first
currency as fallback, and the tooltip carries the granted/topped-up
breakdown.
2026-09-20 11:16:39 +08:00
bowenliang123 af9c69bd09 fix(host): classify backfill format refusals by the observable name family
The persistence seam translates the format edge's
SessionFormatUnsupportedMigrationError into SessionFormatUnsupportedError
(with the "source vN artifact remains unchanged" suffix) before the error
escapes, so the exact-name check never matched on any supported dsh
baseline: the skip summary never fired and every refused legacy session
kept its own WARN (issue #75). Classify by the SessionFormatUnsupported
prefix instead, and cover both faces of the translation in the fixture.
2026-09-20 10:30:01 +08:00
bowenliang123 716497fac2 fix(client): price DeepSeek peak windows at twice the models.dev list
The models.dev deepseek branch publishes the official OFF-PEAK rates
(deepseek-flash: 0.15/0.6/0.003 USD per 1M = api-docs.deepseek.com's
valley tier), but the estimator read the book as the peak list and then
halved the off bucket — every DeepSeek session under-estimated ~2x in
both periods. Flip the semantics: DeepSeek's peak bucket prices at
twice the book rate and the off bucket at book; the tooltip's peak
column doubles for DeepSeek rows with its structure untouched, so
deepseek-flash now reproduces the official peak|off-peak list exactly.
Fixes #78
2026-09-19 21:39:55 +08:00
bowenliang123 670a0fb83b feat(client): land the jump on the Current Composition card
The jump's landing used to reset the scrollport to the top, where the
stats cards hide the composition card and the pinned turn's trend chart
below the fold. The landing now reveals the Current Composition card
(`data-lc-current`) so the card and the trend chart share the
scrollport.

The reveal walks from the anchor to the nearest scrollable ancestor —
host-agnostic by need: the conversation tab scrolls the shared
`[data-conversation-scroll]` container while the right Sidebar scrolls
dockkit's pane body, a CSS-module class with no stable attribute of its
own. The rect delta lands the anchor flush regardless of the
container's current position, overriding the scroll ledger's restore;
repeat jumps re-reveal. No scrollable ancestor (or a hostile chain)
degrades to a quiet no-op.
2026-09-19 08:54:56 +08:00
bowenliang123 f8abef8c40 fix(client): make the chat-to-Context jump actually pin the clicked turn
The jump's seq resolution ran inside the click handler, but the
assistant-actions seat's `useChat` is a real React hook — calling it
off-render throws the dispatcher guard (React #321), which the seat
guard swallowed, so no focus was ever recorded and the landing view
stayed on its default selection. The button now resolves the reply's
seq at RENDER time (unconditional seat read, click consumes the
captured value), so the pin finally fires in production.

The relay also wakes mounted views now: the sidebar landing keeps the
Context panel mounted across jumps, so repeat clicks re-pin it instead
of recording a focus nothing consumes (the conversation-tab landing had
the same gap whenever the tab was already active).
2026-09-18 15:19:37 +08:00
bowenliang123 a1277bac34 feat(client): land the chat-to-Context jump in the right Sidebar
The assistant-action jump now opens the Context tab on the right Sidebar
over the guarded `ctx.sidebarRight.openTab` optional seam (the column
expands in the same step, keeping the clicked reply in view) instead of
switching the conversation tab. Every harness or placement the seam
cannot serve — lines older than 0.1.5-rc.1, tab-only placement, no
mounted session surface, a hostile face — keeps the conversation-tab
activation as fallback. The tooltip is reworded neutrally ("Context
view") since the landing follows what the harness serves.
2026-09-18 14:49:03 +08:00
Bowen Liang 73dd36418c fix(host): arm the projection warm-up behind the dashboard's first open
The contextActivity/contextTimeline warm-up used to cold-read the whole
session corpus at every startup, whether the dashboard was ever opened or
not — and a legacy log the running harness refuses to migrate re-attempted
and re-warned per session on every boot (one reporter measured 115 warn
lines, ~60 KB per start; #75). Under that sustained read pressure the host
also stalls its own client handshake, leaving the workspace sidebar to
mount without its session rows until a restart.

The pass now runs on demand, once per host process: the client POSTs the
plugin's /api/dsh-context/backfill fetch route (the same authenticated
/api fence as the detail route) the first time the Context Insights panel
opens, and every later hit is a no-op. The route and the cold-path faces
compose in either order; a deployment without the connection service or
the exact-route registry never arms the pass — such a client could not
open the dashboard to read the rows anyway.

Skips are accounted instead of spammed: a migration-refused log
(permanent, source-side) drops to a per-session debug line and joins one
summary info line per pass; unexpected per-session failures keep their
own warn, now proof against hostile error objects whose toString throws.
The inter-session yield widens to 100 ms so an on-demand pass never
squeezes the host's other services.
2026-09-17 22:44:22 +08:00
bowenliang123 ffee2a6652 fix(client): drop the running-sessions badge from the Context Insights entry
The green pill on the sidebar-foot entry read as a notification badge
(#74): a running-task count is not something a footer control should
surface as an attention marker. The entry returns to a plain icon +
label; the count's only consumer was the badge, so the tally helper and
the button's useSessions seat plumbing go with it.

Fixes #74.
2026-09-17 17:01:01 +08:00
Bowen Liang 77839c8d3f feat(client): step token figures up to billions in fmt
Values at 1e9 and above rendered as four-digit M figures (1055.9M);
fmt now steps to a one-decimal B suffix, same style as k and M.
2026-09-17 08:27:38 +08:00
Bowen Liang 0003ceabaa fix(client): count only the sessions each KPI figure covers
The cost cell's sub-line now counts the sessions whose spend the price
book could estimate (costSessions), and the cache-hit cell's the
sessions whose usage feeds the rate (usageSessions) — a session with
usage but no book rates feeds the rate while pricing to nothing, and a
session without usage counts toward neither figure.
2026-09-17 00:53:49 +08:00
Bowen Liang b37dac10cb fix(client): shorten the overview KPI copy that clipped its cards
The cost and cache-hit subtitles now qualify with the session count they
aggregate over (a shared ov.kpi.sessionsSub key) instead of long
explanations that overflowed the narrow KPI cards; the heatmap card's
hint reads 'Filter by date'.
2026-09-17 00:36:20 +08:00
Bowen Liang dd0ccbf044 feat(client): add month labels to the activity heatmap
Each column is labeled when a month begins inside it: the month changes
between its Monday and Sunday, or its Monday is the 1st; the label names
the Sunday's month. Months that began before the window stay unlabeled.
2026-09-17 00:28:08 +08:00
bowenliang123 c463620b71 feat(client): add a per-user toggle for the Context Insights sidebar entry
New insightsEntry field ('show' | 'hide', default 'show') in the
dsh-context settings namespace, edited from the Plugin configuration
card. The sidebar entry subscribes to the store and renders null while
hidden; the seat stays registered so flipping back needs no remount.

Fails open everywhere: a schema-less host, an unserved namespace, a
section without the field, a stale persisted value (loose union), or a
write that never persists all resolve to the visible entry.
2026-09-16 21:22:10 +08:00
bowenliang123 95121ffe97 fix(client): keep the insights columns' scrollbars clear of the cards
The sessions/KPI columns' own 8px bar rendered flush against the card
edges, riding on them while scrolling, and a column gaining or losing
the bar reflowed its content by the bar's width. Reserve the track
(scrollbar-gutter: stable) and pad the columns so the thumb stays 8px
clear of the cards; the folded single-column layout resets the padding.
2026-09-16 21:08:07 +08:00
bowenliang123 80a8abd038 fix(host): refold version-stale timeline rows in the startup warm-up 2026-09-16 18:18:05 +08:00
bowenliang123 be08195086 feat(client): add tool-calls and active-time KPIs to the insights panel 2026-09-16 18:07:33 +08:00
bowenliang123 872c24c0ce feat(client): show the last user message on session cards 2026-09-16 18:05:14 +08:00
bowenliang123 068a4fb035 feat(client): split the insights panel into 3:7 columns 2026-09-16 17:37:38 +08:00
bowenliang123 2500ddb03c feat(client): split the insights panel into 2:3 columns 2026-09-16 17:30:03 +08:00
bowenliang123 8de11e2c32 fix(client): mirror the settings trigger's geometry in the entry button 2026-09-16 17:17:28 +08:00
bowenliang123 aca9f29a23 fix(client): rename the dashboard entry to Context Insights 2026-09-16 17:16:09 +08:00
bowenliang123 ff09c4298b fix(client): reword the heat card subtitle 2026-09-16 17:11:45 +08:00
bowenliang123 c4fa859d69 feat(client): tip heat cells instantly with active sessions 2026-09-16 17:10:50 +08:00
bowenliang123 2e1b746723 fix(client): shorten the heat card subtitle 2026-09-16 16:51:38 +08:00
bowenliang123 6cf805d0e5 fix(client): right-align the dashboard card's stat values 2026-09-16 15:18:25 +08:00
bowenliang123 9b8fe848ba feat(client): rework the context dashboard
- Page the session grid at 12 cards per page: a filter change re-anchors
  the pager at the first page, and clamping keeps a shrunken list's view
  in range instead of showing a blank page.
- Redesign the session cards: 3 columns, a project pill, a first-active-
  day line under the relative time, wrap-clamped titles with uniform row
  heights, a turns-and-steps lead figure, and the cost label shortened
  to 费用/Cost.
- Restack the insight band as one folding row — the KPI 2x2 block beside
  the activity heatmap — and drop the aggregate composition card with
  its now-unused fold, keys, and styles.
2026-09-16 14:25:43 +08:00
bowenliang123 a06a302f4c fix(client): keep overlay inputs focused across re-renders
useEscapeClose resubscribed on every render — callers pass a fresh
closure each time — and each resubscribe's cleanup re-ran the pre-open
focus restore, yanking focus out of the overlay's own inputs on every
keystroke. Hold the callback in a latest-ref so the subscription and
its restore live on the active transitions alone.
2026-09-16 14:25:13 +08:00
bowenliang123 5f9b773f01 feat(client): read the composition unit from the card subtitle
The donut center shows the total alone — the overflowing
"tokens (estimated)" moves to the card's subtitle as plain
"tokens" — and the sort buttons follow the workspace browser's
vocabulary: Last updated / Total Tokens / Current Context.
2026-09-16 11:59:58 +08:00
bowenliang123 6048aa7f18 feat(client): hug the dashboard's heat card to its grid
The card pins to the 252px its fixed grid needs — about half the
previous share — and the composition card takes the rest; wrapped
below the row's fold, the heat card stretches again.
2026-09-16 11:59:42 +08:00
bowenliang123 62ff87ea0a fix(client): hide archived sessions from the Context Dashboard
The raw session list carries every session while the workspace browser
hides its archive set, so the dashboard surfaced archived ghosts and
read as duplicates and stale rows. rowsOfSnapshot now drops rows present
in the workspaces snapshot's archivedSessionIds, failing open — an
absent seat or a hostile shape archives nothing.
2026-09-16 11:43:01 +08:00
bowenliang123 154f716f7a feat(client): compact the dashboard's activity heatmap
Default the window to the last 8 weeks, draw fixed-size cells centered
in the card (the block's height now follows the seven cell rows instead
of stretching to the card's width), floor data-less days with a visible
gray, and rebalance the chart row to heat:composition 2:3 so the
composition legend flows into multiple columns.
2026-09-16 11:42:46 +08:00
Bowen Liang 2f265f3f18 feat(client): add the Context Dashboard panel above Settings
A cross-session insight surface: the sidebar-foot entry (stacked above
Settings by the harness's own foot layout, with a running-sessions badge)
opens a frame-wide overlay through the shared module store. The panel reads
the root-scope useSessions/useWorkspaces standard kit — every list row's
host-cached projection values — so no session log needs opening:

- KPI band over the picked range (7d / 30d / all): active sessions, billed
  tokens, estimated cost (models.dev book), cache-hit rate.
- Activity heatmap (GitHub-style, 12 weeks, square cells filling the card):
  a day click pins the session grid to that day.
- Aggregate composition donut over the range's sessions.
- Session cards with a mini composition ring, billed/turns/cost figures and
  a workspace-group / project breadcrumb (deduped when both names match);
  grid filters by range, pinned day, group chips (incl. Ungrouped), and
  search; sorts by recency, tokens, or context size; a card click opens the
  session through the harness's own sessions.open and closes the panel.

Subagent-origin rows are excluded, mirroring the workspace browser's own
predicate (plain open() cannot address them). Every row is sanitized in
isolation — a hostile row degrades to a metadata-only card, never an error.
2026-09-16 08:56:30 +08:00
Bowen Liang e33deda752 feat(host): add the contextActivity daily-ledger projection with a cold-read backfill
The Context Dashboard's heatmap and session cards read the session list's
projection column, which only serves durable cache rows. The new
contextActivity unit folds each assistant settlement into a per-day ledger
(billed tokens + completed requests, keyed by local day, 400-day retention)
alongside the existing contextTimeline/contextHeaders units.

A session folded before a unit existed — or one that predates the plugin
entirely — has no row until it next goes live, so a one-pass warm-up
(backfill.ts) cold-reads every such session's durable log through the
projection cache's own cold-read ladder (coldSnapshot): each unit seeds
from its cached rows, folds the remainder, and the refreshed checkpoint is
written back. Live sessions are skipped (a throwing liveness probe skips
conservatively too), unreadable logs are logged and skipped per session,
and the whole pass rides a deferred inject across the
sessionQuery/sessionProjectionCache/sessionPersistence/sessions services.
2026-09-16 08:55:49 +08:00
Bowen Liang 29a8514c7e feat(client): price the stats board's cost cells across the whole agent family
The Context card's cost cell now prices the current agent TOGETHER with
every subagent session's cumulative billed totals, and a new subagent-cost
cell prices the subagents' share alone; both hover bubbles explain their
scope, the merged one listing the per-model rates of every billed side.
The subagents' usage folds out of the session-list snapshot's warm
contextTimeline rows (subagentCostFoldOf, whole descendant subtree,
cycles cut), with cold relatives' slim heads standing in. Those head
fetches leave the Agent network card's factory closure for a shared
page-scope cache (agentHeads.ts) the stats board's seat reads too, so a
relative is fetched once no matter how many cards consume it; the card's
list-snapshot subscription moves there as useSessionsSnapshot.
2026-09-16 00:12:31 +08:00
Bowen Liang ede62413e6 fix(client): replay the cached cold-relative head across graph remounts
The fetched composition heads lived in component state while the promise
cache lived in the factory closure, so a remount (tab switch) reset the
composed rings back to green and the cache hit kept every relative from
ever re-fetching. A cached read now replays into the fresh instance, with
an identity bail-out so the settled replay on every snapshot tick cannot
loop. Also drops a redundant route wrapper and aligns the spec helper with
the fetch transport it stubs.
2026-09-15 08:52:16 +08:00
Bowen Liang dae94c225a fix(client): backfill cold relatives' composition in the Agent network
The session-list block serves projection values only from the host's
projection cache, so a relative that never attached since the timeline unit
last changed lists pressure-only (occupancy without composition) and its
ring degraded to the single fallback arc — every subagent ring looked green
until its session was opened. The card now fetches those nodes' slim heads
from the /api detail route (one read per session per page, failures sticky)
and re-folds the forest with the row's missing contextTimeline injected; a
row's own value always wins, and any fetch failure degrades back to the
pressure-only ring.
2026-09-15 08:33:23 +08:00
Bowen Liang a82e6a1dc7 fix(host): mount the detail endpoint through Connection's /api fetch registry
The split generation's detail transport rode connection.rpc.handle, whose
registration reads webServer off the connection service's own context —
cordis's strict-read guard rejects that for plugin callers, so the channel
never armed and every detail read fell through to the static fallback (405).
Mount the handler through connection.fetch.register instead (the same
exact-route seam the harness's own file-upload and media-reference routes
use), riding the authenticated /api fence; the client POSTs it same-origin.

The detail payload now also carries the slim wire head at the same fold cut
(the composition scalars the Agent network's cold-node ring fetch renders
from); a missing or malformed head drops out alone, never rejecting the
payload the detail cards consume.
2026-09-15 08:32:14 +08:00
bowenliang123 f349430eda fix(host): degrade a missing injection source to an empty producer label
injectionSourceName read source.kind/plugin unguarded on its first line,
so a null/undefined source would have thrown the fold's caller (issue #69).
The fold's only call site already gates on isInjection, so the reachable
behavior is unchanged; the guard closes the trap for future callers and
matches isInjection's tolerance for foreign source shapes.
2026-09-14 17:16:11 +08:00
bowenliang123 33fd7ae680 feat(client): hold the hover tint on the expanded browser category header
The Context browser's category header carried only the hover utility, so
the tint vanished the moment the pointer left an expanded card and the
header melted into its body. The open state now keeps the hover
background via a lc-br-cat-open state class - the same token as the
hover and hover-link tints - on every category card alike.
2026-09-14 14:25:51 +08:00
bowenliang123 053c4c2330 feat(client): face the /context slash row with a localized label and glyph
The slash menu candidate carried only its name, section, and description,
so the row rendered the bare "context" while every built-in command row
showed its icon and localized title. The candidate now carries the plugin
emblem and a cmd.label title (the name renders as the trailing alias on
builds that show titles). Both fields are pure display data: harness
builds that predate their rendering ignore them and keep the bare name.
2026-09-14 11:46:37 +08:00
bowenliang123 099b7073f4 feat(browser): label injected rows with the fold-stamped source identity
The Context browser's Injected Context and Skill Injections rows previewed
the raw injected content, while the Context Events card named the producer
(plugin id, reconciled instruction files, durable kind). The fold now
stamps that identity on the surface node alongside the event, and the rows
prefer it for the collapsed preview - the content stays one expand away,
and the row filter still scans the folded text. Nodes folded before the
stamp (additive-optional on the wire and persisted state, no stateVersion
bump) degrade to the previous content preview.
2026-09-14 11:22:47 +08:00
bowenliang123 73318a5cd6 style(client): reword the token-estimate footer note
Spell out DeepSeek Harness instead of the dsh shorthand and name the
note's subject (Token 数量统计 / Token amount) in both locales.
2026-09-14 10:27:43 +08:00
Bowen Liang ee63b59e75 fix(client): group the /context slash row under a localized section heading
The slash menu renders one source-title row per trigger source, titled
from the harness's own slash.menu dictionary; the plugin's source name
`context` is not a key there, so the row showed the bare raw name and
read as a duplicate command (closes #67). Candidates now carry a
localized `section`, which the menu renders in place of the
source-title row — 上下文 in Chinese, Context in English.
2026-09-14 02:04:47 +08:00
Bowen Liang 022768ef54 fix(client): light the skill bucket in the trend chart's category hover-link
The hover-link's re-light rules are a hand-enumerated CSS list keyed by
category, and the skill bucket joined the vocabulary without one — hovering
the browser's skill section (or the overview's) dimmed every bar with
nothing lit. Add the missing rule and pin the list-must-track-Category
constraint next to it.
2026-09-14 01:32:04 +08:00
Bowen Liang afd9af63f0 feat: surface skill occupancy through a dedicated skill-injection category
The fold buckets all three skill-content shapes the harness injects — the
<available_skills> catalog digest, a user-explicit /name invocation's
instructions message, and the content a skill-tool load returns — into a
new 'skill' composition category instead of burying them in inject/tool
(closes #66). Skill-tool loads keep their tool identity stamped, so the
call tallies, hit counts, and failure marking are unchanged.

stateVersion 18 → 19: the per-category sums change shape, so cached rows
refold from the durable log on upgrade (docs/compatibility.md).

The trend chart, overview, Token donut, stats, and the Context browser all
gain the seventh bucket (orange-500); the browser's skill section pins its
rows like injects, so long sessions keep listing what the sums count.
2026-09-14 01:11:37 +08:00
Bowen Liang 4de2f2cf9e feat(client): show the turn's step total in step-granularity labels
detail.step/tip.step now read "第 s 步 (共 n 步)" / "Turn t · Step s of n" —
the same per-turn tally a turn-mode bar's stepCount carries, so step and
turn granularities agree on how busy a turn was. A shared turnStepsOf()
getter feeds the trend tooltip, the detail head, the files scope label,
and the browser picker/meta rows.
2026-09-14 00:00:34 +08:00
Bowen Liang 3d594ba316 feat(client): deepen the stats cache-hit cell to two decimals with a formula tip
The Context stats grid's cache-hit cell now carries the chat stats line's
figure two decimals deep and explains itself through the shared stat-tip
bubble (the whole-session cache-read share of billed input), localized in
both locales.
2026-09-13 23:59:56 +08:00