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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -0
  3. data/README.md +262 -205
  4. data/app/controllers/a2a/rails/agent_cards_controller.rb +3 -0
  5. data/app/controllers/a2a/rails/requests_controller.rb +53 -0
  6. data/lib/a2a/rails/agent.rb +33 -2
  7. data/lib/a2a/rails/agent_card/builder.rb +3 -1
  8. data/lib/a2a/rails/agent_card/security.rb +115 -0
  9. data/lib/a2a/rails/authentication.rb +71 -0
  10. data/lib/a2a/rails/configuration.rb +95 -1
  11. data/lib/a2a/rails/dispatcher.rb +36 -4
  12. data/lib/a2a/rails/engine.rb +9 -0
  13. data/lib/a2a/rails/errors.rb +3 -0
  14. data/lib/a2a/rails/execution_plan.rb +40 -0
  15. data/lib/a2a/rails/file_artifact.rb +77 -0
  16. data/lib/a2a/rails/protocol/agent2agent_adapter.rb +11 -0
  17. data/lib/a2a/rails/protocol/request_handler.rb +124 -13
  18. data/lib/a2a/rails/protocol/task_mapper.rb +21 -0
  19. data/lib/a2a/rails/request_guard.rb +76 -0
  20. data/lib/a2a/rails/runtime.rb +15 -6
  21. data/lib/a2a/rails/skill.rb +7 -2
  22. data/lib/a2a/rails/task/active_record_store.rb +451 -0
  23. data/lib/a2a/rails/task/artifact_mapper.rb +2 -0
  24. data/lib/a2a/rails/task/lifecycle.rb +18 -8
  25. data/lib/a2a/rails/task/memory_store.rb +39 -15
  26. data/lib/a2a/rails/task/store.rb +16 -4
  27. data/lib/a2a/rails/task_execution_arguments.rb +78 -0
  28. data/lib/a2a/rails/task_execution_job.rb +79 -0
  29. data/lib/a2a/rails/version.rb +1 -1
  30. data/lib/a2a-rails.rb +7 -0
  31. data/lib/generators/a2a/rails/task_store_generator.rb +46 -0
  32. data/lib/generators/a2a/rails/templates/create_a2a_rails_tasks.rb.tt +31 -0
  33. data/lib/generators/a2a/rails/templates/initializer.rb.tt +4 -0
  34. data/lib/tasks/a2a_rails_tasks.rake +54 -0
  35. 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
- > **Status: the v0.1.0 release candidate is prepared and CI-verified; the actual public release is next.**
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
- ## Current Status
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
- The A2A v1.0 SDK integration path, Gem core, Protocol Adapter, Agent / Skill DSL, Task lifecycle, Configuration, Agent Card generation, Rails Engine HTTP endpoints, generators, source-tree Quick Start, packaged-gem installation path, and v0.1.0 release-candidate metadata are now verified.
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
- **Current stage:** Step 15-13 completed — v0.1.0 release-candidate preparation
22
+ ## Runnable Rails Demo / 実際に動くサンプル ⭐
12
23
 
