Skip to content

Commit 03e0611

Browse files
committed
Add copy constructors and Clone methods to the various .NET config option bags
Consuming libraries like Agent Framework sometimes have a need to clone the various options bags, e.g. their caller passes in options and that middle library needs to tweak the settings before passing them along (e.g. set Streaming to true or false) but it doesn't want to mutate the caller's object. Without clone methods, such libraries need to manually copy every property, which then means when new properties are added, they get ignored and options are lost. This PR adds such public Clone methods, and accomodates the non-sealed nature of the types by adding protected copy constructors that the virtual Clone methods use. (If instead we want to seal these types, that'd be viable as well.)
1 parent 4dc5629 commit 03e0611

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

dotnet/src/Types.cs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,34 @@ public enum ConnectionState
2424

2525
public class CopilotClientOptions
2626
{
27+
/// <summary>
28+
/// Initializes a new instance of the <see cref="CopilotClientOptions"/> class.
29+
/// </summary>
30+
public CopilotClientOptions() { }
31+
32+
/// <summary>
33+
/// Initializes a new instance of the <see cref="CopilotClientOptions"/> class
34+
/// by copying the properties of the specified instance.
35+
/// </summary>
36+
protected CopilotClientOptions(CopilotClientOptions? other)
37+
{
38+
if (other is null) return;
39+
40+
AutoRestart = other.AutoRestart;
41+
AutoStart = other.AutoStart;
42+
CliArgs = (string[]?)other.CliArgs?.Clone();
43+
CliPath = other.CliPath;
44+
CliUrl = other.CliUrl;
45+
Cwd = other.Cwd;
46+
Environment = other.Environment is not null ? new Dictionary<string, string>(other.Environment) : null;
47+
GithubToken = other.GithubToken;
48+
Logger = other.Logger;
49+
LogLevel = other.LogLevel;
50+
Port = other.Port;
51+
UseLoggedInUser = other.UseLoggedInUser;
52+
UseStdio = other.UseStdio;
53+
}
54+
2755
/// <summary>
2856
/// Path to the Copilot CLI executable. If not specified, uses the bundled CLI from the SDK.
2957
/// </summary>
@@ -53,6 +81,15 @@ public class CopilotClientOptions
5381
/// Default: true (but defaults to false when GithubToken is provided).
5482
/// </summary>
5583
public bool? UseLoggedInUser { get; set; }
84+
85+
/// <summary>
86+
/// Creates a shallow clone of this <see cref="CopilotClientOptions"/> instance.
87+
/// </summary>
88+
/// <remarks>
89+
/// Collection properties are copied into new collections so that modifications
90+
/// to the clone do not affect the original.
91+
/// </remarks>
92+
public virtual CopilotClientOptions Clone() => new(this);
5693
}
5794

5895
public class ToolBinaryResult
@@ -692,6 +729,40 @@ public class InfiniteSessionConfig
692729

693730
public class SessionConfig
694731
{
732+
/// <summary>
733+
/// Initializes a new instance of the <see cref="SessionConfig"/> class.
734+
/// </summary>
735+
public SessionConfig() { }
736+
737+
/// <summary>
738+
/// Initializes a new instance of the <see cref="SessionConfig"/> class
739+
/// by copying the properties of the specified instance.
740+
/// </summary>
741+
protected SessionConfig(SessionConfig? other)
742+
{
743+
if (other is null) return;
744+
745+
AvailableTools = other.AvailableTools is not null ? [.. other.AvailableTools] : null;
746+
ConfigDir = other.ConfigDir;
747+
CustomAgents = other.CustomAgents is not null ? [.. other.CustomAgents] : null;
748+
DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null;
749+
ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null;
750+
Hooks = other.Hooks;
751+
InfiniteSessions = other.InfiniteSessions;
752+
McpServers = other.McpServers is not null ? new Dictionary<string, object>(other.McpServers) : null;
753+
Model = other.Model;
754+
OnPermissionRequest = other.OnPermissionRequest;
755+
OnUserInputRequest = other.OnUserInputRequest;
756+
Provider = other.Provider;
757+
ReasoningEffort = other.ReasoningEffort;
758+
SessionId = other.SessionId;
759+
SkillDirectories = other.SkillDirectories is not null ? [.. other.SkillDirectories] : null;
760+
Streaming = other.Streaming;
761+
SystemMessage = other.SystemMessage;
762+
Tools = other.Tools is not null ? [.. other.Tools] : null;
763+
WorkingDirectory = other.WorkingDirectory;
764+
}
765+
695766
public string? SessionId { get; set; }
696767
public string? Model { get; set; }
697768

@@ -769,10 +840,53 @@ public class SessionConfig
769840
/// When enabled (default), sessions automatically manage context limits and persist state.
770841
/// </summary>
771842
public InfiniteSessionConfig? InfiniteSessions { get; set; }
843+
844+
/// <summary>
845+
/// Creates a shallow clone of this <see cref="SessionConfig"/> instance.
846+
/// </summary>
847+
/// <remarks>
848+
/// Collection properties are copied into new collections so that modifications
849+
/// to the clone do not affect the original.
850+
/// </remarks>
851+
public virtual SessionConfig Clone() => new(this);
772852
}
773853

774854
public class ResumeSessionConfig
775855
{
856+
/// <summary>
857+
/// Initializes a new instance of the <see cref="ResumeSessionConfig"/> class.
858+
/// </summary>
859+
public ResumeSessionConfig() { }
860+
861+
/// <summary>
862+
/// Initializes a new instance of the <see cref="ResumeSessionConfig"/> class
863+
/// by copying the properties of the specified instance.
864+
/// </summary>
865+
protected ResumeSessionConfig(ResumeSessionConfig? other)
866+
{
867+
if (other is null) return;
868+
869+
AvailableTools = other.AvailableTools is not null ? [.. other.AvailableTools] : null;
870+
ConfigDir = other.ConfigDir;
871+
CustomAgents = other.CustomAgents is not null ? [.. other.CustomAgents] : null;
872+
DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null;
873+
DisableResume = other.DisableResume;
874+
ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null;
875+
Hooks = other.Hooks;
876+
InfiniteSessions = other.InfiniteSessions;
877+
McpServers = other.McpServers is not null ? new Dictionary<string, object>(other.McpServers) : null;
878+
Model = other.Model;
879+
OnPermissionRequest = other.OnPermissionRequest;
880+
OnUserInputRequest = other.OnUserInputRequest;
881+
Provider = other.Provider;
882+
ReasoningEffort = other.ReasoningEffort;
883+
SkillDirectories = other.SkillDirectories is not null ? [.. other.SkillDirectories] : null;
884+
Streaming = other.Streaming;
885+
SystemMessage = other.SystemMessage;
886+
Tools = other.Tools is not null ? [.. other.Tools] : null;
887+
WorkingDirectory = other.WorkingDirectory;
888+
}
889+
776890
/// <summary>
777891
/// Model to use for this session. Can change the model when resuming.
778892
/// </summary>
@@ -870,13 +984,49 @@ public class ResumeSessionConfig
870984
/// Infinite session configuration for persistent workspaces and automatic compaction.
871985
/// </summary>
872986
public InfiniteSessionConfig? InfiniteSessions { get; set; }
987+
988+
/// <summary>
989+
/// Creates a shallow clone of this <see cref="ResumeSessionConfig"/> instance.
990+
/// </summary>
991+
/// <remarks>
992+
/// Collection properties are copied into new collections so that modifications
993+
/// to the clone do not affect the original.
994+
/// </remarks>
995+
public virtual ResumeSessionConfig Clone() => new(this);
873996
}
874997

875998
public class MessageOptions
876999
{
1000+
/// <summary>
1001+
/// Initializes a new instance of the <see cref="MessageOptions"/> class.
1002+
/// </summary>
1003+
public MessageOptions() { }
1004+
1005+
/// <summary>
1006+
/// Initializes a new instance of the <see cref="MessageOptions"/> class
1007+
/// by copying the properties of the specified instance.
1008+
/// </summary>
1009+
protected MessageOptions(MessageOptions? other)
1010+
{
1011+
if (other is null) return;
1012+
1013+
Attachments = other.Attachments is not null ? [.. other.Attachments] : null;
1014+
Mode = other.Mode;
1015+
Prompt = other.Prompt;
1016+
}
1017+
8771018
public string Prompt { get; set; } = string.Empty;
8781019
public List<UserMessageDataAttachmentsItem>? Attachments { get; set; }
8791020
public string? Mode { get; set; }
1021+
1022+
/// <summary>
1023+
/// Creates a shallow clone of this <see cref="MessageOptions"/> instance.
1024+
/// </summary>
1025+
/// <remarks>
1026+
/// Collection properties are copied into new collections so that modifications
1027+
/// to the clone do not affect the original.
1028+
/// </remarks>
1029+
public virtual MessageOptions Clone() => new(this);
8801030
}
8811031

8821032
public delegate void SessionEventHandler(SessionEvent sessionEvent);

0 commit comments

Comments
 (0)