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

项目工程记忆 yuyuyyyyyyyyyyyy/dsh-project-memory

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

跨会话记住项目结论,规划前自动召回注入提示词

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

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

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

状态速览

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

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

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

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

可执行外部命令读写/删除本地文件
查看扫描证据(共 36 处,展示前 34 条)
  • 写入本地文件
    index.js:124
    await mkdir(dir, { recursive: true })
  • 读取本地文件
    index.js:171
    const parsed = JSON.parse(await readFile(path, 'utf8'))
  • 读取本地文件
    index.js:200
    const info = await stat(path).catch(() => undefined)
  • 写入本地文件
    index.js:219
    await rename(path, aside)
  • 删除本地文件
    index.js:223
    await rm(aside, { force: true }).catch(() => {})
  • 删除本地文件
    index.js:238
    await rm(held.path, { force: true }).catch(() => {})
  • 读取本地文件
    index.js:775
    const document = JSON.parse(await readFile(this.recordPath(recordId), 'utf8'))
  • 读取本地文件
    index.js:796
    entries = await readdir(this.recordsDir, { withFileTypes: true })
  • 读取本地文件
    index.js:837
    await readdir(this.recordsDir)
  • 写入本地文件
    lease.test.mjs:70
    writeFileSync(file, lines.concat('').join('\n'))
  • 读取本地文件
    lease.test.mjs:125
    return JSON.parse(readFileSync(join(store, DOMAIN_DIR, 'records', id + '.json'), 'utf8')).record
  • 读取本地文件
    lease.test.mjs:135
    if (existsSync(path)) return true
  • 执行外部命令来自配置/变量
    lease.test.mjs:144
    const child = spawn(process.execPath, ['--import', './register-deps.mjs', 'lease.test.mjs', 'child', ...args], {
  • 读取本地文件
    lease.test.mjs:158
    const id = readFileSync(join(store, 'seed-id.txt'), 'utf8').trim()
  • 写入本地文件
    lease.test.mjs:159
    writeFileSync(join(store, 'ready-' + role + '.json'), JSON.stringify({ role }))
  • 写入本地文件
    lease.test.mjs:163
    writeFileSync(join(store, 'result-verify.json'), JSON.stringify({
  • 写入本地文件
    lease.test.mjs:183
    writeFileSync(join(store, 'result-' + role + '.json'), JSON.stringify({ role, calls: n, errors }))
  • 写入本地文件
    lease.test.mjs:219
    writeFileSync(join(store, 'seed-id.txt'), seeded.id)
  • 写入本地文件
    lease.test.mjs:225
    writeFileSync(join(store, 'go'), 'go')
  • 读取本地文件
    lease.test.mjs:231
    return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')).errors : ['child ' + role + ' produced no result']
  • 读取本地文件
    lease.test.mjs:231
    return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')).errors : ['child ' + role + ' produced no result']
  • 读取本地文件
    lease.test.mjs:239
    const held = existsSync(leases) ? readdirSync(leases) : []
  • 写入本地文件
    loader.test.mjs:124
    writeFileSync(file, [
  • 写入本地文件
    regression-consistency.test.mjs:62
    writeFileSync(file, lines.concat('').join('\n'))
  • 读取本地文件
    regression-consistency.test.mjs:139
    const indexIds = (store, project) => JSON.parse(readFileSync(join(store, DOMAIN_DIR, 'index', project + '.json'), 'utf8')).record.ids
  • 读取本地文件
    regression-consistency.test.mjs:156
    check('C1 the document is gone from the medium', existsSync(recordPath(store, seed.id)) === false)
  • 读取本地文件
    regression-consistency.test.mjs:167
    check('C5 the deleted document was NOT written back', existsSync(recordPath(store, seed.id)) === false)
  • 读取本地文件
    regression-consistency.test.mjs:262
    return JSON.parse(readFileSync(leasePath(store, project), 'utf8'))
  • 读取本地文件
    regression-consistency.test.mjs:278
    check('L8 the successor releases its own lock', existsSync(leasePath(store, project)) === false)
  • 读取本地文件
    regression-consistency.test.mjs:279
    check('L9 no stale lock files are left behind', readdirSync(join(store, DOMAIN_DIR, '.leases')).length === 0, JSON.stringify(readdirSync(join(store, D
  • 写入本地文件
    regression-forget.test.mjs:52
    writeFileSync(file, [
  • 写入本地文件
    regression.test.mjs:55
    writeFileSync(file, [
  • 写入本地文件
    test.mjs:87
    writeFileSync(file, lines.concat('').join('\n'))
  • 写入本地文件
    test.mjs:468
    writeFileSync(blocker, 'a file where a directory would have to go')
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

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

功能亮点

  • 跨会话持久记录项目结论
  • 规划前召回注入提示词
  • 记录根因、失败尝试与约束

给 DeepSeek Harness 上编码智能体的宿主层跨会话工程记忆:按项目持久记录根因、失败尝试和约束,在智能体规划前召回进提示词。一条 `dsh plugin add` 搞定,不用选预设,所有模式都自带。

README

由 DeepSeek 最新模型翻译生成
dsh-project-memory 为运行在 DeepSeek Harness 上的编码代理提供跨会话的工程记忆。 一个跨多个会话在同一项目上工作的编码代理,对自己的工作没有记忆。它会重新推导已经找到的原因,更糟的是——它会重试项目已经证明行不通的方法。这个插件为项目提供一个工程结论的持久存储,检索与当前任务相关的少数几条,并在代理规划之前将它们注入提示词。 它存储的是结论,不是对话记录。一条记录将观察到的证据、推断和已验证的结论分开,因为把这三者混为一谈,正是一个猜测变成“历史”的方式。 它贡献了什么 一个宿主平面(host-plane)的 Cordis 插件。它不改变已发布的 harness:没有核心包被修补,没有已发布的预设被复制或编辑。 没有模式需要选择 因为这一行位于宿主平面,它已经在每一个代理预设中处于激活状态——standard、ptc、minimal、cordis,以及你自己编写的任何预设。模式选择器中没有 project-memory 预设可供选择,而编写一个反而是倒退:预设领域(preset realm)会获得自己的实例,因此每个预设的副本将不再跨预设共享。如果早先的安装留下了这样一个预设,请删除它——没有任何东西依赖它。
展开完整文档(README 剩余 1 节)
dsh-project-memory

为运行在 DeepSeek Harness 上的编码代理提供跨会话的工程记忆。

一个跨多个会话在同一项目上工作的编码代理,对自己的工作没有记忆。它会重新推导已经找到的原因,更糟的是——它会重试项目已经证明行不通的方法。这个插件为项目提供一个工程结论的持久存储,检索与当前任务相关的少数几条,并在代理规划之前将它们注入提示词。

它存储的是结论,不是对话记录。一条记录将观察到的证据、推断和已验证的结论分开,因为把这三者混为一谈,正是一个猜测变成“历史”的方式。

它贡献了什么

一个宿主平面(host-plane)的 Cordis 插件。它不改变已发布的 harness:没有核心包被修补,没有已发布的预设被复制或编辑。

没有模式需要选择

因为这一行位于宿主平面,它已经在每一个代理预设中处于激活状态——standard、ptc、minimal、cordis,以及你自己编写的任何预设。模式选择器中没有 project-memory 预设可供选择,而编写一个反而是倒退:预设领域(preset realm)会获得自己的实例,因此每个预设的副本将不再跨预设共享。如果早先的安装留下了这样一个预设,请删除它——没有任何东西依赖它。

| 贡献 | 钩子 | 原因 |
| projectMemory 服务 | 宿主平面 | 记录项目每个会话的回写;每个会话(预设领域)的实例会破坏这一特性 |
| project-memory:recall | ctx.systemPrompt.context() | 召回在提示词组装期间运行,因此历史在代理对任务进行推理之前就已存在 |
| project-memory:usage | ctx.systemPrompt.section() | 常设约定:先搜索;当该轮产生了可复用的知识时记录;纠正而不是重复 |
| memory_search | ctx.tools.register() | 按文件、符号、错误文本、症状、功能进行显式查找 |
| memory_record | ctx.tools.register() | 写入一条结构化结论 |
| memory_correct | ctx.tools.register() | 将先前的结论标记为 superseded / corrected / invalidated / confirmed,可选地链接一个替代项 |
| memory_forget | ctx.tools.register() | 删除一条记录,或一批有界的非活动记录,并带有项目检查 |
| memory_audit | ctx.tools.register() | 回读被召回了什么、为什么,以及什么被淘汰了 |

记录模式

字段,按它们承载的陈述类型分组:

| 分组 | 字段 |
| 身份 | id、version、project、project_path、created_at、session_id、cwd |
| 问题 | problem、symptoms[] |
| 观察到的 | facts[] — { statement, source } |
| 推断的 | hypotheses[] — { statement, status: open \| rejected \| confirmed } |
| 尝试 | attempts[] — { action, outcome: failed \| partial \| succeeded, why }、failed_attempts[] |
| 已结束 | root_cause、conclusion: fact \| inferred \| verified \| decision、verification |
| 结果 | changes[]、constraints[] |
| 召回信号 | related_files[]、related_symbols[]、tags[] |
| 修订 | status: active \| confirmed \| superseded \| corrected \| invalidated、superseded_by、supersedes[]、revision、revised_at |

除非 verification 非空,否则 memory_record 会拒绝 conclusion: "verified"。存储不会接受未经证实的声明为已验证。

召回

在每次提示组装时,插件会从会话日志构建一个查询:

- 最后一条人类消息(source.kind === 'user' —— 绝不是注入的上下文,绝不是工具结果);
- 从最近的 tool/call 参数中采集的文件路径和符号;
- 会话的 cwd。

候选会被过滤为仅该项目并评分:

| 信号 | 权重 |
| related_files 路径命中 | 5 |
| related_symbols 命中 | 4 |
| tags 命中 | 3 |
| problem / symptoms 词项命中 | 2 |
| 先前的 failed_attempts / constraints 词项命中 | 1.5 |
| 字符 n-gram 相似度 —— 仅当以上均未匹配时 | ×6,下限 0.12 |

单个 CJK 字符的得分只是一个词的一小部分,因为 CJK 文本会被分词为 unigram 和 bigram,而仅靠 unigram 会让不相关的文本匹配成功。最多保留 maxRecallRecords(默认 3)条,每条都有界;被取代或失效的记录会连同它们所替代的记录一起被过滤掉。当没有任何内容超过 minScore 时,不会注入任何内容 —— 没有空块,也不会为不相关的任务产生每请求成本。

项目标识

projectIdOf(cwd) = 完全规范化后的 cwd 的 slug 加上其哈希。每次读取都按该键过滤,因此两个具有相同症状的项目无法看到彼此的记忆。Windows 路径会进行大小写折叠;分隔符会被规范化。

透明度

每次召回、写入和修订都会追加到有界的审计轨迹中,可通过 memory_audit 读取,并可按操作和项目过滤。每条条目都携带查询、候选数量、注入的记录 id、每条记录的相关性原因以及注入大小 —— 因此“为什么这条被召回,什么落选了?”是可以回答的。使用 DSH_PROJECT_MEMORY_DEBUG=1(或行上的 debug: true)时,条目也会以 info 级别记录日志。

安装

dsh plugin --profile web add github:yuyuyyyyyyyyyyyy/dsh-project-memory

然后重启 dsh。这就是整个安装过程。此包声明了 dsh.bundle.patch,因此 dsh plugin 会将其追加到配置文件的 dsh.profile.bundles,而该 bundle 自身的 cordis.patch.yml 会在宿主侧挂载该插件。将 web 替换为你的配置文件名称;如果你愿意,可将 github: 替换为注册表名称、tarball 或本地路径。

dsh plugin 是一个轻量的 pnpm 转发器,因此 pnpm 必须在 PATH 上(corepack enable pnpm,或 npm i -g pnpm)。此包不附带安装脚本,因此没有任何需要允许 pnpm 构建的内容。

手动安装,不使用 pnpm

1. 将此目录放在 $DSH_HOME/profiles/node_modules/dsh-project-memory/,或放在 $DSH_HOME/profiles/ 下的任何其他位置,只要 Node 的 node_modules 查找能从 profile 目录走到那里即可。
2. 将 "dsh-project-memory" 添加到 $DSH_HOME/profiles//package.json 中的 dsh.profile.bundles 数组。
3. 重启 dsh。

裸包名会从 profile 目录的 node_modules 解析;./relative 名称会相对于 patch 文件自身的目录解析;!!js 名称会导致启动崩溃(参见部署约束)。

部署约束

这些是踩坑后总结出来的;它们也存在于本插件所管理的记忆中。

1. patch 行的 name 必须是字面字符串——绝不能是 !!js 表达式。
patch 层由 anchorInsertedPluginNames 锚定,它只会重写它能证明是字符串的 name(typeof entry.name === 'string')。
一个 !!js 标量会以 {__jsExpr} 对象的形式到达,跳过该重写,进入 Loader 的 import(),然后启动会以 name.startsWith is not a function 崩溃。!!js 用于行的 config 值;行的 name 会在任何插值之前被用于解析模块。
2. 编辑插件源码不会更新已挂载在运行中进程里的预设。
模块按 URL 缓存:改动组合、挂载戳记变化,甚至将预设指向一个全新命名的文件,都会继续提供该进程最初导入的模块。请重启 dsh。
3. 同一个 $DSH_HOME 上的两个 dsh 进程无法共享一个会话。
会话产物对每个会话是单写入者的:当第二个实例打开第一个实例持有的会话时,会以 SessionAlreadyOwnedError (gateway/internal) 失败。不同的会话确实可以并行运行——关于这会为记忆存储带来什么代价,请参见并发写入者。

配置

每个键都是可选的:

- insert:
- id: project-memory
name: dsh-project-memory
config:
maxRecallRecords: 3
maxRecallChars: 2600
perRecordChars: 900
minScore: 3
auditLimit: 50
leaseWaitMs: 15000
leaseStaleMs: 30000
storeDir: ''
debug: false

storeDir 默认为 $DSH_HOME/storages/dsh_project_memory——即 shell 自身的布局,写入租约和记录读取都依赖它。仅当存储后端的根目录在别处时才设置它。

存储

记录存放在 harness 自身的存储栈上——一个基于 JSON 后端的 dsh_project_memory 域,而不是一个并行的存储:

$DSH_HOME/storages/dsh_project_memory/records/.json
$DSH_HOME/storages/dsh_project_memory/index/.json

该域使用 per-record 布局,并带有 invalidRecords: 'backup-and-skip',因此单个损坏的文档绝不会让整个存储付之东流。每个文档都是一个
带版本的信封 { version, record }。
$DSH_HOME/storages/dsh_project_memory/.leases/.lock 就位于它们旁边:这是以独占方式创建的锁,用于在跨进程间串行化写入者(参见并发写入者)。它不是文档,因此逐记录加载器会忽略它。

测试

test.mjs 通过真实的 Cordis Loader、基于真实的组合文件挂载该插件,并使用真实的存储中枢、领域设施、工具注册表和系统提示注册表。只有存储介质是伪造的(memory-backend.mjs),因此不会触及 $DSH_HOME。

the dependency mirror inside the installed harness
export DSH_PLUGIN_DEPS="$DSH_HOME/profiles/node_modules"
node --import ./register-deps.mjs test.mjs # 78 checks
node --import ./register-deps.mjs regression-consistency.test.mjs # 27 checks
node --import ./register-deps.mjs lease.test.mjs # 9 checks, 3 processes
node --import ./register-deps.mjs loader.test.mjs # 6 checks
node --import ./register-deps.mjs regression.test.mjs # 10 checks
node --import ./register-deps.mjs regression-forget.test.mjs # 17 checks

Or run all six suites through the package entry point:
npm test

test.mjs 覆盖:无历史记录时的首次遭遇、写入(包括拒绝未经证实的 verified)、跨会话召回、失败计划禁止、更正与取代、项目隔离、对 40 多条记录的有界召回、审计过滤,以及在同一介质上重新加载。

regression.test.mjs 固定了两个曾经发布过且容易重新引入的缺陷:

- 一个长度正确但内容错误的索引(其 id 不匹配任何记录)会在打开时被修复——仅比较长度会使其保持原样;
- 没有替代项的 memory_correct(status: "corrected") 会被拒绝,因为召回会过滤掉被取代/失效的记录,而更正只有在有替代项时才有意义——没有替代项时,过时的文本会继续到达后续会话。

并发写入者

多个 dsh 进程可以共享一个 $DSH_HOME——它们不能共享同一个会话,但可以各自拥有不同的会话——而且每一个进程都会写入同一个项目存储。写入是被串行化的,而不是寄希望于不发生冲突。

在每次变更之前,插件会以独占创建(wx)方式获取 /.leases/.lock,因此文件系统只允许一个持有者。在该持有期间,它会重新读取记录文档,而不是信任自己的内存副本,递增该修订号,然后写入。锁文件会标明其持有者,因此竞争进程可以看到谁正在写入哪条记录,并且 memory_audit 会记录 write-conflict / write-bypass。

用三个操作系统进程测量(lease.test.mjs:两个写入者,对一条记录各进行 40 次更新,外加一个独立的验证者):

| | 80 次更新后的修订号 | 失败的写入 |
| 无租约 | 41——一个写入者的整条谱系,一半更新丢失 | 3 |
| 有租约 | 81——每次更新都得以保留 | 0 |
租约是尽力而为的协调,而非互斥锁,而这一区别正是全部关键所在:

- 一个按时完成的竞争者会被等待其结束。这是正常的争用,并被审计为 write-conflict(附带 waited_ms 和操作信息)。
- 只有当锁的年龄超过 leaseStaleMs 且其持有者的 pid 已不存在时,锁才会被窃取。仅凭陈旧本身永远不是证据,因此一个存活但缓慢的持有者不会因为年龄而被抢夺。
- 尽管如此,等待仍受 leaseWaitMs(默认 15 秒)限制。当预算耗尽时,写入者接管该文件——无论持有者是否存活——并继续执行,审计为 write-bypass。这是有意为之:因为另一个进程卡住而拒绝存储一个工程结论,后果会更糟。

因此,互斥仅在不超过 leaseWaitMs 的范围内成立;超过之后,两个写入者可能同时进入。降低预算会让这种情况立即出现——用 leaseWaitMs: 120 对 400 毫秒的持有进行探测,会测得 maxActive: 2 和一次 write-bypass——而在默认值下,则需要持有者卡住 15 秒。请将保证理解为:不会基于一个已被某个已完成的竞争写入所取代的快照来计算更新,而不是写入者之间互斥。

两条不变式防止协调本身变成故障:

- 无法获取的租约永远不会导致写入失败。 如果锁无法创建——只读存储、拒绝创建的沙箱——写入会在无协调的情况下继续,并且这种降级会被审计一次,记为 write-lease-unavailable。
- 只要介质能被完整读取,介质就是权威。 在可读存储中缺失的记录是已删除,而非陈旧;项目的索引会从介质重建,而不是与本进程的内存合并——正是那种合并把一条已删除的记录重新放回了索引。

锁是一个文件,而不是内核锁:一个在写入中途被终止的进程会把它遗留下来,这就是为什么年龄加上已死亡的 pid 才能证明窃取是正当的。来自介质的瞬时 EPERM/EBUSY——在 Windows 上,另一个进程只是在重命名期间保持某个记录文件打开——会被重试,而不是直接暴露出来。

lease.test.mjs 是跨进程测试套件:它在真实的 JSON 存储上启动两个竞争的写入者进程和一个验证者,并断言全部 80 次更新都得以保留、没有任何写入失败、每个租约都被释放,以及一个新进程能看到最终状态。它(修订版 41)在没有租约的构建上会失败,这正是该修复得到验证的方式。

状态

可用的 beta 版,适用于低并发的个人使用。 离线测试套件针对真实的 DSH 模块全部通过(78 + 27 + 9 + 6 + 10 + 17 = 147 项检查)。在一个实际项目中,完整链路已被观察到:产生一个结论,自主地记录它(提示中没有任何要求记住任何内容的指令),该记录落入 JSON 后端,之后某个会话的第一个提示已经携带了它,并且一个新会话会复用它。memory_correct 现已在一次实时记录也是如此——而正是那次运行发现了第八个缺陷:一次不带替换的更正把 replacement_id: undefined 写入了它的审计条目,并且由于工具结果必须是无损 JSON,测试框架随后在该进程的剩余时间里拒绝了每一次 memory_audit 调用。已在源头修复(?? null),并在工具边界再次修复;regression-consistency.test.mjs 的 R7–R10 固定了这两处。

随后一次独立审计针对 ba4778a 复现了七个一致性缺陷:一条被删除的记录重新出现以及索引中的幽灵 id、一个租约持有者删除了其继任者的锁、等待预算破坏了互斥、memory_correct 在没有 cwd 的情况下跳过了其所有权检查、memory_audit 读取了另一个项目、一个审计操作被其自身的载荷覆盖,以及一次召回预算超支。所有七个缺陷均已修复,每个都由 regression-consistency.test.mjs 固定,并且审计器自己的探针和七条断言针对修复未经修改地通过。

这不是什么:一个严格的跨进程一致性保证。协调在设计上是尽力而为的(参见并发写入者),并且 releaseLease 带有一个已知的、未能复现的 TOCTOU 风险(参见已知限制)。

将租约迁移到测试框架内部的 flock 附加组件——这将提供句柄持有的锁以及内核清理死掉的持有者——是刻意不做的。它是一个内部子路径加上一个原生模块,而且它仍然会让产品决策(对活着但卡住的持有者永远等待,还是接管)悬而未决。当以下任一情况成真时再重新考虑:并发写入确实被观察到丢失更新;此包开始承诺严格一致性;测试框架将 flock 发布为受支持的接口;或者它开始同时服务多个长期存活的进程。

已知限制

- 召回是词法层面的。 精确 token 命中加上字符 n-gram 回退——没有嵌入。一个改述过的问题可能无法匹配;memory_search 是逃生通道。
- n-gram 回退对短记录过于宽松。 在少量 n-gram 上的 Jaccard 很容易达到 0.12 的下限:两段简短、几乎不相关的文本共享一个 bigram 就可能得分 ≈ 0.2,越过下限,并且(如果满足 minScore)被召回。当这有影响时,降低 maxRecallRecords 或提高 minScore。
- 没有持久化的倒排索引——候选集在每次召回时从内存中的域表评分;每个项目的 id 索引是一种可重建的便利设施,在打开时从记录表修复。在数百条记录时没问题;在数万条时未经测量。
- 精度优先于召回。 minScore(默认 3)会静默丢弃弱候选;一条边缘记录可能在得分 ≈ 4 时错过注入。
- 审计追踪是内存中的——有界,重启后丢失。
- 第二个进程的内存视图在它重新打开之前保持陈旧。 租约使并发写入安全;读取仍然来自域所依据的表在打开时播种,因此另一个进程在会话中途添加的记录会列在索引中,但在此处解析不到任何内容,直到此进程重启。
- 租约释放不是原子的,这是一个真实风险,而非已证实的缺陷。 releaseLease 读取锁,比较其所有者令牌,然后才移除它。如果在读取和移除之间发生接管,就会删除继任者的锁并允许第三个写入者进入。没有任何探测复现过这种交错,也没有测试覆盖它,因此这里将其记录为已知风险;要消除它,需要一个拥有打开句柄(或 flock)的原语,而不是一个路径。互斥本身也是如此:它只在 leaseWaitMs 之前成立——参见并发写入者。
- 项目身份就是工作目录,因此从两个不同目录打开的同一个仓库(一个会话中是其根目录,另一个会话中是其子目录)会被视为两个项目。记录不会跨越这条界线。请将会话锚定在仓库根目录,否则从另一个目录看内存会显得空无一物。
- 一个工作目录就是一个池。 在同一根目录下开发的多个子系统——比如 job_agent_mvp/、_site/v3/,以及你在这里也打开的兄弟目录中的插件——共享一个记录集和一个召回排名;召回不会按路径或子系统将它们分开。记录的 related_files 携带路径,但在注入记录之前并不要求有重叠。这是一个已知边界,不是缺陷:拆分池会改变召回的排名方式,而这需要在设计之前进行真实样本比较。
- 记录依赖于模型遵循契约。 契约文本刻意具有规定性:在测试中,纯许可性的措辞产生了零次写入。

兼容性

已在 2026-09-20 针对 DeepSeek Harness 0.1.5-rc.2(Node 24)验证。该 harness 是开发者预览版,并明确表示会做出破坏兼容性的更改;此处的一次绿色运行是关于此构建的证据,而不是对下一个构建的承诺。该插件仅依赖 tools、storageDomain 和 systemPrompt 服务以及 dsh.bundle.patch,这是它所能使用的最窄接口面。

许可证

MIT——参见 LICENSE。
预览分享卡片长什么样 ↓yuyuyyyyyyyyyyyy/dsh-project-memory
分享 · DeepSeek Harness Hub
yuyuyyyyyyyyyyyy/dsh-project-memory

跨会话记住项目结论,规划前自动召回注入提示词

这是你的仓库?认领条目

回到顶部