diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs
index 5ef1be352..33dce2ec6 100644
--- a/dotnet/src/Generated/SessionEvents.cs
+++ b/dotnet/src/Generated/SessionEvents.cs
@@ -17,7 +17,7 @@ namespace GitHub.Copilot.SDK;
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[JsonPolymorphic(
TypeDiscriminatorPropertyName = "type",
- UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization)]
+ IgnoreUnrecognizedTypeDiscriminators = true)]
[JsonDerivedType(typeof(AbortEvent), "abort")]
[JsonDerivedType(typeof(AssistantIntentEvent), "assistant.intent")]
[JsonDerivedType(typeof(AssistantMessageEvent), "assistant.message")]
@@ -79,7 +79,7 @@ namespace GitHub.Copilot.SDK;
[JsonDerivedType(typeof(UserInputCompletedEvent), "user_input.completed")]
[JsonDerivedType(typeof(UserInputRequestedEvent), "user_input.requested")]
[JsonDerivedType(typeof(UserMessageEvent), "user.message")]
-public abstract partial class SessionEvent
+public partial class SessionEvent
{
/// Unique event identifier (UUID v4), generated when the event is emitted.
[JsonPropertyName("id")]
@@ -102,7 +102,7 @@ public abstract partial class SessionEvent
/// The event type discriminator.
///
[JsonIgnore]
- public abstract string Type { get; }
+ public virtual string Type => "unknown";
/// Deserializes a JSON string into a .
public static SessionEvent FromJson(string json) =>
diff --git a/dotnet/test/ForwardCompatibilityTests.cs b/dotnet/test/ForwardCompatibilityTests.cs
new file mode 100644
index 000000000..d3f5b7785
--- /dev/null
+++ b/dotnet/test/ForwardCompatibilityTests.cs
@@ -0,0 +1,100 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+using Xunit;
+
+namespace GitHub.Copilot.SDK.Test;
+
+///
+/// Tests for forward-compatible handling of unknown session event types.
+/// Verifies that the SDK gracefully handles event types introduced by newer CLI versions.
+///
+public class ForwardCompatibilityTests
+{
+ [Fact]
+ public void FromJson_KnownEventType_DeserializesNormally()
+ {
+ var json = """
+ {
+ "id": "00000000-0000-0000-0000-000000000001",
+ "timestamp": "2026-01-01T00:00:00Z",
+ "parentId": null,
+ "type": "user.message",
+ "data": {
+ "content": "Hello"
+ }
+ }
+ """;
+
+ var result = SessionEvent.FromJson(json);
+
+ Assert.IsType(result);
+ Assert.Equal("user.message", result.Type);
+ }
+
+ [Fact]
+ public void FromJson_UnknownEventType_ReturnsBaseSessionEvent()
+ {
+ var json = """
+ {
+ "id": "12345678-1234-1234-1234-123456789abc",
+ "timestamp": "2026-06-15T10:30:00Z",
+ "parentId": "abcdefab-abcd-abcd-abcd-abcdefabcdef",
+ "type": "future.feature_from_server",
+ "data": { "key": "value" }
+ }
+ """;
+
+ var result = SessionEvent.FromJson(json);
+
+ Assert.IsType(result);
+ Assert.Equal("unknown", result.Type);
+ }
+
+ [Fact]
+ public void FromJson_UnknownEventType_PreservesBaseMetadata()
+ {
+ var json = """
+ {
+ "id": "12345678-1234-1234-1234-123456789abc",
+ "timestamp": "2026-06-15T10:30:00Z",
+ "parentId": "abcdefab-abcd-abcd-abcd-abcdefabcdef",
+ "type": "future.feature_from_server",
+ "data": {}
+ }
+ """;
+
+ var result = SessionEvent.FromJson(json);
+
+ Assert.Equal(Guid.Parse("12345678-1234-1234-1234-123456789abc"), result.Id);
+ Assert.Equal(DateTimeOffset.Parse("2026-06-15T10:30:00Z"), result.Timestamp);
+ Assert.Equal(Guid.Parse("abcdefab-abcd-abcd-abcd-abcdefabcdef"), result.ParentId);
+ }
+
+ [Fact]
+ public void FromJson_MultipleEvents_MixedKnownAndUnknown()
+ {
+ var events = new[]
+ {
+ """{"id":"00000000-0000-0000-0000-000000000001","timestamp":"2026-01-01T00:00:00Z","parentId":null,"type":"user.message","data":{"content":"Hi"}}""",
+ """{"id":"00000000-0000-0000-0000-000000000002","timestamp":"2026-01-01T00:00:00Z","parentId":null,"type":"future.unknown_type","data":{}}""",
+ """{"id":"00000000-0000-0000-0000-000000000003","timestamp":"2026-01-01T00:00:00Z","parentId":null,"type":"user.message","data":{"content":"Bye"}}""",
+ };
+
+ var results = events.Select(SessionEvent.FromJson).ToList();
+
+ Assert.Equal(3, results.Count);
+ Assert.IsType(results[0]);
+ Assert.IsType(results[1]);
+ Assert.IsType(results[2]);
+ }
+
+ [Fact]
+ public void SessionEvent_Type_DefaultsToUnknown()
+ {
+ var evt = new SessionEvent();
+
+ Assert.Equal("unknown", evt.Type);
+ }
+}
diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts
index 3aeb0eef3..0df541b24 100644
--- a/scripts/codegen/csharp.ts
+++ b/scripts/codegen/csharp.ts
@@ -521,11 +521,11 @@ namespace GitHub.Copilot.SDK;
lines.push(`/// Provides the base class from which all session events derive.`);
lines.push(`/// `);
lines.push(`[DebuggerDisplay("{DebuggerDisplay,nq}")]`);
- lines.push(`[JsonPolymorphic(`, ` TypeDiscriminatorPropertyName = "type",`, ` UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization)]`);
+ lines.push(`[JsonPolymorphic(`, ` TypeDiscriminatorPropertyName = "type",`, ` IgnoreUnrecognizedTypeDiscriminators = true)]`);
for (const variant of [...variants].sort((a, b) => a.typeName.localeCompare(b.typeName))) {
lines.push(`[JsonDerivedType(typeof(${variant.className}), "${variant.typeName}")]`);
}
- lines.push(`public abstract partial class SessionEvent`, `{`);
+ lines.push(`public partial class SessionEvent`, `{`);
lines.push(...xmlDocComment(baseDesc("id"), " "));
lines.push(` [JsonPropertyName("id")]`, ` public Guid Id { get; set; }`, "");
lines.push(...xmlDocComment(baseDesc("timestamp"), " "));
@@ -535,7 +535,7 @@ namespace GitHub.Copilot.SDK;
lines.push(...xmlDocComment(baseDesc("ephemeral"), " "));
lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`, ` [JsonPropertyName("ephemeral")]`, ` public bool? Ephemeral { get; set; }`, "");
lines.push(` /// `, ` /// The event type discriminator.`, ` /// `);
- lines.push(` [JsonIgnore]`, ` public abstract string Type { get; }`, "");
+ lines.push(` [JsonIgnore]`, ` public virtual string Type => "unknown";`, "");
lines.push(` /// Deserializes a JSON string into a .`);
lines.push(` public static SessionEvent FromJson(string json) =>`, ` JsonSerializer.Deserialize(json, SessionEventsJsonContext.Default.SessionEvent)!;`, "");
lines.push(` /// Serializes this event to a JSON string.`);