@octamem/octamem-openclaw 1.0.0 → 1.0.3

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/README.md CHANGED
@@ -9,11 +9,7 @@ OctaMem personal memory plugin for [OpenClaw](https://openclaw.ai). Adds search/
9
9
 
10
10
  ## Install
11
11
 
12
- ```bash
13
- npm install @octamem/octamem-openclaw
14
- ```
15
-
16
- Or from the OpenClaw plugins list:
12
+ From the OpenClaw plugins list:
17
13
 
18
14
  ```bash
19
15
  openclaw plugins install @octamem/octamem-openclaw
@@ -42,6 +38,21 @@ Restart OpenClaw after configuring.
42
38
  - **Tools**: `octamem_get` (search) and `octamem_add` (add) for the agent to query or store memory when needed.
43
39
  - **CLI**: `openclaw octamem status | search <query> | details | add <content>`.
44
40
 
41
+ ## Use only OctaMem (disable local/file memory)
42
+
43
+ **`openclaw octamem configure`** automatically adds `tools.deny: ["memory_search", "memory_get"]` to your OpenClaw config so only OctaMem is used (no core/file memory). No manual config edit needed.
44
+
45
+ If you ever need to set it yourself, add to `~/.openclaw/openclaw.json` under `"tools"`:
46
+
47
+ ```json
48
+ "tools": {
49
+ "profile": "coding",
50
+ "deny": ["memory_search", "memory_get"]
51
+ }
52
+ ```
53
+
54
+ Restart OpenClaw after changing config.
55
+
45
56
  ## Config (optional)
46
57
 
47
58
  In OpenClaw plugin config you can set:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octamem/octamem-openclaw",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "description": "OctaMem personal memory plugin for OpenClaw",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -26,7 +26,7 @@ function memoryUnavailable(reason: string) {
26
26
 
27
27
  const MAX_MEMORY_SECTION_LEN = 1200
28
28
 
29
- /** Formats API memory response as readable text for prependContext (no raw JSON). */
29
+ /** Formats API memory response as readable text (used only for CLI/tool display). */
30
30
  function formatMemoryBlock(raw: unknown): string {
31
31
  const data = raw && typeof raw === "object" && "data" in (raw as object)
32
32
  ? (raw as { data?: Record<string, unknown> }).data
@@ -45,6 +45,16 @@ function formatMemoryBlock(raw: unknown): string {
45
45
  return lines.length ? lines.join("\n\n") : "No memory data."
46
46
  }
47
47
 
48
+ /** True when OctaMem data has no memory content (semantic/episodic/procedural all empty). */
49
+ function isMemoryDataEmpty(data: Record<string, unknown>): boolean {
50
+ const s = (v: unknown) => (typeof v === "string" ? v : v != null ? String(v) : "").trim()
51
+ return (
52
+ !s((data as Record<string, unknown>).semantic_memory) &&
53
+ !s((data as Record<string, unknown>).episodic_memory) &&
54
+ !s((data as Record<string, unknown>).procedural_memory)
55
+ )
56
+ }
57
+
48
58
  /**
49
59
  * OpenClaw plugin: OctaMem personal memory (search, add, auto-recall, auto-capture).
50
60
  * Registers tools octamem_get / octamem_add, CLI (octamem status/search/details/add), and lifecycle hooks.
@@ -229,15 +239,50 @@ export default {
229
239
  const r = await client.search(clean, prevCtx)
230
240
  if (!r.success) {
231
241
  const msg = r.error ?? "Search failed."
232
- // appendSystemContext: inject into system prompt so it is not stored with the user message (docs.openclaw.ai/plugin).
233
- return { appendSystemContext: `\n\n### Personal Memory\nIn your response, tell the user: Personal memory is temporarily unavailable. ${msg}` } as { prependContext?: string }
242
+ return {
243
+ appendSystemContext: `\n\n### Personal Memory\nIn your response, tell the user: Personal memory is temporarily unavailable. ${msg}`,
244
+ }
234
245
  }
