@indexnetwork/protocol 14.3.2-rc.477.1 → 17.0.0-rc.479.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.
- package/CHANGELOG.md +141 -0
- package/IMPLEMENTATION.md +50 -52
- package/STABILITY.md +3 -3
- package/dist/chat/chat.agent.js +2 -3
- package/dist/chat/chat.graph.d.ts +41 -1
- package/dist/chat/chat.graph.js +108 -127
- package/dist/chat/negotiator.persona.d.ts +1 -1
- package/dist/chat/negotiator.persona.js +1 -4
- package/dist/chat/negotiator.prompt.js +1 -2
- package/dist/chat/onboarding.persona.js +0 -1
- package/dist/chat/reporter.persona.js +0 -1
- package/dist/chat/signal.persona.js +0 -1
- package/dist/contacts/application/contact.tools.d.ts +6 -9
- package/dist/contacts/application/contact.tools.js +11 -92
- package/dist/contacts/application/index.d.ts +2 -14
- package/dist/contacts/application/index.js +2 -13
- package/dist/contacts/domain/contact.types.d.ts +0 -23
- package/dist/contacts/domain/index.d.ts +2 -6
- package/dist/contacts/index.d.ts +1 -1
- package/dist/contacts/index.js +1 -1
- package/dist/contacts/ports/contact.repository.port.d.ts +5 -12
- package/dist/contacts/ports/contact.repository.port.js +4 -6
- package/dist/contacts/ports/contact.tools.port.d.ts +1 -3
- package/dist/contacts/ports/contact.tools.port.js +1 -2
- package/dist/contacts/ports/index.d.ts +1 -1
- package/dist/contacts/ports/index.js +1 -1
- package/dist/discovery/hyde.frame.d.ts +4 -4
- package/dist/discovery/hyde.graph.d.ts +71 -12
- package/dist/discovery/hyde.graph.js +337 -335
- package/dist/discovery/lens.inferrer.d.ts +6 -6
- package/dist/enrichment/enrichment.graph.d.ts +127 -9
- package/dist/enrichment/enrichment.graph.js +633 -655
- package/dist/enrichment/enrichment.state.d.ts +1 -1
- package/dist/enrichment/enrichment.tools.context-read.d.ts +10 -0
- package/dist/enrichment/enrichment.tools.context-read.js +353 -0
- package/dist/enrichment/enrichment.tools.context-write.d.ts +9 -0
- package/dist/enrichment/enrichment.tools.context-write.js +417 -0
- package/dist/enrichment/enrichment.tools.d.ts +11 -1
- package/dist/enrichment/enrichment.tools.helpers.d.ts +139 -0
- package/dist/enrichment/enrichment.tools.helpers.js +234 -0
- package/dist/enrichment/enrichment.tools.js +16 -975
- package/dist/index.d.ts +27 -50
- package/dist/index.js +17 -28
- package/dist/intents/application/intent.graph.d.ts +71 -67
- package/dist/intents/application/intent.graph.execute.d.ts +59 -0
- package/dist/intents/application/intent.graph.execute.js +301 -0
- package/dist/intents/application/intent.graph.infer.d.ts +44 -0
- package/dist/intents/application/intent.graph.infer.js +97 -0
- package/dist/intents/application/intent.graph.js +96 -888
- package/dist/intents/application/intent.graph.reconcile.d.ts +71 -0
- package/dist/intents/application/intent.graph.reconcile.js +274 -0
- package/dist/intents/application/intent.graph.shared.d.ts +70 -0
- package/dist/intents/application/intent.graph.shared.js +153 -0
- package/dist/intents/domain/intent.state.d.ts +1 -1
- package/dist/maintenance/maintenance.graph.d.ts +66 -3
- package/dist/maintenance/maintenance.graph.js +155 -156
- package/dist/mcp/mcp.authorization-policy.d.ts +24 -28
- package/dist/mcp/mcp.authorization-policy.js +14 -34
- package/dist/mcp/mcp.server.d.ts +3 -3
- package/dist/mcp/mcp.server.js +14 -20
- package/dist/negotiations/application/negotiation.candidates.d.ts +83 -0
- package/dist/negotiations/application/negotiation.candidates.js +162 -0
- package/dist/negotiations/application/negotiation.graph.d.ts +91 -157
- package/dist/negotiations/application/negotiation.graph.finalize.d.ts +5 -0
- package/dist/negotiations/application/negotiation.graph.finalize.js +280 -0
- package/dist/negotiations/application/negotiation.graph.init.d.ts +54 -0
- package/dist/negotiations/application/negotiation.graph.init.js +227 -0
- package/dist/negotiations/application/negotiation.graph.js +69 -1388
- package/dist/negotiations/application/negotiation.graph.screen.d.ts +23 -0
- package/dist/negotiations/application/negotiation.graph.screen.js +108 -0
- package/dist/negotiations/application/negotiation.graph.shared.d.ts +75 -0
- package/dist/negotiations/application/negotiation.graph.shared.js +125 -0
- package/dist/negotiations/application/negotiation.graph.turn.d.ts +105 -0
- package/dist/negotiations/application/negotiation.graph.turn.js +484 -0
- package/dist/negotiations/domain/negotiation.state.d.ts +1 -1
- package/dist/negotiations/domain/negotiation.state.js +0 -1
- package/dist/networks/application/indexer.graph.d.ts +165 -7
- package/dist/networks/application/indexer.graph.js +339 -388
- package/dist/networks/application/indexer.state.d.ts +1 -1
- package/dist/networks/application/membership.graph.d.ts +83 -5
- package/dist/networks/application/membership.graph.js +177 -207
- package/dist/networks/application/network.graph.d.ts +148 -5
- package/dist/networks/application/network.graph.js +249 -278
- package/dist/networks/domain/membership.state.d.ts +1 -1
- package/dist/networks/domain/network.state.d.ts +1 -1
- package/dist/opportunities/application/delivery-card.cache.d.ts +1 -1
- package/dist/opportunities/application/delivery-card.cache.js +2 -2
- package/dist/opportunities/application/index.d.ts +2 -2
- package/dist/opportunities/application/index.js +2 -2
- package/dist/opportunities/application/opportunity.evaluator.js +1 -1
- package/dist/opportunities/application/opportunity.graph.d.ts +686 -473
- package/dist/opportunities/application/opportunity.graph.discovery-strategies.d.ts +109 -0
- package/dist/opportunities/application/opportunity.graph.discovery-strategies.js +451 -0
- package/dist/opportunities/application/opportunity.graph.discovery.d.ts +69 -0
- package/dist/opportunities/application/opportunity.graph.discovery.js +397 -0
- package/dist/opportunities/application/opportunity.graph.evaluation.d.ts +82 -0
- package/dist/opportunities/application/opportunity.graph.evaluation.js +608 -0
- package/dist/opportunities/application/opportunity.graph.js +120 -3690
- package/dist/opportunities/application/opportunity.graph.modes.d.ts +538 -0
- package/dist/opportunities/application/opportunity.graph.modes.js +538 -0
- package/dist/opportunities/application/opportunity.graph.negotiate.d.ts +109 -0
- package/dist/opportunities/application/opportunity.graph.negotiate.js +392 -0
- package/dist/opportunities/application/opportunity.graph.persist-node.d.ts +93 -0
- package/dist/opportunities/application/opportunity.graph.persist-node.js +765 -0
- package/dist/opportunities/application/opportunity.graph.prep.d.ts +150 -0
- package/dist/opportunities/application/opportunity.graph.prep.js +381 -0
- package/dist/opportunities/application/opportunity.graph.shared.d.ts +173 -0
- package/dist/opportunities/application/opportunity.graph.shared.js +199 -0
- package/dist/opportunities/application/opportunity.presentation.d.ts +399 -0
- package/dist/opportunities/application/{opportunity.presenter.js → opportunity.presentation.js} +735 -9
- package/dist/opportunities/application/opportunity.tools.cards.d.ts +152 -0
- package/dist/opportunities/application/opportunity.tools.cards.js +235 -0
- package/dist/opportunities/application/opportunity.tools.d.ts +8 -114
- package/dist/opportunities/application/opportunity.tools.js +28 -711
- package/dist/opportunities/application/opportunity.tools.list.d.ts +10 -0
- package/dist/opportunities/application/opportunity.tools.list.js +493 -0
- package/dist/opportunities/domain/index.d.ts +3 -3
- package/dist/opportunities/domain/index.js +3 -3
- package/dist/opportunities/domain/opportunity.state.d.ts +19 -19
- package/dist/opportunities/index.d.ts +6 -6
- package/dist/opportunities/index.js +4 -4
- package/dist/opportunities/ports/opportunity.tools.port.d.ts +1 -1
- package/dist/opportunities/radar/radar.graph.d.ts +57 -13
- package/dist/opportunities/radar/radar.graph.js +470 -471
- package/dist/premises/premise.graph.d.ts +88 -20
- package/dist/premises/premise.graph.js +207 -218
- package/dist/questions/domain/question.schema.d.ts +40 -40
- package/dist/shared/agent/tool.factory.js +0 -8
- package/dist/shared/agent/tool.helpers.d.ts +21 -45
- package/dist/shared/agent/tool.helpers.js +1 -2
- package/dist/shared/agent/tool.registry.d.ts +4 -3
- package/dist/shared/agent/tool.registry.js +2 -4
- package/dist/shared/agent/tool.runtime.js +0 -2
- package/dist/shared/agent/utility.tools.js +9 -13
- package/dist/shared/interfaces/database.capabilities.d.ts +100 -0
- package/dist/shared/interfaces/database.capabilities.js +7 -0
- package/dist/shared/interfaces/database.entities.d.ts +533 -0
- package/dist/shared/interfaces/database.entities.js +10 -0
- package/dist/shared/interfaces/database.identity-queries.d.ts +294 -0
- package/dist/shared/interfaces/database.identity-queries.js +4 -0
- package/dist/shared/interfaces/database.interface.d.ts +15 -2277
- package/dist/shared/interfaces/database.interface.js +8 -0
- package/dist/shared/interfaces/database.member-queries.d.ts +221 -0
- package/dist/shared/interfaces/database.member-queries.js +4 -0
- package/dist/shared/interfaces/database.negotiation.d.ts +305 -0
- package/dist/shared/interfaces/database.negotiation.js +26 -0
- package/dist/shared/interfaces/database.network-queries.d.ts +277 -0
- package/dist/shared/interfaces/database.network-queries.js +4 -0
- package/dist/shared/interfaces/database.opportunity-queries.d.ts +285 -0
- package/dist/shared/interfaces/database.opportunity-queries.js +4 -0
- package/dist/shared/interfaces/database.port.d.ts +322 -0
- package/dist/shared/interfaces/database.port.js +29 -0
- package/dist/shared/schemas/discovery-question.schema.d.ts +14 -14
- package/dist/shared/schemas/negotiation-digest.schema.d.ts +2 -2
- package/package.json +2 -15
- package/dist/contacts/application/contact.inviter.d.ts +0 -49
- package/dist/contacts/application/contact.inviter.js +0 -64
- package/dist/integrations/application/index.d.ts +0 -14
- package/dist/integrations/application/index.js +0 -14
- package/dist/integrations/application/integration.tools.d.ts +0 -27
- package/dist/integrations/application/integration.tools.js +0 -98
- package/dist/integrations/domain/index.d.ts +0 -16
- package/dist/integrations/domain/index.js +0 -1
- package/dist/integrations/domain/integration.types.d.ts +0 -51
- package/dist/integrations/domain/integration.types.js +0 -9
- package/dist/integrations/index.d.ts +0 -10
- package/dist/integrations/index.js +0 -8
- package/dist/integrations/ports/index.d.ts +0 -25
- package/dist/integrations/ports/index.js +0 -23
- package/dist/integrations/ports/integration.adapter.port.d.ts +0 -62
- package/dist/integrations/ports/integration.adapter.port.js +0 -15
- package/dist/integrations/ports/integration.importer.port.d.ts +0 -31
- package/dist/integrations/ports/integration.importer.port.js +0 -15
- package/dist/integrations/ports/integration.tools.port.d.ts +0 -25
- package/dist/integrations/ports/integration.tools.port.js +0 -18
- package/dist/opportunities/application/opportunity.card-presentation.d.ts +0 -44
- package/dist/opportunities/application/opportunity.card-presentation.js +0 -93
- package/dist/opportunities/application/opportunity.presenter.d.ts +0 -156
- package/dist/opportunities/domain/opportunity.presentation-cache.d.ts +0 -5
- package/dist/opportunities/domain/opportunity.presentation-cache.js +0 -11
- package/dist/opportunities/domain/opportunity.presentation.d.ts +0 -76
- package/dist/opportunities/domain/opportunity.presentation.js +0 -516
- package/dist/opportunities/domain/opportunity.safe-presentation.d.ts +0 -104
- package/dist/opportunities/domain/opportunity.safe-presentation.js +0 -102
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery pipeline, stage 3: candidate retrieval.
|
|
3
|
+
*
|
|
4
|
+
* Three shapes share this node — a direct-connection fast path, a
|
|
5
|
+
* context-sourced path, and the intent/query HyDE path. The retrieval
|
|
6
|
+
* strategies themselves live in `opportunity.graph.discovery-strategies.ts`.
|
|
7
|
+
*/
|
|
8
|
+
import { getModelName } from '../../shared/agent/model.config.js';
|
|
9
|
+
import { timed } from '../../shared/observability/performance.js';
|
|
10
|
+
import { withCandidateEvidence } from '../domain/opportunity.evidence.js';
|
|
11
|
+
import { discoveryIntentMatchingEnabled, discoveryProfileMatchingEnabled, discoveryProfileSource } from '../discovery.env.js';
|
|
12
|
+
import { buildDiscovererContext, discoveryLog } from './opportunity.graph.shared.js';
|
|
13
|
+
import { auxiliaryStrategyTraces, collectHydeResults, computeLensStats, mergeStrategyCandidates, runAuxiliaryStrategies, runQueryHydeDiscovery, toLensEmbeddings } from './opportunity.graph.discovery-strategies.js';
|
|
14
|
+
// Search limits - fixed values for candidate retrieval
|
|
15
|
+
// (The options.limit controls final output, not search pool)
|
|
16
|
+
const LIMIT_PER_STRATEGY = 30;
|
|
17
|
+
const PER_INDEX_LIMIT = 80;
|
|
18
|
+
/**
|
|
19
|
+
* Node 3: Discovery
|
|
20
|
+
* Generates HyDE embeddings and performs semantic search.
|
|
21
|
+
*/
|
|
22
|
+
export async function discoveryNode(state, deps) {
|
|
23
|
+
return timed("OpportunityGraph.discovery", async () => {
|
|
24
|
+
const startTime = Date.now();
|
|
25
|
+
const discoveryUserId = state.onBehalfOfUserId ?? state.userId;
|
|
26
|
+
/** Filter candidates to targetUserId when set (direct-connection mode). */
|
|
27
|
+
const filterByTarget = (candidates) => {
|
|
28
|
+
if (!state.targetUserId)
|
|
29
|
+
return candidates;
|
|
30
|
+
const filtered = candidates.filter(c => c.candidateUserId === state.targetUserId);
|
|
31
|
+
discoveryLog.verbose('targetUserId filter applied', {
|
|
32
|
+
targetUserId: state.targetUserId,
|
|
33
|
+
before: candidates.length,
|
|
34
|
+
after: filtered.length,
|
|
35
|
+
});
|
|
36
|
+
return filtered;
|
|
37
|
+
};
|
|
38
|
+
discoveryLog.verbose('Starting semantic search', {
|
|
39
|
+
targetIndexesCount: state.targetNetworks.length,
|
|
40
|
+
discoverySource: state.discoverySource,
|
|
41
|
+
searchQueryPreview: state.searchQuery?.trim().slice(0, 60) ?? '(none)',
|
|
42
|
+
});
|
|
43
|
+
try {
|
|
44
|
+
if (state.targetNetworks.length === 0) {
|
|
45
|
+
discoveryLog.warn('No target indexes for search');
|
|
46
|
+
return { candidates: [] };
|
|
47
|
+
}
|
|
48
|
+
if (state.targetUserId) {
|
|
49
|
+
return await discoverDirectConnection(state, deps, discoveryUserId, startTime);
|
|
50
|
+
}
|
|
51
|
+
// Discovery match-type gating (DISCOVERY_ALLOWED_TYPES / DISCOVERY_PROFILE_SOURCE).
|
|
52
|
+
const ctx = {
|
|
53
|
+
state,
|
|
54
|
+
deps,
|
|
55
|
+
discoveryUserId,
|
|
56
|
+
limitPerStrategy: LIMIT_PER_STRATEGY,
|
|
57
|
+
perIndexLimit: PER_INDEX_LIMIT,
|
|
58
|
+
corpusGating: {
|
|
59
|
+
intents: discoveryIntentMatchingEnabled(),
|
|
60
|
+
profile: discoveryProfileMatchingEnabled(),
|
|
61
|
+
profileCorpus: discoveryProfileSource(),
|
|
62
|
+
},
|
|
63
|
+
intentResultsEnabled: discoveryIntentMatchingEnabled(),
|
|
64
|
+
premiseResultsEnabled: discoveryProfileMatchingEnabled() && discoveryProfileSource() === 'premise',
|
|
65
|
+
contextResultsEnabled: discoveryProfileMatchingEnabled() && discoveryProfileSource() === 'user_context',
|
|
66
|
+
};
|
|
67
|
+
if (state.discoverySource === 'context') {
|
|
68
|
+
return await discoverFromContext(ctx, filterByTarget, startTime);
|
|
69
|
+
}
|
|
70
|
+
return await discoverFromIntent(ctx, filterByTarget, startTime);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
74
|
+
discoveryLog.error('Failed', { error });
|
|
75
|
+
return {
|
|
76
|
+
candidates: [],
|
|
77
|
+
error: 'Failed to search for candidates.',
|
|
78
|
+
trace: [{
|
|
79
|
+
node: "discovery_fatal",
|
|
80
|
+
detail: `Discovery failed: ${errMsg}`,
|
|
81
|
+
data: { error: errMsg },
|
|
82
|
+
}],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Direct-connection fast path.
|
|
89
|
+
* When targetUserId is set (user @-mentioned someone), bypass vector search
|
|
90
|
+
* and construct candidates directly from shared networks.
|
|
91
|
+
*/
|
|
92
|
+
async function discoverDirectConnection(state, deps, discoveryUserId, startTime) {
|
|
93
|
+
const targetUserId = state.targetUserId;
|
|
94
|
+
if (targetUserId === discoveryUserId) {
|
|
95
|
+
discoveryLog.warn('Direct-connection target matches discoverer; skipping self-match', { targetUserId });
|
|
96
|
+
return {
|
|
97
|
+
candidates: [],
|
|
98
|
+
trace: [{
|
|
99
|
+
node: "discovery",
|
|
100
|
+
detail: "Direct connection skipped: target user is discoverer",
|
|
101
|
+
data: { targetUserId },
|
|
102
|
+
}],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
discoveryLog.verbose('Direct-connection mode — bypassing vector search', { targetUserId });
|
|
106
|
+
const targetMemberships = await deps.database.getNetworkMemberships(targetUserId);
|
|
107
|
+
const targetUserIndexIds = targetMemberships.map(m => m.networkId);
|
|
108
|
+
const sharedIndexIds = state.targetNetworks
|
|
109
|
+
.filter(ti => targetUserIndexIds.includes(ti.networkId))
|
|
110
|
+
.map(ti => ti.networkId);
|
|
111
|
+
if (sharedIndexIds.length === 0) {
|
|
112
|
+
discoveryLog.warn('Target user shares no indexes with discoverer', {
|
|
113
|
+
targetUserId,
|
|
114
|
+
discovererIndexes: state.targetNetworks.map(ti => ti.networkId),
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
candidates: [],
|
|
118
|
+
trace: [{
|
|
119
|
+
node: "discovery",
|
|
120
|
+
detail: `Direct connection: target user shares no indexes`,
|
|
121
|
+
data: { targetUserId },
|
|
122
|
+
}],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// Fetch target user's active intents to build intent-level candidates
|
|
126
|
+
const targetIntents = await deps.database.getActiveIntents(targetUserId);
|
|
127
|
+
const directCandidates = [];
|
|
128
|
+
if (targetIntents.length > 0) {
|
|
129
|
+
// Build one candidate per intent per shared network it belongs to
|
|
130
|
+
for (const intent of targetIntents) {
|
|
131
|
+
const intentNetworkIds = await deps.database.getNetworkIdsForIntent(intent.id);
|
|
132
|
+
const overlapping = sharedIndexIds.filter(id => intentNetworkIds.includes(id));
|
|
133
|
+
for (const networkId of overlapping) {
|
|
134
|
+
directCandidates.push(withCandidateEvidence({
|
|
135
|
+
candidateUserId: targetUserId,
|
|
136
|
+
candidateIntentId: intent.id,
|
|
137
|
+
networkId,
|
|
138
|
+
similarity: 1.0,
|
|
139
|
+
lens: 'explicit_mention',
|
|
140
|
+
candidatePayload: intent.payload,
|
|
141
|
+
candidateSummary: intent.summary ?? undefined,
|
|
142
|
+
discoverySource: 'query',
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Always add a profile-level candidate (so evaluation runs even without intents)
|
|
148
|
+
if (directCandidates.length === 0) {
|
|
149
|
+
directCandidates.push(withCandidateEvidence({
|
|
150
|
+
candidateUserId: targetUserId,
|
|
151
|
+
networkId: sharedIndexIds[0],
|
|
152
|
+
similarity: 1.0,
|
|
153
|
+
lens: 'explicit_mention',
|
|
154
|
+
candidatePayload: '',
|
|
155
|
+
candidateSummary: undefined,
|
|
156
|
+
discoverySource: 'query',
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
discoveryLog.verbose('Direct candidates constructed', {
|
|
160
|
+
count: directCandidates.length,
|
|
161
|
+
sharedIndexes: sharedIndexIds.length,
|
|
162
|
+
targetIntents: targetIntents.length,
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
candidates: directCandidates,
|
|
166
|
+
trace: [{
|
|
167
|
+
node: "discovery",
|
|
168
|
+
detail: `Direct connection → ${directCandidates.length} candidate(s) from ${sharedIndexIds.length} shared network(es)`,
|
|
169
|
+
data: {
|
|
170
|
+
targetUserId,
|
|
171
|
+
candidateCount: directCandidates.length,
|
|
172
|
+
sharedIndexes: sharedIndexIds.length,
|
|
173
|
+
durationMs: Date.now() - startTime,
|
|
174
|
+
},
|
|
175
|
+
}],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Context source: HyDE (when a search query exists) plus the additive
|
|
180
|
+
* premise/context strategies.
|
|
181
|
+
*/
|
|
182
|
+
async function discoverFromContext(ctx, filterByTarget, startTime) {
|
|
183
|
+
const { state } = ctx;
|
|
184
|
+
if (state.searchQuery?.trim()) {
|
|
185
|
+
discoveryLog.verbose('Context source with searchQuery → running query HyDE + premise paths', {
|
|
186
|
+
searchQuery: state.searchQuery.trim().substring(0, 80),
|
|
187
|
+
});
|
|
188
|
+
const queryResult = await runQueryHydeDiscovery(ctx);
|
|
189
|
+
const queryCandidates = queryResult?.candidates ?? [];
|
|
190
|
+
discoveryLog.verbose('Query HyDE path complete', { candidatesFound: queryCandidates.length });
|
|
191
|
+
const traceEntries = [];
|
|
192
|
+
// Lens input trace (captured from runQueryHydeDiscovery)
|
|
193
|
+
if (queryResult) {
|
|
194
|
+
traceEntries.push({
|
|
195
|
+
node: "lens_input",
|
|
196
|
+
detail: "Profile context for lens inference",
|
|
197
|
+
data: queryResult.lensInput,
|
|
198
|
+
});
|
|
199
|
+
// Lens output and HyDE document traces
|
|
200
|
+
if (queryResult.hydeOutput.lenses.length > 0) {
|
|
201
|
+
traceEntries.push({
|
|
202
|
+
node: "lens_output",
|
|
203
|
+
detail: `Inferred ${queryResult.hydeOutput.lenses.length} lens(es): ${queryResult.hydeOutput.lenses.map(l => l.label).join(', ')}`,
|
|
204
|
+
data: { lenses: queryResult.hydeOutput.lenses, model: getModelName("lensInferrer") },
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
for (const [lens, doc] of Object.entries(queryResult.hydeOutput.hydeDocuments)) {
|
|
208
|
+
if (doc?.hydeText) {
|
|
209
|
+
traceEntries.push({
|
|
210
|
+
node: "hyde_query",
|
|
211
|
+
detail: `[${lens}] "${doc.hydeText.slice(0, 120)}${doc.hydeText.length > 120 ? '...' : ''}"`,
|
|
212
|
+
data: { lens, hydeTextPreview: doc.hydeText.slice(0, 300) + (doc.hydeText.length > 300 ? '...' : '') },
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
traceEntries.push({
|
|
218
|
+
node: "discovery",
|
|
219
|
+
detail: `HyDE search → ${queryCandidates.length} candidate(s) from query path`,
|
|
220
|
+
data: {
|
|
221
|
+
candidateCount: queryCandidates.length,
|
|
222
|
+
byLens: computeLensStats(queryCandidates),
|
|
223
|
+
searchQuery: state.searchQuery?.trim().slice(0, 80),
|
|
224
|
+
durationMs: Date.now() - startTime,
|
|
225
|
+
model: getModelName("hydeGenerator"),
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
const [premiseCands, contextCands, contextSimCands] = await runAuxiliaryStrategies(ctx);
|
|
229
|
+
const withPremisesAndContext = mergeStrategyCandidates(queryCandidates, premiseCands, contextCands, contextSimCands);
|
|
230
|
+
traceEntries.push(...auxiliaryStrategyTraces(premiseCands, contextCands, contextSimCands));
|
|
231
|
+
return { candidates: filterByTarget(withPremisesAndContext), trace: traceEntries };
|
|
232
|
+
}
|
|
233
|
+
// No search query — premise-to-premise + context-to-intent discovery
|
|
234
|
+
const [premiseCands, contextCands, contextSimCands] = await runAuxiliaryStrategies(ctx);
|
|
235
|
+
if (premiseCands.length > 0 || contextCands.length > 0 || contextSimCands.length > 0) {
|
|
236
|
+
const merged = mergeStrategyCandidates(premiseCands, contextCands, contextSimCands);
|
|
237
|
+
const traceEntries = auxiliaryStrategyTraces(premiseCands, contextCands, contextSimCands);
|
|
238
|
+
traceEntries.push({
|
|
239
|
+
node: "discovery",
|
|
240
|
+
detail: `${[premiseCands.length > 0 && 'premise-to-premise', contextCands.length > 0 && 'context-to-intent', contextSimCands.length > 0 && 'context-to-context'].filter(Boolean).length} strategies → ${premiseCands.length + contextCands.length + contextSimCands.length} raw, ${merged.length} after dedup`,
|
|
241
|
+
});
|
|
242
|
+
return { candidates: filterByTarget(merged), trace: traceEntries };
|
|
243
|
+
}
|
|
244
|
+
return { candidates: [] };
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Intent source: HyDE over the resolved intent's payload (or the search query),
|
|
248
|
+
* then the additive strategies on top.
|
|
249
|
+
*/
|
|
250
|
+
async function discoverFromIntent(ctx, filterByTarget, startTime) {
|
|
251
|
+
const { state, deps, discoveryUserId } = ctx;
|
|
252
|
+
const resolvedIntent = state.resolvedTriggerIntentId
|
|
253
|
+
? state.indexedIntents.find((i) => i.intentId === state.resolvedTriggerIntentId)
|
|
254
|
+
: state.indexedIntents[0];
|
|
255
|
+
const searchText = state.searchQuery ?? resolvedIntent?.payload ?? '';
|
|
256
|
+
if (!searchText) {
|
|
257
|
+
discoveryLog.warn('No search text available for intent path');
|
|
258
|
+
const [premiseCands, contextCands, contextSimCands] = await runAuxiliaryStrategies(ctx);
|
|
259
|
+
const merged = mergeStrategyCandidates(premiseCands, contextCands, contextSimCands);
|
|
260
|
+
if (merged.length > 0) {
|
|
261
|
+
return {
|
|
262
|
+
candidates: filterByTarget(merged),
|
|
263
|
+
trace: [{ node: "discovery", detail: `No search text; premise → ${premiseCands.length}, context → ${contextCands.length}, context-sim → ${contextSimCands.length}, merged → ${merged.length} candidate(s)` }],
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return { candidates: [] };
|
|
267
|
+
}
|
|
268
|
+
const discovererContext = buildDiscovererContext(state.sourceProfile, state.indexedIntents);
|
|
269
|
+
const discoveryLensInput = {
|
|
270
|
+
profileContext: discovererContext,
|
|
271
|
+
model: getModelName("lensInferrer"),
|
|
272
|
+
};
|
|
273
|
+
const hydeResult = await deps.hydeGenerator.invoke({
|
|
274
|
+
sourceType: 'query',
|
|
275
|
+
sourceText: searchText,
|
|
276
|
+
forceRegenerate: false,
|
|
277
|
+
profileContext: discovererContext,
|
|
278
|
+
});
|
|
279
|
+
const hydeEmbeddings = hydeResult.hydeEmbeddings;
|
|
280
|
+
const lenses = hydeResult.lenses ?? [];
|
|
281
|
+
if (!hydeEmbeddings || Object.keys(hydeEmbeddings).length === 0) {
|
|
282
|
+
const [premiseCands, contextCands, contextSimCands] = await runAuxiliaryStrategies(ctx);
|
|
283
|
+
const merged = mergeStrategyCandidates(premiseCands, contextCands, contextSimCands);
|
|
284
|
+
if (merged.length > 0) {
|
|
285
|
+
return {
|
|
286
|
+
hydeEmbeddings: {},
|
|
287
|
+
candidates: filterByTarget(merged),
|
|
288
|
+
trace: [{ node: "discovery", detail: `No HyDE embeddings; premise → ${premiseCands.length}, context → ${contextCands.length}, context-sim → ${contextSimCands.length}, merged → ${merged.length} candidate(s)` }],
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return { hydeEmbeddings: {}, candidates: [] };
|
|
292
|
+
}
|
|
293
|
+
const lensEmbeddings = toLensEmbeddings(hydeEmbeddings, lenses);
|
|
294
|
+
const allCandidates = [];
|
|
295
|
+
await Promise.all(state.targetNetworks.map(async (targetIndex) => {
|
|
296
|
+
const results = await deps.embedder.searchWithHydeEmbeddings(lensEmbeddings, {
|
|
297
|
+
indexScope: [targetIndex.networkId],
|
|
298
|
+
excludeUserId: discoveryUserId,
|
|
299
|
+
limitPerStrategy: ctx.limitPerStrategy,
|
|
300
|
+
limit: ctx.perIndexLimit,
|
|
301
|
+
minScore: deps.retrievalMinSimilarity,
|
|
302
|
+
corpusGating: ctx.corpusGating,
|
|
303
|
+
});
|
|
304
|
+
allCandidates.push(...collectHydeResults(results, targetIndex.networkId, ctx));
|
|
305
|
+
}));
|
|
306
|
+
const byUserAndIndex = new Map();
|
|
307
|
+
for (const c of allCandidates) {
|
|
308
|
+
const entityKey = c.candidateIntentId
|
|
309
|
+
? `intent:${c.candidateIntentId}`
|
|
310
|
+
: c.candidatePremiseId
|
|
311
|
+
? `premise:${c.candidatePremiseId}`
|
|
312
|
+
: `context:${c.candidateContextId}`;
|
|
313
|
+
const key = `${c.candidateUserId}:${c.networkId}:${entityKey}`;
|
|
314
|
+
if (!byUserAndIndex.has(key) || c.similarity > (byUserAndIndex.get(key)?.similarity ?? 0)) {
|
|
315
|
+
byUserAndIndex.set(key, c);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const candidates = Array.from(byUserAndIndex.values());
|
|
319
|
+
discoveryLog.verbose('Intent-path discovery complete', { candidatesFound: candidates.length });
|
|
320
|
+
const usedLenses = Object.keys(hydeEmbeddings);
|
|
321
|
+
// Build trace with individual candidate similarity scores
|
|
322
|
+
const traceEntries = [{
|
|
323
|
+
node: "lens_input",
|
|
324
|
+
detail: "Profile context for lens inference",
|
|
325
|
+
data: discoveryLensInput,
|
|
326
|
+
}];
|
|
327
|
+
if (lenses.length > 0) {
|
|
328
|
+
traceEntries.push({
|
|
329
|
+
node: "lens_output",
|
|
330
|
+
detail: `Inferred ${lenses.length} lens(es): ${lenses.map(l => l.label).join(', ')}`,
|
|
331
|
+
data: { lenses, model: getModelName("lensInferrer") },
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
traceEntries.push({
|
|
335
|
+
node: "discovery",
|
|
336
|
+
detail: `Query: "${searchText.slice(0, 50)}${searchText.length > 50 ? '...' : ''}" → ${candidates.length} candidate(s)`,
|
|
337
|
+
data: {
|
|
338
|
+
query: searchText.slice(0, 100),
|
|
339
|
+
lenses: usedLenses,
|
|
340
|
+
candidateCount: candidates.length,
|
|
341
|
+
byLens: computeLensStats(candidates),
|
|
342
|
+
durationMs: Date.now() - startTime,
|
|
343
|
+
model: getModelName("hydeGenerator"),
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
// Show the HyDE-generated hypothetical documents used for search
|
|
347
|
+
const hydeDocuments = hydeResult.hydeDocuments;
|
|
348
|
+
if (hydeDocuments) {
|
|
349
|
+
for (const [lens, doc] of Object.entries(hydeDocuments)) {
|
|
350
|
+
if (doc?.hydeText) {
|
|
351
|
+
traceEntries.push({
|
|
352
|
+
node: "hyde_query",
|
|
353
|
+
detail: `[${lens}] "${doc.hydeText.slice(0, 120)}${doc.hydeText.length > 120 ? '...' : ''}"`,
|
|
354
|
+
data: {
|
|
355
|
+
lens,
|
|
356
|
+
hydeTextPreview: doc.hydeText.slice(0, 160) + (doc.hydeText.length > 160 ? '...' : ''),
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// Add top candidates with similarity scores
|
|
363
|
+
const sortedCandidates = [...candidates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
364
|
+
for (const c of sortedCandidates) {
|
|
365
|
+
traceEntries.push({
|
|
366
|
+
node: "match",
|
|
367
|
+
detail: `Similarity ${Math.round(c.similarity * 100)}% via ${c.lens}`,
|
|
368
|
+
data: {
|
|
369
|
+
userId: c.candidateUserId,
|
|
370
|
+
similarity: Math.round(c.similarity * 100),
|
|
371
|
+
lens: c.lens,
|
|
372
|
+
hasIntent: !!c.candidateIntentId,
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
const [premiseCands, contextCands, contextSimCands] = await runAuxiliaryStrategies(ctx);
|
|
377
|
+
const allStrategies = mergeStrategyCandidates(candidates, premiseCands, contextCands, contextSimCands);
|
|
378
|
+
if (premiseCands.length > 0 || contextCands.length > 0 || contextSimCands.length > 0) {
|
|
379
|
+
traceEntries.push({
|
|
380
|
+
node: "discovery",
|
|
381
|
+
detail: `+ Premise → ${premiseCands.length}, Context → ${contextCands.length}, Context-sim → ${contextSimCands.length}, merged to ${allStrategies.length} candidate(s)`,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
hydeEmbeddings: hydeEmbeddings,
|
|
386
|
+
candidates: filterByTarget(allStrategies),
|
|
387
|
+
trace: traceEntries,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/** Trace summary for {@link discoveryNode}. */
|
|
391
|
+
export function discoveryTraceSummary(result) {
|
|
392
|
+
const r = result;
|
|
393
|
+
if (r?.error)
|
|
394
|
+
return `error: ${r.error}`;
|
|
395
|
+
const candidates = r?.candidates;
|
|
396
|
+
return candidates ? `Found ${candidates.length} candidate(s)` : undefined;
|
|
397
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery pipeline, stages 4–5: evaluation and ranking.
|
|
3
|
+
*
|
|
4
|
+
* Evaluation builds an entity bundle from the discoverer plus the top candidates,
|
|
5
|
+
* asks the evaluator to score them, and maps the verdicts back onto graph state.
|
|
6
|
+
*/
|
|
7
|
+
import type { DebugMetaAgent } from '../../agents/index.js';
|
|
8
|
+
import type { CandidateMatch, EvaluatedOpportunity } from '../domain/opportunity.state.js';
|
|
9
|
+
import { type OpportunityGraphDeps, type OpportunityState } from './opportunity.graph.shared.js';
|
|
10
|
+
/**
|
|
11
|
+
* Node 3: Evaluation (Entity bundle)
|
|
12
|
+
* Builds entity bundle from source + candidates, invokes entity-bundle evaluator, maps to EvaluatedOpportunity with networkId from entities.
|
|
13
|
+
*/
|
|
14
|
+
export declare function evaluationNode(state: OpportunityState, deps: OpportunityGraphDeps): Promise<{
|
|
15
|
+
evaluatedOpportunities: never[];
|
|
16
|
+
agentTimings: never[];
|
|
17
|
+
candidates?: undefined;
|
|
18
|
+
remainingCandidates?: undefined;
|
|
19
|
+
error?: undefined;
|
|
20
|
+
trace?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
candidates: never[];
|
|
23
|
+
evaluatedOpportunities: never[];
|
|
24
|
+
remainingCandidates: never[];
|
|
25
|
+
error: string;
|
|
26
|
+
agentTimings: never[];
|
|
27
|
+
trace?: undefined;
|
|
28
|
+
} | {
|
|
29
|
+
candidates: never[];
|
|
30
|
+
evaluatedOpportunities: never[];
|
|
31
|
+
remainingCandidates: never[];
|
|
32
|
+
agentTimings: never[];
|
|
33
|
+
error?: undefined;
|
|
34
|
+
trace?: undefined;
|
|
35
|
+
} | {
|
|
36
|
+
candidates: CandidateMatch[];
|
|
37
|
+
evaluatedOpportunities: EvaluatedOpportunity[];
|
|
38
|
+
remainingCandidates: CandidateMatch[];
|
|
39
|
+
trace: {
|
|
40
|
+
node: string;
|
|
41
|
+
detail?: string;
|
|
42
|
+
data?: Record<string, unknown>;
|
|
43
|
+
}[];
|
|
44
|
+
agentTimings: DebugMetaAgent[];
|
|
45
|
+
error?: undefined;
|
|
46
|
+
} | {
|
|
47
|
+
evaluatedOpportunities: never[];
|
|
48
|
+
error: string;
|
|
49
|
+
trace: {
|
|
50
|
+
node: string;
|
|
51
|
+
detail: string;
|
|
52
|
+
data: {
|
|
53
|
+
error: string;
|
|
54
|
+
candidateCount: number;
|
|
55
|
+
durationMs: number;
|
|
56
|
+
};
|
|
57
|
+
}[];
|
|
58
|
+
agentTimings: DebugMetaAgent[];
|
|
59
|
+
candidates?: undefined;
|
|
60
|
+
remainingCandidates?: undefined;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Node 4: Ranking
|
|
64
|
+
* Sorts evaluated opportunities by score, applies limit, dedupes by actor-set hash.
|
|
65
|
+
*/
|
|
66
|
+
export declare function rankingNode(state: OpportunityState): Promise<{
|
|
67
|
+
evaluatedOpportunities: EvaluatedOpportunity[];
|
|
68
|
+
error?: undefined;
|
|
69
|
+
trace?: undefined;
|
|
70
|
+
} | {
|
|
71
|
+
evaluatedOpportunities: never[];
|
|
72
|
+
error: string;
|
|
73
|
+
trace: {
|
|
74
|
+
node: string;
|
|
75
|
+
detail: string;
|
|
76
|
+
data: {
|
|
77
|
+
error: string;
|
|
78
|
+
};
|
|
79
|
+
}[];
|
|
80
|
+
}>;
|
|
81
|
+
/** Trace summary for {@link rankingNode}. */
|
|
82
|
+
export declare function rankingTraceSummary(result: unknown): string | undefined;
|