Saltar al contenido principal
H

dsh-fork-map

hercules-debug/dsh-fork-map

Per-workspace Fork library: a bookmark on each finished assistant turn stores that turn boundary as a full compressed snapshot that survives deletion of the source session; a "Fork 库" conversation tab lists a workspace's forks as a tree (a fork saved from a launched session nests under its parent) with name, turn count, last question and answer, note and source link, and "从这里启动新会话" seeds a new session from any fork, repeatable. Each fork then names itself from its own context: the snapshot is replayed to the model with a naming request appended, and that exchange is never written into the snapshot or a session.

Instalar

dsh plugin --profile web add github:hercules-debug/dsh-fork-map

README

🌿 dsh-fork-map

把对话的某一刻存下来,以后想从这里重来几次都行。

给 DeepSeek Harness Web 用的 Fork 库插件。 每个工作区一个持久化 Fork 库。Fork 是可以反复取用的「上下文资源」,不是一次性按钮。

DSH Node License


为什么要它

DSH 原生的「分叉」按钮点一下就立刻生成一个新会话,分叉点本身不会留下来。想从同一个位置再试一次,你得回到原会话、找到那一轮、再点一次。原会话一旦删了,那个位置就彻底没了。

实际用起来常见这些情况:

  • 花了二十轮把项目背景、约束、代码结构讲清楚,接下来想从这个状态出发试三种不同方案。
  • 某一轮回复写得很好,想把它当成以后反复用的起点,比如固定的 code review 上下文、某个模块的讲解上下文。
  • 会话越聊越乱,想回到之前某个干净的节点重新开始,又不想留着那个乱掉的会话。

dsh-fork-map 把分叉点变成一个独立、持久、可命名的东西:

原生分叉Fork 库
点一下之后立刻变成一个新会话存进库里,当前对话不受打断
能用几次一次不限,每次都新建独立会话
源会话删了无法再从那个位置分叉不受影响,快照是完整独立的
怎么找回来去原会话里翻到那一轮「Fork 库」标签页按工作区列出
命名、备注不支持Fork 自己起名写备注,也可手动改
谱系侧栏父子嵌套Fork 图:从 Fork 启动的会话再存 Fork,自动挂成子节点

三步用法

1. 存:在任意一轮回复上点「存为 Fork」

每轮回复下方的操作栏里,原生分叉按钮旁边多了一个书签图标。点一下,到这一轮为止的完整上下文就存进当前工作区的 Fork 库,对话本身不受打断。

  …助手的回复…

  👍  👎  ⑂  🔖   已存入 Fork 库
             ↑
         存为 Fork
  • 同一轮重复点只存一份,会提示「这一轮已在 Fork 库中」。
  • 还在生成中的轮次不能存,等它结束再点。

2. 看:打开「Fork 库」标签

对话区顶部「对话 / 轨迹」旁边多了一个「Fork 库」标签,里面是当前工作区的 Fork 图:

Fork 库   my-project                       [当前工作区 | 全部]  刷新

my-project   ~/code/my-project · 3 个 Fork
┌────────────────────────────────────────────────────┐
│ 项目背景讲清楚了                     12 轮 · 2 天前 │
│ 最后提问  鉴权模块现在的问题在哪?                  │
│ 最后回复  主要有三处:token 刷新没加锁……            │
│ 从这里分别试方案 A / B / C                          │
│ 来源:重构鉴权(已删除) · 快照 84K                 │
│ [从这里启动新会话] [改名] [AI 起名] [改备注] [删除] │
└────────────────────────────────────────────────────┘
   ┆
   ├─ ┌──────────────────────────────────────────┐
   ┆  │ 方案 B 跑通了               16 轮 · 1 天前 │
   ┆  └──────────────────────────────────────────┘
   └─ ┌──────────────────────────────────────────┐
      │ 方案 C:先改数据库          15 轮 · 今天   │
      └──────────────────────────────────────────┘
image
  • 每张卡片显示名称、轮数、最后一问、最后一答、备注和来源会话。
  • 来源会话可以点,点了直接跳过去;已删除的会划线标出。
  • 在某个 Fork 启动出来的会话里再存 Fork,新 Fork 会挂在原 Fork 下面,谱系一眼能看清。
  • 默认只看当前工作区,切到「全部」可以看所有工作区。

3. 用:点「从这里启动新会话」

新会话带着到那一轮为止的完整上下文直接打开,标题为 <名称> · Fork N,并归入同一路径的工作区。可以连续点,每次都是一个全新的独立会话,互不影响。

名字和备注:Fork 自己起

存下 Fork 后,卡片先显示「正在起名…」,几秒后名字和备注自动出现。

