@memtensor/memos-cloud-openclaw-plugin 0.1.11 → 0.1.12-beta.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.
@@ -0,0 +1,116 @@
1
+ import { randomBytes, randomUUID } from "node:crypto";
2
+ import { readFileSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ const ARMS_ENDPOINT = "https://proj-xtrace-e218d9316b328f196a3c640cc7ca84-cn-hangzhou.cn-hangzhou.log.aliyuncs.com/rum/web/v2?workspace=default-cms-1026429231103299-cn-hangzhou&service_id=a3u72ukxmr@bed68dd882dd823439015"
7
+ const ARMS_PID = "a3u72ukxmr@c42a249fb14f4d9";
8
+ const ARMS_ENV = "prod";
9
+ const ARMS_UID_FILE = new URL("../.memos_arms_uid", import.meta.url);
10
+
11
+ let armsUidCache = "";
12
+
13
+ function readUidFromFile() {
14
+ try {
15
+ return readFileSync(ARMS_UID_FILE, "utf-8").trim();
16
+ } catch {
17
+ return "";
18
+ }
19
+ }
20
+
21
+ function writeUidToFile(value) {
22
+ try {
23
+ writeFileSync(ARMS_UID_FILE, `${value}\n`, { mode: 0o600 });
24
+ } catch {}
25
+ }
26
+
27
+ function createEventId() {
28
+ const traceId = randomBytes(16).toString("hex");
29
+ const spanId = randomBytes(8).toString("hex");
30
+ return `00-${traceId}-${spanId}`;
31
+ }
32
+
33
+ function readOpenClawDeviceId(log) {
34
+ try {
35
+ const deviceFile = join(homedir(), ".openclaw", "identity", "device.json");
36
+ const content = readFileSync(deviceFile, "utf-8");
37
+ const data = JSON.parse(content);
38
+ if (data && typeof data.deviceId === "string" && data.deviceId.trim()) {
39
+ return `uid_${data.deviceId.trim()}`;
40
+ }
41
+ } catch (err) {
42
+ log?.warn?.(`[memos-cloud] Failed to read OpenClaw deviceId: ${String(err)}`);
43
+ }
44
+ return "";
45
+ }
46
+
47
+ function loadArmsUid(log) {
48
+ if (armsUidCache) return armsUidCache;
49
+
50
+ const openclawDevice = readOpenClawDeviceId(log);
51
+ if (openclawDevice) {
52
+ armsUidCache = openclawDevice;
53
+ writeUidToFile(armsUidCache);
54
+ return armsUidCache;
55
+ }
56
+
57
+ const fromUidFile = readUidFromFile();
58
+ if (fromUidFile) {
59
+ armsUidCache = fromUidFile;
60
+ return armsUidCache;
61
+ }
62
+
63
+ armsUidCache = `uid_${randomUUID()}`;
64
+ writeUidToFile(armsUidCache);
65
+ return armsUidCache;
66
+ }
67
+
68
+ function buildPayload(ctx, eventName, payload, log) {
69
+ return {
70
+ app: {
71
+ id: ARMS_PID,
72
+ env: ARMS_ENV,
73
+ type: "node",
74
+ },
75
+ user: { id: loadArmsUid(log) },
76
+ session: { id: ctx.sessionId },
77
+ net: {},
78
+ view: { id: "plugin", name: "memos-cloud-openclaw" },
79
+ events: [
80
+ {
81
+ event_id: createEventId(),
82
+ event_type: 'custom',
83
+ type: "memos_plugin",
84
+ group: "memos_cloud",
85
+ name: eventName,
86
+ timestamp: +new Date(),
87
+ properties: { ...payload }
88
+ }
89
+ ]
90
+ };
91
+ }
92
+
93
+ export async function reportRumEvent(eventName, payload, cfg, ctx, log) {
94
+ if (!cfg.rumEnabled) return;
95
+ const controller = new AbortController();
96
+ const timeoutId = setTimeout(
97
+ () => controller.abort(),
98
+ Number.isFinite(cfg.rumTimeoutMs) ? Math.max(1000, cfg.rumTimeoutMs) : 3000,
99
+ );
100
+ try {
101
+ const body = buildPayload(ctx, eventName, payload, log);
102
+ const res = await fetch(ARMS_ENDPOINT, {
103
+ method: "POST",
104
+ headers: { "Content-Type": "text/plain" },
105
+ body: JSON.stringify(body),
106
+ signal: controller.signal,
107
+ });
108
+ if (!res.ok) {
109
+ throw new Error(`HTTP ${res.status}`);
110
+ }
111
+ } catch (err) {
112
+ log.warn?.(`[memos-cloud] RUM report failed: ${String(err)}`);
113
+ } finally {
114
+ clearTimeout(timeoutId);
115
+ }
116
+ }
@@ -1,270 +1,187 @@
1
- import https from "https";
2
- import fs from "fs";
3
- import path from "path";
4
- import { fileURLToPath } from "url";
5
- import { spawn, exec } from "child_process";
6
- import os from "os";
7
-
8
- /**
9
- * Kill a spawned child process and its entire process tree.
10
- */
11
- function killProcessTree(child) {
12
- try {
13
- if (process.platform === "win32") {
14
- exec(`taskkill /pid ${child.pid} /T /F`, () => {});
15
- } else {
16
- // On Unix, kill the process group
17
- process.kill(-child.pid, "SIGKILL");
18
- }
19
- } catch (e) {
20
- // Fallback: try the basic kill
21
- try { child.kill("SIGKILL"); } catch (_) {}
22
- }
23
- }
24
-
25
- let isUpdating = false;
26
-
27
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
28
-
29
- const CHECK_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours check interval
30
- const UPDATE_TIMEOUT = 3 * 60 * 1000; // 3 minutes timeout for the CLI update command to finish
31
- const PLUGIN_NAME = "@memtensor/memos-cloud-openclaw-plugin";
32
- const CHECK_FILE = path.join(os.tmpdir(), "memos_openclaw_update_check.json");
33
-
34
- const ANSI = {
35
- RESET: "\x1b[0m",
36
- GREEN: "\x1b[32m",
37
- YELLOW: "\x1b[33m",
38
- CYAN: "\x1b[36m",
39
- RED: "\x1b[31m"
40
- };
41
-
42
-
43
- function getPackageVersion() {
44
- try {
45
- const pkgPath = path.join(__dirname, "..", "package.json");
46
- const pkgData = fs.readFileSync(pkgPath, "utf-8");
47
- const pkg = JSON.parse(pkgData);
48
- return pkg.version;
49
- } catch (err) {
50
- return null;
51
- }
52
- }
53
-
54
- function getLatestVersion(log) {
55
- return new Promise((resolve, reject) => {
56
- const req = https.get(
57
- `https://registry.npmjs.org/${PLUGIN_NAME}/latest`,
58
- { timeout: 5000 },
59
- (res) => {
60
- if (res.statusCode !== 200) {
61
- req.destroy();
62
- return reject(new Error(`Failed to fetch version, status: ${res.statusCode}`));
63
- }
64
-
65
- let body = "";
66
- res.on("data", (chunk) => {
67
- body += chunk;
68
- });
69
-
70
- res.on("end", () => {
71
- try {
72
- const data = JSON.parse(body);
73
- resolve(data.version);
74
- } catch (err) {
75
- reject(err);
76
- }
77
- });
78
- }
79
- );
80
-
81
- req.on("error", (err) => {
82
- reject(err);
83
- });
84
-
85
- req.on("timeout", () => {
86
- req.destroy();
87
- reject(new Error("Timeout getting latest version"));
88
- });
89
- });
90
- }
91
-
92
- function compareVersions(v1, v2) {
93
- // Split pre-release tags (e.g. 0.1.8-beta.1 -> "0.1.8" and "beta.1")
94
- const split1 = v1.split("-");
95
- const split2 = v2.split("-");
96
- const parts1 = split1[0].split(".").map(Number);
97
- const parts2 = split2[0].split(".").map(Number);
98
-
99
- // Compare major.minor.patch
100
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
101
- const p1 = parts1[i] || 0;
102
- const p2 = parts2[i] || 0;
103
- if (p1 > p2) return 1;
104
- if (p1 < p2) return -1;
105
- }
106
-
107
- // If base versions are equal, compare pre-release tags.
108
- // A version WITH a pre-release tag is LOWER than a version WITHOUT one.
109
- // e.g. 0.1.8-beta is less than 0.1.8. 0.1.8 is the final release.
110
- const hasPre1 = split1.length > 1;
111
- const hasPre2 = split2.length > 1;
112
-
113
- if (hasPre1 && !hasPre2) return -1; // v1 is a beta, v2 is a full release
114
- if (!hasPre1 && hasPre2) return 1; // v1 is a full release, v2 is a beta
115
- if (!hasPre1 && !hasPre2) return 0; // both are full releases and equal
116
-
117
- // If both are pre-releases, do a basic string compare on the tag
118
- // "alpha" < "beta" < "rc"
119
- if (split1[1] > split2[1]) return 1;
120
- if (split1[1] < split2[1]) return -1;
121
-
122
- return 0;
123
- }
124
-
125
- export function startUpdateChecker(log) {
126
- // Only start the interval if we are in the gateway
127
- const isGateway = process.argv.includes("gateway");
128
- if (!isGateway) {
129
- return;
130
- }
131
-
132
- const runCheck = async () => {
133
- if (isUpdating) {
134
- log.info?.(`${ANSI.YELLOW}[memos-cloud] An update sequence is currently in progress, skipping this check.${ANSI.RESET}`);
135
- return;
136
- }
137
-
138
- // TRULY PREVENT LOOPS: The instant we start a check, record the time BEFORE any network or processing happens.
139
- // This absolutely guarantees that even if the network hangs, NPM crashes, or openclaw update causes an immediate hot reload,
140
- // the system has already advanced the 12-hour/1-min clock and will NOT re-enter this function on boot.
141
- try {
142
- fs.writeFileSync(CHECK_FILE, JSON.stringify({ time: Date.now() }));
143
- } catch (e) {
144
- log.warn?.(`${ANSI.RED}[memos-cloud] Failed to write timestamp file: ${e.message}${ANSI.RESET}`);
145
- }
146
-
147
- const currentVersion = getPackageVersion();
148
- if (!currentVersion) {
149
- log.warn?.(`${ANSI.RED}[memos-cloud] Could not read current version from package.json${ANSI.RESET}`);
150
- return;
151
- }
152
-
153
- try {
154
- const latestVersion = await getLatestVersion(log);
155
-
156
- // Normal version check
157
- if (compareVersions(latestVersion, currentVersion) <= 0) {
158
- return;
159
- }
160
-
161
- log.info?.(`${ANSI.YELLOW}[memos-cloud] Update available: ${currentVersion} -> ${latestVersion}. Updating in background...${ANSI.RESET}`);
162
-
163
- let dotCount = 0;
164
- const progressInterval = setInterval(() => {
165
- dotCount++;
166
- const dots = ".".repeat(dotCount % 4);
167
- log.info?.(`${ANSI.YELLOW}[memos-cloud] Update in progress for memos-cloud-openclaw-plugin${dots}${ANSI.RESET}`);
168
- }, 30000); // Log every 30 seconds to show it's still alive without spamming
169
-
170
- const cliName = (() => {
171
- // Check the full path of the entry script (e.g., .../moltbot/bin/index.js) or the executable
172
- const scriptPath = process.argv[1] ? process.argv[1].toLowerCase() : "";
173
- const execPath = process.execPath ? process.execPath.toLowerCase() : "";
174
-
175
- if (scriptPath.includes("moltbot") || execPath.includes("moltbot")) return "moltbot";
176
- if (scriptPath.includes("clawdbot") || execPath.includes("clawdbot")) return "clawdbot";
177
- return "openclaw";
178
- })();
179
-
180
- isUpdating = true;
181
- const spawnOpts = { shell: true };
182
- // On Unix, detach the process so we can kill the entire process group on timeout
183
- if (process.platform !== "win32") {
184
- spawnOpts.detached = true;
185
- }
186
- const child = spawn(cliName, ["plugins", "update", "memos-cloud-openclaw-plugin"], spawnOpts);
187
-
188
- // Timeout mechanism: forcefully kill the update process if it hangs for more than the configured timeout
189
- const updateTimeout = setTimeout(() => {
190
- log.warn?.(`${ANSI.RED}[memos-cloud] Update process timed out. Please try manually running: ${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`);
191
- killProcessTree(child);
192
-
193
- // Fallback: if kill failed and the close event never fires, forcefully release the lock after 5 seconds
194
- setTimeout(() => {
195
- if (isUpdating) {
196
- clearInterval(progressInterval);
197
- isUpdating = false;
198
- }
199
- }, 5000);
200
- }, UPDATE_TIMEOUT);
201
-
202
- child.stdout.on("data", (data) => {
203
- const outText = data.toString();
204
- log.info?.(`${ANSI.CYAN}[${cliName}-cli]${ANSI.RESET}\n${outText.trim()}`);
205
-
206
- // Auto-reply to any [y/N] prompts from the CLI
207
- if (outText.toLowerCase().includes("[y/n]")) {
208
- child.stdin.write("y\n");
209
- }
210
- });
211
-
212
- child.stderr.on("data", (data) => {
213
- const errText = data.toString();
214
- log.warn?.(`${ANSI.RED}[${cliName}-cli]${ANSI.RESET}\n${errText.trim()}`);
215
-
216
- // Some CLIs output interactive prompts to stderr instead of stdout
217
- if (errText.toLowerCase().includes("[y/n]")) {
218
- child.stdin.write("y\n");
219
- }
220
- });
221
-
222
- child.on("close", (code) => {
223
- clearTimeout(updateTimeout);
224
- clearInterval(progressInterval);
225
- isUpdating = false;
226
-
227
- // Wait for a brief moment to let file system sync if needed
228
- setTimeout(() => {
229
- const postUpdateVersion = getPackageVersion();
230
- const actuallyUpdated = (postUpdateVersion === latestVersion) && (postUpdateVersion !== currentVersion);
231
-
232
- if (code !== 0 || !actuallyUpdated) {
233
- log.warn?.(`${ANSI.RED}[memos-cloud] Auto-update failed or version did not change. Please refer to the CLI logs above, or run manually: ${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`);
234
- } else {
235
- log.info?.(`${ANSI.GREEN}[memos-cloud] Successfully updated to version ${latestVersion}. Please restart the gateway to apply changes.${ANSI.RESET}`);
236
- }
237
- }, 1000); // Small 1-second buffer for file systems
238
- });
239
-
240
- } catch (error) {
241
- log.warn?.(`${ANSI.RED}[memos-cloud] Update check failed entirely: ${error.message}${ANSI.RESET}`);
242
- }
243
- };
244
-
245
- // Check when we last ran
246
- let lastCheckTime = 0;
247
- try {
248
- if (fs.existsSync(CHECK_FILE)) {
249
- const data = JSON.parse(fs.readFileSync(CHECK_FILE, "utf-8"));
250
- lastCheckTime = data.time || 0;
251
- }
252
- } catch (e) {}
253
-
254
- const now = Date.now();
255
- const timeSinceLastCheck = now - lastCheckTime;
256
-
257
- // If the interval has passed, run it IMMEDIATELY without delay.
258
- // The immediate file-write at the top of runCheck() will prevent loop scenarios.
259
- if (timeSinceLastCheck >= CHECK_INTERVAL) {
260
- runCheck();
261
- setInterval(runCheck, CHECK_INTERVAL);
262
- } else {
263
- // If it hasn't been the full interval yet, wait the remaining time, then trigger interval
264
- const timeUntilNextCheck = CHECK_INTERVAL - timeSinceLastCheck;
265
- setTimeout(() => {
266
- runCheck();
267
- setInterval(runCheck, CHECK_INTERVAL);
268
- }, timeUntilNextCheck);
269
- }
270
- }
1
+ import https from "https";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import os from "os";
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+
9
+ const CHECK_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours check interval
10
+ const PLUGIN_NAME = "@memtensor/memos-cloud-openclaw-plugin";
11
+ const CHECK_FILE = path.join(os.tmpdir(), "memos_openclaw_update_check.json");
12
+
13
+ const ANSI = {
14
+ RESET: "\x1b[0m",
15
+ GREEN: "\x1b[32m",
16
+ YELLOW: "\x1b[33m",
17
+ CYAN: "\x1b[36m",
18
+ RED: "\x1b[31m"
19
+ };
20
+
21
+
22
+ function getPackageVersion() {
23
+ try {
24
+ const pkgPath = path.join(__dirname, "..", "package.json");
25
+ const pkgData = fs.readFileSync(pkgPath, "utf-8");
26
+ const pkg = JSON.parse(pkgData);
27
+ return pkg.version;
28
+ } catch (err) {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ function getLatestVersion(log) {
34
+ return new Promise((resolve, reject) => {
35
+ const req = https.get(
36
+ `https://registry.npmjs.org/${PLUGIN_NAME}/latest`,
37
+ { timeout: 5000 },
38
+ (res) => {
39
+ if (res.statusCode !== 200) {
40
+ req.destroy();
41
+ return reject(new Error(`Failed to fetch version, status: ${res.statusCode}`));
42
+ }
43
+
44
+ let body = "";
45
+ res.on("data", (chunk) => {
46
+ body += chunk;
47
+ });
48
+
49
+ res.on("end", () => {
50
+ try {
51
+ const data = JSON.parse(body);
52
+ resolve(data.version);
53
+ } catch (err) {
54
+ reject(err);
55
+ }
56
+ });
57
+ }
58
+ );
59
+
60
+ req.on("error", (err) => {
61
+ reject(err);
62
+ });
63
+
64
+ req.on("timeout", () => {
65
+ req.destroy();
66
+ reject(new Error("Timeout getting latest version"));
67
+ });
68
+ });
69
+ }
70
+
71
+ function compareVersions(v1, v2) {
72
+ // Split pre-release tags (e.g. 0.1.8-beta.1 -> "0.1.8" and "beta.1")
73
+ const split1 = v1.split("-");
74
+ const split2 = v2.split("-");
75
+ const parts1 = split1[0].split(".").map(Number);
76
+ const parts2 = split2[0].split(".").map(Number);
77
+
78
+ // Compare major.minor.patch
79
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
80
+ const p1 = parts1[i] || 0;
81
+ const p2 = parts2[i] || 0;
82
+ if (p1 > p2) return 1;
83
+ if (p1 < p2) return -1;
84
+ }
85
+
86
+ // If base versions are equal, compare pre-release tags.
87
+ // A version WITH a pre-release tag is LOWER than a version WITHOUT one.
88
+ // e.g. 0.1.8-beta is less than 0.1.8. 0.1.8 is the final release.
89
+ const hasPre1 = split1.length > 1;
90
+ const hasPre2 = split2.length > 1;
91
+
92
+ if (hasPre1 && !hasPre2) return -1; // v1 is a beta, v2 is a full release
93
+ if (!hasPre1 && hasPre2) return 1; // v1 is a full release, v2 is a beta
94
+ if (!hasPre1 && !hasPre2) return 0; // both are full releases and equal
95
+
96
+ // If both are pre-releases, do a basic string compare on the tag
97
+ // "alpha" < "beta" < "rc"
98
+ if (split1[1] > split2[1]) return 1;
99
+ if (split1[1] < split2[1]) return -1;
100
+
101
+ return 0;
102
+ }
103
+
104
+ export function startUpdateChecker(log) {
105
+ // Only start the interval if we are in the gateway
106
+ const isGateway = process.argv.includes("gateway");
107
+ if (!isGateway) {
108
+ return;
109
+ }
110
+
111
+ const runCheck = async () => {
112
+ // TRULY PREVENT LOOPS: The instant we start a check, record the time BEFORE any network or processing happens.
113
+ // This absolutely guarantees that even if the network hangs, NPM crashes, or openclaw update causes an immediate hot reload,
114
+ // the system has already advanced the 12-hour/1-min clock and will NOT re-enter this function on boot.
115
+ try {
116
+ fs.writeFileSync(CHECK_FILE, JSON.stringify({ time: Date.now() }));
117
+ } catch (e) {
118
+ log.warn?.(`${ANSI.RED}[memos-cloud] Failed to write timestamp file: ${e.message}${ANSI.RESET}`);
119
+ }
120
+
121
+ const currentVersion = getPackageVersion();
122
+ if (!currentVersion) {
123
+ log.warn?.(`${ANSI.RED}[memos-cloud] Could not read current version from package.json${ANSI.RESET}`);
124
+ return;
125
+ }
126
+
127
+ try {
128
+ const latestVersion = await getLatestVersion(log);
129
+
130
+ // Normal version check
131
+ if (compareVersions(latestVersion, currentVersion) <= 0) {
132
+ return;
133
+ }
134
+
135
+ const cliName = (() => {
136
+ // Check the full path of the entry script (e.g., .../moltbot/bin/index.js) or the executable
137
+ const scriptPath = process.argv[1] ? process.argv[1].toLowerCase() : "";
138
+ const execPath = process.execPath ? process.execPath.toLowerCase() : "";
139
+
140
+ if (scriptPath.includes("moltbot") || execPath.includes("moltbot")) return "moltbot";
141
+ if (scriptPath.includes("clawdbot") || execPath.includes("clawdbot")) return "clawdbot";
142
+ return "openclaw";
143
+ })();
144
+
145
+ const border = "=".repeat(64);
146
+ log.info?.("");
147
+ log.info?.(`${ANSI.GREEN}${border}${ANSI.RESET}`);
148
+ log.info?.(`${ANSI.YELLOW}🚀 [memos-cloud] NEW VERSION AVAILABLE!${ANSI.RESET}`);
149
+ log.info?.(`${ANSI.CYAN}📦 Current version : ${currentVersion}${ANSI.RESET}`);
150
+ log.info?.(`${ANSI.GREEN}✨ Latest version : ${latestVersion}${ANSI.RESET}`);
151
+ log.info?.(`${ANSI.CYAN}────────────────────────────────────────────────────────────────${ANSI.RESET}`);
152
+ log.info?.(`${ANSI.GREEN}Please run the following command to update manually:${ANSI.RESET}`);
153
+ log.info?.(`${ANSI.YELLOW}${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`);
154
+ log.info?.(`${ANSI.GREEN}${border}${ANSI.RESET}`);
155
+ log.info?.("");
156
+
157
+ } catch (error) {
158
+ log.warn?.(`${ANSI.RED}[memos-cloud] Update check failed entirely: ${error.message}${ANSI.RESET}`);
159
+ }
160
+ };
161
+
162
+ // Check when we last ran
163
+ let lastCheckTime = 0;
164
+ try {
165
+ if (fs.existsSync(CHECK_FILE)) {
166
+ const data = JSON.parse(fs.readFileSync(CHECK_FILE, "utf-8"));
167
+ lastCheckTime = data.time || 0;
168
+ }
169
+ } catch (e) {}
170
+
171
+ const now = Date.now();
172
+ const timeSinceLastCheck = now - lastCheckTime;
173
+
174
+ // If the interval has passed, run it IMMEDIATELY without delay.
175
+ // The immediate file-write at the top of runCheck() will prevent loop scenarios.
176
+ if (timeSinceLastCheck >= CHECK_INTERVAL) {
177
+ runCheck();
178
+ setInterval(runCheck, CHECK_INTERVAL);
179
+ } else {
180
+ // If it hasn't been the full interval yet, wait the remaining time, then trigger interval
181
+ const timeUntilNextCheck = CHECK_INTERVAL - timeSinceLastCheck;
182
+ setTimeout(() => {
183
+ runCheck();
184
+ setInterval(runCheck, CHECK_INTERVAL);
185
+ }, timeUntilNextCheck);
186
+ }
187
+ }
@@ -0,0 +1,50 @@
1
+ export const CONFIG_RESOLUTION_FIELDS = [
2
+ { key: "baseUrl", configMode: "truthy", envVar: "MEMOS_BASE_URL", envMode: "truthy", fallbackSource: "default", uiDefaultValue: "https://memos.memtensor.cn/api/openmem/v1" },
3
+ { key: "apiKey", configMode: "truthy", envVar: "MEMOS_API_KEY", envMode: "truthy", fallbackSource: "empty", inheritedFrom: "env", inheritedFallback: "" },
4
+ { key: "userId", configMode: "truthy", envVar: "MEMOS_USER_ID", envMode: "truthy", fallbackSource: "default", uiDefaultValue: "openclaw-user", inheritedFrom: "env", inheritedFallback: "openclaw-user" },
5
+ { key: "useDirectSessionUserId", configMode: "nullish", envVar: "MEMOS_USE_DIRECT_SESSION_USER_ID", envMode: "defined", fallbackSource: "default", uiDefaultValue: false },
6
+ { key: "conversationId", configMode: "truthy", envVar: "MEMOS_CONVERSATION_ID", envMode: "truthy", fallbackSource: "empty", inheritedFrom: "env", inheritedFallback: "" },
7
+ { key: "conversationIdPrefix", configMode: "nullish", envVar: "MEMOS_CONVERSATION_PREFIX", envMode: "defined", fallbackSource: "empty", inheritedFrom: "env", inheritedFallback: "" },
8
+ { key: "conversationIdSuffix", configMode: "nullish", envVar: "MEMOS_CONVERSATION_SUFFIX", envMode: "defined", fallbackSource: "empty", inheritedFrom: "env", inheritedFallback: "" },
9
+ { key: "conversationSuffixMode", configMode: "nullish", envVar: "MEMOS_CONVERSATION_SUFFIX_MODE", envMode: "truthy", fallbackSource: "default", uiDefaultValue: "none" },
10
+ { key: "resetOnNew", configMode: "nullish", envVar: "MEMOS_CONVERSATION_RESET_ON_NEW", envMode: "defined", fallbackSource: "default", uiDefaultValue: true },
11
+ { key: "queryPrefix", configMode: "nullish", envMode: "none", fallbackSource: "empty", inheritedValue: "" },
12
+ { key: "maxQueryChars", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: 0 },
13
+ { key: "recallEnabled", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: true },
14
+ { key: "recallGlobal", configMode: "nullish", envVar: "MEMOS_RECALL_GLOBAL", envMode: "defined", fallbackSource: "default", uiDefaultValue: true },
15
+ { key: "maxItemChars", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: 8000 },
16
+ { key: "memoryLimitNumber", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: 9 },
17
+ { key: "preferenceLimitNumber", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: 6 },
18
+ { key: "includePreference", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: true },
19
+ { key: "includeToolMemory", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: false },
20
+ { key: "toolMemoryLimitNumber", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: 6 },
21
+ { key: "relativity", configMode: "nullish", envVar: "MEMOS_RELATIVITY", envMode: "defined", fallbackSource: "default", uiDefaultValue: 0.45 },
22
+ { key: "filter", configMode: "nullish", envMode: "none", fallbackSource: "empty", inheritedValue: undefined },
23
+ { key: "knowledgebaseIds", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: [] },
24
+ { key: "addEnabled", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: true },
25
+ { key: "captureStrategy", configMode: "nullish", envVar: "MEMOS_CAPTURE_STRATEGY", envMode: "truthy", fallbackSource: "default", uiDefaultValue: "last_turn" },
26
+ { key: "maxMessageChars", configMode: "nullish", envVar: "MEMOS_MAX_MESSAGE_CHARS", envMode: "defined", fallbackSource: "default", uiDefaultValue: 20000, inheritedFrom: "env", inheritedFallback: 20000 },
27
+ { key: "includeAssistant", configMode: "nullish", envVar: "MEMOS_INCLUDE_ASSISTANT", envMode: "defined", fallbackSource: "default", uiDefaultValue: true },
28
+ { key: "tags", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: ["openclaw"] },
29
+ { key: "info", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: {} },
30
+ { key: "asyncMode", configMode: "nullish", envVar: "MEMOS_ASYNC_MODE", envMode: "defined", fallbackSource: "default", uiDefaultValue: true },
31
+ { key: "agentId", configMode: "nullish", envMode: "none", fallbackSource: "empty", inheritedValue: undefined },
32
+ { key: "multiAgentMode", configMode: "nullish", envVar: "MEMOS_MULTI_AGENT_MODE", envMode: "defined", fallbackSource: "default", uiDefaultValue: false },
33
+ { key: "allowedAgents", configMode: "nullish", envVar: "MEMOS_ALLOWED_AGENTS", envMode: "defined", fallbackSource: "default", uiDefaultValue: [] },
34
+ { key: "agentOverrides", configKey: "agentOverrides", resolvedKey: "_agentOverrides", configMode: "nullish", envVar: "MEMOS_AGENT_OVERRIDES", envMode: "defined", fallbackSource: "default", uiDefaultValue: {} },
35
+ { key: "appId", configMode: "nullish", envMode: "none", fallbackSource: "empty", inheritedValue: undefined },
36
+ { key: "allowPublic", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: false },
37
+ { key: "allowKnowledgebaseIds", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: [] },
38
+ { key: "recallFilterEnabled", configMode: "nullish", envVar: "MEMOS_RECALL_FILTER_ENABLED", envMode: "defined", fallbackSource: "default", uiDefaultValue: false },
39
+ { key: "recallFilterBaseUrl", configMode: "nullish", envVar: "MEMOS_RECALL_FILTER_BASE_URL", envMode: "defined", fallbackSource: "empty" },
40
+ { key: "recallFilterApiKey", configMode: "nullish", envVar: "MEMOS_RECALL_FILTER_API_KEY", envMode: "defined", fallbackSource: "empty" },
41
+ { key: "recallFilterModel", configMode: "nullish", envVar: "MEMOS_RECALL_FILTER_MODEL", envMode: "defined", fallbackSource: "empty" },
42
+ { key: "recallFilterTimeoutMs", configMode: "nullish", envVar: "MEMOS_RECALL_FILTER_TIMEOUT_MS", envMode: "defined", fallbackSource: "default", uiDefaultValue: 6000 },
43
+ { key: "recallFilterRetries", configMode: "nullish", envVar: "MEMOS_RECALL_FILTER_RETRIES", envMode: "defined", fallbackSource: "default", uiDefaultValue: 0 },
44
+ { key: "recallFilterCandidateLimit", configMode: "nullish", envVar: "MEMOS_RECALL_FILTER_CANDIDATE_LIMIT", envMode: "defined", fallbackSource: "default", uiDefaultValue: 30 },
45
+ { key: "recallFilterMaxItemChars", configMode: "nullish", envVar: "MEMOS_RECALL_FILTER_MAX_ITEM_CHARS", envMode: "defined", fallbackSource: "default", uiDefaultValue: 500 },
46
+ { key: "recallFilterFailOpen", configMode: "nullish", envVar: "MEMOS_RECALL_FILTER_FAIL_OPEN", envMode: "defined", fallbackSource: "default", uiDefaultValue: true },
47
+ { key: "timeoutMs", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: 5000 },
48
+ { key: "retries", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: 1 },
49
+ { key: "throttleMs", configMode: "nullish", envVar: "MEMOS_THROTTLE_MS", envMode: "defined", fallbackSource: "default", uiDefaultValue: 0 },
50
+ ];