dsh-plugin-tool-management 0.16.6 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +2 -1
  3. package/README_EN.md +2 -1
  4. package/docs/images/1-EN.png +0 -0
  5. package/docs/images/1.png +0 -0
  6. package/docs/images/2-EN.png +0 -0
  7. package/docs/images/2.png +0 -0
  8. package/docs/images/3-EN.png +0 -0
  9. package/docs/images/3.png +0 -0
  10. package/docs/images/4-EN.png +0 -0
  11. package/docs/images/4.png +0 -0
  12. package/docs/images/5-EN.png +0 -0
  13. package/docs/images/5.png +0 -0
  14. package/docs/images/6-EN.png +0 -0
  15. package/docs/images/6.png +0 -0
  16. package/docs/images/7-EN.png +0 -0
  17. package/docs/images/7.png +0 -0
  18. package/docs/images/8-EN.png +0 -0
  19. package/docs/images/8.png +0 -0
  20. package/docs/update.md +43 -0
  21. package/lib/audit-log.js +6 -2
  22. package/lib/client.js +44 -17
  23. package/lib/compat/probe.js +75 -24
  24. package/lib/context-inject.js +73 -7
  25. package/lib/host-names.js +150 -0
  26. package/lib/hub.js +82 -11
  27. package/lib/imports/parsers.js +40 -15
  28. package/lib/index.js +168 -74
  29. package/lib/mcp/loader-token.js +26 -0
  30. package/lib/mcp/manager.js +314 -87
  31. package/lib/mcp/patch-yaml.js +207 -40
  32. package/lib/mcp/secret-guard.js +122 -16
  33. package/lib/memories/archive-engine.js +21 -11
  34. package/lib/memories/index-io.js +125 -12
  35. package/lib/memories/service.js +163 -40
  36. package/lib/memories/snapshot.js +32 -9
  37. package/lib/op-registry.js +51 -8
  38. package/lib/ops/compat.js +6 -2
  39. package/lib/ops/prompts.js +18 -5
  40. package/lib/ops/scene-records.js +117 -10
  41. package/lib/ops/sessions.js +50 -4
  42. package/lib/ops/snapshot.js +105 -12
  43. package/lib/ops/trash.js +45 -9
  44. package/lib/prompts/service.js +7 -2
  45. package/lib/request-gate.js +115 -56
  46. package/lib/scene-prompt-sync.js +46 -7
  47. package/lib/scenes/candidates.js +21 -3
  48. package/lib/sessions/workspace.js +40 -5
  49. package/lib/skills/core.js +74 -4
  50. package/lib/skills/service.js +9 -4
  51. package/lib/subagents/service.js +6 -1
  52. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,82 @@
10
10
 
11
11
  ---
12
12
 
