memorylake-openclaw 0.0.14 → 0.0.15-beta.2

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.
Files changed (2) hide show
  1. package/index.ts +118 -3
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -68,6 +68,52 @@ interface MemoryItem {
68
68
  user_id?: string;
69
69
  created_at?: string;
70
70
  updated_at?: string;
71
+ has_unresolved_conflict?: boolean;
72
+ }
73
+
74
+ interface ConflictMemorySnapshot {
75
+ memory_id: string;
76
+ memory_history_id?: string;
77
+ memory_text: string;
78
+ }
79
+
80
+ interface ConflictFileChunk {
81
+ chunk: { type?: string; text: string; range?: string };
82
+ document_id?: string;
83
+ document_name?: string;
84
+ }
85
+
86
+ interface ConflictResolve {
87
+ id: string;
88
+ strategy: string;
89
+ keep_memory_id?: string;
90
+ forgotten_memory_ids?: string[];
91
+ resolved_by?: string;
92
+ created_at?: string;
93
+ }
94
+
95
+ interface ConflictItem {
96
+ id: string;
97
+ name: string;
98
+ description: string;
99
+ category: "m2m" | "m2d";
100
+ conflict_type: "logical" | "knowledge";
101
+ memory_ids: string[];
102
+ memory_snapshots: ConflictMemorySnapshot[];
103
+ file_chunks: ConflictFileChunk[];
104
+ resolved: boolean;
105
+ resolve?: ConflictResolve;
106
+ stale?: boolean;
107
+ event_id?: string;
108
+ created_at?: string;
109
+ updated_at?: string;
110
+ }
111
+
112
+ interface ConflictListResponse {
113
+ items: ConflictItem[];
114
+ page: number;
115
+ total: number;
116
+ page_size: number;
71
117
  }
72
118
 
