autosnippet 3.2.8 → 3.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/bin/cli.js +6 -5
  2. package/dashboard/dist/assets/index-BTAsOZv2.js +128 -0
  3. package/dashboard/dist/assets/index-C_72Ct98.css +1 -0
  4. package/dashboard/dist/index.html +2 -2
  5. package/lib/cli/AiScanService.js +23 -26
  6. package/lib/cli/SetupService.js +1 -1
  7. package/lib/cli/deploy/FileManifest.js +1 -1
  8. package/lib/core/AstAnalyzer.js +1 -1
  9. package/lib/core/discovery/index.js +2 -2
  10. package/lib/external/ai/AiProvider.js +66 -172
  11. package/lib/external/ai/providers/GoogleGeminiProvider.js +29 -5
  12. package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
  13. package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
  14. package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +1 -1
  15. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
  16. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
  17. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
  18. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +291 -204
  19. package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +7 -6
  20. package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
  21. package/lib/external/mcp/handlers/bootstrap-internal.js +2 -2
  22. package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
  23. package/lib/http/HttpServer.js +1 -1
  24. package/lib/http/middleware/requestLogger.js +1 -0
  25. package/lib/http/routes/ai.js +240 -35
  26. package/lib/http/routes/candidates.js +2 -3
  27. package/lib/http/routes/extract.js +13 -11
  28. package/lib/http/routes/modules.js +2 -2
  29. package/lib/http/routes/recipes.js +5 -5
  30. package/lib/http/routes/remote.js +134 -255
  31. package/lib/http/routes/violations.js +0 -54
  32. package/lib/http/utils/sse-sessions.js +1 -1
  33. package/lib/infrastructure/logging/Logger.js +5 -4
  34. package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
  35. package/lib/injection/ServiceContainer.js +64 -17
  36. package/lib/platform/ScreenCaptureService.js +177 -0
  37. package/lib/platform/ios/routes/spm.js +2 -2
  38. package/lib/service/agent/AgentEventBus.js +207 -0
  39. package/lib/service/agent/AgentFactory.js +535 -0
  40. package/lib/service/agent/AgentMessage.js +240 -0
  41. package/lib/service/agent/AgentRouter.js +228 -0
  42. package/lib/service/agent/AgentRuntime.js +1056 -0
  43. package/lib/service/agent/AgentState.js +217 -0
  44. package/lib/service/agent/IntentClassifier.js +331 -0
  45. package/lib/service/agent/LarkTransport.js +389 -0
  46. package/lib/service/agent/capabilities.js +409 -0
  47. package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
  48. package/lib/service/{chat → agent/context}/ExplorationTracker.js +112 -33
  49. package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +5 -3
  50. package/lib/service/agent/core/LoopContext.js +170 -0
  51. package/lib/service/agent/core/MessageAdapter.js +223 -0
  52. package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
  53. package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +15 -98
  54. package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
  55. package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
  56. package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
  57. package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +85 -135
  58. package/lib/service/agent/domain/insight-producer.js +270 -0
  59. package/lib/service/agent/domain/scan-prompts.js +444 -0
  60. package/lib/service/agent/forced-summary.js +266 -0
  61. package/lib/service/agent/index.js +91 -0
  62. package/lib/service/{chat → agent}/memory/ActiveContext.js +29 -1
  63. package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
  64. package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
  65. package/lib/service/{chat → agent}/memory/SessionStore.js +1 -1
  66. package/lib/service/{chat → agent}/memory/index.js +1 -1
  67. package/lib/service/agent/policies.js +442 -0
  68. package/lib/service/agent/presets.js +305 -0
  69. package/lib/service/agent/strategies.js +756 -0
  70. package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
  71. package/lib/service/agent/tools/ai-analysis.js +75 -0
  72. package/lib/service/{chat → agent}/tools/composite.js +2 -1
  73. package/lib/service/{chat → agent}/tools/guard.js +1 -121
  74. package/lib/service/{chat → agent}/tools/index.js +27 -21
  75. package/lib/service/{chat → agent}/tools/infrastructure.js +1 -1
  76. package/lib/service/agent/tools/knowledge-graph.js +112 -0
  77. package/lib/service/agent/tools/scan-recipe.js +189 -0
  78. package/lib/service/agent/tools/system-interaction.js +476 -0
  79. package/lib/service/automation/DirectiveDetector.js +0 -1
  80. package/lib/service/automation/FileWatcher.js +0 -8
  81. package/lib/service/automation/handlers/CreateHandler.js +7 -3
  82. package/lib/service/automation/handlers/DraftHandler.js +7 -6
  83. package/lib/service/module/ModuleService.js +40 -73
  84. package/lib/service/skills/SignalCollector.js +26 -19
  85. package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
  86. package/lib/shared/FieldSpec.js +1 -1
  87. package/lib/shared/StyleGuide.js +1 -1
  88. package/package.json +4 -1
  89. package/resources/native-ui/screenshot.swift +228 -0
  90. package/dashboard/dist/assets/index-D5jiDBQG.css +0 -1
  91. package/dashboard/dist/assets/index-e5OKj-Ni.js +0 -128
  92. package/lib/core/discovery/SpmDiscoverer.js +0 -5
  93. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -750
  94. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
  95. package/lib/http/routes/spm.js +0 -5
  96. package/lib/infrastructure/external/XcodeAutomation.js +0 -15
  97. package/lib/service/chat/ChatAgent.js +0 -1602
  98. package/lib/service/chat/Memory.js +0 -161
  99. package/lib/service/chat/ProducerAgent.js +0 -431
  100. package/lib/service/chat/ReasoningTrace.js +0 -523
  101. package/lib/service/chat/TaskPipeline.js +0 -357
  102. package/lib/service/chat/WorkingMemory.js +0 -359
  103. package/lib/service/chat/memory/PersistentMemory.js +0 -450
  104. package/lib/service/chat/tools/ai-analysis.js +0 -267
  105. package/lib/service/chat/tools/knowledge-graph.js +0 -234
  106. package/lib/service/chat/tools.js +0 -18
  107. package/lib/service/snippet/PlaceholderConverter.js +0 -5
  108. package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
  109. /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
  110. /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
  111. /package/lib/service/{chat → agent}/tools/ast-graph.js +0 -0
  112. /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
  113. /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
  114. /package/lib/service/{chat → agent}/tools/query.js +0 -0
