Skip to content

Fix MEAI001 leak from experimental RequiresConfirmation in source-generated AIContent contexts - #7659

Merged
jeffhandley merged 1 commit into
dotnet:mainfrom
jeffhandley:jeffhandley/fix-ai-json-experimental-leak
Jul 27, 2026
Merged

Fix MEAI001 leak from experimental RequiresConfirmation in source-generated AIContent contexts#7659
jeffhandley merged 1 commit into
dotnet:mainfrom
jeffhandley:jeffhandley/fix-ai-json-experimental-leak

Conversation

@jeffhandley

@jeffhandley jeffhandley commented Jul 27, 2026

Copy link
Copy Markdown
Member

Tracks #7658

Problem

ToolApprovalRequestContent.RequiresConfirmation is [Experimental(MEAI001)], while ToolApprovalRequestContent is a stable, statically registered [JsonDerivedType] of the [JsonPolymorphic] AIContent and InputRequestContent hierarchies. This regression was introduced in 10.8.0 by Add ToolApprovalRequestContent.RequiresConfirmation (#7549).

As a result, System.Text.Json source generation emits the experimental member into the JSON metadata for any consumer context that reaches AIContent, even when the consumer never uses approval APIs:

[JsonSerializable(typeof(List<AIContent>))]
internal partial class JsonContext : JsonSerializerContext;

The build emits MEAI001 from source-generated code. Consumers are then forced into a project-wide suppression as the only practical remedy, but the suppression does not remove the member from their generated contract: their externally exposed JSON payloads still include the experimental requiresConfirmation field without an approval-feature opt-in.

Fix

ToolApprovalRequestContent is stable; only RequiresConfirmation is experimental. Keep the type in the static polymorphic contract, but gate its serialization at the member level using the established pattern used by UsageDetails token counts and HostedFileContent:

[Experimental(...)]
[JsonIgnore]
public bool RequiresConfirmation
{
    get => RequiresConfirmationCore;
    set => RequiresConfirmationCore = value;
}

[JsonInclude]
[JsonPropertyName("requiresConfirmation")]
internal bool RequiresConfirmationCore { get; set; } = true;

The internal member remains available to the package-owned default serialization path, so persisted conversations continue to round-trip requiresConfirmation. Consumer source-generated contracts omit the experimental field, and direct use of the public property continues to require MEAI001 opt-in.

The two Microsoft.Extensions.AI.Evaluation.Reporting projects had gained <NoWarn>$(NoWarn);MEAI001</NoWarn> alongside #7549 solely because of this leak. The fix removes both suppressions; their clean builds demonstrate that they were leak-driven. Abstractions and AI retain their suppressions because they legitimately use experimental APIs directly.

Regression coverage

Adds a no-suppression consumer-boundary guard in Microsoft.Extensions.AI.Stabilization.Tests:

  • AIContentSerializationRegressionContext.cs source-generates List<AIContent>. Any experimental member leaking into the contract produces MEAI001 at compile time.
  • AIContentSerializationRegressionTests.cs confirms stable List<AIContent> round-trips through that generated context.
  • ToolApprovalRequestContent tests verify RequiresConfirmation still round-trips as both true and false through AIJsonUtilities.DefaultOptions and AIContent polymorphism.

Validation

  • Microsoft.Extensions.AI.Stabilization.Tests: 90/90 pass across net472, net8.0, net9.0, and net10.0 without an MEAI001 suppression.
  • Microsoft.Extensions.AI.Abstractions.Tests: 1634 pass; the intended persistence round-trip remains intact.
  • Negative proof: restoring the original public serialized property produces MEAI001 in the generated AIContentSerializationRegressionContext.ToolApprovalRequestContent.g.cs across all four target frameworks.
Microsoft Reviewers: Open in CodeFlow

…erated AIContent contexts

ToolApprovalRequestContent.RequiresConfirmation is [Experimental(MEAI001)] but the
type is a stable, statically-registered [JsonDerivedType] of the [JsonPolymorphic]
AIContent. System.Text.Json's source generator therefore emits the experimental member
into the JSON metadata of any consumer whose JsonSerializerContext includes AIContent
(e.g. List<AIContent>), forcing that consumer to suppress MEAI001 even when it never
uses approval APIs.

Route serialization through a non-experimental internal member: the public property is
now [JsonIgnore] and delegates to internal RequiresConfirmationCore, which carries
[JsonInclude] and [JsonPropertyName("requiresConfirmation")] so the value still
round-trips through the package's default serialization options. This mirrors the
existing pattern on UsageDetails token counts and HostedFileContent.

Also removes the now-unnecessary MEAI001 suppressions from the two Evaluation.Reporting
projects, which had gained them solely because of this leak (PR dotnet#7549).

Adds a no-suppression, MEAI001-as-error regression guard in the Stabilization tests: a
source-generated context over List<AIContent> that fails to compile if any experimental
member leaks back into consumer metadata.

Fixes dotnet#7658

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 27, 2026 11:07
@jeffhandley
jeffhandley requested review from a team as code owners July 27, 2026 11:07
@jeffhandley
jeffhandley requested a review from jozkee July 27, 2026 11:07

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 fixes a regression where the experimental ToolApprovalRequestContent.RequiresConfirmation member leaked into consumers’ System.Text.Json source-generated AIContent contracts (triggering MEAI001) by gating serialization at the member level while preserving round-tripping through the library’s default serialization path.

Changes:

  • Mark RequiresConfirmation as [JsonIgnore] and route JSON through an internal RequiresConfirmationCore property annotated with [JsonInclude]/[JsonPropertyName].
  • Add a stabilization test JsonSerializerContext rooted at List<AIContent> plus a runtime round-trip test to ensure the generated context is functional.
  • Remove now-unnecessary MEAI001 suppressions from the two Evaluation.Reporting projects.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/Libraries/Microsoft.Extensions.AI.Stabilization.Tests/AIContentSerializationRegressionTests.cs Runtime round-trip test to validate the source-generated List<AIContent> context works.
test/Libraries/Microsoft.Extensions.AI.Stabilization.Tests/AIContentSerializationRegressionContext.cs Compile-time guard context to prevent experimental-member leakage into consumer source generation.
src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/CSharp/Microsoft.Extensions.AI.Evaluation.Reporting.csproj Removes MEAI001 suppression that was only needed due to the leak.
src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting.Azure/Microsoft.Extensions.AI.Evaluation.Reporting.Azure.csproj Removes MEAI001 suppression that was only needed due to the leak.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ToolApprovalRequestContent.cs Implements the [JsonIgnore] + internal *Core serialization pattern to stop source-gen leakage while preserving default-option round-trip behavior.

@jozkee jozkee left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you.

@jeffhandley
jeffhandley merged commit afda3f7 into dotnet:main Jul 27, 2026
7 checks passed
jeffhandley added a commit that referenced this pull request Jul 27, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GuillaumeBodson pushed a commit to GuillaumeBodson/Filer that referenced this pull request Aug 13, 2026
Updated
[Microsoft.AspNetCore.Authentication.JwtBearer](https://github.com/dotnet/dotnet)
from 10.0.10 to 10.0.11.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.Authentication.JwtBearer's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.AspNetCore.Components.WebAssembly.DevServer](https://github.com/dotnet/dotnet)
from 10.0.10 to 10.0.11.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.Components.WebAssembly.DevServer's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.AspNetCore.Identity.EntityFrameworkCore](https://github.com/dotnet/dotnet)
from 10.0.10 to 10.0.11.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.Identity.EntityFrameworkCore's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.EntityFrameworkCore](https://github.com/dotnet/dotnet) from
10.0.10 to 10.0.11.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.EntityFrameworkCore's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.EntityFrameworkCore.Design](https://github.com/dotnet/dotnet)
from 10.0.10 to 10.0.11.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.EntityFrameworkCore.Design's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Extensions.DependencyInjection.Abstractions](https://github.com/dotnet/dotnet)
from 10.0.10 to 10.0.11.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.DependencyInjection.Abstractions's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore](https://github.com/dotnet/dotnet)
from 10.0.10 to 10.0.11.

<details>
<summary>Release notes</summary>

_Sourced from
[Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Extensions.Diagnostics.Testing](https://github.com/dotnet/extensions)
from 10.8.0 to 10.9.0.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Diagnostics.Testing's
releases](https://github.com/dotnet/extensions/releases)._

## 10.9.0

Version 10.9.0 is headlined by changes in these areas:

* **AI:** New experimental routing APIs center on the abstract
`RoutingChatClient` base class, with `SemanticRoutingChatClient` as a
concrete semantic-routing implementation. Separately, the abstract
`FailoverChatClient` specialization and its concrete
`OrderedFailoverChatClient` implementation add failover routing.
* **AI Evaluation:** The generated report gains redesigned Overview,
Cases, History, and Comparison views.
* **ASP.NET Core and HTTP diagnostics:** The release adds HTTP request
latency log enrichment and fixes configuration binding, response-body
logging, request-path redaction, and resilience package version
handling.
* **Source-generated logging and service discovery:** Fixes cover
classification type qualification, thread-local state cleanup, and DNS
query suffix handling.

## Experimental API Changes

### New Experimental APIs

* New experimental API: HTTP request latency log enrichment
(`EXTEXP0013`) #​7602
* New experimental API: Chat client routing and failover (`MEAI001`)
#​7662

## What's Changed

### AI (`Microsoft.Extensions.AI`,
`Microsoft.Extensions.AI.Abstractions`, and
`Microsoft.Extensions.AI.OpenAI`)

* Add extensible chat client routing #​7662 by @​joshuajyue (co-authored
by @​Copilot)
* Pass the request's options to the selected client #​7685 by
@​joshuajyue (co-authored by @​jozkee @​Copilot)
* AI.Abstractions: fix ExcludeFromSchema dropped under concurrent
AIFunction creation #​7677 by @​jozkee (co-authored by @​Copilot)
* Cap OpenAI dependency version
([b10f9c0](dotnet/extensions@b10f9c0))
by @​jeffhandley (co-authored by @​Copilot)

**Note: Microsoft.Extensions.AI.OpenAI constrains its dependency for
OpenAI to 2.12.x, preventing OpenAI updates to 2.13.0+ due to an
incompatibility. We expect to release Microsoft.Extensions.AI.OpenAI
version 10.9.1 during the week of August 17 to address this issue.**

### HTTP Resilience and Diagnostics
(`Microsoft.Extensions.Http.Resilience` and
`Microsoft.Extensions.Http.Diagnostics`)

* Fix Grpc.Net.ClientFactory version range check - Fixes #​7565 #​7566
by @​Ghost93
* Fix response body logging under debugger #​7678 by @​Rimobul
* Redact outgoing path when route is unknown #​7687 by @​Rimobul
* Fix HTTP client logging config binding #​7691 by @​Rimobul

### ASP.NET Core Extensions
(`Microsoft.AspNetCore.Diagnostics.Middleware`)

* Rename HttpLatencyTelemetry extensions class and drop redundant TFM
guard #​7645 by @​EasyL0ver (co-authored by @​Copilot)
* Add HTTP request latency log enricher (experimental) #​7602 by
@​EasyL0ver (co-authored by @​Copilot)

### Logging Source Generator (`Microsoft.Gen.Logging`)

* [Microsoft.Gen.Logging] Clear thread-local state when logging throws
#​7682 by @​Rimobul
* [Microsoft.Gen.Logging] Fully qualify classification types #​7689 by
@​Rimobul

### AI Evaluation (`Microsoft.Extensions.AI.Evaluation.Reporting`)

* [Microsoft.Extensions.AI.Evaluation.Reporting] Evaluation report
redesign #​7609 by @​grafanaKibana

### Project Templates (`Microsoft.McpServer.ProjectTemplates`)

* Remove MCP server project template #​7680 by @​jeffhandley
(co-authored by @​Copilot)

 ... (truncated)

## 10.8.4

This servicing update refreshes the .NET AI project templates ahead of
the July 30, 2026 retirement of GitHub Models — removing the GitHub
Models provider option and updating template dependencies.

As a result, both the AI Chat Web (`aichatweb`) and AI Agent Web API
(`aiagent-webapi`) templates now **require** the AI service provider to
be chosen explicitly via `--provider`; there is no longer a default. One
of the following must be selected:

- `--provider azureopenai` — Azure OpenAI
- `--provider ollama` — Ollama (for local development)
- `--provider openai` — OpenAI Platform

## Packages in this release

| Package | Version |
|---|---|
| Microsoft.Extensions.AI.Templates | 10.8.4-preview.3.26379.3 |
| Microsoft.Agents.AI.ProjectTemplates | 1.13.0-preview.1.26379.3 |

## What's Changed

### Project templates

- **Removed the GitHub Models provider** from the AI Chat Web and AI
Agent Web API templates, ahead of [GitHub Models being fully retired on
July 30,
2026](https://github.blog/changelog/2026-07-01-github-models-is-being-fully-retired-on-july-30-2026/).
The `--provider` option is now required with no default
([#​7667](dotnet/extensions#7667)).
- Updated AI template dependencies — bumped `Aspire.Hosting.AppHost` to
`13.4.6` and `CommunityToolkit.VectorData.SqliteVec` to
`1.0.0-preview.4` (aligned `System.Linq.AsyncEnumerable` to `10.0.9`),
replacing earlier workaround package pins
([#​7639](dotnet/extensions#7639)).

## Full Changelog

- dotnet/extensions@v10.8.3...v10.8.4


## 10.8.3

## Packages in this release

| Package | Version |
|---|---|
| Microsoft.Extensions.AI | 10.8.3 |
| Microsoft.Extensions.AI.Abstractions | 10.8.3 |
| Microsoft.Extensions.AI.OpenAI | 10.8.3 |

## Experimental API Changes

### Experimental API behavior updates

- Updated serialization behavior for experimental
`ToolApprovalRequestContent.RequiresConfirmation` so it no longer leaks
into consumer source-generated `AIContent` JSON metadata unless approval
APIs are intentionally used
([#​7659](dotnet/extensions#7659)).

## What's Changed

### AI abstractions and serialization

- Fixed MEAI001 leakage from `RequiresConfirmation` in source-generated
`AIContent` contexts by using an internal JSON-included backing member
while keeping the public experimental member ignored for
source-generation metadata
([#​7659](dotnet/extensions#7659)).

## Test Improvements

- Added stabilization regression coverage to verify consumer
source-generated `List<AIContent>` contexts compile and round-trip
without requiring MEAI001 suppression
([#​7659](dotnet/extensions#7659)).

## Full Changelog

- dotnet/extensions@v10.8.2...v10.8.3


## 10.8.2

This servicing release updates
Microsoft.Extensions.VectorData.ConformanceTests to 10.8.2 and includes
targeted test framework migration fixes.

## Packages in this release

| Package | Version | Note |
|---------|---------|---------|
| Microsoft.Extensions.VectorData.Abstractions | 10.8.2 | Published
August 7, 2026 |
| Microsoft.Extensions.VectorData.ConformanceTests | 10.8.2 | |

**Update: August 7, 2026**
The Microsoft.Extensions.VectorData.Abstractions package was initially
excluded from this release by mistake. Because
Microsoft.Extensions.VectorData.ConformanceTests has a dependency on
Microsoft.Extensions.VectorData.Abstractions, that led to failures when
updating to Microsoft.Extensions.VectorData.ConformanceTests 10.8.2.

Microsoft.Extensions.VectorData.Abstractions was published August 7,
2026 to resolve that issue.

## What's Changed

### AI

* Move Microsoft.Extensions.VectorData.ConformanceTests to xUnit 3
#​7636 by @​adamsitnik (co-authored by @​Copilot)

## Acknowledgements

* @​roji reviewed pull requests

**Full Changelog**:
dotnet/extensions@v10.8.1...v10.8.2

## 10.8.1

This servicing release updates the Microsoft.Extensions.AI,
Microsoft.Extensions.AI.Abstractions, and Microsoft.Extensions.AI.OpenAI
packages to 10.8.1 with two targeted fixes: correct
tool-call/tool-result ordering when resuming approval-gated functions
with service-managed chat history, and preservation of the OpenAI
Responses reasoning item id for stateless (store=false) encrypted
reasoning.

## Packages in this release

| Package | Version |
|---------|---------|
| Microsoft.Extensions.AI | 10.8.1 |
| Microsoft.Extensions.AI.Abstractions | 10.8.1 |
| Microsoft.Extensions.AI.OpenAI | 10.8.1 |

## What's Changed

### AI

* Fix FICC tool_calls/tool ordering with approvals and service-managed
chat history #​7617 by @​westey-m
* Roundtrip OpenAI Responses reasoning item id for stateless
(store=false) encrypted reasoning #​7629 by @​rogerbarreto (co-authored
by @​tarekgh)

## Acknowledgements

* @​jozkee reviewed pull requests

**Full Changelog**:
dotnet/extensions@v10.8.0...v10.8.1


Commits viewable in [compare
view](dotnet/extensions@v10.8.0...v10.9.0).
</details>

Updated [Microsoft.Extensions.Http](https://github.com/dotnet/dotnet)
from 10.0.10 to 10.0.11.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Http's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This was referenced Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants