@prmichaelsen/remember-mcp 2.8.0 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/AGENT.md +296 -250
  2. package/CHANGELOG.md +468 -0
  3. package/README.md +163 -46
  4. package/agent/commands/acp.clarification-create.md +382 -0
  5. package/agent/commands/acp.command-create.md +0 -1
  6. package/agent/commands/acp.design-create.md +0 -1
  7. package/agent/commands/acp.init.md +0 -1
  8. package/agent/commands/acp.package-create.md +0 -1
  9. package/agent/commands/acp.package-info.md +0 -1
  10. package/agent/commands/acp.package-install.md +0 -1
  11. package/agent/commands/acp.package-list.md +0 -1
  12. package/agent/commands/acp.package-publish.md +0 -1
  13. package/agent/commands/acp.package-remove.md +0 -1
  14. package/agent/commands/acp.package-search.md +0 -1
  15. package/agent/commands/acp.package-update.md +0 -1
  16. package/agent/commands/acp.package-validate.md +0 -1
  17. package/agent/commands/acp.pattern-create.md +0 -1
  18. package/agent/commands/acp.plan.md +0 -1
  19. package/agent/commands/acp.proceed.md +0 -1
  20. package/agent/commands/acp.project-create.md +0 -1
  21. package/agent/commands/acp.project-info.md +309 -0
  22. package/agent/commands/acp.project-list.md +0 -1
  23. package/agent/commands/acp.project-remove.md +379 -0
  24. package/agent/commands/acp.project-set.md +0 -1
  25. package/agent/commands/acp.project-update.md +296 -0
  26. package/agent/commands/acp.report.md +0 -1
  27. package/agent/commands/acp.resume.md +0 -1
  28. package/agent/commands/acp.status.md +0 -1
  29. package/agent/commands/acp.sync.md +0 -1
  30. package/agent/commands/acp.task-create.md +17 -10
  31. package/agent/commands/acp.update.md +0 -1
  32. package/agent/commands/acp.validate.md +0 -1
  33. package/agent/commands/acp.version-check-for-updates.md +0 -1
  34. package/agent/commands/acp.version-check.md +0 -1
  35. package/agent/commands/acp.version-update.md +0 -1
  36. package/agent/commands/command.template.md +0 -5
  37. package/agent/commands/git.commit.md +13 -2
  38. package/agent/commands/git.init.md +0 -1
  39. package/agent/design/comment-memory-type.md +2 -2
  40. package/agent/design/local.collaborative-memory-sync.md +265 -0
  41. package/agent/design/local.content-flags.md +210 -0
  42. package/agent/design/local.ghost-persona-system.md +273 -0
  43. package/agent/design/local.group-acl-integration.md +338 -0
  44. package/agent/design/local.memory-acl-schema.md +352 -0
  45. package/agent/design/local.memory-collection-pattern-v2.md +348 -0
  46. package/agent/design/local.moderation-and-space-config.md +257 -0
  47. package/agent/design/local.v2-api-reference.md +621 -0
  48. package/agent/design/local.v2-migration-guide.md +191 -0
  49. package/agent/design/local.v2-usage-examples.md +265 -0
  50. package/agent/design/permissions-storage-architecture.md +11 -3
  51. package/agent/design/soft-delete-system.md +291 -0
  52. package/agent/design/trust-escalation-prevention.md +9 -2
  53. package/agent/design/trust-system-implementation.md +12 -3
  54. package/agent/milestones/milestone-13-soft-delete-system.md +306 -0
  55. package/agent/milestones/milestone-14-memory-collection-v2.md +182 -0
  56. package/agent/milestones/milestone-15-moderation-space-config.md +126 -0
  57. package/agent/package.template.yaml +0 -17
  58. package/agent/progress.yaml +762 -49
  59. package/agent/scripts/acp.common.sh +2 -0
  60. package/agent/scripts/acp.install.sh +15 -85
  61. package/agent/scripts/acp.package-install-optimized.sh +454 -0
  62. package/agent/scripts/acp.package-install.sh +248 -380
  63. package/agent/scripts/acp.package-validate.sh +0 -99
  64. package/agent/scripts/acp.project-info.sh +218 -0
  65. package/agent/scripts/acp.project-remove.sh +302 -0
  66. package/agent/scripts/acp.project-update.sh +296 -0
  67. package/agent/scripts/acp.yaml-parser.sh +128 -10
  68. package/agent/tasks/milestone-14-memory-collection-v2/task-165-core-infrastructure-setup.md +171 -0
  69. package/agent/tasks/milestone-14-memory-collection-v2/task-166-update-remember-publish.md +191 -0
  70. package/agent/tasks/milestone-14-memory-collection-v2/task-167-update-remember-retract.md +186 -0
  71. package/agent/tasks/milestone-14-memory-collection-v2/task-168-implement-remember-revise.md +184 -0
  72. package/agent/tasks/milestone-14-memory-collection-v2/task-169-update-remember-search-space.md +179 -0
  73. package/agent/tasks/milestone-14-memory-collection-v2/task-170-update-remember-create-update.md +139 -0
  74. package/agent/tasks/milestone-14-memory-collection-v2/task-172-performance-testing-optimization.md +161 -0
  75. package/agent/tasks/milestone-14-memory-collection-v2/task-173-documentation-examples.md +258 -0
  76. package/agent/tasks/milestone-15-moderation-space-config/task-174-add-moderation-schema-fields.md +57 -0
  77. package/agent/tasks/milestone-15-moderation-space-config/task-175-create-space-config-service.md +64 -0
  78. package/agent/tasks/milestone-15-moderation-space-config/task-176-wire-moderation-publish-flow.md +45 -0
  79. package/agent/tasks/milestone-15-moderation-space-config/task-177-add-moderation-search-filters.md +70 -0
  80. package/agent/tasks/milestone-15-moderation-space-config/task-178-create-remember-moderate-tool.md +69 -0
  81. package/agent/tasks/milestone-15-moderation-space-config/task-179-documentation-integration-tests.md +58 -0
  82. package/agent/tasks/milestone-16-ghost-system/task-187-ghost-config-firestore.md +41 -0
  83. package/agent/tasks/milestone-16-ghost-system/task-188-trust-filter-integration.md +44 -0
  84. package/agent/tasks/milestone-16-ghost-system/task-189-ghost-memory-filtering.md +43 -0
  85. package/agent/tasks/milestone-16-ghost-system/task-190-ghost-config-tools.md +45 -0
  86. package/agent/tasks/milestone-16-ghost-system/task-191-escalation-firestore.md +38 -0
  87. package/agent/tasks/milestone-16-ghost-system/task-192-documentation-verification.md +39 -0
  88. package/agent/tasks/milestone-7-trust-permissions/task-180-access-result-permission-types.md +69 -0
  89. package/agent/tasks/milestone-7-trust-permissions/task-181-firestore-permissions-access-logs.md +56 -0
  90. package/agent/tasks/milestone-7-trust-permissions/task-182-trust-enforcement-service.md +68 -0
  91. package/agent/tasks/milestone-7-trust-permissions/task-183-access-control-service.md +70 -0
  92. package/agent/tasks/milestone-7-trust-permissions/task-184-permission-tools.md +79 -0
  93. package/agent/tasks/milestone-7-trust-permissions/task-185-wire-trust-into-search-query.md +55 -0
  94. package/agent/tasks/milestone-7-trust-permissions/task-186-documentation-verification.md +56 -0
  95. package/agent/tasks/task-70-add-soft-delete-schema-fields.md +165 -0
  96. package/agent/tasks/task-71-implement-delete-confirmation-flow.md +257 -0
  97. package/agent/tasks/task-72-add-deleted-filter-to-search-tools.md +18 -0
  98. package/agent/tasks/task-73-update-relationship-handling.md +18 -0
  99. package/agent/tasks/task-74-add-unit-tests-soft-delete.md +18 -0
  100. package/agent/tasks/task-75-update-documentation-changelog.md +26 -0
  101. package/agent/tasks/task-76-fix-indexnullstate-schema-bug.md +197 -0
  102. package/dist/collections/composite-ids.d.ts +106 -0
  103. package/dist/collections/core-infrastructure.spec.d.ts +11 -0
  104. package/dist/collections/dot-notation.d.ts +106 -0
  105. package/dist/collections/tracking-arrays.d.ts +176 -0
  106. package/dist/constants/content-types.d.ts +1 -0
  107. package/dist/schema/v2-collections-comments.spec.d.ts +8 -0
  108. package/dist/schema/v2-collections.d.ts +210 -0
  109. package/dist/server-factory.d.ts +15 -0
  110. package/dist/server-factory.js +3261 -1316
  111. package/dist/server.js +2926 -1236
  112. package/dist/services/access-control.d.ts +103 -0
  113. package/dist/services/access-control.spec.d.ts +2 -0
  114. package/dist/services/credentials-provider.d.ts +24 -0
  115. package/dist/services/credentials-provider.spec.d.ts +2 -0
  116. package/dist/services/escalation.service.d.ts +22 -0
  117. package/dist/services/escalation.service.spec.d.ts +2 -0
  118. package/dist/services/ghost-config.service.d.ts +55 -0
  119. package/dist/services/ghost-config.service.spec.d.ts +2 -0
  120. package/dist/services/space-config.service.d.ts +23 -0
  121. package/dist/services/space-config.service.spec.d.ts +2 -0
  122. package/dist/services/trust-enforcement.d.ts +83 -0
  123. package/dist/services/trust-enforcement.spec.d.ts +2 -0
  124. package/dist/services/trust-validator.d.ts +43 -0
  125. package/dist/services/trust-validator.spec.d.ts +2 -0
  126. package/dist/tools/confirm-publish-moderation.spec.d.ts +8 -0
  127. package/dist/tools/confirm.d.ts +8 -1
  128. package/dist/tools/create-memory.d.ts +2 -1
  129. package/dist/tools/create-memory.spec.d.ts +10 -0
  130. package/dist/tools/create-relationship.d.ts +2 -1
  131. package/dist/tools/delete-memory.d.ts +7 -31
  132. package/dist/tools/delete-relationship.d.ts +2 -1
  133. package/dist/tools/deny.d.ts +2 -1
  134. package/dist/tools/find-similar.d.ts +10 -2
  135. package/dist/tools/get-preferences.d.ts +2 -1
  136. package/dist/tools/ghost-config.d.ts +27 -0
  137. package/dist/tools/ghost-config.spec.d.ts +2 -0
  138. package/dist/tools/moderate.d.ts +20 -0
  139. package/dist/tools/moderate.spec.d.ts +5 -0
  140. package/dist/tools/publish.d.ts +11 -3
  141. package/dist/tools/query-memory.d.ts +11 -2
  142. package/dist/tools/query-space.d.ts +4 -1
  143. package/dist/tools/retract.d.ts +29 -0
  144. package/dist/tools/revise.d.ts +45 -0
  145. package/dist/tools/revise.spec.d.ts +8 -0
  146. package/dist/tools/search-memory.d.ts +8 -1
  147. package/dist/tools/search-relationship.d.ts +10 -2
  148. package/dist/tools/search-space.d.ts +25 -5
  149. package/dist/tools/search-space.spec.d.ts +9 -0
  150. package/dist/tools/set-preference.d.ts +2 -1
  151. package/dist/tools/update-memory.d.ts +2 -1
  152. package/dist/tools/update-relationship.d.ts +2 -1
  153. package/dist/types/access-result.d.ts +48 -0
  154. package/dist/types/access-result.spec.d.ts +2 -0
  155. package/dist/types/auth.d.ts +46 -0
  156. package/dist/types/ghost-config.d.ts +36 -0
  157. package/dist/types/memory.d.ts +11 -1
  158. package/dist/types/preferences.d.ts +1 -1
  159. package/dist/types/space-memory.d.ts +3 -0
  160. package/dist/utils/auth-helpers.d.ts +14 -0
  161. package/dist/utils/auth-helpers.spec.d.ts +2 -0
  162. package/dist/utils/test-data-generator.d.ts +124 -0
  163. package/dist/utils/test-data-generator.spec.d.ts +12 -0
  164. package/dist/utils/weaviate-filters.d.ts +19 -0
  165. package/dist/v2-performance.e2e.d.ts +17 -0
  166. package/dist/v2-smoke.e2e.d.ts +14 -0
  167. package/dist/weaviate/client.d.ts +5 -8
  168. package/dist/weaviate/space-schema.d.ts +2 -2
  169. package/docs/performance/v2-benchmarks.md +80 -0
  170. package/jest.e2e.config.js +14 -3
  171. package/package.json +1 -1
  172. package/scripts/.collection-recreation-state.yaml +16 -0
  173. package/scripts/.gitkeep +5 -0
  174. package/scripts/README-collection-recreation.md +224 -0
  175. package/scripts/README.md +51 -0
  176. package/scripts/backup-collections.ts +543 -0
  177. package/scripts/delete-collection.ts +137 -0
  178. package/scripts/migrate-recreate-collections.ts +578 -0
  179. package/scripts/migrate-v1-to-v2.ts +1094 -0
  180. package/scripts/package-lock.json +1113 -0
  181. package/scripts/package.json +27 -0
  182. package/src/collections/composite-ids.ts +193 -0
  183. package/src/collections/core-infrastructure.spec.ts +353 -0
  184. package/src/collections/dot-notation.ts +212 -0
  185. package/src/collections/tracking-arrays.ts +298 -0
  186. package/src/constants/content-types.ts +20 -0
  187. package/src/schema/v2-collections-comments.spec.ts +141 -0
  188. package/src/schema/v2-collections.ts +433 -0
  189. package/src/server-factory.ts +89 -20
  190. package/src/server.ts +45 -17
  191. package/src/services/access-control.spec.ts +383 -0
  192. package/src/services/access-control.ts +291 -0
  193. package/src/services/credentials-provider.spec.ts +22 -0
  194. package/src/services/credentials-provider.ts +34 -0
  195. package/src/services/escalation.service.spec.ts +183 -0
  196. package/src/services/escalation.service.ts +150 -0
  197. package/src/services/ghost-config.service.spec.ts +339 -0
  198. package/src/services/ghost-config.service.ts +219 -0
  199. package/src/services/space-config.service.spec.ts +102 -0
  200. package/src/services/space-config.service.ts +79 -0
  201. package/src/services/trust-enforcement.spec.ts +309 -0
  202. package/src/services/trust-enforcement.ts +197 -0
  203. package/src/services/trust-validator.spec.ts +108 -0
  204. package/src/services/trust-validator.ts +105 -0
  205. package/src/tools/confirm-publish-moderation.spec.ts +240 -0
  206. package/src/tools/confirm.ts +914 -116
  207. package/src/tools/create-memory.spec.ts +126 -0
  208. package/src/tools/create-memory.ts +20 -27
  209. package/src/tools/create-relationship.ts +30 -8
  210. package/src/tools/delete-memory.ts +99 -64
  211. package/src/tools/delete-relationship.ts +15 -6
  212. package/src/tools/deny.ts +8 -1
  213. package/src/tools/find-similar.ts +44 -6
  214. package/src/tools/get-preferences.ts +10 -1
  215. package/src/tools/ghost-config.spec.ts +180 -0
  216. package/src/tools/ghost-config.ts +230 -0
  217. package/src/tools/moderate.spec.ts +277 -0
  218. package/src/tools/moderate.ts +219 -0
  219. package/src/tools/publish.ts +99 -41
  220. package/src/tools/query-memory.ts +44 -9
  221. package/src/tools/query-space.ts +39 -4
  222. package/src/tools/retract.ts +292 -0
  223. package/src/tools/revise.spec.ts +146 -0
  224. package/src/tools/revise.ts +283 -0
  225. package/src/tools/search-memory.ts +46 -10
  226. package/src/tools/search-relationship.ts +30 -7
  227. package/src/tools/search-space.spec.ts +341 -0
  228. package/src/tools/search-space.ts +323 -99
  229. package/src/tools/set-preference.ts +10 -1
  230. package/src/tools/update-memory.ts +24 -5
  231. package/src/tools/update-relationship.ts +10 -1
  232. package/src/types/access-result.spec.ts +193 -0
  233. package/src/types/access-result.ts +62 -0
  234. package/src/types/auth.ts +52 -0
  235. package/src/types/ghost-config.ts +46 -0
  236. package/src/types/memory.ts +20 -1
  237. package/src/types/preferences.ts +2 -2
  238. package/src/types/space-memory.ts +5 -0
  239. package/src/utils/auth-helpers.spec.ts +75 -0
  240. package/src/utils/auth-helpers.ts +25 -0
  241. package/src/utils/test-data-generator.spec.ts +317 -0
  242. package/src/utils/test-data-generator.ts +292 -0
  243. package/src/utils/weaviate-filters.ts +32 -5
  244. package/src/v2-performance.e2e.ts +173 -0
  245. package/src/v2-smoke.e2e.ts +401 -0
  246. package/src/weaviate/client.spec.ts +5 -5
  247. package/src/weaviate/client.ts +55 -35
  248. package/src/weaviate/schema.ts +11 -239
  249. package/src/weaviate/space-schema.spec.ts +28 -25
  250. package/src/weaviate/space-schema.ts +35 -11
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Trust validator — validation and suggestions for trust-sensitive operations.
3
+ *
4
+ * See agent/design/local.ghost-persona-system.md
5
+ */
6
+
7
+ import type { ContentType } from '../types/memory.js';
8
+
9
+ /**
10
+ * Validation result for trust assignment.
11
+ */
12
+ export interface TrustValidationResult {
13
+ valid: boolean;
14
+ warning?: string;
15
+ }
16
+
17
+ /**
18
+ * Validate a trust level assignment.
19
+ * Warns if trust < 0.25 (very private — existence-only for most accessors).
20
+ * Returns invalid for out-of-range values.
21
+ *
22
+ * @param trustLevel - The trust level being assigned (0-1)
23
+ * @param content - Optional content for context-aware validation
24
+ */
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}` };
28
+ }
29
+
30
+ if (trustLevel < 0.25) {
31
+ return {
32
+ 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.`,
34
+ };
35
+ }
36
+
37
+ return { valid: true };
38
+ }
39
+
40
+ /**
41
+ * Suggest an appropriate trust level based on content type and tags.
42
+ *
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 (conservative — metadata only)
50
+ *
51
+ * Tag overrides:
52
+ * - 'private' or 'secret': 0.1 (near-hidden)
53
+ * - 'public': 1.0 (open to all)
54
+ *
55
+ * @param contentType - The type of content
56
+ * @param tags - Optional tags that may affect suggestion
57
+ * @returns Suggested trust level (0-1)
58
+ */
59
+ export function suggestTrustLevel(contentType: ContentType, tags?: string[]): number {
60
+ // Tag-based overrides take priority
61
+ if (tags && tags.length > 0) {
62
+ const lowerTags = tags.map(t => t.toLowerCase());
63
+ if (lowerTags.includes('private') || lowerTags.includes('secret')) {
64
+ return 0.1;
65
+ }
66
+ if (lowerTags.includes('public')) {
67
+ return 1.0;
68
+ }
69
+ }
70
+
71
+ // Content type-based suggestions
72
+ switch (contentType) {
73
+ // Personal — higher trust needed
74
+ case 'journal':
75
+ case 'memory':
76
+ case 'event':
77
+ return 0.75;
78
+
79
+ // System/internal
80
+ case 'system':
81
+ case 'audit':
82
+ case 'action':
83
+ case 'history':
84
+ return 0.5;
85
+
86
+ // Business
87
+ case 'invoice':
88
+ case 'contract':
89
+ return 0.5;
90
+
91
+ // Communication
92
+ case 'email':
93
+ case 'conversation':
94
+ case 'meeting':
95
+ return 0.5;
96
+
97
+ // Ghost conversations — private by default
98
+ case 'ghost':
99
+ return 0.75;
100
+
101
+ // Default — conservative
102
+ default:
103
+ return 0.25;
104
+ }
105
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Tests for moderation status wiring in the publish flow.
3
+ *
4
+ * Verifies that executePublishMemory() sets moderation_status
5
+ * based on SpaceConfig.require_moderation for each destination.
6
+ */
7
+
8
+ import { handleConfirm } from './confirm.js';
9
+
10
+ // ─── Mocks (factories only — configured in beforeEach) ──────
11
+
12
+ jest.mock('../weaviate/client.js', () => ({
13
+ getWeaviateClient: jest.fn(),
14
+ getMemoryCollectionName: jest.fn((userId: string) => `Memory_users_${userId}`),
15
+ fetchMemoryWithAllProperties: jest.fn(),
16
+ }));
17
+
18
+ jest.mock('../weaviate/space-schema.js', () => ({
19
+ ensurePublicCollection: jest.fn(),
20
+ }));
21
+
22
+ jest.mock('../services/confirmation-token.service.js', () => ({
23
+ confirmationTokenService: { confirmRequest: jest.fn() },
24
+ }));
25
+
26
+ jest.mock('../services/space-config.service.js', () => ({
27
+ getSpaceConfig: jest.fn(),
28
+ }));
29
+
30
+ jest.mock('../utils/logger.js', () => ({
31
+ logger: { info: jest.fn(), debug: jest.fn(), warn: jest.fn(), error: jest.fn() },
32
+ }));
33
+
34
+ jest.mock('../utils/debug.js', () => ({
35
+ createDebugLogger: jest.fn(() => ({
36
+ info: jest.fn(),
37
+ debug: jest.fn(),
38
+ trace: jest.fn(),
39
+ error: jest.fn(),
40
+ time: jest.fn((_: string, fn: () => any) => fn()),
41
+ })),
42
+ }));
43
+
44
+ jest.mock('../collections/dot-notation.js', () => ({
45
+ CollectionType: { GROUPS: 'groups' },
46
+ getCollectionName: jest.fn((_: string, id: string) => `Memory_groups_${id}`),
47
+ }));
48
+
49
+ jest.mock('../collections/composite-ids.js', () => ({
50
+ generateCompositeId: jest.fn((u: string, m: string) => `${u}.${m}`),
51
+ parseCompositeId: jest.fn(),
52
+ }));
53
+
54
+ jest.mock('../collections/tracking-arrays.js', () => ({
55
+ addToSpaceIds: jest.fn(),
56
+ addToGroupIds: jest.fn(),
57
+ removeFromSpaceIds: jest.fn(),
58
+ removeFromGroupIds: jest.fn(),
59
+ getPublishedLocations: jest.fn(),
60
+ }));
61
+
62
+ jest.mock('../utils/error-handler.js', () => ({
63
+ handleToolError: jest.fn(),
64
+ }));
65
+
66
+ // ─── Import mocked modules ──────────────────────────────────
67
+
68
+ import { getWeaviateClient, fetchMemoryWithAllProperties } from '../weaviate/client.js';
69
+ import { ensurePublicCollection } from '../weaviate/space-schema.js';
70
+ import { confirmationTokenService } from '../services/confirmation-token.service.js';
71
+ import { getSpaceConfig } from '../services/space-config.service.js';
72
+
73
+ const mockGetWeaviateClient = getWeaviateClient as jest.MockedFunction<any>;
74
+ const mockFetchMemory = fetchMemoryWithAllProperties as jest.MockedFunction<any>;
75
+ const mockEnsurePublicCollection = ensurePublicCollection as jest.MockedFunction<any>;
76
+ const mockConfirmRequest = confirmationTokenService.confirmRequest as jest.MockedFunction<any>;
77
+ const mockGetSpaceConfig = getSpaceConfig as jest.MockedFunction<any>;
78
+
79
+ // ─── Shared per-test mock state ──────────────────────────────
80
+
81
+ let spaceInsert: jest.Mock;
82
+ let spaceUpdate: jest.Mock;
83
+ let groupInsert: jest.Mock;
84
+ let groupUpdate: jest.Mock;
85
+ let userUpdate: jest.Mock;
86
+
87
+ const ORIGINAL_MEMORY = {
88
+ properties: {
89
+ user_id: 'user-1',
90
+ content: 'Test memory',
91
+ content_type: 'text',
92
+ tags: ['test'],
93
+ space_ids: [],
94
+ group_ids: [],
95
+ },
96
+ };
97
+
98
+ function makePublishRequest(overrides: Record<string, any> = {}) {
99
+ return {
100
+ request_id: 'req-1',
101
+ userId: 'user-1',
102
+ action: 'publish_memory' as const,
103
+ payload: { memory_id: 'mem-1', spaces: [], groups: [], ...overrides },
104
+ createdAt: Date.now(),
105
+ expiresAt: Date.now() + 300_000,
106
+ status: 'confirmed',
107
+ };
108
+ }
109
+
110
+ // ─── Tests ───────────────────────────────────────────────────
111
+
112
+ describe('publish moderation wiring', () => {
113
+ beforeEach(() => {
114
+ jest.clearAllMocks();
115
+
116
+ // Fresh mock functions per test
117
+ spaceInsert = jest.fn().mockResolvedValue(undefined);
118
+ spaceUpdate = jest.fn().mockResolvedValue(undefined);
119
+ groupInsert = jest.fn().mockResolvedValue(undefined);
120
+ groupUpdate = jest.fn().mockResolvedValue(undefined);
121
+ userUpdate = jest.fn().mockResolvedValue(undefined);
122
+
123
+ // Weaviate client: route collections by name
124
+ mockGetWeaviateClient.mockReturnValue({
125
+ collections: {
126
+ get: jest.fn().mockImplementation((name: string) => {
127
+ if (name.startsWith('Memory_groups_')) {
128
+ return { data: { insert: groupInsert, update: groupUpdate } };
129
+ }
130
+ // User collection
131
+ return { data: { insert: jest.fn(), update: userUpdate } };
132
+ }),
133
+ },
134
+ });
135
+
136
+ // Space collection via ensurePublicCollection
137
+ mockEnsurePublicCollection.mockResolvedValue({
138
+ data: { insert: spaceInsert, update: spaceUpdate },
139
+ });
140
+
141
+ // fetchMemoryWithAllProperties: 1st call = original, subsequent = null (not already published)
142
+ mockFetchMemory
143
+ .mockResolvedValueOnce(ORIGINAL_MEMORY)
144
+ .mockResolvedValue(null);
145
+
146
+ // Default: unmoderated
147
+ mockGetSpaceConfig.mockResolvedValue({
148
+ require_moderation: false,
149
+ default_write_mode: 'owner_only',
150
+ });
151
+ });
152
+
153
+ describe('spaces publication', () => {
154
+ it('sets moderation_status to approved for unmoderated space', async () => {
155
+ mockConfirmRequest.mockResolvedValue(makePublishRequest({ spaces: ['public'] }));
156
+
157
+ await handleConfirm({ token: 'tok-1' }, 'user-1');
158
+
159
+ expect(mockGetSpaceConfig).toHaveBeenCalledWith('public', 'space');
160
+ expect(spaceInsert).toHaveBeenCalledTimes(1);
161
+ expect(spaceInsert.mock.calls[0][0].properties.moderation_status).toBe('approved');
162
+ });
163
+
164
+ it('sets moderation_status to pending for moderated space', async () => {
165
+ mockConfirmRequest.mockResolvedValue(makePublishRequest({ spaces: ['moderated-space'] }));
166
+ mockGetSpaceConfig.mockResolvedValue({ require_moderation: true, default_write_mode: 'owner_only' });
167
+
168
+ await handleConfirm({ token: 'tok-2' }, 'user-1');
169
+
170
+ expect(mockGetSpaceConfig).toHaveBeenCalledWith('moderated-space', 'space');
171
+ expect(spaceInsert).toHaveBeenCalledTimes(1);
172
+ expect(spaceInsert.mock.calls[0][0].properties.moderation_status).toBe('pending');
173
+ });
174
+
175
+ it('sets pending if any of multiple spaces requires moderation', async () => {
176
+ mockConfirmRequest.mockResolvedValue(makePublishRequest({ spaces: ['open', 'strict'] }));
177
+ mockGetSpaceConfig
178
+ .mockResolvedValueOnce({ require_moderation: false, default_write_mode: 'owner_only' })
179
+ .mockResolvedValueOnce({ require_moderation: true, default_write_mode: 'owner_only' });
180
+
181
+ await handleConfirm({ token: 'tok-3' }, 'user-1');
182
+
183
+ expect(spaceInsert).toHaveBeenCalledTimes(1);
184
+ expect(spaceInsert.mock.calls[0][0].properties.moderation_status).toBe('pending');
185
+ });
186
+ });
187
+
188
+ describe('groups publication', () => {
189
+ it('sets moderation_status to approved for unmoderated group', async () => {
190
+ mockConfirmRequest.mockResolvedValue(makePublishRequest({ groups: ['team-alpha'] }));
191
+
192
+ await handleConfirm({ token: 'tok-4' }, 'user-1');
193
+
194
+ expect(mockGetSpaceConfig).toHaveBeenCalledWith('team-alpha', 'group');
195
+ expect(groupInsert).toHaveBeenCalledTimes(1);
196
+ expect(groupInsert.mock.calls[0][0].properties.moderation_status).toBe('approved');
197
+ });
198
+
199
+ it('sets moderation_status to pending for moderated group', async () => {
200
+ mockConfirmRequest.mockResolvedValue(makePublishRequest({ groups: ['strict-group'] }));
201
+ mockGetSpaceConfig.mockResolvedValue({ require_moderation: true, default_write_mode: 'owner_only' });
202
+
203
+ await handleConfirm({ token: 'tok-5' }, 'user-1');
204
+
205
+ expect(mockGetSpaceConfig).toHaveBeenCalledWith('strict-group', 'group');
206
+ expect(groupInsert).toHaveBeenCalledTimes(1);
207
+ expect(groupInsert.mock.calls[0][0].properties.moderation_status).toBe('pending');
208
+ });
209
+
210
+ it('sets independent moderation status per group', async () => {
211
+ mockConfirmRequest.mockResolvedValue(makePublishRequest({ groups: ['open-group', 'strict-group'] }));
212
+ // Reset fetchMemory: 1st = original, 2nd = null (open-group), 3rd = null (strict-group)
213
+ mockFetchMemory.mockReset();
214
+ mockFetchMemory
215
+ .mockResolvedValueOnce(ORIGINAL_MEMORY)
216
+ .mockResolvedValue(null);
217
+
218
+ mockGetSpaceConfig
219
+ .mockResolvedValueOnce({ require_moderation: false, default_write_mode: 'owner_only' })
220
+ .mockResolvedValueOnce({ require_moderation: true, default_write_mode: 'owner_only' });
221
+
222
+ await handleConfirm({ token: 'tok-6' }, 'user-1');
223
+
224
+ expect(groupInsert).toHaveBeenCalledTimes(2);
225
+ expect(groupInsert.mock.calls[0][0].properties.moderation_status).toBe('approved');
226
+ expect(groupInsert.mock.calls[1][0].properties.moderation_status).toBe('pending');
227
+ });
228
+ });
229
+
230
+ describe('default behavior', () => {
231
+ it('defaults to approved when getSpaceConfig returns defaults', async () => {
232
+ mockConfirmRequest.mockResolvedValue(makePublishRequest({ spaces: ['unknown-space'] }));
233
+
234
+ await handleConfirm({ token: 'tok-7' }, 'user-1');
235
+
236
+ expect(spaceInsert).toHaveBeenCalledTimes(1);
237
+ expect(spaceInsert.mock.calls[0][0].properties.moderation_status).toBe('approved');
238
+ });
239
+ });
240
+ });