dsh-cohub 0.4.4 → 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
@@ -54,6 +54,46 @@ co-planner - 只读。综合需求+信息+规范输出结构化任务分解方
54
54
  - 并行派发按下方「并行派发方式(原则 + 参数,禁止超预算大并行)」执行:优先并发:单条 assistant 消息里同时发出 N 个 delegate tool_use 块(N ≤ schedule.maxParallelBatch),由 agent loop 并发执行;单批规模受 \`schedule.maxParallelBatch\` 约束,禁止把可能超出墙钟预算的整批压进一次执行单元
55
55
  - delegate 按 cordis.patch.yml 的 skills 配置路由 provider/model 并 spawn 子代理;子代理不共享本会话,prompt 必须自包含(写全任务目标、相关文件路径、约束、期望输出格式;角色身份由 delegate 自动注入)
56
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
+
57
97
  ### 并行派发方式(原则 + 参数,禁止超预算大并行)
58
98
  - **并行原则(任何环境成立)**:无依赖且不冲突的任务并行;有依赖/同文件写冲突串行;并行度克制,收益递减收敛分批。
59
99
  - **批大小参数**:单批 ≤ \`schedule.maxParallelBatch\`(当前生效值见系统提示词注入的「调度参数」段;部署可调,本环境观测值 2-3)。
@@ -765,6 +805,17 @@ Orchestrator 会在 prompt 中明确指定:
765
805
 
766
806
  // src/council.ts
767
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
768
819
  function shortModelLabel(model) {
769
820
  return model.split("/").pop() ?? model;
770
821
  }
@@ -821,13 +872,6 @@ ${r.result}`).join(`
821
872
  return parts.join(`
822
873
  `);
823
874
  }
824
- function contentText(output) {
825
- if (!Array.isArray(output))
826
- return "";
827
- return output.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
828
-
829
- `);
830
- }
831
875
  function createCouncilTool(config, ctx) {
832
876
  return defineTool({
833
877
  name: "council_session",
@@ -1051,13 +1095,6 @@ function pickEnvContractText(args) {
1051
1095
  }
1052
1096
  return { text: DEFAULT_ENV_CONTRACT, source: "default" };
1053
1097
  }
1054
- function contentText2(output) {
1055
- if (!Array.isArray(output))
1056
- return "";
1057
- return output.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
1058
-
1059
- `);
1060
- }
1061
1098
  function normalizeErrorSignature(data) {
1062
1099
  if (!data || typeof data !== "object")
1063
1100
  return null;
@@ -1070,7 +1107,7 @@ function normalizeErrorSignature(data) {
1070
1107
  if (msg || code)
1071
1108
  return "err:" + (code ? code + " " : "") + msg.slice(0, 120);
1072
1109
  }
1073
- const text = contentText2(data?.message?.content).trim();
1110
+ const text = contentText(data?.message?.content).trim();
1074
1111
  if (text)
1075
1112
  return "err:" + text.slice(0, 120);
1076
1113
  return data?.message?.isError === true ? "err:unknown" : null;
@@ -1187,7 +1224,7 @@ class EnvSignatureLearner {
1187
1224
  this.errorSigs.add(sig);
1188
1225
  }
1189
1226
  } else {
1190
- const text = contentText2(d?.message?.content);
1227
+ const text = contentText(d?.message?.content);
1191
1228
  if (/await\s+import\s*\(/i.test(text))
1192
1229
  this.sawAwaitImportSuccess = true;
1193
1230
  }
@@ -1195,13 +1232,6 @@ class EnvSignatureLearner {
1195
1232
  }
1196
1233
 
1197
1234
  // src/delegate.ts
1198
- function contentText3(output) {
1199
- if (!Array.isArray(output))
1200
- return "";
1201
- return output.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
1202
-
1203
- `);
1204
- }
1205
1235
  function normalizeErrorSignature2(data) {
1206
1236
  if (!data || typeof data !== "object")
1207
1237
  return null;
@@ -1214,7 +1244,7 @@ function normalizeErrorSignature2(data) {
1214
1244
  if (msg || code)
1215
1245
  return "err:" + (code ? code + " " : "") + msg.slice(0, 120);
1216
1246
  }
1217
- const text = contentText3(data?.message?.content).trim();
1247
+ const text = contentText(data?.message?.content).trim();
1218
1248
  if (text)
1219
1249
  return "err:" + text.slice(0, 120);
1220
1250
  return data?.message?.isError === true ? "err:unknown" : null;
@@ -1406,6 +1436,59 @@ class StallWatchdog {
1406
1436
  });
1407
1437
  }
