autosnippet 3.2.7 → 3.2.9

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 (147) hide show
  1. package/bin/cli.js +13 -5
  2. package/dashboard/dist/assets/index-BTAsOZv2.js +128 -0
  3. package/dashboard/dist/assets/index-C_72Ct98.css +1 -0
  4. package/dashboard/dist/index.html +2 -2
  5. package/lib/cli/AiScanService.js +26 -29
  6. package/lib/cli/SetupService.js +1 -1
  7. package/lib/core/AstAnalyzer.js +27 -5
  8. package/lib/core/analysis/CallEdgeResolver.js +402 -0
  9. package/lib/core/analysis/CallGraphAnalyzer.js +367 -0
  10. package/lib/core/analysis/CallSiteExtractor.js +629 -0
  11. package/lib/core/analysis/DataFlowInferrer.js +57 -0
  12. package/lib/core/analysis/ImportPathResolver.js +189 -0
  13. package/lib/core/analysis/ImportRecord.js +105 -0
  14. package/lib/core/analysis/SymbolTableBuilder.js +211 -0
  15. package/lib/core/ast/ProjectGraph.js +8 -0
  16. package/lib/core/ast/lang-dart.js +352 -5
  17. package/lib/core/ast/lang-go.js +212 -10
  18. package/lib/core/ast/lang-java.js +205 -1
  19. package/lib/core/ast/lang-kotlin.js +330 -1
  20. package/lib/core/ast/lang-python.js +31 -2
  21. package/lib/core/ast/lang-rust.js +284 -3
  22. package/lib/core/ast/lang-swift.js +180 -1
  23. package/lib/core/ast/lang-typescript.js +290 -1
  24. package/lib/core/discovery/index.js +2 -2
  25. package/lib/external/ai/AiProvider.js +66 -172
  26. package/lib/external/ai/providers/GoogleGeminiProvider.js +23 -1
  27. package/lib/external/mcp/McpServer.js +1 -0
  28. package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
  29. package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
  30. package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +22 -1
  31. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
  32. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +2 -1
  33. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
  34. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
  35. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +311 -162
  36. package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +102 -7
  37. package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
  38. package/lib/external/mcp/handlers/bootstrap-external.js +9 -2
  39. package/lib/external/mcp/handlers/bootstrap-internal.js +19 -8
  40. package/lib/external/mcp/handlers/consolidated.js +9 -0
  41. package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
  42. package/lib/external/mcp/handlers/guard.js +3 -3
  43. package/lib/external/mcp/handlers/structure.js +62 -0
  44. package/lib/external/mcp/handlers/wiki-external.js +66 -3
  45. package/lib/external/mcp/tools.js +36 -1
  46. package/lib/http/HttpServer.js +1 -1
  47. package/lib/http/middleware/requestLogger.js +1 -0
  48. package/lib/http/routes/ai.js +240 -35
  49. package/lib/http/routes/candidates.js +2 -3
  50. package/lib/http/routes/extract.js +13 -11
  51. package/lib/http/routes/modules.js +2 -2
  52. package/lib/http/routes/recipes.js +9 -5
  53. package/lib/http/routes/remote.js +149 -270
  54. package/lib/http/routes/violations.js +0 -54
  55. package/lib/http/utils/sse-sessions.js +1 -1
  56. package/lib/infrastructure/logging/Logger.js +5 -4
  57. package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
  58. package/lib/injection/ServiceContainer.js +70 -28
  59. package/lib/platform/ScreenCaptureService.js +177 -0
  60. package/lib/platform/ios/index.js +2 -2
  61. package/lib/platform/ios/routes/spm.js +2 -2
  62. package/lib/platform/ios/spm/PackageSwiftParser.js +14 -3
  63. package/lib/platform/ios/spm/SpmDiscoverer.js +123 -17
  64. package/lib/platform/ios/spm/{SpmService.js → SpmHelper.js} +43 -675
  65. package/lib/platform/ios/xcode/XcodeWriteUtils.js +1 -1
  66. package/lib/service/agent/AgentEventBus.js +207 -0
  67. package/lib/service/agent/AgentFactory.js +490 -0
  68. package/lib/service/agent/AgentMessage.js +240 -0
  69. package/lib/service/agent/AgentRouter.js +228 -0
  70. package/lib/service/agent/AgentRuntime.js +1016 -0
  71. package/lib/service/agent/AgentState.js +217 -0
  72. package/lib/service/agent/IntentClassifier.js +331 -0
  73. package/lib/service/agent/LarkTransport.js +389 -0
  74. package/lib/service/agent/capabilities.js +408 -0
  75. package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
  76. package/lib/service/{chat → agent/context}/ExplorationTracker.js +77 -22
  77. package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +14 -2
  78. package/lib/service/agent/core/LoopContext.js +170 -0
  79. package/lib/service/agent/core/MessageAdapter.js +223 -0
  80. package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
  81. package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +19 -98
  82. package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
  83. package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
  84. package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
  85. package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +91 -123
  86. package/lib/service/agent/domain/insight-producer.js +267 -0
  87. package/lib/service/agent/domain/scan-prompts.js +105 -0
  88. package/lib/service/agent/forced-summary.js +266 -0
  89. package/lib/service/agent/index.js +91 -0
  90. package/lib/service/{chat → agent}/memory/ActiveContext.js +3 -1
  91. package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
  92. package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
  93. package/lib/service/{chat → agent}/memory/SessionStore.js +5 -4
  94. package/lib/service/{chat → agent}/memory/index.js +1 -1
  95. package/lib/service/agent/policies.js +442 -0
  96. package/lib/service/agent/presets.js +303 -0
  97. package/lib/service/agent/strategies.js +717 -0
  98. package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
  99. package/lib/service/agent/tools/ai-analysis.js +75 -0
  100. package/lib/service/{chat → agent}/tools/ast-graph.js +229 -32
  101. package/lib/service/{chat → agent}/tools/composite.js +2 -1
  102. package/lib/service/{chat → agent}/tools/guard.js +1 -121
  103. package/lib/service/{chat → agent}/tools/index.js +33 -22
  104. package/lib/service/{chat → agent}/tools/infrastructure.js +6 -1
  105. package/lib/service/agent/tools/knowledge-graph.js +112 -0
  106. package/lib/service/agent/tools/scan-recipe.js +189 -0
  107. package/lib/service/agent/tools/system-interaction.js +476 -0
  108. package/lib/service/automation/DirectiveDetector.js +0 -1
  109. package/lib/service/automation/FileWatcher.js +0 -8
  110. package/lib/service/automation/handlers/CreateHandler.js +7 -3
  111. package/lib/service/automation/handlers/DraftHandler.js +7 -6
  112. package/lib/service/cursor/CursorDeliveryPipeline.js +167 -1
  113. package/lib/service/knowledge/CodeEntityGraph.js +327 -2
  114. package/lib/service/knowledge/KnowledgeService.js +5 -1
  115. package/lib/service/module/ModuleService.js +49 -73
  116. package/lib/service/skills/SignalCollector.js +26 -19
  117. package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
  118. package/lib/service/wiki/WikiGenerator.js +1 -1
  119. package/lib/shared/FieldSpec.js +1 -1
  120. package/lib/shared/PathGuard.js +1 -1
  121. package/lib/shared/StyleGuide.js +1 -1
  122. package/package.json +4 -1
  123. package/resources/native-ui/screenshot.swift +228 -0
  124. package/dashboard/dist/assets/index-BaGY7kJI.css +0 -1
  125. package/dashboard/dist/assets/index-DfHY_3ln.js +0 -128
  126. package/lib/core/discovery/SpmDiscoverer.js +0 -5
  127. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -749
  128. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
  129. package/lib/http/routes/spm.js +0 -5
  130. package/lib/infrastructure/external/XcodeAutomation.js +0 -15
  131. package/lib/service/chat/ChatAgent.js +0 -1602
  132. package/lib/service/chat/Memory.js +0 -161
  133. package/lib/service/chat/ProducerAgent.js +0 -431
  134. package/lib/service/chat/ReasoningTrace.js +0 -523
  135. package/lib/service/chat/TaskPipeline.js +0 -357
  136. package/lib/service/chat/WorkingMemory.js +0 -357
  137. package/lib/service/chat/memory/PersistentMemory.js +0 -450
  138. package/lib/service/chat/tools/ai-analysis.js +0 -267
  139. package/lib/service/chat/tools/knowledge-graph.js +0 -234
  140. package/lib/service/chat/tools.js +0 -18
  141. package/lib/service/snippet/PlaceholderConverter.js +0 -5
  142. package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
  143. /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
  144. /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
  145. /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
  146. /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
  147. /package/lib/service/{chat → agent}/tools/query.js +0 -0
