autosnippet 3.2.3 → 3.2.6
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.
- package/README.md +2 -4
- package/bin/cli.js +164 -145
- package/config/constitution.yaml +3 -0
- package/dashboard/dist/assets/{index-DNOHYBhy.css → index-BaGY7kJI.css} +1 -1
- package/dashboard/dist/assets/{index-6itPuGFl.js → index-DfHY_3ln.js} +25 -25
- package/dashboard/dist/index.html +2 -2
- package/lib/cli/CliLogger.js +78 -0
- package/lib/cli/SetupService.js +9 -719
- package/lib/cli/UpgradeService.js +23 -398
- package/lib/cli/deploy/FileDeployer.js +562 -0
- package/lib/cli/deploy/FileManifest.js +272 -0
- package/lib/external/mcp/McpServer.js +22 -26
- package/lib/external/mcp/autoApproveInjector.js +1 -0
- package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +5 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +25 -3
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +6 -6
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +4 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +5 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +89 -44
- package/lib/external/mcp/handlers/consolidated.js +8 -9
- package/lib/external/mcp/handlers/dimension-complete-external.js +4 -4
- package/lib/external/mcp/handlers/guard.js +283 -5
- package/lib/external/mcp/handlers/task.js +183 -9
- package/lib/external/mcp/tools.js +32 -81
- package/lib/http/routes/task.js +55 -0
- package/lib/service/chat/AnalystAgent.js +12 -12
- package/lib/service/chat/ChatAgent.js +227 -545
- package/lib/service/chat/ChatAgentPrompts.js +9 -11
- package/lib/service/chat/ContextWindow.js +2 -296
- package/lib/service/chat/EpisodicConsolidator.js +15 -15
- package/lib/service/chat/ExplorationTracker.js +1262 -0
- package/lib/service/chat/HandoffProtocol.js +8 -9
- package/lib/service/chat/Memory.js +4 -0
- package/lib/service/chat/ProducerAgent.js +9 -6
- package/lib/service/chat/ProjectSemanticMemory.js +4 -0
- package/lib/service/chat/ReasoningTrace.js +182 -0
- package/lib/service/chat/WorkingMemory.js +4 -0
- package/lib/service/chat/memory/ActiveContext.js +910 -0
- package/lib/service/chat/memory/MemoryCoordinator.js +662 -0
- package/lib/service/chat/memory/PersistentMemory.js +450 -0
- package/lib/service/chat/memory/SessionStore.js +896 -0
- package/lib/service/chat/memory/index.js +13 -0
- package/lib/service/chat/tools/ast-graph.js +17 -16
- package/lib/service/cursor/AgentInstructionsGenerator.js +75 -40
- package/lib/service/cursor/FileProtection.js +4 -1
- package/lib/service/guard/GuardCheckEngine.js +10 -3
- package/lib/service/task/TaskGraphService.js +3 -3
- package/lib/shared/LanguageService.js +2 -1
- package/package.json +1 -1
- package/skills/autosnippet-intent/SKILL.md +1 -3
- package/skills/autosnippet-recipes/SKILL.md +1 -3
- package/templates/claude-code/commands/prime.md +19 -0
- package/templates/claude-code/hooks/autosnippet-session.sh +63 -0
- package/templates/claude-code/settings.json +21 -0
- package/templates/copilot-instructions.md +66 -177
- package/templates/cursor-hooks/commands/prime.md +12 -0
- package/templates/cursor-hooks/hooks/session-start.sh +10 -0
- package/templates/cursor-hooks/hooks.json +11 -0
- package/templates/cursor-rules/autosnippet-conventions.mdc +52 -3
- package/templates/cursor-rules/autosnippet-workflow.mdc +51 -27
- package/lib/external/mcp/handlers/decide.js +0 -109
- package/lib/external/mcp/handlers/ready.js +0 -42
- package/lib/service/chat/ReasoningLayer.js +0 -888
- package/templates/claude-hooks.yaml +0 -19
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryCoordinator — 记忆系统统一协调器
|
|
3
|
+
*
|
|
4
|
+
* 设计原则 (CoALA / MemGPT / Generative Agents / Mem0):
|
|
5
|
+
* - Single Coordinator: 所有记忆操作通过此模块路由
|
|
6
|
+
* - Budget-Aware Injection: 记忆注入受统一 token 预算管控
|
|
7
|
+
* - Extract-Update Write Path: 写入经去重/合并/冲突解决
|
|
8
|
+
* - Graceful Degradation: 任意子系统故障不影响核心执行
|
|
9
|
+
*
|
|
10
|
+
* 生命周期:
|
|
11
|
+
* - Bootstrap 模式: 会话级 (orchestrator 创建, 贯穿所有维度)
|
|
12
|
+
* - User Chat 模式: 实例级 (ChatAgent 构造函数创建)
|
|
13
|
+
*
|
|
14
|
+
* @module MemoryCoordinator
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import Logger from '../../../infrastructure/logging/Logger.js';
|
|
18
|
+
import { ActiveContext } from './ActiveContext.js';
|
|
19
|
+
import { SessionStore } from './SessionStore.js';
|
|
20
|
+
|
|
21
|
+
// ── 预算分配策略 (§4.1) ──
|
|
22
|
+
|
|
23
|
+
const BUDGET_PROFILES = Object.freeze({
|
|
24
|
+
user: {
|
|
25
|
+
activeContext: 0.2,
|
|
26
|
+
sessionStore: 0.0,
|
|
27
|
+
persistentMemory: 0.6,
|
|
28
|
+
conversationLog: 0.2,
|
|
29
|
+
},
|
|
30
|
+
analyst: {
|
|
31
|
+
activeContext: 0.45,
|
|
32
|
+
sessionStore: 0.35,
|
|
33
|
+
persistentMemory: 0.15,
|
|
34
|
+
conversationLog: 0.05,
|
|
35
|
+
},
|
|
36
|
+
producer: {
|
|
37
|
+
activeContext: 0.25,
|
|
38
|
+
sessionStore: 0.55,
|
|
39
|
+
persistentMemory: 0.15,
|
|
40
|
+
conversationLog: 0.05,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/** 默认记忆 token 总预算 */
|
|
45
|
+
const DEFAULT_MEMORY_BUDGET = 4000;
|
|
46
|
+
|
|
47
|
+
/** 副作用工具 — 不缓存结果 (B3 fix) */
|
|
48
|
+
const NON_CACHEABLE_TOOLS = new Set([
|
|
49
|
+
'submit_knowledge',
|
|
50
|
+
'submit_with_check',
|
|
51
|
+
'note_finding',
|
|
52
|
+
'get_previous_analysis',
|
|
53
|
+
'get_previous_evidence',
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
// ── 写入路由: 规则匹配模式 (from ChatAgent.#extractMemory) ──
|
|
57
|
+
|
|
58
|
+
const PREFERENCE_PATTERNS = [
|
|
59
|
+
/我们(项目|团队)?(不用|不使用|禁止|避免|偏好|习惯|规范是)/,
|
|
60
|
+
/以后(都|请|要)/,
|
|
61
|
+
/记住/,
|
|
62
|
+
/we\s+(don'?t|never|always|prefer|avoid)\s+use/i,
|
|
63
|
+
/remember\s+(to|that)/i,
|
|
64
|
+
/our\s+(convention|standard|rule)\s+is/i,
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const DECISION_PATTERNS = [
|
|
68
|
+
/决定(了|用|采用|使用)/,
|
|
69
|
+
/(确认|同意|通过)(了|这个方案|审核)/,
|
|
70
|
+
/就(这样|这么)(做|定|办)/,
|
|
71
|
+
/let'?s\s+(go\s+with|use|adopt)/i,
|
|
72
|
+
/approved|confirmed|decided/i,
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
const MEMORY_TAG_REGEX = /\[MEMORY:(\w+)\]\s*([\s\S]*?)\s*\[\/MEMORY\]/g;
|
|
76
|
+
|
|
77
|
+
export class MemoryCoordinator {
|
|
78
|
+
// ── Config ──
|
|
79
|
+
#mode; // 'user' | 'bootstrap'
|
|
80
|
+
#totalBudget;
|
|
81
|
+
#budgetAllocation;
|
|
82
|
+
|
|
83
|
+
// ── Tier 3: Persistent (跨会话) ──
|
|
84
|
+
#persistentMemory; // PersistentMemory (extends ProjectSemanticMemory)
|
|
85
|
+
#conversationLog; // ConversationStore
|
|
86
|
+
|
|
87
|
+
// ── Tier 2: Session (会话级) ──
|
|
88
|
+
#sessionStore; // SessionStore (合并 EpisodicMemory + ToolResultCache)
|
|
89
|
+
|
|
90
|
+
// ── Tier 1: Dimension (维度级) ──
|
|
91
|
+
#activeContexts; // Map<scopeId, ActiveContext>
|
|
92
|
+
#currentScopeId;
|
|
93
|
+
|
|
94
|
+
#logger;
|
|
95
|
+
#completedScopes;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {object} config
|
|
99
|
+
* @param {object} [config.persistentMemory] — PersistentMemory / ProjectSemanticMemory 实例
|
|
100
|
+
* @param {object} [config.sessionStore] — SessionStore 实例 (bootstrap 模式)
|
|
101
|
+
* @param {object} [config.conversationLog] — ConversationStore 实例
|
|
102
|
+
* @param {'user'|'bootstrap'} [config.mode='bootstrap']
|
|
103
|
+
* @param {number} [config.totalMemoryBudget=4000] — 记忆 section 的 token 总预算
|
|
104
|
+
*/
|
|
105
|
+
constructor(config = {}) {
|
|
106
|
+
this.#persistentMemory = config.persistentMemory || null;
|
|
107
|
+
this.#sessionStore = config.sessionStore || null;
|
|
108
|
+
this.#conversationLog = config.conversationLog || null;
|
|
109
|
+
this.#mode = config.mode || 'bootstrap';
|
|
110
|
+
this.#totalBudget = config.totalMemoryBudget || DEFAULT_MEMORY_BUDGET;
|
|
111
|
+
|
|
112
|
+
this.#activeContexts = new Map();
|
|
113
|
+
this.#currentScopeId = null;
|
|
114
|
+
this.#completedScopes = new Set();
|
|
115
|
+
|
|
116
|
+
this.#budgetAllocation = {};
|
|
117
|
+
this.#logger = Logger.getInstance();
|
|
118
|
+
|
|
119
|
+
// 应用默认预算
|
|
120
|
+
this.allocateBudget(this.#mode === 'user' ? 'user' : 'analyst');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ═══════════════════════════════════════════════════════════
|
|
124
|
+
// 预算管理
|
|
125
|
+
// ═══════════════════════════════════════════════════════════
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 配置总预算 (由 ChatAgent.execute 入口调用)
|
|
129
|
+
* @param {object} options
|
|
130
|
+
* @param {number} options.totalContextBudget — 模型总上下文 token 数
|
|
131
|
+
* @param {string} [options.model]
|
|
132
|
+
*/
|
|
133
|
+
configure({ totalContextBudget, model } = {}) {
|
|
134
|
+
if (totalContextBudget) {
|
|
135
|
+
// 记忆 section 约占总上下文的 12.5%
|
|
136
|
+
this.#totalBudget = Math.round(totalContextBudget * 0.125);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 按模式分配预算
|
|
142
|
+
* @param {'user'|'analyst'|'producer'} mode
|
|
143
|
+
* @param {number} [totalTokens] — 覆盖总预算
|
|
144
|
+
*/
|
|
145
|
+
allocateBudget(mode, totalTokens) {
|
|
146
|
+
if (totalTokens) {
|
|
147
|
+
this.#totalBudget = totalTokens;
|
|
148
|
+
}
|
|
149
|
+
const profile = BUDGET_PROFILES[mode] || BUDGET_PROFILES.analyst;
|
|
150
|
+
this.#budgetAllocation = {
|
|
151
|
+
activeContext: Math.round(this.#totalBudget * profile.activeContext),
|
|
152
|
+
sessionStore: Math.round(this.#totalBudget * profile.sessionStore),
|
|
153
|
+
persistentMemory: Math.round(this.#totalBudget * profile.persistentMemory),
|
|
154
|
+
conversationLog: Math.round(this.#totalBudget * profile.conversationLog),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** @returns {number} */
|
|
159
|
+
getTotalBudget() {
|
|
160
|
+
return this.#totalBudget;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** @returns {Record<string, number>} */
|
|
164
|
+
getBudgetAllocation() {
|
|
165
|
+
return { ...this.#budgetAllocation };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 获取消息缓冲区可用预算 (F2)
|
|
170
|
+
* @param {number} totalContextBudget — 模型总上下文 token
|
|
171
|
+
* @param {number} [systemPromptEstimate=2000]
|
|
172
|
+
* @param {number} [toolSchemaEstimate=3000]
|
|
173
|
+
* @param {number} [safetyMargin=3000]
|
|
174
|
+
* @returns {number}
|
|
175
|
+
*/
|
|
176
|
+
getMessageBudget(totalContextBudget, systemPromptEstimate = 2000, toolSchemaEstimate = 3000, safetyMargin = 3000) {
|
|
177
|
+
return totalContextBudget - this.#totalBudget - systemPromptEstimate - toolSchemaEstimate - safetyMargin;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ═══════════════════════════════════════════════════════════
|
|
181
|
+
// 读取 (Prompt 构建)
|
|
182
|
+
// ═══════════════════════════════════════════════════════════
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 构建静态记忆 prompt (execute 入口调用一次)
|
|
186
|
+
* 包含: PersistentMemory + ConversationLog + SessionStore 上下文
|
|
187
|
+
*
|
|
188
|
+
* @param {object} [options]
|
|
189
|
+
* @param {string} [options.mode] — 'user' | 'analyst' | 'producer'
|
|
190
|
+
* @param {string} [options.taskContext] — 当前任务描述 (用于 relevance 打分)
|
|
191
|
+
* @param {string} [options.currentDimId] — 当前维度 (用于 SessionStore 过滤)
|
|
192
|
+
* @param {string[]} [options.focusKeywords] — 聚焦关键词
|
|
193
|
+
* @returns {string}
|
|
194
|
+
*/
|
|
195
|
+
buildStaticMemoryPrompt(options = {}) {
|
|
196
|
+
const parts = [];
|
|
197
|
+
let surplus = 0;
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
// ── 1. PersistentMemory / Memory ──
|
|
201
|
+
const pmBudget = this.#budgetAllocation.persistentMemory || 0;
|
|
202
|
+
if (pmBudget > 0) {
|
|
203
|
+
const pmSection = this.#buildPersistentMemorySection(options);
|
|
204
|
+
if (pmSection) {
|
|
205
|
+
const used = this.#estimateTokens(pmSection);
|
|
206
|
+
surplus += Math.max(0, pmBudget - used);
|
|
207
|
+
parts.push(pmSection);
|
|
208
|
+
} else {
|
|
209
|
+
surplus += pmBudget;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ── 2. SessionStore (legacy: EpisodicMemory) ──
|
|
214
|
+
const ssBudget = this.#budgetAllocation.sessionStore || 0;
|
|
215
|
+
if (ssBudget > 0) {
|
|
216
|
+
const ssSection = this.#buildSessionStoreSection(options);
|
|
217
|
+
if (ssSection) {
|
|
218
|
+
const used = this.#estimateTokens(ssSection);
|
|
219
|
+
surplus += Math.max(0, ssBudget - used);
|
|
220
|
+
parts.push(ssSection);
|
|
221
|
+
} else {
|
|
222
|
+
surplus += ssBudget;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ── 3. ConversationLog ──
|
|
227
|
+
const clBudget = this.#budgetAllocation.conversationLog || 0;
|
|
228
|
+
if (clBudget > 0 && this.#conversationLog) {
|
|
229
|
+
// ConversationLog 通常通过 history 传入,此处预留
|
|
230
|
+
surplus += clBudget;
|
|
231
|
+
}
|
|
232
|
+
} catch (err) {
|
|
233
|
+
this.#logger.warn(`[MemoryCoordinator] buildStaticMemoryPrompt error: ${err.message}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 静态 prompt 不做二次重分配 (动态 prompt 使用 surplus)
|
|
237
|
+
this._lastSurplus = surplus;
|
|
238
|
+
|
|
239
|
+
return parts.filter(Boolean).join('\n');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 构建动态记忆 prompt (每轮调用)
|
|
244
|
+
* 包含: ActiveContext / WorkingMemory 上下文
|
|
245
|
+
*
|
|
246
|
+
* @param {object} [options]
|
|
247
|
+
* @param {string} [options.mode]
|
|
248
|
+
* @returns {string}
|
|
249
|
+
*/
|
|
250
|
+
buildDynamicMemoryPrompt(options = {}) {
|
|
251
|
+
try {
|
|
252
|
+
const acBudget = (this.#budgetAllocation.activeContext || 0) + (this._lastSurplus || 0);
|
|
253
|
+
if (acBudget <= 0) return '';
|
|
254
|
+
|
|
255
|
+
const ac = options.scopeId
|
|
256
|
+
? this.getActiveContext(options.scopeId)
|
|
257
|
+
: this.#getCurrentActiveContext();
|
|
258
|
+
if (!ac) return '';
|
|
259
|
+
|
|
260
|
+
return ac.buildContext(acBudget) || '';
|
|
261
|
+
} catch (err) {
|
|
262
|
+
this.#logger.warn(`[MemoryCoordinator] buildDynamicMemoryPrompt error: ${err.message}`);
|
|
263
|
+
return '';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 合并构建完整记忆 prompt (便捷方法)
|
|
269
|
+
* @param {object} [options]
|
|
270
|
+
* @returns {string}
|
|
271
|
+
*/
|
|
272
|
+
buildMemoryPrompt(options = {}) {
|
|
273
|
+
const staticPart = this.buildStaticMemoryPrompt(options);
|
|
274
|
+
const dynamicPart = this.buildDynamicMemoryPrompt(options);
|
|
275
|
+
return [staticPart, dynamicPart].filter(Boolean).join('\n');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ═══════════════════════════════════════════════════════════
|
|
279
|
+
// 写入
|
|
280
|
+
// ═══════════════════════════════════════════════════════════
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 记录工具调用观察 (合并 WM.observe + TRC.set)
|
|
284
|
+
* @param {string} toolName
|
|
285
|
+
* @param {object} args
|
|
286
|
+
* @param {*} result
|
|
287
|
+
* @param {number} round — 当前迭代轮次
|
|
288
|
+
* @param {boolean} [cacheHit=false] — 本次是否缓存命中
|
|
289
|
+
*/
|
|
290
|
+
recordObservation(toolName, args, result, round, cacheHit = false) {
|
|
291
|
+
try {
|
|
292
|
+
// ActiveContext 的数据记录由 trace.recordToolCall() 处理,
|
|
293
|
+
// 此处只处理缓存写入。
|
|
294
|
+
|
|
295
|
+
// 委托给 SessionStore 缓存
|
|
296
|
+
if (!cacheHit && this.#sessionStore) {
|
|
297
|
+
if (!NON_CACHEABLE_TOOLS.has(toolName)) {
|
|
298
|
+
this.#sessionStore.cacheToolResult(toolName, args, result);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
} catch (err) {
|
|
302
|
+
this.#logger.warn(`[MemoryCoordinator] recordObservation error: ${err.message}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 记录关键发现 (从 note_finding handler 调用)
|
|
308
|
+
* @param {string} finding
|
|
309
|
+
* @param {string} evidence
|
|
310
|
+
* @param {number} importance — 1-10
|
|
311
|
+
* @param {number} round
|
|
312
|
+
* @param {string} [scopeId] — 显式指定 scope (并行安全)
|
|
313
|
+
* @returns {string} — 响应消息
|
|
314
|
+
*/
|
|
315
|
+
noteFinding(finding, evidence, importance, round, scopeId) {
|
|
316
|
+
try {
|
|
317
|
+
const ac = scopeId ? this.getActiveContext(scopeId) : this.#getCurrentActiveContext();
|
|
318
|
+
if (ac) {
|
|
319
|
+
ac.noteKeyFinding(finding, evidence, importance, round);
|
|
320
|
+
return `📌 已记录发现 [${importance}/10]: "${finding.substring(0, 80)}" — 当前共 ${ac.scratchpadSize} 条关键发现`;
|
|
321
|
+
}
|
|
322
|
+
return '⚠ 工作记忆未初始化 (仅在 bootstrap 分析期间可用)';
|
|
323
|
+
} catch (err) {
|
|
324
|
+
this.#logger.warn(`[MemoryCoordinator] noteFinding error: ${err.message}`);
|
|
325
|
+
return `⚠ 记录发现失败: ${err.message}`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 从对话中提取记忆 (迁移自 ChatAgent.#extractMemory)
|
|
331
|
+
*
|
|
332
|
+
* 写入路由 (WriteRouter):
|
|
333
|
+
* - 规则 1: 只在 user 源触发规则匹配 (B4 fix)
|
|
334
|
+
* - 规则 2: [MEMORY] 标签提取 (所有源)
|
|
335
|
+
*
|
|
336
|
+
* @param {string} prompt — 用户输入
|
|
337
|
+
* @param {string} reply — AI 回复
|
|
338
|
+
* @param {'user'|'system'} source
|
|
339
|
+
*/
|
|
340
|
+
extractFromConversation(prompt, reply, source) {
|
|
341
|
+
// §7.6 step 4: 只写 PersistentMemory (不再双写 Memory.js)
|
|
342
|
+
if (!this.#persistentMemory) return;
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
// ── 层 1: 规则快速匹配 (仅 user 源) ──
|
|
346
|
+
if (source === 'user') {
|
|
347
|
+
if (PREFERENCE_PATTERNS.some((p) => p.test(prompt))) {
|
|
348
|
+
this.#persistentMemory.append({
|
|
349
|
+
type: 'preference',
|
|
350
|
+
content: prompt.substring(0, 200),
|
|
351
|
+
source,
|
|
352
|
+
importance: 5,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (DECISION_PATTERNS.some((p) => p.test(prompt))) {
|
|
357
|
+
this.#persistentMemory.append({
|
|
358
|
+
type: 'fact',
|
|
359
|
+
content: prompt.substring(0, 200),
|
|
360
|
+
source,
|
|
361
|
+
importance: 7,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ── 层 2: [MEMORY] 标签提取 (所有源) ──
|
|
367
|
+
if (reply) {
|
|
368
|
+
const regex = new RegExp(MEMORY_TAG_REGEX.source, MEMORY_TAG_REGEX.flags);
|
|
369
|
+
let match;
|
|
370
|
+
while ((match = regex.exec(reply)) !== null) {
|
|
371
|
+
const type = match[1];
|
|
372
|
+
const content = match[2].trim();
|
|
373
|
+
if (content && ['preference', 'decision', 'context'].includes(type)) {
|
|
374
|
+
this.#persistentMemory.append({
|
|
375
|
+
type: type === 'decision' ? 'fact' : type === 'context' ? 'fact' : type,
|
|
376
|
+
content: content.substring(0, 200),
|
|
377
|
+
source,
|
|
378
|
+
importance: type === 'decision' ? 7 : 5,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
} catch {
|
|
384
|
+
/* memory write failure is non-critical */
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ═══════════════════════════════════════════════════════════
|
|
389
|
+
// 缓存代理 (委托到 SessionStore / legacy ToolResultCache)
|
|
390
|
+
// ═══════════════════════════════════════════════════════════
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* 获取缓存的工具结果
|
|
394
|
+
* @param {string} toolName
|
|
395
|
+
* @param {object} args
|
|
396
|
+
* @returns {*|null}
|
|
397
|
+
*/
|
|
398
|
+
getCachedResult(toolName, args) {
|
|
399
|
+
try {
|
|
400
|
+
if (NON_CACHEABLE_TOOLS.has(toolName)) return null;
|
|
401
|
+
return this.#sessionStore?.getCachedResult(toolName, args) ?? null;
|
|
402
|
+
} catch {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* 缓存工具结果
|
|
409
|
+
* @param {string} toolName
|
|
410
|
+
* @param {object} args
|
|
411
|
+
* @param {*} result
|
|
412
|
+
*/
|
|
413
|
+
cacheToolResult(toolName, args, result) {
|
|
414
|
+
try {
|
|
415
|
+
if (NON_CACHEABLE_TOOLS.has(toolName)) return;
|
|
416
|
+
this.#sessionStore?.cacheToolResult(toolName, args, result);
|
|
417
|
+
} catch {
|
|
418
|
+
/* non-critical */
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ═══════════════════════════════════════════════════════════
|
|
423
|
+
// 维度生命周期
|
|
424
|
+
// ═══════════════════════════════════════════════════════════
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* 创建维度作用域 (D2/D3: 多 scope 支持)
|
|
428
|
+
*
|
|
429
|
+
* @param {string} scopeId — 如 'api-patterns:analyst', 'api-patterns:producer'
|
|
430
|
+
* @param {object} [config]
|
|
431
|
+
* @param {boolean} [config.lightweight=false] — 轻量模式 (User Chat)
|
|
432
|
+
* @returns {object} — WorkingMemory (Phase 2) / ActiveContext (Phase 3)
|
|
433
|
+
*/
|
|
434
|
+
createDimensionScope(scopeId, config = {}) {
|
|
435
|
+
this.#currentScopeId = scopeId;
|
|
436
|
+
|
|
437
|
+
// Phase 3: 创建 ActiveContext 实例
|
|
438
|
+
const ac = new ActiveContext({
|
|
439
|
+
lightweight: config.lightweight || false,
|
|
440
|
+
maxRecentRounds: config.maxRecentRounds || 3,
|
|
441
|
+
});
|
|
442
|
+
this.#activeContexts.set(scopeId, ac);
|
|
443
|
+
this.#logger.debug(`[MemoryCoordinator] scope created: ${scopeId} (ActiveContext)`);
|
|
444
|
+
return ac;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* 完成维度: 蒸馏 + 存储到 SessionStore
|
|
449
|
+
* @param {string} scopeId
|
|
450
|
+
* @param {object} [report] — 附加报告数据
|
|
451
|
+
*/
|
|
452
|
+
completeDimension(scopeId, report) {
|
|
453
|
+
try {
|
|
454
|
+
const ac = this.#activeContexts.get(scopeId);
|
|
455
|
+
const distilled = ac ? ac.distill() : null;
|
|
456
|
+
|
|
457
|
+
if (distilled && this.#sessionStore && report) {
|
|
458
|
+
this.#sessionStore.storeDimensionReport(
|
|
459
|
+
scopeId.replace(/:.*$/, ''),
|
|
460
|
+
{
|
|
461
|
+
...report,
|
|
462
|
+
workingMemoryDistilled: distilled,
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// 清理 ActiveContext
|
|
468
|
+
if (ac) {
|
|
469
|
+
ac.clear();
|
|
470
|
+
this.#activeContexts.delete(scopeId);
|
|
471
|
+
}
|
|
472
|
+
this.#completedScopes.add(scopeId);
|
|
473
|
+
|
|
474
|
+
// 切换当前 scope 到下一个或清空
|
|
475
|
+
if (this.#currentScopeId === scopeId) {
|
|
476
|
+
this.#currentScopeId = null;
|
|
477
|
+
}
|
|
478
|
+
this.#logger.debug(`[MemoryCoordinator] scope completed: ${scopeId}`);
|
|
479
|
+
} catch (err) {
|
|
480
|
+
this.#logger.warn(`[MemoryCoordinator] completeDimension error: ${err.message}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* 完成会话: 触发 Consolidator
|
|
486
|
+
* @returns {Promise<{consolidated: number}|null>}
|
|
487
|
+
*/
|
|
488
|
+
async completeSession() {
|
|
489
|
+
try {
|
|
490
|
+
this.#currentScopeId = null;
|
|
491
|
+
this.#logger.info('[MemoryCoordinator] session completed');
|
|
492
|
+
return { consolidated: 0 };
|
|
493
|
+
} catch (err) {
|
|
494
|
+
this.#logger.warn(`[MemoryCoordinator] completeSession error: ${err.message}`);
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// ═══════════════════════════════════════════════════════════
|
|
500
|
+
// 状态查询
|
|
501
|
+
// ═══════════════════════════════════════════════════════════
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* 获取当前或指定 scope 的 ActiveContext / WorkingMemory
|
|
505
|
+
* @param {string} [scopeId]
|
|
506
|
+
* @returns {object|null}
|
|
507
|
+
*/
|
|
508
|
+
getActiveContext(scopeId) {
|
|
509
|
+
const id = scopeId || this.#currentScopeId;
|
|
510
|
+
if (!id) return null;
|
|
511
|
+
return this.#activeContexts.get(id) || null;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* 获取 SessionStore
|
|
516
|
+
* @returns {SessionStore|null}
|
|
517
|
+
*/
|
|
518
|
+
getSessionStore() {
|
|
519
|
+
return this.#sessionStore || null;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* 获取 PersistentMemory / ProjectSemanticMemory
|
|
524
|
+
* @returns {object|null}
|
|
525
|
+
*/
|
|
526
|
+
getPersistentMemory() {
|
|
527
|
+
return this.#persistentMemory || null;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* 获取 ConversationLog / ConversationStore
|
|
532
|
+
* @returns {object|null}
|
|
533
|
+
*/
|
|
534
|
+
getConversationLog() {
|
|
535
|
+
return this.#conversationLog || null;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// ═══════════════════════════════════════════════════════════
|
|
539
|
+
// 自动摘要 (F23)
|
|
540
|
+
// ═══════════════════════════════════════════════════════════
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* 对话更新后触发自动摘要
|
|
544
|
+
* @param {string} conversationId
|
|
545
|
+
* @param {object} aiProvider
|
|
546
|
+
*/
|
|
547
|
+
async onConversationUpdated(conversationId, aiProvider) {
|
|
548
|
+
if (!this.#conversationLog || !aiProvider) return;
|
|
549
|
+
try {
|
|
550
|
+
const messages = this.#conversationLog.load(conversationId, { tokenBudget: Infinity });
|
|
551
|
+
if (messages.length >= 12) {
|
|
552
|
+
await this.#conversationLog.summarize(conversationId, { aiProvider });
|
|
553
|
+
}
|
|
554
|
+
} catch {
|
|
555
|
+
// 摘要失败不影响主流程
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// ═══════════════════════════════════════════════════════════
|
|
560
|
+
// 断点续传
|
|
561
|
+
// ═══════════════════════════════════════════════════════════
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* 保存 checkpoint
|
|
565
|
+
* @param {string} projectRoot
|
|
566
|
+
*/
|
|
567
|
+
async checkpoint(projectRoot) {
|
|
568
|
+
try {
|
|
569
|
+
if (this.#sessionStore?.saveCheckpoint) {
|
|
570
|
+
await this.#sessionStore.saveCheckpoint(projectRoot);
|
|
571
|
+
}
|
|
572
|
+
} catch (err) {
|
|
573
|
+
this.#logger.warn(`[MemoryCoordinator] checkpoint error: ${err.message}`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* 恢复 checkpoint
|
|
579
|
+
* @param {string} projectRoot
|
|
580
|
+
* @returns {Promise<boolean>}
|
|
581
|
+
*/
|
|
582
|
+
async restore(projectRoot) {
|
|
583
|
+
try {
|
|
584
|
+
if (this.#sessionStore?.loadCheckpoint) {
|
|
585
|
+
return await this.#sessionStore.loadCheckpoint(projectRoot);
|
|
586
|
+
}
|
|
587
|
+
return false;
|
|
588
|
+
} catch (err) {
|
|
589
|
+
this.#logger.warn(`[MemoryCoordinator] restore error: ${err.message}`);
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// ═══════════════════════════════════════════════════════════
|
|
595
|
+
// 清理
|
|
596
|
+
// ═══════════════════════════════════════════════════════════
|
|
597
|
+
|
|
598
|
+
dispose() {
|
|
599
|
+
for (const ac of this.#activeContexts.values()) {
|
|
600
|
+
try { ac.clear(); } catch { /* non-critical */ }
|
|
601
|
+
}
|
|
602
|
+
this.#activeContexts.clear();
|
|
603
|
+
this.#sessionStore = null;
|
|
604
|
+
this.#currentScopeId = null;
|
|
605
|
+
this.#completedScopes.clear();
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// ═══════════════════════════════════════════════════════════
|
|
609
|
+
// 私有方法
|
|
610
|
+
// ═══════════════════════════════════════════════════════════
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* 获取当前 scope 的 ActiveContext
|
|
614
|
+
* @returns {ActiveContext|null}
|
|
615
|
+
*/
|
|
616
|
+
#getCurrentActiveContext() {
|
|
617
|
+
if (!this.#currentScopeId) return null;
|
|
618
|
+
return this.#activeContexts.get(this.#currentScopeId) || null;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* 构建 PersistentMemory section
|
|
623
|
+
*/
|
|
624
|
+
#buildPersistentMemorySection(options = {}) {
|
|
625
|
+
if (this.#persistentMemory?.toPromptSection) {
|
|
626
|
+
return this.#persistentMemory.toPromptSection({ source: 'user' }) || '';
|
|
627
|
+
}
|
|
628
|
+
return '';
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* 构建 SessionStore section (legacy: EpisodicMemory)
|
|
633
|
+
*/
|
|
634
|
+
#buildSessionStoreSection(options = {}) {
|
|
635
|
+
const ss = this.#sessionStore;
|
|
636
|
+
if (!ss?.buildContextForDimension) return '';
|
|
637
|
+
|
|
638
|
+
const dimId = options.currentDimId;
|
|
639
|
+
if (!dimId) return '';
|
|
640
|
+
|
|
641
|
+
try {
|
|
642
|
+
return ss.buildContextForDimension(dimId, options.focusKeywords || []) || '';
|
|
643
|
+
} catch {
|
|
644
|
+
return '';
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* 粗略估算 token 数 (CJK 感知)
|
|
650
|
+
* @param {string} text
|
|
651
|
+
* @returns {number}
|
|
652
|
+
*/
|
|
653
|
+
#estimateTokens(text) {
|
|
654
|
+
if (!text) return 0;
|
|
655
|
+
// 粗略: 英文 ~4 chars/token, 中文 ~2 chars/token
|
|
656
|
+
const cjkCount = (text.match(/[\u4e00-\u9fff\u3000-\u303f]/g) || []).length;
|
|
657
|
+
const restCount = text.length - cjkCount;
|
|
658
|
+
return Math.ceil(cjkCount / 2 + restCount / 4);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export default MemoryCoordinator;
|