@ibgib/core-gib 0.1.18 → 0.1.19

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 (31) hide show
  1. package/dist/sync/sync-innerspace-constants.respec.d.mts +8 -0
  2. package/dist/sync/sync-innerspace-constants.respec.d.mts.map +1 -0
  3. package/dist/sync/sync-innerspace-constants.respec.mjs +116 -0
  4. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -0
  5. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +2 -2
  6. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  7. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +3 -3
  8. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
  9. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +2 -2
  10. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  11. package/dist/sync/sync-innerspace-partial-update.respec.d.mts +7 -0
  12. package/dist/sync/sync-innerspace-partial-update.respec.d.mts.map +1 -0
  13. package/dist/sync/sync-innerspace-partial-update.respec.mjs +116 -0
  14. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -0
  15. package/dist/sync/sync-innerspace.respec.mjs +5 -5
  16. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  17. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  18. package/dist/sync/sync-saga-coordinator.mjs +159 -8
  19. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  20. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +7 -0
  21. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  22. package/package.json +1 -1
  23. package/src/sync/sync-innerspace-constants.respec.mts +133 -0
  24. package/src/sync/sync-innerspace-deep-updates.respec.mts +1 -1
  25. package/src/sync/sync-innerspace-dest-ahead.respec.mts +2 -2
  26. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +1 -1
  27. package/src/sync/sync-innerspace-partial-update.respec.mts +150 -0
  28. package/src/sync/sync-innerspace.respec.mts +4 -4
  29. package/src/sync/sync-saga-coordinator.mts +173 -9
  30. package/src/sync/sync-saga-message/sync-saga-message-types.mts +5 -0
  31. package/tmp.md +426 -375
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @module sync-innerspace-partial-update.respec
3
+ *
4
+ * Verifies Sync Scenarios where the Destination has partial history.
5
+ */
6
+
7
+ import {
8
+ respecfully, lastOfAll, ifWe, iReckon,
9
+ ifWeMight
10
+ } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
11
+ const maam = `[${import.meta.url}]`, sir = maam;
12
+ import { delay, extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
13
+ import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
14
+ import { getDependencyGraph } from '../common/other/graph-helper.mjs';
15
+
16
+ import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
17
+ import { putInSpace, getFromSpace } from '../witness/space/space-helper.mjs';
18
+ import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
19
+ import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
20
+ import { createTimelineRootHelper, getTestKeystoneServiceHelper } from '../agent-helpers.mjs';
21
+ import { mut8Timeline } from '../timeline/timeline-api.mjs';
22
+ import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace-v1.mjs';
23
+ import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-space-types.mjs';
24
+ import { toDto } from '../common/other/ibgib-helper.mjs';
25
+
26
+ const logalot = true;
27
+ const lc = `[sync-innerspace-partial-update.respec]`;
28
+
29
+ await respecfully(sir, `Sync InnerSpaces (Partial Update)`, async () => {
30
+
31
+ let metaspace: Metaspace_Innerspace;
32
+ let sourceSpace: InnerSpace_V1;
33
+ let destSpace: InnerSpace_V1;
34
+
35
+ interface TestData {
36
+ type: string;
37
+ label?: string;
38
+ uuid?: string;
39
+ n?: number;
40
+ }
41
+
42
+ await respecfully(sir, `Sender Newer (Partial History)`, async () => {
43
+ // 1. Setup Spaces
44
+ metaspace = new Metaspace_Innerspace(undefined);
45
+ await metaspace.initialize({
46
+ getFnAlert: () => async ({ title, msg }) => { },
47
+ getFnPrompt: () => async ({ title, msg }) => { return ''; },
48
+ getFnPromptPassword: () => async (title, msg) => { return null; },
49
+ });
50
+ while (!metaspace.initialized) { await delay(10); }
51
+
52
+ const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
53
+ await defaultLocalUserSpace!.initialized;
54
+
55
+ sourceSpace = new InnerSpace_V1({
56
+ ...DEFAULT_INNER_SPACE_DATA_V1,
57
+ name: 'source',
58
+ uuid: 'source_uuid',
59
+ description: 'source test space',
60
+ });
61
+ await sourceSpace.initialized;
62
+
63
+ destSpace = new InnerSpace_V1({
64
+ ...DEFAULT_INNER_SPACE_DATA_V1,
65
+ name: 'dest',
66
+ uuid: 'dest_uuid',
67
+ description: 'dest test space',
68
+ });
69
+ await destSpace.initialized;
70
+
71
+ // 2. See Data
72
+ // Root -> V1 (Shared) -> V2 (Source Only)
73
+
74
+ const root = await createTimelineRootHelper<TestData>({
75
+ ib: 'timeline_root_partial',
76
+ data: { type: 'root', label: 'Root' },
77
+ space: sourceSpace,
78
+ });
79
+
80
+ // V1
81
+ const v1 = await mut8Timeline<TestData>({
82
+ timeline: root,
83
+ mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V1' } },
84
+ metaspace,
85
+ space: sourceSpace,
86
+ });
87
+
88
+ // V2 (Source Only)
89
+ const v2 = await mut8Timeline<TestData>({
90
+ timeline: v1,
91
+ mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V2' } },
92
+ metaspace,
93
+ space: sourceSpace,
94
+ });
95
+ const addrV2 = getIbGibAddr({ ibGib: v2 });
96
+
97
+ // Transfer Root & V1 to Dest (Simulate previous sync)
98
+ await putInSpace({ space: destSpace, ibGibs: [root, v1] });
99
+
100
+ await ifWe(sir, 'verify setup', async () => {
101
+ // Verify Dest has V1
102
+ const checkV1 = await getFromSpace({ space: destSpace, addr: getIbGibAddr({ ibGib: v1 }) });
103
+ iReckon(sir, checkV1.success).asTo('Dest has V1').isGonnaBeTrue();
104
+
105
+ // Verify Dest does NOT have V2
106
+ const checkV2 = await getFromSpace({ space: destSpace, addr: addrV2 });
107
+ iReckon(sir, checkV2.success && !!checkV2.ibGibs?.length).asTo('Dest has V2').isGonnaBeFalse();
108
+ });
109
+
110
+ // 3. Setup Sync
111
+ const mockKeystone = await getTestKeystoneServiceHelper();
112
+ const senderCoordinator = new SyncSagaCoordinator(mockKeystone);
113
+ const receiverCoordinator = new SyncSagaCoordinator(mockKeystone);
114
+
115
+ const peer = new SyncPeerInnerspace_V1({
116
+ senderSpace: sourceSpace,
117
+ receiverSpace: destSpace,
118
+ receiverCoordinator: receiverCoordinator,
119
+ receiverMetaspace: metaspace,
120
+ });
121
+
122
+ // 4. Run Sync (Source Pushes V2)
123
+ console.log(`${lc} Running Sync...`);
124
+ const resSync = await senderCoordinator.sync({
125
+ peer: peer,
126
+ localSpace: sourceSpace,
127
+ metaspace: metaspace,
128
+ domainIbGibs: [v2],
129
+ useSessionIdentity: false,
130
+ });
131
+
132
+ await resSync.done;
133
+
134
+ // 5. Verify Dest (Should now have V2)
135
+ console.log(`${lc} Verifying Destination...`);
136
+
137
+ await ifWe(sir, `verify dest updated`, async () => {
138
+ // Verify Tip (V2)
139
+ const getV2 = await getFromSpace({ space: destSpace, addr: addrV2 });
140
+ iReckon(sir, getV2.success).asTo('V2 present in Dest').isGonnaBeTrue();
141
+
142
+ // Verify V2 points to V1
143
+ const v2IbGib = getV2.ibGibs![0];
144
+ const pastAddr = v2IbGib.rel8ns?.past?.at(-1);
145
+ iReckon(sir, pastAddr === getIbGibAddr({ ibGib: v1 })).asTo('V2 points to V1').isGonnaBeTrue();
146
+ });
147
+
148
+ });
149
+
150
+ });
@@ -33,7 +33,7 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
33
33
  let sourceSpace: InnerSpace_V1;
34
34
  let destSpace: InnerSpace_V1;
35
35
 
36
- await ifWeMight(sir, `Basic Push Sync (Source -> Dest)`, async () => {
36
+ await ifWe(sir, `Basic Push Sync (Source -> Dest)`, async () => {
37
37
  // 1. Setup Spaces
38
38
  metaspace = new Metaspace_Innerspace(undefined);
39
39
  await metaspace.initialize({
@@ -150,18 +150,18 @@ await respecfully(sir, `Sync InnerSpaces`, async () => {
150
150
  try {
151
151
  const getChildInDest = await getFromSpace({ space: destSpace, addr: childAddr });
152
152
 
153
- await ifWeMight(sir, `verify success getChildInDest`, async () => {
153
+ await ifWe(sir, `verify success getChildInDest`, async () => {
154
154
  iReckon(sir, getChildInDest.success).asTo('Child present in Dest').isGonnaBeTrue();
155
155
  });
156
156
 
157
- await ifWeMight(sir, `verify getChildInDest.ibGibs`, async () => {
157
+ await ifWe(sir, `verify getChildInDest.ibGibs`, async () => {
158
158
  const firstChild = getChildInDest.ibGibs?.[0];
159
159
  console.log(`${lc} firstChild: ${pretty(firstChild)}`);
160
160
  iReckon(sir, firstChild?.data?.n).asTo('Child content matches').isGonnaBe(2);
161
161
  });
162
162
 
163
163
  } catch (error) {
164
- await ifWeMight(sir, `doh`, async () => {
164
+ await ifWe(sir, `doh`, async () => {
165
165
  // hack here I'm getting tired...
166
166
  iReckon(sir, true).asTo(`error: ${extractErrorMsg(error)}`).isGonnaBeFalse();
167
167
  });
@@ -381,7 +381,7 @@ export class SyncSagaCoordinator {
381
381
  const result = await this.handleSagaFrame({
382
382
  sagaIbGib: remoteFrame as SyncIbGib_V1,
383
383
  srcGraph,
384
- space: tempSpace,
384
+ space: localSpace, // Must be localSpace (Source) to find domain data
385
385
  identity: sessionIdentity,
386
386
  metaspace
387
387
  });
@@ -610,14 +610,35 @@ export class SyncSagaCoordinator {
610
610
  if (logalot) { console.log(`${lc} starting...`); }
611
611
 
612
612
  // Extract Init Data
613
- const initData = messageData as SyncInitData; // Using renamed variable for clarity
613
+ const initData = messageData as SyncSagaMessageInitData_V1; // Using renamed variable for clarity
614
614
  if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
615
615
  if (!initData || !initData.knowledgeVector) {
616
616
  throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
617
617
  }
618
618
 
619
- // Stones Analysis
620
- // TODO: Handle stones separately if needed, though they are usually dependencies of timelines.
619
+ // 2. Gap Analysis
620
+ const conflicts: { tjp: string, localAddr?: string, remoteAddr: string, reason: string }[] = [];
621
+ const conflictStrategy: SyncConflictStrategy = initData.conflictStrategy || 'abort'; // Default to abort if not specified, or we should parameterize this
622
+
623
+ const deltaReqAddrs: string[] = [];
624
+ const pushOfferAddrs: string[] = [];
625
+
626
+ // Stones Analysis (Constants / Non-TJPs)
627
+ const stones = initData.stones || [];
628
+ if (stones.length > 0) {
629
+ if (logalot) { console.log(`${lc} processing stones: ${stones.length}`); }
630
+ // Check if we have these stones
631
+ const resStones = await getFromSpace({ space, addrs: stones });
632
+ const addrsNotFound = resStones.rawResultIbGib?.data?.addrsNotFound;
633
+ if (addrsNotFound && addrsNotFound.length > 0) {
634
+ if (logalot) { console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`); }
635
+ addrsNotFound.forEach(addr => {
636
+ if (!deltaReqAddrs.includes(addr)) {
637
+ deltaReqAddrs.push(addr);
638
+ }
639
+ });
640
+ }
641
+ }
621
642
 
622
643
  const remoteKV = initData.knowledgeVector;
623
644
  if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
@@ -639,11 +660,7 @@ export class SyncSagaCoordinator {
639
660
  }
640
661
 
641
662
  // 2. Gap Analysis
642
- const conflicts: { tjp: string, localAddr?: string, remoteAddr: string, reason: string }[] = [];
643
- const conflictStrategy: SyncConflictStrategy = initData.conflictStrategy || 'abort'; // Default to abort if not specified, or we should parameterize this
644
663
 
645
- const deltaReqAddrs: string[] = [];
646
- const pushOfferAddrs: string[] = [];
647
664
 
648
665
  for (const tjp of remoteTjps) {
649
666
  const remoteAddr = remoteKV[tjp];
@@ -738,6 +755,96 @@ export class SyncSagaCoordinator {
738
755
  return { frame: conflictFrame };
739
756
  }
740
757
 
758
+ // 2. Add Push Offers (Missing in Local)
759
+ // Check if we have them. If not, ask for them.
760
+ for (const addr of pushOfferAddrs) {
761
+ const hasIt = await getFromSpace({ addr, space });
762
+ if (!hasIt.success || !hasIt.ibGibs || hasIt.ibGibs.length === 0) {
763
+ // If we don't have it, we put it in `deltaReqAddrs` of the Ack.
764
+ deltaReqAddrs.push(addr);
765
+ }
766
+ }
767
+
768
+ // 3. Build Knowledge Vector (Full History for known timelines)
769
+ // [NEW] Smart Diff
770
+ // We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
771
+ // Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
772
+
773
+ const knowledgeVector: { [groupKey: string]: string[] } = {};
774
+ const relevantAddrs = new Set([...pushOfferAddrs, ...deltaReqAddrs]);
775
+
776
+ // [Smart Diff] Populate knowledge from timelines identified by Sender
777
+ for (const tjp of remoteTjps) {
778
+ const localAddr = localKV[tjp];
779
+ if (localAddr) {
780
+ const res = await getFromSpace({ addr: localAddr, space });
781
+ if (res.success && res.ibGibs?.[0]) {
782
+ const ibGib = res.ibGibs[0];
783
+ const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
784
+ if (!knowledgeVector[realTjp]) {
785
+ const past = ibGib.rel8ns?.past || [];
786
+ knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
787
+ }
788
+ }
789
+ }
790
+ }
791
+
792
+ // [Smart Diff] Also check individual requested addresses (Fall back for constants/unknown TJPs)
793
+ for (const addr of relevantAddrs) {
794
+ // Only process if not already covered by TJP logic above
795
+ // We can't really know if it's covered easily without resolving.
796
+ // But if we don't have it (requesting), we won't find it here anyway.
797
+ // If we DO have it (push offer), we might find it.
798
+ const res = await getFromSpace({ addr, space });
799
+ if (res.success && res.ibGibs?.[0]) {
800
+ const ibGib = res.ibGibs[0];
801
+ const tjpAddr = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib });
802
+
803
+ if (!knowledgeVector[tjpAddr]) {
804
+ const past = ibGib.rel8ns?.past || [];
805
+ knowledgeVector[tjpAddr] = [getIbGibAddr({ ibGib }), ...past];
806
+ }
807
+ } else {
808
+ // We don't have `addr`.
809
+ }
810
+ }
811
+
812
+ // Also populate from `knowledgeVector` in Init if we want bidirectional?
813
+ // No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
814
+ // Let's assume standard flow:
815
+ // 1. Sender says "I have X"
816
+ // 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
817
+ // Problem: Receiver doesn't know X is related to Y without X.
818
+
819
+ // SOLUTION:
820
+ // The *Sender* must include TJP/Past info in `Init`? Or `pushOfferAddrs` should be richer?
821
+ // OR: Receiver does a check.
822
+ // Wait, if Sender sends V2, it's just an address.
823
+ // If Receiver doesn't have it, it's opaque.
824
+
825
+ // REVISIT "Constant / No TJP" logic:
826
+ // IF we are testing "Sender Newer", meaning Sender has V2, Receiver has V1.
827
+ // Sender calls `sync([V2])`. Init Frame contains `stones: [V2_Address]`.
828
+ // Receiver checks V2_Address. Not found.
829
+ // Receiver requests V2.
830
+ // Receiver sends Ack(DeltaReq: [V2], Knowledge: {}).
831
+ // Sender receives Ack. Sender sends V2 *AND* its deps (V1, Root).
832
+ // Receiver has V1. Sender sends V1 anyway.
833
+ // This is "Naive Deep Sync".
834
+
835
+ // TO ACHIEVE "Smart Diff":
836
+ // Receiver needs to know "Oh, V2 is a timeline tip of TJP_A".
837
+ // If Sender doesn't send TJP info, Receiver is blind.
838
+
839
+ // Proposed Fix (Short Term):
840
+ // `SyncInitData` should include TJP mappings or we rely on `knowledgeVector` in `Init`?
841
+ // `SyncSagaMessageInitData_V1` extends `SyncInitData`.
842
+ // `SyncInitData` has `knowledgeVector`.
843
+ // If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
844
+
845
+ // Let's implement Sender populating `Init.knowledgeVector`.
846
+ // But `SyncSagaCoordinator.startSaga` creates Init.
847
+
741
848
  // 3. Create Ack Frame
742
849
  const sagaId = sagaIbGib.data!.uuid;
743
850
  // Create Payload Stone
@@ -746,6 +853,7 @@ export class SyncSagaCoordinator {
746
853
  stage: SyncStage.ack,
747
854
  deltaReqAddrs,
748
855
  pushOfferAddrs,
856
+ knowledgeVector,
749
857
  };
750
858
 
751
859
  const ackStone = await this.createSyncMsgStone({
@@ -820,7 +928,17 @@ export class SyncSagaCoordinator {
820
928
  }
821
929
 
822
930
  // 2. Process Delta Requests (Push Payload)
931
+ // [NEW] Smart Diff: Use knowledgeVector to skip dependencies
932
+ const skipAddrs = new Set<string>();
933
+ if (ackData.knowledgeVector) {
934
+ Object.values(ackData.knowledgeVector).forEach(addrs => {
935
+ addrs.forEach(a => skipAddrs.add(a));
936
+ });
937
+ }
938
+
823
939
  const payloadIbGibs: IbGib_V1[] = [];
940
+ // Gather all tips to sync first
941
+ const tipsToSync: IbGib_V1[] = [];
824
942
  for (const addr of deltaReqAddrs) {
825
943
  let ibGib = srcGraph[addr];
826
944
  if (!ibGib) {
@@ -830,12 +948,58 @@ export class SyncSagaCoordinator {
830
948
  }
831
949
  }
832
950
  if (ibGib) {
833
- payloadIbGibs.push(ibGib);
951
+ tipsToSync.push(ibGib);
834
952
  } else {
835
953
  throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
836
954
  }
837
955
  }
838
956
 
957
+ // Calculate Dependency Graph for ALL tips, effectively utilizing common history
958
+ // Pass skipAddrs to `getDependencyGraph` or gather manually.
959
+ // `getDependencyGraph` takes a single ibGib.
960
+ // We can optimize by doing it for each tip and unioning the result?
961
+ // Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
962
+ // We will loop.
963
+
964
+ const allDepsSet = new Set<string>();
965
+
966
+ for (const tip of tipsToSync) {
967
+ // Always include the tip itself
968
+ const tipAddr = getIbGibAddr({ ibGib: tip });
969
+ // Only process if not skipped (though deltaReq implies they barely just asked for it)
970
+ // But detailed deps might be skipped.
971
+
972
+ // Get Graph with Skips
973
+ // Logic: "Give me everything related to Tip, EXCEPT X, Y, Z"
974
+ const deps = await getDependencyGraph({
975
+ ibGib: tip,
976
+ space,
977
+ skipAddrs: Array.from(skipAddrs)
978
+ });
979
+
980
+ // [FIX] Ensure Tip is included if not in deps (e.g. constant with no rel8ns)
981
+ let tipIncluded = false;
982
+
983
+ if (deps) {
984
+ Object.values(deps).forEach(d => {
985
+ const dAddr = getIbGibAddr({ ibGib: d });
986
+ if (!allDepsSet.has(dAddr)) {
987
+ allDepsSet.add(dAddr);
988
+ payloadIbGibs.push(d);
989
+ }
990
+ if (dAddr === tipAddr) { tipIncluded = true; }
991
+ });
992
+ }
993
+
994
+ if (!tipIncluded && !skipAddrs.has(tipAddr)) {
995
+ if (logalot) { console.log(`${lc} Tip not in deps, adding explicitly: ${tipAddr}`); }
996
+ if (!allDepsSet.has(tipAddr)) {
997
+ allDepsSet.add(tipAddr);
998
+ payloadIbGibs.push(tip);
999
+ }
1000
+ }
1001
+ }
1002
+
839
1003
  // 3. Create Delta Frame
840
1004
  const sagaId = ackData.sagaId;
841
1005
  const deltaData: SyncSagaMessageDeltaData_V1 = {
@@ -42,6 +42,11 @@ export interface SyncSagaMessageAckData_V1 extends SyncSagaMessageData_V1 {
42
42
  stage: typeof SyncStage.ack;
43
43
  deltaReqAddrs: string[];
44
44
  pushOfferAddrs: string[];
45
+ /**
46
+ * Map of group keys (TJP or Constant Addr) to list of known addresses (Full History).
47
+ * Used by Sender to calculate differential payloads.
48
+ */
49
+ knowledgeVector?: { [groupKey: string]: string[] };
45
50
  }
46
51
 
47
52
  export interface SyncSagaMessageDeltaData_V1 extends SyncSagaMessageData_V1, SyncDeltaData {