autosnippet 3.2.8 → 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 (113) 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/core/AstAnalyzer.js +1 -1
  8. package/lib/core/discovery/index.js +2 -2
  9. package/lib/external/ai/AiProvider.js +66 -172
  10. package/lib/external/ai/providers/GoogleGeminiProvider.js +23 -1
  11. package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
  12. package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
  13. package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +1 -1
  14. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
  15. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
  16. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
  17. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +287 -204
  18. package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +7 -6
  19. package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
  20. package/lib/external/mcp/handlers/bootstrap-internal.js +2 -2
  21. package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
  22. package/lib/http/HttpServer.js +1 -1
  23. package/lib/http/middleware/requestLogger.js +1 -0
  24. package/lib/http/routes/ai.js +240 -35
  25. package/lib/http/routes/candidates.js +2 -3
  26. package/lib/http/routes/extract.js +13 -11
  27. package/lib/http/routes/modules.js +2 -2
  28. package/lib/http/routes/recipes.js +9 -5
  29. package/lib/http/routes/remote.js +134 -255
  30. package/lib/http/routes/violations.js +0 -54
  31. package/lib/http/utils/sse-sessions.js +1 -1
  32. package/lib/infrastructure/logging/Logger.js +5 -4
  33. package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
  34. package/lib/injection/ServiceContainer.js +64 -17
  35. package/lib/platform/ScreenCaptureService.js +177 -0
  36. package/lib/platform/ios/routes/spm.js +2 -2
  37. package/lib/service/agent/AgentEventBus.js +207 -0
  38. package/lib/service/agent/AgentFactory.js +490 -0
  39. package/lib/service/agent/AgentMessage.js +240 -0
  40. package/lib/service/agent/AgentRouter.js +228 -0
  41. package/lib/service/agent/AgentRuntime.js +1016 -0
  42. package/lib/service/agent/AgentState.js +217 -0
  43. package/lib/service/agent/IntentClassifier.js +331 -0
  44. package/lib/service/agent/LarkTransport.js +389 -0
  45. package/lib/service/agent/capabilities.js +408 -0
  46. package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
  47. package/lib/service/{chat → agent/context}/ExplorationTracker.js +25 -14
  48. package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +1 -1
  49. package/lib/service/agent/core/LoopContext.js +170 -0
  50. package/lib/service/agent/core/MessageAdapter.js +223 -0
  51. package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
  52. package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +19 -98
  53. package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
  54. package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
  55. package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
  56. package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +85 -135
  57. package/lib/service/agent/domain/insight-producer.js +267 -0
  58. package/lib/service/agent/domain/scan-prompts.js +105 -0
  59. package/lib/service/agent/forced-summary.js +266 -0
  60. package/lib/service/agent/index.js +91 -0
  61. package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
  62. package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
  63. package/lib/service/{chat → agent}/memory/SessionStore.js +1 -1
  64. package/lib/service/{chat → agent}/memory/index.js +1 -1
  65. package/lib/service/agent/policies.js +442 -0
  66. package/lib/service/agent/presets.js +303 -0
  67. package/lib/service/agent/strategies.js +717 -0
  68. package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
  69. package/lib/service/agent/tools/ai-analysis.js +75 -0
  70. package/lib/service/{chat → agent}/tools/composite.js +2 -1
  71. package/lib/service/{chat → agent}/tools/guard.js +1 -121
  72. package/lib/service/{chat → agent}/tools/index.js +27 -21
  73. package/lib/service/{chat → agent}/tools/infrastructure.js +1 -1
  74. package/lib/service/agent/tools/knowledge-graph.js +112 -0
  75. package/lib/service/agent/tools/scan-recipe.js +189 -0
  76. package/lib/service/agent/tools/system-interaction.js +476 -0
  77. package/lib/service/automation/DirectiveDetector.js +0 -1
  78. package/lib/service/automation/FileWatcher.js +0 -8
  79. package/lib/service/automation/handlers/CreateHandler.js +7 -3
  80. package/lib/service/automation/handlers/DraftHandler.js +7 -6
  81. package/lib/service/module/ModuleService.js +40 -73
  82. package/lib/service/skills/SignalCollector.js +26 -19
  83. package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
  84. package/lib/shared/FieldSpec.js +1 -1
  85. package/lib/shared/StyleGuide.js +1 -1
  86. package/package.json +4 -1
  87. package/resources/native-ui/screenshot.swift +228 -0
  88. package/dashboard/dist/assets/index-D5jiDBQG.css +0 -1
  89. package/dashboard/dist/assets/index-e5OKj-Ni.js +0 -128
  90. package/lib/core/discovery/SpmDiscoverer.js +0 -5
  91. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -750
  92. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
  93. package/lib/http/routes/spm.js +0 -5
  94. package/lib/infrastructure/external/XcodeAutomation.js +0 -15
  95. package/lib/service/chat/ChatAgent.js +0 -1602
  96. package/lib/service/chat/Memory.js +0 -161
  97. package/lib/service/chat/ProducerAgent.js +0 -431
  98. package/lib/service/chat/ReasoningTrace.js +0 -523
  99. package/lib/service/chat/TaskPipeline.js +0 -357
  100. package/lib/service/chat/WorkingMemory.js +0 -359
  101. package/lib/service/chat/memory/PersistentMemory.js +0 -450
  102. package/lib/service/chat/tools/ai-analysis.js +0 -267
  103. package/lib/service/chat/tools/knowledge-graph.js +0 -234
  104. package/lib/service/chat/tools.js +0 -18
  105. package/lib/service/snippet/PlaceholderConverter.js +0 -5
  106. package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
  107. /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
  108. /package/lib/service/{chat → agent}/memory/ActiveContext.js +0 -0
  109. /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
  110. /package/lib/service/{chat → agent}/tools/ast-graph.js +0 -0
  111. /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
  112. /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
  113. /package/lib/service/{chat → agent}/tools/query.js +0 -0
@@ -4,11 +4,11 @@
4
4
  * ⚠️ 本文件是「内部 Agent」专用 — 由 bootstrap.js Phase 5 调用。
5
5
  * 外部 Agent (Cursor/Copilot) 不经过此管线,它们自行分析代码。
6
6
  *
7
- * 核心架构: AnalystGateProducer (双 Agent 模式)
7
+ * 核心架构: PipelineStrategy 驱动 (Analyze QualityGateProduce RejectionGate)
8
8
  *
9
- * 1. Analyst Agent 自由探索代码 (AST 工具 + 文件搜索)
10
- * 2. HandoffProtocol 质量门控
11
- * 3. Producer Agent 格式化输出 (submit_knowledge)
9
+ * 1. Analyze 阶段: 自由探索代码 (AST 工具 + 文件搜索)
10
+ * 2. QualityGate: 质量门控 (insightGateEvaluator)
11
+ * 3. Produce 阶段: 格式化输出 (submit_knowledge)
12
12
  * 4. TierScheduler 分层并行执行
13
13
  *
14
14
  * @module pipeline/orchestrator
