dsh-code Release Notes - 2.0.0
Overview
2.0.0 moves the terminal to the upstream @deepseek-ai/dsh@0.2.0-rc.2 line and eliminates the two most reported visual defects: persistent full-terminal flickering during streaming output, and stable blank rows appearing between the transcript and the composer after terminal resizing. The flickering fix wraps every Ink frame in DEC 2026 synchronized output so the terminal presents only final frame states, and the blank-row fix makes every terminal size change trigger a source-backed replay that fills the viewport budget. The release also adds a live git-branch status indicator, interactive hooks and LSP configuration panels, delegated subagent operations under a unified panel key grammar, a ratified plugin keystone lock, and comprehensive bilingual copy cleanup across every panel.
Upstream alignment to 0.2.0-rc.2
- Every Harness dependency moves to 0.2.0-rc.2 across two upstream releases (0.2.0-rc.1 and 0.2.0-rc.2, audited independently before each bump). The 0.2.0 line adds a shared OTel transport service (
dsh-otel), a timedask_user_questiontool,ToolCallRecoveryfor pending tool-result restoration on interrupted steps, and an opt-inpromptTailGraceMsfor terminal-bash — all additive, no breaking interface changes. - The base patch adds one
otelrow ahead ofsession-telemetry-otel; the telemetry collector endpoint and export batching change are invisible to the terminal surface (telemetry stays in FEEDBACK_ONLY mode). - The installation commands in both READMEs pin
@deepseek-ai/dsh@0.2.0-rc.2explicitly;/updatederives the host line from this release's own peer dependencies, so the launcher, the bundle, and profile plugins move as one aligned step.
Streaming flickering eliminated (DEC 2026 synchronized output)
- Ink's log-update erases and rewrites the entire dynamic region on every rendered frame with no line-level diff — during streaming that is roughly thirty full-area rewrites per second. A frame-level diagnostic probe confirmed the worst case: when the Yoga root height reaches the terminal rows, Ink's internal clear-all path fires up to fifty-seven times per second, each write reprinting the full Static backlog.
- The fix wraps the stdout stream Ink renders through in a synchronized-output delegation object: every frame that contains ANSI erase sequences is concatenated with the DEC 2026 begin (
\x1b[?2026h) and end (\x1b[?2026l) sequences into one atomic write. The terminal buffers the intermediate erase-then-partially-rewrite state and presents only the final frame — flickering becomes structurally invisible. Terminals without DEC 2026 support silently ignore the sequences (graceful degradation, no functional impact). - The wrapper is a plain object, not a Proxy: a Proxy's
Reflect.getre-entered Node's stdout getter chain causing stack-overflow crashes, and splitting the wrap across multiple sequentialwrite()calls re-fired the completion callback three times crashing React's scheduler with "Should not already be working." Both failure modes are documented in the newAGENTS.mdto prevent reintroduction.
Blank rows after resizing fixed
- The transcript viewport's flush cursor only moves forward. When the terminal grew taller without a column change (dragging a VS Code panel divider or a tmux pane), the budget grew but the cursor stayed — the anchored Box padded the gap with real blank rows between the transcript tail and the composer until enough new content settled to fill the space.
- Any terminal size change (rows or columns) now triggers the same clear-and-replay path after a 75ms debounce settles the burst: Static remounts, the flush cursor resets, and the settled history re-flushes to fill the new budget. The replay is coalesced — one rapid resize burst produces exactly one replay.
- The anchored surface height additionally clamps to its content row count (
min(dynamicRows, allLiveLines.length)), so the Box never pads below its own content even in the one-frame window before the replay lands.
Live git branch in the status bar
- The status bar's branch badge now follows external checkouts without a restart. A
fs.watchwatcher on the repository's.gitdirectory observes HEAD changes (branch switches and detached-head moves), debounces re-reads to 150ms, and publishes through a subscribe store the status line reads viauseSyncExternalStore. Codex's terminal refreshes its branch only at turn boundaries; this implementation stays live during idle periods too. Outside a repository or on a detached HEAD the badge stays absent without spinning up a watcher.
Interactive hooks and LSP configuration panels
/hooksopens a full interactive panel over the host's hooks service: list every registered hook (Claude-code and Codex bridge types with their lifecycle events), toggle enable/disable in-memory for the session, and open the underlying settings file for durable edits. The panel follows the unified key grammar: Enter drills in, Space toggles (safe, reversible), Backspace×2 arms destructive actions,rrefreshes./lspmanages language servers: list configured servers with their transport and status, install from the catalog, remove with the two-press destructive arm, and view live diagnostics. The panel surfaces the install command it would run so the user can reproduce it manually.
Delegated subagent operations and the unified panel key grammar
/agentsgains full send-message and interrupt operations on continuable subagents: select any listed agent, compose a follow-up prompt, and deliver it without leaving the panel. Every interactive panel now follows one ratified key grammar — Enter for primary action, Space for safe reversible actions, Backspace pressed twice for destructive operations,rfor refresh, esc/q to close. No letter shortcuts exist for actions expressible by these five.
The ratified plugin keystone lock
KEYSTONE_MODULESpins exactly the 44 module names this bundle's own patch declares plusllm-pi-ai. A drift test walks the base patch, the bundle patch, and every preset file, verifying that the lock's notion of "what we consider load-bearing" matches what the patches actually name. When upstream adds or removes base rows, the drift test fails with a precise diff, and updating the lock is a deliberate two-file change (the lock plustests/base-row-ids.json).
Bilingual copy cleanup
- Roughly sixty Chinese keys and one hundred thirty-five English spots across every panel were rewritten against the kernel's official Chinese READMEs for terminology: 子代理 for subagent, 委派 for delegation, 钩子 for hooks, 压缩 for compaction, 权限预设 for permission presets, 谱系 for lineage, 转录 for transcript, 实机 for real-machine verification. A banned-word list (dramatic metaphors, slang, military language) is now enforced by convention and documented in
AGENTS.md. - Commit messages, log entries, and panel copy follow the same bilingual standard: an English paragraph followed by a semantically mirrored Chinese paragraph, keeping code identifiers and technical terms in English within Chinese prose.
Development documentation
AGENTS.md(new, 272 lines, ten sections) records every operational rule learned in this release cycle: real-terminal boot tests after stdout changes (a 1282-test suite passed while the TUI crashed on boot), poll-based timing assertions (fixed sleeps flake under parallel load), the columns-minus-one anti-autowrap contract, the stdout callback single-write rule, Proxy getter recursion, the flush cursor's monotonicity, version-alignment procedure (including the session-safe CLI upgrade order), fourteen tool-call discipline rules each drawn from a real error, and the open-issue registry that prevents re-investigating known defects.
Rendering and layout fixes
- The
/searchpanel renders each result as two physical rows (a main row plus a match snippet); the viewport now slices by that geometry so short terminals stop overflowing the panel beyond its maxHeight budget. NoticeLinetruncates atcolumns − 3instead ofcolumns − 2, honoring the codebase-wide contract that no painted line exactly fills the terminal width (VS Code auto-wraps on the exact last column, producing an unaccounted row).- The streaming tail's body-column budget reserves one safety column so CJK kinsoku overhang (which can paint up to two cells past the column limit on a filled row) stays within the terminal edge.
- The branch watcher's test poll extends to an explicit 15-second timeout so it never races vitest's default 5-second timeout under parallel suite load.
Compatibility and upgrade notes
- The supported host line is
@deepseek-ai/dsh@0.2.0-rc.2; an older-line host refuses the new bundle at boot by design, so the install commands pin the host version. - Sessions and configuration from 1.x migrate in place; no manual steps are needed. The settings-migration safety net from 1.6.0 (stranded
settings.yamlre-import) continues to cover upgrades from 0.1.x hosts. - npm remains the primary channel; the GitHub Release tarball remains the fallback for restricted networks. A tag push creates the GitHub Release asset but does not run
npm publish. - The
lite-preset/directory (local-only development preset) is not part of this release; the keystone drift test tolerates its absence.
Upgrading from 1.6.0:
npm install -g @deepseek-ai/dsh@0.2.0-rc.2 dsh-code@2.0.0
dsh plugin --profile cli add dsh-code@2.0.0Close all running dsh sessions before the global install (replacing the files under a live process causes a read EIO crash on the TTY stream — the same rule is in AGENTS.md §3.2).