@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.
- 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 +56 -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/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 +13 -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/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/index.d.ts +12 -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/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 +16 -1
- package/src/graphs/Graph.ts +0 -23
- package/src/index.ts +4 -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/index.ts +68 -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/utils/__tests__/credentials.test.ts +130 -0
- 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,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CapabilityKind,
|
|
3
|
+
AuthSource,
|
|
4
|
+
type Capability,
|
|
5
|
+
type CapabilityProvider,
|
|
6
|
+
type CredentialMap,
|
|
7
|
+
} from '../types';
|
|
8
|
+
|
|
9
|
+
describe('CapabilityProvider types', () => {
|
|
10
|
+
it('CapabilityKind enum covers tool/skill/mcp', () => {
|
|
11
|
+
expect(CapabilityKind.TOOL).toBe('tool');
|
|
12
|
+
expect(CapabilityKind.SKILL).toBe('skill');
|
|
13
|
+
expect(CapabilityKind.MCP).toBe('mcp');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('AuthSource enum covers server/user/forwarded', () => {
|
|
17
|
+
expect(AuthSource.SERVER).toBe('server');
|
|
18
|
+
expect(AuthSource.USER).toBe('user');
|
|
19
|
+
expect(AuthSource.FORWARDED).toBe('forwarded');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('Capability shape reserves upstream metadata fields', () => {
|
|
23
|
+
const cap: Capability = {
|
|
24
|
+
kind: CapabilityKind.TOOL,
|
|
25
|
+
name: 'sample',
|
|
26
|
+
description: 'test capability',
|
|
27
|
+
schema: { type: 'object' },
|
|
28
|
+
authConfig: [],
|
|
29
|
+
metadata: {
|
|
30
|
+
icon: '/icon.svg',
|
|
31
|
+
category: 'test',
|
|
32
|
+
tags: ['a', 'b'],
|
|
33
|
+
auth: 'oauth',
|
|
34
|
+
expires_at: 1735689600,
|
|
35
|
+
injectedMessages: false,
|
|
36
|
+
sessionAware: false,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
expect(cap.name).toBe('sample');
|
|
40
|
+
expect(cap.metadata.expires_at).toBe(1735689600);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('CredentialMap is a plain string record', () => {
|
|
44
|
+
const creds: CredentialMap = {
|
|
45
|
+
OPENAI_API_KEY: 'sk-xxx',
|
|
46
|
+
WOLFRAM_APP_ID: 'abc123',
|
|
47
|
+
};
|
|
48
|
+
expect(Object.keys(creds)).toHaveLength(2);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('CapabilityProvider is an implementable contract', () => {
|
|
52
|
+
class FakeProvider implements CapabilityProvider {
|
|
53
|
+
readonly providerId = 'fake';
|
|
54
|
+
async fetchManifest() {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
async createRunnables() {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const p: CapabilityProvider = new FakeProvider();
|
|
62
|
+
expect(p.providerId).toBe('fake');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2ACapabilityProvider — client-side adapter for consuming A2A-served
|
|
3
|
+
* remote agents as capabilities.
|
|
4
|
+
*
|
|
5
|
+
* Same pattern as `MCPCapabilityProvider`: take a unified spec map,
|
|
6
|
+
* speak the protocol (A2A JSON-RPC over HTTP), expose each skill as a
|
|
7
|
+
* `Capability` / `StructuredTool`.
|
|
8
|
+
*
|
|
9
|
+
* Design:
|
|
10
|
+
* - One `A2AClient` per remote spec, cached after first use.
|
|
11
|
+
* - `fetchManifest()`:
|
|
12
|
+
* for each remote, GET /.well-known/agent.json → emit one
|
|
13
|
+
* Capability per advertised skill (or one per remote when
|
|
14
|
+
* `flattenAsSingleTool` is set).
|
|
15
|
+
* - `createRunnables()`:
|
|
16
|
+
* each Capability becomes a StructuredTool whose invoke sends a
|
|
17
|
+
* JSON-RPC `tasks/send` to the remote and returns the final
|
|
18
|
+
* artifact's text.
|
|
19
|
+
*
|
|
20
|
+
* What the host supplies:
|
|
21
|
+
* - `remotes`: unified spec map (normalized from whatever source).
|
|
22
|
+
* - `getAuthHeaders?`: per-remote header resolver. Called at connect
|
|
23
|
+
* time. OAuth / token refresh stays in the host.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { tool, type StructuredToolInterface } from '@langchain/core/tools';
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
CapabilityKind,
|
|
30
|
+
type Capability,
|
|
31
|
+
type CapabilityFilter,
|
|
32
|
+
type CapabilityProvider,
|
|
33
|
+
type CredentialMap,
|
|
34
|
+
} from '@/providers/types';
|
|
35
|
+
import {
|
|
36
|
+
CAPABILITY_NAME_SEPARATOR,
|
|
37
|
+
formatCapabilityName,
|
|
38
|
+
parseCapabilityName as parseSharedName,
|
|
39
|
+
} from '@/providers/capabilityNaming';
|
|
40
|
+
import { A2AClient, extractTaskText, generateRpcId } from './client';
|
|
41
|
+
import { consoleLogger, getA2AEnvDefaults } from './config';
|
|
42
|
+
import type {
|
|
43
|
+
A2AAgentCard,
|
|
44
|
+
A2ALogger,
|
|
45
|
+
A2AProviderConfig,
|
|
46
|
+
A2ARemoteSpec,
|
|
47
|
+
A2ASkill,
|
|
48
|
+
} from './types';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Per-remote cached state. The agent card is fetched lazily and reused.
|
|
52
|
+
*/
|
|
53
|
+
interface RemoteConnection {
|
|
54
|
+
remoteName: string;
|
|
55
|
+
spec: A2ARemoteSpec;
|
|
56
|
+
client: A2AClient;
|
|
57
|
+
card: A2AAgentCard;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class A2ACapabilityProvider implements CapabilityProvider {
|
|
61
|
+
readonly providerId: string;
|
|
62
|
+
private readonly config: Required<
|
|
63
|
+
Pick<A2AProviderConfig, 'clientInfo' | 'timeoutMs'>
|
|
64
|
+
> &
|
|
65
|
+
Pick<A2AProviderConfig, 'remotes' | 'getAuthHeaders'>;
|
|
66
|
+
private readonly logger: A2ALogger;
|
|
67
|
+
private readonly connections = new Map<string, RemoteConnection>();
|
|
68
|
+
|
|
69
|
+
constructor(config: A2AProviderConfig) {
|
|
70
|
+
if (!config.remotes || Object.keys(config.remotes).length === 0) {
|
|
71
|
+
throw new Error('A2ACapabilityProvider: at least one remote is required');
|
|
72
|
+
}
|
|
73
|
+
const env = getA2AEnvDefaults();
|
|
74
|
+
this.config = {
|
|
75
|
+
remotes: config.remotes,
|
|
76
|
+
getAuthHeaders: config.getAuthHeaders,
|
|
77
|
+
clientInfo: config.clientInfo ?? {
|
|
78
|
+
name: env.clientName,
|
|
79
|
+
version: env.clientVersion,
|
|
80
|
+
},
|
|
81
|
+
timeoutMs: config.timeoutMs ?? env.timeoutMs,
|
|
82
|
+
};
|
|
83
|
+
this.logger = config.logger ?? consoleLogger;
|
|
84
|
+
const names = Object.keys(config.remotes).sort().join(',');
|
|
85
|
+
this.providerId = `a2a:${names}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async fetchManifest(filter?: CapabilityFilter): Promise<Capability[]> {
|
|
89
|
+
if (filter?.kind && filter.kind !== CapabilityKind.A2A) return [];
|
|
90
|
+
|
|
91
|
+
const capabilities: Capability[] = [];
|
|
92
|
+
for (const remoteName of Object.keys(this.config.remotes)) {
|
|
93
|
+
try {
|
|
94
|
+
const conn = await this.ensureConnection(remoteName);
|
|
95
|
+
const caps = this.cardToCapabilities(remoteName, conn.card, conn.spec);
|
|
96
|
+
for (const cap of caps) {
|
|
97
|
+
if (this.capabilityMatchesFilter(cap, filter)) capabilities.push(cap);
|
|
98
|
+
}
|
|
99
|
+
} catch (err) {
|
|
100
|
+
// One remote failing must not block others — log and continue.
|
|
101
|
+
this.logger.warn(
|
|
102
|
+
`[a2a] failed to fetch manifest for remote "${remoteName}":`,
|
|
103
|
+
err instanceof Error ? err.message : err
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.logger.debug(
|
|
109
|
+
`[a2a] manifest assembled — ${capabilities.length} capabilities across ${this.connections.size}/${Object.keys(this.config.remotes).length} connected remotes`
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return capabilities;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async createRunnables(
|
|
116
|
+
capabilities: Capability[],
|
|
117
|
+
_credentials: CredentialMap
|
|
118
|
+
): Promise<StructuredToolInterface[]> {
|
|
119
|
+
const runnables: StructuredToolInterface[] = [];
|
|
120
|
+
for (const cap of capabilities) {
|
|
121
|
+
if (cap.kind !== CapabilityKind.A2A) continue;
|
|
122
|
+
const parsed = parseCapabilityName(cap.name);
|
|
123
|
+
if (!parsed) {
|
|
124
|
+
this.logger.warn(
|
|
125
|
+
`[a2a] skipping capability "${cap.name}" — unparseable`
|
|
126
|
+
);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
runnables.push(
|
|
130
|
+
this.buildProxyTool(parsed.remoteName, parsed.skillId, cap)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return runnables;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Disconnect any cached clients. A2A is stateless over HTTP so this
|
|
137
|
+
* is effectively a cache clear — no sockets to close. */
|
|
138
|
+
async close(): Promise<void> {
|
|
139
|
+
this.connections.clear();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// --- internals ---------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
private async ensureConnection(
|
|
145
|
+
remoteName: string
|
|
146
|
+
): Promise<RemoteConnection> {
|
|
147
|
+
const existing = this.connections.get(remoteName);
|
|
148
|
+
if (existing) return existing;
|
|
149
|
+
|
|
150
|
+
const spec = this.config.remotes[remoteName];
|
|
151
|
+
if (!spec) {
|
|
152
|
+
throw new Error(`[a2a] remote "${remoteName}" not in config`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const authHeaders = await this.resolveAuthHeaders(remoteName);
|
|
156
|
+
const headers = {
|
|
157
|
+
...(spec.headers ?? {}),
|
|
158
|
+
...(authHeaders ?? {}),
|
|
159
|
+
'user-agent': `${this.config.clientInfo.name}/${this.config.clientInfo.version}`,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const client = new A2AClient({
|
|
163
|
+
baseUrl: spec.url,
|
|
164
|
+
headers,
|
|
165
|
+
timeoutMs: this.config.timeoutMs,
|
|
166
|
+
cardPath: spec.cardPath,
|
|
167
|
+
rpcPath: spec.rpcPath,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
this.logger.debug(
|
|
171
|
+
`[a2a] fetching card from "${remoteName}" at ${spec.url}`
|
|
172
|
+
);
|
|
173
|
+
const card = await client.fetchAgentCard();
|
|
174
|
+
this.logger.debug(
|
|
175
|
+
`[a2a] connected to "${remoteName}" — ${card.skills.length ?? 0} skills discovered`
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const conn: RemoteConnection = { remoteName, spec, client, card };
|
|
179
|
+
this.connections.set(remoteName, conn);
|
|
180
|
+
return conn;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private async resolveAuthHeaders(
|
|
184
|
+
remoteName: string
|
|
185
|
+
): Promise<Record<string, string> | undefined> {
|
|
186
|
+
if (!this.config.getAuthHeaders) return undefined;
|
|
187
|
+
try {
|
|
188
|
+
return await this.config.getAuthHeaders(remoteName);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
this.logger.warn(
|
|
191
|
+
`[a2a] getAuthHeaders("${remoteName}") threw:`,
|
|
192
|
+
err instanceof Error ? err.message : err
|
|
193
|
+
);
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private cardToCapabilities(
|
|
199
|
+
remoteName: string,
|
|
200
|
+
card: A2AAgentCard,
|
|
201
|
+
spec: A2ARemoteSpec
|
|
202
|
+
): Capability[] {
|
|
203
|
+
if (spec.flattenAsSingleTool) {
|
|
204
|
+
// One capability per remote, named after the remote. Useful when
|
|
205
|
+
// the card has no skills listed or you want coarse routing.
|
|
206
|
+
return [
|
|
207
|
+
{
|
|
208
|
+
kind: CapabilityKind.A2A,
|
|
209
|
+
name: formatCapabilityName(remoteName),
|
|
210
|
+
description: card.description ?? card.name,
|
|
211
|
+
schema: MESSAGE_INPUT_SCHEMA,
|
|
212
|
+
authConfig: [],
|
|
213
|
+
metadata: {
|
|
214
|
+
category: 'a2a',
|
|
215
|
+
tags: [remoteName, ...(card.skills.map((s) => s.id) ?? [])],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!card.skills || card.skills.length === 0) {
|
|
222
|
+
// No skills advertised — expose a single message endpoint so the
|
|
223
|
+
// agent can still invoke the remote with free-form text.
|
|
224
|
+
return [
|
|
225
|
+
{
|
|
226
|
+
kind: CapabilityKind.A2A,
|
|
227
|
+
name: formatCapabilityName(remoteName),
|
|
228
|
+
description:
|
|
229
|
+
card.description ?? `Delegate to remote agent "${card.name}".`,
|
|
230
|
+
schema: MESSAGE_INPUT_SCHEMA,
|
|
231
|
+
authConfig: [],
|
|
232
|
+
metadata: { category: 'a2a', tags: [remoteName] },
|
|
233
|
+
},
|
|
234
|
+
];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return card.skills.map(
|
|
238
|
+
(skill): Capability => skillToCapability(remoteName, skill)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private capabilityMatchesFilter(
|
|
243
|
+
cap: Capability,
|
|
244
|
+
filter?: CapabilityFilter
|
|
245
|
+
): boolean {
|
|
246
|
+
if (!filter) return true;
|
|
247
|
+
if (filter.kind && cap.kind !== filter.kind) return false;
|
|
248
|
+
if (filter.names && !filter.names.includes(cap.name)) return false;
|
|
249
|
+
if (filter.tags?.length) {
|
|
250
|
+
const capTags = new Set(cap.metadata.tags ?? []);
|
|
251
|
+
if (!filter.tags.some((t) => capTags.has(t))) return false;
|
|
252
|
+
}
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private buildProxyTool(
|
|
257
|
+
remoteName: string,
|
|
258
|
+
skillId: string | undefined,
|
|
259
|
+
cap: Capability
|
|
260
|
+
): StructuredToolInterface {
|
|
261
|
+
const logger = this.logger;
|
|
262
|
+
const ensureConnection = this.ensureConnection.bind(this);
|
|
263
|
+
|
|
264
|
+
return tool(
|
|
265
|
+
async (input: unknown): Promise<string> => {
|
|
266
|
+
// DEBUG: log tool invocation (remove after POC stabilizes)
|
|
267
|
+
logger.debug(
|
|
268
|
+
`[a2a] invoking ${remoteName}${skillId ? `:${skillId}` : ''}`
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const message = coerceInputToA2AMessage(input, skillId);
|
|
272
|
+
const conn = await ensureConnection(remoteName);
|
|
273
|
+
const task = await conn.client.sendTask({
|
|
274
|
+
id: generateRpcId(),
|
|
275
|
+
message,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
return extractTaskText(task);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
throw new Error(
|
|
282
|
+
`A2A ${remoteName}${skillId ? `:${skillId}` : ''} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: cap.name,
|
|
288
|
+
description: cap.description,
|
|
289
|
+
schema: (cap.schema as object) ?? MESSAGE_INPUT_SCHEMA,
|
|
290
|
+
responseFormat: 'content',
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
// Helpers exported for testing
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
// CAPABILITY_NAME_SEPARATOR + formatCapabilityName live in the shared
|
|
301
|
+
// `@/providers/capabilityNaming` module so every provider uses the same
|
|
302
|
+
// encoding. Re-exported here for backward-compatible imports from the
|
|
303
|
+
// A2A barrel.
|
|
304
|
+
export { CAPABILITY_NAME_SEPARATOR, formatCapabilityName };
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Parse a capability name produced by this provider, returning A2A-specific
|
|
308
|
+
* field names (`remoteName` / `skillId`). Thin adapter over the shared
|
|
309
|
+
* `parseCapabilityName` — kept local so callers see the A2A vocabulary.
|
|
310
|
+
*/
|
|
311
|
+
export function parseCapabilityName(
|
|
312
|
+
name: string
|
|
313
|
+
): { remoteName: string; skillId?: string } | null {
|
|
314
|
+
const parsed = parseSharedName(name);
|
|
315
|
+
if (!parsed) return null;
|
|
316
|
+
return {
|
|
317
|
+
remoteName: parsed.sourceName,
|
|
318
|
+
skillId: parsed.itemName,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Derive a Capability from a skill entry on the agent card. */
|
|
323
|
+
export function skillToCapability(
|
|
324
|
+
remoteName: string,
|
|
325
|
+
skill: A2ASkill
|
|
326
|
+
): Capability {
|
|
327
|
+
return {
|
|
328
|
+
kind: CapabilityKind.A2A,
|
|
329
|
+
name: formatCapabilityName(remoteName, skill.id),
|
|
330
|
+
description: skill.description || skill.name || skill.id,
|
|
331
|
+
schema: MESSAGE_INPUT_SCHEMA,
|
|
332
|
+
authConfig: [],
|
|
333
|
+
metadata: {
|
|
334
|
+
category: 'a2a',
|
|
335
|
+
tags: [remoteName, ...(skill.tags ?? [])],
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Default input schema for A2A capabilities. Every invocation is a text
|
|
342
|
+
* message — we don't try to infer a per-skill input shape because the
|
|
343
|
+
* A2A spec's skill entries don't include a parameter schema.
|
|
344
|
+
*/
|
|
345
|
+
export const MESSAGE_INPUT_SCHEMA = {
|
|
346
|
+
type: 'object',
|
|
347
|
+
properties: {
|
|
348
|
+
message: {
|
|
349
|
+
type: 'string',
|
|
350
|
+
description:
|
|
351
|
+
'Instruction or question to send to the remote agent as a user message.',
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
required: ['message'],
|
|
355
|
+
} as const;
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Coerce arbitrary LLM-supplied input into a valid A2A message. If the
|
|
359
|
+
* input is a string, wraps it directly. If it's `{ message: "..." }`,
|
|
360
|
+
* uses `message`. Otherwise JSON-stringifies so the agent receives
|
|
361
|
+
* something it can try to interpret.
|
|
362
|
+
*
|
|
363
|
+
* When a skill id is known, it's prepended to the message text so the
|
|
364
|
+
* remote agent can route — some A2A implementations use this, others
|
|
365
|
+
* ignore it, it's best-effort.
|
|
366
|
+
*/
|
|
367
|
+
export function coerceInputToA2AMessage(
|
|
368
|
+
input: unknown,
|
|
369
|
+
skillId?: string
|
|
370
|
+
): { role: 'user'; parts: Array<{ type: 'text'; text: string }> } {
|
|
371
|
+
let text: string;
|
|
372
|
+
if (typeof input === 'string') {
|
|
373
|
+
text = input;
|
|
374
|
+
} else if (input && typeof input === 'object' && 'message' in input) {
|
|
375
|
+
const val = (input as { message: unknown }).message;
|
|
376
|
+
text = typeof val === 'string' ? val : JSON.stringify(val);
|
|
377
|
+
} else {
|
|
378
|
+
text = JSON.stringify(input);
|
|
379
|
+
}
|
|
380
|
+
if (skillId) {
|
|
381
|
+
text = `[skill: ${skillId}]\n${text}`;
|
|
382
|
+
}
|
|
383
|
+
return { role: 'user', parts: [{ type: 'text', text }] };
|
|
384
|
+
}
|