Skip to content

Commit 6ffb9e1

Browse files
stephentoubCopilot
andauthored
Config parity across SDKs: add largeOutput, pluginDirectories, spell out Directory (#1482)
* Config parity across SDKs: add largeOutput, pluginDirectories, spell out directory Bring the six language SDKs (.NET, Node, Python, Go, Java, Rust) into closer alignment on session-configuration surface area: - Expose `largeOutput` / `LargeToolOutputConfig` on session create and resume in every SDK so callers can configure the CLI's large tool-output handling (enabled, maxSizeBytes, outputDirectory). Wire field stays `largeOutput.outputDir` for compatibility with the runtime. - Expose `pluginDirectories` on session create and resume in every SDK, documented as an explicit opt-in that loads plugin agents and rules even when `enableConfigDiscovery` is false. Defaults to null/none, which is the safe choice for multi-tenant hosts. - Spell out `Dir` -> `Directory` in public APIs for consistency with the rest of Types.cs (e.g. SkillDirectories, InstructionDirectories): `configDir` -> `configDirectory`, `outputDir` -> `outputDirectory`. The wire JSON names (`configDir`, `outputDir`) are preserved via language-specific serializer annotations, so this is a source-only rename. - Node SDK also re-exports `ReasoningSummary` and adds `reasoningSummary` support on resume, matching what the other SDKs already had. Notes for reviewers: - `args`/`toolArgs`/`modifiedArgs` are intentionally left abbreviated, matching the wire field names and the shell/process/CLI argument idiom used elsewhere (RuntimeConnection, MCP stdio, hook inputs). - New unit tests cover serialization (wire field stays `outputDir` / `configDir`), clone semantics for the new fields, and round-trips through CreateSessionRequest / ResumeSessionConfig in every SDK. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix CI: formatters and linters across SDKs - python: ruff isort fix (separate aliased import) - nodejs: prettier format on test/client.test.ts - go: gofmt alignment in client_test.go and session_e2e_test.go - rust: clippy field_reassign_with_default fix in types.rs test - java: spotless:apply across 10 files (line wrap, javadoc multi-line) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Go: type MaxSizeBytes as *int64 to match other SDKs Reviewer flagged that `*int` is architecture-dependent and narrower than the u64/long/Long used in the other SDKs. Switch to `*int64` (matches Java `Long` and .NET `long?`) and update the test literal cast. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4e3b8e2 commit 6ffb9e1

38 files changed

Lines changed: 1407 additions & 100 deletions

dotnet/src/Client.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,7 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
852852
localSessionId,
853853
config.ClientName,
854854
config.ReasoningEffort,
855+
config.ReasoningSummary,
855856
config.Tools?.Select(ToolDefinition.FromAIFunction).ToList(),
856857
wireSystemMessage,
857858
toolFilter.AvailableTools,
@@ -871,7 +872,7 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
871872
config.CustomAgents,
872873
config.DefaultAgent,
873874
config.Agent,
874-
config.ConfigDir,
875+
config.ConfigDirectory,
875876
config.EnableConfigDiscovery,
876877
config.SkillDirectories,
877878
config.DisabledSkills,
@@ -886,6 +887,8 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
886887
RemoteSession: config.RemoteSession,
887888
Cloud: config.Cloud,
888889
InstructionDirectories: config.InstructionDirectories,
890+
PluginDirectories: config.PluginDirectories,
891+
LargeOutput: config.LargeOutput,
889892
Canvases: config.Canvases,
890893
RequestCanvasRenderer: config.RequestCanvasRenderer,
891894
RequestExtensions: config.RequestExtensions,
@@ -1033,6 +1036,7 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
10331036
config.ClientName,
10341037
config.Model,
10351038
config.ReasoningEffort,
1039+
config.ReasoningSummary,
10361040
config.Tools?.Select(ToolDefinition.FromAIFunction).ToList(),
10371041
wireSystemMessage,
10381042
toolFilter.AvailableTools,
@@ -1045,7 +1049,7 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
10451049
config.OnAutoModeSwitchRequest != null ? true : null,
10461050
hasHooks ? true : null,
10471051
config.WorkingDirectory,
1048-
config.ConfigDir,
1052+
config.ConfigDirectory,
10491053
config.EnableConfigDiscovery,
10501054
config.SuppressResumeEvent is true ? true : null,
10511055
config.Streaming is true ? true : null,
@@ -1068,6 +1072,8 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
10681072
RemoteSession: config.RemoteSession,
10691073
ContinuePendingWork: config.ContinuePendingWork,
10701074
InstructionDirectories: config.InstructionDirectories,
1075+
PluginDirectories: config.PluginDirectories,
1076+
LargeOutput: config.LargeOutput,
10711077
Canvases: config.Canvases,
10721078
RequestCanvasRenderer: config.RequestCanvasRenderer,
10731079
RequestExtensions: config.RequestExtensions,
@@ -2147,6 +2153,7 @@ internal record CreateSessionRequest(
21472153
string? SessionId,
21482154
string? ClientName,
21492155
string? ReasoningEffort,
2156+
ReasoningSummary? ReasoningSummary,
21502157
IList<ToolDefinition>? Tools,
21512158
SystemMessageConfig? SystemMessage,
21522159
IList<string>? AvailableTools,
@@ -2166,7 +2173,7 @@ internal record CreateSessionRequest(
21662173
IList<CustomAgentConfig>? CustomAgents,
21672174
DefaultAgentConfig? DefaultAgent,
21682175
string? Agent,
2169-
string? ConfigDir,
2176+
[property: JsonPropertyName("configDir")] string? ConfigDirectory,
21702177
bool? EnableConfigDiscovery,
21712178
IList<string>? SkillDirectories,
21722179
IList<string>? DisabledSkills,
@@ -2181,6 +2188,8 @@ internal record CreateSessionRequest(
21812188
RemoteSessionMode? RemoteSession = null,
21822189
CloudSessionOptions? Cloud = null,
21832190
IList<string>? InstructionDirectories = null,
2191+
IList<string>? PluginDirectories = null,
2192+
LargeToolOutputConfig? LargeOutput = null,
21842193
#pragma warning disable GHCP001
21852194
IList<CanvasDeclaration>? Canvases = null,
21862195
bool? RequestCanvasRenderer = null,
@@ -2219,6 +2228,7 @@ internal record ResumeSessionRequest(
22192228
string? ClientName,
22202229
string? Model,
22212230
string? ReasoningEffort,
2231+
ReasoningSummary? ReasoningSummary,
22222232
IList<ToolDefinition>? Tools,
22232233
SystemMessageConfig? SystemMessage,
22242234
IList<string>? AvailableTools,
@@ -2231,7 +2241,7 @@ internal record ResumeSessionRequest(
22312241
bool? RequestAutoModeSwitch,
22322242
bool? Hooks,
22332243
string? WorkingDirectory,
2234-
string? ConfigDir,
2244+
[property: JsonPropertyName("configDir")] string? ConfigDirectory,
22352245
bool? EnableConfigDiscovery,
22362246
bool? SuppressResumeEvent,
22372247
bool? Streaming,
@@ -2254,6 +2264,8 @@ internal record ResumeSessionRequest(
22542264
RemoteSessionMode? RemoteSession = null,
22552265
bool? ContinuePendingWork = null,
22562266
IList<string>? InstructionDirectories = null,
2267+
IList<string>? PluginDirectories = null,
2268+
LargeToolOutputConfig? LargeOutput = null,
22572269
#pragma warning disable GHCP001
22582270
IList<CanvasDeclaration>? Canvases = null,
22592271
bool? RequestCanvasRenderer = null,

dotnet/src/Types.cs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,6 +2303,37 @@ public sealed class InfiniteSessionConfig
23032303
public double? BufferExhaustionThreshold { get; set; }
23042304
}
23052305

2306+
/// <summary>
2307+
/// Configuration for handling large tool outputs.
2308+
/// </summary>
2309+
/// <remarks>
2310+
/// When a tool produces output exceeding the configured size, the output is
2311+
/// written to a temp file and a reference is returned to the model instead of
2312+
/// returning to it the full payload.
2313+
/// </remarks>
2314+
public sealed class LargeToolOutputConfig
2315+
{
2316+
/// <summary>
2317+
/// Whether large output handling is enabled.
2318+
/// </summary>
2319+
/// <remarks>The default value is <see langword="true"/>.</remarks>
2320+
[JsonPropertyName("enabled")]
2321+
public bool? Enabled { get; set; }
2322+
2323+
/// <summary>
2324+
/// Maximum size in bytes before output is written to a temp file.
2325+
/// </summary>
2326+
[JsonPropertyName("maxSizeBytes")]
2327+
public long? MaxSizeBytes { get; set; }
2328+
2329+
/// <summary>
2330+
/// Directory to write temp files to.
2331+
/// </summary>
2332+
/// <remarks>The default value is the OS temp directory.</remarks>
2333+
[JsonPropertyName("outputDir")]
2334+
public string? OutputDirectory { get; set; }
2335+
}
2336+
23062337
/// <summary>
23072338
/// GitHub repository metadata to associate with a cloud session.
23082339
/// </summary>
@@ -2350,7 +2381,7 @@ protected SessionConfigBase(SessionConfigBase? other)
23502381
AvailableTools = other.AvailableTools is not null ? [.. other.AvailableTools] : null;
23512382
ClientName = other.ClientName;
23522383
Commands = other.Commands is not null ? [.. other.Commands] : null;
2353-
ConfigDir = other.ConfigDir;
2384+
ConfigDirectory = other.ConfigDirectory;
23542385
CustomAgents = other.CustomAgents is not null ? [.. other.CustomAgents] : null;
23552386
DefaultAgent = other.DefaultAgent;
23562387
Agent = other.Agent;
@@ -2360,6 +2391,7 @@ protected SessionConfigBase(SessionConfigBase? other)
23602391
ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null;
23612392
Hooks = other.Hooks;
23622393
InfiniteSessions = other.InfiniteSessions;
2394+
LargeOutput = other.LargeOutput;
23632395
McpServers = other.McpServers is not null
23642396
? (other.McpServers is Dictionary<string, McpServerConfig> dict
23652397
? new Dictionary<string, McpServerConfig>(dict, dict.Comparer)
@@ -2380,6 +2412,7 @@ protected SessionConfigBase(SessionConfigBase? other)
23802412
CoauthorEnabled = other.CoauthorEnabled;
23812413
ManageScheduleEnabled = other.ManageScheduleEnabled;
23822414
ReasoningEffort = other.ReasoningEffort;
2415+
ReasoningSummary = other.ReasoningSummary;
23832416
CreateSessionFsProvider = other.CreateSessionFsProvider;
23842417
GitHubToken = other.GitHubToken;
23852418
RemoteSession = other.RemoteSession;
@@ -2391,6 +2424,7 @@ protected SessionConfigBase(SessionConfigBase? other)
23912424
CanvasHandler = other.CanvasHandler;
23922425
#pragma warning restore GHCP001
23932426
SkillDirectories = other.SkillDirectories is not null ? [.. other.SkillDirectories] : null;
2427+
PluginDirectories = other.PluginDirectories is not null ? [.. other.PluginDirectories] : null;
23942428
InstructionDirectories = other.InstructionDirectories is not null ? [.. other.InstructionDirectories] : null;
23952429
Streaming = other.Streaming;
23962430
IncludeSubAgentStreamingEvents = other.IncludeSubAgentStreamingEvents;
@@ -2412,14 +2446,22 @@ protected SessionConfigBase(SessionConfigBase? other)
24122446
/// </summary>
24132447
public string? ReasoningEffort { get; set; }
24142448

2449+
/// <summary>
2450+
/// Reasoning summary mode for models that support configurable reasoning summaries.
2451+
/// </summary>
2452+
/// <remarks>
2453+
/// Use <see cref="ReasoningSummary.None"/> to suppress summary output regardless of whether reasoning is enabled.
2454+
/// </remarks>
2455+
public ReasoningSummary? ReasoningSummary { get; set; }
2456+
24152457
/// <summary>Per-property overrides for model capabilities, deep-merged over runtime defaults.</summary>
24162458
public ModelCapabilitiesOverride? ModelCapabilities { get; set; }
24172459

24182460
/// <summary>
24192461
/// Override the default configuration directory location.
24202462
/// When specified, the session will use this directory for storing config and state.
24212463
/// </summary>
2422-
public string? ConfigDir { get; set; }
2464+
public string? ConfigDirectory { get; set; }
24232465

24242466
/// <summary>
24252467
/// When <see langword="true"/>, automatically discovers MCP server configurations
@@ -2593,6 +2635,17 @@ protected SessionConfigBase(SessionConfigBase? other)
25932635
/// <summary>Directories to load skills from.</summary>
25942636
public IList<string>? SkillDirectories { get; set; }
25952637

2638+
/// <summary>
2639+
/// Local filesystem paths to Open Plugins-format directories
2640+
/// (https://open-plugins.com/) to load for this session.
2641+
/// </summary>
2642+
/// <remarks>
2643+
/// Relative paths resolve against <see cref="WorkingDirectory"/> (or the
2644+
/// runtime cwd if unset). Treated as an explicit opt-in: plugin agents
2645+
/// and rules load even when <see cref="EnableConfigDiscovery"/> is false.
2646+
/// </remarks>
2647+
public IList<string>? PluginDirectories { get; set; }
2648+
25962649
/// <summary>Additional directories to search for custom instruction files.</summary>
25972650
public IList<string>? InstructionDirectories { get; set; }
25982651

@@ -2605,6 +2658,14 @@ protected SessionConfigBase(SessionConfigBase? other)
26052658
/// </summary>
26062659
public InfiniteSessionConfig? InfiniteSessions { get; set; }
26072660

2661+
/// <summary>
2662+
/// Configuration for handling large tool outputs. When a tool produces
2663+
/// output exceeding the configured size, the output is written to a temp
2664+
/// file and a reference is returned to the model instead of the full
2665+
/// payload.
2666+
/// </summary>
2667+
public LargeToolOutputConfig? LargeOutput { get; set; }
2668+
26082669
/// <summary>
26092670
/// Optional event handler registered on the session before the session.create / session.resume
26102671
/// RPC is issued, ensuring early events are delivered.

dotnet/test/E2E/ClientE2ETests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*---------------------------------------------------------------------------------------------
1+
/*---------------------------------------------------------------------------------------------
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
*--------------------------------------------------------------------------------------------*/
44

dotnet/test/E2E/ClientOptionsE2ETests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*---------------------------------------------------------------------------------------------
1+
/*---------------------------------------------------------------------------------------------
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
*--------------------------------------------------------------------------------------------*/
44

dotnet/test/E2E/SessionE2ETests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ public async Task SendAndWait_Throws_OperationCanceledException_When_Token_Cance
550550
public async Task Should_Create_Session_With_Custom_Config_Dir()
551551
{
552552
var customConfigDir = Path.Join(Ctx.HomeDir, "custom-config");
553-
var session = await CreateSessionAsync(new SessionConfig { ConfigDir = customConfigDir });
553+
var session = await CreateSessionAsync(new SessionConfig { ConfigDirectory = customConfigDir });
554554

555555
Assert.Matches(@"^[a-f0-9-]+$", session.SessionId);
556556

dotnet/test/Unit/CloneTests.cs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*---------------------------------------------------------------------------------------------
1+
/*---------------------------------------------------------------------------------------------
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
*--------------------------------------------------------------------------------------------*/
44

@@ -68,7 +68,8 @@ public void SessionConfig_Clone_CopiesAllProperties()
6868
ClientName = "my-app",
6969
Model = "gpt-4",
7070
ReasoningEffort = "high",
71-
ConfigDir = "/config",
71+
ReasoningSummary = ReasoningSummary.Detailed,
72+
ConfigDirectory = "/config",
7273
AvailableTools = ["tool1", "tool2"],
7374
ExcludedTools = ["tool3"],
7475
WorkingDirectory = "/workspace",
@@ -91,6 +92,8 @@ public void SessionConfig_Clone_CopiesAllProperties()
9192
SkillDirectories = ["/skills"],
9293
InstructionDirectories = ["/instructions"],
9394
DisabledSkills = ["skill1"],
95+
PluginDirectories = ["/plugins"],
96+
LargeOutput = new LargeToolOutputConfig { Enabled = true, MaxSizeBytes = 2048, OutputDirectory = "/tmp/out" },
9497
OnExitPlanModeRequest = static (_, _) => Task.FromResult(new ExitPlanModeResult()),
9598
OnAutoModeSwitchRequest = static (_, _) => Task.FromResult(AutoModeSwitchResponse.No),
9699
};
@@ -101,7 +104,8 @@ public void SessionConfig_Clone_CopiesAllProperties()
101104
Assert.Equal(original.ClientName, clone.ClientName);
102105
Assert.Equal(original.Model, clone.Model);
103106
Assert.Equal(original.ReasoningEffort, clone.ReasoningEffort);
104-
Assert.Equal(original.ConfigDir, clone.ConfigDir);
107+
Assert.Equal(original.ReasoningSummary, clone.ReasoningSummary);
108+
Assert.Equal(original.ConfigDirectory, clone.ConfigDirectory);
105109
Assert.Equal(original.AvailableTools, clone.AvailableTools);
106110
Assert.Equal(original.ExcludedTools, clone.ExcludedTools);
107111
Assert.Equal(original.WorkingDirectory, clone.WorkingDirectory);
@@ -117,6 +121,8 @@ public void SessionConfig_Clone_CopiesAllProperties()
117121
Assert.Equal(original.SkillDirectories, clone.SkillDirectories);
118122
Assert.Equal(original.InstructionDirectories, clone.InstructionDirectories);
119123
Assert.Equal(original.DisabledSkills, clone.DisabledSkills);
124+
Assert.Equal(original.PluginDirectories, clone.PluginDirectories);
125+
Assert.Same(original.LargeOutput, clone.LargeOutput);
120126
Assert.Same(original.OnExitPlanModeRequest, clone.OnExitPlanModeRequest);
121127
Assert.Same(original.OnAutoModeSwitchRequest, clone.OnAutoModeSwitchRequest);
122128
}
@@ -356,6 +362,35 @@ public void ResumeSessionConfig_Clone_CopiesContinuePendingWork()
356362
Assert.True(clone.ContinuePendingWork);
357363
}
358364

365+
[Fact]
366+
public void ResumeSessionConfig_Clone_CopiesReasoningSummary()
367+
{
368+
var original = new ResumeSessionConfig
369+
{
370+
ReasoningSummary = ReasoningSummary.None,
371+
};
372+
373+
var clone = original.Clone();
374+
375+
Assert.Equal(original.ReasoningSummary, clone.ReasoningSummary);
376+
}
377+
378+
[Fact]
379+
public void ResumeSessionConfig_Clone_CopiesPluginDirectoriesAndLargeOutput()
380+
{
381+
var largeOutput = new LargeToolOutputConfig { Enabled = false, MaxSizeBytes = 4096, OutputDirectory = "/tmp/resume" };
382+
var original = new ResumeSessionConfig
383+
{
384+
PluginDirectories = ["/resume/plugins"],
385+
LargeOutput = largeOutput,
386+
};
387+
388+
var clone = original.Clone();
389+
390+
Assert.Equal(original.PluginDirectories, clone.PluginDirectories);
391+
Assert.Same(original.LargeOutput, clone.LargeOutput);
392+
}
393+
359394
[Fact]
360395
public void ResumeSessionConfig_Clone_PreservesContinuePendingWorkDefault()
361396
{

0 commit comments

Comments
 (0)