From b4a4db4b833daa87b31c92709f3f6623feb6c95c Mon Sep 17 00:00:00 2001 From: PratikDhanave Date: Fri, 24 Jul 2026 08:10:32 +0530 Subject: [PATCH 1/2] Carry over remaining SessionConfig fields when resuming copilot sessions The first turn of a copilot-backed agent has no service ID and creates the session via copySessionConfig, which clones the full SessionConfig. Later turns carry a service ID and go through copyResumeSessionConfig, which hand-copied only 18 of the fields shared with copilot.ResumeSessionConfig. Every other configured option (ClientName, Providers/Models, Capi, ReasoningSummary, ContextTier, the Enable*/Skip* toggles, DefaultAgent/Agent, PluginDirectories, GitHubToken, RemoteSession, and more) was silently dropped, so a multi-turn agent's configuration quietly stopped taking effect after the first turn. Mirror the create path by copying every SessionConfig field that also exists on ResumeSessionConfig, matching the SDK's own CreateSession/ResumeSession parity. Extend the rich fixture and the resume test to assert the wire params now match the create request. --- provider/copilotprovider/copilot.go | 72 ++++++++++++++++++------ provider/copilotprovider/copilot_test.go | 71 ++++++++++++++++++++++- 2 files changed, 124 insertions(+), 19 deletions(-) diff --git a/provider/copilotprovider/copilot.go b/provider/copilotprovider/copilot.go index adb1daa3..400afc2e 100644 --- a/provider/copilotprovider/copilot.go +++ b/provider/copilotprovider/copilot.go @@ -261,24 +261,60 @@ func copyResumeSessionConfig(source *copilot.SessionConfig) copilot.ResumeSessio return copilot.ResumeSessionConfig{Streaming: copilot.Bool(true)} } return copilot.ResumeSessionConfig{ - Model: source.Model, - ReasoningEffort: source.ReasoningEffort, - Tools: source.Tools, - SystemMessage: source.SystemMessage, - AvailableTools: source.AvailableTools, - ExcludedTools: source.ExcludedTools, - Provider: source.Provider, - OnPermissionRequest: source.OnPermissionRequest, - OnUserInputRequest: source.OnUserInputRequest, - Hooks: source.Hooks, - WorkingDirectory: source.WorkingDirectory, - ConfigDirectory: source.ConfigDirectory, - MCPServers: source.MCPServers, - CustomAgents: source.CustomAgents, - SkillDirectories: source.SkillDirectories, - DisabledSkills: source.DisabledSkills, - InfiniteSessions: source.InfiniteSessions, - Streaming: copyBoolDefaultTrue(source.Streaming), + ClientName: source.ClientName, + Model: source.Model, + ReasoningEffort: source.ReasoningEffort, + ReasoningSummary: source.ReasoningSummary, + ContextTier: source.ContextTier, + Tools: source.Tools, + SystemMessage: source.SystemMessage, + AvailableTools: source.AvailableTools, + ExcludedTools: source.ExcludedTools, + ExcludedBuiltInAgents: source.ExcludedBuiltInAgents, + Provider: source.Provider, + Capi: source.Capi, + Providers: source.Providers, + Models: source.Models, + ModelCapabilities: source.ModelCapabilities, + EnableSessionTelemetry: source.EnableSessionTelemetry, + EnableCitations: source.EnableCitations, + SessionLimits: source.SessionLimits, + SkipCustomInstructions: source.SkipCustomInstructions, + CustomAgentsLocalOnly: source.CustomAgentsLocalOnly, + CoauthorEnabled: source.CoauthorEnabled, + ManageScheduleEnabled: source.ManageScheduleEnabled, + OnPermissionRequest: source.OnPermissionRequest, + OnMCPAuthRequest: source.OnMCPAuthRequest, + OnUserInputRequest: source.OnUserInputRequest, + Hooks: source.Hooks, + WorkingDirectory: source.WorkingDirectory, + ConfigDirectory: source.ConfigDirectory, + EnableConfigDiscovery: source.EnableConfigDiscovery, + SkipEmbeddingRetrieval: source.SkipEmbeddingRetrieval, + EmbeddingCacheStorage: source.EmbeddingCacheStorage, + OrganizationCustomInstructions: source.OrganizationCustomInstructions, + EnableOnDemandInstructionDiscovery: source.EnableOnDemandInstructionDiscovery, + EnableFileHooks: source.EnableFileHooks, + EnableHostGitOperations: source.EnableHostGitOperations, + EnableSessionStore: source.EnableSessionStore, + EnableSkills: source.EnableSkills, + IncludeSubAgentStreamingEvents: source.IncludeSubAgentStreamingEvents, + MCPServers: source.MCPServers, + MCPOAuthTokenStorage: source.MCPOAuthTokenStorage, + CustomAgents: source.CustomAgents, + DefaultAgent: source.DefaultAgent, + Agent: source.Agent, + SkillDirectories: source.SkillDirectories, + PluginDirectories: source.PluginDirectories, + InstructionDirectories: source.InstructionDirectories, + DisabledSkills: source.DisabledSkills, + InfiniteSessions: source.InfiniteSessions, + LargeOutput: source.LargeOutput, + ToolSearch: source.ToolSearch, + Memory: source.Memory, + GitHubToken: source.GitHubToken, + RemoteSession: source.RemoteSession, + Streaming: copyBoolDefaultTrue(source.Streaming), } } diff --git a/provider/copilotprovider/copilot_test.go b/provider/copilotprovider/copilot_test.go index ee27ca1e..2277e6b0 100644 --- a/provider/copilotprovider/copilot_test.go +++ b/provider/copilotprovider/copilot_test.go @@ -161,6 +161,40 @@ func TestCopyResumeSessionConfig_CopiesAllProperties(t *testing.T) { } assertStringSlice(t, request["disabledSkills"], []string{"skill1"}, "disabledSkills") assertEqual(t, request["streaming"], true, "streaming") + + // Fields beyond the originally hand-copied set must also carry over so that + // multi-turn options keep taking effect after the first turn. + assertEqual(t, request["clientName"], "test-client", "clientName") + assertEqual(t, request["reasoningSummary"], "concise", "reasoningSummary") + assertEqual(t, request["contextTier"], "long_context", "contextTier") + assertEqual(t, request["mcpOAuthTokenStorage"], "in-memory", "mcpOAuthTokenStorage") + assertStringSlice(t, request["excludedBuiltinAgents"], []string{"builtin1"}, "excludedBuiltinAgents") + assertEqual(t, request["enableSessionTelemetry"], true, "enableSessionTelemetry") + assertEqual(t, request["enableCitations"], true, "enableCitations") + assertEqual(t, request["enableConfigDiscovery"], true, "enableConfigDiscovery") + assertEqual(t, request["skipEmbeddingRetrieval"], true, "skipEmbeddingRetrieval") + assertEqual(t, request["embeddingCacheStorage"], "in-memory", "embeddingCacheStorage") + assertEqual(t, request["organizationCustomInstructions"], "org instructions", "organizationCustomInstructions") + assertEqual(t, request["enableOnDemandInstructionDiscovery"], true, "enableOnDemandInstructionDiscovery") + assertEqual(t, request["enableFileHooks"], true, "enableFileHooks") + assertEqual(t, request["enableHostGitOperations"], true, "enableHostGitOperations") + assertEqual(t, request["enableSessionStore"], true, "enableSessionStore") + assertEqual(t, request["enableSkills"], true, "enableSkills") + assertEqual(t, request["skipCustomInstructions"], true, "skipCustomInstructions") + assertEqual(t, request["customAgentsLocalOnly"], true, "customAgentsLocalOnly") + assertEqual(t, request["coauthorEnabled"], true, "coauthorEnabled") + assertEqual(t, request["manageScheduleEnabled"], true, "manageScheduleEnabled") + assertEqual(t, request["includeSubAgentStreamingEvents"], false, "includeSubAgentStreamingEvents") + assertEqual(t, request["agent"], "custom-agent", "agent") + assertStringSlice(t, request["pluginDirectories"], []string{"/plugins"}, "pluginDirectories") + assertStringSlice(t, request["instructionDirectories"], []string{"/instructions"}, "instructionDirectories") + assertEqual(t, request["gitHubToken"], "gh-token-123", "gitHubToken") + assertEqual(t, request["remoteSession"], "on", "remoteSession") + for _, key := range []string{"providers", "models", "capi", "modelCapabilities", "sessionLimits", "defaultAgent", "largeOutput", "toolSearch", "memory"} { + if request[key] == nil { + t.Fatalf("%s was not sent on resume", key) + } + } } func TestCopySessionConfig_WithStreamingDisabled_PreservesStreamingValue(t *testing.T) { @@ -609,8 +643,11 @@ func dataContent(t *testing.T, name, value string) *message.DataContent { func richSessionConfig() *copilot.SessionConfig { return &copilot.SessionConfig{ + ClientName: "test-client", Model: "gpt-4o", ReasoningEffort: "high", + ReasoningSummary: copilot.ReasoningSummaryConcise, + ContextTier: copilot.ContextTierLongContext, SystemMessage: &copilot.SystemMessageConfig{Mode: "append", Content: "Be helpful"}, AvailableTools: []string{"tool1", "tool2"}, ExcludedTools: []string{"tool3"}, @@ -631,7 +668,39 @@ func richSessionConfig() *copilot.SessionConfig { MCPServers: map[string]copilot.MCPServerConfig{ "server1": copilot.MCPStdioServerConfig{Command: "npx"}, }, - DisabledSkills: []string{"skill1"}, + MCPOAuthTokenStorage: "in-memory", + DisabledSkills: []string{"skill1"}, + ExcludedBuiltInAgents: []string{"builtin1"}, + Providers: []copilot.NamedProviderConfig{{Name: "prov1", BaseURL: "https://example.com"}}, + Models: []copilot.ProviderModelConfig{{ID: "m1", Provider: "prov1"}}, + Capi: &copilot.CapiSessionOptions{EnableWebSocketResponses: copilot.Bool(true)}, + ModelCapabilities: &rpc.ModelCapabilitiesOverride{}, + SessionLimits: &rpc.SessionLimitsConfig{}, + EnableSessionTelemetry: copilot.Bool(true), + EnableCitations: copilot.Bool(true), + EnableConfigDiscovery: copilot.Bool(true), + SkipEmbeddingRetrieval: copilot.Bool(true), + EmbeddingCacheStorage: copilot.String("in-memory"), + OrganizationCustomInstructions: copilot.String("org instructions"), + EnableOnDemandInstructionDiscovery: copilot.Bool(true), + EnableFileHooks: copilot.Bool(true), + EnableHostGitOperations: copilot.Bool(true), + EnableSessionStore: copilot.Bool(true), + EnableSkills: copilot.Bool(true), + SkipCustomInstructions: copilot.Bool(true), + CustomAgentsLocalOnly: copilot.Bool(true), + CoauthorEnabled: copilot.Bool(true), + ManageScheduleEnabled: copilot.Bool(true), + IncludeSubAgentStreamingEvents: copilot.Bool(false), + DefaultAgent: &copilot.DefaultAgentConfig{ExcludedTools: []string{"dtool"}}, + Agent: "custom-agent", + PluginDirectories: []string{"/plugins"}, + InstructionDirectories: []string{"/instructions"}, + LargeOutput: &copilot.LargeToolOutputConfig{Enabled: copilot.Bool(true)}, + ToolSearch: &copilot.ToolSearchConfig{Enabled: copilot.Bool(true)}, + Memory: &copilot.MemoryConfiguration{Enabled: true}, + GitHubToken: "gh-token-123", + RemoteSession: rpc.RemoteSessionModeOn, } } From 9f4928989798f148e0a3e0ea7fc825634795802b Mon Sep 17 00:00:00 2001 From: PratikDhanave Date: Fri, 24 Jul 2026 10:37:15 +0530 Subject: [PATCH 2/2] copilotprovider: clone Tools in copyResumeSessionConfig to avoid mutating shared config --- provider/copilotprovider/copilot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/copilotprovider/copilot.go b/provider/copilotprovider/copilot.go index 400afc2e..f8fde2de 100644 --- a/provider/copilotprovider/copilot.go +++ b/provider/copilotprovider/copilot.go @@ -266,7 +266,7 @@ func copyResumeSessionConfig(source *copilot.SessionConfig) copilot.ResumeSessio ReasoningEffort: source.ReasoningEffort, ReasoningSummary: source.ReasoningSummary, ContextTier: source.ContextTier, - Tools: source.Tools, + Tools: slices.Clone(source.Tools), SystemMessage: source.SystemMessage, AvailableTools: source.AvailableTools, ExcludedTools: source.ExcludedTools,