235
246
  const raw = r.data
247
+ // Pass search response as-is to the LLM (inner data payload as JSON).
248
+ const dataPayload =
249
+ raw && typeof raw === "object" && "data" in (raw as object)
250
+ ? (raw as { data?: unknown }).data
251
+ : raw
252
+ const hasPayload = dataPayload != null && typeof dataPayload === "object"
253
+ const isEmpty =
254
+ !hasPayload ||
255
+ (hasPayload && isMemoryDataEmpty(dataPayload as Record<string, unknown>))
236
256
  const block =
237
- typeof raw === "string"
238
- ? raw
239
- : formatMemoryBlock(raw)
240
- return { appendSystemContext: `\n\n### Personal Memory\n${block}` } as { prependContext?: string }
257
+ hasPayload && !isEmpty
258
+ ? JSON.stringify(dataPayload, null, 2)
259
+ : isEmpty
260
+ ? "No memory data."
261
+ : typeof raw === "string"
262
+ ? raw
263
+ : JSON.stringify(raw ?? {}, null, 2)
264
+ const sourceRule =
265
+ "Personal memory is OctaMem only. If the user asks where memory/data comes from, you MUST answer: from OctaMem. Never say local, workspace, or not Octamem."
266
+ const summarizeInstruction =
267
+ "The following is the raw memory search result from OctaMem. Summarize it in a clear, natural response for the user; do not dump the raw structure. "
268
+ const memoryOnlyInstruction =
269
+ "[SOURCE: All memory below is from OctaMem only. When asked where memory comes from, always answer: from OctaMem. Never say 'local', 'workspace', or 'not Octamem'.] " +
270
+ summarizeInstruction +
271
+ "For 'who is X?', 'what do you know about X?', or any fact about people/places/things the user may have told you: base your answer ONLY on the memory data below. Do not use general knowledge or invent details. " +
272
+ (isEmpty
273
+ ? "[CRITICAL: The section below is EMPTY (No memory data). You MUST reply only that you don't have that in your personal memory (OctaMem). Do not guess, invent, or use your training knowledge.] "
274
+ : "") +
275
+ "When asked the source of memory data, reply: It comes from OctaMem. Never say local or workspace.\n\n"
276
+ const memoryBlockContent = isEmpty
277
+ ? "No memory data.\n[If you answer from general knowledge here, you are wrong. Reply only: I don't have that in my personal memory.]"
278
+ : block
279
+ const prepend = isEmpty
280
+ ? `\n\n[Memory source rule] ${sourceRule}\n[When Personal Memory is empty below, you MUST NOT use general knowledge. Say only: I don't have that in my personal memory.]\n`
281
+ : `\n\n[Memory source rule] ${sourceRule}\n`
282
+ return {
283
+ prependSystemContext: prepend,
284
+ appendSystemContext: `\n\n### Personal Memory (OctaMem)\n${memoryOnlyInstruction}${memoryBlockContent}`,
285
+ }
241
286
  } catch {
242
287
  // skip
243
288
  }
@@ -78,6 +78,18 @@ export function writeOctaMemPluginConfig(
78
78
  plugins.entries = entries
79
79
  data.plugins = plugins
80
80
 
81
+ // So only OctaMem is used for memory: disable core memory tools (file-based).
82
+ const coreMemoryToolsToDeny = ["memory_search", "memory_get"]
83
+ const tools = (data.tools && typeof data.tools === "object"
84
+ ? { ...(data.tools as Record<string, unknown>) }
85
+ : {}) as Record<string, unknown>
86
+ let deny = Array.isArray(tools.deny) ? [...(tools.deny as string[])] : []
87
+ for (const id of coreMemoryToolsToDeny) {
88
+ if (!deny.includes(id)) deny.push(id)
89
+ }
90
+ tools.deny = deny
91
+ data.tools = tools
92
+
81
93
  const dir = path.dirname(configPath)
82
94
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
83
95
  fs.writeFileSync(configPath, JSON.stringify(data, null, 2), "utf-8")