@indexnetwork/protocol 0.1.0

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 (365) hide show
  1. package/dist/agents/chat.agent.d.ts +218 -0
  2. package/dist/agents/chat.agent.d.ts.map +1 -0
  3. package/dist/agents/chat.agent.js +884 -0
  4. package/dist/agents/chat.agent.js.map +1 -0
  5. package/dist/agents/chat.prompt.d.ts +18 -0
  6. package/dist/agents/chat.prompt.d.ts.map +1 -0
  7. package/dist/agents/chat.prompt.js +372 -0
  8. package/dist/agents/chat.prompt.js.map +1 -0
  9. package/dist/agents/chat.prompt.modules.d.ts +61 -0
  10. package/dist/agents/chat.prompt.modules.d.ts.map +1 -0
  11. package/dist/agents/chat.prompt.modules.js +366 -0
  12. package/dist/agents/chat.prompt.modules.js.map +1 -0
  13. package/dist/agents/chat.title.generator.d.ts +20 -0
  14. package/dist/agents/chat.title.generator.d.ts.map +1 -0
  15. package/dist/agents/chat.title.generator.js +66 -0
  16. package/dist/agents/chat.title.generator.js.map +1 -0
  17. package/dist/agents/home.categorizer.d.ts +28 -0
  18. package/dist/agents/home.categorizer.d.ts.map +1 -0
  19. package/dist/agents/home.categorizer.js +170 -0
  20. package/dist/agents/home.categorizer.js.map +1 -0
  21. package/dist/agents/hyde.generator.d.ts +27 -0
  22. package/dist/agents/hyde.generator.d.ts.map +1 -0
  23. package/dist/agents/hyde.generator.js +75 -0
  24. package/dist/agents/hyde.generator.js.map +1 -0
  25. package/dist/agents/hyde.strategies.d.ts +17 -0
  26. package/dist/agents/hyde.strategies.d.ts.map +1 -0
  27. package/dist/agents/hyde.strategies.js +29 -0
  28. package/dist/agents/hyde.strategies.js.map +1 -0
  29. package/dist/agents/intent.clarifier.d.ts +29 -0
  30. package/dist/agents/intent.clarifier.d.ts.map +1 -0
  31. package/dist/agents/intent.clarifier.js +186 -0
  32. package/dist/agents/intent.clarifier.js.map +1 -0
  33. package/dist/agents/intent.indexer.d.ts +77 -0
  34. package/dist/agents/intent.indexer.d.ts.map +1 -0
  35. package/dist/agents/intent.indexer.js +164 -0
  36. package/dist/agents/intent.indexer.js.map +1 -0
  37. package/dist/agents/intent.inferrer.d.ts +95 -0
  38. package/dist/agents/intent.inferrer.d.ts.map +1 -0
  39. package/dist/agents/intent.inferrer.js +238 -0
  40. package/dist/agents/intent.inferrer.js.map +1 -0
  41. package/dist/agents/intent.reconciler.d.ts +106 -0
  42. package/dist/agents/intent.reconciler.d.ts.map +1 -0
  43. package/dist/agents/intent.reconciler.js +184 -0
  44. package/dist/agents/intent.reconciler.js.map +1 -0
  45. package/dist/agents/intent.verifier.d.ts +97 -0
  46. package/dist/agents/intent.verifier.d.ts.map +1 -0
  47. package/dist/agents/intent.verifier.js +234 -0
  48. package/dist/agents/intent.verifier.js.map +1 -0
  49. package/dist/agents/invite.generator.d.ts +47 -0
  50. package/dist/agents/invite.generator.d.ts.map +1 -0
  51. package/dist/agents/invite.generator.js +56 -0
  52. package/dist/agents/invite.generator.js.map +1 -0
  53. package/dist/agents/lens.inferrer.d.ts +37 -0
  54. package/dist/agents/lens.inferrer.d.ts.map +1 -0
  55. package/dist/agents/lens.inferrer.js +98 -0
  56. package/dist/agents/lens.inferrer.js.map +1 -0
  57. package/dist/agents/model.config.d.ts +120 -0
  58. package/dist/agents/model.config.d.ts.map +1 -0
  59. package/dist/agents/model.config.js +76 -0
  60. package/dist/agents/model.config.js.map +1 -0
  61. package/dist/agents/negotiation.insights.generator.d.ts +32 -0
  62. package/dist/agents/negotiation.insights.generator.d.ts.map +1 -0
  63. package/dist/agents/negotiation.insights.generator.js +105 -0
  64. package/dist/agents/negotiation.insights.generator.js.map +1 -0
  65. package/dist/agents/negotiation.proposer.d.ts +26 -0
  66. package/dist/agents/negotiation.proposer.d.ts.map +1 -0
  67. package/dist/agents/negotiation.proposer.js +67 -0
  68. package/dist/agents/negotiation.proposer.js.map +1 -0
  69. package/dist/agents/negotiation.responder.d.ts +26 -0
  70. package/dist/agents/negotiation.responder.d.ts.map +1 -0
  71. package/dist/agents/negotiation.responder.js +71 -0
  72. package/dist/agents/negotiation.responder.js.map +1 -0
  73. package/dist/agents/opportunity.evaluator.d.ts +253 -0
  74. package/dist/agents/opportunity.evaluator.d.ts.map +1 -0
  75. package/dist/agents/opportunity.evaluator.js +413 -0
  76. package/dist/agents/opportunity.evaluator.js.map +1 -0
  77. package/dist/agents/opportunity.presenter.d.ts +115 -0
  78. package/dist/agents/opportunity.presenter.d.ts.map +1 -0
  79. package/dist/agents/opportunity.presenter.js +524 -0
  80. package/dist/agents/opportunity.presenter.js.map +1 -0
  81. package/dist/agents/profile.generator.d.ts +67 -0
  82. package/dist/agents/profile.generator.d.ts.map +1 -0
  83. package/dist/agents/profile.generator.js +97 -0
  84. package/dist/agents/profile.generator.js.map +1 -0
  85. package/dist/agents/profile.hyde.generator.d.ts +43 -0
  86. package/dist/agents/profile.hyde.generator.d.ts.map +1 -0
  87. package/dist/agents/profile.hyde.generator.js +113 -0
  88. package/dist/agents/profile.hyde.generator.js.map +1 -0
  89. package/dist/agents/suggestion.generator.d.ts +24 -0
  90. package/dist/agents/suggestion.generator.d.ts.map +1 -0
  91. package/dist/agents/suggestion.generator.js +96 -0
  92. package/dist/agents/suggestion.generator.js.map +1 -0
  93. package/dist/graphs/chat.graph.d.ts +312 -0
  94. package/dist/graphs/chat.graph.d.ts.map +1 -0
  95. package/dist/graphs/chat.graph.js +267 -0
  96. package/dist/graphs/chat.graph.js.map +1 -0
  97. package/dist/graphs/home.graph.d.ts +180 -0
  98. package/dist/graphs/home.graph.d.ts.map +1 -0
  99. package/dist/graphs/home.graph.js +598 -0
  100. package/dist/graphs/home.graph.js.map +1 -0
  101. package/dist/graphs/hyde.graph.d.ts +110 -0
  102. package/dist/graphs/hyde.graph.d.ts.map +1 -0
  103. package/dist/graphs/hyde.graph.js +235 -0
  104. package/dist/graphs/hyde.graph.js.map +1 -0
  105. package/dist/graphs/index.graph.d.ts +620 -0
  106. package/dist/graphs/index.graph.d.ts.map +1 -0
  107. package/dist/graphs/index.graph.js +226 -0
  108. package/dist/graphs/index.graph.js.map +1 -0
  109. package/dist/graphs/index_membership.graph.d.ts +250 -0
  110. package/dist/graphs/index_membership.graph.d.ts.map +1 -0
  111. package/dist/graphs/index_membership.graph.js +204 -0
  112. package/dist/graphs/index_membership.graph.js.map +1 -0
  113. package/dist/graphs/intent.graph.d.ts +490 -0
  114. package/dist/graphs/intent.graph.d.ts.map +1 -0
  115. package/dist/graphs/intent.graph.js +787 -0
  116. package/dist/graphs/intent.graph.js.map +1 -0
  117. package/dist/graphs/intent_index.graph.d.ts +396 -0
  118. package/dist/graphs/intent_index.graph.d.ts.map +1 -0
  119. package/dist/graphs/intent_index.graph.js +331 -0
  120. package/dist/graphs/intent_index.graph.js.map +1 -0
  121. package/dist/graphs/maintenance.graph.d.ts +177 -0
  122. package/dist/graphs/maintenance.graph.d.ts.map +1 -0
  123. package/dist/graphs/maintenance.graph.js +173 -0
  124. package/dist/graphs/maintenance.graph.js.map +1 -0
  125. package/dist/graphs/negotiation.graph.d.ts +819 -0
  126. package/dist/graphs/negotiation.graph.d.ts.map +1 -0
  127. package/dist/graphs/negotiation.graph.js +255 -0
  128. package/dist/graphs/negotiation.graph.js.map +1 -0
  129. package/dist/graphs/opportunity.graph.d.ts +1082 -0
  130. package/dist/graphs/opportunity.graph.d.ts.map +1 -0
  131. package/dist/graphs/opportunity.graph.js +2534 -0
  132. package/dist/graphs/opportunity.graph.js.map +1 -0
  133. package/dist/graphs/profile.graph.d.ts +617 -0
  134. package/dist/graphs/profile.graph.d.ts.map +1 -0
  135. package/dist/graphs/profile.graph.js +839 -0
  136. package/dist/graphs/profile.graph.js.map +1 -0
  137. package/dist/graphs/tests/chat.graph.mocks.d.ts +104 -0
  138. package/dist/graphs/tests/chat.graph.mocks.d.ts.map +1 -0
  139. package/dist/graphs/tests/chat.graph.mocks.js +225 -0
  140. package/dist/graphs/tests/chat.graph.mocks.js.map +1 -0
  141. package/dist/index.d.ts +62 -0
  142. package/dist/index.d.ts.map +1 -0
  143. package/dist/index.js +44 -0
  144. package/dist/index.js.map +1 -0
  145. package/dist/interfaces/auth.interface.d.ts +15 -0
  146. package/dist/interfaces/auth.interface.d.ts.map +1 -0
  147. package/dist/interfaces/auth.interface.js +2 -0
  148. package/dist/interfaces/auth.interface.js.map +1 -0
  149. package/dist/interfaces/cache.interface.d.ts +43 -0
  150. package/dist/interfaces/cache.interface.d.ts.map +1 -0
  151. package/dist/interfaces/cache.interface.js +6 -0
  152. package/dist/interfaces/cache.interface.js.map +1 -0
  153. package/dist/interfaces/chat-session.interface.d.ts +11 -0
  154. package/dist/interfaces/chat-session.interface.d.ts.map +1 -0
  155. package/dist/interfaces/chat-session.interface.js +2 -0
  156. package/dist/interfaces/chat-session.interface.js.map +1 -0
  157. package/dist/interfaces/contact.interface.d.ts +48 -0
  158. package/dist/interfaces/contact.interface.d.ts.map +1 -0
  159. package/dist/interfaces/contact.interface.js +2 -0
  160. package/dist/interfaces/contact.interface.js.map +1 -0
  161. package/dist/interfaces/database.interface.d.ts +1495 -0
  162. package/dist/interfaces/database.interface.d.ts.map +1 -0
  163. package/dist/interfaces/database.interface.js +2 -0
  164. package/dist/interfaces/database.interface.js.map +1 -0
  165. package/dist/interfaces/embedder.interface.d.ts +85 -0
  166. package/dist/interfaces/embedder.interface.d.ts.map +1 -0
  167. package/dist/interfaces/embedder.interface.js +5 -0
  168. package/dist/interfaces/embedder.interface.js.map +1 -0
  169. package/dist/interfaces/enrichment.interface.d.ts +40 -0
  170. package/dist/interfaces/enrichment.interface.d.ts.map +1 -0
  171. package/dist/interfaces/enrichment.interface.js +2 -0
  172. package/dist/interfaces/enrichment.interface.js.map +1 -0
  173. package/dist/interfaces/integration.interface.d.ts +91 -0
  174. package/dist/interfaces/integration.interface.d.ts.map +1 -0
  175. package/dist/interfaces/integration.interface.js +2 -0
  176. package/dist/interfaces/integration.interface.js.map +1 -0
  177. package/dist/interfaces/queue.interface.d.ts +17 -0
  178. package/dist/interfaces/queue.interface.d.ts.map +1 -0
  179. package/dist/interfaces/queue.interface.js +5 -0
  180. package/dist/interfaces/queue.interface.js.map +1 -0
  181. package/dist/interfaces/scraper.interface.d.ts +31 -0
  182. package/dist/interfaces/scraper.interface.d.ts.map +1 -0
  183. package/dist/interfaces/scraper.interface.js +2 -0
  184. package/dist/interfaces/scraper.interface.js.map +1 -0
  185. package/dist/interfaces/storage.interface.d.ts +46 -0
  186. package/dist/interfaces/storage.interface.d.ts.map +1 -0
  187. package/dist/interfaces/storage.interface.js +6 -0
  188. package/dist/interfaces/storage.interface.js.map +1 -0
  189. package/dist/mcp/mcp.server.d.ts +29 -0
  190. package/dist/mcp/mcp.server.d.ts.map +1 -0
  191. package/dist/mcp/mcp.server.js +171 -0
  192. package/dist/mcp/mcp.server.js.map +1 -0
  193. package/dist/states/chat.state.d.ts +126 -0
  194. package/dist/states/chat.state.d.ts.map +1 -0
  195. package/dist/states/chat.state.js +112 -0
  196. package/dist/states/chat.state.js.map +1 -0
  197. package/dist/states/home.state.d.ts +100 -0
  198. package/dist/states/home.state.d.ts.map +1 -0
  199. package/dist/states/home.state.js +74 -0
  200. package/dist/states/home.state.js.map +1 -0
  201. package/dist/states/hyde.state.d.ts +54 -0
  202. package/dist/states/hyde.state.d.ts.map +1 -0
  203. package/dist/states/hyde.state.js +66 -0
  204. package/dist/states/hyde.state.js.map +1 -0
  205. package/dist/states/index.state.d.ts +179 -0
  206. package/dist/states/index.state.d.ts.map +1 -0
  207. package/dist/states/index.state.js +56 -0
  208. package/dist/states/index.state.js.map +1 -0
  209. package/dist/states/index_membership.state.d.ts +77 -0
  210. package/dist/states/index_membership.state.d.ts.map +1 -0
  211. package/dist/states/index_membership.state.js +43 -0
  212. package/dist/states/index_membership.state.js.map +1 -0
  213. package/dist/states/intent.state.d.ts +203 -0
  214. package/dist/states/intent.state.d.ts.map +1 -0
  215. package/dist/states/intent.state.js +153 -0
  216. package/dist/states/intent.state.js.map +1 -0
  217. package/dist/states/intent_index.state.d.ts +148 -0
  218. package/dist/states/intent_index.state.d.ts.map +1 -0
  219. package/dist/states/intent_index.state.js +100 -0
  220. package/dist/states/intent_index.state.js.map +1 -0
  221. package/dist/states/maintenance.state.d.ts +36 -0
  222. package/dist/states/maintenance.state.d.ts.map +1 -0
  223. package/dist/states/maintenance.state.js +56 -0
  224. package/dist/states/maintenance.state.js.map +1 -0
  225. package/dist/states/negotiation.state.d.ts +230 -0
  226. package/dist/states/negotiation.state.d.ts.map +1 -0
  227. package/dist/states/negotiation.state.js +82 -0
  228. package/dist/states/negotiation.state.js.map +1 -0
  229. package/dist/states/opportunity.state.d.ts +300 -0
  230. package/dist/states/opportunity.state.d.ts.map +1 -0
  231. package/dist/states/opportunity.state.js +207 -0
  232. package/dist/states/opportunity.state.js.map +1 -0
  233. package/dist/states/profile.state.d.ts +172 -0
  234. package/dist/states/profile.state.d.ts.map +1 -0
  235. package/dist/states/profile.state.js +133 -0
  236. package/dist/states/profile.state.js.map +1 -0
  237. package/dist/streamers/chat.streamer.d.ts +55 -0
  238. package/dist/streamers/chat.streamer.d.ts.map +1 -0
  239. package/dist/streamers/chat.streamer.js +186 -0
  240. package/dist/streamers/chat.streamer.js.map +1 -0
  241. package/dist/streamers/index.d.ts +3 -0
  242. package/dist/streamers/index.d.ts.map +1 -0
  243. package/dist/streamers/index.js +3 -0
  244. package/dist/streamers/index.js.map +1 -0
  245. package/dist/streamers/response.streamer.d.ts +36 -0
  246. package/dist/streamers/response.streamer.d.ts.map +1 -0
  247. package/dist/streamers/response.streamer.js +46 -0
  248. package/dist/streamers/response.streamer.js.map +1 -0
  249. package/dist/support/chat.utils.d.ts +42 -0
  250. package/dist/support/chat.utils.d.ts.map +1 -0
  251. package/dist/support/chat.utils.js +89 -0
  252. package/dist/support/chat.utils.js.map +1 -0
  253. package/dist/support/debug-meta.sanitizer.d.ts +18 -0
  254. package/dist/support/debug-meta.sanitizer.d.ts.map +1 -0
  255. package/dist/support/debug-meta.sanitizer.js +82 -0
  256. package/dist/support/debug-meta.sanitizer.js.map +1 -0
  257. package/dist/support/feed.health.d.ts +32 -0
  258. package/dist/support/feed.health.d.ts.map +1 -0
  259. package/dist/support/feed.health.js +76 -0
  260. package/dist/support/feed.health.js.map +1 -0
  261. package/dist/support/introducer.discovery.d.ts +78 -0
  262. package/dist/support/introducer.discovery.d.ts.map +1 -0
  263. package/dist/support/introducer.discovery.js +101 -0
  264. package/dist/support/introducer.discovery.js.map +1 -0
  265. package/dist/support/log.d.ts +65 -0
  266. package/dist/support/log.d.ts.map +1 -0
  267. package/dist/support/log.js +76 -0
  268. package/dist/support/log.js.map +1 -0
  269. package/dist/support/lucide.icon-catalog.d.ts +22 -0
  270. package/dist/support/lucide.icon-catalog.d.ts.map +1 -0
  271. package/dist/support/lucide.icon-catalog.js +101 -0
  272. package/dist/support/lucide.icon-catalog.js.map +1 -0
  273. package/dist/support/opportunity.card-text.d.ts +39 -0
  274. package/dist/support/opportunity.card-text.d.ts.map +1 -0
  275. package/dist/support/opportunity.card-text.js +333 -0
  276. package/dist/support/opportunity.card-text.js.map +1 -0
  277. package/dist/support/opportunity.constants.d.ts +9 -0
  278. package/dist/support/opportunity.constants.d.ts.map +1 -0
  279. package/dist/support/opportunity.constants.js +11 -0
  280. package/dist/support/opportunity.constants.js.map +1 -0
  281. package/dist/support/opportunity.discover.d.ts +144 -0
  282. package/dist/support/opportunity.discover.d.ts.map +1 -0
  283. package/dist/support/opportunity.discover.js +610 -0
  284. package/dist/support/opportunity.discover.js.map +1 -0
  285. package/dist/support/opportunity.enricher.d.ts +44 -0
  286. package/dist/support/opportunity.enricher.d.ts.map +1 -0
  287. package/dist/support/opportunity.enricher.js +245 -0
  288. package/dist/support/opportunity.enricher.js.map +1 -0
  289. package/dist/support/opportunity.persist.d.ts +39 -0
  290. package/dist/support/opportunity.persist.d.ts.map +1 -0
  291. package/dist/support/opportunity.persist.js +63 -0
  292. package/dist/support/opportunity.persist.js.map +1 -0
  293. package/dist/support/opportunity.presentation.d.ts +21 -0
  294. package/dist/support/opportunity.presentation.d.ts.map +1 -0
  295. package/dist/support/opportunity.presentation.js +75 -0
  296. package/dist/support/opportunity.presentation.js.map +1 -0
  297. package/dist/support/opportunity.sanitize.d.ts +18 -0
  298. package/dist/support/opportunity.sanitize.d.ts.map +1 -0
  299. package/dist/support/opportunity.sanitize.js +89 -0
  300. package/dist/support/opportunity.sanitize.js.map +1 -0
  301. package/dist/support/opportunity.utils.d.ts +99 -0
  302. package/dist/support/opportunity.utils.d.ts.map +1 -0
  303. package/dist/support/opportunity.utils.js +184 -0
  304. package/dist/support/opportunity.utils.js.map +1 -0
  305. package/dist/support/performance.d.ts +19 -0
  306. package/dist/support/performance.d.ts.map +1 -0
  307. package/dist/support/performance.js +43 -0
  308. package/dist/support/performance.js.map +1 -0
  309. package/dist/support/profile.enrichment-display-name.d.ts +16 -0
  310. package/dist/support/profile.enrichment-display-name.d.ts.map +1 -0
  311. package/dist/support/profile.enrichment-display-name.js +22 -0
  312. package/dist/support/profile.enrichment-display-name.js.map +1 -0
  313. package/dist/support/protocol.logger.d.ts +22 -0
  314. package/dist/support/protocol.logger.d.ts.map +1 -0
  315. package/dist/support/protocol.logger.js +44 -0
  316. package/dist/support/protocol.logger.js.map +1 -0
  317. package/dist/support/request-context.d.ts +19 -0
  318. package/dist/support/request-context.d.ts.map +1 -0
  319. package/dist/support/request-context.js +7 -0
  320. package/dist/support/request-context.js.map +1 -0
  321. package/dist/tools/contact.tools.d.ts +7 -0
  322. package/dist/tools/contact.tools.d.ts.map +1 -0
  323. package/dist/tools/contact.tools.js +115 -0
  324. package/dist/tools/contact.tools.js.map +1 -0
  325. package/dist/tools/index.d.ts +17 -0
  326. package/dist/tools/index.d.ts.map +1 -0
  327. package/dist/tools/index.js +140 -0
  328. package/dist/tools/index.js.map +1 -0
  329. package/dist/tools/index.tools.d.ts +3 -0
  330. package/dist/tools/index.tools.d.ts.map +1 -0
  331. package/dist/tools/index.tools.js +423 -0
  332. package/dist/tools/index.tools.js.map +1 -0
  333. package/dist/tools/integration.tools.d.ts +13 -0
  334. package/dist/tools/integration.tools.d.ts.map +1 -0
  335. package/dist/tools/integration.tools.js +77 -0
  336. package/dist/tools/integration.tools.js.map +1 -0
  337. package/dist/tools/intent.tools.d.ts +3 -0
  338. package/dist/tools/intent.tools.d.ts.map +1 -0
  339. package/dist/tools/intent.tools.js +458 -0
  340. package/dist/tools/intent.tools.js.map +1 -0
  341. package/dist/tools/opportunity.tools.d.ts +44 -0
  342. package/dist/tools/opportunity.tools.d.ts.map +1 -0
  343. package/dist/tools/opportunity.tools.js +814 -0
  344. package/dist/tools/opportunity.tools.js.map +1 -0
  345. package/dist/tools/profile.tools.d.ts +3 -0
  346. package/dist/tools/profile.tools.d.ts.map +1 -0
  347. package/dist/tools/profile.tools.js +513 -0
  348. package/dist/tools/profile.tools.js.map +1 -0
  349. package/dist/tools/tool.helpers.d.ts +225 -0
  350. package/dist/tools/tool.helpers.d.ts.map +1 -0
  351. package/dist/tools/tool.helpers.js +172 -0
  352. package/dist/tools/tool.helpers.js.map +1 -0
  353. package/dist/tools/tool.registry.d.ts +12 -0
  354. package/dist/tools/tool.registry.d.ts.map +1 -0
  355. package/dist/tools/tool.registry.js +62 -0
  356. package/dist/tools/tool.registry.js.map +1 -0
  357. package/dist/tools/utility.tools.d.ts +3 -0
  358. package/dist/tools/utility.tools.d.ts.map +1 -0
  359. package/dist/tools/utility.tools.js +107 -0
  360. package/dist/tools/utility.tools.js.map +1 -0
  361. package/dist/types/chat-streaming.types.d.ts +472 -0
  362. package/dist/types/chat-streaming.types.d.ts.map +1 -0
  363. package/dist/types/chat-streaming.types.js +260 -0
  364. package/dist/types/chat-streaming.types.js.map +1 -0
  365. package/package.json +32 -0
