Skip to content

Commit 40009e1

Browse files
committed
refactor: use IgnoreUnrecognizedTypeDiscriminators per review feedback
1 parent 496c49b commit 40009e1

File tree

8 files changed

+113
-287
lines changed

8 files changed

+113
-287
lines changed

dotnet/src/Client.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,8 +1303,11 @@ public void OnSessionEvent(string sessionId, JsonElement? @event)
13031303
var session = client.GetSession(sessionId);
13041304
if (session != null && @event != null)
13051305
{
1306-
var evt = SessionEvent.TryFromJson(@event.Value.GetRawText(), client._logger);
1307-
session.DispatchEvent(evt);
1306+
var evt = SessionEvent.FromJson(@event.Value.GetRawText());
1307+
if (evt != null)
1308+
{
1309+
session.DispatchEvent(evt);
1310+
}
13081311
}
13091312
}
13101313

dotnet/src/Generated/SessionEvents.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace GitHub.Copilot.SDK;
1717
[DebuggerDisplay("{DebuggerDisplay,nq}")]
1818
[JsonPolymorphic(
1919
TypeDiscriminatorPropertyName = "type",
20-
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization)]
20+
IgnoreUnrecognizedTypeDiscriminators = true)]
2121
[JsonDerivedType(typeof(AbortEvent), "abort")]
2222
[JsonDerivedType(typeof(AssistantIntentEvent), "assistant.intent")]
2323
[JsonDerivedType(typeof(AssistantMessageEvent), "assistant.message")]
@@ -79,7 +79,7 @@ namespace GitHub.Copilot.SDK;
7979
[JsonDerivedType(typeof(UserInputCompletedEvent), "user_input.completed")]
8080
[JsonDerivedType(typeof(UserInputRequestedEvent), "user_input.requested")]
8181
[JsonDerivedType(typeof(UserMessageEvent), "user.message")]
82-
public abstract partial class SessionEvent
82+
public partial class SessionEvent
8383
{
8484
/// <summary>Unique event identifier (UUID v4), generated when the event is emitted.</summary>
8585
[JsonPropertyName("id")]
@@ -102,7 +102,7 @@ public abstract partial class SessionEvent
102102
/// The event type discriminator.
103103
/// </summary>
104104
[JsonIgnore]
105-
public abstract string Type { get; }
105+
public virtual string Type => "unknown";
106106

107107
/// <summary>Deserializes a JSON string into a <see cref="SessionEvent"/>.</summary>
108108
public static SessionEvent FromJson(string json) =>

dotnet/src/Session.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,8 @@ public async Task<IReadOnlyList<SessionEvent>> GetMessagesAsync(CancellationToke
681681
"session.getMessages", [new GetMessagesRequest { SessionId = SessionId }], cancellationToken);
682682

683683
return response.Events
684-
.Select(e => SessionEvent.TryFromJson(e.ToJsonString(), _logger))
684+
.Select(e => SessionEvent.FromJson(e.ToJsonString()))
685+
.OfType<SessionEvent>()
685686
.ToList();
686687
}
687688

dotnet/src/SessionEventExtensions.cs

Lines changed: 0 additions & 59 deletions
This file was deleted.

dotnet/src/UnknownSessionEvent.cs

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
using Xunit;
6+
7+
namespace GitHub.Copilot.SDK.Test;
8+
9+
/// <summary>
10+
/// Tests for forward-compatible handling of unknown session event types.
11+
/// Verifies that the SDK gracefully handles event types introduced by newer CLI versions.
12+
/// </summary>
13+
public class ForwardCompatibilityTests
14+
{
15+
[Fact]
16+
public void FromJson_KnownEventType_DeserializesNormally()
17+
{
18+
var json = """
19+
{
20+
"id": "00000000-0000-0000-0000-000000000001",
21+
"timestamp": "2026-01-01T00:00:00Z",
22+
"parentId": null,
23+
"type": "user.message",
24+
"data": {
25+
"content": "Hello"
26+
}
27+
}
28+
""";
29+
30+
var result = SessionEvent.FromJson(json);
31+
32+
Assert.IsType<UserMessageEvent>(result);
33+
Assert.Equal("user.message", result.Type);
34+
}
35+
36+
[Fact]
37+
public void FromJson_UnknownEventType_ReturnsBaseSessionEvent()
38+
{
39+
var json = """
40+
{
41+
"id": "12345678-1234-1234-1234-123456789abc",
42+
"timestamp": "2026-06-15T10:30:00Z",
43+
"parentId": "abcdefab-abcd-abcd-abcd-abcdefabcdef",
44+
"type": "future.feature_from_server",
45+
"data": { "key": "value" }
46+
}
47+
""";
48+
49+
var result = SessionEvent.FromJson(json);
50+
51+
Assert.IsType<SessionEvent>(result);
52+
Assert.Equal("unknown", result.Type);
53+
}
54+
55+
[Fact]
56+
public void FromJson_UnknownEventType_PreservesBaseMetadata()
57+
{
58+
var json = """
59+
{
60+
"id": "12345678-1234-1234-1234-123456789abc",
61+
"timestamp": "2026-06-15T10:30:00Z",
62+
"parentId": "abcdefab-abcd-abcd-abcd-abcdefabcdef",
63+
"type": "future.feature_from_server",
64+
"data": {}
65+
}
66+
""";
67+
68+
var result = SessionEvent.FromJson(json);
69+
70+
Assert.Equal(Guid.Parse("12345678-1234-1234-1234-123456789abc"), result.Id);
71+
Assert.Equal(DateTimeOffset.Parse("2026-06-15T10:30:00Z"), result.Timestamp);
72+
Assert.Equal(Guid.Parse("abcdefab-abcd-abcd-abcd-abcdefabcdef"), result.ParentId);
73+
}
74+
75+
[Fact]
76+
public void FromJson_MultipleEvents_MixedKnownAndUnknown()
77+
{
78+
var events = new[]
79+
{
80+
"""{"id":"00000000-0000-0000-0000-000000000001","timestamp":"2026-01-01T00:00:00Z","parentId":null,"type":"user.message","data":{"content":"Hi"}}""",
81+
"""{"id":"00000000-0000-0000-0000-000000000002","timestamp":"2026-01-01T00:00:00Z","parentId":null,"type":"future.unknown_type","data":{}}""",
82+
"""{"id":"00000000-0000-0000-0000-000000000003","timestamp":"2026-01-01T00:00:00Z","parentId":null,"type":"user.message","data":{"content":"Bye"}}""",
83+
};
84+
85+
var results = events.Select(SessionEvent.FromJson).ToList();
86+
87+
Assert.Equal(3, results.Count);
88+
Assert.IsType<UserMessageEvent>(results[0]);
89+
Assert.IsType<SessionEvent>(results[1]);
90+
Assert.IsType<UserMessageEvent>(results[2]);
91+
}
92+
93+
[Fact]
94+
public void SessionEvent_Type_DefaultsToUnknown()
95+
{
96+
var evt = new SessionEvent();
97+
98+
Assert.Equal("unknown", evt.Type);
99+
}
100+
}

0 commit comments

Comments
 (0)