diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index 9b95114b16..f7ca336e6f 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -55,11 +55,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/ConfigureAGUIJsonOptions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/ConfigureAGUIJsonOptions.cs
index 27a23ed4d6..78eccec92f 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/ConfigureAGUIJsonOptions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/ConfigureAGUIJsonOptions.cs
@@ -16,11 +16,18 @@ public void Configure(JsonOptions options)
{
var chain = options.SerializerOptions.TypeInfoResolverChain;
- // Agent Framework abstractions first to ensure M.E.AI types are handled via its resolver,
- // followed by the AG-UI wire-format resolver for protocol types (the AG-UI context is needed
- // on the net10 TypedResults.ServerSentEvents path, which serializes events through the
- // configured ASP.NET Core JsonSerializerOptions).
- chain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
- chain.Add(AGUIJsonSerializerContext.Default.Options.TypeInfoResolver!);
+ // Both resolvers must go in front of the reflection-based resolver ASP.NET Core already
+ // placed at the head of the chain; appending leaves them unreachable for every type that
+ // reflection can handle, which silently discards the AG-UI wire-format rules.
+ //
+ // AGUIJsonUtilities.DefaultTypeInfoResolver is the AG-UI context plus the modifier that omits
+ // properties with no value. Without it the SSE events go out with explicit nulls for their
+ // optional fields ("parentRunId": null and similar), which receiving SDKs reject. The AG-UI
+ // resolver is needed on the net10 TypedResults.ServerSentEvents path, which serializes events
+ // through the configured ASP.NET Core JsonSerializerOptions.
+ //
+ // Agent Framework abstractions follow so that M.E.AI types are handled via its resolver.
+ chain.Insert(0, AGUIJsonUtilities.DefaultTypeInfoResolver);
+ chain.Insert(1, AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
}
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/ConfigureAGUIJsonOptionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/ConfigureAGUIJsonOptionsTests.cs
index 9ffe491bf6..fd9f6ae742 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/ConfigureAGUIJsonOptionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/ConfigureAGUIJsonOptionsTests.cs
@@ -33,6 +33,19 @@ public void AddAGUIServer_ConfiguresJsonOptions_ResolvesAgentAbstractionsTypes()
options.Invoking(o => o.GetTypeInfo(typeof(ChatMessage))).Should().NotThrow();
}
+ [Fact]
+ public void AddAGUIServer_ConfiguresJsonOptions_OmitsOptionalFieldsWithNoValue()
+ {
+ JsonSerializerOptions options = BuildConfiguredSerializerOptions();
+
+ string json = JsonSerializer.Serialize(new RunStartedEvent { ThreadId = "thread", RunId = "run" }, options);
+
+ // AG-UI receivers declare the optional event fields as optional, not nullable, so writing them
+ // as explicit nulls fails validation client-side. The configured options must omit them.
+ json.Should().NotContain("null");
+ json.Should().Be("""{"type":"RUN_STARTED","threadId":"thread","runId":"run"}""");
+ }
+
private static JsonSerializerOptions BuildConfiguredSerializerOptions()
{
ServiceCollection services = new();