autosnippet 2.9.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +12 -12
  2. package/bin/cli.js +53 -40
  3. package/config/constitution.yaml +9 -2
  4. package/dashboard/dist/assets/{icons-CH-H9x0E.js → icons-D4IWpDIk.js} +105 -100
  5. package/dashboard/dist/assets/index-CWBNcF9z.css +1 -0
  6. package/dashboard/dist/assets/index-DHtzhbuG.js +120 -0
  7. package/dashboard/dist/index.html +3 -3
  8. package/lib/cli/AiScanService.js +35 -36
  9. package/lib/cli/KnowledgeSyncService.js +345 -0
  10. package/lib/cli/SetupService.js +8 -26
  11. package/lib/cli/UpgradeService.js +28 -0
  12. package/lib/core/gateway/GatewayActionRegistry.js +48 -58
  13. package/lib/domain/index.js +16 -11
  14. package/lib/domain/knowledge/KnowledgeEntry.js +289 -0
  15. package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
  16. package/lib/domain/knowledge/Lifecycle.js +99 -0
  17. package/lib/domain/knowledge/index.js +27 -0
  18. package/lib/domain/knowledge/values/Constraints.js +128 -0
  19. package/lib/domain/knowledge/values/Content.js +69 -0
  20. package/lib/domain/knowledge/values/Quality.js +81 -0
  21. package/lib/domain/knowledge/values/Reasoning.js +70 -0
  22. package/lib/domain/knowledge/values/Relations.js +142 -0
  23. package/lib/domain/knowledge/values/Stats.js +72 -0
  24. package/lib/domain/knowledge/values/index.js +9 -0
  25. package/lib/external/ai/AiProvider.js +85 -11
  26. package/lib/external/mcp/McpServer.js +7 -5
  27. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +18 -2
  28. package/lib/external/mcp/handlers/bootstrap.js +116 -11
  29. package/lib/external/mcp/handlers/browse.js +76 -73
  30. package/lib/external/mcp/handlers/candidate.js +26 -275
  31. package/lib/external/mcp/handlers/guard.js +2 -0
  32. package/lib/external/mcp/handlers/knowledge.js +267 -0
  33. package/lib/external/mcp/handlers/structure.js +25 -23
  34. package/lib/external/mcp/handlers/system.js +10 -12
  35. package/lib/external/mcp/tools.js +134 -140
  36. package/lib/http/HttpServer.js +14 -8
  37. package/lib/http/routes/ai.js +4 -3
  38. package/lib/http/routes/extract.js +48 -4
  39. package/lib/http/routes/knowledge.js +246 -0
  40. package/lib/http/routes/search.js +12 -17
  41. package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
  42. package/lib/infrastructure/database/migrations/017_camelcase_knowledge_entries.js +107 -0
  43. package/lib/infrastructure/external/XcodeAutomation.js +187 -103
  44. package/lib/injection/ServiceContainer.js +69 -60
  45. package/lib/repository/knowledge/KnowledgeRepository.impl.js +338 -0
  46. package/lib/service/automation/DirectiveDetector.js +2 -3
  47. package/lib/service/automation/FileWatcher.js +59 -28
  48. package/lib/service/automation/XcodeIntegration.js +931 -156
  49. package/lib/service/automation/handlers/AlinkHandler.js +5 -4
  50. package/lib/service/automation/handlers/CreateHandler.js +53 -19
  51. package/lib/service/automation/handlers/DraftHandler.js +1 -1
  52. package/lib/service/automation/handlers/GuardHandler.js +183 -20
  53. package/lib/service/automation/handlers/SearchHandler.js +25 -22
  54. package/lib/service/candidate/SimilarityService.js +2 -2
  55. package/lib/service/chat/AnalystAgent.js +9 -0
  56. package/lib/service/chat/CandidateGuardrail.js +22 -11
  57. package/lib/service/chat/ChatAgent.js +132 -54
  58. package/lib/service/chat/ContextWindow.js +5 -5
  59. package/lib/service/chat/HandoffProtocol.js +1 -0
  60. package/lib/service/chat/ProducerAgent.js +40 -13
  61. package/lib/service/chat/ReasoningLayer.js +854 -0
  62. package/lib/service/chat/ReasoningTrace.js +329 -0
  63. package/lib/service/chat/tools.js +308 -205
  64. package/lib/service/cursor/CursorDeliveryPipeline.js +279 -0
  65. package/lib/service/cursor/KnowledgeCompressor.js +87 -0
  66. package/lib/service/cursor/RulesGenerator.js +168 -0
  67. package/lib/service/cursor/SkillsSyncer.js +268 -0
  68. package/lib/service/cursor/TokenBudget.js +58 -0
  69. package/lib/service/cursor/TopicClassifier.js +141 -0
  70. package/lib/service/guard/GuardCheckEngine.js +99 -10
  71. package/lib/service/guard/GuardService.js +57 -46
  72. package/lib/service/knowledge/ConfidenceRouter.js +159 -0
  73. package/lib/service/knowledge/KnowledgeFileWriter.js +595 -0
  74. package/lib/service/knowledge/KnowledgeService.js +802 -0
  75. package/lib/service/recipe/RecipeParser.js +3 -12
  76. package/lib/service/search/SearchEngine.js +67 -22
  77. package/lib/service/skills/SignalCollector.js +14 -9
  78. package/lib/service/skills/SkillAdvisor.js +13 -11
  79. package/lib/service/snippet/SnippetFactory.js +5 -5
  80. package/lib/service/spm/SpmService.js +15 -48
  81. package/lib/shared/RecipeReadinessChecker.js +6 -11
  82. package/package.json +1 -1
  83. package/scripts/install-cursor-skill.js +0 -6
  84. package/scripts/migrate-md-to-knowledge.mjs +364 -0
  85. package/skills/autosnippet-analysis/SKILL.md +15 -7
  86. package/skills/autosnippet-candidates/SKILL.md +8 -8
  87. package/skills/autosnippet-coldstart/SKILL.md +8 -4
  88. package/skills/autosnippet-concepts/SKILL.md +7 -6
  89. package/skills/autosnippet-create/SKILL.md +13 -13
  90. package/skills/autosnippet-intent/SKILL.md +3 -2
  91. package/skills/autosnippet-lifecycle/SKILL.md +5 -5
  92. package/skills/autosnippet-recipes/SKILL.md +18 -6
  93. package/templates/constitution.yaml +1 -1
  94. package/templates/copilot-instructions.md +6 -6
  95. package/templates/recipes-setup/README.md +3 -3
  96. package/dashboard/dist/assets/index-CqJRvYRL.js +0 -197
  97. package/dashboard/dist/assets/index-DICm9PNa.css +0 -1
  98. package/lib/cli/CandidateSyncService.js +0 -261
  99. package/lib/cli/SyncService.js +0 -356
  100. package/lib/domain/candidate/Candidate.js +0 -196
  101. package/lib/domain/candidate/CandidateRepository.js +0 -107
  102. package/lib/domain/candidate/Reasoning.js +0 -52
  103. package/lib/domain/recipe/Recipe.js +0 -421
  104. package/lib/domain/recipe/RecipeRepository.js +0 -54
  105. package/lib/domain/types/CandidateStatus.js +0 -52
  106. package/lib/http/routes/candidates.js +0 -559
  107. package/lib/http/routes/recipes.js +0 -397
  108. package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
  109. package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
  110. package/lib/service/candidate/CandidateAggregator.js +0 -52
  111. package/lib/service/candidate/CandidateFileWriter.js +0 -383
  112. package/lib/service/candidate/CandidateService.js +0 -1001
  113. package/lib/service/recipe/RecipeFileWriter.js +0 -514
  114. package/lib/service/recipe/RecipeService.js +0 -786
  115. package/lib/service/recipe/RecipeStatsTracker.js +0 -148
