dsh-anima

DSH PLUGIN记忆与知识

仓库 Melody-cx/dsh-anima

记忆与知识 L5 · 运行验证通过 安全审查:低风险 可信度:金 可安装插件
安装这个插件

DSH 助手人格与长期记忆插件:独立可用,不依赖其它插件。管 AI 自身的名字与角色、对用户的称呼、回复风格与自定义指令;人设支持全局 + 按工作区两级覆盖;长期记忆走三段式(候选捕获 → 待审缓冲区 → 用户裁决才生效),并支持一键导入其它 AI 的历史记忆。

一句话选型结论

按资料字段整理

dsh-anima 是记忆与知识类的 DSH 可安装插件,验证等级 L5 · 运行验证通过,安全审查为低风险,有可直接复制的安装命令。验证通过不等于安全:插件会读写文件、执行命令或联网,装前先到仓库确认它做了什么;涉及密钥或系统权限的,建议先在隔离环境试装。

分类记忆与知识 收录类型可安装插件 验证等级L5 · 运行验证通过 安全审查低风险
验证等级 L5 L5 · 运行验证通过
GitHub 星标 2 复刻 0 · 未关闭 issue 0
最近更新 2026-10-22 创建于 2026-10-14
README 31.2 KB 正文体量,越大说明说明书写得越细

安装

可安装插件
在 DSH 里执行,装完用 dsh plugin list 确认已挂上;标了 L4 / L5 的命令已在沙盒里真实跑通
dsh plugin --profile web add github:Melody-cx/dsh-anima#v1.0.0
  1. 1
    确认版本与运行环境

    仓库没有标注兼容版本与 Node.js 要求,先确认你的 DSH 版本能装,再继续。

  2. 2
    执行安装命令

    在 DSH 里执行上面的命令;多包仓库要注意区分具体子包名,别装错包。

  3. 3
    确认已挂载

    用 dsh plugin list 查看列表里是否出现这个插件,出现即挂载成功。

  4. 4
    试跑并检查权限

    加载后跑一个最小用例,确认它能正常工作;同时看它申请了哪些文件、命令或网络权限。

兼容 DSH 版本未标注版本不匹配时安装会报错
Node.js 要求未标注来自仓库 package.json 的 engines 字段
前置依赖无需要同时安装的同级依赖
npm 包名未发布到 npm可用 npm view 查看包信息

能做什么

记忆与知识
字段来自项目仓库与公开索引,中文说明由本站标注

适用场景

记忆

上游标签

deepseek-harnessdshdsh-pluginmemorypersona

验证等级与安全审查

L5 · 运行验证通过
自动化流程结论,不是人工审核
验证等级L5 · 运行验证通过在 dsh 沙盒中完成安装与运行冒烟——装得上、跑得起来
安全审查低风险静态分析结论,未标注不代表已通过审查
仓库健康度活跃按仓库活跃度与最近更新时间给出
可信度金综合仓库活跃度与验证结果

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 验证等级 L5:安装和运行都在沙盒里跑通过,装完能直接用的概率高。
  • 静态安全审查为低风险,没有发现明显可疑行为。
  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有声明 Node.js 版本要求,版本不匹配可能装不上。
  • 验证通过不等于安全:插件会读写文件、执行命令或联网,装前先到仓库确认它做了什么。

更适合

  • 需要在记忆与知识场景里给 DSH 补能力的人。
  • 想装完就能用、不想自己排依赖的人。
  • 能接受在本地装依赖、愿意自己确认权限的人。

不太适合

  • 只使用 DSH 官方内置能力、不接受第三方插件的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
插件验证等级安全审查星标兼容版本安装命令
dsh-anima当前 L5 · 运行验证通过 低风险 2 未标注 有
ruflo L3 · 安装规范通过 低风险 7.4 万 未标注 无
OpenViking L3 · 安装规范通过 低风险 3.9 万 未标注 无
WeKnora L4 · 沙盒安装通过 低风险 3.3 万 未标注 有
distilly L4 · 沙盒安装通过 低风险 2.5 万 未标注 有
EverOS L4 · 沙盒安装通过 低风险 1.3 万 未标注 有

仓库档案

记忆与知识
字段来自项目仓库与公开索引,中文说明由本站标注
作者 / 维护方Melody-cx仓库 dsh-anima · 默认分支 main
分类记忆与知识memory
收录类型可安装插件声明了 dsh.bundle、可用 dsh plugin add 安装的条目
开发语言未标注仓库主语言
许可证MIT商用或二次分发前请确认条款原文
星标 / 复刻 / issue2 / 0 / 0收录时的数值,非实时
创建 / 更新2026-10-14 · 2026-10-22最后推送 2026-10-14
本站收录2026-10-30首次进入本站目录的时间

