@prmichaelsen/remember-mcp 2.8.0 → 3.0.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.
Files changed (74) hide show
  1. package/AGENT.md +1 -1
  2. package/CHANGELOG.md +130 -0
  3. package/README.md +95 -1
  4. package/agent/commands/acp.command-create.md +0 -1
  5. package/agent/commands/acp.design-create.md +0 -1
  6. package/agent/commands/acp.init.md +0 -1
  7. package/agent/commands/acp.package-create.md +0 -1
  8. package/agent/commands/acp.package-info.md +0 -1
  9. package/agent/commands/acp.package-install.md +0 -1
  10. package/agent/commands/acp.package-list.md +0 -1
  11. package/agent/commands/acp.package-publish.md +0 -1
  12. package/agent/commands/acp.package-remove.md +0 -1
  13. package/agent/commands/acp.package-search.md +0 -1
  14. package/agent/commands/acp.package-update.md +0 -1
  15. package/agent/commands/acp.package-validate.md +0 -1
  16. package/agent/commands/acp.pattern-create.md +0 -1
  17. package/agent/commands/acp.plan.md +0 -1
  18. package/agent/commands/acp.proceed.md +0 -1
  19. package/agent/commands/acp.project-create.md +0 -1
  20. package/agent/commands/acp.project-list.md +0 -1
  21. package/agent/commands/acp.project-set.md +0 -1
  22. package/agent/commands/acp.report.md +0 -1
  23. package/agent/commands/acp.resume.md +0 -1
  24. package/agent/commands/acp.status.md +0 -1
  25. package/agent/commands/acp.sync.md +0 -1
  26. package/agent/commands/acp.task-create.md +0 -1
  27. package/agent/commands/acp.update.md +0 -1
  28. package/agent/commands/acp.validate.md +0 -1
  29. package/agent/commands/acp.version-check-for-updates.md +0 -1
  30. package/agent/commands/acp.version-check.md +0 -1
  31. package/agent/commands/acp.version-update.md +0 -1
  32. package/agent/commands/command.template.md +0 -5
  33. package/agent/commands/git.commit.md +0 -1
  34. package/agent/commands/git.init.md +0 -1
  35. package/agent/design/soft-delete-system.md +291 -0
  36. package/agent/milestones/milestone-13-soft-delete-system.md +306 -0
  37. package/agent/package.template.yaml +0 -17
  38. package/agent/progress.yaml +136 -2
  39. package/agent/scripts/acp.install.sh +4 -84
  40. package/agent/scripts/acp.package-install.sh +33 -112
  41. package/agent/scripts/acp.package-validate.sh +0 -99
  42. package/agent/tasks/task-70-add-soft-delete-schema-fields.md +165 -0
  43. package/agent/tasks/task-71-implement-delete-confirmation-flow.md +257 -0
  44. package/agent/tasks/task-72-add-deleted-filter-to-search-tools.md +18 -0
  45. package/agent/tasks/task-73-update-relationship-handling.md +18 -0
  46. package/agent/tasks/task-74-add-unit-tests-soft-delete.md +18 -0
  47. package/agent/tasks/task-75-update-documentation-changelog.md +26 -0
  48. package/dist/server-factory.js +677 -501
  49. package/dist/server.js +677 -501
  50. package/dist/tools/delete-memory.d.ts +5 -30
  51. package/dist/tools/find-similar.d.ts +8 -1
  52. package/dist/tools/query-memory.d.ts +8 -1
  53. package/dist/tools/search-memory.d.ts +6 -0
  54. package/dist/tools/search-relationship.d.ts +8 -1
  55. package/dist/types/memory.d.ts +8 -0
  56. package/dist/types/space-memory.d.ts +3 -0
  57. package/dist/utils/weaviate-filters.d.ts +19 -0
  58. package/dist/weaviate/client.d.ts +1 -1
  59. package/package.json +1 -1
  60. package/src/tools/confirm.ts +65 -1
  61. package/src/tools/create-relationship.ts +14 -1
  62. package/src/tools/delete-memory.ts +91 -63
  63. package/src/tools/find-similar.ts +30 -5
  64. package/src/tools/query-memory.ts +18 -5
  65. package/src/tools/search-memory.ts +18 -5
  66. package/src/tools/search-relationship.ts +19 -5
  67. package/src/tools/update-memory.ts +8 -0
  68. package/src/types/memory.ts +11 -0
  69. package/src/types/space-memory.ts +5 -0
  70. package/src/utils/weaviate-filters.ts +28 -1
  71. package/src/weaviate/client.ts +5 -0
  72. package/src/weaviate/schema.ts +17 -0
  73. package/src/weaviate/space-schema.spec.ts +5 -2
  74. package/src/weaviate/space-schema.ts +17 -5
