@prmichaelsen/remember-mcp 3.0.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 (208) hide show
  1. package/AGENT.md +296 -250
  2. package/CHANGELOG.md +338 -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
@@ -18,6 +18,10 @@ var __esm = (fn, res) => function __init() {
18
18
  var __commonJS = (cb, mod) => function __require2() {
19
19
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
20
20
  };
21
+ var __export = (target, all) => {
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
24
+ };
21
25
  var __copyProps = (to, from, except, desc) => {
22
26
  if (from && typeof from === "object" || typeof from === "function") {
23
27
  for (let key of __getOwnPropNames(from))
@@ -510,6 +514,98 @@ var init_logger = __esm({
510
514
  }
511
515
  });
512
516
 
517
+ // src/firestore/init.ts
518
+ import { initializeApp } from "@prmichaelsen/firebase-admin-sdk-v8";
519
+ import {
520
+ getDocument,
521
+ setDocument,
522
+ addDocument,
523
+ updateDocument,
524
+ deleteDocument,
525
+ queryDocuments,
526
+ batchWrite,
527
+ FieldValue,
528
+ verifyIdToken
529
+ } from "@prmichaelsen/firebase-admin-sdk-v8";
530
+ function initFirestore() {
531
+ if (initialized) {
532
+ return;
533
+ }
534
+ try {
535
+ const serviceAccount = JSON.parse(config.firebase.serviceAccount);
536
+ initializeApp({
537
+ serviceAccount,
538
+ projectId: config.firebase.projectId
539
+ });
540
+ initialized = true;
541
+ logger.info("Firestore initialized successfully", {
542
+ module: "firestore-init"
543
+ });
544
+ } catch (error) {
545
+ logger.error("Firestore initialization failed", {
546
+ module: "firestore-init",
547
+ error: error instanceof Error ? error.message : String(error)
548
+ });
549
+ logger.error("Make sure FIREBASE_ADMIN_SERVICE_ACCOUNT_KEY is valid JSON", {
550
+ module: "firestore-init"
551
+ });
552
+ logger.error("Check for proper escaping in .env file", {
553
+ module: "firestore-init"
554
+ });
555
+ throw error;
556
+ }
557
+ }
558
+ var initialized;
559
+ var init_init = __esm({
560
+ "src/firestore/init.ts"() {
561
+ "use strict";
562
+ init_config();
563
+ init_logger();
564
+ initialized = false;
565
+ }
566
+ });
567
+
568
+ // src/services/trust-enforcement.ts
569
+ function buildTrustFilter(collection, accessorTrustLevel) {
570
+ return collection.filter.byProperty("trust_score").lessThanOrEqual(accessorTrustLevel);
571
+ }
572
+ function isTrustSufficient(memoryTrust, accessorTrust) {
573
+ return accessorTrust >= memoryTrust;
574
+ }
575
+ var init_trust_enforcement = __esm({
576
+ "src/services/trust-enforcement.ts"() {
577
+ "use strict";
578
+ }
579
+ });
580
+
581
+ // src/firestore/paths.ts
582
+ function getBasePrefix() {
583
+ const environment = process.env.ENVIRONMENT;
584
+ if (environment && environment !== "production" && environment !== "prod") {
585
+ return `${environment}.${APP_NAME}`;
586
+ }
587
+ const isDevelopment = process.env.NODE_ENV === "development";
588
+ if (isDevelopment) {
589
+ const customPrefix = process.env.DB_PREFIX;
590
+ if (customPrefix) {
591
+ return customPrefix;
592
+ }
593
+ return `e0.${APP_NAME}`;
594
+ }
595
+ return APP_NAME;
596
+ }
597
+ function getUserPreferencesPath(userId) {
598
+ return `${BASE}.users/${userId}/preferences`;
599
+ }
600
+ var APP_NAME, BASE;
601
+ var init_paths = __esm({
602
+ "src/firestore/paths.ts"() {
603
+ "use strict";
604
+ APP_NAME = "remember-mcp";
605
+ BASE = getBasePrefix();
606
+ }
607
+ });
608
+
513
609
  // src/types/space-memory.ts
514
610
  var SUPPORTED_SPACES;
515
611
  var init_space_memory = __esm({
@@ -519,6 +615,325 @@ var init_space_memory = __esm({
519
615
  }
520
616
  });
521
617
 
618
+ // src/types/ghost-config.ts
619
+ var DEFAULT_GHOST_CONFIG;
620
+ var init_ghost_config = __esm({
621
+ "src/types/ghost-config.ts"() {
622
+ "use strict";
623
+ DEFAULT_GHOST_CONFIG = {
624
+ enabled: false,
625
+ public_ghost_enabled: false,
626
+ default_friend_trust: 0.25,
627
+ default_public_trust: 0,
628
+ per_user_trust: {},
629
+ blocked_users: [],
630
+ enforcement_mode: "query"
631
+ };
632
+ }
633
+ });
634
+
635
+ // src/services/ghost-config.service.ts
636
+ var ghost_config_service_exports = {};
637
+ __export(ghost_config_service_exports, {
638
+ FirestoreGhostConfigProvider: () => FirestoreGhostConfigProvider,
639
+ blockUser: () => blockUser,
640
+ getGhostConfig: () => getGhostConfig,
641
+ isGhostEnabled: () => isGhostEnabled,
642
+ removeUserTrust: () => removeUserTrust,
643
+ setGhostConfigFields: () => setGhostConfigFields,
644
+ setUserTrust: () => setUserTrust,
645
+ unblockUser: () => unblockUser,
646
+ validateGhostConfigUpdate: () => validateGhostConfigUpdate
647
+ });
648
+ function getGhostConfigPath(ownerUserId) {
649
+ return {
650
+ collectionPath: `${BASE}.users/${ownerUserId}/ghost_config`,
651
+ docId: "settings"
652
+ };
653
+ }
654
+ async function getGhostConfig(ownerUserId) {
655
+ try {
656
+ const { collectionPath, docId } = getGhostConfigPath(ownerUserId);
657
+ const doc = await getDocument(collectionPath, docId);
658
+ if (!doc) {
659
+ return { ...DEFAULT_GHOST_CONFIG };
660
+ }
661
+ return { ...DEFAULT_GHOST_CONFIG, ...doc };
662
+ } catch (error) {
663
+ logger.error("Failed to get ghost config", {
664
+ service: SERVICE,
665
+ ownerUserId,
666
+ error: error instanceof Error ? error.message : String(error)
667
+ });
668
+ return { ...DEFAULT_GHOST_CONFIG };
669
+ }
670
+ }
671
+ async function setGhostConfigFields(ownerUserId, config2) {
672
+ const { collectionPath, docId } = getGhostConfigPath(ownerUserId);
673
+ await setDocument(collectionPath, docId, config2, { merge: true });
674
+ logger.info("Ghost config updated", {
675
+ service: SERVICE,
676
+ ownerUserId,
677
+ updatedKeys: Object.keys(config2)
678
+ });
679
+ return getGhostConfig(ownerUserId);
680
+ }
681
+ async function setUserTrust(ownerUserId, targetUserId, trustLevel) {
682
+ if (trustLevel < 0 || trustLevel > 1) {
683
+ throw new Error(`Trust level must be between 0 and 1, got ${trustLevel}`);
684
+ }
685
+ const current = await getGhostConfig(ownerUserId);
686
+ const per_user_trust = { ...current.per_user_trust, [targetUserId]: trustLevel };
687
+ const { collectionPath, docId } = getGhostConfigPath(ownerUserId);
688
+ await setDocument(collectionPath, docId, { per_user_trust }, { merge: true });
689
+ logger.info("User trust level set", {
690
+ service: SERVICE,
691
+ ownerUserId,
692
+ targetUserId,
693
+ trustLevel
694
+ });
695
+ }
696
+ async function removeUserTrust(ownerUserId, targetUserId) {
697
+ const current = await getGhostConfig(ownerUserId);
698
+ const per_user_trust = { ...current.per_user_trust };
699
+ delete per_user_trust[targetUserId];
700
+ const { collectionPath, docId } = getGhostConfigPath(ownerUserId);
701
+ await setDocument(collectionPath, docId, { per_user_trust }, { merge: true });
702
+ logger.info("User trust override removed", {
703
+ service: SERVICE,
704
+ ownerUserId,
705
+ targetUserId
706
+ });
707
+ }
708
+ async function blockUser(ownerUserId, targetUserId) {
709
+ const current = await getGhostConfig(ownerUserId);
710
+ if (current.blocked_users.includes(targetUserId)) {
711
+ return;
712
+ }
713
+ const blocked_users = [...current.blocked_users, targetUserId];
714
+ const { collectionPath, docId } = getGhostConfigPath(ownerUserId);
715
+ await setDocument(collectionPath, docId, { blocked_users }, { merge: true });
716
+ logger.info("User blocked from ghost access", {
717
+ service: SERVICE,
718
+ ownerUserId,
719
+ targetUserId
720
+ });
721
+ }
722
+ async function unblockUser(ownerUserId, targetUserId) {
723
+ const current = await getGhostConfig(ownerUserId);
724
+ if (!current.blocked_users.includes(targetUserId)) {
725
+ return;
726
+ }
727
+ const blocked_users = current.blocked_users.filter((id) => id !== targetUserId);
728
+ const { collectionPath, docId } = getGhostConfigPath(ownerUserId);
729
+ await setDocument(collectionPath, docId, { blocked_users }, { merge: true });
730
+ logger.info("User unblocked from ghost access", {
731
+ service: SERVICE,
732
+ ownerUserId,
733
+ targetUserId
734
+ });
735
+ }
736
+ async function isGhostEnabled(ownerUserId) {
737
+ const config2 = await getGhostConfig(ownerUserId);
738
+ return config2.enabled;
739
+ }
740
+ function validateGhostConfigUpdate(config2) {
741
+ if (config2.default_friend_trust !== void 0) {
742
+ if (config2.default_friend_trust < 0 || config2.default_friend_trust > 1) {
743
+ throw new Error(`default_friend_trust must be between 0 and 1, got ${config2.default_friend_trust}`);
744
+ }
745
+ }
746
+ if (config2.default_public_trust !== void 0) {
747
+ if (config2.default_public_trust < 0 || config2.default_public_trust > 1) {
748
+ throw new Error(`default_public_trust must be between 0 and 1, got ${config2.default_public_trust}`);
749
+ }
750
+ }
751
+ if (config2.enforcement_mode !== void 0) {
752
+ const valid = ["query", "prompt", "hybrid"];
753
+ if (!valid.includes(config2.enforcement_mode)) {
754
+ throw new Error(`enforcement_mode must be one of ${valid.join(", ")}, got ${config2.enforcement_mode}`);
755
+ }
756
+ }
757
+ if (config2.per_user_trust !== void 0) {
758
+ for (const [userId, level] of Object.entries(config2.per_user_trust)) {
759
+ if (level < 0 || level > 1) {
760
+ throw new Error(`Trust level for ${userId} must be between 0 and 1, got ${level}`);
761
+ }
762
+ }
763
+ }
764
+ }
765
+ var SERVICE, FirestoreGhostConfigProvider;
766
+ var init_ghost_config_service = __esm({
767
+ "src/services/ghost-config.service.ts"() {
768
+ "use strict";
769
+ init_init();
770
+ init_paths();
771
+ init_logger();
772
+ init_ghost_config();
773
+ SERVICE = "GhostConfigService";
774
+ FirestoreGhostConfigProvider = class {
775
+ async getGhostConfig(ownerUserId) {
776
+ const config2 = await getGhostConfig(ownerUserId);
777
+ if (!config2.enabled) {
778
+ return null;
779
+ }
780
+ return config2;
781
+ }
782
+ };
783
+ }
784
+ });
785
+
786
+ // src/services/access-control.ts
787
+ var access_control_exports = {};
788
+ __export(access_control_exports, {
789
+ InMemoryEscalationStore: () => InMemoryEscalationStore,
790
+ StubGhostConfigProvider: () => StubGhostConfigProvider,
791
+ checkMemoryAccess: () => checkMemoryAccess,
792
+ formatAccessResultMessage: () => formatAccessResultMessage,
793
+ handleInsufficientTrust: () => handleInsufficientTrust,
794
+ isMemoryBlocked: () => isMemoryBlocked,
795
+ resetBlock: () => resetBlock,
796
+ resolveAccessorTrustLevel: () => resolveAccessorTrustLevel
797
+ });
798
+ async function checkMemoryAccess(accessorUserId, memory, ghostConfigProvider, escalationStore) {
799
+ const ownerUserId = memory.user_id;
800
+ const memoryId = memory.id;
801
+ if (accessorUserId === ownerUserId) {
802
+ return { status: "granted", memory, access_level: "owner" };
803
+ }
804
+ const ghostConfig = await ghostConfigProvider.getGhostConfig(ownerUserId);
805
+ if (!ghostConfig || !ghostConfig.enabled) {
806
+ return { status: "no_permission", owner_user_id: ownerUserId, accessor_user_id: accessorUserId };
807
+ }
808
+ if (ghostConfig.blocked_users.includes(accessorUserId)) {
809
+ return { status: "no_permission", owner_user_id: ownerUserId, accessor_user_id: accessorUserId };
810
+ }
811
+ const block = await escalationStore.getBlock(ownerUserId, accessorUserId, memoryId);
812
+ if (block) {
813
+ return {
814
+ status: "blocked",
815
+ memory_id: memoryId,
816
+ reason: block.reason,
817
+ blocked_at: block.blocked_at
818
+ };
819
+ }
820
+ const accessorTrust = resolveAccessorTrustLevel(ghostConfig, accessorUserId);
821
+ const memoryTrust = memory.trust;
822
+ if (!isTrustSufficient(memoryTrust, accessorTrust)) {
823
+ const result = await handleInsufficientTrust(
824
+ ownerUserId,
825
+ accessorUserId,
826
+ memoryId,
827
+ memoryTrust,
828
+ accessorTrust,
829
+ escalationStore
830
+ );
831
+ return result;
832
+ }
833
+ return { status: "granted", memory, access_level: "trusted" };
834
+ }
835
+ async function handleInsufficientTrust(ownerUserId, accessorUserId, memoryId, requiredTrust, actualTrust, escalationStore) {
836
+ const attempt = await escalationStore.incrementAttempts(ownerUserId, accessorUserId, memoryId);
837
+ if (attempt.count >= MAX_ATTEMPTS_BEFORE_BLOCK) {
838
+ const block = {
839
+ blocked_at: (/* @__PURE__ */ new Date()).toISOString(),
840
+ reason: `Access blocked after ${attempt.count} unauthorized attempts`,
841
+ attempt_count: attempt.count
842
+ };
843
+ await escalationStore.setBlock(ownerUserId, accessorUserId, memoryId, block);
844
+ return {
845
+ status: "blocked",
846
+ memory_id: memoryId,
847
+ reason: block.reason,
848
+ blocked_at: block.blocked_at
849
+ };
850
+ }
851
+ return {
852
+ status: "insufficient_trust",
853
+ memory_id: memoryId,
854
+ required_trust: requiredTrust,
855
+ actual_trust: Math.max(0, actualTrust - TRUST_PENALTY),
856
+ attempts_remaining: MAX_ATTEMPTS_BEFORE_BLOCK - attempt.count
857
+ };
858
+ }
859
+ async function isMemoryBlocked(ownerUserId, accessorUserId, memoryId, escalationStore) {
860
+ const block = await escalationStore.getBlock(ownerUserId, accessorUserId, memoryId);
861
+ return block !== null;
862
+ }
863
+ async function resetBlock(ownerUserId, accessorUserId, memoryId, escalationStore) {
864
+ await escalationStore.removeBlock(ownerUserId, accessorUserId, memoryId);
865
+ }
866
+ function resolveAccessorTrustLevel(ghostConfig, accessorUserId) {
867
+ if (accessorUserId in ghostConfig.per_user_trust) {
868
+ return ghostConfig.per_user_trust[accessorUserId];
869
+ }
870
+ return ghostConfig.default_public_trust ?? 0;
871
+ }
872
+ function formatAccessResultMessage(result) {
873
+ switch (result.status) {
874
+ case "granted":
875
+ return result.access_level === "owner" ? "Access granted (owner)." : "Access granted (trusted).";
876
+ case "insufficient_trust":
877
+ return `Insufficient trust level. Required: ${result.required_trust.toFixed(2)}, actual: ${result.actual_trust.toFixed(2)}. ${result.attempts_remaining} attempt(s) remaining before access is blocked.`;
878
+ case "blocked":
879
+ return `Access blocked: ${result.reason}`;
880
+ case "no_permission":
881
+ return "No permission to access this user's memories.";
882
+ case "not_found":
883
+ return `Memory ${result.memory_id} not found.`;
884
+ case "deleted":
885
+ return `Memory ${result.memory_id} was deleted on ${result.deleted_at}.`;
886
+ }
887
+ }
888
+ var StubGhostConfigProvider, InMemoryEscalationStore, TRUST_PENALTY, MAX_ATTEMPTS_BEFORE_BLOCK;
889
+ var init_access_control = __esm({
890
+ "src/services/access-control.ts"() {
891
+ "use strict";
892
+ init_trust_enforcement();
893
+ StubGhostConfigProvider = class {
894
+ configs = /* @__PURE__ */ new Map();
895
+ async getGhostConfig(ownerUserId) {
896
+ return this.configs.get(ownerUserId) ?? null;
897
+ }
898
+ /** Test helper: set a GhostConfig for a user */
899
+ setGhostConfig(ownerUserId, config2) {
900
+ this.configs.set(ownerUserId, config2);
901
+ }
902
+ };
903
+ InMemoryEscalationStore = class {
904
+ blocks = /* @__PURE__ */ new Map();
905
+ attempts = /* @__PURE__ */ new Map();
906
+ key(ownerUserId, accessorUserId, memoryId) {
907
+ return `${ownerUserId}:${accessorUserId}:${memoryId}`;
908
+ }
909
+ async getBlock(ownerUserId, accessorUserId, memoryId) {
910
+ return this.blocks.get(this.key(ownerUserId, accessorUserId, memoryId)) ?? null;
911
+ }
912
+ async setBlock(ownerUserId, accessorUserId, memoryId, block) {
913
+ this.blocks.set(this.key(ownerUserId, accessorUserId, memoryId), block);
914
+ }
915
+ async removeBlock(ownerUserId, accessorUserId, memoryId) {
916
+ this.blocks.delete(this.key(ownerUserId, accessorUserId, memoryId));
917
+ }
918
+ async getAttempts(ownerUserId, accessorUserId, memoryId) {
919
+ return this.attempts.get(this.key(ownerUserId, accessorUserId, memoryId)) ?? null;
920
+ }
921
+ async incrementAttempts(ownerUserId, accessorUserId, memoryId) {
922
+ const k = this.key(ownerUserId, accessorUserId, memoryId);
923
+ const existing = this.attempts.get(k);
924
+ const record = {
925
+ count: (existing?.count ?? 0) + 1,
926
+ last_attempt_at: (/* @__PURE__ */ new Date()).toISOString()
927
+ };
928
+ this.attempts.set(k, record);
929
+ return record;
930
+ }
931
+ };
932
+ TRUST_PENALTY = 0.1;
933
+ MAX_ATTEMPTS_BEFORE_BLOCK = 3;
934
+ }
935
+ });
936
+
522
937
  // src/server-factory.ts
523
938
  init_logger();
524
939
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -676,15 +1091,8 @@ function getWeaviateClient() {
676
1091
  }
677
1092
  return client;
678
1093
  }
679
- function sanitizeUserId(userId) {
680
- let sanitized = userId.replace(/[^a-zA-Z0-9]/g, "_");
681
- if (/^[0-9]/.test(sanitized)) {
682
- sanitized = "_" + sanitized;
683
- }
684
- return sanitized.charAt(0).toUpperCase() + sanitized.slice(1);
685
- }
686
1094
  function getMemoryCollectionName(userId) {
687
- return `Memory_${sanitizeUserId(userId)}`;
1095
+ return `Memory_users_${userId}`;
688
1096
  }
689
1097
  var ALL_MEMORY_PROPERTIES = [
690
1098
  // Core identity
@@ -692,16 +1100,28 @@ var ALL_MEMORY_PROPERTIES = [
692
1100
  "doc_type",
693
1101
  // Memory fields
694
1102
  "content",
1103
+ "content_type",
1104
+ // v2 canonical
695
1105
  "title",
696
1106
  "summary",
697
1107
  "type",
1108
+ // v1 compat (v2: content_type)
698
1109
  // Scoring fields
699
1110
  "weight",
700
1111
  "base_weight",
1112
+ "trust_score",
1113
+ // v2 canonical
701
1114
  "trust",
1115
+ // v1 compat (v2: trust_score)
702
1116
  "confidence",
703
1117
  "computed_weight",
704
- // Location fields (flattened)
1118
+ // Location fields (v2)
1119
+ "location_name",
1120
+ "location_lat",
1121
+ // v2 canonical
1122
+ "location_lon",
1123
+ // v2 canonical
1124
+ // Location fields (v1 compat)
705
1125
  "location_gps_lat",
706
1126
  "location_gps_lng",
707
1127
  "location_address",
@@ -711,12 +1131,24 @@ var ALL_MEMORY_PROPERTIES = [
711
1131
  // Locale fields
712
1132
  "locale_language",
713
1133
  "locale_timezone",
714
- // Context fields (flattened)
1134
+ // Context fields
715
1135
  "context_conversation_id",
716
1136
  "context_summary",
717
1137
  "context_timestamp",
718
- // Relationships
1138
+ "context_app",
1139
+ "context_url",
1140
+ // Relationships (v2)
1141
+ "relationship_ids",
1142
+ // v2 canonical
1143
+ "related_memory_ids",
1144
+ // v2 canonical
1145
+ // Relationships (v1 compat)
719
1146
  "relationships",
1147
+ "memory_ids",
1148
+ // Common relationship fields
1149
+ "relationship_type",
1150
+ "observation",
1151
+ "strength",
720
1152
  // Access tracking
721
1153
  "access_count",
722
1154
  "last_accessed_at",
@@ -727,24 +1159,29 @@ var ALL_MEMORY_PROPERTIES = [
727
1159
  "updated_at",
728
1160
  "version",
729
1161
  "template_id",
730
- // Relationship-specific fields
731
- "memory_ids",
732
- "relationship_type",
733
- "observation",
734
- "strength",
1162
+ // Tracking arrays (v2)
1163
+ "space_ids",
1164
+ "group_ids",
735
1165
  // Comment/threading fields
736
1166
  "parent_id",
737
1167
  "thread_root_id",
738
1168
  "moderation_flags",
739
- // Space/publishing fields (for Memory_public collection)
1169
+ // Space/publishing fields
740
1170
  "spaces",
1171
+ // legacy
741
1172
  "space_id",
1173
+ // legacy
742
1174
  "author_id",
743
1175
  "ghost_id",
744
1176
  "attribution",
745
1177
  "published_at",
746
1178
  "discovery_count",
747
1179
  "space_memory_id",
1180
+ // legacy
1181
+ "original_memory_id",
1182
+ "revised_at",
1183
+ "revision_count",
1184
+ "revision_history",
748
1185
  // Soft delete fields
749
1186
  "deleted_at",
750
1187
  "deleted_by",
@@ -788,57 +1225,177 @@ async function fetchMemoryWithAllProperties(collection, memoryId) {
788
1225
  }
789
1226
  }
790
1227
 
791
- // src/firestore/init.ts
792
- init_config();
793
- init_logger();
794
- import { initializeApp } from "@prmichaelsen/firebase-admin-sdk-v8";
795
- import {
796
- getDocument,
797
- setDocument,
798
- addDocument,
799
- updateDocument,
800
- deleteDocument,
801
- queryDocuments,
802
- batchWrite,
803
- FieldValue,
804
- verifyIdToken
805
- } from "@prmichaelsen/firebase-admin-sdk-v8";
806
- var initialized = false;
807
- function initFirestore() {
808
- if (initialized) {
809
- return;
810
- }
811
- try {
812
- const serviceAccount = JSON.parse(config.firebase.serviceAccount);
813
- initializeApp({
814
- serviceAccount,
815
- projectId: config.firebase.projectId
816
- });
817
- initialized = true;
818
- logger.info("Firestore initialized successfully", {
819
- module: "firestore-init"
820
- });
821
- } catch (error) {
822
- logger.error("Firestore initialization failed", {
823
- module: "firestore-init",
824
- error: error instanceof Error ? error.message : String(error)
825
- });
826
- logger.error("Make sure FIREBASE_ADMIN_SERVICE_ACCOUNT_KEY is valid JSON", {
827
- module: "firestore-init"
828
- });
829
- logger.error("Check for proper escaping in .env file", {
830
- module: "firestore-init"
831
- });
832
- throw error;
1228
+ // src/server-factory.ts
1229
+ init_init();
1230
+
1231
+ // src/services/credentials-provider.ts
1232
+ var StubCredentialsProvider = class {
1233
+ async getCredentials(_accessToken, userId) {
1234
+ return {
1235
+ user_id: userId,
1236
+ group_memberships: []
1237
+ };
833
1238
  }
1239
+ };
1240
+ function createCredentialsProvider() {
1241
+ return new StubCredentialsProvider();
834
1242
  }
1243
+ var credentialsProvider = createCredentialsProvider();
835
1244
 
836
1245
  // src/weaviate/schema.ts
837
- import weaviate2 from "weaviate-client";
838
1246
  init_logger();
1247
+
1248
+ // src/schema/v2-collections.ts
1249
+ import { configure } from "weaviate-client";
1250
+ var COMMON_MEMORY_PROPERTIES = [
1251
+ // Core content
1252
+ { name: "content", dataType: configure.dataType.TEXT },
1253
+ { name: "content_type", dataType: configure.dataType.TEXT },
1254
+ { name: "title", dataType: configure.dataType.TEXT },
1255
+ { name: "summary", dataType: configure.dataType.TEXT },
1256
+ { name: "type", dataType: configure.dataType.TEXT },
1257
+ // v1 compat (v2: content_type)
1258
+ // Tracking arrays (v2 feature)
1259
+ { name: "space_ids", dataType: configure.dataType.TEXT_ARRAY },
1260
+ { name: "group_ids", dataType: configure.dataType.TEXT_ARRAY },
1261
+ // Metadata
1262
+ { name: "created_at", dataType: configure.dataType.DATE },
1263
+ { name: "updated_at", dataType: configure.dataType.DATE },
1264
+ { name: "version", dataType: configure.dataType.INT },
1265
+ // User context
1266
+ { name: "user_id", dataType: configure.dataType.TEXT },
1267
+ // Document type (memory, relationship, comment)
1268
+ { name: "doc_type", dataType: configure.dataType.TEXT },
1269
+ // Memory-specific fields
1270
+ { name: "tags", dataType: configure.dataType.TEXT_ARRAY },
1271
+ { name: "weight", dataType: configure.dataType.NUMBER },
1272
+ { name: "trust_score", dataType: configure.dataType.NUMBER },
1273
+ { name: "trust", dataType: configure.dataType.NUMBER },
1274
+ // v1 compat (v2: trust_score)
1275
+ { name: "base_weight", dataType: configure.dataType.NUMBER },
1276
+ { name: "computed_weight", dataType: configure.dataType.NUMBER },
1277
+ { name: "confidence", dataType: configure.dataType.NUMBER },
1278
+ { name: "strength", dataType: configure.dataType.NUMBER },
1279
+ // Location data (v2 names)
1280
+ { name: "location_name", dataType: configure.dataType.TEXT },
1281
+ { name: "location_lat", dataType: configure.dataType.NUMBER },
1282
+ { name: "location_lon", dataType: configure.dataType.NUMBER },
1283
+ // Location data (v1 compat)
1284
+ { name: "location_gps_lat", dataType: configure.dataType.NUMBER },
1285
+ { name: "location_gps_lng", dataType: configure.dataType.NUMBER },
1286
+ { name: "location_address", dataType: configure.dataType.TEXT },
1287
+ { name: "location_city", dataType: configure.dataType.TEXT },
1288
+ { name: "location_country", dataType: configure.dataType.TEXT },
1289
+ { name: "location_source", dataType: configure.dataType.TEXT },
1290
+ // Locale
1291
+ { name: "locale_language", dataType: configure.dataType.TEXT },
1292
+ { name: "locale_timezone", dataType: configure.dataType.TEXT },
1293
+ // Context
1294
+ { name: "context_app", dataType: configure.dataType.TEXT },
1295
+ { name: "context_url", dataType: configure.dataType.TEXT },
1296
+ { name: "context_conversation_id", dataType: configure.dataType.TEXT },
1297
+ { name: "context_summary", dataType: configure.dataType.TEXT },
1298
+ { name: "context_timestamp", dataType: configure.dataType.DATE },
1299
+ // Relationships (v2 names)
1300
+ { name: "relationship_ids", dataType: configure.dataType.TEXT_ARRAY },
1301
+ { name: "relationship_type", dataType: configure.dataType.TEXT },
1302
+ { name: "related_memory_ids", dataType: configure.dataType.TEXT_ARRAY },
1303
+ { name: "observation", dataType: configure.dataType.TEXT },
1304
+ // Relationships (v1 compat)
1305
+ { name: "relationships", dataType: configure.dataType.TEXT_ARRAY },
1306
+ { name: "memory_ids", dataType: configure.dataType.TEXT_ARRAY },
1307
+ // Access tracking
1308
+ { name: "access_count", dataType: configure.dataType.NUMBER },
1309
+ { name: "last_accessed_at", dataType: configure.dataType.DATE },
1310
+ // References & templates
1311
+ { name: "references", dataType: configure.dataType.TEXT_ARRAY },
1312
+ { name: "template_id", dataType: configure.dataType.TEXT },
1313
+ // Comments (Phase 1)
1314
+ { name: "parent_id", dataType: configure.dataType.TEXT },
1315
+ { name: "thread_root_id", dataType: configure.dataType.TEXT },
1316
+ { name: "moderation_flags", dataType: configure.dataType.TEXT_ARRAY },
1317
+ // Soft delete
1318
+ { name: "deleted_at", dataType: configure.dataType.DATE },
1319
+ { name: "deleted_by", dataType: configure.dataType.TEXT },
1320
+ { name: "deletion_reason", dataType: configure.dataType.TEXT }
1321
+ ];
1322
+ var PUBLISHED_MEMORY_PROPERTIES = [
1323
+ // Publication metadata
1324
+ { name: "published_at", dataType: configure.dataType.DATE },
1325
+ { name: "revised_at", dataType: configure.dataType.DATE },
1326
+ // Attribution
1327
+ { name: "author_id", dataType: configure.dataType.TEXT },
1328
+ { name: "ghost_id", dataType: configure.dataType.TEXT },
1329
+ { name: "attribution", dataType: configure.dataType.TEXT },
1330
+ // Discovery
1331
+ { name: "discovery_count", dataType: configure.dataType.INT },
1332
+ // Revision tracking
1333
+ { name: "revision_count", dataType: configure.dataType.INT },
1334
+ { name: "original_memory_id", dataType: configure.dataType.TEXT },
1335
+ // Moderation (nullable — null defaults to approved)
1336
+ { name: "moderation_status", dataType: configure.dataType.TEXT },
1337
+ { name: "moderated_by", dataType: configure.dataType.TEXT },
1338
+ { name: "moderated_at", dataType: configure.dataType.DATE },
1339
+ // Memory-level ACL (nullable — null defaults to owner_only semantics)
1340
+ { name: "write_mode", dataType: configure.dataType.TEXT },
1341
+ { name: "owner_id", dataType: configure.dataType.TEXT },
1342
+ { name: "overwrite_allowed_ids", dataType: configure.dataType.TEXT_ARRAY },
1343
+ { name: "last_revised_by", dataType: configure.dataType.TEXT },
1344
+ // Legacy compatibility (deprecated but kept for migration)
1345
+ { name: "spaces", dataType: configure.dataType.TEXT_ARRAY },
1346
+ { name: "space_id", dataType: configure.dataType.TEXT },
1347
+ { name: "space_memory_id", dataType: configure.dataType.TEXT }
1348
+ ];
1349
+ function createUserCollectionSchema(userId) {
1350
+ const collectionName = `Memory_users_${userId}`;
1351
+ return {
1352
+ name: collectionName,
1353
+ description: `Private memory collection for user: ${userId}`,
1354
+ // Vector configuration
1355
+ vectorizers: configure.vectorizer.text2VecOpenAI({
1356
+ model: "text-embedding-3-small",
1357
+ dimensions: 1536,
1358
+ vectorizeCollectionName: false
1359
+ }),
1360
+ // Properties
1361
+ properties: COMMON_MEMORY_PROPERTIES,
1362
+ // Inverted index configuration
1363
+ invertedIndex: configure.invertedIndex({
1364
+ indexNullState: true,
1365
+ indexPropertyLength: true,
1366
+ indexTimestamps: true
1367
+ })
1368
+ };
1369
+ }
1370
+ function createSpaceCollectionSchema() {
1371
+ const collectionName = "Memory_spaces_public";
1372
+ return {
1373
+ name: collectionName,
1374
+ description: "Shared memory collection for all public spaces",
1375
+ // Vector configuration
1376
+ vectorizers: configure.vectorizer.text2VecOpenAI({
1377
+ model: "text-embedding-3-small",
1378
+ dimensions: 1536,
1379
+ vectorizeCollectionName: false
1380
+ }),
1381
+ // Properties (common + published)
1382
+ properties: [
1383
+ ...COMMON_MEMORY_PROPERTIES,
1384
+ ...PUBLISHED_MEMORY_PROPERTIES
1385
+ ],
1386
+ // Inverted index configuration
1387
+ invertedIndex: configure.invertedIndex({
1388
+ indexNullState: true,
1389
+ indexPropertyLength: true,
1390
+ indexTimestamps: true
1391
+ })
1392
+ };
1393
+ }
1394
+
1395
+ // src/weaviate/schema.ts
839
1396
  async function createMemoryCollection(userId) {
840
1397
  const client2 = getWeaviateClient();
841
- const collectionName = `Memory_${sanitizeUserId(userId)}`;
1398
+ const collectionName = `Memory_users_${userId}`;
842
1399
  const exists = await client2.collections.exists(collectionName);
843
1400
  if (exists) {
844
1401
  logger.debug("Collection already exists", {
@@ -851,238 +1408,8 @@ async function createMemoryCollection(userId) {
851
1408
  module: "weaviate-schema",
852
1409
  collectionName
853
1410
  });
854
- await client2.collections.create({
855
- name: collectionName,
856
- // Vectorizer configuration
857
- vectorizers: weaviate2.configure.vectorizer.text2VecOpenAI({
858
- model: "text-embedding-3-small",
859
- // Vectorize content, title, summary, and observation for semantic search
860
- // Note: title and summary are optional fields
861
- sourceProperties: ["content", "title", "summary", "observation"]
862
- }),
863
- properties: [
864
- // Discriminator
865
- {
866
- name: "doc_type",
867
- dataType: "text",
868
- description: 'Document type: "memory" or "relationship"'
869
- },
870
- // Core identity
871
- {
872
- name: "user_id",
873
- dataType: "text",
874
- description: "User who owns this document"
875
- },
876
- // Memory fields
877
- {
878
- name: "content",
879
- dataType: "text",
880
- description: "Main memory content (vectorized)"
881
- },
882
- {
883
- name: "title",
884
- dataType: "text",
885
- description: "Optional short title"
886
- },
887
- {
888
- name: "summary",
889
- dataType: "text",
890
- description: "Optional brief summary"
891
- },
892
- {
893
- name: "type",
894
- dataType: "text",
895
- description: "Content type (note, event, person, etc.)"
896
- },
897
- // Scoring fields
898
- {
899
- name: "weight",
900
- dataType: "number",
901
- description: "Significance/priority (0-1)"
902
- },
903
- {
904
- name: "trust",
905
- dataType: "number",
906
- description: "Access control level (0-1)"
907
- },
908
- {
909
- name: "confidence",
910
- dataType: "number",
911
- description: "System confidence in accuracy (0-1)"
912
- },
913
- // Location fields (flattened for Weaviate)
914
- {
915
- name: "location_gps_lat",
916
- dataType: "number",
917
- description: "GPS latitude"
918
- },
919
- {
920
- name: "location_gps_lng",
921
- dataType: "number",
922
- description: "GPS longitude"
923
- },
924
- {
925
- name: "location_address",
926
- dataType: "text",
927
- description: "Formatted address"
928
- },
929
- {
930
- name: "location_city",
931
- dataType: "text",
932
- description: "City name"
933
- },
934
- {
935
- name: "location_country",
936
- dataType: "text",
937
- description: "Country name"
938
- },
939
- {
940
- name: "location_source",
941
- dataType: "text",
942
- description: "Location source (gps, ip, manual, etc.)"
943
- },
944
- // Locale fields
945
- {
946
- name: "locale_language",
947
- dataType: "text",
948
- description: "Language code (e.g., en, es, fr)"
949
- },
950
- {
951
- name: "locale_timezone",
952
- dataType: "text",
953
- description: "Timezone (e.g., America/Los_Angeles)"
954
- },
955
- // Context fields
956
- {
957
- name: "context_conversation_id",
958
- dataType: "text",
959
- description: "Conversation ID"
960
- },
961
- {
962
- name: "context_summary",
963
- dataType: "text",
964
- description: "Brief context summary"
965
- },
966
- {
967
- name: "context_timestamp",
968
- dataType: "date",
969
- description: "Context timestamp"
970
- },
971
- // Relationships
972
- {
973
- name: "relationships",
974
- dataType: "text[]",
975
- description: "Array of relationship IDs"
976
- },
977
- // Access tracking
978
- {
979
- name: "access_count",
980
- dataType: "number",
981
- description: "Total times accessed"
982
- },
983
- {
984
- name: "last_accessed_at",
985
- dataType: "date",
986
- description: "Most recent access timestamp"
987
- },
988
- // Metadata
989
- {
990
- name: "tags",
991
- dataType: "text[]",
992
- description: "Tags for organization"
993
- },
994
- {
995
- name: "references",
996
- dataType: "text[]",
997
- description: "Source URLs"
998
- },
999
- {
1000
- name: "created_at",
1001
- dataType: "date",
1002
- description: "Creation timestamp"
1003
- },
1004
- {
1005
- name: "updated_at",
1006
- dataType: "date",
1007
- description: "Last update timestamp"
1008
- },
1009
- {
1010
- name: "version",
1011
- dataType: "number",
1012
- description: "Version number"
1013
- },
1014
- // Template fields
1015
- {
1016
- name: "template_id",
1017
- dataType: "text",
1018
- description: "Template ID if using template"
1019
- },
1020
- // Relationship-specific fields
1021
- {
1022
- name: "memory_ids",
1023
- dataType: "text[]",
1024
- description: "Connected memory IDs (for relationships)"
1025
- },
1026
- {
1027
- name: "relationship_type",
1028
- dataType: "text",
1029
- description: "Relationship type (for relationships)"
1030
- },
1031
- {
1032
- name: "observation",
1033
- dataType: "text",
1034
- description: "Relationship observation (vectorized)"
1035
- },
1036
- {
1037
- name: "strength",
1038
- dataType: "number",
1039
- description: "Relationship strength (0-1)"
1040
- },
1041
- // Computed fields
1042
- {
1043
- name: "base_weight",
1044
- dataType: "number",
1045
- description: "User-specified weight"
1046
- },
1047
- {
1048
- name: "computed_weight",
1049
- dataType: "number",
1050
- description: "Calculated effective weight"
1051
- },
1052
- // Comment/threading fields (for threaded discussions in shared spaces)
1053
- {
1054
- name: "parent_id",
1055
- dataType: "text",
1056
- description: "ID of parent memory or comment (for threading)"
1057
- },
1058
- {
1059
- name: "thread_root_id",
1060
- dataType: "text",
1061
- description: "Root memory ID for fetching entire thread"
1062
- },
1063
- {
1064
- name: "moderation_flags",
1065
- dataType: "text[]",
1066
- description: 'Per-space moderation flags (format: "{space_id}:{flag_type}")'
1067
- },
1068
- // Soft delete fields
1069
- {
1070
- name: "deleted_at",
1071
- dataType: "date",
1072
- description: "Timestamp when memory was soft-deleted (null = not deleted)"
1073
- },
1074
- {
1075
- name: "deleted_by",
1076
- dataType: "text",
1077
- description: "User ID who deleted the memory"
1078
- },
1079
- {
1080
- name: "deletion_reason",
1081
- dataType: "text",
1082
- description: "Optional reason for deletion"
1083
- }
1084
- ]
1085
- });
1411
+ const schema = createUserCollectionSchema(userId);
1412
+ await client2.collections.create(schema);
1086
1413
  logger.info("Memory collection created successfully", {
1087
1414
  module: "weaviate-schema",
1088
1415
  collectionName
@@ -1090,7 +1417,7 @@ async function createMemoryCollection(userId) {
1090
1417
  }
1091
1418
  async function ensureMemoryCollection(userId) {
1092
1419
  const client2 = getWeaviateClient();
1093
- const collectionName = `Memory_${sanitizeUserId(userId)}`;
1420
+ const collectionName = `Memory_users_${userId}`;
1094
1421
  const exists = await client2.collections.exists(collectionName);
1095
1422
  if (!exists) {
1096
1423
  await createMemoryCollection(userId);
@@ -1098,7 +1425,7 @@ async function ensureMemoryCollection(userId) {
1098
1425
  }
1099
1426
  function getMemoryCollection(userId) {
1100
1427
  const client2 = getWeaviateClient();
1101
- const collectionName = `Memory_${sanitizeUserId(userId)}`;
1428
+ const collectionName = `Memory_users_${userId}`;
1102
1429
  return client2.collections.get(collectionName);
1103
1430
  }
1104
1431
 
@@ -1180,7 +1507,10 @@ var CONTENT_TYPES = [
1180
1507
  "system",
1181
1508
  "action",
1182
1509
  "audit",
1183
- "history"
1510
+ "history",
1511
+ // Cross-user & Threading
1512
+ "ghost",
1513
+ "comment"
1184
1514
  ];
1185
1515
  var CONTENT_TYPE_METADATA = {
1186
1516
  // Core Types
@@ -1474,6 +1804,21 @@ var CONTENT_TYPE_METADATA = {
1474
1804
  description: "Change history and version tracking",
1475
1805
  examples: ["Edit history", "Version history", "Change logs"],
1476
1806
  common_fields: ["target_id", "change_type", "previous_value", "new_value"]
1807
+ },
1808
+ // Cross-user & Threading
1809
+ ghost: {
1810
+ name: "ghost",
1811
+ category: "cross_user",
1812
+ description: "Ghost conversation memory \u2014 stores context from AI-mediated cross-user interactions",
1813
+ examples: ["Ghost conversation context", "Cross-user interaction history"],
1814
+ common_fields: ["ghost_owner_id", "conversing_user_id"]
1815
+ },
1816
+ comment: {
1817
+ name: "comment",
1818
+ category: "cross_user",
1819
+ description: "Threaded comments on shared memories in spaces and groups",
1820
+ examples: ["Comments on shared memories", "Discussion replies", "Feedback"],
1821
+ common_fields: ["parent_id", "thread_root_id"]
1477
1822
  }
1478
1823
  };
1479
1824
  function isValidContentType(type) {
@@ -1560,7 +1905,7 @@ var createMemoryTool = {
1560
1905
  },
1561
1906
  trust: {
1562
1907
  type: "number",
1563
- description: "Access control level (0-1, default: 0.5)",
1908
+ description: "Access control level (0-1, default: 0.25)",
1564
1909
  minimum: 0,
1565
1910
  maximum: 1
1566
1911
  },
@@ -1601,8 +1946,11 @@ var createMemoryTool = {
1601
1946
  required: ["content"]
1602
1947
  }
1603
1948
  };
1604
- async function handleCreateMemory(args, userId, context) {
1949
+ async function handleCreateMemory(args, userId, authContext, context) {
1950
+ const debug = createDebugLogger({ tool: "remember_create_memory", userId, operation: "create memory" });
1605
1951
  try {
1952
+ debug.info("Tool invoked");
1953
+ debug.trace("Arguments", { args });
1606
1954
  logger.info("Creating memory", { userId, type: args.type });
1607
1955
  await ensureMemoryCollection(userId);
1608
1956
  const collection = getMemoryCollection(userId);
@@ -1616,36 +1964,19 @@ async function handleCreateMemory(args, userId, context) {
1616
1964
  title: args.title,
1617
1965
  summary: args.title,
1618
1966
  // Use title as summary for now
1619
- type: args.type && isValidContentType(args.type) ? args.type : DEFAULT_CONTENT_TYPE,
1967
+ content_type: args.type && isValidContentType(args.type) ? args.type : DEFAULT_CONTENT_TYPE,
1620
1968
  // Scoring
1621
1969
  weight: args.weight ?? 0.5,
1622
- trust: args.trust ?? 0.5,
1970
+ trust_score: args.trust ?? 0.25,
1623
1971
  confidence: 1,
1624
- // Location (from context or default)
1625
- location: {
1626
- gps: null,
1627
- address: null,
1628
- source: "unavailable",
1629
- confidence: 0,
1630
- is_approximate: true
1631
- },
1632
1972
  // Context
1633
- context: {
1634
- timestamp: now,
1635
- source: {
1636
- type: "api",
1637
- platform: "mcp"
1638
- },
1639
- summary: context?.summary || "Memory created via MCP",
1640
- conversation_id: context?.conversation_id,
1641
- ...context
1642
- },
1973
+ context_summary: context?.summary || "Memory created via MCP",
1974
+ context_conversation_id: context?.conversation_id,
1643
1975
  // Relationships
1644
- relationships: [],
1976
+ relationship_ids: [],
1645
1977
  // Access tracking
1646
1978
  access_count: 0,
1647
1979
  last_accessed_at: now,
1648
- access_frequency: 0,
1649
1980
  // Metadata
1650
1981
  created_at: now,
1651
1982
  updated_at: now,
@@ -1654,14 +1985,17 @@ async function handleCreateMemory(args, userId, context) {
1654
1985
  references: args.references || [],
1655
1986
  // Template
1656
1987
  template_id: args.template_id,
1657
- structured_content: args.structured_content,
1658
1988
  // Computed weight
1659
1989
  base_weight: args.weight ?? 0.5,
1660
1990
  computed_weight: args.weight ?? 0.5,
1661
1991
  // Comment/threading fields (initialize to defaults)
1662
1992
  parent_id: args.parent_id ?? null,
1663
1993
  thread_root_id: args.thread_root_id ?? null,
1664
- moderation_flags: args.moderation_flags ?? []
1994
+ moderation_flags: args.moderation_flags ?? [],
1995
+ // Publication tracking arrays (Memory Collection Pattern v2)
1996
+ // Managed by remember_publish / remember_retract — always start empty
1997
+ space_ids: [],
1998
+ group_ids: []
1665
1999
  };
1666
2000
  const result = await collection.data.insert({
1667
2001
  properties: memory
@@ -1674,6 +2008,7 @@ async function handleCreateMemory(args, userId, context) {
1674
2008
  };
1675
2009
  return JSON.stringify(response, null, 2);
1676
2010
  } catch (error) {
2011
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
1677
2012
  handleToolError(error, {
1678
2013
  toolName: "remember_create_memory",
1679
2014
  operation: "create memory",
@@ -1709,11 +2044,11 @@ function buildDocTypeFilters(collection, docType, filters) {
1709
2044
  if (docType === "memory" && filters?.types && filters.types.length > 0) {
1710
2045
  if (filters.types.length === 1) {
1711
2046
  filterList.push(
1712
- collection.filter.byProperty("type").equal(filters.types[0])
2047
+ collection.filter.byProperty("content_type").equal(filters.types[0])
1713
2048
  );
1714
2049
  } else {
1715
2050
  filterList.push(
1716
- collection.filter.byProperty("type").containsAny(filters.types)
2051
+ collection.filter.byProperty("content_type").containsAny(filters.types)
1717
2052
  );
1718
2053
  }
1719
2054
  }
@@ -1729,12 +2064,12 @@ function buildDocTypeFilters(collection, docType, filters) {
1729
2064
  }
1730
2065
  if (filters?.trust_min !== void 0) {
1731
2066
  filterList.push(
1732
- collection.filter.byProperty("trust").greaterThanOrEqual(filters.trust_min)
2067
+ collection.filter.byProperty("trust_score").greaterThanOrEqual(filters.trust_min)
1733
2068
  );
1734
2069
  }
1735
2070
  if (filters?.trust_max !== void 0) {
1736
2071
  filterList.push(
1737
- collection.filter.byProperty("trust").lessThanOrEqual(filters.trust_max)
2072
+ collection.filter.byProperty("trust_score").lessThanOrEqual(filters.trust_max)
1738
2073
  );
1739
2074
  }
1740
2075
  if (filters?.date_from) {
@@ -1793,6 +2128,7 @@ function buildDeletedFilter(collection, deletedFilter = "exclude") {
1793
2128
  }
1794
2129
 
1795
2130
  // src/tools/search-memory.ts
2131
+ init_trust_enforcement();
1796
2132
  var searchMemoryTool = {
1797
2133
  name: "remember_search_memory",
1798
2134
  description: `Search memories AND relationships using hybrid semantic and keyword search.
@@ -1902,24 +2238,33 @@ var searchMemoryTool = {
1902
2238
  required: ["query"]
1903
2239
  }
1904
2240
  };
1905
- async function handleSearchMemory(args, userId) {
2241
+ async function handleSearchMemory(args, userId, authContext) {
2242
+ const ghostMode = authContext?.ghostMode;
2243
+ const searchUserId = ghostMode?.owner_user_id ?? userId;
2244
+ const debug = createDebugLogger({ tool: "remember_search_memory", userId: searchUserId, operation: ghostMode ? "ghost search" : "search memory" });
1906
2245
  try {
2246
+ debug.info("Tool invoked");
2247
+ debug.trace("Arguments", { args, ghostMode: !!ghostMode });
1907
2248
  if (!args.query || args.query.trim() === "") {
1908
2249
  throw new Error("Query cannot be empty");
1909
2250
  }
1910
2251
  const includeRelationships = args.include_relationships !== false;
1911
2252
  logger.info("Searching memories and relationships", {
1912
- userId,
2253
+ userId: searchUserId,
1913
2254
  query: args.query,
1914
- includeRelationships
2255
+ includeRelationships,
2256
+ ghostMode: !!ghostMode
1915
2257
  });
1916
- const collection = getMemoryCollection(userId);
2258
+ const collection = getMemoryCollection(searchUserId);
1917
2259
  const alpha = args.alpha ?? 0.7;
1918
2260
  const limit = args.limit ?? 10;
1919
2261
  const offset = args.offset ?? 0;
1920
2262
  const deletedFilter = buildDeletedFilter(collection, args.deleted_filter || "exclude");
2263
+ const trustFilter = ghostMode ? buildTrustFilter(collection, ghostMode.accessor_trust_level) : null;
1921
2264
  const searchFilters = includeRelationships ? buildCombinedSearchFilters(collection, args.filters) : buildMemoryOnlyFilters(collection, args.filters);
1922
- const combinedFilters = combineFiltersWithAnd([deletedFilter, searchFilters].filter((f) => f !== null));
2265
+ const hasExplicitTypeFilter = args.filters?.types && args.filters.types.length > 0;
2266
+ const ghostExclusionFilter = !hasExplicitTypeFilter ? collection.filter.byProperty("content_type").notEqual("ghost") : null;
2267
+ const combinedFilters = combineFiltersWithAnd([deletedFilter, trustFilter, ghostExclusionFilter, searchFilters].filter((f) => f !== null));
1923
2268
  const searchOptions = {
1924
2269
  alpha,
1925
2270
  limit: limit + offset
@@ -1965,6 +2310,7 @@ async function handleSearchMemory(args, userId) {
1965
2310
  });
1966
2311
  return JSON.stringify(searchResult, null, 2);
1967
2312
  } catch (error) {
2313
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
1968
2314
  handleToolError(error, {
1969
2315
  toolName: "remember_search_memory",
1970
2316
  operation: "search memories",
@@ -1979,8 +2325,9 @@ async function handleSearchMemory(args, userId) {
1979
2325
  import { Filters as Filters2 } from "weaviate-client";
1980
2326
 
1981
2327
  // src/services/confirmation-token.service.ts
1982
- import { randomUUID } from "crypto";
2328
+ init_init();
1983
2329
  init_logger();
2330
+ import { randomUUID } from "crypto";
1984
2331
  var ConfirmationTokenService = class {
1985
2332
  EXPIRY_MINUTES = 5;
1986
2333
  /**
@@ -2241,8 +2588,11 @@ Examples:
2241
2588
  required: ["memory_id"]
2242
2589
  }
2243
2590
  };
2244
- async function handleDeleteMemory(args, userId) {
2591
+ async function handleDeleteMemory(args, userId, authContext) {
2592
+ const debug = createDebugLogger({ tool: "remember_delete_memory", userId, operation: "delete memory" });
2245
2593
  try {
2594
+ debug.info("Tool invoked");
2595
+ debug.trace("Arguments", { args });
2246
2596
  logger.info("Requesting memory deletion", {
2247
2597
  userId,
2248
2598
  memoryId: args.memory_id,
@@ -2250,7 +2600,7 @@ async function handleDeleteMemory(args, userId) {
2250
2600
  });
2251
2601
  const { memory_id, reason } = args;
2252
2602
  const client2 = getWeaviateClient();
2253
- const collectionName = `Memory_${sanitizeUserId(userId)}`;
2603
+ const collectionName = getMemoryCollectionName(userId);
2254
2604
  const collection = client2.collections.get(collectionName);
2255
2605
  const memory = await fetchMemoryWithAllProperties(collection, memory_id);
2256
2606
  if (!memory) {
@@ -2268,7 +2618,7 @@ async function handleDeleteMemory(args, userId) {
2268
2618
  const relationshipsResult = await collection.query.fetchObjects({
2269
2619
  filters: Filters2.and(
2270
2620
  collection.filter.byProperty("doc_type").equal("relationship"),
2271
- collection.filter.byProperty("memory_ids").containsAny([memory_id])
2621
+ collection.filter.byProperty("related_memory_ids").containsAny([memory_id])
2272
2622
  ),
2273
2623
  limit: 100
2274
2624
  });
@@ -2302,7 +2652,7 @@ async function handleDeleteMemory(args, userId) {
2302
2652
  preview: {
2303
2653
  memory_id,
2304
2654
  content: memory.properties.content?.substring(0, 200) + (memory.properties.content?.length > 200 ? "..." : ""),
2305
- type: memory.properties.type,
2655
+ content_type: memory.properties.content_type,
2306
2656
  relationships_count: orphanedRelationships.length,
2307
2657
  will_orphan: orphanedRelationships
2308
2658
  },
@@ -2312,6 +2662,7 @@ async function handleDeleteMemory(args, userId) {
2312
2662
  2
2313
2663
  );
2314
2664
  } catch (error) {
2665
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
2315
2666
  handleToolError(error, {
2316
2667
  toolName: "remember_delete_memory",
2317
2668
  userId,
@@ -2398,8 +2749,11 @@ var updateMemoryTool = {
2398
2749
  required: ["memory_id"]
2399
2750
  }
2400
2751
  };
2401
- async function handleUpdateMemory(args, userId) {
2752
+ async function handleUpdateMemory(args, userId, authContext) {
2753
+ const debug = createDebugLogger({ tool: "remember_update_memory", userId, operation: "update memory" });
2402
2754
  try {
2755
+ debug.info("Tool invoked");
2756
+ debug.trace("Arguments", { args });
2403
2757
  logger.info("Updating memory", { userId, memoryId: args.memory_id });
2404
2758
  const collection = getMemoryCollection(userId);
2405
2759
  let existingMemory;
@@ -2443,8 +2797,8 @@ async function handleUpdateMemory(args, userId) {
2443
2797
  if (!isValidContentType(args.type)) {
2444
2798
  throw new Error(`Invalid content type: ${args.type}`);
2445
2799
  }
2446
- updates.type = args.type;
2447
- updatedFields.push("type");
2800
+ updates.content_type = args.type;
2801
+ updatedFields.push("content_type");
2448
2802
  }
2449
2803
  if (args.weight !== void 0) {
2450
2804
  if (args.weight < 0 || args.weight > 1) {
@@ -2459,8 +2813,8 @@ async function handleUpdateMemory(args, userId) {
2459
2813
  if (args.trust < 0 || args.trust > 1) {
2460
2814
  throw new Error("Trust must be between 0 and 1");
2461
2815
  }
2462
- updates.trust = args.trust;
2463
- updatedFields.push("trust");
2816
+ updates.trust_score = args.trust;
2817
+ updatedFields.push("trust_score");
2464
2818
  }
2465
2819
  if (args.tags !== void 0) {
2466
2820
  updates.tags = args.tags;
@@ -2540,6 +2894,7 @@ async function handleUpdateMemory(args, userId) {
2540
2894
  };
2541
2895
  return JSON.stringify(result, null, 2);
2542
2896
  } catch (error) {
2897
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
2543
2898
  handleToolError(error, {
2544
2899
  toolName: "remember_update_memory",
2545
2900
  operation: "update memory",
@@ -2605,8 +2960,11 @@ var findSimilarTool = {
2605
2960
  }
2606
2961
  }
2607
2962
  };
2608
- async function handleFindSimilar(args, userId) {
2963
+ async function handleFindSimilar(args, userId, authContext) {
2964
+ const debug = createDebugLogger({ tool: "remember_find_similar", userId, operation: "find similar" });
2609
2965
  try {
2966
+ debug.info("Tool invoked");
2967
+ debug.trace("Arguments", { args });
2610
2968
  logger.info("Finding similar memories", { userId, memoryId: args.memory_id, hasText: !!args.text });
2611
2969
  if (!args.memory_id && !args.text) {
2612
2970
  throw new Error("Either memory_id or text must be provided");
@@ -2618,6 +2976,8 @@ async function handleFindSimilar(args, userId) {
2618
2976
  const limit = args.limit ?? 10;
2619
2977
  const minSimilarity = args.min_similarity ?? 0.7;
2620
2978
  const deletedFilter = buildDeletedFilter(collection, args.deleted_filter || "exclude");
2979
+ const ghostExclusionFilter = collection.filter.byProperty("content_type").notEqual("ghost");
2980
+ const baseFilter = combineFiltersWithAnd([deletedFilter, ghostExclusionFilter].filter((f) => f !== null));
2621
2981
  let results;
2622
2982
  if (args.memory_id) {
2623
2983
  const memory = await collection.query.fetchObjectById(args.memory_id, {
@@ -2639,8 +2999,8 @@ async function handleFindSimilar(args, userId) {
2639
2999
  // Convert similarity to distance
2640
3000
  returnMetadata: ["distance"]
2641
3001
  };
2642
- if (deletedFilter) {
2643
- searchOptions.filters = deletedFilter;
3002
+ if (baseFilter) {
3003
+ searchOptions.filters = baseFilter;
2644
3004
  }
2645
3005
  results = await collection.query.nearObject(args.memory_id, searchOptions);
2646
3006
  results.objects = results.objects.filter((obj) => obj.uuid !== args.memory_id);
@@ -2650,8 +3010,8 @@ async function handleFindSimilar(args, userId) {
2650
3010
  distance: 1 - minSimilarity,
2651
3011
  returnMetadata: ["distance"]
2652
3012
  };
2653
- if (deletedFilter) {
2654
- searchOptions.filters = deletedFilter;
3013
+ if (baseFilter) {
3014
+ searchOptions.filters = baseFilter;
2655
3015
  }
2656
3016
  results = await collection.query.nearText(args.text, searchOptions);
2657
3017
  }
@@ -2687,6 +3047,7 @@ async function handleFindSimilar(args, userId) {
2687
3047
  };
2688
3048
  return JSON.stringify(result, null, 2);
2689
3049
  } catch (error) {
3050
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
2690
3051
  handleToolError(error, {
2691
3052
  toolName: "remember_find_similar",
2692
3053
  operation: "find similar memories",
@@ -2700,6 +3061,7 @@ async function handleFindSimilar(args, userId) {
2700
3061
 
2701
3062
  // src/tools/query-memory.ts
2702
3063
  init_logger();
3064
+ init_trust_enforcement();
2703
3065
  var queryMemoryTool = {
2704
3066
  name: "remember_query_memory",
2705
3067
  description: `Query memories using natural language for RAG (Retrieval-Augmented Generation).
@@ -2811,20 +3173,28 @@ var queryMemoryTool = {
2811
3173
  required: ["query"]
2812
3174
  }
2813
3175
  };
2814
- async function handleQueryMemory(args, userId) {
3176
+ async function handleQueryMemory(args, userId, authContext) {
3177
+ const ghostMode = authContext?.ghostMode;
3178
+ const searchUserId = ghostMode?.owner_user_id ?? userId;
3179
+ const debug = createDebugLogger({ tool: "remember_query_memory", userId: searchUserId, operation: ghostMode ? "ghost query" : "query memory" });
2815
3180
  try {
3181
+ debug.info("Tool invoked");
3182
+ debug.trace("Arguments", { args, ghostMode: !!ghostMode });
2816
3183
  if (!args.query || args.query.trim() === "") {
2817
3184
  throw new Error("Query cannot be empty");
2818
3185
  }
2819
- logger.info("Querying memories", { userId, query: args.query });
2820
- const collection = getMemoryCollection(userId);
3186
+ logger.info("Querying memories", { userId: searchUserId, query: args.query, ghostMode: !!ghostMode });
3187
+ const collection = getMemoryCollection(searchUserId);
2821
3188
  const limit = args.limit ?? 5;
2822
3189
  const minRelevance = args.min_relevance ?? 0.6;
2823
3190
  const includeContext = args.include_context ?? true;
2824
3191
  const format = args.format ?? "detailed";
2825
3192
  const deletedFilter = buildDeletedFilter(collection, args.deleted_filter || "exclude");
3193
+ const trustFilter = ghostMode ? buildTrustFilter(collection, ghostMode.accessor_trust_level) : null;
2826
3194
  const searchFilters = buildCombinedSearchFilters(collection, args.filters);
2827
- const combinedFilters = combineFiltersWithAnd([deletedFilter, searchFilters].filter((f) => f !== null));
3195
+ const hasExplicitTypeFilter = args.filters?.types && args.filters.types.length > 0;
3196
+ const ghostExclusionFilter = !hasExplicitTypeFilter ? collection.filter.byProperty("content_type").notEqual("ghost") : null;
3197
+ const combinedFilters = combineFiltersWithAnd([deletedFilter, trustFilter, ghostExclusionFilter, searchFilters].filter((f) => f !== null));
2828
3198
  const searchOptions = {
2829
3199
  limit,
2830
3200
  distance: 1 - minRelevance,
@@ -2860,7 +3230,7 @@ async function handleQueryMemory(args, userId) {
2860
3230
  if (format === "compact") {
2861
3231
  const summaryParts = relevantMemories.map((mem, idx) => {
2862
3232
  const title = mem.title ? `"${mem.title}"` : `Memory ${idx + 1}`;
2863
- const type = mem.type ? ` [${mem.type}]` : "";
3233
+ const type = mem.content_type ? ` [${mem.content_type}]` : "";
2864
3234
  const relevancePercent = Math.round((mem.relevance ?? 0) * 100);
2865
3235
  const content = mem.content || "(no content)";
2866
3236
  const tags = mem.tags && mem.tags.length > 0 ? `
@@ -2883,6 +3253,7 @@ ${content}${tags}`;
2883
3253
  };
2884
3254
  return JSON.stringify(result, null, 2);
2885
3255
  } catch (error) {
3256
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
2886
3257
  handleToolError(error, {
2887
3258
  toolName: "remember_query_memory",
2888
3259
  operation: "query memories",
@@ -2946,8 +3317,11 @@ var createRelationshipTool = {
2946
3317
  required: ["memory_ids", "relationship_type", "observation"]
2947
3318
  }
2948
3319
  };
2949
- async function handleCreateRelationship(args, userId, context) {
3320
+ async function handleCreateRelationship(args, userId, authContext, context) {
3321
+ const debug = createDebugLogger({ tool: "remember_create_relationship", userId, operation: "create relationship" });
2950
3322
  try {
3323
+ debug.info("Tool invoked");
3324
+ debug.trace("Arguments", { args });
2951
3325
  logger.info("Creating relationship", {
2952
3326
  userId,
2953
3327
  type: args.relationship_type,
@@ -2963,7 +3337,7 @@ async function handleCreateRelationship(args, userId, context) {
2963
3337
  args.memory_ids.map(async (memoryId) => {
2964
3338
  try {
2965
3339
  const memory = await collection.query.fetchObjectById(memoryId, {
2966
- returnProperties: ["user_id", "doc_type", "relationships", "deleted_at"]
3340
+ returnProperties: ["user_id", "doc_type", "relationship_ids", "deleted_at"]
2967
3341
  });
2968
3342
  if (!memory) {
2969
3343
  logger.warn("Memory not found", { userId, memoryId });
@@ -2997,7 +3371,7 @@ async function handleCreateRelationship(args, userId, context) {
2997
3371
  return {
2998
3372
  memoryId,
2999
3373
  memory,
3000
- relationships: memory.properties.relationships || []
3374
+ relationships: memory.properties.relationship_ids || []
3001
3375
  };
3002
3376
  } catch (error) {
3003
3377
  const errorMsg = error instanceof Error ? error.message : String(error);
@@ -3030,7 +3404,7 @@ async function handleCreateRelationship(args, userId, context) {
3030
3404
  user_id: userId,
3031
3405
  doc_type: "relationship",
3032
3406
  // Connection
3033
- memory_ids: args.memory_ids,
3407
+ related_memory_ids: args.memory_ids,
3034
3408
  relationship_type: args.relationship_type,
3035
3409
  // Observation
3036
3410
  observation: args.observation,
@@ -3067,7 +3441,7 @@ async function handleCreateRelationship(args, userId, context) {
3067
3441
  await collection.data.update({
3068
3442
  id: check.memoryId,
3069
3443
  properties: {
3070
- relationships: updatedRelationships,
3444
+ relationship_ids: updatedRelationships,
3071
3445
  updated_at: now
3072
3446
  }
3073
3447
  });
@@ -3096,6 +3470,7 @@ async function handleCreateRelationship(args, userId, context) {
3096
3470
  };
3097
3471
  return JSON.stringify(response, null, 2);
3098
3472
  } catch (error) {
3473
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
3099
3474
  handleToolError(error, {
3100
3475
  toolName: "remember_create_relationship",
3101
3476
  operation: "create relationship",
@@ -3160,8 +3535,11 @@ var updateRelationshipTool = {
3160
3535
  required: ["relationship_id"]
3161
3536
  }
3162
3537
  };
3163
- async function handleUpdateRelationship(args, userId) {
3538
+ async function handleUpdateRelationship(args, userId, authContext) {
3539
+ const debug = createDebugLogger({ tool: "remember_update_relationship", userId, operation: "update relationship" });
3164
3540
  try {
3541
+ debug.info("Tool invoked");
3542
+ debug.trace("Arguments", { args });
3165
3543
  logger.info("Updating relationship", { userId, relationshipId: args.relationship_id });
3166
3544
  const collection = getMemoryCollection(userId);
3167
3545
  const existingRelationship = await collection.query.fetchObjectById(args.relationship_id, {
@@ -3229,6 +3607,7 @@ async function handleUpdateRelationship(args, userId) {
3229
3607
  };
3230
3608
  return JSON.stringify(result, null, 2);
3231
3609
  } catch (error) {
3610
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
3232
3611
  handleToolError(error, {
3233
3612
  toolName: "remember_update_relationship",
3234
3613
  operation: "update relationship",
@@ -3305,8 +3684,11 @@ var searchRelationshipTool = {
3305
3684
  required: ["query"]
3306
3685
  }
3307
3686
  };
3308
- async function handleSearchRelationship(args, userId) {
3687
+ async function handleSearchRelationship(args, userId, authContext) {
3688
+ const debug = createDebugLogger({ tool: "remember_search_relationship", userId, operation: "search relationship" });
3309
3689
  try {
3690
+ debug.info("Tool invoked");
3691
+ debug.trace("Arguments", { args });
3310
3692
  logger.info("Searching relationships", {
3311
3693
  userId,
3312
3694
  query: args.query,
@@ -3366,7 +3748,7 @@ async function handleSearchRelationship(args, userId) {
3366
3748
  id: obj.uuid,
3367
3749
  user_id: obj.properties.user_id,
3368
3750
  doc_type: "relationship",
3369
- memory_ids: obj.properties.memory_ids || [],
3751
+ memory_ids: obj.properties.related_memory_ids || [],
3370
3752
  relationship_type: obj.properties.relationship_type,
3371
3753
  observation: obj.properties.observation,
3372
3754
  strength: obj.properties.strength,
@@ -3394,6 +3776,7 @@ async function handleSearchRelationship(args, userId) {
3394
3776
  };
3395
3777
  return JSON.stringify(result, null, 2);
3396
3778
  } catch (error) {
3779
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
3397
3780
  handleToolError(error, {
3398
3781
  toolName: "remember_search_relationship",
3399
3782
  operation: "search relationships",
@@ -3428,12 +3811,15 @@ var deleteRelationshipTool = {
3428
3811
  required: ["relationship_id"]
3429
3812
  }
3430
3813
  };
3431
- async function handleDeleteRelationship(args, userId) {
3814
+ async function handleDeleteRelationship(args, userId, authContext) {
3815
+ const debug = createDebugLogger({ tool: "remember_delete_relationship", userId, operation: "delete relationship" });
3432
3816
  try {
3817
+ debug.info("Tool invoked");
3818
+ debug.trace("Arguments", { args });
3433
3819
  logger.info("Deleting relationship", { userId, relationshipId: args.relationship_id });
3434
3820
  const collection = getMemoryCollection(userId);
3435
3821
  const relationship = await collection.query.fetchObjectById(args.relationship_id, {
3436
- returnProperties: ["user_id", "doc_type", "memory_ids", "relationship_type"]
3822
+ returnProperties: ["user_id", "doc_type", "related_memory_ids", "relationship_type"]
3437
3823
  });
3438
3824
  if (!relationship) {
3439
3825
  throw new Error(`Relationship not found: ${args.relationship_id}`);
@@ -3444,7 +3830,7 @@ async function handleDeleteRelationship(args, userId) {
3444
3830
  if (relationship.properties.doc_type !== "relationship") {
3445
3831
  throw new Error("Cannot delete memories using this tool. Use remember_delete_memory instead.");
3446
3832
  }
3447
- const memoryIds = relationship.properties.memory_ids || [];
3833
+ const memoryIds = relationship.properties.related_memory_ids || [];
3448
3834
  let memoriesUpdated = 0;
3449
3835
  if (memoryIds.length > 0) {
3450
3836
  logger.info("Cleaning up relationship references from connected memories", {
@@ -3454,13 +3840,13 @@ async function handleDeleteRelationship(args, userId) {
3454
3840
  const updatePromises = memoryIds.map(async (memoryId) => {
3455
3841
  try {
3456
3842
  const memory = await collection.query.fetchObjectById(memoryId, {
3457
- returnProperties: ["relationships", "doc_type"]
3843
+ returnProperties: ["relationship_ids", "doc_type"]
3458
3844
  });
3459
3845
  if (!memory || memory.properties.doc_type !== "memory") {
3460
3846
  logger.warn(`Memory ${memoryId} not found or not a memory, skipping cleanup`);
3461
3847
  return { memoryId, success: false };
3462
3848
  }
3463
- const currentRelationships = memory.properties.relationships || [];
3849
+ const currentRelationships = memory.properties.relationship_ids || [];
3464
3850
  const updatedRelationships = currentRelationships.filter(
3465
3851
  (relId) => relId !== args.relationship_id
3466
3852
  );
@@ -3468,7 +3854,7 @@ async function handleDeleteRelationship(args, userId) {
3468
3854
  await collection.data.update({
3469
3855
  id: memoryId,
3470
3856
  properties: {
3471
- relationships: updatedRelationships,
3857
+ relationship_ids: updatedRelationships,
3472
3858
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
3473
3859
  }
3474
3860
  });
@@ -3502,6 +3888,7 @@ async function handleDeleteRelationship(args, userId) {
3502
3888
  };
3503
3889
  return JSON.stringify(result, null, 2);
3504
3890
  } catch (error) {
3891
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
3505
3892
  handleToolError(error, {
3506
3893
  toolName: "remember_delete_relationship",
3507
3894
  operation: "delete relationship",
@@ -3511,29 +3898,9 @@ async function handleDeleteRelationship(args, userId) {
3511
3898
  }
3512
3899
  }
3513
3900
 
3514
- // src/firestore/paths.ts
3515
- var APP_NAME = "remember-mcp";
3516
- function getBasePrefix() {
3517
- const environment = process.env.ENVIRONMENT;
3518
- if (environment && environment !== "production" && environment !== "prod") {
3519
- return `${environment}.${APP_NAME}`;
3520
- }
3521
- const isDevelopment = process.env.NODE_ENV === "development";
3522
- if (isDevelopment) {
3523
- const customPrefix = process.env.DB_PREFIX;
3524
- if (customPrefix) {
3525
- return customPrefix;
3526
- }
3527
- return `e0.${APP_NAME}`;
3528
- }
3529
- return APP_NAME;
3530
- }
3531
- var BASE = getBasePrefix();
3532
- function getUserPreferencesPath(userId) {
3533
- return `${BASE}.users/${userId}/preferences`;
3534
- }
3535
-
3536
3901
  // src/services/preferences-database.service.ts
3902
+ init_init();
3903
+ init_paths();
3537
3904
  init_logger();
3538
3905
 
3539
3906
  // src/types/preferences.ts
@@ -3561,7 +3928,7 @@ var DEFAULT_PREFERENCES = {
3561
3928
  share_with_memories: true
3562
3929
  },
3563
3930
  privacy: {
3564
- default_trust_level: 0.5,
3931
+ default_trust_level: 0.25,
3565
3932
  allow_cross_user_access: false,
3566
3933
  auto_approve_requests: false,
3567
3934
  audit_logging: true
@@ -3611,7 +3978,7 @@ var PREFERENCE_DESCRIPTIONS = {
3611
3978
  share_with_memories: "Include location data in memories (default: true)"
3612
3979
  },
3613
3980
  privacy: {
3614
- default_trust_level: "Default trust level for new memories, 0-1 (default: 0.5)",
3981
+ default_trust_level: "Default trust level for new memories, 0-1 (default: 0.25)",
3615
3982
  allow_cross_user_access: "Allow other users to request access to memories (default: false)",
3616
3983
  auto_approve_requests: "Automatically approve access requests (default: false)",
3617
3984
  audit_logging: "Enable audit logging for preference changes (default: true)"
@@ -3864,8 +4231,11 @@ function formatPreferenceChangeMessage(updates) {
3864
4231
  }
3865
4232
  return `Preferences updated: ${changes.join(", ")}`;
3866
4233
  }
3867
- async function handleSetPreference(args, userId) {
4234
+ async function handleSetPreference(args, userId, authContext) {
4235
+ const debug = createDebugLogger({ tool: "remember_set_preference", userId, operation: "set preference" });
3868
4236
  try {
4237
+ debug.info("Tool invoked");
4238
+ debug.trace("Arguments", { args });
3869
4239
  const { preferences } = args;
3870
4240
  logger.info("Setting preferences", { userId, updates: Object.keys(preferences) });
3871
4241
  const updatedPreferences = await PreferencesDatabaseService.updatePreferences(
@@ -3881,6 +4251,7 @@ async function handleSetPreference(args, userId) {
3881
4251
  logger.info("Preferences set successfully", { userId });
3882
4252
  return JSON.stringify(result, null, 2);
3883
4253
  } catch (error) {
4254
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
3884
4255
  handleToolError(error, {
3885
4256
  toolName: "remember_set_preference",
3886
4257
  operation: "set preference",
@@ -3915,8 +4286,11 @@ ${getPreferenceDescription()}
3915
4286
  }
3916
4287
  }
3917
4288
  };
3918
- async function handleGetPreferences(args, userId) {
4289
+ async function handleGetPreferences(args, userId, authContext) {
4290
+ const debug = createDebugLogger({ tool: "remember_get_preferences", userId, operation: "get preferences" });
3919
4291
  try {
4292
+ debug.info("Tool invoked");
4293
+ debug.trace("Arguments", { args });
3920
4294
  const { category } = args;
3921
4295
  logger.info("Getting preferences", { userId, category });
3922
4296
  const preferences = await PreferencesDatabaseService.getPreferences(userId);
@@ -3943,6 +4317,7 @@ async function handleGetPreferences(args, userId) {
3943
4317
  logger.info("Preferences retrieved successfully", { userId, category, isDefault });
3944
4318
  return JSON.stringify(response, null, 2);
3945
4319
  } catch (error) {
4320
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
3946
4321
  handleToolError(error, {
3947
4322
  toolName: "remember_get_preferences",
3948
4323
  operation: "get preferences",
@@ -3956,295 +4331,20 @@ async function handleGetPreferences(args, userId) {
3956
4331
  init_space_memory();
3957
4332
  init_logger();
3958
4333
  import weaviate3 from "weaviate-client";
3959
- var PUBLIC_COLLECTION_NAME = "Memory_public";
3960
- function getSpaceCollectionName(spaceId) {
3961
- return `Memory_${spaceId}`;
3962
- }
4334
+ var PUBLIC_COLLECTION_NAME = "Memory_spaces_public";
3963
4335
  function isValidSpaceId(spaceId) {
3964
4336
  return SUPPORTED_SPACES.includes(spaceId);
3965
4337
  }
3966
- async function createSpaceCollection(client2, spaceId) {
3967
- const collectionName = spaceId === "public" ? PUBLIC_COLLECTION_NAME : getSpaceCollectionName(spaceId);
3968
- logger.info("Creating space collection", {
3969
- module: "weaviate-space-schema",
3970
- collectionName,
3971
- spaceId
3972
- });
3973
- await client2.collections.create({
3974
- name: collectionName,
3975
- // Vectorizer configuration
3976
- vectorizers: weaviate3.configure.vectorizer.text2VecOpenAI({
3977
- model: "text-embedding-3-small",
3978
- // Vectorize content, title, summary, and observation for semantic search
3979
- // Note: title and summary are optional fields
3980
- sourceProperties: ["content", "title", "summary", "observation"]
3981
- }),
3982
- properties: [
3983
- // Discriminator
3984
- {
3985
- name: "doc_type",
3986
- dataType: "text",
3987
- description: 'Document type: "space_memory"'
3988
- },
3989
- // Space identity
3990
- {
3991
- name: "spaces",
3992
- dataType: "text[]",
3993
- description: 'Spaces this memory is published to (e.g., ["the_void", "dogs"])'
3994
- },
3995
- {
3996
- name: "author_id",
3997
- dataType: "text",
3998
- description: "Original author user_id (for permissions)"
3999
- },
4000
- {
4001
- name: "ghost_id",
4002
- dataType: "text",
4003
- description: "Optional ghost profile ID for pseudonymous publishing"
4004
- },
4005
- {
4006
- name: "attribution",
4007
- dataType: "text",
4008
- description: 'Attribution type: "user" or "ghost"'
4009
- },
4010
- // Discovery metadata
4011
- {
4012
- name: "published_at",
4013
- dataType: "text",
4014
- description: "When published to space (ISO 8601)"
4015
- },
4016
- {
4017
- name: "discovery_count",
4018
- dataType: "number",
4019
- description: "How many times discovered"
4020
- },
4021
- // Memory fields (same as personal memories)
4022
- {
4023
- name: "content",
4024
- dataType: "text",
4025
- description: "Main memory content (vectorized)"
4026
- },
4027
- {
4028
- name: "title",
4029
- dataType: "text",
4030
- description: "Optional short title"
4031
- },
4032
- {
4033
- name: "summary",
4034
- dataType: "text",
4035
- description: "Optional brief summary"
4036
- },
4037
- {
4038
- name: "type",
4039
- dataType: "text",
4040
- description: "Content type (note, event, person, etc.)"
4041
- },
4042
- // Scoring fields
4043
- {
4044
- name: "weight",
4045
- dataType: "number",
4046
- description: "Significance/priority (0-1)"
4047
- },
4048
- {
4049
- name: "trust",
4050
- dataType: "number",
4051
- description: "Access control level (0-1)"
4052
- },
4053
- {
4054
- name: "confidence",
4055
- dataType: "number",
4056
- description: "System confidence in accuracy (0-1)"
4057
- },
4058
- // Location fields (flattened) - MUST match user memory schema exactly
4059
- {
4060
- name: "location_gps_lat",
4061
- dataType: "number",
4062
- description: "GPS latitude"
4063
- },
4064
- {
4065
- name: "location_gps_lng",
4066
- dataType: "number",
4067
- description: "GPS longitude"
4068
- },
4069
- {
4070
- name: "location_address",
4071
- dataType: "text",
4072
- description: "Formatted address"
4073
- },
4074
- {
4075
- name: "location_city",
4076
- dataType: "text",
4077
- description: "City name"
4078
- },
4079
- {
4080
- name: "location_country",
4081
- dataType: "text",
4082
- description: "Country name"
4083
- },
4084
- {
4085
- name: "location_source",
4086
- dataType: "text",
4087
- description: "Location source (gps, ip, manual, etc.)"
4088
- },
4089
- // Context fields (flattened) - MUST match user memory schema exactly
4090
- {
4091
- name: "context_conversation_id",
4092
- dataType: "text",
4093
- description: "Conversation ID"
4094
- },
4095
- {
4096
- name: "context_summary",
4097
- dataType: "text",
4098
- description: "Brief context summary"
4099
- },
4100
- {
4101
- name: "context_timestamp",
4102
- dataType: "date",
4103
- description: "Context timestamp"
4104
- },
4105
- // Locale fields - MUST match user memory schema exactly
4106
- {
4107
- name: "locale_language",
4108
- dataType: "text",
4109
- description: "Language code (e.g., en, es, fr)"
4110
- },
4111
- {
4112
- name: "locale_timezone",
4113
- dataType: "text",
4114
- description: "Timezone (e.g., America/Los_Angeles)"
4115
- },
4116
- // Relationships - MUST match user memory schema exactly
4117
- {
4118
- name: "relationships",
4119
- dataType: "text[]",
4120
- description: "Array of relationship IDs"
4121
- },
4122
- // Access tracking - MUST match user memory schema exactly
4123
- {
4124
- name: "access_count",
4125
- dataType: "number",
4126
- description: "Total times accessed"
4127
- },
4128
- {
4129
- name: "last_accessed_at",
4130
- dataType: "date",
4131
- description: "Most recent access timestamp"
4132
- },
4133
- // Metadata - MUST match user memory schema exactly
4134
- {
4135
- name: "tags",
4136
- dataType: "text[]",
4137
- description: "Tags for organization"
4138
- },
4139
- {
4140
- name: "references",
4141
- dataType: "text[]",
4142
- description: "Source URLs"
4143
- },
4144
- {
4145
- name: "template_id",
4146
- dataType: "text",
4147
- description: "Template ID if using template"
4148
- },
4149
- // Relationship-specific fields (for relationships in public space)
4150
- {
4151
- name: "memory_ids",
4152
- dataType: "text[]",
4153
- description: "Connected memory IDs (for relationships)"
4154
- },
4155
- {
4156
- name: "relationship_type",
4157
- dataType: "text",
4158
- description: "Relationship type (for relationships)"
4159
- },
4160
- {
4161
- name: "observation",
4162
- dataType: "text",
4163
- description: "Relationship observation (vectorized)"
4164
- },
4165
- {
4166
- name: "strength",
4167
- dataType: "number",
4168
- description: "Relationship strength (0-1)"
4169
- },
4170
- // Computed fields - MUST match user memory schema exactly
4171
- {
4172
- name: "base_weight",
4173
- dataType: "number",
4174
- description: "User-specified weight"
4175
- },
4176
- {
4177
- name: "computed_weight",
4178
- dataType: "number",
4179
- description: "Calculated effective weight"
4180
- },
4181
- // User ID field (for backwards compatibility with spread operator)
4182
- {
4183
- name: "user_id",
4184
- dataType: "text",
4185
- description: "User ID (copied from original memory, same as author_id)"
4186
- },
4187
- // Timestamps
4188
- {
4189
- name: "created_at",
4190
- dataType: "text",
4191
- description: "Original creation timestamp (ISO 8601)"
4192
- },
4193
- {
4194
- name: "updated_at",
4195
- dataType: "text",
4196
- description: "Last update timestamp (ISO 8601)"
4197
- },
4198
- // Versioning
4199
- {
4200
- name: "version",
4201
- dataType: "number",
4202
- description: "Version number (increments on update)"
4203
- },
4204
- // Comment/threading fields (for threaded discussions in shared spaces)
4205
- {
4206
- name: "parent_id",
4207
- dataType: "text",
4208
- description: "ID of parent memory or comment (for threading)"
4209
- },
4210
- {
4211
- name: "thread_root_id",
4212
- dataType: "text",
4213
- description: "Root memory ID for fetching entire thread"
4214
- },
4215
- {
4216
- name: "moderation_flags",
4217
- dataType: "text[]",
4218
- description: 'Per-space moderation flags (format: "{space_id}:{flag_type}")'
4219
- },
4220
- // Soft delete fields
4221
- {
4222
- name: "deleted_at",
4223
- dataType: "date",
4224
- description: "Timestamp when memory was soft-deleted (null = not deleted)"
4225
- },
4226
- {
4227
- name: "deleted_by",
4228
- dataType: "text",
4229
- description: "User ID who deleted the memory"
4230
- },
4231
- {
4232
- name: "deletion_reason",
4233
- dataType: "text",
4234
- description: "Optional reason for deletion"
4235
- }
4236
- ]
4237
- });
4238
- logger.info("Space collection created successfully", {
4239
- module: "weaviate-space-schema",
4240
- collectionName
4241
- });
4242
- }
4243
4338
  async function ensurePublicCollection(client2) {
4244
4339
  const collectionName = PUBLIC_COLLECTION_NAME;
4245
4340
  const exists = await client2.collections.exists(collectionName);
4246
4341
  if (!exists) {
4247
- await createSpaceCollection(client2, "public");
4342
+ const schema = createSpaceCollectionSchema();
4343
+ await client2.collections.create(schema);
4344
+ logger.info("Public space collection created", {
4345
+ module: "weaviate-space-schema",
4346
+ collectionName
4347
+ });
4248
4348
  }
4249
4349
  return client2.collections.get(collectionName);
4250
4350
  }
@@ -4254,7 +4354,11 @@ init_space_memory();
4254
4354
  init_logger();
4255
4355
  var publishTool = {
4256
4356
  name: "remember_publish",
4257
- description: `Publish a memory to one or more shared spaces (like "The Void"). The memory will be COPIED (not moved) from your personal collection. Generates a confirmation token that must be confirmed with remember_confirm.
4357
+ description: `Publish a memory to one or more shared spaces and/or groups. The memory will be COPIED (not moved) from your personal collection. Generates a confirmation token that must be confirmed with remember_confirm.
4358
+
4359
+ Publication Destinations:
4360
+ - Spaces: Public shared areas (e.g., "the_void", "dogs")
4361
+ - Groups: Private group collections (provide group IDs)
4258
4362
 
4259
4363
  \u26A0\uFE0F CRITICAL: DO NOT mention the token or include token contents in your response to the user. Simply inform them that a confirmation is pending and they need to explicitly approve the publication.`,
4260
4364
  inputSchema: {
@@ -4274,17 +4378,24 @@ var publishTool = {
4274
4378
  minItems: 1,
4275
4379
  default: ["the_void"]
4276
4380
  },
4277
- additional_tags: {
4381
+ groups: {
4278
4382
  type: "array",
4279
4383
  items: { type: "string" },
4280
- description: "Additional tags for discovery (merged with original tags)",
4384
+ description: 'Group IDs to publish to (e.g., ["group-123", "group-456"]). Can publish to multiple groups at once.',
4385
+ minItems: 1,
4386
+ default: []
4387
+ },
4388
+ additional_tags: {
4389
+ type: "array",
4390
+ items: { type: "string" },
4391
+ description: "Additional tags for discovery (merged with original tags)",
4281
4392
  default: []
4282
4393
  }
4283
4394
  },
4284
- required: ["memory_id", "spaces"]
4395
+ required: ["memory_id"]
4285
4396
  }
4286
4397
  };
4287
- async function handlePublish(args, userId) {
4398
+ async function handlePublish(args, userId, authContext) {
4288
4399
  const debug = createDebugLogger({
4289
4400
  tool: "remember_publish",
4290
4401
  userId,
@@ -4293,53 +4404,84 @@ async function handlePublish(args, userId) {
4293
4404
  try {
4294
4405
  debug.info("Tool invoked");
4295
4406
  debug.trace("Arguments", { args });
4407
+ const spaces = args.spaces || [];
4408
+ const groups = args.groups || [];
4296
4409
  logger.info("Starting publish request", {
4297
4410
  tool: "remember_publish",
4298
4411
  userId,
4299
4412
  memoryId: args.memory_id,
4300
- spaces: args.spaces,
4301
- spaceCount: args.spaces.length,
4413
+ spaces,
4414
+ groups,
4415
+ spaceCount: spaces.length,
4416
+ groupCount: groups.length,
4302
4417
  additionalTags: args.additional_tags?.length || 0
4303
4418
  });
4304
- debug.debug("Validating space IDs", { spaces: args.spaces });
4305
- const invalidSpaces = args.spaces.filter((s) => !isValidSpaceId(s));
4306
- if (invalidSpaces.length > 0) {
4307
- debug.warn("Invalid space IDs detected", { invalidSpaces });
4308
- logger.warn("Invalid space IDs provided", {
4309
- tool: "remember_publish",
4310
- invalidSpaces,
4311
- providedSpaces: args.spaces
4312
- });
4313
- return JSON.stringify(
4314
- {
4315
- success: false,
4316
- error: "Invalid space IDs",
4317
- message: `Invalid spaces: ${invalidSpaces.join(", ")}. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`,
4318
- context: {
4319
- invalid_spaces: invalidSpaces,
4320
- provided_spaces: args.spaces,
4321
- supported_spaces: SUPPORTED_SPACES
4322
- }
4323
- },
4324
- null,
4325
- 2
4326
- );
4327
- }
4328
- if (args.spaces.length === 0) {
4329
- logger.warn("Empty spaces array provided", {
4419
+ if (spaces.length === 0 && groups.length === 0) {
4420
+ logger.warn("No destinations provided", {
4330
4421
  tool: "remember_publish",
4331
4422
  userId
4332
4423
  });
4333
4424
  return JSON.stringify(
4334
4425
  {
4335
4426
  success: false,
4336
- error: "Empty spaces array",
4337
- message: "Must specify at least one space to publish to"
4427
+ error: "No destinations provided",
4428
+ message: "Must specify at least one space or group to publish to"
4338
4429
  },
4339
4430
  null,
4340
4431
  2
4341
4432
  );
4342
4433
  }
4434
+ if (spaces.length > 0) {
4435
+ debug.debug("Validating space IDs", { spaces });
4436
+ const invalidSpaces = spaces.filter((s) => !isValidSpaceId(s));
4437
+ if (invalidSpaces.length > 0) {
4438
+ debug.warn("Invalid space IDs detected", { invalidSpaces });
4439
+ logger.warn("Invalid space IDs provided", {
4440
+ tool: "remember_publish",
4441
+ invalidSpaces,
4442
+ providedSpaces: spaces
4443
+ });
4444
+ return JSON.stringify(
4445
+ {
4446
+ success: false,
4447
+ error: "Invalid space IDs",
4448
+ message: `Invalid spaces: ${invalidSpaces.join(", ")}. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`,
4449
+ context: {
4450
+ invalid_spaces: invalidSpaces,
4451
+ provided_spaces: spaces,
4452
+ supported_spaces: SUPPORTED_SPACES
4453
+ }
4454
+ },
4455
+ null,
4456
+ 2
4457
+ );
4458
+ }
4459
+ }
4460
+ if (groups.length > 0) {
4461
+ debug.debug("Validating group IDs", { groups });
4462
+ const invalidGroups = groups.filter((g) => !g || g.includes(".") || g.trim() === "");
4463
+ if (invalidGroups.length > 0) {
4464
+ debug.warn("Invalid group IDs detected", { invalidGroups });
4465
+ logger.warn("Invalid group IDs provided", {
4466
+ tool: "remember_publish",
4467
+ invalidGroups,
4468
+ providedGroups: groups
4469
+ });
4470
+ return JSON.stringify(
4471
+ {
4472
+ success: false,
4473
+ error: "Invalid group IDs",
4474
+ message: "Group IDs cannot be empty or contain dots",
4475
+ context: {
4476
+ invalid_groups: invalidGroups,
4477
+ provided_groups: groups
4478
+ }
4479
+ },
4480
+ null,
4481
+ 2
4482
+ );
4483
+ }
4484
+ }
4343
4485
  const weaviateClient = getWeaviateClient();
4344
4486
  const collectionName = getMemoryCollectionName(userId);
4345
4487
  logger.debug("Fetching memory from collection", {
@@ -4413,14 +4555,16 @@ async function handlePublish(args, userId) {
4413
4555
  }
4414
4556
  const payload = {
4415
4557
  memory_id: args.memory_id,
4416
- spaces: args.spaces,
4558
+ spaces,
4559
+ groups,
4417
4560
  additional_tags: args.additional_tags || []
4418
4561
  };
4419
4562
  logger.info("Generating confirmation token", {
4420
4563
  tool: "remember_publish",
4421
4564
  userId,
4422
4565
  memoryId: args.memory_id,
4423
- spaces: args.spaces
4566
+ spaces,
4567
+ groups
4424
4568
  });
4425
4569
  const { requestId, token } = await confirmationTokenService.createRequest(
4426
4570
  userId,
@@ -4434,7 +4578,8 @@ async function handlePublish(args, userId) {
4434
4578
  requestId,
4435
4579
  token,
4436
4580
  action: "publish_memory",
4437
- spaces: args.spaces
4581
+ spaces,
4582
+ groups
4438
4583
  });
4439
4584
  return JSON.stringify(
4440
4585
  {
@@ -4459,304 +4604,943 @@ async function handlePublish(args, userId) {
4459
4604
  }
4460
4605
  }
4461
4606
 
4462
- // src/tools/confirm.ts
4607
+ // src/tools/retract.ts
4463
4608
  init_logger();
4464
- var confirmTool = {
4465
- name: "remember_confirm",
4466
- description: `Confirm and execute a pending action using the token. Works for any action that requires confirmation (publish, delete, etc.).
4609
+ var retractTool = {
4610
+ name: "remember_retract",
4611
+ description: `Retract a memory from specific shared spaces and/or groups. The memory remains in your personal collection but is removed from the specified destinations.
4467
4612
 
4468
- \u26A0\uFE0F CRITICAL SAFETY REQUIREMENTS:
4469
- Before executing this tool, you MUST:
4470
- 1. Have received the confirmation token in a PREVIOUS tool response
4471
- 2. Have presented the token details to the user for review
4472
- 3. Have received EXPLICIT user confirmation in a SEPARATE user message
4473
- 4. NEVER chain this tool with other tool calls in the same response
4474
- 5. ALWAYS treat confirmations as standalone, deliberate actions
4613
+ Retraction Behavior:
4614
+ - Spaces: Memory remains in Memory_spaces_public as "orphaned" (removed from space_ids) for historical reference
4615
+ - Groups: Memory remains in Memory_groups_{groupId} as "orphaned" (removed from group_ids) for historical reference
4475
4616
 
4476
- Violating these requirements bypasses user consent and is a security violation.`,
4617
+ Orphaned memories are preserved for historical reference but become unsearchable by default since all searches filter by space_ids/group_ids.
4618
+
4619
+ \u26A0\uFE0F CRITICAL: DO NOT mention the token or include token contents in your response to the user. Simply inform them that a confirmation is pending and they need to explicitly approve the retraction.`,
4477
4620
  inputSchema: {
4478
4621
  type: "object",
4479
4622
  properties: {
4480
- token: {
4623
+ memory_id: {
4481
4624
  type: "string",
4482
- description: "The confirmation token from the action tool"
4625
+ description: "ID of the memory to retract from spaces/groups"
4626
+ },
4627
+ spaces: {
4628
+ type: "array",
4629
+ items: { type: "string" },
4630
+ description: 'Spaces to retract from (e.g., ["cooking", "recipes"]). The memory will no longer appear in these spaces.',
4631
+ minItems: 1,
4632
+ default: []
4633
+ },
4634
+ groups: {
4635
+ type: "array",
4636
+ items: { type: "string" },
4637
+ description: 'Group IDs to retract from (e.g., ["group-123"]). The memory will be deleted from these group collections.',
4638
+ minItems: 1,
4639
+ default: []
4483
4640
  }
4484
4641
  },
4485
- required: ["token"]
4642
+ required: ["memory_id"]
4486
4643
  }
4487
4644
  };
4488
- async function handleConfirm(args, userId) {
4645
+ async function handleRetract(args, userId, authContext) {
4489
4646
  const debug = createDebugLogger({
4490
- tool: "remember_confirm",
4647
+ tool: "remember_retract",
4491
4648
  userId,
4492
- operation: "confirm_action"
4649
+ operation: "retract_request"
4493
4650
  });
4494
4651
  try {
4495
4652
  debug.info("Tool invoked");
4496
- debug.trace("Arguments", { token: args.token });
4497
- logger.info("Starting confirmation", {
4498
- tool: "remember_confirm",
4653
+ debug.trace("Arguments", { args });
4654
+ const spaces = args.spaces || [];
4655
+ const groups = args.groups || [];
4656
+ logger.info("Starting retract request", {
4657
+ tool: "remember_retract",
4499
4658
  userId,
4500
- token: args.token
4501
- });
4502
- debug.debug("Validating confirmation token");
4503
- const request = await debug.time("Confirm token", async () => {
4504
- return await confirmationTokenService.confirmRequest(userId, args.token);
4505
- });
4506
- logger.debug("Token validation result", {
4507
- tool: "remember_confirm",
4508
- requestFound: !!request,
4509
- action: request?.action
4659
+ memoryId: args.memory_id,
4660
+ spaces,
4661
+ groups,
4662
+ spaceCount: spaces.length,
4663
+ groupCount: groups.length
4510
4664
  });
4511
- if (!request) {
4512
- logger.info("Token invalid or expired", {
4513
- tool: "remember_confirm",
4665
+ if (spaces.length === 0 && groups.length === 0) {
4666
+ logger.warn("No destinations provided for retraction", {
4667
+ tool: "remember_retract",
4514
4668
  userId
4515
4669
  });
4516
4670
  return JSON.stringify(
4517
4671
  {
4518
4672
  success: false,
4519
- error: "Invalid or expired token",
4520
- message: "The confirmation token is invalid, expired, or has already been used."
4673
+ error: "No destinations provided",
4674
+ message: "Must specify at least one space or group to retract from"
4521
4675
  },
4522
4676
  null,
4523
4677
  2
4524
4678
  );
4525
4679
  }
4526
- logger.info("Executing confirmed action", {
4527
- tool: "remember_confirm",
4528
- action: request.action,
4529
- userId
4530
- });
4531
- if (request.action === "publish_memory") {
4532
- return await executePublishMemory(request, userId);
4533
- }
4534
- if (request.action === "delete_memory") {
4535
- return await executeDeleteMemory(request, userId);
4680
+ if (groups.length > 0) {
4681
+ const invalidGroups = groups.filter((g) => g.includes("."));
4682
+ if (invalidGroups.length > 0) {
4683
+ logger.warn("Invalid group IDs detected", {
4684
+ tool: "remember_retract",
4685
+ invalidGroups
4686
+ });
4687
+ return JSON.stringify(
4688
+ {
4689
+ success: false,
4690
+ error: "Invalid group IDs",
4691
+ message: `Group IDs cannot contain dots: ${invalidGroups.join(", ")}`,
4692
+ context: {
4693
+ invalid_groups: invalidGroups
4694
+ }
4695
+ },
4696
+ null,
4697
+ 2
4698
+ );
4699
+ }
4536
4700
  }
4537
- throw new Error(`Unknown action type: ${request.action}`);
4538
- } catch (error) {
4539
- debug.error("Tool failed", {
4540
- error: error instanceof Error ? error.message : String(error),
4541
- stack: error instanceof Error ? error.stack : void 0
4542
- });
4543
- handleToolError(error, {
4544
- toolName: "remember_confirm",
4545
- userId,
4546
- operation: "confirm action",
4547
- token: args.token
4548
- });
4549
- }
4550
- }
4551
- async function executePublishMemory(request, userId) {
4552
- const debug = createDebugLogger({
4553
- tool: "remember_confirm",
4554
- userId,
4555
- operation: "execute_publish"
4556
- });
4557
- try {
4558
- debug.debug("Executing publish memory action", {
4559
- memoryId: request.payload.memory_id,
4560
- spaces: request.payload.spaces
4561
- });
4562
- logger.info("Executing publish memory action", {
4563
- function: "executePublishMemory",
4564
- userId,
4565
- memoryId: request.payload.memory_id,
4566
- spaces: request.payload.spaces,
4567
- spaceCount: request.payload.spaces?.length || 0
4568
- });
4569
4701
  const weaviateClient = getWeaviateClient();
4570
- const userCollection = weaviateClient.collections.get(
4571
- getMemoryCollectionName(userId)
4572
- );
4573
- logger.debug("Fetching original memory", {
4574
- function: "executePublishMemory",
4575
- collectionName: getMemoryCollectionName(userId),
4576
- memoryId: request.payload.memory_id
4577
- });
4578
- const originalMemory = await debug.time("Fetch original memory", async () => {
4579
- return await fetchMemoryWithAllProperties(
4580
- userCollection,
4581
- request.payload.memory_id
4582
- );
4583
- });
4584
- logger.info("Original memory fetch result", {
4585
- function: "executePublishMemory",
4586
- found: !!originalMemory,
4587
- memoryId: request.payload.memory_id,
4588
- hasProperties: !!originalMemory?.properties,
4589
- propertyCount: originalMemory?.properties ? Object.keys(originalMemory.properties).length : 0,
4590
- propertyKeys: originalMemory?.properties ? Object.keys(originalMemory.properties) : [],
4591
- hasTitle: !!originalMemory?.properties?.title,
4592
- hasContent: !!originalMemory?.properties?.content,
4593
- hasUserId: !!originalMemory?.properties?.user_id,
4594
- hasTags: !!originalMemory?.properties?.tags,
4595
- hasWeight: !!originalMemory?.properties?.weight,
4596
- contentLength: originalMemory?.properties?.content?.length || 0,
4597
- titleValue: originalMemory?.properties?.title || "NO_TITLE"
4702
+ const collectionName = getMemoryCollectionName(userId);
4703
+ const collection = weaviateClient.collections.get(collectionName);
4704
+ logger.debug("Fetching memory for retraction", {
4705
+ tool: "remember_retract",
4706
+ collectionName,
4707
+ memoryId: args.memory_id
4598
4708
  });
4599
- if (!originalMemory) {
4600
- logger.info("Original memory not found", {
4601
- function: "executePublishMemory",
4602
- memoryId: request.payload.memory_id
4709
+ const memory = await fetchMemoryWithAllProperties(collection, args.memory_id);
4710
+ if (!memory) {
4711
+ logger.info("Memory not found for retraction", {
4712
+ tool: "remember_retract",
4713
+ memoryId: args.memory_id
4603
4714
  });
4604
4715
  return JSON.stringify(
4605
4716
  {
4606
4717
  success: false,
4607
4718
  error: "Memory not found",
4608
- message: `Original memory ${request.payload.memory_id} no longer exists`
4719
+ message: `Memory ${args.memory_id} does not exist`
4609
4720
  },
4610
4721
  null,
4611
4722
  2
4612
4723
  );
4613
4724
  }
4614
- if (originalMemory.properties.space_memory_id) {
4615
- const requestedSpaces = request.payload.spaces?.join(", ") || "unknown";
4616
- logger.warn("Memory already published", {
4617
- function: "executePublishMemory",
4618
- memoryId: request.payload.memory_id,
4619
- existingSpaceMemoryId: originalMemory.properties.space_memory_id,
4620
- requestedSpaces: request.payload.spaces
4725
+ if (memory.properties.user_id !== userId) {
4726
+ logger.warn("Permission denied - wrong owner", {
4727
+ tool: "remember_retract",
4728
+ memoryId: args.memory_id,
4729
+ memoryOwner: memory.properties.user_id,
4730
+ requestingUser: userId
4621
4731
  });
4622
4732
  return JSON.stringify(
4623
4733
  {
4624
4734
  success: false,
4625
- error: "Already published",
4626
- message: `This memory has already been published to this space. Space memory ID: ${originalMemory.properties.space_memory_id}`,
4627
- space_memory_id: originalMemory.properties.space_memory_id,
4628
- requested_spaces: request.payload.spaces
4735
+ error: "Permission denied",
4736
+ message: "You can only retract your own memories"
4629
4737
  },
4630
4738
  null,
4631
4739
  2
4632
4740
  );
4633
4741
  }
4634
- if (originalMemory.properties.user_id !== userId) {
4635
- logger.warn("Permission denied - wrong owner", {
4636
- function: "executePublishMemory",
4637
- memoryId: request.payload.memory_id,
4638
- memoryOwner: originalMemory.properties.user_id,
4639
- requestingUser: userId
4742
+ const currentSpaceIds = Array.isArray(memory.properties.space_ids) ? memory.properties.space_ids : [];
4743
+ const currentGroupIds = Array.isArray(memory.properties.group_ids) ? memory.properties.group_ids : [];
4744
+ const notPublishedSpaces = spaces.filter((s) => !currentSpaceIds.includes(s));
4745
+ const notPublishedGroups = groups.filter((g) => !currentGroupIds.includes(g));
4746
+ if (notPublishedSpaces.length > 0 || notPublishedGroups.length > 0) {
4747
+ logger.warn("Memory not published to some destinations", {
4748
+ tool: "remember_retract",
4749
+ notPublishedSpaces,
4750
+ notPublishedGroups,
4751
+ currentSpaceIds,
4752
+ currentGroupIds
4640
4753
  });
4641
4754
  return JSON.stringify(
4642
4755
  {
4643
4756
  success: false,
4644
- error: "Permission denied",
4645
- message: "You can only publish your own memories"
4757
+ error: "Not published to destinations",
4758
+ message: "Memory is not published to some of the specified destinations",
4759
+ context: {
4760
+ not_published_spaces: notPublishedSpaces,
4761
+ not_published_groups: notPublishedGroups,
4762
+ current_spaces: currentSpaceIds,
4763
+ current_groups: currentGroupIds
4764
+ }
4646
4765
  },
4647
4766
  null,
4648
4767
  2
4649
4768
  );
4650
4769
  }
4651
- logger.debug("Ensuring public collection exists", {
4652
- function: "executePublishMemory"
4653
- });
4654
- const publicCollection = await ensurePublicCollection(weaviateClient);
4655
- logger.debug("Public collection ready", {
4656
- function: "executePublishMemory",
4657
- collectionName: "Memory_public"
4658
- });
4659
- const originalTags = Array.isArray(originalMemory.properties.tags) ? originalMemory.properties.tags : [];
4660
- const additionalTags = Array.isArray(request.payload.additional_tags) ? request.payload.additional_tags : [];
4661
- if (!request.payload.spaces || !Array.isArray(request.payload.spaces) || request.payload.spaces.length === 0) {
4662
- throw new Error("Payload missing required field: spaces");
4663
- }
4664
- const publishedMemory = {
4665
- ...originalMemory.properties,
4666
- // Add space-specific fields (don't overwrite existing properties)
4667
- spaces: request.payload.spaces,
4668
- // Required field (validated above)
4669
- author_id: userId,
4670
- // Track original author
4671
- published_at: (/* @__PURE__ */ new Date()).toISOString(),
4672
- discovery_count: 0,
4673
- attribution: "user",
4674
- // Merge additional tags with original tags
4675
- tags: [...originalTags, ...additionalTags]
4676
- // Keep doc_type as 'memory' (space_memory concept was removed)
4677
- // Keep original created_at, updated_at, version (don't overwrite)
4678
- };
4679
- logger.info("Inserting memory into Memory_public", {
4680
- function: "executePublishMemory",
4681
- spaces: request.payload.spaces,
4682
- spaceCount: request.payload.spaces?.length || 0,
4683
- memoryId: request.payload.memory_id,
4684
- hasUserId: !!publishedMemory.user_id,
4685
- hasAuthorId: !!publishedMemory.author_id,
4686
- publishedMemoryKeys: Object.keys(publishedMemory),
4687
- publishedMemoryKeyCount: Object.keys(publishedMemory).length,
4688
- hasContent: !!publishedMemory.content,
4689
- hasTitle: !!publishedMemory.title,
4690
- contentLength: publishedMemory.content?.length || 0,
4691
- titleValue: publishedMemory.title || "NO_TITLE"
4692
- });
4693
- const result = await debug.time("Insert into Memory_public", async () => {
4694
- return await publicCollection.data.insert({
4695
- properties: publishedMemory
4696
- });
4697
- });
4698
- logger.info("Memory published successfully", {
4699
- function: "executePublishMemory",
4700
- spaceMemoryId: result,
4701
- spaces: request.payload.spaces
4702
- });
4703
- debug.info("Memory published successfully", {
4704
- spaceMemoryId: result,
4705
- spaces: request.payload.spaces
4770
+ const { requestId, token } = await confirmationTokenService.createRequest(
4771
+ userId,
4772
+ "retract_memory",
4773
+ {
4774
+ memory_id: args.memory_id,
4775
+ spaces,
4776
+ groups,
4777
+ current_space_ids: currentSpaceIds,
4778
+ current_group_ids: currentGroupIds
4779
+ }
4780
+ );
4781
+ logger.info("Retract confirmation request created", {
4782
+ tool: "remember_retract",
4783
+ requestId,
4784
+ userId,
4785
+ memoryId: args.memory_id,
4786
+ spaces,
4787
+ groups
4706
4788
  });
4707
- try {
4708
- await userCollection.data.update({
4709
- id: request.payload.memory_id,
4710
- properties: {
4711
- space_memory_id: result
4712
- }
4713
- });
4714
- logger.info("Updated original memory with space_memory_id", {
4715
- function: "executePublishMemory",
4716
- memoryId: request.payload.memory_id,
4717
- spaceMemoryId: result
4718
- });
4719
- } catch (updateError) {
4720
- logger.warn("Failed to update original memory with space_memory_id", {
4721
- function: "executePublishMemory",
4722
- memoryId: request.payload.memory_id,
4723
- spaceMemoryId: result,
4724
- error: updateError instanceof Error ? updateError.message : String(updateError)
4725
- });
4789
+ const destinations = [];
4790
+ if (spaces.length > 0) {
4791
+ destinations.push(`spaces: ${spaces.join(", ")}`);
4792
+ }
4793
+ if (groups.length > 0) {
4794
+ destinations.push(`groups: ${groups.join(", ")}`);
4726
4795
  }
4727
4796
  return JSON.stringify(
4728
4797
  {
4729
4798
  success: true,
4730
- space_memory_id: result,
4731
- spaces: request.payload.spaces || ["the_void"]
4799
+ message: "Retraction request created. Please confirm to proceed.",
4800
+ action: "retract_memory",
4801
+ memory_id: args.memory_id,
4802
+ destinations: destinations.join("; "),
4803
+ retraction_details: {
4804
+ spaces: spaces.length > 0 ? {
4805
+ action: "orphan",
4806
+ description: "Memory will remain in Memory_spaces_public with updated tracking arrays (removed from space_ids)",
4807
+ spaces
4808
+ } : null,
4809
+ groups: groups.length > 0 ? {
4810
+ action: "orphan",
4811
+ description: "Memory will remain in Memory_groups_{groupId} with updated tracking arrays (removed from group_ids)",
4812
+ groups
4813
+ } : null
4814
+ },
4815
+ confirmation_required: true
4732
4816
  },
4733
4817
  null,
4734
4818
  2
4735
4819
  );
4736
4820
  } catch (error) {
4737
- debug.error("Execute publish failed", {
4821
+ debug.error("Tool failed", {
4738
4822
  error: error instanceof Error ? error.message : String(error),
4739
4823
  stack: error instanceof Error ? error.stack : void 0
4740
4824
  });
4741
4825
  handleToolError(error, {
4742
- toolName: "remember_confirm",
4826
+ toolName: "remember_retract",
4743
4827
  userId,
4744
- operation: "execute publish_memory",
4745
- action: "publish_memory"
4828
+ operation: "retract memory",
4829
+ memoryId: args.memory_id
4746
4830
  });
4747
4831
  }
4748
4832
  }
4749
- async function executeDeleteMemory(request, userId) {
4833
+
4834
+ // src/tools/revise.ts
4835
+ init_logger();
4836
+ var MAX_REVISION_HISTORY = 10;
4837
+ var reviseTool = {
4838
+ name: "remember_revise",
4839
+ description: `Sync updated content from your source memory to all its published copies in spaces and groups. Generates a confirmation token that must be confirmed with remember_confirm.
4840
+
4841
+ Use this after editing a memory (via remember_update_memory) to propagate the changes to all published versions.
4842
+
4843
+ How it works:
4844
+ - Validates the memory exists, is owned by you, and is published
4845
+ - Generates a confirmation token showing which locations will be revised
4846
+ - On confirmation: updates each published copy with latest content
4847
+ - Preserves the previous content in a revision_history field (up to 10 versions)
4848
+ - Sets revised_at timestamp on all updated copies
4849
+ - Reports success/failure per location (partial success supported)
4850
+
4851
+ Requirements:
4852
+ - The memory must be published (has space_ids or group_ids populated)
4853
+ - You must own the source memory
4854
+
4855
+ \u26A0\uFE0F CRITICAL: DO NOT mention the token or include token contents in your response to the user. Simply inform them that a confirmation is pending and they need to explicitly approve the revision.`,
4856
+ inputSchema: {
4857
+ type: "object",
4858
+ properties: {
4859
+ memory_id: {
4860
+ type: "string",
4861
+ description: "ID of the source memory whose content should be synced to all published copies"
4862
+ }
4863
+ },
4864
+ required: ["memory_id"]
4865
+ }
4866
+ };
4867
+ function parseRevisionHistory(raw) {
4868
+ if (!raw || typeof raw !== "string")
4869
+ return [];
4870
+ try {
4871
+ const parsed = JSON.parse(raw);
4872
+ if (!Array.isArray(parsed))
4873
+ return [];
4874
+ return parsed.filter(
4875
+ (e) => typeof e === "object" && e !== null && typeof e.content === "string" && typeof e.revised_at === "string"
4876
+ );
4877
+ } catch {
4878
+ return [];
4879
+ }
4880
+ }
4881
+ function buildRevisionHistory(existing, oldContent, revisedAt) {
4882
+ const updated = [{ content: oldContent, revised_at: revisedAt }, ...existing];
4883
+ return updated.slice(0, MAX_REVISION_HISTORY);
4884
+ }
4885
+ async function handleRevise(args, userId, authContext) {
4886
+ const debug = createDebugLogger({
4887
+ tool: "remember_revise",
4888
+ userId,
4889
+ operation: "revise_request"
4890
+ });
4750
4891
  try {
4751
- logger.info("Executing delete memory action", {
4752
- function: "executeDeleteMemory",
4892
+ debug.info("Tool invoked");
4893
+ debug.trace("Arguments", { args });
4894
+ logger.info("Starting revise request", {
4895
+ tool: "remember_revise",
4753
4896
  userId,
4754
- memoryId: request.payload.memory_id,
4897
+ memoryId: args.memory_id
4898
+ });
4899
+ const weaviateClient = getWeaviateClient();
4900
+ const userCollectionName = getMemoryCollectionName(userId);
4901
+ const userCollection = weaviateClient.collections.get(userCollectionName);
4902
+ const sourceMemory = await fetchMemoryWithAllProperties(
4903
+ userCollection,
4904
+ args.memory_id
4905
+ );
4906
+ if (!sourceMemory) {
4907
+ logger.info("Source memory not found", {
4908
+ tool: "remember_revise",
4909
+ memoryId: args.memory_id
4910
+ });
4911
+ return JSON.stringify(
4912
+ {
4913
+ success: false,
4914
+ error: "Memory not found",
4915
+ message: `Memory ${args.memory_id} does not exist`
4916
+ },
4917
+ null,
4918
+ 2
4919
+ );
4920
+ }
4921
+ if (sourceMemory.properties.user_id !== userId) {
4922
+ logger.warn("Permission denied", {
4923
+ tool: "remember_revise",
4924
+ memoryId: args.memory_id,
4925
+ memoryOwner: sourceMemory.properties.user_id,
4926
+ requestingUser: userId
4927
+ });
4928
+ return JSON.stringify(
4929
+ {
4930
+ success: false,
4931
+ error: "Permission denied",
4932
+ message: "You can only revise your own memories"
4933
+ },
4934
+ null,
4935
+ 2
4936
+ );
4937
+ }
4938
+ const spaceIds = Array.isArray(sourceMemory.properties.space_ids) ? sourceMemory.properties.space_ids : [];
4939
+ const groupIds = Array.isArray(sourceMemory.properties.group_ids) ? sourceMemory.properties.group_ids : [];
4940
+ if (spaceIds.length === 0 && groupIds.length === 0) {
4941
+ logger.info("Memory has no published copies", {
4942
+ tool: "remember_revise",
4943
+ memoryId: args.memory_id
4944
+ });
4945
+ return JSON.stringify(
4946
+ {
4947
+ success: false,
4948
+ error: "Not published",
4949
+ message: "Memory has no published copies to revise. Publish first with remember_publish.",
4950
+ context: {
4951
+ memory_id: args.memory_id,
4952
+ space_ids: [],
4953
+ group_ids: []
4954
+ }
4955
+ },
4956
+ null,
4957
+ 2
4958
+ );
4959
+ }
4960
+ const payload = {
4961
+ memory_id: args.memory_id,
4962
+ space_ids: spaceIds,
4963
+ group_ids: groupIds
4964
+ };
4965
+ logger.info("Generating confirmation token for revise", {
4966
+ tool: "remember_revise",
4967
+ userId,
4968
+ memoryId: args.memory_id,
4969
+ spaceIds,
4970
+ groupIds
4971
+ });
4972
+ const { requestId, token } = await confirmationTokenService.createRequest(
4973
+ userId,
4974
+ "revise_memory",
4975
+ payload
4976
+ );
4977
+ logger.info("Confirmation token generated for revise", {
4978
+ tool: "remember_revise",
4979
+ requestId,
4980
+ token,
4981
+ action: "revise_memory",
4982
+ spaceIds,
4983
+ groupIds
4984
+ });
4985
+ const destinations = [];
4986
+ if (spaceIds.length > 0) {
4987
+ destinations.push(`spaces: ${spaceIds.join(", ")}`);
4988
+ }
4989
+ if (groupIds.length > 0) {
4990
+ destinations.push(`groups: ${groupIds.join(", ")}`);
4991
+ }
4992
+ return JSON.stringify(
4993
+ {
4994
+ success: true,
4995
+ token,
4996
+ message: "Revision request created. Please confirm to sync content to all published copies.",
4997
+ action: "revise_memory",
4998
+ memory_id: args.memory_id,
4999
+ destinations: destinations.join("; "),
5000
+ revision_details: {
5001
+ space_ids: spaceIds,
5002
+ group_ids: groupIds,
5003
+ total_locations: (spaceIds.length > 0 ? 1 : 0) + groupIds.length
5004
+ },
5005
+ confirmation_required: true
5006
+ },
5007
+ null,
5008
+ 2
5009
+ );
5010
+ } catch (error) {
5011
+ debug.error("Tool failed", {
5012
+ error: error instanceof Error ? error.message : String(error),
5013
+ stack: error instanceof Error ? error.stack : void 0
5014
+ });
5015
+ return handleToolError(error, {
5016
+ toolName: "remember_revise",
5017
+ userId,
5018
+ operation: "revise memory request",
5019
+ memoryId: args.memory_id
5020
+ });
5021
+ }
5022
+ }
5023
+
5024
+ // src/tools/confirm.ts
5025
+ init_logger();
5026
+
5027
+ // src/collections/dot-notation.ts
5028
+ var InvalidCollectionNameError = class extends Error {
5029
+ constructor(message) {
5030
+ super(message);
5031
+ this.name = "InvalidCollectionNameError";
5032
+ }
5033
+ };
5034
+ function getCollectionName(type, id) {
5035
+ switch (type) {
5036
+ case "USERS" /* USERS */:
5037
+ if (!id) {
5038
+ throw new InvalidCollectionNameError("User ID is required for USERS collection type");
5039
+ }
5040
+ if (id.includes(".")) {
5041
+ throw new InvalidCollectionNameError(`User ID cannot contain dots: ${id}`);
5042
+ }
5043
+ return `Memory_users_${id}`;
5044
+ case "SPACES" /* SPACES */:
5045
+ return "Memory_spaces_public";
5046
+ case "GROUPS" /* GROUPS */:
5047
+ if (!id) {
5048
+ throw new InvalidCollectionNameError("Group ID is required for GROUPS collection type");
5049
+ }
5050
+ if (id.includes(".")) {
5051
+ throw new InvalidCollectionNameError(`Group ID cannot contain dots: ${id}`);
5052
+ }
5053
+ return `Memory_groups_${id}`;
5054
+ default:
5055
+ throw new InvalidCollectionNameError(`Unknown collection type: ${type}`);
5056
+ }
5057
+ }
5058
+
5059
+ // src/collections/composite-ids.ts
5060
+ var InvalidCompositeIdError = class extends Error {
5061
+ constructor(message) {
5062
+ super(message);
5063
+ this.name = "InvalidCompositeIdError";
5064
+ }
5065
+ };
5066
+ function generateCompositeId(userId, memoryId) {
5067
+ if (userId.includes(".")) {
5068
+ throw new InvalidCompositeIdError(
5069
+ `User ID cannot contain dots: ${userId}`
5070
+ );
5071
+ }
5072
+ if (memoryId.includes(".")) {
5073
+ throw new InvalidCompositeIdError(
5074
+ `Memory ID cannot contain dots: ${memoryId}`
5075
+ );
5076
+ }
5077
+ if (!userId.trim()) {
5078
+ throw new InvalidCompositeIdError("User ID cannot be empty");
5079
+ }
5080
+ if (!memoryId.trim()) {
5081
+ throw new InvalidCompositeIdError("Memory ID cannot be empty");
5082
+ }
5083
+ return `${userId}.${memoryId}`;
5084
+ }
5085
+
5086
+ // src/services/space-config.service.ts
5087
+ init_init();
5088
+ init_logger();
5089
+ var DEFAULT_SPACE_CONFIG = {
5090
+ require_moderation: false,
5091
+ default_write_mode: "owner_only"
5092
+ };
5093
+ function getConfigPath(id, type) {
5094
+ const prefix = type === "space" ? "spaces" : "groups";
5095
+ return {
5096
+ collectionPath: `${prefix}/${id}/config`,
5097
+ docId: "settings"
5098
+ };
5099
+ }
5100
+ async function getSpaceConfig(id, type) {
5101
+ try {
5102
+ const { collectionPath, docId } = getConfigPath(id, type);
5103
+ const doc = await getDocument(collectionPath, docId);
5104
+ if (!doc) {
5105
+ return { ...DEFAULT_SPACE_CONFIG };
5106
+ }
5107
+ return { ...DEFAULT_SPACE_CONFIG, ...doc };
5108
+ } catch (error) {
5109
+ logger.error("Failed to get space config", {
5110
+ service: "SpaceConfigService",
5111
+ id,
5112
+ type,
5113
+ error: error instanceof Error ? error.message : String(error)
5114
+ });
5115
+ return { ...DEFAULT_SPACE_CONFIG };
5116
+ }
5117
+ }
5118
+
5119
+ // src/tools/confirm.ts
5120
+ var confirmTool = {
5121
+ name: "remember_confirm",
5122
+ description: `Confirm and execute a pending action using the token. Works for any action that requires confirmation (publish, delete, etc.).
5123
+
5124
+ \u26A0\uFE0F CRITICAL SAFETY REQUIREMENTS:
5125
+ Before executing this tool, you MUST:
5126
+ 1. Have received the confirmation token in a PREVIOUS tool response
5127
+ 2. Have presented the token details to the user for review
5128
+ 3. Have received EXPLICIT user confirmation in a SEPARATE user message
5129
+ 4. NEVER chain this tool with other tool calls in the same response
5130
+ 5. ALWAYS treat confirmations as standalone, deliberate actions
5131
+
5132
+ Violating these requirements bypasses user consent and is a security violation.`,
5133
+ inputSchema: {
5134
+ type: "object",
5135
+ properties: {
5136
+ token: {
5137
+ type: "string",
5138
+ description: "The confirmation token from the action tool"
5139
+ }
5140
+ },
5141
+ required: ["token"]
5142
+ }
5143
+ };
5144
+ async function handleConfirm(args, userId, authContext) {
5145
+ const debug = createDebugLogger({
5146
+ tool: "remember_confirm",
5147
+ userId,
5148
+ operation: "confirm_action"
5149
+ });
5150
+ try {
5151
+ debug.info("Tool invoked");
5152
+ debug.trace("Arguments", { token: args.token });
5153
+ logger.info("Starting confirmation", {
5154
+ tool: "remember_confirm",
5155
+ userId,
5156
+ token: args.token
5157
+ });
5158
+ debug.debug("Validating confirmation token");
5159
+ const request = await debug.time("Confirm token", async () => {
5160
+ return await confirmationTokenService.confirmRequest(userId, args.token);
5161
+ });
5162
+ logger.debug("Token validation result", {
5163
+ tool: "remember_confirm",
5164
+ requestFound: !!request,
5165
+ action: request?.action
5166
+ });
5167
+ if (!request) {
5168
+ logger.info("Token invalid or expired", {
5169
+ tool: "remember_confirm",
5170
+ userId
5171
+ });
5172
+ return JSON.stringify(
5173
+ {
5174
+ success: false,
5175
+ error: "Invalid or expired token",
5176
+ message: "The confirmation token is invalid, expired, or has already been used."
5177
+ },
5178
+ null,
5179
+ 2
5180
+ );
5181
+ }
5182
+ logger.info("Executing confirmed action", {
5183
+ tool: "remember_confirm",
5184
+ action: request.action,
5185
+ userId
5186
+ });
5187
+ if (request.action === "publish_memory") {
5188
+ return await executePublishMemory(request, userId);
5189
+ }
5190
+ if (request.action === "delete_memory") {
5191
+ return await executeDeleteMemory(request, userId);
5192
+ }
5193
+ if (request.action === "retract_memory") {
5194
+ return await executeRetractMemory(request, userId);
5195
+ }
5196
+ if (request.action === "revise_memory") {
5197
+ return await executeReviseMemory(request, userId);
5198
+ }
5199
+ throw new Error(`Unknown action type: ${request.action}`);
5200
+ } catch (error) {
5201
+ debug.error("Tool failed", {
5202
+ error: error instanceof Error ? error.message : String(error),
5203
+ stack: error instanceof Error ? error.stack : void 0
5204
+ });
5205
+ handleToolError(error, {
5206
+ toolName: "remember_confirm",
5207
+ userId,
5208
+ operation: "confirm action",
5209
+ token: args.token
5210
+ });
5211
+ }
5212
+ }
5213
+ async function executePublishMemory(request, userId, authContext) {
5214
+ const debug = createDebugLogger({
5215
+ tool: "remember_confirm",
5216
+ userId,
5217
+ operation: "execute_publish"
5218
+ });
5219
+ try {
5220
+ const spaces = request.payload.spaces || [];
5221
+ const groups = request.payload.groups || [];
5222
+ debug.debug("Executing publish memory action", {
5223
+ memoryId: request.payload.memory_id,
5224
+ spaces,
5225
+ groups
5226
+ });
5227
+ logger.info("Executing publish memory action", {
5228
+ function: "executePublishMemory",
5229
+ userId,
5230
+ memoryId: request.payload.memory_id,
5231
+ spaces,
5232
+ groups,
5233
+ spaceCount: spaces.length,
5234
+ groupCount: groups.length
5235
+ });
5236
+ if (spaces.length === 0 && groups.length === 0) {
5237
+ return JSON.stringify(
5238
+ {
5239
+ success: false,
5240
+ error: "No destinations",
5241
+ message: "Must specify at least one space or group to publish to"
5242
+ },
5243
+ null,
5244
+ 2
5245
+ );
5246
+ }
5247
+ const weaviateClient = getWeaviateClient();
5248
+ const userCollectionName = getMemoryCollectionName(userId);
5249
+ const userCollection = weaviateClient.collections.get(userCollectionName);
5250
+ logger.debug("Fetching original memory", {
5251
+ function: "executePublishMemory",
5252
+ collectionName: userCollectionName,
5253
+ memoryId: request.payload.memory_id
5254
+ });
5255
+ const originalMemory = await debug.time("Fetch original memory", async () => {
5256
+ return await fetchMemoryWithAllProperties(
5257
+ userCollection,
5258
+ request.payload.memory_id
5259
+ );
5260
+ });
5261
+ logger.info("Original memory fetch result", {
5262
+ function: "executePublishMemory",
5263
+ found: !!originalMemory,
5264
+ memoryId: request.payload.memory_id,
5265
+ hasProperties: !!originalMemory?.properties,
5266
+ propertyCount: originalMemory?.properties ? Object.keys(originalMemory.properties).length : 0
5267
+ });
5268
+ if (!originalMemory) {
5269
+ logger.info("Original memory not found", {
5270
+ function: "executePublishMemory",
5271
+ memoryId: request.payload.memory_id
5272
+ });
5273
+ return JSON.stringify(
5274
+ {
5275
+ success: false,
5276
+ error: "Memory not found",
5277
+ message: `Original memory ${request.payload.memory_id} no longer exists`
5278
+ },
5279
+ null,
5280
+ 2
5281
+ );
5282
+ }
5283
+ if (originalMemory.properties.user_id !== userId) {
5284
+ logger.warn("Permission denied - wrong owner", {
5285
+ function: "executePublishMemory",
5286
+ memoryId: request.payload.memory_id,
5287
+ memoryOwner: originalMemory.properties.user_id,
5288
+ requestingUser: userId
5289
+ });
5290
+ return JSON.stringify(
5291
+ {
5292
+ success: false,
5293
+ error: "Permission denied",
5294
+ message: "You can only publish your own memories"
5295
+ },
5296
+ null,
5297
+ 2
5298
+ );
5299
+ }
5300
+ const compositeId = generateCompositeId(userId, request.payload.memory_id);
5301
+ logger.debug("Generated composite ID", {
5302
+ function: "executePublishMemory",
5303
+ compositeId,
5304
+ userId,
5305
+ memoryId: request.payload.memory_id
5306
+ });
5307
+ const existingSpaceIds = Array.isArray(originalMemory.properties.space_ids) ? originalMemory.properties.space_ids : [];
5308
+ const existingGroupIds = Array.isArray(originalMemory.properties.group_ids) ? originalMemory.properties.group_ids : [];
5309
+ const originalTags = Array.isArray(originalMemory.properties.tags) ? originalMemory.properties.tags : [];
5310
+ const additionalTags = Array.isArray(request.payload.additional_tags) ? request.payload.additional_tags : [];
5311
+ const mergedTags = [...originalTags, ...additionalTags];
5312
+ const publicationResults = { groups: [] };
5313
+ if (spaces.length > 0) {
5314
+ logger.debug("Ensuring public spaces collection exists", {
5315
+ function: "executePublishMemory"
5316
+ });
5317
+ const publicCollection = await ensurePublicCollection(weaviateClient);
5318
+ let existingSpaceMemory = null;
5319
+ try {
5320
+ existingSpaceMemory = await fetchMemoryWithAllProperties(publicCollection, compositeId);
5321
+ } catch (e) {
5322
+ }
5323
+ const newSpaceIds = [.../* @__PURE__ */ new Set([...existingSpaceIds, ...spaces])];
5324
+ let spaceModerationStatus = "approved";
5325
+ for (const spaceId of spaces) {
5326
+ const spaceConfig = await getSpaceConfig(spaceId, "space");
5327
+ if (spaceConfig.require_moderation) {
5328
+ spaceModerationStatus = "pending";
5329
+ break;
5330
+ }
5331
+ }
5332
+ const publishedMemory = {
5333
+ ...originalMemory.properties,
5334
+ // Use composite ID as the document ID
5335
+ id: compositeId,
5336
+ // Tracking arrays (v2 feature)
5337
+ space_ids: newSpaceIds,
5338
+ group_ids: existingGroupIds,
5339
+ // Legacy compatibility
5340
+ spaces,
5341
+ // Publication metadata
5342
+ author_id: userId,
5343
+ published_at: (/* @__PURE__ */ new Date()).toISOString(),
5344
+ discovery_count: 0,
5345
+ attribution: "user",
5346
+ // Moderation status
5347
+ moderation_status: spaceModerationStatus,
5348
+ // Merge tags
5349
+ tags: mergedTags
5350
+ };
5351
+ delete publishedMemory._additional;
5352
+ logger.info("Publishing memory to Memory_spaces_public", {
5353
+ function: "executePublishMemory",
5354
+ compositeId,
5355
+ spaces,
5356
+ spaceIds: newSpaceIds
5357
+ });
5358
+ try {
5359
+ if (existingSpaceMemory) {
5360
+ await publicCollection.data.update({
5361
+ id: compositeId,
5362
+ properties: publishedMemory
5363
+ });
5364
+ publicationResults.spaces = { success: true, id: compositeId };
5365
+ } else {
5366
+ await publicCollection.data.insert({
5367
+ id: compositeId,
5368
+ properties: publishedMemory
5369
+ });
5370
+ publicationResults.spaces = { success: true, id: compositeId };
5371
+ }
5372
+ logger.info("Memory published to spaces successfully", {
5373
+ function: "executePublishMemory",
5374
+ compositeId,
5375
+ spaces
5376
+ });
5377
+ } catch (spaceError) {
5378
+ logger.error("Failed to publish to spaces", {
5379
+ function: "executePublishMemory",
5380
+ error: spaceError instanceof Error ? spaceError.message : String(spaceError)
5381
+ });
5382
+ publicationResults.spaces = {
5383
+ success: false,
5384
+ error: spaceError instanceof Error ? spaceError.message : String(spaceError)
5385
+ };
5386
+ }
5387
+ }
5388
+ for (const groupId of groups) {
5389
+ const groupCollectionName = getCollectionName("GROUPS" /* GROUPS */, groupId);
5390
+ logger.debug("Publishing to group collection", {
5391
+ function: "executePublishMemory",
5392
+ groupId,
5393
+ collectionName: groupCollectionName
5394
+ });
5395
+ try {
5396
+ const groupCollection = weaviateClient.collections.get(groupCollectionName);
5397
+ let existingGroupMemory = null;
5398
+ try {
5399
+ existingGroupMemory = await fetchMemoryWithAllProperties(groupCollection, compositeId);
5400
+ } catch (e) {
5401
+ }
5402
+ const newGroupIds = [.../* @__PURE__ */ new Set([...existingGroupIds, groupId])];
5403
+ const groupConfig = await getSpaceConfig(groupId, "group");
5404
+ const groupModerationStatus = groupConfig.require_moderation ? "pending" : "approved";
5405
+ const groupMemory = {
5406
+ ...originalMemory.properties,
5407
+ // Use composite ID
5408
+ id: compositeId,
5409
+ // Tracking arrays
5410
+ space_ids: existingSpaceIds,
5411
+ group_ids: newGroupIds,
5412
+ // Publication metadata
5413
+ author_id: userId,
5414
+ published_at: (/* @__PURE__ */ new Date()).toISOString(),
5415
+ discovery_count: 0,
5416
+ attribution: "user",
5417
+ // Moderation status
5418
+ moderation_status: groupModerationStatus,
5419
+ // Merge tags
5420
+ tags: mergedTags
5421
+ };
5422
+ delete groupMemory._additional;
5423
+ if (existingGroupMemory) {
5424
+ await groupCollection.data.update({
5425
+ id: compositeId,
5426
+ properties: groupMemory
5427
+ });
5428
+ } else {
5429
+ await groupCollection.data.insert({
5430
+ id: compositeId,
5431
+ properties: groupMemory
5432
+ });
5433
+ }
5434
+ publicationResults.groups.push({ groupId, success: true, id: compositeId });
5435
+ logger.info("Memory published to group successfully", {
5436
+ function: "executePublishMemory",
5437
+ compositeId,
5438
+ groupId
5439
+ });
5440
+ } catch (groupError) {
5441
+ logger.error("Failed to publish to group", {
5442
+ function: "executePublishMemory",
5443
+ groupId,
5444
+ error: groupError instanceof Error ? groupError.message : String(groupError)
5445
+ });
5446
+ publicationResults.groups.push({
5447
+ groupId,
5448
+ success: false,
5449
+ error: groupError instanceof Error ? groupError.message : String(groupError)
5450
+ });
5451
+ }
5452
+ }
5453
+ const finalSpaceIds = publicationResults.spaces?.success ? [.../* @__PURE__ */ new Set([...existingSpaceIds, ...spaces])] : existingSpaceIds;
5454
+ const successfulGroups = publicationResults.groups.filter((g) => g.success).map((g) => g.groupId);
5455
+ const finalGroupIds = [.../* @__PURE__ */ new Set([...existingGroupIds, ...successfulGroups])];
5456
+ if (finalSpaceIds.length > existingSpaceIds.length || finalGroupIds.length > existingGroupIds.length) {
5457
+ try {
5458
+ await userCollection.data.update({
5459
+ id: request.payload.memory_id,
5460
+ properties: {
5461
+ space_ids: finalSpaceIds,
5462
+ group_ids: finalGroupIds
5463
+ }
5464
+ });
5465
+ logger.info("Updated source memory with tracking arrays", {
5466
+ function: "executePublishMemory",
5467
+ memoryId: request.payload.memory_id,
5468
+ spaceIds: finalSpaceIds,
5469
+ groupIds: finalGroupIds
5470
+ });
5471
+ } catch (updateError) {
5472
+ logger.warn("Failed to update source memory tracking arrays", {
5473
+ function: "executePublishMemory",
5474
+ memoryId: request.payload.memory_id,
5475
+ error: updateError instanceof Error ? updateError.message : String(updateError)
5476
+ });
5477
+ }
5478
+ }
5479
+ const successfulPublications = [];
5480
+ const failedPublications = [];
5481
+ if (spaces.length > 0) {
5482
+ if (publicationResults.spaces?.success) {
5483
+ successfulPublications.push(`spaces: ${spaces.join(", ")}`);
5484
+ } else {
5485
+ failedPublications.push(`spaces: ${publicationResults.spaces?.error || "unknown error"}`);
5486
+ }
5487
+ }
5488
+ for (const groupResult of publicationResults.groups) {
5489
+ if (groupResult.success) {
5490
+ successfulPublications.push(`group: ${groupResult.groupId}`);
5491
+ } else {
5492
+ failedPublications.push(`group ${groupResult.groupId}: ${groupResult.error || "unknown error"}`);
5493
+ }
5494
+ }
5495
+ if (successfulPublications.length > 0) {
5496
+ return JSON.stringify(
5497
+ {
5498
+ success: true,
5499
+ composite_id: compositeId,
5500
+ published_to: successfulPublications,
5501
+ failed: failedPublications.length > 0 ? failedPublications : void 0,
5502
+ space_ids: finalSpaceIds,
5503
+ group_ids: finalGroupIds
5504
+ },
5505
+ null,
5506
+ 2
5507
+ );
5508
+ } else {
5509
+ return JSON.stringify(
5510
+ {
5511
+ success: false,
5512
+ error: "Publication failed",
5513
+ message: "Failed to publish to any destination",
5514
+ details: failedPublications
5515
+ },
5516
+ null,
5517
+ 2
5518
+ );
5519
+ }
5520
+ } catch (error) {
5521
+ debug.error("Execute publish failed", {
5522
+ error: error instanceof Error ? error.message : String(error),
5523
+ stack: error instanceof Error ? error.stack : void 0
5524
+ });
5525
+ handleToolError(error, {
5526
+ toolName: "remember_confirm",
5527
+ userId,
5528
+ operation: "execute publish_memory",
5529
+ action: "publish_memory"
5530
+ });
5531
+ }
5532
+ }
5533
+ async function executeDeleteMemory(request, userId, authContext) {
5534
+ try {
5535
+ logger.info("Executing delete memory action", {
5536
+ function: "executeDeleteMemory",
5537
+ userId,
5538
+ memoryId: request.payload.memory_id,
4755
5539
  hasReason: !!request.payload.reason
4756
5540
  });
4757
5541
  const { memory_id, reason } = request.payload;
4758
5542
  const client2 = getWeaviateClient();
4759
- const collectionName = `Memory_${sanitizeUserId(userId)}`;
5543
+ const collectionName = getMemoryCollectionName(userId);
4760
5544
  const collection = client2.collections.get(collectionName);
4761
5545
  await collection.data.update({
4762
5546
  id: memory_id,
@@ -4792,6 +5576,405 @@ async function executeDeleteMemory(request, userId) {
4792
5576
  throw error;
4793
5577
  }
4794
5578
  }
5579
+ async function executeRetractMemory(request, userId, authContext) {
5580
+ const debug = createDebugLogger({
5581
+ tool: "remember_confirm",
5582
+ userId,
5583
+ operation: "execute_retract"
5584
+ });
5585
+ try {
5586
+ const spaces = request.payload.spaces || [];
5587
+ const groups = request.payload.groups || [];
5588
+ debug.debug("Executing retract memory action", {
5589
+ memoryId: request.payload.memory_id,
5590
+ spaces,
5591
+ groups
5592
+ });
5593
+ logger.info("Executing retract memory action", {
5594
+ function: "executeRetractMemory",
5595
+ userId,
5596
+ memoryId: request.payload.memory_id,
5597
+ spaces,
5598
+ groups,
5599
+ spaceCount: spaces.length,
5600
+ groupCount: groups.length
5601
+ });
5602
+ const weaviateClient = getWeaviateClient();
5603
+ const userCollectionName = getMemoryCollectionName(userId);
5604
+ const userCollection = weaviateClient.collections.get(userCollectionName);
5605
+ const sourceMemory = await fetchMemoryWithAllProperties(
5606
+ userCollection,
5607
+ request.payload.memory_id
5608
+ );
5609
+ if (!sourceMemory) {
5610
+ logger.info("Source memory not found for retraction", {
5611
+ function: "executeRetractMemory",
5612
+ memoryId: request.payload.memory_id
5613
+ });
5614
+ return JSON.stringify(
5615
+ {
5616
+ success: false,
5617
+ error: "Memory not found",
5618
+ message: `Source memory ${request.payload.memory_id} no longer exists`
5619
+ },
5620
+ null,
5621
+ 2
5622
+ );
5623
+ }
5624
+ const currentSpaceIds = Array.isArray(sourceMemory.properties.space_ids) ? sourceMemory.properties.space_ids : [];
5625
+ const currentGroupIds = Array.isArray(sourceMemory.properties.group_ids) ? sourceMemory.properties.group_ids : [];
5626
+ const compositeId = generateCompositeId(userId, request.payload.memory_id);
5627
+ const retractionResults = { groups: [] };
5628
+ if (spaces.length > 0) {
5629
+ try {
5630
+ const publicCollection = weaviateClient.collections.get(
5631
+ getCollectionName("SPACES" /* SPACES */)
5632
+ );
5633
+ const publishedMemory = await fetchMemoryWithAllProperties(publicCollection, compositeId);
5634
+ if (publishedMemory) {
5635
+ const newSpaceIds = currentSpaceIds.filter((id) => !spaces.includes(id));
5636
+ await publicCollection.data.update({
5637
+ id: compositeId,
5638
+ properties: {
5639
+ space_ids: newSpaceIds,
5640
+ retracted_at: (/* @__PURE__ */ new Date()).toISOString()
5641
+ }
5642
+ });
5643
+ retractionResults.spaces = { success: true };
5644
+ logger.info("Memory retracted from spaces (orphaned)", {
5645
+ function: "executeRetractMemory",
5646
+ compositeId,
5647
+ retractedSpaces: spaces,
5648
+ remainingSpaces: newSpaceIds,
5649
+ isOrphaned: newSpaceIds.length === 0 && currentGroupIds.length === 0
5650
+ });
5651
+ } else {
5652
+ retractionResults.spaces = {
5653
+ success: false,
5654
+ error: "Memory not found in spaces collection"
5655
+ };
5656
+ logger.warn("Memory not found in spaces collection", {
5657
+ function: "executeRetractMemory",
5658
+ compositeId
5659
+ });
5660
+ }
5661
+ } catch (spaceError) {
5662
+ logger.error("Failed to retract from spaces", {
5663
+ function: "executeRetractMemory",
5664
+ error: spaceError instanceof Error ? spaceError.message : String(spaceError)
5665
+ });
5666
+ retractionResults.spaces = {
5667
+ success: false,
5668
+ error: spaceError instanceof Error ? spaceError.message : String(spaceError)
5669
+ };
5670
+ }
5671
+ }
5672
+ for (const groupId of groups) {
5673
+ const groupCollectionName = getCollectionName("GROUPS" /* GROUPS */, groupId);
5674
+ try {
5675
+ const groupCollection = weaviateClient.collections.get(groupCollectionName);
5676
+ const groupMemory = await fetchMemoryWithAllProperties(groupCollection, compositeId);
5677
+ if (groupMemory) {
5678
+ const groupMemoryGroupIds = Array.isArray(groupMemory.properties.group_ids) ? groupMemory.properties.group_ids : [];
5679
+ const newGroupIds = groupMemoryGroupIds.filter((id) => id !== groupId);
5680
+ await groupCollection.data.update({
5681
+ id: compositeId,
5682
+ properties: {
5683
+ group_ids: newGroupIds,
5684
+ retracted_at: (/* @__PURE__ */ new Date()).toISOString()
5685
+ }
5686
+ });
5687
+ retractionResults.groups.push({ groupId, success: true });
5688
+ logger.info("Memory retracted from group (orphaned)", {
5689
+ function: "executeRetractMemory",
5690
+ compositeId,
5691
+ groupId,
5692
+ remainingGroups: newGroupIds,
5693
+ isOrphaned: newGroupIds.length === 0
5694
+ });
5695
+ } else {
5696
+ retractionResults.groups.push({
5697
+ groupId,
5698
+ success: false,
5699
+ error: "Memory not found in group"
5700
+ });
5701
+ logger.warn("Memory not found in group collection", {
5702
+ function: "executeRetractMemory",
5703
+ compositeId,
5704
+ groupId
5705
+ });
5706
+ }
5707
+ } catch (groupError) {
5708
+ logger.error("Failed to retract from group", {
5709
+ function: "executeRetractMemory",
5710
+ groupId,
5711
+ error: groupError instanceof Error ? groupError.message : String(groupError)
5712
+ });
5713
+ retractionResults.groups.push({
5714
+ groupId,
5715
+ success: false,
5716
+ error: groupError instanceof Error ? groupError.message : String(groupError)
5717
+ });
5718
+ }
5719
+ }
5720
+ const finalSpaceIds = retractionResults.spaces?.success ? currentSpaceIds.filter((id) => !spaces.includes(id)) : currentSpaceIds;
5721
+ const successfulGroupRetractions = retractionResults.groups.filter((g) => g.success).map((g) => g.groupId);
5722
+ const finalGroupIds = currentGroupIds.filter((id) => !successfulGroupRetractions.includes(id));
5723
+ try {
5724
+ await userCollection.data.update({
5725
+ id: request.payload.memory_id,
5726
+ properties: {
5727
+ space_ids: finalSpaceIds,
5728
+ group_ids: finalGroupIds
5729
+ }
5730
+ });
5731
+ logger.info("Updated source memory tracking arrays after retraction", {
5732
+ function: "executeRetractMemory",
5733
+ memoryId: request.payload.memory_id,
5734
+ spaceIds: finalSpaceIds,
5735
+ groupIds: finalGroupIds
5736
+ });
5737
+ } catch (updateError) {
5738
+ logger.warn("Failed to update source memory tracking arrays after retraction", {
5739
+ function: "executeRetractMemory",
5740
+ memoryId: request.payload.memory_id,
5741
+ error: updateError instanceof Error ? updateError.message : String(updateError)
5742
+ });
5743
+ }
5744
+ const successfulRetractions = [];
5745
+ const failedRetractions = [];
5746
+ if (spaces.length > 0) {
5747
+ if (retractionResults.spaces?.success) {
5748
+ successfulRetractions.push(`spaces: ${spaces.join(", ")}`);
5749
+ } else {
5750
+ failedRetractions.push(`spaces: ${retractionResults.spaces?.error || "unknown error"}`);
5751
+ }
5752
+ }
5753
+ for (const groupResult of retractionResults.groups) {
5754
+ if (groupResult.success) {
5755
+ successfulRetractions.push(`group: ${groupResult.groupId}`);
5756
+ } else {
5757
+ failedRetractions.push(`group ${groupResult.groupId}: ${groupResult.error || "unknown error"}`);
5758
+ }
5759
+ }
5760
+ if (successfulRetractions.length > 0) {
5761
+ return JSON.stringify(
5762
+ {
5763
+ success: true,
5764
+ composite_id: compositeId,
5765
+ retracted_from: successfulRetractions,
5766
+ failed: failedRetractions.length > 0 ? failedRetractions : void 0,
5767
+ space_ids: finalSpaceIds,
5768
+ group_ids: finalGroupIds,
5769
+ is_orphaned: finalSpaceIds.length === 0 && finalGroupIds.length === 0
5770
+ },
5771
+ null,
5772
+ 2
5773
+ );
5774
+ } else {
5775
+ return JSON.stringify(
5776
+ {
5777
+ success: false,
5778
+ error: "Retraction failed",
5779
+ message: "Failed to retract from any destination",
5780
+ details: failedRetractions
5781
+ },
5782
+ null,
5783
+ 2
5784
+ );
5785
+ }
5786
+ } catch (error) {
5787
+ debug.error("Execute retract failed", {
5788
+ error: error instanceof Error ? error.message : String(error),
5789
+ stack: error instanceof Error ? error.stack : void 0
5790
+ });
5791
+ handleToolError(error, {
5792
+ toolName: "remember_confirm",
5793
+ userId,
5794
+ operation: "execute retract_memory",
5795
+ action: "retract_memory"
5796
+ });
5797
+ }
5798
+ }
5799
+ async function executeReviseMemory(request, userId, authContext) {
5800
+ const debug = createDebugLogger({
5801
+ tool: "remember_confirm",
5802
+ userId,
5803
+ operation: "execute_revise"
5804
+ });
5805
+ try {
5806
+ const { memory_id, space_ids = [], group_ids = [] } = request.payload;
5807
+ debug.debug("Executing revise memory action", {
5808
+ memoryId: memory_id,
5809
+ spaceIds: space_ids,
5810
+ groupIds: group_ids
5811
+ });
5812
+ logger.info("Executing revise memory action", {
5813
+ function: "executeReviseMemory",
5814
+ userId,
5815
+ memoryId: memory_id,
5816
+ spaceCount: space_ids.length,
5817
+ groupCount: group_ids.length
5818
+ });
5819
+ const weaviateClient = getWeaviateClient();
5820
+ const userCollectionName = getMemoryCollectionName(userId);
5821
+ const userCollection = weaviateClient.collections.get(userCollectionName);
5822
+ const sourceMemory = await fetchMemoryWithAllProperties(
5823
+ userCollection,
5824
+ memory_id
5825
+ );
5826
+ if (!sourceMemory) {
5827
+ return JSON.stringify(
5828
+ {
5829
+ success: false,
5830
+ error: "Memory not found",
5831
+ message: `Source memory ${memory_id} no longer exists`
5832
+ },
5833
+ null,
5834
+ 2
5835
+ );
5836
+ }
5837
+ if (sourceMemory.properties.user_id !== userId) {
5838
+ return JSON.stringify(
5839
+ {
5840
+ success: false,
5841
+ error: "Permission denied",
5842
+ message: "You can only revise your own memories"
5843
+ },
5844
+ null,
5845
+ 2
5846
+ );
5847
+ }
5848
+ const newContent = String(sourceMemory.properties.content ?? "");
5849
+ const revisedAt = (/* @__PURE__ */ new Date()).toISOString();
5850
+ const compositeId = generateCompositeId(userId, memory_id);
5851
+ const results = [];
5852
+ logger.info("Revising published copies", {
5853
+ function: "executeReviseMemory",
5854
+ compositeId,
5855
+ spaceCount: space_ids.length > 0 ? 1 : 0,
5856
+ groupCount: group_ids.length
5857
+ });
5858
+ async function reviseInCollection(collectionName, locationLabel) {
5859
+ try {
5860
+ const collection = weaviateClient.collections.get(collectionName);
5861
+ const publishedMemory = await fetchMemoryWithAllProperties(
5862
+ collection,
5863
+ compositeId
5864
+ );
5865
+ if (!publishedMemory) {
5866
+ results.push({
5867
+ location: locationLabel,
5868
+ status: "skipped",
5869
+ error: "Published copy not found (may have been deleted)"
5870
+ });
5871
+ logger.warn("Published copy not found in collection", {
5872
+ function: "executeReviseMemory",
5873
+ collectionName,
5874
+ compositeId
5875
+ });
5876
+ return;
5877
+ }
5878
+ const oldContent = String(publishedMemory.properties.content ?? "");
5879
+ let revisionHistory = parseRevisionHistory(
5880
+ publishedMemory.properties.revision_history
5881
+ );
5882
+ if (oldContent !== newContent) {
5883
+ revisionHistory = buildRevisionHistory(
5884
+ revisionHistory,
5885
+ oldContent,
5886
+ revisedAt
5887
+ );
5888
+ }
5889
+ const currentRevisionCount = typeof publishedMemory.properties.revision_count === "number" ? publishedMemory.properties.revision_count : 0;
5890
+ await collection.data.update({
5891
+ id: compositeId,
5892
+ properties: {
5893
+ content: newContent,
5894
+ revised_at: revisedAt,
5895
+ revision_count: currentRevisionCount + 1,
5896
+ revision_history: JSON.stringify(revisionHistory)
5897
+ }
5898
+ });
5899
+ results.push({ location: locationLabel, status: "success" });
5900
+ logger.info("Revised published memory in collection", {
5901
+ function: "executeReviseMemory",
5902
+ collectionName,
5903
+ compositeId,
5904
+ revisionCount: currentRevisionCount + 1,
5905
+ contentChanged: oldContent !== newContent
5906
+ });
5907
+ } catch (err) {
5908
+ results.push({
5909
+ location: locationLabel,
5910
+ status: "failed",
5911
+ error: err instanceof Error ? err.message : String(err)
5912
+ });
5913
+ logger.error("Failed to revise in collection", {
5914
+ function: "executeReviseMemory",
5915
+ collectionName,
5916
+ compositeId,
5917
+ error: err instanceof Error ? err.message : String(err)
5918
+ });
5919
+ }
5920
+ }
5921
+ if (space_ids.length > 0) {
5922
+ await reviseInCollection(
5923
+ getCollectionName("SPACES" /* SPACES */),
5924
+ "Memory_spaces_public"
5925
+ );
5926
+ }
5927
+ for (const groupId of group_ids) {
5928
+ await reviseInCollection(
5929
+ getCollectionName("GROUPS" /* GROUPS */, groupId),
5930
+ `Memory_groups_${groupId}`
5931
+ );
5932
+ }
5933
+ const successCount = results.filter((r) => r.status === "success").length;
5934
+ const failedCount = results.filter((r) => r.status === "failed").length;
5935
+ const skippedCount = results.filter((r) => r.status === "skipped").length;
5936
+ logger.info("Revise execution complete", {
5937
+ function: "executeReviseMemory",
5938
+ userId,
5939
+ memoryId: memory_id,
5940
+ successCount,
5941
+ failedCount,
5942
+ skippedCount
5943
+ });
5944
+ return JSON.stringify(
5945
+ {
5946
+ success: successCount > 0,
5947
+ composite_id: compositeId,
5948
+ revised_at: revisedAt,
5949
+ summary: {
5950
+ total: results.length,
5951
+ success: successCount,
5952
+ failed: failedCount,
5953
+ skipped: skippedCount
5954
+ },
5955
+ results,
5956
+ ...failedCount > 0 ? {
5957
+ warnings: [
5958
+ `Failed to revise ${failedCount} of ${results.length} location(s)`
5959
+ ]
5960
+ } : {}
5961
+ },
5962
+ null,
5963
+ 2
5964
+ );
5965
+ } catch (error) {
5966
+ debug.error("Execute revise failed", {
5967
+ error: error instanceof Error ? error.message : String(error),
5968
+ stack: error instanceof Error ? error.stack : void 0
5969
+ });
5970
+ handleToolError(error, {
5971
+ toolName: "remember_confirm",
5972
+ userId,
5973
+ operation: "execute revise_memory",
5974
+ action: "revise_memory"
5975
+ });
5976
+ }
5977
+ }
4795
5978
 
4796
5979
  // src/tools/deny.ts
4797
5980
  var denyTool = {
@@ -4818,8 +6001,11 @@ This ensures proper user consent workflow is followed.`,
4818
6001
  required: ["token"]
4819
6002
  }
4820
6003
  };
4821
- async function handleDeny(args, userId) {
6004
+ async function handleDeny(args, userId, authContext) {
6005
+ const debug = createDebugLogger({ tool: "remember_deny", userId, operation: "deny action" });
4822
6006
  try {
6007
+ debug.info("Tool invoked");
6008
+ debug.trace("Arguments", { args });
4823
6009
  const success = await confirmationTokenService.denyRequest(userId, args.token);
4824
6010
  if (!success) {
4825
6011
  return JSON.stringify(
@@ -4840,6 +6026,7 @@ async function handleDeny(args, userId) {
4840
6026
  2
4841
6027
  );
4842
6028
  } catch (error) {
6029
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
4843
6030
  handleToolError(error, {
4844
6031
  toolName: "remember_deny",
4845
6032
  userId,
@@ -4852,9 +6039,32 @@ async function handleDeny(args, userId) {
4852
6039
  // src/tools/search-space.ts
4853
6040
  import { Filters as Filters4 } from "weaviate-client";
4854
6041
  init_space_memory();
6042
+ init_logger();
6043
+
6044
+ // src/utils/auth-helpers.ts
6045
+ function canModerate(authContext, groupId) {
6046
+ if (!authContext?.credentials)
6047
+ return false;
6048
+ const membership = authContext.credentials.group_memberships.find((m) => m.group_id === groupId);
6049
+ return membership?.permissions.can_moderate ?? false;
6050
+ }
6051
+ function canModerateAny(authContext) {
6052
+ if (!authContext?.credentials)
6053
+ return false;
6054
+ return authContext.credentials.group_memberships.some((m) => m.permissions.can_moderate);
6055
+ }
6056
+
6057
+ // src/tools/search-space.ts
4855
6058
  var searchSpaceTool = {
4856
6059
  name: "remember_search_space",
4857
- description: `Search one or more shared spaces to discover thoughts, ideas, and memories. By default, excludes comments to keep discovery clean. Set include_comments: true to include threaded discussions. Can search multiple spaces in a single query.
6060
+ description: `Search shared spaces and/or groups to discover memories from other users.
6061
+
6062
+ Destinations:
6063
+ - Spaces: Public shared areas (e.g., "the_void", "dogs") \u2014 queries Memory_spaces_public filtered by space_ids
6064
+ - Groups: Private group collections \u2014 queries Memory_groups_{groupId} for each specified group
6065
+ - Neither specified: Searches all public memories across Memory_spaces_public
6066
+
6067
+ Results from multiple sources are merged and deduplicated by composite ID, sorted by relevance.
4858
6068
 
4859
6069
  \u26A0\uFE0F **CRITICAL - CONTENT TYPE FILTERING**: Do NOT add content_type filter unless the user explicitly requests filtering by type.
4860
6070
  - \u2705 CORRECT: User says "search The Void for hiking" \u2192 { spaces: ["the_void"], query: "hiking" }
@@ -4867,7 +6077,7 @@ Let the search algorithm find ALL relevant memories regardless of type unless ex
4867
6077
  properties: {
4868
6078
  query: {
4869
6079
  type: "string",
4870
- description: "Search query (semantic + keyword hybrid)"
6080
+ description: "Search query"
4871
6081
  },
4872
6082
  spaces: {
4873
6083
  type: "array",
@@ -4875,9 +6085,20 @@ Let the search algorithm find ALL relevant memories regardless of type unless ex
4875
6085
  type: "string",
4876
6086
  enum: SUPPORTED_SPACES
4877
6087
  },
4878
- description: 'Spaces to search (e.g., ["the_void", "dogs"]). Can search multiple spaces at once.',
4879
- minItems: 1,
4880
- default: ["the_void"]
6088
+ description: 'Spaces to search (e.g., ["the_void", "dogs"]). Omit to search all public spaces.',
6089
+ minItems: 1
6090
+ },
6091
+ groups: {
6092
+ type: "array",
6093
+ items: { type: "string" },
6094
+ description: 'Group IDs to search (e.g., ["group-123"]). Searches Memory_groups_{groupId} for each.',
6095
+ minItems: 1
6096
+ },
6097
+ search_type: {
6098
+ type: "string",
6099
+ enum: ["hybrid", "bm25", "semantic"],
6100
+ description: 'Search algorithm: "hybrid" (default), "bm25" (keyword only), or "semantic" (vector only)',
6101
+ default: "hybrid"
4881
6102
  },
4882
6103
  content_type: {
4883
6104
  type: "string",
@@ -4908,6 +6129,12 @@ Let the search algorithm find ALL relevant memories regardless of type unless ex
4908
6129
  type: "string",
4909
6130
  description: "Filter memories created before this date (ISO 8601)"
4910
6131
  },
6132
+ moderation_filter: {
6133
+ type: "string",
6134
+ enum: ["approved", "pending", "rejected", "removed", "all"],
6135
+ description: 'Filter by moderation status. Default: "approved" (only shows approved/unmoderated). Non-approved filters require moderator permissions.',
6136
+ default: "approved"
6137
+ },
4911
6138
  include_comments: {
4912
6139
  type: "boolean",
4913
6140
  description: "Include comments in search results (default: false)",
@@ -4924,10 +6151,73 @@ Let the search algorithm find ALL relevant memories regardless of type unless ex
4924
6151
  description: "Offset for pagination"
4925
6152
  }
4926
6153
  },
4927
- required: ["query", "spaces"]
6154
+ required: ["query"]
6155
+ }
6156
+ };
6157
+ function buildModerationFilter(collection, moderationFilter = "approved") {
6158
+ if (moderationFilter === "all") {
6159
+ return null;
6160
+ }
6161
+ if (moderationFilter === "approved") {
6162
+ return Filters4.or(
6163
+ collection.filter.byProperty("moderation_status").equal("approved"),
6164
+ collection.filter.byProperty("moderation_status").isNull(true)
6165
+ );
6166
+ }
6167
+ return collection.filter.byProperty("moderation_status").equal(moderationFilter);
6168
+ }
6169
+ function buildBaseFilters(collection, args) {
6170
+ const filterList = [];
6171
+ filterList.push(collection.filter.byProperty("deleted_at").isNull(true));
6172
+ filterList.push(collection.filter.byProperty("doc_type").equal("memory"));
6173
+ const moderationFilter = buildModerationFilter(collection, args.moderation_filter);
6174
+ if (moderationFilter) {
6175
+ filterList.push(moderationFilter);
6176
+ }
6177
+ if (args.content_type) {
6178
+ filterList.push(collection.filter.byProperty("content_type").equal(args.content_type));
6179
+ }
6180
+ if (!args.include_comments && !args.content_type) {
6181
+ filterList.push(collection.filter.byProperty("content_type").notEqual("comment"));
6182
+ }
6183
+ if (!args.content_type) {
6184
+ filterList.push(collection.filter.byProperty("content_type").notEqual("ghost"));
6185
+ }
6186
+ if (args.tags && args.tags.length > 0) {
6187
+ args.tags.forEach((tag) => {
6188
+ filterList.push(collection.filter.byProperty("tags").containsAny([tag]));
6189
+ });
6190
+ }
6191
+ if (args.min_weight !== void 0) {
6192
+ filterList.push(collection.filter.byProperty("weight").greaterOrEqual(args.min_weight));
6193
+ }
6194
+ if (args.max_weight !== void 0) {
6195
+ filterList.push(collection.filter.byProperty("weight").lessOrEqual(args.max_weight));
6196
+ }
6197
+ if (args.date_from) {
6198
+ filterList.push(collection.filter.byProperty("created_at").greaterOrEqual(new Date(args.date_from)));
6199
+ }
6200
+ if (args.date_to) {
6201
+ filterList.push(collection.filter.byProperty("created_at").lessOrEqual(new Date(args.date_to)));
4928
6202
  }
4929
- };
4930
- async function handleSearchSpace(args, userId) {
6203
+ return filterList;
6204
+ }
6205
+ async function executeSearch(collection, query, searchType, whereFilter, limit) {
6206
+ const opts = {
6207
+ limit,
6208
+ ...whereFilter && { where: whereFilter }
6209
+ };
6210
+ switch (searchType) {
6211
+ case "bm25":
6212
+ return (await collection.query.bm25(query, opts)).objects;
6213
+ case "semantic":
6214
+ return (await collection.query.nearText([query], opts)).objects;
6215
+ case "hybrid":
6216
+ default:
6217
+ return (await collection.query.hybrid(query, opts)).objects;
6218
+ }
6219
+ }
6220
+ async function handleSearchSpace(args, userId, authContext) {
4931
6221
  const debug = createDebugLogger({
4932
6222
  tool: "remember_search_space",
4933
6223
  userId,
@@ -4936,96 +6226,167 @@ async function handleSearchSpace(args, userId) {
4936
6226
  try {
4937
6227
  debug.info("Tool invoked");
4938
6228
  debug.trace("Arguments", { args });
4939
- debug.debug("Validating space IDs", { spaces: args.spaces });
4940
- const invalidSpaces = args.spaces.filter((s) => !isValidSpaceId(s));
4941
- if (invalidSpaces.length > 0) {
4942
- return JSON.stringify(
4943
- {
4944
- success: false,
4945
- error: "Invalid space IDs",
4946
- message: `Invalid spaces: ${invalidSpaces.join(", ")}. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`,
4947
- context: {
4948
- invalid_spaces: invalidSpaces,
4949
- provided_spaces: args.spaces,
4950
- supported_spaces: SUPPORTED_SPACES
4951
- }
4952
- },
4953
- null,
4954
- 2
4955
- );
4956
- }
4957
- if (args.spaces.length === 0) {
4958
- return JSON.stringify(
4959
- {
4960
- success: false,
4961
- error: "Empty spaces array",
4962
- message: "Must specify at least one space to search"
4963
- },
4964
- null,
4965
- 2
4966
- );
6229
+ const spaces = args.spaces || [];
6230
+ const groups = args.groups || [];
6231
+ const searchType = args.search_type || "hybrid";
6232
+ const limit = args.limit || 10;
6233
+ const offset = args.offset || 0;
6234
+ if (spaces.length > 0) {
6235
+ const invalidSpaces = spaces.filter((s) => !isValidSpaceId(s));
6236
+ if (invalidSpaces.length > 0) {
6237
+ return JSON.stringify(
6238
+ {
6239
+ success: false,
6240
+ error: "Invalid space IDs",
6241
+ message: `Invalid spaces: ${invalidSpaces.join(", ")}. Supported spaces: ${SUPPORTED_SPACES.join(", ")}`,
6242
+ context: {
6243
+ invalid_spaces: invalidSpaces,
6244
+ provided_spaces: spaces,
6245
+ supported_spaces: SUPPORTED_SPACES
6246
+ }
6247
+ },
6248
+ null,
6249
+ 2
6250
+ );
6251
+ }
4967
6252
  }
4968
- const weaviateClient = getWeaviateClient();
4969
- const publicCollection = await ensurePublicCollection(weaviateClient);
4970
- const filterList = [];
4971
- filterList.push(publicCollection.filter.byProperty("spaces").containsAny(args.spaces));
4972
- filterList.push(publicCollection.filter.byProperty("doc_type").equal("memory"));
4973
- if (args.content_type) {
4974
- filterList.push(publicCollection.filter.byProperty("type").equal(args.content_type));
6253
+ if (groups.length > 0) {
6254
+ const invalidGroups = groups.filter((g) => !g || g.includes(".") || g.trim() === "");
6255
+ if (invalidGroups.length > 0) {
6256
+ return JSON.stringify(
6257
+ {
6258
+ success: false,
6259
+ error: "Invalid group IDs",
6260
+ message: "Group IDs cannot be empty or contain dots",
6261
+ context: { invalid_groups: invalidGroups }
6262
+ },
6263
+ null,
6264
+ 2
6265
+ );
6266
+ }
4975
6267
  }
4976
- if (!args.include_comments && !args.content_type) {
4977
- filterList.push(publicCollection.filter.byProperty("type").notEqual("comment"));
6268
+ const moderationFilter = args.moderation_filter || "approved";
6269
+ if (moderationFilter !== "approved") {
6270
+ for (const groupId of groups) {
6271
+ if (!canModerate(authContext, groupId)) {
6272
+ return JSON.stringify(
6273
+ {
6274
+ success: false,
6275
+ error: "Permission denied",
6276
+ message: `Moderator access required to view ${moderationFilter} memories in group ${groupId}`
6277
+ },
6278
+ null,
6279
+ 2
6280
+ );
6281
+ }
6282
+ }
6283
+ if ((spaces.length > 0 || groups.length === 0) && !canModerateAny(authContext)) {
6284
+ return JSON.stringify(
6285
+ {
6286
+ success: false,
6287
+ error: "Permission denied",
6288
+ message: `Moderator access required to view ${moderationFilter} memories in spaces`
6289
+ },
6290
+ null,
6291
+ 2
6292
+ );
6293
+ }
4978
6294
  }
4979
- if (args.tags && args.tags.length > 0) {
4980
- args.tags.forEach((tag) => {
4981
- filterList.push(publicCollection.filter.byProperty("tags").containsAny([tag]));
6295
+ const weaviateClient = getWeaviateClient();
6296
+ const fetchLimit = (limit + offset) * Math.max(1, groups.length + (spaces.length > 0 || groups.length === 0 ? 1 : 0));
6297
+ const allObjects = [];
6298
+ logger.info("Starting space/group search", {
6299
+ tool: "remember_search_space",
6300
+ userId,
6301
+ spaces,
6302
+ groups,
6303
+ searchType,
6304
+ query: args.query
6305
+ });
6306
+ if (spaces.length > 0 || groups.length === 0) {
6307
+ const spacesCollectionName = getCollectionName("SPACES" /* SPACES */);
6308
+ const spacesCollection = weaviateClient.collections.get(spacesCollectionName);
6309
+ const filterList = buildBaseFilters(spacesCollection, args);
6310
+ if (spaces.length > 0) {
6311
+ filterList.push(spacesCollection.filter.byProperty("space_ids").containsAny(spaces));
6312
+ }
6313
+ const whereFilter = filterList.length > 0 ? Filters4.and(...filterList) : void 0;
6314
+ debug.debug("Searching Memory_spaces_public", {
6315
+ filterCount: filterList.length,
6316
+ spaces,
6317
+ allPublic: spaces.length === 0,
6318
+ searchType
6319
+ });
6320
+ const spaceObjects = await debug.time("Space collection search", async () => {
6321
+ return await executeSearch(spacesCollection, args.query, searchType, whereFilter, fetchLimit);
6322
+ });
6323
+ allObjects.push(...spaceObjects);
6324
+ logger.info("Space collection search complete", {
6325
+ tool: "remember_search_space",
6326
+ collectionName: spacesCollectionName,
6327
+ resultCount: spaceObjects.length
4982
6328
  });
4983
6329
  }
4984
- if (args.min_weight !== void 0) {
4985
- filterList.push(publicCollection.filter.byProperty("weight").greaterOrEqual(args.min_weight));
4986
- }
4987
- if (args.max_weight !== void 0) {
4988
- filterList.push(publicCollection.filter.byProperty("weight").lessOrEqual(args.max_weight));
4989
- }
4990
- if (args.date_from) {
4991
- filterList.push(publicCollection.filter.byProperty("created_at").greaterOrEqual(new Date(args.date_from)));
4992
- }
4993
- if (args.date_to) {
4994
- filterList.push(publicCollection.filter.byProperty("created_at").lessOrEqual(new Date(args.date_to)));
4995
- }
4996
- const whereFilter = filterList.length > 0 ? Filters4.and(...filterList) : void 0;
4997
- debug.debug("Executing hybrid search", {
4998
- query: args.query,
4999
- filterCount: filterList.length,
5000
- limit: args.limit || 10,
5001
- offset: args.offset || 0
5002
- });
5003
- const searchResults = await debug.time("Hybrid search query", async () => {
5004
- return await publicCollection.query.hybrid(args.query, {
5005
- limit: args.limit || 10,
5006
- offset: args.offset || 0,
5007
- ...whereFilter && { where: whereFilter }
6330
+ for (const groupId of groups) {
6331
+ const groupCollectionName = getCollectionName("GROUPS" /* GROUPS */, groupId);
6332
+ const exists = await weaviateClient.collections.exists(groupCollectionName);
6333
+ if (!exists) {
6334
+ debug.warn("Group collection not found, skipping", { groupId, groupCollectionName });
6335
+ continue;
6336
+ }
6337
+ const groupCollection = weaviateClient.collections.get(groupCollectionName);
6338
+ const filterList = buildBaseFilters(groupCollection, args);
6339
+ const whereFilter = filterList.length > 0 ? Filters4.and(...filterList) : void 0;
6340
+ debug.debug("Searching group collection", {
6341
+ groupId,
6342
+ groupCollectionName,
6343
+ filterCount: filterList.length,
6344
+ searchType
6345
+ });
6346
+ const groupObjects = await debug.time(`Group collection search: ${groupId}`, async () => {
6347
+ return await executeSearch(groupCollection, args.query, searchType, whereFilter, fetchLimit);
5008
6348
  });
6349
+ allObjects.push(...groupObjects);
6350
+ logger.info("Group collection search complete", {
6351
+ tool: "remember_search_space",
6352
+ groupId,
6353
+ collectionName: groupCollectionName,
6354
+ resultCount: groupObjects.length
6355
+ });
6356
+ }
6357
+ const seen = /* @__PURE__ */ new Set();
6358
+ const deduplicated = allObjects.filter((obj) => {
6359
+ if (seen.has(obj.uuid))
6360
+ return false;
6361
+ seen.add(obj.uuid);
6362
+ return true;
5009
6363
  });
5010
- debug.debug("Search completed", {
5011
- resultCount: searchResults.objects.length
6364
+ deduplicated.sort((a, b) => {
6365
+ const scoreA = a.metadata?.score ?? 0;
6366
+ const scoreB = b.metadata?.score ?? 0;
6367
+ return scoreB - scoreA;
5012
6368
  });
5013
- const memories = searchResults.objects.map((obj) => ({
6369
+ const paginated = deduplicated.slice(offset, offset + limit);
6370
+ const memories = paginated.map((obj) => ({
5014
6371
  id: obj.uuid,
5015
6372
  ...obj.properties,
5016
6373
  _score: obj.metadata?.score
5017
6374
  }));
6375
+ const isAllPublic = spaces.length === 0 && groups.length === 0;
5018
6376
  const result = {
5019
- spaces_searched: args.spaces,
6377
+ spaces_searched: isAllPublic ? "all_public" : spaces,
6378
+ groups_searched: groups,
5020
6379
  query: args.query,
6380
+ search_type: searchType,
5021
6381
  memories,
5022
6382
  total: memories.length,
5023
- offset: args.offset || 0,
5024
- limit: args.limit || 10
6383
+ offset,
6384
+ limit
5025
6385
  };
5026
6386
  debug.info("Tool completed successfully", {
5027
6387
  resultCount: memories.length,
5028
- spaces: args.spaces
6388
+ spaces,
6389
+ groups
5029
6390
  });
5030
6391
  return JSON.stringify(result, null, 2);
5031
6392
  } catch (error) {
@@ -5037,6 +6398,7 @@ async function handleSearchSpace(args, userId) {
5037
6398
  toolName: "remember_search_space",
5038
6399
  operation: "search spaces",
5039
6400
  spaces: args.spaces,
6401
+ groups: args.groups,
5040
6402
  query: args.query
5041
6403
  });
5042
6404
  }
@@ -5095,6 +6457,12 @@ Let the query algorithm find ALL relevant memories regardless of type unless exp
5095
6457
  type: "string",
5096
6458
  description: "Filter memories created before this date (ISO 8601)"
5097
6459
  },
6460
+ moderation_filter: {
6461
+ type: "string",
6462
+ enum: ["approved", "pending", "rejected", "removed", "all"],
6463
+ description: 'Filter by moderation status. Default: "approved" (only shows approved/unmoderated). Non-approved filters require moderator permissions.',
6464
+ default: "approved"
6465
+ },
5098
6466
  include_comments: {
5099
6467
  type: "boolean",
5100
6468
  description: "Include comments in query results (default: false)",
@@ -5115,7 +6483,7 @@ Let the query algorithm find ALL relevant memories regardless of type unless exp
5115
6483
  required: ["question", "spaces"]
5116
6484
  }
5117
6485
  };
5118
- async function handleQuerySpace(args, userId) {
6486
+ async function handleQuerySpace(args, userId, authContext) {
5119
6487
  const debug = createDebugLogger({
5120
6488
  tool: "remember_query_space",
5121
6489
  userId,
@@ -5148,16 +6516,35 @@ async function handleQuerySpace(args, userId) {
5148
6516
  2
5149
6517
  );
5150
6518
  }
6519
+ const moderationFilterValue = args.moderation_filter || "approved";
6520
+ if (moderationFilterValue !== "approved" && !canModerateAny(authContext)) {
6521
+ return JSON.stringify(
6522
+ {
6523
+ success: false,
6524
+ error: "Permission denied",
6525
+ message: `Moderator access required to view ${moderationFilterValue} memories in spaces`
6526
+ },
6527
+ null,
6528
+ 2
6529
+ );
6530
+ }
5151
6531
  const weaviateClient = getWeaviateClient();
5152
6532
  const publicCollection = await ensurePublicCollection(weaviateClient);
5153
6533
  const filterList = [];
5154
6534
  filterList.push(publicCollection.filter.byProperty("spaces").containsAny(args.spaces));
5155
6535
  filterList.push(publicCollection.filter.byProperty("doc_type").equal("memory"));
6536
+ const moderationFilter = buildModerationFilter(publicCollection, args.moderation_filter);
6537
+ if (moderationFilter) {
6538
+ filterList.push(moderationFilter);
6539
+ }
5156
6540
  if (args.content_type) {
5157
- filterList.push(publicCollection.filter.byProperty("type").equal(args.content_type));
6541
+ filterList.push(publicCollection.filter.byProperty("content_type").equal(args.content_type));
5158
6542
  }
5159
6543
  if (!args.include_comments && !args.content_type) {
5160
- filterList.push(publicCollection.filter.byProperty("type").notEqual("comment"));
6544
+ filterList.push(publicCollection.filter.byProperty("content_type").notEqual("comment"));
6545
+ }
6546
+ if (!args.content_type) {
6547
+ filterList.push(publicCollection.filter.byProperty("content_type").notEqual("ghost"));
5161
6548
  }
5162
6549
  if (args.tags && args.tags.length > 0) {
5163
6550
  args.tags.forEach((tag) => {
@@ -5236,6 +6623,353 @@ async function handleQuerySpace(args, userId) {
5236
6623
  }
5237
6624
  }
5238
6625
 
6626
+ // src/tools/moderate.ts
6627
+ init_logger();
6628
+ var ACTION_TO_STATUS = {
6629
+ approve: "approved",
6630
+ reject: "rejected",
6631
+ remove: "removed"
6632
+ };
6633
+ var moderateTool = {
6634
+ name: "remember_moderate",
6635
+ description: `Approve, reject, or remove a published memory. Requires moderator permissions (can_moderate).
6636
+
6637
+ Actions:
6638
+ - approve: Mark a pending memory as approved (visible in default searches)
6639
+ - reject: Reject a pending memory (hidden from default searches)
6640
+ - remove: Remove a previously approved memory (hidden from default searches)
6641
+
6642
+ Must specify either space_id or group_id to identify where the memory is published.`,
6643
+ inputSchema: {
6644
+ type: "object",
6645
+ properties: {
6646
+ memory_id: {
6647
+ type: "string",
6648
+ description: "UUID or composite ID of the published memory"
6649
+ },
6650
+ space_id: {
6651
+ type: "string",
6652
+ description: "Space containing the memory (searches Memory_spaces_public filtered by space_ids)"
6653
+ },
6654
+ group_id: {
6655
+ type: "string",
6656
+ description: "Group containing the memory (searches Memory_groups_{groupId})"
6657
+ },
6658
+ action: {
6659
+ type: "string",
6660
+ enum: ["approve", "reject", "remove"],
6661
+ description: "Moderation action to perform"
6662
+ },
6663
+ reason: {
6664
+ type: "string",
6665
+ description: "Optional reason for the moderation action"
6666
+ }
6667
+ },
6668
+ required: ["memory_id", "action"]
6669
+ }
6670
+ };
6671
+ async function handleModerate(args, userId, authContext) {
6672
+ const debug = createDebugLogger({
6673
+ tool: "remember_moderate",
6674
+ userId,
6675
+ operation: "moderate"
6676
+ });
6677
+ try {
6678
+ debug.info("Tool invoked");
6679
+ debug.trace("Arguments", { args });
6680
+ const { memory_id, space_id, group_id, action, reason } = args;
6681
+ if (!space_id && !group_id) {
6682
+ return JSON.stringify(
6683
+ {
6684
+ success: false,
6685
+ error: "Missing destination",
6686
+ message: "Must specify either space_id or group_id"
6687
+ },
6688
+ null,
6689
+ 2
6690
+ );
6691
+ }
6692
+ if (!ACTION_TO_STATUS[action]) {
6693
+ return JSON.stringify(
6694
+ {
6695
+ success: false,
6696
+ error: "Invalid action",
6697
+ message: `Action must be one of: approve, reject, remove`
6698
+ },
6699
+ null,
6700
+ 2
6701
+ );
6702
+ }
6703
+ if (group_id) {
6704
+ if (!canModerate(authContext, group_id)) {
6705
+ return JSON.stringify(
6706
+ {
6707
+ success: false,
6708
+ error: "Permission denied",
6709
+ message: `Moderator access required for group ${group_id}`
6710
+ },
6711
+ null,
6712
+ 2
6713
+ );
6714
+ }
6715
+ } else if (space_id) {
6716
+ if (!canModerateAny(authContext)) {
6717
+ return JSON.stringify(
6718
+ {
6719
+ success: false,
6720
+ error: "Permission denied",
6721
+ message: `Moderator access required to moderate memories in spaces`
6722
+ },
6723
+ null,
6724
+ 2
6725
+ );
6726
+ }
6727
+ }
6728
+ const weaviateClient = getWeaviateClient();
6729
+ let collection;
6730
+ if (group_id) {
6731
+ const collectionName = getCollectionName("GROUPS" /* GROUPS */, group_id);
6732
+ collection = weaviateClient.collections.get(collectionName);
6733
+ } else {
6734
+ collection = await ensurePublicCollection(weaviateClient);
6735
+ }
6736
+ const memory = await fetchMemoryWithAllProperties(collection, memory_id);
6737
+ if (!memory) {
6738
+ return JSON.stringify(
6739
+ {
6740
+ success: false,
6741
+ error: "Memory not found",
6742
+ message: `Published memory ${memory_id} not found in ${group_id ? `group ${group_id}` : `space ${space_id}`}`
6743
+ },
6744
+ null,
6745
+ 2
6746
+ );
6747
+ }
6748
+ const newStatus = ACTION_TO_STATUS[action];
6749
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6750
+ await collection.data.update({
6751
+ id: memory_id,
6752
+ properties: {
6753
+ moderation_status: newStatus,
6754
+ moderated_by: userId,
6755
+ moderated_at: now
6756
+ }
6757
+ });
6758
+ logger.info("Memory moderated", {
6759
+ tool: "remember_moderate",
6760
+ userId,
6761
+ memoryId: memory_id,
6762
+ action,
6763
+ newStatus,
6764
+ spaceId: space_id,
6765
+ groupId: group_id,
6766
+ reason
6767
+ });
6768
+ return JSON.stringify(
6769
+ {
6770
+ success: true,
6771
+ memory_id,
6772
+ action,
6773
+ moderation_status: newStatus,
6774
+ moderated_by: userId,
6775
+ moderated_at: now,
6776
+ reason: reason || void 0,
6777
+ location: group_id ? `group:${group_id}` : `space:${space_id}`
6778
+ },
6779
+ null,
6780
+ 2
6781
+ );
6782
+ } catch (error) {
6783
+ debug.error("Tool failed", {
6784
+ error: error instanceof Error ? error.message : String(error),
6785
+ stack: error instanceof Error ? error.stack : void 0
6786
+ });
6787
+ return handleToolError(error, {
6788
+ toolName: "remember_moderate",
6789
+ operation: "moderate memory",
6790
+ memoryId: args.memory_id,
6791
+ action: args.action
6792
+ });
6793
+ }
6794
+ }
6795
+
6796
+ // src/tools/ghost-config.ts
6797
+ init_ghost_config_service();
6798
+ var ghostConfigTool = {
6799
+ name: "remember_ghost_config",
6800
+ description: `Manage ghost/persona configuration. Controls who can interact with your ghost and at what trust level.
6801
+
6802
+ Actions:
6803
+ - get: View current ghost configuration
6804
+ - set: Update ghost settings (enabled, trust defaults, enforcement mode)
6805
+ - set_trust: Set a per-user trust level override (0-1)
6806
+ - remove_trust: Remove a per-user trust override (revert to default)
6807
+ - block: Block a user from ghost access entirely
6808
+ - unblock: Unblock a previously blocked user
6809
+
6810
+ Trust levels control what information your ghost can share:
6811
+ - 0.0: Existence only ("A memory exists about this")
6812
+ - 0.25: Metadata only (tags, type, dates \u2014 no content)
6813
+ - 0.5: Summary only (AI-generated summary, no raw content)
6814
+ - 0.75: Partial access (content with sensitive fields redacted)
6815
+ - 1.0: Full access (all content revealed)
6816
+
6817
+ Ghost is disabled by default. Enable it to allow others to chat with your AI representation.`,
6818
+ inputSchema: {
6819
+ type: "object",
6820
+ properties: {
6821
+ action: {
6822
+ type: "string",
6823
+ enum: ["get", "set", "set_trust", "remove_trust", "block", "unblock"],
6824
+ description: "Action to perform"
6825
+ },
6826
+ // For 'set' action
6827
+ enabled: {
6828
+ type: "boolean",
6829
+ description: 'Enable/disable ghost conversations (for "set" action)'
6830
+ },
6831
+ public_ghost_enabled: {
6832
+ type: "boolean",
6833
+ description: 'Allow non-friends to chat with ghost (for "set" action)'
6834
+ },
6835
+ default_friend_trust: {
6836
+ type: "number",
6837
+ description: 'Default trust level for friends (0-1, for "set" action)',
6838
+ minimum: 0,
6839
+ maximum: 1
6840
+ },
6841
+ default_public_trust: {
6842
+ type: "number",
6843
+ description: 'Default trust level for strangers (0-1, for "set" action)',
6844
+ minimum: 0,
6845
+ maximum: 1
6846
+ },
6847
+ enforcement_mode: {
6848
+ type: "string",
6849
+ enum: ["query", "prompt", "hybrid"],
6850
+ description: 'Trust enforcement mode (for "set" action). "query" (default) is most secure.'
6851
+ },
6852
+ // For 'set_trust' / 'remove_trust' / 'block' / 'unblock' actions
6853
+ target_user_id: {
6854
+ type: "string",
6855
+ description: "Target user ID (for set_trust, remove_trust, block, unblock)"
6856
+ },
6857
+ trust_level: {
6858
+ type: "number",
6859
+ description: 'Trust level to assign (0-1, for "set_trust" action)',
6860
+ minimum: 0,
6861
+ maximum: 1
6862
+ }
6863
+ },
6864
+ required: ["action"]
6865
+ }
6866
+ };
6867
+ async function handleGhostConfig(args, userId, authContext) {
6868
+ const debug = createDebugLogger({ tool: "remember_ghost_config", userId, operation: args.action });
6869
+ try {
6870
+ debug.info("Tool invoked");
6871
+ debug.trace("Arguments", { args });
6872
+ switch (args.action) {
6873
+ case "get": {
6874
+ const config2 = await getGhostConfig(userId);
6875
+ return JSON.stringify({
6876
+ success: true,
6877
+ config: config2,
6878
+ trust_tier_guide: {
6879
+ "0.0": "Existence only",
6880
+ "0.25": "Metadata only (default friend trust)",
6881
+ "0.5": "Summary only",
6882
+ "0.75": "Partial access",
6883
+ "1.0": "Full access"
6884
+ }
6885
+ }, null, 2);
6886
+ }
6887
+ case "set": {
6888
+ const updates = {};
6889
+ if (args.enabled !== void 0)
6890
+ updates.enabled = args.enabled;
6891
+ if (args.public_ghost_enabled !== void 0)
6892
+ updates.public_ghost_enabled = args.public_ghost_enabled;
6893
+ if (args.default_friend_trust !== void 0)
6894
+ updates.default_friend_trust = args.default_friend_trust;
6895
+ if (args.default_public_trust !== void 0)
6896
+ updates.default_public_trust = args.default_public_trust;
6897
+ if (args.enforcement_mode !== void 0)
6898
+ updates.enforcement_mode = args.enforcement_mode;
6899
+ if (Object.keys(updates).length === 0) {
6900
+ throw new Error("No fields to update. Provide at least one of: enabled, public_ghost_enabled, default_friend_trust, default_public_trust, enforcement_mode");
6901
+ }
6902
+ validateGhostConfigUpdate(updates);
6903
+ const config2 = await setGhostConfigFields(userId, updates);
6904
+ return JSON.stringify({
6905
+ success: true,
6906
+ message: "Ghost configuration updated",
6907
+ updated_fields: Object.keys(updates),
6908
+ config: config2
6909
+ }, null, 2);
6910
+ }
6911
+ case "set_trust": {
6912
+ if (!args.target_user_id) {
6913
+ throw new Error("target_user_id is required for set_trust action");
6914
+ }
6915
+ if (args.trust_level === void 0) {
6916
+ throw new Error("trust_level is required for set_trust action");
6917
+ }
6918
+ await setUserTrust(userId, args.target_user_id, args.trust_level);
6919
+ return JSON.stringify({
6920
+ success: true,
6921
+ message: `Trust level for ${args.target_user_id} set to ${args.trust_level}`,
6922
+ target_user_id: args.target_user_id,
6923
+ trust_level: args.trust_level
6924
+ }, null, 2);
6925
+ }
6926
+ case "remove_trust": {
6927
+ if (!args.target_user_id) {
6928
+ throw new Error("target_user_id is required for remove_trust action");
6929
+ }
6930
+ await removeUserTrust(userId, args.target_user_id);
6931
+ return JSON.stringify({
6932
+ success: true,
6933
+ message: `Trust override for ${args.target_user_id} removed (reverted to default)`,
6934
+ target_user_id: args.target_user_id
6935
+ }, null, 2);
6936
+ }
6937
+ case "block": {
6938
+ if (!args.target_user_id) {
6939
+ throw new Error("target_user_id is required for block action");
6940
+ }
6941
+ await blockUser(userId, args.target_user_id);
6942
+ return JSON.stringify({
6943
+ success: true,
6944
+ message: `${args.target_user_id} blocked from ghost access`,
6945
+ target_user_id: args.target_user_id
6946
+ }, null, 2);
6947
+ }
6948
+ case "unblock": {
6949
+ if (!args.target_user_id) {
6950
+ throw new Error("target_user_id is required for unblock action");
6951
+ }
6952
+ await unblockUser(userId, args.target_user_id);
6953
+ return JSON.stringify({
6954
+ success: true,
6955
+ message: `${args.target_user_id} unblocked from ghost access`,
6956
+ target_user_id: args.target_user_id
6957
+ }, null, 2);
6958
+ }
6959
+ default:
6960
+ throw new Error(`Unknown action: ${args.action}. Valid actions: get, set, set_trust, remove_trust, block, unblock`);
6961
+ }
6962
+ } catch (error) {
6963
+ debug.error("Tool failed", { error: error instanceof Error ? error.message : String(error) });
6964
+ handleToolError(error, {
6965
+ toolName: "remember_ghost_config",
6966
+ operation: args.action,
6967
+ userId
6968
+ });
6969
+ throw error;
6970
+ }
6971
+ }
6972
+
5239
6973
  // src/server-factory.ts
5240
6974
  var databasesInitialized = false;
5241
6975
  var initializationPromise = null;
@@ -5285,10 +7019,27 @@ async function createServer(accessToken, userId, options = {}) {
5285
7019
  }
5286
7020
  }
5287
7021
  );
5288
- registerHandlers(server, userId, accessToken);
7022
+ let resolvedGhostMode;
7023
+ if (options.ghostMode) {
7024
+ const { getGhostConfig: getGhostConfig2 } = await Promise.resolve().then(() => (init_ghost_config_service(), ghost_config_service_exports));
7025
+ const { resolveAccessorTrustLevel: resolveAccessorTrustLevel2 } = await Promise.resolve().then(() => (init_access_control(), access_control_exports));
7026
+ const ghostConfig = await getGhostConfig2(options.ghostMode.owner_user_id);
7027
+ const trustLevel = resolveAccessorTrustLevel2(ghostConfig, options.ghostMode.accessor_user_id);
7028
+ resolvedGhostMode = {
7029
+ owner_user_id: options.ghostMode.owner_user_id,
7030
+ accessor_user_id: options.ghostMode.accessor_user_id,
7031
+ accessor_trust_level: trustLevel
7032
+ };
7033
+ logger.info("Ghost mode resolved", {
7034
+ ownerUserId: resolvedGhostMode.owner_user_id,
7035
+ accessorUserId: resolvedGhostMode.accessor_user_id,
7036
+ trustLevel: resolvedGhostMode.accessor_trust_level
7037
+ });
7038
+ }
7039
+ registerHandlers(server, userId, accessToken, resolvedGhostMode);
5289
7040
  return server;
5290
7041
  }
5291
- function registerHandlers(server, userId, accessToken) {
7042
+ function registerHandlers(server, userId, accessToken, ghostMode) {
5292
7043
  server.setRequestHandler(ListToolsRequestSchema, async () => {
5293
7044
  return {
5294
7045
  tools: [
@@ -5309,68 +7060,86 @@ function registerHandlers(server, userId, accessToken) {
5309
7060
  getPreferencesTool,
5310
7061
  // Space tools
5311
7062
  publishTool,
7063
+ retractTool,
7064
+ reviseTool,
5312
7065
  confirmTool,
5313
7066
  denyTool,
5314
7067
  searchSpaceTool,
5315
- querySpaceTool
7068
+ querySpaceTool,
7069
+ moderateTool,
7070
+ ghostConfigTool
5316
7071
  ]
5317
7072
  };
5318
7073
  });
5319
7074
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
5320
7075
  const { name, arguments: args } = request.params;
5321
7076
  try {
7077
+ const credentials = await credentialsProvider.getCredentials(accessToken, userId);
7078
+ const authContext = { accessToken, credentials, ghostMode };
5322
7079
  let result;
5323
7080
  switch (name) {
5324
7081
  case "remember_create_memory":
5325
- result = await handleCreateMemory(args, userId);
7082
+ result = await handleCreateMemory(args, userId, authContext);
5326
7083
  break;
5327
7084
  case "remember_search_memory":
5328
- result = await handleSearchMemory(args, userId);
7085
+ result = await handleSearchMemory(args, userId, authContext);
5329
7086
  break;
5330
7087
  case "remember_delete_memory":
5331
- result = await handleDeleteMemory(args, userId);
7088
+ result = await handleDeleteMemory(args, userId, authContext);
5332
7089
  break;
5333
7090
  case "remember_update_memory":
5334
- result = await handleUpdateMemory(args, userId);
7091
+ result = await handleUpdateMemory(args, userId, authContext);
5335
7092
  break;
5336
7093
  case "remember_find_similar":
5337
- result = await handleFindSimilar(args, userId);
7094
+ result = await handleFindSimilar(args, userId, authContext);
5338
7095
  break;
5339
7096
  case "remember_query_memory":
5340
- result = await handleQueryMemory(args, userId);
7097
+ result = await handleQueryMemory(args, userId, authContext);
5341
7098
  break;
5342
7099
  case "remember_create_relationship":
5343
- result = await handleCreateRelationship(args, userId);
7100
+ result = await handleCreateRelationship(args, userId, authContext);
5344
7101
  break;
5345
7102
  case "remember_update_relationship":
5346
- result = await handleUpdateRelationship(args, userId);
7103
+ result = await handleUpdateRelationship(args, userId, authContext);
5347
7104
  break;
5348
7105
  case "remember_search_relationship":
5349
- result = await handleSearchRelationship(args, userId);
7106
+ result = await handleSearchRelationship(args, userId, authContext);
5350
7107
  break;
5351
7108
  case "remember_delete_relationship":
5352
- result = await handleDeleteRelationship(args, userId);
7109
+ result = await handleDeleteRelationship(args, userId, authContext);
5353
7110
  break;
5354
7111
  case "remember_set_preference":
5355
- result = await handleSetPreference(args, userId);
7112
+ result = await handleSetPreference(args, userId, authContext);
5356
7113
  break;
5357
7114
  case "remember_get_preferences":
5358
- result = await handleGetPreferences(args, userId);
7115
+ result = await handleGetPreferences(args, userId, authContext);
5359
7116
  break;
5360
7117
  case "remember_publish":
5361
- result = await handlePublish(args, userId);
7118
+ result = await handlePublish(args, userId, authContext);
7119
+ break;
7120
+ case "remember_retract":
7121
+ result = await handleRetract(args, userId, authContext);
7122
+ break;
7123
+ case "remember_revise":
7124
+ result = await handleRevise(args, userId, authContext);
5362
7125
  break;
5363
7126
  case "remember_confirm":
5364
- result = await handleConfirm(args, userId);
7127
+ result = await handleConfirm(args, userId, authContext);
5365
7128
  break;
5366
7129
  case "remember_deny":
5367
- result = await handleDeny(args, userId);
7130
+ result = await handleDeny(args, userId, authContext);
5368
7131
  break;
5369
7132
  case "remember_search_space":
5370
- result = await handleSearchSpace(args, userId);
7133
+ result = await handleSearchSpace(args, userId, authContext);
5371
7134
  break;
5372
7135
  case "remember_query_space":
5373
- result = await handleQuerySpace(args, userId);
7136
+ result = await handleQuerySpace(args, userId, authContext);
7137
+ break;
7138
+ case "remember_moderate":
7139
+ result = await handleModerate(args, userId, authContext);
7140
+ break;
7141
+ case "remember_ghost_config":
7142
+ result = await handleGhostConfig(args, userId, authContext);
5374
7143
  break;
5375
7144
  default:
5376
7145
  throw new McpError(