dsh-plugin-tool-management 0.8.0 → 0.8.5
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.
- package/CHANGELOG.md +27 -0
- package/README.md +3 -0
- package/README_EN.md +3 -0
- package/docs/update.md +14 -0
- package/lib/client.js +98 -45
- package/lib/compat/probe.js +6 -4
- package/lib/index.js +220 -23
- package/lib/mcp/state-section.js +1 -1
- package/lib/rules/archive-engine.js +54 -3
- package/lib/rules/archive.js +23 -1
- package/lib/rules/service.js +14 -2
- package/lib/skills/core.js +32 -0
- package/lib/skills/service.js +36 -10
- package/lib/subagents/catalog.js +2 -1
- package/lib/subagents/service.js +14 -16
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,33 @@
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [0.8.5] - 2026-09-16
|
|
14
|
+
|
|
15
|
+
### 新增
|
|
16
|
+
|
|
17
|
+
- 场景级 MCP 服务器备注:场景档案的 MCP 段可为每台勾选的服务器写专属备注,进入该场景时覆盖全局备注(写入 notes.json),退出场景时按快照恢复改动前的备注;未配场景备注的服务器照常使用全局备注。
|
|
18
|
+
- 未运行服务器的工具可见性:新增「已知工具」侧车(`dsh-plugin-tool-management-mcp-tools.json`),枚举到 live 工具时按服务器记下名单——未运行的服务器不再永远显示 0 工具,工具勾选框也能列出最后一次见过的工具。从未运行过的服务器可在编辑弹窗点「启动服务器读取工具」:临时启用、轮询等工具注册(≤30s)、记入名单后恢复原启停状态。
|
|
19
|
+
|
|
20
|
+
### 变更
|
|
21
|
+
|
|
22
|
+
- 新增技能 / 子智能体 / MCP 一律默认不启动(用户裁定,三域同口径):新建与导入的技能显式写入停用名单;新建/导入/回收站恢复的人设默认停用;新增 MCP 服务器创建即停用(显式传 `enabled: true` 才创建即启动)。启动与否由用户手动开启。
|
|
23
|
+
- 场景档案 MCP「编辑」弹窗重做:行内备注输入框移除,「选工具」改为「编辑」,点击打开固定尺寸次级弹窗——第一栏备注、第二栏工具勾选(含全选/清空),有备注的服务器行显示「有备注」标签。
|
|
24
|
+
- 系统提示词注入段文案优化:子智能体人设段措辞改为「可从下面的人设中选用」(此前读起来像"只能调用这几个"),并补充委派机制说明;场景记忆段标题下加固定引导语,让模型明确这是当前场景的常驻参考知识;MCP 备注前缀由「用户备注:」统一为「备注:」。
|
|
25
|
+
- peer 依赖去掉 semver 上界(`>=0.1.5-rc.2 <0.2.0-0` → `>=0.1.5-rc.2`):宿主跨 minor 升级不再被 pnpm peer 校验挡住安装,兼容性改由运行时能力探测兜底。
|
|
26
|
+
|
|
27
|
+
### 修复
|
|
28
|
+
|
|
29
|
+
- 历史页「已移除」标签红灰两色统一为灰色:此前红色 = 从未登记、灰色 = 曾登记被移除,颜色区分无实际意义。
|
|
30
|
+
- 目录真实存在但没有「重新登记」按钮:`canRegister` 判定从「宿主会话索引命中」放宽为「磁盘目录真实存在」(带 stat 缓存,同一目录只探一次盘),与 `registerWorkspace` 的 realpath 目录校验一致——目录在即可一键重新登记;目录已删除的组显示「⚠ 目录已不存在」提示。
|
|
31
|
+
- MCP 状态段缓存不刷新:MCP 工具加载完成(`tools/change`)与进/退/切换场景后均主动重算状态段——此前段停留在「空/旧集合」的缓存值,出现「切换 MCP 后系统提示词没有 MCP 段」「退出场景后备注看似没恢复」的现象。
|
|
32
|
+
- 退出场景备注永不回退:rules-index.json 读盘时的快照规范化按白名单解析,漏掉了 `snapshot.mcpNotes`——进入时拍下的恢复名单一读回来就被剥掉,退出模式拿不到名单、备注永远停在场景值。现已透传该字段(并补进入覆盖/退出恢复/未勾选不触碰三条直证回归测试;未勾选服务器的场景备注不再在进入时覆盖其全局备注)。
|
|
33
|
+
|
|
34
|
+
### 说明
|
|
35
|
+
|
|
36
|
+
- 人设"文件缺失 = 全部启用"的老数据兼容语义保留:只有首次新建/导入/恢复时物化现状并排除新名;已有技能与人设的启用状态不动。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
13
40
|
## [0.8.0] - 2026-09-15
|
|
14
41
|
|
|
15
42
|
### 新增
|
package/README.md
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](package.json)
|
|
6
6
|
[](https://github.com/ouli-1242/dsh-plugin-tool-management)
|
|
7
|
+
|
|
7
8
|
[](https://dsh.market/)
|
|
9
|
+
[](https://awesome-dsh-plugin.com)
|
|
10
|
+
[](https://dshfind.com/zh/plugins/ouli-1242/dsh-plugin-tool-management)
|
|
8
11
|
|
|
9
12
|
**简体中文** · [English](README_EN.md) · [Changelog](CHANGELOG.md) · [版本更新概要](docs/update.md)
|
|
10
13
|
|
package/README_EN.md
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](package.json)
|
|
6
6
|
[](https://github.com/ouli-1242/dsh-plugin-tool-management)
|
|
7
|
+
|
|
7
8
|
[](https://dsh.market/)
|
|
9
|
+
[](https://awesome-dsh-plugin.com)
|
|
10
|
+
[](https://dshfind.com/zh/plugins/ouli-1242/dsh-plugin-tool-management)
|
|
8
11
|
|
|
9
12
|
[简体中文](README.md) · **English** · [Changelog](CHANGELOG.md) · [Release overview](docs/update.md)
|
|
10
13
|
|
package/docs/update.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
| 版本 | 发布 | 主题 |
|
|
6
6
|
|---|---|---|
|
|
7
|
+
| [0.8.5](#085---2026-09-16) | 2026-09-16 | 历史页标签统一 + 目录存在即可重新登记 + 场景 MCP 备注 + 新增三域默认不启动 |
|
|
7
8
|
| [0.8.0](#080---2026-09-15) | 2026-09-15 | 场景锁定 + 子智能体开关 + 四域导出 |
|
|
8
9
|
| [0.7.0](#070---2026-09-15) | 2026-09-15 | 回收站全量 + 预设注入可见性 + 密钥令牌化 |
|
|
9
10
|
| [0.6.0](#060---2026-09-14) | 2026-09-14 | 宿主兼容体检 + 兼容页 |
|
|
@@ -13,6 +14,19 @@
|
|
|
13
14
|
|
|
14
15
|
---
|
|
15
16
|
|
|
17
|
+
## 0.8.5 - 2026-09-16
|
|
18
|
+
|
|
19
|
+
- **新增技能 / 子智能体 / MCP 一律默认不启动**:创建与导入都不自动开启,由你手动打开(三域同口径)。
|
|
20
|
+
- 历史页「已移除」标签红灰两色统一为灰色(此前红色 = 从未登记、灰色 = 曾登记被移除,区分无实际意义)。
|
|
21
|
+
- 目录真实存在但没有「重新登记」按钮的问题修复:只要磁盘目录在,就能一键重新登记(带 stat 缓存,同一目录只探一次盘)。
|
|
22
|
+
- 目录已删除的工作区组补明确状态:组头显示「⚠ 目录已不存在」,悬停说明原因与处理方式(会话仍可恢复/删除)。
|
|
23
|
+
- peer 依赖去掉 semver 上界:宿主跨 minor 升级不再挡住插件安装,兼容性由运行时能力探测兜底。
|
|
24
|
+
- 系统提示词注入段文案优化:子智能体段明确"人设是可用选项而非唯一可选",并补充委派机制说明;场景记忆段加固定引导语(常驻参考知识)。
|
|
25
|
+
- 新增场景级 MCP 备注:场景档案的 MCP 段可为勾选的服务器写专属备注,进入场景覆盖全局备注、退出恢复原备注;MCP 备注前缀统一为「备注:」。
|
|
26
|
+
- 未运行的 MCP 服务器现在也能看到工具数和勾选工具(按「最后见过的工具名」记录兜底)。
|
|
27
|
+
- 场景档案 MCP 编辑改为固定尺寸弹窗:第一栏备注、第二栏工具勾选,「选工具」并入「编辑」。
|
|
28
|
+
- 从未运行过的服务器:编辑弹窗里点「启动服务器读取工具」即可读取工具名单(读完后自动恢复原启停状态)。
|
|
29
|
+
|
|
16
30
|
## 0.8.0 - 2026-09-15
|
|
17
31
|
|
|
18
32
|
- **场景锁定**:锁住后 MCP / 技能 / 子智能体 / 记忆 / 提示词整体只读(勾没勾的都不能动);未启动不能上锁,锁定中关不掉也切不走。
|
package/lib/client.js
CHANGED
|
@@ -76,7 +76,7 @@ window.__ModuleLoader__.load({
|
|
|
76
76
|
.dsm-modal-list .dsm-modal-body>*{flex:none}
|
|
77
77
|
.dsm-fm{display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-1)}.dsm-fm-row{display:grid;grid-template-columns:minmax(96px,150px) minmax(0,1fr);gap:10px;padding:8px 12px;border-bottom:1px solid var(--dsw-alias-border-l1);font-size:12px;line-height:18px}.dsm-fm-row:last-child{border-bottom:0}.dsm-fm-key{color:var(--dsw-alias-label-secondary);word-break:break-word}.dsm-fm-raw{margin-left:5px;color:var(--dsw-alias-label-tertiary);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:10px}.dsm-fm-val{min-width:0;color:var(--dsw-alias-label-primary);word-break:break-word;white-space:pre-wrap}
|
|
78
78
|
@container(max-width:780px){.dsm-table-head{display:none}.dsm-row{grid-template-columns:minmax(0,1fr) max-content;gap:8px;padding:11px 13px}.dsm-row>.dsm-tags,.dsm-row>.dsm-status{grid-column:1}.dsm-row-actions{grid-column:2;grid-row:1 / span 3}.dsm-path{display:none}}@media(max-width:760px){.dsm-summary{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:720px){.dsm-title-row{flex-wrap:wrap}}@container(max-width:520px){.dsm-head{flex-direction:column}.dsm-actions{width:100%;margin-left:0}.dsm-actions .dsm-btn{flex:1}.dsm-filters{flex-direction:column}.dsm-source-filter{width:100%}.dsm-summary{grid-template-columns:1fr}}
|
|
79
|
-
.dsm-hist-row{display:flex;align-items:center;gap:11px;padding:10px 13px;border-bottom:1px solid var(--dsw-alias-border-l1)}.dsm-hist-row:last-child{border-bottom:0}.dsm-hist-main{min-width:0;flex:1}.dsm-hist-title{overflow:hidden;font-size:13px;font-weight:580;text-overflow:ellipsis;white-space:nowrap}.dsm-hist-cwd{overflow:hidden;margin-top:2px;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:17px;text-overflow:ellipsis;white-space:nowrap}.dsm-hist-cwd-missing{color:var(--dsw-alias-state-error-primary)}.dsm-hist-actions{display:flex;gap:6px;flex:none}.dsm-hist-check,.dsm-hist-group-check{flex:none;width:15px;height:15px;margin:0 4px 0 0;cursor:pointer;accent-color:var(--dsw-alias-state-success-primary)}.dsm-hist-group-head{gap:10px}.dsm-hist-group-extra{display:flex;align-items:center;gap:8px;flex:none;margin-left:4px}.dsm-hist-group-extra .dsm-tag{flex:none}.dsm-hist-register-path{margin-top:-4px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-mono,monospace);font-size:11px;word-break:break-all}.dsm-hist-batch{display:flex;align-items:center;gap:8px;margin-left:2px;padding-left:10px;border-left:1px solid var(--dsw-alias-border-l2)}.dsm-hist-batch-count{color:var(--dsw-alias-label-secondary);font-size:12px;white-space:nowrap}.dsm-hist-batch-actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px;flex:none}.dsm-dir-row{display:flex;gap:8px}.dsm-dir-row .dsm-control{flex:1}.dsm-dir-list{display:flex;max-height:220px;flex-direction:column;overflow:auto;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-1)}.dsm-dir-item{display:flex;width:100%;align-items:center;padding:8px 12px;border:0;border-bottom:1px solid var(--dsw-alias-border-l1);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;text-align:left;cursor:pointer}.dsm-dir-item:last-child{border-bottom:0}.dsm-dir-item:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsm-dir-item::before{content:"📁";margin-right:8px;font-size:12px}
|
|
79
|
+
.dsm-hist-row{display:flex;align-items:center;gap:11px;padding:10px 13px;border-bottom:1px solid var(--dsw-alias-border-l1)}.dsm-hist-row:last-child{border-bottom:0}.dsm-hist-main{min-width:0;flex:1}.dsm-hist-title{overflow:hidden;font-size:13px;font-weight:580;text-overflow:ellipsis;white-space:nowrap}.dsm-hist-cwd{overflow:hidden;margin-top:2px;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:17px;text-overflow:ellipsis;white-space:nowrap}.dsm-hist-cwd-missing{color:var(--dsw-alias-state-error-primary)}.dsm-hist-actions{display:flex;gap:6px;flex:none}.dsm-hist-check,.dsm-hist-group-check{flex:none;width:15px;height:15px;margin:0 4px 0 0;cursor:pointer;accent-color:var(--dsw-alias-state-success-primary)}.dsm-hist-group-head{gap:10px}.dsm-hist-group-extra{display:flex;align-items:center;gap:8px;flex:none;margin-left:4px}.dsm-hist-group-extra .dsm-tag{flex:none}.dsm-hist-dir-missing{flex:none;color:var(--dsw-alias-state-error-primary);font-size:11px;white-space:nowrap}.dsm-hist-register-path{margin-top:-4px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-mono,monospace);font-size:11px;word-break:break-all}.dsm-hist-batch{display:flex;align-items:center;gap:8px;margin-left:2px;padding-left:10px;border-left:1px solid var(--dsw-alias-border-l2)}.dsm-hist-batch-count{color:var(--dsw-alias-label-secondary);font-size:12px;white-space:nowrap}.dsm-hist-batch-actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px;flex:none}.dsm-dir-row{display:flex;gap:8px}.dsm-dir-row .dsm-control{flex:1}.dsm-dir-list{display:flex;max-height:220px;flex-direction:column;overflow:auto;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-1)}.dsm-dir-item{display:flex;width:100%;align-items:center;padding:8px 12px;border:0;border-bottom:1px solid var(--dsw-alias-border-l1);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;text-align:left;cursor:pointer}.dsm-dir-item:last-child{border-bottom:0}.dsm-dir-item:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsm-dir-item::before{content:"📁";margin-right:8px;font-size:12px}
|
|
80
80
|
.dsm-rule-shadowed .dsm-name,.dsm-rule-shadowed .dsm-note{color:var(--dsw-alias-state-error-primary)}.dsm-rule-shadow-hint{margin-top:3px;color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:17px}.dsm-rule-invalid{border-color:var(--dsw-alias-state-error-primary)!important}.dsm-rule-hint{color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:17px}.dsm-rule-budget{display:flex;flex-direction:column;gap:6px}.dsm-budget-meta{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.dsm-budget-meta strong{margin-right:0;font-size:13px;font-weight:680}.dsm-budget-over-text{color:var(--dsw-alias-state-error-primary);font-size:11px}.dsm-budget-bar{height:6px;overflow:hidden;border-radius:99px;background:var(--dsw-alias-interactive-bg-hover)}.dsm-budget-fill{height:100%;border-radius:99px;background:var(--dsw-alias-state-success-primary);transition:width 160ms ease}.dsm-budget-fill.dsm-budget-over{background:var(--dsw-alias-state-error-primary)}.dsm-char-count{color:var(--dsw-alias-label-tertiary);font-size:11px}.dsm-char-over{color:var(--dsw-alias-state-error-primary)}
|
|
81
81
|
.dsm-combo-row{display:flex;gap:8px}.dsm-combo-row .dsm-control{flex:1}
|
|
82
82
|
.dsm-attach-list{display:flex;flex-direction:column;gap:6px}.dsm-attach-actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
|
|
@@ -93,7 +93,7 @@ window.__ModuleLoader__.load({
|
|
|
93
93
|
/* 滚动容器里的子项不许被压缩:否则内容一多,筛选框会被压到比 input 还矮、与下面的条目叠在一起。 */
|
|
94
94
|
.dsm-seg-body>*{flex:none}
|
|
95
95
|
.dsm-seg-foot{display:flex;align-items:center;gap:8px;padding:7px 12px;border-top:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
96
|
-
.dsm-pick{display:flex;align-items:center;gap:9px;padding:6px 8px;border-radius:7px}
|
|
96
|
+
.dsm-pick{display:flex;align-items:center;gap:9px;padding:6px 8px;border-radius:7px}.dsm-mcp-edit-tools-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.dsm-mcp-edit-tools-actions{display:flex;align-items:center;gap:8px}.dsm-seg-empty-actions{display:flex;justify-content:center;padding:4px 0 12px}.dsm-seg-empty-error{padding:0 10px 10px;color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px;text-align:center}
|
|
97
97
|
.dsm-pick:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
98
98
|
.dsm-pick input[type=checkbox]{flex:none;width:15px;height:15px;margin:0;cursor:pointer;accent-color:var(--dsw-alias-state-success-primary)}
|
|
99
99
|
/* .dsm-pick-main 必须是**纵向 flex**:它原本只是个行内 span,名称与说明会在同一行里内联排布,
|
|
@@ -701,8 +701,8 @@ html,body{scrollbar-gutter:stable}.dsm-settings-scroll-host{overflow-y:scroll!im
|
|
|
701
701
|
"scenes.archive.summary": "档案:MCP {mcp} 台 · 技能 {skills} 个 · 子智能体 {subagents} 个",
|
|
702
702
|
"scenes.archive.note": "段未定义 = 不改动该域;段已定义 = 按勾选集双向切换:勾的启用、没勾的停用。MCP 会真的启停服务器进程,技能会启停它所在的来源。",
|
|
703
703
|
"scenes.mcp.noServers": "还没有可选的 MCP 服务器", "scenes.mcp.toolCount": "{count} 个工具", "scenes.skills.empty": "还没有可选的技能",
|
|
704
|
-
"scenes.mcp.allTools": "全部工具", "scenes.mcp.pickedCount": "指定 {count} 个工具", "scenes.mcp.notRunning": "未运行", "scenes.mcp.
|
|
705
|
-
"scenes.mcp.toolsOf": "
|
|
704
|
+
"scenes.mcp.allTools": "全部工具", "scenes.mcp.pickedCount": "指定 {count} 个工具", "scenes.mcp.notRunning": "未运行", "scenes.mcp.edit": "编辑", "scenes.mcp.done": "完成", "scenes.mcp.noteLabel": "场景备注", "scenes.mcp.notePlaceholder": "进入该场景时覆盖全局备注;退出后恢复", "scenes.mcp.hasNote": "有备注",
|
|
705
|
+
"scenes.mcp.toolsOf": "选择工具", "scenes.mcp.probeTools": "启动服务器读取工具", "scenes.mcp.unknownCount": "工具数未知 · 点「编辑」读取", "scenes.mcp.noteHint": "此备注随场景保存;仅在该场景本段**勾选**了这台服务器时,进入场景才会覆盖「MCP」页与系统提示词里的备注,退出后自动恢复。", "scenes.mcp.noTools": "该服务器当前没有可列出的工具(从未运行过,或已配置的工具尚未被记录)", "scenes.mcp.drillHint": "勾选 = 该场景下启用;不勾 = 停用。「全选」= 全部工具。",
|
|
706
706
|
"scenes.skills.hint": "添加「技能集」后,勾选该场景下启用的技能。", "scenes.subagents.hint": "添加「子智能体绑定」后,勾选本场景可调用的人设。", "scenes.subagents.empty": "还没有人设——到「子智能体」页创建。",
|
|
707
707
|
"scenes.filter.skills": "筛选技能/目录名称", "scenes.filter.subagents": "筛选人设(名称或描述)", "scenes.filter.servers": "筛选服务器",
|
|
708
708
|
|
|
@@ -802,7 +802,7 @@ html,body{scrollbar-gutter:stable}.dsm-settings-scroll-host{overflow-y:scroll!im
|
|
|
802
802
|
"import.none": "没有导入任何文件(全部被跳过)", "import.skipped": "已跳过:{items}",
|
|
803
803
|
"error.import.noFiles": "没有选择要导入的文件",
|
|
804
804
|
"nav.title": "工具",
|
|
805
|
-
"agm.stat.total": "个提示词", "agm.stat.active": "个生效中", "agm.loading": "加载中…", "agm.empty": "暂无提示词,点「新增提示词」创建", "agm.btn.delete.blocked": "正在生效的提示词预设不能删除;先启用别的场景、换绑提示词,或先把别的预设应用上去。", "agm.active": "生效中", "agm.active.hint.scene": "当前生效:场景「{scene}」绑定的提示词预设", "agm.active.hint.file": "当前生效:AGENTS.md 就是这一份", "agm.file.applied": "文件里是它", "agm.file.applied.hint": "AGENTS.md 当前内容是它;启用的场景绑了别的提示词,切换场景时已按场景把文件改成那一份", "agm.apply.hint": "应用后写入 AGENTS.md;宿主每轮重读该文件,下一轮对话生效", "agm.btn.reapply": "重新应用", "agm.btn.apply": "应用", "agm.applyModal.title": "应用预设", "agm.edit.title": "编辑预设", "agm.remove.title": "删除预设", "agm.btn.edit": "编辑", "agm.btn.delete": "删除", "agm.btn.new": "新增提示词", "agm.btn.import": "导入提示词", "agm.result.imported": "已导入 {count} 份预设:{names}", "agm.btn.save": "保存", "agm.btn.create": "创建", "agm.btn.confirmRemove": "确认删除", "agm.btn.confirmApply": "确认应用", "agm.field.id": "id(目录名)", "agm.field.id.hint": "中文、空格、点都可以;不能含斜杠、反斜杠与 : * ? < > 等符号;≤64 字符。改 id 等于目录改名。", "agm.field.id.placeholder": "例如:工作基线", "agm.field.content": "内容", "agm.field.content.placeholder": "直接写这份预设的正文(Markdown);留空 = 用空白模板", "agm.field.copyFrom": "或从现有预设复制", "agm.field.copyFrom.none": "不复制(空白模板)", "agm.field.desc": "描述(只给使用者看)", "agm.field.desc.placeholder": "这份预设是干什么的、什么时候用它", "agm.result.renamed": "已保存,id 改为「{id}」", "agm.result.rebound": "(同步了 {count} 个场景的绑定)", "agm.remove.suffix": " ?此操作不可恢复。", "agm.apply.prefix": "将把 ", "agm.apply.suffix": " 的内容写入 ~/.dsh/AGENTS.md。", "agm.apply.note": "下一轮对话生效;当前内容已备份到 __last-applied__(保留最近 5 代)", "hist.err.load": "加载失败", "hist.err.unarchive": "恢复失败", "hist.err.delete": "删除失败", "hist.err.retention": "设置失败", "hist.err.import": "导入失败", "hist.err.sessions": "加载会话列表失败", "hist.err.export": "导出失败", "hist.err.archive": "归档失败", "hist.retention.forever": "永久保留", "hist.retention.days": "{count} 天", "hist.arch.all": "全部会话", "hist.arch.archived": "仅已归档", "hist.arch.live": "仅未归档", "hist.arch.missing": "仅目录丢失", "hist.ws.all": "全部工作区", "hist.ungrouped": "未分组", "hist.group.removed": "已移除", "hist.group.removed.hint": "该目录当前在 DSH 里没有工作区登记;分组按会话目录重建,重新登记后会自动并回同一组", "hist.btn.registerWs": "重新登记", "hist.btn.registerWs.hint": "重新登记这个目录,并把该目录下已有的会话挂回该组(只写工作区记账,不移动、不删除文件与会话)", "hist.register.title": "重新登记?", "hist.register.body": "将为「{title}」在 DSH 中重新创建一条工作区登记,并把该目录下已有的会话挂回这个工作区(恢复其分组)。只写工作区记账,不移动、不删除任何文件与会话。", "hist.register.confirm": "确认登记", "hist.register.done.title": "已重新登记", "hist.register.done.body": "工作区「{title}」已登记;该目录下的归档会话会在刷新后并入这一组。", "hist.register.done.attached": "已把该目录下 {count} 个已有会话挂回这个工作区。", "hist.register.done.skipped": "{count} 个会话未能挂回:{reason}", "hist.err.register": "登记失败", "scenes.field.prompt": "提示词预设", "scenes.agents.note.applied": "已把「{id}」写入 AGENTS.md", "scenes.agents.note.restored": "已恢复进场景前的 AGENTS.md", "scenes.agents.note.error": "AGENTS.md 未写入:{reason}", "scenes.field.prompt.hint": "启用这个场景时,就用这份预设作为当前的全局指令基线。默认选中当前生效的那份。", "scenes.prompt.activeTag": "生效中", "scenes.prompt.noPresets": "还没有提示词预设;可先去「提示词」页新建一个。", "scenes.prompt.bound": "提示词:{id}", "scenes.prompt.live": "提示词生效中:{id}", "scenes.prompt.missing": "预设不存在:{id}", "scenes.prompt.tag.hint": "这个场景绑定的提示词预设", "scenes.enable.blocked": "已启用场景「{name}」;先关掉它才能启用别的", "scenes.result.created.collapsed": "场景「{name}」已创建,默认不启动(原先的「全部启用」已收敛为单选)", "scenes.legacyAll": "检测到 {count} 个场景同时处于启用状态(历史「全部启用」遗留);除「全局」外同时只能启用一个。", "scenes.legacyAll.fix": "收敛为单选", "agm.scene.tag": "场景「{scene}」正在使用", "agm.scene.tag.hint": "由这个场景绑定,随场景开关生效", "agm.scene.missing": "场景「{scene}」绑定了它,但文件不存在", "hist.restore.note.registered": "已恢复 {count} 个会话;工作区「{title}」已重新登记,会话已挂回该组。", "hist.restore.note.attached": "已恢复 {count} 个会话,并挂回工作区「{title}」。", "hist.restore.note.skip": "{count} 个会话的工作区归属未能恢复:{reason}", "hist.purge.title": "永久删除?", "hist.purge.one": "永久删除会话「{title}」及其全部记录,不可恢复", "hist.purge.batch": "永久删除所选 {count} 个会话及其全部记录,不可恢复", "hist.btn.restore": "恢复", "hist.btn.purge": "永久删除", "hist.btn.confirmPurge": "确认删除", "hist.selectGroup": "全选组 {title}", "hist.group.count": "{count} 个", "hist.selectAll": "全选", "hist.deselectAll": "取消全选", "hist.btn.import": "导入会话", "hist.btn.export": "导出会话", "hist.selected": "已选 {count} 项", "hist.btn.restoreSelected": "恢复所选 ({count})", "hist.btn.deleteSelected": "删除所选 ({count})", "hist.stat.archived": "个归档", "hist.stat.projects": "个项目", "hist.stat.retention": "天保留", "hist.search.placeholder": "搜索标题 / 会话 ID / 项目路径", "hist.loading": "加载中…", "hist.empty.search": "无匹配的归档会话", "hist.empty.none": "暂无归档会话", "hist.import.cwd": "项目目录(可留空)", "hist.import.cwd.placeholder": "绝对路径,可留空(会话归入未分组)", "hist.import.drop": "选择或拖入对话文件", "hist.import.done": "已创建会话 {id}({count} 条消息),可在 DSH 会话列表中继续对话。", "hist.export.hint": "导出选中会话为转录文件,可再次导入", "hist.export.dir": "导出目录(绝对路径,自动创建)", "hist.export.browse": "浏览并选择文件夹", "hist.btn.browse": "选择", "hist.export.pickDir": "选择导出目录", "hist.export.format": "格式", "hist.export.scope": "会话范围", "hist.export.workspace": "工作区", "hist.export.sessions": "会话({count} 个)", "hist.export.loading": "加载会话列表…", "hist.cwd.missing": "⚠ 工作区目录已不存在:", "hist.tag.archived": "已归档", "hist.tag.live": "未归档", "hist.export.empty": "没有符合条件的会话", "hist.result.exported": "已导出 {count} 个会话到 {dir}", "hist.result.skipped": ";跳过 {count} 个({ids})", "hist.result.archived": "已归档 {count} 个会话到 History,可到 History 页继续管理", "hist.btn.archiveSelected": "归档所选", "hist.btn.archiveSelected.title": "把选中的会话收进 History,纳入保留期管理", "hist.export.busy": "处理中…", "hist.btn.exportSelected": "导出 {count} 个会话", "hist.error.title": "操作失败", "hist.error.unknown": "未知错误",
|
|
805
|
+
"agm.stat.total": "个提示词", "agm.stat.active": "个生效中", "agm.loading": "加载中…", "agm.empty": "暂无提示词,点「新增提示词」创建", "agm.btn.delete.blocked": "正在生效的提示词预设不能删除;先启用别的场景、换绑提示词,或先把别的预设应用上去。", "agm.active": "生效中", "agm.active.hint.scene": "当前生效:场景「{scene}」绑定的提示词预设", "agm.active.hint.file": "当前生效:AGENTS.md 就是这一份", "agm.file.applied": "文件里是它", "agm.file.applied.hint": "AGENTS.md 当前内容是它;启用的场景绑了别的提示词,切换场景时已按场景把文件改成那一份", "agm.apply.hint": "应用后写入 AGENTS.md;宿主每轮重读该文件,下一轮对话生效", "agm.btn.reapply": "重新应用", "agm.btn.apply": "应用", "agm.applyModal.title": "应用预设", "agm.edit.title": "编辑预设", "agm.remove.title": "删除预设", "agm.btn.edit": "编辑", "agm.btn.delete": "删除", "agm.btn.new": "新增提示词", "agm.btn.import": "导入提示词", "agm.result.imported": "已导入 {count} 份预设:{names}", "agm.btn.save": "保存", "agm.btn.create": "创建", "agm.btn.confirmRemove": "确认删除", "agm.btn.confirmApply": "确认应用", "agm.field.id": "id(目录名)", "agm.field.id.hint": "中文、空格、点都可以;不能含斜杠、反斜杠与 : * ? < > 等符号;≤64 字符。改 id 等于目录改名。", "agm.field.id.placeholder": "例如:工作基线", "agm.field.content": "内容", "agm.field.content.placeholder": "直接写这份预设的正文(Markdown);留空 = 用空白模板", "agm.field.copyFrom": "或从现有预设复制", "agm.field.copyFrom.none": "不复制(空白模板)", "agm.field.desc": "描述(只给使用者看)", "agm.field.desc.placeholder": "这份预设是干什么的、什么时候用它", "agm.result.renamed": "已保存,id 改为「{id}」", "agm.result.rebound": "(同步了 {count} 个场景的绑定)", "agm.remove.suffix": " ?此操作不可恢复。", "agm.apply.prefix": "将把 ", "agm.apply.suffix": " 的内容写入 ~/.dsh/AGENTS.md。", "agm.apply.note": "下一轮对话生效;当前内容已备份到 __last-applied__(保留最近 5 代)", "hist.err.load": "加载失败", "hist.err.unarchive": "恢复失败", "hist.err.delete": "删除失败", "hist.err.retention": "设置失败", "hist.err.import": "导入失败", "hist.err.sessions": "加载会话列表失败", "hist.err.export": "导出失败", "hist.err.archive": "归档失败", "hist.retention.forever": "永久保留", "hist.retention.days": "{count} 天", "hist.arch.all": "全部会话", "hist.arch.archived": "仅已归档", "hist.arch.live": "仅未归档", "hist.arch.missing": "仅目录丢失", "hist.ws.all": "全部工作区", "hist.ungrouped": "未分组", "hist.group.removed": "已移除", "hist.group.removed.hint": "该目录当前在 DSH 里没有工作区登记;分组按会话目录重建,重新登记后会自动并回同一组", "hist.group.dirMissing": "目录已不存在", "hist.group.dirMissing.hint": "工作区目录已从磁盘删除,无法重新登记;组内会话仍可恢复(归入未分组)或永久删除", "hist.btn.registerWs": "重新登记", "hist.btn.registerWs.hint": "重新登记这个目录,并把该目录下已有的会话挂回该组(只写工作区记账,不移动、不删除文件与会话)", "hist.register.title": "重新登记?", "hist.register.body": "将为「{title}」在 DSH 中重新创建一条工作区登记,并把该目录下已有的会话挂回这个工作区(恢复其分组)。只写工作区记账,不移动、不删除任何文件与会话。", "hist.register.confirm": "确认登记", "hist.register.done.title": "已重新登记", "hist.register.done.body": "工作区「{title}」已登记;该目录下的归档会话会在刷新后并入这一组。", "hist.register.done.attached": "已把该目录下 {count} 个已有会话挂回这个工作区。", "hist.register.done.skipped": "{count} 个会话未能挂回:{reason}", "hist.err.register": "登记失败", "scenes.field.prompt": "提示词预设", "scenes.agents.note.applied": "已把「{id}」写入 AGENTS.md", "scenes.agents.note.restored": "已恢复进场景前的 AGENTS.md", "scenes.agents.note.error": "AGENTS.md 未写入:{reason}", "scenes.field.prompt.hint": "启用这个场景时,就用这份预设作为当前的全局指令基线。默认选中当前生效的那份。", "scenes.prompt.activeTag": "生效中", "scenes.prompt.noPresets": "还没有提示词预设;可先去「提示词」页新建一个。", "scenes.prompt.bound": "提示词:{id}", "scenes.prompt.live": "提示词生效中:{id}", "scenes.prompt.missing": "预设不存在:{id}", "scenes.prompt.tag.hint": "这个场景绑定的提示词预设", "scenes.enable.blocked": "已启用场景「{name}」;先关掉它才能启用别的", "scenes.result.created.collapsed": "场景「{name}」已创建,默认不启动(原先的「全部启用」已收敛为单选)", "scenes.legacyAll": "检测到 {count} 个场景同时处于启用状态(历史「全部启用」遗留);除「全局」外同时只能启用一个。", "scenes.legacyAll.fix": "收敛为单选", "agm.scene.tag": "场景「{scene}」正在使用", "agm.scene.tag.hint": "由这个场景绑定,随场景开关生效", "agm.scene.missing": "场景「{scene}」绑定了它,但文件不存在", "hist.restore.note.registered": "已恢复 {count} 个会话;工作区「{title}」已重新登记,会话已挂回该组。", "hist.restore.note.attached": "已恢复 {count} 个会话,并挂回工作区「{title}」。", "hist.restore.note.skip": "{count} 个会话的工作区归属未能恢复:{reason}", "hist.purge.title": "永久删除?", "hist.purge.one": "永久删除会话「{title}」及其全部记录,不可恢复", "hist.purge.batch": "永久删除所选 {count} 个会话及其全部记录,不可恢复", "hist.btn.restore": "恢复", "hist.btn.purge": "永久删除", "hist.btn.confirmPurge": "确认删除", "hist.selectGroup": "全选组 {title}", "hist.group.count": "{count} 个", "hist.selectAll": "全选", "hist.deselectAll": "取消全选", "hist.btn.import": "导入会话", "hist.btn.export": "导出会话", "hist.selected": "已选 {count} 项", "hist.btn.restoreSelected": "恢复所选 ({count})", "hist.btn.deleteSelected": "删除所选 ({count})", "hist.stat.archived": "个归档", "hist.stat.projects": "个项目", "hist.stat.retention": "天保留", "hist.search.placeholder": "搜索标题 / 会话 ID / 项目路径", "hist.loading": "加载中…", "hist.empty.search": "无匹配的归档会话", "hist.empty.none": "暂无归档会话", "hist.import.cwd": "项目目录(可留空)", "hist.import.cwd.placeholder": "绝对路径,可留空(会话归入未分组)", "hist.import.drop": "选择或拖入对话文件", "hist.import.done": "已创建会话 {id}({count} 条消息),可在 DSH 会话列表中继续对话。", "hist.export.hint": "导出选中会话为转录文件,可再次导入", "hist.export.dir": "导出目录(绝对路径,自动创建)", "hist.export.browse": "浏览并选择文件夹", "hist.btn.browse": "选择", "hist.export.pickDir": "选择导出目录", "hist.export.format": "格式", "hist.export.scope": "会话范围", "hist.export.workspace": "工作区", "hist.export.sessions": "会话({count} 个)", "hist.export.loading": "加载会话列表…", "hist.cwd.missing": "⚠ 工作区目录已不存在:", "hist.tag.archived": "已归档", "hist.tag.live": "未归档", "hist.export.empty": "没有符合条件的会话", "hist.result.exported": "已导出 {count} 个会话到 {dir}", "hist.result.skipped": ";跳过 {count} 个({ids})", "hist.result.archived": "已归档 {count} 个会话到 History,可到 History 页继续管理", "hist.btn.archiveSelected": "归档所选", "hist.btn.archiveSelected.title": "把选中的会话收进 History,纳入保留期管理", "hist.export.busy": "处理中…", "hist.btn.exportSelected": "导出 {count} 个会话", "hist.error.title": "操作失败", "hist.error.unknown": "未知错误",
|
|
806
806
|
},
|
|
807
807
|
en: {
|
|
808
808
|
"title": "Skills", "desc": "Manage skills: enable, import, create, trash.", "link.project": "GitHub", "link.feedback": "Issues",
|
|
@@ -935,8 +935,8 @@ html,body{scrollbar-gutter:stable}.dsm-settings-scroll-host{overflow-y:scroll!im
|
|
|
935
935
|
"scenes.archive.summary": "Profile: {mcp} MCP server(s) · {skills} skill(s) · {subagents} subagent(s)",
|
|
936
936
|
"scenes.archive.note": "An undefined section leaves that domain untouched; a defined section switches both ways: checked items are enabled, unchecked ones disabled. MCP really starts/stops the server process; skills toggle the source they live in.",
|
|
937
937
|
"scenes.mcp.noServers": "No MCP servers to pick from yet", "scenes.mcp.toolCount": "{count} tool(s)", "scenes.skills.empty": "No skills to pick from yet",
|
|
938
|
-
"scenes.mcp.allTools": "all tools", "scenes.mcp.pickedCount": "{count} tools picked", "scenes.mcp.notRunning": "not running", "scenes.mcp.
|
|
939
|
-
"scenes.mcp.toolsOf": "
|
|
938
|
+
"scenes.mcp.allTools": "all tools", "scenes.mcp.pickedCount": "{count} tools picked", "scenes.mcp.notRunning": "not running", "scenes.mcp.edit": "Edit", "scenes.mcp.done": "Done", "scenes.mcp.noteLabel": "Scene note", "scenes.mcp.notePlaceholder": "Overrides the global note in this scene; restored on exit", "scenes.mcp.hasNote": "has note",
|
|
939
|
+
"scenes.mcp.toolsOf": "Pick tools", "scenes.mcp.probeTools": "Start the server to load tools", "scenes.mcp.unknownCount": "unknown tool count — open Edit to load", "scenes.mcp.noteHint": "Saved with the scene. It overrides the note shown on the MCP page and in the system prompt only while this scene is entered — and only for servers checked in this section. Restored on exit.", "scenes.mcp.noTools": "No tools listed for this server (it has never run, or its tools were never recorded)", "scenes.mcp.drillHint": "Checked = enabled in this scene; unchecked = disabled. “All” = every tool.",
|
|
940
940
|
"scenes.skills.hint": "Add the skills section first, then check the skills enabled in this scene.", "scenes.subagents.hint": "Add the subagent section first, then check the personas callable in this scene.", "scenes.subagents.empty": "No personas yet — create one on the Subagents page.",
|
|
941
941
|
"scenes.filter.skills": "Filter skills / directories", "scenes.filter.subagents": "Filter personas (name or description)", "scenes.filter.servers": "Filter servers",
|
|
942
942
|
|
|
@@ -1035,7 +1035,7 @@ html,body{scrollbar-gutter:stable}.dsm-settings-scroll-host{overflow-y:scroll!im
|
|
|
1035
1035
|
"import.none": "Nothing was imported (all skipped)", "import.skipped": "Skipped: {items}",
|
|
1036
1036
|
"error.import.noFiles": "No files selected",
|
|
1037
1037
|
"nav.title": "Tools",
|
|
1038
|
-
"agm.stat.total": " prompts", "agm.stat.active": " active", "agm.loading": "Loading…", "agm.empty": "No prompts yet — click “New prompt” to create one", "agm.btn.delete.blocked": "The preset currently in effect cannot be deleted; enable another scene, rebind the prompt, or apply a different preset first.", "agm.active": "Active", "agm.active.hint.scene": "Currently active: the prompt preset bound to scene “{scene}”", "agm.active.hint.file": "Currently active: AGENTS.md is exactly this preset", "agm.file.applied": "In AGENTS.md", "agm.file.applied.hint": "AGENTS.md currently holds this one; the enabled scene binds a different preset, which now takes effect", "agm.apply.hint": "Writes AGENTS.md; the host re-reads that file every turn, so it takes effect on the next turn", "agm.btn.reapply": "Apply again", "agm.btn.apply": "Apply", "agm.applyModal.title": "Apply preset", "agm.edit.title": "Edit preset", "agm.remove.title": "Delete preset", "agm.btn.edit": "Edit", "agm.btn.delete": "Delete", "agm.btn.new": "New prompt", "agm.btn.import": "Import prompts", "agm.result.imported": "Imported {count} preset(s): {names}", "agm.btn.save": "Save", "agm.btn.create": "Create", "agm.btn.confirmRemove": "Delete", "agm.btn.confirmApply": "Apply", "agm.field.id": "id (directory name)", "agm.field.id.hint": "Letters, spaces and dots are fine; no slashes or : * ? < >; up to 64 chars. Changing it renames the directory.", "agm.field.id.placeholder": "e.g. work-baseline", "agm.field.content": "Content", "agm.field.content.placeholder": "Write the preset body (Markdown) here; leave blank to start from the empty template", "agm.field.copyFrom": "Or copy from an existing preset", "agm.field.copyFrom.none": "Do not copy (empty template)", "agm.field.desc": "Description (for humans only)", "agm.field.desc.placeholder": "What this preset is for and when to use it", "agm.result.renamed": "Saved; id is now “{id}”", "agm.result.rebound": " ({count} scene binding(s) updated)", "agm.remove.suffix": "? This cannot be undone.", "agm.apply.prefix": "Write the contents of ", "agm.apply.suffix": " into ~/.dsh/AGENTS.md.", "agm.apply.note": "Takes effect on the next turn; the current content is backed up to __last-applied__ (last 5 kept)", "hist.err.load": "Load failed", "hist.err.unarchive": "Restore failed", "hist.err.delete": "Delete failed", "hist.err.retention": "Update failed", "hist.err.import": "Import failed", "hist.err.sessions": "Failed to load sessions", "hist.err.export": "Export failed", "hist.err.archive": "Archive failed", "hist.retention.forever": "Keep forever", "hist.retention.days": "{count} days", "hist.arch.all": "All sessions", "hist.arch.archived": "Archived only", "hist.arch.live": "Not archived only", "hist.arch.missing": "Missing directory only", "hist.ws.all": "All workspaces", "hist.ungrouped": "Ungrouped", "hist.group.removed": "Removed", "hist.group.removed.hint": "This directory has no DSH workspace registration right now; the group is rebuilt from session directories and rejoins automatically once registered again", "hist.btn.registerWs": "Register", "hist.btn.registerWs.hint": "Register this directory again and attach its existing sessions back to that group (workspace accounting only — no file or session is moved or deleted)", "hist.register.title": "Register again?", "hist.register.body": "A workspace registration will be created in DSH for “{title}”, and the sessions under this directory will be attached back to it (restoring their grouping). This writes workspace accounting only — no file or session is moved or deleted.", "hist.register.confirm": "Register", "hist.register.done.title": "Registered", "hist.register.done.body": "Workspace “{title}” is registered; archived sessions under this directory join that group after a refresh.", "hist.register.done.attached": "Attached {count} existing session(s) under this directory back to the workspace.", "hist.register.done.skipped": "{count} session(s) could not be attached: {reason}", "hist.err.register": "Registration failed", "scenes.field.prompt": "Prompt preset", "scenes.agents.note.applied": "wrote “{id}” into AGENTS.md", "scenes.agents.note.restored": "restored the AGENTS.md baseline from before the scene", "scenes.agents.note.error": "AGENTS.md was not written: {reason}", "scenes.field.prompt.hint": "While this scene is enabled, its preset is the active global baseline. Defaults to the one currently in effect.", "scenes.prompt.activeTag": "in effect", "scenes.prompt.noPresets": "No prompt presets yet — create one on the Prompts page.", "scenes.prompt.bound": "Prompt: {id}", "scenes.prompt.live": "Prompt active: {id}", "scenes.prompt.missing": "Preset missing: {id}", "scenes.prompt.tag.hint": "The prompt preset bound to this scene", "scenes.enable.blocked": "Scene “{name}” is already enabled; turn it off first", "scenes.result.created.collapsed": "Scene “{name}” created and left off (the legacy “all enabled” default collapsed to a single choice)", "scenes.legacyAll": "{count} scenes are enabled at once (a leftover of the legacy “all enabled” default); only one scene besides “Global” may be enabled.", "scenes.legacyAll.fix": "Collapse to one", "agm.scene.tag": "In use by scene “{scene}”", "agm.scene.tag.hint": "Bound by that scene; takes effect with the scene switch", "agm.scene.missing": "Bound by scene “{scene}”, but the file is missing", "hist.restore.note.registered": "Restored {count} session(s); workspace “{title}” was registered again and the sessions are back in that group.", "hist.restore.note.attached": "Restored {count} session(s) and attached them to workspace “{title}”.", "hist.restore.note.skip": "Workspace ownership could not be restored for {count} session(s): {reason}", "hist.purge.title": "Delete forever?", "hist.purge.one": "Delete “{title}” and all of its records forever — this cannot be undone", "hist.purge.batch": "Delete the {count} selected sessions and all of their records forever — this cannot be undone", "hist.btn.restore": "Restore", "hist.btn.purge": "Delete forever", "hist.btn.confirmPurge": "Delete", "hist.selectGroup": "Select group {title}", "hist.group.count": "{count}", "hist.selectAll": "Select all", "hist.deselectAll": "Clear selection", "hist.btn.import": "Import sessions", "hist.btn.export": "Export sessions", "hist.selected": "{count} selected", "hist.btn.restoreSelected": "Restore selected ({count})", "hist.btn.deleteSelected": "Delete selected ({count})", "hist.stat.archived": " archived", "hist.stat.projects": " projects", "hist.stat.retention": " days kept", "hist.search.placeholder": "Search title / session id / project path", "hist.loading": "Loading…", "hist.empty.search": "No matching archived sessions", "hist.empty.none": "No archived sessions yet", "hist.import.cwd": "Project directory (optional)", "hist.import.cwd.placeholder": "Absolute path, optional (the session goes to Ungrouped)", "hist.import.drop": "Choose or drop a conversation file", "hist.import.done": "Created session {id} ({count} messages); continue it from the DSH session list.", "hist.export.hint": "Export the selected sessions as transcript files that can be imported again", "hist.export.dir": "Export directory (absolute path, created automatically)", "hist.export.browse": "Browse and choose a folder", "hist.btn.browse": "Choose", "hist.export.pickDir": "Choose export directory", "hist.export.format": "Format", "hist.export.scope": "Session scope", "hist.export.workspace": "Workspace", "hist.export.sessions": "Sessions ({count})", "hist.export.loading": "Loading sessions…", "hist.cwd.missing": "⚠ The workspace directory no longer exists: ", "hist.tag.archived": "Archived", "hist.tag.live": "Not archived", "hist.export.empty": "No matching sessions", "hist.result.exported": "Exported {count} session(s) to {dir}", "hist.result.skipped": "; skipped {count} ({ids})", "hist.result.archived": "Archived {count} session(s) to History; manage them on the History page", "hist.btn.archiveSelected": "Archive selected", "hist.btn.archiveSelected.title": "Move the selected sessions into History so retention applies to them", "hist.export.busy": "Working…", "hist.btn.exportSelected": "Export {count} session(s)", "hist.error.title": "Operation failed", "hist.error.unknown": "Unknown error",
|
|
1038
|
+
"agm.stat.total": " prompts", "agm.stat.active": " active", "agm.loading": "Loading…", "agm.empty": "No prompts yet — click “New prompt” to create one", "agm.btn.delete.blocked": "The preset currently in effect cannot be deleted; enable another scene, rebind the prompt, or apply a different preset first.", "agm.active": "Active", "agm.active.hint.scene": "Currently active: the prompt preset bound to scene “{scene}”", "agm.active.hint.file": "Currently active: AGENTS.md is exactly this preset", "agm.file.applied": "In AGENTS.md", "agm.file.applied.hint": "AGENTS.md currently holds this one; the enabled scene binds a different preset, which now takes effect", "agm.apply.hint": "Writes AGENTS.md; the host re-reads that file every turn, so it takes effect on the next turn", "agm.btn.reapply": "Apply again", "agm.btn.apply": "Apply", "agm.applyModal.title": "Apply preset", "agm.edit.title": "Edit preset", "agm.remove.title": "Delete preset", "agm.btn.edit": "Edit", "agm.btn.delete": "Delete", "agm.btn.new": "New prompt", "agm.btn.import": "Import prompts", "agm.result.imported": "Imported {count} preset(s): {names}", "agm.btn.save": "Save", "agm.btn.create": "Create", "agm.btn.confirmRemove": "Delete", "agm.btn.confirmApply": "Apply", "agm.field.id": "id (directory name)", "agm.field.id.hint": "Letters, spaces and dots are fine; no slashes or : * ? < >; up to 64 chars. Changing it renames the directory.", "agm.field.id.placeholder": "e.g. work-baseline", "agm.field.content": "Content", "agm.field.content.placeholder": "Write the preset body (Markdown) here; leave blank to start from the empty template", "agm.field.copyFrom": "Or copy from an existing preset", "agm.field.copyFrom.none": "Do not copy (empty template)", "agm.field.desc": "Description (for humans only)", "agm.field.desc.placeholder": "What this preset is for and when to use it", "agm.result.renamed": "Saved; id is now “{id}”", "agm.result.rebound": " ({count} scene binding(s) updated)", "agm.remove.suffix": "? This cannot be undone.", "agm.apply.prefix": "Write the contents of ", "agm.apply.suffix": " into ~/.dsh/AGENTS.md.", "agm.apply.note": "Takes effect on the next turn; the current content is backed up to __last-applied__ (last 5 kept)", "hist.err.load": "Load failed", "hist.err.unarchive": "Restore failed", "hist.err.delete": "Delete failed", "hist.err.retention": "Update failed", "hist.err.import": "Import failed", "hist.err.sessions": "Failed to load sessions", "hist.err.export": "Export failed", "hist.err.archive": "Archive failed", "hist.retention.forever": "Keep forever", "hist.retention.days": "{count} days", "hist.arch.all": "All sessions", "hist.arch.archived": "Archived only", "hist.arch.live": "Not archived only", "hist.arch.missing": "Missing directory only", "hist.ws.all": "All workspaces", "hist.ungrouped": "Ungrouped", "hist.group.removed": "Removed", "hist.group.removed.hint": "This directory has no DSH workspace registration right now; the group is rebuilt from session directories and rejoins automatically once registered again", "hist.group.dirMissing": "Directory missing", "hist.group.dirMissing.hint": "The workspace directory was deleted from disk, so it cannot be registered again; sessions in this group can still be restored (they join Ungrouped) or deleted forever", "hist.btn.registerWs": "Register", "hist.btn.registerWs.hint": "Register this directory again and attach its existing sessions back to that group (workspace accounting only — no file or session is moved or deleted)", "hist.register.title": "Register again?", "hist.register.body": "A workspace registration will be created in DSH for “{title}”, and the sessions under this directory will be attached back to it (restoring their grouping). This writes workspace accounting only — no file or session is moved or deleted.", "hist.register.confirm": "Register", "hist.register.done.title": "Registered", "hist.register.done.body": "Workspace “{title}” is registered; archived sessions under this directory join that group after a refresh.", "hist.register.done.attached": "Attached {count} existing session(s) under this directory back to the workspace.", "hist.register.done.skipped": "{count} session(s) could not be attached: {reason}", "hist.err.register": "Registration failed", "scenes.field.prompt": "Prompt preset", "scenes.agents.note.applied": "wrote “{id}” into AGENTS.md", "scenes.agents.note.restored": "restored the AGENTS.md baseline from before the scene", "scenes.agents.note.error": "AGENTS.md was not written: {reason}", "scenes.field.prompt.hint": "While this scene is enabled, its preset is the active global baseline. Defaults to the one currently in effect.", "scenes.prompt.activeTag": "in effect", "scenes.prompt.noPresets": "No prompt presets yet — create one on the Prompts page.", "scenes.prompt.bound": "Prompt: {id}", "scenes.prompt.live": "Prompt active: {id}", "scenes.prompt.missing": "Preset missing: {id}", "scenes.prompt.tag.hint": "The prompt preset bound to this scene", "scenes.enable.blocked": "Scene “{name}” is already enabled; turn it off first", "scenes.result.created.collapsed": "Scene “{name}” created and left off (the legacy “all enabled” default collapsed to a single choice)", "scenes.legacyAll": "{count} scenes are enabled at once (a leftover of the legacy “all enabled” default); only one scene besides “Global” may be enabled.", "scenes.legacyAll.fix": "Collapse to one", "agm.scene.tag": "In use by scene “{scene}”", "agm.scene.tag.hint": "Bound by that scene; takes effect with the scene switch", "agm.scene.missing": "Bound by scene “{scene}”, but the file is missing", "hist.restore.note.registered": "Restored {count} session(s); workspace “{title}” was registered again and the sessions are back in that group.", "hist.restore.note.attached": "Restored {count} session(s) and attached them to workspace “{title}”.", "hist.restore.note.skip": "Workspace ownership could not be restored for {count} session(s): {reason}", "hist.purge.title": "Delete forever?", "hist.purge.one": "Delete “{title}” and all of its records forever — this cannot be undone", "hist.purge.batch": "Delete the {count} selected sessions and all of their records forever — this cannot be undone", "hist.btn.restore": "Restore", "hist.btn.purge": "Delete forever", "hist.btn.confirmPurge": "Delete", "hist.selectGroup": "Select group {title}", "hist.group.count": "{count}", "hist.selectAll": "Select all", "hist.deselectAll": "Clear selection", "hist.btn.import": "Import sessions", "hist.btn.export": "Export sessions", "hist.selected": "{count} selected", "hist.btn.restoreSelected": "Restore selected ({count})", "hist.btn.deleteSelected": "Delete selected ({count})", "hist.stat.archived": " archived", "hist.stat.projects": " projects", "hist.stat.retention": " days kept", "hist.search.placeholder": "Search title / session id / project path", "hist.loading": "Loading…", "hist.empty.search": "No matching archived sessions", "hist.empty.none": "No archived sessions yet", "hist.import.cwd": "Project directory (optional)", "hist.import.cwd.placeholder": "Absolute path, optional (the session goes to Ungrouped)", "hist.import.drop": "Choose or drop a conversation file", "hist.import.done": "Created session {id} ({count} messages); continue it from the DSH session list.", "hist.export.hint": "Export the selected sessions as transcript files that can be imported again", "hist.export.dir": "Export directory (absolute path, created automatically)", "hist.export.browse": "Browse and choose a folder", "hist.btn.browse": "Choose", "hist.export.pickDir": "Choose export directory", "hist.export.format": "Format", "hist.export.scope": "Session scope", "hist.export.workspace": "Workspace", "hist.export.sessions": "Sessions ({count})", "hist.export.loading": "Loading sessions…", "hist.cwd.missing": "⚠ The workspace directory no longer exists: ", "hist.tag.archived": "Archived", "hist.tag.live": "Not archived", "hist.export.empty": "No matching sessions", "hist.result.exported": "Exported {count} session(s) to {dir}", "hist.result.skipped": "; skipped {count} ({ids})", "hist.result.archived": "Archived {count} session(s) to History; manage them on the History page", "hist.btn.archiveSelected": "Archive selected", "hist.btn.archiveSelected.title": "Move the selected sessions into History so retention applies to them", "hist.export.busy": "Working…", "hist.btn.exportSelected": "Export {count} session(s)", "hist.error.title": "Operation failed", "hist.error.unknown": "Unknown error",
|
|
1039
1039
|
}
|
|
1040
1040
|
};
|
|
1041
1041
|
|
|
@@ -2669,6 +2669,7 @@ function callApi(path, options) {
|
|
|
2669
2669
|
kind: meta ? meta.kind : null,
|
|
2670
2670
|
registered: !!(meta && meta.registered),
|
|
2671
2671
|
canRegister: !!(meta && meta.canRegister),
|
|
2672
|
+
dirMissing: !!(meta && meta.dirMissing),
|
|
2672
2673
|
ungrouped: key === 'ungrouped',
|
|
2673
2674
|
items: items2, maxAt: maxAt,
|
|
2674
2675
|
})
|
|
@@ -2695,7 +2696,7 @@ function callApi(path, options) {
|
|
|
2695
2696
|
items2.sort(function (a, b) { return ((b.archivedAt || b.createdAt) || 0) - ((a.archivedAt || a.createdAt) || 0) })
|
|
2696
2697
|
var maxAt = 0
|
|
2697
2698
|
items2.forEach(function (it) { var at = (it.archivedAt || it.createdAt) || 0; if (at > maxAt) maxAt = at })
|
|
2698
|
-
groups.push({ key: key, title: title, workspaceId: wid, path: wsRec && wsRec.path, kind: null, registered: false, canRegister: false, ungrouped: key === 'ungrouped', items: items2, maxAt: maxAt })
|
|
2699
|
+
groups.push({ key: key, title: title, workspaceId: wid, path: wsRec && wsRec.path, kind: null, registered: false, canRegister: false, dirMissing: false, ungrouped: key === 'ungrouped', items: items2, maxAt: maxAt })
|
|
2699
2700
|
})
|
|
2700
2701
|
// 组按组内最新时间戳降序;未分组固定最后。
|
|
2701
2702
|
groups.sort(function (g1, g2) {
|
|
@@ -2990,9 +2991,10 @@ function callApi(path, options) {
|
|
|
2990
2991
|
React.createElement('span', { className: 'dsm-source-title dsm-hist-group-title', title: g.title }, g.title),
|
|
2991
2992
|
React.createElement('span', { className: 'dsm-count' }, t('hist.group.count', { count: g.items.length }))),
|
|
2992
2993
|
detached ? React.createElement('div', { className: 'dsm-hist-group-extra' },
|
|
2993
|
-
React.createElement('span', { className: 'dsm-tag'
|
|
2994
|
+
React.createElement('span', { className: 'dsm-tag', title: t('hist.group.removed.hint') },
|
|
2994
2995
|
t('hist.group.removed')),
|
|
2995
|
-
g.canRegister ? React.createElement('button', { type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy, title: t('hist.btn.registerWs.hint'), onClick: function () { setModal({ type: 'register', path: g.path, title: g.title }) } }, t('hist.btn.registerWs')) : null
|
|
2996
|
+
g.canRegister ? React.createElement('button', { type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy, title: t('hist.btn.registerWs.hint'), onClick: function () { setModal({ type: 'register', path: g.path, title: g.title }) } }, t('hist.btn.registerWs')) : null,
|
|
2997
|
+
g.dirMissing ? React.createElement('span', { className: 'dsm-hist-dir-missing', title: t('hist.group.dirMissing.hint') }, '⚠ ' + t('hist.group.dirMissing')) : null) : null,
|
|
2996
2998
|
g.path ? React.createElement('span', { className: 'dsm-path', title: g.path }, g.path) : null),
|
|
2997
2999
|
open ? React.createElement('div', { className: 'dsm-source-body' }, g.items.map(renderRow)) : null)
|
|
2998
3000
|
}
|
|
@@ -3428,6 +3430,7 @@ function callApi(path, options) {
|
|
|
3428
3430
|
setModal({ type: 'scene-archive', name: name, drill: null, memQuery: '', skillQuery: '', subQuery: '', mcpQuery: '',
|
|
3429
3431
|
sections: {
|
|
3430
3432
|
mcp: archive.mcp ? Object.assign({}, archive.mcp) : null,
|
|
3433
|
+
mcpNotes: archive.mcpNotes && Object.keys(archive.mcpNotes).length ? Object.assign({}, archive.mcpNotes) : null,
|
|
3431
3434
|
skills: Array.isArray(archive.skills) ? archive.skills.slice() : null,
|
|
3432
3435
|
subagents: Array.isArray(archive.subagents) ? archive.subagents.slice() : null,
|
|
3433
3436
|
memories: Array.isArray(archive.memories) ? archive.memories.slice() : null,
|
|
@@ -3451,6 +3454,18 @@ function callApi(path, options) {
|
|
|
3451
3454
|
else mcp[server] = '*'
|
|
3452
3455
|
setSections(Object.assign({}, sections, { mcp: mcp }))
|
|
3453
3456
|
}
|
|
3457
|
+
/**
|
|
3458
|
+
* 场景级 MCP 备注(v0.8.1):该场景进入时覆盖全局备注、退出恢复。
|
|
3459
|
+
* 空值 = 删除该服务器的场景备注;mcpNotes 全空 → null(等同未定义,保存时不上报)。
|
|
3460
|
+
*/
|
|
3461
|
+
function setMcpNote(server, value) {
|
|
3462
|
+
var sections = modalSections(); if (!sections) return
|
|
3463
|
+
var notes = Object.assign({}, sections.mcpNotes || {})
|
|
3464
|
+
var v = String(value == null ? '' : value).trim()
|
|
3465
|
+
if (v) notes[server] = v
|
|
3466
|
+
else delete notes[server]
|
|
3467
|
+
setSections(Object.assign({}, sections, { mcpNotes: Object.keys(notes).length ? notes : null }))
|
|
3468
|
+
}
|
|
3454
3469
|
/**
|
|
3455
3470
|
* 新加段一律**默认不勾选**(用户明确要求:MCP 工具集 / 技能集 / 子智能体绑定都不预勾)。
|
|
3456
3471
|
*
|
|
@@ -3565,38 +3580,11 @@ function callApi(path, options) {
|
|
|
3565
3580
|
: React.createElement('div', { className: 'dsm-pick-empty' }, q ? t('scenes.mem.noMatch') : t('scenes.mem.emptyScene'))),
|
|
3566
3581
|
})
|
|
3567
3582
|
}
|
|
3568
|
-
/** 段 1:MCP 工具集(服务器级勾选 →
|
|
3583
|
+
/** 段 1:MCP 工具集(服务器级勾选 → 行内「编辑」打开固定尺寸弹窗:备注 + 工具勾选)。 */
|
|
3569
3584
|
function mcpSeg() {
|
|
3570
3585
|
var sections = modalSections()
|
|
3571
3586
|
var defined = !!sections.mcp
|
|
3572
3587
|
var servers = modal.inventory.mcpServers || []
|
|
3573
|
-
if (modal.drill) {
|
|
3574
|
-
var spec = (sections.mcp || {})[modal.drill]
|
|
3575
|
-
var known = drillTools
|
|
3576
|
-
var checkedCount = spec === '*' ? (known || []).length : (Array.isArray(spec) ? spec.length : 0)
|
|
3577
|
-
return seg({
|
|
3578
|
-
title: t('scenes.mcp.toolsOf') + ' · ' + modal.drill,
|
|
3579
|
-
count: t('scenes.seg.checked', { checked: checkedCount, total: (known || []).length }),
|
|
3580
|
-
actions: [
|
|
3581
|
-
React.createElement('button', { key: 'back', type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy, onClick: function () { setModal(Object.assign({}, modal, { drill: null })) } }, t('scenes.mcp.back')),
|
|
3582
|
-
React.createElement('button', { key: 'all', type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy || !known, onClick: function () { mcpDrillSpec('*') } }, t('scenes.seg.selectAll')),
|
|
3583
|
-
React.createElement('button', { key: 'none', type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy, onClick: function () { mcpDrillSpec([]) } }, t('scenes.seg.clear')),
|
|
3584
|
-
],
|
|
3585
|
-
body: known === null
|
|
3586
|
-
? React.createElement('div', { className: 'dsm-seg-body' }, React.createElement('div', { className: 'dsm-pick-empty' }, t('memory.loading')))
|
|
3587
|
-
: segList(known.map(function (item) {
|
|
3588
|
-
var on = spec === '*' ? true : (Array.isArray(spec) && spec.indexOf(item.short) >= 0)
|
|
3589
|
-
return pickRow({
|
|
3590
|
-
disabled: busy,
|
|
3591
|
-
key: item.key,
|
|
3592
|
-
checked: on,
|
|
3593
|
-
name: item.short,
|
|
3594
|
-
onChange: function () { toggleDrillTool(modal.drill, item.short) },
|
|
3595
|
-
})
|
|
3596
|
-
}), t('scenes.mcp.noTools')),
|
|
3597
|
-
foot: React.createElement('div', { className: 'dsm-seg-foot' }, t('scenes.mcp.drillHint')),
|
|
3598
|
-
})
|
|
3599
|
-
}
|
|
3600
3588
|
return seg({
|
|
3601
3589
|
title: t('memory.archive.tools'),
|
|
3602
3590
|
count: defined ? t('scenes.seg.checked', { checked: Object.keys(sections.mcp).length, total: servers.length }) : t('scenes.archive.sectionOff'),
|
|
@@ -3616,17 +3604,23 @@ function callApi(path, options) {
|
|
|
3616
3604
|
var selected = sections.mcp[server.name] !== undefined
|
|
3617
3605
|
var spec2 = sections.mcp[server.name]
|
|
3618
3606
|
var specText = !selected ? '' : spec2 === '*' ? t('scenes.mcp.allTools') : t('scenes.mcp.pickedCount', { count: spec2.length })
|
|
3607
|
+
var hasNote = Boolean((sections.mcpNotes || {})[server.name])
|
|
3608
|
+
// 工具数未知(从未运行过且无记录):显示引导而不是误导性的 0。
|
|
3609
|
+
var desc = server.toolCount === null || server.toolCount === undefined
|
|
3610
|
+
? (server.live || server.serverDisabled ? null : t('scenes.mcp.unknownCount'))
|
|
3611
|
+
: t('scenes.mcp.toolCount', { count: server.toolCount })
|
|
3619
3612
|
return pickRow({
|
|
3620
|
-
|
|
3613
|
+
disabled: busy,
|
|
3621
3614
|
key: server.name,
|
|
3622
3615
|
checked: selected,
|
|
3623
3616
|
name: server.name,
|
|
3624
|
-
desc:
|
|
3617
|
+
desc: desc,
|
|
3625
3618
|
meta: [
|
|
3626
3619
|
server.live ? null : React.createElement('span', { key: 'nr', className: 'dsm-tag dsm-tag-off' }, t('scenes.mcp.notRunning')),
|
|
3627
3620
|
specText ? React.createElement('span', { key: 'spec' }, specText) : null,
|
|
3621
|
+
hasNote ? React.createElement('span', { key: 'note', className: 'dsm-tag' }, t('scenes.mcp.hasNote')) : null,
|
|
3628
3622
|
],
|
|
3629
|
-
actions: selected ? React.createElement('button', { type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy, onClick: function (e) { e.preventDefault(); e.stopPropagation(); openDrill(server.name) } }, t('scenes.mcp.
|
|
3623
|
+
actions: selected ? React.createElement('button', { type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy, onClick: function (e) { e.preventDefault(); e.stopPropagation(); openDrill(server.name) } }, t('scenes.mcp.edit')) : null,
|
|
3630
3624
|
onChange: function () { toggleMcpServer(server.name) },
|
|
3631
3625
|
})
|
|
3632
3626
|
}))
|
|
@@ -3635,6 +3629,53 @@ function callApi(path, options) {
|
|
|
3635
3629
|
foot: archiveSegFoot(defined, defined ? Object.keys(sections.mcp).length : 0),
|
|
3636
3630
|
})
|
|
3637
3631
|
}
|
|
3632
|
+
/**
|
|
3633
|
+
* 服务器编辑弹窗(v0.8.1 重做):固定尺寸的次级弹窗,第一栏是场景备注输入框,
|
|
3634
|
+
* 第二栏是工具勾选列表 —— 「选工具」并入这里,行内不再塞备注输入框。
|
|
3635
|
+
*/
|
|
3636
|
+
function mcpEditNode() {
|
|
3637
|
+
var sections = modalSections(); if (!sections) return null
|
|
3638
|
+
var server = modal.drill
|
|
3639
|
+
var spec = (sections.mcp || {})[server]
|
|
3640
|
+
var noteVal = (sections.mcpNotes || {})[server] || ''
|
|
3641
|
+
var known = drillTools
|
|
3642
|
+
var checkedCount = spec === '*' ? (known || []).length : (Array.isArray(spec) ? spec.length : 0)
|
|
3643
|
+
return React.createElement('div', { className: 'dsm-mask', key: 'mcp-edit' },
|
|
3644
|
+
React.createElement('div', { className: 'dsm-modal' },
|
|
3645
|
+
React.createElement('div', { className: 'dsm-modal-head' },
|
|
3646
|
+
React.createElement('h3', { className: 'dsm-modal-title' }, t('scenes.mcp.edit') + ' · ' + server + '(' + t('scenes.seg.checked', { checked: checkedCount, total: (known || []).length }) + ')'),
|
|
3647
|
+
React.createElement('button', { type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy, onClick: function () { setModal(Object.assign({}, modal, { drill: null })) } }, t('scenes.mcp.done'))),
|
|
3648
|
+
React.createElement('div', { className: 'dsm-modal-body' },
|
|
3649
|
+
React.createElement('div', { className: 'dsm-field' },
|
|
3650
|
+
React.createElement('label', { className: 'dsm-label' }, t('scenes.mcp.noteLabel')),
|
|
3651
|
+
React.createElement('input', { className: 'dsm-control', type: 'text', value: noteVal, maxLength: 200, placeholder: t('scenes.mcp.notePlaceholder'), onChange: function (e) { setMcpNote(server, e.target.value) } }),
|
|
3652
|
+
React.createElement('p', { className: 'dsm-help' }, t('scenes.mcp.noteHint'))),
|
|
3653
|
+
React.createElement('div', { className: 'dsm-field' },
|
|
3654
|
+
React.createElement('div', { className: 'dsm-mcp-edit-tools-head' },
|
|
3655
|
+
React.createElement('label', { className: 'dsm-label' }, t('scenes.mcp.toolsOf')),
|
|
3656
|
+
React.createElement('div', { className: 'dsm-mcp-edit-tools-actions' },
|
|
3657
|
+
React.createElement('button', { type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy || !known, onClick: function () { mcpDrillSpec('*') } }, t('scenes.seg.selectAll')),
|
|
3658
|
+
React.createElement('button', { type: 'button', className: 'dsm-btn dsm-btn-quiet', disabled: busy, onClick: function () { mcpDrillSpec([]) } }, t('scenes.seg.clear')))),
|
|
3659
|
+
known === null
|
|
3660
|
+
? React.createElement('div', { className: 'dsm-seg-body' }, React.createElement('div', { className: 'dsm-pick-empty' }, t('memory.loading')))
|
|
3661
|
+
: (known || []).length
|
|
3662
|
+
? segList((known || []).map(function (item) {
|
|
3663
|
+
var on = spec === '*' ? true : (Array.isArray(spec) && spec.indexOf(item.short) >= 0)
|
|
3664
|
+
return pickRow({
|
|
3665
|
+
disabled: busy,
|
|
3666
|
+
key: item.key,
|
|
3667
|
+
checked: on,
|
|
3668
|
+
name: item.short,
|
|
3669
|
+
onChange: function () { toggleDrillTool(server, item.short) },
|
|
3670
|
+
})
|
|
3671
|
+
}), t('scenes.mcp.noTools'))
|
|
3672
|
+
: React.createElement('div', { className: 'dsm-seg-body' },
|
|
3673
|
+
React.createElement('div', { className: 'dsm-pick-empty' }, t('scenes.mcp.noTools')),
|
|
3674
|
+
modal.drillError ? React.createElement('div', { className: 'dsm-seg-empty-error' }, modal.drillError) : null,
|
|
3675
|
+
React.createElement('div', { className: 'dsm-seg-empty-actions' },
|
|
3676
|
+
React.createElement('button', { type: 'button', className: 'dsm-btn dsm-btn-secondary', disabled: drillTools === null, onClick: probeMcpTools }, t('scenes.mcp.probeTools')))),
|
|
3677
|
+
React.createElement('div', { className: 'dsm-seg-foot' }, t('scenes.mcp.drillHint'))))))
|
|
3678
|
+
}
|
|
3638
3679
|
/** 段 2:技能集。 */
|
|
3639
3680
|
/**
|
|
3640
3681
|
* 技能行的人类可读展示:技能名 + 来源名。
|
|
@@ -3790,7 +3831,7 @@ function callApi(path, options) {
|
|
|
3790
3831
|
*/
|
|
3791
3832
|
function archiveNode() {
|
|
3792
3833
|
if (!modal || modal.type !== 'scene-archive') return null
|
|
3793
|
-
|
|
3834
|
+
var main = React.createElement(Modal, { key: 'sarch', wide: true, className: 'dsm-modal-archive', title: t('memory.archive.title') + ' · ' + (modal.name === '' ? t('memory.scene.global') : modal.name), closeLabel: t('btn.close'), onClose: function () { setModal(null) } },
|
|
3794
3835
|
React.createElement('div', { className: 'dsm-form' },
|
|
3795
3836
|
React.createElement('div', { className: 'dsm-archive-meta' },
|
|
3796
3837
|
React.createElement('span', null, t('scenes.archive.summary', {
|
|
@@ -3805,15 +3846,27 @@ function callApi(path, options) {
|
|
|
3805
3846
|
memoriesSeg(),
|
|
3806
3847
|
React.createElement('div', { className: 'dsm-modal-actions' },
|
|
3807
3848
|
React.createElement('button', { type: 'button', className: 'dsm-btn dsm-btn-primary', disabled: busy, onClick: submitArchive }, t('memory.archive.save')))))
|
|
3849
|
+
// MCP 服务器编辑弹窗渲染在外层:不能放进列表 Modal 的滚动容器里(会被裁掉)。
|
|
3850
|
+
var edit = modal.drill ? mcpEditNode() : null
|
|
3851
|
+
return edit ? React.createElement(React.Fragment, { key: 'sarch-wrap' }, main, edit) : main
|
|
3808
3852
|
}
|
|
3809
3853
|
function openDrill(server) {
|
|
3810
|
-
setModal(Object.assign({}, modal, { drill: server }))
|
|
3854
|
+
setModal(Object.assign({}, modal, { drill: server, drillError: null }))
|
|
3811
3855
|
setDrillTools(null)
|
|
3812
3856
|
apiCall('mcpm-tools', { serverName: server }).then(function (res) {
|
|
3813
3857
|
if (res && res.ok) setDrillTools((res.tools || []).map(function (x) { return { key: server + '/' + x.name, short: x.name, enabled: x.enabled !== false } }))
|
|
3814
3858
|
else setDrillTools([])
|
|
3815
3859
|
}).catch(function () { setDrillTools([]) })
|
|
3816
3860
|
}
|
|
3861
|
+
/** 从未运行过的服务器:临时启动枚举工具(后端会轮询并在完成后恢复原启停状态)。 */
|
|
3862
|
+
function probeMcpTools() {
|
|
3863
|
+
var server = modal.drill
|
|
3864
|
+
setDrillTools(null)
|
|
3865
|
+
apiCall('mcpm-tools-refresh', { serverName: server }).then(function (res) {
|
|
3866
|
+
if (res && res.ok) setDrillTools((res.tools || []).map(function (x) { return { key: server + '/' + x.name, short: x.name, enabled: x.enabled !== false } }))
|
|
3867
|
+
else { setDrillTools([]); setModal(Object.assign({}, modal, { drillError: res && res.error ? String(res.error) : '' })) }
|
|
3868
|
+
}).catch(function (e) { setDrillTools([]); setModal(Object.assign({}, modal, { drillError: String((e && e.message) || e) })) })
|
|
3869
|
+
}
|
|
3817
3870
|
function toggleDrillTool(server, short) {
|
|
3818
3871
|
var sections = modalSections(); if (!sections) return
|
|
3819
3872
|
var mcp = Object.assign({}, sections.mcp || {})
|
|
@@ -3828,7 +3881,7 @@ function callApi(path, options) {
|
|
|
3828
3881
|
setBusy(true)
|
|
3829
3882
|
// P5:档案里**不再写 memories 段** —— 记忆的开关是 `rules[*].enabled` 单一真相源,
|
|
3830
3883
|
// 在段里勾选时已经即时提交了,这里没有「待保存」的记忆状态。
|
|
3831
|
-
apiCall('scene-archive-save', { scene: modal.name, archive: (function () { var payload = {}; if (modal.sections.mcp) payload.mcp = modal.sections.mcp; if (modal.sections.skills) payload.skills = modal.sections.skills; if (modal.sections.subagents) payload.subagents = modal.sections.subagents; return payload })() }).then(function (res) {
|
|
3884
|
+
apiCall('scene-archive-save', { scene: modal.name, archive: (function () { var payload = {}; if (modal.sections.mcp) payload.mcp = modal.sections.mcp; if (modal.sections.mcpNotes) payload.mcpNotes = modal.sections.mcpNotes; if (modal.sections.skills) payload.skills = modal.sections.skills; if (modal.sections.subagents) payload.subagents = modal.sections.subagents; return payload })() }).then(function (res) {
|
|
3832
3885
|
setBusy(false)
|
|
3833
3886
|
if (res && res.ok) {
|
|
3834
3887
|
setModal(null)
|
package/lib/compat/probe.js
CHANGED
|
@@ -41,15 +41,17 @@ export const IDENTITY_PACKAGES = [
|
|
|
41
41
|
'@deepseek-ai/dsh-storage-domain',
|
|
42
42
|
'@deepseek-ai/dsh-spill-local',
|
|
43
43
|
];
|
|
44
|
-
/** Peers this plugin was written and verified against
|
|
45
|
-
export const EXPECTED_PEER_RANGE = '>=0.1.5-rc.2
|
|
44
|
+
/** Peers this plugin was written and verified against(只声明最低版本,无上界)。 */
|
|
45
|
+
export const EXPECTED_PEER_RANGE = '>=0.1.5-rc.2';
|
|
46
46
|
/** The release this plugin's adapters were last verified against. */
|
|
47
47
|
export const VERIFIED_HOST_VERSION = '0.1.5-rc.2';
|
|
48
48
|
/**
|
|
49
49
|
* peer range 的下界 —— 界面展示用。
|
|
50
50
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
51
|
+
* 刻意不带 semver 上界:官方持续发版,硬上界会在宿主跨 minor 升级时直接挡住
|
|
52
|
+
* 插件安装(pnpm peer 校验失败)。上界改为由**运行时能力探测**兜底 ——
|
|
53
|
+
* 宿主版本高于已验证范围时,`assessHost` 会逐项探测并把缺失能力降级/禁用,
|
|
54
|
+
* 而不是在安装期拒绝整包。展示时只应把「最低要求版本」当事实。
|
|
53
55
|
* 从 EXPECTED_PEER_RANGE 派生,避免两处手写漂移。
|
|
54
56
|
*/
|
|
55
57
|
export const EXPECTED_MIN_HOST_VERSION = EXPECTED_PEER_RANGE.match(/^>=\s*([^\s]+)/)?.[1] ?? VERIFIED_HOST_VERSION;
|