@github/copilot-sdk 1.0.13-preview.6 → 1.0.14-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.
@@ -266,13 +266,13 @@ function createServerRpc(connection) {
266
266
  /**
267
267
  * Enables installed plugins for new sessions.
268
268
  *
269
- * @param params Plugin names (or specs) to enable.
269
+ * @param params Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against.
270
270
  */
271
271
  enable: async (params) => connection.sendRequest("plugins.enable", params),
272
272
  /**
273
273
  * Disables installed plugins for new sessions.
274
274
  *
275
- * @param params Plugin names (or specs) to disable.
275
+ * @param params Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against.
276
276
  */
277
277
  disable: async (params) => connection.sendRequest("plugins.disable", params),
278
278
  /** @experimental */
@@ -438,7 +438,11 @@ function createServerRpc(connection) {
438
438
  *
439
439
  * @returns Validated device-managed settings discovered before a session exists.
440
440
  */
441
- read: async () => connection.sendRequest("managedSettings.read", {})
441
+ read: async () => connection.sendRequest("managedSettings.read", {}),
442
+ /**
443
+ * Force-refreshes enterprise managed settings for every account: wipes the persistent server-policy cache (the whole `<cacheHome>/managed-settings` directory) and drops this runtime process's in-memory retained server policy. It does not itself fetch policy — the effect is that the next time a session resolves managed settings for an account, that resolution re-fetches the account's org policy from the network instead of serving a cached response. Note that `managedSettings.read` returns only device/MDM settings and never triggers the account server-policy fetch, so a host implementing "sync account policy" should start a fresh session resolution rather than treat a subsequent `managedSettings.read` as the refreshed org policy. Mirrors the invalidation a sign-out performs, broadened from the one signing-out account to all of them; device/MDM layers describe the machine, not the account, and are left untouched. Rejects if the on-disk cache cannot be removed.
444
+ */
445
+ clearCache: async () => connection.sendRequest("managedSettings.clearCache", {})
442
446
  },
443
447
  /** @experimental */
444
448
  runtime: {
@@ -518,7 +522,15 @@ function createServerRpc(connection) {
518
522
  */
519
523
  list: async (params) => connection.sendRequest("sessions.list", params),
520
524
  /**
521
- * Reads a page of durable events directly from a local session's persisted journal without creating, resuming, or activating the session. The initial backward read uses a bounded tail scan for fast first paint; cursor continuations preserve the session event-log paging semantics. Persisted events may omit payloads that are reconstructed only for an active session.
525
+ * Reads client-owned metadata for multiple persisted local sessions without opening them. Results preserve request order and report missing, corrupt, unsupported, or temporarily unavailable sessions independently.
526
+ *
527
+ * @param params Bounded batch request for client-owned metadata from persisted local sessions.
528
+ *
529
+ * @returns Ordered client metadata outcomes for the requested local sessions.
530
+ */
531
+ getClientMetadata: async (params) => connection.sendRequest("sessions.getClientMetadata", params),
532
+ /**
533
+ * Reads a page of durable events directly from a local session's persisted journal without creating, resuming, or activating the session. The first read pins the currently opened journal generation and its byte-length boundary; opaque cursor continuations remain on that generation across runtime-owned compaction, truncation, and rewrite operations, which replace the live path atomically, and events appended after the boundary are excluded. For cold hydration, await the first successful page before activation and establish lossless live-event buffering before resume; merge subsequent live events by ID, preserving persisted order and letting live payloads win. Continuations are process-local, single-use capabilities bound to the originating session and storage context and must be paged sequentially; concurrent or repeated use of the same cursor expires that duplicate read rather than reading the generation twice. A complete snapshot has cursorStatus 'ok' and hasMore false. Snapshots expire after five idle minutes, with at most eight retained per process and idle-only eviction under pressure; completion and cancelled-worker exit release their handles. No transcript copy is created, but retained handles may keep replaced files' disk blocks alive until release. Pages have a soft 1 MiB serialized event-array budget including resolved binary assets; one oversized event is returned alone to guarantee progress. Working memory also includes a record/lookahead and asset resolution; resolving the first binary reference may scan the full pinned generation to build a bounded offset index. If the snapshot expires, is evicted, is cancelled before a continuation is established, or becomes unreadable after an observable unsupported in-place shortening, the continuation returns cursorStatus 'expired' with an empty terminal page and never falls back to a different generation. A missing or initially unreadable journal is an RPC error. Persisted history excludes ephemeral events and may omit payloads that are reconstructed only for an active session; use the active session event stream for post-resume live events.
522
534
  *
523
535
  * @param params Pagination options for reading an inactive or active local session's persisted event journal.
524
536
  *
@@ -799,7 +811,15 @@ function createSessionRpc(connection, sessionId) {
799
811
  *
800
812
  * @returns Managed sandbox enforcement state for a session.
801
813
  */
802
- getEnforcementStatus: async () => connection.sendRequest("session.sandbox.getEnforcementStatus", { sessionId })
814
+ getEnforcementStatus: async () => connection.sendRequest("session.sandbox.getEnforcementStatus", { sessionId }),
815
+ /**
816
+ * Disables sandboxing for the remainder of the current session and approves the referenced pending sandbox-bypass permission request. The request is rejected unless the exact request is still pending and the effective sandbox policy permits bypass.
817
+ *
818
+ * @param params Request to disable sandboxing for the current session while resolving an active sandbox-bypass permission prompt.
819
+ *
820
+ * @returns Result of attempting to disable sandboxing for the current session.
821
+ */
822
+ disableForSession: async (params) => connection.sendRequest("session.sandbox.disableForSession", { sessionId, ...params })
803
823
  },
804
824
  /**
805
825
  * Aborts the current agent turn.
@@ -963,6 +983,14 @@ function createSessionRpc(connection, sessionId) {
963
983
  * @returns Complete current or terminal factory run envelope.
964
984
  */
965
985
  cancel: async (params) => connection.sendRequest("session.factory.cancel", { sessionId, ...params }),
986
+ /**
987
+ * Pauses a running factory and returns its settled run envelope.
988
+ *
989
+ * @param params Parameters for pausing a running factory.
990
+ *
991
+ * @returns Complete current or terminal factory run envelope.
992
+ */
993
+ pause: async (params) => connection.sendRequest("session.factory.pause", { sessionId, ...params }),
966
994
  /**
967
995
  * Records a batch of ordered factory progress lines.
968
996
  *
@@ -1002,9 +1030,9 @@ function createSessionRpc(connection, sessionId) {
1002
1030
  /** @experimental */
1003
1031
  model: {
1004
1032
  /**
1005
- * Gets the currently selected model for the session.
1033
+ * Gets the session's authoritative model snapshot, including the committed Auto preference and any newer unclaimed Auto preference waiting for a future user turn.
1006
1034
  *
1007
- * @returns The currently selected model, reasoning effort, and context tier for the session. The context tier reflects `Session.getContextTier()`, restored from the session journal on resume.
1035
+ * @returns The session's authoritative model snapshot. Auto preference fields are configuration for the virtual `auto` model and do not change the selected model identifier. The context tier reflects `Session.getContextTier()`, restored from the session journal on resume.
1008
1036
  */
1009
1037
  getCurrent: async () => connection.sendRequest("session.model.getCurrent", { sessionId }),
1010
1038
  /**
@@ -1015,6 +1043,22 @@ function createSessionRpc(connection, sessionId) {
1015
1043
  * @returns The model identifier active on the session after the switch.
1016
1044
  */
1017
1045
  switchTo: async (params) => connection.sendRequest("session.model.switchTo", { sessionId, ...params }),
1046
+ /**
1047
+ * Requests an Auto preference change without changing the session's selected model. The latest unclaimed request wins; the runtime commits it only after a later prompt using the `auto` model mints a usable model and token pair. A `pending` response confirms that the request was accepted, not that it committed. Observe eventual success through `session.model_change`, failure through the ephemeral `session.auto_tier_switch_failed` event, or current unclaimed state through `session.model.getCurrent`.
1048
+ *
1049
+ * @param params An Auto preference request for the session. This updates Auto configuration only; it does not change the selected model to `auto`.
1050
+ *
1051
+ * @returns Immediate acknowledgement and Auto preference snapshot after a switch request. This result never implies that a pending preference committed.
1052
+ */
1053
+ switchAutoTier: async (params) => connection.sendRequest("session.model.switchAutoTier", { sessionId, ...params }),
1054
+ /**
1055
+ * Replaces or clears the host-supplied model allowlist for a running session.
1056
+ *
1057
+ * @param params Host-supplied exact model selection IDs to allow for this running session. CAPI IDs are intersected with repository `.github/allowed_models.txt` policy; provider-qualified IDs remain exempt from repository-only policy but are restricted by this host list. Omit or pass null to clear the host restriction; an explicit empty or disjoint list is rejected. Validation and pre-selection fallback failures preserve the previous restriction. Failures after a fallback selection commits retain the new restriction and selected model; callers should inspect current session state after such an error.
1058
+ *
1059
+ * @returns The applied host allowlist and effective session model policy after intersection.
1060
+ */
1061
+ setAllowedModels: async (params) => connection.sendRequest("session.model.setAllowedModels", { sessionId, ...params }),
1018
1062
  /**
1019
1063
  * Updates the session's reasoning effort without changing the selected model.
1020
1064
  *
@@ -1221,6 +1265,15 @@ function createSessionRpc(connection, sessionId) {
1221
1265
  diff: async (params) => connection.sendRequest("session.workspaces.diff", { sessionId, ...params })
1222
1266
  },
1223
1267
  /** @experimental */
1268
+ autopilotObjective: {
1269
+ /**
1270
+ * Reads the current canonical autopilot objective state for this session.
1271
+ *
1272
+ * @returns Canonical runtime state for the session's current autopilot objective.
1273
+ */
1274
+ getState: async () => connection.sendRequest("session.autopilotObjective.getState", { sessionId })
1275
+ },
1276
+ /** @experimental */
1224
1277
  completions: {
1225
1278
  /**
1226
1279
  * Gets the characters that should trigger host-driven completions for the session. Empty disables host-driven completions (e.g. local sessions, or a relay host that does not advertise them).
@@ -1251,7 +1304,7 @@ function createSessionRpc(connection, sessionId) {
1251
1304
  /**
1252
1305
  * Starts fleet mode by submitting the fleet orchestration prompt to the session.
1253
1306
  *
1254
- * @param params Optional user prompt to combine with the fleet orchestration instructions.
1307
+ * @param params Parameters for starting fleet orchestration: an optional user prompt combined with the fleet instructions, plus the send options forwarded to the resulting turn.
1255
1308
  *
1256
1309
  * @returns Indicates whether fleet mode was successfully activated.
1257
1310
  */
@@ -1314,6 +1367,22 @@ function createSessionRpc(connection, sessionId) {
1314
1367
  * @returns Background tasks currently tracked by the session.
1315
1368
  */
1316
1369
  list: async () => connection.sendRequest("session.tasks.list", { sessionId }),
1370
+ /**
1371
+ * Registers a client-owned task, or reclaims an orphaned task belonging to the same extension principal.
1372
+ *
1373
+ * @param params Registers or reclaims a client-owned task.
1374
+ *
1375
+ * @returns Result of registering or reclaiming a client-owned task.
1376
+ */
1377
+ register: async (params) => connection.sendRequest("session.tasks.register", { sessionId, ...params }),
1378
+ /**
1379
+ * Publishes generic progress or a terminal outcome for a client-owned task.
1380
+ *
1381
+ * @param params Updates a client-owned task.
1382
+ *
1383
+ * @returns Result of publishing a client-owned task update.
1384
+ */
1385
+ update: async (params) => connection.sendRequest("session.tasks.update", { sessionId, ...params }),
1317
1386
  /**
1318
1387
  * Refreshes metadata for any detached background shells the runtime knows about.
1319
1388
  *
@@ -1777,7 +1846,7 @@ function createSessionRpc(connection, sessionId) {
1777
1846
  */
1778
1847
  set: async (params) => connection.sendRequest("session.tools.set", { sessionId, ...params }),
1779
1848
  /**
1780
- * Updates the current session's live subagent settings after user settings change. The persisted user settings remain the source of truth for future sessions.
1849
+ * Sets the current session's live subagent settings override, which takes precedence over persisted user settings until cleared. Persisted user settings remain the source of truth for future sessions.
1781
1850
  *
1782
1851
  * @param params Subagent settings to apply to the current session
1783
1852
  *
@@ -2127,6 +2196,20 @@ function createSessionRpc(connection, sessionId) {
2127
2196
  * @returns Point-in-time snapshot of slow-changing session identifier and state fields
2128
2197
  */
2129
2198
  snapshot: async () => connection.sendRequest("session.metadata.snapshot", { sessionId }),
2199
+ /**
2200
+ * Returns the client-owned string metadata persisted with this local session. The metadata is not included in model context, events, telemetry, snapshots, or remote exports.
2201
+ *
2202
+ * @returns Client-owned, case-sensitive string metadata persisted with a local session. Clients should namespace keys by owner. Keys must be non-empty and at most 256 UTF-8 bytes; keys under `copilot/` and `github/` are reserved. Values may contain at most 16 KiB of UTF-8 data. A bag may contain at most 128 entries and its serialized sidecar may contain at most 64 KiB. The runtime stores but never interprets these values.
2203
+ */
2204
+ getClientMetadata: async () => connection.sendRequest("session.metadata.getClientMetadata", { sessionId }),
2205
+ /**
2206
+ * Atomically patches the client-owned string metadata persisted with this local session and returns the committed bag.
2207
+ *
2208
+ * @param params Atomic patch for client-owned session metadata. Operations apply in clear, remove, then set order. The resulting bag must satisfy the ClientMetadata entry and serialized-size limits. Local storage coordinates concurrent runtime processes; custom SessionFs providers must serialize writers that access the same session from multiple processes.
2209
+ *
2210
+ * @returns Client-owned, case-sensitive string metadata persisted with a local session. Clients should namespace keys by owner. Keys must be non-empty and at most 256 UTF-8 bytes; keys under `copilot/` and `github/` are reserved. Values may contain at most 16 KiB of UTF-8 data. A bag may contain at most 128 entries and its serialized sidecar may contain at most 64 KiB. The runtime stores but never interprets these values.
2211
+ */
2212
+ updateClientMetadata: async (params) => connection.sendRequest("session.metadata.updateClientMetadata", { sessionId, ...params }),
2130
2213
  /**
2131
2214
  * Reports whether the local session is currently processing user/agent messages.
2132
2215
  *
@@ -2584,7 +2667,13 @@ function createInternalSessionRpc(connection, sessionId) {
2584
2667
  *
2585
2668
  * @returns Resolved persisted factory identity and resumed run envelope.
2586
2669
  */
2587
- resumeFromTool: async (params) => connection.sendRequest("session.factory.resumeFromTool", { sessionId, ...params })
2670
+ resumeFromTool: async (params) => connection.sendRequest("session.factory.resumeFromTool", { sessionId, ...params }),
2671
+ /**
2672
+ * Atomically pauses an owned factory attempt at a durable checkpoint.
2673
+ *
2674
+ * @param params Parameters for an owned durable pause checkpoint.
2675
+ */
2676
+ pauseAtCheckpoint: async (params) => connection.sendRequest("session.factory.pauseAtCheckpoint", { sessionId, ...params })
2588
2677
  },
2589
2678
  /** @experimental */
2590
2679
  model: {
@@ -2782,6 +2871,11 @@ function registerClientSessionApiHandlers(connection, getHandlers) {
2782
2871
  if (!handler) throw new Error(`No factory handler registered for session: ${params.sessionId}`);
2783
2872
  return handler.abort(params);
2784
2873
  });
2874
+ connection.onRequest("tasks.cancel", async (params) => {
2875
+ const handler = getHandlers(params.sessionId).tasks;
2876
+ if (!handler) throw new Error(`No tasks handler registered for session: ${params.sessionId}`);
2877
+ return handler.cancel(params);
2878
+ });
2785
2879
  connection.onRequest("sessionFs.readFile", async (params) => {
2786
2880
  const handler = getHandlers(params.sessionId).sessionFs;
2787
2881
  if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
@@ -249,6 +249,7 @@ class CopilotSession {
249
249
  eventHandlers = /* @__PURE__ */ new Set();
250
250
  typedEventHandlers = /* @__PURE__ */ new Map();
251
251
  toolHandlers = /* @__PURE__ */ new Map();
252
+ pendingExternalTools = /* @__PURE__ */ new Map();
252
253
  canvases = /* @__PURE__ */ new Map();
253
254
  bearerTokenProviders = /* @__PURE__ */ new Map();
254
255
  commandHandlers = /* @__PURE__ */ new Map();
@@ -268,6 +269,7 @@ class CopilotSession {
268
269
  _capabilities = {};
269
270
  openCanvasInstances = [];
270
271
  disconnected = false;
272
+ disconnecting = false;
271
273
  onDisconnected;
272
274
  /** @internal Client session API handlers, populated by CopilotClient during create/resume. */
273
275
  clientSessionApis = {};
@@ -467,6 +469,7 @@ class CopilotSession {
467
469
  ...await (0, import_telemetry.getTraceContext)(this.traceContextProvider),
468
470
  sessionId: this.sessionId,
469
471
  prompt: options.prompt,
472
+ source: options.source,
470
473
  displayPrompt: options.displayPrompt,
471
474
  attachments: options.attachments,
472
475
  mode: options.mode,
@@ -525,6 +528,10 @@ class CopilotSession {
525
528
  return;
526
529
  }
527
530
  this.disconnected = true;
531
+ for (const controller of this.pendingExternalTools.values()) {
532
+ controller.abort();
533
+ }
534
+ this.pendingExternalTools.clear();
528
535
  this._runOnDisconnected();
529
536
  this.eventHandlers.clear();
530
537
  this.typedEventHandlers.clear();
@@ -628,6 +635,15 @@ class CopilotSession {
628
635
  tracestate
629
636
  );
630
637
  }
638
+ } else if (event.type === "external_tool.completed") {
639
+ const { requestId } = event.data;
640
+ if (requestId) {
641
+ const controller = this.pendingExternalTools.get(requestId);
642
+ if (controller) {
643
+ this.pendingExternalTools.delete(requestId);
644
+ controller.abort();
645
+ }
646
+ }
631
647
  } else if (event.type === "permission.requested") {
632
648
  const { requestId, permissionRequest, resolvedByHook } = event.data;
633
649
  if (resolvedByHook) {
@@ -708,16 +724,35 @@ class CopilotSession {
708
724
  * @internal
709
725
  */
710
726
  async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler, traceparent, tracestate) {
727
+ const controller = new AbortController();
728
+ if (this.disconnected || this.pendingExternalTools.has(requestId)) {
729
+ return;
730
+ }
731
+ this.pendingExternalTools.set(requestId, controller);
711
732
  try {
712
733
  let availableTools;
713
734
  if (toolName === TOOL_SEARCH_TOOL_NAME) {
735
+ if (controller.signal.aborted) {
736
+ return;
737
+ }
738
+ const aborted = new Promise((resolve) => {
739
+ controller.signal.addEventListener("abort", () => resolve(void 0), {
740
+ once: true
741
+ });
742
+ });
714
743
  try {
715
- const metadata = await this.rpc.tools.getCurrentMetadata();
716
- availableTools = metadata.tools ?? void 0;
744
+ const metadata = await Promise.race([
745
+ this.rpc.tools.getCurrentMetadata(),
746
+ aborted
747
+ ]);
748
+ availableTools = metadata?.tools ?? void 0;
717
749
  } catch {
718
750
  availableTools = void 0;
719
751
  }
720
752
  }
753
+ if (controller.signal.aborted) {
754
+ return;
755
+ }
721
756
  const rawResult = await handler(args, {
722
757
  sessionId: this.sessionId,
723
758
  toolCallId,
@@ -725,7 +760,8 @@ class CopilotSession {
725
760
  arguments: args,
726
761
  availableTools,
727
762
  traceparent,
728
- tracestate
763
+ tracestate,
764
+ signal: controller.signal
729
765
  });
730
766
  let result;
731
767
  if (rawResult == null) {
@@ -737,12 +773,12 @@ class CopilotSession {
737
773
  } else {
738
774
  result = JSON.stringify(rawResult);
739
775
  }
740
- if (this.disconnected) {
776
+ if (!this._claimExternalTool(requestId, controller)) {
741
777
  return;
742
778
  }
743
779
  await this.rpc.tools.handlePendingToolCall({ requestId, result });
744
780
  } catch (error) {
745
- if (this.disconnected) {
781
+ if (!this._claimExternalTool(requestId, controller)) {
746
782
  return;
747
783
  }
748
784
  const message = error instanceof Error ? error.message : String(error);
@@ -753,7 +789,19 @@ class CopilotSession {
753
789
  throw rpcError;
754
790
  }
755
791
  }
792
+ } finally {
793
+ if (this.pendingExternalTools.get(requestId) === controller) {
794
+ this.pendingExternalTools.delete(requestId);
795
+ }
796
+ controller.abort();
797
+ }
798
+ }
799
+ _claimExternalTool(requestId, controller) {
800
+ if (this.disconnected || this.pendingExternalTools.get(requestId) !== controller) {
801
+ return false;
756
802
  }
803
+ this.pendingExternalTools.delete(requestId);
804
+ return true;
757
805
  }
758
806
  /**
759
807
  * Executes a permission handler and sends the result back via RPC.
@@ -1507,13 +1555,27 @@ class CopilotSession {
1507
1555
  * ```
1508
1556
  */
1509
1557
  async disconnect() {
1510
- if (this.disconnected) {
1558
+ if (this.disconnected || this.disconnecting) {
1511
1559
  return;
1512
1560
  }
1513
- await this.connection.sendRequest("session.destroy", {
1514
- sessionId: this.sessionId
1515
- });
1516
- this._markDisconnected();
1561
+ this.disconnecting = true;
1562
+ try {
1563
+ let response = { success: false };
1564
+ for (let attempt = 0; attempt < 2 && !response.success; attempt++) {
1565
+ response = await this.connection.sendRequest("session.detach", {
1566
+ sessionId: this.sessionId
1567
+ });
1568
+ }
1569
+ if (!response.success) {
1570
+ throw new Error(
1571
+ `Failed to disconnect session ${this.sessionId}: ${response.error || "Unknown error"}`
1572
+ );
1573
+ }
1574
+ this._markDisconnected();
1575
+ } catch (error) {
1576
+ this.disconnecting = false;
1577
+ throw error;
1578
+ }
1517
1579
  }
1518
1580
  /** Enables `await using session = ...` syntax for automatic cleanup. */
1519
1581
  async [Symbol.asyncDispose]() {
@@ -1555,11 +1617,48 @@ class CopilotSession {
1555
1617
  * ```typescript
1556
1618
  * await session.setModel("gpt-5.4");
1557
1619
  * await session.setModel("claude-sonnet-4.6", { reasoningEffort: "high" });
1620
+ *
1621
+ * // Select the Auto model and its routing preference in one call.
1622
+ * await session.setModel("auto", { autoTier: "intelligence" });
1558
1623
  * ```
1559
1624
  */
1560
1625
  async setModel(model, options) {
1561
1626
  await this.rpc.model.switchTo({ modelId: model, ...options });
1562
1627
  }
1628
+ /**
1629
+ * Change the Auto routing preference without changing the selected model.
1630
+ *
1631
+ * The runtime does not apply the preference immediately. It records the
1632
+ * request and commits it only when a later user turn using the `auto` model
1633
+ * successfully obtains a usable model from the provider. A `pending` status
1634
+ * therefore confirms that the request was accepted, not that it took effect.
1635
+ *
1636
+ * Watch for the outcome through the `session.model_change` event on success,
1637
+ * or the ephemeral `session.auto_tier_switch_failed` event on failure. You
1638
+ * can also read the current committed and in-flight state at any time with
1639
+ * `session.rpc.model.getCurrent()`.
1640
+ *
1641
+ * Only the most recent request survives: issuing a new request replaces any
1642
+ * earlier one that has not yet been claimed by a turn.
1643
+ *
1644
+ * @param autoTier - Routing preference to activate, or `null` to return to
1645
+ * the provider's default Auto routing
1646
+ * @returns The runtime's immediate acknowledgement and Auto preference snapshot
1647
+ *
1648
+ * @experimental Part of an experimental Auto routing surface and may change
1649
+ * or be removed in a future release.
1650
+ *
1651
+ * @example
1652
+ * ```typescript
1653
+ * const result = await session.setAutoTier("intelligence");
1654
+ * if (result.status === "pending") {
1655
+ * // Takes effect on a later turn that uses the `auto` model.
1656
+ * }
1657
+ * ```
1658
+ */
1659
+ async setAutoTier(autoTier) {
1660
+ return await this.rpc.model.switchAutoTier({ autoTier });
1661
+ }
1563
1662
  /**
1564
1663
  * Log a message to the session timeline.
1565
1664
  * The message appears in the session event stream and is visible to SDK consumers
@@ -1,2 +1,2 @@
1
- export declare const COPILOT_CLI_VERSION = "1.0.83-3";
1
+ export declare const COPILOT_CLI_VERSION = "1.0.84-5";
2
2
  export declare const COPILOT_CLI_USE_NPM_PACKAGE = false;
@@ -1,4 +1,4 @@
1
- const COPILOT_CLI_VERSION = "1.0.83-3";
1
+ const COPILOT_CLI_VERSION = "1.0.84-5";
2
2
  const COPILOT_CLI_USE_NPM_PACKAGE = false;
3
3
  export {
4
4
  COPILOT_CLI_USE_NPM_PACKAGE,
package/dist/client.d.ts CHANGED
@@ -7,10 +7,13 @@ export declare class CopilotClient {
7
7
  private ffiHost;
8
8
  private connection;
9
9
  private messageWriter;
10
+ private connectionClosed;
10
11
  private socket;
11
12
  private runtimePort;
12
13
  private actualHost;
13
14
  private state;
15
+ /** Shared in-flight start; concurrent callers await it instead of spawning another CLI. */
16
+ private startPromise;
14
17
  private sessions;
15
18
  private stderrBuffer;
16
19
  /** Resolved connection mode chosen in the constructor. */
@@ -121,6 +124,7 @@ export declare class CopilotClient {
121
124
  * ```
122
125
  */
123
126
  start(): Promise<void>;
127
+ private doStart;
124
128
  /**
125
129
  * Stops the CLI server and closes all active sessions.
126
130
  *
package/dist/client.js CHANGED
@@ -214,10 +214,13 @@ class CopilotClient {
214
214
  ffiHost = null;
215
215
  connection = null;
216
216
  messageWriter = null;
217
+ connectionClosed = false;
217
218
  socket = null;
218
219
  runtimePort = null;
219
220
  actualHost = "localhost";
220
221
  state = "disconnected";
222
+ /** Shared in-flight start; concurrent callers await it instead of spawning another CLI. */
223
+ startPromise = null;
221
224
  sessions = /* @__PURE__ */ new Map();
222
225
  stderrBuffer = "";
223
226
  // Captures CLI stderr for error messages
@@ -595,7 +598,19 @@ class CopilotClient {
595
598
  if (this.state === "connected") {
596
599
  return;
597
600
  }
601
+ if (this.startPromise) {
602
+ return this.startPromise;
603
+ }
604
+ this.startPromise = this.doStart();
605
+ try {
606
+ await this.startPromise;
607
+ } finally {
608
+ this.startPromise = null;
609
+ }
610
+ }
611
+ async doStart() {
598
612
  this.forceStopping = false;
613
+ this.connectionClosed = false;
599
614
  this.processTransportError = null;
600
615
  this.state = "connecting";
601
616
  try {
@@ -694,7 +709,7 @@ class CopilotClient {
694
709
  }
695
710
  this.sessions.clear();
696
711
  this.githubTokenProviders.clear();
697
- if (this.connection && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
712
+ if (this.connection && !this.connectionClosed && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
698
713
  const runtimeShutdownStart = Date.now();
699
714
  const shutdownPromise = this.rpc.runtime.shutdown();
700
715
  void shutdownPromise.catch(() => void 0);
@@ -783,7 +798,7 @@ class CopilotClient {
783
798
  const host = this.ffiHost;
784
799
  this.ffiHost = null;
785
800
  try {
786
- host.dispose();
801
+ await host.dispose();
787
802
  } catch (error) {
788
803
  errors.push(
789
804
  new Error(
@@ -878,11 +893,12 @@ class CopilotClient {
878
893
  this.cliProcess = null;
879
894
  }
880
895
  if (this.ffiHost) {
896
+ const host = this.ffiHost;
897
+ this.ffiHost = null;
881
898
  try {
882
- this.ffiHost.dispose();
899
+ await host.dispose();
883
900
  } catch {
884
901
  }
885
- this.ffiHost = null;
886
902
  }
887
903
  if (this.cliStartTimeout) {
888
904
  clearTimeout(this.cliStartTimeout);
@@ -1199,6 +1215,7 @@ class CopilotClient {
1199
1215
  ...this.onGitHubTelemetry != null ? { enableGitHubTelemetryForwarding: true } : {},
1200
1216
  mcpServers: toWireMcpServers(config.mcpServers),
1201
1217
  mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
1218
+ authClientIdMetadataUrl: config.authClientIdMetadataUrl,
1202
1219
  envValueMode: "direct",
1203
1220
  customAgents: toWireCustomAgents(config.customAgents),
1204
1221
  customAgentsLocalOnly: config.customAgentsLocalOnly,
@@ -1259,6 +1276,7 @@ class CopilotClient {
1259
1276
  await this.updateSessionOptionsForMode(session, config);
1260
1277
  this.commitGitHubTokenProvider(returnedSessionId, gitHubTokenProviderRegistrationId);
1261
1278
  } catch (e) {
1279
+ session?._markDisconnected();
1262
1280
  if (registeredId !== void 0) {
1263
1281
  this.sessions.delete(registeredId);
1264
1282
  }
@@ -1443,6 +1461,7 @@ class CopilotClient {
1443
1461
  ...this.onGitHubTelemetry != null ? { enableGitHubTelemetryForwarding: true } : {},
1444
1462
  mcpServers: toWireMcpServers(config.mcpServers),
1445
1463
  mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
1464
+ authClientIdMetadataUrl: config.authClientIdMetadataUrl,
1446
1465
  envValueMode: "direct",
1447
1466
  customAgents: toWireCustomAgents(config.customAgents),
1448
1467
  customAgentsLocalOnly: config.customAgentsLocalOnly,
@@ -1491,6 +1510,7 @@ class CopilotClient {
1491
1510
  await this.updateSessionOptionsForMode(session, config);
1492
1511
  this.commitGitHubTokenProvider(sessionId, gitHubTokenProviderRegistrationId);
1493
1512
  } catch (e) {
1513
+ session._markDisconnected();
1494
1514
  this.sessions.delete(sessionId);
1495
1515
  if (gitHubTokenProviderRegistrationId !== void 0) {
1496
1516
  this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId);
@@ -1606,7 +1626,10 @@ class CopilotClient {
1606
1626
  const raceAgainstExit = (p) => this.processExitPromise ? Promise.race([p, this.processExitPromise]) : p;
1607
1627
  let serverVersion;
1608
1628
  try {
1609
- const connectParams = { token: this.effectiveConnectionToken };
1629
+ const connectParams = {
1630
+ token: this.effectiveConnectionToken,
1631
+ supportedTaskKinds: ["agent", "client", "shell"]
1632
+ };
1610
1633
  if (this.onGitHubTelemetry != null) {
1611
1634
  connectParams.enableGitHubTelemetryForwarding = true;
1612
1635
  }
@@ -2237,12 +2260,24 @@ stderr: ${stderrOutput}` : ""}`
2237
2260
  return await this.handleHooksInvoke(params);
2238
2261
  }
2239
2262
  );
2240
- this.connection.onClose(() => {
2263
+ const connection = this.connection;
2264
+ const markDisconnected = () => {
2265
+ if (this.connection !== connection) {
2266
+ return;
2267
+ }
2268
+ this.connectionClosed = true;
2241
2269
  this.state = "disconnected";
2270
+ for (const session of this.sessions.values()) {
2271
+ session._markDisconnected();
2272
+ }
2273
+ this.sessions.clear();
2242
2274
  this.githubTokenProviders.clear();
2243
- });
2244
- this.connection.onError((_error) => {
2245
- this.state = "disconnected";
2275
+ };
2276
+ this.connection.onClose(markDisconnected);
2277
+ this.connection.onError(() => {
2278
+ if (this.connection === connection) {
2279
+ this.state = "disconnected";
2280
+ }
2246
2281
  });
2247
2282
  }
2248
2283
  handleSessionEventNotification(notification) {
@@ -4,12 +4,16 @@ export declare class FfiRuntimeHost {
4
4
  private readonly cliEntrypoint;
5
5
  private readonly environment;
6
6
  private readonly args;
7
+ private static readonly quarantinedHosts;
7
8
  private readonly lib;
8
9
  private serverId;
9
10
  private connectionId;
10
11
  private disposed;
12
+ private starting;
11
13
  private outboundCallback;
12
14
  private keepAliveTimer;
15
+ private cleanupRetryTimer;
16
+ private cleanupInProgress;
13
17
  /** The stream JSON-RPC reads server→client frames from. */
14
18
  readonly receiveStream: PassThrough;
15
19
  /** The stream JSON-RPC writes client→server frames to. */
@@ -30,6 +34,8 @@ export declare class FfiRuntimeHost {
30
34
  */
31
35
  private feedInbound;
32
36
  private unregisterCallback;
33
- /** Closes the FFI connection, shuts down the native host, and releases resources. */
34
- dispose(): void;
37
+ private scheduleCleanupRetry;
38
+ private tryFinalizeCleanup;
39
+ /** Awaits the initial cleanup attempt; a non-quiescent close is retried in the background. */
40
+ dispose(): Promise<void>;
35
41
  }