OpenViking
volcengine
Self-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:Taler97/dsh-rollback
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
English | 中文
File-mutation rollback plugin for DeepSeek Harness: it observes every successful tool result for the pre-image a write/edit mutation reports, checkpoints that pre-image into the workspace git object database or a snapshot store, and exposes restore through a model-facing rollback_files tool and a /rollback human command. It registers no service and changes no loop code — capture rides the tools/result observation event, and restore writes files directly (never through the fs policy seam or the sandbox, because undoing a mutation must not be gated by the policy the mutation passed).
The package is an installable bundle (declares dsh.bundle), so it plugs into a profile without touching the harness. All you need is a dsh CLI; the runtime peer packages (@deepseek-ai/dsh-tools, @deepseek-ai/cordis, ...) resolve from the dsh installation itself, so nothing else is installed.
dsh CLI on the machine (dsh plugin shells out to pnpm, so pnpm must be on PATH).demo below is initialized on first use; use any name.dsh plugin --profile demo add dsh-rollback
Other sources work the same way:
# straight from git (sources are built by the prepare script; pin a commit)
dsh plugin --profile demo add github:you/dsh-rollback#<sha>
# or from a local tarball
dsh plugin --profile demo add ./dsh-rollback-0.1.0.tgz
$DSH_HOME/profiles/demo/ ($DSH_HOME defaults to ~/.dsh) now lists dsh-rollback in dependencies and in dsh.profile.bundles — the reconciler adds it automatically because the package declares dsh.bundle. The equivalent manual patch layer row:- id: rollback
name: dsh-rollback
config:
mode: auto # auto | git | snapshot
storeDir: '' # '' = <harness home>/rollback
maxRecords: 200
gitPath: git
rollback_files tool in its tool list,/rollback answers rollback: nothing to restore (instead of an unknown-command error) before any mutation happened.write a file notes.md with the content hello.write it again with goodbye — the first content is silently checkpointed.rollback_files), or type /rollback yourself.notes.md: it says hello again./rollback [count]Type it in the chat input (base web and headless profiles mount the command registry the command needs):
/rollback — undo the single most recent captured mutation in this session's working directory,/rollback 3 — undo the three most recent ones.The output lists every restored file and its action:
rollback: restored 2 file mutation(s):
restored /ws/src/lib/parse.ts
deleted /ws/src/lib/generated.ts
rollback_filesOne model-facing tool, rollback_files {count} (no prompt section). It is meant for the model to undo its own write/edit mistakes instead of asking the user. Restores are scoped to the calling session's working directory and reported as a per-file summary — restored file contents are not echoed.
Only successful write/edit tool results that carry a before pre-image. Mutations through bash, str_replace_editor, or raw subprocesses carry no pre-image and are not captured (see Known Limitations). A session can only restore records at or under its own working directory.
One write overwrite, undone. Same file, four states:
| Step | Action | notes.md |
|---|---|---|
| 1 | original state | hello |
| 2 | model writes a broken edit — pre-image captured |
goodbye |
| 3 | model calls rollback_files {"count": 1} |
(transparent) |
| 4 | restored, byte-for-byte | hello |
The full transcript of that turn:
# 1. original
$ cat /ws/notes.md
hello
# 2. the model overwrites it; tools/result carries the pre-image "hello",
# and the capture listener checkpoints it into the git object database
> tool/call write {"path": "/ws/notes.md", "content": "goodbye"}
> tool/result {"path": "/ws/notes.md", "before": "hello", ...}
# 3. the model realizes the mistake and undoes it
> tool/call rollback_files {"count": 1}
> tool/result "rollback: restored 1 file mutation(s):
restored /ws/notes.md"
# 4. back to the pre-mutation content
$ cat /ws/notes.md
hello
Under the hood: git hash-object -w wrote the pre-image into git's object database (zero index/branch/working-tree pollution), and one line was appended to the durable manifest.jsonl — the same undo works after a restart.
flowchart TD
M[模型调用 write/edit] --> R["tools/result 观察事件"]
R --> C{结果带 before 改前映像?}
C -- 否 --> X[忽略]
C -- 是 --> S[CheckpointStore 捕获]
S --> G{工作区是 git 仓库?}
G -- 是 --> B["git hash-object -w 存 blob"]
G -- 否 --> P["写 storeDir/snapshots/ 快照"]
S --> MF["追加 manifest.jsonl"]
U["模型调 rollback_files / 用户 /rollback"] --> RS["restore 按 session.cwd 作用域"]
RS --> RR["git cat-file / 快照 / 删除文件"]
The diagram above is a full worked example with a before/after transcript — see Demo.
rollback)A function/namespace plugin (name / inject / Config / apply), not a service. It is a loop-hygiene guard in the same family as dsh-tool-call-timeout-policy: it layers a safety net over the documented tools/* extension points instead of touching the agent loop.
| Key | Type | Default | Meaning |
|---|---|---|---|
mode |
'auto' \| 'git' \| 'snapshot' |
'auto' |
git checkpoints every pre-image as a git blob (requires a repository; a non-repository path fails loud and captures nothing); snapshot always copies pre-images under storeDir/snapshots/; auto picks git per file when the workspace is a repository and snapshots otherwise. |
storeDir |
string | '' |
Root holding the durable manifest.jsonl and snapshots/. Empty resolves to rollback under the Harness home. |
maxRecords |
number | 200 |
Upper bound on in-memory records per store; the oldest are dropped beyond it (the durable manifest keeps everything). |
gitPath |
string | 'git' |
Git executable name or absolute path. |
Capture. A tools/result listener converts a successful write/edit outcome into a checkpoint: the outcome's before field is the pre-mutation content (null records a file that did not exist). blob pre-images are written with git hash-object -w --stdin inside the file's repository (discovered by walking up to .git, cached per directory) — zero index/branch/working-tree pollution, content-addressed and deduplicated by git itself. Every record is appended as one JSONL line to manifest.jsonl; a store replay on plugin load restores the in-memory list, so restores survive restarts. Only absolute local display paths are captured; relative or remote display paths (non-local filesystem backends) are ignored.
Restore. restore(count, under) re-materializes the count most-recent records whose path lies at or under under (the calling agent's session working directory): blob via git cat-file blob <hash>, snapshot from storeDir/snapshots/<ref>, and absent by deleting the file. Writes are atomic (temp file + rename) and create parent directories. Restored records are removed from the in-memory list; the manifest stays append-only, so a restart replays the same records and a later restore re-applies the identical pre-image (idempotent, no double-undo).
Exposure.
rollback_files tool — model-facing restore, parameter count (integer, default 1). Registered on ctx.tools; not concurrency-safe. It refuses when the calling execution has no session working directory./rollback [count] command — the same restore for the receiving agent's session; the command child activates only when a command registry is composed (base web and headless profiles mount dsh-commands).Git blobs are the same mechanism ccAgent uses: hash-object -w writes the pre-image without touching index, refs, or the working tree; cat-file restores bytes verbatim; unreferenced blobs are reclaimed by git's own gc. Git is spawned directly via node:child_process (never through ctx.shell or ctx.subprocess): a restore is a deliberate system-level undo, so it must not be confined by the sandbox or shell policy it is undoing.
This plugin adds one model-facing tool, rollback_files (integer parameter count, string output), and no prompt section. It changes no other tool's schema or system prompt. The /rollback command is a human command plane entry; it never reaches the model.
Zero tokens on normal operation. A rollback_files call adds its small tool/result pair; the restored file content is not echoed (only a summary line). Capture itself is invisible to the model.
Append-only; the added tool schema and result follow the reusable request prefix and do not invalidate existing KV-cache entries.
write/edit mutations are checkpointed — mutations made through bash, str_replace_editor, or raw subprocesses carry no before pre-image in their result and are not captured. A plan-scoped batch backup (capture every file a plan touches before execution) is the corresponding generalization, deferred.manifest.jsonl and re-appear on replay (idempotent re-restore, never a double-undo), but a long-lived harness home grows without compaction.restoreOnFailure is deliberately not offered in v1; auto-restore would need to attribute failure to specific mutations first.pnpm install # peer packages resolve from npm releases
pnpm run build # tsdown -> lib/ (ESM + d.mts), self-contained
pnpm test # vitest, 9 store-level tests
MIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: checkpoint。