Repository navigation
Conversation
…bs#387 Phase-B (reconciled) Stacks on Phase-A (the AG-UI core) and closes out Phase-B of awslabs#387 as a single, net-new commit. This squashes the reconciled A2A hardening work (the "best of both implementations" plus review 4638092590, adjudicated in #20) into one authored change, layered on top of the core PR so it can be reviewed as a stacked PR. Additive and default-off: the Agent Card / A2A listener runs on its own port (:9890), is gated by CAO_AGENT_CARD_ENABLED, and binds loopback unless an operator opts into external discoverability. With the auth layer off, the untokened behavior stays byte-identical. A2A transport & Agent Card - A2A v1.0 JSON-RPC 2.0 (task.send / task.get / task.cancel), SSE streaming, and REST polling for agent-to-agent interop. - Ed25519-signed Agent Card + JWKS published on the dedicated listener. - Per-method scope enforcement resolved BEFORE any body parse (task.send/cancel -> cao:write; task.get and reads -> cao:read; cao:admin satisfies any), returning 401/403 with WWW-Authenticate and JSON-RPC error bodies; the stream gate lives at the APIRouter so future routes inherit it. Hardening (best-of-both + review 4638092590) - Fail-closed mount guard: the pure _should_mount_a2a() decision refuses to mount the task routes on a non-loopback bind while auth is disabled; the Agent Card discovery doc still publishes. - Capacity as a transport condition: a full store refuses new task.send with RESOURCE_EXHAUSTED at HTTP 429 + Retry-After: 30 (domain errors still ride 200); bounded (CAO_A2A_MAX_TASKS) + TTL-evicting (CAO_A2A_TASK_TTL). - Tolerant env bounds: from_env survives malformed/non-positive values with a default + warning instead of a startup crash, and never disables the bound. - Create-only task ids: a resubmitted id is refused with INVALID_PARAMS and the stored task is untouched, so a peer can never overwrite another peer's task; Task.from_dict tolerates an omitted id (server-UUID path). Shift-left testing - Real-JWT auth matrix (test/a2a/test_auth_enforcement.py) — nothing in the token path stubbed; per-method scopes, unknown-method METHOD_NOT_FOUND, and bearer edge cases (case-insensitive scheme, empty bearer, duplicate headers). - Store bounds (test/a2a/test_store_bounds.py), create-only id integrity (test/a2a/test_task_id_integrity.py), RPC/stream/coverage suites, and the Agent Card builder/router/signing suites (test/agent_card/*). - 8-case parametrized mount-guard decision table + lifespan integration (test/api/test_a2a_mount_guard.py); the core PR's "A2A absent" default-off assertion is reconciled to "present but default-off". - Authenticated e2e round-trip (test/e2e/test_a2a_roundtrip.py): 401 untokened, then the full send -> poll lifecycle with real JWTs. Video verification proof - Builds on the Phase-A recorded walkthroughs under docs/media/ (agui-generative-ui-demo.webm, agui-live-remediation-demo.webm); the A2A verification artifacts and applied review patches are captured under docs/reviews/pr387-a2a-fixes/ and docs/reviews/pr387-phase1-verification-*. No dependency changes: authlib->dev and the python-multipart removal already landed in the core PR, so this rebases cleanly onto Phase-A. Co-authored-by: Kiro Agent <244629292+kiro-agent@users.noreply.github.com>
5 tasks done
Owner
Author
|
Closing at the author's own request, recorded in #25's body: 'Supersedes the unsigned #23 (same content, now SSH-signed → Verified). Please close #23.' A symbol-level containment check across all ten of this PR's a2a/agent_card modules finds zero symbols absent from #25. Seven non-code files that #25 does NOT carry have been archived to disk first: the six docs/reviews/ files (two of them literal .patch files) and docs/auth.md. For the record, this PR's 40-file diff remains the cleanest shape of the A2A delta and is the shape a fresh upstream submission should copy — #25 re-presents 104 files of already-landed Phase-A work alongside it. Branch preserved. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request was created by @kiro-agent on behalf of @plauzy 👻
Comment with /kiro fix to address specific feedback or /kiro all to address everything.
Learn about Kiro Web
What this is
This closes out Phase-B of awslabs#387 as a single, net-new commit, stacked on top of Phase-A (#22). It reproduces the reconciled A2A hardening work — "best of both implementations" plus review 4638092590, adjudicated in #20 — squashed into one authored change.
Co-authored-by: Kiro Agent), matching awslabs/cli-agent-orchestrator@33c593d.kiro/pr387-phase-a-agui-core(Phase-A), so this PR shows only the incremental A2A surface.Scope (additive, default-off)
The Agent Card / A2A listener runs on its own port (
:9890), gated byCAO_AGENT_CARD_ENABLED, binds loopback unless an operator opts into external discoverability. Untokened behavior stays byte-identical with the auth layer off.task.send/get/cancel), SSE streaming, REST polling.send/cancel→cao:write;get/reads →cao:read;cao:adminsatisfies any),401/403withWWW-Authenticate+ JSON-RPC bodies; the stream gate lives at theAPIRouter.Hardening (best-of-both + review 4638092590)
_should_mount_a2a()refuses to mount task routes on a non-loopback bind while auth is disabled; the Agent Card doc still publishes.task.sendwithRESOURCE_EXHAUSTEDat HTTP 429 +Retry-After: 30(domain errors still ride 200); bounded (CAO_A2A_MAX_TASKS) + TTL-evicting (CAO_A2A_TASK_TTL).from_envsurvives malformed/non-positive values (default + warning), never disables the bound.INVALID_PARAMSand the stored task is untouched;Task.from_dicttolerates an omitted id.Shift-left testing
test/a2a/test_auth_enforcement.py) — nothing in the token path stubbed.test/a2a/*,test/agent_card/*).test/api/test_a2a_mount_guard.py).test/e2e/test_a2a_roundtrip.py).test/api/test_default_off_listeners.py).:9890port-contention when run back-to-back in-process (a pre-existing quirk noted in reconcile PR #387 remediation — A2A hardened (PR-B, best of both + review 4638092590) #20); it passes in isolation.Video verification proof
Builds on Phase-A's recorded walkthroughs (
docs/media/agui-*.webm); the A2A verification artifacts and applied review patches are captured underdocs/reviews/pr387-a2a-fixes/anddocs/reviews/pr387-phase1-verification-2026-07-07.md.No dependency changes:
authlib→dev and thepython-multipartremoval already landed in Phase-A, so this rebases cleanly.Co-authored-by: Kiro Agent <244629292+kiro-agent@users.noreply.github.com>