Four Context-page enhancements (lane-ctx-deep, F1-F4): - F1 context window breakdown: replace percentage-only card header with a stacked-bar breakdown of input / cached / output token buckets, plus a right-side gauge showing the live window occupancy ratio. - F2 compact Config tab: fold the sprawling profile Config editor into a Config tab on the Context page card, with the same yml-leaf ordering as the top-of-window profile picker. - F3 intervention marker: on the intervention timeline, emit a marker glyph at each user-intervention row (turn-flow-glyph-style) so the card scans as a single stream instead of a header + separate list. - F4 subagent drilldown: when a turn's tool trace hits a subagent, the Trace panel's Config + Output tabs get a second row of Subagent Config / Subagent Output tabs immediately below, driven by the same fold-in-place shape the parent panel already uses. 4 new renderer modules (compact-config-model / context-window-breakdown / intervention-timeline / subagent-drilldown), 6 new test files (41 tests, all node --test style), 4 QA shoot scripts for CDP-driven regression screenshots.
DSH Desktop (reference host)
A minimal Electron desktop that talks to the DeepSeek Harness (DSH) runtime over its internal JSON-RPC 2.0 protocol. It is the reference host for the protocol: a place to see the whole capability surface working end to end before we build the "real" apps against it. Not a shipped product — a demo kept outside the official repo so the shell can move fast and the protocol can stay strict.
Demo walkthrough — 3.6 MB mp4, click poster to play.
Quick start
Two pnpm install steps: once at the repo root (installs tsx, which
this shell uses to run the runtime's .ts bins), then once inside
examples/desktop/ for the shell's own deps.
# from the repo root of your deepseek-harness clone
pnpm install
cd examples/desktop
pnpm install
pnpm start
First launch shows a two-step onboarding overlay (role · approval mode)
that writes a starter ~/.dsh-desktop/ and boots the stdio-deepseek
profile. From that point on, pnpm start picks up where you left off —
sessions and overlays live under ~/.dsh-desktop/. (See below for
what happens when you launch without DEEPSEEK_API_KEY set — you get
a one-click switch card, not a red wall.)
Prereqs: node 22.22+ / pnpm 11.7+, plus the DSH runtime SDK on disk. The shell auto-detects the SDK in two shapes:
- In-repo (default when you clone deepseek-harness): the shell
sits at
examples/desktop/and finds the runtime by walking up to the repo root. Nothing to configure. - Sibling checkout: if you've cloned this shell into its own
directory next to a
deepseek-harness-dev/checkout (the original dev-workflow layout), the shell falls back to that sibling. Prefers the.worktrees/integration/worktree when materialized — that's wheredaemon-demolives until it lands on master.
Set DSH_DEV_ROOT=… to force any custom layout.
The default profile is stdio-deepseek — a real DeepSeek model,
real tool calls, real approvals. That's on purpose (flipped
2026-07-18 per boss call): new downloaders should see the actual
model on the first prompt, not an echo bot. It needs
DEEPSEEK_API_KEY; the two supported places to put it are:
- A
.envfile at the DSH runtime root (repo root when you cloned deepseek-harness; the siblingdeepseek-harness-dev/checkout in the dev-workflow layout), one line, formatDEEPSEEK_API_KEY=sk-...— the runtime picks this up on spawn. - Or exported in your shell before
pnpm start(export DEEPSEEK_API_KEY=sk-...).
If no key is present when you launch, the shell doesn't wall you off
with a red banner — it renders a runtime card titled
"DEEPSEEK_API_KEY needed for real-model profile" with hint text
that spells out both options ("(1) set DEEPSEEK_API_KEY in .env or
your shell (see README Quick Start), or (2) try the keyless echo demo
to explore the UI first") and a Switch to keyless demo
(stdio-echo) button that one-click drops you into the echo profile
so you can walk every tab without a key. Any profile you switch to
persists to ~/.dsh-desktop/config.json and comes back on next boot.
The model dropdown filters itself to models the current profile can
actually reach, so if you switch to an echo profile you'll only see
mock-echo, and switching back to stdio-deepseek restores
deepseek-v4-flash / deepseek-v4-pro. See the Profiles table
below for the full matrix; the "What's real vs demo" section right
after that spells out which surfaces are live wire vs. fixture on
each profile.
Pages & Features
The sidebar nav groups the 14 pages into three bands: observation
(what the runtime is doing right now), iteration (loop-in tooling
you point at the runtime), and runtime & rest (the plumbing you
configure and the places you send the results). Pages marked
(demo) carry a demo chip on the nav item; the "What's real vs
demo" section below spells out exactly which surfaces are fixture and
which gap ticket tracks the missing wire.
Observation
Chat
The default pane. A conversation is a DSH session: the sidebar lists
all your sessions with a pulsing live-dot on whichever one has a turn in
flight, and the transcript replays cleanly when you switch between them.
Streaming assistant text, reasoning blocks, and tool calls all render
inline as they arrive; every tool call is a collapsible <details> block
keyed by callId, reasoning is a first-class bubble (not hidden behind
a toggle), and context/message / steering/message events surface as
📎 cards so you can see what the runtime added to the model's context
and why. A Cancel button appears mid-turn and cuts the stream via
session/cancel.
Session Tree
Sessions are not a flat list — DSH records lineage in the session header
(parentSession, seedLength) and emits subagent.started /
subagent.finished notifications when a child agent runs. The Tree
tab folds the flat session/list into a forest so you can see the
whole fork ancestry at a glance. Orphans (children whose parent is
missing from the list) surface with an (orphan) badge rather than
being silently promoted to roots. Inside chat, every assistant bubble
carries a hover-revealed fork from here button that mints a child
session at that exact seq and marks it in the tree with a ⑂ forks from here (N) card.
Context
A per-turn ledger of everything the runtime injected, compacted, or
recalled on the current session. Each event row shows the source knob
(inject / recall / compact), the payload preview, and the seq it
attached to, so you can walk backwards from any assistant reply to the
exact context slice that produced it. Per-knob chips call out which
gap-ticket (G) writeback is still pending — the page is real wire
for events that already ship (context/message, session/compact
outcomes) and honestly labels the rest.
Tracing
The project-wide runs table — every session across every profile in a
single eight-column aggregate: Name / Most Recent Run / Trace Count /
Error Rate / P50 / P99 / Total Tokens / Total Cost. Clicking a row
opens a tri-view drawer (Tree / Timeline / Graph — see Feature
highlights) that recursively unfolds a single session's event tree,
LLM/tool timing spans, and callgraph. Meant for the "which of my 200
sessions actually cost me tokens this afternoon" question, backed by
the same session/list projection the Chat sidebar reads.
Iteration
Playground (demo)
An isolated scratch runtime that lives right next to the Plugins pane.
Enter via the Playground button; a fresh daemon boots against a
throwaway leaf so you can try a plugin lineup without disturbing your
main session. Discard throws the whole thing away; Apply promotes
the tried lineup back into your user overlay. The compare drawer picks
a live session, copies its first user message into the playground
input, and paints the live session's events alongside the playground
stream — same prompt, two overlays, side by side. The scratch boot is
real; the standalone Playground page hasn't fully landed, so the nav
button currently shims to Plugins.
Hub (demo)
The plugin discovery surface — the "shop" side of the same list the
Plugins page edits. Plugin listings themselves are real wire
(daemon/plugins/list), but the surrounding catalogue metadata
(categories, descriptions, ratings, install counts) is fixture pending
G1 / G11 / G12. A page-footer SDK legend spells out which chip on a
card is coming from where.
Bench
The benchmark harness surface — pick a benchmark, pick a lineup, watch
the runner tick through cases and stream the pass/fail column into
your session tree. The benchmark list and detail views are fixtures
until the wire methods land; the page header carries a legend chip
demo · G18/G19/G20 pending so you can tell fixture rows from live
ones at a glance.
Rubrics (demo)
The RL-annotation surface: pick a session, walk its turns, attach a
rubric score + a written justification to each assistant reply.
"Create from scratch" opens a rubric editor; "Import from…" pulls a
rubric spec from a plugin. Drafts saved live in memory until the G1
seam lands — the page-header legend demo · G1 seam pending calls
that out.
Runtime & rest
Plugins
DSH is a cordis leaf — a flat list of {id, name, config?} entries —
and the Plugins tab is where you see and shape that list. Three
sub-areas: Installed (base entries and user-overlay entries side by
side; toggle any row, or Add plugin… to append a new one — changes
stage into your user overlay and ship on the next Apply + restart),
Browse (a curated marketplace read from config/plugin-index.json;
cards carry icon, package name, permission badges (net / fs /
subprocess), and a one-click Install that writes an overlay patch),
and Vibe a plugin (opens a chat under a leaf that mounts
@deepseek-ai/dsh-tool-cordis so the model gets cordis_inspect,
cordis_mount, cordis_unmount and can literally write plugin code
that extends its own runtime in a node:vm sandbox — two clicks from
"I need a plugin that does X" to a running plugin that does X).
Runtimes
Local runtime profiles and their isolated daemons in one place — one
row per profile with a status dot, PID, socket path, and the compose
recipe (which cordis leaves it's built from). Real wire on the plugin
list (daemon/plugins/list); the compose recipe view is fixture-tier
with a visible composed locally · gap G8 chip. Meant for the "which
daemon am I actually talking to right now" question when you have
several profiles alive.
Missions
Single-screen view of everything happening across every session at
once. Built for the "agent has fanned out" case — plan execution,
code-review sweeps, research bundles — where the flat chat view stops
being enough. Three subviews over the same in-memory aggregate:
Tree (one info-dense row per session with status dot, title,
last-activity, event / tool-call / todo counts, and the last-event
summary), Topology (hand-rolled SVG layered layout of the
parent→child DAG; running nodes get a pulsing halo, edges into or out
of a running session carry a dashed flow animation), and Board
(kanban over todos aggregated across every session that emitted a
todo/write, three columns, each card badges back to its originating
session). Every card click jumps back to Chat for that session. The
header button mock: mission demo injects a synthetic 3-level 8-node
scenario so every subview is demoable without a live daemon; the
default empty-state preview shows three ghost cards labelled preview
until a real todo/write replaces them.
Growth
An auditable self-evolution log. Every plugin install, toggle, vibe
session, overlay apply, and onboarding choice appends a line to
~/.dsh-desktop/growth-log.jsonl; the page turns that into a heatmap
- timeline + identity card. Deliberately "facts, not persona copy" — every timeline node is clickable, and either drops you into the actual session that produced it or shows the raw log row. Each node also has an Ask DSH about this pill that opens a chat scoped to that entry.
PRs
A PR list that lives one click away from the chat. gh pr list runs
in the current profile's cwd (default: the DSH dev clone), so the
list is the real PRs against deepseek-harness/deepseek-harness.
Rows carry state / title / head → base / diff totals / relative
time. Clicking a row opens the PR in your default browser; the
trailing Ask DSH button mints a fresh session, seeds it with a
scoped review prompt, and switches you to Chat. Two clicks from PR
list to a running review chat. Falls back to a small demo dataset
when gh isn't installed or you're signed out, with a demo data
badge and a banner explaining the fix.
Settings
Profile switch, model dropdown, DEEPSEEK_API_KEY presence check, and
the local pricing table (pricing lives in the shell, not the daemon,
so tokens-to-cost math is auditable client-side). Also where you
reset onboarding, override DSH_DEV_ROOT / DSH_ARTIFACT_DIR, and
inspect which plugins the current profile composes. Every knob here
is real wire.
Off-nav affordances
Quick chat
A floating composer that opens above whichever tab you're on. Trigger with ⌘⇧Space (or Ctrl+Shift+Space on Linux/Windows), or click Quick chat in the chat pane header. The card shows the five most recent sessions with click-to-jump, plus a fresh composer. Submitting mints a new session, switches to Chat, and sends the prompt. Meant for the "quick thought, don't lose my place" case.
Devtools drawer
The user-visible embodiment of DSH's model-visible ⟺ logged contract:
every session.event the runtime shipped over the wire lands here.
Dev-facing families (hook/*, request/header*, approval/*,
permission/*, bash/sandbox-mode, step/*, tool/code-dispatch, …)
that would otherwise spam chat live here instead. Open with the ⚙
button in the chat header, or press ⌥D. Preset filters (All /
Approvals / Hooks / Requests), auto-populated type chips, and a
case-insensitive search box that runs against event type, session id,
or pretty-printed payload. Auto-scroll follows the tail; a 500-entry
ring buffer accumulates in the background so opening the drawer later
still shows the recent tail.
Feature highlights
The affordances that cut across pages — the recurring ways the shell teaches you to read what the runtime is doing. Every one of these is a place where DSH departs from a "chat app that also lists tools" and becomes a visualiser of the runtime's actual state.
- Trace tri-view (Tree / Timeline / Graph). One session, three
reprojections of the same event stream. Tree is the recursive event
hierarchy (turn → tool call → sub-events). Timeline is a Gantt-style
span chart with LLM latency and tool latency on separate lanes.
Graph is a callgraph over
parentSession+ subagent edges. Wired from the Tracing table and from any assistant bubble's ⋯ menu. - Recursive collapsible Fields tree. Every event payload — no
matter how deep — renders as a folder-tree of
field: valuerows you can twist open one level at a time. No "click to expand JSON in a modal"; the whole payload is the UI. The same widget backssession.eventinspection, tool-call args/results, and the{ }drawer. - Reasoning as a first-class bubble. Reasoning deltas stream into their own bubble with a distinct visual weight, not folded behind a "show thinking" toggle. The runtime emits them; the shell renders them; if the model has none, the bubble simply doesn't appear.
- Diff and terminal tool cards. Tool calls that carry a
filesystem-diff payload render a unified-diff card with per-hunk
syntax highlighting; tool calls that carry a shell/bash payload
render a terminal card with a scrollback pane. Both fall back to the
generic collapsible-details view if the
metadiscriminant is missing. - Fork & edit-rerun. Every assistant bubble has a hover-revealed
fork from here button; use it to mint a child session seeded at
that exact seq, edit the user turn, and let a different reply
stream in. The child is anchored back in the session tree with a
⑂ forks from here (N)card so the branching is legible. { }JSON drawer — zero loss. Every event card has a{ }button in the corner that swings a drawer holding the raw JSON of the sourcesession.event, verbatim. The pretty renderer is a projection; the JSON is the source of truth, always one click away.- Compaction visualisation. When the runtime emits a
session/compactoutcome, the compacted range renders as a collapsed banner inline in chat with the summary + token delta side by side, and the Context page ledger anchors a per-turn marker at the same seq. You can always see where the model's memory got trimmed and by how much. - MCP surface. MCP tools are first-class citizens in the plugin list, the tool-call renderer, and the plugin catalog — same widget chrome, same permission badges, same Vibe hooks. An MCP tool is just another plugin the model sees.
- Demo-chip convention. Every fixture-backed surface labels
itself: a
demochip on the nav item for whole-page fixtures, ademocorner badge on individual mock-minted cards, amock · <method> not on wire yetchip on card headers where the UX is real but the method is proposed, and a page-header legend on mixed pages that names the gap ticket. Fixture vs real is always legible. - Real-model default profile.
stdio-deepseek(real DeepSeek, real tool calls, real approvals) is the default on first launch (flipped 2026-07-18) so new downloaders see the actual model on the first prompt, not an echo bot. Keyless demoing is one click away via the Switch to keyless demo (stdio-echo) card that renders in place of a red-wall error whenDEEPSEEK_API_KEYis missing.
Profiles
The profile dropdown in the sidebar swaps the whole runtime. Three you'll actually use, plus two vibe variants:
| Profile | What it's for | Needs |
|---|---|---|
| stdio-echo | Keyless demo, works on master. Direct-spawns the jsonrpc-demo bin (present on deepseek-harness master) over stdio. Runtime state (plugin list, sandbox mode, compact) is unavailable because there's no daemon to ask, but conversation, tool cards, forks, and devtools all work. This is the one-click fallback offered by the missing-key card. |
none |
| stdio-deepseek | Real DeepSeek adapter, real tool calls, real approvals. Default on first launch (2026-07-18) — new downloaders should see a real reply, not the echo bot. Use this for actual work, and to demo any live-tool behaviour. | DEEPSEEK_API_KEY in the repo's .env or your shell |
| daemon-echo | Full-feature demo, keyless. Boots the real daemon over a Unix socket; the model is a mock that echoes your input back. Every renderer capability (daemon plugin list, sandbox toggles, live compact) is demoable. Not yet available on master — the daemon-demo bin lives in the .worktrees/integration worktree of the dev clone until it lands upstream; wait for that or use DSH_DEV_ROOT to point at a checkout that has it. |
daemon-demo bin (integration worktree only) |
| daemon-vibe-echo | daemon-echo + the vibe leaf loaded. The Vibe entry point is gated (mock-echo can't actually compose plugins) but the leaf is there for UI walkthroughs. Not yet available on master — same reason as daemon-echo. | daemon-demo bin (integration worktree only) |
| stdio-vibe-deepseek | Real Vibe. cordis_mount etc. run under the DeepSeek adapter and the model can extend its own runtime. |
DEEPSEEK_API_KEY |
The model dropdown right below the profile dropdown filters itself to
models the current profile can actually reach: mock-echo on the echo
profiles, deepseek-v4-flash / deepseek-v4-pro (plus their [1m]
long-context variants) on stdio-deepseek. Switching profile also
resets the model to the profile's default, so you can't accidentally
end up on a model your current runtime has no adapter for.
What's real vs demo
Every surface in the shell is either driven by a live JSON-RPC method or filled with an on-disk fixture so the whole capability is walkable without a live daemon. Both are useful for different reasons — real surfaces show you what a plugin author can actually hook into today, fixtures show you the shape the runtime will emit once the wire lands. The two are visually distinguishable so you can tell at a glance:
demochip on the nav item — the whole page is fixture-only.democorner badge on a card — the card was minted by amock-*debug button or a fixture seed, not by a real wire event.mock · <method> not on wire yetchip on a card header — the card family is real UX; the method is proposed but not yet on the shipped protocol.- page-header legend chip (e.g.
wire · file-tier · G<n>on Hub / Runtimes / Bench) — the page mixes real wire and fixture; the chip spells out which is which and which gap-ticket (G) tracks the missing wire.
The current split, on the default stdio-deepseek profile (the same
9-real / 5-fixture set applies to daemon-echo — the model
underneath swaps, the surfaces don't):
Real wire (9 surfaces):
- Chat —
session/prompt+session/cancel+ streamingsession.event(assistant chunks, reasoning deltas, tool calls, context injections all reach real events). - Session Tree — real
session/listfolded into a forest viaparentSession/seedLengthin eachSessionListEntry.header. - Context page — real
session.eventledger (inject / recall / compact events all wire); per-knob chips call out which G writeback is still pending. - Tracing page —
session/listprojection (eight-column table over real sessions). - Plugins → Installed / Browse —
daemon/plugins/listandplugins:addare real wire; the curated Browse index is a fixture clearly labelledCurated demo index. - Runtimes —
daemon/plugins/listis real; the compose recipe view is fixture-tier with acomposed locally · gap G8chip. - PRs — real
gh pr listin the profile'scwd; the fallback demo dataset is only used whenghis missing / signed out, and shows a visibledemo databadge. - Devtools — every
session.eventthe runtime shipped shows up here, live. - Settings — profile switch,
DEEPSEEK_API_KEYpresence check, and the local pricing table are all real (pricing lives in the shell, not the daemon).
Fixture-only (5 surfaces):
- Playground —
demochip on the nav item. The scratch runtime boot is real, but the entry point currently shims to the Plugins tab; the standalone Playground page hasn't landed. - Hub —
demochip on the nav item + page-footer SDK legend. Plugin listings are real wire; the surrounding catalogue metadata (categories, descriptions, ratings) is fixture pending G1 / G11 / G12. - Bench —
demochip on the nav item + page-header legend (demo · G18/G19/G20 pending). The benchmark list and detail views are fixtures until the wire methods land. - Rubrics —
demochip on the nav item + page-header legend (demo · G1 seam pending). Drafts saved from "Create from scratch" live in memory only. - Missions empty-state preview — the three-card ghost kanban
("Draft the release notes", "Refactor the compact seam", "Land the
artifact preview PR") is a
previewfixture; real todos replace it as soon as any session writes atodo/write.
The full audit — every card, every wire method, every gap ticket
(G1..G21) — lives in docs/review-demo-labels.md.
Known limitations
The demo is deliberately narrow in a few places — either because a wire method is still landing, or because the runtime capability doesn't exist in the target profile.
- stdio profiles don't have runtime-state features. No plugin
list, no live sandbox-mode badge, no
session/compactbutton — those read/write daemon state that only the daemon profiles expose. session/compactis proposed, not shipped. The Compact now button works if the daemon has acompact-basic(or equivalent) leaf mounted; on MethodNotFound the button greys out with an explanatory tooltip. No mock compaction is fabricated.session/forkis still landing. The fork from here button and the tree smoke scenario fall back to a syntheticsession/newand badge the child(mock)so callers know it's an empty child, not seeded.- Old sessions can't be resumed after
kill -9. The daemon's SDK server owns a per-connection sessionId map. Respawn works and new sessions work; resuming a session that predates the crash does not. - Plugin toggle is full-daemon-respawn. The Apply + restart
button tears the daemon down and brings it back up. Per-plugin
dispose + re-mount is on the runtime side's list (
daemon/plugins/toggle,daemon/plugins/reload). - Mission Control has no persistence. A page reload wipes the
aggregate and reseeds from the next
session/listrefresh — the view is a live overlay, not a store. - Artifact preview opens in your default browser. No embedded
webview, no cloud tunnel; the shell hosts a
127.0.0.1static server and drops a card into chat when a new file appears in the artifact dir. - Growth reads jsonl +
session/list. A follow-up will migrate to thesession/list+session/eventsaggregation so events that never touch the overlay (pure chat activity) also show up. - Widget cards are demoable via the header mocks. No live tool on
any shipped profile emits
card: 'widget'yet; the threemock: widget · …buttons inject a synthetictool/call+tool/resultpair so the pipeline is exercised end to end.
For developers
Everything below this line is implementation notes. If you're just using the demo, you're done above.
Architecture
Topology mirrors ChatGPT.app: an Electron main process spawns the DSH
daemon (long-lived host process) and connects a Unix-domain socket
for JSON-RPC frames; the renderer only renders. A stdio-* fallback
is kept for airgapped smoke tests and for demoing without a daemon
build.
+-----------------+ IPC +------------------+ unix-socket JSON-RPC v2 +-----------------+
| renderer | <--------------> | Electron main | <---------------------------> | DSH daemon |
| (vanilla JS) | preload API | (RuntimeSuper + | newline-delimited frames | (daemon-demo) |
+-----------------+ | DaemonSuper — | +-----------------+
| respawn/probe) |
+------------------+
Fallback (stdio-echo / stdio-deepseek):
direct-spawn jsonrpc-demo bin, stdio frames
Structure
src/
main/
jsonrpc-client.js pure JSON-RPC 2.0 framing (unit-tested)
transport.js StdioTransport + SocketTransport
runtime.js supervisor: transport + client + crash re-spawn + initialize
daemon.js probe+spawn+respawn for the daemon profile
profiles.js which cordis.yml + model to launch
gh-prs.js gh CLI wrapper (pure)
artifact-server.js 127.0.0.1 static server + SSE live-reload
main.js BrowserWindow, IPC surface
preload/preload.js contextIsolation bridge
renderer/
index.html two-pane layout
style.css vanilla, no framework
renderer.js session-event → DOM dispatch
(~30 IIFE modules — mission, plugins, growth, playground, quick-chat, devtools, …)
test/
*.test.js 439 unit tests (node --test, no Electron)
smoke-runtime.js headless smoke scenarios (stdio / daemon / kill / tree)
Protocol surface used
From packages/ui/jsonrpc/src/protocol.ts (v2):
initialize({ cwd, model, protocolVersion: 2, capabilities: {interruptions:true} })→{ serverInfo, protocolVersion, capabilities }session/new({ sessionId })→{ sessionId }session/prompt({ sessionId, contentBlocks })→{ accepted: true }session/cancel({ sessionId, reason })→{ cancelled }session/list({})→{ sessions: [{ sessionId, header, live, persisted }] }session/events({ sessionId })→ metadata list;session/events({ sessionId, seq, before, after })→ full-event windowshutdown→{}
Server→client requests:
session/interrupt({ sessionId, interruptId, payload | spec })→{ outcome: 'accepted', payload } | { outcome: 'rejected' } | { outcome: 'cancelled' }
Notifications: session.event, session.finished, subagent.started,
subagent.finished.
Daemon-only: daemon/ping → { name, version, pid, startedAt }.
Proposed (not yet in shipped protocol; graceful fallback in the shell):
session/fork, session/compact, daemon/plugins/list,
daemon/plugins/toggle, daemon/plugins/reload,
daemon/persona/get.
Tests
pnpm test # 439 unit tests (node --test, no Electron)
node test/smoke-runtime.js stdio # stdio-echo end-to-end
node test/smoke-runtime.js daemon # daemon-echo + session/list + prompt
node test/smoke-runtime.js kill # SIGKILL daemon → auto-respawn + reconnect
node test/smoke-runtime.js tree # session tree + fork lineage (mock fallback)
node test/smoke-runtime.js all # everything (~90s)
Manual verification recipes
Kill-recovery. In another shell, pkill -9 -f daemon-demo/src/bin.ts.
Status bar flips running → crashed → respawning → running on schedule
[0,300,1000,2500,5000]ms. Send a new prompt — new session works;
old sessions can't be resumed (see Known Limitations).
Approval card. Header debug button mock-approval renders an inline
card wired to the real interrupt-resolution path. In a coding-agent
profile with a real bash tool call, a live session/interrupt request
from the daemon hits the same handler and gets an accepted /
rejected / cancelled response over the JSON-RPC id.
Plugins toggle. pnpm start, click Plugins, uncheck bash,
click Apply + restart, wait for status dot green, reopen the tab —
bash shows dim / unchecked / still source=base.
Onboarding reset. rm -rf ~/.dsh-desktop && pnpm start — overlay
covers the app, pick role + approval mode; overlay dismisses and
runtime restarts once. Or use sidebar → Reset onboarding.
Vibe. Ensure your DSH runtime .env has DEEPSEEK_API_KEY (see
Quick Start for .env locations). Sidebar profile → stdio-vibe-deepseek.
Plugins → Vibe a plugin. A new session opens; ask it to mount a
plugin and watch a cordis_mount tool block appear.
Wire needs owed by the protocol team
session/fork(see Known Limitations).session/compact(see Known Limitations).daemon/plugins/list/daemon/plugins/toggle/daemon/plugins/reload(per-plugin dispose + re-mount).daemon/persona/get+ role templates on the daemon side; today the role choice from onboarding lives in~/.dsh-desktop/config.jsonon the shell.SessionListEntry.header.seedLengthpopulated end to end so fork markers can anchor at the exact seq rather than the current tail.
Adaptive layout, widget channel, artifact preview
Three cross-cutting affordances documented in detail in the design docs
under docs/:
- Adaptive layout — four heuristic buckets (chat / code-review /
artifact / monitor) auto-toggle a body class and a handful of CSS
variables based on the recent event mix. Manual lock available; see
src/renderer/layout-heuristics.jsandlayout-controller.js. - Widget channel — inline interactive cards that ride on the
existing
tool/resultevent'smetafield. Seedocs/widget-channel-design.mdfor the wire shape, plussrc/renderer/widgets.jsand the three header mocks. - Artifact preview —
127.0.0.1static server + SSE live-reload that watches~/Library/Application Support/dsh-desktop-demo/.artifacts/(override withDSH_ARTIFACT_DIR=…). Opens in your default browser; no embedded webview. Tool-driven and debug-mock paths both exercised. Seesrc/main/artifact-server.js.
Design docs
Deeper background lives under docs/: arch-review-report.md,
capability-ui-coverage.md, product-flow-review.md,
product-ia-design.md, qa-walkthrough-report.md,
ui-refs-distilled.md, widget-channel-design.md.


