@indexnetwork/protocol 3.0.0 → 3.1.0-rc.248.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/index.d.ts +7 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/intent/intent.indexer.d.ts +3 -3
  6. package/dist/intent/intent.reconciler.d.ts +6 -6
  7. package/dist/intent/intent.tools.d.ts.map +1 -1
  8. package/dist/intent/intent.tools.js +9 -3
  9. package/dist/intent/intent.tools.js.map +1 -1
  10. package/dist/network/indexer/indexer.graph.d.ts +22 -11
  11. package/dist/network/indexer/indexer.graph.d.ts.map +1 -1
  12. package/dist/network/indexer/indexer.graph.js +47 -44
  13. package/dist/network/indexer/indexer.graph.js.map +1 -1
  14. package/dist/network/indexer/indexer.state.d.ts +3 -3
  15. package/dist/opportunity/opportunity.evaluator.d.ts +27 -9
  16. package/dist/opportunity/opportunity.evaluator.d.ts.map +1 -1
  17. package/dist/opportunity/opportunity.evaluator.js +9 -1
  18. package/dist/opportunity/opportunity.evaluator.js.map +1 -1
  19. package/dist/opportunity/opportunity.evidence.d.ts +22 -0
  20. package/dist/opportunity/opportunity.evidence.d.ts.map +1 -0
  21. package/dist/opportunity/opportunity.evidence.js +72 -0
  22. package/dist/opportunity/opportunity.evidence.js.map +1 -0
  23. package/dist/opportunity/opportunity.graph.d.ts +8 -7
  24. package/dist/opportunity/opportunity.graph.d.ts.map +1 -1
  25. package/dist/opportunity/opportunity.graph.js +74 -28
  26. package/dist/opportunity/opportunity.graph.js.map +1 -1
  27. package/dist/opportunity/opportunity.state.d.ts +10 -2
  28. package/dist/opportunity/opportunity.state.d.ts.map +1 -1
  29. package/dist/opportunity/opportunity.state.js.map +1 -1
  30. package/dist/premise/premise.graph.d.ts +14 -2
  31. package/dist/premise/premise.graph.d.ts.map +1 -1
  32. package/dist/premise/premise.graph.js +49 -20
  33. package/dist/premise/premise.graph.js.map +1 -1
  34. package/dist/premise/premise.indexer.d.ts +2 -2
  35. package/dist/premise/premise.state.d.ts +2 -0
  36. package/dist/premise/premise.state.d.ts.map +1 -1
  37. package/dist/premise/premise.state.js +8 -0
  38. package/dist/premise/premise.state.js.map +1 -1
  39. package/dist/shared/assignment/network-assignment.policy.d.ts +59 -0
  40. package/dist/shared/assignment/network-assignment.policy.d.ts.map +1 -0
  41. package/dist/shared/assignment/network-assignment.policy.js +101 -0
  42. package/dist/shared/assignment/network-assignment.policy.js.map +1 -0
  43. package/dist/shared/hyde/hyde.graph.d.ts +6 -6
  44. package/dist/shared/hyde/hyde.state.d.ts +2 -2
  45. package/dist/shared/interfaces/database.interface.d.ts +31 -17
  46. package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
  47. package/dist/shared/interfaces/database.interface.js.map +1 -1
  48. package/dist/shared/schemas/network-assignment.schema.d.ts +135 -0
  49. package/dist/shared/schemas/network-assignment.schema.d.ts.map +1 -0
  50. package/dist/shared/schemas/network-assignment.schema.js +55 -0
  51. package/dist/shared/schemas/network-assignment.schema.js.map +1 -0
  52. package/dist/shared/schemas/question.schema.d.ts +2 -2
  53. package/package.json +1 -1
@@ -28,6 +28,7 @@ import { protocolLogger, withCallLogging } from '../shared/observability/protoco
28
28
  import { timed } from '../shared/observability/performance.js';
29
29
  import { renderNetworkContext } from '../shared/network/metadata.renderer.js';
30
30
  import { requestContext } from "../shared/observability/request-context.js";
31
+ import { mergeOpportunityEvidence, withCandidateEvidence, withMatchedStrategies, } from './opportunity.evidence.js';
31
32
  const logger = protocolLogger('OpportunityGraph');
32
33
  /** Time window for persist-node dedup. Parallel jobs arrive within seconds; 10 min catches those while allowing new opportunities for long-connected pairs. */
33
34
  const DEDUP_WINDOW_MS = 10 * 60 * 1000;
@@ -43,6 +44,13 @@ function getSourcePremiseDiscoveryLimit() {
43
44
  const parsed = Number.parseInt(raw, 10);
44
45
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_SOURCE_PREMISE_DISCOVERY_LIMIT;
45
46
  }