@@ -17,12 +17,15 @@
17
17
  import fs from 'node:fs/promises';
18
18
  import path from 'node:path';
19
19
  import Logger from '../../../../../infrastructure/logging/Logger.js';
20
- import { AnalystAgent } from '../../../../../service/chat/AnalystAgent.js';
21
- import { EpisodicConsolidator } from '../../../../../service/chat/EpisodicConsolidator.js';
22
- import { ProducerAgent } from '../../../../../service/chat/ProducerAgent.js';
23
- import { ProjectSemanticMemory } from '../../../../../service/chat/ProjectSemanticMemory.js';
24
- import { MemoryCoordinator } from '../../../../../service/chat/memory/MemoryCoordinator.js';
25
- import { SessionStore } from '../../../../../service/chat/memory/SessionStore.js';
20
+ import { EpisodicConsolidator } from '../../../../../service/agent/domain/EpisodicConsolidator.js';
21
+ import { PersistentMemory } from '../../../../../service/agent/memory/PersistentMemory.js';
22
+ import { MemoryCoordinator } from '../../../../../service/agent/memory/MemoryCoordinator.js';
23
+ import { SessionStore } from '../../../../../service/agent/memory/SessionStore.js';
24
+ import { AgentMessage } from '../../../../../service/agent/AgentMessage.js';
25
+ import { BudgetPolicy } from '../../../../../service/agent/policies.js';
26
+ import { PRESETS } from '../../../../../service/agent/presets.js';
27
+ import { ContextWindow } from '../../../../../service/agent/context/ContextWindow.js';
28
+ import { ExplorationTracker } from '../../../../../service/agent/context/ExplorationTracker.js';
26
29
  import { clearCheckpoints, loadCheckpoints, saveDimensionCheckpoint } from './checkpoint.js';
27
30
  import { buildTierReflection, DIMENSION_CONFIGS_V3, getFullDimensionConfig } from './dimension-configs.js';
28
31
  import { getDimensionFocusKeywords } from '../shared/dimension-sop.js';
@@ -61,30 +64,29 @@ export async function fillDimensionsV3(fillContext) {
61
64
  const isIncremental = incrementalPlan?.canIncremental && incrementalPlan?.mode === 'incremental';
62
65
  const emitter = new BootstrapEventEmitter(ctx.container);
63
66
  logger.info(
64
- `[Bootstrap-v3] ═══ fillDimensionsV3 entered — ${isIncremental ? 'INCREMENTAL' : 'FULL'} pipeline`
67
+ `[Insight-v3] ═══ fillDimensionsV3 entered — ${isIncremental ? 'INCREMENTAL' : 'FULL'} pipeline`
65
68
  );
66
69
 
67
70
  let allFiles = fillContext.allFiles;
68
71
  fillContext.allFiles = null;
69
72
 
70
73
  // ═══════════════════════════════════════════════════════════
71
- // Step 0: AI 可用性检查
74
+ // Step 0: AI 可用性检查 (v7.2: 使用 AgentFactory)
72
75
  // ═══════════════════════════════════════════════════════════
73
- let chatAgent = null;
76
+ let agentFactory = null;
74
77
  try {
75
- chatAgent = ctx.container.get('chatAgent');
76
- if (chatAgent && !chatAgent.hasRealAI) {
77
- chatAgent = null;
78
- }
79
- if (chatAgent) {
80
- chatAgent.resetGlobalSubmittedTitles();
78
+ agentFactory = ctx.container.get('agentFactory');
79
+ // 检查 AI Provider 是否可用
80
+ const aiProvider = ctx.container.singletons?.aiProvider;
81
+ if (!aiProvider || aiProvider.name === 'mock') {
82
+ agentFactory = null;
81
83
  }
82
84
  } catch {
83
85
  /* not available */
84
86
  }
85
87
 
86
- if (!chatAgent) {
87
- logger.info('[Bootstrap-v3] AI not available — entering rule-based fallback');
88
+ if (!agentFactory) {
89
+ logger.info('[Insight-v3] AI not available — entering rule-based fallback');
88
90
  emitter.emitProgress('bootstrap:ai-unavailable', {
89
91
  message: 'AI 不可用,将使用规则化降级提取基础知识。请配置 AI Provider 以获取完整分析。',
90
92
  });
@@ -247,21 +249,20 @@ export async function fillDimensionsV3(fillContext) {
247
249
  if (projectGraph) {
248
250
  const overview = projectGraph.getOverview();
249
251
  logger.info(
250
- `[Bootstrap-v3] ProjectGraph: ${overview.totalClasses} classes, ${overview.totalProtocols} protocols (${overview.buildTimeMs}ms)`
252
+ `[Insight-v3] ProjectGraph: ${overview.totalClasses} classes, ${overview.totalProtocols} protocols (${overview.buildTimeMs}ms)`
251
253
  );
252
254
  }
253
255
  } catch (e) {
254
- logger.warn(`[Bootstrap-v3] ProjectGraph build failed: ${e.message}`);
256
+ logger.warn(`[Insight-v3] ProjectGraph build failed: ${e.message}`);
255
257
  }
256
258
 
257
259
  // ═══════════════════════════════════════════════════════════
258
260
  // Step 1: 构建 Agents + 上下文
259
261
  // ═══════════════════════════════════════════════════════════
260
- const analystAgent = new AnalystAgent(chatAgent, projectGraph, { maxRetries: 1 });
261
- const producerAgent = new ProducerAgent(chatAgent);
262
+ logger.info('[Insight-v7] Using unified AgentRuntime pipeline (no legacy Analyst/Producer wrappers)');
262
263
 
263
- // 注入文件缓存
264
- chatAgent.setFileCache(allFiles);
264
+ // 注入文件缓存到容器 (v7.2: 通过容器传递)
265
+ ctx.container.singletons._fileCache = allFiles;
265
266
 
266
267
  // 项目信息
267
268
  const projectInfo = {
@@ -289,7 +290,7 @@ export async function fillDimensionsV3(fillContext) {
289
290
  sessionStore = incrementalPlan.restoredEpisodic;
290
291
  const restoredDims = sessionStore.getCompletedDimensions();
291
292
  logger.info(
292
- `[Bootstrap-v3] Restored SessionStore: ${restoredDims.length} dims [${restoredDims.join(', ')}]`
293
+ `[Insight-v3] Restored SessionStore: ${restoredDims.length} dims [${restoredDims.join(', ')}]`
293
294
  );
294
295
 
295
296
  // 同步恢复 DimensionContext 的 digests (兼容)
@@ -308,23 +309,23 @@ export async function fillDimensionsV3(fillContext) {
308
309
  });
309
310
  }
310
311
 
311
- // v4.1: ProjectSemanticMemory — 项目级永久语义记忆 (Tier 3)
312
- // 加载历史 bootstrap 记忆 → 注入 AnalystAgent prompt
312
+ // v4.1: PersistentMemory — 项目级永久语义记忆 (Tier 3)
313
+ // 加载历史 bootstrap 记忆 → 注入 Analyst promptBuilder
313
314
  let semanticMemory = null;
314
315
  try {
315
316
  const db = ctx.container.get('database');
316
317
  if (db) {
317
- semanticMemory = new ProjectSemanticMemory(db, { logger });
318
+ semanticMemory = new PersistentMemory(db, { logger });
318
319
  const smStats = semanticMemory.getStats();
319
320
  if (smStats.total > 0) {
320
321
  logger.info(
321
- `[Bootstrap-v3] Loaded ${smStats.total} semantic memories from previous bootstrap ` +
322
+ `[Insight-v3] Loaded ${smStats.total} semantic memories from previous bootstrap ` +
322
323
  `(fact: ${smStats.byType.fact || 0}, insight: ${smStats.byType.insight || 0}, preference: ${smStats.byType.preference || 0})`
323
324
  );
324
325
  }
325
326
  }
326
327
  } catch (smErr) {
327
- logger.warn(`[Bootstrap-v3] SemanticMemory init failed (non-blocking): ${smErr.message}`);
328
+ logger.warn(`[Insight-v3] SemanticMemory init failed (non-blocking): ${smErr.message}`);
328
329
  }
329
330
 
330
331
  // Phase E: CodeEntityGraph — 代码实体关系图谱 (供 Analyst prompt 注入)
@@ -337,12 +338,12 @@ export async function fillDimensionsV3(fillContext) {
337
338
  const topo = codeEntityGraphInst.getTopology();
338
339
  if (topo.totalEntities > 0) {
339
340
  logger.info(
340
- `[Bootstrap-v3] CodeEntityGraph: ${topo.totalEntities} entities, ${topo.totalEdges} edges`
341
+ `[Insight-v3] CodeEntityGraph: ${topo.totalEntities} entities, ${topo.totalEdges} edges`
341
342
  );
342
343
  }
343
344
  }
344
345
  } catch (cegErr) {
345
- logger.warn(`[Bootstrap-v3] CodeEntityGraph init failed (non-blocking): ${cegErr.message}`);
346
+ logger.warn(`[Insight-v3] CodeEntityGraph init failed (non-blocking): ${cegErr.message}`);
346
347
  }
347
348
 
348
349
  // v5.0: MemoryCoordinator — 统一记忆协调器 (会话级)
@@ -378,14 +379,14 @@ export async function fillDimensionsV3(fillContext) {
378
379
  }
379
380
  if (incrementalSkippedDims.length > 0) {
380
381
  logger.info(
381
- `[Bootstrap-v3] ⏩ Incremental skip: [${incrementalSkippedDims.join(', ')}] ` +
382
+ `[Insight-v3] ⏩ Incremental skip: [${incrementalSkippedDims.join(', ')}] ` +
382
383
  `(using historical results)`
383
384
  );
384
385
  }
385
386
  }
386
387
 
387
388
  logger.info(
388
- `[Bootstrap-v3] Active dimensions: [${activeDimIds.join(', ')}], concurrency=${enableParallel ? concurrency : 1}${isIncremental ? `, incremental skip: [${incrementalSkippedDims.join(', ')}]` : ''}`
389
+ `[Insight-v3] Active dimensions: [${activeDimIds.join(', ')}], concurrency=${enableParallel ? concurrency : 1}${isIncremental ? `, incremental skip: [${incrementalSkippedDims.join(', ')}]` : ''}`
389
390
  );
390
391
 
391
392
  // ── P3: 断点续传 — 加载有效 checkpoints ──
@@ -404,7 +405,7 @@ export async function fillDimensionsV3(fillContext) {
404
405
  ...checkpoint,
405
406
  });
406
407
  skippedDims.push(dimId);
407
- logger.info(`[Bootstrap-v3] ⏩ 跳过已完成维度 (checkpoint): "${dimId}"`);
408
+ logger.info(`[Insight-v3] ⏩ 跳过已完成维度 (checkpoint): "${dimId}"`);
408
409
  }
