@github/copilot-language-server 1.552.0 → 1.554.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.
Files changed (75) hide show
  1. package/README.md +94 -1
  2. package/dist/contextWorker.js +14 -14
  3. package/dist/main.js +1030 -1020
  4. package/dist/node_modules/@github/copilot-sdk/dist/cjs/cliVersion.js +1 -1
  5. package/dist/node_modules/@github/copilot-sdk/dist/cjs/client.js +507 -37
  6. package/dist/node_modules/@github/copilot-sdk/dist/cjs/copilotRequestHandler.js +156 -20
  7. package/dist/node_modules/@github/copilot-sdk/dist/cjs/extension.js +13 -9
  8. package/dist/node_modules/@github/copilot-sdk/dist/cjs/ffiRuntimeHost.js +14 -3
  9. package/dist/node_modules/@github/copilot-sdk/dist/cjs/generated/rpc.js +1366 -304
  10. package/dist/node_modules/@github/copilot-sdk/dist/cjs/host.js +67 -0
  11. package/dist/node_modules/@github/copilot-sdk/dist/cjs/index.js +12 -7
  12. package/dist/node_modules/@github/copilot-sdk/dist/cjs/installationConfirmation.js +98 -0
  13. package/dist/node_modules/@github/copilot-sdk/dist/cjs/runtimeArtifacts.js +1 -0
  14. package/dist/node_modules/@github/copilot-sdk/dist/cjs/schema.js +40 -0
  15. package/dist/node_modules/@github/copilot-sdk/dist/cjs/session.js +474 -151
  16. package/dist/node_modules/@github/copilot-sdk/dist/cjs/sessionFsProvider.js +57 -0
  17. package/dist/node_modules/@github/copilot-sdk/dist/cjs/types.js +5 -2
  18. package/dist/node_modules/@github/copilot-sdk/dist/cjs/{factory.js → workflow.js} +33 -33
  19. package/dist/node_modules/@github/copilot-sdk/package.json +27 -16
  20. package/dist/node_modules/zod/locales/package.json +1 -2
  21. package/dist/node_modules/zod/mini/package.json +1 -2
  22. package/dist/node_modules/zod/package.json +1 -1
  23. package/dist/node_modules/zod/v3/package.json +1 -2
  24. package/dist/node_modules/zod/v4/classic/errors.js +2 -2
  25. package/dist/node_modules/zod/v4/classic/from-json-schema.cjs +16 -31
  26. package/dist/node_modules/zod/v4/classic/from-json-schema.js +16 -31
  27. package/dist/node_modules/zod/v4/classic/package.json +1 -2
  28. package/dist/node_modules/zod/v4/classic/schemas.cjs +119 -358
  29. package/dist/node_modules/zod/v4/classic/schemas.js +118 -356
  30. package/dist/node_modules/zod/v4/core/api.cjs +2 -7
  31. package/dist/node_modules/zod/v4/core/api.js +2 -7
  32. package/dist/node_modules/zod/v4/core/core.cjs +1 -3
  33. package/dist/node_modules/zod/v4/core/core.js +2 -4
  34. package/dist/node_modules/zod/v4/core/errors.cjs +23 -26
  35. package/dist/node_modules/zod/v4/core/errors.js +23 -26
  36. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +20 -16
  37. package/dist/node_modules/zod/v4/core/json-schema-processors.js +20 -16
  38. package/dist/node_modules/zod/v4/core/package.json +1 -2
  39. package/dist/node_modules/zod/v4/core/parse.cjs +7 -7
  40. package/dist/node_modules/zod/v4/core/parse.js +7 -7
  41. package/dist/node_modules/zod/v4/core/regexes.cjs +3 -9
  42. package/dist/node_modules/zod/v4/core/regexes.js +1 -7
  43. package/dist/node_modules/zod/v4/core/schemas.cjs +65 -211
  44. package/dist/node_modules/zod/v4/core/schemas.js +64 -210
  45. package/dist/node_modules/zod/v4/core/to-json-schema.cjs +1 -12
  46. package/dist/node_modules/zod/v4/core/to-json-schema.js +1 -12
  47. package/dist/node_modules/zod/v4/core/util.cjs +30 -54
  48. package/dist/node_modules/zod/v4/core/util.js +32 -55
  49. package/dist/node_modules/zod/v4/core/versions.cjs +2 -2
  50. package/dist/node_modules/zod/v4/core/versions.js +2 -2
  51. package/dist/node_modules/zod/v4/locales/en.cjs +0 -4
  52. package/dist/node_modules/zod/v4/locales/en.js +0 -4
  53. package/dist/node_modules/zod/v4/locales/fr.cjs +7 -24
  54. package/dist/node_modules/zod/v4/locales/fr.js +7 -24
  55. package/dist/node_modules/zod/v4/locales/index.cjs +1 -8
  56. package/dist/node_modules/zod/v4/locales/index.js +0 -3
  57. package/dist/node_modules/zod/v4/locales/it.cjs +1 -1
  58. package/dist/node_modules/zod/v4/locales/it.js +1 -1
  59. package/dist/node_modules/zod/v4/locales/ka.cjs +8 -8
  60. package/dist/node_modules/zod/v4/locales/ka.js +8 -8
  61. package/dist/node_modules/zod/v4/locales/package.json +1 -2
  62. package/dist/node_modules/zod/v4/locales/uz.cjs +0 -1
  63. package/dist/node_modules/zod/v4/locales/uz.js +0 -1
  64. package/dist/node_modules/zod/v4/mini/package.json +1 -2
  65. package/dist/node_modules/zod/v4/mini/schemas.cjs +4 -41
  66. package/dist/node_modules/zod/v4/mini/schemas.js +4 -40
  67. package/dist/node_modules/zod/v4/package.json +1 -2
  68. package/dist/node_modules/zod/v4-mini/package.json +1 -2
  69. package/package.json +20 -20
  70. package/dist/node_modules/zod/v4/locales/el.cjs +0 -136
  71. package/dist/node_modules/zod/v4/locales/el.js +0 -109
  72. package/dist/node_modules/zod/v4/locales/hr.cjs +0 -149
  73. package/dist/node_modules/zod/v4/locales/hr.js +0 -122
  74. package/dist/node_modules/zod/v4/locales/ro.cjs +0 -146
  75. package/dist/node_modules/zod/v4/locales/ro.js +0 -119
@@ -40,17 +40,69 @@ var import_node = require("vscode-jsonrpc/node.js");
40
40
  var import_rpc = require("./generated/rpc.js");
41
41
  var import_sdkProtocolVersion = require("./sdkProtocolVersion.js");
42
42
  var import_session = require("./session.js");
