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
@@ -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,16 @@
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 { ANALYST_BUDGET } from '../../../../../service/agent/domain/insight-analyst.js';
28
+ import { ContextWindow } from '../../../../../service/agent/context/ContextWindow.js';
29
+ import { ExplorationTracker } from '../../../../../service/agent/context/ExplorationTracker.js';
26
30
  import { clearCheckpoints, loadCheckpoints, saveDimensionCheckpoint } from './checkpoint.js';
27
31
  import { buildTierReflection, DIMENSION_CONFIGS_V3, getFullDimensionConfig } from './dimension-configs.js';
28
32
  import { getDimensionFocusKeywords } from '../shared/dimension-sop.js';
@@ -61,30 +65,29 @@ export async function fillDimensionsV3(fillContext) {
61
65
  const isIncremental = incrementalPlan?.canIncremental && incrementalPlan?.mode === 'incremental';
62
66
  const emitter = new BootstrapEventEmitter(ctx.container);
63
67
  logger.info(
64
- `[Bootstrap-v3] ═══ fillDimensionsV3 entered — ${isIncremental ? 'INCREMENTAL' : 'FULL'} pipeline`
68
+ `[Insight-v3] ═══ fillDimensionsV3 entered — ${isIncremental ? 'INCREMENTAL' : 'FULL'} pipeline`
65
69
  );
66
70
 
67
71
  let allFiles = fillContext.allFiles;
68
72
  fillContext.allFiles = null;
69
73
 
70
74
  // ═══════════════════════════════════════════════════════════
71
- // Step 0: AI 可用性检查
75
+ // Step 0: AI 可用性检查 (v7.2: 使用 AgentFactory)
72
76
  // ═══════════════════════════════════════════════════════════
73
- let chatAgent = null;
77
+ let agentFactory = null;
74
78
  try {
75
- chatAgent = ctx.container.get('chatAgent');
76
- if (chatAgent && !chatAgent.hasRealAI) {
77
- chatAgent = null;
78
- }
79
- if (chatAgent) {
80
- chatAgent.resetGlobalSubmittedTitles();
79
+ agentFactory = ctx.container.get('agentFactory');
80
+ // 检查 AI Provider 是否可用
81
+ const aiProvider = ctx.container.singletons?.aiProvider;
82
+ if (!aiProvider || aiProvider.name === 'mock') {
83
+ agentFactory = null;
81
84
  }
82
85
  } catch {
83
86
  /* not available */
84
87
  }
85
88
 
86
- if (!chatAgent) {
87
- logger.info('[Bootstrap-v3] AI not available — entering rule-based fallback');
89
+ if (!agentFactory) {
90
+ logger.info('[Insight-v3] AI not available — entering rule-based fallback');
88
91
  emitter.emitProgress('bootstrap:ai-unavailable', {
89
92
  message: 'AI 不可用,将使用规则化降级提取基础知识。请配置 AI Provider 以获取完整分析。',
90
93
  });
@@ -247,21 +250,20 @@ export async function fillDimensionsV3(fillContext) {
247
250
  if (projectGraph) {
248
251
  const overview = projectGraph.getOverview();
249
252
  logger.info(
250
- `[Bootstrap-v3] ProjectGraph: ${overview.totalClasses} classes, ${overview.totalProtocols} protocols (${overview.buildTimeMs}ms)`
253
+ `[Insight-v3] ProjectGraph: ${overview.totalClasses} classes, ${overview.totalProtocols} protocols (${overview.buildTimeMs}ms)`
251
254
  );
252
255
  }
253
256
  } catch (e) {
254
- logger.warn(`[Bootstrap-v3] ProjectGraph build failed: ${e.message}`);
257
+ logger.warn(`[Insight-v3] ProjectGraph build failed: ${e.message}`);
255
258
  }
256
259
 
257
260
  // ═══════════════════════════════════════════════════════════
258
261
  // Step 1: 构建 Agents + 上下文
259
262
  // ═══════════════════════════════════════════════════════════
260
- const analystAgent = new AnalystAgent(chatAgent, projectGraph, { maxRetries: 1 });
261
- const producerAgent = new ProducerAgent(chatAgent);
263
+ logger.info('[Insight-v7] Using unified AgentRuntime pipeline (no legacy Analyst/Producer wrappers)');
262
264
 
263
- // 注入文件缓存
264
- chatAgent.setFileCache(allFiles);
265
+ // 注入文件缓存到容器 (v7.2: 通过容器传递)
266
+ ctx.container.singletons._fileCache = allFiles;
265
267
 
266
268
  // 项目信息
267
269
  const projectInfo = {
@@ -289,7 +291,7 @@ export async function fillDimensionsV3(fillContext) {
289
291
  sessionStore = incrementalPlan.restoredEpisodic;
290
292
  const restoredDims = sessionStore.getCompletedDimensions();
291
293
  logger.info(
292
- `[Bootstrap-v3] Restored SessionStore: ${restoredDims.length} dims [${restoredDims.join(', ')}]`
294
+ `[Insight-v3] Restored SessionStore: ${restoredDims.length} dims [${restoredDims.join(', ')}]`
293
295
  );
294
296
 
295
297
  // 同步恢复 DimensionContext 的 digests (兼容)
@@ -308,23 +310,23 @@ export async function fillDimensionsV3(fillContext) {
308
310
  });
309
311
  }
310
312
 
311
- // v4.1: ProjectSemanticMemory — 项目级永久语义记忆 (Tier 3)
312
- // 加载历史 bootstrap 记忆 → 注入 AnalystAgent prompt
313
+ // v4.1: PersistentMemory — 项目级永久语义记忆 (Tier 3)
314
+ // 加载历史 bootstrap 记忆 → 注入 Analyst promptBuilder
313
315
  let semanticMemory = null;
314
316
  try {
315
317
  const db = ctx.container.get('database');
316
318
  if (db) {
317
- semanticMemory = new ProjectSemanticMemory(db, { logger });
319
+ semanticMemory = new PersistentMemory(db, { logger });
318
320
  const smStats = semanticMemory.getStats();
319
321
  if (smStats.total > 0) {
320
322
  logger.info(
321
- `[Bootstrap-v3] Loaded ${smStats.total} semantic memories from previous bootstrap ` +
323
+ `[Insight-v3] Loaded ${smStats.total} semantic memories from previous bootstrap ` +
322
324
  `(fact: ${smStats.byType.fact || 0}, insight: ${smStats.byType.insight || 0}, preference: ${smStats.byType.preference || 0})`
323
325
  );
324
326
  }
325
327
  }
326
328
  } catch (smErr) {
327
- logger.warn(`[Bootstrap-v3] SemanticMemory init failed (non-blocking): ${smErr.message}`);
329
+ logger.warn(`[Insight-v3] SemanticMemory init failed (non-blocking): ${smErr.message}`);
328
330
  }
329
331
 
330
332
  // Phase E: CodeEntityGraph — 代码实体关系图谱 (供 Analyst prompt 注入)
@@ -337,12 +339,12 @@ export async function fillDimensionsV3(fillContext) {
337
339
  const topo = codeEntityGraphInst.getTopology();
338
340
  if (topo.totalEntities > 0) {
339
341
  logger.info(
340
- `[Bootstrap-v3] CodeEntityGraph: ${topo.totalEntities} entities, ${topo.totalEdges} edges`
342
+ `[Insight-v3] CodeEntityGraph: ${topo.totalEntities} entities, ${topo.totalEdges} edges`
341
343
  );
342
344
  }
343
345
  }
344
346
  } catch (cegErr) {
345
- logger.warn(`[Bootstrap-v3] CodeEntityGraph init failed (non-blocking): ${cegErr.message}`);
347
+ logger.warn(`[Insight-v3] CodeEntityGraph init failed (non-blocking): ${cegErr.message}`);
346
348
  }
347
349
 
348
350
  // v5.0: MemoryCoordinator — 统一记忆协调器 (会话级)
@@ -378,14 +380,14 @@ export async function fillDimensionsV3(fillContext) {
378
380
  }
379
381
  if (incrementalSkippedDims.length > 0) {
380
382
  logger.info(
381
- `[Bootstrap-v3] ⏩ Incremental skip: [${incrementalSkippedDims.join(', ')}] ` +
383
+ `[Insight-v3] ⏩ Incremental skip: [${incrementalSkippedDims.join(', ')}] ` +
382
384
  `(using historical results)`
383
385
  );
384
386
  }
385
387
  }
386
388
 
387
389
  logger.info(
388
- `[Bootstrap-v3] Active dimensions: [${activeDimIds.join(', ')}], concurrency=${enableParallel ? concurrency : 1}${isIncremental ? `, incremental skip: [${incrementalSkippedDims.join(', ')}]` : ''}`
390
+ `[Insight-v3] Active dimensions: [${activeDimIds.join(', ')}], concurrency=${enableParallel ? concurrency : 1}${isIncremental ? `, incremental skip: [${incrementalSkippedDims.join(', ')}]` : ''}`
389
391
  );
390
392
 
391
393
  // ── P3: 断点续传 — 加载有效 checkpoints ──
@@ -404,7 +406,7 @@ export async function fillDimensionsV3(fillContext) {
404
406
  ...checkpoint,
405
407
  });
406
408
  skippedDims.push(dimId);
407
- logger.info(`[Bootstrap-v3] ⏩ 跳过已完成维度 (checkpoint): "${dimId}"`);
409
+ logger.info(`[Insight-v3] ⏩ 跳过已完成维度 (checkpoint): "${dimId}"`);
408
410
  }
