Skip to content

fix(a2a): include assistant turns in conversation history - #508

Merged
justintime4tea merged 1 commit into
mainfrom
justingross/GH-368-a2a-history-omits-assistant-turns
Aug 11, 2026
Merged

fix(a2a): include assistant turns in conversation history#508
justintime4tea merged 1 commit into
mainfrom
justingross/GH-368-a2a-history-omits-assistant-turns

Conversation

@justintime4tea

Copy link
Copy Markdown
Collaborator

A follow-up message on an existing contextId was given the prior user turns but never the agent's own replies, so it answered as if it had never spoken.

get_history_for_context rebuilt history from Task::history alone. The upstream request handler writes that field once, with the prompt, and never appends to it; the agent's reply is streamed out as artifacts instead, leaving the Role::Agent arm of convert_a2a_msg_to_aura unreachable. The same call also passed include_artifacts: Some(false), so the reply was never fetched, and sorted the context's tasks newest-first, which would have replayed a multi-task exchange backwards.

History now rebuilds the assistant turn from the artifacts the client saw: the streamed "response" chunks concatenated with no separator, falling back to "final" for a reply that only arrived as a final response. Tool-call, reasoning, and scratchpad artifacts are skipped. Tasks replay oldest-first, and a task already carrying a ROLE_AGENT message keeps it rather than gaining a second one. The two artifact ids are now module consts shared with the sites that emit them, so reader and writer cannot drift apart.

A2A clients still see only the prompt in task.history. The upstream handler owns every task write and carries a stale in-memory task across events, so writing the agent turn back would either race it or have to go through SharedTaskStore, which conflicts with the Redis store's terminal-immutability rule. Reconstructing on read works with both stores and touches no write path.

test_sequential_tasks_share_the_assistant_turn covers the half that test_sequential_tasks_share_context cannot. It routes a per-run secret in through a forwarded header that the agent reports, then sends the follow-up without that header so the tool cannot surface the value again, and asserts the follow-up made no tool call at all, leaving the prior assistant turn as the only possible source.

Fixes: #368

@justintime4tea
justintime4tea requested a review from a team August 5, 2026 19:48
@greptile-apps

greptile-apps Bot commented Aug 5, 2026

Copy link
Copy Markdown

Greptile Summary

This PR reconstructs prior assistant turns from persisted A2A response artifacts so follow-up tasks receive complete conversation history.

  • Shares artifact identifiers between the stream writer and history reader.
  • Fetches artifacts and replays prior tasks oldest-first.
  • Avoids duplicating assistant turns already recorded in task history.
  • Adds unit and integration coverage for streamed chunks, final-only replies, ordering, and sequential context reuse.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete blocking or independently actionable non-blocking defects identified.

The changed history path consistently reads the same artifact identifiers emitted by the executor, prefers streamed response text with a final-only fallback, prevents duplicate recorded assistant turns, and is covered across reconstruction and sequential-context scenarios.

Important Files Changed

Filename Overview
crates/aura-web-server/src/a2a/agent_executor.rs Reconstructs assistant history from response/final artifacts, requests artifacts during task listing, and reorders prior exchanges oldest-first with focused unit coverage.
crates/aura-web-server/tests/a2a_test.rs Adds an end-to-end sequential-task test proving a follow-up can recall a value present only in the prior assistant reply, plus reusable A2A test helpers.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Handler as A2A Handler
    participant Store as Task Store
    participant Executor
    participant Agent
    Client->>Handler: Send first user message
    Handler->>Store: Persist task prompt
    Handler->>Executor: Execute task
    Executor->>Agent: Stream prompt with prior history
    Agent-->>Executor: Response chunks and final response
    Executor-->>Handler: Response/final artifacts
    Handler->>Store: Persist artifacts
    Client->>Handler: Send follow-up with contextId
    Handler->>Executor: Execute follow-up
    Executor->>Store: List context tasks with artifacts
    Executor->>Executor: Rebuild user and assistant turns
    Executor->>Agent: Stream follow-up with complete history
Loading

Reviews (1): Last reviewed commit: "fix(a2a): include assistant turns in con..." | Re-trigger Greptile

Shearerbeard
Shearerbeard previously approved these changes Aug 6, 2026

@Shearerbeard Shearerbeard left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving for now but i think after we flesh out park/reify #271 and get closer to task attach/detach this will have the same generic home + backend for all agent initializations - not just specific A2A

A follow-up message on an existing contextId was given the prior
user turns but never the agent's own replies, so it answered as if
it had never spoken.

get_history_for_context rebuilt history from Task::history alone.
The upstream request handler writes that field once, with the
prompt, and never appends to it; the agent's reply is streamed out
as artifacts instead, leaving the Role::Agent arm of
convert_a2a_msg_to_aura unreachable. The same call also passed
include_artifacts: Some(false), so the reply was never fetched, and
sorted the context's tasks newest-first, which would have replayed
a multi-task exchange backwards.

History now rebuilds the assistant turn from the artifacts the
client saw: the streamed "response" chunks concatenated with no
separator, falling back to "final" for a reply that only arrived as
a final response. Tool-call, reasoning, and scratchpad artifacts
are skipped. Tasks replay oldest-first, and a task already carrying
a ROLE_AGENT message keeps it rather than gaining a second one. The
two artifact ids are now module consts shared with the sites that
emit them, so reader and writer cannot drift apart.

A2A clients still see only the prompt in task.history. The upstream
handler owns every task write and carries a stale in-memory task
across events, so writing the agent turn back would either race it
or have to go through SharedTaskStore, which conflicts with the
Redis store's terminal-immutability rule. Reconstructing on read
works with both stores and touches no write path.

test_sequential_tasks_share_the_assistant_turn covers the half that
test_sequential_tasks_share_context cannot. It routes a per-run
secret in through a forwarded header that the agent reports, then
sends the follow-up without that header so the tool cannot surface
the value again, and asserts the follow-up made no tool call at
all, leaving the prior assistant turn as the only possible source.

Fixes: #368
@justintime4tea
justintime4tea force-pushed the justingross/GH-368-a2a-history-omits-assistant-turns branch from e7c3c93 to c579bfb Compare August 11, 2026 13:41

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@justintime4tea
justintime4tea merged commit 72f1e8c into main Aug 11, 2026
9 checks passed
@justintime4tea
justintime4tea deleted the justingross/GH-368-a2a-history-omits-assistant-turns branch August 11, 2026 14:01
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: A2A conversation history omits assistant turns — follow-up messages don't see prior answers

2 participants