@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.
Files changed (2) hide show
  1. package/index.js +74 -164
  2. 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
- // --- Setup tool (always registered) ---
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
- api.registerTool({
251
- name: "pentatonic_memory_setup",
252
- description: `Guide the user through setting up Pentatonic Memory.
255
+ const store = hosted
256
+ ? (content, metadata) => hostedStore(config, content, metadata)
257
+ : (content, metadata) => localStore(baseUrl, content, metadata);
253
258
 
254
- Two modes:
255
- 1. "local" fully private, Docker-based (PostgreSQL + pgvector + Ollama). No cloud.
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
- Call this to get instructions for the user's chosen mode.`,
259
- parameters: {
260
- type: "object",
261
- properties: {
262
- mode: { type: "string", enum: ["local", "hosted"], description: "Which mode the user wants" },
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
- Restart OpenClaw to activate.`;
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
- return `## Hosted TES Setup
295
-
296
- Run in terminal:
297
- \`\`\`
298
- npx @pentatonic-ai/ai-agent-sdk init
299
- \`\`\`
300
-
301
- This creates a TES account and generates API credentials.
302
-
303
- Then add to openclaw.json:
304
- \`\`\`json
305
- {
306
- "plugins": {
307
- "slots": { "contextEngine": "pentatonic-memory" },
308
- "entries": {
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
- Restart OpenClaw to activate.`;
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
- // --- Mode-specific registration ---
327
-
328
- if (hosted) {
329
- log("Hosted mode — routing through TES");
330
-
331
- api.registerContextEngine("pentatonic-memory", () =>
332
- createHostedContextEngine(config, {
333
- searchLimit: config.search_limit || 5,
334
- minScore: config.min_score || 0.3,
335
- logger: log,
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
- const mode = hosted ? "hosted" : (config.memory_url || config.database_url) ? "local" : "unconfigured";
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.7",
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",