dsh-session-map
Manifest validA draggable, zoomable radial map of the session tree for DSH: see which conversation branched off which, and switch by clicking a node.
dsh-session-map
English | 中文
Adds a third tab to the DSH conversation view: a draggable, zoomable map of your session tree.
It answers the question the native session list cannot:
Which message, in which conversation, did this thread branch off from — and how many branches hang under it?
The native list is a line. Parent/child shows up as indentation, and once a tree has several branches the children all look the same. This plugin draws the tree as a map: click a node, you switch to that session.
What it looks like
[root · linear transform]
│
[linear map] ← parent, always directly above
│
┌───────────┴───────────┐
[image & kernel] [isomorphism test] ← children, spread across the lower half
\ /
[injective & surjective] [kernel & image] … ← shrinks automatically at 3+ children
The centre is the current session (the focus), the parent sits directly above, and children spread across the lower semicircle — the entire upper half is reserved for the parent. That rule is not just aesthetics: node cards are wide (276 px at the centre), so a child landing at dead-left or dead-right has its connecting line completely swallowed by the card, and the picture reads as "no line at all". (That was a real defect in v0.1.0, fixed in v0.1.1 — see the regression guard under Known limitations.)
- Click a node → switch to that session. (A click that ends a drag does not count, so panning never switches you by accident.)
- Whole-tree toggle lays the entire tree out at once (radial tidy-tree + layered guide rings).
- Drag = pan; wheel = zoom anchored at the pointer (same feel as a game map). There are also
− / +buttons and a "fit everything" button. - A breadcrumb across the top shows the path from the current focus to the root; the right side shows
depth N · M branches. - Status dot in each node's upper-right corner: running (cyan) / waiting for you (amber) / done (dim).
Install
dsh plugin --profile web add dsh-session-map
After restarting DSH, the conversation view gains a third tab: Conversation | Traces | Tree map. It sits after the two built-in tabs. If you do not see it, the plugin was installed but not mounted — check that its name is in dsh.profile.bundles.
Uninstall
dsh plugin --profile web remove dsh-session-map
This plugin does not modify any built-in code. It is purely additive; once removed, the environment is exactly as it was.
Structure
dsh-session-map/
├── package.json # manifest: dsh.bundle.patch + dsh.client
├── cordis.patch.yml # bundle layer: inserts itself into the config tree
├── dsh/
│ ├── index.js # host half (Node) — deliberately empty, see below
│ └── client.js # client half (browser) — all of the implementation
└── README.md
Why the host half is empty
dsh/index.js exports only name / inject = [] / apply(_ctx) {} and does nothing.
There is exactly one reason: the Cordis config tree needs a resolvable module entry point for the bundle to attach. Every capability this plugin has lives on the UI side; the host side has nothing to do. So it registers no routes, no services, reads no config — what it changes = zero.
Why the client half looks the way it does
An external plugin's client bundle goes through the DSH shell's lazy-CJS protocol:
window.__ModuleLoader__.load({
id: 'dsh-session-map',
factory: function (require) {
var React = require('react') // use the shell's copy; do not bundle your own
// ...
return { apply: apply, inject: ['slots', 'sessions'], __internals: { ... } }
}
})
React must come from require('react') — the shell's own copy. Bundling your own React means hooks cannot see the shell's runtime context.
Mount point
Registered into the conversation.view seat (a list-type seat; the built-in UI ships two tabs, "Conversation" and "Traces"):
scope.slots.inject('conversation.view', function () {
return scope.slots.register({
name: 'conversation.view',
id: 'session-tree',
order: 20, // after Traces
label: 'Tree map',
inject: function (sessionId) {
return {
sessionId: sessionId,
open: function (id) { scope.sessions.open(id) }
}
}
}, TreeMap)
})
Known limitations
Ordered by severity.
- 🔴 It gets crowded at
n≥5. If the centre session has more than 5 direct branches, the lower semicircle cannot hold cards that wide and adjacent nodes overlap. The root cause is a geometric hard limit: cards are 208 px wide, the container is only ~720 px, and 5 cards need 1040 px. But real data almost never hits this — across 69 real sessions scanned, a user's own conversation had at most 2 branches; the sessions with many children (e.g. 23) were all subagents. - 🟠 Subagent sessions appear in the tree. The data-side reason for the previous point: DSH's
useSessions.idsincludes all 69 sessions, 34 of which are subagents. The plugin currently treats them all alike. - 🟡 Whole-tree view is bounded by window size. Large trees require zooming out, which shrinks the text — an inherent cost of a canvas, not a bug. Use focus mode for detail.
- 🟡 Dragging has no inertia or bounds. The feel is "tracks your hand", not "flings and coasts". Deliberate.
- 🟡 Only
parentIdis honoured. An empty session whose parent has not been persisted yet shows as "upstream" but cannot be clicked through; say something inside it and it persists. - ⚪
labelis not localised — hardcoded English "Tree map". - ⚪ No HMR wiring: editing
client.jstakes effect on page refresh (the bundle route derives its rev from the content hash). "No DSH restart needed" was measured, not documented.
A regression guard
v0.1.0 had a real defect: at n=2 the children landed at dead-left and dead-right (±90°), and with Rx=223 against "centre half-width 138 + child half-width 104 = 242", the entire connecting line hid under the cards — 3 nodes on screen, 0 lines, reading as "feature not implemented". There is now a dedicated assertion guarding it (a visibleLen sampling method requiring every centre↔child visible segment to exceed 22 px, scanned over n=1..4). Whoever next touches the angles in layoutFocus will trip it first.
Other limits, stated plainly
- The virtual root is a display-only grouping node, not a session that exists in DSH. It adds one node at the "root" level (the real root count is the N written in its title).
- Forking from a source session with no completed turn is rejected by the host (
session/fork-unavailable); the plugin reports this honestly and retries once against the current main session, recording the fallback rather than hiding it.
License
MIT
Comments
Loading…
Similar plugins
by RyanZeeee
A visual, non-linear conversation canvas for DeepSeek Harness (DSH) — every turn is a node, every question grows a branch. DSH 插件:把线性聊天记录变成一张可以看、可以点的对话地图,每轮问答是一个节点,每次提问都是一条新分支。
★ 4
MIT
JavaScript
Sep 13, 2026
dsh plugin --profile web add dsh-chattreeby orphiczhou
Left-sidebar session tree for DeepSeek Harness: every session with its nested subagent descendants, click-through to multi-turn conversations, plus the tree_send tool for messaging any continuable ses
★ 0
MIT
JavaScript
Oct 2, 2026
dsh plugin --profile web add @orphiczhou/dsh-session-treeby Tasihi89
Visual conversation map for DeepSeek Harness — sessions as cards on a whiteboard: drag to arrange, double-click to chat, draw an edge to fork with injected context. ADHD-friendly spatial memory. · DSH
★ 103
MIT
JavaScript
Aug 23, 2026
dsh plugin --profile web add dsh-talk-mapby minhaoli
Enhanced session tree for the DSH sidebar (fork of ui-workspace): tag chips, group-by-tag, drag-to-move across workspaces, delete with backup.
★ 0
MIT
JavaScript
Oct 17, 2026
dsh plugin --profile web add dsh-session-treeA live conversation map for DeepSeek Harness web: real-time session tree with subagent lineage, running state, and one-click navigation.
★ 0
dsh plugin --profile web add dsh-conversation-mapby lwklbb
A DSH Web plugin that turns chat sessions into editable mind maps, with Markdown, Mermaid, PlantUML, XMind text, and TODO exports.
★ 3
MIT
JavaScript
Aug 17, 2026
dsh plugin --profile web add dsh-sessiongraph