@prmichaelsen/remember-mcp 2.8.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT.md +296 -250
- package/CHANGELOG.md +468 -0
- package/README.md +163 -46
- package/agent/commands/acp.clarification-create.md +382 -0
- package/agent/commands/acp.command-create.md +0 -1
- package/agent/commands/acp.design-create.md +0 -1
- package/agent/commands/acp.init.md +0 -1
- package/agent/commands/acp.package-create.md +0 -1
- package/agent/commands/acp.package-info.md +0 -1
- package/agent/commands/acp.package-install.md +0 -1
- package/agent/commands/acp.package-list.md +0 -1
- package/agent/commands/acp.package-publish.md +0 -1
- package/agent/commands/acp.package-remove.md +0 -1
- package/agent/commands/acp.package-search.md +0 -1
- package/agent/commands/acp.package-update.md +0 -1
- package/agent/commands/acp.package-validate.md +0 -1
- package/agent/commands/acp.pattern-create.md +0 -1
- package/agent/commands/acp.plan.md +0 -1
- package/agent/commands/acp.proceed.md +0 -1
- package/agent/commands/acp.project-create.md +0 -1
- package/agent/commands/acp.project-info.md +309 -0
- package/agent/commands/acp.project-list.md +0 -1
- package/agent/commands/acp.project-remove.md +379 -0
- package/agent/commands/acp.project-set.md +0 -1
- package/agent/commands/acp.project-update.md +296 -0
- package/agent/commands/acp.report.md +0 -1
- package/agent/commands/acp.resume.md +0 -1
- package/agent/commands/acp.status.md +0 -1
- package/agent/commands/acp.sync.md +0 -1
- package/agent/commands/acp.task-create.md +17 -10
- package/agent/commands/acp.update.md +0 -1
- package/agent/commands/acp.validate.md +0 -1
- package/agent/commands/acp.version-check-for-updates.md +0 -1
- package/agent/commands/acp.version-check.md +0 -1
- package/agent/commands/acp.version-update.md +0 -1
- package/agent/commands/command.template.md +0 -5
- package/agent/commands/git.commit.md +13 -2
- package/agent/commands/git.init.md +0 -1
- package/agent/design/comment-memory-type.md +2 -2
- package/agent/design/local.collaborative-memory-sync.md +265 -0
- package/agent/design/local.content-flags.md +210 -0
- package/agent/design/local.ghost-persona-system.md +273 -0
- package/agent/design/local.group-acl-integration.md +338 -0
- package/agent/design/local.memory-acl-schema.md +352 -0
- package/agent/design/local.memory-collection-pattern-v2.md +348 -0
- package/agent/design/local.moderation-and-space-config.md +257 -0
- package/agent/design/local.v2-api-reference.md +621 -0
- package/agent/design/local.v2-migration-guide.md +191 -0
- package/agent/design/local.v2-usage-examples.md +265 -0
- package/agent/design/permissions-storage-architecture.md +11 -3
- package/agent/design/soft-delete-system.md +291 -0
- package/agent/design/trust-escalation-prevention.md +9 -2
- package/agent/design/trust-system-implementation.md +12 -3
- package/agent/milestones/milestone-13-soft-delete-system.md +306 -0
- package/agent/milestones/milestone-14-memory-collection-v2.md +182 -0
- package/agent/milestones/milestone-15-moderation-space-config.md +126 -0
- package/agent/package.template.yaml +0 -17
- package/agent/progress.yaml +762 -49
- package/agent/scripts/acp.common.sh +2 -0
- package/agent/scripts/acp.install.sh +15 -85
- package/agent/scripts/acp.package-install-optimized.sh +454 -0
- package/agent/scripts/acp.package-install.sh +248 -380
- package/agent/scripts/acp.package-validate.sh +0 -99
- package/agent/scripts/acp.project-info.sh +218 -0
- package/agent/scripts/acp.project-remove.sh +302 -0
- package/agent/scripts/acp.project-update.sh +296 -0
- package/agent/scripts/acp.yaml-parser.sh +128 -10
- package/agent/tasks/milestone-14-memory-collection-v2/task-165-core-infrastructure-setup.md +171 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-166-update-remember-publish.md +191 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-167-update-remember-retract.md +186 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-168-implement-remember-revise.md +184 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-169-update-remember-search-space.md +179 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-170-update-remember-create-update.md +139 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-172-performance-testing-optimization.md +161 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-173-documentation-examples.md +258 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-174-add-moderation-schema-fields.md +57 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-175-create-space-config-service.md +64 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-176-wire-moderation-publish-flow.md +45 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-177-add-moderation-search-filters.md +70 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-178-create-remember-moderate-tool.md +69 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-179-documentation-integration-tests.md +58 -0
- package/agent/tasks/milestone-16-ghost-system/task-187-ghost-config-firestore.md +41 -0
- package/agent/tasks/milestone-16-ghost-system/task-188-trust-filter-integration.md +44 -0
- package/agent/tasks/milestone-16-ghost-system/task-189-ghost-memory-filtering.md +43 -0
- package/agent/tasks/milestone-16-ghost-system/task-190-ghost-config-tools.md +45 -0
- package/agent/tasks/milestone-16-ghost-system/task-191-escalation-firestore.md +38 -0
- package/agent/tasks/milestone-16-ghost-system/task-192-documentation-verification.md +39 -0
- package/agent/tasks/milestone-7-trust-permissions/task-180-access-result-permission-types.md +69 -0
- package/agent/tasks/milestone-7-trust-permissions/task-181-firestore-permissions-access-logs.md +56 -0
- package/agent/tasks/milestone-7-trust-permissions/task-182-trust-enforcement-service.md +68 -0
- package/agent/tasks/milestone-7-trust-permissions/task-183-access-control-service.md +70 -0
- package/agent/tasks/milestone-7-trust-permissions/task-184-permission-tools.md +79 -0
- package/agent/tasks/milestone-7-trust-permissions/task-185-wire-trust-into-search-query.md +55 -0
- package/agent/tasks/milestone-7-trust-permissions/task-186-documentation-verification.md +56 -0
- package/agent/tasks/task-70-add-soft-delete-schema-fields.md +165 -0
- package/agent/tasks/task-71-implement-delete-confirmation-flow.md +257 -0
- package/agent/tasks/task-72-add-deleted-filter-to-search-tools.md +18 -0
- package/agent/tasks/task-73-update-relationship-handling.md +18 -0
- package/agent/tasks/task-74-add-unit-tests-soft-delete.md +18 -0
- package/agent/tasks/task-75-update-documentation-changelog.md +26 -0
- package/agent/tasks/task-76-fix-indexnullstate-schema-bug.md +197 -0
- package/dist/collections/composite-ids.d.ts +106 -0
- package/dist/collections/core-infrastructure.spec.d.ts +11 -0
- package/dist/collections/dot-notation.d.ts +106 -0
- package/dist/collections/tracking-arrays.d.ts +176 -0
- package/dist/constants/content-types.d.ts +1 -0
- package/dist/schema/v2-collections-comments.spec.d.ts +8 -0
- package/dist/schema/v2-collections.d.ts +210 -0
- package/dist/server-factory.d.ts +15 -0
- package/dist/server-factory.js +3261 -1316
- package/dist/server.js +2926 -1236
- package/dist/services/access-control.d.ts +103 -0
- package/dist/services/access-control.spec.d.ts +2 -0
- package/dist/services/credentials-provider.d.ts +24 -0
- package/dist/services/credentials-provider.spec.d.ts +2 -0
- package/dist/services/escalation.service.d.ts +22 -0
- package/dist/services/escalation.service.spec.d.ts +2 -0
- package/dist/services/ghost-config.service.d.ts +55 -0
- package/dist/services/ghost-config.service.spec.d.ts +2 -0
- package/dist/services/space-config.service.d.ts +23 -0
- package/dist/services/space-config.service.spec.d.ts +2 -0
- package/dist/services/trust-enforcement.d.ts +83 -0
- package/dist/services/trust-enforcement.spec.d.ts +2 -0
- package/dist/services/trust-validator.d.ts +43 -0
- package/dist/services/trust-validator.spec.d.ts +2 -0
- package/dist/tools/confirm-publish-moderation.spec.d.ts +8 -0
- package/dist/tools/confirm.d.ts +8 -1
- package/dist/tools/create-memory.d.ts +2 -1
- package/dist/tools/create-memory.spec.d.ts +10 -0
- package/dist/tools/create-relationship.d.ts +2 -1
- package/dist/tools/delete-memory.d.ts +7 -31
- package/dist/tools/delete-relationship.d.ts +2 -1
- package/dist/tools/deny.d.ts +2 -1
- package/dist/tools/find-similar.d.ts +10 -2
- package/dist/tools/get-preferences.d.ts +2 -1
- package/dist/tools/ghost-config.d.ts +27 -0
- package/dist/tools/ghost-config.spec.d.ts +2 -0
- package/dist/tools/moderate.d.ts +20 -0
- package/dist/tools/moderate.spec.d.ts +5 -0
- package/dist/tools/publish.d.ts +11 -3
- package/dist/tools/query-memory.d.ts +11 -2
- package/dist/tools/query-space.d.ts +4 -1
- package/dist/tools/retract.d.ts +29 -0
- package/dist/tools/revise.d.ts +45 -0
- package/dist/tools/revise.spec.d.ts +8 -0
- package/dist/tools/search-memory.d.ts +8 -1
- package/dist/tools/search-relationship.d.ts +10 -2
- package/dist/tools/search-space.d.ts +25 -5
- package/dist/tools/search-space.spec.d.ts +9 -0
- package/dist/tools/set-preference.d.ts +2 -1
- package/dist/tools/update-memory.d.ts +2 -1
- package/dist/tools/update-relationship.d.ts +2 -1
- package/dist/types/access-result.d.ts +48 -0
- package/dist/types/access-result.spec.d.ts +2 -0
- package/dist/types/auth.d.ts +46 -0
- package/dist/types/ghost-config.d.ts +36 -0
- package/dist/types/memory.d.ts +11 -1
- package/dist/types/preferences.d.ts +1 -1
- package/dist/types/space-memory.d.ts +3 -0
- package/dist/utils/auth-helpers.d.ts +14 -0
- package/dist/utils/auth-helpers.spec.d.ts +2 -0
- package/dist/utils/test-data-generator.d.ts +124 -0
- package/dist/utils/test-data-generator.spec.d.ts +12 -0
- package/dist/utils/weaviate-filters.d.ts +19 -0
- package/dist/v2-performance.e2e.d.ts +17 -0
- package/dist/v2-smoke.e2e.d.ts +14 -0
- package/dist/weaviate/client.d.ts +5 -8
- package/dist/weaviate/space-schema.d.ts +2 -2
- package/docs/performance/v2-benchmarks.md +80 -0
- package/jest.e2e.config.js +14 -3
- package/package.json +1 -1
- package/scripts/.collection-recreation-state.yaml +16 -0
- package/scripts/.gitkeep +5 -0
- package/scripts/README-collection-recreation.md +224 -0
- package/scripts/README.md +51 -0
- package/scripts/backup-collections.ts +543 -0
- package/scripts/delete-collection.ts +137 -0
- package/scripts/migrate-recreate-collections.ts +578 -0
- package/scripts/migrate-v1-to-v2.ts +1094 -0
- package/scripts/package-lock.json +1113 -0
- package/scripts/package.json +27 -0
- package/src/collections/composite-ids.ts +193 -0
- package/src/collections/core-infrastructure.spec.ts +353 -0
- package/src/collections/dot-notation.ts +212 -0
- package/src/collections/tracking-arrays.ts +298 -0
- package/src/constants/content-types.ts +20 -0
- package/src/schema/v2-collections-comments.spec.ts +141 -0
- package/src/schema/v2-collections.ts +433 -0
- package/src/server-factory.ts +89 -20
- package/src/server.ts +45 -17
- package/src/services/access-control.spec.ts +383 -0
- package/src/services/access-control.ts +291 -0
- package/src/services/credentials-provider.spec.ts +22 -0
- package/src/services/credentials-provider.ts +34 -0
- package/src/services/escalation.service.spec.ts +183 -0
- package/src/services/escalation.service.ts +150 -0
- package/src/services/ghost-config.service.spec.ts +339 -0
- package/src/services/ghost-config.service.ts +219 -0
- package/src/services/space-config.service.spec.ts +102 -0
- package/src/services/space-config.service.ts +79 -0
- package/src/services/trust-enforcement.spec.ts +309 -0
- package/src/services/trust-enforcement.ts +197 -0
- package/src/services/trust-validator.spec.ts +108 -0
- package/src/services/trust-validator.ts +105 -0
- package/src/tools/confirm-publish-moderation.spec.ts +240 -0
- package/src/tools/confirm.ts +914 -116
- package/src/tools/create-memory.spec.ts +126 -0
- package/src/tools/create-memory.ts +20 -27
- package/src/tools/create-relationship.ts +30 -8
- package/src/tools/delete-memory.ts +99 -64
- package/src/tools/delete-relationship.ts +15 -6
- package/src/tools/deny.ts +8 -1
- package/src/tools/find-similar.ts +44 -6
- package/src/tools/get-preferences.ts +10 -1
- package/src/tools/ghost-config.spec.ts +180 -0
- package/src/tools/ghost-config.ts +230 -0
- package/src/tools/moderate.spec.ts +277 -0
- package/src/tools/moderate.ts +219 -0
- package/src/tools/publish.ts +99 -41
- package/src/tools/query-memory.ts +44 -9
- package/src/tools/query-space.ts +39 -4
- package/src/tools/retract.ts +292 -0
- package/src/tools/revise.spec.ts +146 -0
- package/src/tools/revise.ts +283 -0
- package/src/tools/search-memory.ts +46 -10
- package/src/tools/search-relationship.ts +30 -7
- package/src/tools/search-space.spec.ts +341 -0
- package/src/tools/search-space.ts +323 -99
- package/src/tools/set-preference.ts +10 -1
- package/src/tools/update-memory.ts +24 -5
- package/src/tools/update-relationship.ts +10 -1
- package/src/types/access-result.spec.ts +193 -0
- package/src/types/access-result.ts +62 -0
- package/src/types/auth.ts +52 -0
- package/src/types/ghost-config.ts +46 -0
- package/src/types/memory.ts +20 -1
- package/src/types/preferences.ts +2 -2
- package/src/types/space-memory.ts +5 -0
- package/src/utils/auth-helpers.spec.ts +75 -0
- package/src/utils/auth-helpers.ts +25 -0
- package/src/utils/test-data-generator.spec.ts +317 -0
- package/src/utils/test-data-generator.ts +292 -0
- package/src/utils/weaviate-filters.ts +32 -5
- package/src/v2-performance.e2e.ts +173 -0
- package/src/v2-smoke.e2e.ts +401 -0
- package/src/weaviate/client.spec.ts +5 -5
- package/src/weaviate/client.ts +55 -35
- package/src/weaviate/schema.ts +11 -239
- package/src/weaviate/space-schema.spec.ts +28 -25
- package/src/weaviate/space-schema.ts +35 -11
package/src/tools/confirm.ts
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Generic confirmation tool that executes any pending action.
|
|
5
5
|
* This is the second phase of the confirmation workflow.
|
|
6
|
+
*
|
|
7
|
+
* Memory Collection Pattern v2:
|
|
8
|
+
* - Multi-space publication to Memory_spaces_public
|
|
9
|
+
* - Multi-group publication to Memory_groups_{groupId}
|
|
10
|
+
* - Composite IDs ({userId}.{memoryId}) for published memories
|
|
11
|
+
* - Tracking arrays (space_ids, group_ids) on source and published memories
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
14
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -12,6 +18,12 @@ import { ensurePublicCollection } from '../weaviate/space-schema.js';
|
|
|
12
18
|
import { handleToolError } from '../utils/error-handler.js';
|
|
13
19
|
import { logger } from '../utils/logger.js';
|
|
14
20
|
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
|
+
import type { AuthContext } from '../types/auth.js';
|
|
26
|
+
import { getSpaceConfig } from '../services/space-config.service.js';
|
|
15
27
|
|
|
16
28
|
/**
|
|
17
29
|
* Tool definition for remember_confirm
|
|
@@ -62,7 +74,8 @@ interface ConfirmArgs {
|
|
|
62
74
|
*/
|
|
63
75
|
export async function handleConfirm(
|
|
64
76
|
args: ConfirmArgs,
|
|
65
|
-
userId: string
|
|
77
|
+
userId: string,
|
|
78
|
+
authContext?: AuthContext
|
|
66
79
|
): Promise<string> {
|
|
67
80
|
const debug = createDebugLogger({
|
|
68
81
|
tool: 'remember_confirm',
|
|
@@ -120,10 +133,20 @@ export async function handleConfirm(
|
|
|
120
133
|
return await executePublishMemory(request, userId);
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
136
|
+
// Handle delete_memory action
|
|
137
|
+
if (request.action === 'delete_memory') {
|
|
138
|
+
return await executeDeleteMemory(request, userId);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Handle retract_memory action
|
|
142
|
+
if (request.action === 'retract_memory') {
|
|
143
|
+
return await executeRetractMemory(request, userId);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Handle revise_memory action
|
|
147
|
+
if (request.action === 'revise_memory') {
|
|
148
|
+
return await executeReviseMemory(request, userId);
|
|
149
|
+
}
|
|
127
150
|
|
|
128
151
|
throw new Error(`Unknown action type: ${request.action}`);
|
|
129
152
|
} catch (error) {
|
|
@@ -142,10 +165,17 @@ export async function handleConfirm(
|
|
|
142
165
|
|
|
143
166
|
/**
|
|
144
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
|
|
145
174
|
*/
|
|
146
175
|
async function executePublishMemory(
|
|
147
176
|
request: ConfirmationRequest & { request_id: string },
|
|
148
|
-
userId: string
|
|
177
|
+
userId: string,
|
|
178
|
+
authContext?: AuthContext
|
|
149
179
|
): Promise<string> {
|
|
150
180
|
const debug = createDebugLogger({
|
|
151
181
|
tool: 'remember_confirm',
|
|
@@ -154,28 +184,47 @@ async function executePublishMemory(
|
|
|
154
184
|
});
|
|
155
185
|
|
|
156
186
|
try {
|
|
187
|
+
// Normalize arrays (handle undefined)
|
|
188
|
+
const spaces = request.payload.spaces || [];
|
|
189
|
+
const groups = request.payload.groups || [];
|
|
190
|
+
|
|
157
191
|
debug.debug('Executing publish memory action', {
|
|
158
192
|
memoryId: request.payload.memory_id,
|
|
159
|
-
spaces
|
|
193
|
+
spaces,
|
|
194
|
+
groups,
|
|
160
195
|
});
|
|
161
196
|
|
|
162
197
|
logger.info('Executing publish memory action', {
|
|
163
198
|
function: 'executePublishMemory',
|
|
164
199
|
userId,
|
|
165
200
|
memoryId: request.payload.memory_id,
|
|
166
|
-
spaces
|
|
167
|
-
|
|
201
|
+
spaces,
|
|
202
|
+
groups,
|
|
203
|
+
spaceCount: spaces.length,
|
|
204
|
+
groupCount: groups.length,
|
|
168
205
|
});
|
|
169
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
|
+
|
|
170
220
|
// Fetch the memory NOW (during confirmation, not from stored payload)
|
|
171
221
|
const weaviateClient = getWeaviateClient();
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
);
|
|
222
|
+
const userCollectionName = getMemoryCollectionName(userId);
|
|
223
|
+
const userCollection = weaviateClient.collections.get(userCollectionName);
|
|
175
224
|
|
|
176
225
|
logger.debug('Fetching original memory', {
|
|
177
226
|
function: 'executePublishMemory',
|
|
178
|
-
collectionName:
|
|
227
|
+
collectionName: userCollectionName,
|
|
179
228
|
memoryId: request.payload.memory_id,
|
|
180
229
|
});
|
|
181
230
|
|
|
@@ -192,14 +241,6 @@ async function executePublishMemory(
|
|
|
192
241
|
memoryId: request.payload.memory_id,
|
|
193
242
|
hasProperties: !!originalMemory?.properties,
|
|
194
243
|
propertyCount: originalMemory?.properties ? Object.keys(originalMemory.properties).length : 0,
|
|
195
|
-
propertyKeys: originalMemory?.properties ? Object.keys(originalMemory.properties) : [],
|
|
196
|
-
hasTitle: !!originalMemory?.properties?.title,
|
|
197
|
-
hasContent: !!originalMemory?.properties?.content,
|
|
198
|
-
hasUserId: !!originalMemory?.properties?.user_id,
|
|
199
|
-
hasTags: !!originalMemory?.properties?.tags,
|
|
200
|
-
hasWeight: !!originalMemory?.properties?.weight,
|
|
201
|
-
contentLength: originalMemory?.properties?.content?.length || 0,
|
|
202
|
-
titleValue: originalMemory?.properties?.title || 'NO_TITLE',
|
|
203
244
|
});
|
|
204
245
|
|
|
205
246
|
if (!originalMemory) {
|
|
@@ -218,28 +259,6 @@ async function executePublishMemory(
|
|
|
218
259
|
);
|
|
219
260
|
}
|
|
220
261
|
|
|
221
|
-
// Check if memory has already been published
|
|
222
|
-
if (originalMemory.properties.space_memory_id) {
|
|
223
|
-
const requestedSpaces = request.payload.spaces?.join(', ') || 'unknown';
|
|
224
|
-
logger.warn('Memory already published', {
|
|
225
|
-
function: 'executePublishMemory',
|
|
226
|
-
memoryId: request.payload.memory_id,
|
|
227
|
-
existingSpaceMemoryId: originalMemory.properties.space_memory_id,
|
|
228
|
-
requestedSpaces: request.payload.spaces,
|
|
229
|
-
});
|
|
230
|
-
return JSON.stringify(
|
|
231
|
-
{
|
|
232
|
-
success: false,
|
|
233
|
-
error: 'Already published',
|
|
234
|
-
message: `This memory has already been published to this space. Space memory ID: ${originalMemory.properties.space_memory_id}`,
|
|
235
|
-
space_memory_id: originalMemory.properties.space_memory_id,
|
|
236
|
-
requested_spaces: request.payload.spaces,
|
|
237
|
-
},
|
|
238
|
-
null,
|
|
239
|
-
2
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
262
|
// Verify ownership again
|
|
244
263
|
if (originalMemory.properties.user_id !== userId) {
|
|
245
264
|
logger.warn('Permission denied - wrong owner', {
|
|
@@ -258,125 +277,904 @@ async function executePublishMemory(
|
|
|
258
277
|
2
|
|
259
278
|
);
|
|
260
279
|
}
|
|
261
|
-
|
|
262
|
-
logger.debug('Ensuring public collection exists', {
|
|
263
|
-
function: 'executePublishMemory',
|
|
264
|
-
});
|
|
265
280
|
|
|
266
|
-
//
|
|
267
|
-
const
|
|
281
|
+
// Generate composite ID for published memories
|
|
282
|
+
const compositeId = generateCompositeId(userId, request.payload.memory_id);
|
|
268
283
|
|
|
269
|
-
logger.debug('
|
|
284
|
+
logger.debug('Generated composite ID', {
|
|
270
285
|
function: 'executePublishMemory',
|
|
271
|
-
|
|
286
|
+
compositeId,
|
|
287
|
+
userId,
|
|
288
|
+
memoryId: request.payload.memory_id,
|
|
272
289
|
});
|
|
273
|
-
|
|
274
|
-
//
|
|
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
|
|
275
300
|
const originalTags = Array.isArray(originalMemory.properties.tags)
|
|
276
301
|
? originalMemory.properties.tags
|
|
277
302
|
: [];
|
|
278
303
|
const additionalTags = Array.isArray(request.payload.additional_tags)
|
|
279
304
|
? request.payload.additional_tags
|
|
280
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])];
|
|
281
332
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
+
}
|
|
285
406
|
}
|
|
286
407
|
|
|
287
|
-
//
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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,
|
|
509
|
+
},
|
|
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,
|
|
306
600
|
memoryId: request.payload.memory_id,
|
|
307
|
-
|
|
308
|
-
hasAuthorId: !!publishedMemory.author_id,
|
|
309
|
-
publishedMemoryKeys: Object.keys(publishedMemory),
|
|
310
|
-
publishedMemoryKeyCount: Object.keys(publishedMemory).length,
|
|
311
|
-
hasContent: !!publishedMemory.content,
|
|
312
|
-
hasTitle: !!publishedMemory.title,
|
|
313
|
-
contentLength: publishedMemory.content?.length || 0,
|
|
314
|
-
titleValue: publishedMemory.title || 'NO_TITLE',
|
|
601
|
+
hasReason: !!request.payload.reason,
|
|
315
602
|
});
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
+
},
|
|
323
618
|
});
|
|
324
|
-
|
|
325
|
-
logger.info('Memory
|
|
326
|
-
function: '
|
|
327
|
-
|
|
328
|
-
|
|
619
|
+
|
|
620
|
+
logger.info('Memory soft-deleted successfully', {
|
|
621
|
+
function: 'executeDeleteMemory',
|
|
622
|
+
userId,
|
|
623
|
+
memoryId: memory_id,
|
|
624
|
+
deletedAt: new Date().toISOString(),
|
|
329
625
|
});
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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,
|
|
334
687
|
});
|
|
335
688
|
|
|
336
|
-
//
|
|
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)
|
|
738
|
+
);
|
|
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
|
+
}
|
|
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
|
+
|
|
801
|
+
if (groupMemory) {
|
|
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
|
+
});
|
|
818
|
+
|
|
819
|
+
retractionResults.groups.push({ groupId, success: true });
|
|
820
|
+
|
|
821
|
+
logger.info('Memory retracted from group (orphaned)', {
|
|
822
|
+
function: 'executeRetractMemory',
|
|
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
|
+
}
|
|
854
|
+
|
|
855
|
+
// STEP 3: Update source memory tracking arrays
|
|
856
|
+
const finalSpaceIds = retractionResults.spaces?.success
|
|
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
|
|
337
866
|
try {
|
|
338
867
|
await userCollection.data.update({
|
|
339
868
|
id: request.payload.memory_id,
|
|
340
869
|
properties: {
|
|
341
|
-
|
|
870
|
+
space_ids: finalSpaceIds,
|
|
871
|
+
group_ids: finalGroupIds,
|
|
342
872
|
},
|
|
343
873
|
});
|
|
344
|
-
|
|
345
|
-
logger.info('Updated
|
|
346
|
-
function: '
|
|
874
|
+
|
|
875
|
+
logger.info('Updated source memory tracking arrays after retraction', {
|
|
876
|
+
function: 'executeRetractMemory',
|
|
347
877
|
memoryId: request.payload.memory_id,
|
|
348
|
-
|
|
878
|
+
spaceIds: finalSpaceIds,
|
|
879
|
+
groupIds: finalGroupIds,
|
|
349
880
|
});
|
|
350
881
|
} catch (updateError) {
|
|
351
|
-
logger.warn('Failed to update
|
|
352
|
-
function: '
|
|
882
|
+
logger.warn('Failed to update source memory tracking arrays after retraction', {
|
|
883
|
+
function: 'executeRetractMemory',
|
|
353
884
|
memoryId: request.payload.memory_id,
|
|
354
|
-
spaceMemoryId: result,
|
|
355
885
|
error: updateError instanceof Error ? updateError.message : String(updateError),
|
|
356
886
|
});
|
|
357
|
-
// Don't fail the
|
|
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
|
+
return JSON.stringify(
|
|
913
|
+
{
|
|
914
|
+
success: true,
|
|
915
|
+
composite_id: compositeId,
|
|
916
|
+
retracted_from: successfulRetractions,
|
|
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,
|
|
932
|
+
},
|
|
933
|
+
null,
|
|
934
|
+
2
|
|
935
|
+
);
|
|
936
|
+
}
|
|
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
|
+
|
|
988
|
+
// Fetch source memory
|
|
989
|
+
const weaviateClient = getWeaviateClient();
|
|
990
|
+
const userCollectionName = getMemoryCollectionName(userId);
|
|
991
|
+
const userCollection = weaviateClient.collections.get(userCollectionName);
|
|
992
|
+
|
|
993
|
+
const sourceMemory = await fetchMemoryWithAllProperties(
|
|
994
|
+
userCollection,
|
|
995
|
+
memory_id
|
|
996
|
+
);
|
|
997
|
+
|
|
998
|
+
if (!sourceMemory) {
|
|
999
|
+
return JSON.stringify(
|
|
1000
|
+
{
|
|
1001
|
+
success: false,
|
|
1002
|
+
error: 'Memory not found',
|
|
1003
|
+
message: `Source memory ${memory_id} no longer exists`,
|
|
1004
|
+
},
|
|
1005
|
+
null,
|
|
1006
|
+
2
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Verify ownership again
|
|
1011
|
+
if (sourceMemory.properties.user_id !== userId) {
|
|
1012
|
+
return JSON.stringify(
|
|
1013
|
+
{
|
|
1014
|
+
success: false,
|
|
1015
|
+
error: 'Permission denied',
|
|
1016
|
+
message: 'You can only revise your own memories',
|
|
1017
|
+
},
|
|
1018
|
+
null,
|
|
1019
|
+
2
|
|
1020
|
+
);
|
|
358
1021
|
}
|
|
359
1022
|
|
|
360
|
-
|
|
1023
|
+
const newContent = String(sourceMemory.properties.content ?? '');
|
|
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
|
+
|
|
361
1145
|
return JSON.stringify(
|
|
362
1146
|
{
|
|
363
|
-
success:
|
|
364
|
-
|
|
365
|
-
|
|
1147
|
+
success: successCount > 0,
|
|
1148
|
+
composite_id: compositeId,
|
|
1149
|
+
revised_at: revisedAt,
|
|
1150
|
+
summary: {
|
|
1151
|
+
total: results.length,
|
|
1152
|
+
success: successCount,
|
|
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
|
+
: {}),
|
|
366
1164
|
},
|
|
367
1165
|
null,
|
|
368
1166
|
2
|
|
369
1167
|
);
|
|
370
1168
|
} catch (error) {
|
|
371
|
-
debug.error('Execute
|
|
1169
|
+
debug.error('Execute revise failed', {
|
|
372
1170
|
error: error instanceof Error ? error.message : String(error),
|
|
373
1171
|
stack: error instanceof Error ? error.stack : undefined,
|
|
374
1172
|
});
|
|
375
1173
|
handleToolError(error, {
|
|
376
1174
|
toolName: 'remember_confirm',
|
|
377
1175
|
userId,
|
|
378
|
-
operation: 'execute
|
|
379
|
-
action: '
|
|
1176
|
+
operation: 'execute revise_memory',
|
|
1177
|
+
action: 'revise_memory',
|
|
380
1178
|
});
|
|
381
1179
|
}
|
|
382
1180
|
}
|