ethan-skill 1.12.0 → 1.14.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/CHANGELOG.md +307 -0
- package/README.md +85 -33
- package/dist/agents/index.d.ts +14 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +37 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/orchestrator.d.ts +10 -0
- package/dist/agents/orchestrator.d.ts.map +1 -0
- package/dist/agents/orchestrator.js +654 -0
- package/dist/agents/orchestrator.js.map +1 -0
- package/dist/agents/orchestrator.test.d.ts +2 -0
- package/dist/agents/orchestrator.test.d.ts.map +1 -0
- package/dist/agents/orchestrator.test.js +151 -0
- package/dist/agents/orchestrator.test.js.map +1 -0
- package/dist/agents/presets.d.ts +16 -0
- package/dist/agents/presets.d.ts.map +1 -0
- package/dist/agents/presets.js +164 -0
- package/dist/agents/presets.js.map +1 -0
- package/dist/agents/types.d.ts +33 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +7 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/cli/index.js +255 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/loader/custom-agent-loader.d.ts +13 -0
- package/dist/loader/custom-agent-loader.d.ts.map +1 -0
- package/dist/loader/custom-agent-loader.js +120 -0
- package/dist/loader/custom-agent-loader.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +161 -1
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Multi-Agent 编排器
|
|
4
|
+
* 将 Pipeline 的步骤分配给不同 Agent,生成协作式超级 Prompt
|
|
5
|
+
* 执行模型:Prompt 生成(粘贴到 AI 编辑器执行,零 API 成本)
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.buildMultiAgentPrompt = buildMultiAgentPrompt;
|
|
9
|
+
const presets_1 = require("./presets");
|
|
10
|
+
/** 根据 skillId 解析对应的 Agent(兜底 Code Agent) */
|
|
11
|
+
function resolveAgent(skillId, agents, routing) {
|
|
12
|
+
const agentId = routing[skillId];
|
|
13
|
+
return agents.find((a) => a.id === agentId) ?? agents.find((a) => a.id === 'coder') ?? agents[0];
|
|
14
|
+
}
|
|
15
|
+
/** 生成 Multi-Agent 编排超级 Prompt */
|
|
16
|
+
function buildMultiAgentPrompt(pipeline, skills, agents, options) {
|
|
17
|
+
const { context, lang = 'zh', snapshot, mode = 'sequential' } = options;
|
|
18
|
+
const isEn = lang === 'en';
|
|
19
|
+
const routing = (0, presets_1.buildSkillRouting)(agents);
|
|
20
|
+
const participatingAgents = (0, presets_1.getAgentsForPipeline)(pipeline.skillIds, agents, routing);
|
|
21
|
+
const total = skills.length;
|
|
22
|
+
if (mode === 'parallel') {
|
|
23
|
+
return isEn
|
|
24
|
+
? buildEnParallelPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total)
|
|
25
|
+
: buildZhParallelPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total);
|
|
26
|
+
}
|
|
27
|
+
if (mode === 'review-loop') {
|
|
28
|
+
return isEn
|
|
29
|
+
? buildEnReviewLoopPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total)
|
|
30
|
+
: buildZhReviewLoopPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total);
|
|
31
|
+
}
|
|
32
|
+
if (mode === 'consensus') {
|
|
33
|
+
return isEn
|
|
34
|
+
? buildEnConsensusPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total)
|
|
35
|
+
: buildZhConsensusPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total);
|
|
36
|
+
}
|
|
37
|
+
// sequential(默认)
|
|
38
|
+
if (isEn) {
|
|
39
|
+
return buildEnPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total);
|
|
40
|
+
}
|
|
41
|
+
return buildZhPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total);
|
|
42
|
+
}
|
|
43
|
+
// ─── 中文版 ──────────────────────────────────────────────────────────────────
|
|
44
|
+
function buildZhPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total) {
|
|
45
|
+
const header = `# 🤖 Multi-Agent 编排:${pipeline.name}
|
|
46
|
+
|
|
47
|
+
**任务背景**:${context}
|
|
48
|
+
**Pipeline**:${pipeline.name}(\`${pipeline.id}\`)
|
|
49
|
+
**执行模式**:Multi-Agent 协作编排
|
|
50
|
+
**参与 Agents**:${participatingAgents.length} 个 | **总步数**:${total} 步`;
|
|
51
|
+
// Agent 阵容表
|
|
52
|
+
const agentStepMap = buildAgentStepMap(skills, agents, routing, false);
|
|
53
|
+
const rosterRows = participatingAgents.map((a) => {
|
|
54
|
+
const steps = agentStepMap[a.id] || [];
|
|
55
|
+
const stepStr = steps.length > 0 ? `步骤 ${steps.join('、')}` : '—';
|
|
56
|
+
return `| ${a.emoji} **${a.name}** | ${a.role} | ${stepStr} |`;
|
|
57
|
+
}).join('\n');
|
|
58
|
+
const roster = `## 🎭 Agent 阵容
|
|
59
|
+
|
|
60
|
+
| Agent | 职责 | 负责步骤 |
|
|
61
|
+
|-------|------|---------|
|
|
62
|
+
${rosterRows}`;
|
|
63
|
+
const protocol = `## 📋 协作协议(必须严格遵守)
|
|
64
|
+
|
|
65
|
+
1. 每个 Agent 严格按分配步骤顺序执行,**不越权执行其他 Agent 的步骤**
|
|
66
|
+
2. 每步完成后输出 **Handoff 摘要**(≤150字),交接给下一个 Agent
|
|
67
|
+
3. 下一个 Agent 必须**先阅读 Handoff 摘要**,再开始执行
|
|
68
|
+
4. 所有步骤完成后,将输出折叠在 \`<details>\` 标签中
|
|
69
|
+
5. 全部步骤完成后,输出一份完整的 Multi-Agent 执行报告
|
|
70
|
+
|
|
71
|
+
**每步输出格式:**
|
|
72
|
+
|
|
73
|
+
\`\`\`
|
|
74
|
+
<details>
|
|
75
|
+
<summary>✅ 步骤 N:[Agent名] · [Skill名] — 已完成</summary>
|
|
76
|
+
|
|
77
|
+
[本步骤完整输出]
|
|
78
|
+
|
|
79
|
+
</details>
|
|
80
|
+
\`\`\``;
|
|
81
|
+
// 步骤详情(含 Agent 标签和 Handoff)
|
|
82
|
+
const stepsSection = skills.map((skill, i) => {
|
|
83
|
+
const stepNum = i + 1;
|
|
84
|
+
const agent = resolveAgent(skill.id, agents, routing);
|
|
85
|
+
const nextAgent = i < skills.length - 1 ? resolveAgent(skills[i + 1].id, agents, routing) : null;
|
|
86
|
+
const handoffFrom = i > 0 ? resolveAgent(skills[i - 1].id, agents, routing) : null;
|
|
87
|
+
const handoffNote = handoffFrom && handoffFrom.id !== agent.id
|
|
88
|
+
? `> 📨 **Handoff 摘要**(来自 ${handoffFrom.emoji} ${handoffFrom.name}):\`[上一步核心产出,由上一 Agent 填写]\`\n\n`
|
|
89
|
+
: i > 0
|
|
90
|
+
? `> 📝 **前序输出**(${agent.emoji} ${agent.name} 连续执行):使用步骤 ${i} 的核心产出作为背景。\n\n`
|
|
91
|
+
: '';
|
|
92
|
+
const stepsContent = skill.steps
|
|
93
|
+
.map((s, si) => {
|
|
94
|
+
const title = s.title.replace(/^\d+[.、]\s*/, '');
|
|
95
|
+
const indented = s.content.split('\n').map((l) => ` ${l}`).join('\n');
|
|
96
|
+
return ` ${si + 1}. **${title}**\n${indented}`;
|
|
97
|
+
})
|
|
98
|
+
.join('\n\n');
|
|
99
|
+
const handoffTo = nextAgent && nextAgent.id !== agent.id
|
|
100
|
+
? `\n\n#### ✅ Handoff → ${nextAgent.emoji} ${nextAgent.name}\n\n> 🔖 请在此输出本步核心产出(≤150字),交接给 **${nextAgent.name}**:\n> \`[请填写:完成了什么、关键决策、下一步需要注意什么]\``
|
|
101
|
+
: '';
|
|
102
|
+
return `### ${agent.emoji} ${agent.name} — 步骤 ${stepNum}/${total}:${skill.name}
|
|
103
|
+
|
|
104
|
+
${handoffNote}**目标**:${skill.description}
|
|
105
|
+
|
|
106
|
+
**执行内容**:
|
|
107
|
+
|
|
108
|
+
${stepsContent}
|
|
109
|
+
|
|
110
|
+
**输出格式**:${skill.outputFormat}${handoffTo}`;
|
|
111
|
+
}).join('\n\n---\n\n');
|
|
112
|
+
// 最终报告模板
|
|
113
|
+
const reportTemplate = buildZhReportTemplate(pipeline, skills, agents, participatingAgents, routing, context);
|
|
114
|
+
const startCmd = `**立即开始执行:${participatingAgents[0].emoji} ${participatingAgents[0].name} 执行步骤 1。**`;
|
|
115
|
+
const snapshotBlock = snapshot ?? '';
|
|
116
|
+
return [header, snapshotBlock, '---', roster, '---', protocol, '---', `## 🔄 执行流程`, stepsSection, '---', reportTemplate, startCmd]
|
|
117
|
+
.filter(Boolean)
|
|
118
|
+
.join('\n\n');
|
|
119
|
+
}
|
|
120
|
+
function buildZhReportTemplate(pipeline, skills, agents, participatingAgents, routing, context) {
|
|
121
|
+
// 按 Agent 分组步骤
|
|
122
|
+
const agentSections = participatingAgents.map((agent) => {
|
|
123
|
+
const agentSkills = skills.filter((s) => {
|
|
124
|
+
const a = resolveAgent(s.id, agents, routing);
|
|
125
|
+
return a.id === agent.id;
|
|
126
|
+
});
|
|
127
|
+
const skillList = agentSkills.map((s) => `### ${s.name}\n[完整输出]\n\n---`).join('\n\n');
|
|
128
|
+
return `## ${agent.emoji} ${agent.name} 产出\n\n${skillList}`;
|
|
129
|
+
}).join('\n\n');
|
|
130
|
+
return `## 📄 最终输出要求
|
|
131
|
+
|
|
132
|
+
全部步骤完成后,输出以下 Multi-Agent 执行报告:
|
|
133
|
+
|
|
134
|
+
# Multi-Agent 执行报告 — ${context}
|
|
135
|
+
> Pipeline:${pipeline.name} | Agents:${participatingAgents.map((a) => `${a.emoji} ${a.name}`).join(' → ')}
|
|
136
|
+
|
|
137
|
+
${agentSections}
|
|
138
|
+
|
|
139
|
+
*由 Ethan Multi-Agent 自动生成*`;
|
|
140
|
+
}
|
|
141
|
+
// ─── 英文版 ──────────────────────────────────────────────────────────────────
|
|
142
|
+
function buildEnPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total) {
|
|
143
|
+
const header = `# 🤖 Multi-Agent Orchestration: ${pipeline.name}
|
|
144
|
+
|
|
145
|
+
**Context**: ${context}
|
|
146
|
+
**Pipeline**: ${pipeline.name} (\`${pipeline.id}\`)
|
|
147
|
+
**Mode**: Multi-Agent Collaboration
|
|
148
|
+
**Agents**: ${participatingAgents.length} | **Steps**: ${total}`;
|
|
149
|
+
const agentStepMap = buildAgentStepMap(skills, agents, routing, false);
|
|
150
|
+
const rosterRows = participatingAgents.map((a) => {
|
|
151
|
+
const steps = agentStepMap[a.id] || [];
|
|
152
|
+
const stepStr = steps.length > 0 ? `Steps ${steps.join(', ')}` : '—';
|
|
153
|
+
return `| ${a.emoji} **${a.nameEn}** | ${a.role} | ${stepStr} |`;
|
|
154
|
+
}).join('\n');
|
|
155
|
+
const roster = `## 🎭 Agent Roster
|
|
156
|
+
|
|
157
|
+
| Agent | Role | Assigned Steps |
|
|
158
|
+
|-------|------|----------------|
|
|
159
|
+
${rosterRows}`;
|
|
160
|
+
const protocol = `## 📋 Collaboration Protocol (Must Follow)
|
|
161
|
+
|
|
162
|
+
1. Each Agent executes only its assigned steps — **no overstepping**
|
|
163
|
+
2. After each step, output a **Handoff Summary** (≤150 words) for the next Agent
|
|
164
|
+
3. The next Agent must **read the Handoff Summary** before starting
|
|
165
|
+
4. Wrap each step output in \`<details>\` collapse tags
|
|
166
|
+
5. After all steps complete, output a merged Multi-Agent Execution Report
|
|
167
|
+
|
|
168
|
+
**Per-step output format:**
|
|
169
|
+
|
|
170
|
+
\`\`\`
|
|
171
|
+
<details>
|
|
172
|
+
<summary>✅ Step N: [Agent Name] · [Skill Name] — Completed</summary>
|
|
173
|
+
|
|
174
|
+
[Full step output]
|
|
175
|
+
|
|
176
|
+
</details>
|
|
177
|
+
\`\`\``;
|
|
178
|
+
const stepsSection = skills.map((skill, i) => {
|
|
179
|
+
const stepNum = i + 1;
|
|
180
|
+
const agent = resolveAgent(skill.id, agents, routing);
|
|
181
|
+
const nextAgent = i < skills.length - 1 ? resolveAgent(skills[i + 1].id, agents, routing) : null;
|
|
182
|
+
const handoffFrom = i > 0 ? resolveAgent(skills[i - 1].id, agents, routing) : null;
|
|
183
|
+
const handoffNote = handoffFrom && handoffFrom.id !== agent.id
|
|
184
|
+
? `> 📨 **Handoff Summary** (from ${handoffFrom.emoji} ${handoffFrom.nameEn}): \`[Previous step's key output — filled by prior Agent]\`\n\n`
|
|
185
|
+
: i > 0
|
|
186
|
+
? `> 📝 **Prior Output** (${agent.emoji} ${agent.nameEn} continues): Use Step ${i} core output as background.\n\n`
|
|
187
|
+
: '';
|
|
188
|
+
const stepsContent = skill.steps
|
|
189
|
+
.map((s, si) => {
|
|
190
|
+
const title = s.title.replace(/^\d+[.、]\s*/, '');
|
|
191
|
+
const indented = s.content.split('\n').map((l) => ` ${l}`).join('\n');
|
|
192
|
+
return ` ${si + 1}. **${title}**\n${indented}`;
|
|
193
|
+
})
|
|
194
|
+
.join('\n\n');
|
|
195
|
+
const handoffTo = nextAgent && nextAgent.id !== agent.id
|
|
196
|
+
? `\n\n#### ✅ Handoff → ${nextAgent.emoji} ${nextAgent.nameEn}\n\n> 🔖 Output core outcome (≤150 words) for **${nextAgent.nameEn}**:\n> \`[Fill in: what was completed, key decisions, notes for next Agent]\``
|
|
197
|
+
: '';
|
|
198
|
+
return `### ${agent.emoji} ${agent.nameEn} — Step ${stepNum}/${total}: ${skill.nameEn.replace(/_/g, ' ')}
|
|
199
|
+
|
|
200
|
+
${handoffNote}**Goal**: ${skill.descriptionEn ?? skill.description}
|
|
201
|
+
|
|
202
|
+
**Execution**:
|
|
203
|
+
|
|
204
|
+
${stepsContent}
|
|
205
|
+
|
|
206
|
+
**Output Format**: ${skill.outputFormat}${handoffTo}`;
|
|
207
|
+
}).join('\n\n---\n\n');
|
|
208
|
+
const reportTemplate = buildEnReportTemplate(pipeline, skills, agents, participatingAgents, routing, context);
|
|
209
|
+
const startCmd = `**Begin: ${participatingAgents[0].emoji} ${participatingAgents[0].nameEn} executes Step 1 now.**`;
|
|
210
|
+
const snapshotBlock = snapshot ?? '';
|
|
211
|
+
return [header, snapshotBlock, '---', roster, '---', protocol, '---', `## 🔄 Execution Flow`, stepsSection, '---', reportTemplate, startCmd]
|
|
212
|
+
.filter(Boolean)
|
|
213
|
+
.join('\n\n');
|
|
214
|
+
}
|
|
215
|
+
function buildEnReportTemplate(pipeline, skills, agents, participatingAgents, routing, context) {
|
|
216
|
+
const agentSections = participatingAgents.map((agent) => {
|
|
217
|
+
const agentSkills = skills.filter((s) => {
|
|
218
|
+
const a = resolveAgent(s.id, agents, routing);
|
|
219
|
+
return a.id === agent.id;
|
|
220
|
+
});
|
|
221
|
+
const skillList = agentSkills.map((s) => `### ${s.nameEn.replace(/_/g, ' ')}\n[Full output]\n\n---`).join('\n\n');
|
|
222
|
+
return `## ${agent.emoji} ${agent.nameEn} Output\n\n${skillList}`;
|
|
223
|
+
}).join('\n\n');
|
|
224
|
+
return `## 📄 Final Output Requirements
|
|
225
|
+
|
|
226
|
+
After all steps complete, output this Multi-Agent Execution Report:
|
|
227
|
+
|
|
228
|
+
# Multi-Agent Execution Report — ${context}
|
|
229
|
+
> Pipeline: ${pipeline.name} | Agents: ${participatingAgents.map((a) => `${a.emoji} ${a.nameEn}`).join(' → ')}
|
|
230
|
+
|
|
231
|
+
${agentSections}
|
|
232
|
+
|
|
233
|
+
*Auto-generated by Ethan Multi-Agent*`;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 构建 agentId → 步骤序号列表 的映射,用于 Agent 阵容表展示
|
|
237
|
+
*/
|
|
238
|
+
function buildAgentStepMap(skills, agents, routing, _isEn) {
|
|
239
|
+
const map = {};
|
|
240
|
+
skills.forEach((skill, i) => {
|
|
241
|
+
const agent = resolveAgent(skill.id, agents, routing);
|
|
242
|
+
if (!map[agent.id])
|
|
243
|
+
map[agent.id] = [];
|
|
244
|
+
map[agent.id].push(i + 1);
|
|
245
|
+
});
|
|
246
|
+
return map;
|
|
247
|
+
}
|
|
248
|
+
// ─── Parallel 模式 ────────────────────────────────────────────────────────────
|
|
249
|
+
function buildZhParallelPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total) {
|
|
250
|
+
const agentStepMap = buildAgentStepMap(skills, agents, routing, false);
|
|
251
|
+
const rosterRows = participatingAgents.map((a) => {
|
|
252
|
+
const steps = agentStepMap[a.id] || [];
|
|
253
|
+
return `| ${a.emoji} **${a.name}** | ${a.role} | 步骤 ${steps.join('、')} |`;
|
|
254
|
+
}).join('\n');
|
|
255
|
+
const header = `# 🤖 Multi-Agent 并行分析:${pipeline.name}
|
|
256
|
+
|
|
257
|
+
**任务背景**:${context}
|
|
258
|
+
**Pipeline**:${pipeline.name}(\`${pipeline.id}\`)
|
|
259
|
+
**执行模式**:🔀 Parallel — 各 Agent 并行输出,最后汇总整合
|
|
260
|
+
**参与 Agents**:${participatingAgents.length} 个 | **总步数**:${total} 步
|
|
261
|
+
|
|
262
|
+
## 🎭 Agent 阵容
|
|
263
|
+
|
|
264
|
+
| Agent | 职责 | 负责步骤 |
|
|
265
|
+
|-------|------|---------|
|
|
266
|
+
${rosterRows}`;
|
|
267
|
+
const protocol = `## 📋 并行协议(必须严格遵守)
|
|
268
|
+
|
|
269
|
+
1. 本次采用 **并行模式**:所有 Agent 基于同一任务背景,**同时**从各自专业视角输出分析
|
|
270
|
+
2. 每个 Agent 输出完成后,进入"🏛️ 汇总整合"阶段
|
|
271
|
+
3. 汇总整合阶段负责合并所有 Agent 产出,提炼出统一结论与行动项
|
|
272
|
+
4. 用 \`<details>\` 折叠各 Agent 的详细输出,最终报告呈现汇总结论
|
|
273
|
+
|
|
274
|
+
**输出格式:**
|
|
275
|
+
\`\`\`
|
|
276
|
+
<details>
|
|
277
|
+
<summary>✅ [Agent名] 分析完成</summary>
|
|
278
|
+
[Agent 完整输出]
|
|
279
|
+
</details>
|
|
280
|
+
\`\`\``;
|
|
281
|
+
const taskDesc = `## 📢 任务发布(全体 Agent 同时接收)
|
|
282
|
+
|
|
283
|
+
> **任务**:${context}
|
|
284
|
+
> **需要从以下维度并行分析并给出产出:**`;
|
|
285
|
+
const agentBlocks = participatingAgents.map((agent) => {
|
|
286
|
+
const agentSkills = skills.filter((s) => resolveAgent(s.id, agents, routing).id === agent.id);
|
|
287
|
+
const skillNames = agentSkills.map((s) => s.name).join('、');
|
|
288
|
+
const stepsContent = agentSkills.map((skill) => {
|
|
289
|
+
const stepsList = skill.steps.map((s, si) => {
|
|
290
|
+
const title = s.title.replace(/^\d+[.、]\s*/, '');
|
|
291
|
+
const indented = s.content.split('\n').map((l) => ` ${l}`).join('\n');
|
|
292
|
+
return ` ${si + 1}. **${title}**\n${indented}`;
|
|
293
|
+
}).join('\n\n');
|
|
294
|
+
return `#### ${skill.name}\n${stepsList}\n**输出格式**:${skill.outputFormat}`;
|
|
295
|
+
}).join('\n\n---\n\n');
|
|
296
|
+
return `### ${agent.emoji} ${agent.name} — 并行分析(${skillNames})
|
|
297
|
+
|
|
298
|
+
> 从 **${agent.role}** 视角,对任务进行全面分析:
|
|
299
|
+
|
|
300
|
+
${stepsContent}`;
|
|
301
|
+
}).join('\n\n---\n\n');
|
|
302
|
+
const synthesis = `## 🏛️ 汇总整合阶段
|
|
303
|
+
|
|
304
|
+
> 所有 Agent 输出完成后,进行以下整合:
|
|
305
|
+
|
|
306
|
+
1. **共识点**:各 Agent 分析中的一致结论
|
|
307
|
+
2. **分歧与补充**:各 Agent 的独特见解与专项建议
|
|
308
|
+
3. **行动项**(优先级排序):
|
|
309
|
+
- 🔴 立即行动
|
|
310
|
+
- 🟡 近期计划
|
|
311
|
+
- 🟢 长期优化
|
|
312
|
+
4. **最终建议**:综合各角度的最优方案
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
# 并行分析报告 — ${context}
|
|
317
|
+
> Pipeline:${pipeline.name} | 模式:Parallel | Agents:${participatingAgents.map((a) => `${a.emoji} ${a.name}`).join(' + ')}
|
|
318
|
+
|
|
319
|
+
## 汇总结论
|
|
320
|
+
[由上述各 Agent 分析综合得出]
|
|
321
|
+
|
|
322
|
+
*由 Ethan Multi-Agent Parallel 自动生成*`;
|
|
323
|
+
return [header, snapshot ?? '', '---', protocol, '---', taskDesc, agentBlocks, '---', synthesis]
|
|
324
|
+
.filter(Boolean)
|
|
325
|
+
.join('\n\n');
|
|
326
|
+
}
|
|
327
|
+
function buildEnParallelPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total) {
|
|
328
|
+
const agentStepMap = buildAgentStepMap(skills, agents, routing, true);
|
|
329
|
+
const rosterRows = participatingAgents.map((a) => {
|
|
330
|
+
const steps = agentStepMap[a.id] || [];
|
|
331
|
+
return `| ${a.emoji} **${a.nameEn}** | ${a.role} | Steps ${steps.join(', ')} |`;
|
|
332
|
+
}).join('\n');
|
|
333
|
+
const header = `# 🤖 Multi-Agent Parallel Analysis: ${pipeline.name}
|
|
334
|
+
|
|
335
|
+
**Context**: ${context}
|
|
336
|
+
**Pipeline**: ${pipeline.name} (\`${pipeline.id}\`)
|
|
337
|
+
**Mode**: 🔀 Parallel — All agents analyze simultaneously, then synthesize
|
|
338
|
+
**Agents**: ${participatingAgents.length} | **Steps**: ${total}
|
|
339
|
+
|
|
340
|
+
## 🎭 Agent Roster
|
|
341
|
+
|
|
342
|
+
| Agent | Role | Assigned Steps |
|
|
343
|
+
|-------|------|----------------|
|
|
344
|
+
${rosterRows}`;
|
|
345
|
+
const agentBlocks = participatingAgents.map((agent) => {
|
|
346
|
+
const agentSkills = skills.filter((s) => resolveAgent(s.id, agents, routing).id === agent.id);
|
|
347
|
+
const skillNames = agentSkills.map((s) => s.nameEn.replace(/_/g, ' ')).join(', ');
|
|
348
|
+
const stepsContent = agentSkills.map((skill) => {
|
|
349
|
+
const stepsList = skill.steps.map((s, si) => {
|
|
350
|
+
const title = s.title.replace(/^\d+[.、]\s*/, '');
|
|
351
|
+
const indented = s.content.split('\n').map((l) => ` ${l}`).join('\n');
|
|
352
|
+
return ` ${si + 1}. **${title}**\n${indented}`;
|
|
353
|
+
}).join('\n\n');
|
|
354
|
+
return `#### ${skill.nameEn.replace(/_/g, ' ')}\n${stepsList}\n**Output Format**: ${skill.outputFormat}`;
|
|
355
|
+
}).join('\n\n---\n\n');
|
|
356
|
+
return `### ${agent.emoji} ${agent.nameEn} — Parallel Analysis (${skillNames})
|
|
357
|
+
|
|
358
|
+
${stepsContent}`;
|
|
359
|
+
}).join('\n\n---\n\n');
|
|
360
|
+
const synthesis = `## 🏛️ Synthesis Phase
|
|
361
|
+
|
|
362
|
+
1. **Consensus**: Agreed conclusions from all agents
|
|
363
|
+
2. **Divergence & Additions**: Unique insights per agent
|
|
364
|
+
3. **Action Items** (prioritized):
|
|
365
|
+
- 🔴 Immediate
|
|
366
|
+
- 🟡 Near-term
|
|
367
|
+
- 🟢 Long-term
|
|
368
|
+
4. **Final Recommendation**: Optimal approach from all angles
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
# Parallel Analysis Report — ${context}
|
|
373
|
+
> Pipeline: ${pipeline.name} | Mode: Parallel | Agents: ${participatingAgents.map((a) => `${a.emoji} ${a.nameEn}`).join(' + ')}
|
|
374
|
+
|
|
375
|
+
*Auto-generated by Ethan Multi-Agent Parallel*`;
|
|
376
|
+
return [header, snapshot ?? '', '---', `## 📢 Task Broadcast\n\n> **Task**: ${context}\n> All agents analyze in parallel from their domain.`, agentBlocks, '---', synthesis]
|
|
377
|
+
.filter(Boolean)
|
|
378
|
+
.join('\n\n');
|
|
379
|
+
}
|
|
380
|
+
// ─── Review-Loop 模式 ─────────────────────────────────────────────────────────
|
|
381
|
+
function buildZhReviewLoopPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total) {
|
|
382
|
+
const coderAgent = agents.find((a) => a.id === 'coder') ?? participatingAgents[0];
|
|
383
|
+
const reviewerAgent = agents.find((a) => a.id === 'reviewer') ?? agents.find((a) => a.id === 'qa') ?? participatingAgents[participatingAgents.length - 1];
|
|
384
|
+
const header = `# 🤖 Multi-Agent Review-Loop:${pipeline.name}
|
|
385
|
+
|
|
386
|
+
**任务背景**:${context}
|
|
387
|
+
**Pipeline**:${pipeline.name}(\`${pipeline.id}\`)
|
|
388
|
+
**执行模式**:🔄 Review-Loop — 实现 → 审查 → 修改(最多 2 轮迭代)
|
|
389
|
+
**核心 Agents**:${coderAgent.emoji} ${coderAgent.name}(执行)+ ${reviewerAgent.emoji} ${reviewerAgent.name}(审查)
|
|
390
|
+
**总步数**:${total} 步`;
|
|
391
|
+
const protocol = `## 📋 Review-Loop 协议(必须严格遵守)
|
|
392
|
+
|
|
393
|
+
1. **Loop 1 — 实现阶段**:${coderAgent.emoji} ${coderAgent.name} 按步骤执行全部任务
|
|
394
|
+
2. **Loop 1 — 审查阶段**:${reviewerAgent.emoji} ${reviewerAgent.name} 对实现结果进行全面审查
|
|
395
|
+
- 若审查通过(无 Blocker)→ 直接输出最终报告
|
|
396
|
+
- 若有 Blocker/Major 问题 → 进入 Loop 2
|
|
397
|
+
3. **Loop 2 — 修改阶段**:${coderAgent.emoji} ${coderAgent.name} 仅修复审查指出的问题
|
|
398
|
+
4. **Loop 2 — 确认阶段**:${reviewerAgent.emoji} ${reviewerAgent.name} 确认修复,输出最终报告
|
|
399
|
+
5. **最多 2 轮迭代**,第 2 轮结束后无论如何输出最终报告`;
|
|
400
|
+
const stepsContent = skills.map((skill, i) => {
|
|
401
|
+
const stepNum = i + 1;
|
|
402
|
+
const stepsList = skill.steps.map((s, si) => {
|
|
403
|
+
const title = s.title.replace(/^\d+[.、]\s*/, '');
|
|
404
|
+
const indented = s.content.split('\n').map((l) => ` ${l}`).join('\n');
|
|
405
|
+
return ` ${si + 1}. **${title}**\n${indented}`;
|
|
406
|
+
}).join('\n\n');
|
|
407
|
+
return `#### 步骤 ${stepNum}:${skill.name}\n${stepsList}\n**输出格式**:${skill.outputFormat}`;
|
|
408
|
+
}).join('\n\n---\n\n');
|
|
409
|
+
const loop1 = `## 🔄 Loop 1 — 实现阶段
|
|
410
|
+
|
|
411
|
+
${coderAgent.emoji} **${coderAgent.name}** 按顺序执行以下所有步骤:
|
|
412
|
+
|
|
413
|
+
${stepsContent}
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## 🔄 Loop 1 — 审查阶段
|
|
418
|
+
|
|
419
|
+
${reviewerAgent.emoji} **${reviewerAgent.name}** 对上述全部实现进行审查:
|
|
420
|
+
|
|
421
|
+
1. 检查每个步骤的输出是否满足任务目标
|
|
422
|
+
2. 识别 🚫 Blocker / ⚠️ Major / 💡 Minor 问题
|
|
423
|
+
3. 评估整体质量与完整性
|
|
424
|
+
|
|
425
|
+
**审查结论:**
|
|
426
|
+
- [ ] ✅ 通过(无 Blocker/Major)→ 跳过 Loop 2,直接输出最终报告
|
|
427
|
+
- [ ] ⚠️ 需要修改(有 Blocker/Major)→ 进入 Loop 2
|
|
428
|
+
|
|
429
|
+
**问题清单(如有):**
|
|
430
|
+
| 问题级别 | 步骤 | 问题描述 | 修改建议 |
|
|
431
|
+
|---------|------|---------|---------|
|
|
432
|
+
| [级别] | [步骤N] | [描述] | [建议] |`;
|
|
433
|
+
const loop2 = `## 🔄 Loop 2 — 修改阶段(仅在 Loop 1 审查不通过时执行)
|
|
434
|
+
|
|
435
|
+
${coderAgent.emoji} **${coderAgent.name}** 根据审查意见,**仅修复 Blocker 和 Major 问题**:
|
|
436
|
+
|
|
437
|
+
> 📨 **审查意见摘要**(来自 ${reviewerAgent.emoji} ${reviewerAgent.name}):\`[Loop 1 审查问题列表]\`
|
|
438
|
+
|
|
439
|
+
针对每个 Blocker/Major 问题输出修改内容。
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## 🔄 Loop 2 — 确认阶段
|
|
444
|
+
|
|
445
|
+
${reviewerAgent.emoji} **${reviewerAgent.name}** 确认修复是否满足要求:
|
|
446
|
+
|
|
447
|
+
- [ ] ✅ 修复通过 → 输出最终报告
|
|
448
|
+
- [ ] ❌ 仍有问题 → 记录遗留问题,强制输出最终报告(已达迭代上限)`;
|
|
449
|
+
const finalReport = `## 📄 最终输出
|
|
450
|
+
|
|
451
|
+
# Review-Loop 执行报告 — ${context}
|
|
452
|
+
> Pipeline:${pipeline.name} | 模式:Review-Loop | Agents:${coderAgent.emoji} ${coderAgent.name} ⇄ ${reviewerAgent.emoji} ${reviewerAgent.name}
|
|
453
|
+
|
|
454
|
+
## 最终实现产出
|
|
455
|
+
[Loop 1 或 Loop 2 修改后的完整输出]
|
|
456
|
+
|
|
457
|
+
## 审查摘要
|
|
458
|
+
- 发现问题:[Blocker X 个 | Major Y 个 | Minor Z 个]
|
|
459
|
+
- 已修复:[描述]
|
|
460
|
+
- 遗留(如有):[描述]
|
|
461
|
+
|
|
462
|
+
*由 Ethan Multi-Agent Review-Loop 自动生成*`;
|
|
463
|
+
return [header, snapshot ?? '', '---', protocol, '---', loop1, '---', loop2, '---', finalReport]
|
|
464
|
+
.filter(Boolean)
|
|
465
|
+
.join('\n\n');
|
|
466
|
+
}
|
|
467
|
+
function buildEnReviewLoopPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total) {
|
|
468
|
+
const coderAgent = agents.find((a) => a.id === 'coder') ?? participatingAgents[0];
|
|
469
|
+
const reviewerAgent = agents.find((a) => a.id === 'reviewer') ?? agents.find((a) => a.id === 'qa') ?? participatingAgents[participatingAgents.length - 1];
|
|
470
|
+
const header = `# 🤖 Multi-Agent Review-Loop: ${pipeline.name}
|
|
471
|
+
|
|
472
|
+
**Context**: ${context}
|
|
473
|
+
**Pipeline**: ${pipeline.name} (\`${pipeline.id}\`)
|
|
474
|
+
**Mode**: 🔄 Review-Loop — Implement → Review → Fix (max 2 iterations)
|
|
475
|
+
**Core Agents**: ${coderAgent.emoji} ${coderAgent.nameEn} (execute) + ${reviewerAgent.emoji} ${reviewerAgent.nameEn} (review)
|
|
476
|
+
**Steps**: ${total}`;
|
|
477
|
+
const stepsContent = skills.map((skill, i) => {
|
|
478
|
+
const stepNum = i + 1;
|
|
479
|
+
const stepsList = skill.steps.map((s, si) => {
|
|
480
|
+
const title = s.title.replace(/^\d+[.、]\s*/, '');
|
|
481
|
+
const indented = s.content.split('\n').map((l) => ` ${l}`).join('\n');
|
|
482
|
+
return ` ${si + 1}. **${title}**\n${indented}`;
|
|
483
|
+
}).join('\n\n');
|
|
484
|
+
return `#### Step ${stepNum}: ${skill.nameEn.replace(/_/g, ' ')}\n${stepsList}\n**Output Format**: ${skill.outputFormat}`;
|
|
485
|
+
}).join('\n\n---\n\n');
|
|
486
|
+
const loop1 = `## 🔄 Loop 1 — Implementation
|
|
487
|
+
|
|
488
|
+
${coderAgent.emoji} **${coderAgent.nameEn}** executes all steps in order:
|
|
489
|
+
|
|
490
|
+
${stepsContent}
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## 🔄 Loop 1 — Review
|
|
495
|
+
|
|
496
|
+
${reviewerAgent.emoji} **${reviewerAgent.nameEn}** reviews all outputs:
|
|
497
|
+
|
|
498
|
+
- [ ] ✅ Pass (no Blockers/Major) → Skip Loop 2, output final report
|
|
499
|
+
- [ ] ⚠️ Issues found → Enter Loop 2`;
|
|
500
|
+
const loop2 = `## 🔄 Loop 2 — Fix (only if Loop 1 review failed)
|
|
501
|
+
|
|
502
|
+
${coderAgent.emoji} **${coderAgent.nameEn}** fixes Blocker and Major issues only.
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## 🔄 Loop 2 — Confirm
|
|
507
|
+
|
|
508
|
+
${reviewerAgent.emoji} **${reviewerAgent.nameEn}** confirms fixes, then outputs final report.`;
|
|
509
|
+
const finalReport = `## 📄 Final Output
|
|
510
|
+
|
|
511
|
+
# Review-Loop Report — ${context}
|
|
512
|
+
> Pipeline: ${pipeline.name} | Mode: Review-Loop | Agents: ${coderAgent.emoji} ${coderAgent.nameEn} ⇄ ${reviewerAgent.emoji} ${reviewerAgent.nameEn}
|
|
513
|
+
|
|
514
|
+
*Auto-generated by Ethan Multi-Agent Review-Loop*`;
|
|
515
|
+
return [header, snapshot ?? '', '---', loop1, '---', loop2, '---', finalReport]
|
|
516
|
+
.filter(Boolean)
|
|
517
|
+
.join('\n\n');
|
|
518
|
+
}
|
|
519
|
+
// ─── Consensus 模式 ───────────────────────────────────────────────────────────
|
|
520
|
+
function buildZhConsensusPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total) {
|
|
521
|
+
const agentStepMap = buildAgentStepMap(skills, agents, routing, false);
|
|
522
|
+
const rosterRows = participatingAgents.map((a) => {
|
|
523
|
+
const steps = agentStepMap[a.id] || [];
|
|
524
|
+
return `| ${a.emoji} **${a.name}** | ${a.role} | 步骤 ${steps.join('、')} |`;
|
|
525
|
+
}).join('\n');
|
|
526
|
+
const header = `# 🤖 Multi-Agent 共识决策:${pipeline.name}
|
|
527
|
+
|
|
528
|
+
**任务背景**:${context}
|
|
529
|
+
**Pipeline**:${pipeline.name}(\`${pipeline.id}\`)
|
|
530
|
+
**执行模式**:🏛️ Consensus — 各 Agent 独立提案,共识整合最优解
|
|
531
|
+
**参与 Agents**:${participatingAgents.length} 个 | **总步数**:${total} 步
|
|
532
|
+
|
|
533
|
+
## 🎭 Agent 阵容
|
|
534
|
+
|
|
535
|
+
| Agent | 职责 | 负责步骤 |
|
|
536
|
+
|-------|------|---------|
|
|
537
|
+
${rosterRows}`;
|
|
538
|
+
const protocol = `## 📋 共识协议(必须严格遵守)
|
|
539
|
+
|
|
540
|
+
1. **提案阶段**:每个 Agent 从自己的视角,**独立**地为任务给出完整方案
|
|
541
|
+
2. Agent 之间**互不干扰**,不参考其他 Agent 的方案
|
|
542
|
+
3. **共识阶段**:比较所有提案,提炼共识、识别分歧
|
|
543
|
+
4. **最终推荐**:基于共识,输出最优综合方案与明确行动计划`;
|
|
544
|
+
const proposalBlocks = participatingAgents.map((agent, idx) => {
|
|
545
|
+
const agentSkills = skills.filter((s) => resolveAgent(s.id, agents, routing).id === agent.id);
|
|
546
|
+
const stepsContent = agentSkills.map((skill) => {
|
|
547
|
+
const stepsList = skill.steps.map((s, si) => {
|
|
548
|
+
const title = s.title.replace(/^\d+[.、]\s*/, '');
|
|
549
|
+
const indented = s.content.split('\n').map((l) => ` ${l}`).join('\n');
|
|
550
|
+
return ` ${si + 1}. **${title}**\n${indented}`;
|
|
551
|
+
}).join('\n\n');
|
|
552
|
+
return `#### ${skill.name}\n${stepsList}\n**输出格式**:${skill.outputFormat}`;
|
|
553
|
+
}).join('\n\n---\n\n');
|
|
554
|
+
return `### 提案 ${idx + 1}:${agent.emoji} ${agent.name}
|
|
555
|
+
|
|
556
|
+
> 以下是 **${agent.name}** 基于 **${agent.role}** 视角的独立方案:
|
|
557
|
+
|
|
558
|
+
${stepsContent}`;
|
|
559
|
+
}).join('\n\n---\n\n');
|
|
560
|
+
const consensus = `## 🏛️ 共识整合阶段
|
|
561
|
+
|
|
562
|
+
> 所有提案提交完毕,现在进行共识分析:
|
|
563
|
+
|
|
564
|
+
### 1. 共识点(各方认同的结论)
|
|
565
|
+
[列出各 Agent 方案中一致的内容]
|
|
566
|
+
|
|
567
|
+
### 2. 分歧点(不同视角的差异)
|
|
568
|
+
| 维度 | ${participatingAgents.map((a) => a.name).join(' | ')} | 分析 |
|
|
569
|
+
|------|${participatingAgents.map(() => '---').join('|')}|------|
|
|
570
|
+
| [方面] | [各 Agent 观点] | [如何取舍] |
|
|
571
|
+
|
|
572
|
+
### 3. 最终推荐方案
|
|
573
|
+
基于共识整合,推荐以下最优方案:
|
|
574
|
+
|
|
575
|
+
[综合各 Agent 优势的最终方案]
|
|
576
|
+
|
|
577
|
+
### 4. 行动计划
|
|
578
|
+
| 优先级 | 行动项 | 负责方 |
|
|
579
|
+
|--------|--------|--------|
|
|
580
|
+
| 🔴 P0 | [立即执行] | [Agent] |
|
|
581
|
+
| 🟡 P1 | [近期计划] | [Agent] |
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
# 共识决策报告 — ${context}
|
|
586
|
+
> Pipeline:${pipeline.name} | 模式:Consensus | Agents:${participatingAgents.map((a) => `${a.emoji} ${a.name}`).join(' + ')}
|
|
587
|
+
|
|
588
|
+
*由 Ethan Multi-Agent Consensus 自动生成*`;
|
|
589
|
+
return [header, snapshot ?? '', '---', protocol, '---', `## 📝 提案阶段(各 Agent 独立输出)`, proposalBlocks, '---', consensus]
|
|
590
|
+
.filter(Boolean)
|
|
591
|
+
.join('\n\n');
|
|
592
|
+
}
|
|
593
|
+
function buildEnConsensusPrompt(pipeline, skills, agents, participatingAgents, routing, context, snapshot, total) {
|
|
594
|
+
const agentStepMap = buildAgentStepMap(skills, agents, routing, true);
|
|
595
|
+
const rosterRows = participatingAgents.map((a) => {
|
|
596
|
+
const steps = agentStepMap[a.id] || [];
|
|
597
|
+
return `| ${a.emoji} **${a.nameEn}** | ${a.role} | Steps ${steps.join(', ')} |`;
|
|
598
|
+
}).join('\n');
|
|
599
|
+
const header = `# 🤖 Multi-Agent Consensus: ${pipeline.name}
|
|
600
|
+
|
|
601
|
+
**Context**: ${context}
|
|
602
|
+
**Pipeline**: ${pipeline.name} (\`${pipeline.id}\`)
|
|
603
|
+
**Mode**: 🏛️ Consensus — Each agent proposes independently, then synthesize
|
|
604
|
+
**Agents**: ${participatingAgents.length} | **Steps**: ${total}
|
|
605
|
+
|
|
606
|
+
## 🎭 Agent Roster
|
|
607
|
+
|
|
608
|
+
| Agent | Role | Assigned Steps |
|
|
609
|
+
|-------|------|----------------|
|
|
610
|
+
${rosterRows}`;
|
|
611
|
+
const proposalBlocks = participatingAgents.map((agent, idx) => {
|
|
612
|
+
const agentSkills = skills.filter((s) => resolveAgent(s.id, agents, routing).id === agent.id);
|
|
613
|
+
const stepsContent = agentSkills.map((skill) => {
|
|
614
|
+
const stepsList = skill.steps.map((s, si) => {
|
|
615
|
+
const title = s.title.replace(/^\d+[.、]\s*/, '');
|
|
616
|
+
const indented = s.content.split('\n').map((l) => ` ${l}`).join('\n');
|
|
617
|
+
return ` ${si + 1}. **${title}**\n${indented}`;
|
|
618
|
+
}).join('\n\n');
|
|
619
|
+
return `#### ${skill.nameEn.replace(/_/g, ' ')}\n${stepsList}\n**Output Format**: ${skill.outputFormat}`;
|
|
620
|
+
}).join('\n\n---\n\n');
|
|
621
|
+
return `### Proposal ${idx + 1}: ${agent.emoji} ${agent.nameEn}
|
|
622
|
+
|
|
623
|
+
> Independent proposal from **${agent.nameEn}** (${agent.role}):
|
|
624
|
+
|
|
625
|
+
${stepsContent}`;
|
|
626
|
+
}).join('\n\n---\n\n');
|
|
627
|
+
const consensus = `## 🏛️ Consensus Phase
|
|
628
|
+
|
|
629
|
+
### 1. Agreed Points
|
|
630
|
+
[Common conclusions across all proposals]
|
|
631
|
+
|
|
632
|
+
### 2. Divergences
|
|
633
|
+
| Dimension | ${participatingAgents.map((a) => a.nameEn).join(' | ')} | Resolution |
|
|
634
|
+
|-----------|${participatingAgents.map(() => '---').join('|')}|------------|
|
|
635
|
+
|
|
636
|
+
### 3. Final Recommendation
|
|
637
|
+
[Synthesized optimal approach]
|
|
638
|
+
|
|
639
|
+
### 4. Action Plan
|
|
640
|
+
| Priority | Action | Owner |
|
|
641
|
+
|----------|--------|-------|
|
|
642
|
+
| 🔴 P0 | [Immediate] | [Agent] |
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
# Consensus Report — ${context}
|
|
647
|
+
> Pipeline: ${pipeline.name} | Mode: Consensus | Agents: ${participatingAgents.map((a) => `${a.emoji} ${a.nameEn}`).join(' + ')}
|
|
648
|
+
|
|
649
|
+
*Auto-generated by Ethan Multi-Agent Consensus*`;
|
|
650
|
+
return [header, snapshot ?? '', '---', `## 📝 Proposal Phase (Each Agent Independent)`, proposalBlocks, '---', consensus]
|
|
651
|
+
.filter(Boolean)
|
|
652
|
+
.join('\n\n');
|
|
653
|
+
}
|
|
654
|
+
//# sourceMappingURL=orchestrator.js.map
|