// index.ts import axios from "axios"; const DEFAULT_BASE_URL = "http://192.168.0.200:8420"; const DEFAULT_TIMEOUT_MS = 10000; const DEFAULT_KNOWLEDGE_USER_ID = "knowledge_base"; function getBaseUrl(plugin) { return (plugin?.config?.baseUrl || process.env.MEM0_BASE_URL || DEFAULT_BASE_URL); } function getDefaultUserId(plugin) { return (plugin?.runtime?.agent?.id || globalThis?.openclaw?.agent?.id || plugin?.config?.userId || "default"); } function getKnowledgeUserId(plugin) { return (plugin?.config?.knowledgeUserId || process.env.MEM0_KNOWLEDGE_USER_ID || DEFAULT_KNOWLEDGE_USER_ID); } function http(plugin) { return axios.create({ baseURL: getBaseUrl(plugin), timeout: DEFAULT_TIMEOUT_MS, }); } function normalizeSourceFile(value) { if (!value) return undefined; const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : undefined; } function buildSourceQuery(sourceFile) { return sourceFile .replace(/\.pdf$/i, "") .replace(/[-_]/g, " ") .trim(); } function filterBySource(results, sourceFile) { return results.filter((r) => r?.metadata?.source_file && r.metadata.source_file === sourceFile); } function summarizeKnowledgeResults(results) { const pages = results .map((r) => r?.metadata?.page) .filter((v) => typeof v === "number"); const chapters = results .map((r) => r?.metadata?.chapter) .filter((v) => typeof v === "number"); const created = results .map((r) => r?.created_at) .filter((v) => typeof v === "string"); return { count: results.length, pageRange: pages.length ? [Math.min(...pages), Math.max(...pages)] : null, chapterRange: chapters.length ? [Math.min(...chapters), Math.max(...chapters)] : null, createdAtRange: created.length ? [created.sort()[0], created.sort()[created.length - 1]] : null, }; } export const activate = (plugin) => { const client = http(plugin); // write conversational memory plugin.write = async ({ text, userId }) => { const finalUserId = userId || getDefaultUserId(plugin); if (!text) throw new Error("Missing text for memory write"); const res = await client.post(`/memories`, { text, userId: finalUserId }); return res.data; }; // search conversational memory plugin.search = async ({ query, userId }) => { const finalUserId = userId || getDefaultUserId(plugin); if (!query) throw new Error("Missing query for memory search"); const res = await client.post(`/memories/search`, { query, userId: finalUserId, }); return res.data; }; // search knowledge base plugin.searchKnowledge = async ({ query, userId, limit }) => { const finalUserId = userId || getKnowledgeUserId(plugin); if (!query) throw new Error("Missing query for knowledge search"); const res = await client.post(`/knowledge/search`, { query, userId: finalUserId, limit, }); return res.data; }; // list knowledge sources (books) plugin.listKnowledgeSources = async ({ userId } = {}) => { const finalUserId = userId || getKnowledgeUserId(plugin); const res = await client.post(`/knowledge/sources`, { user_id: finalUserId, }); return res.data; }; // describe a single book by source_file (metadata summary) plugin.describeKnowledgeBook = async ({ sourceFile, userId }) => { const finalUserId = userId || getKnowledgeUserId(plugin); const normalized = normalizeSourceFile(sourceFile); if (!normalized) throw new Error("Missing sourceFile for knowledge describe"); const res = await client.post(`/knowledge/search`, { query: buildSourceQuery(normalized), userId: finalUserId, limit: 200, }); const filtered = filterBySource(res.data?.results || [], normalized); const summary = summarizeKnowledgeResults(filtered); return { sourceFile: normalized, summary, sample: filtered.slice(0, 5), hintQuery: buildSourceQuery(normalized), }; }; // search within a single book (client-side filtered) plugin.searchKnowledgeBook = async ({ query, sourceFile, userId, limit = 8 }) => { const finalUserId = userId || getKnowledgeUserId(plugin); const normalized = normalizeSourceFile(sourceFile); if (!query) throw new Error("Missing query for knowledge search"); if (!normalized) throw new Error("Missing sourceFile for book search"); const res = await client.post(`/knowledge/search`, { query, userId: finalUserId, limit: Math.max(limit, 20), }); const filtered = filterBySource(res.data?.results || [], normalized); return { sourceFile: normalized, results: filtered.slice(0, limit), }; }; // search across a set of books plugin.searchKnowledgeBooks = async ({ query, sourceFiles, userId, limit = 8 }) => { const finalUserId = userId || getKnowledgeUserId(plugin); if (!query) throw new Error("Missing query for knowledge search"); if (!Array.isArray(sourceFiles) || sourceFiles.length === 0) { throw new Error("Missing sourceFiles for multi-book search"); } const res = await client.post(`/knowledge/search`, { query, userId: finalUserId, limit: Math.max(limit * 3, 20), }); const normalizedSources = sourceFiles .map((s) => normalizeSourceFile(s)) .filter(Boolean); const filtered = (res.data?.results || []).filter((r) => normalizedSources.includes(r?.metadata?.source_file)); return { sourceFiles: normalizedSources, results: filtered.slice(0, limit), }; }; // write to knowledge base plugin.writeKnowledge = async ({ text, userId }) => { const finalUserId = userId || getKnowledgeUserId(plugin); if (!text) throw new Error("Missing text for knowledge write"); const res = await client.post(`/knowledge`, { text, userId: finalUserId }); return res.data; }; // read/recall recent memories (requires /memories/recent on server) plugin.read = async ({ userId, limit = 5 }) => { const finalUserId = userId || getDefaultUserId(plugin); const res = await client.post(`/memories/recent`, { userId: finalUserId, limit, }); return res.data.results || []; }; };