principles-disciple 1.7.4 → 1.7.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/dist/commands/focus.js +30 -155
- package/dist/constants/diagnostician.d.ts +16 -0
- package/dist/constants/diagnostician.js +60 -0
- package/dist/constants/tools.d.ts +2 -2
- package/dist/constants/tools.js +1 -1
- package/dist/core/config.d.ts +12 -0
- package/dist/core/config.js +7 -0
- package/dist/core/evolution-engine.js +1 -1
- package/dist/core/focus-history.d.ts +92 -0
- package/dist/core/focus-history.js +490 -0
- package/dist/core/init.js +2 -2
- package/dist/core/profile.js +1 -1
- package/dist/hooks/gate.js +3 -3
- package/dist/hooks/prompt.js +73 -22
- package/dist/hooks/subagent.js +1 -2
- package/dist/http/principles-console-route.d.ts +7 -0
- package/dist/http/principles-console-route.js +243 -1
- package/dist/index.js +0 -2
- package/dist/service/central-database.d.ts +104 -0
- package/dist/service/central-database.js +648 -0
- package/dist/service/evolution-worker.js +3 -3
- package/dist/tools/deep-reflect.js +1 -2
- package/dist/utils/subagent-probe.d.ts +11 -0
- package/dist/utils/subagent-probe.js +46 -1
- package/package.json +2 -1
- package/templates/langs/en/core/AGENTS.md +1 -1
- package/templates/langs/en/core/TOOLS.md +1 -1
- package/templates/langs/zh/core/AGENTS.md +1 -1
- package/templates/langs/zh/core/TOOLS.md +1 -1
- package/{agents/auditor.md → templates/langs/zh/skills/pd-auditor/SKILL.md} +3 -3
- package/{agents/diagnostician.md → templates/langs/zh/skills/pd-diagnostician/SKILL.md} +39 -29
- package/{agents/explorer.md → templates/langs/zh/skills/pd-explorer/SKILL.md} +3 -3
- package/{agents/implementer.md → templates/langs/zh/skills/pd-implementer/SKILL.md} +3 -3
- package/{agents/planner.md → templates/langs/zh/skills/pd-planner/SKILL.md} +3 -3
- package/{agents/reporter.md → templates/langs/zh/skills/pd-reporter/SKILL.md} +3 -3
- package/{agents/reviewer.md → templates/langs/zh/skills/pd-reviewer/SKILL.md} +3 -3
- package/dist/core/agent-loader.d.ts +0 -44
- package/dist/core/agent-loader.js +0 -147
- package/dist/tools/agent-spawn.d.ts +0 -54
- package/dist/tools/agent-spawn.js +0 -456
|
@@ -1,456 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Spawn Tool
|
|
3
|
-
*
|
|
4
|
-
* Provides a tool for spawning subagents with predefined agent definitions.
|
|
5
|
-
* Uses the low-level OpenClaw Subagent API.
|
|
6
|
-
*/
|
|
7
|
-
import { Type } from '@sinclair/typebox';
|
|
8
|
-
import * as fs from 'fs';
|
|
9
|
-
import { randomUUID } from 'node:crypto';
|
|
10
|
-
import { loadAgentDefinition, listAvailableAgents } from '../core/agent-loader.js';
|
|
11
|
-
import { resolvePdPath } from '../core/paths.js';
|
|
12
|
-
import { extractEvolutionTaskId, registerEvolutionTaskSession } from '../service/evolution-worker.js';
|
|
13
|
-
import { isSubagentRuntimeAvailable } from '../utils/subagent-probe.js';
|
|
14
|
-
/**
|
|
15
|
-
* Extract assistant text from session messages
|
|
16
|
-
*/
|
|
17
|
-
function extractAssistantText(messages) {
|
|
18
|
-
if (!messages || typeof messages !== 'object')
|
|
19
|
-
return '';
|
|
20
|
-
const m = messages;
|
|
21
|
-
// Try assistantTexts helper first
|
|
22
|
-
if (m.assistantTexts && Array.isArray(m.assistantTexts)) {
|
|
23
|
-
return m.assistantTexts.join('\n');
|
|
24
|
-
}
|
|
25
|
-
// Fall back to parsing messages array
|
|
26
|
-
if (m.messages && Array.isArray(m.messages)) {
|
|
27
|
-
// Find the last assistant message
|
|
28
|
-
for (let i = m.messages.length - 1; i >= 0; i--) {
|
|
29
|
-
const msg = m.messages[i];
|
|
30
|
-
if (msg.role === 'assistant' && msg.content) {
|
|
31
|
-
if (typeof msg.content === 'string') {
|
|
32
|
-
return msg.content;
|
|
33
|
-
}
|
|
34
|
-
if (Array.isArray(msg.content)) {
|
|
35
|
-
const textParts = msg.content
|
|
36
|
-
.filter((c) => c.type === 'text' && c.text)
|
|
37
|
-
.map((c) => c.text);
|
|
38
|
-
if (textParts.length > 0) {
|
|
39
|
-
return textParts.join('\n');
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return '';
|
|
46
|
-
}
|
|
47
|
-
async function registerDiagnosticianRun(api, task, sessionKey) {
|
|
48
|
-
const taskId = extractEvolutionTaskId(task);
|
|
49
|
-
if (!taskId)
|
|
50
|
-
return;
|
|
51
|
-
try {
|
|
52
|
-
const workspaceDir = api.resolvePath('.');
|
|
53
|
-
const queuePath = resolvePdPath(workspaceDir, 'EVOLUTION_QUEUE');
|
|
54
|
-
if (!fs.existsSync(queuePath)) {
|
|
55
|
-
api.logger?.warn?.(`[PD:AgentSpawn] Evolution task ${taskId} not registered because queue file is missing`);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
await registerEvolutionTaskSession((key) => resolvePdPath(workspaceDir, key), taskId, sessionKey, api.logger);
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
api.logger?.warn?.(`[PD:AgentSpawn] Failed to register evolution task session: ${String(error)}`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Build the full system prompt for a subagent
|
|
66
|
-
* Combines the agent definition with any context-specific additions
|
|
67
|
-
*/
|
|
68
|
-
function buildSubagentSystemPrompt(agentDef, _task) {
|
|
69
|
-
// The systemPrompt from the agent definition is the main content
|
|
70
|
-
// It will be appended to OpenClaw's minimal subagent prompt
|
|
71
|
-
return agentDef.systemPrompt;
|
|
72
|
-
}
|
|
73
|
-
const INTERNAL_AGENT_USAGE_GUIDANCE = 'pd_run_worker is only for Principles Disciple internal workers. ' +
|
|
74
|
-
'Use agents_list / sessions_list / sessions_spawn / sessions_send for peer agents or cross-session communication. ' +
|
|
75
|
-
'Use subagents to inspect already-dispatched internal workers.';
|
|
76
|
-
function buildInternalAgentUsageMessage(availableAgents) {
|
|
77
|
-
return [
|
|
78
|
-
'pd_run_worker is reserved for Principles Disciple internal workers.',
|
|
79
|
-
`Allowed internal roles: ${availableAgents.join(', ')}`,
|
|
80
|
-
'Use `agents_list` to discover peer agent ids.',
|
|
81
|
-
'Use `sessions_spawn` to create or orchestrate another peer session.',
|
|
82
|
-
'Use `sessions_list` to inspect running peer sessions.',
|
|
83
|
-
'Use `sessions_send` to talk to another existing session.',
|
|
84
|
-
'Use `subagents` to inspect already-dispatched internal workers such as diagnostician or explorer.',
|
|
85
|
-
].join('\n');
|
|
86
|
-
}
|
|
87
|
-
function looksLikeSessionOrPeerCoordinationTask(task) {
|
|
88
|
-
const normalized = task.trim().toLowerCase();
|
|
89
|
-
if (!normalized)
|
|
90
|
-
return false;
|
|
91
|
-
const explicitMarkers = [
|
|
92
|
-
'sessions_send',
|
|
93
|
-
'sessions_spawn',
|
|
94
|
-
'sessions_list',
|
|
95
|
-
'agents_list',
|
|
96
|
-
'sessionkey',
|
|
97
|
-
'session key',
|
|
98
|
-
'sessionid',
|
|
99
|
-
'session id',
|
|
100
|
-
'agentid',
|
|
101
|
-
'agent id',
|
|
102
|
-
'other session',
|
|
103
|
-
'another session',
|
|
104
|
-
'peer agent',
|
|
105
|
-
'other agent',
|
|
106
|
-
'another agent',
|
|
107
|
-
'same-level agent',
|
|
108
|
-
'same level agent',
|
|
109
|
-
'cross-session',
|
|
110
|
-
'cross session',
|
|
111
|
-
'send a message to',
|
|
112
|
-
'message another session',
|
|
113
|
-
'talk to another session',
|
|
114
|
-
'subagent',
|
|
115
|
-
'worker status',
|
|
116
|
-
'running status',
|
|
117
|
-
'still running',
|
|
118
|
-
'inspect status',
|
|
119
|
-
'check status',
|
|
120
|
-
'同级智能体',
|
|
121
|
-
'另一个智能体',
|
|
122
|
-
'其他智能体',
|
|
123
|
-
'另一个会话',
|
|
124
|
-
'其他会话',
|
|
125
|
-
'跨会话',
|
|
126
|
-
'给另一个会话发消息',
|
|
127
|
-
];
|
|
128
|
-
return explicitMarkers.some((marker) => normalized.includes(marker));
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Sanitize params for debug logging (remove sensitive values)
|
|
132
|
-
*/
|
|
133
|
-
function sanitizeParamsForLogging(params) {
|
|
134
|
-
const SENSITIVE_PATTERNS = /password|token|api[_-]?key|secret|credential|authorization|cookie|session/i;
|
|
135
|
-
const sanitized = {};
|
|
136
|
-
for (const [key, value] of Object.entries(params)) {
|
|
137
|
-
if (SENSITIVE_PATTERNS.test(key)) {
|
|
138
|
-
sanitized[key] = '[REDACTED]';
|
|
139
|
-
}
|
|
140
|
-
else if (typeof value === 'string' && value.length > 200) {
|
|
141
|
-
sanitized[key] = value.slice(0, 200) + '...[truncated]';
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
sanitized[key] = value;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return sanitized;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Helper to read string parameter from rawParams
|
|
151
|
-
* Supports both camelCase and snake_case parameter names
|
|
152
|
-
*/
|
|
153
|
-
function readStringParam(rawParams, key) {
|
|
154
|
-
const value = rawParams[key];
|
|
155
|
-
if (typeof value === 'string')
|
|
156
|
-
return value.trim() || undefined;
|
|
157
|
-
// Try snake_case alias
|
|
158
|
-
const snakeKey = key.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
159
|
-
const snakeValue = rawParams[snakeKey];
|
|
160
|
-
if (typeof snakeValue === 'string')
|
|
161
|
-
return snakeValue.trim() || undefined;
|
|
162
|
-
return undefined;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Helper to read boolean parameter from rawParams
|
|
166
|
-
* Supports both camelCase and snake_case parameter names
|
|
167
|
-
*/
|
|
168
|
-
function readBooleanParam(rawParams, key) {
|
|
169
|
-
const value = rawParams[key];
|
|
170
|
-
if (typeof value === 'boolean')
|
|
171
|
-
return value;
|
|
172
|
-
// Try snake_case alias
|
|
173
|
-
const snakeKey = key.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
174
|
-
const snakeValue = rawParams[snakeKey];
|
|
175
|
-
if (typeof snakeValue === 'boolean')
|
|
176
|
-
return snakeValue;
|
|
177
|
-
// Backward compatibility for legacy prompt guidance / callers.
|
|
178
|
-
if (key === 'runInBackground' && typeof rawParams.async === 'boolean') {
|
|
179
|
-
return rawParams.async;
|
|
180
|
-
}
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Create Agent Spawn Tool
|
|
185
|
-
*
|
|
186
|
-
* Uses factory pattern to capture `api` in closure, following OpenClaw plugin SDK conventions.
|
|
187
|
-
* The execute signature must be: async (_toolCallId: string, rawParams: Record<string, unknown>)
|
|
188
|
-
*/
|
|
189
|
-
export function createAgentSpawnTool(api) {
|
|
190
|
-
return {
|
|
191
|
-
name: 'pd_run_worker',
|
|
192
|
-
description: `启动指定类型的子智能体执行任务。
|
|
193
|
-
|
|
194
|
-
可用的智能体类型:
|
|
195
|
-
- explorer: 快速收集证据(文件、日志、复现步骤)
|
|
196
|
-
- diagnostician: 根因分析(verb/adjective + 5Whys)
|
|
197
|
-
- auditor: 演演审计(axiom/system/via-negativa)
|
|
198
|
-
- planner: 制定电影剧本计划
|
|
199
|
-
- implementer: 按计划执行代码修改
|
|
200
|
-
- reviewer: 代码审查(正确性、安全性、可维护性)
|
|
201
|
-
- reporter: 最终汇报(技术细节转管理报告)
|
|
202
|
-
|
|
203
|
-
示例调用: pd_run_worker(agentType="diagnostician", task="分析 Pain 7386ccfb 的根因")`,
|
|
204
|
-
parameters: Type.Object({
|
|
205
|
-
agentType: Type.String({
|
|
206
|
-
description: '【必填】智能体类型,必须是以下之一: explorer, diagnostician, auditor, planner, implementer, reviewer, reporter',
|
|
207
|
-
}),
|
|
208
|
-
task: Type.String({
|
|
209
|
-
description: '【必填】任务描述,告诉智能体要做什么',
|
|
210
|
-
}),
|
|
211
|
-
runInBackground: Type.Optional(Type.Boolean({
|
|
212
|
-
description: '【可选】是否后台运行。true=立即返回不等待结果,false=等待执行完成。默认 false',
|
|
213
|
-
})),
|
|
214
|
-
}),
|
|
215
|
-
/**
|
|
216
|
-
* Execution logic for the agent spawn tool
|
|
217
|
-
*
|
|
218
|
-
* OpenClaw tool execute signature:
|
|
219
|
-
* - First parameter: _toolCallId (string) - the tool call ID
|
|
220
|
-
* - Second parameter: rawParams (Record<string, unknown>) - the actual parameters
|
|
221
|
-
* - Third parameter (optional): signal (AbortSignal) - for cancellation
|
|
222
|
-
*/
|
|
223
|
-
async execute(_toolCallId, rawParams) {
|
|
224
|
-
const agentType = readStringParam(rawParams, 'agentType') || '';
|
|
225
|
-
const task = readStringParam(rawParams, 'task') || '';
|
|
226
|
-
const runAsync = readBooleanParam(rawParams, 'runInBackground') === true;
|
|
227
|
-
const availableInternalAgents = listAvailableAgents();
|
|
228
|
-
// Log summary at info level, full params at debug level
|
|
229
|
-
api.logger?.info?.(`[PD:AgentSpawn] toolCallId=${_toolCallId}, agentType=${agentType || 'MISSING'}, hasTask=${!!task}, taskLength=${task.length}, runInBackground=${runAsync}`);
|
|
230
|
-
api.logger?.debug?.(`[PD:AgentSpawn] Full rawParams: ${JSON.stringify(sanitizeParamsForLogging(rawParams))}`);
|
|
231
|
-
if (!agentType) {
|
|
232
|
-
api.logger?.warn?.(`[PD:AgentSpawn] Missing agentType, hasTask=${!!task}`);
|
|
233
|
-
return {
|
|
234
|
-
content: [{
|
|
235
|
-
type: 'text',
|
|
236
|
-
text: `❌ 缺少 agentType 参数。请按以下格式调用:
|
|
237
|
-
|
|
238
|
-
pd_run_worker(
|
|
239
|
-
agentType="diagnostician", // 必填: explorer, diagnostician, auditor, planner, implementer, reviewer, reporter
|
|
240
|
-
task="分析问题的根因" // 必填: 任务描述
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
可用的智能体类型: ${availableInternalAgents.join(', ')}`
|
|
244
|
-
}]
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
if (!task) {
|
|
248
|
-
api.logger?.warn?.(`[PD:AgentSpawn] Missing task for agentType=${agentType}`);
|
|
249
|
-
return {
|
|
250
|
-
content: [{
|
|
251
|
-
type: 'text',
|
|
252
|
-
text: `❌ 缺少 task 参数。请提供任务描述,例如:
|
|
253
|
-
|
|
254
|
-
pd_run_worker(
|
|
255
|
-
agentType="${agentType || 'diagnostician'}",
|
|
256
|
-
task="分析 Pain xxx 的根因"
|
|
257
|
-
)`
|
|
258
|
-
}]
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
if (looksLikeSessionOrPeerCoordinationTask(task)) {
|
|
262
|
-
api.logger?.warn?.(`[PD:AgentSpawn] Rejected likely peer/session misuse for task: ${task}`);
|
|
263
|
-
return {
|
|
264
|
-
content: [{
|
|
265
|
-
type: 'text',
|
|
266
|
-
text: buildInternalAgentUsageMessage(availableInternalAgents)
|
|
267
|
-
}]
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
if (!availableInternalAgents.includes(agentType)) {
|
|
271
|
-
return {
|
|
272
|
-
content: [{
|
|
273
|
-
type: 'text',
|
|
274
|
-
text: `❌ 未知的智能体类型: "${agentType}"
|
|
275
|
-
|
|
276
|
-
可用的智能体类型: ${availableInternalAgents.join(', ')}
|
|
277
|
-
|
|
278
|
-
示例: pd_run_worker(agentType="diagnostician", task="分析问题根因")`
|
|
279
|
-
}]
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
// Load agent definition
|
|
283
|
-
const agentDef = loadAgentDefinition(agentType);
|
|
284
|
-
if (!agentDef) {
|
|
285
|
-
return {
|
|
286
|
-
content: [{
|
|
287
|
-
type: 'text',
|
|
288
|
-
text: `❌ 无法加载智能体定义: ${agentType}`
|
|
289
|
-
}]
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
// Check subagent runtime availability.
|
|
293
|
-
// api.runtime.subagent is always a Proxy object (truthy), even in embedded mode.
|
|
294
|
-
// Use the shared probe utility to detect which mode we're in.
|
|
295
|
-
const subagentRuntime = api.runtime?.subagent;
|
|
296
|
-
if (!isSubagentRuntimeAvailable(subagentRuntime)) {
|
|
297
|
-
return {
|
|
298
|
-
content: [{
|
|
299
|
-
type: 'text',
|
|
300
|
-
text: `❌ Subagent runtime 不可用。\n\n当前运行在 embedded 模式(openclaw agent CLI),该模式不支持派生子智能体。\n\n请通过 Gateway 模式运行(openclaw gateway),然后再调用 pd_run_worker。`
|
|
301
|
-
}]
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
// Build session key
|
|
305
|
-
const sessionKey = `agent:${agentType}:${randomUUID()}`;
|
|
306
|
-
// Build system prompt
|
|
307
|
-
const extraSystemPrompt = buildSubagentSystemPrompt(agentDef, task);
|
|
308
|
-
const startTime = Date.now();
|
|
309
|
-
try {
|
|
310
|
-
// Run subagent
|
|
311
|
-
await subagentRuntime.run({
|
|
312
|
-
sessionKey,
|
|
313
|
-
message: task,
|
|
314
|
-
extraSystemPrompt,
|
|
315
|
-
lane: 'subagent',
|
|
316
|
-
deliver: false, // Critical: don't send directly to external channels
|
|
317
|
-
idempotencyKey: randomUUID(),
|
|
318
|
-
});
|
|
319
|
-
if (agentType === 'diagnostician') {
|
|
320
|
-
await registerDiagnosticianRun(api, task, sessionKey);
|
|
321
|
-
}
|
|
322
|
-
if (runAsync) {
|
|
323
|
-
const duration = Date.now() - startTime;
|
|
324
|
-
return {
|
|
325
|
-
content: [{
|
|
326
|
-
type: 'text',
|
|
327
|
-
text: `✅ 已在后台启动 **${agentDef.name}** (${(duration / 1000).toFixed(1)}s)。它不会阻塞当前对话。`
|
|
328
|
-
}]
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
// Wait for completion (with configurable timeout to prevent indefinite block)
|
|
332
|
-
const timeoutMs = api.config?.get?.('intervals.task_timeout_ms') || (30 * 60 * 1000);
|
|
333
|
-
const result = await subagentRuntime.waitForRun({
|
|
334
|
-
runId: sessionKey,
|
|
335
|
-
timeoutMs,
|
|
336
|
-
});
|
|
337
|
-
const duration = Date.now() - startTime;
|
|
338
|
-
// Handle timeout
|
|
339
|
-
if (result.status === 'timeout') {
|
|
340
|
-
return {
|
|
341
|
-
content: [{
|
|
342
|
-
type: 'text',
|
|
343
|
-
text: `⚠️ 智能体 **${agentDef.name}** 执行超时 (${(duration / 1000).toFixed(1)}s)。
|
|
344
|
-
|
|
345
|
-
建议:
|
|
346
|
-
- 简化任务描述
|
|
347
|
-
- 分解为多个子任务
|
|
348
|
-
- 检查任务是否需要更多上下文`
|
|
349
|
-
}]
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
// Handle error
|
|
353
|
-
if (result.status === 'error') {
|
|
354
|
-
return {
|
|
355
|
-
content: [{
|
|
356
|
-
type: 'text',
|
|
357
|
-
text: `❌ 智能体 **${agentDef.name}** 执行失败: ${result.error || '未知错误'}`
|
|
358
|
-
}]
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
// Get results
|
|
362
|
-
const messages = await subagentRuntime.getSessionMessages({ sessionKey });
|
|
363
|
-
const output = extractAssistantText(messages);
|
|
364
|
-
if (!output || output.trim() === '') {
|
|
365
|
-
return {
|
|
366
|
-
content: [{
|
|
367
|
-
type: 'text',
|
|
368
|
-
text: `⚠️ 智能体 **${agentDef.name}** 执行完成,但没有返回输出。`
|
|
369
|
-
}]
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
// Return formatted result
|
|
373
|
-
return {
|
|
374
|
-
content: [{
|
|
375
|
-
type: 'text',
|
|
376
|
-
text: `✅ **${agentDef.name}** 执行完成 (${(duration / 1000).toFixed(1)}s)
|
|
377
|
-
|
|
378
|
-
---
|
|
379
|
-
|
|
380
|
-
${output}`
|
|
381
|
-
}]
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
catch (err) {
|
|
385
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
386
|
-
return {
|
|
387
|
-
content: [{
|
|
388
|
-
type: 'text',
|
|
389
|
-
text: `❌ 智能体 **${agentDef.name}** 执行异常: ${errorMsg}`
|
|
390
|
-
}]
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
finally {
|
|
394
|
-
// Cleanup session (P1 fix: log failures instead of silent swallow)
|
|
395
|
-
if (!runAsync) {
|
|
396
|
-
try {
|
|
397
|
-
await subagentRuntime.deleteSession({ sessionKey });
|
|
398
|
-
}
|
|
399
|
-
catch (cleanupErr) {
|
|
400
|
-
const cleanupErrMsg = cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr);
|
|
401
|
-
api.logger?.error?.(`[PD:AgentSpawn] Failed to cleanup session ${sessionKey}: ${cleanupErrMsg}`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
// Legacy export for backward compatibility with internal code
|
|
409
|
-
// This is the "unconfigured" tool that needs to be created via createAgentSpawnTool()
|
|
410
|
-
export const agentSpawnTool = {
|
|
411
|
-
name: 'pd_run_worker',
|
|
412
|
-
description: `启动指定类型的子智能体执行任务。
|
|
413
|
-
|
|
414
|
-
可用的智能体类型:
|
|
415
|
-
- explorer: 快速收集证据(文件、日志、复现步骤)
|
|
416
|
-
- diagnostician: 根因分析(verb/adjective + 5Whys)
|
|
417
|
-
- auditor: 演演审计(axiom/system/via-negativa)
|
|
418
|
-
- planner: 制定电影剧本计划
|
|
419
|
-
- implementer: 按计划执行代码修改
|
|
420
|
-
- reviewer: 代码审查(正确性、安全性、可维护性)
|
|
421
|
-
- reporter: 最终汇报(技术细节转管理报告)
|
|
422
|
-
|
|
423
|
-
示例调用: pd_run_worker(agentType="diagnostician", task="分析 Pain 7386ccfb 的根因")`,
|
|
424
|
-
parameters: Type.Object({
|
|
425
|
-
agentType: Type.String({
|
|
426
|
-
description: '【必填】智能体类型,必须是以下之一: explorer, diagnostician, auditor, planner, implementer, reviewer, reporter',
|
|
427
|
-
}),
|
|
428
|
-
task: Type.String({
|
|
429
|
-
description: '【必填】任务描述,告诉智能体要做什么',
|
|
430
|
-
}),
|
|
431
|
-
runInBackground: Type.Optional(Type.Boolean({
|
|
432
|
-
description: '【可选】是否后台运行。true=立即返回不等待结果,false=等待执行完成。默认 false',
|
|
433
|
-
})),
|
|
434
|
-
}),
|
|
435
|
-
// This execute method throws to prevent accidental usage of the unconfigured tool
|
|
436
|
-
execute: () => {
|
|
437
|
-
throw new Error('agentSpawnTool is a metadata-only export. ' +
|
|
438
|
-
'Use createAgentSpawnTool(api) to get an executable instance.');
|
|
439
|
-
},
|
|
440
|
-
};
|
|
441
|
-
/**
|
|
442
|
-
* Batch spawn multiple agents in sequence
|
|
443
|
-
* Useful for evolution workflow
|
|
444
|
-
*/
|
|
445
|
-
export async function spawnAgentSequence(agents, api, onProgress) {
|
|
446
|
-
const results = new Map();
|
|
447
|
-
const tool = createAgentSpawnTool(api);
|
|
448
|
-
for (const { type, task } of agents) {
|
|
449
|
-
const toolCallId = `spawn-sequence-${type}-${randomUUID()}`;
|
|
450
|
-
const result = await tool.execute(toolCallId, { agentType: type, task });
|
|
451
|
-
const text = result.content[0]?.text || '';
|
|
452
|
-
results.set(type, text);
|
|
453
|
-
onProgress?.(type, text);
|
|
454
|
-
}
|
|
455
|
-
return results;
|
|
456
|
-
}
|