From 1cad3b4b8719c043789f85966c1a1b08d5ddbf00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez?= Date: Wed, 19 Aug 2026 12:46:22 +0200 Subject: [PATCH 1/2] fix: configurable A2A sync timeout (@a2a_sync_timeout), overridable via config :a2ui, :a2a_sync_timeout (default 5_000ms). Make phoenix deps compulsory again; keep configurable A2A sync timeout Reverts the optional-dependency guards: phoenix_live_view and phoenix_html are required deps again, and the Code.ensure_loaded? wrappers around the renderer modules are removed. The configurable @a2a_sync_timeout (config :a2ui, :a2a_sync_timeout) stays. --- lib/a2ui/a2a.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/a2ui/a2a.ex b/lib/a2ui/a2a.ex index f161b14..37f3f2e 100644 --- a/lib/a2ui/a2a.ex +++ b/lib/a2ui/a2a.ex @@ -42,6 +42,10 @@ if Code.ensure_loaded?(A2A.Agent) do alias A2UI.Connection alias A2UI.Protocol.Message, as: Msg + # Timeout (ms) for the A2A sync/drain handshake with the A2UI agent. + # Overridable per-application: `config :a2ui, a2a_sync_timeout: `. + @a2a_sync_timeout Application.compile_env(:a2ui, :a2a_sync_timeout, 5_000) + @doc false defmacro __using__(opts) do agent = Keyword.fetch!(opts, :agent) @@ -152,7 +156,7 @@ if Code.ensure_loaded?(A2A.Agent) do receive do {:a2ui_sync_ack, ^ref} -> :ok after - 5_000 -> :timeout + @a2a_sync_timeout -> :timeout end end @@ -166,7 +170,7 @@ if Code.ensure_loaded?(A2A.Agent) do A2A.Part.Data.new(Msg.to_map(msg)) end) after - 5_000 -> [] + @a2a_sync_timeout -> [] end end From 1a24813d3cb43d7181a0ec0750aacb0a46f0dc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez?= Date: Fri, 21 Aug 2026 10:36:14 +0200 Subject: [PATCH 2/2] add documentation --- README.md | 14 ++++++++++++++ lib/a2ui/a2a.ex | 11 +++++++++++ 2 files changed, 25 insertions(+) diff --git a/README.md b/README.md index f34ddf1..16f7814 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,20 @@ config :a2ui, component_modules: %{"StatusBadge" => MyApp.A2UI.StatusBadge} Set `use_default_components: false` to replace all built-in components with your own. See the `A2UI.ComponentRenderer` and `A2UI.Components.Renderer` hexdocs for the full assigns contract, available helpers, and configuration details. +## A2A Configuration + +The A2A server adapter waits for the wrapped agent to acknowledge synchronization and for +buffered A2UI responses to be drained. Configure the timeout for each wait in milliseconds: + +```elixir +# config/config.exs +config :a2ui, :a2a_sync_timeout, 5_000 +``` + +The default is `5_000` milliseconds. This is a compile-time setting, so changes require +recompiling the application. If synchronization times out, processing continues; if draining +times out, the response contains no buffered A2UI parts. + ## Installation Add `a2ui` to your list of dependencies in `mix.exs`: diff --git a/lib/a2ui/a2a.ex b/lib/a2ui/a2a.ex index 37f3f2e..2b3a4f6 100644 --- a/lib/a2ui/a2a.ex +++ b/lib/a2ui/a2a.ex @@ -29,6 +29,17 @@ if Code.ensure_loaded?(A2A.Agent) do - All other options (`:name`, `:description`, `:version`, `:skills`) are forwarded to `use A2A.Agent`. + ## Configuration + + Configure the timeout for each A2A synchronization acknowledgement and buffered response + drain in milliseconds: + + config :a2ui, :a2a_sync_timeout, 5_000 + + The default is `5_000` milliseconds. This is a compile-time setting, so changes require + recompiling the application. If synchronization times out, processing continues; if draining + times out, the response contains no buffered A2UI parts. + ## Protocol Mapping - First A2A message in a task triggers `handle_connect` on the A2UI agent