@zhushanwen/pi-subagent-workflow 5.0.2 → 7.0.0
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/agents/{reviewer.md → code-reviewer.md} +20 -3
- package/agents/context-builder.md +5 -0
- package/agents/doc-reviewer.md +9 -2
- package/agents/explorer.md +5 -0
- package/agents/general-purpose.md +5 -0
- package/agents/oracle.md +20 -4
- package/agents/orchestrator.md +6 -1
- package/agents/planner.md +5 -0
- package/agents/researcher.md +5 -0
- package/agents/worker.md +5 -0
- package/package.json +6 -4
- package/src/execution/__tests__/agent-registry.test.ts +189 -119
- package/src/execution/__tests__/crash-recovery.test.ts +0 -1
- package/src/execution/__tests__/execute-options-mapper.test.ts +4 -4
- package/src/execution/__tests__/index-session-start.test.ts +0 -1
- package/src/execution/__tests__/model-resolver.test.ts +20 -0
- package/src/execution/__tests__/session-start-reaper.test.ts +0 -2
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +1 -1
- package/src/execution/agent-registry.ts +92 -169
- package/src/execution/execute-options-mapper.ts +2 -2
- package/src/execution/model-config-service.ts +13 -34
- package/src/execution/model-resolver.ts +5 -3
- package/src/execution/subagent-service.ts +9 -6
- package/src/execution/subprocess-agent-runner.ts +3 -2
- package/src/index.ts +4 -25
- package/src/injectors/__tests__/subagent-list-injector.test.ts +266 -14
- package/src/injectors/__tests__/workflow-list-injector.test.ts +236 -32
- package/src/injectors/subagent-list-injector.ts +99 -48
- package/src/injectors/workflow-list-injector.ts +65 -50
- package/src/interface/__tests__/detectors.test.ts +100 -43
- package/src/interface/__tests__/subagent-tool-prompt.test.ts +8 -12
- package/src/interface/__tests__/tool-workflow-script-generate.test.ts +163 -0
- package/src/interface/__tests__/workflow-tool-prompt.test.ts +55 -9
- package/src/interface/subagent-tool.ts +7 -4
- package/src/interface/tool-workflow-script.ts +27 -10
- package/src/interface/tool-workflow.ts +174 -81
- package/src/orchestration/__tests__/args-validator.test.ts +143 -0
- package/src/orchestration/__tests__/config-loader.test.ts +124 -40
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +33 -2
- package/src/orchestration/__tests__/lifecycle.test.ts +59 -2
- package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +116 -51
- package/src/orchestration/__tests__/script-lint.test.ts +167 -1
- package/src/orchestration/__tests__/worker-host.test.ts +120 -0
- package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +69 -0
- package/src/orchestration/__tests__/worker-script-builder.test.ts +51 -1
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +2 -2
- package/src/orchestration/__tests__/workflows-e2e.test.ts +177 -24
- package/src/orchestration/agent-opts-resolver.ts +51 -101
- package/src/orchestration/args-validator.ts +127 -0
- package/src/orchestration/config-loader.ts +63 -94
- package/src/orchestration/error-recovery.ts +6 -9
- package/src/orchestration/launcher.ts +77 -41
- package/src/orchestration/lifecycle.ts +7 -0
- package/src/orchestration/models/ports.ts +0 -14
- package/src/orchestration/models/run-spec.ts +24 -1
- package/src/orchestration/models/types.ts +15 -8
- package/src/orchestration/models/workflow-script-registry.ts +3 -0
- package/src/orchestration/models/workflow-script.ts +10 -14
- package/src/orchestration/script-lint.ts +159 -0
- package/src/orchestration/worker-host.ts +5 -0
- package/src/orchestration/worker-script-builder.ts +15 -4
- package/src/orchestration/workflow-script-registry-impl.ts +34 -29
- package/src/shared/__tests__/meta-parser.test.ts +304 -0
- package/src/shared/__tests__/resource-discovery.test.ts +167 -7
- package/src/shared/__tests__/resource-meta.test.ts +51 -0
- package/src/shared/agent-ref.ts +36 -0
- package/src/shared/meta-parser.ts +257 -0
- package/src/shared/resource-discovery.ts +88 -2
- package/src/shared/resource-meta.ts +60 -0
- package/workflows/README.md +2 -2
- package/workflows/_shared/agent-refs.cjs +40 -0
- package/workflows/chain.js +30 -6
- package/workflows/map-reduce.js +33 -5
- package/workflows/parallel.js +34 -7
- package/workflows/review-fix-loop-utils.cjs +23 -103
- package/workflows/review-fix-loop.js +121 -58
- package/workflows/scatter-gather.js +30 -6
package/workflows/parallel.js
CHANGED
|
@@ -14,11 +14,23 @@
|
|
|
14
14
|
//
|
|
15
15
|
// ⚠️ lintScript 约束(本脚本已遵守):含 parallel() 入口,禁止 bare IIFE
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
/* @pi-meta
|
|
18
|
+
name: parallel
|
|
19
|
+
description: 通用编排:多视角并行分析同一目标,再聚合汇总
|
|
20
|
+
phases: [parallel-analyze, aggregate]
|
|
21
|
+
parameters:
|
|
22
|
+
type: object
|
|
23
|
+
properties:
|
|
24
|
+
target: { type: string }
|
|
25
|
+
perspectives: { type: array, items: { type: string } }
|
|
26
|
+
agents: { type: string }
|
|
27
|
+
required: [target]
|
|
28
|
+
usage: |
|
|
29
|
+
## 使用说明
|
|
30
|
+
- 多视角并行分析后聚合;perspectives 缺省用 3 个默认视角
|
|
31
|
+
- agents:逗号分隔的 agent .md 绝对路径;1 个应用于所有视角,N 个一一对应视角数
|
|
32
|
+
- 示例:workflow run parallel --args target="<分析目标>" agents="/path/analyst.md"
|
|
33
|
+
*/
|
|
22
34
|
|
|
23
35
|
// ── 入参($ARGS)──────────────────────────────────────────────────
|
|
24
36
|
const target = $ARGS.target;
|
|
@@ -32,7 +44,21 @@ if (perspectives.some((p) => typeof p !== "string")) {
|
|
|
32
44
|
throw new Error("parallel 参数 perspectives 必须是字符串数组,实际含非字符串元素");
|
|
33
45
|
}
|
|
34
46
|
|
|
35
|
-
|
|
47
|
+
// S4:agents 参数 = 逗号分隔的 agentRef 路径数组;1 个 = 所有视角,N 个 = 一一对应
|
|
48
|
+
// worker 沙箱为 eval 模式:require 相对路径以 cwd 为基准(非脚本目录),
|
|
49
|
+
// 必须用 workerData.scriptPath 锚定脚本目录(review-fix-loop 同模式)。
|
|
50
|
+
const SCRIPT_DIR = workerData && workerData.scriptPath
|
|
51
|
+
? require("path").dirname(workerData.scriptPath)
|
|
52
|
+
: process.cwd();
|
|
53
|
+
const { parseAgentRefs, agentRefAt } = require(SCRIPT_DIR + "/_shared/agent-refs.cjs");
|
|
54
|
+
const agentRefs = parseAgentRefs($ARGS.agents);
|
|
55
|
+
const agentFor = (i) => {
|
|
56
|
+
if (agentRefs.length === 1) return { agent: agentRefs[0] };
|
|
57
|
+
const ref = agentRefAt(agentRefs, i);
|
|
58
|
+
return ref ? { agent: ref } : {};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
log("parallel 开始,target=" + target + " perspectives=" + JSON.stringify(perspectives) + (agentRefs.length ? " agents=" + agentRefs.join(",") : ""));
|
|
36
62
|
|
|
37
63
|
let currentPhase = "init";
|
|
38
64
|
let outcome;
|
|
@@ -44,7 +70,7 @@ try {
|
|
|
44
70
|
|
|
45
71
|
// parallel() 接受 Promise 数组;agent() 返回 Promise。allSettled 语义。
|
|
46
72
|
const perPerspectiveRaw = await parallel(
|
|
47
|
-
perspectives.map((p) =>
|
|
73
|
+
perspectives.map((p, i) =>
|
|
48
74
|
agent({
|
|
49
75
|
prompt:
|
|
50
76
|
"从「" + p + "」角度分析以下目标,给出评分和发现的问题:\n\n" + target,
|
|
@@ -62,6 +88,7 @@ try {
|
|
|
62
88
|
required: ["perspective", "score", "findings"],
|
|
63
89
|
},
|
|
64
90
|
description: "parallel-" + p,
|
|
91
|
+
...agentFor(i),
|
|
65
92
|
})
|
|
66
93
|
),
|
|
67
94
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// review-fix-loop-utils.cjs — review-fix-loop.js 的可测纯函数模块
|
|
2
2
|
//
|
|
3
|
-
//
|
|
3
|
+
// workflow 编排逻辑的纯函数抽到独立 .cjs,
|
|
4
4
|
// 供 vitest 单测直接 require(extensions/subagent-workflow/src/__tests__/review-fix-loop-utils.test.ts)
|
|
5
5
|
// 与 worker 运行时共用(review-fix-loop.js 经 workerData.scriptPath 定位本文件)。
|
|
6
6
|
//
|
|
@@ -8,35 +8,21 @@
|
|
|
8
8
|
// 通过 fail(msg) 回调注入(调用方抛 "review-fix-loop: <msg>",与 workflow 内 fail() 一致)。
|
|
9
9
|
"use strict";
|
|
10
10
|
|
|
11
|
-
const fs = require("fs");
|
|
12
11
|
const path = require("path");
|
|
13
12
|
|
|
14
13
|
const TARGET_TYPES = ["git-diff", "file", "dir", "text"];
|
|
15
14
|
const VALID_ARG_KEYS = new Set([
|
|
16
15
|
"targetType", "target", "agents", "batchNames", "reviewPrompt", "fixPrompt",
|
|
17
|
-
"autoCommit", "maxRounds", "stuckThreshold", "
|
|
18
|
-
"recheckAfterFix", "fixAgent", "maxFixAttempts", "convergeNewIssues", "convergeRounds",
|
|
16
|
+
"autoCommit", "maxRounds", "stuckThreshold", "skipCleanAgents",
|
|
17
|
+
"recheckAfterFix", "fixAgent", "maxFixAttempts", "convergeNewIssues", "convergeRounds",
|
|
18
|
+
"fallowScan", "_runId",
|
|
19
19
|
]);
|
|
20
20
|
|
|
21
|
-
function normalizeBool(v, name, def, fail) {
|
|
22
|
-
if (v === undefined || v === null || v === "") return def;
|
|
23
|
-
if (v === true || v === "true") return true;
|
|
24
|
-
if (v === false || v === "false") return false;
|
|
25
|
-
fail("参数 " + name + " 必须是布尔值(true/false),实际: " + JSON.stringify(v));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function normalizeInt(v, name, def, fail) {
|
|
29
|
-
if (v === undefined || v === null || v === "") return def;
|
|
30
|
-
const n = typeof v === "number" ? v : Number(String(v).trim());
|
|
31
|
-
if (!Number.isInteger(n) || n <= 0) fail("参数 " + name + " 必须是正整数,实际: " + JSON.stringify(v));
|
|
32
|
-
return n;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
21
|
/**
|
|
36
22
|
* 批次解析:batch1..batchN(缺号报错)/ agents 简写。两者必传其一,缺省直接报错(无默认 agent)。
|
|
37
23
|
* @param args $ARGS 形状的对象(batchN 键、agents 键)
|
|
38
24
|
* @param fail 报错回调(抛错终止)
|
|
39
|
-
* @returns string[][] 每批的
|
|
25
|
+
* @returns string[][] 每批的 agentRef 路径数组
|
|
40
26
|
*/
|
|
41
27
|
function parseBatches(args, fail) {
|
|
42
28
|
const batchKeys = Object.keys(args)
|
|
@@ -62,7 +48,7 @@ function parseBatches(args, fail) {
|
|
|
62
48
|
return rawBatches.map((raw, idx) => {
|
|
63
49
|
if (typeof raw !== "string" || !raw.trim()) fail("batch" + (idx + 1) + " 不能为空");
|
|
64
50
|
const names = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
65
|
-
if (names.length === 0) fail("batch" + (idx + 1) + " 为空(逗号分隔 agent
|
|
51
|
+
if (names.length === 0) fail("batch" + (idx + 1) + " 为空(逗号分隔 agent .md 绝对路径)");
|
|
66
52
|
if (new Set(names).size !== names.length) fail("batch" + (idx + 1) + " 内存在重复 agent: " + names);
|
|
67
53
|
return names;
|
|
68
54
|
});
|
|
@@ -76,15 +62,6 @@ function resolveBatchNames(rawBatchNames, batches, fail) {
|
|
|
76
62
|
return rawBatchNames.length ? rawBatchNames : batches.map((_, i) => "batch-" + (i + 1));
|
|
77
63
|
}
|
|
78
64
|
|
|
79
|
-
/** fallow-scan 只在 git-diff 类型下有意义。 */
|
|
80
|
-
function validateFallowScan(batches, targetType, fail) {
|
|
81
|
-
for (let i = 0; i < batches.length; i++) {
|
|
82
|
-
if (batches[i].includes("fallow-scan") && targetType !== "git-diff") {
|
|
83
|
-
fail("fallow-scan 只支持 targetType=git-diff(它审查 git 变更的静态分析),实际 targetType=" + targetType);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
65
|
/** 审查指令模板(按 targetType 生成,注入每个 review agent 的 prompt)。 */
|
|
89
66
|
function buildReviewInstruction(targetType, target) {
|
|
90
67
|
switch (targetType) {
|
|
@@ -694,17 +671,6 @@ function normalizeAggregatorResult(raw) {
|
|
|
694
671
|
};
|
|
695
672
|
}
|
|
696
673
|
|
|
697
|
-
/** review- 前缀兜底判定:agent-registry.ts 的报错文案是 `Agent "${name}" not found. ...`
|
|
698
|
-
* (名字夹在 "Agent" 与 "not found" 之间),不能用连续子串 "Agent not found" 匹配。
|
|
699
|
-
* 已带 review- 前缀的 agent 不再重试(防死循环)。 */
|
|
700
|
-
function shouldRetryWithReviewPrefix(error, agentName) {
|
|
701
|
-
return typeof error === "string"
|
|
702
|
-
&& error.includes("not found")
|
|
703
|
-
&& typeof agentName === "string"
|
|
704
|
-
&& agentName.length > 0
|
|
705
|
-
&& !agentName.startsWith("review-");
|
|
706
|
-
}
|
|
707
|
-
|
|
708
674
|
/** 从 aggregated.md 内容回退解析(JSON 无效时的兜底,依赖 "- Must-fix: N" 固定格式)。 */
|
|
709
675
|
function parseAggregatedMd(content) {
|
|
710
676
|
const mustFixMatch = content.match(/[-*]\s*Must[-_]fix\s*[::]\s*(\d+)/i);
|
|
@@ -716,69 +682,29 @@ function parseAggregatedMd(content) {
|
|
|
716
682
|
};
|
|
717
683
|
}
|
|
718
684
|
|
|
719
|
-
/**
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
* 引号包裹的值剥引号、空值(`value || undefined`)回退。
|
|
723
|
-
* @param content 文件原文
|
|
724
|
-
* @param fallbackName 无 name 字段时的兜底(通常为 basename)
|
|
725
|
-
* @returns {name, model, description, systemPrompt, report, title, isCustom}
|
|
726
|
-
*/
|
|
727
|
-
function parseAgentMd(content, fallbackName) {
|
|
728
|
-
let name = fallbackName;
|
|
729
|
-
let model, description;
|
|
730
|
-
let body = content.trim();
|
|
731
|
-
if (content.startsWith("---")) {
|
|
732
|
-
const closeIdx = content.indexOf("---", 3);
|
|
733
|
-
if (closeIdx !== -1) {
|
|
734
|
-
const yaml = content.slice(3, closeIdx);
|
|
735
|
-
body = content.slice(closeIdx + 3).trim();
|
|
736
|
-
const extract = (key) => {
|
|
737
|
-
// 分隔符用 [ \t]* 而非 \s*:\s 含换行,空值 key(如 `name:`)后紧跟的下一行内容
|
|
738
|
-
// 会被 \s* 吞掉换行后捕获成该 key 的值。仅匹配空格/制表符则空值 → .+ 不匹配 → undefined。
|
|
739
|
-
const m = yaml.match(new RegExp("^" + key + ":[ \t]*(.+)$", "m"));
|
|
740
|
-
if (!m) return undefined;
|
|
741
|
-
let v = m[1].trim();
|
|
742
|
-
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
|
|
743
|
-
return v || undefined;
|
|
744
|
-
};
|
|
745
|
-
name = extract("name") || name;
|
|
746
|
-
model = extract("model");
|
|
747
|
-
description = extract("description");
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
return { name, model, description, systemPrompt: body, report: name, title: description || name, isCustom: true };
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/** 自定义 .md agent 加载:fs 读取 + parseAgentMd。读取失败经 fail 回调(缺省时直接抛错)。 */
|
|
754
|
-
function loadAgentMd(filePath, fail) {
|
|
755
|
-
let content;
|
|
756
|
-
try {
|
|
757
|
-
content = fs.readFileSync(filePath, "utf-8");
|
|
758
|
-
} catch (e) {
|
|
759
|
-
const msg = "agent 文件读取失败: " + filePath + " (" + e.message + ")";
|
|
760
|
-
if (typeof fail === "function") fail(msg);
|
|
761
|
-
throw new Error("review-fix-loop: " + msg);
|
|
762
|
-
}
|
|
763
|
-
return parseAgentMd(content, path.basename(filePath, ".md"));
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
/** fallow-scan:内置工具型 def(无 .md,跑 fallow audit 静态分析)。 */
|
|
685
|
+
/** fallow-scan:内置工具型 def(无 .md,跑 fallow audit 静态分析)。
|
|
686
|
+
* 不由 batchN 触发(batchN 值域 = agent .md 路径)——由独立参数 fallowScan=true 在脚本层
|
|
687
|
+
* 前置插入为首批。 */
|
|
767
688
|
const FALLOW_DEF = { name: "fallow-scan", title: "FALLOW STATIC ANALYSIS", report: "fallow-scan", isFallow: true };
|
|
768
689
|
|
|
769
690
|
/**
|
|
770
|
-
* Agent defs
|
|
771
|
-
*
|
|
772
|
-
* report
|
|
773
|
-
*
|
|
774
|
-
*
|
|
691
|
+
* Agent defs 解析(S4 路径统一版):batchN/fixAgent 值全部是 agentRef(.md 绝对路径)。
|
|
692
|
+
*
|
|
693
|
+
* - def 只含标识(path/name/report/title),**不读文件**——agent 内容的加载与 systemPrompt
|
|
694
|
+
* 注入由主线程 resolveAgentOpts(agent-call 按 path 加载)统一完成
|
|
695
|
+
* - `fallow-scan` 是脚本内部保留字(fallowScan 参数前置插入的首批),非用户参数值域
|
|
696
|
+
* @param batchNames 批内 agentRef 路径数组
|
|
775
697
|
*/
|
|
776
|
-
function resolveAgentDefs(batchNames
|
|
777
|
-
const loadFn = loader || loadAgentMd;
|
|
698
|
+
function resolveAgentDefs(batchNames) {
|
|
778
699
|
return batchNames.map((item) => {
|
|
779
700
|
if (item === "fallow-scan") return FALLOW_DEF;
|
|
780
|
-
if (item
|
|
781
|
-
|
|
701
|
+
if (!/^\/|^~\//.test(item) || !item.endsWith(".md")) {
|
|
702
|
+
throw new Error(
|
|
703
|
+
"review-fix-loop: 无效 agent 引用: " + item + "——必须是 .md 绝对路径(<available_subagents> 的 <location>)",
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
const name = item.split("/").pop().replace(/\.md$/, "");
|
|
707
|
+
return { path: item, name, report: name, title: name.toUpperCase() };
|
|
782
708
|
});
|
|
783
709
|
}
|
|
784
710
|
|
|
@@ -840,11 +766,8 @@ function resolveBatchTerminated(batchClean, terminated) {
|
|
|
840
766
|
module.exports = {
|
|
841
767
|
TARGET_TYPES,
|
|
842
768
|
VALID_ARG_KEYS,
|
|
843
|
-
normalizeBool,
|
|
844
|
-
normalizeInt,
|
|
845
769
|
parseBatches,
|
|
846
770
|
resolveBatchNames,
|
|
847
|
-
validateFallowScan,
|
|
848
771
|
buildReviewInstruction,
|
|
849
772
|
lockReviewBase,
|
|
850
773
|
buildScopedRecheckPrompt,
|
|
@@ -865,9 +788,6 @@ module.exports = {
|
|
|
865
788
|
parseResult,
|
|
866
789
|
normalizeAggregatorResult,
|
|
867
790
|
parseAggregatedMd,
|
|
868
|
-
shouldRetryWithReviewPrefix,
|
|
869
|
-
parseAgentMd,
|
|
870
|
-
loadAgentMd,
|
|
871
791
|
resolveAgentDefs,
|
|
872
792
|
recordAgentClean,
|
|
873
793
|
recordAgentDirty,
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
//
|
|
7
7
|
// 用法:
|
|
8
8
|
// workflow run review-fix-loop --args targetType=git-diff target=main \
|
|
9
|
-
// batch1=
|
|
9
|
+
// batch1="/path/reviewer-a.md,/path/reviewer-b.md" autoCommit=true
|
|
10
10
|
// workflow run review-fix-loop --args targetType=file target=/path/to/doc.md \
|
|
11
|
-
// batch1=reviewer autoCommit=false
|
|
11
|
+
// batch1="/path/doc-reviewer.md" autoCommit=false
|
|
12
12
|
//
|
|
13
|
+
// S4 路径统一:batch1..batchN/fixAgent 值 = agentRef(.md 绝对路径,<available_subagents> 的
|
|
14
|
+
// <location>);fallowScan=true 独立参数前置插入静态分析批次(不占 batchN)。
|
|
13
15
|
// ⚠️ 唯一带写操作的内置 workflow:fix 阶段会修改文件(autoCommit=true 时 commit)。
|
|
14
16
|
// ⚠️ lintScript 约束(本脚本已遵守):含 parallel() 入口,禁止 bare IIFE;
|
|
15
17
|
// agent() 调用顺序确定(批次按配置顺序稳定排序,callId 重放安全)。
|
|
@@ -20,11 +22,45 @@
|
|
|
20
22
|
// main 4.0.0 版自包含 677 行、缺批次参数时默认单批 ["reviewer"]、recheckAfterFix 默认 false。
|
|
21
23
|
// merge 时保留本版(功能更全),详见 .changeset/tidy-waves-description-phase-lint.md。
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
/* @pi-meta
|
|
26
|
+
name: review-fix-loop
|
|
27
|
+
description: >-
|
|
28
|
+
多批串行审查-修复循环:批内并行 review 聚合 must-fix 后迭代修复直到 clean
|
|
29
|
+
(唯一带写操作与 commit 副作用的内置 workflow,autoCommit 默认 false)
|
|
30
|
+
when: 用户要 review 并迭代修复至 clean
|
|
31
|
+
notFor: 单纯审查不改代码
|
|
32
|
+
phases: [Review, Fix]
|
|
33
|
+
parameters:
|
|
34
|
+
type: object
|
|
35
|
+
properties:
|
|
36
|
+
targetType: { type: string, enum: [git-diff, file, dir, text] }
|
|
37
|
+
target: { type: string, minLength: 1, pattern: '\S' }
|
|
38
|
+
autoCommit: { type: boolean, default: false }
|
|
39
|
+
maxRounds: { type: integer, default: 10, minimum: 1 }
|
|
40
|
+
stuckThreshold: { type: integer, default: 3, minimum: 1 }
|
|
41
|
+
skipCleanAgents: { type: boolean, default: true }
|
|
42
|
+
recheckAfterFix: { type: boolean, default: false }
|
|
43
|
+
fixAgent: { type: string }
|
|
44
|
+
maxFixAttempts: { type: integer, default: 2, minimum: 1 }
|
|
45
|
+
convergeNewIssues: { type: integer, default: 1, minimum: 1 }
|
|
46
|
+
convergeRounds: { type: integer, default: 2, minimum: 1 }
|
|
47
|
+
reviewPrompt: { type: string }
|
|
48
|
+
fixPrompt: { type: string }
|
|
49
|
+
fallowScan: { type: boolean, default: false }
|
|
50
|
+
agents: { type: string }
|
|
51
|
+
batchNames: { type: string }
|
|
52
|
+
patternProperties:
|
|
53
|
+
"^batch\\d+$":
|
|
54
|
+
type: string
|
|
55
|
+
description: 任意 batchN 编号,至少一个;值为 agent .md 绝对路径(逗号分隔多 agent)
|
|
56
|
+
required: [targetType, target]
|
|
57
|
+
usage: |
|
|
58
|
+
## 使用说明
|
|
59
|
+
- batch1..batchN 与 agents 互斥(至少传一个 batchN);值 = agentRef(.md 绝对路径,<available_subagents> 的 <location>)
|
|
60
|
+
- fixAgent:fix 阶段执行者(agentRef),缺省用通用 subagent + 内联 fixPrompt
|
|
61
|
+
- fallowScan=true 仅 targetType=git-diff 合法(前置静态分析批次,不占 batchN)
|
|
62
|
+
- 示例:workflow run review-fix-loop --args targetType=git-diff target=main batch1="/path/fallow-agent.md,/path/reviewer.md" autoCommit=true
|
|
63
|
+
*/
|
|
28
64
|
|
|
29
65
|
// ── 参数解析 + 白名单校验(fail-fast) ────────────────────────────
|
|
30
66
|
|
|
@@ -33,19 +69,16 @@ function fail(msg) {
|
|
|
33
69
|
}
|
|
34
70
|
|
|
35
71
|
// ── 可测纯函数模块 ────────────────────────────────────────────────
|
|
36
|
-
//
|
|
37
|
-
// 的纯函数在 review-fix-loop-utils.cjs
|
|
38
|
-
// vitest 单测见 src/__tests__/review-fix-loop-utils.test.ts
|
|
72
|
+
// 参数校验(白名单,类型校验由 m3 args-validator schema 接管)/批次解析/聚合结果解析/审查指令构建
|
|
73
|
+
// 的纯函数在 review-fix-loop-utils.cjs,
|
|
74
|
+
// vitest 单测见 src/__tests__/review-fix-loop-utils.test.ts。
|
|
39
75
|
// worker 运行时经 workerData.scriptPath 定位自身目录——内置 workflow 在 npm 包内,
|
|
40
76
|
// process.cwd() 是用户项目目录,不能作为锚点;其他引擎无 workerData 时回退 cwd。
|
|
41
77
|
const {
|
|
42
78
|
TARGET_TYPES,
|
|
43
79
|
VALID_ARG_KEYS,
|
|
44
|
-
normalizeBool,
|
|
45
|
-
normalizeInt,
|
|
46
80
|
parseBatches,
|
|
47
81
|
resolveBatchNames,
|
|
48
|
-
validateFallowScan,
|
|
49
82
|
buildReviewInstruction,
|
|
50
83
|
lockReviewBase,
|
|
51
84
|
buildScopedRecheckPrompt,
|
|
@@ -65,7 +98,6 @@ const {
|
|
|
65
98
|
parseResult,
|
|
66
99
|
normalizeAggregatorResult,
|
|
67
100
|
parseAggregatedMd,
|
|
68
|
-
shouldRetryWithReviewPrefix,
|
|
69
101
|
resolveAgentDefs,
|
|
70
102
|
recordAgentClean,
|
|
71
103
|
recordAgentDirty,
|
|
@@ -82,7 +114,7 @@ const {
|
|
|
82
114
|
for (const key of Object.keys($ARGS)) {
|
|
83
115
|
if (VALID_ARG_KEYS.has(key)) continue;
|
|
84
116
|
if (/^batch\d+$/.test(key)) continue;
|
|
85
|
-
fail("未知参数: " + key + "(合法参数: targetType/target/batch1..batchN/agents/batchNames/reviewPrompt/fixPrompt/autoCommit/maxRounds/stuckThreshold/
|
|
117
|
+
fail("未知参数: " + key + "(合法参数: targetType/target/batch1..batchN/agents/batchNames/reviewPrompt/fixPrompt/autoCommit/maxRounds/stuckThreshold/skipCleanAgents/recheckAfterFix/fallowScan/fixAgent/maxFixAttempts/convergeNewIssues/convergeRounds)");
|
|
86
118
|
}
|
|
87
119
|
|
|
88
120
|
const targetType = $ARGS.targetType;
|
|
@@ -98,14 +130,26 @@ const reviewPrompt = typeof $ARGS.reviewPrompt === "string" && $ARGS.reviewPromp
|
|
|
98
130
|
const fixPrompt = typeof $ARGS.fixPrompt === "string" && $ARGS.fixPrompt.trim()
|
|
99
131
|
? $ARGS.fixPrompt.trim()
|
|
100
132
|
: "修复全部 must-fix 问题(critical/major)。最小正确修复,不做重构、不做风格改动。";
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
133
|
+
// 字符串强制转换(m2 exec-review MAJOR-1):LLM 可能以字符串传布尔/整数(旧 normalizeBool
|
|
134
|
+
// 正是为此防御——"false" ?? false 是 truthy 会误触发 commit)。m3 args-validator
|
|
135
|
+
// (chokepoint + coerceTypes)上线后此处为双保险,直接执行路径仍受保护。
|
|
136
|
+
const coerceBool = (v, fallback) =>
|
|
137
|
+
typeof v === "boolean" ? v : v === "true" ? true : v === "false" ? false : fallback;
|
|
138
|
+
const coerceInt = (v, fallback) =>
|
|
139
|
+
typeof v === "number" && Number.isInteger(v)
|
|
140
|
+
? v
|
|
141
|
+
: typeof v === "string" && /^\d+$/.test(v.trim())
|
|
142
|
+
? parseInt(v, 10)
|
|
143
|
+
: fallback;
|
|
144
|
+
|
|
145
|
+
const autoCommit = coerceBool($ARGS.autoCommit, false);
|
|
146
|
+
const maxRounds = coerceInt($ARGS.maxRounds, 10);
|
|
147
|
+
const stuckThreshold = coerceInt($ARGS.stuckThreshold, 3);
|
|
148
|
+
const skipCleanAgents = coerceBool($ARGS.skipCleanAgents, true);
|
|
105
149
|
// 默认 recheckAfterFix=false:clean agent 下轮跳过(与 skipCleanAgents=true 字面语义一致),
|
|
106
150
|
// RC-5(fix 后全批全量重审放大 token)在默认场景消失。传 true 启用可选强回归模式:fix 后重派
|
|
107
151
|
// 全批,clean agent 走限定 prompt(buildScopedRecheckPrompt,只审 modifiedFiles,5.5)。
|
|
108
|
-
const recheckAfterFix =
|
|
152
|
+
const recheckAfterFix = coerceBool($ARGS.recheckAfterFix, false);
|
|
109
153
|
// fixAgent(5.3):值语义同 batchN 的 agent 项(内置名 / agent.md 路径),解析复用
|
|
110
154
|
// resolveAgentDefs 白名单与加载逻辑。传入时 fix 阶段用 agent({agent: ...}) 派发(代码场景
|
|
111
155
|
// 的 verify 命令写在该 agent.md 内);未传保持现状(通用 subagent + 内联 prompt)。
|
|
@@ -114,10 +158,10 @@ const FIX_AGENT_RAW = typeof $ARGS.fixAgent === "string" && $ARGS.fixAgent.trim(
|
|
|
114
158
|
const FIX_DEF = FIX_AGENT_RAW ? resolveAgentDefs([FIX_AGENT_RAW])[0] : null;
|
|
115
159
|
// 5.7 收敛终止参数:maxFixAttempts(needs-redesign 阈值,RC-7)/ convergeNewIssues +
|
|
116
160
|
// convergeRounds(新发现率收敛阈值)
|
|
117
|
-
const maxFixAttempts =
|
|
118
|
-
const convergeNewIssues =
|
|
119
|
-
const convergeRounds =
|
|
120
|
-
const MODEL =
|
|
161
|
+
const maxFixAttempts = coerceInt($ARGS.maxFixAttempts, 2);
|
|
162
|
+
const convergeNewIssues = coerceInt($ARGS.convergeNewIssues, 1);
|
|
163
|
+
const convergeRounds = coerceInt($ARGS.convergeRounds, 2);
|
|
164
|
+
const MODEL = $MODEL;
|
|
121
165
|
|
|
122
166
|
// base 锁定(RC-6,5.6):git-diff 场景 run 启动时锁定 base commit,全程用锁定 hash 构造
|
|
123
167
|
// diff 指令,防止 run 期间 base ref 被更新导致各轮 diff 范围不一致。rev-parse 失败(非 git
|
|
@@ -134,7 +178,15 @@ const reviewInstruction = buildReviewInstruction(targetType, lockedBase.base);
|
|
|
134
178
|
|
|
135
179
|
// 批次解析:batch1..batchN(缺号报错)/ agents 简写;无默认批次——缺批次参数时
|
|
136
180
|
// parseBatches 直接 fail-fast(与头注释「batch1..batchN/agents 必传」一致)
|
|
137
|
-
const
|
|
181
|
+
const rawBatches = parseBatches($ARGS, fail);
|
|
182
|
+
|
|
183
|
+
// S4:fallowScan 独立参数(不由 batchN 触发——batchN 值域 = agentRef 路径)。
|
|
184
|
+
// fallow 作为内置首批前置插入(fallow-scan 保留字在 resolveAgentDefs 内解析)。
|
|
185
|
+
const fallowScan = coerceBool($ARGS.fallowScan, false);
|
|
186
|
+
if (fallowScan && targetType !== "git-diff") {
|
|
187
|
+
fail("fallowScan 只支持 targetType=git-diff(它审查 git 变更的静态分析),实际 targetType=" + targetType);
|
|
188
|
+
}
|
|
189
|
+
const BATCHES = fallowScan ? [["fallow-scan"], ...rawBatches] : rawBatches;
|
|
138
190
|
|
|
139
191
|
// batchNames(数量校验)
|
|
140
192
|
const rawBatchNames = typeof $ARGS.batchNames === "string" && $ARGS.batchNames.trim()
|
|
@@ -142,9 +194,6 @@ const rawBatchNames = typeof $ARGS.batchNames === "string" && $ARGS.batchNames.t
|
|
|
142
194
|
: [];
|
|
143
195
|
const BATCH_NAMES = resolveBatchNames(rawBatchNames, BATCHES, fail);
|
|
144
196
|
|
|
145
|
-
// fallow-scan 只在 git-diff 类型下有意义
|
|
146
|
-
validateFallowScan(BATCHES, targetType, fail);
|
|
147
|
-
|
|
148
197
|
// ── Schemas ─────────────────────────────────────────────────────────
|
|
149
198
|
|
|
150
199
|
const reviewerSchema = {
|
|
@@ -253,6 +302,32 @@ const STATE_FILE = RUN_ROOT + "/state.json";
|
|
|
253
302
|
fs.mkdirSync(RUN_ROOT, { recursive: true });
|
|
254
303
|
log("Run directory: " + RUN_ROOT);
|
|
255
304
|
|
|
305
|
+
// ── Startup fail-fast: validate agent ref paths exist (ADR-0003 D6) ─
|
|
306
|
+
// 启动期校验所有 batchN/fixAgent 路径存在,不存在立即报错(带 location 恢复指引),
|
|
307
|
+
// 避免跑到 round 中段 agent-call 时 loadByPath 失败才暴露。FALLOW_DEF(isFallow,
|
|
308
|
+
// 无 path)跳过——它是内置工具标记非文件路径。
|
|
309
|
+
function validateAgentPaths(defs) {
|
|
310
|
+
for (const def of defs) {
|
|
311
|
+
if (def.isFallow || !def.path) continue;
|
|
312
|
+
// MF-2:与 normalizeRef(src/shared/agent-ref.ts)对齐——~/ 前缀展开为 homedir 后再 statSync。
|
|
313
|
+
// resolveAgentDefs 接受 ~/ 前缀、normalizeRef 运行时也展开,此处不展开会「先接受后拒绝」误报 ENOENT。
|
|
314
|
+
const expanded = def.path.startsWith("~/")
|
|
315
|
+
? path.join(os.homedir(), def.path.slice(2))
|
|
316
|
+
: def.path;
|
|
317
|
+
try {
|
|
318
|
+
fs.statSync(expanded);
|
|
319
|
+
} catch {
|
|
320
|
+
fail("Agent file not found: " + def.path + ". Check <available_subagents> <location> for valid agent refs (absolute .md path).");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const startupDefs = [];
|
|
325
|
+
for (const batch of BATCHES) {
|
|
326
|
+
startupDefs.push(...resolveAgentDefs(batch));
|
|
327
|
+
}
|
|
328
|
+
if (FIX_DEF) startupDefs.push(FIX_DEF);
|
|
329
|
+
validateAgentPaths(startupDefs);
|
|
330
|
+
|
|
256
331
|
// ── State management (persistent, atomic writes) ────────────────────
|
|
257
332
|
|
|
258
333
|
function loadState() {
|
|
@@ -310,16 +385,18 @@ function buildReviewCall(def, round, max, batchIndex, roundDir, scoped) {
|
|
|
310
385
|
? "\nPrior batch reports (optional context): " + RUN_ROOT + "/batch-*/ (use read)"
|
|
311
386
|
: "";
|
|
312
387
|
const base = {
|
|
313
|
-
model: MODEL
|
|
388
|
+
model: MODEL,
|
|
314
389
|
schema: reviewerSchema,
|
|
315
390
|
description: def.name,
|
|
316
391
|
timeoutMs: 3_600_000, // 1h(只读审查 + retry 退避余量)
|
|
317
|
-
// returnMeta: true —
|
|
318
|
-
// {value, error},raw.error
|
|
392
|
+
// returnMeta: true — 失败时 resolve
|
|
393
|
+
// {value, error},raw.error 可检测(结构化终止可达);成功时
|
|
319
394
|
// value = parsedOutput ?? content,parseResult 作用于 raw.value(MF-1)。
|
|
320
395
|
returnMeta: true,
|
|
321
396
|
};
|
|
322
397
|
|
|
398
|
+
// S4:agentRef = 路径(非 fallow 的 def 必有 path)——systemPrompt/model 由主线程
|
|
399
|
+
// resolveAgentOpts 按 path 加载注入,脚本不再拼 md 内容。
|
|
323
400
|
if (def.isFallow) {
|
|
324
401
|
return {
|
|
325
402
|
...base,
|
|
@@ -345,9 +422,6 @@ function buildReviewCall(def, round, max, batchIndex, roundDir, scoped) {
|
|
|
345
422
|
// scoped 分支(recheck 限定)在下方单独处理(含对账段)。
|
|
346
423
|
if (round > 1 && !scoped) {
|
|
347
424
|
const prevRoundDir = RUN_ROOT + "/batch-" + batchIndex + "/round-" + (round - 1);
|
|
348
|
-
const r2Spec = def.isCustom
|
|
349
|
-
? "\n\nReviewer specification (from agent file):\n" + def.systemPrompt
|
|
350
|
-
: "";
|
|
351
425
|
return {
|
|
352
426
|
...base,
|
|
353
427
|
schema: { ...reviewerSchema, required: [...reviewerSchema.required, "reconciliation"] },
|
|
@@ -359,8 +433,8 @@ function buildReviewCall(def, round, max, batchIndex, roundDir, scoped) {
|
|
|
359
433
|
? state.fixResults[state.fixResults.length - 1]
|
|
360
434
|
: null,
|
|
361
435
|
knownRemaining: (state.knownRemaining && Array.isArray(state.knownRemaining)) ? state.knownRemaining : [],
|
|
362
|
-
})
|
|
363
|
-
agent: def.
|
|
436
|
+
}),
|
|
437
|
+
agent: def.path,
|
|
364
438
|
};
|
|
365
439
|
}
|
|
366
440
|
|
|
@@ -380,14 +454,11 @@ function buildReviewCall(def, round, max, batchIndex, roundDir, scoped) {
|
|
|
380
454
|
fixResult: state.fixResults && state.fixResults.length
|
|
381
455
|
? state.fixResults[state.fixResults.length - 1]
|
|
382
456
|
: null,
|
|
383
|
-
})
|
|
384
|
-
agent: def.
|
|
457
|
+
}),
|
|
458
|
+
agent: def.path,
|
|
385
459
|
};
|
|
386
460
|
}
|
|
387
461
|
|
|
388
|
-
const spec = def.isCustom
|
|
389
|
-
? "\n\nReviewer specification (from agent file):\n" + def.systemPrompt
|
|
390
|
-
: "";
|
|
391
462
|
return {
|
|
392
463
|
...base,
|
|
393
464
|
prompt: [
|
|
@@ -396,24 +467,18 @@ function buildReviewCall(def, round, max, batchIndex, roundDir, scoped) {
|
|
|
396
467
|
reviewInstruction + prevBatchesHint,
|
|
397
468
|
"",
|
|
398
469
|
"Review requirements:",
|
|
399
|
-
reviewPrompt
|
|
470
|
+
reviewPrompt,
|
|
400
471
|
"",
|
|
401
472
|
"output 路径:" + roundDir + "/" + def.report + ".md",
|
|
402
473
|
"Write report to: " + roundDir + "/" + def.report + ".md",
|
|
403
474
|
].join("\n"),
|
|
404
|
-
agent: def.
|
|
475
|
+
agent: def.path,
|
|
405
476
|
};
|
|
406
477
|
}
|
|
407
478
|
|
|
408
|
-
//
|
|
479
|
+
// S4:agentRef = 路径,主线程按路径加载(systemPrompt 注入);无名字查找,无需前缀兜底
|
|
409
480
|
async function runReviewAgent(call) {
|
|
410
|
-
|
|
411
|
-
if (raw && typeof raw === "object" && raw.error
|
|
412
|
-
&& shouldRetryWithReviewPrefix(raw.error, call.agent)) {
|
|
413
|
-
log("Agent not found: " + call.agent + " — retrying with review- prefix");
|
|
414
|
-
raw = await agent({ ...call, agent: "review-" + call.agent });
|
|
415
|
-
}
|
|
416
|
-
return raw;
|
|
481
|
+
return agent(call);
|
|
417
482
|
}
|
|
418
483
|
|
|
419
484
|
// ── Main loop: batches (serial) × rounds (per-batch) ────────────────
|
|
@@ -777,21 +842,19 @@ for (let batchIndex = 1; batchIndex <= BATCHES.length; batchIndex++) {
|
|
|
777
842
|
prompt: buildFixPrompt({
|
|
778
843
|
header: "Fix round " + round + " (batch " + batchIndex + ")",
|
|
779
844
|
reportContent,
|
|
780
|
-
fixPrompt
|
|
781
|
-
? fixPrompt + "\n\nFixer specification (from agent file):\n" + FIX_DEF.systemPrompt
|
|
782
|
-
: fixPrompt,
|
|
845
|
+
fixPrompt,
|
|
783
846
|
commitInstr,
|
|
784
847
|
caution: agg.fixes_caution && agg.fixes_caution.length ? agg.fixes_caution : [],
|
|
785
848
|
}),
|
|
786
849
|
schema: fixSchema,
|
|
787
|
-
//
|
|
788
|
-
//
|
|
789
|
-
model: MODEL
|
|
850
|
+
// S4:fixAgent = agentRef 路径(主线程按路径加载 + frontmatter model 传播);
|
|
851
|
+
// 未传保持现状(通用 subagent + 内联 fixPrompt)。
|
|
852
|
+
model: MODEL,
|
|
790
853
|
description: (FIX_DEF && FIX_DEF.name) || "fix",
|
|
791
854
|
// fix 不设 timeoutMs = 不限时(execute-options-mapper: undefined/<=0 → 不设超时)。
|
|
792
855
|
// 带写操作(改项目代码)可能很久(大重构/多文件),不应被墙钟超时打断。
|
|
793
856
|
returnMeta: true,
|
|
794
|
-
...(FIX_DEF &&
|
|
857
|
+
...(FIX_DEF && FIX_DEF.path ? { agent: FIX_DEF.path } : {}),
|
|
795
858
|
});
|
|
796
859
|
|
|
797
860
|
// returnMeta 下 fxRaw = {value, error}:先查 error(失败分支可达,MF-1),再对 value 做 parseResult
|
|
@@ -12,11 +12,22 @@
|
|
|
12
12
|
//
|
|
13
13
|
// ⚠️ lintScript 约束(本脚本已遵守):含 parallel() 入口(兼 agent 嵌套),禁止 bare IIFE
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
/* @pi-meta
|
|
16
|
+
name: scatter-gather
|
|
17
|
+
description: 通用编排:scatter 拆分 → parallel 处理 → gather 合并 三段
|
|
18
|
+
phases: [scatter, process, gather]
|
|
19
|
+
parameters:
|
|
20
|
+
type: object
|
|
21
|
+
properties:
|
|
22
|
+
task: { type: string }
|
|
23
|
+
agents: { type: string }
|
|
24
|
+
required: [task]
|
|
25
|
+
usage: |
|
|
26
|
+
## 使用说明
|
|
27
|
+
- scatter 拆解大任务 → process 并行处理 → gather 聚合结果
|
|
28
|
+
- agents:逗号分隔的 agent .md 绝对路径,按顺序对应 scatter/process/gather 三段
|
|
29
|
+
- 示例:workflow run scatter-gather --args task="<大任务描述>" agents="/path/splitter.md,/path/processor.md"
|
|
30
|
+
*/
|
|
20
31
|
|
|
21
32
|
// ── 入参($ARGS)──────────────────────────────────────────────────
|
|
22
33
|
const task = $ARGS.task;
|
|
@@ -24,7 +35,17 @@ if (!task) {
|
|
|
24
35
|
throw new Error("scatter-gather 缺少必需参数 task。用法:workflow run scatter-gather --args task=\"<大任务描述>\"");
|
|
25
36
|
}
|
|
26
37
|
|
|
27
|
-
|
|
38
|
+
// S4:agents 参数 = 逗号分隔的 agentRef 路径数组,按顺序对应 scatter/process/gather
|
|
39
|
+
// worker 沙箱为 eval 模式:require 相对路径以 cwd 为基准(非脚本目录),
|
|
40
|
+
// 必须用 workerData.scriptPath 锚定脚本目录(review-fix-loop 同模式)。
|
|
41
|
+
const SCRIPT_DIR = workerData && workerData.scriptPath
|
|
42
|
+
? require("path").dirname(workerData.scriptPath)
|
|
43
|
+
: process.cwd();
|
|
44
|
+
const { parseAgentRefs, agentRefAt } = require(SCRIPT_DIR + "/_shared/agent-refs.cjs");
|
|
45
|
+
const agentRefs = parseAgentRefs($ARGS.agents);
|
|
46
|
+
const stepAgent = (i) => { const ref = agentRefAt(agentRefs, i); return ref ? { agent: ref } : {}; };
|
|
47
|
+
|
|
48
|
+
log("scatter-gather 开始,task=" + task + (agentRefs.length ? ",agents=" + agentRefs.join(",") : ""));
|
|
28
49
|
|
|
29
50
|
let currentPhase = "init";
|
|
30
51
|
let outcome;
|
|
@@ -56,6 +77,7 @@ try {
|
|
|
56
77
|
required: ["subtasks"],
|
|
57
78
|
},
|
|
58
79
|
description: "scatter-split",
|
|
80
|
+
...stepAgent(0),
|
|
59
81
|
});
|
|
60
82
|
|
|
61
83
|
const subtasks = Array.isArray(split?.subtasks) ? split.subtasks : [];
|
|
@@ -84,6 +106,7 @@ try {
|
|
|
84
106
|
required: ["subtask", "result"],
|
|
85
107
|
},
|
|
86
108
|
description: "scatter-process-" + s.name,
|
|
109
|
+
...stepAgent(1),
|
|
87
110
|
})
|
|
88
111
|
),
|
|
89
112
|
);
|
|
@@ -129,6 +152,7 @@ try {
|
|
|
129
152
|
required: ["mergedResult", "completeness"],
|
|
130
153
|
},
|
|
131
154
|
description: "scatter-gather-merge",
|
|
155
|
+
...stepAgent(2),
|
|
132
156
|
});
|
|
133
157
|
|
|
134
158
|
outcome = {
|