dsh-todo-continuity
让没做完的任务清单跨回合保留。DSH 在每个回合开始时清空 todos 投影,指望模型重写整份清单,于是回合一旦被中断,计划虽然durable 地留在会话日志里,界面上却永久消失——而 todo_write 只写不读,用户和模型都拿不回来。本插件只注册一个普通的 session/event 监听器,按会话记住最后一份清单;当 turn/start 到来而那份清单仍有未完成项时,把它补折回去,投影就不再显示空计划。不 fork tool-todo、不注册任何 waterfall:插件换不掉投影(注册表对 stateVersion 不一致直接抛错),追加折叠所反应的那条事件才是够得着的缝。刻意收得很窄——只管未完成的清单、每次回合开始最多补一条、失败即放行。已用真实会话上的受控 A/B 验证:两次运行内容相同,唯一差别是插件装没装,都用真实的投影定义折叠。零依赖。
安装
dsh plugin --profile web add github:apex-mochen/dsh-todo-continuity
需要可复现安装时,可在仓库后追加 #commit 固定提交。
让没做完的任务清单跨回合保留。DSH 在每个回合开始时清空 todos 投影,指望模型重写整份清单,于是回合一旦被中断,计划虽然durable 地留在会话日志里,界面上却永久消失——而 todo_write 只写不读,用户和模型都拿不回来。本插件只注册一个普通的 session/event 监听器,按会话记住最后一份清单;当 turn/start 到来而那份清单仍有未完成项时,把它补折回去,投影就不再显示空计划。不 fork tool-todo、不注册任何 waterfall:插件换不掉投影(注册表对 stateVersion 不一致直接抛错),追加折叠所反应的那条事件才是够得着的缝。刻意收得很窄——只管未完成的清单、每次回合开始最多补一条、失败即放行。已用真实会话上的受控 A/B 验证:两次运行内容相同,唯一差别是插件装没装,都用真实的投影定义折叠。零依赖。
该插件未提供要点说明,请参考仓库 README。
- 安装并启动 DeepSeek Harness:
npx @deepseek-ai/dsh web - 在终端执行上面的安装命令(CLI 会解析插件并核验来源)
- 用 dsh plugins list 确认已安装,必要时重启 Harness 生效
插件以当前 dsh 进程的权限运行,安装时可能执行代码。请先通读仓库源码与许可证,确认无破坏性命令与越权访问;本站只做索引,不对第三方插件安全性作担保。
| 代码仓库 | github.com/apex-mochen/dsh-todo-continuity |
| 许可证 | MIT |
| 主要语言 | main |
| 下载量 | 2 |
| GitHub 星标 | 0 |
| 最近推送 | 2026-09-15 |
| 收录日期 | 2026-09-19 |
| 分类 | 会话与消息 |
事实信息来自公开插件目录快照(2026-10-01),介绍文案由本站再加工。
以下为插件仓库 README 全文(原始内容,由公开目录抓取整理)。
# dsh-todo-continuity
**Keeps an unfinished task list visible across turns.**
DSH clears the `todos` projection at the start of every turn, expecting the model to rewrite the whole
list. When a turn is interrupted, the list is durably in the session log but gone from the UI — and
`todo_write` is write-only, so neither you nor the model can read it back. This plugin re-appends the
last list when a turn starts and that list still has unfinished work.
## The behaviour it addresses
`packages/todo/tool-todo/src/index.ts:130-145` — the fold, and its own comment describing it as
intentional:
```ts
// Standing-plan fold: latest whole todo/write list, cleared by the next
// turn/start (turn/end keeps the finished checklist visible); null before the
// first write or after a later turn begins; every other event returns the
// same state reference.
apply: (state, event) => {
if (event.type === 'todo/write') return event.data.todos
if (event.type === 'turn/start') return null
return state
},
stateVersion: 2,
```
Rewriting each turn is a deliberate design — the tool description tells the model to send the entire
list every call. The problem is the interrupted case: the next `turn/start` clears the list anyway, and
`todo_write` (`:203-222`) only appends snapshots and returns counts, so there is no way to read the
previous list back. The plan is not lost — it is in the log — but it is unreachable from the UI, and
the model has no way to recover it either.
This is **community addition #21** to the verified unfixed-issue list in DSH discussion
[#6520](https://github.com/deepseek-ai/deepseek-harness/discussions/6520).
## What the plugin does
Registers one ordinary `session/event` listener. It remembers the last `todo/write` list per session,
and when a `turn/start` arrives whose remembered list still has unfinished items, it appends that list
back as a fresh `todo/write`. A projection is a pure fold over the durable log, so appending the event
the fold reacts to restores the state — nothing else needs to change.
Deliberately narrow:
- **Only unfinished lists.** A list with everything `completed` is left to the built-in behaviour, so
the plugin changes nothing about the case it does not exist for.
- **Once per turn start.** At most one extra event per `turn/start`.
- **No fork.** It does not disable or replace `tool-todo`. It cannot: the projection registry
refcounts a repeated key only at an equal `stateVersion` and throws on a mismatch
(`session-projection/src/index.ts:276-283`), so the first definition always wins. Appending is the
reachable seam.
- **No waterfall.** It registers no `pre-execute` / `pre-step` / `llm/stream` hook, so it has no
`next()` obligation and cannot swallow another plugin's behaviour.
- **Fails open.** If the append cannot be made, the caller keeps exactly the built-in behaviour.
## Verified
A controlled A/B on real sessions — two identical headless runs differing only in whether this plugin
was installed, both folded through the **real** `todos` projection definition. Raw output in
[EVIDENCE.md](./EVIDENCE.md):
| | real events | folded final state |
|---|---|---|
| without the plugin | `turn/start#4 → todo/write#24 → turn/end#47 → turn/start#49 → turn/end#51` | `null` — list gone |
| with the plugin | `… → turn/start#49 → **todo/write#51** → turn/end#52` | the 3-item list |
The extra event has no `tool/call` or `tool/result` behind it (unlike the model's own `todo_write`),
and the control run has no such event at all — which is what establishes that the plugin caused it.
## Install
```bash
dsh plugin --profile web add github:apex-mochen/dsh-todo-continuity
```
Restart the profile afterwards.
## Configuration
```yaml
- id: dsh-todo-continuity
config:
verbose: false # log each carry-over
enabled: true # set false to keep it installed but inert
```
## Two implementation notes worth knowing
**The append is deferred by a microtask, and that is required, not stylistic.** `Session.append`
publishes its event to listeners while its own acceptance boundary is still open, and rejects a
reentrant append (`core/session/src/index.ts:729-731`, *"session append cannot reenter while another
append is being published"*; the flag is set at `:742`, listeners run at `:752`, cleared in `finally` at
`:757`). A `session/event` listener runs inside that window, so an inline append throws every time — and
because this plugin fails open, it would have silently done nothing. An earlier version made exactly
that mistake; source reading caught it before any run.
**Listener order works out.** The projection registry has already folded `turn/start` to `null` by the
time this plugin's listener runs: `on` pushes unless `prepend` is set (`vendor/cordis/src/events.ts:255`),
`dispatch` preserves that order (`:172-174`), `emit` invokes them synchronously and forward (`:194-196`),
and the registry registers in its own constructor while the base bundle loads.
## Compatibility
- DSH `0.1.x` (peer: `@deepseek-ai/cordis ^4.0.1`)
- Node.js 20+
- One implementation file, zero dependencies, no process/filesystem/network access
## This overrides a designed behaviour
Per-turn clearing is documented as intentional, so this plugin is a deliberate product-semantics
change, not a bug fix — scoped as narrowly as it can be. The alternative fix is upstream: clear only
when the list has nothing left to do. DSH does not accept external pull requests today
(`CONTRIBUTING.md`), so a plugin is the reachable seam.
## License
MIT
数据来源:公开的 DeepSeek Harness 插件目录与各插件 GitHub 仓库。本站为独立第三方目录,与 DeepSeek、幻方(High-Flyer)及插件作者均无隶属或背书关系。