Skip to content

Commit b14e5b6

Browse files
Add modelCapabilities override to all SDK languages
Regenerated RPC types and added modelCapabilities support to Python, Go, and C# SDKs to match the Node.js implementation. All languages now support: - modelCapabilities on createSession/create_session/CreateSession - modelCapabilities on setModel/set_model/SetModel/SetModelAsync - E2E tests verifying vision toggle via setModel changes image processing Also includes: - Regenerated codegen output with proper type names (no Purple/Fluffy) - C# codegen fix to respect JSON Schema 'title' for class naming - Fix Go E2E tests for DataContent type change in session events - Fix Python generated Union import in session events Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent ff6893a commit b14e5b6

38 files changed

+1611
-232
lines changed

dotnet/src/Client.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,8 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
506506
Commands: config.Commands?.Select(c => new CommandWireDefinition(c.Name, c.Description)).ToList(),
507507
RequestElicitation: config.OnElicitationRequest != null,
508508
Traceparent: traceparent,
509-
Tracestate: tracestate);
509+
Tracestate: tracestate,
510+
ModelCapabilities: config.ModelCapabilities);
510511

511512
var response = await InvokeRpcAsync<CreateSessionResponse>(
512513
connection.Rpc, "session.create", [request], cancellationToken);
@@ -626,7 +627,8 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
626627
Commands: config.Commands?.Select(c => new CommandWireDefinition(c.Name, c.Description)).ToList(),
627628
RequestElicitation: config.OnElicitationRequest != null,
628629
Traceparent: traceparent,
629-
Tracestate: tracestate);
630+
Tracestate: tracestate,
631+
ModelCapabilities: config.ModelCapabilities);
630632

631633
var response = await InvokeRpcAsync<ResumeSessionResponse>(
632634
connection.Rpc, "session.resume", [request], cancellationToken);
@@ -1605,7 +1607,8 @@ internal record CreateSessionRequest(
16051607
List<CommandWireDefinition>? Commands = null,
16061608
bool? RequestElicitation = null,
16071609
string? Traceparent = null,
1608-
string? Tracestate = null);
1610+
string? Tracestate = null,
1611+
ModelCapabilitiesOverride? ModelCapabilities = null);
16091612

16101613
internal record ToolDefinition(
16111614
string Name,
@@ -1656,7 +1659,8 @@ internal record ResumeSessionRequest(
16561659
List<CommandWireDefinition>? Commands = null,
16571660
bool? RequestElicitation = null,
16581661
string? Traceparent = null,
1659-
string? Tracestate = null);
1662+
string? Tracestate = null,
1663+
ModelCapabilitiesOverride? ModelCapabilities = null);
16601664

16611665
internal record ResumeSessionResponse(
16621666
string SessionId,
@@ -1797,6 +1801,7 @@ private static LogLevel MapLevel(TraceEventType eventType)
17971801
[JsonSerializable(typeof(ListSessionsResponse))]
17981802
[JsonSerializable(typeof(GetSessionMetadataRequest))]
17991803
[JsonSerializable(typeof(GetSessionMetadataResponse))]
1804+
[JsonSerializable(typeof(ModelCapabilitiesOverride))]
18001805
[JsonSerializable(typeof(PermissionRequestResult))]
18011806
[JsonSerializable(typeof(PermissionRequestResponseV2))]
18021807
[JsonSerializable(typeof(ProviderConfig))]

dotnet/src/Generated/Rpc.cs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ public class ModelCapabilitiesSupports
5555
public bool? ReasoningEffort { get; set; }
5656
}
5757

58+
/// <summary>Vision-specific limits.</summary>
59+
public class ModelCapabilitiesLimitsVision
60+
{
61+
/// <summary>MIME types the model accepts.</summary>
62+
[JsonPropertyName("supported_media_types")]
63+
public List<string> SupportedMediaTypes { get => field ??= []; set; }
64+
65+
/// <summary>Maximum number of images per prompt.</summary>
66+
[JsonPropertyName("max_prompt_images")]
67+
public double MaxPromptImages { get; set; }
68+
69+
/// <summary>Maximum image size in bytes.</summary>
70+
[JsonPropertyName("max_prompt_image_size")]
71+
public double MaxPromptImageSize { get; set; }
72+
}
73+
5874
/// <summary>Token limits for prompts, outputs, and context window.</summary>
5975
public class ModelCapabilitiesLimits
6076
{
@@ -69,6 +85,10 @@ public class ModelCapabilitiesLimits
6985
/// <summary>Maximum total context window size in tokens.</summary>
7086
[JsonPropertyName("max_context_window_tokens")]
7187
public double MaxContextWindowTokens { get; set; }
88+
89+
/// <summary>Vision-specific limits.</summary>
90+
[JsonPropertyName("vision")]
91+
public ModelCapabilitiesLimitsVision? Vision { get; set; }
7292
}
7393