@@ -1,12 +1,18 @@
1
1
  /**
2
- * HandoffProtocol.js — Analyst → Producer 交接协议
2
+ * insight-gate.js — Insight 质量门控领域函数
3
3
  *
4
- * 职责:
5
- * 1. Analyst 执行结果构建 AnalysisReport
6
- * 2. 质量门控: 判断分析是否足够深入
7
- * 3. 提供重试提示构建
4
+ * 从旧 HandoffProtocol.js 完整迁移的纯函数模块:
5
+ * - 分析文本清洗 (sanitizeAnalysisText)
6
+ * - AnalysisReport 构建 (v1)
7
+ * - AnalysisArtifact 构建 (v2, 含 evidenceMap/findings/negativeSignals)
8
+ * - 多维度质量评分 (buildQualityScores)
9
+ * - 质量门控 (v1 + v2)
10
+ * - 重试 Prompt 构建
11
+ * - PipelineStrategy gate.evaluator 适配器 (insightGateEvaluator)
8
12
  *
9
- * @module HandoffProtocol
13
+ * PipelineStrategy 的 bootstrap preset 直接引用。
14
+ *
15
+ * @module insight-gate
10
16
  */
11
17
 
12
18
  import { EvidenceCollector } from './EvidenceCollector.js';
@@ -19,94 +25,58 @@ import { EvidenceCollector } from './EvidenceCollector.js';
19
25
  * 清理 Analyst 分析文本中可能泄漏的系统 nudge / graceful exit 指令。
