a2a_protocol_server/executor.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Tom F.
3
4//! Agent executor trait.
5//!
6//! [`AgentExecutor`] is the primary extension point for implementing A2A agent
7//! logic. The server framework calls [`execute`](AgentExecutor::execute) for
8//! every incoming `message/send` or `message/stream` request and
9//! [`cancel`](AgentExecutor::cancel) for `tasks/cancel`.
10
11use std::future::Future;
12use std::pin::Pin;
13
14use a2a_protocol_types::error::A2aResult;
15
16use crate::request_context::RequestContext;
17use crate::streaming::EventQueueWriter;
18
19/// Trait for implementing A2A agent execution logic.
20///
21/// Implementors process incoming messages by writing events (status updates,
22/// artifacts) to the provided [`EventQueueWriter`]. The executor runs in a
23/// spawned task and should signal completion by writing a terminal status
24/// update and returning `Ok(())`.
25///
26/// # Object safety
27///
28/// This trait is object-safe: methods return `Pin<Box<dyn Future>>` so that
29/// executors can be used as `Arc<dyn AgentExecutor>`. This eliminates the
30/// need for generic parameters on [`RequestHandler`](crate::RequestHandler),
31/// [`RestDispatcher`](crate::RestDispatcher), and
32/// [`JsonRpcDispatcher`](crate::JsonRpcDispatcher), simplifying the entire
33/// server API surface.
34///
35/// # Example
36///
37/// ```rust,no_run
38/// use std::pin::Pin;
39/// use std::future::Future;
40/// use a2a_protocol_server::executor::AgentExecutor;
41/// use a2a_protocol_server::request_context::RequestContext;
42/// use a2a_protocol_server::streaming::EventQueueWriter;
43/// use a2a_protocol_types::error::A2aResult;
44///
45/// struct MyAgent;
46///
47/// impl AgentExecutor for MyAgent {
48/// fn execute<'a>(
49/// &'a self,
50/// ctx: &'a RequestContext,
51/// queue: &'a dyn EventQueueWriter,
52/// ) -> Pin<Box<dyn Future<Output = A2aResult<()>> + Send + 'a>> {
53/// Box::pin(async move {
54/// // Write status updates and artifacts to `queue`.
55/// Ok(())
56/// })
57/// }
58/// }
59/// ```
60pub trait AgentExecutor: Send + Sync + 'static {
61 /// Executes agent logic for the given request.
62 ///
63 /// Write [`StreamResponse`](a2a_protocol_types::events::StreamResponse) events to
64 /// `queue` as the agent progresses. The method should return `Ok(())`
65 /// after writing the final event, or `Err(...)` on failure.
66 ///
67 /// # Errors
68 ///
69 /// Returns an [`A2aError`](a2a_protocol_types::error::A2aError) if execution fails.
70 fn execute<'a>(
71 &'a self,
72 ctx: &'a RequestContext,
73 queue: &'a dyn EventQueueWriter,
74 ) -> Pin<Box<dyn Future<Output = A2aResult<()>> + Send + 'a>>;
75
76 /// Cancels an in-progress task.
77 ///
78 /// The default implementation returns an error indicating the task is not
79 /// cancelable. Override this to support task cancellation.
80 ///
81 /// # Errors
82 ///
83 /// Returns an [`A2aError`](a2a_protocol_types::error::A2aError) if cancellation fails
84 /// or is not supported.
85 fn cancel<'a>(
86 &'a self,
87 ctx: &'a RequestContext,
88 _queue: &'a dyn EventQueueWriter,
89 ) -> Pin<Box<dyn Future<Output = A2aResult<()>> + Send + 'a>> {
90 Box::pin(async move {
91 Err(a2a_protocol_types::error::A2aError::task_not_cancelable(
92 &ctx.task_id,
93 ))
94 })
95 }
96
97 /// Called during handler shutdown to allow cleanup of external resources
98 /// (database connections, file handles, etc.).
99 ///
100 /// The default implementation is a no-op.
101 fn on_shutdown<'a>(&'a self) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
102 Box::pin(async {})
103 }
104}