409
411
  }
410
412
 
@@ -412,6 +414,10 @@ export async function fillDimensionsV3(fillContext) {
412
414
  const dimensionCandidates = {};
413
415
  const dimensionStats = {}; // P4.2: 维度级统计
414
416
 
417
+ // ── 跨维度去重集合 (实例级持久化,等效旧 ChatAgent.#globalSubmittedTitles/Patterns) ──
418
+ const globalSubmittedTitles = new Set();
419
+ const globalSubmittedPatterns = new Set();
420
+
415
421
  /**
416
422
  * 执行单个维度: Analyst → Gate → Producer
417
423
  */
@@ -432,7 +438,7 @@ export async function fillDimensionsV3(fillContext) {
432
438
  restoredFromIncremental: true,
433
439
  };
434
440
  dimensionStats[dimId] = dimResult;
435
- logger.info(`[Bootstrap-v3] ⏩ "${dimId}" — incremental skip (historical result)`);
441
+ logger.info(`[Insight-v3] ⏩ "${dimId}" — incremental skip (historical result)`);
436
442
  return dimResult;
437
443
  }
438
444
 
@@ -472,7 +478,7 @@ export async function fillDimensionsV3(fillContext) {
472
478
  candidatesSummary: [],
473
479
  });
474
480
  logger.info(
475
- `[Bootstrap-v3] ✅ Checkpoint "${dimId}": analysisText restored (${cp.analysisText.length} chars) — Skill generation enabled`
481
+ `[Insight-v3] ✅ Checkpoint "${dimId}": analysisText restored (${cp.analysisText.length} chars) — Skill generation enabled`
476
482
  );