73
119
  interface AddResultItem {
@@ -250,6 +296,7 @@ interface MemoryLakeProvider {
250
296
  searchWeb(query: string, options: WebSearchOptions): Promise<WebSearchResponse>;
251
297
  searchOpenData(query: string, options: OpenDataSearchOptions): Promise<OpenDataSearchResponse>;
252
298
  getProject(): Promise<ProjectInfo>;
299
+ listConflicts(memoryIds: string[], userId: string): Promise<ConflictItem[]>;
253
300
  }
254
301
 
255
302
  // ============================================================================
@@ -270,14 +317,17 @@ class PlatformProvider implements MemoryLakeProvider {
270
317
  private readonly webSearchPath: string;
271
318
  private readonly openDataSearchPath: string;
272
319
  private readonly projectPath: string;
273
-
320
+ private readonly conflictsPath: string;
321
+ private readonly projectId: string;
274
322
 
275
323
  constructor(host: string, apiKey: string, projectId: string) {
324
+ this.projectId = projectId;
276
325
  this.basePath = `openapi/memorylake/api/v2/projects/${projectId}/memories`;
277
326
  this.docSearchPath = `openapi/memorylake/api/v1/projects/${projectId}/documents/search`;
278
327
  this.webSearchPath = "openapi/memorylake/api/v1/search";
279
328
  this.openDataSearchPath = "openapi/memorylake/api/v1/search/opendata";
280
329
  this.projectPath = `openapi/memorylake/api/v1/projects/${projectId}`;
330
+ this.conflictsPath = `openapi/memorylake/api/v2/projects/${projectId}/memories/conflicts`;
281
331
  this.http = got.extend({
282
332
  prefixUrl: host,
283
333
  headers: {
@@ -310,6 +360,7 @@ class PlatformProvider implements MemoryLakeProvider {
310
360
  const body: Record<string, unknown> = {
311
361
  query,
312
362
  user_id: options.user_id,
363
+ with_conflicts: true,
313
364
  };
314
365
  if (options.top_k != null) body.top_k = options.top_k;
315
366
  if (options.threshold != null) body.threshold = options.threshold;
@@ -422,6 +473,23 @@ class PlatformProvider implements MemoryLakeProvider {
422
473
  return info;
423
474
  }
424
475
 
476
+ async listConflicts(memoryIds: string[], userId: string): Promise<ConflictItem[]> {
477
+ if (memoryIds.length === 0) return [];
478
+ const searchParams: Record<string, string> = {
479
+ resolved: "false",
480
+ memory_ids: memoryIds.join(","),
481
+ };
482
+ const resp = await this.http
483
+ .get(this.conflictsPath, {
484
+ searchParams,
485
+ headers: { "X-User-ID": userId },
486
+ })
487
+ .json<ApiResponse<ConflictListResponse>>();
488
+ if (!resp.success) throw new Error(resp.message ?? "list conflicts failed");
489
+ const data = resp.data;
490
+ return Array.isArray(data?.items) ? data.items : [];
491
+ }
492
+
425
493
  }
426
494
 
427
495
  // ============================================================================
@@ -435,6 +503,7 @@ function normalizeMemoryItem(raw: any): MemoryItem {
435
503
  user_id: raw.user_id,
436
504
  created_at: raw.created_at,
437
505
  updated_at: raw.updated_at,
506
+ has_unresolved_conflict: raw.has_unresolved_conflict ?? false,
438
507
  };
439
508
  }
440
509
 
@@ -548,6 +617,24 @@ function buildWebSearchContext(results: WebSearchResult[]): string {
548
617
  .join("\n\n");
549
618
  }
550
619
 
620
+ function buildConflictContext(conflicts: ConflictItem[], maxChunkLength = 200): string {
621
+ return conflicts
622
+ .map((c) => {
623
+ const parts: string[] = [
624
+ `- [${c.conflict_type}] ${c.description}`,
625
+ ];
626
+ for (const snap of c.memory_snapshots ?? []) {
627
+ parts.push(` Memory(${snap.memory_id}): ${snap.memory_text.slice(0, maxChunkLength)}`);
628
+ }
629
+ for (const fc of c.file_chunks ?? []) {
630
+ const docLabel = fc.document_name ?? fc.document_id ?? "unknown";
631
+ parts.push(` Document(${docLabel}): ${fc.chunk.text.slice(0, maxChunkLength)}`);
632
+ }
633
+ return parts.join("\n");
634
+ })
635
+ .join("\n");
636
+ }
637
+
551
638
  function buildOpenDataContext(results: OpenDataSearchResult[]): string {
552
639
  const filtered = results.map((r) => {
553
640
  const item: Record<string, unknown> = {};
@@ -1774,15 +1861,40 @@ const memoryPlugin = {
1774
1861
  const contextParts: string[] = [];
1775
1862
 
1776
1863
  if (memoryResult.status === "fulfilled" && memoryResult.value.length > 0) {
1777
- const memoryContext = memoryResult.value
1864
+ const memories = memoryResult.value;
1865
+ const memoryContext = memories
1778
1866
  .map((r) => `- ${r.content}`)
1779
1867
  .join("\n");
1780
1868
  contextParts.push(
1781
1869
  `<relevant-memories>\nThe following memories may be relevant to this conversation:\n${memoryContext}\n</relevant-memories>`,
1782
1870
  );
1783
1871
  api.logger.info(
1784
- `memorylake-openclaw: injecting ${memoryResult.value.length} memories into context`,
1872
+ `memorylake-openclaw: injecting ${memories.length} memories into context`,
1785
1873
  );
1874
+
1875
+ // Fetch conflict details for memories flagged with unresolved conflicts
1876
+ const conflictedIds = memories
1877
+ .filter((m) => m.has_unresolved_conflict)
1878
+ .map((m) => m.id);
1879
+ if (conflictedIds.length > 0) {
1880
+ try {
1881
+ const conflicts = await effectiveProvider.listConflicts(conflictedIds, effectiveCfg.userId);
1882
+ if (conflicts.length > 0) {
1883
+ const conflictContext = buildConflictContext(conflicts);
1884
+ contextParts.push(
1885
+ `<memory-conflicts>\nThe following conflicts exist among the recalled memories. ` +
1886
+ `Consider these contradictions when using the above memories.\n` +
1887
+ `If you have not already informed the user about these conflicts in this conversation, briefly mention that some recalled memories contain contradictions and note which points are uncertain. Do not repeat this notice if you have already done so.\n` +
1888
+ `${conflictContext}\n</memory-conflicts>`,
1889
+ );
1890
+ api.logger.info(
1891
+ `memorylake-openclaw: injecting ${conflicts.length} memory conflicts into context`,
1892
+ );
1893
+ }
1894
+ } catch (err) {
1895
+ api.logger.warn(`memorylake-openclaw: conflict fetch failed: ${String(err)}`);
1896
+ }
1897
+ }
1786
1898
  } else if (memoryResult.status === "rejected") {
1787
1899
  api.logger.warn(`memorylake-openclaw: memory recall failed: ${String(memoryResult.reason)}`);
1788
1900
  }
@@ -1880,6 +1992,9 @@ const memoryPlugin = {
1880
1992
  if (textContent.includes("<relevant-memories>")) {
1881
1993
  textContent = textContent.replace(/<relevant-memories>[\s\S]*?<\/relevant-memories>\s*/g, "").trim();
1882
1994
  }
1995
+ if (textContent.includes("<memory-conflicts>")) {
1996
+ textContent = textContent.replace(/<memory-conflicts>[\s\S]*?<\/memory-conflicts>\s*/g, "").trim();
1997
+ }
1883
1998
  if (textContent.includes("<relevant-documents>")) {
1884
1999
  textContent = textContent.replace(/<relevant-documents>[\s\S]*?<\/relevant-documents>\s*/g, "").trim();
1885
2000
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorylake-openclaw",
3
- "version": "0.0.14",
3
+ "version": "0.0.15-beta.2",
4
4
  "type": "module",
5
5
  "description": "MemoryLake memory backend for OpenClaw",
6
6
  "license": "MIT",