@indexnetwork/protocol 6.11.1-rc.402.1 → 6.12.0-rc.403.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 (33) hide show
  1. package/dist/negotiation/negotiation.agent.d.ts +10 -0
  2. package/dist/negotiation/negotiation.agent.d.ts.map +1 -1
  3. package/dist/negotiation/negotiation.agent.js +26 -8
  4. package/dist/negotiation/negotiation.agent.js.map +1 -1
  5. package/dist/negotiation/negotiation.attribution.d.ts +92 -0
  6. package/dist/negotiation/negotiation.attribution.d.ts.map +1 -0
  7. package/dist/negotiation/negotiation.attribution.js +110 -0
  8. package/dist/negotiation/negotiation.attribution.js.map +1 -0
  9. package/dist/negotiation/negotiation.graph.d.ts +19 -0
  10. package/dist/negotiation/negotiation.graph.d.ts.map +1 -1
  11. package/dist/negotiation/negotiation.graph.js +128 -6
  12. package/dist/negotiation/negotiation.graph.js.map +1 -1
  13. package/dist/negotiation/negotiation.screen.d.ts +24 -0
  14. package/dist/negotiation/negotiation.screen.d.ts.map +1 -1
  15. package/dist/negotiation/negotiation.screen.js +20 -1
  16. package/dist/negotiation/negotiation.screen.js.map +1 -1
  17. package/dist/negotiation/negotiation.state.d.ts +33 -0
  18. package/dist/negotiation/negotiation.state.d.ts.map +1 -1
  19. package/dist/negotiation/negotiation.state.js +33 -0
  20. package/dist/negotiation/negotiation.state.js.map +1 -1
  21. package/dist/opportunity/opportunity.evidence.d.ts.map +1 -1
  22. package/dist/opportunity/opportunity.evidence.js +8 -1
  23. package/dist/opportunity/opportunity.evidence.js.map +1 -1
  24. package/dist/opportunity/opportunity.graph.d.ts.map +1 -1
  25. package/dist/opportunity/opportunity.graph.js +91 -4
  26. package/dist/opportunity/opportunity.graph.js.map +1 -1
  27. package/dist/shared/interfaces/agent-dispatcher.interface.d.ts +8 -0
  28. package/dist/shared/interfaces/agent-dispatcher.interface.d.ts.map +1 -1
  29. package/dist/shared/interfaces/agent-dispatcher.interface.js.map +1 -1
  30. package/dist/shared/interfaces/database.interface.d.ts +27 -2
  31. package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
  32. package/dist/shared/interfaces/database.interface.js.map +1 -1
  33. package/package.json +1 -1
@@ -55,6 +55,27 @@ const negotiateExistingLog = protocolLogger('OpportunityGraph:NegotiateExisting'
55
55
  const routingLog = protocolLogger('OpportunityGraph:Routing');
56
56
  /** Time window for persist-node dedup. Suppresses a second opportunity with the same person while a recent one (within 30 days) is still in flight, so a person is not re-surfaced multiple times within a month (EDG-23). */
57
57
  const DEDUP_WINDOW_MS = 30 * 24 * 60 * 60 * 1000;
58
+ /**
59
+ * IND-567: Cool-down window (ms) for cross-query rejection suppression.
60
+ * Candidates with a recently rejected or stalled opportunity within this window
61
+ * receive a similarity penalty during evaluation ranking. Default 7 days.
62
+ * Override with DISCOVERY_REJECTION_COOLDOWN_DAYS (positive float).
63
+ */
64
+ const DEFAULT_REJECTION_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000;
65
+ function getRejectionCooldownMs() {
66
+ const raw = process.env.DISCOVERY_REJECTION_COOLDOWN_DAYS;
67
+ if (!raw)
68
+ return DEFAULT_REJECTION_COOLDOWN_MS;
69
+ const n = Number.parseFloat(raw);
70
+ return Number.isFinite(n) && n > 0 ? Math.round(n * 24 * 60 * 60 * 1000) : DEFAULT_REJECTION_COOLDOWN_MS;
71
+ }
72
+ /**
73
+ * Similarity multiplier applied to candidates that fall within the rejection
74
+ * cool-down window (IND-567). 0.5 halves their ranking score, typically
75
+ * pushing them below the evaluation-batch cut while leaving a soft trace in
76
+ * the trace log rather than silently dropping them.
77
+ */
78
+ const REJECTION_COOLDOWN_SIMILARITY_PENALTY = 0.5;
58
79
  const NEGOTIATION_INTENT_LIMIT = 5;
59
80
  const ACTIVE_NEGOTIATION_TASK_STATES = new Set([
60
81
  'submitted',
@@ -1449,8 +1470,45 @@ export class OpportunityGraphFactory {
1449
1470
  removed: sortedCandidates.length - dedupedCandidates.length,
1450
1471
  });
1451
1472
  }
1452
- const batchToEvaluate = eligibleCandidates.slice(0, EVAL_BATCH_SIZE);
1453
- const remaining = eligibleCandidates.slice(EVAL_BATCH_SIZE);
1473
+ // ── IND-567: Rejection cool-down penalty ──────────────────────────
1474
+ // Candidates with a recently rejected or stalled opportunity receive a
1475
+ // similarity penalty so they are ranked lower (and often pushed out of
1476
+ // the evaluation batch). This prevents cross-query re-surfacing of
1477
+ // false-positive matches that were already caught downstream.
1478
+ // The persist-node dedup is still the hard gate; this is a soft guard
1479
+ // that reduces evaluator cost and LLM false-positive rate.
1480
+ const rejectionCooldownIds = new Set();
1481
+ if (eligibleCandidates.length > 0
1482
+ && typeof this.database.getRecentlyRejectedOpportunityCounterparties === 'function') {
1483
+ try {
1484
+ const cooldownMs = getRejectionCooldownMs();
1485
+ const ids = await this.database.getRecentlyRejectedOpportunityCounterparties(discoveryUserId, eligibleCandidates.map((c) => c.candidateUserId), cooldownMs);
1486
+ for (const id of ids)
1487
+ rejectionCooldownIds.add(id);
1488
+ if (rejectionCooldownIds.size > 0) {
1489
+ evaluationLog.info('IND-567 rejection cool-down: applying similarity penalty', {
1490
+ affectedCount: rejectionCooldownIds.size,
1491
+ cooldownDays: Math.round(cooldownMs / (24 * 60 * 60 * 1000)),
1492
+ penalty: REJECTION_COOLDOWN_SIMILARITY_PENALTY,
1493
+ });
1494
+ }
1495
+ }
1496
+ catch (err) {
1497
+ evaluationLog.warn('IND-567 rejection cool-down: lookup failed, skipping penalty', {
1498
+ error: err instanceof Error ? err.message : String(err),
1499
+ });
1500
+ }
1501
+ }
1502
+ // Apply penalty and re-sort so penalised candidates fall to the back.
1503
+ const eligibleCandidatesAfterCooldown = rejectionCooldownIds.size > 0
1504
+ ? eligibleCandidates
1505
+ .map((c) => rejectionCooldownIds.has(c.candidateUserId)
1506
+ ? { ...c, similarity: c.similarity * REJECTION_COOLDOWN_SIMILARITY_PENALTY }
1507
+ : c)
1508
+ .sort((a, b) => b.similarity - a.similarity)
1509
+ : eligibleCandidates;
1510
+ const batchToEvaluate = eligibleCandidatesAfterCooldown.slice(0, EVAL_BATCH_SIZE);
1511
+ const remaining = eligibleCandidatesAfterCooldown.slice(EVAL_BATCH_SIZE);
1454
1512
  // Early termination: if search was query-driven and no query-sourced candidates remain,
1455
1513
  // clear remaining to prevent pointless pagination through non-query leftovers
1456
1514
  const isQueryDriven = !!state.searchQuery?.trim();
@@ -1500,6 +1558,35 @@ export class OpportunityGraphFactory {
1500
1558
  intentSummary = intent.summary ?? undefined;
1501
1559
  }
1502
1560
  }
1561
+ // IND-567 Fix A: fetch premise text for query_premise candidates.
1562
+ // The query-path sets candidatePayload='' for premise hits because
1563
+ // the vector-search result only carries a premise ID, not its text.
1564
+ // Without the text, renderOpportunityEvidenceForPrompt emits a line
1565
+ // with no domain content, letting the evaluator score on lens label
1566
+ // alone — which produces cross-domain false positives at confidence 1.0.
1567
+ // Mirror the getIntent fetch pattern: populate the evidence assertionText
1568
+ // from the DB so the evaluator can see the candidate's actual claim.
1569
+ let candidateEvidence = c.evidence;
1570
+ if (c.candidatePremiseId != null
1571
+ && c.candidateIntentId == null
1572
+ && (!c.candidatePayload || c.candidatePayload === '')
1573
+ && typeof this.database.getPremise === 'function') {
1574
+ try {
1575
+ const premise = await this.database.getPremise(c.candidatePremiseId);
1576
+ const assertionText = premise?.assertion?.text;
1577
+ if (assertionText) {
1578
+ candidateEvidence = (c.evidence ?? []).map((ev) => ev.kind === 'query_premise' && ev.candidatePremiseId === c.candidatePremiseId
1579
+ ? { ...ev, payload: assertionText, assertionText }
1580
+ : ev);
1581
+ }
1582
+ }
1583
+ catch (premiseFetchErr) {
1584
+ evaluationLog.warn('IND-567: failed to fetch premise text for evaluator', {
1585
+ candidatePremiseId: c.candidatePremiseId,
1586
+ error: premiseFetchErr instanceof Error ? premiseFetchErr.message : String(premiseFetchErr),
1587
+ });
1588
+ }
1589
+ }
1503
1590
  const evidenceKey = buildEvaluatorEvidenceKey(c);
1504
1591
  return {
1505
1592
  userId: c.candidateUserId,
@@ -1516,7 +1603,7 @@ export class OpportunityGraphFactory {
1516
1603
  evidenceKey,
1517
1604
  ragScore: c.similarity * 100,
1518
1605
  matchedVia: c.lens,
1519
- evidence: c.evidence,
1606
+ evidence: candidateEvidence, // IND-567 Fix A: may carry populated assertionText
1520
1607
  };
1521
1608
  }));
1522
1609
  const userIdToIndexId = new Map();
@@ -1808,7 +1895,7 @@ export class OpportunityGraphFactory {
1808
1895
  // Only pass opportunities that passed the threshold to downstream nodes
1809
1896
  const passedOpportunities = evaluatedOpportunities.filter((o) => o.score >= minScore);
1810
1897
  return {
1811
- candidates: eligibleCandidates,
1898
+ candidates: eligibleCandidatesAfterCooldown,
1812
1899
  evaluatedOpportunities: passedOpportunities,
1813
1900
  remainingCandidates: effectiveRemaining,
1814
1901
  trace: traceEntries,