Chuyển đến nội dung chính

dsh-expedition-memory

Đã xác minh

dsh-expedition-memory · v0.3.0 · MIT · Giao diện web

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

Cài đặt

dsh plugin add dsh-expedition-memory

Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.

Mã nguồn

Phát hành lên npm mà không có repository công khai. Hãy kiểm tra nội dung package trước khi cài.

Thẻ

Tác giả

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。