@matrixorigin/thememoria 0.4.0
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 +368 -0
- package/openclaw/client.ts +1030 -0
- package/openclaw/config.ts +629 -0
- package/openclaw/format.ts +55 -0
- package/openclaw/index.ts +2360 -0
- package/openclaw.plugin.json +254 -0
- package/package.json +49 -0
- package/scripts/connect_openclaw_memoria.mjs +172 -0
- package/scripts/install-openclaw-memoria.sh +727 -0
- package/scripts/uninstall-openclaw-memoria.sh +362 -0
- package/scripts/verify_plugin_install.mjs +149 -0
- package/skills/memoria-memory/SKILL.md +43 -0
- package/skills/memoria-recovery/SKILL.md +29 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PLUGIN_ID="memory-memoria"
|
|
5
|
+
DEFAULT_INSTALL_DIR="${HOME}/.local/share/openclaw-plugins/openclaw-memoria"
|
|
6
|
+
|
|
7
|
+
MEMORIA_TOOL_NAMES=(
|
|
8
|
+
memory_search
|
|
9
|
+
memory_get
|
|
10
|
+
memory_store
|
|
11
|
+
memory_retrieve
|
|
12
|
+
memory_recall
|
|
13
|
+
memory_list
|
|
14
|
+
memory_stats
|
|
15
|
+
memory_profile
|
|
16
|
+
memory_correct
|
|
17
|
+
memory_purge
|
|
18
|
+
memory_forget
|
|
19
|
+
memory_health
|
|
20
|
+
memory_observe
|
|
21
|
+
memory_governance
|
|
22
|
+
memory_consolidate
|
|
23
|
+
memory_reflect
|
|
24
|
+
memory_extract_entities
|
|
25
|
+
memory_link_entities
|
|
26
|
+
memory_rebuild_index
|
|
27
|
+
memory_capabilities
|
|
28
|
+
memory_snapshot
|
|
29
|
+
memory_snapshots
|
|
30
|
+
memory_rollback
|
|
31
|
+
memory_branch
|
|
32
|
+
memory_branches
|
|
33
|
+
memory_checkout
|
|
34
|
+
memory_branch_delete
|
|
35
|
+
memory_merge
|
|
36
|
+
memory_diff
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
log() {
|
|
40
|
+
printf '[memory-memoria] %s\n' "$*"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fail() {
|
|
44
|
+
printf '[memory-memoria] error: %s\n' "$*" >&2
|
|
45
|
+
exit 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
need_cmd() {
|
|
49
|
+
command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
usage() {
|
|
53
|
+
cat <<'EOF'
|
|
54
|
+
Remove the OpenClaw Memoria plugin and its OpenClaw config.
|
|
55
|
+
|
|
56
|
+
Usage:
|
|
57
|
+
bash scripts/uninstall-openclaw-memoria.sh [options]
|
|
58
|
+
curl -fsSL <raw-script-url> | bash -s --
|
|
59
|
+
|
|
60
|
+
Options:
|
|
61
|
+
--source-dir <path> Also delete this plugin checkout after uninstall.
|
|
62
|
+
--keep-source Keep the managed install directory on disk.
|
|
63
|
+
--help Show this help text.
|
|
64
|
+
|
|
65
|
+
Environment overrides:
|
|
66
|
+
OPENCLAW_HOME Optional target OpenClaw home.
|
|
67
|
+
|
|
68
|
+
What gets removed by default:
|
|
69
|
+
- plugins.entries["memory-memoria"]
|
|
70
|
+
- plugins.installs["memory-memoria"]
|
|
71
|
+
- plugins.allow entry for memory-memoria
|
|
72
|
+
- plugins.load.paths entries that point at this plugin
|
|
73
|
+
- tool policy entries for the Memoria tool surface
|
|
74
|
+
- managed companion skills in ~/.openclaw/skills: memoria-memory, memoria-recovery
|
|
75
|
+
- the default managed plugin dir: ~/.local/share/openclaw-plugins/openclaw-memoria
|
|
76
|
+
|
|
77
|
+
What gets restored:
|
|
78
|
+
- plugins.slots.memory -> memory-core
|
|
79
|
+
- plugins.entries["memory-core"].enabled -> true
|
|
80
|
+
EOF
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
config_file_path() {
|
|
84
|
+
if [[ -n "${OPENCLAW_HOME_VALUE}" ]]; then
|
|
85
|
+
printf '%s/.openclaw/openclaw.json' "${OPENCLAW_HOME_VALUE}"
|
|
86
|
+
else
|
|
87
|
+
printf '%s/.openclaw/openclaw.json' "${HOME}"
|
|
88
|
+
fi
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
skills_dir_path() {
|
|
92
|
+
if [[ -n "${OPENCLAW_HOME_VALUE}" ]]; then
|
|
93
|
+
printf '%s/.openclaw/skills' "${OPENCLAW_HOME_VALUE}"
|
|
94
|
+
else
|
|
95
|
+
printf '%s/.openclaw/skills' "${HOME}"
|
|
96
|
+
fi
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
OPENCLAW_HOME_VALUE="${OPENCLAW_HOME:-}"
|
|
100
|
+
SOURCE_DIR=""
|
|
101
|
+
KEEP_SOURCE=false
|
|
102
|
+
|
|
103
|
+
while [[ $# -gt 0 ]]; do
|
|
104
|
+
case "$1" in
|
|
105
|
+
--source-dir)
|
|
106
|
+
SOURCE_DIR="${2:?missing value for --source-dir}"
|
|
107
|
+
shift 2
|
|
108
|
+
;;
|
|
109
|
+
--keep-source)
|
|
110
|
+
KEEP_SOURCE=true
|
|
111
|
+
shift
|
|
112
|
+
;;
|
|
113
|
+
--help|-h)
|
|
114
|
+
usage
|
|
115
|
+
exit 0
|
|
116
|
+
;;
|
|
117
|
+
*)
|
|
118
|
+
fail "Unknown option: $1"
|
|
119
|
+
;;
|
|
120
|
+
esac
|
|
121
|
+
done
|
|
122
|
+
|
|
123
|
+
need_cmd node
|
|
124
|
+
|
|
125
|
+
CONFIG_FILE="$(config_file_path)"
|
|
126
|
+
MANAGED_SKILLS_DIR="$(skills_dir_path)"
|
|
127
|
+
MEMORIA_TOOL_NAMES_JSON="$(printf '%s\n' "${MEMORIA_TOOL_NAMES[@]}" | node -e 'const fs=require("node:fs"); const lines=fs.readFileSync(0,"utf8").trim().split(/\n+/).filter(Boolean); process.stdout.write(JSON.stringify(lines));')"
|
|
128
|
+
|
|
129
|
+
UNINSTALL_RESULT="$(
|
|
130
|
+
CONFIG_FILE="${CONFIG_FILE}" \
|
|
131
|
+
PLUGIN_ID="${PLUGIN_ID}" \
|
|
132
|
+
DEFAULT_INSTALL_DIR="${DEFAULT_INSTALL_DIR}" \
|
|
133
|
+
SOURCE_DIR_RAW="${SOURCE_DIR}" \
|
|
134
|
+
KEEP_SOURCE="${KEEP_SOURCE}" \
|
|
135
|
+
MEMORIA_TOOL_NAMES_JSON="${MEMORIA_TOOL_NAMES_JSON}" \
|
|
136
|
+
node - <<'NODE'
|
|
137
|
+
const fs = require("node:fs");
|
|
138
|
+
const path = require("node:path");
|
|
139
|
+
|
|
140
|
+
const configPath = path.resolve(process.env.CONFIG_FILE);
|
|
141
|
+
const pluginId = process.env.PLUGIN_ID;
|
|
142
|
+
const defaultInstallDir = path.resolve(process.env.DEFAULT_INSTALL_DIR);
|
|
143
|
+
const sourceDirRaw = process.env.SOURCE_DIR_RAW || "";
|
|
144
|
+
const keepSource = process.env.KEEP_SOURCE === "true";
|
|
145
|
+
const memoriaToolNames = JSON.parse(process.env.MEMORIA_TOOL_NAMES_JSON);
|
|
146
|
+
|
|
147
|
+
function readJson(filePath) {
|
|
148
|
+
if (!fs.existsSync(filePath)) {
|
|
149
|
+
return {};
|
|
150
|
+
}
|
|
151
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function writeJson(filePath, value) {
|
|
155
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
156
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function maybePath(rawPath) {
|
|
160
|
+
if (!rawPath) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
return path.resolve(rawPath.replace(/^~(?=$|\/|\\)/, process.env.HOME || "~"));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function manifestPluginId(candidatePath) {
|
|
167
|
+
try {
|
|
168
|
+
const manifestPath = path.join(candidatePath, "openclaw.plugin.json");
|
|
169
|
+
if (!fs.existsSync(manifestPath)) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
173
|
+
return typeof manifest.id === "string" ? manifest.id : null;
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const data = readJson(configPath);
|
|
180
|
+
let changed = false;
|
|
181
|
+
const plugins = data.plugins && typeof data.plugins === "object" && !Array.isArray(data.plugins)
|
|
182
|
+
? data.plugins
|
|
183
|
+
: {};
|
|
184
|
+
const tools = data.tools && typeof data.tools === "object" && !Array.isArray(data.tools)
|
|
185
|
+
? data.tools
|
|
186
|
+
: {};
|
|
187
|
+
|
|
188
|
+
const candidatePaths = new Set([defaultInstallDir]);
|
|
189
|
+
const sourceDir = maybePath(sourceDirRaw);
|
|
190
|
+
if (sourceDir) {
|
|
191
|
+
candidatePaths.add(sourceDir);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (plugins.entries && typeof plugins.entries === "object" && !Array.isArray(plugins.entries)) {
|
|
195
|
+
if (pluginId in plugins.entries) {
|
|
196
|
+
delete plugins.entries[pluginId];
|
|
197
|
+
changed = true;
|
|
198
|
+
}
|
|
199
|
+
const quotedKey = JSON.stringify(pluginId);
|
|
200
|
+
if (quotedKey in plugins.entries) {
|
|
201
|
+
delete plugins.entries[quotedKey];
|
|
202
|
+
changed = true;
|
|
203
|
+
}
|
|
204
|
+
if (Object.keys(plugins.entries).length === 0) {
|
|
205
|
+
delete plugins.entries;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (plugins.installs && typeof plugins.installs === "object" && !Array.isArray(plugins.installs)) {
|
|
210
|
+
if (pluginId in plugins.installs) {
|
|
211
|
+
delete plugins.installs[pluginId];
|
|
212
|
+
changed = true;
|
|
213
|
+
}
|
|
214
|
+
if (Object.keys(plugins.installs).length === 0) {
|
|
215
|
+
delete plugins.installs;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (Array.isArray(plugins.allow)) {
|
|
220
|
+
const next = plugins.allow.filter((entry) => entry !== pluginId);
|
|
221
|
+
if (next.length !== plugins.allow.length) {
|
|
222
|
+
plugins.allow = next;
|
|
223
|
+
changed = true;
|
|
224
|
+
}
|
|
225
|
+
if (plugins.allow.length === 0) {
|
|
226
|
+
delete plugins.allow;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (plugins.load && typeof plugins.load === "object" && !Array.isArray(plugins.load) && Array.isArray(plugins.load.paths)) {
|
|
231
|
+
const next = plugins.load.paths.filter((entry) => {
|
|
232
|
+
if (typeof entry !== "string") {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
const resolved = maybePath(entry);
|
|
236
|
+
if (resolved && candidatePaths.has(resolved)) {
|
|
237
|
+
changed = true;
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
if (resolved && manifestPluginId(resolved) === pluginId) {
|
|
241
|
+
changed = true;
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
if ((entry.includes("openclaw-memoria") || entry.includes(pluginId)) && (!resolved || !fs.existsSync(resolved))) {
|
|
245
|
+
changed = true;
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
});
|
|
250
|
+
plugins.load.paths = next;
|
|
251
|
+
if (plugins.load.paths.length === 0) {
|
|
252
|
+
delete plugins.load.paths;
|
|
253
|
+
}
|
|
254
|
+
if (Object.keys(plugins.load).length === 0) {
|
|
255
|
+
delete plugins.load;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (plugins.slots && typeof plugins.slots === "object" && !Array.isArray(plugins.slots) && plugins.slots.memory === pluginId) {
|
|
260
|
+
plugins.slots.memory = "memory-core";
|
|
261
|
+
plugins.entries = plugins.entries && typeof plugins.entries === "object" && !Array.isArray(plugins.entries)
|
|
262
|
+
? plugins.entries
|
|
263
|
+
: {};
|
|
264
|
+
const coreEntry = plugins.entries["memory-core"] && typeof plugins.entries["memory-core"] === "object" && !Array.isArray(plugins.entries["memory-core"])
|
|
265
|
+
? plugins.entries["memory-core"]
|
|
266
|
+
: (plugins.entries["memory-core"] = {});
|
|
267
|
+
coreEntry.enabled = true;
|
|
268
|
+
changed = true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
for (const key of ["allow", "alsoAllow"]) {
|
|
272
|
+
if (Array.isArray(tools[key])) {
|
|
273
|
+
const next = tools[key].filter((entry) => !memoriaToolNames.includes(entry));
|
|
274
|
+
if (next.length !== tools[key].length) {
|
|
275
|
+
tools[key] = next;
|
|
276
|
+
changed = true;
|
|
277
|
+
}
|
|
278
|
+
if (tools[key].length === 0) {
|
|
279
|
+
delete tools[key];
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (data.agents && typeof data.agents === "object" && Array.isArray(data.agents.list)) {
|
|
285
|
+
for (const agent of data.agents.list) {
|
|
286
|
+
if (!agent || typeof agent !== "object" || Array.isArray(agent) || !agent.tools || typeof agent.tools !== "object" || Array.isArray(agent.tools)) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
for (const key of ["allow", "alsoAllow"]) {
|
|
290
|
+
if (Array.isArray(agent.tools[key])) {
|
|
291
|
+
const next = agent.tools[key].filter((entry) => !memoriaToolNames.includes(entry));
|
|
292
|
+
if (next.length !== agent.tools[key].length) {
|
|
293
|
+
agent.tools[key] = next;
|
|
294
|
+
changed = true;
|
|
295
|
+
}
|
|
296
|
+
if (agent.tools[key].length === 0) {
|
|
297
|
+
delete agent.tools[key];
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (Object.keys(agent.tools).length === 0) {
|
|
302
|
+
delete agent.tools;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (Object.keys(plugins).length > 0) {
|
|
308
|
+
data.plugins = plugins;
|
|
309
|
+
} else {
|
|
310
|
+
delete data.plugins;
|
|
311
|
+
}
|
|
312
|
+
if (Object.keys(tools).length > 0) {
|
|
313
|
+
data.tools = tools;
|
|
314
|
+
} else {
|
|
315
|
+
delete data.tools;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (changed && fs.existsSync(configPath)) {
|
|
319
|
+
writeJson(`${configPath}.bak`, readJson(configPath));
|
|
320
|
+
writeJson(configPath, data);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const deletedPaths = [];
|
|
324
|
+
if (!keepSource && fs.existsSync(defaultInstallDir)) {
|
|
325
|
+
fs.rmSync(defaultInstallDir, { recursive: true, force: true });
|
|
326
|
+
deletedPaths.push(defaultInstallDir);
|
|
327
|
+
}
|
|
328
|
+
if (sourceDir && fs.existsSync(sourceDir)) {
|
|
329
|
+
fs.rmSync(sourceDir, { recursive: true, force: true });
|
|
330
|
+
deletedPaths.push(sourceDir);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
process.stdout.write(JSON.stringify({
|
|
334
|
+
ok: true,
|
|
335
|
+
configFile: configPath,
|
|
336
|
+
configChanged: changed,
|
|
337
|
+
deletedPaths
|
|
338
|
+
}));
|
|
339
|
+
NODE
|
|
340
|
+
)"
|
|
341
|
+
|
|
342
|
+
for skill_name in memoria-memory memoria-recovery; do
|
|
343
|
+
if [[ -d "${MANAGED_SKILLS_DIR}/${skill_name}" ]]; then
|
|
344
|
+
rm -rf "${MANAGED_SKILLS_DIR:?}/${skill_name}"
|
|
345
|
+
log "Removed managed skill: ${skill_name}"
|
|
346
|
+
fi
|
|
347
|
+
done
|
|
348
|
+
|
|
349
|
+
log "Removed OpenClaw Memoria plugin configuration"
|
|
350
|
+
log "${UNINSTALL_RESULT}"
|
|
351
|
+
|
|
352
|
+
cat <<EOF
|
|
353
|
+
|
|
354
|
+
Uninstall complete.
|
|
355
|
+
|
|
356
|
+
Config file: ${CONFIG_FILE}
|
|
357
|
+
|
|
358
|
+
Recommended follow-up checks:
|
|
359
|
+
cd ~
|
|
360
|
+
openclaw plugins list --json | rg 'memory-memoria|openclaw-memoria' || true
|
|
361
|
+
openclaw config get 'plugins.slots.memory'
|
|
362
|
+
EOF
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
function readArg(name, fallback = "") {
|
|
8
|
+
const index = process.argv.indexOf(name);
|
|
9
|
+
if (index >= 0 && index + 1 < process.argv.length) {
|
|
10
|
+
return process.argv[index + 1];
|
|
11
|
+
}
|
|
12
|
+
return fallback;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const openclawBin = readArg("--openclaw-bin", "openclaw");
|
|
16
|
+
const memoriaBin = readArg("--memoria-bin", "memoria");
|
|
17
|
+
const configFile = path.resolve(readArg("--config-file", path.join(process.env.HOME || "", ".openclaw", "openclaw.json")));
|
|
18
|
+
|
|
19
|
+
function run(command, args) {
|
|
20
|
+
return execFileSync(command, args, {
|
|
21
|
+
encoding: "utf8",
|
|
22
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
23
|
+
}).trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function tryParseJson(raw) {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(raw);
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseCommandJson(raw) {
|
|
35
|
+
const direct = tryParseJson(raw);
|
|
36
|
+
if (direct) {
|
|
37
|
+
return direct;
|
|
38
|
+
}
|
|
39
|
+
const start = raw.indexOf("{");
|
|
40
|
+
const end = raw.lastIndexOf("}");
|
|
41
|
+
if (start >= 0 && end > start) {
|
|
42
|
+
return tryParseJson(raw.slice(start, end + 1)) ?? raw;
|
|
43
|
+
}
|
|
44
|
+
return raw;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parsePort(rawProtocol, rawPort) {
|
|
48
|
+
if (rawPort) {
|
|
49
|
+
return Number.parseInt(rawPort, 10);
|
|
50
|
+
}
|
|
51
|
+
return rawProtocol === "mysql:" ? 3306 : 80;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function checkTcp(host, port, timeoutMs = 1500) {
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
const socket = new net.Socket();
|
|
57
|
+
let settled = false;
|
|
58
|
+
|
|
59
|
+
const finish = (value) => {
|
|
60
|
+
if (settled) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
settled = true;
|
|
64
|
+
socket.destroy();
|
|
65
|
+
resolve(value);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
socket.setTimeout(timeoutMs);
|
|
69
|
+
socket.once("connect", () => finish(true));
|
|
70
|
+
socket.once("timeout", () => finish(false));
|
|
71
|
+
socket.once("error", () => finish(false));
|
|
72
|
+
socket.connect(port, host);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!fs.existsSync(configFile)) {
|
|
77
|
+
throw new Error(`OpenClaw config file not found: ${configFile}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const config = JSON.parse(fs.readFileSync(configFile, "utf8"));
|
|
81
|
+
const pluginEntry = config?.plugins?.entries?.["memory-memoria"];
|
|
82
|
+
if (!pluginEntry || pluginEntry.enabled !== true) {
|
|
83
|
+
throw new Error("plugins.entries.memory-memoria is not enabled");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const pluginConfig = pluginEntry.config ?? {};
|
|
87
|
+
if (pluginConfig.memoriaExecutable == null) {
|
|
88
|
+
throw new Error("plugins.entries.memory-memoria.config.memoriaExecutable is missing");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const resolvedMemoriaBin = pluginConfig.memoriaExecutable || memoriaBin;
|
|
92
|
+
const openclawVersion = run(openclawBin, ["--version"]);
|
|
93
|
+
const configValidation = tryParseJson(run(openclawBin, ["config", "validate", "--json"]));
|
|
94
|
+
const memoriaVersion = run(resolvedMemoriaBin, ["--version"]);
|
|
95
|
+
const capabilities = run(openclawBin, ["memoria", "capabilities"]);
|
|
96
|
+
|
|
97
|
+
if (!capabilities.includes("memory_branch") || !capabilities.includes("memory_snapshot")) {
|
|
98
|
+
throw new Error("OpenClaw memoria capabilities output is missing expected Rust Memoria tools");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const result = {
|
|
102
|
+
ok: true,
|
|
103
|
+
configFile,
|
|
104
|
+
openclawVersion,
|
|
105
|
+
memoriaVersion,
|
|
106
|
+
memoriaExecutable: pluginConfig.memoriaExecutable,
|
|
107
|
+
configValidation,
|
|
108
|
+
deepChecks: {
|
|
109
|
+
backend: pluginConfig.backend ?? "embedded",
|
|
110
|
+
performed: false,
|
|
111
|
+
skipped: null,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if ((pluginConfig.backend ?? "embedded") === "embedded" && typeof pluginConfig.dbUrl === "string") {
|
|
116
|
+
const dbUrl = new URL(pluginConfig.dbUrl);
|
|
117
|
+
const dbReachable = await checkTcp(
|
|
118
|
+
dbUrl.hostname || "127.0.0.1",
|
|
119
|
+
parsePort(dbUrl.protocol, dbUrl.port),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
result.deepChecks.dbUrl = pluginConfig.dbUrl;
|
|
123
|
+
result.deepChecks.dbReachable = dbReachable;
|
|
124
|
+
|
|
125
|
+
if (dbReachable) {
|
|
126
|
+
const userId =
|
|
127
|
+
typeof pluginConfig.defaultUserId === "string" && pluginConfig.defaultUserId.trim()
|
|
128
|
+
? pluginConfig.defaultUserId.trim()
|
|
129
|
+
: "openclaw-user";
|
|
130
|
+
const statsRaw = run(openclawBin, ["memoria", "stats", "--user-id", userId]);
|
|
131
|
+
const listRaw = run(openclawBin, ["ltm", "list", "--limit", "1", "--user-id", userId]);
|
|
132
|
+
result.deepChecks.performed = true;
|
|
133
|
+
result.deepChecks.userId = userId;
|
|
134
|
+
result.deepChecks.stats = parseCommandJson(statsRaw);
|
|
135
|
+
result.deepChecks.list = parseCommandJson(listRaw);
|
|
136
|
+
} else {
|
|
137
|
+
result.deepChecks.skipped = "Embedded database is not reachable; skipped stats/list verification.";
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
result.deepChecks.skipped = "Deep verification is only automatic for embedded mode.";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log(
|
|
144
|
+
JSON.stringify(
|
|
145
|
+
result,
|
|
146
|
+
null,
|
|
147
|
+
2,
|
|
148
|
+
),
|
|
149
|
+
);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memoria-memory
|
|
3
|
+
description: |
|
|
4
|
+
Use Memoria for durable user/project memory in OpenClaw.
|
|
5
|
+
Triggers: "remember this", "save to memory", "what do you remember",
|
|
6
|
+
"forget this", "correct memory", "update memory", "use memoria".
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Memoria Memory
|
|
10
|
+
|
|
11
|
+
Use Memoria tools for durable memory. Do not default to `MEMORY.md` or `memory/YYYY-MM-DD.md` unless the user explicitly asks for file-based memory.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
- The user asks you to remember a fact, preference, decision, or workflow.
|
|
16
|
+
- The user asks what you already know about them, the project, or a prior session.
|
|
17
|
+
- The user asks to correct, update, or delete stored memory.
|
|
18
|
+
|
|
19
|
+
## Store
|
|
20
|
+
|
|
21
|
+
1. Choose the smallest durable fact worth keeping.
|
|
22
|
+
2. Use the most specific tool available:
|
|
23
|
+
- `memory_profile` for stable user preferences or profile traits
|
|
24
|
+
- `memory_store` for facts, procedures, and project knowledge
|
|
25
|
+
3. Prefer short, atomic entries.
|
|
26
|
+
4. After storing something important, verify with `memory_recall` or `memory_search`.
|
|
27
|
+
|
|
28
|
+
## Recall
|
|
29
|
+
|
|
30
|
+
1. When the user asks "what do you know", "what do you remember", or refers to a prior session, query Memoria first.
|
|
31
|
+
2. Use `memory_recall` for semantic retrieval and `memory_get` only when you already have a specific memory id.
|
|
32
|
+
|
|
33
|
+
## Repair
|
|
34
|
+
|
|
35
|
+
1. If the user says a memory is wrong, use `memory_correct`.
|
|
36
|
+
2. If the user wants it removed, use `memory_forget` or `memory_purge`.
|
|
37
|
+
3. After repair, verify with `memory_recall` or `memory_search`.
|
|
38
|
+
|
|
39
|
+
## Rules
|
|
40
|
+
|
|
41
|
+
- Do not claim only `memory_search` and `memory_get` exist when other `memory_*` tools are available.
|
|
42
|
+
- Do not store transient small talk unless the user asks or it is clearly a stable preference.
|
|
43
|
+
- Prefer Memoria for durable cross-session memory; prefer workspace files only for explicit file-based notes.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memoria-recovery
|
|
3
|
+
description: |
|
|
4
|
+
Recover from accidental memory loss with snapshots and rollback.
|
|
5
|
+
Triggers: "restore memory", "rollback memory", "undo deleted memory",
|
|
6
|
+
"recover deleted memory", "take a memory snapshot".
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Memoria Recovery
|
|
10
|
+
|
|
11
|
+
Use Memoria snapshot tools when memory state needs a checkpoint or a rollback.
|
|
12
|
+
|
|
13
|
+
## Before risky changes
|
|
14
|
+
|
|
15
|
+
1. If you are about to bulk-delete, purge, or rewrite memory, create a snapshot first with `memory_snapshot`.
|
|
16
|
+
2. Tell the user a checkpoint exists and can be restored.
|
|
17
|
+
|
|
18
|
+
## After accidental deletion or corruption
|
|
19
|
+
|
|
20
|
+
1. Inspect available recovery points with `memory_snapshots`.
|
|
21
|
+
2. Choose the most recent good snapshot.
|
|
22
|
+
3. Restore with `memory_rollback`.
|
|
23
|
+
4. Verify recovery with `memory_recall`, `memory_list`, or `memory_stats`.
|
|
24
|
+
|
|
25
|
+
## Rules
|
|
26
|
+
|
|
27
|
+
- Prefer rollback over manually re-creating many deleted memories.
|
|
28
|
+
- If the user wants a selective logical fix rather than full restore, use `memory_correct` or `memory_forget` instead.
|
|
29
|
+
- After rollback, state what was restored and what you verified.
|