diff --git a/go/client.go b/go/client.go index 0673b9971..cad460557 100644 --- a/go/client.go +++ b/go/client.go @@ -607,9 +607,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.ReasoningSummary = config.ReasoningSummary req.ContextTier = config.ContextTier req.ConfigDir = config.ConfigDirectory - if config.EnableConfigDiscovery { - req.EnableConfigDiscovery = Bool(true) - } + req.EnableConfigDiscovery = config.EnableConfigDiscovery req.SkipEmbeddingRetrieval = config.SkipEmbeddingRetrieval req.EmbeddingCacheStorage = config.EmbeddingCacheStorage req.OrganizationCustomInstructions = config.OrganizationCustomInstructions @@ -960,9 +958,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, } req.WorkingDirectory = config.WorkingDirectory req.ConfigDir = config.ConfigDirectory - if config.EnableConfigDiscovery { - req.EnableConfigDiscovery = Bool(true) - } + req.EnableConfigDiscovery = config.EnableConfigDiscovery req.SkipEmbeddingRetrieval = config.SkipEmbeddingRetrieval req.EmbeddingCacheStorage = config.EmbeddingCacheStorage req.OrganizationCustomInstructions = config.OrganizationCustomInstructions @@ -974,9 +970,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, if config.SuppressResumeEvent { req.DisableResume = Bool(true) } - if config.ContinuePendingWork { - req.ContinuePendingWork = Bool(true) - } + req.ContinuePendingWork = config.ContinuePendingWork req.MCPServers = config.MCPServers req.MCPOAuthTokenStorage = config.MCPOAuthTokenStorage req.EnvValueMode = "direct" diff --git a/go/client_test.go b/go/client_test.go index 99b6fcb3b..6236a95ab 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -466,6 +466,73 @@ func TestSessionRequests_ContextTier(t *testing.T) { }) } +func TestSessionRequests_EnableConfigDiscovery(t *testing.T) { + t.Run("create includes enableConfigDiscovery when true", func(t *testing.T) { + req := createSessionRequest{EnableConfigDiscovery: Bool(true)} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["enableConfigDiscovery"] != true { + t.Errorf("Expected enableConfigDiscovery to be true, got %v", m["enableConfigDiscovery"]) + } + }) + + t.Run("create includes enableConfigDiscovery when false", func(t *testing.T) { + req := createSessionRequest{EnableConfigDiscovery: Bool(false)} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["enableConfigDiscovery"] != false { + t.Errorf("Expected enableConfigDiscovery to be false, got %v", m["enableConfigDiscovery"]) + } + }) + + t.Run("create omits enableConfigDiscovery when unset", func(t *testing.T) { + req := createSessionRequest{} + data, _ := json.Marshal(req) + var m map[string]any + json.Unmarshal(data, &m) + if _, ok := m["enableConfigDiscovery"]; ok { + t.Error("Expected enableConfigDiscovery to be omitted when unset") + } + }) + + t.Run("resume includes enableConfigDiscovery when false", func(t *testing.T) { + req := resumeSessionRequest{SessionID: "s1", EnableConfigDiscovery: Bool(false)} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["enableConfigDiscovery"] != false { + t.Errorf("Expected enableConfigDiscovery to be false, got %v", m["enableConfigDiscovery"]) + } + }) + + t.Run("resume omits enableConfigDiscovery when unset", func(t *testing.T) { + req := resumeSessionRequest{SessionID: "s1"} + data, _ := json.Marshal(req) + var m map[string]any + json.Unmarshal(data, &m) + if _, ok := m["enableConfigDiscovery"]; ok { + t.Error("Expected enableConfigDiscovery to be omitted when unset") + } + }) +} + func TestSessionRequests_PluginDirectoriesAndLargeOutput(t *testing.T) { pluginDirs := []string{"/tmp/plugins/a", "/tmp/plugins/b"} enabled := true @@ -1333,6 +1400,24 @@ func TestResumeSessionRequest_ContinuePendingWork(t *testing.T) { } }) + t.Run("forwards continuePendingWork when false", func(t *testing.T) { + req := resumeSessionRequest{ + SessionID: "s1", + ContinuePendingWork: Bool(false), + } + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["continuePendingWork"] != false { + t.Errorf("Expected continuePendingWork to be false, got %v", m["continuePendingWork"]) + } + }) + t.Run("omits continuePendingWork when not set", func(t *testing.T) { req := resumeSessionRequest{SessionID: "s1"} data, _ := json.Marshal(req) diff --git a/go/internal/e2e/client_options_e2e_test.go b/go/internal/e2e/client_options_e2e_test.go index 90e29eee5..4f8b74f2b 100644 --- a/go/internal/e2e/client_options_e2e_test.go +++ b/go/internal/e2e/client_options_e2e_test.go @@ -159,7 +159,7 @@ func TestClientOptionsE2E(t *testing.T) { } session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - EnableConfigDiscovery: true, + EnableConfigDiscovery: copilot.Bool(true), EnableOnDemandInstructionDiscovery: copilot.Bool(true), IncludeSubAgentStreamingEvents: copilot.Bool(false), OnPermissionRequest: copilot.PermissionHandler.ApproveAll, diff --git a/go/internal/e2e/pending_work_resume_e2e_test.go b/go/internal/e2e/pending_work_resume_e2e_test.go index 03fcbd3fb..00419aec5 100644 --- a/go/internal/e2e/pending_work_resume_e2e_test.go +++ b/go/internal/e2e/pending_work_resume_e2e_test.go @@ -107,7 +107,7 @@ func TestPendingWorkResumeE2E(t *testing.T) { t.Cleanup(func() { resumedClient.ForceStop() }) session2, err := resumedClient.ResumeSession(t.Context(), sessionID, &copilot.ResumeSessionConfig{ - ContinuePendingWork: true, + ContinuePendingWork: copilot.Bool(true), OnPermissionRequest: func(_ copilot.PermissionRequest, _ copilot.PermissionInvocation) (rpc.PermissionDecision, error) { return &rpc.PermissionDecisionNoResult{}, nil }, @@ -200,7 +200,7 @@ func TestPendingWorkResumeE2E(t *testing.T) { t.Cleanup(func() { resumedClient.ForceStop() }) session2, err := resumedClient.ResumeSession(t.Context(), sessionID, &copilot.ResumeSessionConfig{ - ContinuePendingWork: true, + ContinuePendingWork: copilot.Bool(true), OnPermissionRequest: copilot.PermissionHandler.ApproveAll, }) if err != nil { @@ -305,7 +305,7 @@ func TestPendingWorkResumeE2E(t *testing.T) { t.Cleanup(func() { resumedClient.ForceStop() }) session2, err := resumedClient.ResumeSession(t.Context(), sessionID, &copilot.ResumeSessionConfig{ - ContinuePendingWork: true, + ContinuePendingWork: copilot.Bool(true), OnPermissionRequest: copilot.PermissionHandler.ApproveAll, }) if err != nil { @@ -379,7 +379,7 @@ func TestPendingWorkResumeE2E(t *testing.T) { t.Cleanup(func() { resumedClient.ForceStop() }) resumedSession, err := resumedClient.ResumeSession(t.Context(), sessionID, &copilot.ResumeSessionConfig{ - ContinuePendingWork: true, + ContinuePendingWork: copilot.Bool(true), OnPermissionRequest: copilot.PermissionHandler.ApproveAll, }) if err != nil { @@ -482,7 +482,7 @@ func TestPendingWorkResumeE2E(t *testing.T) { // to assert the runtime doesn't re-invoke the tool on resume (orphan // auto-completion happens internally). resumeConfig := &copilot.ResumeSessionConfig{ - ContinuePendingWork: false, + ContinuePendingWork: copilot.Bool(false), OnPermissionRequest: copilot.PermissionHandler.ApproveAll, } if scenario.disconnectOriginalClient { @@ -600,7 +600,7 @@ func TestPendingWorkResumeE2E(t *testing.T) { t.Cleanup(func() { resumedClient.ForceStop() }) resumedSession, err := resumedClient.ResumeSession(t.Context(), sessionID, &copilot.ResumeSessionConfig{ - ContinuePendingWork: true, + ContinuePendingWork: copilot.Bool(true), OnPermissionRequest: copilot.PermissionHandler.ApproveAll, }) if err != nil { diff --git a/go/internal/e2e/rpc_server_e2e_test.go b/go/internal/e2e/rpc_server_e2e_test.go index 1b5a2407e..6f6c77e7d 100644 --- a/go/internal/e2e/rpc_server_e2e_test.go +++ b/go/internal/e2e/rpc_server_e2e_test.go @@ -474,7 +474,7 @@ func TestRPCServerE2E(t *testing.T) { session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ SessionID: sessionID, WorkingDirectory: workingDirectory, - EnableConfigDiscovery: false, + EnableConfigDiscovery: copilot.Bool(false), OnPermissionRequest: copilot.PermissionHandler.ApproveAll, }) if err != nil { diff --git a/go/internal/e2e/skills_e2e_test.go b/go/internal/e2e/skills_e2e_test.go index 80cb4f686..06a96cf95 100644 --- a/go/internal/e2e/skills_e2e_test.go +++ b/go/internal/e2e/skills_e2e_test.go @@ -258,7 +258,7 @@ func TestSkillsE2E(t *testing.T) { disabledSession, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ OnPermissionRequest: copilot.PermissionHandler.ApproveAll, WorkingDirectory: projectDir, - EnableConfigDiscovery: false, + EnableConfigDiscovery: copilot.Bool(false), }) if err != nil { t.Fatalf("CreateSession (disabled) failed: %v", err) @@ -278,7 +278,7 @@ func TestSkillsE2E(t *testing.T) { enabledSession, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ OnPermissionRequest: copilot.PermissionHandler.ApproveAll, WorkingDirectory: projectDir, - EnableConfigDiscovery: true, + EnableConfigDiscovery: copilot.Bool(true), }) if err != nil { t.Fatalf("CreateSession (enabled) failed: %v", err) diff --git a/go/samples/manual_tool_resume/main.go b/go/samples/manual_tool_resume/main.go index 1e0a23f5b..a7391ff40 100644 --- a/go/samples/manual_tool_resume/main.go +++ b/go/samples/manual_tool_resume/main.go @@ -143,7 +143,7 @@ func main() { } session2, err := client2.ResumeSession(ctx, sessionID, &copilot.ResumeSessionConfig{ Tools: []copilot.Tool{tool}, - ContinuePendingWork: true, + ContinuePendingWork: copilot.Bool(true), }) if err != nil { panic(err) @@ -177,7 +177,7 @@ func main() { } session3, err := client3.ResumeSession(ctx, sessionID, &copilot.ResumeSessionConfig{ Tools: []copilot.Tool{tool}, - ContinuePendingWork: true, + ContinuePendingWork: copilot.Bool(true), }) if err != nil { panic(err) diff --git a/go/types.go b/go/types.go index 7a53de69f..a944a452b 100644 --- a/go/types.go +++ b/go/types.go @@ -901,13 +901,14 @@ type SessionConfig struct { // ConfigDirectory overrides the default configuration directory location. // When specified, the session will use this directory for storing config and state. ConfigDirectory string - // EnableConfigDiscovery, when true, automatically discovers MCP server configurations + // EnableConfigDiscovery, when non-nil, controls automatic discovery of MCP server configurations // (e.g. .mcp.json, .vscode/mcp.json) and skill directories from the working directory // and merges them with any explicitly provided MCPServers and SkillDirectories, with // explicit values taking precedence on name collision. + // Nil leaves the runtime default unchanged; use Bool(false) to explicitly disable discovery. // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are // always loaded from the working directory regardless of this setting. - EnableConfigDiscovery bool + EnableConfigDiscovery *bool // SkipEmbeddingRetrieval, when non-nil, controls embedding-based retrieval // for this session. Use in multitenant deployments to prevent cross-session // information leakage through the shared embedding cache. @@ -1314,13 +1315,14 @@ type ResumeSessionConfig struct { WorkingDirectory string // ConfigDirectory overrides the default configuration directory location. ConfigDirectory string - // EnableConfigDiscovery, when true, automatically discovers MCP server configurations + // EnableConfigDiscovery, when non-nil, controls automatic discovery of MCP server configurations // (e.g. .mcp.json, .vscode/mcp.json) and skill directories from the working directory // and merges them with any explicitly provided MCPServers and SkillDirectories, with // explicit values taking precedence on name collision. + // Nil leaves the runtime default unchanged; use Bool(false) to explicitly disable discovery. // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are // always loaded from the working directory regardless of this setting. - EnableConfigDiscovery bool + EnableConfigDiscovery *bool // SkipEmbeddingRetrieval, when non-nil, controls embedding-based retrieval // for this session. Use in multitenant deployments to prevent cross-session // information leakage through the shared embedding cache. @@ -1399,15 +1401,16 @@ type ResumeSessionConfig struct { // SuppressResumeEvent, when true, skips emitting the session.resume event. // Useful for reconnecting to a session without triggering resume-related side effects. SuppressResumeEvent bool - // ContinuePendingWork, when true, instructs the runtime to continue any tool calls - // or permission prompts that were still pending when the session was last suspended. - // When false (the default), the runtime treats pending work as interrupted on resume. + // ContinuePendingWork, when non-nil, controls whether the runtime continues any + // tool calls or permission prompts that were still pending when the session was + // last suspended. Nil leaves the runtime default unchanged; use Bool(false) to + // explicitly treat pending work as interrupted on resume. // // For permission requests, the runtime re-emits permission.requested so the // registered OnPermissionRequest handler can re-prompt; for external tool calls, // the consumer is expected to supply the result via the corresponding low-level // RPC method. - ContinuePendingWork bool + ContinuePendingWork *bool // OnEvent is an optional event handler registered before the session.resume RPC // is issued, ensuring early events are delivered. See SessionConfig.OnEvent. OnEvent SessionEventHandler