Skip to content

dsh-expedition-memory

Verified

dsh-expedition-memory · v0.3.0 · MIT · Web UI

Task-level memory for long-horizon DSH sessions: force-injected, compaction-proof, manually mounted.

Install

dsh plugin add dsh-expedition-memory

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Tags

Creators

Readme

dsh-expedition-memory

给 DSH 长任务用的任务级记忆。

一个跑几百轮、经历几十次压缩的任务,开头敲定的约束、中途确认的事实,都会被摘要掉。本插件给它们一个能挺过压缩的存身之处:强制注入、每一步都在场,而不是等你想起来去检索。

<task-memory task="refactor-auth">
[约束 constraint](必须遵守)

### c1
必须用 zstd,不要引入 gzip

### c7 · [待审阅]
禁止改动 public API
[决策 decision](已定,除非有新证据否则不要重开)

### d3
选 SQLite 而非 Markdown
</task-memory>

三条性质

  1. 强制注入,而非检索。 记忆不是供你查询的知识库;它搭乘 agent loop 的动态运行时上下文通道,每一步都在场。那是 DSH 里唯一对压缩做了显式失效补偿的注入通道,所以每次压缩后记忆都会自行重新投影,而不是被悄悄摘要掉。
  2. 挂载全靠手动。 不从工作区、会话或"最近的任务"里推断任何东西。在你把某个任务记忆挂载进会话之前,本插件的贡献是彻底的零——未挂载的会话渲染出的贡献为空,框架会丢弃它。所以全局启用本插件不花任何 token。
  3. agent 写入,人类治理。 agent 可以记录和修订条目,且写入立即生效——若每次写入都要拦截,本系统要服务的长时无人值守运行就无从谈起。因此每个条目带审阅状态:在人类确认那个确切修订之前,它一直是 [unreviewed]。此后再编辑它会重新回到 [unreviewed]。

为什么不直接用 AGENTS.md?

AGENTS.md 是作为一条普通的 user/message 经 agent/pre-step 注入的,注入位置在表层(surface)中部——压缩范围可以也确实会把它遮蔽,因此它只能靠每一步重新注入自己来存活。它们覆盖的又是全局级与项目级作用域,而任务两者都不是。

本插件用一条专为抗压缩而选的注入通道,覆盖第三种作用域。见 DECISIONS.md 的决策 2。

适用范围:只有长任务需要它。中小型工作用 AGENTS.md 加代码、文档和 git 历史就够了——干完活开个新会话即可。本插件刻意不加任何启发式来猜你处在哪种任务里;挂不挂由你决定。

安装

dsh plugin --profile headless add dsh-expedition-memory

装进来的是构建产物的独立副本,与开发仓库无关:在仓库里改代码、pnpm build,都不会影响已经装好的那份。更新用 dsh plugin --profile headless update dsh-expedition-memory。

从 link: 迁移过来时必须两步 —— 先 remove 再 add:

dsh plugin --profile headless remove dsh-expedition-memory
dsh plugin --profile headless add dsh-expedition-memory

remove 不能省。 [实测] link: 还在的时候直接 add,pnpm 打印 Already up to date 然后静默保留 link: —— 你以为换成了独立副本,实际还在读本地仓库。唯一的成功标志:profile 的 package.json 里那一行从 link:… 变成 "^x.y.z"。

记忆存放在 $DSH_HOME/task-memory/memory.db(一个 SQLite 库)。可以用 profile 的 cordis.patch.yml 覆盖:

- id: expedition-memory
  config:
    dataDir: /custom/place           # 换库位置
    capacityWarnBytes: 8000          # 提示线
    capacityDangerBytes: 24000       # 强警告线
    allowAgentWrites: true           # 关掉可让 agent 不能写(运维开关)

怎么用

界面里有三个入口

分三层,因为记忆是在三种不同作用域下被访问的。把控件放错层,得到的控件就无法工作。

所有文案都走 DSH 自带的 locale 服务,自带 zh 与 en 两份字典。

① 全局库 —— 左栏的图标。 点开把主栏换成记忆工作区:本机的全部任务记忆。不涉及任何会话,所以增删改查、审阅、导出都在这里能做。挂载不在这里——那需要会话。

② 会话内的「任务记忆」标签页 —— 在 对话 / 轨迹 旁边。 显示本会话注入了什么,挂载也在这里发生。面板顶部三种状态:已挂载 / 本会话尚未挂载 / 继承只读(子 agent)。

