Skip to content

Releases: extracurricular-ai/dsh-filesnap

v0.2.1

Choose a tag to compare

@S2thend S2thend released this 27 Aug 21:53

v0.2.1

Changed

The snapshot engine moves to filesnap 0.4.0.
Its new capture --declared-window <TURNS|unlimited> bounds how long a path the edit
API declared keeps being scanned after the turn that declared it. This plugin passes
nothing and keeps the engine's default of 99 turns, so captures cover what they
covered before.

A 0.x minor means "breaking", so the contract was diffed rather than trusted:

  • every subcommand this plugin drives — capture, declare, log, restore,
    undo, status — is unchanged but for that one added optional flag;
  • the JSON Lines envelope is still v: 1;
  • 0.4.0 reads a store written by 0.3.1 — the same turns come back with the same
    manifest hashes. Existing rewind points survive the upgrade.

The client manifest names packages that exist. dsh.client.inject still listed
@deepseek-ai/dsh-client-runtime, which the harness removed, and did not name what
actually carries the services this plugin injects — ctx.slots from
dsh-client-ui-renderer, ctx.sessions from dsh-api-session-controller.

This is a correction, not a repair, and that was checked before it was claimed: with
the 0.2.0 manifest, on a harness that has none of that package, the browser half still
loads. 0.2.0 is not broken in the field. What this fixes is a declaration that was
quietly false, and would stop being harmless the day the loader gets stricter.

Added

  • Compared with the other dsh rewind plugins
    — eight plugins across two grids, every claim checked against the published package,
    with the three ways a rewind can lose your work called out first.
  • What happens if the rewind itself is interrupted — the axis none of those grids
    covers. A restore here deletes only against a tombstone, so "I could not read it"
    never becomes "it was not there"; the rescue point exists before the first byte
    lands; the store is outside your project; and a turn whose capture failed is absent
    from the list rather than listed and refused on use.
  • A roadmap, and a
    note where the argument is made that gc, delete and doctor are engine commands
    this plugin does not expose yet — with the invocation that works today.
  • CONTRIBUTING, CODE_OF_CONDUCT, SECURITY, and issue and pull-request templates.
    Issues and pull requests are welcome.

Fixed — development only

The harness moved on: resolveSessionPreset is gone, sessionProjections.restore
takes the session header as a fourth argument, and the chat slots split into
packages/client/ui-chat while ctx.slots moved to packages/client/ui-renderer.
Building against a current harness works again, and harness:link now says which
failure it hit — a package the harness removed is not a checkout you forgot to build,
and reporting both the same way sent this project's CI looking in the wrong place.

None of this changes what the published plugin does at runtime. The imports that
follow the split are type-only, and both slot names it registers into exist on either
side of it.


变更

快照引擎升级到 filesnap 0.4.0
它新增的 capture --declared-window <TURNS|unlimited> 用来限定:一个由编辑 API 声明过
的路径,在最后一次声明它的那一轮之后还会被扫描多久。本插件不传这个参数,沿用引擎默认的
99 轮,所以捕获范围和之前一致。

0.x 的 minor 意味着"破坏性",所以契约是逐项对过的,不是默认它兼容:

  • 插件驱动的六个子命令 —— capturedeclarelogrestoreundostatus
    —— 除那一个新增的可选参数外完全没变;
  • JSON Lines 信封仍然是 v: 1;
  • 0.4.0 能读 0.3.1 写下的 store —— 同样的轮次回来了,manifest 哈希也一样。
    已有的回退点不会因为升级而失效。

客户端 manifest 现在只写真实存在的包。 dsh.client.inject 里还留着
@deepseek-ai/dsh-client-runtime(已被 harness 删除),却没写出真正提供所需服务的那两个
—— ctx.slots 来自 dsh-client-ui-renderer,ctx.sessions 来自
dsh-api-session-controller

