當對話因紅字「本輪運行失敗」(turn error) 而終止時,這個外掛會自動替那個會話送出「繼續」,在無人值守下努力完成目標。
- 一個 turn 以
error(紅字「本輪運行失敗」)或以max-tokens(橘字「已达到输出 token 上限/回答被截断」,回答被截斷)結束時,開始/延續一段「失敗連續」。 - 排一個倒數;倒數歸零時送出設定文字(預設「繼續」)一次,然後停止計時——改為等待大模型的下一輪結果。
- 排隊提示詞保留+插隊後歸位:失敗發生時已經排在 inbox 裡的提示詞(在失敗的 turn 之前就輸入、排在它後面的工作)會先被保留(從 agent 的 inbox 暫時移出),不會在「繼續」之前搶先開跑。「繼續」一插隊送出(steering 進 next-step),這些提示詞就立刻按原本順序(FIFO)放回 next-turn 排隊——driver 領取時一定先取完 next-step 的「繼續」再取 next-turn,所以「繼續」保證先執行、排隊任務緊接在後依序恢復,不會消失。保留時先掃 next-step 再掃 next-turn,使歸位順序與 driver 的領取優先序一致。取消、使用者接管、關閉外掛等其他停止方式也會把保留的提示詞放回,不會遺失;唯有 archive 會連同排隊訊息一起取消。
- 使用者即時接管:循環進行中(倒數或已送出「繼續」,含已暫停)新輸入的訊息一律視為明確接管——立即停止循環、放行所有保留中的提示詞,你剛輸入的訊息馬上被執行(絕不會被默默吞掉:對話記錄只有在被 driver 領取後才會出現,所以保留機制絕不攔截新輸入的使用者訊息)。
- 若下一輪又失敗,連續失敗數 +1,按列表遞增重試間隔(
5 → 10 → 15 → 30 → 60 → 60 → 60 → 60,之後固定 60 分鐘),重新排一個倒數;倒數期間排隊任務會再次被保留,等到下一次「繼續」插隊送出時又立即歸位(每個重試循環都如此)。 - 中間若有成功(turn 以
completed結束),整段失敗連續重置回最開始(下次失敗從 5 分鐘重新起算)。
所以它不是「無腦無限重試」,而是:失敗→送一次→等下一輪;連續失敗才逐步拉長間隔;一旦成功就歸零。
- 觸發:簡中/繁中/英文介面的紅字都一樣,因為判斷依據是 Host 的
turn/end事件與agent/error事件(以 turn 編號去重),而非前端字串。 - 停止:你手動送出任何訊息(即時接管)、重試成功(重置)、turn 被中止(
aborted,例如你按下停止鍵)、按取消/✕、會話被 archive、超過上限時數(預設 24h,0=無上限)、或外掛被關閉。 - 受阻重排(
blocked):若「繼續」在 pre-step 階段被其他外掛拒絕(例如子代理數量上限、RPM 限制的 reject 模式),該次輸入會被丟棄、turn 以blocked結束——此時視同失敗重新倒數(照遞增間隔再送一次),不會讓循環卡死在等待中。 - 上限停止記錄:超過上限停止時,會在該會話的對話記錄中寫入一則通知訊息(⛔ 自動繼續已停止…),讓使用者能看到無人值守循環為何完全停止。
- 429 限流冷卻:若最後一次失敗是 API 429(Rate Limit,
code === RATE_LIMIT或 HTTP 429),不停止,改為固定間隔(cooldownMinutes,預設 720 分鐘,可在 UI 設定)送出「繼續」以冷卻;連續 429 維持此間隔,回復正常後自動切回原遞增間隔。進入冷卻時在會話寫入通知(含本次 429 發生時間與預計下次送出時間)。冷卻期間排隊提示詞同樣被保留。 - 空轉防護:保留提示詞期間,driver 偶爾會關閉一個沒有內容的 completed 邊界 turn(排程收斂的副產物)。這種 turn 不算成功——不會重置連續失敗、也不會提前放行保留中的提示詞;只有真正有輸入/輸出的完成才會放行。
- 持久化:設定是一般的 DSH volatile Config(見下方「設定如何儲存」),重啟
dsh web後仍保留。 - 保留提示詞的持久性限制:保留中的提示詞放在外掛的記憶體佇列裡,但保留時間很短——只在「失敗倒數中」到「『繼續』插隊送出」之間;送出後立即歸位,等待中的每一輪也都會在送「繼續」時歸位。正常停止(成功、取消、接管、關閉外掛、
dsh web正常重啟)都會即時放回 inbox,不會遺失;僅程序異常終止(crash/強制 kill)且恰好在倒數期間,該輪保留佇列才無法回放。 - 多語 UI:繁中/簡中/英文(透過
localeservice)。 - Session format v4 訊息:外掛送出的「繼續」與所有通知訊息都帶有 v4 要求的 producer-owned source(
source: { kind: 'plugin:dsh-plugin-fallback-continue' };通知另帶form: 'notice'與 ≤120 字的summary)。DSH 0.2 的 v4 codec 會拒絕舊的 v3 包裝{ kind: 'plugin', plugin }(讀寫皆然),一則這樣的訊息會讓整個 session log 無法持久化;test/controller.test.mjs釘住這個形狀。
| 項目 | 說明 | 預設 |
|---|---|---|
| 啟用 | 總開關 | 關 |
| 自動送出的文字 | 每次送給大模型的文字 | 繼續 |
| 重試間隔(分鐘) | 逗號分隔,用完重複最後一個 | 5,10,15,30,60,60,60,60 |
| 超過上限自動停止 | 啟用 24h 這類上限 | 開 |
| 上限時數 | 從最近一次失敗連續起算,0=無上限 |
24 |
| 429 冷卻間隔(分鐘) | 遇到 429 後改用的固定重試間隔 | 720 |
- 右下角浮動條(只看當前會話):倒數中顯示
⏳ 4:59 後自動繼續「繼續」(#2),每秒更新;點文字=暫停/繼續,點 ✕=取消。送出後顯示已送出「繼續」,等待結果(不再計時)。 - 設定頁「失敗自動繼續」:總開關、自動文字、間隔、上限設定,以及完整等待清單(每條的倒數/狀態、第幾次、失敗原因、保留中的排隊訊息數、暫停/立即重試/取消)。倒數中按鈕顯示「暫停」,點擊後狀態列顯示「已暫停」、按鈕變為「繼續」。卡片底部顯示版本號。
- 工作區會話清單:進入等待的會話,其列尾會顯示橘色小圓徽章(倒數中
⏳、已送出✓),懸停可看「正等待自動送出『繼續』(第幾次)」,不必切換進該會話就能看到它仍在無人值守運作。
本外掛是標準 DSH web 外掛(npm package):
# 安裝到本機的 web profile(會進 node_modules)
dsh plugin --profile web add github:akwangho/dsh-fallback-continue然後在你的 $DSH_HOME/profiles/web/cordis.patch.yml 加上:
- insert:
- id: fallback-continue
name: 'dsh-plugin-fallback-continue'最後重啟 dsh web 生效。(若只是本機目錄測試,也可直接複製 lib/、locale/ 與 package.json 到 node_modules/dsh-plugin-fallback-continue/——少了 locale/ 的話,「設定 → 外掛」清單會抓不到名稱與說明。)
設定不再由外掛自建 namespace 存放,而是走 DSH 標準的 plugin Config 機制:
lib/config.js宣告Config,六個欄位全部.volatile()。volatile 有兩個關鍵效果:- DSH 的
settingsservice 只把 volatile 欄位投影成可編輯表單,所以設定頁才存得下。 - Loader 會把變更寫進執行中的 reference(
loader/volatile-update)而不重新掛載外掛——所以你在設定頁改開關或間隔時,正在倒數的計時與保留中的提示詞都不會被清掉。
- DSH 的
- 儲存位置就是 profile 的
cordis.patch.yml(DSH 自己寫回),不是外掛的檔案。 - Host 半部只讀取 config,並在
loader/volatile-update或settings/document-updated時重新讀取並套用到進行中的循環。瀏覽器半部透過ctx.configForms.get('fallback-continue')讀寫,跟第一方外掛(如dsh-client-ui-theme)用的是同一套路徑。 - 外掛呼叫
settings.configure({ auto: false }, ctx.fiber),告訴 DSH「這支外掛自帶設定頁」,避免 DSH 再自動產生一份重複表單。 - namespace 必須等於 profile entry id,也就是
cordis.patch.yml裡的id: fallback-continue。若你改了 id,設定頁會顯示紅色診斷而不是安靜地存不進去。 - 排程相關的設定(重試間隔、冷卻間隔、上限)一改,已經在倒數的計時會立即改用新值重新計算,不會繼續倒舊的數字。
- 啟用外掛不會回溯補送已經發生的失敗(那個 turn 早就結束了);要在下一次失敗才開始循環。
package.json— npm package(含dsh.clientmetadata)與唯一版本號來源。locale/en.json、locale/zh.json— 外掛在「設定 → 外掛」清單中的顯示名稱與說明。lib/index.js— Host 半部(匯出Config、TypertfallbackContinueRemote 服務與插件裝配)。lib/config.js— Config schema(volatile 欄位)與 settings namespace,無 Host 相依、可直接單元測試。lib/controller.js— 狀態機本體:失敗偵測、遞增間隔、排隊提示詞保留/放行、優先插隊送「繼續」、停止條件。lib/pure.js— 純函式(intervalFor/normalizeConfig/DEFAULTS/boundaryHasContent 等),無相依、可直接單元測試。lib/client.js— Client 半部(右下角倒數、設定卡、等待清單、自我診斷)。test/host.test.mjs、test/controller.test.mjs、test/manifest.test.mjs、test/client.test.mjs— 純函式、狀態機、Config schema、manifest 相容性與 Client 半部(浮動視窗)的單元測試(npm test)。
DSH 在掛載 profile 外掛前會先做相容性預檢:讀取外掛 package.json 的 peerDependencies(不執行任何外掛程式碼),把名稱為 @deepseek-ai/dsh 或以 @deepseek-ai/dsh- 開頭的 peer 逐一與目前 DSH 版本比對。只要有一個不符合,整個外掛就會被停用,stderr 印一行然後外掛在 UI 上完全消失:
dsh: disabling profile plugin row "fallback-continue": Plugin
dsh-plugin-fallback-continue@1.9.4 is incompatible with dsh 0.2.0-rc.2: peerDependencies {...}
這正是 1.9.x 在 DSH 從 0.1.x 升到 0.2.x 之後「看不見」的原因:peer 被寫成 ^0.1.0-rc.6,而 0.x 的 caret 只允許同一個 minor。
因此本外掛的 @deepseek-ai/dsh-* peer 一律使用涵蓋整條 0.x 的明確範圍(>=0.1.0-rc.6 <1.0.0),不再使用 caret/tilde。test/manifest.test.mjs 會直接對 package.json 守住這條規則,確保下次 DSH 升級不會又無聲消失。
- 已驗證可載入的 DSH 版本:
0.1.0-rc.6、0.1.0-rc.8、0.2.0-rc.2、0.2.0、0.3.0-rc.1、0.9.9。 - DSH
1.x刻意不相容:那時需要重新稽核 API,再依 audit 結果放寬。 @deepseek-ai/dsh-client-runtime在 0.2 已移除,已從dsh.client.inject移除。- Client 的 sessions store 在 0.2 移除了
SessionListState.current:目前開啟的會話改由byId[*].retainedBy.mainView > 0判定(與內建 AppFrame/DocumentTitle 相同掃描)。浮動視窗先前讀s.current,因此在 0.2 找不到當前會話而完全不顯示——只剩設定頁能暫停/取消。test/client.test.mjs用 0.2 的 snapshot 形狀守住這條路徑。 - 這些 peer 在執行時都由 DSH host 提供,因此標記為 optional peer,
npm install不會把它們抓進本機node_modules。 - 想略過預檢(不建議):
dsh plugin allow-version為dsh-plugin-fallback-continue@<版本>開豁免。
外掛若載入失敗,不會再無聲消失:
- 設定頁頂端/底部會列出紅色診斷(缺少
connection/slots、RPC 連不到 host、slot 註冊失敗等)。 - 連設定頁都註冊不起來時,畫面左下角會出現一個固定的小紅框寫著第一條問題。
- 各個 UI 註冊彼此獨立:其中一項失敗不會連帶讓其他項消失。
- 重啟 DSH 程序後,等待中的倒數會重來(狀態只在記憶體,不跨程序留存)。
- 「繼續」以 steering(插隊)方式送出:對閒置中的 agent 會直接開始新 turn。配合排隊提示詞保留,送出當下 inbox 已清空,所以「繼續」一定是 driver 下一個領取的輸入,用來先完成尚未完成的任務。
- 有些失敗是確定性的(例如模型不支援那麼大的 token 數),重試永遠不會成功——這種請手動處理(換模型或調參數),不要等自動繼續。
- 更新程式後需重啟
dsh web才會生效。