154 lines
4.1 KiB
TypeScript
154 lines
4.1 KiB
TypeScript
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;
|
|
};
|
|
|
|
const latestCortexCaptureHistoryByKey = new Map<string, CortexCaptureHistoryEntry>();
|
|
|
|
function buildHistoryCacheKey(params: {
|
|
agentId: string;
|
|
sessionId?: string;
|
|
channelId?: string;
|
|
}): string {
|
|
return [params.agentId, params.sessionId ?? "", params.channelId ?? ""].join("\u0000");
|
|
}
|
|
|
|
function cacheHistoryEntry(entry: CortexCaptureHistoryEntry): void {
|
|
latestCortexCaptureHistoryByKey.set(
|
|
buildHistoryCacheKey({
|
|
agentId: entry.agentId,
|
|
sessionId: entry.sessionId,
|
|
channelId: entry.channelId,
|
|
}),
|
|
entry,
|
|
);
|
|
}
|
|
|
|
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<void> {
|
|
const historyPath = resolveHistoryPath(env);
|
|
await fsp.mkdir(path.dirname(historyPath), { recursive: true });
|
|
await fsp.appendFile(historyPath, `${JSON.stringify(entry)}\n`, "utf8");
|
|
cacheHistoryEntry(entry);
|
|
}
|
|
|
|
export async function readRecentCortexCaptureHistory(params?: {
|
|
limit?: number;
|
|
env?: NodeJS.ProcessEnv;
|
|
}): Promise<CortexCaptureHistoryEntry[]> {
|
|
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 function getCachedLatestCortexCaptureHistoryEntry(params: {
|
|
agentId: string;
|
|
sessionId?: string;
|
|
channelId?: string;
|
|
}): CortexCaptureHistoryEntry | null {
|
|
return (
|
|
latestCortexCaptureHistoryByKey.get(
|
|
buildHistoryCacheKey({
|
|
agentId: params.agentId,
|
|
sessionId: params.sessionId,
|
|
channelId: params.channelId,
|
|
}),
|
|
) ?? null
|
|
);
|
|
}
|
|
|
|
export async function getLatestCortexCaptureHistoryEntry(params: {
|
|
agentId: string;
|
|
sessionId?: string;
|
|
channelId?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
}): Promise<CortexCaptureHistoryEntry | null> {
|
|
const recent = await readRecentCortexCaptureHistory({ limit: 100, env: params.env });
|
|
const match =
|
|
recent.find(
|
|
(entry) =>
|
|
entry.agentId === params.agentId &&
|
|
(params.sessionId ? entry.sessionId === params.sessionId : true) &&
|
|
(params.channelId ? entry.channelId === params.channelId : true),
|
|
) ?? null;
|
|
if (match) {
|
|
cacheHistoryEntry(match);
|
|
}
|
|
return match;
|
|
}
|