@prmichaelsen/remember-mcp 3.20.1 → 4.0.1

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 (46) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/agent/milestones/milestone-23-trust-level-protection.md +122 -0
  3. package/agent/progress.yaml +96 -3
  4. package/agent/tasks/milestone-23-trust-level-protection/task-525-remove-trust-from-create-update.md +69 -0
  5. package/agent/tasks/milestone-23-trust-level-protection/task-526-add-request-set-trust-level-tool.md +108 -0
  6. package/agent/tasks/milestone-23-trust-level-protection/task-527-update-confirm-deny-secret-token.md +60 -0
  7. package/agent/tasks/milestone-23-trust-level-protection/task-528-update-trust-scale-references.md +73 -0
  8. package/agent/tasks/milestone-23-trust-level-protection/task-529-version-bump-and-release.md +87 -0
  9. package/dist/server-factory.js +151 -47
  10. package/dist/server.js +150 -42
  11. package/dist/services/trust-validator.d.ts +16 -14
  12. package/dist/tools/confirm.d.ts +1 -0
  13. package/dist/tools/confirm.spec.d.ts +5 -0
  14. package/dist/tools/create-internal-memory.d.ts +0 -7
  15. package/dist/tools/create-memory.d.ts +0 -7
  16. package/dist/tools/deny.d.ts +1 -0
  17. package/dist/tools/deny.spec.d.ts +5 -0
  18. package/dist/tools/query-memory.d.ts +2 -0
  19. package/dist/tools/request-set-trust-level.d.ts +32 -0
  20. package/dist/tools/request-set-trust-level.spec.d.ts +2 -0
  21. package/dist/tools/search-memory.d.ts +2 -0
  22. package/dist/tools/update-internal-memory.d.ts +0 -6
  23. package/dist/tools/update-memory.d.ts +0 -7
  24. package/package.json +2 -2
  25. package/src/server-factory.ts +6 -0
  26. package/src/server.ts +6 -0
  27. package/src/services/trust-validator.spec.ts +57 -51
  28. package/src/services/trust-validator.ts +28 -26
  29. package/src/tools/confirm.spec.ts +108 -0
  30. package/src/tools/confirm.ts +24 -1
  31. package/src/tools/create-internal-memory.ts +0 -3
  32. package/src/tools/create-memory.spec.ts +6 -2
  33. package/src/tools/create-memory.ts +1 -9
  34. package/src/tools/deny.spec.ts +59 -0
  35. package/src/tools/deny.ts +6 -1
  36. package/src/tools/ghost-config.ts +19 -19
  37. package/src/tools/publish.ts +2 -0
  38. package/src/tools/query-memory.ts +4 -2
  39. package/src/tools/request-set-trust-level.spec.ts +87 -0
  40. package/src/tools/request-set-trust-level.ts +109 -0
  41. package/src/tools/retract.ts +2 -0
  42. package/src/tools/revise.ts +2 -0
  43. package/src/tools/search-memory.ts +4 -2
  44. package/src/tools/update-internal-memory.ts +0 -3
  45. package/src/tools/update-memory.ts +0 -8
  46. package/src/types/memory.ts +1 -1
@@ -2,107 +2,113 @@ import { validateTrustAssignment, suggestTrustLevel } from './trust-validator.js
2
2
 