@@ -1,47 +1,22 @@
1
1
  /**
2
2
  * remember_delete_memory tool
3
- * Delete a memory from the user's collection
3
+ * Request to delete a memory (requires confirmation)
4
4
  */
5
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
5
6
  /**
6
7
  * Tool definition for remember_delete_memory
7
8
  */
8
- export declare const deleteMemoryTool: {
9
- name: string;
10
- description: string;
11
- inputSchema: {
12
- type: string;
13
- properties: {
14
- memory_id: {
15
- type: string;
16
- description: string;
17
- };
18
- delete_relationships: {
19
- type: string;
20
- description: string;
21
- default: boolean;
22
- };
23
- };
24
- required: string[];
25
- };
26
- };
9
+ export declare const deleteMemoryTool: Tool;
27
10
  /**
28
11
  * Delete memory arguments
29
12
  */
30
13
  export interface DeleteMemoryArgs {
31
14
  memory_id: string;
32
- delete_relationships?: boolean;
33
- }
34
- /**
35
- * Delete memory result
36
- */
37
- export interface DeleteMemoryResult {
38
- memory_id: string;
39
- deleted: boolean;
40
- relationships_deleted?: number;
41
- message: string;
15
+ reason?: string;
42
16
  }
43
17
  /**
44
18
  * Handle remember_delete_memory tool
19
+ * Creates confirmation token and returns preview
45
20
  */
46
21
  export declare function handleDeleteMemory(args: DeleteMemoryArgs, userId: string): Promise<string>;
47
22
  //# sourceMappingURL=delete-memory.d.ts.map
@@ -2,7 +2,7 @@
2
2
  * remember_find_similar tool
3
3
  * Find similar memories using vector similarity search
4
4
  */
5
- import type { Memory } from '../types/memory.js';
5
+ import type { Memory, DeletedFilter } from '../types/memory.js';
6
6
  /**
7
7
  * Tool definition for remember_find_similar
8
8
  */
@@ -39,6 +39,12 @@ export declare const findSimilarTool: {
39
39
  description: string;
40
40
  default: boolean;
41
41
  };
42
+ deleted_filter: {
43
+ type: string;
44
+ enum: string[];
45
+ default: string;
46
+ description: string;
47
+ };
42
48
  };
43
49
  };
44
50
  };
@@ -51,6 +57,7 @@ export interface FindSimilarArgs {
51
57
  limit?: number;
52
58
  min_similarity?: number;
53
59
  include_relationships?: boolean;
60
+ deleted_filter?: DeletedFilter;
54
61
  }
55
62
  /**
56
63
  * Similar memory result
@@ -2,7 +2,7 @@
2
2
  * remember_query_memory tool
3
3
  * RAG (Retrieval-Augmented Generation) queries with natural language
4
4
  */
5
- import type { Memory, SearchFilters } from '../types/memory.js';
5
+ import type { Memory, SearchFilters, DeletedFilter } from '../types/memory.js';
6
6
  /**
7
7
  * Tool definition for remember_query_memory
8
8
  */
@@ -77,6 +77,12 @@ export declare const queryMemoryTool: {
77
77
  enum: string[];
78
78
  default: string;
79
79
  };
