dsh-agent-pill
DSH (DeepSeek Harness) web plugin: a ZCode-style agent activity pill (top-right status capsule) plus a popover summary panel, toggled with Ctrl+Alt+P.
- host half: aggregates the current conversation's Goal / Subagents / Agent status / workflow runs / background jobs and exposes a fenced JSON API (
/pill/api) with full control verbs. - client half: top-right floating status capsule + popover panel (goal card, workflow history with steps and observed files, subagent tree, job list with output/kill).
UI behavior
- Tooltip-style popover (v0.4.0): the panel expands as a light popover anchored to the capsule — no fixed right-side drawer. It flips across all four viewport edges to stay fully on screen, clamps to
min(360px, viewport)wide and ~70vh tall with inner scrolling, and closes on outside click, Esc, or the shortcut. - Collapsible sections (v0.4.0): Goal / Agent / Subagents / Jobs headers toggle their content; collapsed state is remembered in
localStorage(v0.13.0: sections default expanded — the capsule sees everything at a glance). - Draggable capsule: drag the pill to any screen edge; the seat persists in
localStorageacross reloads and stays clamped to the viewport on window resize. - Auto light/dark theme: the palette is driven by CSS variables that follow the DSH theme signal (
<body data-ds-dark-theme>), so the capsule and panel switch between the white and the dark ("moon night") scheme instantly — includingsystemmode following the OS. - Shortcut: Ctrl+Alt+P toggles the panel (click also works).
- Goal card shows the objective, phase, rounds, and elapsed time (ZCode-style), with a rounds progress bar (
roundsStarted / maxGoalRounds) and the activation state. - Capsule summary (v0.3.0): the pill shows the goal's elapsed time plus live badges — running subagents, running jobs, an active workflow run (
wf, with the current phase in the tooltip), and failed jobs (red). - Workflow history (v0.4.0): the most recent workflow runs (bounded ring of 5) are listed in the Workflow section. Each run expands to show its steps (each
agent()call: seq, label, phase, outcome) and the files observed while the run was active (from the host'sfs/observedfeed, deduped, attributed at run level — the feed has no session dimension). Settled runs keep their details until replaced. - Jobs as step entries (v0.4.0): each background job is rendered as a single step entry (status, output summary, timing) — jobs have no structured steps, so no file extraction is attempted.
- Subagent tree (v0.3.0): descendants are indented by depth, and each row shows its run duration and terminal stop reason (failed settles render red) — timestamps come from the host's
subagent/start/subagent/endobservation, since the durable listing carries none. - Usage section (v0.3.0–v0.12.0, removed): previously showed the session's token pressure and cost estimate; removed in v0.12.0 along with the Sessions fleet view — the pill now shows only what exists for the current session.
- Live tool call (v0.5.0): the current session's in-flight tool name is shown in the capsule strip and tooltip while running (from the session event feed;
toolSinceclears ontool/result). - Queued message badge (v0.5.0):
agent/inboxevents keep a per-session queued-message count; the capsule shows aqbadge (Cursor's queued-messages idea). - Completion notifications (v0.5.0): browser notifications fire once per event when a workflow settles, a background job fails, or a goal completes (permission requested lazily; a refresh registers settled ids without replaying them).
- Tool durations (v0.6.0): the capsule strip shows the latest tool event with its duration (
⛭ write · 0s,✓ edit done · 12s) —tool/call/tool/resultpairing, DSH executes tools serially, so a single in-flight slot pairs them; the timeline records every tool start/end. - Real context window (v0.6.0–v0.12.0, removed): the Usage pressure bar previously used the resolved model context window (
llm.resolveModelInfo); removed with the Usage section. - Step ↔ subagent linkage (v0.6.0): workflow step rows resolve their
childIdagainst the observed subagent rows and show the child's run duration and terminal color. - Empty-state hiding (v0.6.0): a section renders only when it has real content — Goal without a goal, Subagents without children, Jobs without entries, and an Activity section with no events are all hidden entirely. Together with v0.13.0's default-expanded sections, the capsule shows everything that exists and nothing that doesn't.
- Detail layers (v0.7.0): clicking a workflow run or a subagent row pushes a new detail layer inside the popover (back button in the header) instead of expanding in place — the workflow detail shows its full steps with subagent durations and observed files, the subagent detail shows its identity, mode, timing, terminal state and a stop control. The layer auto-returns when the target disappears and resets on session switch.
- Live capsule label (v0.8.0): the capsule text becomes the running workflow's
name·phase, falls back to the current tool name, then toAGENT— what the agent is doing is now visible without opening anything. - Status strip (v0.8.0–v0.11.0, removed): the popover header's one-line "what is happening now" was replaced by the v0.11.0 redesign — the capsule itself is the activity strip and the Activity timeline leads the popover.
- File diffs (v0.8.0): workflow file chips open a file detail layer with the collected result-time diff (
tool/resultmeta diffs fromdsh-tool-fs: new-file, edit, or overwrite; rendered as a line-level diff) plus a copy-path button. - Idle long-poll (v0.8.0): while everything is idle the client parks on a host long-poll (
POST /pill/api/poll, 30s cap) instead of polling every 1.5s — activity wakes it, then the active cadence resumes. - File list with inline diffs (v0.9.0): the workflow detail's Files section is now a list — each file shows its
+N/-Nline badge, expands inline to the diff (default: changed lines only, switchable to "with context"), carries a copy-path button, and no longer requires a third detail layer. - Adaptive popover width (v0.9.0): the popover width follows its content (clamped 320–520px, viewport-capped) via ResizeObserver; diff rows keep their width (
white-space: pre) instead of wrapping. - Activity timeline (v0.9.0): a new Activity section streams the recent host events (tool calls and completions, file activity with per-path merge counting, workflow phase changes, subagent starts/ends, goal changes; bounded ring of 40) — "eyes on what the agent is doing internally".
Interaction (v0.14.x, grill-me consensus — ZCode researched)
The plugin follows ZCode's task-status visibility (goal → rounds → checklist items → current round) plus Cursor's "files under each instruction":
1. Turn-tail file rows (conversation.chat.turnTail): each user instruction in the conversation shows the files that instruction handled (+N/-N badges, inline diff with changes-only/context toggle, copy path) — Claude Code's "Files changed" pattern, per session × turn. 2. Capsule = round strip: while busy the pill shows 第N轮 · 动作 (round number + the latest activity event), ⏳ 工具名 while an approval is pending (click to review), and the latest event otherwise; when fully idle it shrinks to a bare dot (drag and click still work) and regrows on the next activity. Click or Ctrl+Alt+P toggles the popover; drag to move. 3. Goal = round view (v0.14.0): the goal card (objective, phase, elapsed, rounds bar) is followed by a Rounds list — one row per turn: #轮号 · 标题 · ⚙N 工具 · 校验状态 (✓ completed / ⏸ blocked / ✗ error / … open), with that round's output files inline (diffs + Git status marker M/A/D/R/? from the host's git status --porcelain aggregation). 4. Popover (capsule-anchored): the activity timeline (full feed, first screen) followed by the console sections — Workflow (recent runs with file-count badges; detail layer with steps and inline file diffs), Subagents (descendant tree with stable identity colors; detail layer with a link to the workflow step that ran the child and that workflow's files), Goal (round view) and Jobs (with job detail layer). All sections default expanded and remember their state in localStorage.
Data is current-session only: round meta (title, tool count, end reason) is aggregated from turn/start / user/message / tool/call / turn/end; files are aggregated per sessionId × turn from tool/result diffs; the timeline is per session; sessionless events (workflow/subagent/fs) attach to the last active session; approvals come from approval/asked / approval/decided.
Install
dsh plugin --profile web add dsh-agent-pill
# restart the profile (the web composition disables host-side HMR)Features
| Section | Data source | Control verbs |
|---|---|---|
| Goal | goal/change session-event projection replay + live append feed (last-wins fold) | goal.pause / goal.resume / goal.complete / goal.clear (CAS {id, revision}, requires a live agent) |
| Subagents | ctx.subagents.listDescendants (durable tree, live-preferred) + process-local subagent/start / subagent/end timestamps | subagent.interrupt (human-parent authority; cancels a continuable child's current turn) |
| Agent | ctx.agents.get(id)?.status + agent/status events (idle / running); live tool-call block from the session event feed; agent/inbox queued-message count | read-only |
| Workflow | workflow/start / workflow/phase / workflow/agent-start / workflow/agent-end / workflow/end events (bounded history of 5 runs, each with steps); files from the global fs/observed feed while a run is active | read-only |
| Jobs | ctx.jobs.list(caller) + onJobsChanged invalidation (single step entry per job) | jobs.kill (registry stock API); jobs.output (event replay, never consumes the model's cursor) |
API routes
All routes pass the browser-trust fence (loopback Host or trustedHosts; cross-site requests refused) and answer {ok, value} / {ok:false, error:{code,message}}.
POST /pill/api/state—{sessionId}→ aggregated snapshot (goal / agent / subagents / jobs / services)POST /pill/api/goal.pause|resume|complete|clear—{sessionId, id, revision}POST /pill/api/subagent.interrupt—{sessionId, childId}POST /pill/api/jobs.output—{sessionId, id}POST /pill/api/jobs.kill—{sessionId, id, reason?}
Build from source
pnpm install
pnpm build # tsc host → tsc client → tsdown (lib/index.js + lib/client.js)- host half: plain ESM (
lib/index.js); runtime@deepseek-ai/*peers resolve from the deployment's module fallback. - client half:
window.__ModuleLoader__.load({id:'dsh-agent-pill', factory})protocol artifact (lib/client.js); platform modules external, everything else inlined.
Mount manually (without npm)
Create a junction from your profile's plugins/ directory to this package, then append to the profile's cordis.patch.yml:
- insert:
- id: agent-pill
name: 'dsh-agent-pill' # or a relative path like './plugins/dsh-agent-pill'> A patch entry must be an id-targeted override or an insert list — a bare - name: <package> row is skipped by applyEntryPatches.
> Restart the DSH profile after any cordis.patch.yml / bundles change (the web composition disables host-side HMR); client bundle changes only need a hard browser refresh.
License
MIT