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)

102 lines (87 loc) 4 kB
/** * offload-client — Plugin registration entry point. * Stateless, server-delegated offload client. * 1 hook (after_tool_call) + 1 Context Engine (assemble → compaction API). */ import type { OffloadClientConfig, Logger } from "./types.js"; import { defaultOffloadClientConfig } from "./types.js"; import { OffloadApiClient } from "./offload-api-client.js"; import { OffloadContextEngine } from "./context-engine.js"; import { createAfterToolCallHandler } from "./hooks/after-tool-call.js"; export interface OpenClawPluginApi { on: (hookName: string, handler: (...args: any[]) => any) => void; registerContextEngine: (id: string, factoryOrInstance: any) => any; logger?: Logger; } /** * Register the offload-client plugin. * Call this from the main plugin's register() when offload-client config is enabled. */ export function registerOffloadClient(api: OpenClawPluginApi, userConfig: Partial<OffloadClientConfig>): void { const config: OffloadClientConfig = { ...defaultOffloadClientConfig(), ...userConfig }; const logger: Logger = api.logger ?? { info: console.log, warn: console.warn, error: console.error, debug: console.debug }; if (!config.enabled) { logger.info("[offload-client] disabled by config"); return; } if (!config.serverUrl || !config.apiKey || !config.serviceId) { logger.error("[offload-client] missing required config: serverUrl, apiKey, or serviceId"); return; } const client = new OffloadApiClient(config, logger); // Context Engine: occupies slot, assemble() calls compaction API const engine = new OffloadContextEngine(client, config, logger); // Hook: fire-and-forget ingest on every tool call (with context from engine per session) const afterToolCallHandler = createAfterToolCallHandler( client, config, logger, (sessionKey) => engine.getContext(sessionKey), config.agentName, // ← NEW: pass agentName for sessionId construction ); api.on("after_tool_call", afterToolCallHandler); // ── Memory management hooks ── // agent_end: clear token cache after each agent turn api.on("agent_end", (_event: any, ctx: { sessionKey?: string; sessionId?: string }) => { const sk = ctx.sessionKey ?? ctx.sessionId; if (sk) { engine.resetSession(sk); logger.debug?.(`[offload-client] reset session state: ${sk}`); } }); // gateway_stop: emergency cleanup on shutdown api.on("gateway_stop", async () => { engine.clearAllSessions(); logger.info("[offload-client] all session states cleared on gateway_stop"); }); try { const result = api.registerContextEngine("memory-tencentdb", () => engine) as any; if (result?.ok === false) { logger.error( `[offload-client] Context Engine slot occupied by "${result.existingOwner ?? "unknown"}". ` + `Compaction disabled — only ingest will work.`, ); } else { logger.info("[offload-client] Context Engine registered"); } } catch (err) { logger.error(`[offload-client] registerContextEngine failed: ${err}. Compaction disabled.`); } // ── Health check (async, non-blocking) ── client.checkHealth().then((ok) => { if (!ok) { logger.warn( `[offload-client] ⚠️ Server ${config.serverUrl} unreachable! ` + `Ingest calls will fail silently until server becomes available.` ); } else { logger.info(`[offload-client] Server health OK: ${config.serverUrl}`); } }).catch((_err) => { // ignore }); logger.info(`[offload-client] registered (server=${config.serverUrl})`); } export { OffloadApiClient } from "./offload-api-client.js"; export { OffloadContextEngine } from "./context-engine.js"; export { createAfterToolCallHandler } from "./hooks/after-tool-call.js"; export { estimateTokens, estimateMessageTokens, estimateAllTokens } from "./token-estimator.js"; export type { OffloadClientConfig, ToolPairPayload, CompactionResult, CompactionReport, Logger } from "./types.js";