UNPKG

@tencentdb-agent-memory/memory-tencentdb

Version:

Four-layer local memory system plugin for OpenClaw — auto-captures, structures, and profiles conversational knowledge using local LLM + SQLite vector search (L0→L1→L2→L3 pipeline)

117 lines (99 loc) 3.42 kB
import { randomUUID } from "node:crypto"; import type { StorageAdapter } from "../storage/adapter.js"; import { StoragePaths } from "../storage/types.js"; export const REPORT_CONST = { PLUGIN: "plugin", } as const; export type ReportPayload = Record<string, unknown>; export interface IReporter { reportFunc(category: string, payload: ReportPayload): void; } // ── Singleton ── let _reporter: IReporter | undefined; export function initReporter(opts: { enabled: boolean; type: string; logger: { info: (msg: string) => void; debug?: (msg: string) => void }; instanceId: string; pluginVersion: string; }): void { if (_reporter) return; if (!opts.enabled) return; switch (opts.type) { case "local": _reporter = new LocalReporter(opts.logger, opts.instanceId, opts.pluginVersion); break; // TODO: add new reporter type default: opts.logger.debug?.(`[memory-tdai] Unknown reporter type "${opts.type}", disabled reporting`); break; } } export function setReporter(reporter: IReporter): void { _reporter = reporter; } /** * Reset the reporter singleton so that the next `initReporter` call takes effect. * Must be called at plugin re-registration (hot-reload) to pick up config changes. */ export function resetReporter(): void { _reporter = undefined; } export function report(event: string, data: ReportPayload): void { if (!_reporter) return; try { _reporter.reportFunc(REPORT_CONST.PLUGIN, { event, ...data }); } catch { /* never block business logic */ } } // ── LocalReporter (default) ── class LocalReporter implements IReporter { constructor( private readonly logger: { info: (msg: string) => void }, private readonly instanceId: string, private readonly pluginVersion: string, ) {} reportFunc(category: string, payload: ReportPayload): void { try { this.logger.info(JSON.stringify({ tag: "METRIC", category, plugin: "memory-tdai", instanceId: this.instanceId, pluginVersion: this.pluginVersion, ts: new Date().toISOString(), ...payload, })); } catch { /* swallow */ } } } // ── Instance ID (persisted per-install) ── let _instanceIdCache: string | undefined; export async function getOrCreateInstanceId(pluginDataDir: string, storage?: StorageAdapter): Promise<string> { if (_instanceIdCache) return _instanceIdCache; try { let existing: string | null; if (storage) { existing = await storage.readFile(StoragePaths.instanceId); } else { const fs = await import("node:fs/promises"); const path = await import("node:path"); existing = await fs.default.readFile(path.default.join(pluginDataDir, ".metadata", "instance_id"), "utf-8"); } if (existing?.trim()) { _instanceIdCache = existing.trim(); return _instanceIdCache; } } catch { /* file doesn't exist */ } const newId = randomUUID(); if (storage) { await storage.writeFile(StoragePaths.instanceId, newId); } else { const fs = await import("node:fs/promises"); const path = await import("node:path"); const idFile = path.default.join(pluginDataDir, ".metadata", "instance_id"); await fs.default.mkdir(path.default.dirname(idFile), { recursive: true }); await fs.default.writeFile(idFile, newId, "utf-8"); } _instanceIdCache = newId; return newId; }