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