@pencil-agent/nano-pencil 1.11.20 → 1.11.21
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.
|
@@ -99,6 +99,7 @@ export declare class NanoMemEngine {
|
|
|
99
99
|
getAllEpisodes(): Promise<Episode[]>;
|
|
100
100
|
runStartupMaintenance(maintenanceVersion?: number): Promise<{
|
|
101
101
|
ran: boolean;
|
|
102
|
+
backupPath?: string;
|
|
102
103
|
deduplicated: {
|
|
103
104
|
knowledge: number;
|
|
104
105
|
lessons: number;
|
|
@@ -110,6 +111,7 @@ export declare class NanoMemEngine {
|
|
|
110
111
|
};
|
|
111
112
|
migratedEpisodesToV2: number;
|
|
112
113
|
}>;
|
|
114
|
+
private createMaintenanceBackup;
|
|
113
115
|
private syncEpisodeToV2;
|
|
114
116
|
private mapEpisodeToV2;
|
|
115
117
|
private makeEpisodeMemoryId;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* [LOCUS]: packages/mem-core/src/engine.ts - facade layer composing all memory subsystems
|
|
5
5
|
* [COVENANT]: Change engine API → update this header and verify against packages/mem-core/CLAUDE.md
|
|
6
6
|
*/
|
|
7
|
+
import { cp, mkdir, readdir } from "node:fs/promises";
|
|
7
8
|
import { join } from "node:path";
|
|
8
9
|
import { getConfig } from "./config.js";
|
|
9
10
|
import { consolidateEpisodes } from "./consolidation.js";
|
|
@@ -660,11 +661,13 @@ export class NanoMemEngine {
|
|
|
660
661
|
if (alreadyMaintained) {
|
|
661
662
|
return {
|
|
662
663
|
ran: false,
|
|
664
|
+
backupPath: undefined,
|
|
663
665
|
deduplicated: { knowledge: 0, lessons: 0, events: 0, preferences: 0, facets: 0, work: 0, total: 0 },
|
|
664
666
|
migratedEpisodesToV2: 0,
|
|
665
667
|
};
|
|
666
668
|
}
|
|
667
669
|
const now = new Date().toISOString();
|
|
670
|
+
const backupPath = await this.createMaintenanceBackup(meta, v2Meta, maintenanceVersion, now);
|
|
668
671
|
const deduplicated = await this.deduplicateAll();
|
|
669
672
|
const episodes = await this.getAllEpisodes();
|
|
670
673
|
for (const episode of episodes) {
|
|
@@ -675,6 +678,8 @@ export class NanoMemEngine {
|
|
|
675
678
|
...(await loadMeta(this.metaPath)),
|
|
676
679
|
lastMaintenanceAt: now,
|
|
677
680
|
lastMaintenanceVersion: maintenanceVersion,
|
|
681
|
+
lastBackupAt: meta.lastBackupAt ?? now,
|
|
682
|
+
lastBackupVersion: Math.max(meta.lastBackupVersion ?? 0, maintenanceVersion),
|
|
678
683
|
}),
|
|
679
684
|
saveV2Meta(this.v2Paths, {
|
|
680
685
|
...(await loadV2Meta(this.v2Paths)),
|
|
@@ -682,14 +687,60 @@ export class NanoMemEngine {
|
|
|
682
687
|
lastMaintenanceAt: now,
|
|
683
688
|
lastMaintenanceVersion: maintenanceVersion,
|
|
684
689
|
lastMigrationAt: (await loadV2Meta(this.v2Paths)).lastMigrationAt ?? now,
|
|
690
|
+
lastBackupAt: v2Meta.lastBackupAt ?? now,
|
|
691
|
+
lastBackupVersion: Math.max(v2Meta.lastBackupVersion ?? 0, maintenanceVersion),
|
|
685
692
|
}),
|
|
686
693
|
]);
|
|
687
694
|
return {
|
|
688
695
|
ran: true,
|
|
696
|
+
backupPath,
|
|
689
697
|
deduplicated,
|
|
690
698
|
migratedEpisodesToV2: episodes.length,
|
|
691
699
|
};
|
|
692
700
|
}
|
|
701
|
+
async createMaintenanceBackup(meta, v2Meta, maintenanceVersion, now) {
|
|
702
|
+
const alreadyBackedUp = (meta.lastBackupVersion ?? 0) >= maintenanceVersion &&
|
|
703
|
+
(v2Meta.lastBackupVersion ?? 0) >= maintenanceVersion;
|
|
704
|
+
if (alreadyBackedUp)
|
|
705
|
+
return undefined;
|
|
706
|
+
const safeTimestamp = now.replace(/[:.]/g, "-");
|
|
707
|
+
const backupRoot = join(this.cfg.memoryDir, "_backups");
|
|
708
|
+
const backupDir = join(backupRoot, `maintenance-v${maintenanceVersion}-${safeTimestamp}`);
|
|
709
|
+
await mkdir(backupDir, { recursive: true });
|
|
710
|
+
const filesToCopy = [
|
|
711
|
+
this.knowledgePath,
|
|
712
|
+
this.lessonsPath,
|
|
713
|
+
this.eventsPath,
|
|
714
|
+
this.preferencesPath,
|
|
715
|
+
this.facetsPath,
|
|
716
|
+
this.workPath,
|
|
717
|
+
this.metaPath,
|
|
718
|
+
];
|
|
719
|
+
for (const filePath of filesToCopy) {
|
|
720
|
+
try {
|
|
721
|
+
await cp(filePath, join(backupDir, filePath.split("/").pop() ?? "unknown.json"));
|
|
722
|
+
}
|
|
723
|
+
catch {
|
|
724
|
+
// Missing files are fine for first-run users.
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
for (const dirName of ["episodes", "v2"]) {
|
|
728
|
+
const sourceDir = join(this.cfg.memoryDir, dirName);
|
|
729
|
+
try {
|
|
730
|
+
await readdir(sourceDir);
|
|
731
|
+
await cp(sourceDir, join(backupDir, dirName), { recursive: true });
|
|
732
|
+
}
|
|
733
|
+
catch {
|
|
734
|
+
// Skip directories that do not exist yet.
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
await writeJson(join(backupDir, "manifest.json"), {
|
|
738
|
+
createdAt: now,
|
|
739
|
+
maintenanceVersion,
|
|
740
|
+
memoryDir: this.cfg.memoryDir,
|
|
741
|
+
});
|
|
742
|
+
return backupDir;
|
|
743
|
+
}
|
|
693
744
|
async syncEpisodeToV2(ep) {
|
|
694
745
|
const [episodes, facets, links, procedural, meta] = await Promise.all([
|
|
695
746
|
loadV2Episodes(this.v2Paths),
|
|
@@ -309,6 +309,9 @@ export default function nanomemExtension(pi) {
|
|
|
309
309
|
const maintenance = await engine.runStartupMaintenance(1);
|
|
310
310
|
if (maintenance.ran && ctx.hasUI) {
|
|
311
311
|
const notes = [];
|
|
312
|
+
if (maintenance.backupPath) {
|
|
313
|
+
notes.push(`backup saved to ${maintenance.backupPath}`);
|
|
314
|
+
}
|
|
312
315
|
if (maintenance.deduplicated.total > 0) {
|
|
313
316
|
notes.push(`deduped ${maintenance.deduplicated.total} entries`);
|
|
314
317
|
}
|
|
@@ -165,6 +165,8 @@ export interface V2Meta {
|
|
|
165
165
|
lastReconsolidationAt?: string;
|
|
166
166
|
lastMaintenanceAt?: string;
|
|
167
167
|
lastMaintenanceVersion?: number;
|
|
168
|
+
lastBackupAt?: string;
|
|
169
|
+
lastBackupVersion?: number;
|
|
168
170
|
}
|
|
169
171
|
export interface NanoMemV2Snapshot {
|
|
170
172
|
episodes: EpisodeMemory[];
|
|
@@ -116,6 +116,8 @@ export interface Meta {
|
|
|
116
116
|
version: number;
|
|
117
117
|
lastMaintenanceAt?: string;
|
|
118
118
|
lastMaintenanceVersion?: number;
|
|
119
|
+
lastBackupAt?: string;
|
|
120
|
+
lastBackupVersion?: number;
|
|
119
121
|
}
|
|
120
122
|
/** Mem0-style update operations */
|
|
121
123
|
export type UpdateAction = "add" | "update" | "delete" | "noop";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pencil-agent/nano-pencil",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.21",
|
|
4
4
|
"description": "CLI writing agent with read, bash, edit, write tools and session management. Based on pi; supports DashScope Coding Plan. Soul enabled by default for AI personality evolution.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|