13
+ ## [0.17.0] - 2026-09-30
14
+
15
+ 对抗性审查收尾:三批(P0/P1/P2 表 19 条 + §5 脆弱性地图 F1–F12)与随后一轮复审(14 条发现 + 2 条替代项)全部处理完毕,逐条到终态;P0 / P1 每条都先复现坏行为再修。报告与终态处置表在 `review/`(本地工作稿,按 `.gitignore` 不入版本库)。
16
+
17
+ ### 修复
18
+
19
+ - **记忆索引:一次读失败不再等于「整份配置被清空」**(P0-1):读失败(EACCES / EBUSY)曾被当成「没有索引」,随后任何一次写都会把空索引落盘,界面 5 秒轮询的读操作也会触发。现在只有 ENOENT 算新用户,其余读失败一律按「不可信」处理(拒写 + 界面按全部未启用显示)。
20
+ - **场景回收站:含子目录 / bundle 记忆的场景恢复不再必然失败**(P0-2):负载名拒收带 `/` 的名字,而场景记忆天生是目录树 —— 删除能成功、恢复必失败且不可再恢复;删除回滚失败还会把回收站条目整条删掉。现在负载名支持安全相对路径,恢复失败退回回收站,回滚没做完绝不清理条目。
21
+ - **记忆回收站恢复不再能写到记忆根之外**(P0-3):条目里的组名是磁盘数据,`../..` 能把正文写到记忆根外。现在与回收站列表同一道判据,并在落盘前核对落点是否在根内。
22
+ - **整机迁移快照不再导出明文凭据**(P0-4):默认导出只认 `key` / `token` / `secret` / `password` 四个参数名,`?api_key=` / `?access_token=` / `?sig=` 等全部漏掉。现在与 MCP 列表同一套打码,并补上非绝对地址的兜底;`$VAR` / `!!js` 这类间接引用改回原样保留。
23
+ - **MCP 体检不再回未打码的地址**(P0-5):它是免令牌的只读操作,却把配置里的 URL 原文回给客户端。现在地址一律打码后回(主机与路径保留),命令名仍明文(与 `mcpm-list` 口径一致)。
24
+ - **注入取数失败不再伪装成「已清空」**(P1-8 / F2):某个域抛异常时被当成「这轮没内容」,还向模型发一条假的「已清空」,界面显示成「未投递」。现在异常与真的空分开:抛异常的域这轮不发,「注入实况」新增「取数失败」状态,兼容页那一行同时承载装配失败与运行期失败。
25
+ - **读失败不再改名用户文件**(本版自查):上一批把读失败也接到「改名留存」那条处置上,而读失败常是瞬时的 —— 等于把「重试即可恢复」升级成「记忆全没了」。现在两条路分开:解析失败照旧改名留存,读失败只标记不可信、不动文件,下次读成功自动解除。
26
+ - **场景改名 / 删除→恢复不再丢记忆的索引条目**(P1-1):改名没搬每条记忆的启停 / 排序 / 标签 / 置顶 / 备注,这些条目被当悬空引用清掉 —— 用户显式停用的记忆会自己重新启用。现在改名按前缀整体迁移,删除时一并收进回收站、恢复时合并回来。
27
+ - **删除 / 恢复场景不再跟随目录链接搬走根外文件**(P1-2,已实测复现):`memories/<场景>` 若是指向根外的链接,列目录会列出链接目标的内容,删除会真的把根外文件搬进回收站。现在动手前核对落点是否在根内,遍历时跳过链接。
28
+ - **MCP 导出不再谎报「hub 目录写入失败」**(P1-3):它把一份 JSON 交给了只接受补丁格式的写入器,校验必然失败且错误被吞,界面给的诊断是错的。改用普通 JSON 写入。
29
+ - **停用的 MCP 工具不再能被调用**(P1-4):`mcp__<server>__<tool>` 对 `__` 有歧义,而守卫取第一个 `__`、列表取最后一个、场景档案又取第一个 —— serverName 含下划线时守卫查表不命中即放行,这是**执行侧**失效。现在三处共用同一个切分函数 `splitMcpToolName`。
30
+ - **重复 loader id 守卫不再只扫 MCP 行**(P1-5):导入一条非 MCP 条目就能撞出重复 id,而同 id 会让插件组装失败、DSH 起不来。现在扫全部 insert 子条目(顶层覆盖块不算)。
31
+ - **整机迁移的技能域从「永远不工作」到能用**(P1-6):导出侧读错返回结构,技能永远导出 0 项而界面显示「迁移完成」;导入侧又把 zip 喂给只认目录和 `.md` 的入口。现在导出按正确结构展开,导入改走能吃 zip 的链路。
32
+ - **`history-delete` 收敛到「只能删已归档会话」**(P1-7):批量删除一直求交归档集合,单删却直接删 —— 一个客户端 bug 就能永久删除任意已保存会话。现在单删也先确认在归档集合里(查不到归档状态即拒绝)。
33
+ - **场景绑定的预设正文有了与 AGENTS.md 相同的注入预算**(P2-1):AGENTS.md 分支有 64 KiB 截断,场景分支一点上限都没有。现在复用同一套截断与提示。
34
+ - **全局 AGENTS.md 与场景基线快照改为原子写**(P2-2 / P2-16):AGENTS.md 是宿主每步都重读的文件,中断可能留下半截内容进模型上下文;基线快照损坏会让「退出场景恢复 AGENTS.md」静默失效。现在两者原子替换,快照读不出来时如实报警。
35
+ - **编辑 / 删除 MCP 不再删掉手写 `config` 的覆盖块**(P2-6):补丁按条目 id 生效,手写配置是合法用法,删除时却被整块带走。现在只删纯块;「启停值读不出来」(如 `!!js …`)也不再被当成显式启用。
36
+ - **去重读的不再是「另一个文件」**(P2-19):全局指令路径有两条来源,去重时用了不是宿主实际读的那一份,同一份预设正文被注入两遍。现在由调用方把宿主实际读的那一份喂进来。
37
+ - **保存场景档案不再抹掉别的场景**(P2-3):档案切片在写队列外读、队列内写,窗口里另一个写者的改动会被陈旧整片回写覆盖。现在每次写显式声明改哪几片,档案另走逐场景增量。
38
+ - **场景名口径统一:命名用单段、定位用完整路径**(P2-4):多段名(`a/b`)能通过校验并被写进启用集合,但注入时两边对不上,表现为「开关点了、场景没生效」且零报错。现在命名路径必须单段(多段当场报错),定位路径按完整路径寻址。
39
+ - **备份名加上毫秒与进程内序号**(P2-5):时间戳只到秒,同一次操作连写两遍时后一份覆盖前一份,「保留 5 份」实际变成「保留 5 个秒」。两代格式都认,存量备份仍会被清理。
40
+ - **行级编辑不再改坏块标量**(P2-7):折叠空行会把 3+ 连续换行折成 1 个,而块标量(`!!js |`)的内容就是换行与缩进 —— 折一次等于改写用户手写的表达式。现在跳过块标量内部,认不出形状时退化为旧行为。
41
+ - **`tools.restrict` 缺失时不再静默**(P2-8):全局 tools 面没有 `restrict` 时直接返回,停用工具仍留在模型可见的工具表里,兼容页还报「ok」。现在与另一处同 id、同文案地上报。
42
+ - **整机迁移导入不再吞掉各域的「需要注意」**(P2-9):只取成功 / 跳过数,于是「这些密钥已是打码占位符,请重新填写」到不了界面。现在透传并渲染(记忆域带上跳过原因,上限 5 条)。
43
+ - **`mcpmRestart` 不再把服务器留在停用态**(P2-11):某些形状下(条目自带 `disabled: true`、靠覆盖块启用)恢复步骤会把生效值回落到条目自带值。现在恢复前比对一次,不等就显式写回。
44
+ - **令牌操作不再把「覆盖条目」当成「第二份补丁」**(P2-12):判据只对 insert 条目成立,而 0.1.7 bundle 挂载下令牌的写点就是覆盖条目 —— 那种安装下所有令牌操作被全量拒绝。现在按形状分类,写点优先取覆盖条目。
45
+ - **不可解析的超时值不再写成 `NaN`**(P2-13):手写补丁里 `toolCallTimeoutMs` 被读成字符串,`Number("30s")` 直接把 `NaN` 落盘。现在认不出来就整行不写,`0` 也不写。
46
+ - **「已知工具」侧车的读-改-写整段进写锁**(P2-15):两个并发写者各读旧表、各并进自己那台服务器,后写的整份覆盖先写的。现在锁内读 → 合并 → 落盘。
47
+ - **技能上传不再能从暂存目录逃出去**(P2-17):路径校验只挡「正好是 `..`」的段,而 Windows 会裁掉段尾的空格,`.. ` 等于 `..`。现在按段判「会不会被文件系统改写」,正常点文件(`.DS_Store`)照常放行。
48
+ - **子智能体列表不再列出管不动的隐藏人设**(P2-18):隐藏名(`.foo`)被判 hidden,列表里却出现一个「看得见、改不了、删不掉」的人设。现在列表侧与校验侧同一道谓词。
49
+
50
+ ### 修复(复审)
51
+
52
+ - **`skill-trash-delete` 的路径穿越堵住**(F1,P0):`id` 直接拼进回收站目录路径,`id=".."` 就能把整个 `hub/trash` 交给系统回收站。现在走既有的单段名校验。
53
+ - **「读改写例外」不再被借道**(F2,P1):`tool-table` 免令牌的依据是「纯读」,而「保存方案 / 删除方案」走同一个操作名却会写侧车。现在写条件改为数据化声明:带写触发键就必须过令牌。
54
+ - **场景锁定对模型面生效**(F3,P1):冻结只包在界面那条入口上,锁着一个非活动场景时模型仍能改技能 / 记忆 / MCP / 子智能体 / 场景档案。现在两侧同一份判据;「锁着仍能解锁、仍能退出场景」用契约测试钉住。
55
+ - **打码 URL 不再被写回补丁**(F4,P1):新增 / 导入 MCP 拿到的是已打码的地址,写回时不做还原,`%3Credacted%3E` 真的落进补丁。现在所有写路径强制还原,还原不出来就报错要求重填。
56
+ - **打码链补上 userinfo 与 fragment**(F5,P1):`https://user:pass@host/…` 与 `…#access_token=abc` 原样输出,而这两种形态在托管服务里都常见。现在一并打码。
57
+ - **块标量识别按 YAML 规范补全**(F6,P1):识别只认 `|` / `>` 紧跟指示符的形态,`!!js |` / `&a |` / `|2-` 都不算,照样被折 = 改写手写表达式。现在按规范最小集补齐,另加「认不出的疑似块标量头 → 整份文件不折叠」。
58
+ - **注入通道的「取数失败」覆盖到域内部**(F7,P1):上一轮只堵住通道拿到异常那一层,而某个域自己就把 IO 错误吞成空串。现在域内部也把 IO 失败与真的空分开;文件不存在仍按正常空处理。
59
+ - **「首选」对模型真正生效**(F8,P1):界面与模型侧共用同一个分组函数,而 provider 那条路径没传 `preferred` —— 同名首选只影响界面显示。现在 provider 路径传上;首选来源处于停用时如实回报「已记录,但它不会生效」。
60
+ - **单删的归档判据取权威集合**(F9,P2):单删用有损投影(头部读不出来的会话被静默跳过),批量路径用权威集合 —— 同一份数据两条路结论相反。现在单删也取权威集合。
61
+ - **「只能删已归档」成为不变量**(F10,P2):级联删除按父子关系直接删、不问归档集合,删一个已归档父会话会连带删掉未归档的子会话。现在只吃已归档的子会话,读不出归档状态时什么都不级联。
62
+ - **记忆回收站恢复失败会回滚**(F11,P2):复制中途失败留下「半放回 + 目标已非空」,而条目还在 —— 下一次恢复撞「同名记忆已存在」,这条记忆再也恢复不了。现在失败时清掉落点并保留条目,可直接重试。
63
+ - **`.txt` 按内容嗅探,不再一律当纯文本**(F12,P2):改名成 `.txt` 的 JSONL / Markdown 转写会被整份折成一条 user 消息,而返回的是「导入成功」。现在先嗅探,认不出且行数不少时如实提示。
64
+ - **引用探测失败不再放行删除**(F13,P2):`refs()` 返回 `null` 时放行 —— 删掉被场景绑定的预设会静默让场景驱动失效。现在探测失败一律拒绝,并说明是「探测失败」而非「有引用」。
65
+ - **场景基线写失败不再被吞**(F14,P2):失败只写一行 `warn`,AGENTS.md 照旧切进场景态而没有退路,界面零提示。现在返回失败原因,与「AGENTS.md 未写入」分开上报。
66
+
67
+ ### 新增
68
+
69
+ - **兼容探测表新增 `fs.patch-io`**(F5):补丁文件的读写要经 `ctx.fs` 升级沙箱策略,五个方法任一改名会让 19 处补丁写路径一起报错。现在钉在兼容页上;「参数语义变了」探不到,如实分开。
70
+ - **挂载心跳带上宿主进程身份**(F1):额外记 `pid` 与 `hostStartedAt`,`check:host` 据此直接给结论(写心跳的进程还在 → 这一轮确实挂上了;已不在 → 宿主重启过)。局限:进程号会被复用。
71
+ - **doctor 新增 `AGENTS.md` 增量刷新的 drift canary**(F3):官方改成事件驱动 / 改 digest 口径时旧正文会继续注入且零报错。现在加一组源码标记检查,缺失时直说影响面。
72
+ - **`hostRootCandidates()` 成为「宿主在哪」的唯一口径**(F10):运行时、doctor、host-deps 此前各写一份,doctor 还少了桌面版归档那条(桌面宿主上会报「找不到宿主」或比出假的「两份不同拷贝」)。现在三处共用一份。
73
+ - **`mcpmListView` 上报 MCP 工具前缀的漂移**(F6):前缀抽成 `MCP_TOOL_PREFIX`(唯一一处)并加一条 inform-only 上报。判据是「`mcp` 词根后换了分隔符」且至少两个同名根的工具 —— 第一版没排除本插件自己的 `mcp_manager_*`,会让这条在每台机器上误报,已自查改掉。
74
+ - **服务器名的 `__` 歧义会出声了**(N2):`serverName` 允许含 `__`,于是「`a` 的 `b__c`」与「`a__b` 的 `c`」在全名上同形。本轮不收紧校验,改为加一条 inform-only 上报,两种形状取更具体的那条只出一行;判据是纯函数且零误报,只上报、不阻断、不自动改名。
75
+
76
+ ### 变更
77
+
78
+ - **两份 peer range 不再漂移,且明确「不设上界」**(F9 / §5.4):`probe.ts` 与 `package.json` 此前是两个写法(且前者 caret 隐含上界,与注释自相矛盾)。现在两处是同一个字符串并由契约测试钉住;无法用 range 消除的缺口(尚未枚举的新 rc 在 npm7+ 下仍会 ERESOLVE)写进注释。
79
+ - **契约测试共补 34 条不变量**(`test/contracts.test.mjs`,三批 18 条 + 复审 16 条):全部钉纯函数不变量与宿主契约,不加界面测试。
80
+
81
+ ### 说明
82
+
83
+ - **实测**:`npm run build`、`npm run check:i18n`(zh/en 各 1501 键)、`npm run typecheck:client`、`npm run lint` 通过;`node --test test/contracts.test.mjs` **67/67**(33 既有 + 34 新增)、`test/client-exports.test.mjs` 1/1;`doctor.mjs` / `host-deps.mjs` 正常跑完。
84
+ - **先复现再下结论**:复审 14 + 2 条每条都先复现坏行为,修完再把修复回退掉重跑同一份探针 —— 只证明「改完是绿的」不足以说明探针在判别。回退后转红数:F9 / F10 5 项、F11–F14 10 项、N1 7 项、N2 2 项。
85
+ - **N1 在本机是 no-op(如实标注)**:本机 7 个已配置 serverName、105 条已知工具,无一含 `__`,停用表为空 —— 三种切法逐条比对 0 条不一致,这次统一切法是为将来修的。**残余风险**:两个真实服务器名互为 `__` 前缀时,守卫(候选必须窄,否则 fail-open)与显示侧仍可能给出不同答案,由 N2 的上报把它变成可见提示。
86
+ - **未做的验证**:按 `AGENTS.md`,功能验证由用户在真实环境完成 —— 本次未在真实宿主里点过界面。三处只做了静态核对:`mcpmListView` 的前缀漂移上报、`webServer.register` 失败的运行期上报、备份名的新格式。
87
+ - **本版仍未处理的部分**:两份报告里没有了。两处明确不做、记录在案:① §5.4 余下两条属持续维护成本(每出一个宿主 rc 需手工同步 5 处、桌面宿主缺「布局漂移」上报),记在适配矩阵里;② **F15 不收紧**(拒绝含 `__` 的 `serverName`)—— 今天放行 `__` 的 5 处校验一旦收紧,存量服务器会从「能管」变成「管不了」,改由 N1 + N2 处理。
88
+
13
89
  ## [0.16.6] - 2026-09-30