@@ -30,13 +30,14 @@ import { TaskPipeline } from './TaskPipeline.js';
30
30
  import { Memory } from './Memory.js';
31
31
  import { ConversationStore } from './ConversationStore.js';
32
32
  import { ContextWindow, PhaseRouter, limitToolResult } from './ContextWindow.js';
33
+ import { ReasoningLayer } from './ReasoningLayer.js';
33
34
 
34
35
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
35
36
  const PROJECT_ROOT = path.resolve(__dirname, '../../..');
36
37
  const SKILLS_DIR = path.resolve(PROJECT_ROOT, 'skills');
37
38
  const SOUL_PATH = path.resolve(PROJECT_ROOT, 'SOUL.md');
38
39
  const MAX_ITERATIONS = 6;
39
- /** 系统调用 (如 bootstrap) 允许更多迭代,因为每维度需要多次 submit_candidate */
40
+ /** 系统调用 (如 bootstrap) 允许更多迭代,因为每维度需要多次 submit_knowledge */
40
41
  const MAX_ITERATIONS_SYSTEM = 30;
41
42
  /** 原生函数调用模式下,已提交 ≥ MIN_SUBMITS_FOR_EARLY_EXIT 个候选后,连续 N 轮无新提交则提前退出 */
42
43
  const MIN_SUBMITS_FOR_EARLY_EXIT = 1;
@@ -69,23 +70,23 @@ const SYSTEM_CONTINUATION_PROMPT = `你的分析计划很好。但你需要 **
69
70
  请现在开始执行:
70
71
  1. 用 \`search_project_code\` 搜索项目代码获取真实示例
71
72
  2. 用 \`read_project_file\` 查看完整文件内容
72
- 3. 对每个值得保留的信号,用 \`submit_candidate\` 提交候选
73
+ 3. 对每个值得保留的信号,用 \`submit_knowledge\` 提交候选
73
74
 
74
75
  ⚡ 推荐使用 batch_actions 一次提交多条候选:
75
76
  \`\`\`batch_actions
76
77
  [
77
- {"tool": "submit_candidate", "params": {"title": "[Bootstrap] xxx/子主题", "code": "# 标题 — 项目特写\\n\\n> 摘要...\\n\\n描述和代码交织...", "language": "objectivec", "category": "Service", "summary": "...", "tags": ["bootstrap"], "source": "bootstrap", "reasoning": {"whyStandard": "...", "sources": ["file1"], "confidence": 0.7}}},
78
- {"tool": "submit_candidate", "params": {"title": "...", "code": "...", ...}}
78
+ {"tool": "submit_knowledge", "params": {"title": "[Bootstrap] xxx/子主题", "code": "# 标题 — 项目特写\\n\\n> 摘要...\\n\\n描述和代码交织...", "language": "objectivec", "category": "Service", "summary": "...", "tags": ["bootstrap"], "source": "bootstrap", "reasoning": {"whyStandard": "...", "sources": ["file1"], "confidence": 0.7}}},
79
+ {"tool": "submit_knowledge", "params": {"title": "...", "code": "...", ...}}
79
80
  ]
80
81
  \`\`\`
81
82
 
82
83
  请立即开始执行,不要再输出分析文字。`;
83
84
 
84
85
  /**
85
- * 系统调用提交提示 — 当 AI 做了工具调用(search/read)、写了分析文本,但没调 submit_candidate 时注入
86
- * 引导 AI 将已有分析转化为实际的 submit_candidate 调用
86
+ * 系统调用提交提示 — 当 AI 做了工具调用(search/read)、写了分析文本,但没调 submit_knowledge 时注入
87
+ * 引导 AI 将已有分析转化为实际的 submit_knowledge 调用
87
88
  */
