microsoft / microsoft/agent-framework
.NET: [Bug]: AGUIChatClient client-tool loop duplicates persisted messages with MapAGUIServer + AgentSessionStore
@javiercn is already working on this.
Since Aug 26, 2026.
- Dominant language
- Python
- Stars
- 13.6k
- Forks
- 2.3k
- Avg merge
- 2d 45m
- Merged PRs (30d)
- 358
Description
Description
When an AG-UI server owns durable conversation history through an AgentSessionStore, a client-side tool invocation causes the request user message and function call to be persisted twice.
My repo contains a focused live integration test.
The server owns cross-turn conversation history:
builder.AddAIAgent(agentName, (_, _) => agent)
.WithSessionStore((sp, agentName) =>
sp.GetKeyedService<CosmosAgentSessionStore>(agentName)!);
agentHost.MapAGUIServer(agentName, "/ag-ui");
The client sends only the current user message for each user-initiated turn. It creates a fresh local AgentSession for that turn only, retaining it only long enough for the internal client-tool loop to complete.
var session = await agent.CreateSessionAsync(cancellationToken);
await foreach (var update in agent.RunStreamingAsync(
[new ChatMessage(ChatRole.User, "Where am I?")],
session,
cancellationToken: cancellationToken))
{
// consume stream
}
The client registers a tool such as:
AIFunctionFactory.Create(
() => "51.3967°N, -1.3172°E",
name: "get_user_location")
Actual behavior
After one client-tool turn completes, the persisted server history is:
user: Where am I?
assistant: functionCall get_user_location (callId: X)
user: Where am I? <-- duplicate
assistant: functionCall get_user_location (X) <-- duplicate
tool: functionResult (X)
assistant: final text response
The duplicate user message retains the same messageId, and the duplicate function call retains the same callId. The function result is persisted only once.
The issue is visible when a conversation is reloaded from durable history: each user request appears twice, while each tool result appears once.
Expected behavior
A single client-tool turn should persist:
user: Where am I?
assistant: functionCall get_user_location (callId: X)
tool: functionResult (X)
assistant: final text response
Investigation
The focused regression test reads the raw serialized hosted session directly and fails because the user message occurs twice.
The apparent cause is that AGUIChatClient’s automatic client-tool continuation reuses its accumulated temporary local history when submitting the follow-up request. That request contains the original user message and function call alongside the tool result. The hosted AG-UI server then appends those resent messages to the durable AgentSessionStore history.
This does not appear to be caused by application-managed cross-turn continuation:
• The application only provides ThreadId and ParentRunId on later user-initiated turns.
• The duplicate occurs within one user turn and its automatic client-tool follow-up.
• No explicit messages override is supplied through RawRepresentationFactory.
• Disabling the Foundry Memory provider does not affect reproduction.
Question
What is the supported AGUIChatClient client-tool continuation pattern when MapAGUIServer owns durable transcript state via AgentSessionStore?
Specifically, should the client-tool resume request be treated as a delta by the server, or is there a supported client API for sending only the tool result rather than accumulated local messages?
Code Sample
Repo here: https://github.com/ecoDriverltd/FoundryAgentsExperiment/tree/session-store-only
Test: 'AGUIClientToolContinuationPersistsEachProtocolMessageOnce' in:
https://github.com/ecoDriverltd/FoundryAgentsExperiment/blob/session-store-only/FoundryAgentsExperiment.IntegrationTests/AgentChatTests.cs
Error Messages / Stack Traces
Package Versions
Microsoft.Agents.AI.Hosting.AGUI.AspNetCore 1.18.0-preview.260818.1, AGUI.Client 1.18.0-preview.260818.1
.NET Version
.NET 10
Additional Context
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.