@cc-soul/openclaw 1.0.0

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.
@@ -0,0 +1,688 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ import { MEMORIES_PATH, HISTORY_PATH, loadJson, debouncedSave } from "./persistence.ts";
4
+ import { spawnCLI } from "./cli.ts";
5
+ function shuffleArray(arr) {
6
+ const result = [...arr];
7
+ for (let i = result.length - 1; i > 0; i--) {
8
+ const j = Math.floor(Math.random() * (i + 1));
9
+ [result[i], result[j]] = [result[j], result[i]];
10
+ }
11
+ return result;
12
+ }
13
+ __name(shuffleArray, "shuffleArray");
14
+ const memoryState = {
15
+ memories: [],
16
+ chatHistory: []
17
+ };
18
+ const MAX_MEMORIES = 1e4;
19
+ const MAX_HISTORY = 100;
20
+ const INJECT_HISTORY = 30;
21
+ function addToHistory(user, assistant) {
22
+ memoryState.chatHistory.push({
23
+ user: user.slice(0, 1e3),
24
+ assistant: assistant.slice(0, 2e3),
25
+ ts: Date.now()
26
+ });
27
+ if (memoryState.chatHistory.length > MAX_HISTORY) {
28
+ const trimmed = memoryState.chatHistory.slice(-MAX_HISTORY);
29
+ memoryState.chatHistory.length = 0;
30
+ memoryState.chatHistory.push(...trimmed);
31
+ }
32
+ debouncedSave(HISTORY_PATH, memoryState.chatHistory);
33
+ }
34
+ __name(addToHistory, "addToHistory");
35
+ function buildHistoryContext() {
36
+ if (memoryState.chatHistory.length === 0) return "";
37
+ const recent = memoryState.chatHistory.slice(-INJECT_HISTORY);
38
+ const lines = recent.map((t) => {
39
+ const timeStr = new Date(t.ts).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
40
+ return `[${timeStr}] \u7528\u6237: ${t.user}
41
+ \u52A9\u624B: ${t.assistant}`;
42
+ }).join("\n\n");
43
+ return `[\u5BF9\u8BDD\u5386\u53F2\uFF08\u6700\u8FD1${recent.length}\u8F6E\uFF09]
44
+ ${lines}`;
45
+ }
46
+ __name(buildHistoryContext, "buildHistoryContext");
47
+ function tagMemoryAsync(memIndex, content) {
48
+ const safeContent = content.slice(0, 200).replace(/"/g, '\\"').replace(/\n/g, " ");
49
+ spawnCLI(
50
+ `\u4E3A\u4EE5\u4E0B\u5185\u5BB9\u751F\u621010-15\u4E2A\u8BED\u4E49\u6807\u7B7E\uFF0C\u8981\u6C42\uFF1A
51
+ 1. \u5305\u542B\u539F\u6587\u5173\u952E\u8BCD
52
+ 2. \u5305\u542B\u540C\u4E49\u8BCD\uFF08\u5982"dyld"\u2192\u4E5F\u52A0"\u52A8\u6001\u94FE\u63A5\u5668","mach-o"\u2192\u4E5F\u52A0"\u4E8C\u8FDB\u5236"\uFF09
53
+ 3. \u5305\u542B\u4E0A\u4F4D\u6982\u5FF5\uFF08\u5982"python flask"\u2192\u4E5F\u52A0"web\u6846\u67B6","\u540E\u7AEF"\uFF09
54
+ 4. \u4E2D\u82F1\u6587\u90FD\u8981
55
+ \u9017\u53F7\u5206\u9694\uFF0C\u53EA\u8F93\u51FA\u6807\u7B7E\uFF1A
56
+ "${safeContent}"`,
57
+ (output) => {
58
+ if (!output) return;
59
+ const tags = output.split(/[,,]/).map((t) => t.trim().toLowerCase().replace(/^[#\s]+/, "")).filter((t) => t.length >= 2 && t.length <= 20);
60
+ if (tags.length >= 3 && memIndex < memoryState.memories.length) {
61
+ memoryState.memories[memIndex].tags = tags;
62
+ saveMemories();
63
+ console.log(`[cc-soul][tags] tagged mem[${memIndex}]: [${tags.join(", ")}]`);
64
+ }
65
+ }
66
+ );
67
+ }
68
+ __name(tagMemoryAsync, "tagMemoryAsync");
69
+ function batchTagUntaggedMemories() {
70
+ const untagged = memoryState.memories.map((m, i) => ({ m, i })).filter(({ m }) => !m.tags || m.tags.length === 0).slice(0, 5);
71
+ if (untagged.length === 0) return;
72
+ console.log(`[cc-soul][tags] batch tagging ${untagged.length} untagged memories`);
73
+ let delay = 0;
74
+ for (const { m, i } of untagged) {
75
+ setTimeout(() => tagMemoryAsync(i, m.content), delay);
76
+ delay += 1e4;
77
+ }
78
+ }
79
+ __name(batchTagUntaggedMemories, "batchTagUntaggedMemories");
80
+ function loadMemories() {
81
+ const loaded = loadJson(MEMORIES_PATH, []);
82
+ memoryState.memories.length = 0;
83
+ memoryState.memories.push(...loaded);
84
+ const loadedHistory = loadJson(HISTORY_PATH, []);
85
+ memoryState.chatHistory.length = 0;
86
+ memoryState.chatHistory.push(...loadedHistory);
87
+ }
88
+ __name(loadMemories, "loadMemories");
89
+ function saveMemories() {
90
+ debouncedSave(MEMORIES_PATH, memoryState.memories);
91
+ }
92
+ __name(saveMemories, "saveMemories");
93
+ function defaultVisibility(scope) {
94
+ if (scope === "fact" || scope === "discovery") return "global";
95
+ if (scope === "correction" || scope === "preference") return "channel";
96
+ if (scope === "proactive" || scope === "curiosity" || scope === "reflection") return "private";
97
+ return "channel";
98
+ }
99
+ __name(defaultVisibility, "defaultVisibility");
100
+ function addMemory(content, scope, userId, visibility, channelId) {
101
+ if (!content || content.length < 3) return;
102
+ const isDup = memoryState.memories.some(
103
+ (m) => m.scope === scope && (m.content === content || m.content.length > 10 && content.includes(m.content.slice(0, 20)))
104
+ );
105
+ if (isDup) return;
106
+ const resolvedVisibility = visibility || defaultVisibility(scope);
107
+ const newIndex = memoryState.memories.length;
108
+ memoryState.memories.push({ content, scope, ts: Date.now(), userId, visibility: resolvedVisibility, channelId });
109
+ if (memoryState.memories.length > MAX_MEMORIES) {
110
+ const trimmed = memoryState.memories.slice(-Math.floor(MAX_MEMORIES * 0.8));
111
+ memoryState.memories.length = 0;
112
+ memoryState.memories.push(...trimmed);
113
+ }
114
+ invalidateIDF();
115
+ saveMemories();
116
+ if (content.length > 10) {
117
+ const lastIdx = memoryState.memories.length - 1;
118
+ if (lastIdx >= 0 && memoryState.memories[lastIdx].content === content && !memoryState.memories[lastIdx].tags) {
119
+ tagMemoryAsync(lastIdx, content);
120
+ }
121
+ }
122
+ }
123
+ __name(addMemory, "addMemory");
124
+ function addMemoryWithEmotion(content, scope, userId, visibility, channelId, emotion) {
125
+ addMemory(content, scope, userId, visibility, channelId);
126
+ if (emotion) {
127
+ const validEmotions = ["neutral", "warm", "important", "painful", "funny"];
128
+ const matched = validEmotions.find((e) => emotion.includes(e)) || "neutral";
129
+ const idx = memoryState.memories.findIndex((m) => m.content === content);
130
+ if (idx >= 0) {
131
+ memoryState.memories[idx].emotion = matched;
132
+ saveMemories();
133
+ }
134
+ } else if (content.length > 20) {
135
+ spawnCLI(
136
+ `\u8FD9\u6761\u8BB0\u5FC6\u7684\u60C5\u611F\u6743\u91CD\uFF1F"${content.slice(0, 100)}" \u56DE\u7B54: neutral/warm/important/painful/funny \u53EA\u56DE\u4E00\u4E2A\u8BCD`,
137
+ (output) => {
138
+ const emotion2 = output?.trim().toLowerCase() || "neutral";
139
+ const validEmotions = ["neutral", "warm", "important", "painful", "funny"];
140
+ const matched = validEmotions.find((e) => emotion2.includes(e)) || "neutral";
141
+ const idx = memoryState.memories.findIndex((m) => m.content === content);
142
+ if (idx >= 0) {
143
+ memoryState.memories[idx].emotion = matched;
144
+ saveMemories();
145
+ }
146
+ }
147
+ );
148
+ }
149
+ }
150
+ __name(addMemoryWithEmotion, "addMemoryWithEmotion");
151
+ let idfCache = null;
152
+ function buildIDF() {
153
+ if (idfCache && idfCache.size > 0) return idfCache;
154
+ const df = /* @__PURE__ */ new Map();
155
+ const N = memoryState.memories.length || 1;
156
+ for (const mem of memoryState.memories) {
157
+ const words = new Set((mem.content.match(/[\u4e00-\u9fff]{2,}|[a-z]{3,}/gi) || []).map((w) => w.toLowerCase()));
158
+ for (const w of words) {
159
+ df.set(w, (df.get(w) || 0) + 1);
160
+ }
161
+ }
162
+ const idf = /* @__PURE__ */ new Map();
163
+ for (const [word, count] of df) {
164
+ idf.set(word, Math.log(N / (1 + count)));
165
+ }
166
+ idfCache = idf;
167
+ return idf;
168
+ }
169
+ __name(buildIDF, "buildIDF");
170
+ function tfidfVector(text, idf) {
171
+ const words = (text.match(/[\u4e00-\u9fff]{2,}|[a-z]{3,}/gi) || []).map((w) => w.toLowerCase());
172
+ const tf = /* @__PURE__ */ new Map();
173
+ for (const w of words) {
174
+ tf.set(w, (tf.get(w) || 0) + 1);
175
+ }
176
+ const vec = /* @__PURE__ */ new Map();
177
+ for (const [word, count] of tf) {
178
+ const idfVal = idf.get(word) || 1;
179
+ vec.set(word, count / words.length * idfVal);
180
+ }
181
+ return vec;
182
+ }
183
+ __name(tfidfVector, "tfidfVector");
184
+ function cosineSim(a, b) {
185
+ let dot = 0, normA = 0, normB = 0;
186
+ for (const [word, val] of a) {
187
+ const bVal = b.get(word) || 0;
188
+ dot += val * bVal;
189
+ normA += val * val;
190
+ }
191
+ for (const [, val] of b) {
192
+ normB += val * val;
193
+ }
194
+ if (normA === 0 || normB === 0) return 0;
195
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
196
+ }
197
+ __name(cosineSim, "cosineSim");
198
+ function recall(msg, topN = 3, userId, channelId) {
199
+ if (memoryState.memories.length === 0 || !msg) return [];
200
+ const queryWords = new Set(
201
+ (msg.match(/[\u4e00-\u9fff]{2,}|[a-z]{3,}/gi) || []).map((w) => w.toLowerCase())
202
+ );
203
+ if (queryWords.size === 0) return [];
204
+ let idf = null;
205
+ const scored = [];
206
+ for (const mem of memoryState.memories) {
207
+ const vis = mem.visibility || "global";
208
+ if (vis === "channel" && channelId && mem.channelId && mem.channelId !== channelId) continue;
209
+ if (vis === "private" && userId && mem.userId && mem.userId !== userId) continue;
210
+ let sim = 0;
211
+ if (mem.tags && mem.tags.length > 0) {
212
+ let hits = 0;
213
+ for (const qw of queryWords) {
214
+ for (const tag of mem.tags) {
215
+ if (tag.includes(qw) || qw.includes(tag)) {
216
+ hits += 1;
217
+ break;
218
+ }
219
+ }
220
+ }
221
+ sim = hits / Math.max(1, queryWords.size);
222
+ } else {
223
+ if (!idf) idf = buildIDF();
224
+ const queryVec = tfidfVector(msg, idf);
225
+ const memVec = tfidfVector(mem.content, idf);
226
+ sim = cosineSim(queryVec, memVec);
227
+ }
228
+ if (sim < 0.05) continue;
229
+ const ageDays = mem.ts > 0 ? (Date.now() - mem.ts) / 864e5 : 30;
230
+ const recency = 1 / (1 + ageDays * 0.03);
231
+ const scopeBoost = mem.scope === "preference" || mem.scope === "fact" ? 1.3 : mem.scope === "correction" ? 1.5 : 1;
232
+ const emotionBoost = mem.emotion === "important" ? 1.4 : mem.emotion === "painful" ? 1.3 : mem.emotion === "warm" ? 1.2 : 1;
233
+ const userBoost = userId && mem.userId && mem.userId === userId ? 1.3 : 1;
234
+ const consolidatedBoost = mem.scope === "consolidated" ? 1.5 : 1;
235
+ scored.push({ ...mem, score: sim * recency * scopeBoost * emotionBoost * userBoost * consolidatedBoost });
236
+ }
237
+ scored.sort((a, b) => b.score - a.score);
238
+ return scored.slice(0, topN);
239
+ }
240
+ __name(recall, "recall");
241
+ function invalidateIDF() {
242
+ idfCache = null;
243
+ }
244
+ __name(invalidateIDF, "invalidateIDF");
245
+ function consolidateFragments() {
246
+ if (memoryState.memories.length === 0) return "";
247
+ const prefs = memoryState.memories.filter((m) => m.scope === "preference").slice(-5);
248
+ const facts = memoryState.memories.filter((m) => m.scope === "fact").slice(-5);
249
+ const recent = memoryState.memories.filter((m) => m.scope === "topic").slice(-10);
250
+ const parts = [];
251
+ if (facts.length) parts.push("\u5DF2\u77E5: " + facts.map((f) => f.content).join("; "));
252
+ if (prefs.length) parts.push("\u504F\u597D: " + prefs.map((p) => p.content).join("; "));
253
+ if (recent.length) {
254
+ const topics = [...new Set(recent.map((r) => r.content.replace("\u8BDD\u9898: ", "")))].slice(-5);
255
+ parts.push("\u8FD1\u671F\u8BDD\u9898: " + topics.join(", "));
256
+ }
257
+ const emotional = memoryState.memories.filter((m) => m.emotion && m.emotion !== "neutral").slice(-5);
258
+ if (emotional.length) {
259
+ parts.push("\u5370\u8C61\u6DF1\u523B: " + emotional.map((m) => `${m.content} (${m.emotion})`).join("; "));
260
+ }
261
+ const dreams = memoryState.memories.filter((m) => m.scope === "dream").slice(-3);
262
+ if (dreams.length) {
263
+ parts.push("\u68A6\u5883\u6D1E\u5BDF: " + dreams.map((d) => d.content).join("; "));
264
+ }
265
+ const curiosities = memoryState.memories.filter((m) => m.scope === "curiosity").slice(-3);
266
+ if (curiosities.length) {
267
+ parts.push("\u597D\u5947: " + curiosities.map((c) => c.content).join("; "));
268
+ }
269
+ return parts.join("\n");
270
+ }
271
+ __name(consolidateFragments, "consolidateFragments");
272
+ let lastConsolidationTs = 0;
273
+ const CONSOLIDATION_COOLDOWN_MS = 24 * 3600 * 1e3;
274
+ function consolidateMemories() {
275
+ if (memoryState.memories.length < 500) return;
276
+ if (Date.now() - lastConsolidationTs < CONSOLIDATION_COOLDOWN_MS) return;
277
+ lastConsolidationTs = Date.now();
278
+ const groups = /* @__PURE__ */ new Map();
279
+ for (const mem of memoryState.memories) {
280
+ const key = mem.scope || "unknown";
281
+ if (!groups.has(key)) groups.set(key, []);
282
+ groups.get(key).push(mem);
283
+ }
284
+ for (const [scope, mems] of groups) {
285
+ if (mems.length < 50) continue;
286
+ if (scope === "consolidated") continue;
287
+ const oldest = mems.sort((a, b) => a.ts - b.ts).slice(0, 20);
288
+ const contents = oldest.map((m) => m.content).join("\n");
289
+ spawnCLI(
290
+ `\u4EE5\u4E0B\u662F${scope}\u7C7B\u578B\u7684${oldest.length}\u6761\u8BB0\u5FC6\uFF0C\u8BF7\u5408\u5E76\u4E3A1-3\u6761\u6458\u8981\uFF08\u4FDD\u7559\u5173\u952E\u4FE1\u606F\uFF09\uFF1A
291
+
292
+ ${contents.slice(0, 1500)}
293
+
294
+ \u683C\u5F0F\uFF1A\u6BCF\u6761\u6458\u8981\u4E00\u884C`,
295
+ (output) => {
296
+ if (!output || output.length < 10) return;
297
+ const summaries = output.split("\n").filter((l) => l.trim().length > 5).slice(0, 3);
298
+ const indicesToDelete = oldest.map((old) => memoryState.memories.findIndex((m) => m.content === old.content && m.ts === old.ts)).filter((idx) => idx >= 0).sort((a, b) => b - a);
299
+ for (const idx of indicesToDelete) {
300
+ memoryState.memories.splice(idx, 1);
301
+ }
302
+ for (const summary of summaries) {
303
+ memoryState.memories.push({
304
+ content: summary.trim(),
305
+ scope: "consolidated",
306
+ ts: Date.now(),
307
+ visibility: oldest[0]?.visibility || "global"
308
+ });
309
+ }
310
+ saveMemories();
311
+ invalidateIDF();
312
+ console.log(`[cc-soul][memory] consolidated ${oldest.length} ${scope} memories -> ${summaries.length} summaries`);
313
+ }
314
+ );
315
+ }
316
+ }
317
+ __name(consolidateMemories, "consolidateMemories");
318
+ let lastRecallFeedbackTs = 0;
319
+ const RECALL_FEEDBACK_COOLDOWN = 6e4;
320
+ function recallFeedbackLoop(userMsg, recalledContents) {
321
+ const now = Date.now();
322
+ if (now - lastRecallFeedbackTs < RECALL_FEEDBACK_COOLDOWN) return;
323
+ if (memoryState.memories.length < 20) return;
324
+ if (userMsg.length < 10) return;
325
+ lastRecallFeedbackTs = now;
326
+ const recalledSet = new Set(recalledContents);
327
+ const candidates = shuffleArray(memoryState.memories.filter((m) => !recalledSet.has(m.content) && m.content.length > 15)).slice(0, 20);
328
+ if (candidates.length === 0) return;
329
+ const candidateList = candidates.map((m, i) => `${i + 1}. [${m.scope}] ${m.content.slice(0, 80)}`).join("\n");
330
+ const recalledList = recalledContents.length > 0 ? recalledContents.map((c) => c.slice(0, 60)).join("; ") : "(\u65E0)";
331
+ spawnCLI(
332
+ `\u7528\u6237\u95EE\u4E86: "${userMsg.slice(0, 200)}"
333
+ \u7CFB\u7EDF\u53EC\u56DE\u4E86: ${recalledList}
334
+
335
+ \u4EE5\u4E0B\u662F\u672A\u88AB\u53EC\u56DE\u7684\u8BB0\u5FC6\uFF0C\u54EA\u4E9B\u5176\u5B9E\u548C\u7528\u6237\u7684\u95EE\u9898\u76F8\u5173\uFF1F
336
+ ${candidateList}
337
+
338
+ \u53EA\u8F93\u51FA\u76F8\u5173\u8BB0\u5FC6\u7684\u7F16\u53F7\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982\u679C\u90FD\u4E0D\u76F8\u5173\u5C31\u56DE\u7B54"\u65E0"`,
339
+ (output) => {
340
+ if (!output || output.includes("\u65E0")) return;
341
+ const nums = output.match(/\d+/g)?.map(Number) || [];
342
+ const queryWords = (userMsg.match(/[\u4e00-\u9fff]{2,}|[a-z]{3,}/gi) || []).map((w) => w.toLowerCase()).slice(0, 8);
343
+ let patched = 0;
344
+ for (const num of nums) {
345
+ const idx = num - 1;
346
+ if (idx < 0 || idx >= candidates.length) continue;
347
+ const mem = candidates[idx];
348
+ const memIdx = memoryState.memories.findIndex((m) => m.content === mem.content && m.ts === mem.ts);
349
+ if (memIdx < 0) continue;
350
+ if (!mem.tags) mem.tags = [];
351
+ for (const w of queryWords) {
352
+ if (!mem.tags.includes(w)) {
353
+ mem.tags.push(w);
354
+ }
355
+ }
356
+ if (mem.tags.length > 25) mem.tags = mem.tags.slice(-25);
357
+ patched++;
358
+ }
359
+ if (patched > 0) {
360
+ saveMemories();
361
+ console.log(`[cc-soul][recall-feedback] patched ${patched} memories with cross-tags from query`);
362
+ }
363
+ }
364
+ );
365
+ }
366
+ __name(recallFeedbackLoop, "recallFeedbackLoop");
367
+ let cachedAssociation = null;
368
+ const ASSOCIATION_COOLDOWN = 3e4;
369
+ function triggerAssociativeRecall(userMsg, topRecalled) {
370
+ if (userMsg.length < 10) return;
371
+ if (cachedAssociation && Date.now() - cachedAssociation.ts < ASSOCIATION_COOLDOWN) return;
372
+ const recalledSet = new Set(topRecalled);
373
+ const pool = shuffleArray(memoryState.memories.filter((m) => !recalledSet.has(m.content) && m.content.length > 15 && m.scope !== "proactive")).slice(0, 30);
374
+ if (pool.length < 5) return;
375
+ const memList = pool.map((m, i) => `${i + 1}. ${m.content.slice(0, 80)}`).join("\n");
376
+ spawnCLI(
377
+ `\u7528\u6237\u8BF4: "${userMsg.slice(0, 200)}"
378
+
379
+ \u4EE5\u4E0B\u662F\u4E00\u4E9B\u5386\u53F2\u8BB0\u5FC6\uFF0C\u54EA\u4E9B\u548C\u7528\u6237\u5F53\u524D\u7684\u8BDD\u9898\u6709\u9690\u542B\u5173\u8054\uFF1F\uFF08\u4E0D\u662F\u5B57\u9762\u5339\u914D\uFF0C\u800C\u662F\u6DF1\u5C42\u8054\u60F3\uFF09
380
+ ${memList}
381
+
382
+ \u9009\u51FA\u6700\u76F8\u5173\u76841-3\u6761\uFF0C\u8F93\u51FA\u7F16\u53F7\u548C\u4E00\u53E5\u8BDD\u8BF4\u660E\u4E3A\u4EC0\u4E48\u76F8\u5173\u3002\u683C\u5F0F: "\u7F16\u53F7: \u539F\u56E0"\u3002\u90FD\u4E0D\u76F8\u5173\u5C31\u56DE\u7B54"\u65E0"`,
383
+ (output) => {
384
+ if (!output || output.includes("\u65E0") || output.length < 5) {
385
+ cachedAssociation = null;
386
+ return;
387
+ }
388
+ cachedAssociation = {
389
+ query: userMsg.slice(0, 50),
390
+ result: output.slice(0, 300),
391
+ ts: Date.now()
392
+ };
393
+ console.log(`[cc-soul][associative] found deep associations: ${output.slice(0, 80)}`);
394
+ }
395
+ );
396
+ }
397
+ __name(triggerAssociativeRecall, "triggerAssociativeRecall");
398
+ function getAssociativeRecall() {
399
+ if (!cachedAssociation) return "";
400
+ if (Date.now() - cachedAssociation.ts > 3e5) {
401
+ cachedAssociation = null;
402
+ return "";
403
+ }
404
+ return `[\u6DF1\u5C42\u8054\u60F3] ${cachedAssociation.result}`;
405
+ }
406
+ __name(getAssociativeRecall, "getAssociativeRecall");
407
+ let lastSessionSummaryTs = 0;
408
+ const SESSION_SUMMARY_COOLDOWN = 18e5;
409
+ function parseMemoryCommands(responseText) {
410
+ const commands = [];
411
+ const rememberPattern = /[((](?:记下了|记住|记下|save)[::]\s*(.+?)[))]/g;
412
+ let match;
413
+ while ((match = rememberPattern.exec(responseText)) !== null) {
414
+ commands.push({ action: "remember", content: match[1].trim() });
415
+ }
416
+ const forgetPattern = /[((](?:忘掉|忘记|forget|过时了)[::]\s*(.+?)[))]/g;
417
+ while ((match = forgetPattern.exec(responseText)) !== null) {
418
+ commands.push({ action: "forget", content: match[1].trim() });
419
+ }
420
+ const updatePattern = /[((](?:更正记忆|更新记忆|update)[::]\s*(.+?)\s*(?:→|->)+\s*(.+?)[))]/g;
421
+ while ((match = updatePattern.exec(responseText)) !== null) {
422
+ commands.push({ action: "update", content: match[2].trim(), oldContent: match[1].trim() });
423
+ }
424
+ const searchPattern = /[((](?:想查|查一下|search|回忆一下)[::]\s*(.+?)[))]/g;
425
+ while ((match = searchPattern.exec(responseText)) !== null) {
426
+ commands.push({ action: "search", content: match[1].trim() });
427
+ }
428
+ return commands;
429
+ }
430
+ __name(parseMemoryCommands, "parseMemoryCommands");
431
+ let pendingSearchResults = [];
432
+ function getPendingSearchResults() {
433
+ const results = [...pendingSearchResults];
434
+ pendingSearchResults = [];
435
+ return results;
436
+ }
437
+ __name(getPendingSearchResults, "getPendingSearchResults");
438
+ function executeMemoryCommands(commands, userId, channelId) {
439
+ for (const cmd of commands) {
440
+ switch (cmd.action) {
441
+ case "remember":
442
+ addMemory(cmd.content, "fact", userId, "global", channelId);
443
+ console.log(`[cc-soul][active-memory] REMEMBER: ${cmd.content.slice(0, 60)}`);
444
+ break;
445
+ case "forget": {
446
+ const keyword = cmd.content.toLowerCase();
447
+ let forgotten = 0;
448
+ for (const mem of memoryState.memories) {
449
+ if (mem.content.toLowerCase().includes(keyword) && mem.scope !== "consolidated") {
450
+ mem.scope = "expired";
451
+ forgotten++;
452
+ }
453
+ }
454
+ if (forgotten > 0) {
455
+ saveMemories();
456
+ console.log(`[cc-soul][active-memory] FORGET: marked ${forgotten} memories as expired (keyword: ${cmd.content.slice(0, 30)})`);
457
+ }
458
+ break;
459
+ }
460
+ case "update": {
461
+ if (!cmd.oldContent) break;
462
+ const oldKw = cmd.oldContent.toLowerCase();
463
+ for (const mem of memoryState.memories) {
464
+ if (mem.content.toLowerCase().includes(oldKw) && mem.scope !== "expired") {
465
+ console.log(`[cc-soul][active-memory] UPDATE: "${mem.content.slice(0, 40)}" \u2192 "${cmd.content.slice(0, 40)}"`);
466
+ mem.content = cmd.content;
467
+ mem.ts = Date.now();
468
+ mem.tags = void 0;
469
+ break;
470
+ }
471
+ }
472
+ saveMemories();
473
+ break;
474
+ }
475
+ case "search": {
476
+ const results = recall(cmd.content, 5, userId, channelId);
477
+ if (results.length > 0) {
478
+ pendingSearchResults = results.map((m) => `- ${m.content}${m.emotion && m.emotion !== "neutral" ? ` (${m.emotion})` : ""}`);
479
+ console.log(`[cc-soul][active-memory] SEARCH "${cmd.content.slice(0, 30)}": found ${results.length} results (cached for next turn)`);
480
+ }
481
+ break;
482
+ }
483
+ }
484
+ }
485
+ }
486
+ __name(executeMemoryCommands, "executeMemoryCommands");
487
+ let lastContradictionScan = 0;
488
+ const CONTRADICTION_SCAN_COOLDOWN = 24 * 36e5;
489
+ function scanForContradictions() {
490
+ const now = Date.now();
491
+ if (now - lastContradictionScan < CONTRADICTION_SCAN_COOLDOWN) return;
492
+ if (memoryState.memories.length < 20) return;
493
+ lastContradictionScan = now;
494
+ const conflictScopes = ["fact", "preference", "correction"];
495
+ const groups = /* @__PURE__ */ new Map();
496
+ for (const mem of memoryState.memories) {
497
+ if (!conflictScopes.includes(mem.scope)) continue;
498
+ if (mem.scope === "expired") continue;
499
+ if (!groups.has(mem.scope)) groups.set(mem.scope, []);
500
+ groups.get(mem.scope).push(mem);
501
+ }
502
+ for (const [scope, mems] of groups) {
503
+ if (mems.length < 5) continue;
504
+ const sorted = [...mems].sort((a, b) => b.ts - a.ts);
505
+ const recent = sorted.slice(0, 10);
506
+ const older = sorted.slice(10, 20);
507
+ if (older.length < 3) continue;
508
+ const recentList = recent.map((m, i) => `\u65B0${i + 1}. ${m.content.slice(0, 80)}`).join("\n");
509
+ const olderList = older.map((m, i) => `\u65E7${i + 1}. ${m.content.slice(0, 80)}`).join("\n");
510
+ spawnCLI(
511
+ `\u4EE5\u4E0B\u662F\u540C\u7C7B\u578B(${scope})\u7684\u65B0\u65E7\u8BB0\u5FC6\uFF0C\u68C0\u67E5\u662F\u5426\u6709\u77DB\u76FE\uFF08\u540C\u4E00\u4EF6\u4E8B\u8BF4\u6CD5\u4E0D\u540C\u3001\u524D\u540E\u4E0D\u4E00\u81F4\uFF09\u3002
512
+
513
+ \u6700\u8FD1\u7684\u8BB0\u5FC6:
514
+ ${recentList}
515
+
516
+ \u8F83\u65E9\u7684\u8BB0\u5FC6:
517
+ ${olderList}
518
+
519
+ \u5982\u679C\u6709\u77DB\u76FE\uFF0C\u8F93\u51FA\u683C\u5F0F: "\u65E7N \u4E0E \u65B0M \u77DB\u76FE: \u539F\u56E0"\uFF08\u53EF\u591A\u6761\uFF09
520
+ \u5982\u679C\u6CA1\u6709\u77DB\u76FE\uFF0C\u56DE\u7B54"\u65E0"`,
521
+ (output) => {
522
+ if (!output || output.includes("\u65E0")) return;
523
+ const lines = output.split("\n").filter((l) => l.includes("\u77DB\u76FE"));
524
+ let expired = 0;
525
+ for (const line of lines) {
526
+ const oldMatch = line.match(/旧(\d+)/);
527
+ if (oldMatch) {
528
+ const idx = parseInt(oldMatch[1]) - 1;
529
+ if (idx >= 0 && idx < older.length) {
530
+ const memIdx = memoryState.memories.findIndex((m) => m.content === older[idx].content && m.ts === older[idx].ts);
531
+ if (memIdx >= 0) {
532
+ memoryState.memories[memIdx].scope = "expired";
533
+ expired++;
534
+ }
535
+ }
536
+ }
537
+ }
538
+ if (expired > 0) {
539
+ saveMemories();
540
+ console.log(`[cc-soul][contradiction] expired ${expired} contradicted memories in scope "${scope}"`);
541
+ }
542
+ }
543
+ );
544
+ }
545
+ }
546
+ __name(scanForContradictions, "scanForContradictions");
547
+ let lastPredictionTs = 0;
548
+ let cachedPrediction = [];
549
+ function predictiveRecall(userId, channelId) {
550
+ const now = Date.now();
551
+ const results = [...cachedPrediction];
552
+ cachedPrediction = [];
553
+ return results;
554
+ }
555
+ __name(predictiveRecall, "predictiveRecall");
556
+ function generatePrediction(recentTopics, userId) {
557
+ if (recentTopics.length === 0) return;
558
+ if (Date.now() - lastPredictionTs < 6e4) return;
559
+ lastPredictionTs = Date.now();
560
+ const topicStr = recentTopics.slice(-3).join("\u3001");
561
+ const candidates = memoryState.memories.filter((m) => {
562
+ if (m.scope === "expired" || m.scope === "proactive") return false;
563
+ const content = m.content.toLowerCase();
564
+ return recentTopics.some((t) => content.includes(t.toLowerCase()));
565
+ }).sort((a, b) => b.ts - a.ts).slice(0, 5);
566
+ if (candidates.length > 0) {
567
+ cachedPrediction = candidates.map((m) => m.content);
568
+ console.log(`[cc-soul][predictive] pre-loaded ${candidates.length} memories for topics: ${topicStr}`);
569
+ }
570
+ }
571
+ __name(generatePrediction, "generatePrediction");
572
+ function triggerSessionSummary(recentTurns) {
573
+ const now = Date.now();
574
+ if (now - lastSessionSummaryTs < SESSION_SUMMARY_COOLDOWN) return;
575
+ if (memoryState.chatHistory.length < 3) return;
576
+ lastSessionSummaryTs = now;
577
+ const turns = memoryState.chatHistory.slice(-(recentTurns || 10));
578
+ const conversation = turns.map((t) => `\u7528\u6237: ${t.user.slice(0, 200)}
579
+ \u52A9\u624B: ${t.assistant.slice(0, 200)}`).join("\n\n");
580
+ spawnCLI(
581
+ `\u4EE5\u4E0B\u662F\u4E00\u6BB5\u5B8C\u6574\u5BF9\u8BDD\uFF0C\u8BF7\u5199\u4E00\u6761\u9AD8\u8D28\u91CF\u7684\u4F1A\u8BDD\u6458\u8981\uFF082-3\u53E5\u8BDD\uFF09\uFF0C\u5305\u542B\uFF1A
582
+ 1. \u8BA8\u8BBA\u4E86\u4EC0\u4E48\u4E3B\u9898
583
+ 2. \u5173\u952E\u7ED3\u8BBA\u6216\u51B3\u5B9A
584
+ 3. \u662F\u5426\u6709\u9057\u7559\u95EE\u9898
585
+ \u4E0D\u8981\u8BF4"\u7528\u6237\u548C\u52A9\u624B\u8BA8\u8BBA\u4E86..."\uFF0C\u76F4\u63A5\u5199\u5185\u5BB9\u3002
586
+
587
+ ${conversation}`,
588
+ (output) => {
589
+ if (output && output.length > 20) {
590
+ addMemory(`[\u4F1A\u8BDD\u6458\u8981] ${output.slice(0, 300)}`, "consolidated", void 0, "global");
591
+ console.log(`[cc-soul][session-summary] ${output.slice(0, 80)}`);
592
+ }
593
+ }
594
+ );
595
+ }
596
+ __name(triggerSessionSummary, "triggerSessionSummary");
597
+ let lastNetworkCleanup = 0;
598
+ const NETWORK_CLEANUP_COOLDOWN = 24 * 36e5;
599
+ function cleanupNetworkKnowledge() {
600
+ const now = Date.now();
601
+ if (now - lastNetworkCleanup < NETWORK_CLEANUP_COOLDOWN) return;
602
+ lastNetworkCleanup = now;
603
+ let expired = 0;
604
+ let downgraded = 0;
605
+ for (const mem of memoryState.memories) {
606
+ if (!mem.content.startsWith("[\u7F51\u7EDC\u77E5\u8BC6")) continue;
607
+ if (mem.scope === "expired") continue;
608
+ const ageDays = (now - mem.ts) / 864e5;
609
+ if (ageDays > 90 && (!mem.tags || mem.tags.length === 0)) {
610
+ mem.scope = "expired";
611
+ expired++;
612
+ continue;
613
+ }
614
+ if (mem.content.includes("\u4F4E\u53EF\u4FE1") && ageDays > 30) {
615
+ mem.scope = "expired";
616
+ expired++;
617
+ continue;
618
+ }
619
+ if (mem.content.includes("\u5F85\u9A8C\u8BC1") && ageDays > 60) {
620
+ mem.scope = "expired";
621
+ downgraded++;
622
+ continue;
623
+ }
624
+ }
625
+ if (expired > 0 || downgraded > 0) {
626
+ saveMemories();
627
+ console.log(`[cc-soul][network-cleanup] expired ${expired}, downgraded ${downgraded} network memories`);
628
+ }
629
+ }
630
+ __name(cleanupNetworkKnowledge, "cleanupNetworkKnowledge");
631
+ function resolveNetworkConflicts() {
632
+ const now = Date.now();
633
+ const localFacts = memoryState.memories.filter(
634
+ (m) => !m.content.startsWith("[\u7F51\u7EDC\u77E5\u8BC6") && (m.scope === "fact" || m.scope === "consolidated") && m.scope !== "expired"
635
+ );
636
+ const networkFacts = memoryState.memories.filter(
637
+ (m) => m.content.startsWith("[\u7F51\u7EDC\u77E5\u8BC6") && m.scope !== "expired"
638
+ );
639
+ if (localFacts.length === 0 || networkFacts.length === 0) return;
640
+ let resolved = 0;
641
+ for (const net of networkFacts) {
642
+ const netWords = new Set(
643
+ (net.content.match(/[\u4e00-\u9fff]{2,}|[a-z]{3,}/gi) || []).map((w) => w.toLowerCase())
644
+ );
645
+ for (const local of localFacts) {
646
+ const localWords = (local.content.match(/[\u4e00-\u9fff]{2,}|[a-z]{3,}/gi) || []).map((w) => w.toLowerCase());
647
+ const overlap = localWords.filter((w) => netWords.has(w)).length;
648
+ if (overlap >= 3 && local.content !== net.content.replace(/^\[网络知识[||][^\]]*\]\s*/, "")) {
649
+ if (local.ts > net.ts) {
650
+ net.scope = "expired";
651
+ resolved++;
652
+ break;
653
+ }
654
+ }
655
+ }
656
+ }
657
+ if (resolved > 0) {
658
+ saveMemories();
659
+ console.log(`[cc-soul][network-conflicts] resolved ${resolved} network vs local conflicts (local wins)`);
660
+ }
661
+ }
662
+ __name(resolveNetworkConflicts, "resolveNetworkConflicts");
663
+ export {
664
+ addMemory,
665
+ addMemoryWithEmotion,
666
+ addToHistory,
667
+ batchTagUntaggedMemories,
668
+ buildHistoryContext,
669
+ cleanupNetworkKnowledge,
670
+ consolidateFragments,
671
+ consolidateMemories,
672
+ executeMemoryCommands,
673
+ generatePrediction,
674
+ getAssociativeRecall,
675
+ getPendingSearchResults,
676
+ invalidateIDF,
677
+ loadMemories,
678
+ memoryState,
679
+ parseMemoryCommands,
680
+ predictiveRecall,
681
+ recall,
682
+ recallFeedbackLoop,
683
+ resolveNetworkConflicts,
684
+ saveMemories,
685
+ scanForContradictions,
686
+ triggerAssociativeRecall,
687
+ triggerSessionSummary
688
+ };
@@ -0,0 +1 @@
1
+ function checkAugmentConsistency(t){if(t.length<2)return"";const n=[],s=t.map(t=>t.content.toLowerCase()),e=[["简洁","详细"],["简短","展开"],["不擅长","擅长"],["不确定","确定"],["谨慎","果断"],["先共情","直接给方案"]];for(const[t,o]of e){const e=s.some(n=>n.includes(t)),u=s.some(t=>t.includes(o));e&&u&&n.push(`"${t}" vs "${o}"`)}const o=s.some(t=>t.includes("不擅长")||t.includes("说不确定")),u=s.some(t=>t.includes("擅长领域")||t.includes("高信心"));o&&u&&n.push('计划说"不擅长"但知识边界说"高信心"——以最新计划为准');const f=s.filter(t=>t.includes("[相关记忆]")||t.includes("[确定性知识]")),c=s.filter(t=>t.includes("[注意规则]")||t.includes("[行动计划"));if(f.length>0&&c.length>0)for(const t of f)for(const s of c){const e=new Set(t.match(/[\u4e00-\u9fff]{2,}/g)||[]),o=new Set(s.match(/[\u4e00-\u9fff]{2,}/g)||[]);if([...e].filter(t=>o.has(t)).length>=3){n.push("记忆和规则可能涉及同一话题——以规则/计划为准");break}}return 0===n.length?"":`[元认知警告] 注入上下文有潜在冲突: ${n.join(";")}。请以最新信息和规则为准。`}let lastAugmentSnapshot=[];function snapshotAugments(t){lastAugmentSnapshot=[...t]}function getLastAugmentSnapshot(){return lastAugmentSnapshot}export{checkAugmentConsistency,getLastAugmentSnapshot,snapshotAugments};