14
90
 
15
91
  官方内置技能管理补全 web 侧:web 宿主照注的沙盒诊断技能接入技能页(可见、可停),Office 三件套按裁定在 web 上一并暴露;并补齐接管后缺失的 LibreOffice Kit 路径段。
package/README.md CHANGED
@@ -101,7 +101,7 @@ dsh plugin --profile web add dsh-plugin-tool-management@latest
101
101
  - **工具级开关**:单个工具可独立停用(模型看不见也调不到),整台支持批量。
102
102
  - **停着也能看清单**:没在跑的服务器仍显示上次见过的工具名与描述(标「上次运行时」);从没跑过的可以一键启动读取。
103
103
  - **备注**:每台服务器可写一句给模型的提示,随状态段一起注入。曾经连上过、现在连不上的服务器会列出来并标注 —— 这样模型会说"它没连上,检查一下",而不是建议你去装一个。
104
- - **密钥打码**:默认把凭据整值打码(`$VAR`、`!!js` 这类间接引用不动;URL 查询串换成 `?<redacted>`),看明文要令牌。打码只管界面展示,详见「配置与安全」。
104
+ - **密钥打码**:默认把凭据整值打码(`$VAR`、`!!js` 这类间接引用不动;URL 的查询串、`user:pass@` 与 `#片段` 一并打码),看明文要令牌。打码值**不会被写回配置** —— 保存时你原样留着的字段会还原成已存的值,地址里没有可还原的原值时整次保存会被拒绝(静默丢掉查询串会留下一个"连得上但认证失败"的地址)。打码只管界面展示,详见「配置与安全」。
105
105
  - **迁移与备份**:跨项目级 / 全局迁移失败自动回滚;支持 JSON 导出导入。
