@github/copilot-sdk 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/session.js CHANGED
@@ -15,7 +15,7 @@ function isOpenCanvasInstance(value) {
15
15
  return false;
16
16
  }
17
17
  const instance = value;
18
- return typeof instance.instanceId === "string" && instance.instanceId.length > 0 && typeof instance.extensionId === "string" && instance.extensionId.length > 0 && typeof instance.canvasId === "string" && instance.canvasId.length > 0 && typeof instance.reopen === "boolean" && (instance.availability === "ready" || instance.availability === "stale");
18
+ return typeof instance.instanceId === "string" && instance.instanceId.length > 0 && typeof instance.extensionId === "string" && instance.extensionId.length > 0 && typeof instance.canvasId === "string" && instance.canvasId.length > 0;
19
19
  }
20
20
  class CopilotSession {
21
21
  /**
@@ -40,6 +40,7 @@ class CopilotSession {
40
40
  typedEventHandlers = /* @__PURE__ */ new Map();
41
41
  toolHandlers = /* @__PURE__ */ new Map();
42
42
  canvases = /* @__PURE__ */ new Map();
43
+ bearerTokenProviders = /* @__PURE__ */ new Map();
43
44
  commandHandlers = /* @__PURE__ */ new Map();
44
45
  permissionHandler;
45
46
  userInputHandler;
@@ -52,6 +53,7 @@ class CopilotSession {
52
53
  traceContextProvider;
53
54
  _capabilities = {};
54
55
  openCanvasInstances = [];
56
+ disconnected = false;
55
57
  /** @internal Client session API handlers, populated by CopilotClient during create/resume. */
56
58
  clientSessionApis = {};
57
59
  /**
@@ -155,6 +157,21 @@ class CopilotSession {
155
157
  unsubscribe();
156
158
  }
157
159
  }
160
+ /** @internal */
161
+ _markDisconnected() {
162
+ this.disconnected = true;
163
+ this.eventHandlers.clear();
164
+ this.typedEventHandlers.clear();
165
+ this.toolHandlers.clear();
166
+ this.permissionHandler = void 0;
167
+ this.userInputHandler = void 0;
168
+ this.elicitationHandler = void 0;
169
+ this.exitPlanModeHandler = void 0;
170
+ this.autoModeSwitchHandler = void 0;
171
+ this.commandHandlers.clear();
172
+ this.canvases.clear();
173
+ this.transformCallbacks?.clear();
174
+ }
158
175
  on(eventTypeOrHandler, handler) {
159
176
  if (typeof eventTypeOrHandler === "string" && handler) {
160
177
  const eventType = eventTypeOrHandler;
@@ -208,6 +225,9 @@ class CopilotSession {
208
225
  * @internal
209
226
  */
210
227
  _handleBroadcastEvent(event) {
228
+ if (this.disconnected) {
229
+ return;
230
+ }
211
231
  if (event.type === "external_tool.requested") {
212
232
  const { requestId, toolName } = event.data;
213
233
  const args = event.data.arguments;
@@ -313,8 +333,14 @@ class CopilotSession {
313
333
  } else {
314
334
  result = JSON.stringify(rawResult);
315
335
  }
336
+ if (this.disconnected) {
337
+ return;
338
+ }
316
339
  await this.rpc.tools.handlePendingToolCall({ requestId, result });
317
340
  } catch (error) {
341
+ if (this.disconnected) {
342
+ return;
343
+ }
318
344
  const message = error instanceof Error ? error.message : String(error);
319
345
  try {
320
346
  await this.rpc.tools.handlePendingToolCall({ requestId, error: message });
@@ -337,8 +363,14 @@ class CopilotSession {
337
363
  if (result.kind === "no-result") {
338
364
  return;
339
365
  }
366
+ if (this.disconnected) {
367
+ return;
368
+ }
340
369
  await this.rpc.permissions.handlePendingPermissionRequest({ requestId, result });
341
370
  } catch (_error) {
371
+ if (this.disconnected) {
372
+ return;
373
+ }
342
374
  try {
343
375
  await this.rpc.permissions.handlePendingPermissionRequest({
344
376
  requestId,
@@ -374,8 +406,14 @@ class CopilotSession {
374
406
  }
375
407
  try {
376
408
  await handler({ sessionId: this.sessionId, command, commandName, args });
409
+ if (this.disconnected) {
410
+ return;
411
+ }
377
412
  await this.rpc.commands.handlePendingCommand({ requestId });
378
413
  } catch (error) {
414
+ if (this.disconnected) {
415
+ return;
416
+ }
379
417
  const message = error instanceof Error ? error.message : String(error);
380
418
  try {
381
419
  await this.rpc.commands.handlePendingCommand({ requestId, error: message });
@@ -471,6 +509,43 @@ class CopilotSession {
471
509
  }
472
510
  };
473
511
  }
512
+ /**
513
+ * Registers per-provider {@link GetBearerToken} callbacks for BYOK providers
514
+ * configured with managed-identity / on-demand bearer-token auth.
515
+ *
516
+ * The runtime never receives the callback itself; the SDK strips it from the
517
+ * provider config and instead sends `hasBearerTokenProvider: true`. When the
518
+ * runtime needs a token it issues a session-scoped `providerToken.getToken`
519
+ * request, which this handler routes to the matching per-provider callback.
520
+ *
521
+ * @param providers - Map of provider name → callback, or undefined/empty to clear.
522
+ * @internal This method is called internally when creating/resuming a session.
523
+ */
524
+ registerBearerTokenProviders(providers) {
525
+ this.bearerTokenProviders.clear();
526
+ if (!providers || providers.size === 0) {
527
+ delete this.clientSessionApis.providerToken;
528
+ return;
529
+ }
530
+ for (const [name, callback] of providers) {
531
+ this.bearerTokenProviders.set(name, callback);
532
+ }
533
+ const self = this;
534
+ this.clientSessionApis.providerToken = {
535
+ async getToken(params) {
536
+ const callback = self.bearerTokenProviders.get(params.providerName);
537
+ if (!callback) {
538
+ throw new Error(
539
+ `No bearer-token provider registered for provider "${params.providerName}"`
540
+ );
541
+ }
542
+ const token = await callback({
543
+ providerName: params.providerName
544
+ });
545
+ return { token };
546
+ }
547
+ };
548
+ }
474
549
  /**
475
550
  * Registers command handlers for this session.
476
551
  *
@@ -825,17 +900,13 @@ class CopilotSession {
825
900
  * ```
826
901
  */
827
902
  async disconnect() {
903
+ if (this.disconnected) {
904
+ return;
905
+ }
828
906
  await this.connection.sendRequest("session.destroy", {
829
907
  sessionId: this.sessionId
830
908
  });
831
- this.eventHandlers.clear();
832
- this.typedEventHandlers.clear();
833
- this.toolHandlers.clear();
834
- this.permissionHandler = void 0;
835
- this.userInputHandler = void 0;
836
- this.elicitationHandler = void 0;
837
- this.exitPlanModeHandler = void 0;
838
- this.autoModeSwitchHandler = void 0;
909
+ this._markDisconnected();
839
910
  }
840
911
  /** Enables `await using session = ...` syntax for automatic cleanup. */
841
912
  async [Symbol.asyncDispose]() {
package/dist/types.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { Canvas } from "./canvas.js";
5
5
  import type { SessionFsProvider } from "./sessionFsProvider.js";
6
+ import type { CopilotRequestHandler } from "./copilotRequestHandler.js";
6
7
  import type { ReasoningSummary, SessionEvent as GeneratedSessionEvent } from "./generated/session-events.js";
7
8
  import type { CopilotSession } from "./session.js";
8
9
  import type { ModelBillingTokenPrices, OpenCanvasInstance, RemoteSessionMode } from "./generated/rpc.js";
@@ -17,6 +18,9 @@ export type { SessionFsFileInfo } from "./sessionFsProvider.js";
17
18
  export type { SessionFsSqliteQueryResult } from "./sessionFsProvider.js";
18
19
  export type { SessionFsSqliteQueryType } from "./sessionFsProvider.js";
19
20
  export type { SessionFsSqliteProvider } from "./sessionFsProvider.js";
21
+ export type { LlmInferenceHeaders } from "./generated/rpc.js";
22
+ export type { CopilotRequestContext } from "./copilotRequestHandler.js";
23
+ export { CopilotRequestHandler, CopilotWebSocketHandler, CopilotWebSocketCloseStatus, CopilotWebSocketForwarder, } from "./copilotRequestHandler.js";
20
24
  /**
21
25
  * Options for creating a CopilotClient
22
26
  */
@@ -243,6 +247,29 @@ export interface CopilotClientOptions {
243
247
  * instead of the server's default local filesystem storage.
244
248
  */
245
249
  sessionFs?: SessionFsConfig;
250
+ /**
251
+ * Custom handler for outbound model-layer requests (experimental).
252
+ *
253
+ * When provided, the client registers as the runtime's request handler
254
+ * on connection: every outbound model-layer request the runtime would
255
+ * otherwise have issued itself — plain HTTP, streaming SSE, and
256
+ * WebSocket — is dispatched back to the handler over JSON-RPC. The
257
+ * handler returns the response verbatim, exactly as if the runtime had
258
+ * issued the request itself.
259
+ *
260
+ * Subclass {@link CopilotRequestHandler} and override the hooks you need;
261
+ * an instance that overrides nothing is a transparent pass-through.
262
+ *
263
+ * v1 notes:
264
+ * - HTTP (buffered and streaming SSE) and WebSocket transports are all
265
+ * intercepted. The handler receives a `transport` discriminator on the
266
+ * {@link CopilotRequestContext} for both.
267
+ * - The handler is set process-globally on the runtime; the same
268
+ * handler is invoked for every session created on this client.
269
+ *
270
+ * @experimental
271
+ */
272
+ requestHandler?: CopilotRequestHandler;
246
273
  /**
247
274
  * Server-wide idle timeout for sessions in seconds.
248
275
  * Sessions without activity for this duration are automatically cleaned up.
@@ -622,7 +649,7 @@ export interface ToolCallResponsePayload {
622
649
  * Known system message section identifiers for the "customize" mode.
623
650
  * Each section corresponds to a distinct part of the system prompt.
624
651
  */
625
- export type SystemMessageSection = "identity" | "tone" | "tool_efficiency" | "environment_context" | "code_change_rules" | "guidelines" | "safety" | "tool_instructions" | "custom_instructions" | "runtime_instructions" | "last_instructions";
652
+ export type SystemMessageSection = "preamble" | "identity" | "tone" | "tool_efficiency" | "environment_context" | "code_change_rules" | "guidelines" | "safety" | "tool_instructions" | "custom_instructions" | "runtime_instructions" | "last_instructions";
626
653
  /** Section metadata for documentation and tooling. */
627
654
  export declare const SYSTEM_MESSAGE_SECTIONS: Record<SystemMessageSection, {
628
655
  description: string;
@@ -638,9 +665,11 @@ export type SectionTransformFn = (currentContent: string) => string | Promise<st
638
665
  * - `"remove"`: Remove the section
639
666
  * - `"append"`: Append to existing section content
640
667
  * - `"prepend"`: Prepend to existing section content
668
+ * - `"preserve"`: No-op marker that opts an individually-addressable section out of a
669
+ * group-level `"remove"` (e.g. keep `tone` when removing the `identity` group)
641
670
  * - `function`: Transform callback — receives current section content, returns new content
642
671
  */
643
- export type SectionOverrideAction = "replace" | "remove" | "append" | "prepend" | SectionTransformFn;
672
+ export type SectionOverrideAction = "replace" | "remove" | "append" | "prepend" | "preserve" | SectionTransformFn;
644
673
  /**
645
674
  * Override operation for a single system message section.
646
675
  */
@@ -1260,6 +1289,30 @@ export interface ExtensionInfo {
1260
1289
  /** Stable provider name within the source namespace. */
1261
1290
  name: string;
1262
1291
  }
1292
+ /**
1293
+ * Provider-scoped options for the Copilot API (CAPI).
1294
+ *
1295
+ * These settings apply to the built-in Copilot API provider only. They live
1296
+ * under their own namespace because a single session can host multiple
1297
+ * providers (CAPI alongside BYOK via {@link ProviderConfig}), so transport and
1298
+ * provider-level choices are conceptually per-provider rather than global.
1299
+ */
1300
+ export interface CapiSessionOptions {
1301
+ /**
1302
+ * Whether to use the WebSocket transport for the CAPI Responses API.
1303
+ *
1304
+ * WebSocket transport is enabled by default whenever the selected model
1305
+ * advertises the `ws:/responses` endpoint. Set this to `false` to fall back
1306
+ * to the HTTP Responses transport instead — useful for users behind proxies
1307
+ * where WebSocket connections fail.
1308
+ *
1309
+ * Setting this to `false` is equivalent to setting the
1310
+ * `COPILOT_CLI_DISABLE_WEBSOCKET_RESPONSES` environment variable.
1311
+ *
1312
+ * @default true
1313
+ */
1314
+ enableWebSocketResponses?: boolean;
1315
+ }
1263
1316
  /**
1264
1317
  * Shared configuration fields used by both {@link SessionConfig} (for
1265
1318
  * creating a new session) and {@link ResumeSessionConfig} (for resuming
@@ -1400,6 +1453,12 @@ export interface SessionConfigBase {
1400
1453
  * When specified, uses the provided API endpoint instead of the Copilot API.
1401
1454
  */
1402
1455
  provider?: ProviderConfig;
1456
+ /**
1457
+ * Provider-scoped options for the built-in Copilot API (CAPI), such as
1458
+ * opting out of the WebSocket Responses transport. See
1459
+ * {@link CapiSessionOptions}.
1460
+ */
1461
+ capi?: CapiSessionOptions;
1403
1462
  /**
1404
1463
  * Named BYOK provider connections (transport + credentials), referenced by
1405
1464
  * {@link models} entries via {@link NamedProviderConfig.name}.
@@ -1739,6 +1798,37 @@ export interface ResumeSessionConfig extends SessionConfigBase {
1739
1798
  */
1740
1799
  openCanvases?: OpenCanvasInstance[];
1741
1800
  }
1801
+ /**
1802
+ * Arguments passed to a {@link GetBearerToken} callback when the runtime needs a
1803
+ * fresh bearer token for a BYOK provider.
1804
+ *
1805
+ * @experimental Part of the experimental managed-identity / bearer-token-provider
1806
+ * surface and may change or be removed in future SDK or CLI releases.
1807
+ */
1808
+ export interface ProviderTokenArgs {
1809
+ /**
1810
+ * Name of the BYOK provider needing a token. For the singular, whole-session
1811
+ * {@link ProviderConfig} this is the implicit provider name (`"default"`); for
1812
+ * {@link NamedProviderConfig} entries it is {@link NamedProviderConfig.name}.
1813
+ *
1814
+ * The callback closes over its own token scope/audience; the runtime is
1815
+ * provider-agnostic and forwards only the provider name.
1816
+ */
1817
+ providerName: string;
1818
+ }
1819
+ /**
1820
+ * Per-provider callback that resolves a bearer token on demand, returning the
1821
+ * raw token string (without the `Bearer ` prefix). The Copilot SDK itself takes
1822
+ * no Azure dependency: the consumer supplies this callback backed by their own
1823
+ * identity library (for example `@azure/identity`'s
1824
+ * `DefaultAzureCredential.getToken(scope)`), and the runtime calls it once before
1825
+ * each outbound model request. The runtime does no caching of its own, so the
1826
+ * callback (or the identity library it wraps) owns token caching and refresh.
1827
+ *
1828
+ * @experimental Part of the experimental managed-identity / bearer-token-provider
1829
+ * surface and may change or be removed in future SDK or CLI releases.
1830
+ */
1831
+ export type GetBearerToken = (args: ProviderTokenArgs) => Promise<string>;
1742
1832
  /**
1743
1833
  * Configuration for a custom API provider.
1744
1834
  */
@@ -1751,6 +1841,16 @@ export interface ProviderConfig {
1751
1841
  * API format (openai/azure only). Defaults to "completions".
1752
1842
  */
1753
1843
  wireApi?: "completions" | "responses";
1844
+ /**
1845
+ * Transport for OpenAI Responses requests. Defaults to "http".
1846
+ *
1847
+ * Set to "websockets" to deliver Responses API requests over a persistent
1848
+ * WebSocket connection instead of HTTP. Useful for long-running,
1849
+ * tool-call-heavy sessions that benefit from incremental
1850
+ * `previous_response_id` continuations. Applies to OpenAI-compatible
1851
+ * providers using `wireApi: "responses"`.
1852
+ */
1853
+ transport?: "http" | "websockets";
1754
1854
  /**
1755
1855
  * API endpoint URL
1756
1856
  */
@@ -1765,6 +1865,17 @@ export interface ProviderConfig {
1765
1865
  * Takes precedence over apiKey when both are set.
1766
1866
  */
1767
1867
  bearerToken?: string;
1868
+ /**
1869
+ * Per-request bearer-token provider for managed-identity / on-demand auth.
1870
+ * When set, the SDK keeps this function client-side (it is never serialized)
1871
+ * and the runtime calls back into this client to acquire a token before each
1872
+ * outbound request. The runtime does no caching of its own, so the callback
1873
+ * owns token caching and refresh. Mutually exclusive with {@link apiKey} /
1874
+ * {@link bearerToken}.
1875
+ *
1876
+ * @experimental
1877
+ */
1878
+ getBearerToken?: GetBearerToken;
1768
1879
  /**
1769
1880
  * Azure-specific options
1770
1881
  */
@@ -1844,6 +1955,17 @@ export interface NamedProviderConfig {
1844
1955
  * Takes precedence over {@link apiKey} when both are set.
1845
1956
  */
1846
1957
  bearerToken?: string;
1958
+ /**
1959
+ * Per-request bearer-token provider for managed-identity / on-demand auth.
1960
+ * When set, the SDK keeps this function client-side (it is never serialized)
1961
+ * and the runtime calls back into this client to acquire a token before each
1962
+ * outbound request. The runtime does no caching of its own, so the callback
1963
+ * owns token caching and refresh. Mutually exclusive with {@link apiKey} /
1964
+ * {@link bearerToken}.
1965
+ *
1966
+ * @experimental
1967
+ */
1968
+ getBearerToken?: GetBearerToken;
1847
1969
  /**
1848
1970
  * Azure-specific options.
1849
1971
  */
package/dist/types.js CHANGED
@@ -1,4 +1,10 @@
1
1
  import { createSessionFsAdapter } from "./sessionFsProvider.js";
2
+ import {
3
+ CopilotRequestHandler,
4
+ CopilotWebSocketHandler,
5
+ CopilotWebSocketCloseStatus,
6
+ CopilotWebSocketForwarder
7
+ } from "./copilotRequestHandler.js";
2
8
  const RuntimeConnection = {
3
9
  /**
4
10
  * Spawn a runtime child process and communicate over its stdin/stdout.
@@ -73,7 +79,10 @@ function defineTool(name, config) {
73
79
  return { name, ...config };
74
80
  }
75
81
  const SYSTEM_MESSAGE_SECTIONS = {
76
- identity: { description: "Agent identity preamble and mode statement" },
82
+ preamble: { description: "Agent identity preamble and mode statement" },
83
+ identity: {
84
+ description: "Section group covering the identity preamble and its sibling sub-sections (tone, tool efficiency, etc.)"
85
+ },
77
86
  tone: { description: "Response style, conciseness rules, output formatting preferences" },
78
87
  tool_efficiency: { description: "Tool usage patterns, parallel calling, batching guidelines" },
79
88
  environment_context: { description: "CWD, OS, git root, directory listing, available tools" },
@@ -94,6 +103,10 @@ const defaultJoinSessionPermissionHandler = () => ({
94
103
  kind: "no-result"
95
104
  });
96
105
  export {
106
+ CopilotRequestHandler,
107
+ CopilotWebSocketCloseStatus,
108
+ CopilotWebSocketForwarder,
109
+ CopilotWebSocketHandler,
97
110
  RuntimeConnection,
98
111
  SYSTEM_MESSAGE_SECTIONS,
99
112
  approveAll,
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/github/copilot-sdk.git"
6
6
  },
7
- "version": "1.0.3",
7
+ "version": "1.0.4",
8
8
  "description": "TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC",
9
9
  "main": "./dist/cjs/index.js",
10
10
  "types": "./dist/index.d.ts",
@@ -56,13 +56,14 @@
56
56
  "author": "GitHub",
57
57
  "license": "MIT",
58
58
  "dependencies": {
59
- "@github/copilot": "^1.0.64-1",
59
+ "@github/copilot": "^1.0.65",
60
60
  "vscode-jsonrpc": "^8.2.1",
61
61
  "zod": "^4.3.6"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@platformatic/vfs": "^0.3.0",
65
65
  "@types/node": "^25.2.0",
66
+ "@types/ws": "^8.18.1",
66
67
  "@typescript-eslint/eslint-plugin": "^8.54.0",
67
68
  "@typescript-eslint/parser": "^8.54.0",
68
69
  "esbuild": "^0.28.1",
@@ -76,7 +77,8 @@
76
77
  "semver": "^7.7.3",
77
78
  "tsx": "^4.20.6",
78
79
  "typescript": "^5.0.0",
79
- "vitest": "^4.0.18"
80
+ "vitest": "^4.0.18",
81
+ "ws": "^8.21.0"
80
82
  },
81
83
  "engines": {
82
84
  "node": "^20.19.0 || >=22.12.0"