409
410
  }
410
411
 
@@ -412,6 +413,10 @@ export async function fillDimensionsV3(fillContext) {
412
413
  const dimensionCandidates = {};
413
414
  const dimensionStats = {}; // P4.2: 维度级统计
414
415
 
416
+ // ── 跨维度去重集合 (实例级持久化,等效旧 ChatAgent.#globalSubmittedTitles/Patterns) ──
417
+ const globalSubmittedTitles = new Set();
418
+ const globalSubmittedPatterns = new Set();
419
+
415
420
  /**
416
421
  * 执行单个维度: Analyst → Gate → Producer
417
422
  */
@@ -432,7 +437,7 @@ export async function fillDimensionsV3(fillContext) {
432
437
  restoredFromIncremental: true,
433
438
  };
434
439
  dimensionStats[dimId] = dimResult;
435
- logger.info(`[Bootstrap-v3] ⏩ "${dimId}" — incremental skip (historical result)`);
440
+ logger.info(`[Insight-v3] ⏩ "${dimId}" — incremental skip (historical result)`);
436
441
  return dimResult;
437
442
  }
438
443
 
@@ -472,7 +477,7 @@ export async function fillDimensionsV3(fillContext) {
472
477
  candidatesSummary: [],
473
478
  });
474
479
  logger.info(
475
- `[Bootstrap-v3] ✅ Checkpoint "${dimId}": analysisText restored (${cp.analysisText.length} chars) — Skill generation enabled`
480
+ `[Insight-v3] ✅ Checkpoint "${dimId}": analysisText restored (${cp.analysisText.length} chars) — Skill generation enabled`
476
481
  );
477
482
  }
478
483
 