这是订正,不是修复,而且是先验证再下结论的:用 0.2.0 的 manifest、在一个根本没有那个
包的 harness 上,浏览器那半照样加载。0.2.0 在线上没有坏。 这次改掉的是一处悄悄失真的
声明 —— 等哪天加载器变严格,它就不再无害了。

新增

  • 和其他 dsh rewind 插件的对比
    —— 八个插件、两张表格,每一条都对着已发布的包核实过,并把"会让你丢工作"的三种情况放在
    最前面。
  • 如果回退过程本身被中断 —— 那两张表都没覆盖的维度。这里的还原只在有 tombstone 授权
    时才删文件,所以"读不到"永远不等于"它不存在";救援点在第一个字节落盘之前就已存在;存储
    不在你的项目里;捕获失败的那一轮根本不出现在列表里,而不是列出来、点了再报错。
  • 路线图,
    以及在论证发生的地方直接说明:gcdeletedoctor 是引擎的命令,插件还没暴露出来
    —— 并给出今天就能用的调用方式。
  • CONTRIBUTINGCODE_OF_CONDUCTSECURITY,以及 issue 与 PR 模板。欢迎提
    issue 和 PR。

修复 —— 仅影响开发

harness 往前走了:resolveSessionPreset 没了,sessionProjections.restore 多了第四个
参数(会话 header),chat slots 拆进了 packages/client/ui-chat,ctx.slots 挪到了
packages/client/ui-renderer。现在对着新版 harness 能正常构建了;并且 harness:link
会说清楚到底是哪一种失败 —— "harness 删掉了这个包"和"你忘了构建"是两回事,把它们报成
同一句话,曾把本项目的 CI 指向了错误的方向。

这些都不改变已发布插件的运行时行为。 跟随拆分而调整的 import 全是 type-only,而插件
注册的两个 slot 名在拆分前后都存在。

Full Changelog: v0.2.0...v0.2.1

v0.2.0

Choose a tag to compare

@S2thend S2thend released this 27 Aug 15:47

v0.2.0 — A rewind now moves both halves to the same moment

Fixed

On 0.1.1 the control under a turn's answer restored the files to before that turn
while the fork kept that turn — so a transcript could end at "the file now contains
v3" with v1 on disk. The conversation went on describing work the workspace no longer
held.

sessions.fork({atSeq}) does not cut at its anchor. It takes the first turn/end
at or after it and keeps that whole turn — the convention the harness's own
per-message fork button relies on, where a message seq means "keep the turn this
message belongs to". This plugin passed a seq one event before the turn opened, which
the host rounds forward onto that same turn's end.

A point now anchors on the message that closed the previous turn. Restoring the
point for turn N hands back the workspace as turn N-1 left it, and the fork ends at
turn N-1: both halves describe the same moment.

The command path had a second fork implementation that disagreed with the host by the
same rule. Both paths now apply the host's, located by index rather than by treating a
seq as an offset — chunk events do not survive into the persisted log, so seq is sparse
on a resumed session.

Changed

  • The first turn of a session no longer offers a rewind control. There is no
    completed turn for a fork to keep. An omitted atSeq is refused rather than passed
    along, because the host reads it as the last completed turn — the newest state, the
    opposite of a rewind.
  • RewindPoint.boundary is now optional and names a seq inside the turn a fork
    keeps. It is reachable through the exported FilesnapProjection, which is why this
    is a minor rather than a patch.
  • Projection stateVersion 2 → 3; cached folds are discarded and refolded.

Added

  • A Chinese README.
  • A section on the filesnap engine,
    with measurements, and on using it from an agent that is not this one.
  • Credit to codex-rewind, where
    this design was worked out first.

Upgrading

Forks made by 0.1.1 are not repaired by upgrading. A child session created then
still holds the turn whose writes were reverted; the files in it are correct for the
point that was restored, and the last turn of its transcript is not.


修复

