@prmichaelsen/remember-mcp 3.0.0 → 3.13.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 +358 -0
- package/README.md +68 -45
- package/agent/commands/acp.clarification-create.md +382 -0
- package/agent/commands/acp.project-info.md +309 -0
- package/agent/commands/acp.project-remove.md +379 -0
- package/agent/commands/acp.project-update.md +296 -0
- package/agent/commands/acp.task-create.md +17 -9
- package/agent/commands/git.commit.md +13 -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/trust-escalation-prevention.md +9 -2
- package/agent/design/trust-system-implementation.md +12 -3
- package/agent/milestones/milestone-14-memory-collection-v2.md +182 -0
- package/agent/milestones/milestone-15-moderation-space-config.md +126 -0
- package/agent/progress.yaml +628 -49
- package/agent/scripts/acp.common.sh +2 -0
- package/agent/scripts/acp.install.sh +11 -1
- package/agent/scripts/acp.package-install-optimized.sh +454 -0
- package/agent/scripts/acp.package-install.sh +247 -300
- 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-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 +2798 -1029
- package/dist/server.js +2526 -1012
- 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 +2 -1
- package/dist/tools/delete-relationship.d.ts +2 -1
- package/dist/tools/deny.d.ts +2 -1
- package/dist/tools/find-similar.d.ts +2 -1
- 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 +3 -1
- 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 +2 -1
- package/dist/tools/search-relationship.d.ts +2 -1
- 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 +3 -1
- package/dist/types/preferences.d.ts +1 -1
- 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/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 +869 -135
- package/src/tools/create-memory.spec.ts +126 -0
- package/src/tools/create-memory.ts +20 -27
- package/src/tools/create-relationship.ts +17 -8
- package/src/tools/delete-memory.ts +13 -6
- package/src/tools/delete-relationship.ts +15 -6
- package/src/tools/deny.ts +8 -1
- package/src/tools/find-similar.ts +21 -8
- 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 +28 -6
- 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 +30 -7
- package/src/tools/search-relationship.ts +11 -2
- 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 +16 -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 +9 -1
- package/src/types/preferences.ts +2 -2
- 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 +4 -4
- 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 +51 -36
- package/src/weaviate/schema.ts +11 -256
- package/src/weaviate/space-schema.spec.ts +24 -24
- package/src/weaviate/space-schema.ts +18 -6
|
@@ -7,6 +7,8 @@ import type { RelationshipUpdate } from '../types/memory.js';
|
|
|
7
7
|
import { getMemoryCollection } from '../weaviate/schema.js';
|
|
8
8
|
import { logger } from '../utils/logger.js';
|
|
9
9
|
import { handleToolError } from '../utils/error-handler.js';
|
|
10
|
+
import { createDebugLogger } from '../utils/debug.js';
|
|
11
|
+
import type { AuthContext } from '../types/auth.js';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Tool definition for remember_update_relationship
|
|
@@ -89,9 +91,15 @@ export interface UpdateRelationshipResult {
|
|
|
89
91
|
*/
|
|
90
92
|
export async function handleUpdateRelationship(
|
|
91
93
|
args: UpdateRelationshipArgs,
|
|
92
|
-
userId: string
|
|
94
|
+
userId: string,
|
|
95
|
+
authContext?: AuthContext
|
|
93
96
|
): Promise<string> {
|
|
97
|
+
const debug = createDebugLogger({ tool: 'remember_update_relationship', userId, operation: 'update relationship' });
|
|
98
|
+
|
|
94
99
|
try {
|
|
100
|
+
debug.info('Tool invoked');
|
|
101
|
+
debug.trace('Arguments', { args });
|
|
102
|
+
|
|
95
103
|
logger.info('Updating relationship', { userId, relationshipId: args.relationship_id });
|
|
96
104
|
|
|
97
105
|
const collection = getMemoryCollection(userId);
|
|
@@ -184,6 +192,7 @@ export async function handleUpdateRelationship(
|
|
|
184
192
|
|
|
185
193
|
return JSON.stringify(result, null, 2);
|
|
186
194
|
} catch (error) {
|
|
195
|
+
debug.error('Tool failed', { error: error instanceof Error ? error.message : String(error) });
|
|
187
196
|
handleToolError(error, {
|
|
188
197
|
toolName: 'remember_update_relationship',
|
|
189
198
|
operation: 'update relationship',
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AccessResult,
|
|
3
|
+
AccessGranted,
|
|
4
|
+
AccessInsufficientTrust,
|
|
5
|
+
AccessBlocked,
|
|
6
|
+
AccessNoPermission,
|
|
7
|
+
AccessNotFound,
|
|
8
|
+
AccessDeleted,
|
|
9
|
+
AccessResultStatus,
|
|
10
|
+
} from './access-result.js';
|
|
11
|
+
import type { Memory } from './memory.js';
|
|
12
|
+
import { DEFAULT_GHOST_CONFIG, type GhostConfig, type TrustEnforcementMode } from './ghost-config.js';
|
|
13
|
+
|
|
14
|
+
// Minimal Memory fixture for testing type narrowing
|
|
15
|
+
const mockMemory: Memory = {
|
|
16
|
+
id: 'mem-1',
|
|
17
|
+
user_id: 'user-owner',
|
|
18
|
+
doc_type: 'memory',
|
|
19
|
+
content: 'Test memory',
|
|
20
|
+
type: 'note',
|
|
21
|
+
weight: 0.5,
|
|
22
|
+
trust: 0.5,
|
|
23
|
+
location: { gps: null, address: null, source: 'unavailable', confidence: 0, is_approximate: true },
|
|
24
|
+
context: { timestamp: '2026-01-01T00:00:00Z', source: { type: 'manual' } },
|
|
25
|
+
relationships: [],
|
|
26
|
+
access_count: 0,
|
|
27
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
28
|
+
updated_at: '2026-01-01T00:00:00Z',
|
|
29
|
+
version: 1,
|
|
30
|
+
tags: [],
|
|
31
|
+
base_weight: 0.5,
|
|
32
|
+
} as Memory;
|
|
33
|
+
|
|
34
|
+
describe('AccessResult type narrowing', () => {
|
|
35
|
+
it('narrows to AccessGranted', () => {
|
|
36
|
+
const result: AccessResult = {
|
|
37
|
+
status: 'granted',
|
|
38
|
+
memory: mockMemory,
|
|
39
|
+
access_level: 'owner',
|
|
40
|
+
};
|
|
41
|
+
if (result.status === 'granted') {
|
|
42
|
+
expect(result.memory.id).toBe('mem-1');
|
|
43
|
+
expect(result.access_level).toBe('owner');
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('narrows to AccessGranted with trusted level', () => {
|
|
48
|
+
const result: AccessResult = {
|
|
49
|
+
status: 'granted',
|
|
50
|
+
memory: mockMemory,
|
|
51
|
+
access_level: 'trusted',
|
|
52
|
+
};
|
|
53
|
+
if (result.status === 'granted') {
|
|
54
|
+
expect(result.access_level).toBe('trusted');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('narrows to AccessInsufficientTrust', () => {
|
|
59
|
+
const result: AccessResult = {
|
|
60
|
+
status: 'insufficient_trust',
|
|
61
|
+
memory_id: 'mem-1',
|
|
62
|
+
required_trust: 0.75,
|
|
63
|
+
actual_trust: 0.25,
|
|
64
|
+
attempts_remaining: 2,
|
|
65
|
+
};
|
|
66
|
+
if (result.status === 'insufficient_trust') {
|
|
67
|
+
expect(result.required_trust).toBe(0.75);
|
|
68
|
+
expect(result.actual_trust).toBe(0.25);
|
|
69
|
+
expect(result.attempts_remaining).toBe(2);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('narrows to AccessBlocked', () => {
|
|
74
|
+
const result: AccessResult = {
|
|
75
|
+
status: 'blocked',
|
|
76
|
+
memory_id: 'mem-1',
|
|
77
|
+
reason: 'Repeated unauthorized attempts',
|
|
78
|
+
blocked_at: '2026-01-15T10:00:00Z',
|
|
79
|
+
};
|
|
80
|
+
if (result.status === 'blocked') {
|
|
81
|
+
expect(result.reason).toBe('Repeated unauthorized attempts');
|
|
82
|
+
expect(result.blocked_at).toBe('2026-01-15T10:00:00Z');
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('narrows to AccessNoPermission', () => {
|
|
87
|
+
const result: AccessResult = {
|
|
88
|
+
status: 'no_permission',
|
|
89
|
+
owner_user_id: 'user-owner',
|
|
90
|
+
accessor_user_id: 'user-accessor',
|
|
91
|
+
};
|
|
92
|
+
if (result.status === 'no_permission') {
|
|
93
|
+
expect(result.owner_user_id).toBe('user-owner');
|
|
94
|
+
expect(result.accessor_user_id).toBe('user-accessor');
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('narrows to AccessNotFound', () => {
|
|
99
|
+
const result: AccessResult = {
|
|
100
|
+
status: 'not_found',
|
|
101
|
+
memory_id: 'mem-missing',
|
|
102
|
+
};
|
|
103
|
+
if (result.status === 'not_found') {
|
|
104
|
+
expect(result.memory_id).toBe('mem-missing');
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('narrows to AccessDeleted', () => {
|
|
109
|
+
const result: AccessResult = {
|
|
110
|
+
status: 'deleted',
|
|
111
|
+
memory_id: 'mem-deleted',
|
|
112
|
+
deleted_at: '2026-01-10T08:00:00Z',
|
|
113
|
+
};
|
|
114
|
+
if (result.status === 'deleted') {
|
|
115
|
+
expect(result.memory_id).toBe('mem-deleted');
|
|
116
|
+
expect(result.deleted_at).toBe('2026-01-10T08:00:00Z');
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('switch statement covers all variants exhaustively', () => {
|
|
121
|
+
const results: AccessResult[] = [
|
|
122
|
+
{ status: 'granted', memory: mockMemory, access_level: 'owner' },
|
|
123
|
+
{ status: 'insufficient_trust', memory_id: 'mem-1', required_trust: 0.5, actual_trust: 0.1, attempts_remaining: 1 },
|
|
124
|
+
{ status: 'blocked', memory_id: 'mem-1', reason: 'blocked', blocked_at: '2026-01-01T00:00:00Z' },
|
|
125
|
+
{ status: 'no_permission', owner_user_id: 'owner', accessor_user_id: 'accessor' },
|
|
126
|
+
{ status: 'not_found', memory_id: 'mem-1' },
|
|
127
|
+
{ status: 'deleted', memory_id: 'mem-1', deleted_at: '2026-01-01T00:00:00Z' },
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const statuses: AccessResultStatus[] = [];
|
|
131
|
+
for (const result of results) {
|
|
132
|
+
switch (result.status) {
|
|
133
|
+
case 'granted':
|
|
134
|
+
statuses.push(result.status);
|
|
135
|
+
break;
|
|
136
|
+
case 'insufficient_trust':
|
|
137
|
+
statuses.push(result.status);
|
|
138
|
+
break;
|
|
139
|
+
case 'blocked':
|
|
140
|
+
statuses.push(result.status);
|
|
141
|
+
break;
|
|
142
|
+
case 'no_permission':
|
|
143
|
+
statuses.push(result.status);
|
|
144
|
+
break;
|
|
145
|
+
case 'not_found':
|
|
146
|
+
statuses.push(result.status);
|
|
147
|
+
break;
|
|
148
|
+
case 'deleted':
|
|
149
|
+
statuses.push(result.status);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
expect(statuses).toEqual([
|
|
155
|
+
'granted',
|
|
156
|
+
'insufficient_trust',
|
|
157
|
+
'blocked',
|
|
158
|
+
'no_permission',
|
|
159
|
+
'not_found',
|
|
160
|
+
'deleted',
|
|
161
|
+
]);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('GhostConfig defaults', () => {
|
|
166
|
+
it('has correct default values', () => {
|
|
167
|
+
expect(DEFAULT_GHOST_CONFIG.enabled).toBe(false);
|
|
168
|
+
expect(DEFAULT_GHOST_CONFIG.public_ghost_enabled).toBe(false);
|
|
169
|
+
expect(DEFAULT_GHOST_CONFIG.default_friend_trust).toBe(0.25);
|
|
170
|
+
expect(DEFAULT_GHOST_CONFIG.default_public_trust).toBe(0);
|
|
171
|
+
expect(DEFAULT_GHOST_CONFIG.per_user_trust).toEqual({});
|
|
172
|
+
expect(DEFAULT_GHOST_CONFIG.blocked_users).toEqual([]);
|
|
173
|
+
expect(DEFAULT_GHOST_CONFIG.enforcement_mode).toBe('query');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('GhostConfig accepts valid per_user_trust overrides', () => {
|
|
177
|
+
const config: GhostConfig = {
|
|
178
|
+
...DEFAULT_GHOST_CONFIG,
|
|
179
|
+
enabled: true,
|
|
180
|
+
per_user_trust: { 'user-friend': 0.75, 'user-close': 1.0 },
|
|
181
|
+
};
|
|
182
|
+
expect(config.per_user_trust['user-friend']).toBe(0.75);
|
|
183
|
+
expect(config.per_user_trust['user-close']).toBe(1.0);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('GhostConfig accepts all enforcement modes', () => {
|
|
187
|
+
const modes: TrustEnforcementMode[] = ['query', 'prompt', 'hybrid'];
|
|
188
|
+
for (const mode of modes) {
|
|
189
|
+
const config: GhostConfig = { ...DEFAULT_GHOST_CONFIG, enforcement_mode: mode };
|
|
190
|
+
expect(config.enforcement_mode).toBe(mode);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discriminated union for type-safe access control responses.
|
|
3
|
+
* See agent/design/access-control-result-pattern.md
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Memory } from './memory.js';
|
|
7
|
+
|
|
8
|
+
/** Access granted — owner or trusted cross-user access */
|
|
9
|
+
export interface AccessGranted {
|
|
10
|
+
status: 'granted';
|
|
11
|
+
memory: Memory;
|
|
12
|
+
access_level: 'owner' | 'trusted';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Trust level insufficient for this memory */
|
|
16
|
+
export interface AccessInsufficientTrust {
|
|
17
|
+
status: 'insufficient_trust';
|
|
18
|
+
memory_id: string;
|
|
19
|
+
required_trust: number;
|
|
20
|
+
actual_trust: number;
|
|
21
|
+
attempts_remaining: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Access blocked after repeated unauthorized attempts */
|
|
25
|
+
export interface AccessBlocked {
|
|
26
|
+
status: 'blocked';
|
|
27
|
+
memory_id: string;
|
|
28
|
+
reason: string;
|
|
29
|
+
blocked_at: string; // ISO 8601
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** No ghost/permission relationship exists */
|
|
33
|
+
export interface AccessNoPermission {
|
|
34
|
+
status: 'no_permission';
|
|
35
|
+
owner_user_id: string;
|
|
36
|
+
accessor_user_id: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Memory not found */
|
|
40
|
+
export interface AccessNotFound {
|
|
41
|
+
status: 'not_found';
|
|
42
|
+
memory_id: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Memory was deleted */
|
|
46
|
+
export interface AccessDeleted {
|
|
47
|
+
status: 'deleted';
|
|
48
|
+
memory_id: string;
|
|
49
|
+
deleted_at: string; // ISO 8601
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Discriminated union of all access result variants */
|
|
53
|
+
export type AccessResult =
|
|
54
|
+
| AccessGranted
|
|
55
|
+
| AccessInsufficientTrust
|
|
56
|
+
| AccessBlocked
|
|
57
|
+
| AccessNoPermission
|
|
58
|
+
| AccessNotFound
|
|
59
|
+
| AccessDeleted;
|
|
60
|
+
|
|
61
|
+
/** All possible access result statuses */
|
|
62
|
+
export type AccessResultStatus = AccessResult['status'];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth types for group membership validation and access control.
|
|
3
|
+
*
|
|
4
|
+
* These types define the contract for credentials providers and
|
|
5
|
+
* the AuthContext threaded through tool handlers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface GroupPermissions {
|
|
9
|
+
can_read: boolean;
|
|
10
|
+
can_publish: boolean;
|
|
11
|
+
can_revise: boolean;
|
|
12
|
+
can_propose: boolean;
|
|
13
|
+
can_overwrite: boolean;
|
|
14
|
+
can_comment: boolean;
|
|
15
|
+
can_retract_own: boolean;
|
|
16
|
+
can_retract_any: boolean;
|
|
17
|
+
can_manage_members: boolean;
|
|
18
|
+
can_moderate: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface GroupMembership {
|
|
22
|
+
group_id: string;
|
|
23
|
+
permissions: GroupPermissions;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface UserCredentials {
|
|
27
|
+
user_id: string;
|
|
28
|
+
group_memberships: GroupMembership[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Ghost conversation context — resolved server-side, never from tool args */
|
|
32
|
+
export interface GhostModeContext {
|
|
33
|
+
/** The ghost owner's user ID (whose memories are being searched) */
|
|
34
|
+
owner_user_id: string;
|
|
35
|
+
/** The accessor's user ID (who is chatting with the ghost) */
|
|
36
|
+
accessor_user_id: string;
|
|
37
|
+
/** Resolved trust level (looked up from GhostConfig, not user-supplied) */
|
|
38
|
+
accessor_trust_level: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AuthContext {
|
|
42
|
+
accessToken: string | null;
|
|
43
|
+
credentials: UserCredentials | null;
|
|
44
|
+
/** Present when the server is running in ghost conversation mode */
|
|
45
|
+
ghostMode?: GhostModeContext;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type WriteMode = 'owner_only' | 'group_editors' | 'anyone';
|
|
49
|
+
|
|
50
|
+
export interface CredentialsProvider {
|
|
51
|
+
getCredentials(accessToken: string, userId: string): Promise<UserCredentials>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost/persona configuration types.
|
|
3
|
+
* See agent/design/local.ghost-persona-system.md
|
|
4
|
+
*
|
|
5
|
+
* GhostConfig is stored in Firestore at users/{ownerUserId}/ghost_config.
|
|
6
|
+
* Trust is owned by remember-mcp (not passed per-request) to prevent
|
|
7
|
+
* prompt injection tampering.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Trust enforcement mode — how memories are filtered for cross-user access */
|
|
11
|
+
export type TrustEnforcementMode = 'query' | 'prompt' | 'hybrid';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Per-user ghost configuration stored in Firestore.
|
|
15
|
+
*
|
|
16
|
+
* - query mode (default): memories above threshold never returned from Weaviate
|
|
17
|
+
* - prompt mode: all memories returned, formatted/redacted by trust level
|
|
18
|
+
* - hybrid mode: query filter for trust 0.0, prompt filter for rest
|
|
19
|
+
*/
|
|
20
|
+
export interface GhostConfig {
|
|
21
|
+
/** Whether ghost conversations are enabled for this user (default: false) */
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
/** Allow non-friends (strangers) to initiate ghost conversations (default: false) */
|
|
24
|
+
public_ghost_enabled: boolean;
|
|
25
|
+
/** Default trust level for friends (default: 0.25 — metadata only) */
|
|
26
|
+
default_friend_trust: number;
|
|
27
|
+
/** Default trust level for strangers (default: 0 — existence only) */
|
|
28
|
+
default_public_trust: number;
|
|
29
|
+
/** Per-user trust level overrides: userId → trust level (0-1) */
|
|
30
|
+
per_user_trust: Record<string, number>;
|
|
31
|
+
/** Users blocked from ghost access entirely */
|
|
32
|
+
blocked_users: string[];
|
|
33
|
+
/** How trust is enforced when filtering memories (default: 'query') */
|
|
34
|
+
enforcement_mode: TrustEnforcementMode;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Default GhostConfig values for a new user */
|
|
38
|
+
export const DEFAULT_GHOST_CONFIG: GhostConfig = {
|
|
39
|
+
enabled: false,
|
|
40
|
+
public_ghost_enabled: false,
|
|
41
|
+
default_friend_trust: 0.25,
|
|
42
|
+
default_public_trust: 0,
|
|
43
|
+
per_user_trust: {},
|
|
44
|
+
blocked_users: [],
|
|
45
|
+
enforcement_mode: 'query',
|
|
46
|
+
};
|
package/src/types/memory.ts
CHANGED
|
@@ -56,7 +56,10 @@ export type ContentType =
|
|
|
56
56
|
| 'system'
|
|
57
57
|
| 'action'
|
|
58
58
|
| 'audit'
|
|
59
|
-
| 'history'
|
|
59
|
+
| 'history'
|
|
60
|
+
// Cross-user & Threading
|
|
61
|
+
| 'ghost'
|
|
62
|
+
| 'comment';
|
|
60
63
|
|
|
61
64
|
/**
|
|
62
65
|
* GPS coordinates
|
|
@@ -201,6 +204,11 @@ export interface Memory {
|
|
|
201
204
|
deleted_at?: Date | null; // Timestamp when memory was soft-deleted (null = not deleted)
|
|
202
205
|
deleted_by?: string; // User ID who deleted the memory
|
|
203
206
|
deletion_reason?: string; // Optional reason for deletion
|
|
207
|
+
|
|
208
|
+
// Publication Tracking (Memory Collection Pattern v2)
|
|
209
|
+
// Managed by remember_publish / remember_retract — do NOT modify directly
|
|
210
|
+
space_ids?: string[]; // Spaces this memory has been published to
|
|
211
|
+
group_ids?: string[]; // Groups this memory has been published to
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
/**
|
package/src/types/preferences.ts
CHANGED
|
@@ -109,7 +109,7 @@ export const DEFAULT_PREFERENCES: Omit<UserPreferences, 'user_id' | 'created_at'
|
|
|
109
109
|
share_with_memories: true,
|
|
110
110
|
},
|
|
111
111
|
privacy: {
|
|
112
|
-
default_trust_level: 0.
|
|
112
|
+
default_trust_level: 0.25,
|
|
113
113
|
allow_cross_user_access: false,
|
|
114
114
|
auto_approve_requests: false,
|
|
115
115
|
audit_logging: true,
|
|
@@ -169,7 +169,7 @@ export const PREFERENCE_DESCRIPTIONS = {
|
|
|
169
169
|
share_with_memories: 'Include location data in memories (default: true)',
|
|
170
170
|
},
|
|
171
171
|
privacy: {
|
|
172
|
-
default_trust_level: 'Default trust level for new memories, 0-1 (default: 0.
|
|
172
|
+
default_trust_level: 'Default trust level for new memories, 0-1 (default: 0.25)',
|
|
173
173
|
allow_cross_user_access: 'Allow other users to request access to memories (default: false)',
|
|
174
174
|
auto_approve_requests: 'Automatically approve access requests (default: false)',
|
|
175
175
|
audit_logging: 'Enable audit logging for preference changes (default: true)',
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { canModerate, canModerateAny } from './auth-helpers.js';
|
|
2
|
+
import type { AuthContext, GroupPermissions } from '../types/auth.js';
|
|
3
|
+
|
|
4
|
+
const BASE_PERMISSIONS: GroupPermissions = {
|
|
5
|
+
can_read: true,
|
|
6
|
+
can_publish: true,
|
|
7
|
+
can_revise: false,
|
|
8
|
+
can_propose: false,
|
|
9
|
+
can_overwrite: false,
|
|
10
|
+
can_comment: true,
|
|
11
|
+
can_retract_own: true,
|
|
12
|
+
can_retract_any: false,
|
|
13
|
+
can_manage_members: false,
|
|
14
|
+
can_moderate: false,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function makeAuthContext(groups: Array<{ group_id: string; can_moderate: boolean }>): AuthContext {
|
|
18
|
+
return {
|
|
19
|
+
accessToken: 'tok',
|
|
20
|
+
credentials: {
|
|
21
|
+
user_id: 'user-1',
|
|
22
|
+
group_memberships: groups.map(g => ({
|
|
23
|
+
group_id: g.group_id,
|
|
24
|
+
permissions: { ...BASE_PERMISSIONS, can_moderate: g.can_moderate },
|
|
25
|
+
})),
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('canModerate', () => {
|
|
31
|
+
it('returns false for undefined authContext', () => {
|
|
32
|
+
expect(canModerate(undefined, 'group-1')).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('returns false for null credentials', () => {
|
|
36
|
+
expect(canModerate({ accessToken: null, credentials: null }, 'group-1')).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('returns false when user is not a member of the group', () => {
|
|
40
|
+
const auth = makeAuthContext([{ group_id: 'other-group', can_moderate: true }]);
|
|
41
|
+
expect(canModerate(auth, 'group-1')).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('returns false when user is a member but not a moderator', () => {
|
|
45
|
+
const auth = makeAuthContext([{ group_id: 'group-1', can_moderate: false }]);
|
|
46
|
+
expect(canModerate(auth, 'group-1')).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('returns true when user has can_moderate on the group', () => {
|
|
50
|
+
const auth = makeAuthContext([{ group_id: 'group-1', can_moderate: true }]);
|
|
51
|
+
expect(canModerate(auth, 'group-1')).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('canModerateAny', () => {
|
|
56
|
+
it('returns false for undefined authContext', () => {
|
|
57
|
+
expect(canModerateAny(undefined)).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('returns false when no group has can_moderate', () => {
|
|
61
|
+
const auth = makeAuthContext([
|
|
62
|
+
{ group_id: 'g1', can_moderate: false },
|
|
63
|
+
{ group_id: 'g2', can_moderate: false },
|
|
64
|
+
]);
|
|
65
|
+
expect(canModerateAny(auth)).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('returns true when any group has can_moderate', () => {
|
|
69
|
+
const auth = makeAuthContext([
|
|
70
|
+
{ group_id: 'g1', can_moderate: false },
|
|
71
|
+
{ group_id: 'g2', can_moderate: true },
|
|
72
|
+
]);
|
|
73
|
+
expect(canModerateAny(auth)).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth helper utilities for permission checking.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { AuthContext } from '../types/auth.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check if the user has can_moderate permission for a specific group.
|
|
9
|
+
*/
|
|
10
|
+
export function canModerate(authContext: AuthContext | undefined, groupId: string): boolean {
|
|
11
|
+
if (!authContext?.credentials) return false;
|
|
12
|
+
const membership = authContext.credentials.group_memberships
|
|
13
|
+
.find(m => m.group_id === groupId);
|
|
14
|
+
return membership?.permissions.can_moderate ?? false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if the user has can_moderate permission for ANY group.
|
|
19
|
+
* Useful for space-level moderation where there's no specific group context.
|
|
20
|
+
*/
|
|
21
|
+
export function canModerateAny(authContext: AuthContext | undefined): boolean {
|
|
22
|
+
if (!authContext?.credentials) return false;
|
|
23
|
+
return authContext.credentials.group_memberships
|
|
24
|
+
.some(m => m.permissions.can_moderate);
|
|
25
|
+
}
|