3
3
  describe('validateTrustAssignment', () => {
4
4
  it('accepts trust levels in valid range', () => {
5
- expect(validateTrustAssignment(0).valid).toBe(true);
6
- expect(validateTrustAssignment(0.5).valid).toBe(true);
7
- expect(validateTrustAssignment(1.0).valid).toBe(true);
5
+ expect(validateTrustAssignment(1).valid).toBe(true);
6
+ expect(validateTrustAssignment(3).valid).toBe(true);
7
+ expect(validateTrustAssignment(5).valid).toBe(true);
8
8
  });
9
9
 
10
- it('rejects trust levels below 0', () => {
11
- const result = validateTrustAssignment(-0.1);
10
+ it('rejects trust levels below 1', () => {
11
+ const result = validateTrustAssignment(0);
12
12
  expect(result.valid).toBe(false);
13
- expect(result.warning).toContain('-0.1');
13
+ expect(result.warning).toContain('0');
14
14
  });
15
15
 
16
- it('rejects trust levels above 1', () => {
17
- const result = validateTrustAssignment(1.5);
16
+ it('rejects trust levels above 5', () => {
17
+ const result = validateTrustAssignment(6);
18
18
  expect(result.valid).toBe(false);
19
- expect(result.warning).toContain('1.5');
19
+ expect(result.warning).toContain('6');
20
20
  });
21
21
 
22
- it('warns for trust < 0.25', () => {
23
- const result = validateTrustAssignment(0.1);
22
+ it('rejects non-integer trust levels', () => {
23
+ const result = validateTrustAssignment(2.5);
24
+ expect(result.valid).toBe(false);
25
+ expect(result.warning).toContain('2.5');
26
+ });
27
+
28
+ it('warns for trust >= 4 (RESTRICTED)', () => {
29
+ const result = validateTrustAssignment(4);
24
30
  expect(result.valid).toBe(true);
25
31
  expect(result.warning).toContain('very restrictive');
26
32
  });
27
33
 
28
- it('warns for trust 0', () => {
29
- const result = validateTrustAssignment(0);
34
+ it('warns for trust 5 (SECRET)', () => {
35
+ const result = validateTrustAssignment(5);
30
36
  expect(result.valid).toBe(true);
31
37
  expect(result.warning).toContain('very restrictive');
32
38
  });
33
39
 
34
- it('does not warn for trust >= 0.25', () => {
35
- expect(validateTrustAssignment(0.25).warning).toBeUndefined();
36
- expect(validateTrustAssignment(0.5).warning).toBeUndefined();
37
- expect(validateTrustAssignment(1.0).warning).toBeUndefined();
40
+ it('does not warn for trust <= 3', () => {
41
+ expect(validateTrustAssignment(1).warning).toBeUndefined();
42
+ expect(validateTrustAssignment(2).warning).toBeUndefined();
43
+ expect(validateTrustAssignment(3).warning).toBeUndefined();
38
44
  });
39
45
  });
40
46
 
41
47
  describe('suggestTrustLevel', () => {
42
48
  describe('tag overrides', () => {
43
- it('returns 0.1 for private tag', () => {
44
- expect(suggestTrustLevel('note', ['private'])).toBe(0.1);
49
+ it('returns 5 (SECRET) for private tag', () => {
50
+ expect(suggestTrustLevel('note', ['private'])).toBe(5);
45
51
  });
46
52
 
47
- it('returns 0.1 for secret tag', () => {
48
- expect(suggestTrustLevel('note', ['Secret'])).toBe(0.1);
53
+ it('returns 5 (SECRET) for secret tag', () => {
54
+ expect(suggestTrustLevel('note', ['Secret'])).toBe(5);
49
55
  });
50
56
 
51
- it('returns 1.0 for public tag', () => {
52
- expect(suggestTrustLevel('journal', ['public'])).toBe(1.0);
57
+ it('returns 1 (PUBLIC) for public tag', () => {
58
+ expect(suggestTrustLevel('journal', ['public'])).toBe(1);
53
59
  });
54
60
 
55
61
  it('tag override takes priority over content type', () => {
56
- // journal normally suggests 0.75, but 'private' overrides to 0.1
57
- expect(suggestTrustLevel('journal', ['private'])).toBe(0.1);
62
+ // journal normally suggests 4, but 'private' overrides to 5
63
+ expect(suggestTrustLevel('journal', ['private'])).toBe(5);
58
64
  });
59
65
  });
60
66
 
61
67
  describe('content type suggestions', () => {
62
- it('suggests 0.75 for personal types', () => {
63
- expect(suggestTrustLevel('journal')).toBe(0.75);
64
- expect(suggestTrustLevel('memory')).toBe(0.75);
65
- expect(suggestTrustLevel('event')).toBe(0.75);
68
+ it('suggests 4 (RESTRICTED) for personal types', () => {
69
+ expect(suggestTrustLevel('journal')).toBe(4);
70
+ expect(suggestTrustLevel('memory')).toBe(4);
71
+ expect(suggestTrustLevel('event')).toBe(4);
66
72
  });
67
73
 
68
- it('suggests 0.5 for system types', () => {
69
- expect(suggestTrustLevel('system')).toBe(0.5);
70
- expect(suggestTrustLevel('audit')).toBe(0.5);
71
- expect(suggestTrustLevel('action')).toBe(0.5);
72
- expect(suggestTrustLevel('history')).toBe(0.5);
74
+ it('suggests 3 (CONFIDENTIAL) for system types', () => {
75
+ expect(suggestTrustLevel('system')).toBe(3);
76
+ expect(suggestTrustLevel('audit')).toBe(3);
77
+ expect(suggestTrustLevel('action')).toBe(3);
78
+ expect(suggestTrustLevel('history')).toBe(3);
73
79
  });
74
80
 
75
- it('suggests 0.5 for business types', () => {
76
- expect(suggestTrustLevel('invoice')).toBe(0.5);
77
- expect(suggestTrustLevel('contract')).toBe(0.5);
81
+ it('suggests 3 (CONFIDENTIAL) for business types', () => {
82
+ expect(suggestTrustLevel('invoice')).toBe(3);
83
+ expect(suggestTrustLevel('contract')).toBe(3);
78
84
  });
79
85
 
80
- it('suggests 0.5 for communication types', () => {
81
- expect(suggestTrustLevel('email')).toBe(0.5);
82
- expect(suggestTrustLevel('conversation')).toBe(0.5);
83
- expect(suggestTrustLevel('meeting')).toBe(0.5);
86
+ it('suggests 3 (CONFIDENTIAL) for communication types', () => {
87
+ expect(suggestTrustLevel('email')).toBe(3);
88
+ expect(suggestTrustLevel('conversation')).toBe(3);
89
+ expect(suggestTrustLevel('meeting')).toBe(3);
84
90
  });
85
91
 
86
- it('suggests 0.75 for ghost type', () => {
87
- expect(suggestTrustLevel('ghost')).toBe(0.75);
92
+ it('suggests 4 (RESTRICTED) for ghost type', () => {
93
+ expect(suggestTrustLevel('ghost')).toBe(4);
88
94
  });
89
95
 
90
- it('suggests 0.25 for general/creative types', () => {
91
- expect(suggestTrustLevel('note')).toBe(0.25);
92
- expect(suggestTrustLevel('code')).toBe(0.25);
93
- expect(suggestTrustLevel('article')).toBe(0.25);
94
- expect(suggestTrustLevel('recipe')).toBe(0.25);
95
- expect(suggestTrustLevel('bookmark')).toBe(0.25);
96
+ it('suggests 2 (INTERNAL) for general/creative types', () => {
97
+ expect(suggestTrustLevel('note')).toBe(2);
98
+ expect(suggestTrustLevel('code')).toBe(2);
99
+ expect(suggestTrustLevel('article')).toBe(2);
100
+ expect(suggestTrustLevel('recipe')).toBe(2);
101
+ expect(suggestTrustLevel('bookmark')).toBe(2);
96
102
  });
97
103
  });
98
104
 
99
105
  describe('edge cases', () => {
100
106
  it('handles empty tags array', () => {
101
- expect(suggestTrustLevel('note', [])).toBe(0.25);
107
+ expect(suggestTrustLevel('note', [])).toBe(2);
102
108
  });
103
109
 
104
110
  it('handles undefined tags', () => {
105
- expect(suggestTrustLevel('note')).toBe(0.25);
111
+ expect(suggestTrustLevel('note')).toBe(2);
106
112
  });
107
113
  });
108
114
  });
@@ -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 < 0.25 (very private existence-only for most accessors).
22
+ * Warns if trust >= 4 (Restricted/Secretvery restrictive).
20
23
  * Returns invalid for out-of-range values.
21
24
  *
22
- * @param trustLevel - The trust level being assigned (0-1)
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, content?: string): TrustValidationResult {
26
- if (trustLevel < 0 || trustLevel > 1) {
27
- return { valid: false, warning: `Trust level must be between 0 and 1, got ${trustLevel}` };
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 < 0.25) {
32
+ if (trustLevel >= 4) {
31
33
  return {
32
34
  valid: true,
33
- warning: `Trust level ${trustLevel} is very restrictive — most accessors will see only that this memory exists. Consider 0.25+ for metadata visibility.`,
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
- * - System/audit/action: 0.5 (internal, summary access for trusted users)
45
- * - Personal (journal, memory, event): 0.75 (share with close friends)
46
- * - Business (invoice, contract): 0.5 (summary only for collaborators)
47
- * - Communication (email, conversation): 0.5 (summary only)
48
- * - Creative/content: 0.25 (metadata for discovery, full access for trusted)
49
- * - Default: 0.25 (conservativemetadata only)
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': 0.1 (near-hidden)
53
- * - 'public': 1.0 (open to all)
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 (0-1)
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 0.1;
66
+ return 5; // SECRET
65
67
  }
66
68
  if (lowerTags.includes('public')) {
67
- return 1.0;
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 0.75;
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 0.5;
86
+ return 3; // CONFIDENTIAL
85
87
 
86
88
  // Business
87
89
  case 'invoice':
88
90
  case 'contract':
89
- return 0.5;
91
+ return 3; // CONFIDENTIAL
90
92
 
91
93
  // Communication
92
94
  case 'email':
93
95
  case 'conversation':
94
96
  case 'meeting':
95
- return 0.5;
97
+ return 3; // CONFIDENTIAL
96
98
 
97
99
  // Ghost conversations — private by default
98
100
  case 'ghost':
99
- return 0.75;
101
+ return 4; // RESTRICTED
100
102
 
101
103
  // Default — conservative
102
104
  default:
103
- return 0.25;
105
+ return 2; // INTERNAL
104
106
  }
105
107
  }
@@ -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
  {