dsh-plugin-tool-management 0.9.1 → 0.11.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 (79) hide show
  1. package/CHANGELOG.md +119 -1
  2. package/README.md +227 -201
  3. package/README_EN.md +227 -199
  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 +144 -12
  21. package/lib/client.js +8437 -5929
  22. package/lib/compat/preset-reach.js +1 -10
  23. package/lib/compat/probe.js +158 -25
  24. package/lib/context-inject.js +396 -53
  25. package/lib/host-names.js +12 -0
  26. package/lib/http-fence.js +35 -15
  27. package/lib/hub.js +31 -3
  28. package/lib/imports/parsers.js +15 -9
  29. package/lib/imports/upload.js +43 -4
  30. package/lib/index.js +940 -3765
  31. package/lib/mcp/loader-token.js +238 -0
  32. package/lib/mcp/manager.js +1681 -0
  33. package/lib/mcp/override-blocks.js +20 -9
  34. package/lib/mcp/patch-yaml.js +351 -0
  35. package/lib/mcp/secret-guard.js +145 -0
  36. package/lib/mcp/state-section.js +64 -21
  37. package/lib/{rules → memories}/archive-engine.js +65 -10
  38. package/lib/{rules → memories}/archive.js +6 -7
  39. package/lib/memories/constants.js +128 -0
  40. package/lib/memories/index-io.js +330 -0
  41. package/lib/memories/projection.js +280 -0
  42. package/lib/memories/service.js +686 -0
  43. package/lib/memories/snapshot.js +672 -0
  44. package/lib/ops/candidates.js +64 -0
  45. package/lib/ops/compat.js +136 -0
  46. package/lib/ops/ctx.js +9 -0
  47. package/lib/ops/memory.js +678 -0
  48. package/lib/ops/prompts.js +107 -0
  49. package/lib/ops/scene-records.js +460 -0
  50. package/lib/ops/scene-sync.js +17 -0
  51. package/lib/ops/sessions.js +603 -0
  52. package/lib/ops/trash.js +140 -0
  53. package/lib/paths.js +103 -0
  54. package/lib/prompts/preset-id.js +49 -0
  55. package/lib/{agents-md → prompts}/service.js +1 -1
  56. package/lib/request-gate.js +320 -0
  57. package/lib/scene-prompt-sync.js +4 -4
  58. package/lib/scenes/candidates.js +344 -0
  59. package/lib/{history → sessions}/bridge.js +22 -5
  60. package/lib/sessions/history.js +323 -0
  61. package/lib/{history → sessions}/tombstone.js +1 -2
  62. package/lib/{history → sessions}/workspace.js +92 -24
  63. package/lib/skills/catalog.js +6 -9
  64. package/lib/skills/core.js +79 -43
  65. package/lib/skills/readonly-discovery.js +4 -1
  66. package/lib/skills/service.js +98 -13
  67. package/lib/subagents/catalog.js +31 -15
  68. package/lib/subagents/service.js +506 -89
  69. package/lib/subagents/tools.js +29 -4
  70. package/lib/tools/deps.js +8 -0
  71. package/lib/tools/mcp.js +110 -0
  72. package/lib/tools/memory.js +87 -0
  73. package/lib/tools/prompt.js +70 -0
  74. package/lib/tools/skills.js +139 -0
  75. package/lib/tools/subagent.js +40 -0
  76. package/package.json +105 -102
  77. package/lib/agents-md/preset-id.js +0 -49
  78. package/lib/history/projcache.js +0 -335
  79. package/lib/rules/service.js +0 -2971
