@prmichaelsen/remember-mcp 2.2.1 → 2.3.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 +4 -4
- package/CHANGELOG.md +45 -0
- package/README.md +43 -3
- package/agent/commands/acp.init.md +376 -0
- package/agent/commands/acp.proceed.md +311 -0
- package/agent/commands/acp.status.md +280 -0
- package/agent/commands/acp.version-check-for-updates.md +275 -0
- package/agent/commands/acp.version-check.md +190 -0
- package/agent/commands/acp.version-update.md +288 -0
- package/agent/commands/command.template.md +273 -0
- package/agent/design/core-memory-user-profile.md +1253 -0
- package/agent/design/ghost-profiles-pseudonymous-identity.md +194 -0
- package/agent/design/publish-tools-confirmation-flow.md +922 -0
- package/agent/milestones/milestone-10-shared-spaces.md +169 -0
- package/agent/progress.yaml +90 -4
- package/agent/scripts/install.sh +118 -0
- package/agent/scripts/update.sh +22 -10
- package/agent/scripts/version.sh +35 -0
- package/agent/tasks/task-27-implement-llm-provider-interface.md +51 -0
- package/agent/tasks/task-28-implement-llm-provider-factory.md +64 -0
- package/agent/tasks/task-29-update-config-for-llm.md +71 -0
- package/agent/tasks/task-30-implement-bedrock-provider.md +147 -0
- package/agent/tasks/task-31-implement-background-job-service.md +120 -0
- package/agent/tasks/task-32-test-llm-provider-integration.md +152 -0
- package/agent/tasks/task-34-create-confirmation-token-service.md +191 -0
- package/agent/tasks/task-35-create-space-memory-types-schema.md +183 -0
- package/agent/tasks/task-36-implement-remember-publish.md +227 -0
- package/agent/tasks/task-37-implement-remember-confirm.md +225 -0
- package/agent/tasks/task-38-implement-remember-deny.md +161 -0
- package/agent/tasks/task-39-implement-remember-search-space.md +188 -0
- package/agent/tasks/task-40-implement-remember-query-space.md +193 -0
- package/agent/tasks/task-41-configure-firestore-ttl.md +188 -0
- package/agent/tasks/task-42-create-tests-shared-spaces.md +216 -0
- package/agent/tasks/task-43-update-documentation.md +255 -0
- package/dist/llm/types.d.ts +1 -0
- package/dist/server-factory.js +914 -1
- package/dist/server.js +916 -3
- package/dist/services/confirmation-token.service.d.ts +99 -0
- package/dist/services/confirmation-token.service.spec.d.ts +5 -0
- package/dist/tools/confirm.d.ts +20 -0
- package/dist/tools/deny.d.ts +19 -0
- package/dist/tools/publish.d.ts +22 -0
- package/dist/tools/query-space.d.ts +28 -0
- package/dist/tools/search-space.d.ts +29 -0
- package/dist/types/space-memory.d.ts +80 -0
- package/dist/weaviate/space-schema.d.ts +59 -0
- package/dist/weaviate/space-schema.spec.d.ts +5 -0
- package/package.json +1 -1
- package/src/llm/types.ts +0 -0
- package/src/server-factory.ts +33 -0
- package/src/server.ts +33 -0
- package/src/services/confirmation-token.service.spec.ts +254 -0
- package/src/services/confirmation-token.service.ts +232 -0
- package/src/tools/confirm.ts +176 -0
- package/src/tools/deny.ts +70 -0
- package/src/tools/publish.ts +167 -0
- package/src/tools/query-space.ts +197 -0
- package/src/tools/search-space.ts +189 -0
- package/src/types/space-memory.ts +94 -0
- package/src/weaviate/space-schema.spec.ts +131 -0
- package/src/weaviate/space-schema.ts +275 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Space Schema utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
getSpaceCollectionName,
|
|
7
|
+
sanitizeSpaceId,
|
|
8
|
+
getSpaceDisplayName,
|
|
9
|
+
isValidSpaceId,
|
|
10
|
+
ensureSpaceCollection,
|
|
11
|
+
} from './space-schema';
|
|
12
|
+
import type { WeaviateClient } from 'weaviate-client';
|
|
13
|
+
|
|
14
|
+
// Mock Weaviate client
|
|
15
|
+
const mockWeaviateClient = {
|
|
16
|
+
collections: {
|
|
17
|
+
exists: jest.fn(),
|
|
18
|
+
create: jest.fn(),
|
|
19
|
+
get: jest.fn(),
|
|
20
|
+
},
|
|
21
|
+
} as unknown as WeaviateClient;
|
|
22
|
+
|
|
23
|
+
describe('Space Schema Utilities', () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
jest.clearAllMocks();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('getSpaceCollectionName', () => {
|
|
29
|
+
it('should return collection name with Memory_ prefix', () => {
|
|
30
|
+
expect(getSpaceCollectionName('the_void')).toBe('Memory_the_void');
|
|
31
|
+
expect(getSpaceCollectionName('public_space')).toBe('Memory_public_space');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('sanitizeSpaceId', () => {
|
|
36
|
+
it('should convert display name to snake_case', () => {
|
|
37
|
+
expect(sanitizeSpaceId('The Void')).toBe('the_void');
|
|
38
|
+
expect(sanitizeSpaceId('Public Space')).toBe('public_space');
|
|
39
|
+
expect(sanitizeSpaceId('MY SPACE')).toBe('my_space');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should handle multiple spaces', () => {
|
|
43
|
+
expect(sanitizeSpaceId('The Void Space')).toBe('the_void_space');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should handle already lowercase', () => {
|
|
47
|
+
expect(sanitizeSpaceId('the_void')).toBe('the_void');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('getSpaceDisplayName', () => {
|
|
52
|
+
it('should return display name for known space IDs', () => {
|
|
53
|
+
expect(getSpaceDisplayName('the_void')).toBe('The Void');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should return space ID if not found in map', () => {
|
|
57
|
+
expect(getSpaceDisplayName('unknown_space')).toBe('unknown_space');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('isValidSpaceId', () => {
|
|
62
|
+
it('should return true for supported spaces', () => {
|
|
63
|
+
expect(isValidSpaceId('the_void')).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should return false for unsupported spaces', () => {
|
|
67
|
+
expect(isValidSpaceId('unknown_space')).toBe(false);
|
|
68
|
+
expect(isValidSpaceId('public_space')).toBe(false); // Not yet supported
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('ensureSpaceCollection', () => {
|
|
73
|
+
it('should return existing collection if it exists', async () => {
|
|
74
|
+
const mockCollection = { name: 'Memory_the_void' };
|
|
75
|
+
(mockWeaviateClient.collections.exists as jest.Mock).mockResolvedValue(true);
|
|
76
|
+
(mockWeaviateClient.collections.get as jest.Mock).mockReturnValue(mockCollection);
|
|
77
|
+
|
|
78
|
+
const result = await ensureSpaceCollection(mockWeaviateClient, 'the_void');
|
|
79
|
+
|
|
80
|
+
expect(result).toBe(mockCollection);
|
|
81
|
+
expect(mockWeaviateClient.collections.exists).toHaveBeenCalledWith('Memory_the_void');
|
|
82
|
+
expect(mockWeaviateClient.collections.get).toHaveBeenCalledWith('Memory_the_void');
|
|
83
|
+
expect(mockWeaviateClient.collections.create).not.toHaveBeenCalled();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should create collection if it does not exist', async () => {
|
|
87
|
+
const mockCollection = { name: 'Memory_the_void' };
|
|
88
|
+
(mockWeaviateClient.collections.exists as jest.Mock).mockResolvedValue(false);
|
|
89
|
+
(mockWeaviateClient.collections.create as jest.Mock).mockResolvedValue(undefined);
|
|
90
|
+
(mockWeaviateClient.collections.get as jest.Mock).mockReturnValue(mockCollection);
|
|
91
|
+
|
|
92
|
+
const result = await ensureSpaceCollection(mockWeaviateClient, 'the_void');
|
|
93
|
+
|
|
94
|
+
expect(result).toBe(mockCollection);
|
|
95
|
+
expect(mockWeaviateClient.collections.exists).toHaveBeenCalledWith('Memory_the_void');
|
|
96
|
+
expect(mockWeaviateClient.collections.create).toHaveBeenCalled();
|
|
97
|
+
expect(mockWeaviateClient.collections.get).toHaveBeenCalledWith('Memory_the_void');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should throw error for invalid space ID', async () => {
|
|
101
|
+
await expect(
|
|
102
|
+
ensureSpaceCollection(mockWeaviateClient, 'invalid_space')
|
|
103
|
+
).rejects.toThrow('Invalid space ID: invalid_space');
|
|
104
|
+
|
|
105
|
+
expect(mockWeaviateClient.collections.exists).not.toHaveBeenCalled();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should create collection with correct schema', async () => {
|
|
109
|
+
(mockWeaviateClient.collections.exists as jest.Mock).mockResolvedValue(false);
|
|
110
|
+
(mockWeaviateClient.collections.create as jest.Mock).mockResolvedValue(undefined);
|
|
111
|
+
(mockWeaviateClient.collections.get as jest.Mock).mockReturnValue({});
|
|
112
|
+
|
|
113
|
+
await ensureSpaceCollection(mockWeaviateClient, 'the_void');
|
|
114
|
+
|
|
115
|
+
const createCall = (mockWeaviateClient.collections.create as jest.Mock).mock.calls[0][0];
|
|
116
|
+
expect(createCall.name).toBe('Memory_the_void');
|
|
117
|
+
expect(createCall.vectorizers).toBeDefined();
|
|
118
|
+
expect(createCall.properties).toBeDefined();
|
|
119
|
+
expect(createCall.properties.length).toBeGreaterThan(20); // Should have many properties
|
|
120
|
+
|
|
121
|
+
// Check for space-specific properties
|
|
122
|
+
const propertyNames = createCall.properties.map((p: any) => p.name);
|
|
123
|
+
expect(propertyNames).toContain('space_id');
|
|
124
|
+
expect(propertyNames).toContain('author_id');
|
|
125
|
+
expect(propertyNames).toContain('ghost_id');
|
|
126
|
+
expect(propertyNames).toContain('published_at');
|
|
127
|
+
expect(propertyNames).toContain('discovery_count');
|
|
128
|
+
expect(propertyNames).toContain('attribution');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weaviate space collection schema and utilities
|
|
3
|
+
*
|
|
4
|
+
* Manages shared space collections where users can publish memories
|
|
5
|
+
* for discovery by other users.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import weaviate, { type WeaviateClient, type Collection } from 'weaviate-client';
|
|
9
|
+
import { config } from '../config.js';
|
|
10
|
+
import { SUPPORTED_SPACES, type SpaceId } from '../types/space-memory.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get collection name for a space
|
|
14
|
+
*
|
|
15
|
+
* @param spaceId - Space identifier (snake_case)
|
|
16
|
+
* @returns Collection name in format Memory_{space_id}
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* getSpaceCollectionName('the_void') // Returns 'Memory_the_void'
|
|
20
|
+
*/
|
|
21
|
+
export function getSpaceCollectionName(spaceId: string): string {
|
|
22
|
+
return `Memory_${spaceId}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Sanitize display name to space ID
|
|
27
|
+
*
|
|
28
|
+
* Converts display names like "The Void" to snake_case IDs like "the_void"
|
|
29
|
+
*
|
|
30
|
+
* @param displayName - Display name with spaces and mixed case
|
|
31
|
+
* @returns snake_case space ID
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* sanitizeSpaceId('The Void') // Returns 'the_void'
|
|
35
|
+
* sanitizeSpaceId('Public Space') // Returns 'public_space'
|
|
36
|
+
*/
|
|
37
|
+
export function sanitizeSpaceId(displayName: string): string {
|
|
38
|
+
return displayName.toLowerCase().replace(/\s+/g, '_');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get display name for a space ID
|
|
43
|
+
*
|
|
44
|
+
* @param spaceId - Space identifier
|
|
45
|
+
* @returns Display name or the space ID if not found
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* getSpaceDisplayName('the_void') // Returns 'The Void'
|
|
49
|
+
*/
|
|
50
|
+
export function getSpaceDisplayName(spaceId: string): string {
|
|
51
|
+
const { SPACE_DISPLAY_NAMES } = require('../types/space-memory.js');
|
|
52
|
+
return SPACE_DISPLAY_NAMES[spaceId as SpaceId] || spaceId;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Validate space ID
|
|
57
|
+
*
|
|
58
|
+
* @param spaceId - Space identifier to validate
|
|
59
|
+
* @returns True if valid, false otherwise
|
|
60
|
+
*/
|
|
61
|
+
export function isValidSpaceId(spaceId: string): boolean {
|
|
62
|
+
return SUPPORTED_SPACES.includes(spaceId as SpaceId);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create a space collection with schema
|
|
67
|
+
*
|
|
68
|
+
* @param client - Weaviate client
|
|
69
|
+
* @param spaceId - Space identifier
|
|
70
|
+
*/
|
|
71
|
+
async function createSpaceCollection(
|
|
72
|
+
client: WeaviateClient,
|
|
73
|
+
spaceId: string
|
|
74
|
+
): Promise<void> {
|
|
75
|
+
const collectionName = getSpaceCollectionName(spaceId);
|
|
76
|
+
|
|
77
|
+
console.log(`[Weaviate] Creating space collection ${collectionName}...`);
|
|
78
|
+
|
|
79
|
+
// Create collection with schema (same as Memory schema but for spaces)
|
|
80
|
+
await client.collections.create({
|
|
81
|
+
name: collectionName,
|
|
82
|
+
|
|
83
|
+
// Vectorizer configuration
|
|
84
|
+
vectorizers: weaviate.configure.vectorizer.text2VecOpenAI({
|
|
85
|
+
model: 'text-embedding-3-small',
|
|
86
|
+
// Vectorize content for semantic search
|
|
87
|
+
sourceProperties: ['content', 'observation'],
|
|
88
|
+
}),
|
|
89
|
+
|
|
90
|
+
properties: [
|
|
91
|
+
// Discriminator
|
|
92
|
+
{
|
|
93
|
+
name: 'doc_type',
|
|
94
|
+
dataType: 'text' as any,
|
|
95
|
+
description: 'Document type: "space_memory"',
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// Space identity
|
|
99
|
+
{
|
|
100
|
+
name: 'space_id',
|
|
101
|
+
dataType: 'text' as any,
|
|
102
|
+
description: 'Space identifier (e.g., "the_void")',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'author_id',
|
|
106
|
+
dataType: 'text' as any,
|
|
107
|
+
description: 'Original author user_id (for permissions)',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'ghost_id',
|
|
111
|
+
dataType: 'text' as any,
|
|
112
|
+
description: 'Optional ghost profile ID for pseudonymous publishing',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'attribution',
|
|
116
|
+
dataType: 'text' as any,
|
|
117
|
+
description: 'Attribution type: "user" or "ghost"',
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// Discovery metadata
|
|
121
|
+
{
|
|
122
|
+
name: 'published_at',
|
|
123
|
+
dataType: 'text' as any,
|
|
124
|
+
description: 'When published to space (ISO 8601)',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'discovery_count',
|
|
128
|
+
dataType: 'number' as any,
|
|
129
|
+
description: 'How many times discovered',
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// Memory fields (same as personal memories)
|
|
133
|
+
{
|
|
134
|
+
name: 'content',
|
|
135
|
+
dataType: 'text' as any,
|
|
136
|
+
description: 'Main memory content (vectorized)',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'title',
|
|
140
|
+
dataType: 'text' as any,
|
|
141
|
+
description: 'Optional short title',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'summary',
|
|
145
|
+
dataType: 'text' as any,
|
|
146
|
+
description: 'Optional brief summary',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'type',
|
|
150
|
+
dataType: 'text' as any,
|
|
151
|
+
description: 'Content type (note, event, person, etc.)',
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Scoring fields
|
|
155
|
+
{
|
|
156
|
+
name: 'weight',
|
|
157
|
+
dataType: 'number' as any,
|
|
158
|
+
description: 'Significance/priority (0-1)',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'trust',
|
|
162
|
+
dataType: 'number' as any,
|
|
163
|
+
description: 'Access control level (0-1)',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'confidence',
|
|
167
|
+
dataType: 'number' as any,
|
|
168
|
+
description: 'System confidence in accuracy (0-1)',
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
// Location fields (flattened)
|
|
172
|
+
{
|
|
173
|
+
name: 'location_gps_latitude',
|
|
174
|
+
dataType: 'number' as any,
|
|
175
|
+
description: 'GPS latitude',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'location_gps_longitude',
|
|
179
|
+
dataType: 'number' as any,
|
|
180
|
+
description: 'GPS longitude',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'location_address_formatted',
|
|
184
|
+
dataType: 'text' as any,
|
|
185
|
+
description: 'Formatted address',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'location_address_city',
|
|
189
|
+
dataType: 'text' as any,
|
|
190
|
+
description: 'City',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: 'location_address_country',
|
|
194
|
+
dataType: 'text' as any,
|
|
195
|
+
description: 'Country',
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
// Context fields (flattened)
|
|
199
|
+
{
|
|
200
|
+
name: 'context_conversation_id',
|
|
201
|
+
dataType: 'text' as any,
|
|
202
|
+
description: 'Conversation ID',
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: 'context_platform',
|
|
206
|
+
dataType: 'text' as any,
|
|
207
|
+
description: 'Platform where created',
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
// Tags and relationships
|
|
211
|
+
{
|
|
212
|
+
name: 'tags',
|
|
213
|
+
dataType: 'text[]' as any,
|
|
214
|
+
description: 'Tags for categorization',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: 'related_memory_ids',
|
|
218
|
+
dataType: 'text[]' as any,
|
|
219
|
+
description: 'IDs of related memories',
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
// Timestamps
|
|
223
|
+
{
|
|
224
|
+
name: 'created_at',
|
|
225
|
+
dataType: 'text' as any,
|
|
226
|
+
description: 'Original creation timestamp (ISO 8601)',
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: 'updated_at',
|
|
230
|
+
dataType: 'text' as any,
|
|
231
|
+
description: 'Last update timestamp (ISO 8601)',
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
// Versioning
|
|
235
|
+
{
|
|
236
|
+
name: 'version',
|
|
237
|
+
dataType: 'number' as any,
|
|
238
|
+
description: 'Version number (increments on update)',
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
console.log(`[Weaviate] Space collection ${collectionName} created successfully`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Ensure a space collection exists, creating it if needed
|
|
248
|
+
*
|
|
249
|
+
* @param client - Weaviate client
|
|
250
|
+
* @param spaceId - Space identifier
|
|
251
|
+
* @returns Collection reference
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* const collection = await ensureSpaceCollection(client, 'the_void');
|
|
255
|
+
*/
|
|
256
|
+
export async function ensureSpaceCollection(
|
|
257
|
+
client: WeaviateClient,
|
|
258
|
+
spaceId: string
|
|
259
|
+
): Promise<Collection<any>> {
|
|
260
|
+
// Validate space ID
|
|
261
|
+
if (!isValidSpaceId(spaceId)) {
|
|
262
|
+
throw new Error(`Invalid space ID: ${spaceId}. Supported spaces: ${SUPPORTED_SPACES.join(', ')}`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const collectionName = getSpaceCollectionName(spaceId);
|
|
266
|
+
|
|
267
|
+
// Check if collection exists
|
|
268
|
+
const exists = await client.collections.exists(collectionName);
|
|
269
|
+
|
|
270
|
+
if (!exists) {
|
|
271
|
+
await createSpaceCollection(client, spaceId);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return client.collections.get(collectionName);
|
|
275
|
+
}
|