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

自进化记忆与技能 lmzhen/dsh-evolution

DeepSeek Harnessv0.2.2
lmzhen4 天前有提交已提供中文文档扫描:含敏感能力

dsh-evolution:给 DeepSeek Harness 的自进化记忆与技能

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

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

自动兼容检查未通过:缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add lmzhen/dsh-evolution
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · other
装得上吗
npm 包存在但缺 main/exports/bin 入口声明,源码安装也可能起不来
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 4 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-evolution @ 0.2.2
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/30 15:55:48

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 84 处,展示前 67 条)
  • 读取本地文件
    packages/evolution-commands/src/doctor-view.ts:257
    if (existsSync(manifest)) {
  • 读取本地文件
    packages/evolution-commands/src/doctor-view.ts:258
    const parsed = JSON.parse(readFileSync(manifest, 'utf8')) as { version?: unknown }
  • 读取本地文件
    packages/evolution-commands/src/doctor-view.ts:278
    const parsed = JSON.parse(readFileSync(manifest, 'utf8')) as { version?: unknown }
  • 读取本地文件
    packages/evolution-commands/src/doctor.ts:331
    raw = readFileSync(join(dir, 'package.json'), 'utf8')
  • 读取本地文件
    packages/evolution-commands/src/doctor.ts:396
    const parsed = JSON.parse(readFileSync(path, 'utf8')) as { bases?: unknown }
  • 读取本地文件
    packages/evolution-commands/src/doctor.ts:526
    delta = readFileSync(deltaPath, 'utf8')
  • 读取本地文件
    packages/evolution-commands/src/doctor.ts:577
    fresh = composePresetEntry(readFileSync(resolvePresetBasePatch(base.name, target), 'utf8'), delta, {
  • 读取本地文件
    packages/evolution-commands/src/index.ts:1370
    if (!existsSync(deltaPath)) return err(`预设文件在 ${source} 里缺失:是否装了 dsh-evolution-agent-preset 包?`)
  • 读取本地文件
    packages/evolution-commands/src/index.ts:1373
    const delta = readFileSync(deltaPath, 'utf8')
  • 读取本地文件
    packages/evolution-commands/src/index.ts:1382
    const current = existsSync(profile.patchPath) ? readFileSync(profile.patchPath, 'utf8') : ''
  • 读取本地文件
    packages/evolution-commands/src/index.ts:1382
    const current = existsSync(profile.patchPath) ? readFileSync(profile.patchPath, 'utf8') : ''
  • 读取本地文件
    packages/evolution-commands/src/index.ts:1396
    const entryText = composePresetEntry(readFileSync(basePatch, 'utf8'), delta, {
  • 发起网络请求
    packages/evolution-commands/src/index.ts:1489
    const decision = await approval.request({
  • 读取本地文件
    packages/evolution-commands/src/index.ts:1819
    const parsed = JSON.parse(readFileSync(path, 'utf8')) as { default?: unknown; bases?: unknown }
  • 读取本地文件
    packages/evolution-commands/src/profiles.ts:43
    if (!existsSync(root)) return { kind: 'scanned', profiles: [] }
  • 读取本地文件
    packages/evolution-commands/src/profiles.ts:46
    entries = readdirSync(root, { withFileTypes: true })
  • 读取本地文件
    packages/evolution-commands/src/profiles.ts:55
    if (!existsSync(join(dir, 'package.json'))) continue
  • 读取本地文件
    packages/evolution-commands/src/profiles.ts:79
    const manifest = JSON.parse(readFileSync(join(profileDir, 'package.json'), 'utf8')) as { dsh?: { profile?: { bundles?: unknown } } }
  • 读取本地文件
    packages/evolution-commands/src/profiles.ts:94
    return readFileSync(path, 'utf8')
  • 发起网络请求
    packages/evolution-core/src/http-routes.ts:56
    fetch(request: Request): Promise<Response>
  • 写入本地文件
    packages/evolution-core/src/io.ts:23
    rename(path: string, destination: string): Promise<void>
  • 读取本地文件
    packages/evolution-core/src/io.ts:287
    : await stat(target).then(value => value.mode & 0o777).catch(() => undefined)
  • 删除本地文件
    packages/evolution-core/src/io.ts:298
    await rm(tmp, { force: true }).catch(() => {})
  • 删除本地文件
    packages/evolution-core/src/io.ts:331
    await rm(tmp, { force: true }).catch(() => {})
  • 读取本地文件
    packages/evolution-core/src/io.ts:440
    try { entries = await readdir(dir) } catch { return }
  • 读取本地文件
    packages/evolution-core/src/io.ts:449
    const st = await stat(tmpPath)
  • 删除本地文件
    packages/evolution-core/src/io.ts:453
    await rm(tmpPath, { force: true })
  • 读取本地文件
    packages/evolution-core/src/io.ts:467
    const st = await stat(corruptPath)
  • 删除本地文件
    packages/evolution-core/src/io.ts:468
    if (Date.now() - st.mtimeMs > CORRUPT_SWEEP_AGE_MS) await rm(corruptPath, { force: true })
  • 读取本地文件
    packages/evolution-core/src/io.ts:477
    try { return await readFile(path, 'utf8') } catch (error) {
  • 写入本地文件
    packages/evolution-core/src/io.ts:486
    await mkdir(dirname(path), { recursive: true })
  • 写入本地文件
    packages/evolution-core/src/io.ts:495
    await mkdir(dirname(path), { recursive: true })
  • 读取本地文件
    packages/evolution-core/src/io.ts:500
    current = await readFile(path, 'utf8')
  • 删除本地文件
    packages/evolution-core/src/io.ts:509
    await rm(path, { force: true })
  • 删除本地文件递归
    packages/evolution-core/src/io.ts:519
    await rm(path, { recursive: true, force: true })
  • 写入本地文件
    packages/evolution-core/src/io.ts:535
    async rename(path, destination) {
  • 写入本地文件
    packages/evolution-core/src/io.ts:536
    await mkdir(dirname(destination), { recursive: true })
  • 写入本地文件
    packages/evolution-core/src/io.ts:544
    await mkdir(dirname(destination), { recursive: true })
  • 读取本地文件
    packages/evolution-core/src/package-identity.ts:26
    if (existsSync(manifest)) {
  • 读取本地文件
    packages/evolution-core/src/package-identity.ts:27
    const parsed = JSON.parse(readFileSync(manifest, 'utf8')) as { name?: unknown }
  • 读取本地文件
    packages/evolution-core/src/skill-discovery.ts:45
    if (!existsSync(root)) return []
  • 读取本地文件
    packages/evolution-core/src/skill-discovery.ts:48
    try { entries = readdirSync(root) } catch { return [] }
  • 读取本地文件
    packages/evolution-core/src/skill-discovery.ts:52
    try { if (!statSync(dir).isDirectory()) continue } catch { continue }
  • 读取本地文件
    packages/evolution-core/src/skill-discovery.ts:54
    if (!existsSync(md)) continue
  • 读取本地文件
    packages/evolution-core/src/write-inventory.ts:158
    raw = readFileSync(fileURLToPath(SITES_URL), 'utf8')
  • 发起网络请求
    packages/evolution-learning-graph/src/index.ts:611
    const decision = await approval.request({
  • 发起网络请求
    packages/evolution-learning-graph/src/index.ts:686
    const decision = await memoryApproval.request({
  • 发起网络请求
    packages/evolution-learning-graph/src/index.ts:724
    const decision = await approval.request({
  • 发起网络请求
    packages/evolution-learning-graph/src/index.ts:769
    const decision = await memoryApproval.request({
  • 写入本地文件
    packages/evolution-merge/src/target-claim.ts:27
    await mkdir(dirname(path), { recursive: true })
  • 删除本地文件
    packages/evolution-merge/src/target-claim.ts:33
    try { return await fn() } finally { await unlink(guard).catch(() => {}) }
  • 读取本地文件
    packages/evolution-merge/src/target-claim.ts:36
    const pid = Number(await readFile(guard, 'utf8').catch(() => ''))
  • 删除本地文件
    packages/evolution-merge/src/target-claim.ts:37
    if (Number.isFinite(pid) && pid > 0 && !isProcessAlive(pid)) await unlink(guard).catch(() => {})
  • 读取本地文件
    packages/evolution-merge-engine/src/engine.js:24
    return existsSync(p) ? createHash('sha256').update(readFileSync(p)).digest('hex') : ''
  • 读取本地文件
    packages/evolution-merge-engine/src/engine.js:24
    return existsSync(p) ? createHash('sha256').update(readFileSync(p)).digest('hex') : ''
  • 读取本地文件
    packages/evolution-merge-engine/src/engine.js:97
    const models = int8 && pca && existsSync(int8) && existsSync(pca) ? 'ok' : 'missing'
  • 读取本地文件
    packages/evolution-merge-engine/verify-upstream.mjs:6
    const doc = JSON.parse(readFileSync(join(root, 'UPSTREAM.json'), 'utf8'))
  • 读取本地文件
    packages/evolution-merge-engine/verify-upstream.mjs:13
    if (!existsSync(p)) { console.error('MISSING', rel); bad++; continue }
  • 读取本地文件
    packages/evolution-merge-engine/verify-upstream.mjs:14
    const b = readFileSync(p)
  • 发起网络请求
    packages/evolution-review/src/index.ts:1818
    const decision = await approval.request({
  • 发起网络请求
    packages/test-support/host-route-harness.ts:82
    return route.fetch(request)
  • 写入本地文件
    packages/test-support/mutation-fixture.ts:32
    await mkdir(dirname(full), { recursive: true })
  • 写入本地文件
    packages/test-support/mutation-fixture.ts:33
    await writeFile(full, file.content, 'utf8')
  • 执行外部命令来自配置/变量
    packages/test-support/mutation-fixture.ts:41
    execFile(process.execPath, [script, ...args], { encoding: 'utf8', maxBuffer: 8 * 1024 * 1024 }, (error, stdout, stderr) => {
  • 删除本地文件递归
    packages/test-support/temp-home.ts:53
    await rm(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
  • 发起网络请求
    packages/tool-memory/src/index.ts:463
    const decision = await approval.request({
  • 发起网络请求
    packages/tool-skill-manage/src/index.ts:1236
    const decision = await approval.request({
扫描于 2026/11/2(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库

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

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

功能亮点

  • 智能体自进化
  • 记忆与技能管理

Hermes 风格的智能体自进化插件系列,专为 DeepSeek Harness 打造

README

由 DeepSeek 最新模型翻译生成
dsh-evolution:给 DeepSeek Harness 的自进化记忆与技能 English · 中文 一次安装:Agent 会记下你项目的要点,并持续改进自己的技能。 目录:这是什么 · 你该装吗 · 安装 ·选一种安装方式 · 工作原理 · 日常使用 ·如何调低或关掉 · 安全边界 · 已知限制 ·故障排查 · 文档索引 @lmzhen 下的社区发布包由 dsh-evolution 社区维护,不是 DeepSeek 官方发布;它们既不用 @deepseek-ai/ 包名,也不用 DeepSeek 品牌。 这是什么 30 个可组合的 Cordis 插件,给一套 DeepSeek Harness 装上自进化层:Agent 会回顾自己的对话,维护记忆、提出并修补技能,并让这个技能库随时间新陈代谢。模型只能写记忆和技能——策略、提示词、路由、状态和审计历史是它永远碰不到的控制面数据。 它不是模型,不是托管服务,也没有自己的 GUI:它带来的是模型工具、斜杠命令,以及你自己 $DSH_HOME 下的文件。 你该装吗 如果你已经烦透了每个新会话都要重新解释同一个项目,或者烦透了手工维护一堆永远不进步的技能,那就装它。装了之后: - 事实留得下来。 评审会把真正重要的东西写进记忆,之后的会话自动拿到这份注入。 - 技能会自己变好。
展开完整文档(README 剩余 1 节)
dsh-evolution:给 DeepSeek Harness 的自进化记忆与技能

English · 中文

一次安装:Agent 会记下你项目的要点,并持续改进自己的技能。

目录:这是什么 · 你该装吗 · 安装 ·选一种安装方式 · 工作原理 · 日常使用 ·如何调低或关掉 · 安全边界 · 已知限制 ·故障排查 · 文档索引

@lmzhen 下的社区发布包由 dsh-evolution 社区维护,不是 DeepSeek 官方发布;它们既不用 @deepseek-ai/ 包名,也不用 DeepSeek 品牌。

这是什么

30 个可组合的 Cordis 插件,给一套 DeepSeek Harness 装上自进化层:Agent 会回顾自己的对话,维护记忆、提出并修补技能,并让这个技能库随时间新陈代谢。模型只能写记忆和技能——策略、提示词、路由、状态和审计历史是它永远碰不到的控制面数据。

它不是模型,不是托管服务,也没有自己的 GUI:它带来的是模型工具、斜杠命令,以及你自己 $DSH_HOME 下的文件。

你该装吗

如果你已经烦透了每个新会话都要重新解释同一个项目,或者烦透了手工维护一堆永远不进步的技能,那就装它。装了之后:

- 事实留得下来。 评审会把真正重要的东西写进记忆,之后的会话自动拿到这份注入。
- 技能会自己变好。 Agent 从真实发生过的事里修补自己的技能,计划先验证再执行——还有技能整理(curator)会合并近重复、归档已经没人用的。
- 控制权始终在你手里。 每一次写入都过威胁扫描和不可变策略;再加一个开关,每次写入都会先等你批准。技能销毁从来不是硬删除;每次写入都留下被替换掉的那一版内容——单个技能可以退回它最近 20 版中的任意一版,不用动库里其他技能。
- 一切看得见。 计数、运行报告和变更日志都是 $DSH_HOME 下的文件。没有遥测服务——除了你本来就在发的提示词,什么都不离开你的机器。

如果你想要一个完全撒手不管的黑盒,或者需要「按会话给工具、但不要 profile 级自动化」(那是安装形态 ①:各形态及其验证状态都在 packages/INSTALL.md),那就别装。

安装

dsh plugin --profile web add @lmzhen/dsh-evolution-all # 默认全量包
重启 dsh 让 profile 重新组合,然后在任意会话里: /evolution doctor

你会看到装上了 29 个包(发布面 30 个;@lmzhen/dsh-evolution-preset 是 agent 预设形态,不进 node_modules);重启之后,doctor 报告会说明它识别到的安装形态和找到的行。不用问模型就能验证:

dsh --profile web --dump-config | grep -c 'id: evolution-' # 19 行
dsh --profile web --dump-config | grep -c 'id:' # 209 个 id,全部互不相同

启动时不写任何东西:第一次评审要等间隔窗口。想让每次写入都先过你的批准,而不是全自动?加上 approval.enabled: true(也就是 M2)。

选一种安装方式(M1–M4)

M 编号在本文件定义;安装形态以及各形态在平台线上的验证状态,唯一出处是 packages/INSTALL.md。

| 形态 | 怎么做 | 你得到什么 |
| M1 全自动(默认;0.3.54 起) | dsh plugin --profile web add @lmzhen/dsh-evolution-all | 两条回路都跑、都写盘;每个会话都有模型工具 |
| M2 人审把关 | M1 + approval.enabled: true | 每次写入都变成 /evolution pending,由你批准或拒绝 |
| M3 只装底座 | dsh plugin --profile web add @lmzhen/dsh-evolution-host | 自动化照常跑,模型拿不到记忆/技能工具 |
| M4 按会话启用(进阶) | M3 + /evolution preset install | 只有选了 Evolution 预设的会话才有工具(与 M1、旧 one-click 预设互斥) |

这几种只能挂一种:几个 bundle 共享基础设施行,重复的 id 会让加载器在启动时直接中止。bundle 还把状态行钉在 provider: json 上,这样无关的 overlay 不会悄悄把你的状态挪到一个空域上。

兼容性

| | 取值 |
| 已验证的 DSH 平台线 | 0.2.0-rc.2(PLATFORM_VERSION;钉住的开发基线是 UPSTREAM_SHA=fb2c4b9e…) |
| 声明的依赖窗口 | 每个 @deepseek-ai/dsh- 依赖/peer 上都是 ^0.2.0-rc.1(PLATFORM_FLOOR) |
| 家族版本 | 0.30.18(本树版本;npm 上的 tag 由发布 tag 决定——各形态状态见 packages/INSTALL.md) |
| Node | 22.19+ 或 24+(engines) |
| 平台线沿革 | 0.17.0-rc.1 起 → 0.2.0-rc.2;0.3.70–0.16.1 → 0.1.5-rc.2;0.3.69 及更早 → 0.1.1-rc.2 |

两个值回答两个问题:PLATFORM_FLOOR 是发布出去的 range 声明的口径,PLATFORM_VERSION 是门禁实际验证的那条线。floor 必须收得住整条线——^0.2.0-rc.1 收得住 0.2.0-rc.2,而 ^0.2.0-rc.2 会拒绝它自己验证过的那个 rc。预发布 range 仍然不跨线:更早的预发布线在依赖解析阶段就失败,这是支持窗口,不是 bug。某个 tag 是哪条线,就是该 tag 的 .github/workflows/release.yml 里的 PLATFORM_VERSION(git show <tag>:.github/workflows/release.yml);上表那行沿革是它按版本区间的汇总。

工作原理

| 层 | 做什么 | 你能察觉到什么 |
| Review | 观察会话事件,应用 substantive 门,产出经过验证的计划(evolution-review、evolution-plan-validator) | 评审发生在对话边界,不会打断任务中途 |
| 记忆回路 | 写持久事实与用户画像,把指引注入新会话(memory、memory-files、tool-memory) | 新会话已经知道你的项目 |
| 技能回路 | 通过 skill_manage 工具提出、写入、修补技能;目录把它们暴露给模型(tool-skill-manage、evolution-skill-catalog) | 你不必自己写的技能,而且任何单个技能都能退回上一版 |
| Curator(界面卡片叫技能整理) | 确定性的 stale/archive 生命周期 + LLM 提名,每次运行前先快照(evolution-curator) | 技能库保持小而整洁,重复的被合并 |
| 控制面 | 威胁扫描、不可变策略、可选带重放的分阶段审批(evolution-threat、evolution-policy、evolution-approval) | 被拒绝的写入会告诉你原因,破坏性动作绝不静默 |
| 管道 | IO 接缝、状态 provider、事件、activity、replay、学习图谱(evolution-io、evolution-state、evolution-activity、evolution-replay) | 一切都是你能读、能备份、能删除的文件 |

$DSH_HOME/evolution/events.json 追加式的反馈/用量时间线 $DSH_HOME/evolution/review-state.json 每个会话的评审计数 $DSH_HOME/evolution/activity.json 计划执行结果 reports/ curator 运行报告 $DSH_HOME/skills/ 技能树(+ .usage.json 计数、.history/ 内容版本) $DSH_HOME/memories/ MEMORY.md + USER.md

有两个默认值值得先知道。评审在会话内跑(reviewMode: 'inject',0.3.74 起就是默认)——不会另起子代理,因此没有单独的评审账本;想要干净的父上下文或专用模型,subagent 是显式 opt-in。跨会话消费者按会话生效: review 与用量遥测只有在某个会话能解析出家族的模型工具时才作用于它——M1 下每个会话都成立,host-only 安装(M3)则永远不成立,插件会为此报告一次,而不是静默失败。

日常使用

- /evolution doctor 报告装了什么:安装形态、受作用域约束的行、服务、待办数量。
- /evolution pending + approve / reject:分阶段写入(M2)。
- /evolution curator status:上次运行与下次到期(curator 每小时 tick;默认每 168 h 到期一次,所以安静几周是正常的)。
- /evolution preset install [--base <name>[,<name>...]]:生成家族 agent 预设(M4)。
- /evolution skill history <name> 列出某个技能的内容版本;/evolution skill undo <name> [--to v<N>] 把该技能退回某一版(只动内容——冻结标记、用量计数与整理状态保持不变,撤销本身也记为新的一版)。左栏那一行 技能历史(在平台自带面板行旁边)是同一件事的图形面:列出有版本记录的技能,把正文版本与文件版本分开显示,确认后回退某一版——调用的仍是同一条 curator.undo。
- /evolution mutations 是写入日志;/graph 把技能与记忆画成图谱。

完整的命令面只渲染一次,放在 packages/README.md(§Command reference);环境变量与字段级旋钮的完整参考也在那里。

在界面里改参数

家族在浏览器里有两处界面,都是可选行。设置栏 「自进化」 把你能改的参数分成五张可折叠卡片: 会话回顾/长期记忆/记忆写入/技能整理/技能写入规则——卡片名、每个字段的中文标签与说明都来自参数注册表。每个字段带单位、当前值的来源(我改过 / 默认)、按注册表类型渲染的控件(开关/下拉/ 数字/文本),以及那句说明。改完按「保存」;「放弃修改」丢弃草稿;想让某个字段回到默认值,按它自己的「恢复默认值」。下拉框里显示的是中文名,存进去的仍是原值(注册表新增的可选 valueLabels)。
另一处是左栏面板 「技能历史」(sidebar.panellist 一行 + 同 id 的 main 面板体):浏览每次技能写入留下的内容版本,并回退某一版——与终端里的 /evolution skill history 是同一份数据。

写入落在 ~/.dsh/settings.yaml 里属于该插件的那一段,即时生效、不用重启。同一个 store 也可以在会话里用 /evolution policy set <id> <value> 写;/evolution params 给人看的那份表是中文的(参数/分组/档位/生效/来源/当前值,外加一行档位图例),脚本用 --json 则保持原字段名与取值。
资源上限、provider、影响提示词身份这类安装时定的参数仍留在 cordis.yml/profile 的补丁层——卡片只列可改项, doctor 的「参数面分歧」一节会报告两边不一致的地方。

如何调低或关掉

| 拨盘 | 取值 | 字段 |
| autonomy | auto / reviewed / observe | approval.enabled、reviewEnabled |
| scope | global / per-session | evolution-all vs host + agent 预设 |
| curatorBackground | on / off | autoStart / curatorIntervalHours / minIdleHours |
| memoryInjection | on / off | memoryEnabled(关掉 = 整行变 no-op) |
| threatStrictness | strict / 豁免清单 | threatExemptLabels(按配置点声明;清单唯一出处是 evolution-threat/README.md) |

三种调低方式,按安静程度递增:加闸门(approval.enabled: true)、停止评审(reviewEnabled: false)、缩到 M3(完全没有模型工具)。几个容易让人意外的默认值:reviewEnabled: true、reviewMode: 'inject'、reviewMemoryInterval = reviewSkillInterval = 10 轮,substantive 门 =「≥3 次工具调用 或 ≥200 个用户字符 或 ≥500 个 agent 字符」,curator 到期间隔 168 h。

环境变量

完整清单只有一个家:packages/README.md 的 Environment variables 一节——每个变量、读它的层、默认值、确切语义都在那里。这里刻意不复述:第二份副本正是 DSH_AGENT_PRESET_ROOT 漂移的原因(它指的是存放平台 .patch.yml base patch 的目录,不是「预设安装目的地」)。

安全与边界

1. 模型只能写记忆和技能:其他一切都是控制面数据(能力包/动态插件同样不在写入面内:创建、审批与激活归平台 Creator 模式;0.3.66 起本家族的 capability 适配器已移除,见 packages/INSTALL.md)。
2. 每一次变更都有闸门:tools.guard 的威胁扫描,打开审批时再加上分阶段审批;被批准的写入会经它注册时的同一个 runner 重放。
3. 没有任何东西被静默销毁:技能归档移进 .archive/,而且每次 curator 运行前都会先给技能树做快照。
4. 离开会话、交给模型的文本会被脱敏(PEM 块、URL 里的凭据、内联赋值、camelCase 凭据键)。
5. 没有遥测。 计数和报告都是 $DSH_HOME 下的文件;本家族不发布 ./invariant 伴生包,因为平台永远不会执行它。

已知限制

- reviewMode 默认是 inject:评审在会话内跑,不产生 evolution/plan-applied 账本,所以 activity 存储和 replay 视图会一直空着,直到你选择 subagent 评审。
- 会话作用域的消费者在 host-only 安装(M3)上永远匹配不上;在 M4 上只有选了 Evolution 预设的会话才匹配。
- 浏览器里是两处可选行:设置栏(自进化)与左栏面板(技能历史)。家族加的是斜杠命令、模型工具和这两处界面,没有别的网页 UI。面板的渲染没有 CI 覆盖(家族没有渲染型客户端测试车道),靠 tsc、客户端 bundle 构建、api 层 spec 与真机过一遍兜着。
- 只有一个平台锚点:换一条新的平台线需要一次家族迁移,而不是改个配置。
- npm dist-tags.next 停在 0.3.18(历史残留);latest 是正确的,add 解析的也是它。

故障排查

还什么都没写。 第一次评审要等间隔窗口(reviewMemoryInterval / reviewSkillInterval,默认 10 轮)和 substantive 门——一行字的会话本来就不值得评审。

activity.json 是空的。 这是 inject 模式的行为,不是故障;见上面第一条已知限制。

怎么确认正在跑哪个版本? 拿 profiles/<name>/pnpm-lock.yaml 和 npm view @lmzhen/dsh-evolution-all version 对一下;dsh 重启时 profile 才会重新组合。

威胁扫描拒绝了一次写入。 改写它,或者用 threatExemptLabels 豁免一个已知无害的 label。

错误码

| 编号 | 含义 | 下一步 |
| 启动报 invariants: package "…" is already registered | 两个 bundle 把同一批行挂了两次 | evolution-all / evolution-host / evolution-preset 只留一个 |
| E-301 / E-302 / E-303 | approval / curator / replay 服务未挂载 | 挂上对应的行(它们随 host/all 提供),然后跑 doctor |
| E-306 | 该部署对前台写入做 stage,但这条命令没法通过技能 runner 重放 | 直接 approve-and-execute,或者明确设成 stageForeground: false |
| E-304 | 挂载的 approval 服务早于 release 能力(升级不完整/错位) | 改用 reject 处理该 pending 记录,不要 release |
| E-305 | 本次调用没有携带 agent(脚本/headless 调用方走到了需要会话的分支) | 在 GUI 或 CLI 的会话里运行该命令 |
| E-307 / E-311 | 设置服务缺失或报不出 section(/evolution params、/evolution policy set 需要它) | 挂上设置行(随 host/all 提供),然后跑 doctor |
| E-308 / E-313 | 参数分组或参数 id 不存在 | 用 /evolution params 列出来:每个注册 id 的层级与 owner 都在里面 |
| E-314 / E-315 / E-316 | 该 id 是安装时定的参数、不能由用户改,或它的 owner 没挂载 | 在 cordis.yml 里写,或挂上 owner 行 —— 报文里点名层级与 owner |
| E-309 / E-310 | 你的写入输给了并发修订,或设置服务拒绝了它(原因随报文给出) | 用 /evolution params 重读后重试 |
| E-317 | 新建或删除技能前的那次确认被否了(skillWriteConfirm 为 ask/timeout 时前台 create 与不带 absorbed_into 的 delete 才会问;默认 auto 不问) | 什么都没写;确实要做就再调一次 |
| E-318 | 没读就写:本会话没有加载过该技能,写入被拒(只对技能;前台操作者自己的会话豁免) | 先用 skill 工具读它,再重试写入 |
| E-319 | 新建或裸删除前的那次确认,等满 skillWriteConfirmTimeoutSeconds 秒没人回答(skillWriteConfirm: 'timeout'),写入被取消 | 把 skillWriteConfirm 设成 auto(不问直接写)、保持 ask(一直等回答),或把超时调大 |
| doctor 报 install form: none | 没有装任何 bundle | dsh plugin --profile web add @lmzhen/dsh-evolution-all |

本表是「症状 → 下一步」对照;每条编号的原文只存一处(evolution-core/src/errors.ts,E-301–E-322),运行时按原文输出。

卸载

dsh plugin --profile web remove @lmzhen/dsh-evolution-all

回路停了,数据不会:记忆、技能、状态、报告和审批历史都留在 $DSH_HOME 下,重装后会重新读回来。

文档索引

| 文档 | 里面有什么 |
| INSTALL.md | 安装形态、scope、profile 覆盖示例 |
| packages/INSTALL.md | 安装形态的语义与逐平台验证矩阵 |
| packages/README.md | 命令参考、包地图、环境变量/旋钮参考、布局说明 |
| CHANGELOG.md | 每个版本改了什么,连同原因和证据 |
| CONTRIBUTING.md | 一个事实只能写在哪儿、十八步门禁、仓库约定 |

维护者:上游升级对照清单

1. 技能 provider 的影子 rank:本家族的 provider 注册 EVOLUTION_SKILL_RANK = 390,依赖上游的 USER_DSH_RANK(0.1.5-rc.2 上是 400,0.2.0-rc.2 上仍是 400——packages/skill/skill-filesystem/src/index.ts:39)排在它上面;两边都要复核。
2. @deepseek-ai 包名撞名:发布工具会把家族包名改写成 @lmzhen;每个上游版本都要检查有没有和我们撞名的包。
3. ToolRuntime 参数冻结:tool.execute 拿到的是 deepFreeze 过的快照:要新建对象,不要往 args 上赋值。
4. 逐技能调用的 frontmatter:影子 provider 必须继续解析和上游一样的键;它对旧键的处理口径,唯一出处是 evolution-skill-catalog/README.md。
5. Home 路径语义:上游 resolveDshHome 只用 trim() 做 adoption 判定、保留原始 env 值、展开 ~ 并总是解析成绝对路径;evolutionRoot 与 install-layered.mjs 走的是同一条线。

许可与归属

MIT,见 LICENSE。设计灵感来自 Hermes Agent(MIT);这是独立实现,与 Nous Research、DeepSeek 均无隶属或背书关系。欢迎到仓库提 issue 和反馈——本家族不带遥测,所以附上你的 /evolution doctor 输出是最快的路径。Anchored Standard 兼容 fixture 取自 xiaobright/dsh-anchored-standard(MIT)。
预览分享卡片长什么样 ↓lmzhen/dsh-evolution
分享 · DeepSeek Harness Hub
lmzhen/dsh-evolution

dsh-evolution:给 DeepSeek Harness 的自进化记忆与技能

回到顶部