🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

任务记忆卡 xjhao-heim/dsh-task-memory

DeepSeek Harness
xjhao-heim24 天前有提交已提供中文文档扫描:含敏感能力

把做过的任务存成卡片,下次会话直接调用

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/10 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add xjhao-heim/dsh-task-memory
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · chat
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 24 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件发起外部网络请求
查看扫描证据(共 16 处,展示前 15 条)
  • 发起网络请求
    client.js:172
    const response = await fetch(url, init);
  • 读取本地文件
    lib/settings.js:99
    const raw = JSON.parse(await readFile(path, "utf8"));
  • 写入本地文件
    lib/settings.js:125
    await mkdir(join(path, ".."), { recursive: true });
  • 写入本地文件
    lib/settings.js:128
    await writeFile(temporary, `${JSON.stringify(clean, null, 2)}\n`, "utf8");
  • 写入本地文件
    lib/settings.js:129
    await rename(temporary, path);
  • 删除本地文件
    lib/settings.js:131
    await unlink(temporary).catch(() => {});
  • 读取本地文件
    lib/settings.js:174
    const raw = JSON.parse(readFileSync(path, "utf8"));
  • 读取本地文件
    lib/legacy.js:61
    const raw = JSON.parse(await readFile(path, "utf8"));
  • 读取本地文件
    lib/legacy.js:88
    entries = await readdir(current, { withFileTypes: true });
  • 读取本地文件
    lib/legacy.js:110
    const exists = await stat(paths.notes).then((info) => info.isDirectory(), () => false);
  • 读取本地文件
    lib/legacy.js:119
    for (const entry of (await readdir(paths.notes, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
  • 读取本地文件
    lib/legacy.js:125
    text = await readFile(file, "utf8");
  • 读取本地文件
    lib/legacy.js:258
    const bytes = await readFile(asset.file);
  • 删除本地文件递归
    lib/legacy.js:279
    await rm(target, { recursive: true, force: true });
  • 写入本地文件
    lib/db.js:88
    if (path !== ":memory:") mkdirSync(dirname(path), { recursive: true });
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 任务存成记忆卡
  • 下次会话直接调用
  • 工作区级任务记忆

DeepSeek Harness 插件:工作区级任务记忆,把做过的任务存成记忆卡,下次会话不必重新分析一遍。

README

由 DeepSeek 最新模型翻译生成
dsh-task-memory DeepSeek Harness 插件:工作区级任务记忆。 解决的问题:相似的活会反复出现,而每次会话都从零重新分析一遍。这个插件把「做过的任务」存成一人一张的记忆卡,并且让卡片以两段式被检索——先看索引,命中后才读那一张的正文,所以记忆再长也不需要每次全读。 两段式检索 | 阶段 | 机制 | 成本 | | 索引 | 一张有界的卡片摘要清单,注入到会话(一行一卡:名字 + 描述 + 触发词) | 常驻,且按使用频率排序、有硬上限 | | 正文 | 命中后用 task_memory_load 取那一张卡的正文 | 一次调用,只读一张 | 索引不进对话历史,也不按轮次重复;其余卡片用 task_memory_index / task_memory_search 发现。记忆再长也不需要每次全读。 上架过的卡片(见下)会额外出现在 Harness 的技能目录里,可以用内置 skill 工具加载——但那是可选的,不是索引机制本身。 数据布局 所有工作区共用一个 SQLite 数据库:$DSH_HOME/task-memory/memory.db(默认 ~/.dsh/task-memory/memory.db)。表内用 workspace 列隔离各工作区。
展开完整文档(README 剩余 1 节)
dsh-task-memory

DeepSeek Harness 插件:工作区级任务记忆。

解决的问题:相似的活会反复出现,而每次会话都从零重新分析一遍。这个插件把「做过的任务」存成一人一张的记忆卡,并且让卡片以两段式被检索——先看索引,命中后才读那一张的正文,所以记忆再长也不需要每次全读。

两段式检索

| 阶段 | 机制 | 成本 |
| 索引 | 一张有界的卡片摘要清单,注入到会话(一行一卡:名字 + 描述 + 触发词) | 常驻,且按使用频率排序、有硬上限 |
| 正文 | 命中后用 task_memory_load 取那一张卡的正文 | 一次调用,只读一张 |

索引不进对话历史,也不按轮次重复;其余卡片用 task_memory_index / task_memory_search 发现。记忆再长也不需要每次全读。

上架过的卡片(见下)会额外出现在 Harness 的技能目录里,可以用内置 skill 工具加载——但那是可选的,不是索引机制本身。

数据布局

所有工作区共用一个 SQLite 数据库:$DSH_HOME/task-memory/memory.db(默认 ~/.dsh/task-memory/memory.db)。表内用 workspace 列隔离各工作区。

meta (key, value) ← schema 版本等
cards (workspace, name, description, when_to_use, triggers, tags,
status, published, revision, created, updated, body, hits, last_used)
assets (workspace, card, path, bytes) ← 外键级联,随卡片一起删除

用 node:sqlite(Node 内置)实现,零第三方依赖、无需编译。本机 dsh-memento 已在用同一机制,所以 Electron 宿主也支持。

数据库相对文件存储买到的三个性质:

| 性质 | 说明 |
| 原子性 | 卡片与附件在同一事务里写入,崩溃不会留下「有附件没卡片」 |
| 唯一身份 | 卡名是主键列,「一件事只留一张卡」是 schema 保证,不是靠代码反复检查 |
| 计数不漂移 | hits/last_used 是卡片行上的列,不可能与卡片失联 |

索引仍然永远从行数据现算,不存索引表;## 变更记录 段落承担版本历史,所以也不需要 journal。

工具

| 工具 | 作用 |
| task_memory_index | 完整索引,可按 query / status / tag / tier 过滤。索引被上限截断时兜底 |
| task_memory_load | 按名字取一张卡的正文、元数据与附件清单 |
| task_memory_asset | 读取某张卡的某个附件(代码片段、补丁等,存于数据库) |
| task_memory_save | 落卡。默认 auto 模式会拒绝为已记录的任务再建一张卡,并返回候选卡 |
| task_memory_search | 跨卡片正文全文检索(带片段),触发词都没命中时兜底 |

新鲜度分档

记忆库有两种「旧」:内容过时(status: verified/draft/stale,人工判断)和久未使用(档位,自动按时间算)。两者正交——一张卡可以既「已确认」又「遗忘」(内容正确但一年没用了)。合成一个字段就再也说不清用户指的是哪种。

五档(天数可配置)

| 档位 | 默认距离 | 在注入索引里的呈现 |
| 近期 | ≤ 7 天 | 名字 + 描述 + 触发词 + 年龄 + 使用次数 |
| 之前 | 8–30 天 | 同上 |
| 很久之前 | 31–90 天 | 名字 + 描述(去掉触发词)+ 年龄 |
| 远古 | 91–365 天 | 只有名字,合并成一行 |
| 遗忘 | > 365 天 | 不列出,只报一行数量 |

这就是「检索频率不同」:越旧的卡越不容易出现在模型眼前,但一次也没丢。

时间基准:max(updated, lastUsed)

- 刚改写过的卡 = 知识新鲜 → 算活跃
- 刚被检索过的卡 = 仍被需要 → 算活跃

取较新者。只用 updated 会让「写完再没碰过」的卡一直假装新鲜;只用 lastUsed 会把刚创建、还没被检索过的卡判成遗忘。两者都是 cards 表上的列,所以不可能与卡片失联。

遗忘 ≠ 删除

遗忘档只做三件事:不注入、面板可见并标记、工具仍能检索到。行数据一个字段都不动——一个会自己删记忆的记忆插件比一个拥挤的更糟。

想清理时用面板上的「清理遗忘」按钮:它先列出所有遗忘档卡片让你确认,再删除指定名字的那些。没有自动清理。

档位边界写进配置(tierDays),随时可改;改完不需要迁移任何数据,因为档位永远是算出来的,不存进文件。

筛选

面板顶部有两个筛选器:

| 控件 | 作用 |
| 档位下拉 | 默认(按配置,通常只看近期)/ 全部档位 / 仅近期 / 仅之前 / 仅很久之前 / 仅远古 / 仅遗忘 |
| 起止日期 | 按最后活动日期过滤;范围默认取真实数据的最早~最晚,可改 |
| 日期条 | 每个「有卡片的日期」一个可点标签(显示月-日与张数),点一下就把范围收到那一天 |

日期条是必需的补充:原生 的日历弹层同样由 Chromium 绘制、CSS 碰不到,而用日期选择器排错时最关键的信息是「哪天有记忆」。所以那个信息放在选择器旁边,而不是指望日历里能标出来。

档位筛选在宿主侧执行(不是浏览器里过滤):列表本身有上限,本地过滤会把本该被查询捞出来的卡藏掉。

工具 task_memory_index 也接受 tier 参数,含义与面板一致。

设置页

「设置 → 任务记忆」区块,改完保存立即生效(不需要重启):

| 设置 | 说明 |
| 面板默认显示哪些档位 | 打开面板时默认勾选的档位;默认只看近期 |
| 档位边界(天) | 四个边界,改完立刻影响分档 |
| 注入索引的卡片上限 | 索引里最多列多少张 |
| 单张卡正文上限 | 超出截断并给出数据库位置 |
| 回合结束自动落卡 | 关掉后只能由你明确要求才记录 |

设置存在哪里:$DSH_HOME/task-memory/settings.json(默认 ~/.dsh/task-memory/settings.json)。

优先顺序是 设置文件 > cordis.patch.yml 里的 config > 内置默认。设置文件赢,因为它代表你最后一次在界面上做的选择;loader config 仍然是新安装的种子值。

打开设置页会补齐缺失的参数。 页面上每个参数都显示它的有效值,而一个「没有记录」的参数是无法在界面里改的——所以读取设置时会把本地文件缺的字段按当前生效的值写进去(不是写死默认值:通过 loader config 设过 maxCatalogCards: 99 的部署,文件里也该是 99,否则文件与运行中的插件说的不是一回事)。已经写过的值永远不会被覆盖。

为什么不用宿主自己的设置服务:dsh-settings 只渲染插件用 schemastery 声明了 .volatile() 字段的表单,而本插件零依赖、没有 schema。手写 YAML 去改 profile 的 patch 文件则有损坏整个 profile 启动配置的风险——为了一个设置面板不值得。所以设置写进自己的 JSON 文件,原子写(临时文件 + rename),坏文件退化成「没有覆盖」而不是启动失败。

兼容旧版记忆

早期版本把记忆存成 <工作区>/.dsh/task-memory/notes/<卡名>/SKILL.md 加一个 stats.json。那些文件里的知识仍然有效,所以面板上有一个「兼容旧版记忆」按钮,按当前选择的工作区把它搬进数据库。

点击后是三步,每步都有确认:

1. 预演(不写任何东西)—— 告诉你将导入多少张、其中多少是新建、多少与库里已有卡片同名、多少附件、多少张无法解析。
2. 导入 —— 已存在的同名卡片不会被覆盖(导入永远不破坏库里更新的版本),然后报告实际写入了什么。
3. 删除旧文件 —— 这一步单独确认,且在导入成功之后才提出。

导入用 importCard 原样写回:原始日期、revision、hits、last_used 都保留,而不是当作新卡重新推导。删除只动 notes/ 与 stats.json 两项,同目录下别的插件数据不会被碰。

自动落卡

默认不需要你开口。 每个回合结束时,如果这一轮确实做了实事(工具调用 ≥ 3 次)而且还没有存过卡,插件会自动追问模型一次:

任务记忆自动检查:本轮工作已结束。回顾本轮,判断是否产生了值得留存的东西……

模型据此判断本轮是否有非显然结论/踩坑/可复用流程:有就调 task_memory_save,没有就回一句「本轮无需记录」。

为什么要这么做:一段提示词只能「请求」模型记录,而刚干完活的模型不会可靠地执行又一条指令。之前的实测就是——你不主动说「记住这个」,卡片永远是零。所以这里用的是真实触发点而不是请求:宿主在关闭回合前会 await agent/turn-stopping,监听器调 agent.steer(...) 就能让机器多跑一步。

为什么是「追问」而不是「直接写」:只有模型分得清「可复用的根因」和「一次性的查询结果」。缺的不是判断力,而是提醒自己去判断这件事。

三个防呆:

| 条件 | 行为 |
| 本轮工具调用 < 3 次 | 不提醒(查询、闲聊、小改动) |
| 本轮已经存过卡 | 不提醒(记忆已是最新) |
| 本轮本身就是提醒引发的复查 | 不提醒(防死循环的关键) |

模型说「不值得存」时不会硬写。关闭方式:把 autoCapture 设为 false。

上架与不上架

卡片默认只留在本工作区的任务记忆里,不进技能目录。 需要时用 publish: true 单独上架。

为什么默认不上架:本插件的卡片注册成 runtime 技能后,会出现在 Harness 的技能目录里,而技能中心(Skill Center)页面列的就是同一份目录。技能中心是给人挑通用技能的地方,不该被某个工作区的排查记录塞满。而且技能中心虽然能编辑技能,却无法操作 runtime 条目(它列出的 runtime 技能没有文件路径),所以这些卡片在那里只是一排点了没反应的噪声。

| 状态 | 出现在任务记忆索引 | 出现在技能目录/技能中心 | 加载方式 |
| 未上架(默认) | ✅ | ❌ | task_memory_load / task_memory_search |
| 已上架(publish: true) | ✅ | ✅ | 上面两种,外加内置 skill 工具 |

上架与否不影响可检索性:未上架的卡一样会被注入索引、一样能被工具检索和加载,只是不会污染面向人的技能列表。上架状态是粘性的——一次 update 不会把它悄悄取消,只有显式传 publish: false 才撤下。

判断标准:跨任务复用的通用方法论才值得上架;某个项目的排查记录、一次性结论留在工作区即可。

上架后立即生效,不用等

技能的收集结果在注册表里是按 revision 缓存的,所以「改了卡片」本身不会让目录刷新。本插件在每次写入后主动调 control.invalidate() —— 它把 revision 加一、清掉缓存并通知观察者,于是下一次收集必然重新读取。

关键在于宿主侧的消费方式:@deepseek-ai/dsh-tool-skill 在每一步(agent/pre-step)都会 snapshot() 一次并比对摘要,摘要变了才推送新目录。所以 invalidate 之后的下一个步骤就会看到新卡片。

这对模型侧的技能列表是立即的。技能中心页面是浏览器里的列表,点一下刷新即可看到;它读的是同一份已失效的缓存,不会再拿到旧数据。

只记一份

task_memory_save 的三种模式:

- auto(默认):先用本地相似度(字符 trigram + 触发词重合 + 标签重合)比对已有卡片。判定为同一件事时不创建,直接返回那张卡的 name 与匹配理由,让模型改用 update。
- update:合并进指定卡。合并按 ## 段落进行——模型这次写到的段落被替换,没提到的段落原样保留,因此后一次保存不会悄悄抹掉先前的结论。revision 递增,triggers / tags 取并集。
- force-create:确实是一个不同任务时才用。

索引有硬上限(默认 50 张,按加载次数→更新时间排序)。被截断的卡仍然可以通过 task_memory_index / task_memory_search 找到——这是「记忆可以很长,但每次不必全读」的落地方式。

记忆面板(P3)

Harness 侧边栏里的「任务记忆」入口,中间列打开面板。左侧是卡片列表、右侧是详情/编辑区,两列各自滚动——详情不会因为你记忆变多而被推得越来越远。

| 操作 | 说明 |
| 浏览 | 顶部选工作区(带卡片数),左栏列表:名字、状态、描述、更新时间、revision、加载次数、触发词 |
| 查看 | 点左栏卡片,右栏显示正文与元数据 |
| 新建 | 点右上角「新建卡片」,在右栏填写;勾选「作为技能上架」(默认不勾) |
| 编辑 | 右栏直接改字段与正文后保存;上架开关也在这里 |
| 删除 | 二次确认,删掉卡片及其全部附件(外键级联) |
| 检索 | 按正文全文过滤左栏列表 |

状态显示中文:「已确认 / 草稿 / 可能过时」。存储里仍然是 verified / draft / stale 标识符 —— 面板只翻译显示,因为那三个词是工具 schema 和数据库列使用的值,就地改写会破坏与模型的契约。

人工编辑与模型写入的语义不同:模型 update 是按 ## 段落合并,而面板保存是逐字替换——你在编辑器里写的就是最终内容,删掉的段落和触发词不会自己回来。

实现要点:Host 侧用 ctx.webServer.register 挂 /api/task-memory/ 六条路由,客户端用 fetch 调用,两个 slot(sidebar.panellist + main)提供入口和页面。样式只用主题 token,明暗主题都跟随宿主。

界面里的下拉框是自绘的,不是原生 :后者的展开弹层由 Chromium/OS 绘制、CSS 碰不到,在暗色主题下会是白底黑字,与其余控件格格不入。自绘版直接采用宿主菜单的配方(--dsw-menu-surface-fill + --dsw-menu-backdrop-filter + --dsw-elevation-prominent),并保留原生控件的全部键盘与无障碍行为(方向键、Home/End、Enter/Space、Escape、点外部关闭、焦点回到触发器)。

安装

本插件不使用 npm 安装、没有 node_modules、不依赖任何 @deepseek-ai/ 包——link: 安装的插件是从真实路径加载的,Node 的解析走不到 profile 的 node_modules,所以裸包名会失败。lib/schema.js 因此自己实现了注册契约。

1. 加依赖(link: 指向本目录):
"dependencies": { "dsh-task-memory": "link:D:/AI/dsh-task-memory" }

2. 加进 bundles:
"bundles": [ ..., "dsh-task-memory" ]

3. 建 junction,让 Node 能解析到它: New-Item -ItemType Junction -Path "$env:USERPROFILE\.dsh\profiles\<profile>\node_modules\dsh-task-memory" -Target "D:\AI\dsh-task-memory"

配置

- id: dsh-task-memory
name: dsh-task-memory
config:
maxCatalogCards: 50 # 注入索引的卡片上限 maxBodyChars: 24000 # 单张卡正文的读取上限,超出截断并给出数据库位置 includeSystemPrompt: true autoCapture: true # 回合结束自动追问是否落卡;设 false 关闭
defaultTiers: [recent] # 面板默认显示哪些档位
tierDays: # 新鲜度档位的天数边界,必须递增 past: 7 # ≤7 天算「近期」 old: 30 # ≤30 天算「之前」 ancient: 90 # ≤90 天算「很久之前」 forgotten: 365 # ≤365 天算「远古」,超过则「遗忘」

这些都可以在「设置 → 任务记忆」里改,改完立即生效并存进设置文件(优先于这里的 loader config)。

测试

node test/all.test.js

node --test 会为每个文件 spawn 子进程(在受限沙箱下会被拒),test/all.test.js 把十组用例导入同一进程执行,断言完全相同。

test/setup.js 在任何测试运行前把 DSH_HOME 指向临时目录,所以跑测试绝不会创建或改动你真实的 ~/.dsh/task-memory/。每个用例还各自开 :memory: 库,互不干扰。每个测试文件都单独引入它,这样一个套件被单独运行时同样安全。

另有一个真实 Cordis 运行时的集成测试(test/integration.cordis.mjs:装配、通过真实注册表读写、上架语义、写入触发目录重扫、面板路由、回合结束追问及其防循环、卸载清理)。它必须在 profile 目录下运行——插件是 junction 安装的,从 D:\AI\dsh-task-memory 里跑会解析不到 @deepseek-ai/*。运行方式见该文件头部注释。

限制

- 卡片按会话工作目录隔离(数据库里是 workspace 列),不跨工作区共享。面板顶部的选择器可以在工作区间切换查看。
- 去重是启发式:它宁可拒绝新建也不轻易产生第二张卡,但最终判断在模型;工具会给出匹配理由,模型可以据此改用 force-create。
- 删除卡片不提供模型工具——模型能自行删除的记忆就是会消失的记忆。删除只在面板里由人操作。
- 自动落卡是追问而非直接写入:模型判断不值得存就不会存。这保住了判断质量,代价是它偶尔会漏存——想确保记下来时,直接说「记住这个」。
- 改用数据库后,卡片不再能直接用 read/edit 工具改;附件也用 task_memory_asset 读,而不是给一个文件路径。
- 改动插件的 JS 代码后需要重启 Harness 才会加载(patchReload: live 只覆盖新增插件与配置改动)。
预览分享卡片长什么样 ↓xjhao-heim/dsh-task-memory
分享 · DeepSeek Harness Hub
xjhao-heim/dsh-task-memory

把做过的任务存成卡片,下次会话直接调用

这是你的仓库?认领条目

回到顶部