@github/copilot-sdk 1.0.2 → 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/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?.getBearerToken) {
102
+ const { getBearerToken, ...rest } = provider;
103
+ callbacks.set(DEFAULT_PROVIDER_NAME, getBearerToken);
104
+ wireProvider = {
105
+ ...rest,
106
+ hasBearerTokenProvider: true
107
+ };
108
+ }
109
+ let wireProviders = providers;
110
+ if (providers?.some((p) => p.getBearerToken)) {
111
+ wireProviders = providers.map((p) => {
112
+ if (!p.getBearerToken) return p;
113
+ const { getBearerToken, ...rest } = p;
114
+ callbacks.set(p.name, getBearerToken);
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(
@@ -170,28 +197,57 @@ function getNodeExecPath() {
170
197
  }
171
198
  return process.execPath;
172
199
  }
200
+ function getCliPlatformPackageNames() {
201
+ const arch = process.arch;
202
+ const variants = process.platform === "linux" ? ["linux", "linuxmusl"] : [process.platform];
203
+ return variants.map((variant) => `@github/copilot-${variant}-${arch}`);
204
+ }
173
205
  function getBundledCliPath() {
206
+ const packageNames = getCliPlatformPackageNames();
174
207
  if (typeof import_meta.resolve === "function") {
175
- const sdkUrl = import_meta.resolve("@github/copilot/sdk");
176
- const sdkPath = (0, import_node_url.fileURLToPath)(sdkUrl);
177
- return (0, import_node_path.join)((0, import_node_path.dirname)((0, import_node_path.dirname)(sdkPath)), "index.js");
208
+ for (const packageName of packageNames) {
209
+ try {
210
+ const sdkUrl = import_meta.resolve(`${packageName}/sdk`);
211
+ const sdkPath = (0, import_node_url.fileURLToPath)(sdkUrl);
212
+ return (0, import_node_path.join)((0, import_node_path.dirname)((0, import_node_path.dirname)(sdkPath)), "index.js");
213
+ } catch {
214
+ }
215
+ }
216
+ throw new Error(
217
+ `Could not resolve a @github/copilot platform package (tried ${packageNames.join(", ")}). Ensure @github/copilot is installed, or pass cliPath/cliUrl to CopilotClient.`
218
+ );
178
219
  }
179
220
  const req = (0, import_node_module.createRequire)(__filename);
180
221
  const searchPaths = req.resolve.paths("@github/copilot") ?? [];
181
222
  for (const base of searchPaths) {
182
- const candidate = (0, import_node_path.join)(base, "@github", "copilot", "index.js");
183
- if ((0, import_node_fs.existsSync)(candidate)) {
184
- return candidate;
223
+ for (const packageName of packageNames) {
224
+ const candidate = (0, import_node_path.join)(base, ...packageName.split("/"), "index.js");
225
+ if ((0, import_node_fs.existsSync)(candidate)) {
226
+ return candidate;
227
+ }
185
228
  }
186
229
  }
187
230
  throw new Error(
188
- `Could not find @github/copilot package. Searched ${searchPaths.length} paths. Ensure it is installed, or pass cliPath/cliUrl to CopilotClient.`
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.`
189
232
  );
190
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
+ }
191
246
  class CopilotClient {
192
247
  cliStartTimeout = null;
193
248
  cliProcess = null;
194
249
  connection = null;
250
+ messageWriter = null;
195
251
  socket = null;
196
252
  runtimePort = null;
197
253
  actualHost = "localhost";
@@ -223,6 +279,8 @@ class CopilotClient {
223
279
  negotiatedProtocolVersion = null;
224
280
  /** Connection-level session filesystem config, set via constructor option. */
225
281
  sessionFsConfig = null;
282
+ requestHandler = null;
283
+ llmInferenceHandlers = {};
226
284
  /**
227
285
  * Typed server-scoped RPC methods.
228
286
  * @throws Error if the client is not connected
@@ -253,6 +311,13 @@ class CopilotClient {
253
311
  const level = this.options.logLevel?.toLowerCase();
254
312
  if (level === "debug" || level === "all") {
255
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}
256
321
  `);
257
322
  }
258
323
  }
@@ -315,6 +380,8 @@ class CopilotClient {
315
380
  this.onListModels = options.onListModels;
316
381
  this.onGetTraceContext = options.onGetTraceContext;
317
382
  this.sessionFsConfig = options.sessionFs ?? null;
383
+ this.requestHandler = options.requestHandler ?? null;
384
+ this.setupLlmInference();
318
385
  const effectiveEnv = options.env ?? process.env;
319
386
  this.resolvedEnv = effectiveEnv;
320
387
  this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
@@ -394,6 +461,20 @@ class CopilotClient {
394
461
  }
395
462
  session.clientSessionApis.sessionFs = (0, import_sessionFsProvider.createSessionFsAdapter)(provider);
396
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
+ }
397
478
  /**
398
479
  * Starts the CLI server and establishes a connection.
399
480
  *
@@ -431,6 +512,9 @@ class CopilotClient {
431
512
  capabilities: this.sessionFsConfig.capabilities
432
513
  });
433
514
  }
515
+ if (this.requestHandler) {
516
+ await this.connection.sendRequest("llmInference.setProvider", {});
517
+ }
434
518
  this.state = "connected";
435
519
  } catch (error) {
436
520
  this.state = "error";
@@ -463,7 +547,8 @@ class CopilotClient {
463
547
  */
464
548
  async stop() {
465
549
  const errors = [];
466
- for (const session of this.sessions.values()) {
550
+ const activeSessions = [...this.sessions.values()];
551
+ for (const session of activeSessions) {
467
552
  const sessionId = session.sessionId;
468
553
  let lastError = null;
469
554
  for (let attempt = 1; attempt <= 3; attempt++) {
@@ -487,8 +572,10 @@ class CopilotClient {
487
572
  );
488
573
  }
489
574
  }
575
+ for (const session of activeSessions) {
576
+ session._markDisconnected();
577
+ }
490
578
  this.sessions.clear();
491
- let runtimeShutdownCompleted = false;
492
579
  if (this.connection && this.cliProcess && !this.isExternalServer) {
493
580
  const runtimeShutdownStart = Date.now();
494
581
  const shutdownPromise = this.rpc.runtime.shutdown();
@@ -499,7 +586,6 @@ class CopilotClient {
499
586
  RUNTIME_SHUTDOWN_TIMEOUT_MS,
500
587
  `runtime.shutdown timed out after ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms`
501
588
  );
502
- runtimeShutdownCompleted = true;
503
589
  this.logDebugTiming(
504
590
  "CopilotClient.stop runtime shutdown complete",
505
591
  runtimeShutdownStart
@@ -516,6 +602,9 @@ class CopilotClient {
516
602
  );
517
603
  }
518
604
  }
605
+ if (this.messageWriter) {
606
+ this.messageWriter.suppressWriteErrors = true;
607
+ }
519
608
  if (this.connection) {
520
609
  try {
521
610
  this.connection.dispose();
@@ -527,6 +616,7 @@ class CopilotClient {
527
616
  );
528
617
  }
529
618
  this.connection = null;
619
+ this.messageWriter = null;
530
620
  this._rpc = null;
531
621
  this._internalRpc = null;
532
622
  }
@@ -554,16 +644,13 @@ class CopilotClient {
554
644
  this.cliProcess = null;
555
645
  try {
556
646
  if (child.exitCode == null && child.signalCode == null) {
557
- const exitedGracefully = runtimeShutdownCompleted ? await waitForChildExit(child, RUNTIME_SHUTDOWN_TIMEOUT_MS) : false;
558
- if (!exitedGracefully) {
559
- child.kill();
560
- if (!await waitForChildExit(child, RUNTIME_SHUTDOWN_TIMEOUT_MS)) {
561
- errors.push(
562
- new Error(
563
- `Timed out waiting for CLI process to exit after kill: ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms`
564
- )
565
- );
566
- }
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
+ );
567
654
  }
568
655
  }
569
656
  } catch (error) {
@@ -626,13 +713,20 @@ class CopilotClient {
626
713
  */
627
714
  async forceStop() {
628
715
  this.forceStopping = true;
716
+ for (const session of this.sessions.values()) {
717
+ session._markDisconnected();
718
+ }
629
719
  this.sessions.clear();
720
+ if (this.messageWriter) {
721
+ this.messageWriter.suppressWriteErrors = true;
722
+ }
630
723
  if (this.connection) {
631
724
  try {
632
725
  this.connection.dispose();
633
726
  } catch {
634
727
  }
635
728
  this.connection = null;
729
+ this.messageWriter = null;
636
730
  this._rpc = null;
637
731
  this._internalRpc = null;
638
732
  }
@@ -818,6 +912,11 @@ class CopilotClient {
818
912
  const callerSessionId = config.sessionId;
819
913
  const useServerGeneratedId = config.cloud != null && callerSessionId == null;
820
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);
821
920
  const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
822
921
  config.systemMessage
823
922
  );
@@ -831,6 +930,9 @@ class CopilotClient {
831
930
  s.registerTools(config.tools);
832
931
  s.registerCanvases(config.canvases);
833
932
  s.registerCommands(config.commands);
933
+ if (bearerTokenCallbacks.size > 0) {
934
+ s.registerBearerTokenProviders(bearerTokenCallbacks);
935
+ }
834
936
  s.registerPermissionHandler(config.onPermissionRequest);
835
937
  if (config.onUserInputRequest) {
836
938
  s.registerUserInputHandler(config.onUserInputRequest);
@@ -894,7 +996,10 @@ class CopilotClient {
894
996
  availableTools: toolFilterOptions.availableTools,
895
997
  excludedTools: toolFilterOptions.excludedTools,
896
998
  toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
897
- provider: config.provider,
999
+ provider: bearerWireProvider,
1000
+ capi: config.capi,
1001
+ providers: bearerWireProviders,
1002
+ models: config.models,
898
1003
  enableSessionTelemetry: config.enableSessionTelemetry,
899
1004
  modelCapabilities: config.modelCapabilities,
900
1005
  largeOutput: toWireLargeOutput(config.largeOutput),
@@ -932,7 +1037,8 @@ class CopilotClient {
932
1037
  memory: config.memory,
933
1038
  gitHubToken: config.gitHubToken,
934
1039
  remoteSession: config.remoteSession,
935
- cloud: config.cloud
1040
+ cloud: config.cloud,
1041
+ expAssignments: config.expAssignments
936
1042
  });
937
1043
  const {
938
1044
  sessionId: returnedSessionId,
@@ -999,6 +1105,14 @@ class CopilotClient {
999
1105
  session.registerTools(config.tools);
1000
1106
  session.registerCanvases(config.canvases);
1001
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
+ }
1002
1116
  session.registerPermissionHandler(config.onPermissionRequest);
1003
1117
  if (config.onUserInputRequest) {
1004
1118
  session.registerUserInputHandler(config.onUserInputRequest);
@@ -1060,7 +1174,10 @@ class CopilotClient {
1060
1174
  name: cmd.name,
1061
1175
  description: cmd.description
1062
1176
  })),
1063
- provider: config.provider,
1177
+ provider: bearerWireProvider,
1178
+ capi: config.capi,
1179
+ providers: bearerWireProviders,
1180
+ models: config.models,
1064
1181
  modelCapabilities: config.modelCapabilities,
1065
1182
  largeOutput: toWireLargeOutput(config.largeOutput),
1066
1183
  requestPermission: config.onPermissionRequest !== import_types.defaultJoinSessionPermissionHandler,
@@ -1099,7 +1216,8 @@ class CopilotClient {
1099
1216
  continuePendingWork: config.continuePendingWork,
1100
1217
  gitHubToken: config.gitHubToken,
1101
1218
  remoteSession: config.remoteSession,
1102
- openCanvases: config.openCanvases
1219
+ openCanvases: config.openCanvases,
1220
+ expAssignments: config.expAssignments
1103
1221
  });
1104
1222
  const { workspacePath, capabilities, openCanvases } = response;
1105
1223
  session["_workspacePath"] = workspacePath;
@@ -1641,13 +1759,21 @@ stderr: ${stderrOutput}`
1641
1759
  throw new Error("CLI process not started");
1642
1760
  }
1643
1761
  this.cliProcess.stdin?.on("error", (err) => {
1644
- if (!this.forceStopping) {
1645
- 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 {
1646
1771
  }
1647
1772
  });
1773
+ this.messageWriter = new TeardownResilientStreamMessageWriter(this.cliProcess.stdin);
1648
1774
  this.connection = (0, import_node.createMessageConnection)(
1649
1775
  new import_node.StreamMessageReader(this.cliProcess.stdout),
1650
- new import_node.StreamMessageWriter(this.cliProcess.stdin)
1776
+ this.messageWriter
1651
1777
  );
1652
1778
  this.attachConnectionHandlers();
1653
1779
  this.connection.listen();
@@ -1659,9 +1785,10 @@ stderr: ${stderrOutput}`
1659
1785
  if (this.cliProcess) {
1660
1786
  throw new Error("CLI child process was unexpectedly started in parent process mode");
1661
1787
  }
1788
+ this.messageWriter = new TeardownResilientStreamMessageWriter(process.stdout);
1662
1789
  this.connection = (0, import_node.createMessageConnection)(
1663
1790
  new import_node.StreamMessageReader(process.stdin),
1664
- new import_node.StreamMessageWriter(process.stdout)
1791
+ this.messageWriter
1665
1792
  );
1666
1793
  this.attachConnectionHandlers();
1667
1794
  this.connection.listen();
@@ -1681,9 +1808,10 @@ stderr: ${stderrOutput}`
1681
1808
  }, 1e4);
1682
1809
  this.socket.connect(this.runtimePort, this.actualHost, () => {
1683
1810
  clearTimeout(connectionTimeout);
1811
+ this.messageWriter = new TeardownResilientStreamMessageWriter(this.socket);
1684
1812
  this.connection = (0, import_node.createMessageConnection)(
1685
1813
  new import_node.StreamMessageReader(this.socket),
1686
- new import_node.StreamMessageWriter(this.socket)
1814
+ this.messageWriter
1687
1815
  );
1688
1816
  this.attachConnectionHandlers();
1689
1817
  this.connection.listen();
@@ -1731,6 +1859,7 @@ stderr: ${stderrOutput}`
1731
1859
  if (!session) throw new Error(`No session found for sessionId: ${sessionId}`);
1732
1860
  return session.clientSessionApis;
1733
1861
  });
1862
+ (0, import_rpc.registerClientGlobalApiHandlers)(this.connection, this.llmInferenceHandlers);
1734
1863
  this.connection.onClose(() => {
1735
1864
  this.state = "disconnected";
1736
1865
  });