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
@@ -0,0 +1,535 @@
1
+ /**
2
+ * AgentFactory — 统一 Agent 创建工厂
3
+ *
4
+ * 在新架构中,Factory 的职责是:
5
+ * - 将 Preset 配置 + DI 依赖 → AgentRuntime 实例
6
+ * - 提供 Router (intent → preset → runtime)
7
+ * - 提供快捷方法 (createChat, createInsight, ...)
8
+ *
9
+ * 关键变化 (vs 旧 AgentFactory):
10
+ * - 不再创建独立 Agent 子类
11
+ * - 只创建 AgentRuntime,通过 Preset 配置差异化行为
12
+ * - 同一个工厂,同一种 Runtime,不同的配置
13
+ *
14
+ * @module AgentFactory
15
+ */
16
+
17
+ import Logger from '../../infrastructure/logging/Logger.js';
18
+ import { AgentMessage } from './AgentMessage.js';
19
+ import { AgentRouter, PresetName } from './AgentRouter.js';
20
+ import { AgentRuntime } from './AgentRuntime.js';
21
+ import { CapabilityRegistry } from './capabilities.js';
22
+ import { BudgetPolicy, PolicyEngine } from './policies.js';
23
+ import { getPreset, resolveStrategy } from './presets.js';
24
+ import { ContextWindow } from './context/ContextWindow.js';
25
+ import { ExplorationTracker } from './context/ExplorationTracker.js';
26
+ import { MemoryCoordinator } from './memory/MemoryCoordinator.js';
27
+ import { SCAN_TASK_CONFIGS, buildScanPipelineStages, buildRelationsPipelineStages } from './domain/scan-prompts.js';
28
+
29
+ export class AgentFactory {
30
+ #container;
31
+ #toolRegistry;
32
+ #aiProvider;
33
+ #logger;
34
+ /** @type {AgentRouter|null} */
35
+ #router = null;
36
+ /** @type {Object} 共享的 Capability 实例缓存 (如 MemoryCoordinator) */
37
+ #sharedOpts;
38
+
39
+ /**
40
+ * @param {Object} opts
41
+ * @param {Object} opts.container — ServiceContainer 实例
42
+ * @param {import('./tools/ToolRegistry.js').ToolRegistry} opts.toolRegistry
43
+ * @param {import('../../external/ai/AiProvider.js').AiProvider} opts.aiProvider
44
+ * @param {Object} [opts.memoryCoordinator] — MemoryCoordinator 实例 (注入 Conversation)
45
+ * @param {string} [opts.projectBriefing] — 项目概况文本
46
+ * @param {string} [opts.projectRoot] — 项目根目录
47
+ */
48
+ constructor({ container, toolRegistry, aiProvider, memoryCoordinator, projectBriefing, projectRoot }) {
49
+ this.#container = container;
50
+ this.#toolRegistry = toolRegistry;
51
+ this.#aiProvider = aiProvider;
52
+ this.#logger = Logger.getInstance();
53
+ this.#sharedOpts = {
54
+ memoryCoordinator: memoryCoordinator || null,
55
+ projectBriefing: projectBriefing || null,
56
+ projectRoot: projectRoot || process.cwd(),
57
+ };
58
+ }
59
+
60
+ // ─── Router ──────────────────────────────────
61
+
62
+ /**
63
+ * 创建带路由器的自动调度系统
64
+ * @returns {AgentRouter}
65
+ */
66
+ createRouter() {
67
+ if (this.#router) return this.#router;
68
+
69
+ const router = new AgentRouter();
70
+ router.setAiProvider(this.#aiProvider);
71
+ router.setExecutor((presetName, message, opts) => {
72
+ const runtime = this.createRuntime(presetName, opts);
73
+ return runtime.execute(message, opts.strategyOpts || opts);
74
+ });
75
+
76
+ this.#router = router;
77
+ this.#logger.info('[AgentFactory] Router created');
78
+ return router;
79
+ }
80
+
81
+ // ─── 核心创建方法 ─────────────────────────────
82
+
83
+ /**
84
+ * 根据 Preset 名称创建 AgentRuntime
85
+ *
86
+ * 这是一切的根基 — 任何 "Agent 类型" 都通过这个方法创建。
87
+ *
88
+ * @param {string} presetName — Preset 名称 (chat/insight/remote-exec)
89
+ * @param {Object} [overrides] — 覆盖 preset 配置
90
+ * @returns {AgentRuntime}
91
+ */
92
+ createRuntime(presetName, overrides = {}) {
93
+ const preset = getPreset(presetName, overrides);
94
+
95
+ // 实例化 Capabilities
96
+ const capabilities = preset.capabilities.map(name => {
97
+ const opts = this.#getCapabilityOpts(name);
98
+ return CapabilityRegistry.create(name, opts);
99
+ });
100
+
101
+ // 实例化 Policies
102
+ const policyEngine = new PolicyEngine(preset.policies || []);
103
+
104
+ return new AgentRuntime({
105
+ presetName,
106
+ aiProvider: this.#aiProvider,
107
+ toolRegistry: this.#toolRegistry,
108
+ container: this.#container,
109
+ capabilities,
110
+ strategy: preset.strategyInstance,
111
+ policies: policyEngine,
112
+ persona: preset.persona,
113
+ memory: preset.memory,
114
+ onProgress: overrides.onProgress || null,
115
+ onToolCall: overrides.onToolCall || null,
116
+ lang: overrides.lang || null,
117
+ additionalTools: overrides.additionalTools || [],
118
+ projectRoot: this.#sharedOpts.projectRoot,
119
+ });
120
+ }
121
+
122
+ // ─── 快捷方法 (语义化) ─────────────────────
123
+
124
+ /**
125
+ * 创建 ContextWindow (根据当前 AI Provider 自动解析 token 预算)
126
+ * @param {{ isSystem?: boolean }} [opts]
127
+ * @returns {ContextWindow}
128
+ */
129
+ createContextWindow(opts = {}) {
130
+ const modelName = this.#aiProvider?.model || '';
131
+ const tokenBudget = ContextWindow.resolveTokenBudget(modelName, opts);
132
+ return new ContextWindow(tokenBudget);
133
+ }
134
+
135
+ /**
136
+ * 构建系统级多轮执行上下文 — 统一基础设施
137
+ *
138
+ * 抽取 bootstrap orchestrator 中创建 ExplorationTracker / ContextWindow / source 的
139
+ * 通用逻辑,供 scanKnowledge 等系统场景共用完整的多轮 Agent 框架。
140
+ *
141
+ * 与 bootstrap orchestrator 保持一致的 MemoryCoordinator 管理模式:
142
+ * - 创建轻量级 MemoryCoordinator (无 PersistentMemory/SessionStore)
143
+ * - 通过 MC.createDimensionScope 创建并注册 ActiveContext
144
+ * - trace 从 MC.getActiveContext 获取 (统一生命周期管理)
145
+ * - memoryCoordinator 传入 strategyContext,供 reactLoop 每轮 buildDynamicMemoryPrompt
146
+ *
147
+ * 与 bootstrap orchestrator 对齐的关键字段:
148
+ * - activeContext: 与 trace 同一实例 — insightGateEvaluator 通过此字段
149
+ * 决定走 buildAnalysisArtifact (完整: findings/evidenceMap/negativeSignals)
150
+ * 还是 buildAnalysisReport (降级: 仅文本)
151
+ * - outputType: 'candidate' — 设置 quality_gate 的评判标准
152
+ * - dimId: 维度 ID — buildAnalysisArtifact 的 dimensionId 参数
153
+ *
154
+ * bootstrap orchestrator 不使用此方法(它还需要领域特定的 SessionStore / dimContext 等),
155
+ * 但引擎层基础设施是一致的。
156
+ *
157
+ * @param {Object} [opts]
158
+ * @param {Object} [opts.budget] — 预算覆盖 (透传给 ExplorationTracker)
159
+ * @param {string} [opts.trackerStrategy='analyst'] — tracker 策略名: 'analyst' | 'producer' | 'bootstrap'
160
+ * @param {string} [opts.label='default'] — 作用域标签 (用于 scopeId 命名 + dimId)
161
+ * @param {string} [opts.lang] — 项目语言 (透传给 sharedState._projectLanguage)
162
+ * @returns {{ contextWindow: ContextWindow, tracker: ExplorationTracker, trace: import('./memory/ActiveContext.js').ActiveContext, activeContext: import('./memory/ActiveContext.js').ActiveContext, memoryCoordinator: MemoryCoordinator, outputType: string, dimId: string, sharedState: Object, source: string, scopeId: string }}
163
+ */
164
+ buildSystemContext({ budget, trackerStrategy = 'analyst', label = 'default', lang } = {}) {
165
+ // 创建轻量级 MemoryCoordinator (scan 场景无 PersistentMemory/SessionStore)
166
+ const mc = new MemoryCoordinator({ mode: 'bootstrap' });
167
+ const scopeId = `scan:${label}`;
168
+ mc.createDimensionScope(scopeId);
169
+
170
+ const activeContext = mc.getActiveContext(scopeId);
171
+
172
+ return {
173
+ contextWindow: this.createContextWindow({ isSystem: true }),
174
+ tracker: ExplorationTracker.resolve(
175
+ { source: 'system', strategy: trackerStrategy },
176
+ budget || {},
177
+ ),
178
+ // trace & activeContext 是同一个 ActiveContext 实例
179
+ // trace: AgentRuntime reactLoop 使用 (startRound/setThought/endRound)
180
+ // activeContext: insightGateEvaluator 检查此字段决定 artifact 路径
181
+ trace: activeContext,
182
+ activeContext,
183
+ memoryCoordinator: mc,
184
+ // outputType: bootstrap orchestrator 设为 'candidate'(insightGateEvaluator 的评判标准)
185
+ outputType: 'candidate',
186
+ // dimId: buildAnalysisArtifact 的 dimensionId 参数
187
+ dimId: label,
188
+ sharedState: {
189
+ submittedTitles: new Set(),
190
+ submittedPatterns: new Set(),
191
+ // G6: _projectLanguage — ToolExecutionPipeline 透传给工具 handler 上下文
192
+ _projectLanguage: lang || null,
193
+ // G7: _dimensionScopeId — ToolExecutionPipeline 透传给工具 handler (note_finding scope)
194
+ _dimensionScopeId: scopeId,
195
+ },
196
+ source: 'system',
197
+ scopeId,
198
+ };
199
+ }
200
+
201
+ /**
202
+ * 获取 AI Provider 信息 (供 orchestrator 等外部使用)
203
+ * @returns {{ model: string, name: string }}
204
+ */
205
+ getAiProviderInfo() {
206
+ return {
207
+ model: this.#aiProvider?.model || 'unknown',
208
+ name: this.#aiProvider?.name || 'unknown',
209
+ };
210
+ }
211
+
212
+ /**
213
+ * 创建对话 Runtime (Dashboard / 飞书聊天)
214
+ * @param {Object} [opts]
215
+ */
216
+ createChat(opts = {}) {
217
+ return this.createRuntime(PresetName.CHAT, opts);
218
+ }
219
+
220
+ /**
221
+ * 创建洞察 Runtime (深度代码分析 + 知识提取)
222
+ * @param {Object} [opts]
223
+ * @param {Array} [opts.dimensions] — 维度列表 (传给 FanOutStrategy 的 items)
224
+ * @param {Object} [opts.projectInfo] — 项目信息
225
+ */
226
+ createInsight(opts = {}) {
227
+ return this.createRuntime(PresetName.INSIGHT, opts);
228
+ }
229
+
230
+ /**
231
+ * 创建飞书对话 Runtime (知识管理,服务端处理)
232
+ * @param {Object} [opts]
233
+ */
234
+ createLark(opts = {}) {
235
+ return this.createRuntime(PresetName.LARK, opts);
236
+ }
237
+
238
+ /**
239
+ * 创建远程执行 Runtime (飞书终端 / 远程操作)
240
+ * @param {Object} [opts]
241
+ */
242
+ createRemoteExec(opts = {}) {
243
+ return this.createRuntime(PresetName.REMOTE_EXEC, opts);
244
+ }
245
+
246
+ // ─── 领域语义方法 (意图驱动, Agent 直接完成 AI 推理) ─────
247
+
248
+ /**
249
+ * 统一知识扫描 — 走 insight 管线 (Analyze → QualityGate → Produce → RejectionGate)
250
+ *
251
+ * extract 和 summarize 共享工具驱动管线 (collect_scan_recipe),
252
+ * 仅 Produce 阶段的 systemPrompt 和预算不同:
253
+ * - extract: 多文件 target 扫描,24 iter analyze,24 iter produce
254
+ * - summarize: 单文件/代码片段,12 iter analyze,12 iter produce
255
+ *
256
+ * 关系发现请使用单独的 discoverRelations() 方法。
257
+ *
258
+ * @param {Object} opts
259
+ * @param {string} opts.label — 上下文标签(target 名 / 文件名)
260
+ * @param {Array<{name, content, language?}>} opts.files — 源文件
261
+ * @param {'extract'|'summarize'} [opts.task='extract'] — 任务类型
262
+ * @param {string} [opts.lang] — 语言提示
263
+ * @param {boolean} [opts.comprehensive] — 深度扫描标志
264
+ * @returns {Promise<Object>} — task-specific JSON
265
+ */
266
+ async scanKnowledge({ label, files, task = 'extract', lang, comprehensive } = {}) {
267
+ const taskConfig = SCAN_TASK_CONFIGS[task];
268
+ if (!taskConfig) {
269
+ throw new Error(`Unknown scanKnowledge task: "${task}". Available: ${Object.keys(SCAN_TASK_CONFIGS).join(', ')}`);
270
+ }
271
+ const { producePrompt, fallback } = taskConfig;
272
+
273
+ // extract 和 summarize 都使用 code_analysis 分析 + scan_production 工具驱动
274
+ const analyzeCaps = ['code_analysis'];
275
+ const produceCaps = ['scan_production'];
276
+
277
+ // ── 统一 4 阶段 Pipeline (与冷启动 orchestrator 对齐) ──
278
+ // summarize (单文件) 使用较低预算
279
+ const analyzeMaxIter = task === 'summarize' ? 12 : 24;
280
+ const stages = buildScanPipelineStages({
281
+ task,
282
+ producePrompt,
283
+ analyzeCaps,
284
+ produceCaps,
285
+ files,
286
+ analyzeMaxIter,
287
+ });
288
+
289
+ // ── 创建 Runtime — 使用 insight preset + 对齐 policies ──
290
+ const runtime = this.createRuntime(PresetName.INSIGHT, {
291
+ strategy: { type: 'pipeline', maxRetries: 1, stages },
292
+ capabilities: analyzeCaps,
293
+ policies: [
294
+ new BudgetPolicy({
295
+ maxIterations: 30, // 24 stage budget + 6 tracker grace
296
+ maxTokens: 8192,
297
+ temperature: 0.3,
298
+ timeoutMs: 600_000,
299
+ }),
300
+ ],
301
+ memory: { enabled: false },
302
+ lang,
303
+ });
304
+ if (files?.length) {
305
+ runtime.setFileCache(files);
306
+ }
307
+
308
+ // ── 完整的系统级多轮基础设施 (含 MemoryCoordinator 管理 ActiveContext) ──
309
+ const systemCtx = this.buildSystemContext({
310
+ budget: { maxIterations: analyzeMaxIter },
311
+ trackerStrategy: 'analyst',
312
+ label: `${task}:${label}`,
313
+ lang,
314
+ });
315
+
316
+ // ── 执行 ──
317
+ const message = AgentMessage.internal(
318
+ `分析 "${label}" 的 ${files?.length || 0} 个源文件。${comprehensive ? '请进行深度分析。' : ''}`,
319
+ );
320
+ const result = await runtime.execute(message, { strategyContext: systemCtx });
321
+
322
+ // ── 提取结果 — extract 和 summarize 统一从 toolCalls 提取 ──
323
+ const allToolCalls = result.toolCalls || [];
324
+ const recipes = allToolCalls
325
+ .filter(tc => (tc.tool || tc.name) === 'collect_scan_recipe')
326
+ .map(tc => {
327
+ const res = tc.result;
328
+ if (res && typeof res === 'object' && res.status === 'collected' && res.recipe) {
329
+ return res.recipe;
330
+ }
331
+ return null;
332
+ })
333
+ .filter(Boolean);
334
+
335
+ if (recipes.length > 0) {
336
+ // summarize 向后兼容: 扁平化首个 recipe 为 { title, summary, usageGuide, ... }
337
+ if (task === 'summarize') {
338
+ const first = recipes[0];
339
+ return {
340
+ title: first.title || '',
341
+ summary: first.description || first.summary || '',
342
+ usageGuide: first.usageGuide || '',
343
+ category: first.category || '',
344
+ headers: first.headers || [],
345
+ tags: first.tags || [],
346
+ trigger: first.trigger || '',
347
+ recipes,
348
+ extracted: recipes.length,
349
+ };
350
+ }
351
+ return { targetName: label, extracted: recipes.length, recipes };
352
+ }
353
+
354
+ // Fallback: 工具未被调用时,尝试从文本解析
355
+ const produceReply = result.phases?.produce?.reply || result.reply;
356
+ return this.#parseJsonResponse(produceReply, fallback(label));
357
+ }
358
+
359
+ /**
360
+ * 知识图谱关系发现 — 独立管线 (Explore → Synthesize)
361
+ *
362
+ * 与 scanKnowledge 不同,relations 不需要源文件输入,
363
+ * 而是通过查询知识库 + 读取源码发现知识条目间的语义关系。
364
+ *
365
+ * @param {Object} [opts]
366
+ * @param {number} [opts.batchSize=20] — 批次大小提示
367
+ * @returns {Promise<{ analyzed: number, relations: Array }>}
368
+ */
369
+ async discoverRelations({ batchSize = 20 } = {}) {
370
+ const stages = buildRelationsPipelineStages();
371
+
372
+ const runtime = this.createRuntime(PresetName.INSIGHT, {
373
+ strategy: { type: 'pipeline', stages },
374
+ capabilities: ['knowledge_production', 'code_analysis'],
375
+ policies: [
376
+ new BudgetPolicy({
377
+ maxIterations: 28,
378
+ maxTokens: 8192,
379
+ temperature: 0.3,
380
+ timeoutMs: 420_000,
381
+ }),
382
+ ],
383
+ memory: { enabled: false },
384
+ });
385
+
386
+ const message = AgentMessage.internal(
387
+ `探索知识库中所有知识条目之间的语义关系。每批分析约 ${batchSize} 条知识。`,
388
+ );
389
+ const result = await runtime.execute(message);
390
+
391
+ const synthesizeReply = result.phases?.synthesize?.reply || result.reply;
392
+ return this.#parseJsonResponse(synthesizeReply, { analyzed: 0, relations: [] });
393
+ }
394
+
395
+ /**
396
+ * AI 翻译 — chat 模式,单轮生成
397
+ *
398
+ * Agent(LLM) 直接翻译文本,无需工具。
399
+ *
400
+ * @param {string} summary — 中文摘要
401
+ * @param {string} [usageGuide] — 中文使用指南
402
+ * @returns {Promise<{ summaryEn?: string, usageGuideEn?: string, error?: string }>}
403
+ */
404
+ async translateToEnglish(summary, usageGuide) {
405
+ if (!summary && !usageGuide) {
406
+ return { summaryEn: '', usageGuideEn: '' };
407
+ }
408
+
409
+ const runtime = this.createChat({
410
+ policies: [
411
+ new BudgetPolicy({ maxIterations: 1, maxTokens: 4096, temperature: 0.2, timeoutMs: 60_000 }),
412
+ ],
413
+ persona: {
414
+ description: [
415
+ '你是技术文档翻译专家。将中文技术内容翻译为地道的英文。保持技术术语不变。',
416
+ '',
417
+ '## 输出格式(必须是纯 JSON,不包含任何其他文字)',
418
+ '{ "summaryEn": "...", "usageGuideEn": "..." }',
419
+ ].join('\n'),
420
+ },
421
+ memory: { enabled: false },
422
+ });
423
+
424
+ const message = AgentMessage.internal(
425
+ `翻译以下内容为英文,输出纯 JSON:\nsummary: ${summary || '(空)'}\nusageGuide: ${usageGuide || '(空)'}`,
426
+ );
427
+
428
+ const result = await runtime.execute(message);
429
+ return this.#parseJsonResponse(result.reply, {
430
+ summaryEn: summary || '',
431
+ usageGuideEn: usageGuide || '',
432
+ });
433
+ }
434
+
435
+ /**
436
+ * 冷启动知识库 — 直接调用 handler(纯启发式,不需要 LLM)
437
+ *
438
+ * bootstrap_knowledge 是纯启发式工具:SPM Target 扫描 → 依赖图谱 → Guard 审计 →
439
+ * Candidate 创建,全程无 AI 推理。直接调用 handler 即可,无需创建 Agent。
440
+ *
441
+ * @param {{ maxFiles?: number, skipGuard?: boolean, contentMaxLines?: number, loadSkills?: boolean, skipAsyncFill?: boolean }} [opts]
442
+ * @returns {Promise<Object>}
443
+ */
444
+ async bootstrapKnowledge(opts = {}) {
445
+ const { bootstrapKnowledge } = await import('../../external/mcp/handlers/bootstrap-internal.js');
446
+ const result = await bootstrapKnowledge(
447
+ { container: this.#container, logger: this.#logger },
448
+ {
449
+ maxFiles: opts.maxFiles || 500,
450
+ skipGuard: opts.skipGuard || false,
451
+ contentMaxLines: opts.contentMaxLines || 120,
452
+ loadSkills: opts.loadSkills ?? true,
453
+ skipAsyncFill: opts.skipAsyncFill || false,
454
+ },
455
+ );
456
+ const parsed = typeof result === 'string' ? JSON.parse(result) : result;
457
+ return parsed?.data || parsed;
458
+ }
459
+
460
+ /**
461
+ * 通用工具执行 — 直接调用工具 handler
462
+ *
463
+ * 纯数据工具直接执行,无需创建 Agent。
464
+ * AI 推理由各语义方法的 Agent 自主完成,此方法仅用于纯数据工具。
465
+ *
466
+ * @param {string} toolName — 工具名称
467
+ * @param {Object} params — 工具参数
468
+ * @returns {Promise<*>} 工具原始返回值
469
+ */
470
+ async invokeAgent(toolName, params) {
471
+ return this.#toolRegistry.execute(toolName, params, this.#makeToolContext());
472
+ }
473
+
474
+ // ─── 私有方法 ────────────────────────────────
475
+
476
+ /**
477
+ * 解析 Agent 响应中的 JSON(支持 markdown 代码块包装)
478
+ * @param {string} text — Agent 响应文本
479
+ * @param {Object} fallback — 解析失败时的默认值
480
+ * @returns {Object}
481
+ */
482
+ #parseJsonResponse(text, fallback) {
483
+ if (!text) return fallback;
484
+ try {
485
+ // 尝试从 markdown 代码块中提取 JSON
486
+ const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
487
+ if (codeBlockMatch) {
488
+ return JSON.parse(codeBlockMatch[1].trim());
489
+ }
490
+ // 尝试直接提取 JSON 对象
491
+ const objMatch = text.match(/(\{[\s\S]*\})/);
492
+ if (objMatch) {
493
+ return JSON.parse(objMatch[1].trim());
494
+ }
495
+ return JSON.parse(text.trim());
496
+ } catch {
497
+ this.#logger.warn('[AgentFactory] Failed to parse JSON from Agent response');
498
+ return fallback;
499
+ }
500
+ }
501
+
502
+ /**
503
+ * 构建工具 handler 执行所需的上下文对象
504
+ * @returns {Object}
505
+ */
506
+ #makeToolContext() {
507
+ return {
508
+ aiProvider: this.#aiProvider,
509
+ container: this.#container,
510
+ logger: this.#logger,
511
+ projectRoot: this.#sharedOpts.projectRoot,
512
+ };
513
+ }
514
+
515
+ /**
516
+ * 获取 Capability 实例化时需要的依赖注入参数
517
+ */
518
+ #getCapabilityOpts(capabilityName) {
519
+ switch (capabilityName) {
520
+ case 'conversation':
521
+ return {
522
+ memoryCoordinator: this.#sharedOpts.memoryCoordinator,
523
+ projectBriefing: this.#sharedOpts.projectBriefing,
524
+ };
525
+ case 'system_interaction':
526
+ return {
527
+ projectRoot: this.#sharedOpts.projectRoot,
528
+ };
529
+ default:
530
+ return {};
531
+ }
532
+ }
533
+ }
534
+
535
+ export default AgentFactory;