@github/copilot-sdk 1.0.3 → 1.0.5-preview.0

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/README.md CHANGED
@@ -618,14 +618,17 @@ const session = await client.createSession({
618
618
  });
619
619
  ```
620
620
 
621
- Available section IDs: `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`, `runtime_instructions`, `last_instructions`. Use the `SYSTEM_MESSAGE_SECTIONS` constant for descriptions of each section.
621
+ Available section IDs: `preamble`, `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`, `runtime_instructions`, `last_instructions`. Use the `SYSTEM_MESSAGE_SECTIONS` constant for descriptions of each section.
622
622
 
623
- Each section override supports four actions:
623
+ `identity` and `tool_instructions` are section _groups_ that target a collection of related sub-sections as a unit. Use `preamble` to target just the identity preamble without affecting its sibling sub-sections.
624
+
625
+ Each section override supports five actions:
624
626
 
625
627
  - **`replace`** — Replace the section content entirely
626
628
  - **`remove`** — Remove the section from the prompt
627
629
  - **`append`** — Add content after the existing section
628
630
  - **`prepend`** — Add content before the existing section
631
+ - **`preserve`** — No-op that opts an individually-addressable section out of a group-level `remove`
629
632
 
630
633
  Unknown section IDs are handled gracefully: content from `replace`/`append`/`prepend` overrides is appended to additional instructions, and `remove` overrides are silently ignored.
631
634
 
@@ -684,6 +687,7 @@ information across turns. Provide a `memory` configuration on session create or
684
687
  when omitted, the runtime default applies. In the default `"copilot-cli"` client mode the
685
688
  SDK leaves `memory` unset so the runtime applies its own default, while `"empty"` mode
686
689
  defaults `memory` to disabled unless you set it explicitly.
690
+ For more background, see [About GitHub Copilot Memory](https://docs.github.com/en/copilot/concepts/agents/copilot-memory).
687
691
 
688
692
  ```typescript
689
693
  // Enable memory for a session
@@ -33,6 +33,7 @@ var import_rpc = require("./generated/rpc.js");
33
33
  var import_sdkProtocolVersion = require("./sdkProtocolVersion.js");
34
34
  var import_session = require("./session.js");
35
35
  var import_sessionFsProvider = require("./sessionFsProvider.js");
36
+ var import_copilotRequestHandler = require("./copilotRequestHandler.js");
36
37
  var import_telemetry = require("./telemetry.js");
37
38
  var import_toolSet = require("./toolSet.js");
38
39
  var import_types = require("./types.js");
@@ -93,6 +94,32 @@ function toJsonSchema(parameters) {
93
94
  }
94
95
  return parameters;
95
96
  }
97
+ const DEFAULT_PROVIDER_NAME = "default";
98
+ function extractBearerTokenProviders(provider, providers) {
99
+ const callbacks = /* @__PURE__ */ new Map();
100
+ let wireProvider = provider;
101
+ if (provider?.bearerTokenProvider) {
102
+ const { bearerTokenProvider, ...rest } = provider;
103
+ callbacks.set(DEFAULT_PROVIDER_NAME, bearerTokenProvider);
104
+ wireProvider = {
105
+ ...rest,
106
+ hasBearerTokenProvider: true
107
+ };
108
+ }
109
+ let wireProviders = providers;
110
+ if (providers?.some((p) => p.bearerTokenProvider)) {
111
+ wireProviders = providers.map((p) => {
112
+ if (!p.bearerTokenProvider) return p;
113
+ const { bearerTokenProvider, ...rest } = p;
114
+ callbacks.set(p.name, bearerTokenProvider);
115
+ return {
116
+ ...rest,
117
+ hasBearerTokenProvider: true
118
+ };
119
+ });
120
+ }
121
+ return { wireProvider, wireProviders, callbacks };
122
+ }
96
123
  function toWireMcpServers(mcpServers) {
97
124
  if (!mcpServers) return void 0;
98
125
  return Object.fromEntries(
@@ -204,10 +231,23 @@ function getBundledCliPath() {
204
231
  `Could not find a @github/copilot platform package (tried ${packageNames.join(", ")}). Searched ${searchPaths.length} paths. Ensure @github/copilot is installed, or pass cliPath/cliUrl to CopilotClient.`
205
232
  );
206
233
  }
234
+ class TeardownResilientStreamMessageWriter extends import_node.StreamMessageWriter {
235
+ suppressWriteErrors = false;
236
+ async write(msg) {
237
+ try {
238
+ await super.write(msg);
239
+ } catch (error) {
240
+ if (!this.suppressWriteErrors) {
241
+ throw error;
242
+ }
243
+ }
244
+ }
245
+ }
207
246
  class CopilotClient {
208
247
  cliStartTimeout = null;
209
248
  cliProcess = null;
210
249
  connection = null;
250
+ messageWriter = null;
211
251
  socket = null;
212
252
  runtimePort = null;
213
253
  actualHost = "localhost";
@@ -239,6 +279,8 @@ class CopilotClient {
239
279
  negotiatedProtocolVersion = null;
240
280
  /** Connection-level session filesystem config, set via constructor option. */
241
281
  sessionFsConfig = null;
282
+ requestHandler = null;
283
+ llmInferenceHandlers = {};
242
284
  /**
243
285
  * Typed server-scoped RPC methods.
244
286
  * @throws Error if the client is not connected
@@ -269,6 +311,13 @@ class CopilotClient {
269
311
  const level = this.options.logLevel?.toLowerCase();
270
312
  if (level === "debug" || level === "all") {
271
313
  process.stderr.write(`[copilot-sdk] ${message}. Elapsed=${Date.now() - startMs}ms
314
+ `);
315
+ }
316
+ }
317
+ logDebug(message) {
318
+ const level = this.options.logLevel?.toLowerCase();
319
+ if (level === "debug" || level === "all") {
320
+ process.stderr.write(`[copilot-sdk] ${message}
272
321
  `);
273
322
  }
274
323
  }
@@ -331,6 +380,8 @@ class CopilotClient {
331
380
  this.onListModels = options.onListModels;
332
381
  this.onGetTraceContext = options.onGetTraceContext;
333
382
  this.sessionFsConfig = options.sessionFs ?? null;
383
+ this.requestHandler = options.requestHandler ?? null;
384
+ this.setupLlmInference();
334
385
  const effectiveEnv = options.env ?? process.env;
335
386
  this.resolvedEnv = effectiveEnv;
336
387
  this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
@@ -410,6 +461,20 @@ class CopilotClient {
410
461
  }
411
462
  session.clientSessionApis.sessionFs = (0, import_sessionFsProvider.createSessionFsAdapter)(provider);
412
463
  }
464
+ setupLlmInference() {
465
+ if (!this.requestHandler) {
466
+ return;
467
+ }
468
+ this.llmInferenceHandlers = {
469
+ llmInference: (0, import_copilotRequestHandler.createCopilotRequestAdapter)(this.requestHandler, () => {
470
+ if (!this.connection) {
471
+ return void 0;
472
+ }
473
+ this._rpc ??= (0, import_rpc.createServerRpc)(this.connection);
474
+ return this._rpc;
475
+ })
476
+ };
477
+ }
413
478
  /**
414
479
  * Starts the CLI server and establishes a connection.
415
480
  *
@@ -447,6 +512,9 @@ class CopilotClient {
447
512
  capabilities: this.sessionFsConfig.capabilities
448
513
  });
449
514
  }
515
+ if (this.requestHandler) {
516
+ await this.connection.sendRequest("llmInference.setProvider", {});
517
+ }
450
518
  this.state = "connected";
451
519
  } catch (error) {
452
520
  this.state = "error";
@@ -479,7 +547,8 @@ class CopilotClient {
479
547
  */
480
548
  async stop() {
481
549
  const errors = [];
482
- for (const session of this.sessions.values()) {
550
+ const activeSessions = [...this.sessions.values()];
551
+ for (const session of activeSessions) {
483
552
  const sessionId = session.sessionId;
484
553
  let lastError = null;
485
554
  for (let attempt = 1; attempt <= 3; attempt++) {
@@ -503,8 +572,10 @@ class CopilotClient {
503
572
  );
504
573
  }
505
574
  }
575
+ for (const session of activeSessions) {
576
+ session._markDisconnected();
577
+ }
506
578
  this.sessions.clear();
507
- let runtimeShutdownCompleted = false;
508
579
  if (this.connection && this.cliProcess && !this.isExternalServer) {
509
580
  const runtimeShutdownStart = Date.now();
510
581
  const shutdownPromise = this.rpc.runtime.shutdown();
@@ -515,7 +586,6 @@ class CopilotClient {
515
586
  RUNTIME_SHUTDOWN_TIMEOUT_MS,
516
587
  `runtime.shutdown timed out after ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms`
517
588
  );
518
- runtimeShutdownCompleted = true;
519
589
  this.logDebugTiming(
520
590
  "CopilotClient.stop runtime shutdown complete",
521
591
  runtimeShutdownStart
@@ -532,6 +602,9 @@ class CopilotClient {
532
602
  );
533
603
  }
534
604
  }
605
+ if (this.messageWriter) {
606
+ this.messageWriter.suppressWriteErrors = true;
607
+ }
535
608
  if (this.connection) {
536
609
  try {
537
610
  this.connection.dispose();
@@ -543,6 +616,7 @@ class CopilotClient {
543
616
  );
544
617
  }
545
618
  this.connection = null;
619
+ this.messageWriter = null;
546
620
  this._rpc = null;
547
621
  this._internalRpc = null;
548
622
  }
@@ -570,16 +644,13 @@ class CopilotClient {
570
644
  this.cliProcess = null;
571
645
  try {
572
646
  if (child.exitCode == null && child.signalCode == null) {
573
- const exitedGracefully = runtimeShutdownCompleted ? await waitForChildExit(child, RUNTIME_SHUTDOWN_TIMEOUT_MS) : false;
574
- if (!exitedGracefully) {
575
- child.kill();
576
- if (!await waitForChildExit(child, RUNTIME_SHUTDOWN_TIMEOUT_MS)) {
577
- errors.push(
578
- new Error(
579
- `Timed out waiting for CLI process to exit after kill: ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms`
580
- )
581
- );
582
- }
647
+ child.kill();
648
+ if (!await waitForChildExit(child, RUNTIME_SHUTDOWN_TIMEOUT_MS)) {
649
+ errors.push(
650
+ new Error(
651
+ `Timed out waiting for CLI process to exit after kill: ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms`
652
+ )
653
+ );
583
654
  }
584
655
  }
585
656
  } catch (error) {
@@ -642,13 +713,20 @@ class CopilotClient {
642
713
  */
643
714
  async forceStop() {
644
715
  this.forceStopping = true;
716
+ for (const session of this.sessions.values()) {
717
+ session._markDisconnected();
718
+ }
645
719
  this.sessions.clear();
720
+ if (this.messageWriter) {
721
+ this.messageWriter.suppressWriteErrors = true;
722
+ }
646
723
  if (this.connection) {
647
724
  try {
648
725
  this.connection.dispose();
649
726
  } catch {
650
727
  }
651
728
  this.connection = null;
729
+ this.messageWriter = null;
652
730
  this._rpc = null;
653
731
  this._internalRpc = null;
654
732
  }
@@ -834,6 +912,11 @@ class CopilotClient {
834
912
  const callerSessionId = config.sessionId;
835
913
  const useServerGeneratedId = config.cloud != null && callerSessionId == null;
836
914
  const localSessionId = useServerGeneratedId ? void 0 : callerSessionId ?? (0, import_node_crypto.randomUUID)();
915
+ const {
916
+ wireProvider: bearerWireProvider,
917
+ wireProviders: bearerWireProviders,
918
+ callbacks: bearerTokenCallbacks
919
+ } = extractBearerTokenProviders(config.provider, config.providers);
837
920
  const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
838
921
  config.systemMessage
839
922
  );
@@ -847,6 +930,9 @@ class CopilotClient {
847
930
  s.registerTools(config.tools);
848
931
  s.registerCanvases(config.canvases);
849
932
  s.registerCommands(config.commands);
933
+ if (bearerTokenCallbacks.size > 0) {
934
+ s.registerBearerTokenProviders(bearerTokenCallbacks);
935
+ }
850
936
  s.registerPermissionHandler(config.onPermissionRequest);
851
937
  if (config.onUserInputRequest) {
852
938
  s.registerUserInputHandler(config.onUserInputRequest);
@@ -910,8 +996,9 @@ class CopilotClient {
910
996
  availableTools: toolFilterOptions.availableTools,
911
997
  excludedTools: toolFilterOptions.excludedTools,
912
998
  toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
913
- provider: config.provider,
914
- providers: config.providers,
999
+ provider: bearerWireProvider,
1000
+ capi: config.capi,
1001
+ providers: bearerWireProviders,
915
1002
  models: config.models,
916
1003
  enableSessionTelemetry: config.enableSessionTelemetry,
917
1004
  modelCapabilities: config.modelCapabilities,
@@ -950,7 +1037,8 @@ class CopilotClient {
950
1037
  memory: config.memory,
951
1038
  gitHubToken: config.gitHubToken,
952
1039
  remoteSession: config.remoteSession,
953
- cloud: config.cloud
1040
+ cloud: config.cloud,
1041
+ expAssignments: config.expAssignments
954
1042
  });
955
1043
  const {
956
1044
  sessionId: returnedSessionId,
@@ -1017,6 +1105,14 @@ class CopilotClient {
1017
1105
  session.registerTools(config.tools);
1018
1106
  session.registerCanvases(config.canvases);
1019
1107
  session.registerCommands(config.commands);
1108
+ const {
1109
+ wireProvider: bearerWireProvider,
1110
+ wireProviders: bearerWireProviders,
1111
+ callbacks: bearerTokenCallbacks
1112
+ } = extractBearerTokenProviders(config.provider, config.providers);
1113
+ if (bearerTokenCallbacks.size > 0) {
1114
+ session.registerBearerTokenProviders(bearerTokenCallbacks);
1115
+ }
1020
1116
  session.registerPermissionHandler(config.onPermissionRequest);
1021
1117
  if (config.onUserInputRequest) {
1022
1118
  session.registerUserInputHandler(config.onUserInputRequest);
@@ -1078,8 +1174,9 @@ class CopilotClient {
1078
1174
  name: cmd.name,
1079
1175
  description: cmd.description
1080
1176
  })),
1081
- provider: config.provider,
1082
- providers: config.providers,
1177
+ provider: bearerWireProvider,
1178
+ capi: config.capi,
1179
+ providers: bearerWireProviders,
1083
1180
  models: config.models,
1084
1181
  modelCapabilities: config.modelCapabilities,
1085
1182
  largeOutput: toWireLargeOutput(config.largeOutput),
@@ -1119,7 +1216,8 @@ class CopilotClient {
1119
1216
  continuePendingWork: config.continuePendingWork,
1120
1217
  gitHubToken: config.gitHubToken,
1121
1218
  remoteSession: config.remoteSession,
1122
- openCanvases: config.openCanvases
1219
+ openCanvases: config.openCanvases,
1220
+ expAssignments: config.expAssignments
1123
1221
  });
1124
1222
  const { workspacePath, capabilities, openCanvases } = response;
1125
1223
  session["_workspacePath"] = workspacePath;
@@ -1661,13 +1759,21 @@ stderr: ${stderrOutput}`
1661
1759
  throw new Error("CLI process not started");
1662
1760
  }
1663
1761
  this.cliProcess.stdin?.on("error", (err) => {
1664
- if (!this.forceStopping) {
1665
- throw err;
1762
+ if (this.forceStopping) {
1763
+ return;
1764
+ }
1765
+ this.state = "error";
1766
+ const reason = err instanceof Error ? err.stack ?? err.message : String(err);
1767
+ this.logDebug(`stdin pipe error: ${reason}`);
1768
+ try {
1769
+ this.connection?.dispose();
1770
+ } catch {
1666
1771
  }
1667
1772
  });
1773
+ this.messageWriter = new TeardownResilientStreamMessageWriter(this.cliProcess.stdin);
1668
1774
  this.connection = (0, import_node.createMessageConnection)(
1669
1775
  new import_node.StreamMessageReader(this.cliProcess.stdout),
1670
- new import_node.StreamMessageWriter(this.cliProcess.stdin)
1776
+ this.messageWriter
1671
1777
  );
1672
1778
  this.attachConnectionHandlers();
1673
1779
  this.connection.listen();
@@ -1679,9 +1785,10 @@ stderr: ${stderrOutput}`
1679
1785
  if (this.cliProcess) {
1680
1786
  throw new Error("CLI child process was unexpectedly started in parent process mode");
1681
1787
  }
1788
+ this.messageWriter = new TeardownResilientStreamMessageWriter(process.stdout);
1682
1789
  this.connection = (0, import_node.createMessageConnection)(
1683
1790
  new import_node.StreamMessageReader(process.stdin),
1684
- new import_node.StreamMessageWriter(process.stdout)
1791
+ this.messageWriter
1685
1792
  );
1686
1793
  this.attachConnectionHandlers();
1687
1794
  this.connection.listen();
@@ -1701,9 +1808,10 @@ stderr: ${stderrOutput}`
1701
1808
  }, 1e4);
1702
1809
  this.socket.connect(this.runtimePort, this.actualHost, () => {
1703
1810
  clearTimeout(connectionTimeout);
1811
+ this.messageWriter = new TeardownResilientStreamMessageWriter(this.socket);
1704
1812
  this.connection = (0, import_node.createMessageConnection)(
1705
1813
  new import_node.StreamMessageReader(this.socket),
1706
- new import_node.StreamMessageWriter(this.socket)
1814
+ this.messageWriter
1707
1815
  );
1708
1816
  this.attachConnectionHandlers();
1709
1817
  this.connection.listen();
@@ -1751,6 +1859,7 @@ stderr: ${stderrOutput}`
1751
1859
  if (!session) throw new Error(`No session found for sessionId: ${sessionId}`);
1752
1860
  return session.clientSessionApis;
1753
1861
  });
1862
+ (0, import_rpc.registerClientGlobalApiHandlers)(this.connection, this.llmInferenceHandlers);
1754
1863
  this.connection.onClose(() => {
1755
1864
  this.state = "disconnected";
1756
1865
  });