③ 新会话的挂载芯片。 在第一条消息之前,「工作区」与「标准模式」旁边会多出一个「任务记忆」下拉,让你在新会话一开始就挂上记忆。发出第一条消息后它消失——那时标签页已经在了。

另外,已完成的轮次下方会有一行细条,报告该轮对记忆做了什么("写入 2 条 · 停用 1 条")。该轮没有任何变化时它什么都不渲染——每条轮次下都挂一行状态,在长任务里会变成噪音。

手写条目

条目区的添加条目展开一个表单:选类型、写一句自足的话、回车。

你手写的条目立即生效,并直接记为已审阅——审阅机制是用来暴露"上次之后 agent 写了什么"的,催你审阅自己刚敲下的字只是噪音。

添加不需要会话(写入的对象是任务,而任务跨会话),所以全局面板也是完整的管理界面。

两种记忆形态:条目与整份记忆

记忆有两种并存的形态,各自回答不同的问题:

  • 条目(上面那种按 kind 分组的东西)适合边跑边攒、条目之间弱关系的结论。四种类型管的是"这条结论算什么效力、能不能单独撤"。

  • 整份记忆适合一次存入一整块相互关联的内容——一个由若干约束和决策构成的闭环流程。你写一整篇文档,结构完全由你定(标题、小节、表格、代码块都行),系统不解析、不改写、不折行:

    <task-memory task="refactor-auth">
    本文件是一份整体记忆,由 agent 写入,用户尚未确认:当作暂定,依赖时说明,不要用它压过用户当场说的话。
    
    # 上游 Skill 优化:怎么推进、怎么算准出
    
        跑一轮 → 四路径取证 → 产物冻结
           ↓
        逐条归因
    </task-memory>
    

    为什么需要它:按类型把一整块内容打散存,等于把一件东西拆成零件交出去——形状会丢失(流程、取舍推理拆成条目就不存在了),而读的人每一轮都要重做拼装,还可能拼错。

    两条形态不互相取代:缺的是承载形状的能力,不是另一种更好的条目。共存时注入成两个独立区块。

    整份记忆的取舍:它的修订单位是整份(一次写一整篇,替换即整份换掉),旧正文不保留——它没有修订历史,所以删除它不可逆。而条目有修订历史、可逐条停用与恢复。

命令面

交互式会话里:

/memory create refactor-auth      # 新建任务(仅用户)
/memory mount refactor-auth       # 挂载进本会话
/memory status                    # 本会话看到什么
/memory list refactor-auth        # 列出条目
/memory review refactor-auth      # 确认所有待审阅
/memory delete refactor-auth#c1   # 永久删除一条
/memory history refactor-auth#c1  # 这条改过哪几版
/memory diff refactor-auth#c1     # 最后两版的逐行差异
/memory delete-task refactor-auth --yes-refactor-auth   # 删掉整个任务
/memory set-doc refactor-auth "……一整篇文档……"   # 写入整份记忆(整份替换)
/memory show-doc refactor-auth                    # 原样打印整份记忆
/memory review-doc refactor-auth@2                # 确认你读过的那一版
/memory clear-doc refactor-auth --yes-doc-refactor-auth   # 删整份记忆(条目不动)

headless(无 GUI)下用 CLI —— 同一套仅用户可用的能力:

dsh-expedition-memory where                      # 打印库路径
dsh-expedition-memory create refactor-auth
dsh-expedition-memory mount <session-id> refactor-auth
dsh-expedition-memory add refactor-auth constraint "必须用 zstd,不要 gzip"
dsh-expedition-memory review refactor-auth
dsh-expedition-memory show refactor-auth         # 导出可读文本
dsh-expedition-memory history refactor-auth#c1   # 修订历史
dsh-expedition-memory diff refactor-auth#c1      # 前后文本 diff
dsh-expedition-memory delete-task refactor-auth --yes-refactor-auth   # 删整个任务
dsh-expedition-memory set-doc refactor-auth "$(cat plan.md)"          # 写入整份记忆
dsh-expedition-memory show-doc refactor-auth                          # 原样打印
dsh-expedition-memory review-doc refactor-auth@2                      # 确认那一版
dsh-expedition-memory clear-doc refactor-auth --yes-doc-refactor-auth  # 删整份记忆

