@prmichaelsen/remember-mcp 3.19.3 → 4.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.
- package/AGENT.md +10 -3
- package/CHANGELOG.md +38 -0
- package/README.md +1 -1
- package/agent/commands/acp.artifact-glossary.md +530 -0
- package/agent/commands/acp.artifact-reference.md +591 -0
- package/agent/commands/acp.artifact-research.md +594 -0
- package/agent/commands/acp.audit.md +345 -0
- package/agent/commands/acp.clarification-address.md +185 -88
- package/agent/commands/acp.clarification-capture.md +44 -44
- package/agent/commands/acp.clarification-create.md +41 -42
- package/agent/commands/acp.command-create.md +49 -49
- package/agent/commands/acp.design-create.md +53 -35
- package/agent/commands/acp.design-reference.md +42 -42
- package/agent/commands/acp.handoff.md +35 -35
- package/agent/commands/acp.index.md +47 -47
- package/agent/commands/acp.init.md +105 -69
- package/agent/commands/acp.package-create.md +41 -41
- package/agent/commands/acp.package-info.md +40 -40
- package/agent/commands/acp.package-install.md +48 -48
- package/agent/commands/acp.package-list.md +40 -40
- package/agent/commands/acp.package-publish.md +62 -62
- package/agent/commands/acp.package-remove.md +41 -41
- package/agent/commands/acp.package-search.md +48 -48
- package/agent/commands/acp.package-update.md +50 -50
- package/agent/commands/acp.package-validate.md +52 -52
- package/agent/commands/acp.pattern-create.md +61 -43
- package/agent/commands/acp.plan.md +70 -47
- package/agent/commands/acp.proceed.md +188 -66
- package/agent/commands/acp.project-create.md +42 -42
- package/agent/commands/acp.project-info.md +46 -46
- package/agent/commands/acp.project-list.md +41 -41
- package/agent/commands/acp.project-remove.md +36 -36
- package/agent/commands/acp.project-set.md +33 -33
- package/agent/commands/acp.project-update.md +57 -57
- package/agent/commands/acp.projects-restore.md +37 -37
- package/agent/commands/acp.projects-sync.md +39 -39
- package/agent/commands/acp.report.md +50 -50
- package/agent/commands/acp.resume.md +36 -36
- package/agent/commands/acp.sessions.md +46 -46
- package/agent/commands/acp.status.md +43 -43
- package/agent/commands/acp.sync.md +109 -56
- package/agent/commands/acp.task-create.md +51 -49
- package/agent/commands/acp.update.md +66 -45
- package/agent/commands/acp.validate.md +110 -52
- package/agent/commands/acp.version-check-for-updates.md +40 -40
- package/agent/commands/acp.version-check.md +36 -36
- package/agent/commands/acp.version-update.md +43 -43
- package/agent/commands/command.template.md +40 -40
- package/agent/commands/git.commit.md +28 -28
- package/agent/commands/git.init.md +48 -48
- package/agent/design/design.template.md +9 -9
- package/agent/design/local.admin-debugging-tools.md +242 -0
- package/agent/design/requirements.template.md +8 -8
- package/agent/index/.gitkeep +0 -0
- package/agent/index/acp.core.yaml +137 -0
- package/agent/index/local.main.template.yaml +37 -0
- package/agent/index/local.main.yaml +48 -0
- package/agent/manifest.yaml +64 -0
- package/agent/milestones/milestone-1-{title}.template.md +8 -8
- package/agent/milestones/milestone-22-admin-debugging-tools.md +61 -0
- package/agent/milestones/milestone-23-trust-level-protection.md +122 -0
- package/agent/patterns/pattern.template.md +22 -22
- package/agent/progress.template.yaml +13 -3
- package/agent/progress.yaml +173 -3
- package/agent/schemas/package.schema.yaml +276 -0
- package/agent/scripts/acp.project-update.sh +5 -6
- package/agent/tasks/milestone-22-admin-debugging-tools/task-520-admin-gate-infrastructure.md +99 -0
- package/agent/tasks/milestone-22-admin-debugging-tools/task-521-schema-and-collection-tools.md +108 -0
- package/agent/tasks/milestone-22-admin-debugging-tools/task-522-memory-inspection-tools.md +120 -0
- package/agent/tasks/milestone-22-admin-debugging-tools/task-523-user-inspection-tools.md +126 -0
- package/agent/tasks/milestone-22-admin-debugging-tools/task-524-health-and-drift-tools.md +120 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-525-remove-trust-from-create-update.md +69 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-526-add-request-set-trust-level-tool.md +108 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-527-update-confirm-deny-secret-token.md +60 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-528-update-trust-scale-references.md +73 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-529-version-bump-and-release.md +87 -0
- package/agent/tasks/task-1-{title}.template.md +18 -18
- package/dist/server-factory.js +779 -87
- package/dist/server.js +141 -41
- package/dist/services/trust-validator.d.ts +16 -14
- package/dist/tools/admin-collection-stats.d.ts +24 -0
- package/dist/tools/admin-detect-weaviate-drift.d.ts +26 -0
- package/dist/tools/admin-get-weaviate-schema.d.ts +24 -0
- package/dist/tools/admin-health-drift.spec.d.ts +5 -0
- package/dist/tools/admin-health.d.ts +15 -0
- package/dist/tools/admin-inspect-memory.d.ts +29 -0
- package/dist/tools/admin-inspect-user.d.ts +73 -0
- package/dist/tools/admin-inspect-user.spec.d.ts +5 -0
- package/dist/tools/admin-list-collections.d.ts +23 -0
- package/dist/tools/admin-memory-inspection.spec.d.ts +7 -0
- package/dist/tools/admin-schema-collection.spec.d.ts +8 -0
- package/dist/tools/admin-search-across-users.d.ts +42 -0
- package/dist/tools/confirm.d.ts +1 -0
- package/dist/tools/confirm.spec.d.ts +5 -0
- package/dist/tools/create-internal-memory.d.ts +0 -7
- package/dist/tools/create-memory.d.ts +0 -7
- package/dist/tools/deny.d.ts +1 -0
- package/dist/tools/deny.spec.d.ts +5 -0
- package/dist/tools/query-memory.d.ts +2 -0
- package/dist/tools/request-set-trust-level.d.ts +32 -0
- package/dist/tools/request-set-trust-level.spec.d.ts +2 -0
- package/dist/tools/search-memory.d.ts +2 -0
- package/dist/tools/update-internal-memory.d.ts +0 -6
- package/dist/tools/update-memory.d.ts +0 -7
- package/dist/utils/admin.d.ts +21 -0
- package/dist/utils/admin.spec.d.ts +2 -0
- package/package.json +2 -2
- package/src/server-factory.ts +137 -42
- package/src/server.ts +6 -0
- package/src/services/trust-validator.spec.ts +57 -51
- package/src/services/trust-validator.ts +28 -26
- package/src/tools/admin-collection-stats.ts +67 -0
- package/src/tools/admin-detect-weaviate-drift.ts +110 -0
- package/src/tools/admin-get-weaviate-schema.ts +68 -0
- package/src/tools/admin-health-drift.spec.ts +193 -0
- package/src/tools/admin-health.ts +88 -0
- package/src/tools/admin-inspect-memory.ts +86 -0
- package/src/tools/admin-inspect-user.spec.ts +130 -0
- package/src/tools/admin-inspect-user.ts +148 -0
- package/src/tools/admin-list-collections.ts +73 -0
- package/src/tools/admin-memory-inspection.spec.ts +206 -0
- package/src/tools/admin-schema-collection.spec.ts +167 -0
- package/src/tools/admin-search-across-users.ts +104 -0
- package/src/tools/confirm.spec.ts +108 -0
- package/src/tools/confirm.ts +24 -1
- package/src/tools/create-internal-memory.ts +0 -3
- package/src/tools/create-memory.spec.ts +6 -2
- package/src/tools/create-memory.ts +1 -9
- package/src/tools/deny.spec.ts +59 -0
- package/src/tools/deny.ts +6 -1
- package/src/tools/ghost-config.ts +19 -19
- package/src/tools/query-memory.ts +4 -2
- package/src/tools/request-set-trust-level.spec.ts +87 -0
- package/src/tools/request-set-trust-level.ts +107 -0
- package/src/tools/search-memory.ts +4 -2
- package/src/tools/update-internal-memory.ts +0 -3
- package/src/tools/update-memory.ts +0 -8
- package/src/types/memory.ts +1 -1
- package/src/utils/admin.spec.ts +70 -0
- package/src/utils/admin.ts +27 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Trust validator — validation and suggestions for trust-sensitive operations.
|
|
3
3
|
*
|
|
4
|
+
* Uses integer TrustLevel 1–5 scale (higher = more confidential).
|
|
5
|
+
* Aligned with remember-core's trust-validator.service.
|
|
6
|
+
*
|
|
4
7
|
* See agent/design/local.ghost-persona-system.md
|
|
5
8
|
*/
|
|
6
9
|
|
|
@@ -16,21 +19,20 @@ export interface TrustValidationResult {
|
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Validate a trust level assignment.
|
|
19
|
-
* Warns if trust
|
|
22
|
+
* Warns if trust >= 4 (Restricted/Secret — very restrictive).
|
|
20
23
|
* Returns invalid for out-of-range values.
|
|
21
24
|
*
|
|
22
|
-
* @param trustLevel - The trust level being assigned (
|
|
23
|
-
* @param content - Optional content for context-aware validation
|
|
25
|
+
* @param trustLevel - The trust level being assigned (1-5 integer)
|
|
24
26
|
*/
|
|
25
|
-
export function validateTrustAssignment(trustLevel: number
|
|
26
|
-
if (trustLevel <
|
|
27
|
-
return { valid: false, warning: `Trust level must be between
|
|
27
|
+
export function validateTrustAssignment(trustLevel: number): TrustValidationResult {
|
|
28
|
+
if (!Number.isInteger(trustLevel) || trustLevel < 1 || trustLevel > 5) {
|
|
29
|
+
return { valid: false, warning: `Trust level must be an integer between 1 and 5, got ${trustLevel}` };
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
if (trustLevel
|
|
32
|
+
if (trustLevel >= 4) {
|
|
31
33
|
return {
|
|
32
34
|
valid: true,
|
|
33
|
-
warning: `Trust level ${trustLevel} is very restrictive — most accessors will
|
|
35
|
+
warning: `Trust level ${trustLevel} is very restrictive — most accessors will have limited visibility. Consider level 2 (INTERNAL) or 3 (CONFIDENTIAL) for broader access.`,
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -40,31 +42,31 @@ export function validateTrustAssignment(trustLevel: number, content?: string): T
|
|
|
40
42
|
/**
|
|
41
43
|
* Suggest an appropriate trust level based on content type and tags.
|
|
42
44
|
*
|
|
43
|
-
* Guidelines:
|
|
44
|
-
* -
|
|
45
|
-
* -
|
|
46
|
-
* - Business (invoice, contract):
|
|
47
|
-
* - Communication (email, conversation):
|
|
48
|
-
* -
|
|
49
|
-
* - Default:
|
|
45
|
+
* Guidelines (1-5 integer scale):
|
|
46
|
+
* - Personal (journal, memory, event): RESTRICTED (4) — close contacts only
|
|
47
|
+
* - System/audit/action: CONFIDENTIAL (3) — trusted friends
|
|
48
|
+
* - Business (invoice, contract): CONFIDENTIAL (3)
|
|
49
|
+
* - Communication (email, conversation): CONFIDENTIAL (3)
|
|
50
|
+
* - Ghost conversations: RESTRICTED (4)
|
|
51
|
+
* - Default: INTERNAL (2) — conservative
|
|
50
52
|
*
|
|
51
53
|
* Tag overrides:
|
|
52
|
-
* - 'private' or 'secret':
|
|
53
|
-
* - 'public':
|
|
54
|
+
* - 'private' or 'secret': SECRET (5)
|
|
55
|
+
* - 'public': PUBLIC (1)
|
|
54
56
|
*
|
|
55
57
|
* @param contentType - The type of content
|
|
56
58
|
* @param tags - Optional tags that may affect suggestion
|
|
57
|
-
* @returns Suggested trust level (
|
|
59
|
+
* @returns Suggested trust level (1-5)
|
|
58
60
|
*/
|
|
59
61
|
export function suggestTrustLevel(contentType: ContentType, tags?: string[]): number {
|
|
60
62
|
// Tag-based overrides take priority
|
|
61
63
|
if (tags && tags.length > 0) {
|
|
62
64
|
const lowerTags = tags.map(t => t.toLowerCase());
|
|
63
65
|
if (lowerTags.includes('private') || lowerTags.includes('secret')) {
|
|
64
|
-
return
|
|
66
|
+
return 5; // SECRET
|
|
65
67
|
}
|
|
66
68
|
if (lowerTags.includes('public')) {
|
|
67
|
-
return 1
|
|
69
|
+
return 1; // PUBLIC
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
|
|
@@ -74,32 +76,32 @@ export function suggestTrustLevel(contentType: ContentType, tags?: string[]): nu
|
|
|
74
76
|
case 'journal':
|
|
75
77
|
case 'memory':
|
|
76
78
|
case 'event':
|
|
77
|
-
return
|
|
79
|
+
return 4; // RESTRICTED
|
|
78
80
|
|
|
79
81
|
// System/internal
|
|
80
82
|
case 'system':
|
|
81
83
|
case 'audit':
|
|
82
84
|
case 'action':
|
|
83
85
|
case 'history':
|
|
84
|
-
return
|
|
86
|
+
return 3; // CONFIDENTIAL
|
|
85
87
|
|
|
86
88
|
// Business
|
|
87
89
|
case 'invoice':
|
|
88
90
|
case 'contract':
|
|
89
|
-
return
|
|
91
|
+
return 3; // CONFIDENTIAL
|
|
90
92
|
|
|
91
93
|
// Communication
|
|
92
94
|
case 'email':
|
|
93
95
|
case 'conversation':
|
|
94
96
|
case 'meeting':
|
|
95
|
-
return
|
|
97
|
+
return 3; // CONFIDENTIAL
|
|
96
98
|
|
|
97
99
|
// Ghost conversations — private by default
|
|
98
100
|
case 'ghost':
|
|
99
|
-
return
|
|
101
|
+
return 4; // RESTRICTED
|
|
100
102
|
|
|
101
103
|
// Default — conservative
|
|
102
104
|
default:
|
|
103
|
-
return
|
|
105
|
+
return 2; // INTERNAL
|
|
104
106
|
}
|
|
105
107
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remember_admin_collection_stats tool
|
|
3
|
+
* Returns stats for a specific Weaviate collection.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
7
|
+
import { createDebugLogger } from '../utils/debug.js';
|
|
8
|
+
import type { AuthContext } from '../types/auth.js';
|
|
9
|
+
import { isAdmin, adminPermissionError } from '../utils/admin.js';
|
|
10
|
+
import { getWeaviateClient } from '../weaviate/client.js';
|
|
11
|
+
|
|
12
|
+
export const adminCollectionStatsTool = {
|
|
13
|
+
name: 'remember_admin_collection_stats',
|
|
14
|
+
description: `[Admin] Get stats for a Weaviate collection — object count, property count, configuration.
|
|
15
|
+
|
|
16
|
+
Requires admin access (ADMIN_USER_IDS).`,
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
collection_name: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Collection name to get stats for',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ['collection_name'],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface AdminCollectionStatsArgs {
|
|
30
|
+
collection_name: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function handleAdminCollectionStats(
|
|
34
|
+
args: AdminCollectionStatsArgs,
|
|
35
|
+
userId: string,
|
|
36
|
+
_authContext?: AuthContext
|
|
37
|
+
): Promise<string> {
|
|
38
|
+
const debug = createDebugLogger({ tool: 'remember_admin_collection_stats', userId, operation: 'collection stats' });
|
|
39
|
+
try {
|
|
40
|
+
if (!isAdmin(userId)) {
|
|
41
|
+
return JSON.stringify(adminPermissionError());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
debug.info('Tool invoked', { collection_name: args.collection_name });
|
|
45
|
+
|
|
46
|
+
const client = getWeaviateClient();
|
|
47
|
+
const collection = client.collections.get(args.collection_name);
|
|
48
|
+
|
|
49
|
+
// Get config and count in parallel
|
|
50
|
+
const [config, objectCount] = await Promise.all([
|
|
51
|
+
collection.config.get(),
|
|
52
|
+
collection.length(),
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
return JSON.stringify({
|
|
56
|
+
collection_name: args.collection_name,
|
|
57
|
+
object_count: objectCount,
|
|
58
|
+
property_count: config.properties.length,
|
|
59
|
+
properties: config.properties.map((p: any) => p.name),
|
|
60
|
+
vectorizers: config.vectorizers,
|
|
61
|
+
multiTenancy: config.multiTenancy,
|
|
62
|
+
replication: config.replication,
|
|
63
|
+
}, null, 2);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return handleToolError(error, { toolName: 'remember_admin_collection_stats', userId, operation: 'collection stats' });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remember_admin_detect_weaviate_drift tool
|
|
3
|
+
* Compares expected schema properties (from code) against actual Weaviate schema.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
7
|
+
import { createDebugLogger } from '../utils/debug.js';
|
|
8
|
+
import type { AuthContext } from '../types/auth.js';
|
|
9
|
+
import { isAdmin, adminPermissionError } from '../utils/admin.js';
|
|
10
|
+
import { getWeaviateClient } from '../weaviate/client.js';
|
|
11
|
+
import {
|
|
12
|
+
getUserCollectionProperties,
|
|
13
|
+
getPublishedCollectionProperties,
|
|
14
|
+
} from '@prmichaelsen/remember-core/database/weaviate';
|
|
15
|
+
|
|
16
|
+
export const adminDetectWeaviateDriftTool = {
|
|
17
|
+
name: 'remember_admin_detect_weaviate_drift',
|
|
18
|
+
description: `[Admin] Compare expected vs actual Weaviate schema properties per collection.
|
|
19
|
+
|
|
20
|
+
Reports missing properties, extra properties, and overall drift status.
|
|
21
|
+
If no collection_ids provided, checks a sample of collections.
|
|
22
|
+
Requires admin access (ADMIN_USER_IDS).`,
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
collection_ids: {
|
|
27
|
+
type: 'array',
|
|
28
|
+
items: { type: 'string' },
|
|
29
|
+
description: 'Optional — specific collections to check. If omitted, samples available collections.',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export interface AdminDetectWeaviateDriftArgs {
|
|
36
|
+
collection_ids?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getExpectedProperties(collectionName: string): string[] {
|
|
40
|
+
if (collectionName.startsWith('Memory_users_')) {
|
|
41
|
+
return getUserCollectionProperties();
|
|
42
|
+
}
|
|
43
|
+
// Space, group, and friends collections use published properties
|
|
44
|
+
return getPublishedCollectionProperties();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function handleAdminDetectWeaviateDrift(
|
|
48
|
+
args: AdminDetectWeaviateDriftArgs,
|
|
49
|
+
userId: string,
|
|
50
|
+
_authContext?: AuthContext
|
|
51
|
+
): Promise<string> {
|
|
52
|
+
const debug = createDebugLogger({ tool: 'remember_admin_detect_weaviate_drift', userId, operation: 'detect drift' });
|
|
53
|
+
try {
|
|
54
|
+
if (!isAdmin(userId)) {
|
|
55
|
+
return JSON.stringify(adminPermissionError());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
debug.info('Tool invoked', { collection_ids: args.collection_ids });
|
|
59
|
+
|
|
60
|
+
const client = getWeaviateClient();
|
|
61
|
+
|
|
62
|
+
// Determine which collections to check
|
|
63
|
+
let collectionNames: string[];
|
|
64
|
+
if (args.collection_ids && args.collection_ids.length > 0) {
|
|
65
|
+
collectionNames = args.collection_ids;
|
|
66
|
+
} else {
|
|
67
|
+
// Sample: list all and take first few of each type
|
|
68
|
+
const all = await client.collections.listAll();
|
|
69
|
+
const names = all.map((c: any) => c.name).filter((n: string) => n.startsWith('Memory_'));
|
|
70
|
+
collectionNames = names.slice(0, 5); // Sample up to 5
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const results = [];
|
|
74
|
+
|
|
75
|
+
for (const name of collectionNames) {
|
|
76
|
+
try {
|
|
77
|
+
const collection = client.collections.get(name);
|
|
78
|
+
const config = await collection.config.get();
|
|
79
|
+
const actualProperties = config.properties.map((p: any) => p.name);
|
|
80
|
+
const expectedProperties = getExpectedProperties(name);
|
|
81
|
+
|
|
82
|
+
const missing = expectedProperties.filter(p => !actualProperties.includes(p));
|
|
83
|
+
const extra = actualProperties.filter((p: string) => !expectedProperties.includes(p));
|
|
84
|
+
const status = missing.length === 0 && extra.length === 0 ? 'match' : 'drift';
|
|
85
|
+
|
|
86
|
+
results.push({
|
|
87
|
+
collection: name,
|
|
88
|
+
status,
|
|
89
|
+
expected_count: expectedProperties.length,
|
|
90
|
+
actual_count: actualProperties.length,
|
|
91
|
+
missing_properties: missing,
|
|
92
|
+
extra_properties: extra,
|
|
93
|
+
});
|
|
94
|
+
} catch (err) {
|
|
95
|
+
results.push({
|
|
96
|
+
collection: name,
|
|
97
|
+
status: 'error',
|
|
98
|
+
error: err instanceof Error ? err.message : String(err),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return JSON.stringify({
|
|
104
|
+
collections_checked: results.length,
|
|
105
|
+
results,
|
|
106
|
+
}, null, 2);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return handleToolError(error, { toolName: 'remember_admin_detect_weaviate_drift', userId, operation: 'detect drift' });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remember_admin_get_weaviate_schema tool
|
|
3
|
+
* Inspects a Weaviate collection's schema, property types, and index config.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
7
|
+
import { createDebugLogger } from '../utils/debug.js';
|
|
8
|
+
import type { AuthContext } from '../types/auth.js';
|
|
9
|
+
import { isAdmin, adminPermissionError } from '../utils/admin.js';
|
|
10
|
+
import { getWeaviateClient } from '../weaviate/client.js';
|
|
11
|
+
|
|
12
|
+
export const adminGetWeaviateSchemaTool = {
|
|
13
|
+
name: 'remember_admin_get_weaviate_schema',
|
|
14
|
+
description: `[Admin] Inspect a Weaviate collection's schema — property names, types, and configuration.
|
|
15
|
+
|
|
16
|
+
Requires admin access (ADMIN_USER_IDS).`,
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
collection_name: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Collection name (e.g. "Memory_users_abc123", "Memory_spaces_public")',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ['collection_name'],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface AdminGetWeaviateSchemaArgs {
|
|
30
|
+
collection_name: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function handleAdminGetWeaviateSchema(
|
|
34
|
+
args: AdminGetWeaviateSchemaArgs,
|
|
35
|
+
userId: string,
|
|
36
|
+
_authContext?: AuthContext
|
|
37
|
+
): Promise<string> {
|
|
38
|
+
const debug = createDebugLogger({ tool: 'remember_admin_get_weaviate_schema', userId, operation: 'get schema' });
|
|
39
|
+
try {
|
|
40
|
+
if (!isAdmin(userId)) {
|
|
41
|
+
return JSON.stringify(adminPermissionError());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
debug.info('Tool invoked', { collection_name: args.collection_name });
|
|
45
|
+
|
|
46
|
+
const client = getWeaviateClient();
|
|
47
|
+
const collection = client.collections.get(args.collection_name);
|
|
48
|
+
const config = await collection.config.get();
|
|
49
|
+
|
|
50
|
+
return JSON.stringify({
|
|
51
|
+
collection_name: args.collection_name,
|
|
52
|
+
properties: config.properties.map((p: any) => ({
|
|
53
|
+
name: p.name,
|
|
54
|
+
dataType: p.dataType,
|
|
55
|
+
description: p.description,
|
|
56
|
+
indexFilterable: p.indexFilterable,
|
|
57
|
+
indexSearchable: p.indexSearchable,
|
|
58
|
+
tokenization: p.tokenization,
|
|
59
|
+
})),
|
|
60
|
+
vectorizers: config.vectorizers,
|
|
61
|
+
generative: config.generative,
|
|
62
|
+
multiTenancy: config.multiTenancy,
|
|
63
|
+
replication: config.replication,
|
|
64
|
+
}, null, 2);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return handleToolError(error, { toolName: 'remember_admin_get_weaviate_schema', userId, operation: 'get schema' });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for admin health and drift tools.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const mockTestWeaviate = jest.fn();
|
|
6
|
+
const mockTestFirestore = jest.fn();
|
|
7
|
+
const mockConfigGet = jest.fn();
|
|
8
|
+
const mockListAll = jest.fn();
|
|
9
|
+
const mockGetUserProps = jest.fn();
|
|
10
|
+
const mockGetPublishedProps = jest.fn();
|
|
11
|
+
|
|
12
|
+
jest.mock('../weaviate/client.js', () => ({
|
|
13
|
+
testWeaviateConnection: mockTestWeaviate,
|
|
14
|
+
getWeaviateClient: () => ({
|
|
15
|
+
collections: {
|
|
16
|
+
get: () => ({ config: { get: mockConfigGet } }),
|
|
17
|
+
listAll: mockListAll,
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock('../firestore/init.js', () => ({
|
|
23
|
+
testFirestoreConnection: mockTestFirestore,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
jest.mock('@prmichaelsen/remember-core/database/weaviate', () => ({
|
|
27
|
+
getUserCollectionProperties: mockGetUserProps,
|
|
28
|
+
getPublishedCollectionProperties: mockGetPublishedProps,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
import { handleAdminHealth } from './admin-health.js';
|
|
32
|
+
import { handleAdminDetectWeaviateDrift } from './admin-detect-weaviate-drift.js';
|
|
33
|
+
|
|
34
|
+
describe('remember_admin_health', () => {
|
|
35
|
+
const originalEnv = process.env.ADMIN_USER_IDS;
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
process.env.ADMIN_USER_IDS = 'admin_user';
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
if (originalEnv !== undefined) {
|
|
44
|
+
process.env.ADMIN_USER_IDS = originalEnv;
|
|
45
|
+
} else {
|
|
46
|
+
delete process.env.ADMIN_USER_IDS;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('returns healthy when both services are up', async () => {
|
|
51
|
+
mockTestWeaviate.mockResolvedValue(true);
|
|
52
|
+
mockTestFirestore.mockResolvedValue(true);
|
|
53
|
+
|
|
54
|
+
const result = await handleAdminHealth({}, 'admin_user');
|
|
55
|
+
const parsed = JSON.parse(result);
|
|
56
|
+
expect(parsed.overall).toBe('healthy');
|
|
57
|
+
expect(parsed.weaviate.status).toBe('ok');
|
|
58
|
+
expect(parsed.firestore.status).toBe('ok');
|
|
59
|
+
expect(parsed.weaviate.latency_ms).toBeGreaterThanOrEqual(0);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('returns degraded when one service is down', async () => {
|
|
63
|
+
mockTestWeaviate.mockResolvedValue(false);
|
|
64
|
+
mockTestFirestore.mockResolvedValue(true);
|
|
65
|
+
|
|
66
|
+
const result = await handleAdminHealth({}, 'admin_user');
|
|
67
|
+
const parsed = JSON.parse(result);
|
|
68
|
+
expect(parsed.overall).toBe('degraded');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('returns unhealthy when both services are down', async () => {
|
|
72
|
+
mockTestWeaviate.mockResolvedValue(false);
|
|
73
|
+
mockTestFirestore.mockResolvedValue(false);
|
|
74
|
+
|
|
75
|
+
const result = await handleAdminHealth({}, 'admin_user');
|
|
76
|
+
const parsed = JSON.parse(result);
|
|
77
|
+
expect(parsed.overall).toBe('unhealthy');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('handles exceptions gracefully', async () => {
|
|
81
|
+
mockTestWeaviate.mockRejectedValue(new Error('connection refused'));
|
|
82
|
+
mockTestFirestore.mockResolvedValue(true);
|
|
83
|
+
|
|
84
|
+
const result = await handleAdminHealth({}, 'admin_user');
|
|
85
|
+
const parsed = JSON.parse(result);
|
|
86
|
+
expect(parsed.overall).toBe('degraded');
|
|
87
|
+
expect(parsed.weaviate.status).toBe('error');
|
|
88
|
+
expect(parsed.weaviate.message).toContain('connection refused');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('returns permission error for non-admin', async () => {
|
|
92
|
+
const result = await handleAdminHealth({}, 'regular_user');
|
|
93
|
+
const parsed = JSON.parse(result);
|
|
94
|
+
expect(parsed.isError).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('remember_admin_detect_weaviate_drift', () => {
|
|
99
|
+
const originalEnv = process.env.ADMIN_USER_IDS;
|
|
100
|
+
|
|
101
|
+
beforeEach(() => {
|
|
102
|
+
process.env.ADMIN_USER_IDS = 'admin_user';
|
|
103
|
+
jest.clearAllMocks();
|
|
104
|
+
mockGetUserProps.mockReturnValue(['content', 'weight', 'tags']);
|
|
105
|
+
mockGetPublishedProps.mockReturnValue(['content', 'weight', 'tags', 'spaces']);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
afterEach(() => {
|
|
109
|
+
if (originalEnv !== undefined) {
|
|
110
|
+
process.env.ADMIN_USER_IDS = originalEnv;
|
|
111
|
+
} else {
|
|
112
|
+
delete process.env.ADMIN_USER_IDS;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('reports match when schema matches expected', async () => {
|
|
117
|
+
mockConfigGet.mockResolvedValue({
|
|
118
|
+
properties: [
|
|
119
|
+
{ name: 'content' },
|
|
120
|
+
{ name: 'weight' },
|
|
121
|
+
{ name: 'tags' },
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const result = await handleAdminDetectWeaviateDrift(
|
|
126
|
+
{ collection_ids: ['Memory_users_abc'] },
|
|
127
|
+
'admin_user'
|
|
128
|
+
);
|
|
129
|
+
const parsed = JSON.parse(result);
|
|
130
|
+
expect(parsed.results[0].status).toBe('match');
|
|
131
|
+
expect(parsed.results[0].missing_properties).toEqual([]);
|
|
132
|
+
expect(parsed.results[0].extra_properties).toEqual([]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('reports drift when properties are missing', async () => {
|
|
136
|
+
mockConfigGet.mockResolvedValue({
|
|
137
|
+
properties: [{ name: 'content' }],
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const result = await handleAdminDetectWeaviateDrift(
|
|
141
|
+
{ collection_ids: ['Memory_users_abc'] },
|
|
142
|
+
'admin_user'
|
|
143
|
+
);
|
|
144
|
+
const parsed = JSON.parse(result);
|
|
145
|
+
expect(parsed.results[0].status).toBe('drift');
|
|
146
|
+
expect(parsed.results[0].missing_properties).toContain('weight');
|
|
147
|
+
expect(parsed.results[0].missing_properties).toContain('tags');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('reports extra properties in Weaviate', async () => {
|
|
151
|
+
mockConfigGet.mockResolvedValue({
|
|
152
|
+
properties: [
|
|
153
|
+
{ name: 'content' },
|
|
154
|
+
{ name: 'weight' },
|
|
155
|
+
{ name: 'tags' },
|
|
156
|
+
{ name: 'legacy_field' },
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const result = await handleAdminDetectWeaviateDrift(
|
|
161
|
+
{ collection_ids: ['Memory_users_abc'] },
|
|
162
|
+
'admin_user'
|
|
163
|
+
);
|
|
164
|
+
const parsed = JSON.parse(result);
|
|
165
|
+
expect(parsed.results[0].status).toBe('drift');
|
|
166
|
+
expect(parsed.results[0].extra_properties).toContain('legacy_field');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('uses published properties for space collections', async () => {
|
|
170
|
+
mockConfigGet.mockResolvedValue({
|
|
171
|
+
properties: [
|
|
172
|
+
{ name: 'content' },
|
|
173
|
+
{ name: 'weight' },
|
|
174
|
+
{ name: 'tags' },
|
|
175
|
+
{ name: 'spaces' },
|
|
176
|
+
],
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const result = await handleAdminDetectWeaviateDrift(
|
|
180
|
+
{ collection_ids: ['Memory_spaces_public'] },
|
|
181
|
+
'admin_user'
|
|
182
|
+
);
|
|
183
|
+
const parsed = JSON.parse(result);
|
|
184
|
+
expect(parsed.results[0].status).toBe('match');
|
|
185
|
+
expect(mockGetPublishedProps).toHaveBeenCalled();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('returns permission error for non-admin', async () => {
|
|
189
|
+
const result = await handleAdminDetectWeaviateDrift({}, 'regular_user');
|
|
190
|
+
const parsed = JSON.parse(result);
|
|
191
|
+
expect(parsed.isError).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remember_admin_health tool
|
|
3
|
+
* Deep health check — Weaviate and Firestore connectivity with latency.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
7
|
+
import { createDebugLogger } from '../utils/debug.js';
|
|
8
|
+
import type { AuthContext } from '../types/auth.js';
|
|
9
|
+
import { isAdmin, adminPermissionError } from '../utils/admin.js';
|
|
10
|
+
import { testWeaviateConnection } from '../weaviate/client.js';
|
|
11
|
+
import { testFirestoreConnection } from '../firestore/init.js';
|
|
12
|
+
|
|
13
|
+
export const adminHealthTool = {
|
|
14
|
+
name: 'remember_admin_health',
|
|
15
|
+
description: `[Admin] Deep health check — Weaviate and Firestore connectivity with latency.
|
|
16
|
+
|
|
17
|
+
Returns overall status (healthy/degraded/unhealthy).
|
|
18
|
+
Requires admin access (ADMIN_USER_IDS).`,
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export async function handleAdminHealth(
|
|
26
|
+
_args: Record<string, unknown>,
|
|
27
|
+
userId: string,
|
|
28
|
+
_authContext?: AuthContext
|
|
29
|
+
): Promise<string> {
|
|
30
|
+
const debug = createDebugLogger({ tool: 'remember_admin_health', userId, operation: 'health check' });
|
|
31
|
+
try {
|
|
32
|
+
if (!isAdmin(userId)) {
|
|
33
|
+
return JSON.stringify(adminPermissionError());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
debug.info('Tool invoked');
|
|
37
|
+
|
|
38
|
+
// Check Weaviate
|
|
39
|
+
const weaviateStart = Date.now();
|
|
40
|
+
let weaviateOk: boolean;
|
|
41
|
+
let weaviateError: string | undefined;
|
|
42
|
+
try {
|
|
43
|
+
weaviateOk = await testWeaviateConnection();
|
|
44
|
+
} catch (err) {
|
|
45
|
+
weaviateOk = false;
|
|
46
|
+
weaviateError = err instanceof Error ? err.message : String(err);
|
|
47
|
+
}
|
|
48
|
+
const weaviateLatency = Date.now() - weaviateStart;
|
|
49
|
+
|
|
50
|
+
// Check Firestore
|
|
51
|
+
const firestoreStart = Date.now();
|
|
52
|
+
let firestoreOk: boolean;
|
|
53
|
+
let firestoreError: string | undefined;
|
|
54
|
+
try {
|
|
55
|
+
firestoreOk = await testFirestoreConnection();
|
|
56
|
+
} catch (err) {
|
|
57
|
+
firestoreOk = false;
|
|
58
|
+
firestoreError = err instanceof Error ? err.message : String(err);
|
|
59
|
+
}
|
|
60
|
+
const firestoreLatency = Date.now() - firestoreStart;
|
|
61
|
+
|
|
62
|
+
// Determine overall status
|
|
63
|
+
let overall: 'healthy' | 'degraded' | 'unhealthy';
|
|
64
|
+
if (weaviateOk && firestoreOk) {
|
|
65
|
+
overall = 'healthy';
|
|
66
|
+
} else if (!weaviateOk && !firestoreOk) {
|
|
67
|
+
overall = 'unhealthy';
|
|
68
|
+
} else {
|
|
69
|
+
overall = 'degraded';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return JSON.stringify({
|
|
73
|
+
weaviate: {
|
|
74
|
+
status: weaviateOk ? 'ok' : 'error',
|
|
75
|
+
latency_ms: weaviateLatency,
|
|
76
|
+
message: weaviateError,
|
|
77
|
+
},
|
|
78
|
+
firestore: {
|
|
79
|
+
status: firestoreOk ? 'ok' : 'error',
|
|
80
|
+
latency_ms: firestoreLatency,
|
|
81
|
+
message: firestoreError,
|
|
82
|
+
},
|
|
83
|
+
overall,
|
|
84
|
+
}, null, 2);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return handleToolError(error, { toolName: 'remember_admin_health', userId, operation: 'health check' });
|
|
87
|
+
}
|
|
88
|
+
}
|