用任务探针与经验库让插件自我迭代改进
29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/21 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add HeliosLz/harness2-recursion该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · dev
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 12 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件
查看扫描证据(共 118 处,展示前 102 条)
- 写入本地文件e2e/fake-model-server.mjs:22
mkdirSync(dirname(LOG), { recursive: true }) - 写入本地文件e2e/fake-model-server.mjs:106
appendFileSync(LOG, `${JSON.stringify({ url: req.url, body: parsed })}\n`) - 写入本地文件e2e/round6-spike/fake-openai.mjs:14
if (LOG) mkdirSync(dirname(LOG), { recursive: true }) - 写入本地文件e2e/round6-spike/fake-openai.mjs:41
if (LOG) appendFileSync(LOG, `${JSON.stringify({ at: new Date().toISOString(), body: parsed })}\n`) - 读取本地文件e2e/round6-spike/gate-replay.mjs:183
const records = (await readFile(LEDGER, 'utf8')).trim().split('\n').filter(Boolean).map((line) => JSON.parse(line)) - 读取本地文件e2e/round6-spike/gate-replay.mjs:228
const experienceText = await readFile(EXPERIENCE, 'utf8').catch(() => '') - 读取本地文件e2e/round6-spike/gate-replay.mjs:229
const freshStage0ExperienceText = await readFile(STAGE0_EXPERIENCE, 'utf8').catch(() => '') - 写入本地文件e2e/round6-spike/gate-replay.mjs:348
await mkdir(path.dirname(OUT), { recursive: true }) - 写入本地文件e2e/round6-spike/gate-replay.mjs:349
await writeFile(OUT, `${JSON.stringify(verdict, null, 2)}\n`, 'utf8') - 执行外部命令固定命令e2e/round6-spike/run-spike.mjs:34
const run = spawnSync('git', ['status', '--porcelain'], { cwd: REPO, encoding: 'utf8' }) - 读取本地文件e2e/round6-spike/run-spike.mjs:45
const fileHash = async (file) => `sha256:${sha256(await readFile(file, 'utf8'))}` - 执行外部命令来自配置/变量e2e/round6-spike/run-spike.mjs:49
const run = spawnSync(process.execPath, [CLI, ...args], { - 写入本地文件e2e/round6-spike/run-spike.mjs:62
try { writeFileSync(fd, text, 'utf8'); fsyncSync(fd) } finally { closeSync(fd) } - 读取本地文件e2e/round6-spike/run-spike.mjs:113
try { return (await readFile(LOG, 'utf8')).trim().split('\n').filter(Boolean).length } catch { return 0 } - 执行外部命令来自配置/变量e2e/round6-spike/run-spike.mjs:118
const server = spawn(process.execPath, [path.join(HERE, 'fake-openai.mjs')], { - 执行外部命令来自配置/变量e2e/round6-spike/run-spike.mjs:128
const run = spawnSync(process.execPath, [CLI, '--profile', PROFILE, task], { - 读取本地文件e2e/round6-spike/run-spike.mjs:134
const lines = (await readFile(LOG, 'utf8')).trim().split('\n').filter(Boolean).slice(before) - 删除本地文件递归e2e/round6-spike/run-spike.mjs:144
await rm(path.join(ART, 'home'), { recursive: true, force: true }) - 删除本地文件e2e/round6-spike/run-spike.mjs:145
await rm(LOG, { force: true }) - 写入本地文件e2e/round6-spike/run-spike.mjs:146
await mkdir(ART, { recursive: true }) - 写入本地文件e2e/round6-spike/run-spike.mjs:158
await writeFile(PATCH, patchA, 'utf8') - 写入本地文件e2e/round6-spike/run-spike.mjs:159
await copyFile(PATCH, BACKUP) - 写入本地文件e2e/round6-spike/run-spike.mjs:161
await mkdir(path.join(ART, 'revisions', 'rev-A', 'backup'), { recursive: true }) - 写入本地文件e2e/round6-spike/run-spike.mjs:162
await copyFile(PATCH, path.join(ART, 'revisions', 'rev-A', 'backup', 'cordis.patch.yml')) - 读取本地文件e2e/round6-spike/run-spike.mjs:182
const backupBytes = await readFile(path.join(ART, 'revisions', 'rev-A', 'backup', 'cordis.patch.yml'), 'utf8') - 执行外部命令来自配置/变量e2e/run.mjs:35
const result = spawnSync(process.execPath, [cli, ...args], { - 读取本地文件e2e/run.mjs:50
if (!existsSync(cli)) { - 删除本地文件递归e2e/run.mjs:55
await rm(dshHome, { recursive: true, force: true }) - 删除本地文件递归e2e/run.mjs:56
await rm(artifacts, { recursive: true, force: true }) - 写入本地文件e2e/run.mjs:57
await mkdir(artifacts, { recursive: true }) - 写入本地文件e2e/run.mjs:58
await mkdir(dshHome, { recursive: true }) - 读取本地文件e2e/run.mjs:65
const profilePackage = JSON.parse(await readFile(path.join(profileDir, 'package.json'), 'utf8')) - 写入本地文件e2e/run.mjs:71
await writeFile(path.join(profileDir, 'cordis.patch.yml'), `# Generated by e2e/run.mjs - 执行外部命令来自配置/变量e2e/run.mjs:142
const server = spawn(process.execPath, [path.join(repoRoot, 'e2e', 'fake-model-server.mjs')], { - 执行外部命令来自配置/变量e2e/run.mjs:161
const result = spawnSync( - 读取本地文件e2e/run.mjs:180
const ledgerLines = (await readFile(ledgersPath, 'utf8')).trim().split('\n').filter(Boolean) - 读取本地文件e2e/run.mjs:207
const experienceText = await readFile(experiencePath, 'utf8').catch(() => '') - 写入本地文件e2e/run.mjs:229
await writeFile(path.join(artifacts, 'RESULTS.md'), summary.join('\n'), 'utf8') - 写入本地文件e2e/stage0/fake-model-server.mjs:37
mkdirSync(dirname(LOG), { recursive: true }) - 写入本地文件e2e/stage0/fake-model-server.mjs:179
appendFileSync(LOG, `${JSON.stringify({ - 读取本地文件e2e/stage0/stage0.mjs:70
const text = await readFile(file, 'utf8') - 读取本地文件e2e/stage0/stage0.mjs:84
const text = await readFile(file, 'utf8') - 执行外部命令来自配置/变量e2e/stage0/stage0.mjs:93
const result = spawnSync(process.execPath, [CLI, ...args], { - 执行外部命令固定命令e2e/stage0/stage0.mjs:164
const result = spawnSync('git', ['-C', REPO, 'status', '--porcelain'], { encoding: 'utf8' }) - 读取本地文件e2e/stage0/stage0.mjs:175
if (!existsSync(CLI)) { - 读取本地文件e2e/stage0/stage0.mjs:179
if (!existsSync(path.join(REPO, 'packages', packageName, 'lib', 'index.js'))) { - 读取本地文件e2e/stage0/stage0.mjs:189
text = await readFile(EXPERIENCE, 'utf8') - 写入本地文件e2e/stage0/stage0.mjs:209
await mkdir(HOME, { recursive: true }) - 删除本地文件递归e2e/stage0/stage0.mjs:210
await rm(PROFILE_DIR, { recursive: true, force: true }) - 写入本地文件e2e/stage0/stage0.mjs:212
await writeFile( - 写入本地文件e2e/stage0/stage0.mjs:242
await writeFile(PROFILE_PATCH, patchYaml(), 'utf8') - 写入本地文件e2e/stage0/stage0.mjs:245
await writeFile(path.join(DIR, 'profile-dump-final.txt'), dumpText, 'utf8') - 读取本地文件e2e/stage0/stage0.mjs:265
const deps = JSON.parse(await readFile(path.join(PROFILE_DIR, 'package.json'), 'utf8')).dependencies ?? {} - 写入本地文件e2e/stage0/stage0.mjs:272
await writeFile( - 删除本地文件e2e/stage0/stage0.mjs:326
await rm(REQUESTS, { force: true }) - 执行外部命令来自配置/变量e2e/stage0/stage0.mjs:328
const child = spawn(process.execPath, [path.join(SCRIPT_DIR, 'fake-model-server.mjs')], { - 写入本地文件e2e/stage0/stage0.mjs:384
await writeFile(SERVER_LOG, `${stdout}${stderr}`, 'utf8') - 删除本地文件e2e/stage0/stage0.mjs:1318
await rm(LEDGER, { force: true }) - 删除本地文件e2e/stage0/stage0.mjs:1319
await rm(EXPERIENCE, { force: true }) - 删除本地文件e2e/stage0/stage0.mjs:1320
await rm(REQUESTS, { force: true }) - 执行外部命令来自配置/变量e2e/stage0-r6/run.mjs:50
const result = spawnSync(process.execPath, [CLI, ...args], { - 读取本地文件e2e/stage0-r6/run.mjs:61
const text = await readFile(file, 'utf8') - 读取本地文件e2e/stage0-r6/run.mjs:70
const text = await readFile(file, 'utf8') - 执行外部命令固定命令e2e/stage0-r6/run.mjs:136
assert(spawnSync('test', ['-f', CLI]).status === 0, `dsh CLI not found at ${CLI}`) - 执行外部命令固定命令e2e/stage0-r6/run.mjs:137
assert(spawnSync('test', ['-f', FAKE_SERVER]).status === 0, 'stage0 fake server missing') - 执行外部命令固定命令e2e/stage0-r6/run.mjs:139
assert(spawnSync('test', ['-f', path.join(REPO, 'packages', dirName, 'lib', 'index.js')]).status === 0, - 删除本地文件递归e2e/stage0-r6/run.mjs:142
await rm(HOME, { recursive: true, force: true }) - 写入本地文件e2e/stage0-r6/run.mjs:143
await mkdir(ART, { recursive: true }) - 写入本地文件e2e/stage0-r6/run.mjs:144
await mkdir(HOME, { recursive: true }) - 删除本地文件递归e2e/stage0-r6/run.mjs:145
await rm(EDITS_DIR, { recursive: true, force: true }) - 删除本地文件e2e/stage0-r6/run.mjs:146
await rm(LEDGER, { force: true }) - 删除本地文件e2e/stage0-r6/run.mjs:147
await rm(EXPERIENCE, { force: true }) - 删除本地文件e2e/stage0-r6/run.mjs:148
await rm(REQUESTS, { force: true }) - 写入本地文件e2e/stage0-r6/run.mjs:160
await writeFile(path.join(PROFILE_DIR, 'cordis.patch.yml'), patchYaml(), 'utf8') - 执行外部命令来自配置/变量e2e/stage0-r6/run.mjs:169
server = spawn(process.execPath, [FAKE_SERVER], { - 写入本地文件e2e/stage0-r6/run.mjs:201
await writeFile(CLIENT_LOG, `${sessions.map((item) => JSON.stringify(item)).join('\n')}\n`, 'utf8') - 读取本地文件e2e/stage0-r6/run.mjs:207
const bodyEntries = await readdir(EDITS_DIR).catch(() => []) - 读取本地文件e2e/stage0-r6/run.mjs:212
const body = JSON.parse(await readFile(path.join(EDITS_DIR, entry), 'utf8')) - 写入本地文件e2e/stage0-r6/run.mjs:360
await writeFile(RESULTS, `${JSON.stringify({ summary, checks, sessions }, null, 2)}\n`, 'utf8') - 写入本地文件e2e/stage0-r6/run.mjs:361
if (passed) await writeFile(REPORT, report, 'utf8') - 读取本地文件packages/harness2-meta-recursion/src/index.ts:1270
text = await readFile(this.experiencePath, 'utf8') - 读取本地文件packages/harness2-meta-recursion/src/index.ts:1322
const text = await readFile(this.applyStatePath, 'utf8') - 读取本地文件packages/harness2-meta-recursion/src/index.ts:1339
const journal = await readFile(journalPath, 'utf8').catch(() => '') - 读取本地文件packages/harness2-meta-recursion/src/index.ts:1362
try { text = await readFile(this.evaluationsPath, 'utf8') } catch (error) { - 读取本地文件packages/harness2-meta-recursion/src/index.ts:1381
try { report = JSON.parse(await readFile(reportPath, 'utf8')) } catch (error) { - 写入本地文件packages/harness2-meta-recursion/src/index.ts:1447
await copyFile(this.experiencePath, backupPath) - 写入本地文件packages/harness2-meta-recursion/src/index.ts:1460
await mkdir(directory, { recursive: true }) - 写入本地文件packages/harness2-meta-recursion/src/index.ts:1462
await writeFile(temporary, serializeExperience(snapshot), 'utf8') - 写入本地文件packages/harness2-meta-recursion/src/index.ts:1463
await rename(temporary, this.experiencePath) - 写入本地文件packages/harness2-task-recursion/src/edit-store.ts:225
await mkdir(directory, { recursive: true }) - 写入本地文件packages/harness2-task-recursion/src/edit-store.ts:234
await rename(temporary, file) - 读取本地文件packages/harness2-task-recursion/src/edit-store.ts:265
await stat(file) - 读取本地文件packages/harness2-task-recursion/src/edit-store.ts:266
const existing = await readFile(file, 'utf8') - 读取本地文件packages/harness2-task-recursion/src/edit-store.ts:304
text = await readFile(file, 'utf8') - 读取本地文件packages/harness2-task-recursion/src/edit-store.ts:421
const ledger = await readFile(options.ledgerPath, 'utf8') - 读取本地文件packages/harness2-task-recursion/src/edit-store.ts:432
entries = await readdir(options.editsDir) - 读取本地文件packages/harness2-task-recursion/src/edit-store.ts:451
const info = await stat(path.join(options.editsDir, entry)) - 删除本地文件packages/harness2-task-recursion/src/edit-store.ts:456
await rm(path.join(options.editsDir, entry)) - 写入本地文件packages/harness2-task-recursion/src/index.ts:897
await mkdir(path.dirname(absolute), { recursive: true }) - 写入本地文件packages/harness2-task-recursion/src/index.ts:898
await appendFile(absolute, `${JSON.stringify(record)}\n`, 'utf8') - 读取本地文件packages/harness2-task-recursion/src/index.ts:939
text = await readFile(resolveStoragePath(ledgerPath), 'utf8') - 读取本地文件packages/harness2-task-recursion/src/index.ts:978
text = await readFile(resolveStoragePath(ledgerPath), 'utf8')
扫描于 2026/11/1(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-agent-default-model@deepseek-ai/dsh-llm@deepseek-ai/dsh-session@deepseek-ai/dsh-tools@deepseek-ai/schemastery@deepseek-ai/dsh-token-meter给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
Harness² 递归:两个可审计的 dsh 插件——任务级探针/组合证据闭环 + 驾驭级经验库(冻结契约 v4,34/34 测试通过,真实会话端到端验证)
README
由 DeepSeek 最新模型翻译生成harness2-recursion
Harness² 递归方案 —— 两个互相独立、可分别加载的 dsh 插件(第四轮冻结契约版本)。
| 包 | 职责 |
| packages/harness2-task-recursion | 任务级递归:Propose → Probe → probe/completed → 确定性 Compose 门禁 → 账本 v3 → compose/applied/compose/rejected → 下一个 pre-step 应用 pendingPatch |
| packages/harness2-meta-recursion | Harnessing 级递归:消费三事件 → 事后归因(edit 只取事实)→ 经验库 → 冻结只读门面 harness2Experience |
两个包的 src/index.ts 互不 import;src/constants.ts 逐字节相同。契约见 CONTRACT.md,验收见 SELF-CHECK.md。
展开完整文档(README 剩余 1 节)
harness2-recursion
Harness² 递归方案 —— 两个互相独立、可分别加载的 dsh 插件(第四轮冻结契约版本)。
| 包 | 职责 |
| packages/harness2-task-recursion | 任务级递归:Propose → Probe → probe/completed → 确定性 Compose 门禁 → 账本 v3 → compose/applied/compose/rejected → 下一个 pre-step 应用 pendingPatch |
| packages/harness2-meta-recursion | Harnessing 级递归:消费三事件 → 事后归因(edit 只取事实)→ 经验库 → 冻结只读门面 harness2Experience |
两个包的 src/index.ts 互不 import;src/constants.ts 逐字节相同。契约见 CONTRACT.md,验收见 SELF-CHECK.md。
目录
harness2-recursion/
├── CONTRACT.md # 第三轮冻结契约 ├── SELF-CHECK.md # 第三轮 13 项验收清单逐项证据 ├── README.md ├── package.json / tsconfig.base.json ├── test/loader-smoke.mjs # 真实 Cordis 装载/事件/卸载 smoke ├── e2e/ # 真实 headless 会话 E2E(安装/账本/applyMode/经验) ├── docs/ # 社区 Discussion 提案 + 发布清单 ├── LICENSE · .gitignore └── packages/ ├── harness2-task-recursion/ │ ├── src/{index.ts, constants.ts} │ ├── lib/ # tsc 产物(构建生成,不入库) │ ├── cordis.patch.yml · locale/ · icon.svg │ └── test/verify.mjs # 15 项契约自检 └── harness2-meta-recursion/ # 同上(13 项自检)
社区讨论
- 生态提案 Discussion(dsh 社区 Show Your Plugins!):https://github.com/deepseek-ai/deepseek-harness/discussions/10324
- 本仓库:https://github.com/HeliosLz/harness2-recursion
验证
要求 Node ≥ 24(node --test 直接 import src/index.ts,依赖类型剥离)。
npm install
npm run build # lib/index.js + lib/index.d.ts + lib/constants.js
npm run typecheck
npm run verify # task 15 + meta 14 + loader smoke 5 = 34 项
本次结果(2026-10-19):npm run verify 34/34 pass;typecheck/build exit 0;npm pack --dry-run 清单齐全;真实会话 E2E 通过(见下)。
第三轮新增(相对第二轮)
1. 事件 schema:TrialRecord 三个候选元数据字段必填(Q1);ProbeMetrics.tokenCostSource(Q4);ComposeAppliedPayload.appliedEdits(R3-1);ComposeRejectedPayload.rejectedCandidateIds(Q3)。
2. 账本 v3:{version,id,previousSnapshotRef,ledgerEntryId,event,payload,recordedAt};只写 v3,readLedgerTail 接受 v2|v3 并做内存映射、不重写 v2 文件(Q7)。
3. 归因规则:反射必须带 edit.candidateId,且与来源事实严格一致;写入的 edit 字段永远取自来源载荷(R3-3/Q2);不一致 → onError('reflection-edit-mismatch') 且拒绝写入。
4. confidence v3:base(0.6/0.5) × (0.5+0.5·min(1,trials/3)) × repeat(≤1.4) × sourcePenalty(estimated 0.9);trials=0 → 0 且不写入(Q4/Q5)。
5. 双注入阈值:positive 用 minConfidenceToInject,negative 用 minNegativeConfidenceToInject(默认 0.3,Q6)。
6. 组件表:COMPONENT_NAMES 八类冻结;7 项旧名迁移映射 + migratedFrom 审计;未知组件跳过并 onError('unknown-component');移除 workflow-control 回退(R3-7)。
7. token 计量:Propose 用 usage chunk;Probe/baseline 用可选 tokenMeter;否则字符数/4 估算;同一 metrics 单一来源。
8. 第四轮增量:新增终态事件 propose/failed(R4-1,与 Probe/Compose 互斥、append→emit、账本 revision 探测跳过它);rejectedCandidateIds 只取 trials 中出现的 id(R4-2 正式契约);outcome.tokenCostSource/migratedFrom 正式纳入 entry schema(R4-3)。
第三轮如实偏差(详见 CONTRACT §9 / SELF-CHECK §三)
- 无逻辑偏差:第三轮 Q3 冲突已由 propose/failed 独立终态解决(原 TODO(ROUND3_PROPOSE_FAILURE) 移除);预算早停的 rejected ids 与 entry 增量字段已追溯为正式契约。
- 环境依赖继续留 TODO/不验收:pre-step-patch 运行时 API(dsh 0.2.1 无)、Probe 工具沙箱、profile 安装(第四轮执行)。
第五轮:真实环境验证 + 社区材料
真实会话 E2E(已通过)
需要 dsh 0.2.1-rc.2 CLI 运行时(提取方式见 e2e/README.md)
E2E_CLI="<dsh-0.2.1-runtime>/dsh/node_modules/@deepseek-ai/dsh/lib/bin.js" node e2e/run.mjs
隔离 profile 内通过官方 dsh plugin add 安装两包,跑两次真实 headless 会话(假 provider:前 3 次主模型 500 → 触发 request-error 路径,随后延迟成功)。实测结果:
Ledger: 4 v3 records (2 probe/completed + 2 compose/applied)
applyMode: run1=noop-todo, run2=next-task-ledger
Revision anchor chain: null -> led-… -> led-… -> led-…
EXPERIENCE.md entries: 1 (confidence 0.50, measured tokens)
Sessions: 2 (exit 0)
- 首次 noop-todo 是冻结语义(插件激活时尚无账本可读);第二次会话由 readRevisionAnchor 升级为 next-task-ledger。
- 完整证据与复现步骤:e2e/README.md、e2e/artifacts/RESULTS.md。
- 卸载残留:loader smoke 第 5 项验证真实 Cordis dispose 后 7 个监听器全部释放、不再写账本/经验文件。
社区材料
- docs/discussion-proposal.md:Discussion 提案底稿(双插件契约、propose/failed 独立终态、元知识只改进编辑器三个差异点 + 34/34 证据 + 缺口 + 讨论问题)。
- docs/publishing.md:Phase 1 原型仓库/npm/Plugin Directory 发布清单。
- LICENSE(MIT)、.gitignore 已就绪;两包 keywords 含 dsh-plugin(发布前移除 private 并改用可发布包名)。
安装(第四轮/第五轮)
cd harness2-recursion && npm install && npm run build
然后在 Harness Web 的 Plugins 页分别安装两个本地 bundle(target 指向各包目录)。默认 observeOnly: true:只取证、不应用 Compose。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
HeliosLz/harness2-recursion
用任务探针与经验库让插件自我迭代改进
相关插件 · 开发 / 工程
隐私简历生成器reactive-resume/reactive-resume
Reactive Resume 已迁至 GitHub 上的…
★ 45,125已汉化MCP
71.7
AI 架构文档生成器sopaco/deepwiki-rs
🚀 Litho 已进化为 Terrain —— 给你的 AI 智能体一张会呼吸的代码库地图。
★ 3,157已汉化DSH
70.8
长时任务执行闭环AMAP-ML/LongHorizon-Harness
面向 Computer-Use Agent 的 Loop Engineering
★ 1,764已汉化DSH
70.5
AI 编程纪律方法包GanyuanRan/Aegis
让 AI 编程 agent 变得可信:少返工、更安全、说“完成”前先给证据。
★ 1,384已汉化DSH
69.6
多代理编排运行时Enderfga/claw-orchestrator
面向编程智能体的运行时。把 Claude Code、Codex、Antigravity、Grok…
★ 586已汉化MCP
69.4
记忆进化插件csyangwen/dsh-memory-evolve
dsh-memory-evolve 使用场景指南
★ 461已汉化DSH
67.9