@prmichaelsen/remember-mcp 2.8.0 → 3.12.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/AGENT.md +296 -250
- package/CHANGELOG.md +468 -0
- package/README.md +163 -46
- package/agent/commands/acp.clarification-create.md +382 -0
- package/agent/commands/acp.command-create.md +0 -1
- package/agent/commands/acp.design-create.md +0 -1
- package/agent/commands/acp.init.md +0 -1
- package/agent/commands/acp.package-create.md +0 -1
- package/agent/commands/acp.package-info.md +0 -1
- package/agent/commands/acp.package-install.md +0 -1
- package/agent/commands/acp.package-list.md +0 -1
- package/agent/commands/acp.package-publish.md +0 -1
- package/agent/commands/acp.package-remove.md +0 -1
- package/agent/commands/acp.package-search.md +0 -1
- package/agent/commands/acp.package-update.md +0 -1
- package/agent/commands/acp.package-validate.md +0 -1
- package/agent/commands/acp.pattern-create.md +0 -1
- package/agent/commands/acp.plan.md +0 -1
- package/agent/commands/acp.proceed.md +0 -1
- package/agent/commands/acp.project-create.md +0 -1
- package/agent/commands/acp.project-info.md +309 -0
- package/agent/commands/acp.project-list.md +0 -1
- package/agent/commands/acp.project-remove.md +379 -0
- package/agent/commands/acp.project-set.md +0 -1
- package/agent/commands/acp.project-update.md +296 -0
- package/agent/commands/acp.report.md +0 -1
- package/agent/commands/acp.resume.md +0 -1
- package/agent/commands/acp.status.md +0 -1
- package/agent/commands/acp.sync.md +0 -1
- package/agent/commands/acp.task-create.md +17 -10
- package/agent/commands/acp.update.md +0 -1
- package/agent/commands/acp.validate.md +0 -1
- package/agent/commands/acp.version-check-for-updates.md +0 -1
- package/agent/commands/acp.version-check.md +0 -1
- package/agent/commands/acp.version-update.md +0 -1
- package/agent/commands/command.template.md +0 -5
- package/agent/commands/git.commit.md +13 -2
- package/agent/commands/git.init.md +0 -1
- package/agent/design/comment-memory-type.md +2 -2
- package/agent/design/local.collaborative-memory-sync.md +265 -0
- package/agent/design/local.content-flags.md +210 -0
- package/agent/design/local.ghost-persona-system.md +273 -0
- package/agent/design/local.group-acl-integration.md +338 -0
- package/agent/design/local.memory-acl-schema.md +352 -0
- package/agent/design/local.memory-collection-pattern-v2.md +348 -0
- package/agent/design/local.moderation-and-space-config.md +257 -0
- package/agent/design/local.v2-api-reference.md +621 -0
- package/agent/design/local.v2-migration-guide.md +191 -0
- package/agent/design/local.v2-usage-examples.md +265 -0
- package/agent/design/permissions-storage-architecture.md +11 -3
- package/agent/design/soft-delete-system.md +291 -0
- package/agent/design/trust-escalation-prevention.md +9 -2
- package/agent/design/trust-system-implementation.md +12 -3
- package/agent/milestones/milestone-13-soft-delete-system.md +306 -0
- package/agent/milestones/milestone-14-memory-collection-v2.md +182 -0
- package/agent/milestones/milestone-15-moderation-space-config.md +126 -0
- package/agent/package.template.yaml +0 -17
- package/agent/progress.yaml +762 -49
- package/agent/scripts/acp.common.sh +2 -0
- package/agent/scripts/acp.install.sh +15 -85
- package/agent/scripts/acp.package-install-optimized.sh +454 -0
- package/agent/scripts/acp.package-install.sh +248 -380
- package/agent/scripts/acp.package-validate.sh +0 -99
- package/agent/scripts/acp.project-info.sh +218 -0
- package/agent/scripts/acp.project-remove.sh +302 -0
- package/agent/scripts/acp.project-update.sh +296 -0
- package/agent/scripts/acp.yaml-parser.sh +128 -10
- package/agent/tasks/milestone-14-memory-collection-v2/task-165-core-infrastructure-setup.md +171 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-166-update-remember-publish.md +191 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-167-update-remember-retract.md +186 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-168-implement-remember-revise.md +184 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-169-update-remember-search-space.md +179 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-170-update-remember-create-update.md +139 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-172-performance-testing-optimization.md +161 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-173-documentation-examples.md +258 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-174-add-moderation-schema-fields.md +57 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-175-create-space-config-service.md +64 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-176-wire-moderation-publish-flow.md +45 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-177-add-moderation-search-filters.md +70 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-178-create-remember-moderate-tool.md +69 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-179-documentation-integration-tests.md +58 -0
- package/agent/tasks/milestone-16-ghost-system/task-187-ghost-config-firestore.md +41 -0
- package/agent/tasks/milestone-16-ghost-system/task-188-trust-filter-integration.md +44 -0
- package/agent/tasks/milestone-16-ghost-system/task-189-ghost-memory-filtering.md +43 -0
- package/agent/tasks/milestone-16-ghost-system/task-190-ghost-config-tools.md +45 -0
- package/agent/tasks/milestone-16-ghost-system/task-191-escalation-firestore.md +38 -0
- package/agent/tasks/milestone-16-ghost-system/task-192-documentation-verification.md +39 -0
- package/agent/tasks/milestone-7-trust-permissions/task-180-access-result-permission-types.md +69 -0
- package/agent/tasks/milestone-7-trust-permissions/task-181-firestore-permissions-access-logs.md +56 -0
- package/agent/tasks/milestone-7-trust-permissions/task-182-trust-enforcement-service.md +68 -0
- package/agent/tasks/milestone-7-trust-permissions/task-183-access-control-service.md +70 -0
- package/agent/tasks/milestone-7-trust-permissions/task-184-permission-tools.md +79 -0
- package/agent/tasks/milestone-7-trust-permissions/task-185-wire-trust-into-search-query.md +55 -0
- package/agent/tasks/milestone-7-trust-permissions/task-186-documentation-verification.md +56 -0
- package/agent/tasks/task-70-add-soft-delete-schema-fields.md +165 -0
- package/agent/tasks/task-71-implement-delete-confirmation-flow.md +257 -0
- package/agent/tasks/task-72-add-deleted-filter-to-search-tools.md +18 -0
- package/agent/tasks/task-73-update-relationship-handling.md +18 -0
- package/agent/tasks/task-74-add-unit-tests-soft-delete.md +18 -0
- package/agent/tasks/task-75-update-documentation-changelog.md +26 -0
- package/agent/tasks/task-76-fix-indexnullstate-schema-bug.md +197 -0
- package/dist/collections/composite-ids.d.ts +106 -0
- package/dist/collections/core-infrastructure.spec.d.ts +11 -0
- package/dist/collections/dot-notation.d.ts +106 -0
- package/dist/collections/tracking-arrays.d.ts +176 -0
- package/dist/constants/content-types.d.ts +1 -0
- package/dist/schema/v2-collections-comments.spec.d.ts +8 -0
- package/dist/schema/v2-collections.d.ts +210 -0
- package/dist/server-factory.d.ts +15 -0
- package/dist/server-factory.js +3261 -1316
- package/dist/server.js +2926 -1236
- package/dist/services/access-control.d.ts +103 -0
- package/dist/services/access-control.spec.d.ts +2 -0
- package/dist/services/credentials-provider.d.ts +24 -0
- package/dist/services/credentials-provider.spec.d.ts +2 -0
- package/dist/services/escalation.service.d.ts +22 -0
- package/dist/services/escalation.service.spec.d.ts +2 -0
- package/dist/services/ghost-config.service.d.ts +55 -0
- package/dist/services/ghost-config.service.spec.d.ts +2 -0
- package/dist/services/space-config.service.d.ts +23 -0
- package/dist/services/space-config.service.spec.d.ts +2 -0
- package/dist/services/trust-enforcement.d.ts +83 -0
- package/dist/services/trust-enforcement.spec.d.ts +2 -0
- package/dist/services/trust-validator.d.ts +43 -0
- package/dist/services/trust-validator.spec.d.ts +2 -0
- package/dist/tools/confirm-publish-moderation.spec.d.ts +8 -0
- package/dist/tools/confirm.d.ts +8 -1
- package/dist/tools/create-memory.d.ts +2 -1
- package/dist/tools/create-memory.spec.d.ts +10 -0
- package/dist/tools/create-relationship.d.ts +2 -1
- package/dist/tools/delete-memory.d.ts +7 -31
- package/dist/tools/delete-relationship.d.ts +2 -1
- package/dist/tools/deny.d.ts +2 -1
- package/dist/tools/find-similar.d.ts +10 -2
- package/dist/tools/get-preferences.d.ts +2 -1
- package/dist/tools/ghost-config.d.ts +27 -0
- package/dist/tools/ghost-config.spec.d.ts +2 -0
- package/dist/tools/moderate.d.ts +20 -0
- package/dist/tools/moderate.spec.d.ts +5 -0
- package/dist/tools/publish.d.ts +11 -3
- package/dist/tools/query-memory.d.ts +11 -2
- package/dist/tools/query-space.d.ts +4 -1
- package/dist/tools/retract.d.ts +29 -0
- package/dist/tools/revise.d.ts +45 -0
- package/dist/tools/revise.spec.d.ts +8 -0
- package/dist/tools/search-memory.d.ts +8 -1
- package/dist/tools/search-relationship.d.ts +10 -2
- package/dist/tools/search-space.d.ts +25 -5
- package/dist/tools/search-space.spec.d.ts +9 -0
- package/dist/tools/set-preference.d.ts +2 -1
- package/dist/tools/update-memory.d.ts +2 -1
- package/dist/tools/update-relationship.d.ts +2 -1
- package/dist/types/access-result.d.ts +48 -0
- package/dist/types/access-result.spec.d.ts +2 -0
- package/dist/types/auth.d.ts +46 -0
- package/dist/types/ghost-config.d.ts +36 -0
- package/dist/types/memory.d.ts +11 -1
- package/dist/types/preferences.d.ts +1 -1
- package/dist/types/space-memory.d.ts +3 -0
- package/dist/utils/auth-helpers.d.ts +14 -0
- package/dist/utils/auth-helpers.spec.d.ts +2 -0
- package/dist/utils/test-data-generator.d.ts +124 -0
- package/dist/utils/test-data-generator.spec.d.ts +12 -0
- package/dist/utils/weaviate-filters.d.ts +19 -0
- package/dist/v2-performance.e2e.d.ts +17 -0
- package/dist/v2-smoke.e2e.d.ts +14 -0
- package/dist/weaviate/client.d.ts +5 -8
- package/dist/weaviate/space-schema.d.ts +2 -2
- package/docs/performance/v2-benchmarks.md +80 -0
- package/jest.e2e.config.js +14 -3
- package/package.json +1 -1
- package/scripts/.collection-recreation-state.yaml +16 -0
- package/scripts/.gitkeep +5 -0
- package/scripts/README-collection-recreation.md +224 -0
- package/scripts/README.md +51 -0
- package/scripts/backup-collections.ts +543 -0
- package/scripts/delete-collection.ts +137 -0
- package/scripts/migrate-recreate-collections.ts +578 -0
- package/scripts/migrate-v1-to-v2.ts +1094 -0
- package/scripts/package-lock.json +1113 -0
- package/scripts/package.json +27 -0
- package/src/collections/composite-ids.ts +193 -0
- package/src/collections/core-infrastructure.spec.ts +353 -0
- package/src/collections/dot-notation.ts +212 -0
- package/src/collections/tracking-arrays.ts +298 -0
- package/src/constants/content-types.ts +20 -0
- package/src/schema/v2-collections-comments.spec.ts +141 -0
- package/src/schema/v2-collections.ts +433 -0
- package/src/server-factory.ts +89 -20
- package/src/server.ts +45 -17
- package/src/services/access-control.spec.ts +383 -0
- package/src/services/access-control.ts +291 -0
- package/src/services/credentials-provider.spec.ts +22 -0
- package/src/services/credentials-provider.ts +34 -0
- package/src/services/escalation.service.spec.ts +183 -0
- package/src/services/escalation.service.ts +150 -0
- package/src/services/ghost-config.service.spec.ts +339 -0
- package/src/services/ghost-config.service.ts +219 -0
- package/src/services/space-config.service.spec.ts +102 -0
- package/src/services/space-config.service.ts +79 -0
- package/src/services/trust-enforcement.spec.ts +309 -0
- package/src/services/trust-enforcement.ts +197 -0
- package/src/services/trust-validator.spec.ts +108 -0
- package/src/services/trust-validator.ts +105 -0
- package/src/tools/confirm-publish-moderation.spec.ts +240 -0
- package/src/tools/confirm.ts +914 -116
- package/src/tools/create-memory.spec.ts +126 -0
- package/src/tools/create-memory.ts +20 -27
- package/src/tools/create-relationship.ts +30 -8
- package/src/tools/delete-memory.ts +99 -64
- package/src/tools/delete-relationship.ts +15 -6
- package/src/tools/deny.ts +8 -1
- package/src/tools/find-similar.ts +44 -6
- package/src/tools/get-preferences.ts +10 -1
- package/src/tools/ghost-config.spec.ts +180 -0
- package/src/tools/ghost-config.ts +230 -0
- package/src/tools/moderate.spec.ts +277 -0
- package/src/tools/moderate.ts +219 -0
- package/src/tools/publish.ts +99 -41
- package/src/tools/query-memory.ts +44 -9
- package/src/tools/query-space.ts +39 -4
- package/src/tools/retract.ts +292 -0
- package/src/tools/revise.spec.ts +146 -0
- package/src/tools/revise.ts +283 -0
- package/src/tools/search-memory.ts +46 -10
- package/src/tools/search-relationship.ts +30 -7
- package/src/tools/search-space.spec.ts +341 -0
- package/src/tools/search-space.ts +323 -99
- package/src/tools/set-preference.ts +10 -1
- package/src/tools/update-memory.ts +24 -5
- package/src/tools/update-relationship.ts +10 -1
- package/src/types/access-result.spec.ts +193 -0
- package/src/types/access-result.ts +62 -0
- package/src/types/auth.ts +52 -0
- package/src/types/ghost-config.ts +46 -0
- package/src/types/memory.ts +20 -1
- package/src/types/preferences.ts +2 -2
- package/src/types/space-memory.ts +5 -0
- package/src/utils/auth-helpers.spec.ts +75 -0
- package/src/utils/auth-helpers.ts +25 -0
- package/src/utils/test-data-generator.spec.ts +317 -0
- package/src/utils/test-data-generator.ts +292 -0
- package/src/utils/weaviate-filters.ts +32 -5
- package/src/v2-performance.e2e.ts +173 -0
- package/src/v2-smoke.e2e.ts +401 -0
- package/src/weaviate/client.spec.ts +5 -5
- package/src/weaviate/client.ts +55 -35
- package/src/weaviate/schema.ts +11 -239
- package/src/weaviate/space-schema.spec.ts +28 -25
- package/src/weaviate/space-schema.ts +35 -11
package/src/server.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { config, validateConfig } from './config.js';
|
|
|
12
12
|
import { initWeaviateClient, testWeaviateConnection, getWeaviateClient } from './weaviate/client.js';
|
|
13
13
|
import { initFirestore, testFirestoreConnection } from './firestore/init.js';
|
|
14
14
|
import { logger } from './utils/logger.js';
|
|
15
|
+
import type { AuthContext } from './types/auth.js';
|
|
15
16
|
|
|
16
17
|
// Import memory tools
|
|
17
18
|
import { createMemoryTool, handleCreateMemory } from './tools/create-memory.js';
|
|
@@ -33,10 +34,14 @@ import { getPreferencesTool, handleGetPreferences } from './tools/get-preference
|
|
|
33
34
|
|
|
34
35
|
// Import space tools
|
|
35
36
|
import { publishTool, handlePublish } from './tools/publish.js';
|
|
37
|
+
import { retractTool, handleRetract } from './tools/retract.js';
|
|
38
|
+
import { reviseTool, handleRevise } from './tools/revise.js';
|
|
36
39
|
import { confirmTool, handleConfirm } from './tools/confirm.js';
|
|
37
40
|
import { denyTool, handleDeny } from './tools/deny.js';
|
|
38
41
|
import { searchSpaceTool, handleSearchSpace } from './tools/search-space.js';
|
|
39
42
|
import { querySpaceTool, handleQuerySpace } from './tools/query-space.js';
|
|
43
|
+
import { moderateTool, handleModerate } from './tools/moderate.js';
|
|
44
|
+
import { ghostConfigTool, handleGhostConfig } from './tools/ghost-config.js';
|
|
40
45
|
|
|
41
46
|
/**
|
|
42
47
|
* Initialize remember-mcp server
|
|
@@ -107,10 +112,14 @@ function registerHandlers(server: Server): void {
|
|
|
107
112
|
getPreferencesTool,
|
|
108
113
|
// Space tools
|
|
109
114
|
publishTool,
|
|
115
|
+
retractTool,
|
|
116
|
+
reviseTool,
|
|
110
117
|
confirmTool,
|
|
111
118
|
denyTool,
|
|
112
119
|
searchSpaceTool,
|
|
113
120
|
querySpaceTool,
|
|
121
|
+
moderateTool,
|
|
122
|
+
ghostConfigTool,
|
|
114
123
|
],
|
|
115
124
|
};
|
|
116
125
|
});
|
|
@@ -126,73 +135,92 @@ function registerHandlers(server: Server): void {
|
|
|
126
135
|
// TODO: Get userId from authentication context in M6
|
|
127
136
|
const userId = (args as any).user_id || 'default_user';
|
|
128
137
|
|
|
138
|
+
// Standalone mode: no access token or credentials
|
|
139
|
+
const authContext: AuthContext = { accessToken: null, credentials: null };
|
|
140
|
+
|
|
129
141
|
switch (name) {
|
|
130
142
|
case 'remember_create_memory':
|
|
131
|
-
result = await handleCreateMemory(args as any, userId);
|
|
143
|
+
result = await handleCreateMemory(args as any, userId, authContext);
|
|
132
144
|
break;
|
|
133
145
|
|
|
134
146
|
case 'remember_search_memory':
|
|
135
|
-
result = await handleSearchMemory(args as any, userId);
|
|
147
|
+
result = await handleSearchMemory(args as any, userId, authContext);
|
|
136
148
|
break;
|
|
137
149
|
|
|
138
150
|
case 'remember_delete_memory':
|
|
139
|
-
result = await handleDeleteMemory(args as any, userId);
|
|
151
|
+
result = await handleDeleteMemory(args as any, userId, authContext);
|
|
140
152
|
break;
|
|
141
153
|
|
|
142
154
|
case 'remember_update_memory':
|
|
143
|
-
result = await handleUpdateMemory(args as any, userId);
|
|
155
|
+
result = await handleUpdateMemory(args as any, userId, authContext);
|
|
144
156
|
break;
|
|
145
157
|
|
|
146
158
|
case 'remember_find_similar':
|
|
147
|
-
result = await handleFindSimilar(args as any, userId);
|
|
159
|
+
result = await handleFindSimilar(args as any, userId, authContext);
|
|
148
160
|
break;
|
|
149
161
|
|
|
150
162
|
case 'remember_query_memory':
|
|
151
|
-
result = await handleQueryMemory(args as any, userId);
|
|
163
|
+
result = await handleQueryMemory(args as any, userId, authContext);
|
|
152
164
|
break;
|
|
153
165
|
|
|
154
166
|
case 'remember_create_relationship':
|
|
155
|
-
result = await handleCreateRelationship(args as any, userId);
|
|
167
|
+
result = await handleCreateRelationship(args as any, userId, authContext);
|
|
156
168
|
break;
|
|
157
169
|
|
|
158
170
|
case 'remember_update_relationship':
|
|
159
|
-
result = await handleUpdateRelationship(args as any, userId);
|
|
171
|
+
result = await handleUpdateRelationship(args as any, userId, authContext);
|
|
160
172
|
break;
|
|
161
173
|
|
|
162
174
|
case 'remember_search_relationship':
|
|
163
|
-
result = await handleSearchRelationship(args as any, userId);
|
|
175
|
+
result = await handleSearchRelationship(args as any, userId, authContext);
|
|
164
176
|
break;
|
|
165
177
|
|
|
166
178
|
case 'remember_delete_relationship':
|
|
167
|
-
result = await handleDeleteRelationship(args as any, userId);
|
|
179
|
+
result = await handleDeleteRelationship(args as any, userId, authContext);
|
|
168
180
|
break;
|
|
169
181
|
|
|
170
182
|
case 'remember_set_preference':
|
|
171
|
-
result = await handleSetPreference(args as any, userId);
|
|
183
|
+
result = await handleSetPreference(args as any, userId, authContext);
|
|
172
184
|
break;
|
|
173
185
|
|
|
174
186
|
case 'remember_get_preferences':
|
|
175
|
-
result = await handleGetPreferences(args as any, userId);
|
|
187
|
+
result = await handleGetPreferences(args as any, userId, authContext);
|
|
176
188
|
break;
|
|
177
189
|
|
|
178
190
|
case 'remember_publish':
|
|
179
|
-
result = await handlePublish(args as any, userId);
|
|
191
|
+
result = await handlePublish(args as any, userId, authContext);
|
|
192
|
+
break;
|
|
193
|
+
|
|
194
|
+
case 'remember_retract':
|
|
195
|
+
result = await handleRetract(args as any, userId, authContext);
|
|
196
|
+
break;
|
|
197
|
+
|
|
198
|
+
case 'remember_revise':
|
|
199
|
+
result = await handleRevise(args as any, userId, authContext);
|
|
180
200
|
break;
|
|
181
201
|
|
|
182
202
|
case 'remember_confirm':
|
|
183
|
-
result = await handleConfirm(args as any, userId);
|
|
203
|
+
result = await handleConfirm(args as any, userId, authContext);
|
|
184
204
|
break;
|
|
185
205
|
|
|
186
206
|
case 'remember_deny':
|
|
187
|
-
result = await handleDeny(args as any, userId);
|
|
207
|
+
result = await handleDeny(args as any, userId, authContext);
|
|
188
208
|
break;
|
|
189
209
|
|
|
190
210
|
case 'remember_search_space':
|
|
191
|
-
result = await handleSearchSpace(args as any, userId);
|
|
211
|
+
result = await handleSearchSpace(args as any, userId, authContext);
|
|
192
212
|
break;
|
|
193
213
|
|
|
194
214
|
case 'remember_query_space':
|
|
195
|
-
result = await handleQuerySpace(args as any, userId);
|
|
215
|
+
result = await handleQuerySpace(args as any, userId, authContext);
|
|
216
|
+
break;
|
|
217
|
+
|
|
218
|
+
case 'remember_moderate':
|
|
219
|
+
result = await handleModerate(args as any, userId, authContext);
|
|
220
|
+
break;
|
|
221
|
+
|
|
222
|
+
case 'remember_ghost_config':
|
|
223
|
+
result = await handleGhostConfig(args as any, userId, authContext);
|
|
196
224
|
break;
|
|
197
225
|
|
|
198
226
|
default:
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkMemoryAccess,
|
|
3
|
+
handleInsufficientTrust,
|
|
4
|
+
isMemoryBlocked,
|
|
5
|
+
resetBlock,
|
|
6
|
+
resolveAccessorTrustLevel,
|
|
7
|
+
formatAccessResultMessage,
|
|
8
|
+
StubGhostConfigProvider,
|
|
9
|
+
InMemoryEscalationStore,
|
|
10
|
+
} from './access-control.js';
|
|
11
|
+
import type { Memory } from '../types/memory.js';
|
|
12
|
+
import type { AccessResult } from '../types/access-result.js';
|
|
13
|
+
import type { GhostConfig } from '../types/ghost-config.js';
|
|
14
|
+
import { DEFAULT_GHOST_CONFIG } from '../types/ghost-config.js';
|
|
15
|
+
|
|
16
|
+
// ─── Fixtures ──────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
function createMemory(overrides: Partial<Memory> = {}): Memory {
|
|
19
|
+
return {
|
|
20
|
+
id: 'mem-1',
|
|
21
|
+
user_id: 'owner',
|
|
22
|
+
doc_type: 'memory',
|
|
23
|
+
content: 'Test content',
|
|
24
|
+
type: 'note',
|
|
25
|
+
weight: 0.5,
|
|
26
|
+
trust: 0.5,
|
|
27
|
+
location: { gps: null, address: null, source: 'unavailable', confidence: 0, is_approximate: true },
|
|
28
|
+
context: { timestamp: '2026-01-01T00:00:00Z', source: { type: 'manual' } },
|
|
29
|
+
relationships: [],
|
|
30
|
+
access_count: 0,
|
|
31
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
32
|
+
updated_at: '2026-01-01T00:00:00Z',
|
|
33
|
+
version: 1,
|
|
34
|
+
tags: [],
|
|
35
|
+
base_weight: 0.5,
|
|
36
|
+
...overrides,
|
|
37
|
+
} as Memory;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createEnabledGhostConfig(overrides: Partial<GhostConfig> = {}): GhostConfig {
|
|
41
|
+
return {
|
|
42
|
+
...DEFAULT_GHOST_CONFIG,
|
|
43
|
+
enabled: true,
|
|
44
|
+
public_ghost_enabled: true,
|
|
45
|
+
default_public_trust: 0.75,
|
|
46
|
+
...overrides,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── checkMemoryAccess ─────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
describe('checkMemoryAccess', () => {
|
|
53
|
+
let configProvider: StubGhostConfigProvider;
|
|
54
|
+
let escalationStore: InMemoryEscalationStore;
|
|
55
|
+
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
configProvider = new StubGhostConfigProvider();
|
|
58
|
+
escalationStore = new InMemoryEscalationStore();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('grants access to owner (self-access)', async () => {
|
|
62
|
+
const memory = createMemory({ user_id: 'alice', trust: 1.0 });
|
|
63
|
+
const result = await checkMemoryAccess('alice', memory, configProvider, escalationStore);
|
|
64
|
+
expect(result.status).toBe('granted');
|
|
65
|
+
if (result.status === 'granted') {
|
|
66
|
+
expect(result.access_level).toBe('owner');
|
|
67
|
+
expect(result.memory.id).toBe('mem-1');
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('self-access works even with high trust memory', async () => {
|
|
72
|
+
const memory = createMemory({ user_id: 'alice', trust: 1.0 });
|
|
73
|
+
const result = await checkMemoryAccess('alice', memory, configProvider, escalationStore);
|
|
74
|
+
expect(result.status).toBe('granted');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('returns no_permission when ghost not enabled', async () => {
|
|
78
|
+
const memory = createMemory({ user_id: 'owner' });
|
|
79
|
+
// No ghost config set — returns null
|
|
80
|
+
const result = await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
81
|
+
expect(result.status).toBe('no_permission');
|
|
82
|
+
if (result.status === 'no_permission') {
|
|
83
|
+
expect(result.owner_user_id).toBe('owner');
|
|
84
|
+
expect(result.accessor_user_id).toBe('accessor');
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('returns no_permission when ghost explicitly disabled', async () => {
|
|
89
|
+
configProvider.setGhostConfig('owner', { ...DEFAULT_GHOST_CONFIG, enabled: false });
|
|
90
|
+
const memory = createMemory({ user_id: 'owner' });
|
|
91
|
+
const result = await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
92
|
+
expect(result.status).toBe('no_permission');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('returns no_permission when accessor is in blocked_users', async () => {
|
|
96
|
+
configProvider.setGhostConfig('owner', createEnabledGhostConfig({ blocked_users: ['bad-actor'] }));
|
|
97
|
+
const memory = createMemory({ user_id: 'owner', trust: 0.25 });
|
|
98
|
+
const result = await checkMemoryAccess('bad-actor', memory, configProvider, escalationStore);
|
|
99
|
+
expect(result.status).toBe('no_permission');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('grants trust 1.0 memories when accessor trust is 1.0 (existence-only handled by formatting)', async () => {
|
|
103
|
+
configProvider.setGhostConfig('owner', createEnabledGhostConfig({
|
|
104
|
+
per_user_trust: { 'trusted-accessor': 1.0 },
|
|
105
|
+
}));
|
|
106
|
+
const memory = createMemory({ user_id: 'owner', trust: 1.0 });
|
|
107
|
+
const result = await checkMemoryAccess('trusted-accessor', memory, configProvider, escalationStore);
|
|
108
|
+
expect(result.status).toBe('granted');
|
|
109
|
+
if (result.status === 'granted') {
|
|
110
|
+
expect(result.access_level).toBe('trusted');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('trust 1.0 memories trigger insufficient_trust when accessor trust < 1.0', async () => {
|
|
115
|
+
configProvider.setGhostConfig('owner', createEnabledGhostConfig({ default_public_trust: 0.5 }));
|
|
116
|
+
const memory = createMemory({ user_id: 'owner', trust: 1.0 });
|
|
117
|
+
const result = await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
118
|
+
expect(result.status).toBe('insufficient_trust');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('grants access when trust is sufficient', async () => {
|
|
122
|
+
configProvider.setGhostConfig('owner', createEnabledGhostConfig({ default_public_trust: 0.75 }));
|
|
123
|
+
const memory = createMemory({ user_id: 'owner', trust: 0.5 });
|
|
124
|
+
const result = await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
125
|
+
expect(result.status).toBe('granted');
|
|
126
|
+
if (result.status === 'granted') {
|
|
127
|
+
expect(result.access_level).toBe('trusted');
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('returns insufficient_trust when trust is too low', async () => {
|
|
132
|
+
configProvider.setGhostConfig('owner', createEnabledGhostConfig({ default_public_trust: 0.25 }));
|
|
133
|
+
const memory = createMemory({ user_id: 'owner', trust: 0.75 });
|
|
134
|
+
const result = await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
135
|
+
expect(result.status).toBe('insufficient_trust');
|
|
136
|
+
if (result.status === 'insufficient_trust') {
|
|
137
|
+
expect(result.required_trust).toBe(0.75);
|
|
138
|
+
expect(result.actual_trust).toBe(0.15); // 0.25 - 0.1 penalty
|
|
139
|
+
expect(result.attempts_remaining).toBe(2);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('blocks after 3 failed attempts', async () => {
|
|
144
|
+
configProvider.setGhostConfig('owner', createEnabledGhostConfig({ default_public_trust: 0.1 }));
|
|
145
|
+
const memory = createMemory({ user_id: 'owner', trust: 0.75 });
|
|
146
|
+
|
|
147
|
+
// Attempt 1
|
|
148
|
+
const r1 = await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
149
|
+
expect(r1.status).toBe('insufficient_trust');
|
|
150
|
+
if (r1.status === 'insufficient_trust') expect(r1.attempts_remaining).toBe(2);
|
|
151
|
+
|
|
152
|
+
// Attempt 2
|
|
153
|
+
const r2 = await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
154
|
+
expect(r2.status).toBe('insufficient_trust');
|
|
155
|
+
if (r2.status === 'insufficient_trust') expect(r2.attempts_remaining).toBe(1);
|
|
156
|
+
|
|
157
|
+
// Attempt 3 — should block
|
|
158
|
+
const r3 = await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
159
|
+
expect(r3.status).toBe('blocked');
|
|
160
|
+
if (r3.status === 'blocked') {
|
|
161
|
+
expect(r3.reason).toContain('3 unauthorized attempts');
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('returns blocked on subsequent accesses after block', async () => {
|
|
166
|
+
configProvider.setGhostConfig('owner', createEnabledGhostConfig({ default_public_trust: 0.1 }));
|
|
167
|
+
const memory = createMemory({ user_id: 'owner', trust: 0.75 });
|
|
168
|
+
|
|
169
|
+
// Trigger block
|
|
170
|
+
for (let i = 0; i < 3; i++) {
|
|
171
|
+
await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 4th attempt — already blocked
|
|
175
|
+
const result = await checkMemoryAccess('accessor', memory, configProvider, escalationStore);
|
|
176
|
+
expect(result.status).toBe('blocked');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('uses per_user_trust override', async () => {
|
|
180
|
+
configProvider.setGhostConfig('owner', createEnabledGhostConfig({
|
|
181
|
+
default_public_trust: 0.1,
|
|
182
|
+
per_user_trust: { 'trusted-friend': 0.9 },
|
|
183
|
+
}));
|
|
184
|
+
const memory = createMemory({ user_id: 'owner', trust: 0.75 });
|
|
185
|
+
const result = await checkMemoryAccess('trusted-friend', memory, configProvider, escalationStore);
|
|
186
|
+
expect(result.status).toBe('granted');
|
|
187
|
+
if (result.status === 'granted') expect(result.access_level).toBe('trusted');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('block is memory-specific — other memories still accessible', async () => {
|
|
191
|
+
configProvider.setGhostConfig('owner', createEnabledGhostConfig({ default_public_trust: 0.1 }));
|
|
192
|
+
const memHigh = createMemory({ id: 'mem-high', user_id: 'owner', trust: 0.75 });
|
|
193
|
+
const memLow = createMemory({ id: 'mem-low', user_id: 'owner', trust: 0.05 });
|
|
194
|
+
|
|
195
|
+
// Block on high-trust memory
|
|
196
|
+
for (let i = 0; i < 3; i++) {
|
|
197
|
+
await checkMemoryAccess('accessor', memHigh, configProvider, escalationStore);
|
|
198
|
+
}
|
|
199
|
+
expect((await checkMemoryAccess('accessor', memHigh, configProvider, escalationStore)).status).toBe('blocked');
|
|
200
|
+
|
|
201
|
+
// Low-trust memory still accessible
|
|
202
|
+
const lowResult = await checkMemoryAccess('accessor', memLow, configProvider, escalationStore);
|
|
203
|
+
expect(lowResult.status).toBe('granted');
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// ─── resolveAccessorTrustLevel ─────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
describe('resolveAccessorTrustLevel', () => {
|
|
210
|
+
it('returns per_user_trust when set', () => {
|
|
211
|
+
const config = createEnabledGhostConfig({ per_user_trust: { 'alice': 0.9 } });
|
|
212
|
+
expect(resolveAccessorTrustLevel(config, 'alice')).toBe(0.9);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('falls through to default_public_trust when no per_user_trust', () => {
|
|
216
|
+
const config = createEnabledGhostConfig({ default_public_trust: 0.3 });
|
|
217
|
+
expect(resolveAccessorTrustLevel(config, 'stranger')).toBe(0.3);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('returns 0 when default_public_trust not set', () => {
|
|
221
|
+
const config = createEnabledGhostConfig({ default_public_trust: 0 });
|
|
222
|
+
expect(resolveAccessorTrustLevel(config, 'unknown')).toBe(0);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('per_user_trust takes priority over default', () => {
|
|
226
|
+
const config = createEnabledGhostConfig({
|
|
227
|
+
default_public_trust: 0.1,
|
|
228
|
+
per_user_trust: { 'bob': 0.8 },
|
|
229
|
+
});
|
|
230
|
+
expect(resolveAccessorTrustLevel(config, 'bob')).toBe(0.8);
|
|
231
|
+
expect(resolveAccessorTrustLevel(config, 'carol')).toBe(0.1);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('per_user_trust of 0 is used (not falsy fallthrough)', () => {
|
|
235
|
+
const config = createEnabledGhostConfig({
|
|
236
|
+
default_public_trust: 0.5,
|
|
237
|
+
per_user_trust: { 'restricted': 0 },
|
|
238
|
+
});
|
|
239
|
+
expect(resolveAccessorTrustLevel(config, 'restricted')).toBe(0);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// ─── isMemoryBlocked / resetBlock ──────────────────────────────────────────
|
|
244
|
+
|
|
245
|
+
describe('isMemoryBlocked', () => {
|
|
246
|
+
it('returns false when no block exists', async () => {
|
|
247
|
+
const store = new InMemoryEscalationStore();
|
|
248
|
+
expect(await isMemoryBlocked('owner', 'accessor', 'mem-1', store)).toBe(false);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('returns true when block exists', async () => {
|
|
252
|
+
const store = new InMemoryEscalationStore();
|
|
253
|
+
await store.setBlock('owner', 'accessor', 'mem-1', {
|
|
254
|
+
blocked_at: '2026-01-01T00:00:00Z',
|
|
255
|
+
reason: 'test block',
|
|
256
|
+
attempt_count: 3,
|
|
257
|
+
});
|
|
258
|
+
expect(await isMemoryBlocked('owner', 'accessor', 'mem-1', store)).toBe(true);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('resetBlock', () => {
|
|
263
|
+
it('removes an existing block', async () => {
|
|
264
|
+
const store = new InMemoryEscalationStore();
|
|
265
|
+
await store.setBlock('owner', 'accessor', 'mem-1', {
|
|
266
|
+
blocked_at: '2026-01-01T00:00:00Z',
|
|
267
|
+
reason: 'test block',
|
|
268
|
+
attempt_count: 3,
|
|
269
|
+
});
|
|
270
|
+
expect(await isMemoryBlocked('owner', 'accessor', 'mem-1', store)).toBe(true);
|
|
271
|
+
|
|
272
|
+
await resetBlock('owner', 'accessor', 'mem-1', store);
|
|
273
|
+
expect(await isMemoryBlocked('owner', 'accessor', 'mem-1', store)).toBe(false);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('is safe to call when no block exists', async () => {
|
|
277
|
+
const store = new InMemoryEscalationStore();
|
|
278
|
+
await expect(resetBlock('owner', 'accessor', 'mem-1', store)).resolves.toBeUndefined();
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// ─── formatAccessResultMessage ─────────────────────────────────────────────
|
|
283
|
+
|
|
284
|
+
describe('formatAccessResultMessage', () => {
|
|
285
|
+
const memory = createMemory();
|
|
286
|
+
|
|
287
|
+
it('formats granted (owner)', () => {
|
|
288
|
+
const result: AccessResult = { status: 'granted', memory, access_level: 'owner' };
|
|
289
|
+
expect(formatAccessResultMessage(result)).toBe('Access granted (owner).');
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('formats granted (trusted)', () => {
|
|
293
|
+
const result: AccessResult = { status: 'granted', memory, access_level: 'trusted' };
|
|
294
|
+
expect(formatAccessResultMessage(result)).toBe('Access granted (trusted).');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('formats insufficient_trust', () => {
|
|
298
|
+
const result: AccessResult = {
|
|
299
|
+
status: 'insufficient_trust',
|
|
300
|
+
memory_id: 'mem-1',
|
|
301
|
+
required_trust: 0.75,
|
|
302
|
+
actual_trust: 0.15,
|
|
303
|
+
attempts_remaining: 2,
|
|
304
|
+
};
|
|
305
|
+
const msg = formatAccessResultMessage(result);
|
|
306
|
+
expect(msg).toContain('0.75');
|
|
307
|
+
expect(msg).toContain('0.15');
|
|
308
|
+
expect(msg).toContain('2 attempt(s) remaining');
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('formats blocked', () => {
|
|
312
|
+
const result: AccessResult = {
|
|
313
|
+
status: 'blocked',
|
|
314
|
+
memory_id: 'mem-1',
|
|
315
|
+
reason: 'Too many attempts',
|
|
316
|
+
blocked_at: '2026-01-01T00:00:00Z',
|
|
317
|
+
};
|
|
318
|
+
expect(formatAccessResultMessage(result)).toContain('Too many attempts');
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('formats no_permission', () => {
|
|
322
|
+
const result: AccessResult = {
|
|
323
|
+
status: 'no_permission',
|
|
324
|
+
owner_user_id: 'owner',
|
|
325
|
+
accessor_user_id: 'accessor',
|
|
326
|
+
};
|
|
327
|
+
expect(formatAccessResultMessage(result)).toContain('No permission');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('formats not_found', () => {
|
|
331
|
+
const result: AccessResult = { status: 'not_found', memory_id: 'mem-404' };
|
|
332
|
+
expect(formatAccessResultMessage(result)).toContain('mem-404');
|
|
333
|
+
expect(formatAccessResultMessage(result)).toContain('not found');
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('formats deleted', () => {
|
|
337
|
+
const result: AccessResult = {
|
|
338
|
+
status: 'deleted',
|
|
339
|
+
memory_id: 'mem-del',
|
|
340
|
+
deleted_at: '2026-01-10T00:00:00Z',
|
|
341
|
+
};
|
|
342
|
+
expect(formatAccessResultMessage(result)).toContain('mem-del');
|
|
343
|
+
expect(formatAccessResultMessage(result)).toContain('deleted');
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// ─── handleInsufficientTrust ───────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
describe('handleInsufficientTrust', () => {
|
|
350
|
+
it('applies -0.1 trust penalty', async () => {
|
|
351
|
+
const store = new InMemoryEscalationStore();
|
|
352
|
+
const result = await handleInsufficientTrust('owner', 'accessor', 'mem-1', 0.75, 0.25, store);
|
|
353
|
+
expect(result.status).toBe('insufficient_trust');
|
|
354
|
+
if (result.status === 'insufficient_trust') {
|
|
355
|
+
expect(result.actual_trust).toBe(0.15); // 0.25 - 0.1
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('does not let actual_trust go below 0', async () => {
|
|
360
|
+
const store = new InMemoryEscalationStore();
|
|
361
|
+
const result = await handleInsufficientTrust('owner', 'accessor', 'mem-1', 0.75, 0.05, store);
|
|
362
|
+
if (result.status === 'insufficient_trust') {
|
|
363
|
+
expect(result.actual_trust).toBe(0); // max(0, 0.05 - 0.1)
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('blocks after 3 attempts', async () => {
|
|
368
|
+
const store = new InMemoryEscalationStore();
|
|
369
|
+
await handleInsufficientTrust('owner', 'accessor', 'mem-1', 0.75, 0.25, store);
|
|
370
|
+
await handleInsufficientTrust('owner', 'accessor', 'mem-1', 0.75, 0.25, store);
|
|
371
|
+
const result = await handleInsufficientTrust('owner', 'accessor', 'mem-1', 0.75, 0.25, store);
|
|
372
|
+
expect(result.status).toBe('blocked');
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('tracks attempts_remaining correctly', async () => {
|
|
376
|
+
const store = new InMemoryEscalationStore();
|
|
377
|
+
const r1 = await handleInsufficientTrust('owner', 'accessor', 'mem-1', 0.75, 0.25, store);
|
|
378
|
+
if (r1.status === 'insufficient_trust') expect(r1.attempts_remaining).toBe(2);
|
|
379
|
+
|
|
380
|
+
const r2 = await handleInsufficientTrust('owner', 'accessor', 'mem-1', 0.75, 0.25, store);
|
|
381
|
+
if (r2.status === 'insufficient_trust') expect(r2.attempts_remaining).toBe(1);
|
|
382
|
+
});
|
|
383
|
+
});
|