43
+ var import_host = require("./host.js");
43
44
  var import_runtimeArtifacts = require("./runtimeArtifacts.js");
44
45
  var import_cliVersion = require("./cliVersion.js");
45
46
  var import_sessionFsProvider = require("./sessionFsProvider.js");
46
47
  var import_copilotRequestHandler = require("./copilotRequestHandler.js");
48
+ var import_installationConfirmation = require("./installationConfirmation.js");
47
49
  var import_telemetry = require("./telemetry.js");
50
+ var import_schema = require("./schema.js");
48
51
  var import_toolSet = require("./toolSet.js");
49
52
  var import_types = require("./types.js");
50
53
  const MIN_PROTOCOL_VERSION = 3;
51
54
  const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
52
- function isZodSchema(value) {
53
- return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
55
+ const CLOUD_SESSION_CLEANUP_TIMEOUT_MS = 1e4;
56
+ function createMessageConnection(reader, writer) {
57
+ let dispatch;
58
+ let finishDrain;
59
+ let drained;
60
+ let disposed = false;
61
+ const barrier = { jsonrpc: "2.0", method: "$/sdkDrain" };
62
+ const connection = (0, import_node.createMessageConnection)(
63
+ {
64
+ onError: reader.onError,
65
+ onClose: reader.onClose,
66
+ onPartialMessage: reader.onPartialMessage,
67
+ listen: (callback) => {
68
+ dispatch = callback;
69
+ return reader.listen(callback);
70
+ },
71
+ dispose: () => reader.dispose()
72
+ },
73
+ writer,
74
+ void 0,
75
+ {
76
+ messageStrategy: {
77
+ handleMessage: (message, next) => {
78
+ if (message === barrier) {
79
+ finishDrain?.();
80
+ } else {
81
+ next(message);
82
+ }
83
+ }
84
+ }
85
+ }
86
+ );
87
+ connection.onDispose(() => {
88
+ disposed = true;
89
+ finishDrain?.();
90
+ });
91
+ return Object.assign(connection, {
92
+ drain: () => {
93
+ if (!drained) {
94
+ drained = new Promise((resolve2) => {
95
+ finishDrain = resolve2;
96
+ if (disposed || !dispatch) {
97
+ resolve2();
98
+ } else {
99
+ dispatch(barrier);
100
+ }
101
+ });
102
+ }
103
+ return drained;
104
+ }
105
+ });
54
106
  }
55
107
  async function withTimeout(promise, timeoutMs, message) {
56
108
  let timeout;
@@ -96,13 +148,6 @@ async function waitForChildExit(child, timeoutMs) {
96
148
  }
97
149
  });
98
150
  }
99
- function toJsonSchema(parameters) {
100
- if (!parameters) return void 0;
101
- if (isZodSchema(parameters)) {
102
- return parameters.toJSONSchema();
103
- }
104
- return parameters;
105
- }
106
151
  const DEFAULT_PROVIDER_NAME = "default";
