@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,787 @@
1
+ import { StateGraph, START, END } from "@langchain/langgraph";
2
+ import { IntentGraphState } from "../states/intent.state.js";
3
+ import { ExplicitIntentInferrer } from "../agents/intent.inferrer.js";
4
+ import { SemanticVerifier } from "../agents/intent.verifier.js";
5
+ import { IntentReconciler } from "../agents/intent.reconciler.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("IntentGraphFactory");
10
+ const MAX_PERMISSIBLE_ENTROPY = 0.75;
11
+ const MIN_CLEAR_INTENT_SCORE = 40;
12
+ const GENERIC_JOB_PHRASE = /\b(?:a|any|some)\s+job\b/i;
13
+ const parseProfile = (profile) => {
14
+ if (!profile || !profile.trim())
15
+ return null;
16
+ try {
17
+ const parsed = JSON.parse(profile);
18
+ return parsed && typeof parsed === "object" ? parsed : null;
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ };
24
+ const inferRoleFromProfileText = (text) => {
25
+ const normalized = text.toLowerCase();
26
+ if (/\b(engineer|developer)\b/.test(normalized))
27
+ return "software engineering";
28
+ if (/\b(designer|ux|ui)\b/.test(normalized))
29
+ return "product design";
30
+ if (/\b(marketing|marketer|growth)\b/.test(normalized))
31
+ return "marketing";
32
+ if (/\b(product manager|product)\b/.test(normalized))
33
+ return "product management";
34
+ if (/\b(data scientist|machine learning|ml|ai)\b/.test(normalized))
35
+ return "AI/ML";
36
+ if (/\b(sales|account executive|business development)\b/.test(normalized))
37
+ return "sales";
38
+ return null;
39
+ };
40
+ const buildJobQualifierFromProfile = (profile) => {
41
+ if (!profile)
42
+ return null;
43
+ const skills = (profile.attributes?.skills ?? [])
44
+ .filter((skill) => typeof skill === "string" && skill.trim().length > 0)
45
+ .slice(0, 2);
46
+ const interests = (profile.attributes?.interests ?? [])
47
+ .filter((interest) => typeof interest === "string" && interest.trim().length > 0)
48
+ .slice(0, 1);
49
+ const profileText = [
50
+ profile.identity?.bio,
51
+ profile.narrative?.context,
52
+ ]
53
+ .filter((item) => typeof item === "string" && item.trim().length > 0)
54
+ .join(" ");
55
+ const roleHint = inferRoleFromProfileText(profileText);
56
+ if (skills.length > 0 && roleHint) {
57
+ return `${skills.join(" / ")} ${roleHint} role`;
58
+ }
59
+ if (skills.length > 0) {
60
+ return `${skills.join(" / ")} role`;
61
+ }
62
+ if (interests.length > 0 && roleHint) {
63
+ return `${interests[0]} ${roleHint} role`;
64
+ }
65
+ if (roleHint) {
66
+ return `${roleHint} role`;
67
+ }
68
+ return null;
69
+ };
70
+ const enrichVagueIntentWithProfile = (description, profileContext) => {
71
+ const trimmed = description?.trim();
72
+ if (!trimmed)
73
+ return description;
74
+ const isGenericJobRequest = GENERIC_JOB_PHRASE.test(trimmed) ||
75
+ /\b(?:find|get|look(?:ing)?\s+for|want)\s+(?:to\s+)?(?:find\s+)?job\b/i.test(trimmed);
76
+ if (!isGenericJobRequest)
77
+ return description;
78
+ const profile = parseProfile(profileContext);
79
+ const qualifier = buildJobQualifierFromProfile(profile);
80
+ if (!qualifier)
81
+ return description;
82
+ const enriched = trimmed
83
+ .replace(/\ba job\b/i, `a ${qualifier}`)
84
+ .replace(/\bjob\b/i, `${qualifier}`)
85
+ .replace(/\s{2,}/g, " ")
86
+ .trim();
87
+ return enriched.length > 0 ? enriched : description;
88
+ };
89
+ const isVague = (description, entropy, clarity) => {
90
+ if (GENERIC_JOB_PHRASE.test(description))
91
+ return true;
92
+ if (entropy > MAX_PERMISSIBLE_ENTROPY)
93
+ return true;
94
+ if (clarity < MIN_CLEAR_INTENT_SCORE)
95
+ return true;
96
+ return false;
97
+ };
98
+ const toSpeechActType = (classification) => {
99
+ if (classification === "COMMISSIVE" || classification === "DIRECTIVE")
100
+ return classification;
101
+ return null;
102
+ };
103
+ /**
104
+ * Factory class to build and compile the Intent Processing Graph.
105
+ */
106
+ export class IntentGraphFactory {
107
+ constructor(database, embedder, intentQueue) {
108
+ this.database = database;
109
+ this.embedder = embedder;
110
+ this.intentQueue = intentQueue;
111
+ }
112
+ createGraph() {
113
+ // Instantiate Agents (Nodes)
114
+ const inferrer = new ExplicitIntentInferrer();
115
+ const verifier = new SemanticVerifier();
116
+ const reconciler = new IntentReconciler();
117
+ // --- NODE DEFINITIONS ---
118
+ /**
119
+ * Node 0: Prep
120
+ * Always fetches ALL of the user's active intents from the DB via getActiveIntents(userId).
121
+ * This ensures reconciliation can detect duplicates and modifications globally,
122
+ * regardless of index scope.
123
+ */
124
+ const prepNode = async (state) => {
125
+ return timed("IntentGraph.prep", async () => {
126
+ logger.verbose("Starting preparation phase", {
127
+ operationMode: state.operationMode,
128
+ hasContent: !!state.inputContent,
129
+ targetIntentIds: state.targetIntentIds,
130
+ indexId: state.indexId,
131
+ });
132
+ // Gate: write operations require an existing profile
133
+ if (state.operationMode !== 'read') {
134
+ const profile = await this.database.getProfile(state.userId);
135
+ if (!profile) {
136
+ const msg = "You need to create a profile before creating intents. Please set up your profile first.";
137
+ logger.error("Prep failed: no profile for user", { userId: state.userId });
138
+ return { error: msg };
139
+ }
140
+ }
141
+ const activeIntents = await this.database.getActiveIntents(state.userId);
142
+ const formattedActiveIntents = activeIntents
143
+ .map(i => `ID: ${i.id}, Description: ${i.payload}, Summary: ${i.summary || 'N/A'}`)
144
+ .join('\n') || "No active intents.";
145
+ logger.verbose("Fetched active intents", {
146
+ count: activeIntents.length,
147
+ operationMode: state.operationMode
148
+ });
149
+ return {
150
+ activeIntents: formattedActiveIntents,
151
+ trace: [{
152
+ node: "prep",
153
+ detail: `Fetched ${activeIntents.length} active intent(s)`,
154
+ }],
155
+ };
156
+ });
157
+ };
158
+ /**
159
+ * Node 1: Inference
160
+ * Extracts intents from raw content.
161
+ * Phase 4: Uses operation mode to control behavior and determine if node should execute.
162
+ * Phase 5: Passes conversation context for anaphoric resolution.
163
+ */
164
+ const inferenceNode = async (state) => {
165
+ return timed("IntentGraph.inference", async () => {
166
+ logger.verbose("Starting inference", {
167
+ operationMode: state.operationMode,
168
+ hasContent: !!state.inputContent,
169
+ contentPreview: state.inputContent?.substring(0, 50),
170
+ hasConversationContext: !!state.conversationContext,
171
+ conversationMessagesCount: state.conversationContext?.length || 0
172
+ });
173
+ const agentTimingsAccum = [];
174
+ // Phase 4: Control profile fallback based on operation mode
175
+ // Only allow for create operations without explicit content
176
+ const allowProfileFallback = state.operationMode === 'create' && !state.inputContent;
177
+ // Cast operationMode: 'read' and 'propose' map to 'create' for the inferrer
178
+ // (inference node is never called in read mode; propose behaves like create for inference)
179
+ const inferrerMode = (state.operationMode === 'read' || state.operationMode === 'propose') ? 'create' : state.operationMode;
180
+ const _traceEmitterInferrer = requestContext.getStore()?.traceEmitter;
181
+ const inferrerStart = Date.now();
182
+ _traceEmitterInferrer?.({ type: "agent_start", name: "intent-inferrer" });
183
+ const result = await inferrer.invoke(state.inputContent || null, state.userProfile, {
184
+ allowProfileFallback,
185
+ operationMode: inferrerMode,
186
+ conversationContext: state.conversationContext // Phase 5: Pass conversation history
187
+ });
188
+ agentTimingsAccum.push({ name: 'intent.inferrer', durationMs: Date.now() - inferrerStart });
189
+ _traceEmitterInferrer?.({ type: "agent_end", name: "intent-inferrer", durationMs: Date.now() - inferrerStart, summary: result.intents.length > 0 ? `Extracted ${result.intents.length} intent(s)` : "intent-inferrer completed" });
190
+ logger.verbose("Inference complete", {
191
+ inferredCount: result.intents.length,
192
+ operationMode: state.operationMode
193
+ });
194
+ const descriptions = result.intents.map(i => i.description).slice(0, 3);
195
+ const truncated = result.intents.length > 3 ? `... +${result.intents.length - 3} more` : "";
196
+ return {
197
+ inferredIntents: result.intents,
198
+ agentTimings: agentTimingsAccum,
199
+ trace: [{
200
+ node: "inference",
201
+ detail: result.intents.length === 0
202
+ ? "No intents extracted"
203
+ : `Extracted ${result.intents.length}: ${descriptions.map(d => `"${d.slice(0, 50)}${d.length > 50 ? '...' : ''}"`).join(", ")}${truncated}`,
204
+ }],
205
+ };
206
+ });
207
+ };
208
+ /**
209
+ * Node 2: Verification (Map-Reduce / Parallel)
210
+ * Verifies each inferred intent in parallel.
211
+ * Phase 4: Can be skipped for delete operations and updates with no new intents.
212
+ */
213
+ const verificationNode = async (state) => {
214
+ return timed("IntentGraph.verification", async () => {
215
+ const intents = state.inferredIntents;
216
+ logger.verbose("Starting verification", {
217
+ operationMode: state.operationMode,
218
+ intentCount: intents.length
219
+ });
220
+ if (intents.length === 0) {
221
+ logger.verbose("No intents to verify");
222
+ return { verifiedIntents: [], agentTimings: [] };
223
+ }
224
+ logger.verbose(`Verifying ${intents.length} intents in parallel...`);
225
+ const agentTimingsAccum = [];
226
+ // Parallel Execution
227
+ const verificationResults = await Promise.all(intents.map(async (intent) => {
228
+ try {
229
+ let description = intent.description;
230
+ const _traceEmitterVerifier = requestContext.getStore()?.traceEmitter;
231
+ const verifierStart1 = Date.now();
232
+ _traceEmitterVerifier?.({ type: "agent_start", name: "intent-verifier" });
233
+ let verdict = await verifier.invoke(description, state.userProfile);
234
+ agentTimingsAccum.push({ name: 'intent.verifier', durationMs: Date.now() - verifierStart1 });
235
+ _traceEmitterVerifier?.({ type: "agent_end", name: "intent-verifier", durationMs: Date.now() - verifierStart1, summary: `Verified: ${verdict.classification}` });
236
+ if (isVague(description, verdict.semantic_entropy, verdict.felicity_scores.clarity)) {
237
+ const enrichedDescription = enrichVagueIntentWithProfile(description, state.userProfile);
238
+ if (enrichedDescription !== description) {
239
+ logger.verbose("Enriched vague intent using profile context", {
240
+ before: description,
241
+ after: enrichedDescription,
242
+ });
243
+ const _traceEmitterVerifier2 = requestContext.getStore()?.traceEmitter;
244
+ const verifierStart2 = Date.now();
245
+ _traceEmitterVerifier2?.({ type: "agent_start", name: "intent-verifier" });
246
+ const enrichedVerdict = await verifier.invoke(enrichedDescription, state.userProfile);
247
+ agentTimingsAccum.push({ name: 'intent.verifier', durationMs: Date.now() - verifierStart2 });
248
+ _traceEmitterVerifier2?.({ type: "agent_end", name: "intent-verifier", durationMs: Date.now() - verifierStart2, summary: `Verified (enriched): ${enrichedVerdict.classification}` });
249
+ const becameClear = enrichedVerdict.semantic_entropy < verdict.semantic_entropy ||
250
+ enrichedVerdict.felicity_scores.clarity > verdict.felicity_scores.clarity;
251
+ if (becameClear) {
252
+ description = enrichedDescription;
253
+ verdict = enrichedVerdict;
254
+ }
255
+ }
256
+ }
257
+ // Filter Logic: Must be a Commissive, Directive, or Declaration
258
+ const VALID_TYPES = ['COMMISSIVE', 'DIRECTIVE', 'DECLARATION'];
259
+ if (!VALID_TYPES.includes(verdict.classification)) {
260
+ logger.warn(`Dropping intent: "${description}" (Type: ${verdict.classification})`);
261
+ return null;
262
+ }
263
+ if (isVague(description, verdict.semantic_entropy, verdict.felicity_scores.clarity)) {
264
+ logger.warn(`Dropping vague intent after verification: "${description}"`, {
265
+ entropy: verdict.semantic_entropy,
266
+ clarity: verdict.felicity_scores.clarity,
267
+ });
268
+ return null;
269
+ }
270
+ // Calculate Score
271
+ const score = Math.min(verdict.felicity_scores.authority, verdict.felicity_scores.sincerity, verdict.felicity_scores.clarity);
272
+ // Return enriched intent
273
+ return {
274
+ ...intent,
275
+ description,
276
+ verification: verdict,
277
+ score
278
+ };
279
+ }
280
+ catch (e) {
281
+ logger.error(`Error verifying intent: ${intent.description}`, { error: e });
282
+ return null;
283
+ }
284
+ }));
285
+ // Filter out nulls
286
+ const verified = verificationResults.filter((i) => i !== null);
287
+ logger.verbose(`Verification complete`, {
288
+ passed: verified.length,
289
+ total: intents.length,
290
+ operationMode: state.operationMode
291
+ });
292
+ // Build trace entries with Felicity scores for each verified intent
293
+ const traceEntries = verified.map(v => {
294
+ const fs = v.verification?.felicity_scores;
295
+ const entropy = v.verification?.semantic_entropy;
296
+ const classification = v.verification?.classification;
297
+ return {
298
+ node: "verification",
299
+ detail: `"${v.description.slice(0, 40)}${v.description.length > 40 ? '...' : ''}" → ${classification}`,
300
+ data: fs ? {
301
+ clarity: fs.clarity,
302
+ authority: fs.authority,
303
+ sincerity: fs.sincerity,
304
+ entropy: entropy != null ? Math.round(entropy * 100) / 100 : undefined,
305
+ classification,
306
+ score: v.score,
307
+ } : undefined,
308
+ };
309
+ });
310
+ // Add summary trace if some intents were filtered out
311
+ const dropped = intents.length - verified.length;
312
+ if (dropped > 0) {
313
+ traceEntries.unshift({
314
+ node: "verification",
315
+ detail: `Verified ${verified.length}/${intents.length} (${dropped} filtered as invalid)`,
316
+ data: undefined,
317
+ });
318
+ }
319
+ else if (verified.length > 0) {
320
+ traceEntries.unshift({
321
+ node: "verification",
322
+ detail: `Verified ${verified.length} intent(s)`,
323
+ data: undefined,
324
+ });
325
+ }
326
+ return { verifiedIntents: verified, agentTimings: agentTimingsAccum, trace: traceEntries };
327
+ });
328
+ };
329
+ /**
330
+ * Node 3: Reconciliation
331
+ * Decides on final actions (Create, Update, Expire).
332
+ * Phase 4: Handles delete operations directly without LLM reconciliation.
333
+ */
334
+ const reconciliationNode = async (state) => {
335
+ return timed("IntentGraph.reconciliation", async () => {
336
+ logger.verbose("Starting reconciliation", {
337
+ operationMode: state.operationMode,
338
+ verifiedIntentCount: state.verifiedIntents.length,
339
+ targetIntentIds: state.targetIntentIds
340
+ });
341
+ const agentTimingsAccum = [];
342
+ // Phase 4: Handle delete operations directly
343
+ if (state.operationMode === 'delete') {
344
+ if (!state.targetIntentIds || state.targetIntentIds.length === 0) {
345
+ logger.warn("Delete mode with no target IDs");
346
+ return {
347
+ actions: [],
348
+ agentTimings: agentTimingsAccum,
349
+ trace: [{ node: "reconciler", detail: "Delete mode with no target IDs" }],
350
+ };
351
+ }
352
+ logger.verbose("Delete mode - generating expire actions", {
353
+ targetIds: state.targetIntentIds
354
+ });
355
+ const actions = state.targetIntentIds.map(id => ({
356
+ type: 'expire',
357
+ id,
358
+ reasoning: 'User requested deletion'
359
+ }));
360
+ return {
361
+ actions,
362
+ agentTimings: agentTimingsAccum,
363
+ trace: [{
364
+ node: "reconciler",
365
+ detail: `Actions: expire=${actions.length}`,
366
+ }],
367
+ };
368
+ }
369
+ // Standard reconciliation for create/update operations
370
+ const candidates = state.verifiedIntents;
371
+ if (candidates.length === 0) {
372
+ logger.verbose("No verified intents to reconcile");
373
+ return {
374
+ actions: [],
375
+ agentTimings: agentTimingsAccum,
376
+ trace: [{ node: "reconciler", detail: "No intents to reconcile" }],
377
+ };
378
+ }
379
+ // Format candidates for the Reconciler Prompt
380
+ const formattedCandidates = candidates.map(c => `- [${c.type.toUpperCase()}] "${c.description}" (Confidence: ${c.confidence}, Score: ${c.score})\n` +
381
+ ` Reasoning: ${c.reasoning}\n` +
382
+ ` Verification: ${c.verification?.classification} (Flags: ${c.verification?.flags.join(', ') || 'None'})`).join('\n');
383
+ logger.verbose("Invoking reconciler agent", {
384
+ candidateCount: candidates.length,
385
+ operationMode: state.operationMode
386
+ });
387
+ const _traceEmitterReconciler = requestContext.getStore()?.traceEmitter;
388
+ const reconcilerStart = Date.now();
389
+ _traceEmitterReconciler?.({ type: "agent_start", name: "intent-reconciler" });
390
+ const result = await reconciler.invoke(formattedCandidates, state.activeIntents);
391
+ agentTimingsAccum.push({ name: 'intent.reconciler', durationMs: Date.now() - reconcilerStart });
392
+ _traceEmitterReconciler?.({ type: "agent_end", name: "intent-reconciler", durationMs: Date.now() - reconcilerStart, summary: `Reconciled ${result.actions.length} action(s)` });
393
+ logger.verbose("Reconciliation complete", {
394
+ actionCount: result.actions.length,
395
+ operationMode: state.operationMode
396
+ });
397
+ // Count actions by type
398
+ const counts = { create: 0, update: 0, expire: 0 };
399
+ for (const a of result.actions) {
400
+ if (a.type in counts)
401
+ counts[a.type]++;
402
+ }
403
+ return {
404
+ actions: result.actions,
405
+ agentTimings: agentTimingsAccum,
406
+ trace: [{
407
+ node: "reconciler",
408
+ detail: `Actions: create=${counts.create}, update=${counts.update}, expire=${counts.expire}`,
409
+ }],
410
+ };
411
+ });
412
+ };
413
+ /** Strip URLs and "More details at [url]" from intent payloads before persisting. */
414
+ const sanitizePayload = (payload) => {
415
+ if (!payload || typeof payload !== "string")
416
+ return payload;
417
+ let out = payload
418
+ .replace(/\s*More details at\s*:?\s*https?:\/\/[^\s"'<>)\]]+/gi, "")
419
+ .replace(/\s*See\s+https?:\/\/[^\s"'<>)\]]+\s+for\s+more[^.]*\.?/gi, "")
420
+ .replace(/https?:\/\/[^\s"'<>)\]]+/g, "")
421
+ .replace(/\s{2,}/g, " ")
422
+ .trim();
423
+ return out.replace(/[.,;]\s*$/, "").trim() || payload;
424
+ };
425
+ /**
426
+ * Node 4: Executor
427
+ * Executes reconciler actions against the database.
428
+ */
429
+ const executorNode = async (state) => {
430
+ return timed("IntentGraph.executor", async () => {
431
+ const actions = state.actions;
432
+ if (!actions || actions.length === 0) {
433
+ return { executionResults: [] };
434
+ }
435
+ logger.verbose(`Executing ${actions.length} actions...`);
436
+ const results = [];
437
+ const verifiedIntentByPayload = new Map();
438
+ for (const verifiedIntent of state.verifiedIntents) {
439
+ verifiedIntentByPayload.set(verifiedIntent.description, verifiedIntent);
440
+ verifiedIntentByPayload.set(sanitizePayload(verifiedIntent.description), verifiedIntent);
441
+ }
442
+ for (const action of actions) {
443
+ const actionType = action.type.toLowerCase();
444
+ try {
445
+ if (actionType === 'create') {
446
+ const createAction = action;
447
+ const sanitizedPayload = sanitizePayload(createAction.payload);
448
+ const matchedVerifiedIntent = verifiedIntentByPayload.get(createAction.payload) ||
449
+ verifiedIntentByPayload.get(sanitizedPayload);
450
+ // Generate embedding for the intent payload
451
+ let flatEmbedding;
452
+ if (this.embedder) {
453
+ try {
454
+ const embedding = await this.embedder.generate(sanitizedPayload);
455
+ flatEmbedding = Array.isArray(embedding?.[0])
456
+ ? embedding[0]
457
+ : embedding;
458
+ logger.verbose("Generated embedding for new intent", { dimensions: flatEmbedding?.length });
459
+ }
460
+ catch (embErr) {
461
+ logger.error("Failed to generate embedding for intent (continuing without)", { error: embErr });
462
+ }
463
+ }
464
+ const created = await this.database.createIntent({
465
+ userId: state.userId,
466
+ payload: sanitizedPayload,
467
+ confidence: createAction.score ? createAction.score / 100 : 1.0,
468
+ inferenceType: 'explicit',
469
+ sourceType: 'discovery_form',
470
+ embedding: flatEmbedding,
471
+ semanticEntropy: createAction.semanticEntropy ??
472
+ matchedVerifiedIntent?.verification?.semantic_entropy ??
473
+ null,
474
+ referentialAnchor: createAction.referentialAnchor ??
475
+ matchedVerifiedIntent?.verification?.referential_anchor ??
476
+ null,
477
+ felicityAuthority: matchedVerifiedIntent?.verification?.felicity_scores.authority ?? null,
478
+ felicitySincerity: matchedVerifiedIntent?.verification?.felicity_scores.sincerity ?? null,
479
+ intentMode: createAction.intentMode ?? null,
480
+ speechActType: toSpeechActType(matchedVerifiedIntent?.verification?.classification),
481
+ });
482
+ results.push({ actionType: 'create', success: true, intentId: created.id, payload: sanitizedPayload });
483
+ logger.verbose(`Created intent: ${created.id}`);
484
+ this.intentQueue?.addGenerateHydeJob({ intentId: created.id, userId: state.userId }).catch((err) => logger.error('Failed to enqueue intent HyDE job', { intentId: created.id, error: err }));
485
+ }
486
+ else if (actionType === 'update') {
487
+ const updateAction = action;
488
+ const sanitizedPayload = sanitizePayload(updateAction.payload);
489
+ const matchedVerifiedIntent = verifiedIntentByPayload.get(updateAction.payload) ||
490
+ verifiedIntentByPayload.get(sanitizedPayload);
491
+ // Regenerate embedding for the updated payload
492
+ let flatEmbedding;
493
+ if (this.embedder) {
494
+ try {
495
+ const embedding = await this.embedder.generate(sanitizedPayload);
496
+ flatEmbedding = Array.isArray(embedding?.[0])
497
+ ? embedding[0]
498
+ : embedding;
499
+ logger.verbose("Generated embedding for updated intent", { intentId: updateAction.id, dimensions: flatEmbedding?.length });
500
+ }
501
+ catch (embErr) {
502
+ logger.error("Failed to generate embedding for intent update (continuing without)", { error: embErr });
503
+ }
504
+ }
505
+ const updated = await this.database.updateIntent(updateAction.id, {
506
+ payload: sanitizedPayload,
507
+ embedding: flatEmbedding,
508
+ semanticEntropy: matchedVerifiedIntent?.verification?.semantic_entropy ??
509
+ null,
510
+ referentialAnchor: matchedVerifiedIntent?.verification?.referential_anchor ??
511
+ null,
512
+ felicityAuthority: matchedVerifiedIntent?.verification?.felicity_scores.authority ?? null,
513
+ felicitySincerity: matchedVerifiedIntent?.verification?.felicity_scores.sincerity ?? null,
514
+ intentMode: updateAction.intentMode ?? null,
515
+ speechActType: toSpeechActType(matchedVerifiedIntent?.verification?.classification),
516
+ });
517
+ results.push({
518
+ actionType: 'update',
519
+ success: !!updated,
520
+ intentId: updateAction.id,
521
+ payload: sanitizedPayload,
522
+ error: updated ? undefined : 'Intent not found'
523
+ });
524
+ logger.verbose(`Updated intent: ${updateAction.id}`);
525
+ if (updated) {
526
+ this.intentQueue?.addGenerateHydeJob({ intentId: updateAction.id, userId: state.userId }).catch((err) => logger.error('Failed to enqueue intent HyDE job', { intentId: updateAction.id, error: err }));
527
+ }
528
+ }
529
+ else if (actionType === 'expire') {
530
+ const expireAction = action;
531
+ const result = await this.database.archiveIntent(expireAction.id);
532
+ results.push({
533
+ actionType: 'expire',
534
+ success: result.success,
535
+ intentId: expireAction.id,
536
+ error: result.error
537
+ });
538
+ logger.verbose(`Archived intent: ${expireAction.id}`);
539
+ if (result.success) {
540
+ this.intentQueue?.addDeleteHydeJob({ intentId: expireAction.id }).catch((err) => logger.error('Failed to enqueue intent HyDE delete job', { intentId: expireAction.id, error: err }));
541
+ }
542
+ }
543
+ }
544
+ catch (error) {
545
+ logger.error(`Failed to execute ${action.type}:`, { error });
546
+ results.push({
547
+ actionType,
548
+ success: false,
549
+ intentId: 'id' in action ? action.id : undefined,
550
+ error: error instanceof Error ? error.message : 'Unknown error'
551
+ });
552
+ }
553
+ }
554
+ return { executionResults: results };
555
+ });
556
+ };
557
+ // --- QUERY NODE (Read Mode) ---
558
+ /**
559
+ * Node: Query
560
+ * Fast-path read node — fetches intents from DB based on scope.
561
+ * Handles: global user intents, index-scoped (all or filtered by user).
562
+ * No LLM calls; no inference/verification/reconciliation.
563
+ */
564
+ const queryNode = async (state) => {
565
+ return timed("IntentGraph.query", async () => {
566
+ logger.verbose("Starting query (read mode)", {
567
+ userId: state.userId,
568
+ indexId: state.indexId,
569
+ queryUserId: state.queryUserId,
570
+ allUserIntents: state.allUserIntents,
571
+ });
572
+ try {
573
+ // When allUserIntents is true, ignore index scope and return all
574
+ const effectiveIndexId = state.allUserIntents ? undefined : state.indexId;
575
+ if (effectiveIndexId) {
576
+ // Verify membership
577
+ const isMember = await this.database.isIndexMember(effectiveIndexId, state.userId);
578
+ if (!isMember) {
579
+ return {
580
+ readResult: {
581
+ count: 0,
582
+ intents: [],
583
+ message: "Index not found or you are not a member.",
584
+ },
585
+ };
586
+ }
587
+ // Index-scoped read
588
+ if (!state.queryUserId) {
589
+ // All intents in the index (any member can see)
590
+ const intents = await this.database.getIndexIntentsForMember(effectiveIndexId, state.userId, { limit: 50, offset: 0 });
591
+ if (intents.length === 0) {
592
+ return {
593
+ readResult: {
594
+ count: 0,
595
+ intents: [],
596
+ message: "No intents in this index yet.",
597
+ indexId: effectiveIndexId,
598
+ },
599
+ };
600
+ }
601
+ return {
602
+ readResult: {
603
+ count: intents.length,
604
+ indexId: effectiveIndexId,
605
+ intents: intents.map((i) => ({
606
+ id: i.id,
607
+ description: i.payload,
608
+ summary: i.summary,
609
+ createdAt: i.createdAt,
610
+ userId: i.userId,
611
+ userName: i.userName,
612
+ })),
613
+ },
614
+ };
615
+ }
616
+ // Specific user's intents in the index
617
+ const effectiveUserId = state.queryUserId;
618
+ const intents = await this.database.getIntentsInIndexForMember(effectiveUserId, effectiveIndexId);
619
+ if (intents.length === 0) {
620
+ return {
621
+ readResult: {
622
+ count: 0,
623
+ intents: [],
624
+ message: effectiveUserId === state.userId
625
+ ? "You don't have any intents in this index yet."
626
+ : "No intents for that user in this index.",
627
+ indexId: effectiveIndexId,
628
+ },
629
+ };
630
+ }
631
+ const user = await this.database.getUser(effectiveUserId);
632
+ const userName = user?.name ?? null;
633
+ return {
634
+ readResult: {
635
+ count: intents.length,
636
+ indexId: effectiveIndexId,
637
+ intents: intents.map((i) => ({
638
+ id: i.id,
639
+ description: i.payload,
640
+ summary: i.summary,
641
+ createdAt: i.createdAt,
642
+ userId: effectiveUserId,
643
+ userName,
644
+ })),
645
+ },
646
+ };
647
+ }
648
+ // Global (no index scope): return user's own active intents
649
+ const intents = await this.database.getActiveIntents(state.userId);
650
+ if (intents.length === 0) {
651
+ return {
652
+ readResult: {
653
+ count: 0,
654
+ intents: [],
655
+ message: "You don't have any active intents yet. Share what you're looking for.",
656
+ },
657
+ };
658
+ }
659
+ return {
660
+ readResult: {
661
+ count: intents.length,
662
+ intents: intents.map((i) => ({
663
+ id: i.id,
664
+ description: i.payload,
665
+ summary: i.summary,
666
+ createdAt: i.createdAt,
667
+ })),
668
+ },
669
+ };
670
+ }
671
+ catch (err) {
672
+ logger.error("Query node failed", { error: err });
673
+ return {
674
+ readResult: {
675
+ count: 0,
676
+ intents: [],
677
+ message: "Failed to fetch intents. Please try again.",
678
+ },
679
+ };
680
+ }
681
+ });
682
+ };
683
+ // --- CONDITIONAL ROUTING FUNCTIONS ---
684
+ /**
685
+ * After prep: read mode → query; otherwise decide inference vs reconciler by operation mode.
686
+ */
687
+ const afterPrepRoute = (state) => {
688
+ if (state.error) {
689
+ logger.warn('Prep failed with error, short-circuiting to END', { error: state.error });
690
+ return '__end__';
691
+ }
692
+ if (state.operationMode === 'read') {
693
+ logger.verbose('Read mode - routing to query (fast path)');
694
+ return 'query';
695
+ }
696
+ return shouldRunInference(state);
697
+ };
698
+ /**
699
+ * Determines if inference should run based on operation mode.
700
+ * Delete operations skip inference entirely and go straight to reconciliation.
701
+ */
702
+ const shouldRunInference = (state) => {
703
+ if (state.operationMode === 'delete') {
704
+ logger.verbose('Delete mode - skipping inference, routing to reconciliation');
705
+ return 'reconciler';
706
+ }
707
+ logger.verbose('Running inference', {
708
+ operationMode: state.operationMode
709
+ });
710
+ return 'inference';
711
+ };
712
+ /**
713
+ * Determines if verification should run based on operation mode and inferred intents.
714
+ * Skips verification for:
715
+ * - Operations with no inferred intents
716
+ * - Can be extended to skip for update operations with no new intents
717
+ */
718
+ const shouldRunVerification = (state) => {
719
+ if (state.inferredIntents.length === 0) {
720
+ if (state.operationMode === 'propose') {
721
+ logger.verbose('Propose mode with no inferred intents - exiting early');
722
+ return '__end__';
723
+ }
724
+ logger.verbose('No intents to verify - skipping verification, routing to reconciliation');
725
+ return 'reconciler';
726
+ }
727
+ if (state.operationMode === 'update') {
728
+ logger.verbose('Update mode with new intents - running verification');
729
+ return 'verification';
730
+ }
731
+ if (state.operationMode === 'create') {
732
+ logger.verbose('Create mode - running verification');
733
+ return 'verification';
734
+ }
735
+ // Default to verification for safety
736
+ logger.verbose('Default routing to verification');
737
+ return 'verification';
738
+ };
739
+ // --- GRAPH ASSEMBLY WITH CONDITIONAL EDGES (PHASE 4) ---
740
+ const workflow = new StateGraph(IntentGraphState)
741
+ .addNode("prep", prepNode)
742
+ .addNode("query", queryNode)
743
+ .addNode("inference", inferenceNode)
744
+ .addNode("verification", verificationNode)
745
+ .addNode("reconciler", reconciliationNode)
746
+ .addNode("executor", executorNode)
747
+ // Flow paths:
748
+ // - READ: prep → query → END (fast path, no LLM calls)
749
+ // - CREATE: prep → inference → verification → reconciler → executor → END
750
+ // - UPDATE: prep → inference → reconciliation → executor → END (skips verification if no new intents)
751
+ // - DELETE: prep → reconciliation → executor → END (skips inference and verification)
752
+ // - PROPOSE: prep → inference → verification → END (no reconciliation/execution, no DB writes)
753
+ .addEdge(START, "prep")
754
+ // After prep: read mode → query; else inference or reconciler
755
+ .addConditionalEdges("prep", afterPrepRoute, {
756
+ query: "query",
757
+ inference: "inference",
758
+ reconciler: "reconciler",
759
+ __end__: END,
760
+ })
761
+ // Query (read mode) always ends
762
+ .addEdge("query", END)
763
+ // After inference: decide if we need verification (skip if no intents)
764
+ .addConditionalEdges("inference", shouldRunVerification, {
765
+ verification: "verification",
766
+ reconciler: "reconciler",
767
+ __end__: END,
768
+ })
769
+ // After verification: propose mode exits early; others continue to reconciliation
770
+ .addConditionalEdges("verification", (state) => {
771
+ if (state.operationMode === 'propose') {
772
+ logger.verbose('Propose mode - stopping after verification, skipping reconciliation');
773
+ return '__end__';
774
+ }
775
+ return 'reconciler';
776
+ }, {
777
+ reconciler: "reconciler",
778
+ __end__: END,
779
+ })
780
+ // Reconciliation always goes to executor
781
+ .addEdge("reconciler", "executor")
782
+ // Executor is always the end
783
+ .addEdge("executor", END);
784
+ return workflow.compile();
785
+ }
786
+ }
787
+ //# sourceMappingURL=intent.graph.js.map