@prmichaelsen/remember-mcp 3.14.18 → 3.14.20
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.
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: Bump remember-core
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
repository_dispatch:
|
|
5
|
+
types: [dependency-update]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
bump:
|
|
9
|
+
if: github.event.client_payload.package == '@prmichaelsen/remember-core'
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
with:
|
|
14
|
+
ref: mainline
|
|
15
|
+
|
|
16
|
+
- uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: 20
|
|
19
|
+
registry-url: https://registry.npmjs.org
|
|
20
|
+
|
|
21
|
+
- name: Bump remember-core
|
|
22
|
+
run: npm install @prmichaelsen/remember-core@${{ github.event.client_payload.version }} --save
|
|
23
|
+
|
|
24
|
+
- name: Bump package version
|
|
25
|
+
id: bump
|
|
26
|
+
run: |
|
|
27
|
+
CURRENT=$(node -p "require('./package.json').version")
|
|
28
|
+
NEW=$(echo "$CURRENT" | awk -F. '{print $1"."$2"."$3+1}')
|
|
29
|
+
npm version "$NEW" --no-git-tag-version
|
|
30
|
+
echo "version=$NEW" >> "$GITHUB_OUTPUT"
|
|
31
|
+
|
|
32
|
+
- name: Commit and tag
|
|
33
|
+
run: |
|
|
34
|
+
git config user.name "github-actions[bot]"
|
|
35
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
36
|
+
git add package.json package-lock.json
|
|
37
|
+
git commit -m "bump: remember core"
|
|
38
|
+
git commit --allow-empty -m "${{ steps.bump.outputs.version }}"
|
|
39
|
+
git tag "v${{ steps.bump.outputs.version }}"
|
|
40
|
+
git push --follow-tags
|
|
41
|
+
|
|
42
|
+
- name: Publish to npm
|
|
43
|
+
run: npm publish --access public
|
|
44
|
+
env:
|
|
45
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
46
|
+
|
|
47
|
+
- name: Summary
|
|
48
|
+
run: echo "Bumped remember-core to ${{ github.event.client_payload.version }}, published remember-mcp@${{ steps.bump.outputs.version }}"
|
package/agent/progress.yaml
CHANGED
|
@@ -1070,11 +1070,21 @@ tasks:
|
|
|
1070
1070
|
notes: |
|
|
1071
1071
|
Static imports for ghost-config and access-control
|
|
1072
1072
|
|
|
1073
|
+
unassigned:
|
|
1074
|
+
- id: task-201
|
|
1075
|
+
name: Auto-bump remember-core via GitHub Actions
|
|
1076
|
+
status: not_started
|
|
1077
|
+
file: agent/tasks/unassigned/task-201-auto-bump-remember-core-ci.md
|
|
1078
|
+
estimated_hours: 2
|
|
1079
|
+
notes: |
|
|
1080
|
+
Set up repository_dispatch CI/CD: remember-core publish triggers remember-mcp bump + publish.
|
|
1081
|
+
Draft workflow files already created in both repos (not yet committed).
|
|
1082
|
+
|
|
1073
1083
|
documentation:
|
|
1074
1084
|
design_documents: 31
|
|
1075
1085
|
milestone_documents: 14
|
|
1076
1086
|
pattern_documents: 7
|
|
1077
|
-
task_documents:
|
|
1087
|
+
task_documents: 92
|
|
1078
1088
|
|
|
1079
1089
|
progress:
|
|
1080
1090
|
planning: 100%
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Task 201: Auto-bump remember-core via GitHub Actions
|
|
2
|
+
|
|
3
|
+
**Milestone**: Unassigned
|
|
4
|
+
**Estimated Time**: 1-2 hours
|
|
5
|
+
**Dependencies**: None
|
|
6
|
+
**Status**: Not Started
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Objective
|
|
11
|
+
|
|
12
|
+
Set up GitHub Actions CI/CD so that when remember-core publishes a new version to npm, remember-mcp automatically bumps the dependency, patches its own version, and publishes to npm.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Context
|
|
17
|
+
|
|
18
|
+
Currently, bumping remember-core in remember-mcp is a manual process: update package.json, commit, tag, publish. This happens frequently (almost every remember-core release). Automating this eliminates toil and reduces lag between core changes and MCP availability.
|
|
19
|
+
|
|
20
|
+
The approach uses `repository_dispatch` events: remember-core's publish workflow notifies remember-mcp after a successful npm publish, and remember-mcp's workflow handles the bump + publish.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Steps
|
|
25
|
+
|
|
26
|
+
### 1. Configure GitHub Secrets
|
|
27
|
+
|
|
28
|
+
Add required secrets to both repositories:
|
|
29
|
+
|
|
30
|
+
- **remember-core**: `REPO_DISPATCH_TOKEN` - a GitHub PAT with `repo` scope (to trigger dispatches on remember-mcp)
|
|
31
|
+
- **remember-mcp**: `NPM_TOKEN` - an npm access token for publishing
|
|
32
|
+
|
|
33
|
+
### 2. Add dispatch step to remember-core publish workflow
|
|
34
|
+
|
|
35
|
+
File: `remember-core/.github/workflows/publish.yml`
|
|
36
|
+
|
|
37
|
+
A "Notify remember-mcp" step has been drafted that sends a `repository_dispatch` event after successful npm publish. Review and merge.
|
|
38
|
+
|
|
39
|
+
### 3. Add bump workflow to remember-mcp
|
|
40
|
+
|
|
41
|
+
File: `remember-mcp/.github/workflows/bump-remember-core.yml`
|
|
42
|
+
|
|
43
|
+
A workflow has been drafted that:
|
|
44
|
+
- Triggers on `repository_dispatch` with `dependency-update` type
|
|
45
|
+
- Filters to `@prmichaelsen/remember-core` events
|
|
46
|
+
- Installs the new version
|
|
47
|
+
- Patch-bumps remember-mcp version
|
|
48
|
+
- Commits (`bump: remember core` then `{version}`)
|
|
49
|
+
- Tags and pushes
|
|
50
|
+
- Publishes to npm
|
|
51
|
+
|
|
52
|
+
### 4. Fix remember-mcp git remote
|
|
53
|
+
|
|
54
|
+
The remember-mcp git remote currently points to `remember-core.git` instead of `remember-mcp.git`. Fix with:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git remote set-url origin git@github.com:prmichaelsen/remember-mcp.git
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 5. Test end-to-end
|
|
61
|
+
|
|
62
|
+
- Push a version bump to remember-core main
|
|
63
|
+
- Verify remember-core publishes to npm
|
|
64
|
+
- Verify dispatch fires
|
|
65
|
+
- Verify remember-mcp bumps, commits, tags, publishes
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Verification
|
|
70
|
+
|
|
71
|
+
- [ ] `REPO_DISPATCH_TOKEN` secret set in remember-core
|
|
72
|
+
- [ ] `NPM_TOKEN` secret set in remember-mcp
|
|
73
|
+
- [ ] remember-core publish.yml includes dispatch step
|
|
74
|
+
- [ ] remember-mcp bump-remember-core.yml exists
|
|
75
|
+
- [ ] remember-mcp git remote points to correct repo
|
|
76
|
+
- [ ] End-to-end test: remember-core publish triggers remember-mcp bump + publish
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Notes
|
|
81
|
+
|
|
82
|
+
- Draft workflow files already created in both repos (not yet committed)
|
|
83
|
+
- remember-core publish.yml already has NPM_TOKEN configured
|
|
84
|
+
- The bump workflow commits in the existing pattern: `bump: remember core` then `{version}`
|
|
85
|
+
- Consider adding error notifications (GitHub Actions failure emails are on by default)
|
package/dist/server-factory.js
CHANGED
|
@@ -803,6 +803,64 @@ function handleToolError(error, context) {
|
|
|
803
803
|
throw new Error(detailedMessage);
|
|
804
804
|
}
|
|
805
805
|
|
|
806
|
+
// node_modules/@prmichaelsen/remember-core/dist/types/trust.types.js
|
|
807
|
+
var TrustLevel = {
|
|
808
|
+
/** Anyone can see this, including strangers */
|
|
809
|
+
PUBLIC: 1,
|
|
810
|
+
/** Friends and known users */
|
|
811
|
+
INTERNAL: 2,
|
|
812
|
+
/** Trusted friends only */
|
|
813
|
+
CONFIDENTIAL: 3,
|
|
814
|
+
/** Close/intimate contacts only */
|
|
815
|
+
RESTRICTED: 4,
|
|
816
|
+
/** Owner only (or explicitly granted) */
|
|
817
|
+
SECRET: 5
|
|
818
|
+
};
|
|
819
|
+
var TRUST_LABELS = {
|
|
820
|
+
[TrustLevel.PUBLIC]: "Public",
|
|
821
|
+
[TrustLevel.INTERNAL]: "Internal",
|
|
822
|
+
[TrustLevel.CONFIDENTIAL]: "Confidential",
|
|
823
|
+
[TrustLevel.RESTRICTED]: "Restricted",
|
|
824
|
+
[TrustLevel.SECRET]: "Secret"
|
|
825
|
+
};
|
|
826
|
+
var ALL_TRUST_LEVELS = [
|
|
827
|
+
TrustLevel.PUBLIC,
|
|
828
|
+
TrustLevel.INTERNAL,
|
|
829
|
+
TrustLevel.CONFIDENTIAL,
|
|
830
|
+
TrustLevel.RESTRICTED,
|
|
831
|
+
TrustLevel.SECRET
|
|
832
|
+
];
|
|
833
|
+
function isValidTrustLevel(value) {
|
|
834
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 5;
|
|
835
|
+
}
|
|
836
|
+
function normalizeTrustScore(value) {
|
|
837
|
+
if (value == null)
|
|
838
|
+
return TrustLevel.INTERNAL;
|
|
839
|
+
if (isValidTrustLevel(value))
|
|
840
|
+
return value;
|
|
841
|
+
const inverted = 1 - value;
|
|
842
|
+
if (inverted >= 0.875)
|
|
843
|
+
return TrustLevel.SECRET;
|
|
844
|
+
if (inverted >= 0.625)
|
|
845
|
+
return TrustLevel.RESTRICTED;
|
|
846
|
+
if (inverted >= 0.375)
|
|
847
|
+
return TrustLevel.CONFIDENTIAL;
|
|
848
|
+
if (inverted >= 0.125)
|
|
849
|
+
return TrustLevel.INTERNAL;
|
|
850
|
+
return TrustLevel.PUBLIC;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// node_modules/@prmichaelsen/remember-core/dist/types/ghost-config.types.js
|
|
854
|
+
var DEFAULT_GHOST_CONFIG = {
|
|
855
|
+
enabled: false,
|
|
856
|
+
public_ghost_enabled: false,
|
|
857
|
+
default_friend_trust: TrustLevel.INTERNAL,
|
|
858
|
+
default_public_trust: TrustLevel.PUBLIC,
|
|
859
|
+
per_user_trust: {},
|
|
860
|
+
blocked_users: [],
|
|
861
|
+
enforcement_mode: "query"
|
|
862
|
+
};
|
|
863
|
+
|
|
806
864
|
// node_modules/@prmichaelsen/remember-core/dist/types/preferences.types.js
|
|
807
865
|
var DEFAULT_PREFERENCES = {
|
|
808
866
|
templates: {
|
|
@@ -828,7 +886,7 @@ var DEFAULT_PREFERENCES = {
|
|
|
828
886
|
share_with_memories: true
|
|
829
887
|
},
|
|
830
888
|
privacy: {
|
|
831
|
-
default_trust_level:
|
|
889
|
+
default_trust_level: 2,
|
|
832
890
|
allow_cross_user_access: false,
|
|
833
891
|
auto_approve_requests: false,
|
|
834
892
|
audit_logging: true
|
|
@@ -847,6 +905,14 @@ var DEFAULT_PREFERENCES = {
|
|
|
847
905
|
}
|
|
848
906
|
};
|
|
849
907
|
|
|
908
|
+
// node_modules/@prmichaelsen/remember-core/dist/types/rating.types.js
|
|
909
|
+
var RATING_MIN_THRESHOLD = 5;
|
|
910
|
+
function computeRatingAvg(ratingSum, ratingCount) {
|
|
911
|
+
if (ratingCount < RATING_MIN_THRESHOLD)
|
|
912
|
+
return null;
|
|
913
|
+
return ratingSum / ratingCount;
|
|
914
|
+
}
|
|
915
|
+
|
|
850
916
|
// node_modules/@prmichaelsen/remember-core/dist/types/space.types.js
|
|
851
917
|
var SUPPORTED_SPACES = [
|
|
852
918
|
"the_void",
|
|
@@ -1303,6 +1369,35 @@ var DebugLevel2;
|
|
|
1303
1369
|
DebugLevel3[DebugLevel3["TRACE"] = 5] = "TRACE";
|
|
1304
1370
|
})(DebugLevel2 || (DebugLevel2 = {}));
|
|
1305
1371
|
|
|
1372
|
+
// node_modules/@prmichaelsen/remember-core/dist/errors/base.error.js
|
|
1373
|
+
var AppError = class extends Error {
|
|
1374
|
+
context;
|
|
1375
|
+
constructor(message, context = {}) {
|
|
1376
|
+
super(message);
|
|
1377
|
+
this.context = context;
|
|
1378
|
+
this.name = this.constructor.name;
|
|
1379
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
1380
|
+
}
|
|
1381
|
+
toJSON() {
|
|
1382
|
+
return {
|
|
1383
|
+
kind: this.kind,
|
|
1384
|
+
name: this.name,
|
|
1385
|
+
message: this.message,
|
|
1386
|
+
context: this.context
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
// node_modules/@prmichaelsen/remember-core/dist/errors/app-errors.js
|
|
1392
|
+
var ValidationError = class extends AppError {
|
|
1393
|
+
fields;
|
|
1394
|
+
kind = "validation";
|
|
1395
|
+
constructor(message, fields = {}) {
|
|
1396
|
+
super(message, { fields });
|
|
1397
|
+
this.fields = fields;
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1306
1401
|
// node_modules/@prmichaelsen/remember-core/node_modules/uuid/dist/esm/regex.js
|
|
1307
1402
|
var regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
|
|
1308
1403
|
|
|
@@ -1579,9 +1674,15 @@ function buildDocTypeFilters(collection, docType, filters) {
|
|
|
1579
1674
|
if (filters?.relationship_count_max !== void 0) {
|
|
1580
1675
|
filterList.push(collection.filter.byProperty("relationship_count").lessOrEqual(filters.relationship_count_max));
|
|
1581
1676
|
}
|
|
1677
|
+
if (filters?.rating_min !== void 0) {
|
|
1678
|
+
filterList.push(collection.filter.byProperty("rating_bayesian").greaterOrEqual(filters.rating_min));
|
|
1679
|
+
}
|
|
1582
1680
|
if (filters?.tags && filters.tags.length > 0) {
|
|
1583
1681
|
filterList.push(collection.filter.byProperty("tags").containsAny(filters.tags));
|
|
1584
1682
|
}
|
|
1683
|
+
if (filters?.memory_ids && filters.memory_ids.length > 0) {
|
|
1684
|
+
filterList.push(collection.filter.byId().containsAny(filters.memory_ids));
|
|
1685
|
+
}
|
|
1585
1686
|
return combineFiltersWithAnd(filterList);
|
|
1586
1687
|
}
|
|
1587
1688
|
function buildMemoryOnlyFilters(collection, filters) {
|
|
@@ -1943,6 +2044,9 @@ var ALL_MEMORY_PROPERTIES = [
|
|
|
1943
2044
|
"observation",
|
|
1944
2045
|
"strength",
|
|
1945
2046
|
"source",
|
|
2047
|
+
"rating_sum",
|
|
2048
|
+
"rating_count",
|
|
2049
|
+
"rating_bayesian",
|
|
1946
2050
|
"access_count",
|
|
1947
2051
|
"last_accessed_at",
|
|
1948
2052
|
"tags",
|
|
@@ -1988,14 +2092,27 @@ function buildTrustFilter(collection, accessorTrustLevel) {
|
|
|
1988
2092
|
}
|
|
1989
2093
|
|
|
1990
2094
|
// node_modules/@prmichaelsen/remember-core/dist/services/memory.service.js
|
|
2095
|
+
function normalizeDoc(doc) {
|
|
2096
|
+
if ("trust_score" in doc) {
|
|
2097
|
+
doc.trust_score = normalizeTrustScore(doc.trust_score);
|
|
2098
|
+
}
|
|
2099
|
+
const ratingSum = doc.rating_sum;
|
|
2100
|
+
const ratingCount = doc.rating_count;
|
|
2101
|
+
if (ratingSum !== void 0 && ratingCount !== void 0) {
|
|
2102
|
+
doc.rating_avg = computeRatingAvg(ratingSum, ratingCount);
|
|
2103
|
+
}
|
|
2104
|
+
return doc;
|
|
2105
|
+
}
|
|
1991
2106
|
var MemoryService = class {
|
|
1992
2107
|
collection;
|
|
1993
2108
|
userId;
|
|
1994
2109
|
logger;
|
|
1995
|
-
|
|
2110
|
+
options;
|
|
2111
|
+
constructor(collection, userId, logger2, options) {
|
|
1996
2112
|
this.collection = collection;
|
|
1997
2113
|
this.userId = userId;
|
|
1998
2114
|
this.logger = logger2;
|
|
2115
|
+
this.options = options;
|
|
1999
2116
|
}
|
|
2000
2117
|
/**
|
|
2001
2118
|
* Execute a search function, retrying without the deleted_at filter if
|
|
@@ -2018,7 +2135,31 @@ var MemoryService = class {
|
|
|
2018
2135
|
throw new Error(`Memory not found: ${memoryId}`);
|
|
2019
2136
|
if (existing.properties.user_id !== this.userId)
|
|
2020
2137
|
throw new Error("Unauthorized");
|
|
2021
|
-
return { memory: { id: existing.uuid, ...existing.properties } };
|
|
2138
|
+
return { memory: normalizeDoc({ id: existing.uuid, ...existing.properties }) };
|
|
2139
|
+
}
|
|
2140
|
+
// ── Resolve by ID (cross-collection) ─────────────────────────────────
|
|
2141
|
+
/**
|
|
2142
|
+
* Resolve any memory by UUID alone using the Firestore index.
|
|
2143
|
+
* Requires `memoryIndex` and `weaviateClient` in constructor options.
|
|
2144
|
+
*/
|
|
2145
|
+
async resolveById(memoryId) {
|
|
2146
|
+
if (!this.options?.weaviateClient) {
|
|
2147
|
+
throw new Error("resolveById requires weaviateClient in options");
|
|
2148
|
+
}
|
|
2149
|
+
if (this.options?.memoryIndex) {
|
|
2150
|
+
const collectionName = await this.options.memoryIndex.lookup(memoryId);
|
|
2151
|
+
if (collectionName) {
|
|
2152
|
+
const col = this.options.weaviateClient.collections.get(collectionName);
|
|
2153
|
+
const memory = await fetchMemoryWithAllProperties(col, memoryId);
|
|
2154
|
+
if (memory?.properties) {
|
|
2155
|
+
return {
|
|
2156
|
+
memory: normalizeDoc({ id: memory.uuid, ...memory.properties }),
|
|
2157
|
+
collectionName
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
return { memory: null, collectionName: null };
|
|
2022
2163
|
}
|
|
2023
2164
|
// ── Create ──────────────────────────────────────────────────────────
|
|
2024
2165
|
async create(input) {
|
|
@@ -2032,12 +2173,16 @@ var MemoryService = class {
|
|
|
2032
2173
|
summary: input.title,
|
|
2033
2174
|
content_type: contentType,
|
|
2034
2175
|
weight: input.weight ?? 0.5,
|
|
2035
|
-
trust_score: input.trust ??
|
|
2176
|
+
trust_score: normalizeTrustScore(input.trust ?? TrustLevel.INTERNAL),
|
|
2036
2177
|
confidence: 1,
|
|
2037
2178
|
context_summary: input.context_summary || "Memory created",
|
|
2038
2179
|
context_conversation_id: input.context_conversation_id,
|
|
2039
2180
|
relationship_ids: [],
|
|
2040
2181
|
relationship_count: 0,
|
|
2182
|
+
rating_sum: 0,
|
|
2183
|
+
rating_count: 0,
|
|
2184
|
+
rating_bayesian: 3,
|
|
2185
|
+
// (0 + 15) / (0 + 5) = 3.0 (prior mean)
|
|
2041
2186
|
access_count: 0,
|
|
2042
2187
|
last_accessed_at: now,
|
|
2043
2188
|
created_at: now,
|
|
@@ -2057,6 +2202,14 @@ var MemoryService = class {
|
|
|
2057
2202
|
};
|
|
2058
2203
|
const memoryId = await this.collection.data.insert({ properties });
|
|
2059
2204
|
this.logger.info("Memory created", { memoryId, userId: this.userId });
|
|
2205
|
+
if (this.options?.memoryIndex) {
|
|
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}`);
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2060
2213
|
return { memory_id: memoryId, created_at: now };
|
|
2061
2214
|
}
|
|
2062
2215
|
// ── Search (hybrid) ─────────────────────────────────────────────────
|
|
@@ -2094,7 +2247,7 @@ var MemoryService = class {
|
|
|
2094
2247
|
const memories = [];
|
|
2095
2248
|
const relationships = [];
|
|
2096
2249
|
for (const obj of paginated) {
|
|
2097
|
-
const doc = { id: obj.uuid, ...obj.properties };
|
|
2250
|
+
const doc = normalizeDoc({ id: obj.uuid, ...obj.properties });
|
|
2098
2251
|
if (doc.doc_type === "memory")
|
|
2099
2252
|
memories.push(doc);
|
|
2100
2253
|
else if (doc.doc_type === "relationship")
|
|
@@ -2137,7 +2290,7 @@ var MemoryService = class {
|
|
|
2137
2290
|
const paginated = results.objects.slice(offset);
|
|
2138
2291
|
const memories = [];
|
|
2139
2292
|
for (const obj of paginated) {
|
|
2140
|
-
const doc = { id: obj.uuid, ...obj.properties };
|
|
2293
|
+
const doc = normalizeDoc({ id: obj.uuid, ...obj.properties });
|
|
2141
2294
|
if (doc.doc_type === "memory") {
|
|
2142
2295
|
memories.push(doc);
|
|
2143
2296
|
}
|
|
@@ -2181,7 +2334,48 @@ var MemoryService = class {
|
|
|
2181
2334
|
const paginated = results.objects.slice(offset);
|
|
2182
2335
|
const memories = [];
|
|
2183
2336
|
for (const obj of paginated) {
|
|
2184
|
-
const doc = { id: obj.uuid, ...obj.properties };
|
|
2337
|
+
const doc = normalizeDoc({ id: obj.uuid, ...obj.properties });
|
|
2338
|
+
if (doc.doc_type === "memory") {
|
|
2339
|
+
memories.push(doc);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
return {
|
|
2343
|
+
memories,
|
|
2344
|
+
total: memories.length,
|
|
2345
|
+
offset,
|
|
2346
|
+
limit
|
|
2347
|
+
};
|
|
2348
|
+
}
|
|
2349
|
+
// ── By Rating (Bayesian average) ─────────────────────────────────
|
|
2350
|
+
async byRating(input) {
|
|
2351
|
+
const limit = input.limit ?? 50;
|
|
2352
|
+
const offset = input.offset ?? 0;
|
|
2353
|
+
const direction = input.direction ?? "desc";
|
|
2354
|
+
const memoryFilters = buildMemoryOnlyFilters(this.collection, input.filters);
|
|
2355
|
+
const ghostFilters = [];
|
|
2356
|
+
if (input.ghost_context) {
|
|
2357
|
+
ghostFilters.push(buildTrustFilter(this.collection, input.ghost_context.accessor_trust_level));
|
|
2358
|
+
}
|
|
2359
|
+
if (!input.ghost_context?.include_ghost_content) {
|
|
2360
|
+
ghostFilters.push(this.collection.filter.byProperty("content_type").notEqual("ghost"));
|
|
2361
|
+
}
|
|
2362
|
+
const executeQuery = async (useDeletedFilter) => {
|
|
2363
|
+
const deletedFilter = useDeletedFilter ? buildDeletedFilter(this.collection, input.deleted_filter || "exclude") : null;
|
|
2364
|
+
const combinedFilters = combineFiltersWithAnd([deletedFilter, memoryFilters, ...ghostFilters].filter((f) => f !== null));
|
|
2365
|
+
const queryOptions = {
|
|
2366
|
+
limit: limit + offset,
|
|
2367
|
+
sort: this.collection.sort.byProperty("rating_bayesian", direction === "asc")
|
|
2368
|
+
};
|
|
2369
|
+
if (combinedFilters) {
|
|
2370
|
+
queryOptions.filters = combinedFilters;
|
|
2371
|
+
}
|
|
2372
|
+
return this.collection.query.fetchObjects(queryOptions);
|
|
2373
|
+
};
|
|
2374
|
+
const results = await this.retryWithoutDeletedFilter(executeQuery);
|
|
2375
|
+
const paginated = results.objects.slice(offset);
|
|
2376
|
+
const memories = [];
|
|
2377
|
+
for (const obj of paginated) {
|
|
2378
|
+
const doc = normalizeDoc({ id: obj.uuid, ...obj.properties });
|
|
2185
2379
|
if (doc.doc_type === "memory") {
|
|
2186
2380
|
memories.push(doc);
|
|
2187
2381
|
}
|
|
@@ -2240,7 +2434,7 @@ var MemoryService = class {
|
|
|
2240
2434
|
if (!input.include_relationships) {
|
|
2241
2435
|
results.objects = results.objects.filter((o) => o.properties.doc_type === "memory");
|
|
2242
2436
|
}
|
|
2243
|
-
const items = results.objects.map((obj) => ({
|
|
2437
|
+
const items = results.objects.map((obj) => normalizeDoc({
|
|
2244
2438
|
id: obj.uuid,
|
|
2245
2439
|
...obj.properties,
|
|
2246
2440
|
similarity: Math.max(0, Math.min(1, 1 - (obj.metadata?.distance ?? 0)))
|
|
@@ -2269,7 +2463,7 @@ var MemoryService = class {
|
|
|
2269
2463
|
opts.filters = combinedFilters;
|
|
2270
2464
|
return this.collection.query.nearText(input.query, opts);
|
|
2271
2465
|
});
|
|
2272
|
-
const items = results.objects.map((obj) => ({
|
|
2466
|
+
const items = results.objects.map((obj) => normalizeDoc({
|
|
2273
2467
|
id: obj.uuid,
|
|
2274
2468
|
...obj.properties,
|
|
2275
2469
|
relevance: Math.max(0, Math.min(1, 1 - (obj.metadata?.distance ?? 0)))
|
|
@@ -2313,8 +2507,8 @@ var MemoryService = class {
|
|
|
2313
2507
|
updatedFields.push("weight");
|
|
2314
2508
|
}
|
|
2315
2509
|
if (input.trust !== void 0) {
|
|
2316
|
-
if (input.trust
|
|
2317
|
-
throw new Error("Trust must be between
|
|
2510
|
+
if (!isValidTrustLevel(input.trust))
|
|
2511
|
+
throw new Error("Trust must be an integer between 1 and 5");
|
|
2318
2512
|
updates.trust_score = input.trust;
|
|
2319
2513
|
updatedFields.push("trust_score");
|
|
2320
2514
|
}
|
|
@@ -2768,6 +2962,10 @@ var COMMON_MEMORY_PROPERTIES = [
|
|
|
2768
2962
|
{ name: "relationships", dataType: configure.dataType.TEXT_ARRAY },
|
|
2769
2963
|
{ name: "memory_ids", dataType: configure.dataType.TEXT_ARRAY },
|
|
2770
2964
|
{ name: "relationship_count", dataType: configure.dataType.INT },
|
|
2965
|
+
// Rating aggregates (denormalized from Firestore individual ratings)
|
|
2966
|
+
{ name: "rating_sum", dataType: configure.dataType.INT },
|
|
2967
|
+
{ name: "rating_count", dataType: configure.dataType.INT },
|
|
2968
|
+
{ name: "rating_bayesian", dataType: configure.dataType.NUMBER },
|
|
2771
2969
|
// Access tracking
|
|
2772
2970
|
{ name: "access_count", dataType: configure.dataType.NUMBER },
|
|
2773
2971
|
{ name: "last_accessed_at", dataType: configure.dataType.DATE },
|
|
@@ -2944,12 +3142,26 @@ var SpaceService = class {
|
|
|
2944
3142
|
userId;
|
|
2945
3143
|
confirmationTokenService;
|
|
2946
3144
|
logger;
|
|
2947
|
-
|
|
3145
|
+
moderationClient;
|
|
3146
|
+
constructor(weaviateClient, userCollection, userId, confirmationTokenService, logger2, options) {
|
|
2948
3147
|
this.weaviateClient = weaviateClient;
|
|
2949
3148
|
this.userCollection = userCollection;
|
|
2950
3149
|
this.userId = userId;
|
|
2951
3150
|
this.confirmationTokenService = confirmationTokenService;
|
|
2952
3151
|
this.logger = logger2;
|
|
3152
|
+
this.moderationClient = options?.moderationClient;
|
|
3153
|
+
}
|
|
3154
|
+
// ── Content moderation helper ────────────────────────────────────────
|
|
3155
|
+
async checkModeration(content) {
|
|
3156
|
+
if (!this.moderationClient)
|
|
3157
|
+
return;
|
|
3158
|
+
const result = await this.moderationClient.moderate(content);
|
|
3159
|
+
if (!result.pass) {
|
|
3160
|
+
throw new ValidationError(result.reason, {
|
|
3161
|
+
moderation: ["blocked"],
|
|
3162
|
+
...result.category ? { category: [result.category] } : {}
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
2953
3165
|
}
|
|
2954
3166
|
// ── Publish (phase 1: generate confirmation token) ──────────────────
|
|
2955
3167
|
async publish(input) {
|
|
@@ -2984,6 +3196,7 @@ var SpaceService = class {
|
|
|
2984
3196
|
throw new Error(`Space '${spaceId}' only accepts content_type '${requiredType}', got '${memoryContentType ?? "undefined"}'`);
|
|
2985
3197
|
}
|
|
2986
3198
|
}
|
|
3199
|
+
await this.checkModeration(memory.properties.content);
|
|
2987
3200
|
const { token } = await this.confirmationTokenService.createRequest(this.userId, "publish_memory", {
|
|
2988
3201
|
memory_id: input.memory_id,
|
|
2989
3202
|
spaces,
|
|
@@ -3050,6 +3263,7 @@ var SpaceService = class {
|
|
|
3050
3263
|
if (spaceIds.length === 0 && groupIds.length === 0) {
|
|
3051
3264
|
throw new Error("Memory has no published copies to revise. Publish first with publish().");
|
|
3052
3265
|
}
|
|
3266
|
+
await this.checkModeration(memory.properties.content);
|
|
3053
3267
|
const { token } = await this.confirmationTokenService.createRequest(this.userId, "revise_memory", {
|
|
3054
3268
|
memory_id: input.memory_id,
|
|
3055
3269
|
space_ids: spaceIds,
|
package/dist/server.js
CHANGED
|
@@ -807,6 +807,64 @@ function handleToolError(error, context) {
|
|
|
807
807
|
throw new Error(detailedMessage);
|
|
808
808
|
}
|
|
809
809
|
|
|
810
|
+
// node_modules/@prmichaelsen/remember-core/dist/types/trust.types.js
|
|
811
|
+
var TrustLevel = {
|
|
812
|
+
/** Anyone can see this, including strangers */
|
|
813
|
+
PUBLIC: 1,
|
|
814
|
+
/** Friends and known users */
|
|
815
|
+
INTERNAL: 2,
|
|
816
|
+
/** Trusted friends only */
|
|
817
|
+
CONFIDENTIAL: 3,
|
|
818
|
+
/** Close/intimate contacts only */
|
|
819
|
+
RESTRICTED: 4,
|
|
820
|
+
/** Owner only (or explicitly granted) */
|
|
821
|
+
SECRET: 5
|
|
822
|
+
};
|
|
823
|
+
var TRUST_LABELS = {
|
|
824
|
+
[TrustLevel.PUBLIC]: "Public",
|
|
825
|
+
[TrustLevel.INTERNAL]: "Internal",
|
|
826
|
+
[TrustLevel.CONFIDENTIAL]: "Confidential",
|
|
827
|
+
[TrustLevel.RESTRICTED]: "Restricted",
|
|
828
|
+
[TrustLevel.SECRET]: "Secret"
|
|
829
|
+
};
|
|
830
|
+
var ALL_TRUST_LEVELS = [
|
|
831
|
+
TrustLevel.PUBLIC,
|
|
832
|
+
TrustLevel.INTERNAL,
|
|
833
|
+
TrustLevel.CONFIDENTIAL,
|
|
834
|
+
TrustLevel.RESTRICTED,
|
|
835
|
+
TrustLevel.SECRET
|
|
836
|
+
];
|
|
837
|
+
function isValidTrustLevel(value) {
|
|
838
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 5;
|
|
839
|
+
}
|
|
840
|
+
function normalizeTrustScore(value) {
|
|
841
|
+
if (value == null)
|
|
842
|
+
return TrustLevel.INTERNAL;
|
|
843
|
+
if (isValidTrustLevel(value))
|
|
844
|
+
return value;
|
|
845
|
+
const inverted = 1 - value;
|
|
846
|
+
if (inverted >= 0.875)
|
|
847
|
+
return TrustLevel.SECRET;
|
|
848
|
+
if (inverted >= 0.625)
|
|
849
|
+
return TrustLevel.RESTRICTED;
|
|
850
|
+
if (inverted >= 0.375)
|
|
851
|
+
return TrustLevel.CONFIDENTIAL;
|
|
852
|
+
if (inverted >= 0.125)
|
|
853
|
+
return TrustLevel.INTERNAL;
|
|
854
|
+
return TrustLevel.PUBLIC;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// node_modules/@prmichaelsen/remember-core/dist/types/ghost-config.types.js
|
|
858
|
+
var DEFAULT_GHOST_CONFIG = {
|
|
859
|
+
enabled: false,
|
|
860
|
+
public_ghost_enabled: false,
|
|
861
|
+
default_friend_trust: TrustLevel.INTERNAL,
|
|
862
|
+
default_public_trust: TrustLevel.PUBLIC,
|
|
863
|
+
per_user_trust: {},
|
|
864
|
+
blocked_users: [],
|
|
865
|
+
enforcement_mode: "query"
|
|
866
|
+
};
|
|
867
|
+
|
|
810
868
|
// node_modules/@prmichaelsen/remember-core/dist/types/preferences.types.js
|
|
811
869
|
var DEFAULT_PREFERENCES = {
|
|
812
870
|
templates: {
|
|
@@ -832,7 +890,7 @@ var DEFAULT_PREFERENCES = {
|
|
|
832
890
|
share_with_memories: true
|
|
833
891
|
},
|
|
834
892
|
privacy: {
|
|
835
|
-
default_trust_level:
|
|
893
|
+
default_trust_level: 2,
|
|
836
894
|
allow_cross_user_access: false,
|
|
837
895
|
auto_approve_requests: false,
|
|
838
896
|
audit_logging: true
|
|
@@ -851,6 +909,14 @@ var DEFAULT_PREFERENCES = {
|
|
|
851
909
|
}
|
|
852
910
|
};
|
|
853
911
|
|
|
912
|
+
// node_modules/@prmichaelsen/remember-core/dist/types/rating.types.js
|
|
913
|
+
var RATING_MIN_THRESHOLD = 5;
|
|
914
|
+
function computeRatingAvg(ratingSum, ratingCount) {
|
|
915
|
+
if (ratingCount < RATING_MIN_THRESHOLD)
|
|
916
|
+
return null;
|
|
917
|
+
return ratingSum / ratingCount;
|
|
918
|
+
}
|
|
919
|
+
|
|
854
920
|
// node_modules/@prmichaelsen/remember-core/dist/types/space.types.js
|
|
855
921
|
var SUPPORTED_SPACES = [
|
|
856
922
|
"the_void",
|
|
@@ -1307,6 +1373,35 @@ var DebugLevel2;
|
|
|
1307
1373
|
DebugLevel3[DebugLevel3["TRACE"] = 5] = "TRACE";
|
|
1308
1374
|
})(DebugLevel2 || (DebugLevel2 = {}));
|
|
1309
1375
|
|
|
1376
|
+
// node_modules/@prmichaelsen/remember-core/dist/errors/base.error.js
|
|
1377
|
+
var AppError = class extends Error {
|
|
1378
|
+
context;
|
|
1379
|
+
constructor(message, context = {}) {
|
|
1380
|
+
super(message);
|
|
1381
|
+
this.context = context;
|
|
1382
|
+
this.name = this.constructor.name;
|
|
1383
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
1384
|
+
}
|
|
1385
|
+
toJSON() {
|
|
1386
|
+
return {
|
|
1387
|
+
kind: this.kind,
|
|
1388
|
+
name: this.name,
|
|
1389
|
+
message: this.message,
|
|
1390
|
+
context: this.context
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
|
|
1395
|
+
// node_modules/@prmichaelsen/remember-core/dist/errors/app-errors.js
|
|
1396
|
+
var ValidationError = class extends AppError {
|
|
1397
|
+
fields;
|
|
1398
|
+
kind = "validation";
|
|
1399
|
+
constructor(message, fields = {}) {
|
|
1400
|
+
super(message, { fields });
|
|
1401
|
+
this.fields = fields;
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
|
|
1310
1405
|
// node_modules/@prmichaelsen/remember-core/node_modules/uuid/dist/esm/regex.js
|
|
1311
1406
|
var regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
|
|
1312
1407
|
|
|
@@ -1583,9 +1678,15 @@ function buildDocTypeFilters(collection, docType, filters) {
|
|
|
1583
1678
|
if (filters?.relationship_count_max !== void 0) {
|
|
1584
1679
|
filterList.push(collection.filter.byProperty("relationship_count").lessOrEqual(filters.relationship_count_max));
|
|
1585
1680
|
}
|
|
1681
|
+
if (filters?.rating_min !== void 0) {
|
|
1682
|
+
filterList.push(collection.filter.byProperty("rating_bayesian").greaterOrEqual(filters.rating_min));
|
|
1683
|
+
}
|
|
1586
1684
|
if (filters?.tags && filters.tags.length > 0) {
|
|
1587
1685
|
filterList.push(collection.filter.byProperty("tags").containsAny(filters.tags));
|
|
1588
1686
|
}
|
|
1687
|
+
if (filters?.memory_ids && filters.memory_ids.length > 0) {
|
|
1688
|
+
filterList.push(collection.filter.byId().containsAny(filters.memory_ids));
|
|
1689
|
+
}
|
|
1589
1690
|
return combineFiltersWithAnd(filterList);
|
|
1590
1691
|
}
|
|
1591
1692
|
function buildMemoryOnlyFilters(collection, filters) {
|
|
@@ -1947,6 +2048,9 @@ var ALL_MEMORY_PROPERTIES = [
|
|
|
1947
2048
|
"observation",
|
|
1948
2049
|
"strength",
|
|
1949
2050
|
"source",
|
|
2051
|
+
"rating_sum",
|
|
2052
|
+
"rating_count",
|
|
2053
|
+
"rating_bayesian",
|
|
1950
2054
|
"access_count",
|
|
1951
2055
|
"last_accessed_at",
|
|
1952
2056
|
"tags",
|
|
@@ -1992,14 +2096,27 @@ function buildTrustFilter(collection, accessorTrustLevel) {
|
|
|
1992
2096
|
}
|
|
1993
2097
|
|
|
1994
2098
|
// node_modules/@prmichaelsen/remember-core/dist/services/memory.service.js
|
|
2099
|
+
function normalizeDoc(doc) {
|
|
2100
|
+
if ("trust_score" in doc) {
|
|
2101
|
+
doc.trust_score = normalizeTrustScore(doc.trust_score);
|
|
2102
|
+
}
|
|
2103
|
+
const ratingSum = doc.rating_sum;
|
|
2104
|
+
const ratingCount = doc.rating_count;
|
|
2105
|
+
if (ratingSum !== void 0 && ratingCount !== void 0) {
|
|
2106
|
+
doc.rating_avg = computeRatingAvg(ratingSum, ratingCount);
|
|
2107
|
+
}
|
|
2108
|
+
return doc;
|
|
2109
|
+
}
|
|
1995
2110
|
var MemoryService = class {
|
|
1996
2111
|
collection;
|
|
1997
2112
|
userId;
|
|
1998
2113
|
logger;
|
|
1999
|
-
|
|
2114
|
+
options;
|
|
2115
|
+
constructor(collection, userId, logger2, options) {
|
|
2000
2116
|
this.collection = collection;
|
|
2001
2117
|
this.userId = userId;
|
|
2002
2118
|
this.logger = logger2;
|
|
2119
|
+
this.options = options;
|
|
2003
2120
|
}
|
|
2004
2121
|
/**
|
|
2005
2122
|
* Execute a search function, retrying without the deleted_at filter if
|
|
@@ -2022,7 +2139,31 @@ var MemoryService = class {
|
|
|
2022
2139
|
throw new Error(`Memory not found: ${memoryId}`);
|
|
2023
2140
|
if (existing.properties.user_id !== this.userId)
|
|
2024
2141
|
throw new Error("Unauthorized");
|
|
2025
|
-
return { memory: { id: existing.uuid, ...existing.properties } };
|
|
2142
|
+
return { memory: normalizeDoc({ id: existing.uuid, ...existing.properties }) };
|
|
2143
|
+
}
|
|
2144
|
+
// ── Resolve by ID (cross-collection) ─────────────────────────────────
|
|
2145
|
+
/**
|
|
2146
|
+
* Resolve any memory by UUID alone using the Firestore index.
|
|
2147
|
+
* Requires `memoryIndex` and `weaviateClient` in constructor options.
|
|
2148
|
+
*/
|
|
2149
|
+
async resolveById(memoryId) {
|
|
2150
|
+
if (!this.options?.weaviateClient) {
|
|
2151
|
+
throw new Error("resolveById requires weaviateClient in options");
|
|
2152
|
+
}
|
|
2153
|
+
if (this.options?.memoryIndex) {
|
|
2154
|
+
const collectionName = await this.options.memoryIndex.lookup(memoryId);
|
|
2155
|
+
if (collectionName) {
|
|
2156
|
+
const col = this.options.weaviateClient.collections.get(collectionName);
|
|
2157
|
+
const memory = await fetchMemoryWithAllProperties(col, memoryId);
|
|
2158
|
+
if (memory?.properties) {
|
|
2159
|
+
return {
|
|
2160
|
+
memory: normalizeDoc({ id: memory.uuid, ...memory.properties }),
|
|
2161
|
+
collectionName
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return { memory: null, collectionName: null };
|
|
2026
2167
|
}
|
|
2027
2168
|
// ── Create ──────────────────────────────────────────────────────────
|
|
2028
2169
|
async create(input) {
|
|
@@ -2036,12 +2177,16 @@ var MemoryService = class {
|
|
|
2036
2177
|
summary: input.title,
|
|
2037
2178
|
content_type: contentType,
|
|
2038
2179
|
weight: input.weight ?? 0.5,
|
|
2039
|
-
trust_score: input.trust ??
|
|
2180
|
+
trust_score: normalizeTrustScore(input.trust ?? TrustLevel.INTERNAL),
|
|
2040
2181
|
confidence: 1,
|
|
2041
2182
|
context_summary: input.context_summary || "Memory created",
|
|
2042
2183
|
context_conversation_id: input.context_conversation_id,
|
|
2043
2184
|
relationship_ids: [],
|
|
2044
2185
|
relationship_count: 0,
|
|
2186
|
+
rating_sum: 0,
|
|
2187
|
+
rating_count: 0,
|
|
2188
|
+
rating_bayesian: 3,
|
|
2189
|
+
// (0 + 15) / (0 + 5) = 3.0 (prior mean)
|
|
2045
2190
|
access_count: 0,
|
|
2046
2191
|
last_accessed_at: now,
|
|
2047
2192
|
created_at: now,
|
|
@@ -2061,6 +2206,14 @@ var MemoryService = class {
|
|
|
2061
2206
|
};
|
|
2062
2207
|
const memoryId = await this.collection.data.insert({ properties });
|
|
2063
2208
|
this.logger.info("Memory created", { memoryId, userId: this.userId });
|
|
2209
|
+
if (this.options?.memoryIndex) {
|
|
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}`);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2064
2217
|
return { memory_id: memoryId, created_at: now };
|
|
2065
2218
|
}
|
|
2066
2219
|
// ── Search (hybrid) ─────────────────────────────────────────────────
|
|
@@ -2098,7 +2251,7 @@ var MemoryService = class {
|
|
|
2098
2251
|
const memories = [];
|
|
2099
2252
|
const relationships = [];
|
|
2100
2253
|
for (const obj of paginated) {
|
|
2101
|
-
const doc = { id: obj.uuid, ...obj.properties };
|
|
2254
|
+
const doc = normalizeDoc({ id: obj.uuid, ...obj.properties });
|
|
2102
2255
|
if (doc.doc_type === "memory")
|
|
2103
2256
|
memories.push(doc);
|
|
2104
2257
|
else if (doc.doc_type === "relationship")
|
|
@@ -2141,7 +2294,7 @@ var MemoryService = class {
|
|
|
2141
2294
|
const paginated = results.objects.slice(offset);
|
|
2142
2295
|
const memories = [];
|
|
2143
2296
|
for (const obj of paginated) {
|
|
2144
|
-
const doc = { id: obj.uuid, ...obj.properties };
|
|
2297
|
+
const doc = normalizeDoc({ id: obj.uuid, ...obj.properties });
|
|
2145
2298
|
if (doc.doc_type === "memory") {
|
|
2146
2299
|
memories.push(doc);
|
|
2147
2300
|
}
|
|
@@ -2185,7 +2338,48 @@ var MemoryService = class {
|
|
|
2185
2338
|
const paginated = results.objects.slice(offset);
|
|
2186
2339
|
const memories = [];
|
|
2187
2340
|
for (const obj of paginated) {
|
|
2188
|
-
const doc = { id: obj.uuid, ...obj.properties };
|
|
2341
|
+
const doc = normalizeDoc({ id: obj.uuid, ...obj.properties });
|
|
2342
|
+
if (doc.doc_type === "memory") {
|
|
2343
|
+
memories.push(doc);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
return {
|
|
2347
|
+
memories,
|
|
2348
|
+
total: memories.length,
|
|
2349
|
+
offset,
|
|
2350
|
+
limit
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
// ── By Rating (Bayesian average) ─────────────────────────────────
|
|
2354
|
+
async byRating(input) {
|
|
2355
|
+
const limit = input.limit ?? 50;
|
|
2356
|
+
const offset = input.offset ?? 0;
|
|
2357
|
+
const direction = input.direction ?? "desc";
|
|
2358
|
+
const memoryFilters = buildMemoryOnlyFilters(this.collection, input.filters);
|
|
2359
|
+
const ghostFilters = [];
|
|
2360
|
+
if (input.ghost_context) {
|
|
2361
|
+
ghostFilters.push(buildTrustFilter(this.collection, input.ghost_context.accessor_trust_level));
|
|
2362
|
+
}
|
|
2363
|
+
if (!input.ghost_context?.include_ghost_content) {
|
|
2364
|
+
ghostFilters.push(this.collection.filter.byProperty("content_type").notEqual("ghost"));
|
|
2365
|
+
}
|
|
2366
|
+
const executeQuery = async (useDeletedFilter) => {
|
|
2367
|
+
const deletedFilter = useDeletedFilter ? buildDeletedFilter(this.collection, input.deleted_filter || "exclude") : null;
|
|
2368
|
+
const combinedFilters = combineFiltersWithAnd([deletedFilter, memoryFilters, ...ghostFilters].filter((f) => f !== null));
|
|
2369
|
+
const queryOptions = {
|
|
2370
|
+
limit: limit + offset,
|
|
2371
|
+
sort: this.collection.sort.byProperty("rating_bayesian", direction === "asc")
|
|
2372
|
+
};
|
|
2373
|
+
if (combinedFilters) {
|
|
2374
|
+
queryOptions.filters = combinedFilters;
|
|
2375
|
+
}
|
|
2376
|
+
return this.collection.query.fetchObjects(queryOptions);
|
|
2377
|
+
};
|
|
2378
|
+
const results = await this.retryWithoutDeletedFilter(executeQuery);
|
|
2379
|
+
const paginated = results.objects.slice(offset);
|
|
2380
|
+
const memories = [];
|
|
2381
|
+
for (const obj of paginated) {
|
|
2382
|
+
const doc = normalizeDoc({ id: obj.uuid, ...obj.properties });
|
|
2189
2383
|
if (doc.doc_type === "memory") {
|
|
2190
2384
|
memories.push(doc);
|
|
2191
2385
|
}
|
|
@@ -2244,7 +2438,7 @@ var MemoryService = class {
|
|
|
2244
2438
|
if (!input.include_relationships) {
|
|
2245
2439
|
results.objects = results.objects.filter((o) => o.properties.doc_type === "memory");
|
|
2246
2440
|
}
|
|
2247
|
-
const items = results.objects.map((obj) => ({
|
|
2441
|
+
const items = results.objects.map((obj) => normalizeDoc({
|
|
2248
2442
|
id: obj.uuid,
|
|
2249
2443
|
...obj.properties,
|
|
2250
2444
|
similarity: Math.max(0, Math.min(1, 1 - (obj.metadata?.distance ?? 0)))
|
|
@@ -2273,7 +2467,7 @@ var MemoryService = class {
|
|
|
2273
2467
|
opts.filters = combinedFilters;
|
|
2274
2468
|
return this.collection.query.nearText(input.query, opts);
|
|
2275
2469
|
});
|
|
2276
|
-
const items = results.objects.map((obj) => ({
|
|
2470
|
+
const items = results.objects.map((obj) => normalizeDoc({
|
|
2277
2471
|
id: obj.uuid,
|
|
2278
2472
|
...obj.properties,
|
|
2279
2473
|
relevance: Math.max(0, Math.min(1, 1 - (obj.metadata?.distance ?? 0)))
|
|
@@ -2317,8 +2511,8 @@ var MemoryService = class {
|
|
|
2317
2511
|
updatedFields.push("weight");
|
|
2318
2512
|
}
|
|
2319
2513
|
if (input.trust !== void 0) {
|
|
2320
|
-
if (input.trust
|
|
2321
|
-
throw new Error("Trust must be between
|
|
2514
|
+
if (!isValidTrustLevel(input.trust))
|
|
2515
|
+
throw new Error("Trust must be an integer between 1 and 5");
|
|
2322
2516
|
updates.trust_score = input.trust;
|
|
2323
2517
|
updatedFields.push("trust_score");
|
|
2324
2518
|
}
|
|
@@ -2772,6 +2966,10 @@ var COMMON_MEMORY_PROPERTIES = [
|
|
|
2772
2966
|
{ name: "relationships", dataType: configure.dataType.TEXT_ARRAY },
|
|
2773
2967
|
{ name: "memory_ids", dataType: configure.dataType.TEXT_ARRAY },
|
|
2774
2968
|
{ name: "relationship_count", dataType: configure.dataType.INT },
|
|
2969
|
+
// Rating aggregates (denormalized from Firestore individual ratings)
|
|
2970
|
+
{ name: "rating_sum", dataType: configure.dataType.INT },
|
|
2971
|
+
{ name: "rating_count", dataType: configure.dataType.INT },
|
|
2972
|
+
{ name: "rating_bayesian", dataType: configure.dataType.NUMBER },
|
|
2775
2973
|
// Access tracking
|
|
2776
2974
|
{ name: "access_count", dataType: configure.dataType.NUMBER },
|
|
2777
2975
|
{ name: "last_accessed_at", dataType: configure.dataType.DATE },
|
|
@@ -2948,12 +3146,26 @@ var SpaceService = class {
|
|
|
2948
3146
|
userId;
|
|
2949
3147
|
confirmationTokenService;
|
|
2950
3148
|
logger;
|
|
2951
|
-
|
|
3149
|
+
moderationClient;
|
|
3150
|
+
constructor(weaviateClient, userCollection, userId, confirmationTokenService, logger2, options) {
|
|
2952
3151
|
this.weaviateClient = weaviateClient;
|
|
2953
3152
|
this.userCollection = userCollection;
|
|
2954
3153
|
this.userId = userId;
|
|
2955
3154
|
this.confirmationTokenService = confirmationTokenService;
|
|
2956
3155
|
this.logger = logger2;
|
|
3156
|
+
this.moderationClient = options?.moderationClient;
|
|
3157
|
+
}
|
|
3158
|
+
// ── Content moderation helper ────────────────────────────────────────
|
|
3159
|
+
async checkModeration(content) {
|
|
3160
|
+
if (!this.moderationClient)
|
|
3161
|
+
return;
|
|
3162
|
+
const result = await this.moderationClient.moderate(content);
|
|
3163
|
+
if (!result.pass) {
|
|
3164
|
+
throw new ValidationError(result.reason, {
|
|
3165
|
+
moderation: ["blocked"],
|
|
3166
|
+
...result.category ? { category: [result.category] } : {}
|
|
3167
|
+
});
|
|
3168
|
+
}
|
|
2957
3169
|
}
|
|
2958
3170
|
// ── Publish (phase 1: generate confirmation token) ──────────────────
|
|
2959
3171
|
async publish(input) {
|
|
@@ -2988,6 +3200,7 @@ var SpaceService = class {
|
|
|
2988
3200
|
throw new Error(`Space '${spaceId}' only accepts content_type '${requiredType}', got '${memoryContentType ?? "undefined"}'`);
|
|
2989
3201
|
}
|
|
2990
3202
|
}
|
|
3203
|
+
await this.checkModeration(memory.properties.content);
|
|
2991
3204
|
const { token } = await this.confirmationTokenService.createRequest(this.userId, "publish_memory", {
|
|
2992
3205
|
memory_id: input.memory_id,
|
|
2993
3206
|
spaces,
|
|
@@ -3054,6 +3267,7 @@ var SpaceService = class {
|
|
|
3054
3267
|
if (spaceIds.length === 0 && groupIds.length === 0) {
|
|
3055
3268
|
throw new Error("Memory has no published copies to revise. Publish first with publish().");
|
|
3056
3269
|
}
|
|
3270
|
+
await this.checkModeration(memory.properties.content);
|
|
3057
3271
|
const { token } = await this.confirmationTokenService.createRequest(this.userId, "revise_memory", {
|
|
3058
3272
|
memory_id: input.memory_id,
|
|
3059
3273
|
space_ids: spaceIds,
|
package/esbuild.build.js
CHANGED
|
@@ -13,7 +13,10 @@ await esbuild.build({
|
|
|
13
13
|
external: [
|
|
14
14
|
'weaviate-client',
|
|
15
15
|
'@prmichaelsen/firebase-admin-sdk-v8',
|
|
16
|
-
'@modelcontextprotocol/sdk'
|
|
16
|
+
'@modelcontextprotocol/sdk',
|
|
17
|
+
'unpdf',
|
|
18
|
+
'mammoth',
|
|
19
|
+
'turndown',
|
|
17
20
|
],
|
|
18
21
|
banner: {
|
|
19
22
|
js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);"
|
|
@@ -35,7 +38,10 @@ await esbuild.build({
|
|
|
35
38
|
external: [
|
|
36
39
|
'weaviate-client',
|
|
37
40
|
'@prmichaelsen/firebase-admin-sdk-v8',
|
|
38
|
-
'@modelcontextprotocol/sdk'
|
|
41
|
+
'@modelcontextprotocol/sdk',
|
|
42
|
+
'unpdf',
|
|
43
|
+
'mammoth',
|
|
44
|
+
'turndown',
|
|
39
45
|
],
|
|
40
46
|
banner: {
|
|
41
47
|
js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prmichaelsen/remember-mcp",
|
|
3
|
-
"version": "3.14.
|
|
3
|
+
"version": "3.14.20",
|
|
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.32.1",
|
|
54
54
|
"dotenv": "^16.4.5",
|
|
55
55
|
"uuid": "^13.0.0",
|
|
56
56
|
"weaviate-client": "^3.2.0"
|