English | 简体中文
dsh-memoryhub
MemoryHub(mh)与 DeepSeek Harness(dsh)的集成插件。
MemoryHub 把项目记忆保存为 .memoryhub/ 下 git 版本化检查点(checkpoint)中的 净化会话。本插件把它接入 dsh:
- 会话启动自动加载 —— 在会话工作区运行
mh load,把检查点记忆注入为持久
插件上下文。不需要提示词、不需要工具调用;模型直接带着记忆开工。
mh_save打通 dsh 会话到 mh 的桥 —— dsh 会话文件不在 mh 能识别的
transcript 格式之列(Claude Code / pi / Codex),所以插件把活动会话的持久 事件日志渲染成 pi 格式的 JSONL transcript(放在临时目录),再走 mh 现成的 --transcript 通道保存。净化保存与模型手写的压缩保存都支持,且按稳定的 每会话身份工作:重复保存是替换,绝不产生重复。
- 六个工具 ——
mh_load、mh_save、mh_status、mh_list、mh_search、
mh_checkpoint 封装 CLI;常见操作模型不必另起 shell。更少用的操作 (mh link、mh back、mh import、mh ui)仍留在 shell 里,skill 中 有说明。
- Web UI 里的 "Memory" 标签页 —— 与 chat、trajectory 并排:内嵌当前会话
工作区的 mh ui 检查点地图(见 [Memory 标签页](#memory-标签页))。
mh工作流 skill —— 运行时注册,教模型何时加载、何时以及如何保存
(包括撰写压缩摘要),还有 hub 规则(不写 HANDOFF.md 文件;mh 覆盖不到的 一律 git -C .memoryhub)。

前置条件
mh 已安装并在 PATH 上(uv tool install git+https://github.com/solknight48/memoryhub; 需要 git ≥ 2.32、Python ≥ 3.12)。插件以 shell 方式调用它 —— hub 格式、git 提交、 报错措辞都归 mh 所有。
安装
dsh plugin --profile web add github:solknight48/dsh-memoryhub包声明了 dsh.bundle,安装时会把它的 patch 层追加到 profile。想用本地检出: dsh plugin --profile web add ./dsh-memoryhub。
git 安装会拉取源码,而 pnpm ≥ 10 首次运行本包的 prepare 构建前会询问;按 dsh 的提示把 dsh-memoryhub 加进 profile 的 pnpm-workspace.yaml 白名单, 然后重新执行 add。
配置
每个字段都可省略;以下为默认值:
# $DSH_HOME/profiles/<name>/cordis.patch.yml —— 整行重写。
- insert:
- id: memoryhub
name: dsh-memoryhub
config:
mhBin: mh # mh 可执行文件(按 PATH 解析)
autoLoad: true # 每次会话启动都 mh load + 注入
# loadBudget: 6000 # 自动加载的 token 预算;省略则用 mh 默认值
timeoutMs: 20000 # 超过该时长即杀死任何 mh 调用
registerTools: true # 注册六个 mh_* 工具
registerSkill: true # 注册 mh 工作流 skill
noHubHint: false # 找不到 hub 时注入一行提示
uiTab: true # 提供 Memory 标签页请求的 mh-ui URL 路由
uiReadOnly: false # 为 Memory 标签页以只读模式启动 mh ui
uiBudget: none # Memory 标签页地图的初始预算('none' = 不显示超预算角标)
contextWindowTokens: 128000 # 上下文占比估算的兜底窗口大小noHubHint: false 时,没有 .memoryhub/ 的工作区完全静默:不注入、不刷日志。 "项目"是什么由 mh 自己决定 —— 从会话记录的 cwd 向上查找,与在 shell 里的 规则一致。
加载:默认不设预算,附带上下文占比回执
mh_load 加载所选检查点里的每一个会话 —— 原版 mh load 的超预算过滤 在此被部署选择关闭。确实只想装下最新若干会话时,传工具的 budget 参数即可。 (若重新启用自动加载,则通过 loadBudget 保留预算语义:它对每个会话静默注入, 所以按 token 逐项选择是否开启。)
每次成功的 mh_load 结尾都有一行回执,例如:
[memoryhub] memory ≈ 31,240 tokens ≈ 12.2% of the 256,000-token context window (adapter-reported); session total after load ≈ 18.6%窗口大小优先取模型适配器自己上报的值(会话最新的 request/context 事件), 取不到再用 contextWindowTokens。"session total" 加上了最近一次请求实测的 输入大小,读作"这次加载之后对话处于什么水位"。token 计数沿用 mh 自身的 ~4 字符/token 启发式,因此数值与 Memory 标签页里的尺寸一致。
Memory 标签页
本包是一个双面(dual-face)dsh 插件:加载宿主半边的那个 memoryhub 行, 同时把浏览器半边(package.json 里的 dsh.client)放进 web 启动图。浏览器 半边向 conversation.view 槽环注册一个入口 —— 位于 chat 与 trajectory 旁边的 Memory 标签页。
标签页展示的不是重新实现:它就是 mh ui 本身 —— 检查点地图(时间线、token 预算、逐轮编辑),嵌在 iframe 里,因此 mh 的每个功能和修复都原样出现。接线 方式:
1. 标签页向宿主半边请求该会话的地图 URL: GET /plugins/memoryhub/mh-ui?session=<id>。 2. 宿主半边解析会话的工作区(session.header.cwd),在那里惰性启动 mh ui --no-browser --port 0 —— 每个工作区一个服务器,池化管理,插件 卸载时一并杀掉。hub 发现逻辑仍是 mh 自己的(向上查找 .memoryhub/、 MH_HUB 覆盖)。 3. 带令牌的 URL(http://127.0.0.1:<port>/?t=…)从子进程 stdout 解析后返回 给标签页,由其放入 iframe。mh 自身的安全模型(回环绑定、一次性令牌、Host 校验)原样生效;uiReadOnly: true 时地图只读、不可编辑。
没有 hub 的工作区渲染为带重试按钮的空态,而不是起一台服务器。该路由只在 存在 web 服务器时注册,因此 headless 组合完全察觉不到这个功能。
地图以 mh ui --budget <uiBudget> 启动(默认 none,需要 memoryhub 仓库 2026-08-14 及以后、支持 mh ui --budget 的 mh):地图的预算框初始为空,超 预算预览角标保持关闭,与 mh_load 默认全部加载保持一致。把 uiBudget 设为 数字即可恢复预算预览。
保存是如何工作的
mh save 通过 transcript 识别会话。mh 认识 Claude Code、pi、Codex 三种 transcript 格式;dsh(暂时)不在其中。因此在 mh_save 时,插件会:
1. 遍历会话的持久事件日志,只保留 mh 自身净化器保留的内容:真实用户输入 (source.kind === 'user')与助手文本。插件注入的上下文(包括本插件自己的 自动加载快照)、工具调用/结果、推理过程一律不进。紧跟 mh 加载之后的 助手文本同样丢弃,直到下一条真实用户输入:那段回复是加载回执加上刚加载 记忆的摘要 —— 内容本来就在被加载的检查点里,保存它等于把旧记忆重新嵌进 每个新会话,越滚越大。无论是直接走 mh_load 工具还是经 run_code 代码块 (web GUI 的实际路径)发起的加载都能识别;用户自己的请求行保留。检测采用 双重确认:调用须具备加载的形状(直接 mh_load 调用、代码里的 tools.mh_load(…),或作为 shell 命令的 mh load —— grep/edit 载荷里 纯粹提及 mh load 不算)且该调用的结果须带 mh 自己的加载回执
(<!-- mh | loaded: 等);加载失败不会触发任何丢弃。
2. 把它写成 pi 格式 transcript,落在 $TMPDIR/dsh-memoryhub/dsh_<session-id>.jsonl。 3. 运行 mh save --transcript <该文件>(若模型在工具调用里写入了摘要,则 运行 mh save --compact --file <summary.md> --transcript <该文件>)。
值得知道的推论:
- 检查点文件名键遵循 mh 的 pi 规则(
pi-<id12>),尽管会话来自 dsh。仅是
外观差异;每会话身份稳定。
- 桥在每次保存时重建,因此更晚的保存能看到截至当时的整个会话,并替换先前的
表示(mh 对每个会话只保留一份表示)。
- 转向(steering)消息与文件附件不进桥(v1);压缩保存的摘要仍可承载任何
重要内容。
mh import只回填 Claude Code / pi / Codex 历史 —— dsh 历史只能经由本
插件保存进检查点。
验证
npm install
npm run build
npm test # 在临时 HOME 里对真实 mh CLI 做 e2e测试套件覆盖插件实际使用的每条路径:桥接 JSONL → mh save --transcript → mh load、压缩保存替换净化保存、二次保存替换首次,以及 mh-ui 进程池对真实 mh ui 服务器的测试(启动、令牌 URL、页面 200、无令牌 403、无 hub 映射)。 插件注册(六个工具 + mh(runtime) skill + Memory 标签页的客户端 bundle 进入 启动图、/plugins/memoryhub/mh-ui 路由分支)通过在安装了 bundle 的真实 dsh web profile 上启动验证。agent/session-start → mh load → agent.inject 路径使用了与 dsh 自身钩子桥相同的扩展点;但它尚未在真实模型会话上跑过 (需要 API key),Memory 标签页的浏览器内渲染同样没有。
目录结构
src/index.ts 插件:配置 schema、会话启动自动加载、工具、skill、
mh-ui 路由(webServer 软依赖)
src/bridge.ts dsh 会话事件 -> pi 格式 JSONL transcript
src/mh.ts mh CLI 的 execFile 执行器(非零退出码当数据用)
src/mh-ui.ts mh ui 进程池(每工作区一个地图服务器)
src/estimate.ts mh_load 的上下文占比估算(适配器窗口 + mh 启发式)
src/skill.ts 适配 dsh 的 mh 工作流 skill
src/client/ 浏览器半边:Memory 会话视图标签页(mh ui 的 iframe)
tests/ 针对真实 mh 二进制的 e2e许可证
MIT