engram-sdk 0.1.0 → 0.1.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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * temporal.ts — Temporal Intelligence for Engram
3
+ *
4
+ * Handles fact supersession detection and recency-aware scoring.
5
+ * This module addresses the core weakness exposed by Letta's
6
+ * core-memory-update benchmark: when facts change over time,
7
+ * Engram needs to know which version is current.
8
+ *
9
+ * Three strategies (implement in order):
10
+ *
11
+ * 1. Contradiction detection at write time
12
+ * When a new memory is stored, check if it contradicts an
13
+ * existing memory about the same entity+topic. If so, mark
14
+ * the old one as `superseded` and link them with an edge.
15
+ *
16
+ * 2. Recency boost in recall scoring
17
+ * Add a time-decay multiplier so newer memories score higher
18
+ * when content similarity is close. Small effect, but helps
19
+ * break ties in favor of recent information.
20
+ *
21
+ * 3. Temporal spreading activation
22
+ * Weight graph edges by recency — when an entity activates
23
+ * connected memories, more recent connections fire stronger.
24
+ *
25
+ * Target: improve Letta core-memory-update from 41.9% → 65%+
26
+ *
27
+ * @module temporal
28
+ */
29
+ export const DEFAULT_TEMPORAL_CONFIG = {
30
+ detectContradictions: true,
31
+ recencyBoost: {
32
+ enabled: true,
33
+ maxBoost: 0.15,
34
+ halfLifeDays: 7,
35
+ },
36
+ minEntityOverlap: 1,
37
+ contradictionSimilarityThreshold: 0.75,
38
+ };
39
+ // ── Strategy 1: Contradiction Detection ────────────────────
40
+ /**
41
+ * Check if two memories contradict each other.
42
+ *
43
+ * Heuristic approach (no LLM call):
44
+ * - Must share at least one entity
45
+ * - Must share at least one topic OR have high semantic similarity
46
+ * - Content must differ meaningfully (not just rephrasing)
47
+ *
48
+ * For higher accuracy, pass to LLM for verification.
49
+ */
50
+ export function findContradictionCandidates(newMemory, existingMemories, config = DEFAULT_TEMPORAL_CONFIG) {
51
+ const newEntities = new Set(newMemory.entities?.map(e => e.toLowerCase()) ?? []);
52
+ const newTopics = new Set(newMemory.topics?.map(t => t.toLowerCase()) ?? []);
53
+ if (newEntities.size === 0)
54
+ return []; // Can't detect contradictions without entities
55
+ return existingMemories.filter(existing => {
56
+ if (existing.id === newMemory.id)
57
+ return false;
58
+ if (existing.status !== 'active')
59
+ return false;
60
+ // Must share entities
61
+ const existingEntities = new Set(existing.entities?.map(e => e.toLowerCase()) ?? []);
62
+ const sharedEntities = [...newEntities].filter(e => existingEntities.has(e));
63
+ if (sharedEntities.length < config.minEntityOverlap)
64
+ return false;
65
+ // Must share topics or be about the same subject
66
+ const existingTopics = new Set(existing.topics?.map(t => t.toLowerCase()) ?? []);
67
+ const sharedTopics = [...newTopics].filter(t => existingTopics.has(t));
68
+ const hasTopicOverlap = sharedTopics.length > 0;
69
+ // If no topic overlap, require higher entity overlap
70
+ if (!hasTopicOverlap && sharedEntities.length < 2)
71
+ return false;
72
+ // Content must actually differ (not just a duplicate)
73
+ if (existing.content === newMemory.content)
74
+ return false;
75
+ return true;
76
+ });
77
+ }
78
+ /**
79
+ * Use LLM to verify if two memories genuinely contradict each other.
80
+ *
81
+ * Returns null if no contradiction, or a ContradictionResult if confirmed.
82
+ *
83
+ * TODO: Implement with Gemini call
84
+ */
85
+ export async function verifyContradiction(newMemory, oldMemory, llmCall) {
86
+ const prompt = `You are a fact-checking system. Determine if these two statements contradict each other (i.e., the newer one updates or corrects information in the older one).
87
+
88
+ OLDER STATEMENT (written ${oldMemory.createdAt}):
89
+ ${oldMemory.content}
90
+
91
+ NEWER STATEMENT (written ${newMemory.createdAt}):
92
+ ${newMemory.content}
93
+
94
+ Shared entities: ${[...new Set([...(oldMemory.entities ?? []), ...(newMemory.entities ?? [])])].join(', ')}
95
+
96
+ Respond in JSON:
97
+ {
98
+ "contradicts": true/false,
99
+ "confidence": 0.0-1.0,
100
+ "explanation": "brief explanation of what changed, or why they don't contradict"
101
+ }
102
+
103
+ Only mark as contradicting if the newer statement genuinely updates, corrects, or replaces information in the older one. Additions or elaborations are NOT contradictions.`;
104
+ try {
105
+ const response = await llmCall(prompt);
106
+ const cleaned = response.replace(/```json\n?|\n?```/g, '').trim();
107
+ const result = JSON.parse(cleaned);
108
+ if (!result.contradicts)
109
+ return null;
110
+ const newEntities = new Set(newMemory.entities?.map(e => e.toLowerCase()) ?? []);
111
+ const oldEntities = new Set(oldMemory.entities?.map(e => e.toLowerCase()) ?? []);
112
+ const sharedEntities = [...newEntities].filter(e => oldEntities.has(e));
113
+ const newTopics = new Set(newMemory.topics?.map(t => t.toLowerCase()) ?? []);
114
+ const oldTopics = new Set(oldMemory.topics?.map(t => t.toLowerCase()) ?? []);
115
+ const sharedTopics = [...newTopics].filter(t => oldTopics.has(t));
116
+ return {
117
+ newMemoryId: newMemory.id,
118
+ oldMemoryId: oldMemory.id,
119
+ sharedEntities,
120
+ sharedTopics,
121
+ confidence: result.confidence ?? 0.7,
122
+ explanation: result.explanation ?? 'Fact updated',
123
+ };
124
+ }
125
+ catch {
126
+ return null; // LLM parse failure — skip
127
+ }
128
+ }
129
+ // ── Strategy 2: Recency Boost ──────────────────────────────
130
+ /**
131
+ * Calculate a recency boost for a memory based on its age.
132
+ *
133
+ * Uses exponential decay: boost = maxBoost * 2^(-age/halfLife)
134
+ *
135
+ * Examples (with defaults: maxBoost=0.15, halfLife=7 days):
136
+ * - Created today: +0.15
137
+ * - Created 7 days ago: +0.075
138
+ * - Created 14 days ago: +0.0375
139
+ * - Created 30 days ago: +0.009 (negligible)
140
+ *
141
+ * This is additive to the recall score, not multiplicative.
142
+ * It's small enough to not override strong semantic matches,
143
+ * but large enough to break ties between competing facts.
144
+ */
145
+ export function calculateRecencyBoost(memory, config = DEFAULT_TEMPORAL_CONFIG.recencyBoost, now = new Date()) {
146
+ if (!config.enabled)
147
+ return 0;
148
+ const createdAt = new Date(memory.createdAt);
149
+ const ageDays = (now.getTime() - createdAt.getTime()) / (1000 * 60 * 60 * 24);
150
+ if (ageDays < 0)
151
+ return config.maxBoost; // Future date? Give max boost
152
+ if (ageDays === 0)
153
+ return config.maxBoost;
154
+ // Exponential decay: halves every halfLifeDays
155
+ return config.maxBoost * Math.pow(2, -ageDays / config.halfLifeDays);
156
+ }
157
+ // ── Strategy 3: Temporal Spreading Activation ──────────────
158
+ /**
159
+ * Weight a graph edge by recency of the connected memory.
160
+ *
161
+ * When spreading activation traverses the memory graph,
162
+ * edges to more recent memories carry more activation energy.
163
+ *
164
+ * This naturally surfaces the latest version of a fact when
165
+ * multiple memories about the same entity exist.
166
+ *
167
+ * TODO: Integrate with vault.ts spreading activation
168
+ */
169
+ export function temporalEdgeWeight(baseWeight, targetMemory, halfLifeDays = 14, now = new Date()) {
170
+ const ageDays = (now.getTime() - new Date(targetMemory.createdAt).getTime()) / (1000 * 60 * 60 * 24);
171
+ const recencyFactor = Math.pow(2, -ageDays / halfLifeDays);
172
+ // Blend: 70% original weight + 30% recency
173
+ return baseWeight * (0.7 + 0.3 * recencyFactor);
174
+ }
175
+ // ── Integration Points ─────────────────────────────────────
176
+ //
177
+ // To wire this into vault.ts:
178
+ //
179
+ // 1. CONTRADICTION DETECTION (in remember() after dedup):
180
+ // ```
181
+ // if (config.temporal?.detectContradictions) {
182
+ // const candidates = findContradictionCandidates(memory, nearbyMemories);
183
+ // for (const candidate of candidates) {
184
+ // const result = await verifyContradiction(memory, candidate, llmCall);
185
+ // if (result && result.confidence >= 0.7) {
186
+ // this.store.updateStatus(candidate.id, 'superseded');
187
+ // this.store.createEdge(memory.id, candidate.id, 'supersedes', result.confidence);
188
+ // }
189
+ // }
190
+ // }
191
+ // ```
192
+ //
193
+ // 2. RECENCY BOOST (in recall() scoring, step 8):
194
+ // ```
195
+ // const recencyBoost = calculateRecencyBoost(r.memory, config.temporal?.recencyBoost);
196
+ // r.score += recencyBoost;
197
+ // ```
198
+ //
199
+ // 3. TEMPORAL SPREADING ACTIVATION (in spreadingActivation()):
200
+ // ```
201
+ // const weight = temporalEdgeWeight(edge.weight, targetMemory);
202
+ // ```
203
+ //
204
+ // Each can be enabled independently via TemporalConfig.
205
+ //# sourceMappingURL=temporal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temporal.js","sourceRoot":"","sources":["../src/temporal.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -70,13 +70,13 @@ export declare const RememberInputSchema: z.ZodObject<{
70
70
  agentId: z.ZodOptional<z.ZodString>;
71
71
  evidence: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
72
72
  }, "strip", z.ZodTypeAny, {
73
- type: "conversation" | "observation" | "inference" | "consolidation" | "external" | "manual";
73
+ type: "consolidation" | "conversation" | "observation" | "inference" | "external" | "manual";
74
74
  sessionId?: string | undefined;
75
75
  agentId?: string | undefined;
76
76
  evidence?: string[] | undefined;
77
77
  }, {
78
78
  sessionId?: string | undefined;
79
- type?: "conversation" | "observation" | "inference" | "consolidation" | "external" | "manual" | undefined;
79
+ type?: "consolidation" | "conversation" | "observation" | "inference" | "external" | "manual" | undefined;
80
80
  agentId?: string | undefined;
81
81
  evidence?: string[] | undefined;
82
82
  }>>;
@@ -92,7 +92,7 @@ export declare const RememberInputSchema: z.ZodObject<{
92
92
  visibility: "private" | "owner_agents" | "shared" | "public";
93
93
  summary?: string | undefined;
94
94
  source?: {
95
- type: "conversation" | "observation" | "inference" | "consolidation" | "external" | "manual";
95
+ type: "consolidation" | "conversation" | "observation" | "inference" | "external" | "manual";
96
96
  sessionId?: string | undefined;
97
97
  agentId?: string | undefined;
98
98
  evidence?: string[] | undefined;
@@ -110,7 +110,7 @@ export declare const RememberInputSchema: z.ZodObject<{
110
110
  visibility?: "private" | "owner_agents" | "shared" | "public" | undefined;
111
111
  source?: {
112
112
  sessionId?: string | undefined;
113
- type?: "conversation" | "observation" | "inference" | "consolidation" | "external" | "manual" | undefined;
113
+ type?: "consolidation" | "conversation" | "observation" | "inference" | "external" | "manual" | undefined;
114
114
  agentId?: string | undefined;
115
115
  evidence?: string[] | undefined;
116
116
  } | undefined;
@@ -191,6 +191,15 @@ export interface VaultConfig {
191
191
  model?: string;
192
192
  embeddingModel?: string;
193
193
  };
194
+ /** Temporal intelligence settings (contradiction detection, recency boost) */
195
+ temporal?: {
196
+ /** Enable contradiction detection at write time (default: true when LLM is configured) */
197
+ detectContradictions?: boolean;
198
+ /** Minimum entity overlap to consider contradiction (default: 1) */
199
+ minEntityOverlap?: number;
200
+ /** Similarity threshold for contradiction candidates (default: 0.75) */
201
+ contradictionSimilarityThreshold?: number;
202
+ };
194
203
  /** Decay settings */
195
204
  decay?: {
196
205
  /** Base half-life in hours for new memories (default: 168 = 1 week) */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/vault.d.ts CHANGED
@@ -17,6 +17,22 @@ export declare class Vault {
17
17
  * Only dedup within the same type (don't merge episodic into semantic).
18
18
  */
19
19
  private dedup;
20
+ /**
21
+ * Detect contradictions: when a new memory is stored, check if it
22
+ * updates or replaces an existing fact about the same entity.
23
+ *
24
+ * Uses a two-phase approach:
25
+ * 1. Fast heuristic filter (entity/topic overlap) — no LLM cost
26
+ * 2. LLM verification for top candidates — one call per candidate
27
+ *
28
+ * When a contradiction is confirmed, the old memory is marked as
29
+ * `superseded` and linked with a `supersedes` edge. This prevents
30
+ * stale facts from polluting recall results.
31
+ *
32
+ * Only runs when LLM is configured. Fails silently — contradiction
33
+ * detection is best-effort and should never break remember().
34
+ */
35
+ private detectContradictions;
20
36
  /** Compute embedding and store it — can be awaited if needed */
21
37
  computeAndStoreEmbedding(memoryId: string, content: string): Promise<void>;
22
38
  /** Batch compute embeddings for all memories missing them */
@@ -1 +1 @@
1
- {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"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"}
package/dist/vault.js CHANGED
@@ -2,6 +2,7 @@ import path from 'path';
2
2
  import { MemoryStore } from './store.js';
3
3
  import { RememberInputSchema, RecallInputSchema } from './types.js';
4
4
  import { extract } from './extract.js';
5
+ import { calculateRecencyBoost, DEFAULT_TEMPORAL_CONFIG, findContradictionCandidates, verifyContradiction } from './temporal.js';
5
6
  // ============================================================
6
7
  // Vault — The public API for Engram
7
8
  // ============================================================
@@ -54,9 +55,14 @@ export class Vault {
54
55
  .then(() => {
55
56
  // Dedup check: if a very similar memory already exists, merge instead of keeping both
56
57
  this.dedup(memory);
58
+ })
59
+ .then(() => {
60
+ // Contradiction detection: if this memory updates a previous fact,
61
+ // mark the old one as superseded. Only runs when LLM is configured.
62
+ return this.detectContradictions(memory);
57
63
  })
58
64
  .catch(err => {
59
- console.warn(`Failed to compute embedding for ${memory.id}:`, err);
65
+ console.warn(`Failed to process embedding/contradictions for ${memory.id}:`, err);
60
66
  })
61
67
  .finally(() => {
62
68
  this.pendingEmbeddings.delete(p);
@@ -112,6 +118,87 @@ export class Vault {
112
118
  // Dedup is best-effort; don't break remember() if it fails
113
119
  }
114
120
  }
121
+ /**
122
+ * Detect contradictions: when a new memory is stored, check if it
123
+ * updates or replaces an existing fact about the same entity.
124
+ *
125
+ * Uses a two-phase approach:
126
+ * 1. Fast heuristic filter (entity/topic overlap) — no LLM cost
127
+ * 2. LLM verification for top candidates — one call per candidate
128
+ *
129
+ * When a contradiction is confirmed, the old memory is marked as
130
+ * `superseded` and linked with a `supersedes` edge. This prevents
131
+ * stale facts from polluting recall results.
132
+ *
133
+ * Only runs when LLM is configured. Fails silently — contradiction
134
+ * detection is best-effort and should never break remember().
135
+ */
136
+ async detectContradictions(memory) {
137
+ // Skip if LLM not configured or detection disabled
138
+ if (!this.config.llm)
139
+ return;
140
+ if (this.config.temporal?.detectContradictions === false)
141
+ return;
142
+ // Skip if memory was already superseded by dedup
143
+ const current = this.store.getMemoryDirect(memory.id);
144
+ if (!current || current.status !== 'active')
145
+ return;
146
+ // Skip memories with no entities — can't detect contradictions without them
147
+ if (!memory.entities || memory.entities.length === 0)
148
+ return;
149
+ try {
150
+ // Phase 1: Find candidates via vector similarity + entity overlap
151
+ const embedding = this.store.getEmbedding(memory.id);
152
+ if (!embedding)
153
+ return;
154
+ // Wider search than dedup — we want topically similar, not identical
155
+ const similar = this.store.findSimilar(embedding, 0.5, 20);
156
+ const candidateIds = similar
157
+ .filter(s => s.memoryId !== memory.id)
158
+ .map(s => s.memoryId);
159
+ if (candidateIds.length === 0)
160
+ return;
161
+ const candidateMemories = this.store.getMemoriesDirect(candidateIds)
162
+ .filter(m => m.status === 'active');
163
+ // Phase 1b: Heuristic filter — must share entities
164
+ const threshold = this.config.temporal?.contradictionSimilarityThreshold ?? 0.75;
165
+ const minOverlap = this.config.temporal?.minEntityOverlap ?? 1;
166
+ const filtered = findContradictionCandidates(memory, candidateMemories, {
167
+ detectContradictions: true,
168
+ recencyBoost: DEFAULT_TEMPORAL_CONFIG.recencyBoost,
169
+ minEntityOverlap: minOverlap,
170
+ contradictionSimilarityThreshold: threshold,
171
+ });
172
+ if (filtered.length === 0)
173
+ return;
174
+ // Phase 2: LLM verification — check top 3 candidates max
175
+ const llmCall = (prompt) => this.callLLM(this.config.llm.model ?? 'gemini-2.0-flash', prompt, this.config.llm);
176
+ // Only check older memories — newer ones can't be superseded by this memory
177
+ const olderCandidates = filtered
178
+ .filter(c => new Date(c.createdAt) < new Date(memory.createdAt))
179
+ .slice(0, 3);
180
+ for (const candidate of olderCandidates) {
181
+ const result = await verifyContradiction(memory, candidate, llmCall);
182
+ if (result && result.confidence >= 0.7) {
183
+ // Mark the old memory as superseded
184
+ this.store.updateStatus(candidate.id, 'superseded');
185
+ // Create a supersedes edge with the contradiction confidence
186
+ this.store.createEdge(memory.id, candidate.id, 'supersedes', result.confidence);
187
+ // Inherit the old memory's stability (it was a trusted fact)
188
+ const oldStability = candidate.stability;
189
+ if (oldStability > memory.stability) {
190
+ // The new fact inherits some credibility from the old one
191
+ this.store.updateMemory(memory.id, {
192
+ salience: Math.min(1.0, memory.salience + 0.1),
193
+ });
194
+ }
195
+ }
196
+ }
197
+ }
198
+ catch {
199
+ // Contradiction detection is best-effort
200
+ }
201
+ }
115
202
  /** Compute embedding and store it — can be awaited if needed */
116
203
  async computeAndStoreEmbedding(memoryId, content) {
117
204
  if (!this.embedder)
@@ -343,6 +430,9 @@ export class Vault {
343
430
  // Superseded/archived penalty: shouldn't appear in results
344
431
  const statusPenalty = (r.memory.status === 'superseded' || r.memory.status === 'archived') ? 0.5 : 0;
345
432
  r.score = r.score * (0.5 + salienceBoost + stabilityBoost + typeBonus + confidenceBonus) - statusPenalty;
433
+ // Recency boost: newer memories get a small additive bump.
434
+ // Breaks ties between competing facts about the same entity.
435
+ r.score += calculateRecencyBoost(r.memory, DEFAULT_TEMPORAL_CONFIG.recencyBoost);
346
436
  }
347
437
  // 9. Sort by score and return top N
348
438
  results.sort((a, b) => b.score - a.score);