20
26
  * 这些内容如果传给 Producer,会干扰其正常工作流。
21
27
  */
22
- function sanitizeAnalysisText(text) {
28
+ export function sanitizeAnalysisText(text) {
23
29
  if (!text) {
24
30
  return '';
25
31
  }
26
- // 移除 graceful exit nudge 及 digest 模板指令
27
32
  const patterns = [
28
33
  /\*{0,2}⚠️?\s*(?:你已使用|轮次即将耗尽|仅剩|请立即停止|必须立即结束)[^\n]*\n?/gi,
29
34
  /\*{0,2}请立即停止所有工具调用[^\n]*\*{0,2}\n?/gi,
30
35
  /请在回复中直接输出\s*dimensionDigest\s*JSON[^\n]*\n?/gi,
31
36
  /> ?(?:remainingTasks|如果所有信号都已覆盖)[^\n]*\n?/gi,
32
37
  /> ?⚠️ 严禁输出任何非 JSON 内容[^\n]*\n?/gi,
33
- // 移除 AI 回显的 dimensionDigest JSON 块(对 Producer 无价值且会干扰)
34
38
  /```json\s*\n\s*\{\s*"dimensionDigest"\s*:[\s\S]*?\n```/g,
35
-
36
- // ── AI 思考伪影清理 ──
37
-
38
- // 轮次/阶段计数器(如 "第 18/24 轮 | 验证阶段 | 剩余 6 轮")
39
39
  /^-{2,3}\s*\n\s*第\s*\d+\/\d+\s*轮[^\n]*\n(-{2,3}\s*\n)?/gm,
40
- // 独立分隔线 + 空内容
41
40
  /^-{3}\s*$/gm,
42
-
43
- // AI 规划/反思段落("计划偏差分析"、"最终总结阶段" 等)
44
41
  /^#{1,3}\s*(?:计划偏差分析|最终总结阶段|执行计划|下一步计划|分析计划)\s*\n[\s\S]*?(?=\n#{1,3}\s|\n\n(?=[^#\s-]))/gm,
45
-
46
- // 系统提示回显("(提示: ...)")
47
42
  /^\(提示[::][^)]*\)\s*\n?/gm,
48
-
49
- // AI 英文思考泄漏("Wait, I have enough information..."、"Let me..."、"I'll stop here...")
50
43
  /^(?:Wait,|Let me|I'll stop here|I will stop|I need to|I should|I have enough)[^\n]*\n?/gm,
51
-
52
- // 工具提示循环("尝试使用 `tool_name`..."、"- 尝试使用...")
53
44
  /^[-•]\s*尝试使用\s*`[^`]+`[^\n]*\n?/gm,
54
45
  /^💡\s*提示[::]?\s*\n?/gm,
55
-
56
- // 请继续 / 请接续 单行(AI 被截断后的续写请求)
57
46
  /^请(?:继续|接续)[。.]?\s*$/gm,
58
-
59
- // 📊 中期反思块("📊 中期反思 (第 N/M 轮, X% 预算):" 及后续所有内容直到下一个 ## 或 📊)
60
47
  /📊\s*中期反思\s*\([^)]*\):?\s*\n(?:[\s\S]*?(?=\n#{1,3}\s(?!探索计划|第\s*\d)|\n(?=📊)|$))/gm,
61
-
62
- // AI 思考方向列表("你最近的思考方向:" + 编号列表 + 嵌套的探索计划)
63
48
  /^你最近的思考方向:\s*\n(?:[\s\S]*?(?=\n#{1,3}\s(?!探索计划|第\s*\d)|\n(?=📊)|$))/gm,
64
-
65
- // AI 探索计划标题("### 探索计划")— 这是 AI 内部规划,不应出现在最终输出中
66
49
  /^#{1,3}\s*探索计划\s*\n(?:[\s\S]*?(?=\n#{1,3}\s(?!探索计划)|\n\n(?=[^#\s\d-])|\n(?=📊)|$))/gm,
67
- // 编号前缀的探索计划(" 1. ### 探索计划" + 紧随的编号列表项)
68
50
  /^\s*\d+\.\s+#{1,3}\s*探索计划[^\n]*\n(?:\d+\.\s+\*{0,2}[^\n]*\n?)*/gm,
69
-
70
- // AI 轮次标题("### 第 N 轮:..." 开头的规划/反思段落)
71
51
  /^#{1,3}\s*第\s*\d+\s*轮[::][^\n]*\n(?:[\s\S]*?(?=\n#{1,3}\s(?!探索计划|第\s*\d)|\n\n(?=#{1,3}\s)|\n(?=📊)|$))/gm,
72
-
73
- // 行动效率统计行("行动效率: 最近 N 轮中 X% 获取到新信息")
74
52
  /^行动效率[::][^\n]*\n?/gm,
75
53
  /^累计[::]\s*\d+\s*文件[^\n]*\n?/gm,
76
-
77
- // 计划进度("📋 计划进度: 0/1 步骤已完成")
78
54
  /^📋\s*计划进度[::][^\n]*\n?/gm,
79
-
80
- // 请评估提示块("请评估: 1. ...")
81
55
  /^请评估[::]\s*\n(?:\s*\d+\.\s+[^\n]*\n?)*/gm,
82
-
83
- // AI 对话提示回显("(请在继续调用工具前...)", "(由于当前已是第 N 轮...)", "(注意: 每一轮都必须...)")
84
56
  /^\([请由注](?:在继续|于当前|意[::])[^)]*\)\s*\n?/gm,
85
-
86
- // AI 步骤进度与计划更新("已经读取,未完成步骤..."、"计划更新:..."、"更新后的计划:...")
87
57
  /^(?:\d+\.\s+)?(?:`[^`]*`\s+)?(?:已经读取|未完成步骤仅剩|计划更新|更新后的计划)[^\n]*\n?/gm,
88
58
  /^更新后的计划[::]\s*\n(?:\s*\d+\.\s+[^\n]*\n?)*/gm,
89
-
90
- // 纯数字编号残留行(清理被上面 pattern 删除后留下的孤立编号)
91
59
  /^\s*\d+\.\s*$/gm,
60
+ /^>\s*(?:searchHints|remainingTasks|candidateCount|crossRefs|keyFindings|gaps)\s*[::][^\n]*\n?/gm,
61
+ /^\*{0,2}(?:请在|请直接|请确保|请务必|现在开始|输出你的|不要输出|不要再|不要包含)\s*[^。\n]*(?:分析文本|分析总结|分析报告|JSON|工具|输出|文本|报告)[^。\n]*[。.]?\s*\*{0,2}$/gm,
62
+ /^\*{0,2}重要\s*[::][^。\n]*\*{0,2}$/gm,
63
+ /^注意[::]\s*到达第\s*\d+\s*轮时[^\n]*$/gm,
64
+ /^第\s*\d+\/\d+\s*轮\s*\|[^\n]*$/gm,
92
65
  ];
93
66
  let cleaned = text;
94
67
  for (const pat of patterns) {
95
68
  cleaned = cleaned.replace(pat, '');
96
69
  }
97
- // 移除可能残留的空行堆积
98
70
  cleaned = cleaned.replace(/\n{3,}/g, '\n\n').trim();
99
71
  return cleaned;
100
72
  }
