dsh-expert-group 0.1.11 → 0.1.12

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/client-bundle.js CHANGED
@@ -17334,7 +17334,7 @@ renderMainPeriodHint(view,mainPeriod),
17334
17334
  view.peakValleyBroken
17335
17335
  ?react.createElement("p",{className:"deg_settings_error"},view.tExtra("settings.pv.broken"))
17336
17336
  :null,
17337
- mainPeriod===null?modelConfigSection:null,
17337
+ modelConfigSection,
17338
17338
  renderSettingsTaskRow(view),
17339
17339
  renderSettingsNotificationRow(view),
17340
17340
  renderSettingsUnifiedRow(modelView),
@@ -1386,7 +1386,10 @@
1386
1386
  view.peakValleyBroken
1387
1387
  ? react.createElement("p", { className: "deg_settings_error" }, view.tExtra("settings.pv.broken"))
1388
1388
  : null,
1389
- mainPeriod === null ? modelConfigSection : null,
1389
+ // provider 目录是宿主级共享数据(DSH 的 llm 命名空间),不属于 peak / valley 任何一层:
1390
+ // 峰谷启用、本页锁定为「当前时段」编辑视图时仍然渲染,位置与普通模式逐字相同
1391
+ // (副标题之后、任务行之前),切换峰谷前后区域不跳位。
1392
+ modelConfigSection,
1390
1393
  renderSettingsTaskRow(view),
1391
1394
  renderSettingsNotificationRow(view),
1392
1395
  renderSettingsUnifiedRow(modelView),
package/client.js CHANGED
@@ -17334,7 +17334,7 @@ renderMainPeriodHint(view,mainPeriod),
17334
17334
  view.peakValleyBroken
17335
17335
  ?react.createElement("p",{className:"deg_settings_error"},view.tExtra("settings.pv.broken"))
17336
17336
  :null,
17337
- mainPeriod===null?modelConfigSection:null,
17337
+ modelConfigSection,
17338
17338
  renderSettingsTaskRow(view),
17339
17339
  renderSettingsNotificationRow(view),
17340
17340
  renderSettingsUnifiedRow(modelView),
@@ -218,7 +218,9 @@ export function assembleRootWait(state, caps) {
218
218
  state.rootWaitDomain = rootWaitDomain;
219
219
  const { isRootAgentIdentity } = rootWaitDomain;
220
220
 
221
- const roots = typeof agents?.roots === "function" ? agents.roots() : [];
221
+ // rc.2 删除 agents.roots():统一走 root wait 域的双契约枚举(rc.1 roots() /
222
+ // rc.2 list()+root 身份过滤),保证预存 root 仍能登记 lineage 与代次。
223
+ const roots = rootWaitDomain.listRootAgents();
222
224
  for (const root of roots) {
223
225
  if (isRootAgentIdentity(root) && typeof root.id === "string") {
224
226
  host.lineage.activateRoot(root);
@@ -51,7 +51,6 @@ import {
51
51
  */
52
52
  export function assembleGuards(state, caps) {
53
53
  const { logger } = state;
54
- const { agents } = state.services;
55
54
  const { delegationTracker, forgetRootDisposed, rootUserWaitGuard } = state;
56
55
 
57
56
  /**
@@ -64,8 +63,18 @@ export function assembleGuards(state, caps) {
64
63
  state.expertReportGuardDisposers = new Map();
65
64
  state.rootStructuredGuardStopping = false;
66
65
 
66
+ /**
67
+ * root 身份判定(不依赖宿主 roots() 枚举)。
68
+ *
69
+ * 旧实现用 `agents.roots().includes(agent)`;0.2.2-rc.2 删除 roots() 后会恒为 false,
70
+ * 导致 root 结构化文件 guard 在 rc.2 上完全失效。这里改用 root wait 域持有的同一份
71
+ * 结构身份判据(无 parentSession / 无 DEG PARENT marker),两代宿主语义一致。
72
+ */
67
73
  function isRootAgentForStructuredGuard(agent) {
68
- return typeof agents?.roots === "function" && agents.roots().includes(agent);
74
+ // 复用双契约枚举(rc.1 roots() / rc.2 list()+身份过滤)而不是直接判定结构身份:
75
+ // 既保留旧实现"只把真正登记在宿主 root 集合里的 Agent 视为 root"的语义,
76
+ // 也避免 rc.2 上恒为 false 让 root guard 静默失效。
77
+ return state.listRootAgents().includes(agent);
69
78
  }
70
79
  /**
71
80
  * 返回 root 结构化工具 guard 可用的 workspace root。
@@ -190,7 +199,8 @@ export function assembleGuards(state, caps) {
190
199
  installRootStructuredPathGuard(agent);
191
200
  installExpertReportGuardForAgent(agent);
192
201
  });
193
- const roots = typeof agents?.roots === "function" ? agents.roots() : [];
202
+ // rc.2 删除 agents.roots():用双契约枚举补齐插件挂载时已存在的 root。
203
+ const roots = state.listRootAgents();
194
204
  for (const agent of roots) {
195
205
  installRootStructuredPathGuard(agent);
196
206
  }
@@ -563,6 +563,9 @@ export function createRecoveryChildRebuild(deps) {
563
563
  request,
564
564
  label: source.label,
565
565
  signal: op.controller?.signal,
566
+ // 恢复重建同样是新 activation:父 Agent 已在 dispatch 边界验证为当前存活对象,
567
+ // rc.2 的 exact-parent 校验依赖它。
568
+ parent,
566
569
  }));
567
570
  if (!lineage.isLiveGeneration(parent, rootToken)) throw new Error("root generation is no longer live");
568
571
  } catch (error) {
@@ -280,6 +280,8 @@ export function createRoleToolRebuild({ records, core, identity, delegation, rec
280
280
  request,
281
281
  label,
282
282
  signal,
283
+ // rc.2 要求 spec.parent 是精确存活父 Agent;同 id 的 replacement 不能被冒充。
284
+ parent,
283
285
  }));
284
286
  } catch (error) {
285
287
  const capacityError = createSubagentCapacityError(error);
@@ -585,6 +587,8 @@ export function createRoleToolNewChild({ delegation, records, handoff, recovery,
585
587
  request,
586
588
  label,
587
589
  signal,
590
+ // 只传本次工具调用已通过 isLiveGeneration 校验的 exact parent。
591
+ parent,
588
592
  }));
589
593
  } catch (error) {
590
594
  // 容量耗尽在 admission 边界就翻译成 DEG 自有可读错误:后续清理/回滚错误
@@ -426,9 +426,9 @@ export function createExpertChannelResolver({ subagents, storage }) {
426
426
  /**
427
427
  * 宿主当前注册的 provider 名列表。
428
428
  *
429
- * rc.2 的 SubagentService 公开 `list()`(返回注册名、插入顺序);旧替身可能没有
430
- * 该 seam,此时返回 undefined,调用方只按默认名探测。绝不用私有 map 或猜测
431
- * provider 名。
429
+ * 0.2.2-rc.2 删除了该枚举 seam(只有 rc.1 及更早的 SubagentService 有 `list()`);
430
+ * 因此必须按「seam 不存在 → undefined」降级,调用方只按默认名 + 能力位探测。
431
+ * 绝不用私有 map 或猜测 provider 名。
432
432
  *
433
433
  * @returns {string[]|undefined} 注册名数组;seam 缺失/抛错/形状非法时 undefined。
434
434
  */
@@ -655,7 +655,7 @@ export function createExpertChannelResolver({ subagents, storage }) {
655
655
  }
656
656
 
657
657
  /**
658
- * DEG-only 的双契约启动适配器(RC1 startActivation / legacy startContinuable)。
658
+ * DEG-only 的三代启动适配器(rc.2 扁平 startActivation / rc.1 嵌套 startActivation / legacy startContinuable)。
659
659
  *
660
660
  * @param {object} deps - 窄能力面。
661
661
  * @param {object} deps.subagents - subagent 服务。
@@ -667,19 +667,45 @@ export function createExpertActivationStarter({ subagents, channel, structured }
667
667
  const { contextInheritanceError, validSubagentProviderName } = channel;
668
668
  const { armStructuredResultCapture } = structured;
669
669
  /**
670
- * DEG-only 的双契约启动适配器。
670
+ * 宿主 startActivation 的 spec 形状探测(只读、无副作用)。
671
671
  *
672
- * startActivation 存在时只组装 RC1 公共字段:request 仅剥离顶层 label 与 signal
673
- * (label 走 spec.label、signal 走 spec.signal),其余字段原样透传。persona 与
674
- * toolFilter 是 RC1 的 child composition 注入字段——宿主在 child scope 上应用
675
- * 它们(persona → child 系统提示词 section,toolFilter → childCtx.tools.restrict),
672
+ * 0.2.2-rc.2 把 SubagentActivationSpec 从「request 嵌套」扁平化为直接继承
673
+ * SubagentStartRequest,并把 parent 提升为顶层必填字段;同一次改动新增了
674
+ * namesOf / rootOf / resolveName / waitForTreeWork 这些具名多层级协作 seam,并移除了
675
+ * list() 与 waitForChildren()。这里只做能力探测:任一 rc.2 新增的公开方法存在就按
676
+ * 扁平契约调用;都不存在则保持 0.2.1 / 0.2.2-rc.1 的嵌套 request 契约。
677
+ * 探测必须在调用 startActivation 之前完成——两代契约的 spec 形状互不兼容,猜错会让
678
+ * 宿主解引用 undefined.parent/.request 直接抛错。
679
+ *
680
+ * @returns {boolean} 宿主是否使用 rc.2+ 的扁平 activation spec。
681
+ */
682
+ function activationSpecIsFlat() {
683
+ return typeof subagents.namesOf === "function"
684
+ || typeof subagents.rootOf === "function"
685
+ || typeof subagents.resolveName === "function"
686
+ || typeof subagents.waitForTreeWork === "function";
687
+ }
688
+
689
+ /**
690
+ * DEG-only 的三代启动适配器。
691
+ *
692
+ * startActivation 存在时按宿主契约组装 spec:request 仅剥离 DEG 私有字段
693
+ * (顶层 label 与 signal 走 spec 字段,subagentProvider 只用于选择宿主 provider),
694
+ * 其余字段原样透传。parent 必须是**精确的存活父 Agent**——rc.2 在 service 边界用
695
+ * `agents.get(parent.id) === parent` 强校验,缺失会让整个派发以 TypeError 崩溃;
696
+ * rc.1 则从 spec.request.parent 读取同一对象。因此调用方显式传入 parent,
697
+ * request.parent 只作为旧调用点兜底,两代契约都写入同一个精确值。
698
+ *
699
+ * persona 与 toolFilter 是 rc.1 的 child composition 注入字段——宿主在 child scope
700
+ * 上应用它们(persona → child 系统提示词 section,toolFilter → childCtx.tools.restrict),
676
701
  * 因此既不能剥离,也不能把 persona 拼进首条任务消息:任务消息只保留
677
- * buildExpertTaskPrompt 生成的「身份行 + 任务内容」两段。delivery 固定为 parent,
678
- * 让 DSH manager 负责 parent notification 和 activation 生命周期。
702
+ * buildExpertTaskPrompt 生成的「身份行 + 任务内容」两段。rc.2 不再从 activation spec
703
+ * 读取这两个字段,DEG 由 agent/created 的 composition 兜底安装同一份边界。
704
+ * delivery 固定为 parent,让 DSH manager 负责 parent notification 和 activation 生命周期。
679
705
  * startActivation 本身抛错时原样上抛,只有方法不存在才回退旧 startContinuable,
680
706
  * 避免把 admission 错误伪装成兼容路径。
681
707
  */
682
- async function startExpertActivation({ request, label, signal }) {
708
+ async function startExpertActivation({ request, label, signal, parent }) {
683
709
  // 目标 subagent provider(票据 29):唯一来源是请求上的内部字段,所有调用点
684
710
  // (角色工具、fan-out 分片、恢复重建)在组装请求时写入同一份值。它只用于选择
685
711
  // 宿主 provider,必须在调用宿主前剥离,绝不作为 SubagentStartRequest 字段透传。
@@ -699,13 +725,25 @@ export function createExpertActivationStarter({ subagents, channel, structured }
699
725
  subagentProvider: _requestProvider,
700
726
  ...activationRequest
701
727
  } = request ?? {};
702
- const activation = await subagents.startActivation({
728
+ // 精确的存活父 Agent:rc.2 的 manager 会先解引用 spec.parent.id,再做对象身份校验;
729
+ // 这里在插件边界 fail-closed,避免把 undefined.parent 变成宿主内部 TypeError。
730
+ const activationParent = parent ?? activationRequest.parent;
731
+ if (activationParent === null || typeof activationParent !== "object"
732
+ || typeof activationParent.id !== "string" || activationParent.id.length === 0) {
733
+ throw new TypeError("RC startActivation requires an exact live parent agent");
734
+ }
735
+ const specBase = {
703
736
  provider: targetProvider,
704
737
  label: typeof label === "string" ? label : request?.label,
705
- request: activationRequest,
706
738
  signal,
707
739
  delivery: "parent",
708
- });
740
+ };
741
+ // 两代契约都携带同一份精确 parent:rc.1 读 spec.request.parent,rc.2 读 spec.parent。
742
+ // 未知字段(persona/toolFilter 等)在 rc.2 走本地 provider 时被忽略,行为由 DEG 的
743
+ // agent/created composition 兜底保证;绝不为了适配 rc.2 把 rc.1 的合同时序改掉。
744
+ const activation = activationSpecIsFlat()
745
+ ? await subagents.startActivation({ ...specBase, ...activationRequest, parent: activationParent })
746
+ : await subagents.startActivation({ ...specBase, request: { ...activationRequest, parent: activationParent } });
709
747
  // 结构化交付(票据 30):启动成功即挂接 result,绝不在这里 await 完整结果。
710
748
  // request 上的 outputSchema 原样透传给宿主;child_models 只写 structured/标记。
711
749
  armStructuredResultCapture(activation, request);
@@ -1238,8 +1238,11 @@ export function createFanoutToolHooks({ identity, childIo, core, waits, requestB
1238
1238
  });
1239
1239
  }
1240
1240
 
1241
- function startContinuable({ request, signal }) {
1242
- return startExpertActivation({ request, label: request.label, signal });
1241
+ function startContinuable({ request, parentAgent, signal }) {
1242
+ // 0.2.2-rc.2 起宿主强制 spec.parent 为精确存活父 Agent:fan-out lifecycle 已在
1243
+ // admission 时从 lineage 解析出 exact parentAgent(冷恢复时按 parentId 重新解析),
1244
+ // 这里原样透传;缺失时由 startExpertActivation 在插件边界 fail-closed。
1245
+ return startExpertActivation({ request, label: request.label, signal, parent: parentAgent });
1243
1246
  }
1244
1247
 
1245
1248
  /**
@@ -199,6 +199,37 @@ function installRootWaitCore(deps) {
199
199
  && agent.options?.[PARENT_MARKER] === void 0;
200
200
  }
201
201
 
202
+ /**
203
+ * 当前存活 root Agent 快照(rc.1 roots() / rc.2 list() 双契约)。
204
+ *
205
+ * 0.2.2-rc.2 删除了 `AgentRegistry.roots()`,只保留 `list()`(全部 live agent);
206
+ * 插件必须在两代宿主上给出同一份「可枚举 root」事实:优先用 rc.1 的精确 roots(),
207
+ * 缺失时用 rc.2 的 list() 再按 DEG 自己的 root 身份判定过滤(无 parentSession、
208
+ * 无 DEG PARENT marker)。两个 seam 都缺失或抛错时返回空数组——与旧实现
209
+ * `typeof agents.roots !== "function" → []` 的降级语义一致,绝不猜测或伪造 root。
210
+ *
211
+ * @returns {Array<object>} root Agent 只读快照。
212
+ */
213
+ function listRootAgents() {
214
+ try {
215
+ if (typeof agents?.roots === "function") {
216
+ const roots = agents.roots();
217
+ return Array.isArray(roots) ? roots : [];
218
+ }
219
+ if (typeof agents?.list === "function") {
220
+ const all = agents.list();
221
+ // list() 的契约是 Agent 数组;仍先挡 null/非对象,避免身份判定在坏快照上抛错
222
+ // 被外层 catch 静默吞成"没有 root"(丢失可诊断性)。
223
+ return Array.isArray(all)
224
+ ? all.filter((agent) => agent !== null && typeof agent === "object" && isRootAgentIdentity(agent))
225
+ : [];
226
+ }
227
+ } catch (error) {
228
+ logTurnWaitError("list roots", error);
229
+ }
230
+ return [];
231
+ }
232
+
202
233
  function isLiveRootAgent(agent) {
203
234
  return host.lineage.isLiveRoot(agent);
204
235
  }
@@ -361,6 +392,7 @@ function installRootWaitCore(deps) {
361
392
  safeErrorLabel,
362
393
  logTurnWaitError,
363
394
  isRootAgentIdentity,
395
+ listRootAgents,
364
396
  isLiveRootAgent,
365
397
  invalidateRootWaitId,
366
398
  invalidateWaitersForAgentActivity,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-expert-group",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "6个可单独设置模型的专家子代理,侧边栏任务展示,弹窗通知",
5
5
  "icon": "./icon.svg",
6
6
  "type": "module",