7494
/// <summary>Model capabilities and limits.</summary>
@@ -299,6 +319,66 @@ public class SessionModelSwitchToResult
299319
public string? ModelId { get; set; }
300320
}
301321

322+
/// <summary>Feature flags indicating what the model supports.</summary>
323+
public class ModelCapabilitiesOverrideSupports
324+
{
325+
/// <summary>Gets or sets the <c>vision</c> value.</summary>
326+
[JsonPropertyName("vision")]
327+
public bool? Vision { get; set; }
328+
329+
/// <summary>Gets or sets the <c>reasoningEffort</c> value.</summary>
330+
[JsonPropertyName("reasoningEffort")]
331+
public bool? ReasoningEffort { get; set; }
332+
}
333+
334+
/// <summary>RPC data type for ModelCapabilitiesOverrideLimitsVision operations.</summary>
335+
public class ModelCapabilitiesOverrideLimitsVision
336+
{
337+
/// <summary>MIME types the model accepts.</summary>
338+
[JsonPropertyName("supported_media_types")]
339+
public List<string>? SupportedMediaTypes { get; set; }
340+
341+
/// <summary>Maximum number of images per prompt.</summary>
342+
[JsonPropertyName("max_prompt_images")]
343+
public double? MaxPromptImages { get; set; }
344+
345+
/// <summary>Maximum image size in bytes.</summary>
346+
[JsonPropertyName("max_prompt_image_size")]
347+
public double? MaxPromptImageSize { get; set; }
348+
}
349+
350+
/// <summary>Token limits for prompts, outputs, and context window.</summary>
351+
public class ModelCapabilitiesOverrideLimits
352+
{
353+
/// <summary>Gets or sets the <c>max_prompt_tokens</c> value.</summary>
354+
[JsonPropertyName("max_prompt_tokens")]
355+
public double? MaxPromptTokens { get; set; }
356+
357+
/// <summary>Gets or sets the <c>max_output_tokens</c> value.</summary>
358+
[JsonPropertyName("max_output_tokens")]
359+
public double? MaxOutputTokens { get; set; }
360+
361+
/// <summary>Maximum total context window size in tokens.</summary>
362+
[JsonPropertyName("max_context_window_tokens")]
363+
public double? MaxContextWindowTokens { get; set; }
364+
365+
/// <summary>Gets or sets the <c>vision</c> value.</summary>
366+
[JsonPropertyName("vision")]
367+
public ModelCapabilitiesOverrideLimitsVision? Vision { get; set; }
368+
}
369+
370+
/// <summary>Override individual model capabilities resolved by the runtime.</summary>
371+
public class ModelCapabilitiesOverride
372+
{
373+
/// <summary>Feature flags indicating what the model supports.</summary>
374+
[JsonPropertyName("supports")]
375+
public ModelCapabilitiesOverrideSupports? Supports { get; set; }
376+
377+
/// <summary>Token limits for prompts, outputs, and context window.</summary>
378+
[JsonPropertyName("limits")]
379+
public ModelCapabilitiesOverrideLimits? Limits { get; set; }
380+
}
381+
302382
/// <summary>RPC data type for SessionModelSwitchTo operations.</summary>
303383
internal class SessionModelSwitchToRequest
304384
{
@@ -313,6 +393,10 @@ internal class SessionModelSwitchToRequest
313393
/// <summary>Reasoning effort level to use for the model.</summary>
314394
[JsonPropertyName("reasoningEffort")]
315395
public string? ReasoningEffort { get; set; }
396+
397+
/// <summary>Override individual model capabilities resolved by the runtime.</summary>
398+
[JsonPropertyName("modelCapabilities")]
399+
public ModelCapabilitiesOverride? ModelCapabilities { get; set; }
316400
}
317401

318402
/// <summary>RPC data type for SessionModeGet operations.</summary>
@@ -1537,9 +1621,9 @@ public async Task<SessionModelGetCurrentResult> GetCurrentAsync(CancellationToke
15371621
}
15381622

