From 27d31177f0119ac3d571269d8b2348d7c6e6c9aa Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 2 Sep 2026 11:17:36 +0000 Subject: [PATCH 1/5] Rename client info fields for applications Use application and integration terminology in every public SDK while preserving the existing runtime wire protocol. Update tests and documentation to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CHANGELOG.md | 4 +- docs/features/README.md | 2 +- docs/features/client-info.md | 94 ++++++++-------- dotnet/src/Client.cs | 10 +- dotnet/src/Types.cs | 22 ++-- dotnet/test/Unit/CloneTests.cs | 8 +- dotnet/test/Unit/GitHubTelemetryTests.cs | 26 ++--- go/client_test.go | 18 +-- go/types.go | 40 ++++--- .../com/github/copilot/CopilotClient.java | 2 +- .../com/github/copilot/rpc/ClientInfo.java | 106 +++++++++--------- .../copilot/rpc/CopilotClientOptions.java | 10 +- .../github/copilot/GitHubTelemetryTest.java | 25 +++-- nodejs/src/client.ts | 10 +- nodejs/src/types.ts | 24 ++-- nodejs/test/client.test.ts | 31 ++--- python/copilot/client.py | 44 ++++---- python/test_client.py | 30 ++--- rust/src/lib.rs | 70 ++++++------ rust/tests/protocol_version_test.rs | 32 +++--- 20 files changed, 309 insertions(+), 299 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54e0c94d66..f7e753beda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,9 @@ See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the fu ## [Unreleased] -### Feature: declare host identity with client info +### Feature: declare application identity with client info -Client options now accept optional client info (editor name and version, extension name and version) across all six SDKs, exposed idiomatically per language (`clientInfo` in Node.js, `client_info` in Python and Rust, `ClientInfo` in Go and .NET, `setClientInfo` in Java). When set, the SDK forwards it on the `server.connect` handshake so the telemetry the runtime emits on the connection is attributed to the host editor and its Copilot extension instead of the runtime's own build. All fields are optional, and leaving client info unset keeps the runtime's default attribution. See [Client info](./docs/features/client-info.md). +Client options now accept optional client info (application name and version, integration name and version) across all six SDKs, exposed idiomatically per language (`clientInfo` in Node.js, `client_info` in Python and Rust, `ClientInfo` in Go and .NET, `setClientInfo` in Java). When set, the SDK forwards it on the `server.connect` handshake so the telemetry the runtime emits on the connection is attributed to the application and its Copilot integration instead of the runtime's own build. All fields are optional, and leaving client info unset keeps the runtime's default attribution. See [Client info](./docs/features/client-info.md). ### Feature: Node Agent Factories pagination and run notifications diff --git a/docs/features/README.md b/docs/features/README.md index 6350958720..5ea070a5aa 100644 --- a/docs/features/README.md +++ b/docs/features/README.md @@ -20,7 +20,7 @@ These guides cover the capabilities you can add to your Copilot SDK application. | [Image Input](./image-input.md) | Send images to sessions as attachments | | [Streaming Events](./streaming-events.md) | Subscribe to real-time session events (40+ event types) | | [Usage and Billing](./usage-and-billing.md) | Read token counts, context-window utilization, AI credit cost, and account quota | -| [Client info](./client-info.md) | Declare the host editor and extension so runtime telemetry is attributed to your surface | +| [Client info](./client-info.md) | Declare application and integration identity for runtime telemetry attribution | | [Steering & Queueing](./steering-and-queueing.md) | Control message delivery—immediate steering vs. sequential queueing | | [Context Clearing](./context-management.md) | Replace conversation context safely with terminal tools | | [Session Persistence](./session-persistence.md) | Resume sessions across restarts, manage session storage | diff --git a/docs/features/client-info.md b/docs/features/client-info.md index 48ada2d13a..1919f7299c 100644 --- a/docs/features/client-info.md +++ b/docs/features/client-info.md @@ -1,21 +1,21 @@ # Client info -Client info identifies the editor or extension that hosts your Copilot SDK application. Set the optional `clientInfo` client option to attribute runtime telemetry for that connection to your host instead of the runtime's own build. +Client info identifies the application using the Copilot SDK and, when applicable, the Copilot integration within it. Set the optional `clientInfo` client option to attribute runtime telemetry for that connection to your application instead of the runtime's own build. ## When to set client info -Set client info when you embed the SDK in an editor, an extension, or another application with its own identity. +Set client info when your SDK application represents a distinct product, service, or integration whose runtime activity should be attributed consistently. -Leave client info unset for scripts, one-off tools, and back-end jobs that do not represent a distinct host. The runtime then keeps its default attribution. +Leave client info unset for scripts, one-off tools, and jobs that do not represent a distinct application. The runtime then keeps its default attribution. Client info has four optional string fields. Set the fields you know and omit the rest. The SDK includes client info in the `server.connect` handshake only when at least one field has a non-empty value. | Field | Example | Meaning | |---|---|---| -| `editorName` | `"vscode"` | Name of the host editor | -| `editorVersion` | `"1.124.2"` | Version of the host editor | -| `extensionName` | `"copilot-chat"` | Name of the Copilot extension within the host | -| `extensionVersion` | `"0.54.0"` | Version of the Copilot extension within the host | +| `applicationName` | `"acme-developer-portal"` | Name of the application using the SDK | +| `applicationVersion` | `"2.4.0"` | Version of the application using the SDK | +| `integrationName` | `"copilot-assistant"` | Name of the Copilot integration within the application | +| `integrationVersion` | `"1.5.0"` | Version of the Copilot integration within the application | The SDK sends client info once when it establishes the connection. The identity applies for the lifetime of that connection. @@ -33,10 +33,10 @@ import { CopilotClient } from "@github/copilot-sdk"; async function main() { const client = new CopilotClient({ clientInfo: { - editorName: "JetBrains-IU", - editorVersion: "2026.1", - extensionName: "copilot-intellij", - extensionVersion: "1.5.0", + applicationName: "acme-developer-portal", + applicationVersion: "2.4.0", + integrationName: "copilot-assistant", + integrationVersion: "1.5.0", }, }); @@ -52,10 +52,10 @@ import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient({ clientInfo: { - editorName: "JetBrains-IU", - editorVersion: "2026.1", - extensionName: "copilot-intellij", - extensionVersion: "1.5.0", + applicationName: "acme-developer-portal", + applicationVersion: "2.4.0", + integrationName: "copilot-assistant", + integrationVersion: "1.5.0", }, }); @@ -73,10 +73,10 @@ from copilot import CopilotClient client = CopilotClient( client_info={ - "editor_name": "JetBrains-IU", - "editor_version": "2026.1", - "extension_name": "copilot-intellij", - "extension_version": "1.5.0", + "application_name": "acme-developer-portal", + "application_version": "2.4.0", + "integration_name": "copilot-assistant", + "integration_version": "1.5.0", }, ) await client.start() @@ -101,10 +101,10 @@ func main() { ctx := context.Background() client := copilot.NewClient(&copilot.ClientOptions{ ClientInfo: &copilot.ClientInfo{ - EditorName: "JetBrains-IU", - EditorVersion: "2026.1", - ExtensionName: "copilot-intellij", - ExtensionVersion: "1.5.0", + ApplicationName: "acme-developer-portal", + ApplicationVersion: "2.4.0", + IntegrationName: "copilot-assistant", + IntegrationVersion: "1.5.0", }, }) if err := client.Start(ctx); err != nil { @@ -117,10 +117,10 @@ func main() { ```go client := copilot.NewClient(&copilot.ClientOptions{ ClientInfo: &copilot.ClientInfo{ - EditorName: "JetBrains-IU", - EditorVersion: "2026.1", - ExtensionName: "copilot-intellij", - ExtensionVersion: "1.5.0", + ApplicationName: "acme-developer-portal", + ApplicationVersion: "2.4.0", + IntegrationName: "copilot-assistant", + IntegrationVersion: "1.5.0", }, }) if err := client.Start(ctx); err != nil { @@ -140,10 +140,10 @@ await using var client = new CopilotClient(new CopilotClientOptions { ClientInfo = new CopilotClientInfo { - EditorName = "JetBrains-IU", - EditorVersion = "2026.1", - ExtensionName = "copilot-intellij", - ExtensionVersion = "1.5.0", + ApplicationName = "acme-developer-portal", + ApplicationVersion = "2.4.0", + IntegrationName = "copilot-assistant", + IntegrationVersion = "1.5.0", }, }); @@ -165,10 +165,10 @@ public class ClientInfoExample { public static void main(String[] args) throws Exception { var options = new CopilotClientOptions() .setClientInfo(new ClientInfo() - .setEditorName("JetBrains-IU") - .setEditorVersion("2026.1") - .setExtensionName("copilot-intellij") - .setExtensionVersion("1.5.0")); + .setApplicationName("acme-developer-portal") + .setApplicationVersion("2.4.0") + .setIntegrationName("copilot-assistant") + .setIntegrationVersion("1.5.0")); var client = new CopilotClient(options); client.start().get(); @@ -180,10 +180,10 @@ public class ClientInfoExample { ```java var options = new CopilotClientOptions() .setClientInfo(new ClientInfo() - .setEditorName("JetBrains-IU") - .setEditorVersion("2026.1") - .setExtensionName("copilot-intellij") - .setExtensionVersion("1.5.0")); + .setApplicationName("acme-developer-portal") + .setApplicationVersion("2.4.0") + .setIntegrationName("copilot-assistant") + .setIntegrationVersion("1.5.0")); var client = new CopilotClient(options); client.start().get(); @@ -203,10 +203,10 @@ async fn main() -> Result<(), Box> { let _client = Client::start( ClientOptions::new().with_client_info( ClientInfo::new() - .with_editor_name("JetBrains-IU") - .with_editor_version("2026.1") - .with_extension_name("copilot-intellij") - .with_extension_version("1.5.0"), + .with_application_name("acme-developer-portal") + .with_application_version("2.4.0") + .with_integration_name("copilot-assistant") + .with_integration_version("1.5.0"), ), ) .await?; @@ -221,10 +221,10 @@ use github_copilot_sdk::{Client, ClientInfo, ClientOptions}; let client = Client::start( ClientOptions::new().with_client_info( ClientInfo::new() - .with_editor_name("JetBrains-IU") - .with_editor_version("2026.1") - .with_extension_name("copilot-intellij") - .with_extension_version("1.5.0"), + .with_application_name("acme-developer-portal") + .with_application_version("2.4.0") + .with_integration_name("copilot-assistant") + .with_integration_version("1.5.0"), ), ) .await?; diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index a23032a905..92650ee60c 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -2183,7 +2183,7 @@ [new ConnectHandshakeRequest( // `connect` handshake so the first session's un-replayable `session.start` // event is forwarded). Also sent on session.create/resume for older CLIs. _options.OnGitHubTelemetry != null ? true : null, - // Declare the integrating host's identity so the runtime attributes the + // Declare the integrating application's identity so the runtime attributes the // telemetry it emits on this connection to a consistent surface instead // of its own build. Null when the app didn't supply it. ConnectHandshakeClientInfo.From(_options.ClientInfo))], @@ -3213,10 +3213,10 @@ internal record ConnectHandshakeClientInfo( return null; } - var editorName = NullIfEmpty(info.EditorName); - var editorVersion = NullIfEmpty(info.EditorVersion); - var extensionName = NullIfEmpty(info.ExtensionName); - var extensionVersion = NullIfEmpty(info.ExtensionVersion); + var editorName = NullIfEmpty(info.ApplicationName); + var editorVersion = NullIfEmpty(info.ApplicationVersion); + var extensionName = NullIfEmpty(info.IntegrationName); + var extensionVersion = NullIfEmpty(info.IntegrationVersion); if (editorName is null && editorVersion is null && extensionName is null && extensionVersion is null) { return null; diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index ec58000e97..9d80bb6be0 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -467,10 +467,10 @@ private CopilotClientOptions(CopilotClientOptions? other) public bool EnableRemoteSessions { get; set; } /// - /// Declares the integrating host's identity, forwarded to the runtime on the + /// Declares the integrating application's identity, forwarded to the runtime on the /// server.connect handshake. Declaring it lets the telemetry the /// runtime emits on this connection be attributed to a consistent surface - /// (the host editor and its Copilot extension) instead of the runtime's own + /// (the application and its Copilot integration) instead of the runtime's own /// build. All fields are optional; leave it to keep /// the runtime's default attribution. /// @@ -543,7 +543,7 @@ public sealed class TelemetryConfig } /// -/// Identifies the integrating host on the server.connect handshake. +/// Identifies the integrating application on the server.connect handshake. /// /// /// Declaring it lets the telemetry the runtime emits on the connection be @@ -554,24 +554,24 @@ public sealed class TelemetryConfig public sealed class CopilotClientInfo { /// - /// Name of the host editor, e.g. "vscode". + /// Name of the application using the SDK. /// - public string? EditorName { get; set; } + public string? ApplicationName { get; set; } /// - /// Version of the host editor, e.g. "1.124.2". + /// Version of the application using the SDK. /// - public string? EditorVersion { get; set; } + public string? ApplicationVersion { get; set; } /// - /// Name of the Copilot extension within the host, e.g. "copilot-chat". + /// Name of the Copilot integration within the application. /// - public string? ExtensionName { get; set; } + public string? IntegrationName { get; set; } /// - /// Version of the Copilot extension within the host, e.g. "0.54.0". + /// Version of the Copilot integration within the application. /// - public string? ExtensionVersion { get; set; } + public string? IntegrationVersion { get; set; } } /// diff --git a/dotnet/test/Unit/CloneTests.cs b/dotnet/test/Unit/CloneTests.cs index 2d25d4761f..2f213525f6 100644 --- a/dotnet/test/Unit/CloneTests.cs +++ b/dotnet/test/Unit/CloneTests.cs @@ -25,10 +25,10 @@ public void CopilotClientOptions_Clone_CopiesAllProperties() SessionIdleTimeoutSeconds = 600, ClientInfo = new CopilotClientInfo { - EditorName = "example-editor", - EditorVersion = "1.0.0", - ExtensionName = "example-extension", - ExtensionVersion = "2.0.0", + ApplicationName = "example-app", + ApplicationVersion = "1.0.0", + IntegrationName = "example-integration", + IntegrationVersion = "2.0.0", }, }; diff --git a/dotnet/test/Unit/GitHubTelemetryTests.cs b/dotnet/test/Unit/GitHubTelemetryTests.cs index 708e599d9f..6d76af5742 100644 --- a/dotnet/test/Unit/GitHubTelemetryTests.cs +++ b/dotnet/test/Unit/GitHubTelemetryTests.cs @@ -149,19 +149,19 @@ public async Task Connect_Forwards_Declared_ClientInfo() Connection = RuntimeConnection.ForUri(server.Url), ClientInfo = new CopilotClientInfo { - EditorName = "JetBrains-IU", - EditorVersion = "2026.1", - ExtensionName = "copilot-intellij", - ExtensionVersion = "1.5.0", + ApplicationName = "acme-developer-portal", + ApplicationVersion = "2.4.0", + IntegrationName = "copilot-assistant", + IntegrationVersion = "1.5.0", }, }); await client.StartAsync(); var connectParams = server.LastConnectParams ?? throw new InvalidOperationException("connect was not captured."); Assert.True(connectParams.TryGetProperty("clientInfo", out var clientInfo)); - Assert.Equal("JetBrains-IU", clientInfo.GetProperty("editorName").GetString()); - Assert.Equal("2026.1", clientInfo.GetProperty("editorVersion").GetString()); - Assert.Equal("copilot-intellij", clientInfo.GetProperty("extensionName").GetString()); + Assert.Equal("acme-developer-portal", clientInfo.GetProperty("editorName").GetString()); + Assert.Equal("2.4.0", clientInfo.GetProperty("editorVersion").GetString()); + Assert.Equal("copilot-assistant", clientInfo.GetProperty("extensionName").GetString()); Assert.Equal("1.5.0", clientInfo.GetProperty("extensionVersion").GetString()); } @@ -188,13 +188,13 @@ public async Task Connect_Omits_Empty_ClientInfo_Fields() await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url), - ClientInfo = new CopilotClientInfo { EditorName = "example-editor", EditorVersion = "" }, + ClientInfo = new CopilotClientInfo { ApplicationName = "example-app", ApplicationVersion = "" }, }); await client.StartAsync(); var connectParams = server.LastConnectParams ?? throw new InvalidOperationException("connect was not captured."); Assert.True(connectParams.TryGetProperty("clientInfo", out var clientInfo)); - Assert.Equal("example-editor", clientInfo.GetProperty("editorName").GetString()); + Assert.Equal("example-app", clientInfo.GetProperty("editorName").GetString()); Assert.False(clientInfo.TryGetProperty("editorVersion", out _)); Assert.False(clientInfo.TryGetProperty("extensionName", out _)); Assert.False(clientInfo.TryGetProperty("extensionVersion", out _)); @@ -209,10 +209,10 @@ public async Task Connect_Omits_All_Empty_ClientInfo() Connection = RuntimeConnection.ForUri(server.Url), ClientInfo = new CopilotClientInfo { - EditorName = "", - EditorVersion = "", - ExtensionName = "", - ExtensionVersion = "", + ApplicationName = "", + ApplicationVersion = "", + IntegrationName = "", + IntegrationVersion = "", }, }); await client.StartAsync(); diff --git a/go/client_test.go b/go/client_test.go index 99a21255dc..e74c234637 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -300,10 +300,10 @@ func TestClient_ClientInfo(t *testing.T) { client := NewClient(&ClientOptions{ Connection: URIConnection{URL: url}, ClientInfo: &ClientInfo{ - EditorName: "JetBrains-IU", - EditorVersion: "2026.1", - ExtensionName: "copilot-intellij", - ExtensionVersion: "1.5.0", + ApplicationName: "acme-developer-portal", + ApplicationVersion: "2.4.0", + IntegrationName: "copilot-assistant", + IntegrationVersion: "1.5.0", }, }) if err := client.Start(t.Context()); err != nil { @@ -313,9 +313,9 @@ func TestClient_ClientInfo(t *testing.T) { params := findConnect(requests()) want := map[string]any{ - "editorName": "JetBrains-IU", - "editorVersion": "2026.1", - "extensionName": "copilot-intellij", + "editorName": "acme-developer-portal", + "editorVersion": "2.4.0", + "extensionName": "copilot-assistant", "extensionVersion": "1.5.0", } if !reflect.DeepEqual(params["clientInfo"], want) { @@ -344,14 +344,14 @@ func TestClient_ClientInfo(t *testing.T) { client := NewClient(&ClientOptions{ Connection: URIConnection{URL: url}, - ClientInfo: &ClientInfo{EditorName: "example-editor"}, + ClientInfo: &ClientInfo{ApplicationName: "example-app"}, }) if err := client.Start(t.Context()); err != nil { t.Fatalf("Start failed: %v", err) } defer client.ForceStop() - want := map[string]any{"editorName": "example-editor"} + want := map[string]any{"editorName": "example-app"} if got := findConnect(requests())["clientInfo"]; !reflect.DeepEqual(got, want) { t.Fatalf("clientInfo = %v, want %v", got, want) } diff --git a/go/types.go b/go/types.go index a1deebd1d7..993a1ef970 100644 --- a/go/types.go +++ b/go/types.go @@ -193,10 +193,10 @@ type ClientOptions struct { // directory are accessible from GitHub web and mobile. // Ignored when connecting to an existing runtime via [URIConnection]. EnableRemoteSessions bool - // ClientInfo declares the integrating host's identity, forwarded to the + // ClientInfo declares the integrating application's identity, forwarded to the // runtime on the `server.connect` handshake. Declaring it lets the // telemetry the runtime emits on this connection be attributed to a - // consistent surface (the host editor and its Copilot extension) instead of + // consistent surface (the application and its Copilot integration) instead of // the runtime's own build. All fields are optional; leave it nil to keep the // runtime's default attribution. ClientInfo *ClientInfo @@ -211,22 +211,20 @@ type ClientOptions struct { Mode ClientMode } -// ClientInfo identifies the integrating host on the `server.connect` handshake. +// ClientInfo identifies the integrating application on the `server.connect` handshake. // // Declaring it lets the telemetry the runtime emits on the connection be // attributed to a single, consistent surface instead of the runtime's own // build. All fields are optional; an empty field is omitted from the handshake. type ClientInfo struct { - // EditorName is the name of the host editor, e.g. "vscode". - EditorName string - // EditorVersion is the version of the host editor, e.g. "1.124.2". - EditorVersion string - // ExtensionName is the name of the Copilot extension within the host, e.g. - // "copilot-chat". - ExtensionName string - // ExtensionVersion is the version of the Copilot extension within the host, - // e.g. "0.54.0". - ExtensionVersion string + // ApplicationName is the name of the application using the SDK. + ApplicationName string + // ApplicationVersion is the version of the application using the SDK. + ApplicationVersion string + // IntegrationName is the name of the Copilot integration within the application. + IntegrationName string + // IntegrationVersion is the version of the Copilot integration within the application. + IntegrationVersion string } // toWire maps the public [ClientInfo] onto the generated connect wire shape, @@ -238,20 +236,20 @@ func (ci *ClientInfo) toWire() *rpc.ConnectClientInfo { } wire := &rpc.ConnectClientInfo{} populated := false - if ci.EditorName != "" { - wire.EditorName = &ci.EditorName + if ci.ApplicationName != "" { + wire.EditorName = &ci.ApplicationName populated = true } - if ci.EditorVersion != "" { - wire.EditorVersion = &ci.EditorVersion + if ci.ApplicationVersion != "" { + wire.EditorVersion = &ci.ApplicationVersion populated = true } - if ci.ExtensionName != "" { - wire.ExtensionName = &ci.ExtensionName + if ci.IntegrationName != "" { + wire.ExtensionName = &ci.IntegrationName populated = true } - if ci.ExtensionVersion != "" { - wire.ExtensionVersion = &ci.ExtensionVersion + if ci.IntegrationVersion != "" { + wire.ExtensionVersion = &ci.IntegrationVersion populated = true } if !populated { diff --git a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java index 3818c82646..c986e829a4 100644 --- a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java @@ -642,7 +642,7 @@ private void verifyProtocolVersion(Connection connection) throws Exception { if (this.options.getOnGitHubTelemetry() != null) { connectParams.put("enableGitHubTelemetryForwarding", true); } - // Declare the integrating host's identity so the runtime attributes the + // Declare the integrating application's identity so the runtime attributes the // telemetry it emits on this connection to a consistent surface instead of // its own build. Omitted when the app didn't supply it (or supplied no fields). var clientInfo = this.options.getClientInfo(); diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java b/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java index ebb54db6db..23aaf4ac05 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java @@ -9,19 +9,20 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * Identity of the integrating host, declared on the {@code server.connect} + * Identity of the integrating application, declared on the {@code server.connect} * handshake. *

* Declaring it lets the telemetry the runtime emits on the connection be - * attributed to a single, consistent surface (the host editor and its Copilot - * extension) instead of the runtime's own build. All fields are optional; an + * attributed to a single, consistent surface (the application and its Copilot + * integration) instead of the runtime's own build. All fields are optional; an * empty field is omitted from the handshake. * *

Example Usage

* *
{@code
- * var options = new CopilotClientOptions().setClientInfo(new ClientInfo().setEditorName("vscode")
- * 		.setEditorVersion("1.124.2").setExtensionName("copilot-chat").setExtensionVersion("0.54.0"));
+ * var options = new CopilotClientOptions().setClientInfo(new ClientInfo()
+ * 		.setApplicationName("acme-developer-portal").setApplicationVersion("2.4.0")
+ * 		.setIntegrationName("copilot-assistant").setIntegrationVersion("1.5.0"));
  * }
* * @see CopilotClientOptions#setClientInfo(ClientInfo) @@ -30,99 +31,103 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public class ClientInfo { - @JsonProperty("editorName") - private String editorName; + private String applicationName; - @JsonProperty("editorVersion") - private String editorVersion; + private String applicationVersion; - @JsonProperty("extensionName") - private String extensionName; + private String integrationName; - @JsonProperty("extensionVersion") - private String extensionVersion; + private String integrationVersion; /** - * Gets the name of the host editor. + * Gets the name of the application using the SDK. * - * @return the editor name (e.g., {@code "vscode"}), or {@code null} + * @return the application name, or {@code null} */ - public String getEditorName() { - return editorName; + @JsonProperty("editorName") + public String getApplicationName() { + return applicationName; } /** - * Sets the name of the host editor. + * Sets the name of the application using the SDK. * - * @param editorName - * the editor name (e.g., {@code "vscode"}) + * @param applicationName + * the application name * @return this client info for method chaining */ - public ClientInfo setEditorName(String editorName) { - this.editorName = editorName; + @JsonProperty("editorName") + public ClientInfo setApplicationName(String applicationName) { + this.applicationName = applicationName; return this; } /** - * Gets the version of the host editor. + * Gets the version of the application using the SDK. * - * @return the editor version (e.g., {@code "1.124.2"}), or {@code null} + * @return the application version, or {@code null} */ - public String getEditorVersion() { - return editorVersion; + @JsonProperty("editorVersion") + public String getApplicationVersion() { + return applicationVersion; } /** - * Sets the version of the host editor. + * Sets the version of the application using the SDK. * - * @param editorVersion - * the editor version (e.g., {@code "1.124.2"}) + * @param applicationVersion + * the application version * @return this client info for method chaining */ - public ClientInfo setEditorVersion(String editorVersion) { - this.editorVersion = editorVersion; + @JsonProperty("editorVersion") + public ClientInfo setApplicationVersion(String applicationVersion) { + this.applicationVersion = applicationVersion; return this; } /** - * Gets the name of the Copilot extension within the host. + * Gets the name of the Copilot integration within the application. * - * @return the extension name (e.g., {@code "copilot-chat"}), or {@code null} + * @return the integration name, or {@code null} */ - public String getExtensionName() { - return extensionName; + @JsonProperty("extensionName") + public String getIntegrationName() { + return integrationName; } /** - * Sets the name of the Copilot extension within the host. + * Sets the name of the Copilot integration within the application. * - * @param extensionName - * the extension name (e.g., {@code "copilot-chat"}) + * @param integrationName + * the integration name * @return this client info for method chaining */ - public ClientInfo setExtensionName(String extensionName) { - this.extensionName = extensionName; + @JsonProperty("extensionName") + public ClientInfo setIntegrationName(String integrationName) { + this.integrationName = integrationName; return this; } /** - * Gets the version of the Copilot extension within the host. + * Gets the version of the Copilot integration within the application. * - * @return the extension version (e.g., {@code "0.54.0"}), or {@code null} + * @return the integration version, or {@code null} */ - public String getExtensionVersion() { - return extensionVersion; + @JsonProperty("extensionVersion") + public String getIntegrationVersion() { + return integrationVersion; } /** - * Sets the version of the Copilot extension within the host. + * Sets the version of the Copilot integration within the application. * - * @param extensionVersion - * the extension version (e.g., {@code "0.54.0"}) + * @param integrationVersion + * the integration version * @return this client info for method chaining */ - public ClientInfo setExtensionVersion(String extensionVersion) { - this.extensionVersion = extensionVersion; + @JsonProperty("extensionVersion") + public ClientInfo setIntegrationVersion(String integrationVersion) { + this.integrationVersion = integrationVersion; return this; } @@ -135,7 +140,8 @@ public ClientInfo setExtensionVersion(String extensionVersion) { */ @JsonIgnore public boolean isEmpty() { - return isBlank(editorName) && isBlank(editorVersion) && isBlank(extensionName) && isBlank(extensionVersion); + return isBlank(applicationName) && isBlank(applicationVersion) && isBlank(integrationName) + && isBlank(integrationVersion); } private static boolean isBlank(String value) { diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java index 3af76da52f..6075554546 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java @@ -680,7 +680,7 @@ public CopilotClientOptions setTelemetry(TelemetryConfig telemetry) { } /** - * Gets the integrating host's declared identity. + * Gets the integrating application's declared identity. * * @return the client info, or {@code null} * @since 1.6.0 @@ -690,17 +690,17 @@ public ClientInfo getClientInfo() { } /** - * Declares the integrating host's identity, forwarded to the runtime on the + * Declares the integrating application's identity, forwarded to the runtime on the * {@code server.connect} handshake. *

