Skip to content
This repository was archived by the owner on Oct 22, 2026. It is now read-only.

Releases

0.3.0 — final version; discontinued

Choose a tag to compare

@Carrick-K7 Carrick-K7 released this 22 Oct 15:06

Retirement / 停止维护

Decision date / 决定日期:2026-10-22

Decision

dsh-keep-going is discontinued. Version 0.3.0 is the final version.

There will be no further feature development, compatibility work or security updates. The source and existing tags are retained as historical reference. The existing 0.3.0 tag is not moved, and no new package version is needed for this documentation-only decision. Its GitHub Release records this retirement; it is not a recommendation to install or upgrade.

This is a scope and maintenance-cost decision, not a claim that restart recovery has no value or that every implementation is unsafe. In the maintainer's deployment, automatic recovery does not justify the additional lifecycle coupling, recovery state and ongoing maintenance. Manual continuation after a restart is an accepted tradeoff. No replacement recovery subsystem is planned.

Removal

  1. Check for a pending restart or recovery operation before removal. Finish or explicitly cancel it; do not interrupt unrelated tasks or use force just to uninstall.
  2. Remove the keep-going override from the selected profile's cordis.patch.yml, and remove dsh-keep-going from dsh.profile.bundles in that profile's package.json. Remove any separate overlay that inserts this plugin. Do not remove native session, goal or goal-driver entries, or change their existing enablement.
  3. Uninstall the package from that same profile, for example dsh plugin --profile web remove dsh-keep-going. Check the resulting manifest, lockfile and composed profile; CLI behavior can differ by DSH version.
  4. Verify the running loader has disposed the plugin. If it has not, arrange a normal service-manager restart at a safe time. Do not rely on the removed plugin to restore interrupted work, and do not launch a replacement server.
  5. Only after disposal, delete the plugin's dedicated recovery directory (by default $DSH_HOME/dsh-keep-going; check stateDirectory if customized). Verify the exact path first. Do not delete DSH sessions, goals, attachments, shared storage or unrelated configuration.
  6. Confirm the plugin's tools and commands are absent and the existing DSH endpoint remains healthy. Old conversation transcripts may still mention the plugin; they are history, not active configuration.

Uninstalling removes the plugin's restart/drain commands, automatic post-restart recovery and recovery-status tools. It does not uninstall the service manager or native goal driver, nor does it enable a goal driver that was already disabled. Unfinished work may need an explicit human continuation after a restart.

中文说明

本项目停止演进,0.3.0 为最终版本。 不再提供新功能、兼容性适配或安全更新。保留 Git 历史和既有标签用于查阅;不移动 0.3.0 标签,不为停止维护说明额外发一个软件包版本。对应 GitHub Release 仅记录项目终止,不建议新安装或升级。

这是极简化与维护成本的取舍,不是断言恢复能力毫无价值,也不是宣称存在已证实的普遍安全缺陷。在维护者的部署中,自动恢复的收益不足以支持继续维护额外的生命周期耦合与恢复状态。接受重启后手动继续,不另造提醒器、批量继续或替代恢复系统。

移除时先确认没有待执行重启或恢复,再清掉指定 profile 中的插件配置、bundle 声明和依赖;确认插件已从运行时卸载,必要时在安全窗口正常重启。之后才删除核实过路径的插件专属恢复目录。保留原生会话、目标历史及其现有启停设置,不强制中断其它任务,不清理共享存储或备份。

0.2.6 — provably silent outside restart recovery

Choose a tag to compare

@Carrick-K7 Carrick-K7 released this 19 Sep 01:40

0.2.6

While DSH runs normally, this plugin provably does nothing to your conversations.

Why this release exists

A review asked whether non-restart activity could still reach a conversation. Auditing every path that can touch one — restore, queue/steer, resumeGoal, flush, runMaintenance — showed that all of them funnel through recover(), and that two of them were still reachable for a record written during normal operation:

  • a record for a queued human message could reach the native queue and steer() a nudge into a live conversation whose message was already sitting in its inbox;
  • a record for an active goal could re-arm execution that DSH itself had disarmed.

Neither needs a restart to happen, so both were interference.

What changed