README 全文

31.2 KB
作者原文,中文版优先;只做安全转义,不改内容

dsh-anima

DSH 的助手人格与外挂记忆层——一个独立插件,装上即可用。

它管的是「你自己(助手)是谁」:AI 的名字与角色、你对它的称呼、回复风格、
以及你自己写的行为准则;再加一套带待审缓冲区的长期记忆,
以及输入框光轨、输入框快捷开关、工具确认门槛这些界面能力。
字段集不依赖任何其它插件,也不从别处读取配置。

> 工具名为什么是 set_assistant_persona:tools.register() 遇同层重名直接抛错
> (dsh-tools/lib/index.js:2634),与生态里任何同名工具共存时必有一方注册失败、
> 被 .catch 吞成一行 warn —— 那是发布级的静默故障。功能相似不要求撞名。


它解决什么

能力 说明
AI 自己的名字与角色 agentName / agentRole —— 决定它怎么称呼自己、以什么身份说话
用户的称呼 / 背景 / 语气 / 长度 / 语言 addressUserAs / userOccupation / userBio / style / replyLength / language
按工作区两层覆盖 只覆盖声明的字段,其余继承全局;覆盖文件随目录走、可 git
长期记忆 自动沉淀 → 待审缓冲区 → 用户裁决 → 注入
输入框光轨 / 快捷开关 / 工具确认 界面与安全开关
人设预设 6 条内置 + 自建;也可把当前配置存成预设
十二个字段全是自由文本 存的就是注入的原话,另有「一键填入 + 示例」按钮组
记忆随工作区分层 全局记忆处处生效,工作区记忆只在匹配时注入
注入可解释 / 字符预算 每条为什么进 / 不进,条数与字符双约束
本地聚类与冲突提示 纯本地 bigram 相似度,只提示不自动合并
人设变更审计日志 谁在什么时候把哪个字段改成什么
一键导入外部 AI 的记忆 复制提示词 → 粘贴结果 → 机械切分 → 逐条裁决层级
长期记忆的「自动写入」 ❌ 刻意不做(见下)

关于与 dsh-soul 的关系

上表左侧的能力划分与 dsh-soul 0.7.0 的字段集有交集,实现上也刻意与它保持一致
(工具确认语义、光轨动效参数、重置保留预设)。但本插件不依赖它:
字段集完整自持,不读它的配置文件,两者可同时安装且标识全部错开。
这层关系的完整说明见下方「致谢与差异化声明」。

四个差异化设计

功能对齐不等于没有自己的判断。以下四点是本插件相对主流记忆插件的真实增量,
每一条都对应一个具体的失败模式。

1. 双约束注入:条数与字符同时生效

多数实现只有一个「最多注入 N 条」的上限。问题是 40 条长记忆一样能挤爆上下文,
而用户看到的是「我只设了 40 条啊」。

本插件的 selectForInjection 同时受 memory.maxInjected(条数,默认 40)与
memory.maxInjectedChars(字符,默认 4000)约束,取先到者。两条上限各有各的适用场景:
条数管「别让模型看太多碎片」,字符管「别让一次注入吃掉半屏上下文」。

2. 注入可解释:看得见每条为什么进 / 不进

注入不是黑箱。设置页的「注入情况」列出每一条已采纳记忆的去留与原因:

原因码 含义
included 已注入提示词
other-workspace 属于别的工作区,不在本会话注入
no-workspace-context 当前会话没有工作区信息,无法匹配工作区记忆
workspace-scope-unknown 标记为工作区记忆但没记录属于哪个工作区
over-max 已采纳条数超过注入上限,按时间取最新的
over-budget 单条就超出字符预算,取不下
dropped-by-budget 前面几条已占满字符预算
no-injection 记忆层已关闭或上限为 0

区分 over-budget 与 dropped-by-budget 是刻意的:前者要删那条,后者要腾位置 ——
报同一个码,用户不知道该动谁。原因文案由宿主下发(INJECTION_REASON_TEXT 只有一份),
客户端不重写翻译。

3. 本地聚类:只提示,不自动合并

待审缓冲区里若有多条明显在说同一件事,插件用字符二元组(bigram)的 Jaccard 系数
(纯本地、零依赖、零模型调用)算出相似度,把「这 3 条像在说同一件事」摆到用户面前。

刻意不做自动合并:合并意味着生成一段用户没说过的话。本插件的立场是
「判断什么值得记交给模型,什么真的生效交给用户」——自动合并恰好违背它。
聚类只让裁决更快,不替他裁决。同理「候选撞上已采纳记忆」只提示冲突,不改任何数据。

