@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/dist/client.js CHANGED
@@ -24,9 +24,54 @@ import { getTraceContext } from "./telemetry.js";
24
24
  import { ToolSet } from "./toolSet.js";
25
25
  import { defaultJoinSessionPermissionHandler } from "./types.js";
26
26
  const MIN_PROTOCOL_VERSION = 3;
27
+ const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
27
28
  function isZodSchema(value) {
28
29
  return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
29
30
  }
31
+ async function withTimeout(promise, timeoutMs, message) {
32
+ let timeout;
33
+ try {
34
+ return await Promise.race([
35
+ promise,
36
+ new Promise((_, reject) => {
37
+ timeout = setTimeout(() => reject(new Error(message)), timeoutMs);
38
+ })
39
+ ]);
40
+ } finally {
41
+ if (timeout !== void 0) {
42
+ clearTimeout(timeout);
43
+ }
44
+ }
45
+ }
46
+ async function waitForChildExit(child, timeoutMs) {
47
+ if (child.exitCode != null || child.signalCode != null) {
48
+ return true;
49
+ }
50
+ return new Promise((resolve) => {
51
+ let timeout;
52
+ let settled = false;
53
+ const onExit = () => {
54
+ if (settled) {
55
+ return;
56
+ }
57
+ settled = true;
58
+ clearTimeout(timeout);
59
+ resolve(true);
60
+ };
61
+ timeout = setTimeout(() => {
62
+ if (settled) {
63
+ return;
64
+ }
65
+ settled = true;
66
+ child.off("exit", onExit);
67
+ resolve(false);
68
+ }, timeoutMs);
69
+ child.once("exit", onExit);
70
+ if (child.exitCode != null || child.signalCode != null) {
71
+ onExit();
72
+ }
73
+ });
74
+ }
30
75
  function toJsonSchema(parameters) {
31
76
  if (!parameters) return void 0;
32
77
  if (isZodSchema(parameters)) {
@@ -190,6 +235,13 @@ class CopilotClient {
190
235
  }
191
236
  return this._internalRpc;
192
237
  }
238
+ logDebugTiming(message, startMs) {
239
+ const level = this.options.logLevel?.toLowerCase();
240
+ if (level === "debug" || level === "all") {
241
+ process.stderr.write(`[copilot-sdk] ${message}. Elapsed=${Date.now() - startMs}ms
242
+ `);
243
+ }
244
+ }
193
245
  /**
194
246
  * Creates a new CopilotClient instance.
195
247
  *
@@ -376,8 +428,9 @@ class CopilotClient {
376
428
  *
377
429
  * This method performs graceful cleanup:
378
430
  * 1. Closes all active sessions (releases in-memory resources)
379
- * 2. Closes the JSON-RPC connection
380
- * 3. Terminates the CLI server process (if spawned by this client)
431
+ * 2. Requests runtime shutdown for SDK-owned CLI processes
432
+ * 3. Closes the JSON-RPC connection
433
+ * 4. Terminates the CLI server process (if spawned by this client)
381
434
  *
382
435
  * Note: session data on disk is preserved, so sessions can be resumed later.
383
436
  * To permanently remove session data before stopping, call
@@ -421,6 +474,34 @@ class CopilotClient {
421
474
  }
422
475
  }
423
476
  this.sessions.clear();
477
+ let runtimeShutdownCompleted = false;
478
+ if (this.connection && this.cliProcess && !this.isExternalServer) {
479
+ const runtimeShutdownStart = Date.now();
480
+ const shutdownPromise = this.rpc.runtime.shutdown();
481
+ void shutdownPromise.catch(() => void 0);
482
+ try {
483
+ await withTimeout(
484
+ shutdownPromise,
485
+ RUNTIME_SHUTDOWN_TIMEOUT_MS,
486
+ `runtime.shutdown timed out after ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms`
487
+ );
488
+ runtimeShutdownCompleted = true;
489
+ this.logDebugTiming(
490
+ "CopilotClient.stop runtime shutdown complete",
491
+ runtimeShutdownStart
492
+ );
493
+ } catch (error) {
494
+ this.logDebugTiming(
495
+ "CopilotClient.stop runtime shutdown failed",
496
+ runtimeShutdownStart
497
+ );
498
+ errors.push(
499
+ new Error(
500
+ `Failed to gracefully shut down runtime: ${error instanceof Error ? error.message : String(error)}`
501
+ )
502
+ );
503
+ }
504
+ }
424
505
  if (this.connection) {
425
506
  try {
426
507
  this.connection.dispose();
@@ -433,6 +514,7 @@ class CopilotClient {
433
514
  }
434
515
  this.connection = null;
435
516
  this._rpc = null;
517
+ this._internalRpc = null;
436
518
  }
437
519
  this.modelsCache = null;
438
520
  if (this.socket) {
@@ -457,12 +539,18 @@ class CopilotClient {
457
539
  const child = this.cliProcess;
458
540
  this.cliProcess = null;
459
541
  try {
460
- if (child.exitCode === null && child.signalCode === null) {
461
- const exited = new Promise((resolve) => {
462
- child.once("exit", () => resolve());
463
- });
464
- child.kill();
465
- await exited;
542
+ if (child.exitCode == null && child.signalCode == null) {
543
+ const exitedGracefully = runtimeShutdownCompleted ? await waitForChildExit(child, RUNTIME_SHUTDOWN_TIMEOUT_MS) : false;
544
+ if (!exitedGracefully) {
545
+ child.kill();
546
+ if (!await waitForChildExit(child, RUNTIME_SHUTDOWN_TIMEOUT_MS)) {
547
+ errors.push(
548
+ new Error(
549
+ `Timed out waiting for CLI process to exit after kill: ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms`
550
+ )
551
+ );
552
+ }
553
+ }
466
554
  }
467
555
  } catch (error) {
468
556
  errors.push(
@@ -532,6 +620,7 @@ class CopilotClient {
532
620
  }
533
621
  this.connection = null;
534
622
  this._rpc = null;
623
+ this._internalRpc = null;
535
624
  }
536
625
  this.modelsCache = null;
537
626
  if (this.socket) {
@@ -624,7 +713,8 @@ class CopilotClient {
624
713
  enableFileHooks: false,
625
714
  enableHostGitOperations: false,
626
715
  enableSessionStore: false,
627
- enableSkills: false
716
+ enableSkills: false,
717
+ memory: { enabled: false }
628
718
  };
629
719
  }
630
720
  return {};
@@ -774,7 +864,8 @@ class CopilotClient {
774
864
  description: tool.description,
775
865
  parameters: toJsonSchema(tool.parameters),
776
866
  overridesBuiltInTool: tool.overridesBuiltInTool,
777
- skipPermission: tool.skipPermission
867
+ skipPermission: tool.skipPermission,
868
+ defer: tool.defer
778
869
  })),
779
870
  canvases: config.canvases?.map((canvas) => canvas.declaration),
780
871
  requestCanvasRenderer: config.requestCanvasRenderer,
@@ -824,6 +915,7 @@ class CopilotClient {
824
915
  instructionDirectories: config.instructionDirectories,
825
916
  disabledSkills: config.disabledSkills,
826
917
  infiniteSessions: config.infiniteSessions,
918
+ memory: config.memory,
827
919
  gitHubToken: config.gitHubToken,
828
920
  remoteSession: config.remoteSession,
829
921
  cloud: config.cloud
@@ -942,7 +1034,8 @@ class CopilotClient {
942
1034
  description: tool.description,
943
1035
  parameters: toJsonSchema(tool.parameters),
944
1036
  overridesBuiltInTool: tool.overridesBuiltInTool,
945
- skipPermission: tool.skipPermission
1037
+ skipPermission: tool.skipPermission,
1038
+ defer: tool.defer
946
1039
  })),
947
1040
  canvases: config.canvases?.map((canvas) => canvas.declaration),
948
1041
  requestCanvasRenderer: config.requestCanvasRenderer,
@@ -987,6 +1080,7 @@ class CopilotClient {
987
1080
  instructionDirectories: config.instructionDirectories,
988
1081
  disabledSkills: config.disabledSkills,
989
1082
  infiniteSessions: config.infiniteSessions,
1083
+ memory: config.memory,
990
1084
  disableResume: config.suppressResumeEvent,
991
1085
  continuePendingWork: config.continuePendingWork,
992
1086
  gitHubToken: config.gitHubToken,
@@ -1389,6 +1483,8 @@ class CopilotClient {
1389
1483
  envWithoutNodeDebug.COPILOT_OTEL_ENABLED = "true";
1390
1484
  if (t.otlpEndpoint !== void 0)
1391
1485
  envWithoutNodeDebug.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
1486
+ if (t.otlpProtocol !== void 0)
1487
+ envWithoutNodeDebug.OTEL_EXPORTER_OTLP_PROTOCOL = t.otlpProtocol;
1392
1488
  if (t.filePath !== void 0)
1393
1489
  envWithoutNodeDebug.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
1394
1490
  if (t.exporterType !== void 0)