Bundle
dsh-codex-sidebar
Codex-app-style 侧栏 for a DeepSeek Harness 主会话
- Source
- NOirBRight
- License
- MIT
- Updated
- Updated 3 days ago
Readme
# dsh-codex-sidebar
English | [中文](README.zh.md)
A Codex-app-style right-hand sidebar for one [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) session. Files, Review, Browser, and Terminal share a tab strip on the current session.

## What it does
Open a session, then use the sidebar toggle in the conversation header. The drawer occupies the details column of the DSH frame. It does not replace the chat.
- **Files** — read-only preview (source, Markdown, images) and a workspace tree. Click a path in the transcript to fill it.
- **Review** — this turn's changes from the session log, then leftover working-tree diffs. Read-only: no stage, revert, or commit.
- **Browser** — a managed Chromium document in that tab. The session can call `browser_tabs`, `browser_open`, `browser_snapshot`, `browser_click`, and `browser_fill` on HTTP(S) pages and explicit local HTML files whether the sidebar is open or closed. The Host owns the revisioned page viewport, so media dimensions cannot resize the page or move input coordinates. Direct video uses a Browser-owned, STUN-only WebRTC peer when available; the authenticated control WebSocket retains a bounded JPEG fallback for constrained Mobile tunnels. Disposing a DSH session closes its control connections and managed Pages immediately. Hiding the Browser only releases media after the configured grace period and keeps the Page available for Agent tools. Idle tabs are closed; opening the DSH web GUI inside this browser is rejected.
- **Terminal** — a human pty (`script` when present), not an agent shell.
- **Annotations** — click a line or a page to write a note at the mark. Send keeps the official user bubble; numbered chips sit under it. Locators and screenshots go to the model as evidence on that same user message.
- **Edit +/−** — each edit/write tool row shows the increment for that call, after the filename.

Chrome follows the DSH host theme. Tabs persist with that session. Side Chat is retired; cross-session questions belong to the DeepSeek assistant's task-reference flow.



