@prmichaelsen/remember-mcp 3.13.0 → 3.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/agent/milestones/milestone-17-remember-core-migration.md +140 -0
- package/agent/progress.yaml +123 -6
- package/agent/tasks/milestone-17-remember-core-migration/task-193-foundation-setup.md +58 -0
- package/agent/tasks/milestone-17-remember-core-migration/task-194-migrate-relationship-tools.md +47 -0
- package/agent/tasks/milestone-17-remember-core-migration/task-195-migrate-preference-tools.md +34 -0
- package/agent/tasks/milestone-17-remember-core-migration/task-196-migrate-memory-tools.md +46 -0
- package/agent/tasks/milestone-17-remember-core-migration/task-197-migrate-space-confirmation-tools.md +49 -0
- package/agent/tasks/milestone-17-remember-core-migration/task-198-migrate-space-search-moderate.md +46 -0
- package/agent/tasks/milestone-17-remember-core-migration/task-199-migrate-delete-memory.md +43 -0
- package/agent/tasks/milestone-17-remember-core-migration/task-200-code-cleanup-verification.md +52 -0
- package/dist/core-services.d.ts +25 -0
- package/dist/server-factory.js +3208 -3978
- package/dist/server.js +3708 -4474
- package/dist/tools/confirm-publish-moderation.spec.d.ts +3 -2
- package/dist/tools/create-memory.d.ts +1 -1
- package/dist/tools/query-space.d.ts +1 -1
- package/dist/tools/search-space.d.ts +10 -14
- package/jest.config.js +11 -0
- package/package.json +3 -1
- package/src/core-services.ts +50 -0
- package/src/tools/confirm-publish-moderation.spec.ts +120 -176
- package/src/tools/confirm.ts +70 -1035
- package/src/tools/create-memory.ts +16 -67
- package/src/tools/create-relationship.ts +13 -181
- package/src/tools/delete-memory.ts +7 -72
- package/src/tools/delete-relationship.ts +7 -91
- package/src/tools/deny.ts +4 -14
- package/src/tools/find-similar.ts +16 -110
- package/src/tools/get-preferences.ts +3 -8
- package/src/tools/moderate.spec.ts +65 -81
- package/src/tools/moderate.ts +18 -121
- package/src/tools/publish.ts +7 -204
- package/src/tools/query-space.ts +28 -140
- package/src/tools/retract.ts +7 -185
- package/src/tools/revise.ts +4 -136
- package/src/tools/search-relationship.ts +17 -116
- package/src/tools/search-space.ts +58 -304
- package/src/tools/set-preference.ts +3 -8
- package/src/tools/update-memory.ts +22 -190
- package/src/tools/update-relationship.ts +16 -90
- package/src/v2-smoke.e2e.ts +3 -2
- package/dist/collections/composite-ids.d.ts +0 -106
- package/dist/collections/core-infrastructure.spec.d.ts +0 -11
- package/dist/collections/dot-notation.d.ts +0 -106
- package/dist/collections/tracking-arrays.d.ts +0 -176
- package/dist/constants/content-types.d.ts +0 -61
- package/dist/services/confirmation-token.service.d.ts +0 -99
- package/dist/services/confirmation-token.service.spec.d.ts +0 -5
- package/dist/services/preferences-database.service.d.ts +0 -22
- package/dist/services/space-config.service.d.ts +0 -23
- package/dist/services/space-config.service.spec.d.ts +0 -2
- package/src/collections/composite-ids.ts +0 -193
- package/src/collections/core-infrastructure.spec.ts +0 -353
- package/src/collections/dot-notation.ts +0 -212
- package/src/collections/tracking-arrays.ts +0 -298
- package/src/constants/content-types.ts +0 -490
- package/src/services/confirmation-token.service.spec.ts +0 -254
- package/src/services/confirmation-token.service.ts +0 -328
- package/src/services/preferences-database.service.ts +0 -120
- package/src/services/space-config.service.spec.ts +0 -102
- package/src/services/space-config.service.ts +0 -79
package/src/tools/confirm.ts
CHANGED
|
@@ -12,18 +12,11 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
15
|
-
import {
|
|
16
|
-
import { getWeaviateClient, getMemoryCollectionName, fetchMemoryWithAllProperties } from '../weaviate/client.js';
|
|
17
|
-
import { ensurePublicCollection } from '../weaviate/space-schema.js';
|
|
15
|
+
import { getWeaviateClient, getMemoryCollectionName } from '../weaviate/client.js';
|
|
18
16
|
import { handleToolError } from '../utils/error-handler.js';
|
|
19
|
-
import { logger } from '../utils/logger.js';
|
|
20
17
|
import { createDebugLogger } from '../utils/debug.js';
|
|
21
|
-
import { CollectionType, getCollectionName } from '../collections/dot-notation.js';
|
|
22
|
-
import { generateCompositeId, parseCompositeId } from '../collections/composite-ids.js';
|
|
23
|
-
import { addToSpaceIds, addToGroupIds, removeFromSpaceIds, removeFromGroupIds, getPublishedLocations } from '../collections/tracking-arrays.js';
|
|
24
|
-
import { parseRevisionHistory, buildRevisionHistory, type RevisionResult } from './revise.js';
|
|
25
18
|
import type { AuthContext } from '../types/auth.js';
|
|
26
|
-
import {
|
|
19
|
+
import { createCoreServices } from '../core-services.js';
|
|
27
20
|
|
|
28
21
|
/**
|
|
29
22
|
* Tool definition for remember_confirm
|
|
@@ -86,30 +79,13 @@ export async function handleConfirm(
|
|
|
86
79
|
try {
|
|
87
80
|
debug.info('Tool invoked');
|
|
88
81
|
debug.trace('Arguments', { token: args.token });
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Validate and confirm token
|
|
97
|
-
debug.debug('Validating confirmation token');
|
|
98
|
-
const request = await debug.time('Confirm token', async () => {
|
|
99
|
-
return await confirmationTokenService.confirmRequest(userId, args.token);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
logger.debug('Token validation result', {
|
|
103
|
-
tool: 'remember_confirm',
|
|
104
|
-
requestFound: !!request,
|
|
105
|
-
action: request?.action,
|
|
106
|
-
});
|
|
82
|
+
|
|
83
|
+
const { space, token: tokenService } = createCoreServices(userId);
|
|
84
|
+
|
|
85
|
+
// Peek at token to determine action type without consuming it
|
|
86
|
+
const request = await tokenService.validateToken(userId, args.token);
|
|
107
87
|
|
|
108
88
|
if (!request) {
|
|
109
|
-
logger.info('Token invalid or expired', {
|
|
110
|
-
tool: 'remember_confirm',
|
|
111
|
-
userId,
|
|
112
|
-
});
|
|
113
89
|
return JSON.stringify(
|
|
114
90
|
{
|
|
115
91
|
success: false,
|
|
@@ -121,1060 +97,119 @@ export async function handleConfirm(
|
|
|
121
97
|
);
|
|
122
98
|
}
|
|
123
99
|
|
|
124
|
-
|
|
125
|
-
tool: 'remember_confirm',
|
|
126
|
-
action: request.action,
|
|
127
|
-
userId,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// GENERIC: Execute action based on type
|
|
131
|
-
// This is where the generic pattern delegates to action-specific executors
|
|
132
|
-
if (request.action === 'publish_memory') {
|
|
133
|
-
return await executePublishMemory(request, userId);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Handle delete_memory action
|
|
100
|
+
// Handle delete_memory separately (core's confirm doesn't support it)
|
|
137
101
|
if (request.action === 'delete_memory') {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// Handle revise_memory action
|
|
147
|
-
if (request.action === 'revise_memory') {
|
|
148
|
-
return await executeReviseMemory(request, userId);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
throw new Error(`Unknown action type: ${request.action}`);
|
|
152
|
-
} catch (error) {
|
|
153
|
-
debug.error('Tool failed', {
|
|
154
|
-
error: error instanceof Error ? error.message : String(error),
|
|
155
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
156
|
-
});
|
|
157
|
-
handleToolError(error, {
|
|
158
|
-
toolName: 'remember_confirm',
|
|
159
|
-
userId,
|
|
160
|
-
operation: 'confirm action',
|
|
161
|
-
token: args.token,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Execute publish memory action
|
|
168
|
-
*
|
|
169
|
-
* Memory Collection Pattern v2:
|
|
170
|
-
* - Publishes to Memory_spaces_public with composite ID
|
|
171
|
-
* - Publishes to Memory_groups_{groupId} for each group
|
|
172
|
-
* - Updates tracking arrays on source memory
|
|
173
|
-
* - Supports rollback on partial failure
|
|
174
|
-
*/
|
|
175
|
-
async function executePublishMemory(
|
|
176
|
-
request: ConfirmationRequest & { request_id: string },
|
|
177
|
-
userId: string,
|
|
178
|
-
authContext?: AuthContext
|
|
179
|
-
): Promise<string> {
|
|
180
|
-
const debug = createDebugLogger({
|
|
181
|
-
tool: 'remember_confirm',
|
|
182
|
-
userId,
|
|
183
|
-
operation: 'execute_publish',
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
try {
|
|
187
|
-
// Normalize arrays (handle undefined)
|
|
188
|
-
const spaces = request.payload.spaces || [];
|
|
189
|
-
const groups = request.payload.groups || [];
|
|
190
|
-
|
|
191
|
-
debug.debug('Executing publish memory action', {
|
|
192
|
-
memoryId: request.payload.memory_id,
|
|
193
|
-
spaces,
|
|
194
|
-
groups,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
logger.info('Executing publish memory action', {
|
|
198
|
-
function: 'executePublishMemory',
|
|
199
|
-
userId,
|
|
200
|
-
memoryId: request.payload.memory_id,
|
|
201
|
-
spaces,
|
|
202
|
-
groups,
|
|
203
|
-
spaceCount: spaces.length,
|
|
204
|
-
groupCount: groups.length,
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// Validate that at least one destination is provided
|
|
208
|
-
if (spaces.length === 0 && groups.length === 0) {
|
|
209
|
-
return JSON.stringify(
|
|
210
|
-
{
|
|
211
|
-
success: false,
|
|
212
|
-
error: 'No destinations',
|
|
213
|
-
message: 'Must specify at least one space or group to publish to',
|
|
214
|
-
},
|
|
215
|
-
null,
|
|
216
|
-
2
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Fetch the memory NOW (during confirmation, not from stored payload)
|
|
221
|
-
const weaviateClient = getWeaviateClient();
|
|
222
|
-
const userCollectionName = getMemoryCollectionName(userId);
|
|
223
|
-
const userCollection = weaviateClient.collections.get(userCollectionName);
|
|
224
|
-
|
|
225
|
-
logger.debug('Fetching original memory', {
|
|
226
|
-
function: 'executePublishMemory',
|
|
227
|
-
collectionName: userCollectionName,
|
|
228
|
-
memoryId: request.payload.memory_id,
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
const originalMemory = await debug.time('Fetch original memory', async () => {
|
|
232
|
-
return await fetchMemoryWithAllProperties(
|
|
233
|
-
userCollection,
|
|
234
|
-
request.payload.memory_id
|
|
235
|
-
);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
logger.info('Original memory fetch result', {
|
|
239
|
-
function: 'executePublishMemory',
|
|
240
|
-
found: !!originalMemory,
|
|
241
|
-
memoryId: request.payload.memory_id,
|
|
242
|
-
hasProperties: !!originalMemory?.properties,
|
|
243
|
-
propertyCount: originalMemory?.properties ? Object.keys(originalMemory.properties).length : 0,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
if (!originalMemory) {
|
|
247
|
-
logger.info('Original memory not found', {
|
|
248
|
-
function: 'executePublishMemory',
|
|
249
|
-
memoryId: request.payload.memory_id,
|
|
250
|
-
});
|
|
251
|
-
return JSON.stringify(
|
|
252
|
-
{
|
|
253
|
-
success: false,
|
|
254
|
-
error: 'Memory not found',
|
|
255
|
-
message: `Original memory ${request.payload.memory_id} no longer exists`,
|
|
256
|
-
},
|
|
257
|
-
null,
|
|
258
|
-
2
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Verify ownership again
|
|
263
|
-
if (originalMemory.properties.user_id !== userId) {
|
|
264
|
-
logger.warn('Permission denied - wrong owner', {
|
|
265
|
-
function: 'executePublishMemory',
|
|
266
|
-
memoryId: request.payload.memory_id,
|
|
267
|
-
memoryOwner: originalMemory.properties.user_id,
|
|
268
|
-
requestingUser: userId,
|
|
269
|
-
});
|
|
270
|
-
return JSON.stringify(
|
|
271
|
-
{
|
|
272
|
-
success: false,
|
|
273
|
-
error: 'Permission denied',
|
|
274
|
-
message: 'You can only publish your own memories',
|
|
275
|
-
},
|
|
276
|
-
null,
|
|
277
|
-
2
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Generate composite ID for published memories
|
|
282
|
-
const compositeId = generateCompositeId(userId, request.payload.memory_id);
|
|
283
|
-
|
|
284
|
-
logger.debug('Generated composite ID', {
|
|
285
|
-
function: 'executePublishMemory',
|
|
286
|
-
compositeId,
|
|
287
|
-
userId,
|
|
288
|
-
memoryId: request.payload.memory_id,
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// Get existing tracking arrays from source memory
|
|
292
|
-
const existingSpaceIds: string[] = Array.isArray(originalMemory.properties.space_ids)
|
|
293
|
-
? originalMemory.properties.space_ids
|
|
294
|
-
: [];
|
|
295
|
-
const existingGroupIds: string[] = Array.isArray(originalMemory.properties.group_ids)
|
|
296
|
-
? originalMemory.properties.group_ids
|
|
297
|
-
: [];
|
|
298
|
-
|
|
299
|
-
// Prepare tags
|
|
300
|
-
const originalTags = Array.isArray(originalMemory.properties.tags)
|
|
301
|
-
? originalMemory.properties.tags
|
|
302
|
-
: [];
|
|
303
|
-
const additionalTags = Array.isArray(request.payload.additional_tags)
|
|
304
|
-
? request.payload.additional_tags
|
|
305
|
-
: [];
|
|
306
|
-
const mergedTags = [...originalTags, ...additionalTags];
|
|
307
|
-
|
|
308
|
-
// Track publication results for rollback
|
|
309
|
-
const publicationResults: {
|
|
310
|
-
spaces?: { success: boolean; id?: string; error?: string };
|
|
311
|
-
groups: Array<{ groupId: string; success: boolean; id?: string; error?: string }>;
|
|
312
|
-
} = { groups: [] };
|
|
313
|
-
|
|
314
|
-
// STEP 1: Publish to spaces (Memory_spaces_public)
|
|
315
|
-
if (spaces.length > 0) {
|
|
316
|
-
logger.debug('Ensuring public spaces collection exists', {
|
|
317
|
-
function: 'executePublishMemory',
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
const publicCollection = await ensurePublicCollection(weaviateClient);
|
|
321
|
-
|
|
322
|
-
// Check if memory already exists in spaces collection with this composite ID
|
|
323
|
-
let existingSpaceMemory = null;
|
|
324
|
-
try {
|
|
325
|
-
existingSpaceMemory = await fetchMemoryWithAllProperties(publicCollection, compositeId);
|
|
326
|
-
} catch (e) {
|
|
327
|
-
// Memory doesn't exist, which is fine
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Calculate new space_ids array
|
|
331
|
-
const newSpaceIds = [...new Set([...existingSpaceIds, ...spaces])];
|
|
332
|
-
|
|
333
|
-
// Determine moderation status: pending if ANY target space requires moderation
|
|
334
|
-
let spaceModerationStatus = 'approved';
|
|
335
|
-
for (const spaceId of spaces) {
|
|
336
|
-
const spaceConfig = await getSpaceConfig(spaceId, 'space');
|
|
337
|
-
if (spaceConfig.require_moderation) {
|
|
338
|
-
spaceModerationStatus = 'pending';
|
|
339
|
-
break;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Create published memory with tracking arrays
|
|
344
|
-
const publishedMemory: Record<string, any> = {
|
|
345
|
-
...originalMemory.properties,
|
|
346
|
-
// Use composite ID as the document ID
|
|
347
|
-
id: compositeId,
|
|
348
|
-
// Tracking arrays (v2 feature)
|
|
349
|
-
space_ids: newSpaceIds,
|
|
350
|
-
group_ids: existingGroupIds,
|
|
351
|
-
// Legacy compatibility
|
|
352
|
-
spaces: spaces,
|
|
353
|
-
// Publication metadata
|
|
354
|
-
author_id: userId,
|
|
355
|
-
published_at: new Date().toISOString(),
|
|
356
|
-
discovery_count: 0,
|
|
357
|
-
attribution: 'user' as const,
|
|
358
|
-
// Moderation status
|
|
359
|
-
moderation_status: spaceModerationStatus,
|
|
360
|
-
// Merge tags
|
|
361
|
-
tags: mergedTags,
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
// Remove internal Weaviate properties
|
|
365
|
-
delete publishedMemory._additional;
|
|
366
|
-
|
|
367
|
-
logger.info('Publishing memory to Memory_spaces_public', {
|
|
368
|
-
function: 'executePublishMemory',
|
|
369
|
-
compositeId,
|
|
370
|
-
spaces,
|
|
371
|
-
spaceIds: newSpaceIds,
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
try {
|
|
375
|
-
if (existingSpaceMemory) {
|
|
376
|
-
// Update existing memory
|
|
377
|
-
await publicCollection.data.update({
|
|
378
|
-
id: compositeId,
|
|
379
|
-
properties: publishedMemory,
|
|
380
|
-
});
|
|
381
|
-
publicationResults.spaces = { success: true, id: compositeId };
|
|
382
|
-
} else {
|
|
383
|
-
// Insert new memory with specific ID
|
|
384
|
-
await publicCollection.data.insert({
|
|
385
|
-
id: compositeId,
|
|
386
|
-
properties: publishedMemory,
|
|
387
|
-
});
|
|
388
|
-
publicationResults.spaces = { success: true, id: compositeId };
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
logger.info('Memory published to spaces successfully', {
|
|
392
|
-
function: 'executePublishMemory',
|
|
393
|
-
compositeId,
|
|
394
|
-
spaces,
|
|
395
|
-
});
|
|
396
|
-
} catch (spaceError) {
|
|
397
|
-
logger.error('Failed to publish to spaces', {
|
|
398
|
-
function: 'executePublishMemory',
|
|
399
|
-
error: spaceError instanceof Error ? spaceError.message : String(spaceError),
|
|
400
|
-
});
|
|
401
|
-
publicationResults.spaces = {
|
|
402
|
-
success: false,
|
|
403
|
-
error: spaceError instanceof Error ? spaceError.message : String(spaceError)
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// STEP 2: Publish to groups (Memory_groups_{groupId})
|
|
409
|
-
for (const groupId of groups) {
|
|
410
|
-
const groupCollectionName = getCollectionName(CollectionType.GROUPS, groupId);
|
|
411
|
-
|
|
412
|
-
logger.debug('Publishing to group collection', {
|
|
413
|
-
function: 'executePublishMemory',
|
|
414
|
-
groupId,
|
|
415
|
-
collectionName: groupCollectionName,
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
try {
|
|
419
|
-
const groupCollection = weaviateClient.collections.get(groupCollectionName);
|
|
420
|
-
|
|
421
|
-
// Check if memory already exists in this group
|
|
422
|
-
let existingGroupMemory = null;
|
|
423
|
-
try {
|
|
424
|
-
existingGroupMemory = await fetchMemoryWithAllProperties(groupCollection, compositeId);
|
|
425
|
-
} catch (e) {
|
|
426
|
-
// Memory doesn't exist in this group
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Calculate new group_ids array (for this group publication)
|
|
430
|
-
const newGroupIds = [...new Set([...existingGroupIds, groupId])];
|
|
431
|
-
|
|
432
|
-
// Determine moderation status for this group
|
|
433
|
-
const groupConfig = await getSpaceConfig(groupId, 'group');
|
|
434
|
-
const groupModerationStatus = groupConfig.require_moderation ? 'pending' : 'approved';
|
|
435
|
-
|
|
436
|
-
// Create published memory for group
|
|
437
|
-
const groupMemory: Record<string, any> = {
|
|
438
|
-
...originalMemory.properties,
|
|
439
|
-
// Use composite ID
|
|
440
|
-
id: compositeId,
|
|
441
|
-
// Tracking arrays
|
|
442
|
-
space_ids: existingSpaceIds,
|
|
443
|
-
group_ids: newGroupIds,
|
|
444
|
-
// Publication metadata
|
|
445
|
-
author_id: userId,
|
|
446
|
-
published_at: new Date().toISOString(),
|
|
447
|
-
discovery_count: 0,
|
|
448
|
-
attribution: 'user' as const,
|
|
449
|
-
// Moderation status
|
|
450
|
-
moderation_status: groupModerationStatus,
|
|
451
|
-
// Merge tags
|
|
452
|
-
tags: mergedTags,
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
// Remove internal Weaviate properties
|
|
456
|
-
delete groupMemory._additional;
|
|
457
|
-
|
|
458
|
-
if (existingGroupMemory) {
|
|
459
|
-
await groupCollection.data.update({
|
|
460
|
-
id: compositeId,
|
|
461
|
-
properties: groupMemory,
|
|
462
|
-
});
|
|
463
|
-
} else {
|
|
464
|
-
await groupCollection.data.insert({
|
|
465
|
-
id: compositeId,
|
|
466
|
-
properties: groupMemory,
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
publicationResults.groups.push({ groupId, success: true, id: compositeId });
|
|
471
|
-
|
|
472
|
-
logger.info('Memory published to group successfully', {
|
|
473
|
-
function: 'executePublishMemory',
|
|
474
|
-
compositeId,
|
|
475
|
-
groupId,
|
|
476
|
-
});
|
|
477
|
-
} catch (groupError) {
|
|
478
|
-
logger.error('Failed to publish to group', {
|
|
479
|
-
function: 'executePublishMemory',
|
|
480
|
-
groupId,
|
|
481
|
-
error: groupError instanceof Error ? groupError.message : String(groupError),
|
|
482
|
-
});
|
|
483
|
-
publicationResults.groups.push({
|
|
484
|
-
groupId,
|
|
485
|
-
success: false,
|
|
486
|
-
error: groupError instanceof Error ? groupError.message : String(groupError)
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// STEP 3: Update source memory with tracking arrays
|
|
492
|
-
const finalSpaceIds = publicationResults.spaces?.success
|
|
493
|
-
? [...new Set([...existingSpaceIds, ...spaces])]
|
|
494
|
-
: existingSpaceIds;
|
|
495
|
-
|
|
496
|
-
const successfulGroups = publicationResults.groups
|
|
497
|
-
.filter(g => g.success)
|
|
498
|
-
.map(g => g.groupId);
|
|
499
|
-
const finalGroupIds = [...new Set([...existingGroupIds, ...successfulGroups])];
|
|
500
|
-
|
|
501
|
-
// Only update if there are changes
|
|
502
|
-
if (finalSpaceIds.length > existingSpaceIds.length || finalGroupIds.length > existingGroupIds.length) {
|
|
503
|
-
try {
|
|
504
|
-
await userCollection.data.update({
|
|
505
|
-
id: request.payload.memory_id,
|
|
506
|
-
properties: {
|
|
507
|
-
space_ids: finalSpaceIds,
|
|
508
|
-
group_ids: finalGroupIds,
|
|
102
|
+
// Consume the token
|
|
103
|
+
const confirmed = await tokenService.confirmRequest(userId, args.token);
|
|
104
|
+
if (!confirmed) {
|
|
105
|
+
return JSON.stringify(
|
|
106
|
+
{
|
|
107
|
+
success: false,
|
|
108
|
+
error: 'Token already consumed',
|
|
109
|
+
message: 'The confirmation token has already been used.',
|
|
509
110
|
},
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
logger.info('Updated source memory with tracking arrays', {
|
|
513
|
-
function: 'executePublishMemory',
|
|
514
|
-
memoryId: request.payload.memory_id,
|
|
515
|
-
spaceIds: finalSpaceIds,
|
|
516
|
-
groupIds: finalGroupIds,
|
|
517
|
-
});
|
|
518
|
-
} catch (updateError) {
|
|
519
|
-
logger.warn('Failed to update source memory tracking arrays', {
|
|
520
|
-
function: 'executePublishMemory',
|
|
521
|
-
memoryId: request.payload.memory_id,
|
|
522
|
-
error: updateError instanceof Error ? updateError.message : String(updateError),
|
|
523
|
-
});
|
|
524
|
-
// Don't fail the publish if this update fails
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Build response
|
|
529
|
-
const successfulPublications: string[] = [];
|
|
530
|
-
const failedPublications: string[] = [];
|
|
531
|
-
|
|
532
|
-
if (spaces.length > 0) {
|
|
533
|
-
if (publicationResults.spaces?.success) {
|
|
534
|
-
successfulPublications.push(`spaces: ${spaces.join(', ')}`);
|
|
535
|
-
} else {
|
|
536
|
-
failedPublications.push(`spaces: ${publicationResults.spaces?.error || 'unknown error'}`);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
for (const groupResult of publicationResults.groups) {
|
|
541
|
-
if (groupResult.success) {
|
|
542
|
-
successfulPublications.push(`group: ${groupResult.groupId}`);
|
|
543
|
-
} else {
|
|
544
|
-
failedPublications.push(`group ${groupResult.groupId}: ${groupResult.error || 'unknown error'}`);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// Return result
|
|
549
|
-
if (successfulPublications.length > 0) {
|
|
550
|
-
return JSON.stringify(
|
|
551
|
-
{
|
|
552
|
-
success: true,
|
|
553
|
-
composite_id: compositeId,
|
|
554
|
-
published_to: successfulPublications,
|
|
555
|
-
failed: failedPublications.length > 0 ? failedPublications : undefined,
|
|
556
|
-
space_ids: finalSpaceIds,
|
|
557
|
-
group_ids: finalGroupIds,
|
|
558
|
-
},
|
|
559
|
-
null,
|
|
560
|
-
2
|
|
561
|
-
);
|
|
562
|
-
} else {
|
|
563
|
-
return JSON.stringify(
|
|
564
|
-
{
|
|
565
|
-
success: false,
|
|
566
|
-
error: 'Publication failed',
|
|
567
|
-
message: 'Failed to publish to any destination',
|
|
568
|
-
details: failedPublications,
|
|
569
|
-
},
|
|
570
|
-
null,
|
|
571
|
-
2
|
|
572
|
-
);
|
|
573
|
-
}
|
|
574
|
-
} catch (error) {
|
|
575
|
-
debug.error('Execute publish failed', {
|
|
576
|
-
error: error instanceof Error ? error.message : String(error),
|
|
577
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
578
|
-
});
|
|
579
|
-
handleToolError(error, {
|
|
580
|
-
toolName: 'remember_confirm',
|
|
581
|
-
userId,
|
|
582
|
-
operation: 'execute publish_memory',
|
|
583
|
-
action: 'publish_memory',
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* Execute delete memory action
|
|
590
|
-
*/
|
|
591
|
-
async function executeDeleteMemory(
|
|
592
|
-
request: ConfirmationRequest & { request_id: string },
|
|
593
|
-
userId: string,
|
|
594
|
-
authContext?: AuthContext
|
|
595
|
-
): Promise<string> {
|
|
596
|
-
try {
|
|
597
|
-
logger.info('Executing delete memory action', {
|
|
598
|
-
function: 'executeDeleteMemory',
|
|
599
|
-
userId,
|
|
600
|
-
memoryId: request.payload.memory_id,
|
|
601
|
-
hasReason: !!request.payload.reason,
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
const { memory_id, reason } = request.payload;
|
|
605
|
-
|
|
606
|
-
// Soft delete the memory
|
|
607
|
-
const client = getWeaviateClient();
|
|
608
|
-
const collectionName = getMemoryCollectionName(userId);
|
|
609
|
-
const collection = client.collections.get(collectionName);
|
|
610
|
-
|
|
611
|
-
await collection.data.update({
|
|
612
|
-
id: memory_id,
|
|
613
|
-
properties: {
|
|
614
|
-
deleted_at: new Date().toISOString(),
|
|
615
|
-
deleted_by: userId,
|
|
616
|
-
deletion_reason: reason || null,
|
|
617
|
-
},
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
logger.info('Memory soft-deleted successfully', {
|
|
621
|
-
function: 'executeDeleteMemory',
|
|
622
|
-
userId,
|
|
623
|
-
memoryId: memory_id,
|
|
624
|
-
deletedAt: new Date().toISOString(),
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
return JSON.stringify(
|
|
628
|
-
{
|
|
629
|
-
success: true,
|
|
630
|
-
memory_id,
|
|
631
|
-
message: 'Memory deleted successfully',
|
|
632
|
-
},
|
|
633
|
-
null,
|
|
634
|
-
2
|
|
635
|
-
);
|
|
636
|
-
} catch (error) {
|
|
637
|
-
logger.error('Failed to execute delete memory', {
|
|
638
|
-
function: 'executeDeleteMemory',
|
|
639
|
-
userId,
|
|
640
|
-
memoryId: request.payload.memory_id,
|
|
641
|
-
error: error instanceof Error ? error.message : String(error),
|
|
642
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
643
|
-
});
|
|
644
|
-
throw error;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* Execute retract memory action
|
|
650
|
-
*
|
|
651
|
-
* Memory Collection Pattern v2:
|
|
652
|
-
* - Selective retraction from specific spaces/groups
|
|
653
|
-
* - Space memories are orphaned (remain in Memory_spaces_public with empty tracking arrays)
|
|
654
|
-
* - Group memories are deleted from group collections
|
|
655
|
-
* - Tracking arrays updated on source memory
|
|
656
|
-
*/
|
|
657
|
-
async function executeRetractMemory(
|
|
658
|
-
request: ConfirmationRequest & { request_id: string },
|
|
659
|
-
userId: string,
|
|
660
|
-
authContext?: AuthContext
|
|
661
|
-
): Promise<string> {
|
|
662
|
-
const debug = createDebugLogger({
|
|
663
|
-
tool: 'remember_confirm',
|
|
664
|
-
userId,
|
|
665
|
-
operation: 'execute_retract',
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
try {
|
|
669
|
-
// Normalize arrays (handle undefined)
|
|
670
|
-
const spaces = request.payload.spaces || [];
|
|
671
|
-
const groups = request.payload.groups || [];
|
|
672
|
-
|
|
673
|
-
debug.debug('Executing retract memory action', {
|
|
674
|
-
memoryId: request.payload.memory_id,
|
|
675
|
-
spaces,
|
|
676
|
-
groups,
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
logger.info('Executing retract memory action', {
|
|
680
|
-
function: 'executeRetractMemory',
|
|
681
|
-
userId,
|
|
682
|
-
memoryId: request.payload.memory_id,
|
|
683
|
-
spaces,
|
|
684
|
-
groups,
|
|
685
|
-
spaceCount: spaces.length,
|
|
686
|
-
groupCount: groups.length,
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
// Fetch the source memory
|
|
690
|
-
const weaviateClient = getWeaviateClient();
|
|
691
|
-
const userCollectionName = getMemoryCollectionName(userId);
|
|
692
|
-
const userCollection = weaviateClient.collections.get(userCollectionName);
|
|
693
|
-
|
|
694
|
-
const sourceMemory = await fetchMemoryWithAllProperties(
|
|
695
|
-
userCollection,
|
|
696
|
-
request.payload.memory_id
|
|
697
|
-
);
|
|
698
|
-
|
|
699
|
-
if (!sourceMemory) {
|
|
700
|
-
logger.info('Source memory not found for retraction', {
|
|
701
|
-
function: 'executeRetractMemory',
|
|
702
|
-
memoryId: request.payload.memory_id,
|
|
703
|
-
});
|
|
704
|
-
return JSON.stringify(
|
|
705
|
-
{
|
|
706
|
-
success: false,
|
|
707
|
-
error: 'Memory not found',
|
|
708
|
-
message: `Source memory ${request.payload.memory_id} no longer exists`,
|
|
709
|
-
},
|
|
710
|
-
null,
|
|
711
|
-
2
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
// Get current tracking arrays
|
|
716
|
-
const currentSpaceIds: string[] = Array.isArray(sourceMemory.properties.space_ids)
|
|
717
|
-
? sourceMemory.properties.space_ids
|
|
718
|
-
: [];
|
|
719
|
-
const currentGroupIds: string[] = Array.isArray(sourceMemory.properties.group_ids)
|
|
720
|
-
? sourceMemory.properties.group_ids
|
|
721
|
-
: [];
|
|
722
|
-
|
|
723
|
-
// Generate composite ID for published memories
|
|
724
|
-
const compositeId = generateCompositeId(userId, request.payload.memory_id);
|
|
725
|
-
|
|
726
|
-
// Track retraction results
|
|
727
|
-
const retractionResults: {
|
|
728
|
-
spaces?: { success: boolean; error?: string };
|
|
729
|
-
groups: Array<{ groupId: string; success: boolean; error?: string }>;
|
|
730
|
-
} = { groups: [] };
|
|
731
|
-
|
|
732
|
-
// STEP 1: Retract from spaces (Memory_spaces_public)
|
|
733
|
-
// Space memories are ORPHANED, not deleted, for historical reference
|
|
734
|
-
if (spaces.length > 0) {
|
|
735
|
-
try {
|
|
736
|
-
const publicCollection = weaviateClient.collections.get(
|
|
737
|
-
getCollectionName(CollectionType.SPACES)
|
|
111
|
+
null,
|
|
112
|
+
2
|
|
738
113
|
);
|
|
739
|
-
|
|
740
|
-
// Fetch the published memory from spaces
|
|
741
|
-
const publishedMemory = await fetchMemoryWithAllProperties(publicCollection, compositeId);
|
|
742
|
-
|
|
743
|
-
if (publishedMemory) {
|
|
744
|
-
// Calculate new space_ids (remove retracted spaces)
|
|
745
|
-
const newSpaceIds = currentSpaceIds.filter(id => !spaces.includes(id));
|
|
746
|
-
|
|
747
|
-
// Update the published memory with new tracking arrays
|
|
748
|
-
// Memory remains even if space_ids becomes empty (orphaned)
|
|
749
|
-
await publicCollection.data.update({
|
|
750
|
-
id: compositeId,
|
|
751
|
-
properties: {
|
|
752
|
-
space_ids: newSpaceIds,
|
|
753
|
-
retracted_at: new Date().toISOString(),
|
|
754
|
-
},
|
|
755
|
-
});
|
|
756
|
-
|
|
757
|
-
retractionResults.spaces = { success: true };
|
|
758
|
-
|
|
759
|
-
logger.info('Memory retracted from spaces (orphaned)', {
|
|
760
|
-
function: 'executeRetractMemory',
|
|
761
|
-
compositeId,
|
|
762
|
-
retractedSpaces: spaces,
|
|
763
|
-
remainingSpaces: newSpaceIds,
|
|
764
|
-
isOrphaned: newSpaceIds.length === 0 && currentGroupIds.length === 0,
|
|
765
|
-
});
|
|
766
|
-
} else {
|
|
767
|
-
// Memory not found in spaces collection
|
|
768
|
-
retractionResults.spaces = {
|
|
769
|
-
success: false,
|
|
770
|
-
error: 'Memory not found in spaces collection',
|
|
771
|
-
};
|
|
772
|
-
logger.warn('Memory not found in spaces collection', {
|
|
773
|
-
function: 'executeRetractMemory',
|
|
774
|
-
compositeId,
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
|
-
} catch (spaceError) {
|
|
778
|
-
logger.error('Failed to retract from spaces', {
|
|
779
|
-
function: 'executeRetractMemory',
|
|
780
|
-
error: spaceError instanceof Error ? spaceError.message : String(spaceError),
|
|
781
|
-
});
|
|
782
|
-
retractionResults.spaces = {
|
|
783
|
-
success: false,
|
|
784
|
-
error: spaceError instanceof Error ? spaceError.message : String(spaceError),
|
|
785
|
-
};
|
|
786
114
|
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
// STEP 2: Retract from groups (Memory_groups_{groupId})
|
|
790
|
-
// Group memories are ORPHANED (same as spaces), not deleted
|
|
791
|
-
// This preserves historical data while making it unsearchable by default
|
|
792
|
-
for (const groupId of groups) {
|
|
793
|
-
const groupCollectionName = getCollectionName(CollectionType.GROUPS, groupId);
|
|
794
|
-
|
|
795
|
-
try {
|
|
796
|
-
const groupCollection = weaviateClient.collections.get(groupCollectionName);
|
|
797
|
-
|
|
798
|
-
// Check if memory exists in this group
|
|
799
|
-
const groupMemory = await fetchMemoryWithAllProperties(groupCollection, compositeId);
|
|
800
115
|
|
|
801
|
-
|
|
802
|
-
// Get current group_ids from the group memory
|
|
803
|
-
const groupMemoryGroupIds: string[] = Array.isArray(groupMemory.properties.group_ids)
|
|
804
|
-
? groupMemory.properties.group_ids
|
|
805
|
-
: [];
|
|
806
|
-
|
|
807
|
-
// Remove this group from the group_ids array
|
|
808
|
-
const newGroupIds = groupMemoryGroupIds.filter(id => id !== groupId);
|
|
809
|
-
|
|
810
|
-
// Update the memory with new tracking arrays (orphan if empty)
|
|
811
|
-
await groupCollection.data.update({
|
|
812
|
-
id: compositeId,
|
|
813
|
-
properties: {
|
|
814
|
-
group_ids: newGroupIds,
|
|
815
|
-
retracted_at: new Date().toISOString(),
|
|
816
|
-
},
|
|
817
|
-
});
|
|
116
|
+
const { memory_id, reason } = confirmed.payload;
|
|
818
117
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
compositeId,
|
|
824
|
-
groupId,
|
|
825
|
-
remainingGroups: newGroupIds,
|
|
826
|
-
isOrphaned: newGroupIds.length === 0,
|
|
827
|
-
});
|
|
828
|
-
} else {
|
|
829
|
-
// Memory not found in this group
|
|
830
|
-
retractionResults.groups.push({
|
|
831
|
-
groupId,
|
|
832
|
-
success: false,
|
|
833
|
-
error: 'Memory not found in group',
|
|
834
|
-
});
|
|
835
|
-
logger.warn('Memory not found in group collection', {
|
|
836
|
-
function: 'executeRetractMemory',
|
|
837
|
-
compositeId,
|
|
838
|
-
groupId,
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
} catch (groupError) {
|
|
842
|
-
logger.error('Failed to retract from group', {
|
|
843
|
-
function: 'executeRetractMemory',
|
|
844
|
-
groupId,
|
|
845
|
-
error: groupError instanceof Error ? groupError.message : String(groupError),
|
|
846
|
-
});
|
|
847
|
-
retractionResults.groups.push({
|
|
848
|
-
groupId,
|
|
849
|
-
success: false,
|
|
850
|
-
error: groupError instanceof Error ? groupError.message : String(groupError),
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
}
|
|
118
|
+
// Soft delete the memory
|
|
119
|
+
const client = getWeaviateClient();
|
|
120
|
+
const collectionName = getMemoryCollectionName(userId);
|
|
121
|
+
const collection = client.collections.get(collectionName);
|
|
854
122
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
? currentSpaceIds.filter(id => !spaces.includes(id))
|
|
858
|
-
: currentSpaceIds;
|
|
859
|
-
|
|
860
|
-
const successfulGroupRetractions = retractionResults.groups
|
|
861
|
-
.filter(g => g.success)
|
|
862
|
-
.map(g => g.groupId);
|
|
863
|
-
const finalGroupIds = currentGroupIds.filter(id => !successfulGroupRetractions.includes(id));
|
|
864
|
-
|
|
865
|
-
// Update source memory
|
|
866
|
-
try {
|
|
867
|
-
await userCollection.data.update({
|
|
868
|
-
id: request.payload.memory_id,
|
|
123
|
+
await collection.data.update({
|
|
124
|
+
id: memory_id,
|
|
869
125
|
properties: {
|
|
870
|
-
|
|
871
|
-
|
|
126
|
+
deleted_at: new Date().toISOString(),
|
|
127
|
+
deleted_by: userId,
|
|
128
|
+
deletion_reason: reason || null,
|
|
872
129
|
},
|
|
873
130
|
});
|
|
874
131
|
|
|
875
|
-
logger.info('Updated source memory tracking arrays after retraction', {
|
|
876
|
-
function: 'executeRetractMemory',
|
|
877
|
-
memoryId: request.payload.memory_id,
|
|
878
|
-
spaceIds: finalSpaceIds,
|
|
879
|
-
groupIds: finalGroupIds,
|
|
880
|
-
});
|
|
881
|
-
} catch (updateError) {
|
|
882
|
-
logger.warn('Failed to update source memory tracking arrays after retraction', {
|
|
883
|
-
function: 'executeRetractMemory',
|
|
884
|
-
memoryId: request.payload.memory_id,
|
|
885
|
-
error: updateError instanceof Error ? updateError.message : String(updateError),
|
|
886
|
-
});
|
|
887
|
-
// Don't fail the retraction if this update fails
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
// Build response
|
|
891
|
-
const successfulRetractions: string[] = [];
|
|
892
|
-
const failedRetractions: string[] = [];
|
|
893
|
-
|
|
894
|
-
if (spaces.length > 0) {
|
|
895
|
-
if (retractionResults.spaces?.success) {
|
|
896
|
-
successfulRetractions.push(`spaces: ${spaces.join(', ')}`);
|
|
897
|
-
} else {
|
|
898
|
-
failedRetractions.push(`spaces: ${retractionResults.spaces?.error || 'unknown error'}`);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
for (const groupResult of retractionResults.groups) {
|
|
903
|
-
if (groupResult.success) {
|
|
904
|
-
successfulRetractions.push(`group: ${groupResult.groupId}`);
|
|
905
|
-
} else {
|
|
906
|
-
failedRetractions.push(`group ${groupResult.groupId}: ${groupResult.error || 'unknown error'}`);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
// Return result
|
|
911
|
-
if (successfulRetractions.length > 0) {
|
|
912
132
|
return JSON.stringify(
|
|
913
133
|
{
|
|
914
134
|
success: true,
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
failed: failedRetractions.length > 0 ? failedRetractions : undefined,
|
|
918
|
-
space_ids: finalSpaceIds,
|
|
919
|
-
group_ids: finalGroupIds,
|
|
920
|
-
is_orphaned: finalSpaceIds.length === 0 && finalGroupIds.length === 0,
|
|
921
|
-
},
|
|
922
|
-
null,
|
|
923
|
-
2
|
|
924
|
-
);
|
|
925
|
-
} else {
|
|
926
|
-
return JSON.stringify(
|
|
927
|
-
{
|
|
928
|
-
success: false,
|
|
929
|
-
error: 'Retraction failed',
|
|
930
|
-
message: 'Failed to retract from any destination',
|
|
931
|
-
details: failedRetractions,
|
|
135
|
+
memory_id,
|
|
136
|
+
message: 'Memory deleted successfully',
|
|
932
137
|
},
|
|
933
138
|
null,
|
|
934
139
|
2
|
|
935
140
|
);
|
|
936
141
|
}
|
|
937
|
-
} catch (error) {
|
|
938
|
-
debug.error('Execute retract failed', {
|
|
939
|
-
error: error instanceof Error ? error.message : String(error),
|
|
940
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
941
|
-
});
|
|
942
|
-
handleToolError(error, {
|
|
943
|
-
toolName: 'remember_confirm',
|
|
944
|
-
userId,
|
|
945
|
-
operation: 'execute retract_memory',
|
|
946
|
-
action: 'retract_memory',
|
|
947
|
-
});
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
/**
|
|
952
|
-
* Execute revise memory action
|
|
953
|
-
*
|
|
954
|
-
* Memory Collection Pattern v2:
|
|
955
|
-
* - Syncs content from source memory to all published copies
|
|
956
|
-
* - Preserves old content in revision_history (max 10 entries)
|
|
957
|
-
* - Updates revised_at and revision_count on each copy
|
|
958
|
-
* - Supports partial success (some locations may fail)
|
|
959
|
-
*/
|
|
960
|
-
async function executeReviseMemory(
|
|
961
|
-
request: ConfirmationRequest & { request_id: string },
|
|
962
|
-
userId: string,
|
|
963
|
-
authContext?: AuthContext
|
|
964
|
-
): Promise<string> {
|
|
965
|
-
const debug = createDebugLogger({
|
|
966
|
-
tool: 'remember_confirm',
|
|
967
|
-
userId,
|
|
968
|
-
operation: 'execute_revise',
|
|
969
|
-
});
|
|
970
|
-
|
|
971
|
-
try {
|
|
972
|
-
const { memory_id, space_ids = [], group_ids = [] } = request.payload;
|
|
973
|
-
|
|
974
|
-
debug.debug('Executing revise memory action', {
|
|
975
|
-
memoryId: memory_id,
|
|
976
|
-
spaceIds: space_ids,
|
|
977
|
-
groupIds: group_ids,
|
|
978
|
-
});
|
|
979
|
-
|
|
980
|
-
logger.info('Executing revise memory action', {
|
|
981
|
-
function: 'executeReviseMemory',
|
|
982
|
-
userId,
|
|
983
|
-
memoryId: memory_id,
|
|
984
|
-
spaceCount: space_ids.length,
|
|
985
|
-
groupCount: group_ids.length,
|
|
986
|
-
});
|
|
987
142
|
|
|
988
|
-
//
|
|
989
|
-
const
|
|
990
|
-
const userCollectionName = getMemoryCollectionName(userId);
|
|
991
|
-
const userCollection = weaviateClient.collections.get(userCollectionName);
|
|
143
|
+
// Delegate publish/retract/revise to core SpaceService
|
|
144
|
+
const result = await space.confirm({ token: args.token });
|
|
992
145
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
memory_id
|
|
996
|
-
);
|
|
997
|
-
|
|
998
|
-
if (!sourceMemory) {
|
|
146
|
+
// Format response based on action type
|
|
147
|
+
if (result.action === 'retract_memory') {
|
|
999
148
|
return JSON.stringify(
|
|
1000
149
|
{
|
|
1001
|
-
success:
|
|
1002
|
-
|
|
1003
|
-
|
|
150
|
+
success: result.success,
|
|
151
|
+
composite_id: result.composite_id,
|
|
152
|
+
retracted_from: result.retracted_from,
|
|
153
|
+
failed: result.failed?.length ? result.failed : undefined,
|
|
154
|
+
space_ids: result.space_ids,
|
|
155
|
+
group_ids: result.group_ids,
|
|
156
|
+
is_orphaned: (result.space_ids?.length === 0) && (result.group_ids?.length === 0),
|
|
1004
157
|
},
|
|
1005
158
|
null,
|
|
1006
159
|
2
|
|
1007
160
|
);
|
|
1008
161
|
}
|
|
1009
162
|
|
|
1010
|
-
|
|
1011
|
-
|
|
163
|
+
if (result.action === 'revise_memory') {
|
|
164
|
+
const results = result.results || [];
|
|
165
|
+
const successCount = results.filter((r: any) => r.status === 'success').length;
|
|
166
|
+
const failedCount = results.filter((r: any) => r.status === 'failed').length;
|
|
167
|
+
const skippedCount = results.filter((r: any) => r.status === 'skipped').length;
|
|
168
|
+
|
|
1012
169
|
return JSON.stringify(
|
|
1013
170
|
{
|
|
1014
|
-
success:
|
|
1015
|
-
|
|
1016
|
-
|
|
171
|
+
success: result.success,
|
|
172
|
+
composite_id: result.composite_id,
|
|
173
|
+
revised_at: result.revised_at,
|
|
174
|
+
summary: {
|
|
175
|
+
total: results.length,
|
|
176
|
+
success: successCount,
|
|
177
|
+
failed: failedCount,
|
|
178
|
+
skipped: skippedCount,
|
|
179
|
+
},
|
|
180
|
+
results,
|
|
181
|
+
...(failedCount > 0
|
|
182
|
+
? { warnings: [`Failed to revise ${failedCount} of ${results.length} location(s)`] }
|
|
183
|
+
: {}),
|
|
1017
184
|
},
|
|
1018
185
|
null,
|
|
1019
186
|
2
|
|
1020
187
|
);
|
|
1021
188
|
}
|
|
1022
189
|
|
|
1023
|
-
|
|
1024
|
-
const revisedAt = new Date().toISOString();
|
|
1025
|
-
const compositeId = generateCompositeId(userId, memory_id);
|
|
1026
|
-
const results: RevisionResult[] = [];
|
|
1027
|
-
|
|
1028
|
-
logger.info('Revising published copies', {
|
|
1029
|
-
function: 'executeReviseMemory',
|
|
1030
|
-
compositeId,
|
|
1031
|
-
spaceCount: space_ids.length > 0 ? 1 : 0,
|
|
1032
|
-
groupCount: group_ids.length,
|
|
1033
|
-
});
|
|
1034
|
-
|
|
1035
|
-
/**
|
|
1036
|
-
* Update content + revision tracking in a single collection.
|
|
1037
|
-
*/
|
|
1038
|
-
async function reviseInCollection(
|
|
1039
|
-
collectionName: string,
|
|
1040
|
-
locationLabel: string
|
|
1041
|
-
): Promise<void> {
|
|
1042
|
-
try {
|
|
1043
|
-
const collection = weaviateClient.collections.get(collectionName);
|
|
1044
|
-
const publishedMemory = await fetchMemoryWithAllProperties(
|
|
1045
|
-
collection,
|
|
1046
|
-
compositeId
|
|
1047
|
-
);
|
|
1048
|
-
|
|
1049
|
-
if (!publishedMemory) {
|
|
1050
|
-
results.push({
|
|
1051
|
-
location: locationLabel,
|
|
1052
|
-
status: 'skipped',
|
|
1053
|
-
error: 'Published copy not found (may have been deleted)',
|
|
1054
|
-
});
|
|
1055
|
-
logger.warn('Published copy not found in collection', {
|
|
1056
|
-
function: 'executeReviseMemory',
|
|
1057
|
-
collectionName,
|
|
1058
|
-
compositeId,
|
|
1059
|
-
});
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
const oldContent = String(publishedMemory.properties.content ?? '');
|
|
1064
|
-
|
|
1065
|
-
// Build updated revision history (only if content actually changed)
|
|
1066
|
-
let revisionHistory = parseRevisionHistory(
|
|
1067
|
-
publishedMemory.properties.revision_history
|
|
1068
|
-
);
|
|
1069
|
-
if (oldContent !== newContent) {
|
|
1070
|
-
revisionHistory = buildRevisionHistory(
|
|
1071
|
-
revisionHistory,
|
|
1072
|
-
oldContent,
|
|
1073
|
-
revisedAt
|
|
1074
|
-
);
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
const currentRevisionCount =
|
|
1078
|
-
typeof publishedMemory.properties.revision_count === 'number'
|
|
1079
|
-
? publishedMemory.properties.revision_count
|
|
1080
|
-
: 0;
|
|
1081
|
-
|
|
1082
|
-
await collection.data.update({
|
|
1083
|
-
id: compositeId,
|
|
1084
|
-
properties: {
|
|
1085
|
-
content: newContent,
|
|
1086
|
-
revised_at: revisedAt,
|
|
1087
|
-
revision_count: currentRevisionCount + 1,
|
|
1088
|
-
revision_history: JSON.stringify(revisionHistory),
|
|
1089
|
-
},
|
|
1090
|
-
});
|
|
1091
|
-
|
|
1092
|
-
results.push({ location: locationLabel, status: 'success' });
|
|
1093
|
-
|
|
1094
|
-
logger.info('Revised published memory in collection', {
|
|
1095
|
-
function: 'executeReviseMemory',
|
|
1096
|
-
collectionName,
|
|
1097
|
-
compositeId,
|
|
1098
|
-
revisionCount: currentRevisionCount + 1,
|
|
1099
|
-
contentChanged: oldContent !== newContent,
|
|
1100
|
-
});
|
|
1101
|
-
} catch (err) {
|
|
1102
|
-
results.push({
|
|
1103
|
-
location: locationLabel,
|
|
1104
|
-
status: 'failed',
|
|
1105
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1106
|
-
});
|
|
1107
|
-
logger.error('Failed to revise in collection', {
|
|
1108
|
-
function: 'executeReviseMemory',
|
|
1109
|
-
collectionName,
|
|
1110
|
-
compositeId,
|
|
1111
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1112
|
-
});
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
// Revise in Memory_spaces_public (single collection for all spaces)
|
|
1117
|
-
if (space_ids.length > 0) {
|
|
1118
|
-
await reviseInCollection(
|
|
1119
|
-
getCollectionName(CollectionType.SPACES),
|
|
1120
|
-
'Memory_spaces_public'
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
// Revise in each group's collection
|
|
1125
|
-
for (const groupId of group_ids) {
|
|
1126
|
-
await reviseInCollection(
|
|
1127
|
-
getCollectionName(CollectionType.GROUPS, groupId),
|
|
1128
|
-
`Memory_groups_${groupId}`
|
|
1129
|
-
);
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
const successCount = results.filter(r => r.status === 'success').length;
|
|
1133
|
-
const failedCount = results.filter(r => r.status === 'failed').length;
|
|
1134
|
-
const skippedCount = results.filter(r => r.status === 'skipped').length;
|
|
1135
|
-
|
|
1136
|
-
logger.info('Revise execution complete', {
|
|
1137
|
-
function: 'executeReviseMemory',
|
|
1138
|
-
userId,
|
|
1139
|
-
memoryId: memory_id,
|
|
1140
|
-
successCount,
|
|
1141
|
-
failedCount,
|
|
1142
|
-
skippedCount,
|
|
1143
|
-
});
|
|
1144
|
-
|
|
190
|
+
// Default: publish_memory (and any future action types)
|
|
1145
191
|
return JSON.stringify(
|
|
1146
192
|
{
|
|
1147
|
-
success:
|
|
1148
|
-
composite_id:
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
failed: failedCount,
|
|
1154
|
-
skipped: skippedCount,
|
|
1155
|
-
},
|
|
1156
|
-
results,
|
|
1157
|
-
...(failedCount > 0
|
|
1158
|
-
? {
|
|
1159
|
-
warnings: [
|
|
1160
|
-
`Failed to revise ${failedCount} of ${results.length} location(s)`,
|
|
1161
|
-
],
|
|
1162
|
-
}
|
|
1163
|
-
: {}),
|
|
193
|
+
success: result.success,
|
|
194
|
+
composite_id: result.composite_id,
|
|
195
|
+
published_to: result.published_to,
|
|
196
|
+
failed: result.failed?.length ? result.failed : undefined,
|
|
197
|
+
space_ids: result.space_ids,
|
|
198
|
+
group_ids: result.group_ids,
|
|
1164
199
|
},
|
|
1165
200
|
null,
|
|
1166
201
|
2
|
|
1167
202
|
);
|
|
1168
203
|
} catch (error) {
|
|
1169
|
-
debug.error('
|
|
204
|
+
debug.error('Tool failed', {
|
|
1170
205
|
error: error instanceof Error ? error.message : String(error),
|
|
1171
206
|
stack: error instanceof Error ? error.stack : undefined,
|
|
1172
207
|
});
|
|
1173
208
|
handleToolError(error, {
|
|
1174
209
|
toolName: 'remember_confirm',
|
|
1175
210
|
userId,
|
|
1176
|
-
operation: '
|
|
1177
|
-
|
|
211
|
+
operation: 'confirm action',
|
|
212
|
+
token: args.token,
|
|
1178
213
|
});
|
|
1179
214
|
}
|
|
1180
215
|
}
|