dshbase

插件目录 / Developer / dsh-safe-restart

dsh-safe-restart

已验证 · 实测可装 McKayGONG

✓ 持续维护 基于 4 个官方 DSH 包

查看 GitHub ↗ ← 返回插件目录

0Stars
0Forks
0未关闭 issue
—语言
2026-08-22最近推送
跨平台平台

功能简介

Developer 类别的 DeepSeek Harness 插件。

✅
我们的评价
可用 — 实测通过,早期项目

Developer 类别的 DeepSeek Harness 插件。 实测能干净安装、正常启动。早期项目,但功能可用。

「已验证」表示我们的自动化 CI 在干净 profile 里实际执行了 dsh plugin add 并启动成功——仅此而已。功能描述与版本兼容性均为作者声明。这不是安全审计,也不代表对第三方代码的背书。

README

dsh-safe-restart

A macOS-only DSH host plugin that safely restarts the DSH process from inside
an Agent session. It terminates only the exact DSH listener process through a
detached helper, lets the browser reconnect through DSH's native
ConnectionController (no manual refresh), resumes the original persisted
session, and starts exactly one automatic continuation turn to verify the
restart and continue the interrupted task.

The plugin registers a single Agent tool, restart_dsh. There is no Web
button and no settings UI.

Design: docs/plans/2026-08-22-dsh-safe-restart-design.md

What it does

  • One Agent tool: restart_dsh — schedules a safe restart of the current
    DSH host, bound to the invoking Agent's current session.
  • macOS only — every other platform is rejected before any side effect.
  • Verified on DSH 0.1.0-rc.8 — the recovery coordinator relies on host
    internals that are still moving in the rc cycle (agent/created,
    ctx.agents.resume setup, installModelSelection). End-to-end restart +
    auto-continuation was verified on 0.1.0-rc.8 (standalone profile, macOS).
    The declared range is >=0.1.0-rc.8 <0.2.0; anything outside it is untested
    and may need adapter work.
    After any DSH upgrade, re-verify: pnpm test, then one real
    restart_dsh round — confirm PID change, HTTP 200, journal consumed, and a
    continuation turn whose turn/end reason is completed (not error).
  • Detached helper — the journal write is durable first, then a helper
    process is spawned detached and unref'd; it survives host termination,
    replaces the DSH process, and records the outcome.
  • Native browser reconnect — after the host comes back, the existing Web
    client reconnects through DSH's built-in connection/resync loop. No manual
    page refresh and no "continue" message are needed.
  • One automatic continuation turn — the same persisted session resumes
    with its preset and model selection, and exactly one plugin-sourced
    continuation message is delivered per restart.
  • Interrupted streams are not resumed — the exact token stream that was
    running when the host dropped is not continued. DSH closes that turn as
    interrupted; recovery starts a new turn in the same session.

Tool schema

restart_dsh({
  "continue_after_restart": true,   // boolean, default true
  "continuation": "optional instruction" // string, optional, max 2000 chars
})
Field Type Default Meaning
continue_after_restart boolean true Whether to deliver one automatic continuation turn after recovery. Set false for a pure restart with no continuation.
continuation string (optional) — Task-specific instruction for the single continuation turn (max 2000 chars). When omitted, the default instruction is: "Verify that DSH recovered successfully, then continue the pre-restart task using the current session and durable workspace state."

The target session is always the invoking Agent's current session; a caller
cannot name another session. The tool returns:

{ "restartId": "<uuid>", "scheduled": true, "continueAfterRestart": true }

Installation

Install from the plugin repository root:

dsh plugin --profile standalone add /path/to/dsh-safe-restart

The plugin activates on the next DSH start — restart_dsh is not loaded
until then. Use an already-validated external restart path (e.g. a codex /
safe-restart script that stops and relaunches DSH and verifies HTTP 200) for
that one activation restart, then verify http://127.0.0.1:<port> returns
HTTP 200 before continuing.

Usage

Ask the Agent to call restart_dsh. With the default
continue_after_restart: true, what the user sees is:

  1. The host connection drops briefly (the tool response may be cut short).
  2. The browser reconnects automatically through DSH's native
    ConnectionController — no manual refresh.
  3. The original persisted session reappears with its preset and model.
  4. Exactly one new assistant turn appears, carrying the restart ID marker
    [restartId: <uuid>], verifying recovery and continuing the task.

A restart with "continue_after_restart": false performs the same safe
restart and browser recovery but adds no continuation turn.

Safety

  • Never killall — the helper terminates only the recorded PID, and only
    after verifying its exact command line against the launch identity recorded
    at schedule time (the current Node executable plus the DSH entry argument).
    An unrelated or unknown process is never touched.
  • One active restart — a second restart_dsh while a record is in flight
    is rejected (restart already active).
  • 60-second cooldown — a restart within 60 s of the most recent one is
    rejected (restart in cooldown).
  • 30-second health timeout — after spawning the replacement, the helper
    polls the health endpoint for at most 30 s (PID exit and port release use a
    12 s budget).
  • No retry loop — each restartId is executed at most once; a record that
    is not scheduled is refused, and a failed restart is never retried.
  • Exactly-once delivery — before delivering the continuation, the
    coordinator scans the session's durable log for the restart ID marker; a
    previous delivery that crashed before the consumed transition is detected
    and consumed without re-delivering. An ambiguous window becomes
    delivery-uncertain instead of a blind retry.
  • No secrets persisted — the journal and log carry no credentials and no
    environment values.
  • Interrupted token stream is not resumed — the continuation is a fresh
    turn in the same session, never a replay of the interrupted generation.

Storage

All durable state lives under ~/.dsh/storages/dsh-safe-restart/:

Path Purpose
active.json The single in-flight restart record (atomic writes: temp file → fsync → rename).
.lock Exclusive restart lock, created with open(..., 'wx'); one active restart at a time.
history/<restartId>.json Terminal records, archived when a record reaches a terminal phase.
restart.log Timestamped, non-secret lifecycle lines: restartId, old/new PID, port-release result, HTTP result, terminal phase.

Phases

Phase Meaning
scheduled The tool request was persisted; the detached helper has not claimed it yet.
restarting The helper validated and terminated the old PID, spawned the replacement, and is waiting for health.
ready The new host answered HTTP 200; newPid and readyAt are set. Waiting for the resumed session's Agent.
delivering The continuation message was created and handed to the Agent; awaiting durable confirmation.
consumed Terminal: exactly one continuation delivered (or, for continueAfterRestart=false, none) and archived.
failed Terminal: the restart failed with a bounded, non-secret error message; never retried.
delivery-uncertain Terminal: the continuation window was ambiguous (e.g. the host died between delivering and confirmation). Not blindly redelivered.

Troubleshooting

  • failed record — check restart.log and the record's error field
    (e.g. PID validation failure, port not released, health timeout). The record
    is already archived; no action is required for future restarts.

  • delivery-uncertain record — the continuation may or may not have been
    delivered. Check the session's durable log for the marker
    [restartId: <uuid>]:

    • marker present → the message was delivered; the record is archived, done.
    • marker absent → it was not delivered and will not be redelivered; decide
      whether to re-run the task manually.
  • Stale lockless scheduled record — if the helper crashed before it
    could claim the record (or the claim failed), a scheduled record with no
    helper running and no lock can be left behind. Recovery polls it for a
    bounded window and then deliberately leaves it for inspection. It blocks new
    restarts (restart already active).

  • Ready but no Agent (blocks subsequent restarts) — if a record reaches
    ready but the matching session's Agent never reappears (for example the
    session was deleted or the browser tab was never reopened), active.json
    stays in ready and every later restart_dsh is rejected until the
    record is resolved.

    Remedy (manual, after inspection):

    1. Inspect ~/.dsh/storages/dsh-safe-restart/active.json — note its
      restartId and phase — and the latest lines of
      ~/.dsh/storages/dsh-safe-restart/restart.log to see what happened.
    2. If the continuation marker [restartId: <restartId>] already exists in
      the session's durable log, the continuation was delivered; archive the
      record as consumed: move active.json to
      history/<restartId>.json with "phase": "consumed" and remove
      active.json.
    3. If the continuation was never delivered and never will be, archive the
      record with a terminal phase instead ("failed" or
      "delivery-uncertain"), then remove active.json.
    4. Check ~/.dsh/storages/dsh-safe-restart/.lock — it records the owning
      restartId. Remove the lock only when its restartId matches the
      archived record; never delete a lock owned by a different restart.
    5. Leave history/<restartId>.json and restart.log in place — they are
      the audit trail. The next restart_dsh is then allowed again.

Verification script

scripts/verify-real-restart.mjs is an external, read-only evidence collector
that runs outside the DSH process (plain Node ESM, zero dependencies). It
never performs a restart and never touches the live listener. It reads the
journal and probes the HTTP endpoint, then reports one JSON object:

{
  "oldPid": 111,            // replaced DSH PID, from the selected record
  "newPid": 222,            // healthy replacement PID (null when unknown)
  "pidChanged": true,       // newPid is set and differs from oldPid
  "http200": true,          // live GET http://127.0.0.1:<port>/ returned 200
  "phase": "consumed",      // lifecycle phase of the selected record
  "continuationCount": 1    // consumed records that delivered a continuation
}
node scripts/verify-real-restart.mjs --check-latest        # latest history record, default port 3081
node scripts/verify-real-restart.mjs --journal /path/to/journal --port 3000
node scripts/verify-real-restart.mjs --journal /tmp/fixture --port 0 --dry-run   # detection only, no HTTP

--dry-run prints the detection logic (listener PID via lsof -ti tcp:<port>
and a journal contents summary) without probing HTTP, so it can be exercised
without a live restart. It exits nonzero on malformed journal JSON, an
unusable listener detection, or unexpected errors. continuationCount counts
consumed records whose continueAfterRestart is true — per the design each
such record delivered exactly one continuation turn; pure-restart records
(false) delivered none and are not counted.

Uninstall

Remove the plugin with the remove counterpart of the install command, e.g.:

dsh plugin --profile standalone remove dsh-safe-restart

then restart DSH once so the tool is dropped. If you also want to remove the
durable state, delete ~/.dsh/storages/dsh-safe-restart/ after confirming no
restart is in flight (no active.json).

Recovery limitation

If the browser tab was closed during the restart, simply reopen it: DSH's
native reconnect restores the same persisted session (same preset, model,
and history). The continuation is delivered exactly once regardless of how
many times the tab reconnects.

安装

🧩 让 Agent 自动装(推荐)

装一次目录插件,之后本站所有插件都能让 DeepSeek Harness 自动找、自动装:

dsh plugin add dshbase-catalog

然后对 agent 说「帮我装 dsh-safe-restart」,它会在目录里找到并自动安装。文档:dshbase-catalog · 已验证场景包。

该插件是 GitHub 源码(未发 npm)——直接从仓库装:

Web profile:

dsh plugin --profile web add github:McKayGONG/dsh-safe-restart

Headless(CLI)profile:

dsh plugin --profile headless add github:McKayGONG/dsh-safe-restart

实测报告

验证通过:从 GitHub 源码完成 L1 安装 + L2 加载 + L3 运行(dsh 0.1.0-rc.6)。

安全:尚未扫描——我们的每日静态扫描将很快覆盖它。

分享徽章

Developer 里更多

浏览全部 7819 个插件 →