Imported from Quophic/dsh-persona-memory (
AGENTS.md). Install upstream withnpx skills add Quophic/dsh-persona-memory. Copyright stays with the author.
AGENTS.md — dsh-persona-memory 开发约定
本文件给任何在此仓库工作的 agent(DSH / 莲 / Pi 等)在动代码前必读。
⚠️ 首要规范:宿主包必须 peerDependencies(违反会炸掉全部工具)
-
@deepseek-ai/*宿主包(dsh-tools、dsh-llm、dsh-home-paths等)只允许出现在peerDependencies,绝不写进dependencies。 -
为什么:
dsh plugin add(=pnpm)会把dependencies真装进<profile>/node_modules,与宿主 dsh 安装目录形成双物理副本;而dsh-tools的TOOL_RUNTIME_SCHEDULER是普通Symbol()(非Symbol.for(),见dsh-tools/lib/index.js:2409),双副本互不认 → 执行循环读不到调度器 → 所有工具调用崩Cannot read properties of undefined (reading 'prepare')(web profile 必现)。 -
为什么 peer 就够了:兜底目录
$DSH_HOME/profiles/node_modules(每次启动自动维护的 junction 集合)保证 peer 声明下解析自动回落宿主副本——不需要 junction、不需要加载期自检,peer 是唯一必需的声明。 -
安装后验证(在
<profile>目录下跑):node --input-type=module -e " import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); console.log(require.resolve('@deepseek-ai/dsh-tools'));" # 期望输出宿主路径(含 @deepseek-ai\dsh\node_modules\@deepseek-ai\dsh-tools) -
若安装后
<profile>/node_modules/@deepseek-ai/出现真副本(诱因:autoInstallPeers: true或误写进 dependencies)→ 直接删除副本即可,兜底会接管解析。
项目事实
- 用途:DSH 的持久化长期人格记忆插件——
memory(read/add/update/delete/rewrite)+memory_search工具,每请求把记忆注入 system prompt(order 55),带内容扫描(秘密/提示注入拦截)。 - 自动能力(
lib/learning.ts+lib/consolidate.ts+lib/llm-helper.ts+lib/correction.ts):后台学习(每learnIntervalTurns=10 轮用会话自身 LLM 路由复习近期对话并store.add事实)+ 纠正检测(feedback/record+ 对话内模式检测——强/弱/负向模式 + 指令词,仅source.kind==='user'直接人工消息,correctionRateLimitTurns=3 限频)→ 存[correction]失败条目 + 用量提示(usageNudgeThreshold=0.9)+ 自动合并(超限触发 LLM 合并,严格更小/可解析/过扫描才经replaceEntries提交,失败不损坏记忆)。 - FTS5 记忆镜像(
lib/fts.ts):<dir>/.memory-index.sqlite,动态 import node:sqlite(不可用则memory_search回退子串扫描),按文件 mtime 自动重建,literal-phrase 查询(FTS5 语法当数据处理)。 - 向量记忆镜像(
lib/vector-index.ts+lib/embedding.ts):vectorIndexDir(默认$DSH_HOME/memory)下.memory-vec.sqlite,只存 embedding 数据(entries 表:which/text_hash/text/embedding BLOB/created/last,UNIQUE(which,text_hash);meta 表 schema_version);指纹增量同步(sha256 每条原始 § 条目,只 embedding 变化项,Pi 改共享文件自动跟上);检索纯 JS 余弦(条目少,不装 sqlite-vec);provider 二选一:remote(OpenAI 兼容/embeddings,embeddingBaseUrl+embeddingApiKey或DSH_EMBEDDING_API_KEYenv;DeepSeek 官方 embeddings 接口状态不明勿依赖)或local(@xenova/transformers已进 dependencies,首次使用自动从 HuggingFace 下载模型到embeddingCacheDir(默认$DSH_HOME/models),embeddingRemoteHost可换镜像如https://hf-mirror.com,下载进度经 ctx.logger 记录);vectorEnabled默认关,开后在memory_search走 hybrid(FTS5 + 向量 RRF 融合),无向量则 FTS5/子串降级;注入成本与 FTS5 同级(仍 ≤searchMaxResults、只进工具输出)。 - 常驻指令(
lib/standing.ts+lib/standing-command.ts):STANDING.md 每行一条、硬预算(20 条/2000 字符)、始终注入(order 50,早于记忆块 55);只有/standing用户命令或直接编辑能写(模型无写入口,防注入)。 - 失败记忆(
lib/failures.ts):failures.md(hermes 文件名)§ 格式、结构化[category] 内容 — Failed: … — Corrected to: …、最近 7 天/5 条注入(order 52)、纠正检测落 failure 并去重;which: 'failure'字符上限=memory×2。 - 项目级记忆(
lib/projects.ts):projects-memory/<项目名>/MEMORY.md(Pi 兼容根:dir 为 hermes 数据目录时解析到 ~/.pi/agent/projects-memory);git 仓库根为项目身份(worktree 共享)+ 迁移桥;按会话 cwd 自动注入(order 53);project参数需过safeProjectName防路径穿越;项目合并用 projectCharLimit。 - 与 Pi 共享记忆:
MEMORY.md/USER.md磁盘格式与 pi-hermes-memory 完全兼容(§分隔 + 行尾<!-- created=, last= -->注释);dir默认自动指向~/.pi/agent/pi-hermes-memory(存在时),否则$DSH_HOME/memory。 - WebUI 记忆管理页(
lib/admin.ts+client/src/main.ts,0.2.0):Host 用webServer注册/api/persona-memory/*路由(status/checkModel/configSave/update/delete/standingAdd/standingUpdate/standingRemove/rebuildVector/projectUpdate/projectDelete/backup/restoreLatest,loopback-only 防护,同 dsh-ssh 围栏);Client 源码在client/src/main.ts(TS,纯 React.createElement),由scripts/build-client.mjs(esbuild,external react)构建成预构建 bundleclient/client.js(window.__ModuleLoader__.load格式,经dsh.client+exports["./client"]声明加载,非动态插件无 harness.handle,用同源 fetch 调路由);页面注册到settings.sectionslot(id=persona-memory,order=25,label=记忆管理);卡片=常驻指令/记忆文件/项目记忆/检索索引/本地备份/插件配置;检索索引卡=纯状态(FTS5/向量大小、重建按钮,不含任何配置项——向量配置统一在插件配置卡,与 v27 定稿一致);插件配置卡按CFG_SCHEMA(40 项,分组:基础/学习/合并/检索/向量/项目/常驻/失败/管理/备份)schema 驱动渲染(bool→checkbox、number→数字框、select→按钮组、string→输入框),embeddingModel 特殊渲染:输入框+缓存检测+已下载模型按钮组(点击选用),保存写回当前 profile 的cordis.patch.yml中 persona-memory 段(adminProfile配置项指定 profile 名,默认 web);本地备份卡:备份目录$DSH_HOME/memory-backup/latest/(与 Pi 完全隔离、只保留最新快照,8 文件=MEMORY/USER/failures/STANDING + projects-*.md),手动「立即备份/从最新备份恢复」;自动保护(lib/admin.tsstartAutoProtect,index.ts 启动,ctx.timer.interval 每分钟 tick):按autoBackupMin(默认 60,0=关)间隔覆盖备份 + Pi 丢失检测(autoSwitchOnPiLoss,Pi 曾见 MEMORY.md 后消失 → patch 写dir: $DSH_HOME/memory+ restoreFromLatest);Client 源码修改后必须npm run build:client重新构建 + 重装 + 重启 web 才生效(预构建产物按包分发);client 只依赖 react + 浏览器 fetch,不 import 任何 @deepseek-ai client 包运行时值(peerDependencies 已声明 react、dsh-client-runtime、dsh-client-ui-slots、dsh-host-webserver 等)。 - 结构:
index.ts(插件契约,tsc 编译到dist/index.js)/cordis.patch.yml(bundle patch,经"dsh": {"bundle": {"patch": ...}}声明,安装后 dsh 自动激活为 profile 层)/lib/*.ts(memory-store、secret-scanner、memory-tool、memory-search-tool、prompt、learning、llm-helper、consolidate、failures、projects、correction、fts、vector-index、embedding、standing、standing-command、admin)/client/src/main.ts(WebUI 管理页源码,esbuild 构建到client/client.js)/test/smoke.mjs;发布产物 =dist/(tsc 编译)+client/client.js(esbuild 打包),均 gitignore,安装/发布前先npm run build。 - 测试:
npm run build后node test\smoke.mjs(111 项,importdist/编译产物,用真实 hermes 文件副本验证;搜索测试自包含,不依赖真实文件内容——在线插件可能已合并改写)。测试需在 workspace 内建 junctionnode_modules\@deepseek-ai→ 宿主 dsh 副本(files白名单不含 node_modules,不影响安装)。含工具层测试(memory-tool execute 直连项目 store)、并发写测试与向量索引测试(假 embedding provider 验证增量同步/余弦/RRF)。 - 写入并发安全:
lib/memory-store.ts的 add/update/remove 走单锁原子mutate(一次 withLock 内完成读-改-写,{ next?, value }返回协议),杜绝并发工具调用互相覆盖;写前文件指纹预检(sha256,对照 Pi 的 ExternalMemoryWriteConflict)——若外部(Pi/手动编辑)在我们读后改写了文件,不覆盖、重试一次、仍冲突则返回conflict: true。只读(read/search/listRaw)和纯写(rewrite/replaceEntries)走各自锁。 - 与 Pi 字节兼容(对齐 v0.9.4 实测):
\n§\n分隔、<!-- created=, last=, project64= -->元数据、无尾随换行(hermes saveToDisk 不加\n)、charCount=entries.join(delimiter).length(含分隔符)、USER 上限默认 5000、容量超限拒绝写入(overflow: true,tool 层可先合并后重试)、failure 去重按(text, project)、注入包<memory-context>围栏。扫描器/STANDING/项目路径与 Pi 逐项一致。 - 安装:先
npm run build再dsh plugin --profile <名> add file:<本仓库路径>(或 npm 包名)——bundle 自动激活,无需手动挂载;覆盖配置在 profile 的 cordis.patch.yml 按 id 覆盖。file: 安装是真实副本,改代码后必须重新 build + 重装并重启 web 才生效。
迭代与上线 SOP(固定流程,每次照做)
阶段一:UI/逻辑快速迭代期(免重启)——用动态 Cordis 插件(cordis_define kind:new 一次 + kind:existing 追加迭代),双勾授权后 update 免批准,改完秒生效。定稿后才走阶段二。
阶段二:固化进 bundle(正式发布,需要重启 web)
- ⚠️ 先取动态插件最终版源码作为临时对照物(最重要,防止固化缺功能):迭代定稿后、固化前,用
cordis_inspect_self(pluginId, packageId)取最终 Package 的完整 host+client 源码,临时保存到dev/<功能名>-final.js(仅本次固化对照用,含每张卡/路由区块的标注注释)。动态插件只在运行进程内存里,undefine 后即永久丢失——对照物是固化期间唯一依据。- 对照物用完即删:固化完成、逐卡验证一致后,删除
dev/临时文件并git rm。不要长期保留中间版本存档——固化后 bundle 已在 git 有完整历史,git 才是唯一真源;过时存档会误导后续固化(历史教训:v10 三卡存档让固化把「配置移到插件配置卡」的 v27 定稿做反了)。
- 对照物用完即删:固化完成、逐卡验证一致后,删除
- 改代码:
index.ts(插件契约)/lib/*.ts(Host,Node 原生 API)/client/src/main.ts(Client 源码)——改完先npm run build(tsc →dist/+ esbuild →client/client.js)。- Client UI 渲染层逐字复制:React 元素/卡片/交互逻辑与动态版完全相同,只替换 api 调用层(动态
host.call→ bundlefetch('/api/persona-memory/...'))和包壳(动态 factory →window.__ModuleLoader__.load)。 - Host 逻辑层必须改写:动态在受限沙箱(
ctx.get('fs')服务、无 node:fs/process),bundle 有完整 node:fs;动态harness.handle→ bundle webServer 路由。 - 固化后逐卡片对照存档源码:每张卡、每个按钮、每个配置项逐一对,不许凭记忆重写、不许删减 UI 区块。
- Client UI 渲染层逐字复制:React 元素/卡片/交互逻辑与动态版完全相同,只替换 api 调用层(动态
- 升版本:
package.jsonversion 递增(每次上线必须升,否则 pnpm store 判定"无变化"可能不刷新副本)。 - 同步文档:AGENTS.md 功能描述 + README.md 功能表/配置表。
- 验证:
npm run typecheck(host + client 双 tsconfig 零错误)+node test\smoke.mjs(111 项,exit=0)+node --checkdist 全部产物 + 模块级端到端测试(备份/恢复/patch 写回用临时目录,测试内严禁删真实数据目录)。 - 重装(⚠️ 必做,pnpm 有 store 缓存):
验证:dsh plugin --profile web remove dsh-persona-memory # 先 remove dsh plugin --profile web add file:E:\GitHub\lian_dsh # 再 add,强制拿新副本<profile>/node_modules/dsh-persona-memory/package.jsonversion 与仓库一致、dist/lib/admin.js字节数与仓库一致。 - 重启 web:杀
dsh web进程(Get-CimInstance Win32_Process -Filter "Name='node.exe'" | ? CommandLine -match 'dsh')→ 同参数重启 → 刷新 GUI。 - 验证页面:设置页「记忆管理」出现六张卡(逐卡核对功能,重点比对存档源码的 UI 区块是否齐全);动态插件随进程消失无需手动 undefine。
- git 提交(无 dev 分支,直接在 main 上):
git add -A && git commit(message 带版本号)→git push origin main同步远程。
git 工作流(固定,无 dev 分支)
- 永远只在
main分支工作——已删除 dev 分支(历史教训:dev 分支与 dev/ 中间存档一样只会误导)。开发、固化、提交全部直接在 main。 - 每次固化完成后:
git add -A && git commit(带版本号)→git push origin main。仓库任何时候git status应为 clean。
常见坑
- pnpm
add file:复用 store 缓存 → 装到旧版本/旧字节(现象:version 没变、dist/lib/admin.js长度不对)→ 先 remove 再 add,别只 add。 - 改 client/src 后必须
npm run build:client重新构建 + 重装 + 重启 web 才生效(预构建产物按包分发);bundle 无 harness.handle,Client↔Host 走 webServer 路由 + fetch。 - 备份目录
$DSH_HOME/memory-backup/latest/是真实数据,测试/清理时先确认,删了要立即backupAll重建。