1408
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
+ }
1409
1492
  function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1410
1493
  const envContract = {
1411
1494
  enabled: config.delegateEnvContract?.enabled ?? true,
@@ -1446,7 +1529,6 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1446
1529
  parameters: {
1447
1530
  skill: {
1448
1531
  type: "string",
1449
- required: false,
1450
1532
  description: "专职代理技能名,如 co-fixer/co-explorer/co-oracle。缺省时默认 co-fixer"
1451
1533
  },
1452
1534
  prompt: {
@@ -1467,46 +1549,15 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1467
1549
  const { retry, stall, schedule } = resolveRuntimeConfig();
1468
1550
  const RETRYABLE = new Set(retry.retryableReasons);
1469
1551
  ctx.logger?.info?.("[cohub schedule] 生效调度参数:batch=" + (schedule?.maxParallelBatch ?? 3) + " wallMs=" + (schedule?.wallClockBudgetMs ?? 600000) + " jobTracking=" + (schedule?.useJobTracking ?? "auto") + " adaptiveBatch=" + (schedule?.adaptiveBatch ?? "auto"));
1470
- const requestedSkill = args.skill || "co-fixer";
1471
- let skill = COHUB_SKILLS.find((s) => s.name === requestedSkill);
1472
- if (!skill) {
1473
- ctx.logger?.warn?.('delegate: unknown skill "' + requestedSkill + '", falling back to co-fixer');
1474
- skill = COHUB_SKILLS.find((s) => s.name === "co-fixer");
1475
- } else if (!args.skill) {
1476
- ctx.logger?.info?.("delegate: skill not specified, defaulting to co-fixer");
1477
- }
1478
- const route = (getRoutes() ?? []).find((s) => s.name === args.skill);
1552
+ const { skill, route } = resolveSkillAndRoute(args.skill, getRoutes, ctx);
1479
1553
  const brief = skill.brief?.trim();
1480
1554
  if (!brief) {
1481
1555
  ctx.logger?.warn?.('delegate: skill "' + args.skill + '" 缺少 brief,回退完整 content');
1482
1556
  }
1483
- let agentOptions;
1484
- if (route?.provider) {
1485
- const available = new Set(ctx.llm.listProviders().map((p) => p.id));
1486
- if (available.has(route.provider)) {
1487
- agentOptions = {
1488
- provider: route.provider,
1489
- ...route.model ? { model: route.model } : {},
1490
- ...route.maxTokens ? { maxTokens: route.maxTokens } : {}
1491
- };
1492
- }
1493
- }
1557
+ const agentOptions = buildAgentOptions(route, ctx);
1558
+ const contractResult = resolveContractText({ enabled: envContract.enabled, text: envContract.text }, envSig, ctx);
1559
+ const contractText = contractResult.text;
1494
1560
  const fp = envFingerprint();
1495
- const cached = envSig.use !== "off" ? readEnvSignatures(envSig.cachePath) : null;
1496
- const picked = pickEnvContractText({
1497
- manualText: envContract.text,
1498
- envSig,
1499
- cached,
1500
- fingerprint: fp
1501
- });
1502
- if (picked.source === "cache") {
1503
- ctx.logger?.info?.("[cohub envsig] 命中环境契约缓存(指纹一致 + TTL 内)");
1504
- }
1505
- const contractText = envContract.enabled ? `
1506
-
1507
- --- ` + (picked.source === "cache" || picked.source === "manual-contract" ? "执行器环境契约(本环境已验证)" : "执行器环境契约(通用原则 + 探测式自适应)") + ` ---
1508
-
1509
- ` + picked.text : "";
1510
1561
  let lastReason = "unknown";
1511
1562
  let lastPartial = "";
1512
1563
  let lastStall = null;
@@ -1516,11 +1567,12 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1516
1567
  const retryNote = attempt > 0 ? `
1517
1568
 
1518
1569
  [重试 #` + attempt + "] 上一次执行中止,原因:" + lastReason + ";已完成部分:" + (lastPartial || "(无)") + stallNote + "。请从中断处继续,不要重复已完成步骤。" : "";
1519
- const promptText = (brief || skill.content) + contractText + `
1520
-
1521
- --- 你的具体任务 ---
1522
-
1523
- ` + args.prompt + retryNote;
1570
+ const promptText = buildDelegatePrompt({
1571
+ skill,
1572
+ contractText,
1573
+ userPrompt: args.prompt,
1574
+ retryNote
1575
+ });
1524
1576
  const ac = stall.enabled ? new AbortController : null;
1525
1577
  let onExecAbort = null;
1526
1578
  const forwardAbort = () => {
@@ -1613,9 +1665,9 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1613
1665
  } catch {}
1614
1666
  }
1615
1667
  if (outcome?.stopReason === "completed")
1616
- return contentText3(outcome.output);
1668
+ return contentText(outcome.output);
1617
1669
  lastReason = String(outcome?.stopReason ?? "unknown");
1618
- lastPartial = contentText3(outcome?.output ?? []).slice(0, 500);
1670
+ lastPartial = contentText(outcome?.output ?? []).slice(0, 500);
1619
1671
  lastStall = stallInfo;
1620
1672
  if (stallInfo && !stall.recoverable)
1621
1673
  break;
@@ -1637,6 +1689,232 @@ function createDelegateTool(ctx, getRoutes, config = {}, getSettings) {
1637
1689
  });
1638
1690
  }
