@prmichaelsen/remember-mcp 3.14.20 → 3.14.21
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
CHANGED
|
@@ -5,6 +5,14 @@ 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.14.21] - 2026-03-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Wire LLM auto-moderation into SpaceService publish/revise flow (task-202)
|
|
13
|
+
- Create singleton `ModerationClient` from `ANTHROPIC_API_KEY` env var when present
|
|
14
|
+
- Content published to spaces is now screened by Claude Haiku before storage
|
|
15
|
+
|
|
8
16
|
## [3.14.14] - 2026-03-04
|
|
9
17
|
|
|
10
18
|
### 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`.
|
package/dist/server-factory.js
CHANGED
|
@@ -3831,6 +3831,111 @@ var REM_STATE_COLLECTION = `${BASE}.rem_state`;
|
|
|
3831
3831
|
// node_modules/@prmichaelsen/remember-core/dist/services/rem.clustering.js
|
|
3832
3832
|
import { Filters as Filters5 } from "weaviate-client";
|
|
3833
3833
|
|
|
3834
|
+
// node_modules/@prmichaelsen/remember-core/dist/services/moderation.service.js
|
|
3835
|
+
import { createHash as createHash2 } from "crypto";
|
|
3836
|
+
function buildModerationPrompt(content) {
|
|
3837
|
+
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.
|
|
3838
|
+
|
|
3839
|
+
You must be LENIENT. Most content should pass. Only block content that is clearly and explicitly in one of these categories:
|
|
3840
|
+
|
|
3841
|
+
BLOCK categories:
|
|
3842
|
+
|
|
3843
|
+
1. HATE SPEECH \u2014 Explicit dehumanization of ethnic/racial/religious groups, calls for violence against protected groups, racial slurs used to demean.
|
|
3844
|
+
Examples that FAIL:
|
|
3845
|
+
- "All [ethnic group] should be exterminated. They are subhuman parasites."
|
|
3846
|
+
- Explicit Nazi ideology or white supremacist recruitment
|
|
3847
|
+
- "[Racial slur] don't deserve rights"
|
|
3848
|
+
- Genocide celebration ("The Holocaust was a good thing")
|
|
3849
|
+
|
|
3850
|
+
2. VIOLENCE INCITEMENT \u2014 Direct threats against specific people or locations, incitement to commit specific acts of mass violence.
|
|
3851
|
+
Examples that FAIL:
|
|
3852
|
+
- "I'm going to find [person] and make them pay. They won't see it coming."
|
|
3853
|
+
- "Someone should shoot up [location]. That would teach them."
|
|
3854
|
+
Examples that PASS:
|
|
3855
|
+
- Instructions or information about weapons (legally protected speech without incitement)
|
|
3856
|
+
- Historical/philosophical discussion of violence
|
|
3857
|
+
- Fictional violence (games, stories, D&D campaigns)
|
|
3858
|
+
- Colloquial expressions ("I could kill for a burger")
|
|
3859
|
+
|
|
3860
|
+
3. CSAM \u2014 Any sexual content involving minors. Zero tolerance.
|
|
3861
|
+
|
|
3862
|
+
4. SELF-HARM ENCOURAGEMENT \u2014 Detailed instructions or active encouragement for self-harm or suicide.
|
|
3863
|
+
Examples that PASS:
|
|
3864
|
+
- Discussions about depression or mental health struggles
|
|
3865
|
+
- Jokes about suicide or dark humor about self-harm
|
|
3866
|
+
- Journaling about difficult feelings
|
|
3867
|
+
|
|
3868
|
+
ALWAYS ALLOW:
|
|
3869
|
+
- Harsh political opinions, criticism of governments or public figures
|
|
3870
|
+
- Edgy humor, dark comedy, offensive jokes (without targeted dehumanization)
|
|
3871
|
+
- Strong opinions about religion, ideology, or social issues
|
|
3872
|
+
- Profanity and vulgar language
|
|
3873
|
+
- Controversial or uncomfortable topics
|
|
3874
|
+
- Educational/historical content about atrocities
|
|
3875
|
+
- The French Revolution, violent revolution as philosophical concept
|
|
3876
|
+
|
|
3877
|
+
Content to evaluate:
|
|
3878
|
+
---
|
|
3879
|
+
${content}
|
|
3880
|
+
---
|
|
3881
|
+
|
|
3882
|
+
Respond with ONLY valid JSON:
|
|
3883
|
+
{"pass":true}
|
|
3884
|
+
OR
|
|
3885
|
+
{"pass":false,"reason":"<specific, human-friendly explanation of why this was blocked>","category":"<hate_speech|extremism|violence_incitement|csam|self_harm_encouragement>"}`;
|
|
3886
|
+
}
|
|
3887
|
+
var DEFAULT_CACHE_MAX = 1e3;
|
|
3888
|
+
function hashContent(content) {
|
|
3889
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
3890
|
+
}
|
|
3891
|
+
function createModerationClient(options) {
|
|
3892
|
+
const model = options.model ?? "claude-haiku-4-5-20251001";
|
|
3893
|
+
const cacheMax = options.cacheMax ?? DEFAULT_CACHE_MAX;
|
|
3894
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3895
|
+
return {
|
|
3896
|
+
async moderate(content) {
|
|
3897
|
+
const hash = hashContent(content);
|
|
3898
|
+
const cached = cache.get(hash);
|
|
3899
|
+
if (cached)
|
|
3900
|
+
return cached;
|
|
3901
|
+
try {
|
|
3902
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
3903
|
+
method: "POST",
|
|
3904
|
+
headers: {
|
|
3905
|
+
"Content-Type": "application/json",
|
|
3906
|
+
"x-api-key": options.apiKey,
|
|
3907
|
+
"anthropic-version": "2023-06-01"
|
|
3908
|
+
},
|
|
3909
|
+
body: JSON.stringify({
|
|
3910
|
+
model,
|
|
3911
|
+
max_tokens: 256,
|
|
3912
|
+
messages: [{ role: "user", content: buildModerationPrompt(content) }]
|
|
3913
|
+
})
|
|
3914
|
+
});
|
|
3915
|
+
if (!response.ok) {
|
|
3916
|
+
return { pass: false, reason: "Content moderation unavailable. Please try again later." };
|
|
3917
|
+
}
|
|
3918
|
+
const data = await response.json();
|
|
3919
|
+
const text = data.content?.[0]?.text ?? "";
|
|
3920
|
+
const parsed = JSON.parse(text);
|
|
3921
|
+
const result = {
|
|
3922
|
+
pass: parsed.pass === true,
|
|
3923
|
+
reason: parsed.reason ?? "",
|
|
3924
|
+
category: parsed.pass ? void 0 : parsed.category
|
|
3925
|
+
};
|
|
3926
|
+
if (cache.size >= cacheMax) {
|
|
3927
|
+
const oldest = cache.keys().next().value;
|
|
3928
|
+
cache.delete(oldest);
|
|
3929
|
+
}
|
|
3930
|
+
cache.set(hash, result);
|
|
3931
|
+
return result;
|
|
3932
|
+
} catch {
|
|
3933
|
+
return { pass: false, reason: "Content moderation unavailable. Please try again later." };
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
};
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3834
3939
|
// src/weaviate/schema.ts
|
|
3835
3940
|
init_logger();
|
|
3836
3941
|
|
|
@@ -3947,6 +4052,7 @@ function getMemoryCollection(userId) {
|
|
|
3947
4052
|
var coreLogger = createLogger("info");
|
|
3948
4053
|
var tokenService = new ConfirmationTokenService(coreLogger);
|
|
3949
4054
|
var preferencesService = new PreferencesDatabaseService(coreLogger);
|
|
4055
|
+
var moderationClient = process.env.ANTHROPIC_API_KEY ? createModerationClient({ apiKey: process.env.ANTHROPIC_API_KEY }) : void 0;
|
|
3950
4056
|
var coreServicesCache = /* @__PURE__ */ new Map();
|
|
3951
4057
|
function createCoreServices(userId) {
|
|
3952
4058
|
const cached = coreServicesCache.get(userId);
|
|
@@ -3957,7 +4063,7 @@ function createCoreServices(userId) {
|
|
|
3957
4063
|
const services = {
|
|
3958
4064
|
memory: new MemoryService(collection, userId, coreLogger),
|
|
3959
4065
|
relationship: new RelationshipService(collection, userId, coreLogger),
|
|
3960
|
-
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger),
|
|
4066
|
+
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger, { moderationClient }),
|
|
3961
4067
|
preferences: preferencesService,
|
|
3962
4068
|
token: tokenService
|
|
3963
4069
|
};
|
package/dist/server.js
CHANGED
|
@@ -3835,6 +3835,111 @@ var REM_STATE_COLLECTION = `${BASE}.rem_state`;
|
|
|
3835
3835
|
// node_modules/@prmichaelsen/remember-core/dist/services/rem.clustering.js
|
|
3836
3836
|
import { Filters as Filters5 } from "weaviate-client";
|
|
3837
3837
|
|
|
3838
|
+
// node_modules/@prmichaelsen/remember-core/dist/services/moderation.service.js
|
|
3839
|
+
import { createHash as createHash2 } from "crypto";
|
|
3840
|
+
function buildModerationPrompt(content) {
|
|
3841
|
+
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.
|
|
3842
|
+
|
|
3843
|
+
You must be LENIENT. Most content should pass. Only block content that is clearly and explicitly in one of these categories:
|
|
3844
|
+
|
|
3845
|
+
BLOCK categories:
|
|
3846
|
+
|
|
3847
|
+
1. HATE SPEECH \u2014 Explicit dehumanization of ethnic/racial/religious groups, calls for violence against protected groups, racial slurs used to demean.
|
|
3848
|
+
Examples that FAIL:
|
|
3849
|
+
- "All [ethnic group] should be exterminated. They are subhuman parasites."
|
|
3850
|
+
- Explicit Nazi ideology or white supremacist recruitment
|
|
3851
|
+
- "[Racial slur] don't deserve rights"
|
|
3852
|
+
- Genocide celebration ("The Holocaust was a good thing")
|
|
3853
|
+
|
|
3854
|
+
2. VIOLENCE INCITEMENT \u2014 Direct threats against specific people or locations, incitement to commit specific acts of mass violence.
|
|
3855
|
+
Examples that FAIL:
|
|
3856
|
+
- "I'm going to find [person] and make them pay. They won't see it coming."
|
|
3857
|
+
- "Someone should shoot up [location]. That would teach them."
|
|
3858
|
+
Examples that PASS:
|
|
3859
|
+
- Instructions or information about weapons (legally protected speech without incitement)
|
|
3860
|
+
- Historical/philosophical discussion of violence
|
|
3861
|
+
- Fictional violence (games, stories, D&D campaigns)
|
|
3862
|
+
- Colloquial expressions ("I could kill for a burger")
|
|
3863
|
+
|
|
3864
|
+
3. CSAM \u2014 Any sexual content involving minors. Zero tolerance.
|
|
3865
|
+
|
|
3866
|
+
4. SELF-HARM ENCOURAGEMENT \u2014 Detailed instructions or active encouragement for self-harm or suicide.
|
|
3867
|
+
Examples that PASS:
|
|
3868
|
+
- Discussions about depression or mental health struggles
|
|
3869
|
+
- Jokes about suicide or dark humor about self-harm
|
|
3870
|
+
- Journaling about difficult feelings
|
|
3871
|
+
|
|
3872
|
+
ALWAYS ALLOW:
|
|
3873
|
+
- Harsh political opinions, criticism of governments or public figures
|
|
3874
|
+
- Edgy humor, dark comedy, offensive jokes (without targeted dehumanization)
|
|
3875
|
+
- Strong opinions about religion, ideology, or social issues
|
|
3876
|
+
- Profanity and vulgar language
|
|
3877
|
+
- Controversial or uncomfortable topics
|
|
3878
|
+
- Educational/historical content about atrocities
|
|
3879
|
+
- The French Revolution, violent revolution as philosophical concept
|
|
3880
|
+
|
|
3881
|
+
Content to evaluate:
|
|
3882
|
+
---
|
|
3883
|
+
${content}
|
|
3884
|
+
---
|
|
3885
|
+
|
|
3886
|
+
Respond with ONLY valid JSON:
|
|
3887
|
+
{"pass":true}
|
|
3888
|
+
OR
|
|
3889
|
+
{"pass":false,"reason":"<specific, human-friendly explanation of why this was blocked>","category":"<hate_speech|extremism|violence_incitement|csam|self_harm_encouragement>"}`;
|
|
3890
|
+
}
|
|
3891
|
+
var DEFAULT_CACHE_MAX = 1e3;
|
|
3892
|
+
function hashContent(content) {
|
|
3893
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
3894
|
+
}
|
|
3895
|
+
function createModerationClient(options) {
|
|
3896
|
+
const model = options.model ?? "claude-haiku-4-5-20251001";
|
|
3897
|
+
const cacheMax = options.cacheMax ?? DEFAULT_CACHE_MAX;
|
|
3898
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3899
|
+
return {
|
|
3900
|
+
async moderate(content) {
|
|
3901
|
+
const hash = hashContent(content);
|
|
3902
|
+
const cached = cache.get(hash);
|
|
3903
|
+
if (cached)
|
|
3904
|
+
return cached;
|
|
3905
|
+
try {
|
|
3906
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
3907
|
+
method: "POST",
|
|
3908
|
+
headers: {
|
|
3909
|
+
"Content-Type": "application/json",
|
|
3910
|
+
"x-api-key": options.apiKey,
|
|
3911
|
+
"anthropic-version": "2023-06-01"
|
|
3912
|
+
},
|
|
3913
|
+
body: JSON.stringify({
|
|
3914
|
+
model,
|
|
3915
|
+
max_tokens: 256,
|
|
3916
|
+
messages: [{ role: "user", content: buildModerationPrompt(content) }]
|
|
3917
|
+
})
|
|
3918
|
+
});
|
|
3919
|
+
if (!response.ok) {
|
|
3920
|
+
return { pass: false, reason: "Content moderation unavailable. Please try again later." };
|
|
3921
|
+
}
|
|
3922
|
+
const data = await response.json();
|
|
3923
|
+
const text = data.content?.[0]?.text ?? "";
|
|
3924
|
+
const parsed = JSON.parse(text);
|
|
3925
|
+
const result = {
|
|
3926
|
+
pass: parsed.pass === true,
|
|
3927
|
+
reason: parsed.reason ?? "",
|
|
3928
|
+
category: parsed.pass ? void 0 : parsed.category
|
|
3929
|
+
};
|
|
3930
|
+
if (cache.size >= cacheMax) {
|
|
3931
|
+
const oldest = cache.keys().next().value;
|
|
3932
|
+
cache.delete(oldest);
|
|
3933
|
+
}
|
|
3934
|
+
cache.set(hash, result);
|
|
3935
|
+
return result;
|
|
3936
|
+
} catch {
|
|
3937
|
+
return { pass: false, reason: "Content moderation unavailable. Please try again later." };
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
};
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3838
3943
|
// src/weaviate/schema.ts
|
|
3839
3944
|
init_logger();
|
|
3840
3945
|
|
|
@@ -3951,6 +4056,7 @@ function getMemoryCollection(userId) {
|
|
|
3951
4056
|
var coreLogger = createLogger("info");
|
|
3952
4057
|
var tokenService = new ConfirmationTokenService(coreLogger);
|
|
3953
4058
|
var preferencesService = new PreferencesDatabaseService(coreLogger);
|
|
4059
|
+
var moderationClient = process.env.ANTHROPIC_API_KEY ? createModerationClient({ apiKey: process.env.ANTHROPIC_API_KEY }) : void 0;
|
|
3954
4060
|
var coreServicesCache = /* @__PURE__ */ new Map();
|
|
3955
4061
|
function createCoreServices(userId) {
|
|
3956
4062
|
const cached = coreServicesCache.get(userId);
|
|
@@ -3961,7 +4067,7 @@ function createCoreServices(userId) {
|
|
|
3961
4067
|
const services = {
|
|
3962
4068
|
memory: new MemoryService(collection, userId, coreLogger),
|
|
3963
4069
|
relationship: new RelationshipService(collection, userId, coreLogger),
|
|
3964
|
-
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger),
|
|
4070
|
+
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger, { moderationClient }),
|
|
3965
4071
|
preferences: preferencesService,
|
|
3966
4072
|
token: tokenService
|
|
3967
4073
|
};
|
package/package.json
CHANGED
package/src/core-services.ts
CHANGED
|
@@ -12,8 +12,9 @@ import {
|
|
|
12
12
|
PreferencesDatabaseService,
|
|
13
13
|
ConfirmationTokenService,
|
|
14
14
|
createLogger,
|
|
15
|
+
createModerationClient,
|
|
15
16
|
} from '@prmichaelsen/remember-core';
|
|
16
|
-
import type { Logger } from '@prmichaelsen/remember-core';
|
|
17
|
+
import type { Logger, ModerationClient } from '@prmichaelsen/remember-core';
|
|
17
18
|
import { getWeaviateClient } from './weaviate/client.js';
|
|
18
19
|
import { getMemoryCollection } from './weaviate/schema.js';
|
|
19
20
|
|
|
@@ -29,6 +30,9 @@ export interface CoreServices {
|
|
|
29
30
|
const coreLogger: Logger = createLogger('info');
|
|
30
31
|
const tokenService = new ConfirmationTokenService(coreLogger);
|
|
31
32
|
const preferencesService = new PreferencesDatabaseService(coreLogger);
|
|
33
|
+
const moderationClient: ModerationClient | undefined = process.env.ANTHROPIC_API_KEY
|
|
34
|
+
? createModerationClient({ apiKey: process.env.ANTHROPIC_API_KEY })
|
|
35
|
+
: undefined;
|
|
32
36
|
|
|
33
37
|
/** Cached CoreServices per userId — avoids re-instantiation on every tool call */
|
|
34
38
|
const coreServicesCache = new Map<string, CoreServices>();
|
|
@@ -47,7 +51,7 @@ export function createCoreServices(userId: string): CoreServices {
|
|
|
47
51
|
const services: CoreServices = {
|
|
48
52
|
memory: new MemoryService(collection, userId, coreLogger),
|
|
49
53
|
relationship: new RelationshipService(collection, userId, coreLogger),
|
|
50
|
-
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger),
|
|
54
|
+
space: new SpaceService(weaviateClient, collection, userId, tokenService, coreLogger, { moderationClient }),
|
|
51
55
|
preferences: preferencesService,
|
|
52
56
|
token: tokenService,
|
|
53
57
|
};
|