@evermind-ai/openclaw-plugin 1.1.0 → 1.3.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/index.js CHANGED
@@ -1,14 +1,20 @@
1
1
  /**
2
- * EverMemOS ContextEngine Plugin for OpenClaw
3
- * Registers EverMemOS as the context engine for memory management
2
+ * EverOS OpenClaw Plugin
3
+ * Registers the EverOS backend as the context engine for memory management
4
4
  */
5
5
 
6
+ import { createRequire } from "node:module";
6
7
  import { resolveConfig } from "./src/config.js";
7
8
  import { searchMemories, saveMemories } from "./src/memory-api.js";
8
9
  import { buildMemoryPrompt, parseSearchResponse } from "./src/formatter.js";
9
10
  import { collectMessages, toText, isSessionResetPrompt } from "./src/message-utils.js";
11
+ import { ContextAssembler } from "./src/assembler.js";
12
+ import { SubagentTracker } from "./src/subagent.js";
10
13
 
11
- const PLUGIN_ID = "evermemos-openclaw-plugin";
14
+ const require = createRequire(import.meta.url);
15
+ const pluginMeta = require("./openclaw.plugin.json");
16
+ const PLUGIN_ID = pluginMeta.id;
17
+ const PLUGIN_VERSION = pluginMeta.version;
12
18
 