13
- **Next step:** **Step 15-14 — Execute the v0.1.0 release: final artifact verification from the exact main commit, public-release readiness, `v0.1.0` tag, RubyGems publication, GitHub Release, and post-release smoke verification.**
24
+ **[a2a-rails-demo — independent Rails 8 Echo Agent](https://github.com/cuichangquan/a2a-rails-demo)**
14
25
 
15
- Step 15-13 adds `CHANGELOG.md`, canonical v0.1.0 release notes, a pre-release / publish / post-release checklist, and final RubyGems metadata for source, changelog, documentation, issue tracker, and MFA. The packaged-gem smoke now verifies those metadata fields plus README, CHANGELOG, LICENSE, Ruby compatibility, generator discovery, clean-app installation, Agent Card retrieval, and Echo `SendMessage`. The release-candidate CI is **13 / 13 green**, while the Gem suite remains **70 tests / 240 assertions / 0 failures / 0 errors / 0 skips**. See [Draft PR #7](https://github.com/cuichangquan/a2a-rails/pull/7).
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
- Step 15-12 verified the actual built gem in a clean Rails application. Step 15-11 added the intentionally small `install` / `agent` generators and the generator-backed source-tree Quick Start. Step 15-10 implemented the automatically mounted Rails Engine, thin API controllers, a process-local Runtime that shares the in-memory Task Store across HTTP requests, and the Rails-facing SDK logging boundary. Step 15-9 implemented lazy Configuration and Agent Card generation. Step 15-8 wired `SendMessage`, `GetTask`, `ListTasks`, and `CancelTask` through the real SDK. Step 15-7 implemented the SDK-independent Task core. Step 15-6 implemented the Rails-facing Agent / Skill / Dispatcher API. Step 15-5 introduced the loadable Gem skeleton and Protocol Adapter. Step 15-4 verified the `agent2agent 2.0.0` SDK path across the supported Ruby / Rails matrix.
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
- ## Development Progress
32
+ ## What is a2a-rails?
20
33
 
21
- - [x] 1. Research A2A Protocol v1.0
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
- Main principles:
48
+ The Gem provides:
65
49
 
66
- - Do not reimplement the A2A Protocol.
67
- - Focus on Rails integration.
68
- - Keep SDK-specific APIs out of the public API.
69
- - Prefer a small, non-streaming server scope for v0.1.
70
- - Keep a2a-rails independent from ActingFor.
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
- ## Quick Start
59
+ v0.1 is intentionally **server-first** and **non-streaming**.
73
60
 
74
- > The flow below is runtime-verified both from the repository source tree and from the built `a2a-rails-0.1.0` gem installed into a clean Rails application.
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
- ### 1. Add and install
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
- `install` creates only:
91
+ No explicit Engine mount or host `config/routes.rb` change is required.
86
92
 
87
- ```text
88
- config/initializers/a2a_rails.rb
89
- ```
93
+ ## Quick Start
90
94
 
91
- with:
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
- ```ruby
94
- A2A::Rails.configure do |config|
95
- config.agent = "YourAgent"
96
- config.public_base_url = ENV["A2A_PUBLIC_BASE_URL"]
97
- end
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
- `agent echo` creates only:
105
+ The install generator creates:
101
106
 
102
107
  ```text
103
- app/agents/echo_agent.rb
108
+ config/initializers/a2a_rails.rb
104
109
  ```
105
110
 
106
- with:
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
- # Add at least one skill.
115
- end
113
+ ```text
114
+ app/agents/echo_agent.rb
116
115
  ```
117
116
 
118
- The generator deliberately does **not** create a Handler, route, controller, Task Store, job, migration, or registration side effect. It also does not reference a Handler that does not exist yet.
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
- ### 3. Define the Skill
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`. The Agent class name remains a String until an A2A endpoint resolves it, preserving Rails autoload / reload behavior.
167
+ For localhost, leave `A2A_PUBLIC_BASE_URL` unset or set it to `http://localhost:3000`.
167
168
 
168
- No explicit Engine mount or host `config/routes.rb` change is required.
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
- - JSON-RPC A2A interface pointing to `http://localhost:3000/a2a`
191
+ - A2A interface URL ending in `/a2a`
187
192
 
188
- ### 6. Send the first message
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
- Success means:
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 [the complete Quick Start design and verification notes](docs/design/quick-start.md).
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
- ## Target Developer Experience
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**. Handlers receive SDK-independent Ruby Hashes for `message` and `context`.
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 never silently chooses the first Skill.
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
- ## Agent Card
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
- Agent Card behavior:
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
- - generated from the Agent / Skill DSL;
267
- - Skill IDs and default display names are generated from Skill declarations;
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 never exposed;
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 for v0.1;
273
- - generated Cards pass the real `agent2agent 2.0.0` Agent Card schema.
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 the Gem keeps SDK-independent Task states:
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
- The default store is for development and simple synchronous workloads. Tasks and pagination cursors are process-local and are not durable across process restarts or shared between processes.
321
-
322
- ## Rails Integration
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
- Verified contract:
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 15-12 verifies the distribution artifact itself:
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
- - `gem build a2a-rails.gemspec` produces `a2a-rails-0.1.0`;
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
- Step 15-13 extends this verification to user-facing release files and RubyGems metadata. The final published artifact must still be rebuilt and hashed from the exact tagged release commit.
351
+ - `SendMessage`
352
+ - `GetTask`
353
+ - `ListTasks`
354
+ - `CancelTask`
368
355
 
369
- ## Release Candidate
356
+ `ListTasks` supports context/state/timestamp filters, stable newest-first ordering, page sizes 1–100, and opaque snapshot pagination.
370
357
 
371
- Step 15-13 prepares, but does not publish, v0.1.0:
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
- - [CHANGELOG](CHANGELOG.md)
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
- No `v0.1.0` tag, RubyGems publication, GitHub Release, or repository-visibility change is performed in Step 15-13.
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
- ## Test Strategy
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
- v0.1 uses **Minitest** with four layers:
370
+ class ReportsAgent < A2A::Rails::Agent
371
+ execution_mode :sync
384
372
 
385
- ```text
386
- 4. Protocol E2E / Smoke Tests
387
- 3. Rails Integration Tests
388
- 2. Adapter Contract Tests
389
- 1. Core Unit Tests
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
- The current CI matrix covers:
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
- - Gem: Ruby 3.3 / 3.4 / 4.0
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
- Current Step 15-13 result:
385
+ Cancellation changes Task state atomically, but does not stop already-running Handler code or reverse application side effects.
400
386
 
401
- ```text
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
- Known warning-enabled output from upstream `agent2agent 2.0.0` remains, including circular-require / indentation / URI-parser warnings. Those warnings are distinct from the Rack-environment INFO logging that a2a-rails suppresses at the Rails-facing adapter boundary.
389
+ Key boundaries:
411
390
 
412
- ## Gem Structure
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` flows
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
- - [Step 15 SDK compatibility findings](docs/design/sdk-compatibility-spike.md)
531
+ - [SDK compatibility findings](docs/design/sdk-compatibility-spike.md)
494
532
 
495
- ## Release Documents
533
+ ## License
496
534
 
497
- - [CHANGELOG](CHANGELOG.md)
498
- - [v0.1.0 Release Notes](docs/release/v0.1.0.md)
499
- - [v0.1.0 Release Checklist](docs/release/v0.1.0-checklist.md)
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