# dsh-task-board > A **task-board client plugin** for DeepSeek Harness Desktop. Adds a **「Tasks」** tab next to **Chat / Trajectory** in the conversation header, with a three-column kanban that can **truly execute** each task through the DSH agent engine. [English](README.md) · [中文](README.zh.md) ![dsh-task-board screenshot](docs/screenshot.png "Task tab with three-column kanban") --- ## Highlights - **Tab injection** — registers into the official `conversation.view` slot, sitting side by side with the built-in **Chat** (`chat`) and **Trajectory** (`trajectory`) tabs (same mechanism those tabs use). - **Three-column kanban** — `Backlog` / `In Progress` / `Done` columns, grouped by an arbitrary **task group** name; group headers collapse/expand all cards underneath. - **Rich task cards** — UUID `id`, `title`, `group`, Markdown `description`, `workDir`, `files[]` and `status`. - **Drag-to-execute** — dropping a card into **In Progress** creates a **brand-new DSH session bound to the card's `workDir`**, sends the card description as a queued prompt, and the agent really does the work. - **Live progress** — assistant output streams back onto the card in real time while the session runs. - **Automatic lifecycle** — when the turn ends and the session idles, the card auto-flows to **Done** with the final assistant message as its result; failures/timeouts return it to **Backlog** with a visible error. - **Re-run** — drag a Done card back into **In Progress** to execute it again. - **Local persistence** — cards survive reloads (browser `localStorage`, key `dsh-task-board:v1`). - **Theme-aware** — styled with DSH CSS variables (`--dsw-*`), auto light/dark. --- ## How it works (the 5-second version) A DSH "client plugin" is a single npm package with **two halves**: | Half | File | Role | |---|---|---| | Host half | `lib/index.js` | A Cordis plugin the Loader mounts in Node — a no-op for a pure-UI plugin | | Browser half | `lib/client.js` | Registered into the Web GUI as `{ inject: [...], apply(ctx) }` | `package.json` carries the two declarations the harness looks for: ```jsonc "dsh": { "client": { // browser-side declaration (platform MUST be "web") "platform": "web", "inject": [ /* client packages this module depends on in the boot graph */ ] }, "bundle": { // profile-bundle declaration (provides a Loader patch) "patch": "./cordis.patch.yml" } } ``` Inside `apply(ctx)`, the plugin registers a `conversation.view` entry: ```ts ctx.slots.inject('conversation.view', () => ctx.slots.register({ name: 'conversation.view', id: 'taskboard', order: 20, // chat = 0, trajectory = 10, taskboard = 20 locale: 'taskboard', label: () => t('view.task'), }, TaskBoardView), ) ``` When a card is dropped into **In Progress**, the browser half runs the real execution pipeline: 1. `ctx.sessions.create({ cwd: task.workDir })` — the host accepts any absolute `cwd`; a new session is created **per run** (its `sessionId` is stored on the card so you can reopen and inspect it later). 2. `binding = ctx.sessions.binding(sessionId)` → `binding.session.open()`. 3. `session.prompt([{ type: 'text', text: taskDescription }], 'queue')`. 4. Subscribe to `binding.eventSource` and stream `assistant/live-chunk` text back onto the card. 5. Poll the session snapshot: when the window shows `user/message` → `turn/end`, `running === false`, the queue and pending submissions are empty → mark **completed** with the last `assistant/message` text. 6. Errors (`promptError`, `lastAgentError`) or an 8-hour timeout send the card back to **Backlog** with the error shown. > Event-window note: session window entries are shaped `{ type: 'event' | 'transient', event: { type, seq, time, data } }` — the event kind lives on the **inner** `event` object. --- ## Install The plugin targets the desktop profile that runs your GUI. ### Option A — DSH CLI (recommended) Run inside a DSH terminal: ```sh dsh plugin --profile desktop add ``` Because the package declares `dsh.bundle`, the CLI appends it to `dsh.profile.bundles` in `~/.dsh/profiles/desktop/package.json` and installs it as a direct dependency. ### Option B — manual 1. Edit `~/.dsh/profiles/desktop/package.json`: - add the package under `dependencies` (a `file:` spec pointing at the absolute path); - add `"@linsibin/dsh-task-board"` to `dsh.profile.bundles`. 2. Run `pnpm install` inside the profile directory. 3. Restart DSH Desktop. ### Option C — npm ```sh npm install @linsibin/dsh-task-board ``` Then add `"@linsibin/dsh-task-board"` to `dsh.profile.bundles` in your desktop profile and restart DSH Desktop. > Never hand-edit `cordis.yml` — it is rewritten on every launch. Use `cordis.patch.yml` or the flows above. ### Enable **Restart DSH Desktop** (bundle-list changes are never hot-applied). After restart, open or create a conversation: the header tab strip (which already shows **Chat / Trajectory**) will now also show **Tasks**. > The tab only appears while a conversation is open — that is how the official `conversation.view` mechanism works, not a limitation of this plugin. --- ## Verify | Acceptance item | How to check | |---|---| | Tasks tab appears | Open any conversation → tab strip shows **Tasks** after Chat / Trajectory | | Create a card with group & files | **+ New Task** → fill title / group / description / workdir / files (one per line) → card appears in Backlog, grouped | | Drag triggers a real run | Drag card into **In Progress** → running indicator + streaming output; console logs `[dsh-task-board]`; a new session shows up in the session list | | Auto-flow on completion | Turn ends & session idles → card moves to **Done** with a result summary | | Re-run | Drag a Done card back into **In Progress** | | Group collapse | Click a group header to collapse/expand its cards | | Persistence | Reload the page — tasks are still there | **Failure path**: a rejected prompt, agent error, or 8 h timeout returns the card to **Backlog** with a ⚠ message; drag it in again to retry. --- ## Development ``` node scripts/build.mjs # rebuild lib/client.js from src/client.tsx node scripts/build.mjs --watch # rebuild on changes (for client HMR) ``` - **Offline build** — the script uses the TypeScript that ships inside the DSH profile (`~/.dsh/profiles/node_modules/typescript`) via `transpileModule` (syntax transpilation only, no type-checking, no network). Override with the `DSH_TS_LIB` env var if needed. - **Bundle purity** — at runtime the browser bundle may only `require()` platform seed modules (React, `@deepseek-ai/cordis`, …). This plugin therefore ships **zero third-party runtime dependencies**; type annotations in the source are for readers only. - **HMR** — after the first install, editing `src/client.tsx` and rebuilding rewrites `lib/client.js`; the always-on client HMR reloads that plugin in place (plugin React state is lost, sessions/runtime survive). Manifest changes still need a restart. ### Project layout ``` dsh-task-board/ ├─ package.json # dsh.client (web) + dsh.bundle (patch) declarations ├─ cordis.patch.yml # bundle patch: inserts this plugin as a Loader row ├─ tsconfig.json # editor/type-reading only; build does not use it ├─ lib/ │ ├─ index.js # host half (no-op Cordis plugin for the Loader) │ └─ client.js # browser half (BUILT artifact — edit src/, then rebuild) ├─ src/ │ └─ client.tsx # browser-half source: kanban UI, drag & drop, runner └─ scripts/ └─ build.mjs # TSX → CJS + ModuleLoader registration shell → lib/client.js ``` ### Notable details - Because the `file:` dependency install is a **copy**, after changing `src/client.tsx` + rebuilding you must refresh the profile copy (`pnpm install` may say “Already up to date” — copy `lib/client.js` over manually, or use `pnpm link` for a live dev loop). - Running against an arbitrary `workDir`: `session.create({ cwd })` accepts any absolute path, but whether the host fs/sandbox permits an unregistered directory depends on host configuration — register the directory as a workspace first if it gets blocked. - Every execution creates a **new** session in the GUI session list; open it anytime to review exactly what the agent did. --- ## Compatibility Built against DSH Desktop 2.0.5 internals (`@deepseek-ai/dsh-*` 0.1.2-rc.1) and verified on a live desktop profile. Internal APIs may change across DSH versions. ## License MIT