起名的是 Fork 自己:插件把快照还原成模型当时看到的完整上下文,末尾临时加一条命名请求,让模型以「我就是这段对话」的身份给自己起名、写备注(做到了哪、适合从这里接着干什么)。如果这个 Fork 是从另一个 Fork 启动的会话里存的,请求里还会带上父 Fork 的名字和备注,让它说清楚相对父 Fork 又往前走了什么。

  • 不污染 Fork:命名请求和模型的回答只存在于这一次模型调用里,不写进快照,也不写进任何会话。之后从 Fork 启动的会话看不到它。
  • 优先用这段对话最后一次用的模型(请求前缀一致,能命中提示缓存);那个模型不可用时退回当前默认模型。
  • 手动改过的名字或备注不会被自动覆盖。点「AI 起名」会重新生成并覆盖。
  • 起名失败时 Fork 照常可用,名称退回源会话标题,原因写进 dsh 日志。

双击名称可以手动改名。

安装

需要 DSH Web(@deepseek-ai/dsh)和 Node.js 22 或更高版本。

git clone https://github.com/Hercules-debug/dsh-fork-map.git
dsh plugin --profile web add /path/to/dsh-fork-map

装好后重启 dsh web,再刷新浏览器。插件的宿主部分要在进程启动时加载,只刷新页面不够。

手动安装(不用 dsh plugin)
  1. 在 ~/.dsh/profiles/web/package.json 里:
    • dependencies 加 "dsh-fork-map": "link:/path/to/dsh-fork-map"
    • dsh.profile.bundles 加 "dsh-fork-map"
  2. 在 profile 目录运行 pnpm install,或者手动把 node_modules/dsh-fork-map 软链到仓库目录。
  3. 如果 profile 的 cordis.patch.yml 里有 - id: fork-map 加 disabled: true,把这两行删掉。
  4. 重启 dsh web。

工作原理

Fork 是完整快照,不是指向源会话的引用。 保存时,插件把源会话从开头到这一轮结束的全部事件取出来,压缩后单独存一份。之后不管源会话被删除还是继续往下聊,Fork 的内容都不会变。

切点和原生分叉完全一致。 从你点的那条回复往后找第一个 turn/end,再推进到下一个 turn/start 之前,所以轮次之间的模型切换这类事件也会带上。从 Fork 启动的会话和用原生分叉得到的会话,上下文是同一份。

启动走 DSH 自己的会话创建流程。 新会话用的是 session controller 的同一套组装逻辑(composeAgent 加 agents.create),由 DSH 管理生命周期,用当前选中的模型,保留源会话的 agent preset。唯一的区别是不记录父会话,所以侧栏不会把它嵌在一个可能已经删掉的会话下面。Fork 和会话之间的关系由 Fork 库自己记录。

数据存在哪

内容位置
Fork 索引(名称、备注、来源、启动记录,启动记录只在后台用于编号和谱系)~/.dsh/storages/fork_library.json(DSH storage domain)
上下文快照~/.dsh/storages/fork_library_snapshots/<forkId>.json.zst

快照用 zstd 压缩,写入时先写临时文件再原子替换,不会留下半截文件。删除 Fork 只删快照和索引,已经启动的会话不受影响。

HTTP 接口

全部挂在 DSH 的 /api 鉴权范围内,跟 GUI 用同一套登录 cookie,不对外暴露。

方法路径请求体
GET/api/forkLibrary.list无
POST/api/forkLibrary.save{ sessionId, messageId?, atSeq?, label? }
POST/api/forkLibrary.launch{ forkId },返回 { sessionId }
POST/api/forkLibrary.update{ forkId, label?, note? }
POST/api/forkLibrary.delete{ forkId }
POST/api/forkLibrary.autoname{ forkId },让 Fork 重新给自己起名

项目结构

lib/
  index.js     宿主插件入口:打开存储、注册 /api/forkLibrary.* 路由
  library.js   保存 / 启动 / 改名 / 自动起名 / 删除
  store.js     切点计算、上下文还原、摘要提取、快照读写(不依赖 DSH,可单测)
  client.js    Web 端:「存为 Fork」按钮和「Fork 库」标签页
test/
  library.test.js
cordis.patch.yml   把插件挂进 profile

开发与测试:

npm test    # node --test test/*.test.js

测试覆盖:切点规则、未结束轮次的拒绝、同轮去重、同一 Fork 连续启动两次、源会话删除后仍能启动、子 Fork 谱系,删除父 Fork 后子节点的处理,以及自动起名(完整上下文回放、父 Fork 信息注入、不改动快照、不覆盖手动编辑、模型回退)。

已知限制

  • 只能在已经结束的轮次上存 Fork。
  • 子代理(subagent)会话不能存 Fork。
  • 新会话的工作目录固定为保存时的路径。如果这个目录被删或被移动,从 Fork 启动的会话可能无法正常工作,也不会归入任何工作区。
  • 改了插件的宿主部分(lib/index.js、lib/library.js、lib/store.js),要重启 dsh web 才生效。

License

MIT

Plugins relacionados