diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs
index c6a5b971..98e7d747 100644
--- a/dotnet/src/Client.cs
+++ b/dotnet/src/Client.cs
@@ -5,7 +5,6 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using Newtonsoft.Json.Linq;
using StreamJsonRpc;
using System.Collections.Concurrent;
using System.Data;
@@ -13,7 +12,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Net.Sockets;
using System.Text.Json;
-using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
@@ -43,8 +41,8 @@ namespace GitHub.Copilot.SDK;
/// // Handle events
/// using var subscription = session.On(evt =>
/// {
-/// if (evt.Type == "assistant.message")
-/// Console.WriteLine(evt.Data?.Content);
+/// if (evt is AssistantMessageEvent assistantMessage)
+/// Console.WriteLine(assistantMessage.Data?.Content);
/// });
///
/// // Send a message
@@ -724,12 +722,10 @@ static IJsonRpcMessageFormatter CreateFormatter()
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
+ AllowOutOfOrderMetadataProperties = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
- foreach (var converter in SerializerOptions.Default.Converters)
- {
- options.Converters.Add(converter);
- }
+
return new SystemTextJsonFormatter() { JsonSerializerOptions = options };
}
diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs
index 81d6b3e0..6a3b9c5f 100644
--- a/dotnet/src/Generated/SessionEvents.cs
+++ b/dotnet/src/Generated/SessionEvents.cs
@@ -6,7 +6,7 @@
//
// Generated from: @github/copilot/session-events.schema.json
// Generated by: scripts/generate-session-types.ts
-// Generated at: 2026-01-20T04:18:06.775Z
+// Generated at: 2026-01-21T14:50:29.306Z
//
// To update these types:
// 1. Update the schema in copilot-agent-runtime
@@ -14,86 +14,53 @@
//
#nullable enable
-#pragma warning disable CS8618
namespace GitHub.Copilot.SDK
{
using System;
using System.Collections.Generic;
using System.Text.Json;
- using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
- ///
- /// Custom JSON converter for SessionEvent that handles discriminator appearing anywhere in JSON.
- ///
- internal class SessionEventConverter : JsonConverter
- {
- private static readonly Dictionary TypeMap = new()
- {
- ["session.start"] = typeof(SessionStartEvent),
- ["session.resume"] = typeof(SessionResumeEvent),
- ["session.error"] = typeof(SessionErrorEvent),
- ["session.idle"] = typeof(SessionIdleEvent),
- ["session.info"] = typeof(SessionInfoEvent),
- ["session.model_change"] = typeof(SessionModelChangeEvent),
- ["session.handoff"] = typeof(SessionHandoffEvent),
- ["session.truncation"] = typeof(SessionTruncationEvent),
- ["session.usage_info"] = typeof(SessionUsageInfoEvent),
- ["session.compaction_start"] = typeof(SessionCompactionStartEvent),
- ["session.compaction_complete"] = typeof(SessionCompactionCompleteEvent),
- ["user.message"] = typeof(UserMessageEvent),
- ["pending_messages.modified"] = typeof(PendingMessagesModifiedEvent),
- ["assistant.turn_start"] = typeof(AssistantTurnStartEvent),
- ["assistant.intent"] = typeof(AssistantIntentEvent),
- ["assistant.reasoning"] = typeof(AssistantReasoningEvent),
- ["assistant.reasoning_delta"] = typeof(AssistantReasoningDeltaEvent),
- ["assistant.message"] = typeof(AssistantMessageEvent),
- ["assistant.message_delta"] = typeof(AssistantMessageDeltaEvent),
- ["assistant.turn_end"] = typeof(AssistantTurnEndEvent),
- ["assistant.usage"] = typeof(AssistantUsageEvent),
- ["abort"] = typeof(AbortEvent),
- ["tool.user_requested"] = typeof(ToolUserRequestedEvent),
- ["tool.execution_start"] = typeof(ToolExecutionStartEvent),
- ["tool.execution_partial_result"] = typeof(ToolExecutionPartialResultEvent),
- ["tool.execution_complete"] = typeof(ToolExecutionCompleteEvent),
- ["subagent.started"] = typeof(SubagentStartedEvent),
- ["subagent.completed"] = typeof(SubagentCompletedEvent),
- ["subagent.failed"] = typeof(SubagentFailedEvent),
- ["subagent.selected"] = typeof(SubagentSelectedEvent),
- ["hook.start"] = typeof(HookStartEvent),
- ["hook.end"] = typeof(HookEndEvent),
- ["system.message"] = typeof(SystemMessageEvent),
- };
-
- public override SessionEvent? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- // Parse as JsonNode to find the discriminator regardless of property order
- var node = JsonNode.Parse(ref reader);
- if (node is not JsonObject obj)
- throw new JsonException("Expected JSON object");
-
- var typeProp = obj["type"]?.GetValue();
- if (string.IsNullOrEmpty(typeProp))
- throw new JsonException("Missing 'type' discriminator property");
-
- if (!TypeMap.TryGetValue(typeProp, out var targetType))
- throw new JsonException($"Unknown event type: {typeProp}");
-
- // Deserialize to the concrete type without using this converter (to avoid recursion)
- return (SessionEvent?)obj.Deserialize(targetType, SerializerOptions.WithoutConverter);
- }
-
- public override void Write(Utf8JsonWriter writer, SessionEvent value, JsonSerializerOptions options)
- {
- JsonSerializer.Serialize(writer, value, value.GetType(), SerializerOptions.WithoutConverter);
- }
- }
-
///
/// Base class for all session events with polymorphic JSON serialization.
///
- [JsonConverter(typeof(SessionEventConverter))]
+ [JsonPolymorphic(
+ TypeDiscriminatorPropertyName = "type",
+ UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization)]
+ [JsonDerivedType(typeof(AbortEvent), "abort")]
+ [JsonDerivedType(typeof(AssistantIntentEvent), "assistant.intent")]
+ [JsonDerivedType(typeof(AssistantMessageEvent), "assistant.message")]
+ [JsonDerivedType(typeof(AssistantMessageDeltaEvent), "assistant.message_delta")]
+ [JsonDerivedType(typeof(AssistantReasoningEvent), "assistant.reasoning")]
+ [JsonDerivedType(typeof(AssistantReasoningDeltaEvent), "assistant.reasoning_delta")]
+ [JsonDerivedType(typeof(AssistantTurnEndEvent), "assistant.turn_end")]
+ [JsonDerivedType(typeof(AssistantTurnStartEvent), "assistant.turn_start")]
+ [JsonDerivedType(typeof(AssistantUsageEvent), "assistant.usage")]
+ [JsonDerivedType(typeof(HookEndEvent), "hook.end")]
+ [JsonDerivedType(typeof(HookStartEvent), "hook.start")]
+ [JsonDerivedType(typeof(PendingMessagesModifiedEvent), "pending_messages.modified")]
+ [JsonDerivedType(typeof(SessionCompactionCompleteEvent), "session.compaction_complete")]
+ [JsonDerivedType(typeof(SessionCompactionStartEvent), "session.compaction_start")]
+ [JsonDerivedType(typeof(SessionErrorEvent), "session.error")]
+ [JsonDerivedType(typeof(SessionHandoffEvent), "session.handoff")]
+ [JsonDerivedType(typeof(SessionIdleEvent), "session.idle")]
+ [JsonDerivedType(typeof(SessionInfoEvent), "session.info")]
+ [JsonDerivedType(typeof(SessionModelChangeEvent), "session.model_change")]
+ [JsonDerivedType(typeof(SessionResumeEvent), "session.resume")]
+ [JsonDerivedType(typeof(SessionStartEvent), "session.start")]
+ [JsonDerivedType(typeof(SessionTruncationEvent), "session.truncation")]
+ [JsonDerivedType(typeof(SessionUsageInfoEvent), "session.usage_info")]
+ [JsonDerivedType(typeof(SubagentCompletedEvent), "subagent.completed")]
+ [JsonDerivedType(typeof(SubagentFailedEvent), "subagent.failed")]
+ [JsonDerivedType(typeof(SubagentSelectedEvent), "subagent.selected")]
+ [JsonDerivedType(typeof(SubagentStartedEvent), "subagent.started")]
+ [JsonDerivedType(typeof(SystemMessageEvent), "system.message")]
+ [JsonDerivedType(typeof(ToolExecutionCompleteEvent), "tool.execution_complete")]
+ [JsonDerivedType(typeof(ToolExecutionPartialResultEvent), "tool.execution_partial_result")]
+ [JsonDerivedType(typeof(ToolExecutionStartEvent), "tool.execution_start")]
+ [JsonDerivedType(typeof(ToolUserRequestedEvent), "tool.user_requested")]
+ [JsonDerivedType(typeof(UserMessageEvent), "user.message")]
public abstract partial class SessionEvent
{
[JsonPropertyName("id")]
@@ -112,7 +79,7 @@ public abstract partial class SessionEvent
///
/// The event type discriminator.
///
- [JsonPropertyName("type")]
+ [JsonIgnore]
public abstract string Type { get; }
public static SessionEvent FromJson(string json) =>
@@ -127,10 +94,11 @@ public string ToJson() =>
///
public partial class SessionStartEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.start";
[JsonPropertyName("data")]
- public SessionStartData Data { get; set; }
+ public required SessionStartData Data { get; set; }
}
///
@@ -138,10 +106,11 @@ public partial class SessionStartEvent : SessionEvent
///
public partial class SessionResumeEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.resume";
[JsonPropertyName("data")]
- public SessionResumeData Data { get; set; }
+ public required SessionResumeData Data { get; set; }
}
///
@@ -149,10 +118,11 @@ public partial class SessionResumeEvent : SessionEvent
///
public partial class SessionErrorEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.error";
[JsonPropertyName("data")]
- public SessionErrorData Data { get; set; }
+ public required SessionErrorData Data { get; set; }
}
///
@@ -160,10 +130,11 @@ public partial class SessionErrorEvent : SessionEvent
///
public partial class SessionIdleEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.idle";
[JsonPropertyName("data")]
- public SessionIdleData Data { get; set; }
+ public required SessionIdleData Data { get; set; }
}
///
@@ -171,10 +142,11 @@ public partial class SessionIdleEvent : SessionEvent
///
public partial class SessionInfoEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.info";
[JsonPropertyName("data")]
- public SessionInfoData Data { get; set; }
+ public required SessionInfoData Data { get; set; }
}
///
@@ -182,10 +154,11 @@ public partial class SessionInfoEvent : SessionEvent
///
public partial class SessionModelChangeEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.model_change";
[JsonPropertyName("data")]
- public SessionModelChangeData Data { get; set; }
+ public required SessionModelChangeData Data { get; set; }
}
///
@@ -193,10 +166,11 @@ public partial class SessionModelChangeEvent : SessionEvent
///
public partial class SessionHandoffEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.handoff";
[JsonPropertyName("data")]
- public SessionHandoffData Data { get; set; }
+ public required SessionHandoffData Data { get; set; }
}
///
@@ -204,10 +178,11 @@ public partial class SessionHandoffEvent : SessionEvent
///
public partial class SessionTruncationEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.truncation";
[JsonPropertyName("data")]
- public SessionTruncationData Data { get; set; }
+ public required SessionTruncationData Data { get; set; }
}
///
@@ -215,10 +190,11 @@ public partial class SessionTruncationEvent : SessionEvent
///
public partial class SessionUsageInfoEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.usage_info";
[JsonPropertyName("data")]
- public SessionUsageInfoData Data { get; set; }
+ public required SessionUsageInfoData Data { get; set; }
}
///
@@ -226,10 +202,11 @@ public partial class SessionUsageInfoEvent : SessionEvent
///
public partial class SessionCompactionStartEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.compaction_start";
[JsonPropertyName("data")]
- public SessionCompactionStartData Data { get; set; }
+ public required SessionCompactionStartData Data { get; set; }
}
///
@@ -237,10 +214,11 @@ public partial class SessionCompactionStartEvent : SessionEvent
///
public partial class SessionCompactionCompleteEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "session.compaction_complete";
[JsonPropertyName("data")]
- public SessionCompactionCompleteData Data { get; set; }
+ public required SessionCompactionCompleteData Data { get; set; }
}
///
@@ -248,10 +226,11 @@ public partial class SessionCompactionCompleteEvent : SessionEvent
///
public partial class UserMessageEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "user.message";
[JsonPropertyName("data")]
- public UserMessageData Data { get; set; }
+ public required UserMessageData Data { get; set; }
}
///
@@ -259,10 +238,11 @@ public partial class UserMessageEvent : SessionEvent
///
public partial class PendingMessagesModifiedEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "pending_messages.modified";
[JsonPropertyName("data")]
- public PendingMessagesModifiedData Data { get; set; }
+ public required PendingMessagesModifiedData Data { get; set; }
}
///
@@ -270,10 +250,11 @@ public partial class PendingMessagesModifiedEvent : SessionEvent
///
public partial class AssistantTurnStartEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "assistant.turn_start";
[JsonPropertyName("data")]
- public AssistantTurnStartData Data { get; set; }
+ public required AssistantTurnStartData Data { get; set; }
}
///
@@ -281,10 +262,11 @@ public partial class AssistantTurnStartEvent : SessionEvent
///
public partial class AssistantIntentEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "assistant.intent";
[JsonPropertyName("data")]
- public AssistantIntentData Data { get; set; }
+ public required AssistantIntentData Data { get; set; }
}
///
@@ -292,10 +274,11 @@ public partial class AssistantIntentEvent : SessionEvent
///
public partial class AssistantReasoningEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "assistant.reasoning";
[JsonPropertyName("data")]
- public AssistantReasoningData Data { get; set; }
+ public required AssistantReasoningData Data { get; set; }
}
///
@@ -303,10 +286,11 @@ public partial class AssistantReasoningEvent : SessionEvent
///
public partial class AssistantReasoningDeltaEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "assistant.reasoning_delta";
[JsonPropertyName("data")]
- public AssistantReasoningDeltaData Data { get; set; }
+ public required AssistantReasoningDeltaData Data { get; set; }
}
///
@@ -314,10 +298,11 @@ public partial class AssistantReasoningDeltaEvent : SessionEvent
///
public partial class AssistantMessageEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "assistant.message";
[JsonPropertyName("data")]
- public AssistantMessageData Data { get; set; }
+ public required AssistantMessageData Data { get; set; }
}
///
@@ -325,10 +310,11 @@ public partial class AssistantMessageEvent : SessionEvent
///
public partial class AssistantMessageDeltaEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "assistant.message_delta";
[JsonPropertyName("data")]
- public AssistantMessageDeltaData Data { get; set; }
+ public required AssistantMessageDeltaData Data { get; set; }
}
///
@@ -336,10 +322,11 @@ public partial class AssistantMessageDeltaEvent : SessionEvent
///
public partial class AssistantTurnEndEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "assistant.turn_end";
[JsonPropertyName("data")]
- public AssistantTurnEndData Data { get; set; }
+ public required AssistantTurnEndData Data { get; set; }
}
///
@@ -347,10 +334,11 @@ public partial class AssistantTurnEndEvent : SessionEvent
///
public partial class AssistantUsageEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "assistant.usage";
[JsonPropertyName("data")]
- public AssistantUsageData Data { get; set; }
+ public required AssistantUsageData Data { get; set; }
}
///
@@ -358,10 +346,11 @@ public partial class AssistantUsageEvent : SessionEvent
///
public partial class AbortEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "abort";
[JsonPropertyName("data")]
- public AbortData Data { get; set; }
+ public required AbortData Data { get; set; }
}
///
@@ -369,10 +358,11 @@ public partial class AbortEvent : SessionEvent
///
public partial class ToolUserRequestedEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "tool.user_requested";
[JsonPropertyName("data")]
- public ToolUserRequestedData Data { get; set; }
+ public required ToolUserRequestedData Data { get; set; }
}
///
@@ -380,10 +370,11 @@ public partial class ToolUserRequestedEvent : SessionEvent
///
public partial class ToolExecutionStartEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "tool.execution_start";
[JsonPropertyName("data")]
- public ToolExecutionStartData Data { get; set; }
+ public required ToolExecutionStartData Data { get; set; }
}
///
@@ -391,10 +382,11 @@ public partial class ToolExecutionStartEvent : SessionEvent
///
public partial class ToolExecutionPartialResultEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "tool.execution_partial_result";
[JsonPropertyName("data")]
- public ToolExecutionPartialResultData Data { get; set; }
+ public required ToolExecutionPartialResultData Data { get; set; }
}
///
@@ -402,10 +394,11 @@ public partial class ToolExecutionPartialResultEvent : SessionEvent
///
public partial class ToolExecutionCompleteEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "tool.execution_complete";
[JsonPropertyName("data")]
- public ToolExecutionCompleteData Data { get; set; }
+ public required ToolExecutionCompleteData Data { get; set; }
}
///
@@ -413,10 +406,11 @@ public partial class ToolExecutionCompleteEvent : SessionEvent
///
public partial class SubagentStartedEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "subagent.started";
[JsonPropertyName("data")]
- public SubagentStartedData Data { get; set; }
+ public required SubagentStartedData Data { get; set; }
}
///
@@ -424,10 +418,11 @@ public partial class SubagentStartedEvent : SessionEvent
///
public partial class SubagentCompletedEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "subagent.completed";
[JsonPropertyName("data")]
- public SubagentCompletedData Data { get; set; }
+ public required SubagentCompletedData Data { get; set; }
}
///
@@ -435,10 +430,11 @@ public partial class SubagentCompletedEvent : SessionEvent
///
public partial class SubagentFailedEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "subagent.failed";
[JsonPropertyName("data")]
- public SubagentFailedData Data { get; set; }
+ public required SubagentFailedData Data { get; set; }
}
///
@@ -446,10 +442,11 @@ public partial class SubagentFailedEvent : SessionEvent
///
public partial class SubagentSelectedEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "subagent.selected";
[JsonPropertyName("data")]
- public SubagentSelectedData Data { get; set; }
+ public required SubagentSelectedData Data { get; set; }
}
///
@@ -457,10 +454,11 @@ public partial class SubagentSelectedEvent : SessionEvent
///
public partial class HookStartEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "hook.start";
[JsonPropertyName("data")]
- public HookStartData Data { get; set; }
+ public required HookStartData Data { get; set; }
}
///
@@ -468,10 +466,11 @@ public partial class HookStartEvent : SessionEvent
///
public partial class HookEndEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "hook.end";
[JsonPropertyName("data")]
- public HookEndData Data { get; set; }
+ public required HookEndData Data { get; set; }
}
///
@@ -479,32 +478,33 @@ public partial class HookEndEvent : SessionEvent
///
public partial class SystemMessageEvent : SessionEvent
{
+ [JsonIgnore]
public override string Type => "system.message";
[JsonPropertyName("data")]
- public SystemMessageData Data { get; set; }
+ public required SystemMessageData Data { get; set; }
}
public partial class SessionStartData
{
[JsonPropertyName("sessionId")]
- public string SessionId { get; set; }
+ public required string SessionId { get; set; }
[JsonPropertyName("version")]
- public double Version { get; set; }
+ public required double Version { get; set; }
[JsonPropertyName("producer")]
- public string Producer { get; set; }
+ public required string Producer { get; set; }
[JsonPropertyName("copilotVersion")]
- public string CopilotVersion { get; set; }
+ public required string CopilotVersion { get; set; }
[JsonPropertyName("startTime")]
- public DateTimeOffset StartTime { get; set; }
+ public required DateTimeOffset StartTime { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("selectedModel")]
- public string SelectedModel { get; set; }
+ public string? SelectedModel { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("context")]
@@ -514,10 +514,10 @@ public partial class SessionStartData
public partial class SessionResumeData
{
[JsonPropertyName("resumeTime")]
- public DateTimeOffset ResumeTime { get; set; }
+ public required DateTimeOffset ResumeTime { get; set; }
[JsonPropertyName("eventCount")]
- public double EventCount { get; set; }
+ public required double EventCount { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("context")]
@@ -527,14 +527,14 @@ public partial class SessionResumeData
public partial class SessionErrorData
{
[JsonPropertyName("errorType")]
- public string ErrorType { get; set; }
+ public required string ErrorType { get; set; }
[JsonPropertyName("message")]
- public string Message { get; set; }
+ public required string Message { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("stack")]
- public string Stack { get; set; }
+ public string? Stack { get; set; }
}
public partial class SessionIdleData
@@ -544,29 +544,29 @@ public partial class SessionIdleData
public partial class SessionInfoData
{
[JsonPropertyName("infoType")]
- public string InfoType { get; set; }
+ public required string InfoType { get; set; }
[JsonPropertyName("message")]
- public string Message { get; set; }
+ public required string Message { get; set; }
}
public partial class SessionModelChangeData
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("previousModel")]
- public string PreviousModel { get; set; }
+ public string? PreviousModel { get; set; }
[JsonPropertyName("newModel")]
- public string NewModel { get; set; }
+ public required string NewModel { get; set; }
}
public partial class SessionHandoffData
{
[JsonPropertyName("handoffTime")]
- public DateTimeOffset HandoffTime { get; set; }
+ public required DateTimeOffset HandoffTime { get; set; }
[JsonPropertyName("sourceType")]
- public SessionHandoffDataSourceType SourceType { get; set; }
+ public required SessionHandoffDataSourceType SourceType { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("repository")]
@@ -574,54 +574,54 @@ public partial class SessionHandoffData
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("context")]
- public string Context { get; set; }
+ public string? Context { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("summary")]
- public string Summary { get; set; }
+ public string? Summary { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("remoteSessionId")]
- public string RemoteSessionId { get; set; }
+ public string? RemoteSessionId { get; set; }
}
public partial class SessionTruncationData
{
[JsonPropertyName("tokenLimit")]
- public double TokenLimit { get; set; }
+ public required double TokenLimit { get; set; }
[JsonPropertyName("preTruncationTokensInMessages")]
- public double PreTruncationTokensInMessages { get; set; }
+ public required double PreTruncationTokensInMessages { get; set; }
[JsonPropertyName("preTruncationMessagesLength")]
- public double PreTruncationMessagesLength { get; set; }
+ public required double PreTruncationMessagesLength { get; set; }
[JsonPropertyName("postTruncationTokensInMessages")]
- public double PostTruncationTokensInMessages { get; set; }
+ public required double PostTruncationTokensInMessages { get; set; }
[JsonPropertyName("postTruncationMessagesLength")]
- public double PostTruncationMessagesLength { get; set; }
+ public required double PostTruncationMessagesLength { get; set; }
[JsonPropertyName("tokensRemovedDuringTruncation")]
- public double TokensRemovedDuringTruncation { get; set; }
+ public required double TokensRemovedDuringTruncation { get; set; }
[JsonPropertyName("messagesRemovedDuringTruncation")]
- public double MessagesRemovedDuringTruncation { get; set; }
+ public required double MessagesRemovedDuringTruncation { get; set; }
[JsonPropertyName("performedBy")]
- public string PerformedBy { get; set; }
+ public required string PerformedBy { get; set; }
}
public partial class SessionUsageInfoData
{
[JsonPropertyName("tokenLimit")]
- public double TokenLimit { get; set; }
+ public required double TokenLimit { get; set; }
[JsonPropertyName("currentTokens")]
- public double CurrentTokens { get; set; }
+ public required double CurrentTokens { get; set; }
[JsonPropertyName("messagesLength")]
- public double MessagesLength { get; set; }
+ public required double MessagesLength { get; set; }
}
public partial class SessionCompactionStartData
@@ -631,11 +631,11 @@ public partial class SessionCompactionStartData
public partial class SessionCompactionCompleteData
{
[JsonPropertyName("success")]
- public bool Success { get; set; }
+ public required bool Success { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("error")]
- public string Error { get; set; }
+ public string? Error { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("preCompactionTokens")]
@@ -659,7 +659,7 @@ public partial class SessionCompactionCompleteData
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("summaryContent")]
- public string SummaryContent { get; set; }
+ public string? SummaryContent { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("compactionTokensUsed")]
@@ -669,19 +669,19 @@ public partial class SessionCompactionCompleteData
public partial class UserMessageData
{
[JsonPropertyName("content")]
- public string Content { get; set; }
+ public required string Content { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("transformedContent")]
- public string TransformedContent { get; set; }
+ public string? TransformedContent { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("attachments")]
- public UserMessageDataAttachmentsItem[] Attachments { get; set; }
+ public UserMessageDataAttachmentsItem[]? Attachments { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("source")]
- public string Source { get; set; }
+ public string? Source { get; set; }
}
public partial class PendingMessagesModifiedData
@@ -691,57 +691,57 @@ public partial class PendingMessagesModifiedData
public partial class AssistantTurnStartData
{
[JsonPropertyName("turnId")]
- public string TurnId { get; set; }
+ public required string TurnId { get; set; }
}
public partial class AssistantIntentData
{
[JsonPropertyName("intent")]
- public string Intent { get; set; }
+ public required string Intent { get; set; }
}
public partial class AssistantReasoningData
{
[JsonPropertyName("reasoningId")]
- public string ReasoningId { get; set; }
+ public required string ReasoningId { get; set; }
[JsonPropertyName("content")]
- public string Content { get; set; }
+ public required string Content { get; set; }
}
public partial class AssistantReasoningDeltaData
{
[JsonPropertyName("reasoningId")]
- public string ReasoningId { get; set; }
+ public required string ReasoningId { get; set; }
[JsonPropertyName("deltaContent")]
- public string DeltaContent { get; set; }
+ public required string DeltaContent { get; set; }
}
public partial class AssistantMessageData
{
[JsonPropertyName("messageId")]
- public string MessageId { get; set; }
+ public required string MessageId { get; set; }
[JsonPropertyName("content")]
- public string Content { get; set; }
+ public required string Content { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("toolRequests")]
- public AssistantMessageDataToolRequestsItem[] ToolRequests { get; set; }
+ public AssistantMessageDataToolRequestsItem[]? ToolRequests { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("parentToolCallId")]
- public string ParentToolCallId { get; set; }
+ public string? ParentToolCallId { get; set; }
}
public partial class AssistantMessageDeltaData
{
[JsonPropertyName("messageId")]
- public string MessageId { get; set; }
+ public required string MessageId { get; set; }
[JsonPropertyName("deltaContent")]
- public string DeltaContent { get; set; }
+ public required string DeltaContent { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("totalResponseSizeBytes")]
@@ -749,20 +749,20 @@ public partial class AssistantMessageDeltaData
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("parentToolCallId")]
- public string ParentToolCallId { get; set; }
+ public string? ParentToolCallId { get; set; }
}
public partial class AssistantTurnEndData
{
[JsonPropertyName("turnId")]
- public string TurnId { get; set; }
+ public required string TurnId { get; set; }
}
public partial class AssistantUsageData
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("model")]
- public string Model { get; set; }
+ public string? Model { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("inputTokens")]
@@ -790,73 +790,73 @@ public partial class AssistantUsageData
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("initiator")]
- public string Initiator { get; set; }
+ public string? Initiator { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("apiCallId")]
- public string ApiCallId { get; set; }
+ public string? ApiCallId { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("providerCallId")]
- public string ProviderCallId { get; set; }
+ public string? ProviderCallId { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("quotaSnapshots")]
- public Dictionary QuotaSnapshots { get; set; }
+ public Dictionary? QuotaSnapshots { get; set; }
}
public partial class AbortData
{
[JsonPropertyName("reason")]
- public string Reason { get; set; }
+ public required string Reason { get; set; }
}
public partial class ToolUserRequestedData
{
[JsonPropertyName("toolCallId")]
- public string ToolCallId { get; set; }
+ public required string ToolCallId { get; set; }
[JsonPropertyName("toolName")]
- public string ToolName { get; set; }
+ public required string ToolName { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("arguments")]
- public object Arguments { get; set; }
+ public object? Arguments { get; set; }
}
public partial class ToolExecutionStartData
{
[JsonPropertyName("toolCallId")]
- public string ToolCallId { get; set; }
+ public required string ToolCallId { get; set; }
[JsonPropertyName("toolName")]
- public string ToolName { get; set; }
+ public required string ToolName { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("arguments")]
- public object Arguments { get; set; }
+ public object? Arguments { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("parentToolCallId")]
- public string ParentToolCallId { get; set; }
+ public string? ParentToolCallId { get; set; }
}
public partial class ToolExecutionPartialResultData
{
[JsonPropertyName("toolCallId")]
- public string ToolCallId { get; set; }
+ public required string ToolCallId { get; set; }
[JsonPropertyName("partialOutput")]
- public string PartialOutput { get; set; }
+ public required string PartialOutput { get; set; }
}
public partial class ToolExecutionCompleteData
{
[JsonPropertyName("toolCallId")]
- public string ToolCallId { get; set; }
+ public required string ToolCallId { get; set; }
[JsonPropertyName("success")]
- public bool Success { get; set; }
+ public required bool Success { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("isUserRequested")]
@@ -872,88 +872,88 @@ public partial class ToolExecutionCompleteData
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("toolTelemetry")]
- public Dictionary ToolTelemetry { get; set; }
+ public Dictionary? ToolTelemetry { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("parentToolCallId")]
- public string ParentToolCallId { get; set; }
+ public string? ParentToolCallId { get; set; }
}
public partial class SubagentStartedData
{
[JsonPropertyName("toolCallId")]
- public string ToolCallId { get; set; }
+ public required string ToolCallId { get; set; }
[JsonPropertyName("agentName")]
- public string AgentName { get; set; }
+ public required string AgentName { get; set; }
[JsonPropertyName("agentDisplayName")]
- public string AgentDisplayName { get; set; }
+ public required string AgentDisplayName { get; set; }
[JsonPropertyName("agentDescription")]
- public string AgentDescription { get; set; }
+ public required string AgentDescription { get; set; }
}
public partial class SubagentCompletedData
{
[JsonPropertyName("toolCallId")]
- public string ToolCallId { get; set; }
+ public required string ToolCallId { get; set; }
[JsonPropertyName("agentName")]
- public string AgentName { get; set; }
+ public required string AgentName { get; set; }
}
public partial class SubagentFailedData
{
[JsonPropertyName("toolCallId")]
- public string ToolCallId { get; set; }
+ public required string ToolCallId { get; set; }
[JsonPropertyName("agentName")]
- public string AgentName { get; set; }
+ public required string AgentName { get; set; }
[JsonPropertyName("error")]
- public string Error { get; set; }
+ public required string Error { get; set; }
}
public partial class SubagentSelectedData
{
[JsonPropertyName("agentName")]
- public string AgentName { get; set; }
+ public required string AgentName { get; set; }
[JsonPropertyName("agentDisplayName")]
- public string AgentDisplayName { get; set; }
+ public required string AgentDisplayName { get; set; }
[JsonPropertyName("tools")]
- public string[] Tools { get; set; }
+ public string[]? Tools { get; set; }
}
public partial class HookStartData
{
[JsonPropertyName("hookInvocationId")]
- public string HookInvocationId { get; set; }
+ public required string HookInvocationId { get; set; }
[JsonPropertyName("hookType")]
- public string HookType { get; set; }
+ public required string HookType { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("input")]
- public object Input { get; set; }
+ public object? Input { get; set; }
}
public partial class HookEndData
{
[JsonPropertyName("hookInvocationId")]
- public string HookInvocationId { get; set; }
+ public required string HookInvocationId { get; set; }
[JsonPropertyName("hookType")]
- public string HookType { get; set; }
+ public required string HookType { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("output")]
- public object Output { get; set; }
+ public object? Output { get; set; }
[JsonPropertyName("success")]
- public bool Success { get; set; }
+ public required bool Success { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("error")]
@@ -963,14 +963,14 @@ public partial class HookEndData
public partial class SystemMessageData
{
[JsonPropertyName("content")]
- public string Content { get; set; }
+ public required string Content { get; set; }
[JsonPropertyName("role")]
- public SystemMessageDataRole Role { get; set; }
+ public required SystemMessageDataRole Role { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("name")]
- public string Name { get; set; }
+ public string? Name { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("metadata")]
@@ -980,87 +980,87 @@ public partial class SystemMessageData
public partial class SessionStartDataContext
{
[JsonPropertyName("cwd")]
- public string Cwd { get; set; }
+ public required string Cwd { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("gitRoot")]
- public string GitRoot { get; set; }
+ public string? GitRoot { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("repository")]
- public string Repository { get; set; }
+ public string? Repository { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("branch")]
- public string Branch { get; set; }
+ public string? Branch { get; set; }
}
public partial class SessionResumeDataContext
{
[JsonPropertyName("cwd")]
- public string Cwd { get; set; }
+ public required string Cwd { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("gitRoot")]
- public string GitRoot { get; set; }
+ public string? GitRoot { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("repository")]
- public string Repository { get; set; }
+ public string? Repository { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("branch")]
- public string Branch { get; set; }
+ public string? Branch { get; set; }
}
public partial class SessionHandoffDataRepository
{
[JsonPropertyName("owner")]
- public string Owner { get; set; }
+ public required string Owner { get; set; }
[JsonPropertyName("name")]
- public string Name { get; set; }
+ public required string Name { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("branch")]
- public string Branch { get; set; }
+ public string? Branch { get; set; }
}
public partial class SessionCompactionCompleteDataCompactionTokensUsed
{
[JsonPropertyName("input")]
- public double Input { get; set; }
+ public required double Input { get; set; }
[JsonPropertyName("output")]
- public double Output { get; set; }
+ public required double Output { get; set; }
[JsonPropertyName("cachedInput")]
- public double CachedInput { get; set; }
+ public required double CachedInput { get; set; }
}
public partial class UserMessageDataAttachmentsItem
{
[JsonPropertyName("type")]
- public UserMessageDataAttachmentsItemType Type { get; set; }
+ public required UserMessageDataAttachmentsItemType Type { get; set; }
[JsonPropertyName("path")]
- public string Path { get; set; }
+ public required string Path { get; set; }
[JsonPropertyName("displayName")]
- public string DisplayName { get; set; }
+ public required string DisplayName { get; set; }
}
public partial class AssistantMessageDataToolRequestsItem
{
[JsonPropertyName("toolCallId")]
- public string ToolCallId { get; set; }
+ public required string ToolCallId { get; set; }
[JsonPropertyName("name")]
- public string Name { get; set; }
+ public required string Name { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("arguments")]
- public object Arguments { get; set; }
+ public object? Arguments { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("type")]
@@ -1070,86 +1070,86 @@ public partial class AssistantMessageDataToolRequestsItem
public partial class ToolExecutionCompleteDataResult
{
[JsonPropertyName("content")]
- public string Content { get; set; }
+ public required string Content { get; set; }
}
public partial class ToolExecutionCompleteDataError
{
[JsonPropertyName("message")]
- public string Message { get; set; }
+ public required string Message { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("code")]
- public string Code { get; set; }
+ public string? Code { get; set; }
}
public partial class HookEndDataError
{
[JsonPropertyName("message")]
- public string Message { get; set; }
+ public required string Message { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("stack")]
- public string Stack { get; set; }
+ public string? Stack { get; set; }
}
public partial class SystemMessageDataMetadata
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("promptVersion")]
- public string PromptVersion { get; set; }
+ public string? PromptVersion { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("variables")]
- public Dictionary Variables { get; set; }
+ public Dictionary? Variables { get; set; }
}
+ [JsonConverter(typeof(JsonStringEnumConverter))]
public enum SessionHandoffDataSourceType
{
+ [JsonStringEnumMemberName("remote")]
Remote,
+ [JsonStringEnumMemberName("local")]
Local,
}
+ [JsonConverter(typeof(JsonStringEnumConverter))]
public enum UserMessageDataAttachmentsItemType
{
+ [JsonStringEnumMemberName("file")]
File,
+ [JsonStringEnumMemberName("directory")]
Directory,
}
+ [JsonConverter(typeof(JsonStringEnumConverter))]
public enum AssistantMessageDataToolRequestsItemType
{
+ [JsonStringEnumMemberName("function")]
Function,
+ [JsonStringEnumMemberName("custom")]
Custom,
}
+ [JsonConverter(typeof(JsonStringEnumConverter))]
public enum SystemMessageDataRole
{
+ [JsonStringEnumMemberName("system")]
System,
+ [JsonStringEnumMemberName("developer")]
Developer,
}
internal static class SerializerOptions
{
///
- /// Default options with SessionEventConverter for polymorphic deserialization.
+ /// Default options for polymorphic deserialization.
///
public static readonly JsonSerializerOptions Default = new()
{
+ AllowOutOfOrderMetadataProperties = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
- };
-
- ///
- /// Options without SessionEventConverter, used internally by the converter to avoid recursion.
- ///
- internal static readonly JsonSerializerOptions WithoutConverter = new()
- {
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
}
-}
-
-#pragma warning restore CS8618
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs
index 210409f9..011cb99b 100644
--- a/dotnet/src/Session.cs
+++ b/dotnet/src/Session.cs
@@ -30,9 +30,9 @@ namespace GitHub.Copilot.SDK;
/// // Subscribe to events
/// using var subscription = session.On(evt =>
/// {
-/// if (evt.Type == "assistant.message")
+/// if (evt is AssistantMessageEvent assistantMessage)
/// {
-/// Console.WriteLine($"Assistant: {evt.Data?.Content}");
+/// Console.WriteLine($"Assistant: {assistantMessage.Data?.Content}");
/// }
/// });
///
@@ -149,18 +149,20 @@ public async Task SendAsync(MessageOptions options, CancellationToken ca
void Handler(SessionEvent evt)
{
- if (evt is AssistantMessageEvent assistantMessage)
+ switch (evt)
{
- lastAssistantMessage = assistantMessage;
- }
- else if (evt.Type == "session.idle")
- {
- tcs.TrySetResult(lastAssistantMessage);
- }
- else if (evt is SessionErrorEvent errorEvent)
- {
- var message = errorEvent.Data?.Message ?? "session error";
- tcs.TrySetException(new InvalidOperationException($"Session error: {message}"));
+ case AssistantMessageEvent assistantMessage:
+ lastAssistantMessage = assistantMessage;
+ break;
+
+ case SessionIdleEvent:
+ tcs.TrySetResult(lastAssistantMessage);
+ break;
+
+ case SessionErrorEvent errorEvent:
+ var message = errorEvent.Data?.Message ?? "session error";
+ tcs.TrySetException(new InvalidOperationException($"Session error: {message}"));
+ break;
}
}
@@ -194,12 +196,12 @@ void Handler(SessionEvent evt)
///
/// using var subscription = session.On(evt =>
/// {
- /// switch (evt.Type)
+ /// switch (evt)
/// {
- /// case "assistant.message":
+ /// case AssistantMessageEvent:
/// Console.WriteLine($"Assistant: {evt.Data?.Content}");
/// break;
- /// case "session.error":
+ /// case SessionErrorEvent:
/// Console.WriteLine($"Error: {evt.Data?.Message}");
/// break;
/// }
@@ -328,7 +330,7 @@ internal async Task HandlePermissionRequestAsync(JsonEl
/// var events = await session.GetMessagesAsync();
/// foreach (var evt in events)
/// {
- /// if (evt.Type == "assistant.message")
+ /// if (evt is AssistantMessageEvent)
/// {
/// Console.WriteLine($"Assistant: {evt.Data?.Content}");
/// }
diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs
index f109a931..91fcbcb2 100644
--- a/dotnet/src/Types.cs
+++ b/dotnet/src/Types.cs
@@ -8,11 +8,16 @@
namespace GitHub.Copilot.SDK;
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ConnectionState
{
+ [JsonStringEnumMemberName("disconnected")]
Disconnected,
+ [JsonStringEnumMemberName("connecting")]
Connecting,
+ [JsonStringEnumMemberName("connected")]
Connected,
+ [JsonStringEnumMemberName("error")]
Error
}
@@ -105,9 +110,12 @@ public class PermissionInvocation
public delegate Task PermissionHandler(PermissionRequest request, PermissionInvocation invocation);
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SystemMessageMode
{
+ [JsonStringEnumMemberName("append")]
Append,
+ [JsonStringEnumMemberName("replace")]
Replace
}
diff --git a/nodejs/scripts/generate-csharp-session-types.ts b/nodejs/scripts/generate-csharp-session-types.ts
index 46a4914f..24a0bf6e 100644
--- a/nodejs/scripts/generate-csharp-session-types.ts
+++ b/nodejs/scripts/generate-csharp-session-types.ts
@@ -98,7 +98,7 @@ function schemaTypeToCSharp(
if (type === "string") {
if (format === "uuid") return required ? "Guid" : "Guid?";
if (format === "date-time") return required ? "DateTimeOffset" : "DateTimeOffset?";
- return "string";
+ return required ? "string" : "string?";
}
if (type === "number" || type === "integer") {
return required ? "double" : "double?";
@@ -109,21 +109,21 @@ function schemaTypeToCSharp(
if (type === "array") {
const items = schema.items as JSONSchema7 | undefined;
const itemType = items ? schemaTypeToCSharp(items, true, knownTypes) : "object";
- return `${itemType}[]`;
+ return required ? `${itemType}[]` : `${itemType}[]?`;
}
if (type === "object") {
if (schema.additionalProperties) {
const valueSchema = schema.additionalProperties;
if (typeof valueSchema === "object") {
const valueType = schemaTypeToCSharp(valueSchema as JSONSchema7, true, knownTypes);
- return `Dictionary`;
+ return required ? `Dictionary` : `Dictionary?`;
}
- return "Dictionary";
+ return required ? "Dictionary" : "Dictionary?";
}
- return "object";
+ return required ? "object" : "object?";
}
- return "object";
+ return required ? "object" : "object?";
}
/**
@@ -167,13 +167,14 @@ function getOrCreateEnum(
const enumName = generateEnumName(parentClassName, propName);
generatedEnums.set(enumName, { enumName, values });
- // Generate the enum code
- // Use [JsonStringEnumConverter(JsonNamingPolicy.CamelCase)] to serialize PascalCase enum members to camelCase JSON values
+ // Generate the enum code with JsonConverter and JsonStringEnumMemberName attributes
const lines: string[] = [];
+ lines.push(` [JsonConverter(typeof(JsonStringEnumConverter<${enumName}>))]`);
lines.push(` public enum ${enumName}`);
lines.push(` {`);
for (const value of values) {
const memberName = toPascalCaseEnumMember(value);
+ lines.push(` [JsonStringEnumMemberName("${value}")]`);
lines.push(` ${memberName},`);
}
lines.push(` }`);
@@ -268,13 +269,16 @@ function generateDataClass(
enumOutput
);
+ const isNullableType = csharpType.endsWith("?");
if (!isRequired) {
lines.push(
`${indent} [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`
);
}
lines.push(`${indent} [JsonPropertyName("${propName}")]`);
- lines.push(`${indent} public ${csharpType} ${csharpName} { get; set; }`);
+
+ const requiredModifier = isRequired && !isNullableType ? "required " : "";
+ lines.push(`${indent} public ${requiredModifier}${csharpType} ${csharpName} { get; set; }`);
lines.push("");
}
@@ -328,7 +332,10 @@ function generateNestedClass(
);
}
lines.push(`${indent} [JsonPropertyName("${propName}")]`);
- lines.push(`${indent} public ${csharpType} ${csharpName} { get; set; }`);
+
+ const isNullableType = csharpType.endsWith("?");
+ const requiredModifier = isRequired && !isNullableType ? "required " : "";
+ lines.push(`${indent} public ${requiredModifier}${csharpType} ${csharpName} { get; set; }`);
lines.push("");
}
}
@@ -358,24 +365,27 @@ function resolvePropertyType(
): string {
// Handle anyOf - simplify to nullable of the non-null type or object
if (propSchema.anyOf) {
+ const hasNull = propSchema.anyOf.some(
+ (s) => typeof s === "object" && (s as JSONSchema7).type === "null"
+ );
const nonNullTypes = propSchema.anyOf.filter(
(s) => typeof s === "object" && (s as JSONSchema7).type !== "null"
);
if (nonNullTypes.length === 1) {
- // Simple nullable - recurse with the inner type
+ // Simple nullable - recurse with the inner type, marking as not required if null is an option
return resolvePropertyType(
nonNullTypes[0] as JSONSchema7,
parentClassName,
propName,
- false,
+ isRequired && !hasNull,
indent,
knownTypes,
nestedClasses,
enumOutput
);
}
- // Complex union - use object
- return "object";
+ // Complex union - use object, nullable if null is in the union or property is not required
+ return (hasNull || !isRequired) ? "object?" : "object";
}
// Handle enum types
@@ -420,7 +430,7 @@ function resolvePropertyType(
enumOutput
);
nestedClasses.set(itemClassName, nestedCode);
- return `${itemClassName}[]`;
+ return isRequired ? `${itemClassName}[]` : `${itemClassName}[]?`;
}
// Array of enums
@@ -431,7 +441,7 @@ function resolvePropertyType(
items.enum as string[],
enumOutput
);
- return `${enumName}[]`;
+ return isRequired ? `${enumName}[]` : `${enumName}[]?`;
}
// Simple array type
@@ -443,7 +453,7 @@ function resolvePropertyType(
propName,
enumOutput
);
- return `${itemType}[]`;
+ return isRequired ? `${itemType}[]` : `${itemType}[]?`;
}
// Default: use basic type mapping
@@ -489,78 +499,33 @@ export function generateCSharpSessionTypes(schema: JSONSchema7, generatedAt: str
//
#nullable enable
-#pragma warning disable CS8618
namespace GitHub.Copilot.SDK
{
using System;
using System.Collections.Generic;
using System.Text.Json;
- using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
`);
- // Generate the custom converter class
+ // Generate base class with JsonPolymorphic attributes
lines.push(`${indent}/// `);
lines.push(
- `${indent}/// Custom JSON converter for SessionEvent that handles discriminator appearing anywhere in JSON.`
+ `${indent}/// Base class for all session events with polymorphic JSON serialization.`
);
lines.push(`${indent}/// `);
- lines.push(`${indent}internal class SessionEventConverter : JsonConverter`);
- lines.push(`${indent}{`);
- lines.push(`${indent} private static readonly Dictionary TypeMap = new()`);
- lines.push(`${indent} {`);
- for (const variant of variants) {
- lines.push(`${indent} ["${variant.typeName}"] = typeof(${variant.className}),`);
- }
- lines.push(`${indent} };`);
- lines.push("");
- lines.push(
- `${indent} public override SessionEvent? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)`
- );
- lines.push(`${indent} {`);
- lines.push(
- `${indent} // Parse as JsonNode to find the discriminator regardless of property order`
- );
- lines.push(`${indent} var node = JsonNode.Parse(ref reader);`);
- lines.push(`${indent} if (node is not JsonObject obj)`);
- lines.push(`${indent} throw new JsonException("Expected JSON object");`);
- lines.push("");
- lines.push(`${indent} var typeProp = obj["type"]?.GetValue();`);
- lines.push(`${indent} if (string.IsNullOrEmpty(typeProp))`);
- lines.push(
- `${indent} throw new JsonException("Missing 'type' discriminator property");`
- );
- lines.push("");
- lines.push(`${indent} if (!TypeMap.TryGetValue(typeProp, out var targetType))`);
- lines.push(`${indent} throw new JsonException($"Unknown event type: {typeProp}");`);
- lines.push("");
- lines.push(
- `${indent} // Deserialize to the concrete type without using this converter (to avoid recursion)`
- );
- lines.push(
- `${indent} return (SessionEvent?)obj.Deserialize(targetType, SerializerOptions.WithoutConverter);`
- );
- lines.push(`${indent} }`);
- lines.push("");
- lines.push(
- `${indent} public override void Write(Utf8JsonWriter writer, SessionEvent value, JsonSerializerOptions options)`
- );
- lines.push(`${indent} {`);
+ lines.push(`${indent}[JsonPolymorphic(`);
+ lines.push(`${indent} TypeDiscriminatorPropertyName = "type", `);
lines.push(
- `${indent} JsonSerializer.Serialize(writer, value, value.GetType(), SerializerOptions.WithoutConverter);`
+ `${indent} UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization)]`
);
- lines.push(`${indent} }`);
- lines.push(`${indent}}`);
- lines.push("");
- // Generate base class (no longer needs JsonPolymorphic attributes since we use custom converter)
- lines.push(`${indent}/// `);
- lines.push(
- `${indent}/// Base class for all session events with polymorphic JSON serialization.`
- );
- lines.push(`${indent}/// `);
- lines.push(`${indent}[JsonConverter(typeof(SessionEventConverter))]`);
+ // Generate JsonDerivedType attributes for each variant (alphabetized)
+ for (const variant of [...variants].sort((a, b) => a.typeName.localeCompare(b.typeName))) {
+ lines.push(
+ `${indent}[JsonDerivedType(typeof(${variant.className}), "${variant.typeName}")]`
+ );
+ }
lines.push(`${indent}public abstract partial class SessionEvent`);
lines.push(`${indent}{`);
@@ -580,7 +545,7 @@ namespace GitHub.Copilot.SDK
lines.push(`${indent} /// `);
lines.push(`${indent} /// The event type discriminator.`);
lines.push(`${indent} /// `);
- lines.push(`${indent} [JsonPropertyName("type")]`);
+ lines.push(`${indent} [JsonIgnore]`);
lines.push(`${indent} public abstract string Type { get; }`);
lines.push("");
lines.push(`${indent} public static SessionEvent FromJson(string json) =>`);
@@ -602,10 +567,11 @@ namespace GitHub.Copilot.SDK
lines.push(`${indent}/// `);
lines.push(`${indent}public partial class ${variant.className} : SessionEvent`);
lines.push(`${indent}{`);
+ lines.push(`${indent} [JsonIgnore]`);
lines.push(`${indent} public override string Type => "${variant.typeName}";`);
lines.push("");
lines.push(`${indent} [JsonPropertyName("data")]`);
- lines.push(`${indent} public ${variant.dataClassName} Data { get; set; }`);
+ lines.push(`${indent} public required ${variant.dataClassName} Data { get; set; }`);
lines.push(`${indent}}`);
lines.push("");
}
@@ -632,40 +598,18 @@ namespace GitHub.Copilot.SDK
lines.push(`${indent}internal static class SerializerOptions`);
lines.push(`${indent}{`);
lines.push(`${indent} /// `);
- lines.push(
- `${indent} /// Default options with SessionEventConverter for polymorphic deserialization.`
- );
+ lines.push(`${indent} /// Default options for polymorphic deserialization.`);
lines.push(`${indent} /// `);
lines.push(`${indent} public static readonly JsonSerializerOptions Default = new()`);
lines.push(`${indent} {`);
+ lines.push(`${indent} AllowOutOfOrderMetadataProperties = true,`);
lines.push(`${indent} PropertyNamingPolicy = JsonNamingPolicy.CamelCase,`);
- lines.push(`${indent} DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,`);
- lines.push(
- `${indent} Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }`
- );
- lines.push(`${indent} };`);
- lines.push("");
- lines.push(`${indent} /// `);
- lines.push(
- `${indent} /// Options without SessionEventConverter, used internally by the converter to avoid recursion.`
- );
- lines.push(`${indent} /// `);
- lines.push(
- `${indent} internal static readonly JsonSerializerOptions WithoutConverter = new()`
- );
- lines.push(`${indent} {`);
- lines.push(`${indent} PropertyNamingPolicy = JsonNamingPolicy.CamelCase,`);
- lines.push(`${indent} DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,`);
- lines.push(
- `${indent} Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }`
- );
+ lines.push(`${indent} DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull`);
lines.push(`${indent} };`);
lines.push(`${indent}}`);
// Close namespace
lines.push(`}`);
- lines.push("");
- lines.push(`#pragma warning restore CS8618`);
return lines.join("\n");
}