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.
Files changed (40) hide show
  1. package/dist/commands/focus.js +30 -155
  2. package/dist/constants/diagnostician.d.ts +16 -0
  3. package/dist/constants/diagnostician.js +60 -0
  4. package/dist/constants/tools.d.ts +2 -2
  5. package/dist/constants/tools.js +1 -1
  6. package/dist/core/config.d.ts +12 -0
  7. package/dist/core/config.js +7 -0
  8. package/dist/core/evolution-engine.js +1 -1
  9. package/dist/core/focus-history.d.ts +92 -0
  10. package/dist/core/focus-history.js +490 -0
  11. package/dist/core/init.js +2 -2
  12. package/dist/core/profile.js +1 -1
  13. package/dist/hooks/gate.js +3 -3
  14. package/dist/hooks/prompt.js +73 -22
  15. package/dist/hooks/subagent.js +1 -2
  16. package/dist/http/principles-console-route.d.ts +7 -0
  17. package/dist/http/principles-console-route.js +243 -1
  18. package/dist/index.js +0 -2
  19. package/dist/service/central-database.d.ts +104 -0
  20. package/dist/service/central-database.js +648 -0
  21. package/dist/service/evolution-worker.js +3 -3
  22. package/dist/tools/deep-reflect.js +1 -2
  23. package/dist/utils/subagent-probe.d.ts +11 -0
  24. package/dist/utils/subagent-probe.js +46 -1
  25. package/package.json +2 -1
  26. package/templates/langs/en/core/AGENTS.md +1 -1
  27. package/templates/langs/en/core/TOOLS.md +1 -1
  28. package/templates/langs/zh/core/AGENTS.md +1 -1
  29. package/templates/langs/zh/core/TOOLS.md +1 -1
  30. package/{agents/auditor.md → templates/langs/zh/skills/pd-auditor/SKILL.md} +3 -3
  31. package/{agents/diagnostician.md → templates/langs/zh/skills/pd-diagnostician/SKILL.md} +39 -29
  32. package/{agents/explorer.md → templates/langs/zh/skills/pd-explorer/SKILL.md} +3 -3
  33. package/{agents/implementer.md → templates/langs/zh/skills/pd-implementer/SKILL.md} +3 -3
  34. package/{agents/planner.md → templates/langs/zh/skills/pd-planner/SKILL.md} +3 -3
  35. package/{agents/reporter.md → templates/langs/zh/skills/pd-reporter/SKILL.md} +3 -3
  36. package/{agents/reviewer.md → templates/langs/zh/skills/pd-reviewer/SKILL.md} +3 -3
  37. package/dist/core/agent-loader.d.ts +0 -44
  38. package/dist/core/agent-loader.js +0 -147
  39. package/dist/tools/agent-spawn.d.ts +0 -54
  40. 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
- }