106
106
 
107
107
  ### 技能
@@ -170,6 +170,7 @@ dsh plugin --profile web add dsh-plugin-tool-management@latest
170
170
  - **curl / 脚本**:带 `x-dsh-token`,或带浏览器 cookie。
171
171
  - **HTTP 状态码**:除安全栅栏的 401 / 403 外,一律 HTTP 200 + `{ ok: false, error }` —— 脚本分流请以 `body.ok` 为准。
172
172
  - **端口转发**:宿主缺 `connection` 服务时栅栏退到「Host 回环 + 同源」判定,本机进程伪造 `Host: localhost` 即可调用写 op。转发到局域网 / 公网的场景**务必配令牌** —— 陌生人注入 MCP 命令等同远程执行。「选择文件夹」弹窗用的 `dir-list` 能列出任意绝对路径(只读),同一条前提。
173
+ - **服务器名里的 `__`(起名建议)**:工具全名是 `mcp__<服务器名>__<工具名>`,所以服务器名里再出现 `__` 会让它**切不唯一** —— `mcp__a__b__c` 既可能是服务器 `a` 的工具 `b__c`,也可能是服务器 `a__b` 的工具 `c`;两个服务器名互为 `__` 前缀(`a` 与 `a__b`)同理。本轮**不收紧校验**(今天放行 `__` 的 5 处校验一旦收紧,存量服务器会从"能管"变成"管不了",而服务器本身照跑),改为在**兼容页**出一条提示。工具照常可用,只是界面上的勾选可能落不到停用表里那条键上 —— **起名时避开 `__` 就没有这个问题**。
173
174
 