477
483
  }
478
484
 
@@ -521,81 +527,229 @@ export async function fillDimensionsV3(fillContext) {
521
527
 
522
528
  // Session 有效性检查
523
529
  if (taskManager && !taskManager.isSessionValid(sessionId)) {
524
- logger.warn(`[Bootstrap-v3] Session superseded — skipping "${dimId}"`);
530
+ logger.warn(`[Insight-v3] Session superseded — skipping "${dimId}"`);
525
531
  return { candidateCount: 0, error: 'session-superseded' };
526
532
  }
527
533
 
528
534
  emitter.emitDimensionStart(dimId);
529
- logger.info(`[Bootstrap-v3] ── Dimension "${dimId}" (${dimConfig.label}) ──`);
535
+ logger.info(`[Insight-v3] ── Dimension "${dimId}" (${dimConfig.label}) ──`);
530
536
 
531
537
  const dimStartTime = Date.now();
532
538
 
533
539
  try {
534
- // v5.0: 为每个维度创建独立的 ActiveContext 作用域
540
+ // ═══ v3.0: 增强 PipelineStrategy 驱动 ═══
535
541
  const analystScopeId = `${dimId}:analyst`;
536
542
  memoryCoordinator.createDimensionScope(analystScopeId);
537
543
 
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
- }),
544
+ const v3OutputType = DIMENSION_CONFIGS_V3[dimId]?.outputType;
545
+ const needsCandidates = v3OutputType
546
+ ? v3OutputType !== 'skill'
547
+ : !dimConfig.skillWorthy || dimConfig.dualOutput;
548
+
549
+ // ── 获取 Preset 的标准 stages 配置作为基础 ──
550
+ const presetStages = PRESETS.insight.strategy.stages;
551
+
552
+ // ── 构建 per-dimension 的 stages ──
553
+ // NOTE: onToolCall 不再注入 ac.recordToolCall — ToolExecutionPipeline 的
554
+ // traceRecord 中间件已通过 loopCtx.trace 统一记录,避免同一 AC 上双重记录。
555
+ const analyzeStage = {
556
+ ...presetStages[0],
557
+ };
558
+
559
+ let stages;
560
+ if (needsCandidates) {
561
+ // 候选维度: Analyze→QualityGate→Produce→RejectionGate
562
+ const produceStage = {
563
+ ...presetStages[2],
564
+ promptBuilder: (ctx) => {
565
+ memoryCoordinator.allocateBudget('producer');
566
+ return presetStages[2].promptBuilder(ctx);
567
+ },
568
+ };
569
+ stages = [
570
+ analyzeStage,
571
+ presetStages[1], // quality_gate
572
+ produceStage,
573
+ presetStages[3], // rejection_gate
574
+ ];
575
+ } else {
576
+ // Skill-only 维度: 仅 Analyze
577
+ stages = [analyzeStage];
578
+ }
579
+
580
+ // ── 创建 Runtime (使用增强 PipelineStrategy) ──
581
+ const runtime = agentFactory.createRuntime('insight', {
582
+ lang: primaryLang || projectInfo.lang || null,
583
+ strategy: {
584
+ type: 'pipeline',
585
+ maxRetries: 1,
586
+ stages,
587
+ },
588
+ });
589
+ runtime.setFileCache(allFiles);
590
+
591
+ // ── 构建消息 + strategyContext ──
592
+ const message = AgentMessage.internal(`Bootstrap dimension: ${dimConfig.label}`, {
593
+ sessionId,
594
+ dimension: dimId,
595
+ phase: 'bootstrap',
596
+ });
597
+
598
+ const strategyContext = {
599
+ dimConfig,
600
+ projectInfo,
601
+ dimContext,
602
+ sessionStore,
603
+ semanticMemory,
604
+ codeEntityGraph: codeEntityGraphInst,
605
+ projectGraph: null, // ProjectGraph 在 orchestrator 级别可用时注入
606
+ dimId,
607
+ activeContext: memoryCoordinator.getActiveContext(analystScopeId),
608
+ outputType: dimConfig.outputType || 'analysis',
609
+ // ── 引擎增强参数 (PipelineStrategy → reactLoop 透传) ──
610
+ contextWindow: agentFactory.createContextWindow({ isSystem: true }),
611
+ // B1 fix: 分析阶段使用 analyst 策略 (SCAN→EXPLORE→VERIFY→SUMMARIZE)
612
+ // 而非 bootstrap (EXPLORE→PRODUCE→SUMMARIZE),避免 PRODUCE nudge 浪费轮次
613
+ // B3 fix: 透传完整 ANALYST_BUDGET (searchBudget/maxSubmits/softSubmitLimit/idleRoundsToExit)
614
+ tracker: ExplorationTracker.resolve(
615
+ { source: 'system', strategy: 'analyst' },
616
+ { ...ANALYST_BUDGET },
617
+ ),
618
+ trace: memoryCoordinator.getActiveContext(analystScopeId),
619
+ memoryCoordinator,
620
+ sharedState: {
621
+ submittedTitles: globalSubmittedTitles,
622
+ submittedPatterns: globalSubmittedPatterns,
623
+ _dimensionMeta: {
624
+ id: dimId,
625
+ outputType: dimConfig.outputType || 'candidate',
626
+ allowedKnowledgeTypes: dimConfig.allowedKnowledgeTypes || [],
627
+ },
628
+ _projectLanguage: primaryLang || projectInfo.lang || null,
629
+ _dimensionScopeId: analystScopeId,
630
+ },
631
+ source: 'system',
632
+ };
633
+
634
+ // ── 执行 ──
635
+ // 外层超时 = 安全网 (各阶段已有独立超时: Analyst 300s + Producer 180s + 硬缓冲 60s)
636
+ const outerTimeoutMs = 600_000;
637
+ const runResult = await Promise.race([
638
+ runtime.execute(message, { strategyContext }),
550
639
  new Promise((_, reject) =>
551
- setTimeout(() => reject(new Error(`Analyst timeout for "${dimId}"`)), 300_000)
640
+ setTimeout(() => reject(new Error(`Bootstrap runtime timeout for "${dimId}"`)), outerTimeoutMs),
552
641
  ),
553
642
  ]);