Nothing is recorded outside a restart window. Event capture now runs only while a requested restart is draining. Outside that window the durable session log is the only source recovery needs — a later boot re-derives whatever a crash left unfinished — so the plugin writes no records and inspects no conversation on account of ordinary activity.

Non-restart records can never reach a conversation. A record written while nothing was restarting (restartScoped === false) is dropped before any restore, queue or goal call: it is redundant with the durable log, and the boot scan re-derives the real work if the process dies. The gate sits above every send-capable path.

The boot scan still claims them. A provisional record no longer suppresses the restart scan that would turn it into real recovery work (ownsContinuation and the checkpoint's turn check now ignore provisional records), so crash recovery is unchanged: discover claims the work, then it is continued exactly once.

Older records keep their meaning. Records written by earlier versions carry no scope flag; they are still treated as restart work, so a pending question from an earlier restart keeps waiting for your answer instead of being discarded by the upgrade.

Verified

New regressions:

  • steady state touches no conversation at all — real Cordis and a real agent kernel: after ordinary turns in two conversations and 40 poll cycles, no model turn is started by the plugin, no plugin-source message exists in either transcript, goal activation is unchanged, nothing is held, no exit is requested.
  • recovery work for one conversation never spills into another — a conversation waiting for a human answer keeps its recovery in waiting-user while an unrelated conversation runs its own turn; neither receives a plugin message and no extra model request happens.
  • a record made during normal operation never nudges a queued human message / never continues an open turn / never re-arms an active goal — each asserts no queue, no steer, no goal resume, and that the redundant record is dropped.
  • a crash upgrade claims a provisional record before it can be delivered — proves the crash path still recovers exactly once.

158/158 tests pass, including the two-process shipped-CLI acceptance (SIGKILL → cold restore through the real Web SessionController), the real workspace registry, and the real ask_user_question pipeline.

pnpm install --frozen-lockfile --ignore-scripts && pnpm check && pnpm test

Install

dsh plugin --profile web add git+https://github.com/Carrick-K7/dsh-keep-going.git#0.2.6

Requires a supervisor (e.g. systemd Restart=always). DSH 0.1.5-rc.2 tested. GitHub-only; not published to npm.

中文摘要

  • 正常运行期间不写任何记录:事件采集只在"已请求的重启正在排水"时进行。窗口之外,持久会话日志本身就是恢复的唯一来源(崩溃遗留的工作在下一次启动时重新推导),因此插件不会因为普通活动而写入记录或检查任何对话。
  • 非重启记录绝不可能触达对话:restartScoped === false 的记录在任何 restore / queue / 目标调用之前就被丢弃——它与持久日志重复,真出事的活由启动扫描重新推导。这个判断位于所有可能"发东西"的路径之上。
  • 启动扫描仍然认领它们:临时记录不再挡住把它们变成真正恢复工作的重启扫描,崩溃恢复行为不变(先认领,再恰好继续一次)。
  • 旧版记录保持原语义:早期版本写的记录没有 scope 标记,仍按重启工作对待——上一次重启遗留的"等待回答"会继续等你回答,而不是被升级逻辑丢掉。
  • 新增回归:真实 Cordis + 真实内核的"正常运行不触碰任何对话"(40 轮 poll 无额外模型请求、无插件消息、目标状态不变、无锁、不退出)、"一个对话的恢复不溢出到另一个对话"、以及"临时记录不会 nudge 排队消息 / 不会继续未结束的轮次 / 不会重新武装目标"、崩溃升级仍恰好恢复一次。
  • 158/158 测试通过。

0.2.5 — a scheduled restart never freezes DSH

Choose a tag to compare

@Carrick-K7 Carrick-K7 released this 13 Sep 00:34

0.2.5

A scheduled restart no longer freezes DSH. Until the exit actually happens, every conversation keeps working.

The production failure this fixes

On 2026-09-13 the live instance (PID 239199, 0.2.3, up since 07:46) stalled for the whole deployment. One queued restart request was enough:

  • control.request() immediately took a native maintenance lock on every idle agent through agent.runMaintenance(), and check() renewed those locks every 500 ms;
  • a locked agent still reports status: 'idle' in DSH, so every conversation looked free while new messages were only latched into inbox.nextTurn — no model request was ever made;
  • with force: false, reaching the deadline did not fail or release anything: it silently extended itself (token.deadline = now() + drainTimeoutMs). The journal shows "restart still waiting for 3 task(s)" at 08:05:53 and "2 task(s)" at 08:15:54, with the process unchanged the whole time;
  • cancelling was only possible from the conversation that requested the restart, so the deployment stayed frozen as long as another session had work;
  • the same day saw 435 repeated continuation deliveries, because a delivered attempt that kept failing was re-sent instead of being settled.

What changed

Pending is not exiting. A request now only records durable state while it waits: no maintenance locks, no goal disarming, no frozen conversations. A message that arrives while a restart is pending starts its turn immediately.

Locks exist only for the handover. Once nothing is running, the plugin saves durable state first, then takes the locks, verifies once more that no turn started in that window, and asks DSH to exit. If work resumed, the locks are released and the request keeps waiting. A restart that waits for one long task can no longer stall anything else.

Only force: true cuts running work. At the deadline without force, the request keeps waiting for a quiet moment and says so in the log. With force, work still running at the deadline is cut only after the synchronous snapshot has recorded it, and the exit still uses the supervisor restart code.

Cancelling leaves nothing behind. Because a waiting request takes no locks and changes no goal activation, cancelling simply discards the record — there is no state to restore.

No repeated deliveries. A delivered continuation is settled by its verdict: completed retires the record, an unretryable ending marks it blocked. A receipt that belongs to an older turn than the session's newest work is retired instead of being replayed, and every automatic path (delivery exceptions, provider errors, further restart interruptions) is bounded by maxAttempts.

Verified

  • New regressions: a message arriving while a restart is pending starts a turn at once and is not parked in inbox.nextTurn; a waiting request holds no lock and does not touch goal activation; cancelling leaves no lock or goal-state change; a running turn elsewhere is never cut without force; force at the deadline exits with code 75 only after the cut work is saved; an admitted receipt for an older turn is never replayed.
  • 152/152 tests pass, including the two-process shipped-CLI acceptance (SIGKILL → cold restore through the real Web SessionController), the real workspace registry, and the real ask_user_question pipeline.
pnpm install --frozen-lockfile --ignore-scripts && pnpm check && pnpm test

Install

dsh plugin --profile web add git+https://github.com/Carrick-K7/dsh-keep-going.git#0.2.5

Requires a supervisor (e.g. systemd Restart=always). DSH 0.1.5-rc.2 tested. GitHub-only; not published to npm.

中文摘要

  • 待重启不等于正在退出:请求等待期间只保存持久状态——不上维护锁、不解除目标武装、不冻结任何对话;等待期间到达的消息立即开轮。
  • 锁只用于交接那一刻:确认没有任务在跑后,先保存持久状态,再上锁、复核、请 DSH 退出;若期间又有任务开始,则释放锁继续等待。一个等待别的长任务的请求不会再拖住整个部署。
  • 只有 force: true 才允许在期限到达时切断仍在运行的工作;不带 force 时到期只记录日志并继续等安静时机。切除前先做同步快照,退出码仍是服务管理器的重启码。
  • 取消不留残留:等待期间没有任何锁和目标状态改动,取消即丢弃记录。
  • 不再重复投递:投递过的继续任务按判决结案(完成即归档、不可重试即 blocked);属于更早轮次的回执直接收回,不再按定时器重放;所有自动路径都受 maxAttempts 约束。
  • 生产背景:0.2.3 实例(PID 239199)因一个排队中的重启请求把全站冻结——被锁的 agent 在 DSH 里仍显示 idle,消息只进 inbox.nextTurn;force:false 到期只是静默续期(08:05:53“3 task(s)”、08:15:54“2 task(s)”),当天还有 435 次重复投递。
  • 152/152 测试通过,新增“等待期间消息立即开轮、等待不持锁不改目标状态、取消无残留、force 与退出码语义、旧回执不重放”等回归。

0.2.4 — recovery only answers a restart

Choose a tag to compare

@Carrick-K7 Carrick-K7 released this 13 Sep 00:02

0.2.4

Recovery now only ever answers a restart — a conversation you stopped, or one that failed while DSH was running, is left alone.

Why this release exists

0.2.3 could pick up work that had nothing to do with a restart. A conversation stopped by hand, or a turn that failed on provider quota, could be re-sent by the plugin on a timer — in the worst case for a provider account that had no quota left at all. That is not recovery, it is noise addressed to nobody.

What changed

Only a restart interruption is recoverable. A turn ending interrupted (the process died mid-turn) — or a disposal this plugin recorded itself as part of its own exit — is restart work. A turn that ended completed, blocked, max-tokens, aborted by the user/hook/parent, disposed by an ordinary lifecycle, or failed with a provider error is not.

Work recorded during normal operation is only a checkpoint. The plugin still records unfinished turns while DSH runs, so a crash during one can be found; but once that turn ends without a restart, the record is retired. Nothing is sent, retried or resumed behind your back. An exit (a requested restart, a drain) upgrades the checkpoint to restart work.

A stop you performed is final. A session stopped by hand ends as aborted/disposed without the plugin's own exit record, so it is reported as Stopped outside a restart and never re-sent. Same for user cancellations.

Quota, credit and hard request errors stop immediately. Classification now reads the provider's message, not just the error code: usage limit, 余额不足, 额度, 配额, 402, and other quota wording are recognized, as are credential/adapter errors and 4xx statuses (except 408/429). These end the task in blocked with the provider's own message instead of a retry loop.

A failing continuation is reported, not replayed. Once a continuation has been delivered and its turn ends, its verdict is settled: completed retires the record, anything unretryable marks it blocked. Only a further restart interruption may continue it, and one task gets at most maxAttempts (default 3) automatic attempts before it is marked blocked and left for you. Transient delivery failures (queue/flush errors) still back off and retry under the same cap.

New setting

maxAttempts: 3   # automatic attempts per task, 1..10

Evidence from this deployment

  • session-af99297f: 16 attempts / 18 recorded failures against a provider with no registered adapter — that loop is what credentials classification plus the attempt cap now stop at once.
  • session-c3a2fe13: 5 attempts / 6 failures from a 403 ... 5-hour usage limit response — now blocked with the provider's message on the first failure.

Tests

148/148 pass, including the two-process shipped-CLI acceptance (SIGKILL → cold restore through the real Web SessionController), the real workspace registry, the real ask_user_question pipeline, and new regressions for: a manual stop never being recovered, quota wording in English and Chinese, steady-state failures never becoming recovery work, bounded re-delivery across repeated restarts, and caller-request settlement (which previously re-delivered a finished request forever).

pnpm install --frozen-lockfile --ignore-scripts && pnpm check && pnpm test

Install

dsh plugin --profile web add git+https://github.com/Carrick-K7/dsh-keep-going.git#0.2.4

Requires a supervisor (e.g. systemd Restart=always). DSH 0.1.5-rc.2 tested. GitHub-only; not published to npm.

中文摘要

  • 只恢复被重启打断的工作:interrupted(进程死在任务中途),或本插件自己记录的退出造成的 disposal。已完成、blocked、max-tokens、用户/钩子/父级取消、普通生命周期 disposal、提供方报错,一律不算重启遗留。
  • 正常运行期间的记录只是检查点:崩溃时仍能被发现;但该轮一旦以“非重启原因”结束,检查点就被收回,不会向你背后发送、重试或恢复任何东西。重启/排水开始时,检查点升级为重启工作。
  • 手动停止是最终决定:显示为 Stopped outside a restart,绝不重发;用户取消同理。
  • 额度/余额/硬性 4xx 立刻停止:识别 usage limit、余额不足、额度、配额、402 等中英文措辞与凭据/adapter 错误,直接标记受阻并保留提供方原始报错。
  • 失败的继续任务只报告,不重放:已完成则结案;不可重试则 blocked;只有“又一次重启打断”允许接着做,且同一任务最多自动尝试 maxAttempts(默认 3)次。投递异常仍按退避重试,同样受上限约束。
  • 生产证据:session-af99297f(16 次尝试/18 次失败,provider 无 adapter)与 session-c3a2fe13(403 5-hour usage limit,5 次尝试/6 次失败)这两类循环在 0.2.4 下第一次失败即停止。
  • 148/148 测试通过,新增手动停止不恢复、中英文额度识别、正常运行失败不成任务、多次重启有上限、request 结算(此前会无限重发)等回归。

0.2.3 — archived stays archived; removable stopped goals

Choose a tag to compare

@Carrick-K7 Carrick-K7 released this 12 Sep 23:47

0.2.3

Archived conversations stay on the shelf; stopped goals can be removed when you decide.

New in this line (0.2.2 + 0.2.3)

Archived conversations are never touched. DSH's native archive set (ctx.workspaceRegistry.archivedSessionIds) is consulted at discovery, revalidation and listing. A conversation you deliberately archived keeps its unfinished work archived — no restore, no continuation, no goal re-arm, whatever happened before the restart. Recovery records for sessions that later become archived are pruned.

keep_going_clear_goal removes a goal you no longer want, through the shipped SessionController and ctx.goals.clear:

  • a goal that is stopped (paused, blocked, complete) is cleared directly;
  • a goal that is active but not executing is paused and then cleared, so both decisions stay auditable in the session history;
  • a goal that is actively executing is refused — pause it first.

The conversation history and the goal's tombstone are kept; only the recovery record is purged.

No redelivery noise. A recovery message that is already durably queued is no longer re-sent and re-logged on every tick while the session is busy with other work.

Verified in production on this deployment

  • A goal whose model provider had no registered adapter (no adapter registered for provider "token-rhythm") had been burning rounds and goal revisions in a re-arm loop under 0.2.0. After 0.2.1 the loop stopped completely; under 0.2.3 it was removed on request, leaving pause → clear in the durable log.
  • Boot recovery of the same restart restored every eligible session exactly once; each interrupted conversation received at most one continuation, and the session that had an active goal was continued through the goal driver rather than a generic prompt.

Tests

135/135 pass, including a two-process shipped-CLI acceptance (SIGKILL → cold restore through the real Web SessionController, correct per-session provider/model/history), the real workspace registry for archive coverage, the real ask_user_question pipeline, and regressions for re-arm loops, archived skip, clear-goal boundaries and pending-message quiet delivery.

pnpm install --frozen-lockfile --ignore-scripts && pnpm check && pnpm test

Install

dsh plugin --profile web add git+https://github.com/Carrick-K7/dsh-keep-going.git#0.2.3

Requires a supervisor (e.g. systemd Restart=always). DSH 0.1.5-rc.2 tested. GitHub-only; not published to npm.

中文摘要

  • 归档对话绝不被触碰:恢复在发现、复核、列举三个阶段都读取 DSH 原生归档集合,归档的对话即使有未完成工作也保持搁置。
  • 新增 keep_going_clear_goal:移除不再需要的目标。已停止的可直接移除;active 但未在执行的目标先记一次暂停再移除(两步可审计);正在执行的目标会先被拒绝。历史与 tombstone 保留,只清恢复记录。
  • 不再重复投递:已经进入队列的恢复消息不会每 500ms 重发、重记日志。
  • 生产实测:空转的 token-rhythm 目标在 0.2.1 停止循环、0.2.3 按指令移除(日志留下 pause → clear);同一次重启中所有符合条件的会话各恢复一次,active 目标走原生 goal 续轮。
  • 135/135 测试通过,含真实 CLI 双进程 SIGKILL 冷恢复验收。

0.2.1 — stop re-arm loops

Choose a tag to compare

@Carrick-K7 Carrick-K7 released this 12 Sep 16:25

0.2.1

Stop re-arm loops. 0.2.0 restored goals correctly, but a goal whose model provider had no registered adapter showed the failure mode in production: every driver round errored, the native driver disarmed it, and the plugin re-armed it again — about every 40 seconds, burning goal revisions. That is exactly the periodic session restarting this plugin must not do.

Fixed in 0.2.1

  • One re-arm per restart, per goal. A goal driver error that disarms a goal again is DSH's own lifecycle, not a reason to re-arm periodically. The next process restart is the only thing that can recover it again.
  • Missing/unregistered providers are non-retryable. NO_ADAPTER-class errors are reported as blocked with the real reason (e.g. no adapter registered for provider "token-rhythm") instead of being re-armed into guaranteed failure.

Verified live after deploy: the looping goal's re-arm count dropped from ~every 40s to zero, and is now cleanly marked blocked: Goal round limit reached; all other active goals were restored exactly once. 130/130 tests pass, including the new regression that a native disarm after a successful re-arm produces no further re-arm in the same process.

0.2.0 (this line) — restore original work after a restart, then stay out of the way

  • Boot-time one-shot recovery restores unfinished original conversations through the real SessionController, with each session's saved provider, model and history intact. No browser needed. After recovery settles, the plugin is fully dormant — no periodic scanning and no automatic restarting of conversations in steady state. A user opening an old conversation is a normal action, not a restart.
  • Active goals interrupted by a restart are re-armed once and handed to DSH's own goal driver; work belonging to an active goal is handed to the goal round and never receives a generic "continue".
  • A task waiting for the user's answer keeps waiting: the original question is preserved verbatim, never re-dispatched and never answered by a synthetic tool closer, a goal round, or a generated continue. It resumes only when a real same-session human reply arrives. Old approvals are never replayed as allowed-once.
  • Paused, completed and explicitly cancelled work is never revived; fork copies gain no automatic authority over the parent's work; pending human input keeps its original order.
  • Durable recovery store (atomic fsync, consume-on-confirm) survives repeated crashes and temporary delivery failures, with exponential backoff and stable message identities.
  • Restart control: one pending restart at a time; newly arriving input is held durably instead of cut; force:false never treats silence as death; restart uses exit code 75 for the supervisor; stopping is honestly left to the service manager.

Verification

130/130 tests pass, including a two-process acceptance that boots the shipped CLI twice, SIGKILLs the first, and checks cold restoration through the shipped Web SessionController — correct providers, models, histories, one armed goal round, isolated instructions, no browser client.

pnpm install --frozen-lockfile --ignore-scripts && pnpm check && pnpm test

Install

dsh plugin --profile web add git+https://github.com/Carrick-K7/dsh-keep-going.git#0.2.1

Requires a supervisor (e.g. systemd Restart=always) to start DSH again after exit. DSH 0.1.5-rc.2 tested. GitHub-only distribution; not published to npm.

中文摘要

0.2.1 修掉"反复重新武装"循环:目标驱动器报错导致解除武装时,插件不再周期性重新武装——每个目标每次重启只恢复一次;缺失/未注册的模型供应商归类为不可重试并如实标记受阻。生产实测:循环目标的 re-arm 从约 40 秒一次降为 0,并被干净地标记为"目标轮数用尽"。

0.2.x 系列:重启后只做一次恢复,随后完全休眠——不周期扫描、不自动重启对话;被打断的 active 目标交给原生 goal 驱动器续轮而不是收到通用"继续";正在等用户回答的任务保留原问题、绝不代答;暂停/完成/取消的任务绝不复活;持久恢复存储可跨多次崩溃;130/130 测试通过(含真实 CLI 双进程 SIGKILL 验收)。

0.1.2 — an active goal keeps running

Choose a tag to compare

@Carrick-K7 Carrick-K7 released this 12 Sep 03:10

0.1.2

An active goal keeps running — across anything.

This release writes down the rule this plugin exists to serve, and pins it with tests. No behaviour changed: 0.1.2 states the promise, so it stops being something a future change could quietly take away.

The rule

A goal that is still active keeps running. A restart does not stop it, a closed window does not stop it, an idle afternoon does not stop it. Every start re-arms it — whether the restart was asked for by a conversation, typed by you, or done by the service manager, and whether or not anything was woken afterwards.

Only three things stop a goal, and this plugin never does any of them:

Stop By
you pause it you, or the assistant on your instruction
it is finished the assistant marking it complete
it cannot go on DSH itself: the round limit is reached, or a round cannot even be queued (no credit, no credentials, …)

A goal stopped that way stays stopped: "I paused this on purpose" always wins over resuming. If the reason was temporary, such as credit, ask for the goal to be resumed once that is sorted out.

Goals and conversations are independent

They recover separately, on purpose:

  • goals keep running across a restart even when no conversation was woken;
  • a conversation can be woken without any goal being involved;
  • making goal resumption depend on the wake list was considered and rejected — a long-running goal must not depend on whether someone happened to be watching.

What changed in this release

  • The Goals section of both READMEs now states the rule, the three stop conditions and who owns each of them, and the independence of goals and conversations.
  • Two regression tests pin it: an active goal is re-armed by every start, whatever its source, with no wake note present; paused, blocked and complete goals are never brought back.
  • 28 tests in total.

Install

dsh plugin --profile web add git+https://github.com/Carrick-K7/dsh-keep-going.git

Then restart DSH once so the plugin is loaded.

中文摘要

目标只要还是 active,就一直在跑。 重启、关掉窗口、闲置都不该让它停;每次启动都会重新武装它,不管是哪个对话要求的重启、你手打的,还是服务管理器做的。只有三件事会让它停——你暂停它、助手标记完成、或 DSH 自己判定无法继续(回合数用尽、某轮排不上队,例如没额度或凭据无效);这样停下的目标一律保持停止,插件不会擅自恢复。目标与会话是各自独立恢复的:没有会话被唤醒时目标照跑,反之亦然。本版没有改动行为,只是把这条规则写进文档并用测试钉死。

0.1.1 — restart DSH, then carry on

Choose a tag to compare

@Carrick-K7 Carrick-K7 released this 12 Sep 01:26

0.1.1

Restart DSH, then carry on. The answer being written is finished before DSH closes, DSH closes in an orderly way for the service manager to replace, and the conversations the restart interrupted pick their work back up — including long-running goals.

What changed in 0.1.1

Long-running goals come back by themselves. DSH switches a session's goal off every time the session starts, so after a restart a goal that had been working would sit idle until someone asked again. A goal that is still active when its session returns is now re-armed, and the work continues on its own. Goals that were paused, blocked or completed on purpose are left untouched, and a goal that cannot be resumed (its rounds are used up, for example) is logged and left as it was.

Every interrupted conversation is woken, each with the right message. The conversation that asked for the restart receives its own instruction; every other conversation whose answer was cut off receives a neutral "carry on" notice that names no task, so it decides for itself what it was doing. Conversations that were already idle are not woken.

What it does

Tools the assistant can use restart_harness, shutdown_harness, cancel_harness_action
Commands you can type /restart, /shutdown
Settings continuePrompt, drainTimeoutMs, stuckAgentMs, stormLimit, stormWindowMs

A restart waits for the answers in progress, with a limit: when drainTimeoutMs passes it closes anyway, and the note that wakes the conversation is already written. A conversation marked as running that has shown no activity for stuckAgentMs stops holding up the restart. More than stormLimit restarts inside stormWindowMs stops the waking, not the restarting.

Not in scope

Starting DSH again after it closes is the service manager's job (systemd Restart=always, a Windows service, …). Undoing a plugin installation that stops DSH from starting is your job: DSH reports the error and exits. There is no settings page and no browser button.

Install

dsh plugin --profile web add git+https://github.com/Carrick-K7/dsh-keep-going.git

Then restart DSH once so the plugin is loaded.

Verification

Tested against DSH 0.1.5-rc.2, on a live deployment:

  • two real restarts through systemd, with the conversations woken afterwards;
  • a hand-written wake note, to prove the wake path independently of the exit path;
  • the goal behaviour above, first with a throwaway probe plugin and then with this plugin's own code in production;
  • 26 unit and smoke tests (node --test "test/*.test.js"), including a full arm → wake-note → next-start cycle.

Three defects were found by running it for real and are fixed in this line: a missing service declaration that crashed DSH at startup, a wake message that leaked from one conversation into others, and the over-correction that briefly stopped waking interrupted conversations at all.

中文摘要

重启 DSH 时不丢正在进行的活儿:先把正在写的回答写完,再让 DSH 按正常流程收尾退出,由服务管理器拉起新进程;被这次重启打断的会话会被唤醒接着干(发起者收到自己的提示,其它会话收到中立的"请继续"),重启前处于 active 的长任务目标会自动恢复。0.1.1 新增的正是最后这一条。针对 DSH 0.1.5-rc.2 实测:两次真实重启、一次手工唤醒验证、26 个测试全部通过。