174
175
  ## 常见问题
175
176
 
package/README_EN.md CHANGED
@@ -102,7 +102,7 @@ Then remind me to hard-refresh the browser.
102
102
  - **Per-tool switches**: disable individual tools (invisible to the model and blocked at call); whole servers support bulk.
103
103
  - **Stopped servers still show their tools**: one that is not running keeps the names and descriptions last seen (marked "as of last run"); one that never ran can be probed with "start server to read tools".
104
104
  - **Status and notes enter the context**: currently usable servers are listed, and **one that connected before but cannot right now is listed and labelled too** — so the model says "it is not connected, check it" instead of reading "configured but unreachable" as "not configured" and suggesting you install one. Servers that never connected are omitted; your notes travel along as decision hints.
105
- - **Secret masking**: a credential is replaced **as a whole** by `••••••`, and only for keys that look sensitive; `$VAR` and `!!js` are indirections and stay readable, and a URL's query string becomes `?<redacted>`. "Reveal" needs a token. A masked value is never written back into the patch file — on save the plugin restores the stored value for any field you left alone, and refuses the whole save when a URL has nothing to restore from (a silently stripped query string would leave an address that connects but fails authentication).
105
+ - **Secret masking**: a credential is replaced **as a whole** by `••••••`, and only for keys that look sensitive; `$VAR` and `!!js` are indirections and stay readable, and a URL's query string, its `user:pass@` userinfo and its `#fragment` are all masked. "Reveal" needs a token. A masked value is never written back into the patch file — on save the plugin restores the stored value for any field you left alone, and refuses the whole save when a URL has nothing to restore from (a silently stripped query string would leave an address that connects but fails authentication).
106
106
  - **Migrate & back up**: cross-level migration rolls back on failure; JSON export/import.
