@illuma-ai/agents 1.3.1 → 1.4.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/graphs/Graph.cjs +3 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +3 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +58 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/providers/a2a/A2ACapabilityProvider.cjs +288 -0
- package/dist/cjs/providers/a2a/A2ACapabilityProvider.cjs.map +1 -0
- package/dist/cjs/providers/a2a/client.cjs +92 -0
- package/dist/cjs/providers/a2a/client.cjs.map +1 -0
- package/dist/cjs/providers/a2a/config.cjs +38 -0
- package/dist/cjs/providers/a2a/config.cjs.map +1 -0
- package/dist/cjs/providers/capabilityNaming.cjs +43 -0
- package/dist/cjs/providers/capabilityNaming.cjs.map +1 -0
- package/dist/cjs/providers/composite/CompositeCapabilityProvider.cjs +80 -0
- package/dist/cjs/providers/composite/CompositeCapabilityProvider.cjs.map +1 -0
- package/dist/cjs/providers/mcp/MCPCapabilityProvider.cjs +244 -0
- package/dist/cjs/providers/mcp/MCPCapabilityProvider.cjs.map +1 -0
- package/dist/cjs/providers/mcp/config.cjs +42 -0
- package/dist/cjs/providers/mcp/config.cjs.map +1 -0
- package/dist/cjs/providers/mcp/transport.cjs +65 -0
- package/dist/cjs/providers/mcp/transport.cjs.map +1 -0
- package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs +121 -0
- package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs.map +1 -0
- package/dist/cjs/providers/types.cjs +51 -0
- package/dist/cjs/providers/types.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +3 -0
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/proxyTool.cjs +100 -0
- package/dist/cjs/tools/proxyTool.cjs.map +1 -0
- package/dist/cjs/utils/credentials.cjs +142 -0
- package/dist/cjs/utils/credentials.cjs.map +1 -0
- package/dist/cjs/utils/httpClient.cjs +74 -0
- package/dist/cjs/utils/httpClient.cjs.map +1 -0
- package/dist/cjs/utils/toolManifest.cjs +100 -0
- package/dist/cjs/utils/toolManifest.cjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +3 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +3 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +14 -0
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/providers/a2a/A2ACapabilityProvider.mjs +281 -0
- package/dist/esm/providers/a2a/A2ACapabilityProvider.mjs.map +1 -0
- package/dist/esm/providers/a2a/client.mjs +88 -0
- package/dist/esm/providers/a2a/client.mjs.map +1 -0
- package/dist/esm/providers/a2a/config.mjs +35 -0
- package/dist/esm/providers/a2a/config.mjs.map +1 -0
- package/dist/esm/providers/capabilityNaming.mjs +39 -0
- package/dist/esm/providers/capabilityNaming.mjs.map +1 -0
- package/dist/esm/providers/composite/CompositeCapabilityProvider.mjs +78 -0
- package/dist/esm/providers/composite/CompositeCapabilityProvider.mjs.map +1 -0
- package/dist/esm/providers/mcp/MCPCapabilityProvider.mjs +240 -0
- package/dist/esm/providers/mcp/MCPCapabilityProvider.mjs.map +1 -0
- package/dist/esm/providers/mcp/config.mjs +39 -0
- package/dist/esm/providers/mcp/config.mjs.map +1 -0
- package/dist/esm/providers/mcp/transport.mjs +63 -0
- package/dist/esm/providers/mcp/transport.mjs.map +1 -0
- package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs +119 -0
- package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs.map +1 -0
- package/dist/esm/providers/types.mjs +51 -0
- package/dist/esm/providers/types.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +3 -0
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/proxyTool.mjs +98 -0
- package/dist/esm/tools/proxyTool.mjs.map +1 -0
- package/dist/esm/utils/credentials.mjs +135 -0
- package/dist/esm/utils/credentials.mjs.map +1 -0
- package/dist/esm/utils/httpClient.mjs +70 -0
- package/dist/esm/utils/httpClient.mjs.map +1 -0
- package/dist/esm/utils/toolManifest.mjs +96 -0
- package/dist/esm/utils/toolManifest.mjs.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/providers/a2a/A2ACapabilityProvider.d.ts +89 -0
- package/dist/types/providers/a2a/client.d.ts +47 -0
- package/dist/types/providers/a2a/config.d.ts +18 -0
- package/dist/types/providers/a2a/index.d.ts +6 -0
- package/dist/types/providers/a2a/types.d.ts +173 -0
- package/dist/types/providers/capabilityNaming.d.ts +25 -0
- package/dist/types/providers/composite/CompositeCapabilityProvider.d.ts +22 -0
- package/dist/types/providers/composite/index.d.ts +1 -0
- package/dist/types/providers/index.d.ts +13 -0
- package/dist/types/providers/mcp/MCPCapabilityProvider.d.ts +54 -0
- package/dist/types/providers/mcp/config.d.ts +20 -0
- package/dist/types/providers/mcp/index.d.ts +5 -0
- package/dist/types/providers/mcp/transport.d.ts +18 -0
- package/dist/types/providers/mcp/types.d.ts +112 -0
- package/dist/types/providers/tools-server/ToolsServerCapabilityProvider.d.ts +45 -0
- package/dist/types/providers/tools-server/index.d.ts +1 -0
- package/dist/types/providers/types.d.ts +170 -0
- package/dist/types/tools/proxyTool.d.ts +55 -0
- package/dist/types/types/stream.d.ts +10 -0
- package/dist/types/utils/credentials.d.ts +77 -0
- package/dist/types/utils/httpClient.d.ts +46 -0
- package/dist/types/utils/index.d.ts +3 -0
- package/dist/types/utils/toolManifest.d.ts +49 -0
- package/package.json +21 -1
- package/src/graphs/Graph.ts +0 -23
- package/src/index.ts +4 -0
- package/src/providers/__tests__/CompositeCapabilityProvider.test.ts +93 -0
- package/src/providers/__tests__/ToolsServerCapabilityProvider.integration.spec.ts +79 -0
- package/src/providers/__tests__/ToolsServerCapabilityProvider.test.ts +206 -0
- package/src/providers/__tests__/types.test.ts +64 -0
- package/src/providers/a2a/A2ACapabilityProvider.ts +384 -0
- package/src/providers/a2a/__tests__/A2ACapabilityProvider.integration.spec.ts +345 -0
- package/src/providers/a2a/__tests__/A2ACapabilityProvider.test.ts +460 -0
- package/src/providers/a2a/client.ts +115 -0
- package/src/providers/a2a/config.ts +40 -0
- package/src/providers/a2a/index.ts +29 -0
- package/src/providers/a2a/types.ts +191 -0
- package/src/providers/capabilityNaming.ts +42 -0
- package/src/providers/composite/CompositeCapabilityProvider.ts +112 -0
- package/src/providers/composite/index.ts +1 -0
- package/src/providers/index.ts +65 -0
- package/src/providers/mcp/MCPCapabilityProvider.ts +345 -0
- package/src/providers/mcp/__tests__/MCPCapabilityProvider.integration.spec.ts +386 -0
- package/src/providers/mcp/__tests__/MCPCapabilityProvider.test.ts +371 -0
- package/src/providers/mcp/config.ts +45 -0
- package/src/providers/mcp/index.ts +21 -0
- package/src/providers/mcp/transport.ts +76 -0
- package/src/providers/mcp/types.ts +139 -0
- package/src/providers/tools-server/ToolsServerCapabilityProvider.ts +220 -0
- package/src/providers/tools-server/index.ts +1 -0
- package/src/providers/types.ts +187 -0
- package/src/tools/proxyTool.ts +146 -0
- package/src/types/stream.ts +10 -0
- package/src/utils/__tests__/credentials.test.ts +130 -0
- package/src/utils/__tests__/errors.test.ts +6 -4
- package/src/utils/__tests__/httpClient.test.ts +75 -0
- package/src/utils/__tests__/toolManifest.test.ts +116 -0
- package/src/utils/credentials.ts +157 -0
- package/src/utils/httpClient.ts +92 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/toolManifest.ts +109 -0
- package/src/agents/AgentContext.js.map +0 -1
- package/src/agents/AgentContext.test.js.map +0 -1
- package/src/agents/__tests__/AgentContext.test.js.map +0 -1
- package/src/agents/__tests__/resolveStructuredOutputMode.test.js.map +0 -1
- package/src/common/enum.js.map +0 -1
- package/src/common/index.js.map +0 -1
- package/src/events.js.map +0 -1
- package/src/graphs/Graph.js.map +0 -1
- package/src/graphs/MultiAgentGraph.js.map +0 -1
- package/src/graphs/__tests__/structured-output.integration.test.js.map +0 -1
- package/src/graphs/__tests__/structured-output.test.js.map +0 -1
- package/src/graphs/contextManagement.e2e.test.js.map +0 -1
- package/src/graphs/contextManagement.test.js.map +0 -1
- package/src/graphs/handoffValidation.test.js.map +0 -1
- package/src/graphs/index.js.map +0 -1
- package/src/index.js.map +0 -1
- package/src/instrumentation.js.map +0 -1
- package/src/llm/anthropic/index.js.map +0 -1
- package/src/llm/anthropic/types.js.map +0 -1
- package/src/llm/anthropic/utils/message_inputs.js.map +0 -1
- package/src/llm/anthropic/utils/message_outputs.js.map +0 -1
- package/src/llm/anthropic/utils/output_parsers.js.map +0 -1
- package/src/llm/anthropic/utils/tools.js.map +0 -1
- package/src/llm/bedrock/__tests__/bedrock-caching.test.js.map +0 -1
- package/src/llm/bedrock/index.js.map +0 -1
- package/src/llm/bedrock/types.js.map +0 -1
- package/src/llm/bedrock/utils/index.js.map +0 -1
- package/src/llm/bedrock/utils/message_inputs.js.map +0 -1
- package/src/llm/bedrock/utils/message_outputs.js.map +0 -1
- package/src/llm/fake.js.map +0 -1
- package/src/llm/google/index.js.map +0 -1
- package/src/llm/google/types.js.map +0 -1
- package/src/llm/google/utils/common.js.map +0 -1
- package/src/llm/google/utils/tools.js.map +0 -1
- package/src/llm/google/utils/zod_to_genai_parameters.js.map +0 -1
- package/src/llm/openai/index.js.map +0 -1
- package/src/llm/openai/types.js.map +0 -1
- package/src/llm/openai/utils/index.js.map +0 -1
- package/src/llm/openai/utils/isReasoningModel.test.js.map +0 -1
- package/src/llm/openrouter/index.js.map +0 -1
- package/src/llm/openrouter/reasoning.test.js.map +0 -1
- package/src/llm/providers.js.map +0 -1
- package/src/llm/text.js.map +0 -1
- package/src/llm/vertexai/index.js.map +0 -1
- package/src/messages/__tests__/tools.test.js.map +0 -1
- package/src/messages/cache.js.map +0 -1
- package/src/messages/cache.test.js.map +0 -1
- package/src/messages/content.js.map +0 -1
- package/src/messages/content.test.js.map +0 -1
- package/src/messages/core.js.map +0 -1
- package/src/messages/ensureThinkingBlock.test.js.map +0 -1
- package/src/messages/format.js.map +0 -1
- package/src/messages/formatAgentMessages.test.js.map +0 -1
- package/src/messages/formatAgentMessages.tools.test.js.map +0 -1
- package/src/messages/formatMessage.test.js.map +0 -1
- package/src/messages/ids.js.map +0 -1
- package/src/messages/index.js.map +0 -1
- package/src/messages/labelContentByAgent.test.js.map +0 -1
- package/src/messages/prune.js.map +0 -1
- package/src/messages/reducer.js.map +0 -1
- package/src/messages/shiftIndexTokenCountMap.test.js.map +0 -1
- package/src/messages/summarize.js.map +0 -1
- package/src/messages/summarize.test.js.map +0 -1
- package/src/messages/tools.js.map +0 -1
- package/src/mockStream.js.map +0 -1
- package/src/prompts/collab.js.map +0 -1
- package/src/prompts/index.js.map +0 -1
- package/src/prompts/taskmanager.js.map +0 -1
- package/src/run.js.map +0 -1
- package/src/schemas/index.js.map +0 -1
- package/src/schemas/schema-preparation.test.js.map +0 -1
- package/src/schemas/validate.js.map +0 -1
- package/src/schemas/validate.test.js.map +0 -1
- package/src/scripts/abort.js.map +0 -1
- package/src/scripts/ant_web_search.js.map +0 -1
- package/src/scripts/ant_web_search_edge_case.js.map +0 -1
- package/src/scripts/ant_web_search_error_edge_case.js.map +0 -1
- package/src/scripts/args.js.map +0 -1
- package/src/scripts/bedrock-cache-debug.js.map +0 -1
- package/src/scripts/bedrock-content-aggregation-test.js.map +0 -1
- package/src/scripts/bedrock-merge-test.js.map +0 -1
- package/src/scripts/bedrock-parallel-tools-test.js.map +0 -1
- package/src/scripts/caching.js.map +0 -1
- package/src/scripts/cli.js.map +0 -1
- package/src/scripts/cli2.js.map +0 -1
- package/src/scripts/cli3.js.map +0 -1
- package/src/scripts/cli4.js.map +0 -1
- package/src/scripts/cli5.js.map +0 -1
- package/src/scripts/code_exec.js.map +0 -1
- package/src/scripts/code_exec_files.js.map +0 -1
- package/src/scripts/code_exec_multi_session.js.map +0 -1
- package/src/scripts/code_exec_ptc.js.map +0 -1
- package/src/scripts/code_exec_session.js.map +0 -1
- package/src/scripts/code_exec_simple.js.map +0 -1
- package/src/scripts/content.js.map +0 -1
- package/src/scripts/empty_input.js.map +0 -1
- package/src/scripts/handoff-test.js.map +0 -1
- package/src/scripts/image.js.map +0 -1
- package/src/scripts/memory.js.map +0 -1
- package/src/scripts/multi-agent-chain.js.map +0 -1
- package/src/scripts/multi-agent-conditional.js.map +0 -1
- package/src/scripts/multi-agent-document-review-chain.js.map +0 -1
- package/src/scripts/multi-agent-hybrid-flow.js.map +0 -1
- package/src/scripts/multi-agent-parallel-start.js.map +0 -1
- package/src/scripts/multi-agent-parallel.js.map +0 -1
- package/src/scripts/multi-agent-sequence.js.map +0 -1
- package/src/scripts/multi-agent-supervisor.js.map +0 -1
- package/src/scripts/multi-agent-test.js.map +0 -1
- package/src/scripts/parallel-asymmetric-tools-test.js.map +0 -1
- package/src/scripts/parallel-full-metadata-test.js.map +0 -1
- package/src/scripts/parallel-tools-test.js.map +0 -1
- package/src/scripts/programmatic_exec.js.map +0 -1
- package/src/scripts/programmatic_exec_agent.js.map +0 -1
- package/src/scripts/search.js.map +0 -1
- package/src/scripts/sequential-full-metadata-test.js.map +0 -1
- package/src/scripts/simple.js.map +0 -1
- package/src/scripts/single-agent-metadata-test.js.map +0 -1
- package/src/scripts/stream.js.map +0 -1
- package/src/scripts/test-custom-prompt-key.js.map +0 -1
- package/src/scripts/test-handoff-input.js.map +0 -1
- package/src/scripts/test-handoff-preamble.js.map +0 -1
- package/src/scripts/test-handoff-steering.js.map +0 -1
- package/src/scripts/test-multi-agent-list-handoff.js.map +0 -1
- package/src/scripts/test-parallel-agent-labeling.js.map +0 -1
- package/src/scripts/test-parallel-handoffs.js.map +0 -1
- package/src/scripts/test-thinking-handoff-bedrock.js.map +0 -1
- package/src/scripts/test-thinking-handoff.js.map +0 -1
- package/src/scripts/test-thinking-to-thinking-handoff-bedrock.js.map +0 -1
- package/src/scripts/test-tool-before-handoff-role-order.js.map +0 -1
- package/src/scripts/test-tools-before-handoff.js.map +0 -1
- package/src/scripts/test_code_api.js.map +0 -1
- package/src/scripts/thinking-bedrock.js.map +0 -1
- package/src/scripts/thinking-vertexai.js.map +0 -1
- package/src/scripts/thinking.js.map +0 -1
- package/src/scripts/tool_search.js.map +0 -1
- package/src/scripts/tools.js.map +0 -1
- package/src/specs/agent-handoffs-bedrock.integration.test.js.map +0 -1
- package/src/specs/agent-handoffs.test.js.map +0 -1
- package/src/specs/anthropic.simple.test.js.map +0 -1
- package/src/specs/azure.simple.test.js.map +0 -1
- package/src/specs/cache.simple.test.js.map +0 -1
- package/src/specs/custom-event-await.test.js.map +0 -1
- package/src/specs/deepseek.simple.test.js.map +0 -1
- package/src/specs/emergency-prune.test.js.map +0 -1
- package/src/specs/moonshot.simple.test.js.map +0 -1
- package/src/specs/observability.integration.test.js.map +0 -1
- package/src/specs/openai.simple.test.js.map +0 -1
- package/src/specs/openrouter.simple.test.js.map +0 -1
- package/src/specs/prune.test.js.map +0 -1
- package/src/specs/reasoning.test.js.map +0 -1
- package/src/specs/spec.utils.js.map +0 -1
- package/src/specs/thinking-handoff.test.js.map +0 -1
- package/src/specs/thinking-prune.test.js.map +0 -1
- package/src/specs/token-distribution-edge-case.test.js.map +0 -1
- package/src/specs/token-memoization.test.js.map +0 -1
- package/src/specs/tokens.test.js.map +0 -1
- package/src/specs/tool-error.test.js.map +0 -1
- package/src/splitStream.js.map +0 -1
- package/src/splitStream.test.js.map +0 -1
- package/src/stream.js.map +0 -1
- package/src/stream.test.js.map +0 -1
- package/src/test/mockTools.js.map +0 -1
- package/src/tools/BrowserTools.js.map +0 -1
- package/src/tools/Calculator.js.map +0 -1
- package/src/tools/Calculator.test.js.map +0 -1
- package/src/tools/CodeExecutor.js.map +0 -1
- package/src/tools/ProgrammaticToolCalling.js.map +0 -1
- package/src/tools/StreamingToolCallBuffer.js.map +0 -1
- package/src/tools/ToolNode.js.map +0 -1
- package/src/tools/ToolSearch.js.map +0 -1
- package/src/tools/__tests__/BrowserTools.test.js.map +0 -1
- package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.js.map +0 -1
- package/src/tools/__tests__/ProgrammaticToolCalling.test.js.map +0 -1
- package/src/tools/__tests__/StreamingToolCallBuffer.test.js.map +0 -1
- package/src/tools/__tests__/ToolApproval.test.js.map +0 -1
- package/src/tools/__tests__/ToolNode.recovery.test.js.map +0 -1
- package/src/tools/__tests__/ToolNode.session.test.js.map +0 -1
- package/src/tools/__tests__/ToolSearch.integration.test.js.map +0 -1
- package/src/tools/__tests__/ToolSearch.test.js.map +0 -1
- package/src/tools/__tests__/handlers.test.js.map +0 -1
- package/src/tools/__tests__/truncation-recovery.integration.test.js.map +0 -1
- package/src/tools/handlers.js.map +0 -1
- package/src/tools/schema.js.map +0 -1
- package/src/tools/search/anthropic.js.map +0 -1
- package/src/tools/search/content.js.map +0 -1
- package/src/tools/search/content.test.js.map +0 -1
- package/src/tools/search/firecrawl.js.map +0 -1
- package/src/tools/search/format.js.map +0 -1
- package/src/tools/search/highlights.js.map +0 -1
- package/src/tools/search/index.js.map +0 -1
- package/src/tools/search/jina-reranker.test.js.map +0 -1
- package/src/tools/search/rerankers.js.map +0 -1
- package/src/tools/search/schema.js.map +0 -1
- package/src/tools/search/search.js.map +0 -1
- package/src/tools/search/serper-scraper.js.map +0 -1
- package/src/tools/search/test.js.map +0 -1
- package/src/tools/search/tool.js.map +0 -1
- package/src/tools/search/types.js.map +0 -1
- package/src/tools/search/utils.js.map +0 -1
- package/src/types/graph.js.map +0 -1
- package/src/types/graph.test.js.map +0 -1
- package/src/types/index.js.map +0 -1
- package/src/types/llm.js.map +0 -1
- package/src/types/messages.js.map +0 -1
- package/src/types/run.js.map +0 -1
- package/src/types/stream.js.map +0 -1
- package/src/types/tools.js.map +0 -1
- package/src/utils/contextAnalytics.js.map +0 -1
- package/src/utils/contextAnalytics.test.js.map +0 -1
- package/src/utils/events.js.map +0 -1
- package/src/utils/graph.js.map +0 -1
- package/src/utils/handlers.js.map +0 -1
- package/src/utils/index.js.map +0 -1
- package/src/utils/llm.js.map +0 -1
- package/src/utils/llmConfig.js.map +0 -1
- package/src/utils/logging.js.map +0 -1
- package/src/utils/misc.js.map +0 -1
- package/src/utils/run.js.map +0 -1
- package/src/utils/schema.js.map +0 -1
- package/src/utils/title.js.map +0 -1
- package/src/utils/tokens.js.map +0 -1
- package/src/utils/toonFormat.js.map +0 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { tool } from '@langchain/core/tools';
|
|
2
|
+
import { CapabilityKind } from '../types.mjs';
|
|
3
|
+
import { formatCapabilityName, parseCapabilityName as parseCapabilityName$1 } from '../capabilityNaming.mjs';
|
|
4
|
+
export { CAPABILITY_NAME_SEPARATOR } from '../capabilityNaming.mjs';
|
|
5
|
+
import { A2AClient, generateRpcId, extractTaskText } from './client.mjs';
|
|
6
|
+
import { getA2AEnvDefaults, consoleLogger } from './config.mjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A2ACapabilityProvider — client-side adapter for consuming A2A-served
|
|
10
|
+
* remote agents as capabilities.
|
|
11
|
+
*
|
|
12
|
+
* Same pattern as `MCPCapabilityProvider`: take a unified spec map,
|
|
13
|
+
* speak the protocol (A2A JSON-RPC over HTTP), expose each skill as a
|
|
14
|
+
* `Capability` / `StructuredTool`.
|
|
15
|
+
*
|
|
16
|
+
* Design:
|
|
17
|
+
* - One `A2AClient` per remote spec, cached after first use.
|
|
18
|
+
* - `fetchManifest()`:
|
|
19
|
+
* for each remote, GET /.well-known/agent.json → emit one
|
|
20
|
+
* Capability per advertised skill (or one per remote when
|
|
21
|
+
* `flattenAsSingleTool` is set).
|
|
22
|
+
* - `createRunnables()`:
|
|
23
|
+
* each Capability becomes a StructuredTool whose invoke sends a
|
|
24
|
+
* JSON-RPC `tasks/send` to the remote and returns the final
|
|
25
|
+
* artifact's text.
|
|
26
|
+
*
|
|
27
|
+
* What the host supplies:
|
|
28
|
+
* - `remotes`: unified spec map (normalized from whatever source).
|
|
29
|
+
* - `getAuthHeaders?`: per-remote header resolver. Called at connect
|
|
30
|
+
* time. OAuth / token refresh stays in the host.
|
|
31
|
+
*/
|
|
32
|
+
class A2ACapabilityProvider {
|
|
33
|
+
providerId;
|
|
34
|
+
config;
|
|
35
|
+
logger;
|
|
36
|
+
connections = new Map();
|
|
37
|
+
constructor(config) {
|
|
38
|
+
if (!config.remotes || Object.keys(config.remotes).length === 0) {
|
|
39
|
+
throw new Error('A2ACapabilityProvider: at least one remote is required');
|
|
40
|
+
}
|
|
41
|
+
const env = getA2AEnvDefaults();
|
|
42
|
+
this.config = {
|
|
43
|
+
remotes: config.remotes,
|
|
44
|
+
getAuthHeaders: config.getAuthHeaders,
|
|
45
|
+
clientInfo: config.clientInfo ?? {
|
|
46
|
+
name: env.clientName,
|
|
47
|
+
version: env.clientVersion,
|
|
48
|
+
},
|
|
49
|
+
timeoutMs: config.timeoutMs ?? env.timeoutMs,
|
|
50
|
+
};
|
|
51
|
+
this.logger = config.logger ?? consoleLogger;
|
|
52
|
+
const names = Object.keys(config.remotes).sort().join(',');
|
|
53
|
+
this.providerId = `a2a:${names}`;
|
|
54
|
+
}
|
|
55
|
+
async fetchManifest(filter) {
|
|
56
|
+
if (filter?.kind && filter.kind !== CapabilityKind.A2A)
|
|
57
|
+
return [];
|
|
58
|
+
const capabilities = [];
|
|
59
|
+
for (const remoteName of Object.keys(this.config.remotes)) {
|
|
60
|
+
try {
|
|
61
|
+
const conn = await this.ensureConnection(remoteName);
|
|
62
|
+
const caps = this.cardToCapabilities(remoteName, conn.card, conn.spec);
|
|
63
|
+
for (const cap of caps) {
|
|
64
|
+
if (this.capabilityMatchesFilter(cap, filter))
|
|
65
|
+
capabilities.push(cap);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
// One remote failing must not block others — log and continue.
|
|
70
|
+
this.logger.warn(`[a2a] failed to fetch manifest for remote "${remoteName}":`, err instanceof Error ? err.message : err);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
this.logger.debug(`[a2a] manifest assembled — ${capabilities.length} capabilities across ${this.connections.size}/${Object.keys(this.config.remotes).length} connected remotes`);
|
|
74
|
+
return capabilities;
|
|
75
|
+
}
|
|
76
|
+
async createRunnables(capabilities, _credentials) {
|
|
77
|
+
const runnables = [];
|
|
78
|
+
for (const cap of capabilities) {
|
|
79
|
+
if (cap.kind !== CapabilityKind.A2A)
|
|
80
|
+
continue;
|
|
81
|
+
const parsed = parseCapabilityName(cap.name);
|
|
82
|
+
if (!parsed) {
|
|
83
|
+
this.logger.warn(`[a2a] skipping capability "${cap.name}" — unparseable`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
runnables.push(this.buildProxyTool(parsed.remoteName, parsed.skillId, cap));
|
|
87
|
+
}
|
|
88
|
+
return runnables;
|
|
89
|
+
}
|
|
90
|
+
/** Disconnect any cached clients. A2A is stateless over HTTP so this
|
|
91
|
+
* is effectively a cache clear — no sockets to close. */
|
|
92
|
+
async close() {
|
|
93
|
+
this.connections.clear();
|
|
94
|
+
}
|
|
95
|
+
// --- internals ---------------------------------------------------------
|
|
96
|
+
async ensureConnection(remoteName) {
|
|
97
|
+
const existing = this.connections.get(remoteName);
|
|
98
|
+
if (existing)
|
|
99
|
+
return existing;
|
|
100
|
+
const spec = this.config.remotes[remoteName];
|
|
101
|
+
if (!spec) {
|
|
102
|
+
throw new Error(`[a2a] remote "${remoteName}" not in config`);
|
|
103
|
+
}
|
|
104
|
+
const authHeaders = await this.resolveAuthHeaders(remoteName);
|
|
105
|
+
const headers = {
|
|
106
|
+
...(spec.headers ?? {}),
|
|
107
|
+
...(authHeaders ?? {}),
|
|
108
|
+
'user-agent': `${this.config.clientInfo.name}/${this.config.clientInfo.version}`,
|
|
109
|
+
};
|
|
110
|
+
const client = new A2AClient({
|
|
111
|
+
baseUrl: spec.url,
|
|
112
|
+
headers,
|
|
113
|
+
timeoutMs: this.config.timeoutMs,
|
|
114
|
+
cardPath: spec.cardPath,
|
|
115
|
+
rpcPath: spec.rpcPath,
|
|
116
|
+
});
|
|
117
|
+
this.logger.debug(`[a2a] fetching card from "${remoteName}" at ${spec.url}`);
|
|
118
|
+
const card = await client.fetchAgentCard();
|
|
119
|
+
this.logger.debug(`[a2a] connected to "${remoteName}" — ${card.skills.length ?? 0} skills discovered`);
|
|
120
|
+
const conn = { remoteName, spec, client, card };
|
|
121
|
+
this.connections.set(remoteName, conn);
|
|
122
|
+
return conn;
|
|
123
|
+
}
|
|
124
|
+
async resolveAuthHeaders(remoteName) {
|
|
125
|
+
if (!this.config.getAuthHeaders)
|
|
126
|
+
return undefined;
|
|
127
|
+
try {
|
|
128
|
+
return await this.config.getAuthHeaders(remoteName);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
this.logger.warn(`[a2a] getAuthHeaders("${remoteName}") threw:`, err instanceof Error ? err.message : err);
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
cardToCapabilities(remoteName, card, spec) {
|
|
136
|
+
if (spec.flattenAsSingleTool) {
|
|
137
|
+
// One capability per remote, named after the remote. Useful when
|
|
138
|
+
// the card has no skills listed or you want coarse routing.
|
|
139
|
+
return [
|
|
140
|
+
{
|
|
141
|
+
kind: CapabilityKind.A2A,
|
|
142
|
+
name: formatCapabilityName(remoteName),
|
|
143
|
+
description: card.description ?? card.name,
|
|
144
|
+
schema: MESSAGE_INPUT_SCHEMA,
|
|
145
|
+
authConfig: [],
|
|
146
|
+
metadata: {
|
|
147
|
+
category: 'a2a',
|
|
148
|
+
tags: [remoteName, ...(card.skills.map((s) => s.id) ?? [])],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
if (!card.skills || card.skills.length === 0) {
|
|
154
|
+
// No skills advertised — expose a single message endpoint so the
|
|
155
|
+
// agent can still invoke the remote with free-form text.
|
|
156
|
+
return [
|
|
157
|
+
{
|
|
158
|
+
kind: CapabilityKind.A2A,
|
|
159
|
+
name: formatCapabilityName(remoteName),
|
|
160
|
+
description: card.description ?? `Delegate to remote agent "${card.name}".`,
|
|
161
|
+
schema: MESSAGE_INPUT_SCHEMA,
|
|
162
|
+
authConfig: [],
|
|
163
|
+
metadata: { category: 'a2a', tags: [remoteName] },
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
return card.skills.map((skill) => skillToCapability(remoteName, skill));
|
|
168
|
+
}
|
|
169
|
+
capabilityMatchesFilter(cap, filter) {
|
|
170
|
+
if (!filter)
|
|
171
|
+
return true;
|
|
172
|
+
if (filter.kind && cap.kind !== filter.kind)
|
|
173
|
+
return false;
|
|
174
|
+
if (filter.names && !filter.names.includes(cap.name))
|
|
175
|
+
return false;
|
|
176
|
+
if (filter.tags?.length) {
|
|
177
|
+
const capTags = new Set(cap.metadata.tags ?? []);
|
|
178
|
+
if (!filter.tags.some((t) => capTags.has(t)))
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
buildProxyTool(remoteName, skillId, cap) {
|
|
184
|
+
const logger = this.logger;
|
|
185
|
+
const ensureConnection = this.ensureConnection.bind(this);
|
|
186
|
+
return tool(async (input) => {
|
|
187
|
+
// DEBUG: log tool invocation (remove after POC stabilizes)
|
|
188
|
+
logger.debug(`[a2a] invoking ${remoteName}${skillId ? `:${skillId}` : ''}`);
|
|
189
|
+
const message = coerceInputToA2AMessage(input, skillId);
|
|
190
|
+
const conn = await ensureConnection(remoteName);
|
|
191
|
+
const task = await conn.client.sendTask({
|
|
192
|
+
id: generateRpcId(),
|
|
193
|
+
message,
|
|
194
|
+
});
|
|
195
|
+
try {
|
|
196
|
+
return extractTaskText(task);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
throw new Error(`A2A ${remoteName}${skillId ? `:${skillId}` : ''} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
200
|
+
}
|
|
201
|
+
}, {
|
|
202
|
+
name: cap.name,
|
|
203
|
+
description: cap.description,
|
|
204
|
+
schema: cap.schema ?? MESSAGE_INPUT_SCHEMA,
|
|
205
|
+
responseFormat: 'content',
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Parse a capability name produced by this provider, returning A2A-specific
|
|
211
|
+
* field names (`remoteName` / `skillId`). Thin adapter over the shared
|
|
212
|
+
* `parseCapabilityName` — kept local so callers see the A2A vocabulary.
|
|
213
|
+
*/
|
|
214
|
+
function parseCapabilityName(name) {
|
|
215
|
+
const parsed = parseCapabilityName$1(name);
|
|
216
|
+
if (!parsed)
|
|
217
|
+
return null;
|
|
218
|
+
return {
|
|
219
|
+
remoteName: parsed.sourceName,
|
|
220
|
+
skillId: parsed.itemName,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/** Derive a Capability from a skill entry on the agent card. */
|
|
224
|
+
function skillToCapability(remoteName, skill) {
|
|
225
|
+
return {
|
|
226
|
+
kind: CapabilityKind.A2A,
|
|
227
|
+
name: formatCapabilityName(remoteName, skill.id),
|
|
228
|
+
description: skill.description || skill.name || skill.id,
|
|
229
|
+
schema: MESSAGE_INPUT_SCHEMA,
|
|
230
|
+
authConfig: [],
|
|
231
|
+
metadata: {
|
|
232
|
+
category: 'a2a',
|
|
233
|
+
tags: [remoteName, ...(skill.tags ?? [])],
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Default input schema for A2A capabilities. Every invocation is a text
|
|
239
|
+
* message — we don't try to infer a per-skill input shape because the
|
|
240
|
+
* A2A spec's skill entries don't include a parameter schema.
|
|
241
|
+
*/
|
|
242
|
+
const MESSAGE_INPUT_SCHEMA = {
|
|
243
|
+
type: 'object',
|
|
244
|
+
properties: {
|
|
245
|
+
message: {
|
|
246
|
+
type: 'string',
|
|
247
|
+
description: 'Instruction or question to send to the remote agent as a user message.',
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
required: ['message'],
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Coerce arbitrary LLM-supplied input into a valid A2A message. If the
|
|
254
|
+
* input is a string, wraps it directly. If it's `{ message: "..." }`,
|
|
255
|
+
* uses `message`. Otherwise JSON-stringifies so the agent receives
|
|
256
|
+
* something it can try to interpret.
|
|
257
|
+
*
|
|
258
|
+
* When a skill id is known, it's prepended to the message text so the
|
|
259
|
+
* remote agent can route — some A2A implementations use this, others
|
|
260
|
+
* ignore it, it's best-effort.
|
|
261
|
+
*/
|
|
262
|
+
function coerceInputToA2AMessage(input, skillId) {
|
|
263
|
+
let text;
|
|
264
|
+
if (typeof input === 'string') {
|
|
265
|
+
text = input;
|
|
266
|
+
}
|
|
267
|
+
else if (input && typeof input === 'object' && 'message' in input) {
|
|
268
|
+
const val = input.message;
|
|
269
|
+
text = typeof val === 'string' ? val : JSON.stringify(val);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
text = JSON.stringify(input);
|
|
273
|
+
}
|
|
274
|
+
if (skillId) {
|
|
275
|
+
text = `[skill: ${skillId}]\n${text}`;
|
|
276
|
+
}
|
|
277
|
+
return { role: 'user', parts: [{ type: 'text', text }] };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export { A2ACapabilityProvider, MESSAGE_INPUT_SCHEMA, coerceInputToA2AMessage, formatCapabilityName, parseCapabilityName, skillToCapability };
|
|
281
|
+
//# sourceMappingURL=A2ACapabilityProvider.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"A2ACapabilityProvider.mjs","sources":["../../../../src/providers/a2a/A2ACapabilityProvider.ts"],"sourcesContent":["/**\n * A2ACapabilityProvider — client-side adapter for consuming A2A-served\n * remote agents as capabilities.\n *\n * Same pattern as `MCPCapabilityProvider`: take a unified spec map,\n * speak the protocol (A2A JSON-RPC over HTTP), expose each skill as a\n * `Capability` / `StructuredTool`.\n *\n * Design:\n * - One `A2AClient` per remote spec, cached after first use.\n * - `fetchManifest()`:\n * for each remote, GET /.well-known/agent.json → emit one\n * Capability per advertised skill (or one per remote when\n * `flattenAsSingleTool` is set).\n * - `createRunnables()`:\n * each Capability becomes a StructuredTool whose invoke sends a\n * JSON-RPC `tasks/send` to the remote and returns the final\n * artifact's text.\n *\n * What the host supplies:\n * - `remotes`: unified spec map (normalized from whatever source).\n * - `getAuthHeaders?`: per-remote header resolver. Called at connect\n * time. OAuth / token refresh stays in the host.\n */\n\nimport { tool, type StructuredToolInterface } from '@langchain/core/tools';\n\nimport {\n CapabilityKind,\n type Capability,\n type CapabilityFilter,\n type CapabilityProvider,\n type CredentialMap,\n} from '@/providers/types';\nimport {\n CAPABILITY_NAME_SEPARATOR,\n formatCapabilityName,\n parseCapabilityName as parseSharedName,\n} from '@/providers/capabilityNaming';\nimport { A2AClient, extractTaskText, generateRpcId } from './client';\nimport { consoleLogger, getA2AEnvDefaults } from './config';\nimport type {\n A2AAgentCard,\n A2ALogger,\n A2AProviderConfig,\n A2ARemoteSpec,\n A2ASkill,\n} from './types';\n\n/**\n * Per-remote cached state. The agent card is fetched lazily and reused.\n */\ninterface RemoteConnection {\n remoteName: string;\n spec: A2ARemoteSpec;\n client: A2AClient;\n card: A2AAgentCard;\n}\n\nexport class A2ACapabilityProvider implements CapabilityProvider {\n readonly providerId: string;\n private readonly config: Required<\n Pick<A2AProviderConfig, 'clientInfo' | 'timeoutMs'>\n > &\n Pick<A2AProviderConfig, 'remotes' | 'getAuthHeaders'>;\n private readonly logger: A2ALogger;\n private readonly connections = new Map<string, RemoteConnection>();\n\n constructor(config: A2AProviderConfig) {\n if (!config.remotes || Object.keys(config.remotes).length === 0) {\n throw new Error('A2ACapabilityProvider: at least one remote is required');\n }\n const env = getA2AEnvDefaults();\n this.config = {\n remotes: config.remotes,\n getAuthHeaders: config.getAuthHeaders,\n clientInfo: config.clientInfo ?? {\n name: env.clientName,\n version: env.clientVersion,\n },\n timeoutMs: config.timeoutMs ?? env.timeoutMs,\n };\n this.logger = config.logger ?? consoleLogger;\n const names = Object.keys(config.remotes).sort().join(',');\n this.providerId = `a2a:${names}`;\n }\n\n async fetchManifest(filter?: CapabilityFilter): Promise<Capability[]> {\n if (filter?.kind && filter.kind !== CapabilityKind.A2A) return [];\n\n const capabilities: Capability[] = [];\n for (const remoteName of Object.keys(this.config.remotes)) {\n try {\n const conn = await this.ensureConnection(remoteName);\n const caps = this.cardToCapabilities(remoteName, conn.card, conn.spec);\n for (const cap of caps) {\n if (this.capabilityMatchesFilter(cap, filter)) capabilities.push(cap);\n }\n } catch (err) {\n // One remote failing must not block others — log and continue.\n this.logger.warn(\n `[a2a] failed to fetch manifest for remote \"${remoteName}\":`,\n err instanceof Error ? err.message : err\n );\n }\n }\n\n this.logger.debug(\n `[a2a] manifest assembled — ${capabilities.length} capabilities across ${this.connections.size}/${Object.keys(this.config.remotes).length} connected remotes`\n );\n\n return capabilities;\n }\n\n async createRunnables(\n capabilities: Capability[],\n _credentials: CredentialMap\n ): Promise<StructuredToolInterface[]> {\n const runnables: StructuredToolInterface[] = [];\n for (const cap of capabilities) {\n if (cap.kind !== CapabilityKind.A2A) continue;\n const parsed = parseCapabilityName(cap.name);\n if (!parsed) {\n this.logger.warn(\n `[a2a] skipping capability \"${cap.name}\" — unparseable`\n );\n continue;\n }\n runnables.push(\n this.buildProxyTool(parsed.remoteName, parsed.skillId, cap)\n );\n }\n return runnables;\n }\n\n /** Disconnect any cached clients. A2A is stateless over HTTP so this\n * is effectively a cache clear — no sockets to close. */\n async close(): Promise<void> {\n this.connections.clear();\n }\n\n // --- internals ---------------------------------------------------------\n\n private async ensureConnection(\n remoteName: string\n ): Promise<RemoteConnection> {\n const existing = this.connections.get(remoteName);\n if (existing) return existing;\n\n const spec = this.config.remotes[remoteName];\n if (!spec) {\n throw new Error(`[a2a] remote \"${remoteName}\" not in config`);\n }\n\n const authHeaders = await this.resolveAuthHeaders(remoteName);\n const headers = {\n ...(spec.headers ?? {}),\n ...(authHeaders ?? {}),\n 'user-agent': `${this.config.clientInfo.name}/${this.config.clientInfo.version}`,\n };\n\n const client = new A2AClient({\n baseUrl: spec.url,\n headers,\n timeoutMs: this.config.timeoutMs,\n cardPath: spec.cardPath,\n rpcPath: spec.rpcPath,\n });\n\n this.logger.debug(\n `[a2a] fetching card from \"${remoteName}\" at ${spec.url}`\n );\n const card = await client.fetchAgentCard();\n this.logger.debug(\n `[a2a] connected to \"${remoteName}\" — ${card.skills.length ?? 0} skills discovered`\n );\n\n const conn: RemoteConnection = { remoteName, spec, client, card };\n this.connections.set(remoteName, conn);\n return conn;\n }\n\n private async resolveAuthHeaders(\n remoteName: string\n ): Promise<Record<string, string> | undefined> {\n if (!this.config.getAuthHeaders) return undefined;\n try {\n return await this.config.getAuthHeaders(remoteName);\n } catch (err) {\n this.logger.warn(\n `[a2a] getAuthHeaders(\"${remoteName}\") threw:`,\n err instanceof Error ? err.message : err\n );\n return undefined;\n }\n }\n\n private cardToCapabilities(\n remoteName: string,\n card: A2AAgentCard,\n spec: A2ARemoteSpec\n ): Capability[] {\n if (spec.flattenAsSingleTool) {\n // One capability per remote, named after the remote. Useful when\n // the card has no skills listed or you want coarse routing.\n return [\n {\n kind: CapabilityKind.A2A,\n name: formatCapabilityName(remoteName),\n description: card.description ?? card.name,\n schema: MESSAGE_INPUT_SCHEMA,\n authConfig: [],\n metadata: {\n category: 'a2a',\n tags: [remoteName, ...(card.skills.map((s) => s.id) ?? [])],\n },\n },\n ];\n }\n\n if (!card.skills || card.skills.length === 0) {\n // No skills advertised — expose a single message endpoint so the\n // agent can still invoke the remote with free-form text.\n return [\n {\n kind: CapabilityKind.A2A,\n name: formatCapabilityName(remoteName),\n description:\n card.description ?? `Delegate to remote agent \"${card.name}\".`,\n schema: MESSAGE_INPUT_SCHEMA,\n authConfig: [],\n metadata: { category: 'a2a', tags: [remoteName] },\n },\n ];\n }\n\n return card.skills.map(\n (skill): Capability => skillToCapability(remoteName, skill)\n );\n }\n\n private capabilityMatchesFilter(\n cap: Capability,\n filter?: CapabilityFilter\n ): boolean {\n if (!filter) return true;\n if (filter.kind && cap.kind !== filter.kind) return false;\n if (filter.names && !filter.names.includes(cap.name)) return false;\n if (filter.tags?.length) {\n const capTags = new Set(cap.metadata.tags ?? []);\n if (!filter.tags.some((t) => capTags.has(t))) return false;\n }\n return true;\n }\n\n private buildProxyTool(\n remoteName: string,\n skillId: string | undefined,\n cap: Capability\n ): StructuredToolInterface {\n const logger = this.logger;\n const ensureConnection = this.ensureConnection.bind(this);\n\n return tool(\n async (input: unknown): Promise<string> => {\n // DEBUG: log tool invocation (remove after POC stabilizes)\n logger.debug(\n `[a2a] invoking ${remoteName}${skillId ? `:${skillId}` : ''}`\n );\n\n const message = coerceInputToA2AMessage(input, skillId);\n const conn = await ensureConnection(remoteName);\n const task = await conn.client.sendTask({\n id: generateRpcId(),\n message,\n });\n\n try {\n return extractTaskText(task);\n } catch (err) {\n throw new Error(\n `A2A ${remoteName}${skillId ? `:${skillId}` : ''} failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n },\n {\n name: cap.name,\n description: cap.description,\n schema: (cap.schema as object) ?? MESSAGE_INPUT_SCHEMA,\n responseFormat: 'content',\n }\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers exported for testing\n// ---------------------------------------------------------------------------\n\n// CAPABILITY_NAME_SEPARATOR + formatCapabilityName live in the shared\n// `@/providers/capabilityNaming` module so every provider uses the same\n// encoding. Re-exported here for backward-compatible imports from the\n// A2A barrel.\nexport { CAPABILITY_NAME_SEPARATOR, formatCapabilityName };\n\n/**\n * Parse a capability name produced by this provider, returning A2A-specific\n * field names (`remoteName` / `skillId`). Thin adapter over the shared\n * `parseCapabilityName` — kept local so callers see the A2A vocabulary.\n */\nexport function parseCapabilityName(\n name: string\n): { remoteName: string; skillId?: string } | null {\n const parsed = parseSharedName(name);\n if (!parsed) return null;\n return {\n remoteName: parsed.sourceName,\n skillId: parsed.itemName,\n };\n}\n\n/** Derive a Capability from a skill entry on the agent card. */\nexport function skillToCapability(\n remoteName: string,\n skill: A2ASkill\n): Capability {\n return {\n kind: CapabilityKind.A2A,\n name: formatCapabilityName(remoteName, skill.id),\n description: skill.description || skill.name || skill.id,\n schema: MESSAGE_INPUT_SCHEMA,\n authConfig: [],\n metadata: {\n category: 'a2a',\n tags: [remoteName, ...(skill.tags ?? [])],\n },\n };\n}\n\n/**\n * Default input schema for A2A capabilities. Every invocation is a text\n * message — we don't try to infer a per-skill input shape because the\n * A2A spec's skill entries don't include a parameter schema.\n */\nexport const MESSAGE_INPUT_SCHEMA = {\n type: 'object',\n properties: {\n message: {\n type: 'string',\n description:\n 'Instruction or question to send to the remote agent as a user message.',\n },\n },\n required: ['message'],\n} as const;\n\n/**\n * Coerce arbitrary LLM-supplied input into a valid A2A message. If the\n * input is a string, wraps it directly. If it's `{ message: \"...\" }`,\n * uses `message`. Otherwise JSON-stringifies so the agent receives\n * something it can try to interpret.\n *\n * When a skill id is known, it's prepended to the message text so the\n * remote agent can route — some A2A implementations use this, others\n * ignore it, it's best-effort.\n */\nexport function coerceInputToA2AMessage(\n input: unknown,\n skillId?: string\n): { role: 'user'; parts: Array<{ type: 'text'; text: string }> } {\n let text: string;\n if (typeof input === 'string') {\n text = input;\n } else if (input && typeof input === 'object' && 'message' in input) {\n const val = (input as { message: unknown }).message;\n text = typeof val === 'string' ? val : JSON.stringify(val);\n } else {\n text = JSON.stringify(input);\n }\n if (skillId) {\n text = `[skill: ${skillId}]\\n${text}`;\n }\n return { role: 'user', parts: [{ type: 'text', text }] };\n}\n"],"names":["parseSharedName"],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createHttpClient, assertOk } from '../../utils/httpClient.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A2A HTTP client — thin wrappers for the two operations the provider
|
|
5
|
+
* needs: fetching the agent card and sending JSON-RPC task requests.
|
|
6
|
+
*
|
|
7
|
+
* Uses axios (already in deps) via the shared httpClient utility so
|
|
8
|
+
* proxy config + headers work consistently with the tools-server provider.
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_CARD_PATH = '/.well-known/agent.json';
|
|
11
|
+
const DEFAULT_RPC_PATH = '/';
|
|
12
|
+
const JSON_RPC_VERSION = '2.0';
|
|
13
|
+
/**
|
|
14
|
+
* Lightweight single-remote A2A client. One instance per remote; the
|
|
15
|
+
* provider builds one per spec entry and caches them.
|
|
16
|
+
*/
|
|
17
|
+
class A2AClient {
|
|
18
|
+
http;
|
|
19
|
+
cardPath;
|
|
20
|
+
rpcPath;
|
|
21
|
+
constructor(opts) {
|
|
22
|
+
this.http = createHttpClient({
|
|
23
|
+
baseURL: opts.baseUrl,
|
|
24
|
+
headers: opts.headers,
|
|
25
|
+
timeoutMs: opts.timeoutMs,
|
|
26
|
+
});
|
|
27
|
+
this.cardPath = opts.cardPath ?? DEFAULT_CARD_PATH;
|
|
28
|
+
this.rpcPath = opts.rpcPath ?? DEFAULT_RPC_PATH;
|
|
29
|
+
}
|
|
30
|
+
/** GET /.well-known/agent.json (or the configured override). */
|
|
31
|
+
async fetchAgentCard() {
|
|
32
|
+
const res = await this.http.get(this.cardPath);
|
|
33
|
+
assertOk(res.status, this.cardPath, res.data);
|
|
34
|
+
return res.data;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* POST a JSON-RPC `tasks/send` request and return the resulting Task.
|
|
38
|
+
* The caller supplies the params (id, sessionId, message); this helper
|
|
39
|
+
* wraps it in the JSON-RPC envelope, dispatches, and unwraps.
|
|
40
|
+
*/
|
|
41
|
+
async sendTask(params) {
|
|
42
|
+
const result = await this.rpc('tasks/send', params);
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
/** Low-level JSON-RPC dispatcher — exposed for tests / advanced callers. */
|
|
46
|
+
async rpc(method, params) {
|
|
47
|
+
const body = {
|
|
48
|
+
jsonrpc: JSON_RPC_VERSION,
|
|
49
|
+
id: generateRpcId(),
|
|
50
|
+
method,
|
|
51
|
+
params,
|
|
52
|
+
};
|
|
53
|
+
const res = await this.http.post(this.rpcPath, body);
|
|
54
|
+
assertOk(res.status, this.rpcPath, res.data);
|
|
55
|
+
const data = res.data;
|
|
56
|
+
if (data && 'error' in data && data.error) {
|
|
57
|
+
throw new Error(`A2A ${method} error: ${data.error.message} (code ${data.error.code})`);
|
|
58
|
+
}
|
|
59
|
+
if (data && 'result' in data)
|
|
60
|
+
return data.result;
|
|
61
|
+
throw new Error(`A2A ${method}: malformed response (no result)`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Generate a short opaque id for JSON-RPC requests. Not cryptographic —
|
|
66
|
+
* just unique enough to pair request/response in logs.
|
|
67
|
+
*/
|
|
68
|
+
function generateRpcId() {
|
|
69
|
+
return `a2a-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Extract the plain-text output from a completed Task's artifacts. If
|
|
73
|
+
* the task isn't `completed`, throws. If there are no text parts, returns
|
|
74
|
+
* an empty string rather than null so callers always get a string.
|
|
75
|
+
*/
|
|
76
|
+
function extractTaskText(task) {
|
|
77
|
+
if (task.status.state === 'failed') {
|
|
78
|
+
throw new Error(`A2A task ${task.id} failed: ${task.error?.message ?? 'unknown error'}`);
|
|
79
|
+
}
|
|
80
|
+
if (task.status.state !== 'completed') {
|
|
81
|
+
throw new Error(`A2A task ${task.id} not completed (state=${task.status.state})`);
|
|
82
|
+
}
|
|
83
|
+
const parts = task.artifacts?.flatMap((a) => a.parts) ?? [];
|
|
84
|
+
return parts.map((p) => p.text ?? '').join('\n\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { A2AClient, extractTaskText, generateRpcId };
|
|
88
|
+
//# sourceMappingURL=client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.mjs","sources":["../../../../src/providers/a2a/client.ts"],"sourcesContent":["/**\n * A2A HTTP client — thin wrappers for the two operations the provider\n * needs: fetching the agent card and sending JSON-RPC task requests.\n *\n * Uses axios (already in deps) via the shared httpClient utility so\n * proxy config + headers work consistently with the tools-server provider.\n */\n\nimport { createHttpClient, assertOk } from '@/utils/httpClient';\nimport type {\n A2AAgentCard,\n A2ATask,\n A2ATaskParams,\n JsonRpcRequest,\n JsonRpcResponse,\n} from './types';\n\nconst DEFAULT_CARD_PATH = '/.well-known/agent.json';\nconst DEFAULT_RPC_PATH = '/';\nconst JSON_RPC_VERSION = '2.0';\n\n/** Options for constructing a single-remote A2A client. */\nexport interface A2AClientOptions {\n baseUrl: string;\n headers?: Record<string, string>;\n timeoutMs?: number;\n cardPath?: string;\n rpcPath?: string;\n}\n\n/**\n * Lightweight single-remote A2A client. One instance per remote; the\n * provider builds one per spec entry and caches them.\n */\nexport class A2AClient {\n private readonly http: ReturnType<typeof createHttpClient>;\n private readonly cardPath: string;\n private readonly rpcPath: string;\n\n constructor(opts: A2AClientOptions) {\n this.http = createHttpClient({\n baseURL: opts.baseUrl,\n headers: opts.headers,\n timeoutMs: opts.timeoutMs,\n });\n this.cardPath = opts.cardPath ?? DEFAULT_CARD_PATH;\n this.rpcPath = opts.rpcPath ?? DEFAULT_RPC_PATH;\n }\n\n /** GET /.well-known/agent.json (or the configured override). */\n async fetchAgentCard(): Promise<A2AAgentCard> {\n const res = await this.http.get<A2AAgentCard>(this.cardPath);\n assertOk(res.status, this.cardPath, res.data);\n return res.data;\n }\n\n /**\n * POST a JSON-RPC `tasks/send` request and return the resulting Task.\n * The caller supplies the params (id, sessionId, message); this helper\n * wraps it in the JSON-RPC envelope, dispatches, and unwraps.\n */\n async sendTask(params: A2ATaskParams): Promise<A2ATask> {\n const result = await this.rpc<A2ATaskParams, A2ATask>('tasks/send', params);\n return result;\n }\n\n /** Low-level JSON-RPC dispatcher — exposed for tests / advanced callers. */\n async rpc<P, R>(method: string, params: P): Promise<R> {\n const body: JsonRpcRequest<P> = {\n jsonrpc: JSON_RPC_VERSION,\n id: generateRpcId(),\n method,\n params,\n };\n const res = await this.http.post<JsonRpcResponse<R>>(this.rpcPath, body);\n assertOk(res.status, this.rpcPath, res.data);\n\n const data = res.data;\n if (data && 'error' in data && data.error) {\n throw new Error(\n `A2A ${method} error: ${data.error.message} (code ${data.error.code})`\n );\n }\n if (data && 'result' in data) return data.result;\n throw new Error(`A2A ${method}: malformed response (no result)`);\n }\n}\n\n/**\n * Generate a short opaque id for JSON-RPC requests. Not cryptographic —\n * just unique enough to pair request/response in logs.\n */\nexport function generateRpcId(): string {\n return `a2a-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Extract the plain-text output from a completed Task's artifacts. If\n * the task isn't `completed`, throws. If there are no text parts, returns\n * an empty string rather than null so callers always get a string.\n */\nexport function extractTaskText(task: A2ATask): string {\n if (task.status.state === 'failed') {\n throw new Error(\n `A2A task ${task.id} failed: ${task.error?.message ?? 'unknown error'}`\n );\n }\n if (task.status.state !== 'completed') {\n throw new Error(\n `A2A task ${task.id} not completed (state=${task.status.state})`\n );\n }\n const parts = task.artifacts?.flatMap((a) => a.parts) ?? [];\n return parts.map((p) => p.text ?? '').join('\\n\\n');\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
* Read env values with fallbacks. Evaluated lazily so tests that mutate
|
|
9
|
+
* process.env get the current value.
|
|
10
|
+
*/
|
|
11
|
+
function getA2AEnvDefaults() {
|
|
12
|
+
return {
|
|
13
|
+
/**
|
|
14
|
+
* Identity sent to A2A-served remotes in user-agent / request headers.
|
|
15
|
+
* Precedence: constructor config → env var → library default.
|
|
16
|
+
*/
|
|
17
|
+
clientName: process.env.A2A_CLIENT_NAME ?? 'illuma-agents',
|
|
18
|
+
clientVersion: process.env.A2A_CLIENT_VERSION ?? '1.0.0',
|
|
19
|
+
timeoutMs: Number(process.env.A2A_REQUEST_TIMEOUT_MS ?? 30_000),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Console-routed default logger. */
|
|
23
|
+
const consoleLogger = {
|
|
24
|
+
// eslint-disable-next-line no-console
|
|
25
|
+
debug: (...args) => console.debug('[a2a]', ...args),
|
|
26
|
+
// eslint-disable-next-line no-console
|
|
27
|
+
info: (...args) => console.info('[a2a]', ...args),
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
warn: (...args) => console.warn('[a2a]', ...args),
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
error: (...args) => console.error('[a2a]', ...args),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { consoleLogger, getA2AEnvDefaults };
|
|
35
|
+
//# sourceMappingURL=config.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.mjs","sources":["../../../../src/providers/a2a/config.ts"],"sourcesContent":["/**\n * Environment-driven defaults + default logger for the A2A provider.\n *\n * Mirrors the MCP provider's config.ts so the two providers have the same\n * override story for hosts.\n */\n\nimport type { A2ALogger } from './types';\n\n/**\n * Read env values with fallbacks. Evaluated lazily so tests that mutate\n * process.env get the current value.\n */\nexport function getA2AEnvDefaults(): {\n clientName: string;\n clientVersion: string;\n timeoutMs: number;\n} {\n return {\n /**\n * Identity sent to A2A-served remotes in user-agent / request headers.\n * Precedence: constructor config → env var → library default.\n */\n clientName: process.env.A2A_CLIENT_NAME ?? 'illuma-agents',\n clientVersion: process.env.A2A_CLIENT_VERSION ?? '1.0.0',\n timeoutMs: Number(process.env.A2A_REQUEST_TIMEOUT_MS ?? 30_000),\n };\n}\n\n/** Console-routed default logger. */\nexport const consoleLogger: A2ALogger = {\n // eslint-disable-next-line no-console\n debug: (...args) => console.debug('[a2a]', ...args),\n // eslint-disable-next-line no-console\n info: (...args) => console.info('[a2a]', ...args),\n // eslint-disable-next-line no-console\n warn: (...args) => console.warn('[a2a]', ...args),\n // eslint-disable-next-line no-console\n error: (...args) => console.error('[a2a]', ...args),\n};\n"],"names":[],"mappings":"AAAA;;;;;AAKG;AAIH;;;AAGG;SACa,iBAAiB,GAAA;IAK/B,OAAO;AACL;;;AAGG;AACH,QAAA,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,eAAe;AAC1D,QAAA,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO;QACxD,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,MAAM,CAAC;KAChE;AACH;AAEA;AACO,MAAM,aAAa,GAAc;;AAEtC,IAAA,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;;AAEnD,IAAA,IAAI,EAAE,CAAC,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;;AAEjD,IAAA,IAAI,EAAE,CAAC,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;;AAEjD,IAAA,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;;;;;"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared capability-name encoding used by source-specific providers
|
|
3
|
+
* (MCP, A2A, and future providers that expose multi-level namespaces).
|
|
4
|
+
*
|
|
5
|
+
* A capability name encodes `<sourceName>__<itemName>` where:
|
|
6
|
+
* - `sourceName` is the provider-level identifier (MCP server name,
|
|
7
|
+
* A2A remote name, etc.)
|
|
8
|
+
* - `itemName` is the per-source sub-unit (MCP tool name, A2A skill id)
|
|
9
|
+
*
|
|
10
|
+
* Centralizing the separator + parsing here keeps both providers in lockstep
|
|
11
|
+
* and prevents collisions when re-exported from the provider barrel.
|
|
12
|
+
*/
|
|
13
|
+
/** Separator between source and item in an encoded capability name. */
|
|
14
|
+
const CAPABILITY_NAME_SEPARATOR = '__';
|
|
15
|
+
/** Compose a capability name. If `itemName` is omitted, returns just `sourceName`. */
|
|
16
|
+
function formatCapabilityName(sourceName, itemName) {
|
|
17
|
+
return itemName
|
|
18
|
+
? `${sourceName}${CAPABILITY_NAME_SEPARATOR}${itemName}`
|
|
19
|
+
: sourceName;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Parse a capability name into its components. Returns `null` when `name`
|
|
23
|
+
* is empty. When the name has no separator, `itemName` is undefined
|
|
24
|
+
* (the caller can treat that as a whole-source invocation).
|
|
25
|
+
*/
|
|
26
|
+
function parseCapabilityName(name) {
|
|
27
|
+
if (!name)
|
|
28
|
+
return null;
|
|
29
|
+
const sepIdx = name.indexOf(CAPABILITY_NAME_SEPARATOR);
|
|
30
|
+
if (sepIdx < 0)
|
|
31
|
+
return { sourceName: name };
|
|
32
|
+
return {
|
|
33
|
+
sourceName: name.slice(0, sepIdx),
|
|
34
|
+
itemName: name.slice(sepIdx + CAPABILITY_NAME_SEPARATOR.length),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { CAPABILITY_NAME_SEPARATOR, formatCapabilityName, parseCapabilityName };
|
|
39
|
+
//# sourceMappingURL=capabilityNaming.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilityNaming.mjs","sources":["../../../src/providers/capabilityNaming.ts"],"sourcesContent":["/**\n * Shared capability-name encoding used by source-specific providers\n * (MCP, A2A, and future providers that expose multi-level namespaces).\n *\n * A capability name encodes `<sourceName>__<itemName>` where:\n * - `sourceName` is the provider-level identifier (MCP server name,\n * A2A remote name, etc.)\n * - `itemName` is the per-source sub-unit (MCP tool name, A2A skill id)\n *\n * Centralizing the separator + parsing here keeps both providers in lockstep\n * and prevents collisions when re-exported from the provider barrel.\n */\n\n/** Separator between source and item in an encoded capability name. */\nexport const CAPABILITY_NAME_SEPARATOR = '__';\n\n/** Compose a capability name. If `itemName` is omitted, returns just `sourceName`. */\nexport function formatCapabilityName(\n sourceName: string,\n itemName?: string\n): string {\n return itemName\n ? `${sourceName}${CAPABILITY_NAME_SEPARATOR}${itemName}`\n : sourceName;\n}\n\n/**\n * Parse a capability name into its components. Returns `null` when `name`\n * is empty. When the name has no separator, `itemName` is undefined\n * (the caller can treat that as a whole-source invocation).\n */\nexport function parseCapabilityName(\n name: string\n): { sourceName: string; itemName?: string } | null {\n if (!name) return null;\n const sepIdx = name.indexOf(CAPABILITY_NAME_SEPARATOR);\n if (sepIdx < 0) return { sourceName: name };\n return {\n sourceName: name.slice(0, sepIdx),\n itemName: name.slice(sepIdx + CAPABILITY_NAME_SEPARATOR.length),\n };\n}\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;AAWG;AAEH;AACO,MAAM,yBAAyB,GAAG;AAEzC;AACM,SAAU,oBAAoB,CAClC,UAAkB,EAClB,QAAiB,EAAA;AAEjB,IAAA,OAAO;AACL,UAAE,CAAA,EAAG,UAAU,GAAG,yBAAyB,CAAA,EAAG,QAAQ,CAAA;UACpD,UAAU;AAChB;AAEA;;;;AAIG;AACG,SAAU,mBAAmB,CACjC,IAAY,EAAA;AAEZ,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,IAAI;IACtB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,yBAAyB,CAAC;IACtD,IAAI,MAAM,GAAG,CAAC;AAAE,QAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE;IAC3C,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC;QACjC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC;KAChE;AACH;;;;"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CompositeCapabilityProvider — fans out to multiple CapabilityProviders
|
|
3
|
+
* and merges their manifests + runnables into one.
|
|
4
|
+
*
|
|
5
|
+
* Use case: an agent that consumes tools from tools-server AND MCP servers
|
|
6
|
+
* AND (future) skills. The composite exposes a single CapabilityProvider
|
|
7
|
+
* interface to the agent runtime so it doesn't know or care how many
|
|
8
|
+
* backing sources exist.
|
|
9
|
+
*
|
|
10
|
+
* Precedence: later providers do NOT override earlier ones on name
|
|
11
|
+
* collision — collisions are logged and the first-registered capability
|
|
12
|
+
* wins. Callers should ensure providers expose disjoint name spaces.
|
|
13
|
+
*/
|
|
14
|
+
class CompositeCapabilityProvider {
|
|
15
|
+
providerId;
|
|
16
|
+
providers;
|
|
17
|
+
constructor(providers) {
|
|
18
|
+
if (!providers.length) {
|
|
19
|
+
throw new Error('CompositeCapabilityProvider: at least one provider is required');
|
|
20
|
+
}
|
|
21
|
+
this.providers = providers;
|
|
22
|
+
this.providerId = `composite:${providers.map((p) => p.providerId).join(',')}`;
|
|
23
|
+
}
|
|
24
|
+
async fetchManifest(filter) {
|
|
25
|
+
// Fetch all providers in parallel. One provider failing should not
|
|
26
|
+
// prevent others from contributing — log and continue.
|
|
27
|
+
const results = await Promise.allSettled(this.providers.map((p) => p.fetchManifest(filter)));
|
|
28
|
+
const merged = [];
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
for (let i = 0; i < results.length; i++) {
|
|
31
|
+
const result = results[i];
|
|
32
|
+
const provider = this.providers[i];
|
|
33
|
+
if (result.status === 'rejected') {
|
|
34
|
+
// DEBUG
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.debug(`[composite] provider ${provider.providerId} fetchManifest failed — ${String(result.reason)}`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
for (const cap of result.value) {
|
|
40
|
+
if (seen.has(cap.name)) {
|
|
41
|
+
// DEBUG
|
|
42
|
+
// eslint-disable-next-line no-console
|
|
43
|
+
console.debug(`[composite] name collision on "${cap.name}" from ${provider.providerId} — keeping first`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
seen.add(cap.name);
|
|
47
|
+
merged.push(cap);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// DEBUG
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console.debug(`[composite] merged manifest: ${merged.length} caps from ${this.providers.length} providers`);
|
|
53
|
+
return merged;
|
|
54
|
+
}
|
|
55
|
+
async createRunnables(capabilities, credentials) {
|
|
56
|
+
// Determine which capabilities belong to which provider by refetching
|
|
57
|
+
// each provider's manifest and intersecting with the requested set.
|
|
58
|
+
// This keeps providers stateless — we don't require Capability to
|
|
59
|
+
// carry a back-reference to its provider.
|
|
60
|
+
const capabilityNames = new Set(capabilities.map((c) => c.name));
|
|
61
|
+
const perProviderCaps = await Promise.all(this.providers.map(async (p) => {
|
|
62
|
+
const manifest = await p.fetchManifest();
|
|
63
|
+
return manifest.filter((c) => capabilityNames.has(c.name));
|
|
64
|
+
}));
|
|
65
|
+
const allRunnables = [];
|
|
66
|
+
for (let i = 0; i < this.providers.length; i++) {
|
|
67
|
+
const providerCaps = perProviderCaps[i];
|
|
68
|
+
if (!providerCaps.length)
|
|
69
|
+
continue;
|
|
70
|
+
const runnables = await this.providers[i].createRunnables(providerCaps, credentials);
|
|
71
|
+
allRunnables.push(...runnables);
|
|
72
|
+
}
|
|
73
|
+
return allRunnables;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { CompositeCapabilityProvider };
|
|
78
|
+
//# sourceMappingURL=CompositeCapabilityProvider.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CompositeCapabilityProvider.mjs","sources":["../../../../src/providers/composite/CompositeCapabilityProvider.ts"],"sourcesContent":["/**\n * CompositeCapabilityProvider — fans out to multiple CapabilityProviders\n * and merges their manifests + runnables into one.\n *\n * Use case: an agent that consumes tools from tools-server AND MCP servers\n * AND (future) skills. The composite exposes a single CapabilityProvider\n * interface to the agent runtime so it doesn't know or care how many\n * backing sources exist.\n *\n * Precedence: later providers do NOT override earlier ones on name\n * collision — collisions are logged and the first-registered capability\n * wins. Callers should ensure providers expose disjoint name spaces.\n */\n\nimport type { StructuredToolInterface } from '@langchain/core/tools';\nimport type {\n Capability,\n CapabilityFilter,\n CapabilityProvider,\n CredentialMap,\n} from '@/providers/types';\n\nexport class CompositeCapabilityProvider implements CapabilityProvider {\n readonly providerId: string;\n private readonly providers: CapabilityProvider[];\n\n constructor(providers: CapabilityProvider[]) {\n if (!providers.length) {\n throw new Error(\n 'CompositeCapabilityProvider: at least one provider is required'\n );\n }\n this.providers = providers;\n this.providerId = `composite:${providers.map((p) => p.providerId).join(',')}`;\n }\n\n async fetchManifest(filter?: CapabilityFilter): Promise<Capability[]> {\n // Fetch all providers in parallel. One provider failing should not\n // prevent others from contributing — log and continue.\n const results = await Promise.allSettled(\n this.providers.map((p) => p.fetchManifest(filter))\n );\n\n const merged: Capability[] = [];\n const seen = new Set<string>();\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const provider = this.providers[i];\n\n if (result.status === 'rejected') {\n // DEBUG\n // eslint-disable-next-line no-console\n console.debug(\n `[composite] provider ${provider.providerId} fetchManifest failed — ${String(result.reason)}`\n );\n continue;\n }\n\n for (const cap of result.value) {\n if (seen.has(cap.name)) {\n // DEBUG\n // eslint-disable-next-line no-console\n console.debug(\n `[composite] name collision on \"${cap.name}\" from ${provider.providerId} — keeping first`\n );\n continue;\n }\n seen.add(cap.name);\n merged.push(cap);\n }\n }\n\n // DEBUG\n // eslint-disable-next-line no-console\n console.debug(\n `[composite] merged manifest: ${merged.length} caps from ${this.providers.length} providers`\n );\n\n return merged;\n }\n\n async createRunnables(\n capabilities: Capability[],\n credentials: CredentialMap\n ): Promise<StructuredToolInterface[]> {\n // Determine which capabilities belong to which provider by refetching\n // each provider's manifest and intersecting with the requested set.\n // This keeps providers stateless — we don't require Capability to\n // carry a back-reference to its provider.\n const capabilityNames = new Set(capabilities.map((c) => c.name));\n const perProviderCaps = await Promise.all(\n this.providers.map(async (p) => {\n const manifest = await p.fetchManifest();\n return manifest.filter((c) => capabilityNames.has(c.name));\n })\n );\n\n const allRunnables: StructuredToolInterface[] = [];\n for (let i = 0; i < this.providers.length; i++) {\n const providerCaps = perProviderCaps[i];\n if (!providerCaps.length) continue;\n const runnables = await this.providers[i].createRunnables(\n providerCaps,\n credentials\n );\n allRunnables.push(...runnables);\n }\n\n return allRunnables;\n }\n}\n"],"names":[],"mappings":"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"}
|