Skip to content

Commit b3f9f47

Browse files
committed
Make Cognee workspace memory durable
1 parent cc65d45 commit b3f9f47

14 files changed

Lines changed: 1122 additions & 73 deletions

electron/ipc-handlers.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,93 @@ function assertStoreKey(value: unknown): string {
232232
return value;
233233
}
234234

235+
function assertCogneeMemoryLedger(value: unknown): storeLib.StoredCogneeMemoryLedger {
236+
if (!value || typeof value !== "object" || Array.isArray(value)) {
237+
throw new Error("Cognee memory ledger must be an object.");
238+
}
239+
const events = (value as { events?: unknown }).events;
240+
if (!Array.isArray(events)) throw new Error("Cognee memory ledger events must be an array.");
241+
if (events.length > 500) throw new Error("Cognee memory ledger is too large.");
242+
return { events: events.map(assertCogneeMemoryLedgerEvent) };
243+
}
244+
245+
function assertCogneeMemoryLedgerEvent(value: unknown): storeLib.StoredCogneeMemoryLedgerEvent {
246+
if (!value || typeof value !== "object" || Array.isArray(value)) {
247+
throw new Error("Cognee memory ledger event must be an object.");
248+
}
249+
const event = value as Record<string, unknown>;
250+
return {
251+
id: boundedLedgerString(event.id, "id", 120),
252+
operation: assertCogneeMemoryOperation(event.operation),
253+
status: assertCogneeMemoryEventStatus(event.status),
254+
source: boundedLedgerString(event.source, "source", 120),
255+
startedAt: boundedLedgerString(event.startedAt, "startedAt", 80),
256+
durationMs: boundedLedgerNumber(event.durationMs, "durationMs"),
257+
datasetName: optionalLedgerString(event.datasetName, "datasetName", 120),
258+
repo: optionalLedgerString(event.repo, "repo", 300),
259+
branch: optionalLedgerString(event.branch, "branch", 160),
260+
query: optionalLedgerString(event.query, "query", 2_000),
261+
documentCount: boundedLedgerNumber(event.documentCount, "documentCount"),
262+
storedCount: boundedLedgerNumber(event.storedCount, "storedCount"),
263+
recallItemCount: boundedLedgerNumber(event.recallItemCount, "recallItemCount"),
264+
payloadBytes: boundedLedgerNumber(event.payloadBytes, "payloadBytes"),
265+
estimatedTokens: boundedLedgerNumber(event.estimatedTokens, "estimatedTokens"),
266+
accepted: optionalLedgerBoolean(event.accepted, "accepted"),
267+
forgotten: optionalLedgerBoolean(event.forgotten, "forgotten"),
268+
reason: optionalLedgerString(event.reason, "reason", 500),
269+
error: optionalLedgerString(event.error, "error", 500),
270+
titles: assertLedgerTitles(event.titles),
271+
};
272+
}
273+
274+
function assertCogneeMemoryOperation(value: unknown): storeLib.StoredCogneeMemoryLedgerEvent["operation"] {
275+
if (value === "remember" || value === "recall" || value === "improve" || value === "forget") {
276+
return value;
277+
}
278+
throw new Error("Unsupported Cognee memory operation.");
279+
}
280+
281+
function assertCogneeMemoryEventStatus(value: unknown): storeLib.StoredCogneeMemoryLedgerEvent["status"] {
282+
if (value === "succeeded" || value === "skipped" || value === "failed") return value;
283+
throw new Error("Unsupported Cognee memory event status.");
284+
}
285+
286+
function boundedLedgerString(value: unknown, name: string, max: number): string {
287+
if (typeof value !== "string" || !value.trim()) {
288+
throw new Error(`Cognee memory ledger ${name} must be a non-empty string.`);
289+
}
290+
if (value.length > max || /[\0]/.test(value)) {
291+
throw new Error(`Cognee memory ledger ${name} is invalid.`);
292+
}
293+
return value;
294+
}
295+
296+
function optionalLedgerString(value: unknown, name: string, max: number): string | undefined {
297+
if (value === undefined) return undefined;
298+
return boundedLedgerString(value, name, max);
299+
}
300+
301+
function boundedLedgerNumber(value: unknown, name: string): number {
302+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
303+
throw new Error(`Cognee memory ledger ${name} must be a finite non-negative number.`);
304+
}
305+
return Math.round(value);
306+
}
307+
308+
function optionalLedgerBoolean(value: unknown, name: string): boolean | undefined {
309+
if (value === undefined) return undefined;
310+
if (typeof value !== "boolean") {
311+
throw new Error(`Cognee memory ledger ${name} must be a boolean.`);
312+
}
313+
return value;
314+
}
315+
316+
function assertLedgerTitles(value: unknown): string[] {
317+
if (!Array.isArray(value)) throw new Error("Cognee memory ledger titles must be an array.");
318+
if (value.length > 10) throw new Error("Cognee memory ledger titles are too large.");
319+
return value.map((title, index) => boundedLedgerString(title, `titles[${index}]`, 240));
320+
}
321+
235322
function assertGitLabProjectId(value: unknown): string {
236323
if (typeof value !== "string" || !value.trim() || value.length > 300) {
237324
throw new Error("GitLab project id must be a non-empty string.");
@@ -928,6 +1015,11 @@ export function registerIPCHandlers(gitWorker: UtilityProcess) {
9281015
);
9291016
ipcMain.handle("memory:status", () => verifiedCogneeStatus());
9301017
ipcMain.handle("memory:usage", () => cogneeUsage());
1018+
ipcMain.handle("memory:ledger:get", () => storeLib.getCogneeMemoryLedger());
1019+
ipcMain.handle("memory:ledger:set", async (_, payload: unknown) => {
1020+
storeLib.setCogneeMemoryLedger(assertCogneeMemoryLedger(payload));
1021+
});
1022+
ipcMain.handle("memory:ledger:clear", () => storeLib.clearCogneeMemoryLedger());
9311023

9321024
// ============================================================
9331025
// STORE CHANNELS (3)

electron/lib/store.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,33 @@ export interface ActivityItem {
5858
timestamp: number;
5959
}
6060

61+
export interface StoredCogneeMemoryLedgerEvent {
62+
id: string;
63+
operation: "remember" | "recall" | "improve" | "forget";
64+
status: "succeeded" | "skipped" | "failed";
65+
source: string;
66+
startedAt: string;
67+
durationMs: number;
68+
datasetName?: string;
69+
repo?: string;
70+
branch?: string;
71+
query?: string;
72+
documentCount: number;
73+
storedCount: number;
74+
recallItemCount: number;
75+
payloadBytes: number;
76+
estimatedTokens: number;
77+
accepted?: boolean;
78+
forgotten?: boolean;
79+
reason?: string;
80+
error?: string;
81+
titles: string[];
82+
}
83+
84+
export interface StoredCogneeMemoryLedger {
85+
events: StoredCogneeMemoryLedgerEvent[];
86+
}
87+
6188
export interface Settings {
6289
watch_directories: string[];
6390
ai_provider_id?: AIProviderId;
@@ -99,6 +126,7 @@ export interface StoreSchema {
99126
}>;
100127
remote_data_cache: Record<string, unknown>;
101128
activity: ActivityItem[];
129+
cognee_memory_ledger: StoredCogneeMemoryLedger;
102130
settings: StoredSettings;
103131
}
104132

@@ -114,6 +142,7 @@ const store = new Store<StoreSchema>({
114142
ai_audit_log: [],
115143
remote_data_cache: {},
116144
activity: [],
145+
cognee_memory_ledger: { events: [] },
117146
settings: {
118147
watch_directories: ["~/projects", "~/Desktop/persona", "~/Desktop"],
119148
},
@@ -181,6 +210,22 @@ export function clearActivity(): void {
181210
store.set("activity", []);
182211
}
183212

213+
// ============================================================
214+
// COGNEE MEMORY LEDGER
215+
// ============================================================
216+
217+
export function getCogneeMemoryLedger(): StoredCogneeMemoryLedger {
218+
return store.get("cognee_memory_ledger", { events: [] });
219+
}
220+
221+
export function setCogneeMemoryLedger(ledger: StoredCogneeMemoryLedger): void {
222+
store.set("cognee_memory_ledger", ledger);
223+
}
224+
225+
export function clearCogneeMemoryLedger(): void {
226+
store.set("cognee_memory_ledger", { events: [] });
227+
}
228+
184229
// ============================================================
185230
// AI CACHE CRUD
186231
// ============================================================

electron/preload.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,13 @@ const api = {
9797
recall: (payload: unknown) => ipcRenderer.invoke("memory:recall", payload),
9898
improve: (payload: unknown) =>
9999
ipcRenderer.invoke("memory:improve", payload),
100-
forget: (payload: unknown) => ipcRenderer.invoke("memory:forget", payload),
101-
status: () => ipcRenderer.invoke("memory:status"),
102-
usage: () => ipcRenderer.invoke("memory:usage"),
103-
},
100+
forget: (payload: unknown) => ipcRenderer.invoke("memory:forget", payload),
101+
status: () => ipcRenderer.invoke("memory:status"),
102+
usage: () => ipcRenderer.invoke("memory:usage"),
103+
ledgerGet: () => ipcRenderer.invoke("memory:ledger:get"),
104+
ledgerSet: (payload: unknown) => ipcRenderer.invoke("memory:ledger:set", payload),
105+
ledgerClear: () => ipcRenderer.invoke("memory:ledger:clear"),
106+
},
104107

105108
store: {
106109
get: (key: string) => ipcRenderer.invoke("store:get", key),

0 commit comments

Comments
 (0)