@@ -521,81 +526,226 @@ export async function fillDimensionsV3(fillContext) {
521
526
 
522
527
  // Session 有效性检查
523
528
  if (taskManager && !taskManager.isSessionValid(sessionId)) {
524
- logger.warn(`[Bootstrap-v3] Session superseded — skipping "${dimId}"`);
529
+ logger.warn(`[Insight-v3] Session superseded — skipping "${dimId}"`);
525
530
  return { candidateCount: 0, error: 'session-superseded' };
526
531
  }
527
532
 
528
533
  emitter.emitDimensionStart(dimId);
529
- logger.info(`[Bootstrap-v3] ── Dimension "${dimId}" (${dimConfig.label}) ──`);
534
+ logger.info(`[Insight-v3] ── Dimension "${dimId}" (${dimConfig.label}) ──`);
530
535
 
531
536
  const dimStartTime = Date.now();
532
537
 
533
538
  try {
534
- // v5.0: 为每个维度创建独立的 ActiveContext 作用域
539
+ // ═══ v3.0: 增强 PipelineStrategy 驱动 ═══
535
540
  const analystScopeId = `${dimId}:analyst`;
536
541
  memoryCoordinator.createDimensionScope(analystScopeId);
537
542
 
538
- // ── Phase 1: Analyst ──
539
- const analysisReport = await Promise.race([
540
- analystAgent.analyze(dimConfig, projectInfo, {
541
- sessionId,
542
- dimensionContext: dimContext,
543
- // v5.0: 统一 MemoryCoordinator
544
- memoryCoordinator,
545
- // v4.1: Semantic Memory (历史 bootstrap 记忆)
546
- semanticMemory,
547
- // Phase E: Code Entity Graph 代码实体图谱
548
- codeEntityGraph: codeEntityGraphInst,
549
- }),
543
+ const v3OutputType = DIMENSION_CONFIGS_V3[dimId]?.outputType;
544
+ const needsCandidates = v3OutputType
545
+ ? v3OutputType !== 'skill'
546
+ : !dimConfig.skillWorthy || dimConfig.dualOutput;
547
+
548
+ // ── 获取 Preset 的标准 stages 配置作为基础 ──
549
+ const presetStages = PRESETS.insight.strategy.stages;
550
+
551
+ // ── 构建 per-dimension 的 stages ──
552
+ // NOTE: onToolCall 不再注入 ac.recordToolCall — ToolExecutionPipeline 的
553
+ // traceRecord 中间件已通过 loopCtx.trace 统一记录,避免同一 AC 上双重记录。
554
+ const analyzeStage = {
555
+ ...presetStages[0],
556
+ };
557
+
558
+ let stages;
559
+ if (needsCandidates) {
560
+ // 候选维度: Analyze→QualityGate→Produce→RejectionGate
561
+ const produceStage = {
562
+ ...presetStages[2],
563
+ promptBuilder: (ctx) => {
564
+ memoryCoordinator.allocateBudget('producer');
565
+ return presetStages[2].promptBuilder(ctx);
566
+ },
567
+ };
568
+ stages = [
569
+ analyzeStage,
570
+ presetStages[1], // quality_gate
571
+ produceStage,
572
+ presetStages[3], // rejection_gate
573
+ ];
574
+ } else {
575
+ // Skill-only 维度: 仅 Analyze
576
+ stages = [analyzeStage];
577
+ }
578
+
579
+ // ── 创建 Runtime (使用增强 PipelineStrategy) ──
580
+ const runtime = agentFactory.createRuntime('insight', {
581
+ lang: primaryLang || projectInfo.lang || null,
582
+ strategy: {
583
+ type: 'pipeline',
584
+ maxRetries: 1,
585
+ stages,
586
+ },
587
+ });
588
+ runtime.setFileCache(allFiles);
589
+
590
+ // ── 构建消息 + strategyContext ──
591
+ const message = AgentMessage.internal(`Bootstrap dimension: ${dimConfig.label}`, {
592
+ sessionId,
593
+ dimension: dimId,
594
+ phase: 'bootstrap',
595
+ });
596
+
597
+ const strategyContext = {
598
+ dimConfig,
599
+ projectInfo,
600
+ dimContext,
601
+ sessionStore,
602
+ semanticMemory,
603
+ codeEntityGraph: codeEntityGraphInst,
604
+ projectGraph: null, // ProjectGraph 在 orchestrator 级别可用时注入
605
+ dimId,
606
+ activeContext: memoryCoordinator.getActiveContext(analystScopeId),
607
+ outputType: dimConfig.outputType || 'analysis',
608
+ // ── 引擎增强参数 (PipelineStrategy → reactLoop 透传) ──
609
+ contextWindow: agentFactory.createContextWindow({ isSystem: true }),
610
+ tracker: ExplorationTracker.resolve(
611
+ { source: 'system', dimensionMeta: dimConfig },
612
+ { maxIterations: PRESETS.insight.strategy.stages[0].budget?.maxIterations || 24 },
613
+ ),
614
+ trace: memoryCoordinator.getActiveContext(analystScopeId),
615
+ memoryCoordinator,
616
+ sharedState: {
617
+ submittedTitles: globalSubmittedTitles,
618
+ submittedPatterns: globalSubmittedPatterns,
619
+ _dimensionMeta: {
620
+ id: dimId,
621
+ outputType: dimConfig.outputType || 'candidate',
622
+ allowedKnowledgeTypes: dimConfig.allowedKnowledgeTypes || [],
623
+ },
624
+ _projectLanguage: primaryLang || projectInfo.lang || null,
625
+ _dimensionScopeId: analystScopeId,
626
+ },
627
+ source: 'system',
628
+ };
629
+
630
+ // ── 执行 ──
631
+ // 外层超时 = 安全网 (各阶段已有独立超时: Analyst 300s + Producer 180s + 硬缓冲 60s)
632
+ const outerTimeoutMs = 600_000;
633
+ const runResult = await Promise.race([
634
+ runtime.execute(message, { strategyContext }),
550
635
  new Promise((_, reject) =>
551
- setTimeout(() => reject(new Error(`Analyst timeout for "${dimId}"`)), 300_000)
636
+ setTimeout(() => reject(new Error(`Bootstrap runtime timeout for "${dimId}"`)), outerTimeoutMs),
552
637
  ),
553
638
  ]);
554
639
 
555
- // v5.0: 通过 coordinator 蒸馏 ActiveContext → SessionStore
640
+ // ── 提取结果 ──
641
+ const analyzeResult = runResult?.phases?.analyze;
642
+ const gateResult = runResult?.phases?.quality_gate;
643
+ const produceResult = runResult?.phases?.produce;
644
+ const analysisText = (analyzeResult?.reply || runResult?.reply || '').trim();
645
+ const artifact = gateResult?.artifact || {
646
+ analysisText,
647
+ referencedFiles: [],
648
+ findings: [],
649
+ metadata: { toolCallCount: 0 },
650
+ };
651
+
652
+ const runtimeToolCalls = runResult?.toolCalls || [];
653
+ const combinedTokenUsage = runResult?.tokenUsage || { input: 0, output: 0 };
654
+
655
+ // 引用文件: 优先从 artifact 取, 回退从 toolCalls 提取
656
+ const referencedFiles = artifact.referencedFiles?.length > 0
657
+ ? artifact.referencedFiles
658
+ : [...new Set(runtimeToolCalls.flatMap((tc) => {
659
+ const a = tc?.args || tc?.params || {};
660
+ const files = [];
661
+ if (typeof a.filePath === 'string' && a.filePath.trim()) files.push(a.filePath.trim());
662
+ if (Array.isArray(a.filePaths)) {
663
+ for (const f of a.filePaths) {
664
+ if (typeof f === 'string' && f.trim()) files.push(f.trim());
665
+ }
666
+ }
667
+ return files;
668
+ }))];
669
+
670
+ const analysisReport = {
671
+ dimensionId: dimId,
672
+ analysisText: artifact.analysisText || analysisText,
673
+ findings: artifact.findings || [],
674
+ referencedFiles,
675
+ evidenceMap: artifact.evidenceMap || null,
676
+ negativeSignals: artifact.negativeSignals || [],
677
+ metadata: {
678
+ toolCallCount: runtimeToolCalls.length,
679
+ tokenUsage: combinedTokenUsage,
680
+ artifactVersion: artifact.metadata?.artifactVersion || 1,
681
+ },
682
+ };
683
+
684
+ // ── Producer 结果统计 ──
685
+ const submitCalls = runtimeToolCalls.filter(tc => {
686
+ const tool = tc?.tool || tc?.name;
687
+ return tool === 'submit_knowledge' || tool === 'submit_with_check';
688
+ });
689
+ const successCount = submitCalls.filter(tc => {
690
+ const res = tc?.result;
691
+ if (!res) return true;
692
+ if (typeof res === 'string') return !res.includes('rejected') && !res.includes('error');
693
+ return res.status !== 'rejected' && res.status !== 'error';
694
+ }).length;
695
+ const rejectedCount = submitCalls.length - successCount;
696
+
697
+ const producerResult = {
698
+ candidateCount: needsCandidates ? successCount : 0,
699
+ rejectedCount: needsCandidates ? rejectedCount : 0,
700
+ toolCalls: runtimeToolCalls,
701
+ reply: produceResult?.reply || analysisText,
702
+ tokenUsage: combinedTokenUsage,
703
+ };
704
+
705
+ candidateResults.created += producerResult.candidateCount;
706
+ dimensionCandidates[dimId] = { analysisReport, producerResult };
707
+
708
+ // ── Memory Update ──
556
709
  const ac = memoryCoordinator.getActiveContext(analystScopeId);
557
710
  const distilled = ac ? ac.distill() : { keyFindings: [], totalObservations: 0, toolCallSummary: [] };
558
711
  sessionStore.storeDimensionReport(dimId, {
559
712
  analysisText: analysisReport.analysisText,
560
- findings: analysisReport.findings || distilled.keyFindings,
713
+ findings: analysisReport.findings.length > 0 ? analysisReport.findings : distilled.keyFindings,
561
714
  referencedFiles: analysisReport.referencedFiles || [],
562
715
  candidatesSummary: [],
563
716
  workingMemoryDistilled: distilled,
564
717
  });
565
718
 
566
- // v4.2: 记录 Artifact 增强数据 (如果使用了 v2 pipeline)
567
- const isArtifact = !!analysisReport.evidenceMap;
568
- const evidenceMapSize = isArtifact ? analysisReport.evidenceMap.size : 0;
569
- const negativeSignalCount = isArtifact ? analysisReport.negativeSignals?.length || 0 : 0;
570
- const qualityScore = isArtifact ? analysisReport.qualityReport?.totalScore : null;
571
-
572
719
  logger.info(
573
- `[Bootstrap-v3] Analyst "${dimId}": ${analysisReport.analysisText.length} chars, ` +
574
- `${analysisReport.referencedFiles.length} files, ` +
575
- `${distilled.keyFindings.length} key findings, ` +
576
- `${distilled.totalObservations} observations` +
577
- (isArtifact
578
- ? `, evidence: ${evidenceMapSize} files, negative: ${negativeSignalCount}, quality: ${qualityScore}`
579
- : '') +
580
- ` (${Date.now() - dimStartTime}ms)`
720
+ `[Insight-v3] Dimension "${dimId}": analysis=${analysisReport.analysisText.length} chars, ` +
721
+ `files=${analysisReport.referencedFiles.length}, findings=${(analysisReport.findings || distilled.keyFindings).length}, ` +
722
+ `toolCalls=${runtimeToolCalls.length}, degraded=${runResult?.degraded || false} (${Date.now() - dimStartTime}ms)`,
581
723
  );
582
724
 
583
- // ── Phase 2: Producer (如果需要候选输出) ──
584
- let producerResult = { candidateCount: 0, toolCalls: [], reply: '' };
585
- // v3 优先使用 DIMENSION_CONFIGS_V3 的 outputType,回退到 baseDimension 的 skillWorthy/dualOutput
586
- const v3OutputType = DIMENSION_CONFIGS_V3[dimId]?.outputType;
587
- const needsCandidates = v3OutputType
588
- ? v3OutputType !== 'skill' // 'dual' 或 'candidate' 都产出候选
589
- : !dimConfig.skillWorthy || dimConfig.dualOutput;
590
-
591
- // 先保存 Analyst 结果,确保即使 Producer 失败也能生成 Skill
592
- dimensionCandidates[dimId] = {
593
- analysisReport,
594
- producerResult,
595
- };
725
+ // ── Token 用量持久化 (fire-and-forget) ──
726
+ try {
727
+ const tokenStore = ctx.container?.get?.('tokenUsageStore');
728
+ if (tokenStore) {
729
+ const aiProv = runtime.aiProvider;
730
+ tokenStore.record({
731
+ source: 'system',
732
+ dimension: dimId,
733
+ provider: aiProv?.name || null,
734
+ model: aiProv?.model || null,
735
+ inputTokens: combinedTokenUsage.input || 0,
736
+ outputTokens: combinedTokenUsage.output || 0,
737
+ durationMs: Date.now() - dimStartTime,
738
+ toolCalls: runtimeToolCalls.length,
739
+ sessionId: sessionId || null,
740
+ });
741
+ try {
742
+ const realtime = ctx.container?.get?.('realtimeService');
743
+ realtime?.broadcastTokenUsageUpdated?.();
744
+ } catch { /* optional */ }
745
+ }
746
+ } catch { /* token logging should never break execution */ }
596
747
 
597
- // v5.1: analysisText 过短(force-exit 时 AI 仅输出 digest 被清洗后仅剩 50-80 chars)
598
- // 但有足够的结构化发现时,从 findings 合成补充文本,避免 Producer 被 100 char 门控拦截
748
+ // ── v5.1: analysisText 过短补强 ──
599
749
  if (needsCandidates && analysisReport.analysisText.length < 100) {
600
750
  const findings = analysisReport.findings || [];
601
751
  if (findings.length >= 3) {
@@ -612,9 +762,7 @@ export async function fillDimensionsV3(fillContext) {
612
762
  return `${i + 1}. ${text}`;
613
763
  }),
614
764
  ];
615
- // 追加探索记录 (如果有)
616
- const dimReport = sessionStore.getDimensionReport(dimId);
617
- const memDistilled = dimReport?.workingMemoryDistilled;
765
+ const memDistilled = distilled;
618
766
  if (memDistilled?.toolCallSummary?.length > 0) {
619
767
  synthesized.push('', '### 探索记录', '');
620
768
  for (const s of memDistilled.toolCallSummary.slice(0, 10)) {
@@ -624,71 +772,13 @@ export async function fillDimensionsV3(fillContext) {
624
772
  const originalLen = analysisReport.analysisText.length;
625
773
  analysisReport.analysisText = synthesized.join('\n');
626
774
  logger.info(
627
- `[Bootstrap-v3] analysisText 补强 "${dimId}": ${originalLen} → ${analysisReport.analysisText.length} chars ` +
628
- `(from ${findings.length} findings)`
629
- );
630
- }
631
- }
632
-
633
- if (needsCandidates && analysisReport.analysisText.length >= 100) {
634
- try {
635
- // v5.0: 为 Producer 创建独立作用域
636
- const producerScopeId = `${dimId}:producer`;
637
- memoryCoordinator.createDimensionScope(producerScopeId);
638
-
639
- const producerPromise = producerAgent.produce(analysisReport, dimConfig, projectInfo, {
640
- sessionId,
641
- memoryCoordinator,
642
- });
643
-
644
- producerResult = await Promise.race([
645
- producerPromise,
646
- new Promise((_, reject) =>
647
- setTimeout(() => reject(new Error(`Producer timeout for "${dimId}"`)), 180_000)
648
- ),
649
- ]);
650
-
651
- candidateResults.created += producerResult.candidateCount;
652
- // 更新 dimensionCandidates 以包含 Producer 结果
653
- dimensionCandidates[dimId].producerResult = producerResult;
654
- logger.info(
655
- `[Bootstrap-v3] Producer "${dimId}": ${producerResult.candidateCount} candidates (${Date.now() - dimStartTime}ms total)`
656
- );
657
- } catch (producerErr) {
658
- logger.error(
659
- `[Bootstrap-v3] Producer "${dimId}" failed: ${producerErr.message} — Analyst result preserved for Skill generation`
775
+ `[Insight-v3] analysisText 补强 "${dimId}": ${originalLen} → ${analysisReport.analysisText.length} chars ` +
776
+ `(from ${findings.length} findings)`,
660
777
  );
661
- candidateResults.errors.push({ dimId, error: `Producer: ${producerErr.message}` });
662
-
663
- // v5.1: 超时后异步监听实际结果,避免 ghost candidates 永远不被计数
664
- if (producerErr.message.includes('timeout')) {
665
- const dimIdRef = dimId;
666
- // producerPromise 仍在后台执行 — 监听完成后更新统计
667
- // biome-ignore lint: 故意 fire-and-forget
668
- producerPromise
669
- ?.then((actualResult) => {
670
- const count = actualResult?.candidateCount || 0;
671
- if (count > 0) {
672
- logger.info(
673
- `[Bootstrap-v3] Producer "${dimIdRef}" completed post-timeout: ${count} candidates (ghost → reconciled)`
674
- );
675
- if (dimensionStats[dimIdRef]) {
676
- dimensionStats[dimIdRef].candidateCount = count;
677
- }
678
- candidateResults.created += count;
679
- dimensionCandidates[dimIdRef].producerResult = actualResult;
680
- }
681
- })
682
- .catch((finalErr) => {
683
- logger.warn(
684
- `[Bootstrap-v3] Producer "${dimIdRef}" also failed post-timeout: ${finalErr.message}`
685
- );
686
- });
687
- }
688
778
  }
689
779
  }
690
780
 
691
- // ── Phase 3: 记录 DimensionDigest ──
781
+ // ── DimensionDigest ──
692
782
  const digest = parseDimensionDigest(producerResult.reply) || {
693
783
  summary: `v3 分析: ${analysisReport.analysisText.substring(0, 200)}...`,
694
784
  candidateCount: producerResult.candidateCount,
@@ -697,21 +787,19 @@ export async function fillDimensionsV3(fillContext) {
697
787
  gaps: [],
698
788
  };
699
789
  dimContext.addDimensionDigest(dimId, digest);
700
-
701
- // v4.0: 同步 digest 到 SessionStore
702
790
  sessionStore.addDimensionDigest(dimId, digest);
703
791
 
704
- // 记录到 DimensionContext + EpisodicMemory
792
+ // 候选摘要记录到 DimensionContext + SessionStore
705
793
  for (const tc of producerResult.toolCalls || []) {
706
794
  const tool = tc.tool || tc.name;
707
795
  if (tool === 'submit_knowledge' || tool === 'submit_with_check') {
796
+ const args = tc.params || tc.args || {};
708
797
  const candidateSummary = {
709
- title: tc.params?.title || '',
710
- subTopic: tc.params?.category || '',
711
- summary: tc.params?.summary || '',
798
+ title: args.title || '',
799
+ subTopic: args.category || '',
800
+ summary: args.summary || '',
712
801
  };
713
802
  dimContext.addSubmittedCandidate(dimId, candidateSummary);
714
- // v4.0: 同步到 SessionStore
715
803
  sessionStore.addSubmittedCandidate(dimId, candidateSummary);
716
804
  }
717
805
  }
@@ -720,46 +808,41 @@ export async function fillDimensionsV3(fillContext) {
720
808
  type: needsCandidates ? 'candidate' : 'skill',
721
809
  extracted: producerResult.candidateCount,
722
810
  created: producerResult.candidateCount,
723
- // v5.1: 标记 Skill 待生成,Dashboard 可据此避免显示 "无匹配内容"
724
811
  skillPending: dimConfig.skillWorthy && producerResult.candidateCount === 0,
725
- status: 'v3-complete',
812
+ status: 'v3-pipeline-complete',
813
+ degraded: runResult?.degraded || false,
726
814
  durationMs: Date.now() - dimStartTime,
727
- toolCallCount:
728
- (analysisReport.metadata?.toolCallCount || 0) + (producerResult.toolCalls?.length || 0),
729
- source: 'internal-agent',
815
+ toolCallCount: runtimeToolCalls.length,
816
+ source: 'enhanced-pipeline-strategy',
730
817
  });
731
818
 
732
- // P4.1: 聚合 token 用量
733
- const analystTokens = analysisReport.metadata?.tokenUsage || { input: 0, output: 0 };
734
- const producerTokens = producerResult.tokenUsage || { input: 0, output: 0 };
735
- const dimTokenUsage = {
736
- input: (analystTokens.input || 0) + (producerTokens.input || 0),
737
- output: (analystTokens.output || 0) + (producerTokens.output || 0),
738
- };
739
-
819
+ const dimTokenUsage = combinedTokenUsage;
740
820
  const dimResult = {
741
821
  candidateCount: producerResult.candidateCount,
742
822
  rejectedCount: producerResult.rejectedCount || 0,
743
823
  analysisChars: analysisReport.analysisText.length,
744
824
  referencedFiles: analysisReport.referencedFiles.length,
745
825
  durationMs: Date.now() - dimStartTime,
746
- toolCallCount:
747
- (analysisReport.metadata?.toolCallCount || 0) + (producerResult.toolCalls?.length || 0),
826
+ toolCallCount: runtimeToolCalls.length,
748
827
  tokenUsage: dimTokenUsage,
749
- // P3+: 保存 analysisText 供 checkpoint 恢复后 Skill 生成使用
750
828
  analysisText: analysisReport.analysisText,
751
829
  referencedFilesList: analysisReport.referencedFiles || [],
752
830
  };
753
831
 
754
- // P4.2: 记录维度统计
755
832
  dimensionStats[dimId] = dimResult;
756
833
 
757
- // P3: 保存 checkpoint
758
- await saveDimensionCheckpoint(projectRoot, sessionId, dimId, dimResult, digest);
834
+ // P3: 保存 checkpoint — 仅当有实质分析内容时(避免 degraded/空结果污染后续 run)
835
+ if (analysisReport.analysisText.length >= 50) {
836
+ await saveDimensionCheckpoint(projectRoot, sessionId, dimId, dimResult, digest);
837
+ } else {
838
+ logger.warn(
839
+ `[Insight-v3] ⚠ 跳过 checkpoint 保存: "${dimId}" analysisText 过短 (${analysisReport.analysisText.length} chars)`
840
+ );
841
+ }
759
842
 
760
843
  return dimResult;
761
844
  } catch (err) {
762
- logger.error(`[Bootstrap-v3] Dimension "${dimId}" failed: ${err.message}`);
845
+ logger.error(`[Insight-v3] Dimension "${dimId}" failed: ${err.message}`);
763
846
  candidateResults.errors.push({ dimId, error: err.message });
764
847
  emitter.emitDimensionComplete(dimId, { type: 'error', reason: err.message });
765
848
  return { candidateCount: 0, error: err.message };
@@ -789,7 +872,7 @@ export async function fillDimensionsV3(fillContext) {
789
872
  const tierStats = [...tierResults.values()];
790
873
  const totalCandidates = tierStats.reduce((s, r) => s + (r.candidateCount || 0), 0);
791
874
  logger.info(
792
- `[Bootstrap-v3] Tier ${tierIndex + 1} complete: ${tierResults.size} dimensions, ${totalCandidates} candidates`
875
+ `[Insight-v3] Tier ${tierIndex + 1} complete: ${tierResults.size} dimensions, ${totalCandidates} candidates`
793
876
  );
794
877
 
795
878
  // v4.0: Tier 级 Reflection — 综合本 Tier 所有维度的发现
@@ -797,29 +880,29 @@ export async function fillDimensionsV3(fillContext) {
797
880
  const reflection = buildTierReflection(tierIndex, tierResults, sessionStore);
798
881
  sessionStore.addTierReflection(tierIndex, reflection);
799
882
  logger.info(
800
- `[Bootstrap-v3] Tier ${tierIndex + 1} reflection: ` +
883
+ `[Insight-v3] Tier ${tierIndex + 1} reflection: ` +
801
884
  `${reflection.topFindings.length} top findings, ` +
802
885
  `${reflection.crossDimensionPatterns.length} patterns`
803
886
  );
804
887
  } catch (refErr) {
805
- logger.warn(`[Bootstrap-v3] Tier ${tierIndex + 1} reflection failed: ${refErr.message}`);
888
+ logger.warn(`[Insight-v3] Tier ${tierIndex + 1} reflection failed: ${refErr.message}`);
806
889
  }
807
890
  },
808
891
  });
809
892
 
810
893
  logger.info(
811
- `[Bootstrap-v3] All tiers complete: ${results.size} dimensions in ${Date.now() - t0}ms`
894
+ `[Insight-v3] All tiers complete: ${results.size} dimensions in ${Date.now() - t0}ms`
812
895
  );
813
896
  // v4.0: 记录 SessionStore 统计
814
897
  const emStats = sessionStore.getStats();
815
898
  logger.info(
816
- `[Bootstrap-v3] Memory stats: ${emStats.completedDimensions} dims, ` +
899
+ `[Insight-v3] Memory stats: ${emStats.completedDimensions} dims, ` +
817
900
  `${emStats.totalFindings} findings, ${emStats.referencedFiles} files, ` +
818
901
  `${emStats.crossReferences} cross-refs, ${emStats.tierReflections} reflections`
819
902
  );
820
903
  if (emStats.cacheStats) {
821
904
  logger.info(
822
- `[Bootstrap-v3] Cache stats: ${emStats.cacheStats.hitRate} hit rate, ` +
905
+ `[Insight-v3] Cache stats: ${emStats.cacheStats.hitRate} hit rate, ` +
823
906
  `${emStats.cacheStats.searchCacheSize} searches, ${emStats.cacheStats.fileCacheSize} files`
824
907
  );
825
908
  }
@@ -836,7 +919,7 @@ export async function fillDimensionsV3(fillContext) {
836
919
  await executeDimension(dimId);
837
920
  }
838
921
  }
839
- logger.info(`[Bootstrap-v3] Serial execution complete in ${Date.now() - t0}ms`);
922
+ logger.info(`[Insight-v3] Serial execution complete in ${Date.now() - t0}ms`);
840
923
  }
841
924
 
842
925
  // ═══════════════════════════════════════════════════════════
@@ -893,7 +976,7 @@ export async function fillDimensionsV3(fillContext) {
893
976
  }
894
977
  effectiveText = synthesized.join('\n');
895
978
  logger.info(
896
- `[Bootstrap-v3] Skill "${dim.id}": analysisText too short (${analysisText.trim().length} chars), ` +
979
+ `[Insight-v3] Skill "${dim.id}": analysisText too short (${analysisText.trim().length} chars), ` +
897
980
  `synthesized from ${keyFindings.length} findings → ${effectiveText.length} chars`
898
981
  );
899
982
  }
