@github/copilot-sdk 1.0.0 → 1.0.2-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
@@ -2,6 +2,12 @@
2
2
 
3
3
  TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC.
4
4
 
5
+ ## Prerequisites
6
+
7
+ To use the SDK, you'll need:
8
+
9
+ - Node.js ^20.19.0 or >=22.12.0
10
+
5
11
  ## Installation
6
12
 
7
13
  ```bash
@@ -482,6 +488,21 @@ defineTool("safe_lookup", {
482
488
  });
483
489
  ```
484
490
 
491
+ #### Deferring Tools
492
+
493
+ Set `defer` to control whether a tool may be loaded lazily via tool search rather than always pre-loaded. Use `"auto"` to allow the tool to be deferred and surfaced through tool search, or `"never"` to force it to always be pre-loaded. Defaults to `"auto"`.
494
+
495
+ ```ts
496
+ defineTool("lookup_issue", {
497
+ description: "Fetch issue details",
498
+ parameters: z.object({ id: z.string() }),
499
+ defer: "auto",
500
+ handler: async ({ id }) => {
501
+ /* your logic */
502
+ },
503
+ });
504
+ ```
505
+
485
506
  ### Commands
486
507
 
487
508
  Register slash commands so that users of the CLI's TUI can invoke custom actions via `/commandName`. Each command has a `name`, optional `description`, and a `handler` called when the user executes it.
@@ -656,6 +677,28 @@ When enabled, sessions emit compaction events:
656
677
  - `session.compaction_start` - Background compaction started
657
678
  - `session.compaction_complete` - Compaction finished (includes token counts)
658
679
 
680
+ ### Memory
681
+
682
+ Sessions can opt in to the memory feature, which lets the agent persist and recall
683
+ information across turns. Provide a `memory` configuration on session create or resume;
684
+ when omitted, the runtime default applies. In the default `"copilot-cli"` client mode the
685
+ SDK leaves `memory` unset so the runtime applies its own default, while `"empty"` mode
686
+ defaults `memory` to disabled unless you set it explicitly.
687
+
688
+ ```typescript
689
+ // Enable memory for a session
690
+ const session = await client.createSession({
691
+ model: "gpt-5",
692
+ memory: { enabled: true },
693
+ });
694
+
695
+ // Disable memory for a session
696
+ const session = await client.createSession({
697
+ model: "gpt-5",
698
+ memory: { enabled: false },
699
+ });
700
+ ```
701
+
659
702
  ### Multiple Sessions
660
703
 
661
704
  ```typescript
@@ -771,6 +814,7 @@ With just this configuration, the CLI emits spans for every session, message, an
771
814
  **TelemetryConfig options:**
772
815
 
773
816
  - `otlpEndpoint?: string` - OTLP HTTP endpoint URL
817
+ - `otlpProtocol?: "http/json" | "http/protobuf"` - OTLP HTTP protocol for all signals
774
818
  - `filePath?: string` - File path for JSON-lines trace output
775
819
  - `exporterType?: string` - `"otlp-http"` or `"file"`
776
820
  - `sourceName?: string` - Instrumentation scope name
@@ -1033,11 +1077,6 @@ try {
1033
1077
  }
1034
1078
  ```
1035
1079
 
1036
- ## Requirements
1037
-
1038
- - Node.js >= 18.0.0
1039
- - GitHub Copilot CLI installed and in PATH (or provide a custom `connection`)
1040
-
1041
1080
  ## License
1042
1081
 
1043
1082
  MIT
package/dist/canvas.js CHANGED
@@ -4,6 +4,7 @@ class CanvasError extends Error {
4
4
  this.code = code;
5
5
  this.name = "CanvasError";
6
6
  }
7
+ code;
7
8
  /** Default error when an action is declared but no `handler` is wired. */
