The sidebar uses the shared SidebarFrame from @cortexkit/opencode-sidebar 0.2.0 and shows option B from #839: Context, History, Prompt cache, Knowledge, Dreamer, and a Health section that shows only when something is wrong. The panel collapses to one row per section.
0.2.0 added the two things this PR waited for (cortexkit/opencode-sidebar#1, the follow-up of cortexkit/common-auth#12): a collapsible prop for a panel without accounts, and the exported SidebarGaugeRow. The local gauge is gone.
The snapshot gets the values that the new rows need. MC calculates each value during a pass, or reads it with one indexed query when the panel refreshes. There is no migration (decision 2): the historian per-run data stays in memory for each session.
The panel refreshes less often, and render does no reads.
Screenshots
Rendered from the compiled sidebar slot with OpenTUI's testRender, with the OpenCode dark theme colours and fixture values (the values of the #839 mocks).
Expanded
Incident (Health in the warning tone)
Collapsed
Collapsed, incident
Changes
Snapshot data
All new SidebarSnapshot fields are optional, so an older RPC server still works with the new view.
Field
Source
Cost per refresh
historianRun
Kept in memory for each session (at most 256 sessions, cleared on session deletion). It records the outcome, the model that produced the published output, the fallback flag, the primary attempts and rejections, and the totals since the last clean run.
none
waitingMessages
The last value that the compartment trigger calculated.
session_meta, then two counts. The plans use sqlite_autoindex_compartments_1 and idx_memories_project_status_expires. The memory count uses the same workspace visibility rules as m[1].
2 counts
configReloadFailure
The live config reader.
none
A historian attempt counts against the model that produced it. A primary attempt counts as rejected when its output fails validation, or when the host answers with a different model. An invalid fallback output does not count against the primary. The run's primary is the model it started with, after master's start selection (selectHiddenModelStart).
OpenCode 1 and OpenCode 2 use the same snapshot builder. In Rust transform mode, historianRun and waitingMessages are missing, because the TypeScript historian does not run.
Refresh
A 200 ms debounce on host events, and at most one read each second while a step streams.
One read in flight at a time. After a failed read, the panel keeps the last snapshot and waits 30 s.
A 2 s poll only while the historian, the dreamer or a recomp runs. This replaces the recomp poll loop.
loadSidebarSnapshot has a new opt-in throwOnFailure, so that scheduled reads can see a failure. Other callers do not change.
The OpenCode 2 text fallback sidebar read data during render. It now uses the same scheduler.
View
The header toggles (▼/▶). Collapsed, the panel shows one row for each section in words: Context (45% left), Historian (idle · 1h55m), Cache (151 drops queued), and Health (2 issues, in the warning or error tone) only when something is wrong. The collapse preference that MC already stores is now used.
The Health wording follows the shared CortexKit sidebar standard: stat rows in the warning or error tone, no glyphs, and at most one muted hint row, /ctx-status shows details.
The context gauge is the shared SidebarGaugeRow: the warning tone at the session's execute threshold and the error tone at 85%.
An absent value shows —. An unknown usage shows no reading, not 100%.
Removed: the token legend, Hygiene, the dreamer backlog row for each task, the second dreamer progress line, and Stats · Total tokens.
The existing section preferences still apply: historian to History, status to Prompt cache and the notes rows, memory to Memories, dreamer to Dreamer. The preferences file format does not change.
With compaction off, Context shows the native window pressure, and History, Prompt cache and the historian Health rows are not shown (collapsed too).
Build (decision 3)
scripts/build-tui.ts bundles @cortexkit/opencode-sidebar (it ships TSX source) with the Solid transform bound to the host runtime ids, plus a raw variant. This is the same method that anthropic-auth uses. The script change is +69/−5 lines, and the two bundles are 28,792 B (raw) and 29,058 B (host).
bun.lock changes for the dependency swap. bun install on Bun 1.4.4 also wrote the current workspace versions (0.49.35 to 0.49.52) into it.
@cortexkit/opencode-sidebar 0.2.0 declares OpenTUI >=0.4.5, so the plugin now declares @opentui/core and @opentui/solid peers >=0.4.5 and development dependencies ^0.4.5 (lock: OpenTUI 0.4.2 to 0.4.5 and bun-ffi-structs 0.2.3 to 0.2.4). A test fails when the plugin's floor is below the shared sidebar's floor.
One risk: the committed bundle depends on the Bun version. Bun 1.4.0 renames one shadowed local, and Bun 1.4.4 does not. CI uses bun-version: latest, which is 1.4.4 now, so check:tui-compiled passes. A later Bun release can change the output for each contributor. A pinned Bun version in CI would prevent this.
Not in this PR
Rust historian. It records no data for each run. Only the TypeScript historian records it.
Tests
New tests: the historian attribution cases (host switch, repair, explicit fallback, invalid fallback output, unknown producer), the snapshot fields with their query plans, the refresh scheduler, the OpenCode 2 fallback without render reads, and the render cases of the compiled slot (healthy, incident, older snapshot, compaction off, preferences, unknown usage, recomp row, errors, gauge tones, theme change, raw frame bundle, badge contrast, collapsed). The render snapshots compare every cell of every row, trailing spaces included.
The collapsed render test failed before the collapsed view (13 pass, 1 fail) and passes after it (14 of 14). Against the previous expanded and incident renders, the only differences are the header toggle and one blank separator cell's foreground colour inside the shared gauge row, which draws nothing.
Review fixes (86c8aec, one commit for the 12 review findings): each code fix has a test that failed before it and passes after it (snapshot counts and the tag-number join, the stale waiting count, the native-window gauge, the drop-only queued count, the cumulative fallback row, stopped and failed rebuilds in Health, stale .js cleanup, the fixture clock and the preference writer in the render script).
Bun 1.4.4 on head 86c8aec: typecheck passes, Biome passes on the changed files, build:tui twice writes no file, check:tui-compiled passes, and the focused TUI, sidebar RPC, compartment-trigger and memory-storage tests pass (188 of 188).
Full plugin suite, Bun 1.4.4, on head 86c8aec over master dba0c4f4e (bun test --parallel=2, otherwise the package's test command): 10,756 pass, 11 fail, 106 skip across 1,084 files. None of the 11 comes from this branch:
5: npm 12 returns an object from npm pack --json, and scripts/tui-pack-graph.test.ts and the 4 packed review-bundle tests read [0].files. They fail the same way without this branch.
6: apply-operations.test.ts (1) and scripts/calibrate-tokenizer/credentials-cli.test.ts (5, new on master) spawn process.execPath. My Bun 1.4.4 runner is OpenCode's embedded Bun, so process.execPath is not a plain bun. Both files pass on Bun 1.4.0 (10 of 10) and this branch does not change them.
Harness coverage (required)
For each entry select: covered / not applicable — surface does not exist there / NOT covered.
OpenCode 1: covered. Shared snapshot builder and TUI slot.
OpenCode 2: covered. Same snapshot builder; the text fallback uses the same scheduler.
Pi: not applicable. Pi has no sidebar.
OMP: not applicable. OMP has no sidebar.
Rust module: not applicable. The sidebar is TypeScript; in Rust transform mode the historian fields are missing.
Confidence Score: 5/5
The PR appears safe to merge, with the earlier non-blocking memory-count cost still present.