0.1.1 上,一轮回答下面的那个控件会把文件还原到这一轮之前,而 fork 却保留了这一
轮 —— 于是 transcript 停在"文件现在是 v3",磁盘上却是 v1。对话继续描述着工作区已经
不再持有的工作。

sessions.fork({atSeq}) 并不在锚点处切断。它取第一个位于锚点或其之后的 turn/end
并保留那一整轮 —— 这正是 harness 自己那个每条消息旁的 fork 按钮所依赖的约定:传一条消息
的 seq,意思是"保留这条消息所属的那一轮"。而本插件传的是这一轮开始一格的 seq,被
host 向后对齐到了这一轮自己的结尾。

现在点锚在上一轮的收尾消息上。恢复第 N 轮的点 = 把工作区交还成第 N-1 轮结束时的样
子,fork 也停在第 N-1 轮:两半描述同一个时刻。

命令路径上还有第二套 fork 实现,按同一条规则和 host 不一致。现在两条路径都用 host 的规
则,并且用下标查找而不是把 seq 当偏移量 —— chunk 事件不会进入持久化日志,所以在 resume
出来的会话上 seq 是稀疏的。

变更

  • 会话的第 1 轮不再提供回退控件。 它前面没有可供 fork 保留的完整轮次。省略 atSeq
    会被拒绝而不是照传,因为 host 会把它读成最后一个完成的轮次 —— 最新状态,和回退恰好
    相反。
  • RewindPoint.boundary 改为可选,含义是 fork 所保留的那一轮之内的一个 seq。它经由
    导出的 FilesnapProjection 对外可见,所以这是 minor 而不是 patch。
  • Projection stateVersion 2 → 3;缓存的折叠结果会被丢弃并重算。

新增

  • 中文 README。
  • 关于 filesnap 引擎的章节(带实测数
    据),以及如何在别的 agent 上使用它。
  • 致谢 codex-rewind —— 这套设计最
    先是在那里做通的。

升级说明

0.1.1 期间产生的 fork 不会因为升级而被修复。 那时创建的子会话里,仍然保留着那一轮
被回退掉的写入;它里面的文件对于被恢复的那个点是正确的,而 transcript 的最后一轮不是。
Full Changelog: v0.1.1...v0.2.0

v0.1.1

Choose a tag to compare

@S2thend S2thend released this 27 Aug 15:47

v0.1.1 — Install fix

Fixed

npm install dsh-filesnap and dsh plugin add failed on 0.1.0 with ERESOLVE
against every harness package:

Could not resolve dependency:
peer @deepseek-ai/dsh-llm@">=0.0.1-rc.1" from dsh-filesnap@0.1.0

The ranges were written to mean "any published version". They do not. A range
anchored on a prerelease only matches prereleases of the same x.y.z
— so
>=0.0.1-rc.1 admitted 0.0.1-rc.* and any stable release, but not 0.1.0-rc.8,
which is what the harness actually ships. (* and >=0.0.0-0 are no better: neither
matches a prerelease at all.)

Each peer is now >=0.0.1-rc.1 || >=0.1.0-rc.0, which names both prerelease lines
explicitly.

No functional change. If 0.1.0 installed for you, nothing here affects you.

Also

Publishing is gated on a clean typecheck, the full test suite, and a build of both
faces — lib/ is gitignored and is the entire payload, so a release cannot ship a tree
that was never built, or a package missing its browser half. Releases now go out
through npm trusted publishing (OIDC), so there is no long-lived registry token in this
repository.


修复

0.1.0 上 npm install dsh-filesnapdsh plugin add 会对每个 harness 包报
ERESOLVE:

Could not resolve dependency:
peer @deepseek-ai/dsh-llm@">=0.0.1-rc.1" from dsh-filesnap@0.1.0

那些范围本意是"任何已发布的版本"。但一个锚定在预发布版上的范围,只匹配同一个
x.y.z 的预发布版
—— 所以 >=0.0.1-rc.1 接受 0.0.1-rc.* 和任何正式版,却不接受
0.1.0-rc.8,而后者正是 harness 实际发布的。(*>=0.0.0-0 也救不了:它们根本
不匹配任何预发布版。)

