@prmichaelsen/remember-mcp 3.0.0 → 3.13.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 (208) hide show
  1. package/AGENT.md +296 -250
  2. package/CHANGELOG.md +358 -0
  3. package/README.md +68 -45
  4. package/agent/commands/acp.clarification-create.md +382 -0
  5. package/agent/commands/acp.project-info.md +309 -0
  6. package/agent/commands/acp.project-remove.md +379 -0
  7. package/agent/commands/acp.project-update.md +296 -0
  8. package/agent/commands/acp.task-create.md +17 -9
  9. package/agent/commands/git.commit.md +13 -1
  10. package/agent/design/comment-memory-type.md +2 -2
  11. package/agent/design/local.collaborative-memory-sync.md +265 -0
  12. package/agent/design/local.content-flags.md +210 -0
  13. package/agent/design/local.ghost-persona-system.md +273 -0
  14. package/agent/design/local.group-acl-integration.md +338 -0
  15. package/agent/design/local.memory-acl-schema.md +352 -0
  16. package/agent/design/local.memory-collection-pattern-v2.md +348 -0
  17. package/agent/design/local.moderation-and-space-config.md +257 -0
  18. package/agent/design/local.v2-api-reference.md +621 -0
  19. package/agent/design/local.v2-migration-guide.md +191 -0
  20. package/agent/design/local.v2-usage-examples.md +265 -0
  21. package/agent/design/permissions-storage-architecture.md +11 -3
  22. package/agent/design/trust-escalation-prevention.md +9 -2
  23. package/agent/design/trust-system-implementation.md +12 -3
  24. package/agent/milestones/milestone-14-memory-collection-v2.md +182 -0
  25. package/agent/milestones/milestone-15-moderation-space-config.md +126 -0
  26. package/agent/progress.yaml +628 -49
  27. package/agent/scripts/acp.common.sh +2 -0
  28. package/agent/scripts/acp.install.sh +11 -1
  29. package/agent/scripts/acp.package-install-optimized.sh +454 -0
  30. package/agent/scripts/acp.package-install.sh +247 -300
  31. package/agent/scripts/acp.project-info.sh +218 -0
  32. package/agent/scripts/acp.project-remove.sh +302 -0
  33. package/agent/scripts/acp.project-update.sh +296 -0
  34. package/agent/scripts/acp.yaml-parser.sh +128 -10
  35. package/agent/tasks/milestone-14-memory-collection-v2/task-165-core-infrastructure-setup.md +171 -0
  36. package/agent/tasks/milestone-14-memory-collection-v2/task-166-update-remember-publish.md +191 -0
  37. package/agent/tasks/milestone-14-memory-collection-v2/task-167-update-remember-retract.md +186 -0
  38. package/agent/tasks/milestone-14-memory-collection-v2/task-168-implement-remember-revise.md +184 -0
  39. package/agent/tasks/milestone-14-memory-collection-v2/task-169-update-remember-search-space.md +179 -0
  40. package/agent/tasks/milestone-14-memory-collection-v2/task-170-update-remember-create-update.md +139 -0
  41. package/agent/tasks/milestone-14-memory-collection-v2/task-172-performance-testing-optimization.md +161 -0
  42. package/agent/tasks/milestone-14-memory-collection-v2/task-173-documentation-examples.md +258 -0
  43. package/agent/tasks/milestone-15-moderation-space-config/task-174-add-moderation-schema-fields.md +57 -0
  44. package/agent/tasks/milestone-15-moderation-space-config/task-175-create-space-config-service.md +64 -0
  45. package/agent/tasks/milestone-15-moderation-space-config/task-176-wire-moderation-publish-flow.md +45 -0
  46. package/agent/tasks/milestone-15-moderation-space-config/task-177-add-moderation-search-filters.md +70 -0
  47. package/agent/tasks/milestone-15-moderation-space-config/task-178-create-remember-moderate-tool.md +69 -0
  48. package/agent/tasks/milestone-15-moderation-space-config/task-179-documentation-integration-tests.md +58 -0
  49. package/agent/tasks/milestone-16-ghost-system/task-187-ghost-config-firestore.md +41 -0
  50. package/agent/tasks/milestone-16-ghost-system/task-188-trust-filter-integration.md +44 -0
  51. package/agent/tasks/milestone-16-ghost-system/task-189-ghost-memory-filtering.md +43 -0
  52. package/agent/tasks/milestone-16-ghost-system/task-190-ghost-config-tools.md +45 -0
  53. package/agent/tasks/milestone-16-ghost-system/task-191-escalation-firestore.md +38 -0
  54. package/agent/tasks/milestone-16-ghost-system/task-192-documentation-verification.md +39 -0
  55. package/agent/tasks/milestone-7-trust-permissions/task-180-access-result-permission-types.md +69 -0
  56. package/agent/tasks/milestone-7-trust-permissions/task-181-firestore-permissions-access-logs.md +56 -0
  57. package/agent/tasks/milestone-7-trust-permissions/task-182-trust-enforcement-service.md +68 -0
  58. package/agent/tasks/milestone-7-trust-permissions/task-183-access-control-service.md +70 -0
  59. package/agent/tasks/milestone-7-trust-permissions/task-184-permission-tools.md +79 -0
  60. package/agent/tasks/milestone-7-trust-permissions/task-185-wire-trust-into-search-query.md +55 -0
  61. package/agent/tasks/milestone-7-trust-permissions/task-186-documentation-verification.md +56 -0
  62. package/agent/tasks/task-76-fix-indexnullstate-schema-bug.md +197 -0
  63. package/dist/collections/composite-ids.d.ts +106 -0
  64. package/dist/collections/core-infrastructure.spec.d.ts +11 -0
  65. package/dist/collections/dot-notation.d.ts +106 -0
  66. package/dist/collections/tracking-arrays.d.ts +176 -0
  67. package/dist/constants/content-types.d.ts +1 -0
  68. package/dist/schema/v2-collections-comments.spec.d.ts +8 -0
  69. package/dist/schema/v2-collections.d.ts +210 -0
  70. package/dist/server-factory.d.ts +15 -0
  71. package/dist/server-factory.js +2798 -1029
  72. package/dist/server.js +2526 -1012
  73. package/dist/services/access-control.d.ts +103 -0
  74. package/dist/services/access-control.spec.d.ts +2 -0
  75. package/dist/services/credentials-provider.d.ts +24 -0
  76. package/dist/services/credentials-provider.spec.d.ts +2 -0
  77. package/dist/services/escalation.service.d.ts +22 -0
  78. package/dist/services/escalation.service.spec.d.ts +2 -0
  79. package/dist/services/ghost-config.service.d.ts +55 -0
  80. package/dist/services/ghost-config.service.spec.d.ts +2 -0
  81. package/dist/services/space-config.service.d.ts +23 -0
  82. package/dist/services/space-config.service.spec.d.ts +2 -0
  83. package/dist/services/trust-enforcement.d.ts +83 -0
  84. package/dist/services/trust-enforcement.spec.d.ts +2 -0
  85. package/dist/services/trust-validator.d.ts +43 -0
  86. package/dist/services/trust-validator.spec.d.ts +2 -0
  87. package/dist/tools/confirm-publish-moderation.spec.d.ts +8 -0
  88. package/dist/tools/confirm.d.ts +8 -1
  89. package/dist/tools/create-memory.d.ts +2 -1
  90. package/dist/tools/create-memory.spec.d.ts +10 -0
  91. package/dist/tools/create-relationship.d.ts +2 -1
  92. package/dist/tools/delete-memory.d.ts +2 -1
  93. package/dist/tools/delete-relationship.d.ts +2 -1
  94. package/dist/tools/deny.d.ts +2 -1
  95. package/dist/tools/find-similar.d.ts +2 -1
  96. package/dist/tools/get-preferences.d.ts +2 -1
  97. package/dist/tools/ghost-config.d.ts +27 -0
  98. package/dist/tools/ghost-config.spec.d.ts +2 -0
  99. package/dist/tools/moderate.d.ts +20 -0
  100. package/dist/tools/moderate.spec.d.ts +5 -0
  101. package/dist/tools/publish.d.ts +11 -3
  102. package/dist/tools/query-memory.d.ts +3 -1
  103. package/dist/tools/query-space.d.ts +4 -1
  104. package/dist/tools/retract.d.ts +29 -0
  105. package/dist/tools/revise.d.ts +45 -0
  106. package/dist/tools/revise.spec.d.ts +8 -0
  107. package/dist/tools/search-memory.d.ts +2 -1
  108. package/dist/tools/search-relationship.d.ts +2 -1
  109. package/dist/tools/search-space.d.ts +25 -5
  110. package/dist/tools/search-space.spec.d.ts +9 -0
  111. package/dist/tools/set-preference.d.ts +2 -1
  112. package/dist/tools/update-memory.d.ts +2 -1
  113. package/dist/tools/update-relationship.d.ts +2 -1
  114. package/dist/types/access-result.d.ts +48 -0
  115. package/dist/types/access-result.spec.d.ts +2 -0
  116. package/dist/types/auth.d.ts +46 -0
  117. package/dist/types/ghost-config.d.ts +36 -0
  118. package/dist/types/memory.d.ts +3 -1
  119. package/dist/types/preferences.d.ts +1 -1
  120. package/dist/utils/auth-helpers.d.ts +14 -0
  121. package/dist/utils/auth-helpers.spec.d.ts +2 -0
  122. package/dist/utils/test-data-generator.d.ts +124 -0
  123. package/dist/utils/test-data-generator.spec.d.ts +12 -0
  124. package/dist/v2-performance.e2e.d.ts +17 -0
  125. package/dist/v2-smoke.e2e.d.ts +14 -0
  126. package/dist/weaviate/client.d.ts +5 -8
  127. package/dist/weaviate/space-schema.d.ts +2 -2
  128. package/docs/performance/v2-benchmarks.md +80 -0
  129. package/jest.e2e.config.js +14 -3
  130. package/package.json +1 -1
  131. package/scripts/.collection-recreation-state.yaml +16 -0
  132. package/scripts/.gitkeep +5 -0
  133. package/scripts/README-collection-recreation.md +224 -0
  134. package/scripts/README.md +51 -0
  135. package/scripts/backup-collections.ts +543 -0
  136. package/scripts/delete-collection.ts +137 -0
  137. package/scripts/migrate-recreate-collections.ts +578 -0
  138. package/scripts/migrate-v1-to-v2.ts +1094 -0
  139. package/scripts/package-lock.json +1113 -0
  140. package/scripts/package.json +27 -0
  141. package/src/collections/composite-ids.ts +193 -0
  142. package/src/collections/core-infrastructure.spec.ts +353 -0
  143. package/src/collections/dot-notation.ts +212 -0
  144. package/src/collections/tracking-arrays.ts +298 -0
  145. package/src/constants/content-types.ts +20 -0
  146. package/src/schema/v2-collections-comments.spec.ts +141 -0
  147. package/src/schema/v2-collections.ts +433 -0
  148. package/src/server-factory.ts +89 -20
  149. package/src/server.ts +45 -17
  150. package/src/services/access-control.spec.ts +383 -0
  151. package/src/services/access-control.ts +291 -0
  152. package/src/services/credentials-provider.spec.ts +22 -0
  153. package/src/services/credentials-provider.ts +34 -0
  154. package/src/services/escalation.service.spec.ts +183 -0
  155. package/src/services/escalation.service.ts +150 -0
  156. package/src/services/ghost-config.service.spec.ts +339 -0
  157. package/src/services/ghost-config.service.ts +219 -0
  158. package/src/services/space-config.service.spec.ts +102 -0
  159. package/src/services/space-config.service.ts +79 -0
  160. package/src/services/trust-enforcement.spec.ts +309 -0
  161. package/src/services/trust-enforcement.ts +197 -0
  162. package/src/services/trust-validator.spec.ts +108 -0
  163. package/src/services/trust-validator.ts +105 -0
  164. package/src/tools/confirm-publish-moderation.spec.ts +240 -0
  165. package/src/tools/confirm.ts +869 -135
  166. package/src/tools/create-memory.spec.ts +126 -0
  167. package/src/tools/create-memory.ts +20 -27
  168. package/src/tools/create-relationship.ts +17 -8
  169. package/src/tools/delete-memory.ts +13 -6
  170. package/src/tools/delete-relationship.ts +15 -6
  171. package/src/tools/deny.ts +8 -1
  172. package/src/tools/find-similar.ts +21 -8
  173. package/src/tools/get-preferences.ts +10 -1
  174. package/src/tools/ghost-config.spec.ts +180 -0
  175. package/src/tools/ghost-config.ts +230 -0
  176. package/src/tools/moderate.spec.ts +277 -0
  177. package/src/tools/moderate.ts +219 -0
  178. package/src/tools/publish.ts +99 -41
  179. package/src/tools/query-memory.ts +28 -6
  180. package/src/tools/query-space.ts +39 -4
  181. package/src/tools/retract.ts +292 -0
  182. package/src/tools/revise.spec.ts +146 -0
  183. package/src/tools/revise.ts +283 -0
  184. package/src/tools/search-memory.ts +30 -7
  185. package/src/tools/search-relationship.ts +11 -2
  186. package/src/tools/search-space.spec.ts +341 -0
  187. package/src/tools/search-space.ts +323 -99
  188. package/src/tools/set-preference.ts +10 -1
  189. package/src/tools/update-memory.ts +16 -5
  190. package/src/tools/update-relationship.ts +10 -1
  191. package/src/types/access-result.spec.ts +193 -0
  192. package/src/types/access-result.ts +62 -0
  193. package/src/types/auth.ts +52 -0
  194. package/src/types/ghost-config.ts +46 -0
  195. package/src/types/memory.ts +9 -1
  196. package/src/types/preferences.ts +2 -2
  197. package/src/utils/auth-helpers.spec.ts +75 -0
  198. package/src/utils/auth-helpers.ts +25 -0
  199. package/src/utils/test-data-generator.spec.ts +317 -0
  200. package/src/utils/test-data-generator.ts +292 -0
  201. package/src/utils/weaviate-filters.ts +4 -4
  202. package/src/v2-performance.e2e.ts +173 -0
  203. package/src/v2-smoke.e2e.ts +401 -0
  204. package/src/weaviate/client.spec.ts +5 -5
  205. package/src/weaviate/client.ts +51 -36
  206. package/src/weaviate/schema.ts +11 -256
  207. package/src/weaviate/space-schema.spec.ts +24 -24
  208. package/src/weaviate/space-schema.ts +18 -6
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "remember-mcp-scripts",
3
+ "version": "1.0.0",
4
+ "description": "Migration and utility scripts for remember-mcp",
5
+ "type": "module",
6
+ "private": true,
7
+ "scripts": {
8
+ "backup": "tsx backup-collections.ts",
9
+ "backup:dry-run": "tsx backup-collections.ts --dry-run",
10
+ "delete": "tsx delete-collection.ts",
11
+ "migrate:recreate": "tsx migrate-recreate-collections.ts",
12
+ "migrate:recreate:dry-run": "tsx migrate-recreate-collections.ts --dry-run",
13
+ "migrate:v1-to-v2": "tsx migrate-v1-to-v2.ts",
14
+ "migrate:v1-to-v2:dry-run": "tsx migrate-v1-to-v2.ts --dry-run",
15
+ "migrate:v1-to-v2:verify": "tsx migrate-v1-to-v2.ts --verify-only"
16
+ },
17
+ "dependencies": {
18
+ "dotenv": "^16.4.5",
19
+ "weaviate-client": "^3.2.0",
20
+ "yaml": "^2.3.4"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^20.11.19",
24
+ "tsx": "^4.7.1",
25
+ "typescript": "^5.3.3"
26
+ }
27
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Composite ID Utilities
3
+ *
4
+ * Provides utilities for working with composite IDs in Memory Collection Pattern v2.
5
+ * Composite IDs preserve the source reference when memories are published to spaces or groups.
6
+ *
7
+ * Format: {userId}.{memoryId}
8
+ * Example: "user123.my-recipe"
9
+ */
10
+
11
+ /**
12
+ * Components of a composite ID
13
+ */
14
+ export interface CompositeIdComponents {
15
+ userId: string
16
+ memoryId: string
17
+ }
18
+
19
+ /**
20
+ * Error thrown when composite ID is invalid
21
+ */
22
+ export class InvalidCompositeIdError extends Error {
23
+ constructor(message: string) {
24
+ super(message)
25
+ this.name = 'InvalidCompositeIdError'
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Generate a composite ID from user ID and memory ID
31
+ *
32
+ * @param userId - User ID (must not contain dots)
33
+ * @param memoryId - Memory ID (must not contain dots)
34
+ * @returns Composite ID in format {userId}.{memoryId}
35
+ * @throws {InvalidCompositeIdError} If userId or memoryId contains dots
36
+ *
37
+ * @example
38
+ * generateCompositeId('user123', 'my-recipe')
39
+ * // Returns: 'user123.my-recipe'
40
+ */
41
+ export function generateCompositeId(userId: string, memoryId: string): string {
42
+ // Validate that neither component contains dots
43
+ if (userId.includes('.')) {
44
+ throw new InvalidCompositeIdError(
45
+ `User ID cannot contain dots: ${userId}`
46
+ )
47
+ }
48
+
49
+ if (memoryId.includes('.')) {
50
+ throw new InvalidCompositeIdError(
51
+ `Memory ID cannot contain dots: ${memoryId}`
52
+ )
53
+ }
54
+
55
+ // Validate that components are not empty
56
+ if (!userId.trim()) {
57
+ throw new InvalidCompositeIdError('User ID cannot be empty')
58
+ }
59
+
60
+ if (!memoryId.trim()) {
61
+ throw new InvalidCompositeIdError('Memory ID cannot be empty')
62
+ }
63
+
64
+ return `${userId}.${memoryId}`
65
+ }
66
+
67
+ /**
68
+ * Parse a composite ID into its components
69
+ *
70
+ * @param compositeId - Composite ID to parse
71
+ * @returns Object with userId and memoryId
72
+ * @throws {InvalidCompositeIdError} If composite ID format is invalid
73
+ *
74
+ * @example
75
+ * parseCompositeId('user123.my-recipe')
76
+ * // Returns: { userId: 'user123', memoryId: 'my-recipe' }
77
+ */
78
+ export function parseCompositeId(compositeId: string): CompositeIdComponents {
79
+ // Split on first dot only (in case memoryId was generated with dashes that look like dots)
80
+ const parts = compositeId.split('.')
81
+
82
+ if (parts.length !== 2) {
83
+ throw new InvalidCompositeIdError(
84
+ `Invalid composite ID format: ${compositeId}. Composite ID must be exactly 2 parts separated by a dot. Expected format: {userId}.{memoryId}`
85
+ )
86
+ }
87
+
88
+ const [userId, memoryId] = parts
89
+
90
+ // Validate components are not empty
91
+ if (!userId.trim()) {
92
+ throw new InvalidCompositeIdError(
93
+ `Invalid composite ID: ${compositeId}. User ID is empty`
94
+ )
95
+ }
96
+
97
+ if (!memoryId.trim()) {
98
+ throw new InvalidCompositeIdError(
99
+ `Invalid composite ID: ${compositeId}. Memory ID is empty`
100
+ )
101
+ }
102
+
103
+ return { userId, memoryId }
104
+ }
105
+
106
+ /**
107
+ * Check if a string is a composite ID
108
+ *
109
+ * @param id - String to check
110
+ * @returns true if valid composite ID, false otherwise
111
+ *
112
+ * @example
113
+ * isCompositeId('user123.my-recipe') // true
114
+ * isCompositeId('simple-id') // false
115
+ * isCompositeId('too.many.dots') // false
116
+ */
117
+ export function isCompositeId(id: string): boolean {
118
+ try {
119
+ parseCompositeId(id)
120
+ return true
121
+ } catch (error) {
122
+ if (error instanceof InvalidCompositeIdError) {
123
+ return false
124
+ }
125
+ throw error
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Validate a composite ID (returns true on valid, throws on invalid)
131
+ *
132
+ * @param id - Composite ID to validate
133
+ * @returns true if valid
134
+ * @throws {InvalidCompositeIdError} If composite ID is invalid
135
+ *
136
+ * @example
137
+ * validateCompositeId('user123.my-recipe') // Returns true
138
+ * validateCompositeId('invalid') // Throws InvalidCompositeIdError
139
+ */
140
+ export function validateCompositeId(id: string): true {
141
+ parseCompositeId(id) // Will throw if invalid
142
+ return true
143
+ }
144
+
145
+ /**
146
+ * Extract user ID from a composite ID
147
+ *
148
+ * @param compositeId - Composite ID
149
+ * @returns User ID component
150
+ *
151
+ * @example
152
+ * getUserIdFromComposite('user123.my-recipe')
153
+ * // Returns: 'user123'
154
+ */
155
+ export function getUserIdFromComposite(compositeId: string): string {
156
+ const { userId } = parseCompositeId(compositeId)
157
+ return userId
158
+ }
159
+
160
+ /**
161
+ * Extract memory ID from a composite ID
162
+ *
163
+ * @param compositeId - Composite ID
164
+ * @returns Memory ID component
165
+ *
166
+ * @example
167
+ * getMemoryIdFromComposite('user123.my-recipe')
168
+ * // Returns: 'my-recipe'
169
+ */
170
+ export function getMemoryIdFromComposite(compositeId: string): string {
171
+ const { memoryId } = parseCompositeId(compositeId)
172
+ return memoryId
173
+ }
174
+
175
+ /**
176
+ * Check if an ID belongs to a specific user
177
+ *
178
+ * @param compositeId - Composite ID to check
179
+ * @param userId - User ID to match
180
+ * @returns true if composite ID belongs to user, false otherwise
181
+ *
182
+ * @example
183
+ * belongsToUser('user123.my-recipe', 'user123') // true
184
+ * belongsToUser('user123.my-recipe', 'user456') // false
185
+ */
186
+ export function belongsToUser(compositeId: string, userId: string): boolean {
187
+ try {
188
+ const { userId: idUserId } = parseCompositeId(compositeId)
189
+ return idUserId === userId
190
+ } catch {
191
+ return false
192
+ }
193
+ }
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Unit tests for Memory Collection Pattern v2 Core Infrastructure
3
+ *
4
+ * Tests core functionality of:
5
+ * - Dot notation collection utilities
6
+ * - Composite ID utilities
7
+ * - Tracking array management
8
+ * - Schema definitions
9
+ */
10
+
11
+ import {
12
+ CollectionType,
13
+ getCollectionName,
14
+ parseCollectionName,
15
+ validateCollectionName,
16
+ isUserCollection,
17
+ isSpacesCollection,
18
+ isGroupCollection,
19
+ } from './dot-notation.js';
20
+
21
+ import {
22
+ generateCompositeId,
23
+ parseCompositeId,
24
+ isCompositeId,
25
+ validateCompositeId,
26
+ } from './composite-ids.js';
27
+
28
+ import {
29
+ addToSpaceIds,
30
+ removeFromSpaceIds,
31
+ addToGroupIds,
32
+ removeFromGroupIds,
33
+ isPublishedToSpace,
34
+ isPublishedToGroup,
35
+ getPublishedLocations,
36
+ isPublished,
37
+ getPublishedCount,
38
+ initializeTracking,
39
+ addMultipleSpaceIds,
40
+ addMultipleGroupIds,
41
+ type MemoryWithTracking,
42
+ } from './tracking-arrays.js';
43
+
44
+ import {
45
+ createUserCollectionSchema,
46
+ createSpaceCollectionSchema,
47
+ createGroupCollectionSchema,
48
+ getUserCollectionProperties,
49
+ getPublishedCollectionProperties,
50
+ validateV2CollectionName,
51
+ getCollectionType,
52
+ extractIdFromCollectionName,
53
+ } from '../schema/v2-collections.js';
54
+
55
+ // ============================================================================
56
+ // Dot Notation Collection Utilities Tests
57
+ // ============================================================================
58
+
59
+ describe('Dot Notation Collection Utilities', () => {
60
+ describe('getCollectionName', () => {
61
+ it('should generate user collection name', () => {
62
+ expect(getCollectionName(CollectionType.USERS, 'user123')).toBe('Memory_users_user123');
63
+ });
64
+
65
+ it('should generate spaces collection name', () => {
66
+ expect(getCollectionName(CollectionType.SPACES)).toBe('Memory_spaces_public');
67
+ });
68
+
69
+ it('should generate group collection name', () => {
70
+ expect(getCollectionName(CollectionType.GROUPS, 'group456')).toBe('Memory_groups_group456');
71
+ });
72
+
73
+ it('should throw error for user collection without ID', () => {
74
+ expect(() => getCollectionName(CollectionType.USERS)).toThrow('User ID is required');
75
+ });
76
+
77
+ it('should throw error for ID with invalid characters', () => {
78
+ expect(() => getCollectionName(CollectionType.USERS, 'user.123')).toThrow();
79
+ });
80
+ });
81
+
82
+ describe('parseCollectionName', () => {
83
+ it('should parse user collection name', () => {
84
+ const result = parseCollectionName('Memory_users_user123');
85
+ expect(result.type).toBe(CollectionType.USERS);
86
+ expect(result.id).toBe('user123');
87
+ });
88
+
89
+ it('should parse spaces collection name', () => {
90
+ const result = parseCollectionName('Memory_spaces_public');
91
+ expect(result.type).toBe(CollectionType.SPACES);
92
+ expect(result.id).toBeUndefined();
93
+ });
94
+
95
+ it('should parse group collection name', () => {
96
+ const result = parseCollectionName('Memory_groups_group456');
97
+ expect(result.type).toBe(CollectionType.GROUPS);
98
+ expect(result.id).toBe('group456');
99
+ });
100
+ });
101
+
102
+ describe('Collection type checkers', () => {
103
+ it('should identify user collections', () => {
104
+ expect(isUserCollection('Memory_users_user123')).toBe(true);
105
+ expect(isUserCollection('Memory_spaces_public')).toBe(false);
106
+ });
107
+
108
+ it('should identify spaces collection', () => {
109
+ expect(isSpacesCollection('Memory_spaces_public')).toBe(true);
110
+ expect(isSpacesCollection('Memory_users_user123')).toBe(false);
111
+ });
112
+
113
+ it('should identify group collections', () => {
114
+ expect(isGroupCollection('Memory_groups_group456')).toBe(true);
115
+ expect(isGroupCollection('Memory_users_user123')).toBe(false);
116
+ });
117
+ });
118
+ });
119
+
120
+ // ============================================================================
121
+ // Composite ID Utilities Tests
122
+ // ============================================================================
123
+
124
+ describe('Composite ID Utilities', () => {
125
+ describe('generateCompositeId', () => {
126
+ it('should generate composite ID', () => {
127
+ expect(generateCompositeId('user123', 'my-recipe')).toBe('user123.my-recipe');
128
+ });
129
+
130
+ it('should throw error for userId with dots', () => {
131
+ expect(() => generateCompositeId('user.123', 'recipe')).toThrow('cannot contain dots');
132
+ });
133
+
134
+ it('should throw error for empty userId', () => {
135
+ expect(() => generateCompositeId('', 'recipe')).toThrow('cannot be empty');
136
+ });
137
+ });
138
+
139
+ describe('parseCompositeId', () => {
140
+ it('should parse composite ID', () => {
141
+ const result = parseCompositeId('user123.my-recipe');
142
+ expect(result.userId).toBe('user123');
143
+ expect(result.memoryId).toBe('my-recipe');
144
+ });
145
+
146
+ it('should throw error for invalid format', () => {
147
+ expect(() => parseCompositeId('invalid')).toThrow('must be exactly 2 parts');
148
+ });
149
+ });
150
+
151
+ describe('isCompositeId', () => {
152
+ it('should identify valid composite IDs', () => {
153
+ expect(isCompositeId('user123.my-recipe')).toBe(true);
154
+ expect(isCompositeId('simple-id')).toBe(false);
155
+ });
156
+ });
157
+
158
+ describe('validateCompositeId', () => {
159
+ it('should validate correct composite IDs', () => {
160
+ expect(validateCompositeId('user123.my-recipe')).toBe(true);
161
+ });
162
+
163
+ it('should throw error for invalid IDs', () => {
164
+ expect(() => validateCompositeId('invalid')).toThrow();
165
+ });
166
+ });
167
+ });
168
+
169
+ // ============================================================================
170
+ // Tracking Array Management Tests
171
+ // ============================================================================
172
+
173
+ describe('Tracking Array Management', () => {
174
+ describe('addToSpaceIds', () => {
175
+ it('should add space ID to existing arrays', () => {
176
+ const memory: MemoryWithTracking = { space_ids: [], group_ids: [] };
177
+ const result = addToSpaceIds(memory, 'cooking');
178
+ expect(result.space_ids).toEqual(['cooking']);
179
+ });
180
+
181
+ it('should not add duplicate space IDs', () => {
182
+ const memory: MemoryWithTracking = { space_ids: ['cooking'], group_ids: [] };
183
+ const result = addToSpaceIds(memory, 'cooking');
184
+ expect(result.space_ids).toEqual(['cooking']);
185
+ });
186
+
187
+ it('should be immutable', () => {
188
+ const memory: MemoryWithTracking = { space_ids: ['cooking'], group_ids: [] };
189
+ const result = addToSpaceIds(memory, 'recipes');
190
+ expect(memory.space_ids).toEqual(['cooking']); // Original unchanged
191
+ expect(result.space_ids).toEqual(['cooking', 'recipes']);
192
+ });
193
+ });
194
+
195
+ describe('removeFromSpaceIds', () => {
196
+ it('should remove space ID', () => {
197
+ const memory: MemoryWithTracking = { space_ids: ['cooking', 'recipes'], group_ids: [] };
198
+ const result = removeFromSpaceIds(memory, 'cooking');
199
+ expect(result.space_ids).toEqual(['recipes']);
200
+ });
201
+ });
202
+
203
+ describe('addToGroupIds', () => {
204
+ it('should add group ID', () => {
205
+ const memory: MemoryWithTracking = { space_ids: [], group_ids: ['family'] };
206
+ const result = addToGroupIds(memory, 'friends');
207
+ expect(result.group_ids).toEqual(['family', 'friends']);
208
+ });
209
+ });
210
+
211
+ describe('removeFromGroupIds', () => {
212
+ it('should remove group ID', () => {
213
+ const memory: MemoryWithTracking = { space_ids: [], group_ids: ['family', 'friends'] };
214
+ const result = removeFromGroupIds(memory, 'family');
215
+ expect(result.group_ids).toEqual(['friends']);
216
+ });
217
+ });
218
+
219
+ describe('Publication checks', () => {
220
+ it('should check if published to space', () => {
221
+ const memory: MemoryWithTracking = { space_ids: ['cooking'], group_ids: [] };
222
+ expect(isPublishedToSpace(memory, 'cooking')).toBe(true);
223
+ expect(isPublishedToSpace(memory, 'travel')).toBe(false);
224
+ });
225
+
226
+ it('should check if published to group', () => {
227
+ const memory: MemoryWithTracking = { space_ids: [], group_ids: ['family'] };
228
+ expect(isPublishedToGroup(memory, 'family')).toBe(true);
229
+ expect(isPublishedToGroup(memory, 'coworkers')).toBe(false);
230
+ });
231
+
232
+ it('should get publication locations', () => {
233
+ const memory: MemoryWithTracking = { space_ids: ['cooking'], group_ids: ['family'] };
234
+ const result = getPublishedLocations(memory);
235
+ expect(result).toEqual({ spaces: ['cooking'], groups: ['family'] });
236
+ });
237
+
238
+ it('should check if published anywhere', () => {
239
+ const memory1: MemoryWithTracking = { space_ids: ['cooking'], group_ids: [] };
240
+ expect(isPublished(memory1)).toBe(true);
241
+
242
+ const memory2: MemoryWithTracking = { space_ids: [], group_ids: [] };
243
+ expect(isPublished(memory2)).toBe(false);
244
+ });
245
+
246
+ it('should count publications', () => {
247
+ const memory: MemoryWithTracking = { space_ids: ['cooking', 'recipes'], group_ids: ['family'] };
248
+ expect(getPublishedCount(memory)).toBe(3);
249
+ });
250
+ });
251
+
252
+ describe('Batch operations', () => {
253
+ it('should add multiple space IDs', () => {
254
+ const memory: MemoryWithTracking = { space_ids: ['cooking'], group_ids: [] };
255
+ const result = addMultipleSpaceIds(memory, ['recipes', 'baking']);
256
+ expect(result.space_ids).toEqual(['cooking', 'recipes', 'baking']);
257
+ });
258
+
259
+ it('should add multiple group IDs', () => {
260
+ const memory: MemoryWithTracking = { space_ids: [], group_ids: ['family'] };
261
+ const result = addMultipleGroupIds(memory, ['friends', 'coworkers']);
262
+ expect(result.group_ids).toEqual(['family', 'friends', 'coworkers']);
263
+ });
264
+ });
265
+
266
+ describe('Utility functions', () => {
267
+ it('should initialize tracking arrays', () => {
268
+ const memory = { content: 'My recipe' };
269
+ const result = initializeTracking(memory);
270
+ expect(result.space_ids).toEqual([]);
271
+ expect(result.group_ids).toEqual([]);
272
+ });
273
+ });
274
+ });
275
+
276
+ // ============================================================================
277
+ // Schema Definitions Tests
278
+ // ============================================================================
279
+
280
+ describe('Schema Definitions', () => {
281
+ describe('Schema creation', () => {
282
+ it('should create user collection schema', () => {
283
+ const schema = createUserCollectionSchema('user123');
284
+ expect(schema.name).toBe('Memory_users_user123');
285
+ expect(schema.description).toContain('user123');
286
+ expect(schema.properties).toBeDefined();
287
+ expect(schema.vectorizers).toBeDefined();
288
+ });
289
+
290
+ it('should create space collection schema', () => {
291
+ const schema = createSpaceCollectionSchema();
292
+ expect(schema.name).toBe('Memory_spaces_public');
293
+ expect(schema.properties).toBeDefined();
294
+ });
295
+
296
+ it('should create group collection schema', () => {
297
+ const schema = createGroupCollectionSchema('group456');
298
+ expect(schema.name).toBe('Memory_groups_group456');
299
+ expect(schema.properties).toBeDefined();
300
+ });
301
+ });
302
+
303
+ describe('Property lists', () => {
304
+ it('should get user collection properties', () => {
305
+ const props = getUserCollectionProperties();
306
+ expect(props).not.toContain('id'); // 'id' is reserved by Weaviate
307
+ expect(props).toContain('content');
308
+ expect(props).toContain('space_ids');
309
+ expect(props).toContain('group_ids');
310
+ });
311
+
312
+ it('should get published collection properties', () => {
313
+ const props = getPublishedCollectionProperties();
314
+ expect(props).not.toContain('id'); // 'id' is reserved by Weaviate
315
+ expect(props).toContain('published_at');
316
+ expect(props).toContain('author_id');
317
+ });
318
+ });
319
+
320
+ describe('Collection name validation', () => {
321
+ it('should validate v2 collection names', () => {
322
+ expect(validateV2CollectionName('Memory_users_user123')).toBe(true);
323
+ expect(validateV2CollectionName('Memory_spaces_public')).toBe(true);
324
+ expect(validateV2CollectionName('Memory_groups_group456')).toBe(true);
325
+ });
326
+
327
+ it('should reject invalid collection names', () => {
328
+ expect(() => validateV2CollectionName('InvalidName')).toThrow('Invalid v2 collection name');
329
+ });
330
+ });
331
+
332
+ describe('Collection type detection', () => {
333
+ it('should detect collection types', () => {
334
+ expect(getCollectionType('Memory_users_user123')).toBe('users');
335
+ expect(getCollectionType('Memory_spaces_public')).toBe('spaces');
336
+ expect(getCollectionType('Memory_groups_group456')).toBe('groups');
337
+ });
338
+ });
339
+
340
+ describe('ID extraction', () => {
341
+ it('should extract user ID', () => {
342
+ expect(extractIdFromCollectionName('Memory_users_user123')).toBe('user123');
343
+ });
344
+
345
+ it('should extract group ID', () => {
346
+ expect(extractIdFromCollectionName('Memory_groups_group456')).toBe('group456');
347
+ });
348
+
349
+ it('should return null for spaces collection', () => {
350
+ expect(extractIdFromCollectionName('Memory_spaces_public')).toBeNull();
351
+ });
352
+ });
353
+ });