Skip to content

.NET: Honor hosted agent lifetimes and add request-time agent selection for AG-UI hosting - #9382

Merged
SergeyMenshykh merged 8 commits into
microsoft:mainfrom
marcominerva:dotnet-hosting-request-scoped-agents
Oct 20, 2026
Merged

SergeyMenshykh merged 8 commits into
microsoft:mainfrom
marcominerva:dotnet-hosting-request-scoped-agents

Conversation

@marcominerva

@marcominerva Marco Minerva (marcominerva) commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Motivation & Context

MapAGUIServer resolves the hosted agent and its session store from the root service provider when the endpoint is mapped, so Scoped and Transient AddAIAgent registrations are captured as singletons. There is also no way to choose the agent at request time (for example from a route value, header or claim) without mapping one endpoint per agent.

This PR is the AG-UI part of the original #9382, which was split per hosting protocol to make review easier. The A2A, OpenAI Chat Completions and OpenAI Responses parts are in separate PRs and can be merged independently.

Description & Review Guide

  • What are the major changes?
    • MapAGUIServer(agentName) / MapAGUIServer(IHostedAgentBuilder) now resolve the keyed AIAgent, its keyed AgentSessionStore and AgentIsolationKeyProvider from HttpContext.RequestServices for each request, so the configured DI lifetime is honored. Singleton registrations mapped through IHostedAgentBuilder are still resolved once at mapping time. Missing registrations are still reported at mapping time, using IServiceProviderIsKeyedService so that no scoped/transient instance is resolved from the root provider.
    • New MapAGUIServer(string pattern, Func<HttpContext, AIAgent?> agentSelector) overload. A null selection returns 404 before any session state is loaded; the selected agent's Name is used as the stable session storage identity, so sessions persist across new agent instances.
    • The MapAGUIServer(pattern, AIAgent) overload keeps its agent instance but now resolves the session store per request.
    • New shared source helper src/Shared/HostedAgents/HostedAgentResolution.cs (opt-in via InjectSharedHostedAgents), reused by the other hosting PRs of this split.
  • What is the impact of these changes? Scoped/Transient agents hosted via AG-UI get a fresh instance per request instead of being captured. Additive public API only; no breaking change.

Related Issue

Part of #7733 (split from #9382).

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • This is not a breaking change.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Detached OpenAI and A2A execution can outlive request-scoped dependencies, and Responses retrieval bypasses agent selection.

3 open findings
What changed in this PR

Updates .NET protocol hosting to honor agent DI lifetimes and support request-time agent selection.

Changes:

  • Resolves scoped/transient agents and session services per request or operation.
  • Adds selector overloads for AG-UI, OpenAI, and A2A endpoints.
  • Adds stable session identities and extensive lifetime/selection tests.
File Description
dotnet/​src/​Shared/​HostedAgents/​HostedAgentResolution.cs Adds shared registration and identity validation.
dotnet/​eng/​MSBuild/​Shared.props Enables shared hosted-agent source injection.
dotnet/​src/​Microsoft.Agents.AI.Hosting.OpenAI/​Responses/​ResponsesHttpHandler.cs Supports request-selected executors.
dotnet/​src/​Microsoft.Agents.AI.Hosting.OpenAI/​Responses/​IResponsesService.cs Adds executor-specific operations.
dotnet/​src/​Microsoft.Agents.AI.Hosting.OpenAI/​Responses/​InMemoryResponsesService.cs Stores and executes selected executors.
dotnet/​src/​Microsoft.Agents.AI.Hosting.OpenAI/​Responses/​AIAgentResponseExecutor.cs Adds stable session identities.
dotnet/​src/​Microsoft.Agents.AI.Hosting.OpenAI/​EndpointRouteBuilderExtensions.Responses.cs Adds Responses selector mapping.
dotnet/​src/​Microsoft.Agents.AI.Hosting.OpenAI/​EndpointRouteBuilderExtensions.ChatCompletions.cs Adds per-request Chat Completions selection.
dotnet/​src/​Microsoft.Agents.AI.Hosting.OpenAI/​Microsoft.Agents.AI.Hosting.OpenAI.csproj Injects shared resolution code.
dotnet/​src/​Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/​AGUIEndpointRouteBuilderExtensions.cs Resolves AG-UI agents and stores per request.
dotnet/​src/​Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/​Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj Injects shared resolution code.
dotnet/​src/​Microsoft.Agents.AI.Hosting.A2A/​ScopedA2AAgentHandler.cs Adds operation-owned A2A scopes.
dotnet/​src/​Microsoft.Agents.AI.Hosting.A2A/​A2AServerServiceCollectionExtensions.cs Defers A2A service resolution.
dotnet/​src/​Microsoft.Agents.AI.Hosting.A2A/​A2AAgentHandler.cs Extracts task cancellation logic.
dotnet/​src/​Microsoft.Agents.AI.Hosting.A2A/​Microsoft.Agents.AI.Hosting.A2A.csproj Adds shared code and internals visibility.
dotnet/​src/​Microsoft.Agents.AI.Hosting.A2A.AspNetCore/​SelectedAgentA2ARequestHandler.cs Dispatches A2A requests by selected agent.
dotnet/​src/​Microsoft.Agents.AI.Hosting.A2A.AspNetCore/​A2AEndpointRouteBuilderExtensions.cs Adds A2A selector overloads.
dotnet/​src/​Microsoft.Agents.AI.Hosting.A2A.AspNetCore/​Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj Avoids duplicate shared types.
dotnet/​tests/​Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/​ResponseSessionRegressionTests.cs Tests selector persistence and scoped resolution.
dotnet/​tests/​Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/​OpenAIChatCompletionsAgentResolutionTests.cs Tests Chat Completions lifetimes and selection.
dotnet/​tests/​Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/​Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests.csproj Adds TestHost dependency.
dotnet/​tests/​Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/​AGUIRequestScopedAgentTests.cs Tests AG-UI scoped behavior.
dotnet/​tests/​Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/​AGUIEndpointRouteBuilderExtensionsTests.cs Updates AG-UI mapping tests.
dotnet/​tests/​Microsoft.Agents.AI.Hosting.A2A.UnitTests/​A2AServerServiceCollectionExtensionsTests.cs Updates deferred store-resolution expectations.
dotnet/​tests/​Microsoft.Agents.AI.Hosting.A2A.UnitTests/​A2ARequestScopedAgentTests.cs Tests A2A lifetimes, selection, and persistence.

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

