simple_a2a 0.1.0 → 0.3.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -0
  3. data/README.md +78 -38
  4. data/compare_agent2agent.md +460 -0
  5. data/docs/api/client/index.md +19 -0
  6. data/docs/api/index.md +4 -3
  7. data/docs/api/models/index.md +13 -11
  8. data/docs/api/server/index.md +42 -10
  9. data/docs/api/storage/index.md +0 -1
  10. data/docs/architecture/index.md +17 -15
  11. data/docs/architecture/protocol.md +16 -1
  12. data/docs/assets/images/simple_a2a.jpg +0 -0
  13. data/docs/examples/agent-chaining.md +107 -0
  14. data/docs/examples/auth-headers.md +105 -0
  15. data/docs/examples/cancellation.md +105 -0
  16. data/docs/examples/index.md +123 -52
  17. data/docs/examples/interrupted-states.md +114 -0
  18. data/docs/examples/multipart.md +103 -0
  19. data/docs/examples/push-notifications.md +117 -0
  20. data/docs/examples/resubscribe.md +129 -0
  21. data/docs/examples/sqlite-storage.md +131 -0
  22. data/docs/examples/streaming.md +1 -4
  23. data/docs/guides/push-notifications.md +4 -1
  24. data/docs/guides/streaming.md +34 -5
  25. data/docs/index.md +55 -27
  26. data/examples/04_resubscribe/client.rb +140 -0
  27. data/examples/04_resubscribe/server.rb +75 -0
  28. data/examples/05_cancellation/client.rb +150 -0
  29. data/examples/05_cancellation/server.rb +77 -0
  30. data/examples/06_push_notifications/client.rb +192 -0
  31. data/examples/06_push_notifications/server.rb +123 -0
  32. data/examples/07_agent_chaining/client.rb +120 -0
  33. data/examples/07_agent_chaining/server.rb +150 -0
  34. data/examples/08_interrupted_states/client.rb +148 -0
  35. data/examples/08_interrupted_states/server.rb +142 -0
  36. data/examples/09_multipart/client.rb +117 -0
  37. data/examples/09_multipart/server.rb +97 -0
  38. data/examples/10_auth_headers/client.rb +92 -0
  39. data/examples/10_auth_headers/server.rb +98 -0
  40. data/examples/11_sqlite_storage/Brewfile +1 -0
  41. data/examples/11_sqlite_storage/Gemfile +9 -0
  42. data/examples/11_sqlite_storage/client.rb +114 -0
  43. data/examples/11_sqlite_storage/run +154 -0
  44. data/examples/11_sqlite_storage/server.rb +131 -0
  45. data/examples/README.md +384 -0
  46. data/lib/simple_a2a/client/sse.rb +15 -0
  47. data/lib/simple_a2a/server/app.rb +131 -45
  48. data/lib/simple_a2a/server/base.rb +19 -17
  49. data/lib/simple_a2a/server/broadcast_registry.rb +24 -0
  50. data/lib/simple_a2a/server/multi_agent.rb +1 -1
  51. data/lib/simple_a2a/server/push_config_store.rb +29 -0
  52. data/lib/simple_a2a/server/push_sender.rb +1 -0
  53. data/lib/simple_a2a/server/task_broadcast.rb +46 -0
  54. data/lib/simple_a2a/version.rb +1 -1
  55. metadata +38 -20
  56. data/lib/simple_a2a/server/event_router.rb +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1b4e2d504e01c9511e9334010acfb9e1ea99314990586789aff6db82f7c31fd
4
- data.tar.gz: eda5925b5f758c25980af95081f214d652387d9cc61bbd37f0bb6f31d2aa7dbc
3
+ metadata.gz: 8200755c9d568b3b1161815a2dfd8df190efe773ae946a00fafcfddd25b1ca4e
4
+ data.tar.gz: a5f112a8138f111d137b1a443c674a309c0ad98c5c6f5b8473e4c5d3af42d316
5
5
  SHA512:
6
- metadata.gz: 1074249a5196cd4f0c533ef12b4662f4897d03a64f9d626429f0e7cbbd1653c6822e566afa4467793fe3d934d5e9d2451079fdcd5e1ad00159b505af814f4e02
7
- data.tar.gz: 003b13cec22786ee19363f1afa1bdeee064a79a0a600b0b73a3343df6167611dc5b93c8c45fc6522b637c5ae5fde6c322836d6b05743fd53a0a49275ce12786b
6
+ metadata.gz: 39509e60e4665ca244831830f6b9c5cb4e3de384c98c7e5d5ae74ab15bdce5c06fb942fb85a6738a11e2d6080f8ab2c3c6334d0be02f58e04bd1f364a9df1231
7
+ data.tar.gz: d6c85963a895435cffa1b3ec28ad7bc7506d168274ae2281b4e7c590ff0d4b48387cb97a4f249aaf7df01621ade3b8aeaf7e3de14e2e35cfad42625600084635
data/CHANGELOG.md CHANGED
@@ -1,5 +1,72 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2026-05-09
4
+
5
+ ### Added
6
+
7
+ - `tasks/resubscribe` — attach an SSE stream to an existing running task (A2A `SubscribeToTask`
8
+ operation). The first event is the current Task snapshot (spec requirement); subsequent events
9
+ mirror those emitted by the executor in real time.
10
+ - `Client::SSE#resubscribe(task_id:, &block)` — client-side counterpart for `tasks/resubscribe`.
11
+ - `Server::TaskBroadcast` — lock-free, fiber-scheduler-aware fan-out broadcaster. Replaces the
12
+ anonymous streaming router and `EventRouter`. Each running task gets one broadcast; each SSE
13
+ subscriber (original or resubscribed) gets its own `RactorQueue`.
14
+ - `Server::BroadcastRegistry` — thread-safe `task_id → TaskBroadcast` map held at the App class
15
+ level. Allows `tasks/resubscribe` and `tasks/cancel` to locate a live broadcast.
16
+ - `tasks/pushNotification/set|get|delete|list` — push notification config CRUD now fully
17
+ implemented. Configs are stored per task ID in a new `Server::PushConfigStore`; `set` validates
18
+ the task exists and that `webhookUrl` is present before storing.
19
+ - `Server::PushConfigStore` — thread-safe in-memory store for `PushNotificationConfig` objects,
20
+ keyed by task ID. One instance is created per `Server::App` automatically.
21
+ - `examples/04_resubscribe` — two concurrent SSE subscribers demonstrating snapshot-on-join,
22
+ independent fan-out, and single-completion guarantees.
23
+ - `examples/05_cancellation` — three concurrent `tasks/sendSubscribe` streams; one task is
24
+ cancelled mid-flight while the others complete normally.
25
+ - `examples/06_push_notifications` — full push notification lifecycle: submit task, register
26
+ webhook, receive out-of-band HTTP POSTs per step, then get/list/delete the config with no
27
+ open SSE connection required.
28
+ - `examples/07_agent_chaining` — three agents (`ReverseAgent`, `ShoutAgent`, `PipelineAgent`)
29
+ on one port via `A2A.multi_server`; the pipeline executor chains to the other two agents over
30
+ the protocol, invisible to the client.
31
+ - `examples/08_interrupted_states` — `input_required` and `auth_required` interrupted states
32
+ demonstrated with `OrderAgent` (pauses for user input) and `VaultAgent` (blocks on wrong
33
+ token); each follow-up turn is a separate `tasks/send` threaded by `message.context_id`.
34
+ - `examples/09_multipart` — all four `Part` types in one artifact: text summary, JSON metadata,
35
+ base64-encoded binary CSV, and URL reference; client uses `text?/json?/raw?/url?` predicates.
36
+ - `examples/10_auth_headers` — `headers:` option on `A2A.client` with a Bearer token middleware;
37
+ agent card discovery is public while RPC calls require `Authorization: Bearer <token>`.
38
+ - `examples/11_sqlite_storage` — persistent task storage across server restarts via
39
+ `SqliteStorage < A2A::Storage::Base` (WAL mode, mutex, JSON blobs); Brewfile + Gemfile pattern
40
+ keeps SQLite dependency out of the main gem.
41
+ - MkDocs documentation pages for examples 05–11 (`cancellation`, `push-notifications`,
42
+ `agent-chaining`, `interrupted-states`, `multipart`, `auth-headers`, `sqlite-storage`).
43
+ - `compare_agent2agent.md` — side-by-side comparison of A2A protocol operations at the repo root.
44
+
45
+ ### Changed
46
+
47
+ - `handle_cancel` now routes cancellation events through the live broadcast so SSE subscribers
48
+ observe the `canceled` state update in real time.
49
+ - `Server::Base` and `Server::MultiAgent` no longer create an `EventRouter`; they inject a fresh
50
+ `BroadcastRegistry` into each App subclass instead.
51
+ - `Server::Base` now accepts a `push_config_store:` keyword so the executor and the App share
52
+ the same store instance (fixes injection gap).
53
+ - README Examples section expanded to an 11-row table; `docs/examples/index.md` rebuilt with
54
+ a 3×4 SVG grid covering all demos.
55
+
56
+ ### Fixed
57
+
58
+ - Removed phantom `simple_flow` runtime dependency from gemspec.
59
+ - Added missing `digest` require (fixes JWT signing in push notification delivery).
60
+
61
+ ### Removed
62
+
63
+ - `Server::EventRouter` and its `TypedBus`-based pub/sub — superseded by `TaskBroadcast`.
64
+ - `typed_bus` runtime dependency — removed entirely.
65
+
66
+ ### Dependencies
67
+
68
+ - Added `ractor_queue ~> 0.2` runtime dependency (replaces `typed_bus`).
69
+
3
70
  ## [0.1.0] - 2026-05-07
4
71
 
5
72
  - Initial release
data/README.md CHANGED
@@ -1,44 +1,62 @@
1
1
  # simple_a2a
2
2
 
3
- A Ruby gem implementing the [Agent2Agent (A2A) protocol](https://a2a-protocol.org/latest/) — an open standard by Google and the Linux Foundation for interoperability between AI agents.
4
-
5
- `simple_a2a` provides a complete A2A client and server in a single package, built on the async Ruby ecosystem with [Falcon](https://github.com/socketry/falcon) as the recommended HTTP server.
6
-
7
- ## Documentation
8
-
9
- The full documentation website is available at [https://madbomber.github.io/simple_a2a](https://madbomber.github.io/simple_a2a).
3
+ <table>
4
+ <tr>
5
+ <td width="40%">
6
+ <img src="docs/assets/images/simple_a2a.jpg" alt="simple_a2a - AI agents communicating" width="100%">
7
+ </td>
8
+ <td width="60%" valign="top">
9
+ <strong>A Ruby gem implementing the <a href="https://a2a-protocol.org/latest/">Agent2Agent (A2A) protocol</a></strong>
10
+ <br><br>
11
+ A complete A2A client and server in a single package, built on the async Ruby ecosystem with Falcon as the recommended HTTP server.
12
+ <ul>
13
+ <li>Full A2A v1.0 protocol support (backward compatible with v0.3)</li>
14
+ <li>JSON-RPC 2.0 over HTTP(S) — primary binding</li>
15
+ <li>Server-Sent Events (SSE) for streaming responses (<code>tasks/sendSubscribe</code> and <code>tasks/resubscribe</code>)</li>
16
+ <li>Push notifications via webhooks (RS256 JWT)</li>
17
+ <li>Task lifecycle management (<code>submitted → working → completed/failed/canceled</code>)</li>
18
+ <li>AgentCard discovery endpoint at <code>GET /agentCard</code></li>
19
+ <li>Multi-agent hosting with path-based routing via <code>A2A.multi_server</code></li>
20
+ <li>Async-first via the <code>async</code> gem ecosystem (Falcon + async-http)</li>
21
+ <li>Lock-free SSE fan-out via <code>RactorQueue</code> — multiple concurrent subscribers per task</li>
22
+ <li>Rack-compatible server with Roda routing</li>
23
+ <li>Zeitwerk autoloading — top-level module is <code>A2A</code></li>
24
+ <li><a href="https://madbomber.github.io/simple_a2a">Full documentation website</a></li>
25
+ </ul>
26
+ </td>
27
+ </tr>
28
+ </table>
29
+
30
+ ## MCP vs. A2A
31
+
32
+ Two open protocols address different dimensions of AI agent integration — and they are designed to complement each other.
33
+
34
+ **MCP — Vertical Integration (Agent ↕ Environment)**
35
+
36
+ The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP), introduced by Anthropic in November 2024, defines how an AI agent connects to the tools, data sources, and services in its environment — file systems, databases, APIs, browsers, and code execution engines. MCP uses a client-server model where the agent is the client and each external capability is a server. This is *vertical* integration: the agent reaches downward into its local context and outward into external services through a uniform interface.
37
+
38
+ **A2A — Horizontal Integration (Agent ↔ Agent)**
39
+
40
+ The [Agent2Agent Protocol](https://a2a-protocol.org/latest/) (A2A), introduced by Google in April 2025 and donated to the Linux Foundation for vendor-neutral governance, defines how autonomous agents running on different platforms, frameworks, and vendors can discover one another, delegate tasks, and stream results in real time. This is *horizontal* integration: peer agents — each with its own specialization, runtime, and vendor — collaborate as equals across organizational and technology boundaries.
41
+
42
+ **Together**
43
+
44
+ MCP and A2A are complementary. A single agent can use MCP to access its tools and A2A to delegate subtasks to peer agents. `simple_a2a` implements the A2A layer.
10
45
 
11
46
  ## Lineage
12
47
 
13
48
  `simple_a2a` is the successor to my earlier Ruby gem, `simple_acp`. That gem implemented the Agent Communication Protocol (ACP), which IBM Research introduced through the BeeAI project for interoperable agent communication. ACP later merged into A2A under the Linux Foundation, with the ACP team contributing its technology and expertise to the A2A effort.
14
49
 
15
- A2A itself was created by Google and then donated to the Linux Foundation for neutral, open governance. The current A2A specification is maintained by the Linux Foundation-hosted [Agent2Agent project](https://github.com/a2aproject/A2A) and published at [a2a-protocol.org](https://a2a-protocol.org/latest/).
16
-
17
50
  > My opinion: the A2A specification is still a little jagged in places. A simple example is that it does not clearly cover whether an A2A server is expected to host only one agent or may host multiple agents. That is a minor example, but it points to the kind of operational detail the specification still needs to tighten up.
18
51
 
19
52
  References:
20
53
 
54
+ - Anthropic: [Introducing the Model Context Protocol](https://www.anthropic.com/news/model-context-protocol) (November 2024)
55
+ - Google Developers Blog: [A2A: A new era of agent interoperability](https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/) (April 2025)
21
56
  - IBM Research: [Agent Communication Protocol](https://research.ibm.com/projects/agent-communication-protocol)
22
57
  - BeeAI announcement: [ACP Joins Forces with A2A Under the Linux Foundation](https://github.com/orgs/i-am-bee/discussions/5)
23
58
  - Linux Foundation: [Launch of the Agent2Agent Protocol Project](https://www.linuxfoundation.org/press/linux-foundation-launches-the-agent2agent-protocol-project-to-enable-secure-intelligent-communication-between-ai-agents)
24
-
25
- ## Protocol Reference
26
-
27
- - **Official A2A Specification:** [https://a2a-protocol.org/latest/](https://a2a-protocol.org/latest/)
28
- - **A2A Project on GitHub:** [https://github.com/a2aproject/A2A](https://github.com/a2aproject/A2A)
29
-
30
- ## Features
31
-
32
- - Full A2A v1.0 protocol support (backward compatible with v0.3)
33
- - JSON-RPC 2.0 over HTTP(S) — primary binding
34
- - Server-Sent Events (SSE) for streaming responses
35
- - Push notifications via webhooks (RS256 JWT)
36
- - Task lifecycle management (`submitted → working → completed/failed/canceled`)
37
- - AgentCard discovery endpoint at `GET /agentCard`
38
- - Multi-agent hosting with path-based routing via `A2A.multi_server`
39
- - Async-first via the `async` gem ecosystem (Falcon + async-http)
40
- - Rack-compatible server with Roda routing
41
- - Zeitwerk autoloading — top-level module is `A2A`
59
+ - A2A Project on GitHub: [https://github.com/a2aproject/A2A](https://github.com/a2aproject/A2A)
42
60
 
43
61
  ## Installation
44
62
 
@@ -128,7 +146,7 @@ end
128
146
  ```
129
147
 
130
148
  ```ruby
131
- # Client — consume SSE events
149
+ # Client — start a streaming task
132
150
  client = A2A.sse_client(url: "http://localhost:9292")
133
151
 
134
152
  client.send_subscribe(message: A2A::Models::Message.user("go")) do |event|
@@ -141,6 +159,19 @@ client.send_subscribe(message: A2A::Models::Message.user("go")) do |event|
141
159
  end
142
160
  ```
143
161
 
162
+ ```ruby
163
+ # Client — reattach to an already-running task
164
+ # First event is the current Task snapshot; subsequent events are the live stream.
165
+ client.resubscribe(task_id: "existing-task-id") do |event|
166
+ case event
167
+ when Hash
168
+ puts "task snapshot: #{event['status']['state']}"
169
+ when A2A::Models::TaskStatusUpdateEvent
170
+ puts "status: #{event.status.state} (final=#{event.final})"
171
+ end
172
+ end
173
+ ```
174
+
144
175
  ## Multi-agent server
145
176
 
146
177
  Use `A2A.multi_server` to host multiple A2A agents in one Falcon process, with each agent mounted at its own path:
@@ -155,26 +186,35 @@ A2A.multi_server(
155
186
  ).run
156
187
  ```
157
188
 
158
- Each mounted agent has its own AgentCard, executor, storage, and event router.
189
+ Each mounted agent has its own AgentCard, executor, storage, and broadcast registry.
159
190
 
160
191
  ## Examples
161
192
 
162
- The repository includes three runnable demo apps:
193
+ The repository includes eleven runnable demo applications:
163
194
 
164
- | Demo | Shows |
165
- |---|---|
166
- | `01_basic_usage` | Agent discovery, `tasks/send`, task listing, task lookup, and error handling |
167
- | `02_streaming` | `tasks/sendSubscribe` with Server-Sent Events and incremental artifact chunks |
168
- | `03_llm_research` | Multi-agent routing, parallel streaming LLM calls, evaluator agent, and a Sinatra web client |
195
+ | # | Demo | What it shows |
196
+ |---|---|---|
197
+ | 01 | Basic Usage | Agent discovery, `tasks/send`, task listing, task lookup, error handling |
198
+ | 02 | Streaming | `tasks/sendSubscribe`, SSE, incremental artifact chunks |
199
+ | 03 | LLM Research | `multi_server`, parallel SSE agents, evaluator pattern, web client |
200
+ | 04 | Resubscribe | `tasks/resubscribe`, concurrent SSE subscribers, mid-stream join |
201
+ | 05 | Cancellation | `tasks/cancel`, concurrent tasks, cooperative cancellation |
202
+ | 06 | Push Notifications | `tasks/pushNotification` CRUD, webhook delivery, out-of-band events |
203
+ | 07 | Agent Chaining | `A2A.client` inside an executor, agent-to-agent delegation |
204
+ | 08 | Interrupted States | `input_required`, `auth_required`, multi-turn `context_id` threading |
205
+ | 09 | Multipart Artifacts | `Part.text`, `Part.json`, `Part.binary`, `Part.from_url`, predicates |
206
+ | 10 | Auth Headers | `A2A.client(headers:)`, Bearer token, Rack middleware composition |
207
+ | 11 | SQLite Storage | `Storage::Base` injection, WAL persistence, cross-restart survival |
169
208
 
170
- Run the basic and streaming demos end-to-end:
209
+ Run any demo end-to-end from the repository root:
171
210
 
172
211
  ```bash
173
212
  bundle exec ruby examples/run 01_basic_usage
174
- bundle exec ruby examples/run 02_streaming
213
+ bundle exec ruby examples/run 05_cancellation
214
+ bundle exec ruby examples/run 11_sqlite_storage
175
215
  ```
176
216
 
177
- The LLM research demo requires `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and demo-specific gems. See the full documentation for setup details.
217
+ Demo 03 requires `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and additional gems (`ruby_llm`, `async-http-faraday`, `sinatra`). Demo 11 has its own `Gemfile` and `Brewfile`; its `run` script handles setup automatically. See [`examples/README.md`](examples/README.md) for full details on each demo.
178
218
 
179
219
  ## Development
180
220