@pentatonic-ai/openclaw-memory-plugin 0.4.7 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +74 -164
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -243,183 +243,93 @@ export default {
|
|
|
243
243
|
const config = api.config || {};
|
|
244
244
|
const hosted = !!(config.tes_endpoint && config.tes_api_key);
|
|
245
245
|
const baseUrl = config.memory_url || "http://localhost:3333";
|
|
246
|
+
const searchLimit = config.search_limit || 5;
|
|
247
|
+
const minScore = config.min_score || 0.3;
|
|
246
248
|
const log = (msg) => process.stderr.write(`[pentatonic-memory] ${msg}\n`);
|
|
247
249
|
|
|
248
|
-
//
|
|
250
|
+
// Unified search/store that routes to local or hosted
|
|
251
|
+
const search = hosted
|
|
252
|
+
? (query, limit, score) => hostedSearch(config, query, limit, score)
|
|
253
|
+
: (query, limit, score) => localSearch(baseUrl, query, limit, score);
|
|
249
254
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
255
|
+
const store = hosted
|
|
256
|
+
? (content, metadata) => hostedStore(config, content, metadata)
|
|
257
|
+
: (content, metadata) => localStore(baseUrl, content, metadata);
|
|
253
258
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
2. "hosted" — Pentatonic TES cloud. Team-wide shared memory, analytics, higher-dimensional embeddings.
|
|
259
|
+
// --- Context engine: always registered, proxies to backend ---
|
|
260
|
+
// Methods return graceful empty results if backend is unavailable.
|
|
257
261
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
},
|
|
264
|
-
required: ["mode"],
|
|
262
|
+
api.registerContextEngine("pentatonic-memory", () => ({
|
|
263
|
+
info: {
|
|
264
|
+
id: "pentatonic-memory",
|
|
265
|
+
name: `Pentatonic Memory (${hosted ? "Hosted" : "Local"})`,
|
|
266
|
+
ownsCompaction: false,
|
|
265
267
|
},
|
|
266
|
-
async execute({ mode }) {
|
|
267
|
-
if (mode === "local") {
|
|
268
|
-
return `## Local Memory Setup
|
|
269
|
-
|
|
270
|
-
Run in terminal:
|
|
271
|
-
\`\`\`
|
|
272
|
-
npx @pentatonic-ai/ai-agent-sdk memory
|
|
273
|
-
\`\`\`
|
|
274
|
-
|
|
275
|
-
This starts PostgreSQL + pgvector, Ollama, and the memory server via Docker.
|
|
276
|
-
|
|
277
|
-
Then add to openclaw.json:
|
|
278
|
-
\`\`\`json
|
|
279
|
-
{
|
|
280
|
-
"plugins": {
|
|
281
|
-
"slots": { "contextEngine": "pentatonic-memory" },
|
|
282
|
-
"entries": {
|
|
283
|
-
"pentatonic-memory": {
|
|
284
|
-
"enabled": true,
|
|
285
|
-
"config": { "memory_url": "http://localhost:3333" }
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
\`\`\`
|
|
291
268
|
|
|
292
|
-
|
|
269
|
+
async ingest({ sessionId, message }) {
|
|
270
|
+
if (!message?.content) return { ingested: false };
|
|
271
|
+
const role = message.role || message.type;
|
|
272
|
+
if (role !== "user" && role !== "assistant") return { ingested: false };
|
|
273
|
+
try {
|
|
274
|
+
await store(message.content, { session_id: sessionId, role });
|
|
275
|
+
return { ingested: true };
|
|
276
|
+
} catch {
|
|
277
|
+
return { ingested: false };
|
|
293
278
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
{
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
"pentatonic-memory": {
|
|
310
|
-
"enabled": true,
|
|
311
|
-
"config": {
|
|
312
|
-
"tes_endpoint": "https://your-company.api.pentatonic.com",
|
|
313
|
-
"tes_client_id": "your-company",
|
|
314
|
-
"tes_api_key": "tes_your-company_xxxxx"
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
async assemble({ sessionId, messages }) {
|
|
282
|
+
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user" || m.type === "user");
|
|
283
|
+
if (!lastUserMsg?.content) return { messages, estimatedTokens: 0 };
|
|
284
|
+
try {
|
|
285
|
+
const results = await search(lastUserMsg.content, searchLimit, minScore);
|
|
286
|
+
if (!results.length) return { messages, estimatedTokens: 0 };
|
|
287
|
+
const memoryText = results
|
|
288
|
+
.map((m) => `- [${Math.round((m.similarity || 0) * 100)}%] ${m.content}`)
|
|
289
|
+
.join("\n");
|
|
290
|
+
const addition = `[Memory] Relevant context from past conversations:\n${memoryText}`;
|
|
291
|
+
return { messages, estimatedTokens: Math.ceil(addition.length / 4), systemPromptAddition: addition };
|
|
292
|
+
} catch {
|
|
293
|
+
return { messages, estimatedTokens: 0 };
|
|
315
294
|
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
async compact() { return { ok: true, compacted: false }; },
|
|
298
|
+
async afterTurn() {},
|
|
299
|
+
}));
|
|
321
300
|
|
|
322
|
-
|
|
301
|
+
// --- Tools: agent-driven search and store ---
|
|
302
|
+
|
|
303
|
+
api.registerTool({
|
|
304
|
+
name: "memory_search",
|
|
305
|
+
description: "Search memories for relevant context. Use when you need to recall past conversations, decisions, or knowledge.",
|
|
306
|
+
parameters: {
|
|
307
|
+
type: "object",
|
|
308
|
+
properties: {
|
|
309
|
+
query: { type: "string", description: "What to search for" },
|
|
310
|
+
limit: { type: "number", description: "Max results (default 5)" },
|
|
311
|
+
},
|
|
312
|
+
required: ["query"],
|
|
313
|
+
},
|
|
314
|
+
async execute({ query, limit }) {
|
|
315
|
+
return formatResults(await search(query, limit || 5, 0.3));
|
|
323
316
|
},
|
|
324
317
|
});
|
|
325
318
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
api.registerTool({
|
|
340
|
-
name: "memory_search",
|
|
341
|
-
description: "Search memories for relevant context.",
|
|
342
|
-
parameters: {
|
|
343
|
-
type: "object",
|
|
344
|
-
properties: {
|
|
345
|
-
query: { type: "string", description: "What to search for" },
|
|
346
|
-
limit: { type: "number", description: "Max results (default 5)" },
|
|
347
|
-
},
|
|
348
|
-
required: ["query"],
|
|
349
|
-
},
|
|
350
|
-
async execute({ query, limit }) {
|
|
351
|
-
return formatResults(await hostedSearch(config, query, limit || 5, 0.3));
|
|
352
|
-
},
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
api.registerTool({
|
|
356
|
-
name: "memory_store",
|
|
357
|
-
description: "Explicitly store something important.",
|
|
358
|
-
parameters: {
|
|
359
|
-
type: "object",
|
|
360
|
-
properties: { content: { type: "string", description: "What to remember" } },
|
|
361
|
-
required: ["content"],
|
|
362
|
-
},
|
|
363
|
-
async execute({ content }) {
|
|
364
|
-
const result = await hostedStore(config, content, { source: "openclaw-tool" });
|
|
365
|
-
return result ? "Memory stored." : "Failed to store memory.";
|
|
366
|
-
},
|
|
367
|
-
});
|
|
368
|
-
} else {
|
|
369
|
-
// Local mode — HTTP to memory server
|
|
370
|
-
const isConfigured = config.memory_url || config.database_url;
|
|
371
|
-
|
|
372
|
-
if (isConfigured) {
|
|
373
|
-
log(`Local mode — ${baseUrl}`);
|
|
374
|
-
|
|
375
|
-
// Check if server is reachable on startup
|
|
376
|
-
localHealth(baseUrl).then((ok) => {
|
|
377
|
-
if (!ok) log(`Warning: memory server not reachable at ${baseUrl}. Is Docker running?`);
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
api.registerContextEngine("pentatonic-memory", () =>
|
|
381
|
-
createLocalContextEngine(baseUrl, {
|
|
382
|
-
searchLimit: config.search_limit || 5,
|
|
383
|
-
minScore: config.min_score || 0.3,
|
|
384
|
-
logger: log,
|
|
385
|
-
})
|
|
386
|
-
);
|
|
387
|
-
|
|
388
|
-
api.registerTool({
|
|
389
|
-
name: "memory_search",
|
|
390
|
-
description: "Search memories for relevant context.",
|
|
391
|
-
parameters: {
|
|
392
|
-
type: "object",
|
|
393
|
-
properties: {
|
|
394
|
-
query: { type: "string", description: "What to search for" },
|
|
395
|
-
limit: { type: "number", description: "Max results (default 5)" },
|
|
396
|
-
},
|
|
397
|
-
required: ["query"],
|
|
398
|
-
},
|
|
399
|
-
async execute({ query, limit }) {
|
|
400
|
-
return formatResults(await localSearch(baseUrl, query, limit || 5, 0.3));
|
|
401
|
-
},
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
api.registerTool({
|
|
405
|
-
name: "memory_store",
|
|
406
|
-
description: "Explicitly store something important.",
|
|
407
|
-
parameters: {
|
|
408
|
-
type: "object",
|
|
409
|
-
properties: { content: { type: "string", description: "What to remember" } },
|
|
410
|
-
required: ["content"],
|
|
411
|
-
},
|
|
412
|
-
async execute({ content }) {
|
|
413
|
-
const result = await localStore(baseUrl, content, { source: "openclaw-tool" });
|
|
414
|
-
return result ? `Stored: ${result.id}` : "Failed to store memory.";
|
|
415
|
-
},
|
|
416
|
-
});
|
|
417
|
-
} else {
|
|
418
|
-
log("No config — setup tool available. Tell OpenClaw: 'set up pentatonic memory'");
|
|
419
|
-
}
|
|
420
|
-
}
|
|
319
|
+
api.registerTool({
|
|
320
|
+
name: "memory_store",
|
|
321
|
+
description: "Explicitly store something important. Use for decisions, solutions, or facts worth remembering.",
|
|
322
|
+
parameters: {
|
|
323
|
+
type: "object",
|
|
324
|
+
properties: { content: { type: "string", description: "What to remember" } },
|
|
325
|
+
required: ["content"],
|
|
326
|
+
},
|
|
327
|
+
async execute({ content }) {
|
|
328
|
+
const result = await store(content, { source: "openclaw-tool" });
|
|
329
|
+
return result ? "Memory stored." : "Failed to store memory.";
|
|
330
|
+
},
|
|
331
|
+
});
|
|
421
332
|
|
|
422
|
-
|
|
423
|
-
log(`Plugin registered (${mode})`);
|
|
333
|
+
log(`Plugin registered (${hosted ? "hosted" : "local"} — ${hosted ? config.tes_endpoint : baseUrl})`);
|
|
424
334
|
},
|
|
425
335
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pentatonic-ai/openclaw-memory-plugin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"description": "Pentatonic Memory plugin for OpenClaw — persistent, searchable memory with multi-signal retrieval and HyDE query expansion",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|