microsoft / microsoft/agent-framework

.NET: [Bug]: AGUIChatClient client-tool loop duplicates persisted messages with MapAGUIServer + AgentSessionStore

Open
#7,811 3 comments 0 reactions 2 assignees View on GitHub

@javiercn is already working on this.

Since Aug 26, 2026.

.NET ag-ui hosting reproduced
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.