80
+ deleted_filter: {
81
+ type: string;
82
+ enum: string[];
83
+ default: string;
84
+ description: string;
85
+ };
80
86
  };
81
87
  required: string[];
82
88
  };
@@ -91,6 +97,7 @@ export interface QueryMemoryArgs {
91
97
  filters?: SearchFilters;
92
98
  include_context?: boolean;
93
99
  format?: 'detailed' | 'compact';
100
+ deleted_filter?: DeletedFilter;
94
101
  }
95
102
  /**
96
103
  * Memory with relevance score
@@ -77,6 +77,12 @@ export declare const searchMemoryTool: {
77
77
  description: string;
78
78
  default: boolean;
79
79
  };
80
+ deleted_filter: {
81
+ type: string;
82
+ enum: string[];
83
+ default: string;
84
+ description: string;
85
+ };
80
86
  };
81
87
  required: string[];
82
88
  };
@@ -2,7 +2,7 @@
2
2
  * remember_search_relationship tool
3
3
  * Search relationships by observation text or type
4
4
  */
5
- import type { Relationship } from '../types/memory.js';
5
+ import type { Relationship, DeletedFilter } from '../types/memory.js';
6
6
  /**
7
7
  * Tool definition for remember_search_relationship
8
8
  */
@@ -53,6 +53,12 @@ export declare const searchRelationshipTool: {
53
53
  description: string;
54
54
  minimum: number;
55
55
  };
56
+ deleted_filter: {
57
+ type: string;
58
+ enum: string[];
59
+ default: string;
60
+ description: string;
61
+ };
56
62
  };
57
63
  required: string[];
58
64
  };
@@ -68,6 +74,7 @@ export interface SearchRelationshipArgs {
68
74
  tags?: string[];
69
75
  limit?: number;
70
76
  offset?: number;
77
+ deleted_filter?: DeletedFilter;
71
78
  }
72
79
  /**
73
80
  * Search relationship result
@@ -114,6 +114,9 @@ export interface Memory {
114
114
  parent_id?: string | null;
115
115
  thread_root_id?: string | null;
116
116
  moderation_flags?: string[];
117
+ deleted_at?: Date | null;
118
+ deleted_by?: string;
119
+ deletion_reason?: string;
117
120
  }
118
121
  /**
119
122
  * Relationship interface
@@ -162,6 +165,10 @@ export interface SearchFilters {
162
165
  location_radius_meters?: number;
163
166
  has_relationships?: boolean;
164
167
  }
168
+ /**
169
+ * Deleted filter type
170
+ */
171
+ export type DeletedFilter = 'exclude' | 'include' | 'only';
165
172
  /**
166
173
  * Search options
167
174
  */
@@ -170,6 +177,7 @@ export interface SearchOptions {
170
177
  alpha?: number;
171
178
  filters?: SearchFilters;
172
179
  include_relationships?: boolean;
180
+ deleted_filter?: DeletedFilter;
173
181
  limit?: number;
174
182
  offset?: number;
175
183
  }
@@ -47,6 +47,9 @@ export interface SpaceMemory extends Omit<Memory, 'user_id' | 'doc_type'> {
47
47
  * Always 'space_memory' for space memories
48
48
  */
49
49
  doc_type: 'space_memory';
50
+ deleted_at?: Date | null;
51
+ deleted_by?: string;
52
+ deletion_reason?: string;
50
53
  }
51
54
  /**
52
55
  * Search options for space memories
@@ -5,6 +5,10 @@
5
5
  * Replaces old v2 filter format (path/operator/valueText) with v3 collection.filter.byProperty()
6
6
  */
7
7
  import type { SearchFilters } from '../types/memory.js';