554
643
 
555
- // v5.0: 通过 coordinator 蒸馏 ActiveContext → SessionStore
644
+ // ── 提取结果 ──
645
+ const analyzeResult = runResult?.phases?.analyze;
646
+ const gateResult = runResult?.phases?.quality_gate;
647
+ const produceResult = runResult?.phases?.produce;
648
+ const analysisText = (analyzeResult?.reply || runResult?.reply || '').trim();
649
+ const artifact = gateResult?.artifact || {
650
+ analysisText,
651
+ referencedFiles: [],
652
+ findings: [],
653
+ metadata: { toolCallCount: 0 },
654
+ };
655
+
656
+ const runtimeToolCalls = runResult?.toolCalls || [];
657
+ const combinedTokenUsage = runResult?.tokenUsage || { input: 0, output: 0 };
658
+
659
+ // 引用文件: 优先从 artifact 取, 回退从 toolCalls 提取
660
+ const referencedFiles = artifact.referencedFiles?.length > 0
661
+ ? artifact.referencedFiles
662
+ : [...new Set(runtimeToolCalls.flatMap((tc) => {
663
+ const a = tc?.args || tc?.params || {};
664
+ const files = [];
665
+ if (typeof a.filePath === 'string' && a.filePath.trim()) files.push(a.filePath.trim());
666
+ if (Array.isArray(a.filePaths)) {
667
+ for (const f of a.filePaths) {
668
+ if (typeof f === 'string' && f.trim()) files.push(f.trim());
669
+ }
670
+ }
671
+ return files;
672
+ }))];
673
+
674
+ const analysisReport = {
675
+ dimensionId: dimId,
676
+ analysisText: artifact.analysisText || analysisText,
677
+ findings: artifact.findings || [],
678
+ referencedFiles,
679
+ evidenceMap: artifact.evidenceMap || null,
680
+ negativeSignals: artifact.negativeSignals || [],
681
+ metadata: {
682
+ toolCallCount: runtimeToolCalls.length,
683
+ tokenUsage: combinedTokenUsage,
684
+ artifactVersion: artifact.metadata?.artifactVersion || 1,
685
+ },
686
+ };
687
+
688
+ // ── Producer 结果统计 ──
689
+ const submitCalls = runtimeToolCalls.filter(tc => {
690
+ const tool = tc?.tool || tc?.name;
691
+ return tool === 'submit_knowledge' || tool === 'submit_with_check';
692
+ });
693
+ const successCount = submitCalls.filter(tc => {
694
+ const res = tc?.result;
695
+ if (!res) return true;
696
+ if (typeof res === 'string') return !res.includes('rejected') && !res.includes('error');
697
+ return res.status !== 'rejected' && res.status !== 'error';
698
+ }).length;
699
+ const rejectedCount = submitCalls.length - successCount;
700
+
701
+ const producerResult = {
702
+ candidateCount: needsCandidates ? successCount : 0,
703
+ rejectedCount: needsCandidates ? rejectedCount : 0,
704
+ toolCalls: runtimeToolCalls,
705
+ reply: produceResult?.reply || analysisText,
706
+ tokenUsage: combinedTokenUsage,
707
+ };
708
+
709
+ candidateResults.created += producerResult.candidateCount;
710
+ dimensionCandidates[dimId] = { analysisReport, producerResult };
711
+
712
+ // ── Memory Update ──
556
713
  const ac = memoryCoordinator.getActiveContext(analystScopeId);
557
714
  const distilled = ac ? ac.distill() : { keyFindings: [], totalObservations: 0, toolCallSummary: [] };
558
715
  sessionStore.storeDimensionReport(dimId, {
559
716
  analysisText: analysisReport.analysisText,
560
- findings: analysisReport.findings || distilled.keyFindings,
717
+ findings: analysisReport.findings.length > 0 ? analysisReport.findings : distilled.keyFindings,
561
718
  referencedFiles: analysisReport.referencedFiles || [],
562
719
  candidatesSummary: [],
563
720
  workingMemoryDistilled: distilled,
564
721
  });
