Skip to content

.NET: Thread persistence does not work by default in MapAGUI #2517

Closed

Description

Summary

MapAGUI in Microsoft.Agents.AI.Hosting.AGUI.AspNetCore passes thread: null to AIAgent.RunStreamingAsync(), which means the agent's ChatMessageStoreFactory configuration is not properly utilized for thread persistence across requests.

Current Behavior

Looking at AGUIEndpointRouteBuilderExtensions.cs:

var events = aiAgent.RunStreamingAsync(
    messages,
    options: runOptions,    // <-- No thread parameter passed (defaults to null)
    cancellationToken: cancellationToken)

When thread is null, ChatClientAgent calls GetNewThread() which creates a new ChatClientAgentThread with a new ChatMessageStore instance for every single request.

The AG-UI thread ID is passed via ChatOptions.AdditionalProperties[\"ag_ui_thread_id\"], but since no AgentThread is passed, each request:

  1. Creates a brand new thread
  2. Creates a brand new ChatMessageStore via ChatMessageStoreFactory
  3. The ChatMessageStore has no way to correlate with the AG-UI thread ID until AddMessagesAsync is called (after the factory)

Expected Behavior

MapAGUI should support server-side thread persistence so that:

  1. If an agent has a ChatMessageStoreFactory configured, it should be used to restore/persist conversation history per AG-UI thread ID
  2. The ChatMessageStore should be created with knowledge of the AG-UI thread ID upfront
  3. On subsequent requests with the same ag_ui_thread_id, the same logical thread/store should be used

Workaround

Currently, developers must use ambient context (AsyncLocal) to pass the AG-UI thread ID from middleware to the ChatMessageStore, and manually manage thread storage outside of the Agent Framework's normal patterns:

// In middleware before agent runs:
CurrentThreadContext.ThreadId = ExtractThreadIdFromOptions(options);

// In ChatMessageStoreFactory:
ChatMessageStoreFactory = ctx => new CustomChatMessageStore(
    threadStore,
    CurrentThreadContext.ThreadId, // Read from ambient context
    ctx.SerializedState
)

This is fragile and requires duplicating message storage logic outside of the normal Agent Framework patterns.

Related Issues

Environment

  • Package: Microsoft.Agents.AI.Hosting.AGUI.AspNetCore

Activity

  1. added
    .NETUsage: [Issues, PRs], Target: .Net
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Nov 28, 2025
  2. changed the title [-]Thread persistence does not work by default in MapAGUI[/-] [+].NET: Thread persistence does not work by default in MapAGUI[/+] on Nov 28, 2025
  3. changed the title [-].NET: Thread persistence does not work by default in MapAGUI[/-] [+]Python: Thread persistence does not work by default in MapAGUI[/+] on Nov 28, 2025
  4. javiercn commented on Nov 28, 2025

    @javiercn
    Contributor

    David Parks (@DavidParks8) thanks for contacting us.

    This is a current limitation. We will be working on more comprehensive support for this in the future.

  5. DavidParks8 commented on Nov 28, 2025

    @DavidParks8
    MemberAuthor

    Javier Calvarro Nelson (@javiercn) How soon would that be? I'm trying to decide if I should rely on MapAGUI for a live product or if I should just roll my own.

  6. vecode commented on Dec 14, 2025

    @vecode

    David Parks (@DavidParks8) Could you provide more details on how/where you are extracting the thread ID?

  7. arjendeblok commented on Dec 14, 2025

    @arjendeblok

    I also see that in AGUIChatClient the property agui_thread_id is used and in AGUIEndpointRouteBuilderExtensions the property ag_ui_thread_id is used with an additional underscore. I also would like to store my thread in the agent server side.

  8. changed the title [-]Python: Thread persistence does not work by default in MapAGUI[/-] [+].NET: Thread persistence does not work by default in MapAGUI[/+] on Feb 3, 2026
  9. tigertop commented on Mar 15, 2026

    @tigertop

    Any news on this one? Any known workarounds possible at the moment?

  10. javiercn commented on Aug 18, 2026

    @javiercn
    Contributor

    This is resolved by the current MapAGUIServer hosting implementation.

    AG-UI threadId is now used to load or create an AgentSession, which is passed to the agent and saved after streaming:

    builder.Services.AddAGUIServer();
    
    builder.AddAIAgent("assistant", (_, _) => agent)
        .WithInMemorySessionStore();
    
    var app = builder.Build();
    app.MapAGUIServer("assistant", "/ag-ui");

    Requests using the same AG-UI thread resume the stored session; a replacement thread starts a new session.

    Persistence must be configured explicitly. Without an AgentSessionStore, sessions are intentionally ephemeral. WithInMemorySessionStore is appropriate for single-process development; distributed applications should register a durable store.

    Multi-user hosts must additionally scope session lookup by an authenticated user or tenant rather than treating threadId as authorization.

    The former AgentThread/ChatMessageStoreFactory model has been replaced by AgentSession/AgentSessionStore. Since hosted AG-UI sessions now persist through the supported mechanism, this issue can be closed as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

.NETUsage: [Issues, PRs], Target: .Netag-uiUsage: [Issues, PRs], Target: AG-UI protocol integrationhostingUsage: [Issues, PRs], Target: all hosting related solutions

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions