@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.
Files changed (140) hide show
  1. package/AGENT.md +10 -3
  2. package/CHANGELOG.md +38 -0
  3. package/README.md +1 -1
  4. package/agent/commands/acp.artifact-glossary.md +530 -0
  5. package/agent/commands/acp.artifact-reference.md +591 -0
  6. package/agent/commands/acp.artifact-research.md +594 -0
  7. package/agent/commands/acp.audit.md +345 -0
  8. package/agent/commands/acp.clarification-address.md +185 -88
  9. package/agent/commands/acp.clarification-capture.md +44 -44
  10. package/agent/commands/acp.clarification-create.md +41 -42
  11. package/agent/commands/acp.command-create.md +49 -49
  12. package/agent/commands/acp.design-create.md +53 -35
  13. package/agent/commands/acp.design-reference.md +42 -42
  14. package/agent/commands/acp.handoff.md +35 -35
  15. package/agent/commands/acp.index.md +47 -47
  16. package/agent/commands/acp.init.md +105 -69
  17. package/agent/commands/acp.package-create.md +41 -41
  18. package/agent/commands/acp.package-info.md +40 -40
  19. package/agent/commands/acp.package-install.md +48 -48
  20. package/agent/commands/acp.package-list.md +40 -40
  21. package/agent/commands/acp.package-publish.md +62 -62
  22. package/agent/commands/acp.package-remove.md +41 -41
  23. package/agent/commands/acp.package-search.md +48 -48
  24. package/agent/commands/acp.package-update.md +50 -50
  25. package/agent/commands/acp.package-validate.md +52 -52
  26. package/agent/commands/acp.pattern-create.md +61 -43
  27. package/agent/commands/acp.plan.md +70 -47
  28. package/agent/commands/acp.proceed.md +188 -66
  29. package/agent/commands/acp.project-create.md +42 -42
  30. package/agent/commands/acp.project-info.md +46 -46
  31. package/agent/commands/acp.project-list.md +41 -41
  32. package/agent/commands/acp.project-remove.md +36 -36
  33. package/agent/commands/acp.project-set.md +33 -33
  34. package/agent/commands/acp.project-update.md +57 -57
  35. package/agent/commands/acp.projects-restore.md +37 -37
  36. package/agent/commands/acp.projects-sync.md +39 -39
  37. package/agent/commands/acp.report.md +50 -50
  38. package/agent/commands/acp.resume.md +36 -36
  39. package/agent/commands/acp.sessions.md +46 -46
  40. package/agent/commands/acp.status.md +43 -43
  41. package/agent/commands/acp.sync.md +109 -56
  42. package/agent/commands/acp.task-create.md +51 -49
  43. package/agent/commands/acp.update.md +66 -45
  44. package/agent/commands/acp.validate.md +110 -52
  45. package/agent/commands/acp.version-check-for-updates.md +40 -40
  46. package/agent/commands/acp.version-check.md +36 -36
  47. package/agent/commands/acp.version-update.md +43 -43
  48. package/agent/commands/command.template.md +40 -40
  49. package/agent/commands/git.commit.md +28 -28
  50. package/agent/commands/git.init.md +48 -48
  51. package/agent/design/design.template.md +9 -9
  52. package/agent/design/local.admin-debugging-tools.md +242 -0
  53. package/agent/design/requirements.template.md +8 -8
  54. package/agent/index/.gitkeep +0 -0
  55. package/agent/index/acp.core.yaml +137 -0
  56. package/agent/index/local.main.template.yaml +37 -0
  57. package/agent/index/local.main.yaml +48 -0
  58. package/agent/manifest.yaml +64 -0
  59. package/agent/milestones/milestone-1-{title}.template.md +8 -8
  60. package/agent/milestones/milestone-22-admin-debugging-tools.md +61 -0
  61. package/agent/milestones/milestone-23-trust-level-protection.md +122 -0
  62. package/agent/patterns/pattern.template.md +22 -22
  63. package/agent/progress.template.yaml +13 -3
  64. package/agent/progress.yaml +173 -3
  65. package/agent/schemas/package.schema.yaml +276 -0
  66. package/agent/scripts/acp.project-update.sh +5 -6
  67. package/agent/tasks/milestone-22-admin-debugging-tools/task-520-admin-gate-infrastructure.md +99 -0
  68. package/agent/tasks/milestone-22-admin-debugging-tools/task-521-schema-and-collection-tools.md +108 -0
  69. package/agent/tasks/milestone-22-admin-debugging-tools/task-522-memory-inspection-tools.md +120 -0
  70. package/agent/tasks/milestone-22-admin-debugging-tools/task-523-user-inspection-tools.md +126 -0
  71. package/agent/tasks/milestone-22-admin-debugging-tools/task-524-health-and-drift-tools.md +120 -0
  72. package/agent/tasks/milestone-23-trust-level-protection/task-525-remove-trust-from-create-update.md +69 -0
  73. package/agent/tasks/milestone-23-trust-level-protection/task-526-add-request-set-trust-level-tool.md +108 -0
  74. package/agent/tasks/milestone-23-trust-level-protection/task-527-update-confirm-deny-secret-token.md +60 -0
  75. package/agent/tasks/milestone-23-trust-level-protection/task-528-update-trust-scale-references.md +73 -0
  76. package/agent/tasks/milestone-23-trust-level-protection/task-529-version-bump-and-release.md +87 -0
  77. package/agent/tasks/task-1-{title}.template.md +18 -18
  78. package/dist/server-factory.js +779 -87
  79. package/dist/server.js +141 -41
  80. package/dist/services/trust-validator.d.ts +16 -14
  81. package/dist/tools/admin-collection-stats.d.ts +24 -0
  82. package/dist/tools/admin-detect-weaviate-drift.d.ts +26 -0
  83. package/dist/tools/admin-get-weaviate-schema.d.ts +24 -0
  84. package/dist/tools/admin-health-drift.spec.d.ts +5 -0
  85. package/dist/tools/admin-health.d.ts +15 -0
  86. package/dist/tools/admin-inspect-memory.d.ts +29 -0
  87. package/dist/tools/admin-inspect-user.d.ts +73 -0
  88. package/dist/tools/admin-inspect-user.spec.d.ts +5 -0
  89. package/dist/tools/admin-list-collections.d.ts +23 -0
  90. package/dist/tools/admin-memory-inspection.spec.d.ts +7 -0
  91. package/dist/tools/admin-schema-collection.spec.d.ts +8 -0
  92. package/dist/tools/admin-search-across-users.d.ts +42 -0
  93. package/dist/tools/confirm.d.ts +1 -0
  94. package/dist/tools/confirm.spec.d.ts +5 -0
  95. package/dist/tools/create-internal-memory.d.ts +0 -7
  96. package/dist/tools/create-memory.d.ts +0 -7
  97. package/dist/tools/deny.d.ts +1 -0
  98. package/dist/tools/deny.spec.d.ts +5 -0
  99. package/dist/tools/query-memory.d.ts +2 -0
  100. package/dist/tools/request-set-trust-level.d.ts +32 -0
  101. package/dist/tools/request-set-trust-level.spec.d.ts +2 -0
  102. package/dist/tools/search-memory.d.ts +2 -0
  103. package/dist/tools/update-internal-memory.d.ts +0 -6
  104. package/dist/tools/update-memory.d.ts +0 -7
  105. package/dist/utils/admin.d.ts +21 -0
  106. package/dist/utils/admin.spec.d.ts +2 -0
  107. package/package.json +2 -2
  108. package/src/server-factory.ts +137 -42
  109. package/src/server.ts +6 -0
  110. package/src/services/trust-validator.spec.ts +57 -51
  111. package/src/services/trust-validator.ts +28 -26
  112. package/src/tools/admin-collection-stats.ts +67 -0
  113. package/src/tools/admin-detect-weaviate-drift.ts +110 -0
  114. package/src/tools/admin-get-weaviate-schema.ts +68 -0
  115. package/src/tools/admin-health-drift.spec.ts +193 -0
  116. package/src/tools/admin-health.ts +88 -0
  117. package/src/tools/admin-inspect-memory.ts +86 -0
  118. package/src/tools/admin-inspect-user.spec.ts +130 -0
  119. package/src/tools/admin-inspect-user.ts +148 -0
  120. package/src/tools/admin-list-collections.ts +73 -0
  121. package/src/tools/admin-memory-inspection.spec.ts +206 -0
  122. package/src/tools/admin-schema-collection.spec.ts +167 -0
  123. package/src/tools/admin-search-across-users.ts +104 -0
  124. package/src/tools/confirm.spec.ts +108 -0
  125. package/src/tools/confirm.ts +24 -1
  126. package/src/tools/create-internal-memory.ts +0 -3
  127. package/src/tools/create-memory.spec.ts +6 -2
  128. package/src/tools/create-memory.ts +1 -9
  129. package/src/tools/deny.spec.ts +59 -0
  130. package/src/tools/deny.ts +6 -1
  131. package/src/tools/ghost-config.ts +19 -19
  132. package/src/tools/query-memory.ts +4 -2
  133. package/src/tools/request-set-trust-level.spec.ts +87 -0
  134. package/src/tools/request-set-trust-level.ts +107 -0
  135. package/src/tools/search-memory.ts +4 -2
  136. package/src/tools/update-internal-memory.ts +0 -3
  137. package/src/tools/update-memory.ts +0 -8
  138. package/src/types/memory.ts +1 -1
  139. package/src/utils/admin.spec.ts +70 -0
  140. package/src/utils/admin.ts +27 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Tests for admin schema and collection tools:
3
+ * - remember_admin_get_weaviate_schema
4
+ * - remember_admin_list_collections
5
+ * - remember_admin_collection_stats
6
+ */
7
+
8
+ import { handleAdminGetWeaviateSchema } from './admin-get-weaviate-schema.js';
9
+ import { handleAdminListCollections } from './admin-list-collections.js';
10
+ import { handleAdminCollectionStats } from './admin-collection-stats.js';
11
+
12
+ // Mock the Weaviate client
13
+ const mockGet = jest.fn();
14
+ const mockListAll = jest.fn();
15
+ const mockConfigGet = jest.fn();
16
+ const mockLength = jest.fn();
17
+
18
+ jest.mock('../weaviate/client.js', () => ({
19
+ getWeaviateClient: () => ({
20
+ collections: {
21
+ get: (name: string) => {
22
+ mockGet(name);
23
+ return {
24
+ config: { get: mockConfigGet },
25
+ length: mockLength,
26
+ };
27
+ },
28
+ listAll: mockListAll,
29
+ },
30
+ }),
31
+ }));
32
+
33
+ const sampleConfig = {
34
+ properties: [
35
+ { name: 'content', dataType: 'text', description: 'Memory content', indexFilterable: true, indexSearchable: true, tokenization: 'word' },
36
+ { name: 'weight', dataType: 'number', description: 'Significance', indexFilterable: true, indexSearchable: false, tokenization: null },
37
+ ],
38
+ vectorizers: [{ name: 'default', type: 'text2vec-openai' }],
39
+ generative: null,
40
+ multiTenancy: { enabled: false },
41
+ replication: { factor: 1 },
42
+ };
43
+
44
+ describe('remember_admin_get_weaviate_schema', () => {
45
+ const originalEnv = process.env.ADMIN_USER_IDS;
46
+
47
+ beforeEach(() => {
48
+ process.env.ADMIN_USER_IDS = 'admin_user';
49
+ jest.clearAllMocks();
50
+ mockConfigGet.mockResolvedValue(sampleConfig);
51
+ });
52
+
53
+ afterEach(() => {
54
+ if (originalEnv !== undefined) {
55
+ process.env.ADMIN_USER_IDS = originalEnv;
56
+ } else {
57
+ delete process.env.ADMIN_USER_IDS;
58
+ }
59
+ });
60
+
61
+ it('returns schema for admin user', async () => {
62
+ const result = await handleAdminGetWeaviateSchema(
63
+ { collection_name: 'Memory_users_test' },
64
+ 'admin_user'
65
+ );
66
+ const parsed = JSON.parse(result);
67
+ expect(parsed.collection_name).toBe('Memory_users_test');
68
+ expect(parsed.properties).toHaveLength(2);
69
+ expect(parsed.properties[0].name).toBe('content');
70
+ expect(parsed.properties[1].name).toBe('weight');
71
+ });
72
+
73
+ it('returns permission error for non-admin user', async () => {
74
+ const result = await handleAdminGetWeaviateSchema(
75
+ { collection_name: 'Memory_users_test' },
76
+ 'regular_user'
77
+ );
78
+ const parsed = JSON.parse(result);
79
+ expect(parsed.isError).toBe(true);
80
+ expect(parsed.content[0].text).toContain('Permission denied');
81
+ });
82
+ });
83
+
84
+ describe('remember_admin_list_collections', () => {
85
+ const originalEnv = process.env.ADMIN_USER_IDS;
86
+
87
+ beforeEach(() => {
88
+ process.env.ADMIN_USER_IDS = 'admin_user';
89
+ jest.clearAllMocks();
90
+ mockListAll.mockResolvedValue([
91
+ { name: 'Memory_users_abc123' },
92
+ { name: 'Memory_users_def456' },
93
+ { name: 'Memory_spaces_public' },
94
+ { name: 'Memory_groups_team1' },
95
+ ]);
96
+ });
97
+
98
+ afterEach(() => {
99
+ if (originalEnv !== undefined) {
100
+ process.env.ADMIN_USER_IDS = originalEnv;
101
+ } else {
102
+ delete process.env.ADMIN_USER_IDS;
103
+ }
104
+ });
105
+
106
+ it('lists all collections with type categorization', async () => {
107
+ const result = await handleAdminListCollections({}, 'admin_user');
108
+ const parsed = JSON.parse(result);
109
+ expect(parsed.total).toBe(4);
110
+ expect(parsed.collections[0]).toEqual({ name: 'Memory_users_abc123', type: 'user' });
111
+ expect(parsed.collections[2]).toEqual({ name: 'Memory_spaces_public', type: 'space' });
112
+ expect(parsed.collections[3]).toEqual({ name: 'Memory_groups_team1', type: 'group' });
113
+ });
114
+
115
+ it('filters collections by prefix', async () => {
116
+ const result = await handleAdminListCollections({ filter: 'Memory_users_' }, 'admin_user');
117
+ const parsed = JSON.parse(result);
118
+ expect(parsed.total).toBe(2);
119
+ expect(parsed.collections.every((c: any) => c.type === 'user')).toBe(true);
120
+ });
121
+
122
+ it('returns permission error for non-admin user', async () => {
123
+ const result = await handleAdminListCollections({}, 'regular_user');
124
+ const parsed = JSON.parse(result);
125
+ expect(parsed.isError).toBe(true);
126
+ });
127
+ });
128
+
129
+ describe('remember_admin_collection_stats', () => {
130
+ const originalEnv = process.env.ADMIN_USER_IDS;
131
+
132
+ beforeEach(() => {
133
+ process.env.ADMIN_USER_IDS = 'admin_user';
134
+ jest.clearAllMocks();
135
+ mockConfigGet.mockResolvedValue(sampleConfig);
136
+ mockLength.mockResolvedValue(42);
137
+ });
138
+
139
+ afterEach(() => {
140
+ if (originalEnv !== undefined) {
141
+ process.env.ADMIN_USER_IDS = originalEnv;
142
+ } else {
143
+ delete process.env.ADMIN_USER_IDS;
144
+ }
145
+ });
146
+
147
+ it('returns stats for admin user', async () => {
148
+ const result = await handleAdminCollectionStats(
149
+ { collection_name: 'Memory_users_test' },
150
+ 'admin_user'
151
+ );
152
+ const parsed = JSON.parse(result);
153
+ expect(parsed.collection_name).toBe('Memory_users_test');
154
+ expect(parsed.object_count).toBe(42);
155
+ expect(parsed.property_count).toBe(2);
156
+ expect(parsed.properties).toEqual(['content', 'weight']);
157
+ });
158
+
159
+ it('returns permission error for non-admin user', async () => {
160
+ const result = await handleAdminCollectionStats(
161
+ { collection_name: 'Memory_users_test' },
162
+ 'regular_user'
163
+ );
164
+ const parsed = JSON.parse(result);
165
+ expect(parsed.isError).toBe(true);
166
+ });
167
+ });
@@ -0,0 +1,104 @@
1
+ /**
2
+ * remember_admin_search_across_users tool
3
+ * Searches memories across multiple user tenants.
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 { createCoreServices } from '../core-services.js';
11
+
12
+ export const adminSearchAcrossUsersTool = {
13
+ name: 'remember_admin_search_across_users',
14
+ description: `[Admin] Search memories across multiple user tenants.
15
+
16
+ Results include which user each memory belongs to.
17
+ Requires explicit user_id array — no "all users" search.
18
+ Requires admin access (ADMIN_USER_IDS).`,
19
+ inputSchema: {
20
+ type: 'object',
21
+ properties: {
22
+ user_ids: {
23
+ type: 'array',
24
+ items: { type: 'string' },
25
+ description: 'User IDs to search across (required, 1 or more)',
26
+ },
27
+ query: {
28
+ type: 'string',
29
+ description: 'Search query (hybrid search)',
30
+ },
31
+ limit: {
32
+ type: 'number',
33
+ description: 'Max results across all users. Default: 10',
34
+ },
35
+ content_type: {
36
+ type: 'string',
37
+ description: 'Optional content type filter',
38
+ },
39
+ },
40
+ required: ['user_ids', 'query'],
41
+ },
42
+ };
43
+
44
+ export interface AdminSearchAcrossUsersArgs {
45
+ user_ids: string[];
46
+ query: string;
47
+ limit?: number;
48
+ content_type?: string;
49
+ }
50
+
51
+ export async function handleAdminSearchAcrossUsers(
52
+ args: AdminSearchAcrossUsersArgs,
53
+ userId: string,
54
+ _authContext?: AuthContext
55
+ ): Promise<string> {
56
+ const debug = createDebugLogger({ tool: 'remember_admin_search_across_users', userId, operation: 'search across users' });
57
+ try {
58
+ if (!isAdmin(userId)) {
59
+ return JSON.stringify(adminPermissionError());
60
+ }
61
+
62
+ if (!args.user_ids || args.user_ids.length === 0) {
63
+ return JSON.stringify({ error: 'user_ids array is required and must not be empty' });
64
+ }
65
+
66
+ debug.info('Tool invoked', { user_ids: args.user_ids, query: args.query, limit: args.limit });
67
+
68
+ const limit = args.limit ?? 10;
69
+ const allResults: Array<{ user_id: string; memory: any; score?: number }> = [];
70
+ const warnings: string[] = [];
71
+
72
+ for (const targetUserId of args.user_ids) {
73
+ try {
74
+ const services = createCoreServices(targetUserId);
75
+ const searchResult = await services.memory.search({
76
+ query: args.query,
77
+ limit,
78
+ filters: args.content_type ? { types: [args.content_type as any] } : undefined,
79
+ });
80
+
81
+ for (const memory of searchResult.memories) {
82
+ allResults.push({
83
+ user_id: targetUserId,
84
+ memory,
85
+ });
86
+ }
87
+ } catch (err) {
88
+ warnings.push(`User ${targetUserId}: ${err instanceof Error ? err.message : String(err)}`);
89
+ }
90
+ }
91
+
92
+ // Sort by relevance (first results from each user are highest relevance)
93
+ // and limit to requested total
94
+ const limited = allResults.slice(0, limit);
95
+
96
+ return JSON.stringify({
97
+ total: limited.length,
98
+ results: limited,
99
+ warnings: warnings.length > 0 ? warnings : undefined,
100
+ }, null, 2);
101
+ } catch (error) {
102
+ return handleToolError(error, { toolName: 'remember_admin_search_across_users', userId, operation: 'search across users' });
103
+ }
104
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Tests for confirm tool — secret_token passthrough and set_trust_level handling.
3
+ */
4
+
5
+ import { handleConfirm } from './confirm.js';
6
+
7
+ // ─── Mocks ──────────────────────────────────────────────────
8
+
9
+ jest.mock('../core-services.js', () => ({
10
+ createCoreServices: jest.fn(),
11
+ }));
12
+
13
+ jest.mock('../weaviate/client.js', () => ({
14
+ getWeaviateClient: jest.fn(),
15
+ getMemoryCollectionName: jest.fn((userId: string) => `Memory_users_${userId}`),
16
+ }));
17
+
18
+ jest.mock('../utils/debug.js', () => ({
19
+ createDebugLogger: jest.fn(() => ({
20
+ info: jest.fn(),
21
+ debug: jest.fn(),
22
+ trace: jest.fn(),
23
+ error: jest.fn(),
24
+ time: jest.fn((_: string, fn: () => any) => fn()),
25
+ })),
26
+ }));
27
+
28
+ jest.mock('../utils/error-handler.js', () => ({
29
+ handleToolError: jest.fn(),
30
+ }));
31
+
32
+ import { createCoreServices } from '../core-services.js';
33
+ const mockCreateCoreServices = createCoreServices as jest.MockedFunction<any>;
34
+
35
+ // ─── Tests ───────────────────────────────────────────────────
36
+
37
+ describe('confirm tool', () => {
38
+ let mockConfirm: jest.Mock;
39
+ let mockValidateToken: jest.Mock;
40
+
41
+ beforeEach(() => {
42
+ jest.clearAllMocks();
43
+ mockConfirm = jest.fn();
44
+ mockValidateToken = jest.fn();
45
+
46
+ mockCreateCoreServices.mockReturnValue({
47
+ space: { confirm: mockConfirm },
48
+ token: { validateToken: mockValidateToken, confirmRequest: jest.fn() },
49
+ });
50
+ });
51
+
52
+ it('passes secret_token through to space.confirm when provided', async () => {
53
+ mockValidateToken.mockResolvedValue({ action: 'publish_memory' });
54
+ mockConfirm.mockResolvedValue({
55
+ action: 'publish_memory',
56
+ success: true,
57
+ composite_id: 'u1.m1',
58
+ published_to: ['spaces: public'],
59
+ space_ids: ['public'],
60
+ group_ids: [],
61
+ });
62
+
63
+ await handleConfirm({ token: 'tok-1', secret_token: 'sec-abc' }, 'user-1');
64
+
65
+ expect(mockConfirm).toHaveBeenCalledWith({ token: 'tok-1', secret_token: 'sec-abc' });
66
+ });
67
+
68
+ it('works without secret_token (backward compatible)', async () => {
69
+ mockValidateToken.mockResolvedValue({ action: 'publish_memory' });
70
+ mockConfirm.mockResolvedValue({
71
+ action: 'publish_memory',
72
+ success: true,
73
+ composite_id: 'u1.m1',
74
+ published_to: ['spaces: public'],
75
+ space_ids: ['public'],
76
+ group_ids: [],
77
+ });
78
+
79
+ await handleConfirm({ token: 'tok-1' }, 'user-1');
80
+
81
+ expect(mockConfirm).toHaveBeenCalledWith({ token: 'tok-1', secret_token: undefined });
82
+ });
83
+
84
+ it('handles set_trust_level action via memory.confirmSetTrustLevel', async () => {
85
+ const mockConfirmSetTrustLevel = jest.fn().mockResolvedValue({
86
+ memory_id: 'mem-1',
87
+ previous_trust_level: 'normal',
88
+ new_trust_level: 'core',
89
+ updated_at: '2026-03-20T00:00:00Z',
90
+ });
91
+
92
+ mockValidateToken.mockResolvedValue({ action: 'set_trust_level' });
93
+ mockCreateCoreServices.mockReturnValue({
94
+ space: { confirm: mockConfirm },
95
+ token: { validateToken: mockValidateToken, confirmRequest: jest.fn() },
96
+ memory: { confirmSetTrustLevel: mockConfirmSetTrustLevel },
97
+ });
98
+
99
+ const result = JSON.parse(await handleConfirm({ token: 'tok-trust' }, 'user-1') as string);
100
+
101
+ expect(result.success).toBe(true);
102
+ expect(result.memory_id).toBe('mem-1');
103
+ expect(result.previous_trust_level).toBe('normal');
104
+ expect(result.new_trust_level).toBe('core');
105
+ expect(result.message).toBe('Trust level changed from normal to core');
106
+ expect(mockConfirmSetTrustLevel).toHaveBeenCalledWith('tok-trust');
107
+ });
108
+ });
@@ -53,6 +53,10 @@ Violating these requirements bypasses user consent and is a security violation.`
53
53
  type: 'string',
54
54
  description: 'The confirmation token from the action tool',
55
55
  },
56
+ secret_token: {
57
+ type: 'string',
58
+ description: 'HMAC secret token for guard-protected operations. Only required when confirmation guard is enabled on the server.',
59
+ },
56
60
  },
57
61
  required: ['token'],
58
62
  },
@@ -60,6 +64,7 @@ Violating these requirements bypasses user consent and is a security violation.`
60
64
 
