autosnippet 3.2.8 → 3.2.10

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 (114) hide show
  1. package/bin/cli.js +6 -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 +23 -26
  6. package/lib/cli/SetupService.js +1 -1
  7. package/lib/cli/deploy/FileManifest.js +1 -1
  8. package/lib/core/AstAnalyzer.js +1 -1
  9. package/lib/core/discovery/index.js +2 -2
  10. package/lib/external/ai/AiProvider.js +66 -172
  11. package/lib/external/ai/providers/GoogleGeminiProvider.js +29 -5
  12. package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
  13. package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
  14. package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +1 -1
  15. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
  16. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
  17. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
  18. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +291 -204
  19. package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +7 -6
  20. package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
  21. package/lib/external/mcp/handlers/bootstrap-internal.js +2 -2
  22. package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
  23. package/lib/http/HttpServer.js +1 -1
  24. package/lib/http/middleware/requestLogger.js +1 -0
  25. package/lib/http/routes/ai.js +240 -35
  26. package/lib/http/routes/candidates.js +2 -3
  27. package/lib/http/routes/extract.js +13 -11
  28. package/lib/http/routes/modules.js +2 -2
  29. package/lib/http/routes/recipes.js +5 -5
  30. package/lib/http/routes/remote.js +134 -255
  31. package/lib/http/routes/violations.js +0 -54
  32. package/lib/http/utils/sse-sessions.js +1 -1
  33. package/lib/infrastructure/logging/Logger.js +5 -4
  34. package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
  35. package/lib/injection/ServiceContainer.js +64 -17
  36. package/lib/platform/ScreenCaptureService.js +177 -0
  37. package/lib/platform/ios/routes/spm.js +2 -2
  38. package/lib/service/agent/AgentEventBus.js +207 -0
  39. package/lib/service/agent/AgentFactory.js +535 -0
  40. package/lib/service/agent/AgentMessage.js +240 -0
  41. package/lib/service/agent/AgentRouter.js +228 -0
  42. package/lib/service/agent/AgentRuntime.js +1056 -0
  43. package/lib/service/agent/AgentState.js +217 -0
  44. package/lib/service/agent/IntentClassifier.js +331 -0
  45. package/lib/service/agent/LarkTransport.js +389 -0
  46. package/lib/service/agent/capabilities.js +409 -0
  47. package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
  48. package/lib/service/{chat → agent/context}/ExplorationTracker.js +112 -33
  49. package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +5 -3
  50. package/lib/service/agent/core/LoopContext.js +170 -0
  51. package/lib/service/agent/core/MessageAdapter.js +223 -0
  52. package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
  53. package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +15 -98
  54. package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
  55. package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
  56. package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
  57. package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +85 -135
  58. package/lib/service/agent/domain/insight-producer.js +270 -0
  59. package/lib/service/agent/domain/scan-prompts.js +444 -0
  60. package/lib/service/agent/forced-summary.js +266 -0
  61. package/lib/service/agent/index.js +91 -0
  62. package/lib/service/{chat → agent}/memory/ActiveContext.js +29 -1
  63. package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
  64. package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
  65. package/lib/service/{chat → agent}/memory/SessionStore.js +1 -1
  66. package/lib/service/{chat → agent}/memory/index.js +1 -1
  67. package/lib/service/agent/policies.js +442 -0
  68. package/lib/service/agent/presets.js +305 -0
  69. package/lib/service/agent/strategies.js +756 -0
  70. package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
  71. package/lib/service/agent/tools/ai-analysis.js +75 -0
  72. package/lib/service/{chat → agent}/tools/composite.js +2 -1
  73. package/lib/service/{chat → agent}/tools/guard.js +1 -121
  74. package/lib/service/{chat → agent}/tools/index.js +27 -21
  75. package/lib/service/{chat → agent}/tools/infrastructure.js +1 -1
  76. package/lib/service/agent/tools/knowledge-graph.js +112 -0
  77. package/lib/service/agent/tools/scan-recipe.js +189 -0
  78. package/lib/service/agent/tools/system-interaction.js +476 -0
  79. package/lib/service/automation/DirectiveDetector.js +0 -1
  80. package/lib/service/automation/FileWatcher.js +0 -8
  81. package/lib/service/automation/handlers/CreateHandler.js +7 -3
  82. package/lib/service/automation/handlers/DraftHandler.js +7 -6
  83. package/lib/service/module/ModuleService.js +40 -73
  84. package/lib/service/skills/SignalCollector.js +26 -19
  85. package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
  86. package/lib/shared/FieldSpec.js +1 -1
  87. package/lib/shared/StyleGuide.js +1 -1
  88. package/package.json +4 -1
  89. package/resources/native-ui/screenshot.swift +228 -0
  90. package/dashboard/dist/assets/index-D5jiDBQG.css +0 -1
  91. package/dashboard/dist/assets/index-e5OKj-Ni.js +0 -128
  92. package/lib/core/discovery/SpmDiscoverer.js +0 -5
  93. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -750
  94. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
  95. package/lib/http/routes/spm.js +0 -5
  96. package/lib/infrastructure/external/XcodeAutomation.js +0 -15
  97. package/lib/service/chat/ChatAgent.js +0 -1602
  98. package/lib/service/chat/Memory.js +0 -161
  99. package/lib/service/chat/ProducerAgent.js +0 -431
  100. package/lib/service/chat/ReasoningTrace.js +0 -523
  101. package/lib/service/chat/TaskPipeline.js +0 -357
  102. package/lib/service/chat/WorkingMemory.js +0 -359
  103. package/lib/service/chat/memory/PersistentMemory.js +0 -450
  104. package/lib/service/chat/tools/ai-analysis.js +0 -267
  105. package/lib/service/chat/tools/knowledge-graph.js +0 -234
  106. package/lib/service/chat/tools.js +0 -18
  107. package/lib/service/snippet/PlaceholderConverter.js +0 -5
  108. package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
  109. /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
  110. /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
  111. /package/lib/service/{chat → agent}/tools/ast-graph.js +0 -0
  112. /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
  113. /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
  114. /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,111 +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,