@@ -917,14 +1000,14 @@ export async function fillDimensionsV3(fillContext) {
917
1000
  emitter.emitDimensionFailed(dim.id, new Error(result.error));
918
1001
  }
919
1002
  } catch (err) {
920
- logger.warn(`[Bootstrap-v3] Skill generation failed for "${dim.id}": ${err.message}`);
1003
+ logger.warn(`[Insight-v3] Skill generation failed for "${dim.id}": ${err.message}`);
921
1004
  skillResults.failed++;
922
1005
  skillResults.errors.push({ dimId: dim.id, error: err.message });
923
1006
  emitter.emitDimensionFailed(dim.id, err);
924
1007
  }
925
1008
  }
926
1009
  } catch (e) {
927
- logger.warn(`[Bootstrap-v3] Skill generation module import failed: ${e.message}`);
1010
+ logger.warn(`[Insight-v3] Skill generation module import failed: ${e.message}`);
928
1011
  }
929
1012
 
930
1013
  // ═══════════════════════════════════════════════════════════
@@ -958,13 +1041,13 @@ export async function fillDimensionsV3(fillContext) {
958
1041
  if (allCandidates.length > 0) {
959
1042
  const relResult = ceg.populateFromCandidateRelations(allCandidates);
960
1043
  logger.info(
961
- `[Bootstrap-v3] Code Entity Graph relations: ${relResult.edgesCreated} edges from ${allCandidates.length} candidates (${relResult.durationMs}ms)`
1044
+ `[Insight-v3] Code Entity Graph relations: ${relResult.edgesCreated} edges from ${allCandidates.length} candidates (${relResult.durationMs}ms)`
962
1045
  );
963
1046
  }
964
1047
  }
