Skip to content

.NET: ADR for unified dynamic agent resolution across AG-UI, Responses, and A2A#6643

Open
Ashutosh0x wants to merge 3 commits into
microsoft:mainfrom
Ashutosh0x:feature/unified-dynamic-agent-resolution
Open

.NET: ADR for unified dynamic agent resolution across AG-UI, Responses, and A2A#6643
Ashutosh0x wants to merge 3 commits into
microsoft:mainfrom
Ashutosh0x:feature/unified-dynamic-agent-resolution

Conversation

@Ashutosh0x

Copy link
Copy Markdown

Motivation and Context

This ADR addresses the cross-channel consistency requirement raised by @javiercn on #3162:

'Before we move forward with this change, we'd rather have a broader discussion to establish the pattern here, as this needs to be applied not only to AG-UI but to Open AI responses and A2A and we'd rather do everything simultaneously.'

All three hosting channel endpoint builders (MapAGUI, MapOpenAIResponses, MapA2AHttpJson) currently resolve the AIAgent instance once at endpoint registration time. This singleton-capture prevents dynamic per-request agent resolution needed for multi-tenant platforms.

@halllo further identified that AgentSessionStore is also singleton-captured, breaking scoped/transient DI registrations.

Description

Proposes an IAgentResolver interface in the hosting core plus factory-delegate overloads for all three channel types:

  1. IAgentResolver interface - shared DI-registered resolver for route-based/claims-based agent selection
  2. Factory delegate overloads - inline Func<HttpContext, CancellationToken, ValueTask<AIAgent?>> for quick prototyping
  3. Per-request session store resolution - fixes the singleton-capture bug by resolving from HttpContext.RequestServices

The ADR analyzes 3 options (HttpContextRoutingAgent workaround, factory delegates only, IAgentResolver + factory dual API) and recommends the dual API approach.

References

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass (no code changes, ADR only)
  • Is this a breaking change? No - design proposal only

…s, and A2A

Proposes IAgentResolver interface and factory-delegate overloads for
MapAGUI, MapOpenAIResponses, and MapA2AHttpJson to enable per-request
agent resolution. Addresses the cross-channel consistency requirement
raised by @javiercn on PR microsoft#3162 and fixes the AgentSessionStore
singleton-capture scoping issue identified by @halllo.

Refs: microsoft#3162, microsoft#2988, microsoft#2343
Copilot AI review requested due to automatic review settings June 20, 2026 09:22
@moonbox3 moonbox3 added the documentation Improvements or additions to documentation label Jun 20, 2026

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.

Pull request overview

This PR adds a new Architecture Decision Record (ADR) proposing a unified approach to resolve AIAgent instances dynamically per request across the three .NET hosting channels (AG-UI, OpenAI Responses, A2A), addressing current endpoint-registration-time singleton capture that blocks multi-tenant routing and breaks scoped session store usage.

Changes:

  • Introduces ADR-0029 documenting the problem (startup-time agent/session-store capture) and proposing a dual API: IAgentResolver (DI-based) plus factory delegate overloads.
  • Specifies per-request AgentSessionStore resolution via HttpContext.RequestServices to support scoped/transient registrations.
  • Documents expected error-handling behavior (404 on null resolution, 500 on exceptions, startup error if missing resolver).

Comment on lines +128 to +134
public static IServiceCollection AddAgentResolver<TResolver>(this IServiceCollection services)
where TResolver : class, IAgentResolver
{
services.AddHttpContextAccessor();
services.AddSingleton<IAgentResolver, TResolver>();
return services;
}
@Ashutosh0x

Copy link
Copy Markdown
Author

cc @javiercn — This ADR directly addresses the cross-channel consistency requirement you raised on #3162:

'Before we move forward with this change, we'd rather have a broader discussion to establish the pattern here, as this needs to be applied not only to AG-UI but to Open AI responses and A2A and we'd rather do everything simultaneously.'

I analyzed all three endpoint builders (MapAGUI, MapOpenAIResponses, MapA2AHttpJson) and found the same singleton-capture pattern in each. The ADR proposes a dual API approach — IAgentResolver interface for DI-registered resolvers + factory delegate overloads for inline resolution — that works identically across all three channel types.

Would love your architectural feedback, particularly on:

  1. Whether the IAgentResolver abstraction captures the right granularity
  2. Whether per-request AgentSessionStore resolution from HttpContext.RequestServices is the right fix for the scoping bug @halllo identified
  3. Whether this should live in Microsoft.Agents.AI.Hosting or a more specific package

Happy to iterate based on your guidance!

@javiercn

Copy link
Copy Markdown
Contributor

