前言¶
用 DeepSeek Harness(dsh)跑編碼智能體時,常見情況是:某一輪提示寫偏了,Agent 已經改了一堆文件,對話上下文也跟着跑遠。這時只靠 Git 看 diff 不夠——diff 能告訴你現在改了什麼,但不會按「某一條用戶消息發出之前」把工作區和對話一起收回來。dsh 原生的「分支新對話」只分對話、不動項目文件,兩邊很容易對不上。
dsh-turn-rewind 做的就是這件事:在每條用戶消息上掛一個回退入口,按 Turn 把項目文件恢復到發送該消息之前,並可選擇從那一輪重新開對話。本文按社區目錄頁、GitHub 倉庫 README / README.zh.md、package.json 與官方 Harness 倉庫交叉覈對後整理。
DeepSeek Harness 的官方定位是「一切皆插件」,源碼在 deepseek-ai/deepseek-harness。下面介紹的社區插件目錄(deepseek-harness-plugin.com)是獨立站點,與 DeepSeek / 幻方沒有官方從屬關係,不要把它當成官方應用商店。
這是什麼¶
dsh-turn-rewind 是一款會話與消息類插件,由 Anionex 維護,倉庫爲 Anionex/dsh-turn-rewind。npm 包名是 @anionex/dsh-turn-rewind,當前 package.json 版本爲 0.1.1,許可證 BSD-3-Clause。截至 2026-08-17,GitHub 與目錄頁均顯示 65 stars。
它解決的問題可以概括成一句話:給 DSH Session 加一條圍繞工作區改動的安全邊界——先建恢復點,Agent / 用戶 / 外部程序改完工作樹後,再按路徑預覽差異,由人按下最終按鈕才真正恢復。
倉庫把兩層名字分得很清楚:
- Turn Rewind:用戶看到的功能名、倉庫名和 Profile Bundle 名。
- Change Ledger:底層持久恢復引擎。
ctx.changeLedger服務、磁盤格式和存儲路徑繼續用這個名字,因爲它描述的是可複用的快照與恢復層,而不只是 Web 上的回退按鈕。
本倉庫是標準 DSH Profile Bundle:package.json 聲明 dsh.bundle.patch,cordis.patch.yml 直接掛載 @anionex/dsh-turn-rewind,不修改 DSH 主倉庫。
核心功能¶
按用戶消息錨定恢復點¶
當 Profile 同時提供 DSH Agent 服務時,插件會在每輪第一次 agent/pre-step、Agent 開始處理用戶消息之前,保存一份隱藏的文件狀態。保存失敗只會讓該消息無法回退,不會攔住用戶這一輪。
在 Web Profile 中,每條用戶消息下方會在時間和原生「複製」之後多一個純圖標「回退」,提示是「恢復到發送這條消息之前」。打開後先檢查已保存的文件狀態,給出分頁預覽,再讓你選擇恢復方式。
兩種恢復模式¶
| 模式 | 項目文件 | 對話 |
|---|---|---|
| 恢復文件並從這裏繼續(默認) | 先自動備份當前狀態,再恢復項目文件 | 創建並打開一個結束於所選消息之前的新 Session,並把該消息原文填回輸入框 |
| 只恢復文件 | 同樣先備份再恢復 | 當前 Session 保持原位,內容不變 |
DSH Session 日誌只追加、不改寫,所以「從這裏繼續」會新建 Session,而不是截斷原對話。選中第一條消息時,Host 在同一工作目錄創建空 Session;選中後續消息時,在上一輪已完成的 turn/end 處分叉。原 Session 始終保留。
如果項目文件已經是發送所選消息之前的狀態,Turn Rewind 不會退化爲「只開對話分支」,而是提示無需恢復,並引導使用 dsh 原生的分支新對話。倉庫明確區分兩者:分支新對話只分對話、保持項目文件不變;Turn Rewind 一定恢復項目文件,再由你決定要不要開新對話。
先審閱、後寫入¶
恢復生命週期按倉庫說明是這樣走的:
創建恢復點
↓
agent / 用戶 / 外部程序修改工作樹
↓
預覽逐路徑變化
↓
審閱全部或部分恢復計劃
↓
在回退彈窗中按下最終恢復按鈕
↓
先建救援點 → 恢復 → 哈希驗證
彈窗預覽會基於當前工作樹生成一份短期、會話綁定的恢復計劃,這一步不改任何文件。真正修改前會再檢查所選路徑和倉庫狀態,並先創建自動備份。預覽之後出現的相關變化會使計劃失效。同一工作樹只要還有正在運行的 Agent(包括當前來源 Session),恢復會被阻止;空閒 Session 不阻止。
插件不會自動 commit、stash、reset、切分支、改 Git index,也不會替你判斷某項改動「應該回滾」。恢復後的內容可能表現爲當前 HEAD 上的未提交修改,因爲 commit、分支、HEAD 和 index 都保持原樣。Git 正在合併、變基等操作時仍會阻止恢復。
Change Ledger 負責什麼¶
普通 Git 面板可以展示當前 diff,但不擁有完整、持久的恢復生命週期。Change Ledger 獨立負責:
- 內容尋址的恢復點 manifest
- Git worktree、HEAD、分支和進行中 Git 操作的狀態圍欄
- 從審閱到執行之間的 stale plan 檢測
- 短期確認碼與 DSH 人工批准雙門檻
- 每次恢復前自動建立救援點
- 恢復後的內容哈希驗證
- 恢復失敗後的自動回滾
- DSH 重啓時對未完成操作日誌進行對賬
- 可供其他插件依賴的
ctx.changeLedger公共服務
持久格式見倉庫 docs/FORMAT.md,安全與故障模型見 SECURITY.md。
安裝與啓用¶
社區目錄頁給出的安裝命令如下,在 DeepSeek Harness 終端中運行即可:
dsh plugin add github:Anionex/dsh-turn-rewind
dsh CLI 會從 GitHub 解析插件並安裝到當前配置。如需可復現安裝,目錄頁建議固定 commit 哈希:
dsh plugin add github:Anionex/dsh-turn-rewind#<commit>
把 <commit> 換成倉庫裏實際的 commit 哈希。目錄頁同時提醒:插件以當前 dsh 進程的權限運行,安裝時可能執行代碼。安裝前請檢查源代碼倉庫和許可證。
倉庫 README 還給出了按 Profile 掛載的寫法。先在檢出的插件目錄構建,再分別加到需要暴露該服務的 Profile:
pnpm install --frozen-lockfile
pnpm run check
dsh plugin --profile web add @anionex/dsh-turn-rewind
dsh plugin --profile headless add @anionex/dsh-turn-rewind
dsh --profile web --dump-config | grep turn-rewind
修改 Profile Bundle 後需要重啓對應 DSH 進程。package.json 聲明的 Node 引擎是 ^22.19.0 || >=24.0.0。
默認配置可在 Profile 的 patch 層覆蓋,倉庫給出的字段如下:
- id: turn-rewind
config:
storageDir: ~/.dsh/change-ledger/v1
maxRestorePoints: 50
maxTurnCheckpointsPerSession: 30
maxFiles: 20000
maxFileBytes: 16777216
maxSnapshotBytes: 536870912
planTtlMs: 900000
staleLockMs: 30000
未配置時,storageDir 使用 $DSH_HOME/change-ledger/v1,未設置 DSH_HOME 時回退到 ~/.dsh/change-ledger/v1。倉庫要求它不得與被管理的 worktree 重疊。容量與用戶恢復點數量限制都是 fail loud:超限會直接失敗,而不是悄悄丟掉文件。自動 Turn 檢查點有獨立的每會話保留窗口,只清理自己最舊的檢查點;用戶和救援恢復點不會被靜默刪除。
典型用法¶
Web 界面回退¶
- 確認當前 Profile 已掛上
@anionex/dsh-turn-rewind,並且提供 DSH Agent 服務,這樣每輪開始前纔會留下可用的恢復點。 - 在 Web 界面找到某條用戶消息,點擊時間和「複製」後面的回退圖標。
- 彈窗會列出受影響文件,可展開「查看全部文件」。文件會按實際結果顯示爲「恢復之前的版本」「找回文件」「移除後來新增的文件」「恢復文件權限」或「恢復之前的文件類型」。
- 選擇「恢復文件並從這裏繼續」或「只恢復文件」,審閱後按下最終恢復按鈕。彈窗本身就是確認流程,不再要求重複勾選。
- 若選擇「從這裏繼續」,新 Session 創建失敗時,Change Ledger 會自動從備份把文件恢復到操作前的狀態。
Web Profile 提供同源 /turn-rewind 接口:根據所選 user/message 序號找到對應狀態,分頁返回文件預覽,生成短期、會話綁定的恢復授權,並把新對話創建交給 dsh 官方 Host 的 create / fork 生命週期。插件絕不會自動恢復文件。
給其他插件調用 Change Ledger¶
其他 Cordis 插件可以注入 changeLedger,走同一套結構化 API。倉庫示例:
export const inject = ['changeLedger']
export async function apply(ctx: Context) {
const point = await ctx.changeLedger.create({
cwd: '/absolute/git/worktree',
sessionId: 'session-id',
label: 'before refactor',
})
// point.id 是持久恢復點 ID。
}
完整格式類型從 @anionex/dsh-turn-rewind/format 導出;可信集成和測試可以從 @anionex/dsh-turn-rewind/core 使用獨立 Engine。
中途崩潰時,恢復也走同一套 API:用 listRecovery 找到操作的 rescuePointId,用 inspect 審閱該救援點,再對相關路徑走 planRestore / applyRestore。任何路徑寫入前,插件都會先創建救援點和持久 operation journal。如果 dsh 在非終態操作期間退出,下次啓動會把該操作標記爲 interrupted;另一個仍存活的 dsh 進程持有工作樹鎖時,不會誤判其操作。
適用場景與注意事項¶
適合這類情況:
- 編碼 Agent 已經改壞工作區,希望回到某條提示發出之前,而不是隻開一條新對話
- 只想把文件收回去,當前對話先留着對照
- 需要給其他插件提供可檢查、可回滾的工作區恢復點
0.1 系列只支持普通 Git worktree,範圍內包括:tracked 文件(含創建恢復點時已經缺失的 tracked 路徑)、未被 .gitignore 或 Git 標準 excludes 忽略的 untracked 文件、文本和二進制普通文件、符號鏈接,以及可執行位等可移植權限位。
明確不支持或會直接拒絕的對象包括:sparse checkout、submodule gitlink(應分別進入每個 submodule 建恢復點)、ignored 文件、socket / 設備 / FIFO 等特殊文件、擴展屬性 / ACL / 所有者 / 時間戳 / hard-link 拓撲、Git index 和倉庫元數據、非 Git 目錄。如果 ignored 或其他未受管理的文件佔據了待恢復路徑,插件會拒絕恢復,不會遞歸刪除它。
還需要注意:
- 插件以當前 dsh 進程權限運行,安裝前應檢查源碼與許可證。
- 它不沙箱化其他進程,也不能阻止外部程序同時改文件。
- 它對同一操作系統用戶不提供保密或防篡改保證;狀態文件默認僅所有者可讀寫,但宿主用戶仍被當作可信方。
- 「從這裏繼續」在創建子 Session 時有
seedLength圍欄:只有所選user/message和對應的精確turn/start都低於每一層持久圍欄時,才能複用祖先保存的狀態;子 Session 自己的狀態優先,兄弟分支絕不混用。
小結¶
智能體改文件是不可逆的操作,除非事先留下可驗證的恢復點。dsh-turn-rewind 把這件事做成 Turn 級的顯式回退:消息上掛入口,Change Ledger 管快照、審閱、救援和失敗回滾,Git 的 commit / 分支 / index 保持不動。當前版本把範圍收在普通 Git worktree,自動恢復被明確排除。
目錄頁:https://deepseek-harness-plugin.com/zh-CN/plugins/dsh-turn-rewind/
GitHub:https://github.com/Anionex/dsh-turn-rewind