8
+ /**
9
+ * Deleted filter options
10
+ */
11
+ export type DeletedFilter = 'exclude' | 'include' | 'only';
8
12
  /**
9
13
  * Build filters for searching both memories and relationships
10
14
  * Uses OR logic: (doc_type=memory AND memory_filters) OR (doc_type=relationship AND relationship_filters)
@@ -30,8 +34,23 @@ export declare function buildMemoryOnlyFilters(collection: any, filters?: Search
30
34
  * @returns Combined filter or undefined if no filters
31
35
  */
32
36
  export declare function buildRelationshipOnlyFilters(collection: any, filters?: SearchFilters): any;
37
+ /**
38
+ * Combine multiple filters with AND logic
39
+ *
40
+ * @param filters - Array of filter objects
41
+ * @returns Combined filter or undefined
42
+ */
43
+ export declare function combineFiltersWithAnd(filters: any[]): any;
33
44
  /**
34
45
  * Helper to check if a filter result is empty
35
46
  */
36
47
  export declare function hasFilters(filter: any): boolean;
48
+ /**
49
+ * Build filter for deleted_at field based on deleted_filter parameter
50
+ *
51
+ * @param collection - Weaviate collection instance
52
+ * @param deletedFilter - Filter mode: 'exclude' (default), 'include', or 'only'
53
+ * @returns Filter for deleted_at field, or null if no filter needed
54
+ */
55
+ export declare function buildDeletedFilter(collection: any, deletedFilter?: DeletedFilter): any | null;
37
56
  //# sourceMappingURL=weaviate-filters.d.ts.map
@@ -41,7 +41,7 @@ export declare function getAuditCollectionName(userId: string): string;
41
41
  * List of all memory properties to fetch
42
42
  * Matches the actual Weaviate schema properties exactly
43
43
  */