@@ -0,0 +1,839 @@
1
+ import { StateGraph, START, END } from "@langchain/langgraph";
2
+ import { ProfileGraphState } from "../states/profile.state.js";
3
+ import { ProfileGenerator } from "../agents/profile.generator.js";
4
+ import { HydeGenerator } from "../agents/profile.hyde.generator.js";
5
+ import { shouldEnrichGhostDisplayNameFromParallel } from "../support/profile.enrichment-display-name.js";
6
+ import { protocolLogger } from "../support/protocol.logger.js";
7
+ import { timed } from "../support/performance.js";
8
+ import { requestContext } from "../support/request-context.js";
9
+ const logger = protocolLogger("ProfileGraphFactory");
10
+ /** Minimum length for input to be considered meaningful (e.g. not just "Yes") */
11
+ const MIN_MEANINGFUL_INPUT_LENGTH = 20;
12
+ /** Phrases that are confirmations only and must not be used as profile content */
13
+ const CONFIRMATION_PHRASES = new Set([
14
+ "yes", "yeah", "yep", "sure", "ok", "okay", "go ahead", "do it", "please",
15
+ "correct", "right", "exactly", "absolutely", "of course", "sounds good",
16
+ "create one", "create it", "set one up", "set it up", "create my profile",
17
+ "create profile", "set up profile", "create a profile"
18
+ ]);
19
+ /**
20
+ * Returns true only if the input contains real profile information.
21
+ * Confirmation-only replies (e.g. "Yes" to "Would you like to create a profile?")
22
+ * must not be treated as input so we ask for user info / use scraper instead of inventing a profile.
23
+ */
24
+ function isMeaningfulProfileInput(input) {
25
+ if (!input || typeof input !== "string")
26
+ return false;
27
+ const trimmed = input.trim();
28
+ if (trimmed.length < MIN_MEANINGFUL_INPUT_LENGTH)
29
+ return false;
30
+ const lower = trimmed.toLowerCase();
31
+ if (CONFIRMATION_PHRASES.has(lower))
32
+ return false;
33
+ if (CONFIRMATION_PHRASES.has(lower.replace(/[.!?]+$/, "")))
34
+ return false;
35
+ return true;
36
+ }
37
+ /**
38
+ * Returns true only when the value is a fully valid numeric vector (flat or nested).
39
+ * Used so we don't treat DB returns (e.g. pg vector as string, or empty/partial array) as "has embedding".
40
+ * Ensures callers re-embed when vectors contain non-number or NaN/Infinity.
41
+ */
42
+ function hasValidProfileEmbedding(embedding) {
43
+ if (embedding == null)
44
+ return false;
45
+ if (!Array.isArray(embedding))
46
+ return false;
47
+ if (embedding.length === 0)
48
+ return false;
49
+ const first = embedding[0];
50
+ if (Array.isArray(first)) {
51
+ // Nested: number[][]
52
+ for (let i = 0; i < embedding.length; i++) {
53
+ const sub = embedding[i];
54
+ if (!Array.isArray(sub) || sub.length === 0)
55
+ return false;
56
+ for (let j = 0; j < sub.length; j++) {
57
+ const v = sub[j];
58
+ if (typeof v !== "number" || !Number.isFinite(v))
59
+ return false;
60
+ }
61
+ }
62
+ return true;
63
+ }
64
+ // Flat: number[]
65
+ for (let i = 0; i < embedding.length; i++) {
66
+ const v = embedding[i];
67
+ if (typeof v !== "number" || !Number.isFinite(v))
68
+ return false;
69
+ }
70
+ return true;
71
+ }
72
+ /**
73
+ * Factory class to build and compile the Profile Generation Graph.
74
+ *
75
+ * Flow:
76
+ * 1. check_state - Detect what's missing (profile, embeddings, hyde)
77
+ * 2. Conditional routing based on operation mode and missing components:
78
+ * - Query mode: Return immediately (fast path)
79
+ * - Write mode: Generate only what's needed
80
+ * 3. Profile generation (if needed)
81
+ * 4. Profile embedding (if needed)
82
+ * 5. HyDE generation (if needed or profile updated)
83
+ * 6. HyDE embedding (if needed)
84
+ *
85
+ * Key Features:
86
+ * - Read/Write separation (query vs write)
87
+ * - Conditional generation (skip expensive operations if data exists)
88
+ * - Automatic hyde regeneration when profile is updated
89
+ */
90
+ export class ProfileGraphFactory {
91
+ constructor(database, embedder, scraper, enricher) {
92
+ this.database = database;
93
+ this.embedder = embedder;
94
+ this.scraper = scraper;
95
+ this.enricher = enricher;
96
+ }
97
+ createGraph() {
98
+ const profileGenerator = new ProfileGenerator();
99
+ const hydeGenerator = new HydeGenerator();
100
+ // ─────────────────────────────────────────────────────────
101
+ // NODE: Check State
102
+ // Loads existing profile from DB and detects what needs generation:
103
+ // - Profile missing
104
+ // - Profile embedding missing
105
+ // - HyDE description missing
106
+ // - HyDE embedding missing
107
+ // - User information insufficient for scraping
108
+ // ─────────────────────────────────────────────────────────
109
+ const checkStateNode = async (state) => {
110
+ return timed("ProfileGraph.checkState", async () => {
111
+ if (!state.userId) {
112
+ logger.error("Missing userId");
113
+ return {
114
+ error: "userId is required"
115
+ };
116
+ }
117
+ logger.verbose("Checking profile state...", {
118
+ userId: state.userId,
119
+ operationMode: state.operationMode,
120
+ forceUpdate: state.forceUpdate
121
+ });
122
+ try {
123
+ const profile = await this.database.getProfile(state.userId);
124
+ // Query mode: Just return the profile (fast path)
125
+ if (state.operationMode === 'query') {
126
+ logger.verbose("🚀 Query mode - returning existing profile (fast path)", {
127
+ hasProfile: !!profile
128
+ });
129
+ const profileWithId = profile ? await this.database.getProfileByUserId(state.userId) : null;
130
+ return {
131
+ profile: profile || undefined,
132
+ readResult: profile
133
+ ? {
134
+ hasProfile: true,
135
+ profile: {
136
+ id: profileWithId?.id,
137
+ name: profile.identity.name,
138
+ bio: profile.identity.bio,
139
+ location: profile.identity.location,
140
+ skills: profile.attributes.skills,
141
+ interests: profile.attributes.interests,
142
+ },
143
+ }
144
+ : {
145
+ hasProfile: false,
146
+ message: "You don't have a profile yet. Would you like to create one? You can share your LinkedIn, GitHub, or X/Twitter profile, or just tell me about yourself.",
147
+ },
148
+ };
149
+ }
150
+ // Write mode: Detect what needs generation
151
+ // Treat confirmation-only input (e.g. "Yes") as no input so we ask for info / use scraper
152
+ const hasMeaningfulInput = !!state.input && isMeaningfulProfileInput(state.input);
153
+ const needsProfileGeneration = !profile || (state.forceUpdate && hasMeaningfulInput);
154
+ const needsProfileEmbedding = profile && !hasValidProfileEmbedding(profile.embedding);
155
+ const existingHydeDoc = await this.database.getHydeDocument('profile', state.userId, 'mirror');
156
+ const needsHydeGeneration = !existingHydeDoc || (state.forceUpdate && hasMeaningfulInput);
157
+ const needsHydeEmbedding = false; // Profile HyDE lives in hyde_documents; no partial "text only" state
158
+ // Check if we need to scrape (profile generation needed but no meaningful input provided)
159
+ const willNeedScraping = needsProfileGeneration && !hasMeaningfulInput;
160
+ // If we need to scrape, check if we have sufficient user information
161
+ let needsUserInfo = false;
162
+ let missingUserInfo = [];
163
+ if (willNeedScraping) {
164
+ logger.verbose("Will need scraping - checking user information...");
165
+ const user = await this.database.getUser(state.userId);
166
+ if (!user) {
167
+ logger.error("User not found", { userId: state.userId });
168
+ return {
169
+ error: `User not found: ${state.userId}`
170
+ };
171
+ }
172
+ // Check what information we have from the user table (schema: users)
173
+ // Required fields: email, name (always present)
174
+ // Optional fields: intro, avatar, location, socials
175
+ const hasSocials = !!(user.socials && (user.socials.x ||
176
+ user.socials.linkedin ||
177
+ user.socials.github ||
178
+ (user.socials.websites && user.socials.websites.length > 0)));
179
+ // Check if name is a full name (not just email username)
180
+ // For scraping to work well, we need first + last name
181
+ const hasMeaningfulName = user.name &&
182
+ user.name.trim() !== '' &&
183
+ !user.name.includes('@') &&
184
+ user.name.split(/\s+/).filter(Boolean).length >= 2;
185
+ const hasLocation = !!(user.location && user.location.trim() !== '');
186
+ // Minimum requirement for accurate scraping:
187
+ // - At least ONE social link (preferred - most reliable for finding the right person)
188
+ // - OR a full name (first + last) - less reliable but workable
189
+ // Location helps disambiguate but is not required
190
+ const hasMinimumInfo = hasSocials || hasMeaningfulName;
191
+ if (!hasMinimumInfo) {
192
+ needsUserInfo = true;
193
+ // Build precise list of what's missing and would help
194
+ if (!hasSocials) {
195
+ missingUserInfo.push('social_urls');
196
+ }
197
+ if (!hasMeaningfulName) {
198
+ missingUserInfo.push('full_name');
199
+ }
200
+ if (!hasLocation) {
201
+ missingUserInfo.push('location'); // Nice to have
202
+ }
203
+ logger.verbose("⚠️ Insufficient user information for scraping", {
204
+ hasSocials,
205
+ hasMeaningfulName,
206
+ hasLocation,
207
+ currentName: user.name,
208
+ missingUserInfo
209
+ });
210
+ }
211
+ else {
212
+ logger.verbose("✅ Sufficient user information for scraping", {
213
+ hasSocials,
214
+ hasMeaningfulName,
215
+ hasLocation,
216
+ willProceedWith: hasSocials ? 'social links' : 'full name'
217
+ });
218
+ }
219
+ }
220
+ logger.verbose("📊 State detection complete", {
221
+ hasProfile: !!profile,
222
+ needsProfileGeneration,
223
+ needsProfileEmbedding,
224
+ needsHydeGeneration,
225
+ needsHydeEmbedding,
226
+ needsUserInfo,
227
+ missingUserInfo,
228
+ forceUpdate: state.forceUpdate,
229
+ hasInput: !!state.input,
230
+ hasMeaningfulInput,
231
+ hasHydeDocument: !!existingHydeDoc,
232
+ });
233
+ return {
234
+ profile: profile || undefined,
235
+ hydeDescription: existingHydeDoc?.hydeText ?? undefined,
236
+ needsProfileGeneration,
237
+ needsProfileEmbedding,
238
+ needsHydeGeneration,
239
+ needsHydeEmbedding,
240
+ needsUserInfo,
241
+ missingUserInfo
242
+ };
243
+ }
244
+ catch (error) {
245
+ logger.error("Failed to load profile", {
246
+ error: error instanceof Error ? error.message : String(error)
247
+ });
248
+ return {
249
+ profile: undefined,
250
+ error: "Failed to load profile from database"
251
+ };
252
+ }
253
+ });
254
+ };
255
+ // ─────────────────────────────────────────────────────────
256
+ // NODE: Scrape
257
+ // Scrapes data from web if input is not provided
258
+ // ─────────────────────────────────────────────────────────
259
+ const scrapeNode = async (state) => {
260
+ return timed("ProfileGraph.scrape", async () => {
261
+ if (state.input && isMeaningfulProfileInput(state.input)) {
262
+ logger.verbose("Meaningful input already provided - skipping scrape");
263
+ return {};
264
+ }
265
+ logger.verbose("Starting web scrape...", {
266
+ userId: state.userId
267
+ });
268
+ try {
269
+ // Fetch user details to construct objective for web scraping
270
+ const user = await this.database.getUser(state.userId);
271
+ if (!user) {
272
+ logger.error("User not found", { userId: state.userId });
273
+ return {
274
+ error: `User not found: ${state.userId}`
275
+ };
276
+ }
277
+ // Build scraping objective from available user information
278
+ // Priority: social links (most reliable) > name + location > email
279
+ const socialParts = [];
280
+ if (user.socials) {
281
+ if (user.socials.x)
282
+ socialParts.push(`X/Twitter: ${user.socials.x}`);
283
+ if (user.socials.linkedin)
284
+ socialParts.push(`LinkedIn: ${user.socials.linkedin}`);
285
+ if (user.socials.github)
286
+ socialParts.push(`GitHub: ${user.socials.github}`);
287
+ if (user.socials.websites && user.socials.websites.length > 0) {
288
+ user.socials.websites.forEach((url) => socialParts.push(`Website: ${url}`));
289
+ }
290
+ }
291
+ // Construct objective based on what we have
292
+ let objective = `Find information about ${user.name || 'this person'}`;
293
+ if (user.location) {
294
+ objective += ` located in ${user.location}`;
295
+ }
296
+ objective += '.\n\n';
297
+ if (socialParts.length > 0) {
298
+ objective += `Their social profiles:\n${socialParts.join('\n')}\n\n`;
299
+ objective += 'Use these links to find accurate information about their professional background, skills, and interests.';
300
+ }
301
+ else if (user.email) {
302
+ objective += `Their email: ${user.email}\n\n`;
303
+ objective += 'Search for professional information, skills, and background about this person.';
304
+ }
305
+ else {
306
+ objective += 'Search for professional information and background about this person.';
307
+ }
308
+ logger.verbose("Constructed scraping objective", {
309
+ hasSocials: socialParts.length > 0,
310
+ hasLocation: !!user.location,
311
+ objectivePreview: objective.substring(0, 100)
312
+ });
313
+ const scrapedData = await this.scraper.scrape(objective);
314
+ logger.verbose("✅ Scrape complete", {
315
+ dataLength: scrapedData?.length || 0
316
+ });
317
+ return {
318
+ objective,
319
+ input: scrapedData,
320
+ operationsPerformed: { scraped: true }
321
+ };
322
+ }
323
+ catch (error) {
324
+ logger.error("Scrape failed", {
325
+ error: error instanceof Error ? error.message : String(error)
326
+ });
327
+ return {
328
+ error: "Web scrape failed"
329
+ };
330
+ }
331
+ });
332
+ };
333
+ // ─────────────────────────────────────────────────────────
334
+ // NODE: Auto-Generate (Parallel Chat API enrichment)
335
+ // Calls enrichUserProfile to get a structured profile directly.
336
+ // On success, sets prePopulatedProfile (skips LLM generation).
337
+ // On failure, falls back to basic user info for LLM generation.
338
+ // Used in 'generate' mode only.
339
+ // ─────────────────────────────────────────────────────────
340
+ const autoGenerateNode = async (state) => {
341
+ return timed("ProfileGraph.autoGenerate", async () => {
342
+ logger.verbose("Starting auto-generate via Chat API enrichment", {
343
+ userId: state.userId,
344
+ });
345
+ try {
346
+ const user = await this.database.getUser(state.userId);
347
+ if (!user) {
348
+ logger.error("User not found for auto-generate", { userId: state.userId });
349
+ return { error: `User not found: ${state.userId}` };
350
+ }
351
+ const request = {
352
+ name: user.name || undefined,
353
+ email: user.email || undefined,
354
+ linkedin: user.socials?.linkedin || undefined,
355
+ twitter: user.socials?.x || undefined,
356
+ github: user.socials?.github || undefined,
357
+ websites: user.socials?.websites?.length ? user.socials.websites : undefined,
358
+ };
359
+ const buildBasicInfo = () => {
360
+ const parts = [
361
+ user.name ? `Name: ${user.name}` : '',
362
+ user.email ? `Email: ${user.email}` : '',
363
+ user.location ? `Location: ${user.location}` : '',
364
+ user.intro ? `Bio: ${user.intro}` : '',
365
+ ].filter(Boolean).join('\n');
366
+ return parts || "No information available";
367
+ };
368
+ try {
369
+ const enrichment = this.enricher
370
+ ? await this.enricher.enrichUserProfile(request)
371
+ : null;
372
+ if (enrichment && !enrichment.isHuman) {
373
+ logger.info("Enrichment detected non-human entity, soft-deleting ghost", { userId: state.userId });
374
+ await this.database.softDeleteGhost(state.userId);
375
+ return { error: "Non-human entity detected" };
376
+ }
377
+ const hasMeaningfulEnrichment = !!enrichment &&
378
+ enrichment.confidentMatch &&
379
+ (enrichment.identity.bio.trim().length > 0 ||
380
+ enrichment.narrative.context.trim().length > 0 ||
381
+ enrichment.attributes.skills.length > 0 ||
382
+ enrichment.attributes.interests.length > 0);
383
+ if (hasMeaningfulEnrichment) {
384
+ logger.verbose("Chat API enrichment succeeded", {
385
+ userId: state.userId,
386
+ skillsCount: enrichment.attributes.skills.length,
387
+ interestsCount: enrichment.attributes.interests.length,
388
+ });
389
+ // Update user record with enriched data
390
+ const updatePayload = {};
391
+ const enrichedName = enrichment.identity.name?.trim();
392
+ if (enrichedName &&
393
+ shouldEnrichGhostDisplayNameFromParallel({ isGhost: !!user.isGhost, name: user.name ?? '', email: user.email ?? '' }, enrichedName)) {
394
+ updatePayload.name = enrichedName;
395
+ }
396
+ if (enrichment.identity.bio?.trim())
397
+ updatePayload.intro = enrichment.identity.bio.trim();
398
+ if (enrichment.identity.location?.trim())
399
+ updatePayload.location = enrichment.identity.location.trim();
400
+ const socials = {};
401
+ if (enrichment.socials.twitter)
402
+ socials.x = enrichment.socials.twitter;
403
+ if (enrichment.socials.linkedin)
404
+ socials.linkedin = enrichment.socials.linkedin;
405
+ if (enrichment.socials.github)
406
+ socials.github = enrichment.socials.github;
407
+ if (enrichment.socials.websites?.length)
408
+ socials.websites = enrichment.socials.websites;
409
+ if (Object.keys(socials).length > 0)
410
+ updatePayload.socials = socials;
411
+ if (Object.keys(updatePayload).length > 0) {
412
+ await this.database.updateUser(state.userId, updatePayload);
413
+ }
414
+ return {
415
+ prePopulatedProfile: {
416
+ identity: enrichment.identity,
417
+ narrative: enrichment.narrative,
418
+ attributes: enrichment.attributes,
419
+ },
420
+ needsUserInfo: false,
421
+ operationsPerformed: { scraped: true },
422
+ };
423
+ }
424
+ if (user.isGhost) {
425
+ logger.info("Low-confidence enrichment for ghost, soft-deleting", { userId: state.userId });
426
+ await this.database.softDeleteGhost(state.userId);
427
+ return { error: "Enrichment not confident for ghost user" };
428
+ }
429
+ logger.warn("Chat API returned low-signal enrichment, falling back to basic info", { userId: state.userId });
430
+ }
431
+ catch (enrichErr) {
432
+ if (user.isGhost) {
433
+ logger.info("Enrichment failed for ghost, soft-deleting", { userId: state.userId });
434
+ await this.database.softDeleteGhost(state.userId);
435
+ return { error: "Enrichment failed for ghost user" };
436
+ }
437
+ logger.warn("Chat API enrichment failed, falling back to basic info", {
438
+ userId: state.userId,
439
+ error: enrichErr instanceof Error ? enrichErr.message : String(enrichErr),
440
+ });
441
+ }
442
+ return {
443
+ input: buildBasicInfo(),
444
+ needsUserInfo: false,
445
+ needsProfileGeneration: true,
446
+ operationsPerformed: { scraped: true },
447
+ };
448
+ }
449
+ catch (err) {
450
+ logger.error("Auto-generate failed", {
451
+ error: err instanceof Error ? err.message : String(err),
452
+ });
453
+ return { error: `Auto-generate failed: ${err instanceof Error ? err.message : String(err)}` };
454
+ }
455
+ });
456
+ };
457
+ // ─────────────────────────────────────────────────────────
458
+ // NODE: Use Pre-Populated Profile
459
+ // Converts pre-populated profile (from external enrichment like Parallel Chat API)
460
+ // into the format expected by the embedding step, skipping LLM generation.
461
+ // ─────────────────────────────────────────────────────────
462
+ const usePrePopulatedProfileNode = async (state) => {
463
+ return timed("ProfileGraph.usePrePopulatedProfile", async () => {
464
+ if (!state.prePopulatedProfile) {
465
+ logger.error("No pre-populated profile provided");
466
+ return { error: "Pre-populated profile required" };
467
+ }
468
+ logger.verbose("Using pre-populated profile from external enrichment", {
469
+ name: state.prePopulatedProfile.identity.name,
470
+ skillsCount: state.prePopulatedProfile.attributes.skills.length,
471
+ interestsCount: state.prePopulatedProfile.attributes.interests.length,
472
+ });
473
+ return {
474
+ profile: {
475
+ ...state.prePopulatedProfile,
476
+ userId: state.userId,
477
+ embedding: [],
478
+ },
479
+ needsHydeGeneration: true,
480
+ operationsPerformed: { generatedProfile: true },
481
+ };
482
+ });
483
+ };
484
+ // ─────────────────────────────────────────────────────────
485
+ // NODE: Generate Profile
486
+ // Generates profile from input using ProfileGenerator agent.
487
+ // If updating existing profile, merges new information intelligently.
488
+ // ─────────────────────────────────────────────────────────
489
+ const generateProfileNode = async (state) => {
490
+ return timed("ProfileGraph.generateProfile", async () => {
491
+ if (!state.input) {
492
+ logger.error("No input provided for profile generation");
493
+ return {
494
+ error: "Input required for profile generation"
495
+ };
496
+ }
497
+ logger.verbose("Starting profile generation...", {
498
+ hasExistingProfile: !!state.profile,
499
+ isUpdate: state.forceUpdate,
500
+ inputLength: state.input.length
501
+ });
502
+ const agentTimingsAccum = [];
503
+ try {
504
+ // If updating existing profile, include it in the input for context
505
+ let inputWithContext = state.input;
506
+ if (state.profile && state.forceUpdate) {
507
+ inputWithContext = `EXISTING PROFILE:\n${JSON.stringify(state.profile, null, 2)}\n\nUSER REQUEST:\n${state.input}\n\nApply the user's request to the existing profile. Preserve existing data unless the user asks to change or remove it. You may add, update, or remove skills and interests as requested. Output the full updated profile.`;
508
+ logger.verbose("Merging with existing profile");
509
+ }
510
+ const _traceEmitterProfileGen = requestContext.getStore()?.traceEmitter;
511
+ const profileGeneratorStart = Date.now();
512
+ _traceEmitterProfileGen?.({ type: "agent_start", name: "profile-generator" });
513
+ const result = await profileGenerator.invoke(inputWithContext);
514
+ agentTimingsAccum.push({ name: 'profile.generator', durationMs: Date.now() - profileGeneratorStart });
515
+ _traceEmitterProfileGen?.({ type: "agent_end", name: "profile-generator", durationMs: Date.now() - profileGeneratorStart, summary: `Generated profile for ${result.output.identity.name || "user"}` });
516
+ logger.verbose("✅ Profile generated successfully", {
517
+ name: result.output.identity.name,
518
+ skillsCount: result.output.attributes.skills.length,
519
+ interestsCount: result.output.attributes.interests.length
520
+ });
521
+ return {
522
+ profile: {
523
+ ...result.output,
524
+ userId: state.userId,
525
+ embedding: []
526
+ },
527
+ // Mark that hyde needs regeneration since profile was updated
528
+ needsHydeGeneration: true,
529
+ agentTimings: agentTimingsAccum,
530
+ operationsPerformed: { generatedProfile: true }
531
+ };
532
+ }
533
+ catch (error) {
534
+ logger.error("Profile generation failed", {
535
+ error: error instanceof Error ? error.message : String(error)
536
+ });
537
+ return {
538
+ error: "Profile generation failed",
539
+ agentTimings: agentTimingsAccum
540
+ };
541
+ }
542
+ });
543
+ };
544
+ // ─────────────────────────────────────────────────────────
545
+ // NODE: Embed & Save Profile
546
+ // Generates embedding for profile and saves to DB
547
+ // ─────────────────────────────────────────────────────────
548
+ const embedSaveProfileNode = async (state) => {
549
+ return timed("ProfileGraph.embedSaveProfile", async () => {
550
+ if (!state.profile) {
551
+ logger.error("Profile missing in embed step");
552
+ return {
553
+ error: "Profile missing in embed step"
554
+ };
555
+ }
556
+ logger.verbose("Starting profile embedding...", {
557
+ userId: state.userId
558
+ });
559
+ try {
560
+ const profile = { ...state.profile };
561
+ const textToEmbed = [
562
+ '# Identity',
563
+ '## Name', profile.identity.name,
564
+ '## Bio', profile.identity.bio,
565
+ '## Location', profile.identity.location,
566
+ '# Narrative',
567
+ '## Context', profile.narrative.context,
568
+ '# Attributes',
569
+ '## Interests', profile.attributes.interests.join(', '),
570
+ '## Skills', profile.attributes.skills.join(', ')
571
+ ].join('\n');
572
+ logger.verbose("Generating embedding...", {
573
+ textLength: textToEmbed.length
574
+ });
575
+ const embedding = await this.embedder.generate(textToEmbed);
576
+ profile.embedding = embedding;
577
+ logger.verbose("Saving profile to DB...", {
578
+ userId: state.userId,
579
+ embeddingDimensions: Array.isArray(embedding[0]) ? embedding[0].length : embedding.length
580
+ });
581
+ await this.database.saveProfile(state.userId, profile);
582
+ logger.verbose("✅ Profile saved successfully");
583
+ return {
584
+ profile,
585
+ operationsPerformed: { embeddedProfile: true }
586
+ };
587
+ }
588
+ catch (error) {
589
+ logger.error("Failed to embed/save profile", {
590
+ error: error instanceof Error ? error.message : String(error)
591
+ });
592
+ return {
593
+ error: "Failed to embed/save profile"
594
+ };
595
+ }
596
+ });
597
+ };
598
+ // ─────────────────────────────────────────────────────────
599
+ // NODE: Generate HyDE
600
+ // Generates Hypothetical Document Embedding description for profile matching
601
+ // ─────────────────────────────────────────────────────────
602
+ const generateHydeNode = async (state) => {
603
+ return timed("ProfileGraph.generateHyde", async () => {
604
+ if (!state.profile) {
605
+ logger.error("Profile missing for HyDE generation");
606
+ return {
607
+ error: "Profile missing for HyDE generation"
608
+ };
609
+ }
610
+ logger.verbose("Starting HyDE generation...", {
611
+ userId: state.userId,
612
+ profileName: state.profile.identity.name
613
+ });
614
+ const agentTimingsAccum = [];
615
+ try {
616
+ const profileString = JSON.stringify(state.profile, null, 2);
617
+ const _traceEmitterHydeGen = requestContext.getStore()?.traceEmitter;
618
+ const hydeGeneratorStart = Date.now();
619
+ _traceEmitterHydeGen?.({ type: "agent_start", name: "hyde-generator" });
620
+ const result = await hydeGenerator.invoke(profileString);
621
+ agentTimingsAccum.push({ name: 'hyde.generator', durationMs: Date.now() - hydeGeneratorStart });
622
+ _traceEmitterHydeGen?.({ type: "agent_end", name: "hyde-generator", durationMs: Date.now() - hydeGeneratorStart, summary: `Generated HyDE for ${state.profile?.identity?.name || "profile"}` });
623
+ logger.verbose("✅ HyDE generated successfully", {
624
+ descriptionLength: result.textToEmbed.length
625
+ });
626
+ return {
627
+ hydeDescription: result.textToEmbed,
628
+ agentTimings: agentTimingsAccum,
629
+ operationsPerformed: { generatedHyde: true }
630
+ };
631
+ }
632
+ catch (error) {
633
+ logger.error("HyDE generation failed", {
634
+ error: error instanceof Error ? error.message : String(error)
635
+ });
636
+ return {
637
+ error: "HyDE generation failed"
638
+ };
639
+ }
640
+ });
641
+ };
642
+ // ─────────────────────────────────────────────────────────
643
+ // NODE: Embed & Save HyDE
644
+ // Generates embedding for HyDE description and saves to DB
645
+ // ─────────────────────────────────────────────────────────
646
+ const embedSaveHydeNode = async (state) => {
647
+ return timed("ProfileGraph.embedSaveHyde", async () => {
648
+ if (!state.hydeDescription) {
649
+ logger.error("HyDE description missing");
650
+ return {
651
+ error: "HyDE description missing"
652
+ };
653
+ }
654
+ logger.verbose("Starting HyDE embedding...", {
655
+ userId: state.userId,
656
+ descriptionLength: state.hydeDescription.length
657
+ });
658
+ try {
659
+ const hydeEmbedding = await this.embedder.generate(state.hydeDescription);
660
+ // Normalize embedding if needed (Adapters usually handle this, but to be sure)
661
+ const flatHydeEmbedding = Array.isArray(hydeEmbedding[0])
662
+ ? hydeEmbedding[0]
663
+ : hydeEmbedding;
664
+ logger.verbose("Saving HyDE to hyde_documents...", {
665
+ userId: state.userId,
666
+ embeddingDimensions: flatHydeEmbedding.length
667
+ });
668
+ await this.database.saveHydeDocument({
669
+ sourceType: 'profile',
670
+ sourceId: state.userId,
671
+ strategy: 'mirror',
672
+ targetCorpus: 'profiles',
673
+ hydeText: state.hydeDescription,
674
+ hydeEmbedding: flatHydeEmbedding,
675
+ });
676
+ return {
677
+ operationsPerformed: { embeddedHyde: true }
678
+ };
679
+ }
680
+ catch (error) {
681
+ logger.error("Failed to embed/save HyDE", {
682
+ error: error instanceof Error ? error.message : String(error)
683
+ });
684
+ return {
685
+ error: "Failed to embed/save HyDE"
686
+ };
687
+ }
688
+ });
689
+ };
690
+ // ─────────────────────────────────────────────────────────
691
+ // ROUTING CONDITIONS
692
+ // Smart conditional routing based on operation mode and missing components
693
+ // ─────────────────────────────────────────────────────────
694
+ /**
695
+ * Route from check_state to next step based on operation mode and detected needs.
696
+ */
697
+ const checkStateCondition = (state) => {
698
+ // Query mode: Return immediately (fast path)
699
+ if (state.operationMode === 'query') {
700
+ logger.verbose("Query mode - ending (fast path)");
701
+ return END;
702
+ }
703
+ // Pre-populated profile from external enrichment (e.g. Parallel Chat API)
704
+ // Skip profile generation, go directly to embedding
705
+ if (state.prePopulatedProfile) {
706
+ logger.verbose("Pre-populated profile detected - skipping generation, routing to embed");
707
+ return "use_prepopulated_profile";
708
+ }
709
+ // Generate mode: use enrichUserProfile Chat API to auto-generate
710
+ if (state.operationMode === 'generate') {
711
+ logger.verbose("Generate mode - routing to auto_generate");
712
+ return "auto_generate";
713
+ }
714
+ // Check if user information is insufficient for scraping
715
+ // Return early so chat graph can request the missing information
716
+ if (state.needsUserInfo) {
717
+ logger.verbose("⚠️ Insufficient user info - requesting from user", {
718
+ missingInfo: state.missingUserInfo
719
+ });
720
+ return END;
721
+ }
722
+ // Write mode: Check what needs generation
723
+ if (state.needsProfileGeneration) {
724
+ // Only use provided input if it's meaningful (not just "Yes" / confirmation)
725
+ if (state.input && isMeaningfulProfileInput(state.input)) {
726
+ logger.verbose("Profile generation needed with meaningful input provided");
727
+ return "generate_profile";
728
+ }
729
+ else {
730
+ logger.verbose("Profile generation needed - scraping first (no meaningful input)");
731
+ return "scrape";
732
+ }
733
+ }
734
+ // Profile exists but missing embedding
735
+ if (state.needsProfileEmbedding) {
736
+ logger.verbose("Profile embedding needed");
737
+ return "embed_save_profile";
738
+ }
739
+ // Profile and embedding exist, check hyde
740
+ if (state.needsHydeGeneration) {
741
+ logger.verbose("HyDE generation needed");
742
+ return "generate_hyde";
743
+ }
744
+ // Hyde exists but missing embedding
745
+ if (state.needsHydeEmbedding) {
746
+ logger.verbose("HyDE embedding needed");
747
+ return "embed_save_hyde";
748
+ }
749
+ // Everything exists and is up to date
750
+ logger.verbose("All components exist - ending");
751
+ return END;
752
+ };
753
+ /**
754
+ * Route after profile embedding to check if hyde needs generation.
755
+ */
756
+ const afterProfileEmbeddingCondition = (state) => {
757
+ // If profile was just generated/updated, regenerate hyde
758
+ if (state.needsHydeGeneration || state.forceUpdate) {
759
+ logger.verbose("Profile updated - regenerating HyDE");
760
+ return "generate_hyde";
761
+ }
762
+ // Check if hyde embedding is missing
763
+ if (state.needsHydeEmbedding) {
764
+ logger.verbose("HyDE embedding needed");
765
+ return "embed_save_hyde";
766
+ }
767
+ logger.verbose("Profile complete - ending");
768
+ return END;
769
+ };
770
+ /**
771
+ * Route after hyde generation to embedding step.
772
+ * Always embed after generating hyde.
773
+ */
774
+ const afterHydeGenerationCondition = (state) => {
775
+ logger.verbose("HyDE generated - proceeding to embedding");
776
+ return "embed_save_hyde";
777
+ };
778
+ // ─────────────────────────────────────────────────────────
779
+ // GRAPH ASSEMBLY
780
+ // Conditional flow based on operation mode and detected needs
781
+ // ─────────────────────────────────────────────────────────
782
+ const workflow = new StateGraph(ProfileGraphState)
783
+ // Add all nodes
784
+ .addNode("check_state", checkStateNode)
785
+ .addNode("scrape", scrapeNode)
786
+ .addNode("auto_generate", autoGenerateNode)
787
+ .addNode("use_prepopulated_profile", usePrePopulatedProfileNode)
788
+ .addNode("generate_profile", generateProfileNode)
789
+ .addNode("embed_save_profile", embedSaveProfileNode)
790
+ .addNode("generate_hyde", generateHydeNode)
791
+ .addNode("embed_save_hyde", embedSaveHydeNode)
792
+ // Start with state check
793
+ .addEdge(START, "check_state")
794
+ // Conditional routing from check_state
795
+ .addConditionalEdges("check_state", checkStateCondition, {
796
+ use_prepopulated_profile: "use_prepopulated_profile", // Pre-populated profile -> skip generation
797
+ auto_generate: "auto_generate", // Generate mode -> Chat API enrichment
798
+ scrape: "scrape", // Need profile, no input -> scrape first
799
+ generate_profile: "generate_profile", // Need profile, have input -> generate
800
+ embed_save_profile: "embed_save_profile", // Have profile, need embedding
801
+ generate_hyde: "generate_hyde", // Have profile+embedding, need hyde
802
+ embed_save_hyde: "embed_save_hyde", // Have hyde, need embedding
803
+ [END]: END // Query mode or everything exists
804
+ })
805
+ // Pre-populated profile feeds into embedding (skips generation)
806
+ .addEdge("use_prepopulated_profile", "embed_save_profile")
807
+ // Auto-generate routes based on enrichment result
808
+ .addConditionalEdges("auto_generate", (state) => {
809
+ if (state.prePopulatedProfile) {
810
+ logger.verbose("Enrichment succeeded — using pre-populated profile");
811
+ return "use_prepopulated_profile";
812
+ }
813
+ logger.verbose("Enrichment failed — falling back to LLM profile generation");
814
+ return "generate_profile";
815
+ }, {
816
+ use_prepopulated_profile: "use_prepopulated_profile",
817
+ generate_profile: "generate_profile",
818
+ })
819
+ // Scrape -> Generate profile (linear)
820
+ .addEdge("scrape", "generate_profile")
821
+ // Generate profile -> Embed profile (linear)
822
+ .addEdge("generate_profile", "embed_save_profile")
823
+ // After profile embedding, check if hyde needs generation
824
+ .addConditionalEdges("embed_save_profile", afterProfileEmbeddingCondition, {
825
+ generate_hyde: "generate_hyde", // Profile updated -> regenerate hyde
826
+ embed_save_hyde: "embed_save_hyde", // Only hyde embedding missing
827
+ [END]: END // Everything complete
828
+ })
829
+ // After hyde generation, always embed it
830
+ .addConditionalEdges("generate_hyde", afterHydeGenerationCondition, {
831
+ embed_save_hyde: "embed_save_hyde"
832
+ })
833
+ // Hyde embedding -> END (linear)
834
+ .addEdge("embed_save_hyde", END);
835
+ logger.verbose("Graph built successfully");
836
+ return workflow.compile();
837
+ }
838
+ }
839
+ //# sourceMappingURL=profile.graph.js.map