forked from cleveragents/cleveragents-core
33900eab03
Implement the JSON-RPC 2.0 wire format layer and method routing for the A2A protocol, building on the ACP-to-A2A rename from #688. New modules in src/cleveragents/a2a/: - jsonrpc_models.py: Pydantic v2 models for JSON-RPC 2.0 request, response, error, and notification envelopes - jsonrpc_router.py: Method router that dispatches JSON-RPC requests to registered handlers, with support for standard A2A methods and _cleveragents/ extension methods - jsonrpc_handlers.py: Handlers for standard A2A methods (message/send, message/stream, tasks/get, tasks/cancel) and extension method factory that delegates to A2aLocalFacade - jsonrpc_errors.py: Maps domain error taxonomy to JSON-RPC 2.0 error codes (standard -32700 to -32603, application -32001 to -32008) - agent_card.py: Agent Card model and generation for the /.well-known/agent.json capability advertisement endpoint Standard A2A methods: - message/send: Create/update tasks via messages - message/stream: Streaming variant with SSE event indicators - tasks/get: Retrieve task by ID - tasks/cancel: Cancel running tasks CleverAgents extension methods (_cleveragents/ prefix): - plan/use, execute, status, diff, apply, cancel - registry/list_tools, list_resources - context/show - sync/status - namespace/list - health Tests: - Behave BDD: features/a2a_jsonrpc.feature (28 scenarios) - Robot Framework: robot/a2a_jsonrpc.robot (9 integration tests) ISSUES CLOSED: #690
183 lines
7.8 KiB
Gherkin
183 lines
7.8 KiB
Gherkin
Feature: A2A JSON-RPC 2.0 wire format and method routing
|
|
As a client of the A2A protocol
|
|
I want JSON-RPC 2.0 framing for all A2A operations
|
|
So that communication follows the standard wire format
|
|
|
|
# ---------------------------------------------------------------
|
|
# JSON-RPC 2.0 request/response models
|
|
# ---------------------------------------------------------------
|
|
|
|
Scenario: Valid JSON-RPC request has correct fields
|
|
Given a JSON-RPC request with method "message/send" and id 1
|
|
Then the request jsonrpc field should be "2.0"
|
|
And the request method should be "message/send"
|
|
And the request id should be 1
|
|
And the request should not be a notification
|
|
|
|
Scenario: Request without id is a notification
|
|
Given a JSON-RPC notification with method "task/statusUpdate"
|
|
Then the request should be a notification
|
|
And the request id should be None
|
|
|
|
Scenario: Success response has result and no error
|
|
Given a JSON-RPC success response with id 1 and result {"status": "ok"}
|
|
Then the response jsonrpc field should be "2.0"
|
|
And the response id should be 1
|
|
And the response should have result
|
|
And the response should not have error
|
|
|
|
Scenario: Error response has error and no result
|
|
Given a JSON-RPC error response with id 1 code -32601 and message "Method not found"
|
|
Then the response should have error
|
|
And the response should not have result
|
|
And the response error code should be -32601
|
|
And the response error message should be "Method not found"
|
|
|
|
Scenario: JSON-RPC request rejects invalid version
|
|
When I try to create a request with jsonrpc "1.0"
|
|
Then a jsonrpc validation error should be raised
|
|
|
|
Scenario: JSON-RPC request rejects empty method
|
|
When I try to create a request with empty method
|
|
Then a jsonrpc validation error should be raised
|
|
|
|
# ---------------------------------------------------------------
|
|
# JSON-RPC router dispatch
|
|
# ---------------------------------------------------------------
|
|
|
|
Scenario: Router dispatches to registered handler
|
|
Given a JSON-RPC router with "echo" handler
|
|
When I dispatch a request for method "echo" with id 1 and params {"msg": "hello"}
|
|
Then the dispatch response should be successful
|
|
And the dispatch result should contain key "echo" with value "hello"
|
|
|
|
Scenario: Router returns method-not-found for unknown method
|
|
Given a JSON-RPC router with "echo" handler
|
|
When I dispatch a request for method "unknown/method" with id 2
|
|
Then the dispatch response should have error code -32601
|
|
|
|
Scenario: Router returns None for notifications
|
|
Given a JSON-RPC router with "echo" handler
|
|
When I dispatch a notification for method "echo"
|
|
Then the dispatch response should be None
|
|
|
|
Scenario: Router lists registered methods
|
|
Given a JSON-RPC router with standard A2A handlers
|
|
Then the router should have method "message/send"
|
|
And the router should have method "message/stream"
|
|
And the router should have method "tasks/get"
|
|
And the router should have method "tasks/cancel"
|
|
And the router standard methods should have 4 entries
|
|
|
|
Scenario: Router lists extension methods
|
|
Given a JSON-RPC router with extension handlers
|
|
Then the router extension methods should include "_cleveragents/plan/status"
|
|
And the router extension methods should include "_cleveragents/health"
|
|
|
|
Scenario: Router unregister removes handler
|
|
Given a JSON-RPC router with "echo" handler
|
|
When I unregister the "echo" handler
|
|
Then the router should not have method "echo"
|
|
|
|
# ---------------------------------------------------------------
|
|
# Standard A2A method handlers
|
|
# ---------------------------------------------------------------
|
|
|
|
Scenario: message/send creates a task
|
|
Given a JSON-RPC router with standard A2A handlers
|
|
When I dispatch message/send with message role "user" and text "Hello agent"
|
|
Then the dispatch response should be successful
|
|
And the dispatch result should contain key "id"
|
|
And the dispatch result status state should be "working"
|
|
|
|
Scenario: message/stream creates a streaming task
|
|
Given a JSON-RPC router with standard A2A handlers
|
|
When I dispatch message/stream with message role "user" and text "Stream me"
|
|
Then the dispatch response should be successful
|
|
And the dispatch result should contain key "streaming" with value true
|
|
|
|
Scenario: tasks/get retrieves a task
|
|
Given a JSON-RPC router with standard A2A handlers
|
|
And a task created via message/send
|
|
When I dispatch tasks/get with the created task id
|
|
Then the dispatch response should be successful
|
|
And the dispatch result should contain key "id"
|
|
|
|
Scenario: tasks/get returns error for missing task
|
|
Given a JSON-RPC router with standard A2A handlers
|
|
When I dispatch tasks/get with id "nonexistent-task-id"
|
|
Then the dispatch response should have error code -32001
|
|
|
|
Scenario: tasks/cancel cancels a running task
|
|
Given a JSON-RPC router with standard A2A handlers
|
|
And a task created via message/send
|
|
When I dispatch tasks/cancel with the created task id
|
|
Then the dispatch response should be successful
|
|
And the dispatch result status state should be "canceled"
|
|
|
|
Scenario: tasks/cancel rejects already-completed task
|
|
Given a JSON-RPC router with standard A2A handlers
|
|
And a completed task
|
|
When I dispatch tasks/cancel with the completed task id
|
|
Then the dispatch response should have error code -32006
|
|
|
|
# ---------------------------------------------------------------
|
|
# JSON-RPC error code mapping
|
|
# ---------------------------------------------------------------
|
|
|
|
Scenario: ResourceNotFoundError maps to -32001
|
|
When I map a ResourceNotFoundError to JSON-RPC
|
|
Then the mapped error code should be -32001
|
|
|
|
Scenario: ValidationError maps to -32602
|
|
When I map a ValidationError to JSON-RPC
|
|
Then the mapped error code should be -32602
|
|
|
|
Scenario: PlanError maps to -32002
|
|
When I map a PlanError to JSON-RPC
|
|
Then the mapped error code should be -32002
|
|
|
|
Scenario: AuthenticationError maps to -32003
|
|
When I map an AuthenticationError to JSON-RPC
|
|
Then the mapped error code should be -32003
|
|
|
|
Scenario: Generic exception maps to -32603
|
|
When I map a generic Exception to JSON-RPC
|
|
Then the mapped error code should be -32603
|
|
|
|
# ---------------------------------------------------------------
|
|
# Agent Card generation
|
|
# ---------------------------------------------------------------
|
|
|
|
Scenario: Agent Card has required fields
|
|
Given a generated Agent Card with name "TestAgent"
|
|
Then the agent card name should be "TestAgent"
|
|
And the agent card protocol version should be "1.0"
|
|
And the agent card should have capabilities
|
|
And the agent card should have streaming enabled
|
|
|
|
Scenario: Agent Card includes extension methods
|
|
Given a generated Agent Card with default extensions
|
|
Then the agent card should have extensions
|
|
And the first extension should have uri "_cleveragents"
|
|
And the extension methods should include "_cleveragents/plan/status"
|
|
|
|
Scenario: Agent Card serializes to dict
|
|
Given a generated Agent Card with name "SerializeTest"
|
|
When I serialize the agent card to dict
|
|
Then the serialized card should have key "name" with value "SerializeTest"
|
|
And the serialized card should have key "protocol_version" with value "1.0"
|
|
|
|
Scenario: Agent Card well-known path is correct
|
|
Then the agent card path should be "/.well-known/agent.json"
|
|
|
|
# ---------------------------------------------------------------
|
|
# Notification model
|
|
# ---------------------------------------------------------------
|
|
|
|
Scenario: Notification has method and params but no id
|
|
Given a JSON-RPC notification model with method "task/statusUpdate" and params {"taskId": "t1"}
|
|
Then the notification jsonrpc should be "2.0"
|
|
And the notification method should be "task/statusUpdate"
|
|
And the notification params key "taskId" should be "t1"
|