965
1048
  } catch (cegErr) {
966
1049
  logger.warn(
967
- `[Bootstrap-v3] Code Entity Graph relations failed (non-blocking): ${cegErr.message}`
1050
+ `[Insight-v3] Code Entity Graph relations failed (non-blocking): ${cegErr.message}`
968
1051
  );
969
1052
  }
970
1053
 
@@ -978,7 +1061,7 @@ export async function fillDimensionsV3(fillContext) {
978
1061
  try {
979
1062
  const db = ctx.container.get('database');
980
1063
  if (db) {
981
- const semanticMemory = new ProjectSemanticMemory(db, { logger });
1064
+ const semanticMemory = new PersistentMemory(db, { logger });
982
1065
  const consolidator = new EpisodicConsolidator(semanticMemory, { logger });
983
1066
 
984
1067
  consolidationResult = consolidator.consolidate(sessionStore, {
@@ -988,18 +1071,18 @@ export async function fillDimensionsV3(fillContext) {
988
1071
 
989
1072
  const smStats = semanticMemory.getStats();
990
1073
  logger.info(
991
- `[Bootstrap-v3] Semantic Memory consolidation: ` +
1074
+ `[Insight-v3] Semantic Memory consolidation: ` +
992
1075
  `+${consolidationResult.total.added} ADD, ` +
993
1076
  `~${consolidationResult.total.updated} UPDATE, ` +
994
1077
  `⊕${consolidationResult.total.merged} MERGE | ` +
995
1078
  `Total: ${smStats.total} memories (avg importance: ${smStats.avgImportance})`
996
1079
  );
997
1080
  } else {
998
- logger.warn('[Bootstrap-v3] Database not available — skipping Semantic Memory consolidation');
1081
+ logger.warn('[Insight-v3] Database not available — skipping Semantic Memory consolidation');
999
1082
  }
1000
1083
  } catch (consolidateErr) {
1001
1084
  logger.warn(
1002
- `[Bootstrap-v3] Semantic Memory consolidation failed (non-blocking): ${consolidateErr.message}`
1085
+ `[Insight-v3] Semantic Memory consolidation failed (non-blocking): ${consolidateErr.message}`
1003
1086
  );
1004
1087
  }
1005
1088
 
@@ -1023,7 +1106,7 @@ export async function fillDimensionsV3(fillContext) {
1023
1106
 
1024
1107
  logger.info(
1025
1108
  [
1026
- `[Bootstrap-v3] ═══ Pipeline complete ═══`,
1109
+ `[Insight-v3] ═══ Pipeline complete ═══`,
1027
1110
  isIncremental
1028
1111
  ? ` Mode: INCREMENTAL (${incrementalPlan.affectedDimensions.length} affected, ${incrementalSkippedDims.length} skipped)`
1029
1112
  : '',
@@ -1136,9 +1219,9 @@ export async function fillDimensionsV3(fillContext) {
1136
1219
  path.join(reportDir, 'bootstrap-report.json'),
1137
1220
  JSON.stringify(report, null, 2)
1138
1221
  );
1139
- logger.info(`[Bootstrap-v3] 📊 Bootstrap report saved to .autosnippet/bootstrap-report.json`);
1222
+ logger.info(`[Insight-v3] 📊 Bootstrap report saved to .autosnippet/bootstrap-report.json`);
1140
1223
  } catch (reportErr) {
1141
- logger.warn(`[Bootstrap-v3] Bootstrap report generation failed: ${reportErr.message}`);
1224
+ logger.warn(`[Insight-v3] Bootstrap report generation failed: ${reportErr.message}`);
1142
1225
  }
1143
1226
 
1144
1227
  // P3: 成功完成后清理 checkpoints
@@ -1161,15 +1244,15 @@ export async function fillDimensionsV3(fillContext) {
1161
1244
  },
1162
1245
  plan: isIncremental ? incrementalPlan : null,
1163
1246
  });
1164
- logger.info(`[Bootstrap-v3] 📸 Snapshot saved: ${snapshotId}`);
1247
+ logger.info(`[Insight-v3] 📸 Snapshot saved: ${snapshotId}`);
1165
1248
  }
1166
1249
  } catch (snapErr) {
1167
- logger.warn(`[Bootstrap-v3] Snapshot save failed (non-blocking): ${snapErr.message}`);
1250
+ logger.warn(`[Insight-v3] Snapshot save failed (non-blocking): ${snapErr.message}`);
1168
1251
  }
1169
1252
 
1170
1253
  // 释放文件缓存
1171
1254
  allFiles = null;
1172
- chatAgent.setFileCache(null);
1255
+ ctx.container.singletons._fileCache = null;
1173
1256
 
1174
1257
  // ── Cursor Delivery: 生成 4 通道交付物料 ──
1175
1258
  try {
@@ -1179,7 +1262,7 @@ export async function fillDimensionsV3(fillContext) {
1179
1262
  const pipeline = container.get('cursorDeliveryPipeline');
1180
1263
  const deliveryResult = await pipeline.deliver();
1181
1264
  logger.info(
1182
- `[Bootstrap-v3] 🚀 Cursor Delivery complete — ` +
1265
+ `[Insight-v3] 🚀 Cursor Delivery complete — ` +
1183
1266
  `A: ${deliveryResult.channelA.rulesCount} rules, ` +
1184
1267
  `B: ${deliveryResult.channelB.topicCount} topics, ` +
1185
1268
  `C: ${deliveryResult.channelC.synced} skills, ` +
@@ -1188,7 +1271,7 @@ export async function fillDimensionsV3(fillContext) {
1188
1271
  );
1189
1272
  }
1190
1273
  } catch (deliveryErr) {
1191
- logger.warn(`[Bootstrap-v3] Cursor Delivery failed (non-blocking): ${deliveryErr.message}`);
1274
+ logger.warn(`[Insight-v3] Cursor Delivery failed (non-blocking): ${deliveryErr.message}`);
1192
1275
  }
1193
1276
 
1194
1277
  // ── Repo Wiki: 自动生成项目文档 Wiki ──
@@ -1265,7 +1348,7 @@ export async function fillDimensionsV3(fillContext) {
1265
1348
  const wikiResult = await wiki.generate();
1266
1349
  if (wikiResult.success) {
1267
1350
  logger.info(
1268
- `[Bootstrap-v3] 📖 Wiki generated — ${wikiResult.filesGenerated} files, ` +
1351
+ `[Insight-v3] 📖 Wiki generated — ${wikiResult.filesGenerated} files, ` +
1269
1352
  `AI: ${wikiResult.aiComposed || 0}, Synced: ${wikiResult.syncedDocs || 0}, ` +
1270
1353
  `Dedup removed: ${wikiResult.dedup?.removed?.length || 0}`
1271
1354
  );
@@ -1289,7 +1372,7 @@ export async function fillDimensionsV3(fillContext) {
1289
1372
  } catch { /* non-critical */ }
1290
1373
  }
1291
1374
  } catch (wikiErr) {
1292
- logger.warn(`[Bootstrap-v3] Wiki generation failed (non-blocking): ${wikiErr.message}`);
1375
+ logger.warn(`[Insight-v3] Wiki generation failed (non-blocking): ${wikiErr.message}`);
1293
1376
  try {
1294
1377
  const wikiRoute = await import('../../../../../http/routes/wiki.js');
1295
1378
  wikiRoute.patchWikiTask?.({