mr-memory 3.2.0 → 3.2.2
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/index.ts +35 -25
- package/package.json +1 -1
- package/upload.ts +52 -13
package/index.ts
CHANGED
|
@@ -392,6 +392,36 @@ const memoryRouterPlugin = {
|
|
|
392
392
|
api.logger.info?.("memoryrouter: get your free API key at https://memoryrouter.ai");
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
+
// ==================================================================
|
|
396
|
+
// Workspace sync (debounced, fire-and-forget)
|
|
397
|
+
// ==================================================================
|
|
398
|
+
|
|
399
|
+
let lastSyncMs = 0;
|
|
400
|
+
const SYNC_DEBOUNCE_MS = 60_000; // At most once per 60 seconds
|
|
401
|
+
|
|
402
|
+
const triggerSync = (workspaceDir: string) => {
|
|
403
|
+
if (!memoryKey) return;
|
|
404
|
+
const now = Date.now();
|
|
405
|
+
if (now - lastSyncMs < SYNC_DEBOUNCE_MS) return;
|
|
406
|
+
lastSyncMs = now;
|
|
407
|
+
syncWorkspaceFiles({ workspaceDir, endpoint, memoryKey, embeddings, logging })
|
|
408
|
+
.then((result) => {
|
|
409
|
+
if (result.uploaded > 0 || result.deleted > 0) {
|
|
410
|
+
api.logger.info?.(
|
|
411
|
+
`memoryrouter: sync complete — ${result.uploaded} uploaded, ${result.deleted} deleted, ${result.unchanged} unchanged`,
|
|
412
|
+
);
|
|
413
|
+
} else {
|
|
414
|
+
log(`memoryrouter: sync complete — ${result.unchanged} files unchanged`);
|
|
415
|
+
}
|
|
416
|
+
if (result.errors.length > 0) {
|
|
417
|
+
api.logger.warn?.(`memoryrouter: sync had ${result.errors.length} errors`);
|
|
418
|
+
}
|
|
419
|
+
})
|
|
420
|
+
.catch((err) => {
|
|
421
|
+
log(`memoryrouter: sync error — ${err instanceof Error ? err.message : String(err)}`);
|
|
422
|
+
});
|
|
423
|
+
};
|
|
424
|
+
|
|
395
425
|
// ==================================================================
|
|
396
426
|
// Core: before_agent_start — search memories, inject context
|
|
397
427
|
// ==================================================================
|
|
@@ -496,6 +526,10 @@ const memoryRouterPlugin = {
|
|
|
496
526
|
api.on("before_prompt_build", async (event, ctx) => {
|
|
497
527
|
promptBuildFiredThisRun = true;
|
|
498
528
|
log(`memoryrouter: before_prompt_build fired (sessionKey=${ctx.sessionKey}, promptLen=${event.prompt?.length})`);
|
|
529
|
+
|
|
530
|
+
// Trigger workspace sync (fire-and-forget, debounced — catches new files mid-run)
|
|
531
|
+
if (ctx.workspaceDir) triggerSync(ctx.workspaceDir);
|
|
532
|
+
|
|
499
533
|
try {
|
|
500
534
|
const prompt = event.prompt;
|
|
501
535
|
|
|
@@ -1236,35 +1270,11 @@ const memoryRouterPlugin = {
|
|
|
1236
1270
|
api.logger.info?.(`memoryrouter: active (key: ${memoryKey.slice(0, 6)}...)`);
|
|
1237
1271
|
|
|
1238
1272
|
// ── Auto-sync workspace files (non-blocking) ──
|
|
1239
|
-
// Resolve workspace directory
|
|
1240
1273
|
const configWorkspace = (api.config as any).workspace || (api.config as any).agents?.defaults?.workspace;
|
|
1241
1274
|
const workspaceDir = configWorkspace
|
|
1242
1275
|
? path.resolve(configWorkspace.replace(/^~/, homedir()))
|
|
1243
1276
|
: path.join(homedir(), ".openclaw", "workspace");
|
|
1244
|
-
|
|
1245
|
-
// Fire and forget — don't block service startup
|
|
1246
|
-
syncWorkspaceFiles({
|
|
1247
|
-
workspaceDir,
|
|
1248
|
-
endpoint,
|
|
1249
|
-
memoryKey,
|
|
1250
|
-
embeddings,
|
|
1251
|
-
logging,
|
|
1252
|
-
})
|
|
1253
|
-
.then((result) => {
|
|
1254
|
-
if (result.uploaded > 0 || result.deleted > 0) {
|
|
1255
|
-
api.logger.info?.(
|
|
1256
|
-
`memoryrouter: sync complete — ${result.uploaded} uploaded, ${result.deleted} deleted, ${result.unchanged} unchanged`,
|
|
1257
|
-
);
|
|
1258
|
-
} else {
|
|
1259
|
-
log(`memoryrouter: sync complete — ${result.unchanged} files unchanged`);
|
|
1260
|
-
}
|
|
1261
|
-
if (result.errors.length > 0) {
|
|
1262
|
-
api.logger.warn?.(`memoryrouter: sync had ${result.errors.length} errors`);
|
|
1263
|
-
}
|
|
1264
|
-
})
|
|
1265
|
-
.catch((err) => {
|
|
1266
|
-
api.logger.warn?.(`memoryrouter: sync error — ${err instanceof Error ? err.message : String(err)}`);
|
|
1267
|
-
});
|
|
1277
|
+
triggerSync(workspaceDir);
|
|
1268
1278
|
}
|
|
1269
1279
|
},
|
|
1270
1280
|
stop: () => {
|
package/package.json
CHANGED
package/upload.ts
CHANGED
|
@@ -370,10 +370,11 @@ export async function runUpload(params: {
|
|
|
370
370
|
|
|
371
371
|
const workspacePath = params.workspacePath ?? process.cwd();
|
|
372
372
|
|
|
373
|
-
|
|
373
|
+
// ── Target path: specific file/dir upload (old behavior, no sync) ──
|
|
374
374
|
if (targetPath) {
|
|
375
375
|
const resolved = path.resolve(targetPath);
|
|
376
376
|
const stat = await fs.stat(resolved);
|
|
377
|
+
let files: string[];
|
|
377
378
|
if (stat.isDirectory()) {
|
|
378
379
|
const allDirFiles = await fs.readdir(resolved, { recursive: true });
|
|
379
380
|
files = (allDirFiles as string[])
|
|
@@ -385,19 +386,58 @@ export async function runUpload(params: {
|
|
|
385
386
|
} else {
|
|
386
387
|
files = [resolved];
|
|
387
388
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
389
|
+
await uploadFilesLegacy(files, memoryKey, endpoint, embeddings);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ── Determine what to upload ──
|
|
394
|
+
const doWorkspace = !params.hasBrainFlag || params.hasWorkspaceFlag; // default: yes
|
|
395
|
+
const doSessions = !params.hasWorkspaceFlag || params.hasBrainFlag; // default: yes
|
|
396
|
+
|
|
397
|
+
// ── Workspace files: use source_hash sync (smart, tracks changes) ──
|
|
398
|
+
if (doWorkspace) {
|
|
399
|
+
console.log("📁 Syncing workspace files...");
|
|
400
|
+
const { syncWorkspaceFiles } = await import("./sync.js");
|
|
401
|
+
const syncResult = await syncWorkspaceFiles({
|
|
402
|
+
workspaceDir: workspacePath,
|
|
403
|
+
endpoint,
|
|
404
|
+
memoryKey,
|
|
405
|
+
embeddings,
|
|
406
|
+
logging: true,
|
|
407
|
+
});
|
|
408
|
+
console.log(`\n Workspace: ${syncResult.uploaded} uploaded, ${syncResult.deleted} deleted, ${syncResult.unchanged} unchanged`);
|
|
409
|
+
if (syncResult.errors.length > 0) {
|
|
410
|
+
console.log(` ⚠️ ${syncResult.errors.length} errors`);
|
|
411
|
+
for (const err of syncResult.errors.slice(0, 3)) {
|
|
412
|
+
console.log(` • ${err}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
399
415
|
}
|
|
400
416
|
|
|
417
|
+
// ── Session transcripts: batch upload (append-only, no sync needed) ──
|
|
418
|
+
if (doSessions) {
|
|
419
|
+
console.log("\n📝 Uploading session transcripts...");
|
|
420
|
+
const sessionFiles = await discoverBrainFiles(stateDir);
|
|
421
|
+
if (sessionFiles.length === 0) {
|
|
422
|
+
console.log(" No session files found.");
|
|
423
|
+
} else {
|
|
424
|
+
await uploadFilesLegacy(sessionFiles, memoryKey, endpoint, embeddings);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Legacy batch upload for session transcripts and specific-path uploads.
|
|
431
|
+
* Sessions are append-only, so they don't need source_hash tracking.
|
|
432
|
+
*/
|
|
433
|
+
async function uploadFilesLegacy(
|
|
434
|
+
files: string[],
|
|
435
|
+
memoryKey: string,
|
|
436
|
+
endpoint: string,
|
|
437
|
+
embeddings?: string,
|
|
438
|
+
): Promise<void> {
|
|
439
|
+
const uploadUrl = `${endpoint}/v1/memory/upload`;
|
|
440
|
+
|
|
401
441
|
if (files.length === 0) {
|
|
402
442
|
console.log("No files found to upload.");
|
|
403
443
|
return;
|
|
@@ -496,7 +536,6 @@ export async function runUpload(params: {
|
|
|
496
536
|
if (batchFailed > 0) {
|
|
497
537
|
const errHint = result.errors?.[0] ? ` (${result.errors[0].slice(0, 120)})` : "";
|
|
498
538
|
console.log(`⚠ ${batchStored} stored, ${batchFailed} skipped${errHint}`);
|
|
499
|
-
// Show detailed error diagnostics (up to 5 per batch)
|
|
500
539
|
if (result.errors && result.errors.length > 1) {
|
|
501
540
|
for (const err of result.errors.slice(0, 5)) {
|
|
502
541
|
console.log(` → ${err.slice(0, 200)}`);
|