@prmichaelsen/remember-mcp 2.5.2 → 2.6.2
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 +123 -0
- package/agent/milestones/milestone-12-comment-system.md +239 -0
- package/agent/tasks/task-55-add-comment-fields-to-schema.md +171 -0
- package/agent/tasks/task-56-update-search-space-for-comments.md +192 -0
- package/agent/tasks/task-57-update-query-space-for-comments.md +167 -0
- package/agent/tasks/task-58-add-comment-unit-tests.md +255 -0
- package/agent/tasks/task-59-update-documentation-for-comments.md +324 -0
- package/agent/tasks/task-60-standardize-structured-logging.md +300 -0
- package/agent/tasks/task-61-enhance-confirmation-tool-descriptions.md +267 -0
- package/dist/server-factory.js +364 -143
- package/dist/server.js +407 -160
- package/dist/tools/confirm.d.ts +12 -0
- package/dist/tools/deny.d.ts +12 -0
- package/dist/tools/query-space.d.ts +1 -0
- package/dist/tools/search-space.d.ts +1 -0
- package/package.json +1 -1
- package/src/config.ts +8 -1
- package/src/firestore/init.ts +21 -6
- package/src/services/confirmation-token.service.ts +42 -23
- package/src/tools/confirm.ts +69 -16
- package/src/tools/deny.ts +23 -1
- package/src/tools/publish.ts +34 -9
- package/src/tools/query-space.ts +23 -6
- package/src/tools/search-space.ts +14 -1
- package/src/weaviate/client.ts +29 -7
- package/src/weaviate/schema.ts +34 -4
- package/src/weaviate/space-schema.ts +28 -3
package/dist/tools/confirm.d.ts
CHANGED
|
@@ -7,6 +7,18 @@
|
|
|
7
7
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
8
8
|
/**
|
|
9
9
|
* Tool definition for remember_confirm
|
|
10
|
+
*
|
|
11
|
+
* CRITICAL SAFETY: This tool must ONLY be called after explicit user confirmation
|
|
12
|
+
* in a separate message. Never chain with other tools or call immediately after
|
|
13
|
+
* receiving a token. The confirmation workflow requires:
|
|
14
|
+
*
|
|
15
|
+
* 1. Agent calls remember_publish (or other confirmable action)
|
|
16
|
+
* 2. Agent receives token in response
|
|
17
|
+
* 3. Agent presents details to user and asks for confirmation
|
|
18
|
+
* 4. User responds in SEPARATE message with explicit yes/no
|
|
19
|
+
* 5. Agent calls remember_confirm or remember_deny in NEW response
|
|
20
|
+
*
|
|
21
|
+
* Chaining confirmations bypasses user consent and violates security model.
|
|
10
22
|
*/
|
|
11
23
|
export declare const confirmTool: Tool;
|
|
12
24
|
interface ConfirmArgs {
|
package/dist/tools/deny.d.ts
CHANGED
|
@@ -6,6 +6,18 @@
|
|
|
6
6
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
7
7
|
/**
|
|
8
8
|
* Tool definition for remember_deny
|
|
9
|
+
*
|
|
10
|
+
* CRITICAL SAFETY: This tool must ONLY be called after explicit user denial
|
|
11
|
+
* in a separate message. Never chain with other tools or call immediately after
|
|
12
|
+
* receiving a token. The denial workflow requires:
|
|
13
|
+
*
|
|
14
|
+
* 1. Agent calls remember_publish (or other confirmable action)
|
|
15
|
+
* 2. Agent receives token in response
|
|
16
|
+
* 3. Agent presents details to user and asks for confirmation
|
|
17
|
+
* 4. User responds in SEPARATE message with explicit denial
|
|
18
|
+
* 5. Agent calls remember_deny in NEW response
|
|
19
|
+
*
|
|
20
|
+
* Proper user consent workflow must be followed.
|
|
9
21
|
*/
|
|
10
22
|
export declare const denyTool: Tool;
|
|
11
23
|
interface DenyArgs {
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dotenv from 'dotenv';
|
|
2
|
+
import { logger } from './utils/logger.js';
|
|
2
3
|
|
|
3
4
|
dotenv.config();
|
|
4
5
|
|
|
@@ -52,5 +53,11 @@ export function validateConfig(): void {
|
|
|
52
53
|
);
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
// Import logger here to avoid circular dependency
|
|
57
|
+
// Use dynamic import synchronously (logger is already loaded by this point)
|
|
58
|
+
import('./utils/logger.js').then(({ logger }) => {
|
|
59
|
+
logger.info('Configuration validated', {
|
|
60
|
+
module: 'config',
|
|
61
|
+
});
|
|
62
|
+
});
|
|
56
63
|
}
|
package/src/firestore/init.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { initializeApp } from '@prmichaelsen/firebase-admin-sdk-v8';
|
|
2
2
|
import { config } from '../config.js';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
3
4
|
|
|
4
5
|
let initialized = false;
|
|
5
6
|
|
|
@@ -23,11 +24,20 @@ export function initFirestore(): void {
|
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
initialized = true;
|
|
26
|
-
|
|
27
|
+
logger.info('Firestore initialized successfully', {
|
|
28
|
+
module: 'firestore-init',
|
|
29
|
+
});
|
|
27
30
|
} catch (error) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
logger.error('Firestore initialization failed', {
|
|
32
|
+
module: 'firestore-init',
|
|
33
|
+
error: error instanceof Error ? error.message : String(error),
|
|
34
|
+
});
|
|
35
|
+
logger.error('Make sure FIREBASE_ADMIN_SERVICE_ACCOUNT_KEY is valid JSON', {
|
|
36
|
+
module: 'firestore-init',
|
|
37
|
+
});
|
|
38
|
+
logger.error('Check for proper escaping in .env file', {
|
|
39
|
+
module: 'firestore-init',
|
|
40
|
+
});
|
|
31
41
|
throw error;
|
|
32
42
|
}
|
|
33
43
|
}
|
|
@@ -52,10 +62,15 @@ export async function testFirestoreConnection(): Promise<boolean> {
|
|
|
52
62
|
const { getDocument } = await import('@prmichaelsen/firebase-admin-sdk-v8');
|
|
53
63
|
await getDocument('_health_check', 'test');
|
|
54
64
|
|
|
55
|
-
|
|
65
|
+
logger.info('Firestore connection test successful', {
|
|
66
|
+
module: 'firestore-init',
|
|
67
|
+
});
|
|
56
68
|
return true;
|
|
57
69
|
} catch (error) {
|
|
58
|
-
|
|
70
|
+
logger.error('Firestore connection test failed', {
|
|
71
|
+
module: 'firestore-init',
|
|
72
|
+
error: error instanceof Error ? error.message : String(error),
|
|
73
|
+
});
|
|
59
74
|
return false;
|
|
60
75
|
}
|
|
61
76
|
}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Confirmation Token Service
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Manages confirmation tokens for sensitive operations like publishing memories.
|
|
5
5
|
* Tokens are one-time use with 5-minute expiry.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { randomUUID } from 'crypto';
|
|
9
|
-
import {
|
|
10
|
-
getDocument,
|
|
11
|
-
addDocument,
|
|
9
|
+
import {
|
|
10
|
+
getDocument,
|
|
11
|
+
addDocument,
|
|
12
12
|
updateDocument,
|
|
13
13
|
queryDocuments,
|
|
14
|
-
type QueryOptions
|
|
14
|
+
type QueryOptions
|
|
15
15
|
} from '../firestore/init.js';
|
|
16
|
+
import { logger } from '../utils/logger.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Confirmation request stored in Firestore
|
|
@@ -69,21 +70,22 @@ export class ConfirmationTokenService {
|
|
|
69
70
|
|
|
70
71
|
// Add document to Firestore (auto-generates ID)
|
|
71
72
|
const collectionPath = `users/${userId}/requests`;
|
|
72
|
-
|
|
73
|
+
logger.info('Creating confirmation request', {
|
|
74
|
+
service: 'ConfirmationTokenService',
|
|
73
75
|
userId,
|
|
74
76
|
action,
|
|
75
77
|
targetCollection,
|
|
76
78
|
collectionPath,
|
|
77
|
-
|
|
78
|
-
token,
|
|
79
|
-
action,
|
|
80
|
-
payloadKeys: Object.keys(payload),
|
|
81
|
-
},
|
|
79
|
+
payloadKeys: Object.keys(payload),
|
|
82
80
|
});
|
|
83
81
|
|
|
84
|
-
|
|
82
|
+
logger.debug('Calling Firestore addDocument', {
|
|
83
|
+
service: 'ConfirmationTokenService',
|
|
84
|
+
collectionPath,
|
|
85
|
+
});
|
|
85
86
|
const docRef = await addDocument(collectionPath, request);
|
|
86
|
-
|
|
87
|
+
logger.debug('Firestore addDocument returned', {
|
|
88
|
+
service: 'ConfirmationTokenService',
|
|
87
89
|
hasDocRef: !!docRef,
|
|
88
90
|
hasId: !!docRef?.id,
|
|
89
91
|
docRefId: docRef?.id,
|
|
@@ -92,7 +94,8 @@ export class ConfirmationTokenService {
|
|
|
92
94
|
// Validate docRef
|
|
93
95
|
if (!docRef) {
|
|
94
96
|
const error = new Error('Firestore addDocument returned null/undefined');
|
|
95
|
-
|
|
97
|
+
logger.error('CRITICAL: addDocument returned null', {
|
|
98
|
+
service: 'ConfirmationTokenService',
|
|
96
99
|
userId,
|
|
97
100
|
collectionPath,
|
|
98
101
|
});
|
|
@@ -101,7 +104,8 @@ export class ConfirmationTokenService {
|
|
|
101
104
|
|
|
102
105
|
if (!docRef.id) {
|
|
103
106
|
const error = new Error('Firestore addDocument returned docRef without ID');
|
|
104
|
-
|
|
107
|
+
logger.error('CRITICAL: docRef has no ID', {
|
|
108
|
+
service: 'ConfirmationTokenService',
|
|
105
109
|
userId,
|
|
106
110
|
collectionPath,
|
|
107
111
|
docRef,
|
|
@@ -109,7 +113,8 @@ export class ConfirmationTokenService {
|
|
|
109
113
|
throw error;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
|
|
116
|
+
logger.info('Confirmation request created successfully', {
|
|
117
|
+
service: 'ConfirmationTokenService',
|
|
113
118
|
requestId: docRef.id,
|
|
114
119
|
token,
|
|
115
120
|
expiresAt: request.expires_at,
|
|
@@ -117,7 +122,8 @@ export class ConfirmationTokenService {
|
|
|
117
122
|
|
|
118
123
|
return { requestId: docRef.id, token };
|
|
119
124
|
} catch (error) {
|
|
120
|
-
|
|
125
|
+
logger.error('Failed to create confirmation request', {
|
|
126
|
+
service: 'ConfirmationTokenService',
|
|
121
127
|
error: error instanceof Error ? error.message : String(error),
|
|
122
128
|
stack: error instanceof Error ? error.stack : undefined,
|
|
123
129
|
userId,
|
|
@@ -143,7 +149,8 @@ export class ConfirmationTokenService {
|
|
|
143
149
|
): Promise<(ConfirmationRequest & { request_id: string }) | null> {
|
|
144
150
|
const collectionPath = `users/${userId}/requests`;
|
|
145
151
|
|
|
146
|
-
|
|
152
|
+
logger.debug('Validating confirmation token', {
|
|
153
|
+
service: 'ConfirmationTokenService',
|
|
147
154
|
userId,
|
|
148
155
|
token,
|
|
149
156
|
collectionPath,
|
|
@@ -160,20 +167,25 @@ export class ConfirmationTokenService {
|
|
|
160
167
|
|
|
161
168
|
const results = await queryDocuments(collectionPath, queryOptions);
|
|
162
169
|
|
|
163
|
-
|
|
170
|
+
logger.debug('Token query results', {
|
|
171
|
+
service: 'ConfirmationTokenService',
|
|
164
172
|
resultsFound: results.length,
|
|
165
173
|
hasResults: results.length > 0,
|
|
166
174
|
});
|
|
167
175
|
|
|
168
176
|
if (results.length === 0) {
|
|
169
|
-
|
|
177
|
+
logger.info('Token not found or not pending', {
|
|
178
|
+
service: 'ConfirmationTokenService',
|
|
179
|
+
userId,
|
|
180
|
+
});
|
|
170
181
|
return null;
|
|
171
182
|
}
|
|
172
183
|
|
|
173
184
|
const doc = results[0];
|
|
174
185
|
const request = doc.data as ConfirmationRequest;
|
|
175
186
|
|
|
176
|
-
|
|
187
|
+
logger.info('Confirmation request found', {
|
|
188
|
+
service: 'ConfirmationTokenService',
|
|
177
189
|
requestId: doc.id,
|
|
178
190
|
action: request.action,
|
|
179
191
|
status: request.status,
|
|
@@ -183,7 +195,11 @@ export class ConfirmationTokenService {
|
|
|
183
195
|
// Check expiry
|
|
184
196
|
const expiresAt = new Date(request.expires_at);
|
|
185
197
|
if (expiresAt.getTime() < Date.now()) {
|
|
186
|
-
|
|
198
|
+
logger.info('Token expired', {
|
|
199
|
+
service: 'ConfirmationTokenService',
|
|
200
|
+
requestId: doc.id,
|
|
201
|
+
expiresAt: request.expires_at,
|
|
202
|
+
});
|
|
187
203
|
await this.updateStatus(userId, doc.id, 'expired');
|
|
188
204
|
return null;
|
|
189
205
|
}
|
|
@@ -298,7 +314,10 @@ export class ConfirmationTokenService {
|
|
|
298
314
|
// Note: firebase-admin-sdk-v8 doesn't support collectionGroup queries
|
|
299
315
|
// This would need to be implemented differently or rely on Firestore TTL
|
|
300
316
|
// For now, return 0 and rely on Firestore TTL policy
|
|
301
|
-
|
|
317
|
+
logger.warn('cleanupExpired not implemented - relying on Firestore TTL', {
|
|
318
|
+
service: 'ConfirmationTokenService',
|
|
319
|
+
note: 'Configure Firestore TTL policy on requests collection group',
|
|
320
|
+
});
|
|
302
321
|
return 0;
|
|
303
322
|
}
|
|
304
323
|
}
|
package/src/tools/confirm.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* remember_confirm tool
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Generic confirmation tool that executes any pending action.
|
|
5
5
|
* This is the second phase of the confirmation workflow.
|
|
6
6
|
*/
|
|
@@ -10,13 +10,36 @@ import { confirmationTokenService, type ConfirmationRequest } from '../services/
|
|
|
10
10
|
import { getWeaviateClient, getMemoryCollectionName } from '../weaviate/client.js';
|
|
11
11
|
import { ensurePublicCollection } from '../weaviate/space-schema.js';
|
|
12
12
|
import { handleToolError } from '../utils/error-handler.js';
|
|
13
|
+
import { logger } from '../utils/logger.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Tool definition for remember_confirm
|
|
17
|
+
*
|
|
18
|
+
* CRITICAL SAFETY: This tool must ONLY be called after explicit user confirmation
|
|
19
|
+
* in a separate message. Never chain with other tools or call immediately after
|
|
20
|
+
* receiving a token. The confirmation workflow requires:
|
|
21
|
+
*
|
|
22
|
+
* 1. Agent calls remember_publish (or other confirmable action)
|
|
23
|
+
* 2. Agent receives token in response
|
|
24
|
+
* 3. Agent presents details to user and asks for confirmation
|
|
25
|
+
* 4. User responds in SEPARATE message with explicit yes/no
|
|
26
|
+
* 5. Agent calls remember_confirm or remember_deny in NEW response
|
|
27
|
+
*
|
|
28
|
+
* Chaining confirmations bypasses user consent and violates security model.
|
|
16
29
|
*/
|
|
17
30
|
export const confirmTool: Tool = {
|
|
18
31
|
name: 'remember_confirm',
|
|
19
|
-
description:
|
|
32
|
+
description: `Confirm and execute a pending action using the token. Works for any action that requires confirmation (publish, delete, etc.).
|
|
33
|
+
|
|
34
|
+
⚠️ CRITICAL SAFETY REQUIREMENTS:
|
|
35
|
+
Before executing this tool, you MUST:
|
|
36
|
+
1. Have received the confirmation token in a PREVIOUS tool response
|
|
37
|
+
2. Have presented the token details to the user for review
|
|
38
|
+
3. Have received EXPLICIT user confirmation in a SEPARATE user message
|
|
39
|
+
4. NEVER chain this tool with other tool calls in the same response
|
|
40
|
+
5. ALWAYS treat confirmations as standalone, deliberate actions
|
|
41
|
+
|
|
42
|
+
Violating these requirements bypasses user consent and is a security violation.`,
|
|
20
43
|
inputSchema: {
|
|
21
44
|
type: 'object',
|
|
22
45
|
properties: {
|
|
@@ -41,7 +64,8 @@ export async function handleConfirm(
|
|
|
41
64
|
userId: string
|
|
42
65
|
): Promise<string> {
|
|
43
66
|
try {
|
|
44
|
-
|
|
67
|
+
logger.info('Starting confirmation', {
|
|
68
|
+
tool: 'remember_confirm',
|
|
45
69
|
userId,
|
|
46
70
|
token: args.token,
|
|
47
71
|
});
|
|
@@ -49,13 +73,17 @@ export async function handleConfirm(
|
|
|
49
73
|
// Validate and confirm token
|
|
50
74
|
const request = await confirmationTokenService.confirmRequest(userId, args.token);
|
|
51
75
|
|
|
52
|
-
|
|
76
|
+
logger.debug('Token validation result', {
|
|
77
|
+
tool: 'remember_confirm',
|
|
53
78
|
requestFound: !!request,
|
|
54
79
|
action: request?.action,
|
|
55
80
|
});
|
|
56
81
|
|
|
57
82
|
if (!request) {
|
|
58
|
-
|
|
83
|
+
logger.info('Token invalid or expired', {
|
|
84
|
+
tool: 'remember_confirm',
|
|
85
|
+
userId,
|
|
86
|
+
});
|
|
59
87
|
return JSON.stringify(
|
|
60
88
|
{
|
|
61
89
|
success: false,
|
|
@@ -67,7 +95,11 @@ export async function handleConfirm(
|
|
|
67
95
|
);
|
|
68
96
|
}
|
|
69
97
|
|
|
70
|
-
|
|
98
|
+
logger.info('Executing confirmed action', {
|
|
99
|
+
tool: 'remember_confirm',
|
|
100
|
+
action: request.action,
|
|
101
|
+
userId,
|
|
102
|
+
});
|
|
71
103
|
|
|
72
104
|
// GENERIC: Execute action based on type
|
|
73
105
|
// This is where the generic pattern delegates to action-specific executors
|
|
@@ -99,7 +131,8 @@ async function executePublishMemory(
|
|
|
99
131
|
userId: string
|
|
100
132
|
): Promise<string> {
|
|
101
133
|
try {
|
|
102
|
-
|
|
134
|
+
logger.info('Executing publish memory action', {
|
|
135
|
+
function: 'executePublishMemory',
|
|
103
136
|
userId,
|
|
104
137
|
memoryId: request.payload.memory_id,
|
|
105
138
|
spaces: request.payload.spaces,
|
|
@@ -112,19 +145,27 @@ async function executePublishMemory(
|
|
|
112
145
|
getMemoryCollectionName(userId)
|
|
113
146
|
);
|
|
114
147
|
|
|
115
|
-
|
|
148
|
+
logger.debug('Fetching original memory', {
|
|
149
|
+
function: 'executePublishMemory',
|
|
150
|
+
collectionName: getMemoryCollectionName(userId),
|
|
151
|
+
memoryId: request.payload.memory_id,
|
|
152
|
+
});
|
|
116
153
|
|
|
117
154
|
const originalMemory = await userCollection.query.fetchObjectById(
|
|
118
155
|
request.payload.memory_id
|
|
119
156
|
);
|
|
120
157
|
|
|
121
|
-
|
|
158
|
+
logger.debug('Original memory fetch result', {
|
|
159
|
+
function: 'executePublishMemory',
|
|
122
160
|
found: !!originalMemory,
|
|
123
161
|
memoryId: request.payload.memory_id,
|
|
124
162
|
});
|
|
125
163
|
|
|
126
164
|
if (!originalMemory) {
|
|
127
|
-
|
|
165
|
+
logger.info('Original memory not found', {
|
|
166
|
+
function: 'executePublishMemory',
|
|
167
|
+
memoryId: request.payload.memory_id,
|
|
168
|
+
});
|
|
128
169
|
return JSON.stringify(
|
|
129
170
|
{
|
|
130
171
|
success: false,
|
|
@@ -138,7 +179,12 @@ async function executePublishMemory(
|
|
|
138
179
|
|
|
139
180
|
// Verify ownership again
|
|
140
181
|
if (originalMemory.properties.user_id !== userId) {
|
|
141
|
-
|
|
182
|
+
logger.warn('Permission denied - wrong owner', {
|
|
183
|
+
function: 'executePublishMemory',
|
|
184
|
+
memoryId: request.payload.memory_id,
|
|
185
|
+
memoryOwner: originalMemory.properties.user_id,
|
|
186
|
+
requestingUser: userId,
|
|
187
|
+
});
|
|
142
188
|
return JSON.stringify(
|
|
143
189
|
{
|
|
144
190
|
success: false,
|
|
@@ -150,12 +196,17 @@ async function executePublishMemory(
|
|
|
150
196
|
);
|
|
151
197
|
}
|
|
152
198
|
|
|
153
|
-
|
|
199
|
+
logger.debug('Ensuring public collection exists', {
|
|
200
|
+
function: 'executePublishMemory',
|
|
201
|
+
});
|
|
154
202
|
|
|
155
203
|
// Get unified public collection
|
|
156
204
|
const publicCollection = await ensurePublicCollection(weaviateClient);
|
|
157
205
|
|
|
158
|
-
|
|
206
|
+
logger.debug('Public collection ready', {
|
|
207
|
+
function: 'executePublishMemory',
|
|
208
|
+
collectionName: 'Memory_public',
|
|
209
|
+
});
|
|
159
210
|
|
|
160
211
|
// Create published memory (copy with modifications)
|
|
161
212
|
const originalTags = Array.isArray(originalMemory.properties.tags)
|
|
@@ -183,7 +234,8 @@ async function executePublishMemory(
|
|
|
183
234
|
version: 1,
|
|
184
235
|
};
|
|
185
236
|
|
|
186
|
-
|
|
237
|
+
logger.info('Inserting memory into Memory_public', {
|
|
238
|
+
function: 'executePublishMemory',
|
|
187
239
|
spaces: request.payload.spaces,
|
|
188
240
|
spaceCount: request.payload.spaces?.length || 0,
|
|
189
241
|
memoryId: request.payload.memory_id,
|
|
@@ -194,9 +246,10 @@ async function executePublishMemory(
|
|
|
194
246
|
// Insert directly into unified public collection
|
|
195
247
|
const result = await publicCollection.data.insert(publishedMemory as any);
|
|
196
248
|
|
|
197
|
-
|
|
198
|
-
|
|
249
|
+
logger.info('Memory published successfully', {
|
|
250
|
+
function: 'executePublishMemory',
|
|
199
251
|
spaceMemoryId: result,
|
|
252
|
+
spaces: request.payload.spaces,
|
|
200
253
|
});
|
|
201
254
|
|
|
202
255
|
// Return minimal response with spaces array
|
package/src/tools/deny.ts
CHANGED
|
@@ -10,10 +10,32 @@ import { handleToolError } from '../utils/error-handler.js';
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Tool definition for remember_deny
|
|
13
|
+
*
|
|
14
|
+
* CRITICAL SAFETY: This tool must ONLY be called after explicit user denial
|
|
15
|
+
* in a separate message. Never chain with other tools or call immediately after
|
|
16
|
+
* receiving a token. The denial workflow requires:
|
|
17
|
+
*
|
|
18
|
+
* 1. Agent calls remember_publish (or other confirmable action)
|
|
19
|
+
* 2. Agent receives token in response
|
|
20
|
+
* 3. Agent presents details to user and asks for confirmation
|
|
21
|
+
* 4. User responds in SEPARATE message with explicit denial
|
|
22
|
+
* 5. Agent calls remember_deny in NEW response
|
|
23
|
+
*
|
|
24
|
+
* Proper user consent workflow must be followed.
|
|
13
25
|
*/
|
|
14
26
|
export const denyTool: Tool = {
|
|
15
27
|
name: 'remember_deny',
|
|
16
|
-
description:
|
|
28
|
+
description: `Deny a pending action. The request will be marked as denied and the token invalidated. Works for any action that requires confirmation.
|
|
29
|
+
|
|
30
|
+
⚠️ CRITICAL SAFETY REQUIREMENTS:
|
|
31
|
+
Before executing this tool, you MUST:
|
|
32
|
+
1. Have received the confirmation token in a PREVIOUS tool response
|
|
33
|
+
2. Have presented the token details to the user for review
|
|
34
|
+
3. Have received EXPLICIT user denial in a SEPARATE user message
|
|
35
|
+
4. NEVER chain this tool with other tool calls in the same response
|
|
36
|
+
5. ALWAYS treat denials as standalone, deliberate actions
|
|
37
|
+
|
|
38
|
+
This ensures proper user consent workflow is followed.`,
|
|
17
39
|
inputSchema: {
|
|
18
40
|
type: 'object',
|
|
19
41
|
properties: {
|
package/src/tools/publish.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* remember_publish tool
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Generates a confirmation token for publishing a memory to a shared space.
|
|
5
5
|
* This is the first phase of the two-phase publish workflow.
|
|
6
6
|
*/
|
|
@@ -11,6 +11,7 @@ import { getWeaviateClient, getMemoryCollectionName } from '../weaviate/client.j
|
|
|
11
11
|
import { isValidSpaceId } from '../weaviate/space-schema.js';
|
|
12
12
|
import { handleToolError } from '../utils/error-handler.js';
|
|
13
13
|
import { SUPPORTED_SPACES } from '../types/space-memory.js';
|
|
14
|
+
import { logger } from '../utils/logger.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Tool definition for remember_publish
|
|
@@ -60,7 +61,8 @@ export async function handlePublish(
|
|
|
60
61
|
userId: string
|
|
61
62
|
): Promise<string> {
|
|
62
63
|
try {
|
|
63
|
-
|
|
64
|
+
logger.info('Starting publish request', {
|
|
65
|
+
tool: 'remember_publish',
|
|
64
66
|
userId,
|
|
65
67
|
memoryId: args.memory_id,
|
|
66
68
|
spaces: args.spaces,
|
|
@@ -71,7 +73,11 @@ export async function handlePublish(
|
|
|
71
73
|
// Validate all space IDs
|
|
72
74
|
const invalidSpaces = args.spaces.filter(s => !isValidSpaceId(s));
|
|
73
75
|
if (invalidSpaces.length > 0) {
|
|
74
|
-
|
|
76
|
+
logger.warn('Invalid space IDs provided', {
|
|
77
|
+
tool: 'remember_publish',
|
|
78
|
+
invalidSpaces,
|
|
79
|
+
providedSpaces: args.spaces,
|
|
80
|
+
});
|
|
75
81
|
return JSON.stringify(
|
|
76
82
|
{
|
|
77
83
|
success: false,
|
|
@@ -90,7 +96,10 @@ export async function handlePublish(
|
|
|
90
96
|
|
|
91
97
|
// Validate not empty
|
|
92
98
|
if (args.spaces.length === 0) {
|
|
93
|
-
|
|
99
|
+
logger.warn('Empty spaces array provided', {
|
|
100
|
+
tool: 'remember_publish',
|
|
101
|
+
userId,
|
|
102
|
+
});
|
|
94
103
|
return JSON.stringify(
|
|
95
104
|
{
|
|
96
105
|
success: false,
|
|
@@ -105,19 +114,28 @@ export async function handlePublish(
|
|
|
105
114
|
// Verify memory exists and user owns it
|
|
106
115
|
const weaviateClient = getWeaviateClient();
|
|
107
116
|
const collectionName = getMemoryCollectionName(userId);
|
|
108
|
-
|
|
117
|
+
logger.debug('Fetching memory from collection', {
|
|
118
|
+
tool: 'remember_publish',
|
|
119
|
+
collectionName,
|
|
120
|
+
memoryId: args.memory_id,
|
|
121
|
+
});
|
|
109
122
|
|
|
110
123
|
const userCollection = weaviateClient.collections.get(collectionName);
|
|
111
124
|
|
|
112
125
|
const memory = await userCollection.query.fetchObjectById(args.memory_id);
|
|
113
126
|
|
|
114
|
-
|
|
127
|
+
logger.debug('Memory fetch result', {
|
|
128
|
+
tool: 'remember_publish',
|
|
115
129
|
found: !!memory,
|
|
116
130
|
memoryId: args.memory_id,
|
|
117
131
|
});
|
|
118
132
|
|
|
119
133
|
if (!memory) {
|
|
120
|
-
|
|
134
|
+
logger.info('Memory not found', {
|
|
135
|
+
tool: 'remember_publish',
|
|
136
|
+
memoryId: args.memory_id,
|
|
137
|
+
collectionName,
|
|
138
|
+
});
|
|
121
139
|
return JSON.stringify(
|
|
122
140
|
{
|
|
123
141
|
success: false,
|
|
@@ -175,7 +193,12 @@ export async function handlePublish(
|
|
|
175
193
|
additional_tags: args.additional_tags || [],
|
|
176
194
|
};
|
|
177
195
|
|
|
178
|
-
|
|
196
|
+
logger.info('Generating confirmation token', {
|
|
197
|
+
tool: 'remember_publish',
|
|
198
|
+
userId,
|
|
199
|
+
memoryId: args.memory_id,
|
|
200
|
+
spaces: args.spaces,
|
|
201
|
+
});
|
|
179
202
|
|
|
180
203
|
// Generate confirmation token
|
|
181
204
|
const { requestId, token} = await confirmationTokenService.createRequest(
|
|
@@ -185,10 +208,12 @@ export async function handlePublish(
|
|
|
185
208
|
undefined // No single target_collection anymore
|
|
186
209
|
);
|
|
187
210
|
|
|
188
|
-
|
|
211
|
+
logger.info('Confirmation token generated', {
|
|
212
|
+
tool: 'remember_publish',
|
|
189
213
|
requestId,
|
|
190
214
|
token,
|
|
191
215
|
action: 'publish_memory',
|
|
216
|
+
spaces: args.spaces,
|
|
192
217
|
});
|
|
193
218
|
|
|
194
219
|
// Return minimal response - agent already knows memory details
|