@github/copilot-sdk 1.0.16 → 1.0.17-preview.1

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
@@ -9,8 +9,8 @@ To use the SDK, you'll need:
9
9
  - Node.js ^20.19.0 or >=22.12.0
10
10
 
11
11
  The SDK uses an optional `@github/copilot-sdk-<platform>` package containing the
12
- Copilot CLI runtime for the host platform. These packages are built from
13
- verified `github/copilot-cli` release assets when the SDK is published, so
12
+ Copilot CLI runtime for the host platform. These packages contain the verified
13
+ runtime artifacts for that SDK release, so
14
14
  starting the SDK performs no runtime download. Set `COPILOT_CLI_PATH` to use an
15
15
  existing installation instead.
16
16
 
@@ -20,6 +20,10 @@ replaces both with the CLI version published for that snapshot.
20
20
 
21
21
  `npm run pack:release` builds the main package and all platform packages. Set
22
22
  `COPILOT_CLI_DOWNLOAD_BASE_URL` to use a release mirror while packaging.
23
+ Standalone source builds acquire stable/prerelease runtime assets from
24
+ `github/copilot-cli` at `v<runtime-version>`, and canonical unstable assets from
25
+ `github/copilot-sdk` at `runtime-<runtime-version>`. Both checksum and package
26
+ downloads use that release location; mirror overrides retain the same tag scheme.
23
27
  Release workflows instead set `COPILOT_SDK_RUNTIME_PACKAGE_DIR` to a directory
24
28
  containing validated runtime npm package roots named for all eight platforms.
25
29
  This keeps `COPILOT_CLI_USE_NPM_PACKAGE` false and embeds those runtime files in
@@ -368,6 +372,12 @@ Initial acquisition runs during session creation or resume. Cancellation, provid
368
372
  ##### `resumeSession(sessionId: string, config?: ResumeSessionConfig): Promise<CopilotSession>`
369
373
 
370
374
  Resume an existing session. Returns the session with `workspacePath` populated if infinite sessions were enabled.
375
+ `allowTranscriptRecovery` controls whether the runtime may repair a damaged transcript during
376
+ resume. It defaults to `true` in all modes; set it to `false` to reject recovery.
377
+ When repair occurs, inspect `session.transcriptRecovery` for the planned backup path,
378
+ invalid line numbers, and whether `session.start` was moved. The backup
379
+ is written when the repaired transcript is next appended, not during resume. Disabling recovery
380
+ still permits adding a missing newline after an intact final record; it rejects torn tails.
371
381
 
372
382
  ##### `ping(message?: string): Promise<{ message: string; timestamp: string }>`
373
383
 
@@ -22,7 +22,7 @@ __export(cliVersion_exports, {
22
22
  COPILOT_CLI_VERSION: () => COPILOT_CLI_VERSION
23
23
  });
24
24
  module.exports = __toCommonJS(cliVersion_exports);
25
- const COPILOT_CLI_VERSION = "1.0.90";
25
+ const COPILOT_CLI_VERSION = "1.0.92-1";
26
26
  const COPILOT_CLI_USE_NPM_PACKAGE = false;
27
27
  // Annotate the CommonJS export names for ESM import in node:
28
28
  0 && (module.exports = {
@@ -282,6 +282,7 @@ class CopilotClient {
282
282
  requestAdapter = null;
283
283
  messageWriter = null;
284
284
  connectionClosed = false;
285
+ connectionEpoch = 0;
285
286
  socket = null;
286
287
  runtimePort = null;
287
288
  actualHost = "localhost";
@@ -691,6 +692,7 @@ class CopilotClient {
691
692
  }
692
693
  async doStart() {
693
694
  this.forceStopping = false;
695
+ this.connectionEpoch++;
694
696
  this.connectionClosed = false;
695
697
  this.processTransportError = null;
696
698
  this.state = "connecting";
@@ -726,6 +728,9 @@ class CopilotClient {
726
728
  if (this.requestHandler) {
727
729
  await this.connection.sendRequest("llmInference.setProvider", {});
728
730
  }
731
+ if (this.connectionClosed) {
732
+ throw new Error("CLI server connection closed during startup");
733
+ }
729
734
  this.state = "connected";
730
735
  } catch (error) {
731
736
  const startupError = this.processTransportError ?? error;
@@ -845,10 +850,23 @@ class CopilotClient {
845
850
  this.socket = null;
846
851
  try {
847
852
  if (!socket.destroyed) {
848
- await new Promise((resolve2) => {
849
- socket.once("close", () => resolve2());
850
- socket.end();
851
- });
853
+ let timeout;
854
+ try {
855
+ await new Promise((resolve2) => {
856
+ socket.once("close", () => resolve2());
857
+ if (this.connectionClosed) {
858
+ socket.destroy();
859
+ } else {
860
+ timeout = setTimeout(
861
+ () => socket.destroy(),
862
+ RUNTIME_SHUTDOWN_TIMEOUT_MS
863
+ );
864
+ socket.end();
865
+ }
866
+ });
867
+ } finally {
868
+ if (timeout !== void 0) clearTimeout(timeout);
869
+ }
852
870
  }
853
871
  } catch (error) {
854
872
  errors.push(
@@ -1511,6 +1529,7 @@ class CopilotClient {
1511
1529
  allowedModels: config.allowedModels,
1512
1530
  ...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
1513
1531
  sessionId,
1532
+ allowTranscriptRecovery: config.allowTranscriptRecovery,
1514
1533
  clientName: config.clientName,
1515
1534
  model: config.model,
1516
1535
  reasoningEffort: config.reasoningEffort,
@@ -1617,10 +1636,11 @@ class CopilotClient {
1617
1636
  }
1618
1637
  }
1619
1638
  }
1620
- const { workspacePath, capabilities, openCanvases } = response;
1639
+ const { workspacePath, capabilities, openCanvases, transcriptRecovery } = response;
1621
1640
  session["_workspacePath"] = workspacePath;
1622
1641
  session.setCapabilities(capabilities);
1623
1642
  session.setOpenCanvases(openCanvases ?? []);
1643
+ session.setTranscriptRecovery(transcriptRecovery);
1624
1644
  if (config.onMcpAuthRequest) {
1625
1645
  await this.connection.sendRequest("session.eventLog.registerInterest", {
1626
1646
  sessionId,
@@ -2609,8 +2629,13 @@ stderr: ${stderrOutput}` : ""}`
2609
2629
  );
2610
2630
  const connection = this.connection;
2611
2631
  const messageWriter = this.messageWriter;
2632
+ const cliProcess = this.isExternalServer ? null : this.cliProcess;
2633
+ const connectionEpoch = this.connectionEpoch;
2634
+ const isCurrentConnection = () => connectionEpoch === this.connectionEpoch && this.connection === connection && (cliProcess === null || this.cliProcess === cliProcess);
2635
+ let disconnecting = false;
2636
+ let exitFallback;
2612
2637
  const markDisconnected = () => {
2613
- if (this.connection !== connection) {
2638
+ if (!isCurrentConnection()) {
2614
2639
  connection.dispose();
2615
2640
  return;
2616
2641
  }
@@ -2624,13 +2649,48 @@ stderr: ${stderrOutput}` : ""}`
2624
2649
  this.githubTokenProviders.clear();
2625
2650
  this.requestAdapter?.cancelPending();
2626
2651
  connection.dispose();
2652
+ this.socket?.destroy();
2653
+ this.connection = null;
2654
+ this.messageWriter = null;
2655
+ this._rpc = null;
2656
+ this._internalRpc = null;
2657
+ this.modelsCache = null;
2627
2658
  };
2628
- this.connection.onClose(() => {
2659
+ const disconnectAfterDrain = () => {
2660
+ if (exitFallback !== void 0) {
2661
+ clearTimeout(exitFallback);
2662
+ exitFallback = void 0;
2663
+ }
2664
+ if (!isCurrentConnection()) {
2665
+ connection.dispose();
2666
+ return;
2667
+ }
2668
+ if (disconnecting) return;
2669
+ disconnecting = true;
2670
+ this.connectionClosed = true;
2629
2671
  if (messageWriter) messageWriter.suppressWriteErrors = true;
2630
2672
  void connection.drain().then(markDisconnected);
2631
- });
2673
+ };
2674
+ this.connection.onClose(disconnectAfterDrain);
2675
+ if (cliProcess) {
2676
+ cliProcess.once("exit", () => {
2677
+ if (!isCurrentConnection()) {
2678
+ connection.dispose();
2679
+ return;
2680
+ }
2681
+ if (disconnecting) return;
2682
+ if (this.socket) {
2683
+ disconnectAfterDrain();
2684
+ return;
2685
+ }
2686
+ this.connectionClosed = true;
2687
+ if (messageWriter) messageWriter.suppressWriteErrors = true;
2688
+ exitFallback = setTimeout(disconnectAfterDrain, RUNTIME_SHUTDOWN_TIMEOUT_MS);
2689
+ exitFallback.unref();
2690
+ });
2691
+ }
2632
2692
  this.connection.onError(() => {
2633
- if (this.connection === connection) {
2693
+ if (connectionEpoch === this.connectionEpoch && this.connection === connection) {
2634
2694
  this.state = "disconnected";
2635
2695
  }
2636
2696
  });
@@ -208,7 +208,7 @@ class FfiRuntimeHost {
208
208
  );
209
209
  if (!this.connectionId) {
210
210
  this.unregisterCallback();
211
- this.lib.hostShutdown(this.serverId);
211
+ await this.shutdownHost();
212
212
  this.serverId = 0;
213
213
  throw new Error("copilot_runtime_connection_open failed.");
214
214
  }
@@ -217,7 +217,7 @@ class FfiRuntimeHost {
217
217
  } finally {
218
218
  this.starting = false;
219
219
  if (this.disposed) {
220
- void this.tryFinalizeCleanup();
220
+ await this.tryFinalizeCleanup();
221
221
  }
222
222
  }
223
223
  }
@@ -256,6 +256,17 @@ class FfiRuntimeHost {
256
256
  );
257
257
  }
258
258
  }
259
+ shutdownHost() {
260
+ return new Promise((resolvePromise, rejectPromise) => {
261
+ this.lib.hostShutdown.async(this.serverId, (error, result) => {
262
+ if (error) {
263
+ rejectPromise(error);
264
+ } else {
265
+ resolvePromise(result);
266
+ }
267
+ });
268
+ });
269
+ }
259
270
  unregisterCallback() {
260
271
  if (this.outboundCallback === void 0) {
261
272
  return true;
@@ -328,7 +339,7 @@ class FfiRuntimeHost {
328
339
  }
329
340
  if (this.serverId) {
330
341
  try {
331
- if (!this.lib.hostShutdown(this.serverId)) {
342
+ if (!await this.shutdownHost()) {
332
343
  console.error(
333
344
  `In-process FFI host shutdown did not recognize server ${this.serverId}.`
334
345
  );