15391623
/// <summary>Calls "session.model.switchTo".</summary>
1540-
public async Task<SessionModelSwitchToResult> SwitchToAsync(string modelId, string? reasoningEffort = null, CancellationToken cancellationToken = default)
1624+
public async Task<SessionModelSwitchToResult> SwitchToAsync(string modelId, string? reasoningEffort = null, ModelCapabilitiesOverride? modelCapabilities = null, CancellationToken cancellationToken = default)
15411625
{
1542-
var request = new SessionModelSwitchToRequest { SessionId = _sessionId, ModelId = modelId, ReasoningEffort = reasoningEffort };
1626+
var request = new SessionModelSwitchToRequest { SessionId = _sessionId, ModelId = modelId, ReasoningEffort = reasoningEffort, ModelCapabilities = modelCapabilities };
15431627
return await CopilotClient.InvokeRpcAsync<SessionModelSwitchToResult>(_rpc, "session.model.switchTo", [request], cancellationToken);
15441628
}
15451629
}
@@ -2003,6 +2087,11 @@ public async Task<SessionShellKillResult> KillAsync(string processId, SessionShe
20032087
[JsonSerializable(typeof(ModelBilling))]
20042088
[JsonSerializable(typeof(ModelCapabilities))]
20052089
[JsonSerializable(typeof(ModelCapabilitiesLimits))]
2090+
[JsonSerializable(typeof(ModelCapabilitiesLimitsVision))]
2091+
[JsonSerializable(typeof(ModelCapabilitiesOverride))]
2092+
[JsonSerializable(typeof(ModelCapabilitiesOverrideLimits))]
2093+
[JsonSerializable(typeof(ModelCapabilitiesOverrideLimitsVision))]
2094+
[JsonSerializable(typeof(ModelCapabilitiesOverrideSupports))]
20062095
[JsonSerializable(typeof(ModelCapabilitiesSupports))]
20072096
[JsonSerializable(typeof(ModelPolicy))]
20082097
[JsonSerializable(typeof(ModelsListResult))]

dotnet/src/Generated/SessionEvents.cs

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ public partial class UserInputRequestedEvent : SessionEvent
791791
public required UserInputRequestedData Data { get; set; }
792792
}
793793

794-
/// <summary>User input request completion notification signaling UI dismissal.</summary>
794+
/// <summary>User input request completion with the user's response.</summary>
795795
/// <remarks>Represents the <c>user_input.completed</c> event.</remarks>
796796
public partial class UserInputCompletedEvent : SessionEvent
797797
{
@@ -817,7 +817,7 @@ public partial class ElicitationRequestedEvent : SessionEvent
817817
public required ElicitationRequestedData Data { get; set; }
818818
}
819819

820-
/// <summary>Elicitation request completion notification signaling UI dismissal.</summary>
820+
/// <summary>Elicitation request completion with the user's response.</summary>
821821
/// <remarks>Represents the <c>elicitation.completed</c> event.</remarks>
822822
public partial class ElicitationCompletedEvent : SessionEvent
823823
{
@@ -986,7 +986,7 @@ public partial class ExitPlanModeRequestedEvent : SessionEvent
986986
public required ExitPlanModeRequestedData Data { get; set; }
987987
}
988988

989-
/// <summary>Plan mode exit completion notification signaling UI dismissal.</summary>
989+
/// <summary>Plan mode exit completion with the user's approval decision and optional feedback.</summary>
990990
/// <remarks>Represents the <c>exit_plan_mode.completed</c> event.</remarks>
991991
public partial class ExitPlanModeCompletedEvent : SessionEvent
992992
{
@@ -2313,12 +2313,22 @@ public partial class UserInputRequestedData
23132313
public string? ToolCallId { get; set; }
23142314
}
23152315

2316-
/// <summary>User input request completion notification signaling UI dismissal.</summary>
2316+
/// <summary>User input request completion with the user's response.</summary>
23172317
public partial class UserInputCompletedData
23182318
{
23192319
/// <summary>Request ID of the resolved user input request; clients should dismiss any UI for this request.</summary>
23202320
[JsonPropertyName("requestId")]
23212321
public required string RequestId { get; set; }
2322+
2323+
/// <summary>The user's answer to the input request.</summary>
2324+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2325+
[JsonPropertyName("answer")]
2326+
public string? Answer { get; set; }
2327+
2328+
/// <summary>Whether the answer was typed as free-form text rather than selected from choices.</summary>
2329+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2330+
[JsonPropertyName("wasFreeform")]
2331+
public bool? WasFreeform { get; set; }
23222332
}
23232333

