Skip to content

Commit a4fdef7

Browse files
committed
Require session detach support
Remove capability negotiation and the legacy session.destroy fallback. This SDK change will ship only after the detach-capable runtime is released and incorporated. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 69723dd2-e732-43c6-9f9a-a16c2de3a628
1 parent 338d352 commit a4fdef7

3 files changed

Lines changed: 10 additions & 84 deletions

File tree

nodejs/src/client.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ import type { FactoryHandle } from "./factory.js";
9292
* Servers reporting a version below this are rejected.
9393
*/
9494
const MIN_PROTOCOL_VERSION = 3;
95-
const SESSION_DETACH_CAPABILITY = "session.detach";
9695
const RUNTIME_SHUTDOWN_TIMEOUT_MS = 10_000;
9796

9897
/**
@@ -521,7 +520,6 @@ export class CopilotClient {
521520
private _internalRpc: ReturnType<typeof createInternalServerRpc> | null = null;
522521
private processExitPromise: Promise<never> | null = null; // Rejects when CLI process exits
523522
private negotiatedProtocolVersion: number | null = null;
524-
private negotiatedCapabilities: Set<string> = new Set();
525523
/** Connection-level session filesystem config, set via constructor option. */
526524
private sessionFsConfig: SessionFsConfig | null = null;
527525
private requestHandler: CopilotRequestHandler | null = null;
@@ -1465,8 +1463,6 @@ export class CopilotClient {
14651463
{
14661464
mcpAuthHandler: config.onMcpAuthRequest,
14671465
managedSettingsEnabled: config.enableManagedSettings,
1468-
supportsSessionDetach:
1469-
this.negotiatedCapabilities.has(SESSION_DETACH_CAPABILITY),
14701466
onDisconnected: (disconnectedSession) => {
14711467
if (this.sessions.get(sessionId) === disconnectedSession) {
14721468
this.sessions.delete(sessionId);
@@ -1741,7 +1737,6 @@ export class CopilotClient {
17411737
{
17421738
mcpAuthHandler: config.onMcpAuthRequest,
17431739
managedSettingsEnabled: config.enableManagedSettings,
1744-
supportsSessionDetach: this.negotiatedCapabilities.has(SESSION_DETACH_CAPABILITY),
17451740
onDisconnected: (disconnectedSession) => {
17461741
if (this.sessions.get(sessionId) === disconnectedSession) {
17471742
this.sessions.delete(sessionId);
@@ -2077,7 +2072,6 @@ export class CopilotClient {
20772072
this.processExitPromise ? Promise.race([p, this.processExitPromise]) : p;
20782073

20792074
let serverVersion: number | undefined;
2080-
let serverCapabilities: string[] | undefined;
20812075
try {
20822076
const connectParams: {
20832077
token?: string;
@@ -2092,8 +2086,6 @@ export class CopilotClient {
20922086
}
20932087
const result = await raceAgainstExit(this.internalRpc.connect(connectParams));
20942088
serverVersion = result.protocolVersion;
2095-
serverCapabilities = (result as typeof result & { capabilities?: string[] })
2096-
.capabilities;
20972089
} catch (err) {
20982090
if (
20992091
err instanceof ResponseError &&
@@ -2121,9 +2113,7 @@ export class CopilotClient {
21212113
`Please update your SDK or server to ensure compatibility.`
21222114
);
21232115
}
2124-
21252116
this.negotiatedProtocolVersion = serverVersion;
2126-
this.negotiatedCapabilities = new Set(serverCapabilities ?? []);
21272117
}
21282118

21292119
/**

nodejs/src/session.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,6 @@ export class CopilotSession {
427427
private _capabilities: SessionCapabilities = {};
428428
private openCanvasInstances: OpenCanvasInstance[] = [];
429429
private disconnected = false;
430-
private readonly supportsSessionDetach: boolean;
431430
private readonly onDisconnected?: (session: CopilotSession) => void;
432431

433432
/** @internal Client session API handlers, populated by CopilotClient during create/resume. */
@@ -611,14 +610,12 @@ export class CopilotSession {
611610
options?: {
612611
mcpAuthHandler?: McpAuthHandler;
613612
managedSettingsEnabled?: boolean;
614-
supportsSessionDetach?: boolean;
615613
onDisconnected?: (session: CopilotSession) => void;
616614
}
617615
) {
618616
this.traceContextProvider = traceContextProvider;
619617
this.mcpAuthHandler = options?.mcpAuthHandler;
620618
this.managedSettingsEnabled = options?.managedSettingsEnabled === true;
621-
this.supportsSessionDetach = options?.supportsSessionDetach === true;
622619
this.onDisconnected = options?.onDisconnected;
623620
}
624621

@@ -1972,19 +1969,13 @@ export class CopilotSession {
19721969
if (this.disconnected) {
19731970
return;
19741971
}
1975-
if (this.supportsSessionDetach) {
1976-
const response = (await this.connection.sendRequest("session.detach", {
1977-
sessionId: this.sessionId,
1978-
})) as { success: boolean; error?: string };
1979-
if (!response.success) {
1980-
throw new Error(
1981-
`Failed to disconnect session ${this.sessionId}: ${response.error || "Unknown error"}`
1982-
);
1983-
}
1984-
} else {
1985-
await this.connection.sendRequest("session.destroy", {
1986-
sessionId: this.sessionId,
1987-
});
1972+
const response = (await this.connection.sendRequest("session.detach", {
1973+
sessionId: this.sessionId,
1974+
})) as { success: boolean; error?: string };
1975+
if (!response.success) {
1976+
throw new Error(
1977+
`Failed to disconnect session ${this.sessionId}: ${response.error || "Unknown error"}`
1978+
);
19881979
}
19891980
this._markDisconnected();
19901981
this.onDisconnected?.(this);

nodejs/test/client.test.ts

Lines changed: 3 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -876,36 +876,6 @@ describe("CopilotClient", () => {
876876
expect((connectCall![1] as any).enableGitHubTelemetryForwarding).toBeUndefined();
877877
});
878878

879-
it("uses session.detach when the server advertises the capability", async () => {
880-
const client = new CopilotClient();
881-
const sendRequest = vi.fn(async (method: string, params: any) => {
882-
if (method === "connect") {
883-
return {
884-
ok: true,
885-
protocolVersion: 3,
886-
version: "test",
887-
capabilities: ["session.detach"],
888-
};
889-
}
890-
if (method === "session.create") {
891-
return { sessionId: params.sessionId };
892-
}
893-
if (method === "session.detach") {
894-
return { success: true };
895-
}
896-
throw new Error(`Unexpected method: ${method}`);
897-
});
898-
(client as any).connection = { sendRequest };
899-
900-
await (client as any).verifyProtocolVersion();
901-
const session = await client.createSession({ onPermissionRequest: approveAll });
902-
await session.disconnect();
903-
904-
expect(sendRequest).toHaveBeenCalledWith("session.detach", {
905-
sessionId: session.sessionId,
906-
});
907-
});
908-
909879
it("does not opt into GitHub telemetry forwarding without a handler", async () => {
910880
const client = new CopilotClient();
911881
await client.start();
@@ -2191,7 +2161,6 @@ describe("CopilotClient", () => {
21912161
undefined,
21922162
undefined,
21932163
{
2194-
supportsSessionDetach: true,
21952164
onDisconnected,
21962165
}
21972166
);
@@ -2229,8 +2198,7 @@ describe("CopilotClient", () => {
22292198
"test-session",
22302199
{ sendRequest } as any,
22312200
undefined,
2232-
undefined,
2233-
{ supportsSessionDetach: true }
2201+
undefined
22342202
);
22352203

22362204
await expect(session.disconnect()).rejects.toThrow("detach failed");
@@ -2245,13 +2213,7 @@ describe("CopilotClient", () => {
22452213

22462214
it("detaches a session when asynchronously disposed", async () => {
22472215
const sendRequest = vi.fn(async () => ({ success: true }));
2248-
const session = new CopilotSession(
2249-
"test-session",
2250-
{ sendRequest } as any,
2251-
undefined,
2252-
undefined,
2253-
{ supportsSessionDetach: true }
2254-
);
2216+
const session = new CopilotSession("test-session", { sendRequest } as any, undefined);
22552217

22562218
await session[Symbol.asyncDispose]();
22572219

@@ -2260,17 +2222,6 @@ describe("CopilotClient", () => {
22602222
});
22612223
});
22622224

2263-
it("uses legacy destroy when the runtime does not advertise detach", async () => {
2264-
const sendRequest = vi.fn(async () => undefined);
2265-
const session = new CopilotSession("test-session", { sendRequest } as any, undefined);
2266-
2267-
await session.disconnect();
2268-
2269-
expect(sendRequest).toHaveBeenCalledWith("session.destroy", {
2270-
sessionId: "test-session",
2271-
});
2272-
});
2273-
22742225
it("removes a detached session from the client routing map", async () => {
22752226
const client = new CopilotClient();
22762227
const sendRequest = vi.fn(async (method: string, params: any) => {
@@ -2284,7 +2235,6 @@ describe("CopilotClient", () => {
22842235
});
22852236
(client as any).connection = { sendRequest };
22862237
(client as any).state = "connected";
2287-
(client as any).negotiatedCapabilities = new Set(["session.detach"]);
22882238

22892239
const session = await client.createSession({
22902240
onPermissionRequest: approveAll,
@@ -2316,7 +2266,6 @@ describe("CopilotClient", () => {
23162266
});
23172267
(client as any).connection = { sendRequest };
23182268
(client as any).state = "connected";
2319-
(client as any).negotiatedCapabilities = new Set(["session.detach"]);
23202269

23212270
await expect(
23222271
client.createSession({
@@ -2419,7 +2368,6 @@ describe("CopilotClient", () => {
24192368
});
24202369
(client as any).connection = { sendRequest };
24212370
(client as any).state = "connected";
2422-
(client as any).negotiatedCapabilities = new Set(["session.detach"]);
24232371

24242372
await expect(
24252373
client.resumeSession("test-session", {
@@ -2454,7 +2402,6 @@ describe("CopilotClient", () => {
24542402
});
24552403
(client as any).connection = { sendRequest };
24562404
(client as any).state = "connected";
2457-
(client as any).negotiatedCapabilities = new Set(["session.detach"]);
24582405

24592406
await expect(
24602407
client.resumeSession("test-session", {
@@ -3935,9 +3882,7 @@ describe("CopilotClient", () => {
39353882
const resumed = new CopilotSession(
39363883
"resumed-session",
39373884
(client as any).connection,
3938-
undefined,
3939-
undefined,
3940-
{ supportsSessionDetach: true }
3885+
undefined
39413886
);
39423887
(client as any).sessions.set(created.sessionId, created);
39433888
(client as any).sessions.set(resumed.sessionId, resumed);

0 commit comments

Comments
 (0)