1639
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
+
1640
1918
  // src/index.ts
1641
1919
  import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
1642
1920
  var Councillor = z.object({
@@ -1707,10 +1985,6 @@ var CohubSettingsSchema = z.object({
1707
1985
  schedule: ScheduleConfig,
1708
1986
  delegateRetry: DelegateRetry
1709
1987
  });
1710
- function renderScheduleParams(schedule) {
1711
- const s = schedule ?? {};
1712
- return "调度参数(部署配置,非指令):单批 ≤ " + (s.maxParallelBatch ?? 3) + ";墙钟预算 " + (s.wallClockBudgetMs ?? 600000) + " ms" + ";job 跟踪 " + (s.useJobTracking ?? "auto") + ";批间自适应 " + (s.adaptiveBatch ?? "auto");
1713
- }
1714
1988
  var SHIPPED_PRESETS_DIR = fileURLToPath(new URL("../presets/", import.meta.url));
1715
1989
  function installAgentPresets(logger) {
1716
1990
  let entries;
@@ -1771,11 +2045,11 @@ function apply(ctx, config) {
1771
2045
  delegateRetry: config.delegateRetry,
1772
2046
  envSignatures: config.envSignatures
1773
2047
  }, () => currentSettings()));
1774
- ctx.effect(() => ctx.systemPrompt.section({
1775
- name: "cohub:schedule",
1776
- order: 96,
1777
- text: () => renderScheduleParams(currentSettings().schedule)
1778
- }), "cohub.scheduleSection()");
2048
+ ctx.tools.register(createDelegateBatchTool(ctx, () => currentSkills(), {
2049
+ delegateEnvContract: config.delegateEnvContract,
2050
+ delegateRetry: config.delegateRetry,
2051
+ envSignatures: config.envSignatures
2052
+ }, () => currentSettings()));
1779
2053
  if ((config.councillors ?? []).length > 0) {
1780
2054
  if (!ctx.reflect?.get?.("subagents", false)) {
1781
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.4",
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