前言¶
DeepSeek Harness(DSH)的對話視圖通常以列表或畫布形式呈現。當對話輪次較多、分支較多時,列表難以一眼看清整體結構,畫布又沒有明確的分支語義。dsh-client-ui-branch-map 提供一張對話分支圖:把當前工作區的對話畫成一張圖,每條消息是一個節點,每個 Session 是節點上的分支引用,可以像 git 分支一樣從任意節點切出去。
這是什麼¶
dsh-client-ui-branch-map 是一個 DSH 客戶端視圖插件,由維護者 AnichHaveatry 維護。它的核心是把 DSH 工作區裏的對話渲染成一張分支圖:根節點代表 Workspace,消息節點代表一輪對話,Session 是指向某個節點的引用。歸檔的對話不會進入這張圖,子代理運行也不會進入。
核心功能¶
分支圖視圖¶
視圖包含四個基本元素:
- 作用域:恰好一個 Workspace
- 根節點:一個,代表 Workspace 本身
- 消息節點:用戶發出的一條消息(一輪對話)
- 邊:節點 → 在該會話裏緊隨其後的下一條消息
- Session:不是節點,而是指向某個節點的引用,和 git 分支指向 commit 一樣
一個 Session 的引用停在它自己擁有的最後一條消息上;還沒有自己消息的 Session 指向它被切出來的那個節點。兩個 Session 停在同一個節點,讀起來就像兩個 git 分支指向同一個 commit。
行佈局與根節點¶
行按輪號上升排列。輪號小的一定在前,所以這始終是一個合法的拓撲序;兄弟分支落在同一行帶,分叉就讀成兩條並排的 lane。
根節點在樹中水平居中:根級會話向左右兩側展開,新增會話不會把所有根線堆在左邊。
不畫什麼¶
- 子代理運行:子代理是代理自己派出去的工作,不是用戶與它的交互;子代理 Session、以及從子代理再分出來的 Session,都不進入圖,也不佔節點。過濾按血緣做:
isOwnConversation一路走到根,只要祖先是origin: "subagent"就整條排除。 - 歸檔的對話:歸檔集由宿主單獨給出:
useWorkspaces快照的archivedSessionIds。歸檔的 Session 仍然是它工作區的成員,所以不能靠成員表過濾,必須顯式減掉。減掉的是它的引用,以及只由它引入的節點。 - 當前打開的那條會話是例外:它被歸檔時照樣畫。工具欄的
已隱藏 N 條歸檔數的就是被減掉的其它分支。 - 存儲細節:沒有事件序號、沒有 Session id、沒有加載/刷新控件、沒有子代理計數。
操作控件¶
| 控件 | 效果 |
|---|---|
| +分支 on a message | ctx.sessions.fork({ sessionId, atSeq }) — 打開一個新 Session,帶上從根到這條消息的整條路徑,然後切過去。始終顯示,不藏在 hover 裏 |
| 改寫 on a message | 在消息之前分叉,然後把改寫後的文本作爲新分支的第一條 prompt 發出去;原分支不受影響。輪次起點在日誌第一條事件時沒有”之前”可切,這個按鈕不出現(從根開新分支即可) |
| a ref chip on a node | 點它打開那條會話現在所在的位置(它的 tip)。這和”從這個節點開新分支”是兩個不同的問題,標籤也分開寫 |
| 從根開新分支 on the root | 一個從工作區根開始的空 Session |
兩件容易混淆的事,界面刻意分開:
- 打開分支去它的 tip,因爲 ref 記錄的是這條線走到了哪裏;不會把你送到中間的某個節點。
- 從節點開分支纔是”站在這個節點上”:新 Session 的上下文是從根到它的路徑,下一句話從這裏繼續。
atSeq 是包含式的事件切點。一輪自己的 turn/start seq 是包含的,所以下一輪 turn/start 之前的那條事件就是這一輪的末尾——因此切點不會落在輪中間,新 Session 繼承節點的所有祖先、不會多帶一點。
數據與行爲邊界¶
這個插件不讀私有數據、不改對話、不聯網、不落盤。
- 讀:只用
conversation.view的標準客戶端 props:useSessions、useWorkspaces(含它的archivedSessionIds歸檔集)、useProjection("turnOutline");以及ctx.sessions.refreshProjections(id)(宿主語義:只讀投影,不啓動遷移)。 - 寫:視圖自身不寫任何東西。
+分支/改寫是用戶點擊後才調用的ctx.sessions.fork(...),和 DSH 自帶的分支按鈕同一語義;不點就什麼也不發生。 - 會話歷史:從不
retain其它 Session、從不打開別人的歷史;不讀useChat,不讀子代理目錄。 - 存儲:無。沒有 localStorage / sessionStorage / IndexedDB / cookie / 插件配置文件。
- 文件:插件自身不讀寫任何路徑。宿主可能按自己的投影緩存策略緩存派生值,但不會改會話日誌。
- 網絡:無。沒有 fetch、WebSocket、遙測或外部服務。
- 宿主半邊:
lib/index.js的apply是空的。不註冊服務、投影、路由或工具。 - 停用 / 卸載:Tab 消失;樣式表通過 effect 清理,locale 註冊隨 fiber 卸載;沒有需要清理的插件數據。
- 遷移:
refreshProjections不啓動日誌遷移;視圖也不打開別的會話,所以不會觸發格式遷移寫盤。
唯一”碰數據”的地方是用戶主動點分支產生的 Session,以及 openSession 的界面導航——這兩件事和 DSH 自帶的同名操作完全一樣。
安裝與啓用¶
這個包本身就是一個 DSH bundle:聲明瞭 dsh.bundle.patch,自帶的 cordis.patch.yml 按包名插入一行,不寫任何絕對路徑。
# cordis.patch.yml,隨包發佈
- insert:
- id: ui-branch-map
name: dsh-client-ui-branch-map
從 GitHub 安裝(倉庫自帶 lib/,安裝時不需要構建):
dsh plugin --profile desktop add github:AnichHaveatry/dsh-client-ui-branch-map
從 tarball 安裝:
dsh plugin --profile desktop add .\dsh-client-ui-branch-map-0.3.0.tgz
從目錄安裝(開發時用,link: 是活鏈接):
dsh plugin --profile desktop add C:\path\to\dsh-client-ui-branch-map
裝好之後重啓 DSH並刷新頁面。兩個緩存要到重啓才失效:
dsh-client-modules會按 Loader specifier 緩存每個包的 client bundle 路徑;- bundle 字節在激活時快照,改
lib/client.js不會熱更新。
兼容性:peerDependencies 聲明 @deepseek-ai/dsh: ^0.2.0-rc.2。不兼容的 DSH 會跳過這個 bundle 並點名,而不是掛上一個壞掉的視圖。pnpm 安裝時出現 peer 依賴警告是預期的:peer 由 DSH 安裝提供,不在 profile 裏。
這個包和 dsh-client-ui-canvas 是兩個獨立包、兩個獨立 Tab(canvas 與 branch-map),可以並存;想只留一個就卸掉另一個。
適用場景與注意¶
適合需要清晰查看對話分支結構的場景:多輪對話、多次分支、需要快速定位某個分支的當前位置。
插件以當前 dsh 進程權限運行,安裝前應檢查源碼與許可證。插件本身不落盤、不聯網、不讀私有數據,只通過標準客戶端 props 和 ctx.sessions.fork 與宿主交互。
短結尾¶
dsh-client-ui-branch-map 提供了一張清晰的對話分支圖,讓 DSH 工作區的分支結構一目瞭然。它像 git 一樣,用 Session 作爲分支引用,讓你可以方便地從任意消息節點切出新分支。
- 目錄頁:https://www.skillhub.cn/plugins/AnichHaveatry/dsh-client-ui-branch-map
- GitHub:https://github.com/AnichHaveatry/dsh-client-ui-branch-map