565
722
 
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
723
  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)`
724
+ `[Insight-v3] Dimension "${dimId}": analysis=${analysisReport.analysisText.length} chars, ` +
725
+ `files=${analysisReport.referencedFiles.length}, findings=${(analysisReport.findings || distilled.keyFindings).length}, ` +
726
+ `toolCalls=${runtimeToolCalls.length}, degraded=${runResult?.degraded || false} (${Date.now() - dimStartTime}ms)`,
581
727
  );
582
728
 
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
- };
729
+ // ── Token 用量持久化 (fire-and-forget) ──
730
+ try {
731
+ const tokenStore = ctx.container?.get?.('tokenUsageStore');
732
+ if (tokenStore) {
733
+ const aiProv = runtime.aiProvider;
734
+ tokenStore.record({
735
+ source: 'system',
736
+ dimension: dimId,
737
+ provider: aiProv?.name || null,
738
+ model: aiProv?.model || null,
739
+ inputTokens: combinedTokenUsage.input || 0,
740
+ outputTokens: combinedTokenUsage.output || 0,
741
+ durationMs: Date.now() - dimStartTime,
742
+ toolCalls: runtimeToolCalls.length,
743
+ sessionId: sessionId || null,
744
+ });
745
+ try {
746
+ const realtime = ctx.container?.get?.('realtimeService');
747
+ realtime?.broadcastTokenUsageUpdated?.();
748
+ } catch { /* optional */ }
749
+ }
750
+ } catch { /* token logging should never break execution */ }
596
751
 
597
- // v5.1: analysisText 过短(force-exit 时 AI 仅输出 digest 被清洗后仅剩 50-80 chars)
598
- // 但有足够的结构化发现时,从 findings 合成补充文本,避免 Producer 被 100 char 门控拦截
752
+ // ── v5.1: analysisText 过短补强 ──
599
753
  if (needsCandidates && analysisReport.analysisText.length < 100) {
600
754
  const findings = analysisReport.findings || [];
601
755
  if (findings.length >= 3) {
@@ -612,9 +766,7 @@ export async function fillDimensionsV3(fillContext) {
612
766
  return `${i + 1}. ${text}`;
613
767
  }),
614
768
  ];
615
- // 追加探索记录 (如果有)
616
- const dimReport = sessionStore.getDimensionReport(dimId);
617
- const memDistilled = dimReport?.workingMemoryDistilled;
769
+ const memDistilled = distilled;
618
770
  if (memDistilled?.toolCallSummary?.length > 0) {
619
771
  synthesized.push('', '### 探索记录', '');
620
772
  for (const s of memDistilled.toolCallSummary.slice(0, 10)) {
@@ -624,71 +776,13 @@ export async function fillDimensionsV3(fillContext) {
624
776
  const originalLen = analysisReport.analysisText.length;
625
777
  analysisReport.analysisText = synthesized.join('\n');
626
778
  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`
779
+ `[Insight-v3] analysisText 补强 "${dimId}": ${originalLen} → ${analysisReport.analysisText.length} chars ` +
780
+ `(from ${findings.length} findings)`,
660
781
  );
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
782
  }
689
783
  }
690
784
 
691
- // ── Phase 3: 记录 DimensionDigest ──
785
+ // ── DimensionDigest ──
692
786
  const digest = parseDimensionDigest(producerResult.reply) || {
693
787
  summary: `v3 分析: ${analysisReport.analysisText.substring(0, 200)}...`,
694
788
  candidateCount: producerResult.candidateCount,
@@ -697,21 +791,19 @@ export async function fillDimensionsV3(fillContext) {
697
791
  gaps: [],
698
792
  };
699
793
  dimContext.addDimensionDigest(dimId, digest);
700
-
701
- // v4.0: 同步 digest 到 SessionStore
702
794
  sessionStore.addDimensionDigest(dimId, digest);
703
795
 
704
- // 记录到 DimensionContext + EpisodicMemory
796
+ // 候选摘要记录到 DimensionContext + SessionStore
705
797
  for (const tc of producerResult.toolCalls || []) {
706
798
  const tool = tc.tool || tc.name;
707
799
  if (tool === 'submit_knowledge' || tool === 'submit_with_check') {
800
+ const args = tc.params || tc.args || {};
708
801
  const candidateSummary = {
709
- title: tc.params?.title || '',
710
- subTopic: tc.params?.category || '',
711
- summary: tc.params?.summary || '',
802
+ title: args.title || '',
803
+ subTopic: args.category || '',
804
+ summary: args.summary || '',
712
805
  };
713
806
  dimContext.addSubmittedCandidate(dimId, candidateSummary);
714
- // v4.0: 同步到 SessionStore
715
807
  sessionStore.addSubmittedCandidate(dimId, candidateSummary);
716
808
  }
717
809
  }
@@ -720,46 +812,41 @@ export async function fillDimensionsV3(fillContext) {
720
812
  type: needsCandidates ? 'candidate' : 'skill',
721
813
  extracted: producerResult.candidateCount,
722
814
  created: producerResult.candidateCount,
723
- // v5.1: 标记 Skill 待生成,Dashboard 可据此避免显示 "无匹配内容"
724
815
  skillPending: dimConfig.skillWorthy && producerResult.candidateCount === 0,
725
- status: 'v3-complete',
816
+ status: 'v3-pipeline-complete',
817
+ degraded: runResult?.degraded || false,
726
818
  durationMs: Date.now() - dimStartTime,
727
- toolCallCount:
728
- (analysisReport.metadata?.toolCallCount || 0) + (producerResult.toolCalls?.length || 0),
729
- source: 'internal-agent',
819
+ toolCallCount: runtimeToolCalls.length,
820
+ source: 'enhanced-pipeline-strategy',
730
821
  });