101
73
 
102
74
  /**
103
- * 从 Analyst 的执行结果构建 AnalysisReport
75
+ * 从 Analyst 的执行结果构建 AnalysisReport (v1)
104
76
  *
105
- * @param {object} analystResult — ChatAgent.execute() 返回值
106
- * @param {string} analystResult.reply — Analyst 的自然语言分析文本
107
- * @param {Array} analystResult.toolCalls — 工具调用记录
77
+ * @param {object} analystResult — { reply, toolCalls }
108
78
  * @param {string} dimensionId — 维度 ID
109
- * @param {object} [projectGraph] — ProjectGraph 实例 (用于从 className 反查文件路径)
79
+ * @param {object} [projectGraph] — ProjectGraph 实例
110
80
  * @returns {AnalysisReport}
111
81
  */
112
82
  export function buildAnalysisReport(analystResult, dimensionId, projectGraph = null) {
@@ -129,7 +99,6 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
129
99
  if (args.pattern || args.query) {
130
100
  searchQueries.push(args.pattern || args.query);
131
101
  }
132
- // 从搜索结果中提取文件路径
133
102
  if (typeof result === 'string') {
134
103
  const fileMatches = result.match(
135
104
  /(?:^|\n)([\w/.-]+\.(?:go|mod|sum|py|pyi|java|kt|kts|js|ts|jsx|tsx|mjs|cjs|swift|m|h|c|cpp|cc|hpp|cs|rb|rs|sql|json|yaml|yml|toml|xml|html|css|scss|less|sh|md|txt|gradle|properties|proto|vue|svelte|graphql|cfg|conf|ini|env|lock|rst))(?::\d+)?/gi
@@ -147,7 +116,6 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
147
116
  case 'get_class_info':
148
117
  if (args.className) {
149
118
  classesExplored.push(args.className);
150
- // 从 ProjectGraph 反查文件路径
151
119
  if (projectGraph) {
152
120
  const info = projectGraph.getClassInfo(args.className);
153
121
  if (info?.filePath) {
@@ -172,7 +140,7 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
172
140
  }
173
141
  }
174
142
 
175
- // 从分析文本中提取文件路径(支持多语言项目)
143
+ // 从分析文本中提取文件路径
176
144
  const text = sanitizeAnalysisText(analystResult.reply || '');
177
145
  const FILE_EXT_RE =
178
146
  /[\w/.-]+\.(?:go|mod|sum|py|pyi|java|kt|kts|js|ts|jsx|tsx|mjs|cjs|swift|m|h|c|cpp|cc|hpp|cs|rb|rs|sql|json|yaml|yml|toml|xml|html|css|scss|less|sh|md|txt|gradle|properties|proto|vue|svelte|graphql|cfg|conf|ini|env|lock|rst)\b/gi;
@@ -208,80 +176,72 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
208
176
  * 从 Analyst 执行结果构建 AnalysisArtifact (v2 增强版)
209
177
  *
210
178
  * 在 v1 AnalysisReport 基础上增加:
211
- * - evidenceMap: 文件 → 代码片段 + 摘要 (Producer 可直接引用)
179
+ * - evidenceMap: 文件 → 代码片段 + 摘要
212
180
  * - explorationLog: 工具调用意图 + 结果摘要序列
213
- * - negativeSignals: 搜索但未找到的模式 (告知 Producer 不要猜测)
181
+ * - negativeSignals: 搜索但未找到的模式
214
182
  * - findings: 来自 ActiveContext 的结构化发现
215
183
  * - qualityReport: 多维度质量评分
216
184
  *
217
- * 向后兼容: 返回对象包含 v1 的所有字段 (analysisText, referencedFiles, searchQueries, classesExplored, metadata)
218
- *
219
- * @param {object} analystResult — ChatAgent.execute() 返回值
185
+ * @param {object} analystResult { reply, toolCalls }
220
186
  * @param {string} dimensionId — 维度 ID
221
187
  * @param {object} [projectGraph] — ProjectGraph 实例
222
- * @param {object} [activeContext] — ActiveContext 实例 (提供 distill 结果)
188
+ * @param {object} [activeContext] — ActiveContext 实例
223
189
  * @returns {AnalysisArtifact}
224
190
  */
225
191
  export function buildAnalysisArtifact(analystResult, dimensionId, projectGraph = null, activeContext = null) {
226
192
  const toolCalls = analystResult.toolCalls || [];
227
193
 
228
- // §1: 构建 v1 报告 (复用已有逻辑获取 referencedFiles, searchQueries 等)
229
194
  const baseReport = buildAnalysisReport(analystResult, dimensionId, projectGraph);
230
195
 
231
- // §2: 从工具调用中收集结构化证据
232
196
  const collector = new EvidenceCollector();
233
197
  for (let i = 0; i < toolCalls.length; i++) {
234
198
  collector.processToolCall(toolCalls[i], i);
235
199
  }
236
200
  const evidence = collector.build();
237
201
 
238
- // §3: 从 ActiveContext 获取结构化发现 (保持与 distill() 一致的规范形状)
239
202
  const distilled = activeContext?.distill() || { keyFindings: [], toolCallSummary: [] };
240
203
  const findings = distilled.keyFindings.map((f) => ({
241
204
  finding: f.finding,
242
- evidence: f.evidence || '',
205
+ evidence: typeof f.evidence === 'string' ? f.evidence : Array.isArray(f.evidence) ? f.evidence.join(', ') : f.evidence ? String(f.evidence) : '',
243
206
  importance: f.importance,
244
207
  }));
245
208
 
246
- // §4: 合并引用文件 (v1 提取 + evidenceMap 中的文件)
247
209
  const allFiles = new Set(baseReport.referencedFiles);
248
210
  for (const filePath of evidence.evidenceMap.keys()) {
249
211
  allFiles.add(filePath);
250
212
  }
251
213
 
252
- // §5: 计算多维度质量评分
253
214
  const qualityReport = buildQualityScores(
254
215
  baseReport.analysisText,
255
216
  findings,
256
217
  evidence,
257
218
  );
258
219
 
259
- // §6: 构建 AnalysisArtifact (v1 超集)
260
220
  return {
261
- // ═══ Layer 1: Core (注入 Producer prompt) ═══
221
+ // Layer 1: Core
262
222
  analysisText: baseReport.analysisText,
263
223
  findings,
264
224
  referencedFiles: [...allFiles],
265
225
  dimensionId,
266
226
 
267
- // ═══ Layer 2: Detail (按需检索) ═══
227
+ // Layer 2: Detail
268
228
  evidenceMap: evidence.evidenceMap,
269
229
  explorationLog: evidence.explorationLog,
270
230
  negativeSignals: evidence.negativeSignals,
271
231
 
272
- // ═══ Layer 3: Raw (仅审计/debug) ═══
232
+ // Layer 3: Raw
273
233
  fullToolTrace: toolCalls,
274
234
 
275
- // ═══ Quality ═══
235
+ // Quality
276
236
  qualityReport,
277
237
 
278
- // ═══ Metadata (v1 超集) ═══
238
+ // Metadata
279
239
  metadata: {
280
240
  ...baseReport.metadata,
281
241
  artifactVersion: 2,
282
242
  },
283
243
 
284
- // ═══ v1 backward compat ═══
244
+ // v1 backward compat
285
245
  searchQueries: baseReport.searchQueries,
286
246
  classesExplored: baseReport.classesExplored,
287
247
  };
@@ -294,21 +254,15 @@ export function buildAnalysisArtifact(analystResult, dimensionId, projectGraph =
294
254
  /**
295
255
  * 计算 AnalysisArtifact 的多维度质量评分
296
256
  *
297
- * 4 个维度各 0-100 分, 加权计算总分:
298
- * depthScore (30%) — 文件覆盖深度
257
+ * 4 维度各 0-100, 加权:
258
+ * depthScore (30%) — 文件覆盖深度
299
259
  * breadthScore (20%) — 工具使用广度
300
260
  * evidenceScore (30%) — 证据充分性
301
261
  * coherenceScore (20%) — 分析连贯性
302
- *
303
- * @param {string} analysisText — 清洗后分析文本
304
- * @param {Array} findings — 结构化发现
305
- * @param {object} evidence — EvidenceCollector.build() 结果
306
- * @returns {{ scores: object, totalScore: number, suggestions: string[] }}
307
262
  */
308
263
  function buildQualityScores(analysisText, findings, evidence) {
309
264
  const scores = {};
310
265
 
311
- // §1: depthScore — 文件覆盖深度
312
266
  const uniqueFilesRead = evidence.evidenceMap?.size || 0;
313
267
  const snippetCount = [...(evidence.evidenceMap?.values() || [])].reduce(
314
268
  (sum, e) => sum + e.codeSnippets.length,
@@ -316,7 +270,6 @@ function buildQualityScores(analysisText, findings, evidence) {
316
270
  );
317
271
  scores.depthScore = Math.min(100, uniqueFilesRead * 15 + snippetCount * 5);
318
272
 
319
- // §2: breadthScore — 工具种类 + 有效率
320
273
  const toolTypes = new Set((evidence.explorationLog || []).map((e) => e.tool));
321
274
  const logLen = evidence.explorationLog?.length || 0;
322
275
  const effectiveRatio = logLen > 0
@@ -324,7 +277,6 @@ function buildQualityScores(analysisText, findings, evidence) {
324
277
  : 0;
325
278
  scores.breadthScore = Math.min(100, toolTypes.size * 20 + effectiveRatio * 40);
326
279
 
327
- // §3: evidenceScore — 发现数量 + 有证据的发现比例
328
280
  const findingCount = findings?.length || 0;
329
281
  const evidencedFindings = (findings || []).filter((f) => f.evidence && f.evidence.length > 0).length;
330
282
  scores.evidenceScore =
@@ -332,7 +284,6 @@ function buildQualityScores(analysisText, findings, evidence) {
332
284
  ? Math.min(100, (evidencedFindings / findingCount) * 60 + findingCount * 10)
333
285
  : 0;
334
286
 
335
- // §4: coherenceScore — 文本长度 + 结构标记
336
287
  const textLen = analysisText?.length || 0;
337
288
  const hasHeaders = /#{1,3}\s/.test(analysisText || '');
338
289
  const hasLists = /\d+\.\s|[-•]\s/.test(analysisText || '');
@@ -344,7 +295,6 @@ function buildQualityScores(analysisText, findings, evidence) {
344
295
  (findingCount >= 3 ? 20 : findingCount * 7),
345
296
  );
346
297
 
347
- // 加权总分
348
298
  const totalScore = Math.round(
349
299
  scores.depthScore * 0.3 +
350
300
  scores.breadthScore * 0.2 +
@@ -352,7 +302,6 @@ function buildQualityScores(analysisText, findings, evidence) {
352
302
  scores.coherenceScore * 0.2,
353
303
  );
354
304
 
355
- // 改进建议
356
305
  const suggestions = [];
357
306
  if (scores.depthScore < 50) suggestions.push('Need more read_project_file to examine code');
358
307
  if (scores.evidenceScore < 50) suggestions.push('Findings lack file-level evidence');
@@ -366,11 +315,11 @@ function buildQualityScores(analysisText, findings, evidence) {
366
315
  // ──────────────────────────────────────────────────────────────────
367
316
 
368
317
  /**
369
- * 分析质量门控 — 判断 Analyst 的输出是否足够好
318
+ * 分析质量门控
370
319
  *
371
320
  * 自动检测 v1 (AnalysisReport) 和 v2 (AnalysisArtifact):
372
- * - v2: 从 qualityReport.totalScore 计算门控结果
373
- * - v1: 使用原有的 4 条规则
321
+ * - v2: 从 qualityReport.totalScore 计算
322
+ * - v1: 使用 4 条规则
374
323
  *
375
324
  * @param {AnalysisReport|AnalysisArtifact} report
376
325
  * @param {object} [options]
@@ -378,17 +327,12 @@ function buildQualityScores(analysisText, findings, evidence) {
378
327
  * @returns {{ pass: boolean, reason?: string, action?: 'retry' | 'degrade' }}
379
328
  */
380
329
  export function analysisQualityGate(report, options = {}) {
381
- // v2: AnalysisArtifact 带有 qualityReport
382
330
  if (report.qualityReport?.scores) {
383
331
  return applyGateThresholds(report.qualityReport, options);
384
332
  }
385
- // v1: 原有逻辑
386
333
  return analysisQualityGateV1(report, options);
387
334
  }
388
335
 
389
- /**
390
- * v2 质量门控 — 基于多维度评分 + 阈值
391
- */
392
336
  function applyGateThresholds(qualityReport, options = {}) {
393
337
  const { totalScore } = qualityReport;
394
338
  const needsCandidates = options.outputType === 'dual' || options.outputType === 'candidate';
@@ -411,26 +355,18 @@ function applyGateThresholds(qualityReport, options = {}) {
411
355
  };
412
356
  }
413
357
 
414
- /**
415
- * v1 质量门控 — 原有 4 条规则 (保留向后兼容)
416
- */
417
358
  function analysisQualityGateV1(report, options = {}) {
418
359
  const needsCandidates = options.outputType === 'dual' || options.outputType === 'candidate';
419
- // 需要产出候选的维度要求更高门槛
420
360
  const minChars = needsCandidates ? 400 : 200;
421
361
  const minFileRefs = needsCandidates ? 3 : 2;
422
362
 
423
- // 规则 1: 最少字符数 — 分析太短说明未充分探索
424
363
  if (report.analysisText.length < minChars) {
425
364
  return { pass: false, reason: 'Analysis too short', action: 'retry' };
426
365
  }
427
-
428
- // 规则 2: 最少引用文件数 — 未引用文件说明未看代码
429
366
  if (report.referencedFiles.length < minFileRefs) {
430
367
  return { pass: false, reason: 'Too few file references', action: 'retry' };
431
368
  }
432
369
 
433
- // 规则 3: 检测"拒绝回答"模式
434
370
  const refusalPatterns = [
435
371
  /I cannot|I'm unable|I don't have access/i,
436
372
  /无法分析|无法访问|没有足够/,
@@ -439,9 +375,6 @@ function analysisQualityGateV1(report, options = {}) {
439
375
  return { pass: false, reason: 'Agent refused to analyze', action: 'degrade' };
440
376
  }
441
377
 
442
- // 规则 4: 内容实质性检查 — 有结构化内容或足够多的探索
443
- // v3.1: 放宽条件 — tool calling 模式下 AI 往往不输出 markdown 格式
444
- // 只要分析足够长且引用了足够多的文件,就认为有实质性内容
445
378
  const hasStructure =
446
379
  /#{1,3}\s/.test(report.analysisText) ||
447
380
  /\d+\.\s/.test(report.analysisText) ||
@@ -457,7 +390,7 @@ function analysisQualityGateV1(report, options = {}) {
457
390
  }
458
391
 
459
392
  /**
460
- * 构建重试提示 — Gate 失败时给 Analyst 的追加指令
393
+ * 构建重试提示
461
394
  *
462
395
  * @param {string} reason — Gate 失败原因
463
396
  * @returns {string}
@@ -475,32 +408,67 @@ export function buildRetryPrompt(reason) {
475
408
  return hints[reason] || '请更深入地分析代码,引用至少 3 个具体文件,每个发现都要有代码证据。';
476
409
  }
477
410
 
411
+ // ──────────────────────────────────────────────────────────────────
412
+ // PipelineStrategy gate.evaluator 适配器
413
+ // ──────────────────────────────────────────────────────────────────
414
+
415
+ /**
416
+ * 面向 PipelineStrategy gate.evaluator 的包装函数。
417
+ *
418
+ * 将 PipelineStrategy 的 (source, phaseResults, strategyContext) 签名
419
+ * 适配到 buildAnalysisArtifact + analysisQualityGate 调用链。
420
+ *
421
+ * @param {object} source — 前一阶段 (analyze) 的 reactLoop 返回值
422
+ * @param {object} phaseResults — 所有阶段结果
423
+ * @param {object} strategyContext — orchestrator 注入的运行时上下文
424
+ * @returns {{ action: 'pass'|'retry'|'degrade', reason: string, artifact: object }}
425
+ */
426
+ export function insightGateEvaluator(source, phaseResults, strategyContext = {}) {
427
+ if (!source?.reply) {
428
+ return { action: 'degrade', reason: 'No analysis output', artifact: null };
429
+ }
430
+
431
+ const { projectGraph, activeContext, dimId, outputType } = strategyContext;
432
+
433
+ const artifact = activeContext
434
+ ? buildAnalysisArtifact(source, dimId, projectGraph, activeContext)
435
+ : buildAnalysisReport(source, dimId, projectGraph);
436
+
437
+ const gate = analysisQualityGate(artifact, { outputType: outputType || 'analysis' });
438
+
439
+ return {
440
+ action: gate.action || (gate.pass ? 'pass' : 'retry'),
441
+ reason: gate.reason || '',
442
+ artifact,
443
+ };
444
+ }
445
+
478
446
  // ──────────────────────────────────────────────────────────────────
479
447
  // 类型定义 (JSDoc)
480
448
  // ──────────────────────────────────────────────────────────────────
481
449
 
482
450
  /**
483
451
  * @typedef {object} AnalysisReport
484
- * @property {string} analysisText — Analyst 的完整回复文本
485
- * @property {string[]} referencedFiles — 从 toolCalls 中提取的已引用文件路径
486
- * @property {string[]} searchQueries — 从 toolCalls 中提取的搜索查询
487
- * @property {string[]} classesExplored — 从 toolCalls 中提取的已查看类名
488
- * @property {string} dimensionId — 维度 ID
452
+ * @property {string} analysisText
453
+ * @property {string[]} referencedFiles
454
+ * @property {string[]} searchQueries
455
+ * @property {string[]} classesExplored
456
+ * @property {string} dimensionId
489
457
  * @property {object} metadata — { iterations, toolCallCount }
490
458
  */
491
459
 
492
460
  /**
493
461
  * @typedef {object} AnalysisArtifact
494
- * @property {string} analysisText — 清洗后的分析全文
495
- * @property {Array<{claim: string, evidence: string[], importance: number, source: string}>} findings — 结构化发现
496
- * @property {string[]} referencedFiles — 引用文件列表
497
- * @property {string} dimensionId — 维度 ID
498
- * @property {Map<string, import('./EvidenceCollector.js').EvidenceEntry>} evidenceMap — 证据地图
499
- * @property {import('./EvidenceCollector.js').ExplorationEntry[]} explorationLog — 探索日志
500
- * @property {import('./EvidenceCollector.js').NegativeSignal[]} negativeSignals — 负空间信号
501
- * @property {Array} [fullToolTrace] — 完整工具调用追踪 (Layer 3)
502
- * @property {{ scores: object, totalScore: number, suggestions: string[] }} qualityReport — 质量评分
503
- * @property {object} metadata — { iterations, toolCallCount, artifactVersion: 2 }
504
- * @property {string[]} searchQueries — 搜索查询 (v1 兼容)
505
- * @property {string[]} classesExplored — 查看的类 (v1 兼容)
462
+ * @property {string} analysisText
463
+ * @property {Array<{claim: string, evidence: string[], importance: number, source: string}>} findings
464
+ * @property {string[]} referencedFiles
465
+ * @property {string} dimensionId
466
+ * @property {Map<string, import('./EvidenceCollector.js').EvidenceEntry>} evidenceMap
467
+ * @property {import('./EvidenceCollector.js').ExplorationEntry[]} explorationLog
468
+ * @property {import('./EvidenceCollector.js').NegativeSignal[]} negativeSignals
469
+ * @property {Array} [fullToolTrace]
470
+ * @property {{ scores: object, totalScore: number, suggestions: string[] }} qualityReport
471
+ * @property {object} metadata
472
+ * @property {string[]} searchQueries
473
+ * @property {string[]} classesExplored
506
474
  */