107
107
 
108
108
  ### Skills
@@ -173,6 +173,7 @@ The plugin uses the host's own `@deepseek-ai/*` libraries at runtime — they mu
173
173
  - **curl / scripts**: send `x-dsh-token`, or carry the browser cookie.
174
174
  - **HTTP status convention**: except for the fence's 401 / 403, a missing token or a business failure is **HTTP 200 + `{ ok: false, error }`** — branch on `body.ok`, not on the status code.
175
175
  - **Port forwarding**: when the host has no `connection` service the fence falls back to "loopback Host + same origin", which any local process can imitate with `Host: localhost`. If you forward the port to a LAN or the public internet, **configure a token** — a stranger injecting MCP commands is remote code execution. The `dir-list` call behind the folder picker lists any absolute path (read-only) under the same precondition.
176
+ - **`__` in a server name (naming advice)**: a tool's full name is `mcp__<server>__<tool>`, so an extra `__` inside a server name makes it **ambiguous** — `mcp__a__b__c` can be server `a`'s tool `b__c` or server `a__b`'s tool `c`; the same goes for two server names that are `__`-prefix related (`a` and `a__b`). This release deliberately **does not tighten validation** (the 5 places that accept `__` today would turn existing servers from "manageable" into "unmanageable" while the servers themselves keep running); instead the Host tab shows an informational note. The tools keep working — only a checkbox in the UI may fail to land on the key used in the disabled-tools table. **Avoid `__` when naming and none of this applies.**
176
177
 
177
178
  ## FAQ
178
179
 
Binary file
package/docs/images/1.png CHANGED
Binary file
Binary file
package/docs/images/2.png CHANGED
Binary file
Binary file
package/docs/images/3.png CHANGED
Binary file
Binary file
package/docs/images/4.png CHANGED
Binary file
Binary file
package/docs/images/5.png CHANGED
Binary file
Binary file
package/docs/images/6.png CHANGED
Binary file
Binary file
package/docs/images/7.png CHANGED
Binary file
Binary file
package/docs/images/8.png CHANGED
Binary file
package/docs/update.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  | 版本 | 发布 | 主题 |
6
6
  | ---------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
