@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 +16 -5
- package/package.json +1 -1
- package/src/index.ts +52 -7
- package/src/openclaw-config.ts +12 -0
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
|
-
|
|
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
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
|
|
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
|
-
|
|
233
|
-
|
|
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
|
-
|
|
238
|
-
?
|
|
239
|
-
:
|
|
240
|
-
|
|
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
|
}
|
package/src/openclaw-config.ts
CHANGED
|
@@ -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")
|