dsh-session-auto-resume
A DeepSeek Harness plugin that automatically resumes agent sessions interrupted by app closure. When the DSH host relaunches, sessions that were mid-work at closure are detected from persisted state and recovered without human action:
- Sessions with an active goal are explicitly resumed and the goal re-armed — after which the bundled
goal-round-drivercontinues the round budget automatically. (The driver deliberately "never revives work on its own"; this plugin performs the explicit resume the driver requires.) - Mid-turn sessions without a goal are detected and logged with a ready-to-run resume command — surfaced, never auto-worked.
- Cleanly-ended, stale (>48 h), live, and subagent-owned sessions are skipped. Per-boot resume cap. Fail-closed on goal transitions. Never-throw activation.
Why
The desktop host has quit-time inspection of active work but no startup recovery path: after any closure (crash, SIGTERM, deliberate quit/reload), interrupted sessions stay interrupted until a human notices. This plugin closes that gap entirely outside the app bundle, so it survives app updates.
Live verification
Tested against a real closure-then-relaunch cycle (operator-approved SIGTERM of the running host; app relaunched):
- An unscripted interrupted goal session (not planted, not known to the operator) was auto-resumed and its goal re-armed at boot; the round driver then continued the session.
- A session holding an active goal continued on an automatic continuation round with no human message.
- Round budgets advanced correctly; a live session was correctly skipped; 489 subagent-owned sessions were correctly excluded in one pass.
verify-d1.mjs (included) turns any future closure into a one-command verdict: PASS (exit 0) / FAIL (exit 1, names the broken check + evidence tail) / VOID (exit 2, no closure recorded after the plugin armed). Its own fixture selftest (verify-d1.test.mjs) exercises every verdict path against synthetic evidence trees.
Design notes
- Policy is explicit and minimal: only durable goal phase
activeauto-resumes; paused/blocked goals are deliberate human states and are never re-armed; non-goal sessions are surfaced only. The plugin's installation is the one-time authorization. - Resume mirrors the harness's own resume flow (
agents.resume({resumeSessionId, agentOptions, setup})+goals.resume(agent, {id, revision})), including binding the default model — a resume without a model binding produces turns failing withprompt variable "{{model}}" has no value(the agent loop binds that variable fromagent.options). This plugin binds it and logs a loudresume-no-modelline if it cannot. - It never hand-appends to session logs. Session-log append contracts (
headerSeqmust reference an earlier request/header) make cold-log markers unsafe; detection is recorded in the plugin's own log instead. - Fail-closed everywhere: goal resume throws on exhausted budgets or invalid phase transitions (caught, logged, never retried); the boot pass, classification, logging, and resume are each guarded so a plugin failure cannot block host boot.
Install (as a local profile plugin)
Copy this package to
~/.dsh/plugins/dsh-session-auto-resume.Link it into your profile's dependencies and add it to the profile's
dsh.profile.bundleslist:"dependencies": { "@local/dsh-session-auto-resume": "link:/Users/you/.dsh/plugins/dsh-session-auto-resume" }, "dsh": { "profile": { "bundles": [ "@local/dsh-session-auto-resume" ] } }The package's own
cordis.patch.ymlcarries the required insert row (a bundles-list entry alone is silently skipped). It ships withdryRun: true— the first boot logswould-resumedecisions without acting; verify them against your live sessions, then setdryRun: false.Restart the app. Evidence lands in
~/.dsh/logs/session-auto-resume.log(ARMEDon every activation, onepass-doneper boot).
Test
node selftest.mjs # classification truth table, fake-ctx passes, mutation checks
node verify-d1.test.mjs # proves verify-d1 on every verdict path (synthetic fixtures)
Status
- Selftest: all legs green under the app runtime (
ELECTRON_RUN_AS_NODE). - Live fire: one real closure-recovery verified end to end; one defect found and fixed during it (the model binding above).
- Known limitation: sessions are matched against the most recent 30 persisted sessions by creation time (config
scanLimit), and the mechanism mounts at boot only (no hot reload of plugin-file changes).
Not affiliated with DeepSeek. MIT licensed. Filed upstream as a discussion — this repo is the reference implementation.
No comments yet. Be the first to write one.