* Declaring it lets the telemetry the runtime emits on this connection be - * attributed to a consistent surface (the host editor and its Copilot - * extension) instead of the runtime's own build. All fields on + * attributed to a consistent surface (the application and its Copilot + * integration) instead of the runtime's own build. All fields on * {@link ClientInfo} are optional; leave this unset to keep the runtime's * default attribution. * * @param clientInfo - * the host identity to declare + * the application identity to declare * @return this options instance for method chaining * @since 1.6.0 */ diff --git a/java/sdk/src/test/java/com/github/copilot/GitHubTelemetryTest.java b/java/sdk/src/test/java/com/github/copilot/GitHubTelemetryTest.java index af83fc7803..5a6e378b2a 100644 --- a/java/sdk/src/test/java/com/github/copilot/GitHubTelemetryTest.java +++ b/java/sdk/src/test/java/com/github/copilot/GitHubTelemetryTest.java @@ -209,17 +209,18 @@ void clientOmitsForwardingWhenNoHandler() throws Exception { void connectForwardsDeclaredClientInfo() throws Exception { try (var server = new FakeRuntimeServer(); var client = new CopilotClient(new CopilotClientOptions().setCliUrl(server.url()) - .setClientInfo(new ClientInfo().setEditorName("JetBrains-IU").setEditorVersion("2026.1") - .setExtensionName("copilot-intellij").setExtensionVersion("1.5.0")))) { + .setClientInfo(new ClientInfo().setApplicationName("acme-developer-portal") + .setApplicationVersion("2.4.0").setIntegrationName("copilot-assistant") + .setIntegrationVersion("1.5.0")))) { client.start().get(15, TimeUnit.SECONDS); JsonNode connectParams = server.awaitConnect(); JsonNode clientInfo = connectParams.path("clientInfo"); assertEquals(4, clientInfo.size(), "clientInfo should carry only the four declared fields"); - assertEquals("JetBrains-IU", clientInfo.path("editorName").asText()); - assertEquals("2026.1", clientInfo.path("editorVersion").asText()); - assertEquals("copilot-intellij", clientInfo.path("extensionName").asText()); + assertEquals("acme-developer-portal", clientInfo.path("editorName").asText()); + assertEquals("2.4.0", clientInfo.path("editorVersion").asText()); + assertEquals("copilot-assistant", clientInfo.path("extensionName").asText()); assertEquals("1.5.0", clientInfo.path("extensionVersion").asText()); } } @@ -241,13 +242,13 @@ void connectOmitsClientInfoWhenUnset() throws Exception { void connectOmitsEmptyClientInfoFields() throws Exception { try (var server = new FakeRuntimeServer(); var client = new CopilotClient(new CopilotClientOptions().setCliUrl(server.url()) - .setClientInfo(new ClientInfo().setEditorName("example-editor")))) { + .setClientInfo(new ClientInfo().setApplicationName("example-app")))) { client.start().get(15, TimeUnit.SECONDS); JsonNode connectParams = server.awaitConnect(); JsonNode clientInfo = connectParams.path("clientInfo"); - assertEquals("example-editor", clientInfo.path("editorName").asText()); + assertEquals("example-app", clientInfo.path("editorName").asText()); assertFalse(clientInfo.has("editorVersion"), "unset editorVersion should be omitted"); assertFalse(clientInfo.has("extensionName"), "unset extensionName should be omitted"); assertFalse(clientInfo.has("extensionVersion"), "unset extensionVersion should be omitted"); @@ -258,14 +259,14 @@ void connectOmitsEmptyClientInfoFields() throws Exception { void connectDropsEmptyClientInfoFields() throws Exception { try (var server = new FakeRuntimeServer(); var client = new CopilotClient(new CopilotClientOptions().setCliUrl(server.url()) - .setClientInfo(new ClientInfo().setEditorName("vscode").setEditorVersion("")))) { + .setClientInfo(new ClientInfo().setApplicationName("example-app").setApplicationVersion("")))) { client.start().get(15, TimeUnit.SECONDS); JsonNode connectParams = server.awaitConnect(); JsonNode clientInfo = connectParams.path("clientInfo"); assertEquals(1, clientInfo.size(), "clientInfo should carry only the non-empty field"); - assertEquals("vscode", clientInfo.path("editorName").asText()); + assertEquals("example-app", clientInfo.path("editorName").asText()); assertFalse(clientInfo.has("editorVersion"), "empty editorVersion should be dropped"); } } @@ -274,8 +275,8 @@ void connectDropsEmptyClientInfoFields() throws Exception { void connectOmitsAllEmptyClientInfo() throws Exception { try (var server = new FakeRuntimeServer(); var client = new CopilotClient(new CopilotClientOptions().setCliUrl(server.url()) - .setClientInfo(new ClientInfo().setEditorName("").setEditorVersion("").setExtensionName("") - .setExtensionVersion("")))) { + .setClientInfo(new ClientInfo().setApplicationName("").setApplicationVersion("") + .setIntegrationName("").setIntegrationVersion("")))) { client.start().get(15, TimeUnit.SECONDS); @@ -286,7 +287,7 @@ void connectOmitsAllEmptyClientInfo() throws Exception { @Test void optionsRetainAndCloneClientInfo() { - var info = new ClientInfo().setEditorName("example-editor"); + var info = new ClientInfo().setApplicationName("example-app"); var options = new CopilotClientOptions().setClientInfo(info); assertSame(info, options.getClientInfo()); diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index eb8391e1dd..83b0a5f483 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -270,10 +270,10 @@ function toWireCustomAgents(agents: CustomAgentConfig[] | undefined): unknown[] function clientInfoToWire(info: CopilotClientInfo | undefined): ConnectClientInfo | undefined { if (info == null) return undefined; const wire: ConnectClientInfo = {}; - if (info.editorName) wire.editorName = info.editorName; - if (info.editorVersion) wire.editorVersion = info.editorVersion; - if (info.extensionName) wire.extensionName = info.extensionName; - if (info.extensionVersion) wire.extensionVersion = info.extensionVersion; + if (info.applicationName) wire.editorName = info.applicationName; + if (info.applicationVersion) wire.editorVersion = info.applicationVersion; + if (info.integrationName) wire.extensionName = info.integrationName; + if (info.integrationVersion) wire.extensionVersion = info.integrationVersion; return Object.keys(wire).length > 0 ? wire : undefined; } @@ -2247,7 +2247,7 @@ export class CopilotClient { if (this.onGitHubTelemetry != null) { connectParams.enableGitHubTelemetryForwarding = true; } - // Declare the integrating host's identity so the runtime attributes + // Declare the integrating application's identity so the runtime attributes // the telemetry it emits on this connection to a consistent surface // instead of its own build. Empty fields are dropped, and an // all-empty identity is omitted entirely. diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index efed11194a..752a8ed9fd 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -308,7 +308,7 @@ export type InternalRuntimeConnection = RuntimeConnection | ParentProcessRuntime export type CopilotClientMode = "empty" | "copilot-cli"; /** - * Identity of the integrating host, declared once on the `server.connect` + * Identity of the integrating application, declared once on the `server.connect` * handshake so the telemetry the runtime emits on this connection is attributed * to a single, consistent surface rather than to the runtime's own build. * @@ -318,24 +318,24 @@ export type CopilotClientMode = "empty" | "copilot-cli"; */ export interface CopilotClientInfo { /** - * Name of the host editor, e.g. `"vscode"`. + * Name of the application using the SDK, e.g. `"acme-developer-portal"`. */ - editorName?: string; + applicationName?: string; /** - * Version of the host editor, e.g. `"1.124.2"`. + * Version of the application using the SDK, e.g. `"2.4.0"`. */ - editorVersion?: string; + applicationVersion?: string; /** - * Name of the Copilot extension within the host, e.g. `"copilot-chat"`. + * Name of the Copilot integration within the application, e.g. `"copilot-assistant"`. */ - extensionName?: string; + integrationName?: string; /** - * Version of the Copilot extension within the host, e.g. `"0.54.0"`. + * Version of the Copilot integration within the application, e.g. `"1.5.0"`. */ - extensionVersion?: string; + integrationVersion?: string; } export interface CopilotClientOptions { @@ -510,11 +510,11 @@ export interface CopilotClientOptions { enableRemoteSessions?: boolean; /** - * Identity of the integrating host, forwarded to the runtime on the + * Identity of the integrating application, forwarded to the runtime on the * `server.connect` handshake. Declaring it lets the telemetry the runtime * emits on this connection be attributed to a single, consistent surface - * (e.g. the host editor and its Copilot extension) instead of the runtime's - * own build. All fields are optional; omit it to keep the default + * (e.g. the application and its Copilot integration) instead of the + * runtime's own build. All fields are optional; omit it to keep the default * attribution. */ clientInfo?: CopilotClientInfo; diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index cef9cbf490..bd1cf9812f 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -4272,15 +4272,20 @@ describe("connect handshake clientInfo", () => { it("forwards a declared client identity on the connect handshake", async () => { const clientInfo = { - editorName: "JetBrains-IU", - editorVersion: "2026.1", - extensionName: "copilot-intellij", - extensionVersion: "1.5.0", + applicationName: "acme-developer-portal", + applicationVersion: "2.4.0", + integrationName: "copilot-assistant", + integrationVersion: "1.5.0", }; const params = await captureConnectParams({ clientInfo }); - expect(params.clientInfo).toEqual(clientInfo); + expect(params.clientInfo).toEqual({ + editorName: "acme-developer-portal", + editorVersion: "2.4.0", + extensionName: "copilot-assistant", + extensionVersion: "1.5.0", + }); }); it("omits clientInfo from the handshake when the host declares none", async () => { @@ -4292,28 +4297,28 @@ describe("connect handshake clientInfo", () => { it("drops empty fields and omits an all-empty identity", async () => { const allEmpty = await captureConnectParams({ clientInfo: { - editorName: "", - editorVersion: "", - extensionName: "", - extensionVersion: "", + applicationName: "", + applicationVersion: "", + integrationName: "", + integrationVersion: "", }, }); expect(allEmpty).not.toHaveProperty("clientInfo"); const partial = await captureConnectParams({ - clientInfo: { editorName: "vscode", editorVersion: "" }, + clientInfo: { applicationName: "example-app", applicationVersion: "" }, }); - expect(partial.clientInfo).toEqual({ editorName: "vscode" }); + expect(partial.clientInfo).toEqual({ editorName: "example-app" }); }); it("keeps telemetry forwarding alongside a declared identity", async () => { const params = await captureConnectParams({ - clientInfo: { editorName: "example-editor" }, + clientInfo: { applicationName: "example-app" }, onGitHubTelemetry: () => {}, }); expect(params).toMatchObject({ - clientInfo: { editorName: "example-editor" }, + clientInfo: { editorName: "example-app" }, enableGitHubTelemetryForwarding: true, }); }); diff --git a/python/copilot/client.py b/python/copilot/client.py index 4db29d1e78..245c187022 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -508,22 +508,22 @@ class TelemetryConfig(TypedDict, total=False): class ClientInfo(TypedDict, total=False): - """Identity of the integrating host, declared on the ``server.connect`` handshake. + """Identity of the integrating application, declared on ``server.connect``. Declaring it lets the telemetry the runtime emits on this connection be - attributed to a single, consistent surface (the host editor and its Copilot - extension) instead of the runtime's own build. All fields are optional; omit - any of them (or the whole object) to keep the runtime's default attribution. + attributed to a single, consistent surface (the application and its Copilot + integration) instead of the runtime's own build. All fields are optional; + omit any of them (or the whole object) to keep the default attribution. """ - editor_name: str - """Name of the host editor, e.g. ``"vscode"``.""" - editor_version: str - """Version of the host editor, e.g. ``"1.124.2"``.""" - extension_name: str - """Name of the Copilot extension within the host, e.g. ``"copilot-chat"``.""" - extension_version: str - """Version of the Copilot extension within the host, e.g. ``"0.54.0"``.""" + application_name: str + """Name of the application using the SDK, e.g. ``"acme-developer-portal"``.""" + application_version: str + """Version of the application using the SDK, e.g. ``"2.4.0"``.""" + integration_name: str + """Name of the Copilot integration, e.g. ``"copilot-assistant"``.""" + integration_version: str + """Version of the Copilot integration, e.g. ``"1.5.0"``.""" def _client_info_to_wire(client_info: ClientInfo | None) -> dict[str, str] | None: @@ -536,14 +536,14 @@ def _client_info_to_wire(client_info: ClientInfo | None) -> dict[str, str] | Non if not client_info: return None wire: dict[str, str] = {} - if client_info.get("editor_name"): - wire["editorName"] = client_info["editor_name"] - if client_info.get("editor_version"): - wire["editorVersion"] = client_info["editor_version"] - if client_info.get("extension_name"): - wire["extensionName"] = client_info["extension_name"] - if client_info.get("extension_version"): - wire["extensionVersion"] = client_info["extension_version"] + if client_info.get("application_name"): + wire["editorName"] = client_info["application_name"] + if client_info.get("application_version"): + wire["editorVersion"] = client_info["application_version"] + if client_info.get("integration_name"): + wire["extensionName"] = client_info["integration_name"] + if client_info.get("integration_version"): + wire["extensionVersion"] = client_info["integration_version"] return wire or None @@ -1621,7 +1621,7 @@ def __init__( Control integration). When ``True``, sessions in a GitHub repository working directory are accessible from GitHub web and mobile. - client_info: Identity of the integrating host, forwarded to the + client_info: Identity of the integrating application, forwarded to the runtime on the ``server.connect`` handshake. Declaring it lets the telemetry the runtime emits on this connection be attributed to a consistent surface instead of the runtime's own build. All @@ -4102,7 +4102,7 @@ async def _verify_protocol_version(self) -> None: # event is forwarded). Also sent on session.create/resume for older CLIs. if self._on_github_telemetry is not None: connect_params["enableGitHubTelemetryForwarding"] = True - # Declare the integrating host's identity so the runtime attributes + # Declare the integrating application's identity so the runtime attributes # the telemetry it emits on this connection to a consistent surface # instead of its own build. Omitted when the app didn't supply it. client_info = _client_info_to_wire(self._options.client_info) diff --git a/python/test_client.py b/python/test_client.py index 197103dd68..bf8de113ae 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -3156,10 +3156,10 @@ async def test_connect_forwards_client_info(self): client = CopilotClient( connection=RuntimeConnection.for_stdio(path=CLI_PATH), client_info={ - "editor_name": "JetBrains-IU", - "editor_version": "2026.1", - "extension_name": "copilot-intellij", - "extension_version": "1.5.0", + "application_name": "acme-developer-portal", + "application_version": "2.4.0", + "integration_name": "copilot-assistant", + "integration_version": "1.5.0", }, ) captured = {} @@ -3172,9 +3172,9 @@ async def request(self, method, params, **kwargs): client._client = _FakeClient() await client._verify_protocol_version() assert captured["connect"]["clientInfo"] == { - "editorName": "JetBrains-IU", - "editorVersion": "2026.1", - "extensionName": "copilot-intellij", + "editorName": "acme-developer-portal", + "editorVersion": "2.4.0", + "extensionName": "copilot-assistant", "extensionVersion": "1.5.0", } @@ -3196,7 +3196,7 @@ async def request(self, method, params, **kwargs): async def test_connect_forwards_partial_client_info_with_forwarding(self): client = CopilotClient( connection=RuntimeConnection.for_stdio(path=CLI_PATH), - client_info={"editor_name": "example-editor"}, + client_info={"application_name": "example-app"}, on_github_telemetry=lambda _notification: None, ) captured = {} @@ -3208,14 +3208,14 @@ async def request(self, method, params, **kwargs): client._client = _FakeClient() await client._verify_protocol_version() - assert captured["connect"]["clientInfo"] == {"editorName": "example-editor"} + assert captured["connect"]["clientInfo"] == {"editorName": "example-app"} assert captured["connect"]["enableGitHubTelemetryForwarding"] is True @pytest.mark.asyncio async def test_connect_drops_empty_client_info_fields(self): client = CopilotClient( connection=RuntimeConnection.for_stdio(path=CLI_PATH), - client_info={"editor_name": "vscode", "editor_version": ""}, + client_info={"application_name": "example-app", "application_version": ""}, ) captured = {} @@ -3226,17 +3226,17 @@ async def request(self, method, params, **kwargs): client._client = _FakeClient() await client._verify_protocol_version() - assert captured["connect"]["clientInfo"] == {"editorName": "vscode"} + assert captured["connect"]["clientInfo"] == {"editorName": "example-app"} @pytest.mark.asyncio async def test_connect_omits_all_empty_client_info(self): client = CopilotClient( connection=RuntimeConnection.for_stdio(path=CLI_PATH), client_info={ - "editor_name": "", - "editor_version": "", - "extension_name": "", - "extension_version": "", + "application_name": "", + "application_version": "", + "integration_name": "", + "integration_version": "", }, ) captured = {} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 9734417e20..b6b1598cf0 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -401,16 +401,16 @@ pub struct ClientOptions { /// (the default) or are stripped to a minimal/safe baseline. See /// [`ClientMode`] for the contract and trade-offs. pub mode: ClientMode, - /// Declares the integrating host's identity, forwarded to the runtime on + /// Declares the integrating application's identity, forwarded to the runtime on /// the `server.connect` handshake. Declaring it lets the telemetry the /// runtime emits on this connection be attributed to a consistent surface - /// (the host editor and its Copilot extension) instead of the runtime's own + /// (the application and its Copilot integration) instead of the runtime's own /// build. All fields are optional; leave it `None` to keep the runtime's /// default attribution. pub client_info: Option, } -/// Identity of the integrating host, declared on the `server.connect` +/// Identity of the integrating application, declared on the `server.connect` /// handshake. /// /// Declaring it lets the telemetry the runtime emits on the connection be @@ -424,14 +424,14 @@ pub struct ClientOptions { #[derive(Debug, Clone, Default, PartialEq, Eq)] #[non_exhaustive] pub struct ClientInfo { - /// Name of the host editor, e.g. `"vscode"`. - pub editor_name: Option, - /// Version of the host editor, e.g. `"1.124.2"`. - pub editor_version: Option, - /// Name of the Copilot extension within the host, e.g. `"copilot-chat"`. - pub extension_name: Option, - /// Version of the Copilot extension within the host, e.g. `"0.54.0"`. - pub extension_version: Option, + /// Name of the application using the SDK. + pub application_name: Option, + /// Version of the application using the SDK. + pub application_version: Option, + /// Name of the Copilot integration within the application. + pub integration_name: Option, + /// Version of the Copilot integration within the application. + pub integration_version: Option, } impl ClientInfo { @@ -441,27 +441,27 @@ impl ClientInfo { Self::default() } - /// Set the host editor name, e.g. `"vscode"`. - pub fn with_editor_name(mut self, editor_name: impl Into) -> Self { - self.editor_name = Some(editor_name.into()); + /// Set the name of the application using the SDK. + pub fn with_application_name(mut self, application_name: impl Into) -> Self { + self.application_name = Some(application_name.into()); self } - /// Set the host editor version, e.g. `"1.124.2"`. - pub fn with_editor_version(mut self, editor_version: impl Into) -> Self { - self.editor_version = Some(editor_version.into()); + /// Set the version of the application using the SDK. + pub fn with_application_version(mut self, application_version: impl Into) -> Self { + self.application_version = Some(application_version.into()); self } - /// Set the Copilot extension name within the host, e.g. `"copilot-chat"`. - pub fn with_extension_name(mut self, extension_name: impl Into) -> Self { - self.extension_name = Some(extension_name.into()); + /// Set the name of the Copilot integration within the application. + pub fn with_integration_name(mut self, integration_name: impl Into) -> Self { + self.integration_name = Some(integration_name.into()); self } - /// Set the Copilot extension version within the host, e.g. `"0.54.0"`. - pub fn with_extension_version(mut self, extension_version: impl Into) -> Self { - self.extension_version = Some(extension_version.into()); + /// Set the version of the Copilot integration within the application. + pub fn with_integration_version(mut self, integration_version: impl Into) -> Self { + self.integration_version = Some(integration_version.into()); self } @@ -469,10 +469,10 @@ impl ClientInfo { /// SDK omits `clientInfo` from the handshake and the runtime keeps its /// default attribution. fn is_empty(&self) -> bool { - Self::non_empty(&self.editor_name).is_none() - && Self::non_empty(&self.editor_version).is_none() - && Self::non_empty(&self.extension_name).is_none() - && Self::non_empty(&self.extension_version).is_none() + Self::non_empty(&self.application_name).is_none() + && Self::non_empty(&self.application_version).is_none() + && Self::non_empty(&self.integration_name).is_none() + && Self::non_empty(&self.integration_version).is_none() } /// Clone the field only when it holds a non-empty string, so empty fields are @@ -488,10 +488,10 @@ impl ClientInfo { return None; } Some(crate::generated::api_types::ConnectClientInfo { - editor_name: Self::non_empty(&self.editor_name), - editor_version: Self::non_empty(&self.editor_version), - extension_name: Self::non_empty(&self.extension_name), - extension_version: Self::non_empty(&self.extension_version), + editor_name: Self::non_empty(&self.application_name), + editor_version: Self::non_empty(&self.application_version), + extension_name: Self::non_empty(&self.integration_name), + extension_version: Self::non_empty(&self.integration_version), }) } } @@ -1027,7 +1027,7 @@ impl ClientOptions { self } - /// Declare the integrating host's identity, forwarded to the runtime on + /// Declare the integrating application's identity, forwarded to the runtime on /// the `server.connect` handshake so its telemetry is attributed to a /// consistent surface. See [`Self::client_info`]. pub fn with_client_info(mut self, client_info: ClientInfo) -> Self { @@ -1203,7 +1203,7 @@ struct ClientInner { /// `None` for stdio and for external-server transport without an /// explicit token. effective_connection_token: Option, - /// Host identity forwarded on the `connect` handshake, set from + /// Application identity forwarded on the `connect` handshake, set from /// [`ClientOptions::client_info`]. `None` keeps the runtime's default /// telemetry attribution. client_info: Option, @@ -1822,7 +1822,7 @@ impl Client { /// Construct a [`Client`] from raw streams with a preset /// [`ClientInfo`], for integration testing the `connect` handshake's - /// host-identity forwarding path. + /// application-identity forwarding path. #[doc(hidden)] #[cfg(any(test, feature = "test-support"))] pub fn from_streams_with_client_info( @@ -2474,7 +2474,7 @@ impl Client { .on_github_telemetry .is_some() .then_some(true), - // Declare the integrating host's identity so the runtime attributes + // Declare the integrating application's identity so the runtime attributes // the telemetry it emits on this connection to a consistent surface // instead of its own build. `None` when the app didn't supply it, and // empty fields are dropped. diff --git a/rust/tests/protocol_version_test.rs b/rust/tests/protocol_version_test.rs index 33b6c8d155..cd8563f87b 100644 --- a/rust/tests/protocol_version_test.rs +++ b/rust/tests/protocol_version_test.rs @@ -240,10 +240,10 @@ async fn connect_handshake_forwards_auto_generated_token() { .unwrap(); } -/// Positive coverage for host-identity forwarding on the `connect` +/// Positive coverage for application-identity forwarding on the `connect` /// handshake. A client constructed with a [`ClientInfo`] MUST serialize it /// (camelCase) into the outbound `connect` request's `clientInfo` param so -/// the runtime attributes this connection's telemetry to the host surface. +/// the runtime attributes this connection's telemetry to the application. #[tokio::test] async fn connect_handshake_forwards_client_info() { let (client_write, server_read) = duplex(8192); @@ -254,10 +254,10 @@ async fn connect_handshake_forwards_client_info() { std::env::temp_dir(), Some( github_copilot_sdk::ClientInfo::new() - .with_editor_name("JetBrains-IU") - .with_editor_version("2026.1") - .with_extension_name("copilot-intellij") - .with_extension_version("1.5.0"), + .with_application_name("acme-developer-portal") + .with_application_version("2.4.0") + .with_integration_name("copilot-assistant") + .with_integration_version("1.5.0"), ), ) .unwrap(); @@ -273,9 +273,9 @@ async fn connect_handshake_forwards_client_info() { let req = read_framed(&mut server_read).await; assert_eq!(req["method"], "connect"); let client_info = &req["params"]["clientInfo"]; - assert_eq!(client_info["editorName"], "JetBrains-IU"); - assert_eq!(client_info["editorVersion"], "2026.1"); - assert_eq!(client_info["extensionName"], "copilot-intellij"); + assert_eq!(client_info["editorName"], "acme-developer-portal"); + assert_eq!(client_info["editorVersion"], "2.4.0"); + assert_eq!(client_info["extensionName"], "copilot-assistant"); assert_eq!(client_info["extensionVersion"], "1.5.0"); let response = serde_json::json!({ @@ -305,8 +305,8 @@ async fn connect_handshake_omits_empty_client_info_fields() { std::env::temp_dir(), Some( github_copilot_sdk::ClientInfo::new() - .with_editor_name("example-editor") - .with_editor_version(""), + .with_application_name("example-app") + .with_application_version(""), ), ) .unwrap(); @@ -322,7 +322,7 @@ async fn connect_handshake_omits_empty_client_info_fields() { let req = read_framed(&mut server_read).await; assert_eq!(req["method"], "connect"); let client_info = &req["params"]["clientInfo"]; - assert_eq!(client_info["editorName"], "example-editor"); + assert_eq!(client_info["editorName"], "example-app"); assert!(client_info.get("editorVersion").is_none()); assert!(client_info.get("extensionName").is_none()); assert!(client_info.get("extensionVersion").is_none()); @@ -353,10 +353,10 @@ async fn connect_handshake_omits_all_empty_client_info() { std::env::temp_dir(), Some( github_copilot_sdk::ClientInfo::new() - .with_editor_name("") - .with_editor_version("") - .with_extension_name("") - .with_extension_version(""), + .with_application_name("") + .with_application_version("") + .with_integration_name("") + .with_integration_version(""), ), ) .unwrap(); From 721b14952537ec08a0db71a7f146ef118de4e1df Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 2 Sep 2026 11:44:46 +0000 Subject: [PATCH 2/5] Apply Java source formatting Match the repository's Spotless line wrapping for the updated client identity documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/main/java/com/github/copilot/rpc/ClientInfo.java | 9 ++++----- .../com/github/copilot/rpc/CopilotClientOptions.java | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java b/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java index 23aaf4ac05..f25144aa68 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java @@ -9,8 +9,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * Identity of the integrating application, declared on the {@code server.connect} - * handshake. + * Identity of the integrating application, declared on the + * {@code server.connect} handshake. *

