@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.
Files changed (184) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/IMPLEMENTATION.md +50 -52
  3. package/STABILITY.md +3 -3
  4. package/dist/chat/chat.agent.js +2 -3
  5. package/dist/chat/chat.graph.d.ts +41 -1
  6. package/dist/chat/chat.graph.js +108 -127
  7. package/dist/chat/negotiator.persona.d.ts +1 -1
  8. package/dist/chat/negotiator.persona.js +1 -4
  9. package/dist/chat/negotiator.prompt.js +1 -2
  10. package/dist/chat/onboarding.persona.js +0 -1
  11. package/dist/chat/reporter.persona.js +0 -1
  12. package/dist/chat/signal.persona.js +0 -1
  13. package/dist/contacts/application/contact.tools.d.ts +6 -9
  14. package/dist/contacts/application/contact.tools.js +11 -92
  15. package/dist/contacts/application/index.d.ts +2 -14
  16. package/dist/contacts/application/index.js +2 -13
  17. package/dist/contacts/domain/contact.types.d.ts +0 -23
  18. package/dist/contacts/domain/index.d.ts +2 -6
  19. package/dist/contacts/index.d.ts +1 -1
  20. package/dist/contacts/index.js +1 -1
  21. package/dist/contacts/ports/contact.repository.port.d.ts +5 -12
  22. package/dist/contacts/ports/contact.repository.port.js +4 -6
  23. package/dist/contacts/ports/contact.tools.port.d.ts +1 -3
  24. package/dist/contacts/ports/contact.tools.port.js +1 -2
  25. package/dist/contacts/ports/index.d.ts +1 -1
  26. package/dist/contacts/ports/index.js +1 -1
  27. package/dist/discovery/hyde.frame.d.ts +4 -4
  28. package/dist/discovery/hyde.graph.d.ts +71 -12
  29. package/dist/discovery/hyde.graph.js +337 -335
  30. package/dist/discovery/lens.inferrer.d.ts +6 -6
  31. package/dist/enrichment/enrichment.graph.d.ts +127 -9
  32. package/dist/enrichment/enrichment.graph.js +633 -655
  33. package/dist/enrichment/enrichment.state.d.ts +1 -1
  34. package/dist/enrichment/enrichment.tools.context-read.d.ts +10 -0
  35. package/dist/enrichment/enrichment.tools.context-read.js +353 -0
  36. package/dist/enrichment/enrichment.tools.context-write.d.ts +9 -0
  37. package/dist/enrichment/enrichment.tools.context-write.js +417 -0
  38. package/dist/enrichment/enrichment.tools.d.ts +11 -1
  39. package/dist/enrichment/enrichment.tools.helpers.d.ts +139 -0
  40. package/dist/enrichment/enrichment.tools.helpers.js +234 -0
  41. package/dist/enrichment/enrichment.tools.js +16 -975
  42. package/dist/index.d.ts +27 -50
  43. package/dist/index.js +17 -28
  44. package/dist/intents/application/intent.graph.d.ts +71 -67
  45. package/dist/intents/application/intent.graph.execute.d.ts +59 -0
  46. package/dist/intents/application/intent.graph.execute.js +301 -0
  47. package/dist/intents/application/intent.graph.infer.d.ts +44 -0
  48. package/dist/intents/application/intent.graph.infer.js +97 -0
  49. package/dist/intents/application/intent.graph.js +96 -888
  50. package/dist/intents/application/intent.graph.reconcile.d.ts +71 -0
  51. package/dist/intents/application/intent.graph.reconcile.js +274 -0
  52. package/dist/intents/application/intent.graph.shared.d.ts +70 -0
  53. package/dist/intents/application/intent.graph.shared.js +153 -0
  54. package/dist/intents/domain/intent.state.d.ts +1 -1
  55. package/dist/maintenance/maintenance.graph.d.ts +66 -3
  56. package/dist/maintenance/maintenance.graph.js +155 -156
  57. package/dist/mcp/mcp.authorization-policy.d.ts +24 -28
  58. package/dist/mcp/mcp.authorization-policy.js +14 -34
  59. package/dist/mcp/mcp.server.d.ts +3 -3
  60. package/dist/mcp/mcp.server.js +14 -20
  61. package/dist/negotiations/application/negotiation.candidates.d.ts +83 -0
  62. package/dist/negotiations/application/negotiation.candidates.js +162 -0
  63. package/dist/negotiations/application/negotiation.graph.d.ts +91 -157
  64. package/dist/negotiations/application/negotiation.graph.finalize.d.ts +5 -0
  65. package/dist/negotiations/application/negotiation.graph.finalize.js +280 -0
  66. package/dist/negotiations/application/negotiation.graph.init.d.ts +54 -0
  67. package/dist/negotiations/application/negotiation.graph.init.js +227 -0
  68. package/dist/negotiations/application/negotiation.graph.js +69 -1388
  69. package/dist/negotiations/application/negotiation.graph.screen.d.ts +23 -0
  70. package/dist/negotiations/application/negotiation.graph.screen.js +108 -0
  71. package/dist/negotiations/application/negotiation.graph.shared.d.ts +75 -0
  72. package/dist/negotiations/application/negotiation.graph.shared.js +125 -0
  73. package/dist/negotiations/application/negotiation.graph.turn.d.ts +105 -0
  74. package/dist/negotiations/application/negotiation.graph.turn.js +484 -0
  75. package/dist/negotiations/domain/negotiation.state.d.ts +1 -1
  76. package/dist/negotiations/domain/negotiation.state.js +0 -1
  77. package/dist/networks/application/indexer.graph.d.ts +165 -7
  78. package/dist/networks/application/indexer.graph.js +339 -388
  79. package/dist/networks/application/indexer.state.d.ts +1 -1
  80. package/dist/networks/application/membership.graph.d.ts +83 -5
  81. package/dist/networks/application/membership.graph.js +177 -207
  82. package/dist/networks/application/network.graph.d.ts +148 -5
  83. package/dist/networks/application/network.graph.js +249 -278
  84. package/dist/networks/domain/membership.state.d.ts +1 -1
  85. package/dist/networks/domain/network.state.d.ts +1 -1
  86. package/dist/opportunities/application/delivery-card.cache.d.ts +1 -1
  87. package/dist/opportunities/application/delivery-card.cache.js +2 -2
  88. package/dist/opportunities/application/index.d.ts +2 -2
  89. package/dist/opportunities/application/index.js +2 -2
  90. package/dist/opportunities/application/opportunity.evaluator.js +1 -1
  91. package/dist/opportunities/application/opportunity.graph.d.ts +686 -473
  92. package/dist/opportunities/application/opportunity.graph.discovery-strategies.d.ts +109 -0
  93. package/dist/opportunities/application/opportunity.graph.discovery-strategies.js +451 -0
  94. package/dist/opportunities/application/opportunity.graph.discovery.d.ts +69 -0
  95. package/dist/opportunities/application/opportunity.graph.discovery.js +397 -0
  96. package/dist/opportunities/application/opportunity.graph.evaluation.d.ts +82 -0
  97. package/dist/opportunities/application/opportunity.graph.evaluation.js +608 -0
  98. package/dist/opportunities/application/opportunity.graph.js +120 -3690
  99. package/dist/opportunities/application/opportunity.graph.modes.d.ts +538 -0
  100. package/dist/opportunities/application/opportunity.graph.modes.js +538 -0
  101. package/dist/opportunities/application/opportunity.graph.negotiate.d.ts +109 -0
  102. package/dist/opportunities/application/opportunity.graph.negotiate.js +392 -0
  103. package/dist/opportunities/application/opportunity.graph.persist-node.d.ts +93 -0
  104. package/dist/opportunities/application/opportunity.graph.persist-node.js +765 -0
  105. package/dist/opportunities/application/opportunity.graph.prep.d.ts +150 -0
  106. package/dist/opportunities/application/opportunity.graph.prep.js +381 -0
  107. package/dist/opportunities/application/opportunity.graph.shared.d.ts +173 -0
  108. package/dist/opportunities/application/opportunity.graph.shared.js +199 -0
  109. package/dist/opportunities/application/opportunity.presentation.d.ts +399 -0
  110. package/dist/opportunities/application/{opportunity.presenter.js → opportunity.presentation.js} +735 -9
  111. package/dist/opportunities/application/opportunity.tools.cards.d.ts +152 -0
  112. package/dist/opportunities/application/opportunity.tools.cards.js +235 -0
  113. package/dist/opportunities/application/opportunity.tools.d.ts +8 -114
  114. package/dist/opportunities/application/opportunity.tools.js +28 -711
  115. package/dist/opportunities/application/opportunity.tools.list.d.ts +10 -0
  116. package/dist/opportunities/application/opportunity.tools.list.js +493 -0
  117. package/dist/opportunities/domain/index.d.ts +3 -3
  118. package/dist/opportunities/domain/index.js +3 -3
  119. package/dist/opportunities/domain/opportunity.state.d.ts +19 -19
  120. package/dist/opportunities/index.d.ts +6 -6
  121. package/dist/opportunities/index.js +4 -4
  122. package/dist/opportunities/ports/opportunity.tools.port.d.ts +1 -1
  123. package/dist/opportunities/radar/radar.graph.d.ts +57 -13
  124. package/dist/opportunities/radar/radar.graph.js +470 -471
  125. package/dist/premises/premise.graph.d.ts +88 -20
  126. package/dist/premises/premise.graph.js +207 -218
  127. package/dist/questions/domain/question.schema.d.ts +40 -40
  128. package/dist/shared/agent/tool.factory.js +0 -8
  129. package/dist/shared/agent/tool.helpers.d.ts +21 -45
  130. package/dist/shared/agent/tool.helpers.js +1 -2
  131. package/dist/shared/agent/tool.registry.d.ts +4 -3
  132. package/dist/shared/agent/tool.registry.js +2 -4
  133. package/dist/shared/agent/tool.runtime.js +0 -2
  134. package/dist/shared/agent/utility.tools.js +9 -13
  135. package/dist/shared/interfaces/database.capabilities.d.ts +100 -0
  136. package/dist/shared/interfaces/database.capabilities.js +7 -0
  137. package/dist/shared/interfaces/database.entities.d.ts +533 -0
  138. package/dist/shared/interfaces/database.entities.js +10 -0
  139. package/dist/shared/interfaces/database.identity-queries.d.ts +294 -0
  140. package/dist/shared/interfaces/database.identity-queries.js +4 -0
  141. package/dist/shared/interfaces/database.interface.d.ts +15 -2277
  142. package/dist/shared/interfaces/database.interface.js +8 -0
  143. package/dist/shared/interfaces/database.member-queries.d.ts +221 -0
  144. package/dist/shared/interfaces/database.member-queries.js +4 -0
  145. package/dist/shared/interfaces/database.negotiation.d.ts +305 -0
  146. package/dist/shared/interfaces/database.negotiation.js +26 -0
  147. package/dist/shared/interfaces/database.network-queries.d.ts +277 -0
  148. package/dist/shared/interfaces/database.network-queries.js +4 -0
  149. package/dist/shared/interfaces/database.opportunity-queries.d.ts +285 -0
  150. package/dist/shared/interfaces/database.opportunity-queries.js +4 -0
  151. package/dist/shared/interfaces/database.port.d.ts +322 -0
  152. package/dist/shared/interfaces/database.port.js +29 -0
  153. package/dist/shared/schemas/discovery-question.schema.d.ts +14 -14
  154. package/dist/shared/schemas/negotiation-digest.schema.d.ts +2 -2
  155. package/package.json +2 -15
  156. package/dist/contacts/application/contact.inviter.d.ts +0 -49
  157. package/dist/contacts/application/contact.inviter.js +0 -64
  158. package/dist/integrations/application/index.d.ts +0 -14
  159. package/dist/integrations/application/index.js +0 -14
  160. package/dist/integrations/application/integration.tools.d.ts +0 -27
  161. package/dist/integrations/application/integration.tools.js +0 -98
  162. package/dist/integrations/domain/index.d.ts +0 -16
  163. package/dist/integrations/domain/index.js +0 -1
  164. package/dist/integrations/domain/integration.types.d.ts +0 -51
  165. package/dist/integrations/domain/integration.types.js +0 -9
  166. package/dist/integrations/index.d.ts +0 -10
  167. package/dist/integrations/index.js +0 -8
  168. package/dist/integrations/ports/index.d.ts +0 -25
  169. package/dist/integrations/ports/index.js +0 -23
  170. package/dist/integrations/ports/integration.adapter.port.d.ts +0 -62
  171. package/dist/integrations/ports/integration.adapter.port.js +0 -15
  172. package/dist/integrations/ports/integration.importer.port.d.ts +0 -31
  173. package/dist/integrations/ports/integration.importer.port.js +0 -15
  174. package/dist/integrations/ports/integration.tools.port.d.ts +0 -25
  175. package/dist/integrations/ports/integration.tools.port.js +0 -18
  176. package/dist/opportunities/application/opportunity.card-presentation.d.ts +0 -44
  177. package/dist/opportunities/application/opportunity.card-presentation.js +0 -93
  178. package/dist/opportunities/application/opportunity.presenter.d.ts +0 -156
  179. package/dist/opportunities/domain/opportunity.presentation-cache.d.ts +0 -5
  180. package/dist/opportunities/domain/opportunity.presentation-cache.js +0 -11
  181. package/dist/opportunities/domain/opportunity.presentation.d.ts +0 -76
  182. package/dist/opportunities/domain/opportunity.presentation.js +0 -516
  183. package/dist/opportunities/domain/opportunity.safe-presentation.d.ts +0 -104
  184. package/dist/opportunities/domain/opportunity.safe-presentation.js +0 -102
@@ -120,343 +120,23 @@ function isRuntimeVerdict(value) {
120
120
  /** Factory for the HyDE generation graph. Existing five-argument calls remain valid. */
121
121
  export class HydeGraphFactory {
122
122
  constructor(database, embedder, cache, inferrer, generator, options = {}) {
123
- this.database = database;
124
- this.embedder = embedder;
125
- this.cache = cache;
126
- this.inferrer = inferrer;
127
- this.generator = generator;
128
- this.options = options;
123
+ const mode = options.mode ?? getHydeGenerationMode();
124
+ this.deps = {
125
+ database, embedder, cache, inferrer, generator, options, mode,
126
+ ...(mode === HYDE_FRAME_GENERATION_VERSION
127
+ ? { validator: options.validator ?? new HydeValidator() }
128
+ : {}),
129
+ };
129
130
  }
130
131
  createGraph() {
131
- const self = this;
132
- const mode = this.options.mode ?? getHydeGenerationMode();
133
- const validator = mode === HYDE_FRAME_GENERATION_VERSION
134
- ? (this.options.validator ?? new HydeValidator())
135
- : undefined;
136
- /** Node 1: Infer lenses from source text + optional profile context. */
137
- const inferLensesNode = async (state) => {
138
- return timed("HydeGraph.inferLenses", async () => {
139
- const { sourceText, profileContext, maxLenses } = state;
140
- const agentTimingsAccum = [];
141
- try {
142
- const traceEmitter = requestContext.getStore()?.traceEmitter;
143
- const inferrerStart = Date.now();
144
- traceEmitter?.({ type: "agent_start", name: "lens-inferrer" });
145
- const result = await self.inferrer.infer({
146
- sourceText,
147
- profileContext,
148
- maxLenses,
149
- ...(mode === HYDE_FRAME_GENERATION_VERSION ? { frameConstrained: true } : {}),
150
- });
151
- const durationMs = Date.now() - inferrerStart;
152
- agentTimingsAccum.push({ name: 'lens.inferrer', durationMs });
153
- traceEmitter?.({ type: "agent_end", name: "lens-inferrer", durationMs, summary: result.lenses.length > 0 ? `Inferred ${result.lenses.length} lens(es)` : "lens-inferrer completed" });
154
- if (mode === HYDE_FRAME_GENERATION_VERSION) {
155
- const sourceFrame = sanitizeHydeSourceFrame(sourceText, result.sourceFrame ?? emptyFrame());
156
- return {
157
- lenses: result.lenses,
158
- sourceFrame,
159
- frameFingerprint: computeHydeFrameFingerprint(sourceText, sourceFrame),
160
- sourceTextHash: computeHydeSourceTextHash(sourceText),
161
- generatedAt: nextGenerationMarker(),
162
- agentTimings: agentTimingsAccum,
163
- };
164
- }
165
- return { lenses: result.lenses, agentTimings: agentTimingsAccum };
166
- }
167
- catch (error) {
168
- logger.error('Lens inference failed in graph node', { error });
169
- if (mode === HYDE_FRAME_GENERATION_VERSION) {
170
- const sourceFrame = emptyFrame();
171
- return {
172
- lenses: [],
173
- sourceFrame,
174
- frameFingerprint: computeHydeFrameFingerprint(sourceText, sourceFrame),
175
- sourceTextHash: computeHydeSourceTextHash(sourceText),
176
- generatedAt: nextGenerationMarker(),
177
- agentTimings: agentTimingsAccum,
178
- };
179
- }
180
- return { lenses: [], agentTimings: agentTimingsAccum };
181
- }
182
- });
183
- };
184
- /** Node 2: Check the mode-isolated cache/DB for matching documents. */
185
- const checkCacheNode = async (state) => {
186
- return timed("HydeGraph.checkCache", async () => {
187
- const { sourceType, sourceId, sourceText, lenses, forceRegenerate } = state;
188
- if (forceRegenerate)
189
- return { hydeDocuments: {} };
190
- const frameFingerprint = mode === HYDE_FRAME_GENERATION_VERSION
191
- ? requireFrameFingerprint(state.frameFingerprint)
192
- : undefined;
193
- const sourceTextHash = mode === HYDE_FRAME_GENERATION_VERSION
194
- ? state.sourceTextHash ?? computeHydeSourceTextHash(sourceText)
195
- : undefined;
196
- const cached = {};
197
- for (const lens of lenses) {
198
- const key = cacheKey(mode, sourceType, sourceId ?? undefined, sourceText, lens.label, lens.corpus, frameFingerprint);
199
- const fromCache = await self.cache.get(key);
200
- const cacheAccepted = fromCache?.hydeText
201
- && fromCache.hydeEmbedding?.length
202
- && (mode === 'legacy' || isFrameCacheDocument(fromCache, lens.label, frameFingerprint, sourceTextHash));
203
- if (cacheAccepted && fromCache) {
204
- cached[lens.label] = {
205
- ...fromCache,
206
- ...(mode === HYDE_FRAME_GENERATION_VERSION ? { origin: 'cache' } : {}),
207
- };
208
- continue;
209
- }
210
- if (sourceId) {
211
- const fromDb = await self.database.getHydeDocument(sourceType, sourceId, dbStrategy(mode, lens.label, lens.corpus));
212
- const frameDbContext = mode === HYDE_FRAME_GENERATION_VERSION && fromDb
213
- ? fromDb.context
214
- : null;
215
- if (fromDb && (mode === 'legacy' || isFrameDbContext(frameDbContext, lens.label, frameFingerprint, sourceTextHash))) {
216
- cached[lens.label] = {
217
- lens: lens.label,
218
- targetCorpus: fromDb.targetCorpus,
219
- hydeText: fromDb.hydeText,
220
- hydeEmbedding: fromDb.hydeEmbedding,
221
- ...(mode === HYDE_FRAME_GENERATION_VERSION
222
- ? {
223
- origin: 'db',
224
- validationStatus: 'valid',
225
- hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION,
226
- frameFingerprint,
227
- sourceTextHash,
228
- generatedAt: frameDbContext.generatedAt,
229
- }
230
- : {}),
231
- };
232
- }
233
- }
234
- }
235
- if (mode === HYDE_FRAME_GENERATION_VERSION) {
236
- const newestTimestamp = Math.max(...Object.values(cached).map((doc) => Date.parse(doc.generatedAt ?? '')));
237
- if (Number.isFinite(newestTimestamp)) {
238
- return {
239
- hydeDocuments: Object.fromEntries(Object.entries(cached).filter(([, doc]) => Date.parse(doc.generatedAt ?? '') === newestTimestamp)),
240
- };
241
- }
242
- }
243
- return { hydeDocuments: cached };
244
- });
245
- };
246
- const shouldGenerate = (state) => state.lenses.some((lens) => !state.hydeDocuments[lens.label]) ? 'generate' : 'skip';
247
- /** Node 3: Generate all missing documents and return a complete snapshot. */
248
- const generateMissingNode = async (state) => {
249
- return timed("HydeGraph.generateMissing", async () => {
250
- const { sourceText, sourceFrame, lenses, hydeDocuments } = state;
251
- const missing = lenses.filter((lens) => !hydeDocuments[lens.label]);
252
- const agentTimingsAccum = [];
253
- const generated = {};
254
- const sourceTextHash = mode === HYDE_FRAME_GENERATION_VERSION
255
- ? state.sourceTextHash ?? computeHydeSourceTextHash(sourceText)
256
- : undefined;
257
- const generatedAt = mode === HYDE_FRAME_GENERATION_VERSION
258
- ? state.generatedAt ?? nextGenerationMarker()
259
- : undefined;
260
- await Promise.all(missing.map(async (lens) => {
261
- const traceEmitter = requestContext.getStore()?.traceEmitter;
262
- const generatorStart = Date.now();
263
- traceEmitter?.({ type: "agent_start", name: "hyde-generator" });
264
- const out = await self.generator.generate({
265
- sourceText,
266
- lens: lens.label,
267
- corpus: lens.corpus,
268
- ...(mode === HYDE_FRAME_GENERATION_VERSION && sourceFrame ? { sourceFrame } : {}),
269
- });
270
- const durationMs = Date.now() - generatorStart;
271
- agentTimingsAccum.push({ name: 'hyde.generator', durationMs });
272
- traceEmitter?.({ type: "agent_end", name: "hyde-generator", durationMs, summary: `Generated: ${lens.label}` });
273
- generated[lens.label] = {
274
- lens: lens.label,
275
- targetCorpus: lens.corpus,
276
- hydeText: out.text,
277
- hydeEmbedding: [],
278
- ...(mode === HYDE_FRAME_GENERATION_VERSION
279
- ? {
280
- origin: 'generated',
281
- frameFingerprint: requireFrameFingerprint(state.frameFingerprint),
282
- sourceTextHash,
283
- generatedAt,
284
- }
285
- : {}),
286
- };
287
- }));
288
- const retained = mode === HYDE_FRAME_GENERATION_VERSION
289
- ? Object.fromEntries(Object.entries(hydeDocuments).map(([label, doc]) => [
290
- label,
291
- {
292
- ...doc,
293
- frameFingerprint: requireFrameFingerprint(state.frameFingerprint),
294
- sourceTextHash,
295
- generatedAt,
296
- },
297
- ]))
298
- : hydeDocuments;
299
- return { hydeDocuments: { ...retained, ...generated }, agentTimings: agentTimingsAccum };
300
- });
301
- };
302
- /** Frame-v1 only: validate newly generated docs in one batch. */
303
- const validateGeneratedNode = async (state) => {
304
- return timed("HydeGraph.validateGenerated", async () => {
305
- const generated = Object.values(state.hydeDocuments).filter((doc) => doc.origin === 'generated');
306
- if (generated.length === 0 || !validator)
307
- return { hydeDocuments: state.hydeDocuments };
308
- const frame = sanitizeHydeSourceFrame(state.sourceText, state.sourceFrame ?? emptyFrame());
309
- const documents = {};
310
- const lensByDocumentKey = new Map();
311
- for (const doc of generated) {
312
- const key = opaqueDocumentKey(doc);
313
- documents[key] = { corpus: doc.targetCorpus, text: doc.hydeText };
314
- lensByDocumentKey.set(key, doc.lens);
315
- }
316
- const updated = { ...state.hydeDocuments };
317
- const agentTimingsAccum = [];
318
- const traceEmitter = requestContext.getStore()?.traceEmitter;
319
- const validatorStart = Date.now();
320
- let validCount = 0;
321
- let rejectedCount = 0;
322
- let failedOpenCount = 0;
323
- traceEmitter?.({ type: 'agent_start', name: 'hyde-validator' });
324
- try {
325
- const output = await validator.validate({
326
- sourceText: state.sourceText,
327
- sourceFrame: frame,
328
- documents,
329
- });
330
- const rawVerdicts = Array.isArray(output.verdicts)
331
- ? output.verdicts
332
- : [];
333
- for (const key of Object.keys(documents)) {
334
- const lensLabel = lensByDocumentKey.get(key);
335
- if (!lensLabel)
336
- continue;
337
- const matching = rawVerdicts.filter((value) => !!value && typeof value === 'object' && value.key === key);
338
- const doc = updated[lensLabel];
339
- if (!doc)
340
- continue;
341
- if (matching.length !== 1 || !isRuntimeVerdict(matching[0])) {
342
- failedOpenCount += 1;
343
- updated[lensLabel] = { ...doc, validationStatus: 'failed_open', hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION };
344
- continue;
345
- }
346
- const verdict = matching[0];
347
- const hasUnsupportedGrounding = verdict.unsupportedNamedEntities.length > 0
348
- || verdict.unsupportedHardConstraints.length > 0;
349
- const contradictory = verdict.valid === hasUnsupportedGrounding;
350
- if (contradictory) {
351
- failedOpenCount += 1;
352
- updated[lensLabel] = { ...doc, validationStatus: 'failed_open', hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION };
353
- }
354
- else if (!verdict.valid) {
355
- rejectedCount += 1;
356
- delete updated[lensLabel];
357
- }
358
- else {
359
- validCount += 1;
360
- updated[lensLabel] = { ...doc, validationStatus: 'valid', hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION };
361
- }
362
- }
363
- }
364
- catch (error) {
365
- logger.error('HyDE validation failed open', { error });
366
- validCount = 0;
367
- rejectedCount = 0;
368
- failedOpenCount = generated.length;
369
- for (const doc of generated) {
370
- updated[doc.lens] = { ...doc, validationStatus: 'failed_open', hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION };
371
- }
372
- }
373
- finally {
374
- const durationMs = Date.now() - validatorStart;
375
- agentTimingsAccum.push({ name: 'hyde.validator', durationMs });
376
- traceEmitter?.({
377
- type: 'agent_end',
378
- name: 'hyde-validator',
379
- durationMs,
380
- summary: `${validCount} valid, ${rejectedCount} rejected, ${failedOpenCount} failed open`,
381
- });
382
- }
383
- return { hydeDocuments: updated, agentTimings: agentTimingsAccum };
384
- });
385
- };
386
- /** Embed all accepted/cached documents that do not have embeddings. */
387
- const embedNode = async (state) => {
388
- return timed("HydeGraph.embed", async () => {
389
- const toEmbed = [];
390
- const updated = {};
391
- const hydeEmbeddings = {};
392
- for (const [label, doc] of Object.entries(state.hydeDocuments)) {
393
- if (doc.hydeEmbedding?.length) {
394
- updated[label] = doc;
395
- hydeEmbeddings[label] = doc.hydeEmbedding;
396
- }
397
- else {
398
- toEmbed.push({ label, doc });
399
- }
400
- }
401
- if (toEmbed.length > 0) {
402
- const embeddings = await self.embedder.generate(toEmbed.map((item) => item.doc.hydeText), undefined, getAbortSignalConfig());
403
- const embeddingArray = Array.isArray(embeddings[0]) ? embeddings : [embeddings];
404
- for (let i = 0; i < toEmbed.length; i++) {
405
- const { label, doc } = toEmbed[i];
406
- const embedding = embeddingArray[i] ?? [];
407
- updated[label] = { ...doc, hydeEmbedding: embedding };
408
- hydeEmbeddings[label] = embedding;
409
- }
410
- }
411
- return { hydeDocuments: updated, hydeEmbeddings };
412
- });
413
- };
414
- /** Cache/persist only legacy docs or successfully validated frame-v1 docs. */
415
- const cacheResultsNode = async (state) => {
416
- return timed("HydeGraph.cacheResults", async () => {
417
- const { sourceType, sourceId, sourceText, hydeDocuments } = state;
418
- const frameFingerprint = mode === HYDE_FRAME_GENERATION_VERSION
419
- ? requireFrameFingerprint(state.frameFingerprint)
420
- : undefined;
421
- const sourceTextHash = mode === HYDE_FRAME_GENERATION_VERSION
422
- ? state.sourceTextHash ?? computeHydeSourceTextHash(sourceText)
423
- : undefined;
424
- for (const [label, doc] of Object.entries(hydeDocuments)) {
425
- if (mode === HYDE_FRAME_GENERATION_VERSION
426
- && !isFrameCacheDocument(doc, label, frameFingerprint, sourceTextHash))
427
- continue;
428
- const key = cacheKey(mode, sourceType, sourceId ?? undefined, sourceText, label, doc.targetCorpus, frameFingerprint);
429
- await self.cache.set(key, doc, { ttl: HYDE_DEFAULT_CACHE_TTL });
430
- if (sourceId) {
431
- await self.database.saveHydeDocument({
432
- sourceType,
433
- sourceId,
434
- strategy: dbStrategy(mode, label, doc.targetCorpus),
435
- targetCorpus: doc.targetCorpus,
436
- hydeText: doc.hydeText,
437
- hydeEmbedding: doc.hydeEmbedding,
438
- ...(mode === HYDE_FRAME_GENERATION_VERSION ? {
439
- context: {
440
- hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION,
441
- lensLabel: label,
442
- validationStatus: 'valid',
443
- frameFingerprint: doc.frameFingerprint,
444
- sourceTextHash: doc.sourceTextHash,
445
- generatedAt: doc.generatedAt,
446
- },
447
- } : {}),
448
- });
449
- }
450
- }
451
- return {};
452
- });
453
- };
132
+ const deps = this.deps;
133
+ const mode = deps.mode;
454
134
  const workflow = new StateGraph(HydeGraphState)
455
- .addNode('infer_lenses', inferLensesNode)
456
- .addNode('check_cache', checkCacheNode)
457
- .addNode('generate_missing', generateMissingNode)
458
- .addNode('embed', embedNode)
459
- .addNode('cache_results', cacheResultsNode)
135
+ .addNode('infer_lenses', (state) => inferLensesNode(state, deps))
136
+ .addNode('check_cache', (state) => checkCacheNode(state, deps))
137
+ .addNode('generate_missing', (state) => generateMissingNode(state, deps))
138
+ .addNode('embed', (state) => embedNode(state, deps))
139
+ .addNode('cache_results', (state) => cacheResultsNode(state, deps))
460
140
  .addEdge(START, 'infer_lenses')
461
141
  .addEdge('infer_lenses', 'check_cache')
462
142
  .addConditionalEdges('check_cache', shouldGenerate, {
@@ -465,7 +145,7 @@ export class HydeGraphFactory {
465
145
  });
466
146
  if (mode === HYDE_FRAME_GENERATION_VERSION) {
467
147
  workflow
468
- .addNode('validate_generated', validateGeneratedNode)
148
+ .addNode('validate_generated', (state) => validateGeneratedNode(state, deps))
469
149
  .addEdge('generate_missing', 'validate_generated')
470
150
  .addEdge('validate_generated', 'embed');
471
151
  }
@@ -476,5 +156,327 @@ export class HydeGraphFactory {
476
156
  .addEdge('embed', 'cache_results')
477
157
  .addEdge('cache_results', END);
478
158
  return workflow.compile();
159
+ return workflow.compile();
479
160
  }
480
161
  }
162
+ /** Node 1: Infer lenses from source text + optional profile context. */
163
+ export async function inferLensesNode(state, deps) {
164
+ return timed("HydeGraph.inferLenses", async () => {
165
+ const { sourceText, profileContext, maxLenses } = state;
166
+ const agentTimingsAccum = [];
167
+ try {
168
+ const traceEmitter = requestContext.getStore()?.traceEmitter;
169
+ const inferrerStart = Date.now();
170
+ traceEmitter?.({ type: "agent_start", name: "lens-inferrer" });
171
+ const result = await deps.inferrer.infer({
172
+ sourceText,
173
+ profileContext,
174
+ maxLenses,
175
+ ...(deps.mode === HYDE_FRAME_GENERATION_VERSION ? { frameConstrained: true } : {}),
176
+ });
177
+ const durationMs = Date.now() - inferrerStart;
178
+ agentTimingsAccum.push({ name: 'lens.inferrer', durationMs });
179
+ traceEmitter?.({ type: "agent_end", name: "lens-inferrer", durationMs, summary: result.lenses.length > 0 ? `Inferred ${result.lenses.length} lens(es)` : "lens-inferrer completed" });
180
+ if (deps.mode === HYDE_FRAME_GENERATION_VERSION) {
181
+ const sourceFrame = sanitizeHydeSourceFrame(sourceText, result.sourceFrame ?? emptyFrame());
182
+ return {
183
+ lenses: result.lenses,
184
+ sourceFrame,
185
+ frameFingerprint: computeHydeFrameFingerprint(sourceText, sourceFrame),
186
+ sourceTextHash: computeHydeSourceTextHash(sourceText),
187
+ generatedAt: nextGenerationMarker(),
188
+ agentTimings: agentTimingsAccum,
189
+ };
190
+ }
191
+ return { lenses: result.lenses, agentTimings: agentTimingsAccum };
192
+ }
193
+ catch (error) {
194
+ logger.error('Lens inference failed in graph node', { error });
195
+ if (deps.mode === HYDE_FRAME_GENERATION_VERSION) {
196
+ const sourceFrame = emptyFrame();
197
+ return {
198
+ lenses: [],
199
+ sourceFrame,
200
+ frameFingerprint: computeHydeFrameFingerprint(sourceText, sourceFrame),
201
+ sourceTextHash: computeHydeSourceTextHash(sourceText),
202
+ generatedAt: nextGenerationMarker(),
203
+ agentTimings: agentTimingsAccum,
204
+ };
205
+ }
206
+ return { lenses: [], agentTimings: agentTimingsAccum };
207
+ }
208
+ });
209
+ }
210
+ /** Node 2: Check the mode-isolated cache/DB for matching documents. */
211
+ export async function checkCacheNode(state, deps) {
212
+ return timed("HydeGraph.checkCache", async () => {
213
+ const { sourceType, sourceId, sourceText, lenses, forceRegenerate } = state;
214
+ if (forceRegenerate)
215
+ return { hydeDocuments: {} };
216
+ const frameFingerprint = deps.mode === HYDE_FRAME_GENERATION_VERSION
217
+ ? requireFrameFingerprint(state.frameFingerprint)
218
+ : undefined;
219
+ const sourceTextHash = deps.mode === HYDE_FRAME_GENERATION_VERSION
220
+ ? state.sourceTextHash ?? computeHydeSourceTextHash(sourceText)
221
+ : undefined;
222
+ const cached = {};
223
+ for (const lens of lenses) {
224
+ const key = cacheKey(deps.mode, sourceType, sourceId ?? undefined, sourceText, lens.label, lens.corpus, frameFingerprint);
225
+ const fromCache = await deps.cache.get(key);
226
+ const cacheAccepted = fromCache?.hydeText
227
+ && fromCache.hydeEmbedding?.length
228
+ && (deps.mode === 'legacy' || isFrameCacheDocument(fromCache, lens.label, frameFingerprint, sourceTextHash));
229
+ if (cacheAccepted && fromCache) {
230
+ cached[lens.label] = {
231
+ ...fromCache,
232
+ ...(deps.mode === HYDE_FRAME_GENERATION_VERSION ? { origin: 'cache' } : {}),
233
+ };
234
+ continue;
235
+ }
236
+ if (sourceId) {
237
+ const fromDb = await deps.database.getHydeDocument(sourceType, sourceId, dbStrategy(deps.mode, lens.label, lens.corpus));
238
+ const frameDbContext = deps.mode === HYDE_FRAME_GENERATION_VERSION && fromDb
239
+ ? fromDb.context
240
+ : null;
241
+ if (fromDb && (deps.mode === 'legacy' || isFrameDbContext(frameDbContext, lens.label, frameFingerprint, sourceTextHash))) {
242
+ cached[lens.label] = {
243
+ lens: lens.label,
244
+ targetCorpus: fromDb.targetCorpus,
245
+ hydeText: fromDb.hydeText,
246
+ hydeEmbedding: fromDb.hydeEmbedding,
247
+ ...(deps.mode === HYDE_FRAME_GENERATION_VERSION
248
+ ? {
249
+ origin: 'db',
250
+ validationStatus: 'valid',
251
+ hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION,
252
+ frameFingerprint,
253
+ sourceTextHash,
254
+ generatedAt: frameDbContext.generatedAt,
255
+ }
256
+ : {}),
257
+ };
258
+ }
259
+ }
260
+ }
261
+ if (deps.mode === HYDE_FRAME_GENERATION_VERSION) {
262
+ const newestTimestamp = Math.max(...Object.values(cached).map((doc) => Date.parse(doc.generatedAt ?? '')));
263
+ if (Number.isFinite(newestTimestamp)) {
264
+ return {
265
+ hydeDocuments: Object.fromEntries(Object.entries(cached).filter(([, doc]) => Date.parse(doc.generatedAt ?? '') === newestTimestamp)),
266
+ };
267
+ }
268
+ }
269
+ return { hydeDocuments: cached };
270
+ });
271
+ }
272
+ /** Node 3: Generate all missing documents and return a complete snapshot. */
273
+ export async function generateMissingNode(state, deps) {
274
+ return timed("HydeGraph.generateMissing", async () => {
275
+ const { sourceText, sourceFrame, lenses, hydeDocuments } = state;
276
+ const missing = lenses.filter((lens) => !hydeDocuments[lens.label]);
277
+ const agentTimingsAccum = [];
278
+ const generated = {};
279
+ const sourceTextHash = deps.mode === HYDE_FRAME_GENERATION_VERSION
280
+ ? state.sourceTextHash ?? computeHydeSourceTextHash(sourceText)
281
+ : undefined;
282
+ const generatedAt = deps.mode === HYDE_FRAME_GENERATION_VERSION
283
+ ? state.generatedAt ?? nextGenerationMarker()
284
+ : undefined;
285
+ await Promise.all(missing.map(async (lens) => {
286
+ const traceEmitter = requestContext.getStore()?.traceEmitter;
287
+ const generatorStart = Date.now();
288
+ traceEmitter?.({ type: "agent_start", name: "hyde-generator" });
289
+ const out = await deps.generator.generate({
290
+ sourceText,
291
+ lens: lens.label,
292
+ corpus: lens.corpus,
293
+ ...(deps.mode === HYDE_FRAME_GENERATION_VERSION && sourceFrame ? { sourceFrame } : {}),
294
+ });
295
+ const durationMs = Date.now() - generatorStart;
296
+ agentTimingsAccum.push({ name: 'hyde.generator', durationMs });
297
+ traceEmitter?.({ type: "agent_end", name: "hyde-generator", durationMs, summary: `Generated: ${lens.label}` });
298
+ generated[lens.label] = {
299
+ lens: lens.label,
300
+ targetCorpus: lens.corpus,
301
+ hydeText: out.text,
302
+ hydeEmbedding: [],
303
+ ...(deps.mode === HYDE_FRAME_GENERATION_VERSION
304
+ ? {
305
+ origin: 'generated',
306
+ frameFingerprint: requireFrameFingerprint(state.frameFingerprint),
307
+ sourceTextHash,
308
+ generatedAt,
309
+ }
310
+ : {}),
311
+ };
312
+ }));
313
+ const retained = deps.mode === HYDE_FRAME_GENERATION_VERSION
314
+ ? Object.fromEntries(Object.entries(hydeDocuments).map(([label, doc]) => [
315
+ label,
316
+ {
317
+ ...doc,
318
+ frameFingerprint: requireFrameFingerprint(state.frameFingerprint),
319
+ sourceTextHash,
320
+ generatedAt,
321
+ },
322
+ ]))
323
+ : hydeDocuments;
324
+ return { hydeDocuments: { ...retained, ...generated }, agentTimings: agentTimingsAccum };
325
+ });
326
+ }
327
+ /** Frame-v1 only: validate newly generated docs in one batch. */
328
+ export async function validateGeneratedNode(state, deps) {
329
+ return timed("HydeGraph.validateGenerated", async () => {
330
+ const generated = Object.values(state.hydeDocuments).filter((doc) => doc.origin === 'generated');
331
+ if (generated.length === 0 || !deps.validator)
332
+ return { hydeDocuments: state.hydeDocuments };
333
+ const frame = sanitizeHydeSourceFrame(state.sourceText, state.sourceFrame ?? emptyFrame());
334
+ const documents = {};
335
+ const lensByDocumentKey = new Map();
336
+ for (const doc of generated) {
337
+ const key = opaqueDocumentKey(doc);
338
+ documents[key] = { corpus: doc.targetCorpus, text: doc.hydeText };
339
+ lensByDocumentKey.set(key, doc.lens);
340
+ }
341
+ const updated = { ...state.hydeDocuments };
342
+ const agentTimingsAccum = [];
343
+ const traceEmitter = requestContext.getStore()?.traceEmitter;
344
+ const validatorStart = Date.now();
345
+ let validCount = 0;
346
+ let rejectedCount = 0;
347
+ let failedOpenCount = 0;
348
+ traceEmitter?.({ type: 'agent_start', name: 'hyde-validator' });
349
+ try {
350
+ const output = await deps.validator.validate({
351
+ sourceText: state.sourceText,
352
+ sourceFrame: frame,
353
+ documents,
354
+ });
355
+ const rawVerdicts = Array.isArray(output.verdicts)
356
+ ? output.verdicts
357
+ : [];
358
+ for (const key of Object.keys(documents)) {
359
+ const lensLabel = lensByDocumentKey.get(key);
360
+ if (!lensLabel)
361
+ continue;
362
+ const matching = rawVerdicts.filter((value) => !!value && typeof value === 'object' && value.key === key);
363
+ const doc = updated[lensLabel];
364
+ if (!doc)
365
+ continue;
366
+ if (matching.length !== 1 || !isRuntimeVerdict(matching[0])) {
367
+ failedOpenCount += 1;
368
+ updated[lensLabel] = { ...doc, validationStatus: 'failed_open', hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION };
369
+ continue;
370
+ }
371
+ const verdict = matching[0];
372
+ const hasUnsupportedGrounding = verdict.unsupportedNamedEntities.length > 0
373
+ || verdict.unsupportedHardConstraints.length > 0;
374
+ const contradictory = verdict.valid === hasUnsupportedGrounding;
375
+ if (contradictory) {
376
+ failedOpenCount += 1;
377
+ updated[lensLabel] = { ...doc, validationStatus: 'failed_open', hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION };
378
+ }
379
+ else if (!verdict.valid) {
380
+ rejectedCount += 1;
381
+ delete updated[lensLabel];
382
+ }
383
+ else {
384
+ validCount += 1;
385
+ updated[lensLabel] = { ...doc, validationStatus: 'valid', hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION };
386
+ }
387
+ }
388
+ }
389
+ catch (error) {
390
+ logger.error('HyDE validation failed open', { error });
391
+ validCount = 0;
392
+ rejectedCount = 0;
393
+ failedOpenCount = generated.length;
394
+ for (const doc of generated) {
395
+ updated[doc.lens] = { ...doc, validationStatus: 'failed_open', hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION };
396
+ }
397
+ }
398
+ finally {
399
+ const durationMs = Date.now() - validatorStart;
400
+ agentTimingsAccum.push({ name: 'hyde.validator', durationMs });
401
+ traceEmitter?.({
402
+ type: 'agent_end',
403
+ name: 'hyde-validator',
404
+ durationMs,
405
+ summary: `${validCount} valid, ${rejectedCount} rejected, ${failedOpenCount} failed open`,
406
+ });
407
+ }
408
+ return { hydeDocuments: updated, agentTimings: agentTimingsAccum };
409
+ });
410
+ }
411
+ /** Embed all accepted/cached documents that do not have embeddings. */
412
+ export async function embedNode(state, deps) {
413
+ return timed("HydeGraph.embed", async () => {
414
+ const toEmbed = [];
415
+ const updated = {};
416
+ const hydeEmbeddings = {};
417
+ for (const [label, doc] of Object.entries(state.hydeDocuments)) {
418
+ if (doc.hydeEmbedding?.length) {
419
+ updated[label] = doc;
420
+ hydeEmbeddings[label] = doc.hydeEmbedding;
421
+ }
422
+ else {
423
+ toEmbed.push({ label, doc });
424
+ }
425
+ }
426
+ if (toEmbed.length > 0) {
427
+ const embeddings = await deps.embedder.generate(toEmbed.map((item) => item.doc.hydeText), undefined, getAbortSignalConfig());
428
+ const embeddingArray = Array.isArray(embeddings[0]) ? embeddings : [embeddings];
429
+ for (let i = 0; i < toEmbed.length; i++) {
430
+ const { label, doc } = toEmbed[i];
431
+ const embedding = embeddingArray[i] ?? [];
432
+ updated[label] = { ...doc, hydeEmbedding: embedding };
433
+ hydeEmbeddings[label] = embedding;
434
+ }
435
+ }
436
+ return { hydeDocuments: updated, hydeEmbeddings };
437
+ });
438
+ }
439
+ /** Cache/persist only legacy docs or successfully validated frame-v1 docs. */
440
+ export async function cacheResultsNode(state, deps) {
441
+ return timed("HydeGraph.cacheResults", async () => {
442
+ const { sourceType, sourceId, sourceText, hydeDocuments } = state;
443
+ const frameFingerprint = deps.mode === HYDE_FRAME_GENERATION_VERSION
444
+ ? requireFrameFingerprint(state.frameFingerprint)
445
+ : undefined;
446
+ const sourceTextHash = deps.mode === HYDE_FRAME_GENERATION_VERSION
447
+ ? state.sourceTextHash ?? computeHydeSourceTextHash(sourceText)
448
+ : undefined;
449
+ for (const [label, doc] of Object.entries(hydeDocuments)) {
450
+ if (deps.mode === HYDE_FRAME_GENERATION_VERSION
451
+ && !isFrameCacheDocument(doc, label, frameFingerprint, sourceTextHash))
452
+ continue;
453
+ const key = cacheKey(deps.mode, sourceType, sourceId ?? undefined, sourceText, label, doc.targetCorpus, frameFingerprint);
454
+ await deps.cache.set(key, doc, { ttl: HYDE_DEFAULT_CACHE_TTL });
455
+ if (sourceId) {
456
+ await deps.database.saveHydeDocument({
457
+ sourceType,
458
+ sourceId,
459
+ strategy: dbStrategy(deps.mode, label, doc.targetCorpus),
460
+ targetCorpus: doc.targetCorpus,
461
+ hydeText: doc.hydeText,
462
+ hydeEmbedding: doc.hydeEmbedding,
463
+ ...(deps.mode === HYDE_FRAME_GENERATION_VERSION ? {
464
+ context: {
465
+ hydeGenerationVersion: HYDE_FRAME_GENERATION_VERSION,
466
+ lensLabel: label,
467
+ validationStatus: 'valid',
468
+ frameFingerprint: doc.frameFingerprint,
469
+ sourceTextHash: doc.sourceTextHash,
470
+ generatedAt: doc.generatedAt,
471
+ },
472
+ } : {}),
473
+ });
474
+ }
475
+ }
476
+ return {};
477
+ });
478
+ }
479
+ /** After the cache check: generate the missing documents, or skip straight to embedding. */
480
+ export function shouldGenerate(state) {
481
+ return state.lenses.some((lens) => !state.hydeDocuments[lens.label]) ? 'generate' : 'skip';
482
+ }