88
- const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项目信息。但你还没有调用 \`submit_candidate\` 提交任何候选。
89
+ const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项目信息。但你还没有调用 \`submit_knowledge\` 提交任何候选。
89
90
 
90
91
  **你的分析不能只停留在文字描述层面** — 必须通过工具调用将分析结果持久化。
91
92
 
@@ -93,7 +94,7 @@ const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项
93
94
 
94
95
  \`\`\`batch_actions
95
96
  [
96
- {"tool": "submit_candidate", "params": {
97
+ {"tool": "submit_knowledge", "params": {
97
98
  "title": "[Bootstrap] 维度/子主题",
98
99
  "code": "# 标题 — 项目特写\\n\\n> 本项目使用 XX 模式, N 个文件采用此写法\\n\\n描述...\\n\\n\`\`\`objc\\n// 真实代码示例\\n\`\`\`\\n\\n要点说明...",
99
100
  "language": "objectivec",
@@ -103,11 +104,11 @@ const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项
103
104
  "source": "bootstrap",
104
105
  "reasoning": {"whyStandard": "为什么值得保留", "sources": ["真实文件名"], "confidence": 0.7}
105
106
  }},
106
- {"tool": "submit_candidate", "params": {...}}
107
+ {"tool": "submit_knowledge", "params": {...}}
107
108
  ]
108
109
  \`\`\`
109
110
 
110
- 将你上面分析出的每个有价值的发现都转化为一条 submit_candidate 调用。code 字段写「项目特写」风格: 描述和代码交织,用项目真实类名和代码。`;
111
+ 将你上面分析出的每个有价值的发现都转化为一条 submit_knowledge 调用。code 字段写「项目特写」风格: 描述和代码交织,用项目真实类名和代码。`;
111
112
 
112
113
  export class ChatAgent {
113
114
  #toolRegistry;
@@ -342,7 +343,7 @@ export class ChatAgent {
342
343
  // Bootstrap 场景限制可用工具集 (支持外部覆盖)
343
344
  const effectiveAllowedTools = allowedTools || (isSystem ? [
344
345
  'search_project_code', 'read_project_file',
345
- 'submit_candidate', 'submit_with_check',
346
+ 'submit_knowledge', 'submit_with_check',
346
347
  'list_project_structure', 'get_file_summary', 'semantic_search_code',
347
348
  // AST 结构化分析工具
348
349
  'get_project_overview', 'get_class_hierarchy', 'get_class_info',
@@ -373,6 +374,16 @@ export class ChatAgent {
373
374
  const MIN_EXPLORE_ITERS = 16; // 最少探索轮次(冷启动质量保障)
374
375
  const STALE_THRESHOLD = 3; // 连续无新信息轮次触发收敛
375
376
 
377
+ // ── Layer 4: ReasoningLayer — 推理链采集 + 结构化观察 + 反思 + 规划 ──
378
+ const reasoning = new ReasoningLayer({
379
+ enabled: true,
380
+ reflectionEnabled: isSystem, // 仅 system 模式启用反思
381
+ reflectionInterval: isSystem ? 5 : 0,
382
+ planningEnabled: isSystem, // 仅 system 模式启用规划
383
+ replanInterval: isSystem ? 8 : 0,
384
+ deviationThreshold: 0.6,
385
+ });
386
+
376
387
  // ── 主循环 ──
377
388
  while (true) {
378
389
  // PhaseRouter tick + 退出检查
@@ -384,7 +395,7 @@ export class ChatAgent {
384
395
  }
385
396
  // PhaseRouter 因 maxIterations 强制转入 SUMMARIZE → 注入收尾 nudge
386
397
  if (phaseRouter.consumeForcedSummarize()) {
387
- const submitCount = toolCalls.filter(tc => tc.tool === 'submit_candidate' || tc.tool === 'submit_with_check').length;
398
+ const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
388
399
  ctx.appendUserNudge(
389
400
  `⚠️ 轮次即将耗尽 (${phaseRouter.totalIterations}/${budget.maxIterations}),**必须立即结束**。请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹),不要再调用任何工具。\n` +
390
401
  `\`\`\`json\n{"dimensionDigest":{"summary":"分析总结","candidateCount":${submitCount},"keyFindings":["发现"],"crossRefs":{},"gaps":["缺口"],"remainingTasks":[{"signal":"未处理信号","reason":"轮次耗尽","priority":"high","searchHints":["搜索词"]}]}}\n\`\`\`\n> remainingTasks: 列出未来得及处理的信号。已覆盖则留空 \`[]\`。`
@@ -418,7 +429,7 @@ export class ChatAgent {
418
429
  } else if (isSystem && iterationCount >= maxIter && !ctx.__gracefulExitInjected) {
419
430
  // 达到上限 → 注入收尾消息让 AI 快速总结(而非硬中断)
420
431
  this.#logger.info(`[ChatAgent] Iteration cap reached (${iterationCount}/${maxIter}) — injecting graceful exit nudge`);
421
- const submitCount = toolCalls.filter(tc => tc.tool === 'submit_candidate' || tc.tool === 'submit_with_check').length;
432
+ const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
422
433
  ctx.appendUserNudge(
423
434
  `⚠️ 你已使用 ${iterationCount}/${maxIter} 轮次,**必须立即结束**。请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹),不要再调用任何工具。\n` +
424
435
  `\`\`\`json\n{"dimensionDigest":{"summary":"分析总结","candidateCount":${submitCount},"keyFindings":["发现"],"crossRefs":{},"gaps":["缺口"],"remainingTasks":[{"signal":"未处理信号","reason":"轮次耗尽","priority":"high","searchHints":["搜索词"]}]}}\n\`\`\`\n> remainingTasks: 列出未来得及处理的信号。已覆盖则留空 \`[]\`。`
@@ -450,6 +461,15 @@ export class ChatAgent {
450
461
  currentChoice = 'auto';
451
462
  }
452
463
 
464
+ // ── ReasoningLayer Hook 1: beforeAICall — 开始新轮次 + 反思检查 ──
465
+ const reflectionNudge = reasoning.beforeAICall(iterationCount, {
466
+ explorationMetrics, budget, phase: phaseRouter?.phase,
467
+ });
468
+ if (reflectionNudge) {
469
+ ctx.appendUserNudge(reflectionNudge);
470
+ this.#logger.info(`[ChatAgent] 💭 injected reflection at iteration ${iterationCount}`);
471
+ }
472
+
453
473
  // ── 压缩检查 (每次 AI 调用前) ──
454
474
  const compactResult = ctx.compactIfNeeded();
455
475
  if (compactResult.level > 0) {
@@ -508,6 +528,9 @@ export class ChatAgent {
508
528
  this.#logger.info(`[ChatAgent] ✓ AI returned text in ${aiDuration}ms (${(aiResult.text || '').length} chars) — "${textPreview}…"`);
509
529
  }
510
530
  consecutiveAiErrors = 0;
531
+
532
+ // ── ReasoningLayer Hook 2: afterAICall — 提取 Thought ──
533
+ reasoning.afterAICall(aiResult, 'native');
511
534
  } catch (aiErr) {
512
535
  consecutiveAiErrors++;
513
536
  this.#logger.warn(`[ChatAgent] AI call failed (attempt ${consecutiveAiErrors}): ${aiErr.message}`);
@@ -518,10 +541,13 @@ export class ChatAgent {
518
541
  this.#logger.warn(`[ChatAgent] 🛑 circuit breaker is OPEN — skipping to summary`);
519
542
  break;
520
543
  }
544
+ reasoning.afterRound();
521
545
  return {
522
546
  reply: `抱歉,AI 服务暂时不可用(${aiErr.message})。请稍后重试,或检查 API 配置。`,
523
547
  toolCalls,
524
548
  hasContext: toolCalls.length > 0,
549
+ reasoningTrace: reasoning.trace,
550
+ reasoningQuality: reasoning.getQualityMetrics(),
525
551
  };
526
552
  }
527
553
 
@@ -531,10 +557,13 @@ export class ChatAgent {
531
557
  ctx.resetToPromptOnly();
532
558
  break;
533
559
  }
560
+ reasoning.afterRound();
534
561
  return {
535
562
  reply: `抱歉,AI 服务暂时不可用(${aiErr.message})。请稍后重试,或检查 API 配置。`,
536
563
  toolCalls,
537
564
  hasContext: toolCalls.length > 0,
565
+ reasoningTrace: reasoning.trace,
566
+ reasoningQuality: reasoning.getQualityMetrics(),
538
567
  };
539
568
  }
540
569
  await new Promise(r => setTimeout(r, 2000));
@@ -550,8 +579,9 @@ export class ChatAgent {
550
579
  if (aiResult.text) {
551
580
  const reply = this.#cleanFinalAnswer(aiResult.text);
552
581
  const totalDuration = Date.now() - execStartTime;
582
+ reasoning.afterRound();
553
583
  this.#logger.info(`[ChatAgent] ✅ final answer (graceful exit, forced) — ${reply.length} chars, ${toolCalls.length} tool calls, ${totalDuration}ms`);
554
- return { reply, toolCalls, hasContext: toolCalls.length > 0 };
584
+ return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
555
585
  }
556
586
  // 无文本时继续循环,下一轮硬上限兜底
557
587
  continue;
@@ -594,6 +624,9 @@ export class ChatAgent {
594
624
  const summarized = this.#summarizeResult(toolResult);
595
625
  toolCalls.push({ tool: fc.name, params: fc.args, result: summarized });
596
626
 
627
+ // ── ReasoningLayer Hook 3: afterToolExec — 记录 Action + 构建 Observation ──
628
+ reasoning.afterToolExec(fc.name, fc.args, toolResult, explorationMetrics);
629
+
597
630
  // ── 探索进度追踪 (非 PhaseRouter 路径) ──
598
631
  if (!phaseRouter && isSystem) {
599
632
  explorationMetrics.totalToolCalls++;
@@ -671,7 +704,7 @@ export class ChatAgent {
671
704
  explorationMetrics.uniqueQueries.add(qKey);
672
705
  foundNewInfo = true;
673
706
  }
674
- } else if (fc.name !== 'submit_candidate' && fc.name !== 'submit_with_check') {
707
+ } else if (fc.name !== 'submit_knowledge' && fc.name !== 'submit_with_check') {
675
708
  // 其他未分类工具 — 首次算新信息,之后同工具名+同参数去重
676
709
  const qKey = `${fc.name}:${JSON.stringify(fc.args || {}).substring(0, 80)}`;
677
710
  if (!explorationMetrics.uniqueQueries.has(qKey)) {
@@ -692,7 +725,7 @@ export class ChatAgent {
692
725
  let resultStr = limitToolResult(fc.name, toolResult, quota);
693
726
 
694
727
  // ── 重复提交 / 维度范围校验 ──
695
- if (fc.name === 'submit_candidate' || fc.name === 'submit_with_check') {
728
+ if (fc.name === 'submit_knowledge' || fc.name === 'submit_with_check') {
696
729
  const title = fc.args?.title || fc.args?.category || '';
697
730
  const isRejected = typeof toolResult === 'object' && toolResult?.status === 'rejected';
698
731
  const isError = typeof toolResult === 'object' && (toolResult?.error || toolResult?.status === 'error');
@@ -721,6 +754,14 @@ export class ChatAgent {
721
754
  explorationMetrics.staleRounds++;
722
755
  }
723
756
 
757
+ // ── ReasoningLayer Hook 4: afterRound — 关闭轮次 + 写入摘要 ──
758
+ reasoning.afterRound({
759
+ newInfoCount: roundHasNewInfo ? 1 : 0,
760
+ totalCalls: activeCalls.length,
761
+ submitCount: roundSubmitCount,
762
+ explorationMetrics,
763
+ });
764
+
724
765
  // ── PhaseRouter 更新 ──
725
766
  if (phaseRouter) {
726
767
  const transition = phaseRouter.update({
@@ -734,7 +775,7 @@ export class ChatAgent {
734
775
  // 需要一条 user 消息明确告知 AI 切换到提交模式
735
776
  if (transition.transitioned && transition.newPhase === 'PRODUCE') {
736
777
  ctx.appendUserNudge(
737
- '你已充分探索了项目代码,现在请开始调用 submit_candidate 工具来提交你发现的知识候选。不要再搜索,直接提交。'
778
+ '你已充分探索了项目代码,现在请开始调用 submit_knowledge 工具来提交你发现的知识候选。不要再搜索,直接提交。'
738
779
  );
739
780
  this.#logger.info('[ChatAgent] 📝 injected PRODUCE transition nudge');
740
781
  }
@@ -743,7 +784,7 @@ export class ChatAgent {
743
784
  // skill-only 维度从 EXPLORE 直接进入 SUMMARIZE (跳过 PRODUCE),
744
785
  // 需要明确告知 AI 输出 dimensionDigest JSON
745
786
  if (transition.transitioned && transition.newPhase === 'SUMMARIZE') {
746
- const submitCount = toolCalls.filter(tc => tc.tool === 'submit_candidate' || tc.tool === 'submit_with_check').length;
787
+ const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
747
788
  ctx.appendUserNudge(
748
789
  `你已完成分析探索。请在回复中直接输出 dimensionDigest JSON(用 \`\`\`json 包裹),包含以下字段:\n\`\`\`json\n{"dimensionDigest":{"summary":"分析总结(100-200字)","candidateCount":${submitCount},"keyFindings":["关键发现"],"crossRefs":{},"gaps":["未覆盖方面"],"remainingTasks":[{"signal":"未处理的信号/主题","reason":"未完成原因(如:提交上限已达)","priority":"high|medium|low","searchHints":["建议搜索词"]}]}}\n\`\`\`\n> 如果所有信号都已覆盖,remainingTasks 留空数组 \`[]\`。如果有未来得及处理的信号,请在此标记,系统会在下次运行时续传。`
749
790
  );
@@ -781,7 +822,7 @@ export class ChatAgent {
781
822
  // 注入 nudge 让 AI 再输出一次 digest JSON
782
823
  if (transition.transitioned) {
783
824
  ctx.appendAssistantText(aiResult.text || '');
784
- const submitCount = toolCalls.filter(tc => tc.tool === 'submit_candidate' || tc.tool === 'submit_with_check').length;
825
+ const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
785
826
  ctx.appendUserNudge(
786
827
  `请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹):\n\`\`\`json\n{"dimensionDigest":{"summary":"分析总结","candidateCount":${submitCount},"keyFindings":["发现"],"crossRefs":{},"gaps":["缺口"],"remainingTasks":[{"signal":"未处理的信号","reason":"原因","priority":"high","searchHints":["搜索词"]}]}}\n\`\`\`\n> remainingTasks: 记录未来得及处理的信号。已全部覆盖则留空 \`[]\`。`
787
828
  );
@@ -791,8 +832,9 @@ export class ChatAgent {
791
832
  // 已在 SUMMARIZE 阶段 — 这就是最终回答
792
833
  const reply = this.#cleanFinalAnswer(aiResult.text || '');
793
834
  const totalDuration = Date.now() - execStartTime;
835
+ reasoning.afterRound();
794
836
  this.#logger.info(`[ChatAgent] ✅ final answer — ${reply.length} chars, ${phaseRouter.totalIterations} iters, ${toolCalls.length} tool calls, ${totalDuration}ms`);
795
- return { reply, toolCalls, hasContext: toolCalls.length > 0 };
837
+ return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
796
838
  }
797
839
 
798
840
  if (!transition.transitioned) {
@@ -802,7 +844,7 @@ export class ChatAgent {
802
844
  ctx.appendAssistantText(aiResult.text || '');
803
845
  if (phaseRouter.phase === 'PRODUCE') {
804
846
  ctx.appendUserNudge(
805
- '你的分析很好。请继续调用 submit_candidate 提交你发现的知识候选,每个值得记录的模式/实践都应该提交。'
847
+ '你的分析很好。请继续调用 submit_knowledge 提交你发现的知识候选,每个值得记录的模式/实践都应该提交。'
806
848
  );
807
849
  this.#logger.info('[ChatAgent] 📝 injected submit nudge (text in PRODUCE, not transitioning)');
808
850
  }
@@ -811,8 +853,9 @@ export class ChatAgent {
811
853
  // 非生产阶段的未转换文本 = 最终回答
812
854
  const reply = this.#cleanFinalAnswer(aiResult.text || '');
813
855
  const totalDuration = Date.now() - execStartTime;
856
+ reasoning.afterRound();
814
857
  this.#logger.info(`[ChatAgent] ✅ final answer — ${reply.length} chars, ${phaseRouter.totalIterations} iters, ${toolCalls.length} tool calls, ${totalDuration}ms`);
815
- return { reply, toolCalls, hasContext: toolCalls.length > 0 };
858
+ return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
816
859
  }
817
860
 
818
861
  // 其他阶段的文字回答 → 继续循环(PhaseRouter 已自动转换阶段)
@@ -824,21 +867,27 @@ export class ChatAgent {
824
867
  if (ctx.__gracefulExitInjected || !isSystem) {
825
868
  const reply = this.#cleanFinalAnswer(aiResult.text || '');
826
869
  const totalDuration = Date.now() - execStartTime;
870
+ reasoning.afterRound();
827
871
  this.#logger.info(`[ChatAgent] ✅ final answer (${ctx.__gracefulExitInjected ? 'graceful exit' : 'user'}) — ${reply.length} chars, ${toolCalls.length} tool calls, ${totalDuration}ms`);
828
- return { reply, toolCalls, hasContext: toolCalls.length > 0 };
872
+ return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
829
873
  }
830
874
 
831
875
  // system 模式非 graceful exit 的文字回答(理论上不应到这里)
832
876
  const reply = this.#cleanFinalAnswer(aiResult.text || '');
833
877
  const totalDuration = Date.now() - execStartTime;
878
+ reasoning.afterRound();
834
879
  this.#logger.info(`[ChatAgent] ✅ final answer — ${reply.length} chars, ${toolCalls.length} tool calls, ${totalDuration}ms`);
835
- return { reply, toolCalls, hasContext: toolCalls.length > 0 };
880
+ return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
836
881
  }
837
882
 
838
883
  // ── 循环退出: 产出 dimensionDigest 总结 ──
839
- return this.#produceForcedSummary({
884
+ reasoning.afterRound();
885
+ const forcedResult = await this.#produceForcedSummary({
840
886
  source, toolCalls, toolSchemas, ctx, phaseRouter, execStartTime,
841
887
  });
888
+ forcedResult.reasoningTrace = reasoning.trace;
889
+ forcedResult.reasoningQuality = reasoning.getQualityMetrics();
890
+ return forcedResult;
842
891
  }
843
892
 
844
893
  /**
@@ -850,7 +899,7 @@ export class ChatAgent {
850
899
  this.#logger.info(`[ChatAgent] ⚠ producing forced summary (${iterations} iters, ${toolCalls.length} calls)`);
851
900
 
852
901
  const candidateCount = toolCalls.filter(tc =>
853
- tc.tool === 'submit_candidate' || tc.tool === 'submit_with_check'
902
+ tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check'
854
903
  ).length;
855
904
 
856
905
  let finalReply;
@@ -865,7 +914,7 @@ export class ChatAgent {
865
914
  if (isCircuitOpen) throw new Error('circuit open — skip to synthetic digest');
866
915
 
867
916
  const submitSummary = toolCalls
868
- .filter(tc => tc.tool === 'submit_candidate' || tc.tool === 'submit_with_check')
917
+ .filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
869
918
  .map((tc, i) => `${i + 1}. ${tc.params?.title || tc.params?.category || 'untitled'}`)
870
919
  .join('\n');
871
920
 
@@ -912,7 +961,7 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
912
961
  this.#logger.warn(`[ChatAgent] forced summary AI call failed: ${err.message}`);
913
962
  // 合成 digest 兜底
914
963
  const titles = toolCalls
915
- .filter(tc => tc.tool === 'submit_candidate' || tc.tool === 'submit_with_check')
964
+ .filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
916
965
  .map(tc => tc.params?.title || 'untitled');
917
966
  finalReply = `\`\`\`json
918
967
  {
@@ -954,10 +1003,19 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
954
1003
  let consecutiveAiErrors = 0;
955
1004
  const maxIter = source === 'system' ? MAX_ITERATIONS_SYSTEM : MAX_ITERATIONS;
956
1005
 
1006
+ // ── ReasoningLayer (文本解析模式) ──
1007
+ const reasoning = new ReasoningLayer({
1008
+ enabled: true,
1009
+ reflectionEnabled: false, // 文本模式暂不启用反思(避免复杂度)
1010
+ });
1011
+
957
1012
  while (iterations < maxIter) {
958
1013
  iterations++;
959
1014
  const iterStartTime = Date.now();
960
1015
 
1016
+ // ── ReasoningLayer Hook 1: beforeAICall ──
1017
+ reasoning.beforeAICall(iterations);
1018
+
961
1019
  let response;
962
1020
  try {
963
1021
  this.#logger.info(`[ChatAgent] 🔄 text iteration ${iterations}/${maxIter} — calling AI (${messages.length} messages)`);
@@ -969,24 +1027,33 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
969
1027
  const responsePreview = (response || '').substring(0, 120).replace(/\n/g, '↵');
970
1028
  this.#logger.info(`[ChatAgent] ✓ AI responded in ${aiDuration}ms (${(response || '').length} chars) — "${responsePreview}…"`);
971
1029
  consecutiveAiErrors = 0;
1030
+
1031
+ // ── ReasoningLayer Hook 2: afterAICall ──
1032
+ reasoning.afterAICall(response, 'text');
972
1033
  } catch (aiErr) {
973
1034
  consecutiveAiErrors++;
974
1035
  this.#logger.warn(`[ChatAgent] AI call failed (attempt ${consecutiveAiErrors}): ${aiErr.message}`);
975
1036
 
976
1037
  // 熔断器已打开 → 立即返回
977
1038
  if (aiErr.code === 'CIRCUIT_OPEN') {
1039
+ reasoning.afterRound();
978
1040
  return {
979
1041
  reply: `抱歉,AI 服务暂时不可用(${aiErr.message})。请稍后重试,或检查 API 配置。`,
980
1042
  toolCalls,
981
1043
  hasContext: toolCalls.length > 0,
1044
+ reasoningTrace: reasoning.trace,
1045
+ reasoningQuality: reasoning.getQualityMetrics(),
982
1046
  };
983
1047
  }
984
1048
 
985
1049
  if (consecutiveAiErrors >= 2) {
1050
+ reasoning.afterRound();
986
1051
  return {
987
1052
  reply: `抱歉,AI 服务暂时不可用(${aiErr.message})。请稍后重试,或检查 API 配置。`,
988
1053
  toolCalls,
989
1054
  hasContext: toolCalls.length > 0,
1055
+ reasoningTrace: reasoning.trace,
1056
+ reasoningQuality: reasoning.getQualityMetrics(),
990
1057
  };
991
1058
  }
992
1059
  await new Promise(r => setTimeout(r, 2000));
@@ -997,7 +1064,7 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
997
1064
 
998
1065
  if (!actions) {
999
1066
  // ── 系统调用自动续跑 ──
1000
- const hasSubmits = toolCalls.some(tc => tc.tool === 'submit_candidate' || tc.tool === 'submit_with_check');
1067
+ const hasSubmits = toolCalls.some(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check');
1001
1068
  if (source === 'system' && iterations < maxIter && !hasSubmits) {
1002
1069
  if (this.#looksLikeIncompleteStep(response)) {
1003
1070
  this.#logger.info(`[ChatAgent] 🔄 detected planning-only response at iteration ${iterations}, injecting continuation prompt`);
@@ -1017,9 +1084,10 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
1017
1084
 
1018
1085
  const reply = this.#cleanFinalAnswer(response);
1019
1086
  const totalDuration = Date.now() - execStartTime;
1087
+ reasoning.afterRound();
1020
1088
  this.#logger.info(`[ChatAgent] ✅ text final answer — ${reply.length} chars, ${iterations} iterations, ${toolCalls.length} tool calls, ${totalDuration}ms total`);
1021
1089
 
1022
- return { reply, toolCalls, hasContext: toolCalls.length > 0 };
1090
+ return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
1023
1091
  }
1024
1092
 
1025
1093
  // 执行工具
@@ -1058,8 +1126,17 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
1058
1126
  result: summarized,
1059
1127
  });
1060
1128
  batchResults.push({ tool: action.tool, result: toolResult });
1129
+
1130
+ // ── ReasoningLayer Hook 3: afterToolExec ──
1131
+ reasoning.afterToolExec(action.tool, action.params, toolResult);
1061
1132
  }
1062
1133
 
1134
+ // ── ReasoningLayer Hook 4: afterRound ──
1135
+ reasoning.afterRound({
1136
+ totalCalls: actions.length,
1137
+ submitCount: 0,
1138
+ });
1139
+
1063
1140
  // 将工具结果注入为下一轮 prompt
1064
1141
  let observation;
1065
1142
  if (batchResults.length === 1) {
@@ -1098,7 +1175,7 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
1098
1175
  }
1099
1176
 
1100
1177
  const finalReply = this.#cleanFinalAnswer(finalResponse);
1101
- return { reply: finalReply, toolCalls, hasContext: toolCalls.length > 0 };
1178
+ return { reply: finalReply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
1102
1179
  }
1103
1180
 
1104
1181
  /**
@@ -1272,13 +1349,13 @@ ${highSim.map(s => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
1272
1349
  */
1273
1350
  async #taskDiscoverAllRelations({ batchSize = 20 } = {}) {
1274
1351
  const ctx = this.#getToolContext();
1275
- const recipeService = ctx.container.get('recipeService');
1276
- if (!recipeService) throw new Error('RecipeService 不可用');
1352
+ const knowledgeService = ctx.container.get('knowledgeService');
1353
+ if (!knowledgeService) throw new Error('KnowledgeService 不可用');
1277
1354
 
1278
1355
  if (!ctx.aiProvider) throw new Error('AI Provider 未配置,请先设置 API Key');
1279
1356
 
1280
- // 获取所有 recipe
1281
- const { items = [], data = [] } = await recipeService.listRecipes({}, { page: 1, pageSize: 500 });
1357
+ // 获取所有活跃知识条目
1358
+ const { items = [], data = [] } = await knowledgeService.list({ lifecycle: 'active' }, { page: 1, pageSize: 500 });
1282
1359
  const recipes = items.length > 0 ? items : data;
1283
1360
  if (recipes.length < 2) return { discovered: 0, totalPairs: 0, message: `只有 ${recipes.length} 条 Recipe,至少需要 2 条` };
1284
1361
 
@@ -1334,10 +1411,10 @@ ${highSim.map(s => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
1334
1411
  */
1335
1412
  async #taskFullEnrich({ status = 'pending', maxCount = 50 } = {}) {
1336
1413
  const ctx = this.#getToolContext();
1337
- const candidateService = ctx.container.get('candidateService');
1414
+ const knowledgeService = ctx.container.get('knowledgeService');
1338
1415
 
1339
- const { items = [], data = [] } = await candidateService.listCandidates(
1340
- { status }, { page: 1, pageSize: maxCount }
1416
+ const { items = [], data = [] } = await knowledgeService.list(
1417
+ { lifecycle: status }, { page: 1, pageSize: maxCount }
1341
1418
  );
1342
1419
  const candidates = items.length > 0 ? items : data;
1343
1420
  if (candidates.length === 0) return { enriched: 0, message: 'No candidates to enrich' };
@@ -1363,10 +1440,10 @@ ${highSim.map(s => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
1363
1440
  */
1364
1441
  async #taskQualityAudit({ threshold = 0.6, maxCount = 100 } = {}) {
1365
1442
  const ctx = this.#getToolContext();
1366
- const recipeService = ctx.container.get('recipeService');
1443
+ const knowledgeService = ctx.container.get('knowledgeService');
1367
1444
 
1368
- const { items = [], data = [] } = await recipeService.listRecipes(
1369
- { status: 'active' }, { page: 1, pageSize: maxCount }
1445
+ const { items = [], data = [] } = await knowledgeService.list(
1446
+ { lifecycle: 'active' }, { page: 1, pageSize: maxCount }
1370
1447
  );
1371
1448
  const recipes = items.length > 0 ? items : data;
1372
1449
  if (recipes.length === 0) return { total: 0, lowQuality: [], message: 'No active recipes' };
@@ -1493,7 +1570,7 @@ ${code.substring(0, 3000)}
1493
1570
  // 核心工具 — 使用最频繁,直接展示完整 schema
1494
1571
  const coreTools = new Set([
1495
1572
  'search_project_code', 'read_project_file',
1496
- 'search_knowledge', 'submit_candidate', 'submit_with_check', 'analyze_code',
1573
+ 'search_knowledge', 'submit_knowledge', 'submit_with_check', 'analyze_code',
1497
1574
  'bootstrap_knowledge', 'load_skill', 'suggest_skills',
1498
1575
  'create_skill', 'knowledge_overview', 'get_tool_details',
1499
1576
  'plan_task', 'review_my_output',
@@ -1549,8 +1626,8 @@ ${skillSection}
1549
1626
 
1550
1627
  \`\`\`batch_actions
1551
1628
  [
1552
- {"tool": "submit_candidate", "params": {"title": "...", "code": "..."}},
1553
- {"tool": "submit_candidate", "params": {"title": "...", "code": "..."}}
1629
+ {"tool": "submit_knowledge", "params": {"title": "...", "code": "..."}},
1630
+ {"tool": "submit_knowledge", "params": {"title": "...", "code": "..."}}
1554
1631
  ]
1555
1632
  \`\`\`
1556
1633
 
@@ -1616,7 +1693,7 @@ ${this.#projectBriefingCache}
1616
1693
  2. **定向探索** → get_file_summary 快速了解文件角色
1617
1694
  3. **深入研读** → search_project_code / read_project_file 获取真实代码
1618
1695
  4. **语义发现** → semantic_search_code 在知识库查找相关知识
1619
- 5. **知识产出** → submit_candidate 提交有价值的发现
1696
+ 5. **知识产出** → submit_knowledge 提交有价值的发现
1620
1697
 
1621
1698
  ## 高效使用工具(节省轮次)
1622
1699
  - **批量搜索**: search_project_code({ patterns: ["keywordA", "keywordB", "keywordC"] })
@@ -1624,7 +1701,7 @@ ${this.#projectBriefingCache}
1624
1701
  - 合并同类请求为一次调用,避免逐个搜索/读取浪费轮次。
1625
1702
 
1626
1703
  ## 「项目特写」= 基本用法 + 项目特征融合
1627
- submit_candidate 的 code 字段必须是「项目特写」— 将技术的基本用法与本项目的特征融合为一体:
1704
+ submit_knowledge 的 code 字段必须是「项目特写」— 将技术的基本用法与本项目的特征融合为一体:
1628
1705
  1. **项目选择了什么**: 采用了哪种写法/模式/约定
1629
1706
  2. **为什么这样选**: 统计数据(N 个文件、占比 M%)
1630
1707
  3. **项目禁止什么**: 被放弃的写法、反模式、显式禁用标记
@@ -1998,19 +2075,20 @@ submit_candidate 的 code 字段必须是「项目特写」— 将技术的基
1998
2075
  try {
1999
2076
  const db = this.#container?.get('database');
2000
2077
  if (!db) return '';
2001
- // knowledge_type → kind 映射:
2078
+ // knowledgeType → kind 映射:
2002
2079
  // rule: code-standard, code-style, best-practice, boundary-constraint
2003
2080
  // pattern: code-pattern, architecture, solution
2004
2081
  // fact: code-relation, inheritance, call-chain, data-flow, module-dependency
2082
+ // V3: knowledge_entries 统一表(candidates 已合并,lifecycle 替代 status)
2005
2083
  const stats = db.prepare(`
2006
2084
  SELECT
2007
- (SELECT COUNT(*) FROM recipes) as recipeCount,
2008
- (SELECT COUNT(*) FROM recipes WHERE knowledge_type IN ('code-standard','code-style','best-practice','boundary-constraint')) as ruleCount,
2009
- (SELECT COUNT(*) FROM recipes WHERE knowledge_type IN ('code-pattern','architecture','solution')) as patternCount,
2010
- (SELECT COUNT(*) FROM recipes WHERE knowledge_type IN ('code-relation','inheritance','call-chain','data-flow','module-dependency')) as factCount,
2011
- (SELECT COUNT(*) FROM recipes WHERE knowledge_type = 'boundary-constraint') as guardRuleCount,
2012
- (SELECT COUNT(*) FROM candidates WHERE status='pending') as pendingCandidates,
2013
- (SELECT COUNT(*) FROM candidates) as totalCandidates
2085
+ (SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active') as recipeCount,
2086
+ (SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType IN ('code-standard','code-style','best-practice','boundary-constraint')) as ruleCount,
2087
+ (SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType IN ('code-pattern','architecture','solution')) as patternCount,
2088
+ (SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType IN ('code-relation','inheritance','call-chain','data-flow','module-dependency')) as factCount,
2089
+ (SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType = 'boundary-constraint') as guardRuleCount,
2090
+ (SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'pending') as pendingCandidates,
2091
+ (SELECT COUNT(*) FROM knowledge_entries) as totalCandidates
2014
2092
  `).get();
2015
2093
  if (!stats || stats.recipeCount === 0) {
2016
2094
  return '\n## 项目状态\n⚠️ 知识库为空。建议先执行冷启动(bootstrap_knowledge)。\n';
@@ -218,7 +218,7 @@ export class ContextWindow {
218
218
  for (const m of removed) {
219
219
  if (m.role === 'assistant' && m.toolCalls) {
220
220
  for (const tc of m.toolCalls) {
221
- if (tc.name === 'submit_candidate' || tc.name === 'submit_with_check') {
221
+ if (tc.name === 'submit_knowledge' || tc.name === 'submit_with_check') {
222
222
  this.#compactedSubmits.add(tc.args?.title || tc.args?.category || 'untitled');
223
223
  }
224
224
  }
@@ -339,7 +339,7 @@ export class ContextWindow {
339
339
  const m = this.#messages[i];
340
340
  if (m.role === 'assistant' && m.toolCalls) {
341
341
  for (const tc of m.toolCalls) {
342
- if (tc.name === 'submit_candidate' || tc.name === 'submit_with_check') {
342
+ if (tc.name === 'submit_knowledge' || tc.name === 'submit_with_check') {
343
343
  this.#compactedSubmits.add(tc.args?.title || tc.args?.category || 'untitled');
344
344
  }
345
345
  }
@@ -393,8 +393,8 @@ export class ContextWindow {
393
393
  export function limitToolResult(toolName, result, quota) {
394
394
  const { maxChars = 4000, maxMatches = 10 } = quota;
395
395
 
396
- // submit_candidate / submit_with_check 结果很短,不截断
397
- if (toolName === 'submit_candidate' || toolName === 'submit_with_check') {
396
+ // submit_knowledge / submit_with_check 结果很短,不截断
397
+ if (toolName === 'submit_knowledge' || toolName === 'submit_with_check') {
398
398
  const raw = typeof result === 'string' ? result : JSON.stringify(result);
399
399
  return raw.length > 500 ? raw.substring(0, 500) : raw;
400
400
  }
@@ -783,7 +783,7 @@ export class PhaseRouter {
783
783
  if (this.#totalSubmits === 0 && this.#phaseRounds >= 1) {
784
784
  return this.#isSkillOnly
785
785
  ? '你已收集足够信息,请在回复中直接输出 dimensionDigest JSON。'
786
- : '⚠️ 探索阶段已结束。你已收集了足够的项目信息,请 **立即** 调用 submit_candidate 提交候选。不要继续搜索,直接提交。';
786
+ : '⚠️ 探索阶段已结束。你已收集了足够的项目信息,请 **立即** 调用 submit_knowledge 提交候选。不要继续搜索,直接提交。';
787
787
  }
788
788
  if (this.#totalSubmits >= this.#budget.softSubmitLimit && this.#budget.softSubmitLimit > 0) {
789
789
  const remaining = this.#budget.maxSubmits - this.#totalSubmits;
@@ -118,6 +118,7 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
118
118
  iterations: analystResult.toolCalls?.length || 0,
119
119
  toolCallCount: analystResult.toolCalls?.length || 0,
120
120
  tokenUsage: analystResult.tokenUsage || null,
121
+ reasoningQuality: analystResult.reasoningQuality || null,
121
122
  },
122
123
  };
123
124
  }