* Declaring it lets the telemetry the runtime emits on the connection be * attributed to a single, consistent surface (the application and its Copilot @@ -20,9 +20,8 @@ *

Example Usage

* *
{@code
- * var options = new CopilotClientOptions().setClientInfo(new ClientInfo()
- * 		.setApplicationName("acme-developer-portal").setApplicationVersion("2.4.0")
- * 		.setIntegrationName("copilot-assistant").setIntegrationVersion("1.5.0"));
+ * var options = new CopilotClientOptions().setClientInfo(new ClientInfo().setApplicationName("acme-developer-portal")
+ * 		.setApplicationVersion("2.4.0").setIntegrationName("copilot-assistant").setIntegrationVersion("1.5.0"));
  * }
* * @see CopilotClientOptions#setClientInfo(ClientInfo) diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java index 6075554546..c67947bf48 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java @@ -690,8 +690,8 @@ public ClientInfo getClientInfo() { } /** - * Declares the integrating application's identity, forwarded to the runtime on the - * {@code server.connect} handshake. + * Declares the integrating application's identity, forwarded to the runtime on + * the {@code server.connect} handshake. *

* Declaring it lets the telemetry the runtime emits on this connection be * attributed to a consistent surface (the application and its Copilot From edae75ea1c49f133efda3a78d078028a336611a8 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 2 Sep 2026 11:52:26 +0000 Subject: [PATCH 3/5] Clarify client info examples Keep the IDE example across languages, document application-only usage, and explain integrations as optional extensions, plugins, or other application sub-parts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/features/client-info.md | 92 ++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/docs/features/client-info.md b/docs/features/client-info.md index 1919f7299c..f72780e41b 100644 --- a/docs/features/client-info.md +++ b/docs/features/client-info.md @@ -1,6 +1,6 @@ # Client info -Client info identifies the application using the Copilot SDK and, when applicable, the Copilot integration within it. Set the optional `clientInfo` client option to attribute runtime telemetry for that connection to your application instead of the runtime's own build. +Client info identifies the application using the Copilot SDK and, when applicable, a specific integration within it. An integration is an identifiable sub-part of the application through which the SDK is used, such as an extension or plugin. Set the optional `clientInfo` client option to attribute runtime telemetry for that connection to your application instead of the runtime's own build. ## When to set client info @@ -12,10 +12,12 @@ Client info has four optional string fields. Set the fields you know and omit th | Field | Example | Meaning | |---|---|---| -| `applicationName` | `"acme-developer-portal"` | Name of the application using the SDK | -| `applicationVersion` | `"2.4.0"` | Version of the application using the SDK | -| `integrationName` | `"copilot-assistant"` | Name of the Copilot integration within the application | -| `integrationVersion` | `"1.5.0"` | Version of the Copilot integration within the application | +| `applicationName` | `"vscode"` | Name of the application using the SDK | +| `applicationVersion` | `"1.124.2"` | Version of the application using the SDK | +| `integrationName` | `"copilot-chat"` | Name of the extension, plugin, or other application sub-part using the SDK | +| `integrationVersion` | `"0.54.0"` | Version of that extension, plugin, or application sub-part | + +For a standalone application without a distinct integration, set only the application fields. For example, a developer portal could set `applicationName` to `"acme-developer-portal"` and `applicationVersion` to `"2.4.0"`, leaving both integration fields unset. The SDK sends client info once when it establishes the connection. The identity applies for the lifetime of that connection. @@ -33,10 +35,10 @@ import { CopilotClient } from "@github/copilot-sdk"; async function main() { const client = new CopilotClient({ clientInfo: { - applicationName: "acme-developer-portal", - applicationVersion: "2.4.0", - integrationName: "copilot-assistant", - integrationVersion: "1.5.0", + applicationName: "vscode", + applicationVersion: "1.124.2", + integrationName: "copilot-chat", + integrationVersion: "0.54.0", }, }); @@ -52,10 +54,10 @@ import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient({ clientInfo: { - applicationName: "acme-developer-portal", - applicationVersion: "2.4.0", - integrationName: "copilot-assistant", - integrationVersion: "1.5.0", + applicationName: "vscode", + applicationVersion: "1.124.2", + integrationName: "copilot-chat", + integrationVersion: "0.54.0", }, }); @@ -73,10 +75,10 @@ from copilot import CopilotClient client = CopilotClient( client_info={ - "application_name": "acme-developer-portal", - "application_version": "2.4.0", - "integration_name": "copilot-assistant", - "integration_version": "1.5.0", + "application_name": "vscode", + "application_version": "1.124.2", + "integration_name": "copilot-chat", + "integration_version": "0.54.0", }, ) await client.start() @@ -101,10 +103,10 @@ func main() { ctx := context.Background() client := copilot.NewClient(&copilot.ClientOptions{ ClientInfo: &copilot.ClientInfo{ - ApplicationName: "acme-developer-portal", - ApplicationVersion: "2.4.0", - IntegrationName: "copilot-assistant", - IntegrationVersion: "1.5.0", + ApplicationName: "vscode", + ApplicationVersion: "1.124.2", + IntegrationName: "copilot-chat", + IntegrationVersion: "0.54.0", }, }) if err := client.Start(ctx); err != nil { @@ -117,10 +119,10 @@ func main() { ```go client := copilot.NewClient(&copilot.ClientOptions{ ClientInfo: &copilot.ClientInfo{ - ApplicationName: "acme-developer-portal", - ApplicationVersion: "2.4.0", - IntegrationName: "copilot-assistant", - IntegrationVersion: "1.5.0", + ApplicationName: "vscode", + ApplicationVersion: "1.124.2", + IntegrationName: "copilot-chat", + IntegrationVersion: "0.54.0", }, }) if err := client.Start(ctx); err != nil { @@ -140,10 +142,10 @@ await using var client = new CopilotClient(new CopilotClientOptions { ClientInfo = new CopilotClientInfo { - ApplicationName = "acme-developer-portal", - ApplicationVersion = "2.4.0", - IntegrationName = "copilot-assistant", - IntegrationVersion = "1.5.0", + ApplicationName = "vscode", + ApplicationVersion = "1.124.2", + IntegrationName = "copilot-chat", + IntegrationVersion = "0.54.0", }, }); @@ -165,10 +167,10 @@ public class ClientInfoExample { public static void main(String[] args) throws Exception { var options = new CopilotClientOptions() .setClientInfo(new ClientInfo() - .setApplicationName("acme-developer-portal") - .setApplicationVersion("2.4.0") - .setIntegrationName("copilot-assistant") - .setIntegrationVersion("1.5.0")); + .setApplicationName("vscode") + .setApplicationVersion("1.124.2") + .setIntegrationName("copilot-chat") + .setIntegrationVersion("0.54.0")); var client = new CopilotClient(options); client.start().get(); @@ -180,10 +182,10 @@ public class ClientInfoExample { ```java var options = new CopilotClientOptions() .setClientInfo(new ClientInfo() - .setApplicationName("acme-developer-portal") - .setApplicationVersion("2.4.0") - .setIntegrationName("copilot-assistant") - .setIntegrationVersion("1.5.0")); + .setApplicationName("vscode") + .setApplicationVersion("1.124.2") + .setIntegrationName("copilot-chat") + .setIntegrationVersion("0.54.0")); var client = new CopilotClient(options); client.start().get(); @@ -203,10 +205,10 @@ async fn main() -> Result<(), Box> { let _client = Client::start( ClientOptions::new().with_client_info( ClientInfo::new() - .with_application_name("acme-developer-portal") - .with_application_version("2.4.0") - .with_integration_name("copilot-assistant") - .with_integration_version("1.5.0"), + .with_application_name("vscode") + .with_application_version("1.124.2") + .with_integration_name("copilot-chat") + .with_integration_version("0.54.0"), ), ) .await?; @@ -221,10 +223,10 @@ use github_copilot_sdk::{Client, ClientInfo, ClientOptions}; let client = Client::start( ClientOptions::new().with_client_info( ClientInfo::new() - .with_application_name("acme-developer-portal") - .with_application_version("2.4.0") - .with_integration_name("copilot-assistant") - .with_integration_version("1.5.0"), + .with_application_name("vscode") + .with_application_version("1.124.2") + .with_integration_name("copilot-chat") + .with_integration_version("0.54.0"), ), ) .await?; From 648f9e8bf7c909be7f13ee5be28946a3bb4a7afa Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 2 Sep 2026 11:57:00 +0000 Subject: [PATCH 4/5] Clarify integration field documentation Describe integration identity consistently across all six SDKs as an optional application extension, plugin, or other named sub-part. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Types.cs | 4 ++-- go/types.go | 5 +++-- .../main/java/com/github/copilot/rpc/ClientInfo.java | 10 ++++++---- nodejs/src/types.ts | 4 ++-- python/copilot/client.py | 4 ++-- rust/src/lib.rs | 11 +++++++---- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 9d80bb6be0..3af7ea7f12 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -564,12 +564,12 @@ public sealed class CopilotClientInfo public string? ApplicationVersion { get; set; } ///

- /// Name of the Copilot integration within the application. + /// Optionally specifies a named integration within the application, such as an extension or plugin. /// public string? IntegrationName { get; set; } /// - /// Version of the Copilot integration within the application. + /// Optionally specifies the version of that integration. /// public string? IntegrationVersion { get; set; } } diff --git a/go/types.go b/go/types.go index 993a1ef970..772202a0bc 100644 --- a/go/types.go +++ b/go/types.go @@ -221,9 +221,10 @@ type ClientInfo struct { ApplicationName string // ApplicationVersion is the version of the application using the SDK. ApplicationVersion string - // IntegrationName is the name of the Copilot integration within the application. + // IntegrationName optionally identifies a specific integration within the + // application, such as an extension or plugin. IntegrationName string - // IntegrationVersion is the version of the Copilot integration within the application. + // IntegrationVersion is the optional version of the named integration. IntegrationVersion string } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java b/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java index f25144aa68..8706495b28 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java @@ -85,7 +85,8 @@ public ClientInfo setApplicationVersion(String applicationVersion) { } /** - * Gets the name of the Copilot integration within the application. + * Gets the optional name of a specific integration within the application, + * such as an extension or plugin. * * @return the integration name, or {@code null} */ @@ -95,7 +96,8 @@ public String getIntegrationName() { } /** - * Sets the name of the Copilot integration within the application. + * Sets the optional name of a specific integration within the application, + * such as an extension or plugin. * * @param integrationName * the integration name @@ -108,7 +110,7 @@ public ClientInfo setIntegrationName(String integrationName) { } /** - * Gets the version of the Copilot integration within the application. + * Gets the optional version of the named integration. * * @return the integration version, or {@code null} */ @@ -118,7 +120,7 @@ public String getIntegrationVersion() { } /** - * Sets the version of the Copilot integration within the application. + * Sets the optional version of the named integration. * * @param integrationVersion * the integration version diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 752a8ed9fd..128ced3d68 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -328,12 +328,12 @@ export interface CopilotClientInfo { applicationVersion?: string; /** - * Name of the Copilot integration within the application, e.g. `"copilot-assistant"`. + * Optional name of a specific integration within the application, such as an extension or plugin. */ integrationName?: string; /** - * Version of the Copilot integration within the application, e.g. `"1.5.0"`. + * Optional version of the integration identified by `integrationName`. */ integrationVersion?: string; } diff --git a/python/copilot/client.py b/python/copilot/client.py index 245c187022..929194c9b7 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -521,9 +521,9 @@ class ClientInfo(TypedDict, total=False): application_version: str """Version of the application using the SDK, e.g. ``"2.4.0"``.""" integration_name: str - """Name of the Copilot integration, e.g. ``"copilot-assistant"``.""" + """Optional name of an application integration, such as an extension or plugin.""" integration_version: str - """Version of the Copilot integration, e.g. ``"1.5.0"``.""" + """Optional version of the integration named by ``integration_name``.""" def _client_info_to_wire(client_info: ClientInfo | None) -> dict[str, str] | None: diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b6b1598cf0..fd7f12cf14 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -428,9 +428,10 @@ pub struct ClientInfo { pub application_name: Option, /// Version of the application using the SDK. pub application_version: Option, - /// Name of the Copilot integration within the application. + /// Optional name of a specific integration within the application, such as an + /// extension or plugin. pub integration_name: Option, - /// Version of the Copilot integration within the application. + /// Optional version of the integration identified by [`Self::integration_name`]. pub integration_version: Option, } @@ -453,13 +454,15 @@ impl ClientInfo { self } - /// Set the name of the Copilot integration within the application. + /// Set the name of a specific integration within the application, such as an + /// extension or plugin. pub fn with_integration_name(mut self, integration_name: impl Into) -> Self { self.integration_name = Some(integration_name.into()); self } - /// Set the version of the Copilot integration within the application. + /// Set the version of the integration identified by + /// [`Self::with_integration_name`]. pub fn with_integration_version(mut self, integration_version: impl Into) -> Self { self.integration_version = Some(integration_version.into()); self From eb05054f9b9846671638fcbc0852415339d5dffb Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 2 Sep 2026 12:47:47 +0000 Subject: [PATCH 5/5] Apply Java comment formatting Match Spotless wrapping for the clarified integration accessor documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/main/java/com/github/copilot/rpc/ClientInfo.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java b/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java index 8706495b28..ee9d97e1e9 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ClientInfo.java @@ -85,8 +85,8 @@ public ClientInfo setApplicationVersion(String applicationVersion) { } /** - * Gets the optional name of a specific integration within the application, - * such as an extension or plugin. + * Gets the optional name of a specific integration within the application, such + * as an extension or plugin. * * @return the integration name, or {@code null} */ @@ -96,8 +96,8 @@ public String getIntegrationName() { } /** - * Sets the optional name of a specific integration within the application, - * such as an extension or plugin. + * Sets the optional name of a specific integration within the application, such + * as an extension or plugin. * * @param integrationName * the integration name