61
65
  interface ConfirmArgs {
62
66
  token: string;
67
+ secret_token?: string;
63
68
  }
64
69
 
65
70
  /**
@@ -140,8 +145,26 @@ export async function handleConfirm(
140
145
  );
141
146
  }
142
147
 
148
+ // Handle set_trust_level via MemoryService
149
+ if (request.action === 'set_trust_level') {
150
+ const { memory } = createCoreServices(userId);
151
+ const result = await memory.confirmSetTrustLevel(args.token);
152
+ return JSON.stringify(
153
+ {
154
+ success: true,
155
+ memory_id: result.memory_id,
156
+ previous_trust_level: result.previous_trust_level,
157
+ new_trust_level: result.new_trust_level,
158
+ updated_at: result.updated_at,
159
+ message: `Trust level changed from ${result.previous_trust_level} to ${result.new_trust_level}`,
160
+ },
161
+ null,
162
+ 2
163
+ );
164
+ }
165
+
143
166
  // Delegate publish/retract/revise to core SpaceService
144
- const result = await space.confirm({ token: args.token });
167
+ const result = await space.confirm({ token: args.token, secret_token: args.secret_token });
145
168
 
146
169
  // Format response based on action type
147
170
  if (result.action === 'retract_memory') {
@@ -29,7 +29,6 @@ export const createInternalMemoryTool = {
29
29
  title: { type: 'string', description: 'Optional title' },
30
30
  tags: { type: 'array', items: { type: 'string' }, description: 'Additional tags (internal tags added automatically)' },
31
31
  weight: { type: 'number', minimum: 0, maximum: 1, description: 'Significance (0-1)' },
32
- trust: { type: 'number', minimum: 0, maximum: 1, description: 'Trust level (0-1)' },
33
32
  feel_salience: { type: 'number', minimum: 0, maximum: 1 },
34
33
  feel_social_weight: { type: 'number', minimum: 0, maximum: 1 },
35
34
  feel_narrative_importance: { type: 'number', minimum: 0, maximum: 1 },
@@ -43,7 +42,6 @@ export interface CreateInternalMemoryArgs {
43
42
  title?: string;
44
43
  tags?: string[];
45
44
  weight?: number;
46
- trust?: number;
47
45
  [key: string]: any;
48
46
  }
49
47
 
@@ -81,7 +79,6 @@ export async function handleCreateInternalMemory(
81
79
  title: args.title,
82
80
  type: ctx.type as any,
83
81
  weight: args.weight,
84
- trust: args.trust,
85
82
  tags: mergedTags,
86
83
  context_summary: `Internal memory created via MCP (${ctx.type})`,
87
84
  ...feelFields,
@@ -114,13 +114,17 @@ describe('updateMemoryTool definition', () => {
114
114
  expect(required).not.toContain('group_ids');
115
115
  });
116
116
 
117
- it('has optional content, title, type, weight, trust, tags properties', () => {
117
+ it('has optional content, title, type, weight, tags properties', () => {
118
118
  const props = updateMemoryTool.inputSchema.properties as Record<string, any>;
119
119
  expect(props.content).toBeDefined();
120
120
  expect(props.title).toBeDefined();
121
121
  expect(props.type).toBeDefined();
122
122
  expect(props.weight).toBeDefined();
123
- expect(props.trust).toBeDefined();
124
123
  expect(props.tags).toBeDefined();
125
124
  });
125
+
126
+ it('does not expose trust in schema', () => {
127
+ const props = updateMemoryTool.inputSchema.properties as Record<string, any>;
128
+ expect(props.trust).toBeUndefined();
129
+ });
126
130
  });
@@ -18,7 +18,7 @@ export const createMemoryTool = {
18
18
  description: `Create a new memory with optional template.
19
19
 
20
20
  Memories can store any type of information: notes, events, people, recipes, etc.
21
- Each memory has a weight (significance 0-1) and trust level (access control 0-1).
21
+ Each memory has a weight (significance 0-1). Trust defaults to SECRET (level 5) and can be changed via remember_request_set_trust_level.
22
22
  Location and context are automatically captured from the request.
23
23
 
24
24
  **IMPORTANT - Content vs Summary**:
@@ -56,12 +56,6 @@ export const createMemoryTool = {
56
56
  minimum: 0,
57
57
  maximum: 1,
58
58
  },
59
- trust: {
60
- type: 'number',
61
- description: 'Access control level (0-1, default: 0.25)',
62
- minimum: 0,
63
- maximum: 1,
64
- },
65
59
  tags: {
66
60
  type: 'array',
67
61
  items: { type: 'string' },
@@ -149,7 +143,6 @@ export interface CreateMemoryArgs {
149
143
  title?: string;
150
144
  type?: ContentType;
151
145
  weight?: number;
152
- trust?: number;
153
146
  tags?: string[];
154
147
  references?: string[];
155
148
  template_id?: string;
@@ -201,7 +194,6 @@ export async function handleCreateMemory(
201
194
  title: args.title,
202
195
  type: args.type,
203
196
  weight: args.weight,
204
- trust: args.trust,
205
197
  tags: args.tags,
206
198
  references: args.references,
207
199
  template_id: args.template_id,
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Tests for deny tool — secret_token passthrough.
3
+ */
4
+
5
+ import { handleDeny } from './deny.js';
6
+
7
+ // ─── Mocks ──────────────────────────────────────────────────
8
+
9
+ jest.mock('../core-services.js', () => ({
10
+ createCoreServices: jest.fn(),
11
+ }));
12
+
13
+ jest.mock('../utils/debug.js', () => ({
14
+ createDebugLogger: jest.fn(() => ({
15
+ info: jest.fn(),
16
+ debug: jest.fn(),
17
+ trace: jest.fn(),
18
+ error: jest.fn(),
19
+ time: jest.fn((_: string, fn: () => any) => fn()),
20
+ })),
21
+ }));
22
+
23
+ jest.mock('../utils/error-handler.js', () => ({
24
+ handleToolError: jest.fn(),
25
+ }));
26
+
27
+ import { createCoreServices } from '../core-services.js';
28
+ const mockCreateCoreServices = createCoreServices as jest.MockedFunction<any>;
29
+
30
+ // ─── Tests ───────────────────────────────────────────────────
31
+
32
+ describe('deny tool', () => {
33
+ let mockDeny: jest.Mock;
34
+
35
+ beforeEach(() => {
36
+ jest.clearAllMocks();
37
+ mockDeny = jest.fn();
38
+
39
+ mockCreateCoreServices.mockReturnValue({
40
+ space: { deny: mockDeny },
41
+ });
42
+ });
43
+
44
+ it('passes secret_token through to space.deny when provided', async () => {
45
+ mockDeny.mockResolvedValue({ success: true });
46
+
47
+ await handleDeny({ token: 'tok-1', secret_token: 'sec-abc' }, 'user-1');
48
+
49
+ expect(mockDeny).toHaveBeenCalledWith({ token: 'tok-1', secret_token: 'sec-abc' });
50
+ });
51
+
52
+ it('works without secret_token (backward compatible)', async () => {
53
+ mockDeny.mockResolvedValue({ success: true });
54
+
55
+ await handleDeny({ token: 'tok-1' }, 'user-1');
56
+
57
+ expect(mockDeny).toHaveBeenCalledWith({ token: 'tok-1', secret_token: undefined });
58
+ });
59
+ });
package/src/tools/deny.ts CHANGED
@@ -45,6 +45,10 @@ This ensures proper user consent workflow is followed.`,
45
45
  type: 'string',
46
46
  description: 'The confirmation token from the action tool',
47
47
  },
48
+ secret_token: {
49
+ type: 'string',
50
+ description: 'HMAC secret token for guard-protected operations. Only required when confirmation guard is enabled on the server.',
51
+ },
48
52
  },
49
53
  required: ['token'],
50
54
  },
@@ -52,6 +56,7 @@ This ensures proper user consent workflow is followed.`,
52
56
 
53
57
  interface DenyArgs {
54
58
  token: string;
59
+ secret_token?: string;
55
60
  }
56
61
 
57
62
  /**
@@ -68,7 +73,7 @@ export async function handleDeny(
68
73
  debug.trace('Arguments', { args });
69
74
 
70
75
  const { space } = createCoreServices(userId);
71
- const result = await space.deny({ token: args.token });
76
+ const result = await space.deny({ token: args.token, secret_token: args.secret_token });
72
77
 
73
78
  return JSON.stringify(
74
79
  {
@@ -29,17 +29,17 @@ export const ghostConfigTool: Tool = {
29
29
  Actions:
30
30
  - get: View current ghost configuration
31
31
  - set: Update ghost settings (enabled, trust defaults, enforcement mode)
32
- - set_trust: Set a per-user trust level override (0-1)
32
+ - set_trust: Set a per-user trust level override (1-5 integer)
33
33
  - remove_trust: Remove a per-user trust override (revert to default)
34
34
  - block: Block a user from ghost access entirely
35
35
  - unblock: Unblock a previously blocked user
36
36
 
37
- Trust levels control what information your ghost can share:
38
- - 0.0: Existence only ("A memory exists about this")
39
- - 0.25: Metadata only (tags, type, dates no content)
40
- - 0.5: Summary only (AI-generated summary, no raw content)
41
- - 0.75: Partial access (content with sensitive fields redacted)
42
- - 1.0: Full access (all content revealed)
37
+ Trust levels (1-5 integer scale) control what information your ghost can share:
38
+ - 1 (PUBLIC): Full access (all content revealed)
39
+ - 2 (INTERNAL): Partial access (content with sensitive fields redacted)
40
+ - 3 (CONFIDENTIAL): Summary only (AI-generated summary, no raw content)
41
+ - 4 (RESTRICTED): Metadata only (tags, type, dates no content)
42
+ - 5 (SECRET): Existence only ("A memory exists about this")
43
43
 
44
44
  Ghost is disabled by default. Enable it to allow others to chat with your AI representation.`,
45
45
  inputSchema: {
@@ -60,16 +60,16 @@ Ghost is disabled by default. Enable it to allow others to chat with your AI rep
60
60
  description: 'Allow non-friends to chat with ghost (for "set" action)',
61
61
  },
62
62
  default_friend_trust: {
63
- type: 'number',
64
- description: 'Default trust level for friends (0-1, for "set" action)',
65
- minimum: 0,
66
- maximum: 1,
63
+ type: 'integer',
64
+ description: 'Default trust level for friends (1-5 integer, for "set" action)',
65
+ minimum: 1,
66
+ maximum: 5,
67
67
  },
68
68
  default_public_trust: {
69
- type: 'number',
70
- description: 'Default trust level for strangers (0-1, for "set" action)',
71
- minimum: 0,
72
- maximum: 1,
69
+ type: 'integer',
70
+ description: 'Default trust level for strangers (1-5 integer, for "set" action)',
71
+ minimum: 1,
72
+ maximum: 5,
73
73
  },
74
74
  enforcement_mode: {
75
75
  type: 'string',
@@ -82,10 +82,10 @@ Ghost is disabled by default. Enable it to allow others to chat with your AI rep
82
82
  description: 'Target user ID (for set_trust, remove_trust, block, unblock)',
83
83
  },
84
84
  trust_level: {
85
- type: 'number',
86
- description: 'Trust level to assign (0-1, for "set_trust" action)',
87
- minimum: 0,
88
- maximum: 1,
85
+ type: 'integer',
86
+ description: 'Trust level to assign (1-5 integer, for "set_trust" action)',
87
+ minimum: 1,
88
+ maximum: 5,
89
89
  },
90
90
  },
91
91
  required: ['action'],
@@ -91,8 +91,10 @@ export const queryMemoryTool = {
91
91
  description: 'Minimum weight (0-1)',
92
92
  },
93
93
  trust_min: {
94
- type: 'number',
95
- description: 'Minimum trust level (0-1)',
94
+ type: 'integer',
95
+ description: 'Minimum trust level (1-5: 1=PUBLIC, 2=INTERNAL, 3=CONFIDENTIAL, 4=RESTRICTED, 5=SECRET)',
96
+ minimum: 1,
97
+ maximum: 5,
96
98
  },
97
99
  date_from: {
98
100
  type: 'string',