# Changelog > Full release history of dsh-better-sidebar (see the [README](README.md) for the latest release; also mirrored on [GitHub Releases](https://github.com/omdsh-dev/DSH-better-sidebar/releases)). > > πŸ“ The Chinese [CHANGELOG](./CHANGELOG.md) is the authoritative, item-by-item record; this file carries the English summaries and has historically lagged behind it. Where the two disagree, the Chinese one is current. ### v0.28.0 - **Stable plugin support moves to DSH 0.2.1-rc.1+**: all 13 DSH peer floors are `^0.2.1-rc.1`; development dependencies, CI and installers pin the exact RC baseline. No runtime compatibility layer or feature implementation changes are introduced. - **Retain the published alpha's functionality**: the upstream alpha.5 β†’ rc.1 full source-tree diff contains 355 files, all `package.json`, with no source changes. This release updates versions, dependency declarations and documentation only. Companion versions are Cordis peer `^4.0.5` / dev `4.0.5`, group `1.0.5`, schemastery `~3.18.5`, and unchanged include `1.0.10`. - **Move to npm `latest`**: v0.28.0 will take over the plugin's `latest` tag through the existing stable-version workflow; no workflow change is needed. **DSH's own `latest` still points to 0.2.0-rc.2 and cannot run this release**: pin plugin **v0.26.1** on that host. DSH 0.2.1-alpha.5 users pin **v0.28.0-alpha.0**; earlier support-line guidance is unchanged. The user explicitly accepted the independent tags' compatibility risk. - **Fresh RC validation**: see the [DSH 0.2.1-rc.1 adaptation record](docs/plans/2026-10-17-dsh-0.2.1-rc.1-adaptation.md). Incomplete checks remain pending; test counts from the alpha release below are not reused as RC evidence. ### v0.28.0-alpha.0 (prerelease) - **Support moves to DSH 0.2.1-alpha.5+**: 13 DSH peers use `^0.2.1-alpha.5`; development dependencies, CI and installers pin the exact baseline. DSH 0.2.0-rc.2 users should pin npm-published **v0.26.1** (v0.27.0 has not been published). Install this alpha explicitly as `dsh-better-sidebar@0.28.0-alpha.0`, not `latest`. - **Native Connection admission**: HTTP/WS routes delegate to `ctx.connection.requestRejection(req)`, without the removed `webRuntime` service or plugin-owned trust exceptions. - **Runtime invariants integration removed**: upstream removed `@deepseek-ai/dsh-invariants` and `/invariant` subpaths; the plugin removes its companion, types and package entry. Compaction seed coverage retains the public surface fold and seed validator. - **Current working directory**: files, Git, terminals and new side chats follow session `working_directory`; live reads use workingDirectory, cold reads use sessionQuery all projections with lease disposal. Linked worktrees stay base-keyed; each new sidechat seed ends with a current-cwd stamp. - **Companion API / dependencies**: supply `TerminalBlockLabels.commandLine(line)` in both consumers and synchronize all 21 dictionaries; update Cordis `4.0.5`, schemastery `~3.18.5-alpha.2`, group `1.0.5-alpha.2` and include `1.0.10`, without a direct loader pin or overrides. Alpha.5 prefers package `engines.dsh` for compatibility; this plugin leaves it absent and retains its 13 DSH peers. - **Validation record**: [release integration validation](docs/plans/2026-10-17-alpha-release-validation.md) records 221 files / 2681 passing tests in single-worker mode, real alpha.5 mounting, and all three Context integration variants. The [initial adaptation record](docs/plans/2026-10-16-dsh-0.2.1-alpha.5-adaptation.md) preserves earlier adaptation evidence. - **Git Graph fills the available height**: remove the `min(60vh, 460px)` cap and use the space between the changes area and the fixed footer. The graph adapts when the diff preview opens or the window resizes; long histories scroll within the graph, while large pending-change lists retain outer-page scrolling rather than stretching the whole page. - **Optional dsh-context integration**: when the Context sidebar entry is available, provide diagnostic shortcuts in agent details, background-job output windows and the session header. Background jobs target only their explicit real owner, without changing the session semantics of output observation or stop commands. Cross-session navigation selects the target before opening Context and reuses an existing tab in another pane; further user navigation cancels the jump, with a 10-second maximum wait. Hide the entries when dsh-context is absent, disabled or exposing only session tabs. Do not modify context, resume agents or change dsh-context preferences; all entries share one optional Cordis-scoped adapter with no idle polling. ### v0.27.0 - **One Git page with Source Control and a read-only commit graph**: collapsible Changes and Graph sections, independent Merge / Staged / Changes groups, commit-message drafts, and a persistent branch/sync footer. The bounded graph reader supports branch filtering, lane connections, reference badges, hover details and commit inspection without checking out branches or changing refs. The shared preview keeps three tabs: Commit details / Content / Diff. Based on [#944](https://github.com/omdsh-dev/DSH-better-sidebar/pull/944) by @hairyf and [#969](https://github.com/omdsh-dev/DSH-better-sidebar/pull/969) by @vlln; the official lens tabs from [#939](https://github.com/omdsh-dev/DSH-better-sidebar/pull/939) by @hairyf remain, with no third graph lens. - **The page is now named Git** in all 21 dictionaries ([#968](https://github.com/omdsh-dev/DSH-better-sidebar/pull/968), @vlln). Staged and unstaged line counts remain separate for partially staged files ([#909](https://github.com/omdsh-dev/DSH-better-sidebar/pull/909), @UNscientific-9). Fixed an integration regression where selecting a change row moved it outside its group and drew a stray border; real-host browser coverage now checks all three groups at two desktop widths. - **Repository discovery and ownership handling**: bounded BFS discovers nested independent repositories even when the workspace root is a repository ([#607](https://github.com/omdsh-dev/DSH-better-sidebar/pull/607), @iasiv5). A dubious-ownership failure retries only the named repository for that command, without writing Git configuration or trusting `*` ([#827](https://github.com/omdsh-dev/DSH-better-sidebar/pull/827), @yanzhaohui1999). - **Session file operations**: resolve paths from the session cwd ([#886](https://github.com/omdsh-dev/DSH-better-sidebar/pull/886)) and include PTC file calls ([#921](https://github.com/omdsh-dev/DSH-better-sidebar/pull/921)), both by @wang-kaopu; show one operation per row in reverse chronological order ([#940](https://github.com/omdsh-dev/DSH-better-sidebar/pull/940), @hairyf). Explicit cursors and long polling avoid repeatedly replaying an unchanged event window ([#885](https://github.com/omdsh-dev/DSH-better-sidebar/pull/885), @wang-kaopu). - **Side Chat seeds from the latest summarizing compaction**, instead of copying the full parent log, while preserving the required event structure ([#964](https://github.com/omdsh-dev/DSH-better-sidebar/pull/964), @NolanHo). - **Validation**: parsed CSS selection guards, mounted Git layout/graph regressions, and isolated scroll-retention fixtures. DSH support remains **0.2.0-rc.2+**; no host compatibility-layer changes are included. ### v0.26.1 - ⚑ **Steady-state cost fix β€” the "it was fine on 0.24, then it got slow" regression** (community reports: the sidebar turns laggy after being open for a long time in the foreground, and DSH turns laggy after many conversation turns; restarting DSH clears it for a while). An artifact-level A/B puts the dividing line at **v0.25.0**, and v0.26.0's git-polling fix never touched the mechanisms below. Full method and every measurement: [docs/plans/2026-10-15-perf-steady-state.md](docs/plans/2026-10-15-perf-steady-state.md). - **The two 500 ms timers introduced in v0.25 are now visibility-gated** (measured 0.5 β†’ 9.7 ms per tick, up to ~3,500 forced-layout reads per second, and they ran **whether or not anything was visible**): the host right-panel open state is now one module-level shared poller (refcounted; the timer is destroyed while the page is hidden and the value is re-read on show), and the blank-session fallback entry only builds its realign timer while it **actually renders** (the interval used to be registered before the component's early returns, so it ran while rendering `null`), with the retreat walk capped at 100 steps. Same batch: the `settings-nav-icon` body observer is rAF-coalesced and lost `characterData` (it fired for every streamed token in the main conversation), `use-center-column`'s 1.5 s safety net is armed only while no column is cached, and `session-phase` stops fetching while hidden and never re-renders on an unchanged answer. - **Per-tab work became per-session work**: `use-session-root` is now a refcounted per-session store β€” one 5 s timer, alive only while at least one **visible** subscriber holds it, paused while the page is hidden and refreshed immediately on show/focus/un-park. Native tab bodies pass their real `visible`; the file tree's directory watch and the side-chat push are gated the same way. **Previously every visited tab permanently added "one `git rev-parse` subprocess plus a 4,000-event rescan every 5 s"** β€” that resolver sits behind 12 `/sidebar/*` routes and measured 9.6–44.7 ms per cache miss. - **`keepMounted` keeps its semantics but is now bounded** (switching away still preserves scroll, drafts and expansion state): the git-status slot registry releases and prunes, the per-session ops stream is LRU-bounded while keeping its cursor (a parked tab still folds the write it missed), and `bySession` / `previewScrollMemory` / `commitDrafts` / `pendingAnchors` are all bounded. The client no longer re-copies the whole session-state map on every store notify, and the 1 s jobs clock no longer rebuilds the whole row list every tick. - **Host side**: the cwd resolver gained in-flight dedupe and a 15 s TTL (against a 5 s client poll, the old 3 s TTL meant every poll missed), the two whole-repo `--numstat` diffs now carry a pathspec (zero diff processes on a repo with a large unignored tree) plus an 8 MiB output cap, the single-layer `fs.tree` route got a probe gate and the fold probe is capped, cold side-chat threads are memoised, the agent-open queue is capped, and plugin teardown now closes accepted WebSocket connections. - πŸ“Š Measured in the real mount lane (4 visited tabs, 15 s idle window): live 500 ms timers **3 β†’ 1**, live 5 s timers **5 β†’ 0**, `session.cwd` requests **15 β†’ 0**, `elementsFromPoint` hit tests **1,620 β†’ 0**, forced layouts **5,190 β†’ 30**. Per-tick CPU in a Chromium microbenchmark: **0.02–0.32 ms β†’ 0.001 ms** (up to 9.7 ms in the worst crowded case). - πŸ§ͺ New `tests/steady-state.spec.tsx` cross-module gate (no recurring timer while nothing is visible, no layout reads, no requests, one poller per session) plus an explicit inventory of every client `setInterval` site, and a `steady-state` metric with two hard assertions in the perf lane. ### v0.26.0 - ⚑ **git status polling no longer drags the UI down** (follow-up to [#212](https://github.com/omdsh-dev/DSH-better-sidebar/issues/212)): one fetch is `git status --untracked-files=all` plus two whole-repo `--numstat` diffs β€” four processes β€” and on a repository with a large UNIGNORED tree (a nested `node_modules`, a package-manager store) that measured **59,631 entries / 9.24 MB / 0.131 s**, with the 2,000-entry cap only applied AFTER the whole output had been produced and parsed. Three changes together: - **the editor's gutter no longer owns the poll** β€” it passed the feature switch where the tab's visibility belongs, and that parameter is what starts the timer; with native tabs kept mounted, opening any file kept the scan running every 2.5 s for the rest of the page's life. It now reads the shared answer and refreshes only on a file switch or a save, and holds no subscription at all while its tab is parked. - **the baseline is 30 s, with automatic back-off** β€” a round trip over 750 ms / 2 s / 5 s drops the slot to 60 s / 180 s / no automatic timer, a fast answer climbs straight back, and event-driven refreshes (stage, commit, discard, save, manual refresh) stay immediate. - **idle polls notify nobody** β€” the store's version counter is the `useSyncExternalStore` snapshot, so bumping it on every tick re-rendered every consumer once per cadence even when nothing had changed. - Linked-checkout inventory counts now use a single cheap command instead of a full `status()` per checkout; an untracked DIRECTORY counts as one entry there (it used to expand to every file, and the number was silently capped anyway). - πŸš€ Plus everything else in v0.26.0: fully streamed transfers with the three deployment caps removed, a read-only paged preview for very large files, a fourth built-in viewer for `.jsonl` / `.ndjson`, Side Chat live push + visible queue + per-thread model selection, multiple bottom-workbench terminal tabs, the rebuilt Tasks panel, native-right-sidebar rename/delete reconciliation, and several silent data fixes. See the [Chinese CHANGELOG](./CHANGELOG.md#v0260) for the full list. - **Oversized-file preview is now append-on-scroll, the way VS Code's log view works**: the first cut used explicit previous/next paging (each new page REPLACED the current one). Now reaching the bottom pulls the next window and keeps everything already read β€” line numbers continue across page boundaries, and "load all" is simply append-until-EOF. The walk has to be sequential: each page's `offset` IS the previous response's `nextOffset`. The too-large notice grew a "load all" button (showing byte progress while it works, disabled once EOF is reached). Its companion **row ceiling** (`MAX_PREVIEW_ROWS = 50_000`) is the one thing VS Code does not need and this does: its `TextModel` and view are virtualized, so a gigabyte in the model still means a small DOM; this renders real elements. At the ceiling the preview stops and points at the download β€” a visible cap beats a frozen tab, and it sits far above any log a person reads in a side panel (the 6 MB reference log is ~35k records). The ceiling lives in the reading hook rather than the view's own RENDERED row count, because while `loadAll` walks pages in an `await` loop React has not re-rendered: deciding from a stale number overshoots by several pages. The new spec caught exactly that β€” the worker OOMs in ten seconds. > Upload, download, the HTML preview and archive packing all stream now, and there is no size limit left anywhere. The three deployment limit keys `readLimit` / `mediaLimit` / `uploadLimit` are gone with them (a retired key left in an old profile is silently ignored as an unknown field β€” no migration needed); `listLimit` stays. - **Downloads have no 20 MB cap and the host's memory no longer grows with the file**: `/sidebar/file` and `/sidebar/html` went from "`readFile` the whole thing β†’ `res.end(buffer)`" to `createReadStream` + `pipeline`. The load-bearing detail is WHEN the header is written β€” the stream's `open` event is awaited first, so an unreadable file (ENOENT/EACCES/directory) still produces the plugin's JSON error envelope instead of a 200 with an empty body; a failure after that point (the file changed under us, the peer went away) destroys the socket. `writeJson` gained a `headersSent` guard so a route's catch block cannot throw `ERR_HTTP_HEADERS_SENT` on a committed response and turn a truncated download into a crashed request. See `tests/file-stream-route.spec.ts` (a real 21 MiB `node:http` transfer, a mid-stream abort, an unreadable path). - **Upload's two 128 MB gates are gone**: upload was already streamed end to end (`for await (chunk of req)` β†’ temp file β†’ atomic rename); what stood in the way was the client's `MAX_UPLOAD_BYTES` pre-check and the host's per-chunk `limit` check. The streaming core, the failure cleanup and the "never leaves a partial target or temp file" guarantee are untouched. - **Packing is the system's job now, not a ZIP writer of ours**: the hand-written 300-line encoder (`src/zip.ts`, with its `ZIP_MAX_BYTES` / `ZIP_MAX_ENTRIES`) is deleted in favour of probing and running the system `zip` (always with `-y` β€” without it Info-ZIP FOLLOWS symlinks and an `a -> .` loop recurses forever) or `tar` (Windows 10 1803+ ships `bsdtar` and no `zip`). The tool's stdout goes straight into an OS temp file, never into memory, and the download streams it out before the file is deleted. `archive.status` lost its per-entry `done/total` (a child process reports none) and shrank to `{ state, bytes }`; with neither tool installed `archive.build` answers the new wire code `unavailable` and the UI shows an actionable sentence. The client download went back to a plain `` link β€” a Blob would hold the whole archive in the tab's memory and undo the host-side change. - **Saving no longer has a 1 MiB wall**: `fs.write` rode the JSON API, whose request body is capped at 1 MiB, so a file the editor could open could not be written back. Saving moved to the raw `POST /sidebar/write` route (the document IS the request body); the encoding and line-ending restore (#523 / #871) is carried over unchanged and still pinned by `tests/fs-write-route.spec.ts`, now with a case far past the old ceiling. - **Oversized files get a read-only PAGED preview instead of a truncated editor**: `fs.read` went from "whole file or a truncated prefix" to three outcomes (`text` / `binary` / **`too-large`**). Above the editable threshold (5 MiB) a text file is no longer handed over as half a document β€” it answers `too-large` with a 4 KiB sniff head, and the client renders a paged read-only preview (a monospace line list, the log-viewer shape) with a download escape hatch. Oversized BINARIES are unaffected (the media path is the same as before). `FileViewerProps.truncated` and the `truncation` copy key are both deleted β€” "half a document that looks editable" is itself the defect #732 was about. The new `fs.readPage` pages by byte window, trimmed back to the last line break so a page is a whole number of lines; a single over-long line is returned as-is rather than as an empty page (otherwise "next" would sit on an offset that never advances). - **`.jsonl` / `.ndjson` are supported**: they had no highlighting at all (`lang.ts` had no mapping), and mapping them to `json()` is not free β€” `@lezer/json` accepts ONE top-level value, so a 400-record file parses into **402 error nodes** (measured). There is now a hand-written per-line JSON tokenizer (one line at a time, no cross-line state, an unterminated string marked outright) behind a fourth built-in viewer, `jsonl`: the PREVIEW is one collapsible card per record (collapsed to a one-line summary, expanding into the host's `JsonTree`) and the EDITOR is the same saveable CodeMirror, behind the preview/edit toggle. Line references (`a.jsonl#L131`) still flip to the editor and land on the line. - **Fixed an invisible error marker**: `tags.invalid` was `#ffffff` in BOTH schemes β€” white on white in the light theme. It is the token a JSON/JSONL syntax error lands on, and the reader is supposed to be able to see what did not parse; it is now a red wavy underline. - **A `stat` failure is a 400 now, not a 500**: the two byte routes let the raw rejection escape as an "internal" 500, which reads as a plugin fault. An unreadable user path is a 400. - **Paged-read encoding consistency (a regression only a real file would surface)**: the first implementation locked the encoding page 1 sniffed, so a "long ASCII prefix + GBK body" file decided UTF-8 from its first page and decoded the GBK tail into mojibake β€” while a whole-file read had always decoded it correctly. Only WIDE encodings are locked now (UTF-16/UTF-32 is a property of the file; a page that happens to hold no NUL bytes cannot re-litigate it) while byte-oriented encodings are sniffed per page (a pure-ASCII page decodes identically either way). That is safe because pages are line-aligned: `0x0A` can be neither a UTF-8 continuation byte nor a GBK trail byte. Both directions are pinned in `tests/fs-read-page.spec.ts`. - New guards: `tests/file-stream-route.spec.ts`, `tests/fs-read-page.spec.ts`, `tests/jsonl.spec.ts`, `tests/jsonl-language.spec.ts`, `tests/archive-tools.spec.ts`, `tests/archive.spec.ts`, `tests/editor-large-file.spec.tsx`. `tests/zip.spec.ts` was rewritten as `tests/archive.spec.ts` alongside the writer it tested; the single real-binary case self-probes and skips when neither tool is installed (so the Windows CI runner without `zip` does not go red). - Design notes, the comparison against VS Code Web (including the correction that **VS Code does not paginate** β€” its model is threshold + confirmation + turning tokenization off) and the record of implementation deviations live in [docs/plans/2026-10-11-streaming-transfer-jsonl-preview.md](docs/plans/2026-10-11-streaming-transfer-jsonl-preview.md). ### v0.25.0 > πŸ“¦ **Support line moved: DSH `0.2.0-rc.2+` only** (peer floor `^0.2.0-rc.2`). npm's `latest` for DSH is exactly 0.2.0-rc.2 now, and this release pins it. **Hosts on 0.2.0-rc.1 or older (including the 0.1.7 line) should pin v0.24.1** β€” `^0.2.0-rc.2` evaluates false against rc.1, so the host's startup compatibility preflight disables the whole row silently. Declaration and pin change only: zero runtime changes. > > πŸ™ Carries work from several contributors (listed in merge order in the [Chinese CHANGELOG](./CHANGELOG.md#v0250)). This English file was not updated for v0.25.0 at the time. ### v0.24.1 > 🎁 **This release carries everything since v0.22.1** (the Files/Changes UI-UX rebuild and performance work of v0.23.0, the DSH 0.2.0-rc.1 support-line move of v0.24.0, and the fixes below). > > 🐞 **Fix release** for the file tree rebuilding itself on every interaction. (1) The native surface (`sync()` in `src/client/native/index.ts`) treated the `files` takeover as a surplus registration β€” it is keyed by KIND and never appears in `wanted`, so **every session-state write** disposed and re-created it. Replacing a slot registration replaces the host's slot entry, which **unmounts and remounts the whole plugin tab body** (folder toggles, tab switches, bottom-workbench drags all trigger it): the explorer lost its level cache, scroll position and directory watcher, then re-listed the entire visible tree via `fs.trees([root, ...expanded])`. (2) The live-refresh path (`retryDir` in `FileTree.tsx`) **deleted a level from the cache** before refetching, so its rows were replaced by a placeholder and rebuilt (a build, a formatter or the model's own bash made entire folders blink). - πŸ§ͺ Discriminating evidence: a new real-host lane `tests/e2e/zz-expand-refresh.e2e.ts` (a toggle asks only for the toggled level, the tab body survives, rows never drop to zero; reverting either fix turns it red) plus two jsdom guards (reverting the surface fix: `registered` 6 β†’ 12 with every `disposed` entry being `dsh-better-sidebar:files`; the other asserts stale rows stay on screen during a re-list) - πŸ“‰ Measured after the fix (40-child directory): the toggle's `fs.trees` payload goes from `[root, big]` (the empty-cache signature) to **`[big]`**, new WebSockets 1 β†’ **0**, removed rows 45 β†’ **0**, and an external write goes from "remove 40, add 40" to **one added row** - πŸ”§ `src/fs-watch.ts` / `use-dir-watch.ts` / `TreePanel.tsx` / `tab-adapter.tsx` untouched (`fs.watch` does not emit on creation β€” measured and ruled out) ### v0.24.0 > πŸ“¦ **Support line moved**: DSH **0.2.0-rc.1+** only (peer floor `^0.2.0-rc.1`, CI pins `@deepseek-ai/dsh@0.2.0-rc.1`). **The 0.1.7 line (including npm `latest` = 0.1.7-rc.2) should pin v0.22.1 (the last published release on the 0.1.7 line)** β€” the 0.2.0 host's startup compatibility preflight evaluates `semver.satisfies(hostVersion, peerRange, {includePrerelease:true})`, and a caret never spans a minor bump (measured: `^0.1.7-rc.1` vs `0.2.0-rc.1` is **false**, so the whole row is disabled silently). This version therefore keeps no 0.1.7 compatibility branch. - πŸ“¦ **Baseline lifted to `0.2.0-rc.1`**: 14 DSH peers and 27 `@deepseek-ai/*` devDependencies move together; `dsh.plugin.json`'s `engines.dsh` follows; the mount lane (`scripts/e2e-common.sh`) and CI pins follow. - πŸ” **Measured before adapting (purely additive, hence no runtime change)**: across the 19 host packages this plugin consumes, **zero value exports were removed**; the only type-surface changes are `dsh-client-ui-primitives` (optional props on `DisclosureRow` / `TextShimmer` / `Tooltip`, overlay top inset), `dsh-session` (new `ToolCallRecovery`) and `dsh-api-remotes` (a new product-analytics remote); session format stays **v4** (no migration package), `SUBAGENT_DESCRIPTOR_VERSION` stays **3**, `dsh/lib/bin.js` (`dsh plugin` / `dsh web` / the ready line / `--version`) and the `dsh-client-modules` runtime are **byte-identical**, the web profile's client inject list is unchanged, and `ui-primitives`' bare imports are identical (no new test-time dependency). - 🧩 **Host web-profile composition changes, checked though unrelated**: desktop-only `product-analytics` / `desktop-product-telemetry` rows are added (disabled on the web profile); the already-disabled `time-context` / `schedule` / `ui-schedule` rows move into the optional `dsh-experimental-schedule-bundle`. The mount assertion "the host owns exactly one tab entry (terminal) and browser is absent" is unaffected. - πŸ§ͺ **Tests and rules**: the peer-shape assertion in `tests/market-manifest.spec.ts` now pins the current baseline tuple and records the "a caret never spans a minor bump" lesson (compatible with Β§3.4 item 12's "includePrerelease is more permissive" note β€” that permissiveness only holds inside one minor line). - ⚠️ **Only residue**: the optional `@huanlin/dsh-plugin-better-locale` integration (imported by no source file or test; detected structurally at runtime) pins `^0.1.0-rc.8`, and even its newest 0.4.3 pins `^0.1.5-rc.2` β€” both fail the 0.2.0 preflight, so `pnpm peers check` reports its 5 unmet peers. That is upstream not having adapted to 0.2.0, unrelated to this plugin's own 14 peers. ### v0.23.0 > 🧭 **Development-line version (never published to npm; its content ships with v0.24.1)**: the Files and Changes pages rebuilt end to end, plus a round of performance and interaction polish. The Files page gains **Ctrl/Cmd and Shift multi-select** (VS Code semantics), a batch action bar, **Git change coloring** (VS Code's add/modify/delete/conflict tones, directories get a summary dot), **new-folder creation**, reworked drag-and-drop upload, and **"Zip and download" on a multi-selection** β€” archiving became a host-side task with progress (`archive.build` / `archive.status`, per-entry callbacks, 4 concurrent builds, 5-minute TTL, sessionId-bound). "Open with" now keeps **both sources side by side**: the local associated apps the host detects (file manager / VS Code / Cursor / Zed / custom editors, SSH remotes included) plus the plugin's own targets, with a setting (`openWithPluginTargets`) to force coexistence; the context menu was restructured (only default actions at level one, the app list moves into a submenu). The Changes page is **rebuilt as a hierarchy tree** with both the Git view (working tree / staged / history) and the Agent view (per-session edits), and directory rows stage/unstage. Interaction and performance: `/sidebar/api/fs.tree` measured **22.8ms β†’ 3.8ms** (10k entries: one `readdir`, rows built only up to the cap, lowercase-comparison sort, 1.5s directory-level cache invalidated by writes and `fs-watch`), a new batch route `fs.trees` (mount/refresh goes from N+1 requests to one), opening the context menu no longer re-lists directories, and rows no longer re-render with every git poll. Also fixed: "Open to the Side" did nothing (it only wrote into the collapsed bottom workbench; it now routes by surface to the host's `openResource(preferNewPane)` and really splits). - ⚠️ **Behavior change (security-relevant)**: the **workspace path fence was removed** β€” the plugin's fs routes (tree/read/write/rename/remove/mkdir/media/HTML/upload/archive) no longer enforce containment and can read or write any path the host user can reach (OS permissions are the only limit); the `workspaceFence` setting and its notice row are gone. - 🧩 Reusable global components and shared stores were extracted (`IconButton` / `Chip` / `SectionHeader` / `Notice` / `ConfirmDialog` / `StatusBadge` plus a shared git-status store) under the skin contract (`tests/theme.spec.ts`); the ZIP writer is our own (no third-party dependency) and a non-latin1 download name no longer 500s (RFC 5987). - 🐞 Also fixed: the tree's expansion was reset when a file preview was closed and the tree came back (the native surface kept the expansion set per tab; it is now projected from **per-session state**). ### v0.22.1 > πŸ“¦ **Stable release** (npm `latest`): DSH **0.1.7-rc.1+** only (peer floor `^0.1.7-rc.1`, CI pins `@deepseek-ai/dsh@0.1.7-rc.1`) β€” the support line is **unchanged**, so 0.21.1 / 0.22.0 users can upgrade straight away; **hosts on DSH 0.1.6-alpha.2 or earlier still pin v0.19.1**. A patch release: both defects reproduced reliably on a real host while the unit suite stayed **green**. - πŸ› **The `files` takeover's tab-type id could be orphaned β†’ `native register files error: … already registered` spam plus the Files window falling back to the host's empty state until a page refresh** (community issues #770 / #771 / #766). The chain was confirmed verbatim from the desktop shell's own log (`desktop.frontdesk.log`, 2026-09-27 15:51:39.338): during an in-page client entry replacement (plugin-market update, Plugins page disableβ†’enable, HMR rebundle) the plugin's teardown unregisters each built-in descriptor one by one β†’ `service.notify()` (synchronous, inline) drives `sync()` on an **already inactive** plugin context; the drop loop released the `files` takeover β€” which is **not a descriptor** β€” and re-created it **in the same pass**: `tabs.register` lives on the **host** context (still alive) so it took the id anyway, while the `ctx.slots.inject` right after it threw `cannot create effect on inactive context`, `live.set(FILES_KIND, …)` never ran and the disposer was lost β€” the id became **unregistrable for the rest of the page's life**. Fix (community PR #777, @yanzhaohui1999): β‘  the drop loop **skips `FILES_KIND`** β€” the takeover lives and dies by the editor type's `wantsFiles/hasFiles` switch and the seat disposer; β‘‘ **any registration that fails after the host took the id releases that type and the slots already installed**, leaving only a failure the next notification retries; β‘’ teardown / drop loops release entry by entry, so one throwing release no longer aborts the rest. - πŸ› **macOS desktop: window drag worked once and then stopped, and the double-click-title zoom was gone** (issue #772). The plugin host is a direct `body` child, so the shell's `html[data-platform=darwin] body > :not(#root) { -webkit-app-region: no-drag }` (an id-bearing selector β€” only `!important` wins) applied to it, and app-region **ignores `pointer-events`** β€” the viewport-sized panel layer cancelled every `[data-window-drag]` strip beneath it. Fix (community PR #773 merged; #786 completed it): `[data-dsh-better-sidebar]`, `[data-dsh-panel-host]` (with `> *` staying `no-drag`) and the zoom modal `.mermaidModal` opt out with the **neutral** `-webkit-app-region: initial !important` β€” `initial` computes to `none`, which does **not** subtract from the drag region, whereas the literal keyword `none` is not neutral (it computes to `no-drag`). Interactive popups (`.selectionPopup`, `FloatingWindow`, `AnchoredPopover`) deliberately do **not** get `initial`, or a press would start a window drag instead (the #103/#111 regression). - βœ… **Guards**: `tests/native-registration.spec.ts` pins both invariants to the registry's **event log** (4/4 red on the unfixed code, and "a failed registration can still be retried" is no longer a non-discriminating assertion); slot failures are keyed by `name::key` so `registerSlots`' partial-release path is actually exercised; a **deployment-level regression gate** `tests/e2e/native-reload.e2e.ts` was added (`utimesSync` the installed `lib/client.js` β†’ in-page entry replacement β†’ assert no `native register … error`, no diagnostic strip, the Files takeover usable again; red on 3 consecutive runs against npm 0.22.0, green on 3 consecutive runs of the fixed build (it is ONE case, run repeatedly)). The drag contract is shape-pinned by `tests/panel-host-css.spec.ts` on Linux `pnpm test` (there is no macOS runner) and value-pinned by a real cascade probe in the mount lane that reads computed styles against the shell's own rules (both the panel host and the zoom modal red the unfixed artifact). - πŸ“ **Baseline unchanged**: `@deepseek-ai/dsh-*` still pinned to `0.1.7-rc.1`, peer floor `^0.1.7-rc.1`; no public API, contract, dictionary or chunk changes. Incident write-up (and why #766's "absorb" approach was rejected): [docs/plans/2026-09-28-native-files-takeover-reload-leak.md](./docs/plans/2026-09-28-native-files-takeover-reload-leak.md). - πŸ§ͺ **Verification**: `pnpm test` 122 files / 1293 passed / 9 skipped; `pnpm test:mount` and `DSH_CMD=… pnpm test:mount:aggregate` green; the packed bundle carries `app-region:initial!important` twice and `no-drag` twice. ### v0.22.0 > πŸ“¦ **Stable release** (npm `latest`): DSH **0.1.7-rc.1+** only (peer floor `^0.1.7-rc.1`, CI pins `@deepseek-ai/dsh@0.1.7-rc.1`) β€” the support line is unchanged, so v0.21.1 users can upgrade straight away. **Hosts on DSH 0.1.6-alpha.2 or earlier still pin v0.19.1.** The headline is the Tasks page rebuilt from a "subagent topology" into a **workflow graph**, then polished over several rounds of real-host feedback; along the way DSH 0.1.7 deleted the Agent Teams Remote methods, so both the team and background-job data planes were rewritten. - 🧩 **The Tasks page is now a workflow graph (the primary view)**: the session tree renders as layered nodes joined by bezier edges β€” drag to pan, wheel-zoom to the cursor, fit to the content box (double-click the background to refit), and a control cluster bottom-right (graph/tree toggle + fold switch + zoom + fit). The classic **indented tree is kept** (keyboard-navigable); both modes share one view model, so fold state and team enrichment never drift apart, and the default view is a setting. - πŸƒ **Two-segment node cards**: the top segment is the kind badge (main agent / subagent / teammate / workflow / completed aggregate) + phase badge + name + meta line; the bottom bar is the state dot + state word + **the same merged activity line the main agent shows** (concurrent tools grouped by category, counted, with the running call's detail β€” the wording comes straight from the host's `chat` namespace) + a fold chevron on finished nodes. A running bar is swept left-to-right across its **whole** width (1.5 s, token gradient, disabled under `prefers-reduced-motion`). Cards are 8px-rounded, hierarchy is carried by a faint top-segment tint only, and the session you are on wears a heavier accent border. - πŸ”€ **Workflow runs enter the graph**: the host folds `tool-workflow/*` session events into runs (the same events the official workflow-run panel folds); a run hangs under its origin agent, its member agents are re-parented below it and boxed per phase with matching phase badges. Members with no catalog row are synthesized from the run's own data, so a finished run still shows who took part. - πŸ—‚ **Folding is split into two groups that say what they hold**: `βœ“ N completed` (finished work plus failures, with failures called out as `N failed`) and `N idle` (teammates that finished a turn and can be called back at any moment) are **two separate rows**. The **manual chevron always works** (no threshold); the **automatic** rule only sweeps idle members once there are 3 or more of them β€” one or two waiting members are the team's working set, and their cards are where a task line shows up. An aggregate's name line reads "first two names + `+N`", and clicking it expands everything. - πŸͺŸ **Two persistent floating windows** (extracted into a reusable `FloatingWindow`): background-job output, and the shared task's detail/edit surface β€” draggable, resizable from every edge, with a self-scrolling body, dismissed only by its close button or Escape (an outside click, a blur or the anchor leaving the viewport never closes them). The task window hands its spare height to the description (or the multi-line editor), so enlarging it gives the content more room rather than leaving a gap, and the action row is pinned to the footer so a long description cannot push it out of sight. - πŸ‘₯ **Agent Teams board (experimental layer)**: team members enrich their matching nodes and an always-visible strip lists the roster and shared tasks; the task state machine follows the host (pending β†’ claim β†’ in progress β†’ complete β†’ reopen) with reassign / edit / two-step delete, and a stale CAS revision gets its own notice ("changed elsewhere β€” refreshed"). **Member activity is overlaid from `subagents.live`'s `running` flag** rather than guessed on the client. - πŸ”„ **Background jobs now read the host's client service**: DSH 0.1.7's web profile mounts a client-side `ctx.jobs` (a pushed roster plus a non-consuming output stream and `kill`), so the plugin deleted its own `jobs.list` / `jobs.output` / `jobs.kill` routes and the event-replay mirror β€” and never touches the model's `job_output` cursor at all (the host turned that constraint into a capability). Output streams in a persistent floating window with tail-follow, and the drawer auto-collapses once a tree has 8 or more agents. - πŸ›  **Two data planes rewritten for DSH 0.1.7**: β‘  team reads β€” upstream deleted the three `agentTeams.remoteView`-style Remote methods, so the plugin reads the Lead Session's **`agentTeam` Session projection** instead (the same snapshot `subagentCatalog` comes from, push-based), deleting the `teams.view` route and its 5-second poll; the write path keeps two routes (`createTask` / `updateTask`) whose rejections moved from a result union to a thrown `TeamError`, with `TEAM_TASK_STALE_REVISION` mapped to a 409 `team-conflict`. β‘‘ background jobs β€” see above. **The bug this fixed in the wild**: on 0.1.7 the team strip never rendered at all (the route answered `remoteView is not a function`, and the page degraded silently). - πŸ› **Four defects caught on a real host** (every one of them green in unit tests): the per-node fold button did nothing because it answered to the **automatic** rule's guards (non-member + leaf) while the button is a **manual** trigger β€” the two triggers now have their own guards; an **idle** card never drew a fold chevron at all (its render condition was hard-coded to done/error) β€” "not executing" now qualifies; claiming a task flipped its tag to **blocked** because `ready` only means "queued with every blocker done, clear to claim" and is false for every non-pending task β€” the rule is now the single `taskBlocked` / `taskStatusLabel` / `taskTone` / `taskDotState` pair; and "complete" on a queued task always failed (the host state machine demands a claim first), so the action row now follows the state. - 🎨 **Narrow panes and mobile settings**: type scale, row heights and spacing were re-tuned for the native right sidebar's narrow width (content-box centering, zoom clamping, pan bounds, phase-frame colours, thicker edge stubs, a recomputed card height budget); the settings page gained a **Mobile** group β€” on a narrow viewport (≀768px) the Tasks page no longer auto-opens and it defaults to the tree view. - πŸ“ **Baseline**: the DSH pin and support line are **unchanged** (0.1.7-rc.1, peer `^0.1.7-rc.1`). New copy keys (including `teamTaskClaim` / `tasksFoldIdle` / `taskWindowCreate` / `tasksStateProvisioning`) across all 20 dictionaries; design docs: [docs/plans/2026-09-14-tasks-graph-workflow-teams-design.md](docs/plans/2026-09-14-tasks-graph-workflow-teams-design.md), [2026-09-27-tasks-graph-polish.md](docs/plans/2026-09-27-tasks-graph-polish.md), [2026-09-27-floating-window-mobile-settings.md](docs/plans/2026-09-27-floating-window-mobile-settings.md), [2026-09-27-agent-teams-dsh-0.1.7-adaptation.md](docs/plans/2026-09-27-agent-teams-dsh-0.1.7-adaptation.md) (implementation deviations and known limits included). ### v0.20.0 (dev line, **never published**) > 🚫 **This version never reached npm** (npm `latest` is still v0.19.1): it was meant to be the stable release on the 0.1.6-alpha.2 line, but the 0.21 line replaced it before release, so everything below actually shipped in **v0.21.1**. Its original positioning: supports **DSH 0.1.6-alpha.2+** only (peer floor `^0.1.6-alpha.2`, CI pins `@deepseek-ai/dsh@0.1.6-alpha.2`). **This is a breaking-change release**: hosts on 0.1.5-rc.* should pin `dsh-better-sidebar@0.19.1`. See [docs/plans/2026-09-21-dsh-0.1.6-alpha.2-adaptation.md](docs/plans/2026-09-21-dsh-0.1.6-alpha.2-adaptation.md) for the adaptation record. - πŸ–₯️ **Terminal handed to DSH's built-in**: DSH 0.1.6 ships its own right-column terminal (`ui-sidebar-terminal`), so the plugin deleted its whole terminal stack β€” `pty-manager` / `agent-pty` / `pty-deps` / the 8 `terminal_*` tools / the xterm view and font-linking / cross-session terminal pinning β€” along with the `node-pty` dependency, its `allowBuilds` grant and the installer's `-Repair` mode. **Note**: the model therefore loses a cross-call persistent terminal (only one-shot `bash` / `pwsh` remain); the upstream `@deepseek-ai/dsh-tool-terminal` is not mounted by any shipped bundle, so enable it yourself in the profile when you need it. - 🌐 **Browser view handed to the built-in**: the plugin deleted `BrowserView` / the sandbox status bar / the embeddability probe (including the host's `browser.probe` route) and the two settings that only configured its own iframe; it **keeps** the external-link takeover in the chat and UI (protocol-based routing is the capability the host does not have), now targeting DSH's built-in `browser` tab type. - 🧩 **The produced-files row goes back to the host**: DSH 0.1.6 turned `conversation.chat.turnTail` from a chain into a list (upstream deliberately lets several plugins **append** rather than replace each other), so the plugin's takeover could only duplicate the built-in deliverables card and was removed wholesale; click behaviour is unchanged (the built-in goes through the host's `openFile` β†’ this plugin's editor). The cost is the one "reveal in the file tree" entry at the turn tail (the in-tree context menu remains). - πŸ› **Two silent breakages fixed**: once `SidebarRightGuideEntry.id` became required, a missing id left the **whole native surface silently empty** (the throw happens inside an `ctx.inject` callback and is swallowed); once `turnTail` became a list the old registration threw outright and the row died. Both are fixed and guarded by tests, with a registration-failure reporting hook added alongside. - 🎨 **The empty-pane card now matches the built-in card recipe**: `.paneCard` adopts DSH's guide-capsule recipe (0.5px l4 border / 24px radius / layer-1 fill / 56px minimum / 14Γ—20 padding), with tests guarding its geometry, its tokenized colours, and that it does not copy upstream's **undefined** `--dsw-alias-bg-l1/-l2` spellings. - πŸ“ **Baseline**: every `@deepseek-ai/dsh-*` package is pinned to `0.1.6-alpha.2`, the vanished `dsh-code-runtime` package is removed, `diff` / `simple-icons` are hoisted into devDependencies as primitives' new bare imports, and `ui-primitives`' three breakages this release (`IconSendOutline16` gone, `TerminalBlockLabels.noExitCode` added, `ConnectionIndicator.reconnectLabel` removed) are adapted. ### v0.19.1 > πŸ“Œ **Stable release** (npm `latest`, no prerelease suffix): the pinned baseline moves to **DSH 0.1.5-rc.2** (npm `next`) while the **peer floor stays `^0.1.5-rc.1`** β€” nothing in the rc.2 upstream delta touches this plugin (zero changes under `packages/api|host|session|agent`; the only real code edits are the message-feedback dialog, deliverables card CSS and the `CodeFileIcon` SVG data split), so rc.1 hosts need no DSH upgrade to run this version. Hosts on DSH 0.1.5-alpha.2 stay on **v0.19.0-alpha.1**; the 0.1.2-rc.1 stable line keeps using **v0.18.1**. - 🎯 **Adapted to DSH 0.1.5-rc.2**: devDependency pins, the CI mount lane and `SIDEBAR_SERVICE_VERSION` all move to rc.2; there is **no plugin-side code change** (the upstream delta does not touch this plugin β€” file-by-file audit in [docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md](docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md): the vast majority of the 300 changed files are single-line `version` bumps). - 🎨 **File icons (#611) and the built-in tab glyphs are now colored** (#531 + #594 merged, implementation redone for rc.2): - **Files and folders use DSH's own artwork**: the fallback link is the host's `FileTypeIcon` from `ui-primitives` (48 full-color official code/config glyphs plus the category-colored sheets for markdown, images, PDF, Office, video and folders), so the plugin **ships no extension table and needs no icon chunk** β€” #429's 563-entry color dataset and `lib/client-file-icons.js` (255 kB) are gone, along with the `fileIconTheme` switch. Color is simply the behaviour now, and the core bundle grew by 10 kB. - **New public `registerFileIcon` API** (capability `'fileIcons'`): register your own glyphs by extension (`exts`), exact file name (`names`) or directory name (`folderNames`), ranked by priority then registration order. **Caveat**: the host classifier covers every path, so a catch-all (`exts: []`) claims every row the specific rules miss. - **Colored built-in tab glyphs**: Files / Changes / Tasks / Side chat / Terminal / Browser and the diff view all get a colored glyph, painted from `--dsw-alias-*` tokens so every skin still applies. - **The native right Sidebar's tab chips carry a glyph too**: the host's tab definition has no icon field, but the `sidebar.right.pane.tab.title` slot *is* the chip's content β€” the plugin renders `[glyph][title]` there (an editor tab with a path shows that file's icon). The glyph is `aria-hidden`, so the chip's accessible name is unchanged. - πŸ›  **CI fix 1: real Windows-lane timeouts**. `ci-windows` went red eight times in the 2026-09-09/10 window; six of those were process-spawning cases hitting vitest's 5000 ms default: `tests/agent-pty.spec.ts` (real PowerShell + ConPTY per terminal) and `tests/install-powershell.spec.ts` (a cold `powershell.exe` start measured 12.1 s) now declare a 30 s budget each, vitest.config.ts raises the global `testTimeout` from its 5000 ms default to **15 s** (the first real Windows run then lost a *third* file to the same default β€” `tests/git.spec.ts:85` at 9607 ms; three files, one cause, so per-file patches were whack-a-mole), and `waitForTranscript`'s inner poll budget dropped from 5000 ms to 15 s β€” **the inner budget must sit below the outer one** (they used to be the same 5000 ms, a structurally guaranteed timeout). `ci-windows`'s `Test` step now runs `pnpm test:windows` (`--maxWorkers=2`) so the process-spawning specs stop starving each other on a 2-core runner. - πŸ›  **CI fix 2: the mount lane's npm install**. `plugin-mount`'s `npm install -g @deepseek-ai/dsh@` failed four times (two `ETARGET`, two `JavaScript heap out of memory` / exit 134): the pinned version declares its transitives as floating `^` ranges, so while upstream publishes a prerelease in stages (rc.2 appeared package by package between 14:43 and 14:57 UTC on 09-10) npm resolved a mixed rc.1/rc.2 peer graph (3062 ERESOLVE lines). The lane now pins an **already-complete** rc.2 (fixes ETARGET) and passes `NODE_OPTIONS=--max-old-space-size=4096` (fixes the OOM). `--legacy-peer-deps` was tried on the way and rejected by real CI: it skips exactly the peers a global install must supply, and `@deepseek-ai/cordis-plugin-group` β€” required at boot by `dsh-app-boot` β€” is a peer rather than a dependency, so the CLI died with `ERR_MODULE_NOT_FOUND`. ### v0.19.0 > πŸ“Œ **Stable release** (npm `latest`, no prerelease suffix): supports **DSH 0.1.5-rc.1+** only (peer floor `^0.1.5-rc.1`, CI pins `@deepseek-ai/dsh@0.1.5-rc.1`). Hosts on DSH 0.1.5-alpha.2 should stay on **v0.19.0-alpha.1** (the npm `alpha` tag still points there); the 0.1.2-rc.1 stable line keeps using **v0.18.1**. **✨ New** - πŸ“ **The new-tab list gets an optional description back**: DSH 0.1.5-rc.1 restored `SidebarRightGuideEntry.description` (optional), so the plugin restores `TabDescriptor.description` β€” each of the six built-in types declares a line again (all six guide rows β€” Files / Changes / Tasks / Side chat / Terminal / Browser β€” carry one), and the `guideDesc*` keys return to all 20 dictionaries. **The host's native guide renders descriptions only while the listed entries are ≀ 4** (a longer list drops every description rather than truncating), and the plugin contributes six guide entries by default β€” so in the default composition no description is rendered; they appear once the reader disables enough tab types (per-type switches live on the plugin's settings page) to bring the guide down to four entries or fewer. An entry that declares none stays a single icon + title line (the plugin ships no generic fallback sentence). (#613) **πŸ› Fixes** - None. The rc.1 delta over alpha.2 is small (373 changed files, overwhelmingly upstream package version bumps, native-sidebar preview UI polish, and test snapshots); apart from the restored description field (plus upstream's `files` type now using its own coloured folder glyph as the guide glyph) nothing needed adapting. **🧰 CI & internals** - Baseline moved to **DSH 0.1.5-rc.1+** (#613): peer floor, devDependency pins, the CI mount lane, and `dsh.plugin.json`'s `engines.dsh` all follow (rc.1 is both the `latest` and `next` dist-tag on npm). - Explicitly unchanged, no need to re-check: the global main-panel model (the `main` slot / `sidebar.panellist` / `ctx.layout` / root-scoped `rightbar` + `rightbar.session`, still not adopted), the file address grammar (`packages/util/workspace-path` only had its version bumped), the native tab-body host contract (`.paneBody` is still a block scroller with a definite height), the core / agent / session / subagent host APIs, and the `ui-primitives` export surface (only CodeBlock rendering changed). - `@deepseek-ai/dsh-client-ui-primitives@0.1.5-rc.1` still declares no `dependencies` while its bundle still bare-imports `anser` / `shiki` / `@shikijs/langs/*` / `mdast-util-*` / `micromark-*` / `katex` β€” the third-party devDependencies hoisted in the previous release therefore stay, and must not be reverted. - Real-host verification (DSH 0.1.5-rc.1 + plugin 0.19.0): `typecheck` / `lint` / `check:consumer-types` green; unit suite **124 files Β· 1296 passed Β· 9 skipped**; `pnpm peers check` clean; the mount smoke lane against real rc.1 **7 passed** (including the tab-body fill assertion and the new "descriptions render only at ≀4 guide entries" assertion); on the local 3080 instance the guide still shows six icon+title capsules (6 > 4, so upstream renders no descriptions β€” upstream also raised the capsule `min-height` from 48px to 56px), clicking `AGENTS.md` in the file tree lands in the plugin editor (CodeMirror mounted), the side-chat composer sits on the pane floor (host box 962px == pane body 962px, composer 8px off the bottom), the bottom workbench's edges coincide with the centre column, and `pageerror` is 0 (the only console noise is the third-party `dsh-tauri-worktree` `/api/dsh-worktree/attach` 500, unrelated to this change). ### v0.19.0-alpha.1 > πŸ§ͺ **alpha channel** (npm dist-tag `alpha`, install `dsh-better-sidebar@latest`): supports **DSH 0.1.5-alpha.2+** only (peer floor `^0.1.5-alpha.2`, CI pins `@deepseek-ai/dsh@0.1.5-alpha.2`). Stay on **v0.19.0-alpha.0** for 0.1.5-alpha.1; the 0.1.2-rc.1 stable line keeps using **v0.18.1** (npm `latest`). **✨ New** - πŸͺŸ **Every tab body now fills its pane** (#609): the native sidebar's tab-body host is a block scroller with a definite height, not a flex container, and the plugin's tab roots declared only `flex: 1` β€” so they collapsed to content height and the side-chat composer sat right after the transcript instead of at the pane bottom (a long transcript pushed it out of view). The native adapter now wraps every tab body in a `height: 100%` column flex host, restoring the same fill semantics the bottom workbench has for all plugin tabs, third-party `registerTab` descriptors included. **πŸ› Fixes** - 🧭 **File address grammar follows DSH 0.1.5-alpha.2**: `fileAddressFor` always produces a session-scoped address and absolute paths keep their leading `/`; `parseFileAddress` is prefix-based and ignores `?`/`#` suffixes. - πŸͺŸ **Center-column anchor follows alpha.2's global panels**: the `conversation` slot became `main.conversation` under the root-scoped keyed `main` slot, so the locator now resolves the new key and skips `display: contents` slot hosts (alpha.1's old key still works). **🧰 CI & internals** - Baseline moved to DSH 0.1.5-alpha.2 (#609): peer floor, 22 devDependency pins, the CI mount lane, and `dsh.plugin.json` engines all follow; `pnpm peers check` is clean (hoisted the `dsh-session-persistence` transitive peer per Β§3-9). - **`TabDescriptor.description` removed**: alpha.2's native guide entries no longer render a second line (they are icon + title capsules), so the field and the six `guideDesc*` keys (20 dictionaries) are gone; a new pane's default page is now selected from the registry (exactly one guide entry opens that page directly). ### v0.19.0-alpha.0 > πŸ§ͺ **alpha channel** (npm dist-tag `alpha`, install `dsh-better-sidebar@latest`): supports **DSH 0.1.5-alpha.1+** only (peer floor `^0.1.5-alpha.1`, CI pins `@deepseek-ai/dsh@0.1.5-alpha.1`). The 0.1.2-rc.1 stable line keeps using **v0.18.1** (npm `latest`). **✨ New** - πŸ–₯️ **DSH's native right sidebar** (#604): the right column is now DSH's own sidebar β€” all seven plugin tab types register as native tab types with native tab bodies; every file open from the chat goes through `ctx.sidebarRight.openResource(dsh-resource://file/…)`; the `editor` type claims file resources at `extension` priority (outranking the built-in text preview) and takes over the built-in Files page kind (restored on unregister); cross-session opens queue until the target session is on screen. - 🧩 **Own right panel and free windows retired** (#605): with the right column handed back to DSH, the plugin keeps only its bottom workbench (single split tree, per-session persistence) and registers its toggle into DSH's session-header utilities slot; the float API (`floats` / `floatTab` / `dockFloat` / `raiseFloat` / the "Move to Free Window" menu entry), the `'floatWindows'` feature string, and the `openByDefault` / `defaultWidthPercent` / `changesDiffFloat` settings are gone (a persisted `floats` field is ignored, so old documents still load). - πŸ”— **DSH 0.1.5 host contracts** (#603): the `assistant/chunk` event is gone β€” live deltas now fold from `agent/assistant-stream` frames (the side chat transcript's `live` field); `sessionPersistence.inspect` is gone β€” cold reads go through `open(id,'read')`; session headers use `SESSION_FORMAT_VERSION`. **πŸ› Fixes** - Eight session-event read sites follow the 0.1.5 contracts (side-chat transcript, `jobs.output` replay, fork inheritance); custom seeds now carry the fork marker pair, so they no longer inherit the parent's unclaimed inbox input. **🧰 CI & internals** - The real-host mount smoke lane pins 0.1.5-alpha.1 and sweeps the native sidebar's guide page to open every plugin tab type; typecheck / lint / unit tests / mount lane all green. ### v0.18.1 > πŸ“Œ **Stable release** (npm `latest`): the DSH baseline is unchanged (**0.1.2-rc.1+**, peer floor `^0.1.2-rc.1`) β€” this is the incremental cut after v0.18.0: richer op previews in the changes panel, a writable file tree, and five fixes. **✨ Features** - πŸ“„ **Op-preview upgrades in the changes panel** (#499): markdown reading mode with mermaid fences, plus inline `.html` and `.pdf` render previews; diff syntax highlighting extended to mjs/cjs/mts/cts, CSS/SCSS/Less, HTML/XML/SVG/Vue, GraphQL, JSONC/JSON5; new **secret redaction** layer (on by default for previews, toggleable in the pane header) - πŸ—‚οΈ **File-tree rename / delete** (#550): inline rename plus confirmed delete, slimmer context menus and viewport-clamped submenus - 🧩 **Plugin catalog names and shell preset text localized** (#535): they now follow the host language **πŸ› Fixes** - πŸ”€ **Git diff gap folds really expand** (#576, fixes #577): the fold row promised "n lines… click to expand" but did nothing (`-U3` leaves gap segments without row text); the hidden rows now load on demand through `git.show`, sliced from both sides' contents, with loading / failure states and request deduplication β€” plus a fix to that route's `rev:path` addressing (it always returned empty before) - πŸ’¬ **Side-chat seeds no longer inherit the parent's unclaimed inbox messages** (#562): the seed carries the fork markers, so the "side chat sends earlier User messages first on long contexts" ghost message is gone - πŸ–ΌοΈ **Local images in the split markdown renderer** (#569): rewritten to reachable URLs instead of 404ing **🧰 CI and internals** - ESLint flat config wired into CI and the Makefile (#536), Makefile command surface (#526), hardened e2e scripts (#527), shared component-test utilities (#524) - Refactors: Sidebar.tsx split by concern (#542), four polling idioms converged onto `use-polling` (#541), rc.7 `__DSH_MODULES__` fallback removed (#540), One Dark/Light syntax palettes single-sourced (#534), duplicated implementations converged and dead code removed (#525) ### v0.18.0 > πŸ“Œ **Stable release** (npm `latest`): this release targets **DSH 0.1.2-rc.1+ only** (peer floor `^0.1.2-rc.1`); 0.1.0-rc.8 ~ 0.1.1-rc.2 are not supported β€” stable-DSH users should stay pinned to `dsh-better-sidebar@0.17.1`, hosts on 0.1.2-alpha.x keep `dsh-better-sidebar@latest` (v0.18.0-alpha.0). All changes since v0.17.1 (the two intermediate version numbers v0.18.1-alpha.0 / v0.19.0-alpha.0 were never published; their content is folded into this release): **πŸ”— Host track graduation** - **Onto the DSH 0.1.2 line and graduating to stable**: v0.18.0-alpha.0 dropped the 0.1.1-rc.x compatibility layer and fixed blank Side Chat transcripts on alpha.1+ (#472); the baseline then climbed through alpha.3 (#497) and alpha.5 (#516, `Session.events` β†’ `snapshotEvents()`); the chat file-open funnel moved to `remote.session.openWorkspacePath` (#494) and the "Show in folder" reveal scroll was scoped to the tree body (#453). This release pins the baseline to **DSH 0.1.2-rc.1** (zero source delta vs alpha.5 β€” a pure version bump; `dsh-client-locale` resumed publishing, everything aligned to rc.1; real-host mount smoke 14/14 in CI) **✨ Features** - 🌿 **Unified "Changes" tab** (#475): one tab, two lenses β€” Git (real diff / history / stageΒ·commitΒ·revert / worktree & child-repo selection) and This Session (live tracking of every file the model reads / writes / edits); a unified diff renderer (mod pairing + intra-line character highlights + syntax coloring + context folding), a draggable bottom preview pane, and one-click expansion into a dedicated diff tab - πŸ’¬ **Side Chat rendering upgrade** (#486): main-conversation-grade Blocks structure, per-turn usage tails, reconnect banner - βš™οΈ **Workspace path fence toggle** (#458): a new `workspaceFence` declarative settings key with a one-click off affordance and guidance on 403 error surfaces **⚑ Performance** - πŸš€ **Core bundle -45%** (#489): 19 non-zh/en dictionary chunks lazy-loaded, render stabilization (transcript row reuse / tree Sets / batched drags), startup & polling cost cuts (single settings fetch, one git process per tick); a new perf measurement lane; also fixes the bottom-pane drag leaking panel width into the host layout and jumping the native left sidebar - πŸ“‰ Fewer repeated center-column DOM queries (#456) **πŸ› Fixes (selection)** - ✏️ Editor / Markdown: SSH remote editor links open on the client (#522), reading position kept across preview/edit switches (#467), YAML frontmatter hidden in preview (#394), TOC dismiss-on-outside-click + popover z-index (#461), file basename kept in @-reference links (#417) - πŸ’» Terminal / platform: Windows custom shell executables resolve (#503), transient resize failures contained (#428), monospace fallback for unresolvable fonts (#366), Linux absolute paths in WSL workspaces (#455), Windows Explorer reveal selection preserved (#508) - πŸ—‚οΈ Layout / state / file tree / chat: desktop shell layout coexists with the side card (#398), no forced mobile drawer on auto-activation (#373), restored free-window id conflicts (#385), tree auto-refresh on window focus (#469), reference affordance pinned to row tail (#509), selection-popup dismissal + caret-anchored draft insert (#427), collapsed toggle cluster aligned (#361), old-engine scroll-jump compat (#448), worktree listing on older Git (#454) **🧰 CI & internals** - Windows CI lane (#520), Makefile command surface (#526), e2e script hardening + aggregate double-mount regression (#527), shared component test utils deduplicating boilerplate (#524), duplicate-implementation convergence + dead code removal (#525) **🌐 Ecosystem** - 10+ new curated plugins: dsh-better-sidebar-icons (#441), dsh-sidenote (#451, formerly dsh-sidechat #470), dsh-github-workbench (#410), dsh-bilingual-reader (#379), dsh-server-deck (#413), dsh-md-export (#405), dsh-code-nav (#404), dsh-suhuang-scroll (#392), dsh-better-overleaf (#370), and more (each with 18+ language i18n coverage) ### v0.19.0-alpha.0 (0.1.2-alpha.5 adaptation, unpublished, folded into v0.18.0) > πŸ§ͺ **Alpha track**: this release targeted **DSH 0.1.2-alpha.x only** (peer floor `^0.1.2-alpha.5`, npm dist-tag `alpha`). This number was never published separately β€” its content shipped in the **v0.18.0** stable release, and the same number was later reused for the 0.1.5 adaptation line (see above). - πŸ”— **Adapted to DSH 0.1.2-alpha.5 (published to npm, `alpha` dist-tag)**: the CI mount gate's pin, the `dsh.plugin.json` engines floor, and the `@deepseek-ai/*` peer / devDependencies baseline moved up to 0.1.2-alpha.5 (verified by a real-host mount smoke 14/14). `dsh-client-locale` has no alpha.5 upstream (newest is 0.1.2-alpha.3), so its peer floor / devDep pin trail the baseline and already accept the alpha.5 runtime (`pnpm peers check` reports zero mismatches β€” no new transitive peers to hoist). The code adapts to alpha.4's compatibility-flagged change β€” the `Session.events` property was removed, migrated to the on-demand `snapshotEvents()` API (8 sites: sidechat transcript live reads, fork inheritance, `jobs.output` replay, subagent activity), and the `seedLength` meta field the host dropped was removed from thread creation; alpha.4's remaining changes (bidirectional `send_message`, custom-model discovery reusing Profile headers, `SessionSeq`/`SessionLogOffset` strong typing) and alpha.5's upgrade-startup fix were verified not to touch any other plugin surface. ### v0.18.1-alpha.0 > πŸ§ͺ **Alpha track**: this release targets **DSH 0.1.2-alpha.x only** (peer floor `^0.1.2-alpha.3`, npm dist-tag `alpha`, install `dsh-better-sidebar@latest`). This version number was never published separately; its content is folded into the **v0.18.0** stable release. - πŸ”— **Adapted to DSH 0.1.2-alpha.3 (published to npm, `alpha` dist-tag)**: the CI mount gate's pin, the `dsh.plugin.json` engines floor, and the `@deepseek-ai/*` peer / devDependencies baseline moved up to 0.1.2-alpha.3 (verified by a real-host mount smoke 14/14). All 117 commits between alpha.2 and alpha.3 were audited point by point: every host contract this plugin relies on (token auth, slash RPC, `MarkdownText` labels, the event-stream and persistence APIs behind `sidechat.events`, `SettingsNamespaceInput`, `SUBAGENT_DESCRIPTOR_VERSION` (still 3), `dsh-client-store`, the profile loader, the node-pty pin) is unchanged, so no code adaptation was needed; alpha.3's breaking changes (the required `BeginSubmissionInput.mode`, the renamed `attachment-invalid` subagent error, the removed SQLite persistence backend, the identity-gated projection change feed) were all verified not to touch this plugin. ### v0.18.0-alpha.0 > πŸ§ͺ **Alpha track**: this release targets **DSH 0.1.2-alpha.x only** (peer floor `^0.1.2-alpha.2`, npm dist-tag `alpha`, install `dsh-better-sidebar@latest`); 0.1.0-rc.8 ~ 0.1.1-rc.2 are no longer supported β€” stable-DSH users should stay on v0.17.1 (npm `latest`). - πŸ”— **Adapted to DSH 0.1.2-alpha.2 (published to npm, `alpha` dist-tag)**: the CI mount gate's pin and the `@deepseek-ai/*` devDependencies baseline moved up to it (verified by a real-host mount smoke 14/14). Adaptation points: `dsh-settings` dropped the runtime `settingsNamespace` export (namespaces are now validated at compile time β€” the host passes the constant directly); the `dsh-subagent` descriptor version went 2β†’3 (stamped by the host package; the test assertion follows the `SUBAGENT_DESCRIPTOR_VERSION` constant); the restored `SessionEvent.ignorable` and the Remote gateway's unified `RemoteError` wrapping were verified to not affect this plugin. - πŸ› **Fixed blank Side Chat transcripts on DSH 0.1.2-alpha.1+**: transcript polling still called the `ctx.connection.api` face removed in alpha.1 (the error was silently swallowed, so the tab rendered an empty transcript forever). Transcripts now come from the plugin's own `sidechat.events` route (live threads read the in-memory event log, cold threads read session persistence, with `afterSeq` delta pulls, [sidechat-routes.ts](./src/sidechat-routes.ts)). - 🧹 **Dropped the pre-alpha (0.1.1-rc.x and older) compatibility layer**: the e2e host RPC collapsed from the dot/slash dual dialect to slash-only (token URL required, [host-protocol.ts](./tests/e2e/host-protocol.ts)); `MarkdownText` labels collapsed to the nested single shape ([markdown-labels.tsx](./src/client/markdown-labels.tsx), no more dual prop names); peerDependencies / devDependencies / `dsh.client.inject` / the chunk externals allowlist all dropped the defunct `@deepseek-ai/dsh-client-runtime` (four synchronized spots). ### v0.17.1 - πŸ”— **DSH 0.1.2-alpha.1 adaptation (dual-version compatible)**: fully adapted to DSH 0.1.2-alpha.1's Remote gateway, one-time-token browser authentication, and the `MarkdownText` labels contract change β€” the plugin works identically on 0.1.0-rc.8 ~ 0.1.1-rc.2 and 0.1.2-alpha.1 (the latter verified by a real-host mount smoke 14/14 against a source build of the GitHub tag; alpha.1 was never published to npm, and the CI pin moved to the npm-published 0.1.2-alpha.2 in v0.18.0-alpha.0). Highlights: all four `MarkdownText` render sites now go through the dual-shape labels helper ([markdown-labels.tsx](./src/client/markdown-labels.tsx)), fixing the `reading 'code'` crash in markdown/mermaid previews on alpha.1; the e2e mount smoke speaks both wire dialects (token-URL cookie exchange, slash `/api` endpoints with parameter-named args, [tests/e2e/host-protocol.ts](./tests/e2e/host-protocol.ts)); dropped the `@deepseek-ai/dsh-client-runtime` peer removed upstream in 0.1.2-alpha.1 ### v0.16.1 All changes since v0.16.0: **πŸ› Fixes** - 🧊 **Git panel freeze + restart loop** ([#376](https://github.com/omdsh-dev/DSH-better-sidebar/pull/376), fixes [#369](https://github.com/omdsh-dev/DSH-better-sidebar/issues/369)): opening the Source Control panel could freeze the whole page, and the frozen layout restored itself after every reload with no way out β€” three unbounded layers compounding, now all bounded: **β‘  status truncation** β€” the `git status --untracked-files=all` response is capped at 2,000 entries (capped results carry `truncated` and the panel shows a notice, mirroring `fs.read`'s truncation semantics; worktree change counts inherit the bound), so a huge untracked set can no longer freeze the browser main thread; **β‘‘ bounded repository discovery** β€” a non-repository cwd (e.g. the home directory) no longer probes every visible child directory serially without limit: probe timeout 30sβ†’5s, at most 200 probed directories, concurrent requests share one in-flight scan plus a 60s TTL cache, ending the `git rev-parse` spawn storm under `~`; **β‘’ reset escape hatch** β€” loading with `?dsh-sidebar-reset` drops the persisted layout (shared width included) and starts from the default, breaking the loop even when the page is already hung; persisting resumes once the param is gone; `statusTruncated` copy synced across all 19 dictionaries ### v0.16.0 All changes since v0.15.2: **✨ New features** - πŸͺŸ **Free windows** ([#354](https://github.com/omdsh-dev/DSH-better-sidebar/pull/354)): drag any tab (built-in or plugin-registered) out of the tab bar **onto the main conversation area** β€” a dashed drop hint marks the landing zone and release turns it into a floating window (default 390Γ—780, phone-portrait ratio, clamped to the viewport and centered at the drop point). Windows can be moved by dragging the header, resized from the SE corner (β‰₯320Γ—200), raised to the top by clicking anywhere, sent back with the header context menu ("Back to sidebar" / "Close"), and closed with X through the normal `closeTab` lifecycle (pty release etc.). Dragging over a sidebar pane highlights it and releasing **docks** the window back into that pane. `floats` persist per session (restored as-is after refresh, lenient sanitize + geometry clamping). Service semantics: `features` gains `'floatWindows'` β€” `openTab`'s dedupe/id focus on a floating tab **raises its window** (no duplicate open, no panel expansion), `closeTab` / `activateTab` work on floating tabs and still fire their lifecycle callbacks, `visible` is always true inside a float, and agent-terminal reconcile covers floating windows. Tab content reuses the regular render path; plugin tabs are fully supported. Also ships the 8px-grid spacing cleanup of file sub-pages ([design](docs/plans/2026-08-23-free-window-design.md)) - πŸ“‚ **Model-driven sidebar opens (`sidebar_open` tool)** ([#353](https://github.com/omdsh-dev/DSH-better-sidebar/pull/353)): a new global setting `agentOpenTools` (**off by default**) injects **one** tool letting the model open local **files** (editor tab, deduped by path), **folders** (full-window tree rooted there, `meta.dir`) and **HTTP(S) pages** (browser tab, URL prefilled) in the caller's sidebar. Turning the setting off unregisters the tool and clears the undelivered queue; already-open tabs stay. Opens targeting non-active sessions queue and replay when visible (`/sidebar/ws/agent-opens` push, same trust fence). No new public API and no new `/sidebar/api` routes ([design](docs/plans/2026-08-23-agent-open-tools-design.md)) - πŸ“ **README-level inline HTML + table of contents (TOC) in Markdown previews** ([#360](https://github.com/omdsh-dev/DSH-better-sidebar/pull/360)): previews now genuinely render **block-level inline HTML** β€” badge walls `
`, `
` blocks nesting markdown, `
`/``/`` inside table cells, `