I don't feel strongly about what we do in this regard, provided we are consistent and that other folks like @DeagleGross and @ReubenBond @rogerbarreto or @westey-m are happy with the approach.

They might have different opinions on alternative ways in which this can be done/achieved.

That said, the IHttpContextAccessor pattern is not the end of the world, especially if the framework provided it. You can just wrap the agent, or the frameworks could simply put the HttpContext instance on the additional properties of the options, and you get to run your resolution logic within a delegating handler which you can already create with Use(...)

@Ashutosh0x

Copy link
Copy Markdown
Author

Thanks @javiercn — really appreciate the feedback and the direction!

You're right that the IHttpContextAccessor pattern is pragmatic and already familiar to ASP.NET Core developers. Let me think through both approaches:

Option A: IHttpContextAccessor + Use(...) (your suggestion)

csharp app.MapAGUI(/agents/{agentId}, agent) .Use(async (context, next) => { var httpContext = context.HttpContext; var agentId = httpContext.GetRouteValue(agentId); // resolve agent from DI or factory var agent = resolver.Resolve(agentId); // set on additional properties httpContext.Items[ResolvedAgent] = agent; await next(context); });

Pros: No new abstractions needed, uses existing middleware pipeline, developers already know this pattern.

Cons: The initial agent parameter is still required at registration time (even if ignored at runtime), and the resolution logic is duplicated across each endpoint unless wrapped in a shared middleware.

Option B: IAgentResolver (ADR proposal)

Pros: Single abstraction, enforced consistency, testable via DI.

Cons: New interface to learn, more opinionated.

My take

Given your preference for consistency without being overly prescriptive, I think the best path forward is:

  1. Add HttpContext to the options/additional properties (as you suggested) — this is a minimal, non-breaking change that unblocks dynamic resolution for all three channels
  2. Document the Use(...) pattern as the recommended approach for per-request agent selection
  3. Defer IAgentResolver as a potential future convenience layer if the community gravitates toward it

I'll update the ADR to incorporate this direction. Happy to defer to @DeagleGross, @ReubenBond, @rogerbarreto, and @westey-m on which approach feels most natural for the .NET hosting surface.

Also addressing Copilot's review — will fix the AddSingletonAddScoped registration for resolvers that depend on scoped services (DbContext, etc.).

Addresses Copilot review - singleton resolver cannot take scoped
dependencies (DbContext, repositories). Scoped registration enables
per-request resolution which is the core intent of this ADR.

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

Copy link
Copy Markdown
Author

Addressed Copilot's review — pushed ee49e2b5: changed AddSingleton<IAgentResolver>AddScoped<IAgentResolver>.

This ensures resolvers that depend on scoped services (DbContext, per-request repositories) work correctly. Per-request resolution is the core intent of this ADR, so scoped lifetime is the natural fit.

Thanks for the catch @copilot!

- Add Option 4: Use(...) middleware + HttpContext on additional
  properties (Javier's recommended approach)
- Restructure Decision Outcome as phased approach:
  Phase 1: Expose HttpContext + document Use(...) pattern (minimal)
  Phase 2: Factory delegates + IAgentResolver (if demand warrants)
- Add Maintainer Feedback section with Javier's direct quote
- Expand consulted list: @DeagleGross, @ReubenBond, @rogerbarreto,
  @westey-m (as requested by @javiercn)
- Add reference to DeagleGross's A2A hosting work (microsoft#3732)

This revision prioritizes the approach Javier suggested: minimal
framework changes using established ASP.NET Core patterns rather
than introducing new abstractions.
@Ashutosh0x

Copy link
Copy Markdown
Author

ADR Revised — Incorporating @javiercn's Feedback

Updated the ADR (commit a04a40fe) based on Javier's architectural guidance:

What Changed

  1. Added Option 4Use(...) middleware + HttpContext on additional properties (Javier's suggestion). Full code examples showing both inline middleware and framework-level AgentRequestOptions approaches.

  2. Restructured Decision Outcome as a phased approach:

    • Phase 1 (minimal, non-breaking): Expose HttpContext on options, document Use(...) pattern, fix per-request AgentSessionStore resolution
    • Phase 2 (if community demand): Factory delegate overloads + IAgentResolver convenience layer
  3. Added Maintainer Feedback section with @javiercn's direct quote and key takeaways

  4. Expanded consulted list per Javier's request: cc @DeagleGross @ReubenBond @rogerbarreto @westey-m

The revision prioritizes minimal framework changes using established ASP.NET Core patterns (middleware pipeline, IHttpContextAccessor) rather than introducing new abstractions.

Would love input from the team on whether Phase 1 captures the right scope. Happy to iterate further!

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

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants