a2a-rails 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +80 -0
- data/README.md +262 -205
- data/app/controllers/a2a/rails/agent_cards_controller.rb +3 -0
- data/app/controllers/a2a/rails/requests_controller.rb +53 -0
- data/lib/a2a/rails/agent.rb +33 -2
- data/lib/a2a/rails/agent_card/builder.rb +3 -1
- data/lib/a2a/rails/agent_card/security.rb +115 -0
- data/lib/a2a/rails/authentication.rb +71 -0
- data/lib/a2a/rails/configuration.rb +95 -1
- data/lib/a2a/rails/dispatcher.rb +36 -4
- data/lib/a2a/rails/engine.rb +9 -0
- data/lib/a2a/rails/errors.rb +3 -0
- data/lib/a2a/rails/execution_plan.rb +40 -0
- data/lib/a2a/rails/file_artifact.rb +77 -0
- data/lib/a2a/rails/protocol/agent2agent_adapter.rb +11 -0
- data/lib/a2a/rails/protocol/request_handler.rb +124 -13
- data/lib/a2a/rails/protocol/task_mapper.rb +21 -0
- data/lib/a2a/rails/request_guard.rb +76 -0
- data/lib/a2a/rails/runtime.rb +15 -6
- data/lib/a2a/rails/skill.rb +7 -2
- data/lib/a2a/rails/task/active_record_store.rb +451 -0
- data/lib/a2a/rails/task/artifact_mapper.rb +2 -0
- data/lib/a2a/rails/task/lifecycle.rb +18 -8
- data/lib/a2a/rails/task/memory_store.rb +39 -15
- data/lib/a2a/rails/task/store.rb +16 -4
- data/lib/a2a/rails/task_execution_arguments.rb +78 -0
- data/lib/a2a/rails/task_execution_job.rb +79 -0
- data/lib/a2a/rails/version.rb +1 -1
- data/lib/a2a-rails.rb +7 -0
- data/lib/generators/a2a/rails/task_store_generator.rb +46 -0
- data/lib/generators/a2a/rails/templates/create_a2a_rails_tasks.rb.tt +31 -0
- data/lib/generators/a2a/rails/templates/initializer.rb.tt +4 -0
- data/lib/tasks/a2a_rails_tasks.rake +54 -0
- metadata +94 -3
data/README.md
CHANGED
|
@@ -1,53 +1,37 @@
|
|
|
1
1
|
# a2a-rails
|
|
2
|
+
<img src="docs/assets/a2a-rails-handwritten-note.png">
|
|
3
|
+
<br/>
|
|
4
|
+
<img src="docs/assets/a2a-railsで広がるAI協働図鑑.png">
|
|
2
5
|
|
|
3
|
-
Rails-native integration for exposing Rails applications as A2A agents.
|
|
6
|
+
Rails-native integration for exposing Rails applications as A2A v1.0 agents.
|
|
4
7
|
|
|
5
|
-
> **
|
|
8
|
+
> **Release status (2026-10-08):** RubyGems has published **`0.1.0`** and the **`0.2.0.rc2` pre-release**. The current source is being verified as an **unpublished `0.2.0` stable candidate** (Step 26-4). In particular, the Rails/Zeitwerk and migration-generator fixes after rc2 are only in the newer source-built candidate. **No stable `0.2.0` tag, GitHub Release, or RubyGems publication has been authorized.**
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
- RubyGems: https://rubygems.org/gems/a2a-rails
|
|
11
|
+
- Published pre-release: https://github.com/cuichangquan/a2a-rails/releases/tag/v0.2.0-rc.2 (stable `0.2.0` not yet released)
|
|
12
|
+
- Previous stable release: https://github.com/cuichangquan/a2a-rails/releases/tag/v0.1.0
|
|
13
|
+
- Changelog: [CHANGELOG.md](CHANGELOG.md)
|
|
14
|
+
- Release record: [docs/release/v0.1.0-record.md](docs/release/v0.1.0-record.md)
|
|
15
|
+
- **Release readiness:** [current release/deployment checklist](docs/release/security-hardening-release-checklist.md) · [v0.1.0 → proposed v0.2 upgrade guide](docs/release/upgrading-v0.1.0-to-v0.2.md). The old rc1 record is historical evidence only.
|
|
16
|
+
- **Roadmap / 次にやること:** [ROADMAP.md](ROADMAP.md) — [Step 26 / Issue #62](https://github.com/cuichangquan/a2a-rails/issues/62) tracks the `0.2.0` stable candidate and release gates; [production Issue #11](https://github.com/cuichangquan/a2a-rails/issues/11) remains open. See [stable-readiness record](docs/release/v0.2.0-stable-readiness.md).
|
|
17
|
+
- **Official A2A TCK results:** [Pinned JSON-RPC MUST report and reproduction](docs/testing/official-a2a-tck.md) — after Step 17-4: **63 passed / 1 failed / 171 skipped / 30 deselected** (pytest). The remaining `CORE-SEND-003` mismatch is tracked [upstream in #202](https://github.com/a2aproject/a2a-tck/issues/202). The TCK workflow is informational, **not** an A2A conformance certificate.
|
|
8
18
|
|
|
9
|
-
|
|
19
|
+
- [A2Aの全体像(日本語・A4 1枚PDF)](docs/guides/a2a-protocol-overview-ja.pdf) — 登場人物・依頼の流れ・主要用語・MCPとの違いをまとめた学習資料。
|
|
20
|
+
- [A2A at a glance (English, A4 one-page PDF)](docs/guides/a2a-protocol-overview-en.pdf) — Roles, workflow, key terms, and how MCP fits.
|
|
10
21
|
|
|
11
|
-
|
|
22
|
+
## Runnable Rails Demo / 実際に動くサンプル ⭐
|
|
12
23
|
|
|
13
|
-
**
|
|
24
|
+
**[a2a-rails-demo — independent Rails 8 Echo Agent](https://github.com/cuichangquan/a2a-rails-demo)**
|
|
14
25
|
|
|
15
|
-
|
|
26
|
+
Run a complete standalone Rails Agent with the current unreleased `a2a-rails` source pinned by Git commit (the published RubyGems v0.1.0 does not contain direct-Message support). The separate Demo covers Agent Card discovery, JSON-RPC v1.0 SendMessage **Task and direct Message**, GetTask, ListTasks and error handling.
|
|
16
27
|
|
|
17
|
-
|
|
28
|
+
- [Demo Quick Start, code and example curl requests](https://github.com/cuichangquan/a2a-rails-demo#quick-start).
|
|
29
|
+
- [Demo GitHub Actions smoke](https://github.com/cuichangquan/a2a-rails-demo/actions/workflows/smoke.yml) — independent real HTTP tests **16/16 checks passed** ([PR #1](https://github.com/cuichangquan/a2a-rails-demo/pull/1)).
|
|
30
|
+
- **Local-only development/test** demo: no trusted verifier or durable Task store, **not** an internet-facing production template. See [production security](docs/guides/production-security.md).
|
|
18
31
|
|
|
19
|
-
##
|
|
32
|
+
## What is a2a-rails?
|
|
20
33
|
|
|
21
|
-
-
|
|
22
|
-
- [x] 2. Research Ruby A2A SDKs / Gems
|
|
23
|
-
- [x] 3. Research Rails-oriented A2A alternatives
|
|
24
|
-
- [x] 4. Define the problem a2a-rails solves
|
|
25
|
-
- [x] 5. Define v0.1 scope
|
|
26
|
-
- [x] 6. Define terminology
|
|
27
|
-
- [x] 7. Define architecture
|
|
28
|
-
- [x] 8. Define Ruby SDK boundary
|
|
29
|
-
- [x] 9. Public API Design
|
|
30
|
-
- [x] 10. Agent Card Design
|
|
31
|
-
- [x] 11. Task Lifecycle Design
|
|
32
|
-
- [x] 12. Test Strategy
|
|
33
|
-
- [x] 13. Gem Structure
|
|
34
|
-
- [x] 14. Quick Start Design
|
|
35
|
-
- [ ] **15. Implementation / release preparation ← IN PROGRESS**
|
|
36
|
-
- [x] 15-4 SDK compatibility spike
|
|
37
|
-
- [x] 15-5 Gem skeleton + Protocol Adapter
|
|
38
|
-
- [x] 15-6 Agent / Skill DSL + Dispatcher
|
|
39
|
-
- [x] 15-7 Task core + MemoryStore
|
|
40
|
-
- [x] 15-8 Task operations through real SDK
|
|
41
|
-
- [x] 15-9 Configuration + Agent Card
|
|
42
|
-
- [x] 15-10 Rails Engine HTTP integration
|
|
43
|
-
- [x] 15-11 Generators + generated Quick Start
|
|
44
|
-
- [x] 15-12 Packaged-gem / release-readiness verification
|
|
45
|
-
- [x] 15-13 v0.1.0 release-candidate preparation
|
|
46
|
-
- [ ] 15-14 v0.1.0 release execution
|
|
47
|
-
|
|
48
|
-
## v0.1 Direction
|
|
49
|
-
|
|
50
|
-
v0.1 is **server-first** and focuses on exposing a Rails application as an A2A Agent.
|
|
34
|
+
`a2a-rails` lets a Rails application expose an A2A-compatible Agent without making application code depend directly on SDK-specific request and response objects.
|
|
51
35
|
|
|
52
36
|
```text
|
|
53
37
|
A2A Protocol
|
|
@@ -61,61 +45,76 @@ Rails Application
|
|
|
61
45
|
Business Logic
|
|
62
46
|
```
|
|
63
47
|
|
|
64
|
-
|
|
48
|
+
The Gem provides:
|
|
65
49
|
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
50
|
+
- a Rails-native Agent / Skill DSL;
|
|
51
|
+
- A2A Agent Card generation;
|
|
52
|
+
- automatically mounted A2A HTTP endpoints;
|
|
53
|
+
- synchronous Task execution by default, plus opt-in ActiveJob-backed async Task execution on unreleased `main`;
|
|
54
|
+
- SDK-independent Handler inputs;
|
|
55
|
+
- a process-local MemoryStore by default, plus an optional durable ActiveRecordStore on unreleased main;
|
|
56
|
+
- Rails generators for initial setup;
|
|
57
|
+
- an internal Protocol Adapter boundary around the upstream SDK.
|
|
71
58
|
|
|
72
|
-
|
|
59
|
+
v0.1 is intentionally **server-first** and **non-streaming**.
|
|
73
60
|
|
|
74
|
-
>
|
|
61
|
+
> [!WARNING]
|
|
62
|
+
> **Security / production use:** v0.1.0 does not provide built-in authentication or per-caller Task authorization. Do not expose `POST /a2a` to untrusted clients. Before public deployment, protect the endpoint at your application's or network's security boundary. Security hardening is tracked in [Issue #11](https://github.com/cuichangquan/a2a-rails/issues/11).
|
|
63
|
+
|
|
64
|
+
## Requirements
|
|
65
|
+
|
|
66
|
+
- Ruby `>= 3.3`
|
|
67
|
+
- Rails `>= 8.0, < 8.2`
|
|
68
|
+
- A2A protocol version `1.0`
|
|
69
|
+
- `agent2agent ~> 2.0.0`
|
|
75
70
|
|
|
76
|
-
|
|
71
|
+
The original v0.1.0 baseline was verified against:
|
|
72
|
+
|
|
73
|
+
- Ruby 3.3 / 3.4 / 4.0
|
|
74
|
+
- Rails 8.0 / 8.1
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
Add the Gem to an existing Rails application:
|
|
77
79
|
|
|
78
80
|
```bash
|
|
79
81
|
bundle add a2a-rails
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Then generate the initializer and an Agent scaffold:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
80
87
|
bin/rails generate a2a:rails:install
|
|
81
88
|
bin/rails generate a2a:rails:agent echo
|
|
82
|
-
mkdir -p app/services/echo
|
|
83
89
|
```
|
|
84
90
|
|
|
85
|
-
`
|
|
91
|
+
No explicit Engine mount or host `config/routes.rb` change is required.
|
|
86
92
|
|
|
87
|
-
|
|
88
|
-
config/initializers/a2a_rails.rb
|
|
89
|
-
```
|
|
93
|
+
## Quick Start
|
|
90
94
|
|
|
91
|
-
|
|
95
|
+
The following Echo flow is verified against the published `a2a-rails 0.1.0` Gem in a clean Rails 8.1 application.
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
### 1. Generate the setup
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
bin/rails generate a2a:rails:install
|
|
101
|
+
bin/rails generate a2a:rails:agent echo
|
|
102
|
+
mkdir -p app/services/echo
|
|
98
103
|
```
|
|
99
104
|
|
|
100
|
-
|
|
105
|
+
The install generator creates:
|
|
101
106
|
|
|
102
107
|
```text
|
|
103
|
-
|
|
108
|
+
config/initializers/a2a_rails.rb
|
|
104
109
|
```
|
|
105
110
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```ruby
|
|
109
|
-
class EchoAgent < A2A::Rails::Agent
|
|
110
|
-
name "Echo Agent"
|
|
111
|
-
description "TODO"
|
|
112
|
-
version "1.0"
|
|
111
|
+
The Agent generator creates:
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
```text
|
|
114
|
+
app/agents/echo_agent.rb
|
|
116
115
|
```
|
|
117
116
|
|
|
118
|
-
The
|
|
117
|
+
The generators deliberately do not create application business logic, jobs, migrations, Task Stores, or routing side effects.
|
|
119
118
|
|
|
120
119
|
### 2. Create the Handler
|
|
121
120
|
|
|
@@ -133,7 +132,9 @@ class Echo::Reply
|
|
|
133
132
|
end
|
|
134
133
|
```
|
|
135
134
|
|
|
136
|
-
|
|
135
|
+
Handlers receive SDK-independent Ruby Hashes for `message` and `context`.
|
|
136
|
+
|
|
137
|
+
### 3. Define the Agent and Skill
|
|
137
138
|
|
|
138
139
|
Replace `app/agents/echo_agent.rb` with:
|
|
139
140
|
|
|
@@ -163,16 +164,20 @@ A2A::Rails.configure do |config|
|
|
|
163
164
|
end
|
|
164
165
|
```
|
|
165
166
|
|
|
166
|
-
For localhost, leave `A2A_PUBLIC_BASE_URL` unset or set it to `http://localhost:3000`.
|
|
167
|
+
For localhost, leave `A2A_PUBLIC_BASE_URL` unset or set it to `http://localhost:3000`.
|
|
167
168
|
|
|
168
|
-
|
|
169
|
+
The Agent class name remains a String until an A2A endpoint resolves it, preserving Rails autoload / reload behavior.
|
|
169
170
|
|
|
170
171
|
### 5. Check the Agent Card
|
|
171
172
|
|
|
173
|
+
Start Rails:
|
|
174
|
+
|
|
172
175
|
```bash
|
|
173
176
|
bin/rails server
|
|
174
177
|
```
|
|
175
178
|
|
|
179
|
+
Then request the Agent Card:
|
|
180
|
+
|
|
176
181
|
```bash
|
|
177
182
|
curl -sS http://localhost:3000/.well-known/agent-card.json \
|
|
178
183
|
-H "A2A-Version: 1.0"
|
|
@@ -183,9 +188,9 @@ Expected essentials:
|
|
|
183
188
|
- HTTP 200
|
|
184
189
|
- Agent name `Echo Agent`
|
|
185
190
|
- Skill ID `reply`
|
|
186
|
-
-
|
|
191
|
+
- A2A interface URL ending in `/a2a`
|
|
187
192
|
|
|
188
|
-
### 6. Send
|
|
193
|
+
### 6. Send a message
|
|
189
194
|
|
|
190
195
|
```bash
|
|
191
196
|
curl -sS -X POST http://localhost:3000/a2a \
|
|
@@ -205,16 +210,18 @@ curl -sS -X POST http://localhost:3000/a2a \
|
|
|
205
210
|
}'
|
|
206
211
|
```
|
|
207
212
|
|
|
208
|
-
|
|
213
|
+
A successful response reaches:
|
|
209
214
|
|
|
210
215
|
```text
|
|
211
216
|
result.task.status.state == TASK_STATE_COMPLETED
|
|
212
217
|
Artifact Text Part == "Echo: Hello"
|
|
213
218
|
```
|
|
214
219
|
|
|
215
|
-
See [
|
|
220
|
+
See [docs/design/quick-start.md](docs/design/quick-start.md) for the design and verification notes behind this flow.
|
|
221
|
+
|
|
222
|
+
## Public Rails API
|
|
216
223
|
|
|
217
|
-
|
|
224
|
+
A minimal Agent looks like this:
|
|
218
225
|
|
|
219
226
|
```ruby
|
|
220
227
|
class ShoppingAgent < A2A::Rails::Agent
|
|
@@ -248,11 +255,11 @@ A2A::Rails.configure do |config|
|
|
|
248
255
|
end
|
|
249
256
|
```
|
|
250
257
|
|
|
251
|
-
v0.1 targets **one public A2A Agent per Rails application**.
|
|
258
|
+
v0.1 targets **one public A2A Agent per Rails application**.
|
|
252
259
|
|
|
253
|
-
For multiple Skills, the application supplies a Router. The Router receives `skills:` as a frozen `Array<Symbol>` of declared Skill IDs and may return a matching Symbol or String. Unknown selections raise `A2A::Rails::UnknownSkillError`; the Dispatcher
|
|
260
|
+
For multiple Skills, the application supplies a Router. The Router receives `skills:` as a frozen `Array<Symbol>` of declared Skill IDs and may return a matching Symbol or String. Unknown selections raise `A2A::Rails::UnknownSkillError`; the Dispatcher does not silently choose the first Skill.
|
|
254
261
|
|
|
255
|
-
##
|
|
262
|
+
## HTTP Endpoints
|
|
256
263
|
|
|
257
264
|
The Gem automatically exposes:
|
|
258
265
|
|
|
@@ -261,20 +268,47 @@ GET /.well-known/agent-card.json
|
|
|
261
268
|
POST /a2a
|
|
262
269
|
```
|
|
263
270
|
|
|
264
|
-
|
|
271
|
+
The Rails Engine is mounted automatically.
|
|
272
|
+
|
|
273
|
+
### Security work on main (unreleased; not in published v0.1.0)
|
|
274
|
+
|
|
275
|
+
The unreleased `main` branch includes a host-provided `config.authenticate_request` callback for `POST /a2a`, plus per-principal Task ownership checks. Without an authenticator, production and other non-development/test environments fail closed; the local development/test Quick Start remains available. See [Authentication guide](docs/guides/authentication.md).
|
|
276
|
+
|
|
277
|
+
> **Important:** These protections are **absent from RubyGems 0.1.0** but included in the published `0.2.0.rc2` pre-release; the stable candidate has additional fixes. Neither pre-release nor stable source automatically makes a deployed endpoint production-secure. Distributed rate limits, business authorization and the deployment review remain tracked in [Issue #11](https://github.com/cuichangquan/a2a-rails/issues/11).
|
|
278
|
+
|
|
279
|
+
### Step 16-4 request hardening (merged on main; unreleased)
|
|
280
|
+
|
|
281
|
+
The unreleased main branch adds a bounded JSON-RPC body (`config.max_request_bytes`, default 1 MiB), Content-Type validation, stricter parameter checks, a pagination snapshot cap, and reduced exception logging. See [Request Hardening Guide](docs/guides/request-hardening.md).
|
|
282
|
+
|
|
283
|
+
Rate limiting, application-specific authorization and production deployment safeguards remain the host's responsibility or future work. Step 16-5 adds explicit A2A Agent Card Bearer authentication advertisement on unreleased `main`; it must match the host verifier. **These improvements are not included in the published v0.1.0 Gem; see the later rc2 pre-release and current stable candidate.**
|
|
284
|
+
|
|
285
|
+
### Production deployment review (Step 16-6)
|
|
286
|
+
|
|
287
|
+
**Current verdict: NO-GO by default for open public production.** The default MemoryStore remains process-local. Step 21 provides an optional durable ActiveRecordStore, and Step 22 provides optional ActiveJob Task execution, but production async operation requires **both** a shared/durable Task Store and a durable queue backend operated by the host. A real deployment must also provide credential verification, business authorization, TLS/proxy restrictions, distributed rate limits, execution budgets, observability and deployment-specific verification.
|
|
265
288
|
|
|
266
|
-
-
|
|
267
|
-
-
|
|
289
|
+
- [Production security & deployment guide](docs/guides/production-security.md) — responsibilities, sample configuration, security checks and current blockers.
|
|
290
|
+
- [Security release checklist](docs/release/security-hardening-release-checklist.md) — release/upgrade decision, acceptance criteria and artifact verification.
|
|
291
|
+
|
|
292
|
+
Do **not** interpret successful source-tree CI or a merge to `main` as publication of a new RubyGems release.
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
## Agent Card
|
|
296
|
+
|
|
297
|
+
Agent Cards are generated from the Agent / Skill DSL.
|
|
298
|
+
|
|
299
|
+
Behavior in v0.1:
|
|
300
|
+
|
|
301
|
+
- Skill IDs and default display names come from Skill declarations;
|
|
268
302
|
- optional `examples`, `input_modes`, and `output_modes` map to A2A Agent Card fields;
|
|
269
|
-
- Handler internals are
|
|
303
|
+
- Handler internals are not exposed;
|
|
270
304
|
- `public_base_url` wins when configured, otherwise the request base URL is used;
|
|
271
305
|
- default input/output mode is `text/plain`;
|
|
272
|
-
- Streaming, Push Notifications, and Extended Agent Cards are disabled
|
|
273
|
-
- generated Cards pass the
|
|
306
|
+
- Streaming, Push Notifications, and Extended Agent Cards are disabled;
|
|
307
|
+
- generated Cards pass the `agent2agent 2.0.0` Agent Card schema.
|
|
274
308
|
|
|
275
309
|
## Task Lifecycle
|
|
276
310
|
|
|
277
|
-
Internally
|
|
311
|
+
Internally, `a2a-rails` keeps SDK-independent Task states:
|
|
278
312
|
|
|
279
313
|
```text
|
|
280
314
|
SUBMITTED
|
|
@@ -302,114 +336,65 @@ String → Text Part
|
|
|
302
336
|
Hash / Array → Data Part
|
|
303
337
|
nil → no Artifact
|
|
304
338
|
other object → ArtifactMappingError
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
v0.1 Task behavior:
|
|
308
|
-
|
|
309
|
-
- server-generated Task IDs;
|
|
310
|
-
- client Context ID preserved, otherwise server-generated;
|
|
311
|
-
- terminal states are immutable;
|
|
312
|
-
- thread-safe process-local `Task::MemoryStore` by default;
|
|
313
|
-
- `SendMessage`, `GetTask`, `ListTasks`, and `CancelTask` supported;
|
|
314
|
-
- `ListTasks` supports context/state/timestamp filters, stable newest-first ordering, page sizes 1–100, and opaque snapshot pagination;
|
|
315
|
-
- cancellation is atomic, but does not interrupt Handler execution or undo business side effects;
|
|
316
|
-
- v0.1 Handler execution accepts text Message Parts;
|
|
317
|
-
- continuing an existing Task via `message.taskId` is not supported;
|
|
318
|
-
- `INPUT_REQUIRED` and `AUTH_REQUIRED` are out of scope.
|
|
319
339
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
Step 15-10 provides:
|
|
325
|
-
|
|
326
|
-
- an automatically mounted Rails Engine;
|
|
327
|
-
- thin `ActionController::API` controllers;
|
|
328
|
-
- `/.well-known/agent-card.json` and `/a2a` routes;
|
|
329
|
-
- lazy application Agent resolution;
|
|
330
|
-
- one process-local Runtime that shares the default Task Store across HTTP requests;
|
|
331
|
-
- request-derived Agent Card URL fallback;
|
|
332
|
-
- a logging boundary that suppresses the upstream SDK `A2A::Server::Triage` INFO log containing the full Rack environment while restoring prior Console logging state afterward.
|
|
333
|
-
|
|
334
|
-
## Generators
|
|
335
|
-
|
|
336
|
-
Step 15-11 implements:
|
|
337
|
-
|
|
338
|
-
```text
|
|
339
|
-
a2a:rails:install
|
|
340
|
-
a2a:rails:agent NAME
|
|
340
|
+
Unreleased Step 17-3 (not in RubyGems v0.1.0):
|
|
341
|
+
A2A::Rails::FileArtifact.bytes(...) → File Part with base64 raw bytes
|
|
342
|
+
A2A::Rails::FileArtifact.url(...) → File Part with an HTTPS URL
|
|
341
343
|
```
|
|
342
344
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
- `install` creates only `config/initializers/a2a_rails.rb`;
|
|
346
|
-
- `agent echo` creates only `app/agents/echo_agent.rb`;
|
|
347
|
-
- the Agent scaffold has no dangling Handler reference;
|
|
348
|
-
- the generated Agent is not automatically registered;
|
|
349
|
-
- namespace-based generator invocation reproduces the documented files;
|
|
350
|
-
- the generated Echo setup boots under Rails and completes a real A2A `SendMessage` request.
|
|
351
|
-
|
|
352
|
-
Step 15-12 verifies the same generator contract from the **built and installed gem**, in a clean Rails application outside the repository source checkout.
|
|
353
|
-
|
|
354
|
-
## Packaged Gem Verification
|
|
345
|
+
**File Artifact output is unreleased.** Rails Handlers can explicitly return a `FileArtifact.bytes(data:, filename:, media_type:)` or `FileArtifact.url(url:, filename:, media_type:)`. See the [File Artifact output guide](docs/guides/file-artifacts.md). Input file Parts, downloading remote URLs and production file authorization are not supplied by the Gem.
|
|
355
346
|
|
|
356
|
-
Step
|
|
347
|
+
**Unreleased Step 17-4:** A2A v1.0 also permits a direct `Message` from `SendMessage`. The host Agent can opt into `response_mode :message` or select the mode using a callable; the default remains `:task`. Direct replies do **not** create Task records. See the [Direct Message response guide](docs/guides/direct-message-responses.md) for the contract and safety implications.
|
|
357
348
|
|
|
358
|
-
|
|
359
|
-
- the package contains the Engine, generator classes, generator templates, and routes required at runtime;
|
|
360
|
-
- the clean app resolves `a2a-rails-0.1.0` from the installed RubyGems path, not from the repository checkout;
|
|
361
|
-
- actual `bin/rails generate` commands discover both generators;
|
|
362
|
-
- generated files match the documented scaffold contract;
|
|
363
|
-
- the clean app boots and serves the generated Agent Card;
|
|
364
|
-
- `SendMessage` completes with the Echo Artifact;
|
|
365
|
-
- Step 15-12 verified package SHA256: `09c55122c5d5e6cb5f9b735664d70b000f67d23dcaad9f4a8ff960dbe02dd410`.
|
|
349
|
+
Supported Task operations:
|
|
366
350
|
|
|
367
|
-
|
|
351
|
+
- `SendMessage`
|
|
352
|
+
- `GetTask`
|
|
353
|
+
- `ListTasks`
|
|
354
|
+
- `CancelTask`
|
|
368
355
|
|
|
369
|
-
|
|
356
|
+
`ListTasks` supports context/state/timestamp filters, stable newest-first ordering, page sizes 1–100, and opaque snapshot pagination.
|
|
370
357
|
|
|
371
|
-
|
|
358
|
+
The default `Task::MemoryStore` is thread-safe but process-local. Tasks and pagination cursors are not durable across process restarts and are not shared between processes.
|
|
372
359
|
|
|
373
|
-
- [
|
|
374
|
-
- [v0.1.0 release notes](docs/release/v0.1.0.md)
|
|
375
|
-
- [v0.1.0 release checklist](docs/release/v0.1.0-checklist.md)
|
|
376
|
-
|
|
377
|
-
The gemspec publishes canonical links for source code, changelog, documentation, and issue tracking, and requires RubyGems MFA. `README.md`, `CHANGELOG.md`, and `LICENSE` are verified as package contents.
|
|
360
|
+
**Unreleased Step 21:** applications that need durable, multi-worker Task state can opt into `config.task_store = :active_record`. The ActiveRecordStore uses owner-scoped SQL access, row-locked transitions, signed keyset cursors, terminal retention, bounded pruning and maintenance limits. See [ActiveRecord Task Store](docs/guides/active-record-task-store.md). PostgreSQL 16 persistence/locking smoke is verified in CI.
|
|
378
361
|
|
|
379
|
-
|
|
362
|
+
**Unreleased Step 22:** Task execution remains synchronous by default. Hosts can opt into ActiveJob-backed async execution globally, per Agent, or per Skill; precedence is **Skill > Agent > global**.
|
|
380
363
|
|
|
381
|
-
|
|
364
|
+
```ruby
|
|
365
|
+
A2A::Rails.configure do |config|
|
|
366
|
+
config.task_store = :active_record
|
|
367
|
+
config.task_execution_mode = :async
|
|
368
|
+
end
|
|
382
369
|
|
|
383
|
-
|
|
370
|
+
class ReportsAgent < A2A::Rails::Agent
|
|
371
|
+
execution_mode :sync
|
|
384
372
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
373
|
+
skill :build_report,
|
|
374
|
+
description: "Build a report",
|
|
375
|
+
tags: %w[report],
|
|
376
|
+
handler: Reports::Build,
|
|
377
|
+
execution_mode: :async
|
|
378
|
+
end
|
|
390
379
|
```
|
|
391
380
|
|
|
392
|
-
|
|
381
|
+
Async `SendMessage` persists and returns a `SUBMITTED` Task, then a Gem-owned ActiveJob worker claims it atomically and executes the already-selected Skill. Direct-Message responses remain synchronous because they do not create a persisted Task to poll.
|
|
393
382
|
|
|
394
|
-
|
|
395
|
-
- SDK spike: Ruby 3.3 / 3.4 / 4.0
|
|
396
|
-
- Rails: Ruby 3.3 / 3.4 / 4.0 × Rails 8.0 / 8.1
|
|
397
|
-
- Packaged gem: Ruby 3.4 + clean Rails 8.1 application
|
|
383
|
+
Production async use requires a shared/durable Task Store plus a durable ActiveJob backend. The Gem does not provide a distributed transaction between Task persistence and queue enqueue: a small crash window remains after Task commit and before queue acknowledgement. Generic Handler retries are intentionally disabled; duplicate Job delivery is suppressed at Task claim, but exactly-once external side effects are **not** guaranteed. Running cancellation is logical/best-effort, and ambiguous `WORKING` Tasks are not automatically replayed after a worker crash. See [ActiveJob Task Execution](docs/design/active-job-task-execution.md) and [queue adapter / HTTP async verification](docs/testing/queue-adapters.md).
|
|
398
384
|
|
|
399
|
-
|
|
385
|
+
Cancellation changes Task state atomically, but does not stop already-running Handler code or reverse application side effects.
|
|
400
386
|
|
|
401
|
-
|
|
402
|
-
13 / 13 CI jobs green
|
|
403
|
-
70 tests
|
|
404
|
-
240 assertions
|
|
405
|
-
0 failures
|
|
406
|
-
0 errors
|
|
407
|
-
0 skips
|
|
408
|
-
```
|
|
387
|
+
## Architecture
|
|
409
388
|
|
|
410
|
-
|
|
389
|
+
Key boundaries:
|
|
411
390
|
|
|
412
|
-
|
|
391
|
+
- Rails Engine / controllers / routes form the Rails integration layer;
|
|
392
|
+
- SDK-specific behavior stays behind `Protocol::Adapter` / `Protocol::Agent2AgentAdapter`;
|
|
393
|
+
- A2A camelCase fields, `TASK_STATE_*`, SDK schema objects, and SDK errors stay in the Protocol layer;
|
|
394
|
+
- Agent / Handler application constants are resolved lazily through Rails;
|
|
395
|
+
- ActiveRecord is optional and loaded only when ActiveRecordStore is selected; ActiveJob is not a runtime requirement.
|
|
396
|
+
|
|
397
|
+
Gem structure:
|
|
413
398
|
|
|
414
399
|
```text
|
|
415
400
|
lib/a2a/rails/
|
|
@@ -429,23 +414,6 @@ lib/generators/a2a/rails/
|
|
|
429
414
|
└── templates/
|
|
430
415
|
```
|
|
431
416
|
|
|
432
|
-
Key boundaries:
|
|
433
|
-
|
|
434
|
-
- Rails Engine / controllers / routes are only the Rails integration layer.
|
|
435
|
-
- SDK-specific behavior stays behind `Protocol::Adapter` / `Protocol::Agent2AgentAdapter`.
|
|
436
|
-
- A2A camelCase fields, `TASK_STATE_*`, SDK schema objects, and SDK errors stay in the Protocol layer.
|
|
437
|
-
- Agent / Handler application constants remain lazily resolved through Rails.
|
|
438
|
-
- ActiveRecord and ActiveJob are not runtime requirements.
|
|
439
|
-
- v0.1 targets Ruby `>= 3.3` and Rails `>= 8.0, < 8.2`.
|
|
440
|
-
|
|
441
|
-
Current main dependencies:
|
|
442
|
-
|
|
443
|
-
- `agent2agent ~> 2.0.0`
|
|
444
|
-
- `actionpack >= 8.0, < 8.2`
|
|
445
|
-
- `json < 3`
|
|
446
|
-
- `rack >= 3.0, < 4`
|
|
447
|
-
- `railties >= 8.0, < 8.2`
|
|
448
|
-
|
|
449
417
|
## v0.1 Scope
|
|
450
418
|
|
|
451
419
|
Included:
|
|
@@ -456,15 +424,14 @@ Included:
|
|
|
456
424
|
- Agent Card generation
|
|
457
425
|
- `/.well-known/agent-card.json`
|
|
458
426
|
- `POST /a2a`
|
|
459
|
-
- Task lifecycle
|
|
460
|
-
- `GetTask`, `ListTasks`, `CancelTask`
|
|
427
|
+
- synchronous Task lifecycle
|
|
428
|
+
- `SendMessage`, `GetTask`, `ListTasks`, `CancelTask`
|
|
461
429
|
- `A2A-Version: 1.0` validation
|
|
462
430
|
- in-memory Task Store
|
|
463
431
|
- Rails Engine / Routes
|
|
464
432
|
- Configuration
|
|
465
433
|
- `install` / `agent` generators
|
|
466
434
|
- Rails logging boundary
|
|
467
|
-
- test support
|
|
468
435
|
|
|
469
436
|
Not included in v0.1:
|
|
470
437
|
|
|
@@ -474,26 +441,116 @@ Not included in v0.1:
|
|
|
474
441
|
- SSE / BiDi Streaming
|
|
475
442
|
- Push Notifications
|
|
476
443
|
- gRPC
|
|
477
|
-
- Human-in-the-loop
|
|
478
|
-
- `INPUT_REQUIRED` / `AUTH_REQUIRED`
|
|
479
|
-
- Agent Registry / Marketplace
|
|
444
|
+
- Human-in-the-loop flows
|
|
445
|
+
- `INPUT_REQUIRED` / `AUTH_REQUIRED`
|
|
480
446
|
- OAuth Server
|
|
447
|
+
- Agent Registry / Marketplace
|
|
481
448
|
- Authorization Engine
|
|
482
449
|
- ActingFor integration
|
|
483
450
|
- Admin UI
|
|
484
451
|
- LLM Agent Framework
|
|
485
452
|
- Orchestration Framework
|
|
486
453
|
|
|
454
|
+
## Verification
|
|
455
|
+
|
|
456
|
+
The v0.1.0 release candidate completed:
|
|
457
|
+
|
|
458
|
+
```text
|
|
459
|
+
13 / 13 CI jobs green
|
|
460
|
+
70 tests
|
|
461
|
+
240 assertions
|
|
462
|
+
0 failures
|
|
463
|
+
0 errors
|
|
464
|
+
0 skips
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
After publication, `a2a-rails 0.1.0` was fetched back from RubyGems and its SHA256 matched the exact artifact that was pushed.
|
|
468
|
+
|
|
469
|
+
Final published artifact SHA256:
|
|
470
|
+
|
|
471
|
+
```text
|
|
472
|
+
23d34bde6f436723bf01735f3a507cf8529975b1dfed5d5da9b063fc0480b19f
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
A fresh Rails 8.1 application then installed the published Gem from RubyGems and verified:
|
|
476
|
+
|
|
477
|
+
```text
|
|
478
|
+
Agent Card HTTP: 200
|
|
479
|
+
Agent name: Echo Agent
|
|
480
|
+
Skill: reply
|
|
481
|
+
SendMessage HTTP: 200
|
|
482
|
+
Task state: TASK_STATE_COMPLETED
|
|
483
|
+
Artifact: Echo: Hello
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
See [docs/release/v0.1.0-record.md](docs/release/v0.1.0-record.md) for the complete release evidence.
|
|
487
|
+
|
|
488
|
+
## Cross-language A2A interoperability — unreleased main source
|
|
489
|
+
|
|
490
|
+
[Step 18 / PR #27](https://github.com/cuichangquan/a2a-rails/pull/27) verified the **official** Python `a2a-sdk==1.2.2` and Go `a2a-go/v2 v2.6.0` clients against a loopback-only Rails JSON-RPC A2A v1.0 Agent. Both discovered Agent Cards, decoded Task and direct Message responses, queried Tasks and checked terminal cancellation and version errors.
|
|
491
|
+
|
|
492
|
+
- [Final official Python/Go interoperability CI](https://github.com/cuichangquan/a2a-rails/actions/runs/37576849497) — **both PASS**.
|
|
493
|
+
- [Final Ruby/Rails regression CI](https://github.com/cuichangquan/a2a-rails/actions/runs/37576849516) — **13/13 PASS**.
|
|
494
|
+
- [Interop test code, reproduction and limitations](docs/testing/cross-language-interop.md).
|
|
495
|
+
|
|
496
|
+
This evidence applies to the later source/pre-release line, not RubyGems v0.1.0; these tests do not certify full A2A interoperability or public-production safety.
|
|
497
|
+
|
|
498
|
+
## Test Strategy
|
|
499
|
+
|
|
500
|
+
v0.1 uses Minitest with four layers:
|
|
501
|
+
|
|
502
|
+
```text
|
|
503
|
+
4. Protocol E2E / Smoke Tests
|
|
504
|
+
3. Rails Integration Tests
|
|
505
|
+
2. Adapter Contract Tests
|
|
506
|
+
1. Core Unit Tests
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
The CI matrix covers:
|
|
510
|
+
|
|
511
|
+
- Gem: Ruby 3.3 / 3.4 / 4.0
|
|
512
|
+
- SDK spike: Ruby 3.3 / 3.4 / 4.0
|
|
513
|
+
- Rails: Ruby 3.3 / 3.4 / 4.0 × Rails 8.0 / 8.1
|
|
514
|
+
- Packaged Gem: Ruby 3.4 + clean Rails 8.1 application
|
|
515
|
+
|
|
516
|
+
Known warning-enabled output from upstream `agent2agent 2.0.0` can include circular-require, indentation, and URI-parser warnings. Those warnings are distinct from the Rack-environment INFO logging that `a2a-rails` suppresses at the Rails-facing adapter boundary.
|
|
517
|
+
|
|
518
|
+
## Release Documents
|
|
519
|
+
|
|
520
|
+
- [CHANGELOG](CHANGELOG.md)
|
|
521
|
+
- [v0.1.0 Release Notes](docs/release/v0.1.0.md)
|
|
522
|
+
- [v0.1.0 Release Checklist](docs/release/v0.1.0-checklist.md)
|
|
523
|
+
- [v0.1.0 Release Record](docs/release/v0.1.0-record.md)
|
|
524
|
+
|
|
487
525
|
## Design Documents
|
|
488
526
|
|
|
489
527
|
- [v0.1 Design Decisions](docs/design/v0.1-decisions.md)
|
|
490
528
|
- [v0.1 Test Strategy](docs/design/test-strategy.md)
|
|
491
529
|
- [v0.1 Gem Structure](docs/design/gem-structure.md)
|
|
492
530
|
- [v0.1 Quick Start Design](docs/design/quick-start.md)
|
|
493
|
-
- [
|
|
531
|
+
- [SDK compatibility findings](docs/design/sdk-compatibility-spike.md)
|
|
494
532
|
|
|
495
|
-
##
|
|
533
|
+
## License
|
|
496
534
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
535
|
+
The Gem is available as open source under the terms of the MIT License. See [LICENSE](LICENSE).
|
|
536
|
+
|
|
537
|
+
## Official A2A Resources / A2A公式資料
|
|
538
|
+
|
|
539
|
+
- [A2A Protocol overview / A2Aの全体像(日本語・A4 1枚PDF)](docs/guides/a2a-protocol-overview-ja.pdf) — 公式資料をもとに作成した学習資料(2026-10-07)。
|
|
540
|
+
- [A2A at a glance (English, A4 one-page PDF)](docs/guides/a2a-protocol-overview-en.pdf) — Learning guide based on the official documentation (2026-10-07).
|
|
541
|
+
- [A2A Protocol documentation (Latest) / 公式ドキュメント](https://a2a-protocol.org/latest/)
|
|
542
|
+
- [A2A Protocol v1.0.0 documentation / v1.0.0ドキュメント](https://a2a-protocol.org/v1.0.0/)
|
|
543
|
+
- [A2A v1.0.0 Specification / v1.0.0仕様書](https://a2a-protocol.org/v1.0.0/specification/)
|
|
544
|
+
- [Official GitHub / 公式GitHub: a2aproject/A2A](https://github.com/a2aproject/A2A)
|
|
545
|
+
- [Official samples / 公式サンプル: a2aproject/a2a-samples](https://github.com/a2aproject/a2a-samples)
|
|
546
|
+
|
|
547
|
+
## Articles & Community / 紹介記事・コミュニティ
|
|
548
|
+
|
|
549
|
+
### Japanese articles / 日本語の紹介記事
|
|
550
|
+
|
|
551
|
+
- [Zenn: RailsアプリをA2A対応Agentとして公開する「a2a-rails」を作りました](https://zenn.dev/ccq/articles/d52c1b29982663)
|
|
552
|
+
- [Qiita: RailsアプリをA2A対応Agentとして公開する「a2a-rails」を作りました](https://qiita.com/ccq1170/items/0852445745403c8e848b)
|
|
553
|
+
|
|
554
|
+
### Community submissions / コミュニティへの投稿
|
|
555
|
+
|
|
556
|
+
- [awesome-a2a: Resource suggestion #190 / 掲載提案](https://github.com/ai-boost/awesome-a2a/issues/190)
|
|
@@ -5,7 +5,10 @@ module A2A
|
|
|
5
5
|
class AgentCardsController < ApplicationController
|
|
6
6
|
def show
|
|
7
7
|
response.set_header("A2A-Version", Protocol::Agent2AgentAdapter::PROTOCOL_VERSION)
|
|
8
|
+
response.set_header("Cache-Control", "no-store")
|
|
8
9
|
render json: A2A::Rails.runtime.agent_card(request_base_url: request.base_url)
|
|
10
|
+
rescue A2A::Rails::ConfigurationError
|
|
11
|
+
render json: { error: "Agent Card unavailable" }, status: :service_unavailable
|
|
9
12
|
end
|
|
10
13
|
end
|
|
11
14
|
end
|