Rewinds a finished turn in place — a button next to your own message withdraws that message and everything after it from the model context, puts its text back in the composer for editing, and optionally restores the files the turn changed through the DSH filesystem service.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-session-rewind
# from a prebuilt release tarball
dsh plugin --profile web add "https://github.com/2310770803/dsh-session-rewind/releases/latest/download/dsh-session-rewind.tgz"
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:2310770803/dsh-session-rewind
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
中文 | English
A DSH Web plugin that puts Rewind on every finished Turn: a per-Turn control in the Chat timeline plus one entry in the Session header, branching the conversation at that Turn and restoring the files that Turn changed.
In DSH Web, rewind to any finished Turn: every Turn in the Chat timeline has a
Rewind button, and the Session header adds one more entry point that lists every
rewindable Turn of the session. Rewinding forks a new session at that Turn and
restores the files that Turn changed.
Two entry points
| Where | What it does |
|---|---|
The ↶ in the Session header (conversation.session.header.actions) |
Lists every finished Turn of this session (Turn N + prompt preview + file count); pick with ↑↓, confirm with Enter, go back with Esc. After picking a Turn, choose "Rewind conversation only" or "Rewind and restore N files" — no need to scroll back to that Turn. |
Rewind at the end of each finished Turn (conversation.chat.turnTail) |
Rewinds that one Turn directly; the panel lets you check the files to restore and choose "Rewind conversation" or "Restore files only". |
What it does
Choosing a Turn opens a small panel with two independent halves:
- Rewind the conversation. A committed Turn cannot be edited in place: the
session log is append-only and branching is the supported operation. The
panel therefore creates a new session that ends exactly at that Turn
(
sessions.forkwith the Turn'sturn/endseq), opens it, and pre-fills its composer with the Turn's original user prompt so you can edit it and send it again. The original session is left untouched. - Restore the files that Turn changed. While a Turn runs, every file the model creates or rewrites is captured before the mutation and stored as a content-addressed blob. The panel lists those files; the checked ones are put back to their pre-Turn content (a file the Turn created is removed).
The two halves can be used together (Rewind conversation) or separately
(Restore files only).
Why a fork instead of an in-place rewind
The community plugin SiriLee/dsh-rewind
offers an in-place rewind (inside the same session it moves the
model-visible surface back to an earlier user message by appending a
surfaceOp: replace marker). We borrowed its entry points and interaction
(a ↶ next to each message/Turn, a mode picker, a file impact list,
↑↓/Enter/Esc, a header candidate list) but deliberately did not adopt its
in-place rollback, because:
- An in-place rewind needs more than an appended marker: the front end must also
hide the rolled-back rows, and DSH exposes no extension point for that. The
reference plugin does it by wrapping the private
uiConversation.viewsregistry, and its own source comments say "The seam is private … not an extension point" — it breaks with version upgrades. sessions.forkis the branching capability DSH officially gives the UI — the built-in "branch in new chat" button uses it — so it is stable across upgrades and naturally satisfies "context truncated + editable + resendable".
If you would rather have an in-place rewind (same session, no new session), you
can install dsh-rewind-plugin separately; the two do not conflict.
Install
Install from the plugin market (recommended): Plugins → Discover →
Index sources → add
https://raw.githubusercontent.com/2310770803/dsh-session-rewind/main/index.json
→ search rewind or Session Rewind → install. The index format and the release
process are documented in docs/publishing.md.
This machine (profile desktop) already has that index source written into
cordis.patch.yml, so opening Discover shows the entry.
Install from the source directory (what this machine currently does):
// plugin_manager
{ action: 'install_bundle', target: 'E:\\\\DeepSeek Harness\\\\dsh-session-rewind' }
How it is put together
| half | mechanism |
|---|---|
| conversation | Client sessions.fork({ sessionId, atSeq }) + uiWorkspace.openSession, the same idiom as the built-in branch button; the draft is pre-seeded through the Conversation store's per-session localStorage key so the composer already holds the original prompt |
| files (capture) | fs/write-intent / fs/edit-intent waterfalls ({ prepend: true }), with the tool execution mapped back to its Session through a tools/pre-execute WeakMap — the same coupling workspace-changes uses |
| files (restore) | text pre-images through the fs service (path resolution, locks, version guard); binary pre-images and deletions through Node, on the archive's own recorded absolute path |
Where data lives
$DSH_HOME/plugin-data/dsh-session-rewind/
index.json per-session, per-Turn file records
blobs/<sha256>.bin the pre-Turn bytes
Nothing leaves the machine: the four Host routes are loopback-only,
same-origin /api routes with DSH Web's own Host/Origin trust fence.
GET /api/session-rewind.selfcheck reports what the running Host has actually
wired up (archive totals, whether the Web half is in the boot graph, the route
list). It is read-only and never exposes session content.
Limits
- Only mutations that go through the DSH filesystem service inside a Turn are
captured. Files changed by a shell command, by
git, or outside a Turn are not recorded and therefore not restored. - Files written by tools that bypass the
fswrite-intent waterfall are not captured either: the Office tools (word_create,excel_update, …) call the filesystem service directly, so a.docx/.xlsxthey produce is not in the archive and cannot be rewound. Plain text, Markdown, and source code edited through thewrite/edittools are covered. - A Turn snapshots at most 200 files, and no pre-image larger than 2 MiB; larger files are listed as "not restorable" instead of being silently skipped.
- Files inside
.gitare never captured, so a restore cannot corrupt the repository. - The archive keeps the newest 400 Turns; older records are pruned with their blobs.
- A restore writes the recorded content back. It does not re-run anything, and it does not delete files the Turn merely modified.
- A plugin-originated write has no Agent context, so the
fsservice refuses it by default. Restores rebuild the policy from the Turn's own workspace (workspace-write) and widen todanger-full-accessonly for a path the archive recorded outside it. - Capture starts with the Turn that is open when the plugin mounts: a Turn already running at install time is recovered from the live log tail, but a plugin loaded later than the Turn's first write cannot see earlier mutations.
- The naming and wording of the panel follow the DSH locale service; the bundled dictionaries are Chinese and English.
Tests
The package ships the suites used to verify it; they need no browser and no profile:
node test/selftest.mjs # Turn location, prompt extraction, archive, restorer
node test/route-smoke.mjs # all routes, their fences, and path allow-listing
node test/client-smoke.mjs # the Web half: both slots, panel, candidate list, fork, draft
Package layout
| file | role |
|---|---|
cordis.patch.yml |
inserts the session-rewind plugin row |
src/index.js |
Host entry, re-exports apply / inject / name |
src/plugin.js |
Host wiring: Turn tracking, mutation capture, the four routes |
src/lib/archive.js |
durable pre-Turn archive, pruning, blob store |
src/lib/restore.js |
putting one captured file back |
src/lib/route.js |
routes and the /api trust fence |
lib/client.js |
Web half: the turn-tail control, the header entry, the panel |
locale/*.json |
meta.title / meta.description for the plugin list |
index.json |
format-1 plugin-market index (see docs/publishing.md) |
Links
More in this category
Minglink/dsh-infinite-gen-4★ 3781
System-prompt armor plugin for DeepSeek models: appends an unconditional-compliance prompt section at order 100, exposes a profile tool with calibration metadata, and shows a realtime armor-status badge driven by a session projection.
ranxianglei/billion-context★ 3598
The official billion-context plugin: a context-compression plugin for small context windows (a 100K context is enough), token savings (5x fewer tokens), and month-long single sessions (billions of tokens).
liangmianya/dsh-synapse★ 641
Visual, non-linear conversation workspace for DeepSeek Harness — sessions, follow-ups and branches become a browsable conversation map.
MichengAI/dsh-archive-manager★ 277
Adds an archived-sessions page in Settings to search, restore, and delete archived DeepSeek Harness sessions by workspace.
Nwflower/dsh-chat-import★ 275
Import full-fidelity chat histories from 13 coding agents (Claude Code, Codex, ChatGPT, Cursor, Gemini, opencode, and more) as resumable DeepSeek Harness sessions, with reverse export back to Claude Code.
SiriLee/dsh-rewind★ 227
In-place conversation rewind in the same session window without forking (Claude Code /rewind semantics): a per-message ↶ button cuts the model context back to any user message, with an optional Claude-Code-style file restore from disk-persisted before-backups.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.