The selected agent and its session store are bound to HttpContext.RequestServices,
so a run returned immediately as a task would outlive the request scope. Reject
non-streaming requests with ReturnImmediately when AgentRunMode returns a task.
The agent selector now runs for retrieval, cancellation, deletion, and input
item listing as well as creation, returning 404 before any state is loaded
when no agent matches. Stored responses are bound to the selected agent name,
so a response is only visible through requests that select the agent that
created it.
Stored response states outlive execution under a sliding expiration, so clear
the executor, and the selected agent graph it references, once execution ends,
including on failure or cancellation.
@SergeyMenshykh

SergeyMenshykh commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Marco Minerva (@marcominerva), thanks very much for the contribution and for working on this! We’re receiving a large number of contributions at the moment and are finding it challenging to keep up with all the reviews.

Since this PR covers several hosting scenarios and changes a significant amount of code, would you mind splitting it into smaller, focused PRs - a separate PR for each concrete scenario you currently need to support or that is blocking your work? Keeping PRs smaller and focused helps us review and merge contributions much faster.

For future PRs, please also take a look at our updated contribution model.

Thanks again for your work and understanding!

@marcominerva

Copy link
Copy Markdown
Contributor Author

Thank you SergeyMenshykh for your reply.

Some changes are shared across the different scenarios, but over the next few days I'll try to figure out how to split this PR. I'll keep you informed about the progress.

…t-scoped-agents

Adapt SelectedAgentA2ARequestHandler and the agent-selector MapA2AHttpJson overload to the A2A 1.0.0-preview3 API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the A2A, OpenAI Chat Completions and OpenAI Responses changes to separate branches so this PR only covers MapAGUIServer and the shared hosted agent resolution helper.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@marcominerva Marco Minerva (marcominerva) changed the title .NET: Honor hosted agent lifetimes and add request-time agent selection .NET: Honor hosted agent lifetimes and add request-time agent selection for AG-UI hosting Oct 19, 2026
@marcominerva

Copy link
Copy Markdown
Contributor Author

Hi SergeyMenshykh, I've split this PR as suggested. Now there is one PR per hosting scenario:

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

Static review identified no blocking issues in lifetime handling, selection ordering, or session persistence; tests were not executed.

No open issues 🎉

3 resolved since last review

Review effort: Balanced

The explicit-instance MapAGUIServer overload partitioned persisted sessions by the agent instance, while the named, hosted, and selector mappings partition them by the agent name, so the same agent exposed through a named and an explicit-instance mapping no longer shared conversations. Named agents mapped by instance are now identified by their name as well; unnamed agents keep their instance identity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@SergeyMenshykh

Copy link
Copy Markdown
Collaborator

Hi SergeyMenshykh, I've split this PR as suggested. Now there is one PR per hosting scenario:

Marco Minerva (@marcominerva), thanks for splitting this up! This PR LGTM. Of the remaining PRs, which one covers the scenario you need most or that is currently blocking you?

@marcominerva

Copy link
Copy Markdown
Contributor Author

Great news, SergeyMenshykh!

Regarding the other PRs, I have several customers who are currently blocked on the A2A scenario, so getting this support would definitely be very helpful.

Also, while I have your attention, I'd like to ask if it would be possible to prioritize #8801 as well. It has already been approved, and getting it merged too would be another great help to several of my customers. Some of them have previously implemented custom stores based on the expectation that DeleteSessionAsync would be available, so this change would help them move forward to the latest version of the Agent Framework.

Thanks again for your help and for considering these requests!

@SergeyMenshykh
SergeyMenshykh added this pull request to the merge queue Oct 20, 2026
Merged via the queue into microsoft:main with commit 30bc5c1 Oct 20, 2026
32 checks passed

This branch was successfully deployed

1 active deployment
github-app-auth — fb90fb6f Deployed Oct 20, 2026 by marcominerva via add_label #25798
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET Usage: [Issues, PRs], Target: .Net

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants