Skip to content

Preserve inbound A2A message role instead of hardcoding assistant - #1122

Open
PratikDhanave (PratikDhanave) wants to merge 2 commits into
microsoft:mainfrom
PratikDhanaveFork:fix/a2a-inbound-role-mapping
Open

PratikDhanave (PratikDhanave) wants to merge 2 commits into
microsoft:mainfrom
PratikDhanaveFork:fix/a2a-inbound-role-mapping

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

The client inbound path passed a literal message.RoleAssistant for A2A messages (*a2a.Message) and task-status messages, discarding the role carried on the inbound message. So a peer message with the A2A user role was reported as assistant.

The hosting side already maps this (toAgentMessage: agent → assistant, otherwise user), and the Python client does the same (role="assistant" if msg.role == A2ARole.ROLE_AGENT else "user") — the client-path hardcode was an oversight.

Change

  • Add an agentRole helper and use it for the *a2a.Message case and the two task-status-message cases (streaming TaskStatusUpdateEvent and yieldTask input-required). Artifacts carry no role, so they stay assistant.

Test

  • TestRunPreservesInboundUserRole: a response *a2a.Message with MessageRoleUser now maps to RoleUser. Fails before (assistant), passes after. The existing MessageRoleAgent → assistant assertion still holds.

The client inbound path hardcoded message.RoleAssistant for A2A messages and
task-status messages, discarding the actual A2A role. A peer message with the
user role was reported as assistant. Map the role via a shared agentRole
helper (agent -> assistant, otherwise user), matching the hosting-side
toAgentMessage and the Python client. Artifacts carry no role and stay
assistant.
Copilot AI lite review requested due to automatic review settings September 20, 2026 04:30
@github-actions github-actions Bot added area:provider Changes files in the provider area area:provider/a2a Changes files in the provider / a2a area size:medium At most 100 changed lines across at most 5 files labels Sep 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Streaming task status messages can still report inbound user roles as assistant.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity · 1 Low severity

Open (2)
What changed in this PR

Preserves inbound A2A message roles when converting responses into framework messages.

Changes:

  • Added shared A2A-to-framework role mapping.
  • Applied role preservation to messages and task-status updates.
  • Added a user-role response test.
File Description
provider/​a2aprovider/​a2a.go Adds role mapping for inbound responses.
provider/​a2aprovider/​a2a_test.go Tests preservation of user-role messages.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

role := message.RoleAssistant
if e.Status.Message != nil {
messageID = e.Status.Message.ID
role = agentRole(e.Status.Message.Role)
Comment on lines +303 to +305
// A response message carrying the A2A user role must map to RoleUser, not be
// hardcoded to assistant, matching the Python client.
func TestRunPreservesInboundUserRole(t *testing.T) {
@github-actions github-actions Bot added kind:code Changes production behavior or code kind:tests Changes tests, fixtures, or test infrastructure labels Sep 20, 2026
@github-actions

This comment has been minimized.

@github-actions github-actions Bot added the parity-approved Go API consistency review found no parity issues label Sep 20, 2026
Add a role assertion for the input-required task-status message path
(yieldTask) so the user-role mapping cannot regress, and restore the
TestRunWithValidUserMessage doc comment to its own test.
@github-actions github-actions Bot added size:large At most 300 changed lines across at most 10 files and removed size:medium At most 100 changed lines across at most 5 files labels Sep 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Scope: user-visible behavior (bug fix), internal-only (helper is unexported)

Changed Go contract: No exported API added or changed. Observable behavior fix in provider/a2aprovider: the client's RunText/Run inbound path now preserves the peer-supplied A2A message role (user vs agent) instead of hardcoding message.RoleAssistant for *a2a.Message responses, TaskStatusUpdateEvent status messages, and yieldTask input-required status messages. New unexported agentRole(role a2a.MessageRole) message.Role helper centralizes the mapping; artifacts remain hardcoded to assistant (no role field on artifacts).

Upstream evidence reviewed:

  • Python: python/packages/a2a/agent_framework_a2a/_agent.py
    • Line 609: role="assistant" if msg.role == A2ARole.ROLE_AGENT else "user" (plain Message payload — matches Go's *a2a.Message case)
    • Line 877: role="assistant" if status.message.role == A2ARole.ROLE_AGENT else "user" (task-status message — matches Go's yieldTask / TaskStatusUpdateEvent cases)
    • Line 810/940/966: same conditional pattern applied consistently across other status/task-completion paths
    • Line 845/857/904: artifact-derived updates hardcode role="assistant" (no role on A2A artifacts) — matches Go's unchanged artifact handling
  • Go hosting side (already existing, cited in PR body): toAgentMessage maps agent → assistant, else user — same mapping direction as this client-side fix.

Result: aligned. This is a pure bug fix restoring parity with the existing Python client behavior (and the Go hosting-side mapping), with no exported API surface change. The agentRole helper's agent → assistant, else → user logic is an exact match for the Python "assistant" if role == ROLE_AGENT else "user" conditional used at every equivalent site (message, task-status-update, input-required status). Test coverage (TestRunPreservesInboundUserRole, TestRunWithInputRequiredTaskMessagePreservesUserRole) exercises both fixed paths. The unrelated go.mod/go.sum dependency bumps are routine and out of scope for parity review.

No inline findings. No exported API changed, so public-api-change is not applicable.

Generated by Go API Consistency Review Agent · copilot · auto · 28.3 AIC · ⌖ 5.17 AIC · ⊞ 9.2K · ◷

@github-actions github-actions Bot added the kind:dependencies Changes dependencies or manifests label Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider/a2a Changes files in the provider / a2a area area:provider Changes files in the provider area kind:code Changes production behavior or code kind:dependencies Changes dependencies or manifests kind:tests Changes tests, fixtures, or test infrastructure parity-approved Go API consistency review found no parity issues size:large At most 300 changed lines across at most 10 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants