dsh-paoding 0.3.6 → 0.3.8
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 +7 -6
- package/README_en.md +7 -6
- package/docs/architecture.md +1 -1
- package/docs/architecture_en.md +1 -1
- package/docs/configuration.md +36 -2
- package/docs/configuration_en.md +36 -2
- package/docs/installation.md +7 -6
- package/docs/installation_en.md +7 -6
- package/docs/orchestration.md +20 -1
- package/docs/orchestration_en.md +20 -1
- package/package.json +1 -1
- package/plugins/paoding-config-ui/api-core.mjs +62 -31
- package/plugins/paoding-config-ui/index.mjs +29 -5
- package/plugins/paoding-config-ui/lib/client.js +139 -13
- package/plugins/paoding-config-ui/package.json +1 -1
- package/presets/orchestrator/agent.cordis.yml +11 -2
- package/tools/lib/alloc.mjs +16 -8
- package/tools/lib/compose.mjs +152 -21
- package/tools/lib/config.mjs +37 -7
- package/tools/lib/preset-system.mjs +61 -14
- package/tools/lib/state.mjs +10 -2
- package/tools/lib/upgrade.mjs +73 -26
|
@@ -14,13 +14,33 @@ import { assignSlugs, collectState, generateAndInstall, presetIdOf } from '../..
|
|
|
14
14
|
import { autoAssignments } from '../../tools/lib/alloc.mjs'
|
|
15
15
|
import {
|
|
16
16
|
backfillHomePatchRows,
|
|
17
|
+
detectHostVersionTriple,
|
|
17
18
|
detectPresetSystem,
|
|
18
19
|
formatGeneratorMarker,
|
|
20
|
+
isPtcWorkerHost,
|
|
19
21
|
listOrchestratorPresetIds,
|
|
20
22
|
parseGeneratorMarker,
|
|
21
23
|
readHomePatchRows,
|
|
24
|
+
WORKER_PTC,
|
|
25
|
+
WORKER_WORKER_THREAD,
|
|
22
26
|
} from '../../tools/lib/preset-system.mjs'
|
|
23
27
|
|
|
28
|
+
/** 宿主版本三元组 → workflow worker 标记行(生成产物行的形态,与 compose 对齐)。 */
|
|
29
|
+
function workerFlavorOf(hostVersionTriple) {
|
|
30
|
+
return isPtcWorkerHost(hostVersionTriple) ? WORKER_PTC : WORKER_WORKER_THREAD
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 标记 worker 行与当前宿主形态是否兼容:只有「产物形态」与「宿主所需形态」在
|
|
35
|
+
* ptc/worker-thread 上分叉(其中一边是 ptc)才算换代——null(v2/v1 旧标记)与
|
|
36
|
+
* worker-thread 同侧:旧标记 + 保守 worker-thread 现状 = 产物本就按旧行生成且
|
|
37
|
+
* 宿主探测不出新形态要求,不触发重生成(v2 标记 + 真实 ptc 宿主仍会触发,
|
|
38
|
+
* 0.1.5→0.2.0 升级与 v2→v3 标记升级都靠这条路径)。
|
|
39
|
+
*/
|
|
40
|
+
function workerFlavorCompatible(markerWorker, currentWorker) {
|
|
41
|
+
return (markerWorker === WORKER_PTC) === (currentWorker === WORKER_PTC)
|
|
42
|
+
}
|
|
43
|
+
|
|
24
44
|
export function resolveDshHome() {
|
|
25
45
|
return process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
|
|
26
46
|
}
|
|
@@ -151,10 +171,11 @@ export function installAssignments(state, assignments, { dryRun = false, saveCon
|
|
|
151
171
|
//
|
|
152
172
|
// 唯一官方安装通道是插件通道(dsh plugin add dsh-paoding),包不再携带 CLI:
|
|
153
173
|
// preset 的落盘改由插件启动时自愈。对账基准是 preset 目录里的 .generator-version
|
|
154
|
-
// 标记文件(
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
174
|
+
// 标记文件(v3 起内容 = 生成器版本 + 预设安装轨道 + workflow worker 形态三行):
|
|
175
|
+
// 插件包版本变、或宿主轨道翻转 / worker 形态换代(只升 dsh 不动插件——升级最
|
|
176
|
+
// 常见路径,旧标记缺行时同样解析为不符)→ 下次启动自动自愈,一举解决「升级后
|
|
177
|
+
// preset 还是旧版产物」「升 dsh 后 preset 静默消失」与「升 dsh 后 worker 行
|
|
178
|
+
// 失配 never started」三类漂移。
|
|
158
179
|
|
|
159
180
|
// 生成器版本(标记对账基准):插件包根 package.json 读一次即缓存。读失败兜底
|
|
160
181
|
// '0.0.0'——最坏后果只是每次启动多重生成一次 preset,不会装坏任何东西。
|
|
@@ -178,10 +199,13 @@ const PRESET_ENSURE_ABORTED = '插件已卸载(dispose),本次 preset 生
|
|
|
178
199
|
*
|
|
179
200
|
* 判定(满足其一即动盘):
|
|
180
201
|
* - preset 目录 / 标记缺失,或标记版本与 generatorVersion 不符 → 整盘重生成;
|
|
181
|
-
* -
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
202
|
+
* - 版本一致但标记 worker 形态与当前宿主所需分叉(宿主跨过 0.1.5/0.1.6
|
|
203
|
+
* worker 边界;v2/v1 旧标记在真实 ptc 宿主上同样分叉)→ 整盘重生成(该行
|
|
204
|
+
* 长在产物正文里,轻量路径覆盖不到);
|
|
205
|
+
* - 版本 worker 都一致但标记轨道与当前宿主轨道不符(只升 dsh 不动插件 /
|
|
206
|
+
* 旧格式标记)→ 轻量迁移:声明轨上按磁盘产物补写缺失声明行(内容字节
|
|
207
|
+
* 不动,不重生成),directory 轨上落回整盘重生成(残留声明行会让 0.1.6
|
|
208
|
+
* profile 启动失败,撤块自愈由其 directory 分支完成);
|
|
185
209
|
* - 版本轨道都一致 → 对账声明行(缺行补行、孤儿行撤行),齐则跳过。
|
|
186
210
|
* 局部 collectState() 不带运行时事实(首装触发点在插件启动、宿主服务未必就绪,
|
|
187
211
|
* 且与 CLI --auto 语义对齐;预设安装轨道由 collectState 内部按 argv 宿主版本 /
|
|
@@ -210,31 +234,38 @@ export async function ensurePresetInstalled({ dshHome = resolveDshHome(), genera
|
|
|
210
234
|
} catch { /* 目录或标记缺失:按未生成处理 */ }
|
|
211
235
|
|
|
212
236
|
if (marker !== null && marker.version === generatorVersion) {
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
237
|
+
// 版本一致:内容原则上无需重生成,但宿主可能已随 dsh 升降级翻转轨道或
|
|
238
|
+
// 跨过 workflow worker 形态边界(只升 dsh 不动插件是升级常态),声明行也
|
|
239
|
+
// 可能被外部动过——这里做轻量对账。worker 形态不符(v2/v1 旧标记没有第
|
|
240
|
+
// 三行,一律视为不符)不能走轻量路径:该行长在产物正文里,补行/撤行覆盖
|
|
241
|
+
// 不到,必须整盘重生成——落到下方重生成分支。
|
|
242
|
+
const currentWorker = workerFlavorOf(detectHostVersionTriple())
|
|
243
|
+
if (workerFlavorCompatible(marker.worker, currentWorker)) {
|
|
244
|
+
const system = await detectPresetSystem({ dshHome })
|
|
245
|
+
if (shouldAbort?.()) return { installed: false, reason: PRESET_ENSURE_ABORTED }
|
|
246
|
+
const presetIds = listOrchestratorPresetIds(dshHome)
|
|
247
|
+
if (system === 'declarative') {
|
|
248
|
+
const backfilled = backfillHomePatchRows(dshHome, presetIds)
|
|
249
|
+
if (marker.track === 'declarative' && backfilled.length === 0) {
|
|
250
|
+
return { installed: false, reason: 'preset 已由当前版本生成器产出,无需重生成' }
|
|
251
|
+
}
|
|
252
|
+
writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, 'declarative', currentWorker), 'utf8')
|
|
253
|
+
return backfilled.length > 0
|
|
254
|
+
? { installed: false, backfilled, reason: `宿主已切换声明行轨,补写 ${backfilled.length} 条预设声明行(preset 内容未重生成)` }
|
|
255
|
+
: { installed: false, reason: '预设安装轨道标记已更新为声明行轨(内容无需重生成)' }
|
|
222
256
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (marker.track === 'directory') {
|
|
232
|
-
return { installed: false, reason: 'preset 已由当前版本生成器产出,无需重生成' }
|
|
257
|
+
// directory 轨:声明行残留会让 0.1.6 profile 启动失败——有残留走整盘
|
|
258
|
+
// 重生成(directory 分支撤块自愈);行清白时只升级标记格式,不动内容。
|
|
259
|
+
if (readHomePatchRows(dshHome).length === 0) {
|
|
260
|
+
if (marker.track === 'directory') {
|
|
261
|
+
return { installed: false, reason: 'preset 已由当前版本生成器产出,无需重生成' }
|
|
262
|
+
}
|
|
263
|
+
writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, 'directory', currentWorker), 'utf8')
|
|
264
|
+
return { installed: false, reason: '预设安装轨道标记已升级为带轨道格式(内容无需重生成)' }
|
|
233
265
|
}
|
|
234
|
-
|
|
235
|
-
return { installed: false, reason: '预设安装轨道标记已升级为带轨道格式(内容无需重生成)' }
|
|
266
|
+
// 声明行残留 + directory 宿主:落到下方整盘重生成
|
|
236
267
|
}
|
|
237
|
-
//
|
|
268
|
+
// worker 形态不符(含旧格式标记升级到 v3):落到下方整盘重生成
|
|
238
269
|
}
|
|
239
270
|
|
|
240
271
|
if (shouldAbort?.()) return { installed: false, reason: PRESET_ENSURE_ABORTED }
|
|
@@ -262,7 +293,7 @@ export async function ensurePresetInstalled({ dshHome = resolveDshHome(), genera
|
|
|
262
293
|
if (shouldAbort?.()) return { installed: false, reason: PRESET_ENSURE_ABORTED }
|
|
263
294
|
mkdirSync(presetDir, { recursive: true })
|
|
264
295
|
const track = result.presetSystem === 'declarative' ? 'declarative' : 'directory'
|
|
265
|
-
writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, track), 'utf8')
|
|
296
|
+
writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, track, workerFlavorOf(state.hostVersionTriple)), 'utf8')
|
|
266
297
|
// 声明轨上整盘重生成只重建本 preset 的行:其余工作区 preset 目录若在而
|
|
267
298
|
// 声明行缺(轨道翻转后首次触发本路径),一并补齐。
|
|
268
299
|
const backfilled = track === 'declarative' ? backfillHomePatchRows(dshHome, listOrchestratorPresetIds(dshHome)) : []
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* ./api-core.mjs 复用 tools/install.mjs 的检测与生成管线;workspaces 直连
|
|
8
8
|
* 宿主 workspaceRegistry 服务(list / create,幂等);version
|
|
9
9
|
* 复用 tools/lib/version.mjs 的版本检测(npm registry 优先,npmmirror
|
|
10
|
-
* 镜像、GitHub release 依次兜底);upgrade 经 tools/lib/upgrade.mjs
|
|
11
|
-
* spawn
|
|
12
|
-
*
|
|
10
|
+
* 镜像、GitHub release 依次兜底);upgrade 经 tools/lib/upgrade.mjs 完成
|
|
11
|
+
* 一键升级(常规 profile spawn dsh 插件转发器;desktop profile 被 dsh CLI
|
|
12
|
+
* 的 Electron 专属守卫硬拒,改在 profile 目录直跑 pnpm add 精确版本)。
|
|
13
|
+
* 首装 / 升级后 preset 自动重生成(ensurePresetInstalled,等价
|
|
13
14
|
* install --auto 语义)在路由注册完成后异步触发,不阻塞启动。
|
|
14
15
|
* - Client 侧:lib/client.js 经宿主 sidebar.footer.action 键控槽在左侧栏
|
|
15
16
|
* 底部(设置行上方)注册「庖丁配置」入口,点击后在会话中栏打开全页配置
|
|
@@ -254,7 +255,30 @@ async function stateWithRuntimeFacts(ctx, { force = false } = {}) {
|
|
|
254
255
|
// apply in-flight 闸(模块级):apply 落盘期间再来的 apply 直接 409,防并发写配置。
|
|
255
256
|
let applyInFlight = false
|
|
256
257
|
|
|
257
|
-
|
|
258
|
+
// —— 宿主热切换竞态兜底 ————————————————————————————————————————————
|
|
259
|
+
// dsh 0.1.7-rc.2 / 0.2.0-rc.x 插件列表「禁用→启用」会重建条目纤维:新 apply()
|
|
260
|
+
// 先执行,旧 incarnation 的路由注销走异步结算,注册瞬间旧路由常仍在表里,
|
|
261
|
+
// webServer.register 对同 (kind, path) 一律抛 duplicate(组合层契约)。
|
|
262
|
+
// 兜底:只对 duplicate prefix route 这一种错误短重试,等旧注销落地;其余
|
|
263
|
+
// 错误原样抛出,真组合冲突不掩盖。宿主修复后此段可整体移除。
|
|
264
|
+
const DUPLICATE_PREFIX_ROUTE_RE = /^webserver: duplicate prefix route "/
|
|
265
|
+
|
|
266
|
+
/** 带兜底重试的路由注册:仅对 duplicate prefix route 错误重试,其余立即抛出。 */
|
|
267
|
+
export async function registerPrefixRouteWithRetry (webServer, route, { attempts = 8, delayMs = 30 } = {}) {
|
|
268
|
+
let lastError
|
|
269
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
270
|
+
try {
|
|
271
|
+
return webServer.register(route)
|
|
272
|
+
} catch (err) {
|
|
273
|
+
if (!DUPLICATE_PREFIX_ROUTE_RE.test(String(err?.message ?? ''))) throw err
|
|
274
|
+
lastError = err
|
|
275
|
+
if (attempt < attempts - 1) await new Promise(resolve => setTimeout(resolve, delayMs))
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
throw lastError
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export async function apply(ctx) {
|
|
258
282
|
const { webServer } = ctx
|
|
259
283
|
// dispose 探针:插件卸载后,在途异步任务(preset 自愈)不得再写盘。
|
|
260
284
|
let disposed = false
|
|
@@ -265,7 +289,7 @@ export function apply(ctx) {
|
|
|
265
289
|
try { return ctx.get('webRuntime')?.trustedHosts ?? [] } catch { return [] }
|
|
266
290
|
}
|
|
267
291
|
|
|
268
|
-
const disposer = webServer
|
|
292
|
+
const disposer = await registerPrefixRouteWithRetry(webServer, {
|
|
269
293
|
kind: 'prefix',
|
|
270
294
|
path: '/api/paoding',
|
|
271
295
|
handler: async (req, res) => {
|
|
@@ -114,8 +114,11 @@ window.__ModuleLoader__.load({
|
|
|
114
114
|
return Icon ? h(Icon, { size: size }) : null;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
// 降级 / 报错提示行。role 可选:缺省时沿用旧口径(error 行 alert 强提醒,
|
|
118
|
+
// warn 行不带 role 走低调展示);调用方显式传 "status" 则礼貌播报一次,
|
|
119
|
+
// 供「拉取失败但仍可用」这类降级提示用(不抢屏,读屏不打断)。
|
|
120
|
+
function statusRow(cls, iconNode, text, role) {
|
|
121
|
+
return h("div", { className: cls, role: role || (cls === "omd-statusError" ? "alert" : undefined) },
|
|
119
122
|
iconNode ? h("span", { className: "omd-statusIcon" }, iconNode) : null,
|
|
120
123
|
h("span", { className: "omd-statusText" }, text));
|
|
121
124
|
}
|
|
@@ -193,6 +196,27 @@ window.__ModuleLoader__.load({
|
|
|
193
196
|
"PERSISTENCE_UNAVAILABLE:需在 ~/.dsh/cordis.patch.yml 挂 " +
|
|
194
197
|
"@deepseek-ai/dsh-session-persistence-jsonl(host 层、机器级改动,详见 docs/orchestration.md)。";
|
|
195
198
|
|
|
199
|
+
// ── 启动方式(roles.<toolName>.start_mode)─────────────────────────────
|
|
200
|
+
// 定稿键名 'fresh' | 'fork',缺省 fresh。与后端序列化形状一致:config 层
|
|
201
|
+
// normalizeStartMode 恒收成这两个字面量(缺省/非法回落 'fresh',落盘恒写出),
|
|
202
|
+
// assignments 与回填照搬同款取值。注意与 roles.<toolName>.provider(LLM 路由)
|
|
203
|
+
// 无关 —— 本键只决定委派块的块级 provider 行(spawn / fork)。
|
|
204
|
+
var ROLE_SM_FORK = "fork";
|
|
205
|
+
var ROLE_SM_FRESH = "fresh";
|
|
206
|
+
|
|
207
|
+
// 角色条目 start_mode 透传归一:恰好等于 'fork' 才认,其余(缺省 / null /
|
|
208
|
+
// 手编误值)一律 'fresh'(与 normalizeStartMode 同口径)。
|
|
209
|
+
function roleStartModeOf(baseRoles, n) {
|
|
210
|
+
var r = (baseRoles || {})[n];
|
|
211
|
+
return r && r.start_mode === ROLE_SM_FORK ? ROLE_SM_FORK : ROLE_SM_FRESH;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 「继承对话(fork)」选中且该角色配了专用模型时的行内提示(定稿文案):
|
|
215
|
+
// fork 子代理继承主 agent 的提供方与模型,专用模型不生效。
|
|
216
|
+
var ROLE_SM_FORK_MODEL_HINT =
|
|
217
|
+
"继承对话的子代理随主 agent 的当前模型启动,上方专用模型不会生效;" +
|
|
218
|
+
"应用时会忽略该项模型配置。";
|
|
219
|
+
|
|
196
220
|
|
|
197
221
|
// mcp__ 前缀判定(归「MCP 工具」组的依据,供角色工具网格分组与
|
|
198
222
|
// customCoreToolsOf 使用;只做展示分组,不影响识别/分配)。非 mcp__ 名字
|
|
@@ -235,7 +259,8 @@ window.__ModuleLoader__.load({
|
|
|
235
259
|
|
|
236
260
|
// 版本提示(渲染在页头标题旁,由 PageShell 持有):常态版本号不进正文;
|
|
237
261
|
// updateAvailable 时这里渲染醒目提示卡:releaseUrl 新标签页打开 release 页、
|
|
238
|
-
// 「升级」按钮走 onUpgrade
|
|
262
|
+
// 「升级」按钮走 onUpgrade(一键插件通道自升级,通道按 profile 由服务端
|
|
263
|
+
// 分流),并保留当前版本号。
|
|
239
264
|
// latest 为 null 一律不显示新版部分;检测失败(error 非空)改渲染弱化失败行
|
|
240
265
|
// (⚠ 版本检测失败 · 重试,完整原因在 title 悬浮提示),不再整行静默。
|
|
241
266
|
// opts(可省):busy/busyOp 复用面板级互斥(任何操作进行中都禁用升级,
|
|
@@ -717,6 +742,7 @@ window.__ModuleLoader__.load({
|
|
|
717
742
|
var _swr = useState(0), switchRev = _swr[0], setSwitchRev = _swr[1];
|
|
718
743
|
var _wsl = useState([]), wsList = _wsl[0], setWsList = _wsl[1]; // 宿主工作区列表(GET /api/paoding/workspaces;失败降级为空数组,pill 由配置文件键兜底)
|
|
719
744
|
var _wslt = useState(false), wsListTried = _wslt[0], setWsListTried = _wslt[1]; // 列表请求尘埃落定标记(成败均置位):pendingOpenWs 消费条件用
|
|
745
|
+
var _wsle = useState(""), wsListErr = _wsle[0], setWsListErr = _wsle[1]; // 列表拉取失败的降级提示("" = 正常零占位):标签条只剩配置文件兜底的工作区时,给用户一句交代
|
|
720
746
|
var _wsao = useState(false), wsAddOpen = _wsao[0], setWsAddOpen = _wsao[1]; // 「添加工作区」弹窗开合态(旧版是 tab 条下方的内联展开行)
|
|
721
747
|
var _wsap = useState(""), wsAddPath = _wsap[0], setWsAddPath = _wsap[1];
|
|
722
748
|
var _wsae = useState(""), wsAddErr = _wsae[0], setWsAddErr = _wsae[1];
|
|
@@ -724,6 +750,7 @@ window.__ModuleLoader__.load({
|
|
|
724
750
|
var _ran = useState(""), roleAddName = _ran[0], setRoleAddName = _ran[1];
|
|
725
751
|
var _radn = useState(""), roleAddDispName = _radn[0], setRoleAddDispName = _radn[1]; // 弹窗第二字段「显示名」(可选;残值语义同 roleAddName,成功提交才清)
|
|
726
752
|
var _rabm = useState("one-shot"), roleAddBgMode = _rabm[0], setRoleAddBgMode = _rabm[1]; // 弹窗第三字段「会话模式」:'one-shot' | 'continuable'(残值语义同上,成功提交才复位)
|
|
753
|
+
var _rasm = useState("fresh"), roleAddStartMode = _rasm[0], setRoleAddStartMode = _rasm[1]; // 弹窗第四字段「启动方式」:'fresh' | 'fork'(残值语义同上,成功提交才复位)
|
|
727
754
|
var _rae = useState(""), roleAddErr = _rae[0], setRoleAddErr = _rae[1];
|
|
728
755
|
// 「重命名自定义角色」弹窗三件套(原原生 prompt 改模态框):For 为
|
|
729
756
|
// null = 关闭,非空 = 待改名的旧 toolName(身份键,提交时迁移 skills 引用)。
|
|
@@ -808,6 +835,9 @@ window.__ModuleLoader__.load({
|
|
|
808
835
|
// 会话模式透传:仅 'continuable' 认可,其余归 'one-shot'(字面量,
|
|
809
836
|
// 与 config 层 normalizeBackgroundMode 的序列化形状一致)
|
|
810
837
|
background_mode: roleBgModeOf(base.roles, name),
|
|
838
|
+
// 启动方式透传:仅 'fork' 认可,其余归 'fresh'(与 config 层
|
|
839
|
+
// normalizeStartMode 的序列化形状一致;决定委派块块级 provider 行)
|
|
840
|
+
start_mode: roleStartModeOf(base.roles, name),
|
|
811
841
|
tools: (base.roles && base.roles[name] && base.roles[name].tools) ||
|
|
812
842
|
(d.blocks && d.blocks[name]) || [],
|
|
813
843
|
};
|
|
@@ -822,6 +852,8 @@ window.__ModuleLoader__.load({
|
|
|
822
852
|
provider: roleStringRef(base.roles, name, "provider"),
|
|
823
853
|
// 会话模式与内置同款收法:'one-shot' = 缺省一次性,'continuable' = 可续
|
|
824
854
|
background_mode: roleBgModeOf(base.roles, name),
|
|
855
|
+
// 启动方式与内置同款收法:'fresh' = 缺省全新启动,'fork' = 继承对话
|
|
856
|
+
start_mode: roleStartModeOf(base.roles, name),
|
|
825
857
|
tools: base.roles[name].tools || [],
|
|
826
858
|
};
|
|
827
859
|
});
|
|
@@ -852,15 +884,22 @@ window.__ModuleLoader__.load({
|
|
|
852
884
|
|
|
853
885
|
// 工作区列表:面板加载时拉一次宿主注册表(与 state 并行)。失败降级为
|
|
854
886
|
// 空列表——workspaceRowsOf 会用 state.existing.workspaces 的键兜底出 pill,
|
|
855
|
-
//
|
|
887
|
+
// 绝不阻塞面板。降级不再无声:宿主侧故障(如插件实例已 disposed 的僵尸
|
|
888
|
+
// 场景)会让接口 500,标签条只剩配置文件兜底的 tab,用户容易误判成
|
|
889
|
+
// 「功能坏了」,故留一行可见提示;err.message 截短后括注原文,便于对照
|
|
890
|
+
// 日志定位。请求错误 / 响应格式异常都汇入同一个 catch,提示口径一致。
|
|
856
891
|
var loadWorkspaces = useCallback(function () {
|
|
857
892
|
api("workspaces").then(function (d) {
|
|
858
893
|
if (!d || d.ok !== true || !Array.isArray(d.items)) throw new Error("工作区列表响应格式异常");
|
|
859
894
|
setWsList(d.items);
|
|
860
895
|
setWsListTried(true);
|
|
861
|
-
|
|
896
|
+
setWsListErr("");
|
|
897
|
+
}).catch(function (err) {
|
|
862
898
|
setWsList([]); // 静默降级:只用配置文件里已有的工作区键
|
|
863
899
|
setWsListTried(true);
|
|
900
|
+
var msg = err && err.message ? String(err.message) : "";
|
|
901
|
+
setWsListErr("宿主工作区列表拉取失败,仅显示配置文件中的工作区;重启 DSH 后通常可恢复"
|
|
902
|
+
+ (msg ? "(" + (msg.length > 80 ? msg.slice(0, 80) + "…" : msg) + ")" : ""));
|
|
864
903
|
});
|
|
865
904
|
}, [api]);
|
|
866
905
|
|
|
@@ -1246,13 +1285,15 @@ window.__ModuleLoader__.load({
|
|
|
1246
1285
|
var next = JSON.parse(JSON.stringify(prev));
|
|
1247
1286
|
// 新角色全字段出厂形状:与 buildAssignments / restoreBuiltinRole 条目一致
|
|
1248
1287
|
//(model/provider 置 null = 跟随主 agent 当前模型;background_mode
|
|
1249
|
-
// 用弹窗单选值,字面量 'one-shot' | 'continuable'
|
|
1250
|
-
//
|
|
1288
|
+
// 用弹窗单选值,字面量 'one-shot' | 'continuable';start_mode 用弹窗
|
|
1289
|
+
// 单选值,字面量 'fresh' | 'fork',决定委派块块级 provider 行)。name
|
|
1290
|
+
// 是可选显示名(null = 用 toolName 当标题),不再恒 null。
|
|
1251
1291
|
next.roles[name] = {
|
|
1252
1292
|
persona: null,
|
|
1253
1293
|
name: disp,
|
|
1254
1294
|
model: null,
|
|
1255
1295
|
provider: null,
|
|
1296
|
+
start_mode: roleAddStartMode,
|
|
1256
1297
|
background_mode: roleAddBgMode,
|
|
1257
1298
|
// 出厂 tools 默认全选 = 核心工具面(customCoreToolsOf:restrictBase
|
|
1258
1299
|
// 去四个委派名与 mcp__ 工具)+ host 检测库存。两集合不相交,无需去重
|
|
@@ -1265,6 +1306,7 @@ window.__ModuleLoader__.load({
|
|
|
1265
1306
|
setRoleAddName("");
|
|
1266
1307
|
setRoleAddDispName("");
|
|
1267
1308
|
setRoleAddBgMode("one-shot"); // 成功提交即复位出厂默认(取消重开才保留残值)
|
|
1309
|
+
setRoleAddStartMode("fresh"); // 成功提交即复位出厂默认(取消重开才保留残值)
|
|
1268
1310
|
setRoleAddErr("");
|
|
1269
1311
|
toast.ok('已新建角色 "' + name + '",记得「保存并应用」');
|
|
1270
1312
|
}
|
|
@@ -1373,13 +1415,14 @@ window.__ModuleLoader__.load({
|
|
|
1373
1415
|
// (data.blocks[name]),不沿用删前自定义配置——恢复即出厂默认,简化处理。
|
|
1374
1416
|
// 显示名同样置 null(出厂默认),重建条目与 buildAssignments 形状一致。
|
|
1375
1417
|
// 专用模型 / provider 同理置 null:恢复 = 回到「跟随主 agent 当前模型」,
|
|
1376
|
-
// 不保留删前的模型固定配置。
|
|
1418
|
+
// 不保留删前的模型固定配置。start_mode 置 'fresh'、background_mode 置
|
|
1419
|
+
// 'one-shot'(出厂缺省)。
|
|
1377
1420
|
function restoreBuiltinRole(name) {
|
|
1378
1421
|
setAssign(function (prev) {
|
|
1379
1422
|
var next = JSON.parse(JSON.stringify(prev));
|
|
1380
1423
|
next.roles_remove = (next.roles_remove || []).filter(function (r) { return r !== name; });
|
|
1381
1424
|
var defTools = (data && data.blocks && data.blocks[name]) || [];
|
|
1382
|
-
next.roles[name] = { persona: null, name: null, model: null, provider: null, background_mode: "one-shot", tools: defTools.slice() };
|
|
1425
|
+
next.roles[name] = { persona: null, name: null, model: null, provider: null, start_mode: "fresh", background_mode: "one-shot", tools: defTools.slice() };
|
|
1383
1426
|
return next;
|
|
1384
1427
|
});
|
|
1385
1428
|
}
|
|
@@ -1775,9 +1818,35 @@ window.__ModuleLoader__.load({
|
|
|
1775
1818
|
roleAddBgMode === "continuable" && data && data.persistenceAvailable === false
|
|
1776
1819
|
? statusRow("omd-statusWarn", ic(P.IconWarningOutline16, 16), ROLE_BG_PERSISTENCE_WARN)
|
|
1777
1820
|
: null,
|
|
1821
|
+
// 「启动方式」单选(与角色卡 roleStartModeBlock 同款语义):缺省全新,
|
|
1822
|
+
// 选「继承对话」才写 start_mode: 'fork'(委派块块级 provider 行)。
|
|
1823
|
+
h("label", { className: "pd-modalLabel" }, "启动方式"),
|
|
1824
|
+
h("div", { className: "omd-nameRow" },
|
|
1825
|
+
h("label", { className: "omd-toolItem" },
|
|
1826
|
+
h("input", {
|
|
1827
|
+
type: "radio",
|
|
1828
|
+
className: "omd-check",
|
|
1829
|
+
name: "pd-sm-roleadd",
|
|
1830
|
+
checked: roleAddStartMode !== "fork",
|
|
1831
|
+
onChange: function () { setRoleAddStartMode("fresh"); },
|
|
1832
|
+
}),
|
|
1833
|
+
h("span", { className: "omd-toolName" }, "全新(fresh)")),
|
|
1834
|
+
h("label", { className: "omd-toolItem" },
|
|
1835
|
+
h("input", {
|
|
1836
|
+
type: "radio",
|
|
1837
|
+
className: "omd-check",
|
|
1838
|
+
name: "pd-sm-roleadd",
|
|
1839
|
+
checked: roleAddStartMode === "fork",
|
|
1840
|
+
onChange: function () { setRoleAddStartMode("fork"); },
|
|
1841
|
+
}),
|
|
1842
|
+
h("span", { className: "omd-toolName" }, "继承对话(fork)"))),
|
|
1843
|
+
roleAddStartMode === "fork"
|
|
1844
|
+
? h("div", { className: "omd-hint" },
|
|
1845
|
+
"继承对话的子代理以主 agent 已完成的对话轮次为初始内容启动(一次性快照,之后两边各自推进);工具面仍以本角色配置为准。")
|
|
1846
|
+
: null,
|
|
1778
1847
|
roleAddErr ? statusRow("omd-statusError", ic(P.IconWarningOutline16, 16), roleAddErr) : null,
|
|
1779
1848
|
h("div", { className: "omd-hint" },
|
|
1780
|
-
"角色名须为 2-32 位小写字母/数字/下划线/连字符,小写字母开头;内置名不可新建。显示名可选,不能换行,trim 后最多 60 字符,留空用 toolName 当标题。新角色工具面默认放开全部检测到的工具,可在卡片里再收窄。会话模式缺省一次性(one-shot
|
|
1849
|
+
"角色名须为 2-32 位小写字母/数字/下划线/连字符,小写字母开头;内置名不可新建。显示名可选,不能换行,trim 后最多 60 字符,留空用 toolName 当标题。新角色工具面默认放开全部检测到的工具,可在卡片里再收窄。会话模式缺省一次性(one-shot),选「可续」后仍可在角色卡里改。启动方式缺省全新(fresh),选「继承对话」后仍可在角色卡里改。")),
|
|
1781
1850
|
h("div", { className: "pd-modalFooter" },
|
|
1782
1851
|
h(P.Button, { variant: "outline", size: "md", onClick: closeRoleAddModal }, "取消"),
|
|
1783
1852
|
h(P.Button, {
|
|
@@ -2231,6 +2300,50 @@ window.__ModuleLoader__.load({
|
|
|
2231
2300
|
: null);
|
|
2232
2301
|
}
|
|
2233
2302
|
|
|
2303
|
+
// 角色「启动方式」行(内置与自定义角色通用,同 roleBgModeBlock 的定位):
|
|
2304
|
+
// 单选 roles.<toolName>.start_mode ——「全新(fresh)」为缺省(字面量
|
|
2305
|
+
// 'fresh',与 config 层 normalizeStartMode 恒收两个合法值的序列化形状
|
|
2306
|
+
// 一致),「继承对话(fork)」写 'fork'(委派块的块级 provider 行改落
|
|
2307
|
+
// fork)。选中继承对话且该角色配了专用模型时行内提示互斥:fork 子代理
|
|
2308
|
+
// 继承主 agent 的提供方与模型,专用模型不生效(应用时后端 warn 并忽略)。
|
|
2309
|
+
// 注意与「专用模型」行的 provider 字段(LLM 路由)无关。
|
|
2310
|
+
function roleStartModeBlock(name) {
|
|
2311
|
+
var role = assign.roles[name] || {};
|
|
2312
|
+
var fork = role.start_mode === "fork";
|
|
2313
|
+
var hasModel = typeof role.model === "string" && role.model !== "";
|
|
2314
|
+
function commitStartMode(toFork) {
|
|
2315
|
+
setAssign(function (prev) {
|
|
2316
|
+
var next = JSON.parse(JSON.stringify(prev));
|
|
2317
|
+
next.roles[name].start_mode = toFork ? "fork" : "fresh";
|
|
2318
|
+
return next;
|
|
2319
|
+
});
|
|
2320
|
+
}
|
|
2321
|
+
function smOption(key, label, checked, on) {
|
|
2322
|
+
return h("label", { key: key, className: "omd-toolItem" },
|
|
2323
|
+
h("input", {
|
|
2324
|
+
type: "radio",
|
|
2325
|
+
className: "omd-check",
|
|
2326
|
+
name: "pd-sm-" + name, // 每角色独立单选组;name 含 toolName 不串台
|
|
2327
|
+
checked: checked,
|
|
2328
|
+
disabled: busy,
|
|
2329
|
+
onChange: function () { commitStartMode(on); },
|
|
2330
|
+
}),
|
|
2331
|
+
h("span", { className: "omd-toolName" }, label));
|
|
2332
|
+
}
|
|
2333
|
+
return h("div", { className: "omd-mainName" },
|
|
2334
|
+
h("div", { className: "omd-glabel" }, "启动方式"),
|
|
2335
|
+
h("div", { className: "omd-nameRow" },
|
|
2336
|
+
smOption("fresh", "全新(fresh)", !fork, false),
|
|
2337
|
+
smOption("fork", "继承对话(fork)", fork, true)),
|
|
2338
|
+
fork
|
|
2339
|
+
? h("div", { className: "omd-hint" },
|
|
2340
|
+
"继承对话的子代理以主 agent 已完成的对话轮次为初始内容启动(一次性快照,之后两边各自推进);工具面仍以本角色配置为准。")
|
|
2341
|
+
: null,
|
|
2342
|
+
fork && hasModel
|
|
2343
|
+
? statusRow("omd-statusWarn", ic(P.IconWarningOutline16, 16), ROLE_SM_FORK_MODEL_HINT)
|
|
2344
|
+
: null);
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2234
2347
|
function roleCard(name) {
|
|
2235
2348
|
var isMain = name === "__main__";
|
|
2236
2349
|
var display = isMain ? "主 agent" : name;
|
|
@@ -2336,9 +2449,12 @@ window.__ModuleLoader__.load({
|
|
|
2336
2449
|
// warn 并忽略)。留空 = 跟随主 agent 当前模型。
|
|
2337
2450
|
// 「会话模式」单选紧随模型行(内置 / 自定义通用):缺省一次性,
|
|
2338
2451
|
// 选可续时行内给成本提示 + persistence 缺失警告(如有)。
|
|
2452
|
+
// 「启动方式」单选紧随会话模式行(内置 / 自定义通用):缺省全新,
|
|
2453
|
+
// 选继承对话时行内给快照语义提示;配了专用模型再叠加互斥警告。
|
|
2339
2454
|
body.push(roleTitleBlock(name, isCustom));
|
|
2340
2455
|
body.push(roleModelBlock(name));
|
|
2341
2456
|
body.push(roleBgModeBlock(name));
|
|
2457
|
+
body.push(roleStartModeBlock(name));
|
|
2342
2458
|
// 三组网格(同 roleCandidateTools 注释的分组口径):「基础工具」=
|
|
2343
2459
|
// 非 mcp__ 且不在检测库存(blocks ∪ customCoreToolsOf 减 inventory);
|
|
2344
2460
|
// 「MCP 工具」= mcp__ 前缀(含 SRC 自带行,如 tavily);「插件工具」
|
|
@@ -2457,6 +2573,15 @@ window.__ModuleLoader__.load({
|
|
|
2457
2573
|
// 配置对象(全局默认 / 各工作区,标签页)
|
|
2458
2574
|
workspaceBarOf(),
|
|
2459
2575
|
|
|
2576
|
+
// 宿主工作区列表拉取失败的可见降级提示:此时标签条只剩配置文件兜底的
|
|
2577
|
+
// tab,一声不吭的话用户会误以为「注册过的工作区不见了」是功能缺陷。
|
|
2578
|
+
// 仅请求失败 / 响应异常时出现(wsListErr 非空,成功路径零占位);贴在
|
|
2579
|
+
// 标签条容器下方另起一行,不动标签行自身的收纳 / 下拉锚点布局,
|
|
2580
|
+
// role=status 礼貌播报即可(持久化警告同款 statusRow,错误行才用 alert)。
|
|
2581
|
+
wsListErr
|
|
2582
|
+
? statusRow("omd-statusWarn", ic(P.IconWarningOutline16, 16), wsListErr, "status")
|
|
2583
|
+
: null,
|
|
2584
|
+
|
|
2460
2585
|
// 「添加工作区」弹窗:fixed 全屏遮罩不占布局流,挂在根数组末尾即可;
|
|
2461
2586
|
// 未打开时返回 null,零开销
|
|
2462
2587
|
wsModalOf(),
|
|
@@ -2977,8 +3102,9 @@ window.__ModuleLoader__.load({
|
|
|
2977
3102
|
// Esc(只收菜单 / 建议下拉 / 弹窗,不关页);确认弹窗开着时由 ConfirmLayer
|
|
2978
3103
|
// 的捕获级监听截停(只关确认框);事件都到不了这里。其余位置 Esc 一律关页。
|
|
2979
3104
|
// 一键升级(页头更新卡「升级」按钮,统一入口):
|
|
2980
|
-
// POST /api/paoding/upgrade
|
|
2981
|
-
//
|
|
3105
|
+
// POST /api/paoding/upgrade,服务端经插件通道原地换版本(常规 profile 走
|
|
3106
|
+
// dsh 插件转发器;desktop profile 被 dsh CLI 的 Electron 专属守卫硬拒,
|
|
3107
|
+
// 服务端直跑 pnpm add),分钟级操作。确认弹窗(原原生 confirm
|
|
2982
3108
|
// 改通用确认弹窗,confirmCtrl 单例见 70 片段)文案按挂载形态分流:
|
|
2983
3109
|
// dev link 直连形态(layout 由服务端 version 路由按包根 .git 判定)无法
|
|
2984
3110
|
// 就地升级,把手动切换命令说在前头;installed(registry 版)原地换版本,
|
|
@@ -2990,7 +3116,7 @@ window.__ModuleLoader__.load({
|
|
|
2990
3116
|
var isDev = verInfo.layout === "dev";
|
|
2991
3117
|
var msg = isDev
|
|
2992
3118
|
? "当前为开发 link 直连形态:无法就地升级,请手动执行 dsh plugin add dsh-paoding@latest 切换到 registry 版本。仍要继续尝试吗?"
|
|
2993
|
-
: "确定升级到 " + versionTagOf(verInfo.latest) + "
|
|
3119
|
+
: "确定升级到 " + versionTagOf(verInfo.latest) + "?升级将通过插件通道原地换版本,完成后需重启 DSH 生效。";
|
|
2994
3120
|
confirmCtrl.ask({
|
|
2995
3121
|
title: "升级 dsh-paoding",
|
|
2996
3122
|
body: msg,
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
and fix in place — resuming keeps the child's accumulated context, while
|
|
49
49
|
re-delegating starts a cold task and repeats finished work. Only re-delegate
|
|
50
50
|
when the child session is gone or the task needs a different role.
|
|
51
|
+
- A send_message that returned a delivery confirmation has been stored by the child
|
|
52
|
+
session — never resend it while awaiting the reply; collect the result via the
|
|
53
|
+
completion notification or job_output instead. Resend only when the send itself
|
|
54
|
+
failed (nothing was stored) or the child session is confirmed gone.
|
|
51
55
|
- If the same task fails twice, stop retrying: report to the user what failed, why,
|
|
52
56
|
and what you already tried. Never loop a failing delegation.
|
|
53
57
|
- When re-delegating (fresh task or continuation), always include your integration
|
|
@@ -175,7 +179,10 @@
|
|
|
175
179
|
- id: tool-subagent-list-agents
|
|
176
180
|
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
177
181
|
|
|
178
|
-
# subagent / subagent_fork / workflow
|
|
182
|
+
# subagent / subagent_fork / workflow worker / workflow / ralph 仅当用户经 main_agent_extra
|
|
183
|
+
# 显式开启时对主 agent 可达;保留以对齐 standard 全量组合。workflow worker 行
|
|
184
|
+
# 由生成器按宿主版本适配(0.1.5 = workflow-worker-thread;0.1.6+ 换成
|
|
185
|
+
# workflow-ptc——旧包 0.1.6 起已删除,残留会让预设报 never started)。
|
|
179
186
|
- id: tool-subagent
|
|
180
187
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
181
188
|
config:
|
|
@@ -206,7 +213,9 @@
|
|
|
206
213
|
|
|
207
214
|
# ── 角色委派工具 ─────────────────────────────────────────────
|
|
208
215
|
# 每个实例把一个 spawn provider 绑定到一个 toolName;主 agent 调哪个工具,
|
|
209
|
-
# 即选择该子 agent
|
|
216
|
+
# 即选择该子 agent 的工具面。块级 provider 也可写 fork —— 子 agent 以主 agent
|
|
217
|
+
# 已完成的对话轮次为初始内容启动(庖丁经 roles.<toolName>.start_mode 配置),
|
|
218
|
+
# 其余生成期行为不变。子 agent 创建时 tools.restrict(toolFilter)
|
|
210
219
|
# 校验 allow 名字必须存在于其可见注册面 —— 本组合(standard 全量 + host
|
|
211
220
|
# patch 的 mcp/memory)就是这些名字的来源,缺一行都会报 unknown tools。
|
|
212
221
|
|
package/tools/lib/alloc.mjs
CHANGED
|
@@ -17,9 +17,12 @@ import { ROLES } from './util.mjs'
|
|
|
17
17
|
* and as the suggested values shown in the visual config UI. codegraph and
|
|
18
18
|
* memory_search stay on
|
|
19
19
|
* the main agent (matching the static preset); other MCP tools are suggested
|
|
20
|
-
* by tool-name keywords. 角色条目形状 { model, provider, background_mode, persona, tools }:
|
|
20
|
+
* by tool-name keywords. 角色条目形状 { model, provider, start_mode, background_mode, persona, tools }:
|
|
21
21
|
* model / provider(角色专用模型)在此恒为 null(= 不注入 agentOptions,子 agent
|
|
22
|
-
* 继承主 agent 当前模型),仅配置文件 / 配置面板 4d 显式设置;
|
|
22
|
+
* 继承主 agent 当前模型),仅配置文件 / 配置面板 4d 显式设置;start_mode
|
|
23
|
+
* (roles.<toolName>.start_mode,角色启动方式)在此恒为 'fresh'(= 块级 provider
|
|
24
|
+
* 行保持 spawn,子 agent 全新启动),仅配置文件 / 配置面板显式设置为 'fork';
|
|
25
|
+
* background_mode
|
|
23
26
|
* (roles.<toolName>.background_mode,可续模式)在此恒为 'one-shot'(= 不注入
|
|
24
27
|
* backgroundMode 行,dsh-tool-subagent 缺省即 one-shot),仅配置文件 / 配置面板
|
|
25
28
|
* 显式设置为 'continuable'。
|
|
@@ -35,7 +38,7 @@ export function smartDefaults(mcpReports, inventory, staticBase) {
|
|
|
35
38
|
const assign = (role) => {
|
|
36
39
|
// 与 baseAssignments / resolveAssignments 同形状(name 恒 null = 不改名):
|
|
37
40
|
// 缺 name 键会让下游形状断言/序列化层按缺省处理,和 baseAssignments 不一致。
|
|
38
|
-
roles[role] ??= { name: null, model: null, provider: null, background_mode: 'one-shot', persona: null, tools: [...staticBase[role]] }
|
|
41
|
+
roles[role] ??= { name: null, model: null, provider: null, start_mode: 'fresh', background_mode: 'one-shot', persona: null, tools: [...staticBase[role]] }
|
|
39
42
|
for (const t of tools) if (!roles[role].tools.includes(t)) roles[role].tools.push(t)
|
|
40
43
|
}
|
|
41
44
|
if (server === 'codegraph') {
|
|
@@ -77,8 +80,9 @@ export function keywords(names, pattern) {
|
|
|
77
80
|
* 或 --auto)先装完可用基础版,host 工具留给用户在侧栏底部「庖丁配置」入口
|
|
78
81
|
* 里显式开启,避免智能默认把检测到的 host 工具静默塞进委派链。形状与 smartDefaults
|
|
79
82
|
* 返回值完全一致,生成层 / 序列化层无需区分来源;staticBase 复制而非别名,
|
|
80
|
-
* 防调用方原地改写。每个内置角色条目显式带
|
|
81
|
-
* serializeConfig 恒写出 —— fresh
|
|
83
|
+
* 防调用方原地改写。每个内置角色条目显式带 start_mode: 'fresh' 与
|
|
84
|
+
* background_mode: 'one-shot',随 serializeConfig 恒写出 —— fresh 落盘的配置
|
|
85
|
+
* 文件里旋钮可见。
|
|
82
86
|
*/
|
|
83
87
|
export function baseAssignments(staticBase) {
|
|
84
88
|
const roles = {}
|
|
@@ -87,7 +91,7 @@ export function baseAssignments(staticBase) {
|
|
|
87
91
|
// 一律剔除 mcp__ 前缀名(KNOWN_HOST_PLUGINS 工具不在 staticBase 里,无需再滤),
|
|
88
92
|
// 保证落盘的配置文件只含 dsh 基础工具 —— host 工具全部经 UI 显式开启。
|
|
89
93
|
const baseTools = (staticBase[role] ?? []).filter((name) => !name.startsWith('mcp__'))
|
|
90
|
-
roles[role] = { name: null, model: null, provider: null, background_mode: 'one-shot', persona: null, tools: [...baseTools] }
|
|
94
|
+
roles[role] = { name: null, model: null, provider: null, start_mode: 'fresh', background_mode: 'one-shot', persona: null, tools: [...baseTools] }
|
|
91
95
|
}
|
|
92
96
|
return {
|
|
93
97
|
roles,
|
|
@@ -108,8 +112,9 @@ export function baseAssignments(staticBase) {
|
|
|
108
112
|
* main_agent_remove, main_agent_skills, main_agent_skills_inline,
|
|
109
113
|
* main_agent_persona_extra, main_agent_display_name, skills };
|
|
110
114
|
* roles 条目形状
|
|
111
|
-
* { name, model, provider, background_mode, persona, tools }(model / provider =
|
|
112
|
-
* 角色专用模型,缺省 null = 不注入 agentOptions;
|
|
115
|
+
* { name, model, provider, start_mode, background_mode, persona, tools }(model / provider =
|
|
116
|
+
* 角色专用模型,缺省 null = 不注入 agentOptions;start_mode = 角色启动方式,
|
|
117
|
+
* 缺省 'fresh' = 块级 provider 行保持 spawn;background_mode = 角色可续模式,
|
|
113
118
|
* 缺省 'one-shot' = 不注入 backgroundMode 行)。
|
|
114
119
|
*/
|
|
115
120
|
export function resolveAssignments(existing, suggested, staticBase) {
|
|
@@ -124,6 +129,9 @@ export function resolveAssignments(existing, suggested, staticBase) {
|
|
|
124
129
|
name: role.name ?? null,
|
|
125
130
|
model: role.model ?? null,
|
|
126
131
|
provider: role.provider ?? null,
|
|
132
|
+
// 缺键(老配置 / UI 手工构造的半成品对象)回落 'fresh';非法值由
|
|
133
|
+
// 生成层按「非 'fork' 即 fresh」口径兜底。
|
|
134
|
+
start_mode: role.start_mode ?? 'fresh',
|
|
127
135
|
// 缺键(老配置 / UI 手工构造的半成品对象)回落 'one-shot';非法值由
|
|
128
136
|
// 生成层按「非 'continuable' 即 one-shot」口径兜底。
|
|
129
137
|
background_mode: role.background_mode ?? 'one-shot',
|