@oh-my-pi/pi-mnemopi 18.8.4 → 18.8.5
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/CHANGELOG.md +6 -0
- package/package.json +5 -5
- package/src/core/episodic-graph.ts +34 -12
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.8.5] - 2026-10-08
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed new memories gaining graph links (`related_to`, `references`, `ctx`) to memories that were already invalidated, superseded, or expired; proactive linking and consolidation now link only to memories recall can still return, so the graph stops growing toward retired memories ([#14427](https://github.com/can1357/oh-my-pi/pull/14427) by [@tickernelz](https://github.com/tickernelz)).
|
|
10
|
+
|
|
5
11
|
## [18.5.1] - 2026-10-03
|
|
6
12
|
|
|
7
13
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-mnemopi",
|
|
4
|
-
"version": "18.8.
|
|
4
|
+
"version": "18.8.5",
|
|
5
5
|
"description": "Local SQLite memory engine for omp agents",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": {
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@oh-my-pi/pi-ai": "18.8.
|
|
46
|
-
"@oh-my-pi/pi-catalog": "18.8.
|
|
47
|
-
"@oh-my-pi/pi-natives": "18.8.
|
|
48
|
-
"@oh-my-pi/pi-utils": "18.8.
|
|
45
|
+
"@oh-my-pi/pi-ai": "18.8.5",
|
|
46
|
+
"@oh-my-pi/pi-catalog": "18.8.5",
|
|
47
|
+
"@oh-my-pi/pi-natives": "18.8.5",
|
|
48
|
+
"@oh-my-pi/pi-utils": "18.8.5"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"fastembed": "3.0.0",
|
|
@@ -98,14 +98,27 @@ interface EdgeRow {
|
|
|
98
98
|
readonly timestamp: string | null;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
interface MemoryLivenessRow {
|
|
102
|
+
readonly id: string;
|
|
103
|
+
readonly live: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
101
106
|
const EXTRACT_FACTS_MAX_CONTENT_LEN = 4096;
|
|
102
107
|
const MAX_FACTS_PER_MEMORY = 5;
|
|
103
108
|
const DEFAULT_LINK_THRESHOLD = 0.35;
|
|
109
|
+
const LIVE_MEMORY_SQL = "superseded_by IS NULL AND (valid_until IS NULL OR valid_until > ?)";
|
|
104
110
|
|
|
105
111
|
function nowIso(): string {
|
|
106
112
|
return new Date().toISOString();
|
|
107
113
|
}
|
|
108
114
|
|
|
115
|
+
function partitionByLiveness(rows: readonly MemoryLivenessRow[], live: Set<string>, retired: Set<string>): void {
|
|
116
|
+
for (const row of rows) {
|
|
117
|
+
if (row.live === 1) live.add(row.id);
|
|
118
|
+
else retired.add(row.id);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
109
122
|
function unique(values: Iterable<string>, limit = Number.MAX_SAFE_INTEGER): string[] {
|
|
110
123
|
const seen = new Set<string>();
|
|
111
124
|
const out: string[] = [];
|
|
@@ -518,7 +531,7 @@ export class EpisodicGraph {
|
|
|
518
531
|
if (linkExisting) {
|
|
519
532
|
const sourceTokens = contentTokenSet(content);
|
|
520
533
|
for (const otherId of previousMemoryIds) {
|
|
521
|
-
const otherContent = this.memoryContent(otherId);
|
|
534
|
+
const otherContent = this.memoryContent(otherId, timestamp);
|
|
522
535
|
const lexicalScore = Math.round(jaccard(sourceTokens, contentTokenSet(otherContent)) * 1000) / 1000;
|
|
523
536
|
let wroteCtxEdge = false;
|
|
524
537
|
if (lexicalScore >= minLinkScore) {
|
|
@@ -646,33 +659,40 @@ export class EpisodicGraph {
|
|
|
646
659
|
}
|
|
647
660
|
|
|
648
661
|
private knownMemoryIds(exclude: string): string[] {
|
|
662
|
+
const now = nowIso();
|
|
649
663
|
const ids = new Set<string>();
|
|
664
|
+
const live = new Set<string>();
|
|
665
|
+
const retired = new Set<string>();
|
|
650
666
|
const gistRows = this.db
|
|
651
667
|
.query("SELECT DISTINCT memory_id FROM gists WHERE memory_id IS NOT NULL AND memory_id != ?")
|
|
652
668
|
.all(exclude) as { memory_id: string }[];
|
|
653
669
|
for (const row of gistRows) ids.add(row.memory_id);
|
|
654
670
|
try {
|
|
655
|
-
const workingRows = this.db
|
|
656
|
-
id
|
|
657
|
-
|
|
658
|
-
|
|
671
|
+
const workingRows = this.db
|
|
672
|
+
.query(`SELECT id, ${LIVE_MEMORY_SQL} AS live FROM working_memory WHERE id != ?`)
|
|
673
|
+
.all(now, exclude) as MemoryLivenessRow[];
|
|
674
|
+
partitionByLiveness(workingRows, live, retired);
|
|
659
675
|
} catch {
|
|
660
676
|
// Standalone graph stores do not have Beam memory tables.
|
|
661
677
|
}
|
|
662
678
|
try {
|
|
663
|
-
const episodicRows = this.db
|
|
664
|
-
id
|
|
665
|
-
|
|
666
|
-
|
|
679
|
+
const episodicRows = this.db
|
|
680
|
+
.query(`SELECT id, ${LIVE_MEMORY_SQL} AS live FROM episodic_memory WHERE id != ?`)
|
|
681
|
+
.all(now, exclude) as MemoryLivenessRow[];
|
|
682
|
+
partitionByLiveness(episodicRows, live, retired);
|
|
667
683
|
} catch {
|
|
668
684
|
// Standalone graph stores do not have Beam memory tables.
|
|
669
685
|
}
|
|
686
|
+
for (const id of live) ids.add(id);
|
|
687
|
+
for (const id of retired) if (!live.has(id)) ids.delete(id);
|
|
670
688
|
return [...ids];
|
|
671
689
|
}
|
|
672
690
|
|
|
673
|
-
private memoryContent(memoryId: string): string {
|
|
691
|
+
private memoryContent(memoryId: string, now: string): string {
|
|
674
692
|
try {
|
|
675
|
-
const working = this.db
|
|
693
|
+
const working = this.db
|
|
694
|
+
.query(`SELECT content FROM working_memory WHERE id = ? AND ${LIVE_MEMORY_SQL}`)
|
|
695
|
+
.get(memoryId, now) as {
|
|
676
696
|
content: string;
|
|
677
697
|
} | null;
|
|
678
698
|
if (working !== null) return working.content;
|
|
@@ -680,7 +700,9 @@ export class EpisodicGraph {
|
|
|
680
700
|
// Standalone EpisodicGraph users may not have Beam tables.
|
|
681
701
|
}
|
|
682
702
|
try {
|
|
683
|
-
const episodic = this.db
|
|
703
|
+
const episodic = this.db
|
|
704
|
+
.query(`SELECT content FROM episodic_memory WHERE id = ? AND ${LIVE_MEMORY_SQL}`)
|
|
705
|
+
.get(memoryId, now) as {
|
|
684
706
|
content: string;
|
|
685
707
|
} | null;
|
|
686
708
|
if (episodic !== null) return episodic.content;
|