4. 人设变更审计日志

本插件有三个入口能改人设:设置页、/anima 命令、模型调用的 set_assistant_persona 工具。
而「工具确认」默认是关的 —— 也就是说,模型可以直接把你叫它什么、它用什么语气回话改掉,
而你无从知道是谁在什么时候干的。

审计日志只记「改了什么」,不做判断、不参与提示词。它挂在 StateStore.updateConfig 这一个
收口上,而不是在各调用点分别埋点:所有写入路径最终都走这里,分散埋点迟早会漏掉一条,
而漏掉的那条恰恰可能是模型的静默改写。resetConfig 也走审计 —— 否则
「昨天还好好的,今天全空了」在日志里查无此事。

记录上限 200 条,独立成表(不塞进 config):config 每次启动整条读入内存,
审计会涨到几百条,混在一起等于让启动开销随使用时长上升。
长值在写入时截断为 200 字并附「共 N 字」,数组/对象只记规模 —— 审计是索引,不是备份。

看到它的地方有两处:设置页「变更记录」卡(完整列表,可按条清空),
以及 /anima show 的「最近变更」一行——只显示最新那条,格式是
[MM-DD HH:mm] <来源>:<字段名>,刻意不带具体值:值可能很私人,而 show
的输出会被复制到对话里问人。

致谢与差异化声明

本插件的功能集参考了 dsh-soul(MIT)与
dsh-checkpoint-rewind 的设计,并在若干处刻意与它们保持行为一致(存储惰性解析、
失败关闭的确认语义、重置保留用户预设、光轨动效参数)。

需要明确的是:

  • 代码全部独立实现,未拷贝上述项目的源码、注释、提示词文案或图形资源。
  • 两处动效参数刻意取同值(8 层错位描边、虚线段长 12 / 步进 2.2 / 圆角 22 等),
    理由是避免同一台机器上出现两套观感。这类数值属于思想与参数,不受著作权保护;
    记在这里是为了不让读者误以为拷贝。
  • 导航图标刻意画得不同形:dsh-soul 用「光轨弧 + 火花」,本插件用「人像(头+肩)+ 火花」,
    单色 currentColor,viewBox 也另行收紧。
  • 各类标识全部错开,避免共存时互相顶替或注册失败:
类别 dsh-soul 本插件
工具名 set_persona set_assistant_persona
设置页 id soul-settings anima-settings
光轨槽位 id soul-trail anima-trail
开关槽位 id soul-quick-toggle anima-quick-toggle
提示词 section soul:persona anima:identity / :memory / :pending
存储域 —(自有文件) anima
注入来源 kind plugin:dsh-soul plugin:dsh-anima

工具名错开不只是礼貌:dsh-tools 的 register() 遇同层重名直接抛错
(lib/index.js 的 NamedEntries 冲突工厂),两者共存时必有一方注册失败、
并被 .catch 吞成一行 warn —— 那是发布级的静默故障。

核心设计:三段式记忆

这是本插件与市面上 15+ 记忆插件最不一样的地方。

对话中通过 remember 工具投递
↓
待审缓冲区(不生效)
↓  每晚自动整理:去重 / 淘汰 / 与已采纳项比对
↓
通知用户「有 N 条待你裁决」
↓
用户在设置页逐个「采纳 / 丢弃 / 删除」
↓
已采纳 → 注入提示词

为什么不做静默自动写入:记忆一旦静默写入就直接影响后续所有回答,写错了用户很难发现。
本插件把「判断什么值得记」交给模型,把「什么真的生效」交给用户。

整理这一步纯本地、不调用模型——只做去重、淘汰、与已采纳项比对,不做语义归纳。

记忆分两层:global 处处生效,workspace 只在 cwd 归一化后完全相等时注入。
不做「子目录继承」——工作区覆盖是精确匹配语义,记忆若采用继承语义,
用户在子目录里会看到父目录的记忆,排查起来极难。
分层之外的注入解释、预算与聚类见上文「四个差异化设计」。

凭据闸门

任何命中凭据形态的内容在进入缓冲区之前就被拒收(不落盘、不留痕),
覆盖私钥块、OpenAI/AWS/GitHub 令牌、通用 api_key / secret / 密码赋值、Bearer、
URL 内嵌凭据等 11 类模式。

理由:内容一旦落盘缓冲区,就处于「等待进入提示词」的状态,凭据不该走这条路。
凭据判定用的是 11 类正则的机械匹配,没有语义理解,会有误判——所以被拦下时界面会
把命中规则名一起说出来,用户可以删掉那串字符再粘贴。

一键导入外部 AI 的记忆

