@crewx/memory 0.1.23-rc.25 ā 0.1.23-rc.251
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 +1 -1
- package/SKILL.md +64 -64
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +3 -340
- package/dist/src/engine.d.ts +25 -1
- package/dist/src/engine.js +94 -1009
- package/dist/src/mindmap.d.ts +1 -1
- package/dist/src/mindmap.js +18 -518
- package/dist/src/parser.d.ts +1 -1
- package/dist/src/parser.js +1 -7
- package/dist/src/summarizer-failure.d.ts +8 -0
- package/dist/src/summarizer-failure.js +1 -0
- package/dist/src/types.d.ts +1 -1
- package/dist/src/types.js +1 -15
- package/package.json +4 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/src/engine.d.ts.map +0 -1
- package/dist/src/engine.js.map +0 -1
- package/dist/src/mindmap.d.ts.map +0 -1
- package/dist/src/mindmap.js.map +0 -1
- package/dist/src/parser.d.ts.map +0 -1
- package/dist/src/parser.js.map +0 -1
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js.map +0 -1
package/dist/src/engine.js
CHANGED
|
@@ -1,1014 +1,99 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.CATEGORIES = exports.MemoryEngine = void 0;
|
|
7
|
-
exports.getMemoryConfig = getMemoryConfig;
|
|
8
|
-
exports.createMemoryEngine = createMemoryEngine;
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const os_1 = __importDefault(require("os"));
|
|
12
|
-
const child_process_1 = require("child_process");
|
|
13
|
-
const nanoid_1 = require("nanoid");
|
|
14
|
-
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
15
|
-
const parser_1 = require("./parser");
|
|
16
|
-
const knowledge_core_1 = require("@crewx/knowledge-core");
|
|
17
|
-
const CREWX_CLI = process.env.CREWX_CLI || 'npx crewx';
|
|
18
|
-
function parseCrewxResponse(raw) {
|
|
19
|
-
const responseMatch = raw.match(/š Response:\s*ā+\s*([\s\S]*?)(?=š Working Directory|$)/);
|
|
20
|
-
if (responseMatch?.[1]) {
|
|
21
|
-
const captured = responseMatch[1].trim();
|
|
22
|
-
const capturedFirstLine = captured.split('\n')[0]?.trim() ?? '';
|
|
23
|
-
if (!capturedFirstLine.startsWith('{')) {
|
|
24
|
-
return captured;
|
|
25
|
-
}
|
|
26
|
-
return parseJsonlContent(captured);
|
|
27
|
-
}
|
|
28
|
-
return parseJsonlContent(raw);
|
|
29
|
-
}
|
|
30
|
-
function parseJsonlContent(raw) {
|
|
31
|
-
const lines = raw.split('\n').filter((l) => l.trim().startsWith('{'));
|
|
32
|
-
let assistantText = '';
|
|
33
|
-
let hasAssistantMessage = false;
|
|
34
|
-
for (const line of lines) {
|
|
35
|
-
try {
|
|
36
|
-
const obj = JSON.parse(line);
|
|
37
|
-
if (obj.type === 'message' && obj.role === 'assistant') {
|
|
38
|
-
hasAssistantMessage = true;
|
|
39
|
-
if (obj.content)
|
|
40
|
-
assistantText += obj.content;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
catch (_e) {
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
if (hasAssistantMessage)
|
|
47
|
-
return assistantText.trim() || null;
|
|
48
|
-
const trimmed = raw.trim();
|
|
49
|
-
if (trimmed.length === 0)
|
|
50
|
-
return null;
|
|
51
|
-
const firstLine = trimmed.split('\n')[0]?.trim() ?? '';
|
|
52
|
-
if (firstLine.startsWith('{'))
|
|
53
|
-
return null;
|
|
54
|
-
return trimmed;
|
|
55
|
-
}
|
|
56
|
-
function getProjectRoot() {
|
|
57
|
-
try {
|
|
58
|
-
return (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
return process.cwd();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
function getMemoryConfig() {
|
|
65
|
-
try {
|
|
66
|
-
const configPath = process.env.CREWX_CONFIG ?? path_1.default.join(getProjectRoot(), 'crewx.yaml');
|
|
67
|
-
const raw = fs_1.default.readFileSync(configPath, 'utf8');
|
|
68
|
-
const config = js_yaml_1.default.load(raw);
|
|
69
|
-
const settings = config?.settings;
|
|
70
|
-
return settings?.memory ?? {};
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
return {};
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
class MemoryEngine {
|
|
77
|
-
constructor(config = {}) {
|
|
78
|
-
const yamlConfig = getMemoryConfig();
|
|
79
|
-
this.dataDir =
|
|
80
|
-
config.dataDir ??
|
|
81
|
-
process.env['MEMORY_DATA_DIR'] ??
|
|
82
|
-
path_1.default.resolve(process.cwd(), 'memory');
|
|
83
|
-
this.summaryDays =
|
|
84
|
-
config.summaryDays ??
|
|
85
|
-
(yamlConfig.summary_days != null ? Number(yamlConfig.summary_days) : undefined) ??
|
|
86
|
-
(parseInt(process.env['MEMORY_SUMMARY_DAYS'] ?? '', 10) || 7);
|
|
87
|
-
this.recentDays =
|
|
88
|
-
config.recentDays ??
|
|
89
|
-
(yamlConfig.recent_days != null ? Number(yamlConfig.recent_days) : undefined) ??
|
|
90
|
-
(parseInt(process.env['MEMORY_RECENT_DAYS'] ?? '', 10) || 30);
|
|
91
|
-
this.shortTermHours =
|
|
92
|
-
config.shortTermHours ??
|
|
93
|
-
(yamlConfig.short_term_hours != null ? Number(yamlConfig.short_term_hours) : undefined) ??
|
|
94
|
-
24;
|
|
95
|
-
this.summarizerAgent =
|
|
96
|
-
config.summarizerAgent ??
|
|
97
|
-
yamlConfig.summarizer_agent ??
|
|
98
|
-
'@memory_summarizer';
|
|
99
|
-
this.searcherAgent =
|
|
100
|
-
config.searcherAgent ??
|
|
101
|
-
yamlConfig.searcher_agent ??
|
|
102
|
-
'@memory_searcher';
|
|
103
|
-
this.summarizerTimeout =
|
|
104
|
-
config.summarizerTimeout ??
|
|
105
|
-
(yamlConfig.summarizer_timeout != null ? Number(yamlConfig.summarizer_timeout) : undefined) ??
|
|
106
|
-
60000;
|
|
107
|
-
this.searcherTimeout =
|
|
108
|
-
config.searcherTimeout ??
|
|
109
|
-
(yamlConfig.searcher_timeout != null ? Number(yamlConfig.searcher_timeout) : undefined) ??
|
|
110
|
-
300000;
|
|
111
|
-
}
|
|
112
|
-
getAgentDir(agentId) {
|
|
113
|
-
return path_1.default.join(this.dataDir, agentId);
|
|
114
|
-
}
|
|
115
|
-
getEntriesDir(agentId) {
|
|
116
|
-
return path_1.default.join(this.getAgentDir(agentId), 'entries');
|
|
117
|
-
}
|
|
118
|
-
getSummaryPath(agentId) {
|
|
119
|
-
return path_1.default.join(this.getAgentDir(agentId), 'summary.md');
|
|
120
|
-
}
|
|
121
|
-
getDirtySummaryPath(agentId) {
|
|
122
|
-
return path_1.default.join(this.getAgentDir(agentId), '.dirty-summary');
|
|
123
|
-
}
|
|
124
|
-
ensureDir(dir) {
|
|
125
|
-
if (!fs_1.default.existsSync(dir)) {
|
|
126
|
-
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
generateId() {
|
|
130
|
-
return (0, nanoid_1.nanoid)(6);
|
|
131
|
-
}
|
|
132
|
-
getToday() {
|
|
133
|
-
return new Date().toISOString().slice(0, 10);
|
|
134
|
-
}
|
|
135
|
-
getLocalISO8601() {
|
|
136
|
-
const now = new Date();
|
|
137
|
-
const offset = -now.getTimezoneOffset();
|
|
138
|
-
const sign = offset >= 0 ? '+' : '-';
|
|
139
|
-
const offsetHours = String(Math.floor(Math.abs(offset) / 60)).padStart(2, '0');
|
|
140
|
-
const offsetMins = String(Math.abs(offset) % 60).padStart(2, '0');
|
|
141
|
-
const y = now.getFullYear();
|
|
142
|
-
const m = String(now.getMonth() + 1).padStart(2, '0');
|
|
143
|
-
const d = String(now.getDate()).padStart(2, '0');
|
|
144
|
-
const h = String(now.getHours()).padStart(2, '0');
|
|
145
|
-
const min = String(now.getMinutes()).padStart(2, '0');
|
|
146
|
-
const s = String(now.getSeconds()).padStart(2, '0');
|
|
147
|
-
return `${y}-${m}-${d}T${h}:${min}:${s}${sign}${offsetHours}:${offsetMins}`;
|
|
148
|
-
}
|
|
149
|
-
getPrimaryTag(entry) {
|
|
150
|
-
return (entry.tags?.[0]) ?? 'general';
|
|
151
|
-
}
|
|
152
|
-
toEntryTimestamp(entry) {
|
|
153
|
-
if (entry['created_at'])
|
|
154
|
-
return String(entry['created_at']);
|
|
155
|
-
const d = entry['date'];
|
|
156
|
-
const dateStr = d && typeof d === 'object' && typeof d.toISOString === 'function' ? d.toISOString().slice(0, 10) : String(d ?? '');
|
|
157
|
-
if (dateStr.includes('T'))
|
|
158
|
-
return dateStr;
|
|
159
|
-
return dateStr + 'T09:00:00+09:00';
|
|
160
|
-
}
|
|
161
|
-
markSummaryDirty(agentId) {
|
|
162
|
-
this.ensureDir(this.getAgentDir(agentId));
|
|
163
|
-
fs_1.default.writeFileSync(this.getDirtySummaryPath(agentId), String(Date.now()), 'utf-8');
|
|
164
|
-
}
|
|
165
|
-
loadAllEntries(agentId) {
|
|
166
|
-
const entriesDir = this.getEntriesDir(agentId);
|
|
167
|
-
if (!fs_1.default.existsSync(entriesDir))
|
|
168
|
-
return [];
|
|
169
|
-
const files = fs_1.default.readdirSync(entriesDir).filter((f) => f.endsWith('.md'));
|
|
170
|
-
const entries = [];
|
|
171
|
-
for (const file of files) {
|
|
172
|
-
const raw = fs_1.default.readFileSync(path_1.default.join(entriesDir, file), 'utf-8');
|
|
173
|
-
const { data, content } = (0, parser_1.parseFrontmatter)(raw);
|
|
174
|
-
const bodyText = content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
175
|
-
const hasBody = Boolean(bodyText) && bodyText !== '(Add details here)';
|
|
176
|
-
const entry = data;
|
|
177
|
-
entries.push({ ...entry, file, hasBody });
|
|
178
|
-
}
|
|
179
|
-
entries.sort((a, b) => this.toEntryTimestamp(b).localeCompare(this.toEntryTimestamp(a)));
|
|
180
|
-
return entries;
|
|
181
|
-
}
|
|
182
|
-
findEntryById(agentId, memoryId) {
|
|
183
|
-
const entriesDir = this.getEntriesDir(agentId);
|
|
184
|
-
if (!fs_1.default.existsSync(entriesDir))
|
|
185
|
-
return null;
|
|
186
|
-
const files = fs_1.default.readdirSync(entriesDir).filter((f) => f.endsWith('.md'));
|
|
187
|
-
for (const file of files) {
|
|
188
|
-
const filePath = path_1.default.join(entriesDir, file);
|
|
189
|
-
const raw = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
190
|
-
const { data, content } = (0, parser_1.parseFrontmatter)(raw);
|
|
191
|
-
if (data.id === memoryId) {
|
|
192
|
-
return {
|
|
193
|
-
file,
|
|
194
|
-
filePath,
|
|
195
|
-
data: data,
|
|
196
|
-
content,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return null;
|
|
201
|
-
}
|
|
202
|
-
save(agentId, summary, category = 'general', options = {}) {
|
|
203
|
-
this.ensureDir(this.getEntriesDir(agentId));
|
|
204
|
-
const id = this.generateId();
|
|
205
|
-
const dateTime = this.getLocalISO8601();
|
|
206
|
-
const tags = Array.isArray(options.tags)
|
|
207
|
-
? options.tags
|
|
208
|
-
: options.tags ? options.tags.split(',').map((t) => t.trim()) : [];
|
|
209
|
-
const frontmatter = (0, parser_1.stringifyFrontmatter)({
|
|
210
|
-
id,
|
|
211
|
-
type: 'memory',
|
|
212
|
-
date: dateTime,
|
|
213
|
-
category,
|
|
214
|
-
tags,
|
|
215
|
-
summary,
|
|
216
|
-
...(options.important ? { important: true } : {}),
|
|
217
|
-
});
|
|
218
|
-
const filename = `${id}.md`;
|
|
219
|
-
const filepath = path_1.default.join(this.getEntriesDir(agentId), filename);
|
|
220
|
-
const body = options.body ?? '(Add details here)';
|
|
221
|
-
const content = `${frontmatter}\n\n# ${summary}\n\n${body}\n`;
|
|
222
|
-
fs_1.default.writeFileSync(filepath, content, 'utf-8');
|
|
223
|
-
console.log(`ā
Saved: data/${agentId}/entries/${filename}`);
|
|
224
|
-
console.log(` ID: ${id}`);
|
|
225
|
-
console.log(` Tags: ${tags.join(', ') || '(none)'}`);
|
|
226
|
-
console.log(` Category: ${category}`);
|
|
227
|
-
try {
|
|
228
|
-
this.markSummaryDirty(agentId);
|
|
229
|
-
console.log(` š summary.md update scheduled`);
|
|
230
|
-
}
|
|
231
|
-
catch (_e) {
|
|
232
|
-
}
|
|
233
|
-
const saveCtx = {
|
|
234
|
-
commandId: 'memory.save',
|
|
235
|
-
params: { agentId, tags: tags.join(',') },
|
|
236
|
-
resultTags: tags.slice(0, 3),
|
|
237
|
-
resultCount: 1,
|
|
238
|
-
};
|
|
239
|
-
const saveActions = (0, knowledge_core_1.generateNextActions)(saveCtx);
|
|
240
|
-
const saveBlock = (0, knowledge_core_1.formatActions)(saveActions);
|
|
241
|
-
if (saveBlock)
|
|
242
|
-
console.log(saveBlock);
|
|
243
|
-
}
|
|
244
|
-
index(agentId, options = {}) {
|
|
245
|
-
const summaryPath = this.getSummaryPath(agentId);
|
|
246
|
-
const entries = this.loadAllEntries(agentId);
|
|
247
|
-
const topics = [...new Set(entries.map((e) => this.getPrimaryTag(e)))];
|
|
248
|
-
if (options.recent) {
|
|
249
|
-
const days = parseInt(String(options.recent), 10) || this.recentDays;
|
|
250
|
-
this.recent(agentId, days);
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
const cutoff24h = new Date(Date.now() - this.shortTermHours * 60 * 60 * 1000).toISOString();
|
|
254
|
-
const shortTermEntries = entries.filter((e) => this.toEntryTimestamp(e) >= cutoff24h);
|
|
255
|
-
console.log(`# ${agentId} memory summary\n`);
|
|
256
|
-
const summaryLastUpdated = fs_1.default.existsSync(summaryPath)
|
|
257
|
-
? (0, parser_1.parseFrontmatter)(fs_1.default.readFileSync(summaryPath, 'utf-8')).data.last_updated ?? '-'
|
|
258
|
-
: '-';
|
|
259
|
-
console.log(`> ${entries.length} items | topics ${topics.length} items | updated: ${summaryLastUpdated}`);
|
|
260
|
-
if (options.full) {
|
|
261
|
-
const byCat = {};
|
|
262
|
-
for (const entry of entries) {
|
|
263
|
-
const cat = entry.category ?? 'general';
|
|
264
|
-
byCat[cat] = (byCat[cat] ?? 0) + 1;
|
|
265
|
-
}
|
|
266
|
-
const catSummary = Object.entries(byCat)
|
|
267
|
-
.sort((a, b) => b[1] - a[1])
|
|
268
|
-
.map(([cat, count]) => `${cat}:${count}`)
|
|
269
|
-
.join(' ');
|
|
270
|
-
console.log(`> š ${catSummary}`);
|
|
271
|
-
}
|
|
272
|
-
console.log('');
|
|
273
|
-
if (shortTermEntries.length > 0) {
|
|
274
|
-
const sortedShortTerm = [...shortTermEntries].sort((a, b) => {
|
|
275
|
-
const aImp = a.important ? 1 : 0;
|
|
276
|
-
const bImp = b.important ? 1 : 0;
|
|
277
|
-
if (aImp !== bImp)
|
|
278
|
-
return bImp - aImp;
|
|
279
|
-
return this.toEntryTimestamp(b).localeCompare(this.toEntryTimestamp(a));
|
|
280
|
-
});
|
|
281
|
-
console.log(`## š„ last Task (24h)\n`);
|
|
282
|
-
for (const e of sortedShortTerm) {
|
|
283
|
-
const star = e.important ? 'ā ' : '';
|
|
284
|
-
console.log(`- ${star}[${e.id}] ${e.summary} (${this.getPrimaryTag(e)})`);
|
|
285
|
-
}
|
|
286
|
-
console.log('');
|
|
287
|
-
}
|
|
288
|
-
if (options.full) {
|
|
289
|
-
if (fs_1.default.existsSync(summaryPath)) {
|
|
290
|
-
const raw = fs_1.default.readFileSync(summaryPath, 'utf-8');
|
|
291
|
-
const { content: summaryBody } = (0, parser_1.parseFrontmatter)(raw);
|
|
292
|
-
if (entries.length > 0) {
|
|
293
|
-
const newestDate = entries[0]?.date;
|
|
294
|
-
const oldestDate = entries[entries.length - 1]?.date;
|
|
295
|
-
const newest = newestDate ? new Date(`${newestDate}T00:00:00Z`) : null;
|
|
296
|
-
const oldest = oldestDate ? new Date(`${oldestDate}T00:00:00Z`) : null;
|
|
297
|
-
if (newest &&
|
|
298
|
-
oldest &&
|
|
299
|
-
!Number.isNaN(newest.getTime()) &&
|
|
300
|
-
!Number.isNaN(oldest.getTime())) {
|
|
301
|
-
const msPerDay = 24 * 60 * 60 * 1000;
|
|
302
|
-
const diffDays = Math.max(1, Math.floor((newest.getTime() - oldest.getTime()) / msPerDay) + 1);
|
|
303
|
-
const toMD = (d) => `${String(d.getMonth() + 1).padStart(2, '0')}/${String(d.getDate()).padStart(2, '0')}`;
|
|
304
|
-
console.log('---');
|
|
305
|
-
console.log(`š Topic summary (${toMD(oldest)} ~ ${toMD(newest)}, ${diffDays} days)\n`);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
console.log(summaryBody.trim());
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
for (const tag of topics.sort()) {
|
|
312
|
-
const count = entries.filter((e) => this.getPrimaryTag(e) === tag).length;
|
|
313
|
-
console.log(`- **${tag}** (${count} items)`);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
const commandId = options.full ? 'memory.index-full' : 'memory.index';
|
|
318
|
-
const ctx = {
|
|
319
|
-
commandId,
|
|
320
|
-
params: { agentId },
|
|
321
|
-
resultTags: topics.slice(0, 3),
|
|
322
|
-
resultIds: entries.slice(0, 3).map(e => e.id),
|
|
323
|
-
resultCount: entries.length,
|
|
324
|
-
};
|
|
325
|
-
const actions = (0, knowledge_core_1.generateNextActions)(ctx);
|
|
326
|
-
const block = (0, knowledge_core_1.formatActions)(actions);
|
|
327
|
-
if (block)
|
|
328
|
-
console.log(block);
|
|
329
|
-
}
|
|
330
|
-
generateIndexContent(agentId) {
|
|
331
|
-
const entries = this.loadAllEntries(agentId);
|
|
332
|
-
const today = this.getToday();
|
|
333
|
-
const cutoff = new Date(Date.now() - this.recentDays * 24 * 60 * 60 * 1000)
|
|
334
|
-
.toISOString()
|
|
335
|
-
.slice(0, 10);
|
|
336
|
-
const byCategory = {};
|
|
337
|
-
const recent = [];
|
|
338
|
-
for (const entry of entries) {
|
|
339
|
-
if (entry.date >= cutoff)
|
|
340
|
-
recent.push(entry);
|
|
341
|
-
const category = entry.category ?? 'general';
|
|
342
|
-
if (!byCategory[category])
|
|
343
|
-
byCategory[category] = [];
|
|
344
|
-
byCategory[category].push(entry);
|
|
345
|
-
}
|
|
346
|
-
let md = `# ${agentId} memory\n\n`;
|
|
347
|
-
md += `> Last Updated: ${today}\n`;
|
|
348
|
-
md += `> Total: ${entries.length} memories\n\n`;
|
|
349
|
-
md += `## š„ Last ${this.recentDays} days\n\n`;
|
|
350
|
-
if (recent.length === 0) {
|
|
351
|
-
md += `(none)\n\n`;
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
md += `| ID | Date | Category | Summary | Details |\n`;
|
|
355
|
-
md += `|----|------|----------|------|------|\n`;
|
|
356
|
-
for (const entry of recent) {
|
|
357
|
-
const detailIcon = entry.hasBody ? 'š' : '-';
|
|
358
|
-
md += `| ${entry.id} | ${entry.date} | ${entry.category ?? '-'} | [${entry.summary}](entries/${entry.file}) | ${detailIcon} |\n`;
|
|
359
|
-
}
|
|
360
|
-
md += `\n`;
|
|
361
|
-
}
|
|
362
|
-
md += `## š·ļø By category\n\n`;
|
|
363
|
-
for (const [category, items] of Object.entries(byCategory).sort()) {
|
|
364
|
-
md += `- **${category}**: ${items.length} items\n`;
|
|
365
|
-
}
|
|
366
|
-
const olderCount = entries.length - recent.length;
|
|
367
|
-
const oldestDate = entries[entries.length - 1]?.date ?? '-';
|
|
368
|
-
const newestDate = entries[0]?.date ?? '-';
|
|
369
|
-
md += `\n## š Stats\n\n`;
|
|
370
|
-
md += `- Total: ${entries.length} items (${oldestDate} ~ ${newestDate})\n`;
|
|
371
|
-
md += `- Last ${this.recentDays} days: ${recent.length} items\n`;
|
|
372
|
-
if (olderCount > 0)
|
|
373
|
-
md += `- Older than ${this.recentDays} days: ${olderCount} items\n`;
|
|
374
|
-
return md;
|
|
375
|
-
}
|
|
376
|
-
topic(agentId, topicName) {
|
|
377
|
-
const entries = this.loadAllEntries(agentId);
|
|
378
|
-
const filtered = entries.filter((e) => this.getPrimaryTag(e) === topicName);
|
|
379
|
-
if (filtered.length === 0) {
|
|
380
|
-
console.log(`No memories found for topic '${topicName}'.`);
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
console.log(`## š Topic: ${topicName} (${filtered.length} items)\n`);
|
|
384
|
-
for (const entry of filtered) {
|
|
385
|
-
console.log(`### [${entry.date}] ${entry.summary}`);
|
|
386
|
-
console.log(`- File: data/${agentId}/entries/${entry.file}`);
|
|
387
|
-
console.log(`- Category: ${entry.category ?? '-'}`);
|
|
388
|
-
console.log(`- Tags: ${(entry.tags ?? []).join(', ') || '-'}`);
|
|
389
|
-
console.log('');
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
recent(agentId, days = 30) {
|
|
393
|
-
const entries = this.loadAllEntries(agentId);
|
|
394
|
-
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
|
|
395
|
-
.toISOString()
|
|
396
|
-
.slice(0, 10);
|
|
397
|
-
const filtered = entries.filter((e) => e.date >= cutoff);
|
|
398
|
-
if (filtered.length === 0) {
|
|
399
|
-
console.log(`No memories found in the last ${days} days.`);
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
console.log(`## š„ Last ${days} days (${filtered.length} items)\n`);
|
|
403
|
-
for (const entry of filtered) {
|
|
404
|
-
const detailIcon = entry.hasBody ? ' š' : '';
|
|
405
|
-
const timestamp = String(entry.date).includes('T')
|
|
406
|
-
? String(entry.date).slice(0, 16).replace('T', ' ')
|
|
407
|
-
: entry.date;
|
|
408
|
-
console.log(`[${entry.id}] [${timestamp}] [${entry.category ?? '-'}] ${entry.summary}${detailIcon}`);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
find(agentId, keyword) {
|
|
412
|
-
const entriesDir = this.getEntriesDir(agentId);
|
|
413
|
-
if (!fs_1.default.existsSync(entriesDir)) {
|
|
414
|
-
console.log('No memories found.');
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
const files = fs_1.default.readdirSync(entriesDir).filter((f) => f.endsWith('.md'));
|
|
418
|
-
const entries = [];
|
|
419
|
-
for (const file of files) {
|
|
420
|
-
const content = fs_1.default.readFileSync(path_1.default.join(entriesDir, file), 'utf-8');
|
|
421
|
-
const { data, content: body } = (0, parser_1.parseFrontmatter)(content);
|
|
422
|
-
const entry = data;
|
|
423
|
-
entries.push({ ...entry, file, body });
|
|
424
|
-
}
|
|
425
|
-
if (entries.length === 0) {
|
|
426
|
-
console.log('No memories found.');
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
const kcEntries = entries;
|
|
430
|
-
const results = (0, knowledge_core_1.searchEntries)(kcEntries, keyword, { summary: 3, tags: 2, body: 1 });
|
|
431
|
-
if (results.length === 0) {
|
|
432
|
-
console.log(`No related memories found for '${keyword}'.`);
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
console.log(`## š Search results: "${keyword}" (${results.length} items, sorted by BM25 score)\n`);
|
|
436
|
-
for (const entry of results) {
|
|
437
|
-
const score = entry._score != null ? ` [${entry._score}pts]` : '';
|
|
438
|
-
console.log(`[${entry.id}] [${entry.date}] [${this.getPrimaryTag(entry)}]${score} ${entry.summary}`);
|
|
439
|
-
}
|
|
440
|
-
const resultTags = [...new Set(results.flatMap(e => e.tags ?? []))].slice(0, 3);
|
|
441
|
-
const resultIds = results.slice(0, 3).map(e => e.id);
|
|
442
|
-
const findCtx = {
|
|
443
|
-
commandId: 'memory.find',
|
|
444
|
-
params: { agentId, query: keyword },
|
|
445
|
-
resultTags,
|
|
446
|
-
resultIds,
|
|
447
|
-
resultCount: results.length,
|
|
448
|
-
};
|
|
449
|
-
const findActions = (0, knowledge_core_1.generateNextActions)(findCtx);
|
|
450
|
-
const findBlock = (0, knowledge_core_1.formatActions)(findActions);
|
|
451
|
-
if (findBlock)
|
|
452
|
-
console.log(findBlock);
|
|
453
|
-
}
|
|
454
|
-
list(agentId, options = {}) {
|
|
455
|
-
const entries = this.loadAllEntries(agentId);
|
|
456
|
-
let filtered = entries;
|
|
457
|
-
if (options.category) {
|
|
458
|
-
filtered = filtered.filter(e => e.category === options.category);
|
|
459
|
-
}
|
|
460
|
-
if (options.tag) {
|
|
461
|
-
const requiredTags = options.tag.split(',').map(t => t.trim());
|
|
462
|
-
filtered = filtered.filter(e => requiredTags.every(t => (e.tags ?? []).includes(t)));
|
|
463
|
-
}
|
|
464
|
-
if (options.json) {
|
|
465
|
-
const entriesDir = this.getEntriesDir(agentId);
|
|
466
|
-
const result = filtered.map(e => {
|
|
467
|
-
let content = '';
|
|
468
|
-
if (e.file) {
|
|
469
|
-
try {
|
|
470
|
-
const raw = fs_1.default.readFileSync(path_1.default.join(entriesDir, e.file), 'utf-8');
|
|
471
|
-
const { content: rawContent } = (0, parser_1.parseFrontmatter)(raw);
|
|
472
|
-
const body = rawContent.replace(/^#[^\n]*\n*/m, '').trim();
|
|
473
|
-
if (body !== '(Add details here)')
|
|
474
|
-
content = body;
|
|
475
|
-
}
|
|
476
|
-
catch (_err) {
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
return {
|
|
480
|
-
id: e.id,
|
|
481
|
-
title: e.summary ?? '',
|
|
482
|
-
category: e.category ?? 'general',
|
|
483
|
-
tags: e.tags ?? [],
|
|
484
|
-
created_at: e.date ?? '',
|
|
485
|
-
content,
|
|
486
|
-
};
|
|
487
|
-
});
|
|
488
|
-
console.log(JSON.stringify(result, null, 2));
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
if (filtered.length === 0) {
|
|
492
|
-
const filters = [];
|
|
493
|
-
if (options.category)
|
|
494
|
-
filters.push(`category=${options.category}`);
|
|
495
|
-
if (options.tag)
|
|
496
|
-
filters.push(`tag=${options.tag}`);
|
|
497
|
-
console.log(`No memories match the filters. (${filters.join(', ') || 'all'})`);
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
console.log(`## š List: ${agentId} (${filtered.length} items)\n`);
|
|
501
|
-
for (const entry of filtered) {
|
|
502
|
-
console.log(`[${entry.id}] [${entry.date}] [${this.getPrimaryTag(entry)}] ${entry.summary}`);
|
|
503
|
-
}
|
|
504
|
-
const resultTags = [...new Set(filtered.flatMap(e => e.tags ?? []))].slice(0, 3);
|
|
505
|
-
const resultIds = filtered.slice(0, 3).map(e => e.id);
|
|
506
|
-
const listCtx = {
|
|
507
|
-
commandId: 'memory.list',
|
|
508
|
-
params: { agentId, ...(options.category ? { category: options.category } : {}) },
|
|
509
|
-
resultTags,
|
|
510
|
-
resultIds,
|
|
511
|
-
resultCount: filtered.length,
|
|
512
|
-
};
|
|
513
|
-
const listActions = (0, knowledge_core_1.generateNextActions)(listCtx);
|
|
514
|
-
const listBlock = (0, knowledge_core_1.formatActions)(listActions);
|
|
515
|
-
if (listBlock)
|
|
516
|
-
console.log(listBlock);
|
|
517
|
-
}
|
|
518
|
-
stats(agentId) {
|
|
519
|
-
const entries = this.loadAllEntries(agentId);
|
|
520
|
-
if (entries.length === 0) {
|
|
521
|
-
console.log('No memories found.');
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
const byCategory = {};
|
|
525
|
-
for (const entry of entries) {
|
|
526
|
-
const cat = entry.category ?? 'general';
|
|
527
|
-
byCategory[cat] = (byCategory[cat] ?? 0) + 1;
|
|
528
|
-
}
|
|
529
|
-
const byTag = {};
|
|
530
|
-
for (const entry of entries) {
|
|
531
|
-
for (const tag of entry.tags ?? []) {
|
|
532
|
-
byTag[tag] = (byTag[tag] ?? 0) + 1;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
console.log(`# ${agentId} Stats (${entries.length} items)\n`);
|
|
536
|
-
console.log(`## By category`);
|
|
537
|
-
for (const [cat, count] of Object.entries(byCategory).sort((a, b) => b[1] - a[1])) {
|
|
538
|
-
console.log(` ${cat}: ${count} items`);
|
|
539
|
-
}
|
|
540
|
-
console.log(`\n## Top 20 Tags`);
|
|
541
|
-
const sortedTags = Object.entries(byTag).sort((a, b) => b[1] - a[1]).slice(0, 20);
|
|
542
|
-
for (const [tag, count] of sortedTags) {
|
|
543
|
-
console.log(` ${tag}: ${count} items`);
|
|
544
|
-
}
|
|
545
|
-
const topCategories = Object.entries(byCategory).sort((a, b) => b[1] - a[1]).map(t => t[0]);
|
|
546
|
-
const statsCtx = {
|
|
547
|
-
commandId: 'memory.stats',
|
|
548
|
-
params: { agentId },
|
|
549
|
-
resultTags: topCategories.slice(0, 3),
|
|
550
|
-
resultCount: entries.length,
|
|
551
|
-
};
|
|
552
|
-
const statsActions = (0, knowledge_core_1.generateNextActions)(statsCtx);
|
|
553
|
-
const statsBlock = (0, knowledge_core_1.formatActions)(statsActions);
|
|
554
|
-
if (statsBlock)
|
|
555
|
-
console.log(statsBlock);
|
|
556
|
-
}
|
|
557
|
-
tags(agentId) {
|
|
558
|
-
const entries = this.loadAllEntries(agentId);
|
|
559
|
-
if (entries.length === 0) {
|
|
560
|
-
console.log('No memories found.');
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
const byTag = {};
|
|
564
|
-
for (const entry of entries) {
|
|
565
|
-
for (const tag of entry.tags ?? []) {
|
|
566
|
-
byTag[tag] = (byTag[tag] ?? 0) + 1;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
const sortedTags = Object.entries(byTag).sort((a, b) => b[1] - a[1]);
|
|
570
|
-
if (sortedTags.length === 0) {
|
|
571
|
-
console.log('No tags found.');
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
console.log(`## š·ļø All tags (${sortedTags.length} items)\n`);
|
|
575
|
-
for (const [tag, count] of sortedTags) {
|
|
576
|
-
console.log(` ${tag}: ${count} items`);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
get(agentId, memoryId) {
|
|
580
|
-
const entriesDir = this.getEntriesDir(agentId);
|
|
581
|
-
if (!fs_1.default.existsSync(entriesDir)) {
|
|
582
|
-
console.log('No memories found.');
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
const files = fs_1.default.readdirSync(entriesDir).filter((f) => f.endsWith('.md'));
|
|
586
|
-
for (const file of files) {
|
|
587
|
-
const filePath = path_1.default.join(entriesDir, file);
|
|
588
|
-
const raw = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
589
|
-
const { data, content } = (0, parser_1.parseFrontmatter)(raw);
|
|
590
|
-
if (data.id === memoryId) {
|
|
591
|
-
const d = data;
|
|
592
|
-
console.log(`## š ${d.summary}\n`);
|
|
593
|
-
console.log(`- ID: ${d.id}`);
|
|
594
|
-
console.log(`- Date: ${d.date}`);
|
|
595
|
-
console.log(`- Category: ${d.category ?? '-'}`);
|
|
596
|
-
console.log(`- Tags: ${(d.tags ?? []).join(', ') || '-'}`);
|
|
597
|
-
console.log(`- File: data/${agentId}/entries/${file}`);
|
|
598
|
-
console.log(`\n---\n`);
|
|
599
|
-
console.log(content.trim());
|
|
600
|
-
const graphPath = path_1.default.join(this.getAgentDir(agentId), 'graph.json');
|
|
601
|
-
if (fs_1.default.existsSync(graphPath)) {
|
|
602
|
-
try {
|
|
603
|
-
const graph = JSON.parse(fs_1.default.readFileSync(graphPath, 'utf-8'));
|
|
604
|
-
const relatedEdges = graph.edges
|
|
605
|
-
.filter((e) => e.from === memoryId || e.to === memoryId)
|
|
606
|
-
.sort((a, b) => b.weight - a.weight)
|
|
607
|
-
.slice(0, 5);
|
|
608
|
-
if (relatedEdges.length > 0) {
|
|
609
|
-
const nodeMap = {};
|
|
610
|
-
graph.nodes.forEach((n) => {
|
|
611
|
-
nodeMap[n.id] = n;
|
|
612
|
-
});
|
|
613
|
-
console.log(`\n---\n`);
|
|
614
|
-
console.log(`## š Related memories (${relatedEdges.length} items)\n`);
|
|
615
|
-
for (const edge of relatedEdges) {
|
|
616
|
-
const otherId = edge.from === memoryId ? edge.to : edge.from;
|
|
617
|
-
const other = nodeMap[otherId];
|
|
618
|
-
if (other) {
|
|
619
|
-
console.log(`[${edge.weight}] [${otherId}] ${other.summary.slice(0, 50)}...`);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
catch (_e) {
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
const getCtx = {
|
|
628
|
-
commandId: 'memory.get',
|
|
629
|
-
params: { agentId, id: memoryId },
|
|
630
|
-
resultTags: (d.tags ?? []).slice(0, 3),
|
|
631
|
-
resultIds: [memoryId],
|
|
632
|
-
resultCount: 1,
|
|
633
|
-
};
|
|
634
|
-
const getActions = (0, knowledge_core_1.generateNextActions)(getCtx);
|
|
635
|
-
const getBlock = (0, knowledge_core_1.formatActions)(getActions);
|
|
636
|
-
if (getBlock)
|
|
637
|
-
console.log(getBlock);
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
console.log(`ID '${memoryId}' not found.`);
|
|
642
|
-
}
|
|
643
|
-
update(agentId, memoryId, options = {}) {
|
|
644
|
-
const entry = this.findEntryById(agentId, memoryId);
|
|
645
|
-
if (!entry) {
|
|
646
|
-
console.log(`ā ID '${memoryId}' not found.`);
|
|
647
|
-
process.exit(1);
|
|
648
|
-
}
|
|
649
|
-
const { filePath, data, content } = entry;
|
|
650
|
-
const existingBody = content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
651
|
-
const tags = Array.isArray(options.tags)
|
|
652
|
-
? options.tags
|
|
653
|
-
: options.tags ? options.tags.split(',').map((t) => t.trim()) : (data.tags ?? []);
|
|
654
|
-
const newData = {
|
|
655
|
-
...data,
|
|
656
|
-
summary: options.summary ?? data.summary,
|
|
657
|
-
category: options.category ?? data.category,
|
|
658
|
-
tags,
|
|
659
|
-
updated: this.getToday(),
|
|
660
|
-
};
|
|
661
|
-
if (options.important) {
|
|
662
|
-
newData['important'] = true;
|
|
663
|
-
}
|
|
664
|
-
else if (options['no-important']) {
|
|
665
|
-
delete newData['important'];
|
|
666
|
-
}
|
|
667
|
-
const newBody = options.body ?? existingBody ?? '(Add details here)';
|
|
668
|
-
const newFrontmatter = (0, parser_1.stringifyFrontmatter)(newData);
|
|
669
|
-
const newContent = `${newFrontmatter}\n\n# ${String(newData['summary'])}\n\n${newBody}\n`;
|
|
670
|
-
fs_1.default.writeFileSync(filePath, newContent, 'utf-8');
|
|
671
|
-
console.log(`ā
Updated: ${entry.file}`);
|
|
672
|
-
console.log(` ID: ${memoryId}`);
|
|
673
|
-
if (options.summary)
|
|
674
|
-
console.log(` Summary: ${String(newData['summary'])}`);
|
|
675
|
-
if (options.category)
|
|
676
|
-
console.log(` Category: ${String(newData['category'])}`);
|
|
677
|
-
if (options.tags)
|
|
678
|
-
console.log(` Tags: ${newData['tags'].join(', ')}`);
|
|
679
|
-
if (options.body)
|
|
680
|
-
console.log(` Body: (updated)`);
|
|
681
|
-
}
|
|
682
|
-
delete(agentId, memoryId, options = {}) {
|
|
683
|
-
const entry = this.findEntryById(agentId, memoryId);
|
|
684
|
-
if (!entry) {
|
|
685
|
-
console.log(`ā ID '${memoryId}' not found.`);
|
|
686
|
-
process.exit(1);
|
|
687
|
-
}
|
|
688
|
-
if (!options.force) {
|
|
689
|
-
console.log(`ā ļø Delete target: [${entry.data.id}] ${entry.data.summary}`);
|
|
690
|
-
console.log(` File: ${entry.file}`);
|
|
691
|
-
console.log(`\nAdd --force to delete it.`);
|
|
692
|
-
console.log(`Example: memory delete ${agentId} ${memoryId} --force`);
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
695
|
-
fs_1.default.unlinkSync(entry.filePath);
|
|
696
|
-
console.log(`šļø Deleted: ${entry.file}`);
|
|
697
|
-
console.log(` ID: ${memoryId}`);
|
|
698
|
-
console.log(` Summary: ${entry.data.summary}`);
|
|
699
|
-
try {
|
|
700
|
-
this.markSummaryDirty(agentId);
|
|
701
|
-
console.log(` š summary.md update scheduled`);
|
|
702
|
-
}
|
|
703
|
-
catch (_e) {
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
merge(agentId, memoryId1, memoryId2, options = {}) {
|
|
707
|
-
const entry1 = this.findEntryById(agentId, memoryId1);
|
|
708
|
-
const entry2 = this.findEntryById(agentId, memoryId2);
|
|
709
|
-
if (!entry1) {
|
|
710
|
-
console.log(`ā ID '${memoryId1}' not found.`);
|
|
711
|
-
process.exit(1);
|
|
712
|
-
}
|
|
713
|
-
if (!entry2) {
|
|
714
|
-
console.log(`ā ID '${memoryId2}' not found.`);
|
|
715
|
-
process.exit(1);
|
|
716
|
-
}
|
|
717
|
-
const body1 = entry1.content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
718
|
-
const body2 = entry2.content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
719
|
-
const mergedSummary = options.summary ?? `${entry1.data.summary} + ${entry2.data.summary}`;
|
|
720
|
-
const mergedBody = `## Merged memory 1 (${entry1.data.id}, ${entry1.data.date})
|
|
721
|
-
${entry1.data.summary}
|
|
1
|
+
"use strict";var v=exports&&exports.__importDefault||function($){return $&&$.__esModule?$:{"default":$}};Object.defineProperty(exports,"__esModule",{value:true});exports.CATEGORIES=exports.MemoryEngine=exports.SUMMARIZE_LOCK_STALE_MS=exports.SUMMARIZER_FAILURE_THRESHOLD=void 0;exports.getMemoryConfig=P;exports.createMemoryEngine=Z;const l=v(require("fs"));const D=v(require("path"));const N=require("child_process");const B=require("nanoid");const H=v(require("js-yaml"));const T=require("./parser");const j=require("./summarizer-failure");const _=require("@crewx/knowledge-core");const z=require("@crewx/sdk");exports.SUMMARIZER_FAILURE_THRESHOLD=3;const U=".summarizer-health.json";const M=50;function q(){const $=(0,z.resolveCrewxExecutable)({env:process.env,cwd:process.cwd()});if(!$.ok){const n=new Error((0,z.formatCrewxExecutableFailure)($));Object.assign(n,{code:"CLI_UNRESOLVABLE"});throw n}const e=(0,z.createCrewxChildInvocation)($,{baseEnv:process.env});const t=(0,z.resolveWindowsSpawnProgram)({command:e.command,env:e.env,cwd:process.cwd(),packageName:"crewx",allowShellFallback:true});const r=(0,z.materializeWindowsSpawnProgram)(t,e.args);return{command:r.command,args:r.argv,env:e.env,shell:r.shell,windowsHide:r.windowsHide}}function k($,e){const t=q();return(0,N.execFileSync)(t.command,[...t.args,...$],{...e,env:t.env,...t.shell===void 0?{}:{shell:t.shell},...t.windowsHide===void 0?{}:{windowsHide:t.windowsHide}})}exports.SUMMARIZE_LOCK_STALE_MS=10*60*1e3;function O($){const e=$.match(/š Response:\s*ā+\s*([\s\S]*?)(?=š Working Directory|$)/);if(e?.[1]){const t=e[1].trim();const r=t.split("\n")[0]?.trim()??"";if(!r.startsWith("{")){return t}return I(t)}return I($)}function I($){const e=$.split("\n").filter(o=>o.trim().startsWith("{"));let t="";let r=false;for(const o of e){try{const i=JSON.parse(o);if(i.type==="message"&&i.role==="assistant"){r=true;if(i.content)t+=i.content}}catch(i){}}if(r)return t.trim()||null;const n=$.trim();if(n.length===0)return null;const s=n.split("\n")[0]?.trim()??"";if(s.startsWith("{"))return null;return n}function W(){try{return(0,N.execSync)("git rev-parse --show-toplevel",{encoding:"utf8"}).trim()}catch{return process.cwd()}}function P(){try{const $=process.env.CREWX_CONFIG??D.default.join(W(),"crewx.yaml");const e=l.default.readFileSync($,"utf8");const t=H.default.load(e);const r=t?.settings;return r?.memory??{}}catch{return{}}}class L{constructor(e={}){this.searchIndexCache=new Map;const t=P();this.dataDir=e.dataDir??process.env["MEMORY_DATA_DIR"]??D.default.resolve(process.cwd(),"memory");this.summaryDays=e.summaryDays??(t.summary_days!=null?Number(t.summary_days):void 0)??(parseInt(process.env["MEMORY_SUMMARY_DAYS"]??"",10)||7);this.recentDays=e.recentDays??(t.recent_days!=null?Number(t.recent_days):void 0)??(parseInt(process.env["MEMORY_RECENT_DAYS"]??"",10)||30);this.shortTermHours=e.shortTermHours??(t.short_term_hours!=null?Number(t.short_term_hours):void 0)??24;this.summarizerAgent=e.summarizerAgent??t.summarizer_agent??"@memory_summarizer";this.searcherAgent=e.searcherAgent??t.searcher_agent??"@memory_searcher";this.summarizerTimeout=e.summarizerTimeout??(t.summarizer_timeout!=null?Number(t.summarizer_timeout):void 0)??48e4;this.searcherTimeout=e.searcherTimeout??(t.searcher_timeout!=null?Number(t.searcher_timeout):void 0)??3e5}getAgentDir(e){return D.default.join(this.dataDir,e)}getEntriesDir(e){return D.default.join(this.getAgentDir(e),"entries")}getEntryFilesFingerprint(e){const t=this.getEntriesDir(e);if(!l.default.existsSync(t))return{files:[],fingerprint:""};const r=l.default.readdirSync(t).filter(s=>s.endsWith(".md")).sort();const n=r.map(s=>{const o=l.default.statSync(D.default.join(t,s));return`${s}\0${o.size}\0${o.mtimeMs}`}).join("");return{files:r,fingerprint:n}}buildSearchIndex(e,t){const r=this.getEntriesDir(e);const n=t.map(s=>{const o=l.default.readFileSync(D.default.join(r,s),"utf-8");const{data:i,content:d}=(0,T.parseFrontmatter)(o);return{...i,file:s,body:d}});return(0,_.createSearchIndex)(n,{summary:3,tags:2,body:1})}getSearchIndex(e){const t=this.getEntryFilesFingerprint(e);const r=this.searchIndexCache.get(e);if(r?.fingerprint===t.fingerprint){return{files:t.files,index:r.index}}const n=this.buildSearchIndex(e,t.files);this.searchIndexCache.set(e,{fingerprint:t.fingerprint,index:n});return{files:t.files,index:n}}invalidateSearchIndex(e){this.searchIndexCache.delete(e)}createFindActionContext(e,t,r){return{commandId:"memory.find",params:{agentId:e,query:t},resultTags:[...new Set(r.flatMap(n=>n.tags??[]))].slice(0,3),resultIds:r.slice(0,3).map(n=>n.id),resultCount:r.length}}getSummaryPath(e){return D.default.join(this.getAgentDir(e),"summary.md")}getDirtySummaryPath(e){return D.default.join(this.getAgentDir(e),".dirty-summary")}getSummarizeLockPath(){return D.default.join(this.dataDir,".summarize.lock")}ensureDir(e){if(!l.default.existsSync(e)){l.default.mkdirSync(e,{recursive:true})}}generateId(){return(0,B.nanoid)(6)}getToday(){return new Date().toISOString().slice(0,10)}getLocalISO8601(){const e=new Date;const t=-e.getTimezoneOffset();const r=t>=0?"+":"-";const n=String(Math.floor(Math.abs(t)/60)).padStart(2,"0");const s=String(Math.abs(t)%60).padStart(2,"0");const o=e.getFullYear();const i=String(e.getMonth()+1).padStart(2,"0");const d=String(e.getDate()).padStart(2,"0");const f=String(e.getHours()).padStart(2,"0");const a=String(e.getMinutes()).padStart(2,"0");const c=String(e.getSeconds()).padStart(2,"0");return`${o}-${i}-${d}T${f}:${a}:${c}${r}${n}:${s}`}getPrimaryTag(e){return e.tags?.[0]??"general"}toEntryTimestamp(e){if(e["created_at"])return String(e["created_at"]);const t=e["date"];const r=t&&typeof t==="object"&&typeof t.toISOString==="function"?t.toISOString().slice(0,10):String(t??"");if(r.includes("T"))return r;return r+"T09:00:00+09:00"}markSummaryDirty(e){this.ensureDir(this.getAgentDir(e));l.default.writeFileSync(this.getDirtySummaryPath(e),String(Date.now()),"utf-8")}getSummarizerHealthPath(){return D.default.join(this.dataDir,U)}readSummarizerHealth(){try{const e=l.default.readFileSync(this.getSummarizerHealthPath(),"utf-8");const t=JSON.parse(e);return{consecutiveFailures:typeof t.consecutiveFailures==="number"?t.consecutiveFailures:0,notified:Boolean(t.notified),lastError:t.lastError}}catch{return{consecutiveFailures:0,notified:false}}}writeSummarizerHealth(e){this.ensureDir(this.dataDir);l.default.writeFileSync(this.getSummarizerHealthPath(),JSON.stringify(e),"utf-8")}recordSummarizerFailure(e){const t=this.readSummarizerHealth();t.consecutiveFailures+=1;t.lastError=e.code||e.message?e:void 0;if(t.consecutiveFailures>=exports.SUMMARIZER_FAILURE_THRESHOLD&&!t.notified){this.notifySummarizerDegraded(t.consecutiveFailures,t.lastError);t.notified=true}this.writeSummarizerHealth(t)}recordSummarizerSuccess(){const e=this.readSummarizerHealth();if(e.consecutiveFailures===0&&!e.notified)return;this.writeSummarizerHealth({consecutiveFailures:0,notified:false})}notifySummarizerDegraded(e,t){try{k(["notify","AI memory summarization keeps failing \u2014 falling back to mechanical summaries","--level=warning","--body=callSummarizer(agent="+this.summarizerAgent+") failed "+e+" times in a row (last error: "+(0,j.formatSummarizerLastError)(t)+"), so summarization switched to the fallback (mechanical) path."],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:15e3})}catch(r){const n=r;console.error(`[memory] failed to send summarizer-health notify: ${n.message}`)}}loadAllEntries(e){const t=this.getEntriesDir(e);if(!l.default.existsSync(t))return[];const r=l.default.readdirSync(t).filter(s=>s.endsWith(".md"));const n=[];for(const s of r){const o=l.default.readFileSync(D.default.join(t,s),"utf-8");const{data:i,content:d}=(0,T.parseFrontmatter)(o);const f=d.replace(/^#[^\n]*\n*/m,"").trim();const a=Boolean(f)&&f!=="(Add details here)";const c=i;n.push({...c,file:s,hasBody:a})}n.sort((s,o)=>this.toEntryTimestamp(o).localeCompare(this.toEntryTimestamp(s)));return n}findEntryById(e,t){const r=this.getEntriesDir(e);if(!l.default.existsSync(r))return null;const n=l.default.readdirSync(r).filter(s=>s.endsWith(".md"));for(const s of n){const o=D.default.join(r,s);const i=l.default.readFileSync(o,"utf-8");const{data:d,content:f}=(0,T.parseFrontmatter)(i);if(d.id===t){return{file:s,filePath:o,data:d,content:f}}}return null}save(e,t,r="general",n={}){this.ensureDir(this.getEntriesDir(e));const s=this.generateId();const o=this.getLocalISO8601();const i=Array.isArray(n.tags)?n.tags:n.tags?n.tags.split(",").map(h=>h.trim()):[];const d=(0,T.stringifyFrontmatter)({id:s,type:"memory",date:o,category:r,tags:i,summary:t,...n.important?{important:true}:{}});const f=`${s}.md`;const a=D.default.join(this.getEntriesDir(e),f);const c=n.body??"(Add details here)";const m=`${d}
|
|
722
2
|
|
|
723
|
-
${
|
|
3
|
+
# ${t}
|
|
724
4
|
|
|
5
|
+
${c}
|
|
6
|
+
`;l.default.writeFileSync(a,m,"utf-8");this.invalidateSearchIndex(e);console.log(`\u2705 Saved: data/${e}/entries/${f}`);console.log(` ID: ${s}`);console.log(` Tags: ${i.join(", ")||"(none)"}`);console.log(` Category: ${r}`);try{this.markSummaryDirty(e);console.log(` \u{1F4DD} summary.md update scheduled`)}catch(h){}const u={commandId:"memory.save",params:{agentId:e,tags:i.join(",")},resultTags:i.slice(0,3),resultCount:1};const y=(0,_.generateNextActions)(u);const g=(0,_.formatActions)(y);if(g)console.log(g)}index(e,t={}){const r=this.getSummaryPath(e);const n=this.loadAllEntries(e);const s=[...new Set(n.map(u=>this.getPrimaryTag(u)))];if(t.recent){const u=parseInt(String(t.recent),10)||this.recentDays;this.recent(e,u);return}const o=new Date(Date.now()-this.shortTermHours*60*60*1e3).toISOString();const i=n.filter(u=>this.toEntryTimestamp(u)>=o);console.log(`# ${e} memory summary
|
|
7
|
+
`);const d=l.default.existsSync(r)?(0,T.parseFrontmatter)(l.default.readFileSync(r,"utf-8")).data.last_updated??"-":"-";console.log(`> ${n.length} items | topics ${s.length} items | updated: ${d}`);if(t.full){const u={};for(const g of n){const h=g.category??"general";u[h]=(u[h]??0)+1}const y=Object.entries(u).sort((g,h)=>h[1]-g[1]).map(([g,h])=>`${g}:${h}`).join(" ");console.log(`> \u{1F4CA} ${y}`)}console.log("");if(i.length>0){const u=[...i].sort((y,g)=>{const h=y.important?1:0;const w=g.important?1:0;if(h!==w)return w-h;return this.toEntryTimestamp(g).localeCompare(this.toEntryTimestamp(y))});console.log(`## \u{1F525} last Task (24h)
|
|
8
|
+
`);for(const y of u){const g=y.important?"\u2B50 ":"";console.log(`- ${g}[${y.id}] ${y.summary} (${this.getPrimaryTag(y)})`)}console.log("")}if(t.full){if(l.default.existsSync(r)){const u=l.default.readFileSync(r,"utf-8");const{content:y}=(0,T.parseFrontmatter)(u);if(n.length>0){const g=n[0]?.date;const h=n[n.length-1]?.date;const w=g?new Date(`${g}T00:00:00Z`):null;const E=h?new Date(`${h}T00:00:00Z`):null;if(w&&E&&!Number.isNaN(w.getTime())&&!Number.isNaN(E.getTime())){const F=24*60*60*1e3;const S=Math.max(1,Math.floor((w.getTime()-E.getTime())/F)+1);const A=x=>`${String(x.getMonth()+1).padStart(2,"0")}/${String(x.getDate()).padStart(2,"0")}`;console.log("---");console.log(`\u{1F4CB} Topic summary (${A(E)} ~ ${A(w)}, ${S} days)
|
|
9
|
+
`)}}console.log(y.trim())}else{for(const u of s.sort()){const y=n.filter(g=>this.getPrimaryTag(g)===u).length;console.log(`- **${u}** (${y} items)`)}}}const f=t.full?"memory.index-full":"memory.index";const a={commandId:f,params:{agentId:e},resultTags:s.slice(0,3),resultIds:n.slice(0,3).map(u=>u.id),resultCount:n.length};const c=(0,_.generateNextActions)(a);const m=(0,_.formatActions)(c);if(m)console.log(m)}generateIndexContent(e){const t=this.loadAllEntries(e);const r=this.getToday();const n=new Date(Date.now()-this.recentDays*24*60*60*1e3).toISOString().slice(0,10);const s={};const o=[];for(const c of t){if(c.date>=n)o.push(c);const m=c.category??"general";if(!s[m])s[m]=[];s[m].push(c)}let i=`# ${e} memory
|
|
10
|
+
|
|
11
|
+
`;i+=`> Last Updated: ${r}
|
|
12
|
+
`;i+=`> Total: ${t.length} memories
|
|
13
|
+
|
|
14
|
+
`;i+=`## \u{1F525} Last ${this.recentDays} days
|
|
15
|
+
|
|
16
|
+
`;if(o.length===0){i+=`(none)
|
|
17
|
+
|
|
18
|
+
`}else{i+=`| ID | Date | Category | Summary | Details |
|
|
19
|
+
`;i+=`|----|------|----------|------|------|
|
|
20
|
+
`;for(const c of o){const m=c.hasBody?"\u{1F4C4}":"-";i+=`| ${c.id} | ${c.date} | ${c.category??"-"} | [${c.summary}](entries/${c.file}) | ${m} |
|
|
21
|
+
`}i+=`
|
|
22
|
+
`}i+=`## \u{1F3F7}\uFE0F By category
|
|
23
|
+
|
|
24
|
+
`;for(const[c,m]of Object.entries(s).sort()){i+=`- **${c}**: ${m.length} items
|
|
25
|
+
`}const d=t.length-o.length;const f=t[t.length-1]?.date??"-";const a=t[0]?.date??"-";i+=`
|
|
26
|
+
## \u{1F4CA} Stats
|
|
27
|
+
|
|
28
|
+
`;i+=`- Total: ${t.length} items (${f} ~ ${a})
|
|
29
|
+
`;i+=`- Last ${this.recentDays} days: ${o.length} items
|
|
30
|
+
`;if(d>0)i+=`- Older than ${this.recentDays} days: ${d} items
|
|
31
|
+
`;return i}topic(e,t){const r=this.loadAllEntries(e);const n=r.filter(s=>this.getPrimaryTag(s)===t);if(n.length===0){console.log(`No memories found for topic '${t}'.`);return}console.log(`## \u{1F4C2} Topic: ${t} (${n.length} items)
|
|
32
|
+
`);for(const s of n){console.log(`### [${s.date}] ${s.summary}`);console.log(`- File: data/${e}/entries/${s.file}`);console.log(`- Category: ${s.category??"-"}`);console.log(`- Tags: ${(s.tags??[]).join(", ")||"-"}`);console.log("")}}recent(e,t=30){const r=this.getRecentEntries(e,t);if(r.length===0){console.log(`No memories found in the last ${t} days.`);return}console.log(`## \u{1F525} Last ${t} days (${r.length} items)
|
|
33
|
+
`);for(const n of r){const s=n.hasBody?" \u{1F4C4}":"";const o=String(n.date).includes("T")?String(n.date).slice(0,16).replace("T"," "):n.date;console.log(`[${n.id}] [${o}] [${n.category??"-"}] ${n.summary}${s}`)}}getRecentEntries(e,t=30,r={}){const n=this.loadAllEntries(e);const s=new Date(Date.now()-t*24*60*60*1e3).toISOString().slice(0,10);const o=n.filter(i=>this.resolveRecentDate(e,i).slice(0,10)>=s);if(!r.includeBody)return o;return o.map(i=>({...i,body:this.readEntryBody(e,i.file)}))}resolveRecentDate(e,t){const r=this.toDateString(t.updated);if(r)return r;const n=this.toDateString(t["created_at"]);if(n)return n;const s=this.toDateString(t.date);if(s)return s;try{const o=D.default.join(this.getEntriesDir(e),t.file);return l.default.statSync(o).mtime.toISOString()}catch{return""}}toDateString(e){if(e&&typeof e==="object"&&typeof e.toISOString==="function"){return e.toISOString()}return typeof e==="string"?e:""}readEntryBody(e,t){try{const r=l.default.readFileSync(D.default.join(this.getEntriesDir(e),t),"utf-8");const{content:n}=(0,T.parseFrontmatter)(r);return n.replace(/^#[^\n]*\n*/m,"").trim()}catch{return""}}find(e,t){const{files:r,index:n}=this.getSearchIndex(e);if(r.length===0){console.log("No memories found.");return}const s=n.search(t,M+1);const o=s.length>M;const i=s.slice(0,M);if(i.length===0){console.log(`No related memories found for '${t}'.`);return}const d=o?`, top ${M}`:"";console.log(`## \u{1F50D} Search results: "${t}" (${i.length} items${d}, sorted by BM25 score)
|
|
34
|
+
`);for(const m of i){const u=m._score!=null?` [${m._score}pts]`:"";console.log(`[${m.id}] [${m.date}] [${this.getPrimaryTag(m)}]${u} ${m.summary}`)}const f=this.createFindActionContext(e,t,i);const a=(0,_.generateNextActions)(f);const c=(0,_.formatActions)(a);if(c)console.log(c)}list(e,t={}){const r=this.loadAllEntries(e);let n=r;if(t.category){n=n.filter(a=>a.category===t.category)}if(t.tag){const a=t.tag.split(",").map(c=>c.trim());n=n.filter(c=>a.every(m=>(c.tags??[]).includes(m)))}if(t.json){const a=this.getEntriesDir(e);const c=n.map(m=>{let u="";if(m.file){try{const y=l.default.readFileSync(D.default.join(a,m.file),"utf-8");const{content:g}=(0,T.parseFrontmatter)(y);const h=g.replace(/^#[^\n]*\n*/m,"").trim();if(h!=="(Add details here)")u=h}catch(y){}}return{id:m.id,title:m.summary??"",category:m.category??"general",tags:m.tags??[],created_at:m.date??"",content:u}});console.log(JSON.stringify(c,null,2));return}if(n.length===0){const a=[];if(t.category)a.push(`category=${t.category}`);if(t.tag)a.push(`tag=${t.tag}`);console.log(`No memories match the filters. (${a.join(", ")||"all"})`);return}console.log(`## \u{1F4CB} List: ${e} (${n.length} items)
|
|
35
|
+
`);for(const a of n){console.log(`[${a.id}] [${a.date}] [${this.getPrimaryTag(a)}] ${a.summary}`)}const s=[...new Set(n.flatMap(a=>a.tags??[]))].slice(0,3);const o=n.slice(0,3).map(a=>a.id);const i={commandId:"memory.list",params:{agentId:e,...t.category?{category:t.category}:{}},resultTags:s,resultIds:o,resultCount:n.length};const d=(0,_.generateNextActions)(i);const f=(0,_.formatActions)(d);if(f)console.log(f)}stats(e){const t=this.loadAllEntries(e);if(t.length===0){console.log("No memories found.");return}const r={};for(const a of t){const c=a.category??"general";r[c]=(r[c]??0)+1}const n={};for(const a of t){for(const c of a.tags??[]){n[c]=(n[c]??0)+1}}console.log(`# ${e} Stats (${t.length} items)
|
|
36
|
+
`);console.log(`## By category`);for(const[a,c]of Object.entries(r).sort((m,u)=>u[1]-m[1])){console.log(` ${a}: ${c} items`)}console.log(`
|
|
37
|
+
## Top 20 Tags`);const s=Object.entries(n).sort((a,c)=>c[1]-a[1]).slice(0,20);for(const[a,c]of s){console.log(` ${a}: ${c} items`)}const o=Object.entries(r).sort((a,c)=>c[1]-a[1]).map(a=>a[0]);const i={commandId:"memory.stats",params:{agentId:e},resultTags:o.slice(0,3),resultCount:t.length};const d=(0,_.generateNextActions)(i);const f=(0,_.formatActions)(d);if(f)console.log(f)}tags(e){const t=this.loadAllEntries(e);if(t.length===0){console.log("No memories found.");return}const r={};for(const s of t){for(const o of s.tags??[]){r[o]=(r[o]??0)+1}}const n=Object.entries(r).sort((s,o)=>o[1]-s[1]);if(n.length===0){console.log("No tags found.");return}console.log(`## \u{1F3F7}\uFE0F All tags (${n.length} items)
|
|
38
|
+
`);for(const[s,o]of n){console.log(` ${s}: ${o} items`)}}get(e,t){const r=this.getEntriesDir(e);if(!l.default.existsSync(r)){console.log("No memories found.");return}const n=l.default.readdirSync(r).filter(s=>s.endsWith(".md"));for(const s of n){const o=D.default.join(r,s);const i=l.default.readFileSync(o,"utf-8");const{data:d,content:f}=(0,T.parseFrontmatter)(i);if(d.id===t){const a=d;console.log(`## \u{1F4C4} ${a.summary}
|
|
39
|
+
`);console.log(`- ID: ${a.id}`);console.log(`- Date: ${a.date}`);console.log(`- Category: ${a.category??"-"}`);console.log(`- Tags: ${(a.tags??[]).join(", ")||"-"}`);console.log(`- File: data/${e}/entries/${s}`);console.log(`
|
|
40
|
+
---
|
|
41
|
+
`);console.log(f.trim());const c=D.default.join(this.getAgentDir(e),"graph.json");if(l.default.existsSync(c)){try{const g=JSON.parse(l.default.readFileSync(c,"utf-8"));const h=g.edges.filter(w=>w.from===t||w.to===t).sort((w,E)=>E.weight-w.weight).slice(0,5);if(h.length>0){const w={};g.nodes.forEach(E=>{w[E.id]=E});console.log(`
|
|
725
42
|
---
|
|
43
|
+
`);console.log(`## \u{1F517} Related memories (${h.length} items)
|
|
44
|
+
`);for(const E of h){const F=E.from===t?E.to:E.from;const S=w[F];if(S){console.log(`[${E.weight}] [${F}] ${S.summary.slice(0,50)}...`)}}}}catch(g){}}const m={commandId:"memory.get",params:{agentId:e,id:t},resultTags:(a.tags??[]).slice(0,3),resultIds:[t],resultCount:1};const u=(0,_.generateNextActions)(m);const y=(0,_.formatActions)(u);if(y)console.log(y);return}}console.log(`ID '${t}' not found.`)}update(e,t,r={}){const n=this.findEntryById(e,t);if(!n){console.log(`\u274C ID '${t}' not found.`);process.exit(1)}const{filePath:s,data:o,content:i}=n;const d=i.replace(/^#[^\n]*\n*/m,"").trim();const f=Array.isArray(r.tags)?r.tags:r.tags?r.tags.split(",").map(y=>y.trim()):o.tags??[];const a={...o,summary:r.summary??o.summary,category:r.category??o.category,tags:f,updated:this.getToday()};if(r.important){a["important"]=true}else if(r["no-important"]){delete a["important"]}const c=r.body??d??"(Add details here)";const m=(0,T.stringifyFrontmatter)(a);const u=`${m}
|
|
45
|
+
|
|
46
|
+
# ${String(a["summary"])}
|
|
47
|
+
|
|
48
|
+
${c}
|
|
49
|
+
`;l.default.writeFileSync(s,u,"utf-8");this.invalidateSearchIndex(e);console.log(`\u2705 Updated: ${n.file}`);console.log(` ID: ${t}`);if(r.summary)console.log(` Summary: ${String(a["summary"])}`);if(r.category)console.log(` Category: ${String(a["category"])}`);if(r.tags)console.log(` Tags: ${a["tags"].join(", ")}`);if(r.body)console.log(` Body: (updated)`)}delete(e,t,r={}){const n=this.findEntryById(e,t);if(!n){console.log(`\u274C ID '${t}' not found.`);process.exit(1)}if(!r.force){console.log(`\u26A0\uFE0F Delete target: [${n.data.id}] ${n.data.summary}`);console.log(` File: ${n.file}`);console.log(`
|
|
50
|
+
Add --force to delete it.`);console.log(`Example: memory delete ${e} ${t} --force`);return}l.default.unlinkSync(n.filePath);this.invalidateSearchIndex(e);console.log(`\u{1F5D1}\uFE0F Deleted: ${n.file}`);console.log(` ID: ${t}`);console.log(` Summary: ${n.data.summary}`);try{this.markSummaryDirty(e);console.log(` \u{1F4DD} summary.md update scheduled`)}catch(s){}}merge(e,t,r,n={}){const s=this.findEntryById(e,t);const o=this.findEntryById(e,r);if(!s){console.log(`\u274C ID '${t}' not found.`);process.exit(1)}if(!o){console.log(`\u274C ID '${r}' not found.`);process.exit(1)}const i=s.content.replace(/^#[^\n]*\n*/m,"").trim();const d=o.content.replace(/^#[^\n]*\n*/m,"").trim();const f=n.summary??`${s.data.summary} + ${o.data.summary}`;const a=`## Merged memory 1 (${s.data.id}, ${s.data.date})
|
|
51
|
+
${s.data.summary}
|
|
52
|
+
|
|
53
|
+
${i!=="(Add details here)"?i:"(No details)"}
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Merged memory 2 (${o.data.id}, ${o.data.date})
|
|
58
|
+
${o.data.summary}
|
|
59
|
+
|
|
60
|
+
${d!=="(Add details here)"?d:"(No details)"}`;const c=[...new Set([...s.data.tags??[],...o.data.tags??[]])];const m=s.data.date>o.data.date?s.data.date:o.data.date;const u=this.generateId();const y={id:u,type:"memory",date:m,category:s.data.category,tags:c,summary:f,merged_from:[t,r]};const g=`${u}.md`;const h=D.default.join(this.getEntriesDir(e),g);const w=(0,T.stringifyFrontmatter)(y);const E=`${w}
|
|
61
|
+
|
|
62
|
+
# ${f}
|
|
63
|
+
|
|
64
|
+
${a}
|
|
65
|
+
`;l.default.writeFileSync(h,E,"utf-8");l.default.unlinkSync(s.filePath);l.default.unlinkSync(o.filePath);this.invalidateSearchIndex(e);console.log(`\u{1F500} Merge complete!`);console.log(` New ID: ${u}`);console.log(` New file: ${g}`);console.log(` Merged memory: [${t}] + [${r}]`);console.log(` Summary: ${f}`)}buildFallbackSummary(e){const t={};for(const s of e){const o=this.getPrimaryTag(s);if(!t[o])t[o]=[];t[o].push(s)}const r=s=>{if(!s)return"-";const o=String(s).includes("T")?s:`${s}T00:00:00Z`;const i=new Date(o);if(Number.isNaN(i.getTime()))return"-";return`${String(i.getMonth()+1).padStart(2,"0")}/${String(i.getDate()).padStart(2,"0")}`};let n="";for(const[s,o]of Object.entries(t).sort()){const i=o.map(c=>c.date).filter(Boolean).sort();const d=i[0];const f=i[i.length-1];const a=d&&f?`, ${r(d)}~${r(f)}`:"";n+=`## ${s} (${o.length} items${a})
|
|
66
|
+
`;for(const c of o)n+=`- [${c.date}] ${c.summary}
|
|
67
|
+
`;for(const c of o)n+=`\u2192 [entries/${c.file}](entries/${c.file})
|
|
68
|
+
`;n+=`
|
|
69
|
+
`}return n}callSummarizer(e){try{const t=k(["q",this.summarizerAgent],{encoding:"utf-8",timeout:this.summarizerTimeout,stdio:["pipe","pipe","pipe"],input:e});const r=O(t);this.recordSummarizerSuccess();return r}catch(t){const r=t;const n=r.code==="ETIMEDOUT"||r.killed?`timed out after ${this.summarizerTimeout}ms`:r.message;console.error(`[memory] callSummarizer failed (agent=${this.summarizerAgent}), falling back to raw entry list: ${n}`);this.recordSummarizerFailure((0,j.describeSummarizerFailure)(r,this.summarizerTimeout));return null}}generateSummary(e,t={}){const r=this.loadAllEntries(e);if(r.length===0)return{status:"empty"};const n=new Date(Date.now()-this.summaryDays*24*60*60*1e3).toISOString().slice(0,10);const s=t.force?r:r.filter(S=>S.date>=n);if(s.length===0)return{status:"empty",entryCount:r.length};const o=this.getSummaryPath(e);const i=l.default.existsSync(o)?(0,T.parseFrontmatter)(l.default.readFileSync(o,"utf-8")):null;const d=i?.data?.last_entry_id??null;const f=i?.content.trim()??null;const a=(S,A)=>{if(!A)return S;const x=S.findIndex(p=>p.id===A);if(x===-1)return S;return S.slice(0,x)};const c=a(s,d);if(c.length===0&&!t.force){return{status:"up-to-date",entryCount:r.length}}const m=t.force?s:c;const u=[...m].reverse().map(S=>{if(S.file){try{const A=l.default.readFileSync(D.default.join(this.getEntriesDir(e),S.file),"utf-8");const{content:x}=(0,T.parseFrontmatter)(A);const p=x.replace(/^#[^\n]*\n*/m,"").trim();return{...S,body:p}}catch(A){}}return S});const y=[...new Set(s.map(S=>this.getPrimaryTag(S)))].sort();const g=(S,A,x)=>{let p=`You are a memory summarizer for an AI agent.
|
|
70
|
+
|
|
71
|
+
`;if(S){p+=`## Existing summary:
|
|
72
|
+
${S}
|
|
73
|
+
|
|
74
|
+
## New entries to incorporate:
|
|
75
|
+
`}else{p+=`## All entries:
|
|
76
|
+
`}for(const b of A){const C=b.important?" [important]":"";p+=`- [${b.id}] [${b.date}] [${this.getPrimaryTag(b)}]${C} ${b.summary}`;if(b.body&&b.body!=="(Add details here)"){p+=` \u2014 ${b.body.slice(0,200)}`}p+=` (file: entries/${b.file})
|
|
77
|
+
`}p+=`
|
|
78
|
+
## Instructions:
|
|
79
|
+
`;p+=`- Group by topic, each topic as ## heading with entry count and date range, e.g. "## mcp-http (4 items, 01/28~02/14)"
|
|
80
|
+
`;p+=`- Write concise Korean summary per topic (2-5 sentences)
|
|
81
|
+
`;p+=`- After each topic summary, list relevant entry file links as "\u2192 [entries/filename](entries/filename)"
|
|
82
|
+
`;p+=`- Highlight key decisions, insights, and action items
|
|
83
|
+
`;p+=`- If there's an existing summary, merge new information into it
|
|
84
|
+
`;p+=`- Topics to cover: ${x.join(", ")}
|
|
85
|
+
`;p+=`- Output ONLY the markdown body (topic sections), no frontmatter or top-level heading
|
|
86
|
+
`;return p};const h=g(t.force?null:f,u,y);const w=this.callSummarizer(h);const E=s[0]?.id;if(!w){const S=s.map(p=>{if(p.file){try{const b=l.default.readFileSync(D.default.join(this.getEntriesDir(e),p.file),"utf-8");const{content:C}=(0,T.parseFrontmatter)(b);const R=C.replace(/^#[^\n]*\n*/m,"").trim();return{...p,body:R}}catch(b){}}return p});const A=this.buildFallbackSummary(S);const x=(0,T.stringifyFrontmatter)({last_entry_id:E,last_updated:this.getToday(),entry_count:r.length,summary_days:this.summaryDays,summarizer:"fallback"});l.default.writeFileSync(o,`${x}
|
|
87
|
+
|
|
88
|
+
${A}`,"utf-8");return{status:"fallback",entryCount:r.length,newEntries:m.length}}const F=(0,T.stringifyFrontmatter)({last_entry_id:E,last_updated:this.getToday(),entry_count:r.length,summary_days:this.summaryDays,summarizer:"memory_summarizer"});l.default.writeFileSync(o,`${F}
|
|
726
89
|
|
|
727
|
-
|
|
728
|
-
|
|
90
|
+
${w}
|
|
91
|
+
`,"utf-8");return{status:"updated",entryCount:r.length,newEntries:m.length}}summarize(e,t={}){const r=this.loadAllEntries(e);if(r.length===0){console.log("No memories found.");return}console.log(`\u{1F4DD} Starting summary generation (${r.length} items entries)
|
|
92
|
+
`);const n=this.generateSummary(e,{force:t.force});if(n.status==="up-to-date"){console.log("\u2705 summary.md is up to date.")}else if(n.status==="fallback"){console.log(`\u{1F4CB} Fallback summary generated (AI call failed)`)}else if(n.status==="updated"){console.log(`\u{1F916} Summary complete (${n.newEntries} items applied)`)}console.log(` File: data/${e}/summary.md`);console.log(` entries: ${n.entryCount} items`)}isSummarizeLockStale(e){let t;try{t=l.default.readFileSync(e,"utf-8")}catch{return true}const r=t.match(/^pid=(\d+)/m);const n=r?Number(r[1]):NaN;if(Number.isFinite(n)){try{process.kill(n,0);return false}catch(s){const o=s;if(o.code==="ESRCH")return true}}try{const s=l.default.statSync(e);return Date.now()-s.mtimeMs>exports.SUMMARIZE_LOCK_STALE_MS}catch{return true}}acquireSummarizeLock(){const e=this.getSummarizeLockPath();const t=`pid=${process.pid}
|
|
93
|
+
mtime=${new Date().toISOString()}
|
|
94
|
+
`;try{const r=l.default.openSync(e,"wx");l.default.writeSync(r,t);l.default.closeSync(r);return true}catch(r){const n=r;if(n.code!=="EEXIST")throw n}if(!this.isSummarizeLockStale(e)){return false}l.default.writeFileSync(e,t,"utf-8");return true}releaseSummarizeLock(){try{l.default.unlinkSync(this.getSummarizeLockPath())}catch{}}summarizeDirty(){if(!l.default.existsSync(this.dataDir)){console.log(`\u{1F4A1} Register debounce cron:`);console.log(`crewx cron add "*/1 * * * *" "crewx memory summarize-dirty" --mode command --name "memory-debounce"`);return}if(!this.acquireSummarizeLock()){console.log(`\u23ED\uFE0F summarize-dirty is already running (lock held) \u2014 skipping this run.`);return}try{const e=l.default.readdirSync(this.dataDir,{withFileTypes:true}).filter(t=>t.isDirectory()).map(t=>t.name);for(const t of e){const r=this.getDirtySummaryPath(t);if(!l.default.existsSync(r))continue;try{const n=this.generateSummary(t);if(n.status==="updated"){console.log(`\u{1F916} ${t} summary.md updated (${n.newEntries} items applied)`)}else if(n.status==="fallback"){console.log(`\u{1F4CB} ${t} Fallback summary generated (AI call failed)`)}else if(n.status==="up-to-date"){console.log(`\u2705 ${t} summary.md is up to date.`)}else if(n.status==="empty"){console.log(`\u2139\uFE0F ${t} has no memories to summarize.`)}l.default.unlinkSync(r)}catch(n){console.error(`\u274C ${t} summary.md update failed: ${n.message}`)}}}finally{this.releaseSummarizeLock()}console.log(`\u{1F4A1} Register debounce cron:`);console.log(`crewx cron add "*/1 * * * *" "crewx memory summarize-dirty" --mode command --name "memory-debounce"`)}search(e,t){const r=this.loadAllEntries(e);if(r.length===0){console.log("No memories found.");return}const n=r.map(o=>`[${o.id}] [${o.date}] [${this.getPrimaryTag(o)}] ${o.summary}`).join("\n");const s=`Memory list:
|
|
95
|
+
${n}
|
|
729
96
|
|
|
730
|
-
${
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
];
|
|
734
|
-
const newerDate = entry1.data.date > entry2.data.date ? entry1.data.date : entry2.data.date;
|
|
735
|
-
const newId = this.generateId();
|
|
736
|
-
const newData = {
|
|
737
|
-
id: newId,
|
|
738
|
-
type: 'memory',
|
|
739
|
-
date: newerDate,
|
|
740
|
-
category: entry1.data.category,
|
|
741
|
-
tags: mergedTags,
|
|
742
|
-
summary: mergedSummary,
|
|
743
|
-
merged_from: [memoryId1, memoryId2],
|
|
744
|
-
};
|
|
745
|
-
const filename = `${newId}.md`;
|
|
746
|
-
const filepath = path_1.default.join(this.getEntriesDir(agentId), filename);
|
|
747
|
-
const newFrontmatter = (0, parser_1.stringifyFrontmatter)(newData);
|
|
748
|
-
const newContent = `${newFrontmatter}\n\n# ${mergedSummary}\n\n${mergedBody}\n`;
|
|
749
|
-
fs_1.default.writeFileSync(filepath, newContent, 'utf-8');
|
|
750
|
-
fs_1.default.unlinkSync(entry1.filePath);
|
|
751
|
-
fs_1.default.unlinkSync(entry2.filePath);
|
|
752
|
-
console.log(`š Merge complete!`);
|
|
753
|
-
console.log(` New ID: ${newId}`);
|
|
754
|
-
console.log(` New file: ${filename}`);
|
|
755
|
-
console.log(` Merged memory: [${memoryId1}] + [${memoryId2}]`);
|
|
756
|
-
console.log(` Summary: ${mergedSummary}`);
|
|
757
|
-
}
|
|
758
|
-
buildFallbackSummary(entries) {
|
|
759
|
-
const byTag = {};
|
|
760
|
-
for (const entry of entries) {
|
|
761
|
-
const tag = this.getPrimaryTag(entry);
|
|
762
|
-
if (!byTag[tag])
|
|
763
|
-
byTag[tag] = [];
|
|
764
|
-
byTag[tag].push(entry);
|
|
765
|
-
}
|
|
766
|
-
const toMD = (dateStr) => {
|
|
767
|
-
if (!dateStr)
|
|
768
|
-
return '-';
|
|
769
|
-
const normalized = String(dateStr).includes('T') ? dateStr : `${dateStr}T00:00:00Z`;
|
|
770
|
-
const d = new Date(normalized);
|
|
771
|
-
if (Number.isNaN(d.getTime()))
|
|
772
|
-
return '-';
|
|
773
|
-
return `${String(d.getMonth() + 1).padStart(2, '0')}/${String(d.getDate()).padStart(2, '0')}`;
|
|
774
|
-
};
|
|
775
|
-
let body = '';
|
|
776
|
-
for (const [topic, items] of Object.entries(byTag).sort()) {
|
|
777
|
-
const dates = items.map((i) => i.date).filter(Boolean).sort();
|
|
778
|
-
const oldest = dates[0];
|
|
779
|
-
const newest = dates[dates.length - 1];
|
|
780
|
-
const rangeLabel = oldest && newest ? `, ${toMD(oldest)}~${toMD(newest)}` : '';
|
|
781
|
-
body += `## ${topic} (${items.length} items${rangeLabel})\n`;
|
|
782
|
-
for (const e of items)
|
|
783
|
-
body += `- [${e.date}] ${e.summary}\n`;
|
|
784
|
-
for (const e of items)
|
|
785
|
-
body += `ā [entries/${e.file}](entries/${e.file})\n`;
|
|
786
|
-
body += `\n`;
|
|
787
|
-
}
|
|
788
|
-
return body;
|
|
789
|
-
}
|
|
790
|
-
callSummarizer(prompt) {
|
|
791
|
-
const tmpFile = path_1.default.join(os_1.default.tmpdir(), `memory-summarizer-${Date.now()}.txt`);
|
|
792
|
-
try {
|
|
793
|
-
fs_1.default.writeFileSync(tmpFile, prompt, 'utf-8');
|
|
794
|
-
const result = (0, child_process_1.execSync)(`${CREWX_CLI} q "${this.summarizerAgent} $(cat '${tmpFile}')"`, { encoding: 'utf-8', timeout: this.summarizerTimeout, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
795
|
-
return parseCrewxResponse(result);
|
|
796
|
-
}
|
|
797
|
-
catch (_e) {
|
|
798
|
-
return null;
|
|
799
|
-
}
|
|
800
|
-
finally {
|
|
801
|
-
try {
|
|
802
|
-
fs_1.default.unlinkSync(tmpFile);
|
|
803
|
-
}
|
|
804
|
-
catch (_e) {
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
generateSummary(agentId, options = {}) {
|
|
809
|
-
const allEntries = this.loadAllEntries(agentId);
|
|
810
|
-
if (allEntries.length === 0)
|
|
811
|
-
return { status: 'empty' };
|
|
812
|
-
const cutoff = new Date(Date.now() - this.summaryDays * 24 * 60 * 60 * 1000)
|
|
813
|
-
.toISOString()
|
|
814
|
-
.slice(0, 10);
|
|
815
|
-
const entries = options.force
|
|
816
|
-
? allEntries
|
|
817
|
-
: allEntries.filter((e) => e.date >= cutoff);
|
|
818
|
-
if (entries.length === 0)
|
|
819
|
-
return { status: 'empty', entryCount: allEntries.length };
|
|
820
|
-
const summaryPath = this.getSummaryPath(agentId);
|
|
821
|
-
const existing = fs_1.default.existsSync(summaryPath)
|
|
822
|
-
? (0, parser_1.parseFrontmatter)(fs_1.default.readFileSync(summaryPath, 'utf-8'))
|
|
823
|
-
: null;
|
|
824
|
-
const lastEntryId = existing?.data?.last_entry_id ?? null;
|
|
825
|
-
const existingBody = existing?.content.trim() ?? null;
|
|
826
|
-
const getNewSince = (arr, lastId) => {
|
|
827
|
-
if (!lastId)
|
|
828
|
-
return arr;
|
|
829
|
-
const idx = arr.findIndex((e) => e.id === lastId);
|
|
830
|
-
if (idx === -1)
|
|
831
|
-
return arr;
|
|
832
|
-
return arr.slice(0, idx);
|
|
833
|
-
};
|
|
834
|
-
const newEntries = getNewSince(entries, lastEntryId);
|
|
835
|
-
if (newEntries.length === 0 && !options.force) {
|
|
836
|
-
return { status: 'up-to-date', entryCount: allEntries.length };
|
|
837
|
-
}
|
|
838
|
-
const entriesToProcess = options.force ? entries : newEntries;
|
|
839
|
-
const enriched = [...entriesToProcess].reverse().map((e) => {
|
|
840
|
-
if (e.file) {
|
|
841
|
-
try {
|
|
842
|
-
const raw = fs_1.default.readFileSync(path_1.default.join(this.getEntriesDir(agentId), e.file), 'utf-8');
|
|
843
|
-
const { content } = (0, parser_1.parseFrontmatter)(raw);
|
|
844
|
-
const body = content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
845
|
-
return { ...e, body };
|
|
846
|
-
}
|
|
847
|
-
catch (_e) {
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
return e;
|
|
851
|
-
});
|
|
852
|
-
const allTopics = [...new Set(entries.map((e) => this.getPrimaryTag(e)))].sort();
|
|
853
|
-
const buildPrompt = (existBody, newItems, topics) => {
|
|
854
|
-
let p = `You are a memory summarizer for an AI agent.\n\n`;
|
|
855
|
-
if (existBody) {
|
|
856
|
-
p += `## Existing summary:\n${existBody}\n\n## New entries to incorporate:\n`;
|
|
857
|
-
}
|
|
858
|
-
else {
|
|
859
|
-
p += `## All entries:\n`;
|
|
860
|
-
}
|
|
861
|
-
for (const entry of newItems) {
|
|
862
|
-
const importantTag = entry.important ? ' [important]' : '';
|
|
863
|
-
p += `- [${entry.id}] [${entry.date}] [${this.getPrimaryTag(entry)}]${importantTag} ${entry.summary}`;
|
|
864
|
-
if (entry.body && entry.body !== '(Add details here)') {
|
|
865
|
-
p += ` ā ${entry.body.slice(0, 200)}`;
|
|
866
|
-
}
|
|
867
|
-
p += ` (file: entries/${entry.file})\n`;
|
|
868
|
-
}
|
|
869
|
-
p += `\n## Instructions:\n`;
|
|
870
|
-
p += `- Group by topic, each topic as ## heading with entry count and date range, e.g. "## mcp-http (4 items, 01/28~02/14)"\n`;
|
|
871
|
-
p += `- Write concise Korean summary per topic (2-5 sentences)\n`;
|
|
872
|
-
p += `- After each topic summary, list relevant entry file links as "ā [entries/filename](entries/filename)"\n`;
|
|
873
|
-
p += `- Highlight key decisions, insights, and action items\n`;
|
|
874
|
-
p += `- If there's an existing summary, merge new information into it\n`;
|
|
875
|
-
p += `- Topics to cover: ${topics.join(', ')}\n`;
|
|
876
|
-
p += `- Output ONLY the markdown body (topic sections), no frontmatter or top-level heading\n`;
|
|
877
|
-
return p;
|
|
878
|
-
};
|
|
879
|
-
const prompt = buildPrompt(options.force ? null : existingBody, enriched, allTopics);
|
|
880
|
-
const summaryText = this.callSummarizer(prompt);
|
|
881
|
-
const newestEntryId = entries[0]?.id;
|
|
882
|
-
if (!summaryText) {
|
|
883
|
-
const allEnriched = entries.map((e) => {
|
|
884
|
-
if (e.file) {
|
|
885
|
-
try {
|
|
886
|
-
const raw = fs_1.default.readFileSync(path_1.default.join(this.getEntriesDir(agentId), e.file), 'utf-8');
|
|
887
|
-
const { content } = (0, parser_1.parseFrontmatter)(raw);
|
|
888
|
-
const body = content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
889
|
-
return { ...e, body };
|
|
890
|
-
}
|
|
891
|
-
catch (_e) {
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
return e;
|
|
895
|
-
});
|
|
896
|
-
const fallbackBody = this.buildFallbackSummary(allEnriched);
|
|
897
|
-
const fm = (0, parser_1.stringifyFrontmatter)({
|
|
898
|
-
last_entry_id: newestEntryId,
|
|
899
|
-
last_updated: this.getToday(),
|
|
900
|
-
entry_count: allEntries.length,
|
|
901
|
-
summary_days: this.summaryDays,
|
|
902
|
-
summarizer: 'fallback',
|
|
903
|
-
});
|
|
904
|
-
fs_1.default.writeFileSync(summaryPath, `${fm}\n\n${fallbackBody}`, 'utf-8');
|
|
905
|
-
return { status: 'fallback', entryCount: allEntries.length, newEntries: entriesToProcess.length };
|
|
906
|
-
}
|
|
907
|
-
const fm = (0, parser_1.stringifyFrontmatter)({
|
|
908
|
-
last_entry_id: newestEntryId,
|
|
909
|
-
last_updated: this.getToday(),
|
|
910
|
-
entry_count: allEntries.length,
|
|
911
|
-
summary_days: this.summaryDays,
|
|
912
|
-
summarizer: 'memory_summarizer',
|
|
913
|
-
});
|
|
914
|
-
fs_1.default.writeFileSync(summaryPath, `${fm}\n\n${summaryText}\n`, 'utf-8');
|
|
915
|
-
return { status: 'updated', entryCount: allEntries.length, newEntries: entriesToProcess.length };
|
|
916
|
-
}
|
|
917
|
-
summarize(agentId, options = {}) {
|
|
918
|
-
const entries = this.loadAllEntries(agentId);
|
|
919
|
-
if (entries.length === 0) {
|
|
920
|
-
console.log('No memories found.');
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
console.log(`š Starting summary generation (${entries.length} items entries)\n`);
|
|
924
|
-
const result = this.generateSummary(agentId, { force: options.force });
|
|
925
|
-
if (result.status === 'up-to-date') {
|
|
926
|
-
console.log('ā
summary.md is up to date.');
|
|
927
|
-
}
|
|
928
|
-
else if (result.status === 'fallback') {
|
|
929
|
-
console.log(`š Fallback summary generated (AI call failed)`);
|
|
930
|
-
}
|
|
931
|
-
else if (result.status === 'updated') {
|
|
932
|
-
console.log(`š¤ Summary complete (${result.newEntries} items applied)`);
|
|
933
|
-
}
|
|
934
|
-
console.log(` File: data/${agentId}/summary.md`);
|
|
935
|
-
console.log(` entries: ${result.entryCount} items`);
|
|
936
|
-
}
|
|
937
|
-
summarizeDirty() {
|
|
938
|
-
if (!fs_1.default.existsSync(this.dataDir)) {
|
|
939
|
-
console.log(`š” Register debounce cron:`);
|
|
940
|
-
console.log(`npx cron add "*/1 * * * *" "npx memory summarize-dirty" --mode command --name "memory-debounce"`);
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
const agentDirs = fs_1.default
|
|
944
|
-
.readdirSync(this.dataDir, { withFileTypes: true })
|
|
945
|
-
.filter((entry) => entry.isDirectory())
|
|
946
|
-
.map((entry) => entry.name);
|
|
947
|
-
for (const agentId of agentDirs) {
|
|
948
|
-
const dirtyPath = this.getDirtySummaryPath(agentId);
|
|
949
|
-
if (!fs_1.default.existsSync(dirtyPath))
|
|
950
|
-
continue;
|
|
951
|
-
try {
|
|
952
|
-
const result = this.generateSummary(agentId);
|
|
953
|
-
if (result.status === 'updated') {
|
|
954
|
-
console.log(`š¤ ${agentId} summary.md updated (${result.newEntries} items applied)`);
|
|
955
|
-
}
|
|
956
|
-
else if (result.status === 'fallback') {
|
|
957
|
-
console.log(`š ${agentId} Fallback summary generated (AI call failed)`);
|
|
958
|
-
}
|
|
959
|
-
else if (result.status === 'up-to-date') {
|
|
960
|
-
console.log(`ā
${agentId} summary.md is up to date.`);
|
|
961
|
-
}
|
|
962
|
-
else if (result.status === 'empty') {
|
|
963
|
-
console.log(`ā¹ļø ${agentId} has no memories to summarize.`);
|
|
964
|
-
}
|
|
965
|
-
fs_1.default.unlinkSync(dirtyPath);
|
|
966
|
-
}
|
|
967
|
-
catch (e) {
|
|
968
|
-
console.error(`ā ${agentId} summary.md update failed: ${e.message}`);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
console.log(`š” Register debounce cron:`);
|
|
972
|
-
console.log(`npx cron add "*/1 * * * *" "npx memory summarize-dirty" --mode command --name "memory-debounce"`);
|
|
973
|
-
}
|
|
974
|
-
search(agentId, query) {
|
|
975
|
-
const entries = this.loadAllEntries(agentId);
|
|
976
|
-
if (entries.length === 0) {
|
|
977
|
-
console.log('No memories found.');
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
const memoryList = entries
|
|
981
|
-
.map((e) => `[${e.id}] [${e.date}] [${this.getPrimaryTag(e)}] ${e.summary}`)
|
|
982
|
-
.join('\n');
|
|
983
|
-
const task = `Memory list:\n${memoryList}\n\nQuestion: "${query}"`;
|
|
984
|
-
try {
|
|
985
|
-
console.log(`š "${query}" searching... (${this.searcherAgent})\n`);
|
|
986
|
-
const result = (0, child_process_1.execSync)(`${CREWX_CLI} q "${this.searcherAgent} ${task.replace(/"/g, '\\"').replace(/\n/g, ' ')}"`, { encoding: 'utf-8', timeout: this.searcherTimeout, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
987
|
-
const parsed = parseCrewxResponse(result);
|
|
988
|
-
if (parsed) {
|
|
989
|
-
console.log(`## š§ Semantic search results\n`);
|
|
990
|
-
console.log(parsed);
|
|
991
|
-
}
|
|
992
|
-
else {
|
|
993
|
-
console.log(result.trim());
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
catch (error) {
|
|
997
|
-
const err = error;
|
|
998
|
-
if (err.code === 'ETIMEDOUT' || err.killed) {
|
|
999
|
-
console.log('Search failed: timed out (max 5 minutes)');
|
|
1000
|
-
}
|
|
1001
|
-
else {
|
|
1002
|
-
console.log('Search failed:', err.message);
|
|
1003
|
-
}
|
|
1004
|
-
console.log('\nš” Tip: try keyword search with the find command.');
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
exports.MemoryEngine = MemoryEngine;
|
|
1009
|
-
function createMemoryEngine(config) {
|
|
1010
|
-
return new MemoryEngine(config);
|
|
1011
|
-
}
|
|
1012
|
-
var types_1 = require("./types");
|
|
1013
|
-
Object.defineProperty(exports, "CATEGORIES", { enumerable: true, get: function () { return types_1.CATEGORIES; } });
|
|
1014
|
-
//# sourceMappingURL=engine.js.map
|
|
97
|
+
Question: "${t}"`;try{console.log(`\u{1F50D} "${t}" searching... (${this.searcherAgent})
|
|
98
|
+
`);const o=k(["q",`${this.searcherAgent} ${s}`],{encoding:"utf-8",timeout:this.searcherTimeout,stdio:["pipe","pipe","pipe"]});const i=O(o);if(i){console.log(`## \u{1F9E0} Semantic search results
|
|
99
|
+
`);console.log(i)}else{console.log(o.trim())}}catch(o){const i=o;if(i.code==="ETIMEDOUT"||i.killed){console.log("Search failed: timed out (max 5 minutes)")}else{console.log("Search failed:",i.message)}console.log("\n\u{1F4A1} Tip: try keyword search with the find command.")}}}exports.MemoryEngine=L;function Z($){return new L($)}var Y=require("./types");Object.defineProperty(exports,"CATEGORIES",{enumerable:true,get:function(){return Y.CATEGORIES}});
|