agent 看到的工具是 task_memory_write / task_memory_write_document / task_memory_update / task_memory_retire。它能写整份记忆(那是它的工作现场需要的能力),但不能确认或清除它——确认是人类的治理动作,清除是不可逆销毁。它无法创建任务、挂载任务、硬删条目,也没有"读记忆"的工具——记忆是每一步被注入给它的,不需要它去查(见 docs/FEATURES.md 的 C 节)。创建任务、挂载、硬删这类能力仅限用户,是结构使然而非约定使然:UI 通过模型无法调用的私有 RPC 通道访问宿主,而工具面本身就不包含这些操作。

记忆用来记什么

记录那些能挺过压缩、之后仍然重要的内容:

  • constraint —— 必须遵守
  • decision —— 已确立的取舍;没有新证据不要重新打开
  • fact —— 已确立的背景
  • preference —— 你希望事情怎么做

不要记进度("我们进行到第 4 步")。它立刻就会过期,然后被当作仍然成立的事实复述出来——这比不记更糟。进度属于 todo 列表或代码,不属于记忆。

值得了解的行为

  • 绝不静默裁剪。 超过容量阈值时记忆仍然全量注入;UI 只给容量提示,由人来修剪。把部分条目对模型隐藏、却让它自信地基于剩余内容推理,正是本设计拒绝的失败模式。
  • 取消挂载会停止注入,但不会抹除。 最新快照会丢掉这份记忆,但挂载期间注入的那份快照仍留在历史里,直到被压缩遮蔽。快照是取代,不是撤回。
  • 直接改数据库会被发现。 条目带指纹,所以在插件之外做的改动会被报告为[unreviewed]、作者为 unknown——既不隐藏,也不自动修复。
  • 子 agent 以只读方式继承父会话挂载的任务记忆。委派出去的工作也必须遵守约束;但每个任务只有一个写入者,才能保持一致。
  • fork 会继承挂载:手动分叉一个会话时,父会话的挂载会被复制成新会话自己的挂载,因此可写、且与父会话互相独立。只对之后新产生的 fork 生效——已有的旧 fork 不会因此被补上挂载(见决策 19)。挂载是按机器记录的。
  • 记忆是每步常驻成本,而且每次被压缩遮蔽后会重新支付一遍全文。这是容量提示存在的理由,也是"记忆不该无限增长"的理由。

开发

pnpm install
pnpm verify     # typecheck + 单测 + build
pnpm e2e        # 真起 DSH(隔离 DSH_HOME)的端到端检查

改代码前先读 AGENTS.md(给 agent 的导航)或 docs/ 下的文档。

在不碰主力 DSH 安装的情况下试用

scripts/dev-instance.sh start     # 构建 + 建隔离 profile + 启动(仅本机)
scripts/dev-instance.sh status    # 打印要打开的 URL
scripts/dev-instance.sh stop

它用一份 scratch DSH_HOME(.dsh-dev/)和端口 20387,因此能与跑在 19387 上的主力实例并存,~/.dsh 一律不受影响。

要让别人从局域网访问,加 --remote:它同时放行外壳的 Host 检查并起 socat 转发, 启动后逐地址实测可达性。⚠️ 这会把实例暴露到网络,而它跑的是你的凭据。详见 docs/DEV-INSTANCE.md。

⚠️ 别跑 reset —— 它 rm -rf .dsh-dev,连库和会话日志一起删。

发布

pnpm release:patch     # npm version patch && pnpm publish

prepack 会在打包前重建,prepublishOnly 会跑 pnpm verify——测试不过就发不出去。所以不需要(也不该)手工记得先构建:忘了构建这件事被钩子封死了。

lib/ 不进 git(.gitignore 里),files 字段让 npm publish 直接从工作区取它。产物是公开的,因此 tests/artifact-hygiene.test.ts 守着产物的卫生:里面既不能有源码注释,也不能有构建机的绝对路径。src/ 里的注释是写给维护者的设计推理,不该随包公开;这类问题只有真去读产物才会发现,跑测试和类型检查都看不见。

发布授权与镜像同步延迟这两个坑记在 docs/DEV-INSTANCE.md 的发布小节:npm 对新账号要求 bypass-2FA 的 granular token(报 403 而不是 EOTP),而插件管理器的 fallback 镜像可能比 npm 慢,症状是侧边栏报「这个包没有声明组合包」。

文档

  • 改代码(人或 agent)从 AGENTS.md 进:动手前读什么、改完要跑什么、九条会静默失败的硬性契约。
  • 想知道有哪些功能看 docs/FEATURES.md —— 功能树,功能真源。
  • 完整索引(按"你想干什么"导航)在 docs/index.md。