pinion05/dsh-neverdie:進程被殺後自動救回 DSH 會話

前言

用 DSH 跑隔夜構建、多小時 agent 任務或者 goal 驅動的輪次時,最常見的崩潰方式不是模型出錯,而是宿主進程沒了:被 SIGKILL、OOM、超時,或者某次重啓。進程一死,它帶着的所有會話一起停在原地,會話頂部頂着一行 “Task stopped — The DSH process exited before this finished…”,之後就一直掛着,等有人重新發一條消息才繼續。日誌、工作區、模型上下文其實都是完好的,工作只是被中斷,不是丟失。

已有的做法是早上起來手動點一下 Send。pinion05/dsh-neverdie 把這一步交給插件:DSH 重啓之後,掃描一遍會話,找出哪些是進程死掉時正好在跑 turn 的,自動重新提示一句,讓 agent 接着往下幹。

這是什麼

pinion05/dsh-neverdie 是一個 DSH host plugin,屬於「工作流」分類,由 pinion05 維護,許可證 MIT。它解決問題的場景很窄:DSH 進程中途退出,留下一批掛在 “Task stopped” 狀態的會話,插件在 host 重啓後把它們喚醒,並把因重啓而失效的 goal 重新掛上,讓自主輪次繼續跑。

包名是 @npmc_5/dsh-neverdie,倉庫 pinion05/dsh-neverdie,版本 0.3.0,倉庫公開可用,星標 0。它不是一個通用的調度器,也不是重啓 DSH 的工具,只在 host 已經重新起來了之後做一次掃描。

檢測邏輯

進程退出有兩種方式,會話日誌的收尾形態不同,插件據此區分「該不該醒」。

硬崩潰(SIGKILL、OOM)時日誌停在一個還沒閉合的 turn 裏,重啓恢復會給這段冷日誌合成一個 reason 爲 interrupted 的 turn/end 收尾。優雅退出或重啓時,瀕死的進程自己把正在跑的 turn 關掉,reason 是 aborted,cause 是 disposed,被中止的工具調用在對話末尾留下一行 “Error: tool call aborted”。

插件從日誌尾部往前走,找到最後一個 turn/end,只有上面兩種纔算「屍體」:

  1. 沒有更新的 turn、消息,也沒有待處理的 inbox 輸入;
  2. abort 的 cause 不是 user、parent 或 hook——也就是說被用戶主動停掉的會話不會被喚醒;
  3. 尾部如果是重啓後追加的生成標記(例如 session/end-seed),會被跳過;冷日誌則由 sessionQuery.readSession 在內存裏合成 interrupted 收尾,因此無論會話事先有沒有被重新打開,判定都是同一套。

還有一個容易漏的情況:重連的 web 客戶端會把會話日誌恢復出來,但並不恢復它的工作。這種「活着但空轉」的狀態同樣按屍體處理。

判定通過後,插件對候選做 resolveAgent(喚醒冷會話),再重新掛 goal,最後把 revive 消息排隊並寫入持久化存儲。每次掃描都會在 host journal 裏打一條 [neverdie] 行,每條 revive 消息帶 neverdie 這個 source kind,在 transcript 裏可見,也可以從會話日誌裏數出來。

Goal 重新掛載

goal 的激活狀態是進程級的,所以殺掉 turn 的那次重啓同時也讓 session 的 goal 失效了。插件走支持的 goals.resume 路徑重新掛載,前提是 goal 處於 active 且 activation 爲 disarmed。paused、blocked、complete 或者輪次已經用完的 goal 原樣不動;輪次耗盡的 goal 會在日誌裏說明爲什麼不動。

配置

配置項一共六個,默認值如下:

配置項 類型 默認值 說明
enabled boolean true 總開關
delaySeconds number 10 host 啓動後等多久再掃描,取值 0–600
maxAgeMinutes number 60 只救中斷時間在這個窗口內的會話,取值 1–1440
maxRevives number 5 上一次真實用戶消息之後,同一會話連續被 neverdie 喚醒的次數上限,取值 0–10
reviveMessage string 見下 喚醒時注入的消息
resumeGoals boolean true 喚醒期間是否重新掛載 active 但 disarmed 的 goal

默認的 reviveMessage:

[neverdie] The DSH process exited before the previous turn could finish, so that turn was closed. Continue the interrupted task from where it stopped. Tool calls that were cut off have unknown outcomes — inspect their effects before retrying them.

這段消息本身是必要的:harness 不接受純空白的 prompt,所以喚醒必須發一條真實的短消息。消息裏提醒 agent 被斬斷的工具調用結果未知,先檢查現場再重試。

maxRevives 是防止崩潰循環的閘門。host 如果一直死,插件每次啓動都會救,最多連着救 maxRevives 次;只要來了真正用戶消息,預算就清零重算,不會讓崩潰一直燒 token。

安裝與啓用

README 與 package.json 都沒給出安裝命令。倉庫通過 dsh.bundle.patch 指向 ./cordis.patch.yml 聲明瞭一個 Cordis 補丁,同包還導出 index.js、locale/*.json 和 icon.svg,並且聲明瞭對 @deepseek-ai/cordis、@deepseek-ai/schemastery、@deepseek-ai/dsh-llm 的 peer 依賴。

因此這裏不寫安裝命令。按名字拼出來的 dsh plugin add 形式並不來自資料原文,請以你本地 DSH 的插件管理方式爲準,從 npm 包 @npmc_5/dsh-neverdie 或倉庫 pinion05/dsh-neverdie 引入,裝之前先確認 peer dependency 與你現有版本匹配。

典型用法

準備上生產時,先把宿主進程交給監管:

  1. 用 systemd 這類 supervisor 包住 DSH,配置 Restart=on-failure。插件自己不能重啓 host,它只在 host 回來之後做事,「誰來重啓」必須外部給。
  2. 增加 delaySeconds,給啓動和冷日誌恢復留足時間,避免掃描早於恢復邏輯。
  3. 如果預期 host 可能長時間離線,按離線時長調高 maxAgeMinutes,默認 60 分鐘之外的會話不會被救。
  4. 看日誌確認:journal 裏出現 [neverdie] revived session ... 和 [neverdie] re-armed goal ...,說明對應會話被喚醒、對應 goal 被重新掛上。
  5. 出問題時按 transcript 裏的 neverdie 標記逐條覈對,確認某次喚醒屬於預期行爲。

適用場景與注意

適合這類人:DSH 跑長任務、跑 goal 驅動輪次,且 host 由 systemd 之類的外部 supervisor 拉起的。它把「早上起來點 Send」這一步消掉,讓被中斷的任務在重啓後自己接上。

幾條硬約束:

  • 只處理 root conversation。subagent 子會話交給父會話處理,父會話自己的喚醒會把它們對齊。
  • 超過 maxAgeMinutes 的會話保持死亡狀態,不要把窗口拉得太大,否則會把早就該人工接手的任務自動續上。
  • 插件以當前 dsh 進程的權限運行,裝之前讀一遍 index.js 與 cordis.patch.yml,確認補丁行爲與許可證(MIT)都符合你的要求。
  • 生態背景:DSH 的思路是「一切皆插件」,社區目錄站點是獨立站點,與 DeepSeek / 幻方沒有官方從屬關係,安裝前自行覈實來源。

結尾

這個插件沒有擴大 DSH 的能力範圍,它只是把一個本來需要人盯着的環節修好了:進程死了,重啓之後,被中斷的會話自己站起來接着跑,goal 也接着輪次。如果你已經在用 supervisor 拉 DSH,而你的長任務會在夜裏被重啓打斷,值得試一下。

目錄頁:https://www.skillhub.cn/plugins/pinion05/dsh-neverdie
倉庫:https://github.com/pinion05/dsh-neverdie

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