现在每个 peer 都是 >=0.0.1-rc.1 || >=0.1.0-rc.0,把两条预发布线都写明。

功能没有变化。如果 0.1.0 在你那儿装得上,这个版本与你无关。

另外

发布现在被三道门卡着:类型检查、完整测试、以及两边产物的构建 —— lib/
gitignore 里、又是全部载荷,所以一次发布不可能带上从没构建过的代码,也不可能少了浏览器
那一半。发布改走 npm trusted publishing(OIDC),仓库里不再有长期有效的 registry
token。
Full Changelog: https://github.com/extracurricular-ai/dsh-filesnap/commits/v0.1.1

v0.1.0

Choose a tag to compare

@S2thend S2thend released this 27 Aug 15:50

v0.1.0 — First release

Rewind and redo for DeepSeek Harness: go back to the start of an earlier turn — the
conversation and the files it changed.

Works in a project that has never seen git init, and in one that has. Your commits,
your stash, your worktree state are untouched, and nothing is stored inside your
repository.

What it does

  • Captures once per turn, on agent/pre-step, before the model request and before
    any tool runs. The capture is awaited, so a snapshot is never half-taken.
  • Declares a pre-image before an edit lands, so a file the per-turn scan cannot see
    — outside the workspace, over the size limit, past the recency budget — is still
    restorable.
  • /rewind <point> forks the conversation and restores the files into the fork.
    /redo reverses it. /rewind status reports what is tracked and what it costs on
    disk.
  • A rewind control in each turn's own message row, beside copy and branch, and a
    status strip in the session header.

Install

dsh plugin --profile <name> add dsh-filesnap

Nothing to install by hand: the filesnap
engine is a dependency, so the prebuilt binary for your platform comes with it. Every
@deepseek-ai peer is optional — the plugin degrades rather than refusing to load.

Worth knowing before you install

The plugin records its rewind points as session events, and the harness has no
supported way for an out-of-repo plugin to declare an event type — so it declares them
by mutating a harness constant at load. Uninstalling the plugin leaves the
conversations it captured unopenable
, because the reader refuses a log holding a type
it does not know. The data is untouched on disk; reinstalling restores access.


DeepSeek Harness 的回退与重做:回到某一轮开始之前 —— 对话和它改过的文件一起

在从没 git init 过的项目里可用,在 git 仓库里也可用。你的 commit、stash、worktree
状态一概不动,也不会往你的仓库里存任何东西。

它做什么

  • 每轮捕获一次,在 agent/pre-step,早于模型请求、早于任何工具运行。捕获是被
    await 的,所以不会出现拍到一半的快照。
  • 在编辑落盘前声明前像,所以每轮扫描看不到的文件 —— 工作区之外的、超过体积上限
    的、超出近期预算的 —— 仍然可以还原。
  • /rewind <point> fork 出对话并把文件还原进这个 fork,/redo 撤销它,
    /rewind status 报告追踪了什么、占了多少磁盘。
  • 每一轮的消息操作行里有一个回退控件(和复制、分支并排),会话头部有一条状态栏。

安装

dsh plugin --profile <name> add dsh-filesnap

不需要手动装任何东西:filesnap
引擎是依赖项,你平台对应的预编译二进制会跟着装好。所有 @deepseek-ai peer 都是可选的
—— 缺了就降级,而不是拒绝加载。

安装前值得知道

插件把回退点记为会话事件,而 harness 没有为出仓插件提供声明事件类型的受支持途径 ——
所以它在加载时改写了一个 harness 常量。卸载插件会让它捕获过的对话打不开,因为读取
器会拒绝含有未知类型的日志。磁盘上的数据完好,重新安装即可恢复访问。

Full Changelog: https://github.com/extracurricular-ai/dsh-filesnap/commits/v0.1.0