@illuma-ai/agents 1.5.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) hide show
  1. package/README.md +0 -62
  2. package/dist/cjs/agents/AgentContext.cjs +159 -258
  3. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  4. package/dist/cjs/graphs/Graph.cjs +25 -8
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +1 -5
  7. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  8. package/dist/cjs/llm/bedrock/index.cjs +33 -61
  9. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  10. package/dist/cjs/llm/openai/utils/index.cjs +10 -27
  11. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  12. package/dist/cjs/main.cjs +3 -84
  13. package/dist/cjs/main.cjs.map +1 -1
  14. package/dist/cjs/messages/cache.cjs +0 -89
  15. package/dist/cjs/messages/cache.cjs.map +1 -1
  16. package/dist/cjs/messages/format.cjs +10 -68
  17. package/dist/cjs/messages/format.cjs.map +1 -1
  18. package/dist/cjs/tools/BashExecutor.cjs +11 -21
  19. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  20. package/dist/cjs/tools/CodeExecutor.cjs +10 -37
  21. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  22. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +11 -16
  23. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  24. package/dist/cjs/tools/ToolNode.cjs +73 -8
  25. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  26. package/dist/cjs/tools/search/search.cjs +3 -11
  27. package/dist/cjs/tools/search/search.cjs.map +1 -1
  28. package/dist/cjs/tools/search/tool.cjs +4 -28
  29. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  30. package/dist/cjs/tools/search/utils.cjs +3 -10
  31. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  32. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +48 -0
  33. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  34. package/dist/cjs/types/graph.cjs.map +1 -1
  35. package/dist/esm/agents/AgentContext.mjs +159 -258
  36. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  37. package/dist/esm/graphs/Graph.mjs +25 -8
  38. package/dist/esm/graphs/Graph.mjs.map +1 -1
  39. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +1 -5
  40. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  41. package/dist/esm/llm/bedrock/index.mjs +34 -61
  42. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  43. package/dist/esm/llm/openai/utils/index.mjs +10 -27
  44. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  45. package/dist/esm/main.mjs +1 -5
  46. package/dist/esm/main.mjs.map +1 -1
  47. package/dist/esm/messages/cache.mjs +0 -89
  48. package/dist/esm/messages/cache.mjs.map +1 -1
  49. package/dist/esm/messages/format.mjs +10 -68
  50. package/dist/esm/messages/format.mjs.map +1 -1
  51. package/dist/esm/tools/BashExecutor.mjs +12 -22
  52. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  53. package/dist/esm/tools/CodeExecutor.mjs +11 -37
  54. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  55. package/dist/esm/tools/ProgrammaticToolCalling.mjs +12 -17
  56. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  57. package/dist/esm/tools/ToolNode.mjs +73 -8
  58. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  59. package/dist/esm/tools/search/search.mjs +3 -11
  60. package/dist/esm/tools/search/search.mjs.map +1 -1
  61. package/dist/esm/tools/search/tool.mjs +4 -28
  62. package/dist/esm/tools/search/tool.mjs.map +1 -1
  63. package/dist/esm/tools/search/utils.mjs +3 -10
  64. package/dist/esm/tools/search/utils.mjs.map +1 -1
  65. package/dist/esm/tools/subagent/SubagentExecutor.mjs +48 -0
  66. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  67. package/dist/esm/types/graph.mjs.map +1 -1
  68. package/dist/types/agents/AgentContext.d.ts +25 -95
  69. package/dist/types/index.d.ts +0 -1
  70. package/dist/types/llm/bedrock/index.d.ts +1 -54
  71. package/dist/types/messages/format.d.ts +1 -4
  72. package/dist/types/tools/CodeExecutor.d.ts +0 -6
  73. package/dist/types/tools/search/types.d.ts +5 -99
  74. package/dist/types/tools/search/utils.d.ts +2 -2
  75. package/dist/types/tools/subagent/SubagentExecutor.d.ts +29 -0
  76. package/dist/types/types/graph.d.ts +24 -27
  77. package/dist/types/types/index.d.ts +0 -1
  78. package/dist/types/types/run.d.ts +0 -2
  79. package/dist/types/types/tools.d.ts +0 -9
  80. package/package.json +1 -61
  81. package/src/agents/AgentContext.test.ts +176 -0
  82. package/src/agents/AgentContext.ts +178 -304
  83. package/src/agents/__tests__/AgentContext.test.ts +0 -632
  84. package/src/graphs/Graph.ts +27 -8
  85. package/src/index.ts +0 -6
  86. package/src/llm/anthropic/utils/message_inputs.ts +1 -10
  87. package/src/llm/bedrock/__tests__/bedrock-caching.test.ts +18 -166
  88. package/src/llm/bedrock/index.ts +41 -116
  89. package/src/llm/openai/utils/index.ts +14 -31
  90. package/src/messages/cache.test.ts +24 -62
  91. package/src/messages/cache.ts +0 -112
  92. package/src/messages/format.ts +10 -89
  93. package/src/scripts/subagent-configurable-inheritance.ts +263 -0
  94. package/src/specs/anthropic.simple.test.ts +0 -61
  95. package/src/tools/BashExecutor.ts +13 -37
  96. package/src/tools/CodeExecutor.ts +11 -55
  97. package/src/tools/ProgrammaticToolCalling.ts +14 -29
  98. package/src/tools/ToolNode.ts +69 -8
  99. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -60
  100. package/src/tools/__tests__/SubagentExecutor.test.ts +157 -0
  101. package/src/tools/search/search.ts +2 -12
  102. package/src/tools/search/tool.ts +2 -36
  103. package/src/tools/search/types.ts +8 -133
  104. package/src/tools/search/utils.ts +5 -13
  105. package/src/tools/subagent/SubagentExecutor.ts +78 -0
  106. package/src/types/graph.ts +21 -27
  107. package/src/types/index.ts +0 -1
  108. package/src/types/run.ts +0 -2
  109. package/src/types/tools.ts +0 -9
  110. package/dist/cjs/langchain/google-common.cjs +0 -3
  111. package/dist/cjs/langchain/google-common.cjs.map +0 -1
  112. package/dist/cjs/langchain/index.cjs +0 -86
  113. package/dist/cjs/langchain/index.cjs.map +0 -1
  114. package/dist/cjs/langchain/language_models/chat_models.cjs +0 -3
  115. package/dist/cjs/langchain/language_models/chat_models.cjs.map +0 -1
  116. package/dist/cjs/langchain/messages/tool.cjs +0 -3
  117. package/dist/cjs/langchain/messages/tool.cjs.map +0 -1
  118. package/dist/cjs/langchain/messages.cjs +0 -51
  119. package/dist/cjs/langchain/messages.cjs.map +0 -1
  120. package/dist/cjs/langchain/openai.cjs +0 -3
  121. package/dist/cjs/langchain/openai.cjs.map +0 -1
  122. package/dist/cjs/langchain/prompts.cjs +0 -11
  123. package/dist/cjs/langchain/prompts.cjs.map +0 -1
  124. package/dist/cjs/langchain/runnables.cjs +0 -19
  125. package/dist/cjs/langchain/runnables.cjs.map +0 -1
  126. package/dist/cjs/langchain/tools.cjs +0 -23
  127. package/dist/cjs/langchain/tools.cjs.map +0 -1
  128. package/dist/cjs/langchain/utils/env.cjs +0 -11
  129. package/dist/cjs/langchain/utils/env.cjs.map +0 -1
  130. package/dist/cjs/llm/bedrock/cacheSupport.cjs +0 -55
  131. package/dist/cjs/llm/bedrock/cacheSupport.cjs.map +0 -1
  132. package/dist/cjs/tools/search/tavily-scraper.cjs +0 -189
  133. package/dist/cjs/tools/search/tavily-scraper.cjs.map +0 -1
  134. package/dist/cjs/tools/search/tavily-search.cjs +0 -372
  135. package/dist/cjs/tools/search/tavily-search.cjs.map +0 -1
  136. package/dist/cjs/types/agent-cache.cjs +0 -53
  137. package/dist/cjs/types/agent-cache.cjs.map +0 -1
  138. package/dist/esm/langchain/google-common.mjs +0 -2
  139. package/dist/esm/langchain/google-common.mjs.map +0 -1
  140. package/dist/esm/langchain/index.mjs +0 -5
  141. package/dist/esm/langchain/index.mjs.map +0 -1
  142. package/dist/esm/langchain/language_models/chat_models.mjs +0 -2
  143. package/dist/esm/langchain/language_models/chat_models.mjs.map +0 -1
  144. package/dist/esm/langchain/messages/tool.mjs +0 -2
  145. package/dist/esm/langchain/messages/tool.mjs.map +0 -1
  146. package/dist/esm/langchain/messages.mjs +0 -2
  147. package/dist/esm/langchain/messages.mjs.map +0 -1
  148. package/dist/esm/langchain/openai.mjs +0 -2
  149. package/dist/esm/langchain/openai.mjs.map +0 -1
  150. package/dist/esm/langchain/prompts.mjs +0 -2
  151. package/dist/esm/langchain/prompts.mjs.map +0 -1
  152. package/dist/esm/langchain/runnables.mjs +0 -2
  153. package/dist/esm/langchain/runnables.mjs.map +0 -1
  154. package/dist/esm/langchain/tools.mjs +0 -2
  155. package/dist/esm/langchain/tools.mjs.map +0 -1
  156. package/dist/esm/langchain/utils/env.mjs +0 -2
  157. package/dist/esm/langchain/utils/env.mjs.map +0 -1
  158. package/dist/esm/llm/bedrock/cacheSupport.mjs +0 -52
  159. package/dist/esm/llm/bedrock/cacheSupport.mjs.map +0 -1
  160. package/dist/esm/tools/search/tavily-scraper.mjs +0 -186
  161. package/dist/esm/tools/search/tavily-scraper.mjs.map +0 -1
  162. package/dist/esm/tools/search/tavily-search.mjs +0 -370
  163. package/dist/esm/tools/search/tavily-search.mjs.map +0 -1
  164. package/dist/esm/types/agent-cache.mjs +0 -51
  165. package/dist/esm/types/agent-cache.mjs.map +0 -1
  166. package/dist/types/langchain/google-common.d.ts +0 -1
  167. package/dist/types/langchain/index.d.ts +0 -8
  168. package/dist/types/langchain/language_models/chat_models.d.ts +0 -1
  169. package/dist/types/langchain/messages/tool.d.ts +0 -1
  170. package/dist/types/langchain/messages.d.ts +0 -2
  171. package/dist/types/langchain/openai.d.ts +0 -1
  172. package/dist/types/langchain/prompts.d.ts +0 -1
  173. package/dist/types/langchain/runnables.d.ts +0 -2
  174. package/dist/types/langchain/tools.d.ts +0 -2
  175. package/dist/types/langchain/utils/env.d.ts +0 -1
  176. package/dist/types/llm/bedrock/cacheSupport.d.ts +0 -35
  177. package/dist/types/tools/search/tavily-scraper.d.ts +0 -19
  178. package/dist/types/tools/search/tavily-search.d.ts +0 -4
  179. package/dist/types/tools/subagent/types.d.ts +0 -84
  180. package/dist/types/types/agent-cache.d.ts +0 -70
  181. package/src/agents/AgentContext.js.map +0 -1
  182. package/src/agents/AgentContext.test.js.map +0 -1
  183. package/src/agents/__tests__/AgentContext.cacheTtl.live.test.ts +0 -259
  184. package/src/agents/__tests__/AgentContext.crossAgentTier1.live.test.ts +0 -264
  185. package/src/agents/__tests__/AgentContext.crossUserCache.live.test.ts +0 -342
  186. package/src/agents/__tests__/AgentContext.test.js.map +0 -1
  187. package/src/agents/__tests__/resolveStructuredOutputMode.test.js.map +0 -1
  188. package/src/common/enum.js.map +0 -1
  189. package/src/common/index.js.map +0 -1
  190. package/src/events.js.map +0 -1
  191. package/src/graphs/Graph.js.map +0 -1
  192. package/src/graphs/MultiAgentGraph.js.map +0 -1
  193. package/src/graphs/__tests__/structured-output.integration.test.js.map +0 -1
  194. package/src/graphs/__tests__/structured-output.test.js.map +0 -1
  195. package/src/graphs/contextManagement.e2e.test.js.map +0 -1
  196. package/src/graphs/contextManagement.test.js.map +0 -1
  197. package/src/graphs/handoffValidation.test.js.map +0 -1
  198. package/src/graphs/index.js.map +0 -1
  199. package/src/index.js.map +0 -1
  200. package/src/instrumentation.js.map +0 -1
  201. package/src/langchain/google-common.ts +0 -1
  202. package/src/langchain/index.ts +0 -8
  203. package/src/langchain/language_models/chat_models.ts +0 -1
  204. package/src/langchain/messages/tool.ts +0 -5
  205. package/src/langchain/messages.ts +0 -21
  206. package/src/langchain/openai.ts +0 -1
  207. package/src/langchain/prompts.ts +0 -1
  208. package/src/langchain/runnables.ts +0 -7
  209. package/src/langchain/tools.ts +0 -8
  210. package/src/langchain/utils/env.ts +0 -1
  211. package/src/llm/anthropic/index.js.map +0 -1
  212. package/src/llm/anthropic/types.js.map +0 -1
  213. package/src/llm/anthropic/utils/message_inputs.js.map +0 -1
  214. package/src/llm/anthropic/utils/message_outputs.js.map +0 -1
  215. package/src/llm/anthropic/utils/output_parsers.js.map +0 -1
  216. package/src/llm/anthropic/utils/server-tool-inputs.test.ts +0 -436
  217. package/src/llm/anthropic/utils/tools.js.map +0 -1
  218. package/src/llm/bedrock/__tests__/bedrock-caching.test.js.map +0 -1
  219. package/src/llm/bedrock/cacheSupport.test.ts +0 -99
  220. package/src/llm/bedrock/cacheSupport.ts +0 -53
  221. package/src/llm/bedrock/index.js.map +0 -1
  222. package/src/llm/bedrock/types.js.map +0 -1
  223. package/src/llm/bedrock/utils/index.js.map +0 -1
  224. package/src/llm/bedrock/utils/message_inputs.js.map +0 -1
  225. package/src/llm/bedrock/utils/message_outputs.js.map +0 -1
  226. package/src/llm/fake.js.map +0 -1
  227. package/src/llm/google/index.js.map +0 -1
  228. package/src/llm/google/types.js.map +0 -1
  229. package/src/llm/google/utils/common.js.map +0 -1
  230. package/src/llm/google/utils/tools.js.map +0 -1
  231. package/src/llm/google/utils/zod_to_genai_parameters.js.map +0 -1
  232. package/src/llm/openai/index.js.map +0 -1
  233. package/src/llm/openai/types.js.map +0 -1
  234. package/src/llm/openai/utils/index.js.map +0 -1
  235. package/src/llm/openai/utils/isReasoningModel.test.js.map +0 -1
  236. package/src/llm/openrouter/index.js.map +0 -1
  237. package/src/llm/openrouter/reasoning.test.js.map +0 -1
  238. package/src/llm/providers.js.map +0 -1
  239. package/src/llm/text.js.map +0 -1
  240. package/src/llm/vertexai/index.js.map +0 -1
  241. package/src/messages/__tests__/tools.test.js.map +0 -1
  242. package/src/messages/cache.js.map +0 -1
  243. package/src/messages/cache.test.js.map +0 -1
  244. package/src/messages/content.js.map +0 -1
  245. package/src/messages/content.test.js.map +0 -1
  246. package/src/messages/core.js.map +0 -1
  247. package/src/messages/ensureThinkingBlock.test.js.map +0 -1
  248. package/src/messages/format.js.map +0 -1
  249. package/src/messages/formatAgentMessages.test.js.map +0 -1
  250. package/src/messages/formatAgentMessages.tools.test.js.map +0 -1
  251. package/src/messages/formatMessage.test.js.map +0 -1
  252. package/src/messages/ids.js.map +0 -1
  253. package/src/messages/index.js.map +0 -1
  254. package/src/messages/labelContentByAgent.test.js.map +0 -1
  255. package/src/messages/prune.js.map +0 -1
  256. package/src/messages/reducer.js.map +0 -1
  257. package/src/messages/shiftIndexTokenCountMap.test.js.map +0 -1
  258. package/src/messages/summarize.js.map +0 -1
  259. package/src/messages/summarize.test.js.map +0 -1
  260. package/src/messages/tools.js.map +0 -1
  261. package/src/mockStream.js.map +0 -1
  262. package/src/prompts/collab.js.map +0 -1
  263. package/src/prompts/index.js.map +0 -1
  264. package/src/prompts/taskmanager.js.map +0 -1
  265. package/src/run.js.map +0 -1
  266. package/src/schemas/index.js.map +0 -1
  267. package/src/schemas/schema-preparation.test.js.map +0 -1
  268. package/src/schemas/validate.js.map +0 -1
  269. package/src/schemas/validate.test.js.map +0 -1
  270. package/src/scripts/abort.js.map +0 -1
  271. package/src/scripts/ant_web_search.js.map +0 -1
  272. package/src/scripts/ant_web_search_edge_case.js.map +0 -1
  273. package/src/scripts/ant_web_search_error_edge_case.js.map +0 -1
  274. package/src/scripts/args.js.map +0 -1
  275. package/src/scripts/bedrock-cache-debug.js.map +0 -1
  276. package/src/scripts/bedrock-content-aggregation-test.js.map +0 -1
  277. package/src/scripts/bedrock-merge-test.js.map +0 -1
  278. package/src/scripts/bedrock-parallel-tools-test.js.map +0 -1
  279. package/src/scripts/caching.js.map +0 -1
  280. package/src/scripts/cli.js.map +0 -1
  281. package/src/scripts/cli2.js.map +0 -1
  282. package/src/scripts/cli3.js.map +0 -1
  283. package/src/scripts/cli4.js.map +0 -1
  284. package/src/scripts/cli5.js.map +0 -1
  285. package/src/scripts/code_exec.js.map +0 -1
  286. package/src/scripts/code_exec_files.js.map +0 -1
  287. package/src/scripts/code_exec_multi_session.js.map +0 -1
  288. package/src/scripts/code_exec_ptc.js.map +0 -1
  289. package/src/scripts/code_exec_session.js.map +0 -1
  290. package/src/scripts/code_exec_simple.js.map +0 -1
  291. package/src/scripts/content.js.map +0 -1
  292. package/src/scripts/empty_input.js.map +0 -1
  293. package/src/scripts/handoff-test.js.map +0 -1
  294. package/src/scripts/image.js.map +0 -1
  295. package/src/scripts/memory.js.map +0 -1
  296. package/src/scripts/multi-agent-chain.js.map +0 -1
  297. package/src/scripts/multi-agent-conditional.js.map +0 -1
  298. package/src/scripts/multi-agent-document-review-chain.js.map +0 -1
  299. package/src/scripts/multi-agent-hybrid-flow.js.map +0 -1
  300. package/src/scripts/multi-agent-parallel-start.js.map +0 -1
  301. package/src/scripts/multi-agent-parallel.js.map +0 -1
  302. package/src/scripts/multi-agent-sequence.js.map +0 -1
  303. package/src/scripts/multi-agent-supervisor.js.map +0 -1
  304. package/src/scripts/multi-agent-test.js.map +0 -1
  305. package/src/scripts/parallel-asymmetric-tools-test.js.map +0 -1
  306. package/src/scripts/parallel-full-metadata-test.js.map +0 -1
  307. package/src/scripts/parallel-tools-test.js.map +0 -1
  308. package/src/scripts/programmatic_exec.js.map +0 -1
  309. package/src/scripts/programmatic_exec_agent.js.map +0 -1
  310. package/src/scripts/search.js.map +0 -1
  311. package/src/scripts/sequential-full-metadata-test.js.map +0 -1
  312. package/src/scripts/simple.js.map +0 -1
  313. package/src/scripts/single-agent-metadata-test.js.map +0 -1
  314. package/src/scripts/stream.js.map +0 -1
  315. package/src/scripts/test-custom-prompt-key.js.map +0 -1
  316. package/src/scripts/test-handoff-input.js.map +0 -1
  317. package/src/scripts/test-handoff-preamble.js.map +0 -1
  318. package/src/scripts/test-handoff-steering.js.map +0 -1
  319. package/src/scripts/test-multi-agent-list-handoff.js.map +0 -1
  320. package/src/scripts/test-parallel-agent-labeling.js.map +0 -1
  321. package/src/scripts/test-parallel-handoffs.js.map +0 -1
  322. package/src/scripts/test-thinking-handoff-bedrock.js.map +0 -1
  323. package/src/scripts/test-thinking-handoff.js.map +0 -1
  324. package/src/scripts/test-thinking-to-thinking-handoff-bedrock.js.map +0 -1
  325. package/src/scripts/test-tool-before-handoff-role-order.js.map +0 -1
  326. package/src/scripts/test-tools-before-handoff.js.map +0 -1
  327. package/src/scripts/test_code_api.js.map +0 -1
  328. package/src/scripts/thinking-bedrock.js.map +0 -1
  329. package/src/scripts/thinking-vertexai.js.map +0 -1
  330. package/src/scripts/thinking.js.map +0 -1
  331. package/src/scripts/tool_search.js.map +0 -1
  332. package/src/scripts/tools.js.map +0 -1
  333. package/src/specs/agent-handoffs-bedrock.integration.test.js.map +0 -1
  334. package/src/specs/agent-handoffs.test.js.map +0 -1
  335. package/src/specs/anthropic.simple.test.js.map +0 -1
  336. package/src/specs/azure.simple.test.js.map +0 -1
  337. package/src/specs/cache.simple.test.js.map +0 -1
  338. package/src/specs/custom-event-await.test.js.map +0 -1
  339. package/src/specs/deepseek.simple.test.js.map +0 -1
  340. package/src/specs/emergency-prune.test.js.map +0 -1
  341. package/src/specs/moonshot.simple.test.js.map +0 -1
  342. package/src/specs/observability.integration.test.js.map +0 -1
  343. package/src/specs/openai.simple.test.js.map +0 -1
  344. package/src/specs/openrouter.simple.test.js.map +0 -1
  345. package/src/specs/prune.test.js.map +0 -1
  346. package/src/specs/reasoning.test.js.map +0 -1
  347. package/src/specs/spec.utils.js.map +0 -1
  348. package/src/specs/thinking-handoff.test.js.map +0 -1
  349. package/src/specs/thinking-prune.test.js.map +0 -1
  350. package/src/specs/token-distribution-edge-case.test.js.map +0 -1
  351. package/src/specs/token-memoization.test.js.map +0 -1
  352. package/src/specs/tokens.test.js.map +0 -1
  353. package/src/specs/tool-error.test.js.map +0 -1
  354. package/src/splitStream.js.map +0 -1
  355. package/src/splitStream.test.js.map +0 -1
  356. package/src/stream.js.map +0 -1
  357. package/src/stream.test.js.map +0 -1
  358. package/src/test/mockTools.js.map +0 -1
  359. package/src/tools/BrowserTools.js.map +0 -1
  360. package/src/tools/Calculator.js.map +0 -1
  361. package/src/tools/Calculator.test.js.map +0 -1
  362. package/src/tools/CodeExecutor.js.map +0 -1
  363. package/src/tools/ProgrammaticToolCalling.js.map +0 -1
  364. package/src/tools/StreamingToolCallBuffer.js.map +0 -1
  365. package/src/tools/ToolNode.js.map +0 -1
  366. package/src/tools/ToolSearch.js.map +0 -1
  367. package/src/tools/__tests__/BrowserTools.test.js.map +0 -1
  368. package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.js.map +0 -1
  369. package/src/tools/__tests__/ProgrammaticToolCalling.test.js.map +0 -1
  370. package/src/tools/__tests__/StreamingToolCallBuffer.test.js.map +0 -1
  371. package/src/tools/__tests__/ToolApproval.test.js.map +0 -1
  372. package/src/tools/__tests__/ToolNode.recovery.test.js.map +0 -1
  373. package/src/tools/__tests__/ToolNode.session.test.js.map +0 -1
  374. package/src/tools/__tests__/ToolSearch.integration.test.js.map +0 -1
  375. package/src/tools/__tests__/ToolSearch.test.js.map +0 -1
  376. package/src/tools/__tests__/handlers.test.js.map +0 -1
  377. package/src/tools/__tests__/truncation-recovery.integration.test.js.map +0 -1
  378. package/src/tools/handlers.js.map +0 -1
  379. package/src/tools/schema.js.map +0 -1
  380. package/src/tools/search/anthropic.js.map +0 -1
  381. package/src/tools/search/content.js.map +0 -1
  382. package/src/tools/search/content.test.js.map +0 -1
  383. package/src/tools/search/firecrawl.js.map +0 -1
  384. package/src/tools/search/format.js.map +0 -1
  385. package/src/tools/search/highlights.js.map +0 -1
  386. package/src/tools/search/index.js.map +0 -1
  387. package/src/tools/search/jina-reranker.test.js.map +0 -1
  388. package/src/tools/search/rerankers.js.map +0 -1
  389. package/src/tools/search/schema.js.map +0 -1
  390. package/src/tools/search/search.js.map +0 -1
  391. package/src/tools/search/serper-scraper.js.map +0 -1
  392. package/src/tools/search/tavily-scraper.ts +0 -235
  393. package/src/tools/search/tavily-search.ts +0 -424
  394. package/src/tools/search/tavily.test.ts +0 -965
  395. package/src/tools/search/test.js.map +0 -1
  396. package/src/tools/search/tool.js.map +0 -1
  397. package/src/tools/search/types.js.map +0 -1
  398. package/src/tools/search/utils.js.map +0 -1
  399. package/src/tools/subagent/types.test.ts +0 -70
  400. package/src/tools/subagent/types.ts +0 -115
  401. package/src/types/agent-cache.ts +0 -73
  402. package/src/types/graph.js.map +0 -1
  403. package/src/types/graph.test.js.map +0 -1
  404. package/src/types/index.js.map +0 -1
  405. package/src/types/llm.js.map +0 -1
  406. package/src/types/messages.js.map +0 -1
  407. package/src/types/run.js.map +0 -1
  408. package/src/types/stream.js.map +0 -1
  409. package/src/types/tools.js.map +0 -1
  410. package/src/utils/contextAnalytics.js.map +0 -1
  411. package/src/utils/contextAnalytics.test.js.map +0 -1
  412. package/src/utils/events.js.map +0 -1
  413. package/src/utils/graph.js.map +0 -1
  414. package/src/utils/handlers.js.map +0 -1
  415. package/src/utils/index.js.map +0 -1
  416. package/src/utils/llm.js.map +0 -1
  417. package/src/utils/llmConfig.js.map +0 -1
  418. package/src/utils/logging.js.map +0 -1
  419. package/src/utils/misc.js.map +0 -1
  420. package/src/utils/run.js.map +0 -1
  421. package/src/utils/schema.js.map +0 -1
  422. package/src/utils/title.js.map +0 -1
  423. package/src/utils/tokens.js.map +0 -1
  424. package/src/utils/toonFormat.js.map +0 -1
@@ -1,965 +0,0 @@
1
- import axios from 'axios';
2
- import type * as t from './types';
3
- import { TavilyScraper, createTavilyScraper } from './tavily-scraper';
4
- import { createSearchAPI } from './search';
5
- import { createSearchTool } from './tool';
6
-
7
- jest.mock('axios');
8
- const mockedAxios = axios as jest.Mocked<typeof axios>;
9
-
10
- const mockLogger = {
11
- error: jest.fn(),
12
- warn: jest.fn(),
13
- info: jest.fn(),
14
- debug: jest.fn(),
15
- } as unknown as t.Logger;
16
-
17
- describe('Tavily search API', () => {
18
- beforeEach(() => {
19
- jest.clearAllMocks();
20
- });
21
-
22
- it('throws when Tavily API key is missing', () => {
23
- expect(() =>
24
- createSearchAPI({
25
- searchProvider: 'tavily',
26
- tavilyApiKey: '',
27
- })
28
- ).toThrow('TAVILY_API_KEY is required for Tavily API');
29
- });
30
-
31
- it('returns an error for empty Tavily search queries', async () => {
32
- const searchAPI = createSearchAPI({
33
- searchProvider: 'tavily',
34
- tavilyApiKey: 'test-key',
35
- });
36
-
37
- const result = await searchAPI.getSources({ query: ' ' });
38
-
39
- expect(result).toEqual({
40
- success: false,
41
- error: 'Query cannot be empty',
42
- });
43
- expect(mockedAxios.post).not.toHaveBeenCalled();
44
- });
45
-
46
- it('returns an error when the Tavily search request fails', async () => {
47
- mockedAxios.post.mockRejectedValueOnce(new Error('Network error'));
48
-
49
- const searchAPI = createSearchAPI({
50
- searchProvider: 'tavily',
51
- tavilyApiKey: 'test-key',
52
- });
53
-
54
- const result = await searchAPI.getSources({ query: 'example query' });
55
-
56
- expect(result.success).toBe(false);
57
- expect(result.error).toBe('Tavily API request failed: Network error');
58
- });
59
-
60
- it('passes string-mode options and maps Tavily response fields', async () => {
61
- mockedAxios.post.mockResolvedValueOnce({
62
- data: {
63
- answer: 'A concise answer.',
64
- images: [
65
- {
66
- url: 'https://example.com/image.png',
67
- description: 'Example image',
68
- },
69
- {
70
- description: 'Skipped image',
71
- },
72
- 'https://example.com/second.png',
73
- ],
74
- results: [
75
- {
76
- title: 'Example',
77
- url: 'https://example.com',
78
- content: 'Example summary',
79
- published_date: '2026-01-02',
80
- },
81
- ],
82
- },
83
- });
84
-
85
- const searchAPI = createSearchAPI({
86
- searchProvider: 'tavily',
87
- tavilyApiKey: 'test-key',
88
- tavilySearchOptions: {
89
- includeAnswer: 'advanced',
90
- includeRawContent: 'markdown',
91
- includeImages: true,
92
- includeImageDescriptions: true,
93
- safeSearch: true,
94
- timeRange: 'd',
95
- },
96
- });
97
-
98
- const result = await searchAPI.getSources({
99
- query: 'example query',
100
- country: 'US',
101
- });
102
- const [, payload] = mockedAxios.post.mock.calls[0];
103
-
104
- expect(payload).toMatchObject({
105
- query: 'example query',
106
- country: 'united states',
107
- safe_search: true,
108
- include_answer: 'advanced',
109
- include_raw_content: 'markdown',
110
- include_images: true,
111
- include_image_descriptions: true,
112
- time_range: 'day',
113
- });
114
- expect(result.success).toBe(true);
115
- expect(result.data?.answerBox?.snippet).toBe('A concise answer.');
116
- expect(result.data?.organic?.[0]).toMatchObject({
117
- title: 'Example',
118
- link: 'https://example.com',
119
- snippet: 'Example summary',
120
- date: '2026-01-02',
121
- });
122
- expect(result.data?.images?.[0]).toMatchObject({
123
- imageUrl: 'https://example.com/image.png',
124
- title: 'Example image',
125
- position: 1,
126
- });
127
- expect(result.data?.images?.[1]).toMatchObject({
128
- imageUrl: 'https://example.com/second.png',
129
- position: 2,
130
- });
131
- });
132
-
133
- it('omits country for Tavily news searches', async () => {
134
- mockedAxios.post.mockResolvedValueOnce({
135
- data: {
136
- results: [],
137
- },
138
- });
139
-
140
- const searchAPI = createSearchAPI({
141
- searchProvider: 'tavily',
142
- tavilyApiKey: 'test-key',
143
- });
144
-
145
- await searchAPI.getSources({
146
- query: 'example query',
147
- country: 'US',
148
- news: true,
149
- });
150
- const [, payload] = mockedAxios.post.mock.calls[0];
151
-
152
- expect(payload).toMatchObject({
153
- query: 'example query',
154
- topic: 'news',
155
- });
156
- expect(payload).not.toHaveProperty('country');
157
- expect(payload).not.toHaveProperty('safe_search');
158
- });
159
-
160
- it('prioritizes request-level news mode over configured Tavily topic', async () => {
161
- mockedAxios.post.mockResolvedValueOnce({
162
- data: {
163
- results: [
164
- {
165
- title: 'Market news',
166
- url: 'https://example.com/news',
167
- content: 'A news result',
168
- published_date: '2026-01-03',
169
- },
170
- ],
171
- },
172
- });
173
-
174
- const searchAPI = createSearchAPI({
175
- searchProvider: 'tavily',
176
- tavilyApiKey: 'test-key',
177
- tavilySearchOptions: {
178
- topic: 'finance',
179
- },
180
- });
181
-
182
- const result = await searchAPI.getSources({
183
- query: 'example query',
184
- type: 'news',
185
- });
186
- const [, payload] = mockedAxios.post.mock.calls[0];
187
-
188
- expect(payload).toMatchObject({
189
- query: 'example query',
190
- topic: 'news',
191
- });
192
- expect(result.data?.news?.[0]).toMatchObject({
193
- title: 'Market news',
194
- link: 'https://example.com/news',
195
- });
196
- });
197
-
198
- it('maps ISO country codes to Tavily country enum values', async () => {
199
- mockedAxios.post.mockResolvedValueOnce({
200
- data: {
201
- results: [],
202
- },
203
- });
204
-
205
- const searchAPI = createSearchAPI({
206
- searchProvider: 'tavily',
207
- tavilyApiKey: 'test-key',
208
- });
209
-
210
- await searchAPI.getSources({
211
- query: 'example query',
212
- country: 'CZ',
213
- });
214
- const [, payload] = mockedAxios.post.mock.calls[0];
215
-
216
- expect(payload).toMatchObject({
217
- query: 'example query',
218
- country: 'czech republic',
219
- });
220
- expect(payload).not.toHaveProperty('safe_search');
221
- });
222
-
223
- it('omits ISO country mappings that Tavily does not support', async () => {
224
- mockedAxios.post.mockResolvedValueOnce({
225
- data: {
226
- results: [],
227
- },
228
- });
229
-
230
- const searchAPI = createSearchAPI({
231
- searchProvider: 'tavily',
232
- tavilyApiKey: 'test-key',
233
- });
234
-
235
- await searchAPI.getSources({
236
- query: 'example query',
237
- country: 'CD',
238
- });
239
- const [, payload] = mockedAxios.post.mock.calls[0];
240
-
241
- expect(payload).toMatchObject({
242
- query: 'example query',
243
- });
244
- expect(payload).not.toHaveProperty('country');
245
- expect(payload).not.toHaveProperty('safe_search');
246
- });
247
-
248
- it('omits safe search for unsupported Tavily search depths', async () => {
249
- mockedAxios.post.mockResolvedValueOnce({
250
- data: {
251
- results: [],
252
- },
253
- });
254
-
255
- const searchAPI = createSearchAPI({
256
- searchProvider: 'tavily',
257
- tavilyApiKey: 'test-key',
258
- tavilySearchOptions: {
259
- searchDepth: 'fast',
260
- safeSearch: true,
261
- },
262
- });
263
-
264
- await searchAPI.getSources({
265
- query: 'example query',
266
- });
267
- const [, payload] = mockedAxios.post.mock.calls[0];
268
-
269
- expect(payload).toMatchObject({
270
- query: 'example query',
271
- search_depth: 'fast',
272
- });
273
- expect(payload).not.toHaveProperty('safe_search');
274
- });
275
-
276
- it('only sends chunks per source for advanced Tavily searches', async () => {
277
- mockedAxios.post.mockResolvedValue({
278
- data: {
279
- results: [],
280
- },
281
- });
282
-
283
- const basicSearchAPI = createSearchAPI({
284
- searchProvider: 'tavily',
285
- tavilyApiKey: 'test-key',
286
- tavilySearchOptions: {
287
- chunksPerSource: 2,
288
- },
289
- });
290
-
291
- await basicSearchAPI.getSources({
292
- query: 'example query',
293
- });
294
- const [, basicPayload] = mockedAxios.post.mock.calls[0];
295
-
296
- expect(basicPayload).toMatchObject({
297
- query: 'example query',
298
- search_depth: 'basic',
299
- });
300
- expect(basicPayload).not.toHaveProperty('chunks_per_source');
301
-
302
- const advancedSearchAPI = createSearchAPI({
303
- searchProvider: 'tavily',
304
- tavilyApiKey: 'test-key',
305
- tavilySearchOptions: {
306
- searchDepth: 'advanced',
307
- chunksPerSource: 2,
308
- },
309
- });
310
-
311
- await advancedSearchAPI.getSources({
312
- query: 'example query',
313
- });
314
- const [, advancedPayload] = mockedAxios.post.mock.calls[1];
315
-
316
- expect(advancedPayload).toMatchObject({
317
- query: 'example query',
318
- search_depth: 'advanced',
319
- chunks_per_source: 2,
320
- });
321
- });
322
-
323
- it('uses explicit tool safe search config and skips Tavily video subqueries', async () => {
324
- mockedAxios.post.mockResolvedValue({
325
- data: {
326
- results: [],
327
- },
328
- });
329
-
330
- const searchTool = createSearchTool({
331
- searchProvider: 'tavily',
332
- tavilyApiKey: 'test-key',
333
- scraperProvider: 'tavily',
334
- rerankerType: 'none',
335
- logger: mockLogger,
336
- safeSearch: 2,
337
- });
338
-
339
- await searchTool.invoke({
340
- query: 'example query',
341
- videos: true,
342
- });
343
- const [, payload] = mockedAxios.post.mock.calls[0];
344
-
345
- expect(mockedAxios.post).toHaveBeenCalledTimes(1);
346
- expect(payload).toMatchObject({
347
- query: 'example query',
348
- safe_search: true,
349
- });
350
- });
351
-
352
- it('lets explicit tool safe search override Tavily option safe search', async () => {
353
- mockedAxios.post.mockResolvedValue({
354
- data: {
355
- results: [],
356
- },
357
- });
358
-
359
- const searchTool = createSearchTool({
360
- searchProvider: 'tavily',
361
- tavilyApiKey: 'test-key',
362
- scraperProvider: 'tavily',
363
- rerankerType: 'none',
364
- logger: mockLogger,
365
- safeSearch: 0,
366
- tavilySearchOptions: {
367
- safeSearch: true,
368
- },
369
- });
370
-
371
- await searchTool.invoke({
372
- query: 'example query',
373
- });
374
- const [, payload] = mockedAxios.post.mock.calls[0];
375
-
376
- expect(payload).toMatchObject({
377
- query: 'example query',
378
- safe_search: false,
379
- });
380
- });
381
-
382
- it('preserves Tavily scraper connection overrides in the search tool', async () => {
383
- mockedAxios.post
384
- .mockResolvedValueOnce({
385
- data: {
386
- results: [
387
- {
388
- title: 'Example',
389
- url: 'https://example.com',
390
- content: 'Example summary',
391
- },
392
- ],
393
- },
394
- })
395
- .mockResolvedValueOnce({
396
- data: {
397
- results: [
398
- {
399
- url: 'https://example.com',
400
- raw_content: 'Extracted content',
401
- images: [],
402
- },
403
- ],
404
- failed_results: [],
405
- },
406
- });
407
-
408
- const searchTool = createSearchTool({
409
- searchProvider: 'tavily',
410
- tavilyApiKey: 'search-key',
411
- scraperProvider: 'tavily',
412
- tavilyScraperOptions: {
413
- apiKey: 'scraper-key',
414
- apiUrl: 'https://proxy.example.com/extract',
415
- },
416
- rerankerType: 'none',
417
- logger: mockLogger,
418
- });
419
-
420
- await searchTool.invoke({
421
- query: 'example query',
422
- });
423
- const [, , searchConfig] = mockedAxios.post.mock.calls[0];
424
- const [extractUrl, , extractConfig] = mockedAxios.post.mock.calls[1];
425
-
426
- expect(mockedAxios.post).toHaveBeenCalledTimes(2);
427
- expect(searchConfig).toMatchObject({
428
- headers: {
429
- Authorization: 'Bearer search-key',
430
- },
431
- });
432
- expect(extractUrl).toBe('https://proxy.example.com/extract');
433
- expect(extractConfig).toMatchObject({
434
- headers: {
435
- Authorization: 'Bearer scraper-key',
436
- },
437
- });
438
- });
439
- });
440
-
441
- describe('TavilyScraper', () => {
442
- beforeEach(() => {
443
- jest.clearAllMocks();
444
- });
445
-
446
- describe('constructor', () => {
447
- it('warns when TAVILY_API_KEY is not set', () => {
448
- const logger = { ...mockLogger, warn: jest.fn() } as unknown as t.Logger;
449
- new TavilyScraper({ apiKey: '', logger });
450
- expect(logger.warn).toHaveBeenCalledWith(
451
- 'TAVILY_API_KEY is not set. Scraping will not work.'
452
- );
453
- });
454
-
455
- it('uses TAVILY_EXTRACT_URL env var for apiUrl', async () => {
456
- const original = process.env.TAVILY_EXTRACT_URL;
457
- try {
458
- process.env.TAVILY_EXTRACT_URL =
459
- 'https://custom-proxy.example.com/extract';
460
- mockedAxios.post.mockResolvedValueOnce({
461
- data: { results: [], failed_results: [] },
462
- });
463
- const scraper = new TavilyScraper({
464
- apiKey: 'test-key',
465
- logger: mockLogger,
466
- });
467
-
468
- await scraper.scrapeUrl('https://example.com');
469
-
470
- expect(mockedAxios.post.mock.calls[0][0]).toBe(
471
- 'https://custom-proxy.example.com/extract'
472
- );
473
- } finally {
474
- if (original !== undefined) {
475
- process.env.TAVILY_EXTRACT_URL = original;
476
- } else {
477
- delete process.env.TAVILY_EXTRACT_URL;
478
- }
479
- }
480
- });
481
-
482
- it('defaults to https://api.tavily.com/extract', async () => {
483
- const original = process.env.TAVILY_EXTRACT_URL;
484
- try {
485
- delete process.env.TAVILY_EXTRACT_URL;
486
- mockedAxios.post.mockResolvedValueOnce({
487
- data: { results: [], failed_results: [] },
488
- });
489
- const scraper = new TavilyScraper({
490
- apiKey: 'test-key',
491
- logger: mockLogger,
492
- });
493
-
494
- await scraper.scrapeUrl('https://example.com');
495
-
496
- expect(mockedAxios.post.mock.calls[0][0]).toBe(
497
- 'https://api.tavily.com/extract'
498
- );
499
- } finally {
500
- if (original !== undefined) {
501
- process.env.TAVILY_EXTRACT_URL = original;
502
- }
503
- }
504
- });
505
-
506
- it('defaults timeout to 15000ms', async () => {
507
- mockedAxios.post.mockResolvedValueOnce({
508
- data: { results: [], failed_results: [] },
509
- });
510
- const scraper = new TavilyScraper({
511
- apiKey: 'test-key',
512
- logger: mockLogger,
513
- });
514
-
515
- await scraper.scrapeUrl('https://example.com');
516
-
517
- expect(mockedAxios.post.mock.calls[0][2]).toMatchObject({
518
- timeout: 15000,
519
- });
520
- });
521
-
522
- it('defaults advanced extraction timeout to 30000ms', async () => {
523
- mockedAxios.post.mockResolvedValueOnce({
524
- data: { results: [], failed_results: [] },
525
- });
526
- const scraper = new TavilyScraper({
527
- apiKey: 'test-key',
528
- extractDepth: 'advanced',
529
- logger: mockLogger,
530
- });
531
-
532
- await scraper.scrapeUrl('https://example.com');
533
-
534
- expect(mockedAxios.post.mock.calls[0][2]).toMatchObject({
535
- timeout: 30000,
536
- });
537
- });
538
- });
539
-
540
- describe('scrapeUrl', () => {
541
- it('returns error when API key is not set', async () => {
542
- const scraper = createTavilyScraper({ apiKey: '', logger: mockLogger });
543
- const [url, response] = await scraper.scrapeUrl('https://example.com');
544
- expect(url).toBe('https://example.com');
545
- expect(response.success).toBe(false);
546
- expect(response.error).toBe('TAVILY_API_KEY is not set');
547
- });
548
-
549
- it('returns scraped content on success', async () => {
550
- mockedAxios.post.mockResolvedValueOnce({
551
- data: {
552
- results: [
553
- {
554
- url: 'https://example.com',
555
- raw_content: '# Hello World\nSome content here.',
556
- images: ['https://example.com/img.png'],
557
- favicon: 'https://example.com/favicon.ico',
558
- },
559
- ],
560
- failed_results: [],
561
- },
562
- });
563
-
564
- const scraper = createTavilyScraper({
565
- apiKey: 'test-key',
566
- logger: mockLogger,
567
- });
568
- const [url, response] = await scraper.scrapeUrl('https://example.com');
569
-
570
- expect(url).toBe('https://example.com');
571
- expect(response.success).toBe(true);
572
- expect(response.data?.rawContent).toBe(
573
- '# Hello World\nSome content here.'
574
- );
575
- expect(response.data?.images).toEqual(['https://example.com/img.png']);
576
- expect(response.data?.favicon).toBe('https://example.com/favicon.ico');
577
- });
578
-
579
- it('applies per-call extract options to the Tavily payload', async () => {
580
- mockedAxios.post.mockResolvedValueOnce({
581
- data: {
582
- results: [
583
- {
584
- url: 'https://example.com',
585
- raw_content: 'Content',
586
- images: [],
587
- },
588
- ],
589
- failed_results: [],
590
- },
591
- });
592
-
593
- const scraper = createTavilyScraper({
594
- apiKey: 'test-key',
595
- logger: mockLogger,
596
- });
597
- await scraper.scrapeUrl('https://example.com', {
598
- includeFavicon: true,
599
- format: 'text',
600
- timeout: 2000,
601
- });
602
- const [, payload, config] = mockedAxios.post.mock.calls[0];
603
-
604
- expect(payload).toMatchObject({
605
- urls: ['https://example.com'],
606
- include_favicon: true,
607
- format: 'text',
608
- timeout: 2,
609
- });
610
- expect(payload).not.toHaveProperty('chunks_per_source');
611
- expect(config).toMatchObject({ timeout: 2000 });
612
- });
613
-
614
- it('omits extract timeout from the payload when using Tavily defaults', async () => {
615
- mockedAxios.post.mockResolvedValueOnce({
616
- data: {
617
- results: [
618
- {
619
- url: 'https://example.com',
620
- raw_content: 'Content',
621
- images: [],
622
- },
623
- ],
624
- failed_results: [],
625
- },
626
- });
627
-
628
- const scraper = createTavilyScraper({
629
- apiKey: 'test-key',
630
- logger: mockLogger,
631
- extractDepth: 'advanced',
632
- });
633
- await scraper.scrapeUrl('https://example.com');
634
- const [, payload, config] = mockedAxios.post.mock.calls[0];
635
-
636
- expect(payload).toMatchObject({
637
- urls: ['https://example.com'],
638
- extract_depth: 'advanced',
639
- });
640
- expect(payload).not.toHaveProperty('timeout');
641
- expect(config).toMatchObject({ timeout: 30000 });
642
- });
643
-
644
- it('uses the advanced default client timeout for per-call extract depth', async () => {
645
- mockedAxios.post.mockResolvedValueOnce({
646
- data: {
647
- results: [
648
- {
649
- url: 'https://example.com',
650
- raw_content: 'Content',
651
- images: [],
652
- },
653
- ],
654
- failed_results: [],
655
- },
656
- });
657
-
658
- const scraper = createTavilyScraper({
659
- apiKey: 'test-key',
660
- logger: mockLogger,
661
- });
662
- await scraper.scrapeUrl('https://example.com', {
663
- extractDepth: 'advanced',
664
- });
665
- const [, payload, config] = mockedAxios.post.mock.calls[0];
666
-
667
- expect(payload).toMatchObject({
668
- urls: ['https://example.com'],
669
- extract_depth: 'advanced',
670
- });
671
- expect(payload).not.toHaveProperty('timeout');
672
- expect(config).toMatchObject({ timeout: 30000 });
673
- });
674
-
675
- it('handles API failure gracefully', async () => {
676
- mockedAxios.post.mockRejectedValueOnce(new Error('Network error'));
677
-
678
- const scraper = createTavilyScraper({
679
- apiKey: 'test-key',
680
- logger: mockLogger,
681
- });
682
- const [url, response] = await scraper.scrapeUrl('https://example.com');
683
-
684
- expect(url).toBe('https://example.com');
685
- expect(response.success).toBe(false);
686
- expect(response.error).toContain('Tavily Extract API request failed');
687
- expect(response.error).toContain('Network error');
688
- });
689
-
690
- it('reads error from failed_results when results is empty', async () => {
691
- mockedAxios.post.mockResolvedValueOnce({
692
- data: {
693
- results: [],
694
- failed_results: [
695
- {
696
- url: 'https://example.com',
697
- error: 'Page is behind a paywall',
698
- },
699
- ],
700
- },
701
- });
702
-
703
- const scraper = createTavilyScraper({
704
- apiKey: 'test-key',
705
- logger: mockLogger,
706
- });
707
- const [url, response] = await scraper.scrapeUrl('https://example.com');
708
-
709
- expect(url).toBe('https://example.com');
710
- expect(response.success).toBe(false);
711
- expect(response.error).toBe('Page is behind a paywall');
712
- });
713
-
714
- it('matches normalized URLs from Tavily Extract responses', async () => {
715
- mockedAxios.post.mockResolvedValueOnce({
716
- data: {
717
- results: [
718
- {
719
- url: 'https://example.com/article',
720
- raw_content: 'Content',
721
- images: [],
722
- },
723
- ],
724
- failed_results: [],
725
- },
726
- });
727
-
728
- const scraper = createTavilyScraper({
729
- apiKey: 'test-key',
730
- logger: mockLogger,
731
- });
732
- const [url, response] = await scraper.scrapeUrl(
733
- 'https://example.com/article/'
734
- );
735
-
736
- expect(url).toBe('https://example.com/article/');
737
- expect(response.success).toBe(true);
738
- expect(response.data?.rawContent).toBe('Content');
739
- });
740
-
741
- it('returns descriptive error when URL not in results or failed_results', async () => {
742
- mockedAxios.post.mockResolvedValueOnce({
743
- data: { results: [], failed_results: [] },
744
- });
745
-
746
- const scraper = createTavilyScraper({
747
- apiKey: 'test-key',
748
- logger: mockLogger,
749
- });
750
- const [, response] = await scraper.scrapeUrl('https://missing.com');
751
-
752
- expect(response.success).toBe(false);
753
- expect(response.error).toBe('URL not found in Tavily Extract response');
754
- });
755
- });
756
-
757
- describe('scrapeUrls (batch)', () => {
758
- it('batches multiple URLs into a single API call', async () => {
759
- const urls = [
760
- 'https://example.com/1',
761
- 'https://example.com/2',
762
- 'https://example.com/3',
763
- ];
764
-
765
- mockedAxios.post.mockResolvedValueOnce({
766
- data: {
767
- results: urls.map((url) => ({
768
- url,
769
- raw_content: `Content for ${url}`,
770
- images: [],
771
- })),
772
- failed_results: [],
773
- },
774
- });
775
-
776
- const scraper = createTavilyScraper({
777
- apiKey: 'test-key',
778
- logger: mockLogger,
779
- });
780
- const results = await scraper.scrapeUrls(urls);
781
-
782
- expect(mockedAxios.post).toHaveBeenCalledTimes(1);
783
- expect(results).toHaveLength(3);
784
-
785
- for (let i = 0; i < results.length; i++) {
786
- const [url, response] = results[i];
787
- expect(url).toBe(urls[i]);
788
- expect(response.success).toBe(true);
789
- expect(response.data?.rawContent).toBe(`Content for ${urls[i]}`);
790
- }
791
- });
792
-
793
- it('handles mixed success and failure results', async () => {
794
- mockedAxios.post.mockResolvedValueOnce({
795
- data: {
796
- results: [
797
- {
798
- url: 'https://example.com/ok',
799
- raw_content: 'Good content',
800
- images: [],
801
- },
802
- ],
803
- failed_results: [
804
- {
805
- url: 'https://example.com/fail',
806
- error: 'Access denied',
807
- },
808
- ],
809
- },
810
- });
811
-
812
- const scraper = createTavilyScraper({
813
- apiKey: 'test-key',
814
- logger: mockLogger,
815
- });
816
- const results = await scraper.scrapeUrls([
817
- 'https://example.com/ok',
818
- 'https://example.com/fail',
819
- ]);
820
-
821
- expect(results).toHaveLength(2);
822
- expect(results[0][1].success).toBe(true);
823
- expect(results[1][1].success).toBe(false);
824
- expect(results[1][1].error).toBe('Access denied');
825
- });
826
-
827
- it('splits large batches into chunks of 20', async () => {
828
- const urls = Array.from(
829
- { length: 25 },
830
- (_, i) => `https://example.com/${i}`
831
- );
832
-
833
- mockedAxios.post
834
- .mockResolvedValueOnce({
835
- data: {
836
- results: urls.slice(0, 20).map((url) => ({
837
- url,
838
- raw_content: 'content',
839
- images: [],
840
- })),
841
- failed_results: [],
842
- },
843
- })
844
- .mockResolvedValueOnce({
845
- data: {
846
- results: urls.slice(20).map((url) => ({
847
- url,
848
- raw_content: 'content',
849
- images: [],
850
- })),
851
- failed_results: [],
852
- },
853
- });
854
-
855
- const scraper = createTavilyScraper({
856
- apiKey: 'test-key',
857
- logger: mockLogger,
858
- });
859
- const results = await scraper.scrapeUrls(urls);
860
-
861
- expect(mockedAxios.post).toHaveBeenCalledTimes(2);
862
- expect(results).toHaveLength(25);
863
- });
864
-
865
- it('returns errors for all URLs when API key is missing', async () => {
866
- const scraper = createTavilyScraper({ apiKey: '', logger: mockLogger });
867
- const results = await scraper.scrapeUrls([
868
- 'https://a.com',
869
- 'https://b.com',
870
- ]);
871
-
872
- expect(results).toHaveLength(2);
873
- for (const [, response] of results) {
874
- expect(response.success).toBe(false);
875
- expect(response.error).toBe('TAVILY_API_KEY is not set');
876
- }
877
- });
878
- });
879
-
880
- describe('extractContent', () => {
881
- it('returns content and image references', () => {
882
- const scraper = createTavilyScraper({
883
- apiKey: 'test-key',
884
- logger: mockLogger,
885
- });
886
- const [content, references] = scraper.extractContent({
887
- success: true,
888
- data: {
889
- rawContent: 'Hello world',
890
- images: ['https://img.example.com/1.png'],
891
- },
892
- });
893
-
894
- expect(content).toBe('Hello world');
895
- expect(references).toBeDefined();
896
- expect(references?.images).toHaveLength(1);
897
- expect(references?.images[0].originalUrl).toBe(
898
- 'https://img.example.com/1.png'
899
- );
900
- });
901
-
902
- it('returns empty content for failed response', () => {
903
- const scraper = createTavilyScraper({
904
- apiKey: 'test-key',
905
- logger: mockLogger,
906
- });
907
- const [content, references] = scraper.extractContent({
908
- success: false,
909
- error: 'Failed',
910
- });
911
-
912
- expect(content).toBe('');
913
- expect(references).toBeUndefined();
914
- });
915
-
916
- it('returns undefined references when no images', () => {
917
- const scraper = createTavilyScraper({
918
- apiKey: 'test-key',
919
- logger: mockLogger,
920
- });
921
- const [content, references] = scraper.extractContent({
922
- success: true,
923
- data: { rawContent: 'No images here', images: [] },
924
- });
925
-
926
- expect(content).toBe('No images here');
927
- expect(references).toBeUndefined();
928
- });
929
- });
930
-
931
- describe('extractMetadata', () => {
932
- it('returns images_count for successful response', () => {
933
- const scraper = createTavilyScraper({
934
- apiKey: 'test-key',
935
- logger: mockLogger,
936
- });
937
- const metadata = scraper.extractMetadata({
938
- success: true,
939
- data: {
940
- rawContent: 'content',
941
- images: ['a', 'b', 'c'],
942
- favicon: 'https://example.com/favicon.ico',
943
- },
944
- });
945
-
946
- expect(metadata).toEqual({
947
- favicon: 'https://example.com/favicon.ico',
948
- images_count: 3,
949
- });
950
- });
951
-
952
- it('returns empty object for failed response', () => {
953
- const scraper = createTavilyScraper({
954
- apiKey: 'test-key',
955
- logger: mockLogger,
956
- });
957
- const metadata = scraper.extractMetadata({
958
- success: false,
959
- error: 'Failed',
960
- });
961
-
962
- expect(metadata).toEqual({});
963
- });
964
- });
965
- });