13
19
  /**
14
20
  * Convert OpenClaw AgentMessage to EverMemOS message format
@@ -131,7 +137,7 @@ function safePreview(content, maxLength = 200) {
131
137
  }
132
138
 
133
139
  /**
134
- * Create EverMemOS ContextEngine instance
140
+ * Create EverOS ContextEngine instance
135
141
  * @param {Object} pluginConfig - Plugin configuration
136
142
  * @param {Object} logger - Logger instance
137
143
  * @returns {Object} - ContextEngine implementation
@@ -139,36 +145,49 @@ function safePreview(content, maxLength = 200) {
139
145
  function createContextEngineInstance(pluginConfig, logger) {
140
146
  const cfg = resolveConfig(pluginConfig);
141
147
  const log = logger || { info: (...a) => console.log(...a), warn: (...a) => console.warn(...a) };
148
+ const assembler = new ContextAssembler(cfg, log);
149
+ const subagentTracker = new SubagentTracker(cfg, log);
142
150
 
143
- log.info(`[evermemos] ContextEngine config: baseUrl=${cfg.serverUrl}, userId=${cfg.userId}`);
151
+ log.info(`[everos] ContextEngine config: baseUrl=${cfg.serverUrl}, userId=${cfg.userId}`);
144
152
 
145
153
  // Session state - shared across all sessions for this engine instance
146
154
  const sessionState = new Map();
155
+ const SESSION_TTL_MS = 2 * 60 * 60 * 1000; // 2 hours
156
+
157
+ function pruneStaleSessionState() {
158
+ const now = Date.now();
159
+ for (const [key, state] of sessionState) {
160
+ if (now - (state.lastActiveTime || 0) > SESSION_TTL_MS) {
161
+ sessionState.delete(key);
162
+ log.info(`[everos] pruned stale session state: ${key}`);
163
+ }
164
+ }
165
+ }
147
166
 
148
167
  return {
149
168
  info: {
150
169
  id: PLUGIN_ID,
151
- name: "EverMemOS ContextEngine",
152
- version: "1.0.0",
170
+ name: pluginMeta.name,
171
+ version: PLUGIN_VERSION,
153
172
  ownsCompaction: false,
154
173
  },
155
174
 
156
175
  async bootstrap({ sessionId, sessionKey }) {
157
- log.info(`[evermemos] bootstrap: session=${sessionId}, key=${sessionKey}`);
176
+ log.info(`[everos] bootstrap: session=${sessionId}, key=${sessionKey}`);
158
177
 
159
- // Verify EverMemOS backend health
178
+ // Verify EverOS backend health
160
179
  try {
161
180
  const response = await fetch(`${cfg.serverUrl}/health`, {
162
181
  signal: AbortSignal.timeout(5000),
163
182
  });
164
183
  if (response.ok) {
165
184
  const result = await response.json();
166
- log.info(`[evermemos] bootstrap: backend healthy, status=${result.status}`);
185
+ log.info(`[everos] bootstrap: backend healthy, status=${result.status}`);
167
186
  } else {
168
- log.warn(`[evermemos] bootstrap: backend unhealthy, status=${response.status}`);
187
+ log.warn(`[everos] bootstrap: backend unhealthy, status=${response.status}`);
169
188
  }
170
189
  } catch (err) {
171
- log.warn(`[evermemos] bootstrap: health check failed: ${err.message}`);
190
+ log.warn(`[everos] bootstrap: health check failed: ${err.message}`);
172
191
  }
173
192
 
174
193
  // Initialize or get session state
@@ -176,83 +195,81 @@ function createContextEngineInstance(pluginConfig, logger) {
176
195
  sessionState.set(sessionKey, {
177
196
  turnCount: 0,
178
197
  lastAssembleTime: 0,
198
+ lastActiveTime: Date.now(),
179
199
  pendingFlush: false,
180
- pendingMessages: [],
181
200
  });
182
- log.info(`[evermemos] bootstrap: initialized state for ${sessionKey}`);
201
+ log.info(`[everos] bootstrap: initialized state for ${sessionKey}`);
183
202
  } else {
184
- log.info(`[evermemos] bootstrap: reusing existing state for ${sessionKey}, turn=${sessionState.get(sessionKey).turnCount}`);
203
+ log.info(`[everos] bootstrap: reusing existing state for ${sessionKey}, turn=${sessionState.get(sessionKey).turnCount}`);
185
204
  }
186
205
 
187
206
  return { bootstrapped: true };
188
207
  },
189
208
 
190
209
  async ingest({ sessionId, sessionKey, message }) {
191
- log.info(`[evermemos] ingest: session=${sessionKey}, role=${message?.role}, isHeartbeat=${message?.isHeartbeat}`);
192
-
193
- const state = sessionState.get(sessionKey);
194
- if (!state) {
195
- log.warn(`[evermemos] ingest: no state for session=${sessionKey}`);
196
- return { ingested: false };
197
- }
210
+ log.info(`[everos] ingest: session=${sessionKey}, role=${message?.role}, isHeartbeat=${message?.isHeartbeat}`);
198
211
 
199
212
  // Don't ingest heartbeats
200
213
  if (message.isHeartbeat) {
201
214
  return { ingested: false };
202
215
  }
203
216
 
204
- // Store for batch processing in afterTurn
205
- state.pendingMessages.push(message);
206
- log.info(`[evermemos] ingest: collected ${state.pendingMessages.length} messages so far`);
217
+ if (!sessionState.has(sessionKey)) {
218
+ log.warn(`[everos] ingest: no state for session=${sessionKey}`);
219
+ return { ingested: false };
220
+ }
207
221
 
222
+ // Messages are not buffered here; actual saving happens in afterTurn()
223
+ // where the full message list is available from the OpenClaw runtime.
208
224
  return { ingested: true };
209
225
  },
210
226
 
211
227
  async ingestBatch({ sessionId, sessionKey, messages, isHeartbeat }) {
212
- log.info(`[evermemos] ingestBatch: session=${sessionKey}, count=${messages?.length}, isHeartbeat=${isHeartbeat}`);
228
+ log.info(`[everos] ingestBatch: session=${sessionKey}, count=${messages?.length}, isHeartbeat=${isHeartbeat}`);
213
229
 
214
230
  if (isHeartbeat) {
215
231
  return { ingestedCount: 0 };
216
232
  }
217
233
 
218
- const state = sessionState.get(sessionKey);
219
- if (!state) {
220
- log.warn(`[evermemos] ingestBatch: no state for session=${sessionKey}`);
234
+ if (!sessionState.has(sessionKey)) {
235
+ log.warn(`[everos] ingestBatch: no state for session=${sessionKey}`);
221
236
  return { ingestedCount: 0 };
222
237
  }
223
238
 
224
- // Store messages for later processing
225
- state.pendingMessages.push(...messages);
226
- log.info(`[evermemos] ingestBatch: collected ${state.pendingMessages.length} messages so far`);
227
-
228
- return { ingestedCount: messages.length };
239
+ // Messages are not buffered here; actual saving happens in afterTurn()
240
+ // where the full message list is available from the OpenClaw runtime.
241
+ return { ingestedCount: 0 };
229
242
  },
230
243
 
231
244
  async afterTurn({ sessionId, sessionKey, messages, prePromptMessageCount }) {
232
245
  const state = sessionState.get(sessionKey);
233
246
  if (!state) {
234
- log.warn(`[evermemos] afterTurn: no state for session=${sessionKey}`);
247
+ log.warn(`[everos] afterTurn: no state for session=${sessionKey}`);
235
248
  return;
236
249
  }
237
250
 
238
251
  state.turnCount++;
252
+ state.lastActiveTime = Date.now();
239
253
 
240
- // Get new messages (those added after prePromptMessageCount)
254
+ // When OpenClaw provides prePromptMessageCount, save only the new tail.
255
+ // Otherwise, fall back to the last conversational turn to avoid re-saving the full session.
241
256
  const newMessages = prePromptMessageCount !== undefined
242
257
  ? messages.slice(prePromptMessageCount)
243
- : messages;
258
+ : collectMessages(messages);
244
259
 
245
- log.info(`[evermemos] afterTurn: session=${sessionKey}, turn=${state.turnCount}, totalMessages=${messages.length}, newMessages=${newMessages.length}`);
260
+ log.info(`[everos] afterTurn: session=${sessionKey}, turn=${state.turnCount}, totalMessages=${messages.length}, newMessages=${newMessages.length}`);
246
261
 
247
262
  if (newMessages.length === 0) {
248
- log.info(`[evermemos] afterTurn: session=${sessionKey}, turn=${state.turnCount}, no new messages to save`);
263
+ log.info(`[everos] afterTurn: session=${sessionKey}, turn=${state.turnCount}, no new messages to save`);
249
264
  return;
250
265
  }
251
266
 
252
267
  try {
253
- const evermemosMessages = newMessages.map(convertMessage).filter((m) => m.content);
268
+ const evermemosMessages = prePromptMessageCount !== undefined
269
+ ? newMessages.map(convertMessage).filter((m) => m.content)
270
+ : newMessages.filter((m) => m.content);
254
271
  if (evermemosMessages.length === 0) {
255
- log.info(`[evermemos] afterTurn: session=${sessionKey}, turn=${state.turnCount}, no valid messages to save`);
272
+ log.info(`[everos] afterTurn: session=${sessionKey}, turn=${state.turnCount}, no valid messages to save`);
256
273
  return;
257
274
  }
258
275
  await saveMemories(cfg, {
@@ -260,35 +277,39 @@ function createContextEngineInstance(pluginConfig, logger) {
260
277
  groupId: cfg.groupId,
261
278
  messages: evermemosMessages,
262
279
  flush: state.pendingFlush || false,
263
- });
264
- log.info(`[evermemos] afterTurn: session=${sessionKey}, turn=${state.turnCount}, saved ${evermemosMessages.length} messages`);
280
+ }, log);
281
+ log.info(`[everos] afterTurn: session=${sessionKey}, turn=${state.turnCount}, saved ${evermemosMessages.length} messages`);
265
282
 
266
283
  if (state.pendingFlush) {
267
284
  state.pendingFlush = false;
268
- log.info(`[evermemos] afterTurn: flush flag consumed`);
285
+ log.info(`[everos] afterTurn: flush flag consumed`);
269
286
  }
270
287
  } catch (err) {
271
- log.warn(`[evermemos] afterTurn: save failed: ${err.message}`);
288
+ log.warn(`[everos] afterTurn: save failed: ${err.message}`);
272
289
  }
273
290
  },
274
291
 
275
292
  async assemble({ sessionId, sessionKey, messages, tokenBudget }) {
293
+ // Periodically prune stale sessions to prevent memory leaks
294
+ pruneStaleSessionState();
295
+
276
296
  // Initialize state if not exists (assemble can be called before bootstrap)
277
297
  if (!sessionState.has(sessionKey)) {
278
298
  sessionState.set(sessionKey, {
279
299
  turnCount: 0,
280
300
  lastAssembleTime: 0,
301
+ lastActiveTime: Date.now(),
281
302
  pendingFlush: false,
282
- pendingMessages: [],
283
303
  });
284
- log.info(`[evermemos] assemble: initialized state for ${sessionKey}`);
304
+ log.info(`[everos] assemble: initialized state for ${sessionKey}`);
285
305
  }
286
306
 
287
307
  const state = sessionState.get(sessionKey);
308
+ state.lastActiveTime = Date.now();
288
309
 
289
310
  // Get the last user message as query
290
311
  const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
291
- const query = lastUserMsg ? toText(lastUserMsg) : "";
312
+ const query = lastUserMsg ? toText(lastUserMsg.content) : "";
292
313
 
293
314
  if (!query || query.length < 3) {
294
315
  return { messages, estimatedTokens: 0 };
@@ -296,7 +317,7 @@ function createContextEngineInstance(pluginConfig, logger) {
296
317
 
297
318
  // Detect session reset - flush memory but keep current messages
298
319
  if (isSessionResetPrompt(query)) {
299
- log.info(`[evermemos] assemble: session reset detected, keeping current messages`);
320
+ log.info(`[everos] assemble: session reset detected, keeping current messages`);
300
321
  state.pendingFlush = true;
301
322
  // Return original messages without memory injection
302
323
  // The reset intent is captured in the query itself
@@ -313,14 +334,14 @@ function createContextEngineInstance(pluginConfig, logger) {
313
334
  const params = {
314
335
  query,
315
336
  user_id: cfg.userId,
316
- group_ids: cfg.groupId ? [cfg.groupId] : undefined,
337
+ group_id: cfg.groupId || undefined,
317
338
  memory_types: cfg.memoryTypes,
318
339
  retrieve_method: cfg.retrieveMethod,
319
- top_k,
340
+ top_k: topK,
320
341
  };
321
342
 
322
- const result = await searchMemories(cfg, params);
323
- const parsed = parseSearchResponse(result);
343
+ const result = await searchMemories(cfg, params, log);
344
+ const parsed = parseSearchResponse(result) || { episodic: [], traits: [], case: null, skill: null };
324
345
 
325
346
  const memoryCount =
326
347
  (parsed.episodic?.length || 0) +
@@ -340,26 +361,90 @@ function createContextEngineInstance(pluginConfig, logger) {
340
361
  _memory: true,
341
362
  };
342
363
 
343
- log.info(`[evermemos] assemble: session=${sessionKey}, retrieved ${memoryCount} memories`);
364
+ log.info(`[everos] assemble: session=${sessionKey}, retrieved ${memoryCount} memories`);
344
365
 
345
366
  // Return memory message + existing messages
346
367
  return {
347
368
  messages: [memoryMessage, ...messages],
348
- estimatedTokens: Math.floor((memoryText.length + JSON.stringify(messages).length) / 3),
369
+ estimatedTokens: Math.floor(memoryText.length / 4),
349
370
  };
350
371
  } catch (err) {
351
- log.warn(`[evermemos] assemble: ${err.message}`);
372
+ log.warn(`[everos] assemble: ${err.message}`);
352
373
  return { messages, estimatedTokens: 0 };
353
374
  }
354
375
  },
355
376
 
377
+ async prepareSubagentSpawn({ sessionKey, subagentId, prompt, subagentType }) {
378
+ if (!sessionState.has(sessionKey)) {
379
+ sessionState.set(sessionKey, {
380
+ turnCount: 0,
381
+ lastAssembleTime: 0,
382
+ lastActiveTime: Date.now(),
383
+ pendingFlush: false,
384
+ });
385
+ }
386
+
387
+ const state = sessionState.get(sessionKey);
388
+ subagentTracker.register(subagentId, {
389
+ subagentType,
390
+ parentTurnCount: state.turnCount,
391
+ });
392
+
393
+ try {
394
+ const query = toText(prompt);
395
+ const prependContext = query ? await assembler.assembleForSubagent(query) : "";
396
+ return {
397
+ prependContext,
398
+ metadata: {
399
+ subagentId,
400
+ parentTurnCount: state.turnCount,
401
+ },
402
+ };
403
+ } catch (err) {
404
+ log.warn(`[everos] prepareSubagentSpawn: ${err.message}`);
405
+ return {
406
+ prependContext: "",
407
+ metadata: {
408
+ subagentId,
409
+ parentTurnCount: state.turnCount,
410
+ },
411
+ };
412
+ }
413
+ },
414
+
415
+ async onSubagentEnded({ subagentId, messages, success }) {
416
+ subagentTracker.unregister(subagentId);
417
+
418
+ if (!success || !messages?.length) {
419
+ return;
420
+ }
421
+
422
+ try {
423
+ const collected = collectMessages(messages);
424
+ if (!collected.length) {
425
+ return;
426
+ }
427
+
428
+ await saveMemories(cfg, {
429
+ userId: cfg.userId,
430
+ groupId: cfg.groupId,
431
+ messages: collected,
432
+ flush: false,
433
+ }, log);
434
+
435
+ log.info(`[everos] onSubagentEnded: saved ${collected.length} messages from subagent ${subagentId}`);
436
+ } catch (err) {
437
+ log.warn(`[everos] onSubagentEnded: ${err.message}`);
438
+ }
439
+ },
440
+
356
441
  async compact({ sessionId, sessionKey, tokenBudget, currentTokenCount }) {
357
442
  const state = sessionState.get(sessionKey);
358
443
  if (!state) {
359
444
  return { ok: true, compacted: false, reason: "no session state" };
360
445
  }
361
446
 
362
- log.info(`[evermemos] compact: session=${sessionKey}, tokens=${currentTokenCount}, budget=${tokenBudget}`);
447
+ log.info(`[everos] compact: session=${sessionKey}, tokens=${currentTokenCount}, budget=${tokenBudget}`);
363
448
 
364
449
  // Simple compaction strategy: if over 80% of budget, recommend compaction
365
450
  const threshold = tokenBudget ? tokenBudget * 0.8 : 8000;
@@ -374,9 +459,15 @@ function createContextEngineInstance(pluginConfig, logger) {
374
459
  return { ok: true, compacted: false, reason: "within threshold" };
375
460
  },
376
461
 
377
- async dispose() {
378
- // Clean up session states
379
- sessionState.clear();
462
+ async dispose({ sessionKey } = {}) {
463
+ if (sessionKey && sessionState.has(sessionKey)) {
464
+ sessionState.delete(sessionKey);
465
+ log.info(`[everos] dispose: cleared state for ${sessionKey}`);
466
+ } else if (!sessionKey) {
467
+ // No sessionKey = global shutdown; clear all sessions to avoid leaks
468
+ sessionState.clear();
469
+ log.info("[everos] dispose: cleared all session states");
470
+ }
380
471
  },
381
472
  };
382
473
  }
@@ -388,7 +479,7 @@ function createContextEngineInstance(pluginConfig, logger) {
388
479
  export default function register(api) {
389
480
  const log = api.logger || { info: (...a) => console.log(...a), warn: (...a) => console.warn(...a) };
390
481
 
391
- log.info(`[evermemos] Registering EverMemOS ContextEngine plugin`);
482
+ log.info(`[everos] Registering EverOS OpenClaw Plugin`);
392
483
 
393
484
  // Register the ContextEngine factory
394
485
  api.registerContextEngine(PLUGIN_ID, (pluginConfig) => {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "@evermind-ai/openclaw-plugin",
3
- "name": "EverMemOS ContextEngine",
4
- "description": "Full-lifecycle memory management with EverMemOS - supports bootstrap, assemble, afterTurn, compact, and subagent tracking",
5
- "version": "1.0.0",
3
+ "name": "EverOS OpenClaw Plugin",
4
+ "description": "Full-lifecycle memory management for OpenClaw through the EverOS backend, powered by EverMemOS",
5
+ "version": "1.3.0",
6
6
  "kind": "context-engine",
7
7
  "contextEngine": true,
8
8
  "main": "./index.js",
@@ -12,18 +12,18 @@
12
12
  "properties": {
13
13
  "baseUrl": {
14
14
  "type": "string",
15
- "description": "EverMemOS server base URL",
15
+ "description": "EverOS backend base URL",
16
16
  "default": "http://localhost:1995"
17
17
  },
18
18
  "userId": {
19
19
  "type": "string",
20
20
  "description": "Identity used for memory ownership and as message sender",
21
- "default": "evermemos-user"
21
+ "default": "everos-user"
22
22
  },
23
23
  "groupId": {
24
24
  "type": "string",
25
25
  "description": "Group id for shared memory",
26
- "default": "evermemos-group"
26
+ "default": "everos-group"
27
27
  },
28
28
  "topK": {
29
29
  "type": "integer",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "memoryTypes": {
34
34
  "type": "array",
35
- "description": "EverMemOS memory types to search",
35
+ "description": "EverOS memory types to search",
36
36
  "items": {
37
37
  "type": "string",
38
38
  "enum": ["episodic_memory", "profile", "agent_skill", "agent_case"]
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "retrieveMethod": {
43
43
  "type": "string",
44
- "description": "Retrieval strategy used by EverMemOS",
44
+ "description": "Retrieval strategy used by the EverOS backend",
45
45
  "enum": ["keyword", "vector", "hybrid", "rrf", "agentic"],
46
46
  "default": "hybrid"
47
47
  }
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@evermind-ai/openclaw-plugin",
3
- "version": "1.1.0",
4
- "description": "EverMemOS ContextEngine integration for OpenClaw 3.8+",
3
+ "version": "1.3.0",
4
+ "description": "EverOS OpenClaw Plugin for OpenClaw 3.8+",
5
5
  "type": "module",
6
6
  "main": "./index.js",
7
+ "bin": {
8
+ "everos-install": "./bin/install.js"
9
+ },
7
10
  "exports": {
8
11
  ".": "./index.js"
9
12
  },
@@ -12,6 +15,8 @@
12
15
  "openclaw.plugin.json",
13
16
  "README.md",
14
17
  "README.zh.md",
18
+ "SKILL.md",
19
+ "bin/",
15
20
  "src/"
16
21
  ],
17
22
  "keywords": [
package/src/assembler.js CHANGED
@@ -42,16 +42,16 @@ export class ContextAssembler {
42
42
  const params = {
43
43
  query,
44
44
  user_id: this.cfg.userId,
45
- group_ids: this.cfg.groupId ? [this.cfg.groupId] : undefined,
45
+ group_id: this.cfg.groupId || undefined,
46
46
  memory_types: this.cfg.memoryTypes,
47
47
  retrieve_method: this.cfg.retrieveMethod,
48
48
  top_k: topK,
49
49
  };
50
50
 
51
51
  /** @type {any} */
