@memstack/core 0.2.0 → 0.4.0
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 +144 -56
- package/dist/index.cjs +2212 -262
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +496 -27
- package/dist/index.d.ts +496 -27
- package/dist/index.js +2203 -260
- package/dist/index.js.map +1 -1
- package/package.json +32 -32
- package/src/adapters/embedding/cohere.ts +0 -77
- package/src/adapters/embedding/openai.ts +0 -65
- package/src/adapters/llm/anthropic.ts +0 -69
- package/src/adapters/llm/groq.ts +0 -185
- package/src/adapters/llm/ollama.ts +0 -85
- package/src/adapters/llm/openai.ts +0 -178
- package/src/adapters/storage/disk.ts +0 -334
- package/src/adapters/storage/memory.ts +0 -147
- package/src/adapters/storage/postgres.ts +0 -310
- package/src/adapters/storage/redis.ts +0 -274
- package/src/client.ts +0 -236
- package/src/errors.ts +0 -47
- package/src/index.ts +0 -52
- package/src/interfaces.ts +0 -154
- package/src/memory/ContextCompiler.ts +0 -158
- package/src/memory/MemoryStore.ts +0 -266
- package/src/memory/Pruner.ts +0 -66
- package/src/memory/Summarizer.ts +0 -46
- package/src/types.ts +0 -50
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import type { Memory, CompiledContext } from "../types.js";
|
|
2
|
-
import type {
|
|
3
|
-
StorageProvider,
|
|
4
|
-
EmbeddingProvider,
|
|
5
|
-
LLMProvider,
|
|
6
|
-
MemoryStoreInput,
|
|
7
|
-
MemoryRetrieveQuery,
|
|
8
|
-
MemoryCountFilter,
|
|
9
|
-
ContextOptions,
|
|
10
|
-
SummarizeOptions,
|
|
11
|
-
PruneStrategy,
|
|
12
|
-
} from "../interfaces.js";
|
|
13
|
-
import { Summarizer } from "./Summarizer.js";
|
|
14
|
-
import { Pruner } from "./Pruner.js";
|
|
15
|
-
import { ContextCompiler } from "./ContextCompiler.js";
|
|
16
|
-
import { notFound, validationError } from "../errors.js";
|
|
17
|
-
|
|
18
|
-
export interface MemoryStoreConfig {
|
|
19
|
-
storage: StorageProvider;
|
|
20
|
-
embedding?: EmbeddingProvider;
|
|
21
|
-
llm?: LLMProvider;
|
|
22
|
-
embedOnStore?: boolean;
|
|
23
|
-
summarizationPrompt?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const EMBEDDING_BATCH_SIZE = 2048;
|
|
27
|
-
|
|
28
|
-
export class MemoryStore {
|
|
29
|
-
private storage: StorageProvider;
|
|
30
|
-
private embedding?: EmbeddingProvider;
|
|
31
|
-
private summarizer?: Summarizer;
|
|
32
|
-
private pruner: Pruner;
|
|
33
|
-
private compiler: ContextCompiler;
|
|
34
|
-
private embedOnStore: boolean;
|
|
35
|
-
|
|
36
|
-
constructor(config: MemoryStoreConfig) {
|
|
37
|
-
this.storage = config.storage;
|
|
38
|
-
this.embedding = config.embedding;
|
|
39
|
-
this.embedOnStore = config.embedOnStore ?? true;
|
|
40
|
-
if (config.llm) {
|
|
41
|
-
this.summarizer = new Summarizer(config.llm, config.summarizationPrompt);
|
|
42
|
-
}
|
|
43
|
-
this.pruner = new Pruner();
|
|
44
|
-
this.compiler = new ContextCompiler();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async store(input: MemoryStoreInput): Promise<Memory> {
|
|
48
|
-
// On conflict "append": deduplicate by content hash, update existing instead
|
|
49
|
-
if (input.onConflict === "append") {
|
|
50
|
-
const hash = this._hashContent(input.actorId, input.content);
|
|
51
|
-
const existing = await this.storage.retrieve({ actorId: input.actorId, limit: 1000 });
|
|
52
|
-
const match = existing.find(
|
|
53
|
-
(m) => this._hashContent(m.actorId, m.content) === hash
|
|
54
|
-
);
|
|
55
|
-
if (match) {
|
|
56
|
-
// Upsert: update existing instead of creating duplicate
|
|
57
|
-
const mergedTags = [...new Set([...(match.tags ?? []), ...(input.tags ?? [])])];
|
|
58
|
-
const updated = await this.storage.store({
|
|
59
|
-
id: match.id,
|
|
60
|
-
actorId: match.actorId,
|
|
61
|
-
content: match.content,
|
|
62
|
-
memoryType: match.memoryType,
|
|
63
|
-
importance: input.importance ?? match.importance,
|
|
64
|
-
emotionalValence: input.emotionalValence ?? match.emotionalValence,
|
|
65
|
-
tags: mergedTags,
|
|
66
|
-
embedding: match.embedding,
|
|
67
|
-
sourceId: input.sourceId ?? match.sourceId,
|
|
68
|
-
metadata: { ...match.metadata, ...input.metadata },
|
|
69
|
-
expiresAt: input.expiresAt ?? match.expiresAt,
|
|
70
|
-
});
|
|
71
|
-
return updated;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
let withEmbedding = input;
|
|
76
|
-
if (this.embedding && this.embedOnStore) {
|
|
77
|
-
const embeddings = await this.embedding.embed([input.content]);
|
|
78
|
-
withEmbedding = { ...input, embedding: embeddings[0] };
|
|
79
|
-
}
|
|
80
|
-
const { onConflict: _, ...clean } = withEmbedding;
|
|
81
|
-
return this.storage.store(clean);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async storeBatch(inputs: MemoryStoreInput[]): Promise<Memory[]> {
|
|
85
|
-
// Handle onConflict: "append" inputs individually (they need dedup checks)
|
|
86
|
-
const appendInputs = inputs.filter((i) => i.onConflict === "append");
|
|
87
|
-
const normalInputs = inputs.filter((i) => i.onConflict !== "append");
|
|
88
|
-
|
|
89
|
-
const results: Memory[] = [];
|
|
90
|
-
|
|
91
|
-
// Process append inputs one-by-one through store() for dedup
|
|
92
|
-
for (const input of appendInputs) {
|
|
93
|
-
results.push(await this.store(input));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (normalInputs.length === 0) return results;
|
|
97
|
-
|
|
98
|
-
if (this.embedding && this.embedOnStore) {
|
|
99
|
-
for (let i = 0; i < normalInputs.length; i += EMBEDDING_BATCH_SIZE) {
|
|
100
|
-
const batch = normalInputs.slice(i, i + EMBEDDING_BATCH_SIZE);
|
|
101
|
-
const texts = batch.map((inp) => inp.content);
|
|
102
|
-
const embeddings = await this.embedding!.embed(texts);
|
|
103
|
-
const enriched = batch.map((input, j) => ({ ...input, embedding: embeddings[j] }));
|
|
104
|
-
const stored = await this.storage.storeBatch(enriched);
|
|
105
|
-
results.push(...stored);
|
|
106
|
-
}
|
|
107
|
-
} else {
|
|
108
|
-
const stored = await this.storage.storeBatch(normalInputs);
|
|
109
|
-
results.push(...stored);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return results;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async get(id: string): Promise<Memory | null> {
|
|
116
|
-
return this.storage.get(id);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async delete(id: string): Promise<void> {
|
|
120
|
-
return this.storage.delete(id);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async deleteMany(ids: string[]): Promise<number> {
|
|
124
|
-
return this.storage.deleteMany(ids);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async touch(id: string): Promise<void> {
|
|
128
|
-
const memory = await this.storage.get(id);
|
|
129
|
-
if (!memory) throw notFound("Memory", id);
|
|
130
|
-
if (this.storage.touch) {
|
|
131
|
-
return this.storage.touch(id);
|
|
132
|
-
}
|
|
133
|
-
// legacy fallback: storage doesn't support in-place touch
|
|
134
|
-
await this.storage.delete(id);
|
|
135
|
-
await this.storage.store({
|
|
136
|
-
actorId: memory.actorId,
|
|
137
|
-
content: memory.content,
|
|
138
|
-
memoryType: memory.memoryType,
|
|
139
|
-
importance: memory.importance,
|
|
140
|
-
emotionalValence: memory.emotionalValence,
|
|
141
|
-
tags: memory.tags,
|
|
142
|
-
embedding: memory.embedding,
|
|
143
|
-
sourceId: memory.sourceId,
|
|
144
|
-
metadata: memory.metadata,
|
|
145
|
-
expiresAt: memory.expiresAt,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async count(filter?: MemoryCountFilter): Promise<number> {
|
|
150
|
-
return this.storage.count(filter);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async retrieve(query: MemoryRetrieveQuery): Promise<Memory[]> {
|
|
154
|
-
let embedding: number[] | undefined;
|
|
155
|
-
if (this.embedding && query.query && (query.strategy === "semantic" || query.strategy === "hybrid")) {
|
|
156
|
-
const vecs = await this.embedding.embed([query.query]);
|
|
157
|
-
embedding = vecs[0];
|
|
158
|
-
}
|
|
159
|
-
return this.storage.retrieve(query, embedding);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async compileContext(options: ContextOptions): Promise<CompiledContext> {
|
|
163
|
-
const maxTokens = options.maxTokens ?? 2000;
|
|
164
|
-
// Fetch up to 50 memories; compiler will truncate to meet token budget
|
|
165
|
-
const memories = await this.storage.retrieve({ actorId: options.actorId, limit: 50 });
|
|
166
|
-
return this.compiler.compile(memories, { ...options, maxTokens });
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async summarize(
|
|
170
|
-
options: SummarizeOptions,
|
|
171
|
-
onError?: (err: Error) => void
|
|
172
|
-
): Promise<{ summary: Memory; deletedCount: number }> {
|
|
173
|
-
if (!this.summarizer) {
|
|
174
|
-
throw validationError("No LLM configured for summarization");
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const allMemories = await this.storage.retrieve({
|
|
178
|
-
actorId: options.actorId,
|
|
179
|
-
memoryTypes: options.memoryTypes ?? ["interaction"],
|
|
180
|
-
limit: 1000,
|
|
181
|
-
strategy: "recent",
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
let toSummarize = allMemories;
|
|
185
|
-
if (options.olderThan) {
|
|
186
|
-
toSummarize = toSummarize.filter((m) => m.createdAt < options.olderThan!);
|
|
187
|
-
}
|
|
188
|
-
if (options.skipMostRecent) {
|
|
189
|
-
// Sort chronological (oldest first), skip most recent N, keep the rest
|
|
190
|
-
const sorted = [...toSummarize].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
191
|
-
toSummarize = sorted.slice(0, Math.max(0, sorted.length - options.skipMostRecent));
|
|
192
|
-
}
|
|
193
|
-
if (options.targetCount && toSummarize.length > options.targetCount) {
|
|
194
|
-
// Keep only the oldest targetCount (most stale, best to summarize)
|
|
195
|
-
const sorted = [...toSummarize].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
196
|
-
toSummarize = sorted.slice(0, options.targetCount);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (toSummarize.length === 0) {
|
|
200
|
-
throw validationError("No memories to summarize");
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
let summaryContent: string;
|
|
204
|
-
try {
|
|
205
|
-
const result = await this.summarizer.summarize(toSummarize, options);
|
|
206
|
-
summaryContent = result.summaryContent;
|
|
207
|
-
} catch (err) {
|
|
208
|
-
onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
209
|
-
throw err;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const summary: Memory = await this.storage.store({
|
|
213
|
-
actorId: options.actorId ?? toSummarize[0].actorId,
|
|
214
|
-
content: summaryContent,
|
|
215
|
-
memoryType: "summary",
|
|
216
|
-
importance: Math.max(...toSummarize.map((m) => m.importance)),
|
|
217
|
-
emotionalValence:
|
|
218
|
-
toSummarize.reduce((sum, m) => sum + m.emotionalValence, 0) / toSummarize.length,
|
|
219
|
-
tags: ["summary"],
|
|
220
|
-
sourceId: toSummarize[0].id,
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
let deletedCount = 0;
|
|
224
|
-
if (!options.keepOriginals) {
|
|
225
|
-
const ids = toSummarize.map((m) => m.id);
|
|
226
|
-
deletedCount = await this.storage.deleteMany(ids);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return { summary, deletedCount };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async prune(strategy: PruneStrategy): Promise<{ pruned: string[]; count: number }> {
|
|
233
|
-
const allMemories = await this.storage.retrieve({ limit: 10000 });
|
|
234
|
-
const kept = this.pruner.execute(allMemories, strategy);
|
|
235
|
-
const keptIds = new Set(kept.map((k) => k.id));
|
|
236
|
-
const prunedIds = allMemories.filter((m) => !keptIds.has(m.id)).map((m) => m.id);
|
|
237
|
-
let count = 0;
|
|
238
|
-
if (prunedIds.length > 0) {
|
|
239
|
-
count = await this.storage.deleteMany(prunedIds);
|
|
240
|
-
}
|
|
241
|
-
return { pruned: prunedIds, count };
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
async dryRunPrune(strategy: PruneStrategy): Promise<{ wouldPrune: string[]; count: number }> {
|
|
245
|
-
const allMemories = await this.storage.retrieve({ limit: 10000 });
|
|
246
|
-
const kept = this.pruner.execute(allMemories, strategy);
|
|
247
|
-
const keptIds = new Set(kept.map((k) => k.id));
|
|
248
|
-
const wouldPrune = allMemories.filter((m) => !keptIds.has(m.id)).map((m) => m.id);
|
|
249
|
-
return { wouldPrune, count: wouldPrune.length };
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
async export(): Promise<Memory[]> {
|
|
253
|
-
return this.storage.retrieve({ limit: 100000 });
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
private _hashContent(actorId: string, content: string): string {
|
|
257
|
-
const normalized = `${actorId}::${content.toLowerCase().trim()}`;
|
|
258
|
-
// FNV-1a 64-bit — fast, low collision probability for content dedup
|
|
259
|
-
let hash = 14695981039346656037n; // FNV offset basis
|
|
260
|
-
for (let i = 0; i < normalized.length; i++) {
|
|
261
|
-
hash ^= BigInt(normalized.charCodeAt(i));
|
|
262
|
-
hash *= 1099511628211n; // FNV prime
|
|
263
|
-
}
|
|
264
|
-
return hash.toString(36);
|
|
265
|
-
}
|
|
266
|
-
}
|
package/src/memory/Pruner.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import type { Memory } from "../types.js";
|
|
2
|
-
import type { PruneStrategy } from "../interfaces.js";
|
|
3
|
-
|
|
4
|
-
export class Pruner {
|
|
5
|
-
/** Returns memories to KEEP after applying the strategy. */
|
|
6
|
-
execute(memories: Memory[], strategy: PruneStrategy): Memory[] {
|
|
7
|
-
switch (strategy.type) {
|
|
8
|
-
case "byAge":
|
|
9
|
-
return this.keepByAge(memories, strategy);
|
|
10
|
-
case "byImportance":
|
|
11
|
-
return this.keepByImportance(memories, strategy);
|
|
12
|
-
case "byCount":
|
|
13
|
-
return this.keepByCount(memories, strategy);
|
|
14
|
-
case "byType":
|
|
15
|
-
return this.removeByType(memories, strategy);
|
|
16
|
-
case "custom":
|
|
17
|
-
return this.removeCustom(memories, strategy);
|
|
18
|
-
default:
|
|
19
|
-
return memories;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
dryRun(memories: Memory[], strategy: PruneStrategy): Memory[] {
|
|
24
|
-
return this.execute(memories, strategy);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
private keepByAge(memories: Memory[], strategy: PruneStrategy): Memory[] {
|
|
28
|
-
if (!strategy.maxAge) return memories;
|
|
29
|
-
const cutoff = Date.now() - strategy.maxAge;
|
|
30
|
-
return memories.filter((m) => m.createdAt.getTime() >= cutoff);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
private keepByImportance(memories: Memory[], strategy: PruneStrategy): Memory[] {
|
|
34
|
-
const threshold = strategy.minImportance ?? 0.5;
|
|
35
|
-
return memories.filter((m) => m.importance >= threshold);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
private keepByCount(memories: Memory[], strategy: PruneStrategy): Memory[] {
|
|
39
|
-
const maxPerActor = strategy.maxPerActor ?? 100;
|
|
40
|
-
const grouped = new Map<string, Memory[]>();
|
|
41
|
-
for (const m of memories) {
|
|
42
|
-
const list = grouped.get(m.actorId) || [];
|
|
43
|
-
list.push(m);
|
|
44
|
-
grouped.set(m.actorId, list);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const kept: Memory[] = [];
|
|
48
|
-
for (const [, actorMemories] of grouped) {
|
|
49
|
-
const sorted = [...actorMemories].sort((a, b) => b.importance - a.importance);
|
|
50
|
-
kept.push(...sorted.slice(0, maxPerActor));
|
|
51
|
-
}
|
|
52
|
-
return kept;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Remove memories of specified types. Opposite of filtering — removes the matches. */
|
|
56
|
-
private removeByType(memories: Memory[], strategy: PruneStrategy): Memory[] {
|
|
57
|
-
if (!strategy.memoryTypes || strategy.memoryTypes.length === 0) return memories;
|
|
58
|
-
return memories.filter((m) => !strategy.memoryTypes!.includes(m.memoryType));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Remove memories matching the predicate. Predicate returns true = should be removed. */
|
|
62
|
-
private removeCustom(memories: Memory[], strategy: PruneStrategy): Memory[] {
|
|
63
|
-
if (!strategy.shouldRemove) return memories;
|
|
64
|
-
return memories.filter((m) => !strategy.shouldRemove!(m));
|
|
65
|
-
}
|
|
66
|
-
}
|
package/src/memory/Summarizer.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { LLMProvider } from "../interfaces.js";
|
|
2
|
-
import type { Memory } from "../types.js";
|
|
3
|
-
import type { SummarizeOptions } from "../interfaces.js";
|
|
4
|
-
import { MemStackError } from "../errors.js";
|
|
5
|
-
|
|
6
|
-
export class Summarizer {
|
|
7
|
-
private llm: LLMProvider;
|
|
8
|
-
private defaultPrompt: string;
|
|
9
|
-
|
|
10
|
-
constructor(llm: LLMProvider, defaultPrompt?: string) {
|
|
11
|
-
this.llm = llm;
|
|
12
|
-
this.defaultPrompt = defaultPrompt ??
|
|
13
|
-
"You are a memory summarizer. Condense the following list of memories into a single, concise summary paragraph that captures the key events, emotional tone, and important details. Write in third person past tense.";
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async summarize(
|
|
17
|
-
memories: Memory[],
|
|
18
|
-
options: SummarizeOptions = {},
|
|
19
|
-
promptOverride?: string
|
|
20
|
-
): Promise<{ summaryContent: string; tokenCount: number }> {
|
|
21
|
-
if (memories.length === 0) {
|
|
22
|
-
throw new MemStackError("VALIDATION_ERROR", "No memories to summarize");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const sorted = [...memories].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
26
|
-
const memoryTexts = sorted.map((m) => `[${m.createdAt.toISOString()}] ${m.content}`);
|
|
27
|
-
|
|
28
|
-
const system = promptOverride ?? this.defaultPrompt;
|
|
29
|
-
const user = memoryTexts.join("\n\n");
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const response = await this.llm.complete({ system, user });
|
|
33
|
-
return {
|
|
34
|
-
summaryContent: response.text.trim(),
|
|
35
|
-
tokenCount: response.tokens.total,
|
|
36
|
-
};
|
|
37
|
-
} catch (err) {
|
|
38
|
-
if (err instanceof MemStackError) throw err;
|
|
39
|
-
throw new MemStackError(
|
|
40
|
-
"LLM_ERROR",
|
|
41
|
-
`Summarization failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
42
|
-
{ retryable: true, details: { cause: err instanceof Error ? err.message : String(err) } }
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// ── Memory ──
|
|
2
|
-
|
|
3
|
-
export type MemoryType = "interaction" | "summary" | "observation" | "gossip";
|
|
4
|
-
|
|
5
|
-
export interface Memory {
|
|
6
|
-
id: string;
|
|
7
|
-
actorId: string;
|
|
8
|
-
memoryType: MemoryType;
|
|
9
|
-
content: string;
|
|
10
|
-
importance: number;
|
|
11
|
-
emotionalValence: number;
|
|
12
|
-
tags: string[];
|
|
13
|
-
embedding?: number[];
|
|
14
|
-
sourceId?: string;
|
|
15
|
-
metadata?: Record<string, unknown>;
|
|
16
|
-
expiresAt?: Date;
|
|
17
|
-
createdAt: Date;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// ── Context ──
|
|
21
|
-
|
|
22
|
-
export interface CompiledContext {
|
|
23
|
-
systemPrompt: string;
|
|
24
|
-
recentMemories: Memory[];
|
|
25
|
-
importantMemories: Memory[];
|
|
26
|
-
tokenEstimate: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// ── Process Result ──
|
|
30
|
-
|
|
31
|
-
export interface ProcessResult {
|
|
32
|
-
memory: Memory;
|
|
33
|
-
summaryCreated?: Memory;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ── Snapshot ──
|
|
37
|
-
|
|
38
|
-
export interface MemStackSnapshot {
|
|
39
|
-
version: 1;
|
|
40
|
-
memories: Memory[];
|
|
41
|
-
exportedAt: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ── Health ──
|
|
45
|
-
|
|
46
|
-
export interface HealthStatus {
|
|
47
|
-
storage: boolean;
|
|
48
|
-
llm: boolean;
|
|
49
|
-
embedding: boolean;
|
|
50
|
-
}
|