dsh-undo
User-side undo/redo plugin for DeepSeek Harness (dsh), inspired by the undo/redo experience in coding agents like opencode.
The plugin registers two slash commands (never sent to the model):
/undo — rolls the transcript back to before the last message. If the last message is an assistant reply, its whole closing reply flow (assistant fragments and tool results) is shadowed out of the visible context; if the last message is yours, that message is removed. The durable session log keeps every rolled-back event, so nothing is lost.
/redo — restores the messages the most recent /undo removed, re-adding them exactly as they were.
How it works
The session log is append-only, so /undo never deletes events. Instead it appends one empty-content assistant/message whose surface replace op shadows the targeted messages. Empty assistant content derives to no message, so the transcript and the model context show exactly the pre-undo state, while the log remains a complete, replayable, reversible history. /redo re-appends fresh copies of the shadowed events as ordinary surface appends (with re-minted message identities; tool results keep their call correlation, so transcript tool-call → tool-result adjacency is preserved).
Design notes:
- Undo unit is the last reply — one
/undo removes the closing reply flow of the last turn (or the trailing user message), returning to your last input. Repeated /undo calls walk back one reply at a time.
- Redo is invalidated by new user input — any new user-role message clears the redo stack (standard undo/redo semantics). Redo copies carry their source seqs and never invalidate it.
- Durability — every operation flushes through the shared session durability barrier (
ctx.sessions.flush) before reporting success; if persistence cannot be proven, the command reports an error rather than lying.
- Undo history is process-local — like a browser's undo stack, the redo stack is in memory. Restarting the process keeps the log consistent (markers stay shadowed, copies stay restored) but clears the redo stack.
Install
The package is a dsh bundle: package.json declares dsh.bundle pointing at cordis.patch.yml, which activates the plugin row.
From npm:
dsh plugin --profile demo add dsh-undo
From git (source checkout — the prepare script builds lib/ on install; authorize the build in the profile's pnpm-workspace.yaml first):
dsh plugin --profile demo add github:LingLambda/dsh-undo#<sha>
For local development against a source checkout of deepseek-harness, load the plugin directly as an overlay:
pnpm dsh web --patch ./cordis.patch.yml --patch /absolute/path/to/dsh-undo/overlay.yml
where overlay.yml references the source entry:
- insert:
- id: undo
name: /absolute/path/to/dsh-undo/src/index.ts
Usage
Type /undo in the composer to roll back to your last input, then /redo to restore what was undone:
/undo
/redo
Each command's result is shown in the transcript as a command card (已回滚 N 条消息 / 已恢复 N 条消息, or an error such as 没有可撤销的内容).
Develop
yarn install
yarn typecheck
yarn test
yarn build
License
MIT