23242334
/// <summary>Elicitation request; may be form-based (structured input) or URL-based (browser redirect).</summary>
@@ -2358,12 +2368,22 @@ public partial class ElicitationRequestedData
23582368
public string? Url { get; set; }
23592369
}
23602370

2361-
/// <summary>Elicitation request completion notification signaling UI dismissal.</summary>
2371+
/// <summary>Elicitation request completion with the user's response.</summary>
23622372
public partial class ElicitationCompletedData
23632373
{
23642374
/// <summary>Request ID of the resolved elicitation request; clients should dismiss any UI for this request.</summary>
23652375
[JsonPropertyName("requestId")]
23662376
public required string RequestId { get; set; }
2377+
2378+
/// <summary>The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed).</summary>
2379+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2380+
[JsonPropertyName("action")]
2381+
public ElicitationCompletedDataAction? Action { get; set; }
2382+
2383+
/// <summary>The submitted form data when action is 'accept'; keys match the requested schema fields.</summary>
2384+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2385+
[JsonPropertyName("content")]
2386+
public Dictionary<string, object>? Content { get; set; }
23672387
}
23682388

23692389
/// <summary>Sampling request from an MCP server; contains the server name and a requestId for correlation.</summary>
@@ -2543,12 +2563,32 @@ public partial class ExitPlanModeRequestedData
25432563
public required string RecommendedAction { get; set; }
25442564
}
25452565

2546-
/// <summary>Plan mode exit completion notification signaling UI dismissal.</summary>
2566+
/// <summary>Plan mode exit completion with the user's approval decision and optional feedback.</summary>
25472567
public partial class ExitPlanModeCompletedData
25482568
{
25492569
/// <summary>Request ID of the resolved exit plan mode request; clients should dismiss any UI for this request.</summary>
25502570
[JsonPropertyName("requestId")]
25512571
public required string RequestId { get; set; }
2572+
2573+
/// <summary>Whether the plan was approved by the user.</summary>
2574+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2575+
[JsonPropertyName("approved")]
2576+
public bool? Approved { get; set; }
2577+
2578+
/// <summary>Which action the user selected (e.g. 'autopilot', 'interactive', 'exit_only').</summary>
2579+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2580+
[JsonPropertyName("selectedAction")]
2581+
public string? SelectedAction { get; set; }
2582+
2583+
/// <summary>Whether edits should be auto-approved without confirmation.</summary>
2584+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2585+
[JsonPropertyName("autoApproveEdits")]
2586+
public bool? AutoApproveEdits { get; set; }
2587+
2588+
/// <summary>Free-form feedback from the user if they requested changes to the plan.</summary>
2589+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2590+
[JsonPropertyName("feedback")]
2591+
public string? Feedback { get; set; }
25522592
}
25532593

25542594
/// <summary>Event payload for <see cref="SessionToolsUpdatedEvent"/>.</summary>
@@ -4016,6 +4056,21 @@ public enum ElicitationRequestedDataMode
40164056
Url,
40174057
}
40184058

4059+
/// <summary>The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed).</summary>
4060+
[JsonConverter(typeof(JsonStringEnumConverter<ElicitationCompletedDataAction>))]
4061+
public enum ElicitationCompletedDataAction
4062+
{
4063+
/// <summary>The <c>accept</c> variant.</summary>
4064+
[JsonStringEnumMemberName("accept")]
4065+
Accept,
4066+
/// <summary>The <c>decline</c> variant.</summary>
4067+
[JsonStringEnumMemberName("decline")]
4068+
Decline,
4069+
/// <summary>The <c>cancel</c> variant.</summary>
4070+
[JsonStringEnumMemberName("cancel")]
4071+
Cancel,
4072+
}
4073+
40194074
/// <summary>Connection status: connected, failed, needs-auth, pending, disabled, or not_configured.</summary>
40204075
[JsonConverter(typeof(JsonStringEnumConverter<SessionMcpServersLoadedDataServersItemStatus>))]
40214076
public enum SessionMcpServersLoadedDataServersItemStatus

dotnet/src/Session.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,24 +1105,25 @@ await InvokeRpcAsync<object>(
11051105
/// </summary>
11061106
/// <param name="model">Model ID to switch to (e.g., "gpt-4.1").</param>
11071107
/// <param name="reasoningEffort">Reasoning effort level (e.g., "low", "medium", "high", "xhigh").</param>
1108+
/// <param name="modelCapabilities">Per-property overrides for model capabilities, deep-merged over runtime defaults.</param>
11081109
/// <param name="cancellationToken">Optional cancellation token.</param>
11091110
/// <example>
11101111
/// <code>
11111112
/// await session.SetModelAsync("gpt-4.1");
11121113
/// await session.SetModelAsync("claude-sonnet-4.6", "high");
11131114
/// </code>
11141115
/// </example>
1115-
public async Task SetModelAsync(string model, string? reasoningEffort, CancellationToken cancellationToken = default)
1116+
public async Task SetModelAsync(string model, string? reasoningEffort, ModelCapabilitiesOverride? modelCapabilities = null, CancellationToken cancellationToken = default)
11161117
{
1117-
await Rpc.Model.SwitchToAsync(model, reasoningEffort, cancellationToken);
1118+
await Rpc.Model.SwitchToAsync(model, reasoningEffort, modelCapabilities, cancellationToken);
11181119
}
11191120

11201121
/// <summary>
11211122
/// Changes the model for this session.
11221123
/// </summary>
11231124
public Task SetModelAsync(string model, CancellationToken cancellationToken = default)
11241125
{
1125-
return SetModelAsync(model, reasoningEffort: null, cancellationToken);
1126+
return SetModelAsync(model, reasoningEffort: null, modelCapabilities: null, cancellationToken);
11261127
}
11271128

11281129
/// <summary>

dotnet/src/Types.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,7 @@ protected SessionConfig(SessionConfig? other)
15791579
? new Dictionary<string, object>(other.McpServers, other.McpServers.Comparer)
15801580
: null;
15811581
Model = other.Model;
1582+
ModelCapabilities = other.ModelCapabilities;
15821583
OnElicitationRequest = other.OnElicitationRequest;
15831584
OnEvent = other.OnEvent;
15841585
OnPermissionRequest = other.OnPermissionRequest;
@@ -1616,6 +1617,11 @@ protected SessionConfig(SessionConfig? other)
16161617
/// </summary>
16171618
public string? ReasoningEffort { get; set; }
16181619

1620+
/// <summary>
1621+
/// Per-property overrides for model capabilities, deep-merged over runtime defaults.
1622+
/// </summary>
1623+
public ModelCapabilitiesOverride? ModelCapabilities { get; set; }
1624+
16191625
/// <summary>
16201626
/// Override the default configuration directory location.
16211627
/// When specified, the session will use this directory for storing config and state.
@@ -1780,6 +1786,7 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)
17801786
? new Dictionary<string, object>(other.McpServers, other.McpServers.Comparer)
17811787
: null;
17821788
Model = other.Model;
1789+
ModelCapabilities = other.ModelCapabilities;
17831790
OnElicitationRequest = other.OnElicitationRequest;
17841791
OnEvent = other.OnEvent;
17851792
OnPermissionRequest = other.OnPermissionRequest;
@@ -1837,6 +1844,11 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)
18371844
/// </summary>
18381845
public string? ReasoningEffort { get; set; }
18391846

1847+
/// <summary>
1848+
/// Per-property overrides for model capabilities, deep-merged over runtime defaults.
1849+
/// </summary>
1850+
public ModelCapabilitiesOverride? ModelCapabilities { get; set; }
1851+
18401852
/// <summary>
18411853
/// Handler for permission requests from the server.
18421854
/// When provided, the server will call this handler to request permission for operations.
@@ -2439,6 +2451,7 @@ public class SystemMessageTransformRpcResponse
24392451
[JsonSerializable(typeof(MessageOptions))]
24402452
[JsonSerializable(typeof(ModelBilling))]
24412453
[JsonSerializable(typeof(ModelCapabilities))]
2454+
[JsonSerializable(typeof(ModelCapabilitiesOverride))]
24422455
[JsonSerializable(typeof(ModelInfo))]
24432456
[JsonSerializable(typeof(ModelLimits))]
24442457
[JsonSerializable(typeof(ModelPolicy))]

0 commit comments

Comments
 (0)