设置页记忆卡里有一个「导入外部 AI 的记忆」按钮,展开后是两步:

  1. 复制提示词——把一段整理画像的提示词发给你用得最多的那个 AI,让它把你的偏好
    整理成清单。按钮只在真的写进剪贴板后才显示「已复制」;没有剪贴板 API 时会明说
    「复制失败,请手动选中后复制」,不假装成功。
  2. 粘贴结果——粘贴框接受 AI 的输出,也接受你自己手写的笔记(不要求必须是 AI 生成的)。

解析后逐条预览,每条都能改层级(全局 / 本工作区)或直接丢弃,确认后才写进待审缓冲区
——和手动添加、模型投递走同一条路,仍然要用户逐条裁决才生效。

切分是机械的,不做语义理解:按 Markdown 标题、列表项、空行三种边界切;刻意不按句号切,
因为中文画像里「偏好:结论先行。」被切成两条会读不通。小节标签(如「我的使用画像」)
不进候选——它长得像记忆但没有可采纳内容。

归类是建议不是判决:suggestLayer 只看措辞——提到具体磁盘路径或「本项目 / 这个项目」
建议 workspace,提到「所有项目 / 一律 / 每次都要」建议 global。两条都没有时默认 global,
理由是把「记忆动不动就不生效」当默认体验,比跨项目污染更糟。每条建议都带一句理由显示在
条目下方,用户看到「本工作区」就能立刻知道凭什么。

解析阶段就分流:一条原文切完后按顺序过五道闸,没通过的会被分别归到不同的桶里,
每一桶都在界面上显示出来,不静默少一条:

处置 判定 界面上
过短(< 4 信息量) 切出的碎片太短,通常是编号残留 只在汇总里报个数
超长(> 500 字) 单条超过记忆上限 不截断,明确告知「请拆成几条」
凭据 命中 11 类正则之一 整条拦下并说明命中了哪条规则
重复 与已有记忆文本相同 单独列出
超条数 超出单次上限(默认 60 条) 余下部分不进这一批,另开一次导入
同义 与待审缓冲区里的某条相似度 ≥ 0.6 在条目下方提示「已有一条很像」,不自动合并

原文上限 40000 字符,超出直接报错而不是截断(截断等于偷偷丢掉用户画像的后半段)。
同义提示刻意只提示不合并:自动合并会生成一段用户从没说过的话。


安装

从 GitHub 安装(推荐)

本插件尚未发布到 npm,从仓库直接装:

dsh plugin add github:Melody-cx/dsh-anima#v1.0.0

或者手工改 profile 的 package.json:

{
"dependencies": {
"dsh-anima": "github:Melody-cx/dsh-anima#v1.0.0"
},
"dsh": { "profile": { "bundles": [ /* ... */ "dsh-anima" ] } }
}

#v1.0.0 是标签(也可以写 commit SHA,或整个省略以跟随 main)。建议 pin 住一个版本,
否则上游的任何改动都会在下次安装时直接进入你的 profile。git 源下载的是整个仓库 tarball
(本仓库很小),首次安装可能比 npm 源慢一些,超时的话重试一次即可。

然后 pnpm install,重启 DSH。宿主侧改动一律需要重启才生效。

从源码安装(开发用)

在 profile 的 package.json 里加依赖(也可以用 /plugins 界面):

{
"dependencies": {
"dsh-anima": "file:<仓库目录的绝对路径>"
},
"dsh": { "profile": { "bundles": [ /* ... */ "dsh-anima" ] } }
}

本地 file: 依赖是复制语义:改完源码要重新安装才生效,且重跑安装时若 pnpm 判定
changed:false 会直接跳过 —— 正确姿势是先 remove_bundle 再 install_bundle
(或比对 node_modules 副本与源码的 SHA256)。

运行前提(package.json 里已声明,装不上时先看这几条):

  • engines.node = ^22.19.0 || >=24.0.0,engines.dsh = >=0.1.6-0 <0.2.0 || >=0.2.0-0 <0.3.0-0
  • peerDependencies 四包:@deepseek-ai/cordis(^4.0.1 || ^4.0.5-alpha.1)、
    @deepseek-ai/dsh-llm、@deepseek-ai/dsh-storage-domain、@deepseek-ai/dsh-tools
    —— 它们由宿主提供,插件不打包它们
  • dependencies.zod(^4.4.3)是唯一真正要装的运行时依赖
  • 存储栈需在 profile 里组合:@deepseek-ai/dsh-storage + @deepseek-ai/dsh-storage-json(配 root)
  • @deepseek-ai/dsh-storage-domain(配 backend: json)。缺了不会崩,但插件会退化成
    「存储不可用」,设置页会明说原因(见下「存储」一节)

配置

