# dsh-session-sidebar [![Listed on dsh-plugin.org](https://dsh-plugin.org/badges/listed.svg)](https://dsh-plugin.org/plugins/quentincrane/dsh-session-sidebar) [![npm version](https://img.shields.io/npm/v/dsh-session-sidebar.svg?style=flat&color=38bdf8)](https://www.npmjs.com/package/dsh-session-sidebar) [![license](https://img.shields.io/badge/license-MIT-10b981.svg?style=flat)](https://opensource.org/licenses/MIT) [![DSH Compatibility](https://img.shields.io/badge/DSH-Web%20%7C%20Desktop-purple.svg)](https://github.com/deepseek-ai/deepseek-harness) [简体中文](README.md) | English When you have a dozen sessions open in DeepSeek Harness (DSH), the built-in sidebar only lists them by workspace. Figuring out which chat is still running, which one is stuck waiting for tool approval, and which one finished ten minutes ago usually means clicking through them one by one. `dsh-session-sidebar` brings a Codex-inspired sidebar to DSH Web and Desktop. It adds an **Activity View** that puts running, blocked, and unread chats in one place, plus a top-level **Pinned** list so you can pin cross-project chats above individual workspace folders. ## Preview **Activity View**: Click the bell in the sidebar header (or press `⌥⌘U` / `Ctrl+Alt+U`) to see running, awaiting-action, and unread chats under **Priority**, then mark everything read in one click when you're caught up. ![Activity View demo](./docs/demo-activity.en.gif) **Pinning & drag ordering**: Pin frequently used chats to the top-level **Pinned** list (tagged with the workspace each chat came from) or keep them pinned inside their own workspace, and drag rows to reorder them. ![Dual pinning demo](./docs/demo-pins.en.gif) ## Installation DSH Web and DSH Desktop use separate profiles, so install the plugin for whichever environment you use: ```sh # DSH Web dsh plugin --profile web add github:QuentinCrane/dsh-session-sidebar # DSH Desktop dsh plugin --profile desktop add github:QuentinCrane/dsh-session-sidebar ``` Refresh the page (Web) or restart the app (Desktop) and the new sidebar takes over right away. No extra config files needed. > **Desktop note**: On a fresh DSH Desktop install, open the app once so it initializes its profile, quit it completely, and then run Desktop's bundled `dsh` CLI. If `dsh` isn't on your `PATH`, enable it from **Manage dsh Command** in the app menu (see the [Desktop docs](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/desktop/README.md#bundled-command-runtime)). For local development, point `link:` at your checkout path (quote it if the path contains spaces or `&`): ```sh dsh plugin --profile add "link:/path/to/dsh-session-sidebar" ``` ## Features & Usage ### Activity View Click the bell button in the sidebar header or press `⌥⌘U` (macOS) / `Ctrl+Alt+U` (Windows & Linux) to switch to Activity View. Clicking the bell while the sidebar is collapsed to the narrow rail expands it directly into the feed. A blue dot on the bell means a background turn finished since you last opened that chat; an amber dot means a session is waiting on you. At the top of the view, **Priority** collects chats that are currently running (with active subagent counts), blocked on `Approval needed` / `Plan review` / `Waiting for answer`, or unread since their last completed turn. Opening an unread chat clears its unread dot automatically. From the `...` menu next to `Priority`, you can also pull **Pinned** or **Scheduled tasks** into Priority, filter down to **Unread & running only**, or hit **Mark all as read**. Below Priority, recent chats across workspaces are grouped by Today, Yesterday, weekday, and date, each showing its workspace name and a two-line preview of the latest assistant reply. If the default shortcut collides with something else, search for `activity view` in DSH Shortcuts to rebind it. ### Pinning The built-in sidebar only pins sessions inside their own workspace. Click the view settings button in the top-right corner of the sidebar to choose between three pin display modes: - **Per session** (`hybrid`, default): Each chat's `...` menu offers both **Pin to Pinned list** and **Pin in workspace**, and lets you move between them anytime. Useful when you want cross-project threads at the very top while keeping repo-specific threads inside their folder. - **Pinned list** (`space`): Puts all pinned chats into the standalone **Pinned** section above your workspaces, each labeled with its source workspace. - **Inside workspace** (`workspace`): Hides the top-level Pinned section and pins chats at the top of their own workspace folder, just like the native sidebar. In `Per session` mode, pinning a chat to the top Pinned list still leaves it visible in its workspace by default. If you don't want to see it twice, open `...` on the **Pinned** header and turn on **Hide pinned sessions from their workspace**. That same menu also controls the default target for the hover quick-pin button and whether to keep the Pinned header visible when empty. ### Grouping, Ordering & Search The workspace view can group sessions by **Workspace**, **Workspace tree** (nests child folders under parent workspace paths automatically), or flatten everything into a **Single list**. Dragging a session row up or down inside the Pinned list, a workspace folder, or Single list view switches to manual ordering and remembers the arrangement; you can switch back to **Last updated** anytime from the top-right settings menu. The search bar filters titles and workspace names immediately, then runs a debounced full-text search across message history (`Esc` clears and closes it). Standard session and workspace actions (rename, fork, mark read/unread, copy Session ID, archive filter, and a built-in folder picker when adding workspaces) are all supported, with a stop confirmation if you archive a chat that is still running. ## Agent Tool (`activity_pins`) The plugin also registers an `activity_pins` tool backed by the same Host state as the sidebar UI, so you can ask the agent directly instead of clicking through menus: - "Show me which chats are still running or have unread replies" - "Pin this session to the top Pinned list and hide it from its workspace" - "Mark all unread sessions as read" Supported `action` values: | `action` | Description | Optional parameters | |---|---|---| | `list_activity` | List running state, unread status, and latest reply snippet across workspaces | — | | `list_pins` | List pinned sessions in order with their effective scope (`space` / `workspace`) | — | | `status` | Read the current pin mode, quick-pin default, read baseline, and activity filters | — | | `pin_space` / `pin_workspace` | Pin a session to the top-level Pinned list or inside its workspace | `session_id` | | `unpin` | Unpin a session (kept in sync with `workspaceRegistry`) | `session_id` | | `set_pin_mode` | Change global pin display mode and Pinned list options | `pin_mode`, `default_pin_target`, `hide_space_pins_in_workspace`, `always_show_pin_space` | | `mark_read` / `mark_unread` | Manually mark a session as read or unread | `session_id` | | `mark_all_read` | Mark all unread completed sessions as read | — | | `set_activity_prefs` | Update Activity View filter settings | `show_priority_section`, `include_pinned_in_priority`, `include_scheduled_in_priority`, `unread_only` | ## FAQ & Uninstall **I installed it for Web, why does Desktop still show the default sidebar?** Web and Desktop profiles are separate; run the install command again with `--profile desktop` (launch Desktop at least once beforehand so its profile exists). **My pins or read states disappeared after restarting DSH.** Global pin scopes and read watermarks are stored in the Host `storageDomain` (`dsh-session-sidebar`), falling back to memory only if that domain fails to open. Check that DSH is running under the same profile you installed the plugin into. **How do I disable or uninstall it?** Remove `dsh-session-sidebar` from the corresponding profile (or delete the entry added by `cordis.patch.yml`) and refresh or restart DSH to restore the built-in sidebar. Saved pins and read states remain in the Host storage domain if you re-enable the plugin later. ## How It Works & Development Reading full histories for cold sessions just to compute unread badges would be expensive, so the plugin registers a `ctx.sessionProjections` unit (`dshSessionSidebar`) that incrementally records turn-end timestamps and markdown-stripped reply previews as events arrive. Pin scopes, manual ordering, and read watermarks live in `ctx.storageDomain` and stay synced two-way with native `workspaceRegistry` pins. View preferences live in browser `localStorage`, and folder expansion shares `dsh.workspace.view.v5` with the native sidebar so pinning or reading a chat never collapses open folders. Repository layout: - `src/state.js`: DOM-free state layer (projection fold, unread calculation, pin/read reducers). - `index.js`: Host plugin entry registering the session projection, `storageDomain` store, `/dsh-session-sidebar/api` endpoint, and `activity_pins` tool. - `client.js`: Browser bundle mounted on `sidebar.workspaces` and `shell.overlay`, styled with `--dsw-*` theme variables for light and dark themes. - `test/`: Unit tests for state reducers, projections, and client rendering. - `docs/generate_demo_gifs.py`: Script that generates the bilingual demo GIFs in `docs/` (requires `pillow`; uses `ffmpeg` if installed for smaller files). Run syntax checks and tests: ```sh npm run check npm test ``` ## License MIT © [Quentin Crane](https://github.com/QuentinCrane)