+ | [0.17.0](#0170---2026-09-30) | 2026-09-30 | 对抗性审查收尾(三批 19 条 + 复审 14 条 + 2 条替代项,全部处理):修好 3 处数据丢失与 2 处凭据外泄、停用 MCP 工具仍可调用等**执行侧**守卫失效、场景回收站与记忆索引的恢复问题;MCP 工具全名的切分收敛成一套规则、服务器名含 `__` 的歧义会出声提示 |
7
8
  | [0.16.6](#0166---2026-09-30) | 2026-09-30 | 官方内置技能补全 web 侧:web 照注的沙盒诊断技能可见可停 + Office 三件套 web 也暴露 + 补上接管后缺失的 LibreOffice Kit 路径段(0.16.5 桌面已缺) |
8
9
  | [0.16.5](#0165---2026-09-30) | 2026-09-30 | 桌面版误报 5 条「两份拷贝」修掉(身份逐包核对)+ 「全部可用」与红卡不再同屏 + 官方内置技能接入管理(桌面版注入的 4 个可启停;web 不出现) |
9
10
  | [0.16.0](#0160---2026-09-29) | 2026-09-29 | 桌面版宿主身份误判修好(归档宿主按版本核对,带空格路径的宿主版本不再显示 unknown)+ 兼容页三张指标卡各删一行、适配版本改标 0.2.0-rc.2 + 四个域的行状态统一成名字后面的胶囊(已启用 / 未启用,停用类改灰)+ MCP 体检点挪到名字前 + 体检补端点探测 + 删除同步清引用 + 轮询不空转 + 子智能体补上「已启用」 |
@@ -26,6 +27,48 @@
26
27
 
27
28
  ---
28
29
 
30
+ ## 0.17.0 - 2026-09-30
31
+
32
+ - **记忆索引:一次读失败不再等于「整份配置被清空」**:读不到(杀软 / 索引器占住)时以前当成「没有索引」,随后任何一次写都会把空索引落盘 —— 打开面板就可能触发。现在只有文件真的不存在才算新用户,其余读失败一律拒写并如实显示。
33
+ - **场景回收站:含子目录 / bundle 记忆的场景恢复不再必然失败**:删除能成功、恢复必失败且不可再恢复;删除回滚失败还会永久删掉那些记忆。现在恢复失败会退回回收站,回滚没做完绝不清理条目。
34
+ - **回收站恢复不再能写到记忆根之外**:条目里的组名是磁盘数据,`../..` 能把正文写到记忆根外。现在与列表同一道判据,并在落盘前核对落点。
35
+ - **整机迁移快照不再导出明文凭据**:默认导出只认 `key` / `token` / `secret` / `password`,`?api_key=` / `?access_token=` / `?sig=` 这类常见写法全部漏掉。现在与 MCP 列表同一套打码。
36
+ - **MCP 体检不再回未打码的地址**:它是免令牌的只读操作,却把配置里的 URL 原文回给客户端。现在地址一律打码后回。
37
+ - **停用的 MCP 工具不再能被调用**:工具全名 `mcp__<服务器>__<工具>` 对 `__` 有歧义,判断「停用了吗」的地方取第一个 `__`,服务器名含下划线时查不到就当成没停用 —— 照常可调用。现在全插件只剩一套切分规则,执行侧拦截、列表页、场景档案的勾选键三处同源。
38
+ - **注入取数失败不再伪装成「已清空」**:某个域取数抛异常时以前被当成「这轮没内容」,还会向模型发一条假的「已清空」。现在异常与真的空分开,「注入实况」多一个「取数失败」状态。
39
+ - **场景改名 / 删除→恢复不再丢记忆的元数据**:改名没搬每条记忆的启停 / 排序 / 标签 / 置顶 / 备注,于是你显式停用的记忆会自己重新启用。现在改名按前缀整体迁移,删除时一并收进回收站、恢复时合并回来。
40
+ - **删除 / 恢复场景不再跟随目录链接搬走根外文件**(已实测复现):`memories/<场景>` 若是指向别处的链接,删除会真的把根外文件搬进回收站。现在动手前先核对落点。
41
+ - **单删会话收敛到「只能删已归档的」**:批量删除一直是这个口径,单删却直接删 —— 一个客户端 bug 就能永久删除任意已保存的会话。现在单删也先确认在归档列表里;级联删除同样只吃已归档的子会话。
42
+ - **编辑 / 删除 MCP 不再删掉你手写 `config` 的覆盖块**:补丁按条目 id 生效,手写配置是合法用法,删除时却被整块带走。现在只删「纯块」。
43
+ - **编辑 MCP 不再改坏补丁里的多行脚本**:折叠空行会动到块标量(`!!js |`)内部 —— 补丁照旧能读、照旧能跑,只是算出来的值变了。现在折叠跳过块标量内部。
44
+ - **重启 MCP 服务器不再把它留在停用态**:某些形状下恢复步骤会把生效值回落到条目自带值,重启完服务器是关着的。现在恢复前比对一次,不等就写回重启前的状态。
45
+ - **设置令牌不再被误判成「两份补丁」**:bundle 挂载下令牌的落点是一条覆盖条目,却被算成「第二条」—— 那种安装下所有令牌操作都被拒绝。现在按形状分开。
46
+ - **保存场景档案不再抹掉别的场景**:进场景时那段窗口里保存的另一个场景档案会被陈旧数据整片覆盖。现在每次写声明改哪几片,档案另走逐场景增量。
47
+ - **场景名口径统一**:多段名(`a/b`)能通过校验并被写进启用集合,但注入时两边对不上,表现为「开关点了、场景没生效」且零报错。现在新建 / 改名目标 / 启用集合必须单段,定位按完整路径。
48
+ - **全局 AGENTS.md 与场景基线快照改为原子写**:以前中断 / 断电可能留下半截内容进模型上下文;快照损坏会让「退出场景恢复」静默失效。现在原子替换,快照读不出来时如实报警。
49
+ - **整机迁移的技能域从「永远不工作」到能用**:导出侧读错返回结构,技能永远导出 0 项而界面显示「迁移完成」;导入侧又把 zip 喂给只认目录和 `.md` 的入口。现在两侧一起修好。
50
+ - **迁移导入的「需要注意」不再被吞掉**:只报成功 / 跳过数,「这些密钥已是打码占位符,请重新填写」这类要你动手的清单到不了界面。现在会跟着那行一起显示。
51
+ - **技能上传不再能从暂存目录逃出去**:路径校验只挡「正好是 `..`」,而 Windows 会裁掉段尾空格,`.. ` 等于 `..`。现在按段判「会不会被文件系统改写」。
52
+ - **兼容页新增「宿主文件读写」探测**;**`check:host` 新增「AGENTS.md 刷新契约」检查、挂载心跳带上进程身份**(直接判「这一轮插件挂上没」);**doctor 与宿主依赖工具不再各写一套「宿主在哪」**。
53
+ - **MCP 工具名前缀只有一处定义,并会在漂移时提示**;**服务器名含 `__` 时会出声提示**(只提示、不阻断,没有这类名字时一条都不报)。
54
+
55
+ ### 复审那一轮(14 条发现 + 2 条替代项)
56
+
57
+ - **回收站条目 id 不再能路径穿越**:传 `..` 就能把整个 `hub/trash` 交给系统回收站。现在走同一道单段名校验。
58
+ - **「纯读」的名义不再能借来写盘**:`tool-table` 的「保存方案 / 删除方案」走同一个操作名却会写配置,带上就能免令牌改侧车。现在带写触发键就必须过令牌。
59
+ - **场景锁定对模型也生效了**:以前「冻结」只包在界面那条入口上,锁着场景时界面点不动、模型改得动。现在两侧同一份判据(锁着仍能解锁、仍能退出)。
60
+ - **打码地址不再被写回配置**:表单里填的是列表页给的已打码地址,写回时不做还原,真地址从此找不回来。现在所有写路径强制还原,还原不出来就报错让你重填;打码也补上了 `user:pass@` 与 `#access_token=` 两种写法。
61
+ - **编辑 MCP 不再改坏带 tag / anchor 的多行脚本**:`!!js |`、`&a |`、`|2-` 这些写法没被识别成块标量,照样被折。现在按 YAML 规范补齐,认不出形状时整份文件不折叠。
62
+ - **注入假「已清空」补完到域内部**:某些域自己就把读失败吞成空串,通道照样发那条假的「已清空」。现在域内部也把读失败与真的空分开。
63
+ - **「同名首选」对模型真正生效**:首选以前只影响界面显示,模型拿到的仍是按优先级选出的那份;把停用副本设为首选还会「两份都不生效」。现在两边同源,首选来源停用时如实告诉你「已记录,但它不会生效」。
64
+ - **单删会话的归档判据取权威来源**:以前用「读得出来的归档记录」,头部读不出来的已归档会话会被误报「未归档」。现在取权威集合。
65
+ - **记忆回收站恢复失败会回滚**:中途失败留下「一半已放回 + 目标已非空」,下一次恢复撞「同名记忆已存在」,这条记忆再也恢复不了。现在清掉落点、保留条目,可直接重试。
66
+ - **改名成 `.txt` 的转写不再被折成一条消息**:以前一律按纯文本解析,整份对话塞进一条 user 消息而结果报「导入成功」。现在先按内容嗅探,认不出就如实提示。
67
+ - **删预设前先确认没被引用**:探测失败时以前放行删除,删掉被场景绑定的预设会静默让场景驱动失效。现在探测失败一律拒绝。
68
+ - **退出场景「没有退路」时不再静默**:存基线失败时以前只写一行日志,AGENTS.md 照旧切进场景态而恢复不回原内容。现在单独报出来。
69
+
70
+ ---
71
+
29
72
  ## 0.16.6 - 2026-09-30
30
73
 
31
74
  - **web 上注入的沙盒诊断技能看得见、关得掉了**:官方宿主的沙盒技能门控只看 Windows(web 照样向每个会话注入),0.16.5 却把 web 挡在官方内置管理之外——页面不显示、会话照注入。现在 web 也物化官方技能、以更高优先级接管,停用即不注入。
package/lib/audit-log.js CHANGED
@@ -12,7 +12,7 @@
12
12
  // ③ readonly 与失败/抛错的调用不记 —— 记的是「变更成功」,不是「有人试过」。
13
13
  import { appendFile, readFile, stat, unlink, writeFile } from 'node:fs/promises';
14
14
  import { hubPath } from './hub.js';
15
- import { OP_REGISTRY } from './op-registry.js';
15
+ import { OP_REGISTRY, isReadCall } from './op-registry.js';
16
16
  const AUDIT_FILE = 'audit.jsonl';
17
17
  /** 轮转阈值:超过 512KB 保留最近 500 条。这页的本意是「最近改动」,不是无限期审计。 */
18
18
  const ROTATE_OVER_BYTES = 512 * 1024;
@@ -64,9 +64,13 @@ export function recordAudit(entry) {
64
64
  /**
65
65
  * 一次 op 结果要不要记:写类 + 本次成功(`ok !== false`,与服务端「成功返回扁平 {ok:true,…}」
66
66
  * 的口径一致;不返回 ok 的按成功处理)。
67
+ *
68
+ * 「两态 op」(`inject-settings` / `tool-table` / `scene-settings` / `mcpm-settings`)的读侧
69
+ * 不记:`isWriteClassOp` 只看 op 名,而那几个名字既读又写 —— 光打开一次兼容页就会被记成
70
+ * 一次改动,把真变更淹没。判据与写门禁同源(登记表的 `writeWhen`),不在这里另写一份。
67
71
  */
68
72
  export function recordOpAudit(op, args, source, result) {
69
- if (!isWriteClassOp(op) || AUDIT_SKIP.has(op))
73
+ if (!isWriteClassOp(op) || AUDIT_SKIP.has(op) || isReadCall(op, args))
70
74
  return;
71
75
  if (result && typeof result === 'object' && result.ok === false)
72
76
  return;