From 0f03c4d2560b4c1b54b04e0bc448f23b334015b2 Mon Sep 17 00:00:00 2001 From: Marc J Saint-jour <82672745+Junebugg1214@users.noreply.github.com> Date: Thu, 12 Mar 2026 18:40:59 -0400 Subject: [PATCH] feat: integrate Cortex local memory into OpenClaw --- src/agents/cortex-history.ts | 112 +++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/agents/cortex-history.ts diff --git a/src/agents/cortex-history.ts b/src/agents/cortex-history.ts new file mode 100644 index 00000000000..3c6e11a6b31 --- /dev/null +++ b/src/agents/cortex-history.ts @@ -0,0 +1,112 @@ +import fs from "node:fs"; +import fsp from "node:fs/promises"; +import path from "node:path"; +import { resolveStateDir } from "../config/paths.js"; + +export type CortexCaptureHistoryEntry = { + agentId: string; + sessionId?: string; + channelId?: string; + captured: boolean; + score: number; + reason: string; + error?: string; + syncedCodingContext?: boolean; + syncPlatforms?: string[]; + timestamp: number; +}; + +function resolveHistoryPath(env: NodeJS.ProcessEnv = process.env): string { + return path.join(resolveStateDir(env), "logs", "cortex-memory-captures.jsonl"); +} + +export async function appendCortexCaptureHistory( + entry: CortexCaptureHistoryEntry, + env: NodeJS.ProcessEnv = process.env, +): Promise { + const historyPath = resolveHistoryPath(env); + await fsp.mkdir(path.dirname(historyPath), { recursive: true }); + await fsp.appendFile(historyPath, `${JSON.stringify(entry)}\n`, "utf8"); +} + +export async function readRecentCortexCaptureHistory(params?: { + limit?: number; + env?: NodeJS.ProcessEnv; +}): Promise { + const historyPath = resolveHistoryPath(params?.env); + let raw: string; + try { + raw = await fsp.readFile(historyPath, "utf8"); + } catch { + return []; + } + const parsed = raw + .split("\n") + .map((line) => line.trim()) + .filter(Boolean) + .map((line) => { + try { + return JSON.parse(line) as CortexCaptureHistoryEntry; + } catch { + return null; + } + }) + .filter((entry): entry is CortexCaptureHistoryEntry => entry != null); + const limit = Math.max(1, params?.limit ?? 20); + return parsed.slice(-limit).toReversed(); +} + +export function getLatestCortexCaptureHistoryEntrySync(params: { + agentId: string; + sessionId?: string; + channelId?: string; + env?: NodeJS.ProcessEnv; +}): CortexCaptureHistoryEntry | null { + const historyPath = resolveHistoryPath(params.env); + let raw: string; + try { + raw = fs.readFileSync(historyPath, "utf8"); + } catch { + return null; + } + const lines = raw + .split("\n") + .map((line) => line.trim()) + .filter(Boolean); + for (let index = lines.length - 1; index >= 0; index -= 1) { + const line = lines[index]; + if (!line) { + continue; + } + try { + const entry = JSON.parse(line) as CortexCaptureHistoryEntry; + if ( + entry.agentId === params.agentId && + (params.sessionId ? entry.sessionId === params.sessionId : true) && + (params.channelId ? entry.channelId === params.channelId : true) + ) { + return entry; + } + } catch { + continue; + } + } + return null; +} + +export async function getLatestCortexCaptureHistoryEntry(params: { + agentId: string; + sessionId?: string; + channelId?: string; + env?: NodeJS.ProcessEnv; +}): Promise { + const recent = await readRecentCortexCaptureHistory({ limit: 100, env: params.env }); + return ( + recent.find( + (entry) => + entry.agentId === params.agentId && + (params.sessionId ? entry.sessionId === params.sessionId : true) && + (params.channelId ? entry.channelId === params.channelId : true), + ) ?? null + ); +}