feat: add initial A2A runtime integration#1
Conversation
There was a problem hiding this comment.
Pull request overview
Adds initial inbound A2A runtime integration to Camel Agent, including runtime routes, exposed-agent publishing, and persistence/audit correlation of A2A task & conversation metadata.
Changes:
- Introduces A2A runtime bootstrap/binding and Undertow routes for
/a2a/rpc,/a2a/sse/{taskId}, and/.well-known/agent-card.json. - Adds an exposed-agent catalog (YAML loader + agent-card generation) mapping public A2A agent cards to local plan name/version.
- Persists and surfaces A2A correlation metadata in persistence/audit views, with focused unit tests.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pom.xml | Adds ${a2a.version} property for the new A2A component dependency. |
| camel-agent-core/pom.xml | Adds camel-a2a-component dependency to core runtime. |
| docs/roadmap.md | Adds roadmap entry referencing the A2A integration work. |
| docs/pr-drafts/a2a-runtime-integration.md | Adds PR draft/design notes for the A2A runtime integration. |
| camel-agent-persistence-dscope/.../DscopePersistenceFacade.java | Extends stored correlation metadata to include A2A identifiers. |
| camel-agent-core/.../runtime/RuntimeResourceBootstrapper.java | Stages remote exposed-agent config when configured via HTTP URL. |
| camel-agent-core/.../runtime/AgentRuntimeBootstrap.java | Wires A2A protocol beans + A2A runtime route builder when enabled. |
| camel-agent-core/.../runtime/AgentA2ARuntimeRouteBuilder.java | Adds Undertow routes that dispatch to A2A component processors via beans. |
| camel-agent-core/.../runtime/A2ARuntimeProperties.java | Adds A2A runtime configuration parsing and URL/path helpers. |
| camel-agent-core/.../config/CorrelationKeys.java | Adds A2A correlation key constants. |
| camel-agent-core/.../config/AgentHeaders.java | Adds A2A header constants for propagating correlation into the agent component. |
| camel-agent-core/.../component/AgentProducer.java | Binds incoming A2A correlation headers into the correlation registry. |
| camel-agent-core/.../audit/AuditMetadataSupport.java | Derives A2A correlation from events and surfaces it in conversation metadata. |
| camel-agent-core/.../audit/AuditConversationViewProcessor.java | Adds a2a metadata block to conversation view responses. |
| camel-agent-core/.../a2a/AgentA2ATaskRepository.java | Adds in-memory + persisted A2A task storage and conversation event appends. |
| camel-agent-core/.../a2a/AgentA2AProtocolSupport.java | Binds A2A processors/services/agent-card catalog into Camel Main when enabled. |
| camel-agent-core/.../a2a/AgentA2AAgentCardCatalog.java | Generates discovery/extended agent cards from exposed-agent catalog. |
| camel-agent-core/.../a2a/A2AExposedAgentSpec.java | Defines exposed-agent spec model for YAML config. |
| camel-agent-core/.../a2a/A2AExposedAgentCatalogLoader.java | Loads exposed-agent YAML config from classpath/file/http(s). |
| camel-agent-core/.../a2a/A2AExposedAgentCatalog.java | Validates and provides lookup/default behavior for exposed agents. |
| camel-agent-core/src/test/.../A2AExposedAgentCatalogLoaderTest.java | Tests exposed-agent catalog loading and validation. |
| camel-agent-core/src/test/.../AgentA2ATaskRepositoryTest.java | Tests task create/cancel + persisted conversation audit event behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| bind(main, A2AComponentApplicationSupport.BEAN_TASK_EVENT_SERVICE, taskEventService); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_PUSH_CONFIG_SERVICE, pushConfigService); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_TASK_SERVICE, taskRepository); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_AGENT_CARD_CATALOG, agentCardCatalog); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_CREATE_PUSH_CONFIG_PROCESSOR, createPushConfigProcessor); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_GET_PUSH_CONFIG_PROCESSOR, getPushConfigProcessor); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_LIST_PUSH_CONFIGS_PROCESSOR, listPushConfigsProcessor); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_DELETE_PUSH_CONFIG_PROCESSOR, deletePushConfigProcessor); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_ENVELOPE_PROCESSOR, new A2AJsonRpcEnvelopeProcessor(A2AProtocolMethods.CORE_METHODS)); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_ERROR_PROCESSOR, new A2AErrorProcessor()); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_METHOD_PROCESSOR, new A2AMethodDispatchProcessor(methods)); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_SSE_PROCESSOR, new A2ATaskSseProcessor(taskEventService)); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_AGENT_CARD_DISCOVERY_PROCESSOR, new AgentCardDiscoveryProcessor(agentCardCatalog)); | ||
| bind(main, A2AComponentApplicationSupport.BEAN_GET_EXTENDED_AGENT_CARD_PROCESSOR, getExtendedAgentCardProcessor); |
There was a problem hiding this comment.
The A2A beans are only bound if missing, but this method still constructs new taskEventService, taskRepository, agentCardCatalog, and processors that are wired together. If any of the A2AComponentApplicationSupport.BEAN_* names are already present in the Main registry, you can end up with routes using the pre-existing beans while other newly-created processors/services are used elsewhere, causing inconsistent state (e.g., SSE processor using a different event service than the task repository publishes to). Consider either (1) treating these beans as an atomic set (fail fast if any already exist), (2) always overriding them when A2A is enabled, or (3) looking up and reusing existing beans instead of creating new ones.
| current.setUpdatedAt(Instant.now().toString()); | ||
| tasks.put(taskId, current); | ||
| historyByTaskId.computeIfAbsent(taskId, ignored -> new ArrayList<>()).add(copyStatus(canceled)); | ||
| eventService.publishTaskUpdate(current); |
There was a problem hiding this comment.
historyByTaskId stores mutable ArrayList instances inside a ConcurrentMap and later mutates them (e.g., .add(...)). This is not thread-safe and can lead to lost updates or ConcurrentModificationException when history is read while another thread updates it. Use a thread-safe list (e.g., CopyOnWriteArrayList) or update the map with a new immutable list on each change.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
camel-agent-core/src/main/java/io/dscope/camel/agent/a2a/AgentA2ATaskRepository.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 64 out of 65 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
camel-agent-core/src/main/java/io/dscope/camel/agent/runtime/A2ARuntimeProperties.java
Outdated
Show resolved
Hide resolved
camel-agent-core/src/main/java/io/dscope/camel/agent/runtime/AgentRuntimeBootstrap.java
Outdated
Show resolved
Hide resolved
camel-agent-core/src/main/java/io/dscope/camel/agent/a2a/AgentA2AProtocolSupport.java
Show resolved
Hide resolved
camel-agent-core/src/main/java/io/dscope/camel/agent/a2a/A2AToolClient.java
Outdated
Show resolved
Hide resolved
...gent-support-service/src/main/java/io/dscope/camel/agent/samples/SampleAdminMcpBindings.java
Outdated
Show resolved
Hide resolved
camel-agent-core/src/main/java/io/dscope/camel/agent/a2a/A2AExposedAgentCatalog.java
Outdated
Show resolved
Hide resolved
|
@copilot open a new pull request to apply changes based on the comments in this thread |
… issues Co-authored-by: rdobrik <8812511+rdobrik@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
… InMemoryPersistenceFacade Co-authored-by: rdobrik <8812511+rdobrik@users.noreply.github.com>
… InMemoryPersistenceFacade Co-authored-by: rdobrik <8812511+rdobrik@users.noreply.github.com>
Co-authored-by: rdobrik <8812511+rdobrik@users.noreply.github.com>
Co-authored-by: rdobrik <8812511+rdobrik@users.noreply.github.com>
Normalize whitespace for all required fields in A2AExposedAgentCatalog
Fix broken absolute filesystem path in roadmap.md
fix: use CopyOnWriteArrayList for thread-safe conversation history in InMemoryPersistenceFacade
fix: address A2A runtime review feedback — interface types, port fallback, reflection, and field normalization
Summary
This PR adds the first end-to-end Camel Agent A2A integration slice: it exposes local plans over the existing
camel-a2a-componentruntime surface and lets agent tools call remote A2A runtimes througha2a:endpoints while preserving correlation and audit metadata.Scope
POST /a2a/rpc,GET /a2a/sse/{taskId}, andGET /.well-known/agent-card.json{planName, planVersion}a2a:tool execution over JSON-RPC HTTP with correlation reuse for follow-up task callsImplementation notes
agents.yamlremains the internal local plan cataloga2a:targets in the Camel tool executor, call the remote runtime over JSON-RPC, and bind returned remote task identifiers back into the local conversation correlation stateVerification
./mvnw -q -pl camel-agent-core -Dtest=A2AExposedAgentCatalogLoaderTest,AgentA2ATaskRepositoryTest,A2AToolClientTest test./mvnw -q -pl camel-agent-core,camel-agent-starter -am -DskipTests compile./mvnw -q -pl camel-agent-core,camel-agent-persistence-dscope -am -DskipTests compileKnown gaps
samples/agent-support-servicebecause of pre-existing missing sample classes insrc/main/java/io/dscope/camel/agent/samples/Main.java