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 +350 -76
- package/package.json +3 -2
- package/presets/co-orchestrator/agent.cordis.yml +5 -0
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
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 = (
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
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
|
|
1668
|
+
return contentText(outcome.output);
|
|
1617
1669
|
lastReason = String(outcome?.stopReason ?? "unknown");
|
|
1618
|
-
lastPartial =
|
|
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.
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
}
|
|
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.
|
|
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
|