@illuma-ai/agents 1.3.2 → 1.4.0-alpha.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 (344) hide show
  1. package/dist/cjs/graphs/Graph.cjs +3 -18
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/llm/openai/index.cjs +3 -0
  4. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  5. package/dist/cjs/main.cjs +56 -0
  6. package/dist/cjs/main.cjs.map +1 -1
  7. package/dist/cjs/providers/a2a/A2ACapabilityProvider.cjs +288 -0
  8. package/dist/cjs/providers/a2a/A2ACapabilityProvider.cjs.map +1 -0
  9. package/dist/cjs/providers/a2a/client.cjs +92 -0
  10. package/dist/cjs/providers/a2a/client.cjs.map +1 -0
  11. package/dist/cjs/providers/a2a/config.cjs +38 -0
  12. package/dist/cjs/providers/a2a/config.cjs.map +1 -0
  13. package/dist/cjs/providers/capabilityNaming.cjs +43 -0
  14. package/dist/cjs/providers/capabilityNaming.cjs.map +1 -0
  15. package/dist/cjs/providers/mcp/MCPCapabilityProvider.cjs +244 -0
  16. package/dist/cjs/providers/mcp/MCPCapabilityProvider.cjs.map +1 -0
  17. package/dist/cjs/providers/mcp/config.cjs +42 -0
  18. package/dist/cjs/providers/mcp/config.cjs.map +1 -0
  19. package/dist/cjs/providers/mcp/transport.cjs +65 -0
  20. package/dist/cjs/providers/mcp/transport.cjs.map +1 -0
  21. package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs +121 -0
  22. package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs.map +1 -0
  23. package/dist/cjs/providers/types.cjs +51 -0
  24. package/dist/cjs/providers/types.cjs.map +1 -0
  25. package/dist/cjs/tools/ToolNode.cjs +3 -0
  26. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  27. package/dist/cjs/tools/proxyTool.cjs +100 -0
  28. package/dist/cjs/tools/proxyTool.cjs.map +1 -0
  29. package/dist/cjs/utils/credentials.cjs +142 -0
  30. package/dist/cjs/utils/credentials.cjs.map +1 -0
  31. package/dist/cjs/utils/httpClient.cjs +74 -0
  32. package/dist/cjs/utils/httpClient.cjs.map +1 -0
  33. package/dist/cjs/utils/toolManifest.cjs +100 -0
  34. package/dist/cjs/utils/toolManifest.cjs.map +1 -0
  35. package/dist/esm/graphs/Graph.mjs +3 -18
  36. package/dist/esm/graphs/Graph.mjs.map +1 -1
  37. package/dist/esm/llm/openai/index.mjs +3 -0
  38. package/dist/esm/llm/openai/index.mjs.map +1 -1
  39. package/dist/esm/main.mjs +13 -0
  40. package/dist/esm/main.mjs.map +1 -1
  41. package/dist/esm/providers/a2a/A2ACapabilityProvider.mjs +281 -0
  42. package/dist/esm/providers/a2a/A2ACapabilityProvider.mjs.map +1 -0
  43. package/dist/esm/providers/a2a/client.mjs +88 -0
  44. package/dist/esm/providers/a2a/client.mjs.map +1 -0
  45. package/dist/esm/providers/a2a/config.mjs +35 -0
  46. package/dist/esm/providers/a2a/config.mjs.map +1 -0
  47. package/dist/esm/providers/capabilityNaming.mjs +39 -0
  48. package/dist/esm/providers/capabilityNaming.mjs.map +1 -0
  49. package/dist/esm/providers/mcp/MCPCapabilityProvider.mjs +240 -0
  50. package/dist/esm/providers/mcp/MCPCapabilityProvider.mjs.map +1 -0
  51. package/dist/esm/providers/mcp/config.mjs +39 -0
  52. package/dist/esm/providers/mcp/config.mjs.map +1 -0
  53. package/dist/esm/providers/mcp/transport.mjs +63 -0
  54. package/dist/esm/providers/mcp/transport.mjs.map +1 -0
  55. package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs +119 -0
  56. package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs.map +1 -0
  57. package/dist/esm/providers/types.mjs +51 -0
  58. package/dist/esm/providers/types.mjs.map +1 -0
  59. package/dist/esm/tools/ToolNode.mjs +3 -0
  60. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  61. package/dist/esm/tools/proxyTool.mjs +98 -0
  62. package/dist/esm/tools/proxyTool.mjs.map +1 -0
  63. package/dist/esm/utils/credentials.mjs +135 -0
  64. package/dist/esm/utils/credentials.mjs.map +1 -0
  65. package/dist/esm/utils/httpClient.mjs +70 -0
  66. package/dist/esm/utils/httpClient.mjs.map +1 -0
  67. package/dist/esm/utils/toolManifest.mjs +96 -0
  68. package/dist/esm/utils/toolManifest.mjs.map +1 -0
  69. package/dist/types/index.d.ts +2 -0
  70. package/dist/types/providers/a2a/A2ACapabilityProvider.d.ts +89 -0
  71. package/dist/types/providers/a2a/client.d.ts +47 -0
  72. package/dist/types/providers/a2a/config.d.ts +18 -0
  73. package/dist/types/providers/a2a/index.d.ts +6 -0
  74. package/dist/types/providers/a2a/types.d.ts +173 -0
  75. package/dist/types/providers/capabilityNaming.d.ts +25 -0
  76. package/dist/types/providers/index.d.ts +12 -0
  77. package/dist/types/providers/mcp/MCPCapabilityProvider.d.ts +54 -0
  78. package/dist/types/providers/mcp/config.d.ts +20 -0
  79. package/dist/types/providers/mcp/index.d.ts +5 -0
  80. package/dist/types/providers/mcp/transport.d.ts +18 -0
  81. package/dist/types/providers/mcp/types.d.ts +112 -0
  82. package/dist/types/providers/tools-server/ToolsServerCapabilityProvider.d.ts +45 -0
  83. package/dist/types/providers/tools-server/index.d.ts +1 -0
  84. package/dist/types/providers/types.d.ts +170 -0
  85. package/dist/types/tools/proxyTool.d.ts +55 -0
  86. package/dist/types/utils/credentials.d.ts +77 -0
  87. package/dist/types/utils/httpClient.d.ts +46 -0
  88. package/dist/types/utils/index.d.ts +3 -0
  89. package/dist/types/utils/toolManifest.d.ts +49 -0
  90. package/package.json +16 -1
  91. package/src/graphs/Graph.ts +0 -23
  92. package/src/index.ts +4 -0
  93. package/src/providers/__tests__/ToolsServerCapabilityProvider.integration.spec.ts +79 -0
  94. package/src/providers/__tests__/ToolsServerCapabilityProvider.test.ts +206 -0
  95. package/src/providers/__tests__/types.test.ts +64 -0
  96. package/src/providers/a2a/A2ACapabilityProvider.ts +384 -0
  97. package/src/providers/a2a/__tests__/A2ACapabilityProvider.integration.spec.ts +345 -0
  98. package/src/providers/a2a/__tests__/A2ACapabilityProvider.test.ts +460 -0
  99. package/src/providers/a2a/client.ts +115 -0
  100. package/src/providers/a2a/config.ts +40 -0
  101. package/src/providers/a2a/index.ts +29 -0
  102. package/src/providers/a2a/types.ts +191 -0
  103. package/src/providers/capabilityNaming.ts +42 -0
  104. package/src/providers/index.ts +68 -0
  105. package/src/providers/mcp/MCPCapabilityProvider.ts +345 -0
  106. package/src/providers/mcp/__tests__/MCPCapabilityProvider.integration.spec.ts +386 -0
  107. package/src/providers/mcp/__tests__/MCPCapabilityProvider.test.ts +371 -0
  108. package/src/providers/mcp/config.ts +45 -0
  109. package/src/providers/mcp/index.ts +21 -0
  110. package/src/providers/mcp/transport.ts +76 -0
  111. package/src/providers/mcp/types.ts +139 -0
  112. package/src/providers/tools-server/ToolsServerCapabilityProvider.ts +220 -0
  113. package/src/providers/tools-server/index.ts +1 -0
  114. package/src/providers/types.ts +187 -0
  115. package/src/tools/proxyTool.ts +146 -0
  116. package/src/utils/__tests__/credentials.test.ts +130 -0
  117. package/src/utils/__tests__/httpClient.test.ts +75 -0
  118. package/src/utils/__tests__/toolManifest.test.ts +116 -0
  119. package/src/utils/credentials.ts +157 -0
  120. package/src/utils/httpClient.ts +92 -0
  121. package/src/utils/index.ts +3 -0
  122. package/src/utils/toolManifest.ts +109 -0
  123. package/src/agents/AgentContext.js.map +0 -1
  124. package/src/agents/AgentContext.test.js.map +0 -1
  125. package/src/agents/__tests__/AgentContext.test.js.map +0 -1
  126. package/src/agents/__tests__/resolveStructuredOutputMode.test.js.map +0 -1
  127. package/src/common/enum.js.map +0 -1
  128. package/src/common/index.js.map +0 -1
  129. package/src/events.js.map +0 -1
  130. package/src/graphs/Graph.js.map +0 -1
  131. package/src/graphs/MultiAgentGraph.js.map +0 -1
  132. package/src/graphs/__tests__/structured-output.integration.test.js.map +0 -1
  133. package/src/graphs/__tests__/structured-output.test.js.map +0 -1
  134. package/src/graphs/contextManagement.e2e.test.js.map +0 -1
  135. package/src/graphs/contextManagement.test.js.map +0 -1
  136. package/src/graphs/handoffValidation.test.js.map +0 -1
  137. package/src/graphs/index.js.map +0 -1
  138. package/src/index.js.map +0 -1
  139. package/src/instrumentation.js.map +0 -1
  140. package/src/llm/anthropic/index.js.map +0 -1
  141. package/src/llm/anthropic/types.js.map +0 -1
  142. package/src/llm/anthropic/utils/message_inputs.js.map +0 -1
  143. package/src/llm/anthropic/utils/message_outputs.js.map +0 -1
  144. package/src/llm/anthropic/utils/output_parsers.js.map +0 -1
  145. package/src/llm/anthropic/utils/tools.js.map +0 -1
  146. package/src/llm/bedrock/__tests__/bedrock-caching.test.js.map +0 -1
  147. package/src/llm/bedrock/index.js.map +0 -1
  148. package/src/llm/bedrock/types.js.map +0 -1
  149. package/src/llm/bedrock/utils/index.js.map +0 -1
  150. package/src/llm/bedrock/utils/message_inputs.js.map +0 -1
  151. package/src/llm/bedrock/utils/message_outputs.js.map +0 -1
  152. package/src/llm/fake.js.map +0 -1
  153. package/src/llm/google/index.js.map +0 -1
  154. package/src/llm/google/types.js.map +0 -1
  155. package/src/llm/google/utils/common.js.map +0 -1
  156. package/src/llm/google/utils/tools.js.map +0 -1
  157. package/src/llm/google/utils/zod_to_genai_parameters.js.map +0 -1
  158. package/src/llm/openai/index.js.map +0 -1
  159. package/src/llm/openai/types.js.map +0 -1
  160. package/src/llm/openai/utils/index.js.map +0 -1
  161. package/src/llm/openai/utils/isReasoningModel.test.js.map +0 -1
  162. package/src/llm/openrouter/index.js.map +0 -1
  163. package/src/llm/openrouter/reasoning.test.js.map +0 -1
  164. package/src/llm/providers.js.map +0 -1
  165. package/src/llm/text.js.map +0 -1
  166. package/src/llm/vertexai/index.js.map +0 -1
  167. package/src/messages/__tests__/tools.test.js.map +0 -1
  168. package/src/messages/cache.js.map +0 -1
  169. package/src/messages/cache.test.js.map +0 -1
  170. package/src/messages/content.js.map +0 -1
  171. package/src/messages/content.test.js.map +0 -1
  172. package/src/messages/core.js.map +0 -1
  173. package/src/messages/ensureThinkingBlock.test.js.map +0 -1
  174. package/src/messages/format.js.map +0 -1
  175. package/src/messages/formatAgentMessages.test.js.map +0 -1
  176. package/src/messages/formatAgentMessages.tools.test.js.map +0 -1
  177. package/src/messages/formatMessage.test.js.map +0 -1
  178. package/src/messages/ids.js.map +0 -1
  179. package/src/messages/index.js.map +0 -1
  180. package/src/messages/labelContentByAgent.test.js.map +0 -1
  181. package/src/messages/prune.js.map +0 -1
  182. package/src/messages/reducer.js.map +0 -1
  183. package/src/messages/shiftIndexTokenCountMap.test.js.map +0 -1
  184. package/src/messages/summarize.js.map +0 -1
  185. package/src/messages/summarize.test.js.map +0 -1
  186. package/src/messages/tools.js.map +0 -1
  187. package/src/mockStream.js.map +0 -1
  188. package/src/prompts/collab.js.map +0 -1
  189. package/src/prompts/index.js.map +0 -1
  190. package/src/prompts/taskmanager.js.map +0 -1
  191. package/src/run.js.map +0 -1
  192. package/src/schemas/index.js.map +0 -1
  193. package/src/schemas/schema-preparation.test.js.map +0 -1
  194. package/src/schemas/validate.js.map +0 -1
  195. package/src/schemas/validate.test.js.map +0 -1
  196. package/src/scripts/abort.js.map +0 -1
  197. package/src/scripts/ant_web_search.js.map +0 -1
  198. package/src/scripts/ant_web_search_edge_case.js.map +0 -1
  199. package/src/scripts/ant_web_search_error_edge_case.js.map +0 -1
  200. package/src/scripts/args.js.map +0 -1
  201. package/src/scripts/bedrock-cache-debug.js.map +0 -1
  202. package/src/scripts/bedrock-content-aggregation-test.js.map +0 -1
  203. package/src/scripts/bedrock-merge-test.js.map +0 -1
  204. package/src/scripts/bedrock-parallel-tools-test.js.map +0 -1
  205. package/src/scripts/caching.js.map +0 -1
  206. package/src/scripts/cli.js.map +0 -1
  207. package/src/scripts/cli2.js.map +0 -1
  208. package/src/scripts/cli3.js.map +0 -1
  209. package/src/scripts/cli4.js.map +0 -1
  210. package/src/scripts/cli5.js.map +0 -1
  211. package/src/scripts/code_exec.js.map +0 -1
  212. package/src/scripts/code_exec_files.js.map +0 -1
  213. package/src/scripts/code_exec_multi_session.js.map +0 -1
  214. package/src/scripts/code_exec_ptc.js.map +0 -1
  215. package/src/scripts/code_exec_session.js.map +0 -1
  216. package/src/scripts/code_exec_simple.js.map +0 -1
  217. package/src/scripts/content.js.map +0 -1
  218. package/src/scripts/empty_input.js.map +0 -1
  219. package/src/scripts/handoff-test.js.map +0 -1
  220. package/src/scripts/image.js.map +0 -1
  221. package/src/scripts/memory.js.map +0 -1
  222. package/src/scripts/multi-agent-chain.js.map +0 -1
  223. package/src/scripts/multi-agent-conditional.js.map +0 -1
  224. package/src/scripts/multi-agent-document-review-chain.js.map +0 -1
  225. package/src/scripts/multi-agent-hybrid-flow.js.map +0 -1
  226. package/src/scripts/multi-agent-parallel-start.js.map +0 -1
  227. package/src/scripts/multi-agent-parallel.js.map +0 -1
  228. package/src/scripts/multi-agent-sequence.js.map +0 -1
  229. package/src/scripts/multi-agent-supervisor.js.map +0 -1
  230. package/src/scripts/multi-agent-test.js.map +0 -1
  231. package/src/scripts/parallel-asymmetric-tools-test.js.map +0 -1
  232. package/src/scripts/parallel-full-metadata-test.js.map +0 -1
  233. package/src/scripts/parallel-tools-test.js.map +0 -1
  234. package/src/scripts/programmatic_exec.js.map +0 -1
  235. package/src/scripts/programmatic_exec_agent.js.map +0 -1
  236. package/src/scripts/search.js.map +0 -1
  237. package/src/scripts/sequential-full-metadata-test.js.map +0 -1
  238. package/src/scripts/simple.js.map +0 -1
  239. package/src/scripts/single-agent-metadata-test.js.map +0 -1
  240. package/src/scripts/stream.js.map +0 -1
  241. package/src/scripts/test-custom-prompt-key.js.map +0 -1
  242. package/src/scripts/test-handoff-input.js.map +0 -1
  243. package/src/scripts/test-handoff-preamble.js.map +0 -1
  244. package/src/scripts/test-handoff-steering.js.map +0 -1
  245. package/src/scripts/test-multi-agent-list-handoff.js.map +0 -1
  246. package/src/scripts/test-parallel-agent-labeling.js.map +0 -1
  247. package/src/scripts/test-parallel-handoffs.js.map +0 -1
  248. package/src/scripts/test-thinking-handoff-bedrock.js.map +0 -1
  249. package/src/scripts/test-thinking-handoff.js.map +0 -1
  250. package/src/scripts/test-thinking-to-thinking-handoff-bedrock.js.map +0 -1
  251. package/src/scripts/test-tool-before-handoff-role-order.js.map +0 -1
  252. package/src/scripts/test-tools-before-handoff.js.map +0 -1
  253. package/src/scripts/test_code_api.js.map +0 -1
  254. package/src/scripts/thinking-bedrock.js.map +0 -1
  255. package/src/scripts/thinking-vertexai.js.map +0 -1
  256. package/src/scripts/thinking.js.map +0 -1
  257. package/src/scripts/tool_search.js.map +0 -1
  258. package/src/scripts/tools.js.map +0 -1
  259. package/src/specs/agent-handoffs-bedrock.integration.test.js.map +0 -1
  260. package/src/specs/agent-handoffs.test.js.map +0 -1
  261. package/src/specs/anthropic.simple.test.js.map +0 -1
  262. package/src/specs/azure.simple.test.js.map +0 -1
  263. package/src/specs/cache.simple.test.js.map +0 -1
  264. package/src/specs/custom-event-await.test.js.map +0 -1
  265. package/src/specs/deepseek.simple.test.js.map +0 -1
  266. package/src/specs/emergency-prune.test.js.map +0 -1
  267. package/src/specs/moonshot.simple.test.js.map +0 -1
  268. package/src/specs/observability.integration.test.js.map +0 -1
  269. package/src/specs/openai.simple.test.js.map +0 -1
  270. package/src/specs/openrouter.simple.test.js.map +0 -1
  271. package/src/specs/prune.test.js.map +0 -1
  272. package/src/specs/reasoning.test.js.map +0 -1
  273. package/src/specs/spec.utils.js.map +0 -1
  274. package/src/specs/thinking-handoff.test.js.map +0 -1
  275. package/src/specs/thinking-prune.test.js.map +0 -1
  276. package/src/specs/token-distribution-edge-case.test.js.map +0 -1
  277. package/src/specs/token-memoization.test.js.map +0 -1
  278. package/src/specs/tokens.test.js.map +0 -1
  279. package/src/specs/tool-error.test.js.map +0 -1
  280. package/src/splitStream.js.map +0 -1
  281. package/src/splitStream.test.js.map +0 -1
  282. package/src/stream.js.map +0 -1
  283. package/src/stream.test.js.map +0 -1
  284. package/src/test/mockTools.js.map +0 -1
  285. package/src/tools/BrowserTools.js.map +0 -1
  286. package/src/tools/Calculator.js.map +0 -1
  287. package/src/tools/Calculator.test.js.map +0 -1
  288. package/src/tools/CodeExecutor.js.map +0 -1
  289. package/src/tools/ProgrammaticToolCalling.js.map +0 -1
  290. package/src/tools/StreamingToolCallBuffer.js.map +0 -1
  291. package/src/tools/ToolNode.js.map +0 -1
  292. package/src/tools/ToolSearch.js.map +0 -1
  293. package/src/tools/__tests__/BrowserTools.test.js.map +0 -1
  294. package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.js.map +0 -1
  295. package/src/tools/__tests__/ProgrammaticToolCalling.test.js.map +0 -1
  296. package/src/tools/__tests__/StreamingToolCallBuffer.test.js.map +0 -1
  297. package/src/tools/__tests__/ToolApproval.test.js.map +0 -1
  298. package/src/tools/__tests__/ToolNode.recovery.test.js.map +0 -1
  299. package/src/tools/__tests__/ToolNode.session.test.js.map +0 -1
  300. package/src/tools/__tests__/ToolSearch.integration.test.js.map +0 -1
  301. package/src/tools/__tests__/ToolSearch.test.js.map +0 -1
  302. package/src/tools/__tests__/handlers.test.js.map +0 -1
  303. package/src/tools/__tests__/truncation-recovery.integration.test.js.map +0 -1
  304. package/src/tools/handlers.js.map +0 -1
  305. package/src/tools/schema.js.map +0 -1
  306. package/src/tools/search/anthropic.js.map +0 -1
  307. package/src/tools/search/content.js.map +0 -1
  308. package/src/tools/search/content.test.js.map +0 -1
  309. package/src/tools/search/firecrawl.js.map +0 -1
  310. package/src/tools/search/format.js.map +0 -1
  311. package/src/tools/search/highlights.js.map +0 -1
  312. package/src/tools/search/index.js.map +0 -1
  313. package/src/tools/search/jina-reranker.test.js.map +0 -1
  314. package/src/tools/search/rerankers.js.map +0 -1
  315. package/src/tools/search/schema.js.map +0 -1
  316. package/src/tools/search/search.js.map +0 -1
  317. package/src/tools/search/serper-scraper.js.map +0 -1
  318. package/src/tools/search/test.js.map +0 -1
  319. package/src/tools/search/tool.js.map +0 -1
  320. package/src/tools/search/types.js.map +0 -1
  321. package/src/tools/search/utils.js.map +0 -1
  322. package/src/types/graph.js.map +0 -1
  323. package/src/types/graph.test.js.map +0 -1
  324. package/src/types/index.js.map +0 -1
  325. package/src/types/llm.js.map +0 -1
  326. package/src/types/messages.js.map +0 -1
  327. package/src/types/run.js.map +0 -1
  328. package/src/types/stream.js.map +0 -1
  329. package/src/types/tools.js.map +0 -1
  330. package/src/utils/contextAnalytics.js.map +0 -1
  331. package/src/utils/contextAnalytics.test.js.map +0 -1
  332. package/src/utils/events.js.map +0 -1
  333. package/src/utils/graph.js.map +0 -1
  334. package/src/utils/handlers.js.map +0 -1
  335. package/src/utils/index.js.map +0 -1
  336. package/src/utils/llm.js.map +0 -1
  337. package/src/utils/llmConfig.js.map +0 -1
  338. package/src/utils/logging.js.map +0 -1
  339. package/src/utils/misc.js.map +0 -1
  340. package/src/utils/run.js.map +0 -1
  341. package/src/utils/schema.js.map +0 -1
  342. package/src/utils/title.js.map +0 -1
  343. package/src/utils/tokens.js.map +0 -1
  344. package/src/utils/toonFormat.js.map +0 -1
@@ -0,0 +1,460 @@
1
+ /**
2
+ * Unit tests for A2ACapabilityProvider. Uses a mocked A2AClient so the
3
+ * provider can be exercised without spinning up a real remote agent.
4
+ * The integration test file covers the live HTTP path.
5
+ */
6
+
7
+ import {
8
+ A2ACapabilityProvider,
9
+ coerceInputToA2AMessage,
10
+ formatCapabilityName,
11
+ parseCapabilityName,
12
+ skillToCapability,
13
+ } from '../A2ACapabilityProvider';
14
+ import { extractTaskText } from '../client';
15
+ import { CapabilityKind } from '../../types';
16
+ import type { A2AAgentCard, A2ATask } from '../types';
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // A2AClient mock — replaced via jest.mock of ./client
20
+ // ---------------------------------------------------------------------------
21
+
22
+ const clientFetchCard = jest.fn();
23
+ const clientSendTask = jest.fn();
24
+
25
+ jest.mock('../client', () => {
26
+ const actual = jest.requireActual('../client');
27
+ return {
28
+ ...actual,
29
+ A2AClient: jest.fn().mockImplementation(() => ({
30
+ fetchAgentCard: clientFetchCard,
31
+ sendTask: clientSendTask,
32
+ })),
33
+ };
34
+ });
35
+
36
+ const resetMocks = (): void => {
37
+ clientFetchCard.mockReset();
38
+ clientSendTask.mockReset();
39
+ };
40
+
41
+ beforeEach(resetMocks);
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Fixtures
45
+ // ---------------------------------------------------------------------------
46
+
47
+ const makeCard = (
48
+ name: string,
49
+ skills: Array<{
50
+ id: string;
51
+ name: string;
52
+ description: string;
53
+ tags?: string[];
54
+ }> = []
55
+ ): A2AAgentCard => ({
56
+ name,
57
+ description: `${name} description`,
58
+ url: `http://${name}/`,
59
+ version: '1.0.0',
60
+ capabilities: { streaming: true },
61
+ defaultInputModes: ['text/plain'],
62
+ defaultOutputModes: ['text/plain'],
63
+ skills,
64
+ });
65
+
66
+ const makeCompletedTask = (text: string): A2ATask => ({
67
+ id: 'task-1',
68
+ status: { state: 'completed', timestamp: new Date().toISOString() },
69
+ artifacts: [{ parts: [{ type: 'text', text }] }],
70
+ });
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Helper tests
74
+ // ---------------------------------------------------------------------------
75
+
76
+ describe('helpers', () => {
77
+ describe('formatCapabilityName / parseCapabilityName', () => {
78
+ it('joins and splits server + skill', () => {
79
+ const name = formatCapabilityName('research', 'search');
80
+ expect(name).toBe('research__search');
81
+ const parsed = parseCapabilityName(name);
82
+ expect(parsed).toEqual({ remoteName: 'research', skillId: 'search' });
83
+ });
84
+
85
+ it('handles no-skill case (flattened agents)', () => {
86
+ const name = formatCapabilityName('research');
87
+ expect(name).toBe('research');
88
+ const parsed = parseCapabilityName(name);
89
+ expect(parsed).toEqual({ remoteName: 'research' });
90
+ });
91
+
92
+ it('parseCapabilityName returns null on empty input', () => {
93
+ expect(parseCapabilityName('')).toBeNull();
94
+ });
95
+ });
96
+
97
+ describe('skillToCapability', () => {
98
+ it('produces an A2A Capability with metadata tags', () => {
99
+ const cap = skillToCapability('research', {
100
+ id: 'web_search',
101
+ name: 'Web Search',
102
+ description: 'Search the web',
103
+ tags: ['search'],
104
+ });
105
+ expect(cap.kind).toBe(CapabilityKind.A2A);
106
+ expect(cap.name).toBe('research__web_search');
107
+ expect(cap.description).toBe('Search the web');
108
+ expect(cap.metadata.tags).toEqual(['research', 'search']);
109
+ });
110
+ });
111
+
112
+ describe('coerceInputToA2AMessage', () => {
113
+ it('wraps a string directly', () => {
114
+ const msg = coerceInputToA2AMessage('hello');
115
+ expect(msg).toEqual({
116
+ role: 'user',
117
+ parts: [{ type: 'text', text: 'hello' }],
118
+ });
119
+ });
120
+
121
+ it('unwraps a { message } object', () => {
122
+ const msg = coerceInputToA2AMessage({ message: 'hi' });
123
+ expect(msg.parts[0].text).toBe('hi');
124
+ });
125
+
126
+ it('prepends skill marker when skillId is provided', () => {
127
+ const msg = coerceInputToA2AMessage('go', 'search');
128
+ expect(msg.parts[0].text).toBe('[skill: search]\ngo');
129
+ });
130
+
131
+ it('JSON-stringifies other shapes', () => {
132
+ const msg = coerceInputToA2AMessage({ foo: 'bar' });
133
+ expect(msg.parts[0].text).toBe('{"foo":"bar"}');
134
+ });
135
+ });
136
+
137
+ describe('extractTaskText', () => {
138
+ it('joins all text parts from completed artifacts', () => {
139
+ const text = extractTaskText({
140
+ id: 'x',
141
+ status: { state: 'completed', timestamp: 't' },
142
+ artifacts: [
143
+ { parts: [{ type: 'text', text: 'a' }] },
144
+ { parts: [{ type: 'text', text: 'b' }] },
145
+ ],
146
+ });
147
+ expect(text).toBe('a\n\nb');
148
+ });
149
+
150
+ it('throws when status is failed', () => {
151
+ expect(() =>
152
+ extractTaskText({
153
+ id: 'x',
154
+ status: { state: 'failed', timestamp: 't' },
155
+ error: { code: 1, message: 'boom' },
156
+ })
157
+ ).toThrow(/boom/);
158
+ });
159
+
160
+ it('throws when status is not completed', () => {
161
+ expect(() =>
162
+ extractTaskText({
163
+ id: 'x',
164
+ status: { state: 'working', timestamp: 't' },
165
+ })
166
+ ).toThrow(/not completed/);
167
+ });
168
+ });
169
+ });
170
+
171
+ // ---------------------------------------------------------------------------
172
+ // Construction
173
+ // ---------------------------------------------------------------------------
174
+
175
+ describe('A2ACapabilityProvider construction', () => {
176
+ it('rejects empty remotes map', () => {
177
+ expect(() => new A2ACapabilityProvider({ remotes: {} })).toThrow(
178
+ /at least one remote/
179
+ );
180
+ });
181
+
182
+ it('providerId includes sorted remote names', () => {
183
+ const p = new A2ACapabilityProvider({
184
+ remotes: {
185
+ zeta: { url: 'http://z/' },
186
+ alpha: { url: 'http://a/' },
187
+ },
188
+ });
189
+ expect(p.providerId).toBe('a2a:alpha,zeta');
190
+ });
191
+
192
+ it('reads client identity from env when not provided', () => {
193
+ const prev = process.env.A2A_CLIENT_NAME;
194
+ process.env.A2A_CLIENT_NAME = 'test-client';
195
+ try {
196
+ const p = new A2ACapabilityProvider({
197
+ remotes: { r: { url: 'http://r/' } },
198
+ });
199
+ expect(p.providerId).toContain('r');
200
+ } finally {
201
+ if (prev === undefined) delete process.env.A2A_CLIENT_NAME;
202
+ else process.env.A2A_CLIENT_NAME = prev;
203
+ }
204
+ });
205
+ });
206
+
207
+ // ---------------------------------------------------------------------------
208
+ // fetchManifest
209
+ // ---------------------------------------------------------------------------
210
+
211
+ describe('fetchManifest', () => {
212
+ it('returns one Capability per skill by default', async () => {
213
+ clientFetchCard.mockResolvedValue(
214
+ makeCard('research', [
215
+ { id: 'web_search', name: 'Web', description: 'Search the web' },
216
+ { id: 'summarize', name: 'Summarize', description: 'Summarize text' },
217
+ ])
218
+ );
219
+
220
+ const p = new A2ACapabilityProvider({
221
+ remotes: { research: { url: 'http://r/' } },
222
+ });
223
+ const caps = await p.fetchManifest();
224
+
225
+ expect(caps).toHaveLength(2);
226
+ expect(caps[0].kind).toBe(CapabilityKind.A2A);
227
+ expect(caps[0].name).toBe('research__web_search');
228
+ expect(caps[1].name).toBe('research__summarize');
229
+ });
230
+
231
+ it('returns one Capability per remote when flattenAsSingleTool', async () => {
232
+ clientFetchCard.mockResolvedValue(
233
+ makeCard('research', [
234
+ { id: 'web_search', name: 'Web', description: 'Search' },
235
+ ])
236
+ );
237
+
238
+ const p = new A2ACapabilityProvider({
239
+ remotes: { research: { url: 'http://r/', flattenAsSingleTool: true } },
240
+ });
241
+ const caps = await p.fetchManifest();
242
+
243
+ expect(caps).toHaveLength(1);
244
+ expect(caps[0].name).toBe('research');
245
+ expect(caps[0].metadata.tags).toContain('web_search');
246
+ });
247
+
248
+ it('exposes a single free-form capability when the card has no skills', async () => {
249
+ clientFetchCard.mockResolvedValue(makeCard('bare'));
250
+
251
+ const p = new A2ACapabilityProvider({
252
+ remotes: { bare: { url: 'http://b/' } },
253
+ });
254
+ const caps = await p.fetchManifest();
255
+
256
+ expect(caps).toHaveLength(1);
257
+ expect(caps[0].name).toBe('bare');
258
+ });
259
+
260
+ it('merges capabilities from multiple remotes', async () => {
261
+ clientFetchCard
262
+ .mockResolvedValueOnce(
263
+ makeCard('a', [{ id: 's1', name: 'S1', description: 'd' }])
264
+ )
265
+ .mockResolvedValueOnce(
266
+ makeCard('b', [{ id: 's2', name: 'S2', description: 'd' }])
267
+ );
268
+
269
+ const p = new A2ACapabilityProvider({
270
+ remotes: {
271
+ a: { url: 'http://a/' },
272
+ b: { url: 'http://b/' },
273
+ },
274
+ });
275
+ const caps = await p.fetchManifest();
276
+ expect(caps.map((c) => c.name).sort()).toEqual(['a__s1', 'b__s2']);
277
+ });
278
+
279
+ it('continues when one remote fails', async () => {
280
+ clientFetchCard
281
+ .mockRejectedValueOnce(new Error('boom'))
282
+ .mockResolvedValueOnce(
283
+ makeCard('ok', [{ id: 'go', name: 'Go', description: 'g' }])
284
+ );
285
+
286
+ const p = new A2ACapabilityProvider({
287
+ remotes: {
288
+ broken: { url: 'http://x/' },
289
+ ok: { url: 'http://ok/' },
290
+ },
291
+ });
292
+ const caps = await p.fetchManifest();
293
+ expect(caps).toHaveLength(1);
294
+ expect(caps[0].name).toBe('ok__go');
295
+ });
296
+
297
+ it('short-circuits when filter excludes a2a kind', async () => {
298
+ const p = new A2ACapabilityProvider({
299
+ remotes: { r: { url: 'http://r/' } },
300
+ });
301
+ const caps = await p.fetchManifest({ kind: CapabilityKind.TOOL });
302
+ expect(caps).toEqual([]);
303
+ expect(clientFetchCard).not.toHaveBeenCalled();
304
+ });
305
+
306
+ it('applies name filter', async () => {
307
+ clientFetchCard.mockResolvedValue(
308
+ makeCard('r', [
309
+ { id: 's1', name: 'S1', description: 'd' },
310
+ { id: 's2', name: 'S2', description: 'd' },
311
+ ])
312
+ );
313
+
314
+ const p = new A2ACapabilityProvider({
315
+ remotes: { r: { url: 'http://r/' } },
316
+ });
317
+ const caps = await p.fetchManifest({ names: ['r__s2'] });
318
+ expect(caps).toHaveLength(1);
319
+ expect(caps[0].name).toBe('r__s2');
320
+ });
321
+
322
+ it('calls getAuthHeaders once per remote', async () => {
323
+ clientFetchCard.mockResolvedValue(makeCard('r'));
324
+ const getAuthHeaders = jest
325
+ .fn()
326
+ .mockResolvedValue({ Authorization: 'Bearer x' });
327
+
328
+ const p = new A2ACapabilityProvider({
329
+ remotes: { r: { url: 'http://r/' } },
330
+ getAuthHeaders,
331
+ });
332
+ await p.fetchManifest();
333
+
334
+ expect(getAuthHeaders).toHaveBeenCalledTimes(1);
335
+ expect(getAuthHeaders).toHaveBeenCalledWith('r');
336
+ });
337
+
338
+ it('caches the card fetch across calls', async () => {
339
+ clientFetchCard.mockResolvedValue(makeCard('r'));
340
+ const p = new A2ACapabilityProvider({
341
+ remotes: { r: { url: 'http://r/' } },
342
+ });
343
+ await p.fetchManifest();
344
+ await p.fetchManifest();
345
+ expect(clientFetchCard).toHaveBeenCalledTimes(1);
346
+ });
347
+ });
348
+
349
+ // ---------------------------------------------------------------------------
350
+ // createRunnables
351
+ // ---------------------------------------------------------------------------
352
+
353
+ describe('createRunnables', () => {
354
+ it('builds one StructuredTool per A2A capability', async () => {
355
+ clientFetchCard.mockResolvedValue(
356
+ makeCard('r', [
357
+ { id: 's1', name: 'S1', description: 'd' },
358
+ { id: 's2', name: 'S2', description: 'd' },
359
+ ])
360
+ );
361
+
362
+ const p = new A2ACapabilityProvider({
363
+ remotes: { r: { url: 'http://r/' } },
364
+ });
365
+ const caps = await p.fetchManifest();
366
+ const runnables = await p.createRunnables(caps, {});
367
+ expect(runnables).toHaveLength(2);
368
+ expect(runnables[0].name).toBe('r__s1');
369
+ });
370
+
371
+ it('skips non-A2A capabilities', async () => {
372
+ const p = new A2ACapabilityProvider({
373
+ remotes: { r: { url: 'http://r/' } },
374
+ });
375
+ const runnables = await p.createRunnables(
376
+ [
377
+ {
378
+ kind: CapabilityKind.TOOL,
379
+ name: 'other',
380
+ description: 'other',
381
+ authConfig: [],
382
+ metadata: {},
383
+ },
384
+ ],
385
+ {}
386
+ );
387
+ expect(runnables).toHaveLength(0);
388
+ });
389
+
390
+ it('proxy tool sends a task to the remote and returns its text', async () => {
391
+ clientFetchCard.mockResolvedValue(
392
+ makeCard('r', [{ id: 'echo', name: 'Echo', description: 'Echo' }])
393
+ );
394
+ clientSendTask.mockResolvedValue(makeCompletedTask('received: hi'));
395
+
396
+ const p = new A2ACapabilityProvider({
397
+ remotes: { r: { url: 'http://r/' } },
398
+ });
399
+ const caps = await p.fetchManifest();
400
+ const [runnable] = await p.createRunnables(caps, {});
401
+ const result = await runnable.invoke({ message: 'hi' });
402
+
403
+ expect(result).toBe('received: hi');
404
+ expect(clientSendTask).toHaveBeenCalledTimes(1);
405
+ const sentParams = clientSendTask.mock.calls[0][0];
406
+ expect(sentParams.message.parts[0].text).toContain('[skill: echo]');
407
+ expect(sentParams.message.parts[0].text).toContain('hi');
408
+ });
409
+
410
+ it('proxy tool throws when task fails', async () => {
411
+ clientFetchCard.mockResolvedValue(
412
+ makeCard('r', [{ id: 'fail', name: 'F', description: 'F' }])
413
+ );
414
+ clientSendTask.mockResolvedValue({
415
+ id: 't',
416
+ status: { state: 'failed', timestamp: 't' },
417
+ error: { code: 500, message: 'backend exploded' },
418
+ });
419
+
420
+ const p = new A2ACapabilityProvider({
421
+ remotes: { r: { url: 'http://r/' } },
422
+ });
423
+ const [runnable] = await p.createRunnables(await p.fetchManifest(), {});
424
+ await expect(runnable.invoke({ message: 'hi' })).rejects.toThrow(
425
+ /backend exploded/
426
+ );
427
+ });
428
+
429
+ it('proxy tool coerces string input into an A2A message', async () => {
430
+ clientFetchCard.mockResolvedValue(
431
+ makeCard('r', [{ id: 'echo', name: 'Echo', description: 'd' }])
432
+ );
433
+ clientSendTask.mockResolvedValue(makeCompletedTask('ok'));
434
+
435
+ const p = new A2ACapabilityProvider({
436
+ remotes: { r: { url: 'http://r/' } },
437
+ });
438
+ const [runnable] = await p.createRunnables(await p.fetchManifest(), {});
439
+ const result = await runnable.invoke({ message: 'plain string' });
440
+ expect(result).toBe('ok');
441
+ });
442
+ });
443
+
444
+ // ---------------------------------------------------------------------------
445
+ // close
446
+ // ---------------------------------------------------------------------------
447
+
448
+ describe('close', () => {
449
+ it('clears cached connections', async () => {
450
+ clientFetchCard.mockResolvedValue(makeCard('r'));
451
+ const p = new A2ACapabilityProvider({
452
+ remotes: { r: { url: 'http://r/' } },
453
+ });
454
+ await p.fetchManifest();
455
+ await p.close();
456
+ // Next fetchManifest should re-fetch the card.
457
+ await p.fetchManifest();
458
+ expect(clientFetchCard).toHaveBeenCalledTimes(2);
459
+ });
460
+ });
@@ -0,0 +1,115 @@
1
+ /**
2
+ * A2A HTTP client — thin wrappers for the two operations the provider
3
+ * needs: fetching the agent card and sending JSON-RPC task requests.
4
+ *
5
+ * Uses axios (already in deps) via the shared httpClient utility so
6
+ * proxy config + headers work consistently with the tools-server provider.
7
+ */
8
+
9
+ import { createHttpClient, assertOk } from '@/utils/httpClient';
10
+ import type {
11
+ A2AAgentCard,
12
+ A2ATask,
13
+ A2ATaskParams,
14
+ JsonRpcRequest,
15
+ JsonRpcResponse,
16
+ } from './types';
17
+
18
+ const DEFAULT_CARD_PATH = '/.well-known/agent.json';
19
+ const DEFAULT_RPC_PATH = '/';
20
+ const JSON_RPC_VERSION = '2.0';
21
+
22
+ /** Options for constructing a single-remote A2A client. */
23
+ export interface A2AClientOptions {
24
+ baseUrl: string;
25
+ headers?: Record<string, string>;
26
+ timeoutMs?: number;
27
+ cardPath?: string;
28
+ rpcPath?: string;
29
+ }
30
+
31
+ /**
32
+ * Lightweight single-remote A2A client. One instance per remote; the
33
+ * provider builds one per spec entry and caches them.
34
+ */
35
+ export class A2AClient {
36
+ private readonly http: ReturnType<typeof createHttpClient>;
37
+ private readonly cardPath: string;
38
+ private readonly rpcPath: string;
39
+
40
+ constructor(opts: A2AClientOptions) {
41
+ this.http = createHttpClient({
42
+ baseURL: opts.baseUrl,
43
+ headers: opts.headers,
44
+ timeoutMs: opts.timeoutMs,
45
+ });
46
+ this.cardPath = opts.cardPath ?? DEFAULT_CARD_PATH;
47
+ this.rpcPath = opts.rpcPath ?? DEFAULT_RPC_PATH;
48
+ }
49
+
50
+ /** GET /.well-known/agent.json (or the configured override). */
51
+ async fetchAgentCard(): Promise<A2AAgentCard> {
52
+ const res = await this.http.get<A2AAgentCard>(this.cardPath);
53
+ assertOk(res.status, this.cardPath, res.data);
54
+ return res.data;
55
+ }
56
+
57
+ /**
58
+ * POST a JSON-RPC `tasks/send` request and return the resulting Task.
59
+ * The caller supplies the params (id, sessionId, message); this helper
60
+ * wraps it in the JSON-RPC envelope, dispatches, and unwraps.
61
+ */
62
+ async sendTask(params: A2ATaskParams): Promise<A2ATask> {
63
+ const result = await this.rpc<A2ATaskParams, A2ATask>('tasks/send', params);
64
+ return result;
65
+ }
66
+
67
+ /** Low-level JSON-RPC dispatcher — exposed for tests / advanced callers. */
68
+ async rpc<P, R>(method: string, params: P): Promise<R> {
69
+ const body: JsonRpcRequest<P> = {
70
+ jsonrpc: JSON_RPC_VERSION,
71
+ id: generateRpcId(),
72
+ method,
73
+ params,
74
+ };
75
+ const res = await this.http.post<JsonRpcResponse<R>>(this.rpcPath, body);
76
+ assertOk(res.status, this.rpcPath, res.data);
77
+
78
+ const data = res.data;
79
+ if (data && 'error' in data && data.error) {
80
+ throw new Error(
81
+ `A2A ${method} error: ${data.error.message} (code ${data.error.code})`
82
+ );
83
+ }
84
+ if (data && 'result' in data) return data.result;
85
+ throw new Error(`A2A ${method}: malformed response (no result)`);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Generate a short opaque id for JSON-RPC requests. Not cryptographic —
91
+ * just unique enough to pair request/response in logs.
92
+ */
93
+ export function generateRpcId(): string {
94
+ return `a2a-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
95
+ }
96
+
97
+ /**
98
+ * Extract the plain-text output from a completed Task's artifacts. If
99
+ * the task isn't `completed`, throws. If there are no text parts, returns
100
+ * an empty string rather than null so callers always get a string.
101
+ */
102
+ export function extractTaskText(task: A2ATask): string {
103
+ if (task.status.state === 'failed') {
104
+ throw new Error(
105
+ `A2A task ${task.id} failed: ${task.error?.message ?? 'unknown error'}`
106
+ );
107
+ }
108
+ if (task.status.state !== 'completed') {
109
+ throw new Error(
110
+ `A2A task ${task.id} not completed (state=${task.status.state})`
111
+ );
112
+ }
113
+ const parts = task.artifacts?.flatMap((a) => a.parts) ?? [];
114
+ return parts.map((p) => p.text ?? '').join('\n\n');
115
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Environment-driven defaults + default logger for the A2A provider.
3
+ *
4
+ * Mirrors the MCP provider's config.ts so the two providers have the same
5
+ * override story for hosts.
6
+ */
7
+
8
+ import type { A2ALogger } from './types';
9
+
10
+ /**
11
+ * Read env values with fallbacks. Evaluated lazily so tests that mutate
12
+ * process.env get the current value.
13
+ */
14
+ export function getA2AEnvDefaults(): {
15
+ clientName: string;
16
+ clientVersion: string;
17
+ timeoutMs: number;
18
+ } {
19
+ return {
20
+ /**
21
+ * Identity sent to A2A-served remotes in user-agent / request headers.
22
+ * Precedence: constructor config → env var → library default.
23
+ */
24
+ clientName: process.env.A2A_CLIENT_NAME ?? 'illuma-agents',
25
+ clientVersion: process.env.A2A_CLIENT_VERSION ?? '1.0.0',
26
+ timeoutMs: Number(process.env.A2A_REQUEST_TIMEOUT_MS ?? 30_000),
27
+ };
28
+ }
29
+
30
+ /** Console-routed default logger. */
31
+ export const consoleLogger: A2ALogger = {
32
+ // eslint-disable-next-line no-console
33
+ debug: (...args) => console.debug('[a2a]', ...args),
34
+ // eslint-disable-next-line no-console
35
+ info: (...args) => console.info('[a2a]', ...args),
36
+ // eslint-disable-next-line no-console
37
+ warn: (...args) => console.warn('[a2a]', ...args),
38
+ // eslint-disable-next-line no-console
39
+ error: (...args) => console.error('[a2a]', ...args),
40
+ };
@@ -0,0 +1,29 @@
1
+ export { A2ACapabilityProvider } from './A2ACapabilityProvider';
2
+ export {
3
+ CAPABILITY_NAME_SEPARATOR,
4
+ formatCapabilityName,
5
+ parseCapabilityName,
6
+ skillToCapability,
7
+ coerceInputToA2AMessage,
8
+ MESSAGE_INPUT_SCHEMA,
9
+ } from './A2ACapabilityProvider';
10
+ export { A2AClient, extractTaskText, generateRpcId } from './client';
11
+ export type { A2AClientOptions } from './client';
12
+ export { consoleLogger as a2aConsoleLogger, getA2AEnvDefaults } from './config';
13
+ export type {
14
+ A2AAgentCard,
15
+ A2ACardCapabilities,
16
+ A2ALogger,
17
+ A2AMessage,
18
+ A2AProviderConfig,
19
+ A2ARemoteSpec,
20
+ A2ASkill,
21
+ A2ATask,
22
+ A2ATaskParams,
23
+ A2ATaskState,
24
+ A2ATaskStatus,
25
+ JsonRpcRequest,
26
+ JsonRpcResponse,
27
+ JsonRpcSuccess,
28
+ JsonRpcErrorResponse,
29
+ } from './types';