dsh-paoding 0.2.2 → 0.3.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.
- package/README.md +26 -22
- package/README_en.md +26 -22
- package/bin/dsh-paoding.mjs +73 -11
- package/docs/architecture.md +36 -31
- package/docs/architecture_en.md +54 -38
- package/docs/configuration.md +151 -57
- package/docs/configuration_en.md +155 -60
- package/docs/installation.md +96 -234
- package/docs/installation_en.md +111 -252
- package/docs/orchestration.md +24 -24
- package/docs/orchestration_en.md +24 -24
- package/package.json +8 -5
- package/plugins/paoding-config-ui/api-core.mjs +215 -18
- package/plugins/paoding-config-ui/index.mjs +413 -154
- package/plugins/paoding-config-ui/lib/base.css +1083 -42
- package/plugins/paoding-config-ui/lib/client.js +2266 -252
- package/plugins/paoding-config-ui/package.json +2 -2
- package/plugins/paoding-config-ui/runtime-inventory.mjs +27 -26
- package/presets/orchestrator/agent.cordis.yml +19 -9
- package/presets/orchestrator/preset.yml +3 -2
- package/presets/orchestrator/restrict.mjs +2 -1
- package/tools/install.mjs +9 -4
- package/tools/lib/alloc.mjs +60 -29
- package/tools/lib/cli.mjs +127 -279
- package/tools/lib/compose.mjs +261 -95
- package/tools/lib/config.mjs +266 -86
- package/tools/lib/host.mjs +55 -24
- package/tools/lib/skills.mjs +72 -45
- package/tools/lib/state.mjs +172 -40
- package/tools/lib/upgrade.mjs +170 -0
- package/tools/lib/util.mjs +7 -0
- package/tools/lib/version.mjs +233 -0
- package/tools/lib/workspaces.mjs +74 -0
- package/tools/lib/yaml.mjs +92 -15
- package/install.sh +0 -31
- package/tools/lib/wizard.mjs +0 -516
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paoding-config-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|
|
7
|
-
"description": "庖丁 (dsh-paoding) 可视化配置器 — DSH Web
|
|
7
|
+
"description": "庖丁 (dsh-paoding) 可视化配置器 — DSH Web 左侧栏全页配置插件(侧栏入口 + /api/paoding 路由)",
|
|
8
8
|
"dsh": {
|
|
9
9
|
"client": {
|
|
10
10
|
"platform": "web",
|
|
@@ -62,8 +62,9 @@ function collectToolNames(value, out, seen) {
|
|
|
62
62
|
} else if (item && typeof item === 'object') {
|
|
63
63
|
if (typeof item.name === 'string') {
|
|
64
64
|
pushName(item.name, out, seen)
|
|
65
|
-
} else {
|
|
66
|
-
// 兜底:{ [key]: def } 形态的对象,按 key/def.name
|
|
65
|
+
} else if (Object.keys(item).length === 1) {
|
|
66
|
+
// 兜底:{ [key]: def } 形态的对象,按 key/def.name 处理——仅接受单键
|
|
67
|
+
// 对象(多键对象没有唯一「工具名」语义,跳过)
|
|
67
68
|
for (const [k, v] of Object.entries(item)) pushPair(k, v, out, seen)
|
|
68
69
|
}
|
|
69
70
|
}
|
|
@@ -93,25 +94,15 @@ function getService(ctx, key) {
|
|
|
93
94
|
return undefined
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
/** 收集一个对象的可调用方法名(沿原型链,去重,限量)。 */
|
|
97
|
-
function collectMethodNames(svc) {
|
|
98
|
-
const methods = []
|
|
99
|
-
for (let proto = Object.getPrototypeOf(svc); proto && proto !== Object.prototype; proto = Object.getPrototypeOf(proto)) {
|
|
100
|
-
for (const m of Object.getOwnPropertyNames(proto)) {
|
|
101
|
-
if (m !== 'constructor' && typeof svc[m] === 'function' && !methods.includes(m)) methods.push(m)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return methods
|
|
105
|
-
}
|
|
106
|
-
|
|
107
97
|
/**
|
|
108
98
|
* 读取运行时插件清单。返回 { entries, ok }:ok 表示成功拿到 list() 结果
|
|
109
99
|
* (空清单也算成功,区别于服务不可用)。
|
|
110
100
|
*/
|
|
111
101
|
function collectPluginEntries(ctx, errors) {
|
|
112
102
|
const pi = getService(ctx, 'pluginInventory')
|
|
113
|
-
if (pi == null || typeof pi.list !== 'function') return { entries: [], ok: false }
|
|
114
103
|
try {
|
|
104
|
+
// 服务属性访问也纳入 try(cordis 代理 getter 理论上可能抛):兑现「永不抛」契约
|
|
105
|
+
if (pi == null || typeof pi.list !== 'function') return { entries: [], ok: false }
|
|
115
106
|
const result = pi.list()
|
|
116
107
|
// list() 官方返回 { entries: [...] };兼容直接返回数组的形态
|
|
117
108
|
const raw = Array.isArray(result) ? result : Array.isArray(result?.entries) ? result.entries : null
|
|
@@ -135,7 +126,7 @@ function collectPluginEntries(ctx, errors) {
|
|
|
135
126
|
}
|
|
136
127
|
return { entries, ok: true }
|
|
137
128
|
} catch (err) {
|
|
138
|
-
errors.push(`pluginInventory.list()
|
|
129
|
+
errors.push(`pluginInventory.list() failed: ${String(err?.message ?? err)}`)
|
|
139
130
|
return { entries: [], ok: false }
|
|
140
131
|
}
|
|
141
132
|
}
|
|
@@ -145,20 +136,26 @@ function collectPluginEntries(ctx, errors) {
|
|
|
145
136
|
* 1) t.entries() 若可调用(Map / 迭代器 / 数组 归一化);
|
|
146
137
|
* 2) t.schemas() 若可调用(返回 [{ name, description, ... }],全局视图 =
|
|
147
138
|
* 全部已注册工具,含 mcp__<server>__<tool>);
|
|
148
|
-
* 3)
|
|
149
|
-
*
|
|
150
|
-
* 4)
|
|
139
|
+
* 3) 兜底:只试白名单里的只读枚举方法名(entries/schemas/list/keys/
|
|
140
|
+
* listKeys),不做正则泛配(避免误调写方法);
|
|
141
|
+
* 4) 全部失败返回空数组(原因记入 errors)。返回 { names, ok }。
|
|
151
142
|
*/
|
|
152
143
|
function readRegisteredToolNames(ctx, errors) {
|
|
153
144
|
const out = []
|
|
154
145
|
const seen = new Set()
|
|
155
146
|
const t = getService(ctx, 'tools')
|
|
156
|
-
if (t == null)
|
|
147
|
+
if (t == null) {
|
|
148
|
+
// 单侧失败也留痕:UI 可区分「无工具」与「tools 服务不可用」
|
|
149
|
+
errors.push('tools service unavailable')
|
|
150
|
+
return { names: [], ok: false }
|
|
151
|
+
}
|
|
157
152
|
const tried = new Set()
|
|
158
153
|
const attempt = (method, source) => {
|
|
159
|
-
if (
|
|
154
|
+
if (tried.has(method)) return false
|
|
160
155
|
tried.add(method)
|
|
161
156
|
try {
|
|
157
|
+
// 方法属性读取也纳入 try(cordis 代理 getter 可能抛)
|
|
158
|
+
if (typeof t[method] !== 'function') return false
|
|
162
159
|
const result = t[method]()
|
|
163
160
|
// 拿到可迭代结果即视为“服务可用”(即使为空清单);无结果 / 抛错都跳过
|
|
164
161
|
if (result != null && typeof result[Symbol.iterator] === 'function') {
|
|
@@ -176,14 +173,17 @@ function readRegisteredToolNames(ctx, errors) {
|
|
|
176
173
|
let acquired = attempt('entries', 'tools.entries()')
|
|
177
174
|
if (!acquired) acquired = attempt('schemas', 'tools.schemas()')
|
|
178
175
|
|
|
179
|
-
// 3)
|
|
176
|
+
// 3) 兜底:只试白名单里的只读枚举方法名(不做正则泛配);已在 1)/2) 试过
|
|
177
|
+
// 的 entries/schemas 由 tried 去重,不会重复调用
|
|
180
178
|
if (!acquired) {
|
|
181
|
-
const
|
|
182
|
-
for (const m of collectMethodNames(t)) {
|
|
183
|
-
if (!hint.test(m)) continue
|
|
179
|
+
for (const m of ['entries', 'schemas', 'list', 'keys', 'listKeys']) {
|
|
184
180
|
if (attempt(m, `tools.${m}()`)) { acquired = true; break }
|
|
185
181
|
}
|
|
186
182
|
}
|
|
183
|
+
// 有服务但枚举口全不可读:同样留痕(区别于「服务可用但工具清单为空」)
|
|
184
|
+
if (!acquired) {
|
|
185
|
+
errors.push('tools service present but no readable enumeration (entries/schemas/list/keys/listKeys)')
|
|
186
|
+
}
|
|
187
187
|
return { names: out, ok: acquired }
|
|
188
188
|
}
|
|
189
189
|
|
|
@@ -197,9 +197,10 @@ export function collectRuntimeFacts(ctx) {
|
|
|
197
197
|
const tools = readRegisteredToolNames(ctx, errors)
|
|
198
198
|
|
|
199
199
|
// ok 语义:pluginInventory 与 tools 至少一个服务真正可用即为成功;
|
|
200
|
-
// 两个都拿不到才整体失败(UI
|
|
200
|
+
// 两个都拿不到才整体失败(UI 据此提示“已回退文件扫描”)。单侧失败的原因
|
|
201
|
+
// 也已各自记入 errors(上方),UI 可区分「无工具」与「tools 服务不可用」。
|
|
201
202
|
const ok = plugin.ok || tools.ok
|
|
202
|
-
if (!ok) errors.unshift('tools
|
|
203
|
+
if (!ok) errors.unshift('pluginInventory/tools both unavailable: fell back to file scan')
|
|
203
204
|
|
|
204
205
|
let error = errors.join('; ')
|
|
205
206
|
if (error.length > MAX_ERROR_LEN) error = error.slice(0, MAX_ERROR_LEN) + '…'
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
# · orchestrator-restrict → restrict.mjs 在 system-prompt/assemble 瀑布上把
|
|
8
8
|
# 主 agent 的工具面裁剪为「编排 + 内部搜索」白名单;子 agent(origin:
|
|
9
9
|
# 'subagent')放行,由各自 toolFilter 决定工具面
|
|
10
|
-
# · delegation-* →
|
|
10
|
+
# · delegation-* → 4 个角色委派工具(dsh-tool-subagent,search_internal_deep 为静态委派),子 agent 创建时
|
|
11
11
|
# 以 per-child tools.restrict() 只保留各自 allow 的工具
|
|
12
12
|
#
|
|
13
|
-
# 工具税(每请求估算):主 agent 全量 ~16.2k → 白名单后 ~5.5k(含
|
|
14
|
-
# search_external ~3.0k / design ~3.
|
|
13
|
+
# 工具税(每请求估算):主 agent 全量 ~16.2k → 白名单后 ~5.5k(含 4 个委派工具);
|
|
14
|
+
# search_external ~3.0k / design ~3.3k / implement ~3.8k,仅实际委派时发生。
|
|
15
15
|
|
|
16
16
|
# ── identity ────────────────────────────────────────────────────────────────
|
|
17
17
|
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
- id: persona
|
|
20
20
|
name: '@deepseek-ai/dsh-persona'
|
|
21
21
|
config:
|
|
22
|
-
|
|
22
|
+
prefix: |-
|
|
23
23
|
You are the orchestrator agent powered by the {{model}} model. Your working directory is {{cwd}}.
|
|
24
24
|
|
|
25
25
|
Decompose the task, delegate to role agents, integrate results.
|
|
26
|
-
- Internal file questions (finding code, references, reading files): solve yourself with glob/grep/read
|
|
26
|
+
- Internal file questions (finding code, references, reading files): solve yourself with glob/grep/read (and codegraph if enabled) — never delegate them.
|
|
27
27
|
- External research: delegate to search_external.
|
|
28
28
|
- UI/design work: delegate to design.
|
|
29
29
|
- Code implementation: delegate to implement.
|
|
@@ -43,6 +43,11 @@
|
|
|
43
43
|
· aborted → only re-delegate when the cancellation was accidental.
|
|
44
44
|
· completed but poor output → re-delegate with concrete missing points, or send_message
|
|
45
45
|
to ask a continuable child to finish the gaps.
|
|
46
|
+
- Continuable roles (background_mode: continuable): when a delegated run fails or
|
|
47
|
+
the output falls short, first send_message in the SAME child session to continue
|
|
48
|
+
and fix in place — resuming keeps the child's accumulated context, while
|
|
49
|
+
re-delegating starts a cold task and repeats finished work. Only re-delegate
|
|
50
|
+
when the child session is gone or the task needs a different role.
|
|
46
51
|
- If the same task fails twice, stop retrying: report to the user what failed, why,
|
|
47
52
|
and what you already tried. Never loop a failing delegation.
|
|
48
53
|
- When re-delegating (fresh task or continuation), always include your integration
|
|
@@ -167,6 +172,7 @@
|
|
|
167
172
|
- id: tool-subagent-list-agents
|
|
168
173
|
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
169
174
|
|
|
175
|
+
# subagent / subagent_fork / workflow-worker-thread / workflow / ralph 仅当用户经 main_agent_extra 显式开启时对主 agent 可达;保留以对齐 standard 全量组合。
|
|
170
176
|
- id: tool-subagent
|
|
171
177
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
172
178
|
config:
|
|
@@ -207,6 +213,7 @@
|
|
|
207
213
|
config:
|
|
208
214
|
provider: spawn
|
|
209
215
|
toolName: search_external
|
|
216
|
+
backgroundMode: one-shot
|
|
210
217
|
persona: |-
|
|
211
218
|
You are the external-research agent. Your job is web research only — use web_search and the tavily tools; you never modify files. Search the web, extract key facts, and return a concise structured research summary (findings, sources, open questions).
|
|
212
219
|
On tool failure, do not retry a denied operation: report the failure compactly — what you completed, the exact error, which tool/permission you lack, and your suggested next step — so the orchestrator can re-delegate or adjust.
|
|
@@ -223,12 +230,13 @@
|
|
|
223
230
|
- read
|
|
224
231
|
- ask_user_question
|
|
225
232
|
|
|
226
|
-
# design:设计(读参考 + 设计技能 + 出 spec,不写最终代码)~3.
|
|
233
|
+
# design:设计(读参考 + 设计技能 + 出 spec,不写最终代码)~3.3k
|
|
227
234
|
- id: delegation-design
|
|
228
235
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
229
236
|
config:
|
|
230
237
|
provider: spawn
|
|
231
238
|
toolName: design
|
|
239
|
+
backgroundMode: one-shot
|
|
232
240
|
persona: |-
|
|
233
241
|
You are the design agent. You produce UI/UX designs, wireframes, and frontend specs — and may load design skills (frontend-design, html-ppt) via the skill tool. Read reference material first, then deliver a concrete design spec. You do not implement final code.
|
|
234
242
|
On tool failure, do not retry a denied operation: report the failure compactly — what you completed, the exact error, which tool/permission you lack, and your suggested next step — so the orchestrator can re-delegate or adjust.
|
|
@@ -240,26 +248,27 @@
|
|
|
240
248
|
- grep
|
|
241
249
|
- bash
|
|
242
250
|
- skill
|
|
243
|
-
-
|
|
244
|
-
- edit
|
|
251
|
+
- web_search
|
|
245
252
|
- ask_user_question
|
|
246
253
|
- todo_write
|
|
247
254
|
- job_output
|
|
248
255
|
- job_list
|
|
249
256
|
- job_kill
|
|
250
257
|
|
|
251
|
-
# implement:实现(读上下文 → 外科手术式修改 → 验证)~3.
|
|
258
|
+
# implement:实现(读上下文 → 外科手术式修改 → 验证)~3.8k
|
|
252
259
|
- id: delegation-implement
|
|
253
260
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
254
261
|
config:
|
|
255
262
|
provider: spawn
|
|
256
263
|
toolName: implement
|
|
264
|
+
backgroundMode: one-shot
|
|
257
265
|
persona: |-
|
|
258
266
|
You are the implementation agent. You write and edit code: read surrounding context first, make surgical changes, then verify (build/test/grep). Report what you changed and how you verified. You do not redesign architecture.
|
|
259
267
|
On tool failure, do not retry a denied operation: report the failure compactly — what you completed, the exact error, which tool/permission you lack, and your suggested next step — so the orchestrator can re-delegate or adjust.
|
|
260
268
|
toolFilter:
|
|
261
269
|
allow:
|
|
262
270
|
- read
|
|
271
|
+
- read_image
|
|
263
272
|
- write
|
|
264
273
|
- edit
|
|
265
274
|
- glob
|
|
@@ -287,6 +296,7 @@
|
|
|
287
296
|
config:
|
|
288
297
|
provider: spawn
|
|
289
298
|
toolName: search_internal_deep
|
|
299
|
+
backgroundMode: one-shot
|
|
290
300
|
persona: |-
|
|
291
301
|
You are the internal deep-search agent. Your job is repository exploration only — you never modify files and you never use the network. Handle exactly the tasks the orchestrator can't afford in its own context: whole-repo grep dumps, full-file reads beyond a page, cross-directory symbol/definition tracking. Search thoroughly, then return a CONCISE structured summary: what you searched, key files with line numbers, definitions/symbols found, and the takeaway the orchestrator needs to decide next. Never echo large dumps back — condense aggressively; the orchestrator re-delegates or reads specific ranges itself if it needs details.
|
|
292
302
|
On tool failure, do not retry a denied operation: report the failure compactly — what you completed, the exact error, which tool/permission you lack, and your suggested next step — so the orchestrator can re-delegate or adjust.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
name: 编排模式 (Orchestrator)
|
|
2
2
|
description: 按角色分工的编排架构——主 agent 只做编排与内部搜索(glob/grep/read/codegraph),
|
|
3
|
-
通过 search_external / design / implement
|
|
4
|
-
|
|
3
|
+
通过 search_external / design / implement / search_internal_deep 四个委派工具(search_internal_deep
|
|
4
|
+
为静态委派)把联网调研、设计、实现、内部深搜分别交给各自只加载必要工具的子 agent;
|
|
5
|
+
主 agent 不加载任何外部搜索、写文件或技能工具,
|
|
5
6
|
每请求工具税从 ~16.2k tokens 降到 ~5.5k。主 agent 技能目录注入亦归零(实测 ≈1.3k tokens/轮)。
|
|
6
7
|
order: 5
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* would intersect with — and wrongly shrink — those role surfaces.
|
|
11
11
|
*
|
|
12
12
|
* What the main agent keeps:
|
|
13
|
-
* - orchestration: search_external / design /
|
|
13
|
+
* - orchestration: 4 role-delegation tools — search_external / design /
|
|
14
|
+
* implement / search_internal_deep (the last one a static delegation),
|
|
14
15
|
* send_message / list_agents / interrupt_agent (child management)
|
|
15
16
|
* - internal search (hot path, zero delegation round-trip): glob / grep /
|
|
16
17
|
* read / read_image / bash
|
package/tools/install.mjs
CHANGED
|
@@ -3,18 +3,23 @@
|
|
|
3
3
|
* dsh-paoding installer — CLI 入口 + 公共 API 聚合薄层。
|
|
4
4
|
*
|
|
5
5
|
* 原单文件(2767 行)的功能实现已按模块拆分到 tools/lib/*.mjs(util / yaml /
|
|
6
|
-
* config / host / skills / spans / compose / alloc /
|
|
7
|
-
*
|
|
6
|
+
* config / host / skills / spans / compose / alloc / state / cli / workspaces /
|
|
7
|
+
* version / upgrade 模块树);本文件仅保留:
|
|
8
8
|
* - 公共导出面(与旧单文件完全一致,供 plugins/paoding-config-ui 消费):
|
|
9
|
-
* DEFAULT_MAIN_AGENT_PERSONA_EXTRA / collectState / generateAndInstall
|
|
9
|
+
* DEFAULT_MAIN_AGENT_PERSONA_EXTRA / collectState / generateAndInstall
|
|
10
|
+
* (外加工作区 slug / preset id 助手 assignSlugs / presetIdOf /
|
|
11
|
+
* sanitizeSlugBase / stableHash);
|
|
10
12
|
* - 直接运行守卫(CLI entry:import 本模块不触发安装,仅直接执行时经
|
|
11
|
-
* lib/cli.mjs 的 main()
|
|
13
|
+
* lib/cli.mjs 的 main() 运行;官方安装通道是 dsh plugin add dsh-paoding,
|
|
14
|
+
* 本 CLI 仅作仓库内开发兜底)。
|
|
12
15
|
*/
|
|
13
16
|
import { pathToFileURL } from 'node:url'
|
|
14
17
|
import { main } from './lib/cli.mjs'
|
|
15
18
|
|
|
16
19
|
export { DEFAULT_MAIN_AGENT_PERSONA_EXTRA } from './lib/util.mjs'
|
|
17
20
|
export { collectState, generateAndInstall } from './lib/state.mjs'
|
|
21
|
+
// 工作区 slug / preset id(api-core 的 workspaceMeta 等插件侧消费)。
|
|
22
|
+
export { assignSlugs, presetIdOf, sanitizeSlugBase, stableHash } from './lib/workspaces.mjs'
|
|
18
23
|
|
|
19
24
|
// CLI entry: only run main() when invoked directly (importing this module for
|
|
20
25
|
// the visual config UI must not trigger an install).
|
package/tools/lib/alloc.mjs
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* 分配策略:smartDefaults 按工具名关键词(keywords)给出默认角色分配、在无既有
|
|
3
3
|
* 配置时作 --auto 的兜底 assignments;baseAssignments 给出「基础模板」(仅 dsh
|
|
4
4
|
* 内置工具,fresh 非交互安装的默认起点);resolveAssignments 把既有配置 / 建议 /
|
|
5
|
-
* 静态基础合成最终 assignments
|
|
6
|
-
*
|
|
5
|
+
* 静态基础合成最终 assignments;autoAssignments 是 --auto 语义的一站式入口
|
|
6
|
+
* (CLI --auto 分支与插件首装 ensurePresetInstalled 共享,勿两边复制粘贴)。
|
|
7
|
+
* 纯参数计算,仅依赖 util 的 ROLES 常量;被 state(smartDefaults)/ cli /
|
|
8
|
+
* plugins/paoding-config-ui(autoAssignments)引用。
|
|
7
9
|
*/
|
|
8
10
|
import { ROLES } from './util.mjs'
|
|
9
11
|
|
|
@@ -12,11 +14,15 @@ import { ROLES } from './util.mjs'
|
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Default host-tool → role assignment used by --auto without a config file
|
|
15
|
-
* and as the
|
|
17
|
+
* and as the suggested values shown in the visual config UI. codegraph and
|
|
18
|
+
* memory_search stay on
|
|
16
19
|
* the main agent (matching the static preset); other MCP tools are suggested
|
|
17
|
-
* by tool-name keywords. 角色条目形状 { model, provider, persona, tools }:
|
|
20
|
+
* by tool-name keywords. 角色条目形状 { model, provider, background_mode, persona, tools }:
|
|
18
21
|
* model / provider(角色专用模型)在此恒为 null(= 不注入 agentOptions,子 agent
|
|
19
|
-
* 继承主 agent 当前模型),仅配置文件 /
|
|
22
|
+
* 继承主 agent 当前模型),仅配置文件 / 配置面板 4d 显式设置;background_mode
|
|
23
|
+
* (roles.<toolName>.background_mode,可续模式)在此恒为 'one-shot'(= 不注入
|
|
24
|
+
* backgroundMode 行,dsh-tool-subagent 缺省即 one-shot),仅配置文件 / 配置面板
|
|
25
|
+
* 显式设置为 'continuable'。
|
|
20
26
|
*/
|
|
21
27
|
export function smartDefaults(mcpReports, inventory, staticBase) {
|
|
22
28
|
const roles = {}
|
|
@@ -27,7 +33,9 @@ export function smartDefaults(mcpReports, inventory, staticBase) {
|
|
|
27
33
|
const tools = [...inventory].filter((name) => name.startsWith(`mcp__${server}__`))
|
|
28
34
|
byServer.set(server, tools)
|
|
29
35
|
const assign = (role) => {
|
|
30
|
-
|
|
36
|
+
// 与 baseAssignments / resolveAssignments 同形状(name 恒 null = 不改名):
|
|
37
|
+
// 缺 name 键会让下游形状断言/序列化层按缺省处理,和 baseAssignments 不一致。
|
|
38
|
+
roles[role] ??= { name: null, model: null, provider: null, background_mode: 'one-shot', persona: null, tools: [...staticBase[role]] }
|
|
31
39
|
for (const t of tools) if (!roles[role].tools.includes(t)) roles[role].tools.push(t)
|
|
32
40
|
}
|
|
33
41
|
if (server === 'codegraph') {
|
|
@@ -44,7 +52,8 @@ export function smartDefaults(mcpReports, inventory, staticBase) {
|
|
|
44
52
|
}
|
|
45
53
|
if (inventory.has('memory_search')) extra.push('memory_search')
|
|
46
54
|
// main_agent_persona_extra 不在此赋值(null 即可):compose 时 null 回落默认常量。
|
|
47
|
-
// roles_remove /
|
|
55
|
+
// roles_remove / main_agent_display_name 不在此赋值([] / null 即可):删除是
|
|
56
|
+
// 显式配置(显示名同理,null = 默认「编排模式 (Orchestrator)」)。
|
|
48
57
|
return {
|
|
49
58
|
roles,
|
|
50
59
|
roles_remove: [],
|
|
@@ -53,7 +62,7 @@ export function smartDefaults(mcpReports, inventory, staticBase) {
|
|
|
53
62
|
main_agent_skills: [],
|
|
54
63
|
main_agent_skills_inline: [],
|
|
55
64
|
main_agent_persona_extra: null,
|
|
56
|
-
|
|
65
|
+
main_agent_display_name: null,
|
|
57
66
|
skills: {},
|
|
58
67
|
}
|
|
59
68
|
}
|
|
@@ -64,19 +73,21 @@ export function keywords(names, pattern) {
|
|
|
64
73
|
|
|
65
74
|
/**
|
|
66
75
|
* 基础模板 assignments(fresh 非交互安装的默认起点):每个内置角色保留静态
|
|
67
|
-
* preset 的 dsh 基础工具,host/MCP 工具一概不纳入 ——
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
76
|
+
* preset 的 dsh 基础工具,host/MCP 工具一概不纳入 —— 首装(插件通道启动自愈
|
|
77
|
+
* 或 --auto)先装完可用基础版,host 工具留给用户在 设置 → 庖丁配置 里显式
|
|
78
|
+
* 开启,避免智能默认把检测到的 host 工具静默塞进委派链。形状与 smartDefaults
|
|
79
|
+
* 返回值完全一致,生成层 / 序列化层无需区分来源;staticBase 复制而非别名,
|
|
80
|
+
* 防调用方原地改写。每个内置角色条目显式带 background_mode: 'one-shot',随
|
|
81
|
+
* serializeConfig 恒写出 —— fresh 落盘的配置文件里旋钮可见。
|
|
71
82
|
*/
|
|
72
83
|
export function baseAssignments(staticBase) {
|
|
73
84
|
const roles = {}
|
|
74
85
|
for (const role of ROLES) {
|
|
75
86
|
// static preset 的角色 allow 自带静态 host 工具名(如 mcp__tavily__*);模板
|
|
76
87
|
// 一律剔除 mcp__ 前缀名(KNOWN_HOST_PLUGINS 工具不在 staticBase 里,无需再滤),
|
|
77
|
-
// 保证落盘的配置文件只含 dsh 基础工具 —— host 工具全部经 UI
|
|
88
|
+
// 保证落盘的配置文件只含 dsh 基础工具 —— host 工具全部经 UI 显式开启。
|
|
78
89
|
const baseTools = (staticBase[role] ?? []).filter((name) => !name.startsWith('mcp__'))
|
|
79
|
-
roles[role] = { name: null, model: null, provider: null, persona: null, tools: [...baseTools] }
|
|
90
|
+
roles[role] = { name: null, model: null, provider: null, background_mode: 'one-shot', persona: null, tools: [...baseTools] }
|
|
80
91
|
}
|
|
81
92
|
return {
|
|
82
93
|
roles,
|
|
@@ -86,18 +97,20 @@ export function baseAssignments(staticBase) {
|
|
|
86
97
|
main_agent_skills: [],
|
|
87
98
|
main_agent_skills_inline: [],
|
|
88
99
|
main_agent_persona_extra: null,
|
|
89
|
-
|
|
100
|
+
main_agent_display_name: null,
|
|
90
101
|
skills: {},
|
|
91
102
|
}
|
|
92
103
|
}
|
|
93
104
|
|
|
94
105
|
/**
|
|
95
106
|
* Resolve the final assignments from the existing config, smart defaults, or
|
|
96
|
-
* the
|
|
107
|
+
* the visual config UI. Returns { roles, roles_remove, main_agent_extra,
|
|
97
108
|
* main_agent_remove, main_agent_skills, main_agent_skills_inline,
|
|
98
|
-
* main_agent_persona_extra,
|
|
99
|
-
*
|
|
100
|
-
*
|
|
109
|
+
* main_agent_persona_extra, main_agent_display_name, skills };
|
|
110
|
+
* roles 条目形状
|
|
111
|
+
* { name, model, provider, background_mode, persona, tools }(model / provider =
|
|
112
|
+
* 角色专用模型,缺省 null = 不注入 agentOptions;background_mode = 角色可续模式,
|
|
113
|
+
* 缺省 'one-shot' = 不注入 backgroundMode 行)。
|
|
101
114
|
*/
|
|
102
115
|
export function resolveAssignments(existing, suggested, staticBase) {
|
|
103
116
|
if (existing) {
|
|
@@ -111,28 +124,46 @@ export function resolveAssignments(existing, suggested, staticBase) {
|
|
|
111
124
|
name: role.name ?? null,
|
|
112
125
|
model: role.model ?? null,
|
|
113
126
|
provider: role.provider ?? null,
|
|
127
|
+
// 缺键(老配置 / UI 手工构造的半成品对象)回落 'one-shot';非法值由
|
|
128
|
+
// 生成层按「非 'continuable' 即 one-shot」口径兜底。
|
|
129
|
+
background_mode: role.background_mode ?? 'one-shot',
|
|
114
130
|
persona: role.persona ?? null,
|
|
115
|
-
|
|
131
|
+
// 回落的 suggested/staticBase 数组包一层复制,防与来源别名后一处
|
|
132
|
+
// 原地 push 污染另一处(如 smartDefaults 的 assign 复用 suggested)。
|
|
133
|
+
tools: role.tools ?? [...(suggested.roles[toolName]?.tools ?? staticBase[toolName] ?? [])],
|
|
116
134
|
}
|
|
117
135
|
}
|
|
118
136
|
return {
|
|
119
137
|
roles,
|
|
120
138
|
roles_remove: existing.roles_remove ?? [],
|
|
121
|
-
// main_agent_extra
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
// main_agent_extra 回填语义:配置文件里「显式写了键」(has_main_agent_extra
|
|
140
|
+
// 标记,含显式空列表)一律尊重 —— 显式 [] = 用户明确不要任何 host 工具,
|
|
141
|
+
// 不得被智能默认悄悄回填;键缺失(老配置 / UI 手工构造的半成品对象)才用
|
|
142
|
+
// suggested.main_agent_extra 兜底。直接按 has 标记判定:原 `has || extra.length > 0`
|
|
143
|
+
// 的后半支在 has=true 时恒为假(短路先命中),且对手造输入(缺
|
|
144
|
+
// main_agent_extra 键)会抛 TypeError,一并去掉。
|
|
145
|
+
main_agent_extra: existing.has_main_agent_extra
|
|
146
|
+
? existing.main_agent_extra ?? []
|
|
147
|
+
: suggested.main_agent_extra,
|
|
129
148
|
main_agent_remove: existing.main_agent_remove ?? [],
|
|
130
149
|
main_agent_skills: existing.main_agent_skills ?? [],
|
|
131
150
|
main_agent_skills_inline: existing.main_agent_skills_inline ?? [],
|
|
132
151
|
main_agent_persona_extra: existing.main_agent_persona_extra ?? null,
|
|
133
|
-
|
|
152
|
+
main_agent_display_name: existing.main_agent_display_name ?? null,
|
|
134
153
|
skills: existing.skills,
|
|
135
154
|
}
|
|
136
155
|
}
|
|
137
156
|
return suggested
|
|
138
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* --auto 语义的 assignments 一站式构造(CLI main 的 --auto 分支与插件首装
|
|
161
|
+
* ensurePresetInstalled 共用,抽在此处防两边逻辑漂移):既有配置按配置合成
|
|
162
|
+
* (不变);fresh(无配置)缺省基础模板(仅 dsh 基础工具,host/MCP 工具留给
|
|
163
|
+
* Web 配置器显式开启),suggest=true 时改用智能默认自动纳入检测到的 host 工具。
|
|
164
|
+
* 返回值形状与 resolveAssignments 一致,可直接传 generateAndInstall。
|
|
165
|
+
*/
|
|
166
|
+
export function autoAssignments({ existing = null, suggested, staticBase, suggest = false } = {}) {
|
|
167
|
+
if (existing) return resolveAssignments(existing, suggested, staticBase)
|
|
168
|
+
return suggest ? suggested : baseAssignments(staticBase)
|
|
169
|
+
}
|