8
9
  static noHandler() {
9
10
  return new CanvasError(
@@ -29,6 +29,7 @@ class CanvasError extends Error {
29
29
  this.code = code;
30
30
  this.name = "CanvasError";
31
31
  }
32
+ code;
32
33
  /** Default error when an action is declared but no `handler` is wired. */
33
34
  static noHandler() {
34
35
  return new CanvasError(
@@ -38,9 +38,54 @@ var import_toolSet = require("./toolSet.js");
38
38
  var import_types = require("./types.js");
39
39
  const import_meta = {};
40
40
  const MIN_PROTOCOL_VERSION = 3;
41
+ const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
41
42
  function isZodSchema(value) {
42
43
  return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
43
44
  }
45
+ async function withTimeout(promise, timeoutMs, message) {
46
+ let timeout;
47
+ try {
48
+ return await Promise.race([
49
+ promise,
50
+ new Promise((_, reject) => {
51
+ timeout = setTimeout(() => reject(new Error(message)), timeoutMs);
52
+ })
53
+ ]);
54
+ } finally {
55
+ if (timeout !== void 0) {
56
+ clearTimeout(timeout);
57
+ }
58
+ }
59
+ }
60
+ async function waitForChildExit(child, timeoutMs) {
61
+ if (child.exitCode != null || child.signalCode != null) {
62
+ return true;
63
+ }
64
+ return new Promise((resolve) => {
65
+ let timeout;
66
+ let settled = false;
67
+ const onExit = () => {
68
+ if (settled) {
69
+ return;
70
+ }
71
+ settled = true;
72
+ clearTimeout(timeout);
73
+ resolve(true);
74
+ };
75
+ timeout = setTimeout(() => {
76
+ if (settled) {
77
+ return;
78
+ }
79
+ settled = true;
80
+ child.off("exit", onExit);
81
+ resolve(false);
82
+ }, timeoutMs);
83
+ child.once("exit", onExit);
84
+ if (child.exitCode != null || child.signalCode != null) {
85
+ onExit();
86
+ }
87
+ });
88
+ }
44
89
  function toJsonSchema(parameters) {
45
90
  if (!parameters) return void 0;
46
91
  if (isZodSchema(parameters)) {
@@ -204,6 +249,13 @@ class CopilotClient {
204
249
  }
205
250
  return this._internalRpc;
206
251
  }
252
+ logDebugTiming(message, startMs) {
253
+ const level = this.options.logLevel?.toLowerCase();
254
+ if (level === "debug" || level === "all") {
255
+ process.stderr.write(`[copilot-sdk] ${message}. Elapsed=${Date.now() - startMs}ms
256
+ `);
257
+ }
258
+ }
207
259
  /**
208
260
  * Creates a new CopilotClient instance.
209
261
  *
@@ -390,8 +442,9 @@ class CopilotClient {
390
442
  *
391
443
  * This method performs graceful cleanup:
392
444
  * 1. Closes all active sessions (releases in-memory resources)
393
- * 2. Closes the JSON-RPC connection
394
- * 3. Terminates the CLI server process (if spawned by this client)
445
+ * 2. Requests runtime shutdown for SDK-owned CLI processes
446
+ * 3. Closes the JSON-RPC connection
447
+ * 4. Terminates the CLI server process (if spawned by this client)
395
448
  *
396
449
  * Note: session data on disk is preserved, so sessions can be resumed later.
397
450
  * To permanently remove session data before stopping, call
@@ -435,6 +488,34 @@ class CopilotClient {
435
488
  }
436
489
  }
437
490
  this.sessions.clear();
491
+ let runtimeShutdownCompleted = false;
492
+ if (this.connection && this.cliProcess && !this.isExternalServer) {
493
+ const runtimeShutdownStart = Date.now();
494
+ const shutdownPromise = this.rpc.runtime.shutdown();
495
+ void shutdownPromise.catch(() => void 0);
496
+ try {
497
+ await withTimeout(
498
+ shutdownPromise,
499
+ RUNTIME_SHUTDOWN_TIMEOUT_MS,
500
+ `runtime.shutdown timed out after ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms`
501
+ );
502
+ runtimeShutdownCompleted = true;
503
+ this.logDebugTiming(
504
+ "CopilotClient.stop runtime shutdown complete",
505
+ runtimeShutdownStart
506
+ );
507
+ } catch (error) {
508
+ this.logDebugTiming(
509
+ "CopilotClient.stop runtime shutdown failed",
510
+ runtimeShutdownStart
511
+ );
512
+ errors.push(
513
+ new Error(
514
+ `Failed to gracefully shut down runtime: ${error instanceof Error ? error.message : String(error)}`
515
+ )
516
+ );
517
+ }
518
+ }
438
519
  if (this.connection) {
439
520
  try {
440
521
  this.connection.dispose();
@@ -447,6 +528,7 @@ class CopilotClient {
447
528
  }
448
529
  this.connection = null;
449
530
  this._rpc = null;
531
+ this._internalRpc = null;
450
532
  }
451
533
  this.modelsCache = null;
452
534
  if (this.socket) {
@@ -471,12 +553,18 @@ class CopilotClient {
471
553
  const child = this.cliProcess;
472
554
  this.cliProcess = null;
473
555
  try {
474
- if (child.exitCode === null && child.signalCode === null) {
475
- const exited = new Promise((resolve) => {
476
- child.once("exit", () => resolve());
477
- });
478
- child.kill();
479
- await exited;
556
+ 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
+ }
567
+ }
480
568
  }
481
569
  } catch (error) {
482
570
  errors.push(
@@ -546,6 +634,7 @@ class CopilotClient {
546
634
  }
547
635
  this.connection = null;
548
636
  this._rpc = null;
637
+ this._internalRpc = null;
549
638
  }
550
639
  this.modelsCache = null;
551
640
  if (this.socket) {
@@ -638,7 +727,8 @@ class CopilotClient {
638
727
  enableFileHooks: false,
639
728
  enableHostGitOperations: false,
640
729
  enableSessionStore: false,
641
- enableSkills: false
730
+ enableSkills: false,
731
+ memory: { enabled: false }
642
732
  };
643
733
  }
644
734
  return {};
@@ -788,7 +878,8 @@ class CopilotClient {
788
878
  description: tool.description,
789
879
  parameters: toJsonSchema(tool.parameters),
790
880
  overridesBuiltInTool: tool.overridesBuiltInTool,
791
- skipPermission: tool.skipPermission
881
+ skipPermission: tool.skipPermission,
882
+ defer: tool.defer
792
883
  })),
793
884
  canvases: config.canvases?.map((canvas) => canvas.declaration),
794
885
  requestCanvasRenderer: config.requestCanvasRenderer,
@@ -838,6 +929,7 @@ class CopilotClient {
838
929
  instructionDirectories: config.instructionDirectories,
839
930
  disabledSkills: config.disabledSkills,
840
931
  infiniteSessions: config.infiniteSessions,
932
+ memory: config.memory,
841
933
  gitHubToken: config.gitHubToken,
842
934
  remoteSession: config.remoteSession,
843
935
  cloud: config.cloud
@@ -956,7 +1048,8 @@ class CopilotClient {
956
1048
  description: tool.description,
957
1049
  parameters: toJsonSchema(tool.parameters),
958
1050
  overridesBuiltInTool: tool.overridesBuiltInTool,
959
- skipPermission: tool.skipPermission
1051
+ skipPermission: tool.skipPermission,
1052
+ defer: tool.defer
960
1053
  })),
961
1054
  canvases: config.canvases?.map((canvas) => canvas.declaration),
962
1055
  requestCanvasRenderer: config.requestCanvasRenderer,
@@ -1001,6 +1094,7 @@ class CopilotClient {
1001
1094
  instructionDirectories: config.instructionDirectories,
1002
1095
  disabledSkills: config.disabledSkills,
1003
1096
  infiniteSessions: config.infiniteSessions,
1097
+ memory: config.memory,
1004
1098
  disableResume: config.suppressResumeEvent,
1005
1099
  continuePendingWork: config.continuePendingWork,
1006
1100
  gitHubToken: config.gitHubToken,
@@ -1403,6 +1497,8 @@ class CopilotClient {
1403
1497
  envWithoutNodeDebug.COPILOT_OTEL_ENABLED = "true";
1404
1498
  if (t.otlpEndpoint !== void 0)
1405
1499
  envWithoutNodeDebug.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
1500
+ if (t.otlpProtocol !== void 0)
1501
+ envWithoutNodeDebug.OTEL_EXPORTER_OTLP_PROTOCOL = t.otlpProtocol;
1406
1502
  if (t.filePath !== void 0)
1407
1503
  envWithoutNodeDebug.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
1408
1504
  if (t.exporterType !== void 0)