@@ -0,0 +1,64 @@
1
+ // 场景档案与候选源域的 HTTP ops(2026-09-19 从 index.ts 的 handlers 表抽出)。
2
+ //
3
+ // 三个 op 都是**只读候选源**:preset-tools(人设表单的工具候选)、model-candidates
4
+ // (宿主 LLM 目录)、scene-inventory(场景档案勾选器的数据源 v2)。
5
+ export function buildCandidateOps(deps) {
6
+ return {
7
+ // 人设表单的两个候选源之一(只读,均为「按需拉取」——不进 scene-inventory,避免每次开
8
+ // 档案弹窗都枚举预设)。
9
+ // preset-tools:全体 Agent 预设工具名并集 + 各工具所属预设 + 当前会话是否可见。
10
+ // `unavailable` = 本插件自己没注册上的工具:工具列表接口是唯一按名字枚举工具的地方,
11
+ // 注册失败只能在这里被如实回答。
12
+ 'preset-tools': async () => ({
13
+ ok: true,
14
+ ...(await deps.presetToolCandidates()),
15
+ unavailable: deps.getToolFailures().map((f) => ({ ...f })),
16
+ }),
17
+ // model-candidates:宿主 LLM 目录里的 (provider, model) 对(不发网络请求)。
18
+ 'model-candidates': async () => ({ ok: true, ...(await deps.modelCandidates()) }),
19
+ // 场景档案勾选器数据源 v2:全部 MCP 服务器(含未运行)+ 技能全集 + 人设清单。
20
+ 'scene-inventory': async () => {
21
+ const [rowsR, tools, skills, subs] = await Promise.all([deps.mcpmListView(), deps.toolStates(), deps.skillRows(), deps.subagentList()]);
22
+ const disabledRaw = await deps.readDisabledTools();
23
+ const rows = (rowsR && rowsR.rows) || [];
24
+ const mcpServers = [];
25
+ const seen = new Set();
26
+ for (const row of rows) {
27
+ const name = String((row && row.serverName) || '');
28
+ if (!name || seen.has(name))
29
+ continue;
30
+ seen.add(name);
31
+ const live = !!(row.live && row.live.enabled);
32
+ mcpServers.push({
33
+ name,
34
+ level: row.level || null,
35
+ live,
36
+ serverDisabled: !!row.disabled,
37
+ // 停用表里的 ['*'] = 整台工具停用(勾选器据此预勾,避免把"全停"读成"全启用")。
38
+ allToolsDisabled: (disabledRaw[name] || []).indexOf('*') >= 0,
39
+ // 未运行且没有任何已知工具 ⇒ null(客户端显示「工具数未知」并引导读取),
40
+ // 不再给出误导性的 0。
41
+ toolCount: live || Number(row.toolCount) > 0
42
+ ? (typeof row.toolCount === 'number' ? row.toolCount : null)
43
+ : null,
44
+ });
45
+ }
46
+ return {
47
+ ok: true,
48
+ mcpServers,
49
+ tools: Object.entries(tools).map(([key, enabled]) => ({ key, enabled })),
50
+ // 技能行:技能名与来源名分开给(早先只给 `<来源 key>/<技能名>`,自定义目录的
51
+ // 来源 key 是 `custom-<hash>`,于是导入的技能在场景里显示成一串哈希)。
52
+ skills,
53
+ // 人设行要显示"按模式配了什么限制",所以把按模式名单一起带上;
54
+ // presets 只给名字与来源(不枚举工具,避免打开档案弹窗就挂载每个预设)。
55
+ subagents: subs.map((p) => ({ name: p.name, description: p.description, toolsByPreset: p.toolsByPreset ?? null })),
56
+ presets: await deps.presetNames(),
57
+ // 档案编辑器第 4 段「记忆」的候选:场景 + 每个场景里的记忆条数。
58
+ // 记忆正文不在这里返回(勾选集只存 id,渲染时才读盘)。
59
+ scenes: await deps.memorySceneCandidates(),
60
+ memories: await deps.memoryCandidates(),
61
+ };
62
+ },
63
+ };
64
+ }
@@ -0,0 +1,136 @@
1
+ // 兼容 / 注入 / 备份域的 HTTP ops(2026-09-19 从 index.ts 的 handlers 表抽出)。
2
+ //
3
+ // 为什么抽出来:index.ts 的 apply() 闭包里塞了 40 个 op 的内联实现,改一处要在 700 行
4
+ // 的对象字面量里找。这里按域收成一个 `buildCompatOps(deps)` —— 依赖全部显式传参,
5
+ // 不再靠闭包隐式捕获,各域可以单独读、单独改。
6
+ //
7
+ // 边界纪律:本文件只负责「op 的实现」,不负责 ops 表之外的接线(MCP 写后刷新、场景锁定
8
+ // 守卫仍作用在组装后的整张表上,见 index.ts)。
9
+ import { EXPECTED_MIN_HOST_VERSION, EXPECTED_PEER_RANGE, VERIFIED_HOST_VERSION, summarize, } from '../compat/probe.js';
10
+ import { assessPresetReach } from '../compat/preset-reach.js';
11
+ import { pluginLog } from '../skills/service.js';
12
+ export function buildCompatOps(deps) {
13
+ return {
14
+ 'compat-status': async (args) => {
15
+ const force = Boolean(args && args.refresh);
16
+ try {
17
+ const registry = deps.getSessionsRegistry();
18
+ const assessment = registry && typeof registry.capabilities === 'function'
19
+ ? registry.capabilities(force)
20
+ : undefined;
21
+ if (assessment === undefined) {
22
+ return {
23
+ ok: true,
24
+ host: { version: 'unknown' },
25
+ findings: [],
26
+ degraded: [],
27
+ blockers: ['归档服务未挂载:无法探测宿主能力'],
28
+ summary: '宿主能力探测不可用(归档服务未挂载)',
29
+ };
30
+ }
31
+ const summary = summarize(assessment);
32
+ // 每次刷新都留一条结构化日志:升级当天就能在日志里看到降级发生。
33
+ if (force || !deps.compatLog.version || deps.compatLog.version !== assessment.identity.version) {
34
+ deps.compatLog.version = assessment.identity.version;
35
+ void pluginLog()('compat/probe', {
36
+ hostVersion: assessment.identity.version,
37
+ summary,
38
+ degraded: assessment.degraded.map((item) => ({ id: item.id, state: item.state, detail: item.detail })),
39
+ blockers: assessment.identity.blockers,
40
+ sameAsHost: assessment.identity.sameAsHost,
41
+ }).catch(() => { });
42
+ }
43
+ return {
44
+ ok: true,
45
+ host: { version: assessment.identity.version, modules: assessment.identity.modules },
46
+ sameAsHost: assessment.identity.sameAsHost,
47
+ unverified: assessment.identity.unverified,
48
+ findings: assessment.findings,
49
+ degraded: assessment.degraded,
50
+ blockers: assessment.identity.blockers,
51
+ mayDelete: assessment.mayDelete,
52
+ verifiedVersion: VERIFIED_HOST_VERSION,
53
+ expectedPeerRange: EXPECTED_PEER_RANGE,
54
+ minHostVersion: EXPECTED_MIN_HOST_VERSION,
55
+ generatedAt: assessment.generatedAt,
56
+ summary,
57
+ };
58
+ }
59
+ catch (e) {
60
+ return { ok: false, error: deps.message(e) };
61
+ }
62
+ },
63
+ // 预设可达性矩阵:每个 Agent 预设下,本插件的注入类能力到不到得了模型。
64
+ // 只读预设组合文本,不挂载任何预设、不改任何数据。回答的是"面板上说注入了,
65
+ // 模型真的看得到吗" —— 压制型预设(persona complete / includeRuntimeContext:false,
66
+ // 如极简)默认不注入,可在「注入」设置里改成强制注入。
67
+ 'preset-reach': async () => {
68
+ try {
69
+ // 宿主平面的 MCP 工具数:四个官方预设都不在组合里挂 MCP,MCP 由
70
+ // $DSH_HOME/cordis.patch.yml 这一层挂载,所以它对所有预设一视同仁;
71
+ // 这一列要回答的只是"现在有没有 MCP 工具可用"。探测失败 → 不传该字段,
72
+ // 矩阵那一格显示"判断不了"而不是猜。
73
+ let mcpTools;
74
+ try {
75
+ const schemas = (await deps.tools.schemas());
76
+ mcpTools = schemas.filter((s) => String(s && s.name || '').startsWith('mcp__')).length;
77
+ }
78
+ catch {
79
+ mcpTools = undefined;
80
+ }
81
+ const settings = await deps.readInjectSettings();
82
+ const report = await assessPresetReach(deps.presetRoster(), {
83
+ mcpTools,
84
+ inject: { underSuppressingPresets: settings.underSuppressingPresets, domains: settings.domains },
85
+ });
86
+ return { ok: true, ...report };
87
+ }
88
+ catch (e) {
89
+ return { ok: false, error: deps.message(e) };
90
+ }
91
+ },
92
+ // 注入设置(读 / 写):压制型预设下是否仍然注入 + 各域开关。界面在「兼容」页。
93
+ 'inject-settings': (args) => deps.injectSettingsOp(args),
94
+ // patch 备份(改宿主配置前的整份副本):列清单只读,清理按写门禁。
95
+ // 每份备份里都是**明文**凭据,所以给一个显式出口让人能删掉多余的副本(理由见
96
+ // cleanPatchBackups 的注释)。清单不带文件内容,只给名字 / 层级 / 时间 / 大小。
97
+ 'backups-list': async () => {
98
+ const files = await deps.listPatchBackups();
99
+ return { ok: true, files, total: files.length, totalSize: files.reduce((s, f) => s + f.size, 0) };
100
+ },
101
+ 'backups-clean': async (args) => {
102
+ // 口径是**每个层级各删最旧的几份**:`del` 是 `{ 层级: 份数 }`,键与 listPatchBackups
103
+ // 给的层级标签同源(global / profile:<名> / legacy)。认不出的键天然落空(没有这个
104
+ // 层级就没得删),非法值一律忽略 —— 参数畸形时**一份都不删**,这是安全的默认。
105
+ const raw = (args && args.del) || {};
106
+ const del = {};
107
+ if (raw && typeof raw === 'object') {
108
+ for (const key of Object.keys(raw)) {
109
+ const n = Number(raw[key]);
110
+ if (Number.isSafeInteger(n) && n >= 1 && n <= 20)
111
+ del[key] = n;
112
+ }
113
+ }
114
+ const r = await deps.cleanPatchBackups(del);
115
+ // 失败原因(文件被占用等)由界面按当前语言拼:这里只回结构化字段。回中文串会让
116
+ // 英文界面原样露出中文(与 compat-status 的 summary 同一条纪律)。
117
+ return {
118
+ ok: true, removed: r.removed.length, kept: r.kept,
119
+ ...(r.failed.length ? { failedCount: r.failed.length, failedNames: r.failed } : {}),
120
+ };
121
+ },
122
+ // 注入实况(只读):最近活跃会话里模型**真正看到**的五域文本 + 本次运行的投递统计。
123
+ // 回答"勾了开关到底送没送到"——界面配置与实际注入不一致时,这里一眼可见。
124
+ 'injection-live': async () => {
125
+ const live = deps.getContextInjectorLive();
126
+ if (live === null)
127
+ return { ok: false, error: '注入通道未装配' };
128
+ try {
129
+ return { ok: true, ...live() };
130
+ }
131
+ catch (e) {
132
+ return { ok: false, error: deps.message(e) };
133
+ }
134
+ },
135
+ };
136
+ }
package/lib/ops/ctx.js ADDED
@@ -0,0 +1,9 @@
1
+ // 记忆域 op 的**显式上下文契约**(2026-09-19 抽出,配合 memories/service.ts 组装)。
2
+ //
3
+ // 为什么要有它:24 个 op 原先写在 createMemoriesService 闭包里,靠闭包隐式捕获读模型
4
+ // (snapshot / sceneMemory / scenePrompt)、写管道(invalidateSnapshot)、入口
5
+ // (parseId / locateRule / buildProjected)与三个路径配置。抽成独立文件后,这些依赖必须
6
+ // 显式传入 —— 本文件就是那份清单,签名与原声明逐字对应(改这里等于改 op 的可见面)。
7
+ //
8
+ // 刻意不做成 `typeof rc`:那会把契约藏回 service.ts,域文件又得反向引它。
9
+ export {};