44
- export declare const ALL_MEMORY_PROPERTIES: readonly ["user_id", "doc_type", "content", "title", "summary", "type", "weight", "base_weight", "trust", "confidence", "computed_weight", "location_gps_lat", "location_gps_lng", "location_address", "location_city", "location_country", "location_source", "locale_language", "locale_timezone", "context_conversation_id", "context_summary", "context_timestamp", "relationships", "access_count", "last_accessed_at", "tags", "references", "created_at", "updated_at", "version", "template_id", "memory_ids", "relationship_type", "observation", "strength", "parent_id", "thread_root_id", "moderation_flags", "spaces", "space_id", "author_id", "ghost_id", "attribution", "published_at", "discovery_count", "space_memory_id"];
44
+ export declare const ALL_MEMORY_PROPERTIES: readonly ["user_id", "doc_type", "content", "title", "summary", "type", "weight", "base_weight", "trust", "confidence", "computed_weight", "location_gps_lat", "location_gps_lng", "location_address", "location_city", "location_country", "location_source", "locale_language", "locale_timezone", "context_conversation_id", "context_summary", "context_timestamp", "relationships", "access_count", "last_accessed_at", "tags", "references", "created_at", "updated_at", "version", "template_id", "memory_ids", "relationship_type", "observation", "strength", "parent_id", "thread_root_id", "moderation_flags", "spaces", "space_id", "author_id", "ghost_id", "attribution", "published_at", "discovery_count", "space_memory_id", "deleted_at", "deleted_by", "deletion_reason"];
45
45
  /**
46
46
  * Fetch a memory object by ID with all properties
47
47
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/remember-mcp",
3
- "version": "2.8.0",
3
+ "version": "3.0.0",
4
4
  "description": "Multi-tenant memory system MCP server with vector search and relationships",
5
5
  "main": "dist/server.js",
6
6
  "type": "module",
@@ -7,7 +7,7 @@
7
7
 
8
8
  import type { Tool } from '@modelcontextprotocol/sdk/types.js';
9
9
  import { confirmationTokenService, type ConfirmationRequest } from '../services/confirmation-token.service.js';
10
- import { getWeaviateClient, getMemoryCollectionName, fetchMemoryWithAllProperties } from '../weaviate/client.js';
10
+ import { getWeaviateClient, getMemoryCollectionName, fetchMemoryWithAllProperties, sanitizeUserId } from '../weaviate/client.js';
11
11
  import { ensurePublicCollection } from '../weaviate/space-schema.js';
12
12
  import { handleToolError } from '../utils/error-handler.js';
13
13
  import { logger } from '../utils/logger.js';
@@ -120,6 +120,11 @@ export async function handleConfirm(
120
120
  return await executePublishMemory(request, userId);
121
121
  }
122
122
 
123
+ // Handle delete_memory action
124
+ if (request.action === 'delete_memory') {
125
+ return await executeDeleteMemory(request, userId);
126
+ }
127
+
123
128
  // Add other action types here as needed
124
129
  // if (request.action === 'retract_memory') {
125
130
  // return await executeRetractMemory(request, userId);
@@ -380,3 +385,62 @@ async function executePublishMemory(
380
385
  });
381
386
  }
382
387
  }
388
+
389
+ /**
390
+ * Execute delete memory action
391
+ */
392
+ async function executeDeleteMemory(
393
+ request: ConfirmationRequest & { request_id: string },
394
+ userId: string
395
+ ): Promise<string> {
396
+ try {
397
+ logger.info('Executing delete memory action', {
398
+ function: 'executeDeleteMemory',
399
+ userId,
400
+ memoryId: request.payload.memory_id,
401
+ hasReason: !!request.payload.reason,
402
+ });
403
+
404
+ const { memory_id, reason } = request.payload;
405
+
406
+ // Soft delete the memory
407
+ const client = getWeaviateClient();
408
+ const collectionName = `Memory_${sanitizeUserId(userId)}`;
409
+ const collection = client.collections.get(collectionName);
410
+
411
+ await collection.data.update({
412
+ id: memory_id,
413
+ properties: {
414
+ deleted_at: new Date().toISOString(),
415
+ deleted_by: userId,
416
+ deletion_reason: reason || null,
417
+ },
418
+ });
419
+
420
+ logger.info('Memory soft-deleted successfully', {
421
+ function: 'executeDeleteMemory',
422
+ userId,
423
+ memoryId: memory_id,
424
+ deletedAt: new Date().toISOString(),
425
+ });
426
+
427
+ return JSON.stringify(
428
+ {
429
+ success: true,
430
+ memory_id,
431
+ message: 'Memory deleted successfully',
432
+ },
433
+ null,
434
+ 2
435
+ );
436
+ } catch (error) {
437
+ logger.error('Failed to execute delete memory', {
438
+ function: 'executeDeleteMemory',
439
+ userId,
440
+ memoryId: request.payload.memory_id,
441
+ error: error instanceof Error ? error.message : String(error),
442
+ stack: error instanceof Error ? error.stack : undefined,
443
+ });
444
+ throw error;
445
+ }
446
+ }
@@ -117,7 +117,7 @@ export async function handleCreateRelationship(
117
117
  args.memory_ids.map(async (memoryId) => {
118
118
  try {
119
119
  const memory = await collection.query.fetchObjectById(memoryId, {
120
- returnProperties: ['user_id', 'doc_type', 'relationships'],
120
+ returnProperties: ['user_id', 'doc_type', 'relationships', 'deleted_at'],
121
121
  });
122
122
 
123
123
  if (!memory) {
@@ -143,6 +143,19 @@ export async function handleCreateRelationship(
143
143
  return { memoryId, error: 'Cannot create relationship with non-memory document' };
144
144
  }
145
145
 
146
+ // Check if memory is deleted
147
+ if (memory.properties.deleted_at) {
148
+ const deletedAt = typeof memory.properties.deleted_at === 'string'
149
+ ? memory.properties.deleted_at
150
+ : new Date(memory.properties.deleted_at as any).toISOString();
151
+ logger.warn('Attempt to create relationship with deleted memory', {
152
+ userId,
153
+ memoryId,
154
+ deletedAt
155
+ });
156
+ return { memoryId, error: `Memory is deleted (deleted on ${deletedAt})` };
157
+ }
158
+
146
159
  return {
147
160
  memoryId,
148
161
  memory,
@@ -1,26 +1,32 @@
1
1
  /**
2
2
  * remember_delete_memory tool
3
- * Delete a memory from the user's collection
3
+ * Request to delete a memory (requires confirmation)
4
4
  */
5
5
 
6
- import { getMemoryCollection } from '../weaviate/schema.js';
6
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
7
+ import { Filters } from 'weaviate-client';
8
+ import { getWeaviateClient, sanitizeUserId, fetchMemoryWithAllProperties } from '../weaviate/client.js';
9
+ import { confirmationTokenService } from '../services/confirmation-token.service.js';
7
10
  import { logger } from '../utils/logger.js';
8
11
  import { handleToolError } from '../utils/error-handler.js';
9
12
 
10
13
  /**
11
14
  * Tool definition for remember_delete_memory
12
15
  */
13
- export const deleteMemoryTool = {
16
+ export const deleteMemoryTool: Tool = {
14
17
  name: 'remember_delete_memory',
15
- description: `Delete a memory from your collection.
18
+ description: `Request to delete a memory. Requires confirmation via remember_confirm.
16
19
 
17
- Optionally delete connected relationships as well.
18
- This action cannot be undone.
19
-
20
- Examples:
21
- - "Delete that old camping note"
22
- - "Remove the recipe I saved yesterday"
23
- `,
20
+ ⚠️ **IMPORTANT**: This is a two-step process:
21
+ 1. Call remember_delete_memory to request deletion (returns token)
22
+ 2. User must confirm via remember_confirm with the token
23
+
24
+ The memory will be soft-deleted (marked as deleted but not removed from database).
25
+
26
+ Examples:
27
+ - "Delete that old camping note"
28
+ - "Remove the recipe I saved yesterday"
29
+ `,
24
30
  inputSchema: {
25
31
  type: 'object',
26
32
  properties: {
@@ -28,10 +34,9 @@ export const deleteMemoryTool = {
28
34
  type: 'string',
29
35
  description: 'ID of the memory to delete',
30
36
  },
31
- delete_relationships: {
32
- type: 'boolean',
33
- description: 'Also delete connected relationships. Default: false',
34
- default: false,
37
+ reason: {
38
+ type: 'string',
39
+ description: 'Optional reason for deletion',
35
40
  },
36
41
  },
37
42
  required: ['memory_id'],
@@ -43,43 +48,39 @@ export const deleteMemoryTool = {
43
48
  */
44
49
  export interface DeleteMemoryArgs {
45
50
  memory_id: string;
46
- delete_relationships?: boolean;
47
- }
48
-
49
- /**
50
- * Delete memory result
51
- */
52
- export interface DeleteMemoryResult {
53
- memory_id: string;
54
- deleted: boolean;
55
- relationships_deleted?: number;
56
- message: string;
51
+ reason?: string;
57
52
  }
58
53
 
59
54
  /**
60
55
  * Handle remember_delete_memory tool
56
+ * Creates confirmation token and returns preview
61
57
  */
62
58
  export async function handleDeleteMemory(
63
59
  args: DeleteMemoryArgs,
64
60
  userId: string
65
61
  ): Promise<string> {
66
62
  try {
67
- logger.info('Deleting memory', { userId, memoryId: args.memory_id });
63
+ logger.info('Requesting memory deletion', {
64
+ userId,
65
+ memoryId: args.memory_id,
66
+ hasReason: !!args.reason,
67
+ });
68
68
 
69
- const collection = getMemoryCollection(userId);
69
+ const { memory_id, reason } = args;
70
+ const client = getWeaviateClient();
71
+ const collectionName = `Memory_${sanitizeUserId(userId)}`;
72
+ const collection = client.collections.get(collectionName);
70
73
 
71
- // Get memory to verify ownership and get relationships
72
- const memory = await collection.query.fetchObjectById(args.memory_id, {
73
- returnProperties: ['user_id', 'doc_type', 'relationships'],
74
- });
74
+ // Fetch memory to verify ownership and get preview
75
+ const memory = await fetchMemoryWithAllProperties(collection, memory_id);
75
76
 
76
77
  if (!memory) {
77
- throw new Error(`Memory not found: ${args.memory_id}`);
78
+ throw new Error(`Memory not found: ${memory_id}`);
78
79
  }
79
80
 
80
81
  // Verify ownership
81
82
  if (memory.properties.user_id !== userId) {
82
- throw new Error('Unauthorized: Cannot delete another user\'s memory');
83
+ throw new Error(`Cannot delete memory: not owned by user ${userId}`);
83
84
  }
84
85
 
85
86
  // Verify it's a memory (not a relationship)
@@ -87,46 +88,73 @@ export async function handleDeleteMemory(
87
88
  throw new Error('Cannot delete relationships using this tool. Use remember_delete_relationship instead.');
88
89
  }
89
90
 
90
- let relationshipsDeleted = 0;
91
-
92
- // Delete connected relationships if requested
93
- if (args.delete_relationships && memory.properties.relationships) {
94
- const relationshipIds = memory.properties.relationships as string[];
95
-
96
- for (const relId of relationshipIds) {
97
- try {
98
- await collection.data.deleteById(relId);
99
- relationshipsDeleted++;
100
- } catch (error) {
101
- logger.warn(`Failed to delete relationship ${relId}:`, error);
102
- }
103
- }
91
+ // Check if already deleted
92
+ if (memory.properties.deleted_at) {
93
+ throw new Error(`Memory ${memory_id} is already deleted`);
104
94
  }
105
95
 
106
- // Delete the memory
107
- await collection.data.deleteById(args.memory_id);
96
+ // Find relationships that will be orphaned
97
+ const relationshipsResult = await collection.query.fetchObjects({
98
+ filters: Filters.and(
99
+ collection.filter.byProperty('doc_type').equal('relationship'),
100
+ collection.filter.byProperty('memory_ids').containsAny([memory_id])
101
+ ),
102
+ limit: 100,
103
+ });
108
104
 
109
- logger.info('Memory deleted successfully', {
110
- userId,
111
- memoryId: args.memory_id,
112
- relationshipsDeleted
105
+ const orphanedRelationships = relationshipsResult.objects.map(r => r.uuid);
106
+
107
+ logger.info('Found relationships to orphan', {
108
+ userId,
109
+ memoryId: memory_id,
110
+ relationshipCount: orphanedRelationships.length,
113
111
  });
114
112
 
115
- const result: DeleteMemoryResult = {
116
- memory_id: args.memory_id,
117
- deleted: true,
118
- relationships_deleted: relationshipsDeleted,
119
- message: `Memory deleted successfully${relationshipsDeleted > 0 ? ` (${relationshipsDeleted} relationships also deleted)` : ''}`,
120
- };
113
+ // Create confirmation token
114
+ const { requestId, token } = await confirmationTokenService.createRequest(
115
+ userId,
116
+ 'delete_memory',
117
+ {
118
+ memory_id,
119
+ reason: reason || null,
120
+ }
121
+ );
122
+
123
+ // Calculate expiry time (5 minutes from now)
124
+ const expiresAt = new Date(Date.now() + 5 * 60 * 1000);
125
+
126
+ logger.info('Delete confirmation token created', {
127
+ userId,
128
+ memoryId: memory_id,
129
+ requestId,
130
+ token,
131
+ expiresAt: expiresAt.toISOString(),
132
+ });
121
133
 
122
- return JSON.stringify(result, null, 2);
134
+ // Return token and preview
135
+ return JSON.stringify(
136
+ {
137
+ success: true,
138
+ token,
139
+ expires_at: expiresAt.toISOString(),
140
+ preview: {
141
+ memory_id,
142
+ content: memory.properties.content?.substring(0, 200) + (memory.properties.content?.length > 200 ? '...' : ''),
143
+ type: memory.properties.type,
144
+ relationships_count: orphanedRelationships.length,
145
+ will_orphan: orphanedRelationships,
146
+ },
147
+ message: `Deletion requested. Use remember_confirm with token to complete deletion. Token expires in 5 minutes.`,
148
+ },
149
+ null,
150
+ 2
151
+ );
123
152
  } catch (error) {
124
153
  handleToolError(error, {
125
154
  toolName: 'remember_delete_memory',
126
- operation: 'delete memory',
127
155
  userId,
156
+ operation: 'request delete',
128
157
  memoryId: args.memory_id,
129
- deleteRelationships: args.delete_relationships,
130
158
  });
131
159
  }
132
160
  }
@@ -3,10 +3,11 @@
3
3
  * Find similar memories using vector similarity search
4
4
  */
5
5
 
6
- import type { Memory } from '../types/memory.js';
6
+ import type { Memory, DeletedFilter } 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 { buildDeletedFilter } from '../utils/weaviate-filters.js';
10
11
 
11
12
  /**
12
13
  * Tool definition for remember_find_similar
@@ -54,6 +55,12 @@ export const findSimilarTool = {
54
55
  description: 'Include relationships in results. Default: false',
55
56
  default: false,
56
57
  },
58
+ deleted_filter: {
59
+ type: 'string',
60
+ enum: ['exclude', 'include', 'only'],
61
+ default: 'exclude',
62
+ description: 'Filter deleted memories: "exclude" (default, hide deleted), "include" (show all), "only" (show only deleted)',
63
+ },
57
64
  },
58
65
  },
59
66
  };
@@ -67,6 +74,7 @@ export interface FindSimilarArgs {
67
74
  limit?: number;
68
75
  min_similarity?: number;
69
76
  include_relationships?: boolean;
77
+ deleted_filter?: DeletedFilter;
70
78
  }
71
79
 
72
80
  /**
@@ -112,6 +120,9 @@ export async function handleFindSimilar(
112
120
  const limit = args.limit ?? 10;
113
121
  const minSimilarity = args.min_similarity ?? 0.7;
114
122
 
123
+ // Build deleted filter
124
+ const deletedFilter = buildDeletedFilter(collection, args.deleted_filter || 'exclude');
125
+
115
126
  let results: any;
116
127
 
117
128
  if (args.memory_id) {
@@ -136,21 +147,35 @@ export async function handleFindSimilar(
136
147
  }
137
148
 
138
149
  // Find similar using nearObject
139
- results = await collection.query.nearObject(args.memory_id, {
150
+ const searchOptions: any = {
140
151
  limit: limit + 1, // +1 to exclude the source memory itself
141
152
  distance: 1 - minSimilarity, // Convert similarity to distance
142
153
  returnMetadata: ['distance'],
143
- });
154
+ };
155
+
156
+ // Add deleted filter if present
157
+ if (deletedFilter) {
158
+ searchOptions.filters = deletedFilter;
159
+ }
160
+
161
+ results = await collection.query.nearObject(args.memory_id, searchOptions);
144
162
 
145
163
  // Filter out the source memory
146
164
  results.objects = results.objects.filter((obj: any) => obj.uuid !== args.memory_id);
147
165
  } else {
148
166
  // Find similar to text
149
- results = await collection.query.nearText(args.text!, {
167
+ const searchOptions: any = {
150
168
  limit: limit,
151
169
  distance: 1 - minSimilarity,
152
170
  returnMetadata: ['distance'],
153
- });
171
+ };
172
+
173
+ // Add deleted filter if present
174
+ if (deletedFilter) {
175
+ searchOptions.filters = deletedFilter;
176
+ }
177
+
178
+ results = await collection.query.nearText(args.text!, searchOptions);
154
179
  }
155
180
 
156
181
  // Filter to only memories (not relationships) unless requested