@contextableai/openclaw-memory-rebac 0.3.7 → 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.
@@ -6,8 +6,8 @@
6
6
  # 1. Install sentence-transformers for BGE reranker
7
7
  # 2. Overlay per-component config + startup to wire separate clients
8
8
  #
9
- # UPGRADE AUDIT (vs upstream getzep/graphiti v0.28.2, audited 2026-03-21):
10
- # Overlay patches in startup.py — 5 of 6 still needed:
9
+ # UPGRADE AUDIT (vs upstream getzep/graphiti v0.28.2, audited 2026-03-24):
10
+ # Overlay patches in startup.py — 6 of 7 still needed:
11
11
  # [NEEDED] AsyncWorker crash-on-error recovery (startup.py:109-126)
12
12
  # [NEEDED] Neo4j nested attribute sanitization (startup.py:128-250)
13
13
  # [SAFE] None-index extract_edges fix (startup.py:252-298)
@@ -15,6 +15,8 @@
15
15
  # [NEEDED] IS_DUPLICATE_OF edge filtering (startup.py:152-169,216-227,310-318)
16
16
  # [NEEDED] Self-referential edge filtering (startup.py:228-239)
17
17
  # [NEEDED] Singleton client / per-request fix (startup.py:38-87)
18
+ # [NEEDED] resolve_extracted_edge IndexError guard (startup.py:325-349)
19
+ # — fixed upstream v0.28.2+; no Docker image published beyond 0.22.0
18
20
  ###############################################################################
19
21
 
20
22
  FROM zepai/graphiti:0.22.0
@@ -322,6 +322,31 @@ def patch():
322
322
  # Patch the local binding in graphiti.py
323
323
  graphiti_mod.extract_edges = safe_extract_edges
324
324
 
325
+ # -- Guard resolve_extracted_edge against IndexError from out-of-bounds
326
+ # contradicted_facts indices (issue #22) --
327
+ # The LLM sometimes returns fact indices that exceed the existing_edges list
328
+ # length, crashing the list comprehension. Upstream v0.28.2+ adds bounds
329
+ # checking; this patch provides equivalent safety for pinned v0.22.0.
330
+ original_resolve_extracted_edge = edge_ops_mod.resolve_extracted_edge
331
+
332
+ async def safe_resolve_extracted_edge(*args, **kwargs):
333
+ try:
334
+ return await original_resolve_extracted_edge(*args, **kwargs)
335
+ except IndexError as e:
336
+ logger.warning(
337
+ "resolve_extracted_edge: IndexError caught (out-of-bounds "
338
+ "contradicted_facts index): %s — returning edge as-is with "
339
+ "no invalidations",
340
+ e,
341
+ )
342
+ # args[1] is extracted_edge per the function signature
343
+ extracted_edge = args[1] if len(args) > 1 else kwargs.get("extracted_edge")
344
+ if extracted_edge is None:
345
+ raise
346
+ return (extracted_edge, [], [])
347
+
348
+ edge_ops_mod.resolve_extracted_edge = safe_resolve_extracted_edge
349
+
325
350
  return app
326
351
 
327
352
 
@@ -28,7 +28,7 @@ services:
28
28
  POSTGRES_PASSWORD: ${SPICEDB_POSTGRES_PASSWORD:-spicedb}
29
29
  POSTGRES_DB: spicedb
30
30
  volumes:
31
- - graphiti_spicedb_postgres_data:/var/lib/postgresql/data
31
+ - spicedb_postgres_data:/var/lib/postgresql/data
32
32
  healthcheck:
33
33
  test: ["CMD-SHELL", "pg_isready -U spicedb"]
34
34
  interval: 5s
@@ -56,8 +56,8 @@ services:
56
56
  command: serve
57
57
  restart: unless-stopped
58
58
  ports:
59
- - "127.0.0.1:${SPICEDB_GRPC_PORT:-50051}:50051" # gRPC
60
- - "127.0.0.1:${SPICEDB_HTTP_PORT:-8080}:8080" # HTTP metrics / healthz
59
+ - "${SPICEDB_GRPC_PORT:-50051}:50051" # gRPC
60
+ - "${SPICEDB_HTTP_PORT:-8180}:8080" # HTTP metrics / healthz
61
61
  environment:
62
62
  SPICEDB_GRPC_PRESHARED_KEY: ${SPICEDB_PRESHARED_KEY:-dev_token}
63
63
  SPICEDB_DATASTORE_ENGINE: postgres
@@ -76,4 +76,4 @@ services:
76
76
  start_period: 10s
77
77
 
78
78
  volumes:
79
- graphiti_spicedb_postgres_data:
79
+ spicedb_postgres_data:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@contextableai/openclaw-memory-rebac",
3
- "version": "0.3.7",
4
- "description": "OpenClaw two-layer memory plugin: SpiceDB ReBAC authorization + Graphiti knowledge graph",
3
+ "version": "0.4.0",
4
+ "description": "OpenClaw two-layer memory plugin: SpiceDB ReBAC authorization + pluggable storage (Graphiti, EverMemOS)",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -34,13 +34,15 @@
34
34
  "openclaw": "*"
35
35
  },
36
36
  "scripts": {
37
- "build": "rm -rf dist && tsc -p tsconfig.build.json && cp plugin.defaults.json dist/ && mkdir -p dist/backends && cp backends/graphiti.defaults.json dist/backends/",
37
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && cp plugin.defaults.json dist/ && mkdir -p dist/backends && cp backends/graphiti.defaults.json backends/evermemos.defaults.json dist/backends/",
38
38
  "prepublishOnly": "npm run build",
39
39
  "cli": "tsx bin/rebac-mem.ts",
40
40
  "typecheck": "tsc 2>&1 | grep -v '../openclaw/' | grep -c 'error TS' | xargs -I{} test {} -eq 0",
41
41
  "test": "vitest run",
42
42
  "test:watch": "vitest",
43
- "test:e2e": "OPENCLAW_LIVE_TEST=1 vitest run --config vitest.e2e.config.ts"
43
+ "test:e2e": "OPENCLAW_LIVE_TEST=1 vitest run --config vitest.e2e.config.ts",
44
+ "test:e2e:backend": "OPENCLAW_LIVE_TEST=1 vitest run --config vitest.e2e.config.ts e2e-backend.test.ts",
45
+ "test:e2e:evermemos": "OPENCLAW_LIVE_TEST=1 E2E_BACKEND=evermemos vitest run --config vitest.e2e.config.ts e2e-evermemos.test.ts"
44
46
  },
45
47
  "dependencies": {
46
48
  "@authzed/authzed-node": "1.6.1",
package/schema.zed CHANGED
@@ -10,8 +10,10 @@ definition agent {
10
10
 
11
11
  definition group {
12
12
  relation member: person | agent
13
- permission access = member
14
- permission contribute = member
13
+ relation owner: person | agent
14
+ permission access = member + owner
15
+ permission contribute = member + owner
16
+ permission admin = owner
15
17
  }
16
18
 
17
19
  definition memory_fragment {
@@ -24,4 +26,6 @@ definition memory_fragment {
24
26
  permission view = involves + shared_by + source_group->access + involves->represents
25
27
  // Can delete if: you shared it (owner-level control)
26
28
  permission delete = shared_by
29
+ // Can share if: you shared it, or you are an admin of the source group
30
+ permission share = shared_by + source_group->admin
27
31
  }