Back to home@linsibin

dsh-task-board

No description

Stars
0
Language
JavaScript
Created
Sep 7, 2026
Updated
Sep 8, 2026
GitHub repo

Introduction

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 · 中文

dsh-task-board screenshot


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 kanbanBacklog / 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:

HalfFileRole
Host halflib/index.jsA Cordis plugin the Loader mounts in Node — a no-op for a pure-UI plugin
Browser halflib/client.jsRegistered into the Web GUI as { inject: [...], apply(ctx) }

package.json carries the two declarations the harness looks for:

"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:

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/messageturn/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:

dsh plugin --profile desktop add <absolute/path/to/dsh-task-board>

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 "dsh-task-board" to dsh.profile.bundles.
  2. Run pnpm install inside the profile directory.
  3. 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 itemHow to check
Tasks tab appearsOpen 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 runDrag 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 completionTurn ends & session idles → card moves to Done with a result summary
Re-runDrag a Done card back into In Progress
Group collapseClick a group header to collapse/expand its cards
PersistenceReload 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