From 137a592574906ad39129167aff30d3da30cccce6 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Wed, 27 May 2026 16:52:55 -0700 Subject: [PATCH 01/10] feat: add granular per-session flags for multitenancy hardening Add 7 new optional fields to SessionConfigBase across all 6 SDK languages (Node.js, Go, Python, .NET, Rust, Java): - skipEmbeddingRetrieval: prevent cross-session info leakage via embedding cache - organizationCustomInstructions: inject org-level instructions into system prompt - enableOnDemandInstructionDiscovery: control instruction file discovery after file views - enableFileHooks: control loading of file-based hooks from .github/hooks/ - enableHostGitOperations: control git operations on host filesystem - enableSessionStore: control cross-session store for search/retrieval - enableSkills: control skill loading (builtin + discovered) All fields are optional and pass through to the runtime without SDK-side default coercion. Empty mode applies restrictive defaults for multitenancy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Client.cs | 34 +++ dotnet/src/Types.cs | 57 ++++ dotnet/test/E2E/ClientOptionsE2ETests.cs | 142 ++++++++++ go/client.go | 14 + go/mode_empty.go | 48 ++++ go/toolset_test.go | 81 ++++++ go/types.go | 63 +++++ .../github/copilot/SessionRequestBuilder.java | 18 ++ .../copilot/rpc/CreateSessionRequest.java | 133 ++++++++++ .../copilot/rpc/ResumeSessionConfig.java | 246 ++++++++++++++++++ .../copilot/rpc/ResumeSessionRequest.java | 133 ++++++++++ .../com/github/copilot/rpc/SessionConfig.java | 246 ++++++++++++++++++ .../com/github/copilot/ConfigCloneTest.java | 44 ++++ .../copilot/OptionalApiAndJacksonTest.java | 56 ++++ .../copilot/SessionRequestBuilderTest.java | 44 ++++ nodejs/src/client.ts | 26 +- nodejs/src/types.ts | 58 +++++ nodejs/test/toolSet.test.ts | 82 ++++++ python/copilot/_mode.py | 62 ++++- python/copilot/client.py | 99 ++++++- python/test_tool_set.py | 6 + rust/src/session.rs | 48 +++- rust/src/types.rs | 178 +++++++++++++ rust/src/wire.rs | 28 ++ 24 files changed, 1937 insertions(+), 9 deletions(-) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 1aaee445b..d605d2a35 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -572,6 +572,12 @@ private void ApplyConfigDefaultsForMode(SessionConfigBase config) if (_options.Mode == CopilotClientMode.Empty) { config.EnableSessionTelemetry ??= false; + config.SkipEmbeddingRetrieval ??= true; + config.EnableOnDemandInstructionDiscovery ??= false; + config.EnableFileHooks ??= false; + config.EnableHostGitOperations ??= false; + config.EnableSessionStore ??= false; + config.EnableSkills ??= false; } } @@ -829,6 +835,13 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.Agent, config.ConfigDir, config.EnableConfigDiscovery, + config.SkipEmbeddingRetrieval, + config.OrganizationCustomInstructions, + config.EnableOnDemandInstructionDiscovery, + config.EnableFileHooks, + config.EnableHostGitOperations, + config.EnableSessionStore, + config.EnableSkills, config.SkillDirectories, config.DisabledSkills, config.InfiniteSessions, @@ -995,6 +1008,13 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config.WorkingDirectory, config.ConfigDir, config.EnableConfigDiscovery, + config.SkipEmbeddingRetrieval, + config.OrganizationCustomInstructions, + config.EnableOnDemandInstructionDiscovery, + config.EnableFileHooks, + config.EnableHostGitOperations, + config.EnableSessionStore, + config.EnableSkills, config.SuppressResumeEvent is true ? true : null, config.Streaming is true ? true : null, config.IncludeSubAgentStreamingEvents, @@ -2110,6 +2130,13 @@ internal record CreateSessionRequest( string? Agent, string? ConfigDir, bool? EnableConfigDiscovery, + bool? SkipEmbeddingRetrieval, + string? OrganizationCustomInstructions, + bool? EnableOnDemandInstructionDiscovery, + bool? EnableFileHooks, + bool? EnableHostGitOperations, + bool? EnableSessionStore, + bool? EnableSkills, IList? SkillDirectories, IList? DisabledSkills, InfiniteSessionConfig? InfiniteSessions, @@ -2174,6 +2201,13 @@ internal record ResumeSessionRequest( string? WorkingDirectory, string? ConfigDir, bool? EnableConfigDiscovery, + bool? SkipEmbeddingRetrieval, + string? OrganizationCustomInstructions, + bool? EnableOnDemandInstructionDiscovery, + bool? EnableFileHooks, + bool? EnableHostGitOperations, + bool? EnableSessionStore, + bool? EnableSkills, bool? SuppressResumeEvent, bool? Streaming, bool? IncludeSubAgentStreamingEvents, diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index af67f4802..d6b4a7de2 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -2345,6 +2345,13 @@ protected SessionConfigBase(SessionConfigBase? other) Agent = other.Agent; DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null; EnableConfigDiscovery = other.EnableConfigDiscovery; + SkipEmbeddingRetrieval = other.SkipEmbeddingRetrieval; + OrganizationCustomInstructions = other.OrganizationCustomInstructions; + EnableOnDemandInstructionDiscovery = other.EnableOnDemandInstructionDiscovery; + EnableFileHooks = other.EnableFileHooks; + EnableHostGitOperations = other.EnableHostGitOperations; + EnableSessionStore = other.EnableSessionStore; + EnableSkills = other.EnableSkills; ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null; Hooks = other.Hooks; InfiniteSessions = other.InfiniteSessions; @@ -2422,6 +2429,56 @@ protected SessionConfigBase(SessionConfigBase? other) /// public bool? EnableConfigDiscovery { get; set; } + /// + /// When , skips embedding-based retrieval for this session. + /// Use in multitenant deployments to prevent cross-session information leakage + /// through the shared embedding cache. + /// + public bool? SkipEmbeddingRetrieval { get; set; } + + /// + /// Organization-level custom instructions to include in the system prompt. + /// Allows hosts to inject organization-specific guidance without relying on + /// filesystem-based instruction discovery. + /// + public string? OrganizationCustomInstructions { get; set; } + + /// + /// When , enables on-demand discovery of instruction files + /// (for example AGENTS.md and .github/copilot-instructions.md) + /// after successful file views. + /// + public bool? EnableOnDemandInstructionDiscovery { get; set; } + + /// + /// When , enables loading of file-based hooks from + /// .github/hooks/. This is separate from , which + /// controls SDK hook callback registration. + /// + public bool? EnableFileHooks { get; set; } + + /// + /// When , enables git operations on the host filesystem + /// such as branch detection, file status, and commit history. When + /// , no git context is surfaced in the system prompt. + /// + public bool? EnableHostGitOperations { get; set; } + + /// + /// When , enables the cross-session store for search and + /// retrieval across sessions. When , session content is + /// not written to or read from the shared session store. + /// + public bool? EnableSessionStore { get; set; } + + /// + /// When , enables skill loading, including built-in + /// skills and discovered skill directories. When , no + /// skills are loaded regardless of or + /// . + /// + public bool? EnableSkills { get; set; } + /// /// Custom tool declarations available to the language model during the session. /// Declarations backed by an are invoked automatically; declarations without one diff --git a/dotnet/test/E2E/ClientOptionsE2ETests.cs b/dotnet/test/E2E/ClientOptionsE2ETests.cs index 95ddeee75..9d0efb0e4 100644 --- a/dotnet/test/E2E/ClientOptionsE2ETests.cs +++ b/dotnet/test/E2E/ClientOptionsE2ETests.cs @@ -177,6 +177,77 @@ public async Task Should_Omit_EnableSessionTelemetry_When_Not_Set() await session.DisposeAsync(); } + [Fact] + public async Task Should_Forward_New_Session_Config_Fields_In_Create_Wire_Request() + { + var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); + + await using var client = Ctx.CreateClient(options: new CopilotClientOptions + { + Connection = RuntimeConnection.ForStdio(path: cliPath, args: ["--capture-file", capturePath]), + UseLoggedInUser = false, + }); + + await client.StartAsync(); + + var session = await client.CreateSessionAsync(new SessionConfig + { + SkipEmbeddingRetrieval = false, + OrganizationCustomInstructions = "Follow org policy.", + EnableOnDemandInstructionDiscovery = true, + EnableFileHooks = true, + EnableHostGitOperations = false, + EnableSessionStore = true, + EnableSkills = false, + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + using var capture = JsonDocument.Parse(await File.ReadAllTextAsync(capturePath)); + var createRequest = GetCapturedRequestParams(capture.RootElement, "session.create"); + Assert.False(createRequest.GetProperty("skipEmbeddingRetrieval").GetBoolean()); + Assert.Equal("Follow org policy.", createRequest.GetProperty("organizationCustomInstructions").GetString()); + Assert.True(createRequest.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean()); + Assert.True(createRequest.GetProperty("enableFileHooks").GetBoolean()); + Assert.False(createRequest.GetProperty("enableHostGitOperations").GetBoolean()); + Assert.True(createRequest.GetProperty("enableSessionStore").GetBoolean()); + Assert.False(createRequest.GetProperty("enableSkills").GetBoolean()); + + await session.DisposeAsync(); + } + + [Fact] + public async Task Should_Apply_Empty_Mode_Defaults_To_CreateSession_Wire_Request() + { + var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); + + await using var client = Ctx.CreateClient(options: new CopilotClientOptions + { + Connection = RuntimeConnection.ForStdio(path: cliPath, args: ["--capture-file", capturePath]), + Mode = CopilotClientMode.Empty, + UseLoggedInUser = false, + }); + + await client.StartAsync(); + + var session = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + using var capture = JsonDocument.Parse(await File.ReadAllTextAsync(capturePath)); + var createRequest = GetCapturedRequestParams(capture.RootElement, "session.create"); + Assert.False(createRequest.GetProperty("enableSessionTelemetry").GetBoolean()); + Assert.True(createRequest.GetProperty("skipEmbeddingRetrieval").GetBoolean()); + Assert.False(createRequest.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean()); + Assert.False(createRequest.GetProperty("enableFileHooks").GetBoolean()); + Assert.False(createRequest.GetProperty("enableHostGitOperations").GetBoolean()); + Assert.False(createRequest.GetProperty("enableSessionStore").GetBoolean()); + Assert.False(createRequest.GetProperty("enableSkills").GetBoolean()); + Assert.False(createRequest.TryGetProperty("organizationCustomInstructions", out _)); + + await session.DisposeAsync(); + } + [Fact] public async Task Should_Propagate_Activity_TraceContext_To_Session_Create_And_Send() { @@ -293,6 +364,77 @@ public async Task Should_Propagate_Activity_TraceContext_To_Session_Resume() await session.DisposeAsync(); } + [Fact] + public async Task Should_Forward_New_Session_Config_Fields_In_Resume_Wire_Request() + { + var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); + + await using var client = Ctx.CreateClient(options: new CopilotClientOptions + { + Connection = RuntimeConnection.ForStdio(path: cliPath, args: ["--capture-file", capturePath]), + UseLoggedInUser = false, + }); + + await client.StartAsync(); + + var session = await client.ResumeSessionAsync("resume-session", new ResumeSessionConfig + { + SkipEmbeddingRetrieval = false, + OrganizationCustomInstructions = "Resume org policy.", + EnableOnDemandInstructionDiscovery = true, + EnableFileHooks = true, + EnableHostGitOperations = false, + EnableSessionStore = true, + EnableSkills = false, + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + using var capture = JsonDocument.Parse(await File.ReadAllTextAsync(capturePath)); + var resumeRequest = GetCapturedRequestParams(capture.RootElement, "session.resume"); + Assert.False(resumeRequest.GetProperty("skipEmbeddingRetrieval").GetBoolean()); + Assert.Equal("Resume org policy.", resumeRequest.GetProperty("organizationCustomInstructions").GetString()); + Assert.True(resumeRequest.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean()); + Assert.True(resumeRequest.GetProperty("enableFileHooks").GetBoolean()); + Assert.False(resumeRequest.GetProperty("enableHostGitOperations").GetBoolean()); + Assert.True(resumeRequest.GetProperty("enableSessionStore").GetBoolean()); + Assert.False(resumeRequest.GetProperty("enableSkills").GetBoolean()); + + await session.DisposeAsync(); + } + + [Fact] + public async Task Should_Apply_Empty_Mode_Defaults_To_ResumeSession_Wire_Request() + { + var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); + + await using var client = Ctx.CreateClient(options: new CopilotClientOptions + { + Connection = RuntimeConnection.ForStdio(path: cliPath, args: ["--capture-file", capturePath]), + Mode = CopilotClientMode.Empty, + UseLoggedInUser = false, + }); + + await client.StartAsync(); + + var session = await client.ResumeSessionAsync("resume-empty-session", new ResumeSessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + using var capture = JsonDocument.Parse(await File.ReadAllTextAsync(capturePath)); + var resumeRequest = GetCapturedRequestParams(capture.RootElement, "session.resume"); + Assert.False(resumeRequest.GetProperty("enableSessionTelemetry").GetBoolean()); + Assert.True(resumeRequest.GetProperty("skipEmbeddingRetrieval").GetBoolean()); + Assert.False(resumeRequest.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean()); + Assert.False(resumeRequest.GetProperty("enableFileHooks").GetBoolean()); + Assert.False(resumeRequest.GetProperty("enableHostGitOperations").GetBoolean()); + Assert.False(resumeRequest.GetProperty("enableSessionStore").GetBoolean()); + Assert.False(resumeRequest.GetProperty("enableSkills").GetBoolean()); + Assert.False(resumeRequest.TryGetProperty("organizationCustomInstructions", out _)); + + await session.DisposeAsync(); + } + [Fact] public void Should_Accept_GitHubToken_Option() { diff --git a/go/client.go b/go/client.go index 94ef50f5e..fba6fd47a 100644 --- a/go/client.go +++ b/go/client.go @@ -608,6 +608,13 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses if config.EnableConfigDiscovery { req.EnableConfigDiscovery = Bool(true) } + req.SkipEmbeddingRetrieval = config.SkipEmbeddingRetrieval + req.OrganizationCustomInstructions = config.OrganizationCustomInstructions + req.EnableOnDemandInstructionDiscovery = config.EnableOnDemandInstructionDiscovery + req.EnableFileHooks = config.EnableFileHooks + req.EnableHostGitOperations = config.EnableHostGitOperations + req.EnableSessionStore = config.EnableSessionStore + req.EnableSkills = config.EnableSkills req.Tools = config.Tools systemMessage := c.systemMessageForMode(config.SystemMessage) wireSystemMessage, transformCallbacks := extractTransformCallbacks(systemMessage) @@ -866,6 +873,13 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, if config.EnableConfigDiscovery { req.EnableConfigDiscovery = Bool(true) } + req.SkipEmbeddingRetrieval = config.SkipEmbeddingRetrieval + req.OrganizationCustomInstructions = config.OrganizationCustomInstructions + req.EnableOnDemandInstructionDiscovery = config.EnableOnDemandInstructionDiscovery + req.EnableFileHooks = config.EnableFileHooks + req.EnableHostGitOperations = config.EnableHostGitOperations + req.EnableSessionStore = config.EnableSessionStore + req.EnableSkills = config.EnableSkills if config.SuppressResumeEvent { req.DisableResume = Bool(true) } diff --git a/go/mode_empty.go b/go/mode_empty.go index 36e689b24..9461cc577 100644 --- a/go/mode_empty.go +++ b/go/mode_empty.go @@ -126,6 +126,30 @@ func (c *Client) applyConfigDefaultsForMode(config *SessionConfig) { f := false config.EnableSessionTelemetry = &f } + if config.SkipEmbeddingRetrieval == nil { + t := true + config.SkipEmbeddingRetrieval = &t + } + if config.EnableOnDemandInstructionDiscovery == nil { + f := false + config.EnableOnDemandInstructionDiscovery = &f + } + if config.EnableFileHooks == nil { + f := false + config.EnableFileHooks = &f + } + if config.EnableHostGitOperations == nil { + f := false + config.EnableHostGitOperations = &f + } + if config.EnableSessionStore == nil { + f := false + config.EnableSessionStore = &f + } + if config.EnableSkills == nil { + f := false + config.EnableSkills = &f + } } func (c *Client) applyResumeDefaultsForMode(config *ResumeSessionConfig) { @@ -136,6 +160,30 @@ func (c *Client) applyResumeDefaultsForMode(config *ResumeSessionConfig) { f := false config.EnableSessionTelemetry = &f } + if config.SkipEmbeddingRetrieval == nil { + t := true + config.SkipEmbeddingRetrieval = &t + } + if config.EnableOnDemandInstructionDiscovery == nil { + f := false + config.EnableOnDemandInstructionDiscovery = &f + } + if config.EnableFileHooks == nil { + f := false + config.EnableFileHooks = &f + } + if config.EnableHostGitOperations == nil { + f := false + config.EnableHostGitOperations = &f + } + if config.EnableSessionStore == nil { + f := false + config.EnableSessionStore = &f + } + if config.EnableSkills == nil { + f := false + config.EnableSkills = &f + } } // updateSessionOptionsForMode applies the per-mode safe-defaults patch via diff --git a/go/toolset_test.go b/go/toolset_test.go index 6992e1200..9a59df79c 100644 --- a/go/toolset_test.go +++ b/go/toolset_test.go @@ -247,3 +247,84 @@ func TestApplyConfigDefaultsForMode_copilotCliLeavesNil(t *testing.T) { t.Errorf("non-empty mode must not default telemetry") } } + +func TestApplyConfigDefaultsForMode_emptyDefaultsGranularFlags(t *testing.T) { + c := NewClient(&ClientOptions{Mode: ModeEmpty, BaseDirectory: t.TempDir()}) + cfg := &SessionConfig{} + c.applyConfigDefaultsForMode(cfg) + if cfg.SkipEmbeddingRetrieval == nil || *cfg.SkipEmbeddingRetrieval != true { + t.Errorf("expected SkipEmbeddingRetrieval=true in empty mode, got %v", cfg.SkipEmbeddingRetrieval) + } + if cfg.EnableOnDemandInstructionDiscovery == nil || *cfg.EnableOnDemandInstructionDiscovery != false { + t.Errorf("expected EnableOnDemandInstructionDiscovery=false in empty mode, got %v", cfg.EnableOnDemandInstructionDiscovery) + } + if cfg.EnableFileHooks == nil || *cfg.EnableFileHooks != false { + t.Errorf("expected EnableFileHooks=false in empty mode, got %v", cfg.EnableFileHooks) + } + if cfg.EnableHostGitOperations == nil || *cfg.EnableHostGitOperations != false { + t.Errorf("expected EnableHostGitOperations=false in empty mode, got %v", cfg.EnableHostGitOperations) + } + if cfg.EnableSessionStore == nil || *cfg.EnableSessionStore != false { + t.Errorf("expected EnableSessionStore=false in empty mode, got %v", cfg.EnableSessionStore) + } + if cfg.EnableSkills == nil || *cfg.EnableSkills != false { + t.Errorf("expected EnableSkills=false in empty mode, got %v", cfg.EnableSkills) + } +} + +func TestApplyConfigDefaultsForMode_emptyHonorsCallerGranularFlags(t *testing.T) { + c := NewClient(&ClientOptions{Mode: ModeEmpty, BaseDirectory: t.TempDir()}) + falseVal := false + trueVal := true + cfg := &SessionConfig{ + SkipEmbeddingRetrieval: &falseVal, + EnableOnDemandInstructionDiscovery: &trueVal, + EnableFileHooks: &trueVal, + EnableHostGitOperations: &trueVal, + EnableSessionStore: &trueVal, + EnableSkills: &trueVal, + } + c.applyConfigDefaultsForMode(cfg) + if *cfg.SkipEmbeddingRetrieval != false { + t.Errorf("caller-supplied SkipEmbeddingRetrieval must win") + } + if *cfg.EnableOnDemandInstructionDiscovery != true { + t.Errorf("caller-supplied EnableOnDemandInstructionDiscovery must win") + } + if *cfg.EnableFileHooks != true { + t.Errorf("caller-supplied EnableFileHooks must win") + } + if *cfg.EnableHostGitOperations != true { + t.Errorf("caller-supplied EnableHostGitOperations must win") + } + if *cfg.EnableSessionStore != true { + t.Errorf("caller-supplied EnableSessionStore must win") + } + if *cfg.EnableSkills != true { + t.Errorf("caller-supplied EnableSkills must win") + } +} + +func TestApplyConfigDefaultsForMode_copilotCliLeavesGranularFlagsNil(t *testing.T) { + c := NewClient(&ClientOptions{Mode: ModeCopilotCli}) + cfg := &SessionConfig{} + c.applyConfigDefaultsForMode(cfg) + if cfg.SkipEmbeddingRetrieval != nil { + t.Errorf("non-empty mode must not default SkipEmbeddingRetrieval") + } + if cfg.EnableOnDemandInstructionDiscovery != nil { + t.Errorf("non-empty mode must not default EnableOnDemandInstructionDiscovery") + } + if cfg.EnableFileHooks != nil { + t.Errorf("non-empty mode must not default EnableFileHooks") + } + if cfg.EnableHostGitOperations != nil { + t.Errorf("non-empty mode must not default EnableHostGitOperations") + } + if cfg.EnableSessionStore != nil { + t.Errorf("non-empty mode must not default EnableSessionStore") + } + if cfg.EnableSkills != nil { + t.Errorf("non-empty mode must not default EnableSkills") + } +} diff --git a/go/types.go b/go/types.go index 105400931..74a5a939d 100644 --- a/go/types.go +++ b/go/types.go @@ -886,6 +886,34 @@ type SessionConfig struct { // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are // always loaded from the working directory regardless of this setting. EnableConfigDiscovery bool + // SkipEmbeddingRetrieval, when non-nil and true, skips embedding-based retrieval + // for this session. Use in multitenant deployments to prevent cross-session + // information leakage through the shared embedding cache. + SkipEmbeddingRetrieval *bool + // OrganizationCustomInstructions provides organization-level custom instructions + // to include in the system prompt. Allows hosts to inject organization-specific + // guidance without relying on filesystem-based instruction discovery. + OrganizationCustomInstructions string + // EnableOnDemandInstructionDiscovery, when non-nil and true, enables on-demand + // discovery of instruction files (AGENTS.md, .github/copilot-instructions.md, etc.) + // after successful file views. + EnableOnDemandInstructionDiscovery *bool + // EnableFileHooks, when non-nil and true, enables loading of file-based hooks + // from .github/hooks/. This is separate from the Hooks callback parameter which + // gates SDK hook event registration. + EnableFileHooks *bool + // EnableHostGitOperations, when non-nil and true, enables git operations on the + // host filesystem (branch detection, file status, commit history). When false, + // no git context is surfaced in the system prompt. + EnableHostGitOperations *bool + // EnableSessionStore, when non-nil and true, enables the cross-session store for + // search and retrieval across sessions. When false, session content is not written + // to or read from the shared session store. + EnableSessionStore *bool + // EnableSkills, when non-nil and true, enables skill loading (including builtin + // skills and discovered skill directories). When false, no skills are loaded + // regardless of SkillDirectories or EnableConfigDiscovery settings. + EnableSkills *bool // Tools exposes caller-implemented tools to the CLI. A Tool with a nil Handler // is declaration-only; the consumer must resolve its calls via pending tool RPCs. Tools []Tool @@ -1210,6 +1238,27 @@ type ResumeSessionConfig struct { // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are // always loaded from the working directory regardless of this setting. EnableConfigDiscovery bool + // SkipEmbeddingRetrieval, when non-nil and true, skips embedding-based retrieval + // for this session. Use in multitenant deployments to prevent cross-session + // information leakage through the shared embedding cache. + SkipEmbeddingRetrieval *bool + // OrganizationCustomInstructions provides organization-level custom instructions + // to include in the system prompt. + OrganizationCustomInstructions string + // EnableOnDemandInstructionDiscovery, when non-nil and true, enables on-demand + // discovery of instruction files after successful file views. + EnableOnDemandInstructionDiscovery *bool + // EnableFileHooks, when non-nil and true, enables loading of file-based hooks + // from .github/hooks/. + EnableFileHooks *bool + // EnableHostGitOperations, when non-nil and true, enables git operations on the + // host filesystem. + EnableHostGitOperations *bool + // EnableSessionStore, when non-nil and true, enables the cross-session store for + // search and retrieval across sessions. + EnableSessionStore *bool + // EnableSkills, when non-nil and true, enables skill loading. + EnableSkills *bool // Streaming enables streaming of assistant message and reasoning chunks. // When non-nil and true, assistant.message_delta and assistant.reasoning_delta // events with deltaContent are sent as the response is generated. @@ -1526,6 +1575,13 @@ type createSessionRequest struct { Agent string `json:"agent,omitempty"` ConfigDir string `json:"configDir,omitempty"` EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` + SkipEmbeddingRetrieval *bool `json:"skipEmbeddingRetrieval,omitempty"` + OrganizationCustomInstructions string `json:"organizationCustomInstructions,omitempty"` + EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` + EnableFileHooks *bool `json:"enableFileHooks,omitempty"` + EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` + EnableSessionStore *bool `json:"enableSessionStore,omitempty"` + EnableSkills *bool `json:"enableSkills,omitempty"` SkillDirectories []string `json:"skillDirectories,omitempty"` InstructionDirectories []string `json:"instructionDirectories,omitempty"` DisabledSkills []string `json:"disabledSkills,omitempty"` @@ -1582,6 +1638,13 @@ type resumeSessionRequest struct { WorkingDirectory string `json:"workingDirectory,omitempty"` ConfigDir string `json:"configDir,omitempty"` EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` + SkipEmbeddingRetrieval *bool `json:"skipEmbeddingRetrieval,omitempty"` + OrganizationCustomInstructions string `json:"organizationCustomInstructions,omitempty"` + EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` + EnableFileHooks *bool `json:"enableFileHooks,omitempty"` + EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` + EnableSessionStore *bool `json:"enableSessionStore,omitempty"` + EnableSkills *bool `json:"enableSkills,omitempty"` DisableResume *bool `json:"disableResume,omitempty"` ContinuePendingWork *bool `json:"continuePendingWork,omitempty"` Streaming *bool `json:"streaming,omitempty"` diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index d9ad69282..06b64c534 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -133,6 +133,15 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setDisabledSkills(config.getDisabledSkills()); request.setConfigDir(config.getConfigDir()); config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery); + config.getSkipEmbeddingRetrieval().ifPresent(request::setSkipEmbeddingRetrieval); + if (config.getOrganizationCustomInstructions() != null) { + request.setOrganizationCustomInstructions(config.getOrganizationCustomInstructions()); + } + config.getEnableOnDemandInstructionDiscovery().ifPresent(request::setEnableOnDemandInstructionDiscovery); + config.getEnableFileHooks().ifPresent(request::setEnableFileHooks); + config.getEnableHostGitOperations().ifPresent(request::setEnableHostGitOperations); + config.getEnableSessionStore().ifPresent(request::setEnableSessionStore); + config.getEnableSkills().ifPresent(request::setEnableSkills); request.setModelCapabilities(config.getModelCapabilities()); if (config.getCommands() != null && !config.getCommands().isEmpty()) { @@ -212,6 +221,15 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setWorkingDirectory(config.getWorkingDirectory()); request.setConfigDir(config.getConfigDir()); config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery); + config.getSkipEmbeddingRetrieval().ifPresent(request::setSkipEmbeddingRetrieval); + if (config.getOrganizationCustomInstructions() != null) { + request.setOrganizationCustomInstructions(config.getOrganizationCustomInstructions()); + } + config.getEnableOnDemandInstructionDiscovery().ifPresent(request::setEnableOnDemandInstructionDiscovery); + config.getEnableFileHooks().ifPresent(request::setEnableFileHooks); + config.getEnableHostGitOperations().ifPresent(request::setEnableHostGitOperations); + config.getEnableSessionStore().ifPresent(request::setEnableSessionStore); + config.getEnableSkills().ifPresent(request::setEnableSkills); if (config.isDisableResume()) { request.setDisableResume(true); } diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index ae353f8e4..90b192bae 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -105,6 +105,34 @@ public final class CreateSessionRequest { @JsonProperty("enableConfigDiscovery") private Boolean enableConfigDiscovery; + @JsonProperty("skipEmbeddingRetrieval") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean skipEmbeddingRetrieval; + + @JsonProperty("organizationCustomInstructions") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String organizationCustomInstructions; + + @JsonProperty("enableOnDemandInstructionDiscovery") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableOnDemandInstructionDiscovery; + + @JsonProperty("enableFileHooks") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableFileHooks; + + @JsonProperty("enableHostGitOperations") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableHostGitOperations; + + @JsonProperty("enableSessionStore") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableSessionStore; + + @JsonProperty("enableSkills") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableSkills; + @JsonProperty("commands") private List commands; @@ -439,6 +467,111 @@ public void clearEnableConfigDiscovery() { this.enableConfigDiscovery = null; } + /** Gets skip embedding retrieval flag. @return the flag */ + public Boolean getSkipEmbeddingRetrieval() { + return skipEmbeddingRetrieval; + } + + /** Sets skip embedding retrieval flag. @param skipEmbeddingRetrieval the flag */ + public void setSkipEmbeddingRetrieval(boolean skipEmbeddingRetrieval) { + this.skipEmbeddingRetrieval = skipEmbeddingRetrieval; + } + + /** Clears the skipEmbeddingRetrieval setting, reverting to the default behavior. */ + public void clearSkipEmbeddingRetrieval() { + this.skipEmbeddingRetrieval = null; + } + + /** Gets organization custom instructions. @return the instructions */ + public String getOrganizationCustomInstructions() { + return organizationCustomInstructions; + } + + /** Sets organization custom instructions. @param organizationCustomInstructions the instructions */ + public void setOrganizationCustomInstructions(String organizationCustomInstructions) { + this.organizationCustomInstructions = organizationCustomInstructions; + } + + /** Gets enable on-demand instruction discovery flag. @return the flag */ + public Boolean getEnableOnDemandInstructionDiscovery() { + return enableOnDemandInstructionDiscovery; + } + + /** + * Sets enable on-demand instruction discovery flag. @param + * enableOnDemandInstructionDiscovery the flag + */ + public void setEnableOnDemandInstructionDiscovery(boolean enableOnDemandInstructionDiscovery) { + this.enableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery; + } + + /** + * Clears the enableOnDemandInstructionDiscovery setting, reverting to the default behavior. + */ + public void clearEnableOnDemandInstructionDiscovery() { + this.enableOnDemandInstructionDiscovery = null; + } + + /** Gets enable file hooks flag. @return the flag */ + public Boolean getEnableFileHooks() { + return enableFileHooks; + } + + /** Sets enable file hooks flag. @param enableFileHooks the flag */ + public void setEnableFileHooks(boolean enableFileHooks) { + this.enableFileHooks = enableFileHooks; + } + + /** Clears the enableFileHooks setting, reverting to the default behavior. */ + public void clearEnableFileHooks() { + this.enableFileHooks = null; + } + + /** Gets enable host git operations flag. @return the flag */ + public Boolean getEnableHostGitOperations() { + return enableHostGitOperations; + } + + /** Sets enable host git operations flag. @param enableHostGitOperations the flag */ + public void setEnableHostGitOperations(boolean enableHostGitOperations) { + this.enableHostGitOperations = enableHostGitOperations; + } + + /** Clears the enableHostGitOperations setting, reverting to the default behavior. */ + public void clearEnableHostGitOperations() { + this.enableHostGitOperations = null; + } + + /** Gets enable session store flag. @return the flag */ + public Boolean getEnableSessionStore() { + return enableSessionStore; + } + + /** Sets enable session store flag. @param enableSessionStore the flag */ + public void setEnableSessionStore(boolean enableSessionStore) { + this.enableSessionStore = enableSessionStore; + } + + /** Clears the enableSessionStore setting, reverting to the default behavior. */ + public void clearEnableSessionStore() { + this.enableSessionStore = null; + } + + /** Gets enable skills flag. @return the flag */ + public Boolean getEnableSkills() { + return enableSkills; + } + + /** Sets enable skills flag. @param enableSkills the flag */ + public void setEnableSkills(boolean enableSkills) { + this.enableSkills = enableSkills; + } + + /** Clears the enableSkills setting, reverting to the default behavior. */ + public void clearEnableSkills() { + this.enableSkills = null; + } + /** Gets include sub-agent streaming events flag. @return the flag */ public Boolean getIncludeSubAgentStreamingEvents() { return includeSubAgentStreamingEvents; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index e110af3ff..e5701ab6f 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -54,6 +54,13 @@ public class ResumeSessionConfig { private String workingDirectory; private String configDir; private Boolean enableConfigDiscovery; + private Boolean skipEmbeddingRetrieval; + private String organizationCustomInstructions; + private Boolean enableOnDemandInstructionDiscovery; + private Boolean enableFileHooks; + private Boolean enableHostGitOperations; + private Boolean enableSessionStore; + private Boolean enableSkills; private boolean disableResume; private boolean streaming; private Boolean includeSubAgentStreamingEvents; @@ -451,6 +458,238 @@ public ResumeSessionConfig clearEnableConfigDiscovery() { return this; } + /** + * Gets whether embedding-based retrieval is skipped. + * + * @return an {@link java.util.Optional} containing {@code true} to skip + * embedding retrieval or {@code false} to force it, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getSkipEmbeddingRetrieval() { + return Optional.ofNullable(skipEmbeddingRetrieval); + } + + /** + * Sets whether to skip embedding-based retrieval. + * + * @param skipEmbeddingRetrieval + * {@code true} to skip embedding retrieval, {@code false} to keep + * it enabled + * @return this config for method chaining + */ + public ResumeSessionConfig setSkipEmbeddingRetrieval(boolean skipEmbeddingRetrieval) { + this.skipEmbeddingRetrieval = skipEmbeddingRetrieval; + return this; + } + + /** + * Clears the skipEmbeddingRetrieval setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearSkipEmbeddingRetrieval() { + this.skipEmbeddingRetrieval = null; + return this; + } + + /** + * Gets the organization-level custom instructions. + * + * @return the organization-level custom instructions, or {@code null} if not + * set + */ + public String getOrganizationCustomInstructions() { + return organizationCustomInstructions; + } + + /** + * Sets organization-level custom instructions. + * + * @param organizationCustomInstructions + * the organization-level custom instructions + * @return this config for method chaining + */ + public ResumeSessionConfig setOrganizationCustomInstructions(String organizationCustomInstructions) { + this.organizationCustomInstructions = organizationCustomInstructions; + return this; + } + + /** + * Gets whether on-demand instruction file discovery is enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable + * on-demand discovery or {@code false} to disable it, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableOnDemandInstructionDiscovery() { + return Optional.ofNullable(enableOnDemandInstructionDiscovery); + } + + /** + * Sets whether instruction files are discovered on demand. + * + * @param enableOnDemandInstructionDiscovery + * {@code true} to enable on-demand instruction discovery, + * {@code false} to disable it + * @return this config for method chaining + */ + public ResumeSessionConfig setEnableOnDemandInstructionDiscovery(boolean enableOnDemandInstructionDiscovery) { + this.enableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery; + return this; + } + + /** + * Clears the enableOnDemandInstructionDiscovery setting, reverting to the + * default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableOnDemandInstructionDiscovery() { + this.enableOnDemandInstructionDiscovery = null; + return this; + } + + /** + * Gets whether file-based hooks are enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable file + * hooks or {@code false} to disable them, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableFileHooks() { + return Optional.ofNullable(enableFileHooks); + } + + /** + * Sets whether file-based hooks from {@code .github/hooks/} are enabled. + * + * @param enableFileHooks + * {@code true} to enable file hooks, {@code false} to disable them + * @return this config for method chaining + */ + public ResumeSessionConfig setEnableFileHooks(boolean enableFileHooks) { + this.enableFileHooks = enableFileHooks; + return this; + } + + /** + * Clears the enableFileHooks setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableFileHooks() { + this.enableFileHooks = null; + return this; + } + + /** + * Gets whether host git operations are enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable host + * git operations or {@code false} to disable them, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableHostGitOperations() { + return Optional.ofNullable(enableHostGitOperations); + } + + /** + * Sets whether git operations on the host filesystem are enabled. + * + * @param enableHostGitOperations + * {@code true} to enable host git operations, {@code false} to + * disable them + * @return this config for method chaining + */ + public ResumeSessionConfig setEnableHostGitOperations(boolean enableHostGitOperations) { + this.enableHostGitOperations = enableHostGitOperations; + return this; + } + + /** + * Clears the enableHostGitOperations setting, reverting to the default + * behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableHostGitOperations() { + this.enableHostGitOperations = null; + return this; + } + + /** + * Gets whether the cross-session store is enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable the + * session store or {@code false} to disable it, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableSessionStore() { + return Optional.ofNullable(enableSessionStore); + } + + /** + * Sets whether the cross-session store is enabled. + * + * @param enableSessionStore + * {@code true} to enable the session store, {@code false} to disable + * it + * @return this config for method chaining + */ + public ResumeSessionConfig setEnableSessionStore(boolean enableSessionStore) { + this.enableSessionStore = enableSessionStore; + return this; + } + + /** + * Clears the enableSessionStore setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableSessionStore() { + this.enableSessionStore = null; + return this; + } + + /** + * Gets whether skill loading is enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable skill + * loading or {@code false} to disable it, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableSkills() { + return Optional.ofNullable(enableSkills); + } + + /** + * Sets whether skill loading is enabled. + * + * @param enableSkills + * {@code true} to enable skill loading, {@code false} to disable it + * @return this config for method chaining + */ + public ResumeSessionConfig setEnableSkills(boolean enableSkills) { + this.enableSkills = enableSkills; + return this; + } + + /** + * Clears the enableSkills setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableSkills() { + this.enableSkills = null; + return this; + } + /** * Gets whether sub-agent streaming events are included. * @@ -945,6 +1184,13 @@ public ResumeSessionConfig clone() { copy.workingDirectory = this.workingDirectory; copy.configDir = this.configDir; copy.enableConfigDiscovery = this.enableConfigDiscovery; + copy.skipEmbeddingRetrieval = this.skipEmbeddingRetrieval; + copy.organizationCustomInstructions = this.organizationCustomInstructions; + copy.enableOnDemandInstructionDiscovery = this.enableOnDemandInstructionDiscovery; + copy.enableFileHooks = this.enableFileHooks; + copy.enableHostGitOperations = this.enableHostGitOperations; + copy.enableSessionStore = this.enableSessionStore; + copy.enableSkills = this.enableSkills; copy.disableResume = this.disableResume; copy.streaming = this.streaming; copy.includeSubAgentStreamingEvents = this.includeSubAgentStreamingEvents; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index bd8613d54..86212ead4 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -74,6 +74,34 @@ public final class ResumeSessionRequest { @JsonProperty("enableConfigDiscovery") private Boolean enableConfigDiscovery; + @JsonProperty("skipEmbeddingRetrieval") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean skipEmbeddingRetrieval; + + @JsonProperty("organizationCustomInstructions") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String organizationCustomInstructions; + + @JsonProperty("enableOnDemandInstructionDiscovery") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableOnDemandInstructionDiscovery; + + @JsonProperty("enableFileHooks") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableFileHooks; + + @JsonProperty("enableHostGitOperations") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableHostGitOperations; + + @JsonProperty("enableSessionStore") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableSessionStore; + + @JsonProperty("enableSkills") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableSkills; + @JsonProperty("disableResume") private Boolean disableResume; @@ -333,6 +361,111 @@ public void clearEnableConfigDiscovery() { this.enableConfigDiscovery = null; } + /** Gets skip embedding retrieval flag. @return the flag */ + public Boolean getSkipEmbeddingRetrieval() { + return skipEmbeddingRetrieval; + } + + /** Sets skip embedding retrieval flag. @param skipEmbeddingRetrieval the flag */ + public void setSkipEmbeddingRetrieval(boolean skipEmbeddingRetrieval) { + this.skipEmbeddingRetrieval = skipEmbeddingRetrieval; + } + + /** Clears the skipEmbeddingRetrieval setting, reverting to the default behavior. */ + public void clearSkipEmbeddingRetrieval() { + this.skipEmbeddingRetrieval = null; + } + + /** Gets organization custom instructions. @return the instructions */ + public String getOrganizationCustomInstructions() { + return organizationCustomInstructions; + } + + /** Sets organization custom instructions. @param organizationCustomInstructions the instructions */ + public void setOrganizationCustomInstructions(String organizationCustomInstructions) { + this.organizationCustomInstructions = organizationCustomInstructions; + } + + /** Gets enable on-demand instruction discovery flag. @return the flag */ + public Boolean getEnableOnDemandInstructionDiscovery() { + return enableOnDemandInstructionDiscovery; + } + + /** + * Sets enable on-demand instruction discovery flag. @param + * enableOnDemandInstructionDiscovery the flag + */ + public void setEnableOnDemandInstructionDiscovery(boolean enableOnDemandInstructionDiscovery) { + this.enableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery; + } + + /** + * Clears the enableOnDemandInstructionDiscovery setting, reverting to the default behavior. + */ + public void clearEnableOnDemandInstructionDiscovery() { + this.enableOnDemandInstructionDiscovery = null; + } + + /** Gets enable file hooks flag. @return the flag */ + public Boolean getEnableFileHooks() { + return enableFileHooks; + } + + /** Sets enable file hooks flag. @param enableFileHooks the flag */ + public void setEnableFileHooks(boolean enableFileHooks) { + this.enableFileHooks = enableFileHooks; + } + + /** Clears the enableFileHooks setting, reverting to the default behavior. */ + public void clearEnableFileHooks() { + this.enableFileHooks = null; + } + + /** Gets enable host git operations flag. @return the flag */ + public Boolean getEnableHostGitOperations() { + return enableHostGitOperations; + } + + /** Sets enable host git operations flag. @param enableHostGitOperations the flag */ + public void setEnableHostGitOperations(boolean enableHostGitOperations) { + this.enableHostGitOperations = enableHostGitOperations; + } + + /** Clears the enableHostGitOperations setting, reverting to the default behavior. */ + public void clearEnableHostGitOperations() { + this.enableHostGitOperations = null; + } + + /** Gets enable session store flag. @return the flag */ + public Boolean getEnableSessionStore() { + return enableSessionStore; + } + + /** Sets enable session store flag. @param enableSessionStore the flag */ + public void setEnableSessionStore(boolean enableSessionStore) { + this.enableSessionStore = enableSessionStore; + } + + /** Clears the enableSessionStore setting, reverting to the default behavior. */ + public void clearEnableSessionStore() { + this.enableSessionStore = null; + } + + /** Gets enable skills flag. @return the flag */ + public Boolean getEnableSkills() { + return enableSkills; + } + + /** Sets enable skills flag. @param enableSkills the flag */ + public void setEnableSkills(boolean enableSkills) { + this.enableSkills = enableSkills; + } + + /** Clears the enableSkills setting, reverting to the default behavior. */ + public void clearEnableSkills() { + this.enableSkills = null; + } + /** Gets disable resume flag. @return the flag */ public Boolean getDisableResume() { return disableResume; diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 3ba19d8c2..e89bdcb30 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -64,6 +64,13 @@ public class SessionConfig { private List disabledSkills; private String configDir; private Boolean enableConfigDiscovery; + private Boolean skipEmbeddingRetrieval; + private String organizationCustomInstructions; + private Boolean enableOnDemandInstructionDiscovery; + private Boolean enableFileHooks; + private Boolean enableHostGitOperations; + private Boolean enableSessionStore; + private Boolean enableSkills; private ModelCapabilitiesOverride modelCapabilities; private Consumer onEvent; private List commands; @@ -712,6 +719,238 @@ public SessionConfig clearEnableConfigDiscovery() { return this; } + /** + * Gets whether embedding-based retrieval is skipped. + * + * @return an {@link java.util.Optional} containing {@code true} to skip + * embedding retrieval or {@code false} to force it, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getSkipEmbeddingRetrieval() { + return Optional.ofNullable(skipEmbeddingRetrieval); + } + + /** + * Sets whether to skip embedding-based retrieval. + * + * @param skipEmbeddingRetrieval + * {@code true} to skip embedding retrieval, {@code false} to keep + * it enabled + * @return this config instance for method chaining + */ + public SessionConfig setSkipEmbeddingRetrieval(boolean skipEmbeddingRetrieval) { + this.skipEmbeddingRetrieval = skipEmbeddingRetrieval; + return this; + } + + /** + * Clears the skipEmbeddingRetrieval setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearSkipEmbeddingRetrieval() { + this.skipEmbeddingRetrieval = null; + return this; + } + + /** + * Gets the organization-level custom instructions. + * + * @return the organization-level custom instructions, or {@code null} if not + * set + */ + public String getOrganizationCustomInstructions() { + return organizationCustomInstructions; + } + + /** + * Sets organization-level custom instructions. + * + * @param organizationCustomInstructions + * the organization-level custom instructions + * @return this config instance for method chaining + */ + public SessionConfig setOrganizationCustomInstructions(String organizationCustomInstructions) { + this.organizationCustomInstructions = organizationCustomInstructions; + return this; + } + + /** + * Gets whether on-demand instruction file discovery is enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable + * on-demand discovery or {@code false} to disable it, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableOnDemandInstructionDiscovery() { + return Optional.ofNullable(enableOnDemandInstructionDiscovery); + } + + /** + * Sets whether instruction files are discovered on demand. + * + * @param enableOnDemandInstructionDiscovery + * {@code true} to enable on-demand instruction discovery, + * {@code false} to disable it + * @return this config instance for method chaining + */ + public SessionConfig setEnableOnDemandInstructionDiscovery(boolean enableOnDemandInstructionDiscovery) { + this.enableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery; + return this; + } + + /** + * Clears the enableOnDemandInstructionDiscovery setting, reverting to the + * default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableOnDemandInstructionDiscovery() { + this.enableOnDemandInstructionDiscovery = null; + return this; + } + + /** + * Gets whether file-based hooks are enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable file + * hooks or {@code false} to disable them, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableFileHooks() { + return Optional.ofNullable(enableFileHooks); + } + + /** + * Sets whether file-based hooks from {@code .github/hooks/} are enabled. + * + * @param enableFileHooks + * {@code true} to enable file hooks, {@code false} to disable them + * @return this config instance for method chaining + */ + public SessionConfig setEnableFileHooks(boolean enableFileHooks) { + this.enableFileHooks = enableFileHooks; + return this; + } + + /** + * Clears the enableFileHooks setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableFileHooks() { + this.enableFileHooks = null; + return this; + } + + /** + * Gets whether host git operations are enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable host + * git operations or {@code false} to disable them, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableHostGitOperations() { + return Optional.ofNullable(enableHostGitOperations); + } + + /** + * Sets whether git operations on the host filesystem are enabled. + * + * @param enableHostGitOperations + * {@code true} to enable host git operations, {@code false} to + * disable them + * @return this config instance for method chaining + */ + public SessionConfig setEnableHostGitOperations(boolean enableHostGitOperations) { + this.enableHostGitOperations = enableHostGitOperations; + return this; + } + + /** + * Clears the enableHostGitOperations setting, reverting to the default + * behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableHostGitOperations() { + this.enableHostGitOperations = null; + return this; + } + + /** + * Gets whether the cross-session store is enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable the + * session store or {@code false} to disable it, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableSessionStore() { + return Optional.ofNullable(enableSessionStore); + } + + /** + * Sets whether the cross-session store is enabled. + * + * @param enableSessionStore + * {@code true} to enable the session store, {@code false} to disable + * it + * @return this config instance for method chaining + */ + public SessionConfig setEnableSessionStore(boolean enableSessionStore) { + this.enableSessionStore = enableSessionStore; + return this; + } + + /** + * Clears the enableSessionStore setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableSessionStore() { + this.enableSessionStore = null; + return this; + } + + /** + * Gets whether skill loading is enabled. + * + * @return an {@link java.util.Optional} containing {@code true} to enable skill + * loading or {@code false} to disable it, or + * {@link java.util.Optional#empty()} to use the runtime default + */ + @JsonIgnore + public Optional getEnableSkills() { + return Optional.ofNullable(enableSkills); + } + + /** + * Sets whether skill loading is enabled. + * + * @param enableSkills + * {@code true} to enable skill loading, {@code false} to disable it + * @return this config instance for method chaining + */ + public SessionConfig setEnableSkills(boolean enableSkills) { + this.enableSkills = enableSkills; + return this; + } + + /** + * Clears the enableSkills setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableSkills() { + this.enableSkills = null; + return this; + } + /** * Gets whether sub-agent streaming events are included. * @@ -1051,6 +1290,13 @@ public SessionConfig clone() { copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null; copy.configDir = this.configDir; copy.enableConfigDiscovery = this.enableConfigDiscovery; + copy.skipEmbeddingRetrieval = this.skipEmbeddingRetrieval; + copy.organizationCustomInstructions = this.organizationCustomInstructions; + copy.enableOnDemandInstructionDiscovery = this.enableOnDemandInstructionDiscovery; + copy.enableFileHooks = this.enableFileHooks; + copy.enableHostGitOperations = this.enableHostGitOperations; + copy.enableSessionStore = this.enableSessionStore; + copy.enableSkills = this.enableSkills; copy.modelCapabilities = this.modelCapabilities; copy.onEvent = this.onEvent; copy.commands = this.commands != null ? new ArrayList<>(this.commands) : null; diff --git a/java/src/test/java/com/github/copilot/ConfigCloneTest.java b/java/src/test/java/com/github/copilot/ConfigCloneTest.java index f26f67ed9..478eb0187 100644 --- a/java/src/test/java/com/github/copilot/ConfigCloneTest.java +++ b/java/src/test/java/com/github/copilot/ConfigCloneTest.java @@ -215,6 +215,28 @@ void sessionConfigEnableSessionTelemetryDefaultIsNull() { assertTrue(cloned.getEnableSessionTelemetry().isEmpty()); } + @Test + void sessionConfigNewSessionFieldsCopied() { + SessionConfig original = new SessionConfig() + .setSkipEmbeddingRetrieval(true) + .setOrganizationCustomInstructions("Org instructions") + .setEnableOnDemandInstructionDiscovery(false) + .setEnableFileHooks(true) + .setEnableHostGitOperations(false) + .setEnableSessionStore(true) + .setEnableSkills(false); + + SessionConfig cloned = original.clone(); + + assertTrue(cloned.getSkipEmbeddingRetrieval().orElse(false)); + assertEquals("Org instructions", cloned.getOrganizationCustomInstructions()); + assertFalse(cloned.getEnableOnDemandInstructionDiscovery().orElse(true)); + assertTrue(cloned.getEnableFileHooks().orElse(false)); + assertFalse(cloned.getEnableHostGitOperations().orElse(true)); + assertTrue(cloned.getEnableSessionStore().orElse(false)); + assertFalse(cloned.getEnableSkills().orElse(true)); + } + @Test void resumeSessionConfigEnableSessionTelemetryCopied() { ResumeSessionConfig original = new ResumeSessionConfig(); @@ -234,6 +256,28 @@ void resumeSessionConfigEnableSessionTelemetryDefaultIsNull() { assertTrue(cloned.getEnableSessionTelemetry().isEmpty()); } + @Test + void resumeSessionConfigNewSessionFieldsCopied() { + ResumeSessionConfig original = new ResumeSessionConfig() + .setSkipEmbeddingRetrieval(false) + .setOrganizationCustomInstructions("Resume org instructions") + .setEnableOnDemandInstructionDiscovery(true) + .setEnableFileHooks(false) + .setEnableHostGitOperations(true) + .setEnableSessionStore(false) + .setEnableSkills(true); + + ResumeSessionConfig cloned = original.clone(); + + assertFalse(cloned.getSkipEmbeddingRetrieval().orElse(true)); + assertEquals("Resume org instructions", cloned.getOrganizationCustomInstructions()); + assertTrue(cloned.getEnableOnDemandInstructionDiscovery().orElse(false)); + assertFalse(cloned.getEnableFileHooks().orElse(true)); + assertTrue(cloned.getEnableHostGitOperations().orElse(false)); + assertFalse(cloned.getEnableSessionStore().orElse(true)); + assertTrue(cloned.getEnableSkills().orElse(false)); + } + @Test void clonePreservesNullFields() { CopilotClientOptions opts = new CopilotClientOptions(); diff --git a/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java b/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java index 1db593dbe..aa10bb17e 100644 --- a/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java +++ b/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java @@ -353,6 +353,34 @@ void sessionConfig_includeSubAgentStreamingEventsValue() { assertTrue(cfg.getIncludeSubAgentStreamingEvents().get()); } + @Test + void sessionConfig_newSessionFieldsValue() { + var cfg = new SessionConfig(); + assertTrue(cfg.getSkipEmbeddingRetrieval().isEmpty()); + assertNull(cfg.getOrganizationCustomInstructions()); + assertTrue(cfg.getEnableOnDemandInstructionDiscovery().isEmpty()); + assertTrue(cfg.getEnableFileHooks().isEmpty()); + assertTrue(cfg.getEnableHostGitOperations().isEmpty()); + assertTrue(cfg.getEnableSessionStore().isEmpty()); + assertTrue(cfg.getEnableSkills().isEmpty()); + + cfg.setSkipEmbeddingRetrieval(true); + cfg.setOrganizationCustomInstructions("Org instructions"); + cfg.setEnableOnDemandInstructionDiscovery(false); + cfg.setEnableFileHooks(true); + cfg.setEnableHostGitOperations(false); + cfg.setEnableSessionStore(true); + cfg.setEnableSkills(false); + + assertTrue(cfg.getSkipEmbeddingRetrieval().get()); + assertEquals("Org instructions", cfg.getOrganizationCustomInstructions()); + assertFalse(cfg.getEnableOnDemandInstructionDiscovery().get()); + assertTrue(cfg.getEnableFileHooks().get()); + assertFalse(cfg.getEnableHostGitOperations().get()); + assertTrue(cfg.getEnableSessionStore().get()); + assertFalse(cfg.getEnableSkills().get()); + } + @Test void resumeSessionConfig_enableSessionTelemetryValue() { var cfg = new ResumeSessionConfig(); @@ -383,6 +411,34 @@ void resumeSessionConfig_includeSubAgentStreamingEventsValue() { assertFalse(cfg.getIncludeSubAgentStreamingEvents().get()); } + @Test + void resumeSessionConfig_newSessionFieldsValue() { + var cfg = new ResumeSessionConfig(); + assertTrue(cfg.getSkipEmbeddingRetrieval().isEmpty()); + assertNull(cfg.getOrganizationCustomInstructions()); + assertTrue(cfg.getEnableOnDemandInstructionDiscovery().isEmpty()); + assertTrue(cfg.getEnableFileHooks().isEmpty()); + assertTrue(cfg.getEnableHostGitOperations().isEmpty()); + assertTrue(cfg.getEnableSessionStore().isEmpty()); + assertTrue(cfg.getEnableSkills().isEmpty()); + + cfg.setSkipEmbeddingRetrieval(false); + cfg.setOrganizationCustomInstructions("Resume org instructions"); + cfg.setEnableOnDemandInstructionDiscovery(true); + cfg.setEnableFileHooks(false); + cfg.setEnableHostGitOperations(true); + cfg.setEnableSessionStore(false); + cfg.setEnableSkills(true); + + assertFalse(cfg.getSkipEmbeddingRetrieval().get()); + assertEquals("Resume org instructions", cfg.getOrganizationCustomInstructions()); + assertTrue(cfg.getEnableOnDemandInstructionDiscovery().get()); + assertFalse(cfg.getEnableFileHooks().get()); + assertTrue(cfg.getEnableHostGitOperations().get()); + assertFalse(cfg.getEnableSessionStore().get()); + assertTrue(cfg.getEnableSkills().get()); + } + @Test void infiniteSessionConfig_thresholdValues() { var cfg = new InfiniteSessionConfig(); diff --git a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java index 49a4c9c30..6f44b2f95 100644 --- a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -212,6 +212,50 @@ void testBuildResumeRequestSetsClientName() { assertEquals("my-app", request.getClientName()); } + @Test + void testBuildCreateRequestPropagatesNewSessionFields() { + var config = new SessionConfig() + .setSkipEmbeddingRetrieval(true) + .setOrganizationCustomInstructions("Create org instructions") + .setEnableOnDemandInstructionDiscovery(false) + .setEnableFileHooks(true) + .setEnableHostGitOperations(false) + .setEnableSessionStore(true) + .setEnableSkills(false); + + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + + assertTrue(request.getSkipEmbeddingRetrieval()); + assertEquals("Create org instructions", request.getOrganizationCustomInstructions()); + assertFalse(request.getEnableOnDemandInstructionDiscovery()); + assertTrue(request.getEnableFileHooks()); + assertFalse(request.getEnableHostGitOperations()); + assertTrue(request.getEnableSessionStore()); + assertFalse(request.getEnableSkills()); + } + + @Test + void testBuildResumeRequestPropagatesNewSessionFields() { + var config = new ResumeSessionConfig() + .setSkipEmbeddingRetrieval(false) + .setOrganizationCustomInstructions("Resume org instructions") + .setEnableOnDemandInstructionDiscovery(true) + .setEnableFileHooks(false) + .setEnableHostGitOperations(true) + .setEnableSessionStore(false) + .setEnableSkills(true); + + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-11", config); + + assertFalse(request.getSkipEmbeddingRetrieval()); + assertEquals("Resume org instructions", request.getOrganizationCustomInstructions()); + assertTrue(request.getEnableOnDemandInstructionDiscovery()); + assertFalse(request.getEnableFileHooks()); + assertTrue(request.getEnableHostGitOperations()); + assertFalse(request.getEnableSessionStore()); + assertTrue(request.getEnableSkills()); + } + // ========================================================================= // configureSession (ResumeSessionConfig overload) // ========================================================================= diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index a18915014..813b2cd08 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -891,7 +891,15 @@ export class CopilotClient { /** Mode-specific defaults spread under the caller's config (app values win). */ private configDefaultsForMode(): Partial { if (this.options.mode === "empty") { - return { enableSessionTelemetry: false }; + return { + enableSessionTelemetry: false, + skipEmbeddingRetrieval: true, + enableOnDemandInstructionDiscovery: false, + enableFileHooks: false, + enableHostGitOperations: false, + enableSessionStore: false, + enableSkills: false, + }; } return {}; } @@ -1087,6 +1095,14 @@ export class CopilotClient { agent: config.agent, configDir: config.configDir, enableConfigDiscovery: config.enableConfigDiscovery, + skipEmbeddingRetrieval: config.skipEmbeddingRetrieval, + organizationCustomInstructions: config.organizationCustomInstructions, + enableOnDemandInstructionDiscovery: + config.enableOnDemandInstructionDiscovery, + enableFileHooks: config.enableFileHooks, + enableHostGitOperations: config.enableHostGitOperations, + enableSessionStore: config.enableSessionStore, + enableSkills: config.enableSkills, skillDirectories: config.skillDirectories, instructionDirectories: config.instructionDirectories, disabledSkills: config.disabledSkills, @@ -1227,6 +1243,14 @@ export class CopilotClient { workingDirectory: config.workingDirectory, configDir: config.configDir, enableConfigDiscovery: config.enableConfigDiscovery, + skipEmbeddingRetrieval: config.skipEmbeddingRetrieval, + organizationCustomInstructions: config.organizationCustomInstructions, + enableOnDemandInstructionDiscovery: + config.enableOnDemandInstructionDiscovery, + enableFileHooks: config.enableFileHooks, + enableHostGitOperations: config.enableHostGitOperations, + enableSessionStore: config.enableSessionStore, + enableSkills: config.enableSkills, streaming: config.streaming, includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true, mcpServers: toWireMcpServers(config.mcpServers), diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 7aeb0b162..930bccaab 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1807,6 +1807,64 @@ export interface SessionConfigBase { */ gitHubToken?: string; + /** + * When true, skips embedding-based retrieval for this session. + * Use in multitenant deployments to prevent cross-session information leakage + * through the shared embedding cache. + */ + skipEmbeddingRetrieval?: boolean; + + /** + * Organization-level custom instructions to include in the system prompt. + * Allows hosts to inject organization-specific guidance without relying on + * filesystem-based instruction discovery. + */ + organizationCustomInstructions?: string; + + /** + * When true, enables on-demand discovery of instruction files (AGENTS.md, + * .github/copilot-instructions.md, etc.) after successful file views. + * + * @default false + */ + enableOnDemandInstructionDiscovery?: boolean; + + /** + * When true, enables loading of file-based hooks from `.github/hooks/`. + * This is separate from the `hooks` callback parameter which gates SDK + * hook event registration. + * + * @default false + */ + enableFileHooks?: boolean; + + /** + * When true, enables git operations on the host filesystem (branch detection, + * file status, commit history). When false, no git context is surfaced in + * the system prompt. + * + * @default false + */ + enableHostGitOperations?: boolean; + + /** + * When true, enables the cross-session store for search and retrieval + * across sessions. When false, session content is not written to or + * read from the shared session store. + * + * @default false + */ + enableSessionStore?: boolean; + + /** + * When true, enables skill loading (including builtin skills and discovered + * skill directories). When false, no skills are loaded regardless of + * `skillDirectories` or `enableConfigDiscovery` settings. + * + * @default false + */ + enableSkills?: boolean; + /** * Per-session remote behavior control: * - `"off"` — local only, no remote export (default) diff --git a/nodejs/test/toolSet.test.ts b/nodejs/test/toolSet.test.ts index ed0d05771..490224c66 100644 --- a/nodejs/test/toolSet.test.ts +++ b/nodejs/test/toolSet.test.ts @@ -460,6 +460,88 @@ describe("Empty-mode safe defaults", () => { }); }); + it("applies restrictive defaults for granular multitenancy flags in empty mode", async () => { + const { client, spy } = await setupClient(); + await client.createSession({ + onPermissionRequest: approveAll, + availableTools: new ToolSet().addBuiltIn(BuiltInTools.Isolated), + }); + const payload = createPayload(spy); + expect(payload.skipEmbeddingRetrieval).toBe(true); + expect(payload.enableOnDemandInstructionDiscovery).toBe(false); + expect(payload.enableFileHooks).toBe(false); + expect(payload.enableHostGitOperations).toBe(false); + expect(payload.enableSessionStore).toBe(false); + expect(payload.enableSkills).toBe(false); + }); + + it("respects app-supplied overrides for granular multitenancy flags in empty mode", async () => { + const { client, spy } = await setupClient(); + await client.createSession({ + onPermissionRequest: approveAll, + availableTools: new ToolSet().addBuiltIn(BuiltInTools.Isolated), + skipEmbeddingRetrieval: false, + enableOnDemandInstructionDiscovery: true, + enableFileHooks: true, + enableHostGitOperations: true, + enableSessionStore: true, + enableSkills: true, + }); + const payload = createPayload(spy); + expect(payload.skipEmbeddingRetrieval).toBe(false); + expect(payload.enableOnDemandInstructionDiscovery).toBe(true); + expect(payload.enableFileHooks).toBe(true); + expect(payload.enableHostGitOperations).toBe(true); + expect(payload.enableSessionStore).toBe(true); + expect(payload.enableSkills).toBe(true); + }); + + it("passes organizationCustomInstructions through on create", async () => { + const { client, spy } = await setupClient(); + await client.createSession({ + onPermissionRequest: approveAll, + availableTools: new ToolSet().addBuiltIn(BuiltInTools.Isolated), + organizationCustomInstructions: "Follow org coding standards", + }); + const payload = createPayload(spy); + expect(payload.organizationCustomInstructions).toBe("Follow org coding standards"); + }); + + it("does NOT apply granular multitenancy flag defaults in copilot-cli mode", async () => { + const { client, spy } = await setupClient("copilot-cli"); + await client.createSession({ + onPermissionRequest: approveAll, + availableTools: ["builtin:bash"], + }); + const payload = createPayload(spy); + expect(payload.skipEmbeddingRetrieval).toBeUndefined(); + expect(payload.enableOnDemandInstructionDiscovery).toBeUndefined(); + expect(payload.enableFileHooks).toBeUndefined(); + expect(payload.enableHostGitOperations).toBeUndefined(); + expect(payload.enableSessionStore).toBeUndefined(); + expect(payload.enableSkills).toBeUndefined(); + }); + + it("applies granular multitenancy flag defaults on session.resume in empty mode", async () => { + const { client, spy } = await setupClient(); + const session = await client.createSession({ + onPermissionRequest: approveAll, + availableTools: new ToolSet().addBuiltIn(BuiltInTools.Isolated), + }); + spy.mockClear(); + await client.resumeSession(session.sessionId, { + onPermissionRequest: approveAll, + availableTools: new ToolSet().addBuiltIn(BuiltInTools.Isolated), + }); + const resumePayload = spy.mock.calls.find(([m]) => m === "session.resume")![1] as any; + expect(resumePayload.skipEmbeddingRetrieval).toBe(true); + expect(resumePayload.enableOnDemandInstructionDiscovery).toBe(false); + expect(resumePayload.enableFileHooks).toBe(false); + expect(resumePayload.enableHostGitOperations).toBe(false); + expect(resumePayload.enableSessionStore).toBe(false); + expect(resumePayload.enableSkills).toBe(false); + }); + it("forwards the four flags in copilot-cli mode when the app sets them", async () => { const { client, spy } = await setupClient("copilot-cli"); await client.createSession({ diff --git a/python/copilot/_mode.py b/python/copilot/_mode.py index 23e392239..64e500b57 100644 --- a/python/copilot/_mode.py +++ b/python/copilot/_mode.py @@ -172,16 +172,72 @@ def _system_message_for_mode( return out -def _enable_session_telemetry_default( +def _empty_mode_bool_default( mode: CopilotClientMode | None, supplied: bool | None, + empty_default: bool, ) -> bool | None: - """Empty mode defaults telemetry to False; caller value wins.""" if mode == "empty" and supplied is None: - return False + return empty_default return supplied +def _enable_session_telemetry_default( + mode: CopilotClientMode | None, + supplied: bool | None, +) -> bool | None: + """Empty mode defaults telemetry to False; caller value wins.""" + return _empty_mode_bool_default(mode, supplied, False) + + +def _skip_embedding_retrieval_default( + mode: CopilotClientMode | None, + supplied: bool | None, +) -> bool | None: + """Empty mode defaults embedding retrieval to off; caller value wins.""" + return _empty_mode_bool_default(mode, supplied, True) + + +def _enable_on_demand_instruction_discovery_default( + mode: CopilotClientMode | None, + supplied: bool | None, +) -> bool | None: + """Empty mode defaults on-demand instruction discovery to False.""" + return _empty_mode_bool_default(mode, supplied, False) + + +def _enable_file_hooks_default( + mode: CopilotClientMode | None, + supplied: bool | None, +) -> bool | None: + """Empty mode defaults file hooks to False; caller value wins.""" + return _empty_mode_bool_default(mode, supplied, False) + + +def _enable_host_git_operations_default( + mode: CopilotClientMode | None, + supplied: bool | None, +) -> bool | None: + """Empty mode defaults host git operations to False; caller value wins.""" + return _empty_mode_bool_default(mode, supplied, False) + + +def _enable_session_store_default( + mode: CopilotClientMode | None, + supplied: bool | None, +) -> bool | None: + """Empty mode defaults the session store to False; caller value wins.""" + return _empty_mode_bool_default(mode, supplied, False) + + +def _enable_skills_default( + mode: CopilotClientMode | None, + supplied: bool | None, +) -> bool | None: + """Empty mode defaults skills to False; caller value wins.""" + return _empty_mode_bool_default(mode, supplied, False) + + def _post_create_options_patch( mode: CopilotClientMode | None, skip_custom_instructions: bool | None, diff --git a/python/copilot/client.py b/python/copilot/client.py index 2e96b2faa..247e71b0b 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -37,11 +37,17 @@ from ._mode import ( CopilotClientMode, ToolSet, + _enable_file_hooks_default, + _enable_host_git_operations_default, + _enable_on_demand_instruction_discovery_default, + _enable_session_store_default, _enable_session_telemetry_default, + _enable_skills_default, _normalize_tool_filter, _post_create_options_patch, _require_available_tools_for_empty_mode, _require_storage_for_empty_mode, + _skip_embedding_retrieval_default, _system_message_for_mode, _validate_tool_filter_list, ) @@ -1561,6 +1567,13 @@ async def create_session( agent: str | None = None, config_dir: str | None = None, enable_config_discovery: bool | None = None, + skip_embedding_retrieval: bool | None = None, + organization_custom_instructions: str | None = None, + enable_on_demand_instruction_discovery: bool | None = None, + enable_file_hooks: bool | None = None, + enable_host_git_operations: bool | None = None, + enable_session_store: bool | None = None, + enable_skills: bool | None = None, skill_directories: list[str] | None = None, instruction_directories: list[str] | None = None, disabled_skills: list[str] | None = None, @@ -1636,6 +1649,14 @@ async def create_session( explicit values taking precedence on name collision. Custom instruction files (``.github/copilot-instructions.md``, ``AGENTS.md``, etc.) are always loaded regardless of this setting. + skip_embedding_retrieval: When True, skips embedding-based retrieval. + organization_custom_instructions: Organization-level custom instructions. + enable_on_demand_instruction_discovery: Enables on-demand instruction file + discovery. + enable_file_hooks: Enables file-based hooks from ``.github/hooks/``. + enable_host_git_operations: Enables git operations on the host filesystem. + enable_session_store: Enables the cross-session store. + enable_skills: Enables skill loading. skill_directories: Directories to search for skills. instruction_directories: Additional directories to search for custom instruction files. @@ -1693,8 +1714,23 @@ async def create_session( _validate_tool_filter_list("excluded_tools", excluded_tools) # Mode "empty" strips environment_context from the system message. system_message = _system_message_for_mode(mode, system_message) - # Mode "empty" defaults telemetry to off; caller wins. + # Mode "empty" defaults selected session config flags to restrictive values; + # caller-supplied values win. enable_session_telemetry = _enable_session_telemetry_default(mode, enable_session_telemetry) + skip_embedding_retrieval = _skip_embedding_retrieval_default( + mode, skip_embedding_retrieval + ) + enable_on_demand_instruction_discovery = ( + _enable_on_demand_instruction_discovery_default( + mode, enable_on_demand_instruction_discovery + ) + ) + enable_file_hooks = _enable_file_hooks_default(mode, enable_file_hooks) + enable_host_git_operations = _enable_host_git_operations_default( + mode, enable_host_git_operations + ) + enable_session_store = _enable_session_store_default(mode, enable_session_store) + enable_skills = _enable_skills_default(mode, enable_skills) payload: dict[str, Any] = {} if model: @@ -1804,6 +1840,22 @@ async def create_session( # Add config discovery flag if provided if enable_config_discovery is not None: payload["enableConfigDiscovery"] = enable_config_discovery + if skip_embedding_retrieval is not None: + payload["skipEmbeddingRetrieval"] = skip_embedding_retrieval + if organization_custom_instructions is not None: + payload["organizationCustomInstructions"] = organization_custom_instructions + if enable_on_demand_instruction_discovery is not None: + payload["enableOnDemandInstructionDiscovery"] = ( + enable_on_demand_instruction_discovery + ) + if enable_file_hooks is not None: + payload["enableFileHooks"] = enable_file_hooks + if enable_host_git_operations is not None: + payload["enableHostGitOperations"] = enable_host_git_operations + if enable_session_store is not None: + payload["enableSessionStore"] = enable_session_store + if enable_skills is not None: + payload["enableSkills"] = enable_skills # Add skill directories configuration if provided if skill_directories: @@ -1980,6 +2032,13 @@ async def resume_session( agent: str | None = None, config_dir: str | None = None, enable_config_discovery: bool | None = None, + skip_embedding_retrieval: bool | None = None, + organization_custom_instructions: str | None = None, + enable_on_demand_instruction_discovery: bool | None = None, + enable_file_hooks: bool | None = None, + enable_host_git_operations: bool | None = None, + enable_session_store: bool | None = None, + enable_skills: bool | None = None, skill_directories: list[str] | None = None, instruction_directories: list[str] | None = None, disabled_skills: list[str] | None = None, @@ -2056,6 +2115,14 @@ async def resume_session( explicit values taking precedence on name collision. Custom instruction files (``.github/copilot-instructions.md``, ``AGENTS.md``, etc.) are always loaded regardless of this setting. + skip_embedding_retrieval: When True, skips embedding-based retrieval. + organization_custom_instructions: Organization-level custom instructions. + enable_on_demand_instruction_discovery: Enables on-demand instruction file + discovery. + enable_file_hooks: Enables file-based hooks from ``.github/hooks/``. + enable_host_git_operations: Enables git operations on the host filesystem. + enable_session_store: Enables the cross-session store. + enable_skills: Enables skill loading. skill_directories: Directories to search for skills. instruction_directories: Additional directories to search for custom instruction files. @@ -2116,6 +2183,20 @@ async def resume_session( _validate_tool_filter_list("excluded_tools", excluded_tools) system_message = _system_message_for_mode(mode, system_message) enable_session_telemetry = _enable_session_telemetry_default(mode, enable_session_telemetry) + skip_embedding_retrieval = _skip_embedding_retrieval_default( + mode, skip_embedding_retrieval + ) + enable_on_demand_instruction_discovery = ( + _enable_on_demand_instruction_discovery_default( + mode, enable_on_demand_instruction_discovery + ) + ) + enable_file_hooks = _enable_file_hooks_default(mode, enable_file_hooks) + enable_host_git_operations = _enable_host_git_operations_default( + mode, enable_host_git_operations + ) + enable_session_store = _enable_session_store_default(mode, enable_session_store) + enable_skills = _enable_skills_default(mode, enable_skills) payload: dict[str, Any] = {"sessionId": session_id} @@ -2185,6 +2266,22 @@ async def resume_session( payload["configDir"] = config_dir if enable_config_discovery is not None: payload["enableConfigDiscovery"] = enable_config_discovery + if skip_embedding_retrieval is not None: + payload["skipEmbeddingRetrieval"] = skip_embedding_retrieval + if organization_custom_instructions is not None: + payload["organizationCustomInstructions"] = organization_custom_instructions + if enable_on_demand_instruction_discovery is not None: + payload["enableOnDemandInstructionDiscovery"] = ( + enable_on_demand_instruction_discovery + ) + if enable_file_hooks is not None: + payload["enableFileHooks"] = enable_file_hooks + if enable_host_git_operations is not None: + payload["enableHostGitOperations"] = enable_host_git_operations + if enable_session_store is not None: + payload["enableSessionStore"] = enable_session_store + if enable_skills is not None: + payload["enableSkills"] = enable_skills if continue_pending_work is not None: payload["continuePendingWork"] = continue_pending_work diff --git a/python/test_tool_set.py b/python/test_tool_set.py index 3d80d29e8..d7bb7dcf3 100644 --- a/python/test_tool_set.py +++ b/python/test_tool_set.py @@ -6,10 +6,16 @@ from copilot import BUILTIN_TOOLS_ISOLATED, CopilotClient, ToolSet, UriRuntimeConnection from copilot._mode import ( + _enable_file_hooks_default, + _enable_host_git_operations_default, + _enable_on_demand_instruction_discovery_default, + _enable_session_store_default, _enable_session_telemetry_default, + _enable_skills_default, _post_create_options_patch, _require_available_tools_for_empty_mode, _require_storage_for_empty_mode, + _skip_embedding_retrieval_default, _system_message_for_mode, _validate_tool_filter_list, ) diff --git a/rust/src/session.rs b/rust/src/session.rs index e735e405e..46176042b 100644 --- a/rust/src/session.rs +++ b/rust/src/session.rs @@ -820,8 +820,28 @@ impl Client { crate::mode::validate_tool_filter_list("excluded_tools", config.excluded_tools.as_deref())?; config.system_message = crate::mode::system_message_for_mode(mode, config.system_message.take()); - if mode == crate::ClientMode::Empty && config.enable_session_telemetry.is_none() { - config.enable_session_telemetry = Some(false); + if mode == crate::ClientMode::Empty { + if config.enable_session_telemetry.is_none() { + config.enable_session_telemetry = Some(false); + } + if config.skip_embedding_retrieval.is_none() { + config.skip_embedding_retrieval = Some(true); + } + if config.enable_on_demand_instruction_discovery.is_none() { + config.enable_on_demand_instruction_discovery = Some(false); + } + if config.enable_file_hooks.is_none() { + config.enable_file_hooks = Some(false); + } + if config.enable_host_git_operations.is_none() { + config.enable_host_git_operations = Some(false); + } + if config.enable_session_store.is_none() { + config.enable_session_store = Some(false); + } + if config.enable_skills.is_none() { + config.enable_skills = Some(false); + } } let opt_skip_custom_instructions = config.skip_custom_instructions; let opt_custom_agents_local_only = config.custom_agents_local_only; @@ -994,8 +1014,28 @@ impl Client { crate::mode::validate_tool_filter_list("excluded_tools", config.excluded_tools.as_deref())?; config.system_message = crate::mode::system_message_for_mode(mode, config.system_message.take()); - if mode == crate::ClientMode::Empty && config.enable_session_telemetry.is_none() { - config.enable_session_telemetry = Some(false); + if mode == crate::ClientMode::Empty { + if config.enable_session_telemetry.is_none() { + config.enable_session_telemetry = Some(false); + } + if config.skip_embedding_retrieval.is_none() { + config.skip_embedding_retrieval = Some(true); + } + if config.enable_on_demand_instruction_discovery.is_none() { + config.enable_on_demand_instruction_discovery = Some(false); + } + if config.enable_file_hooks.is_none() { + config.enable_file_hooks = Some(false); + } + if config.enable_host_git_operations.is_none() { + config.enable_host_git_operations = Some(false); + } + if config.enable_session_store.is_none() { + config.enable_session_store = Some(false); + } + if config.enable_skills.is_none() { + config.enable_skills = Some(false); + } } let opt_skip_custom_instructions = config.skip_custom_instructions; let opt_custom_agents_local_only = config.custom_agents_local_only; diff --git a/rust/src/types.rs b/rust/src/types.rs index 7b0665f90..1c9b7ce8b 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1131,6 +1131,20 @@ pub struct SessionConfig { pub mcp_servers: Option>, /// When true, the CLI runs config discovery (MCP config files, skills, plugins). pub enable_config_discovery: Option, + /// When true, skips embedding retrieval for this session. + pub skip_embedding_retrieval: Option, + /// Organization-level custom instructions to apply to this session. + pub organization_custom_instructions: Option, + /// When true, enables on-demand instruction discovery for this session. + pub enable_on_demand_instruction_discovery: Option, + /// When true, enables file hooks for this session. + pub enable_file_hooks: Option, + /// When true, allows host Git operations for this session. + pub enable_host_git_operations: Option, + /// When true, enables the session store for this session. + pub enable_session_store: Option, + /// When true, enables skills for this session. + pub enable_skills: Option, /// Skill directory paths passed through to the GitHub Copilot CLI. pub skill_directories: Option>, /// Additional directories to search for custom instruction files. @@ -1274,6 +1288,22 @@ impl std::fmt::Debug for SessionConfig { .field("excluded_tools", &self.excluded_tools) .field("mcp_servers", &self.mcp_servers) .field("enable_config_discovery", &self.enable_config_discovery) + .field("skip_embedding_retrieval", &self.skip_embedding_retrieval) + .field( + "organization_custom_instructions", + &self.organization_custom_instructions, + ) + .field( + "enable_on_demand_instruction_discovery", + &self.enable_on_demand_instruction_discovery, + ) + .field("enable_file_hooks", &self.enable_file_hooks) + .field( + "enable_host_git_operations", + &self.enable_host_git_operations, + ) + .field("enable_session_store", &self.enable_session_store) + .field("enable_skills", &self.enable_skills) .field("skill_directories", &self.skill_directories) .field("instruction_directories", &self.instruction_directories) .field("disabled_skills", &self.disabled_skills) @@ -1358,6 +1388,13 @@ impl Default for SessionConfig { excluded_tools: None, mcp_servers: None, enable_config_discovery: None, + skip_embedding_retrieval: None, + organization_custom_instructions: None, + enable_on_demand_instruction_discovery: None, + enable_file_hooks: None, + enable_host_git_operations: None, + enable_session_store: None, + enable_skills: None, skill_directories: None, instruction_directories: None, disabled_skills: None, @@ -1480,6 +1517,13 @@ impl SessionConfig { mcp_servers: self.mcp_servers, env_value_mode: "direct", enable_config_discovery: self.enable_config_discovery, + skip_embedding_retrieval: self.skip_embedding_retrieval, + organization_custom_instructions: self.organization_custom_instructions, + enable_on_demand_instruction_discovery: self.enable_on_demand_instruction_discovery, + enable_file_hooks: self.enable_file_hooks, + enable_host_git_operations: self.enable_host_git_operations, + enable_session_store: self.enable_session_store, + enable_skills: self.enable_skills, request_user_input, request_permission: permission_active, request_exit_plan_mode, @@ -1731,6 +1775,51 @@ impl SessionConfig { self } + /// Set [`Self::skip_embedding_retrieval`]. + pub fn with_skip_embedding_retrieval(mut self, value: bool) -> Self { + self.skip_embedding_retrieval = Some(value); + self + } + + /// Set [`Self::organization_custom_instructions`]. + pub fn with_organization_custom_instructions( + mut self, + instructions: impl Into, + ) -> Self { + self.organization_custom_instructions = Some(instructions.into()); + self + } + + /// Set [`Self::enable_on_demand_instruction_discovery`]. + pub fn with_enable_on_demand_instruction_discovery(mut self, value: bool) -> Self { + self.enable_on_demand_instruction_discovery = Some(value); + self + } + + /// Set [`Self::enable_file_hooks`]. + pub fn with_enable_file_hooks(mut self, value: bool) -> Self { + self.enable_file_hooks = Some(value); + self + } + + /// Set [`Self::enable_host_git_operations`]. + pub fn with_enable_host_git_operations(mut self, value: bool) -> Self { + self.enable_host_git_operations = Some(value); + self + } + + /// Set [`Self::enable_session_store`]. + pub fn with_enable_session_store(mut self, value: bool) -> Self { + self.enable_session_store = Some(value); + self + } + + /// Set [`Self::enable_skills`]. + pub fn with_enable_skills(mut self, value: bool) -> Self { + self.enable_skills = Some(value); + self + } + /// Set skill directory paths passed through to the CLI. pub fn with_skill_directories(mut self, paths: I) -> Self where @@ -1928,6 +2017,20 @@ pub struct ResumeSessionConfig { pub mcp_servers: Option>, /// Enable config discovery on resume. pub enable_config_discovery: Option, + /// When true, skips embedding retrieval on resume. + pub skip_embedding_retrieval: Option, + /// Organization-level custom instructions to apply on resume. + pub organization_custom_instructions: Option, + /// When true, enables on-demand instruction discovery on resume. + pub enable_on_demand_instruction_discovery: Option, + /// When true, enables file hooks on resume. + pub enable_file_hooks: Option, + /// When true, allows host Git operations on resume. + pub enable_host_git_operations: Option, + /// When true, enables the session store on resume. + pub enable_session_store: Option, + /// When true, enables skills on resume. + pub enable_skills: Option, /// Skill directory paths passed through to the GitHub Copilot CLI on resume. pub skill_directories: Option>, /// Additional directories to search for custom instruction files on @@ -2042,6 +2145,22 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("excluded_tools", &self.excluded_tools) .field("mcp_servers", &self.mcp_servers) .field("enable_config_discovery", &self.enable_config_discovery) + .field("skip_embedding_retrieval", &self.skip_embedding_retrieval) + .field( + "organization_custom_instructions", + &self.organization_custom_instructions, + ) + .field( + "enable_on_demand_instruction_discovery", + &self.enable_on_demand_instruction_discovery, + ) + .field("enable_file_hooks", &self.enable_file_hooks) + .field( + "enable_host_git_operations", + &self.enable_host_git_operations, + ) + .field("enable_session_store", &self.enable_session_store) + .field("enable_skills", &self.enable_skills) .field("skill_directories", &self.skill_directories) .field("instruction_directories", &self.instruction_directories) .field("disabled_skills", &self.disabled_skills) @@ -2165,6 +2284,13 @@ impl ResumeSessionConfig { mcp_servers: self.mcp_servers, env_value_mode: "direct", enable_config_discovery: self.enable_config_discovery, + skip_embedding_retrieval: self.skip_embedding_retrieval, + organization_custom_instructions: self.organization_custom_instructions, + enable_on_demand_instruction_discovery: self.enable_on_demand_instruction_discovery, + enable_file_hooks: self.enable_file_hooks, + enable_host_git_operations: self.enable_host_git_operations, + enable_session_store: self.enable_session_store, + enable_skills: self.enable_skills, request_user_input, request_permission: permission_active, request_exit_plan_mode, @@ -2231,6 +2357,13 @@ impl ResumeSessionConfig { excluded_tools: None, mcp_servers: None, enable_config_discovery: None, + skip_embedding_retrieval: None, + organization_custom_instructions: None, + enable_on_demand_instruction_discovery: None, + enable_file_hooks: None, + enable_host_git_operations: None, + enable_session_store: None, + enable_skills: None, skill_directories: None, instruction_directories: None, disabled_skills: None, @@ -2456,6 +2589,51 @@ impl ResumeSessionConfig { self } + /// Set [`Self::skip_embedding_retrieval`]. + pub fn with_skip_embedding_retrieval(mut self, value: bool) -> Self { + self.skip_embedding_retrieval = Some(value); + self + } + + /// Set [`Self::organization_custom_instructions`]. + pub fn with_organization_custom_instructions( + mut self, + instructions: impl Into, + ) -> Self { + self.organization_custom_instructions = Some(instructions.into()); + self + } + + /// Set [`Self::enable_on_demand_instruction_discovery`]. + pub fn with_enable_on_demand_instruction_discovery(mut self, value: bool) -> Self { + self.enable_on_demand_instruction_discovery = Some(value); + self + } + + /// Set [`Self::enable_file_hooks`]. + pub fn with_enable_file_hooks(mut self, value: bool) -> Self { + self.enable_file_hooks = Some(value); + self + } + + /// Set [`Self::enable_host_git_operations`]. + pub fn with_enable_host_git_operations(mut self, value: bool) -> Self { + self.enable_host_git_operations = Some(value); + self + } + + /// Set [`Self::enable_session_store`]. + pub fn with_enable_session_store(mut self, value: bool) -> Self { + self.enable_session_store = Some(value); + self + } + + /// Set [`Self::enable_skills`]. + pub fn with_enable_skills(mut self, value: bool) -> Self { + self.enable_skills = Some(value); + self + } + /// Set skill directory paths passed through to the CLI on resume. pub fn with_skill_directories(mut self, paths: I) -> Self where diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 29f89d84d..040b7d115 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -75,6 +75,20 @@ pub(crate) struct SessionCreateWire { pub env_value_mode: &'static str, #[serde(skip_serializing_if = "Option::is_none")] pub enable_config_discovery: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub skip_embedding_retrieval: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub organization_custom_instructions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_on_demand_instruction_discovery: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_file_hooks: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_host_git_operations: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_session_store: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_skills: Option, pub request_user_input: bool, pub request_permission: bool, pub request_exit_plan_mode: bool, @@ -153,6 +167,20 @@ pub(crate) struct SessionResumeWire { pub env_value_mode: &'static str, #[serde(skip_serializing_if = "Option::is_none")] pub enable_config_discovery: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub skip_embedding_retrieval: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub organization_custom_instructions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_on_demand_instruction_discovery: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_file_hooks: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_host_git_operations: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_session_store: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_skills: Option, pub request_user_input: bool, pub request_permission: bool, pub request_exit_plan_mode: bool, From e362ea9fdf4a72de5946d2fffd47f235d7ec3b19 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 28 May 2026 11:22:53 -0700 Subject: [PATCH 02/10] address PR review feedback - Go: change OrganizationCustomInstructions to *string for cross-SDK consistency - Go: clarify *bool field docs (nil = runtime default, non-nil = forwarded) - Rust: redact organization_custom_instructions in Debug impls - Node.js: clarify JSDoc defaults apply only in empty mode - Python: replace unused imports with parametrized tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- go/types.go | 301 +++++++++++++++++++++------------------- nodejs/src/types.ts | 31 ++--- python/test_tool_set.py | 55 ++++++-- rust/src/types.rs | 8 +- 4 files changed, 225 insertions(+), 170 deletions(-) diff --git a/go/types.go b/go/types.go index 74a5a939d..adcd98d75 100644 --- a/go/types.go +++ b/go/types.go @@ -886,33 +886,41 @@ type SessionConfig struct { // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are // always loaded from the working directory regardless of this setting. EnableConfigDiscovery bool - // SkipEmbeddingRetrieval, when non-nil and true, skips embedding-based retrieval - // for this session. Use in multitenant deployments to prevent cross-session - // information leakage through the shared embedding cache. + // SkipEmbeddingRetrieval controls embedding-based retrieval for this session. + // When nil, the runtime default is used. When non-nil, the value (true or false) + // is passed through to the runtime. Use in multitenant deployments to prevent + // cross-session information leakage through the shared embedding cache. SkipEmbeddingRetrieval *bool // OrganizationCustomInstructions provides organization-level custom instructions - // to include in the system prompt. Allows hosts to inject organization-specific - // guidance without relying on filesystem-based instruction discovery. - OrganizationCustomInstructions string - // EnableOnDemandInstructionDiscovery, when non-nil and true, enables on-demand - // discovery of instruction files (AGENTS.md, .github/copilot-instructions.md, etc.) - // after successful file views. + // to include in the system prompt. When nil, the runtime default is used. When + // non-nil, the value (including an empty string) is passed through to the runtime. + // Allows hosts to inject organization-specific guidance without relying on + // filesystem-based instruction discovery. + OrganizationCustomInstructions *string + // EnableOnDemandInstructionDiscovery controls on-demand discovery of instruction + // files (AGENTS.md, .github/copilot-instructions.md, etc.) after successful file + // views. When nil, the runtime default is used. When non-nil, the value (true or + // false) is passed through to the runtime. EnableOnDemandInstructionDiscovery *bool - // EnableFileHooks, when non-nil and true, enables loading of file-based hooks - // from .github/hooks/. This is separate from the Hooks callback parameter which - // gates SDK hook event registration. + // EnableFileHooks controls loading of file-based hooks from .github/hooks/. + // When nil, the runtime default is used. When non-nil, the value (true or false) + // is passed through to the runtime. This is separate from the Hooks callback + // parameter which gates SDK hook event registration. EnableFileHooks *bool - // EnableHostGitOperations, when non-nil and true, enables git operations on the - // host filesystem (branch detection, file status, commit history). When false, - // no git context is surfaced in the system prompt. + // EnableHostGitOperations controls git operations on the host filesystem (branch + // detection, file status, commit history). When nil, the runtime default is used. + // When non-nil, the value (true or false) is passed through to the runtime. When + // false, no git context is surfaced in the system prompt. EnableHostGitOperations *bool - // EnableSessionStore, when non-nil and true, enables the cross-session store for - // search and retrieval across sessions. When false, session content is not written - // to or read from the shared session store. + // EnableSessionStore controls the cross-session store for search and retrieval + // across sessions. When nil, the runtime default is used. When non-nil, the value + // (true or false) is passed through to the runtime. When false, session content is + // not written to or read from the shared session store. EnableSessionStore *bool - // EnableSkills, when non-nil and true, enables skill loading (including builtin - // skills and discovered skill directories). When false, no skills are loaded - // regardless of SkillDirectories or EnableConfigDiscovery settings. + // EnableSkills controls skill loading (including builtin skills and discovered + // skill directories). When nil, the runtime default is used. When non-nil, the + // value (true or false) is passed through to the runtime. When false, no skills + // are loaded regardless of SkillDirectories or EnableConfigDiscovery settings. EnableSkills *bool // Tools exposes caller-implemented tools to the CLI. A Tool with a nil Handler // is declaration-only; the consumer must resolve its calls via pending tool RPCs. @@ -1238,26 +1246,33 @@ type ResumeSessionConfig struct { // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are // always loaded from the working directory regardless of this setting. EnableConfigDiscovery bool - // SkipEmbeddingRetrieval, when non-nil and true, skips embedding-based retrieval - // for this session. Use in multitenant deployments to prevent cross-session - // information leakage through the shared embedding cache. + // SkipEmbeddingRetrieval controls embedding-based retrieval for this session. + // When nil, the runtime default is used. When non-nil, the value (true or false) + // is passed through to the runtime. Use in multitenant deployments to prevent + // cross-session information leakage through the shared embedding cache. SkipEmbeddingRetrieval *bool // OrganizationCustomInstructions provides organization-level custom instructions - // to include in the system prompt. - OrganizationCustomInstructions string - // EnableOnDemandInstructionDiscovery, when non-nil and true, enables on-demand - // discovery of instruction files after successful file views. + // to include in the system prompt. When nil, the runtime default is used. When + // non-nil, the value (including an empty string) is passed through to the runtime. + OrganizationCustomInstructions *string + // EnableOnDemandInstructionDiscovery controls on-demand discovery of instruction + // files after successful file views. When nil, the runtime default is used. When + // non-nil, the value (true or false) is passed through to the runtime. EnableOnDemandInstructionDiscovery *bool - // EnableFileHooks, when non-nil and true, enables loading of file-based hooks - // from .github/hooks/. + // EnableFileHooks controls loading of file-based hooks from .github/hooks/. + // When nil, the runtime default is used. When non-nil, the value (true or false) + // is passed through to the runtime. EnableFileHooks *bool - // EnableHostGitOperations, when non-nil and true, enables git operations on the - // host filesystem. + // EnableHostGitOperations controls git operations on the host filesystem. When + // nil, the runtime default is used. When non-nil, the value (true or false) is + // passed through to the runtime. EnableHostGitOperations *bool - // EnableSessionStore, when non-nil and true, enables the cross-session store for - // search and retrieval across sessions. + // EnableSessionStore controls the cross-session store for search and retrieval + // across sessions. When nil, the runtime default is used. When non-nil, the value + // (true or false) is passed through to the runtime. EnableSessionStore *bool - // EnableSkills, when non-nil and true, enables skill loading. + // EnableSkills controls skill loading. When nil, the runtime default is used. + // When non-nil, the value (true or false) is passed through to the runtime. EnableSkills *bool // Streaming enables streaming of assistant message and reasoning chunks. // When non-nil and true, assistant.message_delta and assistant.reasoning_delta @@ -1544,59 +1559,59 @@ type SessionLifecycleHandler func(event SessionLifecycleEvent) // createSessionRequest is the request for session.create type createSessionRequest struct { - Model string `json:"model,omitempty"` - SessionID string `json:"sessionId,omitempty"` - ClientName string `json:"clientName,omitempty"` - ReasoningEffort string `json:"reasoningEffort,omitempty"` - Tools []Tool `json:"tools,omitempty"` - SystemMessage *SystemMessageConfig `json:"systemMessage,omitempty"` - AvailableTools []string `json:"availableTools"` - ExcludedTools []string `json:"excludedTools,omitempty"` - ToolFilterPrecedence *rpc.OptionsUpdateToolFilterPrecedence `json:"toolFilterPrecedence,omitempty"` - Provider *ProviderConfig `json:"provider,omitempty"` - EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` - SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` - CustomAgentsLocalOnly *bool `json:"customAgentsLocalOnly,omitempty"` - CoauthorEnabled *bool `json:"coauthorEnabled,omitempty"` - ManageScheduleEnabled *bool `json:"manageScheduleEnabled,omitempty"` - ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` - RequestPermission *bool `json:"requestPermission,omitempty"` - RequestUserInput *bool `json:"requestUserInput,omitempty"` - RequestExitPlanMode *bool `json:"requestExitPlanMode,omitempty"` - RequestAutoModeSwitch *bool `json:"requestAutoModeSwitch,omitempty"` - Hooks *bool `json:"hooks,omitempty"` - WorkingDirectory string `json:"workingDirectory,omitempty"` - Streaming *bool `json:"streaming,omitempty"` - IncludeSubAgentStreamingEvents *bool `json:"includeSubAgentStreamingEvents,omitempty"` - MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` - EnvValueMode string `json:"envValueMode,omitempty"` - CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` - DefaultAgent *DefaultAgentConfig `json:"defaultAgent,omitempty"` - Agent string `json:"agent,omitempty"` - ConfigDir string `json:"configDir,omitempty"` - EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` - SkipEmbeddingRetrieval *bool `json:"skipEmbeddingRetrieval,omitempty"` - OrganizationCustomInstructions string `json:"organizationCustomInstructions,omitempty"` - EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` - EnableFileHooks *bool `json:"enableFileHooks,omitempty"` - EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` - EnableSessionStore *bool `json:"enableSessionStore,omitempty"` - EnableSkills *bool `json:"enableSkills,omitempty"` - SkillDirectories []string `json:"skillDirectories,omitempty"` - InstructionDirectories []string `json:"instructionDirectories,omitempty"` - DisabledSkills []string `json:"disabledSkills,omitempty"` - InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` - Commands []wireCommand `json:"commands,omitempty"` - RequestElicitation *bool `json:"requestElicitation,omitempty"` - GitHubToken string `json:"gitHubToken,omitempty"` - RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"` - Cloud *CloudSessionOptions `json:"cloud,omitempty"` - Canvases []CanvasDeclaration `json:"canvases,omitempty"` - RequestCanvasRenderer *bool `json:"requestCanvasRenderer,omitempty"` - RequestExtensions *bool `json:"requestExtensions,omitempty"` - ExtensionInfo *ExtensionInfo `json:"extensionInfo,omitempty"` - Traceparent string `json:"traceparent,omitempty"` - Tracestate string `json:"tracestate,omitempty"` + Model string `json:"model,omitempty"` + SessionID string `json:"sessionId,omitempty"` + ClientName string `json:"clientName,omitempty"` + ReasoningEffort string `json:"reasoningEffort,omitempty"` + Tools []Tool `json:"tools,omitempty"` + SystemMessage *SystemMessageConfig `json:"systemMessage,omitempty"` + AvailableTools []string `json:"availableTools"` + ExcludedTools []string `json:"excludedTools,omitempty"` + ToolFilterPrecedence *rpc.OptionsUpdateToolFilterPrecedence `json:"toolFilterPrecedence,omitempty"` + Provider *ProviderConfig `json:"provider,omitempty"` + EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` + SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` + CustomAgentsLocalOnly *bool `json:"customAgentsLocalOnly,omitempty"` + CoauthorEnabled *bool `json:"coauthorEnabled,omitempty"` + ManageScheduleEnabled *bool `json:"manageScheduleEnabled,omitempty"` + ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` + RequestPermission *bool `json:"requestPermission,omitempty"` + RequestUserInput *bool `json:"requestUserInput,omitempty"` + RequestExitPlanMode *bool `json:"requestExitPlanMode,omitempty"` + RequestAutoModeSwitch *bool `json:"requestAutoModeSwitch,omitempty"` + Hooks *bool `json:"hooks,omitempty"` + WorkingDirectory string `json:"workingDirectory,omitempty"` + Streaming *bool `json:"streaming,omitempty"` + IncludeSubAgentStreamingEvents *bool `json:"includeSubAgentStreamingEvents,omitempty"` + MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` + EnvValueMode string `json:"envValueMode,omitempty"` + CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` + DefaultAgent *DefaultAgentConfig `json:"defaultAgent,omitempty"` + Agent string `json:"agent,omitempty"` + ConfigDir string `json:"configDir,omitempty"` + EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` + SkipEmbeddingRetrieval *bool `json:"skipEmbeddingRetrieval,omitempty"` + OrganizationCustomInstructions *string `json:"organizationCustomInstructions,omitempty"` + EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` + EnableFileHooks *bool `json:"enableFileHooks,omitempty"` + EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` + EnableSessionStore *bool `json:"enableSessionStore,omitempty"` + EnableSkills *bool `json:"enableSkills,omitempty"` + SkillDirectories []string `json:"skillDirectories,omitempty"` + InstructionDirectories []string `json:"instructionDirectories,omitempty"` + DisabledSkills []string `json:"disabledSkills,omitempty"` + InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` + Commands []wireCommand `json:"commands,omitempty"` + RequestElicitation *bool `json:"requestElicitation,omitempty"` + GitHubToken string `json:"gitHubToken,omitempty"` + RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"` + Cloud *CloudSessionOptions `json:"cloud,omitempty"` + Canvases []CanvasDeclaration `json:"canvases,omitempty"` + RequestCanvasRenderer *bool `json:"requestCanvasRenderer,omitempty"` + RequestExtensions *bool `json:"requestExtensions,omitempty"` + ExtensionInfo *ExtensionInfo `json:"extensionInfo,omitempty"` + Traceparent string `json:"traceparent,omitempty"` + Tracestate string `json:"tracestate,omitempty"` } // wireCommand is the wire representation of a command (name + description only, no handler). @@ -1614,61 +1629,61 @@ type createSessionResponse struct { // resumeSessionRequest is the request for session.resume type resumeSessionRequest struct { - SessionID string `json:"sessionId"` - ClientName string `json:"clientName,omitempty"` - Model string `json:"model,omitempty"` - ReasoningEffort string `json:"reasoningEffort,omitempty"` - Tools []Tool `json:"tools,omitempty"` - SystemMessage *SystemMessageConfig `json:"systemMessage,omitempty"` - AvailableTools []string `json:"availableTools"` - ExcludedTools []string `json:"excludedTools,omitempty"` - ToolFilterPrecedence *rpc.OptionsUpdateToolFilterPrecedence `json:"toolFilterPrecedence,omitempty"` - Provider *ProviderConfig `json:"provider,omitempty"` - EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` - SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` - CustomAgentsLocalOnly *bool `json:"customAgentsLocalOnly,omitempty"` - CoauthorEnabled *bool `json:"coauthorEnabled,omitempty"` - ManageScheduleEnabled *bool `json:"manageScheduleEnabled,omitempty"` - ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` - RequestPermission *bool `json:"requestPermission,omitempty"` - RequestUserInput *bool `json:"requestUserInput,omitempty"` - RequestExitPlanMode *bool `json:"requestExitPlanMode,omitempty"` - RequestAutoModeSwitch *bool `json:"requestAutoModeSwitch,omitempty"` - Hooks *bool `json:"hooks,omitempty"` - WorkingDirectory string `json:"workingDirectory,omitempty"` - ConfigDir string `json:"configDir,omitempty"` - EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` - SkipEmbeddingRetrieval *bool `json:"skipEmbeddingRetrieval,omitempty"` - OrganizationCustomInstructions string `json:"organizationCustomInstructions,omitempty"` - EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` - EnableFileHooks *bool `json:"enableFileHooks,omitempty"` - EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` - EnableSessionStore *bool `json:"enableSessionStore,omitempty"` - EnableSkills *bool `json:"enableSkills,omitempty"` - DisableResume *bool `json:"disableResume,omitempty"` - ContinuePendingWork *bool `json:"continuePendingWork,omitempty"` - Streaming *bool `json:"streaming,omitempty"` - IncludeSubAgentStreamingEvents *bool `json:"includeSubAgentStreamingEvents,omitempty"` - MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` - EnvValueMode string `json:"envValueMode,omitempty"` - CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` - DefaultAgent *DefaultAgentConfig `json:"defaultAgent,omitempty"` - Agent string `json:"agent,omitempty"` - SkillDirectories []string `json:"skillDirectories,omitempty"` - InstructionDirectories []string `json:"instructionDirectories,omitempty"` - DisabledSkills []string `json:"disabledSkills,omitempty"` - InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` - Commands []wireCommand `json:"commands,omitempty"` - RequestElicitation *bool `json:"requestElicitation,omitempty"` - GitHubToken string `json:"gitHubToken,omitempty"` - RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"` - Canvases []CanvasDeclaration `json:"canvases,omitempty"` - OpenCanvases []rpc.OpenCanvasInstance `json:"openCanvases,omitempty"` - RequestCanvasRenderer *bool `json:"requestCanvasRenderer,omitempty"` - RequestExtensions *bool `json:"requestExtensions,omitempty"` - ExtensionInfo *ExtensionInfo `json:"extensionInfo,omitempty"` - Traceparent string `json:"traceparent,omitempty"` - Tracestate string `json:"tracestate,omitempty"` + SessionID string `json:"sessionId"` + ClientName string `json:"clientName,omitempty"` + Model string `json:"model,omitempty"` + ReasoningEffort string `json:"reasoningEffort,omitempty"` + Tools []Tool `json:"tools,omitempty"` + SystemMessage *SystemMessageConfig `json:"systemMessage,omitempty"` + AvailableTools []string `json:"availableTools"` + ExcludedTools []string `json:"excludedTools,omitempty"` + ToolFilterPrecedence *rpc.OptionsUpdateToolFilterPrecedence `json:"toolFilterPrecedence,omitempty"` + Provider *ProviderConfig `json:"provider,omitempty"` + EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` + SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` + CustomAgentsLocalOnly *bool `json:"customAgentsLocalOnly,omitempty"` + CoauthorEnabled *bool `json:"coauthorEnabled,omitempty"` + ManageScheduleEnabled *bool `json:"manageScheduleEnabled,omitempty"` + ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` + RequestPermission *bool `json:"requestPermission,omitempty"` + RequestUserInput *bool `json:"requestUserInput,omitempty"` + RequestExitPlanMode *bool `json:"requestExitPlanMode,omitempty"` + RequestAutoModeSwitch *bool `json:"requestAutoModeSwitch,omitempty"` + Hooks *bool `json:"hooks,omitempty"` + WorkingDirectory string `json:"workingDirectory,omitempty"` + ConfigDir string `json:"configDir,omitempty"` + EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` + SkipEmbeddingRetrieval *bool `json:"skipEmbeddingRetrieval,omitempty"` + OrganizationCustomInstructions *string `json:"organizationCustomInstructions,omitempty"` + EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` + EnableFileHooks *bool `json:"enableFileHooks,omitempty"` + EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` + EnableSessionStore *bool `json:"enableSessionStore,omitempty"` + EnableSkills *bool `json:"enableSkills,omitempty"` + DisableResume *bool `json:"disableResume,omitempty"` + ContinuePendingWork *bool `json:"continuePendingWork,omitempty"` + Streaming *bool `json:"streaming,omitempty"` + IncludeSubAgentStreamingEvents *bool `json:"includeSubAgentStreamingEvents,omitempty"` + MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` + EnvValueMode string `json:"envValueMode,omitempty"` + CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` + DefaultAgent *DefaultAgentConfig `json:"defaultAgent,omitempty"` + Agent string `json:"agent,omitempty"` + SkillDirectories []string `json:"skillDirectories,omitempty"` + InstructionDirectories []string `json:"instructionDirectories,omitempty"` + DisabledSkills []string `json:"disabledSkills,omitempty"` + InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` + Commands []wireCommand `json:"commands,omitempty"` + RequestElicitation *bool `json:"requestElicitation,omitempty"` + GitHubToken string `json:"gitHubToken,omitempty"` + RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"` + Canvases []CanvasDeclaration `json:"canvases,omitempty"` + OpenCanvases []rpc.OpenCanvasInstance `json:"openCanvases,omitempty"` + RequestCanvasRenderer *bool `json:"requestCanvasRenderer,omitempty"` + RequestExtensions *bool `json:"requestExtensions,omitempty"` + ExtensionInfo *ExtensionInfo `json:"extensionInfo,omitempty"` + Traceparent string `json:"traceparent,omitempty"` + Tracestate string `json:"tracestate,omitempty"` } // resumeSessionResponse is the response from session.resume diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 930bccaab..0da624437 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1810,58 +1810,57 @@ export interface SessionConfigBase { /** * When true, skips embedding-based retrieval for this session. * Use in multitenant deployments to prevent cross-session information leakage - * through the shared embedding cache. + * through the shared embedding cache. When omitted, `mode: "empty"` defaults + * this to `true`; in other modes, the runtime default applies. */ skipEmbeddingRetrieval?: boolean; /** * Organization-level custom instructions to include in the system prompt. * Allows hosts to inject organization-specific guidance without relying on - * filesystem-based instruction discovery. + * filesystem-based instruction discovery. When omitted, no organization-level + * instructions are injected by the SDK. */ organizationCustomInstructions?: string; /** * When true, enables on-demand discovery of instruction files (AGENTS.md, * .github/copilot-instructions.md, etc.) after successful file views. - * - * @default false + * When omitted, `mode: "empty"` defaults this to `false`; in other modes, + * the runtime default applies. */ enableOnDemandInstructionDiscovery?: boolean; /** * When true, enables loading of file-based hooks from `.github/hooks/`. * This is separate from the `hooks` callback parameter which gates SDK - * hook event registration. - * - * @default false + * hook event registration. When omitted, `mode: "empty"` defaults this to + * `false`; in other modes, the runtime default applies. */ enableFileHooks?: boolean; /** * When true, enables git operations on the host filesystem (branch detection, * file status, commit history). When false, no git context is surfaced in - * the system prompt. - * - * @default false + * the system prompt. When omitted, `mode: "empty"` defaults this to `false`; + * in other modes, the runtime default applies. */ enableHostGitOperations?: boolean; /** * When true, enables the cross-session store for search and retrieval * across sessions. When false, session content is not written to or - * read from the shared session store. - * - * @default false + * read from the shared session store. When omitted, `mode: "empty"` + * defaults this to `false`; in other modes, the runtime default applies. */ enableSessionStore?: boolean; /** * When true, enables skill loading (including builtin skills and discovered * skill directories). When false, no skills are loaded regardless of - * `skillDirectories` or `enableConfigDiscovery` settings. - * - * @default false + * `skillDirectories` or `enableConfigDiscovery` settings. When omitted, + * `mode: "empty"` defaults this to `false`; in other modes, the runtime + * default applies. */ enableSkills?: boolean; diff --git a/python/test_tool_set.py b/python/test_tool_set.py index d7bb7dcf3..bf63d2b15 100644 --- a/python/test_tool_set.py +++ b/python/test_tool_set.py @@ -173,15 +173,52 @@ def test_empty_mode_append_promoted_to_customize(self): assert out["sections"]["environment_context"] == {"action": "remove"} -class TestTelemetryDefault: - def test_empty_mode_defaults_to_false(self): - assert _enable_session_telemetry_default("empty", None) is False - - def test_empty_mode_caller_wins(self): - assert _enable_session_telemetry_default("empty", True) is True - - def test_copilot_cli_does_not_change(self): - assert _enable_session_telemetry_default("copilot-cli", None) is None +class TestEmptyModeBooleanDefaults: + @pytest.mark.parametrize( + ("helper", "empty_default"), + [ + (_enable_session_telemetry_default, False), + (_skip_embedding_retrieval_default, True), + (_enable_on_demand_instruction_discovery_default, False), + (_enable_file_hooks_default, False), + (_enable_host_git_operations_default, False), + (_enable_session_store_default, False), + (_enable_skills_default, False), + ], + ) + def test_empty_mode_defaults(self, helper, empty_default): + assert helper("empty", None) is empty_default + + @pytest.mark.parametrize( + "helper", + [ + _enable_session_telemetry_default, + _skip_embedding_retrieval_default, + _enable_on_demand_instruction_discovery_default, + _enable_file_hooks_default, + _enable_host_git_operations_default, + _enable_session_store_default, + _enable_skills_default, + ], + ) + def test_caller_wins(self, helper): + assert helper("empty", True) is True + assert helper("empty", False) is False + + @pytest.mark.parametrize( + "helper", + [ + _enable_session_telemetry_default, + _skip_embedding_retrieval_default, + _enable_on_demand_instruction_discovery_default, + _enable_file_hooks_default, + _enable_host_git_operations_default, + _enable_session_store_default, + _enable_skills_default, + ], + ) + def test_copilot_cli_does_not_change(self, helper): + assert helper("copilot-cli", None) is None class TestPostCreatePatch: diff --git a/rust/src/types.rs b/rust/src/types.rs index 1c9b7ce8b..aafc3139d 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1291,7 +1291,9 @@ impl std::fmt::Debug for SessionConfig { .field("skip_embedding_retrieval", &self.skip_embedding_retrieval) .field( "organization_custom_instructions", - &self.organization_custom_instructions, + &self.organization_custom_instructions + .as_ref() + .map(|_| ""), ) .field( "enable_on_demand_instruction_discovery", @@ -2148,7 +2150,9 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("skip_embedding_retrieval", &self.skip_embedding_retrieval) .field( "organization_custom_instructions", - &self.organization_custom_instructions, + &self.organization_custom_instructions + .as_ref() + .map(|_| ""), ) .field( "enable_on_demand_instruction_discovery", From c8941f7775152b6499fc4c85d50c35951ff69e9d Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 28 May 2026 13:59:34 -0700 Subject: [PATCH 03/10] feat: add embeddingCacheStorage per-session option across all SDKs Adds embeddingCacheStorage field ("persistent" | "in-memory") to session create and resume across all 6 SDKs. In empty mode, defaults to "in-memory" for multitenant isolation. Companion to runtime PR #8388. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Client.cs | 5 +++ dotnet/src/Types.cs | 9 +++++ go/client.go | 2 ++ go/mode_empty.go | 8 +++++ go/types.go | 15 +++++++- .../com/github/copilot/CopilotClient.java | 6 ++++ .../github/copilot/SessionRequestBuilder.java | 6 ++++ .../copilot/rpc/CreateSessionRequest.java | 19 ++++++++++ .../copilot/rpc/ResumeSessionConfig.java | 35 +++++++++++++++++++ .../copilot/rpc/ResumeSessionRequest.java | 19 ++++++++++ .../com/github/copilot/rpc/SessionConfig.java | 35 +++++++++++++++++++ nodejs/src/client.ts | 3 ++ nodejs/src/types.ts | 10 ++++++ python/copilot/_mode.py | 10 ++++++ python/copilot/client.py | 1 + rust/src/types.rs | 7 ++++ 16 files changed, 189 insertions(+), 1 deletion(-) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 1ca31f6c3..791b102aa 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -638,6 +638,7 @@ private void ApplyConfigDefaultsForMode(SessionConfigBase config) { config.EnableSessionTelemetry ??= false; config.SkipEmbeddingRetrieval ??= true; + config.EmbeddingCacheStorage ??= "in-memory"; config.EnableOnDemandInstructionDiscovery ??= false; config.EnableFileHooks ??= false; config.EnableHostGitOperations ??= false; @@ -881,6 +882,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.ConfigDirectory, config.EnableConfigDiscovery, config.SkipEmbeddingRetrieval, + config.EmbeddingCacheStorage, config.OrganizationCustomInstructions, config.EnableOnDemandInstructionDiscovery, config.EnableFileHooks, @@ -1065,6 +1067,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config.ConfigDirectory, config.EnableConfigDiscovery, config.SkipEmbeddingRetrieval, + config.EmbeddingCacheStorage, config.OrganizationCustomInstructions, config.EnableOnDemandInstructionDiscovery, config.EnableFileHooks, @@ -2196,6 +2199,7 @@ internal record CreateSessionRequest( [property: JsonPropertyName("configDir")] string? ConfigDirectory, bool? EnableConfigDiscovery, bool? SkipEmbeddingRetrieval, + string? EmbeddingCacheStorage, string? OrganizationCustomInstructions, bool? EnableOnDemandInstructionDiscovery, bool? EnableFileHooks, @@ -2271,6 +2275,7 @@ internal record ResumeSessionRequest( [property: JsonPropertyName("configDir")] string? ConfigDirectory, bool? EnableConfigDiscovery, bool? SkipEmbeddingRetrieval, + string? EmbeddingCacheStorage, string? OrganizationCustomInstructions, bool? EnableOnDemandInstructionDiscovery, bool? EnableFileHooks, diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 45e817e58..011692dca 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -2388,6 +2388,7 @@ protected SessionConfigBase(SessionConfigBase? other) DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null; EnableConfigDiscovery = other.EnableConfigDiscovery; SkipEmbeddingRetrieval = other.SkipEmbeddingRetrieval; + EmbeddingCacheStorage = other.EmbeddingCacheStorage; OrganizationCustomInstructions = other.OrganizationCustomInstructions; EnableOnDemandInstructionDiscovery = other.EnableOnDemandInstructionDiscovery; EnableFileHooks = other.EnableFileHooks; @@ -2490,6 +2491,14 @@ protected SessionConfigBase(SessionConfigBase? other) /// public bool? SkipEmbeddingRetrieval { get; set; } + /// + /// Controls how the embedding cache is stored for this session. + /// "persistent": Embeddings are cached on disk and shared across sessions/restarts. + /// "in-memory": Embeddings are cached in memory only and discarded when the session ends. + /// When null, mode "empty" defaults to "in-memory"; in other modes, the runtime default applies. + /// + public string? EmbeddingCacheStorage { get; set; } + /// /// Organization-level custom instructions to include in the system prompt. /// Allows hosts to inject organization-specific guidance without relying on diff --git a/go/client.go b/go/client.go index 217ba530a..62d30c85e 100644 --- a/go/client.go +++ b/go/client.go @@ -610,6 +610,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.EnableConfigDiscovery = Bool(true) } req.SkipEmbeddingRetrieval = config.SkipEmbeddingRetrieval + req.EmbeddingCacheStorage = config.EmbeddingCacheStorage req.OrganizationCustomInstructions = config.OrganizationCustomInstructions req.EnableOnDemandInstructionDiscovery = config.EnableOnDemandInstructionDiscovery req.EnableFileHooks = config.EnableFileHooks @@ -959,6 +960,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.EnableConfigDiscovery = Bool(true) } req.SkipEmbeddingRetrieval = config.SkipEmbeddingRetrieval + req.EmbeddingCacheStorage = config.EmbeddingCacheStorage req.OrganizationCustomInstructions = config.OrganizationCustomInstructions req.EnableOnDemandInstructionDiscovery = config.EnableOnDemandInstructionDiscovery req.EnableFileHooks = config.EnableFileHooks diff --git a/go/mode_empty.go b/go/mode_empty.go index 9461cc577..6e89424cb 100644 --- a/go/mode_empty.go +++ b/go/mode_empty.go @@ -130,6 +130,10 @@ func (c *Client) applyConfigDefaultsForMode(config *SessionConfig) { t := true config.SkipEmbeddingRetrieval = &t } + if config.EmbeddingCacheStorage == nil { + inMemory := "in-memory" + config.EmbeddingCacheStorage = &inMemory + } if config.EnableOnDemandInstructionDiscovery == nil { f := false config.EnableOnDemandInstructionDiscovery = &f @@ -164,6 +168,10 @@ func (c *Client) applyResumeDefaultsForMode(config *ResumeSessionConfig) { t := true config.SkipEmbeddingRetrieval = &t } + if config.EmbeddingCacheStorage == nil { + inMemory := "in-memory" + config.EmbeddingCacheStorage = &inMemory + } if config.EnableOnDemandInstructionDiscovery == nil { f := false config.EnableOnDemandInstructionDiscovery = &f diff --git a/go/types.go b/go/types.go index 9e8172947..edab9bff0 100644 --- a/go/types.go +++ b/go/types.go @@ -909,6 +909,12 @@ type SessionConfig struct { // is passed through to the runtime. Use in multitenant deployments to prevent // cross-session information leakage through the shared embedding cache. SkipEmbeddingRetrieval *bool + // EmbeddingCacheStorage controls how the embedding cache is stored for this session. + // - "persistent": Embeddings are cached on disk and shared across sessions/restarts. + // - "in-memory": Embeddings are cached in memory only and discarded when the session ends. + // When nil, the runtime default ("persistent") is used. When non-nil, the value is + // passed through to the runtime. + EmbeddingCacheStorage *string // OrganizationCustomInstructions provides organization-level custom instructions // to include in the system prompt. When nil, the runtime default is used. When // non-nil, the value (including an empty string) is passed through to the runtime. @@ -1314,6 +1320,12 @@ type ResumeSessionConfig struct { // is passed through to the runtime. Use in multitenant deployments to prevent // cross-session information leakage through the shared embedding cache. SkipEmbeddingRetrieval *bool + // EmbeddingCacheStorage controls how the embedding cache is stored for this session. + // - "persistent": Embeddings are cached on disk and shared across sessions/restarts. + // - "in-memory": Embeddings are cached in memory only and discarded when the session ends. + // When nil, the runtime default ("persistent") is used. When non-nil, the value is + // passed through to the runtime. + EmbeddingCacheStorage *string // OrganizationCustomInstructions provides organization-level custom instructions // to include in the system prompt. When nil, the runtime default is used. When // non-nil, the value (including an empty string) is passed through to the runtime. @@ -1322,7 +1334,6 @@ type ResumeSessionConfig struct { // files after successful file views. When nil, the runtime default is used. When // non-nil, the value (true or false) is passed through to the runtime. EnableOnDemandInstructionDiscovery *bool - // EnableFileHooks controls loading of file-based hooks from .github/hooks/. // When nil, the runtime default is used. When non-nil, the value (true or false) // is passed through to the runtime. EnableFileHooks *bool @@ -1673,6 +1684,7 @@ type createSessionRequest struct { ConfigDir string `json:"configDir,omitempty"` EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` SkipEmbeddingRetrieval *bool `json:"skipEmbeddingRetrieval,omitempty"` + EmbeddingCacheStorage *string `json:"embeddingCacheStorage,omitempty"` OrganizationCustomInstructions *string `json:"organizationCustomInstructions,omitempty"` EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` EnableFileHooks *bool `json:"enableFileHooks,omitempty"` @@ -1740,6 +1752,7 @@ type resumeSessionRequest struct { ConfigDir string `json:"configDir,omitempty"` EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` SkipEmbeddingRetrieval *bool `json:"skipEmbeddingRetrieval,omitempty"` + EmbeddingCacheStorage *string `json:"embeddingCacheStorage,omitempty"` OrganizationCustomInstructions *string `json:"organizationCustomInstructions,omitempty"` EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` EnableFileHooks *bool `json:"enableFileHooks,omitempty"` diff --git a/java/src/main/java/com/github/copilot/CopilotClient.java b/java/src/main/java/com/github/copilot/CopilotClient.java index ff6b3ccb8..aafdfe278 100644 --- a/java/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/src/main/java/com/github/copilot/CopilotClient.java @@ -512,6 +512,9 @@ public CompletableFuture createSession(SessionConfig config) { + "the tools it wants — e.g. setAvailableTools(new ToolSet().addBuiltIn(BuiltInTools.ISOLATED))."); } request.setToolFilterPrecedence("excluded"); + if (request.getEmbeddingCacheStorage() == null) { + request.setEmbeddingCacheStorage("in-memory"); + } } long rpcNanos = System.nanoTime(); @@ -626,6 +629,9 @@ public CompletableFuture resumeSession(String sessionId, ResumeS + "the tools it wants — e.g. setAvailableTools(new ToolSet().addBuiltIn(BuiltInTools.ISOLATED))."); } request.setToolFilterPrecedence("excluded"); + if (request.getEmbeddingCacheStorage() == null) { + request.setEmbeddingCacheStorage("in-memory"); + } } long rpcNanos = System.nanoTime(); diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index b46606deb..097bd334b 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -145,6 +145,9 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess config.getEnableHostGitOperations().ifPresent(request::setEnableHostGitOperations); config.getEnableSessionStore().ifPresent(request::setEnableSessionStore); config.getEnableSkills().ifPresent(request::setEnableSkills); + if (config.getEmbeddingCacheStorage() != null) { + request.setEmbeddingCacheStorage(config.getEmbeddingCacheStorage()); + } request.setModelCapabilities(config.getModelCapabilities()); if (config.getCommands() != null && !config.getCommands().isEmpty()) { @@ -237,6 +240,9 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo config.getEnableHostGitOperations().ifPresent(request::setEnableHostGitOperations); config.getEnableSessionStore().ifPresent(request::setEnableSessionStore); config.getEnableSkills().ifPresent(request::setEnableSkills); + if (config.getEmbeddingCacheStorage() != null) { + request.setEmbeddingCacheStorage(config.getEmbeddingCacheStorage()); + } if (config.isDisableResume()) { request.setDisableResume(true); } diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 685be9f20..069152b59 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -145,6 +145,10 @@ public final class CreateSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableSkills; + @JsonProperty("embeddingCacheStorage") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String embeddingCacheStorage; + @JsonProperty("commands") private List commands; @@ -633,6 +637,21 @@ public void clearEnableSkills() { this.enableSkills = null; } + /** Gets embedding cache storage mode. @return the mode */ + public String getEmbeddingCacheStorage() { + return embeddingCacheStorage; + } + + /** Sets embedding cache storage mode. @param embeddingCacheStorage the mode */ + public void setEmbeddingCacheStorage(String embeddingCacheStorage) { + this.embeddingCacheStorage = embeddingCacheStorage; + } + + /** Clears the embeddingCacheStorage setting, reverting to the default behavior. */ + public void clearEmbeddingCacheStorage() { + this.embeddingCacheStorage = null; + } + /** Gets include sub-agent streaming events flag. @return the flag */ public Boolean getIncludeSubAgentStreamingEvents() { return includeSubAgentStreamingEvents; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 005bee1ce..f63487d85 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -66,6 +66,7 @@ public class ResumeSessionConfig { private Boolean enableHostGitOperations; private Boolean enableSessionStore; private Boolean enableSkills; + private String embeddingCacheStorage; private boolean disableResume; private boolean streaming; private Boolean includeSubAgentStreamingEvents; @@ -883,6 +884,39 @@ public ResumeSessionConfig clearEnableSkills() { return this; } + /** + * Gets the embedding cache storage mode. + * + * @return the embedding cache storage mode ({@code "persistent"} or + * {@code "in-memory"}), or {@code null} to use the runtime default + */ + public String getEmbeddingCacheStorage() { + return embeddingCacheStorage; + } + + /** + * Sets the embedding cache storage mode. + * + * @param embeddingCacheStorage + * {@code "persistent"} to persist embeddings across sessions, or + * {@code "in-memory"} for session-scoped storage + * @return this config for method chaining + */ + public ResumeSessionConfig setEmbeddingCacheStorage(String embeddingCacheStorage) { + this.embeddingCacheStorage = embeddingCacheStorage; + return this; + } + + /** + * Clears the embeddingCacheStorage setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEmbeddingCacheStorage() { + this.embeddingCacheStorage = null; + return this; + } + /** * Gets whether sub-agent streaming events are included. * @@ -1452,6 +1486,7 @@ public ResumeSessionConfig clone() { copy.enableHostGitOperations = this.enableHostGitOperations; copy.enableSessionStore = this.enableSessionStore; copy.enableSkills = this.enableSkills; + copy.embeddingCacheStorage = this.embeddingCacheStorage; copy.disableResume = this.disableResume; copy.streaming = this.streaming; copy.includeSubAgentStreamingEvents = this.includeSubAgentStreamingEvents; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index 8ad8f30ba..0cb4601fa 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -108,6 +108,10 @@ public final class ResumeSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableSkills; + @JsonProperty("embeddingCacheStorage") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String embeddingCacheStorage; + @JsonProperty("disableResume") private Boolean disableResume; @@ -507,6 +511,21 @@ public void clearEnableSkills() { this.enableSkills = null; } + /** Gets embedding cache storage mode. @return the mode */ + public String getEmbeddingCacheStorage() { + return embeddingCacheStorage; + } + + /** Sets embedding cache storage mode. @param embeddingCacheStorage the mode */ + public void setEmbeddingCacheStorage(String embeddingCacheStorage) { + this.embeddingCacheStorage = embeddingCacheStorage; + } + + /** Clears the embeddingCacheStorage setting, reverting to the default behavior. */ + public void clearEmbeddingCacheStorage() { + this.embeddingCacheStorage = null; + } + /** Gets disable resume flag. @return the flag */ public Boolean getDisableResume() { return disableResume; diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 51cc4f4cf..05d5d2f69 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -78,6 +78,7 @@ public class SessionConfig { private Boolean enableHostGitOperations; private Boolean enableSessionStore; private Boolean enableSkills; + private String embeddingCacheStorage; private ModelCapabilitiesOverride modelCapabilities; private Consumer onEvent; private List commands; @@ -1193,6 +1194,39 @@ public SessionConfig clearEnableSkills() { return this; } + /** + * Gets the embedding cache storage mode. + * + * @return the embedding cache storage mode ({@code "persistent"} or + * {@code "in-memory"}), or {@code null} to use the runtime default + */ + public String getEmbeddingCacheStorage() { + return embeddingCacheStorage; + } + + /** + * Sets the embedding cache storage mode. + * + * @param embeddingCacheStorage + * {@code "persistent"} to persist embeddings across sessions, or + * {@code "in-memory"} for session-scoped storage + * @return this config instance for method chaining + */ + public SessionConfig setEmbeddingCacheStorage(String embeddingCacheStorage) { + this.embeddingCacheStorage = embeddingCacheStorage; + return this; + } + + /** + * Clears the embeddingCacheStorage setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEmbeddingCacheStorage() { + this.embeddingCacheStorage = null; + return this; + } + /** * Gets whether sub-agent streaming events are included. * @@ -1589,6 +1623,7 @@ public SessionConfig clone() { copy.enableHostGitOperations = this.enableHostGitOperations; copy.enableSessionStore = this.enableSessionStore; copy.enableSkills = this.enableSkills; + copy.embeddingCacheStorage = this.embeddingCacheStorage; copy.modelCapabilities = this.modelCapabilities; copy.onEvent = this.onEvent; copy.commands = this.commands != null ? new ArrayList<>(this.commands) : null; diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index d73c5f350..fe734d290 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -911,6 +911,7 @@ export class CopilotClient { return { enableSessionTelemetry: false, skipEmbeddingRetrieval: true, + embeddingCacheStorage: "in-memory", enableOnDemandInstructionDiscovery: false, enableFileHooks: false, enableHostGitOperations: false, @@ -1139,6 +1140,7 @@ export class CopilotClient { configDir: config.configDirectory, enableConfigDiscovery: config.enableConfigDiscovery, skipEmbeddingRetrieval: config.skipEmbeddingRetrieval, + embeddingCacheStorage: config.embeddingCacheStorage, organizationCustomInstructions: config.organizationCustomInstructions, enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery, @@ -1311,6 +1313,7 @@ export class CopilotClient { configDir: config.configDirectory, enableConfigDiscovery: config.enableConfigDiscovery, skipEmbeddingRetrieval: config.skipEmbeddingRetrieval, + embeddingCacheStorage: config.embeddingCacheStorage, organizationCustomInstructions: config.organizationCustomInstructions, enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index f4448c139..27baa2c0b 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1913,6 +1913,16 @@ export interface SessionConfigBase { */ skipEmbeddingRetrieval?: boolean; + /** + * Controls how the embedding cache is stored for this session. + * - `"persistent"`: Embeddings are cached on disk and shared across sessions/restarts. + * - `"in-memory"`: Embeddings are cached in memory only and discarded when the session ends. + * Use this for multitenant/SDK deployments where sessions must not share cached embeddings. + * When omitted, `mode: "empty"` defaults this to `"in-memory"`; in other modes, + * the runtime default (`"persistent"`) applies. + */ + embeddingCacheStorage?: "persistent" | "in-memory"; + /** * Organization-level custom instructions to include in the system prompt. * Allows hosts to inject organization-specific guidance without relying on diff --git a/python/copilot/_mode.py b/python/copilot/_mode.py index 64e500b57..467226737 100644 --- a/python/copilot/_mode.py +++ b/python/copilot/_mode.py @@ -198,6 +198,16 @@ def _skip_embedding_retrieval_default( return _empty_mode_bool_default(mode, supplied, True) +def _embedding_cache_storage_default( + mode: CopilotClientMode | None, + supplied: str | None, +) -> str | None: + """Empty mode defaults embedding cache storage to in-memory; caller value wins.""" + if mode == "empty" and supplied is None: + return "in-memory" + return supplied + + def _enable_on_demand_instruction_discovery_default( mode: CopilotClientMode | None, supplied: bool | None, diff --git a/python/copilot/client.py b/python/copilot/client.py index 8200bcdd1..22e24231d 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -37,6 +37,7 @@ from ._mode import ( CopilotClientMode, ToolSet, + _embedding_cache_storage_default, _enable_file_hooks_default, _enable_host_git_operations_default, _enable_on_demand_instruction_discovery_default, diff --git a/rust/src/types.rs b/rust/src/types.rs index 5071a742f..f12cd4dce 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1186,6 +1186,9 @@ pub struct SessionConfig { pub enable_config_discovery: Option, /// When true, skips embedding retrieval for this session. pub skip_embedding_retrieval: Option, + /// Controls how the embedding cache is stored for this session. + /// `"persistent"` caches on disk; `"in-memory"` discards when session ends. + pub embedding_cache_storage: Option, /// Organization-level custom instructions to apply to this session. pub organization_custom_instructions: Option, /// When true, enables on-demand instruction discovery for this session. @@ -1486,6 +1489,7 @@ impl Default for SessionConfig { enable_host_git_operations: None, enable_session_store: None, enable_skills: None, + embedding_cache_storage: None, enable_mcp_apps: None, skill_directories: None, instruction_directories: None, @@ -2153,6 +2157,8 @@ pub struct ResumeSessionConfig { pub enable_config_discovery: Option, /// When true, skips embedding retrieval on resume. pub skip_embedding_retrieval: Option, + /// Controls how the embedding cache is stored for this session. + pub embedding_cache_storage: Option, /// Organization-level custom instructions to apply on resume. pub organization_custom_instructions: Option, /// When true, enables on-demand instruction discovery on resume. @@ -2519,6 +2525,7 @@ impl ResumeSessionConfig { enable_host_git_operations: None, enable_session_store: None, enable_skills: None, + embedding_cache_storage: None, enable_mcp_apps: None, skill_directories: None, instruction_directories: None, From 9c331912ac1d9b23e72e520b3580705807572756 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 28 May 2026 15:17:23 -0700 Subject: [PATCH 04/10] fix: apply formatting, convert EmbeddingCacheStorage to enum in .NET, improve tests - Run formatters across all SDKs (prettier, gofmt, ruff, cargo fmt, spotless, dotnet format) - Convert EmbeddingCacheStorage from string to EmbeddingCacheStorageMode enum in .NET - Rename test methods to avoid temporal wording - Expand EnableFileHooks doc comment in Go ResumeSessionConfig - Add embeddingCacheStorage test coverage in Go, Java Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Client.cs | 7 +-- dotnet/src/Types.cs | 23 ++++++++-- dotnet/test/E2E/ClientOptionsE2ETests.cs | 10 ++++- go/toolset_test.go | 17 ++++--- go/types.go | 4 +- .../copilot/rpc/CreateSessionRequest.java | 29 +++++++++--- .../copilot/rpc/ResumeSessionConfig.java | 4 +- .../copilot/rpc/ResumeSessionRequest.java | 29 +++++++++--- .../com/github/copilot/rpc/SessionConfig.java | 4 +- .../com/github/copilot/ConfigCloneTest.java | 23 ++++------ .../copilot/OptionalApiAndJacksonTest.java | 43 +++++++++++++++--- .../copilot/SessionRequestBuilderTest.java | 20 +++------ nodejs/src/client.ts | 6 +-- python/copilot/client.py | 44 ++++++++++--------- rust/src/session.rs | 6 +++ rust/src/types.rs | 8 +++- rust/src/wire.rs | 4 ++ 17 files changed, 189 insertions(+), 92 deletions(-) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 47e5d003b..9608d98c3 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -638,7 +638,7 @@ private void ApplyConfigDefaultsForMode(SessionConfigBase config) { config.EnableSessionTelemetry ??= false; config.SkipEmbeddingRetrieval ??= true; - config.EmbeddingCacheStorage ??= "in-memory"; + config.EmbeddingCacheStorage ??= EmbeddingCacheStorageMode.InMemory; config.EnableOnDemandInstructionDiscovery ??= false; config.EnableFileHooks ??= false; config.EnableHostGitOperations ??= false; @@ -2203,7 +2203,7 @@ internal record CreateSessionRequest( [property: JsonPropertyName("configDir")] string? ConfigDirectory, bool? EnableConfigDiscovery, bool? SkipEmbeddingRetrieval, - string? EmbeddingCacheStorage, + EmbeddingCacheStorageMode? EmbeddingCacheStorage, string? OrganizationCustomInstructions, bool? EnableOnDemandInstructionDiscovery, bool? EnableFileHooks, @@ -2279,7 +2279,7 @@ internal record ResumeSessionRequest( [property: JsonPropertyName("configDir")] string? ConfigDirectory, bool? EnableConfigDiscovery, bool? SkipEmbeddingRetrieval, - string? EmbeddingCacheStorage, + EmbeddingCacheStorageMode? EmbeddingCacheStorage, string? OrganizationCustomInstructions, bool? EnableOnDemandInstructionDiscovery, bool? EnableFileHooks, @@ -2388,6 +2388,7 @@ internal record HooksInvokeResponse( [JsonSerializable(typeof(GetSessionMetadataRequest))] [JsonSerializable(typeof(GetSessionMetadataResponse))] [JsonSerializable(typeof(McpOAuthTokenStorageMode))] + [JsonSerializable(typeof(EmbeddingCacheStorageMode))] [JsonSerializable(typeof(ModelCapabilitiesOverride))] [JsonSerializable(typeof(ProviderConfig))] [JsonSerializable(typeof(ResumeSessionRequest))] diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 5132c2ab4..ed3ec570e 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -2096,6 +2096,21 @@ public enum McpOAuthTokenStorageMode InMemory } +/// +/// Controls how the embedding cache is stored for a session. +/// +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum EmbeddingCacheStorageMode +{ + /// Embeddings are cached on disk, shared across sessions and restarts. + [JsonStringEnumMemberName("persistent")] + Persistent, + + /// Embeddings are cached in memory only and discarded when the session ends. + [JsonStringEnumMemberName("in-memory")] + InMemory +} + /// /// Abstract base class for MCP server configurations. /// @@ -2509,11 +2524,11 @@ protected SessionConfigBase(SessionConfigBase? other) /// /// Controls how the embedding cache is stored for this session. - /// "persistent": Embeddings are cached on disk and shared across sessions/restarts. - /// "in-memory": Embeddings are cached in memory only and discarded when the session ends. - /// When null, mode "empty" defaults to "in-memory"; in other modes, the runtime default applies. + /// : Embeddings are cached on disk and shared across sessions/restarts. + /// : Embeddings are cached in memory only and discarded when the session ends. + /// When null, mode "empty" defaults to ; in other modes, the runtime default applies. /// - public string? EmbeddingCacheStorage { get; set; } + public EmbeddingCacheStorageMode? EmbeddingCacheStorage { get; set; } /// /// Organization-level custom instructions to include in the system prompt. diff --git a/dotnet/test/E2E/ClientOptionsE2ETests.cs b/dotnet/test/E2E/ClientOptionsE2ETests.cs index 3686c8a08..fbce617ac 100644 --- a/dotnet/test/E2E/ClientOptionsE2ETests.cs +++ b/dotnet/test/E2E/ClientOptionsE2ETests.cs @@ -178,7 +178,7 @@ public async Task Should_Omit_EnableSessionTelemetry_When_Not_Set() } [Fact] - public async Task Should_Forward_New_Session_Config_Fields_In_Create_Wire_Request() + public async Task Should_Forward_Granular_Multitenancy_Fields_In_Create_Wire_Request() { var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); @@ -195,6 +195,7 @@ public async Task Should_Forward_New_Session_Config_Fields_In_Create_Wire_Reques SkipEmbeddingRetrieval = false, OrganizationCustomInstructions = "Follow org policy.", EnableOnDemandInstructionDiscovery = true, + EmbeddingCacheStorage = EmbeddingCacheStorageMode.Persistent, EnableFileHooks = true, EnableHostGitOperations = false, EnableSessionStore = true, @@ -207,6 +208,7 @@ public async Task Should_Forward_New_Session_Config_Fields_In_Create_Wire_Reques Assert.False(createRequest.GetProperty("skipEmbeddingRetrieval").GetBoolean()); Assert.Equal("Follow org policy.", createRequest.GetProperty("organizationCustomInstructions").GetString()); Assert.True(createRequest.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean()); + Assert.Equal("persistent", createRequest.GetProperty("embeddingCacheStorage").GetString()); Assert.True(createRequest.GetProperty("enableFileHooks").GetBoolean()); Assert.False(createRequest.GetProperty("enableHostGitOperations").GetBoolean()); Assert.True(createRequest.GetProperty("enableSessionStore").GetBoolean()); @@ -239,6 +241,7 @@ public async Task Should_Apply_Empty_Mode_Defaults_To_CreateSession_Wire_Request Assert.False(createRequest.GetProperty("enableSessionTelemetry").GetBoolean()); Assert.True(createRequest.GetProperty("skipEmbeddingRetrieval").GetBoolean()); Assert.False(createRequest.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean()); + Assert.Equal("in-memory", createRequest.GetProperty("embeddingCacheStorage").GetString()); Assert.False(createRequest.GetProperty("enableFileHooks").GetBoolean()); Assert.False(createRequest.GetProperty("enableHostGitOperations").GetBoolean()); Assert.False(createRequest.GetProperty("enableSessionStore").GetBoolean()); @@ -365,7 +368,7 @@ public async Task Should_Propagate_Activity_TraceContext_To_Session_Resume() } [Fact] - public async Task Should_Forward_New_Session_Config_Fields_In_Resume_Wire_Request() + public async Task Should_Forward_Granular_Multitenancy_Fields_In_Resume_Wire_Request() { var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); @@ -382,6 +385,7 @@ public async Task Should_Forward_New_Session_Config_Fields_In_Resume_Wire_Reques SkipEmbeddingRetrieval = false, OrganizationCustomInstructions = "Resume org policy.", EnableOnDemandInstructionDiscovery = true, + EmbeddingCacheStorage = EmbeddingCacheStorageMode.Persistent, EnableFileHooks = true, EnableHostGitOperations = false, EnableSessionStore = true, @@ -394,6 +398,7 @@ public async Task Should_Forward_New_Session_Config_Fields_In_Resume_Wire_Reques Assert.False(resumeRequest.GetProperty("skipEmbeddingRetrieval").GetBoolean()); Assert.Equal("Resume org policy.", resumeRequest.GetProperty("organizationCustomInstructions").GetString()); Assert.True(resumeRequest.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean()); + Assert.Equal("persistent", resumeRequest.GetProperty("embeddingCacheStorage").GetString()); Assert.True(resumeRequest.GetProperty("enableFileHooks").GetBoolean()); Assert.False(resumeRequest.GetProperty("enableHostGitOperations").GetBoolean()); Assert.True(resumeRequest.GetProperty("enableSessionStore").GetBoolean()); @@ -426,6 +431,7 @@ public async Task Should_Apply_Empty_Mode_Defaults_To_ResumeSession_Wire_Request Assert.False(resumeRequest.GetProperty("enableSessionTelemetry").GetBoolean()); Assert.True(resumeRequest.GetProperty("skipEmbeddingRetrieval").GetBoolean()); Assert.False(resumeRequest.GetProperty("enableOnDemandInstructionDiscovery").GetBoolean()); + Assert.Equal("in-memory", resumeRequest.GetProperty("embeddingCacheStorage").GetString()); Assert.False(resumeRequest.GetProperty("enableFileHooks").GetBoolean()); Assert.False(resumeRequest.GetProperty("enableHostGitOperations").GetBoolean()); Assert.False(resumeRequest.GetProperty("enableSessionStore").GetBoolean()); diff --git a/go/toolset_test.go b/go/toolset_test.go index b2e63a98f..babe63502 100644 --- a/go/toolset_test.go +++ b/go/toolset_test.go @@ -255,6 +255,9 @@ func TestApplyConfigDefaultsForMode_emptyDefaultsGranularFlags(t *testing.T) { if cfg.SkipEmbeddingRetrieval == nil || *cfg.SkipEmbeddingRetrieval != true { t.Errorf("expected SkipEmbeddingRetrieval=true in empty mode, got %v", cfg.SkipEmbeddingRetrieval) } + if cfg.EmbeddingCacheStorage == nil || *cfg.EmbeddingCacheStorage != *String("in-memory") { + t.Errorf("expected EmbeddingCacheStorage=in-memory in empty mode, got %v", cfg.EmbeddingCacheStorage) + } if cfg.EnableOnDemandInstructionDiscovery == nil || *cfg.EnableOnDemandInstructionDiscovery != false { t.Errorf("expected EnableOnDemandInstructionDiscovery=false in empty mode, got %v", cfg.EnableOnDemandInstructionDiscovery) } @@ -277,17 +280,21 @@ func TestApplyConfigDefaultsForMode_emptyHonorsCallerGranularFlags(t *testing.T) falseVal := false trueVal := true cfg := &SessionConfig{ - SkipEmbeddingRetrieval: &falseVal, + SkipEmbeddingRetrieval: &falseVal, + EmbeddingCacheStorage: String("persistent"), EnableOnDemandInstructionDiscovery: &trueVal, - EnableFileHooks: &trueVal, - EnableHostGitOperations: &trueVal, - EnableSessionStore: &trueVal, - EnableSkills: &trueVal, + EnableFileHooks: &trueVal, + EnableHostGitOperations: &trueVal, + EnableSessionStore: &trueVal, + EnableSkills: &trueVal, } c.applyConfigDefaultsForMode(cfg) if *cfg.SkipEmbeddingRetrieval != false { t.Errorf("caller-supplied SkipEmbeddingRetrieval must win") } + if *cfg.EmbeddingCacheStorage != *String("persistent") { + t.Errorf("caller-supplied EmbeddingCacheStorage must win") + } if *cfg.EnableOnDemandInstructionDiscovery != true { t.Errorf("caller-supplied EnableOnDemandInstructionDiscovery must win") } diff --git a/go/types.go b/go/types.go index 7476bdc0f..396159b0b 100644 --- a/go/types.go +++ b/go/types.go @@ -1337,8 +1337,10 @@ type ResumeSessionConfig struct { // files after successful file views. When nil, the runtime default is used. When // non-nil, the value (true or false) is passed through to the runtime. EnableOnDemandInstructionDiscovery *bool + // EnableFileHooks controls loading of file-based hooks from .github/hooks/. // When nil, the runtime default is used. When non-nil, the value (true or false) - // is passed through to the runtime. + // is passed through to the runtime. This is separate from the Hooks callback + // parameter which gates SDK hook event registration. EnableFileHooks *bool // EnableHostGitOperations controls git operations on the host filesystem. When // nil, the runtime default is used. When non-nil, the value (true or false) is diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 662e621c8..c5dc200f9 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -553,12 +553,16 @@ public Boolean getSkipEmbeddingRetrieval() { return skipEmbeddingRetrieval; } - /** Sets skip embedding retrieval flag. @param skipEmbeddingRetrieval the flag */ + /** + * Sets skip embedding retrieval flag. @param skipEmbeddingRetrieval the flag + */ public void setSkipEmbeddingRetrieval(boolean skipEmbeddingRetrieval) { this.skipEmbeddingRetrieval = skipEmbeddingRetrieval; } - /** Clears the skipEmbeddingRetrieval setting, reverting to the default behavior. */ + /** + * Clears the skipEmbeddingRetrieval setting, reverting to the default behavior. + */ public void clearSkipEmbeddingRetrieval() { this.skipEmbeddingRetrieval = null; } @@ -568,7 +572,10 @@ public String getOrganizationCustomInstructions() { return organizationCustomInstructions; } - /** Sets organization custom instructions. @param organizationCustomInstructions the instructions */ + /** + * Sets organization custom instructions. @param organizationCustomInstructions + * the instructions + */ public void setOrganizationCustomInstructions(String organizationCustomInstructions) { this.organizationCustomInstructions = organizationCustomInstructions; } @@ -587,7 +594,8 @@ public void setEnableOnDemandInstructionDiscovery(boolean enableOnDemandInstruct } /** - * Clears the enableOnDemandInstructionDiscovery setting, reverting to the default behavior. + * Clears the enableOnDemandInstructionDiscovery setting, reverting to the + * default behavior. */ public void clearEnableOnDemandInstructionDiscovery() { this.enableOnDemandInstructionDiscovery = null; @@ -613,12 +621,17 @@ public Boolean getEnableHostGitOperations() { return enableHostGitOperations; } - /** Sets enable host git operations flag. @param enableHostGitOperations the flag */ + /** + * Sets enable host git operations flag. @param enableHostGitOperations the flag + */ public void setEnableHostGitOperations(boolean enableHostGitOperations) { this.enableHostGitOperations = enableHostGitOperations; } - /** Clears the enableHostGitOperations setting, reverting to the default behavior. */ + /** + * Clears the enableHostGitOperations setting, reverting to the default + * behavior. + */ public void clearEnableHostGitOperations() { this.enableHostGitOperations = null; } @@ -663,7 +676,9 @@ public void setEmbeddingCacheStorage(String embeddingCacheStorage) { this.embeddingCacheStorage = embeddingCacheStorage; } - /** Clears the embeddingCacheStorage setting, reverting to the default behavior. */ + /** + * Clears the embeddingCacheStorage setting, reverting to the default behavior. + */ public void clearEmbeddingCacheStorage() { this.embeddingCacheStorage = null; } diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 67cc87384..0a26fd6e7 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -669,8 +669,8 @@ public Optional getSkipEmbeddingRetrieval() { * Sets whether to skip embedding-based retrieval. * * @param skipEmbeddingRetrieval - * {@code true} to skip embedding retrieval, {@code false} to keep - * it enabled + * {@code true} to skip embedding retrieval, {@code false} to keep it + * enabled * @return this config for method chaining */ public ResumeSessionConfig setSkipEmbeddingRetrieval(boolean skipEmbeddingRetrieval) { diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index 40c74da6c..66cee6b95 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -414,12 +414,16 @@ public Boolean getSkipEmbeddingRetrieval() { return skipEmbeddingRetrieval; } - /** Sets skip embedding retrieval flag. @param skipEmbeddingRetrieval the flag */ + /** + * Sets skip embedding retrieval flag. @param skipEmbeddingRetrieval the flag + */ public void setSkipEmbeddingRetrieval(boolean skipEmbeddingRetrieval) { this.skipEmbeddingRetrieval = skipEmbeddingRetrieval; } - /** Clears the skipEmbeddingRetrieval setting, reverting to the default behavior. */ + /** + * Clears the skipEmbeddingRetrieval setting, reverting to the default behavior. + */ public void clearSkipEmbeddingRetrieval() { this.skipEmbeddingRetrieval = null; } @@ -429,7 +433,10 @@ public String getOrganizationCustomInstructions() { return organizationCustomInstructions; } - /** Sets organization custom instructions. @param organizationCustomInstructions the instructions */ + /** + * Sets organization custom instructions. @param organizationCustomInstructions + * the instructions + */ public void setOrganizationCustomInstructions(String organizationCustomInstructions) { this.organizationCustomInstructions = organizationCustomInstructions; } @@ -448,7 +455,8 @@ public void setEnableOnDemandInstructionDiscovery(boolean enableOnDemandInstruct } /** - * Clears the enableOnDemandInstructionDiscovery setting, reverting to the default behavior. + * Clears the enableOnDemandInstructionDiscovery setting, reverting to the + * default behavior. */ public void clearEnableOnDemandInstructionDiscovery() { this.enableOnDemandInstructionDiscovery = null; @@ -474,12 +482,17 @@ public Boolean getEnableHostGitOperations() { return enableHostGitOperations; } - /** Sets enable host git operations flag. @param enableHostGitOperations the flag */ + /** + * Sets enable host git operations flag. @param enableHostGitOperations the flag + */ public void setEnableHostGitOperations(boolean enableHostGitOperations) { this.enableHostGitOperations = enableHostGitOperations; } - /** Clears the enableHostGitOperations setting, reverting to the default behavior. */ + /** + * Clears the enableHostGitOperations setting, reverting to the default + * behavior. + */ public void clearEnableHostGitOperations() { this.enableHostGitOperations = null; } @@ -524,7 +537,9 @@ public void setEmbeddingCacheStorage(String embeddingCacheStorage) { this.embeddingCacheStorage = embeddingCacheStorage; } - /** Clears the embeddingCacheStorage setting, reverting to the default behavior. */ + /** + * Clears the embeddingCacheStorage setting, reverting to the default behavior. + */ public void clearEmbeddingCacheStorage() { this.embeddingCacheStorage = null; } diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 3221d0b5a..748f938df 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -1010,8 +1010,8 @@ public Optional getSkipEmbeddingRetrieval() { * Sets whether to skip embedding-based retrieval. * * @param skipEmbeddingRetrieval - * {@code true} to skip embedding retrieval, {@code false} to keep - * it enabled + * {@code true} to skip embedding retrieval, {@code false} to keep it + * enabled * @return this config instance for method chaining */ public SessionConfig setSkipEmbeddingRetrieval(boolean skipEmbeddingRetrieval) { diff --git a/java/src/test/java/com/github/copilot/ConfigCloneTest.java b/java/src/test/java/com/github/copilot/ConfigCloneTest.java index 8ba815cf6..bda6214ef 100644 --- a/java/src/test/java/com/github/copilot/ConfigCloneTest.java +++ b/java/src/test/java/com/github/copilot/ConfigCloneTest.java @@ -232,20 +232,17 @@ void sessionConfigEnableSessionTelemetryDefaultIsNull() { @Test void sessionConfigNewSessionFieldsCopied() { - SessionConfig original = new SessionConfig() - .setSkipEmbeddingRetrieval(true) - .setOrganizationCustomInstructions("Org instructions") - .setEnableOnDemandInstructionDiscovery(false) - .setEnableFileHooks(true) - .setEnableHostGitOperations(false) - .setEnableSessionStore(true) - .setEnableSkills(false); + SessionConfig original = new SessionConfig().setSkipEmbeddingRetrieval(true) + .setOrganizationCustomInstructions("Org instructions").setEnableOnDemandInstructionDiscovery(false) + .setEmbeddingCacheStorage("persistent").setEnableFileHooks(true).setEnableHostGitOperations(false) + .setEnableSessionStore(true).setEnableSkills(false); SessionConfig cloned = original.clone(); assertTrue(cloned.getSkipEmbeddingRetrieval().orElse(false)); assertEquals("Org instructions", cloned.getOrganizationCustomInstructions()); assertFalse(cloned.getEnableOnDemandInstructionDiscovery().orElse(true)); + assertEquals("persistent", cloned.getEmbeddingCacheStorage()); assertTrue(cloned.getEnableFileHooks().orElse(false)); assertFalse(cloned.getEnableHostGitOperations().orElse(true)); assertTrue(cloned.getEnableSessionStore().orElse(false)); @@ -273,13 +270,10 @@ void resumeSessionConfigEnableSessionTelemetryDefaultIsNull() { @Test void resumeSessionConfigNewSessionFieldsCopied() { - ResumeSessionConfig original = new ResumeSessionConfig() - .setSkipEmbeddingRetrieval(false) + ResumeSessionConfig original = new ResumeSessionConfig().setSkipEmbeddingRetrieval(false) .setOrganizationCustomInstructions("Resume org instructions") - .setEnableOnDemandInstructionDiscovery(true) - .setEnableFileHooks(false) - .setEnableHostGitOperations(true) - .setEnableSessionStore(false) + .setEnableOnDemandInstructionDiscovery(true).setEmbeddingCacheStorage("persistent") + .setEnableFileHooks(false).setEnableHostGitOperations(true).setEnableSessionStore(false) .setEnableSkills(true); ResumeSessionConfig cloned = original.clone(); @@ -287,6 +281,7 @@ void resumeSessionConfigNewSessionFieldsCopied() { assertFalse(cloned.getSkipEmbeddingRetrieval().orElse(true)); assertEquals("Resume org instructions", cloned.getOrganizationCustomInstructions()); assertTrue(cloned.getEnableOnDemandInstructionDiscovery().orElse(false)); + assertEquals("persistent", cloned.getEmbeddingCacheStorage()); assertFalse(cloned.getEnableFileHooks().orElse(true)); assertTrue(cloned.getEnableHostGitOperations().orElse(false)); assertFalse(cloned.getEnableSessionStore().orElse(true)); diff --git a/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java b/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java index aa10bb17e..f64345780 100644 --- a/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java +++ b/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java @@ -359,6 +359,7 @@ void sessionConfig_newSessionFieldsValue() { assertTrue(cfg.getSkipEmbeddingRetrieval().isEmpty()); assertNull(cfg.getOrganizationCustomInstructions()); assertTrue(cfg.getEnableOnDemandInstructionDiscovery().isEmpty()); + assertNull(cfg.getEmbeddingCacheStorage()); assertTrue(cfg.getEnableFileHooks().isEmpty()); assertTrue(cfg.getEnableHostGitOperations().isEmpty()); assertTrue(cfg.getEnableSessionStore().isEmpty()); @@ -367,6 +368,7 @@ void sessionConfig_newSessionFieldsValue() { cfg.setSkipEmbeddingRetrieval(true); cfg.setOrganizationCustomInstructions("Org instructions"); cfg.setEnableOnDemandInstructionDiscovery(false); + cfg.setEmbeddingCacheStorage("persistent"); cfg.setEnableFileHooks(true); cfg.setEnableHostGitOperations(false); cfg.setEnableSessionStore(true); @@ -375,6 +377,7 @@ void sessionConfig_newSessionFieldsValue() { assertTrue(cfg.getSkipEmbeddingRetrieval().get()); assertEquals("Org instructions", cfg.getOrganizationCustomInstructions()); assertFalse(cfg.getEnableOnDemandInstructionDiscovery().get()); + assertEquals("persistent", cfg.getEmbeddingCacheStorage()); assertTrue(cfg.getEnableFileHooks().get()); assertFalse(cfg.getEnableHostGitOperations().get()); assertTrue(cfg.getEnableSessionStore().get()); @@ -417,6 +420,7 @@ void resumeSessionConfig_newSessionFieldsValue() { assertTrue(cfg.getSkipEmbeddingRetrieval().isEmpty()); assertNull(cfg.getOrganizationCustomInstructions()); assertTrue(cfg.getEnableOnDemandInstructionDiscovery().isEmpty()); + assertNull(cfg.getEmbeddingCacheStorage()); assertTrue(cfg.getEnableFileHooks().isEmpty()); assertTrue(cfg.getEnableHostGitOperations().isEmpty()); assertTrue(cfg.getEnableSessionStore().isEmpty()); @@ -425,6 +429,7 @@ void resumeSessionConfig_newSessionFieldsValue() { cfg.setSkipEmbeddingRetrieval(false); cfg.setOrganizationCustomInstructions("Resume org instructions"); cfg.setEnableOnDemandInstructionDiscovery(true); + cfg.setEmbeddingCacheStorage("persistent"); cfg.setEnableFileHooks(false); cfg.setEnableHostGitOperations(true); cfg.setEnableSessionStore(false); @@ -433,6 +438,7 @@ void resumeSessionConfig_newSessionFieldsValue() { assertFalse(cfg.getSkipEmbeddingRetrieval().get()); assertEquals("Resume org instructions", cfg.getOrganizationCustomInstructions()); assertTrue(cfg.getEnableOnDemandInstructionDiscovery().get()); + assertEquals("persistent", cfg.getEmbeddingCacheStorage()); assertFalse(cfg.getEnableFileHooks().get()); assertTrue(cfg.getEnableHostGitOperations().get()); assertFalse(cfg.getEnableSessionStore().get()); @@ -621,11 +627,38 @@ void jackson_deserializeInfiniteSessionConfigEmpty() throws Exception { // access: Jackson writes the field when set and omits it when cleared. // // Classes without @JsonProperty on fields (SessionConfig, - // CopilotClientOptions, TelemetryConfig, ProviderConfig) are not - // directly serialized — their values are copied to wire DTOs by - // SessionRequestBuilder. The @JsonIgnore on their Optional-returning - // getters prevents Jackson from attempting to serialize Optional - // wrappers if the class is ever processed. + // CopilotClientOptions, TelemetryConfig, ProviderConfig) are normally + // copied to wire DTOs by SessionRequestBuilder. Their scalar getters can + // still be serialized directly, while @JsonIgnore on Optional-returning + // getters prevents Jackson from attempting to serialize Optional wrappers. + + @Test + void jackson_sessionConfigEmbeddingCacheStorageSerialized() throws Exception { + var cfg = new SessionConfig(); + cfg.setEmbeddingCacheStorage("persistent"); + + String withField = MAPPER.writeValueAsString(cfg); + assertTrue(withField.contains("\"embeddingCacheStorage\":\"persistent\"")); + + cfg.clearEmbeddingCacheStorage(); + + String cleared = MAPPER.writeValueAsString(cfg); + assertFalse(cleared.contains("embeddingCacheStorage")); + } + + @Test + void jackson_resumeSessionConfigEmbeddingCacheStorageSerialized() throws Exception { + var cfg = new ResumeSessionConfig(); + cfg.setEmbeddingCacheStorage("persistent"); + + String withField = MAPPER.writeValueAsString(cfg); + assertTrue(withField.contains("\"embeddingCacheStorage\":\"persistent\"")); + + cfg.clearEmbeddingCacheStorage(); + + String cleared = MAPPER.writeValueAsString(cfg); + assertFalse(cleared.contains("embeddingCacheStorage")); + } @Test void jackson_infiniteSessionConfigClearedFieldsOmitted() throws Exception { diff --git a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java index 9ef5e4810..606a4d6d8 100644 --- a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -252,14 +252,10 @@ void testBuildResumeRequestSetsClientName() { @Test void testBuildCreateRequestPropagatesNewSessionFields() { - var config = new SessionConfig() - .setSkipEmbeddingRetrieval(true) + var config = new SessionConfig().setSkipEmbeddingRetrieval(true) .setOrganizationCustomInstructions("Create org instructions") - .setEnableOnDemandInstructionDiscovery(false) - .setEnableFileHooks(true) - .setEnableHostGitOperations(false) - .setEnableSessionStore(true) - .setEnableSkills(false); + .setEnableOnDemandInstructionDiscovery(false).setEnableFileHooks(true).setEnableHostGitOperations(false) + .setEnableSessionStore(true).setEnableSkills(false); CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); @@ -274,14 +270,10 @@ void testBuildCreateRequestPropagatesNewSessionFields() { @Test void testBuildResumeRequestPropagatesNewSessionFields() { - var config = new ResumeSessionConfig() - .setSkipEmbeddingRetrieval(false) + var config = new ResumeSessionConfig().setSkipEmbeddingRetrieval(false) .setOrganizationCustomInstructions("Resume org instructions") - .setEnableOnDemandInstructionDiscovery(true) - .setEnableFileHooks(false) - .setEnableHostGitOperations(true) - .setEnableSessionStore(false) - .setEnableSkills(true); + .setEnableOnDemandInstructionDiscovery(true).setEnableFileHooks(false).setEnableHostGitOperations(true) + .setEnableSessionStore(false).setEnableSkills(true); ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-11", config); diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index c400c40c9..5ee36b78b 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1144,8 +1144,7 @@ export class CopilotClient { skipEmbeddingRetrieval: config.skipEmbeddingRetrieval, embeddingCacheStorage: config.embeddingCacheStorage, organizationCustomInstructions: config.organizationCustomInstructions, - enableOnDemandInstructionDiscovery: - config.enableOnDemandInstructionDiscovery, + enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery, enableFileHooks: config.enableFileHooks, enableHostGitOperations: config.enableHostGitOperations, enableSessionStore: config.enableSessionStore, @@ -1317,8 +1316,7 @@ export class CopilotClient { skipEmbeddingRetrieval: config.skipEmbeddingRetrieval, embeddingCacheStorage: config.embeddingCacheStorage, organizationCustomInstructions: config.organizationCustomInstructions, - enableOnDemandInstructionDiscovery: - config.enableOnDemandInstructionDiscovery, + enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery, enableFileHooks: config.enableFileHooks, enableHostGitOperations: config.enableHostGitOperations, enableSessionStore: config.enableSessionStore, diff --git a/python/copilot/client.py b/python/copilot/client.py index 6498dc749..66c56a595 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -1580,6 +1580,7 @@ async def create_session( include_sub_agent_streaming_events: bool | None = None, mcp_servers: dict[str, MCPServerConfig] | None = None, mcp_oauth_token_storage: Literal["persistent", "in-memory"] | None = None, + embedding_cache_storage: Literal["persistent", "in-memory"] | None = None, custom_agents: list[CustomAgentConfig] | None = None, default_agent: DefaultAgentConfig | dict[str, Any] | None = None, agent: str | None = None, @@ -1665,6 +1666,10 @@ async def create_session( ``"persistent"`` uses the OS keychain (shared across sessions). ``"in-memory"`` stores tokens in memory (discarded on session end). Defaults to ``"in-memory"`` for safe multitenant behavior. + embedding_cache_storage: Controls how embedding caches are stored. + `"persistent"` uses disk-based storage (shared across sessions). + `"in-memory"` stores embeddings in memory (discarded on session end). + Defaults to `"in-memory"` in empty mode. custom_agents: Custom agent configurations. default_agent: Configuration for the default agent, including tool visibility controls. @@ -1754,13 +1759,9 @@ async def create_session( # Mode "empty" defaults selected session config flags to restrictive values; # caller-supplied values win. enable_session_telemetry = _enable_session_telemetry_default(mode, enable_session_telemetry) - skip_embedding_retrieval = _skip_embedding_retrieval_default( - mode, skip_embedding_retrieval - ) - enable_on_demand_instruction_discovery = ( - _enable_on_demand_instruction_discovery_default( - mode, enable_on_demand_instruction_discovery - ) + skip_embedding_retrieval = _skip_embedding_retrieval_default(mode, skip_embedding_retrieval) + enable_on_demand_instruction_discovery = _enable_on_demand_instruction_discovery_default( + mode, enable_on_demand_instruction_discovery ) enable_file_hooks = _enable_file_hooks_default(mode, enable_file_hooks) enable_host_git_operations = _enable_host_git_operations_default( @@ -1862,6 +1863,9 @@ async def create_session( mcp_oauth_token_storage = _mcp_oauth_token_storage_default(mode, mcp_oauth_token_storage) if mcp_oauth_token_storage is not None: payload["mcpOAuthTokenStorage"] = mcp_oauth_token_storage + embedding_cache_storage = _embedding_cache_storage_default(mode, embedding_cache_storage) + if embedding_cache_storage is not None: + payload["embeddingCacheStorage"] = embedding_cache_storage payload["envValueMode"] = "direct" # Add custom agents configuration if provided @@ -1890,9 +1894,7 @@ async def create_session( if organization_custom_instructions is not None: payload["organizationCustomInstructions"] = organization_custom_instructions if enable_on_demand_instruction_discovery is not None: - payload["enableOnDemandInstructionDiscovery"] = ( - enable_on_demand_instruction_discovery - ) + payload["enableOnDemandInstructionDiscovery"] = enable_on_demand_instruction_discovery if enable_file_hooks is not None: payload["enableFileHooks"] = enable_file_hooks if enable_host_git_operations is not None: @@ -2139,6 +2141,7 @@ async def resume_session( include_sub_agent_streaming_events: bool | None = None, mcp_servers: dict[str, MCPServerConfig] | None = None, mcp_oauth_token_storage: Literal["persistent", "in-memory"] | None = None, + embedding_cache_storage: Literal["persistent", "in-memory"] | None = None, custom_agents: list[CustomAgentConfig] | None = None, default_agent: DefaultAgentConfig | dict[str, Any] | None = None, agent: str | None = None, @@ -2225,6 +2228,10 @@ async def resume_session( ``"persistent"`` uses the OS keychain (shared across sessions). ``"in-memory"`` stores tokens in memory (discarded on session end). Defaults to ``"in-memory"`` for safe multitenant behavior. + embedding_cache_storage: Controls how embedding caches are stored. + `"persistent"` uses disk-based storage (shared across sessions). + `"in-memory"` stores embeddings in memory (discarded on session end). + Defaults to `"in-memory"` in empty mode. custom_agents: Custom agent configurations. default_agent: Configuration for the default agent, including tool visibility controls. @@ -2314,13 +2321,9 @@ async def resume_session( _validate_tool_filter_list("excluded_tools", excluded_tools) system_message = _system_message_for_mode(mode, system_message) enable_session_telemetry = _enable_session_telemetry_default(mode, enable_session_telemetry) - skip_embedding_retrieval = _skip_embedding_retrieval_default( - mode, skip_embedding_retrieval - ) - enable_on_demand_instruction_discovery = ( - _enable_on_demand_instruction_discovery_default( - mode, enable_on_demand_instruction_discovery - ) + skip_embedding_retrieval = _skip_embedding_retrieval_default(mode, skip_embedding_retrieval) + enable_on_demand_instruction_discovery = _enable_on_demand_instruction_discovery_default( + mode, enable_on_demand_instruction_discovery ) enable_file_hooks = _enable_file_hooks_default(mode, enable_file_hooks) enable_host_git_operations = _enable_host_git_operations_default( @@ -2406,9 +2409,7 @@ async def resume_session( if organization_custom_instructions is not None: payload["organizationCustomInstructions"] = organization_custom_instructions if enable_on_demand_instruction_discovery is not None: - payload["enableOnDemandInstructionDiscovery"] = ( - enable_on_demand_instruction_discovery - ) + payload["enableOnDemandInstructionDiscovery"] = enable_on_demand_instruction_discovery if enable_file_hooks is not None: payload["enableFileHooks"] = enable_file_hooks if enable_host_git_operations is not None: @@ -2428,6 +2429,9 @@ async def resume_session( mcp_oauth_token_storage = _mcp_oauth_token_storage_default(mode, mcp_oauth_token_storage) if mcp_oauth_token_storage is not None: payload["mcpOAuthTokenStorage"] = mcp_oauth_token_storage + embedding_cache_storage = _embedding_cache_storage_default(mode, embedding_cache_storage) + if embedding_cache_storage is not None: + payload["embeddingCacheStorage"] = embedding_cache_storage payload["envValueMode"] = "direct" if custom_agents: diff --git a/rust/src/session.rs b/rust/src/session.rs index b38ce7682..7ce16125f 100644 --- a/rust/src/session.rs +++ b/rust/src/session.rs @@ -862,6 +862,9 @@ impl Client { if mode == crate::ClientMode::Empty && config.mcp_oauth_token_storage.is_none() { config.mcp_oauth_token_storage = Some("in-memory".into()); } + if mode == crate::ClientMode::Empty && config.embedding_cache_storage.is_none() { + config.embedding_cache_storage = Some("in-memory".into()); + } let opt_skip_custom_instructions = config.skip_custom_instructions; let opt_custom_agents_local_only = config.custom_agents_local_only; let opt_coauthor_enabled = config.coauthor_enabled; @@ -1112,6 +1115,9 @@ impl Client { if mode == crate::ClientMode::Empty && config.mcp_oauth_token_storage.is_none() { config.mcp_oauth_token_storage = Some("in-memory".into()); } + if mode == crate::ClientMode::Empty && config.embedding_cache_storage.is_none() { + config.embedding_cache_storage = Some("in-memory".into()); + } let opt_skip_custom_instructions = config.skip_custom_instructions; let opt_custom_agents_local_only = config.custom_agents_local_only; let opt_coauthor_enabled = config.coauthor_enabled; diff --git a/rust/src/types.rs b/rust/src/types.rs index 6ab5b00f1..eb3879138 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1389,7 +1389,8 @@ impl std::fmt::Debug for SessionConfig { .field("skip_embedding_retrieval", &self.skip_embedding_retrieval) .field( "organization_custom_instructions", - &self.organization_custom_instructions + &self + .organization_custom_instructions .as_ref() .map(|_| ""), ) @@ -1626,6 +1627,7 @@ impl SessionConfig { tool_filter_precedence: "excluded", mcp_servers: self.mcp_servers, mcp_oauth_token_storage: self.mcp_oauth_token_storage, + embedding_cache_storage: self.embedding_cache_storage, env_value_mode: "direct", enable_config_discovery: self.enable_config_discovery, skip_embedding_retrieval: self.skip_embedding_retrieval, @@ -2327,7 +2329,8 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("skip_embedding_retrieval", &self.skip_embedding_retrieval) .field( "organization_custom_instructions", - &self.organization_custom_instructions + &self + .organization_custom_instructions .as_ref() .map(|_| ""), ) @@ -2468,6 +2471,7 @@ impl ResumeSessionConfig { tool_filter_precedence: "excluded", mcp_servers: self.mcp_servers, mcp_oauth_token_storage: self.mcp_oauth_token_storage, + embedding_cache_storage: self.embedding_cache_storage, env_value_mode: "direct", enable_config_discovery: self.enable_config_discovery, skip_embedding_retrieval: self.skip_embedding_retrieval, diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 61c4b8d53..05f44aabf 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -79,6 +79,8 @@ pub(crate) struct SessionCreateWire { pub mcp_servers: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub mcp_oauth_token_storage: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub embedding_cache_storage: Option, pub env_value_mode: &'static str, #[serde(skip_serializing_if = "Option::is_none")] pub enable_config_discovery: Option, @@ -180,6 +182,8 @@ pub(crate) struct SessionResumeWire { pub mcp_servers: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub mcp_oauth_token_storage: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub embedding_cache_storage: Option, pub env_value_mode: &'static str, #[serde(skip_serializing_if = "Option::is_none")] pub enable_config_discovery: Option, From 54bda5b99a1d9df2bf57c2475b03144e52711234 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 28 May 2026 15:24:06 -0700 Subject: [PATCH 05/10] docs: remove runtime implementation details from property docs Simplify doc comments across all SDKs to focus on user-facing behavior rather than SDK-to-runtime interaction details. Removes boilerplate like 'When nil, the runtime default is used. When non-nil, the value is passed through to the runtime.' in favor of concise, behavior-focused docs that match the existing style of other properties. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Types.cs | 1 - go/types.go | 95 ++++++++----------- .../copilot/rpc/ResumeSessionConfig.java | 20 ++-- .../com/github/copilot/rpc/SessionConfig.java | 14 +-- nodejs/src/types.ts | 24 ++--- 5 files changed, 61 insertions(+), 93 deletions(-) diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index ed3ec570e..2e1a217b3 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -2526,7 +2526,6 @@ protected SessionConfigBase(SessionConfigBase? other) /// Controls how the embedding cache is stored for this session. /// : Embeddings are cached on disk and shared across sessions/restarts. /// : Embeddings are cached in memory only and discarded when the session ends. - /// When null, mode "empty" defaults to ; in other modes, the runtime default applies. /// public EmbeddingCacheStorageMode? EmbeddingCacheStorage { get; set; } diff --git a/go/types.go b/go/types.go index 396159b0b..30216adf0 100644 --- a/go/types.go +++ b/go/types.go @@ -904,47 +904,37 @@ type SessionConfig struct { // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are // always loaded from the working directory regardless of this setting. EnableConfigDiscovery bool - // SkipEmbeddingRetrieval controls embedding-based retrieval for this session. - // When nil, the runtime default is used. When non-nil, the value (true or false) - // is passed through to the runtime. Use in multitenant deployments to prevent - // cross-session information leakage through the shared embedding cache. + // 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. SkipEmbeddingRetrieval *bool // EmbeddingCacheStorage controls how the embedding cache is stored for this session. - // - "persistent": Embeddings are cached on disk and shared across sessions/restarts. - // - "in-memory": Embeddings are cached in memory only and discarded when the session ends. - // When nil, the runtime default ("persistent") is used. When non-nil, the value is - // passed through to the runtime. + // "persistent" caches on disk and shares across sessions/restarts. + // "in-memory" caches in memory only and discards when the session ends. EmbeddingCacheStorage *string // OrganizationCustomInstructions provides organization-level custom instructions - // to include in the system prompt. When nil, the runtime default is used. When - // non-nil, the value (including an empty string) is passed through to the runtime. - // Allows hosts to inject organization-specific guidance without relying on - // filesystem-based instruction discovery. + // to include in the system prompt. Allows hosts to inject organization-specific + // guidance without relying on filesystem-based instruction discovery. OrganizationCustomInstructions *string - // EnableOnDemandInstructionDiscovery controls on-demand discovery of instruction - // files (AGENTS.md, .github/copilot-instructions.md, etc.) after successful file - // views. When nil, the runtime default is used. When non-nil, the value (true or - // false) is passed through to the runtime. + // EnableOnDemandInstructionDiscovery, when non-nil, controls on-demand discovery + // of instruction files (AGENTS.md, .github/copilot-instructions.md, etc.) after + // successful file views. EnableOnDemandInstructionDiscovery *bool - // EnableFileHooks controls loading of file-based hooks from .github/hooks/. - // When nil, the runtime default is used. When non-nil, the value (true or false) - // is passed through to the runtime. This is separate from the Hooks callback - // parameter which gates SDK hook event registration. + // EnableFileHooks, when non-nil, controls loading of file-based hooks from + // .github/hooks/. This is separate from the Hooks callback parameter which + // gates SDK hook event registration. EnableFileHooks *bool - // EnableHostGitOperations controls git operations on the host filesystem (branch - // detection, file status, commit history). When nil, the runtime default is used. - // When non-nil, the value (true or false) is passed through to the runtime. When - // false, no git context is surfaced in the system prompt. + // EnableHostGitOperations, when non-nil, controls git operations on the host + // filesystem (branch detection, file status, commit history). When false, no + // git context is surfaced in the system prompt. EnableHostGitOperations *bool - // EnableSessionStore controls the cross-session store for search and retrieval - // across sessions. When nil, the runtime default is used. When non-nil, the value - // (true or false) is passed through to the runtime. When false, session content is - // not written to or read from the shared session store. + // EnableSessionStore, when non-nil, controls the cross-session store for search + // and retrieval. When false, session content is not written to or read from the + // shared session store. EnableSessionStore *bool - // EnableSkills controls skill loading (including builtin skills and discovered - // skill directories). When nil, the runtime default is used. When non-nil, the - // value (true or false) is passed through to the runtime. When false, no skills - // are loaded regardless of SkillDirectories or EnableConfigDiscovery settings. + // EnableSkills, when non-nil, controls skill loading (including builtin skills + // and discovered skill directories). When false, no skills are loaded regardless + // of SkillDirectories or EnableConfigDiscovery settings. EnableSkills *bool // Tools exposes caller-implemented tools to the CLI. A Tool with a nil Handler // is declaration-only; the consumer must resolve its calls via pending tool RPCs. @@ -1318,40 +1308,31 @@ type ResumeSessionConfig struct { // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are // always loaded from the working directory regardless of this setting. EnableConfigDiscovery bool - // SkipEmbeddingRetrieval controls embedding-based retrieval for this session. - // When nil, the runtime default is used. When non-nil, the value (true or false) - // is passed through to the runtime. Use in multitenant deployments to prevent - // cross-session information leakage through the shared embedding cache. + // 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. SkipEmbeddingRetrieval *bool // EmbeddingCacheStorage controls how the embedding cache is stored for this session. - // - "persistent": Embeddings are cached on disk and shared across sessions/restarts. - // - "in-memory": Embeddings are cached in memory only and discarded when the session ends. - // When nil, the runtime default ("persistent") is used. When non-nil, the value is - // passed through to the runtime. + // "persistent" caches on disk and shares across sessions/restarts. + // "in-memory" caches in memory only and discards when the session ends. EmbeddingCacheStorage *string // OrganizationCustomInstructions provides organization-level custom instructions - // to include in the system prompt. When nil, the runtime default is used. When - // non-nil, the value (including an empty string) is passed through to the runtime. + // to include in the system prompt. OrganizationCustomInstructions *string - // EnableOnDemandInstructionDiscovery controls on-demand discovery of instruction - // files after successful file views. When nil, the runtime default is used. When - // non-nil, the value (true or false) is passed through to the runtime. + // EnableOnDemandInstructionDiscovery, when non-nil, controls on-demand discovery + // of instruction files after successful file views. EnableOnDemandInstructionDiscovery *bool - // EnableFileHooks controls loading of file-based hooks from .github/hooks/. - // When nil, the runtime default is used. When non-nil, the value (true or false) - // is passed through to the runtime. This is separate from the Hooks callback - // parameter which gates SDK hook event registration. + // EnableFileHooks, when non-nil, controls loading of file-based hooks from + // .github/hooks/. This is separate from the Hooks callback parameter which + // gates SDK hook event registration. EnableFileHooks *bool - // EnableHostGitOperations controls git operations on the host filesystem. When - // nil, the runtime default is used. When non-nil, the value (true or false) is - // passed through to the runtime. + // EnableHostGitOperations, when non-nil, controls git operations on the host + // filesystem. EnableHostGitOperations *bool - // EnableSessionStore controls the cross-session store for search and retrieval - // across sessions. When nil, the runtime default is used. When non-nil, the value - // (true or false) is passed through to the runtime. + // EnableSessionStore, when non-nil, controls the cross-session store for search + // and retrieval across sessions. EnableSessionStore *bool - // EnableSkills controls skill loading. When nil, the runtime default is used. - // When non-nil, the value (true or false) is passed through to the runtime. + // EnableSkills, when non-nil, controls skill loading. EnableSkills *bool // Streaming enables streaming of assistant message and reasoning chunks. // When non-nil and true, assistant.message_delta and assistant.reasoning_delta diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 0a26fd6e7..aca028df0 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -618,7 +618,7 @@ public ResumeSessionConfig setConfigDirectory(String configDirectory) { * Gets whether automatic configuration discovery is enabled. * * @return {@code true} to enable discovery, {@code false} to disable, or - * {@code null} to use the runtime default + * {@code null} to use the default behavior */ @JsonIgnore public Optional getEnableConfigDiscovery() { @@ -635,7 +635,7 @@ public Optional getEnableConfigDiscovery() { * * @param enableConfigDiscovery * {@code true} to enable discovery, {@code false} to disable, or - * {@code null} to use the runtime default + * {@code null} to use the default behavior * @return this config for method chaining */ public ResumeSessionConfig setEnableConfigDiscovery(boolean enableConfigDiscovery) { @@ -658,7 +658,7 @@ public ResumeSessionConfig clearEnableConfigDiscovery() { * * @return an {@link java.util.Optional} containing {@code true} to skip * embedding retrieval or {@code false} to force it, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getSkipEmbeddingRetrieval() { @@ -715,7 +715,7 @@ public ResumeSessionConfig setOrganizationCustomInstructions(String organization * * @return an {@link java.util.Optional} containing {@code true} to enable * on-demand discovery or {@code false} to disable it, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableOnDemandInstructionDiscovery() { @@ -751,7 +751,7 @@ public ResumeSessionConfig clearEnableOnDemandInstructionDiscovery() { * * @return an {@link java.util.Optional} containing {@code true} to enable file * hooks or {@code false} to disable them, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableFileHooks() { @@ -785,7 +785,7 @@ public ResumeSessionConfig clearEnableFileHooks() { * * @return an {@link java.util.Optional} containing {@code true} to enable host * git operations or {@code false} to disable them, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableHostGitOperations() { @@ -821,7 +821,7 @@ public ResumeSessionConfig clearEnableHostGitOperations() { * * @return an {@link java.util.Optional} containing {@code true} to enable the * session store or {@code false} to disable it, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableSessionStore() { @@ -856,7 +856,7 @@ public ResumeSessionConfig clearEnableSessionStore() { * * @return an {@link java.util.Optional} containing {@code true} to enable skill * loading or {@code false} to disable it, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableSkills() { @@ -889,7 +889,7 @@ public ResumeSessionConfig clearEnableSkills() { * Gets the embedding cache storage mode. * * @return the embedding cache storage mode ({@code "persistent"} or - * {@code "in-memory"}), or {@code null} to use the runtime default + * {@code "in-memory"}), or {@code null} to use the default behavior */ public String getEmbeddingCacheStorage() { return embeddingCacheStorage; @@ -922,7 +922,7 @@ public ResumeSessionConfig clearEmbeddingCacheStorage() { * Gets whether sub-agent streaming events are included. * * @return {@code true} to include sub-agent streaming events, {@code false} to - * suppress them, or {@code null} to use the runtime default + * suppress them, or {@code null} to use the default behavior */ @JsonIgnore public Optional getIncludeSubAgentStreamingEvents() { diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 748f938df..20234ef6f 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -999,7 +999,7 @@ public SessionConfig clearEnableConfigDiscovery() { * * @return an {@link java.util.Optional} containing {@code true} to skip * embedding retrieval or {@code false} to force it, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getSkipEmbeddingRetrieval() { @@ -1056,7 +1056,7 @@ public SessionConfig setOrganizationCustomInstructions(String organizationCustom * * @return an {@link java.util.Optional} containing {@code true} to enable * on-demand discovery or {@code false} to disable it, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableOnDemandInstructionDiscovery() { @@ -1092,7 +1092,7 @@ public SessionConfig clearEnableOnDemandInstructionDiscovery() { * * @return an {@link java.util.Optional} containing {@code true} to enable file * hooks or {@code false} to disable them, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableFileHooks() { @@ -1126,7 +1126,7 @@ public SessionConfig clearEnableFileHooks() { * * @return an {@link java.util.Optional} containing {@code true} to enable host * git operations or {@code false} to disable them, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableHostGitOperations() { @@ -1162,7 +1162,7 @@ public SessionConfig clearEnableHostGitOperations() { * * @return an {@link java.util.Optional} containing {@code true} to enable the * session store or {@code false} to disable it, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableSessionStore() { @@ -1197,7 +1197,7 @@ public SessionConfig clearEnableSessionStore() { * * @return an {@link java.util.Optional} containing {@code true} to enable skill * loading or {@code false} to disable it, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableSkills() { @@ -1230,7 +1230,7 @@ public SessionConfig clearEnableSkills() { * Gets the embedding cache storage mode. * * @return the embedding cache storage mode ({@code "persistent"} or - * {@code "in-memory"}), or {@code null} to use the runtime default + * {@code "in-memory"}), or {@code null} to use the default behavior */ public String getEmbeddingCacheStorage() { return embeddingCacheStorage; diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 356952f57..b6ce9b774 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1917,8 +1917,7 @@ export interface SessionConfigBase { /** * When true, skips embedding-based retrieval for this session. * Use in multitenant deployments to prevent cross-session information leakage - * through the shared embedding cache. When omitted, `mode: "empty"` defaults - * this to `true`; in other modes, the runtime default applies. + * through the shared embedding cache. */ skipEmbeddingRetrieval?: boolean; @@ -1926,58 +1925,47 @@ export interface SessionConfigBase { * Controls how the embedding cache is stored for this session. * - `"persistent"`: Embeddings are cached on disk and shared across sessions/restarts. * - `"in-memory"`: Embeddings are cached in memory only and discarded when the session ends. - * Use this for multitenant/SDK deployments where sessions must not share cached embeddings. - * When omitted, `mode: "empty"` defaults this to `"in-memory"`; in other modes, - * the runtime default (`"persistent"`) applies. */ embeddingCacheStorage?: "persistent" | "in-memory"; /** * Organization-level custom instructions to include in the system prompt. * Allows hosts to inject organization-specific guidance without relying on - * filesystem-based instruction discovery. When omitted, no organization-level - * instructions are injected by the SDK. + * filesystem-based instruction discovery. */ organizationCustomInstructions?: string; /** * When true, enables on-demand discovery of instruction files (AGENTS.md, * .github/copilot-instructions.md, etc.) after successful file views. - * When omitted, `mode: "empty"` defaults this to `false`; in other modes, - * the runtime default applies. */ enableOnDemandInstructionDiscovery?: boolean; /** * When true, enables loading of file-based hooks from `.github/hooks/`. * This is separate from the `hooks` callback parameter which gates SDK - * hook event registration. When omitted, `mode: "empty"` defaults this to - * `false`; in other modes, the runtime default applies. + * hook event registration. */ enableFileHooks?: boolean; /** * When true, enables git operations on the host filesystem (branch detection, * file status, commit history). When false, no git context is surfaced in - * the system prompt. When omitted, `mode: "empty"` defaults this to `false`; - * in other modes, the runtime default applies. + * the system prompt. */ enableHostGitOperations?: boolean; /** * When true, enables the cross-session store for search and retrieval * across sessions. When false, session content is not written to or - * read from the shared session store. When omitted, `mode: "empty"` - * defaults this to `false`; in other modes, the runtime default applies. + * read from the shared session store. */ enableSessionStore?: boolean; /** * When true, enables skill loading (including builtin skills and discovered * skill directories). When false, no skills are loaded regardless of - * `skillDirectories` or `enableConfigDiscovery` settings. When omitted, - * `mode: "empty"` defaults this to `false`; in other modes, the runtime - * default applies. + * `skillDirectories` or `enableConfigDiscovery` settings. */ enableSkills?: boolean; From 7a12e347f865fe327b79b9e3c74359c6977ad926 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 28 May 2026 15:25:25 -0700 Subject: [PATCH 06/10] test: rename Java test methods to remove temporal 'new' wording Rename to use 'granularMultitenancyFields' instead of 'newSessionFields' for consistency with .NET and Node.js test naming. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/src/test/java/com/github/copilot/ConfigCloneTest.java | 4 ++-- .../java/com/github/copilot/OptionalApiAndJacksonTest.java | 4 ++-- .../java/com/github/copilot/SessionRequestBuilderTest.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/java/src/test/java/com/github/copilot/ConfigCloneTest.java b/java/src/test/java/com/github/copilot/ConfigCloneTest.java index bda6214ef..2efafdfc5 100644 --- a/java/src/test/java/com/github/copilot/ConfigCloneTest.java +++ b/java/src/test/java/com/github/copilot/ConfigCloneTest.java @@ -231,7 +231,7 @@ void sessionConfigEnableSessionTelemetryDefaultIsNull() { } @Test - void sessionConfigNewSessionFieldsCopied() { + void sessionConfigGranularMultitenancyFieldsCopied() { SessionConfig original = new SessionConfig().setSkipEmbeddingRetrieval(true) .setOrganizationCustomInstructions("Org instructions").setEnableOnDemandInstructionDiscovery(false) .setEmbeddingCacheStorage("persistent").setEnableFileHooks(true).setEnableHostGitOperations(false) @@ -269,7 +269,7 @@ void resumeSessionConfigEnableSessionTelemetryDefaultIsNull() { } @Test - void resumeSessionConfigNewSessionFieldsCopied() { + void resumeSessionConfigGranularMultitenancyFieldsCopied() { ResumeSessionConfig original = new ResumeSessionConfig().setSkipEmbeddingRetrieval(false) .setOrganizationCustomInstructions("Resume org instructions") .setEnableOnDemandInstructionDiscovery(true).setEmbeddingCacheStorage("persistent") diff --git a/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java b/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java index f64345780..2b6a79563 100644 --- a/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java +++ b/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java @@ -354,7 +354,7 @@ void sessionConfig_includeSubAgentStreamingEventsValue() { } @Test - void sessionConfig_newSessionFieldsValue() { + void sessionConfig_granularMultitenancyFieldsValue() { var cfg = new SessionConfig(); assertTrue(cfg.getSkipEmbeddingRetrieval().isEmpty()); assertNull(cfg.getOrganizationCustomInstructions()); @@ -415,7 +415,7 @@ void resumeSessionConfig_includeSubAgentStreamingEventsValue() { } @Test - void resumeSessionConfig_newSessionFieldsValue() { + void resumeSessionConfig_granularMultitenancyFieldsValue() { var cfg = new ResumeSessionConfig(); assertTrue(cfg.getSkipEmbeddingRetrieval().isEmpty()); assertNull(cfg.getOrganizationCustomInstructions()); diff --git a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java index 606a4d6d8..c97114b27 100644 --- a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -251,7 +251,7 @@ void testBuildResumeRequestSetsClientName() { } @Test - void testBuildCreateRequestPropagatesNewSessionFields() { + void testBuildCreateRequestPropagatesGranularMultitenancyFields() { var config = new SessionConfig().setSkipEmbeddingRetrieval(true) .setOrganizationCustomInstructions("Create org instructions") .setEnableOnDemandInstructionDiscovery(false).setEnableFileHooks(true).setEnableHostGitOperations(false) @@ -269,7 +269,7 @@ void testBuildCreateRequestPropagatesNewSessionFields() { } @Test - void testBuildResumeRequestPropagatesNewSessionFields() { + void testBuildResumeRequestPropagatesGranularMultitenancyFields() { var config = new ResumeSessionConfig().setSkipEmbeddingRetrieval(false) .setOrganizationCustomInstructions("Resume org instructions") .setEnableOnDemandInstructionDiscovery(true).setEnableFileHooks(false).setEnableHostGitOperations(true) From a630379e936c97ec269f35e0e5d965ddebf4e8ff Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 28 May 2026 15:36:38 -0700 Subject: [PATCH 07/10] fix: resolve Python SDK test failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/_mode.py | 4 ++-- python/test_tool_set.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/python/copilot/_mode.py b/python/copilot/_mode.py index 39d3128cd..9323423f6 100644 --- a/python/copilot/_mode.py +++ b/python/copilot/_mode.py @@ -200,8 +200,8 @@ def _skip_embedding_retrieval_default( def _embedding_cache_storage_default( mode: CopilotClientMode | None, - supplied: str | None, -) -> str | None: + supplied: Literal["persistent", "in-memory"] | None, +) -> Literal["persistent", "in-memory"] | None: """Empty mode defaults embedding cache storage to in-memory; caller value wins.""" if mode == "empty" and supplied is None: return "in-memory" diff --git a/python/test_tool_set.py b/python/test_tool_set.py index bf63d2b15..6a65e0df2 100644 --- a/python/test_tool_set.py +++ b/python/test_tool_set.py @@ -6,6 +6,7 @@ from copilot import BUILTIN_TOOLS_ISOLATED, CopilotClient, ToolSet, UriRuntimeConnection from copilot._mode import ( + _embedding_cache_storage_default, _enable_file_hooks_default, _enable_host_git_operations_default, _enable_on_demand_instruction_discovery_default, @@ -173,6 +174,19 @@ def test_empty_mode_append_promoted_to_customize(self): assert out["sections"]["environment_context"] == {"action": "remove"} +class TestEmptyModeEmbeddingCacheStorageDefaults: + def test_empty_mode_defaults_to_in_memory(self): + assert _embedding_cache_storage_default("empty", None) == "in-memory" + + def test_caller_wins(self): + assert _embedding_cache_storage_default("empty", "persistent") == "persistent" + assert _embedding_cache_storage_default("empty", "in-memory") == "in-memory" + + def test_copilot_cli_does_not_change(self): + assert _embedding_cache_storage_default("copilot-cli", None) is None + assert _embedding_cache_storage_default("copilot-cli", "persistent") == "persistent" + + class TestEmptyModeBooleanDefaults: @pytest.mark.parametrize( ("helper", "empty_default"), From 5860d513670341fde1a24cff8ffe618f3810ff51 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 28 May 2026 15:39:26 -0700 Subject: [PATCH 08/10] fix: add missing Java empty-mode defaults for granular flags The Cross-SDK Consistency Review identified that Java was only applying embeddingCacheStorage and mcpOAuthTokenStorage in empty mode, but missing the 6 boolean flags (skipEmbeddingRetrieval, enableOnDemandInstructionDiscovery, enableFileHooks, enableHostGitOperations, enableSessionStore, enableSkills). Also adds embeddingCacheStorage assertion to Node.js empty-mode test for coverage parity with Go and .NET. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../com/github/copilot/CopilotClient.java | 36 +++++++++++++++++++ nodejs/test/toolSet.test.ts | 1 + 2 files changed, 37 insertions(+) diff --git a/java/src/main/java/com/github/copilot/CopilotClient.java b/java/src/main/java/com/github/copilot/CopilotClient.java index bdd89c094..d8c8f9cdb 100644 --- a/java/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/src/main/java/com/github/copilot/CopilotClient.java @@ -512,9 +512,27 @@ public CompletableFuture createSession(SessionConfig config) { + "the tools it wants — e.g. setAvailableTools(new ToolSet().addBuiltIn(BuiltInTools.ISOLATED))."); } request.setToolFilterPrecedence("excluded"); + if (request.getSkipEmbeddingRetrieval() == null) { + request.setSkipEmbeddingRetrieval(true); + } if (request.getEmbeddingCacheStorage() == null) { request.setEmbeddingCacheStorage("in-memory"); } + if (request.getEnableOnDemandInstructionDiscovery() == null) { + request.setEnableOnDemandInstructionDiscovery(false); + } + if (request.getEnableFileHooks() == null) { + request.setEnableFileHooks(false); + } + if (request.getEnableHostGitOperations() == null) { + request.setEnableHostGitOperations(false); + } + if (request.getEnableSessionStore() == null) { + request.setEnableSessionStore(false); + } + if (request.getEnableSkills() == null) { + request.setEnableSkills(false); + } if (request.getMcpOAuthTokenStorage() == null) { request.setMcpOAuthTokenStorage("in-memory"); } @@ -632,9 +650,27 @@ public CompletableFuture resumeSession(String sessionId, ResumeS + "the tools it wants — e.g. setAvailableTools(new ToolSet().addBuiltIn(BuiltInTools.ISOLATED))."); } request.setToolFilterPrecedence("excluded"); + if (request.getSkipEmbeddingRetrieval() == null) { + request.setSkipEmbeddingRetrieval(true); + } if (request.getEmbeddingCacheStorage() == null) { request.setEmbeddingCacheStorage("in-memory"); } + if (request.getEnableOnDemandInstructionDiscovery() == null) { + request.setEnableOnDemandInstructionDiscovery(false); + } + if (request.getEnableFileHooks() == null) { + request.setEnableFileHooks(false); + } + if (request.getEnableHostGitOperations() == null) { + request.setEnableHostGitOperations(false); + } + if (request.getEnableSessionStore() == null) { + request.setEnableSessionStore(false); + } + if (request.getEnableSkills() == null) { + request.setEnableSkills(false); + } if (request.getMcpOAuthTokenStorage() == null) { request.setMcpOAuthTokenStorage("in-memory"); } diff --git a/nodejs/test/toolSet.test.ts b/nodejs/test/toolSet.test.ts index 88a31d65f..b77b79707 100644 --- a/nodejs/test/toolSet.test.ts +++ b/nodejs/test/toolSet.test.ts @@ -469,6 +469,7 @@ describe("Empty-mode safe defaults", () => { }); const payload = createPayload(spy); expect(payload.skipEmbeddingRetrieval).toBe(true); + expect(payload.embeddingCacheStorage).toBe("in-memory"); expect(payload.enableOnDemandInstructionDiscovery).toBe(false); expect(payload.enableFileHooks).toBe(false); expect(payload.enableHostGitOperations).toBe(false); From 9c5e8bb06f7016ffc4509580f1189d4b9cb6ccba Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 28 May 2026 15:55:43 -0700 Subject: [PATCH 09/10] fix(rust): add embedding_cache_storage to Debug impls and builder API Add the missing embedding_cache_storage field to: - SessionConfig custom Debug impl - ResumeSessionConfig custom Debug impl - SessionConfig fluent builder (with_embedding_cache_storage) - ResumeSessionConfig fluent builder (with_embedding_cache_storage) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- rust/src/types.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/rust/src/types.rs b/rust/src/types.rs index eb3879138..f902f8301 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1385,6 +1385,7 @@ impl std::fmt::Debug for SessionConfig { .field("excluded_tools", &self.excluded_tools) .field("mcp_servers", &self.mcp_servers) .field("mcp_oauth_token_storage", &self.mcp_oauth_token_storage) + .field("embedding_cache_storage", &self.embedding_cache_storage) .field("enable_config_discovery", &self.enable_config_discovery) .field("skip_embedding_retrieval", &self.skip_embedding_retrieval) .field( @@ -1903,6 +1904,15 @@ impl SessionConfig { self } + /// Set embedding cache storage mode. + pub fn with_embedding_cache_storage( + mut self, + embedding_cache_storage: impl Into, + ) -> Self { + self.embedding_cache_storage = Some(embedding_cache_storage.into()); + self + } + /// Enable or disable CLI config discovery (MCP config files, skills, plugins). pub fn with_enable_config_discovery(mut self, enable: bool) -> Self { self.enable_config_discovery = Some(enable); @@ -2325,6 +2335,7 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("excluded_tools", &self.excluded_tools) .field("mcp_servers", &self.mcp_servers) .field("mcp_oauth_token_storage", &self.mcp_oauth_token_storage) + .field("embedding_cache_storage", &self.embedding_cache_storage) .field("enable_config_discovery", &self.enable_config_discovery) .field("skip_embedding_retrieval", &self.skip_embedding_retrieval) .field( @@ -2795,6 +2806,15 @@ impl ResumeSessionConfig { self } + /// Set embedding cache storage mode on resume. + pub fn with_embedding_cache_storage( + mut self, + embedding_cache_storage: impl Into, + ) -> Self { + self.embedding_cache_storage = Some(embedding_cache_storage.into()); + self + } + /// Enable or disable CLI config discovery on resume. pub fn with_enable_config_discovery(mut self, enable: bool) -> Self { self.enable_config_discovery = Some(enable); From 2d82c40e8102a54783fd885ca2df9e02cf73f750 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 28 May 2026 16:13:47 -0700 Subject: [PATCH 10/10] fix: .NET empty-mode tests and Java enableConfigDiscovery docs - Add BaseDirectory and AvailableTools to .NET empty-mode default tests so they don't throw at client construction time - Fix enableConfigDiscovery Javadoc in ResumeSessionConfig: use Optional.empty() instead of null in @return, remove null from @param (setter takes primitive boolean) - Fix stale 'runtime default' wording in SessionConfig.java getter Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/test/E2E/ClientOptionsE2ETests.cs | 4 ++++ .../java/com/github/copilot/rpc/ResumeSessionConfig.java | 8 ++++---- .../main/java/com/github/copilot/rpc/SessionConfig.java | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dotnet/test/E2E/ClientOptionsE2ETests.cs b/dotnet/test/E2E/ClientOptionsE2ETests.cs index fbce617ac..6360cb55a 100644 --- a/dotnet/test/E2E/ClientOptionsE2ETests.cs +++ b/dotnet/test/E2E/ClientOptionsE2ETests.cs @@ -226,6 +226,7 @@ public async Task Should_Apply_Empty_Mode_Defaults_To_CreateSession_Wire_Request { Connection = RuntimeConnection.ForStdio(path: cliPath, args: ["--capture-file", capturePath]), Mode = CopilotClientMode.Empty, + BaseDirectory = Ctx.WorkDir, UseLoggedInUser = false, }); @@ -234,6 +235,7 @@ public async Task Should_Apply_Empty_Mode_Defaults_To_CreateSession_Wire_Request var session = await client.CreateSessionAsync(new SessionConfig { OnPermissionRequest = PermissionHandler.ApproveAll, + AvailableTools = new ToolSet().AddBuiltIn(BuiltInTools.Isolated), }); using var capture = JsonDocument.Parse(await File.ReadAllTextAsync(capturePath)); @@ -416,6 +418,7 @@ public async Task Should_Apply_Empty_Mode_Defaults_To_ResumeSession_Wire_Request { Connection = RuntimeConnection.ForStdio(path: cliPath, args: ["--capture-file", capturePath]), Mode = CopilotClientMode.Empty, + BaseDirectory = Ctx.WorkDir, UseLoggedInUser = false, }); @@ -424,6 +427,7 @@ public async Task Should_Apply_Empty_Mode_Defaults_To_ResumeSession_Wire_Request var session = await client.ResumeSessionAsync("resume-empty-session", new ResumeSessionConfig { OnPermissionRequest = PermissionHandler.ApproveAll, + AvailableTools = new ToolSet().AddBuiltIn(BuiltInTools.Isolated), }); using var capture = JsonDocument.Parse(await File.ReadAllTextAsync(capturePath)); diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index aca028df0..3510cc7fd 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -617,8 +617,9 @@ public ResumeSessionConfig setConfigDirectory(String configDirectory) { /** * Gets whether automatic configuration discovery is enabled. * - * @return {@code true} to enable discovery, {@code false} to disable, or - * {@code null} to use the default behavior + * @return an {@link java.util.Optional} containing {@code true} to enable + * discovery or {@code false} to disable it, or + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableConfigDiscovery() { @@ -634,8 +635,7 @@ public Optional getEnableConfigDiscovery() { * explicitly provided configurations. * * @param enableConfigDiscovery - * {@code true} to enable discovery, {@code false} to disable, or - * {@code null} to use the default behavior + * {@code true} to enable discovery, {@code false} to disable * @return this config for method chaining */ public ResumeSessionConfig setEnableConfigDiscovery(boolean enableConfigDiscovery) { diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 20234ef6f..0d83fe799 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -958,7 +958,7 @@ public SessionConfig setConfigDirectory(String configDirectory) { * * @return an {@link java.util.Optional} containing {@code true} to enable * discovery or {@code false} to disable, or - * {@link java.util.Optional#empty()} to use the runtime default + * {@link java.util.Optional#empty()} to use the default behavior */ @JsonIgnore public Optional getEnableConfigDiscovery() {