## Installation
The exact official DeepSeek Harness 0.1.2-alpha.1 release is required. Later 0.1.2 prereleases or finals remain unsupported until this plugin is revalidated against their public Client contracts. Install from GitHub:
```sh
dsh plugin --profile web add github:NOirBRight/dsh-codex-sidebar#v0.5.10
dsh web
```
Lab (`DSH_HOME=~/.dsh-lab`) uses the same package name:
```sh
DSH_HOME=~/.dsh-lab dsh plugin --profile web add github:NOirBRight/dsh-codex-sidebar#v0.5.10
```
The repository tracks release-ready `lib/` artifacts, so GitHub installation needs no build-script allowlist.
Version 0.5.8 restores managed Browser click/scroll input, keeps the + menu above Review, and makes Direct video negotiate reliably in GUI Chrome. It also raises the verified screen-content profile to 20 fps / 8 Mbps with quality-90 capture and removes duplicate frame pacing, while retaining the bounded JPEG fallback.
Version 0.5.7 clones encoder IPv4 host ICE candidates onto 127.0.0.1 (the UDP socket is bound to 0.0.0.0) so GUI Chrome can reach Direct video without routing to Clash fake-ip 198.18.0.1.
Version 0.5.0 moves the Client integration to the exact official 0.1.2-alpha.1 modules (`ui-session`, `ui-conversation`, `ui-chat`, Client store, and API Remotes) after `dsh-client-runtime` was removed. Transcript consumers read canonical Chat nodes through one plugin Adapter; the current `legacy` compatibility slice is only a fallback inside that Adapter. It also rejoins the bounded Browser transport from 0.3.23 with the revisioned Browser v2 implementation; the 0.4.x Alpha adaptation line did not contain that parallel Browser work.
Since 0.3.0, Review/Files workspace projection is asynchronous and demand-driven: a collapsed sidebar does not scan git, Review rows use summaries, and file details load only when opened. Sidebar state is isolated under `DSH_HOME`, with on-demand fallback migration from `~/.dsh-codex-sidebar/sessions`. Very large or binary file details are bounded summaries rather than unbounded LCS diffs, so the host remains responsive.
Do not list `@deepseek-ai/dsh-tools` (or other host singletons) as a plugin `dependency`. A hoisted copy shadows the host ToolRuntime and every tool call dies on `.prepare`.
The managed Chromium profile has a 256 MiB derived-cache budget by default. Managed Browser layout, direct media, and fallback limits are also validated loader configuration:
```yaml
- name: dsh-codex-sidebar
config:
managedBrowser:
cacheBudgetBytes: 268435456
layoutMinViewport: { width: 320, height: 240 }
layoutMaxViewport: { width: 1920, height: 1440 }
layoutSettleMs: 180
layoutHysteresisPx: 8
layoutPaintTimeoutMs: 1000
preferredMediaRoute: webrtc-preferred
stunUrls: []
webrtcNegotiationTimeoutMs: 10000
webrtcRetryCooldownMs: 30000
maxMediaPeers: 3
maxEncoderPages: 3
directVideoFrameRate: 20
directVideoMaxBitrate: 8000000
directVideoCaptureQuality: 90
directVideoCaptureMaxScale: 1.5
directVideoCaptureMaxRawBytes: 1048576
desktopJpegQuality: 80
desktopJpegFrameIntervalMs: 100
desktopJpegMaxScale: 1.5
desktopScreencastEveryNthFrame: 2
desktopJpegInteractionBurstFrames: 20
desktopJpegMaxRawBytes: 491520
mobileJpegQuality: 65
mobileJpegFrameIntervalMs: 250
mobileJpegMaxScale: 1
mobileScreencastEveryNthFrame: 4
mobileJpegInteractionBurstFrames: 4
mobileJpegMaxRawBytes: 98304
mediaIdleTimeoutMs: 300000
mediaHideGraceMs: 15000
browserCleanupTimeoutMs: 2000
```
The fixed phone, tablet, and laptop presets remain `390×844`, `768×1024`, and `1280×800`. Fit mode proposes one clamped viewport only after the container settles; selecting a fixed preset sends exactly one proposal through its v2 control connection and never commits through the persisted session or ticket path. Fixed presets never consume container resize observations. The Host verifies the exact target Page's CSS viewport after every proposal, including an unchanged one, and again after the first screencast starts on a fixed-layout connection. It does not use that screencast for capture or start direct-media negotiation until the reconnect verification finishes. During a viewport transition it pauses visual reads, media capture, and input without closing the control connection; an internal epoch discards work that crossed even an unchanged-layout verification, while visual results and gestures also recheck their originating document. The client sends each desktop press/release as one atomic tap or drag, and the Host serializes that complete gesture with viewport writes; evidence selection is canceled if its press and release use different presented revisions. A completed action is not reported as failed merely because a transition queued behind it. A completed Mobile touch tap opens the hidden IME and pauses fit proposals until blur; touch scrolling and desktop focus do not pause fit. This reconnect check repairs a Page whose actual viewport drifted while retaining the existing revision and media generation. The Host uses an identity-bound CDP metrics override when Playwright completes without applying the dimensions and closes the target rather than publishing or streaming an unverified layout. Each control connection binds one exact managed Page/CDP identity and disconnects when that target is replaced. WebRTC carries video only and does not request camera, microphone, or audio. `stunUrls` accepts only `stun:` URLs; TURN is rejected. An empty list still permits host ICE candidates, while deployments that need NAT discovery must configure approved STUN servers. `jpeg-only` is available as a diagnostic `preferredMediaRoute`.
The Origin-less Mobile tunnel wraps the Browser JSON frame in another Base64 envelope. Its default 96 KiB limit applies to the encoded JPEG bytes and leaves the complete tunnel plaintext below the 200 KiB ceiling. Browser evidence remains an exact, separate Page capture; its read RPC requires an explicit offset and returns at most 96 KiB of raw JPEG as Base64 with a next offset, rather than sending a multi-megabyte response through the tunnel. The fallback may lower JPEG quality or encoded resolution, but it never changes the committed CSS viewport. `desktopJpegFrameIntervalMs` and `mobileJpegFrameIntervalMs` are hard capture-rate ceilings, including interaction-triggered frames. Each interaction, navigation, refresh, or layout commit permits at most `desktopJpegInteractionBurstFrames` or `mobileJpegInteractionBurstFrames` later passive screencast updates; animation alone becomes quiet when that budget is exhausted. New activity replenishes the budget and retains the latest dirty update. Direct WebRTC video uses the separate `directVideoCaptureQuality`, `directVideoCaptureMaxScale`, and `directVideoCaptureMaxRawBytes` profile against the Host-committed viewport, regardless of whether the control socket has an Origin. These encoder-only JPEG bytes never enter the Mobile tunnel. Each connection retains at most one capture, one unacknowledged frame, and one latest dirty request.
The Browser surface reports `Direct video`, `Low-bandwidth fallback`, `Reconnecting video`, or `Video unavailable` from the route that is actually presentable to the user. Autoplay, decode, missing-track, first-frame, peer, and local negotiation failures decline only the exact current media identity, allowing the Host to restore JPEG without letting a stale generation disrupt the current route.
`ManagedBrowserStream.diagnostics()` exposes fixed-memory counters, gauges, and latency aggregates without page URLs or content. It includes the latest viewport revision/media generation; capture, fallback encoding/send, encoder Canvas paint, and end-to-end fallback acknowledgement latency; encoded bytes and route-budget drops; media outcomes; and current peer, encoder Page, capture, socket, and timer counts. `resources()` remains limited to its existing socket, timer, capture, unacknowledged-frame, and peer ownership fields. At media capacity, the oldest hidden owner is released first, then the oldest still-fallback negotiation; visible active direct-video peers are never capacity-evicted, and a request falls back with `local-capacity` when no safe victim exists. `maxMediaPeers` must not exceed `maxEncoderPages`; invalid capacity configuration fails during plugin load.
The values above are the defaults. `mediaIdleTimeoutMs` releases an inactive direct-video peer while keeping the target Page alive; later interaction may negotiate again after the retry cooldown. When the document or Browser surface becomes hidden, `mediaHideGraceMs` keeps the control connection alive for a short recovery window. Switching to another tool Tab retains the Browser surface as hidden and inert during this interval, so it occupies no layout and accepts no input. Returning before the deadline cancels teardown; expiry closes the control connection and releases its peer and encoder without closing the target Page. `browserCleanupTimeoutMs` bounds how long ordinary Tab close, failed Page creation, and plugin shutdown wait for Browser-owned cleanup. Plugin shutdown revokes local HTML capabilities immediately before waiting for stream sockets, Chromium targets, and other owned work.
Before launch, the plugin performs a read-only, no-follow size estimate over allowlisted derived-cache directories. Chromium's persistent-context startup owns singleton arbitration; the plugin does not rename, remove, or repair profile paths. After a context starts successfully, an over-budget estimate triggers one temporary blank Page and CDP session that run `Network.enable` and `Network.clearBrowserCache`, then always detach and close. Clear failures warn without discarding the context. Chromium's cache API leaves cookies, Local Storage, and IndexedDB intact, while disk and media cache launch arguments limit future growth.
The Browser address bar also accepts an absolute `file:///.../page.html` or `.htm` address. The Host requires a regular, non-symlink entry and projects its canonical parent through a random capability on a separate server bound only to `127.0.0.1:0`; relative assets stay inside that parent, directory listings and traversal are refused, and only `GET`/`HEAD` are served. After open, the Host revalidates the requested path and rejects the request when its observed device/inode identity differs from the open handle. These checks reject static symlinks and observed path changes; they are not a security boundary against a process running as the same operating-system user and concurrently modifying authorized files. Same-Tab opens are serialized and share one Page/CDP identity. Chromium alone receives the private HTTP address. Session state, tools, diagnostics, snapshot accessible names, Browser outlines, desktop clients, and remote Mobile clients continue to see the public `file:` address or a redacted gateway label, never the loopback port or capability. Closing the Tab or session revokes its directory; plugin disposal revokes every capability before waiting for Chromium teardown and closes the listener independently of stalled Browser cleanup. External-open remains HTTP(S)-only.
Local HTML is active content. Its scripts can read resources served from the selected HTML directory and can initiate network requests. Open only HTML you trust; place generated prototypes in a dedicated directory rather than beside credentials or unrelated files.
## Local install
Client typechecking is pinned to the official `dsh-v0.1.2-alpha.1` declarations. The offline pack gate consumes the committed exact-version fixture bundle in `fixtures/alpha1/`; it does not inspect an external DSH checkout or pre-existing dependency tree. Refresh that bundle only from a clean, built checkout of the exact tag with `pnpm run prepare:pack-fixtures`; the generated tarballs include their provenance and integrity records.
The gate invokes `npm`, `pnpm`, `node`, `git`, and `tar` directly with one sanitized child environment. Its pnpm install policy is `--offline --ignore-scripts --strict-peer-dependencies --lockfile=false --registry http://127.0.0.1:9/ --store-dir <fresh> --config.audit=false --config.fund=false`; the audit and fund settings are pnpm config equivalents, not npm-only `--no-audit` or `--no-fund` flags.
```sh
pnpm install
DSH_ALPHA1_CHECKOUT=/path/to/deepseek-harness-alpha1 pnpm run typecheck
pnpm run pack:check
dsh plugin --profile web add "$(pwd)"
dsh web
```
Then open a session and use the sidebar toggle.
## Spec
See `CONTEXT.md` and `docs/adr/`.
## Release installation (Latest)
Codex-style Files, Review, Browser, and Terminal sidebar for one DSH session. The release artifact targets DeepSeek Harness 0.1.2-alpha.1 and contains built Host/Client files only; it has no sibling-repository source, workstation path, link:, or workspace: dependency.
Latest installation (the URL never contains a version):
~~~sh
dsh plugin --profile web add --force \
https://github.com/NOirBRight/dsh-codex-sidebar/releases/latest/download/dsh-codex-sidebar.tgz
~~~
Fixed-version installation:
~~~sh
dsh plugin --profile web add --force \
https://github.com/NOirBRight/dsh-codex-sidebar/releases/download/v0.5.10/dsh-codex-sidebar.tgz
~~~
Update, uninstall, and verify:
~~~sh
# Update to the latest Release
dsh plugin --profile web add --force \
https://github.com/NOirBRight/dsh-codex-sidebar/releases/latest/download/dsh-codex-sidebar.tgz
# Verify the loaded version
dsh plugin --profile web list
dsh plugin --profile web doctor
# Uninstall only this plugin
dsh plugin --profile web remove dsh-codex-sidebar
~~~
Configuration: use the plugin section in Settings for Web UI plugins, or the profile dsh.profile.bundles entry for Host-only plugins. Start with this README's minimal YAML/JSON example and provide credentials/backend addresses explicitly.
Rollback: rerun the fixed v0.5.10 command, verify the profile list, then restart the Web service once. Inspect journalctl --user -u dsh-web.service and dsh plugin --profile web doctor; never put a source checkout in the production profile.
Release and integrity: [v0.5.10](https://github.com/NOirBRight/dsh-codex-sidebar/releases/tag/v0.5.10) · [SHA256SUMS](https://github.com/NOirBRight/dsh-codex-sidebar/releases/download/v0.5.10/SHA256SUMS).
Install
dsh plugin --profile web add github:NOirBRight/dsh-codex-sidebar
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-codex-sidebar from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.