Ref: #325, #328
Summary
A follow-up A2A message on an existing contextId is given the prior user turns but never the agent's own responses. Every follow-up therefore behaves as if it has never spoken.
Observed against aura-web-server with A2A enabled:
task 1 (user): "In exactly three sentences, explain what a log pipeline is."
-> completes, answers correctly
task 2 (user, same contextId): "How many sentences did you just use?"
-> "I haven't responded to that prompt yet, so I used zero sentences."
Expected: the follow-up sees the previous assistant answer and replies "three".
Root cause
get_history_for_context (crates/aura-web-server/src/a2a/agent_executor.rs:549) rebuilds history by listing tasks for the context and flat-mapping each task's history field:
let chat_history: Vec<aura::Message> = task_history
.into_iter()
.flat_map(|t| t.history.unwrap())
.filter_map(|m| convert_a2a_msg_to_aura(&m))
.collect();
The context lookup itself works — list(context_id) returns every task in the conversation. The problem is what lives in task.history: the agent's response is written to artifacts, not to history. A completed task looks like this:
status: TASK_STATE_COMPLETED
history messages: 1
role=ROLE_USER text="In exactly three sentences, explain what a log pipeline is."
artifacts: [("response", 84 parts), ("final", 1 part)]
ROLE_AGENT present in history? False
So flat_map(|t| t.history) can only ever yield user turns. convert_a2a_msg_to_aura has a Role::Agent => Message::assistant(...) arm that is unreachable in practice.
The same call also passes include_artifacts: Some(false), so the assistant text is not even fetched from the store.
Reproduction
Pre-requisites
aura-web-server running with --enable-a2a and any single-agent config (no HITL gating).
Steps
- Send a first message and note
task.contextId:
curl -s -X POST http://localhost:8080/a2a/v1/message:send \
-H "Content-Type: application/json" -H "x-aura-model: <agent-alias>" \
-d '{"message":{"messageId":"m1","role":"ROLE_USER","parts":[{"text":"In exactly three sentences, explain what a log pipeline is."}]}}'
-
Poll GET /a2a/v1/tasks/{id} until TASK_STATE_COMPLETED. Note that history holds only the ROLE_USER message while the answer sits in the response artifact.
-
Send a follow-up on the same contextId:
curl -s -X POST http://localhost:8080/a2a/v1/message:send \
-H "Content-Type: application/json" -H "x-aura-model: <agent-alias>" \
-d '{"message":{"messageId":"m2","contextId":"<CTX>","role":"ROLE_USER","parts":[{"text":"How many sentences did you just use?"}]}}'
- Poll it. The answer denies having responded.
Why the existing test does not catch this
test_sequential_tasks_share_context (crates/aura-web-server/tests/a2a_test.rs:713) passes, because the only thing it asserts is that task 2 can recall what was asked in task 1:
task 1 (user): "Call mock_tool with message='context-test-{uuid}'. Do not say anything else."
task 2 (user): "Tell me what was asked in the first call for this context."
assert: response contains "context-test-{uuid}"
That UUID originates in task 1's user message — the half of history that does carry. The test validates the user-turn path and is blind to the missing assistant turns.
The test's own comment states the expectation that is currently false:
// Wait for task 1 to complete so its full history (tool call, tool result,
// assistant response) is persisted in the task store before task 2 starts.
Tool calls, tool results, and assistant responses are not in task.history.
Suggested fix
Reconstruct the assistant turn from the task's response artifact (requiring include_artifacts: Some(true) in the ListTasksRequest), or write the agent's final message into task.history as ROLE_AGENT when the task completes.
Whichever way, extend test_sequential_tasks_share_context (or add a sibling) to assert on something only the assistant turn could supply — the current phrasing passes without it.
Scope note
Pre-existing, not a regression. git diff main..feature/GH-327-session-storage-trait-and-in-memory touches only shared_task_store.rs under crates/aura-web-server/src/a2a/; get_history_for_context dates to 349014dc feat(a2a): initial implementation.
Relevant to the session-storage roadmap: #328 (Redis/Valkey A2A task store) is motivated partly by making "conversation history by context_id survive load-balancing". Making that history durable and cross-pod will not help while the assistant half of every exchange never reaches the store. Worth fixing before or alongside #328.
Ref: #325, #328
Summary
A follow-up A2A message on an existing
contextIdis given the prior user turns but never the agent's own responses. Every follow-up therefore behaves as if it has never spoken.Observed against
aura-web-serverwith A2A enabled:Expected: the follow-up sees the previous assistant answer and replies "three".
Root cause
get_history_for_context(crates/aura-web-server/src/a2a/agent_executor.rs:549) rebuilds history by listing tasks for the context and flat-mapping each task'shistoryfield:The context lookup itself works —
list(context_id)returns every task in the conversation. The problem is what lives intask.history: the agent's response is written to artifacts, not tohistory. A completed task looks like this:So
flat_map(|t| t.history)can only ever yield user turns.convert_a2a_msg_to_aurahas aRole::Agent => Message::assistant(...)arm that is unreachable in practice.The same call also passes
include_artifacts: Some(false), so the assistant text is not even fetched from the store.Reproduction
Pre-requisites
aura-web-serverrunning with--enable-a2aand any single-agent config (no HITL gating).Steps
task.contextId:Poll
GET /a2a/v1/tasks/{id}untilTASK_STATE_COMPLETED. Note thathistoryholds only theROLE_USERmessage while the answer sits in theresponseartifact.Send a follow-up on the same
contextId:Why the existing test does not catch this
test_sequential_tasks_share_context(crates/aura-web-server/tests/a2a_test.rs:713) passes, because the only thing it asserts is that task 2 can recall what was asked in task 1:That UUID originates in task 1's user message — the half of history that does carry. The test validates the user-turn path and is blind to the missing assistant turns.
The test's own comment states the expectation that is currently false:
Tool calls, tool results, and assistant responses are not in
task.history.Suggested fix
Reconstruct the assistant turn from the task's
responseartifact (requiringinclude_artifacts: Some(true)in theListTasksRequest), or write the agent's final message intotask.historyasROLE_AGENTwhen the task completes.Whichever way, extend
test_sequential_tasks_share_context(or add a sibling) to assert on something only the assistant turn could supply — the current phrasing passes without it.Scope note
Pre-existing, not a regression.
git diff main..feature/GH-327-session-storage-trait-and-in-memorytouches onlyshared_task_store.rsundercrates/aura-web-server/src/a2a/;get_history_for_contextdates to349014dc feat(a2a): initial implementation.Relevant to the session-storage roadmap: #328 (Redis/Valkey A2A task store) is motivated partly by making "conversation history by
context_idsurvive load-balancing". Making that history durable and cross-pod will not help while the assistant half of every exchange never reaches the store. Worth fixing before or alongside #328.