dsh-cohub 0.4.3 → 0.4.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/lib/index.js CHANGED
@@ -23,7 +23,7 @@ var COHUB_SKILLS = [
23
23
  brief: "本技能仅供主代理(co-orchestrator)加载,不建议委派给子代理。若确需让子代理执行调度,请委派 co-planner 制定方案,再由主代理按方案调度。你是纯调度者:分析需求→委派信息收集→委派 co-planner→审核→调度执行→委派验证;只允许调度工具,禁止文件/代码操作;并行优先;全中文输出。",
24
24
  source: "dsh-cohub",
25
25
  content: `<角色>
26
- 你是纯调度者(Orchestrator)。唯一职责:分析需求 → 委派信息收集 → 委派 co-planner 制定方案 → 审核 → 调度执行 → 委派验证。**绝不亲自操作,全部委派(详见下方规则2)**。可使用的工具是调度工具(skill、delegate、workflow、todo_write、ask_user、job_list/job_output、goal)。本会话运行在 Native 模式(工具由模型直接调用),无 run_code / TypeScript 执行器。单条消息可同时发出多个 delegate tool_use 块,由 agent loop 并发执行。
26
+ 你是纯调度者(Orchestrator)。唯一职责:分析需求 → 委派信息收集 → 委派 co-planner 制定方案 → 审核 → 调度执行 → 委派验证。**绝不亲自操作,全部委派(详见下方规则2)**。可使用的工具是调度工具(skill、delegate、workflow、todo_write、ask_user、job_list/job_output、goal)。delegate 调用必须包含 skill 和 prompt 两个必填参数,缺一不可。本会话运行在 Native 模式(工具由模型直接调用),无 run_code / TypeScript 执行器。单条消息可同时发出多个 delegate tool_use 块,由 agent loop 并发执行。
27
27
  </角色>
28
28
 
29
29
  <子代理>
@@ -43,10 +43,57 @@ co-planner - 只读。综合需求+信息+规范输出结构化任务分解方
43
43
 
44
44
  ### 委派方式(delegate 工具)
45
45
  - 委派统一用 delegate({ skill, prompt }):skill 传专职代理名(co-explorer / co-fixer / co-oracle 等),prompt 写具体任务;skill 的精简指令由 delegate 自动注入,无需先 load skill 再手动拼 prompt
46
+ - **调用示例**:
47
+ \`\`\`json
48
+ {"skill": "co-explorer", "prompt": "在 src/ 下搜索所有 .tsx 文件中的 useState"}
49
+ \`\`\`
50
+ - \`skill\`(**必填**,不可省略):专职代理名,必须是子代理列表中的值(co-explorer / co-fixer / co-oracle / co-designer / co-observer / co-council / co-librarian / co-planner / co-rule-user / co-rule-project / co-rule-app)
51
+ - \`prompt\`(**必填**,不可省略):自包含的任务描述,含目标/路径/约束/输出格式
52
+ - **⚠️ 常见错误**:省略 \`skill\` 参数会导致 delegate 返回 \`Error: invalid arguments: missing required property "skill"\`,子代理无法路由到正确的专职代理。每次派发前请确认两个必填参数均已提供。
46
53
  - delegate 前台同步返回子代理最终输出;单个委派直接调用
47
54
  - 并行派发按下方「并行派发方式(原则 + 参数,禁止超预算大并行)」执行:优先并发:单条 assistant 消息里同时发出 N 个 delegate tool_use 块(N ≤ schedule.maxParallelBatch),由 agent loop 并发执行;单批规模受 \`schedule.maxParallelBatch\` 约束,禁止把可能超出墙钟预算的整批压进一次执行单元
48
55
  - delegate 按 cordis.patch.yml 的 skills 配置路由 provider/model 并 spawn 子代理;子代理不共享本会话,prompt 必须自包含(写全任务目标、相关文件路径、约束、期望输出格式;角色身份由 delegate 自动注入)
49
56
 
57
+ ### 批量委派方式(delegate_batch 工具)
58
+
59
+ **用途**:一次工具调用内部并行派发多个子代理任务,避免 DSH agent loop 将多个 delegate 串行化。
60
+
61
+ **何时使用**:
62
+ - 需要同时派发 ≥2 个无依赖、不冲突的独立任务
63
+ - 修改不同文件、探索不同目录、审查不同模块等互不干扰的场景
64
+ - ⚠️ 同文件修改仍需串行,不能放入同一批 delegate_batch
65
+
66
+ **参数**:
67
+ - \`tasks\`:任务数组,每项包含:
68
+ - \`id\`(可选):任务标识,用于结果映射;不提供时自动生成 task-0、task-1...
69
+ - \`skill\`(必填):专职代理名,如 co-fixer、co-explorer、co-oracle
70
+ - \`prompt\`(必填):具体任务描述(自包含,写全目标、文件路径、约束)
71
+
72
+ **特性**:
73
+ - 内部使用 Promise.allSettled() 并行启动所有子代理
74
+ - 每个 task 有独立的 AbortController,超时时主动中止子代理
75
+ - 每个 task 只尝试一次(不重试),通过墙钟预算超时兜底
76
+ - 父级中止时全部子代理取消
77
+ - 返回结构化结果:每个 task 的 status(completed/failed/error)、result、error
78
+
79
+ **示例**:
80
+ delegate_batch({
81
+ tasks: [
82
+ { id: "explore-auth", skill: "co-explorer", prompt: "搜索 src/auth/ 目录下所有认证相关文件" },
83
+ { id: "fix-login", skill: "co-fixer", prompt: "在 src/auth/login.ts 中修复类型错误" },
84
+ { id: "review-api", skill: "co-oracle", prompt: "审查 src/api/routes.ts 的安全问题" },
85
+ ]
86
+ })
87
+
88
+ **对比 delegate**:
89
+ | 维度 | delegate | delegate_batch |
90
+ |------|----------|----------------|
91
+ | 每次调用 | 1 个任务 | N 个任务(N≥1) |
92
+ | 并行方式 | 依赖 agent loop 并发多个 tool_use | 工具内部 Promise.allSettled |
93
+ | 重试 | 支持(P1-2) | 不支持(每个 task 一次) |
94
+ | 停滞检测 | 支持(N2) | 不支持 |
95
+ | 适用场景 | 单任务、需要重试、需要停滞检测 | 批量并行独立任务 |
96
+
50
97
  ### 并行派发方式(原则 + 参数,禁止超预算大并行)
51
98
  - **并行原则(任何环境成立)**:无依赖且不冲突的任务并行;有依赖/同文件写冲突串行;并行度克制,收益递减收敛分批。
52
99
  - **批大小参数**:单批 ≤ \`schedule.maxParallelBatch\`(当前生效值见系统提示词注入的「调度参数」段;部署可调,本环境观测值 2-3)。
@@ -180,6 +227,11 @@ co-fixer 编译测试 →(编译通过后)co-oracle 代码审查 与 co-desi
180
227
 
181
228
  □ **本轮是否提议新增能力?** → 有 → 先过规则 4 三问(低频/重叠/为用而用),任一不过则不新增,先复用现有代理与调度工具
182
229
 
230
+ □ **本轮 delegate 调用参数是否完整?**
231
+ → 每个 delegate 调用是否都包含 skill(必填)和 prompt(必填)?
232
+ → skill 值是否在可用代理列表中?
233
+ → prompt 是否自包含(目标/路径/约束/输出格式)?
234
+
183
235
  </自检清单>
184
236
  `
185
237
  },
@@ -753,6 +805,17 @@ Orchestrator 会在 prompt 中明确指定:
753
805
 
754
806
  // src/council.ts
755
807
  import { defineTool } from "@deepseek-ai/dsh-tools";
808
+
809
+ // src/text-utils.ts
810
+ function contentText(output) {
811
+ if (!Array.isArray(output))
812
+ return "";
813
+ return output.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
814
+
815
+ `);
816
+ }
817
+
818
+ // src/council.ts
756
819
  function shortModelLabel(model) {
757
820
  return model.split("/").pop() ?? model;
758
821
  }
@@ -809,13 +872,6 @@ ${r.result}`).join(`
809
872
  return parts.join(`
810
873
  `);
811
874
  }
812
- function contentText(output) {
813
- if (!Array.isArray(output))
814
- return "";
815
- return output.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
816
-
817
- `);
818
- }
819
875
  function createCouncilTool(config, ctx) {
820
876
  return defineTool({
821
877
  name: "council_session",
@@ -1039,13 +1095,6 @@ function pickEnvContractText(args) {
1039
1095
  }
1040
1096
  return { text: DEFAULT_ENV_CONTRACT, source: "default" };
1041
1097
  }
1042
- function contentText2(output) {
1043
- if (!Array.isArray(output))
1044
- return "";
1045
- return output.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
1046
-
1047
- `);
1048
- }
1049
1098
  function normalizeErrorSignature(data) {
1050
1099
  if (!data || typeof data !== "object")
1051
1100
  return null;
@@ -1058,7 +1107,7 @@ function normalizeErrorSignature(data) {
1058
1107
  if (msg || code)
1059
1108
  return "err:" + (code ? code + " " : "") + msg.slice(0, 120);
1060
1109
  }
1061
- const text = contentText2(data?.message?.content).trim();
1110
+ const text = contentText(data?.message?.content).trim();
1062
1111
  if (text)
1063
1112
  return "err:" + text.slice(0, 120);
1064
1113
  return data?.message?.isError === true ? "err:unknown" : null;
@@ -1175,7 +1224,7 @@ class EnvSignatureLearner {
1175
1224
  this.errorSigs.add(sig);
1176
1225
  }
1177
1226
  } else {
1178
- const text = contentText2(d?.message?.content);
1227
+ const text = contentText(d?.message?.content);
1179
1228
  if (/await\s+import\s*\(/i.test(text))
1180
1229
  this.sawAwaitImportSuccess = true;
1181
1230
  }
@@ -1183,13 +1232,6 @@ class EnvSignatureLearner {
1183
1232
  }
1184
1233
 
1185
1234
  // src/delegate.ts
1186
- function contentText3(output) {
1187
- if (!Array.isArray(output))
1188
- return "";
1189
- return output.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
1190
-
1191
- `);
1192
- }
1193
1235
  function normalizeErrorSignature2(data) {
1194
1236
  if (!data || typeof data !== "object")
1195
1237
  return null;
@@ -1202,7 +1244,7 @@ function normalizeErrorSignature2(data) {
1202
1244
  if (msg || code)
1203
1245
  return "err:" + (code ? code + " " : "") + msg.slice(0, 120);
1204
1246
  }
1205
- const text = contentText3(data?.message?.content).trim();
1247
+ const text = contentText(data?.message?.content).trim();
1206
1248
  if (text)
1207
1249
  return "err:" + text.slice(0, 120);
1208
1250
  return data?.message?.isError === true ? "err:unknown" : null;
@@ -1394,6 +1436,59 @@ class StallWatchdog {
1394
1436
  });
1395
1437
  }
1396
1438
  }
1439
+ function resolveSkillAndRoute(requestedSkill, getRoutes, ctx, logger) {
1440
+ const skillName = requestedSkill || "co-fixer";
1441
+ let skill = COHUB_SKILLS.find((s) => s.name === skillName);
1442
+ if (!skill) {
1443
+ (logger ?? ctx.logger)?.warn?.('delegate: unknown skill "' + skillName + '", falling back to co-fixer');
1444
+ skill = COHUB_SKILLS.find((s) => s.name === "co-fixer");
1445
+ } else if (!requestedSkill) {
1446
+ (logger ?? ctx.logger)?.info?.("delegate: skill not specified, defaulting to co-fixer");
1447
+ }
1448
+ const route = (getRoutes() ?? []).find((s) => s.name === requestedSkill);
1449
+ return { skill, route };
1450
+ }
1451
+ function buildAgentOptions(route, ctx) {
1452
+ if (route?.provider) {
1453
+ const available = new Set(ctx.llm.listProviders().map((p) => p.id));
1454
+ if (available.has(route.provider)) {
1455
+ return {
1456
+ provider: route.provider,
1457
+ ...route.model ? { model: route.model } : {},
1458
+ ...route.maxTokens ? { maxTokens: route.maxTokens } : {}
1459
+ };
1460
+ }
1461
+ }
1462
+ return;
1463
+ }
1464
+ function resolveContractText(envContract, envSig, ctx) {
1465
+ if (!envContract.enabled)
1466
+ return { text: "", source: "disabled" };
1467
+ const fp = envFingerprint();
1468
+ const cached = envSig.use !== "off" ? readEnvSignatures(envSig.cachePath) : null;
1469
+ const picked = pickEnvContractText({
1470
+ manualText: envContract.text,
1471
+ envSig,
1472
+ cached,
1473
+ fingerprint: fp
1474
+ });
1475
+ if (picked.source === "cache") {
1476
+ ctx.logger?.info?.("[cohub envsig] 命中环境契约缓存(指纹一致 + TTL 内)");
1477
+ }
1478
+ const text = `
1479
+
1480
+ --- ` + (picked.source === "cache" || picked.source === "manual-contract" ? "执行器环境契约(本环境已验证)" : "执行器环境契约(通用原则 + 探测式自适应)") + ` ---
1481
+
1482
+ ` + picked.text;
1483
+ return { text, source: picked.source };
1484
+ }
1485
+ function buildDelegatePrompt(params) {
1486
+ return (params.skill.brief || params.skill.content) + params.contractText + `
1487
+
1488
+ --- 你的具体任务 ---
1489
+
1490
+ ` + params.userPrompt + (params.retryNote ?? "");
1491
+ }
1397
1492
  function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1398
1493
  const envContract = {
1399
1494
  enabled: config.delegateEnvContract?.enabled ?? true,
@@ -1434,8 +1529,7 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1434
1529
  parameters: {
1435
1530
  skill: {
1436
1531
  type: "string",
1437
- required: true,
1438
- description: "专职代理技能名,如 co-fixer/co-explorer/co-oracle"
1532
+ description: "专职代理技能名,如 co-fixer/co-explorer/co-oracle。缺省时默认 co-fixer"
1439
1533
  },
1440
1534
  prompt: {
1441
1535
  type: "string",
@@ -1455,41 +1549,15 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1455
1549
  const { retry, stall, schedule } = resolveRuntimeConfig();
1456
1550
  const RETRYABLE = new Set(retry.retryableReasons);
1457
1551
  ctx.logger?.info?.("[cohub schedule] 生效调度参数:batch=" + (schedule?.maxParallelBatch ?? 3) + " wallMs=" + (schedule?.wallClockBudgetMs ?? 600000) + " jobTracking=" + (schedule?.useJobTracking ?? "auto") + " adaptiveBatch=" + (schedule?.adaptiveBatch ?? "auto"));
1458
- const skill = COHUB_SKILLS.find((s) => s.name === args.skill);
1459
- if (!skill)
1460
- throw new Error('delegate: unknown skill "' + args.skill + '"');
1461
- const route = (getRoutes() ?? []).find((s) => s.name === args.skill);
1552
+ const { skill, route } = resolveSkillAndRoute(args.skill, getRoutes, ctx);
1462
1553
  const brief = skill.brief?.trim();
1463
1554
  if (!brief) {
1464
1555
  ctx.logger?.warn?.('delegate: skill "' + args.skill + '" 缺少 brief,回退完整 content');
1465
1556
  }
1466
- let agentOptions;
1467
- if (route?.provider) {
1468
- const available = new Set(ctx.llm.listProviders().map((p) => p.id));
1469
- if (available.has(route.provider)) {
1470
- agentOptions = {
1471
- provider: route.provider,
1472
- ...route.model ? { model: route.model } : {},
1473
- ...route.maxTokens ? { maxTokens: route.maxTokens } : {}
1474
- };
1475
- }
1476
- }
1557
+ const agentOptions = buildAgentOptions(route, ctx);
1558
+ const contractResult = resolveContractText({ enabled: envContract.enabled, text: envContract.text }, envSig, ctx);
1559
+ const contractText = contractResult.text;
1477
1560
  const fp = envFingerprint();
1478
- const cached = envSig.use !== "off" ? readEnvSignatures(envSig.cachePath) : null;
1479
- const picked = pickEnvContractText({
1480
- manualText: envContract.text,
1481
- envSig,
1482
- cached,
1483
- fingerprint: fp
1484
- });
1485
- if (picked.source === "cache") {
1486
- ctx.logger?.info?.("[cohub envsig] 命中环境契约缓存(指纹一致 + TTL 内)");
1487
- }
1488
- const contractText = envContract.enabled ? `
1489
-
1490
- --- ` + (picked.source === "cache" || picked.source === "manual-contract" ? "执行器环境契约(本环境已验证)" : "执行器环境契约(通用原则 + 探测式自适应)") + ` ---
1491
-
1492
- ` + picked.text : "";
1493
1561
  let lastReason = "unknown";
1494
1562
  let lastPartial = "";
1495
1563
  let lastStall = null;
@@ -1499,11 +1567,12 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1499
1567
  const retryNote = attempt > 0 ? `
1500
1568
 
1501
1569
  [重试 #` + attempt + "] 上一次执行中止,原因:" + lastReason + ";已完成部分:" + (lastPartial || "(无)") + stallNote + "。请从中断处继续,不要重复已完成步骤。" : "";
1502
- const promptText = (brief || skill.content) + contractText + `
1503
-
1504
- --- 你的具体任务 ---
1505
-
1506
- ` + args.prompt + retryNote;
1570
+ const promptText = buildDelegatePrompt({
1571
+ skill,
1572
+ contractText,
1573
+ userPrompt: args.prompt,
1574
+ retryNote
1575
+ });
1507
1576
  const ac = stall.enabled ? new AbortController : null;
1508
1577
  let onExecAbort = null;
1509
1578
  const forwardAbort = () => {
@@ -1596,9 +1665,9 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1596
1665
  } catch {}
1597
1666
  }
1598
1667
  if (outcome?.stopReason === "completed")
1599
- return contentText3(outcome.output);
1668
+ return contentText(outcome.output);
1600
1669
  lastReason = String(outcome?.stopReason ?? "unknown");
1601
- lastPartial = contentText3(outcome?.output ?? []).slice(0, 500);
1670
+ lastPartial = contentText(outcome?.output ?? []).slice(0, 500);
1602
1671
  lastStall = stallInfo;
1603
1672
  if (stallInfo && !stall.recoverable)
1604
1673
  break;
@@ -1620,6 +1689,232 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1620
1689
  });
1621
1690
  }
1622
1691
 
1692
+ // src/delegate_batch.ts
1693
+ import { defineTool as defineTool3 } from "@deepseek-ai/dsh-tools";
1694
+ var MAX_BATCH_SIZE = 20;
1695
+ function createDelegateBatchTool(ctx, getRoutes, config = {}, getSettings) {
1696
+ const envContract = {
1697
+ enabled: config.delegateEnvContract?.enabled ?? true,
1698
+ text: config.delegateEnvContract?.text
1699
+ };
1700
+ const envSig = {
1701
+ use: config.envSignatures?.use ?? "auto",
1702
+ ttlMs: Math.max(0, config.envSignatures?.ttlMs ?? 604800000),
1703
+ confirmCount: Math.max(1, Math.trunc(config.envSignatures?.confirmCount ?? 2)),
1704
+ contract: config.envSignatures?.contract,
1705
+ errorCategories: config.envSignatures?.errorCategories,
1706
+ cachePath: config.envSignatures?.cachePath
1707
+ };
1708
+ function resolveRuntimeConfig() {
1709
+ const settingsNow = getSettings ? getSettings() ?? {} : {};
1710
+ const scheduleCfg = settingsNow.schedule ?? config.schedule;
1711
+ return {
1712
+ wallClockBudgetMs: Math.max(0, scheduleCfg?.wallClockBudgetMs ?? 600000)
1713
+ };
1714
+ }
1715
+ return defineTool3({
1716
+ name: "delegate_batch",
1717
+ description: "批量并行委派:一次调用同时启动多个子代理任务,返回每个任务的结构化结果。适合并行派发多个无依赖的独立任务(如同时让多个 co-explorer 探索不同目录,或多个 co-fixer 修改不同文件)。每个 task 只尝试一次,不重试。上限 20 个任务。",
1718
+ parameters: {
1719
+ tasks: {
1720
+ type: "array",
1721
+ required: true,
1722
+ description: "任务列表,每个任务包含 id(可选,用于结果映射)、skill(代理名)、prompt(具体任务描述)。上限 20 个。",
1723
+ items: {
1724
+ type: "object",
1725
+ additionalProperties: false,
1726
+ properties: {
1727
+ id: { type: "string", description: "可选任务标识,用于结果映射" },
1728
+ skill: { type: "string", description: "必填,专职代理技能名,如 co-fixer / co-explorer / co-oracle" },
1729
+ prompt: { type: "string", description: "必填,给该代理的具体任务描述" }
1730
+ }
1731
+ }
1732
+ }
1733
+ },
1734
+ output: {
1735
+ schema: { type: "string" },
1736
+ render: (_args, value) => [{ type: "text", text: String(value) }]
1737
+ },
1738
+ isConcurrencySafe: () => true,
1739
+ async execute(args, exec) {
1740
+ const parent = exec?.agent;
1741
+ if (!parent)
1742
+ throw new Error("delegate_batch requires a calling agent (exec.agent was undefined)");
1743
+ const { wallClockBudgetMs } = resolveRuntimeConfig();
1744
+ const tasks = args.tasks ?? [];
1745
+ if (tasks.length === 0) {
1746
+ return "delegate_batch: 无任务";
1747
+ }
1748
+ if (tasks.length > MAX_BATCH_SIZE) {
1749
+ return `delegate_batch: 任务数 ${tasks.length} 超过上限 ${MAX_BATCH_SIZE},请分批执行。`;
1750
+ }
1751
+ const contractText = envContract.enabled ? resolveContractText(envContract, envSig, ctx).text : "";
1752
+ const starts = await Promise.allSettled(tasks.map(async (task, index) => {
1753
+ const taskId = task.id ?? `task-${index}`;
1754
+ const { skill, route } = resolveSkillAndRoute(task.skill, getRoutes, ctx, ctx.logger);
1755
+ const promptText = buildDelegatePrompt({
1756
+ skill,
1757
+ contractText,
1758
+ userPrompt: task.prompt
1759
+ });
1760
+ const agentOptions = buildAgentOptions(route, ctx);
1761
+ const ac = new AbortController;
1762
+ let onExecAbort = null;
1763
+ if (exec.signal) {
1764
+ if (exec.signal.aborted) {
1765
+ ac.abort();
1766
+ } else {
1767
+ onExecAbort = () => {
1768
+ if (!ac.signal.aborted)
1769
+ ac.abort();
1770
+ };
1771
+ exec.signal.addEventListener("abort", onExecAbort, { once: true });
1772
+ }
1773
+ }
1774
+ try {
1775
+ const run = await ctx.subagents.start("spawn", {
1776
+ label: `delegate_batch:${taskId}:${task.skill}`,
1777
+ prompt: [{ type: "text", text: promptText }],
1778
+ parent,
1779
+ persona: "你是被委派的专职代理:严格遵循任务消息中的角色定义、关键约束、输出格式与工具指令,并完成末尾的具体任务。",
1780
+ ...agentOptions ? { agentOptions } : {},
1781
+ signal: ac.signal
1782
+ });
1783
+ return { taskId, task, run, ac, onExecAbort, startError: undefined };
1784
+ } catch (startError) {
1785
+ if (onExecAbort && exec.signal) {
1786
+ try {
1787
+ exec.signal.removeEventListener("abort", onExecAbort);
1788
+ } catch {}
1789
+ }
1790
+ return { taskId, task, run: undefined, ac, onExecAbort: null, startError };
1791
+ }
1792
+ }));
1793
+ const results = [];
1794
+ const runs = [];
1795
+ try {
1796
+ for (const s of starts) {
1797
+ if (s.status === "rejected") {
1798
+ continue;
1799
+ }
1800
+ const val = s.value;
1801
+ if (val.startError) {
1802
+ results.push({
1803
+ id: val.taskId,
1804
+ skill: val.task.skill,
1805
+ status: "error",
1806
+ error: val.startError instanceof Error ? val.startError.message : String(val.startError)
1807
+ });
1808
+ continue;
1809
+ }
1810
+ const { run, taskId, task, ac, onExecAbort } = val;
1811
+ runs.push({ run, taskId, task, ac, onExecAbort });
1812
+ run.result.catch(() => {});
1813
+ }
1814
+ for (const { run, taskId, task, ac, onExecAbort } of runs) {
1815
+ let outcome;
1816
+ let timer = null;
1817
+ try {
1818
+ outcome = await Promise.race([
1819
+ run.result,
1820
+ new Promise((_, reject) => {
1821
+ timer = setTimeout(() => {
1822
+ if (!ac.signal.aborted)
1823
+ ac.abort();
1824
+ reject(new Error(`超时:${wallClockBudgetMs}ms`));
1825
+ }, wallClockBudgetMs);
1826
+ if (exec.signal && !exec.signal.aborted) {
1827
+ exec.signal.addEventListener("abort", () => {
1828
+ if (timer)
1829
+ clearTimeout(timer);
1830
+ }, { once: true });
1831
+ }
1832
+ })
1833
+ ]);
1834
+ if (outcome?.stopReason === "completed") {
1835
+ results.push({
1836
+ id: taskId,
1837
+ skill: task.skill,
1838
+ status: "completed",
1839
+ result: contentText(outcome.output)
1840
+ });
1841
+ } else {
1842
+ results.push({
1843
+ id: taskId,
1844
+ skill: task.skill,
1845
+ status: "failed",
1846
+ error: `stopReason: ${outcome?.stopReason ?? "unknown"}`,
1847
+ stopReason: outcome?.stopReason
1848
+ });
1849
+ }
1850
+ } catch (e) {
1851
+ results.push({
1852
+ id: taskId,
1853
+ skill: task.skill,
1854
+ status: "error",
1855
+ error: e instanceof Error ? e.message : String(e)
1856
+ });
1857
+ } finally {
1858
+ if (timer)
1859
+ clearTimeout(timer);
1860
+ }
1861
+ }
1862
+ } finally {
1863
+ for (const { run, ac, onExecAbort } of runs) {
1864
+ if (onExecAbort && exec.signal) {
1865
+ try {
1866
+ exec.signal.removeEventListener("abort", onExecAbort);
1867
+ } catch {}
1868
+ }
1869
+ if (!ac.signal.aborted)
1870
+ ac.abort();
1871
+ try {
1872
+ run.dispose();
1873
+ } catch {}
1874
+ }
1875
+ }
1876
+ const completed = results.filter((r) => r.status === "completed");
1877
+ const failed = results.filter((r) => r.status !== "completed");
1878
+ let output = `--- delegate_batch 结果 ---
1879
+ `;
1880
+ output += `总计: ${results.length} 个任务 | 完成: ${completed.length} | 失败: ${failed.length}
1881
+
1882
+ `;
1883
+ if (completed.length > 0) {
1884
+ output += `## 已完成任务
1885
+
1886
+ `;
1887
+ for (const r of completed) {
1888
+ output += `### ${r.id} (${r.skill})
1889
+ ${r.result}
1890
+
1891
+ `;
1892
+ }
1893
+ }
1894
+ if (failed.length > 0) {
1895
+ output += `## 失败/错误任务
1896
+
1897
+ `;
1898
+ for (const r of failed) {
1899
+ output += `### ${r.id} (${r.skill})
1900
+ `;
1901
+ output += `状态: ${r.status}
1902
+ `;
1903
+ if (r.error)
1904
+ output += `错误: ${r.error}
1905
+ `;
1906
+ if (r.stopReason)
1907
+ output += `stopReason: ${r.stopReason}
1908
+ `;
1909
+ output += `
1910
+ `;
1911
+ }
1912
+ }
1913
+ return output;
1914
+ }
1915
+ });
1916
+ }
1917
+
1623
1918
  // src/index.ts
1624
1919
  import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
1625
1920
  var Councillor = z.object({
@@ -1690,10 +1985,6 @@ var CohubSettingsSchema = z.object({
1690
1985
  schedule: ScheduleConfig,
1691
1986
  delegateRetry: DelegateRetry
1692
1987
  });
1693
- function renderScheduleParams(schedule) {
1694
- const s = schedule ?? {};
1695
- return "调度参数(部署配置,非指令):单批 ≤ " + (s.maxParallelBatch ?? 3) + ";墙钟预算 " + (s.wallClockBudgetMs ?? 600000) + " ms" + ";job 跟踪 " + (s.useJobTracking ?? "auto") + ";批间自适应 " + (s.adaptiveBatch ?? "auto");
1696
- }
1697
1988
  var SHIPPED_PRESETS_DIR = fileURLToPath(new URL("../presets/", import.meta.url));
1698
1989
  function installAgentPresets(logger) {
1699
1990
  let entries;
@@ -1754,11 +2045,11 @@ function apply(ctx, config) {
1754
2045
  delegateRetry: config.delegateRetry,
1755
2046
  envSignatures: config.envSignatures
1756
2047
  }, () => currentSettings()));
1757
- ctx.effect(() => ctx.systemPrompt.section({
1758
- name: "cohub:schedule",
1759
- order: 96,
1760
- text: () => renderScheduleParams(currentSettings().schedule)
1761
- }), "cohub.scheduleSection()");
2048
+ ctx.tools.register(createDelegateBatchTool(ctx, () => currentSkills(), {
2049
+ delegateEnvContract: config.delegateEnvContract,
2050
+ delegateRetry: config.delegateRetry,
2051
+ envSignatures: config.envSignatures
2052
+ }, () => currentSettings()));
1762
2053
  if ((config.councillors ?? []).length > 0) {
1763
2054
  if (!ctx.reflect?.get?.("subagents", false)) {
1764
2055
  throw new Error("cohub: council tool requires the subagents service (@deepseek-ai/dsh-subagent)");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-cohub",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "DeepSeek Harness 版 CoHub:中文智能体编排——双 preset 模式(co-orchestrator 纯调度 + cohub-standard 标准)/ 12 技能(11 专职代理 + orchestrator)/ 多模型共识",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -36,7 +36,8 @@
36
36
  }
37
37
  },
38
38
  "scripts": {
39
- "build": "bun run scripts/generate-skills.ts && bun build src/index.ts --outdir lib --target node --format esm --external @deepseek-ai/cordis --external @deepseek-ai/schemastery --external @deepseek-ai/dsh-tools --external @deepseek-ai/dsh-system-prompt --external @deepseek-ai/dsh-skill --external @deepseek-ai/dsh-home-paths --external @deepseek-ai/dsh-settings && bun run scripts/build-client.js",
39
+ "build": "bun run scripts/generate-skills.ts && bun run scripts/guard-schema.ts && bun build src/index.ts --outdir lib --target node --format esm --external @deepseek-ai/cordis --external @deepseek-ai/schemastery --external @deepseek-ai/dsh-tools --external @deepseek-ai/dsh-system-prompt --external @deepseek-ai/dsh-skill --external @deepseek-ai/dsh-home-paths --external @deepseek-ai/dsh-settings && bun run scripts/build-client.js",
40
+ "lint:schema": "bun run scripts/guard-schema.ts",
40
41
  "prepublishOnly": "npm run build",
41
42
  "build:client": "bun run scripts/build-client.js",
42
43
  "audit": "node scripts/audit-session.mjs"
@@ -46,8 +46,13 @@
46
46
  name: '@deepseek-ai/dsh-tool-workflow'
47
47
 
48
48
  # ── 技能(co-* 技能目录由 dsh-cohub 全局注册,本 preset 只需加载工具) ──
49
+ # 注意:catalogEnabled: false 关闭目录注入——Orchestrator 使用 delegate() 委派,
50
+ # 从不通过 skill() 加载 co-* 技能,无需每步注入 14 个技能目录浪费 token。
51
+ # skill() 工具本身仍然注册,子代理(通过 delegate spawn)可正常使用。
49
52
  - id: tool-skill
50
53
  name: '@deepseek-ai/dsh-tool-skill'
54
+ config:
55
+ catalogEnabled: false
51
56
 
52
57
  # ── 任务与后台任务面板 ──────────────────────────────────────────────────
53
58
  - id: tool-todo