47
+ function buildEvaluatorEvidenceKey(candidate) {
48
+ return [
49
+ candidate.candidateUserId,
50
+ candidate.networkId,
51
+ candidate.candidateIntentId ?? candidate.candidatePremiseId ?? candidate.sourceContextId ?? 'profile',
52
+ ].join(':');
53
+ }
46
54
  /**
47
55
  * Builds a compact text summary of the discoverer's profile and active intents
48
56
  * for use as profileContext in HyDE generation.
@@ -210,7 +218,7 @@ export class OpportunityGraphFactory {
210
218
  // BACKEND-5: thousands of parallel vector searches for premise-rich users.
211
219
  const sourcePremises = [];
212
220
  const contextToIntentEnabled = process.env.DISCOVERY_CONTEXT_TO_INTENT !== '0';
213
- const rawContexts = contextToIntentEnabled
221
+ const rawContexts = contextToIntentEnabled && typeof this.database.getUserContexts === 'function'
214
222
  ? await this.database.getUserContexts(discoveryUserId)
215
223
  : [];
216
224
  const sourceContexts = rawContexts
@@ -573,7 +581,7 @@ export class OpportunityGraphFactory {
573
581
  const intentNetworkIds = await this.database.getNetworkIdsForIntent(intent.id);
574
582
  const overlapping = sharedIndexIds.filter(id => intentNetworkIds.includes(id));
575
583
  for (const networkId of overlapping) {
576
- directCandidates.push({
584
+ directCandidates.push(withCandidateEvidence({
577
585
  candidateUserId: state.targetUserId,
578
586
  candidateIntentId: intent.id,
579
587
  networkId,
@@ -582,22 +590,21 @@ export class OpportunityGraphFactory {
582
590
  candidatePayload: intent.payload,
583
591
  candidateSummary: intent.summary ?? undefined,
584
592
  discoverySource: 'query',
585
- });
593
+ }));
586
594
  }
587
595
  }
588
596
  }
589
597
  // Always add a profile-level candidate (so evaluation runs even without intents)
590
598
  if (directCandidates.length === 0) {
591
- directCandidates.push({
599
+ directCandidates.push(withCandidateEvidence({
592
600
  candidateUserId: state.targetUserId,
593
- candidateIntentId: undefined,
594
601
  networkId: sharedIndexIds[0],
595
602
  similarity: 1.0,
596
603
  lens: 'explicit_mention',
597
604
  candidatePayload: '',
598
605
  candidateSummary: undefined,
599
606
  discoverySource: 'query',
600
- });
607
+ }));
601
608
  }
602
609
  logger.verbose('[Graph:Discovery] Direct candidates constructed', {
603
610
  count: directCandidates.length,
@@ -766,7 +773,7 @@ export class OpportunityGraphFactory {
766
773
  minScore,
767
774
  });
768
775
  for (const r of results.filter((x) => x.type === 'intent')) {
769
- all.push({
776
+ all.push(withCandidateEvidence({
770
777
  candidateUserId: r.userId,
771
778
  candidateIntentId: r.id,
772
779
  networkId: targetIndex.networkId,
@@ -775,10 +782,10 @@ export class OpportunityGraphFactory {
775
782
  candidatePayload: '',
776
783
  candidateSummary: undefined,
777
784
  discoverySource: 'query',
778
- });
785
+ }));
779
786
  }
780
787
  for (const r of results.filter((x) => x.type === 'premise')) {
781
- all.push({
788
+ all.push(withCandidateEvidence({
782
789
  candidateUserId: r.userId,
783
790
  candidatePremiseId: r.id,
784
791
  networkId: targetIndex.networkId,
@@ -787,7 +794,7 @@ export class OpportunityGraphFactory {
787
794
  candidatePayload: '',
788
795
  candidateSummary: undefined,
789
796
  discoverySource: 'query',
790
- });
797
+ }));
791
798
  }
792
799
  }));
793
800
  const intentCount = all.filter((c) => c.candidateIntentId).length;
@@ -847,23 +854,27 @@ export class OpportunityGraphFactory {
847
854
  excludeUserId: discoveryUserId,
848
855
  limitPerSource: PREMISE_MATCH_LIMIT_PER_SOURCE,
849
856
  })
850
- : (await Promise.all(sourcePremises.map(sp => self.database.searchPremisesBySimilarity({
851
- embedding: sp.embedding,
852
- networkIds: targetNetworkIds,
853
- excludeUserId: discoveryUserId,
854
- limit: PREMISE_MATCH_LIMIT_PER_SOURCE,
855
- })))).flat();
857
+ : (await Promise.all(sourcePremises.map(async (sp) => {
858
+ const results = await self.database.searchPremisesBySimilarity({
859
+ embedding: sp.embedding,
860
+ networkIds: targetNetworkIds,
861
+ excludeUserId: discoveryUserId,
862
+ limit: PREMISE_MATCH_LIMIT_PER_SOURCE,
863
+ });
864
+ return results.map((r) => ({ ...r, sourcePremiseId: sp.premiseId }));
865
+ }))).flat();
856
866
  const premiseCandidates = [];
857
867
  for (const r of rawResults) {
858
- premiseCandidates.push({
868
+ premiseCandidates.push(withCandidateEvidence({
859
869
  candidateUserId: r.userId,
870
+ sourcePremiseId: r.sourcePremiseId,
860
871
  candidatePremiseId: r.premiseId,
861
872
  networkId: r.networkId,
862
873
  similarity: typeof r.similarity === 'number' ? r.similarity : parseFloat(String(r.similarity)),
863
874
  lens: 'premise_match',
864
875
  candidatePayload: r.assertionText ?? '',
865
876
  discoverySource: 'premise-similarity',
866
- });
877
+ }));
867
878
  }
868
879
  // Dedup by userId + premiseId + networkId (a premise can appear in multiple networks)
869
880
  const byKey = new Map();
@@ -921,16 +932,17 @@ export class OpportunityGraphFactory {
921
932
  minScore,
922
933
  });
923
934
  for (const r of results.filter(r => r.type === 'intent')) {
924
- contextCandidates.push({
935
+ contextCandidates.push(withCandidateEvidence({
925
936
  candidateUserId: r.userId,
926
937
  candidateIntentId: r.id,
938
+ sourceContextId: ctx.contextId,
927
939
  networkId: ctx.networkId,
928
940
  similarity: r.score,
929
941
  lens: r.matchedVia,
930
942
  candidatePayload: '',
931
943
  candidateSummary: undefined,
932
944
  discoverySource: 'context-to-intent',
933
- });
945
+ }));
934
946
  }
935
947
  }
936
948
  else {
@@ -943,16 +955,17 @@ export class OpportunityGraphFactory {
943
955
  minScore: minScore,
944
956
  });
945
957
  for (const r of results) {
946
- contextCandidates.push({
958
+ contextCandidates.push(withCandidateEvidence({
947
959
  candidateUserId: r.userId,
948
960
  candidateIntentId: r.intentId,
961
+ sourceContextId: ctx.contextId,
949
962
  networkId: r.networkId,
950
963
  similarity: typeof r.similarity === 'number' ? r.similarity : parseFloat(String(r.similarity)),
951
964
  lens: 'context_match',
952
965
  candidatePayload: r.payload ?? '',
953
966
  candidateSummary: r.summary ?? undefined,
954
967
  discoverySource: 'context-to-intent',
955
- });
968
+ }));
956
969
  }
957
970
  }
958
971
  }
@@ -988,18 +1001,24 @@ export class OpportunityGraphFactory {
988
1001
  }
989
1002
  else {
990
1003
  existing._strategies.add(c.discoverySource ?? 'unknown');
1004
+ const mergedEvidence = mergeOpportunityEvidence(existing.evidence, c.evidence);
991
1005
  if (c.similarity > existing.similarity) {
992
- Object.assign(existing, c);
1006
+ Object.assign(existing, { ...c, evidence: mergedEvidence });
1007
+ }
1008
+ else {
1009
+ existing.evidence = mergedEvidence;
993
1010
  }
994
1011
  }
995
1012
  }
996
1013
  }
997
1014
  return Array.from(merged.values()).map(({ _strategies, ...c }) => {
1015
+ const matchedStrategies = Array.from(_strategies);
998
1016
  const boost = Math.min((_strategies.size - 1) * 0.05, 0.15);
999
1017
  return {
1000
1018
  ...c,
1001
1019
  similarity: Math.min(c.similarity + boost, 1.0),
1002
- matchedStrategies: Array.from(_strategies),
1020
+ matchedStrategies,
1021
+ evidence: withMatchedStrategies(mergeOpportunityEvidence(c.evidence), matchedStrategies),
1003
1022
  };
1004
1023
  });
1005
1024
  }
@@ -1068,7 +1087,7 @@ export class OpportunityGraphFactory {
1068
1087
  minScore,
1069
1088
  });
1070
1089
  for (const result of results.filter((r) => r.type === 'intent')) {
1071
- allCandidates.push({
1090
+ allCandidates.push(withCandidateEvidence({
1072
1091
  candidateUserId: result.userId,
1073
1092
  candidateIntentId: result.id,
1074
1093
  networkId: targetIndex.networkId,
@@ -1077,10 +1096,10 @@ export class OpportunityGraphFactory {
1077
1096
  candidatePayload: '',
1078
1097
  candidateSummary: undefined,
1079
1098
  discoverySource: 'query',
1080
- });
1099
+ }));
1081
1100
  }
1082
1101
  for (const result of results.filter((r) => r.type === 'premise')) {
1083
- allCandidates.push({
1102
+ allCandidates.push(withCandidateEvidence({
1084
1103
  candidateUserId: result.userId,
1085
1104
  candidatePremiseId: result.id,
1086
1105
  networkId: targetIndex.networkId,
@@ -1089,7 +1108,7 @@ export class OpportunityGraphFactory {
1089
1108
  candidatePayload: '',
1090
1109
  candidateSummary: undefined,
1091
1110
  discoverySource: 'query',
1092
- });
1111
+ }));
1093
1112
  }
1094
1113
  }));
1095
1114
  const byUserAndIndex = new Map();
@@ -1299,6 +1318,7 @@ export class OpportunityGraphFactory {
1299
1318
  summary: i.summary,
1300
1319
  })),
1301
1320
  networkId: '', // Placeholder — overwritten per-pairing below
1321
+ evidenceKey: `${discoveryUserId}::source`,
1302
1322
  ragScore: undefined,
1303
1323
  matchedVia: undefined,
1304
1324
  };
@@ -1313,6 +1333,7 @@ export class OpportunityGraphFactory {
1313
1333
  intentSummary = intent.summary ?? undefined;
1314
1334
  }
1315
1335
  }
1336
+ const evidenceKey = buildEvaluatorEvidenceKey(c);
1316
1337
  return {
1317
1338
  userId: c.candidateUserId,
1318
1339
  profile: {
@@ -1327,14 +1348,35 @@ export class OpportunityGraphFactory {
1327
1348
  ? [{ intentId: c.candidateIntentId, payload: intentPayload ?? '', summary: intentSummary }]
1328
1349
  : undefined,
1329
1350
  networkId: c.networkId,
1351
+ evidenceKey,
1330
1352
  ragScore: c.similarity * 100,
1331
1353
  matchedVia: c.lens,
1354
+ evidence: c.evidence,
1332
1355
  };
1333
1356
  }));
1334
1357
  const userIdToIndexId = new Map();
1358
+ const evidenceByEntityKey = new Map();
1359
+ const entityKeysByUserId = new Map();
1335
1360
  for (const e of candidateEntities) {
1336
1361
  if (!userIdToIndexId.has(e.userId))
1337
1362
  userIdToIndexId.set(e.userId, e.networkId);
1363
+ if (e.evidenceKey) {
1364
+ evidenceByEntityKey.set(e.evidenceKey, mergeOpportunityEvidence(evidenceByEntityKey.get(e.evidenceKey), e.evidence));
1365
+ entityKeysByUserId.set(e.userId, [...(entityKeysByUserId.get(e.userId) ?? []), e.evidenceKey]);
1366
+ }
1367
+ }
1368
+ function evidenceForActor(actor) {
1369
+ if (actor.evidenceKey)
1370
+ return evidenceByEntityKey.get(actor.evidenceKey);
1371
+ const keys = entityKeysByUserId.get(actor.userId) ?? [];
1372
+ const intentKey = actor.intentId ? keys.find((key) => key.endsWith(`:${actor.intentId}`)) : undefined;
1373
+ if (intentKey)
1374
+ return evidenceByEntityKey.get(intentKey);
1375
+ // Avoid leaking unrelated resource evidence when the evaluator collapsed multiple
1376
+ // candidates for the same user into a profile-only actor.
1377
+ if (keys.length === 1)
1378
+ return evidenceByEntityKey.get(keys[0]);
1379
+ return undefined;
1338
1380
  }
1339
1381
  // Lower default threshold to 50 for better recall
1340
1382
  const minScore = state.options.minScore ?? 50;
@@ -1495,6 +1537,7 @@ export class OpportunityGraphFactory {
1495
1537
  const evaluatedOpportunities = pairwiseOpportunities.map((op) => ({
1496
1538
  reasoning: op.reasoning,
1497
1539
  score: op.score,
1540
+ evidence: mergeOpportunityEvidence(...op.actors.map(evidenceForActor)),
1498
1541
  actors: op.actors.map((a) => {
1499
1542
  const isSource = a.userId === discoveryUserId;
1500
1543
  if (isSource) {
@@ -2682,6 +2725,9 @@ export class OpportunityGraphFactory {
2682
2725
  },
2683
2726
  confidence: String(evaluated.score / 100),
2684
2727
  status: initialStatus,
2728
+ metadata: {
2729
+ evidence: evaluated.evidence ?? [],
2730
+ },
2685
2731
  };
2686
2732
  }
2687
2733
  try {