52
- const result = await searchMemories(this.cfg, params);
52
+ const result = await searchMemories(this.cfg, params, this.log);
53
53
  /** @type {ParsedMemoryResponse} */
54
- const parsed = parseSearchResponse(result);
54
+ const parsed = parseSearchResponse(result) || { episodic: [], traits: [], case: null, skill: null };
55
55
 
56
56
  // Count total memories
57
57
  const memoryCount =
@@ -79,16 +79,16 @@ export class ContextAssembler {
79
79
  const params = {
80
80
  query,
81
81
  user_id: this.cfg.userId,
82
- group_ids: this.cfg.groupId ? [this.cfg.groupId] : undefined,
82
+ group_id: this.cfg.groupId || undefined,
83
83
  memory_types: this.cfg.memoryTypes,
84
84
  retrieve_method: this.cfg.retrieveMethod,
85
85
  top_k: topK,
86
86
  };
87
87
 
88
88
  /** @type {any} */
89
- const result = await searchMemories(this.cfg, params);
89
+ const result = await searchMemories(this.cfg, params, this.log);
90
90
  /** @type {ParsedMemoryResponse} */
91
- const parsed = parseSearchResponse(result);
91
+ const parsed = parseSearchResponse(result) || { episodic: [], traits: [], case: null, skill: null };
92
92
 
93
93
  // Use no code block for subagents (cleaner format)
94
94
  return buildMemoryPrompt(parsed, { wrapInCodeBlock: false });
package/src/config.js CHANGED
@@ -5,8 +5,8 @@ export const TIMEOUT_MS = 60000;
5
5
  export function resolveConfig(pc = {}) {
6
6
  return {
7
7
  serverUrl: (pc.baseUrl || DEFAULT_URL).replace(/\/*$/, ""),
8
- userId: pc.userId || "evermemos-user",
9
- groupId: pc.groupId || "evermemos-group",
8
+ userId: pc.userId || "everos-user",
9
+ groupId: pc.groupId || "everos-group",
10
10
  topK: pc.topK ?? 5,
11
11
  memoryTypes: pc.memoryTypes ?? ["episodic_memory", "profile", "agent_skill", "agent_case"],
12
12
  retrieveMethod: pc.retrieveMethod ?? "hybrid",
package/src/formatter.js CHANGED
@@ -1,4 +1,4 @@
1
- export const CONTEXT_BOUNDARY = "user\u200b原\u200b始\u200bquery\u200b:\u200b\u200b\u200b\u200b";
1
+ export const CONTEXT_BOUNDARY = "user\u200boriginal\u200bquery\u200b:\u200b\u200b\u200b\u200b";
2
2
 
3
3
  function timestampToLabel(ts) {
4
4
  if (ts == null || ts === "") return "";
@@ -39,7 +39,8 @@ export async function request(cfg, method, path, params) {
39
39
 
40
40
  try {
41
41
  return await send(url, method, headers, body, ms);
42
- } catch {
42
+ } catch (err) {
43
+ console.warn(`[everos] first request failed, retrying: ${err.message}`);
43
44
  await sleep(150);
44
45
  return send(url, method, headers, body, ms);
45
46
  }
package/src/memory-api.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { request } from "./http-client.js";
2
2
 
3
- export async function searchMemories(cfg, params) {
3
+ const noop = { info() {}, warn() {} };
4
+
5
+ export async function searchMemories(cfg, params, log = noop) {
4
6
  const { memory_types, ...baseParams } = params;
5
7
 
6
8
  const episodicTypes = (memory_types ?? []).filter((t) => t === "episodic_memory" || t === "profile");
@@ -13,9 +15,9 @@ export async function searchMemories(cfg, params) {
13
15
  const results = await Promise.all(
14
16
  searches.map(async ({ label, types }) => {
15
17
  const p = { ...baseParams, memory_types: types };
16
- console.log("[memory-api] GET /api/v1/memories/search", label, JSON.stringify(p));
18
+ log.info(`[everos] GET /api/v1/memories/search ${label}`, JSON.stringify(p));
17
19
  const r = await request(cfg, "GET", "/api/v1/memories/search", p);
18
- console.log("[memory-api] GET response", label, JSON.stringify(r));
20
+ log.info(`[everos] GET response ${label}`, JSON.stringify(r));
19
21
  return r;
20
22
  }),
21
23
  );
@@ -35,15 +37,16 @@ export async function searchMemories(cfg, params) {
35
37
  return merged;
36
38
  }
37
39
 
38
- export async function saveMemories(cfg, { userId, groupId, messages = [], flush = false }) {
40
+ export async function saveMemories(cfg, { userId, groupId, messages = [], flush = false }, log = noop) {
39
41
  if (!messages.length) return;
40
42
  const stamp = Date.now();
41
- for (let i = 0; i < messages.length; i++) {
42
- const { role = "user", content = "", tool_calls, tool_call_id } = messages[i];
43
+
44
+ const payloads = messages.map((msg, i) => {
45
+ const { role = "user", content = "", tool_calls, tool_call_id } = msg;
43
46
  const sender = role === "assistant" ? role : (role === "tool" ? "tool" : userId);
44
47
  const isLast = i === messages.length - 1;
45
48
 
46
- const payload = {
49
+ return {
47
50
  message_id: `em_${stamp}_${i}`,
48
51
  create_time: new Date().toISOString(),
49
52
  role,
@@ -58,20 +61,13 @@ export async function saveMemories(cfg, { userId, groupId, messages = [], flush
58
61
  ...(tool_call_id && { tool_call_id }),
59
62
  ...(flush && isLast && { flush: true }),
60
63
  };
61
- console.log("[memory-api] POST /api/v1/memories", JSON.stringify(payload));
62
- const result = await request(cfg, "POST", "/api/v1/memories", payload);
63
- console.log("[memory-api] POST response", JSON.stringify(result));
64
- }
65
- }
64
+ });
66
65
 
67
- export async function deleteMemories(cfg, { memoryId, userId, groupId }) {
68
- const payload = {};
69
- if (memoryId) payload.memory_id = memoryId;
70
- if (userId) payload.user_id = userId;
71
- if (groupId) payload.group_id = groupId;
72
-
73
- console.log("[memory-api] DELETE /api/v1/memories", JSON.stringify(payload));
74
- const result = await request(cfg, "DELETE", "/api/v1/memories", payload);
75
- console.log("[memory-api] DELETE response", JSON.stringify(result));
76
- return result;
66
+ await Promise.all(
67
+ payloads.map(async (payload) => {
68
+ log.info(`[everos] POST /api/v1/memories`, JSON.stringify(payload));
69
+ const result = await request(cfg, "POST", "/api/v1/memories", payload);
70
+ log.info(`[everos] POST response`, JSON.stringify(result));
71
+ }),
72
+ );
77
73
  }
package/src/subagent.js CHANGED
@@ -38,7 +38,7 @@ export class SubagentTracker {
38
38
  startTime: Date.now(),
39
39
  ...metadata,
40
40
  });
41
- this.log(`[evermemos] subagent tracker: registered ${subagentId}`);
41
+ this.log.info(`[everos] subagent tracker: registered ${subagentId}`);
42
42
  }
43
43
 
44
44
  /**
@@ -49,7 +49,7 @@ export class SubagentTracker {
49
49
  unregister(subagentId) {
50
50
  const removed = this.activeSubagents.delete(subagentId);
51
51
  if (removed) {
52
- this.log(`[evermemos] subagent tracker: unregistered ${subagentId}`);
52
+ this.log.info(`[everos] subagent tracker: unregistered ${subagentId}`);
53
53
  }
54
54
  return removed;
55
55
  }
@@ -108,7 +108,7 @@ export class SubagentTracker {
108
108
  }
109
109
 
110
110
  if (stale.length > 0) {
111
- this.log(`[evermemos] subagent tracker: cleaned up ${stale.length} stale subagents`);
111
+ this.log.info(`[everos] subagent tracker: cleaned up ${stale.length} stale subagents`);
112
112
  }
113
113
 
114
114
  return stale;