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
@@ -53,8 +53,9 @@ function buildEnglishOutput(workspaceDir, sessionId, warnings, stats, summary) {
53
53
  '',
54
54
  'Evolution',
55
55
  `- Queue: pending ${summary.evolution.queue.pending}, in_progress ${summary.evolution.queue.inProgress}, completed ${summary.evolution.queue.completed} (${summary.evolution.dataQuality})`,
56
- `- Directive: ${summary.evolution.directive.exists ? 'present' : 'missing'}, active ${summary.evolution.directive.active === null ? '--' : summary.evolution.directive.active ? 'yes' : 'no'}, age ${formatAge(summary.evolution.directive.ageSeconds, 'en')}`,
56
+ `- Directive (derived from queue, compatibility only): ${summary.evolution.directive.exists ? 'present' : 'missing'}, active ${summary.evolution.directive.active === null ? '--' : summary.evolution.directive.active ? 'yes' : 'no'}, age ${formatAge(summary.evolution.directive.ageSeconds, 'en')}`,
57
57
  `- Directive Task: ${summary.evolution.directive.taskPreview ?? '--'}`,
58
+ `- Phase 3: ready ${summary.phase3.phase3ShadowEligible ? 'yes' : 'no'}, queueTruthReady ${summary.phase3.queueTruthReady ? 'yes' : 'no'}, trustInputReady ${summary.phase3.trustInputReady ? 'yes' : 'no'}, eligible ${summary.phase3.evolutionEligible}, rejected ${summary.phase3.evolutionRejected}${summary.phase3.evolutionRejectedReasons.length > 0 ? ` (${summary.phase3.evolutionRejectedReasons.slice(0, 3).join(', ')})` : ''}${summary.phase3.trustRejectedReasons.length > 0 ? `; trust ${summary.phase3.trustRejectedReasons.slice(0, 2).join(', ')}` : ''}`,
58
59
  '',
59
60
  'Principles',
60
61
  `- candidate principles: ${stats.candidateCount}`,
@@ -91,8 +92,9 @@ function buildChineseOutput(workspaceDir, sessionId, warnings, stats, summary) {
91
92
  '',
92
93
  '\u8fdb\u5316',
93
94
  `- \u961f\u5217: pending ${summary.evolution.queue.pending}\uff0cin_progress ${summary.evolution.queue.inProgress}\uff0ccompleted ${summary.evolution.queue.completed}\uff08${summary.evolution.dataQuality}\uff09`,
94
- `- Directive: ${summary.evolution.directive.exists ? 'present' : 'missing'}\uff0cactive ${summary.evolution.directive.active === null ? '--' : summary.evolution.directive.active ? 'yes' : 'no'}\uff0cage ${formatAge(summary.evolution.directive.ageSeconds, 'zh')}`,
95
+ `- Directive\uff08\u7531\u961f\u5217\u6d3e\u751f\uff0c\u517c\u5bb9\u4ec5\uff09: ${summary.evolution.directive.exists ? 'present' : 'missing'}\uff0cactive ${summary.evolution.directive.active === null ? '--' : summary.evolution.directive.active ? 'yes' : 'no'}\uff0cage ${formatAge(summary.evolution.directive.ageSeconds, 'zh')}`,
95
96
  `- Directive \u4efb\u52a1: ${summary.evolution.directive.taskPreview ?? '--'}`,
97
+ `- Phase 3: ready ${summary.phase3.phase3ShadowEligible ? 'yes' : 'no'}\uff0cqueueTruthReady ${summary.phase3.queueTruthReady ? 'yes' : 'no'}\uff0ctrustInputReady ${summary.phase3.trustInputReady ? 'yes' : 'no'}\uff0celigible ${summary.phase3.evolutionEligible}\uff0crejected ${summary.phase3.evolutionRejected}${summary.phase3.evolutionRejectedReasons.length > 0 ? `\uff08${summary.phase3.evolutionRejectedReasons.slice(0, 3).join(', ')}\uff09` : ''}${summary.phase3.trustRejectedReasons.length > 0 ? `\uff1btrust ${summary.phase3.trustRejectedReasons.slice(0, 2).join(', ')}` : ''}`,
96
98
  '',
97
99
  '\u539f\u5219\u7edf\u8ba1',
98
100
  `- \u5019\u9009\u539f\u5219: ${stats.candidateCount}`,
@@ -9,10 +9,8 @@
9
9
  */
10
10
  import * as fs from 'fs';
11
11
  import * as path from 'path';
12
- import { randomUUID } from 'node:crypto';
13
12
  import { WorkspaceContext } from '../core/workspace-context.js';
14
- import { getHistoryDir, backupToHistory, cleanupHistory, extractVersion, extractDate, } from '../core/focus-history.js';
15
- import { createAgentSpawnTool } from '../tools/agent-spawn.js';
13
+ import { getHistoryDir, backupToHistory, cleanupHistory, extractVersion, extractDate, extractMilestones, archiveMilestonesToDaily, cleanupStaleInfo, } from '../core/focus-history.js';
16
14
  /**
17
15
  * 清理 Markdown 代码块围栏
18
16
  * 移除开头的 ```lang 和结尾的 ```
@@ -45,10 +43,14 @@ function getWorkspaceDir(ctx) {
45
43
  * - 移除:当前任务中已完成的项(- [x])超过 3 个时
46
44
  * - 移除:P0 章节如果全部完成
47
45
  * - 保留:参考章节
46
+ * - 清理:Working Memory 超过 10 条记录时保留最近 10 条
47
+ * - 验证:文件引用指向不存在的文件时移除
48
48
  */
49
- function compressFocusContent(content) {
50
- const lines = content.split('\n');
51
- const result = [];
49
+ function compressFocusContent(content, workspaceDir) {
50
+ // 首先使用 cleanupStaleInfo 进行基础清理
51
+ let result = cleanupStaleInfo(content, workspaceDir);
52
+ const lines = result.split('\n');
53
+ const output = [];
52
54
  let currentSection = '';
53
55
  let inP0Section = false;
54
56
  let p0AllCompleted = true;
@@ -59,7 +61,7 @@ function compressFocusContent(content) {
59
61
  if (inP0Section && p0Lines.length > 0) {
60
62
  if (!skipIfCompleted || !p0AllCompleted) {
61
63
  // P0 有未完成任务,保留 P0 内容
62
- result.push(...p0Lines);
64
+ output.push(...p0Lines);
63
65
  }
64
66
  // 重置状态
65
67
  p0Lines = [];
@@ -86,7 +88,7 @@ function compressFocusContent(content) {
86
88
  // 离开 P0 章节
87
89
  flushP0Lines(true); // P0 完成时跳过,未完成时保留
88
90
  currentSection = 'current';
89
- result.push(line);
91
+ output.push(line);
90
92
  continue;
91
93
  }
92
94
  else if (/^#{1,3}\s*.*当前任务|🔄/.test(trimmedLine)) {
@@ -120,11 +122,11 @@ function compressFocusContent(content) {
120
122
  }
121
123
  }
122
124
  }
123
- result.push(line);
125
+ output.push(line);
124
126
  }
125
127
  // 循环结束后,刷新剩余的 P0 章节
126
128
  flushP0Lines(false); // 保留未完成的 P0
127
- return result.join('\n');
129
+ return output.join('\n');
128
130
  }
129
131
  /**
130
132
  * 显示 CURRENT_FOCUS 状态
@@ -237,144 +239,26 @@ async function compressFocus(workspaceDir, isZh, api) {
237
239
  ? `✅ 当前文件仅 ${oldLines} 行,无需压缩\n\n💡 文件少于 40 行时不建议压缩`
238
240
  : `✅ Current file has only ${oldLines} lines, no need to compress\n\n💡 Compression not recommended for files under 40 lines`;
239
241
  }
240
- // 备份当前版本
242
+ // 1. 提取里程碑
243
+ const milestones = extractMilestones(oldContent);
244
+ // 2. 归档里程碑到 daily memory(而非 MEMORY.md)
245
+ const archivePath = archiveMilestonesToDaily(workspaceDir, milestones, oldVersion);
246
+ const milestonesArchived = archivePath !== null;
247
+ // 3. 备份当前版本
241
248
  const backupPath = backupToHistory(focusPath, oldContent);
242
- // 清理过期历史
249
+ // 4. 清理过期历史
243
250
  cleanupHistory(focusPath);
244
- // 使用子智能体进行智能压缩
245
- // 获取 MEMORY.md 路径
246
- const memoryPath = wctx.resolve('MEMORY_MD');
247
- const compressPrompt = isZh
248
- ? `你是一个专业的项目文档压缩助手。请压缩以下 CURRENT_FOCUS.md 文件内容。
249
-
250
- **重要:在压缩前,你需要提取重要里程碑信息!**
251
-
252
- **第一步:提取里程碑**
253
- 从原始内容中识别已完成的里程碑,这些信息需要保存到记忆文件中。
254
-
255
- **第二步:压缩文件**
256
- 压缩规则:
257
- 1. 保留标题、元数据行(版本、状态、日期)
258
- 2. 保留"📍 状态快照"章节(完整)
259
- 3. 保留"➡️ 下一步"章节(完整,这是最重要的信息)
260
- 4. 保留"📎 参考"章节(完整)
261
- 5. 对于"🔄 当前任务"章节:
262
- - 如果 P0/P1 等子章节全部完成,合并为简短"已完成里程碑"列表(最多5项)
263
- - 保留所有未完成任务(- [ ])
264
- - 已完成任务最多保留 3 个最近的,其余移除
265
- 6. 移除重复信息和冗余描述
266
- 7. 保持 Markdown 格式和语义连贯
267
-
268
- **目标:** 将文件压缩到 40 行以内。
269
-
270
- **原始内容:**
271
- \`\`\`markdown
272
- ${oldContent}
273
- \`\`\`
274
-
275
- **输出格式(必须严格遵循):**
276
- \`\`\`
277
- ===MEMORY===
278
- [需要追加到 memory/MEMORY.md 的里程碑内容,格式:]
279
- ## {YYYY-MM-DD} 里程碑
280
- - [里程碑1]
281
- - [里程碑2]
282
- ...
283
- ===COMPRESSED===
284
- [压缩后的 CURRENT_FOCUS.md 内容]
285
- \`\`\`
286
-
287
- 如果没有需要记录的里程碑,===MEMORY=== 部分留空。`
288
- : `You are a professional project document compression assistant. Please compress the following CURRENT_FOCUS.md file.
289
-
290
- **IMPORTANT: Extract milestones before compression!**
291
-
292
- **Step 1: Extract Milestones**
293
- Identify completed milestones from the original content that should be saved to memory.
294
-
295
- **Step 2: Compress File**
296
- Compression Rules:
297
- 1. Keep title, metadata lines (version, status, date)
298
- 2. Keep "📍 Status Snapshot" section (complete)
299
- 3. Keep "➡️ Next Steps" section (complete, this is the most important)
300
- 4. Keep "📎 References" section (complete)
301
- 5. For "🔄 Current Tasks" section:
302
- - If P0/P1 subsections are all completed, merge into a short "Completed Milestones" list (max 5 items)
303
- - Keep all incomplete tasks (- [ ])
304
- - Keep at most 3 recent completed tasks, remove the rest
305
- 6. Remove duplicate info and redundant descriptions
306
- 7. Maintain Markdown format and semantic coherence
307
-
308
- **Goal:** Compress the file to under 40 lines.
309
-
310
- **Original Content:**
311
- \`\`\`markdown
312
- ${oldContent}
313
- \`\`\`
314
-
315
- **Output Format (must follow strictly):**
316
- \`\`\`
317
- ===MEMORY===
318
- [Content to append to memory/MEMORY.md, format:]
319
- ## {YYYY-MM-DD} Milestones
320
- - [milestone 1]
321
- - [milestone 2]
322
- ...
323
- ===COMPRESSED===
324
- [Compressed CURRENT_FOCUS.md content]
325
- \`\`\`
326
-
327
- If no milestones to record, leave ===MEMORY=== section empty.`;
251
+ // 5. 压缩内容
328
252
  let compressedContent;
329
- let usedAI = false;
330
- let memoryUpdated = false;
331
253
  try {
332
- // 调用子智能体进行压缩
333
- const tool = createAgentSpawnTool(api);
334
- const result = await tool.execute(`focus-compress-${randomUUID()}`, {
335
- agentType: 'reporter', // 使用 reporter 类型,适合总结和压缩
336
- task: compressPrompt,
337
- });
338
- // 解析输出,提取 MEMORY 和 COMPRESSED 部分
339
- const resultText = result?.content?.[0]?.text || '';
340
- if (resultText.trim()) {
341
- const memoryMatch = resultText.match(/===MEMORY===([\s\S]*?)===COMPRESSED===/);
342
- const compressedMatch = resultText.match(/===COMPRESSED===([\s\S]*?)$/);
343
- if (compressedMatch && compressedMatch[1].trim()) {
344
- // 清理 Markdown 代码块围栏
345
- compressedContent = stripMarkdownFence(compressedMatch[1]);
346
- usedAI = true;
347
- // 写入记忆文件(MEMORY.md 在根目录,无需创建目录)
348
- if (memoryMatch && memoryMatch[1].trim()) {
349
- // 清理 Markdown 代码块围栏
350
- const memoryContent = stripMarkdownFence(memoryMatch[1]);
351
- // 追加到 MEMORY.md
352
- const existingMemory = fs.existsSync(memoryPath)
353
- ? fs.readFileSync(memoryPath, 'utf-8')
354
- : '';
355
- const newMemory = existingMemory
356
- ? `${existingMemory}\n\n${memoryContent}`
357
- : memoryContent;
358
- fs.writeFileSync(memoryPath, newMemory, 'utf-8');
359
- memoryUpdated = true;
360
- }
361
- }
362
- else {
363
- // 无法解析输出,回退到简单压缩
364
- compressedContent = compressFocusContent(oldContent);
365
- }
366
- }
367
- else {
368
- // 子智能体返回空,回退到简单压缩
369
- compressedContent = compressFocusContent(oldContent);
370
- }
254
+ compressedContent = compressFocusContent(oldContent, workspaceDir);
255
+ api.logger?.info?.(`[PD:Focus] Compressed CURRENT_FOCUS from ${oldLines} lines`);
371
256
  }
372
257
  catch (error) {
373
- // 子智能体失败,回退到简单压缩
374
- api.logger?.error(`[PD:Focus] AI compression failed, falling back to simple compression: ${String(error)}`);
375
- compressedContent = compressFocusContent(oldContent);
258
+ api.logger?.error?.(`[PD:Focus] Compression failed: ${String(error)}`);
259
+ compressedContent = oldContent;
376
260
  }
377
- // 更新版本号和日期
261
+ // 6. 更新版本号和日期
378
262
  const versionParts = oldVersion.split('.');
379
263
  const majorVersion = parseInt(versionParts[0], 10) || 1;
380
264
  const newVersion = `${majorVersion + 1}`;
@@ -385,17 +269,10 @@ If no milestones to record, leave ===MEMORY=== section empty.`;
385
269
  const newLines = newContent.split('\n').length;
386
270
  const savedLines = oldLines - newLines;
387
271
  fs.writeFileSync(focusPath, newContent, 'utf-8');
388
- const methodNote = usedAI
272
+ const milestoneNote = milestonesArchived
389
273
  ? isZh
390
- ? '🤖 使用 AI 智能压缩'
391
- : '🤖 AI-powered compression'
392
- : isZh
393
- ? '📋 使用规则压缩'
394
- : '📋 Rule-based compression';
395
- const memoryNote = memoryUpdated
396
- ? isZh
397
- ? '📝 已将里程碑写入 MEMORY.md'
398
- : '📝 Milestones saved to MEMORY.md'
274
+ ? `📝 里程碑已归档到 memory/${today}.md`
275
+ : `📝 Milestones archived to memory/${today}.md`
399
276
  : '';
400
277
  if (isZh) {
401
278
  return `✅ **压缩完成**
@@ -409,8 +286,7 @@ If no milestones to record, leave ===MEMORY=== section empty.`;
409
286
  | 节省 | ${savedLines} 行 |
410
287
  | 备份文件 | ${backupPath ? path.basename(backupPath) : '已存在'} |
411
288
 
412
- ${methodNote}${memoryNote ? `\n${memoryNote}` : ''}
413
-
289
+ ${milestoneNote ? `${milestoneNote}\n` : ''}
414
290
  💡 已压缩版本已备份到历史目录
415
291
  💡 输入 \`/pd-focus history\` 查看所有历史版本`;
416
292
  }
@@ -425,8 +301,7 @@ ${methodNote}${memoryNote ? `\n${memoryNote}` : ''}
425
301
  | Saved | ${savedLines} lines |
426
302
  | Backup File | ${backupPath ? path.basename(backupPath) : 'exists'} |
427
303
 
428
- ${methodNote}${memoryNote ? `\n${memoryNote}` : ''}
429
-
304
+ ${milestoneNote ? `${milestoneNote}\n` : ''}
430
305
  💡 Compressed version backed up to history
431
306
  💡 Type \`/pd-focus history\` to view all versions`;
432
307
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Diagnostician 协议常量
3
+ *
4
+ * 用于 evolution-worker.ts(后台心跳)和 prompt.ts(实时路径)
5
+ * 与 templates/langs/zh/skills/pd-diagnostician/SKILL.md 保持一致
6
+ */
7
+ /**
8
+ * 诊断协议摘要(用于 HEARTBEAT.md 注入)
9
+ *
10
+ * 完整版见: templates/langs/zh/skills/pd-diagnostician/SKILL.md
11
+ */
12
+ export declare const DIAGNOSTICIAN_PROTOCOL_SUMMARY = "## Diagnostic Protocol (5 Whys)\n\n**Phase 1 - Evidence Gathering**:\n- Read logs: .state/logs/events.jsonl, SYSTEM.log\n- Search code for error patterns from Reason field\n- Record evidence sources\n\n**Phase 2 - Causal Chain**:\n- Each Why must have evidence support\n- Maximum 5 layers\n- Stop when reaching actionable root cause\n\n**Phase 3 - Root Cause Classification**:\nClassify into one of:\n- **People**: Human error, missing knowledge, communication gap\n- **Design**: Architecture flaw, missing validation, poor abstraction\n- **Assumption**: Invalid assumption, outdated context, edge case\n- **Tooling**: Tool limitation, environment issue, dependency problem\n\n**Phase 4 - Principle Extraction**:\nExtract protection principle with:\n- **trigger_pattern**: When this situation occurs\n- **action**: What to do differently\n\n**Output Format**:\n```json\n{\n \"diagnosis_report\": {\n \"task_id\": \"pain-xxx\",\n \"summary\": \"One-line root cause\",\n \"causal_chain\": [\n { \"why\": 1, \"answer\": \"...\", \"evidence\": \"...\" }\n ],\n \"root_cause\": {\n \"category\": \"Design|People|Assumption|Tooling\",\n \"description\": \"...\"\n },\n \"principle\": {\n \"trigger_pattern\": \"...\",\n \"action\": \"...\"\n }\n }\n}\n```\n";
13
+ /**
14
+ * 完整诊断协议文件路径(相对于插件根目录)
15
+ */
16
+ export declare const DIAGNOSTICIAN_SKILL_PATH = "templates/langs/zh/skills/pd-diagnostician/SKILL.md";
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Diagnostician 协议常量
3
+ *
4
+ * 用于 evolution-worker.ts(后台心跳)和 prompt.ts(实时路径)
5
+ * 与 templates/langs/zh/skills/pd-diagnostician/SKILL.md 保持一致
6
+ */
7
+ /**
8
+ * 诊断协议摘要(用于 HEARTBEAT.md 注入)
9
+ *
10
+ * 完整版见: templates/langs/zh/skills/pd-diagnostician/SKILL.md
11
+ */
12
+ export const DIAGNOSTICIAN_PROTOCOL_SUMMARY = `## Diagnostic Protocol (5 Whys)
13
+
14
+ **Phase 1 - Evidence Gathering**:
15
+ - Read logs: .state/logs/events.jsonl, SYSTEM.log
16
+ - Search code for error patterns from Reason field
17
+ - Record evidence sources
18
+
19
+ **Phase 2 - Causal Chain**:
20
+ - Each Why must have evidence support
21
+ - Maximum 5 layers
22
+ - Stop when reaching actionable root cause
23
+
24
+ **Phase 3 - Root Cause Classification**:
25
+ Classify into one of:
26
+ - **People**: Human error, missing knowledge, communication gap
27
+ - **Design**: Architecture flaw, missing validation, poor abstraction
28
+ - **Assumption**: Invalid assumption, outdated context, edge case
29
+ - **Tooling**: Tool limitation, environment issue, dependency problem
30
+
31
+ **Phase 4 - Principle Extraction**:
32
+ Extract protection principle with:
33
+ - **trigger_pattern**: When this situation occurs
34
+ - **action**: What to do differently
35
+
36
+ **Output Format**:
37
+ \`\`\`json
38
+ {
39
+ "diagnosis_report": {
40
+ "task_id": "pain-xxx",
41
+ "summary": "One-line root cause",
42
+ "causal_chain": [
43
+ { "why": 1, "answer": "...", "evidence": "..." }
44
+ ],
45
+ "root_cause": {
46
+ "category": "Design|People|Assumption|Tooling",
47
+ "description": "..."
48
+ },
49
+ "principle": {
50
+ "trigger_pattern": "...",
51
+ "action": "..."
52
+ }
53
+ }
54
+ }
55
+ \`\`\`
56
+ `;
57
+ /**
58
+ * 完整诊断协议文件路径(相对于插件根目录)
59
+ */
60
+ export const DIAGNOSTICIAN_SKILL_PATH = 'templates/langs/zh/skills/pd-diagnostician/SKILL.md';
@@ -2,9 +2,9 @@ export declare const READ_ONLY_TOOL_NAMES: readonly ["read", "read_file", "read_
2
2
  export declare const LOW_RISK_WRITE_TOOL_NAMES: readonly ["write", "write_file", "edit", "edit_file", "replace", "apply_patch", "insert", "patch"];
3
3
  export declare const BASH_TOOL_NAMES: readonly ["bash", "run_shell_command", "exec", "execute", "shell", "cmd"];
4
4
  export declare const HIGH_RISK_TOOL_NAMES: readonly ["delete_file", "move_file", "bash", "run_shell_command", "exec", "execute", "shell", "cmd"];
5
- export declare const AGENT_TOOL_NAMES: readonly ["pd_run_worker", "sessions_spawn"];
5
+ export declare const AGENT_TOOL_NAMES: readonly ["sessions_spawn"];
6
6
  export declare const CONTENT_LIMITED_TOOL_NAMES: readonly ["write", "write_file", "edit", "edit_file", "replace", "apply_patch", "insert", "patch"];
7
- export declare const CONSTRUCTIVE_TOOL_NAMES: readonly ["write", "write_file", "edit", "edit_file", "replace", "apply_patch", "insert", "patch", "delete_file", "move_file", "bash", "run_shell_command", "exec", "execute", "shell", "cmd", "pd_run_worker", "sessions_spawn", "evolve-task", "init-strategy"];
7
+ export declare const CONSTRUCTIVE_TOOL_NAMES: readonly ["write", "write_file", "edit", "edit_file", "replace", "apply_patch", "insert", "patch", "delete_file", "move_file", "bash", "run_shell_command", "exec", "execute", "shell", "cmd", "sessions_spawn", "evolve-task", "init-strategy"];
8
8
  export declare const EXPLORATORY_TOOL_NAMES: readonly ["read", "read_file", "read_many_files", "image_read", "search_file_content", "grep", "grep_search", "list_directory", "ls", "glob", "lsp_hover", "lsp_goto_definition", "lsp_find_references", "web_fetch", "web_search", "ref_search_documentation", "ref_read_url", "resolve-library-id", "get-library-docs", "memory_recall", "save_memory", "todo_read", "todo_write", "ask_user", "ask_user_question", "deep_reflect", "pd-status", "trust", "report"];
9
9
  export declare const READ_ONLY_TOOLS: Set<string>;
10
10
  export declare const LOW_RISK_WRITE_TOOLS: Set<string>;
@@ -23,7 +23,7 @@ export const HIGH_RISK_TOOL_NAMES = [
23
23
  ...BASH_TOOL_NAMES,
24
24
  ];
25
25
  export const AGENT_TOOL_NAMES = [
26
- 'pd_run_worker', 'sessions_spawn',
26
+ 'sessions_spawn',
27
27
  ];
28
28
  export const CONTENT_LIMITED_TOOL_NAMES = [
29
29
  ...LOW_RISK_WRITE_TOOL_NAMES,
@@ -60,9 +60,19 @@ export interface TrustSettings {
60
60
  limits: {
61
61
  stage_2_max_lines: number;
62
62
  stage_3_max_lines: number;
63
+ stage_2_max_percentage: number;
64
+ stage_3_max_percentage: number;
65
+ min_lines_fallback: number;
63
66
  };
64
67
  history_limit?: number;
65
68
  }
69
+ export interface DiagnosticianSettings {
70
+ context: {
71
+ time_window_minutes: number;
72
+ max_message_length: number;
73
+ max_summary_length: number;
74
+ };
75
+ }
66
76
  export interface PainSettings {
67
77
  language: 'en' | 'zh';
68
78
  trajectory?: {
@@ -70,6 +80,7 @@ export interface PainSettings {
70
80
  busy_timeout_ms?: number;
71
81
  orphan_blob_grace_days?: number;
72
82
  };
83
+ diagnostician?: DiagnosticianSettings;
73
84
  thresholds: {
74
85
  pain_trigger: number;
75
86
  cognitive_paralysis_input: number;
@@ -116,6 +127,18 @@ export interface PainSettings {
116
127
  model_calibration?: Record<string, number>;
117
128
  };
118
129
  gfi_gate?: GfiGateSettings;
130
+ compression?: {
131
+ /** 触发自动压缩的行数阈值 */
132
+ line_threshold?: number;
133
+ /** 触发自动压缩的字节阈值 */
134
+ size_threshold_kb?: number;
135
+ /** 自动压缩间隔(小时) */
136
+ interval_hours?: number;
137
+ /** 保留的已完成任务数 */
138
+ keep_completed_tasks?: number;
139
+ /** Working Memory 最大条数 */
140
+ max_working_memory_artifacts?: number;
141
+ };
119
142
  }
120
143
  export declare const DEFAULT_SETTINGS: PainSettings;
121
144
  export declare class PainConfig {
@@ -10,6 +10,13 @@ import * as path from 'path';
10
10
  // ─────────────────────────────────────────────────────────────
11
11
  export const DEFAULT_SETTINGS = {
12
12
  language: 'zh', // Optimized for the primary user base
13
+ diagnostician: {
14
+ context: {
15
+ time_window_minutes: 5, // pain_timestamp 前后各 5 分钟
16
+ max_message_length: 500, // 每条消息截断到 500 字符
17
+ max_summary_length: 3000, // 对话摘要最大 3000 字符
18
+ }
19
+ },
13
20
  thresholds: {
14
21
  pain_trigger: 40, // Increased tolerance before forcing a stop
15
22
  cognitive_paralysis_input: 4000,
@@ -37,7 +44,7 @@ export const DEFAULT_SETTINGS = {
37
44
  intervals: {
38
45
  worker_poll_ms: 15 * 60 * 1000,
39
46
  initial_delay_ms: 5000,
40
- task_timeout_ms: 30 * 60 * 1000
47
+ task_timeout_ms: 60 * 60 * 1000 // 1 hour, matching evolution-worker.ts default
41
48
  },
42
49
  trust: {
43
50
  stages: {
@@ -73,6 +80,9 @@ export const DEFAULT_SETTINGS = {
73
80
  limits: {
74
81
  stage_2_max_lines: 50, // Was 10. 10 lines is barely enough to fix a function signature.
75
82
  stage_3_max_lines: 300, // Was 100. Allow substantial feature implementation.
83
+ stage_2_max_percentage: 10, // Percentage-based threshold for Stage 2
84
+ stage_3_max_percentage: 15, // Percentage-based threshold for Stage 3
85
+ min_lines_fallback: 20, // Minimum threshold even for small files
76
86
  },
77
87
  history_limit: 50
78
88
  },
@@ -136,6 +146,13 @@ export const DEFAULT_SETTINGS = {
136
146
  'docker\\s+push',
137
147
  '(curl|wget).*\\|\\s*(ba)?sh',
138
148
  ],
149
+ },
150
+ compression: {
151
+ line_threshold: 100, // 超过 100 行触发压缩
152
+ size_threshold_kb: 15, // 超过 15KB 触发压缩
153
+ interval_hours: 24, // 每 24 小时最多压缩一次
154
+ keep_completed_tasks: 3, // 保留最近 3 个已完成任务
155
+ max_working_memory_artifacts: 10, // Working Memory 最多 10 条
139
156
  }
140
157
  };
141
158
  export class PainConfig {
@@ -210,6 +227,14 @@ export class PainConfig {
210
227
  // Ensure intervals are positive
211
228
  if (settings.intervals.worker_poll_ms < 1000)
212
229
  settings.intervals.worker_poll_ms = 15 * 60 * 1000;
230
+ // Ensure percentage limits are in valid range [0, 100]
231
+ const l = settings.trust.limits;
232
+ if (l.stage_2_max_percentage < 0 || l.stage_2_max_percentage > 100)
233
+ l.stage_2_max_percentage = 10;
234
+ if (l.stage_3_max_percentage < 0 || l.stage_3_max_percentage > 100)
235
+ l.stage_3_max_percentage = 15;
236
+ if (l.min_lines_fallback < 1)
237
+ l.min_lines_fallback = 20;
213
238
  }
214
239
  /**
215
240
  * Gets a value using dot notation (e.g. 'thresholds.pain_trigger')
@@ -174,7 +174,7 @@ export class EvolutionEngine {
174
174
  };
175
175
  }
176
176
  // 子智能体检查
177
- if ((context.toolName === 'pd_run_worker' || context.toolName === 'sessions_spawn') && !perms.allowSubagentSpawn) {
177
+ if (context.toolName === 'sessions_spawn' && !perms.allowSubagentSpawn) {
178
178
  return {
179
179
  allowed: false,
180
180
  reason: `Tier ${this.scorecard.currentTier} (${tierDef.name}) 未解锁子智能体权限`,
@@ -0,0 +1,137 @@
1
+ /**
2
+ * EvolutionLogger - 进化流程日志服务
3
+ *
4
+ * 提供两层日志:
5
+ * - 技术层:结构化 JSON,包含 trace_id、stage、metadata
6
+ * - 用户层:中文摘要,便于小白用户理解
7
+ *
8
+ * 日志写入两个地方:
9
+ * 1. SYSTEM_LOG (system.jsonl) - 技术细节
10
+ * 2. evolution_events 表 (SQLite) - 结构化查询
11
+ */
12
+ import type { TrajectoryDatabase } from './trajectory.js';
13
+ export type EvolutionStage = 'pain_detected' | 'queued' | 'started' | 'analyzing' | 'principle_generated' | 'completed' | 'failed';
14
+ export type EvolutionLogLevel = 'debug' | 'info' | 'warn' | 'error';
15
+ export interface EvolutionLogEntry {
16
+ traceId: string;
17
+ stage: EvolutionStage;
18
+ level: EvolutionLogLevel;
19
+ message: string;
20
+ summary: string;
21
+ timestamp: string;
22
+ metadata?: Record<string, unknown>;
23
+ taskId?: string;
24
+ sessionId?: string;
25
+ }
26
+ export interface EvolutionLogInput {
27
+ traceId: string;
28
+ stage: EvolutionStage;
29
+ level?: EvolutionLogLevel;
30
+ message: string;
31
+ summary: string;
32
+ metadata?: Record<string, unknown>;
33
+ taskId?: string;
34
+ sessionId?: string;
35
+ }
36
+ export declare const STAGE_LABELS: Record<EvolutionStage, string>;
37
+ export declare const STAGE_COLORS: Record<EvolutionStage, string>;
38
+ /**
39
+ * 创建新的 trace_id
40
+ * 格式: ev_{timestamp}_{random}
41
+ * 使用 crypto.randomBytes 确保不可预测性
42
+ */
43
+ export declare function createTraceId(): string;
44
+ /**
45
+ * EvolutionLogger 类
46
+ * 管理进化流程的日志记录
47
+ */
48
+ export declare class EvolutionLogger {
49
+ private readonly workspaceDir;
50
+ private readonly trajectory;
51
+ constructor(workspaceDir: string, trajectory?: TrajectoryDatabase);
52
+ /**
53
+ * 记录进化事件
54
+ * 同时写入 SYSTEM_LOG 和 evolution_events 表
55
+ */
56
+ log(input: EvolutionLogInput): void;
57
+ /**
58
+ * 记录 pain_detected 事件
59
+ */
60
+ logPainDetected(params: {
61
+ traceId: string;
62
+ source: string;
63
+ reason: string;
64
+ score: number;
65
+ toolName?: string;
66
+ filePath?: string;
67
+ sessionId?: string;
68
+ }): void;
69
+ /**
70
+ * 记录 queued 事件
71
+ */
72
+ logQueued(params: {
73
+ traceId: string;
74
+ taskId: string;
75
+ score: number;
76
+ source: string;
77
+ reason: string;
78
+ }): void;
79
+ /**
80
+ * 记录 started 事件
81
+ */
82
+ logStarted(params: {
83
+ traceId: string;
84
+ taskId: string;
85
+ }): void;
86
+ /**
87
+ * 记录 analyzing 事件
88
+ */
89
+ logAnalyzing(params: {
90
+ traceId: string;
91
+ taskId: string;
92
+ analysisType?: string;
93
+ }): void;
94
+ /**
95
+ * 记录 principle_generated 事件
96
+ */
97
+ logPrincipleGenerated(params: {
98
+ traceId: string;
99
+ taskId: string;
100
+ principleId: string;
101
+ principleText: string;
102
+ }): void;
103
+ /**
104
+ * 记录 completed 事件
105
+ */
106
+ logCompleted(params: {
107
+ traceId: string;
108
+ taskId: string;
109
+ resolution: 'marker_detected' | 'auto_completed_timeout' | 'manual';
110
+ durationMs?: number;
111
+ principlesGenerated?: number;
112
+ }): void;
113
+ /**
114
+ * 记录 failed 事件
115
+ */
116
+ logFailed(params: {
117
+ traceId: string;
118
+ taskId: string;
119
+ error: string;
120
+ stack?: string;
121
+ }): void;
122
+ }
123
+ /**
124
+ * 获取 EvolutionLogger 实例(单例)
125
+ *
126
+ * 注意:带 trajectory 和不带 trajectory 的请求会返回不同的实例,
127
+ * 因为 trajectory 影响事件持久化行为。
128
+ */
129
+ export declare function getEvolutionLogger(workspaceDir: string, trajectory?: TrajectoryDatabase): EvolutionLogger;
130
+ /**
131
+ * 清理指定 workspace 的 logger 缓存
132
+ */
133
+ export declare function disposeEvolutionLogger(workspaceDir: string): boolean;
134
+ /**
135
+ * 清理所有 logger 缓存(用于测试或进程退出)
136
+ */
137
+ export declare function disposeAllEvolutionLoggers(): void;