@@ -0,0 +1,266 @@
1
+ /**
2
+ * forced-summary.js — 强制退出后的摘要生成
3
+ *
4
+ * 强制退出后的摘要生成独立模块,
5
+ * 供 AgentRuntime.reactLoop() 在循环退出后调用。
6
+ *
7
+ * 支持两种模式:
8
+ * - system: 输出 dimensionDigest JSON (供 Bootstrap 管线消费)
9
+ * - user: 输出人类可读的 Markdown 结构化总结 (前端 AI Chat 展示)
10
+ *
11
+ * @module forced-summary
12
+ */
13
+
14
+ import Logger from '../../infrastructure/logging/Logger.js';
15
+ import { cleanFinalAnswer } from './core/ChatAgentPrompts.js';
16
+
17
+ const logger = Logger.getInstance();
18
+
19
+ /**
20
+ * 生成强制摘要
21
+ *
22
+ * @param {Object} opts
23
+ * @param {import('../../external/ai/AiProvider.js').AiProvider} opts.aiProvider — LLM 提供商
24
+ * @param {string} [opts.source] — 'user' | 'system'
25
+ * @param {Array} opts.toolCalls — 工具调用记录
26
+ * @param {Object} [opts.tracker] — ExplorationTracker 实例
27
+ * @param {Object} [opts.contextWindow] — ContextWindow 实例 (用于避免超出 token)
28
+ * @param {string} opts.prompt — 原始用户 prompt
29
+ * @param {Object} [opts.tokenUsage] — token 用量 (会被修改)
30
+ * @returns {Promise<{ reply: string, tokenUsage: { input: number, output: number } }>}
31
+ */
32
+ export async function produceForcedSummary({
33
+ aiProvider,
34
+ source,
35
+ toolCalls = [],
36
+ tracker,
37
+ contextWindow,
38
+ prompt,
39
+ tokenUsage,
40
+ }) {
41
+ const isSystem = source === 'system';
42
+ const iterations = tracker?.iteration || 0;
43
+ const resultTokenUsage = { input: 0, output: 0 };
44
+
45
+ logger.info(
46
+ `[ForcedSummary] ⚠ producing forced summary (${iterations} iters, ${toolCalls.length} calls, source=${source})`
47
+ );
48
+
49
+ const candidateCount = toolCalls.filter(
50
+ tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check'
51
+ ).length;
52
+
53
+ let finalReply;
54
+
55
+ // 如果熔断器已打开,跳过 AI 调用直接合成摘要
56
+ const isCircuitOpen = aiProvider._circuitState === 'OPEN';
57
+ if (isCircuitOpen) {
58
+ logger.warn(
59
+ `[ForcedSummary] circuit breaker is OPEN — skipping AI summary, using synthetic ${isSystem ? 'digest' : 'summary'}`
60
+ );
61
+ }
62
+
63
+ // 收集工具调用摘要
64
+ const submitSummary = toolCalls
65
+ .filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
66
+ .map((tc, i) => `${i + 1}. ${tc.args?.title || tc.args?.category || tc.params?.title || tc.params?.category || 'untitled'}`)
67
+ .join('\n');
68
+
69
+ try {
70
+ if (isCircuitOpen) {
71
+ throw new Error('circuit open — skip to synthetic summary');
72
+ }
73
+
74
+ let summaryPrompt;
75
+ let systemPrompt;
76
+
77
+ if (isSystem) {
78
+ // system 源: dimensionDigest JSON
79
+ summaryPrompt = `你已完成 ${iterations} 轮工具调用(共 ${toolCalls.length} 次),提交了 ${candidateCount} 个候选。
80
+ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
81
+ **必须**输出 dimensionDigest JSON(用 \`\`\`json 包裹):
82
+ \`\`\`json
83
+ {
84
+ "dimensionDigest": {
85
+ "summary": "本维度分析总结",
86
+ "candidateCount": ${candidateCount},
87
+ "keyFindings": ["发现1", "发现2"],
88
+ "crossRefs": {},
89
+ "gaps": ["未覆盖方面"],
90
+ "remainingTasks": [
91
+ { "signal": "未处理信号名", "reason": "达到提交上限/时间限制", "priority": "high", "searchHints": ["搜索词"] }
92
+ ]
93
+ }
94
+ }
95
+ \`\`\`
96
+ > remainingTasks: 列出本次未来得及处理的信号/主题。已全部覆盖则留空 \`[]\`。`;
97
+ systemPrompt = '直接输出 dimensionDigest JSON 总结,不要调用工具。';
98
+ } else {
99
+ // user 源: Markdown 结构化总结
100
+ const userQuestion = prompt ? `用户的原始问题:「${prompt.slice(0, 500)}」\n\n` : '';
101
+ const toolContextSummary = buildToolContextForUserSummary(toolCalls);
102
+ summaryPrompt = `${userQuestion}你刚才通过 ${toolCalls.length} 次工具调用分析了项目代码。以下是你调用过的工具和获取到的关键信息:
103
+
104
+ ${toolContextSummary}
105
+
106
+ 请基于以上收集到的信息,用**清晰易读的 Markdown** 格式撰写分析总结,直接回答用户的问题。
107
+
108
+ 要求:
109
+ - 使用二级/三级标题组织内容
110
+ - 要有具体的代码文件路径、类名、模式名称等细节
111
+ - 关键发现用列表项罗列
112
+ - 如果发现了架构模式或最佳实践,用简短代码块举例
113
+ - 语言自然流畅,像一份技术分析报告`;
114
+ systemPrompt = '你是项目分析助手。请用纯 Markdown 格式输出结构清晰的分析总结,只输出人类可读的自然语言文档,不要输出 JSON 格式的数据。';
115
+ }
116
+
117
+ // 用空 messages 避免累积上下文导致 400
118
+ const summaryResult = await aiProvider.chatWithTools(summaryPrompt, {
119
+ messages: [],
120
+ toolChoice: 'none',
121
+ systemPrompt,
122
+ temperature: isSystem ? 0.3 : 0.5,
123
+ maxTokens: 8192,
124
+ });
125
+
126
+ if (summaryResult.usage) {
127
+ resultTokenUsage.input += summaryResult.usage.inputTokens || 0;
128
+ resultTokenUsage.output += summaryResult.usage.outputTokens || 0;
129
+ }
130
+ // system 源: dimensionDigest JSON 是预期输出,不能被 cleanFinalAnswer 剥掉
131
+ finalReply = isSystem
132
+ ? (summaryResult.text || '').trim()
133
+ : cleanFinalAnswer(summaryResult.text || '');
134
+ } catch (err) {
135
+ logger.warn(`[ForcedSummary] AI call failed: ${err.message}`);
136
+
137
+ if (isSystem) {
138
+ // system 源兜底: 合成 dimensionDigest JSON
139
+ const titles = toolCalls
140
+ .filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
141
+ .map(tc => tc.args?.title || tc.params?.title || 'untitled');
142
+ finalReply = `\`\`\`json
143
+ {
144
+ "dimensionDigest": {
145
+ "summary": "通过 ${toolCalls.length} 次工具调用分析了项目代码,提交了 ${candidateCount} 个候选。",
146
+ "candidateCount": ${candidateCount},
147
+ "keyFindings": ${JSON.stringify(titles.slice(0, 5))},
148
+ "crossRefs": {},
149
+ "gaps": ["AI 服务异常,部分分析未完成"]
150
+ }
151
+ }
152
+ \`\`\``;
153
+ } else {
154
+ // user 源兜底: 合成 Markdown 摘要
155
+ const toolNames = [...new Set(toolCalls.map(tc => tc.tool))];
156
+ const filesRead = toolCalls
157
+ .filter(tc => tc.tool === 'read_project_file')
158
+ .flatMap(tc => {
159
+ const p = tc.args || tc.params || {};
160
+ if (p.filePaths) return p.filePaths;
161
+ if (p.filePath) return [p.filePath];
162
+ return [];
163
+ })
164
+ .slice(0, 10);
165
+ const searches = toolCalls
166
+ .filter(tc => tc.tool === 'search_project_code' || tc.tool === 'semantic_search_code')
167
+ .map(tc => {
168
+ const p = tc.args || tc.params || {};
169
+ return p.patterns?.[0] || p.query || p.pattern;
170
+ })
171
+ .filter(Boolean)
172
+ .slice(0, 5);
173
+
174
+ finalReply = `## 分析总结\n\n通过 **${toolCalls.length} 次工具调用**探索了项目代码。\n\n`;
175
+ if (searches.length > 0) {
176
+ finalReply += `### 搜索的关键词\n${searches.map(s => `- \`${s}\``).join('\n')}\n\n`;
177
+ }
178
+ if (filesRead.length > 0) {
179
+ finalReply += `### 读取的文件\n${filesRead.map(f => `- \`${f}\``).join('\n')}\n\n`;
180
+ }
181
+ finalReply += `### 使用的工具\n${toolNames.map(t => `- ${t}`).join('\n')}\n\n`;
182
+ finalReply += '> ⚠️ AI 服务异常,未能生成完整分析。请稍后重试或缩小分析范围。';
183
+ }
184
+ }
185
+
186
+ logger.info(`[ForcedSummary] ✅ forced summary — ${finalReply.length} chars`);
187
+ return { reply: finalReply, tokenUsage: resultTokenUsage };
188
+ }
189
+
190
+ /**
191
+ * 从工具调用记录中提取上下文摘要 (供 user 源强制总结使用)
192
+ * @param {Array} toolCalls
193
+ * @returns {string}
194
+ */
195
+ function buildToolContextForUserSummary(toolCalls) {
196
+ const sections = [];
197
+
198
+ // 目录结构探索
199
+ const structureCalls = toolCalls.filter(tc => tc.tool === 'list_project_structure');
200
+ if (structureCalls.length > 0) {
201
+ const dirs = structureCalls.map(tc => (tc.args || tc.params)?.directory || '/').slice(0, 5);
202
+ sections.push(`**目录探索**: ${dirs.map(d => `\`${d}\``).join(', ')}`);
203
+ }
204
+
205
+ // 项目概况
206
+ const overviewCalls = toolCalls.filter(tc => tc.tool === 'get_project_overview');
207
+ if (overviewCalls.length > 0) {
208
+ sections.push('**项目概况**: 已获取');
209
+ }
210
+
211
+ // 代码搜索
212
+ const searchCalls = toolCalls.filter(
213
+ tc => tc.tool === 'search_project_code' || tc.tool === 'semantic_search_code'
214
+ );
215
+ if (searchCalls.length > 0) {
216
+ const queries = searchCalls
217
+ .map(tc => {
218
+ const p = tc.args || tc.params || {};
219
+ return p.patterns?.[0] || p.query || p.pattern;
220
+ })
221
+ .filter(Boolean)
222
+ .slice(0, 8);
223
+ sections.push(`**代码搜索** (${searchCalls.length} 次): ${queries.map(q => `\`${q}\``).join(', ')}`);
224
+ }
225
+
226
+ // 文件读取
227
+ const readCalls = toolCalls.filter(tc => tc.tool === 'read_project_file');
228
+ if (readCalls.length > 0) {
229
+ const files = readCalls
230
+ .flatMap(tc => {
231
+ const p = tc.args || tc.params || {};
232
+ if (p.filePaths) return p.filePaths;
233
+ if (p.filePath) return [p.filePath];
234
+ return [];
235
+ })
236
+ .slice(0, 10);
237
+ sections.push(`**文件读取** (${readCalls.length} 次): ${files.map(f => `\`${f}\``).join(', ')}`);
238
+ }
239
+
240
+ // AST 分析
241
+ const astCalls = toolCalls.filter(tc =>
242
+ ['get_class_hierarchy', 'get_class_info', 'get_protocol_info', 'get_method_overrides', 'get_category_map'].includes(tc.tool)
243
+ );
244
+ if (astCalls.length > 0) {
245
+ const entities = astCalls
246
+ .map(tc => {
247
+ const p = tc.args || tc.params || {};
248
+ return p.className || p.name || p.protocolName || p.rootClass;
249
+ })
250
+ .filter(Boolean)
251
+ .slice(0, 5);
252
+ sections.push(`**AST 结构分析** (${astCalls.length} 次): ${entities.map(e => `\`${e}\``).join(', ')}`);
253
+ }
254
+
255
+ // 知识库搜索
256
+ const kbCalls = toolCalls.filter(tc =>
257
+ ['search_knowledge', 'search_recipes', 'knowledge_overview'].includes(tc.tool)
258
+ );
259
+ if (kbCalls.length > 0) {
260
+ sections.push(`**知识库查询**: ${kbCalls.length} 次`);
261
+ }
262
+
263
+ return sections.length > 0 ? sections.join('\n') : '(工具调用记录为空)';
264
+ }
265
+
266
+ export default produceForcedSummary;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * AutoSnippet Agent 模块 — 统一出口
3
+ *
4
+ * @module service/agent
5
+ *
6
+ * 统一架构: ONE Runtime, 多种配置
7
+ *
8
+ * ┌──────── Transport ────────┐
9
+ * │ HTTP│Lark│CLI│MCP│... │ ← 渠道适配 (AgentMessage)
10
+ * └──────────┬────────────────┘
11
+ * │
12
+ * ┌──────────▼────────────────┐
13
+ * │ AgentRouter │ ← 意图 → Preset
14
+ * └──────────┬────────────────┘
15
+ * │
16
+ * ┌──────────▼────────────────┐
17
+ * │ AgentFactory │ ← Preset + DI → Runtime
18
+ * └──────────┬────────────────┘
19
+ * │
20
+ * ┌──────────▼────────────────────────────────────────┐
21
+ * │ AgentRuntime │
22
+ * │ │
23
+ * │ ┌──────────┐ ┌───────────┐ ┌────────────────┐ │
24
+ * │ │Capability│ │ Strategy │ │ Policy │ │
25
+ * │ │ 技能模块 │ │ 执行策略 │ │ 约束引擎 │ │
26
+ * │ ├──────────┤ ├───────────┤ ├────────────────┤ │
27
+ * │ │• 对话 │ │• Single │ │• Budget 预算 │ │
28
+ * │ │• 代码分析│ │• Pipeline │ │• Safety 安全 │ │
29
+ * │ │• 知识生产│ │• FanOut │ │• Quality 质量 │ │
30
+ * │ │• 系统交互│ │• Adaptive │ │ │ │
31
+ * │ └──────────┘ └───────────┘ └────────────────┘ │
32
+ * │ │
33
+ * │ ┌─────────────────────────────────────────┐ │
34
+ * │ │ ReAct Loop (Thought→Action→Observe) │ │
35
+ * │ └─────────────────────────────────────────┘ │
36
+ * └───────────────────────────────────────────────────┘
37
+ * │
38
+ * ┌──────────▼────────────────┐
39
+ * │ AgentState + EventBus │ ← 状态机 + 事件通信
40
+ * └──────────────────────────-┘
41
+ *
42
+ * Preset 配置表:
43
+ * | Preset | Capabilities | Strategy | Policies |
44
+ * |--------------|----------------------|-------------|------------------|
45
+ * | chat | Conv + Analysis | Single | Budget(8轮) |
46
+ * | bootstrap | Analysis + Knowledge | FanOut+Pipe | Budget+Quality |
47
+ * | scan | Analysis + Knowledge | Pipeline | Budget+Quality |
48
+ * | remote-exec | Conv+Analysis+System | Single | Budget+Safety |
49
+ */
50
+
51
+ // ── Infrastructure ──
52
+ export { AgentState, AgentPhase } from './AgentState.js';
53
+ export { AgentEventBus, AgentEvents } from './AgentEventBus.js';
54
+ export { AgentMessage, Channel } from './AgentMessage.js';
55
+
56
+ // ── Core ──
57
+ export { AgentRuntime } from './AgentRuntime.js';
58
+ export { AgentRouter, PresetName } from './AgentRouter.js';
59
+ export { AgentFactory } from './AgentFactory.js';
60
+
61
+ // ── Capabilities ──
62
+ export {
63
+ Capability,
64
+ Conversation,
65
+ CodeAnalysis,
66
+ KnowledgeProduction,
67
+ SystemInteraction,
68
+ CapabilityRegistry,
69
+ } from './capabilities.js';
70
+
71
+ // ── Strategies ──
72
+ export {
73
+ Strategy,
74
+ SingleStrategy,
75
+ PipelineStrategy,
76
+ FanOutStrategy,
77
+ AdaptiveStrategy,
78
+ StrategyRegistry,
79
+ } from './strategies.js';
80
+
81
+ // ── Policies ──
82
+ export {
83
+ Policy,
84
+ BudgetPolicy,
85
+ SafetyPolicy,
86
+ QualityGatePolicy,
87
+ PolicyEngine,
88
+ } from './policies.js';
89
+
90
+ // ── Presets ──
91
+ export { PRESETS, getPreset, resolveStrategy } from './presets.js';
@@ -148,6 +148,8 @@ export class ActiveContext {
148
148
  #plan = null;
149
149
  /** @type {Array<Plan>} */
150
150
  #planHistory = [];
151
+ /** @type {boolean} 是否期待下一次响应包含计划 (由 ExplorationTracker 设置) */
152
+ #expectingPlan = false;
151
153
 
152
154
  // ── 配置 ──
153
155
  /** @type {number} 保留最近 N 轮原始观察 */
@@ -342,6 +344,11 @@ export class ActiveContext {
342
344
 
343
345
  /**
344
346
  * 从 AI 响应文本中提取计划,自动调用 setPlan/updatePlan
347
+ *
348
+ * 防御措施: 已存在计划时,仅在 #expectingPlan 为 true 时才覆盖。
349
+ * 这防止 reflection 回复中的编号列表(非计划的回应文本)污染已有计划。
350
+ * ExplorationTracker 在发送 plan elicitation / replan 时调用 expectPlan() 授权更新。
351
+ *
345
352
  * @param {string} text — AI 完整响应文本
346
353
  * @param {number} iteration — 当前轮次
347
354
  * @returns {boolean} — 是否成功提取到计划
@@ -350,6 +357,11 @@ export class ActiveContext {
350
357
  const planText = this.#extractPlanFromText(text);
351
358
  if (!planText) return false;
352
359
 
360
+ // Guard: 已有计划时,仅在 expectPlan 授权下才覆盖
361
+ // 防止 reflection/convergence 回复中的编号列表被误捕获为 plan
362
+ if (this.#plan && !this.#expectingPlan) return false;
363
+
364
+ this.#expectingPlan = false;
353
365
  if (this.#plan) {
354
366
  this.#updatePlan(planText, iteration);
355
367
  } else {
@@ -358,6 +370,14 @@ export class ActiveContext {
358
370
  return true;
359
371
  }
360
372
 
373
+ /**
374
+ * 标记「下一次响应可能包含计划」— 授权 extractAndSetPlan 覆盖已有计划
375
+ * 由 ExplorationTracker 在发送 plan elicitation / replan nudge 时调用。
376
+ */
377
+ expectPlan() {
378
+ this.#expectingPlan = true;
379
+ }
380
+
361
381
  /**
362
382
  * 直接设置计划 (公开接口,供 ExplorationTracker 和测试使用)
363
383
  * @param {string} planText
@@ -905,7 +925,15 @@ export class ActiveContext {
905
925
  }
906
926
  }
907
927
 
908
- return planLines.length >= 2 ? planLines.join('\n').trim() : null;
928
+ if (planLines.length < 2) return null;
929
+
930
+ // 防御: 拒绝 "大部分是疑问句" 的编号列表
931
+ // reflection nudge 的 "请评估: 1. ...是什么? 2. ...?" 会被 LLM 回显,
932
+ // 不是真正的探索计划,不能捕获为 plan steps
933
+ const questionCount = planLines.filter(l => /[??]\s*$/.test(l.trim())).length;
934
+ if (questionCount > planLines.length * 0.5) return null;
935
+
936
+ return planLines.join('\n').trim();
909
937
  }
910
938
  }
911
939
 
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * 生命周期:
11
11
  * - Bootstrap 模式: 会话级 (orchestrator 创建, 贯穿所有维度)
12
- * - User Chat 模式: 实例级 (ChatAgent 构造函数创建)
12
+ * - User Chat 模式: 实例级 (AgentRuntime 创建)
13
13
  *
14
14
  * @module MemoryCoordinator
15
15
  */
@@ -53,7 +53,7 @@ const NON_CACHEABLE_TOOLS = new Set([
53
53
  'get_previous_evidence',
54
54
  ]);
55
55
 
56
- // ── 写入路由: 规则匹配模式 (from ChatAgent.#extractMemory) ──
56
+ // ── 写入路由: 规则匹配模式 ──
57
57
 
58
58
  const PREFERENCE_PATTERNS = [
59
59
  /我们(项目|团队)?(不用|不使用|禁止|避免|偏好|习惯|规范是)/,
@@ -81,7 +81,7 @@ export class MemoryCoordinator {
81
81
  #budgetAllocation;
82
82
 
83
83
  // ── Tier 3: Persistent (跨会话) ──
84
- #persistentMemory; // PersistentMemory (extends ProjectSemanticMemory)
84
+ #persistentMemory; // PersistentMemory
85
85
  #conversationLog; // ConversationStore
86
86
 
87
87
  // ── Tier 2: Session (会话级) ──
@@ -96,7 +96,7 @@ export class MemoryCoordinator {
96
96
 
97
97
  /**
98
98
  * @param {object} config
99
- * @param {object} [config.persistentMemory] — PersistentMemory / ProjectSemanticMemory 实例
99
+ * @param {object} [config.persistentMemory] — PersistentMemory 实例
100
100
  * @param {object} [config.sessionStore] — SessionStore 实例 (bootstrap 模式)
101
101
  * @param {object} [config.conversationLog] — ConversationStore 实例
102
102
  * @param {'user'|'bootstrap'} [config.mode='bootstrap']
@@ -125,7 +125,7 @@ export class MemoryCoordinator {
125
125
  // ═══════════════════════════════════════════════════════════
126
126
 
127
127
  /**
128
- * 配置总预算 (由 ChatAgent.execute 入口调用)
128
+ * 配置总预算 (由 AgentRuntime.execute 入口调用)
129
129
  * @param {object} options
130
130
  * @param {number} options.totalContextBudget — 模型总上下文 token 数
131
131
  * @param {string} [options.model]
@@ -327,7 +327,7 @@ export class MemoryCoordinator {
327
327
  }
328
328
 
329
329
  /**
330
- * 从对话中提取记忆 (迁移自 ChatAgent.#extractMemory)
330
+ * 从对话中提取记忆
331
331
  *
332
332
  * 写入路由 (WriteRouter):
333
333
  * - 规则 1: 只在 user 源触发规则匹配 (B4 fix)
@@ -520,7 +520,7 @@ export class MemoryCoordinator {
520
520
  }
521
521
 
522
522
  /**
523
- * 获取 PersistentMemory / ProjectSemanticMemory
523
+ * 获取 PersistentMemory
524
524
  * @returns {object|null}
525
525
  */
526
526
  getPersistentMemory() {