diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 6041fe239..72408e5c2 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1802,7 +1802,17 @@ private async Task VerifyProtocolVersionAsync(Connection connection, Cancellatio _ => null, }; var connectResponse = await InvokeRpcAsync( - connection.Rpc, "connect", [new ConnectRequest { Token = token }], connection.StderrBuffer, cancellationToken); + connection.Rpc, + "connect", + [new ConnectHandshakeRequest( + token, + // Opt in to GitHub telemetry forwarding at the connection level when a + // handler is registered (mirrors the runtime, which reads this flag on the + // `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)], + connection.StderrBuffer, + cancellationToken); serverVersion = (int)connectResponse.ProtocolVersion; } catch (IOException ex) when (ex.InnerException is RemoteRpcException remoteEx && IsUnsupportedConnectMethod(remoteEx)) @@ -2639,6 +2649,10 @@ internal record GetSessionMetadataRequest( internal record GetSessionMetadataResponse( SessionMetadata? Session); + internal record ConnectHandshakeRequest( + string? Token, + [property: JsonPropertyName("enableGitHubTelemetryForwarding")] bool? EnableGitHubTelemetryForwarding = null); + internal record SetForegroundSessionRequest( string SessionId); @@ -2673,6 +2687,7 @@ internal record HooksInvokeResponse( [JsonSerializable(typeof(ListSessionsResponse))] [JsonSerializable(typeof(GetSessionMetadataRequest))] [JsonSerializable(typeof(GetSessionMetadataResponse))] + [JsonSerializable(typeof(ConnectHandshakeRequest))] [JsonSerializable(typeof(McpOAuthTokenStorageMode))] [JsonSerializable(typeof(EmbeddingCacheStorageMode))] [JsonSerializable(typeof(ModelCapabilitiesOverride))] diff --git a/dotnet/test/Unit/GitHubTelemetryTests.cs b/dotnet/test/Unit/GitHubTelemetryTests.cs index 4a41c1cb8..f82e0db6e 100644 --- a/dotnet/test/Unit/GitHubTelemetryTests.cs +++ b/dotnet/test/Unit/GitHubTelemetryTests.cs @@ -53,6 +53,39 @@ public async Task ResumeSession_Opts_Into_Forwarding_When_Handler_Provided() Assert.True(flag.GetBoolean()); } + [Fact] + public async Task Connect_Opts_Into_Forwarding_When_Handler_Provided() + { + await using var server = await FakeTelemetryServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + OnGitHubTelemetry = _ => Task.CompletedTask, + }); + await client.StartAsync(); + + var connectParams = server.LastConnectParams ?? throw new InvalidOperationException("connect was not captured."); + Assert.True(connectParams.TryGetProperty("enableGitHubTelemetryForwarding", out var flag)); + Assert.True(flag.GetBoolean()); + } + + [Fact] + public async Task Connect_Does_Not_Opt_In_Without_Handler() + { + await using var server = await FakeTelemetryServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + }); + await client.StartAsync(); + + var connectParams = server.LastConnectParams ?? throw new InvalidOperationException("connect was not captured."); + var present = connectParams.TryGetProperty("enableGitHubTelemetryForwarding", out var flag); + Assert.True( + !present || flag.ValueKind == JsonValueKind.Null, + "connect request should omit enableGitHubTelemetryForwarding (or send null) when no handler is registered"); + } + [Fact] public async Task CreateSession_Does_Not_Opt_In_Without_Handler() { @@ -187,6 +220,8 @@ public string Url public JsonElement? LastResumeParams { get; private set; } + public JsonElement? LastConnectParams { get; private set; } + public static Task StartAsync() { var listener = new TcpListener(IPAddress.Loopback, 0); @@ -267,12 +302,7 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel object? result = method switch { - "connect" => new Dictionary - { - ["ok"] = true, - ["protocolVersion"] = 3, - ["version"] = "test", - }, + "connect" => CaptureConnect(request), "session.create" => CaptureCreate(request), "session.resume" => CaptureResume(request), "session.send" => new Dictionary { ["messageId"] = "message-1" }, @@ -289,6 +319,17 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel }, cancellationToken); } + private Dictionary CaptureConnect(JsonElement request) + { + LastConnectParams = request.TryGetProperty("params", out var p) ? p.Clone() : null; + return new Dictionary + { + ["ok"] = true, + ["protocolVersion"] = 3, + ["version"] = "test", + }; + } + private Dictionary CaptureCreate(JsonElement request) { LastCreateParams = request.TryGetProperty("params", out var p) ? p.Clone() : null; diff --git a/go/client.go b/go/client.go index 1bbf615d7..5357f2858 100644 --- a/go/client.go +++ b/go/client.go @@ -1685,7 +1685,15 @@ func (c *Client) verifyProtocolVersion(ctx context.Context) error { t := c.effectiveConnectionToken tokenPtr = &t } - connectResult, err := c.internalRPC.Connect(ctx, &rpc.ConnectRequest{Token: tokenPtr}) + connectReq := &connectHandshakeRequest{Token: tokenPtr} + // Opt in to GitHub telemetry forwarding at the connection level when a handler is + // registered (mirrors the runtime, which reads this flag on the `connect` handshake + // so the first session's un-replayable `session.start` event is forwarded). Also + // sent on session.create/resume for older CLIs. + if c.options.OnGitHubTelemetry != nil { + connectReq.EnableGitHubTelemetryForwarding = Bool(true) + } + rawConnectResult, err := c.client.Request(ctx, "connect", connectReq) if err != nil { var rpcErr *jsonrpc2.Error if errors.As(err, &rpcErr) && (rpcErr.Code == jsonrpc2.ErrMethodNotFound.Code || rpcErr.Message == "Unhandled method connect") { @@ -1700,6 +1708,10 @@ func (c *Client) verifyProtocolVersion(ctx context.Context) error { return err } } else { + var connectResult rpc.ConnectResult + if err := json.Unmarshal(rawConnectResult, &connectResult); err != nil { + return err + } v := int(connectResult.ProtocolVersion) serverVersion = &v } @@ -1716,6 +1728,11 @@ func (c *Client) verifyProtocolVersion(ctx context.Context) error { return nil } +type connectHandshakeRequest struct { + Token *string `json:"token,omitempty"` + EnableGitHubTelemetryForwarding *bool `json:"enableGitHubTelemetryForwarding,omitempty"` +} + // stderrBufferSize is the maximum number of bytes kept from the CLI process's // stderr. Only the tail is retained so that memory stays bounded even when the // process produces a large amount of diagnostic output. diff --git a/go/client_test.go b/go/client_test.go index f7d5f50c6..fedcb68bf 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -2487,6 +2487,52 @@ func assertForwardingFlagAbsent(t *testing.T, params json.RawMessage) { } } +func TestClient_ForwardsGitHubTelemetryForwardingOnConnect(t *testing.T) { + rpcClient, server, _ := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + internalRPC: rpc.NewInternalServerRPC(rpcClient), + sessions: make(map[string]*Session), + options: ClientOptions{OnGitHubTelemetry: func(*rpc.GitHubTelemetryNotification) {}}, + } + + connectParams := make(chan json.RawMessage, 1) + server.SetRequestHandler("connect", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + connectParams <- append(json.RawMessage(nil), params...) + return []byte(`{"ok":true,"protocolVersion":3,"version":"test"}`), nil + }) + + if err := client.verifyProtocolVersion(t.Context()); err != nil { + t.Fatalf("verifyProtocolVersion failed: %v", err) + } + assertForwardingFlagTrue(t, <-connectParams) +} + +func TestClient_OmitsGitHubTelemetryForwardingOnConnectWhenNoHandler(t *testing.T) { + rpcClient, server, _ := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + internalRPC: rpc.NewInternalServerRPC(rpcClient), + sessions: make(map[string]*Session), + options: ClientOptions{}, + } + + connectParams := make(chan json.RawMessage, 1) + server.SetRequestHandler("connect", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + connectParams <- append(json.RawMessage(nil), params...) + return []byte(`{"ok":true,"protocolVersion":3,"version":"test"}`), nil + }) + + if err := client.verifyProtocolVersion(t.Context()); err != nil { + t.Fatalf("verifyProtocolVersion failed: %v", err) + } + assertForwardingFlagAbsent(t, <-connectParams) +} + func TestGitHubTelemetryNotificationRoutesToCallback(t *testing.T) { // The runtime forwards telemetry via a JSON-RPC *notification* (no id). // Drive a real Content-Length-framed notification through the transport and diff --git a/java/src/main/java/com/github/copilot/CopilotClient.java b/java/src/main/java/com/github/copilot/CopilotClient.java index 31a892914..b90ccd545 100644 --- a/java/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/src/main/java/com/github/copilot/CopilotClient.java @@ -26,7 +26,7 @@ import com.github.copilot.rpc.CreateSessionResponse; import com.github.copilot.generated.rpc.SessionOptionsUpdateParams; import com.github.copilot.generated.rpc.SessionInstalledPlugin; -import com.github.copilot.generated.rpc.ConnectParams; +import com.github.copilot.generated.rpc.ConnectResult; import com.github.copilot.generated.rpc.GitHubTelemetryNotification; import com.github.copilot.generated.rpc.ServerRpc; import com.github.copilot.generated.rpc.SessionEventLogRegisterInterestParams; @@ -306,11 +306,20 @@ private void verifyProtocolVersion(Connection connection) throws Exception { Integer serverVersion; try { - // Try the new 'connect' RPC which supports connection tokens - var connectParams = new ConnectParams(effectiveConnectionToken); - var connectResponse = connection.rpc - .invoke("connect", connectParams, com.github.copilot.generated.rpc.ConnectResult.class) - .get(30, TimeUnit.SECONDS); + // Try the new 'connect' RPC which supports connection tokens. + var connectParams = new HashMap(); + if (effectiveConnectionToken != null) { + connectParams.put("token", effectiveConnectionToken); + } + // Opt into GitHub telemetry forwarding at the connection level when a handler + // is registered, so the runtime can forward the first session's un-replayable + // start event. Also sent on session create/resume for backward compatibility + // with servers that read the flag there instead. + if (this.options.getOnGitHubTelemetry() != null) { + connectParams.put("enableGitHubTelemetryForwarding", true); + } + var connectResponse = connection.rpc.invoke("connect", connectParams, ConnectResult.class).get(30, + TimeUnit.SECONDS); serverVersion = connectResponse.protocolVersion() != null ? connectResponse.protocolVersion().intValue() : null; diff --git a/java/src/test/java/com/github/copilot/GitHubTelemetryTest.java b/java/src/test/java/com/github/copilot/GitHubTelemetryTest.java index ad950b823..8e35bd9a9 100644 --- a/java/src/test/java/com/github/copilot/GitHubTelemetryTest.java +++ b/java/src/test/java/com/github/copilot/GitHubTelemetryTest.java @@ -32,8 +32,9 @@ /** * Exercises the hand-written GitHub telemetry forwarding surface: the * {@code gitHubTelemetry.event} notification adapter, the - * {@code enableGitHubTelemetryForwarding} capability flag on the create/resume - * requests, and the {@code onGitHubTelemetry} client option. + * {@code enableGitHubTelemetryForwarding} capability flag on the connect + * handshake and the create/resume requests, and the {@code onGitHubTelemetry} + * client option. */ @AllowCopilotExperimental class GitHubTelemetryTest { @@ -146,6 +147,12 @@ void clientOptsSessionsIntoForwardingAndReceivesEvents() throws Exception { client.start().get(15, TimeUnit.SECONDS); + // Connecting must opt into telemetry forwarding at the connection level so + // the runtime can forward the first session's un-replayable start event. + JsonNode connectParams = server.awaitConnect(); + assertTrue(connectParams.path("enableGitHubTelemetryForwarding").asBoolean(), + "connect request should carry enableGitHubTelemetryForwarding=true"); + // Creating a session must opt it into telemetry forwarding. client.createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(15, TimeUnit.SECONDS); @@ -178,6 +185,10 @@ void clientOmitsForwardingWhenNoHandler() throws Exception { client.start().get(15, TimeUnit.SECONDS); + JsonNode connectParams = server.awaitConnect(); + assertFalse(connectParams.has("enableGitHubTelemetryForwarding"), + "connect request should omit the flag when no handler is registered"); + client.createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(15, TimeUnit.SECONDS); JsonNode createParams = server.awaitCreate(); @@ -214,6 +225,7 @@ private static final class FakeRuntimeServer implements AutoCloseable { private final ServerSocket serverSocket; private final Thread acceptThread; private final CompletableFuture ready = new CompletableFuture<>(); + private final CompletableFuture connectParams = new CompletableFuture<>(); private final CompletableFuture createParams = new CompletableFuture<>(); private final CompletableFuture resumeParams = new CompletableFuture<>(); @@ -228,6 +240,10 @@ String url() { return "127.0.0.1:" + serverSocket.getLocalPort(); } + JsonNode awaitConnect() throws Exception { + return connectParams.get(15, TimeUnit.SECONDS); + } + JsonNode awaitCreate() throws Exception { return createParams.get(15, TimeUnit.SECONDS); } @@ -244,8 +260,10 @@ private void acceptLoop() { try { Socket socket = serverSocket.accept(); JsonRpcClient server = JsonRpcClient.fromSocket(socket); - server.registerMethodHandler("connect", - (id, params) -> respond(server, id, Map.of("protocolVersion", 2))); + server.registerMethodHandler("connect", (id, params) -> { + connectParams.complete(params); + respond(server, id, Map.of("protocolVersion", 2)); + }); server.registerMethodHandler("session.create", (id, params) -> { createParams.complete(params); respond(server, id, Map.of("sessionId", params.path("sessionId").asText("created"), "workspacePath", @@ -261,6 +279,7 @@ private void acceptLoop() { ready.complete(server); } catch (IOException e) { ready.completeExceptionally(e); + connectParams.completeExceptionally(e); createParams.completeExceptionally(e); resumeParams.completeExceptionally(e); } diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 160a12d48..9f430600c 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1846,9 +1846,18 @@ export class CopilotClient { let serverVersion: number | undefined; try { - const result = await raceAgainstExit( - this.internalRpc.connect({ token: this.effectiveConnectionToken }) - ); + const connectParams: { + token?: string; + enableGitHubTelemetryForwarding?: boolean; + } = { token: this.effectiveConnectionToken }; + // Opt in to GitHub telemetry forwarding at the connection level when a + // handler is registered (mirrors the runtime, which reads this flag on the + // `connect` handshake so the first session's un-replayable `session.start` + // event is forwarded). Also sent on session.create/resume for older CLIs. + if (this.onGitHubTelemetry != null) { + connectParams.enableGitHubTelemetryForwarding = true; + } + const result = await raceAgainstExit(this.internalRpc.connect(connectParams)); serverVersion = result.protocolVersion; } catch (err) { if ( diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index b17449454..96c32a595 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -488,6 +488,40 @@ describe("CopilotClient", () => { expect(resumePayload.enableGitHubTelemetryForwarding).toBe(true); }); + it("opts into GitHub telemetry forwarding on the connect handshake when a handler is provided", async () => { + const client = new CopilotClient({ onGitHubTelemetry: () => {} }); + onTestFinished(() => client.forceStop()); + + const sendRequest = vi.fn(async (method: string) => { + if (method === "connect") return { ok: true, protocolVersion: 3, version: "test" }; + throw new Error(`Unexpected method: ${method}`); + }); + (client as any).connection = { sendRequest }; + + await (client as any).verifyProtocolVersion(); + + const connectCall = sendRequest.mock.calls.find(([method]) => method === "connect"); + expect(connectCall).toBeDefined(); + expect((connectCall![1] as any).enableGitHubTelemetryForwarding).toBe(true); + }); + + it("does not opt into GitHub telemetry forwarding on the connect handshake without a handler", async () => { + const client = new CopilotClient(); + onTestFinished(() => client.forceStop()); + + const sendRequest = vi.fn(async (method: string) => { + if (method === "connect") return { ok: true, protocolVersion: 3, version: "test" }; + throw new Error(`Unexpected method: ${method}`); + }); + (client as any).connection = { sendRequest }; + + await (client as any).verifyProtocolVersion(); + + const connectCall = sendRequest.mock.calls.find(([method]) => method === "connect"); + expect(connectCall).toBeDefined(); + expect((connectCall![1] as any).enableGitHubTelemetryForwarding).toBeUndefined(); + }); + it("does not opt into GitHub telemetry forwarding without a handler", async () => { const client = new CopilotClient(); await client.start(); diff --git a/python/copilot/client.py b/python/copilot/client.py index 269aaf96c..55d01c5b5 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -70,8 +70,7 @@ OpenCanvasInstance, RemoteSessionMode, ServerRpc, - _ConnectRequest, - _InternalServerRpc, + _ConnectResult, from_datetime, register_client_global_api_handlers, register_client_session_api_handlers, @@ -3303,8 +3302,17 @@ async def _verify_protocol_version(self) -> None: server_version: int | None try: - connect_result = await _InternalServerRpc(self._client)._connect( - _ConnectRequest(token=self._effective_connection_token) + connect_params: dict[str, Any] = {} + if self._effective_connection_token is not None: + connect_params["token"] = self._effective_connection_token + # Opt in to GitHub telemetry forwarding at the connection level when a + # handler is registered (mirrors the runtime, which reads this flag on the + # `connect` handshake so the first session's un-replayable `session.start` + # event is forwarded). Also sent on session.create/resume for older CLIs. + if self._on_github_telemetry is not None: + connect_params["enableGitHubTelemetryForwarding"] = True + connect_result = _ConnectResult.from_dict( + await self._client.request("connect", connect_params) ) server_version = connect_result.protocol_version except JsonRpcError as err: diff --git a/python/test_client.py b/python/test_client.py index 13fc50e73..5e1b8be63 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -2381,6 +2381,37 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_connect_enables_forwarding_when_handler_registered(self): + client = CopilotClient( + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_github_telemetry=lambda _notification: None, + ) + captured = {} + + class _FakeClient: + async def request(self, method, params, **kwargs): + captured[method] = params + return {"ok": True, "protocolVersion": 3, "version": "test"} + + client._client = _FakeClient() + await client._verify_protocol_version() + assert captured["connect"]["enableGitHubTelemetryForwarding"] is True + + @pytest.mark.asyncio + async def test_connect_omits_forwarding_without_handler(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + captured = {} + + class _FakeClient: + async def request(self, method, params, **kwargs): + captured[method] = params + return {"ok": True, "protocolVersion": 3, "version": "test"} + + client._client = _FakeClient() + await client._verify_protocol_version() + assert "enableGitHubTelemetryForwarding" not in captured["connect"] + @pytest.mark.asyncio async def test_event_routes_to_handler(self): from copilot.generated.rpc import GitHubTelemetryNotification diff --git a/rust/src/lib.rs b/rust/src/lib.rs index c31e80dc5..8e8a5491c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1818,12 +1818,40 @@ impl Client { /// param. Server-side, the token is required when the server was /// started with `COPILOT_CONNECTION_TOKEN`. async fn connect_handshake(&self) -> Result> { - let result = self - .rpc() - .connect(crate::generated::api_types::ConnectRequest { - token: self.inner.effective_connection_token.clone(), - }) + // Built inline rather than via the generated `ConnectRequest` so we can + // carry the connection-level telemetry opt-in without hand-editing + // generated code. The runtime reads `enableGitHubTelemetryForwarding` on + // this handshake to forward `gitHubTelemetry.event` for the connection's + // lifetime, which lets the first session's un-replayable `session.start` + // event be forwarded. It is also sent on session.create/resume so older + // CLIs that only read it there still opt in. + #[derive(serde::Serialize)] + #[serde(rename_all = "camelCase")] + struct ConnectParams { + #[serde(skip_serializing_if = "Option::is_none")] + token: Option, + #[serde( + rename = "enableGitHubTelemetryForwarding", + skip_serializing_if = "Option::is_none" + )] + enable_github_telemetry_forwarding: Option, + } + + let params = ConnectParams { + token: self.inner.effective_connection_token.clone(), + enable_github_telemetry_forwarding: self + .inner + .on_github_telemetry + .is_some() + .then_some(true), + }; + let value = self + .call( + crate::generated::api_types::rpc_methods::CONNECT, + Some(serde_json::to_value(params)?), + ) .await?; + let result: crate::generated::api_types::ConnectResult = serde_json::from_value(value)?; Ok(u32::try_from(result.protocol_version).ok()) } diff --git a/rust/tests/session_test.rs b/rust/tests/session_test.rs index 08f8a7653..c9a196971 100644 --- a/rust/tests/session_test.rs +++ b/rust/tests/session_test.rs @@ -911,6 +911,59 @@ async fn resume_session_omits_github_telemetry_forwarding_without_callback() { timeout(TIMEOUT, resume_handle).await.unwrap().unwrap(); } +#[tokio::test] +async fn connect_sends_github_telemetry_forwarding_when_callback_registered() { + let callback: github_copilot_sdk::github_telemetry::GitHubTelemetryCallback = + Arc::new(|_notification| {}); + let (client, mut server_read, mut server_write) = make_client_with_telemetry(callback); + + let handle = tokio::spawn({ + let client = client.clone(); + async move { client.verify_protocol_version().await.unwrap() } + }); + + let request = read_framed(&mut server_read).await; + assert_eq!(request["method"], "connect"); + assert_eq!(request["params"]["enableGitHubTelemetryForwarding"], true); + + let id = request["id"].as_u64().unwrap(); + let response = serde_json::json!({ + "jsonrpc": "2.0", + "id": id, + "result": { "ok": true, "protocolVersion": 3, "version": "test" }, + }); + write_framed(&mut server_write, &serde_json::to_vec(&response).unwrap()).await; + timeout(TIMEOUT, handle).await.unwrap().unwrap(); +} + +#[tokio::test] +async fn connect_omits_github_telemetry_forwarding_without_callback() { + let (client, mut server_read, mut server_write) = make_client(); + + let handle = tokio::spawn({ + let client = client.clone(); + async move { client.verify_protocol_version().await.unwrap() } + }); + + let request = read_framed(&mut server_read).await; + assert_eq!(request["method"], "connect"); + assert!( + request["params"] + .get("enableGitHubTelemetryForwarding") + .is_none_or(Value::is_null), + "forwarding flag should be omitted when no callback is registered" + ); + + let id = request["id"].as_u64().unwrap(); + let response = serde_json::json!({ + "jsonrpc": "2.0", + "id": id, + "result": { "ok": true, "protocolVersion": 3, "version": "test" }, + }); + write_framed(&mut server_write, &serde_json::to_vec(&response).unwrap()).await; + timeout(TIMEOUT, handle).await.unwrap().unwrap(); +} + #[tokio::test] async fn github_telemetry_event_dispatches_to_callback() { use github_copilot_sdk::github_telemetry::GitHubTelemetryNotification;