731
822
 
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
-
823
+ const dimTokenUsage = combinedTokenUsage;
740
824
  const dimResult = {
741
825
  candidateCount: producerResult.candidateCount,
742
826
  rejectedCount: producerResult.rejectedCount || 0,
743
827
  analysisChars: analysisReport.analysisText.length,
744
828
  referencedFiles: analysisReport.referencedFiles.length,
745
829
  durationMs: Date.now() - dimStartTime,
746
- toolCallCount:
747
- (analysisReport.metadata?.toolCallCount || 0) + (producerResult.toolCalls?.length || 0),
830
+ toolCallCount: runtimeToolCalls.length,
748
831
  tokenUsage: dimTokenUsage,
749
- // P3+: 保存 analysisText 供 checkpoint 恢复后 Skill 生成使用
750
832
  analysisText: analysisReport.analysisText,
751
833
  referencedFilesList: analysisReport.referencedFiles || [],
752
834
  };
753
835
 
754
- // P4.2: 记录维度统计
755
836
  dimensionStats[dimId] = dimResult;
756
837
 
757
- // P3: 保存 checkpoint
758
- await saveDimensionCheckpoint(projectRoot, sessionId, dimId, dimResult, digest);
838
+ // P3: 保存 checkpoint — 仅当有实质分析内容时(避免 degraded/空结果污染后续 run)
839
+ if (analysisReport.analysisText.length >= 50) {
840
+ await saveDimensionCheckpoint(projectRoot, sessionId, dimId, dimResult, digest);
841
+ } else {
842
+ logger.warn(
843
+ `[Insight-v3] ⚠ 跳过 checkpoint 保存: "${dimId}" analysisText 过短 (${analysisReport.analysisText.length} chars)`
844
+ );
845
+ }
759
846
 
760
847
  return dimResult;
761
848
  } catch (err) {
762
- logger.error(`[Bootstrap-v3] Dimension "${dimId}" failed: ${err.message}`);
849
+ logger.error(`[Insight-v3] Dimension "${dimId}" failed: ${err.message}`);
763
850
  candidateResults.errors.push({ dimId, error: err.message });
764
851
  emitter.emitDimensionComplete(dimId, { type: 'error', reason: err.message });
765
852
  return { candidateCount: 0, error: err.message };
@@ -789,7 +876,7 @@ export async function fillDimensionsV3(fillContext) {
789
876
  const tierStats = [...tierResults.values()];
790
877
  const totalCandidates = tierStats.reduce((s, r) => s + (r.candidateCount || 0), 0);
791
878
  logger.info(
792
- `[Bootstrap-v3] Tier ${tierIndex + 1} complete: ${tierResults.size} dimensions, ${totalCandidates} candidates`
879
+ `[Insight-v3] Tier ${tierIndex + 1} complete: ${tierResults.size} dimensions, ${totalCandidates} candidates`
793
880
  );
794
881
 
795
882
  // v4.0: Tier 级 Reflection — 综合本 Tier 所有维度的发现
@@ -797,29 +884,29 @@ export async function fillDimensionsV3(fillContext) {
797
884
  const reflection = buildTierReflection(tierIndex, tierResults, sessionStore);
798
885
  sessionStore.addTierReflection(tierIndex, reflection);
799
886
  logger.info(
800
- `[Bootstrap-v3] Tier ${tierIndex + 1} reflection: ` +
887
+ `[Insight-v3] Tier ${tierIndex + 1} reflection: ` +
801
888
  `${reflection.topFindings.length} top findings, ` +
802
889
  `${reflection.crossDimensionPatterns.length} patterns`
803
890
  );
804
891
  } catch (refErr) {
805
- logger.warn(`[Bootstrap-v3] Tier ${tierIndex + 1} reflection failed: ${refErr.message}`);
892
+ logger.warn(`[Insight-v3] Tier ${tierIndex + 1} reflection failed: ${refErr.message}`);
806
893
  }
807
894
  },
808
895
  });
809
896
 
810
897
  logger.info(
811
- `[Bootstrap-v3] All tiers complete: ${results.size} dimensions in ${Date.now() - t0}ms`
898
+ `[Insight-v3] All tiers complete: ${results.size} dimensions in ${Date.now() - t0}ms`
812
899
  );
813
900
  // v4.0: 记录 SessionStore 统计
814
901
  const emStats = sessionStore.getStats();
815
902
  logger.info(
816
- `[Bootstrap-v3] Memory stats: ${emStats.completedDimensions} dims, ` +
903
+ `[Insight-v3] Memory stats: ${emStats.completedDimensions} dims, ` +
817
904
  `${emStats.totalFindings} findings, ${emStats.referencedFiles} files, ` +
818
905
  `${emStats.crossReferences} cross-refs, ${emStats.tierReflections} reflections`
819
906
  );
820
907
  if (emStats.cacheStats) {
821
908
  logger.info(
822
- `[Bootstrap-v3] Cache stats: ${emStats.cacheStats.hitRate} hit rate, ` +
909
+ `[Insight-v3] Cache stats: ${emStats.cacheStats.hitRate} hit rate, ` +
823
910
  `${emStats.cacheStats.searchCacheSize} searches, ${emStats.cacheStats.fileCacheSize} files`
824
911
  );
825
912
  }
@@ -836,7 +923,7 @@ export async function fillDimensionsV3(fillContext) {
836
923
  await executeDimension(dimId);
837
924
  }
838
925
  }
839
- logger.info(`[Bootstrap-v3] Serial execution complete in ${Date.now() - t0}ms`);
926
+ logger.info(`[Insight-v3] Serial execution complete in ${Date.now() - t0}ms`);
840
927
  }
841
928
 
842
929
  // ═══════════════════════════════════════════════════════════
@@ -893,7 +980,7 @@ export async function fillDimensionsV3(fillContext) {
893
980
  }
894
981
  effectiveText = synthesized.join('\n');
895
982
  logger.info(
896
- `[Bootstrap-v3] Skill "${dim.id}": analysisText too short (${analysisText.trim().length} chars), ` +
983
+ `[Insight-v3] Skill "${dim.id}": analysisText too short (${analysisText.trim().length} chars), ` +
897
984
  `synthesized from ${keyFindings.length} findings → ${effectiveText.length} chars`
898
985
  );
899
986
  }
@@ -917,14 +1004,14 @@ export async function fillDimensionsV3(fillContext) {
917
1004
  emitter.emitDimensionFailed(dim.id, new Error(result.error));
918
1005
  }
919
1006
  } catch (err) {
920
- logger.warn(`[Bootstrap-v3] Skill generation failed for "${dim.id}": ${err.message}`);
1007
+ logger.warn(`[Insight-v3] Skill generation failed for "${dim.id}": ${err.message}`);
921
1008
  skillResults.failed++;
922
1009
  skillResults.errors.push({ dimId: dim.id, error: err.message });
923
1010
  emitter.emitDimensionFailed(dim.id, err);
924
1011
  }
925
1012
  }
926
1013
  } catch (e) {
927
- logger.warn(`[Bootstrap-v3] Skill generation module import failed: ${e.message}`);
1014
+ logger.warn(`[Insight-v3] Skill generation module import failed: ${e.message}`);
928
1015
  }
929
1016
 
930
1017
  // ═══════════════════════════════════════════════════════════
@@ -958,13 +1045,13 @@ export async function fillDimensionsV3(fillContext) {
958
1045
  if (allCandidates.length > 0) {
959
1046
  const relResult = ceg.populateFromCandidateRelations(allCandidates);
960
1047
  logger.info(
961
- `[Bootstrap-v3] Code Entity Graph relations: ${relResult.edgesCreated} edges from ${allCandidates.length} candidates (${relResult.durationMs}ms)`
1048
+ `[Insight-v3] Code Entity Graph relations: ${relResult.edgesCreated} edges from ${allCandidates.length} candidates (${relResult.durationMs}ms)`
962
1049
  );
963
1050
  }
964
1051
  }
965
1052
  } catch (cegErr) {
966
1053
  logger.warn(
967
- `[Bootstrap-v3] Code Entity Graph relations failed (non-blocking): ${cegErr.message}`
1054
+ `[Insight-v3] Code Entity Graph relations failed (non-blocking): ${cegErr.message}`
968
1055
  );
969
1056
  }
970
1057
 
@@ -978,7 +1065,7 @@ export async function fillDimensionsV3(fillContext) {
978
1065
  try {
979
1066
  const db = ctx.container.get('database');
980
1067
  if (db) {
981
- const semanticMemory = new ProjectSemanticMemory(db, { logger });
1068
+ const semanticMemory = new PersistentMemory(db, { logger });
982
1069
  const consolidator = new EpisodicConsolidator(semanticMemory, { logger });
983
1070
 
984
1071
  consolidationResult = consolidator.consolidate(sessionStore, {
@@ -988,18 +1075,18 @@ export async function fillDimensionsV3(fillContext) {
988
1075
 
989
1076
  const smStats = semanticMemory.getStats();
990
1077
  logger.info(
991
- `[Bootstrap-v3] Semantic Memory consolidation: ` +
1078
+ `[Insight-v3] Semantic Memory consolidation: ` +
992
1079
  `+${consolidationResult.total.added} ADD, ` +
993
1080
  `~${consolidationResult.total.updated} UPDATE, ` +
994
1081
  `⊕${consolidationResult.total.merged} MERGE | ` +
995
1082
  `Total: ${smStats.total} memories (avg importance: ${smStats.avgImportance})`
996
1083
  );
997
1084
  } else {
998
- logger.warn('[Bootstrap-v3] Database not available — skipping Semantic Memory consolidation');
1085
+ logger.warn('[Insight-v3] Database not available — skipping Semantic Memory consolidation');
999
1086
  }
1000
1087
  } catch (consolidateErr) {
1001
1088
  logger.warn(
1002
- `[Bootstrap-v3] Semantic Memory consolidation failed (non-blocking): ${consolidateErr.message}`
1089
+ `[Insight-v3] Semantic Memory consolidation failed (non-blocking): ${consolidateErr.message}`
1003
1090
  );
1004
1091
  }
1005
1092
 
@@ -1023,7 +1110,7 @@ export async function fillDimensionsV3(fillContext) {
1023
1110
 
1024
1111
  logger.info(
1025
1112
  [
1026
- `[Bootstrap-v3] ═══ Pipeline complete ═══`,
1113
+ `[Insight-v3] ═══ Pipeline complete ═══`,
1027
1114
  isIncremental
1028
1115
  ? ` Mode: INCREMENTAL (${incrementalPlan.affectedDimensions.length} affected, ${incrementalSkippedDims.length} skipped)`
1029
1116
  : '',
@@ -1136,9 +1223,9 @@ export async function fillDimensionsV3(fillContext) {
1136
1223
  path.join(reportDir, 'bootstrap-report.json'),
1137
1224
  JSON.stringify(report, null, 2)
1138
1225
  );
1139
- logger.info(`[Bootstrap-v3] 📊 Bootstrap report saved to .autosnippet/bootstrap-report.json`);
1226
+ logger.info(`[Insight-v3] 📊 Bootstrap report saved to .autosnippet/bootstrap-report.json`);
1140
1227
  } catch (reportErr) {
1141
- logger.warn(`[Bootstrap-v3] Bootstrap report generation failed: ${reportErr.message}`);
1228
+ logger.warn(`[Insight-v3] Bootstrap report generation failed: ${reportErr.message}`);
1142
1229
  }
1143
1230
 
1144
1231
  // P3: 成功完成后清理 checkpoints
@@ -1161,15 +1248,15 @@ export async function fillDimensionsV3(fillContext) {
1161
1248
  },
1162
1249
  plan: isIncremental ? incrementalPlan : null,
1163
1250
  });
1164
- logger.info(`[Bootstrap-v3] 📸 Snapshot saved: ${snapshotId}`);
1251
+ logger.info(`[Insight-v3] 📸 Snapshot saved: ${snapshotId}`);
1165
1252
  }
1166
1253
  } catch (snapErr) {
1167
- logger.warn(`[Bootstrap-v3] Snapshot save failed (non-blocking): ${snapErr.message}`);
1254
+ logger.warn(`[Insight-v3] Snapshot save failed (non-blocking): ${snapErr.message}`);
1168
1255
  }
1169
1256
 
1170
1257
  // 释放文件缓存
1171
1258
  allFiles = null;
1172
- chatAgent.setFileCache(null);
1259
+ ctx.container.singletons._fileCache = null;
1173
1260
 
1174
1261
  // ── Cursor Delivery: 生成 4 通道交付物料 ──
1175
1262
  try {
@@ -1179,7 +1266,7 @@ export async function fillDimensionsV3(fillContext) {
1179
1266
  const pipeline = container.get('cursorDeliveryPipeline');
1180
1267
  const deliveryResult = await pipeline.deliver();
1181
1268
  logger.info(
1182
- `[Bootstrap-v3] 🚀 Cursor Delivery complete — ` +
1269
+ `[Insight-v3] 🚀 Cursor Delivery complete — ` +
1183
1270
  `A: ${deliveryResult.channelA.rulesCount} rules, ` +
1184
1271
  `B: ${deliveryResult.channelB.topicCount} topics, ` +
1185
1272
  `C: ${deliveryResult.channelC.synced} skills, ` +
@@ -1188,7 +1275,7 @@ export async function fillDimensionsV3(fillContext) {
1188
1275
  );
1189
1276
  }
1190
1277
  } catch (deliveryErr) {
1191
- logger.warn(`[Bootstrap-v3] Cursor Delivery failed (non-blocking): ${deliveryErr.message}`);
1278
+ logger.warn(`[Insight-v3] Cursor Delivery failed (non-blocking): ${deliveryErr.message}`);
1192
1279
  }
1193
1280
 
1194
1281
  // ── Repo Wiki: 自动生成项目文档 Wiki ──
@@ -1265,7 +1352,7 @@ export async function fillDimensionsV3(fillContext) {
1265
1352
  const wikiResult = await wiki.generate();
1266
1353
  if (wikiResult.success) {
1267
1354
  logger.info(
1268
- `[Bootstrap-v3] 📖 Wiki generated — ${wikiResult.filesGenerated} files, ` +
1355
+ `[Insight-v3] 📖 Wiki generated — ${wikiResult.filesGenerated} files, ` +
1269
1356
  `AI: ${wikiResult.aiComposed || 0}, Synced: ${wikiResult.syncedDocs || 0}, ` +
1270
1357
  `Dedup removed: ${wikiResult.dedup?.removed?.length || 0}`
1271
1358
  );
@@ -1289,7 +1376,7 @@ export async function fillDimensionsV3(fillContext) {
1289
1376
  } catch { /* non-critical */ }
1290
1377
  }
1291
1378
  } catch (wikiErr) {
1292
- logger.warn(`[Bootstrap-v3] Wiki generation failed (non-blocking): ${wikiErr.message}`);
1379
+ logger.warn(`[Insight-v3] Wiki generation failed (non-blocking): ${wikiErr.message}`);
1293
1380
  try {
1294
1381
  const wikiRoute = await import('../../../../../http/routes/wiki.js');
1295
1382
  wikiRoute.patchWikiTask?.({