92
-
93
- // ── v5.1: Nudge 指令回显清理 ──
94
-
95
- // AI 回显的 dimensionDigest 模板说明行("> searchHints: ...", "> candidateCount: ...")
96
60
  /^>\s*(?:searchHints|remainingTasks|candidateCount|crossRefs|keyFindings|gaps)\s*[::][^\n]*\n?/gm,
97
-
98
- // AI 回显的输出指令("请在 JSON 后紧跟...", "请直接输出...", "现在开始输出...", "输出你的...")
99
61
  /^\*{0,2}(?:请在|请直接|请确保|请务必|现在开始|输出你的|不要输出|不要再|不要包含)\s*[^。\n]*(?:分析文本|分析总结|分析报告|JSON|工具|输出|文本|报告)[^。\n]*[。.]?\s*\*{0,2}$/gm,
100
-
101
- // AI 回显的"重要"强调指令
102
62
  /^\*{0,2}重要\s*[::][^。\n]*\*{0,2}$/gm,
103
-
104
- // AI 回显的"注意"行("注意:到达第 N 轮时...")
105
63
  /^注意[::]\s*到达第\s*\d+\s*轮时[^\n]*$/gm,
106
-
107
- // AI 回显的轮次进度行("第 19/24 轮 | ⚠ 总结阶段 — 请停止工具调用,直接输出分析文本 | 剩余 5 轮")
108
64
  /^第\s*\d+\/\d+\s*轮\s*\|[^\n]*$/gm,
109
65
  ];
110
66
  let cleaned = text;
111
67
  for (const pat of patterns) {
112
68
  cleaned = cleaned.replace(pat, '');
113
69
  }
114
- // 移除可能残留的空行堆积
115
70
  cleaned = cleaned.replace(/\n{3,}/g, '\n\n').trim();
116
71
  return cleaned;
117
72
  }
118
73
 
119
74
  /**
120
- * 从 Analyst 的执行结果构建 AnalysisReport
75
+ * 从 Analyst 的执行结果构建 AnalysisReport (v1)
121
76
  *
122
- * @param {object} analystResult — ChatAgent.execute() 返回值
123
- * @param {string} analystResult.reply — Analyst 的自然语言分析文本
124
- * @param {Array} analystResult.toolCalls — 工具调用记录
77
+ * @param {object} analystResult — { reply, toolCalls }
125
78
  * @param {string} dimensionId — 维度 ID
126
- * @param {object} [projectGraph] — ProjectGraph 实例 (用于从 className 反查文件路径)
79
+ * @param {object} [projectGraph] — ProjectGraph 实例
127
80
  * @returns {AnalysisReport}
128
81
  */
129
82
  export function buildAnalysisReport(analystResult, dimensionId, projectGraph = null) {
@@ -146,7 +99,6 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
146
99
  if (args.pattern || args.query) {
147
100
  searchQueries.push(args.pattern || args.query);
148
101
  }
149
- // 从搜索结果中提取文件路径
150
102
  if (typeof result === 'string') {
151
103
  const fileMatches = result.match(
152
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
@@ -164,7 +116,6 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
164
116
  case 'get_class_info':
165
117
  if (args.className) {
166
118
  classesExplored.push(args.className);
167
- // 从 ProjectGraph 反查文件路径
168
119
  if (projectGraph) {
169
120
  const info = projectGraph.getClassInfo(args.className);
170
121
  if (info?.filePath) {
@@ -189,7 +140,7 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
189
140
  }
190
141
  }
191
142
 
192
- // 从分析文本中提取文件路径(支持多语言项目)
143
+ // 从分析文本中提取文件路径
193
144
  const text = sanitizeAnalysisText(analystResult.reply || '');
194
145
  const FILE_EXT_RE =
195
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;
@@ -225,81 +176,72 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
225
176
  * 从 Analyst 执行结果构建 AnalysisArtifact (v2 增强版)
226
177
  *
227
178
  * 在 v1 AnalysisReport 基础上增加:
228
- * - evidenceMap: 文件 → 代码片段 + 摘要 (Producer 可直接引用)
179
+ * - evidenceMap: 文件 → 代码片段 + 摘要
229
180
  * - explorationLog: 工具调用意图 + 结果摘要序列
230
- * - negativeSignals: 搜索但未找到的模式 (告知 Producer 不要猜测)
181
+ * - negativeSignals: 搜索但未找到的模式
231
182
  * - findings: 来自 ActiveContext 的结构化发现
232
183
  * - qualityReport: 多维度质量评分
233
184
  *
234
- * 向后兼容: 返回对象包含 v1 的所有字段 (analysisText, referencedFiles, searchQueries, classesExplored, metadata)
235
- *
236
- * @param {object} analystResult — ChatAgent.execute() 返回值
185
+ * @param {object} analystResult { reply, toolCalls }
237
186
  * @param {string} dimensionId — 维度 ID
238
187
  * @param {object} [projectGraph] — ProjectGraph 实例
239
- * @param {object} [activeContext] — ActiveContext 实例 (提供 distill 结果)
188
+ * @param {object} [activeContext] — ActiveContext 实例
240
189
  * @returns {AnalysisArtifact}
241
190
  */
242
191
  export function buildAnalysisArtifact(analystResult, dimensionId, projectGraph = null, activeContext = null) {
243
192
  const toolCalls = analystResult.toolCalls || [];
244
193
 
245
- // §1: 构建 v1 报告 (复用已有逻辑获取 referencedFiles, searchQueries 等)
246
194
  const baseReport = buildAnalysisReport(analystResult, dimensionId, projectGraph);
247
195
 
248
- // §2: 从工具调用中收集结构化证据
249
196
  const collector = new EvidenceCollector();
250
197
  for (let i = 0; i < toolCalls.length; i++) {
251
198
  collector.processToolCall(toolCalls[i], i);
252
199
  }
253
200
  const evidence = collector.build();
254
201
 
255
- // §3: 从 ActiveContext 获取结构化发现 (保持与 distill() 一致的规范形状)
256
202
  const distilled = activeContext?.distill() || { keyFindings: [], toolCallSummary: [] };
257
203
  const findings = distilled.keyFindings.map((f) => ({
258
204
  finding: f.finding,
259
- // P0 Fix: evidence 可能是 array/object,强制 string
260
205
  evidence: typeof f.evidence === 'string' ? f.evidence : Array.isArray(f.evidence) ? f.evidence.join(', ') : f.evidence ? String(f.evidence) : '',
261
206
  importance: f.importance,
262
207
  }));
263
208
 
264
- // §4: 合并引用文件 (v1 提取 + evidenceMap 中的文件)
265
209
  const allFiles = new Set(baseReport.referencedFiles);
266
210
  for (const filePath of evidence.evidenceMap.keys()) {
267
211
  allFiles.add(filePath);
268
212
  }
269
213
 
270
- // §5: 计算多维度质量评分
271
214
  const qualityReport = buildQualityScores(
272
215
  baseReport.analysisText,
273
216
  findings,
274
217
  evidence,
275
218
  );
276
219
 
277
- // §6: 构建 AnalysisArtifact (v1 超集)
278
220
  return {
279
- // ═══ Layer 1: Core (注入 Producer prompt) ═══
221
+ // Layer 1: Core
280
222
  analysisText: baseReport.analysisText,
281
223
  findings,
282
224
  referencedFiles: [...allFiles],
283
225
  dimensionId,
284
226
 
285
- // ═══ Layer 2: Detail (按需检索) ═══
227
+ // Layer 2: Detail
286
228
  evidenceMap: evidence.evidenceMap,
287
229
  explorationLog: evidence.explorationLog,
288
230
  negativeSignals: evidence.negativeSignals,
289
231
 
290
- // ═══ Layer 3: Raw (仅审计/debug) ═══
232
+ // Layer 3: Raw
291
233
  fullToolTrace: toolCalls,
292
234
 
293
- // ═══ Quality ═══
235
+ // Quality
294
236
  qualityReport,
295
237
 
296
- // ═══ Metadata (v1 超集) ═══
238
+ // Metadata
297
239
  metadata: {
298
240
  ...baseReport.metadata,
299
241
  artifactVersion: 2,
300
242
  },
301
243
 
302
- // ═══ v1 backward compat ═══
244
+ // v1 backward compat
303
245
  searchQueries: baseReport.searchQueries,
304
246
  classesExplored: baseReport.classesExplored,
305
247
  };
@@ -312,21 +254,15 @@ export function buildAnalysisArtifact(analystResult, dimensionId, projectGraph =
312
254
  /**
313
255
  * 计算 AnalysisArtifact 的多维度质量评分
314
256
  *
315
- * 4 个维度各 0-100 分, 加权计算总分:
316
- * depthScore (30%) — 文件覆盖深度
257
+ * 4 维度各 0-100, 加权:
258
+ * depthScore (30%) — 文件覆盖深度
317
259
  * breadthScore (20%) — 工具使用广度
318
260
  * evidenceScore (30%) — 证据充分性
319
261
  * coherenceScore (20%) — 分析连贯性
320
- *
321
- * @param {string} analysisText — 清洗后分析文本
322
- * @param {Array} findings — 结构化发现
323
- * @param {object} evidence — EvidenceCollector.build() 结果
324
- * @returns {{ scores: object, totalScore: number, suggestions: string[] }}
325
262
  */
326
263
  function buildQualityScores(analysisText, findings, evidence) {
327
264
  const scores = {};
328
265
 
329
- // §1: depthScore — 文件覆盖深度
330
266
  const uniqueFilesRead = evidence.evidenceMap?.size || 0;
331
267
  const snippetCount = [...(evidence.evidenceMap?.values() || [])].reduce(
332
268
  (sum, e) => sum + e.codeSnippets.length,
@@ -334,7 +270,6 @@ function buildQualityScores(analysisText, findings, evidence) {
334
270
  );
335
271
  scores.depthScore = Math.min(100, uniqueFilesRead * 15 + snippetCount * 5);
336
272
 
337
- // §2: breadthScore — 工具种类 + 有效率
338
273
  const toolTypes = new Set((evidence.explorationLog || []).map((e) => e.tool));
339
274
  const logLen = evidence.explorationLog?.length || 0;
340
275
  const effectiveRatio = logLen > 0
@@ -342,7 +277,6 @@ function buildQualityScores(analysisText, findings, evidence) {
342
277
  : 0;
343
278
  scores.breadthScore = Math.min(100, toolTypes.size * 20 + effectiveRatio * 40);
344
279
 
345
- // §3: evidenceScore — 发现数量 + 有证据的发现比例
346
280
  const findingCount = findings?.length || 0;
347
281
  const evidencedFindings = (findings || []).filter((f) => f.evidence && f.evidence.length > 0).length;
348
282
  scores.evidenceScore =
@@ -350,7 +284,6 @@ function buildQualityScores(analysisText, findings, evidence) {
350
284
  ? Math.min(100, (evidencedFindings / findingCount) * 60 + findingCount * 10)
351
285
  : 0;
352
286
 
353
- // §4: coherenceScore — 文本长度 + 结构标记
354
287
  const textLen = analysisText?.length || 0;
355
288
  const hasHeaders = /#{1,3}\s/.test(analysisText || '');
356
289
  const hasLists = /\d+\.\s|[-•]\s/.test(analysisText || '');
@@ -362,7 +295,6 @@ function buildQualityScores(analysisText, findings, evidence) {
362
295
  (findingCount >= 3 ? 20 : findingCount * 7),
363
296
  );
364
297
 
365
- // 加权总分
366
298
  const totalScore = Math.round(
367
299
  scores.depthScore * 0.3 +
368
300
  scores.breadthScore * 0.2 +
@@ -370,7 +302,6 @@ function buildQualityScores(analysisText, findings, evidence) {
370
302
  scores.coherenceScore * 0.2,
371
303
  );
372
304
 
373
- // 改进建议
374
305
  const suggestions = [];
375
306
  if (scores.depthScore < 50) suggestions.push('Need more read_project_file to examine code');
376
307
  if (scores.evidenceScore < 50) suggestions.push('Findings lack file-level evidence');
@@ -384,11 +315,11 @@ function buildQualityScores(analysisText, findings, evidence) {
384
315
  // ──────────────────────────────────────────────────────────────────
385
316
 
386
317
  /**
387
- * 分析质量门控 — 判断 Analyst 的输出是否足够好
318
+ * 分析质量门控
388
319
  *
389
320
  * 自动检测 v1 (AnalysisReport) 和 v2 (AnalysisArtifact):
390
- * - v2: 从 qualityReport.totalScore 计算门控结果
391
- * - v1: 使用原有的 4 条规则
321
+ * - v2: 从 qualityReport.totalScore 计算
322
+ * - v1: 使用 4 条规则
392
323
  *
393
324
  * @param {AnalysisReport|AnalysisArtifact} report
394
325
  * @param {object} [options]
@@ -396,17 +327,12 @@ function buildQualityScores(analysisText, findings, evidence) {
396
327
  * @returns {{ pass: boolean, reason?: string, action?: 'retry' | 'degrade' }}
397
328
  */
398
329
  export function analysisQualityGate(report, options = {}) {
399
- // v2: AnalysisArtifact 带有 qualityReport
400
330
  if (report.qualityReport?.scores) {
401
331
  return applyGateThresholds(report.qualityReport, options);
402
332
  }
403
- // v1: 原有逻辑
404
333
  return analysisQualityGateV1(report, options);
405
334
  }
406
335
 
407
- /**
408
- * v2 质量门控 — 基于多维度评分 + 阈值
409
- */
410
336
  function applyGateThresholds(qualityReport, options = {}) {
411
337
  const { totalScore } = qualityReport;
412
338
  const needsCandidates = options.outputType === 'dual' || options.outputType === 'candidate';
@@ -429,26 +355,18 @@ function applyGateThresholds(qualityReport, options = {}) {
429
355
  };
430
356
  }
431
357
 
432
- /**
433
- * v1 质量门控 — 原有 4 条规则 (保留向后兼容)
434
- */
435
358
  function analysisQualityGateV1(report, options = {}) {
436
359
  const needsCandidates = options.outputType === 'dual' || options.outputType === 'candidate';
437
- // 需要产出候选的维度要求更高门槛
438
360
  const minChars = needsCandidates ? 400 : 200;
439
361
  const minFileRefs = needsCandidates ? 3 : 2;
440
362
 
441
- // 规则 1: 最少字符数 — 分析太短说明未充分探索
442
363
  if (report.analysisText.length < minChars) {
443
364
  return { pass: false, reason: 'Analysis too short', action: 'retry' };
444
365
  }
445
-
446
- // 规则 2: 最少引用文件数 — 未引用文件说明未看代码
447
366
  if (report.referencedFiles.length < minFileRefs) {
448
367
  return { pass: false, reason: 'Too few file references', action: 'retry' };
449
368
  }
450
369
 
451
- // 规则 3: 检测"拒绝回答"模式
452
370
  const refusalPatterns = [
453
371
  /I cannot|I'm unable|I don't have access/i,
454
372
  /无法分析|无法访问|没有足够/,
@@ -457,9 +375,6 @@ function analysisQualityGateV1(report, options = {}) {
457
375
  return { pass: false, reason: 'Agent refused to analyze', action: 'degrade' };
458
376
  }
459
377
 
460
- // 规则 4: 内容实质性检查 — 有结构化内容或足够多的探索
461
- // v3.1: 放宽条件 — tool calling 模式下 AI 往往不输出 markdown 格式
462
- // 只要分析足够长且引用了足够多的文件,就认为有实质性内容
463
378
  const hasStructure =
464
379
  /#{1,3}\s/.test(report.analysisText) ||
465
380
  /\d+\.\s/.test(report.analysisText) ||
@@ -475,7 +390,7 @@ function analysisQualityGateV1(report, options = {}) {
475
390
  }
476
391
 
477
392
  /**
478
- * 构建重试提示 — Gate 失败时给 Analyst 的追加指令
393
+ * 构建重试提示
479
394
  *
480
395
  * @param {string} reason — Gate 失败原因
481
396
  * @returns {string}
@@ -493,32 +408,67 @@ export function buildRetryPrompt(reason) {
493
408
  return hints[reason] || '请更深入地分析代码,引用至少 3 个具体文件,每个发现都要有代码证据。';
494
409
  }
495
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
+
496
446
  // ──────────────────────────────────────────────────────────────────
497
447
  // 类型定义 (JSDoc)
498
448
  // ──────────────────────────────────────────────────────────────────
499
449
 
500
450
  /**
501
451
  * @typedef {object} AnalysisReport
502
- * @property {string} analysisText — Analyst 的完整回复文本
503
- * @property {string[]} referencedFiles — 从 toolCalls 中提取的已引用文件路径
504
- * @property {string[]} searchQueries — 从 toolCalls 中提取的搜索查询
505
- * @property {string[]} classesExplored — 从 toolCalls 中提取的已查看类名
506
- * @property {string} dimensionId — 维度 ID
452
+ * @property {string} analysisText
453
+ * @property {string[]} referencedFiles
454
+ * @property {string[]} searchQueries
455
+ * @property {string[]} classesExplored
456
+ * @property {string} dimensionId
507
457
  * @property {object} metadata — { iterations, toolCallCount }
508
458
  */
509
459
 
510
460
  /**
511
461
  * @typedef {object} AnalysisArtifact
512
- * @property {string} analysisText — 清洗后的分析全文
513
- * @property {Array<{claim: string, evidence: string[], importance: number, source: string}>} findings — 结构化发现
514
- * @property {string[]} referencedFiles — 引用文件列表
515
- * @property {string} dimensionId — 维度 ID
516
- * @property {Map<string, import('./EvidenceCollector.js').EvidenceEntry>} evidenceMap — 证据地图
517
- * @property {import('./EvidenceCollector.js').ExplorationEntry[]} explorationLog — 探索日志
518
- * @property {import('./EvidenceCollector.js').NegativeSignal[]} negativeSignals — 负空间信号
519
- * @property {Array} [fullToolTrace] — 完整工具调用追踪 (Layer 3)
520
- * @property {{ scores: object, totalScore: number, suggestions: string[] }} qualityReport — 质量评分
521
- * @property {object} metadata — { iterations, toolCallCount, artifactVersion: 2 }
522
- * @property {string[]} searchQueries — 搜索查询 (v1 兼容)
523
- * @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
524
474
  */