From 2b3e342448f7ddc24ba4d979790ec9bf8b3cdc8a Mon Sep 17 00:00:00 2001 From: "David Sterling (VS)" Date: Wed, 6 May 2026 10:38:06 -0700 Subject: [PATCH 1/5] Add enableSessionTelemetry option to all SDKs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an optional enableSessionTelemetry field to SessionConfig and ResumeSessionConfig across Node.js, Python, Go, and .NET SDKs. When omitted (default), session telemetry remains enabled — no extra parameter is needed. When set to false, internal session telemetry (Hydro/AppInsights) is disabled for that session. Includes unit tests for serialization, cloning, and wire forwarding in all four languages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Client.cs | 4 ++ dotnet/src/Types.cs | 12 ++++++ dotnet/test/Unit/CloneTests.cs | 35 +++++++++++++++ dotnet/test/Unit/SerializationTests.cs | 32 ++++++++++++++ go/client.go | 2 + go/client_test.go | 59 ++++++++++++++++++++++++++ go/types.go | 6 +++ nodejs/src/client.ts | 2 + nodejs/src/types.ts | 10 +++++ nodejs/test/client.test.ts | 41 ++++++++++++++++++ python/copilot/client.py | 11 +++++ python/copilot/session.py | 4 ++ python/test_client.py | 51 ++++++++++++++++++++++ 13 files changed, 269 insertions(+) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 3ba14bebe..cb724b130 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -568,6 +568,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.AvailableTools, config.ExcludedTools, config.Provider, + config.EnableSessionTelemetry, (bool?)true, config.OnUserInputRequest != null ? true : null, hasHooks ? true : null, @@ -694,6 +695,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config.AvailableTools, config.ExcludedTools, config.Provider, + config.EnableSessionTelemetry, (bool?)true, config.OnUserInputRequest != null ? true : null, hasHooks ? true : null, @@ -1781,6 +1783,7 @@ internal record CreateSessionRequest( IList? AvailableTools, IList? ExcludedTools, ProviderConfig? Provider, + bool? EnableSessionTelemetry, bool? RequestPermission, bool? RequestUserInput, bool? Hooks, @@ -1837,6 +1840,7 @@ internal record ResumeSessionRequest( IList? AvailableTools, IList? ExcludedTools, ProviderConfig? Provider, + bool? EnableSessionTelemetry, bool? RequestPermission, bool? RequestUserInput, bool? Hooks, diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 0a09d32b7..ff5923a97 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -1849,6 +1849,7 @@ protected SessionConfig(SessionConfig? other) OnPermissionRequest = other.OnPermissionRequest; OnUserInputRequest = other.OnUserInputRequest; Provider = other.Provider; + EnableSessionTelemetry = other.EnableSessionTelemetry; ReasoningEffort = other.ReasoningEffort; CreateSessionFsHandler = other.CreateSessionFsHandler; GitHubToken = other.GitHubToken; @@ -1930,6 +1931,11 @@ protected SessionConfig(SessionConfig? other) /// public ProviderConfig? Provider { get; set; } + /// + /// When false, disables internal session telemetry for this session. + /// + public bool? EnableSessionTelemetry { get; set; } + /// /// Handler for permission requests from the server. /// When provided, the server will call this handler to request permission for operations. @@ -2114,6 +2120,7 @@ protected ResumeSessionConfig(ResumeSessionConfig? other) OnPermissionRequest = other.OnPermissionRequest; OnUserInputRequest = other.OnUserInputRequest; Provider = other.Provider; + EnableSessionTelemetry = other.EnableSessionTelemetry; ReasoningEffort = other.ReasoningEffort; CreateSessionFsHandler = other.CreateSessionFsHandler; GitHubToken = other.GitHubToken; @@ -2164,6 +2171,11 @@ protected ResumeSessionConfig(ResumeSessionConfig? other) /// public ProviderConfig? Provider { get; set; } + /// + /// When false, disables internal session telemetry for this session. + /// + public bool? EnableSessionTelemetry { get; set; } + /// /// Reasoning effort level for models that support it. /// Valid values: "low", "medium", "high", "xhigh". diff --git a/dotnet/test/Unit/CloneTests.cs b/dotnet/test/Unit/CloneTests.cs index 10e0bbf45..055d53302 100644 --- a/dotnet/test/Unit/CloneTests.cs +++ b/dotnet/test/Unit/CloneTests.cs @@ -90,6 +90,7 @@ public void SessionConfig_Clone_CopiesAllProperties() ExcludedTools = ["tool3"], WorkingDirectory = "/workspace", Streaming = true, + EnableSessionTelemetry = false, IncludeSubAgentStreamingEvents = false, McpServers = new Dictionary { ["server1"] = new McpStdioServerConfig { Command = "echo" } }, CustomAgents = [new CustomAgentConfig { Name = "agent1" }], @@ -111,6 +112,7 @@ public void SessionConfig_Clone_CopiesAllProperties() Assert.Equal(original.ExcludedTools, clone.ExcludedTools); Assert.Equal(original.WorkingDirectory, clone.WorkingDirectory); Assert.Equal(original.Streaming, clone.Streaming); + Assert.Equal(original.EnableSessionTelemetry, clone.EnableSessionTelemetry); Assert.Equal(original.IncludeSubAgentStreamingEvents, clone.IncludeSubAgentStreamingEvents); Assert.Equal(original.McpServers.Count, clone.McpServers!.Count); Assert.Equal(original.CustomAgents.Count, clone.CustomAgents!.Count); @@ -315,6 +317,19 @@ public void ResumeSessionConfig_Clone_PreservesIncludeSubAgentStreamingEventsDef Assert.True(clone.IncludeSubAgentStreamingEvents); } + [Fact] + public void ResumeSessionConfig_Clone_CopiesEnableSessionTelemetry() + { + var original = new ResumeSessionConfig + { + EnableSessionTelemetry = false, + }; + + var clone = original.Clone(); + + Assert.False(clone.EnableSessionTelemetry); + } + [Fact] public void ResumeSessionConfig_Clone_CopiesContinuePendingWork() { @@ -337,4 +352,24 @@ public void ResumeSessionConfig_Clone_PreservesContinuePendingWorkDefault() Assert.Null(clone.ContinuePendingWork); } + + [Fact] + public void SessionConfig_Clone_PreservesEnableSessionTelemetryDefault() + { + var original = new SessionConfig(); + + var clone = original.Clone(); + + Assert.Null(clone.EnableSessionTelemetry); + } + + [Fact] + public void ResumeSessionConfig_Clone_PreservesEnableSessionTelemetryDefault() + { + var original = new ResumeSessionConfig(); + + var clone = original.Clone(); + + Assert.Null(clone.EnableSessionTelemetry); + } } diff --git a/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs index e58b256f4..713c46abd 100644 --- a/dotnet/test/Unit/SerializationTests.cs +++ b/dotnet/test/Unit/SerializationTests.cs @@ -126,6 +126,38 @@ public void ResumeSessionRequest_CanSerializeInstructionDirectories_WithSdkOptio Assert.Equal("C:\\resume-instructions", root.GetProperty("instructionDirectories")[0].GetString()); } + [Fact] + public void CreateSessionRequest_CanSerializeEnableSessionTelemetry_WithSdkOptions() + { + var options = GetSerializerOptions(); + var requestType = GetNestedType(typeof(CopilotClient), "CreateSessionRequest"); + var request = CreateInternalRequest( + requestType, + ("SessionId", "session-id"), + ("EnableSessionTelemetry", false)); + + var json = JsonSerializer.Serialize(request, requestType, options); + using var document = JsonDocument.Parse(json); + var root = document.RootElement; + Assert.False(root.GetProperty("enableSessionTelemetry").GetBoolean()); + } + + [Fact] + public void ResumeSessionRequest_CanSerializeEnableSessionTelemetry_WithSdkOptions() + { + var options = GetSerializerOptions(); + var requestType = GetNestedType(typeof(CopilotClient), "ResumeSessionRequest"); + var request = CreateInternalRequest( + requestType, + ("SessionId", "session-id"), + ("EnableSessionTelemetry", false)); + + var json = JsonSerializer.Serialize(request, requestType, options); + using var document = JsonDocument.Parse(json); + var root = document.RootElement; + Assert.False(root.GetProperty("enableSessionTelemetry").GetBoolean()); + } + [Fact] public void McpHttpServerConfig_CanSerializeOauthOptions_WithSdkOptions() { diff --git a/go/client.go b/go/client.go index 851dcf4e2..92a00c083 100644 --- a/go/client.go +++ b/go/client.go @@ -630,6 +630,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.AvailableTools = config.AvailableTools req.ExcludedTools = config.ExcludedTools req.Provider = config.Provider + req.EnableSessionTelemetry = config.EnableSessionTelemetry req.ModelCapabilities = config.ModelCapabilities req.WorkingDirectory = config.WorkingDirectory req.MCPServers = config.MCPServers @@ -789,6 +790,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.SystemMessage = wireSystemMessage req.Tools = config.Tools req.Provider = config.Provider + req.EnableSessionTelemetry = config.EnableSessionTelemetry req.ModelCapabilities = config.ModelCapabilities req.AvailableTools = config.AvailableTools req.ExcludedTools = config.ExcludedTools diff --git a/go/client_test.go b/go/client_test.go index a2dccab33..92690f37e 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -990,6 +990,35 @@ func TestResumeSessionRequest_ContinuePendingWork(t *testing.T) { }) } +func TestCreateSessionRequest_EnableSessionTelemetry(t *testing.T) { + t.Run("forwards enableSessionTelemetry when false", func(t *testing.T) { + req := createSessionRequest{ + EnableSessionTelemetry: Bool(false), + } + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["enableSessionTelemetry"] != false { + t.Errorf("Expected enableSessionTelemetry to be false, got %v", m["enableSessionTelemetry"]) + } + }) + + t.Run("omits enableSessionTelemetry when not set", func(t *testing.T) { + req := createSessionRequest{} + data, _ := json.Marshal(req) + var m map[string]any + json.Unmarshal(data, &m) + if _, ok := m["enableSessionTelemetry"]; ok { + t.Error("Expected enableSessionTelemetry to be omitted when not set") + } + }) +} + func TestCreateSessionRequest_IncludeSubAgentStreamingEvents(t *testing.T) { t.Run("defaults to true when nil", func(t *testing.T) { req := createSessionRequest{ @@ -1026,6 +1055,36 @@ func TestCreateSessionRequest_IncludeSubAgentStreamingEvents(t *testing.T) { }) } +func TestResumeSessionRequest_EnableSessionTelemetry(t *testing.T) { + t.Run("forwards enableSessionTelemetry when false", func(t *testing.T) { + req := resumeSessionRequest{ + SessionID: "s1", + EnableSessionTelemetry: Bool(false), + } + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["enableSessionTelemetry"] != false { + t.Errorf("Expected enableSessionTelemetry to be false, got %v", m["enableSessionTelemetry"]) + } + }) + + t.Run("omits enableSessionTelemetry when not set", func(t *testing.T) { + req := resumeSessionRequest{SessionID: "s1"} + data, _ := json.Marshal(req) + var m map[string]any + json.Unmarshal(data, &m) + if _, ok := m["enableSessionTelemetry"]; ok { + t.Error("Expected enableSessionTelemetry to be omitted when not set") + } + }) +} + func TestResumeSessionRequest_IncludeSubAgentStreamingEvents(t *testing.T) { t.Run("defaults to true when nil", func(t *testing.T) { req := resumeSessionRequest{ diff --git a/go/types.go b/go/types.go index dd3ffbbe3..4a1885c92 100644 --- a/go/types.go +++ b/go/types.go @@ -570,6 +570,8 @@ type SessionConfig struct { IncludeSubAgentStreamingEvents *bool // Provider configures a custom model provider (BYOK) Provider *ProviderConfig + // When false, disables internal session telemetry for this session. + EnableSessionTelemetry *bool // ModelCapabilities overrides individual model capabilities resolved by the runtime. // Only non-nil fields are applied over the runtime-resolved capabilities. ModelCapabilities *rpc.ModelCapabilitiesOverride @@ -760,6 +762,8 @@ type ResumeSessionConfig struct { ExcludedTools []string // Provider configures a custom model provider Provider *ProviderConfig + // When false, disables internal session telemetry for this session. + EnableSessionTelemetry *bool // ModelCapabilities overrides individual model capabilities resolved by the runtime. // Only non-nil fields are applied over the runtime-resolved capabilities. ModelCapabilities *rpc.ModelCapabilitiesOverride @@ -1039,6 +1043,7 @@ type createSessionRequest struct { AvailableTools []string `json:"availableTools"` ExcludedTools []string `json:"excludedTools,omitempty"` Provider *ProviderConfig `json:"provider,omitempty"` + EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` RequestPermission *bool `json:"requestPermission,omitempty"` RequestUserInput *bool `json:"requestUserInput,omitempty"` @@ -1088,6 +1093,7 @@ type resumeSessionRequest struct { AvailableTools []string `json:"availableTools"` ExcludedTools []string `json:"excludedTools,omitempty"` Provider *ProviderConfig `json:"provider,omitempty"` + EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` RequestPermission *bool `json:"requestPermission,omitempty"` RequestUserInput *bool `json:"requestUserInput,omitempty"` diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index b1b6b4f46..ac7334859 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -801,6 +801,7 @@ export class CopilotClient { availableTools: config.availableTools, excludedTools: config.excludedTools, provider: config.provider ? toWireProviderConfig(config.provider) : undefined, + enableSessionTelemetry: config.enableSessionTelemetry, modelCapabilities: config.modelCapabilities, requestPermission: true, requestUserInput: !!config.onUserInputRequest, @@ -932,6 +933,7 @@ export class CopilotClient { systemMessage: wireSystemMessage, availableTools: config.availableTools, excludedTools: config.excludedTools, + enableSessionTelemetry: config.enableSessionTelemetry, tools: config.tools?.map((tool) => ({ name: tool.name, description: tool.description, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 59dff3d82..dedd83aea 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1273,6 +1273,15 @@ export interface SessionConfig { */ provider?: ProviderConfig; + /** + * When false, disables internal session telemetry (Hydro/AppInsights) for this session. + * This is independent of the OpenTelemetry configuration in {@link CopilotClientOptions.telemetry}. + * When a custom provider (BYOK) is configured, telemetry is always disabled + * regardless of this flag. + * By default, telemetry remains enabled when this field is omitted. + */ + enableSessionTelemetry?: boolean; + /** * Handler for permission requests from the server. * When provided, the server will call this handler to request permission for operations. @@ -1415,6 +1424,7 @@ export type ResumeSessionConfig = Pick< | "availableTools" | "excludedTools" | "provider" + | "enableSessionTelemetry" | "modelCapabilities" | "streaming" | "includeSubAgentStreamingEvents" diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index b2fe998ee..7328ebc1e 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -98,6 +98,47 @@ describe("CopilotClient", () => { spy.mockRestore(); }); + it("forwards enableSessionTelemetry in session.create request", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => client.forceStop()); + + const spy = vi.spyOn((client as any).connection!, "sendRequest"); + await client.createSession({ + enableSessionTelemetry: false, + onPermissionRequest: approveAll, + }); + + expect(spy).toHaveBeenCalledWith( + "session.create", + expect.objectContaining({ enableSessionTelemetry: false }) + ); + }); + + it("forwards enableSessionTelemetry in session.resume request", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => client.forceStop()); + + const session = await client.createSession({ onPermissionRequest: approveAll }); + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.resume") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + await client.resumeSession(session.sessionId, { + enableSessionTelemetry: false, + onPermissionRequest: approveAll, + }); + + expect(spy).toHaveBeenCalledWith( + "session.resume", + expect.objectContaining({ enableSessionTelemetry: false, sessionId: session.sessionId }) + ); + spy.mockRestore(); + }); + it("defaults includeSubAgentStreamingEvents to true in session.create when not specified", async () => { const client = new CopilotClient(); await client.start(); diff --git a/python/copilot/client.py b/python/copilot/client.py index 0e03dcbf7..34eb988cc 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -1239,6 +1239,7 @@ async def create_session( hooks: SessionHooks | None = None, working_directory: str | None = None, provider: ProviderConfig | None = None, + enable_session_telemetry: bool | None = None, model_capabilities: ModelCapabilitiesOverride | None = None, streaming: bool | None = None, include_sub_agent_streaming_events: bool | None = None, @@ -1287,6 +1288,8 @@ async def create_session( hooks: Lifecycle hooks for the session. working_directory: Working directory for the session. provider: Provider configuration for Azure or custom endpoints. + enable_session_telemetry: When False, disables internal session telemetry + for this session. model_capabilities: Override individual model capabilities resolved by the runtime. streaming: Whether to enable streaming responses. include_sub_agent_streaming_events: Whether to include sub-agent streaming @@ -1421,6 +1424,9 @@ async def create_session( if provider: payload["provider"] = self._convert_provider_to_wire_format(provider) + if enable_session_telemetry is not None: + payload["enableSessionTelemetry"] = enable_session_telemetry + # Add model capabilities override if provided if model_capabilities: payload["modelCapabilities"] = _capabilities_to_dict(model_capabilities) @@ -1545,6 +1551,7 @@ async def resume_session( hooks: SessionHooks | None = None, working_directory: str | None = None, provider: ProviderConfig | None = None, + enable_session_telemetry: bool | None = None, model_capabilities: ModelCapabilitiesOverride | None = None, streaming: bool | None = None, include_sub_agent_streaming_events: bool | None = None, @@ -1594,6 +1601,8 @@ async def resume_session( hooks: Lifecycle hooks for the session. working_directory: Working directory for the session. provider: Provider configuration for Azure or custom endpoints. + enable_session_telemetry: When False, disables internal session telemetry + for this session. model_capabilities: Override individual model capabilities resolved by the runtime. streaming: Whether to enable streaming responses. include_sub_agent_streaming_events: Whether to include sub-agent streaming @@ -1690,6 +1699,8 @@ async def resume_session( payload["excludedTools"] = excluded_tools if provider: payload["provider"] = self._convert_provider_to_wire_format(provider) + if enable_session_telemetry is not None: + payload["enableSessionTelemetry"] = enable_session_telemetry if model_capabilities: payload["modelCapabilities"] = _capabilities_to_dict(model_capabilities) if streaming is not None: diff --git a/python/copilot/session.py b/python/copilot/session.py index 97a505c25..f537fad43 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -882,6 +882,8 @@ class SessionConfig(TypedDict, total=False): working_directory: str # Custom provider configuration (BYOK - Bring Your Own Key) provider: ProviderConfig + # When False, disables internal session telemetry for this session. + enable_session_telemetry: bool # Enable streaming of assistant message and reasoning chunks # When True, assistant.message_delta and assistant.reasoning_delta events # with delta_content are sent as the response is generated @@ -950,6 +952,8 @@ class ResumeSessionConfig(TypedDict, total=False): # registered via tools=. Ignored if available_tools is set. excluded_tools: list[str] provider: ProviderConfig + # When False, disables internal session telemetry for this session. + enable_session_telemetry: bool # Reasoning effort level for models that support it. reasoning_effort: ReasoningEffort on_permission_request: _PermissionHandlerFn diff --git a/python/test_client.py b/python/test_client.py index a890ca12e..26de29287 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -543,6 +543,57 @@ async def mock_request(method, params): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_create_session_forwards_enable_session_telemetry(self): + client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + await client.start() + + try: + captured = {} + original_request = client._client.request + + async def mock_request(method, params): + captured[method] = params + return await original_request(method, params) + + client._client.request = mock_request + await client.create_session( + on_permission_request=PermissionHandler.approve_all, + enable_session_telemetry=False, + ) + assert captured["session.create"]["enableSessionTelemetry"] is False + finally: + await client.force_stop() + + @pytest.mark.asyncio + async def test_resume_session_forwards_enable_session_telemetry(self): + client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + await client.start() + + try: + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all + ) + + captured = {} + original_request = client._client.request + + async def mock_request(method, params): + captured[method] = params + if method == "session.resume": + return {"sessionId": session.session_id} + return await original_request(method, params) + + client._client.request = mock_request + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + enable_session_telemetry=False, + ) + assert captured["session.resume"]["enableSessionTelemetry"] is False + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_create_session_forwards_provider_headers(self): client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) From 2ffae8c8ab4bd3464b148549447c3833e28b5e20 Mon Sep 17 00:00:00 2001 From: David Sterling Date: Wed, 6 May 2026 11:22:53 -0700 Subject: [PATCH 2/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- nodejs/src/types.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index dedd83aea..613a130c2 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1274,11 +1274,9 @@ export interface SessionConfig { provider?: ProviderConfig; /** - * When false, disables internal session telemetry (Hydro/AppInsights) for this session. + * Enables or disables session telemetry for this session. * This is independent of the OpenTelemetry configuration in {@link CopilotClientOptions.telemetry}. - * When a custom provider (BYOK) is configured, telemetry is always disabled - * regardless of this flag. - * By default, telemetry remains enabled when this field is omitted. + * By default, session telemetry remains enabled when this field is omitted. */ enableSessionTelemetry?: boolean; From 0732444c5ec914b97f172d3baafd296d755edf40 Mon Sep 17 00:00:00 2001 From: "David Sterling (VS)" Date: Wed, 6 May 2026 12:01:29 -0700 Subject: [PATCH 3/5] Improve enableSessionTelemetry docs, add E2E tests, fix gofmt - Enrich XML/JSDoc/comment docs across all SDKs: explain default behavior, clarify independence from OpenTelemetry config - Add two .NET E2E tests validating wire serialization of enableSessionTelemetry - Fix gofmt alignment in Go test (CI failure) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Types.cs | 12 ++++- dotnet/test/E2E/ClientOptionsE2ETests.cs | 57 ++++++++++++++++++++++++ go/client_test.go | 2 +- go/types.go | 10 ++++- python/copilot/session.py | 8 +++- 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index ff5923a97..78966a714 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -1932,7 +1932,11 @@ protected SessionConfig(SessionConfig? other) public ProviderConfig? Provider { get; set; } /// - /// When false, disables internal session telemetry for this session. + /// Enables or disables internal session telemetry for this session. + /// When false, disables session telemetry. When null (the default) or true, + /// telemetry remains enabled. + /// This is independent of , which configures + /// OpenTelemetry export for observability. /// public bool? EnableSessionTelemetry { get; set; } @@ -2172,7 +2176,11 @@ protected ResumeSessionConfig(ResumeSessionConfig? other) public ProviderConfig? Provider { get; set; } /// - /// When false, disables internal session telemetry for this session. + /// Enables or disables internal session telemetry for this session. + /// When false, disables session telemetry. When null (the default) or true, + /// telemetry remains enabled. + /// This is independent of , which configures + /// OpenTelemetry export for observability. /// public bool? EnableSessionTelemetry { get; set; } diff --git a/dotnet/test/E2E/ClientOptionsE2ETests.cs b/dotnet/test/E2E/ClientOptionsE2ETests.cs index 31627f5a3..14263de79 100644 --- a/dotnet/test/E2E/ClientOptionsE2ETests.cs +++ b/dotnet/test/E2E/ClientOptionsE2ETests.cs @@ -159,6 +159,63 @@ public async Task Should_Propagate_Process_Options_To_Spawned_Cli() await session.DisposeAsync(); } + [Fact] + public async Task Should_Forward_EnableSessionTelemetry_In_Wire_Request() + { + var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); + + await using var client = Ctx.CreateClient(options: new CopilotClientOptions + { + AutoStart = false, + CliPath = cliPath, + CliArgs = ["--capture-file", capturePath], + UseLoggedInUser = false, + }); + + await client.StartAsync(); + + // When explicitly set to false, it should appear in the wire request + var session = await client.CreateSessionAsync(new SessionConfig + { + EnableSessionTelemetry = false, + 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()); + + await session.DisposeAsync(); + } + + [Fact] + public async Task Should_Omit_EnableSessionTelemetry_When_Not_Set() + { + var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); + + await using var client = Ctx.CreateClient(options: new CopilotClientOptions + { + AutoStart = false, + CliPath = cliPath, + CliArgs = ["--capture-file", capturePath], + UseLoggedInUser = false, + }); + + await client.StartAsync(); + + // When omitted (null/default), the field should not be present in the wire request + 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.TryGetProperty("enableSessionTelemetry", out _)); + + await session.DisposeAsync(); + } + [Fact] public async Task Should_Propagate_Activity_TraceContext_To_Session_Create_And_Send() { diff --git a/go/client_test.go b/go/client_test.go index 92690f37e..28b44086e 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -1058,7 +1058,7 @@ func TestCreateSessionRequest_IncludeSubAgentStreamingEvents(t *testing.T) { func TestResumeSessionRequest_EnableSessionTelemetry(t *testing.T) { t.Run("forwards enableSessionTelemetry when false", func(t *testing.T) { req := resumeSessionRequest{ - SessionID: "s1", + SessionID: "s1", EnableSessionTelemetry: Bool(false), } data, err := json.Marshal(req) diff --git a/go/types.go b/go/types.go index 4a1885c92..23eb40a57 100644 --- a/go/types.go +++ b/go/types.go @@ -570,7 +570,10 @@ type SessionConfig struct { IncludeSubAgentStreamingEvents *bool // Provider configures a custom model provider (BYOK) Provider *ProviderConfig - // When false, disables internal session telemetry for this session. + // EnableSessionTelemetry enables or disables internal session telemetry. + // When false, disables session telemetry. When nil (the default) or true, + // telemetry remains enabled. This is independent of the OpenTelemetry + // configuration in CopilotClientOptions. EnableSessionTelemetry *bool // ModelCapabilities overrides individual model capabilities resolved by the runtime. // Only non-nil fields are applied over the runtime-resolved capabilities. @@ -762,7 +765,10 @@ type ResumeSessionConfig struct { ExcludedTools []string // Provider configures a custom model provider Provider *ProviderConfig - // When false, disables internal session telemetry for this session. + // EnableSessionTelemetry enables or disables internal session telemetry. + // When false, disables session telemetry. When nil (the default) or true, + // telemetry remains enabled. This is independent of the OpenTelemetry + // configuration in CopilotClientOptions. EnableSessionTelemetry *bool // ModelCapabilities overrides individual model capabilities resolved by the runtime. // Only non-nil fields are applied over the runtime-resolved capabilities. diff --git a/python/copilot/session.py b/python/copilot/session.py index f537fad43..5042af89f 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -882,7 +882,9 @@ class SessionConfig(TypedDict, total=False): working_directory: str # Custom provider configuration (BYOK - Bring Your Own Key) provider: ProviderConfig - # When False, disables internal session telemetry for this session. + # Enables or disables internal session telemetry. When False, disables session + # telemetry. When omitted (the default) or True, telemetry remains enabled. + # This is independent of the OpenTelemetry configuration in CopilotClientOptions. enable_session_telemetry: bool # Enable streaming of assistant message and reasoning chunks # When True, assistant.message_delta and assistant.reasoning_delta events @@ -952,7 +954,9 @@ class ResumeSessionConfig(TypedDict, total=False): # registered via tools=. Ignored if available_tools is set. excluded_tools: list[str] provider: ProviderConfig - # When False, disables internal session telemetry for this session. + # Enables or disables internal session telemetry. When False, disables session + # telemetry. When omitted (the default) or True, telemetry remains enabled. + # This is independent of the OpenTelemetry configuration in CopilotClientOptions. enable_session_telemetry: bool # Reasoning effort level for models that support it. reasoning_effort: ReasoningEffort From c936a1c6b0706873487385cf1295f2239b2bf003 Mon Sep 17 00:00:00 2001 From: "David Sterling (VS)" Date: Thu, 7 May 2026 09:42:37 -0700 Subject: [PATCH 4/5] Clarify EnableSessionTelemetry docs: GitHub-auth only, BYOK always disables Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Types.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 78966a714..7ab7417fb 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -1934,7 +1934,9 @@ protected SessionConfig(SessionConfig? other) /// /// Enables or disables internal session telemetry for this session. /// When false, disables session telemetry. When null (the default) or true, - /// telemetry remains enabled. + /// telemetry is enabled for GitHub-authenticated sessions. + /// When a custom (BYOK) is configured, session telemetry is + /// always disabled regardless of this setting. /// This is independent of , which configures /// OpenTelemetry export for observability. /// @@ -2178,7 +2180,9 @@ protected ResumeSessionConfig(ResumeSessionConfig? other) /// /// Enables or disables internal session telemetry for this session. /// When false, disables session telemetry. When null (the default) or true, - /// telemetry remains enabled. + /// telemetry is enabled for GitHub-authenticated sessions. + /// When a custom (BYOK) is configured, session telemetry is + /// always disabled regardless of this setting. /// This is independent of , which configures /// OpenTelemetry export for observability. /// From ef2163f478947af961fcf10a3f762ae08b6cbaf3 Mon Sep 17 00:00:00 2001 From: "David Sterling (VS)" Date: Thu, 7 May 2026 10:58:55 -0700 Subject: [PATCH 5/5] Add BYOK clarification to enableSessionTelemetry docs across all SDKs Clarify that when a custom provider (BYOK) is configured, session telemetry is always disabled regardless of the enableSessionTelemetry setting. Updated Node.js, Go, and Python to match .NET docs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- go/types.go | 8 ++++++-- nodejs/src/types.ts | 5 ++++- python/copilot/session.py | 8 ++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/go/types.go b/go/types.go index 23eb40a57..152f809c8 100644 --- a/go/types.go +++ b/go/types.go @@ -572,7 +572,9 @@ type SessionConfig struct { Provider *ProviderConfig // EnableSessionTelemetry enables or disables internal session telemetry. // When false, disables session telemetry. When nil (the default) or true, - // telemetry remains enabled. This is independent of the OpenTelemetry + // telemetry is enabled for GitHub-authenticated sessions. When a custom + // Provider (BYOK) is configured, session telemetry is always disabled + // regardless of this setting. This is independent of the OpenTelemetry // configuration in CopilotClientOptions. EnableSessionTelemetry *bool // ModelCapabilities overrides individual model capabilities resolved by the runtime. @@ -767,7 +769,9 @@ type ResumeSessionConfig struct { Provider *ProviderConfig // EnableSessionTelemetry enables or disables internal session telemetry. // When false, disables session telemetry. When nil (the default) or true, - // telemetry remains enabled. This is independent of the OpenTelemetry + // telemetry is enabled for GitHub-authenticated sessions. When a custom + // Provider (BYOK) is configured, session telemetry is always disabled + // regardless of this setting. This is independent of the OpenTelemetry // configuration in CopilotClientOptions. EnableSessionTelemetry *bool // ModelCapabilities overrides individual model capabilities resolved by the runtime. diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 613a130c2..ef954c85f 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1275,8 +1275,11 @@ export interface SessionConfig { /** * Enables or disables session telemetry for this session. + * When `false`, disables session telemetry. When omitted (the default) or `true`, + * telemetry is enabled for GitHub-authenticated sessions. + * When a custom {@link provider} (BYOK) is configured, session telemetry is always + * disabled regardless of this setting. * This is independent of the OpenTelemetry configuration in {@link CopilotClientOptions.telemetry}. - * By default, session telemetry remains enabled when this field is omitted. */ enableSessionTelemetry?: boolean; diff --git a/python/copilot/session.py b/python/copilot/session.py index 5042af89f..e2a5e534e 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -883,7 +883,9 @@ class SessionConfig(TypedDict, total=False): # Custom provider configuration (BYOK - Bring Your Own Key) provider: ProviderConfig # Enables or disables internal session telemetry. When False, disables session - # telemetry. When omitted (the default) or True, telemetry remains enabled. + # telemetry. When omitted (the default) or True, telemetry is enabled for + # GitHub-authenticated sessions. When a custom provider (BYOK) is configured, + # session telemetry is always disabled regardless of this setting. # This is independent of the OpenTelemetry configuration in CopilotClientOptions. enable_session_telemetry: bool # Enable streaming of assistant message and reasoning chunks @@ -955,7 +957,9 @@ class ResumeSessionConfig(TypedDict, total=False): excluded_tools: list[str] provider: ProviderConfig # Enables or disables internal session telemetry. When False, disables session - # telemetry. When omitted (the default) or True, telemetry remains enabled. + # telemetry. When omitted (the default) or True, telemetry is enabled for + # GitHub-authenticated sessions. When a custom provider (BYOK) is configured, + # session telemetry is always disabled regardless of this setting. # This is independent of the OpenTelemetry configuration in CopilotClientOptions. enable_session_telemetry: bool # Reasoning effort level for models that support it.