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
@@ -1,450 +0,0 @@
1
- /**
2
- * PersistentMemory — 持久化语义记忆 (Tier 3)
3
- *
4
- * 前身: ProjectSemanticMemory.js (继承 + 增强)
5
- * 合并目标: 统一 Memory.js (JSONL) + ProjectSemanticMemory (SQLite)
6
- *
7
- * 新增功能:
8
- * 1. migrateFromLegacy() — Memory.js JSONL 数据迁移到 SQLite
9
- * 2. 冲突解决 (Mem0 风格) — consolidate 前置矛盾检测 + 自动替换
10
- * 3. 向量嵌入占位接口 — setEmbeddingFunction() 预留,未来替换 Jaccard
11
- * 4. 预算感知 toPromptSection — 支持 tokenBudget 参数 (配合 MemoryCoordinator)
12
- *
13
- * 继承自 ProjectSemanticMemory:
14
- * - add / update / delete / get (基本 CRUD)
15
- * - consolidate (增强: 冲突预解决 → super) (Extract-Update pipeline)
16
- * - retrieve (3D 检索: recency × importance × relevance)
17
- * - search (文本搜索)
18
- * - toPromptSection / load / append / size (Memory.js 兼容层)
19
- * - compact / clearBootstrapMemories (维护 — F15, F16)
20
- * - getStats (统计)
21
- *
22
- * 算法参数 (F20, 继承自 ProjectSemanticMemory):
23
- * - 检索权重: RECENCY=0.2, IMPORTANCE=0.3, RELEVANCE=0.5 (Generative Agents 三维打分)
24
- * - 半衰期: RECENCY_HALF_LIFE_DAYS=7
25
- * - 固化阈值: SIMILARITY_UPDATE=0.85 (同义→UPDATE), SIMILARITY_MERGE=0.6 (相关→MERGE)
26
- * - 遗忘策略: ARCHIVE_DAYS=30 (降级), FORGET_DAYS=90 (删除), MAX_MEMORIES=500
27
- *
28
- * @module PersistentMemory
29
- * @see docs/copilot/memory-system-redesign.md §4.5, §7.6, F15, F16, F20
30
- */
31
-
32
- import fs from 'node:fs';
33
- import path from 'node:path';
34
- import { ProjectSemanticMemory } from '../ProjectSemanticMemory.js';
35
-
36
- // ──────────────────────────────────────────────────────────────
37
- // 矛盾检测模式 (Mem0 风格冲突解决)
38
- // ──────────────────────────────────────────────────────────────
39
-
40
- /** 中文否定/禁止模式 */
41
- const NEGATION_PATTERNS_ZH =
42
- /不(再)?使用|不(再)?用|禁止|废弃|移除|取消|停止|不要|不采用|弃用|淘汰/;
43
-
44
- /** 英文否定/禁止模式 */
45
- const NEGATION_PATTERNS_EN =
46
- /\b(don'?t|do\s+not|never|no\s+longer|removed?|deprecated?|stop|avoid|disable|abandon|drop)\b/i;
47
-
48
- /** 共享词语最少匹配数 — 用于判断两条记忆是否讨论同一主题 */
49
- const MIN_TOPIC_OVERLAP_WORDS = 2;
50
-
51
- /** 共享词语比例阈值 — 低于此值视为不同主题 */
52
- const MIN_TOPIC_OVERLAP_RATIO = 0.3;
53
-
54
- // ──────────────────────────────────────────────────────────────
55
- // PersistentMemory
56
- // ──────────────────────────────────────────────────────────────
57
-
58
- export class PersistentMemory extends ProjectSemanticMemory {
59
- /**
60
- * 向量嵌入函数 (预留接口)
61
- *
62
- * 签名: (queryText: string, contentText: string) => number (0.0-1.0)
63
- * 当前: null → 使用继承的 Jaccard + 子串匹配
64
- * 未来: ADR-3 — 当嵌入模型可用时,通过 setEmbeddingFunction() 注入
65
- *
66
- * @type {Function|null}
67
- */
68
- #embeddingFn;
69
-
70
- /** @type {object|null} */
71
- #logger;
72
-
73
- /**
74
- * @param {import('better-sqlite3').Database} db — better-sqlite3 实例
75
- * @param {object} [opts]
76
- * @param {object} [opts.logger] — Logger 实例
77
- * @param {Function} [opts.embeddingFn] — 向量嵌入函数 (预留)
78
- */
79
- constructor(db, opts = {}) {
80
- super(db, { logger: opts.logger });
81
- this.#embeddingFn = typeof opts.embeddingFn === 'function' ? opts.embeddingFn : null;
82
- this.#logger = opts.logger || null;
83
- }
84
-
85
- // ──────────────────────────────────────────────────────────
86
- // 新增 1: Legacy Migration (Memory.js JSONL → SQLite)
87
- // ──────────────────────────────────────────────────────────
88
-
89
- /**
90
- * 从旧版 Memory.js JSONL 文件迁移数据到 SQLite
91
- *
92
- * 流程:
93
- * 1. 读取 .autosnippet/memory.jsonl (逐行 JSON)
94
- * 2. 映射 type: preference→preference, decision→fact, 其他→fact
95
- * 3. 通过 consolidate() 智能去重合并
96
- * 4. 成功后将旧文件重命名为 .migrated
97
- *
98
- * @param {string} projectRoot — 用户项目根目录
99
- * @returns {Promise<{ migrated: number, skipped: number, error?: string }>}
100
- */
101
- async migrateFromLegacy(projectRoot) {
102
- const legacyPath = path.join(projectRoot, '.autosnippet', 'memory.jsonl');
103
-
104
- if (!fs.existsSync(legacyPath)) {
105
- return { migrated: 0, skipped: 0 };
106
- }
107
-
108
- try {
109
- const raw = fs.readFileSync(legacyPath, 'utf-8').trim();
110
- if (!raw) {
111
- return { migrated: 0, skipped: 0 };
112
- }
113
-
114
- const lines = raw.split('\n').filter(Boolean);
115
- const candidates = lines
116
- .map((line) => {
117
- try {
118
- return JSON.parse(line);
119
- } catch {
120
- return null;
121
- }
122
- })
123
- .filter(Boolean)
124
- .map((m) => ({
125
- type: this.#mapLegacyType(m.type),
126
- content: (m.content || '').trim(),
127
- source: m.source || 'user',
128
- importance: m.type === 'decision' ? 7 : 5,
129
- }))
130
- .filter((m) => m.content.length >= 5); // 过滤过短记忆
131
-
132
- if (candidates.length === 0) {
133
- return { migrated: 0, skipped: lines.length };
134
- }
135
-
136
- const result = this.consolidate(candidates, {
137
- bootstrapSession: 'legacy-migration',
138
- });
139
-
140
- // 迁移成功 → 重命名旧文件 (保留备份)
141
- try {
142
- fs.renameSync(legacyPath, `${legacyPath}.migrated`);
143
- } catch {
144
- // 重命名失败不影响迁移结果
145
- }
146
-
147
- const migrated = result.added + result.merged;
148
- this.#log(
149
- `Legacy migration: ${migrated} migrated (${result.added} added, ${result.merged} merged), ${result.skipped} skipped from ${legacyPath}`
150
- );
151
-
152
- return { migrated, skipped: result.skipped };
153
- } catch (err) {
154
- this.#log(`Legacy migration failed: ${err.message}`);
155
- return { migrated: 0, skipped: 0, error: err.message };
156
- }
157
- }
158
-
159
- // ──────────────────────────────────────────────────────────
160
- // 新增 2: Enhanced Consolidate (冲突预解决)
161
- // ──────────────────────────────────────────────────────────
162
-
163
- /**
164
- * 增强版 consolidate: 在正常 ADD/UPDATE/MERGE 流程之前执行冲突检测
165
- *
166
- * 冲突解决逻辑 (Mem0 风格):
167
- * 1. 对每条候选记忆,在现有库中搜索相似记忆
168
- * 2. 如果发现同类型 + 矛盾内容 → 直接 REPLACE (新信息更可信)
169
- * 3. 已解决的冲突条目不再进入 super.consolidate() 的正常流程
170
- * 4. 未冲突的候选正常走 ADD/UPDATE/MERGE
171
- *
172
- * @param {Array<object>} candidateMemories — 候选记忆列表
173
- * @param {object} [opts]
174
- * @param {string} [opts.bootstrapSession] — Bootstrap session ID
175
- * @returns {{ added: number, updated: number, merged: number, skipped: number, replaced?: number }}
176
- */
177
- consolidate(candidateMemories, opts = {}) {
178
- const { processed, replaced } = this.#preResolveConflicts(candidateMemories);
179
- const result = super.consolidate(processed, opts);
180
- if (replaced > 0) {
181
- result.replaced = replaced;
182
- }
183
- return result;
184
- }
185
-
186
- // ──────────────────────────────────────────────────────────
187
- // 新增 3: Budget-aware toPromptSection
188
- // ──────────────────────────────────────────────────────────
189
-
190
- /**
191
- * 预算感知的 prompt section 生成
192
- *
193
- * 在继承的 toPromptSection 基础上增加 tokenBudget 参数:
194
- * - 根据预算估算可容纳的记忆条数
195
- * - 确保不超过 MemoryCoordinator 分配的预算
196
- *
197
- * @param {object} [opts]
198
- * @param {string} [opts.source] — 过滤 source (user/system/bootstrap)
199
- * @param {string} [opts.query] — 查询上下文 (用于 relevance 打分)
200
- * @param {number} [opts.limit=15] — 最大条数
201
- * @param {number} [opts.tokenBudget] — token 预算 (由 MemoryCoordinator 分配)
202
- * @returns {string} Markdown 格式的记忆摘要
203
- */
204
- toPromptSection({ source, query, limit = 15, tokenBudget } = {}) {
205
- if (tokenBudget && tokenBudget > 0) {
206
- // 估算: 每条记忆约 30 tokens (badge + type + content)
207
- const EST_TOKENS_PER_MEMORY = 30;
208
- const HEADER_TOKENS = 15; // "## 项目记忆 (N 条最相关)" header
209
- const maxByBudget = Math.max(3, Math.floor((tokenBudget - HEADER_TOKENS) / EST_TOKENS_PER_MEMORY));
210
- limit = Math.min(limit, maxByBudget);
211
- }
212
- return super.toPromptSection({ source, query, limit });
213
- }
214
-
215
- // ──────────────────────────────────────────────────────────
216
- // 新增 4: 向量嵌入接口 (ADR-3 预留)
217
- // ──────────────────────────────────────────────────────────
218
-
219
- /**
220
- * 设置向量嵌入函数
221
- *
222
- * 当前架构中,语义检索使用 Jaccard + 子串匹配 (精度有限,§1.2-C2)。
223
- * 预留此接口,未来可注入 embedding 函数提升语义匹配精度。
224
- *
225
- * 注意: 当前嵌入函数仅影响 PersistentMemory 自身的 retrieve() 调用
226
- * 链路 (computeEmbeddingRelevance)。父类 ProjectSemanticMemory 的
227
- * #computeRelevance 使用固有 Jaccard,要完全替换需重构 PSM。
228
- * 这是有意保留的渐进式迁移策略 (ADR-3)。
229
- *
230
- * @param {Function|null} fn — (query: string, content: string) => number (0.0-1.0)
231
- */
232
- setEmbeddingFunction(fn) {
233
- this.#embeddingFn = typeof fn === 'function' ? fn : null;
234
- }
235
-
236
- /**
237
- * 获取当前嵌入函数 (用于检测是否已配置)
238
- * @returns {Function|null}
239
- */
240
- getEmbeddingFunction() {
241
- return this.#embeddingFn;
242
- }
243
-
244
- /**
245
- * 使用嵌入函数计算语义相关性 (如已设置)
246
- *
247
- * 供外部模块 (如 MemoryCoordinator.buildDynamicMemoryPrompt) 在
248
- * 需要更精确语义匹配时调用。如果未设置嵌入函数,返回 null。
249
- *
250
- * @param {string} query
251
- * @param {string} content
252
- * @returns {number|null} — 0.0-1.0 或 null (无嵌入函数)
253
- */
254
- computeEmbeddingRelevance(query, content) {
255
- if (!this.#embeddingFn) return null;
256
- try {
257
- return this.#embeddingFn(query, content);
258
- } catch {
259
- return null;
260
- }
261
- }
262
-
263
- // ──────────────────────────────────────────────────────────
264
- // Private: 冲突预解决 (Mem0 风格)
265
- // ──────────────────────────────────────────────────────────
266
-
267
- /**
268
- * 在 consolidate 主流程前检测并解决矛盾
269
- *
270
- * 对每条候选记忆:
271
- * 1. 使用 search() 查找相似现有记忆
272
- * 2. 如果同类型且检测到矛盾 → 直接 update() 替换旧内容
273
- * 3. 已替换的候选从列表中移除 (不再走 super.consolidate)
274
- *
275
- * @param {Array<object>} candidates
276
- * @returns {{ processed: Array<object>, replaced: number }}
277
- */
278
- #preResolveConflicts(candidates) {
279
- if (!candidates || candidates.length === 0) {
280
- return { processed: [], replaced: 0 };
281
- }
282
-
283
- const processed = [];
284
- let replaced = 0;
285
-
286
- for (const candidate of candidates) {
287
- const content = (candidate.content || '').trim();
288
- if (!content || content.length < 5) {
289
- processed.push(candidate);
290
- continue;
291
- }
292
-
293
- try {
294
- // 使用继承的 search() 公开方法查找相似记忆
295
- const similar = this.search(content, { limit: 3 });
296
- let conflictResolved = false;
297
-
298
- for (const existing of similar) {
299
- // 仅对同类型记忆检测矛盾
300
- if (existing.type === (candidate.type || 'fact')) {
301
- const isContradiction = PersistentMemory.#detectContradiction(
302
- existing.content,
303
- content
304
- );
305
-
306
- if (isContradiction) {
307
- // REPLACE: 新信息覆盖旧信息 (Mem0 原则: 更recent更可信)
308
- this.update(existing.id, {
309
- content: content.substring(0, 500),
310
- importance: Math.max(existing.importance || 5, candidate.importance || 5),
311
- });
312
- conflictResolved = true;
313
- replaced++;
314
- this.#log(
315
- `Conflict resolved: replaced "${existing.content.substring(0, 50)}..." with "${content.substring(0, 50)}..."`
316
- );
317
- break;
318
- }
319
- }
320
- }
321
-
322
- if (!conflictResolved) {
323
- processed.push(candidate);
324
- }
325
- } catch {
326
- // search/update 失败 → 保留候选,走正常 consolidate
327
- processed.push(candidate);
328
- }
329
- }
330
-
331
- return { processed, replaced };
332
- }
333
-
334
- /**
335
- * 检测两段记忆内容是否矛盾
336
- *
337
- * 启发式规则:
338
- * 1. 检查两段内容的否定模式 (中/英文)
339
- * 2. 如果一段有否定另一段没有 (或反之)
340
- * 3. 且两段内容有足够的主题词重叠
341
- * → 判定为矛盾
342
- *
343
- * 示例:
344
- * "我们使用 singleton 模式" vs "不要使用 singleton 模式" → 矛盾
345
- * "使用 React" vs "不使用 Vue" → 非矛盾 (不同主题)
346
- *
347
- * @param {string} contentA — 现有记忆内容
348
- * @param {string} contentB — 候选记忆内容
349
- * @returns {boolean}
350
- */
351
- static #detectContradiction(contentA, contentB) {
352
- if (!contentA || !contentB) return false;
353
-
354
- // 检测否定模式
355
- const aNeg =
356
- NEGATION_PATTERNS_ZH.test(contentA) || NEGATION_PATTERNS_EN.test(contentA);
357
- const bNeg =
358
- NEGATION_PATTERNS_ZH.test(contentB) || NEGATION_PATTERNS_EN.test(contentB);
359
-
360
- // 同向 (都有否定或都没有) → 非矛盾
361
- if (aNeg === bNeg) return false;
362
-
363
- // 异向 → 检查主题重叠度
364
- const wordsA = PersistentMemory.#extractTopicWords(contentA);
365
- const wordsB = PersistentMemory.#extractTopicWords(contentB);
366
-
367
- let overlap = 0;
368
- for (const w of wordsA) {
369
- if (wordsB.has(w)) overlap++;
370
- }
371
-
372
- const minSize = Math.min(wordsA.size, wordsB.size);
373
- if (minSize === 0) return false;
374
-
375
- // 主题重叠达到阈值 → 矛盾
376
- return overlap >= MIN_TOPIC_OVERLAP_WORDS || overlap / minSize >= MIN_TOPIC_OVERLAP_RATIO;
377
- }
378
-
379
- /**
380
- * 提取主题词 (去停用词 + 短词)
381
- * @param {string} text
382
- * @returns {Set<string>}
383
- */
384
- static #extractTopicWords(text) {
385
- if (!text) return new Set();
386
-
387
- // 分词: 空格/标点/CJK边界
388
- const tokens = text
389
- .toLowerCase()
390
- .split(/[\s,;:!?。,;:!?\-_/\\|()[\]{}'"<>·、]+/)
391
- .filter((t) => t.length >= 2);
392
-
393
- // 过滤常见停用词
394
- const stopWords = new Set([
395
- // 中文
396
- '我们', '使用', '项目', '需要', '可以', '应该', '建议', '目前',
397
- '已经', '这个', '那个', '一个', '进行', '通过', '对于',
398
- // 英文
399
- 'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
400
- 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would',
401
- 'could', 'should', 'may', 'might', 'shall', 'can', 'this',
402
- 'that', 'these', 'those', 'with', 'from', 'for', 'and',
403
- 'but', 'not', 'all', 'any', 'each', 'every', 'some',
404
- ]);
405
-
406
- return new Set(tokens.filter((t) => !stopWords.has(t)));
407
- }
408
-
409
- // ──────────────────────────────────────────────────────────
410
- // Private: Helpers
411
- // ──────────────────────────────────────────────────────────
412
-
413
- /**
414
- * 从 Memory.js 的 type 映射到 PSM 的 type
415
- *
416
- * Memory.js types: preference, decision, context
417
- * PSM types: fact, insight, preference
418
- *
419
- * @param {string} legacyType
420
- * @returns {string}
421
- */
422
- #mapLegacyType(legacyType) {
423
- switch (legacyType) {
424
- case 'preference':
425
- return 'preference';
426
- case 'decision':
427
- return 'fact';
428
- case 'context':
429
- return 'fact';
430
- default:
431
- return 'fact';
432
- }
433
- }
434
-
435
- /**
436
- * 日志输出
437
- * @param {string} msg
438
- */
439
- #log(msg) {
440
- const formatted = `[PersistentMemory] ${msg}`;
441
- if (this.#logger?.info) {
442
- this.#logger.info(formatted);
443
- }
444
- }
445
- }
446
-
447
- // ── 向后兼容: 从新模块路径导入时可用旧名称 ──
448
- export { PersistentMemory as ProjectSemanticMemory };
449
-
450
- export default PersistentMemory;