设置页新增一栏「灵台」(settings.section id = anima-settings,order 51)。

  • 助手身份:enabled / agentName / agentRole / addressUserAs / userOccupation / userBio
  • 回复风格:style / headingLists / emoji / tables / replyLength / language / customInstructions
  • 工作区覆盖:workspaceOverridesEnabled / workspaceFileDir
    覆盖文件默认写在 <工作区>/.dsh-anima/override.json(目录名可用 workspaceFileDir 改),随目录走、可 git、可拷给别人;
    该目录会自动写入 .gitignore(内容 *),避免私人语气设定被推到远程。
  • 输入框光轨:trailEnabled / trailColor(#RRGGBB,落盘统一大写)/ trailSpeed(slow|normal|fast)/ trailWidth(thin|normal|thick)
  • 工具确认:requireToolConfirmation —— 开启后模型调 set_assistant_persona 只提交提议,不写配置
  • 长期记忆:memory.enabled / autoCapture / nightlyTriage / nightlyHour / maxPending /
    maxInjected / maxInjectedChars / notifyOnSessionStart / defaultLayer

不做任何迁移

本插件不读任何其它插件或自己旧版本的配置。它是 1.0.0,此前没有发布过任何版本 ——
没有「老用户的介质」这种东西需要兼容,所以代码里既没有一次性迁移,也没有存储格式回退。
这两块曾存在于开发期,都已删除;留着它们的唯一作用是让后人以为有一批需要照顾的存量用户。

将来真要升存储格式:只改 lib/store.mjs 的 DOMAIN_VERSION,并先想清楚老用户的介质怎么办 ——
那个决定不该在发布之后临时补,因为补的时候介质已经在用户机器上了。范式可参考同生态的
dsh-checkpoint-rewind(双 spec 串行回退)。

十二个字段都是自由文本

style / headingLists / emoji / tables / replyLength / language 六个字段不再是下拉框,
存的就是要注入提示词的那句话本身。改之前它们是枚举(style: 'roast'),改之后
style: '毒舌吐槽;直白指出问题、不留情面,但始终对事不对人。' —— 你想怎么写就怎么写,
只要不超过字段上限(agentName / addressUserAs / userOccupation 各 50 字,userBio 500 字,
agentRole / style 各 800 字,headingLists / emoji / tables / replyLength / language 各 300 字,
customInstructions 2000 字 —— 逐字段取值见 lib/config.mjs 的 FIELD_LIMITS)。

留空是合法状态,空串在提示词里等于「不提这件事」:模型按自己判断来,而不是被一句
「请按默认方式处理标题」占用注意力。这也是三项特质的 default 档对应的实现 ——
它不是一个值为 'default' 的枚举,而是空串。

空输入框会劝退人,所以每个字段下方都有一排按钮:

按钮组 来源 点下去
一键填入 lib/wording.mjs 的 WORDING_TABLES 填入一句定稿文案(原档位那句话),填完可以继续改
示例 lib/wording.mjs 的 FIELD_EXAMPLES 填入一段明显属于别人的第一人称,等你改成自己的

两组都随 GET /api/anima/state 下发给设置页,客户端不自己抄一份文案表 ——
否则改了 host 忘了客户端,界面上就会出现两套说法,而真正注入的是 host 那份。

agentName 刻意不给示例:按钮填一个名字进去用户很可能忘改,结果 AI 一直用别人起的名;
这个字段改由 placeholder 提示格式。default 档也刻意不生成按钮 —— 空文案做成按钮,
会让人以为点一下能得到什么,其实只是清空输入框。

兼容旧配置:升级不会等于「偷偷把用户的风格改回默认」。migrateConfig 会把旧版存的档位键
('professional' / 'more' / 'default' …)自动翻译成对应文案,注入提示词的内容一字不变;
用户自己写的句子不命中任何档位键,原样保留。

requireToolConfirmation 与四个 trail* 字段刻意不参与工作区覆盖:一个是安全开关,
一组是界面偏好(而且必须是封闭集合 —— 它们是给 CSS 用的键),都不该因为「换个工作区」而悄悄换掉。

maxInjectedChars(默认 4000,单位字符)与 maxInjected(默认 40 条)取先到者:
只有条数约束时,40 条长记忆一样能挤爆上下文。两者的 0 含义不一样,别记混:

字段 0 的含义
maxInjectedChars 不限字符数(只有条数约束生效)
maxInjected 一条都不注入 —— 它同时关闭所有记忆注入;要停记忆请用 memory.enabled
defaultLayer 决定新记忆记在 global 还是 workspace,默认 global 是刻意的 ——
把「记忆动不动就少一条」当默认体验,比跨工作区污染更难排查。

其余 12 个字段可被工作区覆盖,且覆盖是字段级的 —— 工作区只声明它在意的字段,其余继承全局。

配置落盘:只用 storageDomain(域 anima —— 注意是下划线,宿主的域名校验只接受 ^[a-z][a-z0-9_]*$,连字符会被拒)。

惰性解析(与 dsh-checkpoint-rewind 的做法一致):不在 apply 阶段解析服务,首次真正使用时才 ctx.get('storageDomain')。
dsh-storage-domain 的 apply 体是 ctx.inject(backendServices, …) + 回调里 provide('storageDomain', …),
而 backendServices 是 storage.backend.json(由 storage-json 提供)。本插件若比 storage-json 先装载,
apply 阶段那一次 ctx.get() 必然拿到 undefined —— 这正是 2026-10-10 冷启动时存储后端无声退化的真因:
旧版判空后退化到文件后端,还把结果永久记忆化,于是用户的配置留在旧文件里读不出来。

现在的契约:服务缺失 ⇒ 抛 StorageUnavailableError('storage-not-composed'),绝不换落盘点;
失败不记忆化(服务后来就绪时下一次调用就能成);成功才缓存域句柄。
index.mjs 的 apply 首次 reload() 撞上它时,挂一个 cordis 原生就绪信号
ctx.inject(['storageDomain'], …) 等它 provide 后重试(依赖未就绪时 fiber 保持 PENDING,服务 provide
那一刻回调触发,本仓用真 cordis 探针实证过),期间三段 section 返回空串 —— 宁可不注入,也不冒充默认档。

不做旧数据救援:storageDomain 何时可用取决于装载顺序,同一个人可能早期把配置写进过别的落点。
但本插件是 1.0.0,没有「早期版本的用户」—— 所以打开域时不做任何导入或归档,只打开自己的域。
若将来真需要迁移,唯一安全的形状是:仅在新域 config 记录不存在时动手,且自己的任何失败都只记 warn,
绝不外抛(这段代码在打开域的 try 块里,外抛会被包成新域的 open-failed,让一个本该正常的插件停摆)。

不可用原因可查:GET /api/anima/state 的 source 段带 reason(错误码)与 message(原始报错)
两个字段,取值 storage-not-composed / vocabulary-unavailable / open-failed: <原始报错>;
正常走域时两者均为 null,且 kind === 'storage'。设置页把它翻成人话显示在「存储位置」下方。
存储不可用时该路由返回 503 + code,而不是 500 空壳 —— 设置页要靠它区分「插件没跑」与
「跑了但存储栈没组合」:前者该重装,后者补三行 storage / storage-json / storage-domain 即可。

单一格式戳,无回退:storage-json 的 single 布局在打开时硬比对版本戳 ——
unit.version !== descriptor.version 直接抛 version-mismatch,没有任何自动迁移。本插件的域版本
恒为 1(lib/store.mjs 的 DOMAIN_VERSION),只声明一个 spec,不做串行回退:1.0.0 之前没有发布过,
不存在「老用户的 v2/v3 介质」这种东西需要读。

开发期曾有过兼容回退(按 [v3, v2, v1] 串行 open(),只对 version-mismatch 继续试),随未发布的
v2/v3 格式一起删除。那段代码的教训保留在这里,因为将来升格式时用得上:compatibleVersions 声明了
等于没写
—— 它只被 per-record 布局的 acceptedStamps() 读,single 布局的 parse() 根本不看。
真要做多版本兼容,只能像 dsh-checkpoint-rewind 那样声明双 spec(主 spec + 容错 spec),
且兼容 spec 必须声明全部四张表 —— DomainImpl.table() 对未声明表直接抛错,少一张就会在下一次
table('audit') 时炸。

两套编号,别混:插件自己的版本是 package.json 的 semver(1.0.0,设置页右上角徽标那个),
存储格式戳是个位数(当前是 1),两者互不相干。界面上凡出现格式戳的地方一律写「存储格式 vN」,
凡出现插件版本的地方一律带小数点 —— 混着叫「本版 v3」时用户没法判断哪个数字在说他装的插件旧。
lib/config.mjs 里那个零引用的 CONFIG_VERSION = 3 已删除,就是为了不再制造第二个「v3」。

提示词段落

section 名 order 内容
anima:identity 10 助手自身的名字与角色(可被工作区覆盖,装配期实时求值)
anima:memory 20 已采纳的长期记忆
anima:pending 21 待审条数提示(只报数量,不含内容)

刻意排在 PLAN_POLICY(500) 之前:人设是「我是谁」,应当在行为策略之前确立。

命令与工具

  • /anima [show|pending|adopt |discard |triage|name <名字>|enable|disable|set <键>=<值>|confirm|reject|reset]
  • 工具 remember(text, note?, scope?) —— 投递一条候选到待审缓冲区
  • 工具 set_assistant_persona(...) —— 模型改助手人设;开启「工具确认」时只落提议,
    需用户 /anima confirm 或设置页点「确认」才生效

/anima show 是只读诊断命令:存储栈没组合时它恰恰最该跑得通——用户正是靠它才知道
「插件在,是存储没接上」,所以它容忍装载失败,把原因和「以上是内置默认值」的提示一起打出来。

界面挂载点

槽位 id order 内容
settings.section anima-settings 51 「灵台」设置页
conversation.input.left anima-quick-toggle 51 输入框左侧的「人设已开 / 人设已关」快捷开关
conversation.input.overlay anima-trail 51 输入框光轨(仅助手运行时亮起)

三个 id 全部与 dsh-soul 的对应项错开:settings.section 的文档写明「复用已有 id 即替换那一格」,
撞上就会把对方的设置页顶掉。

设置导航图标用 DOM 替换实现(常量前缀 data-an-settings-nav / data-an-icon-replaced):
壳层的导航图标是 id → 图标的硬编码映射,插件无法通过槽位声明自己的图标。
这是锦上添花的能力,所以代码在入口处检查 document.querySelectorAll 是否存在,没有就整体不接管
——绝不能因为画不出一个图标就让整个客户端装载失败。

光轨为什么带三重自守:它挂在输入框卡片上,一旦尺寸或定位算错就可能反过来影响宿主布局,
所以实现里有三道闸门——内联 cssText 写死 12 条关键属性(防第三方样式表覆盖)、
尺寸超过 TRAIL_MAX_SIDE 直接 disable()(防尺寸正反馈)、挂载后自检
getComputedStyle(svg).position === 'absolute'(防样式被覆盖)。

对外服务

const svc = ctx.get('animaConfig')
await svc.getEffective()              // 全局 + 当前工作区合并后的有效人设
await svc.getConfig()
await svc.updateConfig(patch)
await svc.resetConfig()               // 与设置页「恢复默认」同一条路径,同样保留自建预设
await svc.getSystemPrompt(workspace)  // 模型此刻实际看到的人设文本(与三个 section 逐条对齐)
await svc.listPending()
await svc.runTriage()

getSystemPrompt(workspacePath) 的门控与 anima:identity / anima:memory / anima:pending
三个 section 函数逐条对齐——否则这个服务会答出一份模型实际看不到的文本,那比不提供它更坏。
它刻意不复用 /api/anima/preview:那是设置页的展示口径(带草稿、带 UI 需要的附加字段)。

开发

以下脚本在源码仓库里跑(scripts/ 与 pnpm-lock.yaml 不进 npm 发布包 —— 发布包共 18 个文件:
index.mjs / lib(10 个 .mjs)/ client / assets / cordis.patch.yml / package.json /
LICENSE / README.md / README_EN.md):

node scripts/verify-core.mjs        # 纯函数回归(136 项)
node scripts/smoke-host.mjs         # 宿主侧冒烟(77 项,用假 ctx 真跑 apply)
node scripts/check-client-i18n.mjs  # 客户端字典完整性 + zh/en 对称(zh=en=250 key)
node scripts/smoke-client.mjs       # 客户端渲染冒烟(41 项,假 __ModuleLoader__ + 真钩子运行时)
node scripts/check-live.mjs         # 运行态验收(需 DSH 在跑)

前四套都在进程外跑,只能证明「代码是这么写的」。check-live.mjs 证明的是「进程里那份代码确实生效」——
曾经发生过装机副本停留在旧版、新加的路由一条都没注册、而全部单测依然全绿的情况。
改完插件要重新安装才生效:pnpm 的 file: 依赖是复制语义,重跑 install_bundle 时若判定
changed:false 会直接跳过,正确姿势是 remove_bundle 再 install_bundle(或对比 node_modules
副本与源码的 SHA256 确认)。宿主侧改动一律需要完整重启,热重载不会换掉已缓存的 ESM 模块。

check-live.mjs 里的断言刻意不钉具体值:名字是你的资产,脚本要防的是「数据丢了」,
不是「你改了名」。同理「模型可改哪些字段」这类契约变化,断言长度而不是逐条枚举内容;
路由数量也一样——它点名核对关键路由是否注册,不钉总条数,否则每加一条路由都要回来改这个数字。

verify-core.mjs 与 smoke-host.mjs 都通过 scripts/tools-loader.mjs 把宿主内建包
(@deepseek-ai/dsh-tools、@deepseek-ai/dsh-storage-domain)解析到本地替身 —— 它们只存在于宿主
asar 里,在 DSH 之外解析不到;装进 profile 后走的是真实包,由 check-live.mjs 覆盖。

路由守卫

本插件的 HTTP 路由会读写人设配置与记忆正文,而宿主 webserver 自身不带鉴权
(@deepseek-ai/dsh-host-webserver README 原文:the server carries no TLS,
authentication, or origin policy of its own
),且默认配置可能绑在 0.0.0.0。
因此每条路由都自加一道守卫(index.mjs:467 起),三档判定,任一不过即 403:
非回环来源一律拒;带了 Sec-Fetch-Site 且值不是 same-origin 的拒(浏览器发出的跨站请求会带这个头,
它比 Origin 更早生效,也覆盖了 no-cors 之类的场景);带了 Origin 的还须协议为 http(s)、
主机是回环地址、且 Origin 与 Host 完全一致。
不带 Origin 的回环请求是放行的——本机脚本(curl / PowerShell)属于这一档,
这是刻意为之:守卫拦的是浏览器跨站,不是本机使用者。做法与生态一致
(@michengai/dsh-skills-manager、@linxin666/dsh-remote-web-ui 同类实现)。

破坏性动作的确认

POST /api/anima/config/reset 要求请求体里显式带 {"confirm": true},空 body 一律
400 + code: "confirmation-required",一个字段都不动。设置页的「恢复默认」按钮会先弹一次
确认框,通过后才带该字段。语义与 dsh-checkpoint-rewind 的失败关闭(fail closed)一致:
拿不到明确确认就什么都不做,并说明怎么做才有效。

重置保留用户自建的人设预设——行为与 dsh-soul 的 defaultConfigPreservingPersonas(current) 对齐,
预设是用户一条条攒出来的资产,重置语气设置不该连它一起清掉。

> 这条护栏来自一次真实事故:为确认路由是否注册,对写路由逐一 POST {} 探活,
> /config/reset 当场 200 并清空了 agentName / agentRole / addressUserAs。
> 现在有三道防线:路由要求显式确认、smoke-host.mjs 断言空 body 不得改动字段、
> check-live.mjs 在运行态探一次并在真被清空时就地恢复。

已知边界

  • 配置写入是非原子的(无 tmp+rename),storageDomain 路径下由后端保证。
  • anima:pending 只提示数量,不暴露候选内容——这是刻意的,避免未裁决内容影响回答。
  • 工作区覆盖文件的向上查找最多 32 层。
  • 路由守卫假定请求经过宿主 webserver;若将来宿主在前面加了反向代理,remoteAddress
    会变成代理地址,此守卫需要同步调整。

常见问题

6 问
答案由本站依据条目字段整理,不引用上游原文
dsh-anima 怎么安装?

在 DSH 里执行 `dsh plugin --profile web add github:Melody-cx/dsh-anima#v1.0.0` 即可。

dsh-anima 的验证等级是什么意思?

L5 · 运行验证通过。在 dsh 沙盒中完成安装与运行冒烟——装得上、跑得起来

dsh-anima 安全吗?

低风险。

dsh-anima 装之前要先确认什么?

先看它会读写哪些文件、执行哪些命令、连接哪些网络地址;验证等级只说明装得上、跑得起来,不等于安全。涉及密钥或系统权限的,建议先在隔离环境试装。

dsh-anima 用的是什么许可证?

上游标注为 MIT。商用或二次分发前请到仓库确认许可证原文。

这条记录的数据可靠吗?

本站按仓库名去重后合并整理,字段以作者仓库与 npm 元数据为准;安装命令、仓库地址与 README 保持原文,带 L4 / L5 标记的安装命令已在隔离沙盒里真实跑通。

记忆与知识
按星标排序,可作替代方案

ruflo

ruflo

L3 · 安装规范通过 7.4 万 星

OpenViking

OpenViking

面向 DeepSeek Harness 的 OpenViking 记忆与上下文插件:pre-step 自动召回与画像注入、会话捕获、`vik

L3 · 安装规范通过 3.9 万 星

WeKnora

WeKnora

把 WeKnora 知识库接入 dsh 的四个只读工具:列出知识库、混合检索原文片段、按顺序还原单篇文档,以及直接取用 WeKnora 自己

L4 · 沙盒安装通过 3.3 万 星

distilly

distilly

Distilly — Distill how they think into reusable Skills for any Agent o

L4 · 沙盒安装通过 2.5 万 星

EverOS

EverOS

L4 · 沙盒安装通过 1.3 万 星

MemOS

MemOS

L4 · 沙盒安装通过 1.2 万 星

插件的元信息来自作者仓库;分类、验证等级与安全结论的中文说明为中文整理,最近核验 —。