107
152
  function extractBearerTokenProviders(provider, providers) {
108
153
  const callbacks = /* @__PURE__ */ new Map();
@@ -235,8 +280,10 @@ class CopilotClient {
235
280
  cliProcess = null;
236
281
  ffiHost = null;
237
282
  connection = null;
283
+ requestAdapter = null;
238
284
  messageWriter = null;
239
285
  connectionClosed = false;
286
+ connectionEpoch = 0;
240
287
  socket = null;
241
288
  runtimePort = null;
242
289
  actualHost = "localhost";
@@ -244,6 +291,9 @@ class CopilotClient {
244
291
  /** Shared in-flight start; concurrent callers await it instead of spawning another CLI. */
245
292
  startPromise = null;
246
293
  sessions = /* @__PURE__ */ new Map();
294
+ hostExitCallbacks = /* @__PURE__ */ new Map();
295
+ hostSessionFactories = /* @__PURE__ */ new Map();
296
+ hostHandoffs = /* @__PURE__ */ new Map();
247
297
  stderrBuffer = "";
248
298
  // Captures CLI stderr for error messages
249
299
  /** Resolved connection mode chosen in the constructor. */
@@ -272,6 +322,8 @@ class CopilotClient {
272
322
  /** Connection-level session filesystem config, set via constructor option. */
273
323
  sessionFsConfig = null;
274
324
  requestHandler = null;
325
+ extensionLaunchProvider;
326
+ installationConfirmationHandler;
275
327
  builtinPluginDirectories = [];
276
328
  onGitHubTelemetry;
277
329
  clientGlobalHandlers = {};
@@ -429,6 +481,8 @@ class CopilotClient {
429
481
  this.onGetTraceContext = options.onGetTraceContext;
430
482
  this.sessionFsConfig = options.sessionFs ?? null;
431
483
  this.requestHandler = options.requestHandler ?? null;
484
+ this.extensionLaunchProvider = options.extensionLaunchProvider;
485
+ this.installationConfirmationHandler = options.installationConfirmationHandler;
432
486
  this.onGitHubTelemetry = options.onGitHubTelemetry;
433
487
  this.setupClientGlobalHandlers();
434
488
  const connEnv = conn.kind === "stdio" || conn.kind === "tcp" ? conn.env : void 0;
@@ -527,18 +581,30 @@ class CopilotClient {
527
581
  "SessionFsConfig declares capabilities.sqlite but the provider does not implement sqlite."
528
582
  );
529
583
  }
584
+ if (this.sessionFsConfig.capabilities?.binary && (!provider.readFileBytes || !provider.writeFileBytes)) {
585
+ throw new Error(
586
+ "SessionFsConfig declares capabilities.binary but the provider does not implement readFileBytes and writeFileBytes."
587
+ );
588
+ }
530
589
  session.clientSessionApis.sessionFs = (0, import_sessionFsProvider.createSessionFsAdapter)(provider);
531
590
  }
532
591
  setupClientGlobalHandlers() {
533
592
  const handlers = {};
593
+ handlers.host = {
594
+ exited: async (exit) => {
595
+ this.handleHostExit(exit);
596
+ }
597
+ };
598
+ handlers.extensionLaunchProvider = this.extensionLaunchProvider;
534
599
  if (this.requestHandler) {
535
- handlers.llmInference = (0, import_copilotRequestHandler.createCopilotRequestAdapter)(this.requestHandler, () => {
600
+ this.requestAdapter = (0, import_copilotRequestHandler.createCopilotRequestAdapter)(this.requestHandler, () => {
536
601
  if (!this.connection) {
537
602
  return void 0;
538
603
  }
539
604
  this._rpc ??= (0, import_rpc.createServerRpc)(this.connection);
540
605
  return this._rpc;
541
606
  });
607
+ handlers.llmInference = this.requestAdapter;
542
608
  }
543
609
  if (this.onGitHubTelemetry) {
544
610
  const onGitHubTelemetry = this.onGitHubTelemetry;
@@ -632,6 +698,7 @@ class CopilotClient {
632
698
  }
633
699
  async doStart() {
634
700
  this.forceStopping = false;
701
+ this.connectionEpoch++;
635
702
  this.connectionClosed = false;
636
703
  this.processTransportError = null;
637
704
  this.state = "connecting";
@@ -643,6 +710,9 @@ class CopilotClient {
643
710
  }
644
711
  await this.connectToServer();
645
712
  await this.verifyProtocolVersion();
713
+ if (this.extensionLaunchProvider) {
714
+ await this.rpc.registerExtensionLaunchProvider();
715
+ }
646
716
  if (this.builtinPluginDirectories.length > 0) {
647
717
  try {
648
718
  await this.connection.sendRequest("plugins.builtin.set", {
@@ -664,6 +734,9 @@ class CopilotClient {
664
734
  if (this.requestHandler) {
665
735
  await this.connection.sendRequest("llmInference.setProvider", {});
666
736
  }
737
+ if (this.connectionClosed) {
738
+ throw new Error("CLI server connection closed during startup");
739
+ }
667
740
  this.state = "connected";
668
741
  } catch (error) {
669
742
  const startupError = this.processTransportError ?? error;
@@ -679,7 +752,8 @@ class CopilotClient {
679
752
  * 1. Closes all active sessions (releases in-memory resources)
680
753
  * 2. Requests runtime shutdown for SDK-owned CLI processes
681
754
  * 3. Closes the JSON-RPC connection
682
- * 4. Terminates the CLI server process (if spawned by this client)
755
+ * 4. Signals EOF to an owned stdio process and waits for host cleanup, then
756
+ * terminates the process if it does not exit within the shutdown timeout
683
757
  *
684
758
  * Note: session data on disk is preserved, so sessions can be resumed later.
685
759
  * To permanently remove session data before stopping, call
@@ -731,6 +805,7 @@ class CopilotClient {
731
805
  }
732
806
  this.sessions.clear();
733
807
  this.githubTokenProviders.clear();
808
+ this.requestAdapter?.cancelPending();
734
809
  if (this.connection && !this.connectionClosed && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
735
810
  const runtimeShutdownStart = Date.now();
736
811
  const shutdownPromise = this.rpc.runtime.shutdown();
@@ -781,10 +856,23 @@ class CopilotClient {
781
856
  this.socket = null;
782
857
  try {
783
858
  if (!socket.destroyed) {
784
- await new Promise((resolve2) => {
785
- socket.once("close", () => resolve2());
786
- socket.end();
787
- });
859
+ let timeout;
860
+ try {
861
+ await new Promise((resolve2) => {
862
+ socket.once("close", () => resolve2());
863
+ if (this.connectionClosed) {
864
+ socket.destroy();
865
+ } else {
866
+ timeout = setTimeout(
867
+ () => socket.destroy(),
868
+ RUNTIME_SHUTDOWN_TIMEOUT_MS
869
+ );
870
+ socket.end();
871
+ }
872
+ });
873
+ } finally {
874
+ if (timeout !== void 0) clearTimeout(timeout);
875
+ }
788
876
  }
789
877
  } catch (error) {
790
878
  errors.push(
@@ -796,7 +884,23 @@ class CopilotClient {
796
884
  }
797
885
  if (this.cliProcess && !this.isExternalServer) {
798
886
  const child = this.cliProcess;
799
- this.cliProcess = null;
887
+ if (this.connectionConfig.kind === "stdio" && child.stdin && child.exitCode == null && child.signalCode == null) {
888
+ const gracefulExitStart = Date.now();
889
+ try {
890
+ child.stdin.end();
891
+ const exited = await waitForChildExit(child, RUNTIME_SHUTDOWN_TIMEOUT_MS);
892
+ this.logDebugTiming(
893
+ exited ? "CopilotClient.stop graceful stdio exit complete" : "CopilotClient.stop graceful stdio exit timed out; terminating child",
894
+ gracefulExitStart
895
+ );
896
+ } catch (error) {
897
+ errors.push(
898
+ new Error(
899
+ `Failed to close CLI stdin: ${error instanceof Error ? error.message : String(error)}`
900
+ )
901
+ );
902
+ }
903
+ }
800
904
  try {
801
905
  if (child.exitCode == null && child.signalCode == null) {
802
906
  child.kill();
@@ -814,6 +918,10 @@ class CopilotClient {
814
918
  `Failed to kill CLI process: ${error instanceof Error ? error.message : String(error)}`
815
919
  )
816
920
  );
921
+ } finally {
922
+ if (this.cliProcess === child) {
923
+ this.cliProcess = null;
924
+ }
817
925
  }
818
926
  }
819
927
  if (this.ffiHost) {
@@ -837,6 +945,7 @@ class CopilotClient {
837
945
  this.runtimePort = null;
838
946
  this.stderrBuffer = "";
839
947
  this.processExitPromise = null;
948
+ this.disconnectHosts();
840
949
  return errors;
841
950
  }
842
951
  /**
@@ -881,11 +990,13 @@ class CopilotClient {
881
990
  */
882
991
  async forceStop() {
883
992
  this.forceStopping = true;
993
+ this.disconnectHosts();
884
994
  for (const session of this.sessions.values()) {
885
995
  session._markDisconnected();
886
996
  }
887
997
  this.sessions.clear();
888
998
  this.githubTokenProviders.clear();
999
+ this.requestAdapter?.cancelPending();
889
1000
  if (this.messageWriter) {
890
1001
  this.messageWriter.suppressWriteErrors = true;
891
1002
  }
@@ -1093,6 +1204,9 @@ class CopilotClient {
1093
1204
  if (config.gitHubToken !== void 0 && config.gitHubTokenProvider !== void 0) {
1094
1205
  throw new Error("gitHubToken and gitHubTokenProvider are mutually exclusive");
1095
1206
  }
1207
+ if (config.cloud != null && config.skillProvider) {
1208
+ throw new Error("Skill providers are not supported for cloud sessions.");
1209
+ }
1096
1210
  if (!this.connection) {
1097
1211
  await this.start();
1098
1212
  }
@@ -1152,18 +1266,22 @@ class CopilotClient {
1152
1266
  if (config.hooks) {
1153
1267
  s.registerHooks(config.hooks);
1154
1268
  }
1269
+ if (config.skillProvider) {
1270
+ s.registerSkillProvider(config.skillProvider);
1271
+ }
1155
1272
  if (transformCallbacks) {
1156
1273
  s.registerTransformCallbacks(transformCallbacks);
1157
1274
  }
1158
1275
  if (config.onEvent) {
1159
1276
  s.on(config.onEvent);
1160
1277
  }
1161
- this.sessions.set(sessionId, s);
1162
1278
  this.setupSessionFs(s, config);
1279
+ this.sessions.set(sessionId, s);
1163
1280
  return s;
1164
1281
  };
1165
1282
  let session;
1166
1283
  let registeredId;
1284
+ let uninitializedCloudSessionId;
1167
1285
  if (localSessionId !== void 0) {
1168
1286
  try {
1169
1287
  session = initializeSession(localSessionId);
@@ -1179,6 +1297,7 @@ class CopilotClient {
1179
1297
  const response = await this.connection.sendRequest("session.create", {
1180
1298
  ...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
1181
1299
  model: config.model,
1300
+ allowedModels: config.allowedModels,
1182
1301
  sessionId: localSessionId,
1183
1302
  clientName: config.clientName,
1184
1303
  reasoningEffort: config.reasoningEffort,
@@ -1188,7 +1307,7 @@ class CopilotClient {
1188
1307
  tools: config.tools?.map((tool) => ({
1189
1308
  name: tool.name,
1190
1309
  description: tool.description,
1191
- parameters: toJsonSchema(tool.parameters),
1310
+ parameters: (0, import_schema.toJsonSchema)(tool.parameters),
1192
1311
  overridesBuiltInTool: tool.overridesBuiltInTool,
1193
1312
  skipPermission: tool.skipPermission,
1194
1313
  defer: tool.defer,
@@ -1196,6 +1315,7 @@ class CopilotClient {
1196
1315
  isTerminal: tool.isTerminal
1197
1316
  })),
1198
1317
  toolSearch: config.toolSearch,
1318
+ imageGeneration: config.imageGeneration,
1199
1319
  canvases: config.canvases?.map((canvas) => canvas.declaration),
1200
1320
  requestCanvasRenderer: config.requestCanvasRenderer,
1201
1321
  requestExtensions: config.requestExtensions,
@@ -1236,6 +1356,7 @@ class CopilotClient {
1236
1356
  includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
1237
1357
  ...this.onGitHubTelemetry != null ? { enableGitHubTelemetryForwarding: true } : {},
1238
1358
  mcpServers: toWireMcpServers(config.mcpServers),
1359
+ ...config.diagnostics !== void 0 ? { diagnostics: config.diagnostics } : {},
1239
1360
  mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
1240
1361
  authClientIdMetadataUrl: config.authClientIdMetadataUrl,
1241
1362
  envValueMode: "direct",
@@ -1245,6 +1366,7 @@ class CopilotClient {
1245
1366
  agent: config.agent,
1246
1367
  configDir: config.configDirectory,
1247
1368
  enableConfigDiscovery: config.enableConfigDiscovery,
1369
+ refreshCustomInstructions: config.refreshCustomInstructions,
1248
1370
  skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
1249
1371
  embeddingCacheStorage: config.embeddingCacheStorage,
1250
1372
  organizationCustomInstructions: config.organizationCustomInstructions,
@@ -1254,6 +1376,7 @@ class CopilotClient {
1254
1376
  enableSessionStore: config.enableSessionStore,
1255
1377
  enableSkills: config.enableSkills,
1256
1378
  skillDirectories: config.skillDirectories,
1379
+ ...config.skillProvider ? { hasSkillProvider: true } : {},
1257
1380
  pluginDirectories: config.pluginDirectories,
1258
1381
  instructionDirectories: config.instructionDirectories,
1259
1382
  disabledSkills: config.disabledSkills,
@@ -1267,6 +1390,7 @@ class CopilotClient {
1267
1390
  featureFlags: config.featureFlags,
1268
1391
  expAssignments: config.expAssignments,
1269
1392
  enableManagedSettings: config.enableManagedSettings,
1393
+ enforceManagedModelDefaults: config.enforceManagedModelDefaults,
1270
1394
  managedSettings: config.managedSettings
1271
1395
  });
1272
1396
  const {
@@ -1283,7 +1407,9 @@ class CopilotClient {
1283
1407
  );
1284
1408
  }
1285
1409
  if (session === void 0) {
1410
+ uninitializedCloudSessionId = returnedSessionId;
1286
1411
  session = initializeSession(returnedSessionId);
1412
+ uninitializedCloudSessionId = void 0;
1287
1413
  registeredId = returnedSessionId;
1288
1414
  }
1289
1415
  this.assignGitHubTokenProvider(gitHubTokenProviderRegistrationId, returnedSessionId);
@@ -1305,8 +1431,27 @@ class CopilotClient {
1305
1431
  if (gitHubTokenProviderRegistrationId !== void 0) {
1306
1432
  this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId);
1307
1433
  }
1434
+ if (uninitializedCloudSessionId !== void 0) {
1435
+ try {
1436
+ await withTimeout(
1437
+ this.deleteSession(uninitializedCloudSessionId),
1438
+ CLOUD_SESSION_CLEANUP_TIMEOUT_MS,
1439
+ `session.delete timed out after ${CLOUD_SESSION_CLEANUP_TIMEOUT_MS}ms`
1440
+ );
1441
+ } catch (cleanupError) {
1442
+ throw new AggregateError(
1443
+ [e, cleanupError],
1444
+ "Failed to initialize and delete cloud session"
1445
+ );
1446
+ }
1447
+ }
1308
1448
  throw e;
1309
1449
  }
1450
+ for (const entry of this.hostHandoffs.values()) {
1451
+ if (entry.requestedSessionId === session.sessionId) {
1452
+ entry.configs.set(session, config);
1453
+ }
1454
+ }
1310
1455
  return session;
1311
1456
  }
1312
1457
  /**
@@ -1337,10 +1482,11 @@ class CopilotClient {
1337
1482
  return this.resumeSessionInternal(sessionId, config);
1338
1483
  }
1339
1484
  /** @internal */
1340
- async resumeSessionForExtension(sessionId, config, factories, extensionOptions) {
1341
- return this.resumeSessionInternal(sessionId, config, factories, extensionOptions);
1485
+ async resumeSessionForExtension(sessionId, config, contributions = {}, extensionOptions) {
1486
+ return this.resumeSessionInternal(sessionId, config, contributions, extensionOptions);
1342
1487
  }
1343
- async resumeSessionInternal(sessionId, config, factories, extensionOptions) {
1488
+ async resumeSessionInternal(sessionId, config, contributions = {}, extensionOptions) {
1489
+ const { workflows } = contributions;
1344
1490
  if (config.gitHubToken !== void 0 && config.gitHubTokenProvider !== void 0) {
1345
1491
  throw new Error("gitHubToken and gitHubTokenProvider are mutually exclusive");
1346
1492
  }
@@ -1360,7 +1506,7 @@ class CopilotClient {
1360
1506
  session.registerTools(config.tools);
1361
1507
  session.registerCanvases(config.canvases);
1362
1508
  session.registerCommands(config.commands);
1363
- session.registerFactories(factories);
1509
+ session.registerWorkflows(workflows);
1364
1510
  const {
1365
1511
  wireProvider: bearerWireProvider,
1366
1512
  wireProviders: bearerWireProviders,
@@ -1385,6 +1531,9 @@ class CopilotClient {
1385
1531
  if (config.hooks) {
1386
1532
  session.registerHooks(config.hooks);
1387
1533
  }
1534
+ if (config.skillProvider) {
1535
+ session.registerSkillProvider(config.skillProvider);
1536
+ }
1388
1537
  const modeDefaults = this.configDefaultsForMode();
1389
1538
  config = { ...modeDefaults, ...config };
1390
1539
  config.customAgentsLocalOnly ??= modeDefaults.customAgentsLocalOnly;
@@ -1398,9 +1547,10 @@ class CopilotClient {
1398
1547
  if (config.onEvent) {
1399
1548
  session.on(config.onEvent);
1400
1549
  }
1401
- this.sessions.set(sessionId, session);
1402
- this.setupSessionFs(session, config);
1403
1550
  const toolFilterOptions = this.resolveToolFilterOptions(config);
1551
+ this.setupSessionFs(session, config);
1552
+ const replacedSession = this.sessions.get(sessionId);
1553
+ this.sessions.set(sessionId, session);
1404
1554
  const gitHubTokenProviderRegistrationId = this.registerGitHubTokenProvider(
1405
1555
  config.gitHubTokenProvider,
1406
1556
  sessionId
@@ -1412,8 +1562,10 @@ class CopilotClient {
1412
1562
  }
1413
1563
  try {
1414
1564
  const response = await this.connection.sendRequest("session.resume", {
1565
+ allowedModels: config.allowedModels,
1415
1566
  ...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
1416
1567
  sessionId,
1568
+ allowTranscriptRecovery: config.allowTranscriptRecovery,
1417
1569
  clientName: config.clientName,
1418
1570
  model: config.model,
1419
1571
  reasoningEffort: config.reasoningEffort,
@@ -1432,7 +1584,7 @@ class CopilotClient {
1432
1584
  tools: config.tools?.map((tool) => ({
1433
1585
  name: tool.name,
1434
1586
  description: tool.description,
1435
- parameters: toJsonSchema(tool.parameters),
1587
+ parameters: (0, import_schema.toJsonSchema)(tool.parameters),
1436
1588
  overridesBuiltInTool: tool.overridesBuiltInTool,
1437
1589
  skipPermission: tool.skipPermission,
1438
1590
  defer: tool.defer,
@@ -1440,8 +1592,9 @@ class CopilotClient {
1440
1592
  isTerminal: tool.isTerminal
1441
1593
  })),
1442
1594
  toolSearch: config.toolSearch,
1595
+ imageGeneration: config.imageGeneration,
1443
1596
  canvases: config.canvases?.map((canvas) => canvas.declaration),
1444
- factories: factories?.map((factory) => factory.meta),
1597
+ workflows: workflows?.map((workflow) => workflow.meta),
1445
1598
  requestCanvasRenderer: config.requestCanvasRenderer,
1446
1599
  requestExtensions: config.requestExtensions,
1447
1600
  extensionSdkPath: config.extensionSdkPath,
@@ -1482,6 +1635,7 @@ class CopilotClient {
1482
1635
  includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
1483
1636
  ...this.onGitHubTelemetry != null ? { enableGitHubTelemetryForwarding: true } : {},
1484
1637
  mcpServers: toWireMcpServers(config.mcpServers),
1638
+ ...config.diagnostics !== void 0 ? { diagnostics: config.diagnostics } : {},
1485
1639
  mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
1486
1640
  authClientIdMetadataUrl: config.authClientIdMetadataUrl,
1487
1641
  envValueMode: "direct",
@@ -1490,6 +1644,7 @@ class CopilotClient {
1490
1644
  defaultAgent: config.defaultAgent,
1491
1645
  agent: config.agent,
1492
1646
  skillDirectories: config.skillDirectories,
1647
+ ...config.skillProvider ? { hasSkillProvider: true } : {},
1493
1648
  pluginDirectories: config.pluginDirectories,
1494
1649
  instructionDirectories: config.instructionDirectories,
1495
1650
  disabledSkills: config.disabledSkills,
@@ -1505,6 +1660,7 @@ class CopilotClient {
1505
1660
  featureFlags: config.featureFlags,
1506
1661
  expAssignments: config.expAssignments,
1507
1662
  enableManagedSettings: config.enableManagedSettings,
1663
+ enforceManagedModelDefaults: config.enforceManagedModelDefaults,
1508
1664
  managedSettings: config.managedSettings,
1509
1665
  ...extensionOptions?.requestedEnvironmentVariables ? {
1510
1666
  requestedEnvironmentVariables: extensionOptions.requestedEnvironmentVariables
@@ -1519,10 +1675,11 @@ class CopilotClient {
1519
1675
  }
1520
1676
  }
1521
1677
  }
1522
- const { workspacePath, capabilities, openCanvases } = response;
1678
+ const { workspacePath, capabilities, openCanvases, transcriptRecovery } = response;
1523
1679
  session["_workspacePath"] = workspacePath;
1524
1680
  session.setCapabilities(capabilities);
1525
1681
  session.setOpenCanvases(openCanvases ?? []);
1682
+ session.setTranscriptRecovery(transcriptRecovery);
1526
1683
  if (config.onMcpAuthRequest) {
1527
1684
  await this.connection.sendRequest("session.eventLog.registerInterest", {
1528
1685
  sessionId,
@@ -1533,14 +1690,220 @@ class CopilotClient {
1533
1690
  this.commitGitHubTokenProvider(sessionId, gitHubTokenProviderRegistrationId);
1534
1691
  } catch (e) {
1535
1692
  session._markDisconnected();
1536
- this.sessions.delete(sessionId);
1693
+ if (this.sessions.get(sessionId) === session) {
1694
+ if (replacedSession) {
1695
+ this.sessions.set(sessionId, replacedSession);
1696
+ } else {
1697
+ this.sessions.delete(sessionId);
1698
+ }
1699
+ }
1537
1700
  if (gitHubTokenProviderRegistrationId !== void 0) {
1538
1701
  this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId);
1539
1702
  }
1540
1703
  throw e;
1541
1704
  }
1705
+ for (const entry of this.hostHandoffs.values()) {
1706
+ if (entry.requestedSessionId === session.sessionId) {
1707
+ entry.configs.set(session, { ...config, sessionId });
1708
+ }
1709
+ }
1542
1710
  return session;
1543
1711
  }
1712
+ /**
1713
+ * Start a complete AHP listener hosted in-process by this runtime.
1714
+ *
1715
+ * This connection owns the host. Disposing it or disconnecting the client
1716
+ * stops the listener without deleting sessions. The host uses a separate
1717
+ * SDK connection to the same runtime, not another runtime process.
1718
+ * Only one AHP host may own a compute-scoped catalog in an effective Copilot home
1719
+ * at a time. Other SDK clients and sessions remain usable in that home.
1720
+ * Select localServer, githubEnvironment, or both. An empty localServer
1721
+ * selects 127.0.0.1 on an available port. `onExit` runs at most once; owner disconnection cannot
1722
+ * acknowledge listener cleanup over the disconnected transport.
1723
+ *
1724
+ * @experimental
1725
+ */
1726
+ async startAhpHost(options) {
1727
+ if (!options?.localServer && !options?.githubEnvironment) {
1728
+ throw new Error("At least one of localServer or githubEnvironment is required");
1729
+ }
1730
+ if (this.state !== "connected") {
1731
+ await this.start();
1732
+ }
1733
+ const rpc = this.rpc;
1734
+ const hostId = (0, import_node_crypto.randomUUID)();
1735
+ const {
1736
+ computeId,
1737
+ localServer,
1738
+ githubEnvironment,
1739
+ onExit,
1740
+ createSession,
1741
+ resumeSession,
1742
+ onSessionReleased
1743
+ } = options;
1744
+ if (createSession || resumeSession) {
1745
+ this.hostSessionFactories.set(hostId, {
1746
+ createSession,
1747
+ resumeSession,
1748
+ onSessionReleased
1749
+ });
1750
+ }
1751
+ if (onExit) {
1752
+ this.hostExitCallbacks.set(hostId, onExit);
1753
+ }
1754
+ try {
1755
+ const info = await rpc.host.start({
1756
+ hostId,
1757
+ computeId,
1758
+ localServer,
1759
+ githubEnvironment,
1760
+ sessionFactory: createSession ? true : void 0,
1761
+ resumeFactory: resumeSession ? true : void 0
1762
+ });
1763
+ return new import_host.AhpHost(
1764
+ info,
1765
+ async () => {
1766
+ await rpc.host.dispose({ hostId });
1767
+ },
1768
+ (sessionId) => rpc.host.publishSession({ hostId, sessionId }),
1769
+ () => rpc.host.listSessions({ hostId })
1770
+ );
1771
+ } catch (error) {
1772
+ this.hostExitCallbacks.delete(hostId);
1773
+ this.releaseHostSessions(hostId);
1774
+ throw error;
1775
+ }
1776
+ }
1777
+ handleHostExit(exit) {
1778
+ this.releaseHostSessions(exit.hostId);
1779
+ const onExit = this.hostExitCallbacks.get(exit.hostId);
1780
+ if (onExit) {
1781
+ this.hostExitCallbacks.delete(exit.hostId);
1782
+ const reportError = (error) => {
1783
+ console.error("AHP host exit callback failed", { hostId: exit.hostId, error });
1784
+ };
1785
+ try {
1786
+ void Promise.resolve(onExit(exit)).catch(reportError);
1787
+ } catch (error) {
1788
+ reportError(error);
1789
+ }
1790
+ }
1791
+ }
1792
+ disconnectHosts() {
1793
+ for (const hostId of /* @__PURE__ */ new Set([
1794
+ ...this.hostExitCallbacks.keys(),
1795
+ ...this.hostSessionFactories.keys()
1796
+ ])) {
1797
+ this.handleHostExit({
1798
+ hostId,
1799
+ reason: "ownerDisconnected",
1800
+ error: "Owner connection closed; runtime cleanup cannot be acknowledged on this connection."
1801
+ });
1802
+ }
1803
+ }
1804
+ releaseHostSessions(hostId) {
1805
+ this.hostSessionFactories.delete(hostId);
1806
+ for (const [handoffId, entry] of this.hostHandoffs) {
1807
+ if (entry.hostId === hostId) this.releaseHostSession(handoffId);
1808
+ }
1809
+ }
1810
+ releaseHostSession(handoffId) {
1811
+ const entry = this.hostHandoffs.get(handoffId);
1812
+ if (!entry) return;
1813
+ this.hostHandoffs.delete(handoffId);
1814
+ entry.released = true;
1815
+ entry.configs.clear();
1816
+ entry.controller.abort();
1817
+ if (entry.session) this.notifyHostSessionReleased(entry);
1818
+ }
1819
+ notifyHostSessionReleased(entry) {
1820
+ const { session, onReleased } = entry;
1821
+ entry.session = void 0;
1822
+ if (session && onReleased) {
1823
+ try {
1824
+ void Promise.resolve(onReleased(session)).catch(() => {
1825
+ console.error("AHP session release callback failed");
1826
+ });
1827
+ } catch {
1828
+ console.error("AHP session release callback failed");
1829
+ }
1830
+ }
1831
+ }
1832
+ async createHostSession(params) {
1833
+ const options = this.hostSessionFactories.get(params.hostId);
1834
+ if (!(params.resume ? options?.resumeSession : options?.createSession) || this.hostHandoffs.has(params.handoffId)) {
1835
+ throw new Error("AHP session factory is unavailable or handoff already exists");
1836
+ }
1837
+ const entry = {
1838
+ hostId: params.hostId,
1839
+ requestedSessionId: params.config.sessionId,
1840
+ controller: new AbortController(),
1841
+ configs: /* @__PURE__ */ new Map(),
1842
+ released: false,
1843
+ onReleased: options.onSessionReleased
1844
+ };
1845
+ this.hostHandoffs.set(params.handoffId, entry);
1846
+ const { configDir, ...config } = params.config;
1847
+ if (configDir !== void 0) config.configDirectory = configDir;
1848
+ const expected = structuredClone(config);
1849
+ const cancelled = new Promise((_resolve, reject) => {
1850
+ entry.controller.signal.addEventListener(
1851
+ "abort",
1852
+ () => {
1853
+ reject(new Error("AHP session handoff ended"));
1854
+ },
1855
+ { once: true }
1856
+ );
1857
+ });
1858
+ const materialized = Promise.resolve().then(() => {
1859
+ if (params.resume) {
1860
+ const { sessionId, ...resumeConfig } = config;
1861
+ if (!sessionId) throw new Error("AHP resume requires sessionId");
1862
+ return options.resumeSession({
1863
+ sessionId,
1864
+ config: resumeConfig,
1865
+ signal: entry.controller.signal
1866
+ });
1867
+ }
1868
+ return options.createSession({
1869
+ config,
1870
+ signal: entry.controller.signal
1871
+ });
1872
+ }).then((session) => {
1873
+ entry.session = session;
1874
+ const actual = entry.configs.get(session);
1875
+ entry.configs.clear();
1876
+ if (entry.released) {
1877
+ this.notifyHostSessionReleased(entry);
1878
+ throw new Error("AHP session handoff ended");
1879
+ }
1880
+ if (this.sessions.get(session.sessionId) !== session || session.sessionId !== expected.sessionId) {
1881
+ throw new Error(
1882
+ "AHP callback must return the requested session from this client"
1883
+ );
1884
+ }
1885
+ const contains = (value, required) => {
1886
+ if (required && typeof required === "object" && !Array.isArray(required)) {
1887
+ return !!value && typeof value === "object" && Object.entries(required).every(
1888
+ ([key, child]) => contains(value[key], child)
1889
+ );
1890
+ }
1891
+ return JSON.stringify(value) === JSON.stringify(required);
1892
+ };
1893
+ if ((!params.resume || actual !== void 0) && !contains(actual, expected)) {
1894
+ throw new Error(
1895
+ "AHP callback must preserve the supplied session configuration"
1896
+ );
1897
+ }
1898
+ return { sessionId: session.sessionId };
1899
+ });
1900
+ try {
1901
+ return await Promise.race([materialized, cancelled]);
1902
+ } catch (error) {
1903
+ this.releaseHostSession(params.handoffId);
1904
+ throw error;
1905
+ }
1906
+ }
1544
1907
  /**
1545
1908
  * Sends a ping request to the server to verify connectivity.
1546
1909
  *
@@ -1902,6 +2265,9 @@ class CopilotClient {
1902
2265
  if (this.options.mode === "empty") {
1903
2266
  env.COPILOT_DISABLE_KEYTAR = "1";
1904
2267
  }
2268
+ if (this.options.mode !== "empty") {
2269
+ env.COPILOT_RUNTIME_PROCESS_FILE_LOGGING = "1";
2270
+ }
1905
2271
  if (this.options.telemetry) {
1906
2272
  const t = this.options.telemetry;
1907
2273
  env.COPILOT_OTEL_ENABLED = "true";
@@ -2077,13 +2443,19 @@ stderr: ${stderrOutput}`
2077
2443
  case "inprocess":
2078
2444
  return this.connectViaFfi();
2079
2445
  case "tcp":
2080
- case "uri":
2081
2446
  return this.connectViaTcp();
2447
+ case "uri": {
2448
+ const { host, port } = this.parseCliUrl(this.connectionConfig.url);
2449
+ this.actualHost = host;
2450
+ this.runtimePort = port;
2451
+ return this.connectViaTcp();
2452
+ }
2082
2453
  }
2083
2454
  }
2084
2455
  /** Starts the in-process FFI runtime with SDK-managed typed options. */
2085
2456
  async startInProcessFfi() {
2086
2457
  const explicitEntrypoint = this.resolvedEnv.COPILOT_CLI_PATH;
2458
+ const cliLoginSyncIntegrationId = this.resolvedEnv.GITHUB_COPILOT_INTEGRATION_ID?.trim() || void 0;
2087
2459
  let runtimeLibrary;
2088
2460
  if (explicitEntrypoint) {
2089
2461
  const entrypointDirectory = (0, import_node_path.dirname)((0, import_node_path.resolve)(explicitEntrypoint));
@@ -2095,7 +2467,8 @@ stderr: ${stderrOutput}`
2095
2467
  "runtime.node"
2096
2468
  );
2097
2469
  } else {
2098
- runtimeLibrary = (0, import_node_path.join)((0, import_node_path.dirname)(await getBundledRuntimePath()), "runtime.node");
2470
+ const bundledRuntimePath = await getBundledRuntimePath();
2471
+ runtimeLibrary = (0, import_node_path.join)((0, import_node_path.dirname)(bundledRuntimePath), "runtime.node");
2099
2472
  }
2100
2473
  const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
2101
2474
  const environment = {};
@@ -2124,6 +2497,9 @@ stderr: ${stderrOutput}`
2124
2497
  if (this.options.enableRemoteSessions) {
2125
2498
  args.push("--remote");
2126
2499
  }
2500
+ if (cliLoginSyncIntegrationId) {
2501
+ args.push(`--cli-login-sync-integration-id=${cliLoginSyncIntegrationId}`);
2502
+ }
2127
2503
  const host = FfiRuntimeHost.create(runtimeLibrary, explicitEntrypoint, environment, args);
2128
2504
  this.ffiHost = host;
2129
2505
  await host.start();
@@ -2137,7 +2513,7 @@ stderr: ${stderrOutput}`
2137
2513
  throw new Error("In-process FFI runtime host not started");
2138
2514
  }
2139
2515
  this.messageWriter = new TeardownResilientStreamMessageWriter(this.ffiHost.sendStream);
2140
- this.connection = (0, import_node.createMessageConnection)(
2516
+ this.connection = createMessageConnection(
2141
2517
  new import_node.StreamMessageReader(this.ffiHost.receiveStream),
2142
2518
  this.messageWriter
2143
2519
  );
@@ -2191,7 +2567,7 @@ stderr: ${stderrOutput}` : ""}`
2191
2567
  }
2192
2568
  });
2193
2569
  this.messageWriter = new TeardownResilientStreamMessageWriter(this.cliProcess.stdin);
2194
- this.connection = (0, import_node.createMessageConnection)(
2570
+ this.connection = createMessageConnection(
2195
2571
  new import_node.StreamMessageReader(this.cliProcess.stdout),
2196
2572
  this.messageWriter
2197
2573
  );
@@ -2206,7 +2582,7 @@ stderr: ${stderrOutput}` : ""}`
2206
2582
  throw new Error("CLI child process was unexpectedly started in parent process mode");
2207
2583
  }
2208
2584
  this.messageWriter = new TeardownResilientStreamMessageWriter(process.stdout);
2209
- this.connection = (0, import_node.createMessageConnection)(
2585
+ this.connection = createMessageConnection(
2210
2586
  new import_node.StreamMessageReader(process.stdin),
2211
2587
  this.messageWriter
2212
2588
  );
@@ -2229,7 +2605,7 @@ stderr: ${stderrOutput}` : ""}`
2229
2605
  this.socket.connect(this.runtimePort, this.actualHost, () => {
2230
2606
  clearTimeout(connectionTimeout);
2231
2607
  this.messageWriter = new TeardownResilientStreamMessageWriter(this.socket);
2232
- this.connection = (0, import_node.createMessageConnection)(
2608
+ this.connection = createMessageConnection(
2233
2609
  new import_node.StreamMessageReader(this.socket),
2234
2610
  this.messageWriter
2235
2611
  );
@@ -2276,28 +2652,112 @@ stderr: ${stderrOutput}` : ""}`
2276
2652
  return session.clientSessionApis;
2277
2653
  });
2278
2654
  (0, import_rpc.registerClientGlobalApiHandlers)(this.connection, this.clientGlobalHandlers);
2655
+ if (this.installationConfirmationHandler) {
2656
+ const confirmation = (0, import_installationConfirmation.createInstallationConfirmationAdapter)(
2657
+ this.connection,
2658
+ this.installationConfirmationHandler
2659
+ );
2660
+ this.connection.onRequest(
2661
+ "installations.confirm",
2662
+ (params, token) => confirmation.confirm(params, token)
2663
+ );
2664
+ }
2665
+ this.connection.onRequest(
2666
+ "host.materializeSession",
2667
+ (params) => this.createHostSession(params)
2668
+ );
2669
+ this.connection.onNotification(
2670
+ "host.sessionReleased",
2671
+ (params) => {
2672
+ if (this.hostHandoffs.get(params.handoffId)?.hostId === params.hostId) {
2673
+ this.releaseHostSession(params.handoffId);
2674
+ }
2675
+ }
2676
+ );
2279
2677
  this.connection.onRequest(
2280
2678
  "hooks.invoke",
2281
2679
  async (params) => {
2282
2680
  return await this.handleHooksInvoke(params);
2283
2681
  }
2284
2682
  );
2683
+ this.connection.onRequest(
2684
+ "skillProvider.list",
2685
+ async (params, token) => await this.resolveSkillProviderSession(params)._handleSkillProviderList(token)
2686
+ );
2687
+ this.connection.onRequest(
2688
+ "skillProvider.read",
2689
+ async (params, token) => {
2690
+ const session = this.resolveSkillProviderSession(params);
2691
+ if (typeof params.name !== "string") {
2692
+ throw new Error("Invalid skillProvider.read payload");
2693
+ }
2694
+ return await session._handleSkillProviderRead(params.name, token);
2695
+ }
2696
+ );
2285
2697
  const connection = this.connection;
2698
+ const messageWriter = this.messageWriter;
2699
+ const cliProcess = this.isExternalServer ? null : this.cliProcess;
2700
+ const connectionEpoch = this.connectionEpoch;
2701
+ const isCurrentConnection = () => connectionEpoch === this.connectionEpoch && this.connection === connection && (cliProcess === null || this.cliProcess === cliProcess);
2702
+ let disconnecting = false;
2703
+ let exitFallback;
2286
2704
  const markDisconnected = () => {
2287
- if (this.connection !== connection) {
2705
+ if (!isCurrentConnection()) {
2706
+ connection.dispose();
2288
2707
  return;
2289
2708
  }
2290
2709
  this.connectionClosed = true;
2291
2710
  this.state = "disconnected";
2711
+ this.disconnectHosts();
2292
2712
  for (const session of this.sessions.values()) {
2293
2713
  session._markDisconnected();
2294
2714
  }
2295
2715
  this.sessions.clear();
2296
2716
  this.githubTokenProviders.clear();
2717
+ this.requestAdapter?.cancelPending();
2718
+ connection.dispose();
2719
+ this.socket?.destroy();
2720
+ this.connection = null;
2721
+ this.messageWriter = null;
2722
+ this._rpc = null;
2723
+ this._internalRpc = null;
2724
+ this.modelsCache = null;
2297
2725
  };
2298
- this.connection.onClose(markDisconnected);
2726
+ const disconnectAfterDrain = () => {
2727
+ if (exitFallback !== void 0) {
2728
+ clearTimeout(exitFallback);
2729
+ exitFallback = void 0;
2730
+ }
2731
+ if (!isCurrentConnection()) {
2732
+ connection.dispose();
2733
+ return;
2734
+ }
2735
+ if (disconnecting) return;
2736
+ disconnecting = true;
2737
+ this.connectionClosed = true;
2738
+ if (messageWriter) messageWriter.suppressWriteErrors = true;
2739
+ void connection.drain().then(markDisconnected);
2740
+ };
2741
+ this.connection.onClose(disconnectAfterDrain);
2742
+ if (cliProcess) {
2743
+ cliProcess.once("exit", () => {
2744
+ if (!isCurrentConnection()) {
2745
+ connection.dispose();
2746
+ return;
2747
+ }
2748
+ if (disconnecting) return;
2749
+ if (this.socket) {
2750
+ disconnectAfterDrain();
2751
+ return;
2752
+ }
2753
+ this.connectionClosed = true;
2754
+ if (messageWriter) messageWriter.suppressWriteErrors = true;
2755
+ exitFallback = setTimeout(disconnectAfterDrain, RUNTIME_SHUTDOWN_TIMEOUT_MS);
2756
+ exitFallback.unref();
2757
+ });
2758
+ }
2299
2759
  this.connection.onError(() => {
2300
- if (this.connection === connection) {
2760
+ if (connectionEpoch === this.connectionEpoch && this.connection === connection) {
2301
2761
  this.state = "disconnected";
2302
2762
  }
2303
2763
  });
@@ -2390,6 +2850,16 @@ stderr: ${stderrOutput}` : ""}`
2390
2850
  });
2391
2851
  return { response };
2392
2852
  }
2853
+ resolveSkillProviderSession(params) {
2854
+ if (!params || typeof params.sessionId !== "string") {
2855
+ throw new Error("Invalid skillProvider payload");
2856
+ }
2857
+ const session = this.sessions.get(params.sessionId);
2858
+ if (!session) {
2859
+ throw new Error(`Session not found: ${params.sessionId}`);
2860
+ }
2861
+ return session;
2862
+ }
2393
2863
  async handleHooksInvoke(params) {
2394
2864
  if (!params || typeof params.sessionId !== "string" || typeof params.hookType !== "string") {
2395
2865
  throw new Error("Invalid hooks invoke payload");