# πŸ—‚οΈ DeepSeek Harness Workspace Studio Plugin (Left–Center–Right Three-Pane Layout) English | [δΈ­ζ–‡](README.md) This bundle replaces the DeepSeek Harness Web root layout with **three panes from left to right: a left sidebar (session/workspace selector + file-tree view switch) Β· a highlighted file view and guarded editor Β· chat**. The file browser pane sits left of the chat by default and can be moved to its right in Workspace Settings β†’ Content Browsing. The file tree no longer owns a pane: it is fused into the left sidebar and toggled against the session list via the buttons at the top. It preserves the existing sidebar, conversation, details, and global-overlay Slot contracts, so the built-in plugins keep owning new-session creation, session lists, settings, chat, tool details, and approvals. Tool details open as a right-side drawer over the three-pane layout instead of consuming a permanent pane. The session header's **Mind map** button enters **mind-map mode** anytime: the left area becomes a conversation branch-tree canvas while the chat stays visible and usable on the right. ## πŸ“Έ Screenshots | ![Three-pane layout overview](image/image-1.png) | ![File view and editor](image/image-2.png) | |---|---| ## ✨ Key Highlights | Capability | Description | |---|---| | πŸ“ **Workspace file tree** | Fused into the left sidebar's **File Explorer** view; directories first, incremental expansion, and **expanded state and scroll position restore per session** after a reload | | ⌨️ **CodeMirror 6 editor** | 20+ language syntax highlighting, line numbers, code folding, in-editor search, word wrap, and 14 text encodings | | πŸ—‚οΈ **Preview tabs** | Persisted per session, survive reloads, drag reorder, pinned tabs, drafts never lost, conflict protection, external-change auto-sync | | πŸ–ΌοΈ **Renderer views** | A view-as menu: rendered Markdown / HTML previews, direct image preview, and paged read-only browsing of full file content | | 🎯 **Editor context** | Open files / selections inject as `` / `` prefixes; history keeps a one-line summary; a title guard auto-cleans envelope prefixes leaked into session titles | | 🧹 **File operations** | Right-click create / rename / copy / cut / paste / delete / copy path, with shortcuts | | 🧭 **Mind-map mode** | Conversation branch tree: the session header's **Mind map** button enters mind-map mode, reverse-parses the full session log into turn cards and persists them, forks a new branch at any card, rename / delete cards, archive the whole map | | πŸ“± **Mobile mode** | One-click switch to a centered phone column; file browsing can fill the phone column | | πŸ”’ **Security boundary** | Workspace-confined read/write, path containment, revision conflict protection, symlink rejection | ## 🧩 Features ### File Tree - The file tree is fused into the **left sidebar**, whose top row switches between **Sessions / File Explorer** views. In the File Explorer view the Workspace file tree appears automatically when the current Session belongs to a Workspace (also recognized when the Session `cwd` equals its path); directories sort before files, with incremental expansion, collapse, and manual refresh. - Expanded folders persist per current session and re-expand after a reload; selecting a tab reveals it in the tree and restores the vertical scroll position. - Right-clicking the session title renames the current session. When several sessions share the same title, the context menu shows the matched session id suffix and archiving asks for confirmation (so the wrong session and its branches are never archived). - Both left panes are drag-resizable: a splitter on the sidebar boundary and another on the preview boundary (the two left panes together use at most 80% of the viewport); layout parameters persist globally in `localStorage`. ### Editor - CodeMirror 6 shows line numbers and syntax highlighting based on filenames or extensions; unknown types use plain text. A fold gutter and in-editor search (`Ctrl/Cmd+F`, `F3`) are built in. - `Ctrl+K+J` unfolds every collapsed fold region; `Ctrl+K+1..9` folds code by nesting level (e.g. `Ctrl+K+2` folds every second-level fold region). `Ctrl/Cmd+S` saves from any focus state (including the chat input). - Editable files **open directly in edit mode** (no **Edit** button); the panel header offers **Cancel**, **Save**, **Word wrap**, and **Reload from disk** (refresh). Read-only files (dropped external files, oversized, truncated, mixed line endings, symlink paths, or editing disabled) show a read-only reason banner. - Each file-type group can pick an editor highlight preset (Default, Classic, Warm, Cool, Monochrome, XML (VS Code), and 10+ more) from the Workspace settings page, remembered per type in `localStorage`. ### Encodings - File preview auto-detects the encoding (UTF-8 / UTF-8 BOM / UTF-16 LE / BE / GBK / GB18030 / Big5 / Shift_JIS / EUC-JP / EUC-KR / ISO-8859-1 / Windows-1252 / Windows-1251 / ASCII). - Right-click the preview header to **Open With Encoding…** (re-decode) or **Save As Encoding…** (write back); the panel header shows the current encoding badge. - The encoding list is authoritative from the server `/workspace-studio/api/encodings`; a failed request falls back to the built-in list, so the actions never dead-end. ### Preview Tabs - Opened files enter per-Session preview tabs: `X` to close, drag to reorder, and tabs survive reloads. - Right-click a tab β†’ **Open in new window**: Markdown files (md / markdown / mdx) open as a **rendered document** in the new tab (GFM: tables, task lists, strikethrough; raw HTML renders as literal text, links and images are allowlisted to http / https / mailto β€” the same policy as the in-pane preview, and the page carries no scripts); HTML files run their page scripts as-is, other files show the raw text. - **Pinned tabs**: right-click a tab to **Pin / Unpin**; pinned tabs show a pin icon and sort first, and **Close Other Tabs** only closes unpinned tabs. - Unsaved edits show a `Β·` after the tab label and after the filename in the preview panel title; it disappears once saved. - Unsaved drafts are kept in staging files (see Editing & Saving); localStorage only holds the dirty marker, never content; switching files never silently discards unsaved content. - The tab bar scrolls horizontally with the wheel, and newly activated tabs auto-scroll into view. ### Renderer Views - The preview pane's view mode is driven by a **renderer registry** (the same lineage as the new Harness right-Sidebar document preview): Markdown files switch between **source editing / rendered preview** (GFM rendering, the same policy as **Open in new window**) and **open in the rendered preview by default**; HTML files switch between **source editing / page preview** and **open in the page preview by default** (the toggle returns to source editing); the preview page's relative scripts and stylesheets are read through the standard workspace-files API and packed into the sandboxed iframe (edits apply live; packing is debounced by 400 ms). - Image files (png / jpg / jpeg / gif / webp / bmp / ico / svg) open directly as an **image preview**: complete bytes are read through the standard workspace-files API and re-fetched on refresh or external change. - Read-only text files (truncated, oversized, read-only, …) switch between **source / read-only browse**: the browse view **pages through the full file** (the editor is capped at `maxPreviewBytes`), appending the next page at scroll-to-bottom, rendering Markdown as a document and other code highlighted. - The view mode resets per file switch to that file's default view (rendered preview for Markdown, page preview for HTML, source editing for everything else) and is not persisted; PDF rendering is not provided yet. ### Editing & Saving - Editable files **open directly in edit mode** (Markdown and HTML files open in their preview by default β€” rendered document for Markdown, page preview for HTML; the toggle returns to source editing) with **Save**, **Cancel**, and `Ctrl/Cmd+S`. - **Staging draft file**: editing takes one **snapshot** (the source content); all temporary edits are debounce-written to a **draft file** (`~/.dsh-plugin/dsh-workspace-studio/drafts//`, long-lived), and the **source file is never touched**; a page refresh restores from the draft file (draft + snapshot + encoding). Auto-save is not a "save", so the `Β·` stays until an explicit save. localStorage only keeps the dirty marker, not the edit content or snapshot. - **Save (merge back to source)**: saving re-reads the source and compares it with the snapshot β€” - Source unchanged by other tools (= snapshot): silently write the staged content back to the source, then delete the draft file. - Source changed in different places than your edits: automatic three-way merge keeps both sides and writes back. - Source changed in the same place as your edits: the dialog walks each conflicting region β€” the top two columns show an inline add/delete diff (My changes / Disk version), the bottom two columns show the resulting code without markers, letting you pick **Keep my version / Keep disk version** per region, or **Cancel** to abort the save. - **Cancel**: discards the temporary edits, deletes the draft file, and restores the editor to the source content (the source file itself is not changed). - **External-change auto-sync**: file watching with auto-sync is on by default, checking each open tab against the disk roughly every 2 seconds. A clean, active tab reloads from disk automatically when another tool changes the file and keeps its scroll position (auto mode shows "Reloaded from disk."; watch-only mode shows a "The file changed on disk…" notice). **Dirty tabs are never overwritten** β€” only a notice is shown and you decide (saving merges or asks per region); when the file is deleted, the active tab shows "The file was deleted." - Rejects binary, non-UTF-8, and out-of-Workspace symlink files; truncated large files, mixed-line-ending files, and symlink-traversing paths are read-only. ### File Operations - Creates files and folders at the selected level and renames with `F2`. - Context menu: Copy Name, Copy Path, Copy Relative Path, and **Reveal in Explorer** (opens the OS file manager). - Right-click Copy / Cut / Paste / Delete, with shortcuts `Ctrl/Cmd+C`, `Ctrl/Cmd+X`, `Ctrl/Cmd+V`, `Del`. - Cut + Paste = move; colliding targets auto-rename (`a.txt β†’ a-1.txt`); delete asks for confirmation and adds a warning when unsaved tabs are affected. - The clipboard is in-memory and workspace-isolated (paste from another Workspace is disabled) and resets on reload; external files (dropped-in read-only previews) cannot be renamed or deleted. ### Search - Search results group by file: clicking a file header collapses / expands that file's matches, and clicking a match opens the file and jumps to the line. - Case-sensitive toggling is supported; files too large to be fully searched are marked **Partial**. - The Workspace settings page chooses whether search results default to expanded or collapsed. ### Editor Context - The editor context appears as a non-editable prefix outside the textarea through the existing input dock: active sends freeze the context and render `...` or, for selected text, `...` (no file bytes without a selection); gray sends attach nothing. - The Host validates it, prepends it to the direct user prompt, and the conversation view folds it into a one-line summary above the bubble showing the file name and range; history renders the logged user message only. ### Chat Experience - The session header title area is a **session switcher**: clicking it opens a dropdown listing all sessions (most recently updated first, the current session highlighted, each row suffixed with its Workspace name, subagent sessions marked with a **subagent** badge); clicking a row switches to that session. Fork sessions of a mind-map family stay filtered out (the root stays, for direct access). - The UI language follows the Harness setting (Settings β†’ General β†’ Language; Chinese / English) and switches live, with no restart or reload. - The assistant's Think blocks stay visible as **persistent cards**: the body viewport shows only the latest N lines (adjustable 5–30 via the "Think lines" slider on the Workspace settings page, default 10), follows the newest text while reasoning streams, earlier thinking is reachable through the card's own scrollbar, and clicking the card title row collapses it to a one-line summary; blocks the user collapses are never force-expanded again. Edit/write rows in the conversation (the merged diff cards) follow the same card pattern and **always open**: the change viewport is capped by its own "Edit lines" slider (5–30, default 10) with its own scrollbar for the full change, replacing the old "expand the rest" button. - The `/init` command (Claude Code style) generates or updates `AGENTS.md` at the current session's workspace root; when the file already exists a dialog offers **Update** or **Cancel**, and the current agent analyzes the workspace and generates it. - Drop external files into the preview pane to preview them as read-only tabs (session-only; nothing is written to the workspace). Only text files are accepted: images, folders, and other non-text content show a β€œcannot preview as text” notice (images belong to the chat composer β€” intentional). ### Mind Map Mode (Conversation Branching) - The **Mind Map** button in the session header enters **mind-map mode**: the map opens as a **preview tab** (inside `dsh-ws-preview`, switchable against file tabs) while the **chat stays visible and usable on the right**; closing is the tab's Γ— button. On first entry, the plugin reverse-parses the session's **full event log into all its turns**, renders the whole session as one chain of question cards, and persists them to `~/.dsh-plugin/dsh-workspace-studio/mindmap/` β€” that persisted document is the mind map's **single source of truth**. - Entry asks for confirmation first: once the ordinary session is **converted** into a mind-map session, it is hidden from the sidebar session list and replaced by a self-drawn entry at the **end of the session list under its workspace group**; clicking it opens the session and docks the map as a preview tab. Every fork session derived from the map is hidden from the list too and managed from the map only. The entry supports **drag-reordering** (order persisted per workspace group), right-click to **rename the map's own title** (independent of the root session title) or **Reveal in Explorer**; its icon spins while any session in the family is streaming. - A **virtual root node** sits at the top of the map: clicking it creates a blank **top-level session** (no inherited turns, same workspace cwd, opened immediately so you can ask right away); right-clicking the root lets you choose the workspace new sessions belong to or archive the entire map. - Clicking a card is **switch-first, fork-as-fallback**: a card where a branch is parked (a chain-tail card) **switches to that branch** (the right-side chat follows, the highlight moves β€” free branch switching); a middle card with no parked branch (e.g. card 6 inside branch 6-7) **forks a new branch there** and enters chat, its new turns sitting beside its sibling (6 β†’ 8, 9 beside 7). Every fork belongs to the **same main mind map** β€” a fork never creates a new map β€” and the new branch session stays hidden from the sidebar session list. The branch's new turns are folded back into the document by the Host sync from the branch session's full log. - Right-click a branch to **rename** it; the toolbar can **archive the entire mind map** (with all its branch sessions; the tab closes after archiving the whole map). Right-click any card (root-session cards included) to **delete the card** (a true truncation): a new session is forked from the previous card and replaces the original β€” this card, the turns after it, and every branch derived from them are removed, and the original session is archived (currently no restore path), so the chat and the map both continue from the truncation point with matching numbering. The map supports **grab-pan, wheel zoom**, and **Restore view**. - While a branch is **generating** (a question was submitted and the agent is streaming), the map shows one live "**Generating…**" card for every generating session in the family, each carrying the in-flight question. Each streaming card and its **parent card** receive a matching colorful flowing gradient ring, and the connecting edge uses matching flowing dashes; when the turn completes the card converts into a normal card and the ring and flowing edge disappear. The streaming card is **clickable: it switches to the generating session** so the right-side chat follows it and you can watch the output live (the highlight moves along; an unfinished turn has no turn/end sequence, so it can never be a fork point, and its context menu stays disabled). The last completed card of a currently generating session is then treated as a **middle card** β€” clicking it **forks a new branch** there. - **AI card summaries** (optional, off by default): once enabled under Workspace settings β†’ Mind Map Browsing β†’ AI card summary, the map summarizes each question with the chosen model (one small call per turn, minor token cost; the summary is a suggestion β€” hover a card to see the full text). Right-click a card to **regenerate its summary**, the toolbar can **regenerate all summaries**, the toolbar can also **regenerate all session summaries** (session head cards only β€” existing card summaries are never recalculated, and sessions missing card summaries have the missing ones generated first), and right-clicking a session head offers **summarize this session**. The summary model can follow the session model or be picked explicitly; the summary length and the session-summary length are adjustable separately (20–200 / 20–500 characters). ### Appearance & Settings - Uses Harness semantic theme variables and supports light, dark, and system themes. - The Workspace settings page starts with a **Plugin Update** group: it checks the GitHub main branch (yishengjun8/dsh-workspace-studio) for a newer version, downloads and replaces the plugin's installed files in one click, then prompts you to **restart dsh and refresh the page** to apply (a local `file:` install only replaces the profile copy; the local checkout stays untouched). - Right after the Plugin Update group sits a **Token Statistics** group: its panel aggregates token usage from every session log over a **standard week / calendar month** (this week, last week, this month, last month, or all) or a **custom date range**, shown as **totals** or a **per-model** breakdown (input, cache read, cache write, output); in the per-model view, checkboxes decide which models feed the summary row. **Archived sessions are included by default** (archiving does not delete logs) and can be excluded with a checkbox. Data comes from the `usage` records in session logs; records dropped by compaction or truncation are not included. The Host caches each session's aggregation at `~/.dsh-plugin/dsh-workspace-studio/token-stats/` and **warms it up in the background on every dsh start** β€” unchanged sessions reuse the previous cache (keyed by the persistence index's stat-derived revision, so the first open answers instantly and only changed logs are re-read afterwards). While a background scan is still running the panel shows **partial results** and refreshes itself every 1.5 s (the footer reports "N / M sessions done"); sessions the backend refuses (unsupported legacy formats) are attempted once and reported in the footer, and a long scan checkpoints every 100 sessions so a restart never starts from zero. - The Workspace settings page is grouped: **Session Browsing** (the spin speed of the sidebar mind-map entry icon while the map streams, a 0–3x multiplier where larger is faster and the default is 1.2x), **Mind Map Browsing** (hover and selected highlight colors, session-head and end-card accent colors, the mount-edge curve amount (0–6x with a 5x default where 0 is a straight line), and AI card summaries (an enable toggle, the summary model, the summary length 20–200 chars with a 48 default, and the session-summary length 20–500 chars with a 64 default)), **File Browsing** (tree row height, search result display, file icon badge colors), **Content Browsing** (per-type highlight presets, save-conflict dialog font size, whether the file browser pane sits left or right of the conversation column (default: left), and file watching with auto-sync (on by default, switchable to notify-only without auto-reload)), and **Conversation Page Settings** (Think lines and edit lines). - A **Mobile mode** toggle in the sidebar footer collapses the layout into a centered phone column; the sidebar becomes a floating drawer opened by the whale at the top-left (session list and file tree stay inside it), and a **Browse files** button appears right of the whale in the conversation header to fill the phone column with file browsing while the header stays reachable. Mobile mode is transient, so a reload returns to the desktop layout. ## 🎨 Syntax Highlighting Built in for **20+ languages**: JavaScript/JSX, TypeScript/TSX, JSON, HTML, CSS/SCSS/Less, Markdown/MDX, Python, SQL, XML/SVG, YAML, C/C++, C#, Java, Rust, PHP, Go, Shell, PowerShell, Ruby, TOML, INI, and Dockerfile. `Makefile`, `.gitignore`, `LICENSE`, and unknown extensions stay browsable and editable in plain text. ## 🧩 Dual-Face Implementation One package ships three faces: - **Host entry** (`lib/index.js`) registers `/workspace-studio/api`: it lists directories by Workspace ID, reads bounded UTF-8 files, authorizes the current Session by membership or canonical cwd, and, when editing is explicitly enabled, saves existing regular files, creates files and folders, and renames entries through revision validation, single-segment name checks, and atomic replacement β€” refusing stale revisions instead of overwriting them. It also serves `/mindmap-doc` (read / write / delete) plus `/mindmap-doc/sync`, `/mindmap-doc/index`, `/mindmap-doc/rename`, `/mindmap-doc/models`, `/mindmap-doc/regenerate-summary`, `/mindmap-doc/regenerate-all`, `/mindmap-doc/regenerate-session-summaries`, and `/mindmap-doc/summarize-session`, persisting per-session mind-map documents built by reverse-parsing full event logs and folding in every session's turns, with renames updating only the map title instead of round-tripping the whole document and AI-summary generation/regeneration/session-summarization serialized by the Host. Additionally `/update/check` (compares the installed version against the GitHub main branch) and `/update/download` (verifies and atomically swaps the plugin's own install directory) back the settings "Plugin Update" group; a dsh restart is required to apply the swap. There is also `/token-stats` (aggregates the `assistant/message` usage records of every session log over a client-supplied `[from, to)` epoch-ms window; `archived=0` excludes archived sessions) backing the settings "Token Statistics" group, with the per-day per-model aggregation cached incrementally at `~/.dsh-plugin/dsh-workspace-studio/token-stats/usage-index.json` behind the persistence index's stat-derived revision (unreadable sessions cache that verdict instead of being retried, a long scan checkpoints every 100 sessions, and once an index exists a request answers immediately with partial results plus `warming` / `progress`). - **Browser entry** (`lib/client.js`) provides the compatible `ctx.layout` service and the `usePanelInfo` standard hook (the `panelInfo` root contribution), occupies the root Slot, declares `sidebar`, `main` (keyed, hosting the newer Harness conversation panel), `details`, and `shell.overlay`, and adds the file tree, the CodeMirror 6 browser/editor, the editor-context row, the Workspace settings tab, the `/init` command, and the conversation-branch mind map (preview tab). - **Shared invariants** (`lib/invariant.js`) back every Host request with path-containment and write-eligibility checks. ### Activation Model The layout provider intentionally does not hard-inject `conversation`: the conversation plugin itself consumes `layout`. The bundle therefore uses a child injection after activation to patch the existing `sendSession` seam and to register the editor row in `conversation.input.dock`, avoiding an activation cycle. ### Known Limitations and Deferred Work The editor-context bridge adapts the concrete Harness 0.1.x `sendSession`, input-submit, and queue-steer implementations because the public cross-package faces do not carry arbitrary Composer context. These seams stay behind this package and are restored on unload, so a future Harness release may require updating only this bundle. Layout state, expanded directories, editor selection, and the workspace staging-draft state remain page-memory state; preview tabs and their per-tab vertical scroll positions persist and are restored across reloads and when returning to the previous Session or Workspace (unsaved content itself lives in staging-draft files β€” see Editing & Saving). ### Model Experience When the prefix is active and the primary CodeMirror selection is non-empty, each send captures that exact selected text, its normalized workspace path, and its range, then renders it as a `...` envelope. When the selection is empty, each send captures only the open file path and renders the fixed `...` envelope; it never submits the full file. The Browser bridge prepends the rendered text to the direct user prompt, so the ordinary `user/message` record contains the exact model-visible context. The conversation view folds that same envelope into a compact row above the bubble, showing the file name and the line/column range; hovering that row reveals the full injected XML. Gray prefixes contribute no context, and the same context is intentionally recorded again on each later active turn. #### Token and KV Cache Effect Selection contexts add the selected text plus the `...` envelope to input tokens. The Explorer preflights selected text against its default 65,536-byte UTF-8 limit; the Host independently bounds the complete rendering at 69,632 bytes by default and reads at most 10 MiB for clean revision verification. Truncated previews use browser-authoritative selection text. Path-only contexts add only the `...` envelope and no file bytes. Every active turn has its own logged prompt text, so repeated selections may increase prompt tokens until compaction. ## πŸ“¦ Installation Run from Git Bash, Linux, or WSL. First `cd` into the directory that holds this bundle (use your own path): ```sh cd bash ./install.sh # default target is the web profile bash ./install.sh web # a profile can be supplied explicitly ``` > In the sample path `C:/GreenSoftware/deepseek-harness/deepseek-harness-plugin/dsh-workspace-studio` > the `deepseek-harness-plugin` segment is the author's custom plugin folder name, not a requirement. > `install.sh` resolves the Harness root as two levels above the bundle directory (for the > `pnpm --dir` fallback when `dsh` is not on PATH), so **the recommended layout is a plugin folder > two levels under the Harness root** (as in the example); with `dsh` on PATH the bundle can live > anywhere. The script first uses `dsh` from PATH; when the current directory belongs to a Harness checkout and PATH has no `dsh`, it uses `pnpm --dir dsh`, and `DSH_BIN` may name an executable. After installation, **stop and restart the existing Web process** (stop it first, then start it again so the bundle reloads into the Web process), then refresh `http://127.0.0.1:3080`; the script does not start a second server. ### Install directly from Git Install straight from the plugin repository without a local checkout (the first install builds the client and host artifacts β€” `lib/client.js` and `lib/index.js` β€” from `src/client/` and `src/host/` with tsdown at install time): ```sh bash ./install.sh --git # default target is the web profile bash ./install.sh --git web # a profile can be supplied explicitly ``` The script resolves the git spec to this plugin's GitHub repository (override with the `GIT_SPEC` environment variable) and pins it to the current HEAD commit (`github:/#`), so a later push cannot silently change the installed code. pnpm β‰₯ 10 refuses to run a git dependency's `prepare` build by default, so the first `add` fails; the script parses pnpm's printed allowBuilds key, writes it into the profile's `pnpm-workspace.yaml`, and retries β€” no manual step. > ⚠️ Allowing the build means permitting the package's `prepare` script to run on your machine at install time (outside the agent sandbox). That is expected when installing from this plugin's official repository. The manual equivalent is `dsh plugin --profile web add github:yishengjun8/dsh-workspace-studio`: on failure, copy the allowBuilds key pnpm prints into the profile's `pnpm-workspace.yaml`, then re-run `add`. ## πŸ—‘οΈ Uninstallation ```sh bash ./uninstall.sh ``` Restart the existing Web process after removal; the built-in `ui-layout` returns when the bundle layer is removed. ## βš™οΈ Configuration The plugin row in `cordis.patch.yml` accepts: | Field | Default | Description | |---|---:|---| | `enableEditing` | `false` | Enables the Host write endpoint; this bundle patch sets it to `true`. | | `maxContextBytes` | `65536` | Explorer preflight maximum for selected-text UTF-8 bytes (1024–1048576); path-only contexts submit no file bytes. | | `maxPromptContextBytes` | `69632` | Host maximum for the complete rendered context, including the envelope and selected text (4096–2097152). | | `maxContextSourceBytes` | `10485760` | Maximum raw source bytes read for clean revision verification (1024–104857600). | | `maxEditableBytes` | `1048576` | Maximum UTF-8 bytes saved for one file (1024–10485760). | | `maxEntryNameBytes` | `255` | Maximum UTF-8 bytes allowed for one entry name (1–1024). | | `maxMutationBodyBytes` | `4096` | Maximum JSON bytes accepted by create and rename requests (128–65536). | | `maxPreviewBytes` | `1048576` | Maximum bytes read and returned for one file (1024–10485760). | | `enableUpdateCheck` | `true` | Enables the "Plugin Update" check and download (when `false` the check answers a disabled state, the download endpoint refuses, and the settings group hides after the first check). | > πŸ’‘ Edit the bundle's `cordis.patch.yml` to change these values. To prevent pnpm from reusing an installed local `file:` copy, run `uninstall.sh`, then `install.sh`, and finally restart the Web process. ## πŸ”’ Security Boundary **Path containment**: Host endpoints accept only registered Workspace IDs and relative paths; every read or write resolves the real path and confirms the target stays under the canonical Workspace root, so `..`, absolute paths, and out-of-Workspace symlinks are inaccessible. On Windows, paths containing `:` are also rejected (drive-relative forms like `C:`/`a:b` resolve surprisingly, and a colon is illegal in Windows file names). The endpoints also enforce Host, Origin, and Fetch-Metadata source checks equivalent to the built-in `/api` routes. **Write protection**: The write endpoint accepts `PUT` only when `enableEditing` is enabled; the body must be bounded UTF-8 text carrying the read-time `If-Match` revision, and a mismatch returns a conflict without overwriting. The target must be an existing regular file reached without any symbolic link. Create and rename reuse the same containment checks, require single-segment names, refuse existing targets, and reject Windows reserved device names (`CON`/`PRN`/`AUX`/`NUL`/`COM1-9`/`LPT1-9`) and names ending in a dot or space. The Host commits through a same-directory temporary file, file synchronization, and atomic rename, preserving the original permission mode when possible. **Context safety**: Editor context accepts only a relative path in a Workspace that owns the current Session (through its membership projection or the Session's canonical cwd); a path-only context carries no file bytes. The Host rejects symbolic links, validates clean selections against their disk revision, treats previews truncated by `maxPreviewBytes` as browser-authoritative, and prepends the rendered text to the direct prompt, so the ordinary Session log records the exact model-visible context. The conversation view folds that envelope into a one-line summary above the bubble showing the file name and range, and history renders the logged user message without rereading the current editor or disk. **Self-update protection**: `/update/check` and `/update/download` are open only to trusted sources (the same Host / Origin / Fetch-Metadata fence as the other endpoints); the check downloads and caches the main-branch tarball, and the install consumes exactly those cached bytes (re-verified β€” package name, version, and the `lib/` and `cordis.patch.yml` files) through a same-directory staging dir, a backup, and atomic renames, rolling back automatically on failure. The swap targets the plugin's own install directory (a local `file:` install only affects the profile copy). Both endpoints use only `codeload.github.com`: `github.com` / `api.github.com` / `raw.githubusercontent.com` are commonly redirected to a local TLS proxy by hosts-level GitHub accelerators whose self-signed certificate Node's own CA store rejects, while codeload is unaffected. Updates are triggered only by an explicit click in the settings page β€” never automatically β€” and never restart dsh by themselves. > ⚠️ These constraints govern only the explorer's file endpoints and Composer context; they do not change Agent permission policy, sandboxing, or tool capability. The endpoints provide application-level path-containment checks for trusted local UI use and do not replace Harness kernel-level isolation. ## πŸ“ Project Structure ```text . β”œβ”€β”€ package.json # Single-package manifest: bundle patch + client inject + exports β”œβ”€β”€ cordis.patch.yml # Disables the built-in root layout and mounts this plugin (self-referencing) β”œβ”€β”€ install.sh / uninstall.sh β”œβ”€β”€ src/client/ # Browser source (multi-module: an index.js entry shell + constants/locale/api/stores/…) β”œβ”€β”€ src/host/ # Host source (multi-module; bundles to lib/index.js) β”œβ”€β”€ lib/index.js # Host: bounded Workspace read, save, create, and rename API β”œβ”€β”€ lib/invariant.js # Shared Host invariants └── lib/client.js # Prebuilt three-pane layout, file tree, and editor ``` CodeMirror and its language modules are bundled into the prebuilt plain-JavaScript Client artifact; a local `file:` install runs no builds, while a git install rebuilds it with tsdown through the `prepare` script. To maintain the source, run `pnpm install --config.auto-install-peers=false` in the repo root and then `npm run bundle` to regenerate `lib/client.js` and `lib/index.js`. ## πŸ”„ Compatibility This version targets a Harness `0.1.x` checkout that provides the `conversation.input.dock` Slot, the session input resolver, and the conversation send service. The editor-context behavior is implemented entirely by this bundle and does not require modified Harness source; the send bridge adapts the concrete 0.1.x send, input-submit, and queue-steer seams, so a future release may need only a bundle-internal bridge update. A higher-priority patch that re-enables `ui-layout` competes for the root Slot; retain this bundle's `ui-layout` disable entry.