@prmichaelsen/remember-mcp 3.14.20 → 3.15.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/CHANGELOG.md +16 -0
- package/agent/progress.yaml +16 -3
- package/agent/tasks/milestone-15-moderation-space-config/task-202-wire-llm-moderation-client.md +62 -0
- package/agent/tasks/unassigned/task-174-wire-memory-index-service.md +97 -0
- package/dist/server-factory.js +178 -22
- package/dist/server.js +178 -22
- package/package.json +2 -2
- package/src/core-services.ts +12 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.15.0] - 2026-03-06
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Wire `MemoryIndexService` into `MemoryService` and `SpaceService` (task-174)
|
|
13
|
+
- All new memories and space-published memories are now indexed in Firestore lookup table
|
|
14
|
+
- Enables `resolveById()` cross-collection resolution and fixes 404s on published memories
|
|
15
|
+
|
|
16
|
+
## [3.14.21] - 2026-03-06
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Wire LLM auto-moderation into SpaceService publish/revise flow (task-202)
|
|
21
|
+
- Create singleton `ModerationClient` from `ANTHROPIC_API_KEY` env var when present
|
|
22
|
+
- Content published to spaces is now screened by Claude Haiku before storage
|
|
23
|
+
|
|
8
24
|
## [3.14.14] - 2026-03-04
|
|
9
25
|
|
|
10
26
|
### Changed
|
package/agent/progress.yaml
CHANGED
|
@@ -246,10 +246,10 @@ milestones:
|
|
|
246
246
|
status: completed
|
|
247
247
|
progress: 100%
|
|
248
248
|
started: 2026-02-27
|
|
249
|
-
completed: 2026-
|
|
249
|
+
completed: 2026-03-06
|
|
250
250
|
estimated_weeks: 2
|
|
251
|
-
tasks_completed:
|
|
252
|
-
tasks_total:
|
|
251
|
+
tasks_completed: 7
|
|
252
|
+
tasks_total: 7
|
|
253
253
|
notes: |
|
|
254
254
|
✅ Content moderation lifecycle and per-space/group behavioral config.
|
|
255
255
|
✅ Design: agent/design/local.moderation-and-space-config.md (Implemented)
|
|
@@ -702,6 +702,19 @@ tasks:
|
|
|
702
702
|
✅ CHANGELOG, design doc status, version bump 3.9.0→3.10.0
|
|
703
703
|
📋 Verify all tests pass
|
|
704
704
|
|
|
705
|
+
- id: task-202
|
|
706
|
+
name: Wire LLM Moderation Client to SpaceService
|
|
707
|
+
status: completed
|
|
708
|
+
completed_date: 2026-03-06
|
|
709
|
+
file: agent/tasks/milestone-15-moderation-space-config/task-202-wire-llm-moderation-client.md
|
|
710
|
+
estimated_hours: 0.5
|
|
711
|
+
actual_hours: 0.25
|
|
712
|
+
dependencies: [task-179]
|
|
713
|
+
notes: |
|
|
714
|
+
📋 Connect createModerationClient (remember-core) to SpaceService in core-services.ts
|
|
715
|
+
📋 Auto-create when ANTHROPIC_API_KEY env var is present
|
|
716
|
+
📋 ~5 lines of code change
|
|
717
|
+
|
|
705
718
|
milestone_7:
|
|
706
719
|
- id: task-180
|
|
707
720
|
name: Access Result & Permission Types
|
package/agent/tasks/milestone-15-moderation-space-config/task-202-wire-llm-moderation-client.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Task 202: Wire LLM Moderation Client to SpaceService
|
|
2
|
+
|
|
3
|
+
**Milestone**: M15 - Moderation & Space Config (follow-up)
|
|
4
|
+
**Status**: completed
|
|
5
|
+
**Estimated Hours**: 0.5
|
|
6
|
+
**Priority**: P1
|
|
7
|
+
**Dependencies**: M15 (completed), remember-core moderation.service.ts
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Objective
|
|
12
|
+
|
|
13
|
+
Connect the `createModerationClient` from `@prmichaelsen/remember-core` to the `SpaceService` in `remember-mcp`, so that content published to spaces is automatically screened by Claude Haiku before being stored.
|
|
14
|
+
|
|
15
|
+
Currently, all moderation infrastructure exists but is disconnected:
|
|
16
|
+
- `remember-core` exports `createModerationClient()` (calls Anthropic Messages API with Haiku)
|
|
17
|
+
- `SpaceService` accepts `{ moderationClient }` in its constructor options
|
|
18
|
+
- `SpaceService.checkModeration()` calls `moderationClient.moderate()` on publish/revise
|
|
19
|
+
- But `createCoreServices()` in `remember-mcp` never creates or passes a moderation client
|
|
20
|
+
|
|
21
|
+
## Context
|
|
22
|
+
|
|
23
|
+
- `remember-core/src/services/moderation.service.ts` — factory + types
|
|
24
|
+
- `remember-core/src/services/space.service.ts:237` — constructor accepts `options?.moderationClient`
|
|
25
|
+
- `remember-mcp/src/core-services.ts:50` — SpaceService created without moderation client
|
|
26
|
+
- `remember-mcp-server/.env` already has `ANTHROPIC_API_KEY` set
|
|
27
|
+
|
|
28
|
+
## Steps
|
|
29
|
+
|
|
30
|
+
### 1. Edit `src/core-services.ts`
|
|
31
|
+
|
|
32
|
+
- Import `createModerationClient` from `@prmichaelsen/remember-core`
|
|
33
|
+
- Create a singleton moderation client (only when `ANTHROPIC_API_KEY` env var is present)
|
|
34
|
+
- Pass `{ moderationClient }` as the 6th argument to `new SpaceService()`
|
|
35
|
+
|
|
36
|
+
### 2. Build and verify
|
|
37
|
+
|
|
38
|
+
- Run `npm run build` (or equivalent)
|
|
39
|
+
- Verify TypeScript compiles without errors
|
|
40
|
+
|
|
41
|
+
### 3. Run tests
|
|
42
|
+
|
|
43
|
+
- Run existing test suite to ensure no regressions
|
|
44
|
+
- Moderation client is optional, so existing tests should pass unaffected
|
|
45
|
+
|
|
46
|
+
### 4. Version bump
|
|
47
|
+
|
|
48
|
+
- Bump patch version in package.json
|
|
49
|
+
- Update CHANGELOG.md
|
|
50
|
+
|
|
51
|
+
## Verification
|
|
52
|
+
|
|
53
|
+
- [ ] `createModerationClient` imported from remember-core
|
|
54
|
+
- [ ] Moderation client created conditionally (only when `ANTHROPIC_API_KEY` is set)
|
|
55
|
+
- [ ] Moderation client passed to SpaceService constructor
|
|
56
|
+
- [ ] TypeScript compiles without errors
|
|
57
|
+
- [ ] Existing tests pass
|
|
58
|
+
- [ ] Version bumped
|
|
59
|
+
|
|
60
|
+
## Downstream
|
|
61
|
+
|
|
62
|
+
After this ships, `remember-mcp-server` just needs a dependency bump — no code changes required. `ANTHROPIC_API_KEY` is already configured in its `.env`.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Task 174: Wire MemoryIndexService into Core Services
|
|
2
|
+
|
|
3
|
+
**Milestone**: Unassigned (breaking change from remember-core v0.33.0)
|
|
4
|
+
**Estimated Time**: 0.5-1 hour
|
|
5
|
+
**Dependencies**: remember-core v0.33.0+
|
|
6
|
+
**Status**: Not Started
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Objective
|
|
11
|
+
|
|
12
|
+
Update `src/core-services.ts` to pass the now-required `MemoryIndexService` to both `MemoryService` and `SpaceService` constructors. remember-core v0.33.0 made `MemoryIndexService` a required parameter (no longer optional) in both services.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Context
|
|
17
|
+
|
|
18
|
+
remember-core task-117 made `MemoryIndexService` required in:
|
|
19
|
+
- `MemoryService` constructor: 4th param `options.memoryIndex` (was optional, now required)
|
|
20
|
+
- `SpaceService` constructor: new 6th positional param `memoryIndexService` (before the optional `options`)
|
|
21
|
+
|
|
22
|
+
Currently `src/core-services.ts` line 52 constructs `MemoryService` with 3 args (no memoryIndex) and line 54 constructs `SpaceService` with `{ moderationClient }` as the 6th arg (which is now the 7th position).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Steps
|
|
27
|
+
|
|
28
|
+
### 1. Import MemoryIndexService
|
|
29
|
+
|
|
30
|
+
Add `MemoryIndexService` to the import from `@prmichaelsen/remember-core`.
|
|
31
|
+
|
|
32
|
+
### 2. Create singleton MemoryIndexService
|
|
33
|
+
|
|
34
|
+
Add alongside the other singletons (line ~31):
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
const memoryIndexService = new MemoryIndexService(coreLogger);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 3. Update MemoryService constructor call
|
|
41
|
+
|
|
42
|
+
Change line 52 from:
|
|
43
|
+
```typescript
|
|
44
|
+
memory: new MemoryService(collection, userId, coreLogger),
|
|
45
|
+
```
|
|
46
|
+
to:
|
|
47
|
+
```typescript
|
|
48
|
+
memory: new MemoryService(collection, userId, coreLogger, {
|
|
49
|
+
memoryIndex: memoryIndexService,
|
|
50
|
+
weaviateClient,
|
|
51
|
+
}),
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 4. Update SpaceService constructor call
|
|
55
|
+
|
|
56
|
+
Change line 54 from:
|
|
57
|
+
```typescript
|
|
58
|
+
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger, { moderationClient }),
|
|
59
|
+
```
|
|
60
|
+
to:
|
|
61
|
+
```typescript
|
|
62
|
+
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger, memoryIndexService, { moderationClient }),
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 5. Bump remember-core dependency
|
|
66
|
+
|
|
67
|
+
Update `package.json` to require `@prmichaelsen/remember-core` >= 0.33.0.
|
|
68
|
+
|
|
69
|
+
### 6. Build and verify
|
|
70
|
+
|
|
71
|
+
- `tsc --noEmit` passes
|
|
72
|
+
- All tests pass
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Verification
|
|
77
|
+
|
|
78
|
+
- [ ] `MemoryIndexService` imported and instantiated as singleton
|
|
79
|
+
- [ ] `MemoryService` receives `{ memoryIndex: memoryIndexService, weaviateClient }` in options
|
|
80
|
+
- [ ] `SpaceService` receives `memoryIndexService` as 6th positional arg
|
|
81
|
+
- [ ] `tsc --noEmit` passes
|
|
82
|
+
- [ ] All tests pass
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Files Modified
|
|
87
|
+
|
|
88
|
+
- `src/core-services.ts` — add import, singleton, update constructor calls
|
|
89
|
+
- `package.json` — bump remember-core dependency
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Notes
|
|
94
|
+
|
|
95
|
+
- Single file change (`core-services.ts`) — all service construction is centralized here
|
|
96
|
+
- `weaviateClient` is already available in `createCoreServices()`, so passing it to MemoryService options enables `resolveById()` cross-collection resolution
|
|
97
|
+
- This ensures all new memories created via MCP tools are indexed in the Firestore lookup table
|
package/dist/server-factory.js
CHANGED
|
@@ -1754,6 +1754,9 @@ function getUserPreferencesPath(userId) {
|
|
|
1754
1754
|
function getCollectionRegistryPath() {
|
|
1755
1755
|
return `${BASE}.collection_registry`;
|
|
1756
1756
|
}
|
|
1757
|
+
function getMemoryIndexPath() {
|
|
1758
|
+
return `${BASE}.memory_index`;
|
|
1759
|
+
}
|
|
1757
1760
|
|
|
1758
1761
|
// node_modules/@prmichaelsen/remember-core/dist/services/preferences.service.js
|
|
1759
1762
|
var PreferencesDatabaseService = class {
|
|
@@ -2143,20 +2146,18 @@ var MemoryService = class {
|
|
|
2143
2146
|
* Requires `memoryIndex` and `weaviateClient` in constructor options.
|
|
2144
2147
|
*/
|
|
2145
2148
|
async resolveById(memoryId) {
|
|
2146
|
-
if (!this.options
|
|
2149
|
+
if (!this.options.weaviateClient) {
|
|
2147
2150
|
throw new Error("resolveById requires weaviateClient in options");
|
|
2148
2151
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
};
|
|
2159
|
-
}
|
|
2152
|
+
const collectionName = await this.options.memoryIndex.lookup(memoryId);
|
|
2153
|
+
if (collectionName) {
|
|
2154
|
+
const col = this.options.weaviateClient.collections.get(collectionName);
|
|
2155
|
+
const memory = await fetchMemoryWithAllProperties(col, memoryId);
|
|
2156
|
+
if (memory?.properties) {
|
|
2157
|
+
return {
|
|
2158
|
+
memory: normalizeDoc({ id: memory.uuid, ...memory.properties }),
|
|
2159
|
+
collectionName
|
|
2160
|
+
};
|
|
2160
2161
|
}
|
|
2161
2162
|
}
|
|
2162
2163
|
return { memory: null, collectionName: null };
|
|
@@ -2202,13 +2203,11 @@ var MemoryService = class {
|
|
|
2202
2203
|
};
|
|
2203
2204
|
const memoryId = await this.collection.data.insert({ properties });
|
|
2204
2205
|
this.logger.info("Memory created", { memoryId, userId: this.userId });
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
}
|
|
2210
|
-
this.logger.warn?.(`[MemoryService] Index write failed for ${memoryId}: ${err2}`);
|
|
2211
|
-
}
|
|
2206
|
+
try {
|
|
2207
|
+
const collectionName = this.collection.name;
|
|
2208
|
+
await this.options.memoryIndex.index(memoryId, collectionName);
|
|
2209
|
+
} catch (err2) {
|
|
2210
|
+
this.logger.warn?.(`[MemoryService] Index write failed for ${memoryId}: ${err2}`);
|
|
2212
2211
|
}
|
|
2213
2212
|
return { memory_id: memoryId, created_at: now };
|
|
2214
2213
|
}
|
|
@@ -3142,14 +3141,18 @@ var SpaceService = class {
|
|
|
3142
3141
|
userId;
|
|
3143
3142
|
confirmationTokenService;
|
|
3144
3143
|
logger;
|
|
3144
|
+
memoryIndexService;
|
|
3145
3145
|
moderationClient;
|
|
3146
|
-
|
|
3146
|
+
memoryIndex;
|
|
3147
|
+
constructor(weaviateClient, userCollection, userId, confirmationTokenService, logger2, memoryIndexService2, options) {
|
|
3147
3148
|
this.weaviateClient = weaviateClient;
|
|
3148
3149
|
this.userCollection = userCollection;
|
|
3149
3150
|
this.userId = userId;
|
|
3150
3151
|
this.confirmationTokenService = confirmationTokenService;
|
|
3151
3152
|
this.logger = logger2;
|
|
3153
|
+
this.memoryIndexService = memoryIndexService2;
|
|
3152
3154
|
this.moderationClient = options?.moderationClient;
|
|
3155
|
+
this.memoryIndex = memoryIndexService2;
|
|
3153
3156
|
}
|
|
3154
3157
|
// ── Content moderation helper ────────────────────────────────────────
|
|
3155
3158
|
async checkModeration(content) {
|
|
@@ -3555,6 +3558,11 @@ var SpaceService = class {
|
|
|
3555
3558
|
} else {
|
|
3556
3559
|
await publicCollection.data.insert({ id: weaviateId, properties: publishedMemory });
|
|
3557
3560
|
}
|
|
3561
|
+
try {
|
|
3562
|
+
await this.memoryIndex.index(weaviateId, "Memory_spaces_public");
|
|
3563
|
+
} catch (err2) {
|
|
3564
|
+
this.logger.warn?.(`[SpaceService] Index write failed for ${weaviateId}: ${err2}`);
|
|
3565
|
+
}
|
|
3558
3566
|
successfulPublications.push(`spaces: ${spaces.join(", ")}`);
|
|
3559
3567
|
} catch (err2) {
|
|
3560
3568
|
failedPublications.push(`spaces: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
@@ -3591,6 +3599,11 @@ var SpaceService = class {
|
|
|
3591
3599
|
} else {
|
|
3592
3600
|
await groupCollection.data.insert({ id: weaviateId, properties: groupMemory });
|
|
3593
3601
|
}
|
|
3602
|
+
try {
|
|
3603
|
+
await this.memoryIndex.index(weaviateId, groupCollectionName);
|
|
3604
|
+
} catch (err2) {
|
|
3605
|
+
this.logger.warn?.(`[SpaceService] Index write failed for ${weaviateId} in ${groupCollectionName}: ${err2}`);
|
|
3606
|
+
}
|
|
3594
3607
|
successfulPublications.push(`group: ${groupId}`);
|
|
3595
3608
|
} catch (err2) {
|
|
3596
3609
|
failedPublications.push(`group ${groupId}: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
@@ -3831,6 +3844,144 @@ var REM_STATE_COLLECTION = `${BASE}.rem_state`;
|
|
|
3831
3844
|
// node_modules/@prmichaelsen/remember-core/dist/services/rem.clustering.js
|
|
3832
3845
|
import { Filters as Filters5 } from "weaviate-client";
|
|
3833
3846
|
|
|
3847
|
+
// node_modules/@prmichaelsen/remember-core/dist/services/memory-index.service.js
|
|
3848
|
+
var MemoryIndexService = class {
|
|
3849
|
+
logger;
|
|
3850
|
+
collectionPath;
|
|
3851
|
+
constructor(logger2) {
|
|
3852
|
+
this.logger = logger2;
|
|
3853
|
+
this.collectionPath = getMemoryIndexPath();
|
|
3854
|
+
}
|
|
3855
|
+
/**
|
|
3856
|
+
* Index a memory UUID → collection name mapping.
|
|
3857
|
+
* Uses set() for idempotency (safe to re-index).
|
|
3858
|
+
*/
|
|
3859
|
+
async index(memoryUuid, collectionName) {
|
|
3860
|
+
const entry = {
|
|
3861
|
+
collection_name: collectionName,
|
|
3862
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3863
|
+
};
|
|
3864
|
+
await setDocument2(this.collectionPath, memoryUuid, entry);
|
|
3865
|
+
this.logger.debug?.(`[MemoryIndex] Indexed ${memoryUuid} \u2192 ${collectionName}`);
|
|
3866
|
+
}
|
|
3867
|
+
/**
|
|
3868
|
+
* Look up the collection name for a memory UUID.
|
|
3869
|
+
* Returns null if the memory is not indexed.
|
|
3870
|
+
*/
|
|
3871
|
+
async lookup(memoryUuid) {
|
|
3872
|
+
const doc = await getDocument2(this.collectionPath, memoryUuid);
|
|
3873
|
+
if (!doc)
|
|
3874
|
+
return null;
|
|
3875
|
+
const entry = doc;
|
|
3876
|
+
return entry.collection_name ?? null;
|
|
3877
|
+
}
|
|
3878
|
+
};
|
|
3879
|
+
|
|
3880
|
+
// node_modules/@prmichaelsen/remember-core/dist/services/moderation.service.js
|
|
3881
|
+
import { createHash as createHash2 } from "crypto";
|
|
3882
|
+
function buildModerationPrompt(content) {
|
|
3883
|
+
return `You are a content moderation classifier. Evaluate the following user-generated content and determine if it should be ALLOWED or BLOCKED from a public space.
|
|
3884
|
+
|
|
3885
|
+
You must be LENIENT. Most content should pass. Only block content that is clearly and explicitly in one of these categories:
|
|
3886
|
+
|
|
3887
|
+
BLOCK categories:
|
|
3888
|
+
|
|
3889
|
+
1. HATE SPEECH \u2014 Explicit dehumanization of ethnic/racial/religious groups, calls for violence against protected groups, racial slurs used to demean.
|
|
3890
|
+
Examples that FAIL:
|
|
3891
|
+
- "All [ethnic group] should be exterminated. They are subhuman parasites."
|
|
3892
|
+
- Explicit Nazi ideology or white supremacist recruitment
|
|
3893
|
+
- "[Racial slur] don't deserve rights"
|
|
3894
|
+
- Genocide celebration ("The Holocaust was a good thing")
|
|
3895
|
+
|
|
3896
|
+
2. VIOLENCE INCITEMENT \u2014 Direct threats against specific people or locations, incitement to commit specific acts of mass violence.
|
|
3897
|
+
Examples that FAIL:
|
|
3898
|
+
- "I'm going to find [person] and make them pay. They won't see it coming."
|
|
3899
|
+
- "Someone should shoot up [location]. That would teach them."
|
|
3900
|
+
Examples that PASS:
|
|
3901
|
+
- Instructions or information about weapons (legally protected speech without incitement)
|
|
3902
|
+
- Historical/philosophical discussion of violence
|
|
3903
|
+
- Fictional violence (games, stories, D&D campaigns)
|
|
3904
|
+
- Colloquial expressions ("I could kill for a burger")
|
|
3905
|
+
|
|
3906
|
+
3. CSAM \u2014 Any sexual content involving minors. Zero tolerance.
|
|
3907
|
+
|
|
3908
|
+
4. SELF-HARM ENCOURAGEMENT \u2014 Detailed instructions or active encouragement for self-harm or suicide.
|
|
3909
|
+
Examples that PASS:
|
|
3910
|
+
- Discussions about depression or mental health struggles
|
|
3911
|
+
- Jokes about suicide or dark humor about self-harm
|
|
3912
|
+
- Journaling about difficult feelings
|
|
3913
|
+
|
|
3914
|
+
ALWAYS ALLOW:
|
|
3915
|
+
- Harsh political opinions, criticism of governments or public figures
|
|
3916
|
+
- Edgy humor, dark comedy, offensive jokes (without targeted dehumanization)
|
|
3917
|
+
- Strong opinions about religion, ideology, or social issues
|
|
3918
|
+
- Profanity and vulgar language
|
|
3919
|
+
- Controversial or uncomfortable topics
|
|
3920
|
+
- Educational/historical content about atrocities
|
|
3921
|
+
- The French Revolution, violent revolution as philosophical concept
|
|
3922
|
+
|
|
3923
|
+
Content to evaluate:
|
|
3924
|
+
---
|
|
3925
|
+
${content}
|
|
3926
|
+
---
|
|
3927
|
+
|
|
3928
|
+
Respond with ONLY valid JSON:
|
|
3929
|
+
{"pass":true}
|
|
3930
|
+
OR
|
|
3931
|
+
{"pass":false,"reason":"<specific, human-friendly explanation of why this was blocked>","category":"<hate_speech|extremism|violence_incitement|csam|self_harm_encouragement>"}`;
|
|
3932
|
+
}
|
|
3933
|
+
var DEFAULT_CACHE_MAX = 1e3;
|
|
3934
|
+
function hashContent(content) {
|
|
3935
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
3936
|
+
}
|
|
3937
|
+
function createModerationClient(options) {
|
|
3938
|
+
const model = options.model ?? "claude-haiku-4-5-20251001";
|
|
3939
|
+
const cacheMax = options.cacheMax ?? DEFAULT_CACHE_MAX;
|
|
3940
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3941
|
+
return {
|
|
3942
|
+
async moderate(content) {
|
|
3943
|
+
const hash = hashContent(content);
|
|
3944
|
+
const cached = cache.get(hash);
|
|
3945
|
+
if (cached)
|
|
3946
|
+
return cached;
|
|
3947
|
+
try {
|
|
3948
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
3949
|
+
method: "POST",
|
|
3950
|
+
headers: {
|
|
3951
|
+
"Content-Type": "application/json",
|
|
3952
|
+
"x-api-key": options.apiKey,
|
|
3953
|
+
"anthropic-version": "2023-06-01"
|
|
3954
|
+
},
|
|
3955
|
+
body: JSON.stringify({
|
|
3956
|
+
model,
|
|
3957
|
+
max_tokens: 256,
|
|
3958
|
+
messages: [{ role: "user", content: buildModerationPrompt(content) }]
|
|
3959
|
+
})
|
|
3960
|
+
});
|
|
3961
|
+
if (!response.ok) {
|
|
3962
|
+
return { pass: false, reason: "Content moderation unavailable. Please try again later." };
|
|
3963
|
+
}
|
|
3964
|
+
const data = await response.json();
|
|
3965
|
+
const text = data.content?.[0]?.text ?? "";
|
|
3966
|
+
const parsed = JSON.parse(text);
|
|
3967
|
+
const result = {
|
|
3968
|
+
pass: parsed.pass === true,
|
|
3969
|
+
reason: parsed.reason ?? "",
|
|
3970
|
+
category: parsed.pass ? void 0 : parsed.category
|
|
3971
|
+
};
|
|
3972
|
+
if (cache.size >= cacheMax) {
|
|
3973
|
+
const oldest = cache.keys().next().value;
|
|
3974
|
+
cache.delete(oldest);
|
|
3975
|
+
}
|
|
3976
|
+
cache.set(hash, result);
|
|
3977
|
+
return result;
|
|
3978
|
+
} catch {
|
|
3979
|
+
return { pass: false, reason: "Content moderation unavailable. Please try again later." };
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
|
|
3834
3985
|
// src/weaviate/schema.ts
|
|
3835
3986
|
init_logger();
|
|
3836
3987
|
|
|
@@ -3947,6 +4098,8 @@ function getMemoryCollection(userId) {
|
|
|
3947
4098
|
var coreLogger = createLogger("info");
|
|
3948
4099
|
var tokenService = new ConfirmationTokenService(coreLogger);
|
|
3949
4100
|
var preferencesService = new PreferencesDatabaseService(coreLogger);
|
|
4101
|
+
var moderationClient = process.env.ANTHROPIC_API_KEY ? createModerationClient({ apiKey: process.env.ANTHROPIC_API_KEY }) : void 0;
|
|
4102
|
+
var memoryIndexService = new MemoryIndexService(coreLogger);
|
|
3950
4103
|
var coreServicesCache = /* @__PURE__ */ new Map();
|
|
3951
4104
|
function createCoreServices(userId) {
|
|
3952
4105
|
const cached = coreServicesCache.get(userId);
|
|
@@ -3955,9 +4108,12 @@ function createCoreServices(userId) {
|
|
|
3955
4108
|
const collection = getMemoryCollection(userId);
|
|
3956
4109
|
const weaviateClient = getWeaviateClient();
|
|
3957
4110
|
const services = {
|
|
3958
|
-
memory: new MemoryService(collection, userId, coreLogger
|
|
4111
|
+
memory: new MemoryService(collection, userId, coreLogger, {
|
|
4112
|
+
memoryIndex: memoryIndexService,
|
|
4113
|
+
weaviateClient
|
|
4114
|
+
}),
|
|
3959
4115
|
relationship: new RelationshipService(collection, userId, coreLogger),
|
|
3960
|
-
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger),
|
|
4116
|
+
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger, memoryIndexService, { moderationClient }),
|
|
3961
4117
|
preferences: preferencesService,
|
|
3962
4118
|
token: tokenService
|
|
3963
4119
|
};
|
package/dist/server.js
CHANGED
|
@@ -1758,6 +1758,9 @@ function getUserPreferencesPath(userId) {
|
|
|
1758
1758
|
function getCollectionRegistryPath() {
|
|
1759
1759
|
return `${BASE}.collection_registry`;
|
|
1760
1760
|
}
|
|
1761
|
+
function getMemoryIndexPath() {
|
|
1762
|
+
return `${BASE}.memory_index`;
|
|
1763
|
+
}
|
|
1761
1764
|
|
|
1762
1765
|
// node_modules/@prmichaelsen/remember-core/dist/services/preferences.service.js
|
|
1763
1766
|
var PreferencesDatabaseService = class {
|
|
@@ -2147,20 +2150,18 @@ var MemoryService = class {
|
|
|
2147
2150
|
* Requires `memoryIndex` and `weaviateClient` in constructor options.
|
|
2148
2151
|
*/
|
|
2149
2152
|
async resolveById(memoryId) {
|
|
2150
|
-
if (!this.options
|
|
2153
|
+
if (!this.options.weaviateClient) {
|
|
2151
2154
|
throw new Error("resolveById requires weaviateClient in options");
|
|
2152
2155
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
};
|
|
2163
|
-
}
|
|
2156
|
+
const collectionName = await this.options.memoryIndex.lookup(memoryId);
|
|
2157
|
+
if (collectionName) {
|
|
2158
|
+
const col = this.options.weaviateClient.collections.get(collectionName);
|
|
2159
|
+
const memory = await fetchMemoryWithAllProperties(col, memoryId);
|
|
2160
|
+
if (memory?.properties) {
|
|
2161
|
+
return {
|
|
2162
|
+
memory: normalizeDoc({ id: memory.uuid, ...memory.properties }),
|
|
2163
|
+
collectionName
|
|
2164
|
+
};
|
|
2164
2165
|
}
|
|
2165
2166
|
}
|
|
2166
2167
|
return { memory: null, collectionName: null };
|
|
@@ -2206,13 +2207,11 @@ var MemoryService = class {
|
|
|
2206
2207
|
};
|
|
2207
2208
|
const memoryId = await this.collection.data.insert({ properties });
|
|
2208
2209
|
this.logger.info("Memory created", { memoryId, userId: this.userId });
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
}
|
|
2214
|
-
this.logger.warn?.(`[MemoryService] Index write failed for ${memoryId}: ${err2}`);
|
|
2215
|
-
}
|
|
2210
|
+
try {
|
|
2211
|
+
const collectionName = this.collection.name;
|
|
2212
|
+
await this.options.memoryIndex.index(memoryId, collectionName);
|
|
2213
|
+
} catch (err2) {
|
|
2214
|
+
this.logger.warn?.(`[MemoryService] Index write failed for ${memoryId}: ${err2}`);
|
|
2216
2215
|
}
|
|
2217
2216
|
return { memory_id: memoryId, created_at: now };
|
|
2218
2217
|
}
|
|
@@ -3146,14 +3145,18 @@ var SpaceService = class {
|
|
|
3146
3145
|
userId;
|
|
3147
3146
|
confirmationTokenService;
|
|
3148
3147
|
logger;
|
|
3148
|
+
memoryIndexService;
|
|
3149
3149
|
moderationClient;
|
|
3150
|
-
|
|
3150
|
+
memoryIndex;
|
|
3151
|
+
constructor(weaviateClient, userCollection, userId, confirmationTokenService, logger2, memoryIndexService2, options) {
|
|
3151
3152
|
this.weaviateClient = weaviateClient;
|
|
3152
3153
|
this.userCollection = userCollection;
|
|
3153
3154
|
this.userId = userId;
|
|
3154
3155
|
this.confirmationTokenService = confirmationTokenService;
|
|
3155
3156
|
this.logger = logger2;
|
|
3157
|
+
this.memoryIndexService = memoryIndexService2;
|
|
3156
3158
|
this.moderationClient = options?.moderationClient;
|
|
3159
|
+
this.memoryIndex = memoryIndexService2;
|
|
3157
3160
|
}
|
|
3158
3161
|
// ── Content moderation helper ────────────────────────────────────────
|
|
3159
3162
|
async checkModeration(content) {
|
|
@@ -3559,6 +3562,11 @@ var SpaceService = class {
|
|
|
3559
3562
|
} else {
|
|
3560
3563
|
await publicCollection.data.insert({ id: weaviateId, properties: publishedMemory });
|
|
3561
3564
|
}
|
|
3565
|
+
try {
|
|
3566
|
+
await this.memoryIndex.index(weaviateId, "Memory_spaces_public");
|
|
3567
|
+
} catch (err2) {
|
|
3568
|
+
this.logger.warn?.(`[SpaceService] Index write failed for ${weaviateId}: ${err2}`);
|
|
3569
|
+
}
|
|
3562
3570
|
successfulPublications.push(`spaces: ${spaces.join(", ")}`);
|
|
3563
3571
|
} catch (err2) {
|
|
3564
3572
|
failedPublications.push(`spaces: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
@@ -3595,6 +3603,11 @@ var SpaceService = class {
|
|
|
3595
3603
|
} else {
|
|
3596
3604
|
await groupCollection.data.insert({ id: weaviateId, properties: groupMemory });
|
|
3597
3605
|
}
|
|
3606
|
+
try {
|
|
3607
|
+
await this.memoryIndex.index(weaviateId, groupCollectionName);
|
|
3608
|
+
} catch (err2) {
|
|
3609
|
+
this.logger.warn?.(`[SpaceService] Index write failed for ${weaviateId} in ${groupCollectionName}: ${err2}`);
|
|
3610
|
+
}
|
|
3598
3611
|
successfulPublications.push(`group: ${groupId}`);
|
|
3599
3612
|
} catch (err2) {
|
|
3600
3613
|
failedPublications.push(`group ${groupId}: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
@@ -3835,6 +3848,144 @@ var REM_STATE_COLLECTION = `${BASE}.rem_state`;
|
|
|
3835
3848
|
// node_modules/@prmichaelsen/remember-core/dist/services/rem.clustering.js
|
|
3836
3849
|
import { Filters as Filters5 } from "weaviate-client";
|
|
3837
3850
|
|
|
3851
|
+
// node_modules/@prmichaelsen/remember-core/dist/services/memory-index.service.js
|
|
3852
|
+
var MemoryIndexService = class {
|
|
3853
|
+
logger;
|
|
3854
|
+
collectionPath;
|
|
3855
|
+
constructor(logger2) {
|
|
3856
|
+
this.logger = logger2;
|
|
3857
|
+
this.collectionPath = getMemoryIndexPath();
|
|
3858
|
+
}
|
|
3859
|
+
/**
|
|
3860
|
+
* Index a memory UUID → collection name mapping.
|
|
3861
|
+
* Uses set() for idempotency (safe to re-index).
|
|
3862
|
+
*/
|
|
3863
|
+
async index(memoryUuid, collectionName) {
|
|
3864
|
+
const entry = {
|
|
3865
|
+
collection_name: collectionName,
|
|
3866
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3867
|
+
};
|
|
3868
|
+
await setDocument2(this.collectionPath, memoryUuid, entry);
|
|
3869
|
+
this.logger.debug?.(`[MemoryIndex] Indexed ${memoryUuid} \u2192 ${collectionName}`);
|
|
3870
|
+
}
|
|
3871
|
+
/**
|
|
3872
|
+
* Look up the collection name for a memory UUID.
|
|
3873
|
+
* Returns null if the memory is not indexed.
|
|
3874
|
+
*/
|
|
3875
|
+
async lookup(memoryUuid) {
|
|
3876
|
+
const doc = await getDocument2(this.collectionPath, memoryUuid);
|
|
3877
|
+
if (!doc)
|
|
3878
|
+
return null;
|
|
3879
|
+
const entry = doc;
|
|
3880
|
+
return entry.collection_name ?? null;
|
|
3881
|
+
}
|
|
3882
|
+
};
|
|
3883
|
+
|
|
3884
|
+
// node_modules/@prmichaelsen/remember-core/dist/services/moderation.service.js
|
|
3885
|
+
import { createHash as createHash2 } from "crypto";
|
|
3886
|
+
function buildModerationPrompt(content) {
|
|
3887
|
+
return `You are a content moderation classifier. Evaluate the following user-generated content and determine if it should be ALLOWED or BLOCKED from a public space.
|
|
3888
|
+
|
|
3889
|
+
You must be LENIENT. Most content should pass. Only block content that is clearly and explicitly in one of these categories:
|
|
3890
|
+
|
|
3891
|
+
BLOCK categories:
|
|
3892
|
+
|
|
3893
|
+
1. HATE SPEECH \u2014 Explicit dehumanization of ethnic/racial/religious groups, calls for violence against protected groups, racial slurs used to demean.
|
|
3894
|
+
Examples that FAIL:
|
|
3895
|
+
- "All [ethnic group] should be exterminated. They are subhuman parasites."
|
|
3896
|
+
- Explicit Nazi ideology or white supremacist recruitment
|
|
3897
|
+
- "[Racial slur] don't deserve rights"
|
|
3898
|
+
- Genocide celebration ("The Holocaust was a good thing")
|
|
3899
|
+
|
|
3900
|
+
2. VIOLENCE INCITEMENT \u2014 Direct threats against specific people or locations, incitement to commit specific acts of mass violence.
|
|
3901
|
+
Examples that FAIL:
|
|
3902
|
+
- "I'm going to find [person] and make them pay. They won't see it coming."
|
|
3903
|
+
- "Someone should shoot up [location]. That would teach them."
|
|
3904
|
+
Examples that PASS:
|
|
3905
|
+
- Instructions or information about weapons (legally protected speech without incitement)
|
|
3906
|
+
- Historical/philosophical discussion of violence
|
|
3907
|
+
- Fictional violence (games, stories, D&D campaigns)
|
|
3908
|
+
- Colloquial expressions ("I could kill for a burger")
|
|
3909
|
+
|
|
3910
|
+
3. CSAM \u2014 Any sexual content involving minors. Zero tolerance.
|
|
3911
|
+
|
|
3912
|
+
4. SELF-HARM ENCOURAGEMENT \u2014 Detailed instructions or active encouragement for self-harm or suicide.
|
|
3913
|
+
Examples that PASS:
|
|
3914
|
+
- Discussions about depression or mental health struggles
|
|
3915
|
+
- Jokes about suicide or dark humor about self-harm
|
|
3916
|
+
- Journaling about difficult feelings
|
|
3917
|
+
|
|
3918
|
+
ALWAYS ALLOW:
|
|
3919
|
+
- Harsh political opinions, criticism of governments or public figures
|
|
3920
|
+
- Edgy humor, dark comedy, offensive jokes (without targeted dehumanization)
|
|
3921
|
+
- Strong opinions about religion, ideology, or social issues
|
|
3922
|
+
- Profanity and vulgar language
|
|
3923
|
+
- Controversial or uncomfortable topics
|
|
3924
|
+
- Educational/historical content about atrocities
|
|
3925
|
+
- The French Revolution, violent revolution as philosophical concept
|
|
3926
|
+
|
|
3927
|
+
Content to evaluate:
|
|
3928
|
+
---
|
|
3929
|
+
${content}
|
|
3930
|
+
---
|
|
3931
|
+
|
|
3932
|
+
Respond with ONLY valid JSON:
|
|
3933
|
+
{"pass":true}
|
|
3934
|
+
OR
|
|
3935
|
+
{"pass":false,"reason":"<specific, human-friendly explanation of why this was blocked>","category":"<hate_speech|extremism|violence_incitement|csam|self_harm_encouragement>"}`;
|
|
3936
|
+
}
|
|
3937
|
+
var DEFAULT_CACHE_MAX = 1e3;
|
|
3938
|
+
function hashContent(content) {
|
|
3939
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
3940
|
+
}
|
|
3941
|
+
function createModerationClient(options) {
|
|
3942
|
+
const model = options.model ?? "claude-haiku-4-5-20251001";
|
|
3943
|
+
const cacheMax = options.cacheMax ?? DEFAULT_CACHE_MAX;
|
|
3944
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3945
|
+
return {
|
|
3946
|
+
async moderate(content) {
|
|
3947
|
+
const hash = hashContent(content);
|
|
3948
|
+
const cached = cache.get(hash);
|
|
3949
|
+
if (cached)
|
|
3950
|
+
return cached;
|
|
3951
|
+
try {
|
|
3952
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
3953
|
+
method: "POST",
|
|
3954
|
+
headers: {
|
|
3955
|
+
"Content-Type": "application/json",
|
|
3956
|
+
"x-api-key": options.apiKey,
|
|
3957
|
+
"anthropic-version": "2023-06-01"
|
|
3958
|
+
},
|
|
3959
|
+
body: JSON.stringify({
|
|
3960
|
+
model,
|
|
3961
|
+
max_tokens: 256,
|
|
3962
|
+
messages: [{ role: "user", content: buildModerationPrompt(content) }]
|
|
3963
|
+
})
|
|
3964
|
+
});
|
|
3965
|
+
if (!response.ok) {
|
|
3966
|
+
return { pass: false, reason: "Content moderation unavailable. Please try again later." };
|
|
3967
|
+
}
|
|
3968
|
+
const data = await response.json();
|
|
3969
|
+
const text = data.content?.[0]?.text ?? "";
|
|
3970
|
+
const parsed = JSON.parse(text);
|
|
3971
|
+
const result = {
|
|
3972
|
+
pass: parsed.pass === true,
|
|
3973
|
+
reason: parsed.reason ?? "",
|
|
3974
|
+
category: parsed.pass ? void 0 : parsed.category
|
|
3975
|
+
};
|
|
3976
|
+
if (cache.size >= cacheMax) {
|
|
3977
|
+
const oldest = cache.keys().next().value;
|
|
3978
|
+
cache.delete(oldest);
|
|
3979
|
+
}
|
|
3980
|
+
cache.set(hash, result);
|
|
3981
|
+
return result;
|
|
3982
|
+
} catch {
|
|
3983
|
+
return { pass: false, reason: "Content moderation unavailable. Please try again later." };
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
};
|
|
3987
|
+
}
|
|
3988
|
+
|
|
3838
3989
|
// src/weaviate/schema.ts
|
|
3839
3990
|
init_logger();
|
|
3840
3991
|
|
|
@@ -3951,6 +4102,8 @@ function getMemoryCollection(userId) {
|
|
|
3951
4102
|
var coreLogger = createLogger("info");
|
|
3952
4103
|
var tokenService = new ConfirmationTokenService(coreLogger);
|
|
3953
4104
|
var preferencesService = new PreferencesDatabaseService(coreLogger);
|
|
4105
|
+
var moderationClient = process.env.ANTHROPIC_API_KEY ? createModerationClient({ apiKey: process.env.ANTHROPIC_API_KEY }) : void 0;
|
|
4106
|
+
var memoryIndexService = new MemoryIndexService(coreLogger);
|
|
3954
4107
|
var coreServicesCache = /* @__PURE__ */ new Map();
|
|
3955
4108
|
function createCoreServices(userId) {
|
|
3956
4109
|
const cached = coreServicesCache.get(userId);
|
|
@@ -3959,9 +4112,12 @@ function createCoreServices(userId) {
|
|
|
3959
4112
|
const collection = getMemoryCollection(userId);
|
|
3960
4113
|
const weaviateClient = getWeaviateClient();
|
|
3961
4114
|
const services = {
|
|
3962
|
-
memory: new MemoryService(collection, userId, coreLogger
|
|
4115
|
+
memory: new MemoryService(collection, userId, coreLogger, {
|
|
4116
|
+
memoryIndex: memoryIndexService,
|
|
4117
|
+
weaviateClient
|
|
4118
|
+
}),
|
|
3963
4119
|
relationship: new RelationshipService(collection, userId, coreLogger),
|
|
3964
|
-
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger),
|
|
4120
|
+
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger, memoryIndexService, { moderationClient }),
|
|
3965
4121
|
preferences: preferencesService,
|
|
3966
4122
|
token: tokenService
|
|
3967
4123
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prmichaelsen/remember-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"description": "Multi-tenant memory system MCP server with vector search and relationships",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"type": "module",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
51
51
|
"@prmichaelsen/firebase-admin-sdk-v8": "^2.2.0",
|
|
52
52
|
"@prmichaelsen/mcp-auth": "^7.0.4",
|
|
53
|
-
"@prmichaelsen/remember-core": "^0.
|
|
53
|
+
"@prmichaelsen/remember-core": "^0.33.1",
|
|
54
54
|
"dotenv": "^16.4.5",
|
|
55
55
|
"uuid": "^13.0.0",
|
|
56
56
|
"weaviate-client": "^3.2.0"
|
package/src/core-services.ts
CHANGED
|
@@ -7,13 +7,15 @@
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
MemoryService,
|
|
10
|
+
MemoryIndexService,
|
|
10
11
|
RelationshipService,
|
|
11
12
|
SpaceService,
|
|
12
13
|
PreferencesDatabaseService,
|
|
13
14
|
ConfirmationTokenService,
|
|
14
15
|
createLogger,
|
|
16
|
+
createModerationClient,
|
|
15
17
|
} from '@prmichaelsen/remember-core';
|
|
16
|
-
import type { Logger } from '@prmichaelsen/remember-core';
|
|
18
|
+
import type { Logger, ModerationClient } from '@prmichaelsen/remember-core';
|
|
17
19
|
import { getWeaviateClient } from './weaviate/client.js';
|
|
18
20
|
import { getMemoryCollection } from './weaviate/schema.js';
|
|
19
21
|
|
|
@@ -29,6 +31,10 @@ export interface CoreServices {
|
|
|
29
31
|
const coreLogger: Logger = createLogger('info');
|
|
30
32
|
const tokenService = new ConfirmationTokenService(coreLogger);
|
|
31
33
|
const preferencesService = new PreferencesDatabaseService(coreLogger);
|
|
34
|
+
const moderationClient: ModerationClient | undefined = process.env.ANTHROPIC_API_KEY
|
|
35
|
+
? createModerationClient({ apiKey: process.env.ANTHROPIC_API_KEY })
|
|
36
|
+
: undefined;
|
|
37
|
+
const memoryIndexService = new MemoryIndexService(coreLogger);
|
|
32
38
|
|
|
33
39
|
/** Cached CoreServices per userId — avoids re-instantiation on every tool call */
|
|
34
40
|
const coreServicesCache = new Map<string, CoreServices>();
|
|
@@ -45,9 +51,12 @@ export function createCoreServices(userId: string): CoreServices {
|
|
|
45
51
|
const weaviateClient = getWeaviateClient();
|
|
46
52
|
|
|
47
53
|
const services: CoreServices = {
|
|
48
|
-
memory: new MemoryService(collection, userId, coreLogger
|
|
54
|
+
memory: new MemoryService(collection, userId, coreLogger, {
|
|
55
|
+
memoryIndex: memoryIndexService,
|
|
56
|
+
weaviateClient,
|
|
57
|
+
}),
|
|
49
58
|
relationship: new RelationshipService(collection, userId, coreLogger),
|
|
50
|
-
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger),
|
|
59
|
+
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger, memoryIndexService, { moderationClient }),
|
|
51
60
|
preferences: preferencesService,
|
|
52
61
|
token: tokenService,
|
|
53
62
|
};
|